Skip to main content

Map fields and install-editable values

Building a connector, step 7 (SCIM track, 3 of 3). Previous: Describe the provider's deviations.

Owlie keeps each user and group as a flat record, such as { "givenName": "Maya" }, while SCIM nests attributes (name.givenName) and keeps some in lists (emails). The field lists translate between the two: users.fields and groups.fields name each field Owlie shows and bind it to a SCIM path. The record schemas of the connector's entities, which Sync, provisioning, attribute mappings and policies all read, are derived from these lists.

The field lists​

users.fields and groups.fields are objects keyed by field name. A field name is letters, digits and _, starting with a letter or _. Each field binds one SCIM path:

{
"users": {
"fields": {
"userName": {
"path": "userName",
"title": "Username",
"identifier": true,
"required": true,
"baseline": "identity.email"
},
"givenName": { "path": "name.givenName", "title": "Given name", "baseline": "identity.first_name" },
"email": {
"path": "emails[type eq \"work\"].value",
"title": "Work email",
"baseline": "identity.email",
"elementExtras": { "primary": true }
},
"active": { "path": "active", "title": "Active", "type": "boolean", "readOnly": true },
"department": {
"path": "urn:ietf:params:scim:schemas:extension:enterprise:2.0:User:department",
"title": "Department"
}
}
}
}
PropertyMeaning
pathRequired. The SCIM path the field reads and writes (see Paths).
typestring (the default), boolean, number or datetime. A datetime is a string in ISO 8601 date-time format.
titleThe label Owlie shows for the field.
identifierThe field identifies a record, and Owlie may look a record up by it.
requiredThe field is required in the record. A required user field needs a baseline unless it is readOnly.
sensitiveThe value is a secret or personal data to mask in logs. An identifier cannot be sensitive.
readOnlyThe server sets the value. Owlie reads it and never writes it.
writeOnlyOwlie writes the value but never reads it back, such as a password. An identifier cannot be write-only.
createOnlyThe value can be set when the account is created, and not changed afterwards.
referenceThe value points at another user, such as a manager. { "attribute": "externalId" } names the user attribute the value holds.
elementExtrasConstants added to the list element Owlie creates for a path with a filter, such as { "primary": true }.
baselineFor a user field: an OXL expression over the identity that gives a new account its value, such as identity.email.

Every record also has an id field: the read-only, server-assigned id, read from users.idPath or groups.idPath. It is always the record's first identifier, and you do not declare it. A field named id is refused.

Paths​

A path is one of:

  • A dotted attribute path: userName, name.givenName, meta.lastModified.
  • An extension attribute, prefixed with its schema URN: urn:ietf:params:scim:schemas:extension:enterprise:2.0:User:department, or urn:ietf:params:scim:schemas:extension:enterprise:2.0:User:manager.value.
  • A path with a filter that selects one element of a list: emails[type eq "work"].value. A path has at most one filter, the only operator is eq, and the value is a double-quoted string.
  • A list path without a filter, for vendors that do not use filtered paths: emails.value. Owlie reads the element marked primary, or else the first one.

When Owlie creates a record, a path with a filter becomes a one-element list whose element carries the filtered value and any elementExtras: emails[type eq "work"].value with { "primary": true } writes "emails": [{ "type": "work", "value": "...", "primary": true }]. A list path without a filter on a standard multi-valued attribute (emails, phoneNumbers, ims, photos, addresses, entitlements, roles, groups, members, x509Certificates) also writes a one-element list.

The default lists​

A definition without users.fields gets these user fields, and one without groups.fields gets one group field, displayName, as an identifier. The starter definition writes both lists out.

FieldPathNotes
userNameuserNameIdentifier, required, baseline identity.email
externalIdexternalIdIdentifier
givenNamename.givenNameBaseline identity.first_name
familyNamename.familyNameBaseline identity.last_name
displayNamedisplayNameBaseline identity.display_name
emailemails[type eq "work"].valueBaseline identity.email
activeactiveboolean, read-only
titletitle
departmententerprise extension department
employeeNumberenterprise extension employeeNumber
managerenterprise extension manager.valueReference to a user's externalId

Replace the defaults whole​

A field list you write replaces the default list entirely; Owlie does not merge the two. Start from the default rows (the builder shows them), delete the fields the vendor does not store, and add the ones it does. Owlie refuses a list that breaks these rules:

  • The user list keeps an identifier field whose path is userName, and the group list, when groups are enabled, keeps one whose path is displayName. SCIM requires both, and Owlie looks records up by them.
  • Every identifier field's path is filterable (see Filtering).
  • A required user field that is not read-only has a baseline, or new accounts could not be provisioned.
  • With groups.membership.memberValue set to userName or externalId, the user list has a readable field of that name.

