Directory service


Overview

Description of the directory service.

Directory API operations

Endpoint Examples

An attribute is represented as a key/value pair within the entry JSON object.

Attribute values are given as either a single JSON value, or as an array of JSON values, according whether the attribute is single-valued or multi-valued in the schema.

Individual values can be one of four types: string, integer, boolean, or JSON object. Floating point numbers should be provided as strings since LDAP does not support floating point numbers. Dates are provided in the ISO-8601 format, and binary values are represented as Base64-encoded strings.

Examples:

"createTimestamp": "2018-05-01T15:04:49.866Z"
"ubidEmailJSON": [
  {
    "type": "work",
    "value": "ljones@example.com"
  }
]

Attribute names are always case sensitive and must be specified in the exact form as they appear in the schema. These rules apply for both request and response entities.

Get server metadata

A GET request on the base endpoint, /directory/v1, returns metadata about the server and API. It also returns a list of configured schemas allowed on the server. The following sample shows the GET /directory/v1 operation.

curl -X "GET" "https://ds.example.com/directory/v1" \
-H 'Content-type: application/json' \
-H 'Authorization: Bearer jwtToken'

The response data looks like this:

{
  "vendorName": "Ping Identity Corporation",
  "vendorVersion": "Ping Identity Directory Server 7.1.0.0",
  "startupUUID": "285d32c6-ea6f-4098-b8d3-33532eb18f44",
  "startTime": "20180830201532Z",
  "namingContexts": ["dc=example,dc=com"],
  "_links": {
    "schemas": "https://ds.example.com/directory/v1/schemas"
  }
}

Create an entry

The POST /directory/v1 operation adds a new entry to the directory.

curl -X "POST" "https://ds.example.com/directory/v1" \
-H 'Content-type: application/json' \
-H 'Authorization: Bearer jwtToken' \
-d $'{
  "_dn": "uid=lindajones,ou=people,dc=example,dc=com",
  "objectClass": [ "top", "ubidPerson" ],
  "uid": "lindajones",
  "cn": "Linda Jones",
  "sn": "Jones",
  "ubidEmailJSON": {
    "type": "work",
    "value": "ljones@example.com"
  }
}'

The request body includes the following properties:

  • _dn

    Attribute description.

  • objectClass

    Attribute description.

  • uid

    Attribute description.

  • cn

    Attribute description.

  • sn

    Attribute description.

  • ubidEmailJSON

    Attribute description.

The response returns a 201 Created messsage. The data looks like this:

{
  "_dn": "uid=lindajones,ou=people,dc=example,dc=com",
  "objectClass": [ "top", "ubidPerson" ],
  "uid": "lindajones",
  "cn": "Linda Jones",
  "sn": "Jones",
  "ubidEmailJSON": {
    "type": "work",
    "value": "ljones@example.com"
  },
  "_links": {
    "self": {
      "href": "https://ds.example.com/directory/v1/uid=lindajones,ou=people,dc=example,dc=com"
    }
  }
}

Get an entry

The following sample shows the GET /directory/v1/{dn} operation to return the directory entry specified by its distinguished name.

curl -X "GET" "https://ds.example.com/directory/v1/uid=lindajones,ou=people,dc=example,dc=com" \
-H 'Content-type: application/json' \
-H 'Authorization: Bearer jwtToken'

The response data looks like this:

{
  "_dn": "uid=lindajones,ou=people,dc=example,dc=com",
  "objectClass": [ "top", "ubidPerson" ],
  "uid": "lindajones",
  "cn": "Linda Jones",
  "sn": "Jones",
  "ubidEmailJSON": {
    "type": "work",
    "value": "ljones@example.com"
  },
  "_links": {
    "self": {
      "href": "https://ds.example.com/directory/v1/uid=lindajones,ou=people,dc=example,dc=com"
    },
  }
}

In addition to retrieving the current resource by providing the distinguished name, you can get the record of the currently authenticated user by calling the alias GET /directory/v1/me.

curl -X "GET" "https://ds.example.com/directory/v1/me" \
-H 'Content-type: application/json' \
-H 'Authorization: Bearer jwtToken'

Search for entries

To retrieve a list of entries under a subtree of an entry, use the {dn}/subtree endpoint. This endpoint requires the searchScope parameter. The supported search scopes are:

  • baseObject

    Returns only the object directly described by the base DN.

  • singleLevel

    Returns only the entries that are immediate children of the base entry.

  • wholeSubtree

    Returns the base entry and all of its descendants.

  • subordinateSubtree

    Returns the descendants of the base entry, but not the base entry itself.

