From 9b8284a2cf7a20fa8bcb53380a19571478036138 Mon Sep 17 00:00:00 2001 From: Joern Muehlencord Date: Sun, 25 Nov 2018 15:14:39 +0100 Subject: [PATCH] fixed update of account role mapping --- .../account/web/presentation/AccountView.java | 600 +++++++------- .../account/control/AccountControl.java | 742 +++++++++--------- 2 files changed, 671 insertions(+), 671 deletions(-) diff --git a/account-ui/src/main/java/de/muehlencord/shared/account/web/presentation/AccountView.java b/account-ui/src/main/java/de/muehlencord/shared/account/web/presentation/AccountView.java index f634d61..8e39d74 100644 --- a/account-ui/src/main/java/de/muehlencord/shared/account/web/presentation/AccountView.java +++ b/account-ui/src/main/java/de/muehlencord/shared/account/web/presentation/AccountView.java @@ -1,300 +1,300 @@ -package de.muehlencord.shared.account.web.presentation; - -import de.muehlencord.shared.account.business.account.control.AccountControl; -import de.muehlencord.shared.account.business.application.control.ApplicationRoleControl; -import de.muehlencord.shared.account.business.account.entity.AccountEntity; -import de.muehlencord.shared.account.business.account.entity.AccountException; -import de.muehlencord.shared.account.business.account.entity.AccountLoginEntity; -import de.muehlencord.shared.account.business.account.entity.AccountStatus; -import de.muehlencord.shared.account.business.application.entity.ApplicationRoleEntity; -import de.muehlencord.shared.account.business.application.entity.ApplicationEntity; -import de.muehlencord.shared.jeeutil.FacesUtil; -import java.io.Serializable; -import java.util.ArrayList; -import java.util.List; -import java.util.Locale; -import javax.ejb.EJB; -import javax.faces.component.UIInput; -import javax.faces.context.FacesContext; -import javax.faces.view.ViewScoped; -import javax.inject.Named; -import javax.inject.Inject; -import org.apache.shiro.SecurityUtils; -import org.apache.shiro.subject.Subject; -import org.primefaces.event.SelectEvent; -import org.primefaces.event.UnselectEvent; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * - * @author jomu - */ -@ViewScoped -@Named("accountView") -public class AccountView implements Serializable { - - private static final long serialVersionUID = -8050582392249849438L; - private static final Logger LOGGER = LoggerFactory.getLogger(AccountView.class); - - @Inject - private ApplicationView applicationView; - @EJB - private AccountControl accountService; - @EJB - private ApplicationRoleControl appliationRoleService; - - /** - * boolean flag to determine wether disabled accounts should be shown - * accounts are not deleted but disabled and can be activated in case - */ - private boolean showDisabledAccounts = false; - - // cached accounts - private List accountList = null; - // cached application roles - private List applicationRoles = null; - - // account currently on edit - private AccountEntity currentAccount; - private List currentAccountRoles = null; - private AccountLoginEntity currentAccountLogin; - private String password = null; - private String repeatPassword = null; - - public List getAccounts() { - if (accountList == null) { - accountList = accountService.getAccounts(showDisabledAccounts); - } - return accountList; - } - - public List getAllApplicationRoles() { - if (applicationRoles == null) { - ApplicationEntity application = applicationView.getCurrentApplication(); - applicationRoles = appliationRoleService.getAllRoles(application); - } - return applicationRoles; - } - - public void selectAccount(SelectEvent event) { - // nothing to do, currentAccountRoles are loaded before dialog is shown - } - - public void unselectAccount(UnselectEvent event) { - applicationRoles = null; - currentAccountRoles = null; - } - - public boolean getAccountSelected() { - return currentAccount != null; - } - - public void newAccount() { - currentAccount = new AccountEntity(); - currentAccount.setStatus("NEW"); // TODO add status enum - currentAccountRoles = new ArrayList<>(); - } - - public void editAccount() { - // function called by webpage - if (currentAccount == null) { - currentAccountRoles = null; - } else { - currentAccount = accountService.getAccountEntity(currentAccount.getUsername(), true); - this.currentAccountRoles = new ArrayList<>(); - if (currentAccount.getApplicationRoleList() != null) { - currentAccountRoles.addAll(currentAccount.getApplicationRoleList()); - } - } - } - - public void cancelEditAccount() { - currentAccount = null; - currentAccountRoles = null; - } - - public void saveEditAccount() { - String username = currentAccount.getUsername(); - AccountEntity existingEntity = accountService.getAccountEntity(username, true); - // check if it is a new user (createdBy == null) but a user with same name already exists - if ((currentAccount.getCreatedBy() == null) && (existingEntity != null)) { - FacesUtil.addErrorMessage("editDialogMessaegs", "Create new account failed", "Account with username " + username + " already exists"); - } else { - accountService.saveAccount(currentAccount, currentAccountRoles); - // force accounts to be loaded from database again - accountList = null; - - } - } - - public void deleteAccount() { - try { - accountService.deleteAccount(currentAccount); - accountList.remove(currentAccount); - FacesUtil.addGlobalInfoMessage("Info", "Account " + currentAccount.getUsername() + " deleted"); - currentAccount = null; - currentAccountRoles = null; - } catch (AccountException ex) { - if (LOGGER.isDebugEnabled()) { - LOGGER.debug(ex.toString(), ex); - } else { - LOGGER.error(ex.toString()); - } - - FacesUtil.addGlobalErrorMessage("Error deleting account", ex.getMessage()); - } - } - - public void showDisabledAccountsChange() { - if (LOGGER.isDebugEnabled()) { - LOGGER.debug("show diabled accounts changed to {}", showDisabledAccounts); - } - this.accountList = null; - } - - public List getStatusList() { - return AccountStatus.getAllStatusNames(); - } - - /* **** account login methods **** */ - public boolean validatePasswords(FacesContext context, List components, List values) { - String password = components.get(0).getSubmittedValue().toString(); - String passwordRepeat = components.get(1).getSubmittedValue().toString(); - - if ((password == null) || (passwordRepeat == null)) { - return false; - } - boolean returnValue = password.equals(passwordRepeat); - return returnValue; - } - - public void addAccountLogin() { - if (currentAccount == null) { - // TODO add error handling - } else { - this.currentAccountLogin = accountService.createLoginWithRandomPassword(); - } - } - - public void editAccountLogin() { - if (currentAccount == null) { - // TODO add error handling - } else { - this.currentAccountLogin = currentAccount.getAccountLogin(); - } - } - - public void deleteAccountLogin() { - if (currentAccount == null) { - // TODO add error handling - } else { - accountService.deleteLogin(currentAccount); - currentAccount.setAccountLogin(null); - currentAccountLogin = null; - accountList = null; // force reload - FacesUtil.addGlobalInfoMessage("Account saved", "Login removed"); - } - - } - - public void saveEditAccountLogin() { - // TODO move to account control - to much logic for the view - if ((currentAccountLogin == null) || (currentAccount == null)) { - // TODO add error handling - } else { - - // overwrite password if provided - if ((password != null) && (!password.trim().equals(""))) { - // password has been specified - if (password.equals(repeatPassword)) { - currentAccount.getAccountLogin().setAccountPassword(accountService.getHashedPassword(password)); - FacesUtil.addGlobalInfoMessage("Info", "Password updated"); - } else { - // TODO connect to IPRS - // frontend does validate passwords do match - // someone is trying to cheat - } - } - - if (currentAccountLogin.getId() == null) { - accountService.addLogin(currentAccount, currentAccountLogin); - currentAccount.setAccountLogin(currentAccountLogin); - accountList = null; // force reload of accounts - } else { - accountService.updateLogin(currentAccountLogin); - } - currentAccountLogin = null; - FacesUtil.addGlobalInfoMessage("Account saved", "Login data updated"); - } - } - - public void cancelEditAccountLogin() { - this.currentAccountLogin = null; - } - - public boolean getCurrentLoggedInUser() { - if (currentAccount == null) { - return false; - } - - Subject currentUser = SecurityUtils.getSubject(); - if (currentUser == null) { - // TODO - connect to IPRS - how can this method be called if no user is logged in - return false; - } - String currentUserName = currentUser.getPrincipal().toString(); - return currentUserName.equals(currentAccount.getUsername()); - } - - /* **** getter / setter **** */ - /** - * setter for managed property applicationView - * - * @param applicationView the applicaton view to inject - */ - public void setApplicationView(ApplicationView applicationView) { - this.applicationView = applicationView; - } - - public AccountEntity getCurrentAccount() { - return currentAccount; - } - - public void setCurrentAccount(AccountEntity currentAccount) { - this.currentAccount = currentAccount; - } - - public boolean isShowDisabledAccounts() { - return showDisabledAccounts; - } - - public void setShowDisabledAccounts(boolean showDisabledAccounts) { - this.showDisabledAccounts = showDisabledAccounts; - } - - public List getCurrentAccountRoles() { - return currentAccountRoles; - } - - public void setCurrentAccountRoles(List currentAccountRoles) { - this.currentAccountRoles = currentAccountRoles; - } - - public String getPassword() { - return password; - } - - public void setPassword(String password) { - this.password = password; - } - - public String getRepeatPassword() { - return repeatPassword; - } - - public void setRepeatPassword(String repeatPassword) { - this.repeatPassword = repeatPassword; - } - -} +package de.muehlencord.shared.account.web.presentation; + +import de.muehlencord.shared.account.business.account.control.AccountControl; +import de.muehlencord.shared.account.business.application.control.ApplicationRoleControl; +import de.muehlencord.shared.account.business.account.entity.AccountEntity; +import de.muehlencord.shared.account.business.account.entity.AccountException; +import de.muehlencord.shared.account.business.account.entity.AccountLoginEntity; +import de.muehlencord.shared.account.business.account.entity.AccountStatus; +import de.muehlencord.shared.account.business.application.entity.ApplicationRoleEntity; +import de.muehlencord.shared.account.business.application.entity.ApplicationEntity; +import de.muehlencord.shared.jeeutil.FacesUtil; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; +import javax.ejb.EJB; +import javax.faces.component.UIInput; +import javax.faces.context.FacesContext; +import javax.faces.view.ViewScoped; +import javax.inject.Named; +import javax.inject.Inject; +import org.apache.shiro.SecurityUtils; +import org.apache.shiro.subject.Subject; +import org.primefaces.event.SelectEvent; +import org.primefaces.event.UnselectEvent; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * + * @author jomu + */ +@ViewScoped +@Named("accountView") +public class AccountView implements Serializable { + + private static final long serialVersionUID = -8050582392249849438L; + private static final Logger LOGGER = LoggerFactory.getLogger(AccountView.class); + + @Inject + private ApplicationView applicationView; + @EJB + private AccountControl accountService; + @EJB + private ApplicationRoleControl appliationRoleService; + + /** + * boolean flag to determine wether disabled accounts should be shown + * accounts are not deleted but disabled and can be activated in case + */ + private boolean showDisabledAccounts = false; + + // cached accounts + private List accountList = null; + // cached application roles + private List applicationRoles = null; + + // account currently on edit + private AccountEntity currentAccount; + private List currentAccountRoles = null; + private AccountLoginEntity currentAccountLogin; + private String password = null; + private String repeatPassword = null; + + public List getAccounts() { + if (accountList == null) { + accountList = accountService.getAccounts(showDisabledAccounts); + } + return accountList; + } + + public List getAllApplicationRoles() { + if (applicationRoles == null) { + ApplicationEntity application = applicationView.getCurrentApplication(); + applicationRoles = appliationRoleService.getAllRoles(application); + } + return applicationRoles; + } + + public void selectAccount(SelectEvent event) { + // nothing to do, currentAccountRoles are loaded before dialog is shown + } + + public void unselectAccount(UnselectEvent event) { + applicationRoles = null; + currentAccountRoles = null; + } + + public boolean getAccountSelected() { + return currentAccount != null; + } + + public void newAccount() { + currentAccount = new AccountEntity(); + currentAccount.setStatus("NEW"); // TODO add status enum + currentAccountRoles = new ArrayList<>(); + } + + public void editAccount() { + // function called by webpage + if (currentAccount == null) { + currentAccountRoles = null; + } else { + currentAccount = accountService.getAccountEntity(currentAccount.getUsername(), true); + this.currentAccountRoles = new ArrayList<>(); + if (currentAccount.getApplicationRoleList() != null) { + currentAccountRoles.addAll(currentAccount.getApplicationRoleList()); + } + } + } + + public void cancelEditAccount() { + currentAccount = null; + currentAccountRoles = null; + } + + public void saveEditAccount() { + String username = currentAccount.getUsername(); + AccountEntity existingEntity = accountService.getAccountEntity(username, true); + // check if it is a new user (createdBy == null) but a user with same name already exists + if ((currentAccount.getCreatedBy() == null) && (existingEntity != null)) { + FacesUtil.addErrorMessage("editDialogMessaegs", "Create new account failed", "Account with username " + username + " already exists"); + } else { + accountService.saveAccount(currentAccount, applicationView.getCurrentApplication(), currentAccountRoles); + // force accounts to be loaded from database again + accountList = null; + + } + } + + public void deleteAccount() { + try { + accountService.deleteAccount(currentAccount); + accountList.remove(currentAccount); + FacesUtil.addGlobalInfoMessage("Info", "Account " + currentAccount.getUsername() + " deleted"); + currentAccount = null; + currentAccountRoles = null; + } catch (AccountException ex) { + if (LOGGER.isDebugEnabled()) { + LOGGER.debug(ex.toString(), ex); + } else { + LOGGER.error(ex.toString()); + } + + FacesUtil.addGlobalErrorMessage("Error deleting account", ex.getMessage()); + } + } + + public void showDisabledAccountsChange() { + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("show diabled accounts changed to {}", showDisabledAccounts); + } + this.accountList = null; + } + + public List getStatusList() { + return AccountStatus.getAllStatusNames(); + } + + /* **** account login methods **** */ + public boolean validatePasswords(FacesContext context, List components, List values) { + String password = components.get(0).getSubmittedValue().toString(); + String passwordRepeat = components.get(1).getSubmittedValue().toString(); + + if ((password == null) || (passwordRepeat == null)) { + return false; + } + boolean returnValue = password.equals(passwordRepeat); + return returnValue; + } + + public void addAccountLogin() { + if (currentAccount == null) { + // TODO add error handling + } else { + this.currentAccountLogin = accountService.createLoginWithRandomPassword(); + } + } + + public void editAccountLogin() { + if (currentAccount == null) { + // TODO add error handling + } else { + this.currentAccountLogin = currentAccount.getAccountLogin(); + } + } + + public void deleteAccountLogin() { + if (currentAccount == null) { + // TODO add error handling + } else { + accountService.deleteLogin(currentAccount); + currentAccount.setAccountLogin(null); + currentAccountLogin = null; + accountList = null; // force reload + FacesUtil.addGlobalInfoMessage("Account saved", "Login removed"); + } + + } + + public void saveEditAccountLogin() { + // TODO move to account control - to much logic for the view + if ((currentAccountLogin == null) || (currentAccount == null)) { + // TODO add error handling + } else { + + // overwrite password if provided + if ((password != null) && (!password.trim().equals(""))) { + // password has been specified + if (password.equals(repeatPassword)) { + currentAccountLogin.setAccountPassword(accountService.getHashedPassword(password)); + FacesUtil.addGlobalInfoMessage("Info", "Password updated"); + } else { + // TODO connect to IPRS + // frontend does validate passwords do match + // someone is trying to cheat + } + } + + if (currentAccountLogin.getId() == null) { + accountService.addLogin(currentAccount, currentAccountLogin); + currentAccount.setAccountLogin(currentAccountLogin); + accountList = null; // force reload of accounts + } else { + accountService.updateLogin(currentAccountLogin); + } + currentAccountLogin = null; + FacesUtil.addGlobalInfoMessage("Account saved", "Login data updated"); + } + } + + public void cancelEditAccountLogin() { + this.currentAccountLogin = null; + } + + public boolean getCurrentLoggedInUser() { + if (currentAccount == null) { + return false; + } + + Subject currentUser = SecurityUtils.getSubject(); + if (currentUser == null) { + // TODO - connect to IPRS - how can this method be called if no user is logged in + return false; + } + String currentUserName = currentUser.getPrincipal().toString(); + return currentUserName.equals(currentAccount.getUsername()); + } + + /* **** getter / setter **** */ + /** + * setter for managed property applicationView + * + * @param applicationView the applicaton view to inject + */ + public void setApplicationView(ApplicationView applicationView) { + this.applicationView = applicationView; + } + + public AccountEntity getCurrentAccount() { + return currentAccount; + } + + public void setCurrentAccount(AccountEntity currentAccount) { + this.currentAccount = currentAccount; + } + + public boolean isShowDisabledAccounts() { + return showDisabledAccounts; + } + + public void setShowDisabledAccounts(boolean showDisabledAccounts) { + this.showDisabledAccounts = showDisabledAccounts; + } + + public List getCurrentAccountRoles() { + return currentAccountRoles; + } + + public void setCurrentAccountRoles(List currentAccountRoles) { + this.currentAccountRoles = currentAccountRoles; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public String getRepeatPassword() { + return repeatPassword; + } + + public void setRepeatPassword(String repeatPassword) { + this.repeatPassword = repeatPassword; + } + +} diff --git a/account/src/main/java/de/muehlencord/shared/account/business/account/control/AccountControl.java b/account/src/main/java/de/muehlencord/shared/account/business/account/control/AccountControl.java index d38e789..2581371 100644 --- a/account/src/main/java/de/muehlencord/shared/account/business/account/control/AccountControl.java +++ b/account/src/main/java/de/muehlencord/shared/account/business/account/control/AccountControl.java @@ -1,371 +1,371 @@ -package de.muehlencord.shared.account.business.account.control; - -import de.muehlencord.shared.account.business.account.entity.AccountException; -import de.muehlencord.shared.account.business.account.entity.AccountStatus; -import de.muehlencord.shared.account.business.mail.entity.MailException; -import de.muehlencord.shared.account.business.mail.boundary.MailService; -import de.muehlencord.shared.account.business.account.entity.AccountEntity; -import de.muehlencord.shared.account.business.account.entity.AccountLoginEntity; -import de.muehlencord.shared.account.business.application.entity.ApplicationRoleEntity; -import de.muehlencord.shared.account.business.application.entity.ApplicationEntity; -import de.muehlencord.shared.account.util.AccountPU; -import de.muehlencord.shared.account.util.SecurityUtil; -import de.muehlencord.shared.util.DateUtil; -import java.io.Serializable; -import java.util.ArrayList; -import java.util.Date; -import java.util.List; -import javax.ejb.EJB; -import javax.ejb.Stateless; -import javax.inject.Inject; -import javax.persistence.EntityManager; -import javax.persistence.NoResultException; -import javax.persistence.Query; -import javax.transaction.Transactional; -import org.apache.commons.lang3.RandomStringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.apache.shiro.SecurityUtils; -import org.apache.shiro.subject.Subject; - -/** - * - * @author joern.muehlencord - */ -@Stateless -public class AccountControl implements Serializable { - - private static final Logger LOGGER = LoggerFactory.getLogger(AccountControl.class.getName()); - private static final long serialVersionUID = 3424816272598108101L; - - @EJB - private MailService mailService; - - @Inject - private ApplicationEntity application; - - @Inject - @AccountPU - EntityManager em; - - /** - * returns a list of active accounts - * - * @return a list of active accounts - */ - public List getActiveAccounts() { - Query query = em.createQuery("SELECT a FROM AccountEntity a WHERE a.status <> :status", AccountEntity.class); - query.setParameter("status", AccountStatus.DISABLED.name()); - return query.getResultList(); - } - - /** - * returns a list of active accounts - * - * @return a list of active accounts - */ - public List getAllAccounts() { - Query query = em.createNamedQuery("AccountEntity.findAll"); - return query.getResultList(); - } - - public List getAccounts(boolean includeDisabled) { - if (includeDisabled) { - return getAllAccounts(); - } else { - return getActiveAccounts(); - } - } - - public AccountEntity getAccountEntity(String userName, boolean loadRoles) { - StringBuilder queryBuilder = new StringBuilder(); - queryBuilder.append("SELECT a FROM AccountEntity a "); - if (loadRoles) { - queryBuilder.append("LEFT JOIN FETCH a.applicationRoleList "); - } - queryBuilder.append("WHERE a.username = :username"); - Query query = em.createQuery(queryBuilder.toString()); - query.setParameter("username", userName); - try { - return (AccountEntity) query.getSingleResult(); - } catch (NoResultException ex) { - return null; - } - } - - @Transactional - public AccountEntity saveAccount(AccountEntity account, List applicationRoles) { - Date now = DateUtil.getCurrentTimeInUTC(); - Subject currentUser = SecurityUtils.getSubject(); - String currentLoggedInUser = currentUser.getPrincipal().toString(); - - account.setLastUpdatedBy(currentLoggedInUser); - account.setLastUpdatedOn(now); - - boolean newAccount = (account.getCreatedOn() == null); - - // new account - if (newAccount) { - account.setCreatedOn(now); - account.setCreatedBy(currentLoggedInUser); - em.persist(account); - } else { - em.merge(account); - - // reload account from db and join roles - account = getAccountEntity(account.getUsername(), true); - } - - // assign roles to account - if (account.getApplicationRoleList() == null) { - account.setApplicationRoleList(new ArrayList<>()); - } - - boolean roleSetupChanged = false; - // remove roles which are no longer listed - // ensure this is only done for the given application - keep the other applications untouched - List assignedRoles = new ArrayList<>(); - assignedRoles.addAll(account.getApplicationRoleList()); - for (ApplicationRoleEntity currentlyAssignedRole : assignedRoles) { - if ((currentlyAssignedRole.getApplication().equals(application) && (!applicationRoles.contains(currentlyAssignedRole)))) { - account.getApplicationRoleList().remove(currentlyAssignedRole); - roleSetupChanged = true; - if (LOGGER.isDebugEnabled()) { - LOGGER.debug("Removed role {} ({}) from user {}", currentlyAssignedRole.getRoleName(), application.getApplicationName(), account.getUsername()); - } - } - } - - // add newly added roles to role list - for (ApplicationRoleEntity applicationRole : applicationRoles) { - if (!account.getApplicationRoleList().contains(applicationRole)) { - account.addApplicationRole(applicationRole); - roleSetupChanged = true; - if (LOGGER.isDebugEnabled()) { - LOGGER.debug("Added role {} ({}) to account {}", applicationRole.getRoleName(), application.getApplicationName(), account.getUsername()); - } - } - } - - // update account in database if roles changed - if (roleSetupChanged) { - em.merge(account); - } - return account; - } - - @Transactional - public void deleteAccount(AccountEntity account) throws AccountException { - Date now = new Date(); // Todo now in UTC - Subject currentUser = SecurityUtils.getSubject(); - String currentUserName = currentUser.getPrincipal().toString(); - - if (account.getUsername().equals(currentUserName)) { - throw new AccountException("Cannot delete own account"); - } else { - account.setStatus(AccountStatus.DISABLED.name()); - account.setLastUpdatedBy(currentUserName); - account.setLastUpdatedOn(now); - em.merge(account); - } - - } - - public boolean initPasswordReset(String userName) { - try { - AccountEntity account = getAccountEntity(userName, false); - if (account == null) { - LOGGER.warn("Account with name " + userName + " not found"); - return false; - } - - if (account.getStatus().equals(AccountStatus.BLOCKED.name())) { - LOGGER.warn("Account " + userName + " is locked, cannot initialize password reset"); - return false; - } - - String randomString = RandomStringUtils.random(40, true, true); - - Date validTo = new Date(); // TODO now in UTC - validTo = new Date(validTo.getTime() + 1000 * 600); // 10 minutes to react - - // TODO rework password reset -// account.setPasswordResetHash(randomString); -// account.setPasswordResetOngoing(true); -// account.setPasswordResetValidTo(validTo); - mailService.sendPasswortResetStartEmail(account, randomString); - - em.merge(account); - return true; - } catch (MailException ex) { - LOGGER.error("Error while sending password reset mail. " + ex.toString()); - if (LOGGER.isDebugEnabled()) { - LOGGER.debug("Error while sending password reset mail.", ex); - } - return false; - } - } - - public boolean resetPassword(String userName, String newPassword, String resetPasswordToken) { - AccountEntity account = getAccountEntity(userName, false); - - if (account == null) { - LOGGER.warn("Error while resetting password, no account with username " + userName + " found"); - // TODO add extra logging for intrusion protection system like fail2ban - return false; - } - - /* - if (account.getPasswordResetOngoing() && (account.getPasswordResetHash() != null) && (account.getPasswordResetValidTo() != null)) { - Date now = new Date(); // TODO now in UTC - String storedHash = account.getPasswordResetHash().trim(); - if (account.getPasswordResetValidTo().after(now)) { - if (storedHash.equals(resetPasswordToken)) { - // everything ok, reset password - executePasswordReset(account, newPassword); - LOGGER.info("Updated password for user " + userName); - return true; - } else { - // token is not valid, refuse to change password - LOGGER.warn("Trying to reset password for user " + userName + " but wrong token " + resetPasswordToken + " provided"); - addLoginError(account); - return false; - } - } else { - // password reset token no longer valid - LOGGER.warn("Trying to reset password for user " + userName + " but token is no longer valid"); - addLoginError(account); - return false; - } - } else { - // user is not is password reset mode - LOGGER.warn("Trying to reset password for user " + userName + " but password reset was not requested"); - addLoginError(account); - return false; - } - */ - return false; // FIMXE re-implement password reset - } - - private void executePasswordReset(AccountEntity account, String newPassword) { - Date now = new Date(); // TODO now in UTC - - String hashedPassword = SecurityUtil.createPassword(newPassword); -// account.setAccountPassword(hashedPassword); -// -// account.setPasswordResetOngoing(false); -// account.setPasswordResetHash(null); -// account.setPasswordResetValidTo(null); - - account.setLastUpdatedBy(account.getUsername()); - account.setLastUpdatedOn(now); - em.merge(account); - - } - - public AccountLoginEntity updateSuccessFullLogin(AccountLoginEntity login, String byUser) { - Date now = DateUtil.getCurrentTimeInUTC(); - // a scucessful login ends a password reset procedure - if (login.getPasswordResetOngoing()) { - login.setPasswordResetOngoing(false); - login.setPasswordResetHash(null); - login.setPasswordResetValidTo(null); - login.setLastUpdatedOn(now); - login.setLastUpdatedBy(byUser); - } - - login.setLastLogin(now); - login.setFailureCount(0); - return updateLogin(login); - } - - public AccountLoginEntity updateLogin(AccountLoginEntity login) { - return em.merge(login); - } - - public void updateLogin(AccountEntity account) { - if (account.getAccountLogin() == null) { - // TODO connect to IPRS - how can an account ask for an updated login if the user cannot login - } else { - updateSuccessFullLogin(account.getAccountLogin(), account.getUsername()); - } - } - - public void addLoginError(AccountEntity account) { - // TODO reimplement -// try { -// Date now = new Date(); // TODO now in UTC -// account.setLastFailedLogin(now); -// account.setFailureCount(account.getFailureCount() + 1); -// -// int maxFailedLogins = Integer.parseInt(configService.getConfigValue( "account.maxFailedLogins")); -// if ((account.getFailureCount() >= maxFailedLogins) && (!account.getStatus().equals("LOCKED"))) { // TOD add status enum -// // max failed logins reached, disabling user -// LOGGER.info("Locking account " + account.getUsername() + " due to " + account.getFailureCount() + " failed logins"); -// account.setStatus(AccountStatus.BLOCKED.name()); -// } -// -// // on a failed login request, disable password reset -// account.setPasswordResetOngoing(false); -// account.setPasswordResetHash(null); -// account.setPasswordResetValidTo(null); -// -// account.setLastUpdatedBy("system"); -// account.setLastUpdatedOn(now); -// em.merge(account); -// } catch (ConfigException ex) { -// if (LOGGER.isDebugEnabled()) { -// LOGGER.debug(ex.toString(), ex); -// } else { -// LOGGER.error(ex.toString()); -// } -// } - } - - public AccountLoginEntity createLoginWithRandomPassword() { - AccountLoginEntity login = new AccountLoginEntity(); - String randomPassword = RandomStringUtils.random(20, true, true); - String hashedPassword = SecurityUtil.createPassword(randomPassword); - login.setAccountPassword(hashedPassword); - login.setLastLogin(null); - login.setLastFailedLogin(null); - login.setFailureCount(0); - - return login; - } - - public String getHashedPassword(String password) { - String hashedPassword = SecurityUtil.createPassword(password); - return hashedPassword; - } - - @Transactional - public void addLogin(AccountEntity accountToAdd, AccountLoginEntity accountLogin) { - Date now = DateUtil.getCurrentTimeInUTC(); - Subject currentUser = SecurityUtils.getSubject(); - String currentLoggedInUser = currentUser.getPrincipal().toString(); - - AccountEntity account = em.merge(accountToAdd); - accountLogin.setAccount(account); - accountLogin.setCreatedBy(currentLoggedInUser); - accountLogin.setCreatedOn(now); - accountLogin.setLastUpdatedBy(currentLoggedInUser); - accountLogin.setLastUpdatedOn(now); - em.persist(accountLogin); - - account.setAccountLogin(accountLogin); - em.merge(account); - - } - - @Transactional - public void deleteLogin(AccountEntity accountToDelete) { - AccountEntity account = em.merge(accountToDelete); - AccountLoginEntity login = account.getAccountLogin(); - login.setAccount(null); - account.setAccountLogin(null); - em.remove(login); - em.merge(account); - } - -} +package de.muehlencord.shared.account.business.account.control; + +import de.muehlencord.shared.account.business.account.entity.AccountException; +import de.muehlencord.shared.account.business.account.entity.AccountStatus; +import de.muehlencord.shared.account.business.mail.entity.MailException; +import de.muehlencord.shared.account.business.mail.boundary.MailService; +import de.muehlencord.shared.account.business.account.entity.AccountEntity; +import de.muehlencord.shared.account.business.account.entity.AccountLoginEntity; +import de.muehlencord.shared.account.business.application.entity.ApplicationRoleEntity; +import de.muehlencord.shared.account.business.application.entity.ApplicationEntity; +import de.muehlencord.shared.account.util.AccountPU; +import de.muehlencord.shared.account.util.SecurityUtil; +import de.muehlencord.shared.util.DateUtil; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import javax.ejb.EJB; +import javax.ejb.Stateless; +import javax.inject.Inject; +import javax.persistence.EntityManager; +import javax.persistence.NoResultException; +import javax.persistence.Query; +import javax.transaction.Transactional; +import org.apache.commons.lang3.RandomStringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.apache.shiro.SecurityUtils; +import org.apache.shiro.subject.Subject; + +/** + * + * @author joern.muehlencord + */ +@Stateless +public class AccountControl implements Serializable { + + private static final Logger LOGGER = LoggerFactory.getLogger(AccountControl.class.getName()); + private static final long serialVersionUID = 3424816272598108101L; + + @EJB + private MailService mailService; + + @Inject + private ApplicationEntity application; + + @Inject + @AccountPU + EntityManager em; + + /** + * returns a list of active accounts + * + * @return a list of active accounts + */ + public List getActiveAccounts() { + Query query = em.createQuery("SELECT a FROM AccountEntity a WHERE a.status <> :status", AccountEntity.class); + query.setParameter("status", AccountStatus.DISABLED.name()); + return query.getResultList(); + } + + /** + * returns a list of active accounts + * + * @return a list of active accounts + */ + public List getAllAccounts() { + Query query = em.createNamedQuery("AccountEntity.findAll"); + return query.getResultList(); + } + + public List getAccounts(boolean includeDisabled) { + if (includeDisabled) { + return getAllAccounts(); + } else { + return getActiveAccounts(); + } + } + + public AccountEntity getAccountEntity(String userName, boolean loadRoles) { + StringBuilder queryBuilder = new StringBuilder(); + queryBuilder.append("SELECT a FROM AccountEntity a "); + if (loadRoles) { + queryBuilder.append("LEFT JOIN FETCH a.applicationRoleList "); + } + queryBuilder.append("WHERE a.username = :username"); + Query query = em.createQuery(queryBuilder.toString()); + query.setParameter("username", userName); + try { + return (AccountEntity) query.getSingleResult(); + } catch (NoResultException ex) { + return null; + } + } + + @Transactional + public AccountEntity saveAccount(AccountEntity account, ApplicationEntity app, List applicationRoles) { + Date now = DateUtil.getCurrentTimeInUTC(); + Subject currentUser = SecurityUtils.getSubject(); + String currentLoggedInUser = currentUser.getPrincipal().toString(); + + account.setLastUpdatedBy(currentLoggedInUser); + account.setLastUpdatedOn(now); + + boolean newAccount = (account.getCreatedOn() == null); + + // new account + if (newAccount) { + account.setCreatedOn(now); + account.setCreatedBy(currentLoggedInUser); + em.persist(account); + } else { + em.merge(account); + + // reload account from db and join roles + account = getAccountEntity(account.getUsername(), true); + } + + // assign roles to account + if (account.getApplicationRoleList() == null) { + account.setApplicationRoleList(new ArrayList<>()); + } + + boolean roleSetupChanged = false; + // remove roles which are no longer listed + // ensure this is only done for the given application - keep the other applications untouched + List assignedRoles = new ArrayList<>(); + assignedRoles.addAll(account.getApplicationRoleList()); + for (ApplicationRoleEntity currentlyAssignedRole : assignedRoles) { + if ((currentlyAssignedRole.getApplication().equals(app) && (!applicationRoles.contains(currentlyAssignedRole)))) { + account.getApplicationRoleList().remove(currentlyAssignedRole); + roleSetupChanged = true; + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("Removed role {} ({}) from user {}", currentlyAssignedRole.getRoleName(), application.getApplicationName(), account.getUsername()); + } + } + } + + // add newly added roles to role list + for (ApplicationRoleEntity applicationRole : applicationRoles) { + if (!account.getApplicationRoleList().contains(applicationRole)) { + account.addApplicationRole(applicationRole); + roleSetupChanged = true; + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("Added role {} ({}) to account {}", applicationRole.getRoleName(), application.getApplicationName(), account.getUsername()); + } + } + } + + // update account in database if roles changed + if (roleSetupChanged) { + em.merge(account); + } + return account; + } + + @Transactional + public void deleteAccount(AccountEntity account) throws AccountException { + Date now = new Date(); // Todo now in UTC + Subject currentUser = SecurityUtils.getSubject(); + String currentUserName = currentUser.getPrincipal().toString(); + + if (account.getUsername().equals(currentUserName)) { + throw new AccountException("Cannot delete own account"); + } else { + account.setStatus(AccountStatus.DISABLED.name()); + account.setLastUpdatedBy(currentUserName); + account.setLastUpdatedOn(now); + em.merge(account); + } + + } + + public boolean initPasswordReset(String userName) { + try { + AccountEntity account = getAccountEntity(userName, false); + if (account == null) { + LOGGER.warn("Account with name " + userName + " not found"); + return false; + } + + if (account.getStatus().equals(AccountStatus.BLOCKED.name())) { + LOGGER.warn("Account " + userName + " is locked, cannot initialize password reset"); + return false; + } + + String randomString = RandomStringUtils.random(40, true, true); + + Date validTo = new Date(); // TODO now in UTC + validTo = new Date(validTo.getTime() + 1000 * 600); // 10 minutes to react + + // TODO rework password reset +// account.setPasswordResetHash(randomString); +// account.setPasswordResetOngoing(true); +// account.setPasswordResetValidTo(validTo); + mailService.sendPasswortResetStartEmail(account, randomString); + + em.merge(account); + return true; + } catch (MailException ex) { + LOGGER.error("Error while sending password reset mail. " + ex.toString()); + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("Error while sending password reset mail.", ex); + } + return false; + } + } + + public boolean resetPassword(String userName, String newPassword, String resetPasswordToken) { + AccountEntity account = getAccountEntity(userName, false); + + if (account == null) { + LOGGER.warn("Error while resetting password, no account with username " + userName + " found"); + // TODO add extra logging for intrusion protection system like fail2ban + return false; + } + + /* + if (account.getPasswordResetOngoing() && (account.getPasswordResetHash() != null) && (account.getPasswordResetValidTo() != null)) { + Date now = new Date(); // TODO now in UTC + String storedHash = account.getPasswordResetHash().trim(); + if (account.getPasswordResetValidTo().after(now)) { + if (storedHash.equals(resetPasswordToken)) { + // everything ok, reset password + executePasswordReset(account, newPassword); + LOGGER.info("Updated password for user " + userName); + return true; + } else { + // token is not valid, refuse to change password + LOGGER.warn("Trying to reset password for user " + userName + " but wrong token " + resetPasswordToken + " provided"); + addLoginError(account); + return false; + } + } else { + // password reset token no longer valid + LOGGER.warn("Trying to reset password for user " + userName + " but token is no longer valid"); + addLoginError(account); + return false; + } + } else { + // user is not is password reset mode + LOGGER.warn("Trying to reset password for user " + userName + " but password reset was not requested"); + addLoginError(account); + return false; + } + */ + return false; // FIMXE re-implement password reset + } + + private void executePasswordReset(AccountEntity account, String newPassword) { + Date now = new Date(); // TODO now in UTC + + String hashedPassword = SecurityUtil.createPassword(newPassword); +// account.setAccountPassword(hashedPassword); +// +// account.setPasswordResetOngoing(false); +// account.setPasswordResetHash(null); +// account.setPasswordResetValidTo(null); + + account.setLastUpdatedBy(account.getUsername()); + account.setLastUpdatedOn(now); + em.merge(account); + + } + + public AccountLoginEntity updateSuccessFullLogin(AccountLoginEntity login, String byUser) { + Date now = DateUtil.getCurrentTimeInUTC(); + // a scucessful login ends a password reset procedure + if (login.getPasswordResetOngoing()) { + login.setPasswordResetOngoing(false); + login.setPasswordResetHash(null); + login.setPasswordResetValidTo(null); + login.setLastUpdatedOn(now); + login.setLastUpdatedBy(byUser); + } + + login.setLastLogin(now); + login.setFailureCount(0); + return updateLogin(login); + } + + public AccountLoginEntity updateLogin(AccountLoginEntity login) { + return em.merge(login); + } + + public void updateLogin(AccountEntity account) { + if (account.getAccountLogin() == null) { + // TODO connect to IPRS - how can an account ask for an updated login if the user cannot login + } else { + updateSuccessFullLogin(account.getAccountLogin(), account.getUsername()); + } + } + + public void addLoginError(AccountEntity account) { + // TODO reimplement +// try { +// Date now = new Date(); // TODO now in UTC +// account.setLastFailedLogin(now); +// account.setFailureCount(account.getFailureCount() + 1); +// +// int maxFailedLogins = Integer.parseInt(configService.getConfigValue( "account.maxFailedLogins")); +// if ((account.getFailureCount() >= maxFailedLogins) && (!account.getStatus().equals("LOCKED"))) { // TOD add status enum +// // max failed logins reached, disabling user +// LOGGER.info("Locking account " + account.getUsername() + " due to " + account.getFailureCount() + " failed logins"); +// account.setStatus(AccountStatus.BLOCKED.name()); +// } +// +// // on a failed login request, disable password reset +// account.setPasswordResetOngoing(false); +// account.setPasswordResetHash(null); +// account.setPasswordResetValidTo(null); +// +// account.setLastUpdatedBy("system"); +// account.setLastUpdatedOn(now); +// em.merge(account); +// } catch (ConfigException ex) { +// if (LOGGER.isDebugEnabled()) { +// LOGGER.debug(ex.toString(), ex); +// } else { +// LOGGER.error(ex.toString()); +// } +// } + } + + public AccountLoginEntity createLoginWithRandomPassword() { + AccountLoginEntity login = new AccountLoginEntity(); + String randomPassword = RandomStringUtils.random(20, true, true); + String hashedPassword = SecurityUtil.createPassword(randomPassword); + login.setAccountPassword(hashedPassword); + login.setLastLogin(null); + login.setLastFailedLogin(null); + login.setFailureCount(0); + + return login; + } + + public String getHashedPassword(String password) { + String hashedPassword = SecurityUtil.createPassword(password); + return hashedPassword; + } + + @Transactional + public void addLogin(AccountEntity accountToAdd, AccountLoginEntity accountLogin) { + Date now = DateUtil.getCurrentTimeInUTC(); + Subject currentUser = SecurityUtils.getSubject(); + String currentLoggedInUser = currentUser.getPrincipal().toString(); + + AccountEntity account = em.merge(accountToAdd); + accountLogin.setAccount(account); + accountLogin.setCreatedBy(currentLoggedInUser); + accountLogin.setCreatedOn(now); + accountLogin.setLastUpdatedBy(currentLoggedInUser); + accountLogin.setLastUpdatedOn(now); + em.persist(accountLogin); + + account.setAccountLogin(accountLogin); + em.merge(account); + + } + + @Transactional + public void deleteLogin(AccountEntity accountToDelete) { + AccountEntity account = em.merge(accountToDelete); + AccountLoginEntity login = account.getAccountLogin(); + login.setAccount(null); + account.setAccountLogin(null); + em.remove(login); + em.merge(account); + } + +}