As discussed on grouper-dev, a changelog could provide both audit history and notification. This page is an attempt to work out details.
Complete auditing seems to imply lower-level database tracking, which would historicize who-did-what-when potentially including database access. That kind of auditing might be best performed via triggers or something that sits between the persistence and data layers like p6spy, and I think is beyond the immediate scope of the changelog.
Ideally, the changelog would record business-logic level changes, e.g adding a member to a group, stored in both a human and machine readable format.
The changelog implementation should be flexible, reasonably standard, and simple to support potentially heterogenous consumers.
The changelog should (optionally) provide enough information so that a group could be reconstructed for any given point in time.
Performance impacts should be understood and mitigated via site customization (disabling or enabling of features, configurable implementations).
The change representation format should be configurable, with a reasonable default implementation, which might be LDIF.
It seems that effective changes should not be used to rollback or replay changes, however, their presence for auditing seems important.
dn: name=<name>,uuid=<uuid> changetype: add|modify|delete add|delete|replace: <attribute_name> <attribute_name>: <attribute_value> |
Of note here is that effective changes are identified by the same change number as the parent change.
change_uuid |
change_number |
timestamp |
parent_uuid |
transaction_id |
object_type |
object_uuid |
object_name |
subject_id |
... |
change_representation |
---|---|---|---|---|---|---|---|---|---|---|
c001... |
001 |
20080... |
|
t001... |
group |
u123... |
stem:groupA |
GrouperSystem |
... |
dn: name=stem:groupA,uuid=u123... |
c002... |
001 |
20080... |
c001... |
t001... |
group |
u456... |
stem:groupB |
GrouperSystem |
... |
dn: name=stem:groupB,uuid=u456... |
c003... |
001 |
20080... |
c002... |
t001... |
group |
u789... |
stem:groupC |
GrouperSystem |
... |
dn: name=stem:groupC,uuid=u789... |
c004... |
001 |
20080... |
c003... |
t001... |
group |
u0ab... |
stem:groupD |
GrouperSystem |
... |
dn: name=stem:groupD,uuid=u0ab... |
Again, because a stem's extension is changed, there are 'effective' changes to group names identified by the same change number and parent.
change_uuid |
change_number |
timestamp |
parent_uuid |
transaction_id |
object_type |
object_uuid |
object_name |
subject_id |
... |
change_representation |
---|---|---|---|---|---|---|---|---|---|---|
c001... |
002 |
20080... |
|
t001... |
stem |
s123... |
old_stem |
GrouperSystem |
... |
dn: name=old_stem,uuid=s123... |
c002... |
002 |
20080... |
c001... |
t001... |
group |
u456... |
old_stem:groupA |
GrouperSystem |
... |
dn: name=old_stem:groupA,uuid=u456... |
c003... |
002 |
20080... |
c001... |
t001... |
group |
u789... |
old_stem:groupB |
GrouperSystem |
... |
dn: name=old_stem:groupB,uuid=u789... |
change_uuid |
change_number |
timestamp |
parent_uuid |
transaction_id |
object_type |
object_uuid |
object_name |
subject_id |
... |
change_representation |
---|---|---|---|---|---|---|---|---|---|---|
c001... |
003 |
20080... |
|
t001... |
group |
u123... |
courses:physics2008 |
GrouperSystem |
... |
dn: name=courses:physics2008,uuid=u123... |
change_uuid |
change_number |
timestamp |
parent_uuid |
transaction_id |
object_type |
object_uuid |
object_name |
subject_id |
... |
change_representation |
---|---|---|---|---|---|---|---|---|---|---|
c001... |
004 |
20080... |
|
t001... |
group |
g123... |
stemA:groupA |
GrouperSystem |
... |
dn: name=stemA:groupA, uuid=g123... |
c002... |
005 |
20080... |
|
t001... |
type |
t123... |
groupUUID=g123...,typeUUID=t123... |
GrouperSystem |
... |
dn: groupUUID=g123...,typeUUID=t123... |
c003... |
006 |
20080... |
|
t001... |
group |
g123... |
stemA:groupA |
GrouperSystem |
... |
dn: name=stemA:groupA, uuid=g123... |
change_uuid |
change_number |
timestamp |
parent_uuid |
transaction_id |
object_type |
object_uuid |
object_name |
subject_id |
... |
change_representation |
previous_representation |
---|---|---|---|---|---|---|---|---|---|---|---|
c001... |
005 |
20080... |
|
t001... |
group |
u123... |
stemA:groupA |
GrouperSystem |
... |
dn: name=stemA:groupA, uuid=u123... |
dn: name=stemA:groupA, uuid=u123... |
or omit the previous representation column
change_uuid |
change_number |
timestamp |
parent_uuid |
transaction_id |
object_type |
object_uuid |
object_name |
subject_id |
... |
change_representation |
---|---|---|---|---|---|---|---|---|---|---|
c001... |
007 |
20080... |
|
t001... |
subject |
m123... |
subjectID=old,subjectType=person,subjectSource=s |
GrouperSystem |
... |
dn: subjectID=old,subjectType=person,subjectSource=s,uuid=m123... |
change_uuid |
change_number |
timestamp |
parent_uuid |
transaction_id |
object_type |
object_uuid |
object_name |
subject_id |
... |
change_representation |
---|---|---|---|---|---|---|---|---|---|---|
c001... |
008 |
20080... |
|
t001... |
type |
y123... |
customType |
GrouperSystem |
... |
dn: name=customType,uuid=y123... |
c002... |
008 |
20080... |
c001... |
t001... |
type |
u123... |
groupUUID=g123...,typeUUID=t123... |
GrouperSystem |
... |
dn: groupUUID=g123...,typeUUID=t123... |
change_uuid |
change_number |
timestamp |
parent_uuid |
transaction_id |
object_type |
object_uuid |
object_name |
subject_id |
... |
change_representation |
---|---|---|---|---|---|---|---|---|---|---|
c001... |
009 |
20080... |
|
t001... |
type |
y123... |
customType |
GrouperSystem |
... |
dn: name=customType,uuid=y123... |
c002... |
009 |
20080... |
c001... |
t001... |
type |
u123... |
groupUUID=g123...,typeUUID=t123... |
GrouperSystem |
... |
dn: groupUUID=g123...,typeUUID=t123... |
change_uuid |
change_number |
timestamp |
parent_uuid |
transaction_id |
object_type |
object_uuid |
object_name |
subject_id |
... |
change_representation |
---|---|---|---|---|---|---|---|---|---|---|
c001... |
010 |
20080... |
|
t001... |
stem |
s123... |
root:stem |
GrouperSystem |
... |
dn: name=root:stem, uuid=s123.. |
There probably should be a table which stores, essentially, one row per change. I'm not sure exactly what the columns are, but they should probably provide who-what-when auditing data. Deployers may have quite different needs for searching the changelog, a separate table might be appropriate.
The notification mechanism could be provided by database triggers or a TBD notifier implementation similar to a hook.
I assume that Grouper will provide an api to query the changelog, which perhaps presents changes as LDIF or other configurable representation. However, a simple database schema is desirable to support non-Java clients. Maybe even a web service ?
To maintain the changelog database tables and file system below configurable size or time limits, a scheduled or manually triggered process (probably a command line executable) might be responsible for removing changelog rows and files according to some criteria, e.g. older than a specified date.
Questions or comments? Contact us.