Skip to main content

LDAP definition

Every field of an LDAP connector's definition.json, what Owlie adds to each install's configuration, and what it derives from the definition. The machine-readable form is ldap.schema.json, a JSON Schema (draft-07). The schema checks shapes only. Owlie also applies the rules on this page when it derives the declaration, such as a lookup key's form, a single-bit disabledFlag, password only with write, and valid attribute names, so a definition the schema accepts can still be refused.

In the tables, Editable marks a field that accepts an install-editable value: instead of a literal, the bare expression { "$oxl": "config.<name>" } (an object whose only key is $oxl), where <name> is a field declared under config.properties with the same JSON type. Each install then reads its own copy of that field, seeded from the field's default. Any other expression, and an expression in a field not marked Editable, is refused.

Top level​

FieldTypeRequiredDefaultEditableWhat it does
engineintegerYesNoAlways 1.
descriptionstringNoNoThe catalog description.
iconstringNoNoThe catalog icon: @ plus a domain, such as @openldap.org.
configobjectNoNoHolds properties.
config.propertiesobjectNoNoThe install fields the definition reads, keyed by name. Each is a JSON Schema object: type, title, description, default (the seed), and for lists items, for objects additionalProperties. A field without a default, or with any seed other than "" or null, is required at install. The names url, baseDn and tls are refused.
pageSizeintegerNo500YesEntries per search page, 1 to 1000.
entitiesobjectYesNoThe entities: user, optional group, and lookup entities under any other key.
incrementalobjectNoNoIncremental sync of the account entity.
custom_opsobjectNoNoCustom operations, keyed by operation key: engine-run LDAP writes or PowerShell scripts.
handlersobjectNoNoPowerShell scripts that replace engine handlers, keyed by handler key.

No other top-level field is accepted.

Entity fields​

entities.user is required and is the account. entities.group is optional. Any other key under entities is a lookup entity: a lowercase letter followed by lowercase letters, digits and _, not connection, grant, grants or notification, read and never written. All three share these fields:

