Introduction

This page describes the way we generate reverse LDIF, for each forward operation. A reverse LDIF can be applied on a server in order to revert 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 applied in reverse order. Here we simply focus on how to generate the LDIF for each atomic operation.

Operations

Operations to generate reverse LDIF for:

Operation
AddRequest
DelRequest
ModifyRequest
ModifyDNRequest

AddRequest

Computing the reverse LDIF for an AddRequest is easy: it's a DelRequest where we use the DN of the created entry.

An AddRequest contains those informations :

AddRequest ::= [APPLICATION 8] SEQUENCE {
    entry           LDAPDN,
    attributes      AttributeList }

AttributeList ::= SEQUENCE OF attribute Attribute

For the added entry :

dn: cn=test, dc=example, dc=com
objectclass: top
objectclass: person
cn: test
sn: This is a test

the reverse LDIF will be :

dn: cn=test, dc=example, dc=com
changetype: delete

DelRequest

To produce a reverse LDIF for a DelRequest, we must first read the deleted attribute. We will create a AddRequest based on the read deleted entry :

* read the entry to be deleted
* create a revert ldif AddRequest with this deleted entry
* delete the entry

Considering the existing entry :

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 we have a delRequest which ldif is :

dn: cn=test, dc=example, dc=com
changetype: delete

the reversed ldif should be :

dn: cn=test, dc=example, dc=comchangetype: add
objectclass: top
objectclass: person
cn: test
sn: This is a test
There is still a question regarding operational attributes: should we keep them (in the LDIF)? How do we guarantee that the creatorsName and createTimeStamp attributes are the original ones, instead of the one injected while creating the saved entry? 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.

ModifyRequest

