diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/security/MidPointGuiAuthorizationEvaluator.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/security/MidPointGuiAuthorizationEvaluator.java index 5ad9401f5be..dfb404f21a5 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/security/MidPointGuiAuthorizationEvaluator.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/security/MidPointGuiAuthorizationEvaluator.java @@ -294,16 +294,16 @@ public ObjectSecurityConstraints compileSecurityConstrain } @Override - public ObjectFilter preProcessObjectFilter(String operationUrl, AuthorizationPhaseType phase, + public ObjectFilter preProcessObjectFilter(String[] operationUrls, AuthorizationPhaseType phase, Class objectType, PrismObject object, ObjectFilter origFilter, String limitAuthorizationAction, Task task, OperationResult result) throws SchemaException, ObjectNotFoundException, ExpressionEvaluationException, CommunicationException, ConfigurationException, SecurityViolationException { - return securityEnforcer.preProcessObjectFilter(operationUrl, phase, objectType, object, origFilter, limitAuthorizationAction, task, result); + return securityEnforcer.preProcessObjectFilter(operationUrls, phase, objectType, object, origFilter, limitAuthorizationAction, task, result); } @Override - public boolean canSearch(String operationUrl, + public boolean canSearch(String[] operationUrls, AuthorizationPhaseType phase, Class objectType, PrismObject object, boolean includeSpecial, ObjectFilter filter, Task task, OperationResult result) throws SchemaException, ObjectNotFoundException, ExpressionEvaluationException, CommunicationException, ConfigurationException, SecurityViolationException { - return securityEnforcer.canSearch(operationUrl, phase, objectType, object, includeSpecial, filter, task, result); + return securityEnforcer.canSearch(operationUrls, phase, objectType, object, includeSpecial, filter, task, result); } @Override diff --git a/model/model-api/src/main/java/com/evolveum/midpoint/model/api/ModelAuthorizationAction.java b/model/model-api/src/main/java/com/evolveum/midpoint/model/api/ModelAuthorizationAction.java index 555562a36ee..f8b5e2af931 100644 --- a/model/model-api/src/main/java/com/evolveum/midpoint/model/api/ModelAuthorizationAction.java +++ b/model/model-api/src/main/java/com/evolveum/midpoint/model/api/ModelAuthorizationAction.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. @@ -23,6 +23,8 @@ public enum ModelAuthorizationAction implements DisplayableValue { READ("read", "Read", "READ_HELP"), + GET("get", "Get", "GET_HELP"), + SEARCH("search", "Search", "SEARCH_HELP"), ADD("add", "Add", "ADD_HELP"), MODIFY("modify", "Modify", "MODIFY_HELP"), DELETE("delete", "Delete", "DELETE_HELP"), @@ -85,6 +87,13 @@ public enum ModelAuthorizationAction implements DisplayableValue { RAW_OPERATION("rawOperation", "Raw operation", "RAW_OPERATION_HELP"), PARTIAL_EXECUTION("partialExecution", "Partial execution", "PARTIAL_EXECUTION_HELP"); + + public static final String[] AUTZ_ACTIONS_URLS_SEARCH = new String[] { READ.getUrl(), SEARCH.getUrl() }; + public static final String[] AUTZ_ACTIONS_URLS_GET = new String[] { READ.getUrl(), GET.getUrl() }; + public static final String[] AUTZ_ACTIONS_URLS_ADD = new String[] { ADD.getUrl() }; + public static final String[] AUTZ_ACTIONS_URLS_MODIFY = new String[] { MODIFY.getUrl() }; + public static final String[] AUTZ_ACTIONS_URLS_ASSIGN = new String[] { ASSIGN.getUrl() }; + public static final String[] AUTZ_ACTIONS_URLS_ATTORNEY = new String[] { ATTORNEY.getUrl() }; private String url; private String label; diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/controller/ModelController.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/controller/ModelController.java index bbbb0a4ed38..1ecc3475391 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/controller/ModelController.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/controller/ModelController.java @@ -790,16 +790,23 @@ public SearchResultList> searchObjects(Cla QNameUtil.setTemporarilyTolerateUndeclaredPrefixes(true); } switch (searchProvider) { - case EMULATED: list = emulatedSearchProvider.searchObjects(type, query, options, result); break; - case REPOSITORY: list = cacheRepositoryService.searchObjects(type, query, options, result); break; - case PROVISIONING: list = provisioning.searchObjects(type, query, options, task, result); break; + case EMULATED: + list = emulatedSearchProvider.searchObjects(type, query, options, result); + break; + case REPOSITORY: + list = cacheRepositoryService.searchObjects(type, query, options, result); + break; + case PROVISIONING: + list = provisioning.searchObjects(type, query, options, task, result); + break; case TASK_MANAGER: list = taskManager.searchObjects(type, query, options, result); if (workflowManager != null && TaskType.class.isAssignableFrom(type) && !GetOperationOptions.isRaw(rootOptions) && !GetOperationOptions.isNoFetch(rootOptions)) { workflowManager.augmentTaskObjectList(list, options, task, result); } break; - default: throw new AssertionError("Unexpected search provider: " + searchProvider); + default: + throw new AssertionError("Unexpected search provider: " + searchProvider); } result.computeStatus(); result.cleanupResult(); @@ -816,6 +823,10 @@ public SearchResultList> searchObjects(Cla if (list == null) { list = new SearchResultList<>(new ArrayList>()); } + + if (LOGGER.isTraceEnabled()) { + LOGGER.trace("Basic search returned {} results (before hooks, security, etc.)", list.size()); + } for (PrismObject object : list) { if (hookRegistry != null) { @@ -842,6 +853,10 @@ public SearchResultList> searchObjects(Cla } finally { exitModelMethod(); } + + if (LOGGER.isTraceEnabled()) { + LOGGER.trace("Final search returned {} results (after hooks, security and all other processing)", list.size()); + } return list; } @@ -1051,13 +1066,17 @@ protected boolean isFilterNone(ObjectQuery query, OperationResult result) { } protected void logQuery(ObjectQuery query) { + if (!LOGGER.isTraceEnabled()) { + return; + } if (query != null){ if (query.getPaging() == null) { - LOGGER.trace("Searching objects with null paging (query in TRACE)."); + LOGGER.trace("Searching objects with null paging. Query:\n{}", query.debugDump(1)); } else { - LOGGER.trace("Searching objects from {} to {} ordered {} by {} (query in TRACE).", + LOGGER.trace("Searching objects from {} to {} ordered {} by {}. Query:\n{}", query.getPaging().getOffset(), query.getPaging().getMaxSize(), - query.getPaging().getDirection(), query.getPaging().getOrderBy()); + query.getPaging().getDirection(), query.getPaging().getOrderBy(), + query.debugDump(1)); } } } @@ -1086,6 +1105,7 @@ public SearchResultMetadata searchObjectsIterative(Class< query = preProcessQuerySecurity(type, query, task, result); if (isFilterNone(query, result)) { + LOGGER.trace("Skipping search because filter is NONE"); return null; } @@ -1165,6 +1185,7 @@ public Integer countObjects(Class type, ObjectQuery qu query = preProcessQuerySecurity(type, query, task, result); if (isFilterNone(query, result)) { + LOGGER.trace("Skipping count because filter is NONE"); return 0; } @@ -1180,9 +1201,15 @@ public Integer countObjects(Class type, ObjectQuery qu objectManager = ObjectTypes.ObjectManager.REPOSITORY; } switch (objectManager) { - case PROVISIONING: count = provisioning.countObjects(type, query, options, task, parentResult); break; - case REPOSITORY: count = cacheRepositoryService.countObjects(type, query, options, parentResult); break; - case TASK_MANAGER: count = taskManager.countObjects(type, query, parentResult); break; + case PROVISIONING: + count = provisioning.countObjects(type, query, options, task, parentResult); + break; + case REPOSITORY: + count = cacheRepositoryService.countObjects(type, query, options, parentResult); + break; + case TASK_MANAGER: + count = taskManager.countObjects(type, query, parentResult); + break; default: throw new AssertionError("Unexpected objectManager: " + objectManager); } } catch (ConfigurationException | SecurityViolationException | SchemaException | ObjectNotFoundException | CommunicationException | ExpressionEvaluationException | RuntimeException | Error e) { @@ -1761,13 +1788,14 @@ private ObjectQuery preProcessQuerySecurity(Class obje if (origQuery != null) { origFilter = origQuery.getFilter(); } - ObjectFilter secFilter = securityEnforcer.preProcessObjectFilter(ModelAuthorizationAction.READ.getUrl(), null, objectType, null, origFilter, null, task, result); + ObjectFilter secFilter = securityEnforcer.preProcessObjectFilter(ModelAuthorizationAction.AUTZ_ACTIONS_URLS_SEARCH, null, objectType, null, origFilter, null, task, result); return updateObjectQuery(origQuery, secFilter); } // we expect that objectType is a direct parent of containerType private ObjectQuery preProcessSubobjectQuerySecurity(Class containerType, Class objectType, ObjectQuery origQuery, Task task, OperationResult result) throws SchemaException, ObjectNotFoundException, ExpressionEvaluationException, CommunicationException, ConfigurationException, SecurityViolationException { - ObjectFilter secParentFilter = securityEnforcer.preProcessObjectFilter(ModelAuthorizationAction.READ.getUrl(), null, objectType, null, null, null, task, result); + // Search containers is an operation on one object. Therefore even if it works with a search filter, it requires GET authorizations + ObjectFilter secParentFilter = securityEnforcer.preProcessObjectFilter(ModelAuthorizationAction.AUTZ_ACTIONS_URLS_GET, null, objectType, null, null, null, task, result); if (secParentFilter == null || secParentFilter instanceof AllFilter) { return origQuery; // no need to update the query } diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/controller/ModelInteractionServiceImpl.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/controller/ModelInteractionServiceImpl.java index 03ea78ca2d2..858e03676a0 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/controller/ModelInteractionServiceImpl.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/controller/ModelInteractionServiceImpl.java @@ -399,12 +399,12 @@ public RefinedObjectClassDefinition getEditObjectClassDefinition(PrismObject RoleSelectionSpecification getAssignableRoleSpecifi spec.setFilter(NoneFilter.createNone()); return spec; } - decision = securityConstraints.getActionDecision(ModelAuthorizationAction.MODIFY.getUrl(), AuthorizationPhaseType.REQUEST); + decision = securityConstraints.findAllItemsDecision(ModelAuthorizationAction.MODIFY.getUrl(), AuthorizationPhaseType.REQUEST); if (decision == AuthorizationDecisionType.ALLOW) { getAllRoleTypesSpec(spec, result); result.recordSuccess(); @@ -489,7 +489,7 @@ public RoleSelectionSpecification getAssignableRoleSpecifi } try { - ObjectFilter filter = securityEnforcer.preProcessObjectFilter(ModelAuthorizationAction.ASSIGN.getUrl(), + ObjectFilter filter = securityEnforcer.preProcessObjectFilter(ModelAuthorizationAction.AUTZ_ACTIONS_URLS_ASSIGN, AuthorizationPhaseType.REQUEST, AbstractRoleType.class, focus, AllFilter.createAll(), null, task, result); LOGGER.trace("assignableRoleSpec filter: {}", filter); spec.setFilter(filter); @@ -679,7 +679,7 @@ private RoleSelectionSpecEntry getRoleSelectionSpecEq(EqualFilter eqFilt @Override public ObjectFilter getDonorFilter(Class searchResultType, ObjectFilter origFilter, String targetAuthorizationAction, Task task, OperationResult parentResult) throws SchemaException, ObjectNotFoundException, ExpressionEvaluationException, CommunicationException, ConfigurationException, SecurityViolationException { - return securityEnforcer.preProcessObjectFilter(ModelAuthorizationAction.ATTORNEY.getUrl(), null, searchResultType, null, origFilter, targetAuthorizationAction, task, parentResult); + return securityEnforcer.preProcessObjectFilter(ModelAuthorizationAction.AUTZ_ACTIONS_URLS_ATTORNEY, null, searchResultType, null, origFilter, targetAuthorizationAction, task, parentResult); } @Override @@ -689,7 +689,7 @@ public boolean canSearch(Class r if (objectOid != null) { object = (PrismObject) objectResolver.getObject(objectType, objectOid, null, task, result).asPrismObject(); } - return securityEnforcer.canSearch(ModelAuthorizationAction.READ.getUrl(), null, resultType, object, includeSpecial, query.getFilter(), task, result); + return securityEnforcer.canSearch(ModelAuthorizationAction.AUTZ_ACTIONS_URLS_SEARCH, null, resultType, object, includeSpecial, query.getFilter(), task, result); } @Override diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/controller/SchemaTransformer.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/controller/SchemaTransformer.java index 91ad0fad357..f3440ce9d5b 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/controller/SchemaTransformer.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/controller/SchemaTransformer.java @@ -258,15 +258,15 @@ private void applySchemasAndSecurityPhase(PrismObject throws SchemaException, SecurityViolationException, ConfigurationException, ObjectNotFoundException { Validate.notNull(phase); try { - AuthorizationDecisionType globalReadDecision = securityConstraints.getActionDecision(ModelAuthorizationAction.READ.getUrl(), phase); + AuthorizationDecisionType globalReadDecision = securityConstraints.findAllItemsDecision(ModelAuthorizationAction.AUTZ_ACTIONS_URLS_GET, phase); if (globalReadDecision == AuthorizationDecisionType.DENY) { // shortcut SecurityUtil.logSecurityDeny(object, "because the authorization denies access"); throw new AuthorizationException("Access denied"); } - AuthorizationDecisionType globalAddDecision = securityConstraints.getActionDecision(ModelAuthorizationAction.ADD.getUrl(), phase); - AuthorizationDecisionType globalModifyDecision = securityConstraints.getActionDecision(ModelAuthorizationAction.MODIFY.getUrl(), phase); + AuthorizationDecisionType globalAddDecision = securityConstraints.findAllItemsDecision(ModelAuthorizationAction.ADD.getUrl(), phase); + AuthorizationDecisionType globalModifyDecision = securityConstraints.findAllItemsDecision(ModelAuthorizationAction.MODIFY.getUrl(), phase); applySecurityConstraints(object.getValue().getItems(), securityConstraints, globalReadDecision, globalAddDecision, globalModifyDecision, phase); if (object.isEmpty()) { @@ -300,9 +300,9 @@ public void applySecurityConstraints(List> items, ObjectSecurityConstr continue; } ItemPath nameOnlyItemPath = itemPath.namedSegmentsOnly(); - AuthorizationDecisionType itemReadDecision = computeItemDecision(securityConstraints, nameOnlyItemPath, ModelAuthorizationAction.READ.getUrl(), defaultReadDecision, phase); - AuthorizationDecisionType itemAddDecision = computeItemDecision(securityConstraints, nameOnlyItemPath, ModelAuthorizationAction.ADD.getUrl(), defaultReadDecision, phase); - AuthorizationDecisionType itemModifyDecision = computeItemDecision(securityConstraints, nameOnlyItemPath, ModelAuthorizationAction.MODIFY.getUrl(), defaultReadDecision, phase); + AuthorizationDecisionType itemReadDecision = computeItemDecision(securityConstraints, nameOnlyItemPath, ModelAuthorizationAction.AUTZ_ACTIONS_URLS_GET, defaultReadDecision, phase); + AuthorizationDecisionType itemAddDecision = computeItemDecision(securityConstraints, nameOnlyItemPath, ModelAuthorizationAction.AUTZ_ACTIONS_URLS_ADD, defaultReadDecision, phase); + AuthorizationDecisionType itemModifyDecision = computeItemDecision(securityConstraints, nameOnlyItemPath, ModelAuthorizationAction.AUTZ_ACTIONS_URLS_MODIFY, defaultReadDecision, phase); LOGGER.trace("applySecurityConstraints(item): {}: decisions R={}, A={}, M={}", itemPath, itemReadDecision, itemAddDecision, itemModifyDecision); if (itemDef != null) { @@ -380,9 +380,9 @@ private void applySecurityConstraintsItemDef(D itemDe boolean thisWasSeen = definitionsSeen.containsKey(itemDefinition); definitionsSeen.put(itemDefinition, null); - AuthorizationDecisionType readDecision = computeItemDecision(securityConstraints, nameOnlyItemPath, ModelAuthorizationAction.READ.getUrl(), defaultReadDecision, phase); - AuthorizationDecisionType addDecision = computeItemDecision(securityConstraints, nameOnlyItemPath, ModelAuthorizationAction.ADD.getUrl(), defaultAddDecision, phase); - AuthorizationDecisionType modifyDecision = computeItemDecision(securityConstraints, nameOnlyItemPath, ModelAuthorizationAction.MODIFY.getUrl(), defaultModifyDecision, phase); + AuthorizationDecisionType readDecision = computeItemDecision(securityConstraints, nameOnlyItemPath, ModelAuthorizationAction.AUTZ_ACTIONS_URLS_GET, defaultReadDecision, phase); + AuthorizationDecisionType addDecision = computeItemDecision(securityConstraints, nameOnlyItemPath, ModelAuthorizationAction.AUTZ_ACTIONS_URLS_ADD, defaultAddDecision, phase); + AuthorizationDecisionType modifyDecision = computeItemDecision(securityConstraints, nameOnlyItemPath, ModelAuthorizationAction.AUTZ_ACTIONS_URLS_MODIFY, defaultModifyDecision, phase); boolean anySubElementRead = false; boolean anySubElementAdd = false; @@ -436,9 +436,9 @@ private void applySecurityConstraintsItemDef(D itemDe } } - public AuthorizationDecisionType computeItemDecision(ObjectSecurityConstraints securityConstraints, ItemPath nameOnlyItemPath, String actionUrl, + public AuthorizationDecisionType computeItemDecision(ObjectSecurityConstraints securityConstraints, ItemPath nameOnlyItemPath, String[] actionUrls, AuthorizationDecisionType defaultDecision, AuthorizationPhaseType phase) { - AuthorizationDecisionType explicitDecision = securityConstraints.findItemDecision(nameOnlyItemPath, actionUrl, phase); + AuthorizationDecisionType explicitDecision = securityConstraints.findItemDecision(nameOnlyItemPath, actionUrls, phase); // LOGGER.trace("Explicit decision for {} ({} {}): {}", nameOnlyItemPath, actionUrl, phase, explicitDecision); if (explicitDecision != null) { return explicitDecision; 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 7db01c54c09..b3edd4562b4 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 @@ -206,6 +206,9 @@ public abstract class AbstractSecurityTest extends AbstractInitializedModelInteg protected static final File ROLE_PROP_READ_SOME_MODIFY_SOME_USER_FILE = new File(TEST_DIR, "role-prop-read-some-modify-some-user.xml"); protected static final String ROLE_PROP_READ_SOME_MODIFY_SOME_USER_OID = "00000000-0000-0000-0000-00000000ae08"; + protected static final File ROLE_PROP_GET_SEARCH_SOME_MODIFY_SOME_USER_FILE = new File(TEST_DIR, "role-prop-get-search-some-modify-some-user.xml"); + protected static final String ROLE_PROP_GET_SEARCH_SOME_MODIFY_SOME_USER_OID = "e0f81542-af58-11e8-8537-87b51775fc04"; + protected static final File ROLE_PROP_DENY_MODIFY_SOME_FILE = new File(TEST_DIR, "role-prop-deny-modify-some.xml"); protected static final String ROLE_PROP_DENY_MODIFY_SOME_OID = "d867ca80-b18a-11e6-826e-1b0f95ef9125"; @@ -349,7 +352,10 @@ public abstract class AbstractSecurityTest extends AbstractInitializedModelInteg protected static final File ROLE_UNASSIGN_SELF_REQUESTABLE_FILE = new File(TEST_DIR, "role-unassign-self-requestable.xml"); protected static final String ROLE_UNASSIGN_SELF_REQUESTABLE_OID = "7c903f28-04ed-11e8-bb7a-df31e8679d27"; - + + protected static final File ROLE_END_USER_WITH_PRIVACY_FILE = new File(TEST_DIR, "role-end-user-with-privacy.xml"); + protected static final String ROLE_END_USER_WITH_PRIVACY_OID = "2abaef72-af5b-11e8-ae9a-b33bc5b8cb74"; + protected static final File ORG_REQUESTABLE_FILE = new File(TEST_DIR,"org-requestable.xml"); protected static final String ORG_REQUESTABLE_OID = "8f2bd344-a46c-4c0b-aa34-db08b7d7f7f2"; @@ -389,7 +395,7 @@ public abstract class AbstractSecurityTest extends AbstractInitializedModelInteg protected static final XMLGregorianCalendar JACK_VALID_TO_LONG_AGEAD = XmlTypeConverter.createXMLGregorianCalendar(10000000000000L); protected static final int NUMBER_OF_ALL_USERS = 11; - protected static final int NUMBER_OF_IMPORTED_ROLES = 70; + protected static final int NUMBER_OF_IMPORTED_ROLES = 71; protected static final int NUMBER_OF_ALL_ORGS = 11; protected String userRumRogersOid; @@ -420,6 +426,7 @@ public void initSystem(Task initTask, OperationResult initResult) throws Excepti repoAddObjectFromFile(ROLE_PROP_READ_SOME_MODIFY_SOME_REQ_EXEC_FILE, initResult); repoAddObjectFromFile(ROLE_PROP_READ_SOME_MODIFY_SOME_EXEC_ALL_FILE, initResult); repoAddObjectFromFile(ROLE_PROP_READ_SOME_MODIFY_SOME_USER_FILE, initResult); + repoAddObjectFromFile(ROLE_PROP_GET_SEARCH_SOME_MODIFY_SOME_USER_FILE, initResult); repoAddObjectFromFile(ROLE_PROP_DENY_MODIFY_SOME_FILE, initResult); repoAddObjectFromFile(ROLE_READ_JACKS_CAMPAIGNS_FILE, initResult); repoAddObjectFromFile(ROLE_READ_SOME_ROLES_FILE, initResult); 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 6e01e6f1a8d..3fc22ac4c28 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 @@ -158,15 +158,42 @@ public void initSystem(Task initTask, OperationResult initResult) throws Excepti repoAddObjectFromFile(ROLE_PROP_EXCEPT_ASSIGNMENT_FILE, initResult); repoAddObjectFromFile(ROLE_PROP_EXCEPT_ADMINISTRATIVE_STATUS_FILE, initResult); repoAddObjectFromFile(ROLE_ASSIGN_ORG_FILE, initResult); + repoAddObjectFromFile(ROLE_END_USER_WITH_PRIVACY_FILE, initResult); setDefaultObjectTemplate(UserType.COMPLEX_TYPE, USER_TEMPLATE_SECURITY_OID, initResult); } - protected static final int NUMBER_OF_IMPORTED_ROLES = 10; + protected static final int NUMBER_OF_IMPORTED_ROLES = 11; 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(); + } /** * Simple end-user password change. But clear Jack's credentials before @@ -3004,6 +3031,51 @@ public void test320AutzJackGuybrushValutDweller() throws Exception { assertGlobalStateUntouched(); } + /** + * We can get any users, but we can search only the CAPTAINs. + * + * MID-4860, MID-4654, MID-4859 + */ + @Test + public void test330AutzJackEndUserWithPrivacy() throws Exception { + final String TEST_NAME = "test330AutzJackEndUserWithPrivacy"; + displayTestTitle(TEST_NAME); + // GIVEN + cleanupAutzTest(USER_JACK_OID); + assertNoDummyAccount(RESOURCE_DUMMY_VAULT_NAME, USER_GUYBRUSH_USERNAME); + + assignRole(USER_JACK_OID, ROLE_END_USER_WITH_PRIVACY_OID); + login(USER_JACK_USERNAME); + + // WHEN + displayWhen(TEST_NAME); + + PrismObject userJack = assertGetAllow(UserType.class, USER_JACK_OID); + display("Jack", userJack); + // Access to employeeType is not allowed for get. Therefore is should not part of the result. + PrismAsserts.assertNoItem(userJack, UserType.F_EMPLOYEE_TYPE); + + // Direct get, should be allowed even though guybrush is not a CAPTAIN + PrismObject userBuybrush = assertGetAllow(UserType.class, USER_GUYBRUSH_OID); + display("Guybrush", userBuybrush); + + assertReadDenyRaw(); + assertGetDeny(LookupTableType.class, LOOKUP_LANGUAGES_OID); + + assertSearch(UserType.class, null, 1); + assertSearchDeny(UserType.class, null, SelectorOptions.createCollection(GetOperationOptions.createRaw())); + assertSearch(UserType.class, createNameQuery(USER_JACK_USERNAME), 1); + assertSearchDeny(UserType.class, createNameQuery(USER_JACK_USERNAME), SelectorOptions.createCollection(GetOperationOptions.createRaw())); + assertSearch(UserType.class, createNameQuery(USER_GUYBRUSH_USERNAME), 0); + assertSearchDeny(UserType.class, createNameQuery(USER_GUYBRUSH_USERNAME), SelectorOptions.createCollection(GetOperationOptions.createRaw())); + + assertAddDeny(); + assertModifyDeny(); + assertDeleteDeny(); + + assertGlobalStateUntouched(); + } + private void modifyJackValidTo() throws ObjectNotFoundException, SchemaException, ExpressionEvaluationException, CommunicationException, ConfigurationException, ObjectAlreadyExistsException, PolicyViolationException, SecurityViolationException { Task task = createTask("modifyJackValidTo"); OperationResult result = task.getResult(); diff --git a/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/security/TestSecurityBasic.java b/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/security/TestSecurityBasic.java index 7bd79ae7fd6..bbab62766e6 100644 --- a/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/security/TestSecurityBasic.java +++ b/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/security/TestSecurityBasic.java @@ -630,9 +630,29 @@ public void test216AutzJackPropReadSomeModifySomeUser() throws Exception { assignRole(USER_JACK_OID, ROLE_PROP_READ_SOME_MODIFY_SOME_USER_OID); login(USER_JACK_USERNAME); + doReadSomeModifySomeUser(TEST_NAME); + } + + /** + * Same as test216AutzJackPropReadSomeModifySomeUser, but with get+search instead of read. + */ + @Test + public void test217AutzJackPropGetSearchSomeModifySomeUser() throws Exception { + final String TEST_NAME = "test217AutzJackPropGetSearchSomeModifySomeUser"; + displayTestTitle(TEST_NAME); + // GIVEN + cleanupAutzTest(USER_JACK_OID); + assignRole(USER_JACK_OID, ROLE_PROP_GET_SEARCH_SOME_MODIFY_SOME_USER_OID); + login(USER_JACK_USERNAME); + + doReadSomeModifySomeUser(TEST_NAME); + } + + private void doReadSomeModifySomeUser(final String TEST_NAME) throws Exception { + // WHEN displayWhen(TEST_NAME); - + PrismObject userJack = getUser(USER_JACK_OID); display("Jack", userJack); assertUserJackReadSomeModifySome(userJack, 1); diff --git a/model/model-intest/src/test/resources/logback-test.xml b/model/model-intest/src/test/resources/logback-test.xml index ca0b823bba4..34fd61718ea 100644 --- a/model/model-intest/src/test/resources/logback-test.xml +++ b/model/model-intest/src/test/resources/logback-test.xml @@ -105,9 +105,9 @@ - + - + diff --git a/model/model-intest/src/test/resources/security/role-end-user-with-privacy.xml b/model/model-intest/src/test/resources/security/role-end-user-with-privacy.xml new file mode 100644 index 00000000000..00ce8972d3f --- /dev/null +++ b/model/model-intest/src/test/resources/security/role-end-user-with-privacy.xml @@ -0,0 +1,51 @@ + + + End User With Privacy + End user that can read all other users. But he can search only some of them. + + privacy-get + http://midpoint.evolveum.com/xml/ns/public/security/authorization-model-3#get + + UserType + + name + fullName + activation/administrativeStatus + assignment + + + privacy-search + http://midpoint.evolveum.com/xml/ns/public/security/authorization-model-3#search + + UserType + + + employeeType + CAPTAIN + + + + name + fullName + + employeeType + + diff --git a/model/model-intest/src/test/resources/security/role-prop-get-search-some-modify-some-user.xml b/model/model-intest/src/test/resources/security/role-prop-get-search-some-modify-some-user.xml new file mode 100644 index 00000000000..1eec41ab0e3 --- /dev/null +++ b/model/model-intest/src/test/resources/security/role-prop-get-search-some-modify-some-user.xml @@ -0,0 +1,76 @@ + + + Prop Get+Search Some Modify Some User + Almost the same as role-prop-read-some-modify-some-user. But get+search actions are used insead of read. + + get-search some + http://midpoint.evolveum.com/xml/ns/public/security/authorization-model-3#get + http://midpoint.evolveum.com/xml/ns/public/security/authorization-model-3#search + + UserType + + + employeeType + CAPTAIN + + + + c:name + fullName + activation/administrativeStatus + assignment + + + modify some + http://midpoint.evolveum.com/xml/ns/public/security/authorization-model-3#modify + + UserType + + + employeeType + CAPTAIN + + + + fullName + additionalName + description + activation/validFrom + + + modify-some-operational-exec + Required, these are operational properties that midPoint changes automatically + http://midpoint.evolveum.com/xml/ns/public/security/authorization-model-3#modify + execution + + UserType + + + employeeType + CAPTAIN + + + + activation/validityStatus + activation/validityChangeTimestamp + activation/effectiveStatus + + diff --git a/repo/security-enforcer-api/src/main/java/com/evolveum/midpoint/security/enforcer/api/ObjectSecurityConstraints.java b/repo/security-enforcer-api/src/main/java/com/evolveum/midpoint/security/enforcer/api/ObjectSecurityConstraints.java index ef2dba10022..d75626897f8 100644 --- a/repo/security-enforcer-api/src/main/java/com/evolveum/midpoint/security/enforcer/api/ObjectSecurityConstraints.java +++ b/repo/security-enforcer-api/src/main/java/com/evolveum/midpoint/security/enforcer/api/ObjectSecurityConstraints.java @@ -23,12 +23,10 @@ public interface ObjectSecurityConstraints extends DebugDumpable { /** - * This is old version of the findActionDecision method that was here before we have switched to findAllItemsDecision. - * This method is provided for compatibility. Whenever possible, the algorithms should be changed to use findAllItemsDecision(). - * There are few places where we really need to find action decision. For that reason there is a new findAllItemsDecision() method. + * Almost the same as findAllItemsDecision(String, ...), but in this case there are several equivalent action URLs. + * E.g. "read" and "get" actions. If any of them is denied, operation is denied. If any of them is allowed, operation is allowed. */ - @Deprecated - AuthorizationDecisionType getActionDecision(String actionUrl, AuthorizationPhaseType phase); + AuthorizationDecisionType findAllItemsDecision(String[] actionUrls, AuthorizationPhaseType phase); /** * Returns decision for the whole action. This is fact returns a decision that applies to all items - if there is any. @@ -37,6 +35,8 @@ public interface ObjectSecurityConstraints extends DebugDumpable { */ AuthorizationDecisionType findAllItemsDecision(String actionUrl, AuthorizationPhaseType phase); + AuthorizationDecisionType findItemDecision(ItemPath nameOnlyItemPath, String[] actionUrls, AuthorizationPhaseType phase); + AuthorizationDecisionType findItemDecision(ItemPath nameOnlyItemPath, String actionUrl, AuthorizationPhaseType phase); } diff --git a/repo/security-enforcer-api/src/main/java/com/evolveum/midpoint/security/enforcer/api/SecurityEnforcer.java b/repo/security-enforcer-api/src/main/java/com/evolveum/midpoint/security/enforcer/api/SecurityEnforcer.java index 29994124dad..61de6a592bc 100644 --- a/repo/security-enforcer-api/src/main/java/com/evolveum/midpoint/security/enforcer/api/SecurityEnforcer.java +++ b/repo/security-enforcer-api/src/main/java/com/evolveum/midpoint/security/enforcer/api/SecurityEnforcer.java @@ -93,14 +93,14 @@ void authorize(String operationUrl, * @param limitAuthorizationAction only consider authorizations that are not limited with respect to this action. * If null then all authorizations are considered. */ - ObjectFilter preProcessObjectFilter(String operationUrl, AuthorizationPhaseType phase, + ObjectFilter preProcessObjectFilter(String[] operationUrls, AuthorizationPhaseType phase, Class searchResultType, PrismObject object, ObjectFilter origFilter, String limitAuthorizationAction, Task task, OperationResult result) throws SchemaException, ObjectNotFoundException, ExpressionEvaluationException, CommunicationException, ConfigurationException, SecurityViolationException; /** * @param includeSpecial include special authorizations such as "self" */ - boolean canSearch(String operationUrl, AuthorizationPhaseType phase, + boolean canSearch(String[] operationUrls, AuthorizationPhaseType phase, Class searchResultType, PrismObject object, boolean includeSpecial, ObjectFilter filter, Task task, OperationResult result) throws SchemaException, ObjectNotFoundException, ExpressionEvaluationException, CommunicationException, ConfigurationException, SecurityViolationException; /** diff --git a/repo/security-enforcer-impl/src/main/java/com/evolveum/midpoint/security/enforcer/impl/ObjectSecurityConstraintsImpl.java b/repo/security-enforcer-impl/src/main/java/com/evolveum/midpoint/security/enforcer/impl/ObjectSecurityConstraintsImpl.java index 740c09d6333..045ce216208 100644 --- a/repo/security-enforcer-impl/src/main/java/com/evolveum/midpoint/security/enforcer/impl/ObjectSecurityConstraintsImpl.java +++ b/repo/security-enforcer-impl/src/main/java/com/evolveum/midpoint/security/enforcer/impl/ObjectSecurityConstraintsImpl.java @@ -62,10 +62,20 @@ private ItemSecurityConstraintsImpl getItemConstraints(String action, Authorizat } @Override - public AuthorizationDecisionType getActionDecision(String actionUrl, AuthorizationPhaseType phase) { - return findAllItemsDecision(actionUrl, phase); + public AuthorizationDecisionType findAllItemsDecision(String[] actionUrls, AuthorizationPhaseType phase) { + AuthorizationDecisionType decision = null; + for (String actionUrl : actionUrls) { + AuthorizationDecisionType actionDecision = findAllItemsDecision(actionUrl, phase); + if (actionDecision == AuthorizationDecisionType.DENY) { + return actionDecision; + } + if (actionDecision != null) { + decision = actionDecision; + } + } + return decision; } - + @Override public AuthorizationDecisionType findAllItemsDecision(String actionUrl, AuthorizationPhaseType phase) { if (phase == null) { @@ -95,6 +105,21 @@ public AuthorizationDecisionType getActionDecisionPhase(String actionUrl, Author return null; } + @Override + public AuthorizationDecisionType findItemDecision(ItemPath nameOnlyItemPath, String[] actionUrls, AuthorizationPhaseType phase) { + AuthorizationDecisionType decision = null; + for (String actionUrl : actionUrls) { + AuthorizationDecisionType actionDecision = findItemDecision(nameOnlyItemPath, actionUrl, phase); + if (actionDecision == AuthorizationDecisionType.DENY) { + return actionDecision; + } + if (actionDecision != null) { + decision = actionDecision; + } + } + return decision; + } + @Override public AuthorizationDecisionType findItemDecision(ItemPath nameOnlyItemPath, String actionUrl, AuthorizationPhaseType phase) { if (phase == null) { 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 448988ae548..5fbb7409c61 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 @@ -16,6 +16,7 @@ package com.evolveum.midpoint.security.enforcer.impl; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.List; import java.util.function.Consumer; @@ -985,7 +986,7 @@ public ObjectSecurityConstraints compileSecurityConstrain } @Override - public ObjectFilter preProcessObjectFilter(String operationUrl, AuthorizationPhaseType phase, + public ObjectFilter preProcessObjectFilter(String[] operationUrls, AuthorizationPhaseType phase, Class searchResultType, PrismObject object, ObjectFilter origFilter, String limitAuthorizationAction, Task task, OperationResult result) throws SchemaException, ObjectNotFoundException, ExpressionEvaluationException, CommunicationException, ConfigurationException, SecurityViolationException { MidPointPrincipal principal = getMidPointPrincipal(); if (LOGGER.isTraceEnabled()) { @@ -997,14 +998,14 @@ public ObjectFilter preProcessObjec } ObjectFilter finalFilter; if (phase != null) { - finalFilter = preProcessObjectFilterInternal(principal, operationUrl, phase, + finalFilter = preProcessObjectFilterInternal(principal, operationUrls, phase, true, searchResultType, object, true, origFilter, limitAuthorizationAction, "search pre-process", task, result); } else { - ObjectFilter filterBoth = preProcessObjectFilterInternal(principal, operationUrl, null, + ObjectFilter filterBoth = preProcessObjectFilterInternal(principal, operationUrls, null, false, searchResultType, object, true, origFilter, limitAuthorizationAction, "search pre-process", task, result); - ObjectFilter filterRequest = preProcessObjectFilterInternal(principal, operationUrl, AuthorizationPhaseType.REQUEST, + ObjectFilter filterRequest = preProcessObjectFilterInternal(principal, operationUrls, AuthorizationPhaseType.REQUEST, false, searchResultType, object, true, origFilter, limitAuthorizationAction, "search pre-process", task, result); - ObjectFilter filterExecution = preProcessObjectFilterInternal(principal, operationUrl, AuthorizationPhaseType.EXECUTION, + ObjectFilter filterExecution = preProcessObjectFilterInternal(principal, operationUrls, AuthorizationPhaseType.EXECUTION, false, searchResultType, object, true, origFilter, limitAuthorizationAction, "search pre-process", task, result); finalFilter = ObjectQueryUtil.filterOr(filterBoth, ObjectQueryUtil.filterAnd(filterRequest, filterExecution)); } @@ -1020,7 +1021,7 @@ public ObjectFilter preProcessObjec } @Override - public boolean canSearch(String operationUrl, + public boolean canSearch(String[] operationUrls, AuthorizationPhaseType phase, Class searchResultType, PrismObject object, boolean includeSpecial, ObjectFilter filter, Task task, OperationResult result) throws SchemaException, ObjectNotFoundException, ExpressionEvaluationException, CommunicationException, ConfigurationException, SecurityViolationException { MidPointPrincipal principal = getMidPointPrincipal(); @@ -1033,14 +1034,14 @@ public boolean canSearch(String ope } ObjectFilter finalFilter; if (phase != null) { - finalFilter = preProcessObjectFilterInternal(principal, operationUrl, phase, + finalFilter = preProcessObjectFilterInternal(principal, operationUrls, phase, true, searchResultType, object, includeSpecial, filter, null, "search permission", task, result); } else { - ObjectFilter filterBoth = preProcessObjectFilterInternal(principal, operationUrl, null, + ObjectFilter filterBoth = preProcessObjectFilterInternal(principal, operationUrls, null, false, searchResultType, object, includeSpecial, filter, null, "search permission", task, result); - ObjectFilter filterRequest = preProcessObjectFilterInternal(principal, operationUrl, AuthorizationPhaseType.REQUEST, + ObjectFilter filterRequest = preProcessObjectFilterInternal(principal, operationUrls, AuthorizationPhaseType.REQUEST, false, searchResultType, object, includeSpecial, filter, null, "search permission", task, result); - ObjectFilter filterExecution = preProcessObjectFilterInternal(principal, operationUrl, AuthorizationPhaseType.EXECUTION, + ObjectFilter filterExecution = preProcessObjectFilterInternal(principal, operationUrls, AuthorizationPhaseType.EXECUTION, false, searchResultType, object, includeSpecial, filter, null, "search permission", task, result); finalFilter = ObjectQueryUtil.filterOr(filterBoth, ObjectQueryUtil.filterAnd(filterRequest, filterExecution)); } @@ -1053,7 +1054,7 @@ public boolean canSearch(String ope return decision; } - private ObjectFilter preProcessObjectFilterInternal(MidPointPrincipal principal, String operationUrl, + private ObjectFilter preProcessObjectFilterInternal(MidPointPrincipal principal, String[] operationUrls, AuthorizationPhaseType phase, boolean includeNullPhase, Class objectType, PrismObject object, boolean includeSpecial, ObjectFilter origFilter, String limitAuthorizationAction, String desc, Task task, OperationResult result) throws SchemaException, ObjectNotFoundException, ExpressionEvaluationException, CommunicationException, ConfigurationException, SecurityViolationException { @@ -1074,24 +1075,28 @@ private ObjectFilter preProcessObje if (authority instanceof Authorization) { Authorization autz = (Authorization)authority; String autzHumanReadableDesc = autz.getHumanReadableDesc(); - LOGGER.trace(" Evaluating {}", autzHumanReadableDesc); + LOGGER.trace(" Evaluating {}", autzHumanReadableDesc); // action - if (!autz.getAction().contains(operationUrl) && !autz.getAction().contains(AuthorizationConstants.AUTZ_ALL_URL)) { - LOGGER.trace(" Authorization not applicable for operation {}", operationUrl); + if (!isApplicableForActions(autz, operationUrls)) { + if (LOGGER.isTraceEnabled()) { + LOGGER.trace(" Authorization not applicable for operation {}", prettyActionUrl(operationUrls)); + } continue; } // phase if (autz.getPhase() == phase || (includeNullPhase && autz.getPhase() == null)) { - LOGGER.trace(" Authorization is applicable for phases {} (continuing evaluation)", phase); + LOGGER.trace(" Authorization is applicable for phases {} (continuing evaluation)", phase); } else { - LOGGER.trace(" Authorization is not applicable for phase {}", phase); + LOGGER.trace(" Authorization is not applicable for phase {} (includeNullPhase={})", phase, includeNullPhase); continue; } if (!isApplicableLimitations(autz, limitAuthorizationAction)) { - LOGGER.trace(" Authorization is limited to other action, not applicable for operation {}", operationUrl); + if (LOGGER.isTraceEnabled()) { + LOGGER.trace(" Authorization is limited to other action, not applicable for operation {}", prettyActionUrl(operationUrls)); + } continue; } @@ -1110,9 +1115,9 @@ private ObjectFilter preProcessObje // .. but we need to decide whether this authorization is applicable to the object if (isApplicable(autz.getObject(), object, principal, null, "object", autzHumanReadableDesc, task, result)) { - LOGGER.trace(" Authorization is applicable for object {}", object); + LOGGER.trace(" Authorization is applicable for object {}", object); } else { - LOGGER.trace(" Authorization is not applicable for object {}", object); + LOGGER.trace(" Authorization is not applicable for object {}", object); continue; } } @@ -1120,7 +1125,7 @@ private ObjectFilter preProcessObje boolean applicable = true; if (objectSpecTypes == null || objectSpecTypes.isEmpty()) { - LOGGER.trace(" No {} specification in authorization (authorization is universaly applicable)", objectTargetSpec); + LOGGER.trace(" No {} specification in authorization (authorization is universaly applicable)", objectTargetSpec); autzObjSecurityFilter = AllFilter.createAll(); } else { @@ -1145,12 +1150,10 @@ private ObjectFilter preProcessObje } Class specObjectClass = specObjectDef.getCompileTimeClass(); if (!objectType.isAssignableFrom(specObjectClass)) { - LOGGER.trace(" Authorization not applicable for object because of type mismatch, authorization {}, query {}", - new Object[]{specObjectClass, objectType}); + traceClassMatch("Authorization not applicable for object because of type mismatch", specObjectClass, objectType); continue; } else { - LOGGER.trace(" Authorization is applicable for object because of type match, authorization {}, query {}", - new Object[]{specObjectClass, objectType}); + traceClassMatch("Authorization is applicable for object because of type match", specObjectClass, objectType); // The spec type is a subclass of requested type. So it might be returned from the search. // We need to use type filter. objSpecTypeFilter = TypeFilter.createType(specTypeQName, null); @@ -1336,8 +1339,8 @@ private ObjectFilter preProcessObje // This is "deny all". We cannot have anything stronger than that. // There is no point in continuing the evaluation. if (LOGGER.isTraceEnabled()) { - LOGGER.trace("AUTZ {} done: principal={}, operation={}, phase={}: deny all", - desc, getUsername(principal), prettyActionUrl(operationUrl), phase); + LOGGER.trace(" phase={} done: principal={}, operation={}, {}: deny all", + phase, getUsername(principal), prettyActionUrl(operationUrls), desc); } NoneFilter secFilter = NoneFilter.createNone(); traceFilter("secFilter", null, secFilter); @@ -1352,7 +1355,7 @@ private ObjectFilter preProcessObje traceFilter("securityFilterDeny", autz, securityFilterDeny); } else { - LOGGER.warn("Unknown authority type {} in user {}", authority.getClass(), getUsername(principal)); + LOGGER.warn(" Unknown authority type {} in user {}", authority.getClass(), getUsername(principal)); } } } @@ -1361,13 +1364,13 @@ private ObjectFilter preProcessObje traceFilter("securityFilterDeny", null, securityFilterDeny); if (LOGGER.isTraceEnabled()) { - LOGGER.trace(" Final items: {}", queryItemsSpec.shortDump()); + LOGGER.trace(" final items: {}", queryItemsSpec.shortDump()); } List unsatisfiedItems = queryItemsSpec.evaluateUnsatisfierItems(); if (!unsatisfiedItems.isEmpty()) { if (LOGGER.isTraceEnabled()) { - LOGGER.trace("AUTZ {} done: principal={}, operation={}, phase={}: deny because items {} are not allowed", - desc, getUsername(principal), prettyActionUrl(operationUrl), phase, unsatisfiedItems); + LOGGER.trace(" phase={} done: principal={}, operation={}, {}: deny because items {} are not allowed", + phase, getUsername(principal), prettyActionUrl(operationUrls), desc, unsatisfiedItems); } NoneFilter secFilter = NoneFilter.createNone(); traceFilter("secFilter", null, secFilter); @@ -1380,8 +1383,8 @@ private ObjectFilter preProcessObje } else if (securityFilterAllow == null) { // Nothing has been allowed. This means default deny. if (LOGGER.isTraceEnabled()) { - LOGGER.trace("AUTZ {} done: principal={}, operation={}, phase={}: default deny", - desc, getUsername(principal), prettyActionUrl(operationUrl), phase); + LOGGER.trace(" phase={} {} done: principal={}, operation={}, {}: default deny", + phase, getUsername(principal), prettyActionUrl(operationUrls), desc); } NoneFilter secFilter = NoneFilter.createNone(); traceFilter("secFilter", null, secFilter); @@ -1392,8 +1395,8 @@ private ObjectFilter preProcessObje if (securityFilterDeny == null) { if (LOGGER.isTraceEnabled()) { - LOGGER.trace("AUTZ {} done: principal={}, operation={}, phase={}: allow\n Filter:\n{}", - desc, getUsername(principal), prettyActionUrl(operationUrl), phase, + LOGGER.trace(" phase={} done: principal={}, operation={}, {}: allow\n Filter:\n{}", + phase, getUsername(principal), prettyActionUrl(operationUrls), desc, origWithAllowFilter==null?"null":origWithAllowFilter.debugDump(2)); } traceFilter("origWithAllowFilter", null, origWithAllowFilter); @@ -1401,14 +1404,27 @@ desc, getUsername(principal), prettyActionUrl(operationUrl), phase, } else { ObjectFilter secFilter = ObjectQueryUtil.filterAnd(origWithAllowFilter, NotFilter.createNot(securityFilterDeny)); if (LOGGER.isTraceEnabled()) { - LOGGER.trace("AUTZ {} done: principal={}, operation={}, phase={}: allow (with deny clauses)\n Filter:\n{}", desc, - getUsername(principal), prettyActionUrl(operationUrl), phase, + LOGGER.trace(" phase={} done: principal={}, operation={}, {}: allow (with deny clauses)\n Filter:\n{}", + phase, getUsername(principal), prettyActionUrl(operationUrls), desc, secFilter==null?"null":secFilter.debugDump(2)); } traceFilter("secFilter", null, secFilter); return secFilter; } } + + private boolean isApplicableForActions(Authorization autz, String[] requiredActions) { + List autzActions = autz.getAction(); + if (autzActions.contains(AuthorizationConstants.AUTZ_ALL_URL)) { + return true; + } + for (String requiredAction : requiredActions) { + if (autzActions.contains(requiredAction)) { + return true; + } + } + return false; + } private boolean isApplicableLimitations(Authorization autz, String limitAuthorizationAction) { if (limitAuthorizationAction == null) { @@ -1433,6 +1449,13 @@ private boolean isApplicableRelation(Authorization autz, QName requestRelation) return QNameUtil.contains(autzRelation, requestRelation); } + private void traceClassMatch(String message, Class specObjectClass, Class objectType) { + if (LOGGER.isTraceEnabled()) { + LOGGER.trace(" {}, authorization {}, query {}", + message, specObjectClass.getSimpleName(), objectType.getSimpleName()); + } + } + /** * Very rudimentary and experimental implementation. */ @@ -1535,6 +1558,21 @@ private String prettyActionUrl(String fullUrl) { return DebugUtil.shortenUrl(AuthorizationConstants.NS_SECURITY_PREFIX, fullUrl); } + private String prettyActionUrl(String[] fullUrls) { + if (fullUrls.length == 1) { + return DebugUtil.shortenUrl(AuthorizationConstants.NS_SECURITY_PREFIX, fullUrls[0]); + } else { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < fullUrls.length; i++) { + sb.append(DebugUtil.shortenUrl(AuthorizationConstants.NS_SECURITY_PREFIX, fullUrls[i])); + if (i < fullUrls.length - 1) { + sb.append(","); + } + } + return sb.toString(); + } + } + private String getObjectType(Class type) { if (type == null) { return null;