Separation of duties and risk
Two questions sit next to each other in every governance review: which combinations of access must never land on one person, and who or what is risky right now. This page is how both are expressed — and, more usefully, what each one refuses to conclude.
A rule is a set of filters, not a table of pairs
A separation-of-duties rule declares two or more conditions, each a filter over entitlement attributes. An entitlement belongs to a side if it matches that side's filter. An identity violates the rule when it holds at least one active grant on each side.
Those filters use the same filter DSL that drives workflow triggers, dynamic group membership, audience conditions and scoping — a deliberate choice over the obvious alternative, a table of forbidden entitlement pairs. It earns its keep twice: administrators learn one syntax, and a rule describes a shape rather than an inventory. A pair table has to be extended by hand whenever a new entitlement is created that belongs on one side of an existing conflict, which nobody does, so it quietly stops describing the estate.
Conflicts span systems by construction. Sides resolve to entitlements, grants join back through accounts to the identity, and nothing in that path cares which connector a side lives on. A vendor-master permission in the ERP and a payment approval in a separate finance application is one violation on one person.
What one rule catches, and what it does not
Five rules ship seeded and enabled. Take the fraud one — Vendor master + payment
approval, severity critical, referencing SOX 404 and COSO ICFR:
- Side A: entitlement name contains any of
vendor,supplier,creditor,leverancier - Side B: entitlement name contains any of
payment,AP,approve,betaling
An identity holding SAP Vendor Master Maintenance and AP Payment Approval is flagged, whichever systems those live on. The finding names both grants, their systems and how each was obtained — a reviewer deciding which side to remove needs to know that one arrived through a role and the other was requested by hand.
Now the other half, because it decides whether you trust the number on the dashboard:
- It matches names, so it inherits whatever your source systems call things. An
entitlement named
AP-CRED-MAINT-01contains no side-A keyword. It is a vendor-master permission and this rule will never see it. Seeded rules are a starting position, not an inventory; a rule that matters should be re-stated against attributes you control — a classification, a risk level, an owning application. containsis a case-insensitive substring match, so side B'sAPalso matches SAP Basis Admin, Capacity Planning and Graph API Reader. Hence the dry-run: before you save, the editor reports how many entitlements landed on each side and a sample of who would be flagged.- Only in-target grants count, so access that is expected but not yet provisioned is not a violation for the detector. The enforcement gate sees it earlier.
- Only active identities, and by default no machines — suspended and terminated identities are pending revocation anyway, and batch accounts legitimately hold combinations that would be toxic for a person.
- Nothing in an unconnected system is considered. There is no inference step.
A rule can also be scoped by an identity-side filter, so a conflict that only matters for Finance is only evaluated for Finance.
Two guards that stop a rule being useless
Both sides must be non-empty. A rule is rejected at save if fewer than two conditions carry content. An empty filter matches every entitlement, so one empty side flags every identity holding two grants — a confident, comprehensive, worthless finding, and one that reads as a catastrophic compliance failure rather than as a typo.
An entitlement matching more than one side is dropped from all of them. A permission matching both the vendor and the payment pattern would otherwise occupy both sides at once, and a single grant on it would flag its holder for a conflict of one permission with itself. Such a permission is ambiguous, not two things. The same de-duplication runs in the enforcement gate — a detector and a gate disagreeing about who is in violation is worse than either being wrong alone.
Three actions, and what each does to access
Every rule carries one enforcement action. Three exist, all three are implemented, and the seeded rules ship on the mildest.
flag_only— detect and surface. One finding per rule and identity in the advisor inbox, contributing to that identity's risk score. No access changes.block_overrule_allowed— the conflicting access is blocked, and a time-boxed exception can restore it. The exception is not a checkbox: it is a risk acceptance that runs an approval chain, carries a justification, and expires at a date capped by policy. While one is active the entry readsoverruledrather thanblocked.block_no_exceptions— blocked with no overrule path. The only remedy is removing one side.
Enforcement happens at two points, answering different questions.
At request time, submitting an access request runs a pre-flight that expands both
the requested entitlements and the identity's current holdings through their role and
group hierarchies — a conflict hidden two levels inside a requested role is still a
conflict. Anything that would create a violation under a block_no_exceptions rule is
refused at submission with the rule named, since approving a request whose grant would
be killed on arrival with no exception path wastes an approver's time. Conflicts under
the other two actions ride along as warnings, so the approver decides with the conflict
in front of them.
In reconciliation, access already held is handled where all access is handled. When
an identity's expected access is recomputed, an enforcing rule annotates the
conflicting entries — they stay in the expected set, marked blocked or overruled,
because deleting them would erase the explanation. The apply path acts on the
annotation: a blocked grant present in the target is revoked, an overruled grant that
is absent is restored. The revocation is reversible, since the grant row survives for
an approved overrule to put back.
Which side is blocked is decided rather than guessed, and an entire side goes so the conflict is genuinely broken. One side established and the other new: the new side goes, because the arriving access is the creep. Both established: the side with the most recent grant. All new: all of it. With three or more sides, the newest.
No automated resolution
The advisor finding for a violation has no one-click fix. Its apply action returns a refusal and an explanation, deliberately. Which side to remove is a business judgement: revoking the vendor-maintenance half and revoking the payment-approval half both satisfy the rule and have entirely different consequences for the person's job, and nothing in the data says which is right. The finding names the identity, the rule, both sides and how each grant was obtained; a person chooses, and the advisory closes itself once the conflict is gone. The asymmetry with the blocks above is intentional — a block is a policy decision taken in advance, with a published tie-break, whereas auto-resolution would be the platform making that call after the fact on its own.
Risk scores are weighted sums you can recompute
The identity risk score is a single number from 0 to 100: a sum of components, each a count multiplied by a per-unit weight, capped at a per-component maximum, with the total clamped to 100.
That is the whole model. No learned model, no behavioural baseline, no opaque scoring service — and that is the design position rather than a stage on the way to one. A score that drives revocation and prioritisation has to be defensible to the person it describes and to the auditor asking why. Every score stores its own breakdown, so "why is this 74?" is answered with arithmetic:
| Component | Count | Per unit | Cap | Contribution |
|---|---|---|---|---|
| Critical SoD violation | 1 | 20 | 60 | 20 |
| Dormant privileged grant | 4 | 10 | 25 | 25 (40 capped) |
| Dormant grant | 3 | 3 | 15 | 9 |
| Open critical advisor finding | 1 | 15 | 30 | 15 |
| Never reconciled, or not in 30 days | 1 | 5 | 5 | 5 |
| Total | 74 |
Seventy-four falls in the high band (0–25 low, 26–50 medium, 51–75 high, 76–100 critical). Crossing into critical emits an event, so it can drive a workflow rather than waiting to be noticed on a dashboard.
Four properties matter more than the numbers:
- Caps stop one signal dominating. The fourth dormant privileged grant adds nothing, so a score cannot be run up by one noisy detector.
- Weights are per-tenant and merge per key, so changing one component leaves the other defaults intact and a partial override cannot silently zero the model.
- A component set to zero is disabled, not deleted — it keeps its row and its count, contributing nothing. A tenant governing machine ownership elsewhere can stop that signal moving the score without losing sight of the count.
- Overlapping signals are de-duplicated. Privileged access with no recent certification decision is a finding — unless it already counts as dormant.
Turning the score off stops the sweep and leaves the last computed values in place. Nothing is silently reset behind a toggle.
Two scores, two questions
The identity score answers who is risky. A separate score on each entitlement answers what is risky: privileged access spread across many holders, the share of holders who are dormant, membership of a tracked toxic combination, holders who are themselves high-risk, and exposure through a business-critical application. Same arithmetic, different subject. Neither replaces the administrator-set inherent risk level of an entitlement — that is a judgement about what the permission is, while the resource score measures what has happened around it. An entitlement classified low-risk that has accumulated three hundred holders, most of them dormant, is exactly the case worth seeing.
One eligibility rule, or the number stops meaning anything
The invariant most worth explaining to a reviewer is not about scoring at all: the finding and the score must share their eligibility predicate.
Dormancy proves it. A grant with no last-used timestamp is only dormant if the platform can actually observe activity on that system and has looked at least once; otherwise the empty value means "not tracked", and reading it as dormancy is fabrication. That gate is described in full under non-human identities and ownership.
The point is what happens when two consumers of one signal drift. The advisor's dormancy detector and the risk score's dormancy components once held byte-identical copies of the rule, kept in step by a comment asking the next person to remember. Once they diverge, an administrator sees a score of 74 with dormant grants itemised in its breakdown, opens the inbox it points at, finds nothing — and cannot tell which surface is lying. Both now compute from one shared implementation.
So: change an eligibility predicate — a threshold, a capability gate, a scope — and change it in every path that reads it. A score the inbox cannot account for is worse than no score.
Known edges, published rather than discovered
- Two SoD models exist. The rule described here is what the workbench edits and what everything above enforces. An older enumerated-pair model also survives, with its own endpoints, and still participates in the request-time pre-flight with alert / require-approval / block outcomes. What is configured there is real and enforced; it is simply not what the workbench shows. Consolidating the two is outstanding.
- Resource-side dormancy has no capability gate. The identity score and the advisor both require a system to be genuinely usage-tracked before reading an empty timestamp as dormancy. The per-entitlement dormancy-share component does not yet apply that gate, so on a system reporting no usage it counts holders as dormant when the honest answer is unknown. Prefer the identity-side figure where the two disagree.
- A rule's third and later sides do not contribute to resource risk. Rules support any number of sides; the resource score's toxic-combination component reads only the first two. Detection and enforcement handle all sides correctly — a scoring gap only.
- Scores are periodic, not live — identities every thirty minutes, entitlements hourly, with on-demand recompute for administrators and auditors. A score carries the time it was computed, and a component fires when an identity has not been reconciled in thirty days, so a stale score says so rather than being quietly trusted.
- The score measures findings, not behaviour. Every component counts something observed about configuration and governance state — open findings, unreviewed privilege, unused access, conflicts. None of it observes what a person did. A quiet insider with tidy access scores low, correctly; nothing here is a detection capability.
Further reading: