Sunday, February 27, 2011

How to obtain an OID to be used in a custom object class for LDAP

This is related to my previous post on introducing custom object class/attributes to a LDAP instance.

According to this specification on defining LDAP attributes, object classes, we need to have a unique OID for that attribute/object class , for that to be uniquely identified by the LDAP directory server. 
Hence the sole purpose of using an OID, is to avoid it being conflicted with existing object class/attributes in the LDAP server or those which will be added to the LDAP server in the future.

We can use a made up OID for experimental use, but it is recommended to use a registered OID which is unique, if we are going to ship it with a product as described in detail here and here.

There are several ways of obtaining an OID among which one easy and free way of obtaining it is from IANA. For that you need to fill and submit this application. Once you are assigned an OID, it will be displayed in the public PEN registry.

Another interesting thing is that there is a test branch (starting from 2.25) created so that we can use an unregistered OID with a generated UUID appended.

You may follow the following steps to obtain an OID from the test branch  easily.

1. Obtain a generated UUID from here. (When you click "Get a new UUID" button in that page, you will get a hexadecimal value, which you can not use as it is)
2. Convert it to the proper format. You can easily get this converted in OID format by clicking the "Register your UUID as an OID" button in the resulting page.
3. Step 2 will redirect you to the OID registration page which will show the corresponding OID for the generated UUID in step 1. Here, you can copy the OID and use it without registering or you can proceed registering this generated OID.

Saturday, February 12, 2011

How to introduce custom attributes to LDAP

I. WHY...?
In identity and user management, we come across the need to store various information regarding users in a user store. When we use LDAP as the user store, we store user information as 'attributes' related with user 'entries'.

Although there are common and default set of attributes supported by many LDAP implementations, they might not cater all our requirements. Hence, LDAP servers (like ApacheDS, OpenLDAP) give us the flexibility and extendibility to define our own attributes and introduce them to the LDAP server instance and use them as we need.

II. PROBLEM...
So let me briefly explain how I came across the requirement mentioned in the subject.

WSO2 Identity Server now uses embedded ApacheDS LDAP as its default user store. All the other features which were supported by default JDBC user store earlier, needed to be supported with LDAP as well. 

When supporting features such as signing up with open id and info card, we need to store the attributes defined in open id and info card profiles. In the aspect of storing user attributes, LDAP user store is little bit challenging than JDBC user store--because for an attribute to be used in LDAP entries, it should have been introduced to that particular LDAP instance by default or by us. 

For an example, open id user profile contains the attributes such as 'date of birth' ,'gender', 'postal code' etc..which are not supported by ApacheDS by default.

III. THEORY...
Now let me first take you though the relationship among attributes, object classes, schemas and entries in LDAP,  in order to provide some back ground knowledge.

We store some entity like a user, in an entry in the LDAP. To construct an entry, we use one or more object classes. An object class defines the structure of a particular entry such as what attributes it contains etc. Object classes are grouped into a schema which is known to LDAP server.

Well..let me put it in other words... To use an attribute in an entry, that attribute should be defined in an object class, and that object class must be used in the entry definition. Further, the object class should be included in the schema of the LDAP server.

That implies--> if we want to use an custom attribute/s we should write a custom object class including that attribute/s, and we need to introduce that object class to the schema of the LDAP server instance that we are going to use.
There are two ways that we can introduce a custom object class to a LDAP server.
  1. Writing an object class in a ldif (ldap data interchange format)  file and importing it to the LDAP schema.
  2. Programmatic way--you can use JNDI for that.
In the 'walk through' section of this post, I will describe the first way.
There are three types of object classes:
  1. Structural: used to create entries--an entry should have at least one structural object class. (can have more than one as well)
  2. Auxiliary: basically used to add additional attributes to an entry created with some other structural object class. an entry can have one or more auxiliary object classes. But an entry can not construct only with auxiliary object classes.
  3. Abstract: these basically sit at the top of the object class hierarchies. an example is: 'top' object class in any LDAP.
Another nice concept in object classes is 'inheritance' like in OOP. If we inherit our custom object class from an existing object class, we automatically get all the attributes defined in that object class.
For an example, if we take the famous object class 'inetOrgPerson' which is used to create user entries, it has a nice inheritance hierarchy as below:
             'top'-->'person'-->'organizationalPerson'-->'inetOrgPerson'