This is the most complex operation. The modification is applied on a specific entry, and can impact one or more attribute, one or more value, but it can't modify an attribute which is part iof the entry RDN.

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
objectclass: top
objectclass: person
cn: test
sn: joe doe
l: USA
ou: apache
ou: acme corp
dn: cn=test, ou=system
changetype: modify
add: ou
ou: BigCompany inc.
-
dn: cn=test, ou=system
objectclass: top
objectclass: person
cn: test
sn: joe doe
l: USA
ou: apache
ou: acme corp
ou: BigCompany inc.
In this case, the ou value is simply added dn: cn=test, ou=system
changetype: modify
delete: ou
ou: BigCompany inc.
-
add dn: cn=test, ou=system
objectclass: top
objectclass: person
cn: test
sn: joe doe
l: USA
dn: cn=test, ou=system
changetype: modify
add: ou
ou: BigCompany inc.
-
dn: cn=test, ou=system
objectclass: top
objectclass: person
cn: test
sn: joe doe
l: USA
ou: BigCompany inc.
The ou attribute and its value has been created dn: cn=test, ou=system
changetype: modify
delete: ou
ou: BigCompany inc.
-
add dn: cn=test, ou=system
objectclass: top
objectclass: person
cn: test
sn: joe doe
l: USA
dn: cn=test, ou=system
changetype: modify
add: cn
cn: test
-
dn: cn=test, ou=system
objectclass: top
objectclass: person
cn: test
sn: joe doe
l: USA
Nothing is done. no reverse, void operation
delete dn: cn=test, ou=system
objectclass: top
objectclass: person
cn: test
sn: joe doe
l: USA
ou: apache
ou: acme corp
dn: cn=test, ou=system
changetype: modify
delete: ou
ou: acme corp
-
dn: cn=test, ou=system
objectclass: top
objectclass: person
cn: test
sn: joe doe
l: USA
ou: apache
The ou=acme corp value has been deleted dn: cn=test, ou=system
changetype: modify
add: ou
ou: acme corp
-
delete dn: cn=test, ou=system
objectclass: top
objectclass: person
cn: test
sn: joe doe
l: USA
ou: apache
ou: acme corp
dn: cn=test, ou=system
changetype: modify
delete: ou
-
dn: cn=test, ou=system
objectclass: top
objectclass: person
cn: test
sn: joe doe
l: USA
The ou attribute has been removed dn: cn=test, ou=system
changetype: modify
add: ou
ou: apache
ou: acme corp
-
delete dn: cn=test, ou=system
objectclass: top
objectclass: person
cn: test
sn: joe doe
l: USA
ou: apache
ou: acme corp
dn: cn=test, ou=system
changetype: modify
delete: ou
ou: apache
ou: acme corp
-
dn: cn=test, ou=system
objectclass: top
objectclass: person
cn: test
sn: joe doe
l: USA
As all the ou values have been removed,
the attribute itself is deleted
dn: cn=test, ou=system
changetype: modify
add: ou
ou: apache
ou: acme corp
-
replace dn: cn=test, ou=system
objectclass: top
objectclass: person
cn: test
sn: joe doe
l: USA
ou: apache
ou: acme corp
dn: cn=test, ou=system
changetype: modify
replace: ou
ou: directory
ou: BigCompany inc.
-
dn: cn=test, ou=system
objectclass: top
objectclass: person
cn: test
sn: joe doe
l: USA
ou: directory
ou: BigCompany inc.
The ou attributes' values are replaced
by the new values.
dn: cn=test, ou=system
changetype: modify
replace: ou
ou: apache
ou: acme corp
-
replace dn: cn=test, ou=system
objectclass: top
objectclass: person
cn: test
sn: joe doe
l: USA
dn: cn=test, ou=system
changetype: modify
replace: ou
ou: apache
ou: acme corp
-
dn: cn=test, ou=system
objectclass: top
objectclass: person
cn: test
sn: joe doe
l: USA
ou: apache
ou: acme corp
Create the ou attribute dn: cn=test, ou=system
changetype: modify
replace: ou
-
replace dn: cn=test, ou=system
objectclass: top
objectclass: person
cn: test
sn: joe doe
l: USA
ou: apache
ou: acme corp
dn: cn=test, ou=system
changetype: modify
replace: ou
-
dn: cn=test, ou=system
objectclass: top
objectclass: person
cn: test
sn: joe doe
l: USA
Delete the ou attribute dn: cn=test, ou=system
changetype: modify
replace: ou
ou: apache
ou: acme corp
-

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 :

cn=test, dc=example, dc=orgobjectClass: personobjectClass: topcn: test

will be transformed to the entry

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 :

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.

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 :

  1. 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
  2. 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
  3. 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
  4. 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
  5. 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
  6. 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
  7. 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
  8. 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
  9. 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
  10. 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
  11. 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
  12. 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.
  13. 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 :

  1. The initial entry does not contain any of the RDN AVA
  2. 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
