Skip to content

Commit

Permalink
Added relation parameter to min/maxAssignees policy constraints (MID-…
Browse files Browse the repository at this point in the history
…3797).
  • Loading branch information
mederly committed Mar 21, 2017
1 parent c2b8db4 commit 65945e9
Show file tree
Hide file tree
Showing 8 changed files with 236 additions and 104 deletions.
Expand Up @@ -627,7 +627,7 @@ public static boolean relationsEquivalent(QName relation1, QName relation2) {
if (ObjectTypeUtil.isDefaultRelation(relation1)) {
return ObjectTypeUtil.isDefaultRelation(relation2);
} else {
return QNameUtil.match(relation2, relation1);
return QNameUtil.match(relation1, relation2);
}
}
}
Expand Up @@ -192,7 +192,7 @@
<xsd:element name="relation" type="xsd:QName" minOccurs="0" maxOccurs="unbounded">
<xsd:annotation>
<xsd:documentation>
Relation(s) to which this constraint applies. All of these relation must match
Relation(s) to which this constraint applies. All of these relations must match
the defined multiplicity. If no relation is present, org:default (i.e. null) is assumed.
</xsd:documentation>
</xsd:annotation>
Expand Down
Expand Up @@ -308,59 +308,101 @@ private <F extends FocusType> void checkAssigneeConstraints(LensContext<F> conte

private <F extends FocusType> void checkAssigneeConstraints(LensContext<F> context, EvaluatedAssignment<F> assignment, PlusMinusZero plusMinus, OperationResult result) throws PolicyViolationException, SchemaException {
PrismObject<?> target = assignment.getTarget();
if (target != null) {
Objectable targetType = target.asObjectable();
if (targetType instanceof AbstractRoleType) {
Collection<EvaluatedPolicyRule> policyRules = assignment.getThisTargetPolicyRules();
for (EvaluatedPolicyRule policyRule: policyRules) {
PolicyConstraintsType policyConstraints = policyRule.getPolicyConstraints();
if (policyConstraints != null && (!policyConstraints.getMinAssignees().isEmpty() || !policyConstraints.getMaxAssignees().isEmpty())) {
String focusOid = null;
if (context.getFocusContext() != null) {
focusOid = context.getFocusContext().getOid();
}
int numberOfAssigneesExceptMyself = countAssignees((PrismObject<? extends AbstractRoleType>)target, focusOid, result);
if (plusMinus == PlusMinusZero.PLUS) {
numberOfAssigneesExceptMyself++;
}
for (MultiplicityPolicyConstraintType constraint: policyConstraints.getMinAssignees()) {
Integer multiplicity = XsdTypeMapper.multiplicityToInteger(constraint.getMultiplicity());
// Complain only if the situation is getting worse
if (multiplicity >= 0 && numberOfAssigneesExceptMyself < multiplicity && plusMinus == PlusMinusZero.MINUS) {
EvaluatedPolicyRuleTrigger trigger = new EvaluatedPolicyRuleTrigger(PolicyConstraintKindType.MIN_ASSIGNEES,
constraint, ""+target+" requires at least "+multiplicity+
" assignees. The operation would result in "+numberOfAssigneesExceptMyself+" assignees.");
assignment.triggerConstraint(policyRule, trigger);

}
}
for (MultiplicityPolicyConstraintType constraint: policyConstraints.getMaxAssignees()) {
Integer multiplicity = XsdTypeMapper.multiplicityToInteger(constraint.getMultiplicity());
// Complain only if the situation is getting worse
if (multiplicity >= 0 && numberOfAssigneesExceptMyself > multiplicity && plusMinus == PlusMinusZero.PLUS) {
EvaluatedPolicyRuleTrigger trigger = new EvaluatedPolicyRuleTrigger(PolicyConstraintKindType.MAX_ASSIGNEES,
constraint, ""+target+" requires at most "+multiplicity+
" assignees. The operation would result in "+numberOfAssigneesExceptMyself+" assignees.");
assignment.triggerConstraint(policyRule, trigger);

}
}
}
if (target == null || !(target.asObjectable() instanceof AbstractRoleType)) {
return;
}
AbstractRoleType targetRole = (AbstractRoleType) target.asObjectable();
QName relation = ObjectTypeUtil.normalizeRelation(assignment.getRelation());
Collection<EvaluatedPolicyRule> policyRules = assignment.getThisTargetPolicyRules();
for (EvaluatedPolicyRule policyRule: policyRules) {
PolicyConstraintsType policyConstraints = policyRule.getPolicyConstraints();
if (policyConstraints == null) {
continue;
}
List<MultiplicityPolicyConstraintType> relevantMinAssignees = getForRelation(policyConstraints.getMinAssignees(), relation);
List<MultiplicityPolicyConstraintType> relevantMaxAssignees = getForRelation(policyConstraints.getMaxAssignees(), relation);
if (relevantMinAssignees.isEmpty() && relevantMaxAssignees.isEmpty()) {
continue;
}
String focusOid = null;
if (context.getFocusContext() != null) {
focusOid = context.getFocusContext().getOid();
}
int currentAssigneesExceptMyself = getNumberOfAssigneesExceptMyself(targetRole, focusOid, relation, result);
for (MultiplicityPolicyConstraintType constraint: relevantMinAssignees) {
Integer requiredMultiplicity = XsdTypeMapper.multiplicityToInteger(constraint.getMultiplicity());
if (requiredMultiplicity <= 0) {
continue; // unbounded or 0
}
// Complain only if the situation is getting worse
if (currentAssigneesExceptMyself < requiredMultiplicity && plusMinus == PlusMinusZero.MINUS) {
EvaluatedPolicyRuleTrigger<MultiplicityPolicyConstraintType> trigger =
new EvaluatedPolicyRuleTrigger<>(PolicyConstraintKindType.MIN_ASSIGNEES,
constraint, target+" requires at least " + requiredMultiplicity
+ " assignees with the relation of '" + relation.getLocalPart()
+ "'. The operation would result in "+currentAssigneesExceptMyself+" assignees.");
assignment.triggerConstraint(policyRule, trigger);
}
}
for (MultiplicityPolicyConstraintType constraint: relevantMaxAssignees) {
Integer requiredMultiplicity = XsdTypeMapper.multiplicityToInteger(constraint.getMultiplicity());
if (requiredMultiplicity < 0) {
continue; // unbounded
}
// Complain only if the situation is getting worse
if (currentAssigneesExceptMyself >= requiredMultiplicity && plusMinus == PlusMinusZero.PLUS) {
EvaluatedPolicyRuleTrigger<MultiplicityPolicyConstraintType> trigger =
new EvaluatedPolicyRuleTrigger<>(PolicyConstraintKindType.MAX_ASSIGNEES,
constraint, target + " requires at most " + requiredMultiplicity +
" assignees with the relation of '" + relation.getLocalPart()
+ "'. The operation would result in " + (currentAssigneesExceptMyself+1) + " assignees.");
assignment.triggerConstraint(policyRule, trigger);
}
}
}
}

private int countAssignees(PrismObject<? extends AbstractRoleType> target, String selfOid, OperationResult result) throws SchemaException {
private List<MultiplicityPolicyConstraintType> getForRelation(List<MultiplicityPolicyConstraintType> all, QName relation) {
return all.stream()
.filter(c -> containsRelation(c, relation))
.collect(Collectors.toList());
}

private boolean containsRelation(MultiplicityPolicyConstraintType constraint, QName relation) {
return getConstraintRelations(constraint).stream()
.anyMatch(constraintRelation -> ObjectTypeUtil.relationMatches(constraintRelation, relation));
}

private List<QName> getConstraintRelations(MultiplicityPolicyConstraintType constraint) {
return !constraint.getRelation().isEmpty() ?
constraint.getRelation() :
Collections.singletonList(SchemaConstants.ORG_DEFAULT);
}

/**
* Returns numbers of assignees with the given relation name.
*/
private int getNumberOfAssigneesExceptMyself(AbstractRoleType target, String selfOid, QName relation, OperationResult result)
throws SchemaException {
S_AtomicFilterExit q = QueryBuilder.queryFor(FocusType.class, prismContext)
.item(FocusType.F_ASSIGNMENT, AssignmentType.F_TARGET_REF).ref(target.getOid());
if (selfOid != null) {
q = q.and().not().id(selfOid);
}
ObjectQuery query = q.build();
return repositoryService.countObjects(FocusType.class, query, result);
List<PrismObject<FocusType>> assignees = repositoryService.searchObjects(FocusType.class, query, null, result);
int count = 0;
assignee: for (PrismObject<FocusType> assignee : assignees) {
for (AssignmentType assignment : assignee.asObjectable().getAssignment()) {
if (assignment.getTargetRef() != null
&& ObjectTypeUtil.relationsEquivalent(relation, assignment.getTargetRef().getRelation())) {
count++;
continue assignee;
}
}
}
return count;
}


public <F extends FocusType> boolean processPruning(LensContext<F> context,
DeltaSetTriple<EvaluatedAssignmentImpl<F>> evaluatedAssignmentTriple,
Expand Down
Expand Up @@ -29,6 +29,7 @@
import javax.xml.datatype.XMLGregorianCalendar;
import javax.xml.namespace.QName;

import com.evolveum.midpoint.util.QNameUtil;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.annotation.DirtiesContext.ClassMode;
import org.springframework.test.context.ContextConfiguration;
Expand All @@ -41,7 +42,6 @@
import com.evolveum.midpoint.model.api.context.EvaluatedAssignmentTarget;
import com.evolveum.midpoint.model.api.context.ModelContext;
import com.evolveum.midpoint.model.intest.AbstractInitializedModelIntegrationTest;
import com.evolveum.midpoint.prism.Objectable;
import com.evolveum.midpoint.prism.PrismContainer;
import com.evolveum.midpoint.prism.PrismObject;
import com.evolveum.midpoint.prism.PrismProperty;
Expand Down Expand Up @@ -212,6 +212,8 @@ public void initSystem(Task initTask, OperationResult initResult)
repoAddObjectFromFile(ROLE_META_FOOL_FILE, RoleType.class, initResult);
repoAddObjectFromFile(ROLE_BLOODY_FOOL_FILE, RoleType.class, initResult);

repoAddObjectFromFile(USER_RAPP_FILE, initResult);

dummyResourceCtl.addGroup(GROUP_FOOLS_NAME);
dummyResourceCtl.addGroup(GROUP_SIMPLETONS_NAME);

Expand Down Expand Up @@ -1844,6 +1846,43 @@ public void test612JackAssignRoleGovernor() throws Exception {
assertAssignees(ROLE_GOVERNOR_OID, 1);
}

/**
* Governor has maxAssignees=0 for 'approver'
*/
@Test
public void test613JackAssignRoleGovernorAsApprover() throws Exception {

if (!testMultiplicityConstraintsForNonDefaultRelations()) {
return;
}

final String TEST_NAME = "test613JackAssignRoleGovernorAsApprover";
TestUtil.displayTestTile(this, TEST_NAME);
assumeAssignmentPolicy(AssignmentPolicyEnforcementType.RELATIVE);

Task task = taskManager.createTaskInstance(TestRbac.class.getName() + "." + TEST_NAME);
OperationResult result = task.getResult();

try {
// WHEN
assignRole(USER_JACK_OID, ROLE_GOVERNOR_OID, SchemaConstants.ORG_APPROVER, task, result);

AssertJUnit.fail("Unexpected success");
} catch (PolicyViolationException e) {
// this is expected
display("Expected exception", e);
}

// THEN
TestUtil.displayThen(TEST_NAME);
result.computeStatus();
TestUtil.assertFailure(result);

assertNoAssignments(USER_JACK_OID);

assertAssignees(ROLE_GOVERNOR_OID, 1);
}

/**
* Role cannibal has minAssignees=2. It is assigned to nobody. Even though assigning
* it to lemonhead would result in assignees=1 which violates the policy, the assignment
Expand Down Expand Up @@ -2043,7 +2082,73 @@ public void test628RedskullUnassignRoleCanibal() throws Exception {
assertAssignees(ROLE_CANNIBAL_OID, 2);
assertAssignees(ROLE_GOVERNOR_OID, 1);
}


@Test
public void test630RappAssignRoleCanibalAsOwner() throws Exception {

if (!testMultiplicityConstraintsForNonDefaultRelations()) {
return;
}

final String TEST_NAME = "test630RappAssignRoleCanibalAsOwner";
TestUtil.displayTestTile(this, TEST_NAME);
assumeAssignmentPolicy(AssignmentPolicyEnforcementType.RELATIVE);

Task task = taskManager.createTaskInstance(TestRbac.class.getName() + "." + TEST_NAME);
OperationResult result = task.getResult();

assertAssignees(ROLE_CANNIBAL_OID, 2);

// WHEN
assignRole(USER_RAPP_OID, ROLE_CANNIBAL_OID, SchemaConstants.ORG_OWNER, task, result);

// THEN
TestUtil.displayThen(TEST_NAME);
result.computeStatus();
TestUtil.assertSuccess(result);

assertAssignees(ROLE_CANNIBAL_OID, 2);
assertAssignees(ROLE_CANNIBAL_OID, SchemaConstants.ORG_OWNER, 1);
}

@Test
public void test632RappUnassignRoleCanibalAsOwner() throws Exception {

if (!testMultiplicityConstraintsForNonDefaultRelations()) {
return;
}

final String TEST_NAME = "test632RappUnassignRoleCanibalAsOwner";
TestUtil.displayTestTile(this, TEST_NAME);
assumeAssignmentPolicy(AssignmentPolicyEnforcementType.RELATIVE);

Task task = taskManager.createTaskInstance(TestRbac.class.getName() + "." + TEST_NAME);
OperationResult result = task.getResult();

assertAssignees(ROLE_CANNIBAL_OID, 2);
assertAssignees(ROLE_CANNIBAL_OID, SchemaConstants.ORG_OWNER, 1);

try {
// WHEN
TestUtil.displayWhen(TEST_NAME);
// null namespace to test no-namespace "approver" relation
unassignRole(USER_RAPP_OID, ROLE_CANNIBAL_OID, QNameUtil.nullNamespace(SchemaConstants.ORG_OWNER), task, result);

AssertJUnit.fail("Unexpected success");
} catch (PolicyViolationException e) {
// this is expected
display("Expected exception", e);
}

// THEN
TestUtil.displayThen(TEST_NAME);
result.computeStatus();
TestUtil.assertFailure(result);

assertAssignees(ROLE_CANNIBAL_OID, 2);
assertAssignees(ROLE_CANNIBAL_OID, SchemaConstants.ORG_OWNER, 1);
}

@Test
public void test649ElaineUnassignRoleGovernor() throws Exception {
final String TEST_NAME = "test649ElaineUnassignRoleGovernor";
Expand Down Expand Up @@ -4110,4 +4215,7 @@ public void test857JackReconcile() throws Exception {

}

protected boolean testMultiplicityConstraintsForNonDefaultRelations() {
return true;
}
}
Expand Up @@ -15,64 +15,11 @@
*/
package com.evolveum.midpoint.model.intest.rbac;

import static org.testng.AssertJUnit.assertNotNull;
import static org.testng.AssertJUnit.assertNull;
import static com.evolveum.midpoint.test.IntegrationTestTools.display;
import static org.testng.AssertJUnit.assertEquals;
import static org.testng.AssertJUnit.assertTrue;

import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import javax.xml.namespace.QName;

import com.evolveum.midpoint.prism.query.builder.QueryBuilder;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.annotation.DirtiesContext.ClassMode;
import org.springframework.test.context.ContextConfiguration;
import org.testng.AssertJUnit;
import org.testng.annotations.Test;

import com.evolveum.icf.dummy.resource.DummyAccount;
import com.evolveum.midpoint.model.api.context.EvaluatedAssignmentTarget;
import com.evolveum.midpoint.model.api.context.EvaluatedAssignment;
import com.evolveum.midpoint.model.api.context.ModelContext;
import com.evolveum.midpoint.model.intest.AbstractInitializedModelIntegrationTest;
import com.evolveum.midpoint.prism.PrismContainer;
import com.evolveum.midpoint.prism.PrismObject;
import com.evolveum.midpoint.prism.PrismProperty;
import com.evolveum.midpoint.prism.PrismPropertyDefinition;
import com.evolveum.midpoint.prism.delta.DeltaSetTriple;
import com.evolveum.midpoint.prism.delta.ItemDelta;
import com.evolveum.midpoint.prism.delta.ObjectDelta;
import com.evolveum.midpoint.prism.path.IdItemPathSegment;
import com.evolveum.midpoint.prism.path.ItemPath;
import com.evolveum.midpoint.prism.path.NameItemPathSegment;
import com.evolveum.midpoint.prism.query.EqualFilter;
import com.evolveum.midpoint.prism.query.ObjectFilter;
import com.evolveum.midpoint.prism.query.ObjectQuery;
import com.evolveum.midpoint.prism.schema.PrismSchema;
import com.evolveum.midpoint.prism.util.PrismAsserts;
import com.evolveum.midpoint.prism.util.PrismTestUtil;
import com.evolveum.midpoint.schema.constants.SchemaConstants;
import com.evolveum.midpoint.schema.result.OperationResult;
import com.evolveum.midpoint.schema.util.MiscSchemaUtil;
import com.evolveum.midpoint.task.api.Task;
import com.evolveum.midpoint.test.DummyResourceContoller;
import com.evolveum.midpoint.test.IntegrationTestTools;
import com.evolveum.midpoint.test.util.TestUtil;
import com.evolveum.midpoint.util.DOMUtil;
import com.evolveum.midpoint.util.exception.PolicyViolationException;
import com.evolveum.midpoint.xml.ns._public.common.common_3.AssignmentPolicyEnforcementType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.AssignmentType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectReferenceType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.RoleType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.ShadowType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.UserType;
import com.evolveum.prism.xml.ns._public.types_3.EvaluationTimeType;
import java.io.File;

/**
* @author semancik
Expand All @@ -89,8 +36,14 @@ public class TestRbacDeprecated extends TestRbac {
protected File getRoleGovernorFile() {
return ROLE_GOVERNOR_DEPRECATED_FILE;
}


@Override
protected File getRoleCannibalFile() {
return ROLE_CANNIBAL_DEPRECATED_FILE;
}

@Override
protected boolean testMultiplicityConstraintsForNonDefaultRelations() {
return false;
}
}

0 comments on commit 65945e9

Please sign in to comment.