Skip to content

Commit

Permalink
Support for tenantRef for resources and tasks (MID-4866)
Browse files Browse the repository at this point in the history
  • Loading branch information
semancik committed Sep 13, 2018
1 parent 657da76 commit cfc972d
Show file tree
Hide file tree
Showing 8 changed files with 306 additions and 15 deletions.
Expand Up @@ -740,7 +740,7 @@ private <F extends FocusType> void createAssignmentDelta(LensContext<F> context,

}

public <F extends ObjectType> void processOrgAssignments(LensContext<F> context, OperationResult result) throws SchemaException, PolicyViolationException {
public <F extends ObjectType> void processOrgAssignments(LensContext<F> context, Task task, OperationResult result) throws SchemaException, PolicyViolationException, ObjectNotFoundException, CommunicationException, ConfigurationException, SecurityViolationException, ExpressionEvaluationException {

LensFocusContext<F> focusContext = context.getFocusContext();
if (focusContext == null) {
Expand Down Expand Up @@ -797,10 +797,10 @@ public <F extends ObjectType> void processOrgAssignments(LensContext<F> context,
}
}

computeTenantRef(context, result);
computeTenantRef(context, task, result);
}

private <F extends ObjectType> void computeTenantRef(LensContext<F> context, OperationResult result) throws PolicyViolationException, SchemaException {
private <F extends ObjectType> void computeTenantRef(LensContext<F> context, Task task, OperationResult result) throws PolicyViolationException, SchemaException, ObjectNotFoundException, CommunicationException, ConfigurationException, SecurityViolationException, ExpressionEvaluationException {
String tenantOid = null;
LensFocusContext<F> focusContext = context.getFocusContext();
PrismObject<F> objectNew = focusContext.getObjectNew();
Expand Down Expand Up @@ -831,28 +831,87 @@ private <F extends ObjectType> void computeTenantRef(LensContext<F> 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 <O extends ObjectType> void computeTenantRefLegacy(LensContext<O> context, Task task, OperationResult result) throws PolicyViolationException, SchemaException, ObjectNotFoundException, CommunicationException, ConfigurationException, SecurityViolationException, ExpressionEvaluationException {
String tenantOid = null;

LensFocusContext<O> focusContext = context.getFocusContext();
PrismObject<O> 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 <F extends ObjectType> void addTenantRefDelta(LensContext<F> context, PrismObject<F> objectNew, String tenantOid) throws SchemaException {
LensFocusContext<F> focusContext = context.getFocusContext();
ObjectReferenceType currentTenantRef = objectNew.asObjectable().getTenantRef();
if (currentTenantRef == null) {
if (tenantOid == null) {
return;
} 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);
}
}
}
Expand Down
Expand Up @@ -118,14 +118,23 @@ public <O extends ObjectType, F extends FocusType> 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<F>)context, activityDescription, now, task, result);
} else {
processFocusNonFocus(context, activityDescription, now, task, result);
}

processFocusFocus((LensContext<F>)context, activityDescription, now, task, result);
}

private <O extends ObjectType> void processFocusNonFocus(LensContext<O> 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 <F extends FocusType> void processFocusFocus(LensContext<F> context, String activityDescription,
XMLGregorianCalendar now, Task task, OperationResult result)
throws ObjectNotFoundException, SchemaException, ExpressionEvaluationException, PolicyViolationException,
Expand Down Expand Up @@ -233,7 +242,7 @@ private <F extends FocusType> void processFocusFocus(LensContext<F> context, Str
partialProcessingOptions::getAssignments);

medic.partialExecute("assignmentsOrg",
() -> assignmentProcessor.processOrgAssignments(context, result),
() -> assignmentProcessor.processOrgAssignments(context, task, result),
partialProcessingOptions::getAssignmentsOrg);


Expand Down
Expand Up @@ -1030,9 +1030,13 @@ protected void assertAddDenyRaw(File file) throws ObjectAlreadyExistsException,
}

protected <O extends ObjectType> void assertAddDeny(File file, ModelExecuteOptions options) throws ObjectAlreadyExistsException, ObjectNotFoundException, SchemaException, ExpressionEvaluationException, CommunicationException, ConfigurationException, PolicyViolationException, IOException {
PrismObject<O> object = PrismTestUtil.parseObject(file);
assertAddDeny(object, options);
}

