2 * Copyright (C) 2016, 2017 Roland Häder
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU Affero General Public License as
6 * published by the Free Software Foundation, either version 3 of the
7 * License, or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU Affero General Public License for more details.
14 * You should have received a copy of the GNU Affero General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 package org.mxchange.jjobs.beans.user;
19 import java.text.MessageFormat;
20 import java.util.Locale;
21 import java.util.Objects;
23 import javax.enterprise.context.RequestScoped;
24 import javax.enterprise.event.Event;
25 import javax.enterprise.event.Observes;
26 import javax.enterprise.inject.Any;
27 import javax.faces.FacesException;
28 import javax.faces.context.FacesContext;
29 import javax.faces.view.facelets.FaceletException;
30 import javax.inject.Inject;
31 import javax.inject.Named;
32 import org.mxchange.jcontacts.contact.Contact;
33 import org.mxchange.jcoreee.utils.FacesUtils;
34 import org.mxchange.jjobs.beans.BaseJobsController;
35 import org.mxchange.jjobs.beans.contact.JobsAdminContactWebRequestController;
36 import org.mxchange.jjobs.beans.contact.JobsContactWebSessionController;
37 import org.mxchange.jjobs.beans.localization.JobsLocalizationSessionController;
38 import org.mxchange.jusercore.events.user.add.AdminAddedUserEvent;
39 import org.mxchange.jusercore.events.user.add.ObservableAdminAddedUserEvent;
40 import org.mxchange.jusercore.events.user.created.ObservableCreatedUserEvent;
41 import org.mxchange.jusercore.events.user.delete.AdminDeletedUserEvent;
42 import org.mxchange.jusercore.events.user.delete.ObservableAdminDeletedUserEvent;
43 import org.mxchange.jusercore.events.user.linked.AdminLinkedUserEvent;
44 import org.mxchange.jusercore.events.user.linked.ObservableAdminLinkedUserEvent;
45 import org.mxchange.jusercore.events.user.locked.AdminLockedUserEvent;
46 import org.mxchange.jusercore.events.user.locked.ObservableAdminLockedUserEvent;
47 import org.mxchange.jusercore.events.user.unlocked.AdminUnlockedUserEvent;
48 import org.mxchange.jusercore.events.user.unlocked.ObservableAdminUnlockedUserEvent;
49 import org.mxchange.jusercore.events.user.update.AdminUpdatedUserDataEvent;
50 import org.mxchange.jusercore.events.user.update.ObservableAdminUpdatedUserDataEvent;
51 import org.mxchange.jusercore.exceptions.EmailAddressAlreadyRegisteredException;
52 import org.mxchange.jusercore.exceptions.UserNameAlreadyRegisteredException;
53 import org.mxchange.jusercore.exceptions.UserNotFoundException;
54 import org.mxchange.jusercore.exceptions.UserStatusConfirmedException;
55 import org.mxchange.jusercore.exceptions.UserStatusLockedException;
56 import org.mxchange.jusercore.exceptions.UserStatusUnconfirmedException;
57 import org.mxchange.jusercore.model.user.AdminUserSessionBeanRemote;
58 import org.mxchange.jusercore.model.user.LoginUser;
59 import org.mxchange.jusercore.model.user.User;
60 import org.mxchange.jusercore.model.user.UserSessionBeanRemote;
61 import org.mxchange.jusercore.model.user.profilemodes.ProfileMode;
62 import org.mxchange.jusercore.model.user.status.UserAccountStatus;
63 import org.mxchange.juserlogincore.container.login.UserLoginContainer;
64 import org.mxchange.juserlogincore.events.registration.ObservableUserRegisteredEvent;
65 import org.mxchange.juserlogincore.exceptions.UserPasswordRepeatMismatchException;
66 import org.mxchange.juserlogincore.login.UserLoginUtils;
69 * A user controller (bean)
71 * @author Roland Häder<roland@mxchange.org>
73 @Named ("adminUserController")
75 public class JobsAdminUserWebRequestBean extends BaseJobsController implements JobsAdminUserWebRequestController {
80 private static final long serialVersionUID = 542_145_347_916L;
83 * An event fired when the administrator has added a new user
87 private Event<ObservableAdminAddedUserEvent> addedUserEvent;
90 * Regular contact controller
93 private JobsAdminContactWebRequestController adminContactController;
96 * Administrative user EJB
98 @EJB (lookup = "java:global/jjobs-ejb/adminUser!org.mxchange.jusercore.model.user.AdminUserSessionBeanRemote")
99 private AdminUserSessionBeanRemote adminUserBean;
104 private Contact contact;
107 * Regular contact controller
110 private JobsContactWebSessionController contactController;
113 * Event being fired when admin has deleted user
117 private Event<ObservableAdminDeletedUserEvent> deleteUserEvent;
120 * Localization controller
123 private JobsLocalizationSessionController localizationController;
126 * An event fired when the administrator has updated a new user
130 private Event<ObservableAdminUpdatedUserDataEvent> updatedUserDataEvent;
140 @EJB (lookup = "java:global/jjobs-ejb/user!org.mxchange.jusercore.model.user.UserSessionBeanRemote")
141 private UserSessionBeanRemote userBean;
144 * Regular user controller
147 private JobsUserWebSessionController userController;
152 private String userDeleteReason;
155 * An event fired when the administrator has linked a user with existing
160 private Event<ObservableAdminLinkedUserEvent> userLinkedEvent;
165 private String userLockReason;
168 * Event being fired when an administrator has locked a user
172 private Event<ObservableAdminLockedUserEvent> userLockedEvent;
175 * Flag whether user must change password after login
177 private Boolean userMustChangePassword;
182 private String userName;
185 * User password (clear-text from web form)
187 private String userPassword;
190 * User password repeated (clear-text from web form)
192 private String userPasswordRepeat;
195 * Event being fired when administrator unlocks an account
199 private Event<ObservableAdminUnlockedUserEvent> userUnlockedEvent;
202 * Default constructor
204 public JobsAdminUserWebRequestBean () {
205 // Call super constructor
210 * Adds user instance to database by preparing a complete user instance and
211 * sending it to the EJB. The data set in the controller is being verified,
212 * e.g. if the user name or email address is not used yet.
214 * @return Redirect outcome
216 public String addUser () {
217 System.out.println("addUser: this.contact=" + this.getContact());
218 // As the form cannot validate the data (required="true"), check it here
219 if (this.getUserName() == null) {
221 throw new NullPointerException("userName is null"); //NOI18N
222 } else if (this.getUserName().isEmpty()) {
224 throw new IllegalArgumentException("userName is null"); //NOI18N
225 } else if (this.getContact() == null) {
226 // No contact instance set, so test required fields: gender, first name and family name
227 this.adminContactController.validateContactData();
230 // Create new user instance
231 User newUser = new LoginUser();
233 // Set user name, CONFIRMED and INVISIBLE
234 newUser.setUserName(this.getUserName());
235 newUser.setUserMustChangePassword(this.getUserMustChangePassword());
236 newUser.setUserAccountStatus(UserAccountStatus.CONFIRMED);
237 newUser.setUserProfileMode(ProfileMode.INVISIBLE);
239 // Get locale from view-root
240 Locale locale = FacesContext.getCurrentInstance().getViewRoot().getLocale();
243 newUser.setUserLocale(locale);
248 // Is a contact instance in helper set?
249 if (this.getContact() instanceof Contact) {
250 // Then use it for contact linking
251 userContact = this.getContact();
253 // Create contact instance
254 userContact = this.contactController.createContactInstance();
257 // Set contact in user
258 newUser.setUserContact(userContact);
260 // Init variable for password
261 String password = null;
263 // Is the user name or email address used already?
264 // @TODO Add password length check
265 if (this.userController.isUserNameRegistered(newUser)) {
266 // User name is already used
267 throw new FaceletException(new UserNameAlreadyRegisteredException(newUser));
268 } else if ((this.getContact() == null) && (this.contactController.isEmailAddressRegistered(newUser.getUserContact()))) {
269 // Email address is already used
270 this.showFacesMessage("admin_add_user:emailAddress", "ERROR_EMAIL_ADDRESS_ALREADY_USED"); //NOI18N
272 // Always clear password
273 this.setUserPassword(null);
274 this.setUserPasswordRepeat(null);
278 } else if ((this.getUserPassword() == null && (this.getUserPasswordRepeat() == null)) || ((this.getUserPassword().isEmpty()) && (this.getUserPasswordRepeat().isEmpty()))) {
279 // Empty password entered, then generate one
280 password = UserLoginUtils.createRandomPassword(JobsUserWebSessionController.MINIMUM_PASSWORD_LENGTH);
281 } else if (!this.isSamePasswordEntered()) {
282 // Both passwords don't match
283 throw new FaceletException(new UserPasswordRepeatMismatchException(newUser));
285 // Both match, so get it from this bean
286 password = this.getUserPassword();
289 // The password should not be null and at least 5 characters long
290 assert (password != null) : "password is null"; //NOI18N
291 assert (password.length() >= JobsUserWebSessionController.MINIMUM_PASSWORD_LENGTH) : "Password is not long enough."; //NOI18N
293 // Encrypt password and set it
294 newUser.setUserEncryptedPassword(UserLoginUtils.encryptPassword(password));
297 // Now, that all is set, call EJB
298 if (this.getContact() instanceof Contact) {
299 // Link contact with this user
300 User updatedUser = this.adminUserBean.linkUser(newUser);
303 this.userLinkedEvent.fire(new AdminLinkedUserEvent(updatedUser));
306 User updatedUser = this.adminUserBean.addUser(newUser);
309 this.addedUserEvent.fire(new AdminAddedUserEvent(updatedUser));
311 } catch (final UserNameAlreadyRegisteredException | EmailAddressAlreadyRegisteredException ex) {
313 throw new FaceletException(ex);
317 this.setContact(null);
322 // Return to user list (for now)
323 return "admin_list_user"; //NOI18N
327 * Event observer for when a bean helper has successfully created a user
328 * instance, means the user exists. If the user does not exist, this event
329 * should not fire but instead a proper exception must be thrown.
331 * @param event User created event
333 public void afterCreatedUserEvent (@Observes final ObservableCreatedUserEvent event) {
334 // Is the instance valid?
337 throw new NullPointerException("event is null"); //NOI18N
338 } else if (event.getCreatedUser() == null) {
340 throw new NullPointerException("event.createdUser is null"); //NOI18N
341 } else if (event.getCreatedUser().getUserId() == null) {
343 throw new NullPointerException("event.createdUser.userId is null"); //NOI18N
344 } else if (event.getCreatedUser().getUserId() < 1) {
346 throw new NullPointerException(MessageFormat.format("event.createdUser.userId={0} is not valid", event.getCreatedUser().getUserId())); //NOI18N
350 this.setUser(event.getCreatedUser());
354 * Event observer for new user registrations
356 * @param event User registration event
358 public void afterUserRegistrationEvent (@Observes final ObservableUserRegisteredEvent event) {
359 // event should not be null
362 throw new NullPointerException("event is null"); //NOI18N
363 } else if (event.getRegisteredUser() == null) {
365 throw new NullPointerException("event.user is null"); //NOI18N
366 } else if (event.getRegisteredUser().getUserId() == null) {
368 throw new NullPointerException("event.user.userId is null"); //NOI18N
369 } else if (event.getRegisteredUser().getUserId() < 1) {
371 throw new IllegalArgumentException(MessageFormat.format("userId of user={0} is not valid: {1}", event.getRegisteredUser(), event.getRegisteredUser().getUserId())); //NOI18N
375 User registeredUser = event.getRegisteredUser();
377 // @TODO Nothing to do with the user here?
383 * Deletes given user account
385 * @return Redirect outcome
387 public String deleteUserData () {
388 // Is the user instance valid and CONFIRMED?
389 if (this.getUser() == null) {
391 throw new NullPointerException("user is null"); //NOI18N
392 } else if (this.getUser().getUserId() == null) {
394 throw new NullPointerException("user.userId is null"); //NOI18N
395 } else if (this.getUser().getUserId() < 1) {
397 throw new IllegalArgumentException(MessageFormat.format("user.userId={0} is not valid", this.getUser().getUserId())); //NOI18N
401 // All fine, delete it
402 this.adminUserBean.deleteUser(this.getUser(), this.getUserDeleteReason());
403 } catch (final UserNotFoundException ex) {
404 // Should not happen, so throw again
405 throw new FaceletException(ex);
409 this.deleteUserEvent.fire(new AdminDeletedUserEvent(this.getUser(), this.getUserDeleteReason()));
412 return "admin_list_user"; //NOI18N
416 * Edits currently loaded user's data in database.
418 * @return Redirect outcome
420 public String editUserData () {
421 // Null password means not setting it
422 String encryptedPassword = null;
424 // Check if user instance is in helper and valid
425 if (this.getUser() == null) {
427 throw new NullPointerException("beanHelper.user is null"); //NOI18N
428 } else if (this.getUser().getUserId() == null) {
430 throw new NullPointerException("beanHelper.user.userId is null"); //NOI18N
431 } else if (this.getUser().getUserId() < 1) {
433 throw new IllegalStateException(MessageFormat.format("beanHelper.user.userId={0} is invalid", this.getUser().getUserId())); //NOI18N
434 } else if (this.getUserName() == null) {
435 // Not all required fields are set
436 throw new NullPointerException("this.userName is null"); //NOI18N
437 } else if (this.getUserName().isEmpty()) {
438 // Not all required fields are set
439 throw new IllegalArgumentException("this.userName is empty"); //NOI18N
440 } else if (((!this.getUserPassword().isEmpty()) || (!this.getUserPasswordRepeat().isEmpty())) && (!this.isSamePasswordEntered())) {
441 // Clear password fields
442 this.setUserPassword(null);
443 this.setUserPasswordRepeat(null);
445 // Not same password entered
446 this.showFacesMessage("form_edit_user:userPassword", "ADMIN_USER_PASSWORD_REPEAT_DIFFERENT"); //NOI18N
448 } else if ((!Objects.equals(this.getUser().getUserName(), this.getUserName())) && (this.userBean.ifUserNameExists(this.getUserName()))) {
452 // User name already exists
453 this.showFacesMessage("form_edit_user:userName", "ADMIN_USER_NAME_ALREADY_EXISTS"); //NOI18N
455 } else if (this.isSamePasswordEntered()) {
456 // Same password entered, create container
457 if ((Objects.equals(this.getUser().getUserMustChangePassword(), this.getUserMustChangePassword())) && (UserLoginUtils.ifPasswordMatches(new UserLoginContainer(this.getUser(), this.getUserPassword())))) {
458 // Clear password fields
459 this.setUserPassword(null);
460 this.setUserPasswordRepeat(null);
462 // Same password entered
463 this.showFacesMessage("form_edit_user:userPassword", "ADMIN_USER_ENTERED_SAME_AS_OLD_PASSWORD"); //NOI18N
468 encryptedPassword = UserLoginUtils.encryptPassword(this.getUserPassword());
471 // Set user name and flag
472 this.getUser().setUserName(this.getUserName());
473 this.getUser().setUserMustChangePassword(this.getUserMustChangePassword());
475 // Is a password set?
476 if (encryptedPassword != null) {
478 this.getUser().setUserEncryptedPassword(encryptedPassword);
481 // Call EJB for updating user data
482 User updatedUser = this.userBean.updateUserData(this.getUser());
485 this.updatedUserDataEvent.fire(new AdminUpdatedUserDataEvent(updatedUser));
487 // Return to user list (for now)
488 return "admin_list_user"; //NOI18N
492 * Getter for contact instance
494 * @return Contact instance
496 public Contact getContact () {
501 * Setter for contact instance
503 * @param contact Contact instance
505 public void setContact (final Contact contact) {
506 this.contact = contact;
510 * Getter for user instance
512 * @return User instance
514 public User getUser () {
519 * Setter for user instance
521 * @param user User instance
523 public void setUser (final User user) {
528 * Getter for user delete reason
530 * @return User delete reason
532 public String getUserDeleteReason () {
533 return this.userDeleteReason;
537 * Setter for user delete reason
539 * @param userDeleteReason User delete reason
541 public void setUserDeleteReason (final String userDeleteReason) {
542 this.userDeleteReason = userDeleteReason;
546 * Getter for user lock reason
548 * @return User lock reason
550 public String getUserLockReason () {
551 return this.userLockReason;
555 * Setter for user lock reason
557 * @param userLockReason User lock reason
559 public void setUserLockReason (final String userLockReason) {
560 this.userLockReason = userLockReason;
564 * Getter for flag if user needs to change password
566 * @return Flag if user needs to change password
568 public Boolean getUserMustChangePassword () {
569 return this.userMustChangePassword;
573 * Setter for flag if user needs to change password
575 * @param userMustChangePassword Flag if user needs to change password
577 public void setUserMustChangePassword (final Boolean userMustChangePassword) {
578 this.userMustChangePassword = userMustChangePassword;
582 * Getter for user name
586 public String getUserName () {
587 return this.userName;
591 * Setter for user name
593 * @param userName User name
595 public void setUserName (final String userName) {
596 this.userName = userName;
600 * Getter for clear-text user password
602 * @return Clear-text user password
604 public String getUserPassword () {
605 return this.userPassword;
609 * Setter for clear-text user password
611 * @param userPassword Clear-text user password
613 public void setUserPassword (final String userPassword) {
614 this.userPassword = userPassword;
618 * Getter for clear-text user password repeated
620 * @return Clear-text user password repeated
622 public String getUserPasswordRepeat () {
623 return this.userPasswordRepeat;
627 * Setter for clear-text user password repeated
629 * @param userPasswordRepeat Clear-text user password repeated
631 public void setUserPasswordRepeat (final String userPasswordRepeat) {
632 this.userPasswordRepeat = userPasswordRepeat;
636 * Locks selected user's account. This method makes sure that a lock reason
637 * is provided that th user later can read on login attempts.
639 * @return Redirect outcome
641 public String lockUserAccount () {
642 // Is the user instance valid and CONFIRMED?
643 if (this.getUser() == null) {
645 throw new NullPointerException("this.user is null"); //NOI18N
646 } else if (this.getUser().getUserId() == null) {
648 throw new NullPointerException("this.user.userId is null"); //NOI18N
649 } else if (this.getUser().getUserId() < 1) {
651 throw new IllegalArgumentException(MessageFormat.format("this.user.userId={0} is not valid", this.getUser().getUserId())); //NOI18N
652 } else if (this.getUser().getUserAccountStatus() == UserAccountStatus.LOCKED) {
653 // User account is locked
654 throw new FacesException(new UserStatusLockedException(this.getUser()));
655 } else if (this.getUser().getUserAccountStatus() == UserAccountStatus.UNCONFIRMED) {
656 // User account is locked
657 throw new FaceletException(new UserStatusUnconfirmedException(this.getUser()));
658 } else if (this.getUserLockReason() == null) {
660 throw new NullPointerException("this.userLockReason is null"); //NOI18N
661 } else if (this.getUserLockReason().isEmpty()) {
663 throw new IllegalArgumentException("this.userLockReason is empty"); //NOI18N
666 // Init updated user instance
671 String baseUrl = FacesUtils.generateBaseUrl();
673 // Call EJB to lock account
674 updatedUser = this.adminUserBean.lockUserAccount(this.getUser(), this.getUserLockReason(), baseUrl);
675 } catch (final UserStatusLockedException | UserStatusUnconfirmedException | UserNotFoundException ex) {
677 throw new FaceletException(ex);
681 this.userLockedEvent.fire(new AdminLockedUserEvent(updatedUser));
686 // Should go fine at this point, redirect to user profile
687 return "admin_show_user"; //NOI18N
691 * Unlocks selected user's account. This method makes sure that the account
694 * @return Redirect outcome
696 public String unlockUserAccount () {
697 // Is the user instance valid and CONFIRMED?
698 if (this.getUser() == null) {
700 throw new NullPointerException("this.user is null"); //NOI18N
701 } else if (this.getUser().getUserId() == null) {
703 throw new NullPointerException("this.user.userId is null"); //NOI18N
704 } else if (this.getUser().getUserId() < 1) {
706 throw new IllegalArgumentException(MessageFormat.format("this.user.userId={0} is not valid", this.getUser().getUserId())); //NOI18N
707 } else if (this.getUser().getUserAccountStatus() == UserAccountStatus.CONFIRMED) {
708 // User account is locked
709 throw new FacesException(new UserStatusConfirmedException(this.getUser()));
710 } else if (this.getUser().getUserAccountStatus() == UserAccountStatus.UNCONFIRMED) {
711 // User account is locked
712 throw new FaceletException(new UserStatusUnconfirmedException(this.getUser()));
715 // Init updated user instance
720 String baseUrl = FacesUtils.generateBaseUrl();
722 // Call EJB to unlock account
723 updatedUser = this.adminUserBean.unlockUserAccount(this.getUser(), baseUrl);
724 } catch (final UserStatusConfirmedException | UserStatusUnconfirmedException | UserNotFoundException ex) {
726 throw new FaceletException(ex);
730 this.userUnlockedEvent.fire(new AdminUnlockedUserEvent(updatedUser));
735 // Should go fine at this point, redirect to user profile
736 return "admin_show_user"; //NOI18N
742 private void clear () {
744 this.setContact(null);
745 this.setUserLockReason(null);
746 this.setUserMustChangePassword(null);
747 this.setUserName(null);
751 * Checks if same password is entered and that they are not empty.
753 * @return Whether the same password was entered
755 private boolean isSamePasswordEntered () {
756 return ((!this.getUserPassword().isEmpty()) && (Objects.equals(this.getUserPassword(), this.getUserPasswordRepeat())));