Directories for the REST of us

Great introduction to the use of OpenDJ’s new RESTful service: search, modify and manage LDAP directory entries over HTTP/S!

Product Manager Ludo Poitou and Architect Matt Swift at last year’s ForgeRock Open Identity Stack Summit. Go over to the link and sign up to get access to links to video of over 20 other talks given at the Summit, and to get on the mailing list for upcoming events (you may also get a sales call, but if you’re a real IT pro you’ll know how to handle those both firmly and with kindness — you may even find a support relationship with ForgeRock wouldn’t be such a bad thing…).

FYI the RESTful interface comes up on port 8080 by default. I always change this to 8389 to avoid collisions with other software (it seems all Java app servers want 8080 by default).

The service is enabled in $DSHOME/config/config.ldif. The entry and attribute mapping is set out in $DSHOME/config/http-config.json.

Here are a few examples using the default mappings:

Look for everyone with the first name “Michael”:

http://myuser:mypass@dldap.example.com:8389/users?_queryFilter=(displayName+sw+"Michael")&_fields=_id,displayName

The guy with the uid “myuser”:

http://myuser:mypass@ldap.example.com:8389/users?_queryFilter=(_id+eq+"myuser")&_prettyPrint=true

Get the uids and full names of everyone managed by Mike Michaels:

http://myuser:mypass@ldap.example.com:8389/users?_queryFilter=manager/displayName+eq+"Mike Michaels"&_fields=_id,displayName&_prettyPrint=true

To enable the service find “dn: cn=HTTP Connection Handler,cn=Connection Handlers,cn=config” and set ds-cfg-enabled to “true”. To change the port from the default of 8080, edit ds-cfg-listen-port.

The shipping http-config.json is really bare bones and not very useful. Here’s one of my test versions with what I think are some reasonable changes (this was a preliminary effort to test the server’s functionality, so it is in no may complete — I’m also not at all happy with how this formatted under my current theme):

