/******************************************************************************* * Copyright (c) 2015 Development Gateway, Inc and others. * * All rights reserved. This program and the accompanying materials * are made available under the terms of the MIT License (MIT) * which accompanies this distribution, and is available at * https://opensource.org/licenses/MIT * * Contributors: * Development Gateway - initial API and implementation *******************************************************************************/ package org.devgateway.toolkit.forms.wicket.page.user; import org.apache.wicket.Component; import org.apache.wicket.ajax.AjaxRequestTarget; import org.apache.wicket.authroles.authorization.strategies.role.annotations.AuthorizeInstantiation; import org.apache.wicket.authroles.authorization.strategies.role.metadata.MetaDataRoleAuthorizationStrategy; import org.apache.wicket.markup.html.form.Form; import org.apache.wicket.markup.html.form.validation.EqualPasswordInputValidator; import org.apache.wicket.model.Model; import org.apache.wicket.model.StringResourceModel; 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.apache.wicket.validation.IValidatable; import org.apache.wicket.validation.IValidator; import org.apache.wicket.validation.ValidationError; import org.apache.wicket.validation.validator.EmailAddressValidator; import org.apache.wicket.validation.validator.PatternValidator; import org.devgateway.toolkit.forms.WebConstants; import org.devgateway.toolkit.forms.security.SecurityConstants; import org.devgateway.toolkit.forms.security.SecurityUtil; import org.devgateway.toolkit.forms.service.SendEmailService; import org.devgateway.toolkit.forms.wicket.components.form.CheckBoxBootstrapFormComponent; import org.devgateway.toolkit.forms.wicket.components.form.PasswordFieldBootstrapFormComponent; import org.devgateway.toolkit.forms.wicket.components.form.Select2ChoiceBootstrapFormComponent; import org.devgateway.toolkit.forms.wicket.components.form.Select2MultiChoiceBootstrapFormComponent; import org.devgateway.toolkit.forms.wicket.components.form.TextFieldBootstrapFormComponent; import org.devgateway.toolkit.forms.wicket.page.Homepage; import org.devgateway.toolkit.forms.wicket.page.edit.AbstractEditPage; import org.devgateway.toolkit.forms.wicket.page.lists.ListUserPage; import org.devgateway.toolkit.forms.wicket.providers.GenericPersistableJpaRepositoryTextChoiceProvider; import org.devgateway.toolkit.persistence.dao.Person; import org.devgateway.toolkit.persistence.dao.categories.Group; import org.devgateway.toolkit.persistence.dao.categories.Role; import org.devgateway.toolkit.persistence.repository.GroupRepository; import org.devgateway.toolkit.persistence.repository.PersonRepository; import org.devgateway.toolkit.persistence.repository.RoleRepository; import org.springframework.security.crypto.password.StandardPasswordEncoder; import org.wicketstuff.annotation.mount.MountPath; import java.util.List; @AuthorizeInstantiation(SecurityConstants.Roles.ROLE_USER) @MountPath(value = "/account") public class EditUserPage extends AbstractEditPage<Person> { private static final long serialVersionUID = 5208480049061989277L; @SpringBean private PersonRepository userRepository; @SpringBean private GroupRepository groupRepository; @SpringBean private RoleRepository roleRepository; @SpringBean private SendEmailService sendEmailService; protected TextFieldBootstrapFormComponent<String> userName = new TextFieldBootstrapFormComponent<>("username"); protected TextFieldBootstrapFormComponent<String> firstName = new TextFieldBootstrapFormComponent<>("firstName"); protected TextFieldBootstrapFormComponent<String> lastName = new TextFieldBootstrapFormComponent<>("lastName"); protected TextFieldBootstrapFormComponent<String> email = new TextFieldBootstrapFormComponent<>("email"); protected TextFieldBootstrapFormComponent<String> title = new TextFieldBootstrapFormComponent<>("title"); protected Select2ChoiceBootstrapFormComponent<Group> group = new Select2ChoiceBootstrapFormComponent<>("group", new GenericPersistableJpaRepositoryTextChoiceProvider<>(groupRepository)); protected Select2MultiChoiceBootstrapFormComponent<Role> roles = new Select2MultiChoiceBootstrapFormComponent<>( "roles", new Model<>("Roles"), new GenericPersistableJpaRepositoryTextChoiceProvider<>(roleRepository)); protected CheckBoxBootstrapFormComponent enabled = new CheckBoxBootstrapFormComponent("enabled"); protected CheckBoxBootstrapFormComponent changePassword = new CheckBoxBootstrapFormComponent("changePassword"); protected final PasswordFieldBootstrapFormComponent password = new PasswordFieldBootstrapFormComponent("plainPassword"); protected final PasswordFieldBootstrapFormComponent cpassword = new PasswordFieldBootstrapFormComponent("plainPasswordCheck", new Model<>()); protected CheckBoxBootstrapFormComponent changePass = new CheckBoxBootstrapFormComponent("changePass") { private static final long serialVersionUID = -1591795804543610117L; @Override protected void onUpdate(final AjaxRequestTarget target) { password.getField().setEnabled(this.getModelObject()); cpassword.getField().setEnabled(this.getModelObject()); target.add(password); target.add(cpassword); } }; public EditUserPage(final PageParameters parameters) { super(parameters); this.jpaRepository = userRepository; this.listPageClass = ListUserPage.class; } protected class UniqueUsernameValidator implements IValidator<String> { private static final long serialVersionUID = -2412508063601996929L; private Long userId; public UniqueUsernameValidator() { this.userId = new Long(-1); } public UniqueUsernameValidator(final Long userId) { this.userId = userId; } @Override public void validate(final IValidatable<String> validatable) { String username = validatable.getValue(); List<Person> persons = userRepository.findByName(username); for (int i = 0; i < persons.size(); i++) { if (persons.get(i) != null && !persons.get(i).getId().equals(userId)) { ValidationError error = new ValidationError(getString("uniqueUser")); validatable.error(error); break; } } } } protected class UniqueEmailAddressValidator implements IValidator<String> { private static final long serialVersionUID = 972971245491631372L; private Long userId; public UniqueEmailAddressValidator() { this.userId = new Long(-1); } public UniqueEmailAddressValidator(final Long userId) { this.userId = userId; } @Override public void validate(final IValidatable<String> validatable) { String emailAddress = validatable.getValue(); Person person = userRepository.findByEmail(emailAddress); if (person != null && !person.getId().equals(userId)) { ValidationError error = new ValidationError(getString("uniqueEmailAddress")); validatable.error(error); } } } public class PasswordPatternValidator extends PatternValidator { private static final long serialVersionUID = 7886016396095273777L; // 1 digit, 1 lower, 1 upper, 1 symbol "@#$%", from 6 to 20 // private static final String PASSWORD_PATTERN = // "((?=.*\\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[@#$%]).{6,20})"; // 1 digit, 1 caps letter, from 10 to 20 private static final String PASSWORD_PATTERN = "((?=.*\\d)(?=.*[a-z]).{10,20})"; public PasswordPatternValidator() { super(PASSWORD_PATTERN); } } public class UsernamePatternValidator extends PatternValidator { private static final long serialVersionUID = -5456988677371244333L; private static final String USERNAME_PATTERN = "[a-zA-Z0-9]*"; public UsernamePatternValidator() { super(USERNAME_PATTERN); } } @Override protected Person newInstance() { return new Person(); } @Override protected void onInitialize() { Person person = SecurityUtil.getCurrentAuthenticatedPerson(); if (!SecurityUtil.isCurrentUserAdmin()) { if (person.getId() != getPageParameters().get(WebConstants.PARAM_ID).toLong()) { setResponsePage(getApplication().getHomePage()); } } super.onInitialize(); userName.required(); userName.getField().add(new UsernamePatternValidator()); StringValue idPerson = getPageParameters().get(WebConstants.PARAM_ID); if (!idPerson.isNull()) { userName.getField().add(new UniqueUsernameValidator(idPerson.toLong())); } else { userName.getField().add(new UniqueUsernameValidator()); } userName.setIsFloatedInput(true); editForm.add(userName); MetaDataRoleAuthorizationStrategy.authorize(userName, Component.ENABLE, SecurityConstants.Roles.ROLE_ADMIN); firstName.required(); firstName.setIsFloatedInput(true); editForm.add(firstName); lastName.required(); lastName.setIsFloatedInput(true); editForm.add(lastName); email.required(); email.getField().add(EmailAddressValidator.getInstance()); if (!idPerson.isNull()) { email.getField().add(new UniqueEmailAddressValidator(idPerson.toLong())); } else { email.getField().add(new UniqueEmailAddressValidator()); } email.setIsFloatedInput(true); editForm.add(email); title.setIsFloatedInput(true); editForm.add(title); group.required(); group.setIsFloatedInput(true); editForm.add(group); MetaDataRoleAuthorizationStrategy.authorize(group, Component.RENDER, SecurityConstants.Roles.ROLE_ADMIN); roles.required(); roles.getField().setOutputMarkupId(true); roles.setIsFloatedInput(true); editForm.add(roles); MetaDataRoleAuthorizationStrategy.authorize(roles, Component.RENDER, SecurityConstants.Roles.ROLE_ADMIN); // stop resetting the password fields each time they are rendered password.getField().setResetPassword(false); cpassword.getField().setResetPassword(false); if (SecurityUtil.isCurrentUserAdmin() && !SecurityUtil.isUserAdmin(compoundModel.getObject()) && idPerson.isNull()) { // hide the change password checkbox and set it's model to true compoundModel.getObject().setChangePass(true); changePass.setVisibilityAllowed(false); } else { compoundModel.getObject().setChangePass(compoundModel.getObject().getChangePassword()); password.getField().setEnabled(compoundModel.getObject().getChangePassword()); cpassword.getField().setEnabled(compoundModel.getObject().getChangePassword()); } changePass.setIsFloatedInput(true); editForm.add(changePass); password.getField().add(new PasswordPatternValidator()); password.setOutputMarkupId(true); password.setIsFloatedInput(true); editForm.add(password); cpassword.setOutputMarkupId(true); cpassword.setIsFloatedInput(true); editForm.add(cpassword); editForm.add(new EqualPasswordInputValidator(password.getField(), cpassword.getField())); enabled.setIsFloatedInput(true); editForm.add(enabled); MetaDataRoleAuthorizationStrategy.authorize(enabled, Component.RENDER, SecurityConstants.Roles.ROLE_ADMIN); changePassword.setIsFloatedInput(true); editForm.add(changePassword); MetaDataRoleAuthorizationStrategy.authorize(changePassword, Component.RENDER, SecurityConstants.Roles.ROLE_ADMIN); MetaDataRoleAuthorizationStrategy.authorize(deleteButton, Component.RENDER, SecurityConstants.Roles.ROLE_ADMIN); } protected boolean isChangePassPage() { return false; } @Override public SaveEditPageButton getSaveEditPageButton() { return new SaveEditPageButton("save", new StringResourceModel("save", EditUserPage.this, null)) { private static final long serialVersionUID = 5214537995514151323L; @Override protected void onSubmit(final AjaxRequestTarget target, final Form<?> form) { Person saveable = editForm.getModelObject(); StandardPasswordEncoder encoder = new StandardPasswordEncoder(""); // encode the password if (saveable.getChangePass()) { saveable.setPassword(encoder.encode(password.getField().getModelObject())); } else { if (saveable.getPassword() == null || saveable.getPassword().compareTo("") == 0) { feedbackPanel.error(new StringResourceModel("nullPassword", this, null).getString()); target.add(feedbackPanel); return; } } // user just changed his password so don't force him to change // it again next time if (isChangePassPage()) { saveable.setChangePassword(false); } jpaRepository.save(saveable); if (!SecurityUtil.isCurrentUserAdmin()) { setResponsePage(Homepage.class); } else { setResponsePage(listPageClass); } } }; } }