objectClasses:
User:
# SCIM-specific mapping for the whole object class
scim:
extensions:
enterprise: "urn:ietf:params:scim:schemas:extension:enterprise:2.0:User"
attributes:
givenName:
scim:
path: name.givenName
primaryEmail:
scim:
path: emails[primary eq true].value
employeeNumber:
scim:
path: urn:ietf:params:scim:schemas:extension:enterprise:2.0:User:employeeNumber
Office:
attributes:
name:
connId:
name: NAME
Declarative YAML
The SCIMREST framework supports a declarative YAML form for schema definitions, operation handlers, and authentication, in addition to (and instead of) Groovy scripts.
YAML documents and Groovy scripts are two front-ends over the same builders — a YAML document drives the same live builders the Groovy DSL does, so both forms can be mixed per connector and a connector can migrate a script to YAML without changing the manifest.
This document is part of the SCIMREST connector tutorial. See link for other topics.
Groovy to YAML fallback
The loader resolves each manifest script resource with a YAML fallback: when the referenced .groovy file is missing from the bundle, a .yaml / .yml file with the same base name is loaded instead.
Parsing is strict: a file must contain exactly one document, and unknown keys fail fast.
Schema documents
A YAML schema document describes object classes under the objectClasses root (one entry per object class; a file may describe several classes). Documents naming the same object class merge into one definition, so a native definition and a ConnId overlay can stay in separate files.
scim.extensions in the example is shown for the target shape — that key is not bindable in YAML yet (it fails fast), the rest of the example is fully supported.
|
Keys:
| Key | Description |
|---|---|
|
One object class (the counterpart of |
|
The corresponding object-class flags |
|
Built-in attribute aliases, e.g. |
|
|
|
One attribute — see the attribute keys below |
|
A reference attribute (memberships, foreign keys): |
|
An association between object classes: |
Attribute keys (all optional — only present keys are applied, so the defaults stay untouched):
| Key | Description |
|---|---|
|
The regular attribute settings (see user schema) |
|
JSON wire type, OpenAPI format, and the embedded object class for structured attributes |
|
The JSON wire mapping block; |
|
The ConnId-side name (including the |
|
The SCIM wire name, SCIM type, and SCIM attribute path (a path string such as |
`scim.implementation: { deserialize: |
, serialize: |
}` |
Groovy blocks for custom value mapping |
A bare attr: (no keys) declares the attribute with defaults — the counterpart of attribute("x") with an empty closure.
Attribute paths
The path key of the json: and scim: blocks maps the attribute to a (nested) wire location. A scalar uses the block’s default format: json.path is a basic JSONPath expression ($.name.givenName, $.emails[?(@.primary == true)].value) and scim.path is a SCIM attribute path (name.givenName, emails[primary eq true].value, or a full extension URI). An explicit format is supported via a { type, value } mapping (the type is case-insensitive):
attributes:
primaryEmail:
json:
path:
type: JSON_POINTER # JSON_PATH | JSON_POINTER | SCIM
value: /emails/0/value
The counterpart of the Groovy json { path { type JSON_POINTER; value "/emails/0/value" } } / scim { path "…" } DSL. The expression is parsed lazily, so an invalid expression fails at schema build time with the file location.
The YAML schema is fully literal: the builder deliberately gets no runtime context, so context-dependent Groovy fails fast at load time.
Operation documents
An operation document covers one or more of search, create, update, or delete per object class, under the same objectClasses root; a top-level authentication block may be added in the same file. One file may carry several object classes.
Search
objectClasses:
User:
search:
endpoints:
- path: /users/search
responseFormat: JSON_OBJECT
emptyFilterSupported: true
objectExtractor: |
response.body().get("users")
pagingSupport: |
request.queryParameter("size", paging.pageSize)
.queryParameter("offset", paging.pageOffset)
supportedFilters:
- spec: attribute("login").eq().anySingleValue()
request: |
request.queryParameter("login", value)
- path: /users/{id}
singleResult: true
supportedFilters:
- spec: attribute("id").eq().anySingleValue()
request: |
request.pathParameter("id", value)
attributeResolvers:
- attribute: team
resolutionType: PER_OBJECT
implementation: |
# Resolve the team attribute for each result object
custom:
emptyFilterSupported: true
supportedFilters:
- spec: attribute("login").eq().anySingleValue()
implementation: |
# Custom search logic — see the custom search guide
| Key | Description |
|---|---|
|
The search / list endpoint path (required; search endpoints use GET) |
|
Whether the response is a JSON array or a JSON object: |
|
Groovy block extracting the list of objects from the response (variable |
|
Groovy block adding paging parameters (variables |
|
The endpoint returns exactly one object |
|
The endpoint supports the empty (list-all) filter |
|
A filter specification as a build-time Groovy expression, e.g. |
|
Groovy block mapping the filter onto the request (variables |
|
UID/NAME rewriting for reference attributes: |
|
One resolver per entry: |
|
Custom search: |
Create / update / delete
objectClasses:
User:
create:
endpoints:
- method: POST
path: users
request:
contentType: APPLICATION_JSON
update:
endpoints:
- method: PATCH
path: /users/{id}
request:
contentType: APPLICATION_JSON
supportedAttributes:
- firstName
- lastName
- email
# Dedicated endpoint — no body required
# (the transition filter is accepted but not enforced yet)
- method: POST
path: /users/{id}/lock
request:
body: EMPTY
supportedAttributes:
- name: status
transition:
from: active
to: locked
delete:
endpoints:
- method: DELETE
path: /users/{id}
| Key | Description |
|---|---|
|
Per-operation switch, e.g. |
|
HTTP method ( |
|
The endpoint path (required); |
|
The request content type |
|
Groovy block producing the request body, or |
|
Plain attribute names, a fixed value ( |
Authentication
An authentication block (top-level, not per object class) configures the authentication methods of the rest and/or scim namespace:
authentication:
rest:
bearer:
implementation: |
request.header("Authorization", "token " + decrypt(configuration.restTokenValue))
apiKey:
implementation: |
request.header("Authorization", "token " + decrypt(configuration.restTokenValue))
preference:
- bearer
- apiKey
| Key | Description |
|---|---|
|
The per-namespace channel blocks |
|
Each accepts an |
|
The OAuth 2.0 flavors; each accepts the hooks |
|
Ordered list of method names ( |
See authentication & authorization for the properties each method consumes.