diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/api/model/ReadOnlyValueModel.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/api/model/ReadOnlyValueModel.java new file mode 100644 index 00000000000..eaa5beaa4a2 --- /dev/null +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/api/model/ReadOnlyValueModel.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2010-2018 Evolveum + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.evolveum.midpoint.gui.api.model; + +import org.apache.wicket.model.AbstractReadOnlyModel; +import org.jetbrains.annotations.NotNull; + +/** + * EXPERIMENTAL + * TODO better name + * + * @author mederly + */ +public class ReadOnlyValueModel extends AbstractReadOnlyModel { + + @NotNull private final T object; + + public ReadOnlyValueModel(@NotNull T object) { + this.object = object; + } + + @Override + public T getObject() { + return object; + } +} diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/api/util/WebComponentUtil.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/api/util/WebComponentUtil.java index 7566cb1f46a..f0d7f91b310 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/api/util/WebComponentUtil.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/api/util/WebComponentUtil.java @@ -46,7 +46,7 @@ import javax.xml.namespace.QName; import com.evolveum.midpoint.gui.api.SubscriptionType; -import com.evolveum.midpoint.gui.api.model.ReadOnlyModel; +import com.evolveum.midpoint.gui.api.model.ReadOnlyValueModel; import com.evolveum.midpoint.model.api.ModelExecuteOptions; import com.evolveum.midpoint.schema.GetOperationOptions; import com.evolveum.midpoint.schema.SelectorOptions; @@ -701,9 +701,10 @@ public List getObject() { }; } + // use for small enums only @NotNull - public static IModel> createReadonlyModelFromEnum(@NotNull Class type, @NotNull Predicate filter) { - return new ReadOnlyModel<>(() -> + public static IModel> createReadonlyValueModelFromEnum(@NotNull Class type, @NotNull Predicate filter) { + return new ReadOnlyValueModel<>( Arrays.stream(type.getEnumConstants()) .filter(filter) .collect(Collectors.toList())); diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/assignment/AbstractRoleAssignmentPanel.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/assignment/AbstractRoleAssignmentPanel.java index cb9dc8aadd2..847e99ad3c3 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/assignment/AbstractRoleAssignmentPanel.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/assignment/AbstractRoleAssignmentPanel.java @@ -23,10 +23,7 @@ import javax.xml.namespace.QName; import com.evolveum.midpoint.prism.PrismContainerValue; -import com.evolveum.midpoint.prism.query.builder.S_AtomicFilterExit; -import com.evolveum.midpoint.prism.query.builder.S_FilterEntryOrEmpty; import com.evolveum.midpoint.schema.constants.SchemaConstants; -import com.evolveum.midpoint.util.exception.SchemaException; import com.evolveum.midpoint.web.component.prism.*; import com.evolveum.midpoint.web.page.admin.PageAdminFocus; import com.evolveum.midpoint.web.page.admin.users.component.AssignmentInfoDto; @@ -179,8 +176,8 @@ protected void addSelectedAssignmentsPerformed(AjaxReques createNewAssignmentContainerValueWrapper(newAssignment); } - refreshTable(target); - + refreshTable(target); + reloadSavePreviewButtons(target); } protected List, String>> initColumns() { diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/assignment/AssignmentPanel.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/assignment/AssignmentPanel.java index 563d9ad8449..58fadf2c085 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/assignment/AssignmentPanel.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/assignment/AssignmentPanel.java @@ -23,6 +23,7 @@ import com.evolveum.midpoint.gui.api.util.WebModelServiceUtils; import com.evolveum.midpoint.prism.PrismContainerValue; import com.evolveum.midpoint.prism.path.ItemPath; +import com.evolveum.midpoint.web.component.objectdetails.FocusMainPanel; import com.evolveum.midpoint.web.component.prism.*; import com.evolveum.midpoint.xml.ns._public.common.common_3.ActivationStatusType; import org.apache.commons.lang.StringUtils; @@ -30,7 +31,6 @@ import org.apache.wicket.ajax.AjaxRequestTarget; import org.apache.wicket.extensions.markup.html.repeater.data.grid.ICellPopulator; import org.apache.wicket.extensions.markup.html.repeater.data.table.AbstractColumn; -import org.apache.wicket.extensions.markup.html.repeater.data.table.DataTable; import org.apache.wicket.extensions.markup.html.repeater.data.table.IColumn; import org.apache.wicket.markup.html.WebMarkupContainer; import org.apache.wicket.markup.html.basic.Label; @@ -440,6 +440,7 @@ protected void deleteAssignmentPerformed(AjaxRequestTarget target, List createNewAssignmentContainerValueWrapper(PrismContainerValue newAssignment) { @@ -511,4 +512,11 @@ private IModel getActivationLabelModel(ContainerValueWrapper focusWrapper = getObjectModel().getObject(); + ContainerWrapper assignmentsWrapper = + focusWrapper.findContainerWrapper(new ItemPath(FocusType.F_ASSIGNMENT)); + if (assignmentsWrapper != null) { + for (ContainerValueWrapper assignmentWrapper : assignmentsWrapper.getValues()) { + if (ValueStatus.DELETED.equals(assignmentWrapper.getStatus()) || + ValueStatus.ADDED.equals(assignmentWrapper.getStatus())) { + return true; + } + } + } return false; } diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/admin/reports/component/ReportConfigurationPanel.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/admin/reports/component/ReportConfigurationPanel.java index 0224d7ccfe3..d15d133b291 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/admin/reports/component/ReportConfigurationPanel.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/admin/reports/component/ReportConfigurationPanel.java @@ -69,7 +69,7 @@ protected void initLayout() { createStringResource("ObjectType.description"), ID_LABEL_SIZE, ID_INPUT_SIZE, false); add(description); - IModel choices = WebComponentUtil.createReadonlyModelFromEnum(ExportType.class, e -> e != ExportType.JXL); + IModel choices = WebComponentUtil.createReadonlyValueModelFromEnum(ExportType.class, e -> e != ExportType.JXL); IChoiceRenderer renderer = new EnumChoiceRenderer(); DropDownFormGroup exportType = new DropDownFormGroup(ID_EXPORT_TYPE, new PropertyModel(getModel(), ReportDto.F_EXPORT_TYPE), choices, renderer, createStringResource("ReportType.export"), ID_LABEL_SIZE, ID_INPUT_SIZE, true); diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/admin/server/PageTaskEdit.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/admin/server/PageTaskEdit.java index f7d4f5b5288..91575e89d6d 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/admin/server/PageTaskEdit.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/admin/server/PageTaskEdit.java @@ -114,8 +114,12 @@ protected TaskDto load() { final Task operationTask = getTaskManager().createTaskInstance(OPERATION_LOAD_TASK); final TaskType taskType = loadTaskTypeChecked(taskOid, operationTask, result); currentTaskDto = prepareTaskDto(taskType, operationTask, result); + result.computeStatusIfUnknown(); + if (!result.isSuccess()) { + showResult(result); + } return currentTaskDto; - } catch (SchemaException|ObjectNotFoundException|ExpressionEvaluationException e) { + } catch (SchemaException e) { throw new SystemException("Couldn't prepare task DTO: " + e.getMessage(), e); } } @@ -142,7 +146,6 @@ protected List load() { } } }; - edit = false; initLayout(); } @@ -162,11 +165,6 @@ protected IModel createPageTitleModel() { private TaskType loadTaskTypeChecked(String taskOid, Task operationTask, OperationResult result) { TaskType taskType = loadTaskType(taskOid, operationTask, result); - - if (!result.isSuccess()) { - showResult(result); - } - if (taskType == null) { getSession().error(getString("pageTaskEdit.message.cantTaskDetails")); showResult(result, false); @@ -182,7 +180,6 @@ public IModel> getNodeListModel() { private TaskType loadTaskType(String taskOid, Task operationTask, OperationResult result) { TaskType taskType = null; - try { Collection> options = GetOperationOptions.retrieveItemsNamed( TaskType.F_SUBTASK, @@ -194,17 +191,15 @@ private TaskType loadTaskType(String taskOid, Task operationTask, OperationResul new ItemPath(TaskType.F_WORKFLOW_CONTEXT, WfContextType.F_REQUESTER_REF) )); taskType = getModelService().getObject(TaskType.class, taskOid, options, operationTask, result).asObjectable(); - result.computeStatus(); } catch (Exception ex) { result.recordFatalError("Couldn't get task.", ex); } return taskType; } - private TaskDto prepareTaskDto(TaskType task, Task operationTask, OperationResult result) throws SchemaException, ObjectNotFoundException, ExpressionEvaluationException { - TaskDto taskDto = new TaskDto(task, null, getModelService(), getTaskService(), getModelInteractionService(), + private TaskDto prepareTaskDto(TaskType task, Task operationTask, OperationResult result) throws SchemaException { + return new TaskDto(task, null, getModelService(), getTaskService(), getModelInteractionService(), getTaskManager(), getWorkflowManager(), TaskDtoProviderOptions.fullOptions(), operationTask, result, this); - return taskDto; } @@ -301,7 +296,7 @@ public Component getRefreshingBehaviorParent() { return getRefreshPanel(); } - public void refreshTaskModels() { + private void refreshTaskModels() { TaskDto oldTaskDto = taskDtoModel.getObject(); if (oldTaskDto == null) { LOGGER.warn("Null or empty taskModel"); @@ -320,8 +315,13 @@ public void refreshTaskModels() { currentTaskDto = newTaskDto; taskDtoModel.setObject(newTaskDto); objectWrapperModel.setObject(newWrapper); - } catch (ObjectNotFoundException|SchemaException|ExpressionEvaluationException|RuntimeException|Error e) { + } catch (SchemaException|RuntimeException|Error e) { LoggingUtils.logUnexpectedException(LOGGER, "Couldn't refresh task {}", e, oldTaskDto); + result.recordFatalError("Couldn't refresh task: " + e.getMessage(), e); + } + result.computeStatusIfUnknown(); + if (!result.isSuccess()) { + showResult(result); } } diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/admin/server/dto/TaskDto.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/admin/server/dto/TaskDto.java index ae6b48f0999..6fd3c47c670 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/admin/server/dto/TaskDto.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/admin/server/dto/TaskDto.java @@ -169,7 +169,7 @@ public class TaskDto extends Selectable implements InlineMenuable { // (e.g. with subtasks, if they are needed) - a conservative approach to this is implemented in fillInChildren public TaskDto(@NotNull TaskType taskType, TaskType parentTaskBean, ModelService modelService, TaskService taskService, ModelInteractionService modelInteractionService, TaskManager taskManager, WorkflowManager workflowManager, TaskDtoProviderOptions options, - Task opTask, OperationResult parentResult, PageBase pageBase) throws SchemaException, ObjectNotFoundException, ExpressionEvaluationException { + Task opTask, OperationResult parentResult, PageBase pageBase) throws SchemaException { Validate.notNull(modelService); Validate.notNull(taskService); Validate.notNull(modelInteractionService); @@ -191,18 +191,33 @@ public TaskDto(@NotNull TaskType taskType, TaskType parentTaskBean, ModelService fillInParentTaskAttributes(taskType, parentTaskBean, taskService, options, opTask, thisOpResult); fillInOperationResultAttributes(taskType); if (options.isRetrieveModelContext()) { - fillInModelContext(taskType, modelInteractionService, opTask, thisOpResult); + try { + fillInModelContext(taskType, modelInteractionService, opTask, thisOpResult); + } catch (CommonException | RuntimeException e) { + LoggingUtils.logUnexpectedException(LOGGER, "Couldn't retrieve model context for task {}", e, taskType); + // TODO make sure that op result contains the error (in common cases it is there) + } } if (options.isRetrieveWorkflowContext()) { // TODO fill-in "cheap" wf attributes not only when this option is set - fillInWorkflowAttributes(taskType, modelInteractionService, workflowManager, pageBase.getPrismContext(), opTask, thisOpResult); + try { + fillInWorkflowAttributes(taskType, modelInteractionService, workflowManager, pageBase.getPrismContext(), opTask, thisOpResult); + } catch (CommonException | RuntimeException e) { + LoggingUtils.logUnexpectedException(LOGGER, "Couldn't retrieve workflow-related attributes from task {}", e, taskType); + // TODO make sure that op result contains the error (in common cases it is there) + } } thisOpResult.computeStatusIfUnknown(); fillFromExtension(); - fillInChildren(taskType, modelService, taskService, modelInteractionService, taskManager, workflowManager, options, - opTask, parentResult, pageBase); + try { + fillInChildren(taskType, modelService, taskService, modelInteractionService, taskManager, workflowManager, options, + opTask, parentResult, pageBase); + } catch (CommonException | RuntimeException e) { + LoggingUtils.logUnexpectedException(LOGGER, "Couldn't retrieve children task information for task {}", e, taskType); + // TODO make sure that op result contains the error (in common cases it is there) + } if (options.isCreateHandlerDto()) { handlerDto = HandlerDtoFactory.instance().createDtoForTask(this, pageBase, opTask, thisOpResult); @@ -217,7 +232,7 @@ public TaskDto(@NotNull TaskType taskType, TaskType parentTaskBean, ModelService private void fillInChildren(@NotNull TaskType taskType, ModelService modelService, TaskService taskService, ModelInteractionService modelInteractionService, TaskManager taskManager, WorkflowManager workflowManager, TaskDtoProviderOptions options, Task opTask, OperationResult parentResult, PageBase pageBase) - throws SchemaException, ObjectNotFoundException, ExpressionEvaluationException { + throws SchemaException { TaskType thisTaskWithChildren = null; for (TaskType child : taskType.getSubtask()) { TaskDto childTaskDto = new TaskDto(child, thisTaskWithChildren, modelService, taskService, modelInteractionService, @@ -325,8 +340,7 @@ private void fillInOperationResultAttributes(TaskType taskType) throws SchemaExc } private void fillInModelContext(TaskType taskType, ModelInteractionService modelInteractionService, Task opTask, OperationResult result) throws ObjectNotFoundException { - ModelContext ctx = ModelContextUtil.unwrapModelContext(taskType.getModelOperationContext(), modelInteractionService, opTask, result - ); + ModelContext ctx = ModelContextUtil.unwrapModelContext(taskType.getModelOperationContext(), modelInteractionService, opTask, result); if (ctx != null) { modelOperationStatusDto = new ModelOperationStatusDto(ctx, modelInteractionService, opTask, result); } diff --git a/gui/admin-gui/src/main/resources/static/js/midpoint-theme.js b/gui/admin-gui/src/main/resources/static/js/midpoint-theme.js index 72350e54d2c..2f2c463a95b 100644 --- a/gui/admin-gui/src/main/resources/static/js/midpoint-theme.js +++ b/gui/admin-gui/src/main/resources/static/js/midpoint-theme.js @@ -34,7 +34,8 @@ function clickFuncWicket6(eventData) { || (clickedElement.tagName.toUpperCase() == 'INPUT' && (clickedElement.type.toUpperCase() == 'BUTTON' || clickedElement.type.toUpperCase() == 'SUBMIT'))) - && clickedElement.parentNode.id.toUpperCase() != 'NOBUSY' ) { + && clickedElement.parentNode.id.toUpperCase() != 'NOBUSY' + && clickedElement.disabled == 'false') { showAjaxStatusSign(); } } 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 6bd4ec3bfb1..ea5b97073f5 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 @@ -13212,6 +13212,17 @@ + + + + Specification of lifecycle states and state transitions. + + + 3.7.1 + true + + + @@ -17463,6 +17474,13 @@ tns:FormType + @@ -17502,9 +17520,22 @@

