diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/self/PageAbstractSelfCredentials.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/self/PageAbstractSelfCredentials.java index 8210398ea7d..187111c711b 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/self/PageAbstractSelfCredentials.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/self/PageAbstractSelfCredentials.java @@ -1,448 +1,451 @@ -/* - * Copyright (c) 2010-2019 Evolveum and contributors - * - * This work is dual-licensed under the Apache License 2.0 - * and European Union Public License. See LICENSE file for details. - */ -package com.evolveum.midpoint.web.page.self; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.List; - -import com.evolveum.midpoint.gui.api.util.WebModelServiceUtils; -import com.evolveum.midpoint.prism.*; -import com.evolveum.prism.xml.ns._public.types_3.EncryptedDataType; - -import org.apache.commons.collections4.CollectionUtils; -import org.apache.wicket.ajax.AjaxRequestTarget; -import org.apache.wicket.extensions.markup.html.tabs.AbstractTab; -import org.apache.wicket.extensions.markup.html.tabs.ITab; -import org.apache.wicket.markup.html.WebMarkupContainer; -import org.apache.wicket.markup.html.form.Form; -import org.apache.wicket.model.Model; - -import com.evolveum.midpoint.common.refinery.RefinedObjectClassDefinition; -import com.evolveum.midpoint.gui.api.model.LoadableModel; -import com.evolveum.midpoint.gui.api.util.WebComponentUtil; -import com.evolveum.midpoint.prism.delta.ObjectDelta; -import com.evolveum.midpoint.prism.delta.PropertyDelta; -import com.evolveum.midpoint.prism.path.ItemPath; -import com.evolveum.midpoint.prism.schema.SchemaRegistry; -import com.evolveum.midpoint.schema.GetOperationOptions; -import com.evolveum.midpoint.schema.SchemaConstantsGenerated; -import com.evolveum.midpoint.schema.SelectorOptions; -import com.evolveum.midpoint.schema.constants.SchemaConstants; -import com.evolveum.midpoint.schema.result.OperationResult; -import com.evolveum.midpoint.schema.util.ResourceTypeUtil; -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.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.logging.LoggingUtils; -import com.evolveum.midpoint.util.logging.Trace; -import com.evolveum.midpoint.util.logging.TraceManager; -import com.evolveum.midpoint.web.component.AjaxSubmitButton; -import com.evolveum.midpoint.web.component.TabbedPanel; -import com.evolveum.midpoint.web.component.breadcrumbs.Breadcrumb; -import com.evolveum.midpoint.web.page.admin.home.dto.MyPasswordsDto; -import com.evolveum.midpoint.web.page.admin.home.dto.PasswordAccountDto; -import com.evolveum.midpoint.web.page.self.component.ChangePasswordPanel; -import com.evolveum.midpoint.web.security.util.SecurityUtils; -import com.evolveum.midpoint.xml.ns._public.common.common_3.*; -import com.evolveum.prism.xml.ns._public.types_3.ProtectedStringType; - -/** - * @author Viliam Repan (lazyman) - */ - -public abstract class PageAbstractSelfCredentials extends PageSelf { - private static final long serialVersionUID = 1L; - - protected static final String ID_MAIN_FORM = "mainForm"; - private static final String ID_TAB_PANEL = "tabPanel"; - private static final String ID_SAVE_BUTTON = "save"; - private static final String ID_CANCEL_BUTTON = "cancel"; - - private static final Trace LOGGER = TraceManager.getTrace(PageAbstractSelfCredentials.class); - private static final String DOT_CLASS = PageSelfCredentials.class.getName() + "."; - private static final String OPERATION_LOAD_USER_WITH_ACCOUNTS = DOT_CLASS + "loadUserWithAccounts"; - private static final String OPERATION_LOAD_USER = DOT_CLASS + "loadUser"; - private static final String OPERATION_LOAD_ACCOUNT = DOT_CLASS + "loadAccount"; - private static final String OPERATION_SAVE_PASSWORD = DOT_CLASS + "savePassword"; - private static final String OPERATION_CHECK_PASSWORD = DOT_CLASS + "checkPassword"; - private static final String OPERATION_GET_CREDENTIALS_POLICY = DOT_CLASS + "getCredentialsPolicy"; - - - private LoadableModel model; - private PrismObject focus; - - public PageAbstractSelfCredentials() { - model = new LoadableModel(false) { - private static final long serialVersionUID = 1L; - - @Override - protected MyPasswordsDto load() { - return loadPageModel(); - } - }; - - initLayout(); - } - - @Override - protected void createBreadcrumb() { - super.createBreadcrumb(); - - Breadcrumb bc = getLastBreadcrumb(); - bc.setIcon(new Model<>("fa fa-shield")); - } - - public PageAbstractSelfCredentials(final MyPasswordsDto myPasswordsDto) { - model = new LoadableModel(myPasswordsDto, false) { - private static final long serialVersionUID = 1L; - - @Override - protected MyPasswordsDto load() { - return myPasswordsDto; - } - }; - - initLayout(); - } - - private MyPasswordsDto loadPageModel() { - LOGGER.debug("Loading user and accounts."); - MyPasswordsDto dto = new MyPasswordsDto(); - OperationResult result = new OperationResult(OPERATION_LOAD_USER_WITH_ACCOUNTS); - try { - String focusOid = SecurityUtils.getPrincipalUser().getOid(); - Task task = createSimpleTask(OPERATION_LOAD_USER); - OperationResult subResult = result.createSubresult(OPERATION_LOAD_USER); - focus = getModelService().getObject(FocusType.class, focusOid, null, task, subResult); - subResult.recordSuccessIfUnknown(); - - dto.getAccounts().add(createDefaultPasswordAccountDto(focus)); - - CredentialsPolicyType credentialsPolicyType = getPasswordCredentialsPolicy(); - if (credentialsPolicyType != null) { - PasswordCredentialsPolicyType passwordCredentialsPolicy = credentialsPolicyType.getPassword(); - if (passwordCredentialsPolicy != null) { - CredentialsPropagationUserControlType propagationUserControl = passwordCredentialsPolicy.getPropagationUserControl(); - if (propagationUserControl != null) { - dto.setPropagation(propagationUserControl); - } - PasswordChangeSecurityType passwordChangeSecurity = passwordCredentialsPolicy.getPasswordChangeSecurity(); - if (passwordChangeSecurity != null) { - dto.setPasswordChangeSecurity(passwordChangeSecurity); - } - - } - - } - - if (dto.getPropagation() == null || dto.getPropagation().equals(CredentialsPropagationUserControlType.USER_CHOICE)) { - PrismReference reference = focus.findReference(FocusType.F_LINK_REF); - if (reference == null || reference.getValues() == null) { - LOGGER.debug("No accounts found for user {}.", new Object[]{focusOid}); - return dto; - } - - final Collection> options = getOperationOptionsBuilder() - .item(ShadowType.F_RESOURCE_REF).resolve() - .build(); - List values = reference.getValues(); - for (PrismReferenceValue value : values) { - subResult = result.createSubresult(OPERATION_LOAD_ACCOUNT); - try { - String accountOid = value.getOid(); - task = createSimpleTask(OPERATION_LOAD_ACCOUNT); - - PrismObject account = getModelService().getObject(ShadowType.class, - accountOid, options, task, subResult); - - - dto.getAccounts().add(createPasswordAccountDto(account, task, subResult)); - subResult.recordSuccessIfUnknown(); - } catch (Exception ex) { - LoggingUtils.logUnexpectedException(LOGGER, "Couldn't load account", ex); - subResult.recordFatalError(getString("PageAbstractSelfCredentials.message.couldntLoadAccount.fatalError"), ex); - } - } - } - result.recordSuccessIfUnknown(); - } catch (Exception ex) { - LoggingUtils.logUnexpectedException(LOGGER, "Couldn't load accounts", ex); - result.recordFatalError(getString("PageAbstractSelfCredentials.message.couldntLoadAccounts.fatalError"), ex); - } finally { - result.recomputeStatus(); - } - - Collections.sort(dto.getAccounts()); - - if (!result.isSuccess() && !result.isHandledError()) { - showResult(result); - } - - return dto; - } - - - private void initLayout() { - Form mainForm = new com.evolveum.midpoint.web.component.form.Form<>(ID_MAIN_FORM); - - List tabs = new ArrayList<>(); - tabs.add(new AbstractTab(createStringResource("PageSelfCredentials.tabs.password")) { - private static final long serialVersionUID = 1L; - - @Override - public WebMarkupContainer getPanel(String panelId) { - return new ChangePasswordPanel(panelId, isCheckOldPassword(), model, model.getObject()); - } - }); - - TabbedPanel credentialsTabPanel = WebComponentUtil.createTabPanel(ID_TAB_PANEL, this, tabs, null); - credentialsTabPanel.setOutputMarkupId(true); - - mainForm.add(credentialsTabPanel); - initButtons(mainForm); - - add(mainForm); - - } - - private void initButtons(Form mainForm) { - AjaxSubmitButton save = new AjaxSubmitButton(ID_SAVE_BUTTON, createStringResource("PageBase.button.save")) { - - private static final long serialVersionUID = 1L; - - @Override - protected void onError(AjaxRequestTarget target) { - target.add(getFeedbackPanel()); - } - - @Override - protected void onSubmit(AjaxRequestTarget target) { - onSavePerformed(target); - } - }; - mainForm.setDefaultButton(save); - mainForm.add(save); - - AjaxSubmitButton cancel = new AjaxSubmitButton(ID_CANCEL_BUTTON, createStringResource("PageBase.button.back")) { - - private static final long serialVersionUID = 1L; - - @Override - protected void onError(AjaxRequestTarget target) { - onCancelPerformed(target); - } - - @Override - protected void onSubmit(AjaxRequestTarget target) { - onCancelPerformed(target); - } - }; - mainForm.add(cancel); - } - - private PasswordAccountDto createDefaultPasswordAccountDto(PrismObject focus) { - String customSystemName = WebComponentUtil.getMidpointCustomSystemName(PageAbstractSelfCredentials.this, "midpoint.default.system.name"); - return new PasswordAccountDto(focus.getOid(), focus.getName().getOrig(), - getString("PageSelfCredentials.resourceMidpoint", customSystemName), - WebComponentUtil.isActivationEnabled(focus), true); - } - - private PasswordAccountDto createPasswordAccountDto(PrismObject account, Task task, OperationResult result) throws ObjectNotFoundException, ExpressionEvaluationException, CommunicationException, ConfigurationException, SecurityViolationException { - PrismReference resourceRef = account.findReference(ShadowType.F_RESOURCE_REF); - String resourceName; - if (resourceRef == null || resourceRef.getValue() == null || resourceRef.getValue().getObject() == null) { - resourceName = getString("PageSelfCredentials.couldntResolve"); - } else { - resourceName = WebComponentUtil.getName(resourceRef.getValue().getObject()); - } - - PasswordAccountDto passwordAccountDto = new PasswordAccountDto(account.getOid(), WebComponentUtil.getName(account), - resourceName, WebComponentUtil.isActivationEnabled(account)); - - passwordAccountDto.setPasswordOutbound(getPasswordOutbound(account, task, result)); - passwordAccountDto.setPasswordCapabilityEnabled(hasPasswordCapability(account)); - return passwordAccountDto; - } - - protected void onSavePerformed(AjaxRequestTarget target) { - List selectedAccounts = getSelectedAccountsList(); - - ProtectedStringType oldPassword = null; - if (isCheckOldPassword()) { - LOGGER.debug("Check old password"); - if (model.getObject().getOldPassword() == null - || model.getObject().getOldPassword().trim().equals("")){ - warn(getString("PageSelfCredentials.specifyOldPasswordMessage")); - target.add(getFeedbackPanel()); - return; - } else { - OperationResult checkPasswordResult = new OperationResult(OPERATION_CHECK_PASSWORD); - Task checkPasswordTask = createSimpleTask(OPERATION_CHECK_PASSWORD); - try { - oldPassword = new ProtectedStringType(); - oldPassword.setClearValue(model.getObject().getOldPassword()); - boolean isCorrectPassword = getModelInteractionService().checkPassword(focus.getOid(), oldPassword, - checkPasswordTask, checkPasswordResult); - if (!isCorrectPassword) { - error(getString("PageSelfCredentials.incorrectOldPassword")); - target.add(getFeedbackPanel()); - return; - } - } catch (Exception ex) { - LoggingUtils.logUnexpectedException(LOGGER, "Couldn't check password", ex); - checkPasswordResult.recordFatalError( - getString("PageAbstractSelfCredentials.message.onSavePerformed.fatalError", ex.getMessage()), ex); - target.add(getFeedbackPanel()); - return; - } finally { - checkPasswordResult.computeStatus(); - } - } - } - if (selectedAccounts.isEmpty()) { - warn(getString("PageSelfCredentials.noAccountSelected")); - target.add(getFeedbackPanel()); - return; - } - if (getModelObject().getPassword() == null ) { - warn(getString("PageSelfCredentials.emptyPasswordFiled")); - target.add(getFeedbackPanel()); - return; - } - - OperationResult result = new OperationResult(OPERATION_SAVE_PASSWORD); - try { - MyPasswordsDto dto = model.getObject(); - ProtectedStringType password = dto.getPassword(); - if (!password.isEncrypted()) { - WebComponentUtil.encryptProtectedString(password, true, getMidpointApplication()); - } - final ItemPath valuePath = ItemPath.create(SchemaConstantsGenerated.C_CREDENTIALS, - CredentialsType.F_PASSWORD, PasswordType.F_VALUE); - SchemaRegistry registry = getPrismContext().getSchemaRegistry(); - Collection> deltas = new ArrayList<>(); - - - for (PasswordAccountDto accDto : selectedAccounts) { - PrismObjectDefinition objDef = accDto.isMidpoint() ? - registry.findObjectDefinitionByCompileTimeClass(UserType.class) : - registry.findObjectDefinitionByCompileTimeClass(ShadowType.class); - - PropertyDelta delta = getPrismContext().deltaFactory().property() - .createModificationReplaceProperty(valuePath, objDef, password); - if (oldPassword != null) { - delta.addEstimatedOldValue(getPrismContext().itemFactory().createPropertyValue(oldPassword)); - } - - Class type = accDto.isMidpoint() ? UserType.class : ShadowType.class; - - deltas.add(getPrismContext().deltaFactory().object().createModifyDelta(accDto.getOid(), delta, type - )); - } - getModelService().executeChanges(deltas, null, createSimpleTask(OPERATION_SAVE_PASSWORD, SchemaConstants.CHANNEL_GUI_SELF_SERVICE_URI), result); - - result.computeStatus(); - } catch (Exception ex) { - setEncryptedPasswordData(null); - LoggingUtils.logUnexpectedException(LOGGER, "Couldn't save password changes", ex); - result.recordFatalError(getString("PageAbstractSelfCredentials.save.password.failed", ex.getMessage()), ex); - } finally { - result.computeStatusIfUnknown(); - } - - finishChangePassword(result, target); - } - - protected void setEncryptedPasswordData(EncryptedDataType data) { - MyPasswordsDto dto = model.getObject(); - ProtectedStringType password = dto.getPassword(); - if (password != null){ - password.setEncryptedData(data); - } - } - - protected abstract boolean isCheckOldPassword(); - - protected abstract void finishChangePassword(OperationResult result, AjaxRequestTarget target); - - private List getSelectedAccountsList(){ - List passwordAccountDtos = model.getObject().getAccounts(); - List selectedAccountList = new ArrayList<>(); - if (model.getObject().getPropagation() != null - && model.getObject().getPropagation().equals(CredentialsPropagationUserControlType.MAPPING)){ - selectedAccountList.addAll(passwordAccountDtos); - } else { - for (PasswordAccountDto passwordAccountDto : passwordAccountDtos) { - if (passwordAccountDto.getCssClass().equals(ChangePasswordPanel.SELECTED_ACCOUNT_ICON_CSS)) { - selectedAccountList.add(passwordAccountDto); - } - } - } - return selectedAccountList; - } - private void onCancelPerformed(AjaxRequestTarget target) { - redirectBack(); - } - - private boolean getPasswordOutbound(PrismObject shadow, Task task, OperationResult result) throws ObjectNotFoundException, ExpressionEvaluationException, CommunicationException, ConfigurationException, SecurityViolationException { - try { - RefinedObjectClassDefinition rOCDef = getModelInteractionService().getEditObjectClassDefinition(shadow, - shadow.asObjectable().getResourceRef().asReferenceValue().getObject(), - AuthorizationPhaseType.REQUEST, task, result); - - if (rOCDef != null && !CollectionUtils.isEmpty(rOCDef.getPasswordOutbound())){ - return true; - } - } catch (SchemaException ex) { - - } - return false; - } - - - private boolean hasPasswordCapability(PrismObject shadow) { - - ShadowType shadowType = shadow.asObjectable(); - ResourceType resource = (ResourceType) shadowType.getResourceRef().asReferenceValue().getObject().asObjectable(); - ResourceObjectTypeDefinitionType resourceObjectTypeDefinitionType = ResourceTypeUtil.findObjectTypeDefinition(resource.asPrismObject(), shadowType.getKind(), shadowType.getIntent()); - - return ResourceTypeUtil.isPasswordCapabilityEnabled(resource, resourceObjectTypeDefinitionType); - - } - - public PrismObject getFocus() { - return focus; - } - - private CredentialsPolicyType getPasswordCredentialsPolicy (){ - LOGGER.debug("Getting credentials policy"); - Task task = createSimpleTask(OPERATION_GET_CREDENTIALS_POLICY); - OperationResult result = new OperationResult(OPERATION_GET_CREDENTIALS_POLICY); - CredentialsPolicyType credentialsPolicyType = null; - try { - credentialsPolicyType = getModelInteractionService().getCredentialsPolicy(focus, task, result); - result.recordSuccessIfUnknown(); - } catch (Exception ex) { - LoggingUtils.logUnexpectedException(LOGGER, "Couldn't load credentials policy", ex); - result.recordFatalError( - getString("PageAbstractSelfCredentials.message.getPasswordCredentialsPolicy.fatalError", ex.getMessage()), ex); - } finally { - result.computeStatus(); - } - return credentialsPolicyType; - } - - protected MyPasswordsDto getModelObject() { - return model.getObject(); - } -} +/* + * Copyright (c) 2010-2019 Evolveum and contributors + * + * This work is dual-licensed under the Apache License 2.0 + * and European Union Public License. See LICENSE file for details. + */ +package com.evolveum.midpoint.web.page.self; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +import org.apache.wicket.ajax.AjaxRequestTarget; +import org.apache.wicket.extensions.markup.html.tabs.AbstractTab; +import org.apache.wicket.extensions.markup.html.tabs.ITab; +import org.apache.wicket.markup.html.WebMarkupContainer; +import org.apache.wicket.markup.html.form.Form; +import org.apache.wicket.model.Model; + +import com.evolveum.midpoint.common.refinery.RefinedObjectClassDefinition; +import com.evolveum.midpoint.gui.api.model.LoadableModel; +import com.evolveum.midpoint.gui.api.util.WebComponentUtil; +import com.evolveum.midpoint.prism.PrismObject; +import com.evolveum.midpoint.prism.PrismObjectDefinition; +import com.evolveum.midpoint.prism.PrismReference; +import com.evolveum.midpoint.prism.PrismReferenceValue; +import com.evolveum.midpoint.prism.delta.ObjectDelta; +import com.evolveum.midpoint.prism.delta.PropertyDelta; +import com.evolveum.midpoint.prism.path.ItemPath; +import com.evolveum.midpoint.prism.schema.SchemaRegistry; +import com.evolveum.midpoint.schema.CapabilityUtil; +import com.evolveum.midpoint.schema.GetOperationOptions; +import com.evolveum.midpoint.schema.SchemaConstantsGenerated; +import com.evolveum.midpoint.schema.SelectorOptions; +import com.evolveum.midpoint.schema.constants.SchemaConstants; +import com.evolveum.midpoint.schema.result.OperationResult; +import com.evolveum.midpoint.schema.util.ResourceTypeUtil; +import com.evolveum.midpoint.task.api.Task; +import com.evolveum.midpoint.util.exception.*; +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.AjaxSubmitButton; +import com.evolveum.midpoint.web.component.TabbedPanel; +import com.evolveum.midpoint.web.component.breadcrumbs.Breadcrumb; +import com.evolveum.midpoint.web.page.admin.home.dto.MyPasswordsDto; +import com.evolveum.midpoint.web.page.admin.home.dto.PasswordAccountDto; +import com.evolveum.midpoint.web.page.self.component.ChangePasswordPanel; +import com.evolveum.midpoint.web.security.util.SecurityUtils; +import com.evolveum.midpoint.xml.ns._public.common.common_3.*; +import com.evolveum.midpoint.xml.ns._public.resource.capabilities_3.CredentialsCapabilityType; +import com.evolveum.prism.xml.ns._public.types_3.EncryptedDataType; +import com.evolveum.prism.xml.ns._public.types_3.ProtectedStringType; + +/** + * @author Viliam Repan (lazyman) + */ + +public abstract class PageAbstractSelfCredentials extends PageSelf { + private static final long serialVersionUID = 1L; + + private static final Trace LOGGER = TraceManager.getTrace(PageAbstractSelfCredentials.class); + + protected static final String ID_MAIN_FORM = "mainForm"; + private static final String ID_TAB_PANEL = "tabPanel"; + private static final String ID_SAVE_BUTTON = "save"; + private static final String ID_CANCEL_BUTTON = "cancel"; + + private static final String DOT_CLASS = PageSelfCredentials.class.getName() + "."; + private static final String OPERATION_LOAD_USER_WITH_ACCOUNTS = DOT_CLASS + "loadUserWithAccounts"; + private static final String OPERATION_LOAD_USER = DOT_CLASS + "loadUser"; + private static final String OPERATION_LOAD_ACCOUNT = DOT_CLASS + "loadAccount"; + private static final String OPERATION_SAVE_PASSWORD = DOT_CLASS + "savePassword"; + private static final String OPERATION_CHECK_PASSWORD = DOT_CLASS + "checkPassword"; + private static final String OPERATION_GET_CREDENTIALS_POLICY = DOT_CLASS + "getCredentialsPolicy"; + + + private LoadableModel model; + private PrismObject focus; + + public PageAbstractSelfCredentials() { + model = new LoadableModel(false) { + private static final long serialVersionUID = 1L; + + @Override + protected MyPasswordsDto load() { + return loadPageModel(); + } + }; + + initLayout(); + } + + @Override + protected void createBreadcrumb() { + super.createBreadcrumb(); + + Breadcrumb bc = getLastBreadcrumb(); + bc.setIcon(new Model<>("fa fa-shield")); + } + + private MyPasswordsDto loadPageModel() { + LOGGER.debug("Loading user and accounts."); + MyPasswordsDto dto = new MyPasswordsDto(); + OperationResult result = new OperationResult(OPERATION_LOAD_USER_WITH_ACCOUNTS); + try { + String focusOid = SecurityUtils.getPrincipalUser().getOid(); + Task task = createSimpleTask(OPERATION_LOAD_USER); + OperationResult subResult = result.createSubresult(OPERATION_LOAD_USER); + focus = getModelService().getObject(FocusType.class, focusOid, null, task, subResult); + subResult.recordSuccessIfUnknown(); + + dto.getAccounts().add(createDefaultPasswordAccountDto(focus)); + + CredentialsPolicyType credentialsPolicyType = getPasswordCredentialsPolicy(); + if (credentialsPolicyType != null) { + PasswordCredentialsPolicyType passwordCredentialsPolicy = credentialsPolicyType.getPassword(); + if (passwordCredentialsPolicy != null) { + CredentialsPropagationUserControlType propagationUserControl = passwordCredentialsPolicy.getPropagationUserControl(); + if (propagationUserControl != null) { + dto.setPropagation(propagationUserControl); + } + PasswordChangeSecurityType passwordChangeSecurity = passwordCredentialsPolicy.getPasswordChangeSecurity(); + if (passwordChangeSecurity != null) { + dto.setPasswordChangeSecurity(passwordChangeSecurity); + } + + } + + } + + if (dto.getPropagation() == null || dto.getPropagation().equals(CredentialsPropagationUserControlType.USER_CHOICE)) { + PrismReference reference = focus.findReference(FocusType.F_LINK_REF); + if (reference == null || reference.getValues() == null) { + LOGGER.debug("No accounts found for user {}.", new Object[]{focusOid}); + return dto; + } + + final Collection> options = getOperationOptionsBuilder() + .noFetch() + .item(ShadowType.F_RESOURCE_REF).resolve() + .build(); + List values = reference.getValues(); + for (PrismReferenceValue value : values) { + subResult = result.createSubresult(OPERATION_LOAD_ACCOUNT); + try { + String accountOid = value.getOid(); + task = createSimpleTask(OPERATION_LOAD_ACCOUNT); + + PrismObject account = getModelService().getObject(ShadowType.class, + accountOid, options, task, subResult); + + + dto.getAccounts().add(createPasswordAccountDto(account, task, subResult)); + subResult.recordSuccessIfUnknown(); + } catch (Exception ex) { + LoggingUtils.logUnexpectedException(LOGGER, "Couldn't load account", ex); + subResult.recordFatalError(getString("PageAbstractSelfCredentials.message.couldntLoadAccount.fatalError"), ex); + } + } + } + result.recordSuccessIfUnknown(); + } catch (Exception ex) { + LoggingUtils.logUnexpectedException(LOGGER, "Couldn't load accounts", ex); + result.recordFatalError(getString("PageAbstractSelfCredentials.message.couldntLoadAccounts.fatalError"), ex); + } finally { + result.recomputeStatus(); + } + + Collections.sort(dto.getAccounts()); + + if (!result.isSuccess() && !result.isHandledError()) { + showResult(result); + } + + return dto; + } + + + private void initLayout() { + Form mainForm = new com.evolveum.midpoint.web.component.form.Form<>(ID_MAIN_FORM); + + List tabs = new ArrayList<>(); + tabs.add(new AbstractTab(createStringResource("PageSelfCredentials.tabs.password")) { + private static final long serialVersionUID = 1L; + + @Override + public WebMarkupContainer getPanel(String panelId) { + return new ChangePasswordPanel(panelId, isCheckOldPassword(), model, model.getObject()); + } + }); + + TabbedPanel credentialsTabPanel = WebComponentUtil.createTabPanel(ID_TAB_PANEL, this, tabs, null); + credentialsTabPanel.setOutputMarkupId(true); + + mainForm.add(credentialsTabPanel); + initButtons(mainForm); + + add(mainForm); + + } + + private void initButtons(Form mainForm) { + AjaxSubmitButton save = new AjaxSubmitButton(ID_SAVE_BUTTON, createStringResource("PageBase.button.save")) { + + private static final long serialVersionUID = 1L; + + @Override + protected void onError(AjaxRequestTarget target) { + target.add(getFeedbackPanel()); + } + + @Override + protected void onSubmit(AjaxRequestTarget target) { + onSavePerformed(target); + } + }; + mainForm.setDefaultButton(save); + mainForm.add(save); + + AjaxSubmitButton cancel = new AjaxSubmitButton(ID_CANCEL_BUTTON, createStringResource("PageBase.button.back")) { + + private static final long serialVersionUID = 1L; + + @Override + protected void onError(AjaxRequestTarget target) { + onCancelPerformed(target); + } + + @Override + protected void onSubmit(AjaxRequestTarget target) { + onCancelPerformed(target); + } + }; + mainForm.add(cancel); + } + + private PasswordAccountDto createDefaultPasswordAccountDto(PrismObject focus) { + String customSystemName = WebComponentUtil.getMidpointCustomSystemName(PageAbstractSelfCredentials.this, "midpoint.default.system.name"); + return new PasswordAccountDto(focus.getOid(), focus.getName().getOrig(), + getString("PageSelfCredentials.resourceMidpoint", customSystemName), + WebComponentUtil.isActivationEnabled(focus), true); + } + + private PasswordAccountDto createPasswordAccountDto(PrismObject account, Task task, OperationResult result) throws ObjectNotFoundException, ExpressionEvaluationException, CommunicationException, ConfigurationException, SecurityViolationException { + PrismReference resourceRef = account.findReference(ShadowType.F_RESOURCE_REF); + String resourceName; + if (resourceRef == null || resourceRef.getValue() == null || resourceRef.getValue().getObject() == null) { + resourceName = getString("PageSelfCredentials.couldntResolve"); + } else { + resourceName = WebComponentUtil.getName(resourceRef.getValue().getObject()); + } + + PasswordAccountDto passwordAccountDto = new PasswordAccountDto(account.getOid(), WebComponentUtil.getName(account), + resourceName, WebComponentUtil.isActivationEnabled(account)); + + passwordAccountDto.setPasswordOutbound(getPasswordOutbound(account, task, result)); + passwordAccountDto.setPasswordCapabilityEnabled(hasPasswordCapability(account)); + return passwordAccountDto; + } + + protected void onSavePerformed(AjaxRequestTarget target) { + List selectedAccounts = getSelectedAccountsList(); + + ProtectedStringType oldPassword = null; + if (isCheckOldPassword()) { + LOGGER.debug("Check old password"); + if (model.getObject().getOldPassword() == null + || model.getObject().getOldPassword().trim().equals("")){ + warn(getString("PageSelfCredentials.specifyOldPasswordMessage")); + target.add(getFeedbackPanel()); + return; + } else { + OperationResult checkPasswordResult = new OperationResult(OPERATION_CHECK_PASSWORD); + Task checkPasswordTask = createSimpleTask(OPERATION_CHECK_PASSWORD); + try { + oldPassword = new ProtectedStringType(); + oldPassword.setClearValue(model.getObject().getOldPassword()); + boolean isCorrectPassword = getModelInteractionService().checkPassword(focus.getOid(), oldPassword, + checkPasswordTask, checkPasswordResult); + if (!isCorrectPassword) { + error(getString("PageSelfCredentials.incorrectOldPassword")); + target.add(getFeedbackPanel()); + return; + } + } catch (Exception ex) { + LoggingUtils.logUnexpectedException(LOGGER, "Couldn't check password", ex); + checkPasswordResult.recordFatalError( + getString("PageAbstractSelfCredentials.message.onSavePerformed.fatalError", ex.getMessage()), ex); + target.add(getFeedbackPanel()); + return; + } finally { + checkPasswordResult.computeStatus(); + } + } + } + if (selectedAccounts.isEmpty()) { + warn(getString("PageSelfCredentials.noAccountSelected")); + target.add(getFeedbackPanel()); + return; + } + if (getModelObject().getPassword() == null ) { + warn(getString("PageSelfCredentials.emptyPasswordFiled")); + target.add(getFeedbackPanel()); + return; + } + + OperationResult result = new OperationResult(OPERATION_SAVE_PASSWORD); + try { + MyPasswordsDto dto = model.getObject(); + ProtectedStringType password = dto.getPassword(); + if (!password.isEncrypted()) { + WebComponentUtil.encryptProtectedString(password, true, getMidpointApplication()); + } + final ItemPath valuePath = ItemPath.create(SchemaConstantsGenerated.C_CREDENTIALS, + CredentialsType.F_PASSWORD, PasswordType.F_VALUE); + SchemaRegistry registry = getPrismContext().getSchemaRegistry(); + Collection> deltas = new ArrayList<>(); + + + for (PasswordAccountDto accDto : selectedAccounts) { + PrismObjectDefinition objDef = accDto.isMidpoint() ? + registry.findObjectDefinitionByCompileTimeClass(UserType.class) : + registry.findObjectDefinitionByCompileTimeClass(ShadowType.class); + + PropertyDelta delta = getPrismContext().deltaFactory().property() + .createModificationReplaceProperty(valuePath, objDef, password); + if (oldPassword != null) { + delta.addEstimatedOldValue(getPrismContext().itemFactory().createPropertyValue(oldPassword)); + } + + Class type = accDto.isMidpoint() ? UserType.class : ShadowType.class; + + deltas.add(getPrismContext().deltaFactory().object().createModifyDelta(accDto.getOid(), delta, type + )); + } + getModelService().executeChanges(deltas, null, createSimpleTask(OPERATION_SAVE_PASSWORD, SchemaConstants.CHANNEL_GUI_SELF_SERVICE_URI), result); + + result.computeStatus(); + } catch (Exception ex) { + setEncryptedPasswordData(null); + LoggingUtils.logUnexpectedException(LOGGER, "Couldn't save password changes", ex); + result.recordFatalError(getString("PageAbstractSelfCredentials.save.password.failed", ex.getMessage()), ex); + } finally { + result.computeStatusIfUnknown(); + } + + finishChangePassword(result, target); + } + + protected void setEncryptedPasswordData(EncryptedDataType data) { + MyPasswordsDto dto = model.getObject(); + ProtectedStringType password = dto.getPassword(); + if (password != null){ + password.setEncryptedData(data); + } + } + + protected abstract boolean isCheckOldPassword(); + + protected abstract void finishChangePassword(OperationResult result, AjaxRequestTarget target); + + private List getSelectedAccountsList(){ + List passwordAccountDtos = model.getObject().getAccounts(); + List selectedAccountList = new ArrayList<>(); + if (model.getObject().getPropagation() != null + && model.getObject().getPropagation().equals(CredentialsPropagationUserControlType.MAPPING)){ + selectedAccountList.addAll(passwordAccountDtos); + } else { + for (PasswordAccountDto passwordAccountDto : passwordAccountDtos) { + if (passwordAccountDto.getCssClass().equals(ChangePasswordPanel.SELECTED_ACCOUNT_ICON_CSS)) { + selectedAccountList.add(passwordAccountDto); + } + } + } + return selectedAccountList; + } + private void onCancelPerformed(AjaxRequestTarget target) { + redirectBack(); + } + + private boolean getPasswordOutbound(PrismObject shadow, Task task, OperationResult result) throws ObjectNotFoundException, ExpressionEvaluationException, CommunicationException, ConfigurationException, SecurityViolationException { + try { + + //TODO cannot be null? + PrismObject resource = shadow.asObjectable().getResourceRef().asReferenceValue().getObject(); + + RefinedObjectClassDefinition rOCDef = getModelInteractionService().getEditObjectClassDefinition(shadow, + resource, AuthorizationPhaseType.REQUEST, task, result); + + List passwordOutbound = rOCDef.getPasswordOutbound(); + for (MappingType mapping : passwordOutbound) { + if (MappingStrengthType.WEAK == mapping.getStrength()) { + CredentialsCapabilityType capability = ResourceTypeUtil.getEffectiveCapability(resource.asObjectable(), CredentialsCapabilityType.class); + if (CapabilityUtil.isPasswordReadable(capability)) { + return true; + } + continue; + } + // at least one mapping which is not WEAK + return true; + } + + } catch (SchemaException ex) { + LoggingUtils.logUnexpectedException(LOGGER, "Failed to compute password propagation for {} ", ex, shadow); + result.recordFatalError("Faile to compute password propagation for " + shadow, ex); + showResult(result); + } + + return false; + + } + + + private boolean hasPasswordCapability(PrismObject shadow) { + + ShadowType shadowType = shadow.asObjectable(); + ResourceType resource = (ResourceType) shadowType.getResourceRef().asReferenceValue().getObject().asObjectable(); + ResourceObjectTypeDefinitionType resourceObjectTypeDefinitionType = ResourceTypeUtil.findObjectTypeDefinition(resource.asPrismObject(), shadowType.getKind(), shadowType.getIntent()); + + return ResourceTypeUtil.isPasswordCapabilityEnabled(resource, resourceObjectTypeDefinitionType); + + } + + public PrismObject getFocus() { + return focus; + } + + private CredentialsPolicyType getPasswordCredentialsPolicy (){ + LOGGER.debug("Getting credentials policy"); + Task task = createSimpleTask(OPERATION_GET_CREDENTIALS_POLICY); + OperationResult result = new OperationResult(OPERATION_GET_CREDENTIALS_POLICY); + CredentialsPolicyType credentialsPolicyType = null; + try { + credentialsPolicyType = getModelInteractionService().getCredentialsPolicy(focus, task, result); + result.recordSuccessIfUnknown(); + } catch (Exception ex) { + LoggingUtils.logUnexpectedException(LOGGER, "Couldn't load credentials policy", ex); + result.recordFatalError( + getString("PageAbstractSelfCredentials.message.getPasswordCredentialsPolicy.fatalError", ex.getMessage()), ex); + } finally { + result.computeStatus(); + } + return credentialsPolicyType; + } + + protected MyPasswordsDto getModelObject() { + return model.getObject(); + } +} diff --git a/infra/schema/src/main/java/com/evolveum/midpoint/schema/CapabilityUtil.java b/infra/schema/src/main/java/com/evolveum/midpoint/schema/CapabilityUtil.java index 2e77a9cd27a..5540a6f5c74 100644 --- a/infra/schema/src/main/java/com/evolveum/midpoint/schema/CapabilityUtil.java +++ b/infra/schema/src/main/java/com/evolveum/midpoint/schema/CapabilityUtil.java @@ -1,338 +1,359 @@ -/* - * Copyright (c) 2010-2013 Evolveum and contributors - * - * This work is dual-licensed under the Apache License 2.0 - * and European Union Public License. See LICENSE file for details. - */ -package com.evolveum.midpoint.schema; - -import java.util.Collection; -import java.util.List; - -import javax.xml.bind.JAXBElement; -import javax.xml.namespace.QName; - -import com.evolveum.midpoint.xml.ns._public.common.common_3.CapabilitiesType; -import com.evolveum.midpoint.xml.ns._public.common.common_3.CapabilityCollectionType; -import com.evolveum.midpoint.xml.ns._public.resource.capabilities_3.*; -import org.jetbrains.annotations.NotNull; -import org.w3c.dom.Element; - -import com.evolveum.midpoint.prism.xml.XmlTypeConverter; -import com.evolveum.midpoint.util.DOMUtil; -import com.evolveum.midpoint.util.JAXBUtil; -import com.evolveum.midpoint.util.exception.SchemaException; - -/** - * @author semancik - * - */ -public class CapabilityUtil { - - @SuppressWarnings({ "unchecked", "rawtypes" }) - public static T getCapability(Collection capabilities, Class capabilityClass) { - if (capabilities == null) { - return null; - } - for (Object cap : capabilities) { - if (cap instanceof JAXBElement) { - JAXBElement element = (JAXBElement) cap; - if (capabilityClass.isAssignableFrom(element.getDeclaredType())) { - return (T) element.getValue(); - } - } else if (capabilityClass.isAssignableFrom(cap.getClass())) { - return (T) cap; - } - } - return null; - } - - public static boolean isCapabilityEnabled(Object capability) { - if (capability == null) { - return false; - } - if (capability instanceof JAXBElement) { - capability = ((JAXBElement)capability).getValue(); - } - - if (capability instanceof CapabilityType) { - return CapabilityUtil.isCapabilityEnabled((CapabilityType)capability); - } else if (capability instanceof Element) { - return CapabilityUtil.isCapabilityEnabled((Element)capability); - } else { - throw new IllegalArgumentException("Unexpected capability type "+capability.getClass()); - } - } - - private static boolean isCapabilityEnabled(Element capability) { - if (capability == null) { - return false; - } - ObjectFactory capabilitiesObjectFactory = new ObjectFactory(); - QName enabledElementName = capabilitiesObjectFactory.createEnabled(true).getName(); - Element enabledElement = DOMUtil.getChildElement(capability, enabledElementName); - return enabledElement == null || Boolean.parseBoolean(enabledElement.getTextContent()); - } - - public static boolean isCapabilityEnabled(T capability) { - if (capability == null) { - return false; - } - if (capability.isEnabled() == null) { - return true; - } - return capability.isEnabled(); - } - - public static Object getCapabilityWithSameElementName(List capabilities, Object capability) { - if (capabilities == null) { - return false; - } - QName capabilityElementName = JAXBUtil.getElementQName(capability); - for (Object cap: capabilities) { - QName capElementName = JAXBUtil.getElementQName(cap); - if (capabilityElementName.equals(capElementName)) { - return cap; - } - } - return null; - } - - public static boolean containsCapabilityWithSameElementName(List capabilities, Object capability) { - return getCapabilityWithSameElementName(capabilities, capability) != null; - } - - public static String getCapabilityDisplayName(Object capability) { - // TODO: look for schema annotation - String className; - if (capability instanceof JAXBElement) { - className = ((JAXBElement) capability).getDeclaredType().getSimpleName(); - } else { - className = capability.getClass().getSimpleName(); - } - if (className.endsWith("CapabilityType")) { - return className.substring(0, className.length() - "CapabilityType".length()); - } - return className; - } - - public static boolean isPasswordReturnedByDefault(CredentialsCapabilityType capability) { - if (capability == null) { - return false; - } - PasswordCapabilityType password = capability.getPassword(); - if (password == null) { - return false; - } - if (password.isReturnedByDefault() == null) { - return true; - } - return password.isReturnedByDefault(); - } - - public static boolean isActivationStatusReturnedByDefault(ActivationCapabilityType capability) { - if (capability == null) { - return false; - } - ActivationStatusCapabilityType statusCap = getEffectiveActivationStatus(capability); - if (statusCap == null) { - return false; - } - if (statusCap.isReturnedByDefault() == null) { - return true; - } - return statusCap.isReturnedByDefault(); - } - - public static boolean isActivationLockoutStatusReturnedByDefault(ActivationCapabilityType capability) { - if (capability == null) { - return false; - } - ActivationLockoutStatusCapabilityType statusCap = capability.getLockoutStatus(); - if (statusCap == null) { - return false; - } - if (statusCap.isReturnedByDefault() == null) { - return true; - } - return statusCap.isReturnedByDefault(); - } - - public static boolean isActivationValidFromReturnedByDefault(ActivationCapabilityType capability) { - if (capability == null) { - return false; - } - ActivationValidityCapabilityType valCap = capability.getValidFrom(); - if (valCap == null) { - return false; - } - if (valCap.isReturnedByDefault() == null) { - return true; - } - return valCap.isReturnedByDefault(); - } - - public static boolean isActivationValidToReturnedByDefault(ActivationCapabilityType capability) { - if (capability == null) { - return false; - } - ActivationValidityCapabilityType valCap = capability.getValidTo(); - if (valCap == null) { - return false; - } - if (valCap.isReturnedByDefault() == null) { - return true; - } - return valCap.isReturnedByDefault(); - } - - @SuppressWarnings("unchecked") - public static CapabilityType asCapabilityType(Object capabilityObject) { - return capabilityObject instanceof CapabilityType ? - (CapabilityType) capabilityObject : - ((JAXBElement) capabilityObject).getValue(); - } - - public static void fillDefaults(@NotNull CapabilityType capability) { - if (capability.isEnabled() == null) { - capability.setEnabled(true); - } - if (capability instanceof ActivationCapabilityType) { - ActivationCapabilityType act = ((ActivationCapabilityType) capability); - if (act.getStatus() == null) { - ActivationStatusCapabilityType st = new ActivationStatusCapabilityType(); - act.setStatus(st); - st.setEnabled(false); // TODO check if all midPoint code honors this flag! - st.setReturnedByDefault(false); - st.setIgnoreAttribute(true); - } else { - ActivationStatusCapabilityType st = act.getStatus(); - st.setEnabled(def(st.isEnabled(), true)); - st.setReturnedByDefault(def(st.isReturnedByDefault(), true)); - st.setIgnoreAttribute(def(st.isIgnoreAttribute(), true)); - } - if (act.getLockoutStatus() == null) { - ActivationLockoutStatusCapabilityType st = new ActivationLockoutStatusCapabilityType(); - act.setLockoutStatus(st); - st.setEnabled(false); - st.setReturnedByDefault(false); - st.setIgnoreAttribute(true); - } else { - ActivationLockoutStatusCapabilityType st = act.getLockoutStatus(); - st.setEnabled(def(st.isEnabled(), true)); - st.setReturnedByDefault(def(st.isReturnedByDefault(), true)); - st.setIgnoreAttribute(def(st.isIgnoreAttribute(), true)); - } - if (act.getValidFrom() == null) { - ActivationValidityCapabilityType vf = new ActivationValidityCapabilityType(); - act.setValidFrom(vf); - vf.setEnabled(false); - vf.setReturnedByDefault(false); - } else { - ActivationValidityCapabilityType vf = act.getValidFrom(); - vf.setEnabled(def(vf.isEnabled(), true)); - vf.setReturnedByDefault(def(vf.isReturnedByDefault(), true)); - } - if (act.getValidTo() == null) { - ActivationValidityCapabilityType vt = new ActivationValidityCapabilityType(); - act.setValidTo(vt); - vt.setEnabled(false); - vt.setReturnedByDefault(false); - } else { - ActivationValidityCapabilityType vt = act.getValidTo(); - vt.setEnabled(def(vt.isEnabled(), true)); - vt.setReturnedByDefault(def(vt.isReturnedByDefault(), true)); - } - } else if (capability instanceof CredentialsCapabilityType) { - CredentialsCapabilityType cred = ((CredentialsCapabilityType) capability); - if (cred.getPassword() == null) { - PasswordCapabilityType pc = new PasswordCapabilityType(); - cred.setPassword(pc); - pc.setEnabled(false); - pc.setReturnedByDefault(false); - } else { - PasswordCapabilityType pc = cred.getPassword(); - pc.setEnabled(def(pc.isEnabled(), true)); - pc.setReturnedByDefault(def(pc.isReturnedByDefault(), true)); - } - } - } - - private static Boolean def(Boolean originalValue, boolean defaultValue) { - return originalValue != null ? originalValue : defaultValue; - } - - public static T getEffectiveCapability(CapabilitiesType capabilitiesType, Class capabilityClass) { - if (capabilitiesType == null) { - return null; - } - if (capabilitiesType.getConfigured() != null) { - T configuredCapability = CapabilityUtil.getCapability(capabilitiesType.getConfigured().getAny(), capabilityClass); - if (configuredCapability != null) { - return configuredCapability; - } - // No configured capability entry, fallback to native capability - } - if (capabilitiesType.getNative() != null) { - T nativeCapability = CapabilityUtil.getCapability(capabilitiesType.getNative().getAny(), capabilityClass); - if (nativeCapability != null) { - return nativeCapability; - } - } - return null; - } - - public static ActivationStatusCapabilityType getEffectiveActivationStatus(ActivationCapabilityType act) { - if (act != null && act.getStatus() != null && !Boolean.FALSE.equals(act.getStatus().isEnabled())) { - return act.getStatus(); - } else { - return null; - } - } - - public static ActivationValidityCapabilityType getEffectiveActivationValidFrom(ActivationCapabilityType act) { - if (act != null && act.getValidFrom() != null && !Boolean.FALSE.equals(act.getValidFrom().isEnabled())) { - return act.getValidFrom(); - } else { - return null; - } - } - - public static ActivationValidityCapabilityType getEffectiveActivationValidTo(ActivationCapabilityType act) { - if (act != null && act.getValidTo() != null && !Boolean.FALSE.equals(act.getValidTo().isEnabled())) { - return act.getValidTo(); - } else { - return null; - } - } - - public static ActivationLockoutStatusCapabilityType getEffectiveActivationLockoutStatus(ActivationCapabilityType act) { - if (act != null && act.getLockoutStatus() != null && !Boolean.FALSE.equals(act.getLockoutStatus().isEnabled())) { - return act.getLockoutStatus(); - } else { - return null; - } - } - - public static boolean hasNativeCapability(CapabilitiesType capabilities, Class capabilityClass) { - if (capabilities == null) { - return false; - } - CapabilityCollectionType nativeCaps = capabilities.getNative(); - if (nativeCaps == null) { - return false; - } - return getCapability(nativeCaps.getAny(), capabilityClass) != null; - } - - public static boolean hasConfiguredCapability(CapabilitiesType capabilities, Class capabilityClass) { - if (capabilities == null) { - return false; - } - CapabilityCollectionType configuredCaps = capabilities.getConfigured(); - if (configuredCaps == null) { - return false; - } - return getCapability(configuredCaps.getAny(), capabilityClass) != null; - } -} +/* + * Copyright (c) 2010-2013 Evolveum and contributors + * + * This work is dual-licensed under the Apache License 2.0 + * and European Union Public License. See LICENSE file for details. + */ +package com.evolveum.midpoint.schema; + +import java.util.Collection; +import java.util.List; + +import javax.xml.bind.JAXBElement; +import javax.xml.namespace.QName; + +import com.evolveum.midpoint.schema.util.ResourceTypeUtil; +import com.evolveum.midpoint.xml.ns._public.common.common_3.CapabilitiesType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.CapabilityCollectionType; +import com.evolveum.midpoint.xml.ns._public.resource.capabilities_3.*; + +import org.apache.commons.lang.BooleanUtils; +import org.jetbrains.annotations.NotNull; +import org.w3c.dom.Element; + +import com.evolveum.midpoint.prism.xml.XmlTypeConverter; +import com.evolveum.midpoint.util.DOMUtil; +import com.evolveum.midpoint.util.JAXBUtil; +import com.evolveum.midpoint.util.exception.SchemaException; + +/** + * @author semancik + * + */ +public class CapabilityUtil { + + @SuppressWarnings({ "unchecked", "rawtypes" }) + public static T getCapability(Collection capabilities, Class capabilityClass) { + if (capabilities == null) { + return null; + } + for (Object cap : capabilities) { + if (cap instanceof JAXBElement) { + JAXBElement element = (JAXBElement) cap; + if (capabilityClass.isAssignableFrom(element.getDeclaredType())) { + return (T) element.getValue(); + } + } else if (capabilityClass.isAssignableFrom(cap.getClass())) { + return (T) cap; + } + } + return null; + } + + public static boolean isCapabilityEnabled(Object capability) { + if (capability == null) { + return false; + } + if (capability instanceof JAXBElement) { + capability = ((JAXBElement)capability).getValue(); + } + + if (capability instanceof CapabilityType) { + return CapabilityUtil.isCapabilityEnabled((CapabilityType)capability); + } else if (capability instanceof Element) { + return CapabilityUtil.isCapabilityEnabled((Element)capability); + } else { + throw new IllegalArgumentException("Unexpected capability type "+capability.getClass()); + } + } + + private static boolean isCapabilityEnabled(Element capability) { + if (capability == null) { + return false; + } + ObjectFactory capabilitiesObjectFactory = new ObjectFactory(); + QName enabledElementName = capabilitiesObjectFactory.createEnabled(true).getName(); + Element enabledElement = DOMUtil.getChildElement(capability, enabledElementName); + return enabledElement == null || Boolean.parseBoolean(enabledElement.getTextContent()); + } + + public static boolean isCapabilityEnabled(T capability) { + if (capability == null) { + return false; + } + if (capability.isEnabled() == null) { + return true; + } + return capability.isEnabled(); + } + + public static Object getCapabilityWithSameElementName(List capabilities, Object capability) { + if (capabilities == null) { + return false; + } + QName capabilityElementName = JAXBUtil.getElementQName(capability); + for (Object cap: capabilities) { + QName capElementName = JAXBUtil.getElementQName(cap); + if (capabilityElementName.equals(capElementName)) { + return cap; + } + } + return null; + } + + public static boolean containsCapabilityWithSameElementName(List capabilities, Object capability) { + return getCapabilityWithSameElementName(capabilities, capability) != null; + } + + public static String getCapabilityDisplayName(Object capability) { + // TODO: look for schema annotation + String className; + if (capability instanceof JAXBElement) { + className = ((JAXBElement) capability).getDeclaredType().getSimpleName(); + } else { + className = capability.getClass().getSimpleName(); + } + if (className.endsWith("CapabilityType")) { + return className.substring(0, className.length() - "CapabilityType".length()); + } + return className; + } + + public static boolean isPasswordReturnedByDefault(CredentialsCapabilityType capability) { + if (capability == null) { + return false; + } + PasswordCapabilityType password = capability.getPassword(); + if (password == null) { + return false; + } + if (password.isReturnedByDefault() == null) { + return true; + } + return password.isReturnedByDefault(); + } + + public static boolean isPasswordReadable(CredentialsCapabilityType capabilityType) { + if (capabilityType == null) { + return false; + } + + PasswordCapabilityType passwordCapabilityType = capabilityType.getPassword(); + if (passwordCapabilityType == null) { + return false; + } + + if (BooleanUtils.isFalse(passwordCapabilityType.isEnabled())) { + return false; + } + + Boolean readable = passwordCapabilityType.isReadable(); + return BooleanUtils.isTrue(readable); + } + + public static boolean isActivationStatusReturnedByDefault(ActivationCapabilityType capability) { + if (capability == null) { + return false; + } + ActivationStatusCapabilityType statusCap = getEffectiveActivationStatus(capability); + if (statusCap == null) { + return false; + } + if (statusCap.isReturnedByDefault() == null) { + return true; + } + return statusCap.isReturnedByDefault(); + } + + public static boolean isActivationLockoutStatusReturnedByDefault(ActivationCapabilityType capability) { + if (capability == null) { + return false; + } + ActivationLockoutStatusCapabilityType statusCap = capability.getLockoutStatus(); + if (statusCap == null) { + return false; + } + if (statusCap.isReturnedByDefault() == null) { + return true; + } + return statusCap.isReturnedByDefault(); + } + + public static boolean isActivationValidFromReturnedByDefault(ActivationCapabilityType capability) { + if (capability == null) { + return false; + } + ActivationValidityCapabilityType valCap = capability.getValidFrom(); + if (valCap == null) { + return false; + } + if (valCap.isReturnedByDefault() == null) { + return true; + } + return valCap.isReturnedByDefault(); + } + + public static boolean isActivationValidToReturnedByDefault(ActivationCapabilityType capability) { + if (capability == null) { + return false; + } + ActivationValidityCapabilityType valCap = capability.getValidTo(); + if (valCap == null) { + return false; + } + if (valCap.isReturnedByDefault() == null) { + return true; + } + return valCap.isReturnedByDefault(); + } + + @SuppressWarnings("unchecked") + public static CapabilityType asCapabilityType(Object capabilityObject) { + return capabilityObject instanceof CapabilityType ? + (CapabilityType) capabilityObject : + ((JAXBElement) capabilityObject).getValue(); + } + + public static void fillDefaults(@NotNull CapabilityType capability) { + if (capability.isEnabled() == null) { + capability.setEnabled(true); + } + if (capability instanceof ActivationCapabilityType) { + ActivationCapabilityType act = ((ActivationCapabilityType) capability); + if (act.getStatus() == null) { + ActivationStatusCapabilityType st = new ActivationStatusCapabilityType(); + act.setStatus(st); + st.setEnabled(false); // TODO check if all midPoint code honors this flag! + st.setReturnedByDefault(false); + st.setIgnoreAttribute(true); + } else { + ActivationStatusCapabilityType st = act.getStatus(); + st.setEnabled(def(st.isEnabled(), true)); + st.setReturnedByDefault(def(st.isReturnedByDefault(), true)); + st.setIgnoreAttribute(def(st.isIgnoreAttribute(), true)); + } + if (act.getLockoutStatus() == null) { + ActivationLockoutStatusCapabilityType st = new ActivationLockoutStatusCapabilityType(); + act.setLockoutStatus(st); + st.setEnabled(false); + st.setReturnedByDefault(false); + st.setIgnoreAttribute(true); + } else { + ActivationLockoutStatusCapabilityType st = act.getLockoutStatus(); + st.setEnabled(def(st.isEnabled(), true)); + st.setReturnedByDefault(def(st.isReturnedByDefault(), true)); + st.setIgnoreAttribute(def(st.isIgnoreAttribute(), true)); + } + if (act.getValidFrom() == null) { + ActivationValidityCapabilityType vf = new ActivationValidityCapabilityType(); + act.setValidFrom(vf); + vf.setEnabled(false); + vf.setReturnedByDefault(false); + } else { + ActivationValidityCapabilityType vf = act.getValidFrom(); + vf.setEnabled(def(vf.isEnabled(), true)); + vf.setReturnedByDefault(def(vf.isReturnedByDefault(), true)); + } + if (act.getValidTo() == null) { + ActivationValidityCapabilityType vt = new ActivationValidityCapabilityType(); + act.setValidTo(vt); + vt.setEnabled(false); + vt.setReturnedByDefault(false); + } else { + ActivationValidityCapabilityType vt = act.getValidTo(); + vt.setEnabled(def(vt.isEnabled(), true)); + vt.setReturnedByDefault(def(vt.isReturnedByDefault(), true)); + } + } else if (capability instanceof CredentialsCapabilityType) { + CredentialsCapabilityType cred = ((CredentialsCapabilityType) capability); + if (cred.getPassword() == null) { + PasswordCapabilityType pc = new PasswordCapabilityType(); + cred.setPassword(pc); + pc.setEnabled(false); + pc.setReturnedByDefault(false); + } else { + PasswordCapabilityType pc = cred.getPassword(); + pc.setEnabled(def(pc.isEnabled(), true)); + pc.setReturnedByDefault(def(pc.isReturnedByDefault(), true)); + } + } + } + + private static Boolean def(Boolean originalValue, boolean defaultValue) { + return originalValue != null ? originalValue : defaultValue; + } + + public static T getEffectiveCapability(CapabilitiesType capabilitiesType, Class capabilityClass) { + if (capabilitiesType == null) { + return null; + } + if (capabilitiesType.getConfigured() != null) { + T configuredCapability = CapabilityUtil.getCapability(capabilitiesType.getConfigured().getAny(), capabilityClass); + if (configuredCapability != null) { + return configuredCapability; + } + // No configured capability entry, fallback to native capability + } + if (capabilitiesType.getNative() != null) { + T nativeCapability = CapabilityUtil.getCapability(capabilitiesType.getNative().getAny(), capabilityClass); + if (nativeCapability != null) { + return nativeCapability; + } + } + return null; + } + + public static ActivationStatusCapabilityType getEffectiveActivationStatus(ActivationCapabilityType act) { + if (act != null && act.getStatus() != null && !Boolean.FALSE.equals(act.getStatus().isEnabled())) { + return act.getStatus(); + } else { + return null; + } + } + + public static ActivationValidityCapabilityType getEffectiveActivationValidFrom(ActivationCapabilityType act) { + if (act != null && act.getValidFrom() != null && !Boolean.FALSE.equals(act.getValidFrom().isEnabled())) { + return act.getValidFrom(); + } else { + return null; + } + } + + public static ActivationValidityCapabilityType getEffectiveActivationValidTo(ActivationCapabilityType act) { + if (act != null && act.getValidTo() != null && !Boolean.FALSE.equals(act.getValidTo().isEnabled())) { + return act.getValidTo(); + } else { + return null; + } + } + + public static ActivationLockoutStatusCapabilityType getEffectiveActivationLockoutStatus(ActivationCapabilityType act) { + if (act != null && act.getLockoutStatus() != null && !Boolean.FALSE.equals(act.getLockoutStatus().isEnabled())) { + return act.getLockoutStatus(); + } else { + return null; + } + } + + public static boolean hasNativeCapability(CapabilitiesType capabilities, Class capabilityClass) { + if (capabilities == null) { + return false; + } + CapabilityCollectionType nativeCaps = capabilities.getNative(); + if (nativeCaps == null) { + return false; + } + return getCapability(nativeCaps.getAny(), capabilityClass) != null; + } + + public static boolean hasConfiguredCapability(CapabilitiesType capabilities, Class capabilityClass) { + if (capabilities == null) { + return false; + } + CapabilityCollectionType configuredCaps = capabilities.getConfigured(); + if (configuredCaps == null) { + return false; + } + return getCapability(configuredCaps.getAny(), capabilityClass) != null; + } +} diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/credentials/ProjectionCredentialsProcessor.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/credentials/ProjectionCredentialsProcessor.java index 2aba3949e5f..47d0d89e598 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/credentials/ProjectionCredentialsProcessor.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/credentials/ProjectionCredentialsProcessor.java @@ -1,492 +1,483 @@ -/* - * Copyright (c) 2010-2019 Evolveum and contributors - * - * This work is dual-licensed under the Apache License 2.0 - * and European Union Public License. See LICENSE file for details. - */ -package com.evolveum.midpoint.model.impl.lens.projector.credentials; - -import static com.evolveum.midpoint.prism.delta.ChangeType.MODIFY; - -import java.util.Collection; -import java.util.List; - -import javax.xml.datatype.XMLGregorianCalendar; - -import com.evolveum.midpoint.prism.*; -import com.evolveum.midpoint.prism.delta.*; -import com.evolveum.midpoint.prism.path.ItemPath; -import com.evolveum.midpoint.util.LocalizableMessageBuilder; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; - -import com.evolveum.midpoint.common.refinery.RefinedObjectClassDefinition; -import com.evolveum.midpoint.model.api.context.SynchronizationPolicyDecision; -import com.evolveum.midpoint.model.common.mapping.MappingFactory; -import com.evolveum.midpoint.model.common.stringpolicy.ObjectValuePolicyEvaluator; -import com.evolveum.midpoint.model.common.stringpolicy.ShadowValuePolicyOriginResolver; -import com.evolveum.midpoint.model.common.stringpolicy.ValuePolicyProcessor; -import com.evolveum.midpoint.model.impl.ModelObjectResolver; -import com.evolveum.midpoint.model.impl.lens.LensContext; -import com.evolveum.midpoint.model.impl.lens.LensFocusContext; -import com.evolveum.midpoint.model.impl.lens.LensProjectionContext; -import com.evolveum.midpoint.model.impl.lens.OperationalDataManager; -import com.evolveum.midpoint.model.impl.lens.projector.ContextLoader; -import com.evolveum.midpoint.model.impl.lens.projector.mappings.MappingEvaluator; -import com.evolveum.midpoint.model.impl.lens.projector.mappings.MappingInitializer; -import com.evolveum.midpoint.model.impl.lens.projector.mappings.MappingOutputProcessor; -import com.evolveum.midpoint.model.impl.lens.projector.mappings.MappingTimeEval; -import com.evolveum.midpoint.prism.crypto.EncryptionException; -import com.evolveum.midpoint.prism.crypto.Protector; -import com.evolveum.midpoint.prism.util.ItemDeltaItem; -import com.evolveum.midpoint.repo.common.expression.Source; -import com.evolveum.midpoint.repo.common.expression.ValuePolicyResolver; -import com.evolveum.midpoint.schema.ResourceShadowDiscriminator; -import com.evolveum.midpoint.schema.constants.ExpressionConstants; -import com.evolveum.midpoint.schema.constants.SchemaConstants; -import com.evolveum.midpoint.schema.result.OperationResult; -import com.evolveum.midpoint.schema.util.ResourceTypeUtil; -import com.evolveum.midpoint.security.api.SecurityUtil; -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.ExpressionEvaluationException; -import com.evolveum.midpoint.util.exception.ObjectNotFoundException; -import com.evolveum.midpoint.util.exception.PolicyViolationException; -import com.evolveum.midpoint.util.exception.SchemaException; -import com.evolveum.midpoint.util.exception.SecurityViolationException; -import com.evolveum.midpoint.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.MappingType; -import com.evolveum.midpoint.xml.ns._public.common.common_3.MetadataType; -import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType; -import com.evolveum.midpoint.xml.ns._public.common.common_3.SecurityPolicyType; -import com.evolveum.midpoint.xml.ns._public.common.common_3.ShadowType; -import com.evolveum.midpoint.xml.ns._public.common.common_3.ValuePolicyType; -import com.evolveum.midpoint.xml.ns._public.common.common_3.VariableBindingDefinitionType; -import com.evolveum.midpoint.xml.ns._public.resource.capabilities_3.CredentialsCapabilityType; -import com.evolveum.midpoint.xml.ns._public.resource.capabilities_3.PasswordCapabilityType; -import com.evolveum.prism.xml.ns._public.types_3.ItemPathType; -import com.evolveum.prism.xml.ns._public.types_3.ProtectedStringType; - -/** - * Processor for projection credentials. Which at this moment means just the password. - * - * @author Radovan Semancik - */ -@Component -public class ProjectionCredentialsProcessor { - - private static final Trace LOGGER = TraceManager.getTrace(ProjectionCredentialsProcessor.class); - - @Autowired private PrismContext prismContext; - @Autowired private ContextLoader contextLoader; - @Autowired private MappingFactory mappingFactory; - @Autowired private MappingEvaluator mappingEvaluator; - @Autowired private ValuePolicyProcessor valuePolicyProcessor; - @Autowired private Protector protector; - @Autowired private OperationalDataManager operationalDataManager; - @Autowired private ModelObjectResolver modelObjectResolver; - - public void processProjectionCredentials(LensContext context, - LensProjectionContext projectionContext, XMLGregorianCalendar now, Task task, - OperationResult result) throws ExpressionEvaluationException, ObjectNotFoundException, - SchemaException, PolicyViolationException, CommunicationException, ConfigurationException, SecurityViolationException { - - if (projectionContext.isDelete()) { - return; - } - - LensFocusContext focusContext = context.getFocusContext(); - if (focusContext != null && FocusType.class.isAssignableFrom(focusContext.getObjectTypeClass())) { - - processProjectionCredentialsFocus((LensContext) context, projectionContext, now, task, result); - - } - } - - private void processProjectionCredentialsFocus(LensContext context, - LensProjectionContext projectionContext, XMLGregorianCalendar now, Task task, - OperationResult result) throws ExpressionEvaluationException, ObjectNotFoundException, - SchemaException, PolicyViolationException, CommunicationException, ConfigurationException, SecurityViolationException { - - SecurityPolicyType securityPolicy = determineSecurityPolicy(context, projectionContext, now, task, result); - - processProjectionPasswordMapping(context, projectionContext, securityPolicy, now, task, result); - - validateProjectionPassword(context, projectionContext, securityPolicy, now, task, result); - - applyMetadata(context, projectionContext, now, task, result); - } - - private void processProjectionPasswordMapping(LensContext context, - final LensProjectionContext projCtx, final SecurityPolicyType securityPolicy, XMLGregorianCalendar now, Task task, OperationResult result) - throws ExpressionEvaluationException, ObjectNotFoundException, SchemaException, CommunicationException, ConfigurationException, SecurityViolationException { - LensFocusContext focusContext = context.getFocusContext(); - - PrismObject focusNew = focusContext.getObjectNew(); - if (focusNew == null) { - // This must be a focus delete or something similar. No point in proceeding - LOGGER.trace("focusNew is null, skipping credentials processing"); - return; - } - - PrismObjectDefinition accountDefinition = prismContext.getSchemaRegistry() - .findObjectDefinitionByCompileTimeClass(ShadowType.class); - PrismPropertyDefinition projPasswordPropertyDefinition = accountDefinition - .findPropertyDefinition(SchemaConstants.PATH_PASSWORD_VALUE); - - ResourceShadowDiscriminator rsd = projCtx.getResourceShadowDiscriminator(); - - RefinedObjectClassDefinition refinedProjDef = projCtx.getStructuralObjectClassDefinition(); - if (refinedProjDef == null) { - LOGGER.trace("No RefinedObjectClassDefinition, therefore also no password outbound definition, skipping credentials processing for projection {}", rsd); - return; - } - - List outboundMappingTypes = refinedProjDef.getPasswordOutbound(); - if (outboundMappingTypes == null || outboundMappingTypes.isEmpty()) { - LOGGER.trace("No outbound password mapping for {}, skipping credentials processing", rsd); - return; - } - - // HACK - if (!projCtx.isDoReconciliation() && !projCtx.isAdd() && !isActivated(outboundMappingTypes, focusContext.getDelta())) { - LOGGER.trace("Outbound password mappings not activated for type {}, skipping credentials processing", rsd); - return; - } - - ObjectDelta projDelta = projCtx.getDelta(); - PropertyDelta projPasswordDelta; - if (projDelta != null && projDelta.getChangeType() == MODIFY) { - projPasswordDelta = projDelta.findPropertyDelta(SchemaConstants.PATH_PASSWORD_VALUE); - } else { - projPasswordDelta = null; - } - checkExistingDeltaSanity(projCtx, projPasswordDelta); - - boolean evaluateWeak = getEvaluateWeak(projCtx); - - ItemDeltaItem, PrismPropertyDefinition> focusPasswordIdi = focusContext - .getObjectDeltaObject().findIdi(SchemaConstants.PATH_PASSWORD_VALUE); - - ValuePolicyResolver stringPolicyResolver = new ValuePolicyResolver() { - @Override - public void setOutputPath(ItemPath outputPath) { - } - @Override - public void setOutputDefinition(ItemDefinition outputDefinition) { - } - @Override - public ValuePolicyType resolve() { - return SecurityUtil.getPasswordPolicy(securityPolicy); - } - }; - - MappingInitializer,PrismPropertyDefinition> initializer = - (builder) -> { - builder.defaultTargetDefinition(projPasswordPropertyDefinition); - builder.defaultSource(new Source<>(focusPasswordIdi, ExpressionConstants.VAR_INPUT_QNAME)); - builder.valuePolicyResolver(stringPolicyResolver); - return builder; - }; - - MappingOutputProcessor> processor = - (mappingOutputPath, outputStruct) -> { - PrismValueDeltaSetTriple> outputTriple = outputStruct.getOutputTriple(); - if (outputTriple == null) { - LOGGER.trace("Credentials 'password' expression resulted in null output triple, skipping credentials processing for {}", rsd); - return false; - } - - boolean projectionIsNew = projDelta != null && (projDelta.getChangeType() == ChangeType.ADD - || projCtx.getSynchronizationPolicyDecision() == SynchronizationPolicyDecision.ADD); - - Collection> newValues; - if (projectionIsNew) { - newValues = outputTriple.getNonNegativeValues(); - } else { - newValues = outputTriple.getPlusSet(); - } - - if (!canGetCleartext(newValues)) { - ObjectDelta projectionPrimaryDelta = projCtx.getPrimaryDelta(); - if (projectionPrimaryDelta != null) { - PropertyDelta passwordPrimaryDelta = projectionPrimaryDelta.findPropertyDelta(SchemaConstants.PATH_PASSWORD_VALUE); - if (passwordPrimaryDelta != null) { - // We have only hashed value coming from the mapping. There are not very useful - // for provisioning. But we have primary projection delta - and that is very likely - // to be better. - // Skip all password mappings in this case. Primary delta trumps everything. - // No weak, normal or even strong mapping can change that. - // We need to disregard even strong mapping in this case. If we would heed the strong - // mapping then account initialization won't be possible. - LOGGER.trace("We have primary password delta in projection, skipping credentials processing"); - return false; - } - } - } - - Collection> minusSet = outputTriple.getMinusSet(); - if (minusSet != null && !minusSet.isEmpty()) { - if (!canGetCleartext(minusSet)) { - // We have hashed values in minus set. That is not great, we won't be able to get - // cleartext from that if we need it (e.g. for runAs in provisioning). - // Therefore try to get old value from focus password delta. If that matches with - // hashed value then we have the cleartext. - ProtectedStringType oldProjectionPassword = minusSet.iterator().next().getRealValue(); - PropertyDelta focusPasswordDelta = (PropertyDelta) focusPasswordIdi.getDelta(); - Collection> focusPasswordDeltaOldValues = focusPasswordDelta.getEstimatedOldValues(); - if (focusPasswordDeltaOldValues != null && !focusPasswordDeltaOldValues.isEmpty()) { - ProtectedStringType oldFocusPassword = focusPasswordDeltaOldValues.iterator().next().getRealValue(); - try { - if (oldFocusPassword.canGetCleartext() && protector.compareCleartext(oldFocusPassword, oldProjectionPassword)) { - outputTriple.clearMinusSet(); - outputTriple.addToMinusSet(prismContext.itemFactory().createPropertyValue(oldFocusPassword)); - } - } catch (EncryptionException e) { - throw new SystemException(e.getMessage(), e); - } - } - } - } - - return true; - }; - - - mappingEvaluator.evaluateOutboundMapping(context, projCtx, outboundMappingTypes, - SchemaConstants.PATH_PASSWORD_VALUE, SchemaConstants.PATH_PASSWORD_VALUE, initializer, processor, - now, MappingTimeEval.CURRENT, evaluateWeak, "password mapping", task, result); - - } - - private boolean isActivated(List outboundMappingTypes, ObjectDelta focusDelta) { - if (focusDelta == null) { - return false; - } - for (MappingType outboundMappingType: outboundMappingTypes) { - List sources = outboundMappingType.getSource(); - if (sources.isEmpty()) { - // Default source - if (focusDelta.hasItemDelta(SchemaConstants.PATH_PASSWORD_VALUE)) { - return true; - } - } - for (VariableBindingDefinitionType source: sources) { - ItemPathType pathType = source.getPath(); - ItemPath path = pathType.getItemPath().stripVariableSegment(); - if (focusDelta.hasItemDelta(path)) { - return true; - } - } - } - return false; - } - - private boolean canGetCleartext(Collection> pvals) { - if (pvals == null) { - return false; - } - for (PrismPropertyValue pval: pvals) { - if (pval.getValue().canGetCleartext()) { - return true; - } - } - return false; - } - - private boolean getEvaluateWeak(LensProjectionContext projCtx) { - CredentialsCapabilityType credentialsCapabilityType = ResourceTypeUtil.getEffectiveCapability(projCtx.getResource(), CredentialsCapabilityType.class); - if (credentialsCapabilityType != null) { - PasswordCapabilityType passwordCapabilityType = credentialsCapabilityType.getPassword(); - if (passwordCapabilityType != null) { - if (passwordCapabilityType.isEnabled() != Boolean.FALSE) { - Boolean readable = passwordCapabilityType.isReadable(); - if (readable != null && readable) { - // If we have readable password then we can evaluate the weak mappings - // normally (even if the reads return incomplete values). - return true; - } - } - } - } - // Password not readable. Therefore evaluate weak mappings only during add operaitons. - // We do not know whether there is a password already set on the resource. And we do not - // want to overwrite it every time. - return projCtx.isAdd(); - } - - private void validateProjectionPassword(LensContext context, - final LensProjectionContext projectionContext, final SecurityPolicyType securityPolicy, XMLGregorianCalendar now, Task task, OperationResult result) - throws ExpressionEvaluationException, ObjectNotFoundException, SchemaException, PolicyViolationException, CommunicationException, ConfigurationException, SecurityViolationException { - - if (securityPolicy == null) { - LOGGER.trace("Skipping processing password policies. Security policy not specified."); - return; - } - - ObjectDelta accountDelta = projectionContext.getDelta(); - - if (accountDelta == null){ - LOGGER.trace("Skipping processing password policies. Shadow delta not specified."); - return; - } - - if (accountDelta.isDelete()) { - return; - } - - PrismObject accountShadow = null; - PrismProperty password = null; - if (accountDelta.isAdd()) { - accountShadow = accountDelta.getObjectToAdd(); - if (accountShadow != null){ - password = accountShadow.findProperty(SchemaConstants.PATH_PASSWORD_VALUE); - } - } - if (accountDelta.isModify() || password == null) { - PropertyDelta passwordValueDelta = - accountDelta.findPropertyDelta(SchemaConstants.PATH_PASSWORD_VALUE); - // Modification sanity check - if (accountDelta.getChangeType() == ChangeType.MODIFY && passwordValueDelta != null - && (passwordValueDelta.isAdd() || passwordValueDelta.isDelete())) { - throw new SchemaException("Shadow password value cannot be added or deleted, it can only be replaced"); - } - if (passwordValueDelta == null) { - LOGGER.trace("Skipping processing password policies. Shadow delta does not contain password change."); - return; - } - password = (PrismProperty) passwordValueDelta.getItemNewMatchingPath(null); - } - - if (accountShadow == null) { - accountShadow = projectionContext.getObjectNew(); - } - - String passwordValue = determinePasswordValue(password); - - ObjectValuePolicyEvaluator objectValuePolicyEvaluator = new ObjectValuePolicyEvaluator.Builder() - .now(now) - .originResolver(getOriginResolver(accountShadow)) - .protector(protector) - .securityPolicy(securityPolicy) - .shortDesc("password for " + accountShadow) - .task(task) - .valueItemPath(SchemaConstants.PATH_PASSWORD_VALUE) - .valuePolicyProcessor(valuePolicyProcessor) - .build(); - OperationResult validationResult = objectValuePolicyEvaluator.validateStringValue(passwordValue, result); - -// boolean isValid = valuePolicyProcessor.validateValue(passwordValue, securityPolicy, getOriginResolver(accountShadow), "projection password policy", task, result); - - if (!validationResult.isSuccess()) { - LOGGER.debug("Password for projection {} is not valid (policy={}): {}", projectionContext.getHumanReadableName(), securityPolicy, validationResult.getUserFriendlyMessage()); - result.computeStatus(); - throw new PolicyViolationException( - new LocalizableMessageBuilder() - .key("PolicyViolationException.message.projectionPassword") - .arg(projectionContext.getHumanReadableName()) - .arg(validationResult.getUserFriendlyMessage()) - .build()); - } - } - - private ShadowValuePolicyOriginResolver getOriginResolver(PrismObject accountShadow) { - return new ShadowValuePolicyOriginResolver(accountShadow, modelObjectResolver); - } - - private void applyMetadata(LensContext context, - final LensProjectionContext projectionContext, XMLGregorianCalendar now, Task task, OperationResult result) - throws SchemaException { - - ObjectDelta accountDelta = projectionContext.getDelta(); - - if (projectionContext.isDelete()) { - return; - } - - if (accountDelta == null) { - LOGGER.trace("Skipping application of password metadata. Shadow delta not specified."); - return; - } - - PropertyDelta passwordValueDelta = - accountDelta.findPropertyDelta(SchemaConstants.PATH_PASSWORD_VALUE); - if (passwordValueDelta == null) { - LOGGER.trace("Skipping application of password metadata. No password change."); - return; - } - - if (projectionContext.isAdd()) { - MetadataType metadataType = operationalDataManager.createCreateMetadata(context, now, task); - ContainerDelta metadataDelta = prismContext.deltaFactory().container() - .createDelta(SchemaConstants.PATH_PASSWORD_METADATA, projectionContext.getObjectDefinition()); - PrismContainerValue cval = metadataType.asPrismContainerValue(); - cval.setOriginTypeRecursive(OriginType.OUTBOUND); - metadataDelta.addValuesToAdd(metadataType.asPrismContainerValue()); - projectionContext.swallowToSecondaryDelta(metadataDelta); - - } else if (projectionContext.isModify()) { - ContainerDelta metadataDelta = accountDelta.findContainerDelta(SchemaConstants.PATH_PASSWORD_METADATA); - if (metadataDelta == null) { - Collection> modifyMetadataDeltas = operationalDataManager.createModifyMetadataDeltas(context, SchemaConstants.PATH_PASSWORD_METADATA, projectionContext.getObjectTypeClass(), now, task); - for (ItemDelta itemDelta: modifyMetadataDeltas) { - itemDelta.setOriginTypeRecursive(OriginType.OUTBOUND); - projectionContext.swallowToSecondaryDelta(itemDelta); - } - } - } - - } - - private SecurityPolicyType determineSecurityPolicy(LensContext context, - final LensProjectionContext projCtx, XMLGregorianCalendar now, Task task, OperationResult result) { - SecurityPolicyType securityPolicy = projCtx.getProjectionSecurityPolicy(); - if (securityPolicy != null) { - return securityPolicy; - } - return context.getGlobalSecurityPolicy(); - } - - // On missing password this returns empty string (""). It is then up to password policy whether it allows empty passwords or not. - private String determinePasswordValue(PrismProperty password) { - if (password == null || password.getValue(ProtectedStringType.class) == null) { - return null; - } - - ProtectedStringType passValue = password.getRealValue(); - - return determinePasswordValue(passValue); - } - - private String determinePasswordValue(ProtectedStringType passValue) { - if (passValue == null) { - return null; - } - - String passwordStr = passValue.getClearValue(); - - if (passwordStr == null && passValue.getEncryptedDataType () != null) { - // TODO: is this appropriate handling??? - try { - passwordStr = protector.decryptString(passValue); - } catch (EncryptionException ex) { - throw new SystemException("Failed to process password for focus: " + ex.getMessage(), ex); - } - } - - return passwordStr; - } - - private void checkExistingDeltaSanity(LensProjectionContext projCtx, - PropertyDelta passwordDelta) throws SchemaException { - if (passwordDelta != null && (passwordDelta.isAdd() || passwordDelta.isDelete())) { - throw new SchemaException("Password for projection " + projCtx.getResourceShadowDiscriminator() - + " cannot be added or deleted, it can only be replaced"); - } - } - -} +/* + * Copyright (c) 2010-2019 Evolveum and contributors + * + * This work is dual-licensed under the Apache License 2.0 + * and European Union Public License. See LICENSE file for details. + */ +package com.evolveum.midpoint.model.impl.lens.projector.credentials; + +import static com.evolveum.midpoint.prism.delta.ChangeType.MODIFY; + +import java.util.Collection; +import java.util.List; + +import javax.xml.datatype.XMLGregorianCalendar; + +import com.evolveum.midpoint.prism.*; +import com.evolveum.midpoint.prism.delta.*; +import com.evolveum.midpoint.prism.path.ItemPath; +import com.evolveum.midpoint.schema.CapabilityUtil; +import com.evolveum.midpoint.util.LocalizableMessageBuilder; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import com.evolveum.midpoint.common.refinery.RefinedObjectClassDefinition; +import com.evolveum.midpoint.model.api.context.SynchronizationPolicyDecision; +import com.evolveum.midpoint.model.common.mapping.MappingFactory; +import com.evolveum.midpoint.model.common.stringpolicy.ObjectValuePolicyEvaluator; +import com.evolveum.midpoint.model.common.stringpolicy.ShadowValuePolicyOriginResolver; +import com.evolveum.midpoint.model.common.stringpolicy.ValuePolicyProcessor; +import com.evolveum.midpoint.model.impl.ModelObjectResolver; +import com.evolveum.midpoint.model.impl.lens.LensContext; +import com.evolveum.midpoint.model.impl.lens.LensFocusContext; +import com.evolveum.midpoint.model.impl.lens.LensProjectionContext; +import com.evolveum.midpoint.model.impl.lens.OperationalDataManager; +import com.evolveum.midpoint.model.impl.lens.projector.ContextLoader; +import com.evolveum.midpoint.model.impl.lens.projector.mappings.MappingEvaluator; +import com.evolveum.midpoint.model.impl.lens.projector.mappings.MappingInitializer; +import com.evolveum.midpoint.model.impl.lens.projector.mappings.MappingOutputProcessor; +import com.evolveum.midpoint.model.impl.lens.projector.mappings.MappingTimeEval; +import com.evolveum.midpoint.prism.crypto.EncryptionException; +import com.evolveum.midpoint.prism.crypto.Protector; +import com.evolveum.midpoint.prism.util.ItemDeltaItem; +import com.evolveum.midpoint.repo.common.expression.Source; +import com.evolveum.midpoint.repo.common.expression.ValuePolicyResolver; +import com.evolveum.midpoint.schema.ResourceShadowDiscriminator; +import com.evolveum.midpoint.schema.constants.ExpressionConstants; +import com.evolveum.midpoint.schema.constants.SchemaConstants; +import com.evolveum.midpoint.schema.result.OperationResult; +import com.evolveum.midpoint.schema.util.ResourceTypeUtil; +import com.evolveum.midpoint.security.api.SecurityUtil; +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.ExpressionEvaluationException; +import com.evolveum.midpoint.util.exception.ObjectNotFoundException; +import com.evolveum.midpoint.util.exception.PolicyViolationException; +import com.evolveum.midpoint.util.exception.SchemaException; +import com.evolveum.midpoint.util.exception.SecurityViolationException; +import com.evolveum.midpoint.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.MappingType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.MetadataType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.SecurityPolicyType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.ShadowType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.ValuePolicyType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.VariableBindingDefinitionType; +import com.evolveum.midpoint.xml.ns._public.resource.capabilities_3.CredentialsCapabilityType; +import com.evolveum.midpoint.xml.ns._public.resource.capabilities_3.PasswordCapabilityType; +import com.evolveum.prism.xml.ns._public.types_3.ItemPathType; +import com.evolveum.prism.xml.ns._public.types_3.ProtectedStringType; + +/** + * Processor for projection credentials. Which at this moment means just the password. + * + * @author Radovan Semancik + */ +@Component +public class ProjectionCredentialsProcessor { + + private static final Trace LOGGER = TraceManager.getTrace(ProjectionCredentialsProcessor.class); + + @Autowired private PrismContext prismContext; + @Autowired private ContextLoader contextLoader; + @Autowired private MappingFactory mappingFactory; + @Autowired private MappingEvaluator mappingEvaluator; + @Autowired private ValuePolicyProcessor valuePolicyProcessor; + @Autowired private Protector protector; + @Autowired private OperationalDataManager operationalDataManager; + @Autowired private ModelObjectResolver modelObjectResolver; + + public void processProjectionCredentials(LensContext context, + LensProjectionContext projectionContext, XMLGregorianCalendar now, Task task, + OperationResult result) throws ExpressionEvaluationException, ObjectNotFoundException, + SchemaException, PolicyViolationException, CommunicationException, ConfigurationException, SecurityViolationException { + + if (projectionContext.isDelete()) { + return; + } + + LensFocusContext focusContext = context.getFocusContext(); + if (focusContext != null && FocusType.class.isAssignableFrom(focusContext.getObjectTypeClass())) { + + processProjectionCredentialsFocus((LensContext) context, projectionContext, now, task, result); + + } + } + + private void processProjectionCredentialsFocus(LensContext context, + LensProjectionContext projectionContext, XMLGregorianCalendar now, Task task, + OperationResult result) throws ExpressionEvaluationException, ObjectNotFoundException, + SchemaException, PolicyViolationException, CommunicationException, ConfigurationException, SecurityViolationException { + + SecurityPolicyType securityPolicy = determineSecurityPolicy(context, projectionContext, now, task, result); + + processProjectionPasswordMapping(context, projectionContext, securityPolicy, now, task, result); + + validateProjectionPassword(context, projectionContext, securityPolicy, now, task, result); + + applyMetadata(context, projectionContext, now, task, result); + } + + private void processProjectionPasswordMapping(LensContext context, + final LensProjectionContext projCtx, final SecurityPolicyType securityPolicy, XMLGregorianCalendar now, Task task, OperationResult result) + throws ExpressionEvaluationException, ObjectNotFoundException, SchemaException, CommunicationException, ConfigurationException, SecurityViolationException { + LensFocusContext focusContext = context.getFocusContext(); + + PrismObject focusNew = focusContext.getObjectNew(); + if (focusNew == null) { + // This must be a focus delete or something similar. No point in proceeding + LOGGER.trace("focusNew is null, skipping credentials processing"); + return; + } + + PrismObjectDefinition accountDefinition = prismContext.getSchemaRegistry() + .findObjectDefinitionByCompileTimeClass(ShadowType.class); + PrismPropertyDefinition projPasswordPropertyDefinition = accountDefinition + .findPropertyDefinition(SchemaConstants.PATH_PASSWORD_VALUE); + + ResourceShadowDiscriminator rsd = projCtx.getResourceShadowDiscriminator(); + + RefinedObjectClassDefinition refinedProjDef = projCtx.getStructuralObjectClassDefinition(); + if (refinedProjDef == null) { + LOGGER.trace("No RefinedObjectClassDefinition, therefore also no password outbound definition, skipping credentials processing for projection {}", rsd); + return; + } + + List outboundMappingTypes = refinedProjDef.getPasswordOutbound(); + if (outboundMappingTypes == null || outboundMappingTypes.isEmpty()) { + LOGGER.trace("No outbound password mapping for {}, skipping credentials processing", rsd); + return; + } + + // HACK + if (!projCtx.isDoReconciliation() && !projCtx.isAdd() && !isActivated(outboundMappingTypes, focusContext.getDelta())) { + LOGGER.trace("Outbound password mappings not activated for type {}, skipping credentials processing", rsd); + return; + } + + ObjectDelta projDelta = projCtx.getDelta(); + PropertyDelta projPasswordDelta; + if (projDelta != null && projDelta.getChangeType() == MODIFY) { + projPasswordDelta = projDelta.findPropertyDelta(SchemaConstants.PATH_PASSWORD_VALUE); + } else { + projPasswordDelta = null; + } + checkExistingDeltaSanity(projCtx, projPasswordDelta); + + boolean evaluateWeak = getEvaluateWeak(projCtx); + + ItemDeltaItem, PrismPropertyDefinition> focusPasswordIdi = focusContext + .getObjectDeltaObject().findIdi(SchemaConstants.PATH_PASSWORD_VALUE); + + ValuePolicyResolver stringPolicyResolver = new ValuePolicyResolver() { + @Override + public void setOutputPath(ItemPath outputPath) { + } + @Override + public void setOutputDefinition(ItemDefinition outputDefinition) { + } + @Override + public ValuePolicyType resolve() { + return SecurityUtil.getPasswordPolicy(securityPolicy); + } + }; + + MappingInitializer,PrismPropertyDefinition> initializer = + (builder) -> { + builder.defaultTargetDefinition(projPasswordPropertyDefinition); + builder.defaultSource(new Source<>(focusPasswordIdi, ExpressionConstants.VAR_INPUT_QNAME)); + builder.valuePolicyResolver(stringPolicyResolver); + return builder; + }; + + MappingOutputProcessor> processor = + (mappingOutputPath, outputStruct) -> { + PrismValueDeltaSetTriple> outputTriple = outputStruct.getOutputTriple(); + if (outputTriple == null) { + LOGGER.trace("Credentials 'password' expression resulted in null output triple, skipping credentials processing for {}", rsd); + return false; + } + + boolean projectionIsNew = projDelta != null && (projDelta.getChangeType() == ChangeType.ADD + || projCtx.getSynchronizationPolicyDecision() == SynchronizationPolicyDecision.ADD); + + Collection> newValues; + if (projectionIsNew) { + newValues = outputTriple.getNonNegativeValues(); + } else { + newValues = outputTriple.getPlusSet(); + } + + if (!canGetCleartext(newValues)) { + ObjectDelta projectionPrimaryDelta = projCtx.getPrimaryDelta(); + if (projectionPrimaryDelta != null) { + PropertyDelta passwordPrimaryDelta = projectionPrimaryDelta.findPropertyDelta(SchemaConstants.PATH_PASSWORD_VALUE); + if (passwordPrimaryDelta != null) { + // We have only hashed value coming from the mapping. There are not very useful + // for provisioning. But we have primary projection delta - and that is very likely + // to be better. + // Skip all password mappings in this case. Primary delta trumps everything. + // No weak, normal or even strong mapping can change that. + // We need to disregard even strong mapping in this case. If we would heed the strong + // mapping then account initialization won't be possible. + LOGGER.trace("We have primary password delta in projection, skipping credentials processing"); + return false; + } + } + } + + Collection> minusSet = outputTriple.getMinusSet(); + if (minusSet != null && !minusSet.isEmpty()) { + if (!canGetCleartext(minusSet)) { + // We have hashed values in minus set. That is not great, we won't be able to get + // cleartext from that if we need it (e.g. for runAs in provisioning). + // Therefore try to get old value from focus password delta. If that matches with + // hashed value then we have the cleartext. + ProtectedStringType oldProjectionPassword = minusSet.iterator().next().getRealValue(); + PropertyDelta focusPasswordDelta = (PropertyDelta) focusPasswordIdi.getDelta(); + Collection> focusPasswordDeltaOldValues = focusPasswordDelta.getEstimatedOldValues(); + if (focusPasswordDeltaOldValues != null && !focusPasswordDeltaOldValues.isEmpty()) { + ProtectedStringType oldFocusPassword = focusPasswordDeltaOldValues.iterator().next().getRealValue(); + try { + if (oldFocusPassword.canGetCleartext() && protector.compareCleartext(oldFocusPassword, oldProjectionPassword)) { + outputTriple.clearMinusSet(); + outputTriple.addToMinusSet(prismContext.itemFactory().createPropertyValue(oldFocusPassword)); + } + } catch (EncryptionException e) { + throw new SystemException(e.getMessage(), e); + } + } + } + } + + return true; + }; + + + mappingEvaluator.evaluateOutboundMapping(context, projCtx, outboundMappingTypes, + SchemaConstants.PATH_PASSWORD_VALUE, SchemaConstants.PATH_PASSWORD_VALUE, initializer, processor, + now, MappingTimeEval.CURRENT, evaluateWeak, "password mapping", task, result); + + } + + private boolean isActivated(List outboundMappingTypes, ObjectDelta focusDelta) { + if (focusDelta == null) { + return false; + } + for (MappingType outboundMappingType: outboundMappingTypes) { + List sources = outboundMappingType.getSource(); + if (sources.isEmpty()) { + // Default source + if (focusDelta.hasItemDelta(SchemaConstants.PATH_PASSWORD_VALUE)) { + return true; + } + } + for (VariableBindingDefinitionType source: sources) { + ItemPathType pathType = source.getPath(); + ItemPath path = pathType.getItemPath().stripVariableSegment(); + if (focusDelta.hasItemDelta(path)) { + return true; + } + } + } + return false; + } + + private boolean canGetCleartext(Collection> pvals) { + if (pvals == null) { + return false; + } + for (PrismPropertyValue pval: pvals) { + if (pval.getValue().canGetCleartext()) { + return true; + } + } + return false; + } + + private boolean getEvaluateWeak(LensProjectionContext projCtx) { + CredentialsCapabilityType credentialsCapabilityType = ResourceTypeUtil.getEffectiveCapability(projCtx.getResource(), CredentialsCapabilityType.class); + if (CapabilityUtil.isPasswordReadable(credentialsCapabilityType)) { + return true; + } + // Password not readable. Therefore evaluate weak mappings only during add operaitons. + // We do not know whether there is a password already set on the resource. And we do not + // want to overwrite it every time. + return projCtx.isAdd(); + } + + private void validateProjectionPassword(LensContext context, + final LensProjectionContext projectionContext, final SecurityPolicyType securityPolicy, XMLGregorianCalendar now, Task task, OperationResult result) + throws ExpressionEvaluationException, ObjectNotFoundException, SchemaException, PolicyViolationException, CommunicationException, ConfigurationException, SecurityViolationException { + + if (securityPolicy == null) { + LOGGER.trace("Skipping processing password policies. Security policy not specified."); + return; + } + + ObjectDelta accountDelta = projectionContext.getDelta(); + + if (accountDelta == null){ + LOGGER.trace("Skipping processing password policies. Shadow delta not specified."); + return; + } + + if (accountDelta.isDelete()) { + return; + } + + PrismObject accountShadow = null; + PrismProperty password = null; + if (accountDelta.isAdd()) { + accountShadow = accountDelta.getObjectToAdd(); + if (accountShadow != null){ + password = accountShadow.findProperty(SchemaConstants.PATH_PASSWORD_VALUE); + } + } + if (accountDelta.isModify() || password == null) { + PropertyDelta passwordValueDelta = + accountDelta.findPropertyDelta(SchemaConstants.PATH_PASSWORD_VALUE); + // Modification sanity check + if (accountDelta.getChangeType() == ChangeType.MODIFY && passwordValueDelta != null + && (passwordValueDelta.isAdd() || passwordValueDelta.isDelete())) { + throw new SchemaException("Shadow password value cannot be added or deleted, it can only be replaced"); + } + if (passwordValueDelta == null) { + LOGGER.trace("Skipping processing password policies. Shadow delta does not contain password change."); + return; + } + password = (PrismProperty) passwordValueDelta.getItemNewMatchingPath(null); + } + + if (accountShadow == null) { + accountShadow = projectionContext.getObjectNew(); + } + + String passwordValue = determinePasswordValue(password); + + ObjectValuePolicyEvaluator objectValuePolicyEvaluator = new ObjectValuePolicyEvaluator.Builder() + .now(now) + .originResolver(getOriginResolver(accountShadow)) + .protector(protector) + .securityPolicy(securityPolicy) + .shortDesc("password for " + accountShadow) + .task(task) + .valueItemPath(SchemaConstants.PATH_PASSWORD_VALUE) + .valuePolicyProcessor(valuePolicyProcessor) + .build(); + OperationResult validationResult = objectValuePolicyEvaluator.validateStringValue(passwordValue, result); + +// boolean isValid = valuePolicyProcessor.validateValue(passwordValue, securityPolicy, getOriginResolver(accountShadow), "projection password policy", task, result); + + if (!validationResult.isSuccess()) { + LOGGER.debug("Password for projection {} is not valid (policy={}): {}", projectionContext.getHumanReadableName(), securityPolicy, validationResult.getUserFriendlyMessage()); + result.computeStatus(); + throw new PolicyViolationException( + new LocalizableMessageBuilder() + .key("PolicyViolationException.message.projectionPassword") + .arg(projectionContext.getHumanReadableName()) + .arg(validationResult.getUserFriendlyMessage()) + .build()); + } + } + + private ShadowValuePolicyOriginResolver getOriginResolver(PrismObject accountShadow) { + return new ShadowValuePolicyOriginResolver(accountShadow, modelObjectResolver); + } + + private void applyMetadata(LensContext context, + final LensProjectionContext projectionContext, XMLGregorianCalendar now, Task task, OperationResult result) + throws SchemaException { + + ObjectDelta accountDelta = projectionContext.getDelta(); + + if (projectionContext.isDelete()) { + return; + } + + if (accountDelta == null) { + LOGGER.trace("Skipping application of password metadata. Shadow delta not specified."); + return; + } + + PropertyDelta passwordValueDelta = + accountDelta.findPropertyDelta(SchemaConstants.PATH_PASSWORD_VALUE); + if (passwordValueDelta == null) { + LOGGER.trace("Skipping application of password metadata. No password change."); + return; + } + + if (projectionContext.isAdd()) { + MetadataType metadataType = operationalDataManager.createCreateMetadata(context, now, task); + ContainerDelta metadataDelta = prismContext.deltaFactory().container() + .createDelta(SchemaConstants.PATH_PASSWORD_METADATA, projectionContext.getObjectDefinition()); + PrismContainerValue cval = metadataType.asPrismContainerValue(); + cval.setOriginTypeRecursive(OriginType.OUTBOUND); + metadataDelta.addValuesToAdd(metadataType.asPrismContainerValue()); + projectionContext.swallowToSecondaryDelta(metadataDelta); + + } else if (projectionContext.isModify()) { + ContainerDelta metadataDelta = accountDelta.findContainerDelta(SchemaConstants.PATH_PASSWORD_METADATA); + if (metadataDelta == null) { + Collection> modifyMetadataDeltas = operationalDataManager.createModifyMetadataDeltas(context, SchemaConstants.PATH_PASSWORD_METADATA, projectionContext.getObjectTypeClass(), now, task); + for (ItemDelta itemDelta: modifyMetadataDeltas) { + itemDelta.setOriginTypeRecursive(OriginType.OUTBOUND); + projectionContext.swallowToSecondaryDelta(itemDelta); + } + } + } + + } + + private SecurityPolicyType determineSecurityPolicy(LensContext context, + final LensProjectionContext projCtx, XMLGregorianCalendar now, Task task, OperationResult result) { + SecurityPolicyType securityPolicy = projCtx.getProjectionSecurityPolicy(); + if (securityPolicy != null) { + return securityPolicy; + } + return context.getGlobalSecurityPolicy(); + } + + // On missing password this returns empty string (""). It is then up to password policy whether it allows empty passwords or not. + private String determinePasswordValue(PrismProperty password) { + if (password == null || password.getValue(ProtectedStringType.class) == null) { + return null; + } + + ProtectedStringType passValue = password.getRealValue(); + + return determinePasswordValue(passValue); + } + + private String determinePasswordValue(ProtectedStringType passValue) { + if (passValue == null) { + return null; + } + + String passwordStr = passValue.getClearValue(); + + if (passwordStr == null && passValue.getEncryptedDataType () != null) { + // TODO: is this appropriate handling??? + try { + passwordStr = protector.decryptString(passValue); + } catch (EncryptionException ex) { + throw new SystemException("Failed to process password for focus: " + ex.getMessage(), ex); + } + } + + return passwordStr; + } + + private void checkExistingDeltaSanity(LensProjectionContext projCtx, + PropertyDelta passwordDelta) throws SchemaException { + if (passwordDelta != null && (passwordDelta.isAdd() || passwordDelta.isDelete())) { + throw new SchemaException("Password for projection " + projCtx.getResourceShadowDiscriminator() + + " cannot be added or deleted, it can only be replaced"); + } + } + +}