Model entities
Building a connector, step 3 of 10. Previous: Discover the target system.
Entities are the objects in the system that Owlie tracks. Exactly one of them is the account, the object Owlie links to an identity and provisions. The others are objects that access is granted on (groups, roles, workspaces), objects that hold access themselves, and objects that other records point to. This page covers choosing them, identifying them and describing their records. The next page maps access between them.
Choose the entities
An entity is an object with a stable identity that Owlie needs to know about:
- the account;
- objects that are the target of access: a group, a role, a workspace, a project;
- objects that hold access themselves: a group that is a member of another group;
- lookup objects that records refer to: an organizational unit, a department.
Do not make an entity out of an assignment. A role that exists as an object in the system is an
entity. "Role X in project Y" is a Grant with a qualifier, not a third entity. Reuse one entity
wherever the same object appears: a group that is both a membership target and a holder of folder
access is one group entity, not two.
Entity keys are singular and lowercase: user, group, role, org_unit. The keys grant,
grants, connection and notification are reserved. Once a connector version is published, its
entity keys are part of what installs depend on, so choose them carefully.
The account entity
Exactly one entity is the account. Humans, service accounts and bots are all records of that one entity; Owlie has no second account entity. Map the system's account states onto Owlie's three lifecycle states:
| Owlie state | Means | Typical system states |
|---|---|---|
ACTIVE | The account can sign in. | active, enabled |
DISABLED | The account exists but cannot sign in. | suspended, deactivated, blocked, locked |
DELETED | The account is gone or soft-deleted. | deleted, archived |
Two account shapes need a declaration:
- Invitations. When creating an account sends an invitation and the account becomes real only when
the person accepts, declare
create_semantics: "invite". Owlie shows the account as pending until it is accepted. - Accounts created elsewhere. When something other than this connector creates the accounts
(another connector, or single sign-on the first time someone signs in) and this connector should
only manage their access, declare
create_semantics: "external". Owlie then waits for Sync to find the account before adding Grants, and never creates, disables or deletes it. This is a Grants-only connector.
In the connector builder, set this on the account entity's Details tab under Account creation. Choosing invitations there also offers the re-invite operation, the invite expiry attribute, and whether the account's id changes when the invitation is accepted.
Identifiers
Mark each identifier property with "x-identifier": true, and list the same property names, first
one first, in the entity's identifier_paths. When identifier_paths is present it decides the order,
whatever order the properties are declared in, so always declare it. The first identifier is the
entity's external id. Owlie keys the record by it forever, and every relationship must name the entity
by that same value. Later identifiers are for lookups and matching.
Owlie reads the external id from the first identifier that has a value in the record. If the first is missing or empty on a record, the next identifier keys it instead, and Owlie sees a different record from the one it knew. Make sure the first identifier is present on every record your connector returns.
Choose the system's opaque, immutable id first. An email address or username that can change must
never be the first identifier: when it changes, Owlie would see a new account and lose the old one's
history. When the vendor documents that an id is never changed or reused, also mark it
"x-provider-stable-id": true.
Describe the records
Each entity has a JSON Schema describing the record exactly as the system returns it. Return the system's data as it is; never reshape it into an Owlie format. Anything provisioning writes must be a top-level property. Annotate properties to tell provisioning how to treat them:
| Annotation | Use it for |
|---|---|
readOnly: true | Values the system sets, such as ids and timestamps. Never written. |
writeOnly: true | Values sent but never read back, such as a password. |
"x-sensitive": true | Secrets and personal data to mask in logs. Not allowed on a property marked x-provider-stable-id, or on one that an x-reference points at. |
"x-provisioning": { "requiredOn": ["PROVISION"] } | Required only when creating an account. |
"x-provisioning": { "writableOn": ["PROVISION"] } | Can be set at creation only. |
"x-provisioning": { "immutable": true } | Set once, rejected on later updates. |
"x-reference": { "entity": "user", "attribute": "id" } | A property that points at another account, such as a manager. |
Declare sync_support.supports_incremental: true only when the system has a reliable "changed
since" query. Every field an entity can carry is in Connector manifest.
Engine-based kinds (LDAP, SCIM, SQL, flat file) generate these schemas from their definitions; you
still make the same decisions there.
Write it down
Before moving on, list each entity with its key, what it is in the system, whether it is the account, its first identifier and the call that lists it.
Next: Model Grant kinds