Managing access
Access in TagShape is: permissions go on groups, people go in groups. There is nothing else.
Setting up a group
{
"name": "Readers",
"description": "Can see the library but not change it",
"permissions": [
"TagGroupList", "TagGroupRead",
"TagList", "TagRead",
"VariableList", "VariableRead"
]
}
Needs GroupCreate, and the ability to grant each permission listed. See
granting versus holding.
Two checks run beyond the endpoint permission:
- Grant check — a permission being added that you cannot grant is rejected, and named in the response.
- Dependency check — a group carrying
AuditReadwithoutAuditListis refused. The permission would do nothing, and a group that silently does nothing is worse than a rejected save.
Adding people
{ "userIds": ["9c1e7a4d-…", "3f52a18b-…"] }
Needs GroupUpdate and no grant check — adding somebody to a group is not the same act as deciding what the
group may do.
Changes take effect at the member's next token refresh, not immediately. With the default one-hour token that is at most an hour.
A worked set of groups
A shape that works for most organizations:
| Group | Permissions | For |
|---|---|---|
| Readers | TagGroupList, TagGroupRead, TagList, TagRead, VariableList, VariableRead | Anyone who needs to see the library |
| Editors | Readers + TagGroupCreate/Update/Delete, TagCreate/Update/Delete, VariableCreate/Update/Delete | People who maintain configuration |
| Secret readers | VariableRead, VariableReadSecrets | The few who need actual secret values |
| Importers | BatchProcessingImport | Pipeline accounts that apply a library file |
| Deployment | VariableRead, VariableReadSecrets | Accounts behind deployment API keys |
| Auditors | AuditList, AuditRead | Compliance and investigation |
| Administrators | UserList…UserDelete, GroupList…GroupDelete, PermissionsAll | People who manage access |
| Platform | PermissionsAllGlobal, OrganizationList…OrganizationDelete, AuditGlobalList, AuditGlobalRead | Whoever runs the installation |
Two things worth noticing.
Secret readers is separable from Editors. Writing a sensitive value needs only VariableUpdate, so the people
who maintain configuration need not be the people who can read the secrets in it.
Administrators holds PermissionsAll, not every permission. PermissionsAll lets them hand out any
non-global permission without their being able to use any of them. An administrator can put
VariableReadSecrets on a group without becoming a reader of secrets.
Global permissions
Six permissions reach beyond one organization, and only somebody holding PermissionsAllGlobal can hand any of
them out:
PermissionsAllGlobal, PermissionsImpersonate, AuditGlobalList, AuditGlobalRead, and the five
Organization* permissions.
Keep them to a single platform group with very few members.
Issuing API keys
A key carries the permissions of the account that owns it, so the way to scope a key is to scope its account.
- Create an account for the consumer — not a person's account.
- Put it in a group holding only what the consumer needs.
- Grant that account
ProfileCreateApiKey. - Sign in as it and create the key.
For a key that never expires the account also needs ProfileNeverExpiresApiKey. Think twice: a key that never
expires is one that cannot be forgotten about safely, which is why it is a separate decision.
See API keys.
Reviewing access
| Question | Where |
|---|---|
| What can this person do? | GET /users/{id} — their groups and the permissions those carry |
| Who is in this group? | GET /groups/{id} |
| What keys does this account hold? | GET /users/{id}/apikeys — needs UserRead |
| Who changed a group, and when? | Audit log, entryType of Group |
| What permissions exist? | GET /permissions |
Removing access
| To remove | Do |
|---|---|
| One person's access | Take them out of the group, or delete the account (UserDelete) |
| A capability from everyone in a group | Take the permission off the group (GroupUpdate) |
| A machine credential | DELETE /users/{id}/apikeys/{apiKeyId} (UserDelete) |
Deleting an account does not retroactively invalidate an already-minted JWT. A token remains valid until it
expires — at most an hour by default. Where that matters, shorten Jwt:DurationInMinutes.
Common mistakes
Granting AuditRead alone. It depends on AuditList and does nothing without it. The group save will refuse
it.
Granting VariableReadSecrets alone. Same reason — it depends on VariableRead.
Expecting PermissionsAll to let somebody do things. It governs granting, not doing. Somebody with only
PermissionsAll cannot read a single variable.
Expecting a permission change to take effect immediately. It reaches the caller at their next token refresh.