That's it..I think this background knowledge is pretty enough for the following walk through.

IV. WALK THROUGH...
Following is a simple walk through which includes steps on how to introduce  a custom object class with new set of attributes to ApacheDS LDAP and use them to store user entries.

Pre-requisites: install Apache Directroy Server and ApacheDS Studio

How to write a ldif file:
  • for a structural object class
dn: cn=schema
changetype: modify
add: attributeTypes
attributeTypes: ( 2.25.128424792425578037463837247958458780603.1
        NAME 'dateOfBirth'
        EQUALITY caseIgnoreMatch
        SUBSTR caseIgnoreSubstringsMatch
        SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )
attributeTypes: ( 2.25.128424792425578037463837247958458780603.2
        NAME 'gender'
        EQUALITY caseIgnoreMatch
        SUBSTR caseIgnoreSubstringsMatch
        SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{1024} )
-
add: objectClasses
objectClasses: ( 2.25.128424792425578037463837247958458780603.3
    NAME 'samplePerson'
    DESC 'samplePerson'
    SUP inetOrgPerson
    STRUCTURAL
    MAY  ( dateOfBirth $ gender)
 )
Well.. there are several properties that we have to specify when defining attributes and object classes for LDAP as you can see in the above sample ldif. You can learn about all of them in detail from the RFC here.

Here I will describe only the most important ones.
lines 4,9,16: specifies unique OIDs for each attribute and object class. Sole purpose of an OID is to avoid these attributes/object class being conflicted with any other in the LDAP instance. I describe how to obtain an OID in detail in this  post. You can use the above OID for experimental purpose or just google on 'how to obtain an OID'

line 19: specifies that 'samplePerson' object class inherits from 'inetOrgPerson' object class which usually already exists in any LDAP server.

line 20: specifies this is a structural object class so that you can use it to construct an entry in the LDAP.

Overall, we have defined a new object class called 'samplePerson' which groups two attributes called 'dateOfBirth' and 'gender' and the entries made out of this object class may contain all the attributes from 'inetOrgPerson' super class and the two attributes that we defined.
  • for an auxiliary object class
dn: cn=schema
dn: cn=schema
changetype: modify
add: attributeTypes
attributeTypes: ( 2.25.128424792425578037463837247958458780603.4
        NAME 'role'
        EQUALITY caseIgnoreMatch
        SUBSTR caseIgnoreSubstringsMatch
        SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{1024} )
-
add: objectClasses
objectClasses: ( 2.25.128424792425578037463837247958458780603.5
    NAME 'employedPerson'
    DESC 'empolyedPerson'
    SUP top
    AUXILIARY
    MAY  ( role )
 )
This defines an auxiliary (line 15) object class called 'employedPerson' which groups an attribute called 'role'.

How to add a custom object class to ApacheDS:
  1. Save the above two samples in two files with the extension .ldif.
  2. Start ApacheDS and ApacheDS Studio.
  3. Connect to ApacheDS through ApacheDS studio.
  4. Import two ldif files to the schema of ApacheD LDAP server through ApacheD studio's LDIF import wizard as shown in following image: right click on 'ou=schema'-->Import-->LDIF will give you the following wizard:
   5. Restart the ApacheDS and reconnect to it through ApacheDS Studio for the changes to make effect.    
  
How to use custom object classes/ attributes in user entries:
  1. Get the wizard to create a new entry: (right click 'ou=system'-->New-->NewEntry.)
  2. Search the custom object classes that we added under 'Available Object Classes' on the left hand side of the wizard. Then add both 'samplePerson' and 'employedPerson' object classes as shown in image below:
   There you can see all the inherited object classes are also listed when we added the two that we introduced.
   3. Complete adding the user entry as you usually do.
   4. Try to add attributes to the created user entry. In the attribute search list, now you will be able to see the newly introduced custom attributes.
    Well..now we have come to the end of our long blog post. Hope you got a clear understanding on how to introduce custom object classes and attributes to LDAP.
    Hmm...I'm tired writing this long post...but I hope you are not tired reading it which is my pleasure... :)