{
    // The Rest2LDAP authentication filter configuration. The filter will be
    // disabled if the configuration is not present. Upon successful
    // authentication the filter will create a security context containing the
    // following principals:
    //
    // "dn" - the DN of the user if known (may not be the case for sasl-plain)
    // "id" - the username used for authentication.
    "authenticationFilter" : {
        // Indicates whether the filter should allow HTTP BASIC authentication.
        "supportHTTPBasicAuthentication" : true,
        
        // Indicates whether the filter should allow alternative authentication
        // and, if so, which HTTP headers it should obtain the username and
        // password from.
        "supportAltAuthentication"        : true, 
        "altAuthenticationUsernameHeader" : "X-OpenIDM-Username",
        "altAuthenticationPasswordHeader" : "X-OpenIDM-Password",
        
        // The search parameters to use for "search-simple" authentication. The
        // %s filter format parameters will be substituted with the
        // client-provided username, using LDAP filter string character escaping.
        "searchBaseDN"         : "dc=example,dc=com",
        "searchScope"          : "sub", // Or "one".
        "searchFilterTemplate" : "(&(objectClass=inetOrgPerson)(uid=%s))"
        
        // TODO: support for HTTP sessions?
    },

    // The Rest2LDAP Servlet configuration.
    "servlet" : {
        // The REST APIs and their LDAP attribute mappings.
        "mappings" : {
            "/users" : {
                "baseDN"              : "ou=people,dc=example,dc=com",
                "readOnUpdatePolicy"  : "controls",
                "useSubtreeDelete"    : false,
                "usePermissiveModify" : false,
                "etagAttribute"       : "etag",
                "namingStrategy"      : {
                    "strategy"    : "clientDNNaming",
                    "dnAttribute" : "uid"
                },
                "additionalLDAPAttributes" : [
                    {
                        "type" : "objectClass",
                        "values" : [
                            "top",
                            "person",
                            "organizationalPerson",
                            "inetOrgPerson"
                        ]
                    }
                ],
                "attributes" : {
                    "schemas"     : { "constant" : [ "urn:scim:schemas:core:1.0" ] },
                    "_id"         : { "simple"   : { "ldapAttribute" : "uid", "isSingleValued" : true, "isRequired" : true, "writability" : "createOnly" } },
                    "_rev"        : { "simple"   : { "ldapAttribute" : "etag", "isSingleValued" : true, "writability" : "readOnly" } },
		    // Default mapped mail as "username", how Amazon! Better to map mail to emailAddress and pass "displayName" as "displayName"
                    "emailAddress": { "simple"   : { "ldapAttribute" : "mail", "isSingleValued" : true, "writability" : "readOnly" } },
                    "displayName" : { "simple"   : { "ldapAttribute" : "displayName", "isSingleValued" : true, "isRequired" : true } },
		    // Additional useful attributes
                    "jobTitle"    : { "simple"   : { "ldapAttribute" : "title", "isSingleValued" : true, "isRequired" : false } },
                    "company"     : { "simple"   : { "ldapAttribute" : "o", "isSingleValued" : true, "isRequired" : false } },
                    "country"     : { "simple"   : { "ldapAttribute" : "c", "isSingleValued" : true, "isRequired" : true } },
		    // Name object definition example, labels are typically json-y.
                    "name"        : { "object"   : {
                        "givenName"  : { "simple" : { "ldapAttribute" : "givenName", "isSingleValued" : true } },
                        "familyName" : { "simple" : { "ldapAttribute" : "sn", "isSingleValued" : true, "isRequired" : true } }
                    } },
 		    // manager is a dn value but we're displaying uid? Find way to get dn instead, much more useful.
                    "manager"     : { "reference" : {
                        "ldapAttribute" : "manager",
                        "baseDN"        : "ou=people,dc=example,dc=com",
                        "primaryKey"    : "uid",
                        "mapper"         : { "object" : {
                            "_id"         : { "simple"   : { "ldapAttribute" : "uid", "isSingleValued" : true, "isRequired" : true } },
                            "displayName" : { "simple"   : { "ldapAttribute" : "displayName", "isSingleValued" : true, "writability" : "readOnlyDiscardWrites" } }
                        } }
                    } },
                    "groups"     : { "reference" : {
                        "ldapAttribute" : "isMemberOf",
                        "baseDN"        : "ou=groups,dc=example,dc=com",
                        "writability"   : "readOnly",
                        "primaryKey"    : "cn",
                        "mapper"        : { "object" : {
                            "_id"         : { "simple"   : { "ldapAttribute" : "cn", "isSingleValued" : true } }
                        } }
                    } },
                    "contactInformation" : { "object" : {
                        "telephoneNumber" : { "simple" : { "ldapAttribute" : "telephoneNumber", "isSingleValued" : true } },
                        "emailAddress"    : { "simple" : { "ldapAttribute" : "mail", "isSingleValued" : true } }
                    } },
                    "meta"        : { "object" : {
                        "created"      : { "simple" : { "ldapAttribute" : "createTimestamp", "isSingleValued" : true, "writability" : "readOnly" } },
                        "lastModified" : { "simple" : { "ldapAttribute" : "modifyTimestamp", "isSingleValued" : true, "writability" : "readOnly" } }
                    } }
                }
            },
            "/groups" : {
                "baseDN"              : "ou=groups,dc=example,dc=com",
                "readOnUpdatePolicy"  : "controls",
                "useSubtreeDelete"    : false,
                "usePermissiveModify" : true,
                "etagAttribute"       : "etag",
                "namingStrategy"      : {
                    "strategy"    : "clientDNNaming",
                    "dnAttribute" : "cn"
                },
                "additionalLDAPAttributes" : [
                    {
                        "type" : "objectClass",
                        "values" : [
                            "top",
                            "groupOfUniqueNames"
                        ]
                    }
                ],
                "attributes" : {
                    "schemas"     : { "constant" : [ "urn:scim:schemas:core:1.0" ] },
                    "_id"         : { "simple"   : { "ldapAttribute" : "cn", "isSingleValued" : true, "isRequired" : true, "writability" : "createOnly" } },
                    "_rev"        : { "simple"   : { "ldapAttribute" : "etag", "isSingleValued" : true, "writability" : "readOnly" } },
                    "commonName" : { "simple"   : { "ldapAttribute" : "cn", "isSingleValued" : true, "isRequired" : true, "writability" : "readOnly" } },
                    "members"    : { "reference" : {
                        "ldapAttribute" : "uniqueMember",
                        "baseDN"        : "dc=arrow,dc=com",
                        "primaryKey"    : "uid",
                        "mapper"        : { "object" : {
                            "_id"         : { "simple"   : { "ldapAttribute" : "uid", "isSingleValued" : true, "isRequired" : true } },
                            "commonName"  : { "simple"   : { "ldapAttribute" : "cn", "isSingleValued" : true, "writability" : "readOnlyDiscardWrites" } }
                        } }
                    } },
                    "meta"        : { "object" : {
                        "created"      : { "simple" : { "ldapAttribute" : "createTimestamp", "isSingleValued" : true, "writability" : "readOnly" } },
                        "lastModified" : { "simple" : { "ldapAttribute" : "modifyTimestamp", "isSingleValued" : true, "writability" : "readOnly" } }
                    } }
                }
            }
        }
    }
}
This entry was posted in Development, Identity Management, System Administration on by .

About phil

My name is Phil Lembo. In my day job I’m an enterprise IT architect for a leading distribution and services company. The rest of my time I try to maintain a semi-normal family life in the suburbs of Raleigh, NC. E-mail me at philipATlembobrothersDOTcom. The opinions expressed here are entirely my own and not those of my employers, past, present or future (except where I quote others, who will need to accept responsibility for their own rants).