CodamAIDocs
Topicdone

Only your own data (owner filter)

In user models every person sees only their own rows. How _userId is set and checked.

Variants
CreateReadSearchChange and deletechildren created alongTechnical client without a personon behalf of another person

What this is about

Some data belongs to a single person: notes, drafts, personal settings. That is what user models (level USER) are for. Every row of a user model has a system field _userId, the ID of the person it belongs to.

CDMS adds a condition to every request on a user model: _userId = signed-in person. This condition is called the owner filter. You do not see it in your request and cannot switch it off.

When to put a model on the USER level is described in Model levels: system, tenant, user.

Two people, one table

flowchart LR
    subgraph T["Table note in the tenant's database"]
        R1["Shopping list<br/>_userId = anna"]
        R2["Holiday plan<br/>_userId = anna"]
        R3["Project ideas<br/>_userId = ben"]
    end
    A(["Anna: POST /note/query"]) --> F1{"_userId = anna"}
    B(["Ben: POST /note/query"]) --> F2{"_userId = ben"}
    F1 --> R1
    F1 --> R2
    F2 --> R3

Both send the same search. Anna gets two hits, Ben gets one. totalCount counts only their own rows in each case.

Where the ID comes from

The person’s ID is in the token, by default in the claim sub. CIAS reads it on every request and passes it on to CDMS. The client never sends it itself.

Variants

The owner filter in every operation

When: POST /note/create

  1. 1
    Client→CDMS
    sends the note, with or without _userId
  2. 2
    CDMS
    sets _userId to the ID of the signed-in person; a value sent along does not count
  3. 3
    CDMS→Database
    stores the note

Result: The note always belongs to the person who creates it.

When: POST /note/read/{id}

  1. 1
    CDMS→Database
    reads the row with id and _userId = signed-in person
  2. 2
    CDMS
    no row found → 404 not-found

Result: Someone else's note looks like one that does not exist.

When: POST /note/query

CDMS combines your filters with AND with _userId = signed-in person. Other people's rows are missing from data and do not count in totalCount.

Result: Lists in a response, e.g. the notes on another object, also show only your own rows.

When: PUT, PATCH, DELETE, rollback

  1. 1
    CDMS→Database
    counts the row with id and _userId = signed-in person
  2. 2
    CDMS
    0 → 404 not-found|<Dto>|<id>, even before the role check
  3. 3
    CDMS
    1 → continues as usual; _userId stays unchanged, even if the client sends a different value

Result: Nobody can take over another person's row or push a row onto someone.

When: A create also creates objects of a user model through a relation.

Every object of a user model created along with it also gets the ID of the signed-in person as _userId.

Result: Everything a request creates belongs to the same person.

Decision table

What Anna can do with a note
_userId of the noterole for the operationResult
annayesallowed
annano403 missing-permission|<role>
ben–read, change, delete: 404; search: missing from the list

Technical clients

A server that signs in with client credentials has no real person. The token then contains the ID of its service account. For the owner filter, the service account is a person like any other:

  • What the server creates belongs to the service account.
  • The server sees only rows the service account created itself, no rows of real people.

If a service is supposed to work on data of real people, a user model is usually the wrong choice. Use a tenant model instead and restrict it with an attribute filter or a custom filter.

On behalf of another person

CDMS has no role that lifts the owner filter, not even for administrators. If someone has to see the data of a specific person, they use the user switch of CIAS: whoever has the realm role allowed-user-context-switch sends the other person’s ID in the header user. The person must exist, must belong to the tenant if the request runs under one, and must have consented to the switch. Otherwise CIAS answers with 403. The owner filter then works with their ID and shows exactly their rows. Whether your roles or theirs apply is chosen with the header user-roles. More under User switch by header.

Pitfalls

Where to go next

Sources in the code and the knowledge base
  • CDMS/cdms-system-layer – AbstractLayer (recursivePrepare: set_userId, addSecurityFilters, buildSearchRoot, assertVisibleForWrite, recursiveCreate/Update/Patch: fields starting with _ skipped)
  • CDMS/cdms-persistence-database – AbstractUserModel
  • CDMS/cdms-generator – CdmsYamlLoader (modelType USER), EntityProcessor, DtoProcessor
  • CIAS/cias-authentication – TokenParser (userId), CiasTokenProperties (claim sub), TokenParser.switchUser
  • CDMS/cdms-integrationtest – AbstractDataFilterTest
  • documentation/10-cdms-grundlagen/02-modelle-und-metadaten.md
Search