> For the complete documentation index, see [llms.txt](https://docs.opendatadiscovery.org/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.opendatadiscovery.org/configuration-and-deployment/enable-security/authorization/owners.md).

# Owners

An **Owner** is a catalog-side identity that represents a data steward — a person, team, or service responsible for a data entity or term. Owners are distinct from the authenticated users who sign in to ODD Platform: a signed-in user binds to an Owner through the [User-owner association](/configuration-and-deployment/enable-security/authorization/user-owner-association.md) workflow, and from that point on the user's actions on data entities (creating ownership, editing custom metadata, marking statuses) are attributed to the Owner rather than to the raw username.

This page is the operator's reference for the Owner model — what an Owner is, how Owners get created, how Owner roles work, and the known caveats around Owner CRUD that operators authoring deployment-time scripts or compliance-grade audit policies should understand.

## What an Owner is

An Owner is a single row in the `owner` table with a `name` (free-text, unique among active Owners) and a `deleted_at` soft-delete marker. Each Owner can carry zero or more **Owner roles** (RBAC bundles defined on the [Roles](/configuration-and-deployment/enable-security/authorization/roles.md) page) that grant the bound user catalog-wide permissions. Each Owner can be bound to **at most one** active user (the [user-owner association](/configuration-and-deployment/enable-security/authorization/user-owner-association.md) is a one-to-one relation), and each user can be bound to at most one Owner.

Owners are the unit of ownership on data entities: an entity's ownership relation references an Owner row, not a user, so reassigning a user to a different Owner moves the user's perceived attribution across every entity without rewriting the entities themselves.

You can browse and manage Owners on the [Management → Owners](/features/management.md) tab.

<figure><img src="/files/xlQq9DgsDgEXMHymSZDP" alt=""><figcaption><p>Owners management</p></figcaption></figure>

## How Owners are created

There are three write-paths that produce a new row in the `owner` table. Two of them are the curated path (a deliberate operator action); the third is an emergent accretion path that operators should be aware of when modelling their deployment's Owner directory.

* **Operator-curated (the `OWNER_CREATE` path).** An operator with the `OWNER_CREATE` permission uses the **Create owner** affordance on Management → Owners, or `POST /api/owners`. The form fields are `name` and an optional `roles` list. This is the path documented in operator playbooks and the path most deployments use during initial setup.
* **User self-service via the home-page widget.** When a signed-in user reaches the [Owner-association widget](/configuration-and-deployment/enable-security/authorization/user-owner-association.md#for-regular-users) and types an owner name that does **not** exist in the directory, the platform creates the Owner row on submit (the freeSolo + `OwnerService.getOrCreate` composition described under [Choosing an owner name](/configuration-and-deployment/enable-security/authorization/user-owner-association.md#choosing-an-owner-name)). The user's request then enters the standard self-request / admin-approve flow. No `OWNER_CREATE` permission is required of the requester — the creation rides on the request submission.
* **Admin auto-approve via `DIRECT_OWNER_SYNC`.** A user holding the `DIRECT_OWNER_SYNC` permission can mint a brand-new Owner name and self-bind in a single POST — see [How DIRECT\_OWNER\_SYNC changes the user-side flow](/configuration-and-deployment/enable-security/authorization/user-owner-association.md#how-direct_owner_sync-changes-the-user-side-flow). The Owner row is created on the requester's behalf with no admin review.

Beyond these three explicit paths, the platform's `OwnerService.getOrCreate` helper is also reachable from service-tier callers that take an owner-name string and need to attach the corresponding Owner row (entity-ownership creation, term-ownership creation). The auto-create behaviour there is detailed in the [Lifecycle and known caveats](#lifecycle-and-known-caveats) section below.

## Owner names accumulate over time

Because the free-text input on the home-page widget does not deduplicate against case or whitespace variants, the Owner directory naturally accretes near-duplicates (`John Smith` / `john smith` / `John Smith` with a trailing space) as new users sign in over the deployment's lifetime. The platform does not collapse these on read — `john smith` and `John Smith` are distinct Owners, each potentially bound to a different user, each potentially carrying different roles.

**Operator hygiene recommendation:** pick a stable owner-name convention for your deployment (canonical casing, no leading/trailing whitespace, no Unicode look-alikes) and communicate it to new users. Periodically audit Management → Owners for near-duplicates and merge or rename them. An operator with the `OWNER_RELATION_MANAGE` permission can also remove individual user-Owner bindings through the [Active associations tab](/configuration-and-deployment/enable-security/authorization/user-owner-association.md#removing-an-existing-binding-active-associations-tab).

## Owner roles

An Owner carries an optional list of **roles** — RBAC bundles defined on the [Roles](/configuration-and-deployment/enable-security/authorization/roles.md) page. When a user is bound to an Owner, the Owner's roles **supersede** the user's auth-mode-derived roles for the purposes of catalog-side authorization. This is the platform's mechanism for granting a user catalog-wide permissions (browse-only, edit-glossary, manage-data-entities, etc.) that are independent of the user's identity-provider group memberships.

Attach roles to an Owner from the Management → Owners → Edit form. The `ROLE_CREATE` / `ROLE_UPDATE` / `ROLE_DELETE` permissions gate role management itself.

## Owners on data entities

Ownership of a data entity is recorded as a relationship from the entity to one or more Owner rows, with an optional Owner role (the "title" of that ownership — for example, `Steward`, `Subject Matter Expert`, `On-call`). The relation is curated per-entity on the entity's detail page; the `DATA_ENTITY_OWNERSHIP_CREATE` / `_UPDATE` / `_DELETE` permissions gate those actions.

For the user-facing surface where ownership is curated and displayed, see the [Data entity detail page → Permissions](/features/data-discovery/entity-detail-page.md) reference.

## Lifecycle and known caveats

The Owners API surface and the Owner-CRUD service tier have four operator-visible behaviours that are not obvious from the per-page documentation alone. Each item below names what an operator might assume, what actually happens, and what to do today.

### 1. Owner CRUD events are not audited at the platform tier

The Owner service creates, updates, soft-deletes, and re-creates Owners inside a transactional context but emits **no Activity Feed event** on any of those operations. The activity table's schema requires a `data_entity_id` foreign key on every row — Owner CRUD has no data-entity context, so adding an activity emission to the existing stream would require a coordinated schema change. The `owner_association_request_activity` stream covers the request workflow (user submits → admin approves/denies) but not the Owner entity's own lifecycle.

Combined with the role-binding hard-delete on the Owner DELETE path and the partial-unique-index pattern that allows soft-deleted names to be re-used by a new Owner row, an operator can delete `Alice`, recreate a new `Owner` row with the name `Alice`, and there is no platform-visible record of the rename or the original deletion except the original row's `deleted_at` timestamp.

For compliance frameworks that require identity-change audit trails (SOX records of identity-management changes, NIST 800-53 AU-2 audit events), use one of the compensating controls in [Audit trail scope → Compliance implications](/configuration-and-deployment/enable-security/audit-trail-scope.md#compliance-implications-and-compensating-controls).

### 2. `GET /api/owners` is unauthenticated-read; every signed-in user enumerates the directory

The `/api/owners` endpoint's POST / PUT / DELETE verbs are each gated by an `OWNER_*` permission (see the table below), but the GET verb is **not gated**. There is no `OWNER_READ` permission anywhere on the platform; any authenticated user under `LOGIN_FORM`, `OAUTH2`, or `LDAP` can enumerate the entire Owner directory through `GET /api/owners`. Under `auth.type=DISABLED` the endpoint is anonymously reachable.

| Verb                      | Permission gate                   |
| ------------------------- | --------------------------------- |
| `POST /api/owners`        | `OWNER_CREATE`                    |
| `PUT /api/owners/{id}`    | `OWNER_UPDATE`                    |
| `DELETE /api/owners/{id}` | `OWNER_DELETE`                    |
| `GET /api/owners`         | **none** — any authenticated user |

This matches the platform's broader [read-collaborative posture on Management catalogs](/configuration-and-deployment/enable-security/authorization/permissions.md#management-permissions): all Management read endpoints are reachable to any signed-in user by design. Deployments that need read-side isolation cannot achieve it through `OWNER_*` permissions today.

### 3. Three service-tier side-doors mint Owner rows without `OWNER_CREATE`

In addition to the three explicit Owner-creation paths described above, three other service-tier code paths reach `OwnerService.getOrCreate` and silently mint an Owner row when their input names one that does not exist:

* `POST /api/owner_association_request` — the user-self-request endpoint (any authenticated user). Creating a request with a never-before-seen owner name creates the Owner row before the admin review starts.
* `POST /api/dataentities/{data_entity_id}/ownership` — gated by `DATA_ENTITY_OWNERSHIP_CREATE`. Attaching ownership to an entity with a brand-new owner-name string creates the Owner.
* `POST /api/terms/{term_id}/ownership` — gated by `TERM_OWNERSHIP_CREATE`. Attaching ownership to a glossary term with a brand-new owner-name string creates the Owner.

**Operator consequence:** an operator who grants `DATA_ENTITY_OWNERSHIP_CREATE` to a role expecting it to be a strict "attach to existing Owner" capability actually grants "mint Owner + attach" — the new Owner row appears in `GET /api/owners` immediately and persists across the rename or removal of the entity that triggered its creation. Monitor Owner-directory growth as part of your audit cadence; if `OWNER_CREATE` is reserved to a small operator group, also audit the three side-door endpoints for unintended Owner-name expansion.

### 4. `PUT /api/owners/{id}` with empty (or absent) `roles` silently destroys all role bindings

The `OwnerFormData.roles` field is marked optional in the OpenAPI spec, but the runtime treats absent-or-empty `roles` as a destructive update: the service helper collapses both `null` and `[]` to the empty list, the update transaction calls `deleteOwnerRelationsExcept(ownerId, [])` (which deletes every existing role-binding on this Owner because no existing binding is in the empty set), and then `createRelations(ownerId, [])` (which inserts nothing). The Owner persists; every role-binding is gone.

The UI's read-modify-write pattern always sends the current `roles` array in its PUT request, so the destructive path never surfaces through the Management UI for typical workflows. Direct-API consumers (Terraform providers, CI scripts, automation jobs) calling the endpoint with a partial body — for example, a rename — hit the destructive path on the first omission.

{% hint style="danger" %}
**`PUT /api/owners/{id}` with body `{"name": "new_name"}` removes every role binding on the Owner.** The OpenAPI spec presents `roles` as optional, but the runtime treats absent and empty identically. Always include the current `roles` array verbatim when issuing a PUT request, even for rename-only operations. A platform-side fix that adopts PATCH semantics (with `null` meaning "no change") or marks `roles` required is tracked upstream.
{% endhint %}

Safe-pattern code for direct-API consumers performing a rename:

```bash
OWNER=$(curl -X GET "$ODD/api/owners/$ID" | jq)
NEW_NAME="new_name"
NEW_BODY=$(echo "$OWNER" | jq --arg n "$NEW_NAME" '.name = $n')
curl -X PUT "$ODD/api/owners/$ID" \
     -H "Content-Type: application/json" \
     -d "$NEW_BODY"
```

The fetch-modify-write pattern preserves the existing `roles` array (and any other optional field with the same destructive semantic). UI workflows do not need this pattern — the Management UI already does the fetch-modify-write internally.

## Operator-facing surfaces that depend on user-owner association

A signed-in user without a `user_owner_mapping` row sees every `My`-scoped surface render empty — the platform cannot answer "what changed on the things you own" without the binding. The personalised surfaces that depend on the association:

* The **Activity Feed**'s `My Objects` / `Upstream Dependents` / `Downstream Dependents` tabs (see [Activity Feed → My Objects setup](/features/active-platform-features/activity-feed.md#my-objects-setup-user-owner-association-prerequisite)).
* The **Alerts** page's `My Objects` / `Dependents` tabs (see [Alerting → Known UX limitations](/features/active-platform-features/alerting.md#known-ux-limitations)).
* The recommended-entities widgets on the catalog home page.

A platform admin who creates Owners and binds others without creating a mapping for their own account experiences these surfaces as silent-empty during onboarding — a common "platform is broken" first impression. Adding yourself as an Owner on the entities you want surfaced (Management → Associations → Create association) is the fix; the binding only affects what your account sees and does not change anyone else's access.

## Where to next

* [User-owner association](/configuration-and-deployment/enable-security/authorization/user-owner-association.md) — the user-side workflow for binding to an Owner, the three write-paths in full, the admin operational surface, and the post-association lifecycle.
* [Roles](/configuration-and-deployment/enable-security/authorization/roles.md) — the RBAC bundle definitions that Owners can carry, and the precedence rules when an Owner role and a user-derived role both apply.
* [Permissions](/configuration-and-deployment/enable-security/authorization/permissions.md) — the full enumeration of permission keys, including the `OWNER_*` rows referenced above.
* [Management → Owners](/features/management.md) — the operator UI tab where Owners are browsed, created, edited, and deleted.
* [Audit trail scope](/configuration-and-deployment/enable-security/audit-trail-scope.md) — for the broader picture of what the platform audits and the compensating controls for the negative-half subjects (RBAC, Owner, Term, Namespace, Datasource, Collector lifecycle).


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.opendatadiscovery.org/configuration-and-deployment/enable-security/authorization/owners.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
