From: Roland Häder Date: Wed, 3 Aug 2016 14:39:23 +0000 (+0200) Subject: Continued: (please cherry-pick) X-Git-Url: https://git.mxchange.org/?a=commitdiff_plain;h=5646b6d9899af25f29988e658c3f6e639c684e94;p=pizzaservice-war.git Continued: (please cherry-pick) - rewrote fetching context parameter with protected methods to have this code encapsulated - used this for feature controller - added new user password-change controller (+ interface) to change user's password - used it in template to avoid monolithic super controllers ... - added missing i18n strings - used password-change event for updating user's password history - added new context parameter for "change_user_password" feature - added new context parameter for maximum checked passwords - fixed redirect outcomes Signed-off-by: Roland Häder --- diff --git a/nbproject/faces-config.NavData b/nbproject/faces-config.NavData index 8cb31c14..cfb0d6c4 100644 --- a/nbproject/faces-config.NavData +++ b/nbproject/faces-config.NavData @@ -2,84 +2,85 @@ - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/java/de/chotime/landingpage/beans/user/password/LandingUserPasswordWebRequestBean.java b/src/java/de/chotime/landingpage/beans/user/password/LandingUserPasswordWebRequestBean.java new file mode 100644 index 00000000..fe4c5a91 --- /dev/null +++ b/src/java/de/chotime/landingpage/beans/user/password/LandingUserPasswordWebRequestBean.java @@ -0,0 +1,245 @@ +/* + * Copyright (C) 2016 Cho-Time GmbH + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package de.chotime.landingpage.beans.user.password; + +import de.chotime.landingpage.beans.BaseLandingController; +import de.chotime.landingpage.beans.features.LandingFeaturesWebApplicationController; +import de.chotime.landingpage.beans.login.LandingUserLoginWebSessionController; +import java.util.Objects; +import javax.enterprise.context.RequestScoped; +import javax.enterprise.event.Event; +import javax.enterprise.inject.Any; +import javax.faces.view.facelets.FaceletException; +import javax.inject.Inject; +import javax.inject.Named; +import javax.naming.Context; +import javax.naming.InitialContext; +import javax.naming.NamingException; +import org.mxchange.jusercore.events.user.password_change.UpdatedUserPasswordEvent; +import org.mxchange.jusercore.events.user.password_change.UserUpdatedPasswordEvent; +import org.mxchange.jusercore.exceptions.UserNotFoundException; +import org.mxchange.jusercore.exceptions.UserPasswordMismatchException; +import org.mxchange.jusercore.exceptions.UserStatusLockedException; +import org.mxchange.jusercore.exceptions.UserStatusUnconfirmedException; +import org.mxchange.jusercore.model.user.User; +import org.mxchange.jusercore.model.user.UserSessionBeanRemote; +import org.mxchange.jusercore.model.user.UserUtils; +import org.mxchange.jusercore.model.user.password_history.PasswordHistory; + +/** + * A user password (change) bean (controller) + *

