From c254d27e84c02f6aaf71c5195fda1fee4c6bd715 Mon Sep 17 00:00:00 2001 From: jomu Date: Sat, 10 Nov 2018 14:02:29 +0100 Subject: [PATCH] added frist draft of account ui --- account-ui/faces-config.NavData | 6 + account-ui/nb-configuration.xml | 22 ++ account-ui/pom.xml | 108 ++++++++ .../shared/account/web/AccountProducer.java | 91 +++++++ .../account/web/ApplicationController.java | 59 ++++ .../account/web/ConfigurationProducer.java | 53 ++++ .../account/web/FacesContextProducer.java | 38 +++ .../web/PersistenceContextFactory.java | 42 +++ .../account/web/ResourceBundleProducer.java | 53 ++++ .../web/TransactionJoinInterceptor.java | 48 ++++ .../account/web/presentation/AccountView.java | 180 +++++++++++++ .../web/presentation/ApplicationView.java | 99 +++++++ .../account/web/presentation/GroupView.java | 252 ++++++++++++++++++ .../web/presentation/InstanceView.java | 46 ++++ .../web/presentation/PermissionView.java | 167 ++++++++++++ .../UniqueApplicationValidator.java | 36 +++ .../main/resources/META-INF/persistence.xml | 22 ++ .../main/resources/admin-config.properties | 32 +++ .../src/main/resources/buildInfo.properties | 2 + .../web/presentation/messages.properties | 131 +++++++++ .../web/presentation/messages_de.properties | 132 +++++++++ .../web/presentation/messages_en.properties | 132 +++++++++ account-ui/src/main/webapp/WEB-INF/beans.xml | 7 + .../src/main/webapp/WEB-INF/faces-config.xml | 26 ++ account-ui/src/main/webapp/WEB-INF/shiro.ini | 57 ++++ account-ui/src/main/webapp/WEB-INF/web.xml | 123 +++++++++ account-ui/src/main/webapp/login.xhtml | 99 +++++++ account-ui/src/main/webapp/logout.xhtml | 15 ++ .../composite/confirmationDialog.xhtml | 21 ++ .../src/main/webapp/resources/css/admin.css | 18 ++ .../webapp/resources/template/footer.xhtml | 15 ++ .../webapp/resources/template/leftmenu.xhtml | 46 ++++ .../webapp/resources/template/template.xhtml | 50 ++++ account-ui/src/main/webapp/web/account.xhtml | 251 +++++++++++++++++ account-ui/src/main/webapp/web/index.xhtml | 76 ++++++ .../src/main/webapp/web/permissions.xhtml | 73 +++++ account-ui/src/main/webapp/web/roles.xhtml | 96 +++++++ 37 files changed, 2724 insertions(+) create mode 100644 account-ui/faces-config.NavData create mode 100644 account-ui/nb-configuration.xml create mode 100644 account-ui/pom.xml create mode 100644 account-ui/src/main/java/de/muehlencord/shared/account/web/AccountProducer.java create mode 100644 account-ui/src/main/java/de/muehlencord/shared/account/web/ApplicationController.java create mode 100644 account-ui/src/main/java/de/muehlencord/shared/account/web/ConfigurationProducer.java create mode 100644 account-ui/src/main/java/de/muehlencord/shared/account/web/FacesContextProducer.java create mode 100644 account-ui/src/main/java/de/muehlencord/shared/account/web/PersistenceContextFactory.java create mode 100644 account-ui/src/main/java/de/muehlencord/shared/account/web/ResourceBundleProducer.java create mode 100644 account-ui/src/main/java/de/muehlencord/shared/account/web/TransactionJoinInterceptor.java create mode 100644 account-ui/src/main/java/de/muehlencord/shared/account/web/presentation/AccountView.java create mode 100644 account-ui/src/main/java/de/muehlencord/shared/account/web/presentation/ApplicationView.java create mode 100644 account-ui/src/main/java/de/muehlencord/shared/account/web/presentation/GroupView.java create mode 100644 account-ui/src/main/java/de/muehlencord/shared/account/web/presentation/InstanceView.java create mode 100644 account-ui/src/main/java/de/muehlencord/shared/account/web/presentation/PermissionView.java create mode 100644 account-ui/src/main/java/de/muehlencord/shared/account/web/presentation/UniqueApplicationValidator.java create mode 100644 account-ui/src/main/resources/META-INF/persistence.xml create mode 100644 account-ui/src/main/resources/admin-config.properties create mode 100644 account-ui/src/main/resources/buildInfo.properties create mode 100644 account-ui/src/main/resources/de/muehlencord/shared/account/web/presentation/messages.properties create mode 100644 account-ui/src/main/resources/de/muehlencord/shared/account/web/presentation/messages_de.properties create mode 100644 account-ui/src/main/resources/de/muehlencord/shared/account/web/presentation/messages_en.properties create mode 100644 account-ui/src/main/webapp/WEB-INF/beans.xml create mode 100644 account-ui/src/main/webapp/WEB-INF/faces-config.xml create mode 100644 account-ui/src/main/webapp/WEB-INF/shiro.ini create mode 100644 account-ui/src/main/webapp/WEB-INF/web.xml create mode 100644 account-ui/src/main/webapp/login.xhtml create mode 100644 account-ui/src/main/webapp/logout.xhtml create mode 100644 account-ui/src/main/webapp/resources/composite/confirmationDialog.xhtml create mode 100644 account-ui/src/main/webapp/resources/css/admin.css create mode 100644 account-ui/src/main/webapp/resources/template/footer.xhtml create mode 100644 account-ui/src/main/webapp/resources/template/leftmenu.xhtml create mode 100644 account-ui/src/main/webapp/resources/template/template.xhtml create mode 100644 account-ui/src/main/webapp/web/account.xhtml create mode 100644 account-ui/src/main/webapp/web/index.xhtml create mode 100644 account-ui/src/main/webapp/web/permissions.xhtml create mode 100644 account-ui/src/main/webapp/web/roles.xhtml diff --git a/account-ui/faces-config.NavData b/account-ui/faces-config.NavData new file mode 100644 index 0000000..298bfc5 --- /dev/null +++ b/account-ui/faces-config.NavData @@ -0,0 +1,6 @@ + + + + + + diff --git a/account-ui/nb-configuration.xml b/account-ui/nb-configuration.xml new file mode 100644 index 0000000..5402a7c --- /dev/null +++ b/account-ui/nb-configuration.xml @@ -0,0 +1,22 @@ + + + + + + + Facelets + WildFly + apache20 + js/libs + + diff --git a/account-ui/pom.xml b/account-ui/pom.xml new file mode 100644 index 0000000..f08f62e --- /dev/null +++ b/account-ui/pom.xml @@ -0,0 +1,108 @@ + + + 4.0.0 + + shared + de.muehlencord + 1.1-SNAPSHOT + + + de.muehlencord + account-ui + 1.1-SNAPSHOT + war + + shared-account-ui + + + UTF-8 + ${maven.build.timestamp} + 10 + 10 + + + + + org.primefaces + primefaces + + + + com.github.adminfaces + admin-template + 1.0.0-RC19 + + + + org.omnifaces + omnifaces + 2.7 + + + + org.apache.shiro + shiro-core + + + org.apache.shiro + shiro-web + + + de.muehlencord.shared + shared-shiro-faces + 1.1-SNAPSHOT + + + de.muehlencord.shared + shared-account + 1.1-SNAPSHOT + jar + + + de.muehlencord.sf + filter + 1.0 + + + javax + javaee-web-api + 7.0 + provided + + + + + + + + src/main/resources + true + + **/*.properties + **/*.xml + + + + + account + + + org.apache.maven.plugins + maven-compiler-plugin + 3.8.0 + + 10 + 10 + + + + org.apache.maven.plugins + maven-war-plugin + 3.2.2 + + false + + + + + diff --git a/account-ui/src/main/java/de/muehlencord/shared/account/web/AccountProducer.java b/account-ui/src/main/java/de/muehlencord/shared/account/web/AccountProducer.java new file mode 100644 index 0000000..dd1690b --- /dev/null +++ b/account-ui/src/main/java/de/muehlencord/shared/account/web/AccountProducer.java @@ -0,0 +1,91 @@ +/* + * Copyright 2017 Joern Muehlencord . + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package de.muehlencord.shared.account.web; + +import de.muehlencord.shared.account.business.account.boundary.AccountControl; +import de.muehlencord.shared.account.business.account.entity.Account; +import java.io.Serializable; +import java.util.Locale; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import javax.annotation.ManagedBean; +import javax.ejb.EJB; +import javax.enterprise.context.SessionScoped; +import javax.enterprise.inject.Produces; +import org.apache.shiro.SecurityUtils; +import org.apache.shiro.subject.Subject; + +/** + * + * @author Joern Muehlencord + */ +@ManagedBean +@SessionScoped +public class AccountProducer implements Serializable { + + private static final long serialVersionUID = -3806204732038165311L; + private final Map objectMap = new ConcurrentHashMap<>(); + + @EJB + AccountControl accountController; + + private Account account = null; + + @Produces + public Account getAccount() { + String accountName; + if (account == null) { + Subject subject = SecurityUtils.getSubject(); + if (subject == null) { + return null; + } + + if ((subject.isAuthenticated() == false) && (subject.isRemembered() == false)) { + accountName = "web"; + } else { + accountName = subject.getPrincipal().toString(); + } + account = accountController.getAccountEntity(accountName, true); + } + return account; + } + + public T getValue(String key, Class clazz) { + if (objectMap.containsKey(key)) { + Object obj = objectMap.get(key); + if (obj == null) { + return null; + } + try { + return clazz.cast(obj); + } catch (ClassCastException ex) { + return null; + } + } else { + return null; + } + } + + public void setValue(String key, Object obj) { + objectMap.put(key, obj); + } + + @Produces + public Locale getLocale() { + return Locale.ENGLISH; // TODO depend lcoale on account or on incoming request + } + +} diff --git a/account-ui/src/main/java/de/muehlencord/shared/account/web/ApplicationController.java b/account-ui/src/main/java/de/muehlencord/shared/account/web/ApplicationController.java new file mode 100644 index 0000000..084d3a4 --- /dev/null +++ b/account-ui/src/main/java/de/muehlencord/shared/account/web/ApplicationController.java @@ -0,0 +1,59 @@ +package de.muehlencord.shared.account.web; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Properties; +import javax.annotation.PostConstruct; +import javax.enterprise.context.ApplicationScoped; +import javax.inject.Named; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * + * @author joern.muehlencord + */ +@Named(value = "applicationController") +@ApplicationScoped +public class ApplicationController { + + private final static Logger LOGGER = LoggerFactory.getLogger(ApplicationController.class.getName()); + + private String version; + private String buildDate; + + @PostConstruct + public void init() { + InputStream in = Thread.currentThread().getContextClassLoader().getResourceAsStream("buildInfo.properties"); + if (in == null) { + return; + } + + Properties props = new Properties(); + try { + props.load(in); + + version = props.getProperty("build.version"); + buildDate = props.getProperty("build.timestamp"); + + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("buildInfo.properties read successfully"); + } + + } catch (IOException ex) { + LOGGER.error("Cannot find buildInfo.properties. ", ex); + version = "??"; + buildDate = "??"; + } + + } + + /* *** getter / setter *** */ + public String getVersion() { + return version; + } + + public String getBuildDate() { + return buildDate; + } +} diff --git a/account-ui/src/main/java/de/muehlencord/shared/account/web/ConfigurationProducer.java b/account-ui/src/main/java/de/muehlencord/shared/account/web/ConfigurationProducer.java new file mode 100644 index 0000000..09bd878 --- /dev/null +++ b/account-ui/src/main/java/de/muehlencord/shared/account/web/ConfigurationProducer.java @@ -0,0 +1,53 @@ +/* + * 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.web; + +import de.muehlencord.shared.account.business.config.boundary.ConfigService; +import de.muehlencord.shared.account.business.accountcounfig.entity.AccountConfigurationKey; +import de.muehlencord.shared.account.business.accountcounfig.entity.AccountConfigurationValue; +import de.muehlencord.shared.account.business.config.entity.ConfigException; +import javax.ejb.EJB; +import javax.enterprise.context.Dependent; +import javax.enterprise.inject.Produces; +import javax.enterprise.inject.spi.Annotated; +import javax.enterprise.inject.spi.InjectionPoint; + +/** + * + * @author Joern Muehlencord + */ +@Dependent +public class ConfigurationProducer { + + @EJB + ConfigService configService; + + @Produces + @AccountConfigurationValue(key = AccountConfigurationKey.Producer) + public String produceConfigurationValue(InjectionPoint injectionPoint) { + Annotated annotated = injectionPoint.getAnnotated(); + AccountConfigurationValue annotation = annotated.getAnnotation(AccountConfigurationValue.class); + if (annotation != null) { + AccountConfigurationKey key = annotation.key(); + if (key != null) { + try { + switch (key) { + case BaseUrl: + return configService.getConfigValue("base.url"); + case PasswordResetUrl: + return configService.getConfigValue("base.url") + "/login.xhtml"; + default: + throw new IllegalStateException("Invalid key " + key + " for injection point: " + injectionPoint); + } + } catch (ConfigException ex) { + throw new IllegalStateException("Invalid key " + key + " for injection point: " + injectionPoint + ". Exception: " + ex.getMessage()); + } + } + } + throw new IllegalStateException("No key for injection point: " + injectionPoint); + } + +} diff --git a/account-ui/src/main/java/de/muehlencord/shared/account/web/FacesContextProducer.java b/account-ui/src/main/java/de/muehlencord/shared/account/web/FacesContextProducer.java new file mode 100644 index 0000000..d26de80 --- /dev/null +++ b/account-ui/src/main/java/de/muehlencord/shared/account/web/FacesContextProducer.java @@ -0,0 +1,38 @@ +/* + * Copyright 2017 Joern Muehlencord . + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package de.muehlencord.shared.account.web; + +import javax.enterprise.context.ContextNotActiveException; +import javax.enterprise.context.RequestScoped; +import javax.enterprise.inject.Produces; +import javax.faces.context.FacesContext; + +/** + * + * @author Joern Muehlencord + */ +public class FacesContextProducer { + + @Produces + @RequestScoped + public FacesContext getFacesContext() { + FacesContext ctx = FacesContext.getCurrentInstance(); + if (ctx == null) { + throw new ContextNotActiveException("FacesContext is not active"); + } + return ctx; + } +} diff --git a/account-ui/src/main/java/de/muehlencord/shared/account/web/PersistenceContextFactory.java b/account-ui/src/main/java/de/muehlencord/shared/account/web/PersistenceContextFactory.java new file mode 100644 index 0000000..a4852f2 --- /dev/null +++ b/account-ui/src/main/java/de/muehlencord/shared/account/web/PersistenceContextFactory.java @@ -0,0 +1,42 @@ +package de.muehlencord.shared.account.web; + +import javax.enterprise.context.ApplicationScoped; +import javax.enterprise.context.RequestScoped; +import javax.enterprise.inject.Disposes; +import javax.enterprise.inject.Produces; +import javax.persistence.EntityManager; +import javax.persistence.EntityManagerFactory; +import javax.persistence.PersistenceUnit; +import javax.persistence.SynchronizationType; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * + * @author Joern Muehlencord + */ +@ApplicationScoped +public class PersistenceContextFactory { + + private static final Logger LOGGER = LoggerFactory.getLogger(PersistenceContextFactory.class); + + @PersistenceUnit + EntityManagerFactory emf; + + @Produces + @RequestScoped + public EntityManager getEntityManager() { + if (LOGGER.isTraceEnabled()) { + LOGGER.trace("getting entityManager"); + } + return emf.createEntityManager(SynchronizationType.UNSYNCHRONIZED); + } + + public void closeEntityManager (@Disposes EntityManager em) { + if (LOGGER.isTraceEnabled()) { + LOGGER.trace("closing entityManager"); + } + em.close(); + } + +} diff --git a/account-ui/src/main/java/de/muehlencord/shared/account/web/ResourceBundleProducer.java b/account-ui/src/main/java/de/muehlencord/shared/account/web/ResourceBundleProducer.java new file mode 100644 index 0000000..fc96859 --- /dev/null +++ b/account-ui/src/main/java/de/muehlencord/shared/account/web/ResourceBundleProducer.java @@ -0,0 +1,53 @@ +/* + * Copyright 2017 Joern Muehlencord . + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package de.muehlencord.shared.account.web; + +import java.io.Serializable; +import java.util.Locale; +import java.util.ResourceBundle; +import javax.enterprise.context.RequestScoped; +import javax.enterprise.inject.Produces; +import javax.faces.context.FacesContext; +import javax.inject.Inject; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * + * @author Joern Muehlencord + */ +@RequestScoped +public class ResourceBundleProducer implements Serializable { + + private static final Logger LOGGER = LoggerFactory.getLogger(ResourceBundleProducer.class); + + private static final long serialVersionUID = 3764096270387408239L; + + @Inject + private Locale locale; + + @Inject + private FacesContext facesContext; + + @Produces + public ResourceBundle getResourceBundle() { + ResourceBundle rb = ResourceBundle.getBundle("de.muehlencord.shared.account.web.presentation.messages", facesContext.getViewRoot().getLocale()); + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("ResourceBundle = "+rb); + } + return rb; + } +} diff --git a/account-ui/src/main/java/de/muehlencord/shared/account/web/TransactionJoinInterceptor.java b/account-ui/src/main/java/de/muehlencord/shared/account/web/TransactionJoinInterceptor.java new file mode 100644 index 0000000..39f5089 --- /dev/null +++ b/account-ui/src/main/java/de/muehlencord/shared/account/web/TransactionJoinInterceptor.java @@ -0,0 +1,48 @@ +package de.muehlencord.shared.account.web; + +import javax.annotation.Priority; +import javax.inject.Inject; +import javax.interceptor.AroundInvoke; +import javax.interceptor.Interceptor; +import javax.interceptor.InvocationContext; +import javax.persistence.EntityManager; +import javax.transaction.Transactional; +import static javax.transaction.Transactional.TxType.REQUIRED; +import javax.validation.constraints.NotNull; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + + +/** + * + * @author Joern Muehlencord + */ +@Transactional(value = REQUIRED) +@Interceptor +@Priority(value=TransactionJoinInterceptor.PRIORITY) +public class TransactionJoinInterceptor { + + private static final Logger LOGGER = LoggerFactory.getLogger(TransactionJoinInterceptor.class); + + // attach behind the interceptor of the container + public static final int PRIORITY = Interceptor.Priority.PLATFORM_BEFORE+250; + + private final EntityManager em; + + @Inject + public TransactionJoinInterceptor(@NotNull EntityManager em) { + this.em = em; + } + + + @AroundInvoke + public Object joinTransaction(InvocationContext context) throws Exception { + if (em.isJoinedToTransaction()) { + LOGGER.info("transaction already joined"); + } else { + LOGGER.info("joining transaction"); + em.joinTransaction(); + } + return context.proceed(); + } +} 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 new file mode 100644 index 0000000..79e4d84 --- /dev/null +++ b/account-ui/src/main/java/de/muehlencord/shared/account/web/presentation/AccountView.java @@ -0,0 +1,180 @@ +package de.muehlencord.shared.account.web.presentation; + +import de.muehlencord.shared.account.business.account.boundary.AccountControl; +import de.muehlencord.shared.account.business.account.boundary.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.AccountStatus; +import de.muehlencord.shared.account.business.account.entity.ApplicationRoleEntity; +import de.muehlencord.shared.jeeutil.FacesUtil; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; +import javax.ejb.EJB; +import javax.enterprise.context.SessionScoped; +import javax.inject.Named; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * + * @author jomu + */ +@SessionScoped +@Named +public class AccountView implements Serializable { + + private static final long serialVersionUID = -8050582392249849438L; + private static final Logger LOGGER = LoggerFactory.getLogger(AccountView.class); + + @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; + // boolean flag to toggle buttons which require an account to be selected + private boolean accountSelected = false; + + public List getAccounts() { + if (accountList == null) { + accountList = accountService.getAccounts(showDisabledAccounts); + } + return accountList; + } + + public List getAllApplicationRoles() { + if (applicationRoles == null) { + applicationRoles = appliationRoleService.getAllRoles(); + } + return applicationRoles; + } + + public void newAccount() { + currentAccount = new AccountEntity(); + currentAccount.setUsername(null); + 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)) { + currentAccount.setUsername(null); + FacesUtil.addErrorMessage("editDialogMessaegs", "Create new account failed", "Account with username " + username + " already exists"); + } else { + accountService.saveAccount(currentAccount, currentAccountRoles); + selectAccount(); + if (currentAccount.getId() == null) { + // this was a new account + // 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; + deselectAccount(); + } 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 selectAccount() { + this.accountSelected = true; + } + + public void deselectAccount() { + this.accountSelected = false; + } + + public void showDisabledAccountsChange() { + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("show diabled accounts changed to {}", showDisabledAccounts); + } + this.accountList = null; + } + + public List getStatusList() { + return AccountStatus.getAllStatusNames(); + } + + /* **** getter / setter **** */ + public AccountEntity getCurrentAccount() { + return currentAccount; + } + + public void setCurrentAccount(AccountEntity currentAccount) { + this.currentAccount = currentAccount; + } + + public boolean isAccountSelected() { + return accountSelected; + } + + public void setAccountSelected(boolean accountSelected) { + this.accountSelected = accountSelected; + } + + 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; + } + +} diff --git a/account-ui/src/main/java/de/muehlencord/shared/account/web/presentation/ApplicationView.java b/account-ui/src/main/java/de/muehlencord/shared/account/web/presentation/ApplicationView.java new file mode 100644 index 0000000..86a0d69 --- /dev/null +++ b/account-ui/src/main/java/de/muehlencord/shared/account/web/presentation/ApplicationView.java @@ -0,0 +1,99 @@ +package de.muehlencord.shared.account.web.presentation; + +import de.muehlencord.shared.account.business.application.boundary.ApplicationService; +import de.muehlencord.shared.account.business.application.entity.ApplicationEntity; +import de.muehlencord.shared.jeeutil.FacesUtil; +import java.io.Serializable; +import java.util.List; +import javax.annotation.PostConstruct; +import javax.enterprise.context.SessionScoped; +import javax.inject.Inject; +import javax.inject.Named; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * + * @author Joern Muehlencord + */ +@Named(value = "applicationView") +@SessionScoped +public class ApplicationView implements Serializable { + + private static final long serialVersionUID = -5515249316880163539L; + private static final Logger LOGGER = LoggerFactory.getLogger(ApplicationView.class); + + @Inject + ApplicationService applicationService; + + private ApplicationEntity currentApplication = null; + private List applicationList = null; + private ApplicationEntity editApplication = null; + + @PostConstruct + public void selectDefaultCurrentApplication() { + // force applications to be loaded from database + getAllApplications(); + if ((applicationList != null) && (!applicationList.isEmpty())) { + currentApplication = applicationList.get(0); + } + } + + public List getAllApplications() { + if (applicationList == null) { + applicationList = applicationService.getAllApplications(); + } + return applicationList; + } + + public void selectApplication() { + if (currentApplication != null) { + LOGGER.info("selected application: {}", currentApplication.getApplicationName()); + FacesUtil.addGlobalInfoMessage("Success", "Selected application " + currentApplication.getApplicationName()); + } + } + + public void newApplication() { + this.editApplication = new ApplicationEntity(); + } + + public void cancelEditApplication() { + this.editApplication = null; + } + + public void saveEditApplication() { + if (editApplication == null) { + FacesUtil.addGlobalErrorMessage("Error", "Need to provide data"); + } else if ((editApplication.getApplicationName() == null) || (editApplication.getApplicationName().trim().equals(""))) { + String hint; + if (editApplication.getId() == null) { + hint = "Cannot create application"; + } else { + hint = "Cannot save application"; + } + FacesUtil.addGlobalErrorMessage(hint, "Application name must not be empty"); + } else { + currentApplication = applicationService.createOrUpdate(editApplication); + // force reload of to update view + applicationList = null; + FacesUtil.addGlobalInfoMessage("Info", "Application saved"); + } + } + + /* *** getter / setter *** */ + public ApplicationEntity getCurrentApplication() { + return currentApplication; + } + + public void setCurrentApplication(ApplicationEntity currentApplication) { + this.currentApplication = currentApplication; + } + + public ApplicationEntity getEditApplication() { + return editApplication; + } + + public void setEditApplication(ApplicationEntity editApplication) { + this.editApplication = editApplication; + } +} diff --git a/account-ui/src/main/java/de/muehlencord/shared/account/web/presentation/GroupView.java b/account-ui/src/main/java/de/muehlencord/shared/account/web/presentation/GroupView.java new file mode 100644 index 0000000..be3c456 --- /dev/null +++ b/account-ui/src/main/java/de/muehlencord/shared/account/web/presentation/GroupView.java @@ -0,0 +1,252 @@ +/* + * Copyright 2017 Joern Muehlencord . + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package de.muehlencord.shared.account.web.presentation; + +import de.muehlencord.shared.account.business.account.boundary.ApplicationRoleControl; +import de.muehlencord.shared.account.business.account.entity.AccountException; +import de.muehlencord.shared.account.business.account.entity.ApplicationPermissionEntity; +import de.muehlencord.shared.account.business.account.entity.ApplicationRoleEntity; +import de.muehlencord.shared.jeeutil.FacesUtil; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; +import javax.ejb.EJB; +import javax.enterprise.context.SessionScoped; +import javax.inject.Named; +import javax.validation.constraints.Size; +import org.primefaces.event.SelectEvent; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * + * @author Joern Muehlencord + */ +@SessionScoped +@Named +public class GroupView implements Serializable { + + private static final long serialVersionUID = 1669321020398119007L; + private static final Logger LOGGER = LoggerFactory.getLogger(GroupView.class); + + @EJB + ApplicationRoleControl applicationRoleControl; + + @Size(max = 80) + private String newRoleName; + @Size(max = 200) + private String newRoleDescription; + // flag to determine whether a role is selected or not + private boolean isPermissionSelected = false; + + private List allRoles = null; + private List currentRolePermissions = null; + private List missingApplicationsPermissions = null; + + private ApplicationRoleEntity currentRole; + private ApplicationPermissionEntity currentPermission; + private ApplicationPermissionEntity newPermission; + + public List getAllRoles() { + if (allRoles == null) { + allRoles = applicationRoleControl.getAllRoles(); + } + return allRoles; + } + + public void newRole() { + if ((newRoleName == null) || (newRoleName.trim().length() == 0)) { + FacesUtil.addGlobalErrorMessage("Error", "Permission name must not be null"); + } else if ((newRoleDescription == null) || (newRoleDescription.trim().length() == 0)) { + FacesUtil.addGlobalErrorMessage("Error", "Permission name must not be null"); + } else { + applicationRoleControl.createOrUpdate(newRoleName, newRoleDescription); + allRoles = null; // force reload + newRoleName = null; + newRoleDescription = null; + } + } + + public void editRole() { + if (currentRole == null) { + FacesUtil.addGlobalErrorMessage("Error", "Please select a permission to edit"); + } else { + allRoles = null; // force reload + newRoleName = currentRole.getRoleName(); + newRoleDescription = currentRole.getRoleDescription(); + } + } + + public void deleteRole() { + if (currentRole == null) { + FacesUtil.addGlobalErrorMessage("Error", "Please select a permission to edit"); + } else { + try { + applicationRoleControl.delete(currentRole); + allRoles = null; // force reload + currentRole = null; + currentRolePermissions = null; + } catch (AccountException ex) { + if (LOGGER.isDebugEnabled()) { + LOGGER.debug(ex.toString(), ex); + } else { + LOGGER.debug(ex.toString()); + } + FacesUtil.addGlobalErrorMessage("Error while deleting permission.", ex.toString()); + } + } + } + + public void onRoleSelect(SelectEvent event) { + currentRolePermissions = null; + currentRolePermissions = getRolePermissions(); + missingApplicationsPermissions = null; + missingApplicationsPermissions = getMissingPermissions(); + } + + public List getRolePermissions() { + if (currentRole == null) { + currentRolePermissions = new ArrayList<>(); + return currentRolePermissions; + } else { + if (currentRolePermissions == null) { + try { + currentRolePermissions = applicationRoleControl.getRolePermissions(currentRole); + } catch (AccountException ex) { + if (LOGGER.isDebugEnabled()) { + LOGGER.debug(ex.toString(), ex); + } else { + LOGGER.debug(ex.toString()); + } + FacesUtil.addGlobalErrorMessage("Error while fetching role permissions", "see log for details"); + currentRolePermissions = new ArrayList<>(); + } + } + return currentRolePermissions; + } + } + + public List getMissingPermissions() { + if (currentRole == null) { + missingApplicationsPermissions = new ArrayList<>(); + return missingApplicationsPermissions; + } else { + if (missingApplicationsPermissions == null) { + missingApplicationsPermissions = applicationRoleControl.getNotAssignedApplicationPermissions(currentRole); + + } + return missingApplicationsPermissions; + } + } + + public void addRolePermission() { + if (newPermission == null) { + FacesUtil.addGlobalErrorMessage("Error", "Please select a new permission first"); + } else { + try { + applicationRoleControl.addPermission(currentRole, newPermission); + currentRolePermissions = null; + missingApplicationsPermissions = null; + } catch (AccountException ex) { + if (LOGGER.isDebugEnabled()) { + LOGGER.debug(ex.toString(), ex); + } else { + LOGGER.debug(ex.toString()); + } + FacesUtil.addGlobalErrorMessage("Error while adding permission", ex.getMessage()); + } + } + + } + + public void removeRolePermission() { + if (currentPermission == null) { + FacesUtil.addGlobalErrorMessage("Error", "Please select a permission first"); + } else { + try { + applicationRoleControl.removePermission(currentRole, currentPermission); + currentRolePermissions = null; + missingApplicationsPermissions = null; + } catch (AccountException ex) { + if (LOGGER.isDebugEnabled()) { + LOGGER.debug(ex.toString(), ex); + } else { + LOGGER.debug(ex.toString()); + } + FacesUtil.addGlobalErrorMessage("Error while adding permission", ex.getMessage()); + } + } + + } + + public void selectPermission() { + this.isPermissionSelected = true; + } + + public void deselectPermission() { + this.isPermissionSelected = false; + } + + /* *** getter / setter *** */ + public ApplicationRoleEntity getCurrentRole() { + return currentRole; + } + + public void setCurrentRole(ApplicationRoleEntity currentRole) { + this.currentRole = currentRole; + } + + public ApplicationPermissionEntity getCurrentPermission() { + return currentPermission; + } + + public void setCurrentPermission(ApplicationPermissionEntity currentPermission) { + this.currentPermission = currentPermission; + } + + public String getNewRoleName() { + return newRoleName; + } + + public void setNewRoleName(String newRoleName) { + this.newRoleName = newRoleName; + } + + public String getNewRoleDescription() { + return newRoleDescription; + } + + public void setNewRoleDescription(String newRoleDescription) { + this.newRoleDescription = newRoleDescription; + } + + public ApplicationPermissionEntity getNewPermission() { + return newPermission; + } + + public void setNewPermission(ApplicationPermissionEntity newPermission) { + this.newPermission = newPermission; + } + + public boolean isIsPermissionSelected() { + return isPermissionSelected; + } + + public void setIsPermissionSelected(boolean isPermissionSelected) { + this.isPermissionSelected = isPermissionSelected; + } + +} diff --git a/account-ui/src/main/java/de/muehlencord/shared/account/web/presentation/InstanceView.java b/account-ui/src/main/java/de/muehlencord/shared/account/web/presentation/InstanceView.java new file mode 100644 index 0000000..0a3e436 --- /dev/null +++ b/account-ui/src/main/java/de/muehlencord/shared/account/web/presentation/InstanceView.java @@ -0,0 +1,46 @@ +package de.muehlencord.shared.account.web.presentation; + +import de.muehlencord.shared.account.business.config.boundary.ConfigService; +import de.muehlencord.shared.account.business.config.entity.ConfigException; +import javax.ejb.EJB; +import javax.enterprise.context.ApplicationScoped; +import javax.inject.Named; +import org.slf4j.LoggerFactory; + +/** + * + * @author Joern Muehlencord + */ +@Named(value = "instanceView") +@ApplicationScoped +public class InstanceView { + + private static final org.slf4j.Logger LOGGER = LoggerFactory.getLogger(InstanceView.class); + + @EJB + ConfigService configService; + + public boolean isDevelopmentVersion() { + String instanceName = getInstanceName(); + return !instanceName.equals("Production"); + } + + public String getInstanceName() { + String instanceName; + try { + instanceName = configService.getConfigValue("base.instance"); + } catch (ConfigException ex) { + if (LOGGER.isDebugEnabled()) { + LOGGER.debug(ex.toString(), ex); + } else { + LOGGER.error(ex.toString()); + } + instanceName = "unknown (" + ex.toString() + ")"; + } + if (instanceName == null) { + return "unknown"; + } else { + return instanceName; + } + } +} diff --git a/account-ui/src/main/java/de/muehlencord/shared/account/web/presentation/PermissionView.java b/account-ui/src/main/java/de/muehlencord/shared/account/web/presentation/PermissionView.java new file mode 100644 index 0000000..6d54332 --- /dev/null +++ b/account-ui/src/main/java/de/muehlencord/shared/account/web/presentation/PermissionView.java @@ -0,0 +1,167 @@ +/* + * Copyright 2017 Joern Muehlencord . + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package de.muehlencord.shared.account.web.presentation; + +import de.muehlencord.shared.account.business.account.boundary.ApplicationPermissionControl; +import de.muehlencord.shared.account.business.account.entity.AccountException; +import de.muehlencord.shared.account.business.account.entity.ApplicationPermissionEntity; +import de.muehlencord.shared.jeeutil.FacesUtil; +import java.io.Serializable; +import java.util.List; +import javax.ejb.EJB; +import javax.enterprise.context.SessionScoped; +import javax.inject.Named; +import javax.validation.constraints.Size; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * + * @author Joern Muehlencord + */ +@SessionScoped +@Named +public class PermissionView implements Serializable { + + private static final long serialVersionUID = -1469453490360990772L; + private static final Logger LOGGER = LoggerFactory.getLogger(PermissionView.class); + + @EJB + ApplicationPermissionControl applicationPermissionService; + + @Size(max = 80) + private String newPermissionName; + + @Size(max = 200) + private String newPermissionDescription; + + private ApplicationPermissionEntity currentPermission; + + // flag if a permission is selected - used to control button state + private boolean permissionSelected; + + public List getAppPermissions() { + return applicationPermissionService.getApplicationPermissions(); + } + + public void savePermission() { + if ((newPermissionName == null) || (newPermissionName.trim().length() == 0)) { + FacesUtil.addGlobalErrorMessage("Error", "Permission name must not be null"); + } else if ((newPermissionDescription == null) || (newPermissionDescription.trim().length() == 0)) { + FacesUtil.addGlobalErrorMessage("Error", "Permission name must not be null"); + } else { + applicationPermissionService.createOrUpdate(newPermissionName, newPermissionDescription); + FacesUtil.addGlobalInfoMessage("Pemissin updated", "Permission "+newPermissionName+" updated"); + newPermissionName = null; + newPermissionDescription = null; + deselectPermission(); + } + } + + public void editPermission() { + if (currentPermission == null) { + FacesUtil.addGlobalErrorMessage("Error", "Please select a permission to edit"); + } else { + newPermissionName = currentPermission.getPermissionName(); + newPermissionDescription = currentPermission.getPermissionDescription(); + } + } + + public void deletePermission() { + if (currentPermission == null) { + FacesUtil.addGlobalErrorMessage("Error", "Please select a permission to edit"); + } else { + try { + applicationPermissionService.delete(currentPermission); + currentPermission = null; + } catch (AccountException ex) { + if (LOGGER.isDebugEnabled()) { + LOGGER.debug(ex.toString(), ex); + } else { + LOGGER.debug(ex.toString()); + } + FacesUtil.addGlobalErrorMessage("Error while deleting permission.", ex.toString()); + } + } + } + + public void selectPermission() { + this.permissionSelected = true; + } + + public void deselectPermission() { + this.permissionSelected = false; + this.newPermissionName = null; + this.newPermissionDescription = null; + } + + + public boolean getCanEdit() { + if (!isPermissionSelected()) { + return false; + } + return true; + } + + public boolean getCanDelete() { + if (!isPermissionSelected()) { + return false; + } + if (newPermissionName == null) { + return true; + } + return false; + } + + /* *** getter / setter *** */ + public ApplicationPermissionEntity getCurrentPermission() { + return currentPermission; + } + + public void setCurrentPermission(ApplicationPermissionEntity newCurrentPermission) { + if ((currentPermission == null) || (newCurrentPermission == null) || (!currentPermission.equals(newCurrentPermission))) { + this.newPermissionName = null; + this.newPermissionDescription = null; + } + this.currentPermission = newCurrentPermission; + + } + + public String getNewPermissionName() { + return newPermissionName; + } + + public void setNewPermissionName(String newPermissionName) { + this.newPermissionName = newPermissionName; + } + + public String getNewPermissionDescription() { + return newPermissionDescription; + } + + public void setNewPermissionDescription(String newPermissionDescription) { + this.newPermissionDescription = newPermissionDescription; + } + + public boolean isPermissionSelected() { + return permissionSelected; + } + + public void setPermissionSelected(boolean permissionSelected) { + this.permissionSelected = permissionSelected; + } + +} diff --git a/account-ui/src/main/java/de/muehlencord/shared/account/web/presentation/UniqueApplicationValidator.java b/account-ui/src/main/java/de/muehlencord/shared/account/web/presentation/UniqueApplicationValidator.java new file mode 100644 index 0000000..19da8cb --- /dev/null +++ b/account-ui/src/main/java/de/muehlencord/shared/account/web/presentation/UniqueApplicationValidator.java @@ -0,0 +1,36 @@ +package de.muehlencord.shared.account.web.presentation; + +import java.io.Serializable; +import javax.faces.component.UIComponent; +import javax.faces.context.FacesContext; +import javax.faces.validator.FacesValidator; +import javax.faces.validator.Validator; +import javax.faces.validator.ValidatorException; +import javax.inject.Inject; +import javax.persistence.EntityManager; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * + * @author Joern Muehlencord + */ +@FacesValidator("uniqueApplicationValidator") +public class UniqueApplicationValidator implements Validator, Serializable { + + private static final long serialVersionUID = 2526409681909574670L; + private static final Logger LOGGER = LoggerFactory.getLogger(UniqueApplicationValidator.class); + + @Inject + EntityManager em; + + @Override + public void validate(FacesContext context, UIComponent component, Object value) throws ValidatorException { + String name = (String) value; + LOGGER.info("Name = {}", name) ; + } + + + + +} diff --git a/account-ui/src/main/resources/META-INF/persistence.xml b/account-ui/src/main/resources/META-INF/persistence.xml new file mode 100644 index 0000000..504dc3b --- /dev/null +++ b/account-ui/src/main/resources/META-INF/persistence.xml @@ -0,0 +1,22 @@ + + + + java:/jboss/accountTestDs + de.muehlencord.shared.account.business.account.entity.AccountEntity + de.muehlencord.shared.account.business.account.entity.AccountHistoryEntity + de.muehlencord.shared.account.business.account.entity.ApplicationPermissionEntity + de.muehlencord.shared.account.business.account.entity.ApplicationRoleEntity + de.muehlencord.shared.account.business.config.entity.ConfigEntity + de.muehlencord.shared.account.business.mail.entity.MailTemplateEntity + de.muehlencord.shared.account.business.application.entity.ApplicationEntity + true + ENABLE_SELECTIVE + NONE + + + + + + + + diff --git a/account-ui/src/main/resources/admin-config.properties b/account-ui/src/main/resources/admin-config.properties new file mode 100644 index 0000000..d3b40dd --- /dev/null +++ b/account-ui/src/main/resources/admin-config.properties @@ -0,0 +1,32 @@ +admin.loginPage=/login.xhtml +admin.indexPage=/web/account.xhtml +#admin.dateFormat= +#admin.breadcrumbSize=5 +admin.renderMessages=true +#admin.renderAjaxStatus=true +## disable filter to redirect to login page - shiro security filter is already doing this +admin.disableFilter=true +#admin.renderBreadCrumb=true +#admin.enableSlideMenu=true +#admin.enableRipple=true +#admin.rippleElements= .ripplelink,button.ui-button,.ui-selectlistbox-item,.ui-multiselectlistbox-item,.ui-selectonemenu-label,.ui-selectcheckboxmenu,\ +#.ui-autocomplete-dropdown, .ui-autocomplete-item ... (the list goes on) +admin.skin=skin-purple-light +#admin.autoShowNavbar=true +#admin.ignoredResources= +#admin.loadingImage=ajaxloadingbar.gif +#admin.extensionLessUrls=false +admin.renderControlSidebar=false +#admin.controlSidebar.showOnMobile=false +#admin.controlSidebar.leftMenuTemplate=true +#admin.controlSidebar.fixedLayout=false +#admin.controlSidebar.boxedLayout=false +#admin.controlSidebar.sidebarCollapsed=false +#admin.controlSidebar.expandOnHover=false +#admin.controlSidebar.fixed=false +#admin.controlSidebar.darkSkin=true +#admin.rippleMobileOnly=true +admin.renderMenuSearch=false +## do not autohide +admin.autoHideMessages=false +#admin.messagesHideTimeout=2500 \ No newline at end of file diff --git a/account-ui/src/main/resources/buildInfo.properties b/account-ui/src/main/resources/buildInfo.properties new file mode 100644 index 0000000..37404b0 --- /dev/null +++ b/account-ui/src/main/resources/buildInfo.properties @@ -0,0 +1,2 @@ +build.version=${project.version} +build.timestamp=${timestamp} \ No newline at end of file diff --git a/account-ui/src/main/resources/de/muehlencord/shared/account/web/presentation/messages.properties b/account-ui/src/main/resources/de/muehlencord/shared/account/web/presentation/messages.properties new file mode 100644 index 0000000..ae11f3c --- /dev/null +++ b/account-ui/src/main/resources/de/muehlencord/shared/account/web/presentation/messages.properties @@ -0,0 +1,131 @@ +header_login=Login +header_reset_password=Reset password +message_username_password=Please enter your user name and a new password +button_login=Login +button_cancel=Cancel +button_password_lost=Password lost? +label_username=Username +label_password=Password +label_new_password=New Password +button_password_reset=Reset password +header_passwort_lost=Lost password +message_start_password_reset=Please enter your username to start the password recovery procedure +menu_dashboard=Dashboard +menu_events=Events +menu_administration=Administration +menu_overview=Overview +menu_emails=Emails +menu_account=Account +menu_config=Config +menu_logout=Logout +button_new=New +button_delete=Delete +button_edit=Edit +button_reload=Reload +label_name=Name +label_description=Description +label_event_date=Event Date +label_reservation=Reservation +label_reservation_from_to=Reservation from/to +label_actions=Actions +message_confirm=Are you sure? +button_setup=Setup +button_reservations=Reservations +label_event_name=Event Name +label_event_item_desc=Event Item Description +label_timezone=Timezone +label_event_start=Event Start +label_event_end=Event End +label_reservation_autostart=Reservation start +label_reservation_autoend=Reservation end +label_reservation_active=Reservation active +label_reservation_max_items=Max Items +label_booking_deadline=Booking deadline +label_template_validation=Email validation mail template +label_template_confirmation=Event confirmation mail template +label_template_waitlist=Event waitlist mail template +message_event_not_found=Event not found +label_items=Items +label_costs=Costs +label_all=All +label_yes=Yes +label_no=No +label_status=Status +label_firstname=Firstname +label_lastname=Lastname +label_emailaddress=Emailaddress +label_comment=Comment +label_email_confirmed=Email confirmed +label_booking_number=Booking Number +label_booking_executed=booking executed +tt_log_entries=Show log entries +tt_cancel_reservation=Cancel the current reservation +tt_send_email_again=Send email again +tt_move_from_wl=Move from waitlist +tt_fix_reservation=Try to fix the current reservation +tt_edit_reservation=Edit the reservation +button_refresh_free=Refresh free +button_manual_reserve=Reserve +button_export=Export +label_amount=Amount +label_select=Select +label_created_by=Created by +label_ip=IP +label_ip_forwarded=IP (forwarded) +label_value=Value +label_useragent=Useragent +button_ok=Ok +message_comment=Please add a comment +menu_help=Help +label_event=Event +label_active=Active +label_waitlist=Waitlist +label_is_waitlist=Is waitlist +label_order=Order +message_dynamic_numbering=dynamic numbering (put %n as placeholder) +label_start_number=Start Number +label_end_number=End Number +header_item_def=Define items for event +message_no_event_items=No event item defined +button_overview=Overview +label_reservation_auto_start=Automatically switch on/off +label_item_public=Item public? +label_is_publicitem=Is public +label_customer_comment=Customer comment +label_change_comment=Change comment +label_existing_items=current items +label_new_items=new items +label_available_items=available items +label_no_records=No records found. +button_mail=Mail +header_email_distribution=Email distribution +label_template=Template +label_demomode=Demo mode +message_invalid_email=Please provide a valid email address +menu_permissions=Permissions +button_save=Save +menu_groups=Groups +message_email_sent=email sent +message_email_with_error=emails with error +message_no_email=no email address defined +message_email_not_sent=Error while sending emails +label_seating=Seating +label_attachments=Attachments +label_language=Language +label_subject=Subject +label_bytes=Bytes +label_upload=Upload +header_export=Export +label_export_type=Export Type +label_filtered=Filtered +label_include_deleted=Include deleted +label_include_log=Include Logs +label_template_booking_executed=Booking executed template +label_street=Street +label_zipCode=ZIP Code +label_city=City +label_groupName=Groupname +label_phoneNumber=Phone Number +label_template_waitlist_cancelled=Waitlist cancelled mail template +msgs_menu_status=Status +menu_status=Status diff --git a/account-ui/src/main/resources/de/muehlencord/shared/account/web/presentation/messages_de.properties b/account-ui/src/main/resources/de/muehlencord/shared/account/web/presentation/messages_de.properties new file mode 100644 index 0000000..ac1af1a --- /dev/null +++ b/account-ui/src/main/resources/de/muehlencord/shared/account/web/presentation/messages_de.properties @@ -0,0 +1,132 @@ + +header_login=Anmeldung +header_reset_password=Passwort zur\u00fccksetzten +message_username_password=Bitte geben Deinen Benutzernamen und dein Passwort ein +button_login=Anmelden +button_cancel=Abbruch +button_password_lost=Passwort vergessen? +label_username=Benutzername +label_password=Passwort +label_new_password=Neues Passwort +button_password_reset=Passwort zur\u00fccksetzten +header_passwort_lost=Passwort vergessen +message_start_password_reset=Bitte gib deinen Benutzernamen ein um das Zur\u00fccksetzten des Passworts zu starten. +menu_dashboard=Dashbaord +menu_events=Veranstaltungen +menu_administration=Administration +menu_overview=\u00dcbersicht +menu_emails=E-Mails +menu_account=Benutzer +menu_config=Konfiguration +menu_logout=Abmelden +button_new=Neu +button_delete=L\u00f6schen +button_edit=Bearbeiten +button_reload=Aktualisieren +label_name=Name +label_description=Beschreibung +label_event_date=Veranstaltungsdatum +label_reservation=Reservierung +label_reservation_from_to=Reservierung von/bis +label_actions=Aktionen +message_confirm=Bist du sicher? +button_setup=Setup +button_reservations=Reservierungen +label_event_name=Veranstaltungsname +label_event_item_desc=Veranstaltungsobjekte +label_timezone=Zeitzone +label_event_start=Veranstaltungsbeginn +label_event_end=Veranstaltungsende +label_reservation_autostart=Reservierung von +label_reservation_autoend=Reservierung bis +label_reservation_active=Reservierung aktiv +label_reservation_max_items=Maximale Objekte +label_booking_deadline=Buchungsfrist +label_template_validation=Vorlage Emailvalidierung +label_template_confirmation=Vorlage Reservierungsbest\u00e4tigung +label_template_waitlist=Vorlage Wartelist +message_event_not_found=Veranstaltung nicht gefunden +label_items=Objekte +label_costs=Kosten +label_all=Alle +label_yes=Ja +label_no=Nein +label_status=Status +label_firstname=Vorname +label_lastname=Nachname +label_emailaddress=E-Mail-Adresse +label_comment=Kommentar +label_email_confirmed=E-Mail best\u00e4tigt +label_booking_number=Buchungsnummer +label_booking_executed=Buchungs ausgef\u00fchrt +tt_log_entries=Logbucheintr\u00e4ge +tt_cancel_reservation=Storniere die Reservierung +tt_send_email_again=Sende E-Mail erneut +tt_move_from_wl=Schiebe von Warteliste +tt_fix_reservation=Versuche den Fehler zu beheben +tt_edit_reservation=Bearbeite die Reservierung +button_refresh_free=Frei aktualisieren +button_manual_reserve=Reservieren +button_export=Exportieren +label_amount=Anzahl +label_select=W\u00e4hlen +label_created_by=Erzeugt durch +label_ip=IP +label_ip_forwarded=IP (forwarded) +label_value=Wert +label_useragent=Useragent +button_ok=Ok +message_comment=Bitte geben Sie einen Kommentar an +menu_help=Hilfe +label_event=Veranstaltung +label_active=Aktiv +label_waitlist=Warteliste +label_is_waitlist=Ist Warteliste +label_order=Reihenfolge +message_dynamic_numbering=Dynamische Nummerierung (%n als Platzhalter) +label_start_number=Startnummer +label_end_number=Endnummer +header_item_def=Objekte f\u00fcr Veranstaltung +message_no_event_items=Keine Objekte definiert +button_overview=\u00dcbersicht +label_reservation_auto_start=Automatisch ein/ausschalten +label_item_public=\u00d6ffentlich verf\u00fcgar? +label_is_publicitem=\u00d6ffentlich +label_customer_comment=Kundenkommentar +label_change_comment=\u00c4nderungskommentar +label_existing_items=aktuelle Objekte +label_new_items=neue Objekte +label_available_items=verf\u00fcgbare Objekte +label_no_records=Keine Daten gefunden. +button_mail=E-Mail +header_email_distribution=Emailversand +label_template=Vorlage +label_demomode=Demomodus +message_invalid_email=Bitte geben Sie eine g\u00fcltige Emailadresse an +menu_permissions=Berechtigungen +button_save=Speichern +menu_groups=Gruppen +message_email_sent=Email gesendet +message_email_with_error=Emails mit Fehler +message_no_email=keine Emailadresse verf\u00fcgbar +message_email_not_sent=Fehler beim Versenden der Email +label_seating=Saalplan Platz +label_attachments=Attachments +label_language=Sprache +label_subject=Betreff +label_bytes=Bytes +label_upload=Upload +header_export=Exportieren +label_export_type=Export Art +label_filtered=Gefiltert +label_include_deleted=Einschlie\u00dflich gel\u00f6scht +label_include_log=mit Logbuch +label_template_booking_executed=Vorlage Buchung durchgef\u00fchrt +label_street=Stra\u00dfe +label_zipCode=PLZ +label_city=Ort +label_groupName=Gruppenname +label_phoneNumber=Telefonnummer +label_template_waitlist_cancelled=Vorlage Warteliste Abbruch +msgs_menu_status=Status +menu_status=Status diff --git a/account-ui/src/main/resources/de/muehlencord/shared/account/web/presentation/messages_en.properties b/account-ui/src/main/resources/de/muehlencord/shared/account/web/presentation/messages_en.properties new file mode 100644 index 0000000..382fcb1 --- /dev/null +++ b/account-ui/src/main/resources/de/muehlencord/shared/account/web/presentation/messages_en.properties @@ -0,0 +1,132 @@ + +header_login=Login +header_reset_password=Reset password +message_username_password=Please enter your user name and a new password +button_login=Login +button_cancel=Cancel +button_password_lost=Password lost? +label_username=Username +label_password=Password +label_new_password=New Password +button_password_reset=Reset password +header_passwort_lost=Lost password +message_start_password_reset=Please enter your username to start the password recovery procedure +menu_dashboard=Dashboard +menu_events=Events +menu_administration=Administration +menu_overview=Overview +menu_emails=Emails +menu_account=Account +menu_config=Config +menu_logout=Logout +button_new=New +button_delete=Delete +button_edit=Edit +button_reload=Reload +label_name=Name +label_description=Description +label_event_date=Event Date +label_reservation=Reservation +label_reservation_from_to=Reservation from/to +label_actions=Actions +message_confirm=Are you sure? +button_setup=Setup +button_reservations=Reservations +label_event_name=Event Name +label_event_item_desc=Event Item Description +label_timezone=Timezone +label_event_start=Event Start +label_event_end=Event End +label_reservation_autostart=Reservation Start +label_reservation_autoend=Reservation End +label_reservation_active=Reservation active +label_reservation_max_items=Max Items +label_booking_deadline=Booking deadline +label_template_validation=Email validation mail template +label_template_confirmation=Event confirmation mail template +label_template_waitlist=Event waitlist mail template +message_event_not_found=Event not found +label_items=Items +label_costs=Costs +label_all=All +label_yes=Yes +label_no=No +label_status=Status +label_firstname=Firstname +label_lastname=Lastname +label_emailaddress=Emailaddress +label_comment=Comment +label_email_confirmed=Email confirmed +label_booking_number=Booking Number +label_booking_executed=booking executed +tt_log_entries=Show log entries +tt_cancel_reservation=Cancel the current reservation +tt_send_email_again=Send email again +tt_move_from_wl=Move from waitlist +tt_fix_reservation=Try to fix the current reservation +tt_edit_reservation=Edit the reservation +button_refresh_free=Refresh free +button_manual_reserve=Reserve +button_export=Export +label_amount=Amount +label_select=Select +label_created_by=Created by +label_ip=IP +label_ip_forwarded=IP (forwarded) +label_value=Value +label_useragent=Useragent +button_ok=Ok +message_comment=Please add a comment +menu_help=Help +label_event=Event +label_active=Active +label_waitlist=Waitlist +label_is_waitlist=Is waitlist +label_order=Reservation Order +message_dynamic_numbering=dynamic numbering (put %n as placeholder) +label_start_number=Start Number +label_end_number=End Number +header_item_def=Define items for event +message_no_event_items=No event item defined +button_overview=Overview +label_reservation_auto_start=Automatically switch on/off +label_item_public=Item public? +label_is_publicitem=Is public +label_customer_comment=Customer comment +label_change_comment=Change comment +label_existing_items=current items +label_new_items=new items +label_available_items=available items +label_no_records=No records found. +button_mail=Mail +header_email_distribution=Email distribution +label_template=Template +label_demomode=Demo mode +message_invalid_email=Please provide a valid email address +menu_permissions=Permissions +button_save=Save +menu_groups=Groups +message_email_sent=email sent +message_email_with_error=emails with error +message_no_email=no email address defined +message_email_not_sent=Error while sending emails +label_seating=Seating +label_attachments=Anh\u00e4nge +label_language=Language +label_subject=Subject +label_bytes=Bytes +label_upload=Hochladen +header_export=Export +label_export_type=Export Type +label_filtered=Filtered +label_include_deleted=Include deleted +label_include_log=Include Logs +label_template_booking_executed=Booking executed template +label_street=Street +label_zipCode=ZIP Code +label_city=City +label_groupName=Groupname +label_phoneNumber=Phone Number +label_template_waitlist_cancelled=Waitlist cancelled mail template +msgs_menu_status=Status +menu_status=Status diff --git a/account-ui/src/main/webapp/WEB-INF/beans.xml b/account-ui/src/main/webapp/WEB-INF/beans.xml new file mode 100644 index 0000000..b14f778 --- /dev/null +++ b/account-ui/src/main/webapp/WEB-INF/beans.xml @@ -0,0 +1,7 @@ + + + diff --git a/account-ui/src/main/webapp/WEB-INF/faces-config.xml b/account-ui/src/main/webapp/WEB-INF/faces-config.xml new file mode 100644 index 0000000..d2a8197 --- /dev/null +++ b/account-ui/src/main/webapp/WEB-INF/faces-config.xml @@ -0,0 +1,26 @@ + + + + + org.primefaces.application.exceptionhandler.PrimeExceptionHandlerELResolver + + + en + en + de + + + + de.muehlencord.shared.account.web.presentation.messages + msgs + + + + org.omnifaces.exceptionhandler.FullAjaxExceptionHandlerFactory + + + + diff --git a/account-ui/src/main/webapp/WEB-INF/shiro.ini b/account-ui/src/main/webapp/WEB-INF/shiro.ini new file mode 100644 index 0000000..79e678d --- /dev/null +++ b/account-ui/src/main/webapp/WEB-INF/shiro.ini @@ -0,0 +1,57 @@ +[main] +cacheManager = org.apache.shiro.cache.MemoryConstrainedCacheManager +securityManager.cacheManager = $cacheManager + +# DataSource Setup +datasource = org.apache.shiro.jndi.JndiObjectFactory +datasource.resourceName = java:/jboss/accountTestDs +# TODO - change to accountDs +datasource.resourceRef = true + +# HashService +hashService = org.apache.shiro.crypto.hash.DefaultHashService +hashService.hashIterations = 500000 +hashService.hashAlgorithmName = SHA-512 +hashService.generatePublicSalt = true + +# Password service +passwordService = org.apache.shiro.authc.credential.DefaultPasswordService +passwordService.hashService = $hashService + +# Required password matcher +passwordMatcher = org.apache.shiro.authc.credential.PasswordMatcher +passwordMatcher.passwordService = $passwordService + +# JDBC Realm setup +jdbcRealm = org.apache.shiro.realm.jdbc.JdbcRealm +jdbcRealm.permissionsLookupEnabled=false +jdbcRealm.authenticationQuery = select account_password from account where username = ? and status not in ('LOCKED','DELETED') +jdbcRealm.userRolesQuery = select r.role_name from application_role r, account_role ar, account a WHERE a.username = ? AND a.id = ar.account AND ar.account_role = r.id +jdbcRealm.credentialsMatcher = $passwordMatcher +jdbcRealm.dataSource = $datasource + +# Activate realms +authcStrategy = org.apache.shiro.authc.pam.AllSuccessfulStrategy +securityManager.realms = $jdbcRealm +securityManager.authenticator.authenticationStrategy = $authcStrategy + +# Setup authentication filter +authc = de.muehlencord.shirofaces.filter.FacesAjaxAwarePassThruAuthenticationFilter +authc.loginUrl = /login.xhtml +authc.successUrl = /web/account.xhtml + +roles.unauthorizedUrl = /error/accessDenied.xhtml + +# +# filter setup +# +[urls] +/public/**=anon +/resources/**=anon +/fonts/**=anon +/javax.faces.resource/**=anon +/=anon +/index.html=anon +/login.xhtml=authc +/logout.xhtml=logout +/web/**=authc diff --git a/account-ui/src/main/webapp/WEB-INF/web.xml b/account-ui/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 0000000..6fb534b --- /dev/null +++ b/account-ui/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,123 @@ + + + reservation-system-backed + + javax.faces.PROJECT_STAGE + ${jsf.projectStage} + + + javax.faces.FACELETS_BUFFER_SIZE + 1048576 + + + primefaces.THEME + admin + + + primefaces.FONT_AWESOME + true + + + primefaces.MOVE_SCRIPTS_TO_BOTTOM + true + + + FacesServlet + javax.faces.webapp.FacesServlet + 1 + + + FacesServlet + *.xhtml + + + + 30 + + + + web/account.xhtml + + + + org.apache.shiro.web.env.EnvironmentLoaderListener + + + ShiroFilter + shiroFilter + org.apache.shiro.web.servlet.ShiroFilter + + + shiroFilter + /* + REQUEST + FORWARD + INCLUDE + ERROR + + + + FacesExceptionFilter + facesExceptionFilter + org.omnifaces.filter.FacesExceptionFilter + + + facesExceptionFilter + FacesServlet + + + + contentSecurityFilter + de.muehlencord.sf.filter.ContentSecurityPolicyFilter + + report-only + false + + + default-src + 'none' + + + img-src + 'self' + + + script-src + 'self' 'unsafe-inline' 'unsafe-eval' + + + style-src + 'self' 'unsafe-inline' + + + connect-src + 'self' + + + font-src + 'self' + + + object-src + 'none' + + + media-src + 'none' + + + child-src + 'none' + + + + + OwaspStandardFilter + owaspStandardFilter + de.muehlencord.sf.filter.OwaspStandardFilter + + + owaspStandardFilter + /* + + diff --git a/account-ui/src/main/webapp/login.xhtml b/account-ui/src/main/webapp/login.xhtml new file mode 100644 index 0000000..3cfb3fe --- /dev/null +++ b/account-ui/src/main/webapp/login.xhtml @@ -0,0 +1,99 @@ + + + + + Login Page + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/account-ui/src/main/webapp/logout.xhtml b/account-ui/src/main/webapp/logout.xhtml new file mode 100644 index 0000000..809ae3d --- /dev/null +++ b/account-ui/src/main/webapp/logout.xhtml @@ -0,0 +1,15 @@ + + + + + Logout + + + +

You are logged out.

+
+ +
\ No newline at end of file diff --git a/account-ui/src/main/webapp/resources/composite/confirmationDialog.xhtml b/account-ui/src/main/webapp/resources/composite/confirmationDialog.xhtml new file mode 100644 index 0000000..0181029 --- /dev/null +++ b/account-ui/src/main/webapp/resources/composite/confirmationDialog.xhtml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + diff --git a/account-ui/src/main/webapp/resources/css/admin.css b/account-ui/src/main/webapp/resources/css/admin.css new file mode 100644 index 0000000..771d6f3 --- /dev/null +++ b/account-ui/src/main/webapp/resources/css/admin.css @@ -0,0 +1,18 @@ +/* +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. +*/ +/* + Created on : 24.10.2018, 19:31:57 + Author : Joern Muehlencord +*/ + +.watermark { + position: absolute; + opacity: 0.25; + font-size: 3em; + width: 100%; + text-align: center; + z-index: 1000; +} \ No newline at end of file diff --git a/account-ui/src/main/webapp/resources/template/footer.xhtml b/account-ui/src/main/webapp/resources/template/footer.xhtml new file mode 100644 index 0000000..906ba4a --- /dev/null +++ b/account-ui/src/main/webapp/resources/template/footer.xhtml @@ -0,0 +1,15 @@ + + + + + + Account Management + + + + © Joern Muehlencord - Account Management - version ${applicationController.version} - build date ${applicationController.buildDate} + + + diff --git a/account-ui/src/main/webapp/resources/template/leftmenu.xhtml b/account-ui/src/main/webapp/resources/template/leftmenu.xhtml new file mode 100644 index 0000000..99bfdc8 --- /dev/null +++ b/account-ui/src/main/webapp/resources/template/leftmenu.xhtml @@ -0,0 +1,46 @@ + + + + + + + + diff --git a/account-ui/src/main/webapp/resources/template/template.xhtml b/account-ui/src/main/webapp/resources/template/template.xhtml new file mode 100644 index 0000000..d8e2034 --- /dev/null +++ b/account-ui/src/main/webapp/resources/template/template.xhtml @@ -0,0 +1,50 @@ + + + + + + Product Catalog Designer + + + + + + Account Management + + + + AM + + + + + + + + + + + + + + + + diff --git a/account-ui/src/main/webapp/web/account.xhtml b/account-ui/src/main/webapp/web/account.xhtml new file mode 100644 index 0000000..ce0a7ba --- /dev/null +++ b/account-ui/src/main/webapp/web/account.xhtml @@ -0,0 +1,251 @@ + + + + + Account Overview + + + + List all accounts + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+
+ +
+ + +
+
+ + +
+
+ + + + +
+
+
+ + +
+ + + + + + + + +
+
+ +
+
+ + + + + + +
+
+ +
+ +
+ +
+
+ +
+
+ +
+ +
+ +
+
+ +
+
+ +
+ +
+ +
+
+ + + +
+
+ +
+ + + +
+ +
+
+ + + +
+
+ +
+ +
+ +
+
+ +
+
+ +
+ +
+ +
+
+ +
+
+ +
+ +
+ +
+
+ +
+
+ +
+ +
+ +
+
+ +
+
+ +
+ +
+ +
+
+ +
+
+ +
+
+ + +
+ +
+
+ + + + + + +
+
+ +
+ + +
+ + +
+
+ + +
+
+
+
+ +
+ +
\ No newline at end of file diff --git a/account-ui/src/main/webapp/web/index.xhtml b/account-ui/src/main/webapp/web/index.xhtml new file mode 100644 index 0000000..2c2dbcc --- /dev/null +++ b/account-ui/src/main/webapp/web/index.xhtml @@ -0,0 +1,76 @@ + + + + + Applications + + + + + + +
+
+ + + +
+
+ +
+
+ +
+
+
+
+ + + + + + + +
+
+ +
+
+ + + + +
+
+ +
+ +
+ + +
+
+ + +
+ + +
+
+
+ +
+
\ No newline at end of file diff --git a/account-ui/src/main/webapp/web/permissions.xhtml b/account-ui/src/main/webapp/web/permissions.xhtml new file mode 100644 index 0000000..1f0327f --- /dev/null +++ b/account-ui/src/main/webapp/web/permissions.xhtml @@ -0,0 +1,73 @@ + + + + + Permissions + + + + Edit permissions + + + + + +
+
+
+ + +
+
+
+
+ + +
+
+
+ +
+
+ +
+
+ + + +
+
+ + + + + + + + + + + + + + +
+
+ + + +
+ +
diff --git a/account-ui/src/main/webapp/web/roles.xhtml b/account-ui/src/main/webapp/web/roles.xhtml new file mode 100644 index 0000000..1471eb0 --- /dev/null +++ b/account-ui/src/main/webapp/web/roles.xhtml @@ -0,0 +1,96 @@ + + + + + Group Overview + + + + List all groups + + + + + +
+
+
+ + +
+
+
+
+ + +
+
+
+ +
+
+ +
+
+ + + +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+ + + +
+
+
+ + + +
+
+ +