objectclass: top
objectclass: person
cn: test
sn: This is a test
cn=joe no dn: cn=test, ou=system
changetype: moddn
newrdn: cn=joe
deleteoldrdn: 0
dn: cn=joe, ou=system
objectclass: top
objectclass: person
cn: test
cn: joe
sn: This is a test
dn: cn=joe, ou=system
changetype: moddn
newrdn: cn=test
deleteoldrdn: 1
1.2 dn: cn=test,ou=system
objectclass: top
objectclass: person
cn: test
cn:small
sn: This is a test
cn=small no dn: cn=test, ou=system
changetype: moddn
newrdn: cn=small
deleteoldrdn: 0
dn: cn=small, ou=system
objectclass: top
objectclass: person
cn: test
cn: small
sn: This is a test
dn: cn=small, ou=system
changetype: moddn
newrdn: cn=test
deleteoldrdn: 0
2.1 dn: cn=test,ou=system
objectclass: top
objectclass: person
cn: test
sn: This is a test
cn=joe yes dn: cn=test, ou=system
changetype: moddn
newrdn: cn=joe
deleteoldrdn: 1
dn: cn=joe, ou=system
objectclass: top
objectclass: person
cn: joe
sn: This is a test
dn: cn=joe, ou=system
changetype: moddn
newrdn: cn=test
deleteoldrdn: 1
2.2 dn: cn=test,ou=system
objectclass: top
objectclass: person
cn: test
cn:small
sn: This is a test
cn=small yes dn: cn=test, ou=system
changetype: moddn
newrdn: cn=small
deleteoldrdn: 1
dn: cn=small, ou=system
objectclass: top
objectclass: person
cn: joe
cn: small
sn: This is a test
dn: cn=joe, ou=system
changetype: moddn
newrdn: cn=test
deleteoldrdn: 0
3
dn: cn=small+cn=test,ou=system
objectclass: top
objectclass: person
cn: test
cn: small
sn: This is a test
cn=joe N/A
dn: cn=small+cn=test, ou=system
changetype: moddn
newrdn: cn=joe
deleteoldrdn: 0
dn: cn=joe, ou=system
objectclass: top
objectclass: person
[cn: test]
[cn: small]
cn: joe
sn: This is a test
dn: cn=joe, ou=system
changetype: moddn
deleteoldrdn: 1
newrdn: cn=small+cn=test
4 dn: cn=small+cn=test,ou=system
objectclass: top
objectclass: person
cn: test
cn: small
cn: big
sn: This is a test
cn=big N/A dn: cn=small+cn=test, ou=system
changetype: moddn
newrdn: cn=big
deleteoldrdn: 0
dn: cn=big, ou=system
objectclass: top
objectclass: person
[cn: test]
[cn: small]
cn: big
sn: This is a test
dn: cn=big, ou=system
changetype: moddn
deleteoldrdn: 0
newrdn: cn=small+cn=test
5 cn=small+cn=test
objectclass: top
objectclass: person
cn: test
cn: small
sn: This is a test
cn=test N/A dn: cn=small+cn=test, ou=system
changetype: moddn
newrdn: cn=test
deleteoldrdn: 0
dn: cn=test, ou=system
objectclass: top
objectclass: person
cn: test
[cn: small]
sn: This is a test
dn: cn=test, ou=system
changetype: moddn
deleteoldrdn: 0
newrdn: cn=small+cn=test
6.1 cn=test
objectclass: top
objectclass: person
cn: test
cn: small
sn: This is a test
cn=joe+cn=plumber no dn: cn=test, ou=system
changetype: moddn
newrdn: cn=joe+cn=plumber
deleteoldrdn: 0
dn: cn=joe+cn=plumber, ou=system
objectclass: top
objectclass: person
cn: test
cn: small
cn: joe
cn: plumber
sn: This is a test
dn: cn=joe+cn=plumber, ou=system
changetype: moddn
deleteoldrdn: 1
newrdn: cn=test
6.2 cn=test
objectclass: top
objectclass: person
cn: test
cn: small
sn: This is a test
cn=joe+cn=small no dn: cn=test, ou=system
changetype: moddn
newrdn: cn=joe+cn=small
deleteoldrdn: 0
dn: cn=joe+cn=small, ou=system
objectclass: top
objectclass: person
cn: test
cn: small
cn: joe
cn: plumber
sn: This is a test
dn: cn=joe+cn=small, ou=system
changetype: moddn
deleteoldrdn: 0
newrdn: cn=test

and

dn: cn=test,ou=system
changetype: modify
delete: cn
cn: joe
-
7.1 cn=test
objectclass: top
objectclass: person
cn: test
cn: small
sn: This is a test
cn=joe+cn=plumber yes dn: cn=test, ou=system
changetype: moddn
newrdn: cn=joe+cn=plumber
deleteoldrdn: 1
dn: cn=joe+cn=plumber, ou=system
objectclass: top
objectclass: person
cn: joe
cn: plumber
sn: This is a test
dn: cn=joe+cn=plumber, ou=system
changetype: moddn
deleteoldrdn: 1
newrdn: cn=test
7.2 cn=test
objectclass: top
objectclass: person
cn: test
cn: small
sn: This is a test
cn=joe+cn=small yes dn: cn=test, ou=system
changetype: moddn
newrdn: cn=joe+cn=small
deleteoldrdn: 1
dn: cn=joe+cn=small, ou=system
objectclass: top
objectclass: person
cn: small
cn: joe
cn: plumber
sn: This is a test
dn: cn=joe+cn=small, ou=system
changetype: moddn
deleteoldrdn: 0
newrdn: cn=test