+ * @author Roland Haeder + */ +@Named ("userPasswordController") +@RequestScoped +public class LandingUserPasswordWebRequestBean extends BaseLandingController implements LandingUserPasswordWebRequestController { + + /** + * Serial number + */ + private static final long serialVersionUID = 15_267_867_367_501L; + + /** + * Features controller + */ + @Inject + private LandingFeaturesWebApplicationController featureController; + + /** + * Remote user bean + */ + private final UserSessionBeanRemote userBean; + + /** + * Current password (for confirmation of password change) + */ + private String userCurrentPassword; + + /** + * Login bean (controller) + */ + @Inject + private LandingUserLoginWebSessionController userLoginController; + + /** + * User password (unencrypted from web form) + */ + private String userPassword; + + /** + * User password repeated (unencrypted from web form) + */ + private String userPasswordRepeat; + + /** + * Event being fired when user's password has been updated + */ + @Any + @Inject + private Event userUpdatedPasswordEvent; + + /** + * Default constructor + */ + public LandingUserPasswordWebRequestBean () { + // Try it + try { + // Get initial context + Context context = new InitialContext(); + + // Try to lookup + this.userBean = (UserSessionBeanRemote) context.lookup("java:global/jlandingpage-ejb/user!org.mxchange.jusercore.model.user.UserSessionBeanRemote"); //NOI18N + } catch (final NamingException e) { + // Throw again + throw new FaceletException(e); + } + } + + @Override + public String doChangePassword () { + // This method shall only be called if the user is logged-in + if (!this.userLoginController.isUserLoggedIn()) { + // Not logged-in + throw new IllegalStateException("User is not logged-in"); //NOI18N + } else if (!this.isRequiredChangePasswordSet()) { + // Not all required fields are set + throw new FaceletException("Not all required fields are set."); //NOI18N + } else if (!this.userLoginController.ifCurrentPasswordMatches()) { + // Password not matching + throw new FaceletException(new UserPasswordMismatchException(this.userLoginController.getLoggedInUser())); + } else if (!this.featureController.isFeatureEnabled("change_user_password")) { //NOI18N + // Editing is not allowed + throw new IllegalStateException("User tried to change password."); //NOI18N + } else if (!UserUtils.ifPasswordMatches(this.getUserCurrentPassword(), this.userLoginController.getLoggedInUser())) { + // Password mismatches + this.showFacesMessage("form_user_change_password:userCurrentPassword", "Entered current password does not matched stored password."); //NOI18N + + // Clear bean + this.clear(); + + // No redirect + return ""; //NOI18N + } else if (!Objects.equals(this.getUserPassword(), this.getUserPasswordRepeat())) { + // Both entered passwords don't match + this.showFacesMessage("form_user_change_password:userPasswordRepeat", "Entered new passwords mismatch."); //NOI18N + + // Clear bean + this.clear(); + + // No redirect + return ""; //NOI18N + } else if (Objects.equals(this.getUserCurrentPassword(), this.getUserPassword())) { + // New password matches current + this.showFacesMessage("form_user_change_password:userPassword", "Entered new password is same as current password."); //NOI18N + + // Clear bean + this.clear(); + + // No redirect + return ""; //NOI18N + } else if (this.userLoginController.isPasswordInHistory(this.getUserPassword())) { + // Is already in list (to old passwords are ignored) + this.showFacesMessage("form_user_change_password:userPassword", "Entered new password is has already been used some time ago."); //NOI18N + + // Clear bean + this.clear(); + + // No redirect + return ""; //NOI18N + } + + // Get user instance + User user = this.userLoginController.getLoggedInUser(); + + // Encrypt password + String encryptedPassword = UserUtils.encryptPassword(this.getUserPassword()); + + // Set it in user + user.setUserEncryptedPassword(encryptedPassword); + + try { + // All is set, then update password + PasswordHistory passwordHistory = this.userBean.updateUserPassword(user); + + // Fire event + this.userUpdatedPasswordEvent.fire(new UserUpdatedPasswordEvent(passwordHistory)); + } catch (final UserNotFoundException | UserStatusUnconfirmedException | UserStatusLockedException ex) { + // Clear bean + this.clear(); + + // Throw again + throw new FaceletException(ex); + } + + // Clear bean + this.clear(); + + // Return outcome + return "login_data_saved"; //NOI18N + } + + @Override + public String getUserCurrentPassword () { + return this.userCurrentPassword; + } + + @Override + public void setUserCurrentPassword (final String userCurrentPassword) { + this.userCurrentPassword = userCurrentPassword; + } + + @Override + public String getUserPassword () { + return this.userPassword; + } + + @Override + public void setUserPassword (final String userPassword) { + this.userPassword = userPassword; + } + + @Override + public String getUserPasswordRepeat () { + return this.userPasswordRepeat; + } + + @Override + public void setUserPasswordRepeat (final String userPasswordRepeat) { + this.userPasswordRepeat = userPasswordRepeat; + } + + public boolean isRequiredChangePasswordSet () { + // Is all data set? + return ((this.getUserCurrentPassword() != null) && + (!this.getUserCurrentPassword().isEmpty()) && + (this.getUserPassword() != null) && + (!this.getUserPassword().isEmpty()) && + (this.getUserPasswordRepeat() != null) && + (!this.getUserPasswordRepeat().isEmpty())); + } + + /** + * Clears this bean + */ + private void clear () { + // Clear all data + this.setUserPassword(null); + this.setUserPasswordRepeat(null); + } + +} diff --git a/src/java/de/chotime/landingpage/beans/user/password/LandingUserPasswordWebRequestController.java b/src/java/de/chotime/landingpage/beans/user/password/LandingUserPasswordWebRequestController.java new file mode 100644 index 00000000..a876eab7 --- /dev/null +++ b/src/java/de/chotime/landingpage/beans/user/password/LandingUserPasswordWebRequestController.java @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2016 Cho-Time GmbH + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package de.chotime.landingpage.beans.user.password; + +import java.io.Serializable; +import javax.ejb.Local; + +/** + * An interface for user beans + *

+ * @author Roland Haeder + */ +@Local +public interface LandingUserPasswordWebRequestController extends Serializable { + + /** + * Getter for unencrypted user password + *

+ * @return Unencrypted user password + */ + String getUserPassword (); + + /** + * Setter for unencrypted user password + *

+ * @param userPassword Unencrypted user password + */ + void setUserPassword (final String userPassword); + + /** + * Getter for current unencrypted user password + *

+ * @return Current unencrypted user password + */ + String getUserCurrentPassword (); + + /** + * Setter for current unencrypted user password + *

+ * @param userCurrentPassword Current unencrypted user password + */ + void setUserCurrentPassword (final String userCurrentPassword); + + /** + * Getter for unencrypted user password repeated + *

+ * @return Unencrypted user password repeated + */ + String getUserPasswordRepeat (); + + /** + * Setter for unencrypted user password repeated + *

+ * @param userPasswordRepeat Unencrypted user password repeated + */ + void setUserPasswordRepeat (final String userPasswordRepeat); + + /** + * Changes logged-in user's password. It must not match with current password and should not appear in password history list for X (configurable) entries. + *

+ * @return Redirect outcome + */ + String doChangePassword (); + +} diff --git a/src/java/org/mxchange/localization/bundle_de_DE.properties b/src/java/org/mxchange/localization/bundle_de_DE.properties index b78fc81c..80822e49 100644 --- a/src/java/org/mxchange/localization/bundle_de_DE.properties +++ b/src/java/org/mxchange/localization/bundle_de_DE.properties @@ -589,3 +589,6 @@ ADMIN_MOBILE_PROVIDER_DIAL_PREFIX_REQUIRED=Bitte geben Sie die Vorwahl ohne f\u0 USER_LOGIN_MUST_CHANGE_PASSWORD=Sie m\u00fcssen Ihr Passwort \u00e4ndern. Dies darf nicht mit dem aktuellen \u00fcbereinstimmen. ADMIN_LOGOUT_TITLE=Aus dem Administrationsbereich ausloggen ADMIN_LOGOUT_NOTICE=Bitte loggen Sie sich immer aus, damit die Sitzung sauber beendet ist. Andere M\u00f6gliichkeit ist, den Browser zu schhlie\u00dfen, dann wird die Sitzung auch beendet. +USER_CURRENT_PASSWORD_REQUIRED=Bitte geben Sie Ihr derzeit verwendetes Passwort zur \u00c4nderungsbest\u00e4tigung ein. +USER_NEW_PASSWORD_REQUIRED=Bitte geben Sie ein neues Passwort zweimal ein. Dieses muss anders als Ihr aktuelles sein. +USER_NEW_PASSWORD_REPEAT_REQUIRED=Bitte wiederholen Sie das neue Passwort. Dies dient der Reduzierung von Tippfehlern. diff --git a/src/java/org/mxchange/localization/bundle_en_US.properties b/src/java/org/mxchange/localization/bundle_en_US.properties index ae598b11..2642b7eb 100644 --- a/src/java/org/mxchange/localization/bundle_en_US.properties +++ b/src/java/org/mxchange/localization/bundle_en_US.properties @@ -572,3 +572,6 @@ ADMIN_MOBILE_PROVIDER_DIAL_PREFIX_REQUIRED=Please enter dial prefix for mobile p USER_LOGIN_MUST_CHANGE_PASSWORD=Please change your password. It must not match with your current one. ADMIN_LOGOUT_TITLE=Logout from administration area ADMIN_LOGOUT_NOTICE=Please always logout from administration area that the session is cleanly closed. An other option is to close the browser, then the session is also closed. +USER_CURRENT_PASSWORD_REQUIRED=Please enter your currently used password for confirming password change. +USER_NEW_PASSWORD_REQUIRED=Please a new password twice. This must be different than the current one. +USER_NEW_PASSWORD_REPEAT_REQUIRED=Please repeat your new password. This is for preventing type mistakes. diff --git a/src/java/org/mxchange/pizzaapplication/beans/BasePizzaController.java b/src/java/org/mxchange/pizzaapplication/beans/BasePizzaController.java index 69a0379f..29e50a59 100644 --- a/src/java/org/mxchange/pizzaapplication/beans/BasePizzaController.java +++ b/src/java/org/mxchange/pizzaapplication/beans/BasePizzaController.java @@ -75,4 +75,45 @@ public abstract class BasePizzaController implements Serializable { FacesContext.getCurrentInstance().addMessage(clientId, new FacesMessage(cause.getMessage())); } + /** + * Returns given property key or throws an exception if not found. + *

+ * @param parameterKey Property key + *

+ * @return Property value + *

+ * @throws NullPointerException If given key is not found + * @throws NumberFormatException If no number is given in context parameter + */ + protected int getIntegerContextParameter (final String parameterKey) throws NullPointerException, NumberFormatException { + // Get context parameter + Integer contextValue = Integer.parseInt(this.getStringContextParameter(parameterKey)); + + // Return it + return contextValue; + } + + /** + * Returns given property key or throws an exception if not found. + *

+ * @param parameterKey Property key + *

+ * @return Property value + *

+ * @throws NullPointerException If given key is not found + */ + protected String getStringContextParameter (final String parameterKey) throws NullPointerException { + // Get context parameter + String contextValue = FacesContext.getCurrentInstance().getExternalContext().getInitParameter(parameterKey); + + // Is it null? + if (null == contextValue) { + // Throw NPE + throw new NullPointerException("parameterKey=" + parameterKey + " is not set."); + } + + // Return it + return contextValue; + } + } diff --git a/src/java/org/mxchange/pizzaapplication/beans/features/PizzaFeatureWebApplicationBean.java b/src/java/org/mxchange/pizzaapplication/beans/features/PizzaFeatureWebApplicationBean.java index dc893eba..1e34bb8f 100644 --- a/src/java/org/mxchange/pizzaapplication/beans/features/PizzaFeatureWebApplicationBean.java +++ b/src/java/org/mxchange/pizzaapplication/beans/features/PizzaFeatureWebApplicationBean.java @@ -17,7 +17,6 @@ package org.mxchange.pizzaapplication.beans.features; import javax.enterprise.context.ApplicationScoped; -import javax.faces.context.FacesContext; import javax.inject.Named; import org.mxchange.pizzaapplication.beans.BasePizzaController; @@ -46,11 +45,19 @@ public class PizzaFeatureWebApplicationBean extends BasePizzaController implemen throw new IllegalArgumentException("feature is empty"); //NOI18N } - // Get context parameter - String contextParameter = FacesContext.getCurrentInstance().getExternalContext().getInitParameter(String.format("is_feature_%s_enabled", feature)); //NOI18N + // Default is not enabled + boolean isEnabled = false; - // Is it set? - boolean isEnabled = ((contextParameter instanceof String) && (contextParameter.toLowerCase().equals("true"))); //NOI18N + // Try it as an NPE may come + try { + // Get value from property + String value = this.getStringContextParameter(String.format("is_feature_%s_enabled", feature)); //NOI18N + + // Is it set? + isEnabled = (value.toLowerCase().equals("true")); //NOI18N + } catch (final NullPointerException ex) { + // Ignored + } // Return value return isEnabled; diff --git a/src/java/org/mxchange/pizzaapplication/beans/login/PizzaUserLoginWebSessionBean.java b/src/java/org/mxchange/pizzaapplication/beans/login/PizzaUserLoginWebSessionBean.java index f7808de5..b202cb6f 100644 --- a/src/java/org/mxchange/pizzaapplication/beans/login/PizzaUserLoginWebSessionBean.java +++ b/src/java/org/mxchange/pizzaapplication/beans/login/PizzaUserLoginWebSessionBean.java @@ -21,6 +21,7 @@ import java.util.List; import java.util.Objects; import javax.enterprise.context.SessionScoped; import javax.enterprise.event.Event; +import javax.enterprise.event.Observes; import javax.enterprise.inject.Any; import javax.faces.context.FacesContext; import javax.faces.view.facelets.FaceletException; @@ -35,6 +36,7 @@ import org.mxchange.jusercore.events.login.UserLoggedInEvent; import org.mxchange.jusercore.events.login.UserLoginEvent; import org.mxchange.jusercore.events.logout.ObserveableUserLogoutEvent; import org.mxchange.jusercore.events.logout.UserLogoutEvent; +import org.mxchange.jusercore.events.user.password_change.UpdatedUserPasswordEvent; import org.mxchange.jusercore.exceptions.UserNotFoundException; import org.mxchange.jusercore.exceptions.UserPasswordMismatchException; import org.mxchange.jusercore.exceptions.UserStatusLockedException; @@ -151,6 +153,27 @@ public class PizzaUserLoginWebSessionBean extends BasePizzaController implements } } + @Override + public void afterUserUpdatedPasswordEvent (@Observes final UpdatedUserPasswordEvent event) { + // Check parameter + if (null == event) { + // Throw NPE + throw new NullPointerException("event is null"); //NOI18N + } else if (event.getPasswordHistory() == null) { + // Throw NPE again + throw new NullPointerException("event.passwordHistory is null"); //NOI18N + } else if (event.getPasswordHistory().getUserPasswordHistoryId() == null) { + // ... and again + throw new NullPointerException("event.passwordHistory.userPasswordHistoryId is null"); //NOI18N + } else if (event.getPasswordHistory().getUserPasswordHistoryId() < 1) { + // Invalid value + throw new IllegalArgumentException(MessageFormat.format("event.passwordHistory.userPasswordHistoryId={0} is in valid", event.getPasswordHistory().getUserPasswordHistoryId())); //NOI18N + } + + // All fine, so update list + this.updatePasswordHistory(event.getPasswordHistory()); + } + @Override public String doAdminLogout () { // Is a user logged-in? @@ -294,6 +317,35 @@ public class PizzaUserLoginWebSessionBean extends BasePizzaController implements return this.getLoggedInUser().getUserProfileMode().equals(ProfileMode.INVISIBLE); } + @Override + public boolean isPasswordInHistory (final String userPassword) { + // Default is not found + boolean isPasswordInHistory = false; + + // Init variables + int count = 1; + int maxEntries = this.getIntegerContextParameter("max_user_password_history"); //NOI18N + + // Check all passwords + for (final PasswordHistory entry : this.getUserPasswordHistory()) { + // Is password the same? + if (UserUtils.ifPasswordMatches(userPassword, entry.getUserPasswordHistoryUser())) { + // Yes, found it + isPasswordInHistory = true; + break; + } else if (count == maxEntries) { + // Maximum reached + break; + } + + // Count up + count++; + } + + // Return status + return isPasswordInHistory; + } + @Override public boolean isUserLoggedIn () { // Compare instance @@ -311,4 +363,41 @@ public class PizzaUserLoginWebSessionBean extends BasePizzaController implements this.setCurrentPassword(null); } + /** + * Updates password history by adding given entry to it as long as it is not + * there. + *

+ * @param passwordHistory Password history entry + */ + private void updatePasswordHistory (final PasswordHistory passwordHistory) { + if (null == passwordHistory) { + // Throw NPE + throw new NullPointerException("passwordHistory is null"); //NOI18N + } else if (passwordHistory.getUserPasswordHistoryId() == null) { + // Throw NPE again + throw new NullPointerException("passwordHistory.userPasswordHistoryId is null"); //NOI18N + } else if (passwordHistory.getUserPasswordHistoryId() < 1) { + // Invalid id + throw new IllegalArgumentException(MessageFormat.format("passwordHistory.userPasswordHistoryId={0} is not valid.", passwordHistory.getUserPasswordHistoryId())); //NOI18N + } + + // Is it there? + if (this.userPasswordHistory.contains(passwordHistory)) { + // Excact copy found + return; + } + + // Check all entries + for (final PasswordHistory entry : this.userPasswordHistory) { + // Is same id number? + if (Objects.equals(entry.getUserPasswordHistoryId(), passwordHistory.getUserPasswordHistoryId())) { + // Found it + return; + } + } + + // Not found, so add it + this.userPasswordHistory.add(passwordHistory); + } + } diff --git a/src/java/org/mxchange/pizzaapplication/beans/login/PizzaUserLoginWebSessionController.java b/src/java/org/mxchange/pizzaapplication/beans/login/PizzaUserLoginWebSessionController.java index b4fd092b..a6366947 100644 --- a/src/java/org/mxchange/pizzaapplication/beans/login/PizzaUserLoginWebSessionController.java +++ b/src/java/org/mxchange/pizzaapplication/beans/login/PizzaUserLoginWebSessionController.java @@ -19,6 +19,7 @@ package org.mxchange.pizzaapplication.beans.login; import java.io.Serializable; import java.util.List; import javax.ejb.Local; +import org.mxchange.jusercore.events.user.password_change.UpdatedUserPasswordEvent; import org.mxchange.jusercore.model.user.User; import org.mxchange.jusercore.model.user.password_history.PasswordHistory; @@ -30,6 +31,23 @@ import org.mxchange.jusercore.model.user.password_history.PasswordHistory; @Local public interface PizzaUserLoginWebSessionController extends Serializable { + /** + * Method being call after user's password has been updated (and history + * entry has been created). + *

+ * @param event Event being observed + */ + void afterUserUpdatedPasswordEvent (final UpdatedUserPasswordEvent event); + + /** + * Checks whether given clear-text password is in user's password history. + *

+ * @param userPassword Clear-text password + *

+ * @return Whether clear-text password is in user's password history + */ + boolean isPasswordInHistory (final String userPassword); + /** * Getter for template type *

diff --git a/src/java/org/mxchange/pizzaapplication/beans/user/PizzaUserWebSessionBean.java b/src/java/org/mxchange/pizzaapplication/beans/user/PizzaUserWebSessionBean.java index c905475c..c09ea390 100644 --- a/src/java/org/mxchange/pizzaapplication/beans/user/PizzaUserWebSessionBean.java +++ b/src/java/org/mxchange/pizzaapplication/beans/user/PizzaUserWebSessionBean.java @@ -39,6 +39,7 @@ import org.mxchange.jusercore.events.confirmation.UserConfirmedAccountEvent; import org.mxchange.jusercore.events.login.UserLoggedInEvent; import org.mxchange.jusercore.events.registration.UserRegisteredEvent; import org.mxchange.jusercore.events.user.add.AdminAddedUserEvent; +import org.mxchange.jusercore.events.user.password_change.UpdatedUserPasswordEvent; import org.mxchange.jusercore.events.user.update.AdminUpdatedUserDataEvent; import org.mxchange.jusercore.events.user.update.UpdatedUserPersonalDataEvent; import org.mxchange.jusercore.events.user.update.UserUpdatedPersonalDataEvent; @@ -345,6 +346,27 @@ public class PizzaUserWebSessionBean extends BasePizzaController implements Pizz } } + @Override + public void afterUserUpdatedPasswordEvent (@Observes final UpdatedUserPasswordEvent event) { + // Check parameter + if (null == event) { + // Throw NPE + throw new NullPointerException("event is null"); //NOI18N + } else if (event.getPasswordHistory() == null) { + // Throw NPE again + throw new NullPointerException("event.passwordHistory is null"); //NOI18N + } else if (event.getPasswordHistory().getUserPasswordHistoryId() == null) { + // ... and again + throw new NullPointerException("event.passwordHistory.userPasswordHistoryId is null"); //NOI18N + } else if (event.getPasswordHistory().getUserPasswordHistoryId() < 1) { + // Invalid value + throw new IllegalArgumentException(MessageFormat.format("event.passwordHistory.userPasswordHistoryId={0} is in valid", event.getPasswordHistory().getUserPasswordHistoryId())); //NOI18N + } + + // All fine, so update list + this.updateList(event.getPasswordHistory().getUserPasswordHistoryUser()); + } + @Override public void afterUserUpdatedPersonalData (@Observes final UpdatedUserPersonalDataEvent event) { // Check parameter @@ -471,7 +493,7 @@ public class PizzaUserWebSessionBean extends BasePizzaController implements Pizz throw new FaceletException(new UserPasswordMismatchException(this.userLoginController.getLoggedInUser())); } else if (!this.featureController.isFeatureEnabled("edit_user_data")) { // Editing is not allowed - throw new IllegalStateException("User tried to edit personal data"); //NOI18N + throw new IllegalStateException("User tried to edit personal data."); //NOI18N } // Get user instance @@ -498,7 +520,7 @@ public class PizzaUserWebSessionBean extends BasePizzaController implements Pizz this.updatedPersonalDataEvent.fire(new UserUpdatedPersonalDataEvent(updatedUser)); // All fine - return "user_data_saved"; //NOI18N + return "contact_data_saved"; //NOI18N } @Override diff --git a/src/java/org/mxchange/pizzaapplication/beans/user/PizzaUserWebSessionController.java b/src/java/org/mxchange/pizzaapplication/beans/user/PizzaUserWebSessionController.java index 001bea12..30995458 100644 --- a/src/java/org/mxchange/pizzaapplication/beans/user/PizzaUserWebSessionController.java +++ b/src/java/org/mxchange/pizzaapplication/beans/user/PizzaUserWebSessionController.java @@ -23,6 +23,7 @@ import org.mxchange.jusercore.events.confirmation.UserConfirmedAccountEvent; import org.mxchange.jusercore.events.login.UserLoggedInEvent; import org.mxchange.jusercore.events.registration.UserRegisteredEvent; import org.mxchange.jusercore.events.user.add.AdminAddedUserEvent; +import org.mxchange.jusercore.events.user.password_change.UpdatedUserPasswordEvent; import org.mxchange.jusercore.events.user.update.AdminUpdatedUserDataEvent; import org.mxchange.jusercore.events.user.update.UpdatedUserPersonalDataEvent; import org.mxchange.jusercore.exceptions.UserEmailAddressNotFoundException; @@ -63,6 +64,14 @@ public interface PizzaUserWebSessionController extends Serializable { */ void afterUserConfirmedAccount (final UserConfirmedAccountEvent event); + /** + * Method being call after user's password has been updated (and history + * entry has been created). + *

+ * @param event Event being observed + */ + void afterUserUpdatedPasswordEvent (final UpdatedUserPasswordEvent event); + /** * Listens to fired event when user updated personal data *

diff --git a/web/WEB-INF/web.xml b/web/WEB-INF/web.xml index 3db8bbd3..53f9060a 100644 --- a/web/WEB-INF/web.xml +++ b/web/WEB-INF/web.xml @@ -82,6 +82,16 @@ is_feature_user_must_change_password_enabled true + + Whether users are allowed to change their login password. + is_feature_change_user_password_enabled + true + + + Maximum passwords that must be different. + max_user_password_history + 5 + Faces Servlet javax.faces.webapp.FacesServlet diff --git a/web/user/login_change_password.xhtml b/web/user/login_change_password.xhtml index fe0e4036..847c6623 100644 --- a/web/user/login_change_password.xhtml +++ b/web/user/login_change_password.xhtml @@ -16,7 +16,7 @@ - +

@@ -29,42 +29,66 @@ +
+
+ +
+ +
+ +
+ +
+
+ +
+ +
+
- +
+
+ +
+
- +
+ +
+ +
- +