+ + @@ -17521,6 +17552,7 @@ It has to be a prism property. --> + diff --git a/infra/schema/src/main/resources/xml/ns/public/common/common-policy-3.xsd b/infra/schema/src/main/resources/xml/ns/public/common/common-policy-3.xsd index 70ca5262668..5f2fdaa159b 100644 --- a/infra/schema/src/main/resources/xml/ns/public/common/common-policy-3.xsd +++ b/infra/schema/src/main/resources/xml/ns/public/common/common-policy-3.xsd @@ -1,7 +1,7 @@ + + + +

+ State entry action. Action that is executed when the state is entered. +

+
+
+
+ + + +

+ State exit action. Action that is executed when the state is exited. +

+
+
+
+ + + +

+ Transition to another state. +

+
+
+
+ + + + + + + Specification of lifecycle state transition. + EXPERIMENTAL + + + + 3.7.1 + true + + + + + + +

+ Short name for the transition. It may be used in log files, user interface, etc. +

+
+
+
+ + + +

+ Free-form description of transition purpose, mechanisms, etc. + Used for documentation purposes (comment). +

+
+
+
+ + + +

+ Identifier (URI) of the state that is the target of the transition. +

+
+
+
+ + + +

+ Condition for automatic state transition. If the condition returns true value + then lifecycle transitions to the target state. +

+
+
+
+
+
+ + + + + TODO + EXPERIMENTAL + + + + 3.7.1 + true + + + + + + +

+ Short name of the action. It may be used in log files, user interface, etc. +

+
+
+
+ + + +

+ Free-form description of the action purpose, mechanisms, etc. + Used for documentation purposes (comment). +

+
+
+
+ + + +

+ Action that reduces (purges) object data. +

+
+
+
+ +
+
+ + + + + Action that reduces (purges) object data. + EXPERIMENTAL + + + + 3.7.1 + true + + + + + + +

+ Remove all values of the item. +

