Professional Documents
Culture Documents
Guide
Version 3
Mark Craig
ForgeRock AS
201 Mission St., Suite 2900
San Francisco, CA 94105, USA
+1 415-599-1100 (US)
www.forgerock.com
This work is licensed under the Creative Commons Attribution-NonCommercial-NoDerivs 3.0 Unported License.
To view a copy of this license, visit http://creativecommons.org/licenses/by-nc-nd/3.0/ or send a letter to Creative Commons, 444 Castro Street, Suite 900, Mountain View, California, 94041, USA.
ForgeRock is the trademark of ForgeRock Inc. or its subsidiaries in the U.S. and in other countries. Trademarks are the property of their respective owners.
UNLESS OTHERWISE MUTUALLY AGREED BY THE PARTIES IN WRITING, LICENSOR OFFERS THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY KIND CONCERNING
THE WORK, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, INCLUDING, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE,
NONINFRINGEMENT, OR THE ABSENCE OF LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS, WHETHER OR NOT DISCOVERABLE. SOME JURISDICTIONS DO
NOT ALLOW THE EXCLUSION OF IMPLIED WARRANTIES, SO SUCH EXCLUSION MAY NOT APPLY TO YOU.
EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE LAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR
EXEMPLARY DAMAGES ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
DejaVu Fonts
Bitstream Vera Fonts Copyright
Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved. Bitstream Vera is a trademark of Bitstream, Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy of the fonts accompanying this license ("Fonts") and associated documentation files (the "Font Software"), to reproduce and distribute
the Font Software, including without limitation the rights to use, copy, merge, publish, distribute, and/or sell copies of the Font Software, and to permit persons to whom the Font Software is furnished to do so,
subject to the following conditions:
The above copyright and trademark notices and this permission notice shall be included in all copies of one or more of the Font Software typefaces.
The Font Software may be modified, altered, or added to, and in particular the designs of glyphs or characters in the Fonts may be modified and additional glyphs or characters may be added to the Fonts, only if
the fonts are renamed to names not containing either the words "Bitstream" or the word "Vera".
This License becomes null and void to the extent applicable to Fonts or Font Software that has been modified and is distributed under the "Bitstream Vera" names.
The Font Software may be sold as part of a larger software package but no copy of one or more of the Font Software typefaces may be sold by itself.
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL BITSTREAM OR THE GNOME FOUNDATION BE LIABLE FOR
ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR
OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE.
Except as contained in this notice, the names of Gnome, the Gnome Foundation, and Bitstream Inc., shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Font Software
without prior written authorization from the Gnome Foundation or Bitstream Inc., respectively. For further information, contact: fonts at gnome dot org.
Arev Fonts Copyright
Copyright (c) 2006 by Tavmjong Bah. All Rights Reserved.
Permission is hereby granted, free of charge, to any person obtaining a copy of the fonts accompanying this license ("Fonts") and associated documentation files (the "Font Software"), to reproduce and distribute
the modifications to the Bitstream Vera Font Software, including without limitation the rights to use, copy, merge, publish, distribute, and/or sell copies of the Font Software, and to permit persons to whom the
Font Software is furnished to do so, subject to the following conditions:
The above copyright and trademark notices and this permission notice shall be included in all copies of one or more of the Font Software typefaces.
The Font Software may be modified, altered, or added to, and in particular the designs of glyphs or characters in the Fonts may be modified and additional glyphs or characters may be added to the Fonts, only if
the fonts are renamed to names not containing either the words "Tavmjong Bah" or the word "Arev".
This License becomes null and void to the extent applicable to Fonts or Font Software that has been modified and is distributed under the "Tavmjong Bah Arev" names.
The Font Software may be sold as part of a larger software package but no copy of one or more of the Font Software typefaces may be sold by itself.
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL TAVMJONG BAH BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE.
Except as contained in this notice, the name of Tavmjong Bah shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Font Software without prior written authorization from
Tavmjong Bah. For further information, contact: tavmjong @ free . fr.
Admonition graphics by Yannick Lung. Free for commerical use. Available at Freecns Cumulus.
Table of Contents
Preface ............................................................................................................. v
1. Using This Guide .................................................................................. v
2. Formatting Conventions ....................................................................... vi
3. Accessing Documentation Online ......................................................... vii
4. Joining the ForgeRock Community ...................................................... vii
1. Performing RESTful Operations .................................................................... 1
1.1. About ForgeRock Common REST ....................................................... 2
1.2. Authenticating Over REST ............................................................... 13
1.3. Creating Resources .......................................................................... 15
1.4. Reading a Resource ......................................................................... 16
1.5. Updating Resources ......................................................................... 17
1.6. Deleting Resources .......................................................................... 20
1.7. Patching Resources .......................................................................... 23
1.8. Using Actions ................................................................................... 29
1.9. Querying Resource Collections ......................................................... 32
2. Performing LDAP Operations ...................................................................... 43
2.1. Command-Line Tools ........................................................................ 43
2.2. Searching the Directory ................................................................... 49
2.3. Comparing Attribute Values ............................................................. 57
2.4. Updating the Directory .................................................................... 57
2.5. Changing Passwords ........................................................................ 67
2.6. Configuring Default Settings ............................................................ 69
2.7. Authenticating To the Directory Server ............................................ 70
2.8. Configuring Proxied Authorization .................................................... 73
2.9. Authenticating Using a Certificate .................................................... 76
3. Using LDAP Schema ................................................................................... 91
3.1. Getting Schema Information ............................................................. 92
3.2. Respecting LDAP Schema ................................................................ 95
3.3. Abusing LDAP Schema ..................................................................... 98
3.4. Standard Schema Included With OpenDJ Server ............................... 99
4. Working With Groups of Entries ............................................................... 103
4.1. Creating Static Groups ................................................................... 104
4.2. Creating Dynamic Groups .............................................................. 107
4.3. Creating Virtual Static Groups ....................................................... 109
4.4. Looking Up Group Membership ...................................................... 111
4.5. Nesting Groups Within Groups ....................................................... 111
4.6. Configuring Referential Integrity .................................................... 113
5. Working With Virtual and Collective Attributes ......................................... 117
5.1. Virtual Attributes ........................................................................... 117
5.2. Collective Attributes ....................................................................... 120
6. Working With Referrals ............................................................................ 129
6.1. About Referrals .............................................................................. 130
6.2. Managing Referrals ........................................................................ 130
iii
iv
Preface
This guide shows you how to develop scripts that use OpenDJ tools.
If you are building a Java-based LDAP client application, refer to the OpenDJ
LDAP SDK Developer's Guide instead.
In reading and following the instructions in this guide, you will learn how to:
Access OpenDJ directory server by using REST APIs over HTTP
Access OpenDJ directory server using the LDAP tools delivered with the server
Use LDAP schema
Work with standard LDAP groups and OpenDJ-specific groups
Work with LDAP collective attributes and OpenDJ virtual attributes
Work with LDAP referrals in search results
Formatting Conventions
If you are not yet familiar with OpenDJ directory server installation, read the
OpenDJ Installation Guide first.
Using command-line tools
LDAP and directory services
Basic OpenDJ server configuration
Some examples in this guide require OpenDJ configuration steps.
HTTP, JavaScript Object Notation (JSON), and web applications
Formatting Conventions
Most examples in the documentation are created in GNU/Linux or Mac OS
X operating environments. If distinctions are necessary between operating
environments, examples are labeled with the operating environment name in
parentheses. To avoid repetition file system directory names are often given
only in UNIX format as in /path/to/server, even if the text applies to C:\path\to
\server as well.
Absolute path names usually begin with the placeholder /path/to/. This path
might translate to /opt/, C:\Program Files\, or somewhere else on your system.
Command line, terminal sessions are formatted as follows:
$ echo $JAVA_HOME
/path/to/jdk
"_rev" : "000000005b337348",
"_id" : "newuser",
...
vi
class Test {
public static void main(String [] args) {
System.out.println("This is a program listing.");
}
}
vii
viii
Chapter 1
Server in the OpenDJ Administration Guide. The examples in this chapter use
HTTP, but the procedure also shows how to set up HTTPS access to the server.
Interface stability: Evolving (See SectionI.2, ForgeRock Product Interface
Stability in the OpenDJ Reference.)
The OpenDJ REST API is built on a common ForgeRock HTTP-based REST API
for interacting with JSON Resources. All APIs built on this common layer let you
perform the following operations.
1.1
1.1.1
1.1.2
1.1.3
_pagedResultsCookie
_pagedResultsOffset
_prettyPrint
_queryExpression
_queryFilter
_queryId
_sortKeys
_totalPagedResultsPolicy
Note
Some parameter values are not safe for URLs, so URL-encode
parameter values as necessary.
Continue reading for details about how to use each parameter.
1.1.4
1.1.5
Create
There are two ways to create a resource, either with an HTTP POST or with an
HTTP PUT.
To create a resource using POST, perform an HTTP POST with the query string
parameter _action=create and the JSON resource as a payload. Accept a JSON
response. The server creates the identifier if not specified:
POST /users?_action=create HTTP/1.1
Host: example.com
Accept: application/json
Content-Length: ...
Content-Type: application/json
{ JSON resource }
Read
To create a resource using PUT, perform an HTTP PUT including the casesensitive identifier for the resource in the URL path, and the JSON resource as a
payload. Use the If-None-Match: * header. Accept a JSON response:
PUT /users/some-id HTTP/1.1
Host: example.com
Accept: application/json
Content-Length: ...
Content-Type: application/json
If-None-Match: *
{ JSON resource }
The _id and content of the resource depend on the server implementation. The
server is not required to use the _id that the client provides. The server response
to the create request indicates the resource location as the value of the Location
header.
If you include the If-None-Match header, its value must be *. In this case, the
request creates the object if it does not exist, and fails if the object does exist.
If you include the If-None-Match header with any value other than *, the server
returns an HTTP 400 Bad Request error. For example, creating an object with
If-None-Match: revision returns a bad request error. If you do not include IfNone-Match: *, the request creates the object if it does not exist, and updates the
object if it does exist.
Parameters
You can use the following parameters:
_prettyPrint=true
_fields=field[,field...]
1.1.6
Read
To retrieve a single resource, perform an HTTP GET on the resource by its casesensitive identifier (_id) and accept a JSON response:
GET /users/some-id HTTP/1.1
Host: example.com
Accept: application/json
Update
Parameters
You can use the following parameters:
_prettyPrint=true
_fields=field[,field...]
_mimeType=mime-type
Some resources have fields whose values are multi-media resources such as a
profile photo for example.
By specifying both a single field and also the mime-type for the response
content, you can read a single field value that is a multi-media resource.
In this case, the content type of the field value returned matches the mimetype that you specify, and the body of the response is the multi-media
resource.
The Accept header is not used in this case. For example, Accept: image/png
does not work. Use the _mimeType query string parameter instead.
1.1.7
Update
To update a resource, perform an HTTP PUT including the case-sensitive
identifier (_id) for the resource with the JSON resource as a payload. Use
the If-Match: _rev header to check that you are actually updating the version
you modified. Use If-Match: * if the version does not matter. Accept a JSON
response:
PUT /users/some-id HTTP/1.1
Host: example.com
Accept: application/json
Content-Length: ...
Content-Type: application/json
If-Match: _rev
{ JSON resource }
Delete
_fields=field[,field...]
1.1.8
Delete
To delete a single resource, perform an HTTP DELETE by its case-sensitive
identifier (_id) and accept a JSON response:
DELETE /users/some-id HTTP/1.1
Host: example.com
Accept: application/json
Parameters
You can use the following parameters:
_prettyPrint=true
_fields=field[,field...]
1.1.9
Patch
To patch a resource, send an HTTP PATCH request including the patch for the
resource as the payload. Optionally set the If-Match header to the revision if
the patch should only operate on that version of the resource. Accept a JSON
response:
Action
Note
Some HTTP client libraries do not support the HTTP PATCH
operation. Make sure that the library you use supports HTTP
PATCH before using this REST operation.
For example, the Java Development Kit HTTP client does
not support PATCH as a valid HTTP method. Instead, the
method HttpURLConnection.setRequestMethod("PATCH") throws
ProtocolException.
Parameters
You can use the following parameters:
_prettyPrint=true
_fields=field[,field...]
1.1.10
Action
Actions are a means of extending common REST APIs and are defined by the
resource provider, so the actions you can use depend on the implementation.
The standard action indicated by _action=create is described in Section1.1.5,
"Create".
Parameters
You can use the following parameters. Other parameters might depend on the
specific action implementation:
Query
_prettyPrint=true
_fields=field[,field...]
1.1.11
Query
To query a resource collection (or resource container if you prefer to think of
it that way), perform an HTTP GET and accept a JSON response, including at
least a _queryExpression, _queryFilter, or _queryId parameter. These parameters
cannot be used together:
GET /users?_queryFilter=true HTTP/1.1
Host: example.com
Accept: application/json
The server returns the result as a JSON object including a "results" array and
other fields related to the query string parameters that you specify.
Parameters
You can use the following parameters:
_queryFilter=filter-expression
Query filters request that the server return entries that match the filter
expression. You must URL-escape the filter expression.
The string representation is summarized as follows. Continue reading for
additional explanation:
Query
Expr
OrExpr
AndExpr
NotExpr
PrimaryExpr
ComparisonExpr
PresenceExpr
LiteralExpr
Pointer
OpName
JsonValue
STRING
UTF8STRING
=
=
=
=
=
=
=
=
=
=
OrExpr
AndExpr ( 'or' AndExpr ) *
NotExpr ( 'and' NotExpr ) *
'!' PrimaryExpr | PrimaryExpr
'(' Expr ')' | ComparisonExpr | PresenceExpr | LiteralExpr
Pointer OpName JsonValue
Pointer 'pr'
'true' | 'false'
JSON pointer
'eq' | # equal to
'co' | # contains
'sw' | # starts with
'lt' | # less than
'le' | # less than or equal to
'gt' | # greater than
'ge' | # greater than or equal to
STRING # extended operator
= NUMBER | BOOLEAN | '"' UTF8STRING '"'
= ASCII string not containing white-space
= UTF-8 string possibly containing white-space
Note that white space, double quotes ("), parentheses, and exclamation
characters need URL encoding in HTTP query strings.
A simple filter expression can represent a comparison, presence, or a literal
value.
For comparison expressions use json-pointer comparator json-value, where
the comparator is one of the following:
eq
co
sw
lt
le
gt
ge
(equals)
(contains)
(starts with)
(less than)
(less than or equal to)
(greater than)
(greater than or equal to)
10
Query
_pagedResultsCookie=string
The string is an opaque cookie used by the server to keep track of the
position in the search results. The server returns the cookie in the JSON
response as the value of pagedResultsCookie.
In the request _pageSize must also be set and non-zero. You receive the
cookie value from the provider on the first request, and then supply the
cookie value in subsequent requests until the server returns a null cookie,
meaning that the final page of results has been returned.
The _pagedResultsCookie parameter is supported when used with the
_queryFilter parameter. The _pagedResultsCookie parameter is not
guaranteed to work when used with the _queryExpression and _queryId
parameters.
The _pagedResultsCookie and _pagedResultsOffset parameters are mutually
exclusive, and not to be used together.
_pagedResultsOffset=integer
When _pageSize is non-zero, use this as an index in the result set indicating
Return query results in pages of this size. After the initial request, use
_pagedResultsCookie or _pageResultsOffset to page through the results.
_totalPagedResultsPolicy=string
When a _pageSize is specified, and non-zero, the server calculates the
"totalPagedResults", in accordance with the totalPagedResultsPolicy,
_sortKeys=[+-]field[,[+-]field...]
_prettyPrint=true
11
_fields=field[,field...]
Return only the specified fields in each element of the "results" array in the
response.
The field values are JSON pointers. For example if the resource is {"parent":
{"child":"value"}}, parent/child refers to the "child":"value".
1.1.12
12
410 Gone
The requested resource is no longer available, and will not become available
again. This can happen when resources expire for example.
412 Precondition Failed
The resource's current version does not match the version provided.
415 Unsupported Media Type
The request is in a format not supported by the requested resource for the
requested method.
428 Precondition Required
The resource requires a version, but no version was supplied in the request.
500 Internal Server Error
The server encountered an unexpected condition that prevented it from
fulfilling the request.
501 Not Implemented
The resource does not support the functionality required to fulfill the request.
503 Service Unavailable
The requested resource was temporarily unavailable. The service may have
been disabled, for example.
1.2
"code" : 401,
"reason" : "Unauthorized",
"message" : "Unauthorized"
HTTP status code 401 indicates that the request requires user authentication.
To prevent OpenDJ directory server from requiring authentication, set the HTTP
connection handler property authentication-required to false, as in the following
example:
13
$ dsconfig \
set-connection-handler-prop \
--hostname opendj.example.com \
--port 4444 \
--bindDN "cn=Directory Manager" \
--bindPassword password \
--handler-name "HTTP Connection Handler" \
--set authentication-required:false \
--no-prompt \
--trustAll
By default, both the HTTP connection handler and also the REST to LDAP
gateway allow HTTP Basic authentication and HTTP header-based authentication
in the style of OpenIDM. The authentication mechanisms translate HTTP
authentication to LDAP authentication to the directory server.
When you install OpenDJ either with generated sample user entries or with data
from Example.ldif, the relative distinguished name (DN) attribute for sample user
entries is the user ID (uid) attribute. For example, the DN and user ID for Babs
Jensen are:
dn: uid=bjensen,ou=People,dc=example,dc=com
uid: bjensen
Given this pattern in the user entries, the default REST to LDAP configuration
translates the HTTP user name to the LDAP user ID. User entries are found
1
directly under ou=People,dc=example,dc=com. In other words, Babs Jensen
authenticates as bjensen (password: hifalutin) over HTTP. The corresponding
LDAP bind DN is uid=bjensen,ou=People,dc=example,dc=com.
HTTP Basic authentication works as shown in the following example:
$ curl \
--user bjensen:hifalutin \
http://opendj.example.com:8080/users/bjensen
"_rev" : "0000000016cbb68c",
...
The alternative HTTP Basic username:password@ form in the URL works as shown
in the following example:
1
In general, REST to LDAP mappings require that LDAP entries mapped to JSON resources be immediate
subordinates of the mapping's baseDN.
14
Creating Resources
$ curl \
http://bjensen:hifalutin@opendj.example.com:8080/users/bjensen
"_rev" : "0000000016cbb68c",
...
"_rev" : "0000000016cbb68c",
...
If the directory data is laid out differently or if the user names are email
addresses rather than user IDs, for example, then you must update the
configuration in order for authentication to work.
The REST to LDAP gateway can also translate HTTP user name and password
authentication to LDAP PLAIN SASL authentication. Likewise, the gateway
falls back to proxied authorization as necessary, using a root DN authenticated
connection to LDAP servers. See AppendixA, in the OpenDJ Reference for
details on all configuration choices.
1.3
Creating Resources
There are two alternative 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 shows you how to create a new user entry with ID
newuser:
15
Reading a Resource
$ 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
"_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 and let the server choose the ID, perform an HTTP POST
with _action=create as described in Section1.8, "Using Actions".
1.4
Reading a Resource
To read a resource, perform an HTTP GET as shown in the following example:
16
Updating Resources
$ curl \
--request GET \
--user kvaughan:bribery \
http://opendj.example.com:8080/users/newuser
1.5
"_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.
Use an If-Match header to ensure the resource already exists. For read-only
fields, either include unmodified versions, or omit them from your updated
version.
To update a resource regardless of the revision, use an If-Match: * header. The
following example adds a manager for Sam Carter:
17
Updating Resources
$ curl \
--request PUT \
--user kvaughan:bribery \
--header "Content-Type: application/json" \
--header "If-Match: *" \
--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
"_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" : "2015-09-29T10:24:01Z"
},
"groups" : [ {
"_id" : "Accounting Managers"
} ]
18
Updating Resources
$ curl \
--user kvaughan:bribery \
http://opendj.example.com:8080/users/scarter?_fields=_rev
{"_id":"scarter","_rev":"revision"}
$ curl \
--request PUT \
--user kvaughan:bribery \
--header "If-Match: revision" \
--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
"_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" : "2015-09-29T10:23:27Z"
},
"groups" : [ {
"_id" : "Accounting Managers"
} ],
"manager" : [ {
"_id" : "trigden",
"displayName" : "Torrey Rigden"
} ]
19
Deleting Resources
1.6
Deleting Resources
To delete a resource, perform an HTTP DELETE on the resource URL. The
operation returns the resource you deleted as shown in the following example:
$ curl \
--request DELETE \
--user kvaughan:bribery \
http://opendj.example.com:8080/users/newuser
"_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 IfMatch: revision header as shown in the following example:
20
Deleting Resources
$ curl \
--user kvaughan:bribery \
http://opendj.example.com:8080/users/newuser?_fields=_rev
{"_id":"newuser","_rev":"revision"}
$ curl \
--request DELETE \
--user kvaughan:bribery \
--header "If-Match: revision" \
http://opendj.example.com:8080/users/newuser
"_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 of its children, you must change the configuration,
get the REST to 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 example, the LDAP view of the user to delete shows two child entries as
seen in the following example:
$ ldapsearch --port 1389 --baseDN uid=nbohr,ou=people,dc=example,dc=com "(&)" dn
dn: uid=nbohr,ou=People,dc=example,dc=com
1. In the configuration file for the HTTP connection handler, by default /path/
to/opendj/config/http-config.json, set "useSubtreeDelete" : true.
21
Deleting Resources
Note
After this change, only users who have access to request a
tree delete can delete resources.
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 \
--trustAll
$ 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 \
--trustAll
3. Request the delete as a user who has rights to perform a subtree delete on
the resource as shown in the following example:
22
Patching Resources
$ curl \
--request DELETE \
--user kvaughan:bribery \
http://opendj.example.com:8080/users/nbohr
1.7
"_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
OpenDJ lets you patch JSON resources, updating part of the resource rather
than replacing it. For example, you could change Babs Jensen's email address by
issuing an HTTP PATCH request as in the following example:
23
Patching Resources
$ curl \
--user kvaughan:bribery \
--request PATCH \
--header "Content-Type: application/json" \
--data '[
{
"operation": "replace",
"field": "/contactInformation/emailAddress",
"value": "babs@example.com"
}
]' \
http://opendj.example.com:8080/users/bjensen
"_rev" : "00000000f3fdd370",
"schemas" : [ "urn:scim:schemas:core:1.0" ],
"contactInformation" : {
"telephoneNumber" : "+1 408 555 1862",
"emailAddress" : "babs@example.com"
},
"_id" : "bjensen",
"name" : {
"familyName" : "Jensen",
"givenName" : "Barbara"
},
"userName" : "babs@example.com",
"displayName" : "Barbara Jensen",
"meta" : {
"lastModified" : "2013-05-13T14:35:31Z"
},
"manager" : [ {
"_id" : "trigden",
"displayName" : "Torrey Rigden"
} ]
Notice in the example that the data sent specifies the type of patch operation,
the field to change, and a value that depends on the field you change and on
the operation. A single-valued field takes an object, boolean, string, or number
depending on its type, whereas a multi-valued field takes an array of values.
Getting the type wrong results in an error. Also notice that the patch data is itself
an array. This makes it possible to patch more than one part of the resource by
using a set of patch operations in the same request.
OpenDJ supports four types of patch operations:
add
The add operation ensures that the target field contains the value provided,
creating parent fields as necessary.
If the target field is single-valued and a value already exists, then that value
is replaced with the value you provide. Note that you do not get an error
when adding a value to a single-valued field that already has a value. A
single-valued field is one whose value is not an array (an object, string,
boolean, or number).
24
Patching Resources
If the target field is multi-valued, then the array of values you provide is
merged with the set of values already in the resource. New values are added,
and duplicate values are ignored. A multi-valued field takes an array value.
remove
The remove operation ensures that the target field does not contain the
value provided. If you do not provide a value, the entire field is removed if it
already exists.
If the target field is single-valued and a value is provided, then the provided
value must match the existing value to remove, otherwise the field is left
unchanged.
If the target field is multi-valued, then values in the array you provide are
removed from the existing set of values.
replace
The replace operation removes existing values on the target field, and
replaces them with the values you provide. It is equivalent to performing a
remove on the field, then an add with the values you provide.
increment
One key nuance in how a patch works with OpenDJ concerns multi-valued
fields. Although JSON resources represent multi-valued fields as arrays, OpenDJ
treats those values as sets. In other words, values in the field are unique, and
the ordering of an array of values is not meaningful in the context of patch
2
operations. If you reference array values by index, OpenDJ returns an error.
Perform patch operations as if arrays values were sets. The following example
includes Barbara Jensen in a group by adding her to the set of members:
OpenDJ does allow use of a hyphen to add an element to a set. Include the hyphen as the last element of the
field JSON pointer path. For example: curl --user kvaughan:bribery --request PATCH --header "Content-Type:
application/json" --data '[{ "operation" : "add", "field" : "/members/-", "value" : { "_id" : "bjensen" } }]' http://
opendj.example.com:8080/groups/Directory%20Administrators.
25
Patching Resources
$ curl \
--user kvaughan:bribery \
--request PATCH \
--header "Content-Type: application/json" \
--data '[
{
"operation": "add",
"field": "/members",
"value": [
{
"_id": "bjensen"
}
]
}
]' \
http://opendj.example.com:8080/groups/Directory%20Administrators
"_rev" : "00000000b70c881a",
"schemas" : [ "urn:scim:schemas:core:1.0" ],
"_id" : "Directory Administrators",
"displayName" : "Directory Administrators",
"meta" : {
"lastModified" : "2013-05-13T16:40:23Z"
},
"members" : [ {
"_id" : "kvaughan",
"displayName" : "Kirsten Vaughan"
}, {
"_id" : "rdaugherty",
"displayName" : "Robert Daugherty"
}, {
"_id" : "bjensen",
"displayName" : "Barbara Jensen"
}, {
"_id" : "hmiller",
"displayName" : "Harry Miller"
} ]
26
Patching Resources
$ curl \
--user kvaughan:bribery \
--request PATCH \
--header "Content-Type: application/json" \
--data '[
{
"operation": "remove",
"field": "/members",
"value": [
{
"_id": "bjensen"
}
]
}
]' \
http://opendj.example.com:8080/groups/Directory%20Administrators
"_rev" : "00000000e241797e",
"schemas" : [ "urn:scim:schemas:core:1.0" ],
"_id" : "Directory Administrators",
"displayName" : "Directory Administrators",
"meta" : {
"lastModified" : "2013-05-13T16:40:55Z"
},
"members" : [ {
"_id" : "kvaughan",
"displayName" : "Kirsten Vaughan"
}, {
"_id" : "rdaugherty",
"displayName" : "Robert Daugherty"
}, {
"_id" : "hmiller",
"displayName" : "Harry Miller"
} ]
To change the value of more than one attribute in a patch operation, include
multiple operations in the body of the JSON patch, as shown in the following
example:
27
Patching Resources
$ curl \
--user kvaughan:bribery \
--request PATCH \
--header "Content-Type: application/json" \
--data '[
{
"operation": "replace",
"field": "/contactInformation/telephoneNumber",
"value": "+1 408 555 9999"
},
{
"operation": "add",
"field": "/contactInformation/emailAddress",
"value": "barbara.jensen@example.com"
}
]' \
http://opendj.example.com:8080/users/bjensen
"contactInformation": {
"emailAddress": "barbara.jensen@example.com",
"telephoneNumber": "+1 408 555 9999"
},
"displayName": "Barbara Jensen",
"manager": [
{
"displayName": "Torrey Rigden",
"_id": "trigden"
}
],
"meta": {
"lastModified": "2015-04-07T10:19:41Z"
},
"schemas": [
"urn:scim:schemas:core:1.0"
],
"_rev": "00000000e68ef438",
"name": {
"givenName": "Barbara",
"familyName": "Jensen"
},
"_id": "bjensen",
"userName": "barbara.jensen@example.com"
Notice that for a multi-valued attribute, the value field takes an array, whereas
the value field takes a single value for a single-valued field. Also notice that for
single-valued fields, an add operation has the same effect as a replace operation.
You can use resource revision numbers in If-Match: revision headers to patch
the resource only if the resource matches a particular version, as shown in the
following example:
28
Using Actions
$ curl \
--user kvaughan:bribery \
http://opendj.example.com:8080/users/bjensen?_fields=_rev
{"_id":"bjensen","_rev" : "revision"}
$ curl \
--user kvaughan:bribery \
--request PATCH \
--header "If-Match: revision" \
--header "Content-Type: application/json" \
--data '[
{
"operation": "add",
"field": "/contactInformation/emailAddress",
"value": "babs@example.com"
}
]' \
http://opendj.example.com:8080/users/bjensen
"_rev" : "00000000f946d377",
"schemas" : [ "urn:scim:schemas:core:1.0" ],
"contactInformation" : {
"telephoneNumber" : "+1 408 555 1862",
"emailAddress" : "babs@example.com"
},
"_id" : "bjensen",
"name" : {
"familyName" : "Jensen",
"givenName" : "Barbara"
},
"userName" : "babs@example.com",
"displayName" : "Barbara Jensen",
"meta" : {
"lastModified" : "2013-05-13T16:56:33Z"
},
"manager" : [ {
"_id" : "trigden",
"displayName" : "Torrey Rigden"
} ]
1.8
Using Actions
OpenDJ REST to LDAP implements the actions described in this section.
1.8.1
29
application/json, _action=create in the query string, and the JSON content of the
resource.
--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
1.8.2
"_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"
} ]
30
newPassword
--request POST \
--cacert server-cert.pem \
--user bjensen:hifalutin \
--header "Content-Type: application/json" \
--data '{"oldPassword": "hifalutin", "newPassword": "password"}' \
https://opendj.example.com:8443/users/bjensen?_action=passwordModify
{}
31
$ curl \
--request POST \
--cacert server-cert.pem \
--user kvaughan:bribery \
--header "Content-Type: application/json" \
--data '{}' \
https://opendj.example.com:8443/users/bjensen?_action=passwordModify
{"generatedPassword":"qno66vyz"}
1.9
REST Filter
(&)
_queryFilter=true
(uid=*)
_queryFilter=_id+pr
(uid=bjensen)
_queryFilter=_id+eq+'bjensen'
(uid=*jensen*)
_queryFilter=_id+co+'jensen'
(uid=jensen*)
_queryFilter=_id+sw+'jensen'
(&(uid=*jensen*)(cn=babs*))
_queryFilter=(_id+co+'jensen'+and
+displayName+sw+'babs')
(|(uid=*jensen*)(cn=sam*))
_queryFilter=(_id+co+'jensen'+or
+displayName+sw+'sam')
(!(uid=*jensen*))
_queryFilter=!(_id+co+'jensen')
(uid<=jensen)
_queryFilter=_id+le+'jensen'
32
LDAP Filter
REST Filter
(uid>=jensen)
_queryFilter=_id+ge+'jensen'
For query operations, the filter expression is 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 filter expressions, the simplest json-pointer is a field of the JSON resource,
such as userName or id. A json-pointer can also point to nested elements as
described in the JSON Pointer Internet-Draft:
Comparison expressions
Build filters using the following comparison expressions:
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'"
"result" : [ {
"_id" : "bjensen",
"_rev" : "00000000cf71e05d",
"schemas" : [ "urn:scim:schemas:core:1.0" ],
"userName" : "bjensen@example.com",
"displayName" : "Barbara Jensen",
"name" : {
"givenName" : "Barbara",
"familyName" : "Jensen"
},
"contactInformation" : {
"telephoneNumber" : "+1 408 555 9999",
"emailAddress" : "bjensen@example.com"
},
"meta" : {
"lastModified" : "2015-09-23T14:09:13Z"
},
"manager" : [ {
"_id" : "trigden",
"displayName" : "Torrey Rigden"
} ]
} ],
"resultCount" : 1,
"pagedResultsCookie" : null,
"totalPagedResultsPolicy" : "NONE",
"totalPagedResults" : -1,
"remainingPagedResults" : -1
33
json-pointer co json-value
--user kvaughan:bribery \
"http://opendj.example.com:8080/users
?_queryFilter=userName+co+'jensen'&_fields=userName"
"result" : [ {
"_id" : "ajensen",
"_rev" : "00000000c899a6da",
"userName" : "ajensen@example.com"
}, {
"_id" : "bjensen",
"_rev" : "000000001431e1ef",
"userName" : "bjensen@example.com"
}, {
"_id" : "gjensen",
"_rev" : "00000000cba2a3c3",
"userName" : "gjensen@example.com"
}, {
"_id" : "jjensen",
"_rev" : "0000000046f5a1a2",
"userName" : "jjensen@example.com"
}, {
"_id" : "kjensen",
"_rev" : "00000000a9e0a59d",
"userName" : "kjensen@example.com"
}, {
"_id" : "rjensen",
"_rev" : "00000000f54ea4d2",
"userName" : "rjensen@example.com"
}, {
"_id" : "tjensen",
"_rev" : "0000000095d1a096",
"userName" : "tjensen@example.com"
} ],
"resultCount" : 7,
"pagedResultsCookie" : null,
"totalPagedResultsPolicy" : "NONE",
"totalPagedResults" : -1,
"remainingPagedResults" : -1
json-pointer sw json-value
Matches when the pointer starts with the value, as in the following
example:
34
$ curl \
--user kvaughan:bribery \
"http://opendj.example.com:8080/users
?_queryFilter=userName+sw+'ab'&_fields=userName"
"result" : [ {
"_id" : "abarnes",
"_rev" : "00000000b84ba3b0",
"userName" : "abarnes@example.com"
}, {
"_id" : "abergin",
"_rev" : "0000000011db996e",
"userName" : "abergin@example.com"
} ],
"resultCount" : 2,
"pagedResultsCookie" : null,
"totalPagedResultsPolicy" : "NONE",
"totalPagedResults" : -1,
"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"
"result" : [ {
"_id" : "abarnes",
"_rev" : "00000000b84ba3b0",
"userName" : "abarnes@example.com"
}, {
"_id" : "abergin",
"_rev" : "0000000011db996e",
"userName" : "abergin@example.com"
} ],
"resultCount" : 2,
"pagedResultsCookie" : null,
"totalPagedResultsPolicy" : "NONE",
"totalPagedResults" : -1,
"remainingPagedResults" : -1
json-pointer le json-value
Matches when the pointer is less than or equal to the value, as in the
following example:
35
$ curl \
--user kvaughan:bribery \
"http://opendj.example.com:8080/users
?_queryFilter=userName+le+'ad'&_fields=userName"
"result" : [ {
"_id" : "abarnes",
"_rev" : "00000000b84ba3b0",
"userName" : "abarnes@example.com"
}, {
"_id" : "abergin",
"_rev" : "0000000011db996e",
"userName" : "abergin@example.com"
}, {
"_id" : "achassin",
"_rev" : "00000000cddca3ec",
"userName" : "achassin@example.com"
} ],
"resultCount" : 3,
"pagedResultsCookie" : null,
"totalPagedResultsPolicy" : "NONE",
"totalPagedResults" : -1,
"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"
36
"result" : [ {
"_id" : "ttully",
"_rev" : "00000000d07da286",
"userName" : "ttully@example.com"
}, {
"_id" : "tward",
"_rev" : "0000000083419fa3",
"userName" : "tward@example.com"
}, {
"_id" : "wlutz",
"_rev" : "00000000a4f29dfa",
"userName" : "wlutz@example.com"
} ],
"resultCount" : 3,
"pagedResultsCookie" : null,
"totalPagedResultsPolicy" : "NONE",
"totalPagedResults" : -1,
"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"
"result" : [ {
"_id" : "tward",
"_rev" : "0000000083419fa3",
"userName" : "tward@example.com"
}, {
"_id" : "wlutz",
"_rev" : "00000000a4f29dfa",
"userName" : "wlutz@example.com"
} ],
"resultCount" : 2,
"pagedResultsCookie" : null,
"totalPagedResultsPolicy" : "NONE",
"totalPagedResults" : -1,
"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+pr&_fields=userName"
"result" : [ {
"_id" : "abarnes",
"_rev" : "00000000b84ba3b0",
"userName" : "abarnes@example.com"
}, ... {
"_id" : "newuser",
"_rev" : "00000000fca77472",
"userName" : "newuser@example.com"
} ],
"resultCount" : 152,
"pagedResultsCookie" : null,
"totalPagedResultsPolicy" : "NONE",
"totalPagedResults" : -1,
"remainingPagedResults" : -1
Literal expressions
true matches any resource in the collection.
false matches no resource in the collection.
37
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"
"result" : [ {
"_id" : "Directory Administrators",
"_rev" : "0000000060b85b8b",
"displayName" : "Directory Administrators"
}, {
"_id" : "Accounting Managers",
"_rev" : "0000000053e97a0a",
"displayName" : "Accounting Managers"
}, {
"_id" : "HR Managers",
"_rev" : "000000005ff5730a",
"displayName" : "HR Managers"
}, {
"_id" : "PD Managers",
"_rev" : "000000001e1e75a0",
"displayName" : "PD Managers"
}, {
"_id" : "QA Managers",
"_rev" : "00000000e0747323",
"displayName" : "QA Managers"
} ],
"resultCount" : 5,
"pagedResultsCookie" : null,
"totalPagedResultsPolicy" : "NONE",
"totalPagedResults" : -1,
"remainingPagedResults" : -1
Complex expressions
Combine expressions using boolean operators and, or, and ! (not), and
by using parentheses (expression) with group expressions. The following
example queries resources with last name Jensen and manager name starting
with Bar:
38
$ curl \
--user kvaughan:bribery \
"http://opendj.example.com:8080/users?_queryFilter=\
(userName+co+'jensen'+and+manager/displayName+sw+'Sam')&_fields=displayName"
{
"result" : [ {
"_id" : "jjensen",
"_rev" : "000000003ef3a150",
"displayName" : "Jody Jensen"
}, {
"_id" : "tjensen",
"_rev" : "000000009367a0b6",
"displayName" : "Ted Jensen"
} ],
"resultCount" : 2,
"pagedResultsCookie" : null,
"totalPagedResultsPolicy" : "NONE",
"totalPagedResults" : -1,
"remainingPagedResults" : -1
}
Notice that the filters use the JSON pointers name/familyName and manager/
displayName to identify the fields nested inside the name and manager objects.
You can page through search results using the following query string parameters
that are further described in Section1.1.11, "Query":
_pagedResultsCookie=string
_pagedResultsOffset=integer
_pageSize=integer
The following example demonstrates how paged results are used:
39
# Request five results per page, and retrieve the first page.
$ curl \
--user bjensen:hifalutin \
"http://opendj.example.com:8080/users
?_queryFilter=true&_fields=userName&_pageSize=5"
"result" : [ {
"_id" : "abarnes",
"_rev" : "00000000b589a3d4",
"userName" : "abarnes@example.com"
}, {
"_id" : "abergin",
"_rev" : "00000000131199bd",
"userName" : "abergin@example.com"
}, {
"_id" : "achassin",
"_rev" : "00000000aaf8a2ac",
"userName" : "achassin@example.com"
}, {
"_id" : "ahall",
"_rev" : "0000000023e19cdc",
"userName" : "ahall@example.com"
}, {
"_id" : "ahel",
"_rev" : "0000000033309a22",
"userName" : "ahel@example.com"
} ],
"resultCount" : 5,
"pagedResultsCookie" : "AAAAAAAAAA8=",
"totalPagedResultsPolicy" : "NONE",
"totalPagedResults" : -1,
"remainingPagedResults" : -1
40
--user bjensen:hifalutin \
"http://opendj.example.com:8080/users
?_queryFilter=true&_fields=userName&_pageSize=5\
&_pagedResultsCookie=AAAAAAAAAA8="
{
"result" : [ {
"_id" : "ahunter",
"_rev" : "00000000ec1aa3bb",
"userName" : "ahunter@example.com"
}, {
"_id" : "ajensen",
"_rev" : "00000000d4b9a728",
"userName" : "ajensen@example.com"
}, {
"_id" : "aknutson",
"_rev" : "000000002135ab65",
"userName" : "aknutson@example.com"
}, {
"_id" : "alangdon",
"_rev" : "000000009bc5a8e3",
"userName" : "alangdon@example.com"
}, {
"_id" : "alutz",
"_rev" : "0000000060b9a4bd",
"userName" : "alutz@example.com"
} ],
"resultCount" : 5,
"pagedResultsCookie" : "AAAAAAAAABQ=",
"totalPagedResultsPolicy" : "NONE",
"totalPagedResults" : -1,
"remainingPagedResults" : -1
}
41
42
Chapter 2
2.1
Command-Line Tools
Before you try the examples in this guide, set your PATH to include the OpenDJ
directory server tools. The location of the tools depends on the operating
environment and on the packages used to install OpenDJ. Table2.1, "Paths To
Administration Tools" indicates where to find the tools.
Table 2.1. Paths To Administration Tools
OpenDJ running on...
OpenDJ installed
from...
.zip
/path/to/opendj/bin
Linux distributions
.deb, .rpm
/opt/opendj/bin
Microsoft Windows
.zip
C:\path\to\opendj\bat
Oracle Solaris
SVR4
/usr/opendj/bin
43
Command-Line Tools
You find the installation and upgrade tools, setup, upgrade, and uninstall, in
the parent directory of the other tools, as these tools are not used for everyday
administration. For example, if the path to most tools is /path/to/opendj/bin
you can find these tools in /path/to/opendj. For instructions on how to use the
installation and upgrade tools, see the OpenDJ Installation Guide.
All OpenDJ command-line tools take the --help option.
All commands call Java programs and therefore involve starting a JVM.
Table2.2, "Tools and Server Constraints" indicates the constraints, if any, that
apply when using a command-line tool with a directory server.
Table 2.2. Tools and Server Constraints
44
Commands
Constraints
backendstat
create-rc-script
dsjavaproperties
encode-password
list-backends
setup
start-ds
upgrade
windows-service
control-panel
dsconfig
export-ldif
import-ldif
manage-account
manage-tasks
rebuild-index
restore
status
stop-ds
uninstall
verify-index
dsreplication
Command-Line Tools
Commands
Constraints
This commands is not useful with
other types of directory servers.
make-ldif
base64
ldapcompare
ldapdelete
ldapmodify
ldappasswordmodify
ldapsearch
ldif-diff
ldifmodify
ldifsearch
The following list uses the UNIX names for the commands. On Windows all
command-line tools have the extension .bat:
backendstat
Debug databases for pluggable backends.
For details see backendstat(1) in the OpenDJ Reference.
backup
Back up or schedule backup of directory data.
For details see backup(1) in the OpenDJ Reference.
base64
Encode and decode data in base64 format.
Base64-encoding represents binary data in ASCII, and can be used to encode
character strings in LDIF, for example.
For details see base64(1) in the OpenDJ Reference.
create-rc-script (UNIX)
Generate a script you can use to start, stop, and restart the server either
directly or at system boot and shutdown. Use create-rc-script -f script-file.
45
Command-Line Tools
46
Command-Line Tools
ldapcompare
Compare the attribute values you specify with those stored on entries in the
directory.
For details see ldapcompare(1) in the OpenDJ Reference.
ldapdelete
Delete one entry or an entire branch of subordinate entries in the directory.
For details see ldapdelete(1) in the OpenDJ Reference.
ldapmodify
Modify the specified attribute values for the specified entries.
Use the ldapmodify command with the -a option to add new entries.
For details see ldapmodify(1) in the OpenDJ Reference.
ldappasswordmodify
Modify user passwords.
For details see ldappasswordmodify(1) in the OpenDJ Reference.
ldapsearch
Search a branch of directory data for entries that match the LDAP filter you
specify.
For details see ldapsearch(1) in the OpenDJ Reference.
ldif-diff
Display differences between two LDIF files, with the resulting output having
LDIF format.
For details see ldif-diff(1) in the OpenDJ Reference.
ldifmodify
Similar to the ldapmodify command, modify specified attribute values for
specified entries in an LDIF file.
For details see ldifmodify(1) in the OpenDJ Reference.
ldifsearch
Similar to the ldapsearch command, search a branch of data in LDIF for
entries matching the LDAP filter you specify.
For details see ldifsearch(1) in the OpenDJ Reference.
list-backends
List backends and base DNs served by OpenDJ directory server.
For details see list-backends(1) in the OpenDJ Reference.
47
Command-Line Tools
make-ldif
Generate directory data in LDIF based on templates that define how the data
should appear.
The make-ldif command is designed to help generate test data that mimics
data expected in production, but without compromising real, potentially
private information.
For details see make-ldif(1) in the OpenDJ Reference.
manage-account
Lock and unlock user accounts, and view and manipulate password policy
state information.
For details see manage-account(1) in the OpenDJ Reference.
manage-tasks
View information about tasks scheduled to run in the server, and cancel
specified tasks.
For details see manage-tasks(1) in the OpenDJ Reference.
rebuild-index
Rebuild an index stored in an indexed backend.
For details see rebuild-index(1) in the OpenDJ Reference.
restore
Restore data from backup.
For details see restore(1) in the OpenDJ Reference.
start-ds
Start OpenDJ directory server.
For details see start-ds(1) in the OpenDJ Reference.
status
Display information about the server.
For details see status(1) in the OpenDJ Reference.
stop-ds
Stop OpenDJ directory server.
For details see stop-ds(1) in the OpenDJ Reference.
verify-index
Verify that an index stored in an indexed backend is not corrupt.
For details see verify-index(1) in the OpenDJ Reference.
48
windows-service (Windows)
Register OpenDJ as a Windows Service.
For details see windows-service(1) in the OpenDJ Reference.
2.2
In the example, the LDAP filter indicates to the directory that you want to look up
printer entries where the printerLocation attribute is equal to GNB00.
You also specify the host and port to access directory services, and the type of
protocol to use (for example, LDAP/SSL, or StartTLS to protect communication).
If the directory service does not allow anonymous access to the data you want
to search, you also identify who is performing the search and provide their
credentials, such as a password or certificate. Finally, you can specify a list of
attributes to return. If you do not specify attributes, then the search returns all
user attributes for the entry.
49
Review the following examples in this section to get a sense of how searches
work:
Example2.1, "Search: Using Simple Filters"
Example2.2, "Search: Using Complex Filters"
Example2.3, "Search: Return Operational Attributes"
Example2.4, "Search: Returning Attributes for an Object Class"
Example2.5, "Search: Escaping Search Filter Characters"
Example2.6, "Search: Listing Active Accounts"
Example2.7, "Search: Using Language Subtypes"
Example 2.1. Search: Using Simple Filters
The following example searches for entries with user IDs (uid) containing jensen,
returning only DNs and user ID values:
$ ldapsearch --port 1389 --baseDN dc=example,dc=com "(uid=*jensen*)" uid
dn: uid=ajensen,ou=People,dc=example,dc=com
uid: ajensen
dn: uid=bjensen,ou=People,dc=example,dc=com
uid: bjensen
dn: uid=gjensen,ou=People,dc=example,dc=com
uid: gjensen
dn: uid=jjensen,ou=People,dc=example,dc=com
uid: jjensen
dn: uid=kjensen,ou=People,dc=example,dc=com
uid: kjensen
dn: uid=rjensen,ou=People,dc=example,dc=com
uid: rjensen
dn: uid=tjensen,ou=People,dc=example,dc=com
uid: tjensen
Result Code:
0 (Success)
50
$ ldapsearch \
--port 1389 \
--baseDN ou=people,dc=example,dc=com \
"(&(uid=*jensen*)(l=Santa Clara))" \
@person
dn: uid=ajensen,ou=People,dc=example,dc=com
objectClass: person
objectClass: organizationalPerson
objectClass: inetOrgPerson
objectClass: posixAccount
objectClass: top
cn: Allison Jensen
telephoneNumber: +1 408 555 7892
sn: Jensen
dn: uid=gjensen,ou=People,dc=example,dc=com
objectClass: person
objectClass: organizationalPerson
objectClass: inetOrgPerson
objectClass: posixAccount
objectClass: top
cn: Gern Jensen
telephoneNumber: +1 408 555 3299
sn: Jensen
dn: uid=kjensen,ou=People,dc=example,dc=com
objectClass: person
objectClass: organizationalPerson
objectClass: inetOrgPerson
objectClass: posixAccount
objectClass: top
cn: Kurt Jensen
telephoneNumber: +1 408 555 6127
sn: Jensen
dn: uid=tjensen,ou=People,dc=example,dc=com
objectClass: person
objectClass: organizationalPerson
objectClass: inetOrgPerson
objectClass: posixAccount
objectClass: top
cn: Ted Jensen
telephoneNumber: +1 408 555 8622
sn: Jensen
The command returns the attributes associated with the person object class.
Complex filters can use both "and" syntax, (&(filtercomp)(filtercomp)), and "or"
syntax, (|(filtercomp)(filtercomp)).
Example 2.3. Search: Return Operational Attributes
Use + in the attribute list after the filter to return all operational attributes, as in
the following example:
51
dn: uid=bjensen,ou=People,dc=example,dc=com
numSubordinates: 0
structuralObjectClass: inetOrgPerson
pwdPolicySubentry: cn=Default Password Policy,cn=Password Policies,cn=config
subschemaSubentry: cn=schema
hasSubordinates: false
entryDN: uid=bjensen,ou=people,dc=example,dc=com
entryUUID: fc252fd9-b982-3ed6-b42a-c76d2546312c
52
The following example shows a filter with escaped characters matching an actual
value:
$ ldapsearch --port 1389 --baseDN dc=example,dc=com \
"(description=\28*\5c*\2a\29)" description
dn: uid=bjensen,ou=People,dc=example,dc=com
description: (A \great\ description*)
--port 1389 \
--hostname opendj.example.com \
--bindDN "cn=Directory Manager" \
--bindPassword password
dn: cn=schema
changetype: modify
add: attributeTypes
attributeTypes: ( lastLoginTime-oid
NAME 'lastLoginTime'
DESC 'Last time the user logged in'
EQUALITY generalizedTimeMatch
ORDERING generalizedTimeOrderingMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.24
SINGLE-VALUE
NO-USER-MODIFICATION
USAGE directoryOperation
X-ORIGIN 'OpenDJ example documentation' )
Processing MODIFY request for cn=schema
MODIFY operation successful for DN cn=schema
Configure the applicable password policy to write the last login timestamp when
a user authenticates. The following command configures the default password
policy to write the timestamp in generalized time format to the lastLoginTime
operational attribute on the user's entry:
53
$ dsconfig \
set-password-policy-prop \
--port 4444 \
--hostname opendj.example.com \
--bindDN "cn=Directory Manager" \
--bindPassword password \
--policy-name "Default Password Policy" \
--set last-login-time-attribute:lastLoginTime \
--set last-login-time-format:"yyyyMMddHH'Z'" \
--trustAll \
--no-prompt
Wait for users to authenticate again (or test it yourself) so that OpenDJ writes the
timestamps. The following search then returns users who have authenticated in
the last three months (13 weeks) after you configured OpenDJ to keep the last
login timestamps:
$ ldapsearch \
--port 1389 \
--baseDN dc=example,dc=com \
"(lastLoginTime:1.3.6.1.4.1.26027.1.4.6:=13w)" mail
dn: uid=bjensen,ou=People,dc=example,dc=com
mail: bjensen@example.com
dn: uid=kvaughan,ou=People,dc=example,dc=com
mail: kvaughan@example.com
--port 1389 \
--baseDN dc=example,dc=com \
"(givenName:fr:=Frderique)" cn\;lang-fr
dn: uid=fdupont,ou=People,dc=example,dc=com
cn;lang-fr:: RnJlZMOpcmlxdWUgRHVwb250
At the end of the OID or language subtype, further specify the matching rule as
follows:
54
Definition
Example
Equality comparison, as in
(sn=Jensen).
>=
=*
~=
<=
55
Operator
Definition
Example
[:dn]
[:oid]:=
(lastLoginTime: 1.3.6.1.4.1.26027.
1.4.5:=-13w) matches entries with a
'!(objectclass=person)' matches
non-person entries.
56
'(&(l=San Francisco)(!
(uid=bjensen)))' matches entries
"|(sn=Jensen)(sn=Johnson)" matches
2.3
--port 1389 \
--bindDN "uid=kvaughan,ou=people,dc=example,dc=com" \
--bindPassword bribery \
'authPassword:MD5$dFHgpDxXUT8=$qlC4xMXvmVlusJLz9/WJ5Q==' \
uid=kvaughan,ou=people,dc=example,dc=com
Comparing type authPassword with value
MD5$dFHgpDxXUT8=$qlC4xMXvmVlusJLz9/WJ5Q== in entry
uid=kvaughan,ou=people,dc=example,dc=com
Compare operation returned true for entry
uid=kvaughan,ou=people,dc=example,dc=com
2.4
2.4.1
Adding Entries
With the ldapmodify -a command, authorized users can add entire entries from
the same sort of LDIF file used to import and export data.
Example 2.9. Adding Two New Users
The following example adds two new users:
57
$ cat new-users.ldif
$ ldapmodify \
--defaultAdd \
--port 1389 \
--bindDN "uid=kvaughan,ou=people,dc=example,dc=com" \
--bindPassword bribery \
--filename new-users.ldif
Processing ADD request for cn=Arsene Lupin,ou=Special Users,dc=example,dc=com
ADD operation successful for DN
cn=Arsene Lupin,ou=Special Users,dc=example,dc=com
Processing ADD request for cn=Horace Velmont,ou=Special Users,dc=example,dc=com
ADD operation successful for DN
cn=Horace Velmont,ou=Special Users,dc=example,dc=com
2.4.2
58
$ cat scarter-mods.ldif
dn: uid=scarter,ou=people,dc=example,dc=com
changetype: modify
add: description
description: Accounting Manager
add: jpegphoto
jpegphoto:<file:///tmp/Samantha-Carter.jpg
$ ldapmodify \
--port 1389 \
--bindDN "uid=kvaughan,ou=people,dc=example,dc=com" \
--bindPassword bribery \
--filename scarter-mods.ldif
Processing MODIFY request for uid=scarter,ou=people,dc=example,dc=com
MODIFY operation successful for DN uid=scarter,ou=people,dc=example,dc=com
dn: uid=scarter,ou=people,dc=example,dc=com
changetype: modify
replace: description
description: Accounting Director
$ ldapmodify \
--port 1389 \
--bindDN "uid=kvaughan,ou=people,dc=example,dc=com" \
--bindPassword bribery \
--filename scarter-newdesc.ldif
Processing MODIFY request for uid=scarter,ou=people,dc=example,dc=com
MODIFY operation successful for DN uid=scarter,ou=people,dc=example,dc=com
dn: uid=scarter,ou=people,dc=example,dc=com
changetype: modify
delete: jpegphoto
$ ldapmodify \
--port 1389 \
--bindDN "uid=kvaughan,ou=people,dc=example,dc=com" \
--bindPassword bribery \
--filename scarter-deljpeg.ldif
Processing MODIFY request for uid=scarter,ou=people,dc=example,dc=com
MODIFY operation successful for DN uid=scarter,ou=people,dc=example,dc=com
59
Bob prepares his changes in your application. Bob is almost ready to submit the
new location and description when Carol stops by to ask Bob a few questions.
Alice starts just after Bob, but manages to submit her changes without
interruption. Now Babs's entry looks like this:
dn: uid=bjensen,ou=People,dc=example,dc=com
description: Updated by Alice
telephoneNumber: +47 2108 1746
roomNumber: 1389
l: San Francisco
ETag: 00000000aec2c1e9
In your application, you use the ETag attribute value with the assertion control
to prevent Bob's update from succeeding although the ETag value has changed.
Your application tries the equivalent of the following commands with Bob's
updates:
60
$ cat /path/to/bobs.ldif
dn: uid=bjensen,ou=People,dc=example,dc=com
changetype: modify
replace: l
l: Grenoble
add: description
description: Employee of the Month
$ ldapmodify \
Your application reloads Babs's entry, gets the new ETag value 00000000aec2c1e9,
and lets Bob try again. This time Bob's changes do not collide with other
changes. Babs's entry is successfully updated:
dn: uid=bjensen,ou=People,dc=example,dc=com
description: Employee of the Month
telephoneNumber: +47 2108 1746
roomNumber: 1389
l: Grenoble
ETag: 00000000e882c35e
2.4.3
61
$ dsconfig \
create-plugin \
--port 4444 \
--hostname opendj.example.com \
--bindDN "cn=Directory Manager" \
--bindPassword password \
--type attribute-cleanup \
--plugin-name "Rename email to mail" \
--set enabled:true \
--set rename-inbound-attributes:email:mail \
--trustAll \
--no-prompt
dn: uid=newuser,ou=People,dc=example,dc=com
uid: newuser
objectClass: person
objectClass: organizationalPerson
objectClass: inetOrgPerson
objectClass: top
cn: New User
sn: User
ou: People
email: newuser@example.com
userPassword: changeme
$ ldapmodify \
--port 1389 \
--bindDN "cn=Directory Manager" \
--bindPassword password \
--defaultAdd \
--filename email.ldif
Processing ADD request for uid=newuser,ou=People,dc=example,dc=com
ADD operation successful for DN uid=newuser,ou=People,dc=example,dc=com
62
$ dsconfig \
create-plugin \
--port 4444 \
--hostname opendj.example.com \
--bindDN "cn=Directory Manager" \
--bindPassword password \
--type attribute-cleanup \
--plugin-name "Remove attrs" \
--set enabled:true \
--set remove-inbound-attributes:creatorsName \
--set remove-inbound-attributes:createTimestamp \
--set remove-inbound-attributes:modifiersName \
--set remove-inbound-attributes:modifyTimestamp \
--trustAll \
--no-prompt
dn: uid=badattr,ou=People,dc=example,dc=com
uid: newuser
objectClass: person
objectClass: organizationalPerson
objectClass: inetOrgPerson
objectClass: top
cn: Bad Attr
sn: Attr
ou: People
mail: badattr@example.com
userPassword: changeme
creatorsName: cn=Bad Attr
createTimestamp: Never in a million years.
modifiersName: cn=Directory Manager,cn=Root DNs,cn=config
modifyTimestamp: 20110930164937Z
$ ldapmodify \
--port 1389 \
--bindDN "cn=Directory Manager" \
--bindPassword password \
--defaultAdd \
--filename badattrs.ldif
Processing ADD request for uid=badattr,ou=People,dc=example,dc=com
ADD operation successful for DN uid=badattr,ou=People,dc=example,dc=com
dn: uid=badattr,ou=People,dc=example,dc=com
numSubordinates: 0
structuralObjectClass: inetOrgPerson
pwdPolicySubentry: cn=Default Password Policy,cn=Password Policies,cn=config
subschemaSubentry: cn=schema
hasSubordinates: false
entryDN: uid=badattr,ou=people,dc=example,dc=com
entryUUID: 35e5cb0e-e929-49d8-a50f-2df036d60db9
pwdChangedTime: 20110930165959.135Z
creatorsName: cn=Directory Manager,cn=Root DNs,cn=config
createTimestamp: 20110930165959Z
63
Renaming Entries
2.4.4
Renaming Entries
The Relative Distinguished Name (RDN) refers to the part of an entry's DN that
differentiates it from all other DNs at the same level in the directory tree. For
example, uid=bjensen is the RDN of the entry with the DN uid=bjensen,ou=People,
dc=example,dc=com.
With the ldapmodify command, authorized users can rename entries in the
directory.
When you change the RDN of the entry, you are renaming the entry, modifying
the value of the naming attribute, and the entry's DN.
Example 2.16. Rename: Modifying the DN
Sam Carter is changing her last name to Jensen, and changing her login from
scarter to sjensen. The following example shows you how to rename and change
Sam Carter's entry. Notice the boolean field, deleteoldrdn: 1, which indicates
that the previous RDN, uid: scarter, should be removed. (Setting deleteoldrdn:
0 instead would preserve uid: scarter on the entry.)
$ cat /path/to/scarter-sjensen.ldif
dn: uid=scarter,ou=people,dc=example,dc=com
changetype: modrdn
newrdn: uid=sjensen
deleteoldrdn: 1
dn: uid=sjensen,ou=people,dc=example,dc=com
changetype: modify
replace: cn
cn: Sam Jensen
replace: sn
sn: Jensen
replace: homeDirectory
homeDirectory: /home/sjensen
replace: mail
mail: sjensen@example.com
$ ldapmodify \
--port 1389 \
--bindDN "uid=kvaughan,ou=people,dc=example,dc=com" \
--bindPassword bribery \
--filename /path/to/scarter-sjensen.ldif
Processing MODIFY DN request for uid=scarter,ou=people,dc=example,dc=com
MODIFY DN operation successful for DN uid=scarter,ou=people,dc=example,dc=com
Processing MODIFY request for uid=sjensen,ou=people,dc=example,dc=com
MODIFY operation successful for DN uid=sjensen,ou=people,dc=example,dc=com
64
Moving Entries
2.4.5
Moving Entries
When you rename an entry with child entries, the directory has to move all the
entries underneath it.
Note
The modify DN operation only works when moving entries in the
same backend, under the same suffix. Also, depending on the
number of entries you move, this can be a resource-intensive
operation.
With the ldapmodify command, authorized users can move entries in the
directory.
Example 2.17. Move: Merging Customer and Employees Under ou=People
The following example moves ou=Customers,dc=example,dc=com to ou=People,
dc=example,dc=com, then moves each employee under ou=Employees,dc=example,
dc=com under ou=People,dc=example,dc=com as well, and finally removes the empty
ou=Employees,dc=example,dc=com container. Here, deleteoldrdn: 1 indicates that
the old RDN, ou: Customers, should be removed from the entry. For employees,
deleteoldrdn: 0 indicates that old RDNs, in this case, uid attribute values, should
be preserved:
65
Moving Entries
$ cat move-customers.ldif
dn: ou=Customers,dc=example,dc=com
changetype: modrdn
newrdn: ou=People
deleteoldrdn: 1
newsuperior: dc=example,dc=com
$ ldapmodify \
--port 1389 \
--bindDN "cn=Directory Manager" \
--bindPassword password \
--filename move-customers.ldif
Processing MODIFY DN request for ou=Customers,dc=example,dc=com
MODIFY DN operation successful for DN ou=Customers,dc=example,dc=com
$ cat move-employees.pl
#!/usr/bin/perl -w
$ head -n 6 /tmp/move-employees.ldif
dn: uid=abarnes,ou=Employees,dc=example,dc=com
changetype: moddn
newrdn: uid=abarnes
deleteoldrdn: 0
newsuperior: ou=People,dc=example,dc=com
$ ldapmodify \
--port 1389 \
--bindDN "cn=Directory Manager" \
--bindPassword password \
--filename /tmp/move-employees.ldif
Processing MODIFY DN request for uid=abarnes,ou=Employees,dc=example,dc=com
MODIFY DN operation successful for DN uid=abarnes,ou=Employees,dc=example,dc=com
Processing MODIFY DN request for uid=abergin,ou=Employees,dc=example,dc=com
MODIFY DN operation successful for DN uid=abergin,ou=Employees,dc=example,dc=com
...
Processing MODIFY DN request for uid=wlutz,ou=Employees,dc=example,dc=com
MODIFY DN operation successful for DN uid=wlutz,ou=Employees,dc=example,dc=com
$ ldapdelete \
--port 1389 \
--bindDN "cn=Directory Manager" \
--bindPassword password \
ou=Employees,dc=example,dc=com
Processing DELETE request for ou=Employees,dc=example,dc=com
DELETE operation successful for DN ou=Employees,dc=example,dc=com
66
Deleting Entries
2.4.6
Deleting Entries
With the ldapmodify command, authorized users can delete entries from the
directory.
Example 2.18. Delete: Removing a Subtree
The following example shows you how to use the subtree delete option to remove
all special users from the directory:
$ ldapdelete \
--port 1389 \
--bindDN "cn=Directory Manager" \
--bindPassword password \
--deleteSubtree "ou=Special Users,dc=example,dc=com"
Processing DELETE request for ou=Special Users,dc=example,dc=com
DELETE operation successful for DN ou=Special Users,dc=example,dc=com
2.5
Changing Passwords
With the ldappasswordmodify command, described in ldappasswordmodify(1) in
the OpenDJ Reference, authorized users can change and reset user passwords.
Example 2.19. Resetting Passwords
The following example shows Kirsten Vaughan resetting Sam Carter's password.
Kirsten has the appropriate privilege to reset Sam's password:
$ ldappasswordmodify \
--useStartTLS \
--port 1389 \
--bindDN "uid=kvaughan,ou=people,dc=example,dc=com" \
--bindPassword bribery \
--authzID "dn:uid=scarter,ou=people,dc=example,dc=com" \
--newPassword ChangeMe
The LDAP password modify operation was successful
Tip
The ldappasswordmodify command uses the LDAP Password
Modify extended operation. If this extended operation is
performed on a connection that is already associated with a user
(in other words, when a user first does a bind on the connection,
then requests the LDAP Password Modify extended operation),
67
Changing Passwords
set-password-is-reset \
--bindDN "cn=Directory Manager" \
--bindPassword password \
--targetDN uid=scarter,ou=people,dc=example,dc=com \
--operationValue true
Password Is Reset: true
--port 1389 \
--authzID "dn:uid=bjensen,ou=people,dc=example,dc=com" \
--currentPassword hifalutin \
--newPassword secret12
The LDAP password modify operation was successful
68
$ ldappasswordmodify \
--port 1389 \
--authzID "dn:cn=Directory Manager" \
--currentPassword password \
--newPassword secret12
The LDAP password modify operation was successful
$ ldappasswordmodify \
--port 1389 \
--bindDN uid=bjensen,ou=People,dc=example,dc=com \
--bindPassword hifalutin \
--currentPassword hifalutin \
--newPassword psswrd
The LDAP password modify operation was successful
$ ldapsearch \
--port 1389 \
--bindDN uid=bjensen,ou=People,dc=example,dc=com \
--bindPassword psswrd \
--baseDN dc=example,dc=com \
"(uid=bjensen)" cn
dn: uid=bjensen,ou=People,dc=example,dc=com
userPassword: {SSHA}k0eEeCxj9YRXUp8yJn0Z/mwqe+wrcFb1N1gg2g==
cn: Barbara Jensen
cn: Babs Jensen
2.6
69
Authenticating To
the Directory Server
2.7
70
Authenticating To
the Directory Server
ID bjensen from the address, then build the corresponding DN, uid=bjensen,
ou=people,dc=example,dc=com in order to bind.
When an identifier string provided by the user can be readily mapped to
the user's entry DN, OpenDJ directory server can translate between the
identifier string and the entry DN. This translation is the job of a component
called an identity mapper. Identity mappers are used to perform PLAIN SASL
authentication (with a user name and password), SASL GSSAPI authentication
(Kerberos V5), SASL CRAM MD5, and DIGEST MD5 authentication. They also
handle authorization IDs during password modify extended operations and
proxied authorization.
One use of PLAIN SASL is to translate user names from HTTP Basic
authentication to LDAP authentication. The following example shows PLAIN
SASL authentication using the default Exact Match identity mapper. In this
(contrived) example, Babs Jensen reads the hashed value of her password.
(According to the access controls in the example data, Babs must authenticate to
read her password.) Notice the authentication ID is her user ID, u:bjensen, rather
than the DN of her entry:
$ ldapsearch \
--port 1389 \
--useStartTLS \
--baseDN dc=example,dc=com \
--saslOption mech=PLAIN \
--saslOption authid=u:bjensen \
--bindPassword hifalutin \
"(cn=Babs Jensen)" cn userPassword
dn: uid=bjensen,ou=People,dc=example,dc=com
cn: Barbara Jensen
cn: Babs Jensen
userPassword: {SSHA}7S4Si+vPE513cYQ7otiqb8hjiCzU7XNTv0RPBA==
The Exact Match identity mapper searches for a match between the string
provided (here, bjensen) and the value of a specified attribute (by default the uid
attribute). If you know users are entering their email addresses, you could create
an exact match identity mapper for email addresses, then use that for PLAIN
SASL authentication as in the following example:
71
Authenticating To
the Directory Server
$ dsconfig \
create-identity-mapper \
--hostname opendj.example.com \
--port 4444 \
--bindDN "cn=Directory Manager" \
--bindPassword password \
--mapper-name "Email Mapper" \
--type exact-match \
--set match-attribute:mail \
--set enabled:true \
--no-prompt
$ dsconfig \
set-sasl-mechanism-handler-prop \
--hostname opendj.example.com \
--port 4444 \
--bindDN "cn=Directory Manager" \
--bindPassword password \
--handler-name PLAIN \
--set identity-mapper:"Email Mapper" \
--no-prompt
$ ldapsearch \
--port 1389 \
--useStartTLS \
--baseDN dc=example,dc=com \
--saslOption mech=PLAIN \
--saslOption authid=u:bjensen@example.com \
--bindPassword hifalutin \
"(cn=Babs Jensen)" cn userPassword
dn: uid=bjensen,ou=People,dc=example,dc=com
cn: Barbara Jensen
cn: Babs Jensen
userPassword: {SSHA}7S4Si+vPE513cYQ7otiqb8hjiCzU7XNTv0RPBA==
72
$ dsconfig \
set-sasl-mechanism-handler-prop \
--hostname opendj.example.com \
--port 4444 \
--bindDN "cn=Directory Manager" \
--bindPassword password \
--handler-name PLAIN \
--set identity-mapper:"Regular Expression" \
--no-prompt
$ ldapsearch \
--port 1389 \
--useStartTLS \
--baseDN dc=example,dc=com \
--saslOption mech=PLAIN \
--saslOption authid=u:bjensen@example.com \
--bindPassword hifalutin \
"(cn=Babs Jensen)" cn userPassword
dn: uid=bjensen,ou=People,dc=example,dc=com
cn: Barbara Jensen
cn: Babs Jensen
userPassword: {SSHA}7S4Si+vPE513cYQ7otiqb8hjiCzU7XNTv0RPBA==
2.8
Bind DN no access
Proxy ID no access
No
No
Yes
Yes
73
Note
When you configure resource limits as described in Chapter12,
in the OpenDJ Administration Guide, know that the resource
limits do not change when the user proxies as another user. In
other words, resource limits depend on the bind DN, not the
proxy authorization identity.
Suppose you have an administrative directory client application that has an entry
in the directory with DN cn=My App,ou=Apps,dc=example,dc=com. You can give
that application the access rights and privileges to use proxied authorization.
The default access control for OpenDJ lets authenticated users use the proxied
authorization control.
Suppose also that when directory administrator, Kirsten Vaughan, logs in to your
application to change Babs Jensen's entry, your application looks up Kirsten's
entry, and finds that she has DN uid=kvaughan,ou=People,dc=example,dc=com. For
the example commands in Procedure2.1, "To Configure Proxied Authorization",
My App uses proxied authorization to make a change to Babs's entry as Kirsten.
Procedure 2.1. To Configure Proxied Authorization
In order to carry out LDAP operations on behalf of another user, the user binding
to OpenDJ directory server needs:
Permission to use the LDAP Proxy Authorization Control.
Permissions are granted using access control instructions (ACIs). This calls for
an ACI with a targetcontrol list that includes the Proxy Authorization Control
OID 2.16.840.1.113730.3.4.18 that grants allow(read) permission to the user
binding to the directory.
Permission to proxy as the given authorization user.
This calls for an ACI with a target scope that includes the entry of the
authorization user that grants allow(proxy) permission to the user binding to
the directory.
The privilege to use proxied authorization.
Privileges are granted using the ds-privilege-name attribute.
Follow these steps to configure proxied authorization for applications with DNs
that match cn=*,ou=Apps,dc=example,dc=com:
74
1.
If the global ACIs do not allow access to use the Proxy Authorization Control,
grant access to applications to use the control.
The control has OID 2.16.840.1.113730.3.4.18:
$ ldapmodify \
--port 1389 \
--bindDN "cn=Directory Manager" \
--bindPassword password
dn: dc=example,dc=com
changetype: modify
add: aci
aci: (targetcontrol="2.16.840.1.113730.3.4.18") (version 3.0; acl
"Apps can use the Proxy Authorization Control"; allow(read)
userdn="ldap:///cn=*,ou=Apps,dc=example,dc=com";)
Processing MODIFY request for dc=example,dc=com
MODIFY operation successful for DN dc=example,dc=com
2.
--port 1389 \
--bindDN "cn=Directory Manager" \
--bindPassword password
dn: dc=example,dc=com
changetype: modify
add: aci
aci: (target="ldap:///dc=example,dc=com") (targetattr ="*
")(version 3.0; acl "Allow apps proxied auth"; allow(all, proxy
)(userdn = "ldap:///cn=*,ou=Apps,dc=example,dc=com");)
Processing MODIFY request for dc=example,dc=com
MODIFY operation successful for DN dc=example,dc=com
3.
--port 1389 \
--bindDN "cn=Directory Manager" \
--bindPassword password
dn: cn=My App,ou=Apps,dc=example,dc=com
changetype: modify
add: ds-privilege-name
ds-privilege-name: proxied-auth
Processing MODIFY request for cn=My App,ou=Apps,dc=example,dc=com
MODIFY operation successful for DN cn=My App,ou=Apps,dc=example,dc=com
4.
75
$ ldapmodify \
--port 1389 \
--bindDN "cn=My App,ou=Apps,dc=example,dc=com" \
--bindPassword password \
--proxyAs "dn:uid=kvaughan,ou=People,dc=example,dc=com"
dn: uid=bjensen,ou=People,dc=example,dc=com
changetype: modify
replace: description
description: Changed through proxied auth
Processing MODIFY request for uid=bjensen,ou=People,dc=example,dc=com
MODIFY operation successful for DN uid=bjensen,ou=People,dc=example,dc=com
If you need to map authorization identifiers using the u: form rather than using
dn:, you can set the identity mapper with the global configuration setting,
proxied-authorization-identity-mapper. For example, if you get user ID values
from the client, such as bjensen, you can configure OpenDJ directory server to
use the exact match identity mapper to match those to DNs based on an attribute
of the entry. Use the dsconfig command interactively to determine the settings
you need.
2.9
76
-genkey \
-alias myapp-cert \
-keyalg rsa \
-dname "cn=My App,ou=Apps,dc=example,dc=com" \
-keystore keystore \
-storepass changeit \
-keypass changeit
2.
-selfcert \
-alias myapp-cert \
-validity 7300 \
-keystore keystore \
-storepass changeit \
-keypass changeit
3.
77
$ keytool \
-list \
-v \
-alias myapp-cert \
-keystore keystore \
-storepass changeit
Alias name: myapp-cert
Creation date: Jan 18, 2013
Entry type: PrivateKeyEntry
Certificate chain length: 1
Certificate[1]:
Owner: CN=My App, OU=Apps, DC=example, DC=com
Issuer: CN=My App, OU=Apps, DC=example, DC=com
Serial number: 5ae2277
Valid from: Fri Jan 18 18:27:09 CET 2013 until: Thu Jan 13 18:27:09 CET 2033
Certificate fingerprints:
MD5: 48:AC:F9:13:11:E0:AB:C4:65:A2:83:9E:DB:FE:0C:37
SHA1: F9:61:54:37:AA:C1:BC:92:45:07:64:4B:23:6C:BC:C9:CD:1D:44:0F
SHA256: 2D:B1:58:CD:33:40:E9:...:FD:61:EA:C9:FF:6A:19:93:FE:E4:84:E3
Signature algorithm name: SHA256withRSA
Version: 3
Extensions:
#1: ObjectId: 2.5.29.14 Criticality=false
SubjectKeyIdentifier [
KeyIdentifier [
0000: 54 C0 C5 9C 73 37 85 4B
F2 3B D3 37 FD 45 0A AB
0010: C9 6B 32 95
]
]
4.
T...s7.K.;.7.E..
.k2.
-export \
-alias myapp-cert \
-keystore keystore \
-storepass changeit \
-keypass changeit \
-file myapp-cert.crt
Certificate stored in file </path/to/myapp-cert.crt>
5.
78
If you want to map the certificate subject DN to an attribute of the entry, use
the ds-certificate-subject-dn attribute:
$ cat addcert.ldif
$ ldapmodify \
--port 1389 \
--hostname opendj.example.com \
--bindDN "cn=Directory Manager" \
--bindPassword password \
--filename addcert.ldif
Processing MODIFY request for cn=My App,ou=Apps,dc=example,dc=com
MODIFY operation successful for DN cn=My App,ou=Apps,dc=example,dc=com
6.
79
$ ldapsearch \
--port 1389 \
--hostname opendj.example.com \
--baseDN dc=example,dc=com \
"(cn=My App)"
dn: cn=My App,ou=Apps,dc=example,dc=com
ds-certificate-fingerprint: 4B:F5:CF:2C:2D:B3:86:14:FF:43:A8:37:17:DD:E7:55
userCertificate;binary:: MIIDOzCCAiOgAwIBAgIESfC6IjANBgkqhkiG9w0BAQsFADBOMRMwEQY
KCZImiZPyLGQBGRYDY29tMRcwFQYKCZImiZPyLGQBGRYHZXhhbXBsZTENMAsGA1UECxMEQXBwczEPMA
0GA1UEAxMGTXkgQXBwMB4XDTEzMDExNzE3MTEwM1oXDTEzMDQxNzE3MTEwM1owTjETMBEGCgmSJomT8
ixkARkWA2NvbTEXMBUGCgmSJomT8ixkARkWB2V4YW1wbGUxDTALBgNVBAsTBEFwcHMxDzANBgNVBAMT
Bk15IEFwcDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJQYq+jG4ZQdNkyBT4OQBZ0sFkl
X5o2yBViDMGl1sSWIRGLpFwu6iq1chndPBJYTC+FkT66yEEOwWOpSfcYdFHkMQP0qp5A8mgP6bYkeH1
ROvQ1nhLs0ILuksR10CVIQ5b1zv6bGEFhA9gSKmpHfQOSt9PXq8+kuz+4RgZk9Il28tgDNMm91wSJr7
kqi5g7a2a7Io5s9L2FeLhVSBYwinWQnASk8nENrhcE0hHkrpGsaxdhIQBQQvm+SRC0dI4E9iwBGI3Lw
lV3a4KTa5DlYD6cDREI6B8XlSdc1DaIhwC8CbsE0WJQoCERSURdjkuHrPck6f69HKUFRiC7JMT3dFbs
CAwEAAaMhMB8wHQYDVR0OBBYEFFTAxZxzN4VL8jvTN/1FCqvJazKVMA0GCSqGSIb3DQEBCwUAA4IBAQ
BXsAIEw7I5XUzLFHvXb2N0hmW/Vmhb/Vlv9LTT8JcCRJy4zaiyS9Q+Sp9zQUkrXauFnNAhJLwpAymjZ
MCOq1Th1bw9LnIzbccPQ/1+ZHLKDU5pgnc5BcvaV6Zl6COLLH2OOt0XMZ/OrODBV1M6STfhChqcowff
xp72pWMQe+kpZfzjeDBk4kK2hUNTZsimB9qRyrDAMCIXdmdmFv1o07orxjy8c/6S1329swiiVqFckBR
aXIa8wCcXjpQbZacDODeKk6wZIKxw4miLg1YByCMa7vkUfz+Jj+JHgbHjyoT/G82mtDbX02chLgXbDm
xJPFN3mwAC7NEkSPbqd35nJlf3
objectClass: person
objectClass: inetOrgPerson
objectClass: organizationalPerson
objectClass: ds-certificate-user
objectClass: top
ds-certificate-subject-dn: CN=My App, OU=Apps, DC=example, DC=com
cn: My App
sn: App
7.
When using a self-signed certificate, import the client certificate into the
truststore for OpenDJ.
When the client presents its certificate to OpenDJ, by default OpenDJ must
trust the client certificate before it can accept the connection. If OpenDJ
cannot trust the client certificate, it cannot establish a secure connection:
80
$ keytool \
-import \
-alias myapp-cert \
-file /path/to/myapp-cert.crt \
-keystore /path/to/opendj/config/truststore \
-storepass `cat /path/to/opendj/config/keystore.pin`
Owner: CN=My App, OU=Apps, DC=example, DC=com
Issuer: CN=My App, OU=Apps, DC=example, DC=com
Serial number: 5ae2277
Valid from: Fri Jan 18 18:27:09 CET 2013 until: Thu Jan 13 18:27:09 CET 2033
Certificate fingerprints:
MD5: 48:AC:F9:13:11:E0:AB:C4:65:A2:83:9E:DB:FE:0C:37
SHA1: F9:61:54:37:AA:C1:BC:92:45:07:64:4B:23:6C:BC:C9:CD:1D:44:0F
SHA256: 2D:B1:58:CD:33:40:E9:...:FD:61:EA:C9:FF:6A:19:93:FE:E4:84:E3
Signature algorithm name: SHA256withRSA
Version: 3
Extensions:
#1: ObjectId: 2.5.29.14 Criticality=false
SubjectKeyIdentifier [
KeyIdentifier [
0000: 54 C0 C5 9C 73 37 85 4B
F2 3B D3 37 FD 45 0A AB
0010: C9 6B 32 95
]
]
T...s7.K.;.7.E..
.k2.
8.
$JAVA_HOME/jre/lib/security/cacerts holds the certificates for many CAs. To get the full list, use the following
command:
$ keytool \
-list \
-v \
-keystore $JAVA_HOME/jre/lib/security/cacerts \
-storepass changeit
81
$ keytool \
-import \
-alias ca-cert \
-file ca.crt \
-keystore /path/to/opendj/config/truststore \
-storepass `cat /path/to/opendj/config/keystore.pin`
Owner: EMAILADDRESS=admin@example.com, CN=Example CA, O=Example Corp, C=FR
Issuer: EMAILADDRESS=admin@example.com, CN=Example CA, O=Example Corp, C=FR
Serial number: d4586ea05c878b0c
Valid from: Tue Jan 29 09:30:31 CET 2013 until: Mon Jan 24 09:30:31 CET 2033
Certificate fingerprints:
MD5: 8A:83:61:9B:E7:18:A2:21:CE:92:94:96:59:68:60:FA
SHA1: 01:99:18:38:3A:57:D7:92:7B:D6:03:8C:7B:E4:1D:37:45:0E:29:DA
SHA256: 5D:20:F1:86:CC:CD:64:50:1E:54:...:DF:15:43:07:69:44:00:FB:36:CF
Signature algorithm name: SHA1withRSA
Version: 3
Extensions:
#1: ObjectId: 2.5.29.35 Criticality=false
AuthorityKeyIdentifier [
KeyIdentifier [
0000: 30 07 67 7D 1F 09 B6 E6
90 85 95 58 94 37 FD 31 0.g........X.7.1
0010: 03 D4 56 7B
..V.
]
[EMAILADDRESS=admin@example.com, CN=Example CA, O=Example Corp, C=FR]
SerialNumber: [
d4586ea0 5c878b0c]
]
#2: ObjectId: 2.5.29.19 Criticality=false
BasicConstraints:[
CA:true
PathLen:2147483647
]
#3: ObjectId: 2.5.29.14 Criticality=false
SubjectKeyIdentifier [
KeyIdentifier [
0000: 30 07 67 7D 1F 09 B6 E6
90 85 95 58 94 37 FD 31
0010: 03 D4 56 7B
]
]
0.g........X.7.1
..V.
9.
Stopping Server...
...
... The Directory Server has started successfully
82
Here, pkcs12-store is the file name of the PKCS #12 format store.
2.
Configure the OpenDJ PKCS12 trust manager provider to use the PKCS #12
store, and restart OpenDJ server to force it to read the store.
In the following example, the store password is changeit:
$ dsconfig \
set-trust-manager-provider-prop \
--port 4444 \
--hostname opendj.example.com \
--bindDN "cn=Directory Manager" \
--bindPassword password \
--provider-name PKCS12 \
--set enabled:true \
--set trust-store-pin:changeit \
--no-prompt \
--trustAll
$ stop-ds --restart
3.
83
$ dsconfig \
set-connection-handler-prop \
--port 4444 \
--hostname opendj.example.com \
--bindDN "cn=Directory Manager" \
--bindPassword password \
--handler-name "LDAPS Connection Handler" \
--set trust-manager-provider:PKCS12 \
--no-prompt \
--trustAll
4.
--port 1636 \
--hostname opendj.example.com \
--baseDN dc=example,dc=com \
--useSSL \
--useSASLExternal \
--certNickName myapp-cert \
--keyStorePath keystore \
--keyStorePassword changeit \
--trustStorePath /path/to/opendj/config/keystore \
--trustStorePasswordFile /path/to/opendj/config/keystore.pin \
"(cn=My App)" userPassword
dn: cn=My App,ou=Apps,dc=example,dc=com
userPassword: {SSHA}9jjvsv9wlTW7Ikflzc2/wMNBjAN6G4CbbTKYIw==
84
list-certificate-mappers \
--port 4444 \
--hostname opendj.example.com \
--bindDN "cn=Directory Manager" \
--bindPassword password
Certificate Mapper
: Type
: enabled
------------------------------------:-------------------------------------:-------Fingerprint Mapper
: fingerprint
: true
Subject Attribute to User Attribute : subject-attribute-to-user-attribute : true
Subject DN to User Attribute
: subject-dn-to-user-attribute
: true
Subject Equals DN
: subject-equals-dn
: true
2.
get-certificate-mapper-prop \
--port 4444 \
--hostname opendj.example.com \
--bindDN "cn=Directory Manager" \
--bindPassword password \
--mapper-name "Fingerprint Mapper"
Property
: Value(s)
----------------------:--------------------------enabled
: true
fingerprint-algorithm : md5
fingerprint-attribute : ds-certificate-fingerprint
user-base-dn
: -
3.
85
$ dsconfig \
set-certificate-mapper-prop \
--port 4444 \
--hostname opendj.example.com \
--bindDN "cn=Directory Manager" \
--bindPassword password \
--mapper-name "Fingerprint Mapper" \
--set fingerprint-algorithm:sha1 \
--no-prompt
4.
Set the External SASL Mechanism Handler to use the appropriate certificate
mapper (default: Subject Equals DN).
Client applications use the SASL External mechanism during the bind to have
OpenDJ set the authorization identifier based on the entry that matches the
client certificate:
$ dsconfig \
set-sasl-mechanism-handler-prop \
--port 4444 \
--hostname opendj.example.com \
--bindDN "cn=Directory Manager" \
--bindPassword password \
--handler-name External \
--set certificate-mapper:"Fingerprint Mapper" \
--no-prompt
Also, if OpenDJ directory server uses a certificate for StartTLS that was not
signed by a well-known CA, import the appropriate certificate into the client
keystore, which can then double as a truststore. For example, if OpenDJ uses a
self-signed certificate, import the server certificate into the keystore:
86
$ keytool \
-export \
-alias server-cert \
-file server-cert.crt \
-keystore /path/to/opendj/config/keystore \
-storepass `cat /path/to/opendj/config/keystore.pin`
$ keytool \
-import \
-trustcacerts \
-alias server-cert \
-file server-cert.crt \
-keystore keystore \
-storepass `cat keystore.pin`
If OpenDJ directory server uses a CA-signed certificate, but the CA is not wellknown, import the CA certificate into your keystore:
$ keytool \
-import \
-trustcacerts \
-alias ca-cert \
-file ca-cert.crt \
-keystore keystore \
-storepass `cat keystore.pin`
Now that you can try the example, notice that OpenDJ does not return the
userPassword value for an anonymous search:
$ ldapsearch \
--port 1389 \
--hostname opendj.example.com \
--baseDN dc=example,dc=com \
--useStartTLS \
--trustStorePath keystore \
--trustStorePasswordFile keystore.pin \
"(cn=My App)" userPassword
dn: cn=My App,ou=Apps,dc=example,dc=com
OpenDJ does let users read the values of their own userPassword attributes after
they bind successfully:
87
$ ldapsearch \
--port 1389 \
--hostname opendj.example.com \
--baseDN dc=example,dc=com \
--useStartTLS \
--useSASLExternal \
--certNickName myapp-cert \
--keyStorePath keystore \
--keyStorePasswordFile keystore.pin \
--trustStorePath keystore \
--trustStorePasswordFile keystore.pin \
"(cn=My App)" userPassword
dn: cn=My App,ou=Apps,dc=example,dc=com
userPassword: {SSHA}vy/vTthOQoV/wH3MciTOBKKR4OX+0dSN/a09Ew==
You can also try the same test with other certificate mappers:
88
# Fingerprint mapper
$ dsconfig \
set-sasl-mechanism-handler-prop \
--port 4444 \
--hostname opendj.example.com \
--bindDN "cn=Directory Manager" \
--bindPassword password \
--handler-name External \
--set certificate-mapper:"Fingerprint Mapper" \
--no-prompt
$ ldapsearch \
--port 1389 \
--hostname opendj.example.com \
--baseDN dc=example,dc=com \
--useStartTLS \
--useSASLExternal \
--certNickName myapp-cert \
--keyStorePath keystore \
--keyStorePasswordFile keystore.pin \
--trustStorePath keystore \
--trustStorePasswordFile keystore.pin \
"(cn=My App)" userPassword
dn: cn=My App,ou=Apps,dc=example,dc=com
userPassword: {SSHA}vy/vTthOQoV/wH3MciTOBKKR4OX+0dSN/a09Ew==
set-sasl-mechanism-handler-prop \
--port 4444 \
--hostname opendj.example.com \
--bindDN "cn=Directory Manager" \
--bindPassword password \
--handler-name External \
--set certificate-mapper:"Subject Attribute to User Attribute" \
--no-prompt
$ ldapsearch \
--port 1389 \
--hostname opendj.example.com \
--baseDN dc=example,dc=com \
--useStartTLS \
--useSASLExternal \
--certNickName myapp-cert \
--keyStorePath keystore \
--keyStorePasswordFile keystore.pin \
--trustStorePath keystore \
--trustStorePasswordFile keystore.pin \
"(cn=My App)" userPassword
dn: cn=My App,ou=Apps,dc=example,dc=com
userPassword: {SSHA}vy/vTthOQoV/wH3MciTOBKKR4OX+0dSN/a09Ew==
set-sasl-mechanism-handler-prop \
--port 4444 \
--hostname opendj.example.com \
--bindDN "cn=Directory Manager" \
--bindPassword password \
--handler-name External \
--set certificate-mapper:"Subject DN to User Attribute" \
--no-prompt
$ ldapsearch \
--port 1389 \
--hostname opendj.example.com \
--baseDN dc=example,dc=com \
--useStartTLS \
--useSASLExternal \
89
90
Chapter 3
91
3.1
objectClasses
Object class definitions identify the attribute types that an entry must
have, and may have. Examples of object classes include person and
organizationalUnit. Object classes inherit from other object classes. For
example, inetOrgPerson inherits from person.
Object classes are specified as values of an entry's objectClass attribute.
An object class can be one of the following:
Structural object classes define the core structure of the entry, generally
representing a real-world object.
By default, OpenDJ directory entries have a single structural object class or
at least a single line of structural object class inheritance.
The person object class is structural, for example.
92
matchingRules
A Matching rule determines how the directory server compares attribute
values to assertion values for LDAP search and LDAP compare operations.
For example, in a search having the filter (uid=bjensen) the assertion value is
bjensen.
nameForms
A name form specifies which attribute can be used as the relative DN (RDN)
for a structural object class.
dITStructureRules
--port 1389 \
--baseDN "cn=schema" \
--searchScope base \
--dontWrap \
"(&)" \
objectClasses \
| grep \'person\'
objectClasses: ( 2.5.6.6 NAME 'person' SUP top STRUCTURAL MUST ( sn $ cn )
MAY ( userPassword $ telephoneNumber $ seeAlso $ description )
X-ORIGIN 'RFC 4519' )
93
Notice the use of the object class name in grep \'person\' to filter search results.
The actual result would not be wrapped.
The object class defines which attributes an entry of that object class must
have and which attributes the entry may optionally have. A person entry must
have a cn and an sn attribute. A person entry may optionally have userPassword,
telephoneNumber, seeAlso, and description attributes.
To determine definitions of those attributes, read the LDAP schema as
demonstrated in Example3.3, "Reading Schema Definitions for an Attribute".
Example 3.3. Reading Schema Definitions for an Attribute
The following example shows you how to read the schema definition for the cn
attribute:
$ ldapsearch \
--port 1389 \
--baseDN "cn=schema" \
--searchScope base \
--dontWrap \
"(&)" \
attributeTypes \
| grep \'cn\'
attributeTypes: ( 2.5.4.3 NAME ( 'cn' 'commonName' ) SUP name X-ORIGIN 'RFC 4519' )
The cn attribute inherits its definition from the name attribute. That attribute
definition indicates attribute syntax and matching rules as shown in the following
example:
$ ldapsearch \
--port 1389 \
--baseDN "cn=schema" \
--searchScope base \
--dontWrap \
"(&)" \
attributeTypes \
| grep \'name\'
attributeTypes: ( 2.5.4.41 NAME 'name' EQUALITY caseIgnoreMatch
SUBSTR caseIgnoreSubstringsMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{32768} X-ORIGIN 'RFC 4519' )
This means that the server ignores case when matching a common name value.
Use the OID to read the syntax as shown in the following example:
94
$ ldapsearch \
--port 1389 \
--baseDN "cn=schema" \
--searchScope base \
--dontWrap \
"(&)" \
ldapSyntaxes \
| grep 1.3.6.1.4.1.1466.115.121.1.15
ldapSyntaxes: ( 1.3.6.1.4.1.1466.115.121.1.15 DESC 'Directory String' )
Taken together with the information for the name attribute, the common name
attribute value is a Directory String of at most 32,768 characters. For details
about syntaxes, read RFC 4517, Lightweight Directory Access Protocol (LDAP):
Syntaxes and Matching Rules. That document describes a Directory String as one
or more UTF-8 characters.
3.2
95
96
$ ldapmodify \
--port 1389 \
--bindDN "uid=kvaughan,ou=people,dc=example,dc=com" \
--bindPassword bribery
dn: uid=bjensen,ou=People,dc=example,dc=com
changetype: modify
add: undefined
undefined: This attribute is not defined.
Processing MODIFY request for uid=bjensen,ou=People,dc=example,dc=com
MODIFY operation failed
Result Code: 65 (Object Class Violation)
Additional Information: Entry uid=bjensen,ou=People,dc=example,dc=com cannot
be modified because the resulting entry would have violated the server schema:
Entry uid=bjensen,ou=People,dc=example,dc=com violates
the Directory Server schema configuration because
it includes attribute undefined which is not allowed
by any of the objectclasses defined in that entry
The solution in this case is to make sure that the undefined attribute is defined
and that it is allowed by one of the object classes defined for the entry.
The following request fails to add a second structural object class:
$ ldapmodify \
--port 1389 \
--bindDN "uid=kvaughan,ou=people,dc=example,dc=com" \
--bindPassword bribery
dn: uid=bjensen,ou=People,dc=example,dc=com
changetype: modify
add: objectClass
objectClass: organizationalUnit
Processing MODIFY request for uid=bjensen,ou=People,dc=example,dc=com
MODIFY operation failed
Result Code: 65 (Object Class Violation)
Additional Information: Entry uid=bjensen,ou=People,dc=example,dc=com cannot
be modified because the resulting entry would have violated the server schema:
Entry uid=bjensen,ou=People,dc=example,dc=com violates
the Directory Server schema configuration because
it includes multiple conflicting structural objectclasses
inetOrgPerson and organizationalUnit.
Only a single structural objectclass is allowed in an entry
The solution in this case is to define only one structural object class for the entry.
Either Babs Jensen is a person or an organizational unit, but not both.
Example 3.5. Invalid Attribute Syntax
The following request fails to add an empty string as a common name attribute
value:
97
$ ldapmodify \
--port 1389 \
--bindDN "uid=kvaughan,ou=people,dc=example,dc=com" \
--bindPassword bribery
dn: uid=bjensen,ou=People,dc=example,dc=com
changetype: modify
add: cn
cn:
Processing MODIFY request for uid=bjensen,ou=People,dc=example,dc=com
MODIFY operation failed
Result Code: 21 (Invalid Attribute Syntax)
Additional Information: When attempting to modify entry
uid=bjensen,ou=People,dc=example,dc=com to add one or more values
for attribute cn, value "" was found to be invalid
according to the associated syntax:
The operation attempted to assign a zero-length value to an attribute
with the directory string syntax
3.3
98
$ ldapmodify \
--port 1389 \
--bindDN "uid=kvaughan,ou=people,dc=example,dc=com" \
--bindPassword bribery
dn: uid=bjensen,ou=People,dc=example,dc=com
changetype: modify
add: objectClass
objectClass: extensibleObject
add: undefined
undefined: This attribute is not defined in the LDAP schema.
add: serialNumber
serialNumber: This attribute is not allowed according to the object classes.
Processing MODIFY request for uid=bjensen,ou=People,dc=example,dc=com
MODIFY operation successful for DN uid=bjensen,ou=People,dc=example,dc=com
Use of the extensibleObject object class opens the door to abuse and can prevent
interoperability. Restrict its use to cases where no better alternative is available.
3.4
This file contains a core set of attribute type and object class definitions from
the following Internet-Drafts, RFCs, and standards:
draft-ietf-boreham-numsubordinates
draft-findlay-ldap-groupofentries
draft-furuseth-ldap-untypedobject
draft-good-ldap-changelog
draft-ietf-ldup-subentry
draft-wahl-ldap-adminaddr
RFC 1274
RFC 2079
RFC 2256
RFC 2798
RFC 3045
RFC 3296
RFC 3671
RFC 3672
RFC 4512
RFC 4519
RFC 4523
RFC 4524
99
RFC 4530
RFC 5020
X.501
01-pwpolicy.ldif
This file contains schema definitions from draft-behera-ldap-passwordpolicy (Draft 09), which defines a mechanism for storing password policy
information in an LDAP directory server.
02-config.ldif
This file contains the attribute type and objectclass definitions for use with
the directory server configuration.
03-changelog.ldif
03-rfc2713.ldif
This file contains schema definitions from RFC 2713, which defines a
mechanism for storing serialized Java objects in the directory server.
03-rfc2714.ldif
This file contains schema definitions from RFC 2714, which defines a
mechanism for storing CORBA objects in the directory server.
03-rfc2739.ldif
This file contains schema definitions from RFC 2739, which defines a
mechanism for storing calendar and vCard objects in the directory server.
Note that the definition in RFC 2739 contains a number of errors, and this
schema file has been altered from the standard definition in order to fix a
number of those problems.
03-rfc2926.ldif
This file contains schema definitions from RFC 2926, which defines
a mechanism for mapping between Service Location Protocol (SLP)
advertisements and LDAP.
03-rfc3112.ldif
This file contains schema definitions from RFC 3112, which defines the
authentication password schema.
03-rfc3712.ldif
This file contains schema definitions from RFC 3712, which defines a
mechanism for storing printer information in the directory server.
03-uddiv3.ldif
This file contains schema definitions from RFC 4403, which defines a
mechanism for storing UDDIv3 information in the directory server.
100
04-rfc2307bis.ldif
05-rfc4876.ldif
This file contains schema definitions from RFC 4876, which defines a schema
for storing Directory User Agent (DUA) profiles and preferences in the
directory server.
05-samba.ldif
This file contains schema definitions required when storing Samba user
accounts in the directory server.
05-solaris.ldif
This file contains schema definitions required for Solaris and OpenSolaris
LDAP naming services.
06-compat.ldif
This file contains the attribute type and objectclass definitions for use with
the directory server configuration.
101
102
Chapter 4
103
Tip
The examples in this chapter are written with the assumption
that an ou=Groups,dc=example,dc=com entry already exists. If you
imported data from Example.ldif, then you already have the
entry. If you generated data during setup and did not create an
organizational unit for groups yet, create the entry before you
try the examples:
$ ldapmodify \
--defaultAdd \
--port 1389 \
--bindDN "cn=Directory Manager" \
--bindPassword password
dn: ou=Groups,dc=example,dc=com
objectClass: organizationalunit
objectClass: top
ou: Groups
Processing ADD request for ou=Groups,dc=example,dc=com
ADD operation successful for DN ou=Groups,dc=example,dc=com
4.1
Tip
Large static groups can be a performance bottleneck. The
recommended way to avoid the issue is to use dynamic groups
instead as described in Section4.2, "Creating Dynamic Groups".
If using dynamic groups is not an option for a deployment
with large static groups that are updated regularly, use an
entry cache. For details, see Section18.3.6, Caching Large,
Frequently Used Entries in the OpenDJ Administration Guide.
Static group entries can take the standard object class groupOfNames where each
member attribute value is a distinguished name of an entry, or groupOfUniqueNames
104
where each uniqueMember attribute value has Name and Optional UID syntax.
Like other LDAP attributes, member and uniqueMember attributes take sets of
unique values.
Static group entries can also have the object class groupOfEntries, which is like
groupOfNames except that it is designed to allow groups not to have members.
When creating a group entry, use groupOfNames or groupOfEntries where possible.
To create a static group, add a group entry such as the following to the directory:
$ cat static.ldif
$ ldapmodify \
--port 1389 \
--bindDN "cn=Directory Manager" \
--bindPassword password \
--defaultAdd \
--filename static.ldif
Processing ADD request for cn=My Static Group,ou=Groups,dc=example,dc=com
ADD operation successful for DN cn=My Static Group,ou=Groups,dc=example,dc=com
Name and Optional UID syntax values are a DN optionally followed by #BitString. The BitString, such as
'0101111101'B, serves to distinguish the entry from another entry having the same DN, which can occur when the
original entry was deleted and a new entry created with the same DN.
105
$ cat add2grp.ldif
$ ldapmodify \
--port 1389 \
--bindDN "cn=Directory Manager" \
--bindPassword password \
--filename add2grp.ldif
Processing MODIFY request for cn=My Static Group,ou=Groups,dc=example,dc=com
MODIFY operation successful for DN
cn=My Static Group,ou=Groups,dc=example,dc=com
$ ldapsearch \
--port 1389 \
--baseDN dc=example,dc=com \
"(cn=My Static Group)"
dn: cn=My Static Group,ou=Groups,dc=example,dc=com
ou: Groups
objectClass: groupOfNames
objectClass: top
member: uid=ahunter,ou=People,dc=example,dc=com
member: uid=bjensen,ou=People,dc=example,dc=com
member: uid=tmorris,ou=People,dc=example,dc=com
member: uid=scarter,ou=People,dc=example,dc=com
cn: My Static Group
RFC 4519 says a groupOfNames entry must have at least one member. Although
OpenDJ allows you to create a groupOfNames without members, strictly speaking,
that behavior is not standard. Alternatively, you can use the groupOfEntries
object class as shown in the following example:
106
$ cat group-of-entries.ldif
$ ldapmodify \
--port 1389 \
--bindDN "cn=Directory Manager" \
--bindPassword password \
--defaultAdd \
--filename group-of-entries.ldif
Processing ADD request for
cn=Initially Empty Static Group,ou=Groups,dc=example,dc=com
ADD operation successful for DN
cn=Initially Empty Static Group,ou=Groups,dc=example,dc=com
$ cat add-members.ldif
$ ldapmodify \
--port 1389 \
--bindDN "cn=Directory Manager" \
--bindPassword password \
--filename add-members.ldif
Processing MODIFY request for
cn=Initially Empty Static Group,ou=Groups,dc=example,dc=com
MODIFY operation successful for DN
cn=Initially Empty Static Group,ou=Groups,dc=example,dc=com
4.2
107
$ cat dynamic.ldif
$ ldapmodify \
--port 1389 \
--bindDN "cn=Directory Manager" \
--bindPassword password \
--defaultAdd \
--filename dynamic.ldif
Processing ADD request for cn=My Dynamic Group,ou=Groups,dc=example,dc=com
ADD operation successful for DN cn=My Dynamic Group,ou=Groups,dc=example,dc=com
--port 1389 \
--baseDN dc=example,dc=com \
"(&(uid=*jensen)(isMemberOf=cn=My Dynamic Group,ou=Groups,dc=example,dc=com))" \
mail
dn: uid=bjensen,ou=People,dc=example,dc=com
mail: bjensen@example.com
dn: uid=rjensen,ou=People,dc=example,dc=com
mail: rjensen@example.com
$ ldapmodify \
--port 1389 \
--bindDN "cn=Directory Manager" \
--bindPassword password
dn: uid=ajensen,ou=People,dc=example,dc=com
changetype: modify
replace: l
l: San Francisco
Processing MODIFY request for uid=ajensen,ou=People,dc=example,dc=com
MODIFY operation successful for DN uid=ajensen,ou=People,dc=example,dc=com
^D
$ ldapsearch \
--port 1389 \
--baseDN dc=example,dc=com \
"(&(uid=*jensen)(isMemberOf=cn=My Dynamic Group,ou=Groups,dc=example,dc=com))" \
mail
dn: uid=ajensen,ou=People,dc=example,dc=com
mail: ajensen@example.com
dn: uid=bjensen,ou=People,dc=example,dc=com
mail: bjensen@example.com
dn: uid=rjensen,ou=People,dc=example,dc=com
mail: rjensen@example.com
108
4.3
set-virtual-attribute-prop \
--port 4444 \
--hostname opendj.example.com \
--bindDN "cn=Directory Manager" \
--bindPassword password \
--name "Virtual Static member" \
--set allow-retrieving-membership:true \
--trustAll \
--no-prompt
The following example creates a virtual static group, and reads the group entry
with all members:
109
$ cat virtual.ldif
$ ldapmodify \
--port 1389 \
--bindDN "cn=Directory Manager" \
--bindPassword password \
--defaultAdd \
--filename virtual.ldif
Processing ADD request for cn=Virtual Static,ou=Groups,dc=example,dc=com
ADD operation successful for DN cn=Virtual Static,ou=Groups,dc=example,dc=com
110
4.4
--port 1389 \
--baseDN dc=example,dc=com \
uid=bjensen \
isMemberOf
dn: uid=bjensen,ou=People,dc=example,dc=com
isMemberOf: cn=My Static Group,ou=Groups,dc=example,dc=com
isMemberOf: cn=Virtual Static,ou=Groups,dc=example,dc=com
isMemberOf: cn=My Dynamic Group,ou=Groups,dc=example,dc=com
4.5
$ ldapmodify \
--port 1389 \
--bindDN "cn=Directory Manager" \
--bindPassword password \
--defaultAdd \
--filename /path/to/the-big-shots.ldif
Processing ADD request for cn=The Big Shots,ou=Groups,dc=example,dc=com
ADD operation successful for DN cn=The Big Shots,ou=Groups,dc=example,dc=com
Although not shown in the example above, OpenDJ lets you nest groups within
nested groups, too.
OpenDJ lets you create dynamic groups of groups. The following example shows
a group of other groups. The members of this group are themselves groups, not
users:
111
$ cat /path/to/group-of-groups.ldif
$ ldapmodify \
--port 1389 \
--bindDN "cn=Directory Manager" \
--bindPassword password \
--defaultAdd \
--filename /path/to/group-of-groups.ldif
Processing ADD request for cn=Group of Groups,ou=Groups,dc=example,dc=com
ADD operation successful for DN cn=Group of Groups,ou=Groups,dc=example,dc=com
Use the isMemberOf attribute to determine what groups a member belongs to,
as described in Section4.4, "Looking Up Group Membership". The following
example requests groups that Kirsten Vaughan belongs to:
$ ldapsearch \
--port 1389 \
--baseDN dc=example,dc=com \
uid=kvaughan \
isMemberOf
dn: uid=kvaughan,ou=People,dc=example,dc=com
isMemberOf: cn=Directory Administrators,ou=Groups,dc=example,dc=com
isMemberOf: cn=HR Managers,ou=groups,dc=example,dc=com
isMemberOf: cn=The Big Shots,ou=Groups,dc=example,dc=com
--port 1389 \
--baseDN dc=example,dc=com \
"(cn=Directory Administrators)" \
isMemberOf
dn: cn=Directory Administrators,ou=Groups,dc=example,dc=com
isMemberOf: cn=Group of Groups,ou=Groups,dc=example,dc=com
isMemberOf: cn=The Big Shots,ou=Groups,dc=example,dc=com
The following example shows which groups each group belong to:
112
$ ldapsearch \
--port 1389 \
--baseDN dc=example,dc=com \
ou=Groups \
isMemberOf
dn: ou=Groups,dc=example,dc=com
dn: cn=Accounting Managers,ou=groups,dc=example,dc=com
isMemberOf: cn=Group of Groups,ou=Groups,dc=example,dc=com
isMemberOf: cn=The Big Shots,ou=Groups,dc=example,dc=com
dn: cn=Directory Administrators,ou=Groups,dc=example,dc=com
isMemberOf: cn=Group of Groups,ou=Groups,dc=example,dc=com
isMemberOf: cn=The Big Shots,ou=Groups,dc=example,dc=com
dn: cn=HR Managers,ou=groups,dc=example,dc=com
isMemberOf: cn=Group of Groups,ou=Groups,dc=example,dc=com
isMemberOf: cn=The Big Shots,ou=Groups,dc=example,dc=com
dn: cn=PD Managers,ou=groups,dc=example,dc=com
isMemberOf: cn=Group of Groups,ou=Groups,dc=example,dc=com
isMemberOf: cn=The Big Shots,ou=Groups,dc=example,dc=com
dn: cn=QA Managers,ou=groups,dc=example,dc=com
isMemberOf: cn=Group of Groups,ou=Groups,dc=example,dc=com
isMemberOf: cn=The Big Shots,ou=Groups,dc=example,dc=com
dn: cn=My Static Group,ou=Groups,dc=example,dc=com
isMemberOf: cn=Group of Groups,ou=Groups,dc=example,dc=com
dn: cn=My Dynamic Group,ou=Groups,dc=example,dc=com
dn: cn=The Big Shots,ou=Groups,dc=example,dc=com
isMemberOf: cn=Group of Groups,ou=Groups,dc=example,dc=com
dn: cn=Group of Groups,ou=Groups,dc=example,dc=com
4.6
113
$ dsconfig \
set-plugin-prop \
--port 4444 \
--hostname opendj.example.com \
--bindDN "cn=Directory Manager" \
--bindPassword password \
--plugin-name "Referential Integrity" \
--set enabled:true \
--trustAll \
--no-prompt
With the plugin enabled, you can see OpenDJ referential integrity resolving group
membership automatically:
$ ldapsearch --port 1389 --baseDN dc=example,dc=com "(cn=My Static Group)"
dn: cn=My Static Group,ou=Groups,dc=example,dc=com
ou: Groups
objectClass: groupOfNames
objectClass: top
member: uid=ahunter,ou=People,dc=example,dc=com
member: uid=bjensen,ou=People,dc=example,dc=com
member: uid=tmorris,ou=People,dc=example,dc=com
member: uid=scarter,ou=People,dc=example,dc=com
cn: My Static Group
$ ldapdelete \
--port 1389 \
--bindDN "cn=Directory Manager" \
--bindPassword password \
uid=scarter,ou=People,dc=example,dc=com
Processing DELETE request for uid=scarter,ou=People,dc=example,dc=com
DELETE operation successful for DN uid=scarter,ou=People,dc=example,dc=com
114
You can also configure the referential integrity plugin to check that new entries
added to groups actually exist in the directory by setting the check-references
property to true. You can specify additional criteria once you have activated
the check. To ensure that entries added must match a filter, set the checkreferences-filter-criteria to identify the attribute and the filter. For example,
you can specify that group members must be person entries by setting checkreferences-filter-criteria to member:(objectclass=person). To ensure that
entries must be located in the same naming context, set check-references-scopecriteria to naming-context.
115
116
Chapter 5
5.1
Virtual Attributes
Virtual attributes augment directory entries with attribute values that OpenDJ
directory server computes or obtains dynamically. Virtual attribute values do not
exist in persistent storage. They help to limit the amount of data that needs to
be stored and are great for some uses, such as determining the groups a users
belongs to or adding an ETag to an entry.
Do not index virtual attributes. Virtual attribute values generated by the server
when they are read. They are not designed to be stored in a persistent index.
Since you do not index virtual attributes, searching on a virtual attribute can
result in an unindexed search. For an unindexed search OpenDJ directory
server potentially has to go through all entries to look for candidate matches.
Looking through all entries is resource-intensive for large directories. By default,
OpenDJ directory server allows only the Directory Manager superuser to perform
unindexed searches. Generally avoid searches that use a simple filter with a
117
Virtual Attributes
virtual attribute. Instead, consider the alternatives. You can assign a password
policy to a group as described in Procedure10.5, To Assign a Password Policy
to a Group in the OpenDJ Administration Guide. The procedure use a virtual
attribute only in a subtree specification filter. If you must use a virtual attribute
in a search filter, use it in a complex search filter after narrowing the search by
filtering on an indexed attribute. For example, the following filter first narrows
the search based on the user's ID before checking group membership. Make sure
that the user performing the search has access to read isMemberOf in the results:
(&(uid=user-id)(isMemberOf=group-dn))
Two virtual attributes, entryDN and isMemberOf, can also be used in simple
equality filters. The following example shows how to add access to read
isMemberOf and then run a search that returns the common names for members of
a group:
$ ldapmodify \
--hostname opendj.example.com \
--port 1389 \
--bindDN "cn=Directory Manager" \
--bindPassword password
dn: dc=example,dc=com
changetype: modify
add: aci
aci: (targetattr="isMemberOf")(version 3.0;
acl "See isMemberOf"; allow (read,search,compare) groupdn=
"ldap:///cn=Directory Administrators,ou=Groups,dc=example,dc=com";)
Processing MODIFY request for dc=example,dc=com
MODIFY operation successful for DN dc=example,dc=com
$ ldapsearch \
--hostname opendj.example.com \
--port 1389 \
--baseDN dc=example,dc=com \
--bindDN uid=kvaughan,ou=people,dc=example,dc=com \
--bindPassword bribery \
"(isMemberOf=cn=Directory Administrators,ou=Groups,dc=example,dc=com)" \
cn
dn: uid=hmiller,ou=People,dc=example,dc=com
cn: Harry Miller
dn: uid=kvaughan,ou=People,dc=example,dc=com
cn: Kirsten Vaughan
dn: uid=rdaugherty,ou=People,dc=example,dc=com
cn: Robert Daugherty
118
Virtual Attributes
entryUUID
etag
Entity tag as defined in RFC 2616, useful for checking whether an entry has
changed since you last read it from the directory.
hasSubordinates
numSubordinates
isMemberOf
member
uniqueMember
pwdPolicySubentry
subschemaSubentry
collectiveAttributeSubentries
governingStructureRule
References the rule on what type of subordinates the entry can have.
structuralObjectClass
119
Collective Attributes
These virtual attributes are typically operational, so you get them back from a
search only when you request them:
$ ldapsearch --port 1389 --baseDN dc=example,dc=com dc=example
dn: dc=example,dc=com
dc: example
objectClass: domain
objectClass: top
You can use the existing virtual attribute types to create your own virtual
attributes, and you can also use the user-defined type to create your own virtual
attribute types. The virtual attribute is defined by the server configuration, which
is not replicated:
$ dsconfig \
create-virtual-attribute \
--hostname opendj.example.com \
--port 4444 \
--bindDN "cn=Directory Manager" \
--bindPassword password \
--name "Served By Description" \
--type user-defined \
--set enabled:true \
--set attribute-type:description \
--set base-dn:dc=example,dc=com \
--set value:"Served by OpenDJ.Example.com" \
--trustAll \
--no-prompt
Collective attributes cover many use cases better than virtual attributes.
5.2
Collective Attributes
Collective attributes provide a standard mechanism for defining attributes that
appear on all the entries in a subtree potentially filtered by object class. Standard
collective attribute type names have the prefix c-.
OpenDJ extends collective attributes to make them easier to use. You can define
any OpenDJ attribute as collective using the ;collective attribute option. You can
use LDAP filters in your subtree specification for fine-grained control over which
entries have the collective attributes.
120
Collective Attributes
You can have entries inherit attributes from other entries through collective
attributes. You establish the relationship between entries either by indicating
the attribute holding the DN of the entry from which to inherit the attributes, or
by specifying how to construct the RDN of the entry from which to inherit the
attributes.
Procedure6.3, To Add Privileges For a Group of Administrators in the OpenDJ
Administration Guide demonstrates setting administrative privileges in OpenDJ
using collective attributes. The following examples demonstrate additional ways
to use collective attributes:
Example5.1, "Class of Service With Collective Attributes"
Example5.2, "Inheriting an Attribute From the Manager's Entry"
Example5.3, "Inheriting Attributes From the Locality"
Example 5.1. Class of Service With Collective Attributes
This example defines attributes that specify services available to a user
depending on their service level.
Note
The following example depends on the cos object class, and the
classOfService attribute type defined but commented out in the
Example.ldif file imported as sample data. To try this example
for yourself, add the attribute type and object class definitions
in comments near the top of the file, and then uncomment
the objectClass: cos and classOfService attribute lines in
Example.ldif before importing the data into OpenDJ.
Collective Attributes
You define collective attributes in the user data using a subentry. In other words,
collective attributes can be replicated. Collective attributes use attributes defined
in the directory schema. First, add the mailQuote and diskQuota attributes, and
adjust the definition of the cos object class to allow the two quota attributes:
$ cat quotas.ldif
dn: cn=schema
changetype: modify
add: attributeTypes
attributeTypes: ( example-class-of-service-attribute-type NAME 'classOfService
' EQUALITY caseIgnoreMatch ORDERING caseIgnoreOrderingMatch SUBSTR caseIgnore
SubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE USAGE user
Applications X-ORIGIN 'OpenDJ Documentation Examples' )
add: attributeTypes
attributeTypes: ( example-class-of-service-disk-quota NAME 'diskQuota
' EQUALITY caseIgnoreMatch ORDERING caseIgnoreOrderingMatch SUBSTR case
IgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 USAGE user
Applications X-ORIGIN 'OpenDJ Documentation Examples' )
add: attributeTypes
attributeTypes: ( example-class-of-service-mail-quota NAME 'mailQuota
' EQUALITY caseIgnoreMatch ORDERING caseIgnoreOrderingMatch SUBSTR case
IgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 USAGE user
Applications X-ORIGIN 'OpenDJ Documentation Examples' )
add: objectClasses
objectClasses: ( example-class-of-service-object-class NAME 'cos' SUP top AUX
ILIARY MAY ( classOfService $ diskQuota $ mailQuota ) X-ORIGIN 'OpenDJ Doc
umentation Examples' )
$ ldapmodify \
--port 1389 \
--bindDN "cn=Directory Manager" \
--bindPassword password \
--filename quotas.ldif
Processing MODIFY request for cn=schema
MODIFY operation successful for DN cn=schema
Use the following collective attribute definitions to set the quotas depending on
class of service:
# cos.ldif: quotas by class of service
dn: cn=Bronze Class of Service,dc=example,dc=com
objectClass: collectiveAttributeSubentry
objectClass: extensibleObject
objectClass: subentry
objectClass: top
cn: Bronze Class of Service
diskQuota;collective: 10 GB
mailQuota;collective: 1 GB
subtreeSpecification: { base "ou=People", specificationFilter "(classOfService=
bronze)" }
dn: cn=Silver Class of Service,dc=example,dc=com
objectClass: collectiveAttributeSubentry
122
Collective Attributes
objectClass: extensibleObject
objectClass: subentry
objectClass: top
cn: Silver Class of Service
diskQuota;collective: 50 GB
mailQuota;collective: 5 GB
subtreeSpecification: { base "ou=People", specificationFilter "(classOfService=
silver)" }
dn: cn=Gold Class of Service,dc=example,dc=com
objectClass: collectiveAttributeSubentry
objectClass: extensibleObject
objectClass: subentry
objectClass: top
cn: Gold Class of Service
diskQuota;collective: 100 GB
mailQuota;collective: 10 GB
subtreeSpecification: { base "ou=People", specificationFilter "(classOfService=
gold)" }
You can add the collective attribute subentries by using the ldapmodify
command:
$ ldapmodify \
--port 1389 \
--bindDN "cn=Directory Manager" \
--bindPassword password \
--defaultAdd \
--filename cos.ldif
Processing ADD request for cn=Bronze Class of Service,dc=example,dc=com
ADD operation successful for DN cn=Bronze Class of Service,dc=example,dc=com
Processing ADD request for cn=Silver Class of Service,dc=example,dc=com
ADD operation successful for DN cn=Silver Class of Service,dc=example,dc=com
Processing ADD request for cn=Gold Class of Service,dc=example,dc=com
ADD operation successful for DN cn=Gold Class of Service,dc=example,dc=com
With the collective attributes defined, you can see the results on user entries:
123
Collective Attributes
$ ldapsearch \
--port 1389 \
--baseDN dc=example,dc=com \
uid=bjensen \
classOfService mailQuota diskQuota
dn: uid=bjensen,ou=People,dc=example,dc=com
mailQuota: 1 GB
classOfService: bronze
diskQuota: 10 GB
$ ldapsearch \
--port 1389 \
--baseDN dc=example,dc=com \
uid=kvaughan \
classOfService mailQuota diskQuota
dn: uid=kvaughan,ou=People,dc=example,dc=com
mailQuota: 5 GB
classOfService: silver
diskQuota: 50 GB
$ ldapsearch \
--port 1389 \
--baseDN dc=example,dc=com \
uid=scarter \
classOfService mailQuota diskQuota
dn: uid=scarter,ou=People,dc=example,dc=com
mailQuota: 10 GB
classOfService: gold
diskQuota: 100 GB
124
Collective Attributes
This entry specifies that users inherit department number from their manager.
As seen in Example.ldif, Babs Jensen's manager is Torrey Rigden:
dn: uid=bjensen,ou=People,dc=example,dc=com
manager: uid=trigden, ou=People, dc=example,dc=com
125
Collective Attributes
This specifies that the RDN of the entry to inherit attributes from is like
l=localityName,ou=Locations, where localityName is the value of the l
(localityName) attribute on the user's entry.
In other words, if the user's entry has l: Bristol, then the RDN of the entry from
which to inherit attributes starts with l=Bristol,ou=Locations. The actual entry
looks like this:
dn: l=Bristol,ou=Locations,dc=example,dc=com
objectClass: top
objectClass: locality
objectClass: extensibleObject
l: Bristol
street: 60 Queen Square
preferredLanguage: en-gb
The subentry also specifies two attributes to inherit for preferred language and
street address.
The object class extensibleObject is added to allow the entry to take a preferred
1
language.
Notice the last line of the collective attribute subentry:
collectiveConflictBehavior: real-overrides-virtual
This line indicates that if a collective attribute clashes with a real attribute,
the real value takes precedence over the virtual, collective value. You can also
set collectiveConflictBehavior to virtual-overrides-real for the opposite
precedence, or to merge-real-and-virtual to keep both sets of values.
1
The object class extensibleObject means, "Let me add whatever attributes I want." It is usually better practice to
add your own auxiliary object class if you need to decorate an entry with more attributes. The shortcut is taken here
as the focus of this example is not schema extension, but instead how to use collective attributes.
126
Collective Attributes
Here, users can set their own language preferences. When users set language
preferences manually, the collective attribute subentry is configured to give the
user's settings precedence over the locality-based setting, which is only a default
guess.
Sam Carter is located in Bristol. Sam has specified no preferred languages:
dn: uid=scarter,ou=People,dc=example,dc=com
l: Bristol
Sam inherits both the street address and also preferred language from the Bristol
locality:
$ ldapsearch --port 1389 --baseDN dc=example,dc=com uid=scarter \
preferredLanguage street
dn: uid=scarter,ou=People,dc=example,dc=com
preferredLanguage: en-gb
street: 60 Queen Square
Babs's locality is San Francisco. Babs prefers English, but also knows Korean:
dn: uid=bjensen,ou=People,dc=example,dc=com
preferredLanguage: en, ko;q=0.8
l: San Francisco
Babs inherits the street address from the San Francisco locality, but keeps her
language preferences:
$ ldapsearch --port 1389 --baseDN dc=example,dc=com uid=bjensen \
preferredLanguage street
dn: uid=bjensen,ou=People,dc=example,dc=com
preferredLanguage: en, ko;q=0.8
street: 500 3rd Street
127
128
Chapter 6
Note
Some clients follow referrals on your behalf by default. The
OpenDJ ldapsearch command does not follow referrals.
Referrals are used, for example, when some directory data are temporarily
unavailable due to maintenance. Referrals can also be used when a container
holds only some of the directory data for a suffix and points to other containers
for branches whose data is not available locally.
In this chapter you will learn how to:
Add referrals with the ldapmodify command
Remove referrals with the ldapmodify command
You can also use the Manage Entries window of the control panel to handle
referrals.
129
About Referrals
6.1
About Referrals
Referrals are implemented as entries with LDAP URL ref attribute values that
point elsewhere. The ref attribute type is required by the referral object class.
The referral object class is structural, however, and therefore cannot by default
be added to an entry that already has a structural object class defined. When
adding a ref attribute type to an existing entry, you can use the extensibleObject
auxiliary object class.
When a referral is set, OpenDJ returns the referral to client applications
requesting the affected entry or child entries. Client applications must be capable
of following the referral returned. When the directory server responds, for
example, to your search with referrals to one or more LDAP URLs, your client
then constructs new searches from the LDAP URLs returned, and tries again.
6.2
Managing Referrals
To create an LDAP referral, either create a referral entry, or add the
extensibleObject object class and the ref attribute with an LDAP URL to an
existing entry. This section demonstrates use of the latter approach:
$ cat referral.ldif
dn: ou=People,dc=example,dc=com
changetype: modify
add: objectClass
objectClass: extensibleObject
add: ref
ref: ldap://opendj.example.com:2389/ou=People,dc=example,dc=com
$ ldapmodify \
--port 1389 \
--bindDN "cn=Directory Manager" \
--bindPassword password \
--filename referral.ldif
Processing MODIFY request for ou=People,dc=example,dc=com
MODIFY operation successful for DN ou=People,dc=example,dc=com
130
Managing Referrals
To access the entry instead of the referral, use the Manage DSAIT control:
$ ldapsearch \
--port 1389 \
--baseDN dc=example,dc=com \
--control ManageDSAIT:true \
ou=people \
ref
dn: ou=People,dc=example,dc=com
ref: ldap://opendj.example.com:2389/ou=People,dc=example,dc=com
$ cat people.ldif
dn: ou=People,dc=example,dc=com
changetype: modify
delete: ref
ref: ldap://opendj.example.com:2389/ou=People,dc=example,dc=com
$ ldapmodify \
--port 1389 \
--bindDN "cn=Directory Manager" \
--bindPassword password \
--filename people.ldif
Processing MODIFY request for ou=People,dc=example,dc=com
MODIFY operation successful for DN ou=People,dc=example,dc=com
A referral entry ou=People,dc=example,dc=com indicates that the operation must
be processed at a different server
[ldap://opendj.example.com:2389/ou=People,dc=example,dc=com]
$ ldapmodify \
--port 1389 \
--bindDN "cn=Directory Manager" \
--bindPassword password \
--control ManageDSAIT \
--filename people.ldif
Processing MODIFY request for ou=People,dc=example,dc=com
MODIFY operation successful for DN ou=People,dc=example,dc=com
The example above shows how to remove the referral using the Manage DSAIT
control with the ldapmodify command.
131
132
Index
Authenticating, 70
Referrals, 129
Replication
Not for virtual attributes, 120
Resetting passwords, 31, 67
REST, 1
Certificates, 76
Changing passwords, 31, 68, 69
Collective attributes, 120
Commands, 43
Comparing attribute values, 57
Groups
Dynamic, 107
Membership, 111
Nested, 111
Referential integrity, 113
Static, 104
Virtual static, 109
Schema, 91
Bundled definitions, 99
Reading definitions, 92
Respecting definitions, 95
Searching data, 49
SSL, 76
StartTLS, 76
Updating data, 57
Filtering, 61
HTTP, 1
Identity mappers, 71
JSON, 1
LDIF
Examples, 57
Passwords
Changing, 67
Ports
Settings for tools, 69
Proxied authorization, 73
133
134