What this is about
In CIAS, “attribute” means a named value about a person, for example department = Einkauf or projects = alpha, beta. Such values can live in three places, and the place decides who reads them and who may write them.
The three stores
| CIAS attributes | Profile attributes | Per-tenant attributes | |
|---|---|---|---|
| lives in | Record in CIAS | Account in Keycloak | CIAS, per person and tenant |
| affects requests | no | yes, through the token | yes, they replace the value from the token |
| read | Platform administrator, in the record | Platform administrator | Platform administrator or persons in the same tenant |
| write | Platform administrator, replaces all | Platform administrator, per key | according to the attribute's delegation and the ceiling |
| endpoint | POST /cias/admin/users/{id}/attributes | GET / POST /cias/admin/users/{id}/profile-attributes | GET / POST /cias/admin/users/{id}/tenant-attributes?tenantKey=… |
Each store
When: The subject area wants to remember something that does not touch permissions.
POST /cias/admin/users/{id}/attributes with { "attributes": { "department": "Einkauf" } }. The call replaces the whole set: whatever you do not send is gone afterwards. You read the values with the record, GET /cias/admin/users/{id}. Registration stores its remaining form fields here.
Result: Only for platform administrators. No check against a catalog.
When: A module needs a value in the token, for example projects for an attribute filter.
POST /cias/admin/users/{id}/profile-attributes with { "attributes": { "projects": "alpha,beta" } }. Only the named keys change; an empty value removes the key. CIAS writes each key to Keycloak one by one and then reads back what Keycloak actually holds. The response is this read state.
Result: Only for platform administrators. The value takes effect with the person's next token.
When: A value should apply in one tenant only, for example areas = nord in nordbau and areas = sued in suedlogistik.
POST /cias/admin/users/{id}/tenant-attributes?tenantKey=nordbau with { "attributes": { "areas": ["nord"] } }. Per key, the list replaces the previous values; an empty list clears the key. CIAS checks all keys before it writes anything: if one fails, nothing is written.
Result: The response is everything the person holds in this tenant afterwards.
How per-tenant attributes act on a request is described in One value per person or per tenant.
Who may write a per-tenant attribute
Here the role “platform administrator” is not the only thing that decides. There is a check per attribute:
-
CIASsigned inIs the caller signed in?↳ no 403
-
CIASCatalogHas a module declared the attribute?↳ no 403, also for platform administrators
-
CIASPlatform administratorThen allowed, also for withdrawn attributes
-
CIASwithdrawn?Does the module still declare the attribute?↳ no 403
-
CIASDelegationDoes the caller have one of the roles the attribute is delegated to?↳ no 403
-
CIASCeilingIs every written value within the caller's own value in this tenant?↳ no 403
- Values are written
All rejections look the same: 403 cias.user.administration-denied. Which attribute and which check failed is in the log.
- The installation sets the delegation per attribute, with a bean
DeclaredAttributeDelegation: attribute → roles that may write it. Without this bean, only platform administrators may write. There is no REST interface to change it; you can read it atGET /cias/admin/attributesin the fieldassignableBy. See Who may write an attribute. - The ceiling compares the same way as the filter: split at commas,
*means everything. If you haveareas = nord, westyourself, you may grantnord, but notsuedand not*. Clearing is always allowed if the delegation permits it. See The ceiling: nobody grants more than they have.
Reading
GET /cias/admin/users/{id}/tenant-attributes?tenantKey=nordbau returns all of the person’s values in this tenant, as lists. Platform administrators may read them, and so may persons whose own tenant is nordbau. The values of other tenants stay unreadable.