> For the complete documentation index, see [llms.txt](https://docs.openiam.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.openiam.com/administration-guide/governance/sod/sod-policies-and-rule-sets.md).

# SoD policies and rule sets

What a segregation-of-duties policy and a rule set are, why an organisation needs them, the difference between preventive and detective control, what you can simulate before you commit, and the Inacti

## What this is, and when you need it

**Segregation of duties** and **separation of duties** are the same control and OpenIAM abbreviates both to **SoD**; the combinations it detects are what auditors call **toxic combinations**.

**Segregation of duties** is a security and compliance control that stops any single person from holding a combination of access rights that would let them commit fraud, cause an error nobody else would catch, or abuse a privilege unobserved. It is not a statement that any one entitlement is dangerous. It is a statement about the combination: each right is defensible on its own, and together they remove the second pair of eyes.

The conflicts everyone recognises are the ones to start with.

* **Creating and approving the same payment.** One person raises the vendor, one person releases the payment run. A user who can do both can pay a supplier they invented, and nothing in the process will stop them.
* **Creating and approving the same journal entry**, which is the same control one ledger further on.
* **Holding access across business units that are supposed to stay separate** — a trader and a settlement clerk, a buyer and a goods receipter, a developer and a production release approver.

The drivers are almost always external. **SOX** sections 302 and 404 make management assert that controls over financial reporting operate, and PCAOB auditors test procure-to-pay and record-to-report segregation directly. **SOC 2** and **ISO 27001** both expect conflicting duties to be identified and separated, and to be able to show it. **Internal audit** asks the same question in plainer words: show me who can do both, and show me what you did about it.

Without this control you have no answer to that question. Entitlements reviewed one at a time all look reasonable — a certification campaign that shows a reviewer one role per row will approve both halves of a toxic pair without anybody noticing. The combination stays invisible until somebody builds a report specifically to look for it, which in practice means it stays invisible until an auditor builds that report for you, on their timetable, using their definition of the conflict.

This page is the model. It explains what the objects are, which of the product's two enforcement points each conflict belongs at, and what changes as you move a control from reporting to blocking. Building a policy is [Create an SoD policy](/administration-guide/governance/sod/sod-policies-and-rule-sets/create-an-sod-policy.md); turning a shipped catalogue into policies is [Activate an SoD rule set](/administration-guide/governance/sod/sod-policies-and-rule-sets/activate-an-sod-rule-set.md).

## The pieces

Five words carry all the meaning here.

* A **policy** describes one conflict — a combination of access that no single person should hold at once. One row in `SOD_POLICY`.
* A **segment** is one side of that conflict: a bucket of entitlements that all confer the same capability, so holding any one of them puts the user on that side. A "Create PO" segment might hold the `PO_CREATOR` role and a `create-purchase-order` resource.
* A **violation** is a user who triggers enough segments at once. It is not a workflow object with states — it is the presence of a (user, policy) pair in the violation index, and it ends when the conflict ends.
* A **mitigating control** is a documented compensating measure that lets you accept a violation's risk without removing the access: a named person performing a named review on named evidence.
* An **exemption** formally excuses one user from one policy for one entitlement.

A **rule set** is the sixth object and it sits one level up: a catalogue of rule *recipes* that generate policies. OpenIAM ships ten of them — 360 SAP-derived rules across five verticals in a US and an EU edition — and activation turns the rules you confirm into real `SOD_POLICY` rows. A rule set is not itself enforced; the policies it generates are.

Thresholds are what make a combination a conflict rather than a list, and they are why triggering one segment on its own is not a violation:

{% code overflow="wrap" expandable="true" %}

```
Policy: "Create + Approve PO"   Threshold: 2 segments
│
├── Segment A: "Create PO"      Threshold: 1 entitlement
│   ├── Role: PO_CREATOR
│   └── Resource: create-purchase-order
│
└── Segment B: "Approve PO"     Threshold: 1 entitlement
    ├── Role: PO_APPROVER
    └── Resource: approve-purchase-order

A user holding PO_CREATOR AND PO_APPROVER breaches both segments → violation.
A user holding only PO_CREATOR does not violate (only 1 of 2 segments breached).
```

{% endcode %}

Everything lives on **Access Control → Segregation of Duties** (`/webconsole/segregation-of-duties`), whose six tabs are Policies, Mitigating Controls, Violations, Reports, Rule Set Catalog and Scheduled Detection. Policy editing is `/webconsole/sod-policy-edit?id=<SOD_POLICY_ID>`.

{% hint style="success" %}
**Day one.** Model **one** high-value conflict that the business already recognises — create a payment versus approve a payment is the canonical choice — and take that single policy the whole way through: define it, let OpenIAM find who already holds both sides, remediate them, and prove detection has gone quiet. Do not attempt the whole conflict matrix. A half-configured catalogue produces a violation list nobody can explain, an unexplainable list gets written off as noise, and a control everyone ignores is worse than no control at all, because now you have documented that you knew.
{% endhint %}

## Preventive versus detective controls

OpenIAM gives you exactly two enforcement points, and the design question is which conflicts belong at each.

**Preventive** is `SEVERITY = HARD` on an active policy. OpenIAM evaluates the *resulting* entitlement set — the user's stored roles, groups, organizations and resources merged with what the request would add — and refuses the save before anything is written. It fires on every provisioning path, including access-request fulfilment and business-rule recalculation, which is why a new `HARD` policy can break automation that used to succeed. Reserve it for conflicts where a single unauthorized combination is unacceptable: payment creation plus payment release, journal entry plus journal approval, and privileged Basis access.

**Detective** is `SEVERITY = SOFT` plus scheduled detection. The conflict is recorded, the manager group is notified, the user appears in the violation index and in reports, and the access still happens. Use it where the conflict is sometimes legitimate, where a compensating control already exists, or where blocking would stop the business — and pair it with a `SOD_MITIGATING_CONTROL` and a review frequency so "detective" means something more than a row in a report.

Two practical constraints shape the choice. First, [the severity override on activation](#the-severity-override-on-activation) means the *risk classification* is the real switch, not the severity field. Second, `EXCEPTIONS_ALLOWED` tracks severity automatically — `HARD` policies get exceptions off, so there is no per-user escape hatch from a Critical rule short of a mitigating control or editing the policy. Decide that before you enforce, not after the first blocked hire.

### Five controls, and what each one cannot do

Preventive and detective are the two enforcement points the product gives you, but a defensible programme uses five mechanisms, and each has a limitation to plan around rather than discover.

| Control                  | OpenIAM mechanism                                                                            | Best use                                                                                                   | Main limitation                                                                                                           |
| ------------------------ | -------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------- |
| **Preventive**           | `SEVERITY = HARD` on an active policy, or a `SOD_RISK_CRITICAL` rule in an enforced rule set | Block a new assignment that would complete a known conflict                                                | Disrupts fulfilment whenever the entitlement mapping, the source data or an untested grant path is wrong                  |
| **Detective**            | `SEVERITY = SOFT`, a rule set in `SHADOW`, impacted-user detection, the scheduled digest     | Find conflicts that already exist, are inherited, arrived by import, or appeared when role content changed | The exposure exists until somebody acts on the finding                                                                    |
| **Periodic attestation** | A certification campaign carrying SoD context on the review item                             | Get an accountable human to confirm that retained access is still justified                                | A review decision still has to be fulfilled and re-evaluated before anything has actually changed                         |
| **Compensating**         | A `SOD_MITIGATING_CONTROL` linked to the policy                                              | Govern the residual risk when both duties genuinely have to stay                                           | Removes neither the access nor the violation; it documents oversight and nothing more                                     |
| **Exception**            | A `SOD_EXEMPTION` for one user and one entitlement                                           | Exclude a narrowly approved case under policy                                                              | Needs a rationale, a scope, an owner and a review date, and the product enforces none of them — there is no expiry column |

Note what the table does not say. None of the five is a substitute for another: an exemption without a compensating control is an accepted conflict with no oversight, and a mitigating control without periodic attestation is a paragraph of text that nobody has confirmed is still being performed.

### Before you call a policy preventive

Severity `HARD` is a field on a row. *Preventive* is a property of the whole system, and you have it only once every path that can grant the conflicting entitlement has been shown to block, or to route the request somewhere safe. Work the matrix.

| Assignment path               | What to test                                                                                                                                      |
| ----------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Direct administration**     | An administrator adding the second entitlement on the user's Roles, Groups, Resources or Organizations tab                                        |
| **Access request**            | A self-service or on-behalf-of request that would complete the conflict — and what the requester and the approver are actually told when it fails |
| **Business role**             | Membership of a business role whose content already includes the other side of the conflict                                                       |
| **Business rule**             | A rule that recalculates entitlements from an attribute change. This is the path that fires without anybody pressing a button                     |
| **Synchronization or import** | An inbound feed or entitlement import carrying the conflicting grant                                                                              |
| **Nested entitlement**        | A role or group nested inside one the user already holds — an indirect grant, which is the case hand-testing usually misses                       |
| **Lifecycle event**           | Joiner, mover and rehire processing, where a block surfaces as a failed lifecycle run rather than as a rejected request                           |
| **API and integration**       | Anything calling provisioning directly, including anything that sets `skipSodPolicyCheck`                                                         |

For each path, test a clean grant and a conflicting grant, and record three things: whether the operation was rejected **before** anything reached the managed system, how the requester and approver were informed, and whether any partial change survived. A `HARD` policy that rejects the request but leaves the target account changed is worse than a `SOFT` one, because it reports success at preventing something it did not prevent.

Then design the break-glass path, deliberately. Emergency, service and privileged access needs an approved route that does **not** depend on somebody quietly setting the policy inactive, dropping severity to `SOFT`, or passing `skipSodPolicyCheck` — each of those switches the control off for the entire population, not for the one emergency, and none of them leaves a record that says "we turned the payment control off on Tuesday". A defensible break-glass is a time-boxed grant with a named approver, an independent mitigating control operating for the duration, and a recorded end date, and it leaves the policy enforcing throughout.

Keep detective detection running after you go preventive. It is what covers access that already existed, role content that changed underneath you, and any path that turned out not to be intercepted the way the matrix predicted.

### The recommended adoption sequence

This is the order that works, and each step exists because of the one before it.

1. **Map the business process, then the technical entitlements.** Name the conflicting tasks with the process owner first; only then decide which roles, groups, resources or organizations actually confer them. Working backwards from the entitlement list produces a policy that describes your access model instead of your control objective.
2. **Start soft.** Create one two-sided policy with `SEVERITY = SOFT`, and leave it soft.
3. **Test direct and inherited assignment, in both directions.** A compliant user holding one side must produce no violation. A user holding both must produce one. A user who *inherits* one side through a nested role or a business role must be detected identically — if inherited access is not proven to be caught, nothing else on this list means anything.
4. **Detect across the population, and reconcile with the process owner.** Run the sweep, then classify every hit: confirmed conflict, false positive from a wrong mapping or threshold, inherited access needing an owner's review, an already-approved exception, or stale evidence needing reconciliation.
5. **Correct the mappings.** Every false positive is a mapping or threshold defect, and fixing it is far cheaper before anybody is receiving a digest about it.
6. **Establish ownership.** A manager group on the policy, a named remediation owner, a report audience and a mitigation route — in place before the volume arrives, not after.
7. **Only then go hard.** Promote selected policies to `HARD`, or a rule set from `SHADOW` to `ENFORCED`, under change control, and keep the detective sweep running afterwards.

Steps 3 to 5 are the ones organisations skip, and skipping them is the usual reason an SoD programme stalls: the first digest lands with a population nobody can account for, the list is dismissed as noise, and the policy stays soft forever.

## What you can simulate before you commit

Every question of the form "what would happen if…" has a different answer in OpenIAM depending on what the *it* is. Some changes can be previewed exactly; one important one cannot. Know which is which before you promise a what-if analysis to a control owner.

**Simulating the rule set.** Activating a rule set opens **Activate rule sets (n)**, which is a dry run before it is a commit. It lists every rule with the action it will take — **Will create policy** or **Already activated** — the match outcome for each entitlement reference, and the summary `N policies will be created · N entitlements matched · N entitlements will be created`. Individual rules can be deselected. Nothing is written until **Confirm activation**. That is the impact analysis for *"what does turning this catalogue on actually create in my tenant"*, and it is the step people skip. [Activate an SoD rule set](/administration-guide/governance/sod/sod-policies-and-rule-sets/activate-an-sod-rule-set.md).

**Simulating enforcement.** **Shadow** is the enforcement simulator: the policies are real, detection runs, every violation is recorded, reported and digested, and nothing is ever blocked. Run it for at least one full detection cycle and you have the answer to *"how many people would this policy stop, and which ones"* without having stopped anybody. That is the whole reason the ladder has three rungs rather than two.

**Simulating a rule change on live policies.** When a rule's recipe changes after its policy was generated, the rule-set card shows an out-of-sync count and **Re-sync policies** previews the change first: *These activated rules changed after their policies were created. Choose which live policies to rebuild from the current rule.* Policies edited by hand are flagged — *Edited directly — re-sync overwrites manual changes* — so the preview is also the collision check.

**Simulating a birthright change.** The nearest thing OpenIAM has to a full population what-if is on business rules, not on SoD. Save a rule change, then **Access Control → Business Rules → Out of sync users → Preview impacted users**: every user is evaluated against the pending rules and the ones whose access would change are listed, each with the rule that matched, the conditions that matched it, and the actions that *would be* performed. Nothing is provisioned until you press **Provision impacted users**. Use it before any birthright change that could complete a conflict. [Business rules](/administration-guide/access/business-rules.md).

**Simulating one grant.** At the point of request, the service catalogue evaluates the selection before it reaches the cart and warns *The requested access violates a segregation of duties rule. Are you sure you want to select this item and override these rules?* On an administrative save, a `HARD` policy refuses with **Cannot save due to SoD policy violation** before anything is written. Both are decided on the *resulting* entitlement set, so both answer "would this grant create a conflict" — by attempting it and being refused, which is a what-if you can run safely on a disposable identity.

**Simulating a remediation.** In a user access review, opening a conflicting item gives the reviewer **Resolve SoD Violation**, and it recalculates live as they tick entitlements to remove: the triggered segment count moves from its current value to a projected one, the panel reads **Policy Will Be Resolved** or **Violation Still Active** against the policy threshold, and the confirm button becomes **Confirm Removal (n) — Will Resolve**. Nothing is submitted until they confirm.

### What cannot be simulated

**There is no screen that takes a hypothetical user and a hypothetical entitlement and reports the conflicts the pair would create.** The preventive check is the only answer to that question, and you reach it by attempting the grant on a disposable identity and reading the refusal — which works, and is what to teach, but it is a test rather than a report and it produces no exportable analysis.

**There is no analysis of what a role's own content would introduce.** OpenIAM evaluates entitlements a user holds, not the entitlements nested inside a role you are drafting. Nothing tells you "adding this permission to this role would put 340 people in conflict" before you add it; the way to find out is to add it, run **Preview impacted users** or detection, and read the result. On SAP content the boundary is harder still, because the product never reads SAP role contents at all — [SAP role and SoD analysis](/administration-guide/governance/sod/sap-sod-analysis.md).

**The Violations tab's Detect users button is not a what-if.** It re-detects conflicts in the access people already hold. It answers "who is in conflict now", never "who would be".

## The enforcement ladder

This is the single most important mechanic in the subsystem, and it exists at two levels.

**Per policy**, `SOD_POLICY.SEVERITY` is either `SOFT` or `HARD`:

| Value  | Effect at provisioning time                                                                                                                                                                |
| ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `SOFT` | The conflict is audited (`SOFT_SOD_POLICY_VIOLATION_DETECTION`), the manager group is notified with the `SOD_VIOLATION_SOFT` template, and provisioning **continues**                      |
| `HARD` | The conflict is audited (`HARD_SOD_POLICY_VIOLATION_DETECTION`), the manager group is notified with `SOD_VIOLATION_HARD`, and the save is **blocked**, failing with `SOD_POLICY_VIOLATION` |

**Per rule set**, `SOD_RULE_SET.STATUS` is the ladder you move a whole catalogue along:

| Status     | What it means                                                                                                                                                                           |
| ---------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `INACTIVE` | The catalogue exists, no policies generated. Every shipped rule set ships in this state                                                                                                 |
| `SHADOW`   | Policies exist and are active, but every one is forced to `SEVERITY = SOFT` and `EXCEPTIONS_ALLOWED = Y` regardless of the rule's risk level. Violations are visible and never blocking |
| `ENFORCED` | Severity is derived per policy from its risk classification                                                                                                                             |

Enforcing accepts a transition only from `SHADOW`, and rejects anything else with `INVALID_ARGUMENTS`. Deactivating a set returns it to `INACTIVE` and leaves its policies inactive; re-activating in Shadow re-forces `SOFT`, while re-activating in Enforced leaves each policy's severity as the administrator last had it.

Run Shadow for at least one full detection cycle. It is the only way to find out how many people already hold each conflict before a `HARD` policy starts refusing saves.

## The severity override on activation

{% hint style="danger" %}
**The risk classification silently decides severity — your choice on the policy screen does not survive `Enforce`.** Enforcing a set walks every policy it generated and sets severity purely from the policy's risk tag: `SOD_RISK_CRITICAL` becomes `HARD` with exceptions **off**, and `SOD_RISK_HIGH` or `SOD_RISK_MEDIUM` is forced back to `SOFT` with exceptions **on** — whatever you set on the General Information tab. Hand-hardening a High rule is silently undone.

The same mapping runs at activation, with one difference that matters: at activation an explicit `severity` on the rule JSON *is* honoured and wins over the risk level. All 360 shipped rules leave `severity` null, so shipped content is governed entirely by `riskLevel`. `Enforce` ignores the rule's override entirely and re-derives from the tag.

Check it after every `Enforce`: on the Policies screen, filter to the set and confirm that `HARD` appears against exactly the `SOD_RISK_CRITICAL` policies and nothing else. Any `HARD` policy carrying a High or Medium tag has been reverted since you last looked.

The practical consequence: **change the risk classification, not the severity field.** If you need a High-risk conflict to block, edit the rule's `riskLevel` to `CRITICAL` before activation, or accept that every `Enforce` will flip your manual severity back.
{% endhint %}

The three classifications, what each becomes, and every other value these screens accept are in [SoD rule and policy reference](/administration-guide/reference/sod-rules-and-rule-sets.md).

## Conflict groups: the other toxic-access check

**Conflict groups** are a separate, older toxic-access feature — available since OpenIAM 4.2.2 and described as an extension of the SoD concept — and they are not SoD policies. Configure them at `/webconsole/conflict-groups`, signing in as a Super Security Admin; no **Access Control** menu item leads there, so type the route. The screen has two tabs: **Conflict Groups Configuration** and **Violating conflict groups users**.

A **conflict group** unites a set of OpenIAM objects — roles, groups, resources and organizations — into one group in which every item is toxic in conjunction with any other item from the same group. There is no segment, no threshold, no risk classification and no severity: two members of the same group held at once is toxic access, full stop. The objects do not have to be linked to the user directly, and that is the point of the feature:

> Conflict **Group A** contains **ResourceA21** and **ResourceB11**. Conflict **Group B** contains **GroupA1** and **ResourceB1**. A user granted both **Role A** and **Role B** has toxic access, because between them Role A and Role B inherit objects from at least one conflict group.

Once a conflict group exists, a new entitlement cannot be added to a user if the new link would cause toxic access. Users still appear on **Violating conflict groups users** in two cases: the conflict group was created after they already held the entitlements, or they were saved in *Skip Conflict Group validation* mode.

That mode is set on the classic edit-user page with the **Do you want to continue with toxic access?** checkbox at the foot of the screen, or from a synchronization script:

{% code overflow="wrap" %}

```java
pUser.setSkipConflictGroupCheck(true);
```

{% endcode %}

{% hint style="info" %}
**Which one to use.** A conflict group is the cheap answer to "these things must never be held together": a flat all-pairs rule with a hard block and nothing to configure. A SoD policy is the *auditable* answer — named segments, thresholds, a risk classification, a manager group, mitigating controls, exemptions with a recorded justification, scheduled detection, a digest, a CSV report and rule-set provenance.

Anything you have to evidence to an auditor belongs in a SoD policy. Conflict groups have no severity ladder, no exemption record, no digest and no report, so a violation there is a screen somebody has to remember to open. And treat both skip flags the same way: `setSkipConflictGroupCheck(true)` in a synchronization script suppresses the check for every user the script sets it on, exactly as `skipSodPolicyCheck` does for SoD, and neither leaves anything behind that says the control was bypassed. There is no audit action for a skipped check and no counter of how often it happened, so the only way to know is to read your own scripts and workflows. Keep a list of the paths that set either flag, and treat that list as part of your control documentation.
{% endhint %}

## Verification

The model itself has nothing to save, but three things are worth confirming before you build on it.

| Claim                                   | Evidence                                                                                                                                        |
| --------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- |
| The three risk classifications exist    | **Administration → Metadata Types** with **Grouping** filtered to `SOD_RISK` returns `SOD_RISK_CRITICAL`, `SOD_RISK_HIGH` and `SOD_RISK_MEDIUM` |
| Enforcement hardened only the Criticals | On the Policies screen filtered to the set, `HARD` appears against exactly the `SOD_RISK_CRITICAL` policies                                     |
| A `HARD` policy really blocks           | The negative test fails with `SOD_POLICY_VIOLATION` and leaves a `HARD_SOD_POLICY_VIOLATION_DETECTION` audit entry                              |

## Related

* [Create an SoD policy](/administration-guide/governance/sod/sod-policies-and-rule-sets/create-an-sod-policy.md) — build one by hand, including a conflict that spans two applications
* [Activate an SoD rule set](/administration-guide/governance/sod/sod-policies-and-rule-sets/activate-an-sod-rule-set.md) — turn a shipped catalogue into policies through Shadow and Enforced
* [SoD detection and violations](/administration-guide/governance/sod/sod-detection-and-violations.md) — the inline check, the population sweep, the scheduled detection task and the violation digest
* [Work an SoD violation to closure](/administration-guide/governance/sod/sod-triage-remediation-and-evidence.md) — the violation lifecycle, remediation at the source, exemptions, mitigating controls and the CSV report
* [SAP role and SoD analysis](/administration-guide/governance/sod/sap-sod-analysis.md) — what the shipped SAP content analyses, and what it does not
* [SoD rule and policy reference](/administration-guide/reference/sod-rules-and-rule-sets.md) — every value, the rule JSON schema, the ten catalogues and the tables
* [Risk factor reference](/administration-guide/risk/factors.md#factor-entitlement-sensitivity) — the separate `RISK` grouping, and why it is not `SOD_RISK`

*Applies to OpenIAM 2026.8.1. Last reviewed 3 September 2026.*


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.openiam.com/administration-guide/governance/sod/sod-policies-and-rule-sets.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
