Beta

Create a tenant

A new tenant starts with its own users, OAuth settings, audit history and logs. You are its first Tenant Admin.

BTL Admin

IDENTITY GOVERNANCE · LAB

Review a group with real context, close the loop, and fix a removal that comes back

Build a reviewer worksheet for Photo moderation from real data, decide rule-granted rows once and manual grants one by one, confirm a removal in the directory, review a role, and fix access that a rule keeps restoring.

Partly readyUses your lab tenant

The lesson

Builds on: Why access needs governance, Sources of truth.

New to the labs? Start with the lab toolkit and the shared cast and names every lab uses.

Partly ready. Most of this lab runs today. Steps that wait on platform features are marked, and Missing infrastructure says what they need.

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.

  1. A rule grants the group to one department

    Recorded as scim.group.patch succeeded for lab-provisioning.

  2. Confirm the removal by reading the group back

    Recorded as scim.groups.search succeeded for lab-scim-reader.

  3. Change a role after reviewing its content

    Recorded as tenant.roles.update succeeded.

  4. Fix the source that kept restoring access

    Recorded as scim.user.patch succeeded for lab-hr-feed.

Setup

You are the owner of Photo moderation, like Morgan Hale reviewing who can update oncology records. Its members arrived in different ways, which is what makes the review worth doing.

  1. Open a bash shell and set the variables and helpers from the directory lab, plus HR_ID, HR_SECRET, READER_ID and READER_SECRET (read -rs for secrets).

  2. Make sure Photo moderation holds Cora and Ava, both added by hand in earlier labs. Create [email protected] (Mod Test) in the portal and add it to the group. Do not sign in as it.

  3. Pick one department from the simulated workforce as DEPT and look up the IDs you need.

export DEPT=<a department from the simulated workforce>
export MOD=$(scim -G "$SCIM/Groups" --data-urlencode 'filter=displayName eq "Photo moderation"' | jq -r '.Resources[0].id')
export CORA=$(scim -G "$SCIM/Users" --data-urlencode 'filter=userName eq "[email protected]"' | jq -r '.Resources[0].id')
  1. Press Start on the lab page.

Walkthrough

  1. Grant by rule. Rule R4 says everyone active in DEPT belongs in Photo moderation. Apply it with a small function you will run again later.

apply_r4() { scim -G "$SCIM/Users" --data-urlencode "filter=active eq true and $ENT:department eq \"$DEPT\"" --data-urlencode attributes=id \
  | jq '{schemas:["urn:ietf:params:scim:api:messages:2.0:PatchOp"],Operations:[{op:"add",path:"members",value:[.Resources[] | {value: .id}]}]}' \
  | scim -X PATCH "$SCIM/Groups/$MOD" --data-binary @- -o /dev/null -w 'R4 applied: %{http_code}\n'; }
apply_r4
  1. Build the worksheet. For every member, gather:

    • Department and job: scim "$SCIM/Users/<id>" | jq "{displayName, title, dept: .\"$ENT\".department}"

    • How granted: search Audit for the group ID. lab-provisioning in step 1 means rule R4; you or Ben in the portal means a manual grant.

    • Last used: the Last sign-in column on Users.

    • Peers: how many people in the same department hold the group, against the department's size.

Fill one row per member:

PersonDepartment and jobHow grantedLast usedPeers with this accessDecision and reason

Why it matters: this is "Making a decision". Last use, peers and how the access was granted turn a name on a list into something a reviewer can judge.

  1. Rule-granted rows are one decision. Confirm R4 once: should everyone in DEPT moderate photos? Then check that each of those people's department value is right. The rows themselves need no individual decision.

Why it matters: removing a rule-granted row would last only until the rule ran again. Reviewing through the rule makes a long list short.

  1. Decide the manual grants. lab-tmp-modtest: manual, never signed in, no peers, no reason anyone can give, so remove. Ava: manual, recently used, moved to moderation in the changing jobs lab, so keep and record the reason. Cora: manual, used, the team's longest member, so keep. Record each decision with reason and time.

cat > review.json <<'EOF'
{"review": "Photo moderation", "reviewer": "group owner", "items": [
  {"person": "[email protected]", "decision": "remove", "reason": "No reason, never used, no peers"},
  {"person": "[email protected]", "decision": "keep", "reason": "Moved to moderation; uses it daily"},
  {"person": "[email protected]", "decision": "keep", "reason": "Moderation team member"}]}
EOF
  1. Close the loop. Remove the test account through the provisioning client, then confirm it from the directory with the read-only client.

