CodamAIDocs
Topicdone

Why invisible objects return 404

An object you may not see does not exist from the caller's point of view. This also applies to changing and deleting: you can only write what you can read.

Variants
ReadSearchreferences and lists in the responseChangeDeleteRollbackDownload

What this is about

An object is invisible to you when one of the filters of the row level excludes it: it belongs to another person (own data), it does not match your profile (attribute filter), or a custom filter of the project does not let it through.

For an invisible object CDMS answers exactly as for an object that does not exist at all: 404.

Why not 403?

403 or 404
403
you may not do this
  • says: the object exists
  • an attacker could try other people's ids and learn which ones exist
  • CDMS uses 403 for missing roles: they apply equally to all objects and reveal nothing about a single one
404
does not exist for you
  • says nothing about whether the object exists
  • someone else's object and a made-up id look the same
  • CDMS uses 404 for invisible rows

Variants

Invisible objects in every operation

When: POST /read/{id}

  1. 1
    CDMS
    checks the read role
  2. 2
    CDMS→Database
    reads the row with id and all filters
  3. 3
    CDMS
    no row → 404 not-found

Result: When reading, the role comes first: without the read role you get 403, even for an invisible object.

When: POST /query

Invisible rows are missing from data and do not count in totalCount. There is no error.

Result: The hit count never reveals how many objects there are in total.

When: reference or list you read along

An invisible single reference comes back as null. In a list, invisible entries are missing.

Result: So null can mean: not set or invisible to you.

When: PUT /update/{id}, PATCH /update/{id}

  1. 1
    CDMS→Database
    counts the row with id and the same filters as for reading
  2. 2
    CDMS
    0 → 404 not-found|<Dto>|<id>
  3. 3
    CDMS
    1 → only now checks the update role

Result: When writing, visibility comes first: invisible gives 404, even without the role.

When: A child with an id in a nested write

The same filters as reading, applied to the target: if it is invisible to you, the answer is 404 missing-object|<id>|<model> — the same answer an id that never existed gets. Read permission for the target model is required as well.

Result: You can only connect to what you could have looked at. See The four cases.

When: DELETE /delete/{id}

Same as changing: visibility first (404), then the delete role (403). You do not need a read role, but the read filters still apply.

Result: See How a DELETE runs.

When: POST /{id}/rollback/{revision}

Same as changing: visibility first (404), then the rollback role (403). A deleted object no longer exists; that also gives 404. If the rollback re-links a single reference, the old and the new target are checked as for linking.

Result: You can only roll back what you can see now.

When: GET /{id}/file

The download first reads the object like a read. Invisible → 404, before any file flows.

Result: See Downloading.

When: POST /{id}/history

First the two roles (403), then visibility: invisible → 404 missing-object|<id>|<model>. If the object has been deleted, its last state before the deletion counts. On a model with a filter, an unknown id cannot be told apart from a hidden one.

Result: See Reading the history.

Decision table

Answer depending on role and visibility
Operationrole presentobject visibleAnswer
readno–403 missing-permission|<read role>
readyesno404 not-found
change, delete, rollback–no404 not-found|<Dto>|<id>
change, delete, rollbacknoyes403 missing-permission|<role>
anyyesyesallowed

Pitfalls

Where to go next

Sources in the code and the knowledge base
  • CDMS/cdms-system-layer – AbstractSystemLayer (readObject, updateObject, patchObject, deleteObject, historyRollback), AbstractLayer (recursiveRead, assertVisibleForWrite, fetchAndSetModel, fetchAndSetList)
  • CDMS/cdms-persistence-database – AbstractDatabasePersistence (count, queryObjects)
  • CDMS/cdms-commons – EntityNotFoundException
  • CDMS/cdms-generator – ApiProcessor (download: readObject, then downloadFile)
  • CDMS/cdms-integrationtest – AbstractDataFilterTest (readByIdWithNoAccessFilter, update/patch/delete without access)
  • documentation/60-erweiterung/03-eigene-filter.md
Search