All list requests are paged. The limit parameter specifies the maximum page size. However, the page size can be smaller than requested, either because the server has a configured maximum page size or because you have reached the last page and there are not enough entries to fill the page.

The next link enables navigation forward from the current page to the next page. The directory service does not support a previous link to move backward from the current page.

The size value on the returned object shows the actual number of entries returned in the request.

The following sample shows the GET /directory/v1/{dn}/subtree operation to return the base entry and all of it’s descendants.

curl -X "GET" "https://ds.example.com/directory/v1/ou=people,dc=example,dc=com/subtree" \
-H 'Content-type: application/json' \
-H 'Authorization: Bearer jwtToken' \
-d $'{
  "searchScope": "wholeSubtree",
  "limit": "100"
}'

The response data looks like this:

{
  "size": 100,
  "_embedded": {
    "entries": [
      {
        "_dn": "uid=lindajones,ou=people,dc=example,dc=com",
        "objectClass": [ "top", "person", "organizationalPerson", "inetOrgPerson" ],
        "uid": ["lindajones"],
        "cn": ["Linda Jones"],
        "sn": ["Jones"],
        "_links": {
          "self": {
            "href": "https://ds.example.com/directory/v1/uid=lindajones,ou=people,dc=example,dc=com"
          }
        }
      },

      ... 99 more entries ...

    ]
  },
  "_links": {
    "self": {
      "href": "https://ds.example.com/directory/v1/ou=people,dc=example,dc=com/subtree?searchScope=wholeSubtree&limit=100"
    },
    "next": {
      "href": "https://ds.example.com/directory/v1/ou=people,dc=example,dc=com/subtree?limit=100&cursor=eyJjb29raWUiOiJ1c2VyMTAiLCJmaWx0ZXIiOiIiLCJzY29wZSI6Indob2xlU3VidHJlZSJ9"
    }
  }
}

Note: The searchScope parameter value is encoded in the cursor parameter in the next link URL. The search scope does not need to be provided on subsequent requests.

Include or exclude attributes

