LDAP: Matching against the current time in OpenDJ

In LDAP, attributes have different syntaxes. The one used to indicate date and time is the GeneralizedTime, a string representation of the date and time, typically expressed in GMT time. For example, when an entry is modified, the server maintains the modifytimestamp attribute and sets a value like 20110825120001Z (for 2011, Aug 25, 12:00:01 GMT).

LDAP client applications often have to search for entries based on these date and time attributes, whether it is to find the entries that have been modified , or had the password changed recently… The way it is typically done, is the following: get from the system the current date, add or substract some fixed time (for example if you want to know the entries modified in the last 10 minutes), transform  to a GeneralizedTime, use that string in a search filter: (modifyTimestamp >= 20110825130000Z). If the application repeats that search a minute later, it has to recompute the value again, and again…

Ideally what application writers would like is to express the filter as an expression like (modifyTimestamp>=${CurrentTime} – 10 mn). However this is not compliant with LDAP. The proper way to solve this is to use extensible matching rules, and for that purpose, we’ve added 2 “relative time” matching rules in OpenDJ, the Open source LDAP Directory services for Java: one for “lower than” and one for “greater than”.

matchingrules: ( NAME ( 'relativeTimeLTOrderingMatch' 'relativeTimeOrderingMatch.lt' )
matchingrules: ( NAME ( 'relativeTimeGTOrderingMatch' 'relativeTimeOrderingMatch.gt' )

The way the matching rules work is pretty simple : (attribute:MatchingRule:=Offset), where the offset is a signed integer follow by its unit, either s for seconds, m for minutes, h for hours, d for days or w for weeks.

You can translate a statement to “is Attribute greater than (or lower than) CurrentTime +/- Offset”

(lastLoginTime: will match all entries who have a lastLoginTime value smaller than the Current Time minus 4 weeks, i.e. all entries who have a lastLoginTime older than 4 weeks.

(pwdExpirationTime: will match all entries that have pwdExpirationTime greater than the Current Time plus 5 days, i.e. all entries that will expire in more than 5 days.

The true benefit of those matching rules, is actually when expressing policies in the OpenDJ server, for example for granting or denying access based on some attribute with a generalizedTime syntax, such as last login time, pwdChangedTime, modifyTimeStamp …

For example, imagine an auxiliary objectClass representing a service, with some specific attributes including an expiration date : validUntil. Now, you want to allow these attributes to be read only if the expiration date is not passed.

aci: (targetattr="serviceAttr1 || serverAttr2")(targetfilter="(validUntil:")
  (version 3.0; acl "Read Valid service attributes"; allow (read, search, compare)

As you can see, this is a good way to hide (deny access to) stale data in a directory server, and to simplify client applications that need to search for entries based on some generalizedTime attributes. For example, consider using these “relative time” matching rules for all your audit queries for expired or unused accounts.

Finally, remember that the OpenDJ directory server doesn’t allow unindexed searches by default. So you might also want to create an index for the “relative time” matching rules. That’s a 2 steps process :

Define the index

$ bin/dsconfig create-local-db-index --backend-name userRoot --set index-type:extensible \
 --set index-extensible-matching-rule: \
 --set index-extensible-matching-rule: \
 --index-name createTimestamp -h localhost -p 4444 \
 -D cn=Directory\ Manager -w secret12 -n -X

Rebuild the index

$ bin/rebuild-index -b dc=example,dc=com -i createTimestamp \
 -h localhost -p 4444 -D cn=directory\ manager -w secret12 -X