Merge origin/master

This commit is contained in:
Joern Muehlencord
2019-05-28 17:41:40 +02:00
3 changed files with 104 additions and 94 deletions

View File

@ -21,6 +21,8 @@ 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.ApplicationEntity;
import de.muehlencord.shared.account.business.application.entity.ApplicationRoleEntity;
import de.muehlencord.shared.account.business.config.boundary.ConfigService;
import de.muehlencord.shared.account.business.config.entity.ConfigException;
import de.muehlencord.shared.account.business.instance.boundary.ApplicationPermissions;
import de.muehlencord.shared.account.business.mail.boundary.MailService;
import de.muehlencord.shared.account.business.mail.entity.MailException;
@ -65,10 +67,13 @@ public class AccountControl implements Serializable {
@Inject
private ApplicationEntity application;
@Inject
ConfigService configService;
@Inject
@AccountPU
EntityManager em;
public List<AccountEntity> getAllAccounts(boolean includeDisabled) {
List<AccountEntity> resultList;
if (includeDisabled) {
@ -76,25 +81,25 @@ public class AccountControl implements Serializable {
} else {
resultList = getActiveAccounts();
}
if (SecurityUtil.checkPermission(ApplicationPermissions.ACCOUNT_LIST)) {
return resultList;
} else {
String currentUserName = SecurityUtils.getSubject().getPrincipal().toString();
return resultList.stream()
.filter(account -> account.getAccountLogin() != null)
.filter (account -> account.getUsername().equals (currentUserName))
.filter(account -> account.getUsername().equals(currentUserName))
.collect(Collectors.toList());
}
}
}
/**
* returns a list of active accounts
*
* @return a list of active accounts
*/
private List<AccountEntity> getActiveAccounts() {
Query query = em.createNamedQuery ("AccountEntity.findActiveAccounts");
Query query = em.createNamedQuery("AccountEntity.findActiveAccounts");
query.setParameter("status", AccountStatus.DISABLED.name());
return query.getResultList();
}
@ -109,7 +114,6 @@ public class AccountControl implements Serializable {
return query.getResultList();
}
@Lock(LockType.READ)
@TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
public AccountEntity getAccountEntity(String userName, boolean loadRoles) {
@ -121,7 +125,6 @@ public class AccountControl implements Serializable {
queryBuilder.append("WHERE a.username = :username");
Query query = em.createQuery(queryBuilder.toString());
query.setParameter("username", userName);
query.setHint("org.hibernate.cacheable", true);
try {
return (AccountEntity) query.getSingleResult();
} catch (NoResultException ex) {
@ -207,6 +210,7 @@ public class AccountControl implements Serializable {
}
@Transactional
public boolean initPasswordReset(String userName) {
try {
AccountEntity account = getAccountEntity(userName, false);
@ -215,6 +219,11 @@ public class AccountControl implements Serializable {
return false;
}
if (account.getAccountLogin() == null) {
LOGGER.error("No login for account {} defined, cannot reset password", userName);
return false;
}
if (account.getStatus().equals(AccountStatus.BLOCKED.name())) {
LOGGER.warn("Account " + userName + " is locked, cannot initialize password reset");
return false;
@ -225,15 +234,15 @@ public class AccountControl implements Serializable {
Date validTo = DateUtil.getCurrentTimeInUTC();
validTo = new Date(validTo.getTime() + 1000 * 600); // 10 minutes to react
// TODO rework password reset
// account.setPasswordResetHash(randomString);
// account.setPasswordResetOngoing(true);
// account.setPasswordResetValidTo(validTo);
account.getAccountLogin().setPasswordResetHash(randomString);
account.getAccountLogin().setPasswordResetOngoing(true);
account.getAccountLogin().setPasswordResetValidTo(validTo);
mailService.sendPasswortResetStartEmail(account, randomString);
em.merge(account.getAccountLogin());
em.merge(account);
return true;
} catch (MailException ex) {
} catch (MailException | ConfigException ex) {
LOGGER.error("Error while sending password reset mail. " + ex.toString());
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Error while sending password reset mail.", ex);
@ -242,6 +251,7 @@ public class AccountControl implements Serializable {
}
}
@Transactional
public boolean resetPassword(String userName, String newPassword, String resetPasswordToken) {
AccountEntity account = getAccountEntity(userName, false);
@ -251,11 +261,15 @@ public class AccountControl implements Serializable {
return false;
}
/*
if (account.getPasswordResetOngoing() && (account.getPasswordResetHash() != null) && (account.getPasswordResetValidTo() != null)) {
if (account.getAccountLogin() == null) {
// user has no defined login, cannot reset password
return false;
}
if (account.getAccountLogin().getPasswordResetOngoing() && (account.getAccountLogin().getPasswordResetHash() != null) && (account.getAccountLogin().getPasswordResetValidTo() != null)) {
Date now = DateUtil.getCurrentTimeInUTC();
String storedHash = account.getPasswordResetHash().trim();
if (account.getPasswordResetValidTo().after(now)) {
String storedHash = account.getAccountLogin().getPasswordResetHash().trim();
if (account.getAccountLogin().getPasswordResetValidTo().after(now)) {
if (storedHash.equals(resetPasswordToken)) {
// everything ok, reset password
executePasswordReset(account, newPassword);
@ -279,26 +293,26 @@ public class AccountControl implements Serializable {
addLoginError(account);
return false;
}
*/
return false; // FIMXE re-implement password reset
}
private void executePasswordReset(AccountEntity account, String newPassword) {
Date now = DateUtil.getCurrentTimeInUTC();
String hashedPassword = SecurityUtil.createPassword(newPassword);
// account.setAccountPassword(hashedPassword);
//
// account.setPasswordResetOngoing(false);
// account.setPasswordResetHash(null);
// account.setPasswordResetValidTo(null);
if (account.getAccountLogin() == null) {
return;
}
account.getAccountLogin().setAccountPassword(hashedPassword);
account.getAccountLogin().setPasswordResetOngoing(false);
account.getAccountLogin().setPasswordResetHash(null);
account.getAccountLogin().setPasswordResetValidTo(null);
account.setLastUpdatedBy(account.getUsername());
account.setLastUpdatedOn(now);
em.merge(account);
}
@Transactional
public AccountLoginEntity updateSuccessFullLogin(AccountLoginEntity login, String byUser) {
Date now = DateUtil.getCurrentTimeInUTC();
// a scucessful login ends a password reset procedure
@ -315,10 +329,12 @@ public class AccountControl implements Serializable {
return updateLogin(login);
}
@Transactional
public AccountLoginEntity updateLogin(AccountLoginEntity login) {
return em.merge(login);
}
@Transactional
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
@ -327,35 +343,39 @@ public class AccountControl implements Serializable {
}
}
@Transactional
public void addLoginError(AccountEntity account) {
// TODO reimplement
// try {
// Date now = DateUtil.getCurrentTimeInUTC();
// 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());
// }
// }
if (account.getAccountLogin() == null) {
LOGGER.error("No login defined for {}", account.getUsername());
} else {
try {
Date now = DateUtil.getCurrentTimeInUTC();
account.getAccountLogin().setLastFailedLogin(now);
account.getAccountLogin().setFailureCount(account.getAccountLogin().getFailureCount() + 1);
int maxFailedLogins = Integer.parseInt(configService.getConfigValue("account.maxFailedLogins"));
if ((account.getAccountLogin().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.getAccountLogin().getFailureCount() + " failed logins");
account.setStatus(AccountStatus.BLOCKED.name());
}
// on a failed login request, disable password reset
account.getAccountLogin().setPasswordResetOngoing(false);
account.getAccountLogin().setPasswordResetHash(null);
account.getAccountLogin().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() {
@ -403,5 +423,5 @@ public class AccountControl implements Serializable {
em.remove(login);
em.merge(account);
}
}

View File

@ -23,7 +23,6 @@ import java.util.Date;
import java.util.List;
import java.util.UUID;
import javax.persistence.Basic;
import javax.persistence.Cacheable;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
@ -37,7 +36,6 @@ import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.OneToMany;
import javax.persistence.OneToOne;
import javax.persistence.QueryHint;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
@ -48,35 +46,21 @@ import org.hibernate.annotations.GenericGenerator;
import org.hibernate.annotations.Type;
/**
*
* IMPORANT: DO NOT CACHE - e.g. password changes are not synchronized
* @author joern.muehlencord
*/
@Entity
@Cacheable
@Table(name = "account")
@XmlRootElement
@NamedQueries({
@NamedQuery(name = "AccountEntity.findAll", query = "SELECT a FROM AccountEntity a ORDER by a.lastname, a.firstname",
hints = {
@QueryHint(name = "org.hibernate.cacheable", value = "true"),
@QueryHint(name = "org.hibernate.cacheRegion", value = "Queries")}),
@NamedQuery(name = "AccountEntity.findByUsername", query = "SELECT a FROM AccountEntity a WHERE a.username = :username",
hints = {
@QueryHint(name = "org.hibernate.cacheable", value = "true"),
@QueryHint(name = "org.hibernate.cacheRegion", value = "Queries")}),
@NamedQuery(name = "AccountEntity.findByStatus", query = "SELECT a FROM AccountEntity a WHERE a.status = :status",
hints = {
@QueryHint(name = "org.hibernate.cacheable", value = "true"),
@QueryHint(name = "org.hibernate.cacheRegion", value = "Queries")}),
@NamedQuery(name = "AccountEntity.findActiveAccounts", query = "SELECT a FROM AccountEntity a WHERE a.status <> :status",
hints = {
@QueryHint(name = "org.hibernate.cacheable", value = "true"),
@QueryHint(name = "org.hibernate.cacheRegion", value = "Queries")}),
@NamedQuery(name = "AccountEntity.findAll", query = "SELECT a FROM AccountEntity a ORDER by a.lastname, a.firstname"),
@NamedQuery(name = "AccountEntity.findByUsername", query = "SELECT a FROM AccountEntity a WHERE a.username = :username"),
@NamedQuery(name = "AccountEntity.findByStatus", query = "SELECT a FROM AccountEntity a WHERE a.status = :status"),
@NamedQuery(name = "AccountEntity.findActiveAccounts", query = "SELECT a FROM AccountEntity a WHERE a.status <> :status"),
@NamedQuery(name = "AccountEntity.findByCreatedOn", query = "SELECT a FROM AccountEntity a WHERE a.createdOn = :createdOn"),
@NamedQuery(name = "AccountEntity.findByCreatedBy", query = "SELECT a FROM AccountEntity a WHERE a.createdBy = :createdBy"),
@NamedQuery(name = "AccountEntity.findByLastUpdatedOn", query = "SELECT a FROM AccountEntity a WHERE a.lastUpdatedOn = :lastUpdatedOn"),
@NamedQuery(name = "AccountEntity.findByLastUpdatedBy", query = "SELECT a FROM AccountEntity a WHERE a.lastUpdatedBy = :lastUpdatedBy")
})
public class AccountEntity implements Serializable, Account {

View File

@ -15,16 +15,22 @@
*/
package de.muehlencord.shared.account.business.mail.boundary;
import de.muehlencord.shared.account.business.mail.entity.MailTemplateException;
import de.muehlencord.shared.account.business.account.entity.AccountConfigurationKey;
import de.muehlencord.shared.account.business.account.entity.AccountEntity;
import de.muehlencord.shared.account.business.account.entity.AccountLoginEntity;
import de.muehlencord.shared.account.business.config.boundary.ConfigService;
import de.muehlencord.shared.account.business.config.entity.ConfigException;
import de.muehlencord.shared.account.business.mail.entity.MailDatamodel;
import de.muehlencord.shared.account.business.mail.entity.MailException;
import de.muehlencord.shared.account.business.mail.entity.MailTemplateException;
import java.io.File;
import java.io.IOException;
import java.io.Serializable;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.UUID;
import javax.annotation.Resource;
import javax.ejb.EJB;
import javax.ejb.Stateless;
import javax.inject.Inject;
import javax.mail.Message;
@ -38,13 +44,6 @@ import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import de.muehlencord.shared.account.business.account.entity.AccountConfigurationValue;
import java.io.File;
import java.io.IOException;
import java.io.Serializable;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.List;
/**
*
@ -57,16 +56,19 @@ public class MailService implements Serializable {
private static final Logger LOGGER = LoggerFactory.getLogger(MailService.class);
@EJB
@Inject
private MailTemplateService mailTemplateService;
@Inject
@AccountConfigurationValue(key = AccountConfigurationKey.BaseUrl)
private String baseUrl;
ConfigService configService;
@Inject
@AccountConfigurationValue(key = AccountConfigurationKey.PasswordResetUrl)
private String passwordResetUrl;
// @Inject
// @AccountConfigurationValue(key = AccountConfigurationKey.BaseUrl)
// private String baseUrl;
// @Inject
// @AccountConfigurationValue(key = AccountConfigurationKey.PasswordResetUrl)
// private String passwordResetUrl;
// TODO make this configurable by injection from the application it uses it, fall back to defaul mail setup if not available
@Resource(lookup = "java:jboss/mail/ssgMail")
@ -189,7 +191,7 @@ public class MailService implements Serializable {
}
}
public String sendPasswortResetStartEmail(AccountEntity account, String token) throws MailException {
public String sendPasswortResetStartEmail(AccountEntity account, String token) throws MailException, ConfigException {
MailDatamodel model = new MailDatamodel(account);
/* old aproach via FacesContext - add this back as fallback if injection point if not configured
@ -209,10 +211,14 @@ public class MailService implements Serializable {
} catch (MalformedURLException ex) {
throw new MailException("Error while sending email.", ex);
}
String baseUrl = configService.getConfigValue(configKey);
String resetUrlWithToken = baseUrl + "/login.xhtml?token=" + token;
*/
String passwordResetUrl = configService.getConfigValue("backend.passwordreset.url");
String baseUrl = configService.getConfigValue("backend.base.url");
String resetUrlWithToken = passwordResetUrl + "?token=" + token;
model.addParameter("url", baseUrl);
model.addParameter("resetUrl", resetUrlWithToken);
return sendHTMLMail(account.getEmailaddress(), "Reset your password", model, "password_reset_html");