IDENTITY GOVERNANCE · LAB
Design roles from jobs, mine them from data, and keep them honest
Build a management role from a job description, mine group patterns from the simulated workforce, explain an outlier and a copying habit, spot role explosion, and change a role knowing everyone it reaches.
ReadyUses your lab tenant
The lesson
Builds on: Sources of truth.
New to the labs? Start with the lab toolkit and the shared cast and names every lab uses.
Your progress
Press Start before you begin. Only events your tenant records after that count, in the order below. Checking reads your tenant's Audit, so you need Audit read access in it.
Sign in to start this lab and check your progress. Log in or create an account.
Create a role from a job description
Recorded as
tenant.roles.createsucceeded.Plant a manual grant for mining to find
Recorded as
tenant.groups.memberssucceeded.Try to delete a near-duplicate role that is still held
Recorded as
tenant.roles.deleterejected (role_in_use).Delete the duplicate once nobody holds it
Recorded as
tenant.roles.deletesucceeded.Change a role that several people hold
Recorded as
tenant.roles.updatesucceeded.A role editor cannot add a permission they lack
Recorded as
tenant.roles.updaterejected (access_denied).
Setup
Your tenant's roles are management roles: named bundles of permissions from the catalog on the Roles page. Groups play the application roles that the lesson mines.
Open a bash shell and set the variables and helpers from the directory lab. The simulated workforce from the sources of truth lab must still be there.
Make sure Ben holds
Help deskand can sign in at$ISSUER/manage.Press Start on the lab page.
Walkthrough
From jobs to roles. Write three short job descriptions for running this tenant: help desk (read users, change group membership, lock accounts), provisioning operator (manage SCIM settings and schemas), and auditor (read Audit and Logs, nothing else). Map each task to catalog permissions on the Roles page. Then create the auditor's role,
lab-tmp-auditor, with exactlytenant.audit.readandtenant.logs.read.
Why it matters: this is "From jobs to roles". Starting from the job produces a role described in the words of the people who do the work.
Mine from what people hold. For each department in the simulated workforce, list each person's set of groups and count the patterns.
scim -G "$SCIM/Users" --data-urlencode 'filter=externalId sw "HRSIM-"' --data-urlencode count=200 \
--data-urlencode "attributes=groups,$ENT:department" \
| jq -r ".Resources[] | [.\"$ENT\".department, ((.groups // []) | map(.display) | sort | join(\";\"))] | @tsv" | sort | uniq -c
Each line is a department, a set of groups, and how many people hold exactly that set. Groups everyone in a department holds are the core; a set held by one person is an outlier.
Why it matters: this is "Two ways to find roles". Mining is fast and grounded in what people actually hold.
Plant an outlier and explain it. Pick a department
Aand another departmentBfrom the output. In Groups, add one person fromAto theHRSIM-... Bgroup. Rerun step 2: the outlier appears as a line with a count of 1. Search Audit for the group ID: the membership came from you, by hand.
Why it matters: mining proposes, people decide. The outlier is a leftover to remove, not a new role waiting to be defined.
The copying habit. Add
Print incident reviewsto every person in departmentA, as if each new account had been set up by copying the last one.
export REVIEWS=$(scim -G "$SCIM/Groups" --data-urlencode 'filter=displayName eq "Print incident reviews"' | jq -r '.Resources[0].id')
scim -G "$SCIM/Users" --data-urlencode "filter=$ENT:department eq \"<A>\"" --data-urlencode attributes=id | jq '[.Resources[] | {value: .id}]' \
| jq '{schemas:["urn:ietf:params:scim:api:messages:2.0:PatchOp"],Operations:[{op:"add",path:"members",value:.}]}' \
| scim -X PATCH "$SCIM/Groups/$REVIEWS" --data-binary @- -o /dev/null -w '%{http_code}\n'
Rerun step 2. Mining now proposes Print incident reviews as part of department A's core. Decide that it is not part of the job, and remove it from all of them with one PATCH of remove operations, one per member.
Why it matters: a pattern can describe a habit rather than the job, like the lesson's refund approval that every billing clerk inherited.
Role explosion signs. Create
lab-tmp-help-desk-nights, identical toHelp deskplustenant.users.unlock, and assign it to Ben. Roles now shows two roles that differ by one permission, one of them with a single holder and a time of day in its name. Try to delete it while Ben holds it: refused withrole_in_use. Remove it from Ben, then delete it.
Why it matters: this is "When roles multiply". Shift and site are scope, not content, and a role named after a time of day or a person is a warning sign.
A role change reaches everyone. Add
tenant.users.deletetoHelp desk. In a private window as Ben at$ISSUER/manage, the Delete action now appears on users; do not use it. Audit recordstenant.roles.update.
Why it matters: nobody requested this access and everyone holding the role received it in one step, which is why role changes are high-risk changes.
Restore: remove tenant.users.delete from Help desk and save.
Review the role's content and history. Go through
Help deskpermission by permission: is each still needed, and could you describe it in one plain sentence? Then search Audit for the role's ID: every create and update with actor and time is its change history.
Why it matters: this is "Keeping roles honest". A role can be held by exactly the right people and still carry a permission nobody needs.
Break it
Give Ben a temporary role
lab-tmp-role-editorwithtenant.roles.readandtenant.roles.update. As Ben, openHelp deskand try to addtenant.users.credentials.set, which Ben does not hold. The tenant refuses withaccess_denied: a role editor can only grant permissions they hold themselves.
Restore: remove lab-tmp-role-editor from Ben and delete it.
Check your work
Press Check my progress. The checks look for, in order:
tenant.roles.createsucceeded (step 1)tenant.groups.memberssucceeded (step 3)tenant.roles.deleterejected withrole_in_use(step 5)tenant.roles.deletesucceeded (step 5)tenant.roles.updatesucceeded (step 6)tenant.roles.updaterejected withaccess_denied(Break it)
After Cleanup, step 2's output shows no outliers.
Cleanup
Remove the planted outlier from step 3 and confirm the copied memberships from step 4 are gone.
Delete
lab-tmp-auditor. Ben keeps onlyHelp desk.