Sample scenario

Last modified 22 Apr 2021 17:31 +02:00

This is a sample scenario, as covered by TestPolicyDrivenRoleLifecycle class.

Imagine we have the constraints for a role to be met:

Id Constraint

C1

Each role must have a risk level.

C2

Each role with risk level of high must have a description.

C3

Each role must have an approver. High-risk roles must have at least 2 approvers.

C4

Each role must have an owner.

C5

Each role must have an identifier.

Then there are the following rules:

Id Rule

R1

No role that does not meet C1-C4 might be activated i.e. switched to lifecycleState of active.

R2

Activation of a role must be approved by its owner.

R3

Activation of a role without identifier is subject to an approval by Security Administrators

R4

Active role without identifier must be recertified regularly by Security Administrator.

R5

Validity of an active role without identifier must be limited to 180 days at most.

Reports requested:

Id Requirement

RE1

See all draft roles that do not meet constraints C1-C4.

RE2

See all active roles that do not meet constraint C5.

Rules in action

Let’s say we have a role that breaks almost all constraints: has no risk level defined, no approver, no owner, no identifier, no validity set. After trying to activate such role, the following would appear (currently only for preview changes due to implementation limitations):

[ Image missing ]

The message Role test1234 (OID bbb0d3aa-0b2f-4710-80e0-11360c423c37) requires at least 1 assignees with the relation of 'owner' is intentionally different from the others. It is present just to show how default constraint messages look like, in comparison with custom ones.

Policy rules used

This is how it could be implemented.

