From c7ded2f3479595673e32259c025a29966fce8742 Mon Sep 17 00:00:00 2001 From: Radovan Semancik Date: Mon, 10 Sep 2018 16:27:26 +0200 Subject: [PATCH 01/13] Basic footwork for multitenant security tests (MID-4882) --- .../security/TestSecurityMultitenant.java | 253 ++++++++++++++++++ .../security/multitenant/org-multitenant.xml | 86 ++++++ model/model-intest/testng-integration.xml | 1 + .../test/AbstractModelIntegrationTest.java | 39 ++- .../test/asserter/AssignmentsAsserter.java | 4 - .../midpoint/test/asserter/FocusAsserter.java | 45 +++- .../midpoint/test/asserter/LinkFinder.java | 5 +- .../midpoint/test/asserter/LinksAsserter.java | 2 +- .../midpoint/test/asserter/OrgAsserter.java | 241 +++++++++++++++++ .../test/asserter/ParentOrgRefAsserter.java | 99 +++++++ .../test/asserter/ParentOrgRefFinder.java | 125 +++++++++ .../test/asserter/ParentOrgRefsAsserter.java | 143 ++++++++++ .../test/asserter/PrismObjectAsserter.java | 67 ++++- .../midpoint/test/asserter/UserAsserter.java | 20 ++ 14 files changed, 1099 insertions(+), 31 deletions(-) create mode 100644 model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/security/TestSecurityMultitenant.java create mode 100644 model/model-intest/src/test/resources/security/multitenant/org-multitenant.xml create mode 100644 repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/asserter/OrgAsserter.java create mode 100644 repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/asserter/ParentOrgRefAsserter.java create mode 100644 repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/asserter/ParentOrgRefFinder.java create mode 100644 repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/asserter/ParentOrgRefsAsserter.java 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 new file mode 100644 index 00000000000..4f6797735cc --- /dev/null +++ b/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/security/TestSecurityMultitenant.java @@ -0,0 +1,253 @@ +/* + * 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. + */ +package com.evolveum.midpoint.model.intest.security; + +import static org.testng.AssertJUnit.assertFalse; +import static org.testng.AssertJUnit.assertTrue; +import static org.testng.AssertJUnit.assertNotNull; +import static org.testng.AssertJUnit.assertEquals; + +import java.io.File; +import java.io.IOException; +import java.util.Collection; +import java.util.List; + +import javax.xml.datatype.XMLGregorianCalendar; + +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.model.api.ModelAuthorizationAction; +import com.evolveum.midpoint.model.api.RoleSelectionSpecification; +import com.evolveum.midpoint.prism.PrismContainer; +import com.evolveum.midpoint.prism.PrismObject; +import com.evolveum.midpoint.prism.PrismObjectDefinition; +import com.evolveum.midpoint.prism.PrismReferenceValue; +import com.evolveum.midpoint.prism.delta.ObjectDelta; +import com.evolveum.midpoint.prism.path.ItemPath; +import com.evolveum.midpoint.prism.path.NameItemPathSegment; +import com.evolveum.midpoint.prism.query.ObjectFilter; +import com.evolveum.midpoint.prism.query.ObjectQuery; +import com.evolveum.midpoint.prism.query.TypeFilter; +import com.evolveum.midpoint.prism.util.PrismAsserts; +import com.evolveum.midpoint.prism.util.PrismTestUtil; +import com.evolveum.midpoint.prism.xml.XmlTypeConverter; +import com.evolveum.midpoint.schema.GetOperationOptions; +import com.evolveum.midpoint.schema.SelectorOptions; +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.security.api.MidPointPrincipal; +import com.evolveum.midpoint.security.enforcer.api.AuthorizationParameters; +import com.evolveum.midpoint.task.api.Task; +import com.evolveum.midpoint.test.DummyResourceContoller; +import com.evolveum.midpoint.test.IntegrationTestTools; +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.ActivationStatusType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.ActivationType; +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.AuthorizationPhaseType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.ConstructionType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.ExclusionPolicyConstraintType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.FocusType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.LookupTableType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.MappingType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.MetadataType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.ModelExecuteOptionsType; +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.OrgType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.PasswordType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.PolicyConstraintsType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.PolicyExceptionType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.PolicyRuleType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.ResourceAttributeDefinitionType; +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.SystemConfigurationType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.SystemObjectsType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.UserType; + +/** + * Security tests for multitenant environment. + * + * @author semancik + */ +@ContextConfiguration(locations = {"classpath:ctx-model-intest-test-main.xml"}) +@DirtiesContext(classMode = ClassMode.AFTER_CLASS) +public class TestSecurityMultitenant extends AbstractSecurityTest { + + public static final File TEST_DIR = new File("src/test/resources/security/multitenant"); + + protected static final File ORG_MULTITENANT_FILE = new File(TEST_DIR, "org-multitenant.xml"); + + protected static final String ORG_ROOT_OID = "00000000-8888-6666-1111-000000000000"; + + protected static final String ORG_CORRINO_OID = "00000000-8888-6666-1111-000000001000"; + protected static final String ROLE_CORRINO_ADMIN_OID = "00000000-8888-6666-1111-100000001000"; + + protected static final String ORG_ATREIDES_OID = "00000000-8888-6666-1111-000000002000"; + protected static final String ROLE_ATREIDES_ADMIN_OID = "00000000-8888-6666-1111-100000002000"; + + protected static final String ORG_HARKONNEN_OID = "00000000-8888-6666-1111-000000003000"; + protected static final String ROLE_HARKONNEN_ADMIN_OID = "00000000-8888-6666-1111-100000003000"; + +// protected static final File ROLE_X_FILE = new File(TEST_DIR, "role-vault-dweller.xml"); +// protected static final String ROLE_X_OID = "8d8471f4-2906-11e8-9078-4f2b205aa01d"; + + @Override + public void initSystem(Task initTask, OperationResult initResult) throws Exception { + super.initSystem(initTask, initResult); + +// repoAddObjectFromFile(ROLE_VAULT_DWELLER_FILE, initResult); + + assumeAssignmentPolicy(AssignmentPolicyEnforcementType.RELATIVE); + + } + + @Override + protected boolean doAddOrgstruct() { + return false; + } + + @Override + protected String getTopOrgOid() { + return ORG_ROOT_OID; + } + + protected static final int NUMBER_OF_IMPORTED_ROLES = 0; + + protected int getNumberOfRoles() { + return super.getNumberOfRoles() + NUMBER_OF_IMPORTED_ROLES; + } + + /** + * Stay logged in as administrator. Make sure that our assumptions about + * the users and roles are correct. + */ + @Test + public void test000Sanity() throws Exception { + final String TEST_NAME = "test000Sanity"; + displayTestTitle(TEST_NAME); + // GIVEN + cleanupAutzTest(USER_JACK_OID); + + // WHEN + displayWhen(TEST_NAME); + assertSearch(UserType.class, null, NUMBER_OF_ALL_USERS); + assertSearch(RoleType.class, null, getNumberOfRoles()); + + assertReadAllow(NUMBER_OF_ALL_USERS); + assertReadAllowRaw(NUMBER_OF_ALL_USERS); + assertAddAllow(); + assertAddAllowRaw(); + assertModifyAllow(); + assertDeleteAllow(); + + assertGlobalStateUntouched(); + } + + /** + * Stay logged in as administrator. + * Import orgstruct with tenant and roles and everything. + */ + @Test + public void test010ImportOrgstruct() throws Exception { + final String TEST_NAME = "test010ImportOrgstruct"; + displayTestTitle(TEST_NAME); + // GIVEN + + Task task = createTask(TEST_NAME); + OperationResult result = task.getResult(); + + ImportOptionsType options = MiscSchemaUtil.getDefaultImportOptions(); + ModelExecuteOptionsType modelOptions = new ModelExecuteOptionsType(); + modelOptions.setRaw(false); + options.setModelExecutionOptions(modelOptions); + + // WHEN + displayWhen(TEST_NAME); + importObjectFromFile(ORG_MULTITENANT_FILE, options, task, result); + + // THEN + displayThen(TEST_NAME); + assertSuccess(result); + + dumpOrgTree(); + + assertOrgAfter(ORG_ATREIDES_OID) + .assertIsTenant() + .assignments() + .single() + .assertTargetOid(ORG_ROOT_OID) + .end() + .end() + .assertLinks(0) + .assertParentOrgRefs(ORG_ROOT_OID); + + assertRoleAfter(ROLE_CORRINO_ADMIN_OID) +// .assertTenantRef(ORG_ATREIDES_OID) + .assertParentOrgRefs(ORG_ATREIDES_OID); + + assertGlobalStateUntouched(); + } + + /** + */ + @Test(enabled=false) // work in progress + public void test080AutzJackEndUserPassword() throws Exception { + final String TEST_NAME = "test080AutzJackEndUserPassword"; + displayTestTitle(TEST_NAME); + // GIVEN + cleanupAutzTest(USER_JACK_OID); + +// assignRole(USER_JACK_OID, ROLE_END_USER_OID); + + login(USER_JACK_USERNAME); + + // WHEN + displayWhen(TEST_NAME); + +// assertAllow("set jack's password", +// (task, result) -> modifyUserSetPassword(USER_JACK_OID, "nbusr123", task, result) ); + + // THEN + displayThen(TEST_NAME); + +// XMLGregorianCalendar endTs = clock.currentTimeXMLGregorianCalendar(); +// +// user = getUser(USER_JACK_OID); +// display("user after password change", user); +// PasswordType passwordType = assertUserPassword(user, "nbusr123"); +// MetadataType metadata = passwordType.getMetadata(); +// assertNotNull("No password metadata", metadata); +// assertMetadata("password metadata", metadata, true, false, startTs, endTs, USER_JACK_OID, SchemaConstants.CHANNEL_GUI_USER_URI); + + assertGlobalStateUntouched(); + } + +} diff --git a/model/model-intest/src/test/resources/security/multitenant/org-multitenant.xml b/model/model-intest/src/test/resources/security/multitenant/org-multitenant.xml new file mode 100644 index 00000000000..9728c3f61f7 --- /dev/null +++ b/model/model-intest/src/test/resources/security/multitenant/org-multitenant.xml @@ -0,0 +1,86 @@ + + + + + + + 0000 + Root of the multitenant structure + tenancy + Multitenant root + 0000 + + + + Corrino + tenancy + + + + House Corrino + 1000 + Kaitain + true + + + + Corrino Admin + + + + + + + Atreides + tenancy + + + + House Atreides + 2000 + Caladan + true + + + + Atreides Admin + + + + + + + Harkonnen + tenancy + + + + House Harkonnen + 3000 + Giedi Prime + true + + + + Harkonnen Admin + + + + + + diff --git a/model/model-intest/testng-integration.xml b/model/model-intest/testng-integration.xml index 2522e64e63c..d1f40366fcf 100644 --- a/model/model-intest/testng-integration.xml +++ b/model/model-intest/testng-integration.xml @@ -144,6 +144,7 @@ + diff --git a/model/model-test/src/main/java/com/evolveum/midpoint/model/test/AbstractModelIntegrationTest.java b/model/model-test/src/main/java/com/evolveum/midpoint/model/test/AbstractModelIntegrationTest.java index 4c8a19b22a4..8918d860046 100644 --- a/model/model-test/src/main/java/com/evolveum/midpoint/model/test/AbstractModelIntegrationTest.java +++ b/model/model-test/src/main/java/com/evolveum/midpoint/model/test/AbstractModelIntegrationTest.java @@ -164,6 +164,7 @@ import com.evolveum.midpoint.test.asserter.AbstractAsserter; import com.evolveum.midpoint.test.asserter.DummyAccountAsserter; import com.evolveum.midpoint.test.asserter.FocusAsserter; +import com.evolveum.midpoint.test.asserter.OrgAsserter; import com.evolveum.midpoint.test.asserter.ShadowAsserter; import com.evolveum.midpoint.test.asserter.UserAsserter; import com.evolveum.midpoint.test.util.MidPointAsserts; @@ -181,6 +182,7 @@ import com.evolveum.midpoint.util.exception.TunnelException; import com.evolveum.midpoint.util.logging.Trace; import com.evolveum.midpoint.util.logging.TraceManager; +import com.evolveum.midpoint.xml.ns._public.common.api_types_3.ImportOptionsType; import com.evolveum.midpoint.xml.ns._public.common.common_3.AbstractRoleType; import com.evolveum.midpoint.xml.ns._public.common.common_3.ActivationStatusType; import com.evolveum.midpoint.xml.ns._public.common.common_3.ActivationType; @@ -428,8 +430,12 @@ protected void importObjectFromFile(File file, OperationResult result) throws Fi } protected void importObjectFromFile(File file, Task task, OperationResult result) throws FileNotFoundException { + importObjectFromFile(file, MiscSchemaUtil.getDefaultImportOptions(), task, result); + } + + protected void importObjectFromFile(File file, ImportOptionsType options, Task task, OperationResult result) throws FileNotFoundException { FileInputStream stream = new FileInputStream(file); - modelService.importObjectsFromStream(stream, MiscSchemaUtil.getDefaultImportOptions(), task, result); + modelService.importObjectsFromStream(stream, PrismContext.LANG_XML, options, task, result); } protected Throwable findCause(OperationResult result) { @@ -5417,6 +5423,37 @@ protected UserAsserter assertUserByUsername(String username, String messag return asserter; } + protected OrgAsserter assertOrg(String oid, String message) throws ObjectNotFoundException, SchemaException, SecurityViolationException, CommunicationException, ConfigurationException, ExpressionEvaluationException { + PrismObject org = getObject(OrgType.class, oid); + OrgAsserter asserter = OrgAsserter.forOrg(org, message); + initializeAsserter(asserter); + asserter.assertOid(oid); + return asserter; + } + + protected OrgAsserter assertOrgAfter(String oid) throws ObjectNotFoundException, SchemaException, SecurityViolationException, CommunicationException, ConfigurationException, ExpressionEvaluationException { + OrgAsserter asserter = assertOrg(oid, "after"); + asserter.display(); + asserter.assertOid(oid); + return asserter; + } + + protected FocusAsserter assertRole(String oid, String message) throws ObjectNotFoundException, SchemaException, SecurityViolationException, CommunicationException, ConfigurationException, ExpressionEvaluationException { + PrismObject role = getObject(RoleType.class, oid); + // TODO: change to ServiceAsserter later + FocusAsserter asserter = FocusAsserter.forFocus(role, message); + initializeAsserter(asserter); + asserter.assertOid(oid); + return asserter; + } + + protected FocusAsserter assertRoleAfter(String oid) throws ObjectNotFoundException, SchemaException, SecurityViolationException, CommunicationException, ConfigurationException, ExpressionEvaluationException { + FocusAsserter asserter = assertRole(oid, "after"); + asserter.display(); + asserter.assertOid(oid); + return asserter; + } + protected FocusAsserter assertService(String oid, String message) throws ObjectNotFoundException, SchemaException, SecurityViolationException, CommunicationException, ConfigurationException, ExpressionEvaluationException { PrismObject service = getObject(ServiceType.class, oid); // TODO: change to ServiceAsserter later diff --git a/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/asserter/AssignmentsAsserter.java b/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/asserter/AssignmentsAsserter.java index 40d60fc4afe..1a4637a2c98 100644 --- a/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/asserter/AssignmentsAsserter.java +++ b/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/asserter/AssignmentsAsserter.java @@ -62,10 +62,6 @@ public static AssignmentsAsserter, return new AssignmentsAsserter<>(FocusAsserter.forFocus(focus)); } - PrismObject getLinkTarget(String oid) throws ObjectNotFoundException, SchemaException { - return focusAsserter.getLinkTarget(oid); - } - List getAssignments() { if (assignments == null) { assignments = getFocus().asObjectable().getAssignment(); diff --git a/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/asserter/FocusAsserter.java b/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/asserter/FocusAsserter.java index 237dced173f..5a11893c728 100644 --- a/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/asserter/FocusAsserter.java +++ b/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/asserter/FocusAsserter.java @@ -42,6 +42,7 @@ import com.evolveum.midpoint.xml.ns._public.common.common_3.ActivationType; import com.evolveum.midpoint.xml.ns._public.common.common_3.FocusType; 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.PendingOperationType; import com.evolveum.midpoint.xml.ns._public.common.common_3.ShadowKindType; import com.evolveum.midpoint.xml.ns._public.common.common_3.ShadowType; @@ -52,8 +53,6 @@ */ public class FocusAsserter extends PrismObjectAsserter { - private Map> projectionCache = new HashMap<>(); - public FocusAsserter(PrismObject focus) { super(focus); } @@ -73,6 +72,16 @@ public static FocusAsserter forFocus(PrismObject FocusAsserter forFocus(PrismObject focus, String details) { return new FocusAsserter<>(focus, details); } + + // :::::::::::::::::::::::::::::::::::::::::: + // : NOTE : WARNING : ATTENTION : LOOK HERE : + // :::::::::::::::::::::::::::::::::::::::::: + // + // If you add any method here, add it also in UserAsserter, OrgAsserter and other subclasses. + // + // It is insane to override all those methods from superclass. + // But there is no better way to specify something like type in Java. + // This is lesser evil. @Override public FocusAsserter assertOid() { @@ -110,6 +119,12 @@ public FocusAsserter assertDescription(String expected) { return this; } + @Override + public FocusAsserter assertTenantRef(String expectedOid) { + super.assertTenantRef(expectedOid); + return this; + } + @Override public FocusAsserter assertLifecycleState(String expected) { super.assertLifecycleState(expected); @@ -178,6 +193,19 @@ public ShadowReferenceAsserter> singleLink() { return asserter; } + @Override + public ParentOrgRefsAsserter, RA> parentOrgRefs() { + ParentOrgRefsAsserter,RA> asserter = new ParentOrgRefsAsserter<>(this, getDetails()); + copySetupTo(asserter); + return asserter; + } + + @Override + public FocusAsserter assertParentOrgRefs(String... expectedOids) { + super.assertParentOrgRefs(expectedOids); + return this; + } + public FocusAsserter assertHasProjectionOnResource(String resourceOid) throws ObjectNotFoundException, SchemaException { PrismObject shadow = findProjectionOnResource(resourceOid); assertNotNull("Projection for resource "+resourceOid+" not found in "+desc(), shadow); @@ -219,19 +247,12 @@ private List> getLinkTargets() throws ObjectNotFoundExce for (ObjectReferenceType linkRefType: focusType.getLinkRef()) { String linkTargetOid = linkRefType.getOid(); assertFalse("No linkRef oid in "+desc(), StringUtils.isBlank(linkTargetOid)); - shadows.add(getLinkTarget(linkTargetOid)); + shadows.add(getCachedObject(ShadowType.class, linkTargetOid)); } return shadows; } - PrismObject getLinkTarget(String oid) throws ObjectNotFoundException, SchemaException { - PrismObject shadow = projectionCache.get(oid); - if (shadow == null) { - shadow = resolveObject(ShadowType.class, oid); - projectionCache.put(oid, shadow); - } - return shadow; - } + public FocusAsserter displayWithProjections() throws ObjectNotFoundException, SchemaException { StringBuilder sb = new StringBuilder(); @@ -242,7 +263,7 @@ public FocusAsserter displayWithProjections() throws ObjectNotFoundExcept String linkTargetOid = linkRefType.getOid(); assertFalse("No linkRef oid in "+desc(), StringUtils.isBlank(linkTargetOid)); try { - PrismObject linkTarget = getLinkTarget(linkTargetOid); + PrismObject linkTarget = getCachedObject(ShadowType.class, linkTargetOid); sb.append(linkTarget); ShadowType shadowType = linkTarget.asObjectable(); ObjectReferenceType resourceRef = shadowType.getResourceRef(); diff --git a/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/asserter/LinkFinder.java b/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/asserter/LinkFinder.java index bad3d490c31..55109424485 100644 --- a/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/asserter/LinkFinder.java +++ b/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/asserter/LinkFinder.java @@ -36,8 +36,11 @@ import com.evolveum.prism.xml.ns._public.types_3.ObjectDeltaType; /** + * + * Note: considered to align this with ParentOrgRefFinder into some kind of common superclass. + * But the resulting structure of generics is just too insane. It is lesser evil to have copy&pasted code. + * * @author semancik - * */ public class LinkFinder,RA> { diff --git a/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/asserter/LinksAsserter.java b/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/asserter/LinksAsserter.java index 68a39e1968c..72a5544ef6c 100644 --- a/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/asserter/LinksAsserter.java +++ b/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/asserter/LinksAsserter.java @@ -60,7 +60,7 @@ public static LinksAsserter,Void> } PrismObject getLinkTarget(String oid) throws ObjectNotFoundException, SchemaException { - return focusAsserter.getLinkTarget(oid); + return focusAsserter.getCachedObject(ShadowType.class, oid); } List getLinks() { diff --git a/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/asserter/OrgAsserter.java b/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/asserter/OrgAsserter.java new file mode 100644 index 00000000000..caa62a04615 --- /dev/null +++ b/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/asserter/OrgAsserter.java @@ -0,0 +1,241 @@ +/** + * Copyright (c) 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. + */ +package com.evolveum.midpoint.test.asserter; + +import static org.testng.AssertJUnit.assertEquals; +import static org.testng.AssertJUnit.assertFalse; +import static org.testng.AssertJUnit.assertNotNull; +import static org.testng.AssertJUnit.assertNull; +import static org.testng.AssertJUnit.assertTrue; + +import java.util.List; + +import javax.xml.namespace.QName; + +import com.evolveum.midpoint.prism.PrismObject; +import com.evolveum.midpoint.prism.PrismReference; +import com.evolveum.midpoint.prism.crypto.EncryptionException; +import com.evolveum.midpoint.prism.util.PrismAsserts; +import com.evolveum.midpoint.test.IntegrationTestTools; +import com.evolveum.midpoint.util.exception.ObjectNotFoundException; +import com.evolveum.midpoint.util.exception.SchemaException; +import com.evolveum.midpoint.xml.ns._public.common.common_3.ActivationStatusType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.ActivationType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.CredentialsStorageTypeType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.CredentialsType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.FocusType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.OrgType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.PasswordType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.PendingOperationType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.ShadowKindType; +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.ProtectedStringType; + +/** + * @author semancik + * + */ +public class OrgAsserter extends FocusAsserter { + + public OrgAsserter(PrismObject focus) { + super(focus); + } + + public OrgAsserter(PrismObject focus, String details) { + super(focus, details); + } + + public OrgAsserter(PrismObject focus, RA returnAsserter, String details) { + super(focus, returnAsserter, details); + } + + public static OrgAsserter forOrg(PrismObject focus) { + return new OrgAsserter<>(focus); + } + + public static OrgAsserter forOrg(PrismObject focus, String details) { + return new OrgAsserter<>(focus, details); + } + + // It is insane to override all those methods from superclass. + // But there is no better way to specify something like type in Java. + // This is lesser evil. + @Override + public OrgAsserter assertOid() { + super.assertOid(); + return this; + } + + @Override + public OrgAsserter assertOid(String expected) { + super.assertOid(expected); + return this; + } + + @Override + public OrgAsserter assertOidDifferentThan(String oid) { + super.assertOidDifferentThan(oid); + return this; + } + + @Override + public OrgAsserter assertName() { + super.assertName(); + return this; + } + + @Override + public OrgAsserter assertName(String expectedOrig) { + super.assertName(expectedOrig); + return this; + } + + @Override + public OrgAsserter assertDescription(String expected) { + super.assertDescription(expected); + return this; + } + + @Override + public OrgAsserter assertTenantRef(String expectedOid) { + super.assertTenantRef(expectedOid); + return this; + } + + @Override + public OrgAsserter assertLifecycleState(String expected) { + super.assertLifecycleState(expected); + return this; + } + + @Override + public OrgAsserter assertActiveLifecycleState() { + super.assertActiveLifecycleState(); + return this; + } + + public OrgAsserter assertAdministrativeStatus(ActivationStatusType expected) { + ActivationType activation = getActivation(); + if (activation == null) { + if (expected == null) { + return this; + } else { + fail("No activation in "+desc()); + } + } + assertEquals("Wrong activation administrativeStatus in "+desc(), expected, activation.getAdministrativeStatus()); + return this; + } + + private ActivationType getActivation() { + return getObject().asObjectable().getActivation(); + } + + public OrgAsserter display() { + super.display(); + return this; + } + + public OrgAsserter display(String message) { + super.display(message); + return this; + } + + @Override + public ActivationAsserter, RA> activation() { + ActivationAsserter, RA> asserter = new ActivationAsserter<>(this, getDetails()); + copySetupTo(asserter); + return asserter; + } + + @Override + public LinksAsserter, RA> links() { + LinksAsserter, RA> asserter = new LinksAsserter<>(this, getDetails()); + copySetupTo(asserter); + return asserter; + } + + @Override + public OrgAsserter assertLinks(int expected) { + super.assertLinks(expected); + return this; + } + + @Override + public AssignmentsAsserter, RA> assignments() { + AssignmentsAsserter, RA> asserter = new AssignmentsAsserter<>(this, getDetails()); + copySetupTo(asserter); + return asserter; + } + + public OrgAsserter assertDisplayName(String expectedOrig) { + assertPolyStringProperty(OrgType.F_DISPLAY_NAME, expectedOrig); + return this; + } + + public OrgAsserter assertLocality(String expectedOrig) { + assertPolyStringProperty(OrgType.F_LOCALITY, expectedOrig); + return this; + } + + public OrgAsserter assertIsTenant() { + assertPropertyEquals(OrgType.F_TENANT, true); + return this; + } + + @Override + public OrgAsserter assertHasProjectionOnResource(String resourceOid) throws ObjectNotFoundException, SchemaException { + super.assertHasProjectionOnResource(resourceOid); + return this; + } + + @Override + public ShadowAsserter> projectionOnResource(String resourceOid) throws ObjectNotFoundException, SchemaException { + return super.projectionOnResource(resourceOid); + } + + @Override + public OrgAsserter displayWithProjections() throws ObjectNotFoundException, SchemaException { + super.displayWithProjections(); + return this; + } + + @Override + public ShadowReferenceAsserter> singleLink() { + return (ShadowReferenceAsserter>) super.singleLink(); + } + + @Override + public OrgAsserter assertAssignments(int expected) { + super.assertAssignments(expected); + return this; + } + + @Override + public ParentOrgRefsAsserter, RA> parentOrgRefs() { + ParentOrgRefsAsserter, RA> asserter = new ParentOrgRefsAsserter<>(this, getDetails()); + copySetupTo(asserter); + return asserter; + } + + @Override + public OrgAsserter assertParentOrgRefs(String... expectedOids) { + super.assertParentOrgRefs(expectedOids); + return this; + } + +} diff --git a/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/asserter/ParentOrgRefAsserter.java b/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/asserter/ParentOrgRefAsserter.java new file mode 100644 index 00000000000..05dad61a741 --- /dev/null +++ b/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/asserter/ParentOrgRefAsserter.java @@ -0,0 +1,99 @@ +/** + * Copyright (c) 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. + */ +package com.evolveum.midpoint.test.asserter; + +import static org.testng.AssertJUnit.assertEquals; +import static org.testng.AssertJUnit.assertFalse; +import static org.testng.AssertJUnit.assertNotNull; +import static org.testng.AssertJUnit.assertNull; +import static org.testng.AssertJUnit.assertTrue; + +import javax.xml.datatype.XMLGregorianCalendar; +import javax.xml.namespace.QName; + +import com.evolveum.midpoint.prism.PrismObject; +import com.evolveum.midpoint.prism.PrismReferenceValue; +import com.evolveum.midpoint.prism.delta.ObjectDelta; +import com.evolveum.midpoint.schema.DeltaConvertor; +import com.evolveum.midpoint.schema.constants.ObjectTypes; +import com.evolveum.midpoint.test.IntegrationTestTools; +import com.evolveum.midpoint.test.util.TestUtil; +import com.evolveum.midpoint.util.exception.ObjectNotFoundException; +import com.evolveum.midpoint.util.exception.SchemaException; +import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.OperationResultStatusType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.OrgType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.PendingOperationExecutionStatusType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.PendingOperationType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.ShadowType; +import com.evolveum.prism.xml.ns._public.types_3.ObjectDeltaType; + +/** + * @author semancik + * + */ +public class ParentOrgRefAsserter extends ObjectReferenceAsserter { + + public ParentOrgRefAsserter(PrismReferenceValue refVal) { + super(refVal, OrgType.class); + } + + public ParentOrgRefAsserter(PrismReferenceValue refVal, String detail) { + super(refVal, OrgType.class, detail); + } + + public ParentOrgRefAsserter(PrismReferenceValue refVal, PrismObject resolvedTarget, R returnAsserter, String detail) { + super(refVal, OrgType.class, resolvedTarget, returnAsserter, detail); + } + + @Override + public ParentOrgRefAsserter assertOid() { + super.assertOid(); + return this; + } + + @Override + public ParentOrgRefAsserter assertOid(String expected) { + super.assertOid(expected); + return this; + } + + @Override + public ParentOrgRefAsserter assertOidDifferentThan(String expected) { + super.assertOidDifferentThan(expected); + return this; + } + + public ShadowAsserter> shadow() { + ShadowAsserter> asserter = new ShadowAsserter<>((PrismObject)getRefVal().getObject(), this, "shadow in reference "+desc()); + copySetupTo(asserter); + return asserter; + } + + @Override + public FocusAsserter> target() + throws ObjectNotFoundException, SchemaException { + return new FocusAsserter<>(getResolvedTarget(), this, "object resolved from "+desc()); + } + + @Override + public FocusAsserter> resolveTarget() + throws ObjectNotFoundException, SchemaException { + PrismObject object = resolveTargetObject(); + return new FocusAsserter<>(object, this, "object resolved from "+desc()); + } + +} diff --git a/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/asserter/ParentOrgRefFinder.java b/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/asserter/ParentOrgRefFinder.java new file mode 100644 index 00000000000..52d367faff2 --- /dev/null +++ b/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/asserter/ParentOrgRefFinder.java @@ -0,0 +1,125 @@ +/** + * Copyright (c) 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. + */ +package com.evolveum.midpoint.test.asserter; + +import static org.testng.AssertJUnit.assertEquals; +import java.util.List; + +import javax.xml.namespace.QName; + +import org.testng.AssertJUnit; + +import com.evolveum.midpoint.prism.PrismObject; +import com.evolveum.midpoint.prism.PrismReferenceValue; +import com.evolveum.midpoint.prism.path.ItemPath; +import com.evolveum.midpoint.schema.util.ShadowUtil; +import com.evolveum.midpoint.util.QNameUtil; +import com.evolveum.midpoint.util.exception.ObjectNotFoundException; +import com.evolveum.midpoint.util.exception.SchemaException; +import com.evolveum.midpoint.xml.ns._public.common.common_3.FocusType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.OperationResultStatusType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.OrgType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.PendingOperationExecutionStatusType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.PendingOperationType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.ShadowType; +import com.evolveum.prism.xml.ns._public.types_3.ChangeTypeType; +import com.evolveum.prism.xml.ns._public.types_3.ItemDeltaType; +import com.evolveum.prism.xml.ns._public.types_3.ObjectDeltaType; + +/** + * + * Note: considered to align this with ParentOrgRefFinder into some kind of common superclass. + * But the resulting structure of generics is just too insane. It is lesser evil to have copy&pasted code. + * + * @author semancik + */ +public class ParentOrgRefFinder,RA> { + + private final ParentOrgRefsAsserter refsAsserter; + private String targetOid; + private QName relation; + + public ParentOrgRefFinder(ParentOrgRefsAsserter refsAsserter) { + this.refsAsserter = refsAsserter; + } + + public ParentOrgRefFinder targetOid(String targetOid) { + this.targetOid = targetOid; + return this; + } + + public ParentOrgRefFinder relation(QName relation) { + this.relation = relation; + return this; + } + + public ParentOrgRefAsserter> find() throws ObjectNotFoundException, SchemaException { + PrismReferenceValue found = null; + PrismObject foundTarget = null; + for (PrismReferenceValue ref: refsAsserter.getRefs()) { + PrismObject refTarget = refsAsserter.getRefTarget(ref.getOid()); + if (matches(ref, refTarget)) { + if (found == null) { + found = ref; + foundTarget = refTarget; + } else { + fail("Found more than one parentOrgRef that matches search criteria"); + } + } + } + if (found == null) { + fail("Found no parentOrgRef that matches search criteria"); + } + return refsAsserter.forRef(found, foundTarget); + } + + public ParentOrgRefsAsserter assertCount(int expectedCount) throws ObjectNotFoundException, SchemaException { + int foundCount = 0; + for (PrismReferenceValue ref: refsAsserter.getRefs()) { + PrismObject linkTarget = refsAsserter.getRefTarget(ref.getOid()); + if (matches(ref, linkTarget)) { + foundCount++; + } + } + assertEquals("Wrong number of links for specified criteria in "+refsAsserter.desc(), expectedCount, foundCount); + return refsAsserter; + } + + private boolean matches(PrismReferenceValue refVal, PrismObject refTarget) throws ObjectNotFoundException, SchemaException { + OrgType refTargetType = refTarget.asObjectable(); + + if (targetOid != null) { + if (!targetOid.equals(refVal.getOid())) { + return false; + } + } + + if (relation != null) { + if (!QNameUtil.match(relation, refVal.getRelation())) { + return false; + } + } + + // TODO: more criteria + return true; + } + + protected void fail(String message) { + AssertJUnit.fail(message); + } + +} diff --git a/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/asserter/ParentOrgRefsAsserter.java b/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/asserter/ParentOrgRefsAsserter.java new file mode 100644 index 00000000000..e5e057dc5b8 --- /dev/null +++ b/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/asserter/ParentOrgRefsAsserter.java @@ -0,0 +1,143 @@ +/** + * Copyright (c) 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. + */ +package com.evolveum.midpoint.test.asserter; + +import static org.testng.AssertJUnit.assertEquals; +import static org.testng.AssertJUnit.assertFalse; +import static org.testng.AssertJUnit.assertNotNull; +import static org.testng.AssertJUnit.assertNull; +import static org.testng.AssertJUnit.assertTrue; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import com.evolveum.midpoint.prism.PrismObject; +import com.evolveum.midpoint.prism.PrismReference; +import com.evolveum.midpoint.prism.PrismReferenceValue; +import com.evolveum.midpoint.prism.util.PrismAsserts; +import com.evolveum.midpoint.util.exception.ObjectNotFoundException; +import com.evolveum.midpoint.util.exception.SchemaException; +import com.evolveum.midpoint.xml.ns._public.common.common_3.FocusType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.OrgType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.PendingOperationExecutionStatusType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.PendingOperationType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.ShadowType; +import com.evolveum.prism.xml.ns._public.types_3.ChangeTypeType; + +/** + * + * Note: considered to align this with LinksAsserter into some kind of common superclass. + * But the resulting structure of generics is just too insane. It is lesser evil to have copy&pasted code. + * + * @author semancik + */ +public class ParentOrgRefsAsserter,RA> extends AbstractAsserter { + + private OA objectAsserter; + private List parentOrgRefs; + + public ParentOrgRefsAsserter(OA objectAsserter) { + super(); + this.objectAsserter = objectAsserter; + } + + public ParentOrgRefsAsserter(OA focusAsserter, String details) { + super(details); + this.objectAsserter = focusAsserter; + } + + public static ParentOrgRefsAsserter,Void> forFocus(PrismObject focus) { + return new ParentOrgRefsAsserter<>(FocusAsserter.forFocus(focus)); + } + + PrismObject getRefTarget(String oid) throws ObjectNotFoundException, SchemaException { + return objectAsserter.getCachedObject(OrgType.class, oid); + } + + List getRefs() { + if (parentOrgRefs == null) { + PrismReference linkRef = getFocus().findReference(FocusType.F_PARENT_ORG_REF); + if (linkRef == null) { + parentOrgRefs = new ArrayList<>(); + } else { + parentOrgRefs = linkRef.getValues(); + } + } + return parentOrgRefs; + } + + public ParentOrgRefsAsserter assertRefs(int expected) { + assertEquals("Wrong number of parentOrgRefs in " + desc(), expected, getRefs().size()); + return this; + } + + public ParentOrgRefsAsserter assertNone() { + assertRefs(0); + return this; + } + + public ParentOrgRefsAsserter assertRefs(String... expectedOids) { + PrismAsserts.assertEqualsCollectionUnordered("Wrong parentOrgRefs in " + desc(), getOids(), expectedOids); + return this; + } + + ParentOrgRefAsserter> forRef(PrismReferenceValue refVal, PrismObject target) { + ParentOrgRefAsserter> asserter = new ParentOrgRefAsserter<>(refVal, target, this, "parentOrgRef in "+desc()); + copySetupTo(asserter); + return asserter; + } + + public ParentOrgRefAsserter> single() { + assertRefs(1); + return forRef(getRefs().get(0), null); + } + + PrismObject getFocus() { + return objectAsserter.getObject(); + } + + @Override + public OA end() { + return objectAsserter; + } + + @Override + protected String desc() { + return descWithDetails("parentOrgRefs of "+getFocus()); + } + + public ParentOrgRefFinder by() { + return new ParentOrgRefFinder<>(this); + } + + public ParentOrgRefsAsserter hasTarget(String targetOid) throws ObjectNotFoundException, SchemaException { + return by() + .targetOid(targetOid) + .find() + .end(); + } + + public List getOids() { + List oids = new ArrayList<>(); + for (PrismReferenceValue ref: getRefs()) { + oids.add(ref.getOid()); + } + return oids; + } + +} diff --git a/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/asserter/PrismObjectAsserter.java b/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/asserter/PrismObjectAsserter.java index 6d850afcf0d..cb825cffb19 100644 --- a/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/asserter/PrismObjectAsserter.java +++ b/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/asserter/PrismObjectAsserter.java @@ -21,7 +21,9 @@ import static org.testng.AssertJUnit.assertNull; import static org.testng.AssertJUnit.assertTrue; +import java.util.HashMap; import java.util.List; +import java.util.Map; import javax.xml.namespace.QName; @@ -31,6 +33,9 @@ import com.evolveum.midpoint.prism.util.PrismAsserts; import com.evolveum.midpoint.schema.constants.SchemaConstants; import com.evolveum.midpoint.test.IntegrationTestTools; +import com.evolveum.midpoint.util.exception.ObjectNotFoundException; +import com.evolveum.midpoint.util.exception.SchemaException; +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.PendingOperationType; import com.evolveum.midpoint.xml.ns._public.common.common_3.ShadowType; @@ -39,9 +44,12 @@ * @author semancik * */ -public class PrismObjectAsserter extends AbstractAsserter { +public class PrismObjectAsserter extends AbstractAsserter { private PrismObject object; + + // Cache of focus-related objects: projections, targets, orgs, ... + private Map> objectCache = new HashMap<>(); public PrismObjectAsserter(PrismObject object) { super(); @@ -53,7 +61,7 @@ public PrismObjectAsserter(PrismObject object, String details) { this.object = object; } - public PrismObjectAsserter(PrismObject object, R returnAsserter, String details) { + public PrismObjectAsserter(PrismObject object, RA returnAsserter, String details) { super(returnAsserter, details); this.object = object; } @@ -70,43 +78,50 @@ public static PrismObjectAsserter forObject(Prism return new PrismObjectAsserter<>(shadow, details); } - public PrismObjectAsserter assertOid() { + public PrismObjectAsserter assertOid() { assertNotNull("No OID in "+desc(), getObject().getOid()); return this; } - public PrismObjectAsserter assertOid(String expected) { + public PrismObjectAsserter assertOid(String expected) { assertEquals("Wrong OID in "+desc(), expected, getObject().getOid()); return this; } - public PrismObjectAsserter assertOidDifferentThan(String oid) { + public PrismObjectAsserter assertOidDifferentThan(String oid) { assertFalse("Expected that "+desc()+" will have different OID than "+oid+", but it has the same", oid.equals(getObject().getOid())); return this; } - public PrismObjectAsserter assertName() { + public PrismObjectAsserter assertName() { assertNotNull("No name in "+desc(), getObject().getName()); return this; } - public PrismObjectAsserter assertName(String expectedOrig) { + public PrismObjectAsserter assertName(String expectedOrig) { PrismAsserts.assertEqualsPolyString("Wrong name in "+desc(), expectedOrig, getObject().getName()); return this; } - public PrismObjectAsserter assertDescription(String expected) { + public PrismObjectAsserter assertDescription(String expected) { assertEquals("Wrong description in "+desc(), expected, getObject().asObjectable().getDescription()); return this; } - public PrismObjectAsserter assertLifecycleState(String expected) { + public PrismObjectAsserter assertTenantRef(String expectedOid) { + ObjectReferenceType tenantRef = getObject().asObjectable().getTenantRef(); + assertNotNull("No tenantRef in "+desc(), tenantRef); + assertEquals("Wrong tenantRef OID in "+desc(), expectedOid, tenantRef.getOid()); + return this; + } + + public PrismObjectAsserter assertLifecycleState(String expected) { assertEquals("Wrong lifecycleState in "+desc(), expected, getObject().asObjectable().getLifecycleState()); return this; } - public PrismObjectAsserter assertActiveLifecycleState() { + public PrismObjectAsserter assertActiveLifecycleState() { String actualLifecycleState = getObject().asObjectable().getLifecycleState(); if (actualLifecycleState != null) { assertEquals("Wrong lifecycleState in "+desc(), SchemaConstants.LIFECYCLE_ACTIVE, actualLifecycleState); @@ -118,12 +133,12 @@ protected String desc() { return descWithDetails(object); } - public PrismObjectAsserter display() { + public PrismObjectAsserter display() { display(desc()); return this; } - public PrismObjectAsserter display(String message) { + public PrismObjectAsserter display(String message) { IntegrationTestTools.display(message, object); return this; } @@ -134,8 +149,36 @@ protected void assertPolyStringProperty(QName propName, String expectedOrig) { PrismAsserts.assertEqualsPolyString("Wrong "+propName.getLocalPart()+" in "+desc(), expectedOrig, prop.getRealValue()); } + protected void assertPropertyEquals(QName propName, T expected) { + PrismProperty prop = getObject().findProperty(propName); + assertNotNull("No "+propName.getLocalPart()+" in "+desc(), prop); + T realValue = prop.getRealValue(); + assertNotNull("No value in "+propName.getLocalPart()+" in "+desc(), realValue); + assertEquals("Wrong "+propName.getLocalPart()+" in "+desc(), expected, realValue); + } + public String getOid() { return getObject().getOid(); } + + public ParentOrgRefsAsserter, RA> parentOrgRefs() { + ParentOrgRefsAsserter,RA> asserter = new ParentOrgRefsAsserter<>(this, getDetails()); + copySetupTo(asserter); + return asserter; + } + + public PrismObjectAsserter assertParentOrgRefs(String... expectedOids) { + parentOrgRefs().assertRefs(expectedOids); + return this; + } + + PrismObject getCachedObject(Class type, String oid) throws ObjectNotFoundException, SchemaException { + PrismObject object = (PrismObject) objectCache.get(oid); + if (object == null) { + object = resolveObject(type, oid); + objectCache.put(oid, object); + } + return object; + } } diff --git a/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/asserter/UserAsserter.java b/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/asserter/UserAsserter.java index cf868d8dbdd..ceb8accdb62 100644 --- a/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/asserter/UserAsserter.java +++ b/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/asserter/UserAsserter.java @@ -70,6 +70,9 @@ public static UserAsserter forUser(PrismObject focus, String det return new UserAsserter<>(focus, details); } + // It is insane to override all those methods from superclass. + // But there is no better way to specify something like type in Java. + // This is lesser evil. @Override public UserAsserter assertOid() { super.assertOid(); @@ -106,6 +109,12 @@ public UserAsserter assertDescription(String expected) { return this; } + @Override + public UserAsserter assertTenantRef(String expectedOid) { + super.assertTenantRef(expectedOid); + return this; + } + @Override public UserAsserter assertLifecycleState(String expected) { super.assertLifecycleState(expected); @@ -230,5 +239,16 @@ public UserAsserter assertAssignments(int expected) { return this; } + @Override + public ParentOrgRefsAsserter, RA> parentOrgRefs() { + ParentOrgRefsAsserter, RA> asserter = new ParentOrgRefsAsserter<>(this, getDetails()); + copySetupTo(asserter); + return asserter; + } + @Override + public UserAsserter assertParentOrgRefs(String... expectedOids) { + super.assertParentOrgRefs(expectedOids); + return this; + } } From 70ed77820342b67f896076c6b82378cb5549cefe Mon Sep 17 00:00:00 2001 From: Radovan Semancik Date: Mon, 10 Sep 2018 17:42:10 +0200 Subject: [PATCH 02/13] Automatic update of tenantRef (MID-4882) --- .../model/impl/lens/AssignmentEvaluator.java | 6 ++ .../impl/lens/EvaluatedAssignmentImpl.java | 11 +++- .../model/impl/lens/LensElementContext.java | 6 ++ .../projector/focus/AssignmentProcessor.java | 62 +++++++++++++++++++ .../security/TestSecurityMultitenant.java | 5 +- 5 files changed, 87 insertions(+), 3 deletions(-) diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/AssignmentEvaluator.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/AssignmentEvaluator.java index 53f2323c595..08e34993524 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/AssignmentEvaluator.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/AssignmentEvaluator.java @@ -1088,6 +1088,12 @@ private void collectMembership(FocusType targetType, QName relation, EvaluationC refVal.setTargetName(targetType.getName().toPolyString()); collectMembershipRefVal(refVal, targetType.getClass(), relation, targetType, ctx); + + if (targetType instanceof OrgType) { + if (BooleanUtils.isTrue(((OrgType)targetType).isTenant()) && ctx.evalAssignment.getTenantOid() == null) { + ctx.evalAssignment.setTenantOid(targetType.getOid()); + } + } } private void collectMembership(ObjectReferenceType targetRef, QName relation, EvaluationContext ctx) { diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/EvaluatedAssignmentImpl.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/EvaluatedAssignmentImpl.java index 7244db80455..b41eb8c70f7 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/EvaluatedAssignmentImpl.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/EvaluatedAssignmentImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2017 Evolveum + * 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. @@ -81,6 +81,7 @@ public class EvaluatedAssignmentImpl implements EvaluatedAs // usually, these rules do not cause direct action (e.g. in the case of approvals); // however, there are situations in which they are used (e.g. for exclusion rules) @NotNull private final Collection otherTargetsPolicyRules = new ArrayList<>(); + private String tenantOid; private PrismObject target; private boolean virtual; @@ -245,6 +246,14 @@ public void addDelegationRefVal(PrismReferenceValue org) { delegationRefVals.add(org); } + public String getTenantOid() { + return tenantOid; + } + + public void setTenantOid(String tenantOid) { + this.tenantOid = tenantOid; + } + @NotNull @Override public Collection getAuthorizations() { diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/LensElementContext.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/LensElementContext.java index 3f2ce5e0022..c2060213d35 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/LensElementContext.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/LensElementContext.java @@ -20,6 +20,8 @@ import com.evolveum.midpoint.prism.ConsistencyCheckScope; import com.evolveum.midpoint.prism.Objectable; import com.evolveum.midpoint.prism.delta.PlusMinusZero; +import com.evolveum.midpoint.prism.delta.builder.DeltaBuilder; +import com.evolveum.midpoint.prism.delta.builder.S_ItemEntry; import com.evolveum.midpoint.schema.DeltaConvertor; import com.evolveum.midpoint.schema.result.OperationResult; import com.evolveum.midpoint.schema.util.ObjectTypeUtil; @@ -816,4 +818,8 @@ public boolean isOfType(Class aClass) { } public abstract void deleteSecondaryDeltas(); + + public S_ItemEntry deltaBuilder() throws SchemaException { + return DeltaBuilder.deltaFor(getObjectTypeClass(), getPrismContext()); + } } 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 119282701ae..89836a47b81 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 @@ -29,6 +29,8 @@ import com.evolveum.midpoint.model.impl.lens.projector.policy.PolicyRuleProcessor; import com.evolveum.midpoint.model.impl.util.ModelImplUtils; import com.evolveum.midpoint.xml.ns._public.common.common_3.*; + +import org.apache.commons.lang.BooleanUtils; import org.jetbrains.annotations.NotNull; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; @@ -790,6 +792,66 @@ public void processOrgAssignments(LensContext context, } } } + + computeTenantRef(context, result); + } + + private void computeTenantRef(LensContext context, OperationResult result) throws PolicyViolationException, SchemaException { + String tenantOid = null; + LensFocusContext focusContext = context.getFocusContext(); + PrismObject objectNew = focusContext.getObjectNew(); + if (objectNew == null) { + return; + } + + if (objectNew.canRepresent(OrgType.class) && BooleanUtils.isTrue(((OrgType)objectNew.asObjectable()).isTenant())) { + // Special "zero" case. Tenant org has itself as a tenant. + tenantOid = objectNew.getOid(); + + } else { + + DeltaSetTriple> evaluatedAssignmentTriple = context.getEvaluatedAssignmentTriple(); + for (EvaluatedAssignmentImpl evalAssignment : evaluatedAssignmentTriple.getNonNegativeValues()) { + if (!evalAssignment.isValid()) { + continue; + } + String assignmentTenantOid = evalAssignment.getTenantOid(); + if (assignmentTenantOid == null) { + continue; + } + if (tenantOid == null) { + tenantOid = assignmentTenantOid; + } else { + if (!assignmentTenantOid.equals(tenantOid)) { + throw new PolicyViolationException("Two different tenants ("+tenantOid+", "+assignmentTenantOid+") applicable to "+context.getFocusContext().getHumanReadableName()); + } + } + } + } + + + 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); + } + } else { + if (tenantOid == null) { + LOGGER.trace("Clearing tenantRef"); + ReferenceDelta tenantRefDelta = ReferenceDelta.createModificationReplace(ObjectType.F_TENANT_REF, focusContext.getObjectDefinition(), (PrismReferenceValue)null); + context.getFocusContext().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); + } + } + } } public void checkForAssignmentConflicts(LensContext context, 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 4f6797735cc..05993a1d7a3 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 @@ -201,6 +201,7 @@ public void test010ImportOrgstruct() throws Exception { assertOrgAfter(ORG_ATREIDES_OID) .assertIsTenant() + .assertTenantRef(ORG_ATREIDES_OID) .assignments() .single() .assertTargetOid(ORG_ROOT_OID) @@ -209,8 +210,8 @@ public void test010ImportOrgstruct() throws Exception { .assertLinks(0) .assertParentOrgRefs(ORG_ROOT_OID); - assertRoleAfter(ROLE_CORRINO_ADMIN_OID) -// .assertTenantRef(ORG_ATREIDES_OID) + assertRoleAfter(ROLE_ATREIDES_ADMIN_OID) + .assertTenantRef(ORG_ATREIDES_OID) .assertParentOrgRefs(ORG_ATREIDES_OID); assertGlobalStateUntouched(); From d53a378d75a453fbf7da5a3a2273888dbdc6b1d9 Mon Sep 17 00:00:00 2001 From: Radovan Semancik Date: Wed, 12 Sep 2018 15:11:48 +0200 Subject: [PATCH 03/13] Multitenant authorization improvement (MID-4882) --- .../xml/ns/public/common/common-core-3.xsd | 47 ++++- .../intest/security/AbstractSecurityTest.java | 10 +- .../security/TestSecurityMultitenant.java | 196 +++++++++++++++--- .../src/test/resources/logback-test.xml | 16 +- .../security/multitenant/org-multitenant.xml | 166 +++++++++++++-- .../test/asserter/AssignmentFinder.java | 2 +- .../test/asserter/AssignmentsAsserter.java | 9 + .../midpoint/test/asserter/OrgAsserter.java | 5 + .../test/asserter/PrismObjectAsserter.java | 6 + .../enforcer/impl/SecurityEnforcerImpl.java | 79 ++++++- 10 files changed, 475 insertions(+), 61 deletions(-) diff --git a/infra/schema/src/main/resources/xml/ns/public/common/common-core-3.xsd b/infra/schema/src/main/resources/xml/ns/public/common/common-core-3.xsd index d73f41b54e4..6efe427b9d1 100755 --- a/infra/schema/src/main/resources/xml/ns/public/common/common-core-3.xsd +++ b/infra/schema/src/main/resources/xml/ns/public/common/common-core-3.xsd @@ -12380,6 +12380,9 @@ LIMITATION: Supported only for TaskType and not for search pre-processing. + + 3.9 + @@ -12391,9 +12394,12 @@ LIMITATION: Supported only for TaskType with work items fully fetched; and not for search pre-processing. + + 3.9 + - + The object matches the specification if it has a related object specified by this element. @@ -12401,6 +12407,19 @@ LIMITATION: Supported only for TaskType; and for search pre-processing it is limited to "self". + + 3.9 + + + + + + + The object matches the specification if it is related to the specified tenant. + + + 3.9 + @@ -12446,6 +12465,32 @@ + + + + Selects an object by comparing tenant information. + + + + 3.9 + + + + + + + Selects object/target if it has the same tenant as subject. + Subject must be part of the tenant (must have tenantRef set). + This authorization will not select any object if subject tenantRef is empty. + + + + + + + + + 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 4821aced12f..c6dd4cd89e0 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 @@ -663,10 +663,12 @@ protected void cleanupAutzTest(String userOid, int expectedAssignments) throws O assumeAssignmentPolicy(AssignmentPolicyEnforcementType.RELATIVE); - PrismObject user = getUser(userOid); - assertAssignments(user, expectedAssignments); - if (expectedAssignments == 0) { - assertLinks(user, 0); + if (userOid != null) { + PrismObject user = getUser(userOid); + assertAssignments(user, expectedAssignments); + if (expectedAssignments == 0) { + assertLinks(user, 0); + } } } 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 05993a1d7a3..907034a9e47 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 @@ -105,26 +105,38 @@ public class TestSecurityMultitenant extends AbstractSecurityTest { protected static final File ORG_MULTITENANT_FILE = new File(TEST_DIR, "org-multitenant.xml"); - protected static final String ORG_ROOT_OID = "00000000-8888-6666-1111-000000000000"; + 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"; - protected static final String ORG_CORRINO_OID = "00000000-8888-6666-1111-000000001000"; - protected static final String ROLE_CORRINO_ADMIN_OID = "00000000-8888-6666-1111-100000001000"; + 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"; + protected static final String USER_EDRIC_OID = "00000000-8888-6666-a001-200000000000"; + protected static final String USER_EDRIC_NAME = "edric"; + protected static final String USER_EDRIC_FULL_NAME = "Navigator Edric"; - protected static final String ORG_ATREIDES_OID = "00000000-8888-6666-1111-000000002000"; - protected static final String ROLE_ATREIDES_ADMIN_OID = "00000000-8888-6666-1111-100000002000"; + 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"; + protected static final String USER_SHADDAM_CORRINO_OID = "00000000-8888-6666-a100-200000000000"; + protected static final String USER_SHADDAM_CORRINO_NAME = "shaddam"; + protected static final String USER_SHADDAM_CORRINO_FULL_NAME = "Padishah Emperor Shaddam IV"; - protected static final String ORG_HARKONNEN_OID = "00000000-8888-6666-1111-000000003000"; - protected static final String ROLE_HARKONNEN_ADMIN_OID = "00000000-8888-6666-1111-100000003000"; + 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"; + protected static final String USER_LETO_ATREIDES_OID = "00000000-8888-6666-a200-200000000000"; + protected static final String USER_LETO_ATREIDES_NAME = "leto"; + protected static final String USER_LETO_ATREIDES_FULL_NAME = "Duke Leto Atreides"; + protected static final String USER_PAUL_ATREIDES_OID = "00000000-8888-6666-a200-200000000001"; + protected static final String USER_PAUL_ATREIDES_NAME = "paul"; + protected static final String USER_PAUL_ATREIDES_FULL_NAME = "Paul Atreides"; -// protected static final File ROLE_X_FILE = new File(TEST_DIR, "role-vault-dweller.xml"); -// protected static final String ROLE_X_OID = "8d8471f4-2906-11e8-9078-4f2b205aa01d"; + 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"; + protected static final String USER_VLADIMIR_HARKONNEN_OID = "00000000-8888-6666-a300-200000000000"; @Override public void initSystem(Task initTask, OperationResult initResult) throws Exception { super.initSystem(initTask, initResult); -// repoAddObjectFromFile(ROLE_VAULT_DWELLER_FILE, initResult); - assumeAssignmentPolicy(AssignmentPolicyEnforcementType.RELATIVE); } @@ -174,6 +186,9 @@ public void test000Sanity() throws Exception { /** * Stay logged in as administrator. * Import orgstruct with tenant and roles and everything. + * Make sure that tenantRefs are properly set (they are NOT part of imported file) + * + * MID-4882 */ @Test public void test010ImportOrgstruct() throws Exception { @@ -214,41 +229,168 @@ public void test010ImportOrgstruct() throws Exception { .assertTenantRef(ORG_ATREIDES_OID) .assertParentOrgRefs(ORG_ATREIDES_OID); + assertUserAfter(USER_LETO_ATREIDES_OID) + .assertName(USER_LETO_ATREIDES_NAME) + .assertFullName(USER_LETO_ATREIDES_FULL_NAME) + .assignments() + .assertOrg(ORG_ATREIDES_OID) + .assertRole(ROLE_ATREIDES_ADMIN_OID) + .end() + .assertTenantRef(ORG_ATREIDES_OID) + .assertParentOrgRefs(ORG_ATREIDES_OID) + .assertLinks(0); + + assertUserAfter(USER_PAUL_ATREIDES_OID) + .assertName(USER_PAUL_ATREIDES_NAME) + .assertFullName(USER_PAUL_ATREIDES_FULL_NAME) + .assignments() + .assertOrg(ORG_ATREIDES_OID) + .assertNoRole() + .end() + .assertTenantRef(ORG_ATREIDES_OID) + .assertParentOrgRefs(ORG_ATREIDES_OID) + .assertLinks(0); + + assertOrgAfter(ORG_GUILD_OID) + .assertTenant(null) + .assertTenantRef(null) + .assignments() + .single() + .assertTargetOid(ORG_ROOT_OID) + .end() + .end() + .assertLinks(0) + .assertParentOrgRefs(ORG_ROOT_OID); + + assertUserAfter(USER_EDRIC_OID) + .assertName(USER_EDRIC_NAME) + .assertFullName(USER_EDRIC_FULL_NAME) + .assignments() + .assertOrg(ORG_GUILD_OID) + .assertRole(ROLE_GUILD_BROKEN_ADMIN_OID) + .end() + .assertTenantRef(null) + .assertParentOrgRefs(ORG_GUILD_OID) + .assertLinks(0); + assertGlobalStateUntouched(); } /** + * Leto is Atreides admin. He can see all of House Atreides. + * But nothing else. + * + * MID-4882 */ - @Test(enabled=false) // work in progress - public void test080AutzJackEndUserPassword() throws Exception { - final String TEST_NAME = "test080AutzJackEndUserPassword"; + @Test + public void test100AutzLetoRead() throws Exception { + final String TEST_NAME = "test100AutzLetoRead"; displayTestTitle(TEST_NAME); // GIVEN - cleanupAutzTest(USER_JACK_OID); + cleanupAutzTest(null); + + login(USER_LETO_ATREIDES_NAME); + + // WHEN + displayWhen(TEST_NAME); -// assignRole(USER_JACK_OID, ROLE_END_USER_OID); + // Matching tenant + assertGetAllow(UserType.class, USER_LETO_ATREIDES_OID); + assertGetAllow(UserType.class, USER_PAUL_ATREIDES_OID); + assertGetAllow(OrgType.class, ORG_ATREIDES_OID); + assertGetAllow(RoleType.class, ROLE_ATREIDES_ADMIN_OID); + + // Wrong tenant + assertGetDeny(UserType.class, USER_VLADIMIR_HARKONNEN_OID); + assertGetDeny(OrgType.class, ORG_HARKONNEN_OID); + assertGetDeny(RoleType.class, ROLE_HARKONNEN_ADMIN_OID); - login(USER_JACK_USERNAME); + // No tenant + assertGetDeny(OrgType.class, ORG_GUILD_OID); + assertGetDeny(RoleType.class, ROLE_TENANT_ADMIN_OID); + assertGetDeny(UserType.class, USER_EDRIC_OID); + + assertSearch(UserType.class, null, USER_LETO_ATREIDES_OID, USER_PAUL_ATREIDES_OID); + assertSearch(RoleType.class, null, ROLE_ATREIDES_ADMIN_OID); + assertSearch(OrgType.class, null, ORG_ATREIDES_OID); + + // THEN + displayThen(TEST_NAME); + + assertGlobalStateUntouched(); + } + + /** + * MID-4882 + */ + @Test + public void test102AutzLetoModify() throws Exception { + final String TEST_NAME = "test102AutzLetoModify"; + displayTestTitle(TEST_NAME); + // GIVEN + cleanupAutzTest(null); + + login(USER_LETO_ATREIDES_NAME); // WHEN displayWhen(TEST_NAME); + + // Matching tenant + assertModifyAllow(UserType.class, USER_PAUL_ATREIDES_OID, UserType.F_LOCALITY, createPolyString("Arrakis")); -// assertAllow("set jack's password", -// (task, result) -> modifyUserSetPassword(USER_JACK_OID, "nbusr123", task, result) ); + // Wrong tenant + assertModifyDeny(UserType.class, USER_VLADIMIR_HARKONNEN_OID, UserType.F_LOCALITY, createPolyString("Deepest hell")); + + // No tenant + assertModifyDeny(UserType.class, USER_EDRIC_OID, UserType.F_LOCALITY, createPolyString("Whatever")); // THEN displayThen(TEST_NAME); -// XMLGregorianCalendar endTs = clock.currentTimeXMLGregorianCalendar(); -// -// user = getUser(USER_JACK_OID); -// display("user after password change", user); -// PasswordType passwordType = assertUserPassword(user, "nbusr123"); -// MetadataType metadata = passwordType.getMetadata(); -// assertNotNull("No password metadata", metadata); -// assertMetadata("password metadata", metadata, true, false, startTs, endTs, USER_JACK_OID, SchemaConstants.CHANNEL_GUI_USER_URI); + assertGlobalStateUntouched(); + } + + /** + * Edric is part of Spacing Guld. But the Guild is not tenant. + * Edric has a broken role that should work only for tenants. + * Therefore the role should not work. Edric should not be + * able to access anything. + * + * MID-4882 + */ + @Test + public void test120AutzEdricRead() throws Exception { + final String TEST_NAME = "test120AutzEdricRead"; + displayTestTitle(TEST_NAME); + // GIVEN + cleanupAutzTest(null); + login(USER_EDRIC_NAME); + + // WHEN + displayWhen(TEST_NAME); + + // Wrong tenant + assertGetDeny(UserType.class, USER_LETO_ATREIDES_OID); + assertGetDeny(UserType.class, USER_PAUL_ATREIDES_OID); + assertGetDeny(OrgType.class, ORG_ATREIDES_OID); + assertGetDeny(RoleType.class, ROLE_ATREIDES_ADMIN_OID); + + // No tenant + assertGetDeny(OrgType.class, ORG_GUILD_OID); + assertGetDeny(RoleType.class, ROLE_TENANT_ADMIN_OID); + assertGetDeny(UserType.class, USER_EDRIC_OID); + + assertSearch(UserType.class, null, 0); + assertSearch(RoleType.class, null, 0); + assertSearch(OrgType.class, null, 0); + + // THEN + displayThen(TEST_NAME); + assertGlobalStateUntouched(); } + + } diff --git a/model/model-intest/src/test/resources/logback-test.xml b/model/model-intest/src/test/resources/logback-test.xml index 648dc2323dc..5259eeb0957 100644 --- a/model/model-intest/src/test/resources/logback-test.xml +++ b/model/model-intest/src/test/resources/logback-test.xml @@ -47,7 +47,7 @@ - + @@ -55,9 +55,9 @@ - + - + @@ -68,7 +68,7 @@ - + @@ -97,17 +97,17 @@ - + - + - + - + diff --git a/model/model-intest/src/test/resources/security/multitenant/org-multitenant.xml b/model/model-intest/src/test/resources/security/multitenant/org-multitenant.xml index 9728c3f61f7..b1a8e6d5a6c 100644 --- a/model/model-intest/src/test/resources/security/multitenant/org-multitenant.xml +++ b/model/model-intest/src/test/resources/security/multitenant/org-multitenant.xml @@ -16,71 +16,201 @@ --> + xmlns:org='http://midpoint.evolveum.com/xml/ns/public/common/org-3' + xmlns:q="http://prism.evolveum.com/xml/ns/public/query-3"> - + 0000 Root of the multitenant structure tenancy Multitenant root - 0000 + 000 + + + Tenant Admin Role + + + tenant admin autz + http://midpoint.evolveum.com/xml/ns/public/security/authorization-model-3#read + http://midpoint.evolveum.com/xml/ns/public/security/authorization-model-3#add + http://midpoint.evolveum.com/xml/ns/public/security/authorization-model-3#modify + http://midpoint.evolveum.com/xml/ns/public/security/authorization-model-3#delete + + + true + + + tenant + tenantRef + + + - + + + + Spacing Guild + Organizational structure, but not a tenant + + + + Spacing Guild + 001 + + + + Broken Spacing Guild Admin + Spacing guild administrator that does not really work. + + + + + + + + + + + edric + Edric + Navigator Edric + Navigator + + + + + + + + + + + + Corrino tenancy - + House Corrino - 1000 + 100 Kaitain true - + Corrino Admin - + + + + - + + shaddam + Shaddam + Corrino + Padishah Emperor Shaddam IV + Padishah Emperor + IV + + + + + + + + + + + + Atreides tenancy - + House Atreides - 2000 + 200 Caladan true - + Atreides Admin - + + + + - + + leto + Leto + Atreides + Duke Leto Atreides + Duke + + + + + + + + + + paul + Paul + Atreides + Paul Atreides + + + + + + + + + Harkonnen tenancy - + House Harkonnen - 3000 + 300 Giedi Prime true - + Harkonnen Admin - + + + + + + + baronharkonnen + Vladimir + Harkonnen + Baron Vladimir Harkonnen + Baron + + + + + + + diff --git a/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/asserter/AssignmentFinder.java b/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/asserter/AssignmentFinder.java index a0ab0803add..5bf18933784 100644 --- a/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/asserter/AssignmentFinder.java +++ b/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/asserter/AssignmentFinder.java @@ -116,7 +116,7 @@ private boolean matches(AssignmentType assignment, PrismObject targetObject) } } - if (targetObjectType != null) { + if (targetType != null) { if (targetRef == null || !QNameUtil.match(targetType, targetRef.getType())) { return false; } diff --git a/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/asserter/AssignmentsAsserter.java b/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/asserter/AssignmentsAsserter.java index 1a4637a2c98..cda79778e7b 100644 --- a/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/asserter/AssignmentsAsserter.java +++ b/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/asserter/AssignmentsAsserter.java @@ -33,6 +33,7 @@ import com.evolveum.midpoint.util.exception.SchemaException; import com.evolveum.midpoint.xml.ns._public.common.common_3.AssignmentType; import com.evolveum.midpoint.xml.ns._public.common.common_3.FocusType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.OrgType; import com.evolveum.midpoint.xml.ns._public.common.common_3.PendingOperationExecutionStatusType; import com.evolveum.midpoint.xml.ns._public.common.common_3.PendingOperationType; import com.evolveum.midpoint.xml.ns._public.common.common_3.RoleType; @@ -137,5 +138,13 @@ public AssignmentsAsserter assertNoRole() throws ObjectNotFoundExceptio .assertNone(); return this; } + + public AssignmentsAsserter assertOrg(String orgOid) throws ObjectNotFoundException, SchemaException { + by() + .targetOid(orgOid) + .targetType(OrgType.COMPLEX_TYPE) + .find(); + return this; + } } diff --git a/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/asserter/OrgAsserter.java b/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/asserter/OrgAsserter.java index caa62a04615..962396b727c 100644 --- a/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/asserter/OrgAsserter.java +++ b/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/asserter/OrgAsserter.java @@ -192,6 +192,11 @@ public OrgAsserter assertLocality(String expectedOrig) { return this; } + public OrgAsserter assertTenant(Boolean expected) { + assertPropertyEquals(OrgType.F_TENANT, expected); + return this; + } + public OrgAsserter assertIsTenant() { assertPropertyEquals(OrgType.F_TENANT, true); return this; diff --git a/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/asserter/PrismObjectAsserter.java b/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/asserter/PrismObjectAsserter.java index cb825cffb19..ea02e5543f1 100644 --- a/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/asserter/PrismObjectAsserter.java +++ b/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/asserter/PrismObjectAsserter.java @@ -111,6 +111,9 @@ public PrismObjectAsserter assertDescription(String expected) { public PrismObjectAsserter assertTenantRef(String expectedOid) { ObjectReferenceType tenantRef = getObject().asObjectable().getTenantRef(); + if (tenantRef == null && expectedOid == null) { + return this; + } assertNotNull("No tenantRef in "+desc(), tenantRef); assertEquals("Wrong tenantRef OID in "+desc(), expectedOid, tenantRef.getOid()); return this; @@ -151,6 +154,9 @@ protected void assertPolyStringProperty(QName propName, String expectedOrig) { protected void assertPropertyEquals(QName propName, T expected) { PrismProperty prop = getObject().findProperty(propName); + if (prop == null && expected == null) { + return; + } assertNotNull("No "+propName.getLocalPart()+" in "+desc(), prop); T realValue = prop.getRealValue(); assertNotNull("No value in "+propName.getLocalPart()+" in "+desc(), realValue); diff --git a/repo/security-enforcer-impl/src/main/java/com/evolveum/midpoint/security/enforcer/impl/SecurityEnforcerImpl.java b/repo/security-enforcer-impl/src/main/java/com/evolveum/midpoint/security/enforcer/impl/SecurityEnforcerImpl.java index ac6a3989a37..5149bb9a09a 100644 --- a/repo/security-enforcer-impl/src/main/java/com/evolveum/midpoint/security/enforcer/impl/SecurityEnforcerImpl.java +++ b/repo/security-enforcer-impl/src/main/java/com/evolveum/midpoint/security/enforcer/impl/SecurityEnforcerImpl.java @@ -105,6 +105,7 @@ import com.evolveum.midpoint.xml.ns._public.common.common_3.SpecialObjectSpecificationType; import com.evolveum.midpoint.xml.ns._public.common.common_3.SubjectedObjectSelectorType; import com.evolveum.midpoint.xml.ns._public.common.common_3.TaskType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.TenantSelectorType; import com.evolveum.midpoint.xml.ns._public.common.common_3.UserType; import com.evolveum.prism.xml.ns._public.query_3.SearchFilterType; import com.evolveum.prism.xml.ns._public.types_3.ItemPathType; @@ -692,7 +693,34 @@ private boolean isApplicable(SubjectedObjectSelectorType return false; } } + } + // Tenant + TenantSelectorType tenantSpec = ((OwnedObjectSelectorType)objectSelector).getTenant(); + if (tenantSpec != null) { + if (BooleanUtils.isTrue(tenantSpec.isSameAsSubject())) { + ObjectReferenceType subjectTenantRef = principal.getUser().getTenantRef(); + if (subjectTenantRef == null || subjectTenantRef.getOid() == null) { + LOGGER.trace(" {}: tenant object spec not applicable for {}, object OID {} because subject does not have tenantRef", + autzHumanReadableDesc, desc, object.getOid()); + return false; + } + ObjectReferenceType objectTenantRef = object.asObjectable().getTenantRef(); + if (objectTenantRef == null || objectTenantRef.getOid() == null) { + LOGGER.trace(" {}: tenant object spec not applicable for {}, object OID {} because object does not have tenantRef", + autzHumanReadableDesc, desc, object.getOid()); + return false; + } + if (!subjectTenantRef.getOid().equals(objectTenantRef.getOid())) { + LOGGER.trace(" {}: tenant object spec not applicable for {}, object OID {} because of tenant mismatch", + autzHumanReadableDesc, desc, object.getOid()); + return false; + } + } + } else { + LOGGER.trace(" {}: tenant object spec not applicable for {}, object OID {} because there is a strange tenant specificaiton in authorization", + autzHumanReadableDesc, desc, object.getOid()); + return false; } } @@ -1137,6 +1165,7 @@ private ObjectFilter preProcessObje ObjectReferenceType specOrgRef = objectSpecType.getOrgRef(); OrgRelationObjectSpecificationType specOrgRelation = objectSpecType.getOrgRelation(); RoleRelationObjectSpecificationType specRoleRelation = objectSpecType.getRoleRelation(); + TenantSelectorType specTenant = objectSpecType.getTenant(); QName specTypeQName = objectSpecType.getType(); PrismObjectDefinition objectDefinition = null; @@ -1204,8 +1233,8 @@ private ObjectFilter preProcessObje LOGGER.trace(" Skipping authorization, because specials are present: {}", specSpecial); applicable = false; } - if (specFilterType != null || specOrgRef != null || specOrgRelation != null || specRoleRelation != null) { - throw new SchemaException("Both filter/org/role and special object specification specified in authorization"); + if (specFilterType != null || specOrgRef != null || specOrgRelation != null || specRoleRelation != null || specTenant != null) { + throw new SchemaException("Both filter/org/role/tenant and special object specification specified in authorization"); } ObjectFilter specialFilter = null; for (SpecialObjectSpecificationType special: specSpecial) { @@ -1299,6 +1328,30 @@ private ObjectFilter preProcessObje LOGGER.trace(" roleRelation empty"); } + if (objSpecTypeFilter != null) { + objSpecTypeFilter.setFilter(objSpecSecurityFilter); + objSpecSecurityFilter = objSpecTypeFilter; + } + + // tenant + if (specTenant != null) { + ObjectFilter objSpecTenantFilter = processTenantFilter(principal, autz, specTenant, queryItemsSpec, origFilter); + if (objSpecTenantFilter == null) { + if (autz.maySkipOnSearch()) { + LOGGER.trace(" not applying tenant filter {} because it is not efficient and maySkipOnSearch is set", objSpecTenantFilter); + applicable = false; + } else { + objSpecTenantFilter = NoneFilter.createNone(); + } + } + if (objSpecTenantFilter != null) { + objSpecSecurityFilter = ObjectQueryUtil.filterAnd(objSpecSecurityFilter, objSpecTenantFilter); + LOGGER.trace(" applying tenant filter {}", objSpecTenantFilter); + } + } else { + LOGGER.trace(" roleRelation empty"); + } + if (objSpecTypeFilter != null) { objSpecTypeFilter.setFilter(objSpecSecurityFilter); objSpecSecurityFilter = objSpecTypeFilter; @@ -1504,6 +1557,28 @@ private ObjectFilter processRoleRelationFilter(MidPointPrincipal principal, Auth return ObjectQueryUtil.filterOr(refRoleFilter, membersFilter); } + + private ObjectFilter processTenantFilter(MidPointPrincipal principal, Authorization autz, + TenantSelectorType specTenant, AutzItemPaths queryItemsSpec, ObjectFilter origFilter) { + ObjectFilter tenantFilter = null; + if (BooleanUtils.isTrue(specTenant.isSameAsSubject())) { + ObjectReferenceType subjectTenantRef = principal.getUser().getTenantRef(); + if (subjectTenantRef == null || subjectTenantRef.getOid() == null) { + LOGGER.trace(" subject tenant empty (none filter)"); + tenantFilter = NoneFilter.createNone(); + } else { + tenantFilter = QueryBuilder.queryFor(ObjectType.class, prismContext) + .item(ObjectType.F_TENANT_REF).ref(subjectTenantRef.getOid()) + .buildFilter(); + LOGGER.trace(" applying tenant filter {}", tenantFilter); + } + } else { + tenantFilter = NoneFilter.createNone(); + LOGGER.trace(" tenant authorization empty (none filter)"); + } + + return tenantFilter; + } private List getRoleOidsFromFilter(ObjectFilter origFilter) { if (origFilter == null) { From a337f1fb898d84cbc827aa8dc16c803ca2dbb8e4 Mon Sep 17 00:00:00 2001 From: Pavol Mederly Date: Wed, 12 Sep 2018 16:35:26 +0200 Subject: [PATCH 04/13] Avoid exception on iterative search with paging When searchObjectsIterative is called in state that implicitly results in iteration based on strictly sequential paging, and an incompatible custom paging is requested, we now issue a warning and switch to SIMPLE_PAGING iteration. (Originally we thrown an exception there.) --- .../evolveum/midpoint/repo/sql/SqlRepositoryServiceImpl.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/SqlRepositoryServiceImpl.java b/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/SqlRepositoryServiceImpl.java index 36c83cdaf80..258bc4bef97 100644 --- a/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/SqlRepositoryServiceImpl.java +++ b/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/SqlRepositoryServiceImpl.java @@ -826,11 +826,10 @@ public SearchResultMetadata searchObjectsIterative(Class< if (isCustomPagingOkWithPagedSeqIteration(query)) { iterationMethod = IterationMethodType.STRICTLY_SEQUENTIAL_PAGING; } else { - // TODO switch to LOGGER.error - throw new IllegalArgumentException("Iterative search was defined in the repository configuration, and strict sequentiality " + LOGGER.warn("Iterative search was defined in the repository configuration, and strict sequentiality " + "was requested. However, a custom paging precludes its application. Therefore switching to " + "simple paging iteration method. Paging requested: " + query.getPaging()); - //iterationMethod = IterationMethodType.SIMPLE_PAGING; + iterationMethod = IterationMethodType.SIMPLE_PAGING; } } else { iterationMethod = IterationMethodType.SIMPLE_PAGING; From db05d929e74856879b2a92df0747e1ef496385e9 Mon Sep 17 00:00:00 2001 From: Pavol Mederly Date: Wed, 12 Sep 2018 17:32:04 +0200 Subject: [PATCH 05/13] Add system config change listeners These listeners should implement SystemConfigurationChangeListener interface and be registered on a bean of SystemConfigurationChangeDispatcher type. They will be updated with a current value of system config object. --- ...temConfigurationChangeDispatcherImpl.java} | 15 ++++-- .../test/resources/ctx-model-test-no-repo.xml | 2 +- .../api/SystemConfigurationChangeApplier.java | 29 ---------- .../SystemConfigurationChangeDispatcher.java | 53 +++++++++++++++++++ .../SystemConfigurationChangeListener.java | 37 +++++++++++++ .../SystemConfigurationCacheableAdapter.java | 8 +-- .../repo/sql/SqlRepositoryServiceImpl.java | 4 +- ...temConfigurationChangeDispatcherImpl.java} | 45 ++++++++++++++-- .../quartzimpl/TaskManagerQuartzImpl.java | 8 +-- .../quartzimpl/cluster/ClusterManager.java | 2 +- 10 files changed, 154 insertions(+), 49 deletions(-) rename model/model-impl/src/test/java/com/evolveum/midpoint/model/impl/controller/{DummySystemConfigurationChangeApplierImpl.java => DummySystemConfigurationChangeDispatcherImpl.java} (58%) delete mode 100644 repo/repo-api/src/main/java/com/evolveum/midpoint/repo/api/SystemConfigurationChangeApplier.java create mode 100644 repo/repo-api/src/main/java/com/evolveum/midpoint/repo/api/SystemConfigurationChangeDispatcher.java create mode 100644 repo/repo-api/src/main/java/com/evolveum/midpoint/repo/api/SystemConfigurationChangeListener.java rename repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/{SystemConfigurationChangeApplierImpl.java => SystemConfigurationChangeDispatcherImpl.java} (82%) diff --git a/model/model-impl/src/test/java/com/evolveum/midpoint/model/impl/controller/DummySystemConfigurationChangeApplierImpl.java b/model/model-impl/src/test/java/com/evolveum/midpoint/model/impl/controller/DummySystemConfigurationChangeDispatcherImpl.java similarity index 58% rename from model/model-impl/src/test/java/com/evolveum/midpoint/model/impl/controller/DummySystemConfigurationChangeApplierImpl.java rename to model/model-impl/src/test/java/com/evolveum/midpoint/model/impl/controller/DummySystemConfigurationChangeDispatcherImpl.java index 4d929e1513d..072c13021b3 100644 --- a/model/model-impl/src/test/java/com/evolveum/midpoint/model/impl/controller/DummySystemConfigurationChangeApplierImpl.java +++ b/model/model-impl/src/test/java/com/evolveum/midpoint/model/impl/controller/DummySystemConfigurationChangeDispatcherImpl.java @@ -16,15 +16,24 @@ package com.evolveum.midpoint.model.impl.controller; -import com.evolveum.midpoint.repo.api.SystemConfigurationChangeApplier; +import com.evolveum.midpoint.repo.api.SystemConfigurationChangeDispatcher; +import com.evolveum.midpoint.repo.api.SystemConfigurationChangeListener; import com.evolveum.midpoint.schema.result.OperationResult; /** * @author mederly */ -public class DummySystemConfigurationChangeApplierImpl implements SystemConfigurationChangeApplier { +public class DummySystemConfigurationChangeDispatcherImpl implements SystemConfigurationChangeDispatcher { @Override - public void applySystemConfiguration(boolean ignoreVersion, boolean allowNotFound, OperationResult result) { + public void dispatch(boolean ignoreVersion, boolean allowNotFound, OperationResult result) { + } + + @Override + public void registerListener(SystemConfigurationChangeListener listener) { + } + + @Override + public void unregisterListener(SystemConfigurationChangeListener listener) { } } diff --git a/model/model-impl/src/test/resources/ctx-model-test-no-repo.xml b/model/model-impl/src/test/resources/ctx-model-test-no-repo.xml index 8064c04cc84..7a70b7f20d8 100644 --- a/model/model-impl/src/test/resources/ctx-model-test-no-repo.xml +++ b/model/model-impl/src/test/resources/ctx-model-test-no-repo.xml @@ -45,6 +45,6 @@ - + diff --git a/repo/repo-api/src/main/java/com/evolveum/midpoint/repo/api/SystemConfigurationChangeApplier.java b/repo/repo-api/src/main/java/com/evolveum/midpoint/repo/api/SystemConfigurationChangeApplier.java deleted file mode 100644 index 82baa586797..00000000000 --- a/repo/repo-api/src/main/java/com/evolveum/midpoint/repo/api/SystemConfigurationChangeApplier.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * 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. - */ - -package com.evolveum.midpoint.repo.api; - -import com.evolveum.midpoint.schema.result.OperationResult; -import com.evolveum.midpoint.util.exception.SchemaException; - -/** - * @author mederly - */ -public interface SystemConfigurationChangeApplier { - - void applySystemConfiguration(boolean ignoreVersion, boolean allowNotFound, OperationResult result) throws SchemaException; - -} diff --git a/repo/repo-api/src/main/java/com/evolveum/midpoint/repo/api/SystemConfigurationChangeDispatcher.java b/repo/repo-api/src/main/java/com/evolveum/midpoint/repo/api/SystemConfigurationChangeDispatcher.java new file mode 100644 index 00000000000..494715733e5 --- /dev/null +++ b/repo/repo-api/src/main/java/com/evolveum/midpoint/repo/api/SystemConfigurationChangeDispatcher.java @@ -0,0 +1,53 @@ +/* + * 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. + */ + +package com.evolveum.midpoint.repo.api; + +import com.evolveum.midpoint.schema.result.OperationResult; +import com.evolveum.midpoint.util.exception.SchemaException; + +/** + * Central point of dispatching notifications about changes to the system configuration object. + * + * @author mederly + */ +public interface SystemConfigurationChangeDispatcher { + + /** + * Dispatches information on system configuration object change. + * + * Basically this directly pushes information to lower layers (prism, schema, repo, etc), and calls registered + * listeners that originate in upper layers. + * + * @param ignoreVersion If false, the information is dispatched unconditionally. If true, we dispatch the notification only + * if the system configuration version was really changed. This is to easily support sources that + * "ping" sysconfig object in regular intervals, e.g. the cluster manager thread. + * @param allowNotFound If true, we take non-existence of sysconfig object more easily. To be used e.g. on system init or + * during tests execution. + */ + void dispatch(boolean ignoreVersion, boolean allowNotFound, OperationResult result) throws SchemaException; + + /** + * Registers a listener that will be updated on system configuration object changes. + */ + void registerListener(SystemConfigurationChangeListener listener); + + /** + * Unregisters a listener. + */ + void unregisterListener(SystemConfigurationChangeListener listener); + +} diff --git a/repo/repo-api/src/main/java/com/evolveum/midpoint/repo/api/SystemConfigurationChangeListener.java b/repo/repo-api/src/main/java/com/evolveum/midpoint/repo/api/SystemConfigurationChangeListener.java new file mode 100644 index 00000000000..69008ef88ff --- /dev/null +++ b/repo/repo-api/src/main/java/com/evolveum/midpoint/repo/api/SystemConfigurationChangeListener.java @@ -0,0 +1,37 @@ +/* + * 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. + */ + +package com.evolveum.midpoint.repo.api; + +import com.evolveum.midpoint.xml.ns._public.common.common_3.SystemConfigurationType; +import org.jetbrains.annotations.Nullable; + +/** + * Listener that needs to receive notifications related to system configuration object changes. + */ +public interface SystemConfigurationChangeListener { + /** + * Updates the listener's internal state with the configuration provided. + * + * @param value Current value of the system configuration object. It is 'null' if the object does not exist. + * Usually listeners keep their current state in such cases, but if needed, it will have the information + * about missing sysconfig object, so it could act accordingly. + * + * @return false if the update was not successful, so it needs to be repeated. The same effect is when + * a runtime exception is thrown. + */ + boolean update(@Nullable SystemConfigurationType value); +} diff --git a/repo/repo-common/src/main/java/com/evolveum/midpoint/repo/common/SystemConfigurationCacheableAdapter.java b/repo/repo-common/src/main/java/com/evolveum/midpoint/repo/common/SystemConfigurationCacheableAdapter.java index e27588958d3..074aedc6599 100644 --- a/repo/repo-common/src/main/java/com/evolveum/midpoint/repo/common/SystemConfigurationCacheableAdapter.java +++ b/repo/repo-common/src/main/java/com/evolveum/midpoint/repo/common/SystemConfigurationCacheableAdapter.java @@ -16,7 +16,7 @@ package com.evolveum.midpoint.repo.common; -import com.evolveum.midpoint.repo.api.SystemConfigurationChangeApplier; +import com.evolveum.midpoint.repo.api.SystemConfigurationChangeDispatcher; import com.evolveum.midpoint.schema.result.OperationResult; import com.evolveum.midpoint.util.logging.LoggingUtils; import com.evolveum.midpoint.util.logging.Trace; @@ -35,7 +35,7 @@ public class SystemConfigurationCacheableAdapter implements Cacheable { private static final Trace LOGGER = TraceManager.getTrace(SystemConfigurationCacheableAdapter.class); @Autowired private CacheRegistry cacheRegistry; - @Autowired private SystemConfigurationChangeApplier changeApplier; + @Autowired private SystemConfigurationChangeDispatcher changeDispatcher; @PostConstruct public void register() { @@ -46,9 +46,9 @@ public void register() { public void clearCache() { try { OperationResult result = new OperationResult(SystemConfigurationCacheableAdapter.class.getName() + ".clearCache"); - changeApplier.applySystemConfiguration(true, true, result); + changeDispatcher.dispatch(true, true, result); } catch (Throwable t) { - LoggingUtils.logUnexpectedException(LOGGER, "Couldn't apply updated system configuration", t); + LoggingUtils.logUnexpectedException(LOGGER, "Couldn't dispatch information about updated system configuration", t); } } diff --git a/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/SqlRepositoryServiceImpl.java b/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/SqlRepositoryServiceImpl.java index 258bc4bef97..775a84e4a55 100644 --- a/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/SqlRepositoryServiceImpl.java +++ b/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/SqlRepositoryServiceImpl.java @@ -124,7 +124,7 @@ public class SqlRepositoryServiceImpl extends SqlBaseService implements Reposito @Autowired private MidpointConfiguration midpointConfiguration; @Autowired private PrismContext prismContext; @Autowired private RelationRegistry relationRegistry; - @Autowired private SystemConfigurationChangeApplier systemConfigurationChangeApplier; + @Autowired private SystemConfigurationChangeDispatcher systemConfigurationChangeDispatcher; private final ThreadLocal> conflictWatchersThreadLocal = new ThreadLocal<>(); @@ -1144,7 +1144,7 @@ public FullTextSearchConfigurationType getFullTextSearchConfiguration() { @Override public void postInit(OperationResult result) throws SchemaException { LOGGER.debug("Executing repository postInit method"); - systemConfigurationChangeApplier.applySystemConfiguration(true, true, result); + systemConfigurationChangeDispatcher.dispatch(true, true, result); } @Override diff --git a/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/SystemConfigurationChangeApplierImpl.java b/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/SystemConfigurationChangeDispatcherImpl.java similarity index 82% rename from repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/SystemConfigurationChangeApplierImpl.java rename to repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/SystemConfigurationChangeDispatcherImpl.java index e2b352e8b24..1dd4f1b5c43 100644 --- a/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/SystemConfigurationChangeApplierImpl.java +++ b/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/SystemConfigurationChangeDispatcherImpl.java @@ -21,7 +21,8 @@ import com.evolveum.midpoint.prism.PrismContext; import com.evolveum.midpoint.prism.PrismObject; import com.evolveum.midpoint.repo.api.RepositoryService; -import com.evolveum.midpoint.repo.api.SystemConfigurationChangeApplier; +import com.evolveum.midpoint.repo.api.SystemConfigurationChangeDispatcher; +import com.evolveum.midpoint.repo.api.SystemConfigurationChangeListener; import com.evolveum.midpoint.schema.GetOperationOptions; import com.evolveum.midpoint.schema.RelationRegistry; import com.evolveum.midpoint.schema.SelectorOptions; @@ -43,22 +44,25 @@ import org.springframework.stereotype.Component; import java.util.Collection; +import java.util.HashSet; /** * @author mederly */ @Component -public class SystemConfigurationChangeApplierImpl implements SystemConfigurationChangeApplier { +public class SystemConfigurationChangeDispatcherImpl implements SystemConfigurationChangeDispatcher { - private static final Trace LOGGER = TraceManager.getTrace(SystemConfigurationChangeApplierImpl.class); + private static final Trace LOGGER = TraceManager.getTrace(SystemConfigurationChangeDispatcherImpl.class); @Autowired private RepositoryService repositoryService; @Autowired private PrismContext prismContext; @Autowired private RelationRegistry relationRegistry; + private static final Collection listeners = new HashSet<>(); + private String lastVersionApplied = null; - public synchronized void applySystemConfiguration(boolean ignoreVersion, boolean allowNotFound, + public synchronized void dispatch(boolean ignoreVersion, boolean allowNotFound, OperationResult result) throws SchemaException { LOGGER.trace("Applying system configuration: lastVersionApplied = {}, ignoreVersion = {}", lastVersionApplied, ignoreVersion); @@ -74,8 +78,9 @@ public synchronized void applySystemConfiguration(boolean ignoreVersion, boolean LOGGER.debug("System configuration not found"); result.muteLastSubresultError(); } else { - LOGGER.warn("System configuration not found"); + LOGGER.warn("System configuration not found", e); } + notifyListeners(null); lastVersionApplied = null; return; } @@ -93,6 +98,7 @@ public synchronized void applySystemConfiguration(boolean ignoreVersion, boolean // because this method is called also from the cluster management thread. lastVersionApplied = currentVersion; + notifyListeners(configuration); applyLoggingConfiguration(configurationObject, result); applyRemoteHostAddressHeadersConfiguration(configuration); applyPolyStringNormalizerConfiguration(configuration); @@ -107,6 +113,17 @@ public synchronized void applySystemConfiguration(boolean ignoreVersion, boolean } } + private void notifyListeners(SystemConfigurationType configuration) { + for (SystemConfigurationChangeListener listener : listeners) { + try { + listener.update(configuration); + } catch (Throwable t) { + LoggingUtils.logUnexpectedException(LOGGER, "Couldn't update system configuration listener {}", t, listener); + lastVersionApplied = null; + } + } + } + private void applyLoggingConfiguration(PrismObject configuration, OperationResult result) { try { LoggingConfigurationType loggingWithProfiling = ProfilingConfigurationManager @@ -170,4 +187,22 @@ private void applyOperationResultHandlingConfiguration(SystemConfigurationType c lastVersionApplied = null; } } + + @Override + public synchronized void registerListener(SystemConfigurationChangeListener listener) { + if (!listeners.contains(listener)) { + listeners.add(listener); + } else { + LOGGER.warn("Attempt to register already-registered listener: {}", listener); + } + } + + @Override + public synchronized void unregisterListener(SystemConfigurationChangeListener listener) { + if (listeners.contains(listener)) { + listeners.remove(listener); + } else { + LOGGER.warn("Attempt to unregister a listener that was not registered: {}", listener); + } + } } diff --git a/repo/task-quartz-impl/src/main/java/com/evolveum/midpoint/task/quartzimpl/TaskManagerQuartzImpl.java b/repo/task-quartz-impl/src/main/java/com/evolveum/midpoint/task/quartzimpl/TaskManagerQuartzImpl.java index 5a034bc25ef..61fb3f2fbc1 100644 --- a/repo/task-quartz-impl/src/main/java/com/evolveum/midpoint/task/quartzimpl/TaskManagerQuartzImpl.java +++ b/repo/task-quartz-impl/src/main/java/com/evolveum/midpoint/task/quartzimpl/TaskManagerQuartzImpl.java @@ -38,7 +38,7 @@ import com.evolveum.midpoint.prism.delta.builder.DeltaBuilder; import com.evolveum.midpoint.repo.api.PreconditionViolationException; import com.evolveum.midpoint.repo.api.RepoAddOptions; -import com.evolveum.midpoint.repo.api.SystemConfigurationChangeApplier; +import com.evolveum.midpoint.repo.api.SystemConfigurationChangeDispatcher; import com.evolveum.midpoint.schema.*; import com.evolveum.midpoint.task.api.*; import com.evolveum.midpoint.task.quartzimpl.handlers.PartitioningTaskHandler; @@ -125,7 +125,7 @@ public class TaskManagerQuartzImpl implements TaskManager, BeanFactoryAware { @Autowired private TaskManagerConfiguration configuration; @Autowired private LocalizationService localizationService; - @Autowired private SystemConfigurationChangeApplier systemConfigurationChangeApplier; + @Autowired private SystemConfigurationChangeDispatcher systemConfigurationChangeDispatcher; // instances of all the helper classes (see their definitions for their description) private ExecutionManager executionManager = new ExecutionManager(this); @@ -2239,7 +2239,7 @@ public boolean isLocalNodeClusteringEnabled() { return configuration.isLocalNodeClusteringEnabled(); } - public SystemConfigurationChangeApplier getSystemConfigurationChangeApplier() { - return systemConfigurationChangeApplier; + public SystemConfigurationChangeDispatcher getSystemConfigurationChangeDispatcher() { + return systemConfigurationChangeDispatcher; } } diff --git a/repo/task-quartz-impl/src/main/java/com/evolveum/midpoint/task/quartzimpl/cluster/ClusterManager.java b/repo/task-quartz-impl/src/main/java/com/evolveum/midpoint/task/quartzimpl/cluster/ClusterManager.java index 5649e0b7c26..f1e951d3820 100644 --- a/repo/task-quartz-impl/src/main/java/com/evolveum/midpoint/task/quartzimpl/cluster/ClusterManager.java +++ b/repo/task-quartz-impl/src/main/java/com/evolveum/midpoint/task/quartzimpl/cluster/ClusterManager.java @@ -249,7 +249,7 @@ public PrismObject getNodeById(String nodeIdentifier, OperationResult private void checkSystemConfigurationChanged(OperationResult parentResult) { OperationResult result = parentResult.createSubresult(CHECK_SYSTEM_CONFIGURATION_CHANGED); try { - taskManager.getSystemConfigurationChangeApplier().applySystemConfiguration(false, false, result); + taskManager.getSystemConfigurationChangeDispatcher().dispatch(false, false, result); result.computeStatus(); } catch (Throwable t) { LoggingUtils.logUnexpectedException(LOGGER, "Couldn't apply system configuration", t); From 22aee8d5d18cac980ee878edaac256c228a0f90e Mon Sep 17 00:00:00 2001 From: Radovan Semancik Date: Wed, 12 Sep 2018 17:47:55 +0200 Subject: [PATCH 06/13] Fixes for improved multitenant authorizations (MID-4882) --- .../security/TestSecurityMultitenant.java | 163 +++++++++++------- .../security/multitenant/user-dmurr.xml | 29 ++++ .../security/multitenant/user-duncan.xml | 29 ++++ .../security/multitenant/user-piter.xml | 29 ++++ .../enforcer/impl/SecurityEnforcerImpl.java | 49 +++--- 5 files changed, 216 insertions(+), 83 deletions(-) create mode 100644 model/model-intest/src/test/resources/security/multitenant/user-dmurr.xml create mode 100644 model/model-intest/src/test/resources/security/multitenant/user-duncan.xml create mode 100644 model/model-intest/src/test/resources/security/multitenant/user-piter.xml 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 907034a9e47..e54aaa26dd1 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 @@ -15,81 +15,21 @@ */ package com.evolveum.midpoint.model.intest.security; -import static org.testng.AssertJUnit.assertFalse; -import static org.testng.AssertJUnit.assertTrue; -import static org.testng.AssertJUnit.assertNotNull; -import static org.testng.AssertJUnit.assertEquals; - import java.io.File; -import java.io.IOException; -import java.util.Collection; -import java.util.List; - -import javax.xml.datatype.XMLGregorianCalendar; 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.model.api.ModelAuthorizationAction; -import com.evolveum.midpoint.model.api.RoleSelectionSpecification; -import com.evolveum.midpoint.prism.PrismContainer; -import com.evolveum.midpoint.prism.PrismObject; -import com.evolveum.midpoint.prism.PrismObjectDefinition; -import com.evolveum.midpoint.prism.PrismReferenceValue; -import com.evolveum.midpoint.prism.delta.ObjectDelta; -import com.evolveum.midpoint.prism.path.ItemPath; -import com.evolveum.midpoint.prism.path.NameItemPathSegment; -import com.evolveum.midpoint.prism.query.ObjectFilter; -import com.evolveum.midpoint.prism.query.ObjectQuery; -import com.evolveum.midpoint.prism.query.TypeFilter; -import com.evolveum.midpoint.prism.util.PrismAsserts; -import com.evolveum.midpoint.prism.util.PrismTestUtil; -import com.evolveum.midpoint.prism.xml.XmlTypeConverter; -import com.evolveum.midpoint.schema.GetOperationOptions; -import com.evolveum.midpoint.schema.SelectorOptions; -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.security.api.MidPointPrincipal; -import com.evolveum.midpoint.security.enforcer.api.AuthorizationParameters; import com.evolveum.midpoint.task.api.Task; -import com.evolveum.midpoint.test.DummyResourceContoller; -import com.evolveum.midpoint.test.IntegrationTestTools; -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.ActivationStatusType; -import com.evolveum.midpoint.xml.ns._public.common.common_3.ActivationType; 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.AuthorizationPhaseType; -import com.evolveum.midpoint.xml.ns._public.common.common_3.ConstructionType; -import com.evolveum.midpoint.xml.ns._public.common.common_3.ExclusionPolicyConstraintType; -import com.evolveum.midpoint.xml.ns._public.common.common_3.FocusType; -import com.evolveum.midpoint.xml.ns._public.common.common_3.LookupTableType; -import com.evolveum.midpoint.xml.ns._public.common.common_3.MappingType; -import com.evolveum.midpoint.xml.ns._public.common.common_3.MetadataType; import com.evolveum.midpoint.xml.ns._public.common.common_3.ModelExecuteOptionsType; -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.OrgType; -import com.evolveum.midpoint.xml.ns._public.common.common_3.PasswordType; -import com.evolveum.midpoint.xml.ns._public.common.common_3.PolicyConstraintsType; -import com.evolveum.midpoint.xml.ns._public.common.common_3.PolicyExceptionType; -import com.evolveum.midpoint.xml.ns._public.common.common_3.PolicyRuleType; -import com.evolveum.midpoint.xml.ns._public.common.common_3.ResourceAttributeDefinitionType; 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.SystemConfigurationType; -import com.evolveum.midpoint.xml.ns._public.common.common_3.SystemObjectsType; import com.evolveum.midpoint.xml.ns._public.common.common_3.UserType; /** @@ -109,30 +49,54 @@ public class TestSecurityMultitenant extends AbstractSecurityTest { protected static final String ROLE_TENANT_ADMIN_OID = "00000000-8888-6666-a000-100000000000"; 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"; + protected static final String USER_EDRIC_OID = "00000000-8888-6666-a001-200000000000"; protected static final String USER_EDRIC_NAME = "edric"; protected static final String USER_EDRIC_FULL_NAME = "Navigator Edric"; + protected static final File USER_DMURR_FILE = new File(TEST_DIR, "user-dmurr.xml"); + protected static final String USER_DMURR_OID = "00000000-8888-6666-a001-200000000001"; + protected static final String USER_DMURR_NAME = "dmurr"; + protected static final String USER_DMURR_FULL_NAME = "D'murr Pilru"; + 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"; + protected static final String USER_SHADDAM_CORRINO_OID = "00000000-8888-6666-a100-200000000000"; protected static final String USER_SHADDAM_CORRINO_NAME = "shaddam"; protected static final String USER_SHADDAM_CORRINO_FULL_NAME = "Padishah Emperor Shaddam IV"; 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"; + protected static final String USER_LETO_ATREIDES_OID = "00000000-8888-6666-a200-200000000000"; protected static final String USER_LETO_ATREIDES_NAME = "leto"; protected static final String USER_LETO_ATREIDES_FULL_NAME = "Duke Leto Atreides"; + protected static final String USER_PAUL_ATREIDES_OID = "00000000-8888-6666-a200-200000000001"; protected static final String USER_PAUL_ATREIDES_NAME = "paul"; protected static final String USER_PAUL_ATREIDES_FULL_NAME = "Paul Atreides"; + protected static final File USER_DUNCAN_FILE = new File(TEST_DIR, "user-duncan.xml"); + protected static final String USER_DUNCAN_OID = "00000000-8888-6666-a200-200000000002"; + protected static final String USER_DUNCAN_NAME = "duncan"; + protected static final String USER_DUNCAN_FULL_NAME = "Duncan Idaho"; + 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"; + protected static final String USER_VLADIMIR_HARKONNEN_OID = "00000000-8888-6666-a300-200000000000"; + protected static final File USER_PITER_FILE = new File(TEST_DIR, "user-piter.xml"); + protected static final String USER_PITER_OID = "00000000-8888-6666-a300-200000000001"; + protected static final String USER_PITER_NAME = "piter"; + protected static final String USER_PITER_FULL_NAME = "Piter De Vries"; + @Override public void initSystem(Task initTask, OperationResult initResult) throws Exception { super.initSystem(initTask, initResult); @@ -324,8 +288,51 @@ public void test100AutzLetoRead() throws Exception { * MID-4882 */ @Test - public void test102AutzLetoModify() throws Exception { - final String TEST_NAME = "test102AutzLetoModify"; + public void test102AutzLetoAdd() throws Exception { + final String TEST_NAME = "test102AutzLetoAdd"; + displayTestTitle(TEST_NAME); + // GIVEN + cleanupAutzTest(null); + + login(USER_LETO_ATREIDES_NAME); + + // WHEN + displayWhen(TEST_NAME); + + // Matching tenant + assertAddAllow(USER_DUNCAN_FILE); + + // Wrong tenant + assertAddDeny(USER_PITER_FILE); + + // No tenant + assertAddDeny(USER_DMURR_FILE); + + // THEN + displayThen(TEST_NAME); + + login(USER_ADMINISTRATOR_USERNAME); + + assertUserAfter(USER_DUNCAN_OID) + .assertName(USER_DUNCAN_NAME) + .assertFullName(USER_DUNCAN_FULL_NAME) + .assignments() + .assertOrg(ORG_ATREIDES_OID) + .assertNoRole() + .end() + .assertTenantRef(ORG_ATREIDES_OID) + .assertParentOrgRefs(ORG_ATREIDES_OID) + .assertLinks(0); + + assertGlobalStateUntouched(); + } + + /** + * MID-4882 + */ + @Test + public void test104AutzLetoModify() throws Exception { + final String TEST_NAME = "test104AutzLetoModify"; displayTestTitle(TEST_NAME); // GIVEN cleanupAutzTest(null); @@ -348,7 +355,37 @@ public void test102AutzLetoModify() throws Exception { displayThen(TEST_NAME); assertGlobalStateUntouched(); - } + } + + /** + * MID-4882 + */ + @Test + public void test109AutzLetoDelete() throws Exception { + final String TEST_NAME = "test109AutzLetoDelete"; + displayTestTitle(TEST_NAME); + // GIVEN + cleanupAutzTest(null); + + login(USER_LETO_ATREIDES_NAME); + + // WHEN + displayWhen(TEST_NAME); + + // Matching tenant + assertDeleteAllow(UserType.class, USER_DUNCAN_OID); + + // Wrong tenant + assertDeleteDeny(UserType.class, USER_PITER_OID); + + // No tenant + assertDeleteDeny(UserType.class, USER_DMURR_OID); + + // THEN + displayThen(TEST_NAME); + + assertGlobalStateUntouched(); + } /** * Edric is part of Spacing Guld. But the Guild is not tenant. diff --git a/model/model-intest/src/test/resources/security/multitenant/user-dmurr.xml b/model/model-intest/src/test/resources/security/multitenant/user-dmurr.xml new file mode 100644 index 00000000000..dfb3f0a6775 --- /dev/null +++ b/model/model-intest/src/test/resources/security/multitenant/user-dmurr.xml @@ -0,0 +1,29 @@ + + + + + dmurr + D'murr + Pilru + D'murr Pilru + + + + diff --git a/model/model-intest/src/test/resources/security/multitenant/user-duncan.xml b/model/model-intest/src/test/resources/security/multitenant/user-duncan.xml new file mode 100644 index 00000000000..f745b4a531d --- /dev/null +++ b/model/model-intest/src/test/resources/security/multitenant/user-duncan.xml @@ -0,0 +1,29 @@ + + + + + duncan + Duncan + Idaho + Duncan Idaho + + + + diff --git a/model/model-intest/src/test/resources/security/multitenant/user-piter.xml b/model/model-intest/src/test/resources/security/multitenant/user-piter.xml new file mode 100644 index 00000000000..108f088e097 --- /dev/null +++ b/model/model-intest/src/test/resources/security/multitenant/user-piter.xml @@ -0,0 +1,29 @@ + + + + + piter + Piter + De Vries + De Vries + + + + diff --git a/repo/security-enforcer-impl/src/main/java/com/evolveum/midpoint/security/enforcer/impl/SecurityEnforcerImpl.java b/repo/security-enforcer-impl/src/main/java/com/evolveum/midpoint/security/enforcer/impl/SecurityEnforcerImpl.java index 5149bb9a09a..85348119926 100644 --- a/repo/security-enforcer-impl/src/main/java/com/evolveum/midpoint/security/enforcer/impl/SecurityEnforcerImpl.java +++ b/repo/security-enforcer-impl/src/main/java/com/evolveum/midpoint/security/enforcer/impl/SecurityEnforcerImpl.java @@ -270,7 +270,10 @@ private AccessDecision isAuthorized ItemDecisionFunction itemDecisionFunction = (itemPath, removingContainer) -> decideAllowedItems(itemPath, allowedItems, phase, removingContainer); AccessDecision itemsDecision = null; if (params.hasDelta()) { - itemsDecision = determineDeltaDecision(params.getDelta(), params.getObject(), itemDecisionFunction); + // Behave as if this is execution phase for delete delta authorizations. We do not want to avoid deleting objects just because there + // are automatic/operational items that were generated by midPoint. Otherwise we won't be really able to delete any object. + ItemDecisionFunction itemDecisionFunctionDelete = (itemPath, removingContainer) -> decideAllowedItems(itemPath, allowedItems, AuthorizationPhaseType.EXECUTION, removingContainer); + itemsDecision = determineDeltaDecision(params.getDelta(), params.getObject(), itemDecisionFunction, itemDecisionFunctionDelete); } else if (params.hasObject()) { itemsDecision = determineObjectDecision(params.getObject(), itemDecisionFunction); } @@ -454,9 +457,12 @@ private AccessDecision determineContainerDecision(PrismContainerValue cval, I * The currentObject parameter is the state of the object as we have seen it (the more recent the better). * This is used to check authorization for id-only delete deltas and replace deltas for containers. */ - private AccessDecision determineDeltaDecision(ObjectDelta delta, PrismObject currentObject, ItemDecisionFunction itemDecisionFunction) { + private AccessDecision determineDeltaDecision(ObjectDelta delta, PrismObject currentObject, + ItemDecisionFunction itemDecisionFunction, ItemDecisionFunction itemDecisionFunctionDelete) { if (delta.isAdd()) { return determineObjectDecision(delta.getObjectToAdd(), itemDecisionFunction); + } else if (delta.isDelete()) { + return determineObjectDecision(currentObject, itemDecisionFunctionDelete); } else { AccessDecision decision = null; for (ItemDelta itemDelta: delta.getModifications()) { @@ -1729,24 +1735,27 @@ public MidPointPrincipal createDonorPrincipal(MidPointPrincipal attorneyPrincipa public AccessDecision determineSubitemDecision( ObjectSecurityConstraints securityConstraints, ObjectDelta delta, PrismObject currentObject, String operationUrl, AuthorizationPhaseType phase, ItemPath subitemRootPath) { - return determineDeltaDecision(delta, currentObject, - (nameOnlyItemPath, removingContainer) -> { - if (removingContainer && isInList(nameOnlyItemPath, AuthorizationConstants.OPERATIONAL_ITEMS_ALLOWED_FOR_CONTAINER_DELETE)) { - return null; - } - if (AuthorizationPhaseType.EXECUTION.equals(phase) && isInList(nameOnlyItemPath, AuthorizationConstants.EXECUTION_ITEMS_ALLOWED_BY_DEFAULT)) { - return null; - } - if (subitemRootPath != null && !subitemRootPath.isSubPathOrEquivalent(nameOnlyItemPath)) { -// LOGGER.trace("subitem decision: {} <=> {} (not under root) : {}", subitemRootPath, nameOnlyItemPath, null); - return null; - } - - AuthorizationDecisionType authorizationDecisionType = securityConstraints.findItemDecision(nameOnlyItemPath, operationUrl, phase); - AccessDecision decision = AccessDecision.translate(authorizationDecisionType); -// LOGGER.trace("subitem decision: {} <=> {} : {}", subitemRootPath, nameOnlyItemPath, decision); - return decision; - }); + ItemDecisionFunction itemDecisionFunction = (nameOnlyItemPath, removingContainer) -> subitemDecide(nameOnlyItemPath, removingContainer, securityConstraints, operationUrl, phase, subitemRootPath); + ItemDecisionFunction itemDecisionFunctionDelete = (nameOnlyItemPath, removingContainer) -> subitemDecide(nameOnlyItemPath, removingContainer, securityConstraints, operationUrl, AuthorizationPhaseType.EXECUTION, subitemRootPath); + return determineDeltaDecision(delta, currentObject, itemDecisionFunction, itemDecisionFunctionDelete); + } + + private AccessDecision subitemDecide(ItemPath nameOnlyItemPath, boolean removingContainer, ObjectSecurityConstraints securityConstraints, String operationUrl, AuthorizationPhaseType phase, ItemPath subitemRootPath) { + if (removingContainer && isInList(nameOnlyItemPath, AuthorizationConstants.OPERATIONAL_ITEMS_ALLOWED_FOR_CONTAINER_DELETE)) { + return null; + } + if (AuthorizationPhaseType.EXECUTION.equals(phase) && isInList(nameOnlyItemPath, AuthorizationConstants.EXECUTION_ITEMS_ALLOWED_BY_DEFAULT)) { + return null; + } + if (subitemRootPath != null && !subitemRootPath.isSubPathOrEquivalent(nameOnlyItemPath)) { +// LOGGER.trace("subitem decision: {} <=> {} (not under root) : {}", subitemRootPath, nameOnlyItemPath, null); + return null; + } + + AuthorizationDecisionType authorizationDecisionType = securityConstraints.findItemDecision(nameOnlyItemPath, operationUrl, phase); + AccessDecision decision = AccessDecision.translate(authorizationDecisionType); +// LOGGER.trace("subitem decision: {} <=> {} : {}", subitemRootPath, nameOnlyItemPath, decision); + return decision; } @Override From 0b29bdbc118474815c4bb91beb3bfbe3d074f094 Mon Sep 17 00:00:00 2001 From: Katarina Valalikova Date: Wed, 12 Sep 2018 18:39:07 +0200 Subject: [PATCH 07/13] fix for MID-3934 --- .../impl/sync/SynchronizationServiceImpl.java | 53 ++++--------------- 1 file changed, 10 insertions(+), 43 deletions(-) diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/sync/SynchronizationServiceImpl.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/sync/SynchronizationServiceImpl.java index 0049d6dccd7..b7a7960bcae 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/sync/SynchronizationServiceImpl.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/sync/SynchronizationServiceImpl.java @@ -1,4 +1,5 @@ /* + * Copyright (c) 2010-2018 Evolveum * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -284,22 +285,10 @@ private ObjectSynchronizationDiscriminatorType determineOb return evaluateSynchronizationDivision(divider, syncCtx, task, subResult); } - -// private boolean isPolicyApplicable(ObjectSynchronizationDiscriminatorType synchronizationDiscriminator, ObjectSynchronizationType synchronizationPolicy, PrismObject resource) throws SchemaException { -// if (synchronizationDiscriminator == null) { -// return false; -// } -// return SynchronizationUtils.isPolicyApplicable(synchronizationDiscriminator, synchronizationPolicy, expressionFactory, resource); -// } private boolean isPolicyApplicable(ObjectSynchronizationType synchronizationPolicy, ObjectSynchronizationDiscriminatorType synchronizationDiscriminator, SynchronizationContext syncCtx) throws SchemaException, ObjectNotFoundException, ExpressionEvaluationException, CommunicationException, ConfigurationException, SecurityViolationException { - return SynchronizationServiceUtils.isPolicyApplicable(synchronizationPolicy, synchronizationDiscriminator, expressionFactory, syncCtx); - -// Boolean conditionResult = evaluateSynchronizationPolicyCondition(synchronizationPolicy, syncCtx.getApplicableShadow(), -// syncCtx.getResource(), syncCtx.getSystemConfiguration(), task, result); -// return conditionResult != null ? conditionResult : true; } private ObjectSynchronizationDiscriminatorType evaluateSynchronizationDivision(ObjectSynchronizationSorterType synchronizationSorterType, @@ -329,27 +318,6 @@ private ObjectSynchronizationDiscriminatorType evaluateSyn } } - private Boolean evaluateSynchronizationPolicyCondition(ObjectSynchronizationType synchronizationPolicy, - PrismObject currentShadow, PrismObject resource, - PrismObject configuration, Task task, OperationResult result) - throws SchemaException, ExpressionEvaluationException, ObjectNotFoundException, CommunicationException, ConfigurationException, SecurityViolationException { - if (synchronizationPolicy.getCondition() == null) { - return null; - } - ExpressionType conditionExpressionType = synchronizationPolicy.getCondition(); - String desc = "condition in object synchronization " + synchronizationPolicy.getName(); - ExpressionVariables variables = ModelImplUtils.getDefaultExpressionVariables(null, currentShadow, null, - resource, configuration, null); - try { - ModelExpressionThreadLocalHolder.pushExpressionEnvironment(new ExpressionEnvironment<>(task, result)); - PrismPropertyValue evaluateCondition = ExpressionUtil.evaluateCondition(variables, - conditionExpressionType, expressionFactory, desc, task, result); - return evaluateCondition.getValue(); - } finally { - ModelExpressionThreadLocalHolder.popExpressionEnvironment(); - } - } - private void traceObjectSynchronization(ObjectSynchronizationType obejctSynchronization) { if (LOGGER.isTraceEnabled()) { String policyDesc = null; @@ -373,7 +341,7 @@ private boolean checkSynchronizationPolicy(Synchronization + syncCtx.getApplicableShadow().asObjectable().getObjectClass() + ") " + " on " + syncCtx.getResource() + ", ignoring change from channel " + syncCtx.getChanel(); LOGGER.debug(message); - List> modifications = createShadowIntentAndSynchronizationTimestampDelta(syncCtx.getApplicableShadow(), null); + List> modifications = createShadowIntentAndSynchronizationTimestampDelta(syncCtx, false); executeShadowModifications(syncCtx.getApplicableShadow(), modifications, task, subResult); subResult.recordStatus(OperationResultStatus.NOT_APPLICABLE, message); eventInfo.setNoSynchronizationPolicy(); @@ -385,7 +353,7 @@ private boolean checkSynchronizationPolicy(Synchronization String message = "SYNCHRONIZATION is not enabled for " + syncCtx.getResource() + " ignoring change from channel " + syncCtx.getChanel(); LOGGER.debug(message); - List> modifications = createShadowIntentAndSynchronizationTimestampDelta(syncCtx.getApplicableShadow(), obejctSynchronization.getIntent()); + List> modifications = createShadowIntentAndSynchronizationTimestampDelta(syncCtx, true); executeShadowModifications(syncCtx.getApplicableShadow(), modifications, task, subResult); subResult.recordStatus(OperationResultStatus.NOT_APPLICABLE, message); eventInfo.setSynchronizationNotEnabled(); @@ -416,8 +384,8 @@ private boolean checkTaskConstraints(SynchronizationContex if (!satisfyTaskConstraints(obejctSynchronization, task)) { LOGGER.trace("SYNCHRONIZATION skipping {} because it does not match kind/intent defined in task", new Object[] { syncCtx.getApplicableShadow() }); - List> modifications = createShadowIntentAndSynchronizationTimestampDelta(syncCtx.getApplicableShadow(), - obejctSynchronization.getIntent()); + List> modifications = createShadowIntentAndSynchronizationTimestampDelta(syncCtx, + false); executeShadowModifications(syncCtx.getCurrentShadow(), modifications, task, subResult); subResult.recordStatus(OperationResultStatus.NOT_APPLICABLE, "Skipped because it does not match objectClass/kind/intent"); @@ -459,7 +427,7 @@ private boolean satisfyTaskConstraints(ObjectSynchronizationType synchronization private boolean checkProtected(SynchronizationContext syncCtx, SynchronizationEventInformation eventInfo, Task task, OperationResult subResult) { if (isProtected(syncCtx.getApplicableShadow())) { - List> modifications = createShadowIntentAndSynchronizationTimestampDelta(syncCtx.getApplicableShadow(), syncCtx.getObjectSynchronization().getIntent()); + List> modifications = createShadowIntentAndSynchronizationTimestampDelta(syncCtx, true); executeShadowModifications(syncCtx.getApplicableShadow(), modifications, task, subResult); subResult.recordSuccess(); eventInfo.setProtected(); @@ -505,12 +473,11 @@ private boolean checkDryRunAndUnrelatedChange(Synchronizat return true; } - private List> createShadowIntentAndSynchronizationTimestampDelta(PrismObject currentShadow, - String intent) { - List> modifications = SynchronizationUtils.createSynchronizationTimestampsDelta(currentShadow); - if (StringUtils.isNotBlank(intent)) { + private List> createShadowIntentAndSynchronizationTimestampDelta(SynchronizationContext syncCtx, boolean saveIntent) { + List> modifications = SynchronizationUtils.createSynchronizationTimestampsDelta(syncCtx.getApplicableShadow()); + if (saveIntent && StringUtils.isNotBlank(syncCtx.getObjectSynchronization().getIntent())) { PropertyDelta intentDelta = PropertyDelta.createModificationReplaceProperty(ShadowType.F_INTENT, - currentShadow.getDefinition(), intent); + syncCtx.getApplicableShadow().getDefinition(), syncCtx.getObjectSynchronization().getIntent()); modifications.add(intentDelta); } return modifications; From dcbd1be75a4ba53fc81ef90d64c1823ac9045e0b Mon Sep 17 00:00:00 2001 From: skublik Date: Thu, 13 Sep 2018 08:42:07 +0200 Subject: [PATCH 08/13] adding of ajp connector --- .../boot/EmbeddedTomcatAutoConfiguration.java | 23 +++++++++++++++++-- .../src/main/resources/application.yml | 16 ++++--------- 2 files changed, 26 insertions(+), 13 deletions(-) diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/boot/EmbeddedTomcatAutoConfiguration.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/boot/EmbeddedTomcatAutoConfiguration.java index 41b7b327d30..8711628f79e 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/boot/EmbeddedTomcatAutoConfiguration.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/boot/EmbeddedTomcatAutoConfiguration.java @@ -17,7 +17,9 @@ import javax.servlet.Servlet; +import org.apache.catalina.connector.Connector; import org.apache.catalina.startup.Tomcat; +import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.AutoConfigureOrder; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; @@ -52,10 +54,27 @@ public class EmbeddedTomcatAutoConfiguration { @ConditionalOnClass({ Servlet.class, Tomcat.class }) @ConditionalOnMissingBean(value = TomcatServletWebServerFactory.class, search = SearchStrategy.CURRENT) public static class EmbeddedTomcat { - + + @Value( "${server.tomcat.ajp.enabled:false}" ) + private boolean enableAjp; + + @Value( "${server.tomcat.ajp.port:9090}" ) + private int port; + @Bean public TomcatServletWebServerFactory tomcatEmbeddedServletContainerFactory() { - return new MidPointTomcatServletWebServerFactory(); + MidPointTomcatServletWebServerFactory tomcat = new MidPointTomcatServletWebServerFactory(); + + if(enableAjp) { + Connector ajpConnector = new Connector("AJP/1.3"); + ajpConnector.setPort(port); + ajpConnector.setSecure(false); + ajpConnector.setScheme("http"); + ajpConnector.setAllowTrace(false); + tomcat.addAdditionalTomcatConnectors(ajpConnector); + } + + return tomcat; } } diff --git a/gui/admin-gui/src/main/resources/application.yml b/gui/admin-gui/src/main/resources/application.yml index a26e0492aaf..a7db8b70081 100644 --- a/gui/admin-gui/src/main/resources/application.yml +++ b/gui/admin-gui/src/main/resources/application.yml @@ -10,6 +10,10 @@ server: tomcat: basedir: ${midpoint.home} max-http-post-size: 104857600 # in bytes +## Enable and configuration ajp connector for mp +# ajp: +# enabled: true +# port: 9090 servlet: context-path: /midpoint application-display-name: MidPoint @@ -46,14 +50,4 @@ auth: # enable-csrf: false # default for midpoint is true # more properties with default values can be found here: -# https://docs.spring.io/spring-boot/docs/current/reference/html/common-application-properties.html - -management: - endpoint: - health: - enabled: true - - -#logging: -# level: -# org.springframework: TRACE \ No newline at end of file +# https://docs.spring.io/spring-boot/docs/current/reference/html/common-application-properties.html \ No newline at end of file From 9aaab05ed27880e88f584ba024362cc14250bef1 Mon Sep 17 00:00:00 2001 From: Radovan Semancik Date: Thu, 13 Sep 2018 11:23:23 +0200 Subject: [PATCH 09/13] Mutli-tenant authorizaiton fixes (MID-4882) --- .../evolveum/midpoint/prism/query/TypeFilter.java | 5 ++++- .../intest/security/TestSecurityAdvanced.java | 1 + .../enforcer/impl/SecurityEnforcerImpl.java | 15 +++++---------- 3 files changed, 10 insertions(+), 11 deletions(-) diff --git a/infra/prism/src/main/java/com/evolveum/midpoint/prism/query/TypeFilter.java b/infra/prism/src/main/java/com/evolveum/midpoint/prism/query/TypeFilter.java index 6e8b82571eb..e7b5df5a68e 100644 --- a/infra/prism/src/main/java/com/evolveum/midpoint/prism/query/TypeFilter.java +++ b/infra/prism/src/main/java/com/evolveum/midpoint/prism/query/TypeFilter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2014 Evolveum + * 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. @@ -53,6 +53,9 @@ public ObjectFilter getFilter() { } public void setFilter(ObjectFilter filter) { + if (filter == this) { + throw new IllegalArgumentException("Type filte has itself as a subfilter"); + } this.filter = filter; } diff --git a/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/security/TestSecurityAdvanced.java b/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/security/TestSecurityAdvanced.java index 3fc22ac4c28..57c0b409ffb 100644 --- a/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/security/TestSecurityAdvanced.java +++ b/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/security/TestSecurityAdvanced.java @@ -288,6 +288,7 @@ public void test102AutzLechuckPersonaManagement() throws Exception { assertGetDeny(UserType.class, USER_JACK_OID); assertGetDeny(UserType.class, USER_GUYBRUSH_OID); assertGetAllow(UserType.class, USER_LECHUCK_OID); + display("HEREHERE"); assertGetAllow(UserType.class, USER_CHARLES_OID); // TODO: MID-3899 diff --git a/repo/security-enforcer-impl/src/main/java/com/evolveum/midpoint/security/enforcer/impl/SecurityEnforcerImpl.java b/repo/security-enforcer-impl/src/main/java/com/evolveum/midpoint/security/enforcer/impl/SecurityEnforcerImpl.java index 85348119926..3b61071ed55 100644 --- a/repo/security-enforcer-impl/src/main/java/com/evolveum/midpoint/security/enforcer/impl/SecurityEnforcerImpl.java +++ b/repo/security-enforcer-impl/src/main/java/com/evolveum/midpoint/security/enforcer/impl/SecurityEnforcerImpl.java @@ -722,11 +722,11 @@ private boolean isApplicable(SubjectedObjectSelectorType autzHumanReadableDesc, desc, object.getOid()); return false; } + } else { + LOGGER.trace(" {}: tenant object spec not applicable for {}, object OID {} because there is a strange tenant specificaiton in authorization", + autzHumanReadableDesc, desc, object.getOid()); + return false; } - } else { - LOGGER.trace(" {}: tenant object spec not applicable for {}, object OID {} because there is a strange tenant specificaiton in authorization", - autzHumanReadableDesc, desc, object.getOid()); - return false; } } @@ -1334,11 +1334,6 @@ private ObjectFilter preProcessObje LOGGER.trace(" roleRelation empty"); } - if (objSpecTypeFilter != null) { - objSpecTypeFilter.setFilter(objSpecSecurityFilter); - objSpecSecurityFilter = objSpecTypeFilter; - } - // tenant if (specTenant != null) { ObjectFilter objSpecTenantFilter = processTenantFilter(principal, autz, specTenant, queryItemsSpec, origFilter); @@ -1355,7 +1350,7 @@ private ObjectFilter preProcessObje LOGGER.trace(" applying tenant filter {}", objSpecTenantFilter); } } else { - LOGGER.trace(" roleRelation empty"); + LOGGER.trace(" tenant empty"); } if (objSpecTypeFilter != null) { From bbef1dc961120c532762f059282a213dfd22c21b Mon Sep 17 00:00:00 2001 From: Pavol Mederly Date: Thu, 13 Sep 2018 12:43:07 +0200 Subject: [PATCH 10/13] Remove forgotten "aspect" localization key --- .../src/main/resources/localization/Midpoint.properties | 1 - 1 file changed, 1 deletion(-) diff --git a/gui/admin-gui/src/main/resources/localization/Midpoint.properties b/gui/admin-gui/src/main/resources/localization/Midpoint.properties index 9980c0d45c3..7ae4595d131 100755 --- a/gui/admin-gui/src/main/resources/localization/Midpoint.properties +++ b/gui/admin-gui/src/main/resources/localization/Midpoint.properties @@ -4170,7 +4170,6 @@ ObjectFormType.details.newValue=New object form GuiObjectColumnType.details.newValue=New gui object column GuiObjectListViewType.details.newValue=New gui object list view DashboardWidgetType.details.newValue=New dashboard widget -GenericPcpAspectConfigurationType.details.newValue=New generic pcp aspect configuration RelationDefinitionType.details.newValue=New relation definition GuiActionType.details.newValue=New gui action ApprovalStageDefinitionType.details.newValue=New approval stage definition From 50036c786759af6fb360129a84c0d16d7c545525 Mon Sep 17 00:00:00 2001 From: Katarina Valalikova Date: Thu, 13 Sep 2018 12:48:46 +0200 Subject: [PATCH 11/13] other fixes for MID-3934 --- .../component/data/column/ColumnUtils.java | 2 +- .../admin/resources/ResourceContentPanel.java | 2 +- .../resources/ResourceDetailsTabPanel.java | 4 +- .../web/page/admin/server/PageTaskAdd.java | 6 +-- .../dto/ResourceRelatedHandlerDto.java | 4 +- .../midpoint/common/SynchronizationUtils.java | 26 +++++++--- .../schema/constants/SchemaConstants.java | 2 +- .../impl/sync/SynchronizationServiceImpl.java | 50 +++++++++---------- 8 files changed, 52 insertions(+), 44 deletions(-) diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/data/column/ColumnUtils.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/data/column/ColumnUtils.java index 4ce78a56d2b..a35b8bb5893 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/data/column/ColumnUtils.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/data/column/ColumnUtils.java @@ -506,7 +506,7 @@ public void populateItem(Item>> cellItem, SelectableBean object = (SelectableBean) rowModel.getObject(); PrismProperty pObjectClass = object.getValue() != null ? object.getValue().asPrismObject().findProperty( - new ItemPath(TaskType.F_EXTENSION, SchemaConstants.OBJECTCLASS_PROPERTY_NAME)) + new ItemPath(TaskType.F_EXTENSION, SchemaConstants.MODEL_EXTENSION_OBJECTCLASS)) : null; if (pObjectClass != null) { cellItem.add(new Label(componentId, pObjectClass.getRealValue().getLocalPart())); diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/admin/resources/ResourceContentPanel.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/admin/resources/ResourceContentPanel.java index d9e505217a6..41792aa7c21 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/admin/resources/ResourceContentPanel.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/admin/resources/ResourceContentPanel.java @@ -468,7 +468,7 @@ private List getTasksForKind(List> tasks) { if (taskKind == null) { PrismProperty taskObjectClass = task - .findProperty(new ItemPath(TaskType.F_EXTENSION, SchemaConstants.OBJECTCLASS_PROPERTY_NAME)); + .findProperty(new ItemPath(TaskType.F_EXTENSION, SchemaConstants.MODEL_EXTENSION_OBJECTCLASS)); if (taskObjectClass == null) { LOGGER.warn("Bad task definition. Task {} doesn't contain definition either of objectClass or kind/intent", task.getOid()); diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/admin/resources/ResourceDetailsTabPanel.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/admin/resources/ResourceDetailsTabPanel.java index b4adc71e699..ec7ef5d7599 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/admin/resources/ResourceDetailsTabPanel.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/admin/resources/ResourceDetailsTabPanel.java @@ -409,7 +409,7 @@ private List getTaskFor(List> tasks, } PrismProperty taskObjectClass = task.findProperty( - new ItemPath(TaskType.F_EXTENSION, SchemaConstants.OBJECTCLASS_PROPERTY_NAME)); + new ItemPath(TaskType.F_EXTENSION, SchemaConstants.MODEL_EXTENSION_OBJECTCLASS)); QName taskObjectClassValue = null; if (taskObjectClass != null) { taskObjectClassValue = taskObjectClass.getRealValue(); @@ -442,7 +442,7 @@ private List getTaskFor(List> tasks, } if (SynchronizationUtils.isPolicyApplicable(taskObjectClassValue, taskKindValue, taskIntentValue, - synchronizationPolicy, resource)) { + synchronizationPolicy, resource, true)) { syncTasks.add(task.asObjectable()); } } diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/admin/server/PageTaskAdd.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/admin/server/PageTaskAdd.java index 8bfe3b7c0a4..0d64a113963 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/admin/server/PageTaskAdd.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/admin/server/PageTaskAdd.java @@ -199,7 +199,7 @@ private TaskAddDto loadTask(TaskType taskType) { PrismProperty pObjectClass; try { - pObjectClass = taskType.asPrismObject().findOrCreateProperty(new ItemPath(TaskType.F_EXTENSION, SchemaConstants.OBJECTCLASS_PROPERTY_NAME)); + pObjectClass = taskType.asPrismObject().findOrCreateProperty(new ItemPath(TaskType.F_EXTENSION, SchemaConstants.MODEL_EXTENSION_OBJECTCLASS)); QName objectClass = pObjectClass.getRealValue(); if (objectClass != null){ taskAdd.setObjectClass(objectClass.getLocalPart()); @@ -772,7 +772,7 @@ private TaskType createTask(TaskAddDto dto) throws SchemaException { if(dto.getObjectClass() != null && StringUtils.isNotEmpty(dto.getObjectClass())){ PrismObject prismTask = task.asPrismObject(); - ItemPath path = new ItemPath(TaskType.F_EXTENSION, SchemaConstants.OBJECTCLASS_PROPERTY_NAME); + ItemPath path = new ItemPath(TaskType.F_EXTENSION, SchemaConstants.MODEL_EXTENSION_OBJECTCLASS); PrismProperty objectClassProperty = prismTask.findOrCreateProperty(path); QName objectClass = null; @@ -783,7 +783,7 @@ private TaskType createTask(TaskAddDto dto) throws SchemaException { } SchemaRegistry registry = getPrismContext().getSchemaRegistry(); - PrismPropertyDefinition def = registry.findPropertyDefinitionByElementName(SchemaConstants.OBJECTCLASS_PROPERTY_NAME); + PrismPropertyDefinition def = registry.findPropertyDefinitionByElementName(SchemaConstants.MODEL_EXTENSION_OBJECTCLASS); objectClassProperty.setDefinition(def); objectClassProperty.setRealValue(objectClass); } diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/admin/server/handlers/dto/ResourceRelatedHandlerDto.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/admin/server/handlers/dto/ResourceRelatedHandlerDto.java index 4ffc4fa5ce4..24a9d390d88 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/admin/server/handlers/dto/ResourceRelatedHandlerDto.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/admin/server/handlers/dto/ResourceRelatedHandlerDto.java @@ -90,7 +90,7 @@ private void fillFromExtension(TaskType taskType) { intent = intentItem.getRealValue(); } - PrismProperty objectClassItem = task.getExtension().findProperty(SchemaConstants.OBJECTCLASS_PROPERTY_NAME); + PrismProperty objectClassItem = task.getExtension().findProperty(SchemaConstants.MODEL_EXTENSION_OBJECTCLASS); if(objectClassItem != null && objectClassItem.getRealValue() != null){ objectClass = objectClassItem.getRealValue().getLocalPart(); } @@ -239,7 +239,7 @@ public void setResource(TaskAddResourcesDto resource) { objectClassQName = q; } } - addExtensionDelta(rv, SchemaConstants.OBJECTCLASS_PROPERTY_NAME, objectClassQName, prismContext); + addExtensionDelta(rv, SchemaConstants.MODEL_EXTENSION_OBJECTCLASS, objectClassQName, prismContext); } return rv; } diff --git a/infra/common/src/main/java/com/evolveum/midpoint/common/SynchronizationUtils.java b/infra/common/src/main/java/com/evolveum/midpoint/common/SynchronizationUtils.java index 085d679b922..15c436b0faa 100644 --- a/infra/common/src/main/java/com/evolveum/midpoint/common/SynchronizationUtils.java +++ b/infra/common/src/main/java/com/evolveum/midpoint/common/SynchronizationUtils.java @@ -161,8 +161,7 @@ private static List getSituationFromSam return valuesToDelete; } -public static boolean isPolicyApplicable(QName objectClass, ShadowKindType kind, String intent, ObjectSynchronizationType synchronizationPolicy, PrismObject resource) throws SchemaException{ - + public static boolean isPolicyApplicable(QName objectClass, ShadowKindType kind, String intent, ObjectSynchronizationType synchronizationPolicy, PrismObject resource, boolean strictIntent) throws SchemaException { List policyObjectClasses = synchronizationPolicy.getObjectClass(); if (policyObjectClasses == null || policyObjectClasses.isEmpty()) { @@ -198,8 +197,8 @@ public static boolean isPolicyApplicable(QName objectClass, ShadowKindType kind, // kind ShadowKindType policyKind = synchronizationPolicy.getKind(); - LOGGER.trace("Comparing kinds, policy kind: {}, current kind: {}", policyKind, kind); - if (policyKind != null && kind != null && !policyKind.equals(kind)) { + boolean kindMatch = policyKind != null ? policyKind.equals(kind) : kind == null ? true : false; + if (!kindMatch) { LOGGER.trace("Kinds don't match, skipping policy {}", synchronizationPolicy); return false; } @@ -208,13 +207,24 @@ public static boolean isPolicyApplicable(QName objectClass, ShadowKindType kind, // TODO is the intent always present in shadow at this time? [med] String policyIntent = synchronizationPolicy.getIntent(); LOGGER.trace("Comparing intents, policy intent: {}, current intent: {}", policyIntent, intent); - if (policyIntent != null && intent != null - && !MiscSchemaUtil.equalsIntent(intent, policyIntent)) { - LOGGER.trace("Intents don't match, skipping policy {}", synchronizationPolicy); - return false; + if (!strictIntent) { + if (intent != null && !MiscSchemaUtil.equalsIntent(intent, policyIntent)) { + LOGGER.trace("Intents don't match, skipping policy {}", synchronizationPolicy); + return false; + } + } else { + if (!MiscSchemaUtil.equalsIntent(intent, policyIntent)) { + LOGGER.trace("Intents don't match, skipping policy {}", synchronizationPolicy); + return false; + } } return true; } + + public static boolean isPolicyApplicable(QName objectClass, ShadowKindType kind, String intent, ObjectSynchronizationType synchronizationPolicy, PrismObject resource) throws SchemaException{ + return isPolicyApplicable(objectClass, kind, intent, synchronizationPolicy, resource, false); + + } } diff --git a/infra/schema/src/main/java/com/evolveum/midpoint/schema/constants/SchemaConstants.java b/infra/schema/src/main/java/com/evolveum/midpoint/schema/constants/SchemaConstants.java index c8a58a94d5f..abbec01dc7f 100644 --- a/infra/schema/src/main/java/com/evolveum/midpoint/schema/constants/SchemaConstants.java +++ b/infra/schema/src/main/java/com/evolveum/midpoint/schema/constants/SchemaConstants.java @@ -291,7 +291,7 @@ public abstract class SchemaConstants { public static final QName MODEL_EXTENSION_FINISH_OPERATIONS_ONLY = new QName(NS_MODEL_EXTENSION, "finishOperationsOnly"); public static final QName MODEL_EXTENSION_KIND = new QName(NS_MODEL_EXTENSION, "kind"); public static final QName MODEL_EXTENSION_INTENT = new QName(NS_MODEL_EXTENSION, "intent"); - public static final QName OBJECTCLASS_PROPERTY_NAME = new QName(NS_MODEL_EXTENSION, "objectclass"); + public static final QName MODEL_EXTENSION_OBJECTCLASS = new QName(NS_MODEL_EXTENSION, "objectclass"); public static final QName MODEL_EXTENSION_LAST_SCAN_TIMESTAMP_PROPERTY_NAME = new QName( NS_MODEL_EXTENSION, "lastScanTimestamp"); diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/sync/SynchronizationServiceImpl.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/sync/SynchronizationServiceImpl.java index b7a7960bcae..6bdc139b675 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/sync/SynchronizationServiceImpl.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/sync/SynchronizationServiceImpl.java @@ -378,10 +378,10 @@ private boolean isSynchronizationEnabled(ObjectSynchronizationType synchronizati * @param task * @param subResult * @return + * @throws SchemaException */ - private boolean checkTaskConstraints(SynchronizationContext syncCtx, SynchronizationEventInformation eventInfo, Task task, OperationResult subResult) { - ObjectSynchronizationType obejctSynchronization = syncCtx.getObjectSynchronization(); - if (!satisfyTaskConstraints(obejctSynchronization, task)) { + private boolean checkTaskConstraints(SynchronizationContext syncCtx, SynchronizationEventInformation eventInfo, Task task, OperationResult subResult) throws SchemaException { + if (!satisfyTaskConstraints(syncCtx)) { LOGGER.trace("SYNCHRONIZATION skipping {} because it does not match kind/intent defined in task", new Object[] { syncCtx.getApplicableShadow() }); List> modifications = createShadowIntentAndSynchronizationTimestampDelta(syncCtx, @@ -397,33 +397,31 @@ private boolean checkTaskConstraints(SynchronizationContex return true; } - private boolean satisfyTaskConstraints(ObjectSynchronizationType synchronizationPolicy, Task task) { - PrismProperty kind = task.getExtensionProperty(SchemaConstants.MODEL_EXTENSION_KIND); - if (kind != null && !kind.isEmpty()) { - ShadowKindType kindValue = kind.getRealValue(); - ShadowKindType policyKind = synchronizationPolicy.getKind(); - if (policyKind == null) { - policyKind = ShadowKindType.ACCOUNT; // TODO is this ok? [med] - } - if (!policyKind.equals(kindValue)) { - return false; - } + private boolean satisfyTaskConstraints(SynchronizationContext syncCtx) throws SchemaException { + + ShadowKindType kind = getPropertyValue(syncCtx.getTask(), SchemaConstants.MODEL_EXTENSION_KIND); + String intent = getPropertyValue(syncCtx.getTask(), SchemaConstants.MODEL_EXTENSION_INTENT); + QName objectClass = getPropertyValue(syncCtx.getTask(), SchemaConstants.MODEL_EXTENSION_OBJECTCLASS); + + LOGGER.trace("checking task constraints: {}", syncCtx.getTask()); + + boolean isApplicable = SynchronizationUtils.isPolicyApplicable(objectClass, kind, intent, syncCtx.getObjectSynchronization(), syncCtx.getResource(), true); + //this mean that kind/intent are null in the task..but this can be a case, so check if at least the objectClass is the same + if (!isApplicable && objectClass != null) { + return QNameUtil.matchAny(objectClass, syncCtx.getObjectSynchronization().getObjectClass()); } + + return isApplicable; + } - PrismProperty intent = task.getExtensionProperty(SchemaConstants.MODEL_EXTENSION_INTENT); - if (intent != null && !intent.isEmpty()) { - String intentValue = intent.getRealValue(); - if (StringUtils.isEmpty(synchronizationPolicy.getIntent())) { - return false; - } - if (!synchronizationPolicy.getIntent().equals(intentValue)) { - return false; - } + private T getPropertyValue(Task task, QName propertyName) { + PrismProperty prop = task.getExtensionProperty(propertyName); + if (prop == null || prop.isEmpty()) { + return null; } - - return true; + + return prop.getRealValue(); } - private boolean checkProtected(SynchronizationContext syncCtx, SynchronizationEventInformation eventInfo, Task task, OperationResult subResult) { if (isProtected(syncCtx.getApplicableShadow())) { From 59956c9e4147367393b11b3dc3e55f8a26df01e6 Mon Sep 17 00:00:00 2001 From: Katarina Valalikova Date: Thu, 13 Sep 2018 13:17:48 +0200 Subject: [PATCH 12/13] small cleanup for synchronization service --- .../impl/sync/SynchronizationContext.java | 57 +++++++++++ .../impl/sync/SynchronizationServiceImpl.java | 97 +++++++------------ 2 files changed, 90 insertions(+), 64 deletions(-) diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/sync/SynchronizationContext.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/sync/SynchronizationContext.java index dda2a32b601..b80bfc58aa6 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/sync/SynchronizationContext.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/sync/SynchronizationContext.java @@ -17,15 +17,25 @@ import javax.xml.namespace.QName; +import org.apache.commons.lang.BooleanUtils; + +import com.evolveum.midpoint.common.SynchronizationUtils; import com.evolveum.midpoint.prism.PrismObject; +import com.evolveum.midpoint.prism.PrismProperty; import com.evolveum.midpoint.prism.util.PrismMonitor; import com.evolveum.midpoint.schema.constants.ObjectTypes; +import com.evolveum.midpoint.schema.constants.SchemaConstants; import com.evolveum.midpoint.schema.result.OperationResult; import com.evolveum.midpoint.task.api.Task; +import com.evolveum.midpoint.util.QNameUtil; import com.evolveum.midpoint.util.exception.ConfigurationException; +import com.evolveum.midpoint.util.exception.SchemaException; +import com.evolveum.midpoint.util.logging.Trace; +import com.evolveum.midpoint.util.logging.TraceManager; import com.evolveum.midpoint.xml.ns._public.common.common_3.FocusType; import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectSynchronizationType; import com.evolveum.midpoint.xml.ns._public.common.common_3.ResourceType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.ShadowKindType; import com.evolveum.midpoint.xml.ns._public.common.common_3.ShadowType; import com.evolveum.midpoint.xml.ns._public.common.common_3.SynchronizationReactionType; import com.evolveum.midpoint.xml.ns._public.common.common_3.SynchronizationSituationType; @@ -33,6 +43,8 @@ import com.evolveum.midpoint.xml.ns._public.common.common_3.UserType; public class SynchronizationContext { + + private static final Trace LOGGER = TraceManager.getTrace(SynchronizationContext.class); private PrismObject applicableShadow; private PrismObject currentShadow; @@ -63,6 +75,50 @@ public SynchronizationContext(PrismObject applicableShadow, PrismObj this.result = result; } + public boolean isSynchronizationEnabled() { + if (objectSynchronization == null) { + return false; + } + return BooleanUtils.isNotFalse(objectSynchronization.isEnabled()); + } + + public boolean isProtected() { + if (applicableShadow == null) { + return false; + } + + ShadowType currentShadowType = applicableShadow.asObjectable(); + return BooleanUtils.isTrue(currentShadowType.isProtectedObject()); + } + + public boolean isSatisfyTaskConstraints() throws SchemaException { + + ShadowKindType kind = getTaskPropertyValue(SchemaConstants.MODEL_EXTENSION_KIND); + String intent = getTaskPropertyValue(SchemaConstants.MODEL_EXTENSION_INTENT); + QName objectClass = getTaskPropertyValue(SchemaConstants.MODEL_EXTENSION_OBJECTCLASS); + + LOGGER.trace("checking task constraints: {}", task); + + boolean isApplicable = SynchronizationUtils.isPolicyApplicable(objectClass, kind, intent, objectSynchronization, resource, true); + //this mean that kind/intent are null in the task..but this can be a case, so check if at least the objectClass is the same + if (!isApplicable && objectClass != null) { + return QNameUtil.matchAny(objectClass, objectSynchronization.getObjectClass()); + } + + return isApplicable; + } + + //TODO multi-threded tasks? + private T getTaskPropertyValue(QName propertyName) { + PrismProperty prop = task.getExtensionProperty(propertyName); + if (prop == null || prop.isEmpty()) { + return null; + } + + return prop.getRealValue(); + } + + public PrismObject getApplicableShadow() { return applicableShadow; } @@ -193,4 +249,5 @@ public boolean isForceIntentChange() { public void setForceIntentChange(boolean forceIntentChange) { this.forceIntentChange = forceIntentChange; } + } diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/sync/SynchronizationServiceImpl.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/sync/SynchronizationServiceImpl.java index 6bdc139b675..8b5bc0741f3 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/sync/SynchronizationServiceImpl.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/sync/SynchronizationServiceImpl.java @@ -173,15 +173,15 @@ public void notifyChange(ResourceObjectShadowChangeDescrip ObjectSynchronizationType obejctSynchronization = syncCtx.getObjectSynchronization(); traceObjectSynchronization(obejctSynchronization); - if (!checkSynchronizationPolicy(syncCtx, eventInfo, task, subResult)) { + if (!checkSynchronizationPolicy(syncCtx, eventInfo)) { return; } - if (!checkTaskConstraints(syncCtx, eventInfo, task, subResult)) { + if (!checkTaskConstraints(syncCtx, eventInfo)) { return; } - if (!checkProtected(syncCtx, eventInfo, task, subResult)) { + if (!checkProtected(syncCtx, eventInfo)) { return; } @@ -190,22 +190,22 @@ public void notifyChange(ResourceObjectShadowChangeDescrip syncCtx.getFocusClass(), ModelImplUtils.getPolicyDesc(obejctSynchronization)); } - setupSituation(syncCtx, eventInfo, change, task, subResult); + setupSituation(syncCtx, eventInfo, change); - if (!checkDryRunAndUnrelatedChange(syncCtx, eventInfo, change, now, task, subResult)) { + if (!checkDryRunAndUnrelatedChange(syncCtx, eventInfo, change, now)) { return; } // must be here, because when the reaction has no action, the // situation won't be set. - PrismObject newCurrentShadow = saveSyncMetadata(syncCtx, change, now, task, parentResult); + PrismObject newCurrentShadow = saveSyncMetadata(syncCtx, change, now); if (newCurrentShadow != null) { change.setCurrentShadow(newCurrentShadow); syncCtx.setCurrentShadow(newCurrentShadow); } SynchronizationSituationType newSituation = reactToChange(syncCtx, change, - logDebug, task, subResult); + logDebug); eventInfo.setNewSituation(newSituation); eventInfo.record(task); subResult.computeStatus(); @@ -334,8 +334,10 @@ private void traceObjectSynchronization(ObjectSynchronizationType obejctSynchron } } - private boolean checkSynchronizationPolicy(SynchronizationContext syncCtx, SynchronizationEventInformation eventInfo, Task task, OperationResult subResult) { + private boolean checkSynchronizationPolicy(SynchronizationContext syncCtx, SynchronizationEventInformation eventInfo) { ObjectSynchronizationType obejctSynchronization = syncCtx.getObjectSynchronization(); + OperationResult subResult = syncCtx.getResult(); + Task task = syncCtx.getTask(); if (obejctSynchronization == null) { String message = "SYNCHRONIZATION no matching policy for " + syncCtx.getApplicableShadow() + " (" + syncCtx.getApplicableShadow().asObjectable().getObjectClass() + ") " + " on " + syncCtx.getResource() @@ -349,7 +351,7 @@ private boolean checkSynchronizationPolicy(Synchronization return false; } - if (!isSynchronizationEnabled(obejctSynchronization)) { + if (!syncCtx.isSynchronizationEnabled()) { String message = "SYNCHRONIZATION is not enabled for " + syncCtx.getResource() + " ignoring change from channel " + syncCtx.getChanel(); LOGGER.debug(message); @@ -364,13 +366,6 @@ private boolean checkSynchronizationPolicy(Synchronization return true; } - private boolean isSynchronizationEnabled(ObjectSynchronizationType synchronization) { - if (synchronization == null || synchronization.isEnabled() == null) { - return false; - } - return synchronization.isEnabled(); - } - /** * check if the kind/intent in the syncPolicy satisfy constraints defined in task * @param syncCtx @@ -380,8 +375,10 @@ private boolean isSynchronizationEnabled(ObjectSynchronizationType synchronizati * @return * @throws SchemaException */ - private boolean checkTaskConstraints(SynchronizationContext syncCtx, SynchronizationEventInformation eventInfo, Task task, OperationResult subResult) throws SchemaException { - if (!satisfyTaskConstraints(syncCtx)) { + private boolean checkTaskConstraints(SynchronizationContext syncCtx, SynchronizationEventInformation eventInfo) throws SchemaException { + if (!syncCtx.isSatisfyTaskConstraints()) { + OperationResult subResult = syncCtx.getResult(); + Task task = syncCtx.getTask(); LOGGER.trace("SYNCHRONIZATION skipping {} because it does not match kind/intent defined in task", new Object[] { syncCtx.getApplicableShadow() }); List> modifications = createShadowIntentAndSynchronizationTimestampDelta(syncCtx, @@ -397,34 +394,11 @@ private boolean checkTaskConstraints(SynchronizationContex return true; } - private boolean satisfyTaskConstraints(SynchronizationContext syncCtx) throws SchemaException { - - ShadowKindType kind = getPropertyValue(syncCtx.getTask(), SchemaConstants.MODEL_EXTENSION_KIND); - String intent = getPropertyValue(syncCtx.getTask(), SchemaConstants.MODEL_EXTENSION_INTENT); - QName objectClass = getPropertyValue(syncCtx.getTask(), SchemaConstants.MODEL_EXTENSION_OBJECTCLASS); - - LOGGER.trace("checking task constraints: {}", syncCtx.getTask()); - - boolean isApplicable = SynchronizationUtils.isPolicyApplicable(objectClass, kind, intent, syncCtx.getObjectSynchronization(), syncCtx.getResource(), true); - //this mean that kind/intent are null in the task..but this can be a case, so check if at least the objectClass is the same - if (!isApplicable && objectClass != null) { - return QNameUtil.matchAny(objectClass, syncCtx.getObjectSynchronization().getObjectClass()); - } - return isApplicable; - } - - private T getPropertyValue(Task task, QName propertyName) { - PrismProperty prop = task.getExtensionProperty(propertyName); - if (prop == null || prop.isEmpty()) { - return null; - } - - return prop.getRealValue(); - } - - private boolean checkProtected(SynchronizationContext syncCtx, SynchronizationEventInformation eventInfo, Task task, OperationResult subResult) { - if (isProtected(syncCtx.getApplicableShadow())) { + private boolean checkProtected(SynchronizationContext syncCtx, SynchronizationEventInformation eventInfo) { + if (syncCtx.isProtected()) { + OperationResult subResult = syncCtx.getResult(); + Task task = syncCtx.getTask(); List> modifications = createShadowIntentAndSynchronizationTimestampDelta(syncCtx, true); executeShadowModifications(syncCtx.getApplicableShadow(), modifications, task, subResult); subResult.recordSuccess(); @@ -436,21 +410,9 @@ private boolean checkProtected(SynchronizationContext s return true; } - private boolean isProtected(PrismObject shadow) { - if (shadow == null) { - return false; - } - - ShadowType currentShadowType = shadow.asObjectable(); - if (currentShadowType.isProtectedObject() == null) { - return false; - } - - return currentShadowType.isProtectedObject(); - } - - private boolean checkDryRunAndUnrelatedChange(SynchronizationContext syncCtx, SynchronizationEventInformation eventInfo, ResourceObjectShadowChangeDescription change, XMLGregorianCalendar now, Task task, OperationResult subResult) throws SchemaException { - + private boolean checkDryRunAndUnrelatedChange(SynchronizationContext syncCtx, SynchronizationEventInformation eventInfo, ResourceObjectShadowChangeDescription change, XMLGregorianCalendar now) throws SchemaException { + OperationResult subResult = syncCtx.getResult(); + Task task = syncCtx.getTask(); if (change.isUnrelatedChange() || ModelImplUtils.isDryRun(task)) { if (syncCtx.getApplicableShadow() == null) { throw new IllegalStateException("No current nor old shadow present: " + change); @@ -558,8 +520,10 @@ private void validate(ResourceObjectShadowChangeDescription change) { * {@link SynchronizationSituationType#DISPUTED} situation */ private void setupSituation(SynchronizationContext syncCtx, - SynchronizationEventInformation eventInfo, ResourceObjectShadowChangeDescription change, Task task, OperationResult result) { + SynchronizationEventInformation eventInfo, ResourceObjectShadowChangeDescription change) { + OperationResult result = syncCtx.getResult(); + Task task = syncCtx.getTask(); OperationResult subResult = result.createSubresult(CHECK_SITUATION); LOGGER.trace("Determining situation for resource object shadow."); @@ -814,8 +778,7 @@ private ChangeType getModificationType(ResourceObjectShadowChangeDescription cha } private SynchronizationSituationType reactToChange(SynchronizationContext syncCtx, - ResourceObjectShadowChangeDescription change, boolean logDebug, - Task task, OperationResult parentResult) + ResourceObjectShadowChangeDescription change, boolean logDebug) throws ConfigurationException, ObjectNotFoundException, SchemaException, PolicyViolationException, ExpressionEvaluationException, ObjectAlreadyExistsException, CommunicationException, SecurityViolationException { @@ -855,6 +818,9 @@ private SynchronizationSituationType reactToChange(Synchro final boolean willSynchronize = isSynchronize(syncCtx.getReaction()); LensContext lensContext = null; + + OperationResult parentResult = syncCtx.getResult(); + Task task = syncCtx.getTask(); if (willSynchronize) { lensContext = createLensContext(syncCtx, change, syncCtx.getReaction(), options, parentResult); } @@ -1115,12 +1081,15 @@ private void findReactionDefinition(SynchronizationContext * Saves situation, timestamps, kind and intent (if needed) */ private PrismObject saveSyncMetadata(SynchronizationContext syncCtx, ResourceObjectShadowChangeDescription change, - XMLGregorianCalendar now, Task task, OperationResult parentResult) { + XMLGregorianCalendar now) { PrismObject shadow = syncCtx.getCurrentShadow(); if (shadow == null) { return null; } + OperationResult parentResult = syncCtx.getResult(); + Task task = syncCtx.getTask(); + try { ShadowType shadowType = shadow.asObjectable(); // new situation description From cc07365d69a94a7c470b1f5362f975606e536853 Mon Sep 17 00:00:00 2001 From: Pavol Mederly Date: Thu, 13 Sep 2018 14:09:12 +0200 Subject: [PATCH 13/13] Add FETCH_ALL iteration method (MID-4414) To avoid warnings when falling back from STRICTLY_SEQUENTIAL_PAGING to SIMPLE_PAGING we added a new "dummy" iteration method of FETCH_ALL to be used for small data sets. --- .../xml/ns/public/common/common-core-3.xsd | 17 +++++++++ .../sql/testing/TestSqlRepositoryFactory.java | 1 + .../repo/sql/SearchIterativeTest.java | 38 +++++++++++++++++++ .../repo/sql/SqlRepositoryConfiguration.java | 11 +++++- .../repo/sql/SqlRepositoryServiceImpl.java | 20 ++++++++-- .../repo/sql/helpers/ObjectRetriever.java | 23 +++++++++-- 6 files changed, 103 insertions(+), 7 deletions(-) diff --git a/infra/schema/src/main/resources/xml/ns/public/common/common-core-3.xsd b/infra/schema/src/main/resources/xml/ns/public/common/common-core-3.xsd index d73f41b54e4..75c03eeec94 100755 --- a/infra/schema/src/main/resources/xml/ns/public/common/common-core-3.xsd +++ b/infra/schema/src/main/resources/xml/ns/public/common/common-core-3.xsd @@ -18500,6 +18500,23 @@ + + + + + + This is a workaround for situations when STRICTLY_SEQUENTIAL_PAGING cannot be used because + a client-supplied paging is present. All the objects are fetched as in regular searchObjects() + call and then send to the client one-by-one. (This defeats the basic purpose of + searchObjectsIterative but can be safely used for small numbers of objects.) + + So this method is a safe fallback that is used when STRICTLY_SEQUENTIAL_PAGING is implicitly + chosen but a custom paging exists, provided that the paging contains maxSize clause with a number + not greater than a specified limit (maxObjectsForImplicitFetchAllIterationMethod). + + + + diff --git a/repo/repo-sql-impl-test/src/main/java/com/evolveum/midpoint/repo/sql/testing/TestSqlRepositoryFactory.java b/repo/repo-sql-impl-test/src/main/java/com/evolveum/midpoint/repo/sql/testing/TestSqlRepositoryFactory.java index 83b815bfb46..c35681f756f 100644 --- a/repo/repo-sql-impl-test/src/main/java/com/evolveum/midpoint/repo/sql/testing/TestSqlRepositoryFactory.java +++ b/repo/repo-sql-impl-test/src/main/java/com/evolveum/midpoint/repo/sql/testing/TestSqlRepositoryFactory.java @@ -112,6 +112,7 @@ private void updateConfigurationFromProperties(Configuration configuration, Prop updateConfigurationBooleanProperty(configuration, properties, PROPERTY_ITERATIVE_SEARCH_BY_PAGING); updateConfigurationStringProperty(configuration, properties, PROPERTY_ITERATIVE_SEARCH_BY_PAGING_BATCH_SIZE); + updateConfigurationStringProperty(configuration, properties, PROPERTY_MAX_OBJECTS_FOR_IMPLICIT_FETCH_ALL_ITERATION_METHOD); updateConfigurationBooleanProperty(configuration, properties, PROPERTY_USE_ZIP); updateConfigurationIntegerProperty(configuration, properties, PROPERTY_MIN_POOL_SIZE); diff --git a/repo/repo-sql-impl-test/src/test/java/com/evolveum/midpoint/repo/sql/SearchIterativeTest.java b/repo/repo-sql-impl-test/src/test/java/com/evolveum/midpoint/repo/sql/SearchIterativeTest.java index 240d949e8d7..74e821a00e3 100644 --- a/repo/repo-sql-impl-test/src/test/java/com/evolveum/midpoint/repo/sql/SearchIterativeTest.java +++ b/repo/repo-sql-impl-test/src/test/java/com/evolveum/midpoint/repo/sql/SearchIterativeTest.java @@ -116,6 +116,44 @@ public void test102SimpleSequentialIterationWithMaxSize() throws Exception { assertObjects(objects, 70); } + @Test + public void test103SimpleSequentialIterationWithCustomPagingLarge() throws Exception { + OperationResult result = new OperationResult("test103SimpleSequentialIterationWithCustomPagingLarge"); + + final List> objects = new ArrayList<>(); + + ResultHandler handler = (object, parentResult) -> { + objects.add(object); + return true; + }; + + ObjectQuery query = ObjectQuery.createObjectQuery(ObjectPaging.createPaging(1, null)); + repositoryService.searchObjectsIterative(UserType.class, query, handler, null, true, result); + result.recomputeStatus(); + + assertTrue(result.isSuccess()); + assertObjects(objects, COUNT - 1); + } + + @Test + public void test104SimpleSequentialIterationWithCustomPagingSmall() throws Exception { + OperationResult result = new OperationResult("test104SimpleSequentialIterationWithCustomPagingSmall"); + + final List> objects = new ArrayList<>(); + + ResultHandler handler = (object, parentResult) -> { + objects.add(object); + return true; + }; + + ObjectQuery query = ObjectQuery.createObjectQuery(ObjectPaging.createPaging(1, 200)); + repositoryService.searchObjectsIterative(UserType.class, query, handler, null, true, result); + result.recomputeStatus(); + + assertTrue(result.isSuccess()); + assertObjects(objects, 200); + } + @Test public void test105SimpleNonSequentialIteration() throws Exception { OperationResult result = new OperationResult("test105SimpleNonSequentialIteration"); diff --git a/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/SqlRepositoryConfiguration.java b/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/SqlRepositoryConfiguration.java index ddddc9f0791..f50bfaae1a9 100644 --- a/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/SqlRepositoryConfiguration.java +++ b/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/SqlRepositoryConfiguration.java @@ -195,6 +195,7 @@ public enum WrongSchemaAction { private static final int DEFAULT_EMBEDDED_H2_PORT = 5437; private static final int DEFAULT_MIN_POOL_SIZE = 8; private static final int DEFAULT_MAX_POOL_SIZE = 20; + private static final int DEFAULT_MAX_OBJECTS_FOR_IMPLICIT_FETCH_ALL_ITERATION_METHOD = 500; private static final String USER_HOME_VARIABLE = "user.home"; private static final String MIDPOINT_HOME_VARIABLE = "midpoint.home"; @@ -230,6 +231,7 @@ public enum WrongSchemaAction { //other public static final String PROPERTY_ITERATIVE_SEARCH_BY_PAGING = "iterativeSearchByPaging"; public static final String PROPERTY_ITERATIVE_SEARCH_BY_PAGING_BATCH_SIZE = "iterativeSearchByPagingBatchSize"; + public static final String PROPERTY_MAX_OBJECTS_FOR_IMPLICIT_FETCH_ALL_ITERATION_METHOD = "maxObjectsForImplicitFetchAllIterationMethod"; //closure public static final String PROPERTY_IGNORE_ORG_CLOSURE = "ignoreOrgClosure"; @@ -294,6 +296,7 @@ public enum WrongSchemaAction { private boolean defaultIterativeSearchByPaging; private int defaultIterativeSearchByPagingBatchSize; + private final int maxObjectsForImplicitFetchAllIterationMethod; private final boolean iterativeSearchByPaging; private int iterativeSearchByPagingBatchSize; // not final only because of testing @@ -396,6 +399,8 @@ public SqlRepositoryConfiguration(Configuration configuration) { computeDefaultIterativeSearchParameters(); iterativeSearchByPaging = configuration.getBoolean(PROPERTY_ITERATIVE_SEARCH_BY_PAGING, defaultIterativeSearchByPaging); iterativeSearchByPagingBatchSize = configuration.getInt(PROPERTY_ITERATIVE_SEARCH_BY_PAGING_BATCH_SIZE, defaultIterativeSearchByPagingBatchSize); + maxObjectsForImplicitFetchAllIterationMethod = configuration.getInt(PROPERTY_MAX_OBJECTS_FOR_IMPLICIT_FETCH_ALL_ITERATION_METHOD, + DEFAULT_MAX_OBJECTS_FOR_IMPLICIT_FETCH_ALL_ITERATION_METHOD); ignoreOrgClosure = configuration.getBoolean(PROPERTY_IGNORE_ORG_CLOSURE, false); orgClosureStartupAction = OrgClosureManager.StartupAction.fromValue( @@ -744,7 +749,11 @@ public void setIterativeSearchByPagingBatchSize(int iterativeSearchByPagingBatch this.iterativeSearchByPagingBatchSize = iterativeSearchByPagingBatchSize; } - public String getDataSource() { + public int getMaxObjectsForImplicitFetchAllIterationMethod() { + return maxObjectsForImplicitFetchAllIterationMethod; + } + + public String getDataSource() { return dataSource; } diff --git a/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/SqlRepositoryServiceImpl.java b/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/SqlRepositoryServiceImpl.java index 775a84e4a55..d8b6822c8af 100644 --- a/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/SqlRepositoryServiceImpl.java +++ b/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/SqlRepositoryServiceImpl.java @@ -825,10 +825,16 @@ public SearchResultMetadata searchObjectsIterative(Class< if (strictlySequential) { if (isCustomPagingOkWithPagedSeqIteration(query)) { iterationMethod = IterationMethodType.STRICTLY_SEQUENTIAL_PAGING; - } else { - LOGGER.warn("Iterative search was defined in the repository configuration, and strict sequentiality " + } else if (isCustomPagingOkWithFetchAllIteration(query)) { + LOGGER.debug("Iterative search by paging was defined in the repository configuration, and strict sequentiality " + "was requested. However, a custom paging precludes its application. Therefore switching to " - + "simple paging iteration method. Paging requested: " + query.getPaging()); + + "'fetch all' iteration method. Paging requested: " + query.getPaging()); + iterationMethod = IterationMethodType.FETCH_ALL; + } else { + LOGGER.warn("Iterative search by paging was defined in the repository configuration, and strict sequentiality " + + "was requested. However, a custom paging precludes its application and maxSize is either " + + "undefined or too large (over " + getConfiguration().getMaxObjectsForImplicitFetchAllIterationMethod() + + "). Therefore switching to simple paging iteration method. Paging requested: " + query.getPaging()); iterationMethod = IterationMethodType.SIMPLE_PAGING; } } else { @@ -855,11 +861,19 @@ public SearchResultMetadata searchObjectsIterative(Class< case SINGLE_TRANSACTION: searchObjectsIterativeBySingleTransaction(type, query, handler, options, subResult); break; case SIMPLE_PAGING: objectRetriever.searchObjectsIterativeByPaging(type, query, handler, options, subResult); break; case STRICTLY_SEQUENTIAL_PAGING: objectRetriever.searchObjectsIterativeByPagingStrictlySequential(type, query, handler, options, subResult); break; + case FETCH_ALL: objectRetriever.searchObjectsIterativeByFetchAll(type, query, handler, options, subResult); break; default: throw new AssertionError("iterationMethod: " + iterationMethod); } return null; } + private boolean isCustomPagingOkWithFetchAllIteration(ObjectQuery query) { + return query != null + && query.getPaging() != null + && query.getPaging().getMaxSize() != null + && query.getPaging().getMaxSize() <= getConfiguration().getMaxObjectsForImplicitFetchAllIterationMethod(); + } + public static boolean isCustomPagingOkWithPagedSeqIteration(ObjectQuery query) { if (query == null || query.getPaging() == null) { return true; diff --git a/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/helpers/ObjectRetriever.java b/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/helpers/ObjectRetriever.java index 41271460bc0..8d0fbfff223 100644 --- a/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/helpers/ObjectRetriever.java +++ b/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/helpers/ObjectRetriever.java @@ -787,9 +787,7 @@ public void searchObjectsIterativeAttempt(Class type, } public void searchObjectsIterativeByPaging(Class type, ObjectQuery query, - ResultHandler handler, - Collection> options, - OperationResult result) + ResultHandler handler, Collection> options, OperationResult result) throws SchemaException { try { @@ -912,6 +910,25 @@ public void searchObjectsIterativeByPagingStrictlySequent } } + public void searchObjectsIterativeByFetchAll(Class type, ObjectQuery query, + ResultHandler handler, Collection> options, OperationResult result) + throws SchemaException { + try { + SearchResultList> objects = repositoryService.searchObjects(type, query, options, result); + for (PrismObject object : objects) { + if (!handler.handle(object, result)) { + break; + } + } + } finally { + if (result.isUnknown()) { + result.computeStatus(); + } + result.setSummarizeSuccesses(true); + result.summarize(); + } + } + public boolean isAnySubordinateAttempt(String upperOrgOid, Collection lowerObjectOids) { Session session = null; try {