Configuration Examples
Note
Examples in this documentation depend on features activated in the ds-evaluation
setup profile.
For details, see "Learn About the Evaluation Setup Profile".
The example REST to LDAP mapping configuration file, config/rest2ldap/endpoints/api/example-v1.json
, works well with the ds-evaluation
setup profile. For most deployments, you must customize the mapping to expose additional information.
This example demonstrates how to configure an additional mapping for a custom LDAP object called affiliateObject
, whose affiliate
attribute references user entries. This demonstration extends a server set up with the ds-evaluation
profile. The mappings will also work if you are using the REST to LDAP gateway.
To begin, add the schema for the custom LDAP object, and an example LDAP entry with the object class:
$ldapmodify \ --hostname localhost \ --port 1636 \ --useSsl \ --usePkcs12TrustStore /path/to/opendj/config/keystore \ --trustStorePasswordFile /path/to/opendj/config/keystore.pin \ --bindDN uid=admin \ --bindPassword password << EOF dn: cn=schema changetype: modify add: attributeTypes attributeTypes: ( affiliate-oid NAME 'affiliate' SUP distinguishedName X-SCHEMA-FILE '99-example-mods.ldif' ) - add: objectClasses objectClasses: ( affiliateObject-oid NAME 'affiliateObject' SUP top STRUCTURAL MUST cn MAY ( description $ affiliate ) X-SCHEMA-FILE '99-example-mods.ldif' ) EOF
$ldapmodify \ --hostname localhost \ --port 1636 \ --useSsl \ --usePkcs12TrustStore /path/to/opendj/config/keystore \ --trustStorePasswordFile /path/to/opendj/config/keystore.pin \ --bindDN "uid=kvaughan,ou=people,dc=example,dc=com" \ --bindPassword bribery << EOF dn: ou=affiliates,dc=example,dc=com objectClass: top objectClass: organizationalUnit ou: affiliates dn: cn=My Affiliates,ou=affiliates,dc=example,dc=com objectClass: top objectClass: affiliateObject cn: My Affiliates affiliate: uid=bjensen,ou=People,dc=example,dc=com affiliate: uid=kvaughan,ou=People,dc=example,dc=com EOF
For details, see LDAP Schema and "Add Entries".
In the REST to LDAP mapping configuration file, define an affiliates
collection subresource, and an examples:affiliates:1.0
resource type that describes the objects in the affiliates
collection. This causes the REST API to add an /api/affiliates
path next to /api/users
and /api/groups
. Under /api/affiliates
, REST clients access the affiliate JSON resources.
The following definition for the affiliates
collection subresource belongs in the set of subResources
in the mapping configuration file:
// This sub-resource definition maps JSON resources under /api/affiliates // to LDAP entries under ou=affiliates,dc=example,dc=com. "affiliates" : { "type": "collection", "dnTemplate": "ou=affiliates,dc=example,dc=com", "resource": "examples:affiliates:1.0", "namingStrategy": { "type": "clientDnNaming", "dnAttribute": "cn" } }
The parser for the REST to LDAP mapping configuration file is lenient. It lets you include comments in the JSON, although the JSON standard does not allow comments.
The following definition for the examples:affiliates:1.0
resource type belongs in the set of example-v1
resource types in the mapping configuration file:
// An "affiliate" resource includes property mappings for an "affiliateObject", // which is identified by the "cn" attribute. The affiliate DNs reference person entries. // Rather than return DNs in JSON resources, a mapper returns identifiers and display names. "examples:affiliates:1.0": { "superType": "frapi:opendj:rest2ldap:object:1.0", "objectClasses": [ "affiliateObject" ], "properties": { "displayName": { "type": "simple", "ldapAttribute": "cn", "isRequired": true, "writability": "createOnly" }, "affiliate": { "type": "reference", "resourcePath": "../../users", "ldapAttribute": "affiliate", "isMultiValued": true }, "description": { "type": "simple" } } }
Alternatively, download example-v1.json to replace the existing file.
Replace the directory configuration file,config/rest2ldap/endpoints/api/example-v1.json
, with the updated mapping configuration file. If you have not already done so, enable HTTP access. For details, see "Configure HTTP User APIs". The following example forces the Rest2ldap endpoint to reread its configuration:
$dsconfig \ set-http-endpoint-prop \ --hostname localhost \ --port 4444 \ --bindDN uid=admin \ --bindPassword password \ --endpoint-name "/api" \ --set enabled:false \ --usePkcs12TrustStore /path/to/opendj/config/keystore \ --trustStorePasswordFile /path/to/opendj/config/keystore.pin \ --no-prompt
$dsconfig \ set-http-endpoint-prop \ --hostname localhost \ --port 4444 \ --bindDN uid=admin \ --bindPassword password \ --endpoint-name "/api" \ --set enabled:true \ --usePkcs12TrustStore /path/to/opendj/config/keystore \ --trustStorePasswordFile /path/to/opendj/config/keystore.pin \ --no-prompt
With the updated REST to LDAP mapping configuration loaded, REST clients can access affiliates:
$curl \ --cacert ca-cert.pem \ --user bjensen:hifalutin \ --silent \ "https://localhost:8443/api/affiliates/my%20affiliates?_fields=affiliate/id&_prettyPrint=true"
{ "_id" : "My Affiliates", "_rev" : "<revision>", "affiliate" : [ { "_id" : "bjensen", "_rev" : "<revision>" }, { "_id" : "kvaughan", "_rev" : "<revision>" } ] }
This example demonstrates how to add a per-server password policy over REST. Per-server password policies are set in the server configuration, and not replicated. You must create them on each replica.
The password policy in this example includes:
A password history setting to retain the last five password values.
The same password storage scheme as the default password policy.
The default random password generator.
The default length-based password validator.
The default dictionary password validator, which is available, but not enabled by default.
With the default password policy, a user can change their password to password
. A password policy with the default dictionary validator would not allow this:
$curl \ --request POST \ --cacert ca-cert.pem \ --user bjensen:hifalutin \ --header "Content-Type: application/json" \ --data '{"oldPassword": "hifalutin", "newPassword": "password"}' \ --silent \ "https://localhost:8443/api/users/bjensen?_action=modifyPassword&dryRun=true&passwordQualityAdvice=true"
{}
Update the server configuration to enable the password policy for all Example.com users:
Enable the default dictionary password validator:
$
curl \ --request PATCH \ --user admin:password \ --data '[{"operation": "replace", "field": "/enabled", "value": true}]' \ --cacert ca-cert.pem \ --header "Content-Type: application/json" \ --silent \ "https://localhost:8443/admin/config/password-validators/Dictionary"
Add the password policy:
$
curl \ --request POST \ --user admin:password \ --data '{ "_id": "Per-Server Password Policy", "_schema": "password-policy", "password-attribute": "userPassword", "default-password-storage-scheme": [{"_id": "PBKDF2-HMAC-SHA256"}], "password-generator": { "_id": "Random Password Generator" }, "password-validator": [{"_id": "Dictionary"}, {"_id": "Length-Based Password Validator"}], "password-history-count": 5 }' \ --cacert ca-cert.pem \ --header "Content-Type: application/json" \ --silent \ "https://localhost:8443/admin/config/password-policies/"
Assign the password policy to users:
The following command adds a virtual attribute that assigns the password policy to all Example.com users:
$
curl \ --request POST \ --user admin:password \ --data '{ "_id": "Password Policy Virtual Attribute", "_schema": "user-defined-virtual-attribute", "enabled": true, "base-dn": [ "ou=people,dc=example,dc=com" ], "filter": [ "(objectClass=person)" ], "attribute-type": "ds-pwp-password-policy-dn", "value": [ "cn=Per-Server Password Policy,cn=Password Policies,cn=config" ] }' \ --cacert ca-cert.pem \ --header "Content-Type: application/json" \ --silent \ "https://localhost:8443/admin/config/virtual-attributes/"
Check that the new policy does not let a user change their password to password
:
$curl \ --request POST \ --cacert ca-cert.pem \ --user bjensen:hifalutin \ --header "Content-Type: application/json" \ --data '{"oldPassword": "hifalutin", "newPassword": "password"}' \ --silent \ "https://localhost:8443/api/users/bjensen?_action=modifyPassword&dryRun=true&passwordQualityAdvice=true"
{ "code" : 400, "reason" : "Bad Request", "message" : "Constraint Violation: The provided new password failed the validation checks defined in the server: The provided password contained a word from the server's dictionary", "detail" : { "passwordQualityAdvice" : { "passingCriteria" : [ { "type" : "length-based", "parameters" : { "min-password-length" : 6, "max-password-length" : 0 } } ], "failingCriteria" : [ { "type" : "dictionary", "parameters" : { "case-sensitive-validation" : false, "min-substring-length" : 5, "test-reversed-password" : true, "check-substrings" : true } } ] } } }
For details on password policy settings, see "Per-Server Password Policies".
This example demonstrates how to configure REST to LDAP to manage Internet-Draft subentry password policies. This demonstration extends a server set up with the ds-evaluation
profile. The mappings will also work if you are using the REST to LDAP gateway.
To begin, edit the default REST to LDAP mapping file, config/rest2ldap/endpoints/api/example-v1.json
.
Define an /api/subentries
endpoint that exposes LDAP subentries over REST, including password policies, by adding the following under subResources
:
// This subresource definition maps JSON resources under /api/subentries // to subentries under dc=example,dc=com. "subentries" : { "type": "collection", "dnTemplate": "dc=example,dc=com", "resource": "examples:subentry:1.0", "namingStrategy": { "type": "clientDnNaming", "dnAttribute": "cn" } }
Define the subentry and password policy under the other example-v1
resource types:
// A subentry resource maps to a subentry object. "examples:subentry:1.0": { "superType": "frapi:opendj:rest2ldap:object:1.0", "isAbstract": true, "objectClasses": [ "top", "subentry" ], "properties": { "_id": { "type": "simple", "ldapAttribute": "cn", "isRequired": true, "writability": "createOnly" }, "subtreeSpecification": { "type": "simple" } } }, // A passwordPolicy resource maps to a subentry password policy object. // This mapping uses the LDAP attribute names, // except for passwordValidator which maps to the validator in the configuration. "examples:passwordPolicy:1.0": { "superType": "examples:subentry:1.0", "objectClasses": [ "pwdPolicy", "pwdValidatorPolicy", "subentry" ], "properties": { "pwdAttribute": { "type": "simple", "isRequired": true }, "pwdAllowUserChange": { "type": "simple" }, "pwdCheckQuality": { "type": "simple" }, "pwdExpireWarning": { "type": "simple" }, "pwdFailureCountInterval": { "type": "simple" }, "pwdGraceAuthNLimit": { "type": "simple" }, "pwdInHistory": { "type": "simple" }, "pwdLockout": { "type": "simple" }, "pwdLockoutDuration": { "type": "simple" }, "pwdMaxAge": { "type": "simple" }, "pwdMaxFailure": { "type": "simple" }, "pwdMinAge": { "type": "simple" }, "pwdMinLength": { "type": "simple" }, "pwdMustChange": { "type": "simple" }, "pwdSafeModify": { "type": "simple" }, "passwordValidator" : { "type": "reference", "baseDn": "cn=Password Validators,cn=config", "ldapAttribute": "ds-cfg-password-validator", "primaryKey": "cn", "isMultiValued": true, "mapper": { "type": "object", "properties": { "_id": { "type": "simple", "ldapAttribute": "cn", "isRequired": true } } } } }
In LDAP, you can edit user entries to assign password policies. For REST to LDAP, add corresponding password policy properties to the frapi:opendj:rest2ldap:user:1.0
resource type:
// Administrators can read the current password policy. "pwdPolicy": { "type": "reference", "baseDn": "..,..", "ldapAttribute": "pwdPolicySubentry", "primaryKey": "cn", "searchFilter": "(&(objectclass=subentry)(objectclass=pwdPolicy))", "mapper": { "type": "object", "properties": { "_id": { "type": "simple", "ldapAttribute": "cn", "writability": "readOnlyDiscardWrites" } } } }, // Administrators can set a new password policy. "newPwdPolicy": { "type": "reference", "baseDn": "..,..", "ldapAttribute": "ds-pwp-password-policy-dn", "primaryKey": "cn", "searchFilter": "(&(objectclass=subentry)(objectclass=pwdPolicy))", "mapper": { "type": "object", "properties": { "_id": { "type": "simple", "ldapAttribute": "cn", "isRequired": true } } } },
Alternatively, download subentries.json to replace the existing example-v1.json
file.
Replace the directory configuration file,config/rest2ldap/endpoints/api/example-v1.json
, with the updated mapping configuration file. If you have not already done so, enable HTTP access. For details, see "Configure HTTP User APIs". The following example forces the Rest2ldap endpoint to reread its configuration:
$dsconfig \ set-http-endpoint-prop \ --hostname localhost \ --port 4444 \ --bindDN uid=admin \ --bindPassword password \ --endpoint-name "/api" \ --set enabled:false \ --usePkcs12TrustStore /path/to/opendj/config/keystore \ --trustStorePasswordFile /path/to/opendj/config/keystore.pin \ --no-prompt
$dsconfig \ set-http-endpoint-prop \ --hostname localhost \ --port 4444 \ --bindDN uid=admin \ --bindPassword password \ --endpoint-name "/api" \ --set enabled:true \ --usePkcs12TrustStore /path/to/opendj/config/keystore \ --trustStorePasswordFile /path/to/opendj/config/keystore.pin \ --no-prompt
With the augmented mapping in place, grant access for password administrators to edit and assign password policies:
Grant the necessary privileges to edit subentry password policies:
The
subentry-write
privilege lets password administrators edit subentries.The
config-read
privilege lets password administrators reference password validator entries stored in the server configuration.
Grant access to assign password policies, letting password administrators read users'
pwdPolicySubentry
attributes and write users'ds-pwp-password-policy-dn
attributes.Grant access to assign password policies, letting password administrators write the subentry attributes,
ds-pwp-password-validator
andsubtreeSpecification
.
For this demonstration, grant access to the administrator user Kirsten Vaughan:
# Assign privileges: $ldapmodify \ --hostname localhost \ --port 1636 \ --useSsl \ --usePkcs12TrustStore /path/to/opendj/config/keystore \ --trustStorePasswordFile /path/to/opendj/config/keystore.pin \ --bindDN uid=admin \ --bindPassword password << EOF dn: uid=kvaughan,ou=People,dc=example,dc=com changetype: modify add: ds-privilege-name ds-privilege-name: config-read EOF
$ldapmodify \ --hostname localhost \ --port 1636 \ --useSsl \ --usePkcs12TrustStore /path/to/opendj/config/keystore \ --trustStorePasswordFile /path/to/opendj/config/keystore.pin \ --bindDN uid=admin \ --bindPassword password << EOF dn: uid=kvaughan,ou=People,dc=example,dc=com changetype: modify add: ds-privilege-name ds-privilege-name: subentry-write EOF
# Grant access to subentry policies $ldapmodify \ --hostname localhost \ --port 1636 \ --useSsl \ --usePkcs12TrustStore /path/to/opendj/config/keystore \ --trustStorePasswordFile /path/to/opendj/config/keystore.pin \ --bindDN uid=admin \ --bindPassword password << EOF dn: dc=example,dc=com changetype: modify add: aci aci: (targetattr = "pwdPolicySubentry||ds-pwp-password-policy-dn||ds-pwp-password-validator||subtreeSpecification") (version 3.0;acl "Allow Administrators to manage users' password policies"; allow (all) (groupdn = "ldap:///cn=Directory Administrators,ou=Groups,dc=example,dc=com");) EOF
# Grant access to configuration-based policy elements: $dsconfig \ set-access-control-handler-prop \ --hostname localhost \ --port 4444 \ --bindDN uid=admin \ --bindPassword password \ --add "global-aci:(target=\"ldap:///cn=config\")(targetattr=\"*||+\")\ (version 3.0; acl \"Config read for Kirsten Vaughan\"; allow (read,search,compare)\ userdn=\"ldap:///uid=kvaughan,ou=People,dc=example,dc=com\";)" \ --usePkcs12TrustStore /path/to/opendj/config/keystore \ --trustStorePasswordFile /path/to/opendj/config/keystore.pin \ --no-prompt
Create and assign a subentry password policy:
$ curl \
--request PUT \
--cacert ca-cert.pem \
--user kvaughan:bribery \
--header "Content-Type: application/json" \
--header "If-None-Match: *" \
--data '{
"_id": "Subentry Password Policy with Validators",
"_schema": "examples:passwordPolicy:1.0",
"pwdAttribute": "userPassword",
"pwdAllowUserChange": true,
"pwdFailureCountInterval": 300,
"pwdLockout": true,
"pwdLockoutDuration": 300,
"pwdMaxFailure": 3,
"pwdSafeModify": true,
"passwordValidator": [{"_id": "Character Set"}, {"_id": "Length-Based Password Validator"}],
"subtreeSpecification": "{base \"ou=people\", specificationFilter \"(isMemberOf=cn=Directory Administrators,ou=Groups,dc=example,dc=com)\" }"
}' \
--silent \
https://localhost:8443/api/subentries/Subentry%20Password%20Policy%20with%20Validators?_prettyPrint=true
Observe that the password administrator can view which password policy applies:
$curl \ --cacert ca-cert.pem \ --user kvaughan:bribery \ --silent \ "https://localhost:8443/api/users/kvaughan?_fields=pwdPolicy&_prettyPrint=true"
{ "_id" : "kvaughan", "_rev" : "<revision>", "pwdPolicy" : { "_id" : "Subentry Password Policy with Validators" } }
Observe that the field is not visible to regular users:
$curl \ --cacert ca-cert.pem \ --user bjensen:hifalutin \ --silent \ "https://localhost:8443/api/users/bjensen?_fields=pwdPolicy&_prettyPrint=true"
{ "_id" : "bjensen", "_rev" : "<revision>" }
This example demonstrates how to map an additional existing resource at the root of the REST API.
For example, the ds-evaluation
setup profile includes localities:
dn: l=Bristol,ou=Locations,dc=example,dc=com objectClass: top objectClass: locality l: Bristol street: Broad Quay House, Prince Street dn: l=Montbonnot,ou=Locations,dc=example,dc=com objectClass: top l: Montbonnot street: 55 Rue Blaise Pascal dn: l=Lysaker,ou=Locations,dc=example,dc=com objectClass: top objectClass: locality l: Lysaker street: Lysaker Torg 2 dn: l=San Francisco,ou=Locations,dc=example,dc=com objectClass: top objectClass: locality l: San Francisco street: 201 Mission Street Suite 2900 dn: l=Vancouver,ou=Locations,dc=example,dc=com objectClass: top objectClass: locality l: Vancouver street: 201 Northeast Park Plaza Drive
This demonstration adds an /api/locations
next to /api/users
and /api/groups
.
Edit your copy of the default REST API mapping configuration. Add a location subresource at the top level next to users and groups subresource definitions:
"locations": { "type": "collection", "dnTemplate": "ou=locations,dc=example,dc=com", "resource": "frapi:opendj:rest2ldap:location:1.0", "namingStrategy": { "type": "clientDnNaming", "dnAttribute": "l" } }
Add a location schema after the other schema definitions:
"frapi:opendj:rest2ldap:location:1.0": { "superType": "frapi:opendj:rest2ldap:object:1.0", "objectClasses": ["locality"], "properties": { "_id": { "type": "simple", "ldapAttribute": "l", "isRequired": true, "writability": "createOnly" }, "displayName": { "type": "simple", "ldapAttribute": "l", "isRequired": true, "writability": "readOnly" }, "description": { "type": "simple" }, "state": { "type": "simple", "ldapAttribute": "st" }, "street": { "type": "simple" } } }
Alternatively, download example-locations.json to replace the existing example-v1.json
file.
Replace the directory configuration file,config/rest2ldap/endpoints/api/example-v1.json
, with the updated mapping configuration file. If you have not already done so, enable HTTP access. For details, see "Configure HTTP User APIs". The following example forces the Rest2ldap endpoint to reread its configuration:
$dsconfig \ set-http-endpoint-prop \ --hostname localhost \ --port 4444 \ --bindDN uid=admin \ --bindPassword password \ --endpoint-name "/api" \ --set enabled:false \ --usePkcs12TrustStore /path/to/opendj/config/keystore \ --trustStorePasswordFile /path/to/opendj/config/keystore.pin \ --no-prompt
$dsconfig \ set-http-endpoint-prop \ --hostname localhost \ --port 4444 \ --bindDN uid=admin \ --bindPassword password \ --endpoint-name "/api" \ --set enabled:true \ --usePkcs12TrustStore /path/to/opendj/config/keystore \ --trustStorePasswordFile /path/to/opendj/config/keystore.pin \ --no-prompt
With the updated configuration loaded, view the results:
# List locations: $curl \ --cacert ca-cert.pem \ --user bjensen:hifalutin \ --silent \ "https://localhost:8443/api/locations/?_queryFilter=true&_prettyPrint=true"
{ "result" : [ { "_id" : "Bristol", "_rev" : "<revision>", "_schema" : "frapi:opendj:rest2ldap:location:1.0", "_meta" : { }, "displayName" : "Bristol", "description" : "Office locations", "street" : "Broad Quay House, Prince Street" }, { "_id" : "Montbonnot", "_rev" : "<revision>", "_schema" : "frapi:opendj:rest2ldap:location:1.0", "_meta" : { }, "displayName" : "Montbonnot", "description" : "Office locations", "street" : "55 Rue Blaise Pascal" }, { "_id" : "Lysaker", "_rev" : "<revision>", "_schema" : "frapi:opendj:rest2ldap:location:1.0", "_meta" : { }, "displayName" : "Lysaker", "description" : "Office locations", "street" : "Lysaker Torg 2" }, { "_id" : "San Francisco", "_rev" : "<revision>", "_schema" : "frapi:opendj:rest2ldap:location:1.0", "_meta" : { }, "displayName" : "San Francisco", "description" : "Office locations", "street" : "201 Mission Street Suite 2900" }, { "_id" : "Vancouver", "_rev" : "<revision>", "_schema" : "frapi:opendj:rest2ldap:location:1.0", "_meta" : { }, "displayName" : "Vancouver", "description" : "Office locations", "street" : "201 Northeast Park Plaza Drive" } ], "resultCount" : 5, "pagedResultsCookie" : null, "totalPagedResultsPolicy" : "NONE", "totalPagedResults" : -1, "remainingPagedResults" : -1 }
# Read a single location: $curl \ --cacert ca-cert.pem \ --user bjensen:hifalutin \ --silent \ "https://localhost:8443/api/locations/montbonnot?_prettyPrint=true"
{ "_id" : "Montbonnot", "_rev" : "<revision>", "_schema" : "frapi:opendj:rest2ldap:location:1.0", "displayName" : "Montbonnot", "street" : "55 Rue Blaise Pascal" }
This example demonstrates how to add device resources under user resources. Use this pattern when you have LDAP child entries under parent entries.
For example, the ds-evaluation
setup profile includes a user with two subordinate devices:
dn: uid=nbohr,ou=People,dc=example,dc=com objectClass: person objectClass: cos objectClass: inetOrgPerson objectClass: organizationalPerson objectClass: posixAccount objectClass: top uid: nbohr classOfService: gold userpassword: password facsimileTelephoneNumber: +1 408 555 1213 givenName: Niels cn: Niels Bohr telephoneNumber: +1 408 555 1212 sn: Bohr roomNumber: 0007 homeDirectory: /home/nbohr mail: nbohr@example.com l: Vancouver ou: People uidNumber: 1111 gidNumber: 1000 description: Quantum device example dn: cn=quantum dot,uid=nbohr,ou=People,dc=example,dc=com objectClass: device objectClass: top cn: quantum dot serialNumber: WI-3005 owner: uid=nbohr,ou=People,dc=example,dc=com dn: cn=qubit generator,uid=nbohr,ou=People,dc=example,dc=com objectClass: device objectClass: top cn: qubit generator serialNumber: XF551426 owner: uid=nbohr,ou=People,dc=example,dc=com
In your REST API, the users collection contains zero or more user resources. Each user resource can have a devices collection that contains zero or more device resources. The path to a device is /users/uid/devices/cn
, where uid is the user UID, and cn is the device CN. For example, /users/nbohr/devices/quantum dot
.
Edit your copy of the default REST API mapping configuration. Add the subresources definition to the frapi:opendj:rest2ldap:user:1.0
schema:
"subResources": { "devices": { "type": "collection", "resource": "frapi:opendj:rest2ldap:device:1.0", "namingStrategy": { "type": "clientDnNaming", "dnAttribute": "cn" } } }
Add a device schema after the other schema definitions:
"frapi:opendj:rest2ldap:device:1.0": { "superType": "frapi:opendj:rest2ldap:object:1.0", "objectClasses": ["device"], "properties": { "_id": { "type": "simple", "ldapAttribute": "cn", "isRequired": true, "writability": "createOnly" }, "displayName": { "type": "simple", "ldapAttribute": "cn", "isRequired": true, "writability": "readOnly" }, "description": { "type": "simple" }, "owner": { "type": "reference", "resourcePath": "../..", "ldapAttribute": "owner" }, "serialNumber": { "type": "simple" } } }
Alternatively, download example-subresources.json to replace the default example-v1.json
file.
Replace the directory configuration file,config/rest2ldap/endpoints/api/example-v1.json
, with the updated mapping configuration file. If you have not already done so, enable HTTP access. For details, see "Configure HTTP User APIs". The following example forces the Rest2ldap endpoint to reread its configuration:
$dsconfig \ set-http-endpoint-prop \ --hostname localhost \ --port 4444 \ --bindDN uid=admin \ --bindPassword password \ --endpoint-name "/api" \ --set enabled:false \ --usePkcs12TrustStore /path/to/opendj/config/keystore \ --trustStorePasswordFile /path/to/opendj/config/keystore.pin \ --no-prompt
$dsconfig \ set-http-endpoint-prop \ --hostname localhost \ --port 4444 \ --bindDN uid=admin \ --bindPassword password \ --endpoint-name "/api" \ --set enabled:true \ --usePkcs12TrustStore /path/to/opendj/config/keystore \ --trustStorePasswordFile /path/to/opendj/config/keystore.pin \ --no-prompt
With the updated configuration loaded, view the results:
# Read the user resource: $curl \ --cacert ca-cert.pem \ --user nbohr:password \ --silent \ "https://localhost:8443/api/users/nbohr?_prettyPrint=true"
{ "_id" : "nbohr", "_rev" : "<revision>", "_schema" : "frapi:opendj:rest2ldap:posixUser:1.0", "userName" : "nbohr@example.com", "displayName" : [ "Niels Bohr" ], "name" : { "givenName" : "Niels", "familyName" : "Bohr" }, "description" : "Quantum device example", "contactInformation" : { "telephoneNumber" : "+1 408 555 1212", "emailAddress" : "nbohr@example.com" }, "uidNumber" : 1111, "gidNumber" : 1000, "homeDirectory" : "/home/nbohr" }
# List devices: $curl \ --cacert ca-cert.pem \ --user nbohr:password \ --silent \ "https://localhost:8443/api/users/nbohr/devices/?_queryFilter=true&_prettyPrint=true"
{ "result" : [ { "_id" : "quantum dot", "_rev" : "<revision>", "_schema" : "frapi:opendj:rest2ldap:device:1.0", "displayName" : "quantum dot", "owner" : { "_id" : "nbohr", "_rev" : "<revision>" }, "serialNumber" : "WI-3005" }, { "_id" : "qubit generator", "_rev" : "<revision>", "_schema" : "frapi:opendj:rest2ldap:device:1.0", "displayName" : "qubit generator", "owner" : { "_id" : "nbohr", "_rev" : "<revision>" }, "serialNumber" : "XF551426" } ], "resultCount" : 2, "pagedResultsCookie" : null, "totalPagedResultsPolicy" : "NONE", "totalPagedResults" : -1, "remainingPagedResults" : -1 }
# Read a device resource: $curl \ --cacert ca-cert.pem \ --user nbohr:password \ --silent \ "https://localhost:8443/api/users/nbohr/devices/quantum%20dot?_prettyPrint=true"
{ "_id" : "quantum dot", "_rev" : "000000007ba330d8", "_schema" : "frapi:opendj:rest2ldap:device:1.0", "displayName" : "quantum dot", "owner" : { "_id" : "nbohr", "_rev" : "<revision>" }, "serialNumber" : "WI-3005" }m
This example demonstrates simple user profiles, starting from JSON and working back to LDAP. The following JSON is a example profile:
{ "externalId": "newuser", "userName": "newuser", "name": { "formatted": "New User", "givenName": "User", "familyName": "New" }, "phoneNumbers": [{ "value": "+1 408 555 1212", "type": "work" }], "emails": [ { "value": "newuser@example.com", "type": "work", "primary": true }, { "value": "newuser@example.org", "type": "personal", "primary": false } ] }
Notice these key features of the example profile:
Unlike Common REST JSON objects, this profile has no
"_id"
field.The
"phoneNumbers"
and"emails"
fields are multi-valued, similar to the LDAPtelephoneNumber
andmail
attributes.However, both JSON fields hold arrays of nested objects. The similar LDAP attributes hold simple values. The other fields have a straightforward mapping to standard LDAP attributes, but these fields do not.
The
"type"
field in a phone number or email is an arbitrary label assigned by the user.
This example assumes that the user profile does not exist in LDAP yet. The aim is therefore to translate JSON objects into LDAP entries.
LDAP attributes generally hold values with tightly defined syntaxes, not arbitrary objects. JSON syntax attributes are an exception. The example profile holds a mix of fields that map directly to LDAP attributes, and fields that do not.
When determining them LDAP attributes to use, the first step is to look for natural matches with attributes defined in the default schema. Based on the LDAP Schema Reference, you can derive the following table of correspondences:
Profile JSON Field | LDAP Attribute |
---|---|
| |
| |
| |
| |
| |
| No direct match. |
| No direct match. |
For the "emails"
and "phoneNumbers"
fields, there is no natural match. The uddiEMail
and uddiPhone
attributes come the closest because they optionally include a user-defined type. However, avoid those attributes for the following reasons:
These user profiles are unrelated to Universal Description, Discovery, and Integration (UDDI), which is focused instead on storing data for a SOAP-based web services discovery.
All client applications would have to know how to consume the UDDI-specific format. REST to LDAP has no means to transform
type#value
into{"type": "type", "value": "value"}
.The
uddiEmail
has no provision for the"primary"
boolean value.
It would also be possible, but not advisable, to store "emails"
and "phoneNumbers"
in separate LDAP entries. The LDAP user entry could include attributes that reference email address and phone number entries by their DNs. The email and phone number entries could in turn reference users with the standard owner
attribute. Unfortunately, there is no real value in storing email addresses and phone numbers separately from a user's entry. The email and phone settings will not be shared with other entries, but instead belong only to the profile. Client applications will expect to update them atomically with the user profile. A profile change should not require updating an arbitrary number of LDAP entries. Even if they could be conveniently configured as single updates in the REST API, it would mean that updates to a JSON resource could partially fail in LDAP, a source of potential confusion.
An apt choice for the "emails"
and "phoneNumbers"
fields is therefore JSON syntax attributes. This example defines the following LDAP schema definitions for appropriate JSON attributes:
dn: cn=schema changetype: modify add: attributeTypes attributeTypes: ( example-email-oid NAME 'email' DESC 'An email address with an optional user-defined type and primary boolean' EQUALITY caseIgnoreJsonQueryMatch SYNTAX 1.3.6.1.4.1.36733.2.1.3.1 USAGE userApplications X-SCHEMA-FILE '99-example.ldif' X-ORIGIN 'DS Documentation Examples' ) - add: attributeTypes attributeTypes: ( example-phone-number-oid NAME 'phoneNumber' DESC 'A phone number with an optional user-defined type' EQUALITY caseIgnoreJsonQueryMatch SYNTAX 1.3.6.1.4.1.36733.2.1.3.1 USAGE userApplications X-SCHEMA-FILE '99-example.ldif' X-ORIGIN 'DS Documentation Examples' ) - add: objectClasses objectClasses: ( example-profile-user-oid NAME 'profileUser' DESC 'A user with optional profile components having user-defined types' SUP top AUXILIARY MAY ( email $ phoneNumber ) X-SCHEMA-FILE '99-example.ldif' X-ORIGIN 'DS Documentation Examples' )
An auxiliary object class lets an entry that already has a structural object class hold additional attributes. For background information about LDAP schema, read LDAP Schema.
The following corresponding LDIF uses these definitions to define Example.com data with one user profile. Babs Jensen is the administrator and sole initial user. She can create other profiles and reset passwords:
dn: dc=example,dc=com objectClass: domain objectClass: top dc: example aci: (target ="ldap:///dc=example,dc=com") (targetattr != "userPassword") (version 3.0;acl "Authenticated users have read-search access"; allow (read, search, compare)(userdn = "ldap:///all");) aci: (target ="ldap:///dc=example,dc=com") (targetattr = "*") (version 3.0;acl "Allow Babs to administer other entries"; allow (all)(userdn = "ldap:///uid=bjensen,ou=People,dc=example,dc=com");) dn: ou=People,dc=example,dc=com objectClass: organizationalunit objectClass: top ou: People aci: (target ="ldap:///ou=People,dc=example,dc=com") (targetattr = "*") (version 3.0; acl "Allow self management of profiles"; allow (delete, write)(userdn = "ldap:///self");) dn: uid=bjensen,ou=People,dc=example,dc=com objectClass: profileUser objectClass: person objectClass: inetOrgPerson objectClass: organizationalPerson objectClass: top uid: bjensen ou: People cn: Barbara Jensen cn: Babs Jensen givenname: Barbara sn: Jensen userpassword: hifalutin email: {"value": "bjensen@example.com", "type": "work", "primary": true} phoneNumber: {"value": "+1 408 555 1862", "type": "work"} ds-privilege-name: password-reset
Client applications might look up user profiles based on email addresses or phone numbers. They are not likely to look up user profiles based on user-defined labels for emails and phone numbers. Therefore, instead of indexing all fields of each JSON attribute value, index only the values:
Install a directory server with the
ds-evaluation
setup profile.Configure a custom schema provider to allow the server to index only the JSON
"values"
:$
dsconfig \ create-schema-provider \ --hostname localhost \ --port 4444 \ --bindDN uid=admin \ --bindPassword password \ --provider-name "Value JSON Query Matching Rule" \ --type json-query-equality-matching-rule \ --set enabled:true \ --set case-sensitive-strings:false \ --set ignore-white-space:true \ --set matching-rule-name:caseIgnoreJsonQueryMatch \ --set matching-rule-oid:1.3.6.1.4.1.36733.2.1.4.1 \ --set indexed-field:value \ --usePkcs12TrustStore /path/to/opendj/config/keystore \ --trustStorePasswordFile /path/to/opendj/config/keystore.pin \ --no-prompt
Update the LDAP schema:
$
ldapmodify \ --hostname localhost \ --port 1636 \ --useSsl \ --usePkcs12TrustStore /path/to/opendj/config/keystore \ --trustStorePasswordFile /path/to/opendj/config/keystore.pin \ --bindDN uid=admin \ --bindPassword password << EOF dn: cn=schema changetype: modify add: attributeTypes attributeTypes: ( example-email-oid NAME 'email' DESC 'An email address with an optional user-defined type and primary boolean' EQUALITY caseIgnoreJsonQueryMatch SYNTAX 1.3.6.1.4.1.36733.2.1.3.1 USAGE userApplications X-SCHEMA-FILE '99-example.ldif' X-ORIGIN 'DS Documentation Examples' ) - add: attributeTypes attributeTypes: ( example-phone-number-oid NAME 'phoneNumber' DESC 'A phone number with an optional user-defined type' EQUALITY caseIgnoreJsonQueryMatch SYNTAX 1.3.6.1.4.1.36733.2.1.3.1 USAGE userApplications X-SCHEMA-FILE '99-example.ldif' X-ORIGIN 'DS Documentation Examples' ) - add: objectClasses objectClasses: ( example-profile-user-oid NAME 'profileUser' DESC 'A user with optional profile components having user-defined types' SUP top AUXILIARY MAY ( email $ phoneNumber ) X-SCHEMA-FILE '99-example.ldif' X-ORIGIN 'DS Documentation Examples' ) EOF
Add indexes for the
email
andphoneNumber
JSON attributes:$
dsconfig \ create-backend-index \ --hostname localhost \ --port 4444 \ --bindDN uid=admin \ --bindPassword password \ --backend-name dsEvaluation \ --index-name email \ --set index-type:equality \ --usePkcs12TrustStore /path/to/opendj/config/keystore \ --trustStorePasswordFile /path/to/opendj/config/keystore.pin \ --no-prompt
$dsconfig \ create-backend-index \ --hostname localhost \ --port 4444 \ --bindDN uid=admin \ --bindPassword password \ --backend-name dsEvaluation \ --index-name phoneNumber \ --set index-type:equality \ --usePkcs12TrustStore /path/to/opendj/config/keystore \ --trustStorePasswordFile /path/to/opendj/config/keystore.pin \ --no-prompt
Import the LDAP data with Babs's profile:
$
import-ldif \ --hostname localhost \ --port 4444 \ --bindDN uid=admin \ --bindPassword password \ --backendID dsEvaluation \ --ldifFile /path/to/opendjprofiles.ldif \ --usePkcs12TrustStore /path/to/opendj/config/keystore \ --trustStorePasswordFile /path/to/opendj/config/keystore.pin
(Optional) To check your work, read Babs's profile over LDAP:
$
ldapsearch \ --hostname localhost \ --port 1636 \ --useSsl \ --usePkcs12TrustStore /path/to/opendj/config/keystore \ --trustStorePasswordFile /path/to/opendj/config/keystore.pin \ --baseDn dc=example,dc=com \ "(uid=bjensen)"
dn: uid=bjensen,ou=People,dc=example,dc=com objectClass: profileUser objectClass: person objectClass: inetOrgPerson objectClass: organizationalPerson objectClass: top cn: Barbara Jensen cn: Babs Jensen email: {"value": "bjensen@example.com", "type": "work", "primary": true} givenName: Barbara ou: People phoneNumber: {"value": "+1 408 555 1862", "type": "work"} sn: Jensen uid: bjensen
Now that you have sample data in LDAP, create the REST to LDAP mapping.
The REST to LDAP mapping defines the HTTP API and JSON resources that are backed by the LDAP service and entries.
Rather than patch the default example mapping, this example adds a separate API with a new mapping for the profile view.
The new mapping uses the following features:
A
"serverNaming"
naming strategy ensures proper handing of Common REST"_id"
values. LDAP entries useuid
as the RDN. JSON profiles use server-generated"_id"
values.The JSON profiles have
"externalId"
values corresponding to the LDAPuid
. However, the JSON profiles do not define"_id"
values. Client applications will use the"_id"
values when uniquely identifying a profile, but not when looking up a profile.The
"_id"
maps to theentryUUID
LDAP operational attribute. TheentryUUID
attribute is generated on creation and managed by the LDAP server.As in the default example, passwords are not visible in profiles.
Client applications use dedicated REST actions to manage password reset by administrators and to manage password modifications by users. The REST actions for password management require HTTPS.
The
"externalId"
and"userName"
fields both map to LDAPuid
attributes.You could make it possible to update these fields after creation. This example assumes that these fields might be expected not to change, and so restricts client applications from changing them.
The
"formatted"
and"familyName"
fields of the name map to attributes that are required by the LDAP object class,inetOrgPerson
. They are therefore required in the JSON profile as well.
The full mapping file is shown in the following listing:
{ "version": "1.0", "resourceTypes": { "example-mapping": { "subResources": { "users": { "type": "collection", "dnTemplate": "ou=people,dc=example,dc=com", "resource": "examples:user:1.0", "namingStrategy": { "type": "serverNaming", "dnAttribute": "uid", "idAttribute": "entryUUID" } } } }, "frapi:opendj:rest2ldap:object:1.0": { "isAbstract": true, "objectClasses": [ "top" ], "properties": { "_schema": { "type": "resourceType" }, "_rev": { "type": "simple", "ldapAttribute": "etag", "writability": "readOnly" }, "_meta": { "type": "object", "properties": { "created": { "type": "simple", "ldapAttribute": "createTimestamp", "writability": "readOnly" }, "lastModified": { "type": "simple", "ldapAttribute": "modifyTimestamp", "writability": "readOnly" } } } } }, "examples:user:1.0": { "superType": "frapi:opendj:rest2ldap:object:1.0", "objectClasses": [ "profileUser", "person", "organizationalPerson", "inetOrgPerson" ], "supportedActions": [ "modifyPassword", "resetPassword" ], "properties": { "externalId": { "type": "simple", "ldapAttribute": "uid", "isRequired": true, "writability": "createOnlyDiscardWrites" }, "userName": { "type": "simple", "ldapAttribute": "uid", "isRequired": true, "writability": "createOnlyDiscardWrites" }, "name": { "type": "object", "properties": { "formatted": { "type": "simple", "ldapAttribute": "cn", "isRequired": true }, "givenName": { "type": "simple" }, "familyName": { "type": "simple", "ldapAttribute": "sn", "isRequired": true } } }, "emails": { "type": "json", "ldapAttribute": "email", "isMultiValued": true }, "phoneNumbers": { "type": "json", "ldapAttribute": "phoneNumber", "isMultiValued": true } } } } }
Download the mapping file,
example-mapping.json
.Copy the mapping file to the appropriate server REST to LDAP configuration directory:
$
mkdir /path/to/opendj/config/rest2ldap/endpoints/rest
$cp example-mapping.json /path/to/opendj/config/rest2ldap/endpoints/rest/
The default example defines a REST API under
/api
. This example uses/rest
instead.Enable a Rest2ldap endpoint for the API:
$
dsconfig \ create-http-endpoint \ --hostname localhost \ --port 4444 \ --bindDN uid=admin \ --bindPassword password \ --set authorization-mechanism:HTTP\ Basic \ --set config-directory:config/rest2ldap/endpoints/rest \ --set enabled:true \ --type rest2ldap-endpoint \ --endpoint-name /rest \ --usePkcs12TrustStore /path/to/opendj/config/keystore \ --trustStorePasswordFile /path/to/opendj/config/keystore.pin \ --no-prompt
- Look Up a Profile by Email Address
The following query looks for profiles with email addresses containing
bjensen
:$
curl \ --request GET \ --cacert ca-cert.pem \ --user bjensen:hifalutin \ --silent \ "https://localhost:8443/rest/users/?_queryFilter=emails/value+co+'bjensen'"
{ "result" : [ { "_id" : "<generated-id>", "_rev" : "<revision>", "_schema" : "examples:user:1.0", "externalId" : "bjensen", "userName" : "bjensen", "name" : { "formatted" : [ "Barbara Jensen", "Babs Jensen" ], "givenName" : "Barbara", "familyName" : "Jensen" }, "emails" : [ { "value" : "bjensen@example.com", "type" : "work", "primary" : true } ], "phoneNumbers" : [ { "value" : "+1 408 555 1862", "type" : "work" } ] } ], "resultCount" : 1, "pagedResultsCookie" : null, "totalPagedResultsPolicy" : "NONE", "totalPagedResults" : -1, "remainingPagedResults" : -1 }
- Look Up a Profile by Phone Number
The following query looks for profiles with the phone number
+1 408 555 1862
:$
curl \ --request GET \ --cacert ca-cert.pem \ --user bjensen:hifalutin \ --silent \ "https://localhost:8443/rest/users/?_queryFilter=phoneNumbers/value+eq+'%2B1%20408%20555%201862'"
Notice that the telephone number is URL encoded as
%2B1%20408%20555%201862
.- Create a User Profile
This example creates a profile using the JSON in
newuser.json
:$
curl \ --request POST \ --cacert ca-cert.pem \ --user bjensen:hifalutin \ --header "Content-Type: application/json" \ --data @newuser.json \ --silent \ https://localhost:8443/rest/users/
Upon initial creation, the user has no password, and so cannot authenticate, yet.
- Set a Password
Set a password to allow the user to authenticate.
This example uses Bash shell and jq to manipulate JSON on the command line.
The first operation gets the user profile
"_id"
and holds it in anID
environment variable.The second operation performs an administrative password reset as Babs and holds the resulting generated password in a
PASSWORD
environment variable.The third and final operation uses the generated password to authenticate as the user and modify the password:
$
export ID=$(jq --raw-output '.result[0] ._id' \ <(curl --silent --request GET --cacert ca-cert.pem --user bjensen:hifalutin 2>/dev/null \ "https://localhost:8443/rest/users/?_queryFilter=externalId+eq+'newuser'"))
$export PASSWORD=$(jq --raw-output '.generatedPassword' \ <(curl --silent --request POST --cacert ca-cert.pem --user bjensen:hifalutin \ --header "Content-Type: application/json" --data '{}' \ "https://localhost:8443/rest/users/${ID}?_action=resetPassword"))
$curl \ --request POST \ --cacert ca-cert.pem \ --user "newuser:${PASSWORD}" \ --header "Content-Type: application/json" \ --data "{\"oldPassword\": \"${PASSWORD}\", \"newPassword\": \"password\"}" \ --silent \ "https://localhost:8443/rest/users/${ID}?_action=modifyPassword"
- Read Your Profile
This example employs the user profile
"_id"
:$
curl \ --request GET \ --cacert ca-cert.pem \ --user newuser:password \ --silent \ "https://localhost:8443/rest/users/${ID}"
- Changing a User's Own User-Defined Email Type
This example employs the user profile
"_id"
.Download the JSON patch payload,
changeEmailType.json
:$
curl \ --request PATCH \ --cacert ca-cert.pem \ --user newuser:password \ --header "Content-Type: application/json" \ --data @changeEmailType.json \ --silent \ "https://localhost:8443/rest/users/${ID}"
{ "_id" : "<generated-user-id>", "_rev" : "<revision>", "_schema" : "examples:user:1.0", "_meta" : { "created" : "<datestamp>" }, "externalId" : "newuser", "userName" : "newuser", "name" : { "formatted" : "New User", "givenName" : "User", "familyName" : "New" }, "emails" : [ { "value" : "newuser@example.com", "type" : "work", "primary" : true }, { "value" : "newuser@example.org", "type" : "home", "primary" : false } ], "phoneNumbers" : [ { "value" : "+1 408 555 1212", "type" : "work" } ] }
- Remove Your Email Address
This example employs the user profile
"_id"
.Download the JSON patch payload,
removeWorkEmail.json
:$
curl \ --request PATCH \ --cacert ca-cert.pem \ --user newuser:password \ --header "Content-Type: application/json" \ --data @removeWorkEmail.json \ --silent \ "https://localhost:8443/rest/users/${ID}"
{ "_id" : "<generated-user-id>", "_rev" : "<revision>", "_schema" : "examples:user:1.0", "_meta" : { "created" : "<datestamp>" }, "externalId" : "newuser", "userName" : "newuser", "name" : { "formatted" : "New User", "givenName" : "User", "familyName" : "New" }, "emails" : [ { "value" : "newuser@example.org", "type" : "home", "primary" : true } ], "phoneNumbers" : [ { "value" : "+1 408 555 1212", "type" : "work" } ] }
- Add Your Cell Phone Number
This example employs the user profile
"_id"
.Download the JSON patch payload,
addPersonalCell.json
:$
curl \ --request PATCH \ --cacert ca-cert.pem \ --user newuser:password \ --header "Content-Type: application/json" \ --data @addPersonalCell.json \ --silent \ "https://localhost:8443/rest/users/${ID}"
{ "_id" : "<generated-user-id>", "_rev" : "<revision>", "_schema" : "examples:user:1.0", "_meta" : { "created" : "<datestamp>" }, "externalId" : "newuser", "userName" : "newuser", "name" : { "formatted" : "New User", "givenName" : "User", "familyName" : "New" }, "emails" : [ { "value" : "newuser@example.org", "type" : "home", "primary" : true } ], "phoneNumbers" : [ { "value" : "+1 408 555 1212", "type" : "work" }, { "value": "+1 408 555 1234", "type": "personal cell" } ] }
- Delete Your Profile
This example employs the user profile
"_id"
:$
curl \ --request DELETE \ --cacert ca-cert.pem \ --user newuser:password \ --header "Content-Type: application/json" \ --silent \ "https://localhost:8443/rest/users/${ID}"
Note
API descriptors provide runtime documentation for REST APIs. Although it is possible to serve the descriptors at runtime, do not use production servers for this purpose. Instead, prepare the documentation by reading API descriptors from a server with the same API as production servers. Publish the documentation separately.
Preparing documentation for a Rest2ldap endpoint is an iterative process:
Configure the API.
Run a local copy of a tool for viewing OpenAPI documentation, such as Swagger UI.
View the generated documentation through the tool by reading the OpenAPI format descriptor.
For example, read the descriptor for the
/api
endpoint with a URL such ashttps://kvaughan:bribery@localhost:8443/api?_api
.The following screenshot shows example documentation:
If your browser does not display the generated documentation, disable CORS settings. See your browser's documentation or search the web for details.
Update the API configuration.
Force the Rest2ldap endpoint to reread the updated configuration file:
$
dsconfig \ set-http-endpoint-prop \ --hostname localhost \ --port 4444 \ --bindDN uid=admin \ --bindPassword password \ --endpoint-name "/api" \ --set enabled:false \ --usePkcs12TrustStore /path/to/opendj/config/keystore \ --trustStorePasswordFile /path/to/opendj/config/keystore.pin \ --no-prompt
$dsconfig \ set-http-endpoint-prop \ --hostname localhost \ --port 4444 \ --bindDN uid=admin \ --bindPassword password \ --endpoint-name "/api" \ --set enabled:true \ --usePkcs12TrustStore /path/to/opendj/config/keystore \ --trustStorePasswordFile /path/to/opendj/config/keystore.pin \ --no-prompt
Edit the descriptor.
Publish the final descriptor alongside your production service.
Protect production servers from unwanted API descriptor requests, in one of the following ways:
Follow these steps for direct access through a connection handler:
Set
api-descriptor-enabled
tofalse
:$
dsconfig \ set-connection-handler-prop \ --hostname localhost \ --port 4444 \ --bindDN uid=admin \ --bindPassword password \ --handler-name HTTPS \ --set api-descriptor-enabled:false \ --usePkcs12TrustStore /path/to/opendj/config/keystore \ --trustStorePasswordFile /path/to/opendj/config/keystore.pin \ --no-prompt
Restart the connection handler:
$
dsconfig \ set-connection-handler-prop \ --hostname localhost \ --port 4444 \ --bindDN uid=admin \ --bindPassword password \ --handler-name HTTPS \ --set enabled:false \ --usePkcs12TrustStore /path/to/opendj/config/keystore \ --trustStorePasswordFile /path/to/opendj/config/keystore.pin \ --no-prompt
$dsconfig \ set-connection-handler-prop \ --hostname localhost \ --port 4444 \ --bindDN uid=admin \ --bindPassword password \ --handler-name HTTPS \ --set enabled:true \ --usePkcs12TrustStore /path/to/opendj/config/keystore \ --trustStorePasswordFile /path/to/opendj/config/keystore.pin \ --no-prompt
Follow these steps for the REST to LDAP gateway:
Identify the REST to LDAP endpoints you expose.
In the configuration of the HTTP service where the gateway runs, prevent requests to the API descriptors on those endpoints.
Requests for API descriptors are requests that use the reserved query string parameters,
_api
and_crestapi
.If you expect the requests to come from client applications, you could have the HTTP gateway respond immediately with HTTP status code
501 Not Implemented
, or404 Not Found
, and stop processing the request.If you expect the requests to come from users, you could have the HTTP gateway respond immediately with HTTP status code
301 Moved Permanently
and aLocation
header to redirect the user-agent to the URL of the documentation you publish, and stop processing the request.