From cfc972da540cc74570f41eb98f97fa387007227a Mon Sep 17 00:00:00 2001 From: Radovan Semancik Date: Thu, 13 Sep 2018 17:47:05 +0200 Subject: [PATCH] Support for tenantRef for resources and tasks (MID-4866) --- .../projector/focus/AssignmentProcessor.java | 71 ++++++++++++++-- .../lens/projector/focus/FocusProcessor.java | 21 +++-- .../intest/security/AbstractSecurityTest.java | 12 ++- .../security/TestSecurityMultitenant.java | 83 +++++++++++++++++++ .../src/test/resources/logback-test.xml | 2 +- .../multitenant/resource-dummy-barony.xml | 44 ++++++++++ .../resource-dummy-castle-caladan.xml | 44 ++++++++++ .../multitenant/resource-dummy-junction.xml | 44 ++++++++++ 8 files changed, 306 insertions(+), 15 deletions(-) create mode 100644 model/model-intest/src/test/resources/security/multitenant/resource-dummy-barony.xml create mode 100644 model/model-intest/src/test/resources/security/multitenant/resource-dummy-castle-caladan.xml create mode 100644 model/model-intest/src/test/resources/security/multitenant/resource-dummy-junction.xml diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/focus/AssignmentProcessor.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/focus/AssignmentProcessor.java index 49a9c2936d5..dc765229f9b 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/focus/AssignmentProcessor.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/focus/AssignmentProcessor.java @@ -740,7 +740,7 @@ private void createAssignmentDelta(LensContext context, } - public void processOrgAssignments(LensContext context, OperationResult result) throws SchemaException, PolicyViolationException { + public void processOrgAssignments(LensContext context, Task task, OperationResult result) throws SchemaException, PolicyViolationException, ObjectNotFoundException, CommunicationException, ConfigurationException, SecurityViolationException, ExpressionEvaluationException { LensFocusContext focusContext = context.getFocusContext(); if (focusContext == null) { @@ -797,10 +797,10 @@ public void processOrgAssignments(LensContext context, } } - computeTenantRef(context, result); + computeTenantRef(context, task, result); } - private void computeTenantRef(LensContext context, OperationResult result) throws PolicyViolationException, SchemaException { + private void computeTenantRef(LensContext context, Task task, OperationResult result) throws PolicyViolationException, SchemaException, ObjectNotFoundException, CommunicationException, ConfigurationException, SecurityViolationException, ExpressionEvaluationException { String tenantOid = null; LensFocusContext focusContext = context.getFocusContext(); PrismObject objectNew = focusContext.getObjectNew(); @@ -831,9 +831,68 @@ private void computeTenantRef(LensContext context, Ope } } } + + if (tenantOid == null && (objectNew.canRepresent(ResourceType.class) || objectNew.canRepresent(TaskType.class))) { + // This is somehow "future legacy" code. It will be removed later when we have better support for organizational structure + // membership in resources and tasks. + String desc = "parentOrgRef in "+objectNew; + for (ObjectReferenceType parentOrgRef: objectNew.asObjectable().getParentOrgRef()) { + OrgType parentOrg = objectResolver.resolve(parentOrgRef, OrgType.class, null, desc, task, result); + ObjectReferenceType parentTenantRef = parentOrg.getTenantRef(); + if (parentTenantRef == null || parentTenantRef.getOid() == null) { + continue; + } + if (tenantOid == null) { + tenantOid = parentTenantRef.getOid(); + } else { + if (!parentTenantRef.getOid().equals(tenantOid)) { + throw new PolicyViolationException("Two different tenants ("+tenantOid+", "+parentTenantRef.getOid()+") applicable to "+context.getFocusContext().getHumanReadableName()); + } + } + } + } } + addTenantRefDelta(context, objectNew, tenantOid); + } + + /** + * This is somehow "future legacy" code. It will be removed later when we have better support for organizational structure + * membership in resources and tasks. + */ + public void computeTenantRefLegacy(LensContext context, Task task, OperationResult result) throws PolicyViolationException, SchemaException, ObjectNotFoundException, CommunicationException, ConfigurationException, SecurityViolationException, ExpressionEvaluationException { + String tenantOid = null; + LensFocusContext focusContext = context.getFocusContext(); + PrismObject objectNew = focusContext.getObjectNew(); + if (objectNew == null) { + return; + } + if (!objectNew.canRepresent(ResourceType.class) && !objectNew.canRepresent(TaskType.class)) { + return; + } + + String desc = "parentOrgRef in "+objectNew; + for (ObjectReferenceType parentOrgRef: objectNew.asObjectable().getParentOrgRef()) { + OrgType parentOrg = objectResolver.resolve(parentOrgRef, OrgType.class, null, desc, task, result); + ObjectReferenceType parentTenantRef = parentOrg.getTenantRef(); + if (parentTenantRef == null || parentTenantRef.getOid() == null) { + continue; + } + if (tenantOid == null) { + tenantOid = parentTenantRef.getOid(); + } else { + if (!parentTenantRef.getOid().equals(tenantOid)) { + throw new PolicyViolationException("Two different tenants ("+tenantOid+", "+parentTenantRef.getOid()+") applicable to "+context.getFocusContext().getHumanReadableName()); + } + } + } + + addTenantRefDelta(context, objectNew, tenantOid); + } + + private void addTenantRefDelta(LensContext context, PrismObject objectNew, String tenantOid) throws SchemaException { + LensFocusContext focusContext = context.getFocusContext(); ObjectReferenceType currentTenantRef = objectNew.asObjectable().getTenantRef(); if (currentTenantRef == null) { if (tenantOid == null) { @@ -841,18 +900,18 @@ private void computeTenantRef(LensContext context, Ope } else { LOGGER.trace("Setting tenantRef to {}", tenantOid); ReferenceDelta tenantRefDelta = ReferenceDelta.createModificationReplace(ObjectType.F_TENANT_REF, focusContext.getObjectDefinition(), tenantOid); - context.getFocusContext().swallowToProjectionWaveSecondaryDelta(tenantRefDelta); + focusContext.swallowToProjectionWaveSecondaryDelta(tenantRefDelta); } } else { if (tenantOid == null) { LOGGER.trace("Clearing tenantRef"); ReferenceDelta tenantRefDelta = ReferenceDelta.createModificationReplace(ObjectType.F_TENANT_REF, focusContext.getObjectDefinition(), (PrismReferenceValue)null); - context.getFocusContext().swallowToProjectionWaveSecondaryDelta(tenantRefDelta); + focusContext.swallowToProjectionWaveSecondaryDelta(tenantRefDelta); } else { if (!tenantOid.equals(currentTenantRef.getOid())) { LOGGER.trace("Changing tenantRef to {}", tenantOid); ReferenceDelta tenantRefDelta = ReferenceDelta.createModificationReplace(ObjectType.F_TENANT_REF, focusContext.getObjectDefinition(), tenantOid); - context.getFocusContext().swallowToProjectionWaveSecondaryDelta(tenantRefDelta); + focusContext.swallowToProjectionWaveSecondaryDelta(tenantRefDelta); } } } diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/focus/FocusProcessor.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/focus/FocusProcessor.java index eb259f33524..02eb17e4339 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/focus/FocusProcessor.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/focus/FocusProcessor.java @@ -118,14 +118,23 @@ public void processFocus(LensContext return; } - if (!FocusType.class.isAssignableFrom(focusContext.getObjectTypeClass())) { - // We can do this only for FocusType objects. - return; + if (FocusType.class.isAssignableFrom(focusContext.getObjectTypeClass())) { + processFocusFocus((LensContext)context, activityDescription, now, task, result); + } else { + processFocusNonFocus(context, activityDescription, now, task, result); } - - processFocusFocus((LensContext)context, activityDescription, now, task, result); } + private void processFocusNonFocus(LensContext context, String activityDescription, + XMLGregorianCalendar now, Task task, OperationResult result) + throws ObjectNotFoundException, SchemaException, ExpressionEvaluationException, PolicyViolationException, + ObjectAlreadyExistsException, CommunicationException, ConfigurationException, SecurityViolationException, PreconditionViolationException { + // This is somehow "future legacy" code. It will be removed later when we have better support for organizational structure + // membership in resources and tasks. + assignmentProcessor.computeTenantRefLegacy(context, task, result); + + } + private void processFocusFocus(LensContext context, String activityDescription, XMLGregorianCalendar now, Task task, OperationResult result) throws ObjectNotFoundException, SchemaException, ExpressionEvaluationException, PolicyViolationException, @@ -233,7 +242,7 @@ private void processFocusFocus(LensContext context, Str partialProcessingOptions::getAssignments); medic.partialExecute("assignmentsOrg", - () -> assignmentProcessor.processOrgAssignments(context, result), + () -> assignmentProcessor.processOrgAssignments(context, task, result), partialProcessingOptions::getAssignmentsOrg); diff --git a/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/security/AbstractSecurityTest.java b/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/security/AbstractSecurityTest.java index c6dd4cd89e0..680d14f3504 100644 --- a/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/security/AbstractSecurityTest.java +++ b/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/security/AbstractSecurityTest.java @@ -1030,9 +1030,13 @@ protected void assertAddDenyRaw(File file) throws ObjectAlreadyExistsException, } protected void assertAddDeny(File file, ModelExecuteOptions options) throws ObjectAlreadyExistsException, ObjectNotFoundException, SchemaException, ExpressionEvaluationException, CommunicationException, ConfigurationException, PolicyViolationException, IOException { + PrismObject object = PrismTestUtil.parseObject(file); + assertAddDeny(object, options); + } + + protected void assertAddDeny(PrismObject object, ModelExecuteOptions options) throws ObjectAlreadyExistsException, ObjectNotFoundException, SchemaException, ExpressionEvaluationException, CommunicationException, ConfigurationException, PolicyViolationException, IOException { Task task = taskManager.createTaskInstance(AbstractSecurityTest.class.getName() + ".assertAddDeny"); OperationResult result = task.getResult(); - PrismObject object = PrismTestUtil.parseObject(file); ObjectDelta addDelta = object.createAddDelta(); try { logAttempt("add", object.getCompileTimeClass(), object.getOid(), null); @@ -1051,9 +1055,13 @@ protected void assertAddAllow(File file) throws ObjectAlreadyExistsException, Ob } protected void assertAddAllow(File file, ModelExecuteOptions options) throws ObjectAlreadyExistsException, ObjectNotFoundException, SchemaException, ExpressionEvaluationException, CommunicationException, ConfigurationException, PolicyViolationException, SecurityViolationException, IOException { + PrismObject object = PrismTestUtil.parseObject(file); + assertAddAllow(object, options); + } + + protected void assertAddAllow(PrismObject object, ModelExecuteOptions options) throws ObjectAlreadyExistsException, ObjectNotFoundException, SchemaException, ExpressionEvaluationException, CommunicationException, ConfigurationException, PolicyViolationException, SecurityViolationException, IOException { Task task = taskManager.createTaskInstance(AbstractSecurityTest.class.getName() + ".assertAddAllow"); OperationResult result = task.getResult(); - PrismObject object = PrismTestUtil.parseObject(file); ObjectDelta addDelta = object.createAddDelta(); logAttempt("add", object.getCompileTimeClass(), object.getOid(), null); try { diff --git a/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/security/TestSecurityMultitenant.java b/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/security/TestSecurityMultitenant.java index e54aaa26dd1..87b46b42313 100644 --- a/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/security/TestSecurityMultitenant.java +++ b/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/security/TestSecurityMultitenant.java @@ -16,19 +16,32 @@ package com.evolveum.midpoint.model.intest.security; import java.io.File; +import java.io.IOException; import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.annotation.DirtiesContext.ClassMode; import org.springframework.test.context.ContextConfiguration; import org.testng.annotations.Test; +import com.evolveum.midpoint.prism.PrismObject; +import com.evolveum.midpoint.prism.util.PrismTestUtil; 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.util.exception.CommunicationException; +import com.evolveum.midpoint.util.exception.ConfigurationException; +import com.evolveum.midpoint.util.exception.ExpressionEvaluationException; +import com.evolveum.midpoint.util.exception.ObjectAlreadyExistsException; +import com.evolveum.midpoint.util.exception.ObjectNotFoundException; +import com.evolveum.midpoint.util.exception.PolicyViolationException; +import com.evolveum.midpoint.util.exception.SchemaException; +import com.evolveum.midpoint.util.exception.SecurityViolationException; import com.evolveum.midpoint.xml.ns._public.common.api_types_3.ImportOptionsType; import com.evolveum.midpoint.xml.ns._public.common.common_3.AssignmentPolicyEnforcementType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.ConnectorType; import com.evolveum.midpoint.xml.ns._public.common.common_3.ModelExecuteOptionsType; import com.evolveum.midpoint.xml.ns._public.common.common_3.OrgType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.ResourceType; import com.evolveum.midpoint.xml.ns._public.common.common_3.RoleType; import com.evolveum.midpoint.xml.ns._public.common.common_3.UserType; @@ -48,6 +61,9 @@ public class TestSecurityMultitenant extends AbstractSecurityTest { protected static final String ORG_ROOT_OID = "00000000-8888-6666-a000-000000000000"; protected static final String ROLE_TENANT_ADMIN_OID = "00000000-8888-6666-a000-100000000000"; + + // ===[ Spacing Guild ]=== + protected static final String ORG_GUILD_OID = "00000000-8888-6666-a001-000000000000"; protected static final String ROLE_GUILD_BROKEN_ADMIN_OID = "00000000-8888-6666-a001-100000000001"; @@ -61,6 +77,12 @@ public class TestSecurityMultitenant extends AbstractSecurityTest { protected static final String USER_DMURR_NAME = "dmurr"; protected static final String USER_DMURR_FULL_NAME = "D'murr Pilru"; + private static final File RESOURCE_DUMMY_JUNCTION_FILE = new File(TEST_DIR, "resource-dummy-junction.xml"); + private static final String RESOURCE_DUMMY_JUNCTION_OID = "00000000-8888-6666-a001-300000000000"; + + + // ===[ House Corrino ]=== + protected static final String ORG_CORRINO_OID = "00000000-8888-6666-a100-000000000000"; protected static final String ROLE_CORRINO_ADMIN_OID = "00000000-8888-6666-a100-100000000000"; @@ -69,6 +91,9 @@ public class TestSecurityMultitenant extends AbstractSecurityTest { protected static final String USER_SHADDAM_CORRINO_NAME = "shaddam"; protected static final String USER_SHADDAM_CORRINO_FULL_NAME = "Padishah Emperor Shaddam IV"; + + // ===[ House Atreides ]=== + protected static final String ORG_ATREIDES_OID = "00000000-8888-6666-a200-000000000000"; protected static final String ROLE_ATREIDES_ADMIN_OID = "00000000-8888-6666-a200-100000000000"; @@ -86,6 +111,11 @@ public class TestSecurityMultitenant extends AbstractSecurityTest { protected static final String USER_DUNCAN_NAME = "duncan"; protected static final String USER_DUNCAN_FULL_NAME = "Duncan Idaho"; + private static final File RESOURCE_DUMMY_CASTLE_CALADAN_FILE = new File(TEST_DIR, "resource-dummy-castle-caladan.xml"); + private static final String RESOURCE_DUMMY_CASTLE_CALADAN_OID = "00000000-8888-6666-a200-300000000000"; + + // ===[ House Harkonnen ]=== + protected static final String ORG_HARKONNEN_OID = "00000000-8888-6666-a300-000000000000"; protected static final String ROLE_HARKONNEN_ADMIN_OID = "00000000-8888-6666-a300-100000000000"; @@ -97,12 +127,18 @@ public class TestSecurityMultitenant extends AbstractSecurityTest { protected static final String USER_PITER_NAME = "piter"; protected static final String USER_PITER_FULL_NAME = "Piter De Vries"; + private static final File RESOURCE_DUMMY_BARONY_FILE = new File(TEST_DIR, "resource-dummy-barony.xml"); + private static final String RESOURCE_DUMMY_BARONY_OID = "00000000-8888-6666-a300-300000000000"; + + protected PrismObject dummyConnector; + @Override public void initSystem(Task initTask, OperationResult initResult) throws Exception { super.initSystem(initTask, initResult); assumeAssignmentPolicy(AssignmentPolicyEnforcementType.RELATIVE); + dummyConnector = findConnectorByTypeAndVersion(CONNECTOR_DUMMY_TYPE, CONNECTOR_DUMMY_VERSION, initResult); } @Override @@ -116,6 +152,7 @@ protected String getTopOrgOid() { } protected static final int NUMBER_OF_IMPORTED_ROLES = 0; + protected int getNumberOfRoles() { return super.getNumberOfRoles() + NUMBER_OF_IMPORTED_ROLES; @@ -357,6 +394,52 @@ public void test104AutzLetoModify() throws Exception { assertGlobalStateUntouched(); } + /** + * MID-4882 + */ + @Test + public void test106AutzLetoAddResourceTask() throws Exception { + final String TEST_NAME = "test106AutzLetoAddResourceTask"; + displayTestTitle(TEST_NAME); + // GIVEN + cleanupAutzTest(null); + + login(USER_LETO_ATREIDES_NAME); + + // WHEN + displayWhen(TEST_NAME); + + // Matching tenant + assertAddDummyResourceAllow(RESOURCE_DUMMY_CASTLE_CALADAN_FILE); + + // Wrong tenant + assertAddDummyResourceDeny(RESOURCE_DUMMY_BARONY_FILE); + + // No tenant + assertAddDummyResourceDeny(RESOURCE_DUMMY_JUNCTION_FILE); + + // THEN + displayThen(TEST_NAME); + + login(USER_ADMINISTRATOR_USERNAME); + + assertGlobalStateUntouched(); + } + + private void assertAddDummyResourceAllow(File file) throws SchemaException, IOException, ObjectAlreadyExistsException, ObjectNotFoundException, ExpressionEvaluationException, CommunicationException, ConfigurationException, PolicyViolationException, SecurityViolationException { + PrismObject resource = PrismTestUtil.parseObject(file); + resource.asObjectable() + .connectorRef(dummyConnector.getOid(), ConnectorType.COMPLEX_TYPE); + assertAddAllow(resource, null); + } + + private void assertAddDummyResourceDeny(File file) throws SchemaException, IOException, ObjectAlreadyExistsException, ObjectNotFoundException, ExpressionEvaluationException, CommunicationException, ConfigurationException, PolicyViolationException, SecurityViolationException { + PrismObject resource = PrismTestUtil.parseObject(file); + resource.asObjectable() + .connectorRef(dummyConnector.getOid(), ConnectorType.COMPLEX_TYPE); + assertAddDeny(resource, null); + } + /** * MID-4882 */ diff --git a/model/model-intest/src/test/resources/logback-test.xml b/model/model-intest/src/test/resources/logback-test.xml index 5259eeb0957..c3e658996cf 100644 --- a/model/model-intest/src/test/resources/logback-test.xml +++ b/model/model-intest/src/test/resources/logback-test.xml @@ -97,7 +97,7 @@ - + diff --git a/model/model-intest/src/test/resources/security/multitenant/resource-dummy-barony.xml b/model/model-intest/src/test/resources/security/multitenant/resource-dummy-barony.xml new file mode 100644 index 00000000000..5a9435a0d8c --- /dev/null +++ b/model/model-intest/src/test/resources/security/multitenant/resource-dummy-barony.xml @@ -0,0 +1,44 @@ + + + + + + Dummy Resource Barony + + + + + + + + + + barony + + + + + diff --git a/model/model-intest/src/test/resources/security/multitenant/resource-dummy-castle-caladan.xml b/model/model-intest/src/test/resources/security/multitenant/resource-dummy-castle-caladan.xml new file mode 100644 index 00000000000..cbbcf397599 --- /dev/null +++ b/model/model-intest/src/test/resources/security/multitenant/resource-dummy-castle-caladan.xml @@ -0,0 +1,44 @@ + + + + + + Dummy Resource Castle Caladan + + + + + + + + + + castle-caladan + + + + + diff --git a/model/model-intest/src/test/resources/security/multitenant/resource-dummy-junction.xml b/model/model-intest/src/test/resources/security/multitenant/resource-dummy-junction.xml new file mode 100644 index 00000000000..967ca1e8cf3 --- /dev/null +++ b/model/model-intest/src/test/resources/security/multitenant/resource-dummy-junction.xml @@ -0,0 +1,44 @@ + + + + + + Dummy Resource Junction + + + + + + + + + + junction + + + + +