Overview

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).

Logical Representation of Changes

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.

LDIF change representation

dn: name=<name>,uuid=<uuid>
changetype: add|modify|delete
add|delete|replace: <attribute_name>
<attribute_name>: <attribute_value>

Examples

Group A is a member of Group B which is a member of Group C, and subject D is added as a member to Group A. Membership in Group C grants subjectA the ADMIN privilege on Group D.

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...
changetype: modify
add: members
members: subjectId=subjectA, subjectType=person, subjectSource=source, uuid=m123...
-

c002...

001

20080...

c001...

t001...

group

u456...

stem:groupB

GrouperSystem

...

dn: name=stem:groupB,uuid=u456...
changetype: modify
add: members
members: subjectId=subjectA, subjectType=person, subjectSource=source, uuid=m123...
-

c003...

001

20080...

c002...

t001...

group

u789...

stem:groupC

GrouperSystem

...

dn: name=stem:groupC,uuid=u789...
changetype: modify
add: members
members: subjectId=subjectA, subjectType=person, subjectSource=source, uuid=m123...
-

c004...

001

20080...

c003...

t001...

group

u0ab...

stem:groupD

GrouperSystem

...

dn: name=stem:groupD,uuid=u0ab...
changetype: modify
add: admins
admins: subjectId=subjectA, subjectType=person, subjectSource=source, uuid=m123...
-

Change a stem's extension

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...
changetype: modrdn
newrdn: name=new_stem
deleteoldrdn: 1
-

c002...

002

20080...

c001...

t001...

group

u456...

old_stem:groupA

GrouperSystem

...

dn: name=old_stem:groupA,uuid=u456...
changetype: modrdn
newrdn: name=new_stem:groupA
deleteoldrdn: 1
-

c003...

002

20080...

c001...

t001...

group

u789...

old_stem:groupB

GrouperSystem

...

dn: name=old_stem:groupB,uuid=u789...
changetype: modrdn
newrdn: name=new_stem:groupB
deleteoldrdn: 1
-

Set a group's description and a custom field.

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...
changetype: modify
replace: description
description: new description
-
replace: instructor
instructor: whoever
-

Create a group, and add a custom type and attribute

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...
changetype: add
extension: groupA
displayExtension: Group A
displayName: Group A
name: stemA:groupA
members: subjectId=subjectA, subjectType=person, subjectSource=source, uuid=m123...
admins: subjectId=subjectA, subjectType=person, subjectSource=source, uuid=m123...
parentStem: s123...
creatorID: 0abc...
createTime: 1189447100294
uuid: g123...
-

c002...

005

20080...

 

t001...

type

t123...

groupUUID=g123...,typeUUID=t123...

GrouperSystem

...

dn: groupUUID=g123...,typeUUID=t123...
changetype: add
groupUUID: g123...
typeUUID: t123...
-

c003...

006

20080...

 

t001...

group

g123...

stemA:groupA

GrouperSystem

...

dn: name=stemA:groupA, uuid=g123...
changetype: modify
mail: groupa@memphis.edu
-

Delete a group

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...
changetype: delete
-

dn: name=stemA:groupA, uuid=u123...
attrExtension: groupA
attrDisplayExtension: Group A
...
-

or omit the previous representation column

Change a member's subjectId

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...
changetype: modrdn
newrdn: subjectID=new
deleteoldrdn: 1
-

Create a type and add it to a group

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...
changetype: add
name: customType
creatorUUID: u456...
createTime: 1189445439003
isAssignable: f
isInternal: f
-

c002...

008

20080...

c001...

t001...

type

u123...

groupUUID=g123...,typeUUID=t123...

GrouperSystem

...

dn: groupUUID=g123...,typeUUID=t123...
changetype: add
groupUUID: g123...
typeUUID: t123...
-

Delete a type

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...
changetype: delete
-

c002...

009

20080...

c001...

t001...

type

u123...

groupUUID=g123...,typeUUID=t123...

GrouperSystem

...

dn: groupUUID=g123...,typeUUID=t123...
changetype: delete
-

Create a stem

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..
changetype: add
uuid: s123..
parent_stem: s456...
name: root:stem
description: yet another stem
displayExtension: stem
displayName: Stem
extension: stem
creatorID: 0abc...
createTime: 1189445439384
-

Table schema

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.

Notifications

The notification mechanism could be provided by database triggers or a TBD notifier implementation similar to a hook.

Data Interfaces

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 ?

Rolling the Changelog

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.


     (question) Questions or comments? (info) Contact us.