Segregation of Duties (SoD) policies
Segregation of Duties (SoD) is a security and compliance control that prevents any single user from holding a combination of access rights that could enable fraud, error, or abuse of privilege. In OpenIAM, SoD policies define which entitlements (roles, groups, resources, or organizations) are considered conflicting, detect users who violate those policies, and provide tooling to remediate or formally exempt violations.
The SoD feature is typically used for:
- Preventing a user from holding both "Create Payment" and "Approve Payment" roles simultaneously.
- Flagging users who belong to conflicting business units.
- Ensuring compliance with SOX, SOC 2, ISO 27001, or internal audit requirements.
Core concepts
Policy
A SoD policy is the top-level object defining a conflict. Key attributes are as follows.
| Field | Type | Description |
|---|---|---|
name | String | Human-readable policy name |
description | String (max 1024 chars) | Explanation of the conflict |
active | Boolean | Whether the policy is enforced |
severity | SOFT / HARD | Impact level of a violation (see §5) |
policyThreshold | Integer | Minimum number of violated segments before a policy-level violation is triggered |
exceptionsAllowed | Boolean | Whether exemptions may be granted for this policy |
managerCanHandleSpv | Boolean | Whether the user's manager may handle violations |
managerGroupId | String | ID of the group responsible for managing violations |
riskId / riskName | String | Risk classification category |
segments | Set\<SodPolicySegment\> | One or more conflict segments (see policy segments below) |
mitigatingControls | Set\<SodMitigatingControl\> | Controls that mitigate the policy risk (see mitigating controls below) |
Policy segments
A segment defines the specific set of conflicting entitlements within a policy. A policy can have multiple segments; the policyThreshold controls how many segments must be violated before the policy is considered violated overall.
Each segment contains:
| Field | Description |
|---|---|
name / description | Segment identification |
active | Whether this segment is actively evaluated |
threshold | Minimum conflicting entitlements within the segment to trigger violation |
managerGroupId | Segment-level manager group (overrides policy-level if set) |
roles | Roles that are in conflict within this segment |
groups | Groups that are in conflict within this segment |
resources | Resources that are in conflict within this segment |
organizations | Organizations that are in conflict within this segment |
Example: A segment named "Payment Controls" might contain both the "Payment Creator" role and the "Payment Approver" role. Any user holding both would trigger a violation of this segment.
Mitigating controls
A mitigating control is a compensating measure that reduces the risk of an SoD violation without removing the conflicting access. Mitigating controls are documented in the system for audit purposes and linked to policies.
| Field | Description |
|---|---|
name / description | Control identification |
active | Whether the control is active |
ownerType / ownerId | User or group that owns the control |
managerType / managerId | User or group that manages the control |
reviewFrequency | MONTHLY, QUARTERLY, SEMI_ANNUALLY, or ANNUALLY |
effectiveDate / expirationDate | Validity period of the control |
More on mitigating controls can be found in this document.
Configuration
Creating a policy
REST endpoint: POST /rest/api/sod-policy/save
A new policy is created with a name, description, severity, and at least one segment. The request body (SodPolicyBean) supports:
- Setting the policy active or inactive.
- Linking mitigating controls by ID.
- Defining one or more segments, each with sets of conflicting role/group/resource/organization IDs.
On successful save, the system:
- Persists the policy and its segments to the database (
SOD_POLICY,SOD_POLICY_SEGMENTand related junction tables). - Refreshes the Redis policy cache.
- Triggers a background job to re-evaluate all users against the updated policy.
Audit actions generated are CREATE_SOD_POLICY or UPDATE_SOD_POLICY
Searching policies
REST endpoint: GET /rest/api/sod-policy/search
Supported filters:
| Parameter | Description |
|---|---|
name | Substring match on policy name |
resourceIds | Policies referencing these resources |
groupIds | Policies referencing these groups |
roleIds | Policies referencing these roles |
organizationIds | Policies referencing these organizations |
from / size | Pagination |
sortBy / orderBy | Column and direction for sorting |
Deleting a policy
REST endpoint: DELETE /rest/api/sod-policy/{id}
Removes the policy and its segments. Existing exemptions and historical violation records are retained for audit purposes.
Audit action generated is DELETE_SOD_POLICY.
Violation detection
How violations are detected
Violation detection evaluates each user's full entitlement set against all active SoD policies and segments. There are two types of violations:
| Type | Description |
|---|---|
| Direct | The user holds the conflicting entitlement explicitly and directly |
| Indirect | The user has effective access to a conflicting entitlement via inheritance (e.g., through a parent group or role hierarchy) |
Detection algorithm (per user, per policy):
- Retrieve the user's complete entitlement set from the AuthorizationManager.
- For each active segment in the policy:
a. Apply any existing exemptions — exclude exempted entitlements from evaluation.
b. Compare the user's entitlements against the segment's conflicting sets (roles, groups, resources, organizations).
c. Classify each match as direct or indirect.
d. If the number of matched entitlements meets or exceeds the segment
threshold, record a segment violation. - If the number of violated segments meets or exceeds the policy
policyThreshold, record a policy violation.
Violation data is stored in ElasticSearch for fast querying.
Triggering detection
REST endpoint: POST /rest/api/sod-policy-violation/impacted-users/detect
This triggers a system-wide re-evaluation of all users against all policies. Detection is also triggered automatically when:
- A policy is saved or updated.
- A user's entitlements change via provisioning.
Querying violations
| Endpoint | Description |
|---|---|
GET /rest/api/sod-policy-violation/impacted-users/search | List all users with at least one policy violation |
GET /rest/api/sod-policy-violation/{userId} | Summary of all violated policies for a specific user |
GET /rest/api/sod-policy-violation/{userId}/{sodPolicyId} | Detailed entitlements violating a specific policy for a user |
The detailed response includes the specific conflicting entitlements (direct and indirect), the segment that defines the conflict, and whether each violation has been resolved.
Severity levels
Severity determines what happens at the moment a provisioning operation (user create or update) would result in a SoD conflict. The check runs inside UserMgr.validateAgainstSodPolicy(), which is called during every user save unless the request explicitly sets skipSodPolicyCheck=true.
Hard violation
A HARD violation blocks the provisioning operation entirely.
What happens step by step:
- The system evaluates the user's incoming entitlement set against all active SoD policies (via
AuthManager.isConflict()). - A HARD conflict is found.
- Audit log entry is written with action
HARD_SOD_POLICY_VIOLATION_DETECTION, including the full violation details serialized as JSON. - A
SodPolicyServiceException(ResponseCode.SOD_POLICY_VIOLATION) is thrown — the user save rolls back and the entitlement change is rejected. - The policy's
managerGroupIdmembers are notified via theSOD_VIOLATION_HARDnotification type, every time the violation is detected (including repeat occurrences, not just the first time).
The calling code receives the exception, and the end user or API consumer gets an error response. The conflicting entitlement is never persisted.
Soft violation
A SOFT violation allows the provisioning operation to proceed but records and notifies about the conflict.
What happens step by step:
- The system evaluates the user's incoming entitlement set against all active SoD policies.
- A SOFT conflict is found.
- Audit log entry is written with action
SOFT_SOD_POLICY_VIOLATION_DETECTION. - The user is saved to the database normally — the entitlement change is applied.
- For existing users: if this is a new (previously unknown) violation, the violation is recorded in Elasticsearch (
UserViolatedSodPolicyDoc) and the manager group is notified viaSOD_VIOLATION_SOFT. For repeat known violations, no additional notification is sent. - For new users (no existing ID): the violation is noted in the audit log but no Elasticsearch doc is written and no notification is sent at detection time (the doc is maintained after save via
maintainViolatedSodUserDoc). - After the save completes,
maintainViolatedSodUserDocreconciles the violation tracking document — stale violations (no longer present) are removed from Elasticsearch and the manager group receives aSOD_VIOLATION_RESOLVEDnotification.
Violation resolution notification
When a user's entitlements are updated and a previously recorded SOFT violation no longer applies (e.g., one of the conflicting entitlements was removed), the system:
- Detects that the violation is no longer present during the next save.
- Sends a
SOD_VIOLATION_RESOLVEDnotification to the manager group. - Deletes the violation entry from the ElasticSearch document.
To distinguish between SOFT and HARD violation better, please use the table below.
| Behavior | SOFT | HARD |
|---|---|---|
| Provisioning blocked | No — save proceeds | Yes — save is rejected |
| Exception thrown | No | SodPolicyServiceException |
| Audit action | SOFT_SOD_POLICY_VIOLATION_DETECTION | HARD_SOD_POLICY_VIOLATION_DETECTION |
| Manager group notification | On first (new) violation only | Every time the violation is detected |
| Notification type | SOD_VIOLATION_SOFT | SOD_VIOLATION_HARD |
| Elasticsearch tracking | Yes — recorded in UserViolatedSodPolicyDoc | No (save never completes) |
| Resolution notification | Yes — SOD_VIOLATION_RESOLVED when cleared | N/A |
| Can be skipped | ProvisionUser.skipSodPolicyCheck=true skips both SOFT and HARD checks | Same flag |
Remediation
When a user is found to violate a policy, administrators have two options:
- Remove conflicting access REST endpoint: POST /rest/api/sod-policy-violation/handle-violations (with
isDelete=true) Removes the specified conflicting entitlements from the user via the provisioning service. This is the standard remediation path. - Grant an exemption REST endpoint: POST /rest/api/sod-policy-violation/handle-violations (with
isDelete=false) Creates a formal exemption that allows the user to retain the conflicting entitlements. Requirements and behavior:- The policy must have
exceptionsAllowed=true. - A comment/reason must be provided.
- The exemption records: user ID, policy ID, entitlement type (ROLE, GROUP, RESOURCE, ORGANIZATION), entitlement ID, timestamp, and the ID of the user who granted the exemption.
- Exempted entitlements are excluded from future violation evaluations for that user. Exemptions can be queried per user via the service to support auditing.
- The policy must have
Audit action generated: Logged with user ID, policy ID, entitlement details, and a comment.
Integration with Access Certification
SoD violations are surfaced inside Access Certification reviews. When a manager reviews a user's access during a certification campaign:
- Each access review item is checked against active SoD policies. \
- Violations are displayed as AccessReviewItemSodViolation records attached to the review item.
- Each violation includes: policy name, severity, risk classification, conflicting entitlement details, segment information, and whether it is a direct or indirect violation. - The manager can certify (retain) or revoke access, with SoD context visible for informed decision-making. - Resolved violations are tracked via the resolved flag on the violation record.
Reporting
SoD feature allows reporting on the violations and other activity with the feature involved. The reports are located and can be configured via org.openiam.sod.reports.directory property. The SoD report service generates CSV exports of violations. Reports can be filtered by:
- Specific policy IDs.
- Severity level.
- Risk ID Each row in the report includes: user identity information, entitlement details, violated SoD policies, and associated mitigating controls. The report service uses LRU caching internally to minimize database load for frequently accessed user and entitlement data.
Data model
SodPolicy (SOD_POLICY)├── segments: SodPolicySegment (SOD_POLICY_SEGMENT) [1..N]│ ├── roles → SOD_SEGMENT_ROLE → RoleEntity│ ├── groups → SOD_SEGMENT_GRP → GroupEntity│ ├── resources → SOD_SEGMENT_RES → ResourceEntity│ └── organizations → SOD_SEGMENT_ORG → OrganizationEntity└── mitigatingControls → SOD_POLICY_MC → SodMitigatingControl (SOD_MITIGATING_CONTROL)SodExemption└── references: user, sodPolicy, entitlementType, entitlementId
Boolean flags (active, exceptionsAllowed, managerCanHandleSpv) are stored as Y/N in the database via YesNoConverter.
Caching architecture
- All SoD policies are cached in Redis (
SodPolicyCacheList) for fast access during entitlement evaluations. - The cache is refreshed automatically whenever a policy is created or updated.
- Cache refresh is propagated via RabbitMQ (
SodPolicyMQListener) to all service nodes.
Enumerations Reference
| Enum | Values |
|---|---|
SodSeverity | SOFT, HARD |
SodViolationMode | DIRECT, INDIRECT |
SodMitigatingControlAssigneeType | USER, GROUP |
SodMitigatingControlReviewFrequency | ANNUALLY, SEMI_ANNUALLY, QUARTERLY, MONTHLY |
UserEntitlementType | ROLE, GROUP, RESOURCE, ORGANIZATION |