Policy rules implementing the scenario
<globalPolicyRule>
    <name>basic-constraints-definitions</name>
    <description>
        This is no-action disabled rule that is used to define basic constraints:
        what is an incomplete role (as per C1 to C4), what is a role activation, etc.
        To keep constraints in the logical order we must enclose each in separate "and"
        element. Otherwise the IDE would issue a lot of XSD validation errors
        (because of the ordering prescribed in PolicyConstraintsType definition).
    </description>
    <policyConstraints>
        <and>
            <objectState>
                <name>c1-no-risk-level</name>
                <!-- presentation is not needed, as default key of PolicyConstraint.c1-no-risk-level is used -->
                <filter>
                    <q:equal>
                        <q:path>riskLevel</q:path>
                    </q:equal>
                </filter>
            </objectState>
        </and>
        <and>
            <objectState>
                <!-- could be implemented also as a conjunction of two filter-based objectState constraints -->
                <name>c2-no-description-for-high-risk-role</name>
                <!-- presentation is not needed, as default key of PolicyConstraint.c2-no-description-for-high-risk-role is used -->
                <expression>
                    <script>
                        <code>object.riskLevel == 'high' &amp;&amp; object.description == null</code>
                    </script>
                </expression>
            </objectState>
        </and>
        <and>
            <name>c3-less-than-2-approvers-for-high-risk-role</name>
            <!-- message definition is not needed, as default key of PolicyConstraint.c3-less-than-2-approvers-for-high-risk-role is used -->
            <presentation>
                <final>true</final>        <!-- we don't need to show further details -->
            </presentation>
            <objectState>
                <name>high-risk-role</name>
                <filter>
                    <q:equal>
                        <q:path>riskLevel</q:path>
                        <q:value>high</q:value>
                    </q:equal>
                </filter>
            </objectState>
            <objectMinAssigneesViolation>
                <multiplicity>2</multiplicity>
                <relation>approver</relation>
            </objectMinAssigneesViolation>
        </and>
        <and>
            <name>c3-less-than-1-approver-for-non-high-risk-role</name>
            <!-- message definition is not needed, as default key of PolicyConstraint.c3-less-than-1-approver-for-non-high-risk-role is used -->
            <presentation>
                <final>true</final>
            </presentation>
            <objectState>
                <name>not-high-risk-role</name>
                <filter>
                    <q:not>
                        <q:equal>
                            <q:path>riskLevel</q:path>
                            <q:value>high</q:value>
                        </q:equal>
                    </q:not>
                </filter>
            </objectState>
            <objectMinAssigneesViolation>
                <multiplicity>1</multiplicity>
                <relation>approver</relation>
            </objectMinAssigneesViolation>
        </and>
        <and>
            <objectMinAssigneesViolation>
                <name>c4-no-role-owner</name>
                <!-- PolicyConstraint.c4-no-role-owner is intentionally not defined, to demonstrate the use of the built-in message -->
                <multiplicity>1</multiplicity>
                <relation>owner</relation>
            </objectMinAssigneesViolation>
        </and>
        <and>
            <objectState>
                <name>c5-no-identifier</name>
                <!-- no message definition is needed: a key of PolicyConstraint.c5-no-identifier is used -->
                <!-- situation and expectedUse are set for "active role with no identifier" because that's what we want to certify and report -->
                <presentation>
                    <hidden>true</hidden>
                </presentation>
                <filter>
                    <q:equal>
                        <q:path>identifier</q:path>
                    </q:equal>
                </filter>
            </objectState>
        </and>
        <and>
            <objectState>
                <name>role-active</name>
                <filter>
                    <q:or>
                        <q:equal>
                            <q:path>lifecycleState</q:path>
                            <q:value>active</q:value>
                        </q:equal>
                        <q:equal>
                            <q:path>lifecycleState</q:path>
                        </q:equal>
                    </q:or>
                </filter>
            </objectState>
        </and>
        <and>
            <objectState>
                <name>validity-not-limited</name>
                <presentation>
                    <message>
                        <keyExpression>
                            <script>
                                <code>
                                    import com.evolveum.midpoint.xml.ns._public.common.common_3.*

                                    if (object.activation == null) {
                                        'PolicyConstraint.validity-not-limited.validToNull'
                                    } else if (object.activation.administrativeStatus == ActivationStatusType.ENABLED) {
                                         'PolicyConstraint.validity-not-limited.enabled'
                                    } else if (object.activation.validTo == null) {
                                        'PolicyConstraint.validity-not-limited.validToNull'
                                    } else {
                                        'PolicyConstraint.validity-not-limited.validToNotNull'
                                    }
                                </code>
                            </script>
                        </keyExpression>
                        <argumentExpression>
                            <script>
                                <code>
                                    object.activation?.validTo
                                </code>
                            </script>
                        </argumentExpression>
                    </message>
                </presentation>
                <expression>
                    <script>
                        <code>
                            import com.evolveum.midpoint.xml.ns._public.common.common_3.*
                            import com.evolveum.midpoint.prism.xml.XmlTypeConverter

                            if (object.activation == null) {
                                return true
                            }
                            def state = object.activation.administrativeStatus
                            if (state == ActivationStatusType.ENABLED) {
                                return true
                            } else if (state != null) {
                                return false
                            }
                            def validTo = object.activation.validTo
                            if (validTo == null) {
                                return true
                            }
                            def nowPlus180 = XmlTypeConverter.fromNow(XmlTypeConverter.createDuration("P180D"))
                            return validTo.toGregorianCalendar().compareTo(nowPlus180.toGregorianCalendar()) &gt; 0
                        </code>
                    </script>
                </expression>
            </objectState>
        </and>
        <and>
            <name>active-role-with-no-identifier</name>
            <!-- presentation is not needed, as default key of PolicyConstraint.active-role-with-no-identifier is used -->
            <presentation>
                <final>true</final>
            </presentation>
            <ref>
                <name>role-active</name>
            </ref>
            <ref>
                <name>c5-no-identifier</name>
            </ref>
        </and>
        <and>
            <or>
                <name>incomplete-role-c1-to-c4</name>
                <!-- message definition is not needed, as the default key of PolicyConstraint.incomplete-role-c1-to-c4 is used -->
                <presentation>
                    <hidden>true</hidden>
                </presentation>
                <ref>
                    <name>c1-no-risk-level</name>
                </ref>
                <ref>
                    <name>c2-no-description-for-high-risk-role</name>
                </ref>
                <ref>
                    <name>c3-less-than-1-approver-for-non-high-risk-role</name>
                </ref>
                <ref>
                    <name>c3-less-than-2-approvers-for-high-risk-role</name>
                </ref>
                <ref>
                    <name>c4-no-role-owner</name>
                </ref>
            </or>
        </and>
        <and>
            <transition>
                <name>role-activation</name>
                <presentation>
                    <final>true</final>
                    <hidden>true</hidden>
                </presentation>
                <stateBefore>false</stateBefore>
                <stateAfter>true</stateAfter>
                <constraints>
                    <ref>
                        <name>role-active</name>
                    </ref>
                </constraints>
            </transition>
        </and>
    </policyConstraints>
    <condition>
        <!-- This rule serves as a container for constraints definitions. It is not to be evaluated directly. -->
        <expression>
            <value>false</value>
        </expression>
    </condition>
</globalPolicyRule>
<globalPolicyRule>
    <name>r1-no-activation-of-incomplete-roles</name>
    <description>R1: No role that does not meet C1-C4 might be activated i.e. switched to lifecycleState of active.</description>
    <policyConstraints>
        <name>r1-no-activation-of-incomplete-roles</name>
        <!-- presentation uses PolicyConstraint.r1-no-activation-of-incomplete-roles -->
        <!-- Note: situation is not defined here, as this is a transition-related rule.
             Situation marking incomplete rules is defined in incomplete-role-c1-to-c4 constraint. -->
        <ref>
            <name>incomplete-role-c1-to-c4</name>
        </ref>
        <ref>
            <name>role-activation</name>
        </ref>
    </policyConstraints>
    <policyActions>
        <enforcement/>
    </policyActions>
    <focusSelector>
        <type>RoleType</type>
    </focusSelector>
</globalPolicyRule>
<globalPolicyRule>
    <name>r2-role-activation-approval</name>
    <description>R2: Activation of a role must be approved by its owner.</description>
    <policyConstraints>
        <name>r2-role-activation-approval</name>
        <presentation>
            <final>true</final>
        </presentation>
        <ref>
            <name>role-activation</name>
        </ref>
    </policyConstraints>
    <policyActions>
        <approval>
            <compositionStrategy>
                <order>10</order>
            </compositionStrategy>
            <approvalSchema>
                <stage>
                    <approverRelation>owner</approverRelation>
                </stage>
            </approvalSchema>
        </approval>
    </policyActions>
    <focusSelector>
        <type>RoleType</type>
    </focusSelector>