and

dn: cn=test,ou=system
changetype: modify
delete: cn
cn: joe
-
8.1 cn=test
objectclass: top
objectclass: person
cn: test
cn: big
sn: This is a test
cn=small+cn=test no dn: cn=test, ou=system
changetype: moddn
newrdn: cn=small+cn=test
deleteoldrdn: 0
dn: cn=small+cn=test, ou=system
objectclass: top
objectclass: person
cn: small
cn: test
sn: This is a test
dn: cn=small+cn=test, ou=system
changetype: moddn
deleteoldrdn: 1
newrdn: test
8.2 cn=test
objectclass: top
objectclass: person
cn: test
cn: big
sn: This is a test
cn=big+cn=test+
cn=small
no dn: cn=test, ou=system
changetype: moddn
newrdn: cn=big+cn=test+
cn=small
deleteoldrdn: 0
dn: cn=big+cn=test+cn=small,
ou=system
objectclass: top
objectclass: person
cn: big
cn: test
cn: small
sn: This is a test
dn: cn=big+cn=test+cn=small,
ou=system
changetype: moddn
deleteoldrdn: 0
newrdn: test

and

dn: cn=test, ou=system
changetype: modify
cn: delete
cn: small
-
9.1 cn=test
objectclass: top
objectclass: person
cn: test
cn: big
sn: This is a test
cn=small+cn=test yes dn: cn=test, ou=system
changetype: moddn
newrdn: cn=small+cn=test
deleteoldrdn: 1
dn: cn=small+cn=test, ou=system
objectclass: top
objectclass: person
cn: small
cn: test
sn: This is a test
dn: cn=small+cn=test,ou=system
changetype: moddn
deleteoldrdn: 1
newrdn: test
9.2 cn=test
objectclass: top
objectclass: person
cn: test
cn: big
sn: This is a test
cn=big+cn=test+
cn=small
yes dn: cn=test, ou=system
changetype: moddn
newrdn: cn=big+cn=test+
cn=small
deleteoldrdn: 1
dn: cn=big+cn=test+cn=small,
ou=system
objectclass: top
objectclass: person
cn: big
cn: test
cn: small
sn: This is a test
dn: cn=big+cn=test+cn=small,
ou=system
changetype: moddn
deleteoldrdn: 0
newrdn: test

and

dn: cn=test, ou=system
changetype: modify
cn: delete
cn: small
-
10.1 cn=small+cn=test
objectclass: top
objectclass: person
cn: test
cn: big
cn: small
sn: This is a test
cn=joe+cn=plumber no dn: cn=small+cn=test, ou=system
changetype: moddn
newrdn: cn=joe+cn=plumber
deleteoldrdn: 0
dn: cn=joe+cn=plumber, ou=system
objectclass: top
objectclass: person
cn: test
cn: big
cn: small
cn: joe
cn: plumber
sn: This is a test
dn: cn=joe+cn=plumber, ou=system
changetype: moddn
deleteoldrdn: 1
newrdn: cn=small+cn=test
10.2 cn=small+cn=test
objectclass: top
objectclass: person
cn: test
cn: big
cn: small
sn: This is a test
cn=joe+cn=big no dn: cn=small+cn=test, ou=system
changetype: moddn
newrdn: cn=joe+cn=big
deleteoldrdn: 0
dn: cn=joe+cn=big, ou=system
objectclass: top
objectclass: person
cn: test
cn: big
cn: small
cn: joe
sn: This is a test
dn: cn=joe+cn=big, ou=system
changetype: moddn
deleteoldrdn: 0
newrdn: cn=small+cn=test