protected <O extends ObjectType> void assertAddDeny(PrismObject<O> 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<O> object = PrismTestUtil.parseObject(file);
ObjectDelta<O> addDelta = object.createAddDelta();
try {
logAttempt("add", object.getCompileTimeClass(), object.getOid(), null);
Expand All @@ -1051,9 +1055,13 @@ protected void assertAddAllow(File file) throws ObjectAlreadyExistsException, Ob
}

protected <O extends ObjectType> void assertAddAllow(File file, ModelExecuteOptions options) throws ObjectAlreadyExistsException, ObjectNotFoundException, SchemaException, ExpressionEvaluationException, CommunicationException, ConfigurationException, PolicyViolationException, SecurityViolationException, IOException {
PrismObject<O> object = PrismTestUtil.parseObject(file);
assertAddAllow(object, options);
}

protected <O extends ObjectType> void assertAddAllow(PrismObject<O> 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<O> object = PrismTestUtil.parseObject(file);
ObjectDelta<O> addDelta = object.createAddDelta();
logAttempt("add", object.getCompileTimeClass(), object.getOid(), null);
try {
Expand Down
Expand Up @@ -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;

Expand All @@ -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";
Expand All @@ -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";
Expand All @@ -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";
Expand All @@ -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";
Expand All @@ -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<ConnectorType> 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
Expand All @@ -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;
Expand Down Expand Up @@ -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<ResourceType> 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<ResourceType> resource = PrismTestUtil.parseObject(file);
resource.asObjectable()
.connectorRef(dummyConnector.getOid(), ConnectorType.COMPLEX_TYPE);
assertAddDeny(resource, null);
}

/**
* MID-4882
*/
Expand Down
2 changes: 1 addition & 1 deletion model/model-intest/src/test/resources/logback-test.xml
Expand Up @@ -97,7 +97,7 @@
<logger name="com.evolveum.midpoint.model.impl.controller.ObjectMerger" level="DEBUG" />
<logger name="com.evolveum.midpoint.notifications" level="DEBUG" />
<logger name="com.evolveum.midpoint.security" level="DEBUG" />
<logger name="com.evolveum.midpoint.security.enforcer.impl.SecurityEnforcerImpl" level="TRACE" />
<logger name="com.evolveum.midpoint.security.enforcer.impl.SecurityEnforcerImpl" level="DEBUG" />
<logger name="com.evolveum.midpoint.model.impl.security" level="DEBUG" />
<logger name="com.evolveum.midpoint.model.impl.util.AbstractSearchIterativeTaskHandler" level="DEBUG" />
<logger name="com.evolveum.midpoint.model.impl.sync.SynchronizationServiceImpl" level="DEBUG" />
Expand Down
@@ -0,0 +1,44 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright (c) 2010-2018 Evolveum
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->

<resource oid="00000000-8888-6666-a300-300000000000"
xmlns="http://midpoint.evolveum.com/xml/ns/public/common/common-3"
xmlns:c="http://midpoint.evolveum.com/xml/ns/public/common/common-3"
xmlns:q="http://prism.evolveum.com/xml/ns/public/query-3"
xmlns:t="http://prism.evolveum.com/xml/ns/public/types-3"
xmlns:icfs="http://midpoint.evolveum.com/xml/ns/public/connector/icf-1/resource-schema-3"
xmlns:ri="http://midpoint.evolveum.com/xml/ns/public/resource/instance-3"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

<name>Dummy Resource Barony</name>

<parentOrgRef oid="00000000-8888-6666-a300-000000000000"/> <!-- House Harkonnen -->

<connectorRef type="c:ConnectorType">
<!-- will be supplied by test code -->
</connectorRef>
<connectorConfiguration xmlns:icfi="http://midpoint.evolveum.com/xml/ns/public/connector/icf-1/bundle/com.evolveum.icf.dummy/com.evolveum.icf.dummy.connector.DummyConnector"
xmlns:icfc="http://midpoint.evolveum.com/xml/ns/public/connector/icf-1/connector-schema-3">

<icfc:configurationProperties>
<icfi:instanceId>barony</icfi:instanceId>
</icfc:configurationProperties>

</connectorConfiguration>

</resource>

0 comments on commit cfc972d

Please sign in to comment.