Assigning roles
Give your app's users, agents and scheduled jobs their roles, from the console, the API or a handler, within the grant ceiling.
A role does nothing until someone holds it. This page covers the three ways a role reaches a principal, and the one rule all three share: you can only hand out what you already hold.
Roles, and whose they are
Roles exist at two levels in an app:
- Shared roles (
user,admin,superuser,agent, and the capability roles such as Schema Builder) are seeded into every app and exist in every organization. They carry the seeded policies, and you can add your own policies to them. - An organization's own roles are created by that organization's owner, for their team. They
exist only in that organization, and take only that organization's own policies
(
POLICY_ROLE_SCOPE_MISMATCHotherwise).
A binding is always in an organization: Dana can be admin in her own organization and hold
nothing in another she has joined.
From the console
Under App access › Roles you create roles and attach policies to them (Assign Policy). An agent's roles are set on the agent's page, and a scheduled job's on the job's page. A job is created paused and holding no roles, and giving it one (arming it) is a separate decision, flagged high risk, so an agent asked to do it halts for a person to approve.
From your app, over the API
An organization's owner can assign roles to its members. The call has two steps: review returns
what would be bound and a reviewHash, and confirm with that hash binds it. The hash ties the
confirmation to exactly what was reviewed.
# 1. Review
curl -X POST "$AUTHORIC_URL/$APP_UID/autocrud/users/actions/bind-role" \
-H "Authorization: Bearer $OWNER_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "mode": "review", "user_uid": "<user uid>", "role_key": "admin" }'
# 2. Confirm with the hash from the review
curl -X POST "$AUTHORIC_URL/$APP_UID/autocrud/users/actions/bind-role" \
-H "Authorization: Bearer $OWNER_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "mode": "confirm", "user_uid": "<user uid>", "role_key": "admin", "reviewHash": "<reviewHash>" }'- Name the person by
user_uid(oremail), and the role byrole_uidorrole_key. - Inside your app, a binding is always made in the caller's own organization.
target_org_uidmay name only that one: another organization, or a person who is not in the caller's, answers404, the same as one that does not exist. From the console, the app's owner binds in any of the app's organizations: the person's home organization, or the onetarget_org_uidnames. - Confirming again is harmless: the response says
alreadyBound: true. unbind-roletakes the same body and removes the binding. Removing access needs no ceiling.GET /{appUid}/autocrud/users/actions/{userUid}/roleslists what a user holds.
Every bind and unbind is recorded in the app's audit trail.
From a handler: sdk.roles
Your app has a roster: someone is promoted to dispatcher, someone leaves. That is application logic, so it can live in handler code:
export default async function (data, ctx) {
const { sdk } = ctx;
await sdk.roles.assign({ userUid: data.userUid, roleKey: "dispatcher" });
const held = await sdk.roles.list({ userUid: data.userUid });
// later: await sdk.roles.unassign({ userUid: data.userUid, roleKey: "dispatcher" });
return { roles: held.map((role) => role.key_name) };
}Three independent checks stand behind every assign:
- The handler's capability. Assigning needs
sdk:roles_write, which no handler has by default and which is approved when the handler is published. Reading roles is granted by default. - The caller's own policy. The person running the handler must hold
users:bind-rolethemselves. A handler is never a way to gain a power the caller lacks. - The grant ceiling, below.
The organization comes from the run, never from the arguments: orgUid may only restate the
caller's own organization (ROLE_ORG_MISMATCH), and the person must already belong to it. Roles are
given only to people from handler code; an agent or a scheduled job is never armed this way
(PRINCIPAL_NOT_HUMAN).
The grant ceiling
Every door that grants (binding a role to a person, an agent or a job; attaching a policy to a
role; creating a policy) first checks that the granter already holds every action being handed
out. A role is compared by its contents, so a role that carries one action the granter lacks is
refused with 403 GRANT_EXCEEDS_HOLDER, and the response names the missing actions.
This is what makes delegation safe. Whatever you grant an organization's owner is the most that organization can ever spread among its own members.
Errors to branch on
| Code | Status | Meaning |
|---|---|---|
GRANT_EXCEEDS_HOLDER | 403 | The role carries actions the granter does not hold. |
ROLE_NOT_FOUND | 404 | No such role in this organization. |
ROLE_ORG_MISMATCH | 403 | A role is granted inside the caller's own organization. |
MEMBER_NOT_FOUND | 404 | The person is not a member of that organization. |
PRINCIPAL_NOT_HUMAN | 403 | Handler code tried to give a role to an agent or a job. |
CRON_ROLE_OUTSIDE_ORG | 403 | A scheduled job can only be given a role your organization owns. |
REVIEW_HASH_MISMATCH | 409 | What you confirmed is not what was reviewed. Review again. |
ROLE_PROTECTED | 400 | A seeded role cannot be deleted. |