Two field names carry meaning beyond the schema. Sync reads each account's state from the record's active field (an account is active unless active is false), so keep a field named active bound to the attribute that carries the account's state. Sync does not use users.activeAttribute: only a lookup of one user compares the attribute with its disabledValue (Users). And the account's default matching rule compares an identity's email with the record's email field, or its userName when there is no email. Admins can change the matching rule on the integration; keeping these names makes the default work.

What a new account gets​

A user field's baseline is an OXL expression over the identity, evaluated when Owlie provisions a new account. It reads the identity's attributes, such as identity.email, identity.first_name, identity.last_name, identity.display_name or identity.id. Owlie's GitHub connector, for example, provisions externalId from identity.id. Never hard-code a domain or a tenant name: build every value from the identity. Every identity field an expression can read is in Expressions.

The baselines become the starting provisioning profile of each integration, next to Owlie's defaults for the other operations: a revoked account is disabled and its managed Grants are removed. Admins can change the profile after installing. Read-only fields never get a baseline.

Let installs edit values​

Customers of the same vendor store different attributes: one tenant has a cost centre in an extension, another does not. Three values of a SCIM definition can be install-editable, so each install adapts the connector without a new version: users.fields, groups.fields and filtering.attributes. Every other value, apart from the baseUrl expression, must be written literally in the definition.

To make one install-editable, replace the value with { "$oxl": "config.<name>" } and declare that config field with the list as its default, which is the seed every install starts from:

{
"config": {
"properties": {
"userFields": {
"type": "object",
"title": "User fields",
"description": "The user attributes this install syncs and provisions, keyed by field name, each bound to a SCIM path.",
"default": {
"userName": {
"path": "userName",
"title": "Username",
"identifier": true,
"required": true,
"baseline": "identity.email"
},
"externalId": { "path": "externalId", "title": "External ID", "identifier": true },
"email": { "path": "emails[type eq \"work\"].value", "title": "Work email", "baseline": "identity.email" },
"active": { "path": "active", "title": "Active", "type": "boolean", "readOnly": true }
}
},
"filterAttributes": {
"type": "array",
"items": { "type": "string" },
"title": "Filterable attributes",
"default": ["userName", "externalId", "displayName"]
}
}
},
"filtering": { "attributes": { "$oxl": "config.filterAttributes" } },
"users": { "fields": { "$oxl": "config.userFields" } }
}

The expression must be exactly config.<name>, and the config field must be declared with the matching type: object for a field list, array of strings for filtering.attributes.

Identifiers stay as the author seeded them. An install can add, change and remove ordinary fields, but not identifiers. If its list leaves out an identifier field from the seed, Owlie puts it back, in the seed's order. If its list changes an identifier's path, turns it into an ordinary field, or marks a new field as an identifier, Owlie refuses the change. Identifiers decide which record is which, and an install that changed them would lose track of its accounts.

Each install owns its copy. Installing writes a copy of every seed into the integration's configuration, and the integration's admins edit that copy. When a later version of your connector changes a seed, the integration keeps its own value: upgrading never overwrites it. Instead, the integration's page shows a notice, Owlie updated followed by the connector's name, listing each changed value with Your value, Owlie's previous and Owlie's new. An admin chooses Use Owlie's value or Keep mine for each one.

In the Connector Builder​

On the Install values tab, What installs may edit has a switch for User fields, Group fields and Filterable attributes. Turning one on moves the current value into a config property (userFields, groupFields or filterAttributes) as its default, and points the definition at it. A field list you never wrote moves the default list. A definition without filtering.attributes has no value to move, so the new filterAttributes property has no default: give it one in Raw JSON, the paths the vendor filters on, or the definition stops deriving. The Users and Groups tabs still show the rows, which are now the seed installs start from.

What the engine does with the fields​

  • Users are always fully writable. Every SCIM connector can create, update, enable, disable and delete accounts. An update writes the fields it is given, skipping read-only ones.
  • Group writes follow groups.manage. Creating, updating and deleting groups is available only where you allowed it.
  • Membership writes are one member at a time. Each Grant add or remove changes one user's membership of one group.
  • Lookups use identifiers only. When Owlie has a record's id, it reads the record directly. When it has only another identifier, it filters on that identifier's path; a filter on any other field could match someone else.
  • Values are escaped. Every value in a filter is sent as a quoted, escaped string, so a value containing quotes cannot change the filter. Every id placed in a URL path is URL-encoded.
  • The connection test lists one user, and one group when groups are enabled.

Next: Test your connector