+
+
+
+ + +
+
diff --git a/model/model-common/src/main/java/com/evolveum/midpoint/model/common/stringpolicy/AbstractValuePolicyOriginResolver.java b/model/model-common/src/main/java/com/evolveum/midpoint/model/common/stringpolicy/AbstractValuePolicyOriginResolver.java index a0f4d51a310..d750162cfbc 100644 --- a/model/model-common/src/main/java/com/evolveum/midpoint/model/common/stringpolicy/AbstractValuePolicyOriginResolver.java +++ b/model/model-common/src/main/java/com/evolveum/midpoint/model/common/stringpolicy/AbstractValuePolicyOriginResolver.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2017 Evolveum + * Copyright (c) 2017-2018 Evolveum * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,20 +17,31 @@ import com.evolveum.midpoint.prism.PrismObject; import com.evolveum.midpoint.prism.query.ObjectQuery; +import com.evolveum.midpoint.prism.query.builder.QueryBuilder; import com.evolveum.midpoint.schema.GetOperationOptions; +import com.evolveum.midpoint.schema.ResourceShadowDiscriminator; import com.evolveum.midpoint.schema.ResultHandler; import com.evolveum.midpoint.schema.SelectorOptions; import com.evolveum.midpoint.schema.result.OperationResult; import com.evolveum.midpoint.schema.util.ObjectResolver; +import com.evolveum.midpoint.schema.util.ShadowUtil; import com.evolveum.midpoint.task.api.Task; +import com.evolveum.midpoint.util.Holder; import com.evolveum.midpoint.util.exception.CommunicationException; import com.evolveum.midpoint.util.exception.ConfigurationException; import com.evolveum.midpoint.util.exception.ExpressionEvaluationException; import com.evolveum.midpoint.util.exception.ObjectNotFoundException; import com.evolveum.midpoint.util.exception.SchemaException; import com.evolveum.midpoint.util.exception.SecurityViolationException; +import com.evolveum.midpoint.util.exception.SystemException; +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.ObjectReferenceType; import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.ProhibitedValueItemType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.ShadowDiscriminatorType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.ShadowType; import com.evolveum.midpoint.xml.ns._public.common.common_3.UserType; import com.evolveum.midpoint.xml.ns._public.common.common_3.ValuePolicyOriginType; @@ -40,6 +51,8 @@ */ public abstract class AbstractValuePolicyOriginResolver { + private static final Trace LOGGER = TraceManager.getTrace(AbstractValuePolicyOriginResolver.class); + private final PrismObject object; private final ObjectResolver objectResolver; @@ -60,7 +73,8 @@ public Class getOwnerClass() { } // TODO: later maybe isolate this method to an interface (ValuePolicyTypeResolver) - public void resolve(ResultHandler handler, ValuePolicyOriginType originType, String contextDescription, Task task, OperationResult result) throws ObjectNotFoundException, SchemaException, CommunicationException, ConfigurationException, SecurityViolationException, ExpressionEvaluationException { + public void resolve(ResultHandler handler, ProhibitedValueItemType prohibitedValueItemType, String contextDescription, Task task, OperationResult result) throws ObjectNotFoundException, SchemaException, CommunicationException, ConfigurationException, SecurityViolationException, ExpressionEvaluationException { + ValuePolicyOriginType originType = prohibitedValueItemType.getOrigin(); if (originType == null) { handleObject(handler, result); } @@ -74,6 +88,9 @@ public void resolve(ResultHandler handler, ValuePolicy case PERSONA: handlePersonas(handler, contextDescription, task, result); break; + case PROJECTION: + handleProjections(handler, prohibitedValueItemType, contextDescription, task, result); + break; default: throw new IllegalArgumentException("Unexpected origin type "+originType); } @@ -93,6 +110,52 @@ private

void handlePersonas(ResultHandler

handler, Str handler.handle((PrismObject

) persona.asPrismObject(), result); } } + + private

void handleProjections(ResultHandler

handler, ProhibitedValueItemType prohibitedValueItemType, String contextDescription, Task task, OperationResult result) throws ObjectNotFoundException, SchemaException { + PrismObject object = getObject(); + // Not very efficient. We will usually read the shadows again, as they are already in model context. + // It will also work only for the items that are stored in shadow (usually not attributes, unless caching is enabled). + // But this is good enough for now. + FocusType focusType; + if (object.canRepresent(FocusType.class)) { + focusType = ((FocusType)object.asObjectable()); + } else if (object.canRepresent(ShadowType.class)) { + ObjectQuery query = QueryBuilder + .queryFor(FocusType.class, getObject().getPrismContext()) + .item(UserType.F_LINK_REF).ref(getObject().getOid()) + .build(); + final Holder focusTypeHolder = new Holder<>(); + try { + objectResolver.searchIterative(FocusType.class, query, + SelectorOptions.createCollection(GetOperationOptions.createReadOnly()), + (foundObject, objectResult) -> { + focusTypeHolder.setValue(foundObject.asObjectable()); + return true; + }, task, result); + } catch (CommunicationException | ConfigurationException | SecurityViolationException + | ExpressionEvaluationException e) { + throw new SystemException(e.getMessage(), e); + } + focusType = focusTypeHolder.getValue(); + } else { + return; + } + ResourceShadowDiscriminator shadowDiscriminator = ResourceShadowDiscriminator.fromResourceShadowDiscriminatorType(prohibitedValueItemType.getProjectionDiscriminator()); + for (ObjectReferenceType linkRef: focusType.getLinkRef()) { + GetOperationOptions options = GetOperationOptions.createReadOnly(); + options.setNoFetch(true); + ShadowType shadowType = objectResolver.resolve(linkRef, ShadowType.class, + SelectorOptions.createCollection(options), + "resolving projection shadow in " + contextDescription, task, result); + if (shadowDiscriminator != null) { + if (!ShadowUtil.matches(shadowType.asPrismObject(), shadowDiscriminator)) { + LOGGER.trace("Skipping evaluation of projection {} in {} because it does not match discriminator", shadowType, contextDescription); + continue; + } + } + handler.handle((PrismObject

) shadowType.asPrismObject(), result); + } + } private

