Performing RESTful Operations HTTP JSON REST OpenDJ lets you access directory data as JSON resources over HTTP. This chapter demonstrates basic RESTful client operations using the default configuration and sample directory data imported into OpenDJ from Example.ldif. Before trying the examples, enable HTTP access to OpenDJ directory server as described in procedure, To Set Up REST Access to OpenDJ Directory Server.
Understanding the OpenDJ REST API The OpenDJ REST API is built on a common ForgeRock HTTP-based REST API for interacting with JSON Resources. APIs built on this common layer all let you perform the following operations. Create Add a resource that does not yet exist Read Retrieve a single resource Update Replace an existing resource Delete Remove an existing resource Patch Modify part of an existing resource Action Perform a predefined action Query List a set of resources The present implementation in OpenDJ maps JSON resources onto LDAP entries, meaning REST clients can in principle do just about anything an LDAP client can do with directory data. In addition to query string parameters that depend on the operation, the examples in this chapter make use of the following parameters that apply to the JSON resource returned for all operations. _fields=field[,...] Retain only the specified fields in the JSON resource returned. _prettyPrint=true|false Make the JSON resource returned easy for humans to read.
Authenticating Over REST TODO, https://bugster.forgerock.org/jira/browse/OPENDJ-828
Creating Resources There are two ways to create resources. To create a resource using an ID that you specify, perform an HTTP PUT request with headers Content-Type: application/json and If-None-Match: *, and the JSON content of your resource. The following example creates a new user entry with ID newuser. $ curl --request PUT --user kvaughan:bribery --header "Content-Type: application/json" --header "If-None-Match: *" --data '{ "_id": "newuser", "contactInformation": { "telephoneNumber": "+1 408 555 1212", "emailAddress": "newuser@example.com" }, "name": { "familyName": "New", "givenName": "User" }, "displayName": "New User", "manager": [ { "_id": "kvaughan", "displayName": "Kirsten Vaughan" } ] }' http://opendj.example.com:8080/users/newuser?_prettyPrint=true { "_rev" : "000000005b337348", "schemas" : [ "urn:scim:schemas:core:1.0" ], "contactInformation" : { "telephoneNumber" : "+1 408 555 1212", "emailAddress" : "newuser@example.com" }, "_id" : "newuser", "name" : { "familyName" : "New", "givenName" : "User" }, "userName" : "newuser@example.com", "displayName" : "New User", "meta" : { "created" : "2013-04-11T09:58:27Z" }, "manager" : [ { "_id" : "kvaughan", "displayName" : "Kirsten Vaughan" } ] } To create a resource letting the server choose the ID, perform an HTTP POST with _action=create as described in .
Reading a Resource To read a resource, perform an HTTP GET. $ curl --request GET --user kvaughan:bribery http://opendj.example.com:8080/users/newuser?_prettyPrint=true { "_rev" : "000000005b337348", "schemas" : [ "urn:scim:schemas:core:1.0" ], "contactInformation" : { "telephoneNumber" : "+1 408 555 1212", "emailAddress" : "newuser@example.com" }, "_id" : "newuser", "name" : { "familyName" : "New", "givenName" : "User" }, "userName" : "newuser@example.com", "displayName" : "New User", "meta" : { "created" : "2013-04-11T09:58:27Z" }, "manager" : [ { "_id" : "kvaughan", "displayName" : "Kirsten Vaughan" } ] }
Updating Resources To update a resource, perform an HTTP PUT with the changes to the resource. For read-only fields, either include unmodified versions, or omit them from your updated version. The following example adds a manager for Sam Carter. $ curl --request PUT --user kvaughan:bribery --header "Content-Type: application/json" --data '{ "contactInformation": { "telephoneNumber": "+1 408 555 4798", "emailAddress": "scarter@example.com" }, "name": { "familyName": "Carter", "givenName": "Sam" }, "userName": "scarter@example.com", "displayName": "Sam Carter", "groups": [ { "_id": "Accounting Managers" } ], "manager": [ { "_id": "trigden", "displayName": "Torrey Rigden" } ] }' http://opendj.example.com:8080/users/scarter?_prettyPrint=true { "_rev" : "00000000a1923db2", "schemas" : [ "urn:scim:schemas:core:1.0" ], "contactInformation" : { "telephoneNumber" : "+1 408 555 4798", "emailAddress" : "scarter@example.com" }, "_id" : "scarter", "name" : { "familyName" : "Carter", "givenName" : "Sam" }, "userName" : "scarter@example.com", "displayName" : "Sam Carter", "manager" : [ { "_id" : "trigden", "displayName" : "Torrey Rigden" } ], "meta" : { "lastModified" : "2013-04-12T07:42:34Z" }, "groups" : [ { "_id" : "Accounting Managers" } ] } To update a resource only if the resource matches a particular version, use an If-Match: revision header. $ curl --user kvaughan:bribery http://opendj.example.com:8080/users/scarter?_fields=_rev {"_rev":"00000000b017c5b8"} $ curl --request PUT --user kvaughan:bribery --header "If-Match: 00000000b017c5b8" --header "Content-Type: application/json" --data '{ "contactInformation": { "telephoneNumber": "+1 408 555 1212", "emailAddress": "scarter@example.com" }, "name": { "familyName": "Carter", "givenName": "Sam" }, "userName": "scarter@example.com", "displayName": "Sam Carter", "groups": [ { "_id": "Accounting Managers" } ], "manager": [ { "_id": "trigden", "displayName": "Torrey Rigden" } ] }' http://opendj.example.com:8080/users/scarter?_prettyPrint=true { "_rev" : "00000000a1ee3da3", "schemas" : [ "urn:scim:schemas:core:1.0" ], "contactInformation" : { "telephoneNumber" : "+1 408 555 1212", "emailAddress" : "scarter@example.com" }, "_id" : "scarter", "name" : { "familyName" : "Carter", "givenName" : "Sam" }, "userName" : "scarter@example.com", "displayName" : "Sam Carter", "meta" : { "lastModified" : "2013-04-12T07:47:45Z" }, "groups" : [ { "_id" : "Accounting Managers" } ], "manager" : [ { "_id" : "trigden", "displayName" : "Torrey Rigden" } ] }
Deleting Resources To delete a resource, perform an HTTP DELETE on the resource URL. On success, the operation returns the resource you deleted. $ curl --request DELETE --user kvaughan:bribery http://opendj.example.com:8080/users/newuser?_prettyPrint=true { "_rev" : "000000003a5f3cb2", "schemas" : [ "urn:scim:schemas:core:1.0" ], "contactInformation" : { "telephoneNumber" : "+1 408 555 1212", "emailAddress" : "newuser@example.com" }, "_id" : "newuser", "name" : { "familyName" : "New", "givenName" : "User" }, "userName" : "newuser@example.com", "displayName" : "New User", "meta" : { "created" : "2013-04-11T09:58:27Z" }, "manager" : [ { "_id" : "kvaughan", "displayName" : "Kirsten Vaughan" } ] } To delete a resource only if the resource matches a particular version, use an If-Match: revision header. $ curl --user kvaughan:bribery http://opendj.example.com:8080/users/newuser?_fields=_rev {"_rev":"000000006d8d7358"} $ curl --request DELETE --user kvaughan:bribery --header "If-Match: 000000006d8d7358" http://opendj.example.com:8080/users/newuser?_prettyPrint=true { "_rev" : "00000000383f3cae", "schemas" : [ "urn:scim:schemas:core:1.0" ], "contactInformation" : { "telephoneNumber" : "+1 408 555 1212", "emailAddress" : "newuser@example.com" }, "_id" : "newuser", "name" : { "familyName" : "New", "givenName" : "User" }, "userName" : "newuser@example.com", "displayName" : "New User", "meta" : { "created" : "2013-04-11T12:48:48Z" }, "manager" : [ { "_id" : "kvaughan", "displayName" : "Kirsten Vaughan" } ] } To delete a resource and all its children, you must change the configuration, get the REST LDAP gateway or HTTP Connection Handler to reload its configuration, and perform the operation as a user who has the access rights required. The following steps show one way to do this with the HTTP Connection Handler. In this case the LDAP view of the user to delete shows two child entries. $ ldapsearch --port 1389 --baseDN uid=nbohr,ou=people,dc=example,dc=com "(&)" dn dn: uid=nbohr,ou=People,dc=example,dc=com dn: cn=quantum dot,uid=nbohr,ou=People,dc=example,dc=com dn: cn=qubit generator,uid=nbohr,ou=People,dc=example,dc=com In the configuration file for the HTTP Connection Handler, by default /path/to/OpenDJ/config/http-config.json, set "useSubtreeDelete" : true. After this change, only users who have access to request a tree delete can delete resources. Force the HTTP Connection Handler to reread its configuration. $ dsconfig set-connection-handler-prop --hostname opendj.example.com --port 4444 --bindDN "cn=Directory Manager" --bindPassword password --handler-name "HTTP Connection Handler" --set enabled:false --no-prompt $ dsconfig set-connection-handler-prop --hostname opendj.example.com --port 4444 --bindDN "cn=Directory Manager" --bindPassword password --handler-name "HTTP Connection Handler" --set enabled:true --no-prompt Delete as a user who has rights to perform a subtree delete on the resource. $ curl --request DELETE --user kvaughan:bribery http://opendj.example.com:8080/users/nbohr?_prettyPrint=true { "_rev" : "000000003d912113", "schemas" : [ "urn:scim:schemas:core:1.0" ], "contactInformation" : { "telephoneNumber" : "+1 408 555 1212", "emailAddress" : "nbohr@example.com" }, "_id" : "nbohr", "name" : { "familyName" : "Bohr", "givenName" : "Niels" }, "userName" : "nbohr@example.com", "displayName" : "Niels Bohr" }
Patching Resources TODO, https://bugster.forgerock.org/jira/browse/CREST-3
Using Actions OpenDJ implements an action that lets the server choose the resource ID on creation. To use this action, perform an HTTP POST with header Content-Type: application/json, _action=create in the query string, and the JSON content of your resource. The following example creates a new user entry. Lines are folded for readability. TODO, fix pending https://bugster.forgerock.org/jira/browse/OPENDJ-775 $ curl --request POST --user kvaughan:bribery --header "Content-Type: application/json" --data '{ "_id": "newuser", "contactInformation": { "telephoneNumber": "+1 408 555 1212", "emailAddress": "newuser@example.com" }, "name": { "familyName": "New", "givenName": "User" }, "displayName": "New User", "manager": [ { "_id": "kvaughan", "displayName": "Kirsten Vaughan" } ] }' "http://opendj.example.com:8080/users?_action=create&_prettyPrint=true" { "_rev" : "0000000034a23ca7", "schemas" : [ "urn:scim:schemas:core:1.0" ], "contactInformation" : { "telephoneNumber" : "+1 408 555 1212", "emailAddress" : "newuser@example.com" }, "_id" : "newuser", "name" : { "familyName" : "New", "givenName" : "User" }, "userName" : "newuser@example.com", "displayName" : "New User", "meta" : { "created" : "2013-04-11T11:19:08Z" }, "manager" : [ { "_id" : "kvaughan", "displayName" : "Kirsten Vaughan" } ] } TODO, https://bugster.forgerock.org/jira/browse/OPENDJ-695
Querying Resource Collections To query resource collections, perform an HTTP GET with a _queryFilter=filter parameter in your query string. For query operations, your filter expressions are constructed from the following building blocks. Make sure you URL encode the filter expressions, which are shown here without URL encoding to make them easier to read. In these expressions the simplest json-pointer is a field of the JSON resource, such as userName or id. A json-pointer can however point to nested elements as described in the JSON Pointer Internet-Draft. Comparison expressions You can build filters using the following comparison expressions. Request URLs are folded in the following examples to make them easier to read. json-pointer eq json-value Matches when the pointer equals the value, as in the following example. $ curl --user kvaughan:bribery 'http://opendj.example.com:8080 /users?_queryFilter=userName+eq+"bjensen@example.com"&_prettyPrint=true' { "result" : [ { "_rev" : "00000000315fb731", "schemas" : [ "urn:scim:schemas:core:1.0" ], "manager" : [ { "_id" : "trigden", "displayName" : "Torrey Rigden" } ], "contactInformation" : { "telephoneNumber" : "+1 408 555 1862", "emailAddress" : "bjensen@example.com" }, "_id" : "bjensen", "name" : { "familyName" : "Jensen", "givenName" : "Barbara" }, "userName" : "bjensen@example.com", "displayName" : "Barbara Jensen" } ], "resultCount" : 1, "pagedResultsCookie" : null, "remainingPagedResults" : -1 } json-pointer co json-value Matches when the pointer contains the value, as in the following example. $ curl --user kvaughan:bribery 'http://opendj.example.com:8080 /users?_queryFilter=userName+co+"jensen"&_fields=userName&_prettyPrint=true' { "result" : [ { "userName" : "ajensen@example.com" }, { "userName" : "bjensen@example.com" }, { "userName" : "gjensen@example.com" }, { "userName" : "jjensen@example.com" }, { "userName" : "kjensen@example.com" }, { "userName" : "rjensen@example.com" }, { "userName" : "tjensen@example.com" } ], "resultCount" : 7, "pagedResultsCookie" : null, "remainingPagedResults" : -1 } json-pointer sw json-value Matches when the pointer starts with the value, as in the following example. $ curl --user kvaughan:bribery 'http://opendj.example.com:8080 /users?_queryFilter=userName+sw+"ab"&_fields=userName&_prettyPrint=true' { "result" : [ { "userName" : "abarnes@example.com" }, { "userName" : "abergin@example.com" } ], "resultCount" : 2, "pagedResultsCookie" : null, "remainingPagedResults" : -1 } json-pointer lt json-value Matches when the pointer is less than the value, as in the following example. $ curl --user kvaughan:bribery 'http://opendj.example.com:8080 /users?_queryFilter=userName+lt+"ac"&_fields=userName&_prettyPrint=true' { "result" : [ { "userName" : "abarnes@example.com" }, { "userName" : "abergin@example.com" } ], "resultCount" : 2, "pagedResultsCookie" : null, "remainingPagedResults" : -1 } json-pointer le json-value Matches when the pointer is less than or equal to the value, as in the following example. $ curl --user kvaughan:bribery 'http://opendj.example.com:8080 /users?_queryFilter=userName+le+"ad"&_fields=userName&_prettyPrint=true' { "result" : [ { "userName" : "abarnes@example.com" }, { "userName" : "abergin@example.com" }, { "userName" : "achassin@example.com" } ], "resultCount" : 3, "pagedResultsCookie" : null, "remainingPagedResults" : -1 } json-pointer gt json-value Matches when the pointer is greater than the value, as in the following example. $ curl --user kvaughan:bribery 'http://opendj.example.com:8080 /users?_queryFilter=userName+gt+"tt"&_fields=userName&_prettyPrint=true' { "result" : [ { "userName" : "ttully@example.com" }, { "userName" : "tward@example.com" }, { "userName" : "wlutz@example.com" } ], "resultCount" : 3, "pagedResultsCookie" : null, "remainingPagedResults" : -1 } json-pointer ge json-value Matches when the pointer is greater than or equal to the value, as in the following example. $ curl --user kvaughan:bribery 'http://opendj.example.com:8080 /users?_queryFilter=userName+ge+"tw"&_fields=userName&_prettyPrint=true' { "result" : [ { "userName" : "tward@example.com" }, { "userName" : "wlutz@example.com" } ], "resultCount" : 2, "pagedResultsCookie" : null, "remainingPagedResults" : -1 } Presence expression json-pointer pr matches any resource on which the json-pointer is present, as in the following example. $ curl --user kvaughan:bribery 'http://opendj.example.com:8080 /users?_queryFilter=userName%20pr&_prettyPrint=true' { "result" : [ { "_rev" : "000000002210a544", "schemas" : [ "urn:scim:schemas:core:1.0" ], "manager" : [ { "_id" : "scarter", "displayName" : "Sam Carter" } ], "contactInformation" : { "telephoneNumber" : "+1 408 555 9445", "emailAddress" : "abarnes@example.com" }, "_id" : "abarnes", "name" : { "familyName" : "Barnes", "givenName" : "Anne-Louise" }, "userName" : "abarnes@example.com", "displayName" : "Anne-Louise Barnes" },... many entries omitted ... "_id" : "newuser", "name" : { "familyName" : "New", "givenName" : "User" }, "userName" : "newuser@example.com", "displayName" : "New User", "meta" : { "created" : "2013-03-26T10:52:42Z" } } ], "resultCount" : 152, "pagedResultsCookie" : null, "remainingPagedResults" : -1 } Literal expressions true matches any resource in the collection. false matches no resource in the collection. In other words you can list all resources in a collection as in the following example. $ curl --user kvaughan:bribery 'http://opendj.example.com:8080 /groups?_queryFilter=true&_fields=displayName&_prettyPrint=true' { "result" : [ { "displayName" : "Accounting Managers" }, { "displayName" : "Directory Administrators" }, { "displayName" : "HR Managers" }, { "displayName" : "PD Managers" }, { "displayName" : "QA Managers" } ], "resultCount" : 5, "pagedResultsCookie" : null, "remainingPagedResults" : -1 } Complex expressions You can combine expressions using boolean operators and, or, and ! (not), using parentheses, (expression), to group expressions. The following example queries resources with last name Jensen and manager name starting with Bar. Notice that the filters use the JSON pointers name/familyName and manager/displayName to identify the fields that are nested inside the name and manager objects. $ curl --user kvaughan:bribery 'http://opendj.example.com:8080 /users?_queryFilter=(userName+co+"jensen"+and+manager/displayName+sw+"Sam") &_fields=displayName&_prettyPrint=true' { "result" : [ { "displayName" : "Jody Jensen" }, { "displayName" : "Ted Jensen" } ], "resultCount" : 2, "pagedResultsCookie" : null, "remainingPagedResults" : -1 } You can have the server sort JSON resources before it returns them by using the _sortKeys[+-]=field[,...] query string. TODO, pending implementation https://bugster.forgerock.org/jira/browse/OPENDJ-702 You can page through search results using the following query string parameters. TODO, pending implementation https://bugster.forgerock.org/jira/browse/OPENDJ-701 __pagedResultsCookie=string TODO __pagedResultsOffset=string TODO __pagedResultsCookie=string TODO