FieldTypeRequiredDefaultEditableWhat it does
namestringNoAccount, Group, or the lookup keyNoWhat Owlie calls the records.
baseslist of stringsNoThe install's base DNYesWhole DNs to search under, in order. An install's empty list stops the connector for user and group, and means the whole base DN for a lookup entity. Only bases may reference an install field that has no seed.
scopestringNosubNobase, one or sub.
objectClasseslist of stringsNo[]YesObject class names or OIDs. Each becomes an (objectClass=...) condition; a new entry gets exactly these classes. Required for a create.
filterstringNoYesA parenthesized LDAP filter, ANDed with the object classes. With neither, the entity matches (objectClass=*).
identifierobjectYesNoThe entry's immutable unique id.
identifier.attributestringYesNoAn attribute name, such as entryUUID or objectGUID.
identifier.typestringNostringNostring, binary (read as base64) or guid (read as the GUID's standard text form).
rdnAttributestringYesNoThe attribute that names an entry in its DN, such as uid or cn. Always read.
attributesobjectNo{}YesThe attributes to read, keyed by attribute name (options such as cn;lang-en allowed). dn and account_disabled are refused.
writeobjectNoNouser and group only. Enables create and update.

Attribute fields​

Each entry of attributes:

FieldTypeRequiredDefaultAllowed valuesWhat it does
typestringNostringstring, number, boolean, binary, guid, sidHow the value is read. boolean reads TRUE; binary reads as base64; sid reads as S-1-5-....
multibooleanNofalseRead every value as a list.
titlestringNoThe label Owlie shows.
sensitivebooleanNofalseMark the value as secret or personal data.
readOnlybooleanNofalseNever written by provisioning.
baselinestring or objectNoThe value a new account gets: an OXL expression over the identity, such as "identity.email", or { "usernameGenerator": { "steps": [...], "maxAttempts": 20 } }. An expression may also read generated, the generator's value. Refused on a readOnly attribute. Used only on user attributes.
usernameGeneratorobjectYes, in the object formInside baseline: steps, a non-empty list whose entries each have a non-empty blocks list, and maxAttempts, a positive integer. At most one attribute may have one. See Username generators.

Username generators​

A usernameGenerator produces candidate values for one account attribute. Owlie uses the first candidate when it creates an account, and the next one each time the directory answers that a value is taken, up to maxAttempts candidates in all. A retry changes only that attribute and any attribute whose baseline expression reads generated. Only one attribute may have a generator.

FieldWhat it is
stepsThe candidate patterns, in order. Each step has blocks, whose values are joined. A step without a counter yields one candidate. The last step, when it has a counter, repeats with the counter going up until maxAttempts is reached.
maxAttemptsThe most candidates to try, a positive integer.

A block's type is one of:

typeFieldsValue
fieldfield, modifiersThe identity's value for field, changed by each modifier in order.
literalvalueThe text in value.
incrementstartFrom (default 1), padding (default 1)A counter: startFrom plus the attempt within the step, padded with leading zeros to padding digits.
alphabetIncrementA letter: a on the step's first attempt, then b, and back to a after z.

field is one of first_name, last_name, middle_name, nickname, employee_id, email, display_name, department, title and username. Each reads the identity's field of that name; a field the identity does not have reads as empty. A step whose field blocks all read empty yields no candidate, whatever literals or counter it has.

A modifier's type is one of:

typeFieldsWhat it does
firstNn (default 1)Keeps the first n characters.
lastNn (default 1)Keeps the last n characters.
lowercase, uppercaseChanges the case.
capitalizeUpper-cases the first character and lower-cases the rest.
asciiRemoves accents, then every character that is not a letter or digit: Núñez-O'Brien becomes NunezOBrien.
splitAtdelimiter, keep (before, the default, or after), includeDelimiter (default false)Keeps the text before or after the first delimiter. A value without the delimiter is unchanged.

Put ascii before firstN or lastN, so accented letters are folded before characters are counted. Active Directory's sAMAccountName holds at most 20 characters, so shorten long names with firstN.

write​

The fields of an entity's write object:

FieldTypeRequiredDefaultEditableWhat it does
basestringNoThe entity's first search baseYesThe DN new entries are created under.
dnstringNoYesA DN template with {attribute} placeholders, each replaced by the new entry's escaped value. Empty means no template.
deletebooleanNofalseNoAlso enables delete.

Account fields​

entities.user also takes lifecycle, password and matching.

lifecycle​

One of two forms. The form is fixed. With either, every account record carries a read-only boolean account_disabled, true when the lifecycle attribute marks the account disabled, and Sync reads it (see What Owlie derives). The lifecycle attribute is read on every account read, whether or not attributes lists it.

A value lifecycle:

FieldTypeRequiredEditableWhat it does
attributestringYesYesThe attribute that records the state.
disabledValuestringYesYesWritten on disable; an account holding it reads as disabled.
enabledValuestringNoYesWritten on enable and create. Absent or empty: enable removes the attribute. Must differ from disabledValue.

A flag lifecycle:

FieldTypeRequiredEditableWhat it does
attributestringYesNoAn integer attribute, such as userAccountControl.
disabledFlagintegerYesNoA single bit, such as 2: set on disable, cleared on enable.
newAccountValueintegerYesNo0 or more. A new account is created with this value plus the disabled bit, given its password, then enabled unless the create input has enabled: false.

password​

Requires write on the account.

FieldTypeRequiredDefaultAllowed valuesWhat it does
attributestringYesThe attribute the password is written to, on create and update, only when the request carries a password. Never read.
encodingstringNotexttext, unicodePwdunicodePwd writes Active Directory's format: the quoted password in UTF-16LE.
on_changeobjectNoAttribute names to stringsAttributes every password change writes in the same directory modify, right after the password, such as "pwdLastSet": "-1" to clear Active Directory's "must change password at next logon". Each must be one of the account's writable attributes, spelled exactly, and not its RDN attribute.

matching​

Adds a rule that matches accounts to identities by employee ID, ahead of the email rule.

FieldTypeRequiredEditableWhat it does
employeeIdstringNoYesThe attribute holding each account's employee ID. Empty, or an install-editable value left empty, adds no employee-ID rule.

employeeId must be a plain attribute name (letters, digits and _, not starting with a digit), and must be an attribute the account reads: one listed in attributes, or the identifier. Otherwise Owlie refuses it, naming the install field it came from when the value is install-editable. When an install's own attribute list drops it, the attribute is still read on every account read, as the lifecycle attribute is. An account that matches a terminated identity by this rule relinks to it and reactivates it, so a rehire does not become a new identity. The email rule on mail is always added after it.

Group fields​

entities.group also takes membership, which is required on a group. The fields of membership:

FieldTypeRequiredDefaultEditableWhat it does
attributestringYesNoThe attribute that holds membership, such as member, uniqueMember or memberOf.
directionstringNomember-on-groupNomember-on-group (the group lists member DNs) or memberOf-on-account (the account lists group DNs).
writablebooleanNofalseNoEnables adding and removing members. Only with member-on-group.
nestedbooleanNofalseYesGroups can be members of groups: Sync reports each group's direct members, accounts and groups, found by memberOf. Owlie resolves membership through the nested groups. Needs a directory that keeps memberOf on accounts and groups.

incremental​

FieldTypeRequiredDefaultAllowed valuesWhat it does
attributestringNomodifyTimestampThe attribute that changes when an entry changes.
kindstringNotimestamptimestamp, counterA GeneralizedTime, or an integer that only grows.
overlapSecondsintegerNo3000 to 86400For timestamp: how far before the last marker the next run reads.
sourcestringNoinvocationIdFor counter only: ties the marker to the domain controller that issued it; a marker from another controller restarts from zero.

None of these fields is install-editable.

custom_ops​

Keyed by operation key: a letter, then letters, digits, _ and -. Each entry is either an engine-run LDAP write or a script binding. The operation's handler is <account entity>.op.<key>: custom operations run on the account entity only.

An engine-run LDAP write:

FieldTypeRequiredWhat it does
ldapobjectYesExactly one of replace and move, below.
replaceobjectOne of the twoAttribute name to value, set on the entry the operation runs on. null removes the attribute.
moveobjectOne of the twoMoves the entry, keeping its RDN, under a container DN.
paramstringYes, with moveThe parameter that carries the container DN. Without a params_schema, one requiring it is derived.
entitystringNouser (the default) or group.
title, descriptionstringNoWhat admins and workflows see.
params_schemaobjectNoJSON Schema the parameters are checked against before the write.

A script binding:

FieldTypeRequiredWhat it does
scriptstringYesThe name of scripts/<name>.ps1: a letter or digit, then letters, digits, _ and -, up to 64 characters. The connector must contain it.
entitystringNoThe entity the operation runs on: user (the default) or group.
title, descriptionstringNoWhat admins and workflows see.
params_schemaobjectNoJSON Schema the parameters are checked against before the script runs.
configlist of stringsNoInstall field names passed to the script as $Params.config.
secretsobjectNoParameter name to gateway secret name (letters, digits, _, . and -). The gateway resolves the secret itself. identifiers and config cannot be bound, and params_schema cannot declare a property with either name: Owlie fills both itself.
timeoutMsintegerNo1 to 900000. How long the script may run. The default is 60000. The gateway caps it at its powershellTimeoutMs for the connector and its customConnectorLimits.timeoutMs, both 60000 unless the operator raises them.

handlers​

Keyed by a handler key the definition enables, such as user.disable. Each entry replaces the engine's handler with a script and takes script, config, secrets and timeoutMs, as for a script binding above. A key the definition does not enable, or a custom operation key, is refused, and so is a read-only key (connection.discover): there is no PowerShell broker in that context, so a script bound there would fail every time it ran.

What the script reads as $Params depends on the key. fetchOne, create, fetchAll, fetchAllSince, delete, enable, disable, a Grant kind's add and remove, and connection.testConnection/connection.validateConfig take one value: the script reads it as $Params.payload, beside $Params.identifiers for the entry the operation runs on: null when the operation has none, such as create, fetchAll, fetchAllSince, a Grant write, or either connection operation, which has no entry at all. update takes the entry and the change, so the script reads $Params.identifiers and $Params.input. fetchRelationships takes the external id of the entry whose relationships are read (the group, for a membership grant) and the page request, so the script reads $Params.externalId and $Params.options, with no $Params.identifiers.

A script replacing fetchOne, fetchAll, fetchAllSince or fetchRelationships stands in for a read, so its output is checked before Owlie uses it: a page must be { records, nextCursor, hasMore }, with nextCursor set whenever hasMore is true, and fetchOne must be { record } (the entry, or null when there is none). A script that returns something else fails the operation with a message that says so. With a lifecycle, a script replacing user.fetchOne, user.fetchAll or user.fetchAllSince must give each account record account_disabled (true for a disabled account): Sync reads an account's state from it, and a record without it reads as active.

A handlers entry's secrets cannot name a $Params field the key itself fills: config, always, plus payload and identifiers for a one-argument key, identifiers and input for update, or externalId and options for fetchRelationships.

What the engine adds to each install​

Every LDAP install has these fields besides the ones in config.properties:

FieldTypeRequiredWhat it is
urlstringYesThe directory URL, ldaps:// (TLS) or ldap:// (upgraded with StartTLS).
baseDnstringYesThe install's base DN.
tlsobjectNocaCert, a CA certificate in PEM, and rejectUnauthorized, whether to verify the directory's certificate (default true). A caCert, or rejectUnauthorized: false, needs the gateway operator's allowTlsOverrides.

The one authentication option, bind ("Bind DN and password"), has bindDn and bindPassword, both required. bindPassword is a gateway secret: an install gives the secret's name, which the gateway resolves on your network. A draft test in the Connector Builder takes the password itself instead.

What Owlie derives​

From the definition, Owlie derives the connector's declaration:

PartDerived value
kindldap
provides["sync"], plus provision when the definition has any write: write on an entity, a lifecycle, a writable membership or an engine-run LDAP custom operation
protocols["ldap"], plus powershell when a script is bound
execution_targets["gateway"]
Entitiesuser, group and each lookup entity. Each record has dn and the identifier, and identifier_paths is ["dn", "<identifier>"]; the identifier is marked as the provider-stable id. Attributes become schema properties, read-only unless the entity has write and the attribute is not readOnly. A declared password adds a write-only password property.
Accountsync_support.supports_incremental follows incremental, and supports_deleted is false. The sync profile matches the account's mail to the identity's email; with matching.employeeId, it first matches that attribute to the identity's employee ID. With a lifecycle, the schema has a read-only boolean account_disabled and is_active is account_disabled != true, so Sync and user.fetchOne read the same state; without one, is_active is true. With writes, a provisioning profile whose new-account baseline is each attribute's baseline. With write on the account, revoke disables the account, or deletes it when it has write.delete and no lifecycle. With other writes but no write on the account, create_semantics is external: the account is Grants-only, so provisioning never creates, updates, enables, disables or deletes it, even with a lifecycle, and a revoke removes the memberships Owlie manages and leaves the account alone.
Grant kindsWith a group: member_of, source user, target group, discovered through group per entity, a membership, writable when membership.writable is true. When membership.nested is true or install-editable, principal_types is ["user", "group"] and via_membership is transitive, whatever an install sets.
Custom operationsOne per custom_ops entry, with its title, description and params_schema.

The handlers the engine runs:

HandlersWhen
connection.validateConfig, connection.testConnection, connection.discoverAlways
user.fetchOne, user.fetchAllAlways
user.fetchAllSinceWith incremental
<entity>.create, <entity>.updateWith write on user or group
<entity>.deleteWith write.delete
user.enable, user.disableWith lifecycle
group.fetchOne, group.fetchAll, group.fetchRelationshipsWith a group
grant.member_of.add, grant.member_of.removeWith membership.writable
<key>.fetchOne, <key>.fetchAllFor each lookup entity
<entity>.op.<key>For each custom operation

connection.testConnection reads the first account search base. connection.discover reads the root DSE and the schema, and never writes. What the membership writes treat as already present or already absent is in the Grant write contract; the handler context is in the Handler API.