diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/page/admin/configuration/component/ObjectPolicyConfigurationTabPanel.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/page/admin/configuration/component/ObjectPolicyConfigurationTabPanel.java index 673b21f8d71..93d89c578d8 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/page/admin/configuration/component/ObjectPolicyConfigurationTabPanel.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/page/admin/configuration/component/ObjectPolicyConfigurationTabPanel.java @@ -290,7 +290,7 @@ public void populateItem(Item lifecycleState = rowModel.getObject().findContainerWrapper(new ItemPath(rowModel.getObject().getPath(), ObjectPolicyConfigurationType.F_LIFECYCLE_STATE_MODEL)); - if(lifecycleState.getValues().get(0).getContainerValue().getValue().getState()==null || + if (lifecycleState == null || lifecycleState.getValues().get(0).getContainerValue().getValue().getState()==null || lifecycleState.getValues().get(0).getContainerValue().getValue().getState().isEmpty()) { item.add(new Label(componentId, createStringResource("ObjectPolicyConfigurationTabPanel.lifecycleState.value.no"))); } else { diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/admin/configuration/dto/LoggingDto.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/admin/configuration/dto/LoggingDto.java index 0a40125ae1a..1afc8928f3d 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/admin/configuration/dto/LoggingDto.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/admin/configuration/dto/LoggingDto.java @@ -22,6 +22,7 @@ import java.util.List; import java.util.Map; +import com.evolveum.midpoint.prism.util.CloneUtil; import org.apache.commons.lang.StringUtils; import com.evolveum.midpoint.xml.ns._public.common.common_3.AuditingConfigurationType; @@ -135,7 +136,7 @@ public LoggingConfigurationType getNewObject() { configuration.setRootLoggerLevel(getRootLevel()); for (AppenderConfiguration item : getAppenders()) { - configuration.getAppender().add(item.getConfig()); + configuration.getAppender().add(CloneUtil.clone(item.getConfig())); } for (LoggerConfiguration item : getLoggers()) { 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/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 9bbb65b1010..58e5f65b884 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 @@ -463,6 +463,7 @@ public abstract class SchemaConstants { public static final QName C_EVENT = new QName(NS_C, "event"); public static final QName C_EVENT_HANDLER = new QName(NS_C, "eventHandler"); // TODO: no such element in common-3 - is it OK? public static final QName C_TEXT_FORMATTER = new QName(NS_C, "textFormatter"); + public static final QName C_NOTIFICATION_FUNCTIONS = new QName(NS_C, "notificationFunctions"); public static final QName C_TRANSPORT_NAME = new QName(NS_C, "transportName"); public static final QName C_FROM = new QName(NS_C, "from"); 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 fad6eb16c16..d9a3b6ee857 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 @@ -8471,8 +8471,22 @@ - - + + + + true + 4.0 + + + + + + + true + 4.0 + + + @@ -8660,6 +8674,7 @@ true + 4.0 @@ -9208,6 +9223,17 @@ + + + + Condition driving whether this expression is evaluated at all (for given inputs). + + + true + 3.9 + + + 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-common/src/main/java/com/evolveum/midpoint/model/common/expression/evaluator/AbstractValueTransformationExpressionEvaluator.java b/model/model-common/src/main/java/com/evolveum/midpoint/model/common/expression/evaluator/AbstractValueTransformationExpressionEvaluator.java index 47180eb62ab..478f8b1a660 100644 --- a/model/model-common/src/main/java/com/evolveum/midpoint/model/common/expression/evaluator/AbstractValueTransformationExpressionEvaluator.java +++ b/model/model-common/src/main/java/com/evolveum/midpoint/model/common/expression/evaluator/AbstractValueTransformationExpressionEvaluator.java @@ -15,35 +15,20 @@ */ package com.evolveum.midpoint.model.common.expression.evaluator; -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; +import java.util.*; import java.util.Map.Entry; import javax.xml.namespace.QName; import com.evolveum.midpoint.common.LocalizationService; -import com.evolveum.midpoint.prism.Item; -import com.evolveum.midpoint.prism.ItemDefinition; -import com.evolveum.midpoint.prism.PrismPropertyValue; -import com.evolveum.midpoint.prism.PrismValue; +import com.evolveum.midpoint.prism.*; import com.evolveum.midpoint.prism.delta.DeltaSetTriple; import com.evolveum.midpoint.prism.delta.ItemDelta; import com.evolveum.midpoint.prism.delta.PlusMinusZero; import com.evolveum.midpoint.prism.delta.PrismValueDeltaSetTriple; import com.evolveum.midpoint.prism.path.ItemPath; import com.evolveum.midpoint.prism.polystring.PolyString; -import com.evolveum.midpoint.repo.common.expression.ExpressionEvaluationContext; -import com.evolveum.midpoint.repo.common.expression.ExpressionEvaluator; -import com.evolveum.midpoint.repo.common.expression.ExpressionSyntaxException; -import com.evolveum.midpoint.repo.common.expression.ExpressionVariables; -import com.evolveum.midpoint.repo.common.expression.ItemDeltaItem; -import com.evolveum.midpoint.repo.common.expression.ObjectDeltaObject; -import com.evolveum.midpoint.repo.common.expression.Source; -import com.evolveum.midpoint.repo.common.expression.SourceTriple; +import com.evolveum.midpoint.repo.common.expression.*; import com.evolveum.midpoint.schema.constants.ExpressionConstants; import com.evolveum.midpoint.schema.result.OperationResult; import com.evolveum.midpoint.schema.util.ExceptionUtil; @@ -350,6 +335,14 @@ private PrismValueDeltaSetTriple evaluateRelativeExpression(final List outputTriple = new PrismValueDeltaSetTriple<>(); + Expression, PrismPropertyDefinition> conditionExpression; + if (expressionEvaluatorType.getCondition() != null) { + conditionExpression = ExpressionUtil.createCondition(expressionEvaluatorType.getCondition(), + evaluationContext.getExpressionFactory(), "condition in " + contextDescription, task, result); + } else { + conditionExpression = null; + } + Processor> processor = pvalues -> { if (includeNulls != null && !includeNulls && MiscUtil.isAllNull(pvalues)) { // The case that all the sources are null. There is no point executing the expression. @@ -426,8 +419,22 @@ private PrismValueDeltaSetTriple evaluateRelativeExpression(final List scriptResults; try { - scriptResults = transformSingleValue(scriptVariables, valueDestination, useNew, evaluationContext, - contextDescription, task, result); + boolean conditionResult; + if (conditionExpression != null) { + ExpressionEvaluationContext ctx = new ExpressionEvaluationContext(null, scriptVariables, + "condition in " + contextDescription, task, result); + PrismValueDeltaSetTriple> triple = conditionExpression.evaluate(ctx); + conditionResult = ExpressionUtil.computeConditionResult(triple.getNonNegativeValues()); + } else { + conditionResult = true; + } + if (conditionResult) { + scriptResults = transformSingleValue(scriptVariables, valueDestination, useNew, evaluationContext, + contextDescription, task, result); + } else { + LOGGER.trace("Skipping value transformation because condition evaluated to false in {}", contextDescription); + scriptResults = Collections.emptyList(); + } } catch (ExpressionEvaluationException e) { throw new TunnelException( localizationService.translate( diff --git a/model/model-common/src/main/java/com/evolveum/midpoint/model/common/mapping/MappingImpl.java b/model/model-common/src/main/java/com/evolveum/midpoint/model/common/mapping/MappingImpl.java index aa71cce90d3..70fef8cfca2 100644 --- a/model/model-common/src/main/java/com/evolveum/midpoint/model/common/mapping/MappingImpl.java +++ b/model/model-common/src/main/java/com/evolveum/midpoint/model/common/mapping/MappingImpl.java @@ -815,7 +815,7 @@ private XMLGregorianCalendar parseTimeSource(VariableBindingDefinitionType sourc throw new SchemaException("Empty source path in "+getMappingContextDescription()); } - Object sourceObject = ExpressionUtil.resolvePath(path, variables, sourceContext, objectResolver, "reference time definition in "+getMappingContextDescription(), task, result); + Object sourceObject = ExpressionUtil.resolvePath(path, variables, false, sourceContext, objectResolver, "reference time definition in "+getMappingContextDescription(), task, result); if (sourceObject == null) { return null; } @@ -871,7 +871,7 @@ private Source parseSo name = ItemPath.getName(path.last()); } ItemPath resolvePath = path; - Object sourceObject = ExpressionUtil.resolvePath(path, variables, sourceContext, objectResolver, "source definition in "+getMappingContextDescription(), task, result); + Object sourceObject = ExpressionUtil.resolvePath(path, variables, true, sourceContext, objectResolver, "source definition in "+getMappingContextDescription(), task, result); Item itemOld = null; ItemDelta delta = null; Item itemNew = null; diff --git a/model/model-common/src/test/java/com/evolveum/midpoint/model/common/expression/TestExpressionUtil.java b/model/model-common/src/test/java/com/evolveum/midpoint/model/common/expression/TestExpressionUtil.java index c27cdc5c5ac..53d32f7cee0 100644 --- a/model/model-common/src/test/java/com/evolveum/midpoint/model/common/expression/TestExpressionUtil.java +++ b/model/model-common/src/test/java/com/evolveum/midpoint/model/common/expression/TestExpressionUtil.java @@ -206,7 +206,7 @@ private T resolvePath(String path, ExpressionVariables variables, final Stri ItemPath itemPath = toItemPath(path); // WHEN - Object resolved = ExpressionUtil.resolvePath(itemPath, variables, null, null, TEST_NAME, null, result); + Object resolved = ExpressionUtil.resolvePath(itemPath, variables, false, null, null, TEST_NAME, null, result); // THEN System.out.println("Resolved:"); 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..13c3165a5a4 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 @@ -236,7 +236,7 @@ public ModelContext previewChanges( } OperationResult result = parentResult.createSubresult(PREVIEW_CHANGES); - LensContext context; + LensContext context = null; try { RepositoryCache.enter(); @@ -272,6 +272,8 @@ public ModelContext previewChanges( throw new SystemException(e); } finally { + LensUtil.reclaimSequences(context, cacheRepositoryService, task, result); + RepositoryCache.exit(); } @@ -399,12 +401,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 +491,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 +681,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 +691,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-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/Clockwork.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/Clockwork.java index 9df21fdcb78..bcf021fef48 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/Clockwork.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/Clockwork.java @@ -1056,7 +1056,7 @@ private void processClockworkException(LensContext con result.recordFatalError(e); auditEvent(context, AuditEventStage.EXECUTION, null, true, task, result); recordOperationExecution(context, e, task, result); - reclaimSequences(context, task, result); + LensUtil.reclaimSequences(context, repositoryService, task, result); } private void auditEvent(LensContext context, AuditEventStage stage, @@ -1608,21 +1608,4 @@ private Collection> d } return processedChangedAssignmentValues; } - - private void reclaimSequences(LensContext context, Task task, OperationResult result) throws SchemaException { - Map sequenceMap = context.getSequences(); - LOGGER.trace("Context sequence map: {}", sequenceMap); - for (Entry sequenceMapEntry: sequenceMap.entrySet()) { - Collection unusedValues = new ArrayList<>(1); - unusedValues.add(sequenceMapEntry.getValue()); - try { - LOGGER.trace("Returning value {} to sequence {}", sequenceMapEntry.getValue(), sequenceMapEntry.getKey()); - repositoryService.returnUnusedValuesToSequence(sequenceMapEntry.getKey(), unusedValues, result); - } catch (ObjectNotFoundException e) { - LOGGER.error("Cannot return unused value to sequence {}: it does not exist", sequenceMapEntry.getKey(), e); - // ... but otherwise ignore it and go on - } - } - } - } diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/LensUtil.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/LensUtil.java index a5fc6ab5ac1..74c84b3c17f 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/LensUtil.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/LensUtil.java @@ -15,13 +15,7 @@ */ package com.evolveum.midpoint.model.impl.lens; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; +import java.util.*; import java.util.function.Function; import java.util.function.Supplier; import java.util.stream.Collectors; @@ -41,6 +35,7 @@ import com.evolveum.midpoint.prism.query.RefFilter; import com.evolveum.midpoint.prism.query.builder.QueryBuilder; import com.evolveum.midpoint.prism.polystring.AlphanumericPolyStringNormalizer; +import com.evolveum.midpoint.repo.api.RepositoryService; import com.evolveum.midpoint.schema.SchemaConstantsGenerated; import com.evolveum.midpoint.schema.util.*; import com.evolveum.midpoint.util.DebugUtil; @@ -1086,4 +1081,24 @@ public static SingleLocalizableMessageType interpretLocalizableMessageTemplate(L } return rv; } + + public static void reclaimSequences(LensContext context, RepositoryService repositoryService, Task task, OperationResult result) throws SchemaException { + if (context == null) { + return; + } + + Map sequenceMap = context.getSequences(); + LOGGER.trace("Context sequence map: {}", sequenceMap); + for (Map.Entry sequenceMapEntry: sequenceMap.entrySet()) { + Collection unusedValues = new ArrayList<>(1); + unusedValues.add(sequenceMapEntry.getValue()); + try { + LOGGER.trace("Returning value {} to sequence {}", sequenceMapEntry.getValue(), sequenceMapEntry.getKey()); + repositoryService.returnUnusedValuesToSequence(sequenceMapEntry.getKey(), unusedValues, result); + } catch (ObjectNotFoundException e) { + LOGGER.error("Cannot return unused value to sequence {}: it does not exist", sequenceMapEntry.getKey(), e); + // ... but otherwise ignore it and go on + } + } + } } diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/VariablesUtil.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/VariablesUtil.java index 62c297fdc41..9993b18b6f2 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/VariablesUtil.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/VariablesUtil.java @@ -120,7 +120,7 @@ private static Object variableFromPathExpression(HashMap resulti throw new IllegalArgumentException("Path expression: expected ItemPathType but got " + expressionEvaluator.getValue()); } ItemPath itemPath = ((ItemPathType) expressionEvaluator.getValue()).getItemPath(); - return ExpressionUtil.resolvePath(itemPath, createVariables(resultingVariables), null, ctx.objectResolver, shortDesc, ctx.task, result); + return ExpressionUtil.resolvePath(itemPath, createVariables(resultingVariables), false, null, ctx.objectResolver, shortDesc, ctx.task, result); } private static ExpressionVariables createVariables(HashMap variableMap) { diff --git a/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/AbstractConfiguredModelIntegrationTest.java b/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/AbstractConfiguredModelIntegrationTest.java index 3ddd13b7dbd..13bb1d23e5a 100644 --- a/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/AbstractConfiguredModelIntegrationTest.java +++ b/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/AbstractConfiguredModelIntegrationTest.java @@ -102,6 +102,9 @@ public class AbstractConfiguredModelIntegrationTest extends AbstractModelIntegra protected static final String USER_TEMPLATE_ORG_ASSIGNMENT_FILENAME = COMMON_DIR + "/user-template-org-assignment.xml"; protected static final String USER_TEMPLATE_ORG_ASSIGNMENT_OID = "10000000-0000-0000-0000-000000000444"; + protected static final String USER_TEMPLATE_CARTHESIAN_FILENAME = COMMON_DIR + "/user-template-carthesian.xml"; + protected static final String USER_TEMPLATE_CARTHESIAN_OID = "8e47c2b2-dde6-44a9-a7c0-de21a14cb70d"; + protected static final File OBJECT_TEMPLATE_PERSONA_ADMIN_FILE = new File(COMMON_DIR, "object-template-persona-admin.xml"); protected static final String OBJECT_TEMPLATE_PERSONA_ADMIN_OID = "894ea1a8-2c0a-11e7-a950-ff2047b0c053"; diff --git a/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/AbstractInitializedModelIntegrationTest.java b/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/AbstractInitializedModelIntegrationTest.java index 821b8ea60a9..8508ba33fa2 100644 --- a/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/AbstractInitializedModelIntegrationTest.java +++ b/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/AbstractInitializedModelIntegrationTest.java @@ -216,6 +216,7 @@ public void initSystem(Task initTask, OperationResult initResult) throws Excepti repoAddObjectFromFile(USER_TEMPLATE_INBOUNDS_FILENAME, initResult); repoAddObjectFromFile(USER_TEMPLATE_COMPLEX_INCLUDE_FILENAME, initResult); repoAddObjectFromFile(USER_TEMPLATE_ORG_ASSIGNMENT_FILENAME, initResult); + repoAddObjectFromFile(USER_TEMPLATE_CARTHESIAN_FILENAME, initResult); // Shadows repoAddObjectFromFile(ACCOUNT_SHADOW_GUYBRUSH_DUMMY_FILE, initResult); diff --git a/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/mapping/TestMapping.java b/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/mapping/TestMapping.java index 558d1a8530b..63bbce02650 100644 --- a/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/mapping/TestMapping.java +++ b/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/mapping/TestMapping.java @@ -24,6 +24,11 @@ import javax.xml.datatype.XMLGregorianCalendar; +import com.evolveum.midpoint.prism.Containerable; +import com.evolveum.midpoint.prism.PrismContainerValue; +import com.evolveum.midpoint.prism.delta.builder.DeltaBuilder; +import com.evolveum.midpoint.schema.constants.SchemaConstants; +import com.evolveum.midpoint.xml.ns._public.common.common_3.*; import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.annotation.DirtiesContext.ClassMode; import org.springframework.test.context.ContextConfiguration; @@ -60,14 +65,6 @@ 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.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.FocusType; -import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType; -import com.evolveum.midpoint.xml.ns._public.common.common_3.ShadowType; -import com.evolveum.midpoint.xml.ns._public.common.common_3.UserType; /** * @author semancik @@ -133,8 +130,9 @@ public class TestMapping extends AbstractMappingTest { protected static final String DRINK_GRAPPA = "grappa"; protected static final String DRINK_GIN = "gin"; protected static final String DRINK_MEZCAL = "mezcal"; - + private static final String USER_JIM_NAME = "jim"; + private static final String USER_TYPE_CARTHESIAN = "carthesian"; @Override public void initSystem(Task initTask, OperationResult initResult) throws Exception { @@ -155,6 +153,8 @@ public void initSystem(Task initTask, OperationResult initResult) throws Excepti repoAddObjectFromFile(ROLE_COBALT_NEVERLAND_FILE, initResult); assumeAssignmentPolicy(AssignmentPolicyEnforcementType.FULL); + + setDefaultObjectTemplate(UserType.COMPLEX_TYPE, USER_TYPE_CARTHESIAN, USER_TEMPLATE_CARTHESIAN_OID, initResult); } /** @@ -3441,6 +3441,97 @@ public void test427UnassignAccountFromJack() throws Exception { assertLinks(userAfter, 0); } + /** + * MID-4862 + */ + @Test + public void test500AssignmentsCombinationSingle() throws Exception { + final String TEST_NAME = "test500AssignmentsCombinationSingle"; + displayTestTitle(TEST_NAME); + + // GIVEN + Task task = createTask(TEST_NAME); + OperationResult result = task.getResult(); + + UserType jim = prismContext.createKnownObjectable(UserType.class) + .name(USER_JIM_NAME) + .subtype(USER_TYPE_CARTHESIAN) + .beginAssignment() + .targetRef(ROLE_SUPERUSER_OID, RoleType.COMPLEX_TYPE) + .end(); + + // WHEN + displayWhen(TEST_NAME); + addObject(jim.asPrismObject()); + + // THEN + displayThen(TEST_NAME); + + PrismObject userAfter = getUser(jim.getOid()); + display("User after", userAfter); + assertAssignments(userAfter, 1); + } + + /** + * MID-4862 + */ + @Test + public void test510AssignmentsCombinationCouple() throws Exception { + final String TEST_NAME = "test500AssignmentsCombinationCouple"; + displayTestTitle(TEST_NAME); + + // GIVEN + Task task = createTask(TEST_NAME); + OperationResult result = task.getResult(); + + PrismObject jim = findUserByUsername(USER_JIM_NAME); + + // WHEN + displayWhen(TEST_NAME); + assignOrg(jim.getOid(), ORG_SAVE_ELAINE_OID, task, result); + + // THEN + displayThen(TEST_NAME); + assertSuccess(result); + + PrismObject userAfter = getUser(jim.getOid()); + display("User after", userAfter); + assertAssignments(userAfter, 3); + } + + /** + * MID-4863 + */ + @Test + public void test520DeleteUserAssignment() throws Exception { + final String TEST_NAME = "test520DeleteUserAssignment"; + displayTestTitle(TEST_NAME); + + // GIVEN + Task task = createTask(TEST_NAME); + OperationResult result = task.getResult(); + + PrismObject jim = findUserByUsername(USER_JIM_NAME); + + // WHEN + displayWhen(TEST_NAME); + AssignmentType orgAssignment = findAssignment(jim, ORG_SAVE_ELAINE_OID, SchemaConstants.ORG_DEFAULT); + assertNotNull("org assignment not found", orgAssignment); + PrismContainerValue orgAssignmentPcv = new PrismContainerValue<>(prismContext); + orgAssignmentPcv.setId(orgAssignment.getId()); + ObjectDelta delta = DeltaBuilder.deltaFor(UserType.class, prismContext) + .item(UserType.F_ASSIGNMENT).delete(orgAssignmentPcv) + .asObjectDeltaCast(jim.getOid()); + executeChanges(delta, null, task, result); + + // THEN + displayThen(TEST_NAME); + assertSuccess(result); + + PrismObject userAfter = getUser(jim.getOid()); + display("User after", userAfter); + assertAssignments(userAfter, 1); + } private String rumFrom(String locality) { return "rum from " + locality; 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/common/user-template-carthesian.xml b/model/model-intest/src/test/resources/common/user-template-carthesian.xml new file mode 100644 index 00000000000..efdc9e774fc --- /dev/null +++ b/model/model-intest/src/test/resources/common/user-template-carthesian.xml @@ -0,0 +1,105 @@ + + + + User template for cartesian assignments + + strong + + x + assignment + + + + + + + + y + assignment + + + + + + + + + + + + OrgType + + + name + + + + + + + combined + + true + + + + name + + + + + + + + + + assignment + + + + + + + + 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/model/notifications-api/src/main/java/com/evolveum/midpoint/notifications/api/NotificationFunctions.java b/model/notifications-api/src/main/java/com/evolveum/midpoint/notifications/api/NotificationFunctions.java index ccbe11dc460..a2e3c34c37d 100644 --- a/model/notifications-api/src/main/java/com/evolveum/midpoint/notifications/api/NotificationFunctions.java +++ b/model/notifications-api/src/main/java/com/evolveum/midpoint/notifications/api/NotificationFunctions.java @@ -17,6 +17,7 @@ package com.evolveum.midpoint.notifications.api; import com.evolveum.midpoint.notifications.api.events.Event; +import com.evolveum.midpoint.notifications.api.events.ModelEvent; import com.evolveum.midpoint.prism.PrismObject; import com.evolveum.midpoint.prism.delta.ObjectDelta; import com.evolveum.midpoint.prism.path.ItemPath; @@ -40,4 +41,10 @@ public interface NotificationFunctions { List getSynchronizationPaths(); List getAuxiliaryPaths(); + + // TODO: polish this method + // TODO indicate somehow if password was erased from the focus + // We should (probably) return only a value if it has been (successfully) written to the focus. + String getFocusPasswordFromEvent(ModelEvent modelEvent); + } diff --git a/model/notifications-api/src/main/java/com/evolveum/midpoint/notifications/api/events/Event.java b/model/notifications-api/src/main/java/com/evolveum/midpoint/notifications/api/events/Event.java index 83a72fd355d..0a5c502577f 100644 --- a/model/notifications-api/src/main/java/com/evolveum/midpoint/notifications/api/events/Event.java +++ b/model/notifications-api/src/main/java/com/evolveum/midpoint/notifications/api/events/Event.java @@ -113,4 +113,15 @@ public interface Event extends DebugDumpable, ShortDumpable { * A better is to define handlers in system configuration. */ EventHandlerType getAdHocHandler(); + + /** + * Returns plaintext focus password value, if known. + * Beware: might not always work correctly: + * 1. If the change execution was only partially successful, the value returned might or might not be stored in the repo + * 2. If the password was changed to null, the 'null' value is returned. So the caller cannot distinguish it from "no change" + * situation. A new method for this would be needed. + */ + default String getFocusPassword() { + return null; + } } diff --git a/model/notifications-api/src/main/java/com/evolveum/midpoint/notifications/api/events/ModelEvent.java b/model/notifications-api/src/main/java/com/evolveum/midpoint/notifications/api/events/ModelEvent.java index 566e29de140..4f6cdbefd03 100644 --- a/model/notifications-api/src/main/java/com/evolveum/midpoint/notifications/api/events/ModelEvent.java +++ b/model/notifications-api/src/main/java/com/evolveum/midpoint/notifications/api/events/ModelEvent.java @@ -221,6 +221,10 @@ public String getContentAsFormattedList(boolean showSynchronizationItems, boolea return getNotificationFunctions().getContentAsFormattedList(this, showSynchronizationItems, showAuxiliaryAttributes); } + public String getFocusPassword() { + return getNotificationFunctions().getFocusPasswordFromEvent(this); + } + @Override public String debugDump(int indent) { StringBuilder sb = DebugUtil.createTitleStringBuilderLn(this.getClass(), indent); diff --git a/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/NotificationFunctionsImpl.java b/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/NotificationFunctionsImpl.java index ccf3fd1a1b9..2a51200e722 100644 --- a/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/NotificationFunctionsImpl.java +++ b/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/NotificationFunctionsImpl.java @@ -40,6 +40,7 @@ import com.evolveum.midpoint.schema.result.OperationResult; import com.evolveum.midpoint.schema.util.ObjectTypeUtil; import com.evolveum.midpoint.schema.util.ShadowUtil; +import com.evolveum.midpoint.util.DebugUtil; import com.evolveum.midpoint.util.exception.CommonException; import com.evolveum.midpoint.util.exception.ObjectNotFoundException; import com.evolveum.midpoint.util.exception.SchemaException; @@ -57,6 +58,8 @@ import java.util.*; +import static java.util.Collections.singletonList; + /** * @author mederly */ @@ -377,4 +380,54 @@ private String getResourceObjectModifiedAttributesAsFormattedList(ResourceObject return rv.toString(); } + // TODO: polish this method + // We should (probably) return only a value if it has been (successfully) written to the focus. + @Override + public String getFocusPasswordFromEvent(ModelEvent modelEvent) { + if (modelEvent.getFocusDeltas().isEmpty()) { + LOGGER.trace("getFocusPasswordFromEvent: No user deltas in event"); + return null; + } + String password = getPasswordFromDeltas(modelEvent.getFocusDeltas()); + if (password != null) { + LOGGER.trace("getFocusPasswordFromEvent: Found password in user executed delta(s)"); + return password; + } + // in executed deltas + + //noinspection unchecked + ObjectDelta focusPrimaryDelta = (ObjectDelta) modelEvent.getFocusPrimaryDelta(); + //noinspection unchecked + ObjectDelta focusSecondaryDelta = (ObjectDelta) modelEvent.getFocusSecondaryDelta(); + if (focusPrimaryDelta == null && focusSecondaryDelta == null) { + LOGGER.trace("getFocusPasswordFromEvent: No password in executed delta(s) and no primary/secondary deltas"); + return null; + } + if (focusPrimaryDelta != null) { + password = getPasswordFromDeltas(singletonList(focusPrimaryDelta)); + if (password != null) { + LOGGER.trace("getFocusPasswordFromEvent: Found password in user primary delta, continuing"); + return password; + } + } + if (focusSecondaryDelta != null) { + password = getPasswordFromDeltas(singletonList(focusSecondaryDelta)); + if (password != null) { + LOGGER.trace("getFocusPasswordFromEvent: Found password in user secondary delta(s)"); + return password; + } + } + LOGGER.trace("getFocusPasswordFromEvent: No password in executed delta(s) nor in primary/secondary deltas"); + return null; + } + + private String getPasswordFromDeltas(List> deltas) { + try { + //noinspection unchecked + return midpointFunctions.getPlaintextUserPasswordFromDeltas((List) deltas); + } catch (EncryptionException e) { + LoggingUtils.logUnexpectedException(LOGGER, "Couldn't decrypt password from user deltas: {}", e, DebugUtil.debugDump(deltas)); + return null; + } + } } diff --git a/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/handlers/BaseHandler.java b/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/handlers/BaseHandler.java index 30ecac73e3e..bd8dc90e7fb 100644 --- a/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/handlers/BaseHandler.java +++ b/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/handlers/BaseHandler.java @@ -16,6 +16,7 @@ package com.evolveum.midpoint.notifications.impl.handlers; +import com.evolveum.midpoint.notifications.impl.formatters.TextFormatter; import com.evolveum.midpoint.repo.common.expression.Expression; import com.evolveum.midpoint.repo.common.expression.ExpressionEvaluationContext; import com.evolveum.midpoint.repo.common.expression.ExpressionFactory; @@ -64,19 +65,13 @@ public abstract class BaseHandler implements EventHandler { private static final Trace LOGGER = TraceManager.getTrace(BaseHandler.class); - @Autowired - protected NotificationManagerImpl notificationManager; + @Autowired protected NotificationManagerImpl notificationManager; + @Autowired protected NotificationFunctionsImpl notificationsUtil; + @Autowired protected PrismContext prismContext; + @Autowired protected ExpressionFactory expressionFactory; + @Autowired protected TextFormatter textFormatter; - @Autowired - protected NotificationFunctionsImpl notificationsUtil; - - @Autowired - protected PrismContext prismContext; - - @Autowired - protected ExpressionFactory expressionFactory; - - protected void register(Class clazz) { + protected void register(Class clazz) { notificationManager.registerEventHandler(clazz, this); } @@ -199,10 +194,9 @@ protected ExpressionVariables getDefaultVariables(Event event, OperationResult r ExpressionVariables expressionVariables = new ExpressionVariables(); Map variables = new HashMap<>(); event.createExpressionVariables(variables, result); + variables.put(SchemaConstants.C_TEXT_FORMATTER, textFormatter); + variables.put(SchemaConstants.C_NOTIFICATION_FUNCTIONS, notificationsUtil); expressionVariables.addVariableDefinitions(variables); return expressionVariables; } - - - } diff --git a/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/notifiers/CustomNotifier.java b/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/notifiers/CustomNotifier.java index b46f6000fb8..97bac7709dd 100644 --- a/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/notifiers/CustomNotifier.java +++ b/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/notifiers/CustomNotifier.java @@ -178,12 +178,4 @@ private List evaluateExpression(ExpressionType expressi .evaluateExpressionInContext(expression, params, task, result); return exprResult.getZeroSet().stream().map(PrismPropertyValue::getValue).collect(Collectors.toList()); } - - @Override - protected ExpressionVariables getDefaultVariables(Event event, OperationResult result) { - ExpressionVariables variables = super.getDefaultVariables(event, result); - variables.addVariableDefinition(SchemaConstants.C_TEXT_FORMATTER, textFormatter); - return variables; - } - } diff --git a/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/notifiers/GeneralNotifier.java b/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/notifiers/GeneralNotifier.java index e7047f2daf9..c61c4766ff1 100644 --- a/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/notifiers/GeneralNotifier.java +++ b/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/notifiers/GeneralNotifier.java @@ -385,13 +385,6 @@ private String formatPath(ItemDelta itemDelta) { return sb.toString(); } - @Override - protected ExpressionVariables getDefaultVariables(Event event, OperationResult result) { - ExpressionVariables variables = super.getDefaultVariables(event, result); - variables.addVariableDefinition(SchemaConstants.C_TEXT_FORMATTER, textFormatter); - return variables; - } - public String formatRequester(Event event, OperationResult result) { SimpleObjectRef requesterRef = event.getRequester(); if (requesterRef == null) { diff --git a/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/notifiers/UserPasswordNotifier.java b/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/notifiers/UserPasswordNotifier.java index cd88e326ac3..210fd4dadf7 100644 --- a/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/notifiers/UserPasswordNotifier.java +++ b/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/notifiers/UserPasswordNotifier.java @@ -16,32 +16,21 @@ package com.evolveum.midpoint.notifications.impl.notifiers; -import com.evolveum.midpoint.model.api.expr.MidpointFunctions; import com.evolveum.midpoint.notifications.api.events.Event; import com.evolveum.midpoint.notifications.api.events.ModelEvent; import com.evolveum.midpoint.notifications.impl.NotificationFunctionsImpl; -import com.evolveum.midpoint.prism.crypto.EncryptionException; -import com.evolveum.midpoint.prism.delta.ObjectDelta; import com.evolveum.midpoint.schema.result.OperationResult; import com.evolveum.midpoint.task.api.Task; -import com.evolveum.midpoint.util.DebugUtil; -import com.evolveum.midpoint.util.logging.LoggingUtils; 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.GeneralNotifierType; import com.evolveum.midpoint.xml.ns._public.common.common_3.UserPasswordNotifierType; import com.evolveum.midpoint.xml.ns._public.common.common_3.UserType; - import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import javax.annotation.PostConstruct; -import java.util.List; - -import static java.util.Collections.singletonList; - /** * @author mederly */ @@ -50,9 +39,6 @@ public class UserPasswordNotifier extends GeneralNotifier { private static final Trace LOGGER = TraceManager.getTrace(UserPasswordNotifier.class); - @Autowired - private MidpointFunctions midpointFunctions; - @Autowired private NotificationFunctionsImpl notificationsUtil; @@ -71,60 +57,13 @@ protected boolean quickCheckApplicability(Event event, GeneralNotifierType gener } } - @Override protected boolean checkApplicability(Event event, GeneralNotifierType generalNotifierType, OperationResult result) { if (!event.isAlsoSuccess()) { // TODO LOGGER.trace("Operation was not successful, exiting."); return false; } - return getPasswordFromEvent((ModelEvent) event) != null; // logging is done in the called method - } - - private String getPasswordFromEvent(ModelEvent modelEvent) { - if (modelEvent.getFocusDeltas().isEmpty()) { - LOGGER.trace("No user deltas in event, exiting."); - return null; - } - String password = getPasswordFromDeltas(modelEvent.getFocusDeltas()); - if (password != null) { - LOGGER.trace("Found password in user executed delta(s), continuing."); - return password; - } - //noinspection unchecked - ObjectDelta focusPrimaryDelta = (ObjectDelta) modelEvent.getFocusPrimaryDelta(); - //noinspection unchecked - ObjectDelta focusSecondaryDelta = (ObjectDelta) modelEvent.getFocusSecondaryDelta(); - if (focusPrimaryDelta == null && focusSecondaryDelta == null) { - LOGGER.trace("No password in executed delta(s) and no primary/secondary deltas, exiting."); - return null; - } - if (focusPrimaryDelta != null) { - password = getPasswordFromDeltas(singletonList(focusPrimaryDelta)); - if (password != null) { - LOGGER.trace("Found password in user primary delta, continuing."); - return password; - } - } - if (focusSecondaryDelta != null) { - password = getPasswordFromDeltas(singletonList(focusSecondaryDelta)); - if (password != null) { - LOGGER.trace("Found password in user secondary delta(s), continuing."); - return password; - } - } - LOGGER.trace("No password in executed delta(s) nor in primary/secondary deltas, exiting."); - return null; - } - - private String getPasswordFromDeltas(List> deltas) { - try { - //noinspection unchecked - return midpointFunctions.getPlaintextUserPasswordFromDeltas((List) deltas); - } catch (EncryptionException e) { - LoggingUtils.logException(LOGGER, "Couldn't decrypt password from user deltas: {}", e, DebugUtil.debugDump(deltas)); - return null; - } + return event.getFocusPassword() != null; // logging is done in the called method } @Override @@ -135,7 +74,7 @@ protected String getSubject(Event event, GeneralNotifierType generalNotifierType @Override protected String getBody(Event event, GeneralNotifierType generalNotifierType, String transport, Task task, OperationResult result) { return "Password for user " + notificationsUtil.getObjectType(event.getRequestee(), false, result).getName() - + " is: " + getPasswordFromEvent((ModelEvent) event); + + " is: " + event.getFocusPassword(); } @Override diff --git a/repo/repo-cache/src/main/java/com/evolveum/midpoint/repo/cache/RepositoryCache.java b/repo/repo-cache/src/main/java/com/evolveum/midpoint/repo/cache/RepositoryCache.java index 7d5eb56b187..841f43aaa8f 100644 --- a/repo/repo-cache/src/main/java/com/evolveum/midpoint/repo/cache/RepositoryCache.java +++ b/repo/repo-cache/src/main/java/com/evolveum/midpoint/repo/cache/RepositoryCache.java @@ -500,7 +500,8 @@ private boolean nullOrHarmlessOptions(Collectioncommons-lang commons-lang + + org.apache.commons + commons-collections4 + org.springframework spring-context diff --git a/repo/repo-common/src/main/java/com/evolveum/midpoint/repo/common/expression/Expression.java b/repo/repo-common/src/main/java/com/evolveum/midpoint/repo/common/expression/Expression.java index 06fc7d8de94..c1b7a0eaddf 100644 --- a/repo/repo-common/src/main/java/com/evolveum/midpoint/repo/common/expression/Expression.java +++ b/repo/repo-common/src/main/java/com/evolveum/midpoint/repo/common/expression/Expression.java @@ -347,7 +347,7 @@ private ExpressionVariables processInnerVariables(ExpressionVariables variables, } } else if (variableDefType.getPath() != null) { ItemPath itemPath = variableDefType.getPath().getItemPath(); - Object resolvedValue = ExpressionUtil.resolvePath(itemPath, variables, null, objectResolver, contextDescription, task, result); + Object resolvedValue = ExpressionUtil.resolvePath(itemPath, variables, false, null, objectResolver, contextDescription, task, result); newVariables.addVariableDefinition(varName, resolvedValue); } else { throw new SchemaException("No value for variable "+varName+" in "+contextDescription); diff --git a/repo/repo-common/src/main/java/com/evolveum/midpoint/repo/common/expression/ExpressionUtil.java b/repo/repo-common/src/main/java/com/evolveum/midpoint/repo/common/expression/ExpressionUtil.java index c651c74ec0d..b6f69789b0f 100644 --- a/repo/repo-common/src/main/java/com/evolveum/midpoint/repo/common/expression/ExpressionUtil.java +++ b/repo/repo-common/src/main/java/com/evolveum/midpoint/repo/common/expression/ExpressionUtil.java @@ -170,8 +170,15 @@ public static O convertValue(Class finalExpectedJavaType, Function) { + return ((ObjectDeltaObject) root).normalizeValuesToDelete(true); + } else if (root instanceof ItemDeltaItem) { + // TODO normalize as well + return root; + } else { + return root; + } + } + public static Collection computeTargetValues(VariableBindingDefinitionType target, Object defaultTargetContext, ExpressionVariables variables, ObjectResolver objectResolver, String contextDesc, Task task, OperationResult result) throws SchemaException, ObjectNotFoundException, CommunicationException, ConfigurationException, SecurityViolationException, ExpressionEvaluationException { @@ -238,7 +260,7 @@ public static Collection computeT } ItemPath path = itemPathType.getItemPath(); - Object object = resolvePath(path, variables, defaultTargetContext, objectResolver, contextDesc, task, result); + Object object = resolvePath(path, variables, false, defaultTargetContext, objectResolver, contextDesc, task, result); if (object == null) { return new ArrayList<>(); } else if (object instanceof Item) { diff --git a/repo/repo-common/src/main/java/com/evolveum/midpoint/repo/common/expression/ObjectDeltaObject.java b/repo/repo-common/src/main/java/com/evolveum/midpoint/repo/common/expression/ObjectDeltaObject.java index 762853c515a..03a941091a6 100644 --- a/repo/repo-common/src/main/java/com/evolveum/midpoint/repo/common/expression/ObjectDeltaObject.java +++ b/repo/repo-common/src/main/java/com/evolveum/midpoint/repo/common/expression/ObjectDeltaObject.java @@ -17,14 +17,9 @@ import java.util.ArrayList; import java.util.Collection; +import java.util.List; -import com.evolveum.midpoint.prism.Item; -import com.evolveum.midpoint.prism.ItemDefinition; -import com.evolveum.midpoint.prism.PartiallyResolvedItem; -import com.evolveum.midpoint.prism.PrismContainerValue; -import com.evolveum.midpoint.prism.PrismObject; -import com.evolveum.midpoint.prism.PrismObjectDefinition; -import com.evolveum.midpoint.prism.PrismValue; +import com.evolveum.midpoint.prism.*; import com.evolveum.midpoint.prism.delta.ChangeType; import com.evolveum.midpoint.prism.delta.ItemDelta; import com.evolveum.midpoint.prism.delta.ObjectDelta; @@ -36,6 +31,8 @@ import com.evolveum.midpoint.util.exception.SchemaException; import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType; +import static org.apache.commons.collections4.CollectionUtils.emptyIfNull; + /** * A DTO class defining old object state (before change), delta (change) and new object state (after change). * @@ -306,4 +303,54 @@ public ObjectDeltaObject clone() { return clone; } + public ObjectDeltaObject normalizeValuesToDelete(boolean doClone) { + if (delta == null || delta.getChangeType() != ChangeType.MODIFY) { + return this; + } + boolean foundIdOnlyDeletion = false; + main: for (ItemDelta itemDelta : delta.getModifications()) { + for (PrismValue valueToDelete : emptyIfNull(itemDelta.getValuesToDelete())) { + if (valueToDelete instanceof PrismContainerValue && ((PrismContainerValue) valueToDelete).isIdOnly()) { + foundIdOnlyDeletion = true; + break main; + } + } + } + if (!foundIdOnlyDeletion) { + return this; + } + ObjectDeltaObject object = doClone ? this.clone() : this; + + boolean anyRealChange = false; + for (ItemDelta itemDelta : object.delta.getModifications()) { + if (itemDelta.getValuesToDelete() == null) { + continue; + } + boolean itemDeltaChanged = false; + List newValuesToDelete = new ArrayList<>(); + for (PrismValue valueToDelete : itemDelta.getValuesToDelete()) { + if (valueToDelete instanceof PrismContainerValue && ((PrismContainerValue) valueToDelete).isIdOnly() + && object.oldObject != null /* should always be */) { + Object oldItem = object.oldObject.find(itemDelta.getPath()); + if (oldItem instanceof PrismContainer) { + PrismContainerValue oldValue = ((PrismContainer) oldItem) + .getValue(((PrismContainerValue) valueToDelete).getId()); + if (oldValue != null) { + newValuesToDelete.add(oldValue.clone()); + itemDeltaChanged = true; + continue; + } + } + } + newValuesToDelete.add(valueToDelete); + } + if (itemDeltaChanged) { + itemDelta.resetValuesToDelete(); + //noinspection unchecked + ((ItemDelta) itemDelta).addValuesToDelete(newValuesToDelete); + anyRealChange = true; + } + } + return anyRealChange ? object : this; + } } 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; diff --git a/tools/ninja/pom.xml b/tools/ninja/pom.xml index 32f770cdb7f..1548bf4f3a0 100644 --- a/tools/ninja/pom.xml +++ b/tools/ninja/pom.xml @@ -251,6 +251,7 @@ repackage + false ZIP