From 68e9e7cf4e902a8d89c4935b165917aebb1dd0d9 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Roland=20H=C3=A4der?= Date: Sun, 23 Oct 2022 18:50:48 +0200 Subject: [PATCH] Please cherry-pick: - formatted tag so all attributes are in a separate line - sorted import lines --- .../FinancialsUserRegisterWebRequestBean.java | 568 ++++++++++++++++++ web/admin/contact/admin_contact_list.xhtml | 8 - web/admin/user/admin_user_show.xhtml | 7 +- 3 files changed, 574 insertions(+), 9 deletions(-) create mode 100644 src/java/org/mxchange/jfinancials/beans/user/register/FinancialsUserRegisterWebRequestBean.java diff --git a/src/java/org/mxchange/jfinancials/beans/user/register/FinancialsUserRegisterWebRequestBean.java b/src/java/org/mxchange/jfinancials/beans/user/register/FinancialsUserRegisterWebRequestBean.java new file mode 100644 index 00000000..b583880f --- /dev/null +++ b/src/java/org/mxchange/jfinancials/beans/user/register/FinancialsUserRegisterWebRequestBean.java @@ -0,0 +1,568 @@ +/* + * Copyright (C) 2016 - 2022 Free Software Foundation + * + * 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 org.mxchange.jfinancials.beans.user.register; + +import java.util.Objects; +import javax.ejb.EJB; +import javax.enterprise.context.RequestScoped; +import javax.enterprise.event.Event; +import javax.enterprise.event.Observes; +import javax.enterprise.inject.Any; +import javax.faces.FacesException; +import javax.faces.application.FacesMessage; +import javax.inject.Inject; +import javax.inject.Named; +import org.mxchange.jcontacts.model.contact.Contact; +import org.mxchange.jcontacts.model.contact.UserContact; +import org.mxchange.jcoreee.utils.FacesUtils; +import org.mxchange.jfinancials.beans.BaseFinancialsBean; +import org.mxchange.jfinancials.beans.contact.FinancialsContactWebRequestController; +import org.mxchange.jfinancials.beans.features.FinancialsFeaturesWebApplicationController; +import org.mxchange.jfinancials.beans.localization.FinancialsLocalizationSessionController; +import org.mxchange.jfinancials.beans.user.FinancialsUserWebRequestController; +import org.mxchange.jfinancials.beans.user.list.FinancialsUserListWebViewController; +import org.mxchange.jusercore.exceptions.DataRepeatMismatchException; +import org.mxchange.jusercore.exceptions.EmailAddressAlreadyRegisteredException; +import org.mxchange.jusercore.exceptions.UserNameAlreadyRegisteredException; +import org.mxchange.jusercore.model.user.LoginUser; +import org.mxchange.jusercore.model.user.User; +import org.mxchange.jusercore.model.user.password_history.PasswordHistory; +import org.mxchange.jusercore.model.user.password_history.UserPasswordHistory; +import org.mxchange.jusercore.model.user.profilemodes.ProfileMode; +import org.mxchange.jusercore.model.user.status.UserAccountStatus; +import org.mxchange.jusercore.model.utils.UserUtils; +import org.mxchange.juserlogincore.events.registration.ObservableUserRegisteredEvent; +import org.mxchange.juserlogincore.events.registration.UserRegisteredEvent; +import org.mxchange.juserlogincore.events.user.password_change.ObservableUpdatedUserPasswordEvent; +import org.mxchange.juserlogincore.events.user.password_change.UpdatedUserPasswordEvent; +import org.mxchange.juserlogincore.login.UserLoginUtils; +import org.mxchange.juserlogincore.model.user.register.UserRegistrationSessionBeanRemote; + +/** + * A web bean for user registration + *

+ * @author Roland Häder + */ +@Named ("userRegistrationController") +@RequestScoped +public class FinancialsUserRegisterWebRequestBean extends BaseFinancialsBean implements FinancialsUserRegisterWebRequestController { + + /** + * Serial number + */ + private static final long serialVersionUID = 47_828_986_719_691_592L; + + /** + * Contact controller + */ + @Inject + private FinancialsContactWebRequestController contactController; + + /** + * Features controller + */ + @Inject + private FinancialsFeaturesWebApplicationController featureController; + + /** + * Localization controller + */ + @Inject + private FinancialsLocalizationSessionController localizationController; + + /** + * Remote register session-scoped bean + */ + @EJB (lookup = "java:global/jfinancials-ejb/userRegistration!org.mxchange.juserlogincore.model.user.register.UserRegistrationSessionBeanRemote") + private UserRegistrationSessionBeanRemote registerBean; + + /** + * User list controller + */ + @Inject + private FinancialsUserListWebViewController userListController; + + /** + * User name + */ + private String userName; + + /** + * User password (clear-text from web form) + */ + private String userPassword; + + /** + * An event being fired when a user password was changed + */ + @Inject + @Any + private Event userPasswordChangedEvent; + + /** + * User password repeated (clear-text from web form) + */ + private String userPasswordRepeat; + + /** + * Whether the user wants a public profile + */ + private ProfileMode userProfileMode; + + /** + * An event being fired when a new user has registered + */ + @Inject + @Any + private Event userRegisteredEvent; + + /** + * Default constructor + */ + public FinancialsUserRegisterWebRequestBean () { + // Call super constructor + super(); + } + + /** + * Event observer for user password changes + *

+ * @param event Event being fired + */ + public void afterUserPasswordChangedEvent (@Observes final ObservableUpdatedUserPasswordEvent event) { + // Is it valid? + if (null == event) { + // Throw NPE + throw new NullPointerException("event is null"); //NOI18N + } else if (event.getUserPassword() == null) { + // Throw NPE + throw new NullPointerException("event.userPassword is null"); //NOI18N + } else if (event.getUserPassword().isEmpty()) { + // Throw NPE + throw new IllegalArgumentException("event.userPassword is empty"); //NOI18N + } + + // Set it here + this.setUserPassword(event.getUserPassword()); + this.setUserPasswordRepeat(event.getUserPassword()); + } + + /** + * Registers the user, if not found. Otherwise this method should throw an + * exception. + *

+ * @return Redirection target + */ + public String doFinishRegistration () { + // Is registration enabled? + if (!this.featureController.isFeatureEnabled("user_registration")) { //NOI18N + // Is not enabled + throw new FacesException("Registration is disabled."); //NOI18N + } + + // Get user instance + final User user = this.createUserInstance(true); + + // Null random password means registration requires user-entered password + String randomPassword = null; + + // Is the user already used? + if (null == user) { + // user must be set + throw new NullPointerException("user is null after createUserInstance() was called"); //NOI18N + } else if (!this.isRequiredPersonalDataSet()) { + // Not all required fields are set + throw new FacesException("Not all required fields are set."); //NOI18N + } else if ((this.featureController.isFeatureEnabled("user_login_require_user_name")) && (this.userListController.isUserNameRegistered(user))) { //NOI18N + // Is multi-page enabled? + if (this.featureController.isFeatureEnabled("user_register_multiple_page")) { //NOI18N + // User name is already used, should not happen here + throw new FacesException(new UserNameAlreadyRegisteredException(user)); + } else { + // May happen here, clear user name + this.clearUserName(); + + // Output message + this.showFacesMessage("form_register_single:userName", "ERROR_USER_NAME_ALREADY_USED", FacesMessage.SEVERITY_WARN); //NOI18N + return ""; //NOI18N + } + } else if (this.contactController.isEmailAddressRegistered(user.getUserContact())) { + // Is multi-page enabled? + if (this.featureController.isFeatureEnabled("user_register_multiple_page")) { //NOI18N + // Email address has already been taken, should not happen here + throw new FacesException(new EmailAddressAlreadyRegisteredException(user)); + } else { + // May happen here, reset fields + this.contactController.clearEmailAddresses(); + this.showFacesMessage("form_register_single:emailAddressRepeat", "ERROR_EMAIL_ADDRESS_ALREADY_USED", FacesMessage.SEVERITY_WARN); //NOI18N + return ""; //NOI18N + } + } else if (!this.contactController.isSameEmailAddressEntered()) { + // Is multi-page enabled? + if (this.featureController.isFeatureEnabled("user_register_multiple_page")) { //NOI18N + // Not same email address entered, should not happen here + throw new FacesException(new DataRepeatMismatchException("Email addresses not matching.")); //NOI18N + } else { + // May happen here, reset fields + this.contactController.clearEmailAddresses(); + this.showFacesMessage("form_register_single:emailAddressRepeat", "ERROR_EMAIL_ADDRESSES_MISMATCHING", FacesMessage.SEVERITY_INFO); //NOI18N + return ""; //NOI18N + } + } else if (!this.isSamePasswordEntered()) { + // Is multi-page enabled? + if (this.featureController.isFeatureEnabled("user_register_multiple_page")) { //NOI18N + // Not same password entered, should no longer happen here + throw new FacesException(new DataRepeatMismatchException("Passwords not matching.")); //NOI18N + } else if (this.ifBothPasswordsEmptyAllowed()) { + // Both passwords are left empty and is allowed, then generate a random password + randomPassword = UserLoginUtils.createRandomPassword(FinancialsUserWebRequestController.MINIMUM_PASSWORD_LENGTH); + + // Generate (ignored) password-history + final PasswordHistory passwordHistory = new UserPasswordHistory(randomPassword, user); + + // Fire event + this.userPasswordChangedEvent.fire(new UpdatedUserPasswordEvent(passwordHistory, randomPassword)); + } + } + + // Encrypt password + final String encryptedPassword = UserLoginUtils.encryptPassword(this.getUserPassword()); + + // Set it here + user.setUserEncryptedPassword(encryptedPassword); + + // Is developer mode? + if (this.isDebugModeEnabled("register")) { //NOI18N + // For debugging/programming only: + user.setUserAccountStatus(UserAccountStatus.CONFIRMED); + } else { + // No debugging of this part + user.setUserAccountStatus(UserAccountStatus.UNCONFIRMED); + + // Ask EJB for generating a not-existing confirmation key + final String confirmKey = this.registerBean.generateConfirmationKey(user); + + // Set it in user + user.setUserConfirmKey(confirmKey); + } + + // Init variable + final User registeredUser; + + try { + // Get base URL + final String baseUrl = FacesUtils.generateBaseUrl(); + + // Call bean + registeredUser = this.registerBean.registerUser(user, baseUrl, randomPassword); + + // The id number should be set + assert (registeredUser.getUserId() instanceof Long) : "registeredUser.userId is null after registerUser() was called."; //NOI18N + } catch (final UserNameAlreadyRegisteredException | EmailAddressAlreadyRegisteredException ex) { + // Continue to throw + throw new FacesException(ex); + } + + // Fire event + this.userRegisteredEvent.fire(new UserRegisteredEvent(registeredUser)); + + // All fine, redirect to proper page + return "user_register_done"; //NOI18N + } + + /** + * Handles registration request send from first page. The (maybe) entered + * user name and email address is not used and that privacy and T&C are + * accepted. + *

+ * @return Redirect + */ + public String doRegisterMultiPage1 () { + // Is registration enabled? + if (!this.featureController.isFeatureEnabled("user_registration")) { //NOI18N + // Is not enabled + throw new FacesException("Registration is disabled."); //NOI18N + } + + // Get user instance + final User user = this.createUserInstance(false); + + // First check if user is not null and user name is not used + if same email address is entered + if (null == user) { + // user must be set + throw new NullPointerException("user is null after createUserInstance() was called"); //NOI18N + } else if ((this.featureController.isFeatureEnabled("user_login_require_user_name")) && (this.userListController.isUserNameRegistered(user))) { //NOI18N + // User name is already used, so clear it + this.clearUserName(); + + // Output message + this.showFacesMessage("form_register_page1:userName", "ERROR_USER_NAME_ALREADY_USED", FacesMessage.SEVERITY_WARN); //NOI18N + return ""; //NOI18N + } else if (!this.contactController.isSameEmailAddressEntered()) { + // Not same email address entered, clear both + this.contactController.clearEmailAddresses(); + this.showFacesMessage("form_register_page1:emailAddressRepeat", "ERROR_EMAIL_ADDRESSES_MISMATCHING", FacesMessage.SEVERITY_WARN); //NOI18N + return ""; //NOI18N + } else if (!this.isSamePasswordEntered()) { + // Is multi-page enabled? + if (this.featureController.isFeatureEnabled("user_register_multiple_page")) { //NOI18N + // Clear passwords + this.clearUserPasswords(); + + // Output faces message + this.showFacesMessage("form_register_page1:userPassword", "ERROR_USER_PASSWORD_EMPTY", FacesMessage.SEVERITY_WARN); //NOI18N + this.showFacesMessage("form_register_page1:userPasswordRepeat", "ERROR_USER_PASSWORD_REPEAT_EMPTY", FacesMessage.SEVERITY_WARN); //NOI18N + return ""; //NOI18N + } else if (this.ifBothPasswordsEmptyAllowed()) { + // Both passwords are left empty and is allowed, then generate a random password + final String randomPassword = UserLoginUtils.createRandomPassword(FinancialsUserWebRequestController.MINIMUM_PASSWORD_LENGTH); + + // Generate (ignored) password-history + final PasswordHistory passwordHistory = new UserPasswordHistory(randomPassword, user); + + // Fire event + this.userPasswordChangedEvent.fire(new UpdatedUserPasswordEvent(passwordHistory, randomPassword)); + } + } + + // Create half contact instance with email address + final Contact contact = new UserContact(); + contact.setContactEmailAddress(this.contactController.getEmailAddress()); + + // Set contact in user + user.setUserContact(contact); + + // Check if email address is registered + if (this.contactController.isEmailAddressRegistered(user.getUserContact())) { + // Email address has already been taken, clear both + this.contactController.clearEmailAddresses(); + this.showFacesMessage("form_register_page1:emailAddress", "ERROR_EMAIL_ADDRESS_ALREADY_USED", FacesMessage.SEVERITY_WARN); //NOI18N + return ""; //NOI18N + } + + // Now only redirect to next page as the JSF does it + return "user_register_page2"; //NOI18N + } + + /** + * Getter for user name + *

+ * @return User name + */ + public String getUserName () { + return this.userName; + } + + /** + * Setter for user name + *

+ * @param userName User name + */ + public void setUserName (final String userName) { + this.userName = userName; + } + + /** + * Getter for clear-text user password + *

+ * @return Clear-text user password + */ + public String getUserPassword () { + return this.userPassword; + } + + /** + * Setter for clear-text user password + *

+ * @param userPassword Clear-text user password + */ + public void setUserPassword (final String userPassword) { + this.userPassword = userPassword; + } + + /** + * Getter for clear-text user password repeated + *

+ * @return Clear-text user password repeated + */ + public String getUserPasswordRepeat () { + return this.userPasswordRepeat; + } + + /** + * Setter for clear-text user password repeated + *

+ * @param userPasswordRepeat Clear-text user password repeated + */ + public void setUserPasswordRepeat (final String userPasswordRepeat) { + this.userPasswordRepeat = userPasswordRepeat; + } + + /** + * Getter for user profile mode + *

+ * @return User profile mode + */ + public ProfileMode getUserProfileMode () { + return this.userProfileMode; + } + + /** + * Setter for user profile mode + *

+ * @param userProfileMode User profile mode + */ + public void setUserProfileMode (final ProfileMode userProfileMode) { + this.userProfileMode = userProfileMode; + } + + @Override + public boolean isRequiredChangePersonalDataSet () { + return ((this.getUserProfileMode() != null) && + (this.getUserName() != null) && + (!this.getUserName().isEmpty()) && + (this.contactController.isRequiredChangePersonalDataSet())); + } + + /** + * Clears user name + */ + private void clearUserName () { + // Clear it + this.setUserName(null); + } + + /** + * Clears both user passwords + */ + private void clearUserPasswords () { + // Clear both + this.setUserPassword(null); + this.setUserPasswordRepeat(null); + } + + /** + * Creates an instance from all properties + *

+ * @param createContactData Whether contact data should be created + *

+ * @return A user instance + */ + private User createUserInstance (final boolean createContactData) { + // Required personal data must be set + assert (this.isRequiredPersonalDataSet()) : "All required personal data must be set before invoking this method."; //NOI18N + + // Is user name required? + if (!this.featureController.isFeatureEnabled("user_login_require_username")) { + // Init variables + String randomName = null; + boolean isUsernameFree = false; + + // Get full list + for (final User user : this.userListController.getAllUsers()) { + // Loop until a user name is found + while ((randomName == null) || (randomName.equals(user.getUserName()))) { + // Generate random name + randomName = UserUtils.generateRandomUserName(); + isUsernameFree = true; + } + + // Is non-existing username found + if (isUsernameFree) { + // Also stop looping here + break; + } + } + + // Set it and inivisible profile + this.setUserName(randomName); + this.setUserProfileMode(ProfileMode.INVISIBLE); + + // Generate random password + final String randomPassword = UserLoginUtils.createRandomPassword(FinancialsUserWebRequestController.MINIMUM_PASSWORD_LENGTH); + + // Set random password + this.setUserPassword(randomPassword); + this.setUserPasswordRepeat(randomPassword); + } + + // Create new user instance + final User user = new LoginUser(); + + // Set user name profile mode and locale + user.setUserName(this.getUserName()); + user.setUserProfileMode(this.getUserProfileMode()); + user.setUserLocale(this.localizationController.getLocale()); + + // Is multiple registration page + if ((createContactData) || (!this.featureController.isFeatureEnabled("user_register_multiple_page"))) { //NOI18N + // Create contact instance + final Contact contact = this.contactController.createContactInstance(); + + // Set contact in user + user.setUserContact(contact); + } + + // Return it + return user; + } + + /** + * Checks if both user passwords are left empty and if this is enabled + * (allowed) in context parameter. If true, the calling bean should create a + * random password (preferable with UserUtils.createRandomPassword() and set + * it in both user password fields. + *

+ * @return Whether empty passwords are allowed + */ + private boolean ifBothPasswordsEmptyAllowed () { + // Check feature first + return ((this.featureController.isFeatureEnabled("allow_user_registration_empty_password")) && //NOI18N + ((this.getUserPassword() == null) || (this.getUserPassword().isEmpty())) && + ((this.getUserPasswordRepeat() == null) || (this.getUserPasswordRepeat().isEmpty()))); + } + + /** + * Checks whether all required personal data is set + *

+ * @return Whether the required personal data is set + */ + private boolean isRequiredPersonalDataSet () { + // Check conditions based on of multi-page registration is enabled + if (this.featureController.isFeatureEnabled("user_register_multiple_page")) { //NOI18N + // Multiple registration page + return this.contactController.isRequiredPersonalDataSet(); + } else { + // Single registration page + return (((this.getUserName() != null) || (!this.featureController.isFeatureEnabled("user_login_require_username"))) && //NOI18N + (this.getUserProfileMode() != null) && + (this.contactController.isRequiredPersonalDataSet()) && + (this.getUserPassword() != null) && + (this.getUserPasswordRepeat() != null)); + } + } + + /** + * Checks whether same passwords has been entered + *

+ * @return Whether same passwords has been entered + */ + private boolean isSamePasswordEntered () { + return ((!this.getUserPassword().isEmpty()) && (Objects.equals(this.getUserPassword(), this.getUserPasswordRepeat()))); + } + +} diff --git a/web/admin/contact/admin_contact_list.xhtml b/web/admin/contact/admin_contact_list.xhtml index 0d8e32ee..405d97d5 100644 --- a/web/admin/contact/admin_contact_list.xhtml +++ b/web/admin/contact/admin_contact_list.xhtml @@ -91,13 +91,6 @@ -<<<<<<< HEAD -

- -======= ->>>>>>> 2b3f1b133 (Please cherry-pick:) diff --git a/web/admin/user/admin_user_show.xhtml b/web/admin/user/admin_user_show.xhtml index b29b1353..89f0b91b 100644 --- a/web/admin/user/admin_user_show.xhtml +++ b/web/admin/user/admin_user_show.xhtml @@ -28,7 +28,12 @@ - + -- 2.39.5