/*
* Copyright (c) 2010-2016 Evolveum
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.evolveum.midpoint.web.page.self;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import com.evolveum.prism.xml.ns._public.types_3.EncryptedDataType;
import org.apache.commons.lang3.StringUtils;
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.GetOperationOptions;
import com.evolveum.midpoint.schema.SchemaConstantsGenerated;
import com.evolveum.midpoint.schema.SelectorOptions;
import com.evolveum.midpoint.schema.result.OperationResult;
import com.evolveum.midpoint.task.api.Task;
import com.evolveum.midpoint.util.exception.SchemaException;
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.SecurityUtils;
import com.evolveum.midpoint.xml.ns._public.common.common_3.*;
import com.evolveum.prism.xml.ns._public.types_3.ProtectedStringType;
import org.apache.xml.security.encryption.EncryptedData;
/**
* @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<MyPasswordsDto> model;
private PrismObject<UserType> user;
public PageAbstractSelfCredentials() {
model = new LoadableModel<MyPasswordsDto>(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<String>("fa fa-shield"));
}
public PageAbstractSelfCredentials(final MyPasswordsDto myPasswordsDto) {
model = new LoadableModel<MyPasswordsDto>(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 userOid = SecurityUtils.getPrincipalUser().getOid();
Task task = createSimpleTask(OPERATION_LOAD_USER);
OperationResult subResult = result.createSubresult(OPERATION_LOAD_USER);
user = getModelService().getObject(UserType.class, userOid, null, task, subResult);
subResult.recordSuccessIfUnknown();
dto.getAccounts().add(createDefaultPasswordAccountDto(user));
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 = user.findReference(UserType.F_LINK_REF);
if (reference == null || reference.getValues() == null) {
LOGGER.debug("No accounts found for user {}.", new Object[]{userOid});
return dto;
}
final Collection<SelectorOptions<GetOperationOptions>> options =
SelectorOptions.createCollection(ShadowType.F_RESOURCE, GetOperationOptions.createResolve());
List<PrismReferenceValue> values = reference.getValues();
for (PrismReferenceValue value : values) {
subResult = result.createSubresult(OPERATION_LOAD_ACCOUNT);
try {
String accountOid = value.getOid();
task = createSimpleTask(OPERATION_LOAD_ACCOUNT);
PrismObject<ShadowType> account = getModelService().getObject(ShadowType.class,
accountOid, options, task, subResult);
dto.getAccounts().add(createPasswordAccountDto(account));
subResult.recordSuccessIfUnknown();
} catch (Exception ex) {
LoggingUtils.logUnexpectedException(LOGGER, "Couldn't load account", ex);
subResult.recordFatalError("Couldn't load account.", ex);
}
}
}
result.recordSuccessIfUnknown();
} catch (Exception ex) {
LoggingUtils.logUnexpectedException(LOGGER, "Couldn't load accounts", ex);
result.recordFatalError("Couldn't load accounts", ex);
} finally {
result.recomputeStatus();
}
Collections.sort(dto.getAccounts());
if (!result.isSuccess() && !result.isHandledError()) {
showResult(result);
}
return dto;
}
private void initLayout() {
Form<?> mainForm = new Form<>(ID_MAIN_FORM);
List<ITab> 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<ITab> 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, Form<?> form) {
target.add(getFeedbackPanel());
}
@Override
protected void onSubmit(AjaxRequestTarget target, Form<?> form) {
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, Form<?> form) {
onCancelPerformed(target);
}
@Override
protected void onSubmit(AjaxRequestTarget target, Form<?> form) {
onCancelPerformed(target);
}
};
mainForm.add(cancel);
}
private PasswordAccountDto createDefaultPasswordAccountDto(PrismObject<UserType> user) {
return new PasswordAccountDto(user.getOid(), user.getName().getOrig(),
getString("PageSelfCredentials.resourceMidpoint"), WebComponentUtil.isActivationEnabled(user), true);
}
private PasswordAccountDto createPasswordAccountDto(PrismObject<ShadowType> account) {
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));
return passwordAccountDto;
}
protected void onSavePerformed(AjaxRequestTarget target) {
List<PasswordAccountDto> selectedAccounts = getSelectedAccountsList();
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 {
ProtectedStringType oldPassword = new ProtectedStringType();
oldPassword.setClearValue(model.getObject().getOldPassword());
boolean isCorrectPassword = getModelInteractionService().checkPassword(user.getOid(), oldPassword,
checkPasswordTask, checkPasswordResult);
if (!isCorrectPassword) {
warn(getString("PageSelfCredentials.incorrectOldPassword"));
target.add(getFeedbackPanel());
return;
}
} catch (Exception ex) {
LoggingUtils.logUnexpectedException(LOGGER, "Couldn't check password", ex);
checkPasswordResult.recordFatalError("Couldn't check password." + 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 = new ItemPath(SchemaConstantsGenerated.C_CREDENTIALS,
CredentialsType.F_PASSWORD, PasswordType.F_VALUE);
SchemaRegistry registry = getPrismContext().getSchemaRegistry();
Collection<ObjectDelta<? extends ObjectType>> deltas = new ArrayList<ObjectDelta<? extends ObjectType>>();
for (PasswordAccountDto accDto : selectedAccounts) {
PrismObjectDefinition objDef = accDto.isMidpoint() ?
registry.findObjectDefinitionByCompileTimeClass(UserType.class) :
registry.findObjectDefinitionByCompileTimeClass(ShadowType.class);
PropertyDelta delta = PropertyDelta.createModificationReplaceProperty(valuePath, objDef, password);
Class<? extends ObjectType> type = accDto.isMidpoint() ? UserType.class : ShadowType.class;
deltas.add(ObjectDelta.createModifyDelta(accDto.getOid(), delta, type, getPrismContext()));
}
getModelService().executeChanges(deltas, null, createSimpleTask(OPERATION_SAVE_PASSWORD), 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<PasswordAccountDto> getSelectedAccountsList(){
List<PasswordAccountDto> passwordAccountDtos = model.getObject().getAccounts();
List<PasswordAccountDto> 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 List<ShadowType> loadShadowTypeList() {
// List<ObjectReferenceType> references = user.asObjectable().getLinkRef();
// Task task = createSimpleTask(OPERATION_LOAD_SHADOW);
// List<ShadowType> shadowTypeList = new ArrayList<>();
//
// for (ObjectReferenceType reference : references) {
// OperationResult subResult = new OperationResult(OPERATION_LOAD_SHADOW);
// try {
// Collection<SelectorOptions<GetOperationOptions>> options = SelectorOptions.createCollection(ShadowType.F_RESOURCE,
// GetOperationOptions.createResolve());
//
// if (reference.getOid() == null) {
// continue;
// }
// PrismObject<ShadowType> shadow = WebModelServiceUtils.loadObject(ShadowType.class, reference.getOid(), options, this, task, subResult);
// shadowTypeList.add(shadow.asObjectable());
// } catch (Exception ex) {
// LoggingUtils.logUnexpectedException(LOGGER, "Couldn't load account", ex);
// subResult.recordFatalError("Couldn't load account." + ex.getMessage(), ex);
// } finally {
// subResult.computeStatus();
// }
// }
// return shadowTypeList;
//
// }
private boolean getPasswordOutbound(PrismObject<ShadowType> shadow) {
try {
RefinedObjectClassDefinition rOCDef = getModelInteractionService().getEditObjectClassDefinition(shadow,
shadow.asObjectable().getResource().asPrismObject(),
AuthorizationPhaseType.REQUEST);
if (rOCDef != null && rOCDef.getPasswordOutbound() != null ){
return true;
}
} catch (SchemaException ex) {
}
return false;
}
public PrismObject<UserType> getUser() {
return user;
}
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(user, task, result);
result.recordSuccessIfUnknown();
} catch (Exception ex) {
LoggingUtils.logUnexpectedException(LOGGER, "Couldn't load credentials policy", ex);
result.recordFatalError("Couldn't load credentials policy." + ex.getMessage(), ex);
} finally {
result.computeStatus();
}
return credentialsPolicyType;
}
protected MyPasswordsDto getModelObject() {
return model.getObject();
}
}