diff --git a/account/pom.xml b/account/pom.xml new file mode 100644 index 0000000..84f43f2 --- /dev/null +++ b/account/pom.xml @@ -0,0 +1,94 @@ + + + 4.0.0 + + de.muehlencord.shared + shared-account + 0.1-SNAPSHOT + ejb + + shared-account + + + ${project.build.directory}/endorsed + UTF-8 + + + + + org.apache.shiro + shiro-core + 1.2.4 + + + commons-lang + commons-lang + 2.6 + + + log4j + log4j + 1.2.17 + + + org.freemarker + freemarker + 2.3.23 + + + + javax + javaee-api + 7.0 + provided + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.1 + + 1.8 + 1.8 + + + + org.apache.maven.plugins + maven-ejb-plugin + 2.5.1 + + 3.1 + + + + org.apache.maven.plugins + maven-dependency-plugin + 2.10 + + + validate + + copy + + + ${endorsed.dir} + true + + + javax + javaee-endorsed-api + 7.0 + jar + + + + + + + + + + diff --git a/account/sql/01_config.sql b/account/sql/01_config.sql new file mode 100644 index 0000000..d0b6ce6 --- /dev/null +++ b/account/sql/01_config.sql @@ -0,0 +1,8 @@ +DROP TABLE config; + +CREATE TABLE config ( + config_key varchar(100), + config_value varchar(200), + CONSTRAINT config_pk PRIMARY KEY (config_key) +); +INSERT INTO config (config_key, config_value) VALUES ('account.maxFailedLogins', '5'); \ No newline at end of file diff --git a/account/sql/02_accounts.sql b/account/sql/02_accounts.sql new file mode 100644 index 0000000..67f2931 --- /dev/null +++ b/account/sql/02_accounts.sql @@ -0,0 +1,83 @@ +/** + * Author: joern.muehlencord + * Created: 06.09.2015 + */ + +DROP TABLE account_role; +DROP TABLE account_history; +DROP TABLE account; +DROP TABLE role_permission; +DROP TABLE application_role; +DROP TABLE application_permission; + + +CREATE TABLE application_role ( + role_name varchar(80) NOT NULL, + role_description varchar(200) NOT NULL, + CONSTRAINT pk_application_role_pk PRIMARY KEY (role_name) +); + +CREATE TABLE account ( + username varchar(32) NOT NULL, + emailaddress varchar(200) NOT NULL, + firstname varchar(100) NOT NULL, + lastname varchar(100) NOT NULL, + account_password char(200) NOT NULL, + last_login timestamp with time zone, + last_failed_login timestamp with time zone, + failure_count int NOT NULL DEFAULT 0, + status varchar(10) NOT NULL DEFAULT 'NEW', -- NEW, INIT, OK, BLOCKED, + password_reset_ongoing boolean NOT NULL DEFAULT false, + password_reset_valid_to timestamp with time zone, + password_reset_hash char(200), + created_on timestamp with time zone NOT NULL DEFAULT (now() at time zone 'utc'), + created_by varchar(32) NOT NULL, + last_updated_on timestamp with time zone NOT NULL DEFAULT (now() at time zone 'utc'), + last_updated_by varchar(32) NOT NULL, + CONSTRAINT pk_account PRIMARY KEY (username) +); + +CREATE TABLE account_history ( + id SERIAL NOT NULL, + username varchar(32) NOT NULL, + message varchar(200), + failure_count int NOT NULL DEFAULT 0, + status varchar(20) NOT NULL, -- constants needed, after action - new, init, active, blocked, inactive, marked for deletion + last_updated_on timestamp with time zone NOT NULL DEFAULT (now() at time zone 'utc'), + last_updated_by varchar(32) NOT NULL, + CONSTRAINT pk_account_history PRIMARY KEY (id), + CONSTRAINT fk_account_history_username_fk FOREIGN KEY (username) REFERENCES account (username) +); + +CREATE TABLE account_role ( + username varchar(32) NOT NULL, + role_name varchar(80) NOT NULL, + CONSTRAINT pk_account_role PRIMARY KEY (username, role_name), + CONSTRAINT fk_account_role_account FOREIGN KEY (username) REFERENCES account(username), + CONSTRAINT fk_account_role_role_name FOREIGN KEY (role_name) REFERENCES application_role(role_name) +); + + +CREATE TABLE application_permission ( + permission_name varchar(80) NOT NULL, + permission_description varchar(200) NOT NULL, + CONSTRAINT application_permission_pk PRIMARY KEY (permission_name) +); + +CREATE TABLE role_permission ( + role_name varchar(80) NOT NULL, + permission_name varchar(80) NOT NULL, + CONSTRAINT pk_role_permission_role_permission_name PRIMARY KEY (role_name, permission_name), + CONSTRAINT fk_role_permission_role_name FOREIGN KEY (role_name) REFERENCES application_role(role_name), + CONSTRAINT fk_role_permission_permission_name FOREIGN KEY (permission_name) REFERENCES application_permission(permission_name) +); + +INSERT INTO application_permission (permission_name, permission_description) values ('test:view', 'Display test view'); + +INSERT INTO application_role (role_name, role_description) values ('Admin', 'Admin role'); +INSERT INTO application_role (role_name, role_description) values ('User', 'Standard user role'); + +-- INSERT INTO role_permission (role_name, permission_name) values ('Admin','test:view'); + +INSERT INTO account (username, emailaddress, firstname, lastname, account_password, created_by, last_updated_by) values('admin', 'joern@muehlencord.de', 'Joern', 'Muehlencord','$shiro1$SHA-256$500000$4bHPNH9k539UjdFLgm/HOA==$T/n8skgoGSOtNw/c9ScDlXCiGrx2cZF0Esrvf6WPq6g=', 'admin','admin'); --admin/secret +INSERT INTO account_role (username, role_name) values ('admin', 'Admin'); \ No newline at end of file diff --git a/account/sql/03_templates.sql b/account/sql/03_templates.sql new file mode 100644 index 0000000..140e6c0 --- /dev/null +++ b/account/sql/03_templates.sql @@ -0,0 +1,21 @@ +DROP TABLE mail_template; + +CREATE TABLE mail_template ( + template_name varchar(40) NOT NULL, + template_value text NOT NULL, + CONSTRAINT mail_template_pk PRIMARY KEY (template_name) +); + + +INSERT INTO mail_template (template_name, template_value) VALUES('password_reset_html', +'<#ftl strip_whitespace = true> + + + + Dear ${account.firstname},
+
+ you requested to reset your password at ${parameter.url}. Please open the following URL to proceed.
+ ${parameter.resetUrl}
+
+ + '); diff --git a/account/sql/create_tables.sql b/account/sql/create_tables.sql new file mode 100644 index 0000000..0b75621 --- /dev/null +++ b/account/sql/create_tables.sql @@ -0,0 +1,2 @@ +\i 01_accounts.sql +\i 02_templates.sql \ No newline at end of file diff --git a/account/src/main/java/de/muehlencord/shared/account/business/ConfigService.java b/account/src/main/java/de/muehlencord/shared/account/business/ConfigService.java new file mode 100644 index 0000000..bbe5f72 --- /dev/null +++ b/account/src/main/java/de/muehlencord/shared/account/business/ConfigService.java @@ -0,0 +1,45 @@ +package de.muehlencord.shared.account.business; + +import de.muehlencord.shared.account.entity.ConfigEntity; +import javax.annotation.PostConstruct; +import javax.ejb.Singleton; +import javax.ejb.Startup; +import javax.persistence.EntityManager; +import javax.persistence.PersistenceContext; + +/** + * + * @author joern.muehlencord + */ +@Singleton +@Startup +public class ConfigService { + + @PersistenceContext + EntityManager em; + + private String storagePath = null; + private int maxFailedLogins = 5; + + @PostConstruct + public void init() { + ConfigEntity configEntity = em.find(ConfigEntity.class, "storage.path"); + if (configEntity != null) { + this.storagePath = configEntity.getConfigValue(); + } + configEntity = em.find(ConfigEntity.class, "account.maxFailedLogins"); + if (configEntity != null) { + this.maxFailedLogins = Integer.parseInt(configEntity.getConfigValue()); + } + } + + /* *** getter *** */ + public String getStoragePath() { + return storagePath; + } + + public int getMaxFailedLogins() { + return maxFailedLogins; + } + +} diff --git a/account/src/main/java/de/muehlencord/shared/account/business/account/AccountControl.java b/account/src/main/java/de/muehlencord/shared/account/business/account/AccountControl.java new file mode 100644 index 0000000..6640d6c --- /dev/null +++ b/account/src/main/java/de/muehlencord/shared/account/business/account/AccountControl.java @@ -0,0 +1,278 @@ +package de.muehlencord.shared.account.business.account; + +import de.muehlencord.shared.account.business.ConfigService; +import de.muehlencord.shared.account.business.mail.MailService; +import de.muehlencord.shared.account.business.mail.MailTemplateException; +import de.muehlencord.shared.account.entity.AccountEntity; +import de.muehlencord.shared.account.entity.RoleEntity; +import de.muehlencord.shared.account.util.SecurityUtil; +import freemarker.template.TemplateException; +import java.io.IOException; +import java.net.URISyntaxException; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import javax.ejb.EJB; +import javax.ejb.Stateless; +import javax.mail.MessagingException; +import javax.persistence.EntityManager; +import javax.persistence.NoResultException; +import javax.persistence.PersistenceContext; +import javax.persistence.Query; +import javax.transaction.Transactional; +import org.apache.commons.lang.RandomStringUtils; +import org.apache.log4j.Level; +import org.apache.log4j.Logger; +import org.apache.shiro.SecurityUtils; +import org.apache.shiro.subject.Subject; + +/** + * + * @author joern.muehlencord + */ +@Stateless +public class AccountControl { + + private static final Logger LOGGER = Logger.getLogger(AccountControl.class.getName()); + + @EJB + private ConfigService configService; + + @EJB + private MailService mailService; + + @PersistenceContext + EntityManager em; + + public List getAccounts() { + Query query = em.createQuery("SELECT a FROM AccountEntity a WHERE a.status <> :status", AccountEntity.class); + query.setParameter("status", "DELETED"); // TODO add status enum + return query.getResultList(); + } + + public AccountEntity getAccountEntity(String userName, boolean loadRoles) { + StringBuilder queryBuilder = new StringBuilder(); + queryBuilder.append("SELECT a FROM AccountEntity a "); + if (loadRoles) { + queryBuilder.append("JOIN FETCH a.roleEntityList "); + } + 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 + // TODO add role names from application because only application can now how its roles are named + public AccountEntity saveAccount(AccountEntity account, boolean isAdmin) { + Date now = new Date(); // Todo now in UTC + 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); + + // set default random password, user has to get password via lost passwort option afterwards + String randomPassword = RandomStringUtils.random(20, true, true); + String hashedPassword = SecurityUtil.createPassword(randomPassword); + account.setAccountPassword(hashedPassword); + em.persist(account); + } else { + em.merge(account); + + // reload account from db and join roles + account = getAccountEntity(account.getUsername(), true); + } + + // load Admin or User role from database + String roleName = (isAdmin ? "Admin" : "User"); + Query roleQuery = em.createNamedQuery("RoleEntity.findByRoleName"); + roleQuery.setParameter("roleName", roleName); + RoleEntity role = (RoleEntity) roleQuery.getSingleResult(); + + if (role != null) { + // add new user add required role + // do not request based on newUser variable; this way existing users with missing role (for whatever reason) + // will be fixed automatically + if (account.getRoleEntityList() == null || account.getRoleEntityList().isEmpty()) { + account.setRoleEntityList(new ArrayList<>()); + account.getRoleEntityList().add(role); + em.merge(account); + LOGGER.log(Level.INFO, "Added role " + roleName + " to user " + account.getUsername()); + + } else if (!account.getRoleEntityList().get(0).equals(role)) { + // change role from User to Admin and vice versa + // user already exists, has existing role + // check if existing role is different from current role and change it + // be carefull: this only works as long as a user has exactly one role! + // he is either User or Admin + // TODO add "UserRole" to every user, make this default Role configurable + // TODO add AdminRole in addtion if needed + account.getRoleEntityList().remove(0); + account.getRoleEntityList().add(role); + em.merge(account); + LOGGER.log(Level.INFO, "Switched role of user " + account.getUsername() + " to " + roleName); + + } + } + + return account; + } + + 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("DELETED"); // TODO add enum + account.setLastUpdatedBy(currentUserName); + account.setLastUpdatedOn(now); + em.merge(account); + } + + } + + public boolean initPasswordReset(String userName) { + try { + AccountEntity account = getAccountEntity(userName, false); + if (account == null) { + LOGGER.log(Level.WARN, "Account with name " + userName + " not found"); + return false; + } + + if (account.getStatus().equals("LOCKED")) { // TODO add enumType + LOGGER.log(Level.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 + + account.setPasswordResetHash(randomString); + account.setPasswordResetOngoing(true); + account.setPasswordResetValidTo(validTo); + + mailService.sendPasswortResetStartEmail(account, randomString); + + em.merge(account); + return true; + } catch (MessagingException | MailTemplateException | URISyntaxException | IOException | TemplateException ex) { + LOGGER.log(Level.ERROR, "Error while sending password reset mail. " + ex.toString()); + if (LOGGER.isDebugEnabled()) { + LOGGER.log(Level.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.log(Level.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.log(Level.INFO, "Updated password for user " + userName); + return true; + } else { + // token is not valid, refuse to change password + LOGGER.log(Level.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.log(Level.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.log(Level.WARN, "Trying to reset password for user " + userName + " but password reset was not requested"); + addLoginError(account); + return false; + } + } + + 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 void updateLogin(AccountEntity account) { + Date now = new Date(); // TODO now in UTC + // a scucessful login ends a password reset procedure + if (account.getPasswordResetOngoing()) { + account.setPasswordResetOngoing(false); + account.setPasswordResetHash(null); + account.setPasswordResetValidTo(null); + account.setLastUpdatedOn(now); + account.setLastUpdatedBy(account.getUsername()); + } + + account.setLastLogin(now); + account.setFailureCount(0); + account.setStatus("OK"); // TODO add statusEnum + + em.merge(account); + } + + public void addLoginError(AccountEntity account) { + Date now = new Date(); // TODO now in UTC + account.setLastFailedLogin(now); + account.setFailureCount(account.getFailureCount() + 1); + + int maxFailedLogins = configService.getMaxFailedLogins(); + if ((account.getFailureCount() >= maxFailedLogins) && (!account.getStatus().equals("LOCKED"))) { // TOD add status enum + // max failed logins reached, disabling user + LOGGER.log(Level.INFO, "Locking account " + account.getUsername() + " due to " + account.getFailureCount() + " failed logins"); + account.setStatus("LOCKED"); // TODO add enum + } + + // 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); + } + +} diff --git a/account/src/main/java/de/muehlencord/shared/account/business/account/AccountException.java b/account/src/main/java/de/muehlencord/shared/account/business/account/AccountException.java new file mode 100644 index 0000000..04cc492 --- /dev/null +++ b/account/src/main/java/de/muehlencord/shared/account/business/account/AccountException.java @@ -0,0 +1,25 @@ +package de.muehlencord.shared.account.business.account; + +/** + * + * @author Raimund + */ +public class AccountException extends Exception { + + /** + * Creates a new instance of AccountException without detail + * message. + */ + public AccountException() { + } + + /** + * Constructs an instance of AccountException with the + * specified detail message. + * + * @param msg the detail message. + */ + public AccountException(String msg) { + super(msg); + } +} diff --git a/account/src/main/java/de/muehlencord/shared/account/business/mail/MailDatamodel.java b/account/src/main/java/de/muehlencord/shared/account/business/mail/MailDatamodel.java new file mode 100644 index 0000000..77042c3 --- /dev/null +++ b/account/src/main/java/de/muehlencord/shared/account/business/mail/MailDatamodel.java @@ -0,0 +1,35 @@ +package de.muehlencord.shared.account.business.mail; + +import de.muehlencord.shared.account.entity.AccountEntity; +import java.util.HashMap; +import java.util.Map; + +/** + * + * @author jomu + */ +public class MailDatamodel { + + private final AccountEntity account; + private final Map parameter; + + public MailDatamodel(AccountEntity account) { + this.parameter = new HashMap<>(); + this.account = account; + } + + public void addParameter(String name, String value) { + this.parameter.put(name, value); + } + + + /* **** getter / setter **** */ + + public AccountEntity getAccount() { + return account; + } + + public Map getParameter() { + return parameter; + } +} diff --git a/account/src/main/java/de/muehlencord/shared/account/business/mail/MailException.java b/account/src/main/java/de/muehlencord/shared/account/business/mail/MailException.java new file mode 100644 index 0000000..6471740 --- /dev/null +++ b/account/src/main/java/de/muehlencord/shared/account/business/mail/MailException.java @@ -0,0 +1,36 @@ +package de.muehlencord.shared.account.business.mail; + +/** + * + * @author Raimund + */ +public class MailException extends Exception { + + /** + * Creates a new instance of MailException without detail + * message. + */ + public MailException() { + } + + /** + * Constructs an instance of MailException with the specified + * detail message. + * + * @param msg the detail message. + */ + public MailException(String msg) { + super(msg); + } + + /** + * Constructs an instance of MailException with the specified + * detail message. + * + * @param msg the detail message. + * @param th the root cause + */ + public MailException(String msg, Throwable th) { + super(msg, th); + } +} diff --git a/account/src/main/java/de/muehlencord/shared/account/business/mail/MailService.java b/account/src/main/java/de/muehlencord/shared/account/business/mail/MailService.java new file mode 100644 index 0000000..88283a1 --- /dev/null +++ b/account/src/main/java/de/muehlencord/shared/account/business/mail/MailService.java @@ -0,0 +1,104 @@ +package de.muehlencord.shared.account.business.mail; + +import de.muehlencord.shared.account.entity.AccountEntity; +import freemarker.template.TemplateException; +import java.io.IOException; +import java.net.URISyntaxException; +import java.net.URL; +import java.util.Date; +import javax.annotation.Resource; +import javax.ejb.EJB; +import javax.ejb.Stateless; +import javax.faces.application.FacesMessage; +import javax.faces.context.ExternalContext; +import javax.faces.context.FacesContext; +import javax.mail.BodyPart; +import javax.mail.Message; +import javax.mail.MessagingException; +import javax.mail.Multipart; +import javax.mail.Session; +import javax.mail.Transport; +import javax.mail.internet.InternetAddress; +import javax.mail.internet.MimeBodyPart; +import javax.mail.internet.MimeMessage; +import javax.mail.internet.MimeMultipart; + +/** + * + * @author joern.muehlencord + */ +@Stateless +public class MailService { + + @EJB + private MailTemplateService mailTemplateService; + + @Resource(lookup = "java:jboss/mail/ssgMail") + private Session mailSession; + + public void sendTestEmail(String recipient) throws MailException { + sendMail(recipient, "Test email", "This is a test email"); + } + + public void sendTestHtmlEmail(String recipient) { + Date now = new Date(); + AccountEntity account = new AccountEntity("joern.muehlencord", "joern@muehlencord.de", "Jörn", "Mühlencord", "secret", 0, "NEW", now, "admin", now, "admin"); + MailDatamodel dataModel = new MailDatamodel(account); + dataModel.addParameter("url", "http://url.de"); + dataModel.addParameter("resetUrl", "http://reseturl.de"); + sendHTMLMail(recipient, "Test HTML Email", dataModel, "password_reset"); + } + + public void sendMail(String recipient, String subject, String body) throws MailException { + try { + MimeMessage message = new MimeMessage(mailSession); + message.setSubject(subject); + message.setRecipients(Message.RecipientType.TO, InternetAddress.parse(recipient, false)); + message.setText(body); + Transport.send(message); + } catch (MessagingException ex) { + throw new MailException ("Error while sending email.", ex); + } + } + + public void sendHTMLMail(String recipient, String subject, MailDatamodel dataModel, String templateName) { + try { + Message message = new MimeMessage(mailSession); + message.setRecipients(Message.RecipientType.TO, InternetAddress.parse(recipient, false)); + message.setSubject(subject); + + String body = mailTemplateService.getStringFromTemplate("password_reset_html", dataModel); + + BodyPart bodyPart = new MimeBodyPart(); + bodyPart.setContent(body, "text/html"); + + Multipart multipart = new MimeMultipart(); + multipart.addBodyPart(bodyPart); + message.setContent(multipart, "text/html; charset=UTF-8"); + + Transport.send(message); + } catch (MessagingException | MailTemplateException ex) { + FacesMessage message = new FacesMessage(FacesMessage.SEVERITY_ERROR, "Error while sending email", "Exception: " + ex.toString()); + FacesContext context = FacesContext.getCurrentInstance(); + context.addMessage(null, message); + } + + } + + public void sendPasswortResetStartEmail(AccountEntity account, String token) throws MessagingException, URISyntaxException, IOException, TemplateException, MailTemplateException { + MailDatamodel model = new MailDatamodel(account); + + // String absoluteWebPath = FacesContext.getCurrentInstance().getExternalContext().getApplicationContextPath(); + ExternalContext externalContext = FacesContext.getCurrentInstance().getExternalContext(); + String resetPage = "/login.xhtml?token=" + token; + URL baseUrl = new URL(externalContext.getRequestScheme(), + externalContext.getRequestServerName(), + externalContext.getRequestServerPort(), + externalContext.getRequestContextPath()); + + model.addParameter("url", baseUrl.toString()); + model.addParameter("resetUrl", baseUrl.toString() + resetPage); + + sendHTMLMail(account.getEmailaddress(), "Reset your password", model, "password_reset"); + } +} diff --git a/account/src/main/java/de/muehlencord/shared/account/business/mail/MailServiceLocator.java b/account/src/main/java/de/muehlencord/shared/account/business/mail/MailServiceLocator.java new file mode 100644 index 0000000..b620ddf --- /dev/null +++ b/account/src/main/java/de/muehlencord/shared/account/business/mail/MailServiceLocator.java @@ -0,0 +1,138 @@ +package de.muehlencord.shared.account.business.mail; + +import java.net.URL; + +import javax.ejb.EJBHome; +import javax.ejb.EJBLocalHome; +import javax.jms.ConnectionFactory; +import javax.jms.Destination; +import javax.naming.InitialContext; +import javax.naming.NamingException; +import javax.rmi.PortableRemoteObject; +import javax.sql.DataSource; +import javax.mail.Session; + +/** + * + * @author Raimund + */ +public class MailServiceLocator { + + private InitialContext ic; + + public MailServiceLocator() { + try { + ic = new InitialContext(); + } catch (NamingException ne) { + throw new RuntimeException(ne); + } + } + + private Object lookup(String jndiName) throws NamingException { + return ic.lookup(jndiName); + } + + /** + * Will get the ejb Local home factory. Clients need to cast to the type of + * EJBHome they desire. + * + * @param jndiHomeName jndi home name matching the requested local home + * @return the Local EJB Home corresponding to the homeName + * @throws NamingException if the lookup fails + */ + public EJBLocalHome getLocalHome(String jndiHomeName) throws NamingException { + return (EJBLocalHome) lookup(jndiHomeName); + } + + /** + * Will get the ejb Remote home factory. Clients need to cast to the type of + * EJBHome they desire. + * + * @param jndiHomeName jndi home name matching the requested remote home + * @param className desired type of the object + * @return the EJB Home corresponding to the homeName + * @throws NamingException if the lookup fails + */ + public EJBHome getRemoteHome(String jndiHomeName, Class className) throws NamingException { + Object objref = lookup(jndiHomeName); + return (EJBHome) PortableRemoteObject.narrow(objref, className); + } + + /** + * This method helps in obtaining the JMS connection factory. + * + * @param connFactoryName name of the connection factory + * @return the factory for obtaining JMS connection + * @throws NamingException if the lookup fails + */ + public ConnectionFactory getConnectionFactory(String connFactoryName) throws NamingException { + return (ConnectionFactory) lookup(connFactoryName); + } + + /** + * This method obtains the topic itself for a caller. + * + * @param destName destination name + * @return the Topic Destination to send messages to + * @throws NamingException if the lookup fails + */ + public Destination getDestination(String destName) throws NamingException { + return (Destination) lookup(destName); + } + + /** + * This method obtains the datasource itself for a caller. + * + * @param dataSourceName data source name + * @return the DataSource corresponding to the name parameter + * @throws NamingException if the lookup fails + */ + public DataSource getDataSource(String dataSourceName) throws NamingException { + return (DataSource) lookup(dataSourceName); + } + + /** + * This method obtains the E-mail session itself for a caller. + * + * @param sessionName session name + * @return the Session corresponding to the name parameter + * @throws NamingException if the lookup fails + */ + public Session getSession(String sessionName) throws NamingException { + return (Session) lookup(sessionName); + } + + /** + * Gets the URL corresponding to the environment entry name. + * + * @param envName the environment name + * @return the URL value corresponding to the environment entry name + * @throws NamingException if the lookup fails + */ + public URL getUrl(String envName) throws NamingException { + return (URL) lookup(envName); + } + + /** + * Gets boolean value corresponding to the environment entry name. + * + * @param envName the environment name + * @return the boolean value corresponding to the environment entry + * @throws NamingException if the lookup fails + */ + public boolean getBoolean(String envName) throws NamingException { + Boolean bool = (Boolean) lookup(envName); + return bool.booleanValue(); + } + + /** + * Gets string value corresponding to the environment entry name. + * + * @param envName the environment name + * @return the String value corresponding to the environment entry name + * @throws NamingException if the lookup fails + */ + public String getString(String envName) throws NamingException { + return (String) lookup(envName); + } +} diff --git a/account/src/main/java/de/muehlencord/shared/account/business/mail/MailTemplateException.java b/account/src/main/java/de/muehlencord/shared/account/business/mail/MailTemplateException.java new file mode 100644 index 0000000..3075952 --- /dev/null +++ b/account/src/main/java/de/muehlencord/shared/account/business/mail/MailTemplateException.java @@ -0,0 +1,37 @@ +package de.muehlencord.shared.account.business.mail; + +/** + * + * @author jomu + */ +public class MailTemplateException extends Exception { + + /** + * Creates a new instance of MailTemplateException without + * detail message. + */ + public MailTemplateException() { + } + + /** + * Constructs an instance of MailTemplateException with the + * specified detail message. + * + * @param msg the detail message. + */ + public MailTemplateException(String msg) { + super(msg); + } + + /** + * Constructs an instance of MailTemplateException with the + * specified detail message. + * + * @param msg the detail message. + * @param th the root cause + */ + public MailTemplateException(String msg, Throwable th) { + super(msg, th); + } + +} diff --git a/account/src/main/java/de/muehlencord/shared/account/business/mail/MailTemplateService.java b/account/src/main/java/de/muehlencord/shared/account/business/mail/MailTemplateService.java new file mode 100644 index 0000000..e8a17f5 --- /dev/null +++ b/account/src/main/java/de/muehlencord/shared/account/business/mail/MailTemplateService.java @@ -0,0 +1,66 @@ +package de.muehlencord.shared.account.business.mail; + +import de.muehlencord.shared.account.entity.MailTemplateEntity; +import freemarker.cache.StringTemplateLoader; +import freemarker.template.Configuration; +import freemarker.template.Template; +import freemarker.template.TemplateException; +import freemarker.template.TemplateExceptionHandler; +import java.io.IOException; +import java.io.StringWriter; +import java.io.Writer; +import javax.ejb.Stateless; +import javax.persistence.EntityManager; +import javax.persistence.PersistenceContext; +import javax.persistence.Query; +import org.apache.log4j.Level; +import org.apache.log4j.Logger; + +/** + * + * @author jomu + */ +@Stateless +public class MailTemplateService { + + private static final Logger LOGGER = Logger.getLogger(MailTemplateService.class.getName()); + + @PersistenceContext + EntityManager em; + + public String getStringFromTemplate(String templateName, MailDatamodel dataModel) throws MailTemplateException { + try { + Query query = em.createNamedQuery("MailTemplateEntity.findByTemplateName"); + query.setParameter("templateName", templateName); + MailTemplateEntity templateEntity = (MailTemplateEntity) query.getSingleResult(); + if (templateEntity == null) { + LOGGER.log(Level.ERROR, "Tempate with name " + templateName + " not found"); + return null; + } + + Configuration configuration = new Configuration(Configuration.VERSION_2_3_23); + configuration.setDefaultEncoding("UTF-8"); + configuration.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER); + + StringTemplateLoader stringLoader = new StringTemplateLoader(); + stringLoader.putTemplate(templateEntity.getTemplateName(), templateEntity.getTemplateValue()); + configuration.setTemplateLoader(stringLoader); + + Template template = configuration.getTemplate(templateEntity.getTemplateName()); + + Writer out = new StringWriter(); + template.process(dataModel, out); + String templateString = out.toString(); + return templateString; + } catch (Exception ex) { + String hint = "Error while processing template with name " + templateName + "."; + LOGGER.log(Level.ERROR, hint + " " + ex.toString()); + if (LOGGER.isDebugEnabled()) { + LOGGER.log(Level.DEBUG, hint, ex); + } + throw new MailTemplateException(hint, ex); + + } + + } +} diff --git a/account/src/main/java/de/muehlencord/shared/account/entity/AccountEntity.java b/account/src/main/java/de/muehlencord/shared/account/entity/AccountEntity.java new file mode 100644 index 0000000..0eaa37b --- /dev/null +++ b/account/src/main/java/de/muehlencord/shared/account/entity/AccountEntity.java @@ -0,0 +1,322 @@ +package de.muehlencord.shared.account.entity; + +import java.io.Serializable; +import java.util.Date; +import java.util.List; +import javax.persistence.Basic; +import javax.persistence.CascadeType; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.JoinTable; +import javax.persistence.ManyToMany; +import javax.persistence.NamedQueries; +import javax.persistence.NamedQuery; +import javax.persistence.OneToMany; +import javax.persistence.Table; +import javax.persistence.Temporal; +import javax.persistence.TemporalType; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlTransient; + +/** + * + * @author joern.muehlencord + */ +@Entity +@Table(name = "account") +@XmlRootElement +@NamedQueries({ + @NamedQuery(name = "AccountEntity.findAll", query = "SELECT a FROM AccountEntity a"), + @NamedQuery(name = "AccountEntity.findByUsername", query = "SELECT a FROM AccountEntity a WHERE a.username = :username"), + @NamedQuery(name = "AccountEntity.findByEmailaddress", query = "SELECT a FROM AccountEntity a WHERE a.emailaddress = :emailaddress"), + @NamedQuery(name = "AccountEntity.findByFirstname", query = "SELECT a FROM AccountEntity a WHERE a.firstname = :firstname"), + @NamedQuery(name = "AccountEntity.findByLastname", query = "SELECT a FROM AccountEntity a WHERE a.lastname = :lastname"), + @NamedQuery(name = "AccountEntity.findByAccountPassword", query = "SELECT a FROM AccountEntity a WHERE a.accountPassword = :accountPassword"), + @NamedQuery(name = "AccountEntity.findByLastLogin", query = "SELECT a FROM AccountEntity a WHERE a.lastLogin = :lastLogin"), + @NamedQuery(name = "AccountEntity.findByLastFailedLogin", query = "SELECT a FROM AccountEntity a WHERE a.lastFailedLogin = :lastFailedLogin"), + @NamedQuery(name = "AccountEntity.findByFailureCount", query = "SELECT a FROM AccountEntity a WHERE a.failureCount = :failureCount"), + @NamedQuery(name = "AccountEntity.findByStatus", 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 { + + private static final long serialVersionUID = 1L; + @Id + @Basic(optional = false) + @NotNull + @Size(min = 1, max = 32) + @Column(name = "username") + private String username; + @Basic(optional = false) + @NotNull + @Size(min = 1, max = 200) + @Column(name = "emailaddress") + private String emailaddress; + @Basic(optional = false) + @NotNull + @Size(min = 1, max = 100) + @Column(name = "firstname") + private String firstname; + @Basic(optional = false) + @NotNull + @Size(min = 1, max = 100) + @Column(name = "lastname") + private String lastname; + @Basic(optional = false) + @NotNull + @Size(min = 1, max = 200) + @Column(name = "account_password", columnDefinition = "bpchar(200)") + private String accountPassword; + @Column(name = "last_login") + @Temporal(TemporalType.TIMESTAMP) + private Date lastLogin; + @Column(name = "last_failed_login") + @Temporal(TemporalType.TIMESTAMP) + private Date lastFailedLogin; + @Basic(optional = false) + @NotNull + @Column(name = "failure_count") + private int failureCount; + @Basic(optional = false) + @NotNull + @Size(min = 1, max = 10) + @Column(name = "status") + private String status; + @Basic(optional = false) + @NotNull + @Column(name = "password_reset_ongoing") + private boolean passwordResetOngoing; + @Column(name = "password_reset_valid_to") + @Temporal(TemporalType.TIMESTAMP) + private Date passwordResetValidTo; + @Size(max = 200) + @Column(name = "password_reset_hash", columnDefinition = "bpchar(200)") + private String passwordResetHash; + @Basic(optional = false) + @NotNull + @Column(name = "created_on") + @Temporal(TemporalType.TIMESTAMP) + private Date createdOn; + @Basic(optional = false) + @NotNull + @Size(min = 1, max = 32) + @Column(name = "created_by") + private String createdBy; + @Basic(optional = false) + @NotNull + @Column(name = "last_updated_on") + @Temporal(TemporalType.TIMESTAMP) + private Date lastUpdatedOn; + @Basic(optional = false) + @NotNull + @Size(min = 1, max = 32) + @Column(name = "last_updated_by") + private String lastUpdatedBy; + @JoinTable(name = "account_role", joinColumns = { + @JoinColumn(name = "username", referencedColumnName = "username")}, inverseJoinColumns = { + @JoinColumn(name = "role_name", referencedColumnName = "role_name")}) + @ManyToMany + private List roleEntityList; + @OneToMany(cascade = CascadeType.ALL, mappedBy = "username") + private List accountHistoryEntityList; + + public AccountEntity() { + // empty constructor needed for JPA handling, do not remove + } + + public AccountEntity(String username) { + this.username = username; + } + + public AccountEntity(String username, String emailaddress, String firstname, String lastname, String accountPassword, int failureCount, String status, Date createdOn, String createdBy, Date lastUpdatedOn, String lastUpdatedBy) { + this.username = username; + this.emailaddress = emailaddress; + this.firstname = firstname; + this.lastname = lastname; + this.accountPassword = accountPassword; + this.failureCount = failureCount; + this.status = status; + this.createdOn = createdOn; + this.createdBy = createdBy; + this.lastUpdatedOn = lastUpdatedOn; + this.lastUpdatedBy = lastUpdatedBy; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getEmailaddress() { + return emailaddress; + } + + public void setEmailaddress(String emailaddress) { + this.emailaddress = emailaddress; + } + + public String getFirstname() { + return firstname; + } + + public void setFirstname(String firstname) { + this.firstname = firstname; + } + + public String getLastname() { + return lastname; + } + + public void setLastname(String lastname) { + this.lastname = lastname; + } + + public String getAccountPassword() { + return accountPassword; + } + + public void setAccountPassword(String accountPassword) { + this.accountPassword = accountPassword; + } + + public Date getLastLogin() { + return lastLogin; + } + + public void setLastLogin(Date lastLogin) { + this.lastLogin = lastLogin; + } + + public Date getLastFailedLogin() { + return lastFailedLogin; + } + + public void setLastFailedLogin(Date lastFailedLogin) { + this.lastFailedLogin = lastFailedLogin; + } + + public int getFailureCount() { + return failureCount; + } + + public void setFailureCount(int failureCount) { + this.failureCount = failureCount; + } + + public String getStatus() { + return status; + } + + public void setStatus(String status) { + this.status = status; + } + + public boolean getPasswordResetOngoing() { + return passwordResetOngoing; + } + + public void setPasswordResetOngoing(boolean passwordResetOngoing) { + this.passwordResetOngoing = passwordResetOngoing; + } + + public Date getPasswordResetValidTo() { + return passwordResetValidTo; + } + + public void setPasswordResetValidTo(Date passwordResetValidTo) { + this.passwordResetValidTo = passwordResetValidTo; + } + + public String getPasswordResetHash() { + return passwordResetHash; + } + + public void setPasswordResetHash(String passwordResetHash) { + this.passwordResetHash = passwordResetHash; + } + + public Date getCreatedOn() { + return createdOn; + } + + public void setCreatedOn(Date createdOn) { + this.createdOn = createdOn; + } + + public String getCreatedBy() { + return createdBy; + } + + public void setCreatedBy(String createdBy) { + this.createdBy = createdBy; + } + + public Date getLastUpdatedOn() { + return lastUpdatedOn; + } + + public void setLastUpdatedOn(Date lastUpdatedOn) { + this.lastUpdatedOn = lastUpdatedOn; + } + + public String getLastUpdatedBy() { + return lastUpdatedBy; + } + + public void setLastUpdatedBy(String lastUpdatedBy) { + this.lastUpdatedBy = lastUpdatedBy; + } + + @XmlTransient + public List getRoleEntityList() { + return roleEntityList; + } + + public void setRoleEntityList(List roleEntityList) { + this.roleEntityList = roleEntityList; + } + + @XmlTransient + public List getAccountHistoryEntityList() { + return accountHistoryEntityList; + } + + public void setAccountHistoryEntityList(List accountHistoryEntityList) { + this.accountHistoryEntityList = accountHistoryEntityList; + } + + @Override + public int hashCode() { + int hash = 0; + hash += (username != null ? username.hashCode() : 0); + return hash; + } + + @Override + public boolean equals(Object object) { + // TODO: Warning - this method won't work in the case the id fields are not set + if (!(object instanceof AccountEntity)) { + return false; + } + AccountEntity other = (AccountEntity) object; + if ((this.username == null && other.username != null) || (this.username != null && !this.username.equals(other.username))) { + return false; + } + return true; + } + + @Override + public String toString() { + return "de.muehlencord.ssg.entity.AccountEntity[ username=" + username + " ]"; + } + +} diff --git a/account/src/main/java/de/muehlencord/shared/account/entity/AccountHistoryEntity.java b/account/src/main/java/de/muehlencord/shared/account/entity/AccountHistoryEntity.java new file mode 100644 index 0000000..af07350 --- /dev/null +++ b/account/src/main/java/de/muehlencord/shared/account/entity/AccountHistoryEntity.java @@ -0,0 +1,167 @@ +package de.muehlencord.shared.account.entity; + +import java.io.Serializable; +import java.util.Date; +import javax.persistence.Basic; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; +import javax.persistence.NamedQueries; +import javax.persistence.NamedQuery; +import javax.persistence.Table; +import javax.persistence.Temporal; +import javax.persistence.TemporalType; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; +import javax.xml.bind.annotation.XmlRootElement; + +/** + * + * @author joern.muehlencord + */ +@Entity +@Table(name = "account_history") +@XmlRootElement +@NamedQueries({ + @NamedQuery(name = "AccountHistoryEntity.findAll", query = "SELECT a FROM AccountHistoryEntity a"), + @NamedQuery(name = "AccountHistoryEntity.findById", query = "SELECT a FROM AccountHistoryEntity a WHERE a.id = :id"), + @NamedQuery(name = "AccountHistoryEntity.findByMessage", query = "SELECT a FROM AccountHistoryEntity a WHERE a.message = :message"), + @NamedQuery(name = "AccountHistoryEntity.findByFailureCount", query = "SELECT a FROM AccountHistoryEntity a WHERE a.failureCount = :failureCount"), + @NamedQuery(name = "AccountHistoryEntity.findByStatus", query = "SELECT a FROM AccountHistoryEntity a WHERE a.status = :status"), + @NamedQuery(name = "AccountHistoryEntity.findByLastUpdatedOn", query = "SELECT a FROM AccountHistoryEntity a WHERE a.lastUpdatedOn = :lastUpdatedOn"), + @NamedQuery(name = "AccountHistoryEntity.findByLastUpdatedBy", query = "SELECT a FROM AccountHistoryEntity a WHERE a.lastUpdatedBy = :lastUpdatedBy")}) +public class AccountHistoryEntity implements Serializable { + + private static final long serialVersionUID = 1L; + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Basic(optional = false) + @Column(name = "id") + private Integer id; + @Size(max = 200) + @Column(name = "message") + private String message; + @Basic(optional = false) + @NotNull + @Column(name = "failure_count") + private int failureCount; + @Basic(optional = false) + @NotNull + @Size(min = 1, max = 20) + @Column(name = "status") + private String status; + @Basic(optional = false) + @NotNull + @Column(name = "last_updated_on") + @Temporal(TemporalType.TIMESTAMP) + private Date lastUpdatedOn; + @Basic(optional = false) + @NotNull + @Size(min = 1, max = 32) + @Column(name = "last_updated_by") + private String lastUpdatedBy; + @JoinColumn(name = "username", referencedColumnName = "username") + @ManyToOne(optional = false) + private AccountEntity username; + + public AccountHistoryEntity() { + } + + public AccountHistoryEntity(Integer id) { + this.id = id; + } + + public AccountHistoryEntity(Integer id, int failureCount, String status, Date lastUpdatedOn, String lastUpdatedBy) { + this.id = id; + this.failureCount = failureCount; + this.status = status; + this.lastUpdatedOn = lastUpdatedOn; + this.lastUpdatedBy = lastUpdatedBy; + } + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public int getFailureCount() { + return failureCount; + } + + public void setFailureCount(int failureCount) { + this.failureCount = failureCount; + } + + public String getStatus() { + return status; + } + + public void setStatus(String status) { + this.status = status; + } + + public Date getLastUpdatedOn() { + return lastUpdatedOn; + } + + public void setLastUpdatedOn(Date lastUpdatedOn) { + this.lastUpdatedOn = lastUpdatedOn; + } + + public String getLastUpdatedBy() { + return lastUpdatedBy; + } + + public void setLastUpdatedBy(String lastUpdatedBy) { + this.lastUpdatedBy = lastUpdatedBy; + } + + public AccountEntity getUsername() { + return username; + } + + public void setUsername(AccountEntity username) { + this.username = username; + } + + @Override + public int hashCode() { + int hash = 0; + hash += (id != null ? id.hashCode() : 0); + return hash; + } + + @Override + public boolean equals(Object object) { + // TODO: Warning - this method won't work in the case the id fields are not set + if (!(object instanceof AccountHistoryEntity)) { + return false; + } + AccountHistoryEntity other = (AccountHistoryEntity) object; + if ((this.id == null && other.id != null) || (this.id != null && !this.id.equals(other.id))) { + return false; + } + return true; + } + + @Override + public String toString() { + return "de.muehlencord.ssg.entity.AccountHistoryEntity[ id=" + id + " ]"; + } + +} diff --git a/account/src/main/java/de/muehlencord/shared/account/entity/ConfigEntity.java b/account/src/main/java/de/muehlencord/shared/account/entity/ConfigEntity.java new file mode 100644 index 0000000..66c72b5 --- /dev/null +++ b/account/src/main/java/de/muehlencord/shared/account/entity/ConfigEntity.java @@ -0,0 +1,92 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package de.muehlencord.shared.account.entity; + +import java.io.Serializable; +import javax.persistence.Basic; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.NamedQueries; +import javax.persistence.NamedQuery; +import javax.persistence.Table; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; +import javax.xml.bind.annotation.XmlRootElement; + +/** + * + * @author jomu + */ +@Entity +@Table(name = "config") +@XmlRootElement +@NamedQueries({ + @NamedQuery(name = "ConfigEntity.findAll", query = "SELECT c FROM ConfigEntity c"), + @NamedQuery(name = "ConfigEntity.findByConfigKey", query = "SELECT c FROM ConfigEntity c WHERE c.configKey = :configKey"), + @NamedQuery(name = "ConfigEntity.findByConfigValue", query = "SELECT c FROM ConfigEntity c WHERE c.configValue = :configValue")}) +public class ConfigEntity implements Serializable { + + private static final long serialVersionUID = 1L; + @Id + @Basic(optional = false) + @NotNull + @Size(min = 1, max = 100) + @Column(name = "config_key") + private String configKey; + @Size(max = 200) + @Column(name = "config_value") + private String configValue; + + public ConfigEntity() { + } + + public ConfigEntity(String configKey) { + this.configKey = configKey; + } + + public String getConfigKey() { + return configKey; + } + + public void setConfigKey(String configKey) { + this.configKey = configKey; + } + + public String getConfigValue() { + return configValue; + } + + public void setConfigValue(String configValue) { + this.configValue = configValue; + } + + @Override + public int hashCode() { + int hash = 0; + hash += (configKey != null ? configKey.hashCode() : 0); + return hash; + } + + @Override + public boolean equals(Object object) { + // TODO: Warning - this method won't work in the case the id fields are not set + if (!(object instanceof ConfigEntity)) { + return false; + } + ConfigEntity other = (ConfigEntity) object; + if ((this.configKey == null && other.configKey != null) || (this.configKey != null && !this.configKey.equals(other.configKey))) { + return false; + } + return true; + } + + @Override + public String toString() { + return "de.muehlencord.ssg.entity.ConfigEntity[ configKey=" + configKey + " ]"; + } + +} diff --git a/account/src/main/java/de/muehlencord/shared/account/entity/MailTemplateEntity.java b/account/src/main/java/de/muehlencord/shared/account/entity/MailTemplateEntity.java new file mode 100644 index 0000000..cfa0106 --- /dev/null +++ b/account/src/main/java/de/muehlencord/shared/account/entity/MailTemplateEntity.java @@ -0,0 +1,94 @@ +package de.muehlencord.shared.account.entity; + +import java.io.Serializable; +import javax.persistence.Basic; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.NamedQueries; +import javax.persistence.NamedQuery; +import javax.persistence.Table; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; +import javax.xml.bind.annotation.XmlRootElement; + +/** + * + * @author jomu + */ +@Entity +@Table(name = "mail_template") +@XmlRootElement +@NamedQueries({ + @NamedQuery(name = "MailTemplateEntity.findAll", query = "SELECT m FROM MailTemplateEntity m"), + @NamedQuery(name = "MailTemplateEntity.findByTemplateName", query = "SELECT m FROM MailTemplateEntity m WHERE m.templateName = :templateName"), + @NamedQuery(name = "MailTemplateEntity.findByTemplateValue", query = "SELECT m FROM MailTemplateEntity m WHERE m.templateValue = :templateValue")}) +public class MailTemplateEntity implements Serializable { + + private static final long serialVersionUID = 1L; + @Id + @Basic(optional = false) + @NotNull + @Size(min = 1, max = 40) + @Column(name = "template_name") + private String templateName; + @Basic(optional = false) + @NotNull + @Size(min = 1, max = 2147483647) + @Column(name = "template_value") + private String templateValue; + + public MailTemplateEntity() { + } + + public MailTemplateEntity(String templateName) { + this.templateName = templateName; + } + + public MailTemplateEntity(String templateName, String templateValue) { + this.templateName = templateName; + this.templateValue = templateValue; + } + + public String getTemplateName() { + return templateName; + } + + public void setTemplateName(String templateName) { + this.templateName = templateName; + } + + public String getTemplateValue() { + return templateValue; + } + + public void setTemplateValue(String templateValue) { + this.templateValue = templateValue; + } + + @Override + public int hashCode() { + int hash = 0; + hash += (templateName != null ? templateName.hashCode() : 0); + return hash; + } + + @Override + public boolean equals(Object object) { + // TODO: Warning - this method won't work in the case the id fields are not set + if (!(object instanceof MailTemplateEntity)) { + return false; + } + MailTemplateEntity other = (MailTemplateEntity) object; + if ((this.templateName == null && other.templateName != null) || (this.templateName != null && !this.templateName.equals(other.templateName))) { + return false; + } + return true; + } + + @Override + public String toString() { + return "de.muehlencord.ssg.entity.MailTemplateEntity[ templateName=" + templateName + " ]"; + } + +} diff --git a/account/src/main/java/de/muehlencord/shared/account/entity/PermissionEntity.java b/account/src/main/java/de/muehlencord/shared/account/entity/PermissionEntity.java new file mode 100644 index 0000000..cce033d --- /dev/null +++ b/account/src/main/java/de/muehlencord/shared/account/entity/PermissionEntity.java @@ -0,0 +1,113 @@ +package de.muehlencord.shared.account.entity; + +import java.io.Serializable; +import java.util.List; +import javax.persistence.Basic; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.JoinTable; +import javax.persistence.ManyToMany; +import javax.persistence.NamedQueries; +import javax.persistence.NamedQuery; +import javax.persistence.Table; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlTransient; + +/** + * + * @author joern.muehlencord + */ +@Entity +@Table(name = "application_permission") +@XmlRootElement +@NamedQueries({ + @NamedQuery(name = "PermissionEntity.findAll", query = "SELECT p FROM PermissionEntity p"), + @NamedQuery(name = "PermissionEntity.findByPermissionName", query = "SELECT p FROM PermissionEntity p WHERE p.permissionName = :permissionName"), + @NamedQuery(name = "PermissionEntity.findByPermissionDescription", query = "SELECT p FROM PermissionEntity p WHERE p.permissionDescription = :permissionDescription")}) +public class PermissionEntity implements Serializable { + + private static final long serialVersionUID = 1L; + @Id + @Basic(optional = false) + @NotNull + @Size(min = 1, max = 80) + @Column(name = "permission_name") + private String permissionName; + @Basic(optional = false) + @NotNull + @Size(min = 1, max = 200) + @Column(name = "permission_description") + private String permissionDescription; + @JoinTable(name = "role_permission", joinColumns = { + @JoinColumn(name = "permission_name", referencedColumnName = "permission_name")}, inverseJoinColumns = { + @JoinColumn(name = "role_name", referencedColumnName = "role_name")}) + @ManyToMany + private List roleEntityList; + + public PermissionEntity() { + } + + public PermissionEntity(String permissionName) { + this.permissionName = permissionName; + } + + public PermissionEntity(String permissionName, String permissionDescription) { + this.permissionName = permissionName; + this.permissionDescription = permissionDescription; + } + + public String getPermissionName() { + return permissionName; + } + + public void setPermissionName(String permissionName) { + this.permissionName = permissionName; + } + + public String getPermissionDescription() { + return permissionDescription; + } + + public void setPermissionDescription(String permissionDescription) { + this.permissionDescription = permissionDescription; + } + + @XmlTransient + public List getRoleEntityList() { + return roleEntityList; + } + + public void setRoleEntityList(List roleEntityList) { + this.roleEntityList = roleEntityList; + } + + @Override + public int hashCode() { + int hash = 0; + hash += (permissionName != null ? permissionName.hashCode() : 0); + return hash; + } + + @Override + public boolean equals(Object object) { + // TODO: Warning - this method won't work in the case the id fields are not set + if (!(object instanceof PermissionEntity)) { + return false; + } + PermissionEntity other = (PermissionEntity) object; + if ((this.permissionName == null && other.permissionName != null) || (this.permissionName != null && !this.permissionName.equals(other.permissionName))) { + return false; + } + return true; + } + + @Override + public String toString() { + return "de.muehlencord.ssg.entity.PermissionEntity[ permissionName=" + permissionName + " ]"; + } + +} diff --git a/account/src/main/java/de/muehlencord/shared/account/entity/RoleEntity.java b/account/src/main/java/de/muehlencord/shared/account/entity/RoleEntity.java new file mode 100644 index 0000000..cd03330 --- /dev/null +++ b/account/src/main/java/de/muehlencord/shared/account/entity/RoleEntity.java @@ -0,0 +1,119 @@ +package de.muehlencord.shared.account.entity; + +import java.io.Serializable; +import java.util.List; +import javax.persistence.Basic; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.ManyToMany; +import javax.persistence.NamedQueries; +import javax.persistence.NamedQuery; +import javax.persistence.Table; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlTransient; + +/** + * + * @author joern.muehlencord + */ +@Entity +@Table(name = "application_role") +@XmlRootElement +@NamedQueries({ + @NamedQuery(name = "RoleEntity.findAll", query = "SELECT r FROM RoleEntity r"), + @NamedQuery(name = "RoleEntity.findByRoleName", query = "SELECT r FROM RoleEntity r WHERE r.roleName = :roleName"), + @NamedQuery(name = "RoleEntity.findByRoleDescription", query = "SELECT r FROM RoleEntity r WHERE r.roleDescription = :roleDescription")}) +public class RoleEntity implements Serializable { + + private static final long serialVersionUID = 1L; + @Id + @Basic(optional = false) + @NotNull + @Size(min = 1, max = 80) + @Column(name = "role_name") + private String roleName; + @Basic(optional = false) + @NotNull + @Size(min = 1, max = 200) + @Column(name = "role_description") + private String roleDescription; + @ManyToMany(mappedBy = "roleEntityList") + private List accountEntityList; + @ManyToMany(mappedBy = "roleEntityList") + private List permissionEntityList; + + public RoleEntity() { + } + + public RoleEntity(String roleName) { + this.roleName = roleName; + } + + public RoleEntity(String roleName, String roleDescription) { + this.roleName = roleName; + this.roleDescription = roleDescription; + } + + public String getRoleName() { + return roleName; + } + + public void setRoleName(String roleName) { + this.roleName = roleName; + } + + public String getRoleDescription() { + return roleDescription; + } + + public void setRoleDescription(String roleDescription) { + this.roleDescription = roleDescription; + } + + @XmlTransient + public List getAccountEntityList() { + return accountEntityList; + } + + public void setAccountEntityList(List accountEntityList) { + this.accountEntityList = accountEntityList; + } + + @XmlTransient + public List getPermissionEntityList() { + return permissionEntityList; + } + + public void setPermissionEntityList(List permissionEntityList) { + this.permissionEntityList = permissionEntityList; + } + + @Override + public int hashCode() { + int hash = 0; + hash += (roleName != null ? roleName.hashCode() : 0); + return hash; + } + + @Override + public boolean equals(Object object) { + // TODO: Warning - this method won't work in the case the id fields are not set + if (!(object instanceof RoleEntity)) { + return false; + } + RoleEntity other = (RoleEntity) object; + if ((this.roleName == null && other.roleName != null) || (this.roleName != null && !this.roleName.equals(other.roleName))) { + return false; + } + return true; + } + + @Override + public String toString() { + return "de.muehlencord.ssg.entity.RoleEntity[ roleName=" + roleName + " ]"; + } + +} diff --git a/account/src/main/java/de/muehlencord/shared/account/util/SecurityUtil.java b/account/src/main/java/de/muehlencord/shared/account/util/SecurityUtil.java new file mode 100644 index 0000000..67067bf --- /dev/null +++ b/account/src/main/java/de/muehlencord/shared/account/util/SecurityUtil.java @@ -0,0 +1,33 @@ +package de.muehlencord.shared.account.util; + +import org.apache.log4j.Level; +import org.apache.log4j.Logger; +import org.apache.shiro.authc.credential.DefaultPasswordService; +import org.apache.shiro.crypto.hash.DefaultHashService; +import org.apache.shiro.crypto.hash.Sha512Hash; + +/** + * + * @author jomu + */ +public class SecurityUtil { + + private final static Logger LOGGER = Logger.getLogger (SecurityUtil.class.getName()); + + public static String createPassword(String clearTextPassword) { + // TODO read values from shiro.ini + DefaultHashService hashService = new DefaultHashService(); + hashService.setHashIterations(500000); // + hashService.setHashAlgorithmName(Sha512Hash.ALGORITHM_NAME); + hashService.setGeneratePublicSalt(true); + + DefaultPasswordService passwordService = new DefaultPasswordService(); + passwordService.setHashService(hashService); + + // try to encrypt password + String encryptedPassword = passwordService.encryptPassword(clearTextPassword); + LOGGER.log (Level.TRACE, encryptedPassword); + return encryptedPassword; + } + +} diff --git a/account/src/main/resources/META-INF/MANIFEST.MF b/account/src/main/resources/META-INF/MANIFEST.MF new file mode 100644 index 0000000..59499bc --- /dev/null +++ b/account/src/main/resources/META-INF/MANIFEST.MF @@ -0,0 +1,2 @@ +Manifest-Version: 1.0 +