/*
* Copyright (c) 2010-2016 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.web.page.admin;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
import javax.xml.namespace.QName;
import com.evolveum.midpoint.prism.*;
import com.evolveum.midpoint.schema.constants.ObjectTypes;
import com.evolveum.midpoint.schema.constants.SchemaConstants;
import com.evolveum.midpoint.xml.ns._public.common.common_3.*;
import net.sf.saxon.functions.Abs;
import org.apache.commons.lang.StringUtils;
import org.apache.wicket.Session;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.protocol.http.WebSession;
import com.evolveum.midpoint.gui.api.model.LoadableModel;
import com.evolveum.midpoint.gui.api.util.WebComponentUtil;
import com.evolveum.midpoint.gui.api.util.WebModelServiceUtils;
import com.evolveum.midpoint.model.api.ModelExecuteOptions;
import com.evolveum.midpoint.model.api.context.EvaluatedAssignmentTarget;
import com.evolveum.midpoint.model.api.context.EvaluatedAssignment;
import com.evolveum.midpoint.model.api.context.EvaluatedConstruction;
import com.evolveum.midpoint.model.api.context.ModelContext;
import com.evolveum.midpoint.prism.delta.ContainerDelta;
import com.evolveum.midpoint.prism.delta.DeltaSetTriple;
import com.evolveum.midpoint.prism.delta.ItemDelta;
import com.evolveum.midpoint.prism.delta.ObjectDelta;
import com.evolveum.midpoint.prism.delta.ReferenceDelta;
import com.evolveum.midpoint.prism.path.ItemPath;
import com.evolveum.midpoint.prism.polystring.PolyString;
import com.evolveum.midpoint.prism.schema.SchemaRegistry;
import com.evolveum.midpoint.schema.GetOperationOptions;
import com.evolveum.midpoint.schema.SelectorOptions;
import com.evolveum.midpoint.schema.result.OperationResult;
import com.evolveum.midpoint.security.api.AuthorizationConstants;
import com.evolveum.midpoint.task.api.Task;
import com.evolveum.midpoint.util.exception.CommunicationException;
import com.evolveum.midpoint.util.exception.ConfigurationException;
import com.evolveum.midpoint.util.exception.NoFocusNameSchemaException;
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.logging.LoggingUtils;
import com.evolveum.midpoint.util.logging.Trace;
import com.evolveum.midpoint.util.logging.TraceManager;
import com.evolveum.midpoint.web.component.assignment.AssignmentEditorDto;
import com.evolveum.midpoint.web.component.prism.ContainerStatus;
import com.evolveum.midpoint.web.component.prism.ObjectWrapper;
import com.evolveum.midpoint.web.component.prism.show.PagePreviewChanges;
import com.evolveum.midpoint.web.component.progress.ProgressReportingAwarePage;
import com.evolveum.midpoint.web.component.util.ObjectWrapperUtil;
import com.evolveum.midpoint.web.page.admin.users.component.AssignmentsPreviewDto;
import com.evolveum.midpoint.web.page.admin.users.dto.FocusSubwrapperDto;
import com.evolveum.midpoint.web.page.admin.users.dto.UserDtoStatus;
import com.evolveum.midpoint.web.security.SecurityUtils;
import com.evolveum.midpoint.web.util.validation.MidpointFormValidator;
import com.evolveum.midpoint.web.util.validation.SimpleValidationError;
public abstract class PageAdminFocus<F extends FocusType> extends PageAdminObjectDetails<F>
implements ProgressReportingAwarePage {
private static final long serialVersionUID = 1L;
public static final String AUTH_USERS_ALL = AuthorizationConstants.AUTZ_UI_USERS_ALL_URL;
public static final String AUTH_USERS_ALL_LABEL = "PageAdminUsers.auth.usersAll.label";
public static final String AUTH_USERS_ALL_DESCRIPTION = "PageAdminUsers.auth.usersAll.description";
public static final String AUTH_ORG_ALL = AuthorizationConstants.AUTZ_UI_ORG_ALL_URL;
public static final String AUTH_ORG_ALL_LABEL = "PageAdminUsers.auth.orgAll.label";
public static final String AUTH_ORG_ALL_DESCRIPTION = "PageAdminUsers.auth.orgAll.description";
private LoadableModel<List<FocusSubwrapperDto<ShadowType>>> projectionModel;
private LoadableModel<List<AssignmentEditorDto>> assignmentsModel;
private LoadableModel<List<AssignmentEditorDto>> delegatedToMeModel;
private static final String DOT_CLASS = PageAdminFocus.class.getName() + ".";
private static final String OPERATION_RECOMPUTE_ASSIGNMENTS = DOT_CLASS + "recomputeAssignments";
private static final String OPERATION_LOAD_SHADOW = DOT_CLASS + "loadShadow";
private static final Trace LOGGER = TraceManager.getTrace(PageAdminFocus.class);
@Override
protected void initializeModel(final PrismObject<F> objectToEdit) {
super.initializeModel(objectToEdit);
projectionModel = new LoadableModel<List<FocusSubwrapperDto<ShadowType>>>(false) {
private static final long serialVersionUID = 1L;
@Override
protected List<FocusSubwrapperDto<ShadowType>> load() {
return loadShadowWrappers();
}
};
assignmentsModel = new LoadableModel<List<AssignmentEditorDto>>(false) {
private static final long serialVersionUID = 1L;
@Override
protected List<AssignmentEditorDto> load() {
return loadAssignments();
}
};
delegatedToMeModel= new LoadableModel<List<AssignmentEditorDto>>(false) {
@Override
protected List<AssignmentEditorDto> load() {
return loadDelegatedToMe();
}
};
}
public LoadableModel<List<FocusSubwrapperDto<ShadowType>>> getProjectionModel() {
return projectionModel;
}
public LoadableModel<List<AssignmentEditorDto>> getAssignmentsModel() {
return assignmentsModel;
}
public LoadableModel<List<AssignmentEditorDto>> getDelegatedToMeModel() {
return delegatedToMeModel;
}
public List<FocusSubwrapperDto<ShadowType>> getFocusShadows() {
return projectionModel.getObject();
}
public List<AssignmentEditorDto> getFocusAssignments() {
return assignmentsModel.getObject();
}
protected void reviveModels() throws SchemaException {
super.reviveModels();
WebComponentUtil.revive(projectionModel, getPrismContext());
WebComponentUtil.revive(assignmentsModel, getPrismContext());
}
protected ObjectWrapper<F> loadFocusWrapper(PrismObject<F> userToEdit) {
ObjectWrapper<F> objectWrapper = super.loadObjectWrapper(userToEdit);
return objectWrapper;
}
@Override
public void finishProcessing(AjaxRequestTarget target, OperationResult result, boolean returningFromAsync) {
if (previewRequested) {
finishPreviewProcessing(target, result);
return;
}
if (result.isSuccess() && getDelta() != null && getDelta().getOid().equals(SecurityUtils.getPrincipalUser().getOid())) {
UserType user = null;
if (getObjectWrapper().getObject().asObjectable() instanceof UserType){
user = (UserType) getObjectWrapper().getObject().asObjectable();
}
Session.get().setLocale(WebModelServiceUtils.getLocale(user));
LOGGER.debug("Using {} as locale", getLocale());
WebSession.get().getClientInfo().getProperties().
setTimeZone(WebModelServiceUtils.getTimezone(user));
LOGGER.debug("Using {} as time zone", WebSession.get().getClientInfo().getProperties().getTimeZone());
}
boolean focusAddAttempted = getDelta() != null && getDelta().isAdd();
boolean focusAddSucceeded = focusAddAttempted && StringUtils.isNotEmpty(getDelta().getOid());
// we don't want to allow resuming editing if a new focal object was created (on second 'save' there would be a conflict with itself)
// and also in case of partial errors, like those related to projections (many deltas would be already executed, and this could cause problems on second 'save').
boolean canContinueEditing = !focusAddSucceeded && result.isFatalError();
boolean canExitPage;
if (returningFromAsync) {
canExitPage = getProgressReporter().isAllSuccess(); // if there's at least a warning in the progress table, we would like to keep the table open
} else {
canExitPage = !canContinueEditing; // no point in staying on page if we cannot continue editing (in synchronous case i.e. no progress table present)
}
if (!isKeepDisplayingResults() && canExitPage) {
showResult(result);
redirectBack();
} else {
if (returningFromAsync) {
getProgressReporter().showBackButton(target);
getProgressReporter().hideAbortButton(target);
}
showResult(result);
target.add(getFeedbackPanel());
if (canContinueEditing) {
getProgressReporter().hideBackButton(target);
getProgressReporter().showContinueEditingButton(target);
}
}
}
private void finishPreviewProcessing(AjaxRequestTarget target, OperationResult result) {
getMainPanel().setVisible(true);
getProgressReporter().getProgressPanel().hide();
getProgressReporter().hideAbortButton(target);
getProgressReporter().hideBackButton(target);
getProgressReporter().hideContinueEditingButton(target);
showResult(result);
target.add(getFeedbackPanel());
navigateToNext(new PagePreviewChanges(getProgressReporter().getPreviewResult(), getModelInteractionService()));
}
@Override
public void continueEditing(AjaxRequestTarget target) {
getMainPanel().setVisible(true);
getProgressReporter().getProgressPanel().hide();
getProgressReporter().hideAbortButton(target);
getProgressReporter().hideBackButton(target);
getProgressReporter().hideContinueEditingButton(target);
target.add(this);
}
private List<FocusSubwrapperDto<ShadowType>> loadShadowWrappers() {
// Load the projects with noFetch by default. Only load the full projection on-denand.
// The full projection load happens when loadFullShadow() is explicitly invoked.
return loadSubwrappers(ShadowType.class, UserType.F_LINK_REF, true);
}
public void loadFullShadow(FocusSubwrapperDto<ShadowType> shadowWrapperDto) {
ObjectWrapper<ShadowType> shadowWrapperOld = shadowWrapperDto.getObject();
Task task = createSimpleTask(OPERATION_LOAD_SHADOW);
FocusSubwrapperDto<ShadowType> shadowWrapperDtoNew = loadSubWrapperDto(ShadowType.class, shadowWrapperOld.getObject().getOid(), false, task);
if (shadowWrapperDtoNew == null) {
// No access or error. The status is in the last subresult of task result. TODO: pass the result explicitly to loadSubWrapperDto
OperationResult subresult = task.getResult().getLastSubresult();
shadowWrapperDto.getObject().setFetchResult(subresult);
return;
}
ObjectWrapper<ShadowType> shadowWrapperNew = shadowWrapperDtoNew.getObject();
shadowWrapperOld.copyRuntimeStateTo(shadowWrapperNew);
shadowWrapperDto.setObject(shadowWrapperNew);
}
@Override
protected List<FocusSubwrapperDto<OrgType>> loadOrgWrappers() {
return loadSubwrappers(OrgType.class, UserType.F_PARENT_ORG_REF, false);
}
private <S extends ObjectType> List<FocusSubwrapperDto<S>> loadSubwrappers(Class<S> type,
QName propertyToLoad, boolean noFetch) {
List<FocusSubwrapperDto<S>> list = new ArrayList<>();
ObjectWrapper<F> focusWrapper = getObjectModel().getObject();
PrismObject<F> focus = focusWrapper.getObject();
PrismReference prismReference = focus.findReference(new ItemPath(propertyToLoad));
if (prismReference == null) {
return new ArrayList<>();
}
List<PrismReferenceValue> references = prismReference.getValues();
Task task = createSimpleTask(OPERATION_LOAD_SHADOW);
for (PrismReferenceValue reference : references) {
FocusSubwrapperDto<S> subWrapper = loadSubWrapperDto(type, reference.getOid(), noFetch, task);
if (subWrapper != null) {
list.add(subWrapper);
}
}
return list;
}
private <S extends ObjectType> FocusSubwrapperDto<S> loadSubWrapperDto(Class<S> type, String oid, boolean noFetch, Task task) {
if (oid == null) {
return null;
}
OperationResult subResult = task.getResult().createMinorSubresult(OPERATION_LOAD_SHADOW);
String resourceName = null;
try {
Collection<SelectorOptions<GetOperationOptions>> loadOptions = null;
if (ShadowType.class.equals(type)) {
GetOperationOptions resourceOption = GetOperationOptions.createResolve();
resourceOption.setReadOnly(true);
loadOptions = SelectorOptions.createCollection(ShadowType.F_RESOURCE,
resourceOption);
}
if (noFetch) {
GetOperationOptions rootOptions = SelectorOptions.findRootOptions(loadOptions);
if (rootOptions == null) {
loadOptions.add(new SelectorOptions<GetOperationOptions>(GetOperationOptions.createNoFetch()));
} else {
rootOptions.setNoFetch(true);
}
}
PrismObject<S> projection = WebModelServiceUtils.loadObject(type, oid, loadOptions, this,
task, subResult);
if (LOGGER.isTraceEnabled()) {
LOGGER.trace("Loaded projection {} ({}):\n{}", oid, loadOptions, projection==null?null:projection.debugDump());
}
if (projection == null) {
// No access or error
return null;
}
S projectionType = projection.asObjectable();
OperationResultType fetchResult = projectionType.getFetchResult();
StringBuilder description = new StringBuilder();
if (ShadowType.class.equals(type)) {
ShadowType shadowType = (ShadowType) projectionType;
ResourceType resource = shadowType.getResource();
resourceName = WebComponentUtil.getName(resource);
if (shadowType.getIntent() != null) {
description.append(shadowType.getIntent()).append(", ");
}
} else if (OrgType.class.equals(type)) {
OrgType orgType = (OrgType) projectionType;
resourceName = orgType.getDisplayName() != null
? WebComponentUtil.getOrigStringFromPoly(orgType.getDisplayName()) : "";
}
description.append(WebComponentUtil.getOrigStringFromPoly(projectionType.getName()));
ObjectWrapper<S> wrapper = ObjectWrapperUtil.createObjectWrapper(resourceName,
description.toString(), projection, ContainerStatus.MODIFYING, true, this);
wrapper.setLoadOptions(loadOptions);
wrapper.setFetchResult(OperationResult.createOperationResult(fetchResult));
wrapper.setSelectable(true);
wrapper.setMinimalized(true);
wrapper.initializeContainers(this);
subResult.computeStatus();
return new FocusSubwrapperDto<S>(wrapper, UserDtoStatus.MODIFY);
} catch (Exception ex) {
subResult.recordFatalError("Couldn't load account." + ex.getMessage(), ex);
LoggingUtils.logUnexpectedException(LOGGER, "Couldn't load account", ex);
subResult.computeStatus();
return new FocusSubwrapperDto<S>(false, resourceName, subResult);
}
}
private List<AssignmentEditorDto> loadDelegatedToMe() {
List<AssignmentEditorDto> list = new ArrayList<AssignmentEditorDto>();
ObjectWrapper<F> focusWrapper = getObjectModel().getObject();
PrismObject<F> focus = focusWrapper.getObject();
List<AssignmentType> assignments = focus.asObjectable().getAssignment();
for (AssignmentType assignment : assignments) {
if (assignment.getTargetRef() != null &&
UserType.COMPLEX_TYPE.equals(assignment.getTargetRef().getType())) {
AssignmentEditorDto dto = new AssignmentEditorDto(UserDtoStatus.MODIFY, assignment, this);
dto.setSimpleView(true);
dto.setEditable(false);
list.add(dto);
}
}
Collections.sort(list);
return list;
}
private List<AssignmentEditorDto> loadAssignments() {
List<AssignmentEditorDto> list = new ArrayList<AssignmentEditorDto>();
ObjectWrapper<F> focusWrapper = getObjectModel().getObject();
PrismObject<F> focus = focusWrapper.getObject();
List<AssignmentType> assignments = focus.asObjectable().getAssignment();
for (AssignmentType assignment : assignments) {
if (assignment.getTargetRef() == null ||
!UserType.COMPLEX_TYPE.equals(assignment.getTargetRef().getType())) {
list.add(new AssignmentEditorDto(StringUtils.isEmpty(focusWrapper.getOid()) ?
UserDtoStatus.ADD : UserDtoStatus.MODIFY, assignment, this));
}
}
Collections.sort(list);
return list;
}
@Override
protected void prepareObjectForAdd(PrismObject<F> focus) throws SchemaException {
super.prepareObjectForAdd(focus);
F focusType = focus.asObjectable();
// handle added accounts
List<ShadowType> shadowsToAdd = prepareSubobject(getFocusShadows());
if (!shadowsToAdd.isEmpty()) {
shadowsToAdd.forEach(shadowType -> addDefaultKindAndIntent(shadowType.asPrismObject()));
focusType.getLink().addAll(shadowsToAdd);
}
List<OrgType> orgsToAdd = prepareSubobject(getParentOrgs());
if (!orgsToAdd.isEmpty()){
focusType.getParentOrg().addAll(orgsToAdd);
}
handleAssignmentForAdd(focus, UserType.F_ASSIGNMENT, assignmentsModel.getObject());
}
protected void handleAssignmentForAdd(PrismObject<F> focus, QName containerName,
List<AssignmentEditorDto> assignments) throws SchemaException {
PrismObjectDefinition<F> userDef = focus.getDefinition();
PrismContainerDefinition<AssignmentType> assignmentDef = userDef.findContainerDefinition(containerName);
// handle added assignments
// existing user assignments are not relevant -> delete them
PrismContainer<AssignmentType> assignmentContainer = focus.findOrCreateContainer(containerName);
if (assignmentContainer != null && !assignmentContainer.isEmpty()){
assignmentContainer.clear();
}
// List<AssignmentEditorDto> assignments = getFocusAssignments();
for (AssignmentEditorDto assDto : assignments) {
if (UserDtoStatus.DELETE.equals(assDto.getStatus())) {
continue;
}
AssignmentType assignment = new AssignmentType();
PrismContainerValue<AssignmentType> value = assDto.getNewValue(getPrismContext());
assignment.setupContainerValue(value);
value.applyDefinition(assignmentDef, false);
assignmentContainer.add(assignment.clone().asPrismContainerValue());
// todo remove this block [lazyman] after model is updated - it has
// to remove resource from accountConstruction
removeResourceFromAccConstruction(assignment);
}
}
@Override
protected void prepareObjectDeltaForModify(ObjectDelta<F> focusDelta) throws SchemaException {
super.prepareObjectDeltaForModify(focusDelta);
// handle accounts
SchemaRegistry registry = getPrismContext().getSchemaRegistry();
PrismObjectDefinition<F> objectDefinition = registry
.findObjectDefinitionByCompileTimeClass(getCompileTimeClass());
PrismReferenceDefinition refDef = objectDefinition.findReferenceDefinition(FocusType.F_LINK_REF);
ReferenceDelta refDelta = prepareUserAccountsDeltaForModify(refDef);
if (!refDelta.isEmpty()) {
focusDelta.addModification(refDelta);
}
refDef = objectDefinition.findReferenceDefinition(FocusType.F_PARENT_ORG_REF);
refDelta = prepareUserOrgsDeltaForModify(refDef);
if (!refDelta.isEmpty()) {
focusDelta.addModification(refDelta);
}
// handle assignments
PrismContainerDefinition def = objectDefinition.findContainerDefinition(UserType.F_ASSIGNMENT);
handleAssignmentDeltas(focusDelta, getFocusAssignments(), def);
}
protected ContainerDelta handleAssignmentDeltas(ObjectDelta<F> focusDelta,
List<AssignmentEditorDto> assignments, PrismContainerDefinition def) throws SchemaException {
return handleAssignmentDeltas(focusDelta, assignments, def, false);
}
protected ContainerDelta handleAssignmentDeltas(ObjectDelta<F> focusDelta,
List<AssignmentEditorDto> assignments, PrismContainerDefinition def,
boolean isDelegation) throws SchemaException {
ContainerDelta assDelta = new ContainerDelta(new ItemPath(), def.getName(), def, getPrismContext());
for (AssignmentEditorDto assDto : assignments) {
PrismContainerValue newValue = assDto.getNewValue(getPrismContext());
switch (assDto.getStatus()) {
case ADD:
newValue.applyDefinition(def, false);
assDelta.addValueToAdd(newValue.clone());
break;
case DELETE:
PrismContainerValue oldValue = assDto.getOldValue();
if (isDelegation){
oldValue.applyDefinition(def, false);
} else {
oldValue.applyDefinition(def);
}
assDelta.addValueToDelete(oldValue.clone());
break;
case MODIFY:
if (!assDto.isModified(getPrismContext())) {
LOGGER.trace("Assignment '{}' not modified.", new Object[] { assDto.getName() });
continue;
}
handleModifyAssignmentDelta(assDto, def, newValue, focusDelta);
break;
default:
warn(getString("pageAdminUser.message.illegalAssignmentState", assDto.getStatus()));
}
}
if (!assDelta.isEmpty()) {
assDelta = focusDelta.addModification(assDelta);
}
// todo remove this block [lazyman] after model is updated - it has to
// remove resource from accountConstruction
Collection<PrismContainerValue> values = assDelta.getValues(PrismContainerValue.class);
for (PrismContainerValue value : values) {
AssignmentType ass = new AssignmentType();
ass.setupContainerValue(value);
removeResourceFromAccConstruction(ass);
}
return assDelta;
}
private void handleModifyAssignmentDelta(AssignmentEditorDto assDto,
PrismContainerDefinition assignmentDef, PrismContainerValue newValue, ObjectDelta<F> focusDelta)
throws SchemaException {
LOGGER.debug("Handling modified assignment '{}', computing delta.",
new Object[] { assDto.getName() });
PrismValue oldValue = assDto.getOldValue();
Collection<? extends ItemDelta> deltas = oldValue.diff(newValue);
for (ItemDelta delta : deltas) {
ItemPath deltaPath = delta.getPath().rest();
ItemDefinition deltaDef = assignmentDef.findItemDefinition(deltaPath);
delta.setParentPath(WebComponentUtil.joinPath(oldValue.getPath(), delta.getPath().allExceptLast()));
delta.applyDefinition(deltaDef);
focusDelta.addModification(delta);
}
}
@Override
protected boolean executeForceDelete(ObjectWrapper userWrapper, Task task, ModelExecuteOptions options,
OperationResult parentResult) {
if (isForce()) {
OperationResult result = parentResult.createSubresult("Force delete operation");
try {
ObjectDelta<F> forceDeleteDelta = getForceDeleteDelta(userWrapper);
forceDeleteDelta.revive(getPrismContext());
if (forceDeleteDelta != null && !forceDeleteDelta.isEmpty()) {
getModelService().executeChanges(WebComponentUtil.createDeltaCollection(forceDeleteDelta),
options, task, result);
}
} catch (Exception ex) {
result.recordFatalError("Failed to execute delete operation with force.");
LoggingUtils.logUnexpectedException(LOGGER, "Failed to execute delete operation with force", ex);
return false;
}
result.recomputeStatus();
result.recordSuccessIfUnknown();
return true;
}
return false;
}
private ObjectDelta getForceDeleteDelta(ObjectWrapper focusWrapper) throws SchemaException {
List<FocusSubwrapperDto<ShadowType>> accountDtos = getFocusShadows();
List<ReferenceDelta> refDeltas = new ArrayList<ReferenceDelta>();
ObjectDelta<F> forceDeleteDelta = null;
for (FocusSubwrapperDto<ShadowType> accDto : accountDtos) {
if (!accDto.isLoadedOK()) {
continue;
}
if (accDto.getStatus() == UserDtoStatus.DELETE) {
ObjectWrapper accWrapper = accDto.getObject();
ReferenceDelta refDelta = ReferenceDelta.createModificationDelete(UserType.F_LINK_REF,
focusWrapper.getObject().getDefinition(), accWrapper.getObject());
refDeltas.add(refDelta);
} else if (accDto.getStatus() == UserDtoStatus.UNLINK) {
ObjectWrapper accWrapper = accDto.getObject();
ReferenceDelta refDelta = ReferenceDelta.createModificationDelete(UserType.F_LINK_REF,
focusWrapper.getObject().getDefinition(), accWrapper.getObject().getOid());
refDeltas.add(refDelta);
}
}
if (!refDeltas.isEmpty()) {
forceDeleteDelta = ObjectDelta.createModifyDelta(focusWrapper.getObject().getOid(), refDeltas,
getCompileTimeClass(), getPrismContext());
}
PrismContainerDefinition def = focusWrapper.getObject().findContainer(UserType.F_ASSIGNMENT)
.getDefinition();
if (forceDeleteDelta == null) {
forceDeleteDelta = ObjectDelta.createEmptyModifyDelta(getCompileTimeClass(),
focusWrapper.getObject().getOid(), getPrismContext());
}
handleAssignmentDeltas(forceDeleteDelta, getFocusAssignments(), def);
return forceDeleteDelta;
}
private <P extends ObjectType> List<P> prepareSubobject(List<FocusSubwrapperDto<P>> projections) throws SchemaException{
List<P> projectionsToAdd = new ArrayList<>();
for (FocusSubwrapperDto<P> projection : projections) {
if (!projection.isLoadedOK()) {
continue;
}
if (UserDtoStatus.MODIFY.equals(projection.getStatus())) {
// this is legal e.g. when child org is being create (one assignment comes pre-created)
// TODO do we need more specific checks here?
continue;
}
if (!UserDtoStatus.ADD.equals(projection.getStatus())) {
warn(getString("pageAdminFocus.message.illegalAccountState", projection.getStatus()));
continue;
}
ObjectWrapper<P> projectionWrapper = projection.getObject();
ObjectDelta<P> delta = projectionWrapper.getObjectDelta();
PrismObject<P> proj = delta.getObjectToAdd();
WebComponentUtil.encryptCredentials(proj, true, getMidpointApplication());
projectionsToAdd.add(proj.asObjectable());
}
return projectionsToAdd;
}
@Override
protected List<ObjectDelta<? extends ObjectType>> getAdditionalModifyDeltas(OperationResult result) {
return getShadowModifyDeltas(result);
}
private List<ObjectDelta<? extends ObjectType>> getShadowModifyDeltas(OperationResult result) {
List<ObjectDelta<? extends ObjectType>> deltas = new ArrayList<>();
List<FocusSubwrapperDto<ShadowType>> accounts = getFocusShadows();
OperationResult subResult = null;
for (FocusSubwrapperDto<ShadowType> account : accounts) {
if (!account.isLoadedOK()) {
continue;
}
try {
ObjectWrapper accountWrapper = account.getObject();
ObjectDelta delta = accountWrapper.getObjectDelta();
if (LOGGER.isTraceEnabled()) {
LOGGER.trace("Account delta computed from {} as:\n{}",
new Object[] { accountWrapper, delta.debugDump(3) });
}
if (!UserDtoStatus.MODIFY.equals(account.getStatus())) {
continue;
}
if (delta.isEmpty()
&& (accountWrapper.getOldDelta() == null || accountWrapper.getOldDelta().isEmpty())) {
continue;
}
if (accountWrapper.getOldDelta() != null) {
delta = ObjectDelta.summarize(delta, accountWrapper.getOldDelta());
}
// what is this???
// subResult = result.createSubresult(OPERATION_MODIFY_ACCOUNT);
WebComponentUtil.encryptCredentials(delta, true, getMidpointApplication());
if (LOGGER.isTraceEnabled()) {
LOGGER.trace("Modifying account:\n{}", new Object[] { delta.debugDump(3) });
}
deltas.add(delta);
// subResult.recordSuccess();
} catch (Exception ex) {
// if (subResult != null) {
result.recordFatalError("Couldn't compute account delta.", ex);
// }
LoggingUtils.logUnexpectedException(LOGGER, "Couldn't compute account delta", ex);
}
}
return deltas;
}
/**
* remove this method after model is updated - it has to remove resource
* from accountConstruction
*/
@Deprecated
private void removeResourceFromAccConstruction(AssignmentType assignment) {
ConstructionType accConstruction = assignment.getConstruction();
if (accConstruction == null || accConstruction.getResource() == null) {
return;
}
ObjectReferenceType ref = new ObjectReferenceType();
ref.setOid(assignment.getConstruction().getResource().getOid());
ref.setType(ResourceType.COMPLEX_TYPE);
assignment.getConstruction().setResourceRef(ref);
assignment.getConstruction().setResource(null);
}
private ReferenceDelta prepareUserAccountsDeltaForModify(PrismReferenceDefinition refDef)
throws SchemaException {
ReferenceDelta refDelta = new ReferenceDelta(refDef, getPrismContext());
List<FocusSubwrapperDto<ShadowType>> accounts = getFocusShadows();
for (FocusSubwrapperDto<ShadowType> accDto : accounts) {
if (accDto.isLoadedOK()) {
ObjectWrapper accountWrapper = accDto.getObject();
accountWrapper.revive(getPrismContext());
ObjectDelta delta = accountWrapper.getObjectDelta();
PrismReferenceValue refValue = new PrismReferenceValue(null, OriginType.USER_ACTION, null);
PrismObject<ShadowType> account;
switch (accDto.getStatus()) {
case ADD:
account = delta.getObjectToAdd();
addDefaultKindAndIntent(account);
WebComponentUtil.encryptCredentials(account, true, getMidpointApplication());
refValue.setObject(account);
refDelta.addValueToAdd(refValue);
break;
case DELETE:
account = accountWrapper.getObject();
refValue.setObject(account);
refDelta.addValueToDelete(refValue);
break;
case MODIFY:
// nothing to do, account modifications were applied
// before
continue;
case UNLINK:
refValue.setOid(delta.getOid());
refValue.setTargetType(ShadowType.COMPLEX_TYPE);
refDelta.addValueToDelete(refValue);
break;
default:
warn(getString("pageAdminFocus.message.illegalAccountState", accDto.getStatus()));
}
}
}
return refDelta;
}
private void addDefaultKindAndIntent(PrismObject<ShadowType> account) {
if (account.asObjectable().getKind() == null) {
account.asObjectable().setKind(ShadowKindType.ACCOUNT);
}
if (account.asObjectable().getIntent() == null) {
account.asObjectable().setIntent(SchemaConstants.INTENT_DEFAULT);
}
}
private ReferenceDelta prepareUserOrgsDeltaForModify(PrismReferenceDefinition refDef)
throws SchemaException {
ReferenceDelta refDelta = new ReferenceDelta(refDef, getPrismContext());
List<FocusSubwrapperDto<OrgType>> orgs = getParentOrgs();
for (FocusSubwrapperDto<OrgType> orgDto : orgs) {
if (orgDto.isLoadedOK()) {
ObjectWrapper<OrgType> orgWrapper = orgDto.getObject();
orgWrapper.revive(getPrismContext());
ObjectDelta<OrgType> delta = orgWrapper.getObjectDelta();
PrismReferenceValue refValue = new PrismReferenceValue(null, OriginType.USER_ACTION, null);
switch (orgDto.getStatus()) {
case ADD:
refValue.setOid(delta.getOid());
refValue.setTargetType(OrgType.COMPLEX_TYPE);
refDelta.addValueToAdd(refValue);
break;
case DELETE:
break;
case MODIFY:
break;
case UNLINK:
refValue.setOid(delta.getOid());
refValue.setTargetType(OrgType.COMPLEX_TYPE);
refDelta.addValueToDelete(refValue);
break;
default:
warn(getString("pageAdminFocus.message.illegalAccountState", orgDto.getStatus()));
}
}
}
return refDelta;
}
public List<AssignmentsPreviewDto> recomputeAssignmentsPerformed(AjaxRequestTarget target) {
LOGGER.debug("Recompute user assignments");
Task task = createSimpleTask(OPERATION_RECOMPUTE_ASSIGNMENTS);
OperationResult result = new OperationResult(OPERATION_RECOMPUTE_ASSIGNMENTS);
ObjectDelta<F> delta;
Set<AssignmentsPreviewDto> assignmentDtoSet = new TreeSet<>();
try {
reviveModels();
ObjectWrapper<F> userWrapper = getObjectWrapper();
delta = userWrapper.getObjectDelta();
if (userWrapper.getOldDelta() != null) {
delta = ObjectDelta.summarize(userWrapper.getOldDelta(), delta);
}
switch (userWrapper.getStatus()) {
case ADDING:
PrismObject<F> focus = delta.getObjectToAdd();
prepareObjectForAdd(focus);
getPrismContext().adopt(focus, getCompileTimeClass());
if (LOGGER.isTraceEnabled()) {
LOGGER.trace("Delta before add user:\n{}", new Object[] { delta.debugDump(3) });
}
if (!delta.isEmpty()) {
delta.revive(getPrismContext());
} else {
result.recordSuccess();
}
break;
case MODIFYING:
prepareObjectDeltaForModify(delta);
if (LOGGER.isTraceEnabled()) {
LOGGER.trace("Delta before modify user:\n{}", new Object[] { delta.debugDump(3) });
}
List<ObjectDelta<? extends ObjectType>> accountDeltas = getShadowModifyDeltas(result);
Collection<ObjectDelta<? extends ObjectType>> deltas = new ArrayList<>();
if (!delta.isEmpty()) {
delta.revive(getPrismContext());
deltas.add(delta);
}
for (ObjectDelta accDelta : accountDeltas) {
if (!accDelta.isEmpty()) {
accDelta.revive(getPrismContext());
deltas.add(accDelta);
}
}
break;
default:
error(getString("pageAdminFocus.message.unsupportedState", userWrapper.getStatus()));
}
ModelContext<UserType> modelContext = null;
try {
modelContext = getModelInteractionService()
.previewChanges(WebComponentUtil.createDeltaCollection(delta), null, task, result);
} catch (NoFocusNameSchemaException e) {
info(getString("pageAdminFocus.message.noUserName"));
target.add(getFeedbackPanel());
return null;
}
DeltaSetTriple<? extends EvaluatedAssignment> evaluatedAssignmentTriple = modelContext
.getEvaluatedAssignmentTriple();
Collection<? extends EvaluatedAssignment> evaluatedAssignments = evaluatedAssignmentTriple
.getNonNegativeValues();
if (evaluatedAssignments.isEmpty()) {
info(getString("pageAdminFocus.message.noAssignmentsAvailable"));
target.add(getFeedbackPanel());
return null;
}
for (EvaluatedAssignment<UserType> evaluatedAssignment : evaluatedAssignments) {
if (!evaluatedAssignment.isValid()) {
continue;
}
// roles and orgs
DeltaSetTriple<? extends EvaluatedAssignmentTarget> evaluatedRolesTriple = evaluatedAssignment
.getRoles();
Collection<? extends EvaluatedAssignmentTarget> evaluatedRoles = evaluatedRolesTriple
.getNonNegativeValues();
for (EvaluatedAssignmentTarget role : evaluatedRoles) {
if (role.isEvaluateConstructions()) {
assignmentDtoSet.add(createAssignmentsPreviewDto(role, task, result));
}
}
// all resources
DeltaSetTriple<EvaluatedConstruction> evaluatedConstructionsTriple = evaluatedAssignment
.getEvaluatedConstructions(task, result);
Collection<EvaluatedConstruction> evaluatedConstructions = evaluatedConstructionsTriple
.getNonNegativeValues();
for (EvaluatedConstruction construction : evaluatedConstructions) {
assignmentDtoSet.add(createAssignmentsPreviewDto(construction));
}
}
return new ArrayList<>(assignmentDtoSet);
} catch (Exception e) {
LoggingUtils.logUnexpectedException(LOGGER, "Could not create assignments preview.", e);
error("Could not create assignments preview. Reason: " + e);
target.add(getFeedbackPanel());
}
return null;
}
private AssignmentsPreviewDto createAssignmentsPreviewDto(EvaluatedAssignmentTarget evaluatedAbstractRole,
Task task, OperationResult result) {
return createAssignmentsPreviewDto(evaluatedAbstractRole.getTarget(), evaluatedAbstractRole.isDirectlyAssigned(),
evaluatedAbstractRole.getAssignment(), task, result);
}
protected AssignmentsPreviewDto createAssignmentsPreviewDto(ObjectReferenceType reference,
Task task, OperationResult result) {
PrismObject<? extends FocusType> targetObject = WebModelServiceUtils.resolveReferenceRaw(reference,
PageAdminFocus.this, task, result);
return createAssignmentsPreviewDto(targetObject, true,
null, task, result);
}
protected AssignmentsPreviewDto createDelegableAssignmentsPreviewDto(AssignmentType assignment,
Task task, OperationResult result) {
if (assignment.getTargetRef() != null){
if (RoleType.COMPLEX_TYPE.equals(assignment.getTargetRef().getType())
|| OrgType.COMPLEX_TYPE.equals(assignment.getTargetRef().getType())
|| ServiceType.COMPLEX_TYPE.equals(assignment.getTargetRef().getType())){
PrismObject<AbstractRoleType> targetObject = WebModelServiceUtils.resolveReferenceRaw(assignment.getTargetRef(),
PageAdminFocus.this, task, result);
Boolean isDelegable = false;
if (targetObject != null) {
isDelegable = targetObject.getRealValue().isDelegable();
}
if (Boolean.TRUE.equals(isDelegable)){
return createAssignmentsPreviewDto(targetObject, true, assignment, task, result);
}
}
}
return null;
}
private AssignmentsPreviewDto createAssignmentsPreviewDto(PrismObject<? extends FocusType> targetObject,
boolean isDirectlyAssigned, AssignmentType assignment,
Task task, OperationResult result) {
AssignmentsPreviewDto dto = new AssignmentsPreviewDto();
dto.setTargetOid(targetObject.getOid());
dto.setTargetName(getNameToDisplay(targetObject));
dto.setTargetDescription(targetObject.asObjectable().getDescription());
dto.setTargetClass(targetObject.getCompileTimeClass());
dto.setTargetType(WebComponentUtil.classToQName(getPrismContext(), targetObject.getCompileTimeClass()));
dto.setDirect(isDirectlyAssigned);
if (assignment != null) {
if (assignment.getTenantRef() != null) {
dto.setTenantName(nameFromReference(assignment.getTenantRef(),
task, result));
dto.setTenantRef(assignment.getTenantRef());
}
if (assignment.getOrgRef() != null) {
dto.setOrgRefName(
nameFromReference(assignment.getOrgRef(), task, result));
dto.setOrgRef(assignment.getOrgRef());
}
if (assignment.getTargetRef() != null){
dto.setRelation(assignment.getTargetRef().getRelation());
}
}
return dto;
}
private String getNameToDisplay(PrismObject<? extends FocusType> target) {
if (target.canRepresent(AbstractRoleType.class)) {
String n = PolyString.getOrig(((AbstractRoleType)target.asObjectable()).getDisplayName());
if (StringUtils.isNotBlank(n)) {
return n;
}
}
return PolyString.getOrig(target.asObjectable().getName());
}
private String nameFromReference(ObjectReferenceType reference, Task task, OperationResult result) {
String oid = reference.getOid();
QName type = reference.getType();
Class<? extends ObjectType> clazz = getPrismContext().getSchemaRegistry().getCompileTimeClass(type);
PrismObject<? extends ObjectType> prismObject;
try {
prismObject = getModelService().getObject(clazz, oid,
SelectorOptions.createCollection(GetOperationOptions.createNoFetch()), task, result);
} catch (ObjectNotFoundException | SchemaException | SecurityViolationException
| CommunicationException | ConfigurationException | RuntimeException e) {
LoggingUtils.logUnexpectedException(LOGGER, "Couldn't retrieve name for {}: {}", e,
clazz.getSimpleName(), oid);
return "Couldn't retrieve name for " + oid;
}
ObjectType object = prismObject.asObjectable();
if (object instanceof AbstractRoleType) {
return getNameToDisplay((PrismObject<? extends FocusType>) object.asPrismObject());
} else {
return PolyString.getOrig(object.getName());
}
}
private AssignmentsPreviewDto createAssignmentsPreviewDto(EvaluatedConstruction evaluatedConstruction) {
AssignmentsPreviewDto dto = new AssignmentsPreviewDto();
PrismObject<ResourceType> resource = evaluatedConstruction.getResource();
dto.setTargetOid(resource.getOid());
dto.setTargetName(PolyString.getOrig(resource.asObjectable().getName()));
dto.setTargetDescription(resource.asObjectable().getDescription());
dto.setTargetClass(resource.getCompileTimeClass());
dto.setDirect(evaluatedConstruction.isDirectlyAssigned());
dto.setKind(evaluatedConstruction.getKind());
dto.setIntent(evaluatedConstruction.getIntent());
return dto;
}
@Override
protected void performAdditionalValidation(PrismObject<F> object,
Collection<ObjectDelta<? extends ObjectType>> deltas, Collection<SimpleValidationError> errors) throws SchemaException {
if (object != null && object.asObjectable() != null) {
for (AssignmentType assignment : object.asObjectable().getAssignment()) {
for (MidpointFormValidator validator : getFormValidatorRegistry().getValidators()) {
if (errors == null) {
errors = validator.validateAssignment(assignment);
} else {
errors.addAll(validator.validateAssignment(assignment));
}
}
}
}
}
}