</globalPolicyRule>
<globalPolicyRule>
    <name>r3-no-identifier-role-activation-approval</name>
    <description>R3: Activation of a role without identifier is subject to an approval by Security Administrators.</description>
    <policyConstraints>
        <name>r3-no-identifier-role-activation-approval</name>
        <presentation>
            <final>true</final>
        </presentation>
        <ref>
            <name>role-activation</name>
        </ref>
        <ref>
            <name>c5-no-identifier</name>
        </ref>
    </policyConstraints>
    <policyActions>
        <approval>
            <compositionStrategy>
                <order>10</order>
            </compositionStrategy>
            <approvalSchema>
                <stage>
                    <approverRef oid="a14afc10-e4a2-48a4-abfd-e8a2399f98d3" type="c:OrgType"/> <!-- Security Administrators -->
                </stage>
            </approvalSchema>
        </approval>
    </policyActions>
    <focusSelector>
        <type>RoleType</type>
    </focusSelector>
</globalPolicyRule>
<globalPolicyRule>
    <name>r5-validity-limitation-for-active-role</name>
    <description>R5: Validity of an active role without identifier must be limited to 180 days at most.</description>
    <policyConstraints>
        <name>r5-validity-limitation-for-active-role</name>
        <and>
            <presentation>
                <final>true</final>
                <hidden>true</hidden>
            </presentation>
            <ref>
                <name>active-role-with-no-identifier</name>
            </ref>
        </and>
        <ref>
            <name>validity-not-limited</name>
        </ref>
    </policyConstraints>
    <policyActions>
        <enforcement/>
    </policyActions>
    <focusSelector>
        <type>RoleType</type>
    </focusSelector>
</globalPolicyRule>
<globalPolicyRule>
    <name>re1-report-draft-roles-violating-c1-c4</name>
    <description>RE1: See all draft roles that do not meet constraints C1-C4.</description>
    <policyConstraints>
        <ref>
            <name>incomplete-role-c1-to-c4</name>
        </ref>
    </policyConstraints>
    <policySituation>http://sample.org/situations#incomplete-role-c1-to-c4</policySituation>
    <policyActions>
        <record>
            <policyRules>full</policyRules>	<!-- assuming we want to be able to display details of the problem -->
        </record>
    </policyActions>
    <focusSelector>
        <type>RoleType</type>
    </focusSelector>
</globalPolicyRule>
<globalPolicyRule>
    <name>re2-report-active-roles-violating-c5</name>
    <description>RE2: See all active roles that do not meet constraint C5.</description>
    <policyConstraints>
        <ref>
            <name>active-role-with-no-identifier</name>
        </ref>
    </policyConstraints>
    <policySituation>http://sample.org/situations#active-role-with-no-identifier</policySituation>
    <policyActions>
        <record>
            <policyRules>none</policyRules>	<!-- assuming we only want the list of roles matching this rule (no details needed) -->
        </record>
    </policyActions>
    <focusSelector>
        <type>RoleType</type>
    </focusSelector>
</globalPolicyRule>

Localization properties:

PolicyConstraint.role-activation=Role is being activated
PolicyConstraint.active-role-with-no-identifier=Active role with no identifier [C5]
PolicyConstraint.incomplete-role-c1-to-c4=Role definition is incomplete as defined in C1-C4
PolicyConstraint.c1-no-risk-level=No risk level defined [C1]
PolicyConstraint.c2-no-description-for-high-risk-role=No description for high risk role [C2]
PolicyConstraint.c3-less-than-2-approvers-for-high-risk-role=Less than 2 approvers for high-risk role [C3]
PolicyConstraint.c3-less-than-1-approver-for-non-high-risk-role=No approver for non-high-risk role [C3]
#PolicyConstraint.c4-no-role-owner=No role owner [C4]     # using default presentation instead
PolicyConstraint.c5-no-identifier=No role identifier [C5]
PolicyConstraint.validity-not-limited.status=Validity is not limited (status is set to 'enabled')
PolicyConstraint.validity-not-limited.validToNull=Validity is not limited (validTo is not set)
PolicyConstraint.validity-not-limited.validToNotNull=Validity is not limited (validTo is set to {0})
PolicyConstraint.r1-no-activation-of-incomplete-roles=No role that does not meet C1-C4 might be activated [R1]
PolicyConstraint.r2-role-activation-approval=Activation of a role must be approved by its owner [R2]
PolicyConstraint.r3-no-identifier-role-activation-approval=Activation of a role without identifier is subject to an approval by Security Administrators [R3]
PolicyConstraint.r5-validity-limitation-for-active-role=Validity of an active role without identifier must be limited to 180 days at most [R5]
Was this page helpful?
YES NO
Thanks for your feedback