From c05ba11044f2cef0e7a2e2ee44494de5ecffa23e Mon Sep 17 00:00:00 2001 From: jomu Date: Thu, 22 Nov 2018 14:54:48 +0100 Subject: [PATCH] started to implement permission handling into views and pages --- .../account/web/EnsurePermissionsBean.java | 81 ++++++ .../account/web/FacesContextProducer.java | 2 + .../account/web/PermissionConstants.java | 48 ++++ .../account/web/presentation/AccountView.java | 2 +- .../web/presentation/ApplicationView.java | 31 +- .../web/presentation/PermissionView.java | 4 +- .../account/web/presentation/RoleView.java | 4 +- .../UniqueApplicationRoleNameValidator.java | 125 ++++---- .../UniqueApplicationValidator.java | 15 +- .../UniquePermissionNameValidator.java | 128 +++++---- .../main/resources/META-INF/persistence.xml | 9 +- .../main/resources/admin-config.properties | 2 +- account-ui/src/main/webapp/WEB-INF/shiro.ini | 124 ++++---- account-ui/src/main/webapp/WEB-INF/web.xml | 2 +- account-ui/src/main/webapp/login.xhtml | 190 ++++++------- .../webapp/resources/template/leftmenu.xhtml | 101 ++++--- .../webapp/resources/template/template.xhtml | 2 +- .../web/{account.xhtml => accounts.xhtml} | 180 ++++++------ .../src/main/webapp/web/applications.xhtml | 84 ++++++ account-ui/src/main/webapp/web/index.xhtml | 79 +---- .../src/main/webapp/web/permissions.xhtml | 216 +++++++------- account-ui/src/main/webapp/web/roles.xhtml | 269 +++++++++--------- 22 files changed, 955 insertions(+), 743 deletions(-) create mode 100644 account-ui/src/main/java/de/muehlencord/shared/account/web/EnsurePermissionsBean.java create mode 100644 account-ui/src/main/java/de/muehlencord/shared/account/web/PermissionConstants.java rename account-ui/src/main/webapp/web/{account.xhtml => accounts.xhtml} (62%) create mode 100644 account-ui/src/main/webapp/web/applications.xhtml diff --git a/account-ui/src/main/java/de/muehlencord/shared/account/web/EnsurePermissionsBean.java b/account-ui/src/main/java/de/muehlencord/shared/account/web/EnsurePermissionsBean.java new file mode 100644 index 0000000..cb180ad --- /dev/null +++ b/account-ui/src/main/java/de/muehlencord/shared/account/web/EnsurePermissionsBean.java @@ -0,0 +1,81 @@ +/* + * Copyright 2018 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.AccountPermissions; +import de.muehlencord.shared.account.business.account.entity.AccountException; +import de.muehlencord.shared.account.business.application.boundary.ApplicationPermissions; +import de.muehlencord.shared.account.business.application.control.ApplicationPermissionControl; +import de.muehlencord.shared.account.business.application.control.ApplicationRoleControl; +import de.muehlencord.shared.account.business.application.entity.ApplicationEntity; +import java.util.Arrays; +import javax.enterprise.context.ApplicationScoped; +import javax.enterprise.context.Initialized; +import javax.enterprise.event.Observes; +import javax.inject.Inject; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * + * @author Joern Muehlencord + */ +@ApplicationScoped +public class EnsurePermissionsBean { + + @Inject + ApplicationEntity application; + + @Inject + ApplicationPermissionControl applicationPermissionControl; + + @Inject + ApplicationRoleControl applicationRoleControl; + + private static final Logger LOGGER = LoggerFactory.getLogger(EnsurePermissionsBean.class); + + public void init(@Observes @Initialized(ApplicationScoped.class) Object init) { + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("Ensure all permissions for {} are available", application.getApplicationName()); + } + applicationPermissionControl.setupPermissions(Arrays.asList(ApplicationPermissions.values())); + applicationPermissionControl.setupPermissions(Arrays.asList(AccountPermissions.values())); + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("All permissions added to application", application.getApplicationName()); + } + + // all permissions available - ensure permission is assigned to Admin role + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("Ensuring Admin role for {} has all permissions", application.getApplicationName()); + } + try { + applicationRoleControl.setupRolePermission(Arrays.asList(ApplicationPermissions.values()), "Admin"); // NOI18N + applicationRoleControl.setupRolePermission(Arrays.asList(AccountPermissions.values()), "Admin"); // NOI18N + } catch (AccountException ex) { + LOGGER.error("Error adding permission to Admin role"); + if (LOGGER.isDebugEnabled()) { + LOGGER.debug(ex.toString(), ex); + } else { + LOGGER.error(ex.toString()); + } + } + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("All permissions added to Admin role of {}", application.getApplicationName()); + } + + } + +} 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 index d26de80..5a2b950 100644 --- 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 @@ -15,6 +15,7 @@ */ package de.muehlencord.shared.account.web; +import javax.enterprise.context.ApplicationScoped; import javax.enterprise.context.ContextNotActiveException; import javax.enterprise.context.RequestScoped; import javax.enterprise.inject.Produces; @@ -24,6 +25,7 @@ import javax.faces.context.FacesContext; * * @author Joern Muehlencord */ +@ApplicationScoped public class FacesContextProducer { @Produces diff --git a/account-ui/src/main/java/de/muehlencord/shared/account/web/PermissionConstants.java b/account-ui/src/main/java/de/muehlencord/shared/account/web/PermissionConstants.java new file mode 100644 index 0000000..3970ff4 --- /dev/null +++ b/account-ui/src/main/java/de/muehlencord/shared/account/web/PermissionConstants.java @@ -0,0 +1,48 @@ +/* + * Copyright 2018 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.application.boundary.ApplicationPermissions; +import javax.enterprise.context.ApplicationScoped; +import javax.inject.Named; + +/** + * TODO replace with omnifaces:importConstants currently problems with Netbeans + * to import omnifaces taglib + * + * @author Joern Muehlencord + */ +@Named(value = "permissionConstants") +@ApplicationScoped +public class PermissionConstants { + + public String getApplicationListAll() { + return ApplicationPermissions.APP_LISTALL.getName(); + } + + public String getPermissionsCombined() { + return ApplicationPermissions.PERMISSION_ADD.getName() + "," + + ApplicationPermissions.PERMISSION_EDIT.getName() + "," + + ApplicationPermissions.PERMISSION_DELETE.getName(); + } + + public String getRolesCombined() { + return ApplicationPermissions.ROLE_ADD.getName() + "," + + ApplicationPermissions.ROLE_EDIT.getName() + "," + + ApplicationPermissions.ROLE_DELETE.getName(); + } + +} diff --git a/account-ui/src/main/java/de/muehlencord/shared/account/web/presentation/AccountView.java b/account-ui/src/main/java/de/muehlencord/shared/account/web/presentation/AccountView.java index 89d16a6..f634d61 100644 --- a/account-ui/src/main/java/de/muehlencord/shared/account/web/presentation/AccountView.java +++ b/account-ui/src/main/java/de/muehlencord/shared/account/web/presentation/AccountView.java @@ -6,7 +6,7 @@ import de.muehlencord.shared.account.business.account.entity.AccountEntity; import de.muehlencord.shared.account.business.account.entity.AccountException; import de.muehlencord.shared.account.business.account.entity.AccountLoginEntity; import de.muehlencord.shared.account.business.account.entity.AccountStatus; -import de.muehlencord.shared.account.business.account.entity.ApplicationRoleEntity; +import de.muehlencord.shared.account.business.application.entity.ApplicationRoleEntity; import de.muehlencord.shared.account.business.application.entity.ApplicationEntity; import de.muehlencord.shared.jeeutil.FacesUtil; import java.io.Serializable; 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 index 5e03dd3..b27c96f 100644 --- 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 @@ -2,9 +2,12 @@ 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.account.util.AccountSecurityException; import de.muehlencord.shared.jeeutil.FacesUtil; import java.io.Serializable; +import java.util.ArrayList; import java.util.List; +import java.util.Locale; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; import javax.inject.Named; @@ -27,6 +30,9 @@ public class ApplicationView implements Serializable { @Inject ApplicationService applicationService; + @Inject + Locale locale; + private ApplicationEntity currentApplication = null; private ApplicationEntity editApplication = null; private List applicationList = null; @@ -39,21 +45,32 @@ public class ApplicationView implements Serializable { currentApplication = applicationList.get(0); } if (LOGGER.isDebugEnabled()) { - LOGGER.debug("post construct executed"); - } + LOGGER.debug("post construct executed"); + } } - + @PreDestroy public void predestroy() { if (LOGGER.isDebugEnabled()) { - LOGGER.debug("Predestroy executed"); - } - + LOGGER.debug("Predestroy executed"); + } + } public List getAllApplications() { if (applicationList == null) { - applicationList = applicationService.getAllApplications(); + try { + applicationList = applicationService.getAllApplications(); + return applicationList; + } catch (AccountSecurityException ex) { + if (LOGGER.isDebugEnabled()) { + LOGGER.debug(ex.toString(), ex); + } else { + LOGGER.error(ex.toString()); + } + FacesUtil.addGlobalErrorMessage("Error " + ex.getErrorCode(), ex.getLocalizedMessage(locale)); + return new ArrayList<>(); + } } return applicationList; } 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 index 292a624..5a0c91f 100644 --- 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 @@ -16,7 +16,7 @@ package de.muehlencord.shared.account.web.presentation; import de.muehlencord.shared.account.business.account.entity.AccountException; -import de.muehlencord.shared.account.business.account.entity.ApplicationPermissionEntity; +import de.muehlencord.shared.account.business.application.entity.ApplicationPermissionEntity; import de.muehlencord.shared.account.business.application.control.ApplicationPermissionControl; import de.muehlencord.shared.account.business.application.entity.ApplicationEntity; import de.muehlencord.shared.jeeutil.FacesUtil; @@ -61,7 +61,7 @@ public class PermissionView implements Serializable { FacesUtil.addErrorMessage("editDialogMessages", "Error", "Permission name must not be null"); } else { if (currentPermission.getId() == null) { - applicationPermissionService.create(applicationView.getCurrentApplication(), newPermissionName, newPermissionName); + applicationPermissionService.create(applicationView.getCurrentApplication(), newPermissionName, newPermissionDescription); FacesUtil.addGlobalInfoMessage("Info", "Permission " + newPermissionName + " created"); } else { applicationPermissionService.update(currentPermission); diff --git a/account-ui/src/main/java/de/muehlencord/shared/account/web/presentation/RoleView.java b/account-ui/src/main/java/de/muehlencord/shared/account/web/presentation/RoleView.java index 63030d1..d69937b 100644 --- a/account-ui/src/main/java/de/muehlencord/shared/account/web/presentation/RoleView.java +++ b/account-ui/src/main/java/de/muehlencord/shared/account/web/presentation/RoleView.java @@ -17,8 +17,8 @@ package de.muehlencord.shared.account.web.presentation; import de.muehlencord.shared.account.business.application.control.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.account.business.application.entity.ApplicationPermissionEntity; +import de.muehlencord.shared.account.business.application.entity.ApplicationRoleEntity; import de.muehlencord.shared.account.business.application.entity.ApplicationEntity; import de.muehlencord.shared.jeeutil.FacesUtil; import java.io.Serializable; diff --git a/account-ui/src/main/java/de/muehlencord/shared/account/web/presentation/UniqueApplicationRoleNameValidator.java b/account-ui/src/main/java/de/muehlencord/shared/account/web/presentation/UniqueApplicationRoleNameValidator.java index 5f21451..095c7be 100644 --- a/account-ui/src/main/java/de/muehlencord/shared/account/web/presentation/UniqueApplicationRoleNameValidator.java +++ b/account-ui/src/main/java/de/muehlencord/shared/account/web/presentation/UniqueApplicationRoleNameValidator.java @@ -1,57 +1,68 @@ -package de.muehlencord.shared.account.web.presentation; - -import de.muehlencord.shared.account.business.account.entity.ApplicationRoleEntity; -import de.muehlencord.shared.account.business.application.control.ApplicationRoleControl; -import de.muehlencord.shared.account.business.application.entity.ApplicationEntity; -import de.muehlencord.shared.account.util.AccountPU; -import java.io.Serializable; -import javax.ejb.EJB; -import javax.faces.application.FacesMessage; -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; - -/** - * - * @author Joern Muehlencord - */ -@FacesValidator("uniqueApplicationRoleNameValidator") -public class UniqueApplicationRoleNameValidator implements Validator, Serializable { - - private static final long serialVersionUID = 8165013107453616719L; - - @Inject - @AccountPU - EntityManager em; - - @Inject - ApplicationRoleControl applicationRoleControl; - - @Override - public void validate(FacesContext context, UIComponent component, Object value) throws ValidatorException { - Object applicationObj = component.getAttributes().get("application"); - if ((applicationObj != null) && (applicationObj instanceof ApplicationEntity)) { - ApplicationEntity application = (ApplicationEntity) applicationObj; - if (value == null) { - throw new ValidatorException(new FacesMessage(FacesMessage.SEVERITY_ERROR, "Role name invalid", "Role name must not be empty")); - } - if (value instanceof String) { - String roleName = (String) value; - ApplicationRoleEntity existingRole = applicationRoleControl.findByName(application, roleName); - if (existingRole != null) { - throw new ValidatorException(new FacesMessage(FacesMessage.SEVERITY_ERROR, "Role name invalid", "Role already exists")); - } - } else { - throw new ValidatorException(new FacesMessage(FacesMessage.SEVERITY_ERROR, "Role name invalid", "Role name must be a string value")); - // TODO add IPRS logger - someone is trying to cheat - } - } else { - throw new ValidatorException(new FacesMessage(FacesMessage.SEVERITY_ERROR, "Application not set", "Permission name cannot be set if application is unknown")); - } - } - -} +package de.muehlencord.shared.account.web.presentation; + +import de.muehlencord.shared.account.business.application.entity.ApplicationRoleEntity; +import de.muehlencord.shared.account.business.application.control.ApplicationRoleControl; +import de.muehlencord.shared.account.business.application.entity.ApplicationEntity; +import de.muehlencord.shared.account.util.AccountPU; +import java.io.Serializable; +import java.util.UUID; +import javax.faces.application.FacesMessage; +import javax.faces.component.UIComponent; +import javax.faces.component.UIInput; +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; + +/** + * + * @author Joern Muehlencord + */ +@FacesValidator("uniqueApplicationRoleNameValidator") +public class UniqueApplicationRoleNameValidator implements Validator, Serializable { + + private static final long serialVersionUID = 8165013107453616719L; + + @Inject + @AccountPU + EntityManager em; + + @Inject + ApplicationRoleControl applicationRoleControl; + + @Override + public void validate(FacesContext context, UIComponent component, Object value) throws ValidatorException { + + Object oldRoleNameObj = ((UIInput) component).getValue(); + String oldRoleName = ""; + if (oldRoleNameObj != null) { + oldRoleName = oldRoleNameObj.toString(); + } + + Object applicationObj = component.getAttributes().get("application"); + if ((applicationObj != null) && (applicationObj instanceof ApplicationEntity)) { + ApplicationEntity application = (ApplicationEntity) applicationObj; + if (value == null) { + throw new ValidatorException(new FacesMessage(FacesMessage.SEVERITY_ERROR, "Role name invalid", "Role name must not be empty")); + } + if (value instanceof String) { + String roleName = (String) value; + ApplicationRoleEntity existingRole = applicationRoleControl.findByName(application, roleName); + if (existingRole != null) { + if (!oldRoleName.equals(roleName)) { + // name of role changed and there is another role with the new name already --> this must not happen + throw new ValidatorException(new FacesMessage(FacesMessage.SEVERITY_ERROR, "Role name invalid", "Role already exists")); + } + } + } else { + throw new ValidatorException(new FacesMessage(FacesMessage.SEVERITY_ERROR, "Role name invalid", "Role name must be a string value")); + // TODO add IPRS logger - someone is trying to cheat + } + } else { + throw new ValidatorException(new FacesMessage(FacesMessage.SEVERITY_ERROR, "Application not set", "Permission name cannot be set if application is unknown")); + } + } + +} 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 index 4010e71..52c8f11 100644 --- 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 @@ -3,8 +3,10 @@ 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 java.io.Serializable; +import java.util.UUID; import javax.faces.application.FacesMessage; import javax.faces.component.UIComponent; +import javax.faces.component.UIInput; import javax.faces.context.FacesContext; import javax.faces.validator.FacesValidator; import javax.faces.validator.Validator; @@ -28,6 +30,13 @@ public class UniqueApplicationValidator implements Validator, Serializable { @Override public void validate(FacesContext context, UIComponent component, Object value) throws ValidatorException { + + Object oldAppNameObj = ((UIInput) component).getValue(); + String oldAppName = ""; + if (oldAppNameObj != null) { + oldAppName = oldAppNameObj.toString(); + } + if (value == null) { throw new ValidatorException(new FacesMessage(FacesMessage.SEVERITY_ERROR, "Application name invalid", "Application name must not be empty")); } @@ -35,7 +44,11 @@ public class UniqueApplicationValidator implements Validator, Serializable { String applicationname = (String) value; ApplicationEntity existingApplication = applicationService.findByApplicationName(applicationname); if (existingApplication != null) { - throw new ValidatorException(new FacesMessage(FacesMessage.SEVERITY_ERROR, "Application name invalid", "Application already exists")); + if (!oldAppName.equals(applicationname)) { + // name of application changed and there is another application with the new + // name already --> this must not happen + throw new ValidatorException(new FacesMessage(FacesMessage.SEVERITY_ERROR, "Application name invalid", "Application already exists")); + } } LOGGER.info("Name = {}", applicationname); } else { diff --git a/account-ui/src/main/java/de/muehlencord/shared/account/web/presentation/UniquePermissionNameValidator.java b/account-ui/src/main/java/de/muehlencord/shared/account/web/presentation/UniquePermissionNameValidator.java index 8acc600..36be332 100644 --- a/account-ui/src/main/java/de/muehlencord/shared/account/web/presentation/UniquePermissionNameValidator.java +++ b/account-ui/src/main/java/de/muehlencord/shared/account/web/presentation/UniquePermissionNameValidator.java @@ -1,60 +1,68 @@ -package de.muehlencord.shared.account.web.presentation; - -import de.muehlencord.shared.account.business.account.entity.ApplicationPermissionEntity; -import de.muehlencord.shared.account.business.application.control.ApplicationPermissionControl; -import de.muehlencord.shared.account.business.application.entity.ApplicationEntity; -import de.muehlencord.shared.account.util.AccountPU; -import java.io.Serializable; -import javax.faces.application.FacesMessage; -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("uniquePermissionNameValidator") -public class UniquePermissionNameValidator implements Validator, Serializable { - - private static final long serialVersionUID = 2526409681909574670L; - private static final Logger LOGGER = LoggerFactory.getLogger(UniquePermissionNameValidator.class); - - @Inject - @AccountPU - EntityManager em; - - @Inject - ApplicationPermissionControl applicationPermissionControl; - - @Override - public void validate(FacesContext context, UIComponent component, Object value) throws ValidatorException { - Object applicationObj = component.getAttributes().get("application"); - if ((applicationObj != null) && (applicationObj instanceof ApplicationEntity)) { - ApplicationEntity application = (ApplicationEntity) applicationObj; - if (value == null) { - throw new ValidatorException(new FacesMessage(FacesMessage.SEVERITY_ERROR, "Permission name invalid", "Permission name must not be empty")); - } - if (value instanceof String) { - String permissionName = (String) value; - ApplicationPermissionEntity existingPermission = applicationPermissionControl.findPermissionByName(application, permissionName); - if (existingPermission != null) { - throw new ValidatorException(new FacesMessage(FacesMessage.SEVERITY_ERROR, "Permission name invalid", "Permission already exists")); - } - } else { - throw new ValidatorException(new FacesMessage(FacesMessage.SEVERITY_ERROR, "Permission name invalid", "Permission name must be a string value")); - // TODO add IPRS logger - someone is trying to cheat - } - } else { - throw new ValidatorException(new FacesMessage(FacesMessage.SEVERITY_ERROR, "Application not set", "Permission name cannot be set if application is unknown")); - } - - } - -} +package de.muehlencord.shared.account.web.presentation; + +import de.muehlencord.shared.account.business.application.entity.ApplicationPermissionEntity; +import de.muehlencord.shared.account.business.application.control.ApplicationPermissionControl; +import de.muehlencord.shared.account.business.application.entity.ApplicationEntity; +import de.muehlencord.shared.account.util.AccountPU; +import java.io.Serializable; +import javax.faces.application.FacesMessage; +import javax.faces.component.UIComponent; +import javax.faces.component.UIInput; +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; + +/** + * + * @author Joern Muehlencord + */ +@FacesValidator("uniquePermissionNameValidator") +public class UniquePermissionNameValidator implements Validator, Serializable { + + private static final long serialVersionUID = 2526409681909574670L; + + @Inject + @AccountPU + EntityManager em; + + @Inject + ApplicationPermissionControl applicationPermissionControl; + + @Override + public void validate(FacesContext context, UIComponent component, Object value) throws ValidatorException { + + Object oldPermissionNameObj = ((UIInput) component).getValue(); + String oldPermissionName = ""; + if (oldPermissionNameObj != null) { + oldPermissionName = oldPermissionNameObj.toString(); + } + + Object applicationObj = component.getAttributes().get("application"); + if ((applicationObj != null) && (applicationObj instanceof ApplicationEntity)) { + ApplicationEntity application = (ApplicationEntity) applicationObj; + if (value == null) { + throw new ValidatorException(new FacesMessage(FacesMessage.SEVERITY_ERROR, "Permission name invalid", "Permission name must not be empty")); + } + if (value instanceof String) { + String permissionName = (String) value; + ApplicationPermissionEntity existingPermission = applicationPermissionControl.findPermissionByName(application, permissionName); + if (existingPermission != null) { + if ((!oldPermissionName.equals (permissionName))) { + // name of permission changed and there is another permission with the new + // name already --> this must not happen + throw new ValidatorException(new FacesMessage(FacesMessage.SEVERITY_ERROR, "Permission name invalid", "Permission already exists")); + } + } + } else { + throw new ValidatorException(new FacesMessage(FacesMessage.SEVERITY_ERROR, "Permission name invalid", "Permission name must be a string value")); + // TODO add IPRS logger - someone is trying to cheat + } + } else { + throw new ValidatorException(new FacesMessage(FacesMessage.SEVERITY_ERROR, "Application not set", "Permission name cannot be set if application is unknown")); + } + } + +} diff --git a/account-ui/src/main/resources/META-INF/persistence.xml b/account-ui/src/main/resources/META-INF/persistence.xml index 63351bc..8d4a14b 100644 --- a/account-ui/src/main/resources/META-INF/persistence.xml +++ b/account-ui/src/main/resources/META-INF/persistence.xml @@ -1,13 +1,14 @@ - java:/jboss/accountDs + java:/jboss/accountDs de.muehlencord.shared.account.business.account.entity.AccountEntity de.muehlencord.shared.account.business.account.entity.AccountHistoryEntity de.muehlencord.shared.account.business.account.entity.AccountLoginEntity - de.muehlencord.shared.account.business.account.entity.ApplicationPermissionEntity - de.muehlencord.shared.account.business.account.entity.ApplicationRoleEntity + de.muehlencord.shared.account.business.account.entity.ApiKeyEntity de.muehlencord.shared.account.business.application.entity.ApplicationEntity + de.muehlencord.shared.account.business.application.entity.ApplicationPermissionEntity + de.muehlencord.shared.account.business.application.entity.ApplicationRoleEntity de.muehlencord.shared.account.business.config.entity.ConfigEntity de.muehlencord.shared.account.business.mail.entity.MailTemplateEntity true @@ -15,7 +16,7 @@ NONE - + diff --git a/account-ui/src/main/resources/admin-config.properties b/account-ui/src/main/resources/admin-config.properties index d3b40dd..4b4d3c0 100644 --- a/account-ui/src/main/resources/admin-config.properties +++ b/account-ui/src/main/resources/admin-config.properties @@ -1,5 +1,5 @@ admin.loginPage=/login.xhtml -admin.indexPage=/web/account.xhtml +admin.indexPage=/web/index.xhtml #admin.dateFormat= #admin.breadcrumbSize=5 admin.renderMessages=true diff --git a/account-ui/src/main/webapp/WEB-INF/shiro.ini b/account-ui/src/main/webapp/WEB-INF/shiro.ini index d9288a8..a47f5aa 100644 --- a/account-ui/src/main/webapp/WEB-INF/shiro.ini +++ b/account-ui/src/main/webapp/WEB-INF/shiro.ini @@ -1,62 +1,62 @@ -[main] - -# Context factory required for LDAP -${shiro.contextFactory} - -cacheManager = org.apache.shiro.cache.MemoryConstrainedCacheManager -securityManager.cacheManager = $cacheManager - -# DataSource Setup -datasource = org.apache.shiro.jndi.JndiObjectFactory -datasource.resourceName = java:/jboss/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 -${shiro.passwordMatcher} - -# LDAP Realm setup -${shiro.ldapRealm} - -# JDBC Realm setup -jdbcRealm = org.apache.shiro.realm.jdbc.JdbcRealm -jdbcRealm.permissionsLookupEnabled=false -# jdbcRealm.authenticationQuery = select al.account_password from account a, account_login al where al.account = a.id and a.username = ? and status not in ('LOCKED','DELETED') -jdbcRealm.authenticationQuery = SELECT accl.account_password from account acc, account_login accl, account_role accr, application_role appr WHERE accl.account = acc.id AND acc.id = accr.account AND accr.account_role = appr.id AND appr.application = '143a2bd3-7e0b-4162-a76e-3031331c7dfe' AND acc.status not in ('LOCKED','DELETED') AND acc.username = ? -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 = ${shiro.authcStrategy} -securityManager.realms = ${shiro.realms} -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 -/login.xhtml=authc -/logout.xhtml=logout -/**=authc -# /web/**=authc +[main] + +# Context factory required for LDAP +${shiro.contextFactory} + +cacheManager = org.apache.shiro.cache.MemoryConstrainedCacheManager +securityManager.cacheManager = $cacheManager + +# DataSource Setup +datasource = org.apache.shiro.jndi.JndiObjectFactory +datasource.resourceName = java:/jboss/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 +${shiro.passwordMatcher} + +# LDAP Realm setup +${shiro.ldapRealm} + +# JDBC Realm setup +jdbcRealm = org.apache.shiro.realm.jdbc.JdbcRealm +jdbcRealm.permissionsLookupEnabled=true +jdbcRealm.authenticationQuery = SELECT accl.account_password from account acc, account_login accl, account_role accr, application_role appr WHERE accl.account = acc.id AND acc.id = accr.account AND accr.account_role = appr.id AND appr.application = '143a2bd3-7e0b-4162-a76e-3031331c7dfe' AND acc.status not in ('LOCKED','DELETED') AND acc.username = ? +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.permissionsQuery = select permission_name from application_role appr, role_permission rp, application_permission appp WHERE appr.role_name = ? AND appr.application = '${applicationUuid}' AND rp.application_role = appr.id AND rp.role_permission = appp.id +jdbcRealm.credentialsMatcher = $passwordMatcher +jdbcRealm.dataSource = $datasource + +# Activate realms +authcStrategy = ${shiro.authcStrategy} +securityManager.realms = ${shiro.realms} +securityManager.authenticator.authenticationStrategy = $authcStrategy + +# Setup authentication filter +authc = de.muehlencord.shirofaces.filter.FacesAjaxAwarePassThruAuthenticationFilter +authc.loginUrl = /login.xhtml +authc.successUrl = /web/index.xhtml + +roles.unauthorizedUrl = /error/accessDenied.xhtml + +# +# filter setup +# +[urls] +/public/**=anon +/resources/**=anon +/fonts/**=anon +/javax.faces.resource/**=anon +/login.xhtml=authc +/logout.xhtml=logout +/**=authc +# /web/**=authc diff --git a/account-ui/src/main/webapp/WEB-INF/web.xml b/account-ui/src/main/webapp/WEB-INF/web.xml index 6fb534b..85faa06 100644 --- a/account-ui/src/main/webapp/WEB-INF/web.xml +++ b/account-ui/src/main/webapp/WEB-INF/web.xml @@ -36,7 +36,7 @@ - web/account.xhtml + web/index.xhtml diff --git a/account-ui/src/main/webapp/login.xhtml b/account-ui/src/main/webapp/login.xhtml index 5c4b639..5972e7b 100644 --- a/account-ui/src/main/webapp/login.xhtml +++ b/account-ui/src/main/webapp/login.xhtml @@ -1,96 +1,96 @@ - - - - - Login Page - - - - - - - - - - - - - - - + + + + + Login Page + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/account-ui/src/main/webapp/resources/template/leftmenu.xhtml b/account-ui/src/main/webapp/resources/template/leftmenu.xhtml index e9d2625..1172c56 100644 --- a/account-ui/src/main/webapp/resources/template/leftmenu.xhtml +++ b/account-ui/src/main/webapp/resources/template/leftmenu.xhtml @@ -1,43 +1,58 @@ - - - - - - - - + + + + + + + + diff --git a/account-ui/src/main/webapp/resources/template/template.xhtml b/account-ui/src/main/webapp/resources/template/template.xhtml index 52892e3..e3d7889 100644 --- a/account-ui/src/main/webapp/resources/template/template.xhtml +++ b/account-ui/src/main/webapp/resources/template/template.xhtml @@ -19,7 +19,7 @@ limitations under the License. xmlns:p="http://primefaces.org/ui" template="/admin.xhtml" xmlns:h="http://xmlns.jcp.org/jsf/html"> - + Account UI diff --git a/account-ui/src/main/webapp/web/account.xhtml b/account-ui/src/main/webapp/web/accounts.xhtml similarity index 62% rename from account-ui/src/main/webapp/web/account.xhtml rename to account-ui/src/main/webapp/web/accounts.xhtml index 1978a6b..2a38fbe 100644 --- a/account-ui/src/main/webapp/web/account.xhtml +++ b/account-ui/src/main/webapp/web/accounts.xhtml @@ -19,104 +19,106 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - -
-
-
- - - - + + +
+
+
+ + + + +
-
-
- -
-
- -
-
- - - -
- -
- - +
+ +
+
+ +
+
+ + - - - +
- +
+ + + + + + - - - - -
-
-
+ + + + + + +
+
+ - - + + + + + + + Applications + + + + + + + +
+
+ + + +
+
+ +
+
+ +
+
+ + + +
+ +
+ +
+
+ + + + + + + +
+
+ +
+
+ + + +
+
+ +
+ +
+ + +
+
+ + +
+ + +
+
+
+ +
+
\ 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 index 750dc31..983dc13 100644 --- a/account-ui/src/main/webapp/web/index.xhtml +++ b/account-ui/src/main/webapp/web/index.xhtml @@ -1,84 +1,13 @@ - + template="/resources/template/template.xhtml"> + - Applications + Home - - - -
-
- - - -
-
- -
-
- -
-
- - - -
- -
-
- - -
- - - - - - - -
-
- -
-
- - - -
-
- -
- -
- - -
-
- - -
- - -
-
-
- + Home
\ 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 index 6819ac0..696ca3b 100644 --- a/account-ui/src/main/webapp/web/permissions.xhtml +++ b/account-ui/src/main/webapp/web/permissions.xhtml @@ -1,108 +1,108 @@ - - - - - Permissions - - - - for #{applicationView.currentApplication.applicationName} - - - - - - - - - - - - - - - - - -
- -
-
- -
-
- - - -
-
-
- - - -
- - - - - - - - -
-
-
- - - - - -
-
-
- -
- -
-
- - -
-
-
- -
- -
- - -
-
- - -
- -
-
-
- - - -
- -
+ + + + + Permissions + + + + for #{applicationView.currentApplication.applicationName} + + + + + + + + + + + + + + + + + +
+ +
+
+ +
+
+ + + +
+
+
+ + +
+
+ + + + + + + + +
+
+
+ + + + + +
+
+
+ +
+ +
+
+ + +
+
+
+ +
+ +
+ + +
+
+ + +
+ +
+
+
+ + + +
+ +
diff --git a/account-ui/src/main/webapp/web/roles.xhtml b/account-ui/src/main/webapp/web/roles.xhtml index 73bd590..518667f 100644 --- a/account-ui/src/main/webapp/web/roles.xhtml +++ b/account-ui/src/main/webapp/web/roles.xhtml @@ -1,134 +1,135 @@ - - - - - Roles Overview - - - - for #{applicationView.currentApplication.applicationName} - - - - - - - - - - - - - - - - - -
-
- -
-
- -
-
- - - -
-
-
-
- - - - - - - - - - - - - - - - - -
- -
-
- - - -
-
-
- - - -
- - - - - - - -
-
- -
-
- - - - -
-
- -
-
- -
-
- -
-
- -
- -
- - -
-
- - -
-
-
-
-
- -
+ + + + + Roles Overview + + + + for #{applicationView.currentApplication.applicationName} + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+
+ + + +
+
+
+
+ + + + + + + + + + + + + + + + + +
+ +
+
+ + + +
+
+
+ + +
+
+ + + + + + + +
+
+ +
+
+ + + + +
+
+ +
+
+ +
+
+ +
+
+ +
+ +
+ + +
+
+ + +
+
+
+
+
+ +