Managing permissions and roles¶
Tsuru uses role-based access control to decide which actions an identity can perform and which resources it can act on. Administrators define a role, add permissions to it, and assign the role to a user, group, or team token for a specific context value.
The authorization model can be read from left to right:
user, group, or team token
-> role assignment
-> role permissions
-> matching resource context
-> action allowed or denied
Access is additive. An identity may receive roles directly and, for users, through groups. Tsuru combines the permissions from all applicable role assignments. If none of them grants the requested action in a matching context, the action is denied.
Core concepts¶
- Permission: A named action or family of actions, such as
app.deployorapp.read. - Role: A reusable collection of permissions with one context type, such
as
teamorapp. - Role assignment: A role attached to a user, group, or team token with a
context value. For example, assigning a
teamrole with the valuemyteamlimits that role to resources related tomyteam. - Context: The scope in which a permission applies. A context consists of a
type and, except for
global, a value.
Typical workflow¶
The following example creates a role that can inspect and restart applications owned by a team.
First, inspect the permissions and contexts supported by the target Tsuru installation:
tsuru permission list --tree
Create a role whose assignments will use team names as context values:
tsuru role add app-reader-restarter team \
--description "Read and restart applications owned by one team"
Add the required permissions:
tsuru role permission add app-reader-restarter \
app.read app.update.restart
Assign the role to a user for the myteam context:
tsuru role assign app-reader-restarter user@example.com myteam
Inspect the result:
tsuru role info app-reader-restarter
tsuru user info
The user can now read and restart applications related to myteam, but this
assignment does not grant the same actions for applications outside that team.
To remove the assignment without deleting the reusable role:
tsuru role dissociate app-reader-restarter user@example.com myteam
Note
Roles, permissions, and assignments are separate objects. Removing a role assignment changes one identity's access. Removing a permission changes every assignment of that role. Removing the role removes the reusable role itself and is rejected while users still have it assigned.
Permissions¶
Tsuru permission names form a hierarchy separated by dots. A granted permission covers itself and every permission below it, but not its siblings. For example:
app.update.env.setgrants only that action.app.update.envgrants bothapp.update.env.setandapp.update.env.unset.app.updategrants all application update actions, but does not grantapp.deploy.appgrants all registered application actions.*grants every registered static permission.
Use the narrowest permission that supports the required work. Broad ancestors are convenient, but they also include permissions added below that ancestor in future Tsuru versions.
Find available permissions¶
Permissions can change between Tsuru versions and installations. Query the server instead of relying on a copied list:
tsuru permission list
tsuru permission list --tree
The output includes the context types supported by each permission. global
is available for every permission. Other contexts are defined on a permission
family and inherited by its descendants unless a more specific permission
defines a different set.
For example, the application permission family supports application, team, and
pool contexts, while app.create is limited to team and global contexts. A
role can only receive permissions compatible with the role's context type.
Warning
The * permission and the global context solve different problems. *
selects every static permission; global makes the permissions in one
role assignment apply to every matching resource.
Dynamic service-action permissions¶
Services with an enabled API manifest can declare named operations. Tsuru exposes a dynamic permission for each operation using this format:
service-action.<service-name>.<action>
For example, an action named rules.sync declared by the acl service becomes
service-action.acl.rules.sync. Dynamic permissions use the same hierarchical
rule as static permissions: service-action.acl.rules covers actions below
that prefix, and service-action.acl covers all declared actions for that
service.
Dynamic service-action permissions support global, service,
service-instance, and team contexts. They are listed by
tsuru permission list only while the corresponding service manifest is
enabled and declares the action. Removing or changing manifest actions may
conflict with roles that reference them, so review affected roles when updating
a service manifest.
Add a dynamic permission to a role with the same command used for static permissions:
tsuru role permission add acl-operator service-action.acl.rules.sync
A single add request must contain either static permissions or dynamic permissions; do not mix both kinds in one command invocation.
Roles and assignments¶
A role stores a name, description, context type, and permission list. It does not store a context value. The value is supplied each time the role is assigned, which allows the same role to be reused for different teams or resources.
For example, the same app-reader-restarter role can be assigned to one user
for team-a and another user for team-b. Each assignment expands to the same
permissions with a different team context value.
Manage a role¶
List and inspect roles before changing them:
tsuru role list
tsuru role info app-reader-restarter
Create or delete a role:
tsuru role add <role-name> <role-context>
tsuru role remove <role-name>
Add or remove individual permissions:
tsuru role permission add app-reader-restarter app.read
tsuru role permission remove app-reader-restarter app.read
Permission changes affect every existing assignment of the role immediately. The server authorizes these operations using role-management permissions, not the permission being changed:
- Adding a permission requires the global
role.update.permission.addpermission or an ancestor such asrole.update,role, or*. - Removing a permission requires the global
role.update.permission.removepermission or an ancestor.
When adding a static permission, the permission must exist and support the role's context type. When adding a dynamic permission, it must refer to an action declared by an enabled service manifest, or to a valid ancestor of such an action, and support the role's context type. Static and dynamic permissions cannot be added in the same request.
Removing a permission does not validate that the permission still exists or is currently attached to the role. Removing an absent permission is a successful no-op.
Permission-management privileges
The server does not require an administrator to possess a permission before
adding it to or removing it from a role. Adding a permission to an already
assigned role immediately expands the access of all its assignees. Treat
role.update.permission.add, role.update.permission.remove, and their
ancestors as security-administrator privileges.
Update the role's description, context type, or name:
tsuru role update app-reader-restarter \
--description "Read and restart team applications"
tsuru role update app-reader-restarter --context app
tsuru role update app-reader-restarter --name app-operator
Changing a role affects its existing assignments. In particular, changing its context type changes how stored context values are interpreted. Review all assignments and permission compatibility before making this change.
Remove a role only after it is no longer assigned to users:
tsuru role remove app-reader-restarter
Assign roles to identities¶
tsuru role assign accepts a user email, a team token ID, or a group prefixed
with group::
# User
tsuru role assign app-reader-restarter user@example.com myteam
# Team token
tsuru role assign app-reader-restarter 6f9b1f6a-88d7-4b01-a132-bd44d35506df myteam
# Group
tsuru role assign app-reader-restarter group:platform-engineers myteam
Users receive permissions from both their direct role assignments and the roles assigned to their groups. Team tokens receive permissions from their own role assignments.
For a non-global role, the context value is required and Tsuru validates that the referenced resource exists when the assignment is created. A global role is assigned without a context value:
tsuru role assign platform-auditor user@example.com
To delegate a role, the acting administrator needs the global
role.update.assign permission or an ancestor. Tsuru also verifies that the
administrator holds every static permission in the role at the target context.
For each static permission, any of the following grants is sufficient:
- The exact permission at the exact context type and value.
- An ancestor permission at the exact context type and value.
- The permission or an ancestor with global context.
For example, assigning a team role containing app.create for myteam
requires app.create or an ancestor for team:myteam, or a corresponding
global grant.
Dynamic permissions are not included in this delegation check. A role that
contains only dynamic permissions can be assigned by anyone with
role.update.assign; a mixed role is checked only against its static
permissions.
Role dissociation has a similar rule. The administrator needs the global
role.update.dissociate permission or an ancestor and must hold every static
permission in the role at the supplied context. Consequently, an administrator
may have permission to dissociate roles but still be unable to revoke a role
whose static privileges exceed their own.
Unlike assignment, dissociation does not validate that the context value still refers to an existing resource. It removes the exact role and context-value pair; removing an assignment that is already absent is a successful no-op.
Assignment endpoint behavior¶
The API applies the same authorization model to users, team tokens, and groups, with a few target-specific differences:
| Operation | Routes | Required permission | Additional conditions |
|---|---|---|---|
| Assign role | POST /roles/{name}/user, POST /roles/{name}/token, POST /roles/{name}/group |
Global role.update.assign or an ancestor |
Role exists; context is valid; administrator holds every static role permission at that context |
| Dissociate role | DELETE /roles/{name}/user/{email}, DELETE /roles/{name}/token/{token_id}, DELETE /roles/{name}/group/{group_name} |
Global role.update.dissociate or an ancestor |
Role exists; administrator holds every static role permission at the supplied context |
| Add permission | POST /roles/{name}/permissions |
Global role.update.permission.add or an ancestor |
Role and permission exist; permission supports the role context; static and dynamic permissions are not mixed |
| Remove permission | DELETE /roles/{name}/permissions/{permission} |
Global role.update.permission.remove or an ancestor |
Role exists |
For user assignments, the target user must already exist. For team-token assignments, the token must already exist. Assigning a role to a nonempty group name creates the stored group record when necessary. Dissociating from a group that does not exist is a successful no-op.
All these routes require an authenticated token. A missing authentication token produces HTTP 401; a valid token without the required Tsuru permission produces HTTP 403.
Tip
Prefer group assignments for access shared by a team of people and direct user assignments for exceptions. This keeps audits and removal of access easier to reason about.
Default roles¶
Default-role events assign roles automatically:
user-createapplies global roles when a user is created.team-createapplies team roles to the user who creates a team, with the new team name as the context value.
The role's context type must match the event. Manage defaults with:
tsuru role default add --user-create platform-auditor
tsuru role default add --team-create team-admin
tsuru role default list
tsuru role default remove --user-create platform-auditor
tsuru role default remove --team-create team-admin
Default-role configuration affects future creation events; it does not retroactively add or remove assignments for existing users or teams.
Contexts¶
A context determines where all permissions in a role assignment apply. The
role fixes the context type; the assignment provides the value. Context types
are not hierarchical: a team context does not automatically contain an app
context. Instead, an application authorization check presents all contexts
associated with that application, such as its name, teams, and pool. A grant
matches when its context type and value equal any context presented by the
resource.
| Context type | Assignment value | Typical scope |
|---|---|---|
global |
No value | Every resource checked for the role's permissions |
app |
Application name | One application |
job |
Job name | One job |
team |
Team name | Resources associated with that team |
user |
User email | One user account |
pool |
Pool name | Resources in that pool, where supported |
service |
Service name | One service |
service-instance |
Service and instance identity; see the warning below | One service instance |
volume |
Volume name | One volume |
router |
Router name | One dynamic router |
Only use a context type shown for the permission by
tsuru permission list. Tsuru rejects adding a permission to a role when that
permission does not support the role's context type.
How resource contexts combine¶
Many resources present more than one context during authorization:
- An application presents its application name, every team with access, and its pool.
- A job presents its job name, owner team, and pool.
- A volume presents its volume name, owner team, and pool.
- A service presents its service name and teams with access.
- A service instance presents its service-instance identity and teams with access.
This lets administrators express the same application permission at different
scopes. Given an application named payments-api, owned by payments, in the
production pool, app.deploy may be granted through any compatible role
assignment below:
app context: payments-api
team context: payments
pool context: production
global context: <no value>
These assignments are independent grants. Tsuru does not translate one context type into another and does not require all of them to match.
Global context¶
The global context is accepted by every permission and matches without a
resource value. Use it for true installation-wide administration:
tsuru role add platform-auditor global
tsuru role permission add platform-auditor app.read service.read
tsuru role assign platform-auditor auditor@example.com
Avoid using global roles merely to work around an incorrect context value. A global assignment may grant access to current and future resources throughout the installation.
Service-action context compatibility
At authorization time, current Tsuru code identifies a service instance as
<service-name>/<instance-name>. Some server versions validate new
service-instance role assignments using only the bare instance name. An
assignment created with the bare name may therefore fail to match runtime
authorization, while the composite value may be rejected during
assignment. Verify this behavior on your server version before relying on
service-instance-scoped roles. Until the server behavior is consistent,
prefer a compatible team, service, or global scope according to the
required access boundary.
Dynamic service-action permissions advertise service as an allowed
context, but current service-instance proxy authorization presents only
team and service-instance contexts. A service-scoped dynamic grant may
therefore not match. Prefer a verified team scope until your server
version presents the service context during proxy authorization.
Troubleshooting authorization¶
When an action is unexpectedly denied, check the model in order:
- Run
tsuru permission list --treeand confirm the requested permission exists and supports the role's context type. - Inspect the role with
tsuru role infoand confirm it contains the exact permission or one of its ancestors. - Inspect the user, group, or team token assignment and confirm the stored context value. Context matching is exact.
- Confirm the target resource is actually associated with that value. For example, a team-scoped application role only applies while the team has access to the application.
- For
service-action.*permissions, confirm the service manifest is enabled and still declares the requested action. - Check for client/server version differences if a command or context behaves differently from the examples in this guide.
A grant never turns into an explicit denial. If multiple roles are assigned, any matching grant allows the action; unrelated or nonmatching assignments are simply ignored for that check.
Next steps¶
Use tsuru role --help and tsuru permission --help to discover the commands
available in your client version. The complete command reference is available
under Tsuru Client.