The GET /directory/v1/{dn}/subtree endpoint also supports fine-tuning the response data through the following request parameters:

  • includeAttribute

    Specifies the attribute or attributes in the entry that should be included in the response.

  • excludeAttribute

    Specifies the attribute or attributes in the entry that should be included in the response.

    The following sample shows the GET /directory/v1/{dn}/subtree operation to return the base entry and all of it’s descendants.

    curl -X "GET" "https://ds.example.com/directory/v1/ou=people,dc=example,dc=com/subtree" \
    -H 'Content-type: application/json' \
    -H 'Authorization: Bearer jwtToken' \
    -d $'{
      "searchScope": "wholeSubtree",
      "limit": "100"
    }'

    The response data looks like this:

    {
      "size": 100,
      "_embedded": {
        "entries": [
          {
            "_dn": "uid=lindajones,ou=people,dc=example,dc=com",
            "objectClass": [ "top", "person", "organizationalPerson", "inetOrgPerson" ],
            "uid": ["lindajones"],
            "cn": ["Linda Jones"],
            "sn": ["Jones"],
            "_links": {
              "self": {
                "href": "https://ds.example.com/directory/v1/uid=lindajones,ou=people,dc=example,dc=com"
              }
            }
          },
    
      ... <span class="hljs-number">99</span> more entries ...
    
    ]
    

    }, "_links": { “self”: { “href”: “https://ds.example.com/directory/v1/ou=people,dc=example,dc=com/subtree?searchScope=wholeSubtree&limit=100” }, “next”: { “href”: “https://ds.example.com/directory/v1/ou=people,dc=example,dc=com/subtree?limit=100&cursor=eyJjb29raWUiOiJ1c2VyMTAiLCJmaWx0ZXIiOiIiLCJzY29wZSI6Indob2xlU3VidHJlZSJ9” } } }

Search entries

The GET /directory/v1/{dn}/subtree endpoint supports a SCIM filtering parameter to limit the returned result set to those entries meeting the specified criteria. For example, the request below searches for all people with the last name of Jones.

curl -X "GET" "https://ds.example.com/directory/v1/ou=people,dc=example,dc=com/subtree" \
-H 'Content-type: application/json' \
-H 'Authorization: Bearer jwtToken' \
-d $'{
  "searchScope": "wholeSubtree",
  "filter": "objectClass%20eq%20%22"person%22%20and%20sn%20eq%20%22Jones%22"
}'

Filtering is done before pagination of the response data. The filter parameter is encoded into the cursor pagination parameter; it only needs to be provided in the first request.

The response data looks like this:

{
  "_links": {
    "self": {
      "href": "https://ds.example.com/directory/v1/ou=people,dc=example,dc=com/subtree?searchScope=wholeSubtree&ldapFilter=objectClass%20eq%20%22person%22%20and%20sn%20eq%20%22Jones%22"
    }
  },
  "size": 1,
  "_embedded": {
    "entries": [
      {
        "_dn": "uid=lindajones,ou=people,dc=example,dc=com",
        "objectClass": [ "top", "person", "organizationalPerson", "inetOrgPerson" ],
        "uid": ["lindajones"],
        "cn": ["Linda Jones"],
        "sn": ["Jones"],
        "_links": {
          "self": {
            "href": "https://ds.example.com/directory/v1/uid=lindajones,ou=people,dc=example,dc=com"
          }
        }
      }
    ]
  }
}

Update an entry

The PUT /directory/v1/{dn} endpoint modifies the entry specified by its distinguished name. Attribute values specified in the request body are modified; existing attributes omitted from the request maintain their current values.

Attributes designated for update are replaced entirely. You cannot partially modify an attribute. If you wish to edit a value within a JSON attribute, you must replace the entire attribute. Providing a null value for an entity within a JSON attribute sets the value of the entity to null.

curl -X "PUT" "https://ds.example.com/directory/v1/uid=lindajones,ou=people,dc=example,dc=com" \
-H 'Content-type: application/json' \
-H 'Authorization: Bearer jwtToken' \
-d $'{
  "cn": [ "Linda Jones", "Linda F. Jones" ],
  "ubidEmailJSON": null
}'

The response data looks like this:

{
  "_dn": "entryUUID=a4fbd3ca-f7a0-42f2-af66-13f2c0dd8cd1,ou=people,dc=example,dc=com",
  "objectClass": [ "top", "person", "organizationalPerson", "inetOrgPerson" ],
  "uid": ["lindajones"],
  "cn": [ "Linda Jones", "Linda F. Jones" ],
  "sn": ["Jones"],
  "_links": {
    "self": {
      "href": "https://ds.example.com/directory/v1/entryUUID=a4fbd3ca-f7a0-42f2-af66-13f2c0dd8cd1,ou=people,dc=example,dc=com"
    }
  }
}

Note: To update the DN of an entry, see “Rename or move an entry or subtree”.

Update an entry (LDAP modify)

The PATCH /directory/v1/{dn} endpoint functions just like an LDAP ldapmodify operation. The request body requires the modificationType property, which supports the following four types of modifications:

  • add

    Adds the attribute value (or values) specified in the values property.

  • remove

    Removes the attribute value (or values) specified in the values property.

  • set

    Replaces all the values in the specified attribute with the value (or values) specified in values property.

  • increment

    Changes the numeric value of the attribute by the amount specified in the values property. The request values must contain a single Number, and the attribute must be a single-value integer type.

Note: If the values property is null or an empty array, the update depend on the modification type: set removes the attribute entirely; remove and increment return an error; add is an LDAP No-Op that does not change the directory.

curl -X "PATCH" "https://ds.example.com/directory/v1/uid=lindajones,ou=people,dc=example,dc=com" \
-H 'Content-type: application/json' \
-H 'Authorization: Bearer jwtToken' \
-d $'{
  "modifications": [
    {
      "attributeName": "cn",
      "modificationType": "add",
      "values": [
        "Linda F. Jones"
      ]
    },
    {
      "attributeName": "cn",
      "modificationType": "remove",
      "values": [
        "Linda Jones"
      ]
    },
    {
      "attributeName": "manager",
      "modificationType": "set",
      "values": [
        "Boss",
        "Vice Boss"
      ]
    },
    {
      "attributeName": "salary",
      "modificationType": "increment",
      "values": 20000
    },
    {
      "attributeName": "ubidEmailJSON",
      "modificationType": "add",
      "values": [
        {"type": "home", "value": "user@example.com", "verified": true}
      ]
    }
  ]
}'

