/* * Copyright (c) 2010-2017 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.List; import java.util.stream.Collectors; import org.apache.wicket.RestartResponseException; import org.apache.wicket.ajax.AjaxRequestTarget; import org.apache.wicket.ajax.markup.html.AjaxLink; import org.apache.wicket.markup.html.WebMarkupContainer; import org.apache.wicket.markup.html.basic.Label; import org.apache.wicket.markup.html.form.Form; import org.apache.wicket.markup.html.form.PasswordTextField; import org.apache.wicket.markup.repeater.RepeatingView; import org.apache.wicket.model.IModel; import org.apache.wicket.model.Model; import org.apache.wicket.model.PropertyModel; import org.apache.wicket.request.mapper.parameter.PageParameters; import org.apache.wicket.spring.injection.annot.SpringBean; import org.apache.wicket.util.string.StringValue; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.context.SecurityContextHolder; import com.evolveum.midpoint.gui.api.component.password.PasswordPanel; import com.evolveum.midpoint.gui.api.model.LoadableModel; import com.evolveum.midpoint.gui.api.page.PageBase; import com.evolveum.midpoint.gui.api.util.WebComponentUtil; import com.evolveum.midpoint.gui.api.util.WebModelServiceUtils; import com.evolveum.midpoint.model.api.AuthenticationEvaluator; import com.evolveum.midpoint.model.api.context.PasswordAuthenticationContext; import com.evolveum.midpoint.prism.delta.ObjectDelta; import com.evolveum.midpoint.schema.GetOperationOptions; import com.evolveum.midpoint.schema.SelectorOptions; import com.evolveum.midpoint.schema.constants.SchemaConstants; import com.evolveum.midpoint.schema.result.OperationResult; import com.evolveum.midpoint.security.api.ConnectionEnvironment; import com.evolveum.midpoint.task.api.Task; import com.evolveum.midpoint.util.Producer; import com.evolveum.midpoint.util.logging.Trace; import com.evolveum.midpoint.util.logging.TraceManager; import com.evolveum.midpoint.web.application.PageDescriptor; import com.evolveum.midpoint.web.application.Url; import com.evolveum.midpoint.web.component.AjaxSubmitButton; import com.evolveum.midpoint.web.component.input.TextPanel; import com.evolveum.midpoint.web.component.util.VisibleEnableBehaviour; import com.evolveum.midpoint.web.page.admin.configuration.component.EmptyOnBlurAjaxFormUpdatingBehaviour; import com.evolveum.midpoint.web.page.admin.users.PageUser; import com.evolveum.midpoint.web.page.login.PageLogin; import com.evolveum.midpoint.web.util.OnePageParameterEncoder; import com.evolveum.midpoint.xml.ns._public.common.common_3.ShadowType; import com.evolveum.midpoint.xml.ns._public.common.common_3.UserType; import com.evolveum.prism.xml.ns._public.types_3.ProtectedStringType; @PageDescriptor(urls = {@Url(mountUrl = SchemaConstants.ACCOUNT_ACTIVATION_PREFIX)}) public class PageAccountActivation extends PageBase { private static final long serialVersionUID = 1L; private static final Trace LOGGER = TraceManager.getTrace(PageAccountActivation.class); private IModel<UserType> userModel; private static final String DOT_CLASS = PageUser.class.getName() + "."; private static final String LOAD_USER = DOT_CLASS + "loadUser"; private static final String OPERATION_ACTIVATE_SHADOWS = DOT_CLASS + "activateShadows"; private static final String ID_MAIN_FORM = "mainForm"; private static final String ID_NAME = "username"; private static final String ID_PASSWORD = "password"; private static final String ID_CONFIRM = "confirm"; private static final String ID_ACTIVATION_CONTAINER = "activationContainer"; private static final String ID_CONFIRMATION_CONTAINER = "confirmationContainer"; private static final String ID_ACTIVATED_SHADOWS = "activatedShadows"; private static final String ID_LINK_TO_LOGIN = "linkToLogin"; private boolean activated = false; @SpringBean(name = "passwordAuthenticationEvaluator") private AuthenticationEvaluator<PasswordAuthenticationContext> authenticationEvaluator; public PageAccountActivation(PageParameters params) { UserType user = loadUser(params); userModel = new LoadableModel<UserType>(false) { private static final long serialVersionUID = 1L; @Override protected UserType load() { return user; } }; initLayout(); } private UserType loadUser(PageParameters params){ String userOid = getOidFromParameter(params); if (userOid == null) { getSession().error(getString("PageAccountActivation.user.not found")); throw new RestartResponseException(PageLogin.class); } Task task = createAnonymousTask(LOAD_USER); OperationResult result = new OperationResult(LOAD_USER); return runPrivileged(new Producer<UserType>() { @Override public UserType run() { Collection<SelectorOptions<GetOperationOptions>> options = SelectorOptions.createCollection(UserType.F_LINK_REF, GetOperationOptions.createResolve()); return WebModelServiceUtils.loadObject(UserType.class, userOid, options, PageAccountActivation.this, task, result).asObjectable(); } }); } private void initLayout(){ WebMarkupContainer activationContainer= new WebMarkupContainer(ID_ACTIVATION_CONTAINER); activationContainer.setOutputMarkupId(true); add(activationContainer); activationContainer.add(new VisibleEnableBehaviour() { private static final long serialVersionUID = 1L; @Override public boolean isVisible() { return !activated; } }); Form form = new Form<>(ID_MAIN_FORM); activationContainer.add(form); Label usernamePanel = new Label(ID_NAME, createStringResource("PageAccountActivation.activate.accounts.label", new PropertyModel<>(userModel, "name.orig"))); usernamePanel.add(new VisibleEnableBehaviour() { private static final long serialVersionUID = 1L; @Override public boolean isEnabled() { return false; } }); form.add(usernamePanel); PasswordTextField passwordPanel = new PasswordTextField(ID_PASSWORD, Model.of(new String())); form.add(passwordPanel); AjaxSubmitButton confirmPasswrod = new AjaxSubmitButton(ID_CONFIRM) { private static final long serialVersionUID = 1L; @Override protected void onSubmit(AjaxRequestTarget target, Form<?> form) { propagatePassword(target, form); } @Override protected void onError(AjaxRequestTarget target, Form<?> form) { getSession().error(getString("PageAccountActivation.account.activation.failed")); target.add(getFeedbackPanel()); } }; form.add(confirmPasswrod); WebMarkupContainer confirmationContainer = new WebMarkupContainer(ID_CONFIRMATION_CONTAINER); confirmationContainer.setOutputMarkupId(true); confirmationContainer.add(new VisibleEnableBehaviour() { private static final long serialVersionUID = 1L; @Override public boolean isVisible() { return activated; } }); add(confirmationContainer); AjaxLink<Void> linkToLogin = new AjaxLink<Void>(ID_LINK_TO_LOGIN) { private static final long serialVersionUID = 1L; @Override public void onClick(AjaxRequestTarget target) { setResponsePage(PageLogin.class); } }; confirmationContainer.add(linkToLogin); RepeatingView activatedShadows = new RepeatingView(ID_ACTIVATED_SHADOWS); confirmationContainer.add(activatedShadows); List<ShadowType> shadowsToActivate = getShadowsToActivate(); if (shadowsToActivate.isEmpty()) { LOGGER.error("No accounts to validate for user {}", userModel.getObject()); getSession().warn(getString("PageAccountActivation.nothing.to.activate")); throw new RestartResponseException(PageLogin.class); } for (ShadowType shadow : shadowsToActivate) { Label shadowDesc = new Label(activatedShadows.newChildId(), WebComponentUtil.getName(shadow) + " on resource " + WebComponentUtil.getName(shadow.getResourceRef())); activatedShadows.add(shadowDesc); } } private String getOidFromParameter(PageParameters params){ if (params == null || params.isEmpty()) { LOGGER.error("No page paraeters found for account activation. No user to activate his/her accounts"); return null; } StringValue userValue = params.get(SchemaConstants.USER_ID); if (userValue == null || userValue.isEmpty()) { LOGGER.error("No user defined in the page parameter. Expected user=? attribute filled but didmn't find one."); return null; } return userValue.toString(); } private void propagatePassword(AjaxRequestTarget target, Form<?> form) { List<ShadowType> shadowsToActivate = getShadowsToActivate(); PasswordTextField passwordPanel = (PasswordTextField) form.get(createComponentPath(ID_PASSWORD)); String value = passwordPanel.getModelObject(); ConnectionEnvironment connEnv = new ConnectionEnvironment(); connEnv.setChannel(SchemaConstants.CHANNEL_GUI_USER_URI); UsernamePasswordAuthenticationToken token = null; try { token = authenticationEvaluator.authenticate(connEnv, new PasswordAuthenticationContext(userModel.getObject().getName().getOrig(), value)); } catch (Exception ex) { LOGGER.error("Failed to authenticate user, reason ", ex.getMessage()); getSession().error(getString("PageAccountActivation.authentication.failed")); throw new RestartResponseException(PageAccountActivation.class, getPageParameters()); } if (token == null) { LOGGER.error("Failed to authenticate user"); getSession().error(getString("PageAccountActivation.authentication.failed")); throw new RestartResponseException(PageAccountActivation.class, getPageParameters()); } ProtectedStringType passwordValue = new ProtectedStringType(); passwordValue.setClearValue(value); Collection<ObjectDelta<ShadowType>> passwordDeltas = new ArrayList<>(shadowsToActivate.size()); for (ShadowType shadow : shadowsToActivate) { ObjectDelta<ShadowType> shadowDelta = ObjectDelta.createModificationReplaceProperty(ShadowType.class, shadow.getOid(), SchemaConstants.PATH_PASSWORD_VALUE, getPrismContext(), passwordValue); shadowDelta.addModificationReplaceProperty(ShadowType.F_LIFECYCLE_STATE, SchemaConstants.LIFECYCLE_PROPOSED); passwordDeltas.add(shadowDelta); } OperationResult result = runPrivileged(new Producer<OperationResult>() { @Override public OperationResult run() { OperationResult result = new OperationResult(OPERATION_ACTIVATE_SHADOWS); Task task = createAnonymousTask(OPERATION_ACTIVATE_SHADOWS); WebModelServiceUtils.save((Collection) passwordDeltas, null, result, task, PageAccountActivation.this); return result; } }); result.recomputeStatus(); if (!result.isSuccess()) { getSession().error(getString("PageAccountActivation.account.activation.failed")); LOGGER.error("Failed to acitvate accounts, reason: {} ", result.getMessage()); target.add(getFeedbackPanel()); } else { getSession().success(getString("PageAccountActivation.account.activation.successful")); target.add(getFeedbackPanel()); activated = true; } target.add(PageAccountActivation.this); } private List<ShadowType> getShadowsToActivate(){ UserType userType = userModel.getObject(); List<ShadowType> shadowsToActivate = userType.getLink(); if (shadowsToActivate == null || shadowsToActivate.isEmpty()) { return new ArrayList<>(); } return shadowsToActivate.parallelStream().filter(shadow -> shadow.getLifecycleState().equals(SchemaConstants.LIFECYCLE_PROPOSED)).collect(Collectors.toList()); } }