void handleOwner(ResultHandler

handler, String contextDescription, OperationResult result) throws ObjectNotFoundException, SchemaException, CommunicationException, ConfigurationException, SecurityViolationException, ExpressionEvaluationException { objectResolver.searchIterative(getOwnerClass(), getOwnerQuery(), SelectorOptions.createCollection(GetOperationOptions.createReadOnly()), diff --git a/model/model-common/src/main/java/com/evolveum/midpoint/model/common/stringpolicy/ValuePolicyProcessor.java b/model/model-common/src/main/java/com/evolveum/midpoint/model/common/stringpolicy/ValuePolicyProcessor.java index fa682afeaab..d63fdba87c7 100644 --- a/model/model-common/src/main/java/com/evolveum/midpoint/model/common/stringpolicy/ValuePolicyProcessor.java +++ b/model/model-common/src/main/java/com/evolveum/midpoint/model/common/stringpolicy/ValuePolicyProcessor.java @@ -459,7 +459,7 @@ private boolean checkProhibitedValu return true; }; - originResolver.resolve(handler, prohibitedItemType.getOrigin(), shortDesc, task, result); + originResolver.resolve(handler, prohibitedItemType, shortDesc, task, result); } return isAcceptable.booleanValue(); diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/LensContext.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/LensContext.java index 774432cd779..f74ead8a775 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/LensContext.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/LensContext.java @@ -1227,11 +1227,13 @@ public static LensContext fromLensContextType(LensContextType lensContextType, P return lensContext; } - protected void fixProvisioningTypeInDelta(ObjectDelta delta, Task task, OperationResult result) + private void fixProvisioningTypeInDelta(ObjectDelta delta, Task task, OperationResult result) throws SchemaException, ObjectNotFoundException, CommunicationException, ConfigurationException, ExpressionEvaluationException { if (delta != null && delta.getObjectTypeClass() != null && (ShadowType.class.isAssignableFrom(delta.getObjectTypeClass()) || ResourceType.class.isAssignableFrom(delta.getObjectTypeClass()))) { + // TODO exception can be thrown here (MID-4391) e.g. if resource does not exist any more; consider what to do + // Currently we are on the safe side by making whole conversion fail getProvisioningService().applyDefinition(delta, task, result); } } diff --git a/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/password/AbstractPasswordTest.java b/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/password/AbstractPasswordTest.java index 25108bbe608..fe982b26761 100644 --- a/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/password/AbstractPasswordTest.java +++ b/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/password/AbstractPasswordTest.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. @@ -48,6 +48,7 @@ import com.evolveum.midpoint.prism.util.PrismAsserts; import com.evolveum.midpoint.prism.util.PrismTestUtil; import com.evolveum.midpoint.prism.xml.XmlTypeConverter; +import com.evolveum.midpoint.provisioning.api.ItemComparisonResult; import com.evolveum.midpoint.schema.GetOperationOptions; import com.evolveum.midpoint.schema.SelectorOptions; import com.evolveum.midpoint.schema.constants.SchemaConstants; @@ -78,6 +79,10 @@ public abstract class AbstractPasswordTest extends AbstractInitializedModelInteg protected static final String USER_PASSWORD_VALID_2 = "abcd223"; protected static final String USER_PASSWORD_VALID_3 = "abcd323"; protected static final String USER_PASSWORD_VALID_4 = "abcd423"; + + protected static final String PASSWORD_ALLIGATOR = "all1g4t0r"; + protected static final String PASSWORD_CROCODILE = "cr0c0d1l3"; + protected static final String PASSWORD_GIANT_LIZARD = "G14NTl1z4rd"; public static final File TEST_DIR = new File(MidPointTestConstants.TEST_RESOURCES_DIR, "password"); @@ -88,9 +93,20 @@ public abstract class AbstractPasswordTest extends AbstractInitializedModelInteg protected static final File RESOURCE_DUMMY_LIFECYCLE_FILE = new File(TEST_DIR, "resource-dummy-lifecycle.xml"); protected static final String RESOURCE_DUMMY_LIFECYCLE_OID = "519f131a-147b-11e7-a270-c38e2b225751"; protected static final String RESOURCE_DUMMY_LIFECYCLE_NAME = "lifecycle"; + + protected static final File RESOURCE_DUMMY_SOUVENIR_FILE = new File(TEST_DIR, "resource-dummy-souvenir.xml"); + protected static final String RESOURCE_DUMMY_SOUVENIR_OID = "f4fd7e90-ff6a-11e7-a504-4b84f92fec0e"; + protected static final String RESOURCE_DUMMY_SOUVENIR_NAME = "souvenir"; + + protected static final File RESOURCE_DUMMY_MAVERICK_FILE = new File(TEST_DIR, "resource-dummy-maverick.xml"); + protected static final String RESOURCE_DUMMY_MAVERICK_OID = "72a928b6-ff7b-11e7-9643-7366d7749c31"; + protected static final String RESOURCE_DUMMY_MAVERICK_NAME = "maverick"; protected static final File PASSWORD_POLICY_UGLY_FILE = new File(TEST_DIR, "password-policy-ugly.xml"); protected static final String PASSWORD_POLICY_UGLY_OID = "cfb3fa9e-027a-11e7-8e2c-dbebaacaf4ee"; + + protected static final File PASSWORD_POLICY_MAVERICK_FILE = new File(TEST_DIR, "password-policy-maverick.xml"); + protected static final String PASSWORD_POLICY_MAVERICK_OID = "b26d2bd4-ff83-11e7-94b3-8fa7a87aac6c"; protected static final File SECURITY_POLICY_DEFAULT_STORAGE_HASHING_FILE = new File(TEST_DIR, "security-policy-default-storage-hashing.xml"); protected static final String SECURITY_POLICY_DEFAULT_STORAGE_HASHING_OID = "0ea3b93c-0425-11e7-bbc1-73566dc53d59"; @@ -104,9 +120,12 @@ public abstract class AbstractPasswordTest extends AbstractInitializedModelInteg protected String accountJackOid; protected String accountJackRedOid; + protected String accountJackBlueOid; protected String accountJackUglyOid; protected String accountJackBlackOid; protected String accountJackYellowOid; + protected String accountJackSouvenirOid; + protected String accountJackMaverickOid; protected XMLGregorianCalendar lastPasswordChangeStart; protected XMLGregorianCalendar lastPasswordChangeEnd; @@ -116,6 +135,7 @@ public void initSystem(Task initTask, OperationResult initResult) throws Excepti super.initSystem(initTask, initResult); importObjectFromFile(PASSWORD_POLICY_UGLY_FILE); + importObjectFromFile(PASSWORD_POLICY_MAVERICK_FILE); importObjectFromFile(SECURITY_POLICY_DEFAULT_STORAGE_HASHING_FILE); importObjectFromFile(SECURITY_POLICY_PASSWORD_STORAGE_NONE_FILE); @@ -123,6 +143,8 @@ public void initSystem(Task initTask, OperationResult initResult) throws Excepti initDummyResourcePirate(RESOURCE_DUMMY_UGLY_NAME, RESOURCE_DUMMY_UGLY_FILE, RESOURCE_DUMMY_UGLY_OID, initTask, initResult); initDummyResourcePirate(RESOURCE_DUMMY_LIFECYCLE_NAME, RESOURCE_DUMMY_LIFECYCLE_FILE, RESOURCE_DUMMY_LIFECYCLE_OID, initTask, initResult); + initDummyResourcePirate(RESOURCE_DUMMY_SOUVENIR_NAME, RESOURCE_DUMMY_SOUVENIR_FILE, RESOURCE_DUMMY_SOUVENIR_OID, initTask, initResult); + initDummyResourcePirate(RESOURCE_DUMMY_MAVERICK_NAME, RESOURCE_DUMMY_MAVERICK_FILE, RESOURCE_DUMMY_MAVERICK_OID, initTask, initResult); login(USER_ADMINISTRATOR_USERNAME); } @@ -295,8 +317,8 @@ public void test070AddUserHerman() throws Exception { } @Test - public void test100ModifyUserJackAssignAccount() throws Exception { - final String TEST_NAME = "test100ModifyUserJackAssignAccount"; + public void test100JackAssignAccountDummy() throws Exception { + final String TEST_NAME = "test100JackAssignAccountDummy"; displayTestTitle(TEST_NAME); // GIVEN @@ -482,8 +504,8 @@ public void test112ModifyJackPasswordUserAndAccount() throws Exception { * Add red and ugly dummy resource to the mix. This would be fun. */ @Test - public void test120ModifyUserJackAssignAccountDummyRedAndUgly() throws Exception { - final String TEST_NAME = "test120ModifyUserJackAssignAccountDummyRedAndUgly"; + public void test120JackAssignAccountDummyRedAndUgly() throws Exception { + final String TEST_NAME = "test120JackAssignAccountDummyRedAndUgly"; displayTestTitle(TEST_NAME); // GIVEN @@ -705,8 +727,8 @@ public void test128ModifyJackEmployeeNumberGood() throws Exception { * Black resource has minimum password length constraint (enforced by midPoint). */ @Test - public void test130ModifyUserJackAssignAccountDummyBlack() throws Exception { - final String TEST_NAME = "test130ModifyUserJackAssignAccountDummyBlack"; + public void test130JackAssignAccountDummyBlack() throws Exception { + final String TEST_NAME = "test130JackAssignAccountDummyBlack"; displayTestTitle(TEST_NAME); // GIVEN @@ -772,8 +794,8 @@ public void test132ModifyAccountBlackJackPasswordBad() throws Exception { } @Test - public void test139ModifyUserJackUnassignAccountDummyBlack() throws Exception { - final String TEST_NAME = "test139ModifyUserJackUnassignAccountDummyBlack"; + public void test139JackUnassignAccountDummyBlack() throws Exception { + final String TEST_NAME = "test139JackUnassignAccountDummyBlack"; displayTestTitle(TEST_NAME); // GIVEN @@ -814,8 +836,8 @@ public void test139ModifyUserJackUnassignAccountDummyBlack() throws Exception { * But this time the password is OK. */ @Test - public void test140ModifyUserJackAssignAccountDummyYellow() throws Exception { - final String TEST_NAME = "test140ModifyUserJackAssignAccountDummyYellow"; + public void test140JackAssignAccountDummyYellow() throws Exception { + final String TEST_NAME = "test140JackAssignAccountDummyYellow"; displayTestTitle(TEST_NAME); // GIVEN @@ -911,8 +933,8 @@ public void test142ModifyUserJackPasswordAA() throws Exception { } @Test - public void test200ApplyPasswordPolicy() throws Exception { - final String TEST_NAME = "test200ApplyPasswordPolicy"; + public void test200ApplyPasswordPolicyHistoryLength() throws Exception { + final String TEST_NAME = "test200ApplyPasswordPolicyHistoryLength"; displayTestTitle(TEST_NAME); // GIVEN @@ -1394,14 +1416,13 @@ public void test310PreparePasswordStrengthTests() throws Exception { SystemConfigurationType.F_GLOBAL_PASSWORD_POLICY_REF, task, result); // WHEN - TestUtil.displayWhen(TEST_NAME); + displayWhen(TEST_NAME); assignAccount(USER_JACK_OID, RESOURCE_DUMMY_RED_OID, null, task, result); assignAccount(USER_JACK_OID, RESOURCE_DUMMY_BLUE_OID, null, task, result); // THEN - TestUtil.displayThen(TEST_NAME); - result.computeStatus(); - TestUtil.assertSuccess(result); + displayThen(TEST_NAME); + assertSuccess(result); PrismObject userAfter = getUser(USER_JACK_OID); display("User after change execution", userAfter); @@ -1976,20 +1997,15 @@ public void test404InitializeRappDummyRed() throws Exception { display("User before", userBefore); String accountRedOid = getLinkRefOid(userBefore, RESOURCE_DUMMY_RED_OID); - ObjectDelta shadowDelta = ObjectDelta.createEmptyModifyDelta(ShadowType.class, accountRedOid, prismContext); - ProtectedStringType passwordPs = new ProtectedStringType(); - passwordPs.setClearValue(USER_PASSWORD_VALID_1); - shadowDelta.addModificationReplaceProperty(SchemaConstants.PATH_PASSWORD_VALUE, passwordPs); - shadowDelta.addModificationReplaceProperty(ObjectType.F_LIFECYCLE_STATE, SchemaConstants.LIFECYCLE_ACTIVE); + ObjectDelta shadowDelta = createAccountInitializationDelta(accountRedOid, USER_PASSWORD_VALID_1); // WHEN - TestUtil.displayWhen(TEST_NAME); + displayWhen(TEST_NAME); executeChanges(shadowDelta, null, task, result); // THEN - TestUtil.displayThen(TEST_NAME); - result.computeStatus(); - TestUtil.assertSuccess(result); + displayThen(TEST_NAME); + assertSuccess(result); PrismObject userAfter = getUser(USER_RAPP_OID); display("User after", userAfter); @@ -2360,6 +2376,456 @@ public void test416UserRappEmployeeTypeWreck() throws Exception { } // TODO: employeeType->WRECK + + /** + * MID-4397 + */ + @Test + public void test500JackAssignResourceSouvenir() throws Exception { + final String TEST_NAME = "test500JackAssignResourceSouvenir"; + displayTestTitle(TEST_NAME); + + // GIVEN + Task task = createTask(TEST_NAME); + OperationResult result = task.getResult(); + prepareTest(); + + // preconditions + PrismObject userBefore = getUser(USER_JACK_OID); + display("User before", userBefore); + assertAssignedAccount(userBefore, RESOURCE_DUMMY_OID); + assertAssignedAccount(userBefore, RESOURCE_DUMMY_RED_OID); + assertAssignedAccount(userBefore, RESOURCE_DUMMY_BLUE_OID); + assertAssignedAccount(userBefore, RESOURCE_DUMMY_UGLY_OID); + assertAssignments(userBefore, 4); + assertAccount(userBefore, RESOURCE_DUMMY_OID); + assertAccount(userBefore, RESOURCE_DUMMY_RED_OID); + accountJackBlueOid = assertAccount(userBefore, RESOURCE_DUMMY_BLUE_OID); + assertAccount(userBefore, RESOURCE_DUMMY_UGLY_OID); + assertLinks(userBefore, 4); + assertUserPassword(userBefore, USER_PASSWORD_VALID_3); + + // WHEN + displayWhen(TEST_NAME); + assignAccount(USER_JACK_OID, RESOURCE_DUMMY_SOUVENIR_OID, null, task, result); + + // THEN + displayThen(TEST_NAME); + assertSuccess(result); + + PrismObject userAfter = getUser(USER_JACK_OID); + display("User after", userAfter); + assertAssignments(userAfter, 5); + assertAssignedAccount(userAfter, RESOURCE_DUMMY_OID); + assertAssignedAccount(userAfter, RESOURCE_DUMMY_RED_OID); + assertAssignedAccount(userAfter, RESOURCE_DUMMY_BLUE_OID); + assertAssignedAccount(userAfter, RESOURCE_DUMMY_UGLY_OID); + assertAssignedAccount(userAfter, RESOURCE_DUMMY_SOUVENIR_OID); + assertLinks(userAfter, 5); + assertAccount(userAfter, RESOURCE_DUMMY_OID); + assertAccount(userAfter, RESOURCE_DUMMY_RED_OID); + assertAccount(userAfter, RESOURCE_DUMMY_BLUE_OID); + assertAccount(userAfter, RESOURCE_DUMMY_UGLY_OID); + accountJackSouvenirOid = assertAccount(userAfter, RESOURCE_DUMMY_SOUVENIR_OID); + assertUserPassword(userAfter, USER_PASSWORD_VALID_3); + + PrismObject shadowPasswordCachingModel = getShadowModel(accountJackSouvenirOid); + assertShadowLifecycle(shadowPasswordCachingModel, false); + + assertDummyPasswordConditional(RESOURCE_DUMMY_SOUVENIR_NAME, ACCOUNT_JACK_DUMMY_USERNAME, USER_PASSWORD_VALID_3); + + displayAllNotifications(); + assertSinglePasswordNotificationConditional(RESOURCE_DUMMY_SOUVENIR_NAME, USER_JACK_USERNAME, USER_PASSWORD_VALID_3); + assertAccountActivationNotification(RESOURCE_DUMMY_SOUVENIR_NAME, USER_JACK_USERNAME); + } + + /** + * MID-4397 + */ + @Test + public void test502JackInitializeAccountSouvenir() throws Exception { + final String TEST_NAME = "test502JackInitializeAccountSouvenir"; + displayTestTitle(TEST_NAME); + + // GIVEN + Task task = createTask(TEST_NAME); + OperationResult result = task.getResult(); + prepareTest(); + + ObjectDelta shadowDelta = createAccountInitializationDelta(accountJackSouvenirOid, PASSWORD_ALLIGATOR); + + // WHEN + displayWhen(TEST_NAME); + executeChanges(shadowDelta, null, task, result); + + // THEN + displayThen(TEST_NAME); + assertSuccess(result); + + PrismObject userAfter = getUser(USER_JACK_OID); + display("User after", userAfter); + assertAssignments(userAfter, 5); + assertLinks(userAfter, 5); + assertUserPassword(userAfter, USER_PASSWORD_VALID_3); + + PrismObject shadowPasswordCachingRepo = getShadowRepo(accountJackSouvenirOid); + display("Shadow repo", shadowPasswordCachingRepo); + assertShadowLifecycle(shadowPasswordCachingRepo, SchemaConstants.LIFECYCLE_ACTIVE); + assertCachedResourcePassword(shadowPasswordCachingRepo, PASSWORD_ALLIGATOR); + + assertDummyPassword(RESOURCE_DUMMY_SOUVENIR_NAME, ACCOUNT_JACK_DUMMY_USERNAME, PASSWORD_ALLIGATOR); + + ItemComparisonResult comparisonResult = provisioningService.compare(ShadowType.class, accountJackSouvenirOid, SchemaConstants.PATH_PASSWORD_VALUE, + PASSWORD_ALLIGATOR, task, result); + assertEquals("Wrong comparison result", ItemComparisonResult.MATCH, comparisonResult); + + // TODO +// assertSingleInitializationPasswordNotification(RESOURCE_DUMMY_PASSWORD_CACHING_NAME, USER_JACK_USERNAME, PASSWORD_Alligator); + } + + /** + * MID-4397 + */ + @Test + public void test510JackAssignResourceMaverick() throws Exception { + final String TEST_NAME = "test510JackAssignResourceMaverick"; + displayTestTitle(TEST_NAME); + + // GIVEN + Task task = createTask(TEST_NAME); + OperationResult result = task.getResult(); + prepareTest(); + + // preconditions + PrismObject userBefore = getUser(USER_JACK_OID); + display("User before", userBefore); + assertAssignments(userBefore, 5); + assertLinks(userBefore, 5); + assertUserPassword(userBefore, USER_PASSWORD_VALID_3); + + // WHEN + displayWhen(TEST_NAME); + assignAccount(USER_JACK_OID, RESOURCE_DUMMY_MAVERICK_OID, null, task, result); + + // THEN + displayThen(TEST_NAME); + assertSuccess(result); + + PrismObject userAfter = getUser(USER_JACK_OID); + display("User after", userAfter); + assertAssignments(userAfter, 6); + assertLinks(userAfter, 6); + accountJackMaverickOid = assertAccount(userAfter, RESOURCE_DUMMY_MAVERICK_OID); + assertUserPassword(userAfter, USER_PASSWORD_VALID_3); + + assertDummyPasswordConditional(RESOURCE_DUMMY_MAVERICK_NAME, ACCOUNT_JACK_DUMMY_USERNAME, USER_PASSWORD_VALID_3); + + displayAllNotifications(); + assertSinglePasswordNotificationConditional(RESOURCE_DUMMY_MAVERICK_NAME, USER_JACK_USERNAME, USER_PASSWORD_VALID_3); + assertAccountActivationNotification(RESOURCE_DUMMY_MAVERICK_NAME, USER_JACK_USERNAME); + } + + /** + * Attempt to initialize account with password which is the same as on the souvenir + * resource should fail. Maverick resource password policy states that the password + * must be different that the password on souvenir resource. + * MID-4397 + */ + @Test + public void test512JackInitializeAccountMaverickAlligator() throws Exception { + final String TEST_NAME = "test512JackInitializeAccountMaverick"; + displayTestTitle(TEST_NAME); + + // GIVEN + Task task = createTask(TEST_NAME); + OperationResult result = task.getResult(); + prepareTest(); + + ObjectDelta shadowDelta = createAccountInitializationDelta(accountJackMaverickOid, PASSWORD_ALLIGATOR); + + try { + // WHEN + displayWhen(TEST_NAME); + executeChanges(shadowDelta, null, task, result); + + assertNotReached(); + + } catch (PolicyViolationException e) { + display("Expected exception", e); + } + + // THEN + displayThen(TEST_NAME); + assertFailure(result); + + PrismObject userAfter = getUser(USER_JACK_OID); + display("User after", userAfter); + assertAssignments(userAfter, 6); + assertLinks(userAfter, 6); + assertUserPassword(userAfter, USER_PASSWORD_VALID_3); + + assertDummyPasswordConditional(RESOURCE_DUMMY_MAVERICK_NAME, ACCOUNT_JACK_DUMMY_USERNAME, USER_PASSWORD_VALID_3); + + // TODO +// assertSingleInitializationPasswordNotification(RESOURCE_DUMMY_PASSWORD_CACHING_NAME, USER_JACK_USERNAME, PASSWORD_Alligator); + } + + /** + * Attempt to initialize account with a different password should pass. + * MID-4397 + */ + @Test + public void test514JackInitializeAccountMaverickCrododile() throws Exception { + final String TEST_NAME = "test514JackInitializeAccountMaverickCrododile"; + displayTestTitle(TEST_NAME); + + // GIVEN + Task task = createTask(TEST_NAME); + OperationResult result = task.getResult(); + prepareTest(); + + ObjectDelta shadowDelta = createAccountInitializationDelta(accountJackMaverickOid, PASSWORD_CROCODILE); + + // WHEN + displayWhen(TEST_NAME); + executeChanges(shadowDelta, null, task, result); + + // THEN + displayThen(TEST_NAME); + assertSuccess(result); + + PrismObject userAfter = getUser(USER_JACK_OID); + display("User after", userAfter); + assertAssignments(userAfter, 6); + assertLinks(userAfter, 6); + assertUserPassword(userAfter, USER_PASSWORD_VALID_3); + + PrismObject shadowMaverickRepo = getShadowRepo(accountJackMaverickOid); + display("Shadow repo", shadowMaverickRepo); + assertShadowLifecycle(shadowMaverickRepo, SchemaConstants.LIFECYCLE_ACTIVE); + + assertDummyPassword(RESOURCE_DUMMY_MAVERICK_NAME, ACCOUNT_JACK_DUMMY_USERNAME, PASSWORD_CROCODILE); + + // TODO +// assertSingleInitializationPasswordNotification(RESOURCE_DUMMY_PASSWORD_CACHING_NAME, USER_JACK_USERNAME, PASSWORD_Alligator); + } + + /** + * Attempt to change account password to a conflicting one. It should fail. + * MID-4397 + */ + @Test + public void test516JackChangePasswordResourceMaverickAlligator() throws Exception { + final String TEST_NAME = "test516JackChangePasswordResourceMaverickAlligator"; + displayTestTitle(TEST_NAME); + + // GIVEN + Task task = createTask(TEST_NAME); + OperationResult result = task.getResult(); + prepareTest(); + + try { + // WHEN + displayWhen(TEST_NAME); + modifyAccountChangePassword(accountJackMaverickOid, PASSWORD_ALLIGATOR, task, result); + + assertNotReached(); + + } catch (PolicyViolationException e) { + display("Expected exception", e); + } + + // THEN + displayThen(TEST_NAME); + assertFailure(result); + + PrismObject userAfter = getUser(USER_JACK_OID); + display("User after", userAfter); + assertAssignments(userAfter, 6); + assertLinks(userAfter, 6); + + assertDummyPassword(RESOURCE_DUMMY_MAVERICK_NAME, USER_JACK_USERNAME, PASSWORD_CROCODILE); + } + + /** + * Attempt to change account password to non conflicting one. It should pass. + * MID-4397 + */ + @Test + public void test518JackChangePasswordResourceMaverickGiantLizard() throws Exception { + final String TEST_NAME = "test518JackChangePasswordResourceMaverickGiantLizard"; + displayTestTitle(TEST_NAME); + + // GIVEN + Task task = createTask(TEST_NAME); + OperationResult result = task.getResult(); + prepareTest(); + + // WHEN + displayWhen(TEST_NAME); + modifyAccountChangePassword(accountJackMaverickOid, PASSWORD_GIANT_LIZARD, task, result); + + // THEN + displayThen(TEST_NAME); + assertSuccess(result); + + PrismObject userAfter = getUser(USER_JACK_OID); + display("User after", userAfter); + assertAssignments(userAfter, 6); + assertLinks(userAfter, 6); + + assertDummyPassword(RESOURCE_DUMMY_MAVERICK_NAME, USER_JACK_USERNAME, PASSWORD_GIANT_LIZARD); + } + + /** + * MID-4397 + */ + @Test + public void test530JackUnassignResourceSouvenir() throws Exception { + final String TEST_NAME = "test530JackUnassignResourceSouvenir"; + displayTestTitle(TEST_NAME); + + // GIVEN + Task task = createTask(TEST_NAME); + OperationResult result = task.getResult(); + prepareTest(); + + // preconditions + PrismObject userBefore = getUser(USER_JACK_OID); + display("User before", userBefore); + assertAssignments(userBefore, 6); + assertLinks(userBefore, 6); + + // WHEN + displayWhen(TEST_NAME); + unassignAccount(USER_JACK_OID, RESOURCE_DUMMY_SOUVENIR_OID, null, task, result); + + // THEN + displayThen(TEST_NAME); + assertSuccess(result); + + PrismObject userAfter = getUser(USER_JACK_OID); + display("User after", userAfter); + assertAssignments(userAfter, 5); + assertAssignedNoAccount(userAfter, RESOURCE_DUMMY_SOUVENIR_OID); + assertLinks(userAfter, 5); + } + + /** + * Make it harder for the next test (test535ModifyUserJackPasswordAlligator). + * We set conflicting password on blue resource. But the password policy + * should NOT check blue resource. So the check should pass anyway. + * MID-4397 + */ + @Test + public void test532JackChangePasswordResourceBlueAlligator() throws Exception { + final String TEST_NAME = "test532JackChangePasswordResourceBlueAlligator"; + displayTestTitle(TEST_NAME); + + // GIVEN + Task task = createTask(TEST_NAME); + OperationResult result = task.getResult(); + prepareTest(); + + PrismObject userBefore = getUser(USER_JACK_OID); + display("User before", userBefore); + assertAssignments(userBefore, 5); + assertLinks(userBefore, 5); + accountJackBlueOid = assertAccount(userBefore, RESOURCE_DUMMY_BLUE_OID); + + // WHEN + displayWhen(TEST_NAME); + modifyAccountChangePassword(accountJackBlueOid, PASSWORD_ALLIGATOR, task, result); + + // THEN + displayThen(TEST_NAME); + assertSuccess(result); + + PrismObject userAfter = getUser(USER_JACK_OID); + display("User after", userAfter); + assertAssignments(userAfter, 5); + assertAssignedNoAccount(userAfter, RESOURCE_DUMMY_SOUVENIR_OID); + assertLinks(userAfter, 5); + + assertDummyPassword(RESOURCE_DUMMY_BLUE_NAME, USER_JACK_USERNAME, PASSWORD_ALLIGATOR); + } + + /** + * Additional check: check that the Alligator password would be otherwise legal for user and + * maverick resource. This is additional check that it was really the presence of souvenir + * resource that caused the policy violation. + * MID-4397 + */ + @Test + public void test535ModifyUserJackPasswordAlligator() throws Exception { + final String TEST_NAME = "test535ModifyUserJackPasswordAlligator"; + displayTestTitle(TEST_NAME); + + // GIVEN + Task task = createTask(TEST_NAME); + OperationResult result = task.getResult(); + prepareTest(); + + // WHEN + displayWhen(TEST_NAME); + modifyUserChangePassword(USER_JACK_OID, PASSWORD_ALLIGATOR, task, result); + + // THEN + displayThen(TEST_NAME); + assertSuccess(result); + + PrismObject userAfter = getUser(USER_JACK_OID); + display("User after", userAfter); + assertAssignments(userAfter, 5); + assertLinks(userAfter, 5); + assertUserPassword(userAfter, PASSWORD_ALLIGATOR); + + assertDummyPassword(RESOURCE_DUMMY_MAVERICK_NAME, ACCOUNT_JACK_DUMMY_USERNAME, PASSWORD_ALLIGATOR); + } + + /** + * MID-4397 + */ + @Test + public void test539JackUnassignResourceMaverick() throws Exception { + final String TEST_NAME = "test539JackUnassignResourceMaverick"; + displayTestTitle(TEST_NAME); + + // GIVEN + Task task = createTask(TEST_NAME); + OperationResult result = task.getResult(); + prepareTest(); + + // preconditions + PrismObject userBefore = getUser(USER_JACK_OID); + display("User before", userBefore); + assertAssignments(userBefore, 5); + assertLinks(userBefore, 5); + + // WHEN + displayWhen(TEST_NAME); + unassignAccount(USER_JACK_OID, RESOURCE_DUMMY_MAVERICK_OID, null, task, result); + + // THEN + displayThen(TEST_NAME); + assertSuccess(result); + + PrismObject userAfter = getUser(USER_JACK_OID); + display("User after", userAfter); + assertAssignments(userAfter, 4); + assertAssignedNoAccount(userAfter, RESOURCE_DUMMY_MAVERICK_OID); + assertAssignedNoAccount(userAfter, RESOURCE_DUMMY_SOUVENIR_OID); + assertLinks(userAfter, 4); + } + + protected ObjectDelta createAccountInitializationDelta(String accountOid, String newAccountPassword) { + ObjectDelta shadowDelta = ObjectDelta.createEmptyModifyDelta(ShadowType.class, accountOid, prismContext); + ProtectedStringType passwordPs = new ProtectedStringType(); + passwordPs.setClearValue(newAccountPassword); + shadowDelta.addModificationReplaceProperty(SchemaConstants.PATH_PASSWORD_VALUE, passwordPs); + shadowDelta.addModificationReplaceProperty(ObjectType.F_LIFECYCLE_STATE, SchemaConstants.LIFECYCLE_ACTIVE); + return shadowDelta; + } protected void assertDummyPassword(String userId, String expectedClearPassword) throws SchemaViolationException, ConflictException { assertDummyPassword(null, userId, expectedClearPassword); @@ -2425,6 +2891,8 @@ private void assertShadowPasswordMetadata(PrismObject shadow, } assertShadowPasswordMetadata(shadow, passwordCreated, startCal, endCal, USER_ADMINISTRATOR_OID, SchemaConstants.CHANNEL_GUI_USER_URI); } + + // 9XX tests: Password minimal age, no password, etc. /** * Let's have a baseline for other 90x tests. @@ -2617,5 +3085,21 @@ protected PrismObject getBlueShadow(PrismObject userAfter) protected boolean isPasswordEncryption() { return getPasswordStorageType() == CredentialsStorageTypeType.ENCRYPTION; } + + protected void assertCachedResourcePassword(PrismObject shadow, String expectedPassword) throws Exception { + CredentialsType credentials = shadow.asObjectable().getCredentials(); + if (expectedPassword == null && credentials == null) { + return; + } + assertNotNull("Missing credentendials in repo shadow "+shadow, credentials); + PasswordType passwordType = credentials.getPassword(); + if (expectedPassword == null && passwordType == null) { + return; + } + assertNotNull("Missing password credential in repo shadow "+shadow, passwordType); + ProtectedStringType protectedStringType = passwordType.getValue(); + assertNotNull("No password value in repo shadow "+shadow, protectedStringType); + assertProtectedString("Wrong password value in repo shadow "+shadow, expectedPassword, protectedStringType, CredentialsStorageTypeType.HASHING); + } } 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 7c7be28701f..27aff6de607 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 @@ -1839,6 +1839,94 @@ public void test260AutzJackLimitedRoleAdministrator() throws Exception { display("Empty role with exclusion (2)", roleEmptyExclusion); AssignmentType exclusionAssignment = assertExclusion(roleEmptyExclusion, ROLE_PIRATE_OID); + assertAllow("delete exclusion (2)", + (task, result) -> modifyRoleDeleteAssignment(ROLE_EMPTY_OID, createAssignmentIdOnly(exclusionAssignment.getId()), task, result)); + + // TODO: add exclusion with metadata (should be denied) + + assertDeny("add minAssignee", + (task, result) -> modifyRolePolicyRule(ROLE_EMPTY_OID, createMinAssigneePolicyRule(1), true, task, result)); + + assertDeny("delete maxAssignee 10 (by value)", + (task, result) -> modifyRolePolicyRule(ROLE_MAXASSIGNEES_10_OID, createMaxAssigneePolicyRule(10), false, task, result)); + + assertDeny("delete maxAssignee 10 (by id)", + (task, result) -> modifyRoleDeleteAssignment(ROLE_MAXASSIGNEES_10_OID, createAssignmentIdOnly(10L), task, result)); + + assertDeny("assign role pirate to empty role", + (task, result) -> assignRole(RoleType.class, ROLE_EMPTY_OID, ROLE_PIRATE_OID, task, result)); + + roleEmptyExclusion = assertGetAllow(RoleType.class, ROLE_EMPTY_OID); + display("Empty role without exclusion", roleEmptyExclusion); + assertAssignments(roleEmptyExclusion, 0); + + assertGlobalStateUntouched(); + } + + /** + * MID-4399 + */ + @Test + public void test262AutzJackLimitedRoleAdministratorAndAssignApplicationRoles() throws Exception { + final String TEST_NAME = "test260AutzJackLimitedRoleAdministrator"; + displayTestTitle(TEST_NAME); + // GIVEN + cleanupAutzTest(USER_JACK_OID); + assignRole(USER_JACK_OID, ROLE_LIMITED_ROLE_ADMINISTRATOR_OID); + assignRole(USER_JACK_OID, ROLE_ASSIGN_APPLICATION_ROLES_OID); + login(USER_JACK_USERNAME); + + // WHEN + displayWhen(TEST_NAME); + + assertReadAllow(); + assertReadDenyRaw(); + assertAddDeny(); + assertDeleteDeny(); + + // check ROLE_ASSIGN_APPLICATION_ROLES_OID authorizations + + assertAllow("assign application role to jack", + (task, result) -> assignRole(USER_JACK_OID, ROLE_APPLICATION_1_OID, task, result) + ); + + PrismObject user = getUser(USER_JACK_OID); + assertAssignments(user, 3); + assertAssignedRole(user, ROLE_APPLICATION_1_OID); + + assertDeny("assign business role to jack", + (task, result) -> assignRole(USER_JACK_OID, ROLE_BUSINESS_1_OID, task, result)); + + assertAllow("unassign application role from jack", + (task, result) -> unassignRole(USER_JACK_OID, ROLE_APPLICATION_1_OID, task, result) + ); + + // check ROLE_LIMITED_ROLE_ADMINISTRATOR_OID authorizations + + assertAddAllow(ROLE_EXCLUSION_PIRATE_FILE); + + PrismObject roleExclusion = assertGetAllow(RoleType.class, ROLE_EXCLUSION_PIRATE_OID); + display("Exclusion role", roleExclusion); + assertExclusion(roleExclusion, ROLE_PIRATE_OID); +// display("Exclusion role def", roleExclusion.getDefinition()); + + assertAllow("add exclusion (1)", + (task, result) -> modifyRoleAddExclusion(ROLE_EMPTY_OID, ROLE_PIRATE_OID, task, result)); + + PrismObject roleEmptyExclusion = assertGetAllow(RoleType.class, ROLE_EMPTY_OID); + display("Empty role with exclusion (1)", roleEmptyExclusion); + assertExclusion(roleEmptyExclusion, ROLE_PIRATE_OID); + + assertAllow("delete exclusion (1)", + (task, result) -> modifyRoleDeleteExclusion(ROLE_EMPTY_OID, ROLE_PIRATE_OID, task, result)); + + assertAllow("add exclusion (2)", + (task, result) -> modifyRoleAddExclusion(ROLE_EMPTY_OID, ROLE_PIRATE_OID, task, result)); + + roleEmptyExclusion = assertGetAllow(RoleType.class, ROLE_EMPTY_OID); + display("Empty role with exclusion (2)", roleEmptyExclusion); + AssignmentType exclusionAssignment = assertExclusion(roleEmptyExclusion, ROLE_PIRATE_OID); + display("TTTA1"); assertAllow("delete exclusion (2)", (task, result) -> modifyRoleDeleteAssignment(ROLE_EMPTY_OID, createAssignmentIdOnly(exclusionAssignment.getId()), task, result)); @@ -1864,7 +1952,7 @@ public void test260AutzJackLimitedRoleAdministrator() throws Exception { assertGlobalStateUntouched(); } - + @Test public void test270AutzJackModifyPolicyException() throws Exception { final String TEST_NAME = "test270AutzJackModifyPolicyException"; @@ -2084,4 +2172,19 @@ private void assertDeputySearchAssignmentTarget(String delegatorOid, String... e assertSearch(UserType.class, query, expectedDeputyOids); } + + @Override + protected void cleanupAutzTest(String userOid) throws ObjectNotFoundException, SchemaException, + ExpressionEvaluationException, CommunicationException, ConfigurationException, + ObjectAlreadyExistsException, PolicyViolationException, SecurityViolationException, IOException { + super.cleanupAutzTest(userOid); + + Task task = taskManager.createTaskInstance(TestSecurityAdvanced.class.getName() + ".cleanupAutzTest"); + OperationResult result = task.getResult(); + + cleanupDelete(RoleType.class, ROLE_EXCLUSION_PIRATE_OID, task, result); + } + + + } 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 859e35fa82c..9b44466866f 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 @@ -1956,8 +1956,9 @@ public void test275aAutzJackAssignRequestableRoles() throws Exception { /** * MID-3636 partially + * MID-4399 */ - @Test(enabled=false) + @Test public void test275bAutzJackAssignRequestableOrgs() throws Exception { final String TEST_NAME = "test275bAutzJackAssignRequestableOrgs"; displayTestTitle(TEST_NAME); @@ -1987,7 +1988,7 @@ public void test275bAutzJackAssignRequestableOrgs() throws Exception { ObjectQuery query = new ObjectQuery(); query.addFilter(spec.getFilter()); - assertSearch(AbstractRoleType.class, query, 6); // set to 6 with requestable org + assertSearch(AbstractRoleType.class, query, 9); assertAllow("unassign business role from jack", (task, result) -> unassignOrg(USER_JACK_OID, ORG_REQUESTABLE_OID, task, result)); diff --git a/model/model-intest/src/test/resources/password/password-policy-maverick.xml b/model/model-intest/src/test/resources/password/password-policy-maverick.xml new file mode 100644 index 00000000000..774cf12b7b4 --- /dev/null +++ b/model/model-intest/src/test/resources/password/password-policy-maverick.xml @@ -0,0 +1,59 @@ + + + + + + + Password Policy for Maverick resource + + Testing string policy + + 1 + 30 + 2 + + Alphas + 1 + false + + abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ + + + + Numbers + 1 + false + + 1234567890 + + + + + + + projection + credentials/password/value + + + account + + + + \ No newline at end of file diff --git a/model/model-intest/src/test/resources/password/resource-dummy-maverick.xml b/model/model-intest/src/test/resources/password/resource-dummy-maverick.xml new file mode 100644 index 00000000000..bc097947581 --- /dev/null +++ b/model/model-intest/src/test/resources/password/resource-dummy-maverick.xml @@ -0,0 +1,266 @@ + + + + + + + + Dummy Resource: Maverick + + + + + connectorType + com.evolveum.icf.dummy.connector.DummyConnector + + + connectorVersion + 2.0 + + + + + + + + maverick + true + + whatever + + USEless + + + + false + false + false + + + + + http://midpoint.evolveum.com/xml/ns/public/resource/instance/10000000-0000-0000-0000-000000000004 + + + + account + default + Default Account + true + ri:AccountObjectClass + + icfs:name + Username + + strong + + $user/name + + + + + + + + ri:fullname + Full Name + + + $user/fullName + + + + + ri:title + true + + + ri:location + Location + + strong + + + $user/locality + + + + + + ri:ship + Ship + + + ri:loot + Loot + explicit + + http://pirates.net/avast + + + + + + + ri:weapon + Weapon + + weak + + extension/weapon + + + + + ri:drink + Drink + false + + strong + + rum + + + + + ri:quote + Quote + true + + strong + + Arr! + + + + + ri:wealth + Wealth + false + + + ri:gossip + Gossip + true + + + ri:water + + true + + + + + daviejones + + + calypso + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + true + + + + + ri:AccountObjectClass + account + default + true + + + name + + declare namespace icfs="http://midpoint.evolveum.com/xml/ns/public/connector/icf-1/resource-schema-3"; + $account/attributes/icfs:name + + + + + linked + true + + + deleted + true + + http://midpoint.evolveum.com/xml/ns/public/model/action-3#unlink + + + + unlinked + true + + http://midpoint.evolveum.com/xml/ns/public/model/action-3#link + + + + unmatched + true + + http://midpoint.evolveum.com/xml/ns/public/model/action-3#addFocus + + + + + + + diff --git a/model/model-intest/src/test/resources/password/resource-dummy-souvenir.xml b/model/model-intest/src/test/resources/password/resource-dummy-souvenir.xml new file mode 100644 index 00000000000..0b9a2e013d5 --- /dev/null +++ b/model/model-intest/src/test/resources/password/resource-dummy-souvenir.xml @@ -0,0 +1,269 @@ + + + + + + + + Dummy Resource: Souvenir + + + + + connectorType + com.evolveum.icf.dummy.connector.DummyConnector + + + connectorVersion + 2.0 + + + + + + + + souvenir + true + + whatever + + USEless + true + + + + false + false + false + + + + + http://midpoint.evolveum.com/xml/ns/public/resource/instance/10000000-0000-0000-0000-000000000004 + + + + account + default + Default Account + true + ri:AccountObjectClass + + icfs:name + Username + + strong + + $user/name + + + + + + + + ri:fullname + Full Name + + + $user/fullName + + + + + ri:title + true + + + ri:location + Location + + strong + + + $user/locality + + + + + + ri:ship + Ship + + + ri:loot + Loot + explicit + + http://pirates.net/avast + + + + + + + ri:weapon + Weapon + + weak + + extension/weapon + + + + + ri:drink + Drink + false + + strong + + rum + + + + + ri:quote + Quote + true + + strong + + Arr! + + + + + ri:wealth + Wealth + false + + + ri:gossip + Gossip + true + + + ri:water + + true + + + + + daviejones + + + calypso + + + + + + + + + + + + + + cached + + + + + + + passive + + + + + + + + + + true + true + + + + + ri:AccountObjectClass + account + default + true + + + name + + declare namespace icfs="http://midpoint.evolveum.com/xml/ns/public/connector/icf-1/resource-schema-3"; + $account/attributes/icfs:name + + + + + linked + true + + + deleted + true + + http://midpoint.evolveum.com/xml/ns/public/model/action-3#unlink + + + + unlinked + true + + http://midpoint.evolveum.com/xml/ns/public/model/action-3#link + + + + unmatched + true + + http://midpoint.evolveum.com/xml/ns/public/model/action-3#addFocus + + + + + + + diff --git a/model/model-intest/src/test/resources/security/role-modify-user.xml b/model/model-intest/src/test/resources/security/role-modify-user.xml index e863315b762..6bdee385d2c 100644 --- a/model/model-intest/src/test/resources/security/role-modify-user.xml +++ b/model/model-intest/src/test/resources/security/role-modify-user.xml @@ -1,6 +1,6 @@ http://midpoint.evolveum.com/xml/ns/public/security/authorization-3#users diff --git a/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/impl/ShadowCache.java b/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/impl/ShadowCache.java index afca93fedbd..0a8591beb70 100644 --- a/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/impl/ShadowCache.java +++ b/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/impl/ShadowCache.java @@ -584,6 +584,9 @@ public String addShadow(PrismObject shadowToAdd, OperationProvisioni accessChecker.checkAdd(ctx, shadowToAdd, parentResult); if (shouldExecuteResourceOperationDirectly(ctx)) { + + LOGGER.trace("ADD {}: resource operation, execution starting", shadowToAdd); + try { // RESOURCE OPERATION: add @@ -603,7 +606,7 @@ public String addShadow(PrismObject shadowToAdd, OperationProvisioni return addedShadow.getOid(); } - LOGGER.debug("Resource operation executed (ADD {}), operation state: {}", shadowToAdd, opState.shortDumpLazily()); + LOGGER.debug("ADD {}: resource operation executed, operation state: {}", shadowToAdd, opState.shortDumpLazily()); } else { opState.setExecutionStatus(PendingOperationExecutionStatusType.EXECUTION_PENDING); @@ -611,7 +614,7 @@ public String addShadow(PrismObject shadowToAdd, OperationProvisioni // This will force the entire result (parent) to be IN_PROGRESS rather than SUCCESS. OperationResult delayedSubresult = parentResult.createSubresult(OP_DELAYED_OPERATION); delayedSubresult.setStatus(OperationResultStatus.IN_PROGRESS); - LOGGER.debug("Resource operation NOT executed, execution pending (ADD {})", shadowToAdd); + LOGGER.debug("ADD {}: resource operation NOT executed, execution pending", shadowToAdd); } // REPO OPERATION: add @@ -642,7 +645,7 @@ private ProvisioningOperationState> asyncReturnValue = resouceObjectConverter.addResourceObject(ctx, shadowToAdd, scripts, parentResult); opState.processAsyncResult(asyncReturnValue); - LOGGER.debug("Resource operation executed (ADD {}), operation state: {}", shadowToAdd, opState.shortDumpLazily()); + LOGGER.debug("ADD {}: resource operation executed, operation state: {}", shadowToAdd, opState.shortDumpLazily()); return opState; } catch (Exception ex) { @@ -798,16 +801,16 @@ public String modifyShadow(PrismObject repoShadow, if (shadowManager.isRepositoryOnlyModification(modifications)) { opState.setExecutionStatus(PendingOperationExecutionStatusType.COMPLETED); - LOGGER.debug("Repository-only modification (MODIFY {})", repoShadow); + LOGGER.debug("MODIFY {}: repository-only modification", repoShadow); } else { if (shouldExecuteResourceOperationDirectly(ctx)) { + if (LOGGER.isTraceEnabled()) { + LOGGER.trace("MODIFY {}: resource modification, execution starting\n{}", repoShadow, DebugUtil.debugDump(modifications)); + } + try { - - if (LOGGER.isTraceEnabled()) { - LOGGER.trace("Applying change: {}", DebugUtil.debugDump(modifications)); - } - + AsynchronousOperationReturnValue>> asyncReturnValue = resouceObjectConverter.modifyResourceObject(ctx, repoShadow, scripts, modifications, parentResult); opState.processAsyncResult(asyncReturnValue); @@ -835,13 +838,15 @@ public String modifyShadow(PrismObject repoShadow, return repoShadow.getOid(); } + LOGGER.debug("MODIFY {}: resource operation executed, operation state: {}", repoShadow, opState.shortDumpLazily()); + } else { opState.setExecutionStatus(PendingOperationExecutionStatusType.EXECUTION_PENDING); // Create dummy subresult with IN_PROGRESS state. // This will force the entire result (parent) to be IN_PROGRESS rather than SUCCESS. OperationResult delayedSubresult = parentResult.createSubresult(OP_DELAYED_OPERATION); delayedSubresult.setStatus(OperationResultStatus.IN_PROGRESS); - LOGGER.debug("Resource operation NOT executed, execution pending (MODIFY {})", repoShadow); + LOGGER.debug("MODIFY {}: Resource operation NOT executed, execution pending", repoShadow); } } @@ -982,6 +987,9 @@ public void deleteShadow(PrismObject shadow, ProvisioningOperationOp if (shadow.asObjectable().getFailedOperationType() == null || (shadow.asObjectable().getFailedOperationType() != null && FailedOperationTypeType.ADD != shadow.asObjectable().getFailedOperationType())) { + + LOGGER.trace("DELETE {}: resource deletion, execution starting", shadow); + try { AsynchronousOperationResult asyncReturnValue = resouceObjectConverter.deleteResourceObject(ctx, shadow, scripts, parentResult); @@ -997,6 +1005,11 @@ public void deleteShadow(PrismObject shadow, ProvisioningOperationOp } return; } + + LOGGER.debug("DELETE {}: resource operation executed, operation state: {}", shadow, opState.shortDumpLazily()); + + } else { + LOGGER.trace("DELETE {}: resource deletion skipped (failed ADD operation)", shadow); } } else { @@ -1005,7 +1018,7 @@ public void deleteShadow(PrismObject shadow, ProvisioningOperationOp // This will force the entire result (parent) to be IN_PROGRESS rather than SUCCESS. OperationResult delayedSubresult = parentResult.createSubresult(OP_DELAYED_OPERATION); delayedSubresult.setStatus(OperationResultStatus.IN_PROGRESS); - LOGGER.debug("Resource operation NOT executed, execution pending (DELETE {})", shadow); + LOGGER.debug("DELETE {}: resource operation NOT executed, execution pending", shadow); } LOGGER.trace("Deting object with oid {} form repository.", shadow.getOid()); diff --git a/provisioning/provisioning-impl/src/test/java/com/evolveum/midpoint/provisioning/impl/dummy/TestDummyCaching.java b/provisioning/provisioning-impl/src/test/java/com/evolveum/midpoint/provisioning/impl/dummy/TestDummyCaching.java index 9c93d244238..e5b2bc09dde 100644 --- a/provisioning/provisioning-impl/src/test/java/com/evolveum/midpoint/provisioning/impl/dummy/TestDummyCaching.java +++ b/provisioning/provisioning-impl/src/test/java/com/evolveum/midpoint/provisioning/impl/dummy/TestDummyCaching.java @@ -186,15 +186,13 @@ public void test107BGetModifiedAccountFromCacheHighStaleness() throws Exception XMLGregorianCalendar startTs = clock.currentTimeXMLGregorianCalendar(); // WHEN - TestUtil.displayWhen(TEST_NAME); + displayWhen(TEST_NAME); PrismObject shadow = provisioningService.getObject(ShadowType.class, ACCOUNT_WILL_OID, options, null, result); // THEN - TestUtil.displayThen(TEST_NAME); - result.computeStatus(); - display("getObject result", result); - TestUtil.assertSuccess(result); + displayThen(TEST_NAME); + assertSuccess(result); assertCounterIncrement(InternalCounters.SHADOW_FETCH_OPERATION_COUNT, 0); diff --git a/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/AbstractIntegrationTest.java b/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/AbstractIntegrationTest.java index 1409c91b0a2..81150fa2f26 100644 --- a/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/AbstractIntegrationTest.java +++ b/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/AbstractIntegrationTest.java @@ -2293,4 +2293,11 @@ protected ObjectDelta createAccountPaswordDelta(String shadowOid, St userPasswordPs.setClearValue(newPassword); return ObjectDelta.createModificationReplaceProperty(ShadowType.class, shadowOid, SchemaConstants.PATH_PASSWORD_VALUE, prismContext, userPasswordPs); } + + protected PrismObject getShadowRepo(String shadowOid) throws ObjectNotFoundException, SchemaException { + OperationResult result = new OperationResult("getShadowRepo"); + PrismObject shadow = repositoryService.getObject(ShadowType.class, shadowOid, null, result); + assertSuccess(result); + return shadow; + } }