and

dn: cn=small+cn=test, ou=system
changetype: modify
cn: delete
cn: small
-
11.1 cn=small+cn=test
objectclass: top
objectclass: person
cn: test
cn: big
cn: small
sn: This is a test
cn=joe+cn=plumber yes dn: cn=small+cn=test, ou=system
changetype: moddn
newrdn: cn=joe+cn=plumber
deleteoldrdn: 1
dn: cn=joe+cn=plumber, ou=system
objectclass: top
objectclass: person
cn: big
cn: joe
cn: plumber
sn: This is a test
dn: cn=joe+cn=plumber, ou=system
changetype: moddn
deleteoldrdn: 1
newrdn: cn=small+cn=test
11.2 cn=small+cn=test
objectclass: top
objectclass: person
cn: test
cn: big
cn: small
sn: This is a test
cn=joe+cn=big yes dn: cn=small+cn=test, ou=system
changetype: moddn
newrdn: cn=joe+cn=big
deleteoldrdn: 1
dn: cn=joe+cn=big, ou=system
objectclass: top
objectclass: person
cn: big
cn: joe
sn: This is a test
dn: cn=joe+cn=big, ou=system
changetype: moddn
deleteoldrdn: 0
newrdn: cn=small+cn=test

and

dn: cn=small+cn=test, ou=system
changetype: modify
cn: delete
cn: joe
-
12.1 cn=small+cn=test
objectclass: top
objectclass: person
cn: test
cn: big
cn: small
sn: This is a test
cn=joe+cn=test no dn: cn=small+cn=test, ou=system
changetype: moddn
newrdn: cn=joe+cn=test
deleteoldrdn: 0
dn: cn=joe+cn=test, ou=system
objectclass: top
objectclass: person
cn: test
cn: small
cn: joe
sn: This is a test
dn: cn=joe+cn=test, ou=system
changetype: moddn
deleteoldrdn: 1
newrdn: cn=small+cn=test
12.2 cn=small+cn=test
objectclass: top
objectclass: person
cn: test
cn: big
cn: small
sn: This is a test
cn=big+cn=test no dn: cn=small+cn=test, ou=system
changetype: moddn
newrdn: cn=big+cn=test
deleteoldrdn: 0
dn: cn=big+cn=test, ou=system
objectclass: top
objectclass: person
cn: test
cn: small
cn: big
sn: This is a test
dn: cn=big+cn=test, ou=system
changetype: moddn
deleteoldrdn: 0
newrdn: cn=small+cn=test
13.1 cn=small+cn=test
objectclass: top
objectclass: person
cn: test
cn: big
cn: small
sn: This is a test
cn=joe+cn=test yes dn: cn=small+cn=test, ou=system
changetype: moddn
newrdn: cn=joe+cn=test
deleteoldrdn: 1
dn: cn=joe+cn=test, ou=system
objectclass: top
objectclass: person
cn: test
cn: big
cn: joe
sn: This is a test
dn: cn=joe+cn=test, ou=system
changetype: moddn
deleteoldrdn: 1
newrdn: cn=small+cn=test
13.2 cn=small+cn=test
objectclass: top
objectclass: person
cn: test
cn: big
cn: small
sn: This is a test
cn=big+cn=test yes dn: cn=small+cn=test, ou=system
changetype: moddn
newrdn: cn=big+cn=test
deleteoldrdn: 1
dn: cn=big+cn=test, ou=system
objectclass: top
objectclass: person
cn: test
cn: big
sn: This is a test
dn: cn=big+cn=test, ou=system
changetype: moddn
deleteoldrdn: 0
newrdn: cn=small+cn=test

The following picture represent all the different cases as a decision tree :