The response data looks like this:

{
  "_dn": "uid=lindajones,ou=people,dc=example,dc=com",
  "objectClass": [ "top", "person", "organizationalPerson", "inetOrgPerson" ],
  "uid": ["lindajones"],
  "cn": ["Linda F. Jones"],
  "sn": ["Jones"],
  "manager": [ "Boss", "Vice Boss" ],
  "salary": [120000],
  "ubidEmailJSON": [
    {
      "type": "home",
      "value": "user@example.com",
      "verified": true
    }
  ],
  "_links": {
    "self": {
      "href": "https://ds.example.com/directory/v1/uid=lindajones,ou=people,dc=example,dc=com"
    }
  }
}

Rename or move an entry

The POST /directory/v1/{dn} endpoint functions just like an LDAP moddn operation for moving or renaming one LDAP entry. The request body requires at least one of the following supported attributes:

  • newRDN

    Renames the entry.

  • newSuperiorDN

    Moves the entry to a different organizational unit (ou).

  • deleteOldRDN

    Determines whether the operation is a move or a copy operation. It specifies a move when the attribute value is set to true; it specifies a copy when the value is set to false.

The following sample shows a below copies Linda Jones to the ou=employees,dc=example,dc=com directory, leaving the original entry in ou=people,dc=example,dc=com. This operation uses the vendor-specific application/vnd.pingidentity.directory.rename+json custom content type in the request header.

curl -X "POST" "https://ds.example.com/directory/v1/uid=lindajones,ou=people,dc=example,dc=com" \
-H 'Content-type: application/vnd.pingidentity.directory.rename+json' \
-H 'Authorization: Bearer jwtToken' \
-d $'{
  "newSuperiorDN": "ou=employees,dc=example,dc=com",
  "deleteOldRDN": false
}'

The response data looks like this:

{
  "_dn": "entryUUID=a4fbd3ca-f7a0-42f2-af66-13f2c0dd8cd1,ou=people,dc=example,dc=com",
  "objectClass": [ "top", "person", "organizationalPerson", "inetOrgPerson" ],
  "uid": ["lindajones"],
  "cn": [ "Linda Jones", "Linda F. Jones" ],
  "sn": ["Jones"],
  "_links": {
    "self": {
      "href": "https://ds.example.com/directory/v1/entryUUID=a4fbd3ca-f7a0-42f2-af66-13f2c0dd8cd1,ou=people,dc=example,dc=com"
    }
  }
}

Delete an entry

The following sample shows the DELETE /directory/v1/{dn} operation to delete the entry specified by its distinguished name from the directory.

curl -X "DELETE" "https://ds.example.com/directory/v1/uid=lindajones,ou=people,dc=example,dc=com" \
-H 'Content-type: application/json' \
-H 'Authorization: Bearer jwtToken'

When successful, the response returns a 204 NO CONTENT message.

Note: Only one entry can be deleted in a delete operation. Subtree delete is not supported. Delete operations on an entry with children results in a 400 BAD REQUEST response.

Retrieve a subset of attributes

By default, the GET /directory/v1/{dn} and GET /directory/v1/{dn}/subtree operations return all user attributes and no operational attributes of the returned entry or entries. The following query parameters in the request body can change this behavior to exclude or include designated attributes. These parameters accept a comma-separated list of attributes to include or exclude from the response data:

  • includeAttributes

    Designates the attributes to include in the response data.

  • excludeAttributes

    Designates the attributes to exclude in the response data.

The includeAttributes and excludeAttributes query parameters can accept the following alias characters to simplify attribute filtering:

  • *

    Returns all user attributes of the entry.

  • +

    Returns all operational attributes of the entry.

Note: The service returns an error if the same attribute is provided in both the includeAttributes and excludeAttributes parameters of the request. When using the * and + parameter values, the excludeAttributes value takes precedence. For example, includeAttributes=*&excludeAttributes=sn returns all attributes except sn, but excludeAttributes=*&includeAttributes=sn returns no attributes (except dn).

The following sample shows the GET /directory/v1/{dn} operation with subset filtering to include all user attributes, but exclude the sn attribute from the response.