export MT=$(scim -G "$SCIM/Users" --data-urlencode 'filter=userName eq "[email protected]"' | jq -r '.Resources[0].id')
jq -n --arg u "$MT" '{schemas:["urn:ietf:params:scim:api:messages:2.0:PatchOp"],Operations:[{op:"remove",path:("members[value eq \""+$u+"\"]")}]}' \
  | scim -X PATCH "$SCIM/Groups/$MOD" --data-binary @- -o /dev/null -w '%{http_code}\n'
READ_TOKEN=$(token_for "$READER_ID" "$READER_SECRET" "$SCIM_SCOPE.read")
curl -s -G -H "Authorization: Bearer $READ_TOKEN" "$SCIM/Groups" \
  --data-urlencode "filter=displayName eq \"Photo moderation\" and members[value eq \"$MT\"]" | jq .totalResults

The answer is 0. Add the Audit event ID of the removal to the item in review.json.

Why it matters: this is "Closing the loop". A decision to remove creates work, and the item is complete only when the access is gone, the removal is confirmed in the target, and the result sits next to the decision.

  1. Review a role, and review by manager. List the holders of Help desk on Users. Go through its permissions: does Ben still need tenant.users.lock now that urgent departures go through the source? If not, remove it from the role and record why. Then list Cora's reports with filter=$ENT:manager eq "$CORA" and write the list of their groups for Cora to confirm.

Why it matters: this is "Who reviews what". The role's owner judges what the role allows; a manager judges whether each report's job still needs what they hold.

  1. Removed access comes back. Remove one R4 member from Photo moderation because "they do not need it", using the step 5 PATCH with their ID. Run apply_r4. They are back.

Why it matters: when removed access returns after the next sync, a rule or a source still grants it, and the fix belongs there.

Fix it at the source: as lab-hr-feed, move that person to another department, then run apply_r4 again and remove them from the group. This time they stay removed.

export HR_TOKEN=$(token_for "$HR_ID" "$HR_SECRET" "$SCIM_SCOPE.users $SCIM_SCOPE.groups")
jq -n --arg ent "$ENT" '{schemas:["urn:ietf:params:scim:api:messages:2.0:PatchOp"],Operations:[{op:"replace",path:($ent+":department"),value:"<another department>"}]}' \
  | curl -s -X PATCH "$SCIM/Users/<their id>" -H "Authorization: Bearer $HR_TOKEN" -H "Content-Type: application/scim+json" --data-binary @- -o /dev/null -w '%{http_code}\n'

If the department was right and the rule was wrong, the fix would be a change to R4, decided by its owner.

  1. Check yourself for rubber-stamping. Note how long your decisions took and how many were "keep". Count which rows you could not decide without asking someone.

Why it matters: this is "Rubber-stamping". A review that removed nothing deserves a second look, and silence should never count as approval.

Planned walkthrough

Once G23 exists, the review is a tenant campaign.

  1. Start a review campaign for Photo moderation, owner-reviewed, due in five days. Rows granted by a rule appear as one item for the rule; manual grants with no recorded reason are listed first and marked.

  2. You cannot review your own access; such rows route to another reviewer.

  3. Keeping a marked row requires a reason. An undecided row escalates to the reviewer's manager at the deadline instead of counting as keep.

  4. A remove decision triggers the removal, reads the group back, and stores the confirmation with the decision.

  5. A move recorded by HR opens a small review for the person's new manager with only the access the move did not recalculate.

Check your work

Press Check my progress. The checks look for, in order:

  • scim.group.patch succeeded by lab-provisioning (step 1)

  • scim.groups.search succeeded by lab-scim-reader (step 5)

  • tenant.roles.update succeeded (step 6)

  • scim.user.patch succeeded by lab-hr-feed (step 7)

Your review.json holds a decision and reason for every manual row, and the removal's Audit event ID.

Cleanup

  1. Remove the R4 members from Photo moderation if you no longer want them there, and delete lab-tmp-modtest.

  2. Keep review.json. The evidence lab counts its decisions.

Missing infrastructure

  • G23: there are no review campaigns by manager, entitlement or application, no reviewer assignment that excludes self-review, no deadlines with escalation, no required reasons for keeping marked items, no stored decisions linked to confirmed removals, and no event-triggered reviews after a move.

Back to all labs

We value your privacy

We use cookies and similar technologies to enhance your browsing experience, and analytics to understand our traffic. By clicking "Allow All", you consent to optional analytics. Cookie Policy

The Lab