...
This page describes the way we generate reverse LDIF, for each operationsforward operation. Reverse A reverse LDIF can be applied on a server in order to revert some changes done. The only condition is that an operation.
In this document we do not consider what is required to revert a series of operations which have ordering requirements: i.e. they must be played applied in the exact reverse order.
We should not store a revert LDIF if the operation has failed (for any reason).
Operations
Here we simply focus on how to generate the LDIF for each atomic operation.
Operations
Operations to generate reverse LDIF forHere are the operations we are considering :
Operation |
AddRequest |
DelRequest |
ModifyRequest |
ModifyDNRequest |
AddRequest
Computing the revert reverse LDIF for an AddRequest is easy: it's a DelRequest where we use the DN of the created entry.
...
No Format |
---|
dn: cn=test, dc=example, dc=com changetype: delete |
DelRequest
To produce a revert reverse LDIF for a DelRequest, we must first read the deleted attribute. We will create a Add Request AddRequest based on the read deleted entry :
...
No Format |
---|
dn: cn=test, dc=example, dc=com objectclass: top objectclass: person cn: test sn: This is a testcreatorsName: dc=admin, ou=systemcreateTimestamp: 20071010150132ZmodifiersName: dc=admin, ou=systemmodifyTimestamp: 20071010150133Z |
if if we have a delRequest which ldif is :
No Format |
---|
dn: cn=test, dc=example, dc=com changetype: delete |
the the reversed ldif should be :
No Format |
---|
dn: cn=test, dc=example, dc=comchangetype: add objectclass: top objectclass: person cn: test sn: This is a test |
Note |
---|
There is still a question regarding the operational attributes: should we keep them (in the LDIF)? How de do we guarantee that the creatorName creatorsName and createTimeStamp attributes are the original ones, instead of the one injected while creating the saved entry? We will have some impact on the OperationaAttributesInterceptor..Operational attribute state needs to be captured out of band (from the LDIF data) and is the responsibility of the change log service to manage, capture and track. This information is made available to higher level interfaces which handle all this. So at this level we need not worry: the LDIF's generated have no operational attributes in them as would be normally expected for a valid LDIF. |
To be able to build this reverse ldif, we need to read the previous entry before the deletion.
...
We have three kind of modifications : add, delete and replace. They are applied in the order they are found in the Modify request, so the reverse LDIF must store them in reverse order too.
Depending on the modified values, each basic operation may have some different semantic. The following table present all the possible actions :
modification | initial entry | imported Ldif | resulting entry | Comments | Reverse LDIF |
add | dn: cn=test, ou=system | dn: cn=test, ou=system | dn: cn=test, ou=system | In this case, the ou value is simply added | dn: cn=test, ou=system |
add | dn: cn=test, ou=system | dn: cn=test, ou=system | dn: cn=test, ou=system | The ou attribute and its value has been created | dn: cn=test, ou=system |
add | dn: cn=test, ou=system | dn: cn=test, ou=system | dn: cn=test, ou=system | Nothing is done. | no reverse, void operation |
delete | dn: cn=test, ou=system | dn: cn=test, ou=system | dn: cn=test, ou=system | The ou=acme corp value has been deleted | dn: cn=test, ou=system |
delete | dn: cn=test, ou=system | dn: cn=test, ou=system | dn: cn=test, ou=system | The ou attribute has been removed | dn: cn=test, ou=system |
delete | dn: cn=test, ou=system | dn: cn=test, ou=system | dn: cn=test, ou=system | As all the ou values have been removed, | dn: cn=test, ou=system |
replace | dn: cn=test, ou=system | dn: cn=test, ou=system | dn: cn=test, ou=system | The ou attributes' values are replaced | dn: cn=test, ou=system |
replace | dn: cn=test, ou=system | dn: cn=test, ou=system | dn: cn=test, ou=system | Create the ou attribute | dn: cn=test, ou=system |
replace | dn: cn=test, ou=system | dn: cn=test, ou=system | dn: cn=test, ou=system | Delete the ou attribute | dn: cn=test, ou=system |
ModifyDNRequest
This request is used to move entries or to rename entries or to move and rename entries. Its counterpart in a ldif file is a 'changetype: moddn' or a 'changetype: modrdn' operation (moddn or modrdn are synonymous).
We will separate the ModifyDN into 3 different cases :
- A simple move operation : we change the superior, the RDN remains the same
- A rename operation : the RDN is changed
- A move and rename operation : a combinaison of both previous operations
Move operation
This is the simplest one : we change the superior, without changing the entry's attributes nor the RDN
the following entry :
No Format |
---|
cn=test, dc=example, dc=orgobjectClass: personobjectClass: topcn: test
|
will be transformed to the entry
No Format |
---|
cn=test, ou=systemobjectClass: personobjectClass: topcn: test
|
if we change the superior from dc=example,dc=com to ou=system
and the revert operation will be :
No Format |
---|
cn=test, ou=systemchangetype: moddnnewRdn: cn=test deleteoldrdn: 0
newSuperior: dc=example,dc=com
|
Rename operation
It's a bit more complex. We have to take care of multiple value RDN (RDN like cn=small+sn=test) and also to the existing attributes. The deleteOldRdn flag must also be take into account when dealing with the revert operation.
There are four cases :
- we don't change the superior and we don't delete the old RDN
- we don't change the superior and we delete the old RDN
- we change the superior and we don't delete the old RDN
- we change the superior and we delete the old RDN
The following table gives an example for each of those cases applied on the initial entry :
No Format |
---|
dn: cn=test, dc=example, dc=com
objectclass: top
objectclass: person
cn: test
sn: This is a test
|
The new superior will be 'ou=system'
the new RDN will be 'cn=joe'
Generally speaking, the revert operation may be a simple one or a combinaison of a moddn and a add/delete operation, depending on the case.
There are 13 cases :
- The initial RDN is simple, the target RDN is simple, we don't delete the oldRDN : We just have to remove the newly added AVA if it's not present into the original entry
- The initial RDN is simple, the target RDN is simple, we delete the oldRDN : We have to add the old AVA and to remove the newly added AVA if it's not present into the original entry
- The initial RDN is composite, the target is simple, they don't overlap, we just have to remove the newly added AVA if it's not present into the original entry
- The initial RDN is composite, the target is simple, they don't overlap, we have to add the original AVAs, and to remove the newly added AVAs if it's not present into the original entry
- The initial RDN is composite, the target is simple, they overlap, we delete the oldRDN : We have to add the removed AVAs except the one which is in the new RDN
- The initial RDN is simple, the target RDN is composite, they don't overlap, we don't delete the oldRDN : We have to remove all the new AVAs
- The initial RDN is simple, the target RDN is composite, they don't overlap, we delete the oldRDN : We have to remove all the new AVAs and to add the old AVA
- The initial RDN is simple, the target RDN is composite, they overlap, we don't delete the oldRDN : We have to remove all the new AVAs except the one whgich is in the old RDN
- The initial RDN is simple, the target RDN is composite, they overlap, we delete the oldRDN : We have to remove all the new AVAs except the one which is in the old RDN
- The initial RDN is composite, the target RDN is composite, they don't overlap, we don't delete the oldRDN : We have to remove all the new AVAs which are not present in the original entry
- The initial RDN is composite, the target RDN is composite, they don't overlap, we delete the oldRDN : We have to remove all the new AVAs which are not present in the original entry and add all the removed AVAs from the original entry
- The initial RDN is composite, the target RDN is composite, they overlap, we don't delete the oldRDN : We have to remove all the new AVAs which are not present in the original entry.
- The initial RDN is composite, the target RDN is composite, they overlap, we delete the oldRDN : We have to remove all the new AVAs which are not present in the original entry and add all the removed AVAs from the original entry
Case 11 and 13 are equivalent, so are case 12 and 14. They need two operations to be applied.
We also have sub cases, depending in the presence (or not) of the new RDN AVAs into the initial entry. Rge following table expose all the different cases (28), assuming that the two subcases are :
- The initial entry does not contain any of the RDN AVA
- The initial entry contains at least one of the RDN AVA which is not part of the original RDN
One special and important point : we can't use the deleteOldDN for composite operation, as this may lead to the deletion of a mandatory attribute, hence generating an error when applying the revert operation.
case | orginal entry | target RDN | deleteoldrdn | forward ldif | resulting entry | reverse ldif | |||||
1.1 | dn: cn=test,ou=system | cn=joe | no | dn: cn=test, ou=system | dn: cn=joe, ou=system | dn: cn=joe, ou=system | |||||
1.2 | dn: cn=test,ou=system | cn=small | no | dn: cn=test, ou=system | dn: cn=small, ou=system | dn: cn=small, ou=system | |||||
2.1 | dn: cn=test,ou=system | cn=joe | yes | dn: cn=test, ou=system | dn: cn=joe, ou=system | dn: cn=joe, ou=system | |||||
2.2 | dn: cn=test,ou=system | cn=small | yes | dn: cn=test, ou=system | dn: cn=small, ou=system | dn: cn=joe, ou=system | |||||
3 | dn: cn=small+cn=test,ou=system | cn=joe | N/A | dn: cn=small+cn=test, ou=system | dn: cn=joe, ou=system | dn: cn=joe, ou=system | |||||
4 | dn: cn=small+cn=test,ou=system | cn=big | N/A | dn: cn=small+cn=test, ou=system | dn: cn=big, ou=system | dn: cn=big, ou=system | |||||
5 | cn=small+cn=test | cn=test | N/A | dn: cn=small+cn=test, ou=system | dn: cn=test, ou=system | dn: cn=test, ou=system | |||||
6.1 | cn=test | cn=joe+cn=plumber | no | dn: cn=test, ou=system | dn: cn=joe+cn=plumber, ou=system | ||||||
case | deleteoldrdn | new superior | modifying ldif | resulting entry | reverse ldif | ||||||
1 | no | none | dn: cn=test, dc=example, dc=com | dn: cn=joe, dc=example, dc=com | dn: cn=joe, dc+cn=exampleplumber, dcou=com system | 1 | yes | test | |||
6.2 | cn=test | cn=joe+cn=small | no none | dn: cn=test, dc=example, dc=com , ou=system | dn: cn=joe+cn=small, ou=system | dn: cn=joe+cn=small, ou=system | |||||
7.1 | cn=test | cn=joe+cn=plumber | yes | dn: cn=test, ou=system | dn: cn=joe, dc=example, dc=com +cn=plumber, ou=system | dn: cn=joe+cn=plumber, ou=system | |||||
7.2 | cn=test | cn=joe+cn=small | yes | dn: cn=test, ou=system | dn: cn=joe+cn=small, ou=system | dn: cn=joe, dc=example, dc=com +cn=small, ou=system | |||||
8.1 | cn=test | cn=small+cn=test | no | dn: cn=test, ou=system | dn: cn=small+cn=test, ou=system | dn: cn=small+cn=test, ou=system | |||||
8.2 | cn=test | cn=big+cn=test+ | no | dn: cn=test, ou=system | dn: cn=big+cn=test+cn=small, | dn: cn=big+cn=test+cn=small, | |||||
9.1 | cn=test | cn=small+cn=test | yes | dn: cn=test, ou=system | dn: cn=small+cn=test, ou=system | dn: cn=small+cn=test | 1 | no | =test,ou=system | ||
9.2 | cn=test | cn=big+cn=test+ | yes | dn: cn=test, ou=system | dn: cn=big+cn=test+cn=small, | dn: cn=big+cn=test+cn=small, | |||||
10.1 | cn=small+cn=test | cn=joe+cn=plumber | no | dn: cn=small+cn=test, ou=system | dn: cn=joe+cn=plumber, ou=system | dn: cn=joe+cn=plumber, ou=system | 1 | yes | ou=system | dn: cn=test, dc=example, dc=org | cn=small+cn=test |
10.2 | cn=small+cn=test | cn=joe+cn=big | no | dn: cn=small+cn=test, ou=system | dn: cn=joe+cn=big, ou=system | dn: cn=joe+cn=big, ou=system | |||||
11.1 | cn=small+cn=test | cn=joe+cn=plumber | yes | dn: cn=small+cn=test, ou=system | dn: cn=joe+cn=plumber, ou=system | dn: cn=joe+cn=plumber, ou=system | |||||
11.2 | cn=small+cn=test | cn=joe+cn=big | yes | dn: cn=small+cn=test, ou=system | dn: cn=joe+cn=big, ou=system | dn: cn=joe+cn=big, ou=system | |||||
12.1 | cn=small+cn=test | cn=joe+cn=test | no | dn: cn=small+cn=test, ou=system | dn: cn=joe+cn=test dn: cn=joe, ou=system | dn: cn=joe+cn=test, ou=system | |||||
12.2 | cn=small+cn=test | cn=big+cn=test | no | dn: cn=small+cn=test, ou=system |
Computing the reverse LDIF for a ModifyDN request follows the algorithm :
big+cn=test | dn: cn=big+cn=test, ou=system | dn: cn=big+cn=test, ou=system | ||||
13.1 | cn=small+cn=test | cn=joe+cn=test | yes | dn: cn=small+cn=test, ou=system | dn: cn=joe+cn=test, ou=system | dn: cn=joe+cn=test, ou=system |
13.2 | cn=small+cn=test | cn=big+cn=test | yes | dn: cn=small+cn=test, ou=system | dn: cn=big+cn=test, ou=system | dn: cn=big+cn=test, ou=system |
The following picture represent all the different cases as a decision tree :
reverseLdif.deleteOldRdn = true
if modifyDn.newSuperior not empty
then reverseLdif.newSuperior = modifyDn.dn minus the modifyDN.dn.getRDN
reverseLdif.newRdn = modifyDn.dn.getRDN