curl -X "GET" "https://ds.example.com/directory/v1/uid=lindajones,ou=people,dc=example,dc=com" \
-H 'Content-type: application/json' \
-H 'Authorization: Bearer jwtToken' \
-d $'{
  "searchScope": "baseObject",
  "includeAttributes": "*",
  "excludeAttributes": "sn"
}'

The response data looks like this:

{
  "_dn": "uid=lindajones,ou=people,dc=example,dc=com",
  "uid": ["lindajones"],
  "cn": [ "Linda Jones", "Linda F. Jones" ],
  "objectClass": [ "top", "person", "organizationalPerson", "inetOrgPerson" ],
  "createTimestamp": "2018-05-01T15:04:49.866Z",
  "modifyTimestamp": "2018-05-01T15:04:51.591Z",
  "_links": {
    "self": {
      "href": "https://ds.example.com/directory/v1/entryUUID=a4fbd3ca-f7a0-42f2-af66-13f2c0dd8cd1,ou=people,dc=example,dc=com"
    }
  }
}

Get all schemas

The GET /directory/v1/schemas endpoint returns a list of all configured objectClass entities on the server. If no objectClass entities are configured on the directory server, the request returns an empty list.

The following sample gets all configured schemas and their associated objectClass entities.

curl -X "GET" "https://ds.example.com/directory/v1/schemas" \
-H 'Content-type: application/json' \
-H 'Authorization: Bearer jwtToken' \

The response data looks like this if at least one schema object is defined:

{
  "schemas": [
    {
      "$schema": "http://json-schema.org/draft-07/schema#23",
      "title": "top",
      "required": ["objectClass"],
      "classType": "abstract",
      "type": "object",
      "properties": {
        "objectClass": {
          "type": array,
          "items": {"type": "string"},
          "contains": {"const": "top"},
          "description": "Since 'top' is an abstract objectClass, new objects cannot be created with only 'top' as the objectClass value. A structural objectClass that derives from 'top' must be included"
        }
      },
      "additionalProperties": false,
      "_links": {
        "self": {
          "href": "https://localhost:5034/directory/v1/schemas/top"
        }
      }
    },
    {
      "$schema": "http://json-schema.org/draft-07/schema#23",
      "title": "person",
      ...
    },
    ...
  ],
  "_links": {
    "self: {
      "href": "https://localhost:5034/directory/v1/schemas"
    }
  }
}

The response data looks like this if there are no schemas configured on the directory server.

{
  "schemas": [],
  "_links": {
    "self: {
      "href": "https://localhost:5034/directory/v1/schemas"
    }
  }
}

Get the schema for a specified objectClass

The GET /directory/v1/schemas/{objectclass} endpoint returns the schema associated with the objectClass specified in the request URL.

curl -X "GET" "https://ds.example.com/directory/v1/schemas/{objectclass}" \
-H 'Content-type: application/json' \
-H 'Authorization: Bearer jwtToken' \

The response data looks like this:

{
  "$schema": "http://json-schema.org/draft-07/schema#23",
  "title": "employee",
  "classType": "structural",
  "parent": "user",
  "required": [
    "objectClass",
    "employeeNumber",
    "salary",
    "userName"
  ],
  "type": "object",
  "properties": {
    "objectClass": {
      "oneOf": [
        {
          "const": "employee"
        },
        {
          "type": "array",
          "items": {
            "type": "string"
          },
          "contains": {
            "const": "employee"
          }
        }
      ],
      "description": "New objects of this type must include the 'employee' objectClass value. Returned entries will include the complete objectClass hierarchy: [top, user, employee]"
    },
    "employeeNumber": {
      "type": "string",
      "description": "numerically identifies an employee within an organization"
    },
    "fullName": {
      "oneOf": [
        {
          "type": "string"
        },
        {
          "type": "array",
          "items": {
            "type": "string"
          }
        }
      ]
    },
    "givenName": {
      "oneOf": [
        {
          "type": "string"
        },
        {
          "type": "array",
          "items": {
            "type": "string"
          }
        }
      ]
    },
    "isManager": {
      "type": "boolean"
    },
    "salary": {
      "type": "integer"
    },
    "startDate": {
      "type": "string",
      "format": "dateTime"
    },
    "userName": {
      "type": "string"
    }
  },
  "additionalProperties": false,
  "_links": {
    "self": {
      "href": "https://localhost:5034/directory/v1/schemas/employee"
    },
    "parent": {
      "href": "https://localhost:5034/directory/v1/schemas/user"
    }
  }
}