started to introduce permission checks into controller

This commit is contained in:
2018-11-22 14:53:46 +01:00
parent 8c11d3424e
commit 79c9ab623c
20 changed files with 925 additions and 606 deletions

View File

@ -0,0 +1,41 @@
/*
* 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.business.account.boundary;
import de.muehlencord.shared.account.util.Permission;
/**
*
* @author Joern Muehlencord <joern at muehlencord.de>
*/
public enum AccountPermissions implements Permission {
ACCOUNT_ADD("account:add", "Allow to create a new account"),
ACCOUNT_EDIT ("account:edit", "Allow to edit an existing account"),
ACCOUNT_DELETE("account:delete", "Allow to delete an existing account"),
ACCOUNT_LOGIN_ADD ("account:login:add", "Allow to create a login for a user"),
ACCOUNT_LOGIN_EDIT ("account:login:edit", "Allow to change a login for a user"),
ACCOUNT_LOGIN_DELETE ("account:login:delete", "Allow to delete a login for a user");
private final String name;
private final String description;
private AccountPermissions(String permissionName, String permissionDesc) {
this.name = permissionName;
this.description = permissionDesc;
}
@Override
public String getName() {
return name;
}
@Override
public String getDescription() {
return description;
}
}

View File

@ -22,11 +22,15 @@ import java.util.Locale;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import javax.annotation.ManagedBean;
import javax.annotation.PostConstruct;
import javax.ejb.EJB;
import javax.enterprise.context.SessionScoped;
import javax.enterprise.inject.Produces;
import javax.faces.context.FacesContext;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.subject.Subject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
*
@ -36,6 +40,7 @@ import org.apache.shiro.subject.Subject;
@SessionScoped
public class AccountProducer implements Serializable {
private static final Logger LOGGER = LoggerFactory.getLogger(AccountProducer.class);
private static final long serialVersionUID = -3806204732038165311L;
private final Map<String, Object> objectMap = new ConcurrentHashMap<>();
@ -43,6 +48,24 @@ public class AccountProducer implements Serializable {
AccountControl accountController;
private Account account = null;
private Locale locale = null;
@PostConstruct
public void init() {
FacesContext currentInstance = FacesContext.getCurrentInstance();
if (currentInstance == null) {
locale = Locale.ENGLISH;
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Using default locale {}", locale);
}
} else {
locale = currentInstance.getExternalContext().getRequestLocale();
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Using browser locale {}", locale);
}
}
}
@Produces
public Account getAccount() {
@ -59,6 +82,8 @@ public class AccountProducer implements Serializable {
accountName = subject.getPrincipal().toString();
}
account = accountController.getAccountEntity(accountName, true);
// TODO introduce locale support to account and switch
// to pre-defined locale if set
}
return account;
}
@ -85,7 +110,7 @@ public class AccountProducer implements Serializable {
@Produces
public Locale getLocale() {
return Locale.ENGLISH; // TODO depend lcoale on account or on incoming request
return locale;
}
}

View File

@ -6,7 +6,7 @@ import de.muehlencord.shared.account.business.mail.entity.MailException;
import de.muehlencord.shared.account.business.mail.boundary.MailService;
import de.muehlencord.shared.account.business.account.entity.AccountEntity;
import de.muehlencord.shared.account.business.account.entity.AccountLoginEntity;
import de.muehlencord.shared.account.business.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.account.util.AccountPU;
import de.muehlencord.shared.account.util.SecurityUtil;

View File

@ -1,5 +1,6 @@
package de.muehlencord.shared.account.business.account.entity;
import de.muehlencord.shared.account.business.application.entity.ApplicationRoleEntity;
import de.muehlencord.shared.account.business.config.entity.ConfigEntity;
import java.io.Serializable;
import java.util.ArrayList;

View File

@ -0,0 +1,41 @@
package de.muehlencord.shared.account.business.application.boundary;
import de.muehlencord.shared.account.util.Permission;
/**
*
* @author Joern Muehlencord <joern at muehlencord.de>
*/
public enum ApplicationPermissions implements Permission {
APP_LISTALL("application:listall", "Allows to list all avaiable applications"),
APP_ADD("application:add", "Allow to add a new application"),
APP_EDIT("application:edit", "Allow to edit an application"),
APP_DELETE("application:delete", "Allow to delete an application"),
PERMISSION_ADD("permission:add", "Allow to add a permission to an application"),
PERMISSION_EDIT("permission:edit", "Allow to edit a permission"),
PERMISSION_DELETE("permmission:delete", "Allow to delete a permission"),
ROLE_ADD("role:add", "Allow to add a role to an application"),
ROLE_EDIT("role:edit", "Allow to edit a role"),
ROLE_DELETE("role:delete", "Allow to delete a role"),
ROLE_PERMISSION_ASSIGN("role:permission:assign", "Allow to assign a permission to role"),
ROLE_PERMISSION_REVOKE("role:permission:revoke", "All ow to revoke a permission from a role");
private final String name;
private final String description;
private ApplicationPermissions(String permissionName, String permissionDesc) {
this.name = permissionName;
this.description = permissionDesc;
}
@Override
public String getName() {
return name;
}
@Override
public String getDescription() {
return description;
}
}

View File

@ -2,6 +2,8 @@ package de.muehlencord.shared.account.business.application.boundary;
import de.muehlencord.shared.account.business.application.entity.ApplicationEntity;
import de.muehlencord.shared.account.util.AccountPU;
import de.muehlencord.shared.account.util.AccountSecurityException;
import de.muehlencord.shared.account.util.SecurityUtil;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
@ -32,7 +34,8 @@ public class ApplicationService implements Serializable {
return em.find(ApplicationEntity.class, id);
}
public List<ApplicationEntity> getAllApplications() {
public List<ApplicationEntity> getAllApplications() throws AccountSecurityException {
SecurityUtil.checkPermission(ApplicationPermissions.APP_LISTALL, ApplicationServiceError.LISTALL_DENIED);
Query query = em.createNamedQuery("ApplicationEntity.findAll");
List<ApplicationEntity> resultList = query.getResultList();
if (resultList == null) {

View File

@ -0,0 +1,31 @@
package de.muehlencord.shared.account.business.application.boundary;
import de.muehlencord.shared.account.util.SecurityError;
/**
*
* @author Joern Muehlencord <joern at muehlencord.de>
*/
public enum ApplicationServiceError implements SecurityError {
LISTALL_DENIED("1000", "listall_denied");
private final String errorCode;
private final String messageKey;
private ApplicationServiceError(String errorCode, String messageKey) {
this.errorCode = errorCode;
this.messageKey = messageKey;
}
@Override
public String getErrorCode() {
return errorCode;
}
@Override
public String getMessageKey() {
return messageKey;
}
}

View File

@ -1,18 +1,21 @@
package de.muehlencord.shared.account.business.application.control;
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.ApplicationEntity;
import de.muehlencord.shared.account.business.application.entity.ApplicationPermissionEntity;
import de.muehlencord.shared.account.util.AccountPU;
import de.muehlencord.shared.account.util.Permission;
import java.io.Serializable;
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import java.util.List;
import java.util.ArrayList;
import java.util.List;
import javax.ejb.Stateless;
import javax.inject.Inject;
import javax.persistence.EntityManager;
import javax.persistence.OptimisticLockException;
import javax.persistence.Query;
import javax.transaction.Transactional;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
*
@ -22,11 +25,15 @@ import javax.transaction.Transactional;
public class ApplicationPermissionControl implements Serializable {
private static final long serialVersionUID = -3761100587901739481L;
private static final Logger LOGGER = LoggerFactory.getLogger(ApplicationPermissionControl.class);
@Inject
@AccountPU
EntityManager em;
@Inject
ApplicationEntity application;
public List<ApplicationPermissionEntity> getApplicationPermissions(ApplicationEntity app) {
Query query = em.createNamedQuery("ApplicationPermissionEntity.findAll");
query.setParameter("application", app);
@ -99,4 +106,37 @@ public class ApplicationPermissionControl implements Serializable {
return permissions.get(0);
}
}
@Transactional
public void setupPermissions(List<Permission> permissions) {
for (Permission permission : permissions) {
ApplicationPermissionEntity existingPermission = findByName(application, permission.getName());
if (existingPermission == null) {
// permission not available, create it
LOGGER.info("missing permission {} of {}", permission.getName(), application.getApplicationName());
existingPermission = new ApplicationPermissionEntity(permission.getName(), permission.getDescription());
existingPermission.setApplication(application);
em.persist(existingPermission);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("missing permission {} added to {}", permission.getName(), application.getApplicationName());
}
} else {
if (existingPermission.getPermissionDescription().equals(permission.getDescription())) {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Permission {} for {} already exists, skipping", permission.getName(), application.getApplicationName());
}
} else {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("description of permssion {} for {} differs, resetting to orignal value {}", permission.getName(), application.getApplicationName(), permission.getDescription());
}
existingPermission.setPermissionDescription(permission.getDescription());
em.merge (existingPermission);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("updated permission description {} for {}", permission.getName(), application.getApplicationName());
}
}
}
}
}
}

View File

@ -6,10 +6,11 @@
package de.muehlencord.shared.account.business.application.control;
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.ApplicationEntity;
import de.muehlencord.shared.account.business.application.entity.ApplicationPermissionEntity;
import de.muehlencord.shared.account.business.application.entity.ApplicationRoleEntity;
import de.muehlencord.shared.account.util.AccountPU;
import de.muehlencord.shared.account.util.Permission;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
@ -40,6 +41,10 @@ public class ApplicationRoleControl implements Serializable {
@AccountPU
EntityManager em;
@Inject
ApplicationEntity application;
// TODO requires special role to maintain role for other allication
public List<ApplicationRoleEntity> getAllRoles(ApplicationEntity app) {
Query query = em.createNamedQuery("ApplicationRoleEntity.findAll");
query.setParameter("application", app);
@ -53,10 +58,11 @@ public class ApplicationRoleControl implements Serializable {
}
@Transactional
public void createOrUpdate(ApplicationEntity application, String name, String description) {
ApplicationRoleEntity role = findByName(application, name);
// TODO requires special role to maintain role for other allication
public void createOrUpdate(ApplicationEntity app, String name, String description) {
ApplicationRoleEntity role = findByName(app, name);
if (role == null) {
role = new ApplicationRoleEntity(application, name, description);
role = new ApplicationRoleEntity(app, name, description);
em.persist(role);
} else {
role.setRoleDescription(description);
@ -65,16 +71,19 @@ public class ApplicationRoleControl implements Serializable {
}
@Transactional
// TODO requires special role to maintain role for other allication
public void create(ApplicationRoleEntity role) {
em.persist(role);
}
@Transactional
// TODO requires special role to maintain role for other allication
public void update(ApplicationRoleEntity role) {
em.merge(role);
}
@Transactional
// TODO requires special role to maintain role for other allication
public void delete(ApplicationRoleEntity role) throws AccountException {
ApplicationRoleEntity existingRole = attach(role);
em.remove(existingRole);
@ -129,6 +138,7 @@ public class ApplicationRoleControl implements Serializable {
}
@Transactional
// TODO requires special role to maintain role for other allication
public void addPermission(ApplicationRoleEntity role, ApplicationPermissionEntity permission) throws AccountException {
ApplicationRoleEntity existingRole = attach(role);
if (existingRole.getApplicationPermissionList() == null) {
@ -139,6 +149,7 @@ public class ApplicationRoleControl implements Serializable {
}
@Transactional
// TODO requires special role to maintain role for other allication
public void removePermission(ApplicationRoleEntity role, ApplicationPermissionEntity permission) throws AccountException {
ApplicationRoleEntity existingRole = attach(role);
if ((existingRole.getApplicationPermissionList() != null) && (existingRole.getApplicationPermissionList().contains(permission))) {
@ -147,4 +158,34 @@ public class ApplicationRoleControl implements Serializable {
em.merge(role);
}
@Transactional
public void setupRolePermission(List<Permission> permissions, String roleName) throws AccountException {
ApplicationRoleEntity role = findByName(application, roleName);
if (role == null) {
LOGGER.error("A role with name " + roleName + " is not defined for application " + application.getApplicationName());
} else {
for (Permission permission : permissions) {
ApplicationPermissionEntity existingPermission = applicationPermissionControl.findPermissionByName(application, permission.getName());
if (existingPermission == null) {
LOGGER.error("Required permission " + permission.getName() + " of application " + application.getApplicationName() + " does not exist. Ensure to call setupPermissions first");
} else {
if (role.getApplicationPermissionList().contains(existingPermission)) {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Permission {} already assigned to role {} of {}, skipping", permission.getName(), roleName, application.getApplicationName());
}
} else {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Permission {} not assigned to role {} of {}", permission.getName(), roleName, application.getApplicationName());
}
addPermission(role, existingPermission);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Added permission {} to role {} of {}", permission.getName(), roleName, application.getApplicationName());
}
}
}
}
}
}
}

View File

@ -1,7 +1,5 @@
package de.muehlencord.shared.account.business.application.entity;
import de.muehlencord.shared.account.business.account.entity.ApplicationPermissionEntity;
import de.muehlencord.shared.account.business.account.entity.ApplicationRoleEntity;
import de.muehlencord.shared.account.business.config.entity.ConfigEntity;
import java.io.Serializable;
import java.util.List;

View File

@ -1,4 +1,4 @@
package de.muehlencord.shared.account.business.account.entity;
package de.muehlencord.shared.account.business.application.entity;
import de.muehlencord.shared.account.business.application.entity.ApplicationEntity;
import java.io.Serializable;

View File

@ -1,5 +1,6 @@
package de.muehlencord.shared.account.business.account.entity;
package de.muehlencord.shared.account.business.application.entity;
import de.muehlencord.shared.account.business.account.entity.AccountEntity;
import de.muehlencord.shared.account.business.application.entity.ApplicationEntity;
import java.io.Serializable;
import java.util.List;

View File

@ -0,0 +1,44 @@
/*
* 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.util;
import java.io.Serializable;
import java.util.Locale;
import java.util.ResourceBundle;
/**
*
* @author Joern Muehlencord <joern at muehlencord.de>
*/
public class AccountSecurityException extends Exception implements Serializable {
private static final long serialVersionUID = 8135850463689587815L;
private final SecurityError securityError;
public AccountSecurityException(SecurityError securityError) {
this.securityError = securityError;
}
public String getErrorCode() {
return securityError.getErrorCode();
}
public String getMessageKey() {
return securityError.getMessageKey();
}
public String getMessage() {
ResourceBundle resourceBundle = ResourceBundle.getBundle(securityError.getClass().getName(), Locale.ENGLISH);
return resourceBundle.getString(securityError.getMessageKey());
}
public String getLocalizedMessage(Locale locale) {
ResourceBundle resourceBundle = ResourceBundle.getBundle(securityError.getClass().getName(), locale);
return resourceBundle.getString(securityError.getMessageKey());
}
}

View File

@ -0,0 +1,13 @@
package de.muehlencord.shared.account.util;
/**
*
* @author Joern Muehlencord <joern at muehlencord.de>
*/
public interface Permission {
String getName();
String getDescription();
}

View File

@ -0,0 +1,12 @@
package de.muehlencord.shared.account.util;
/**
*
* @author Joern Muehlencord <joern at muehlencord.de>
*/
public interface SecurityError {
String getErrorCode();
String getMessageKey();
}

View File

@ -1,10 +1,12 @@
package de.muehlencord.shared.account.util;
import org.apache.shiro.SecurityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.apache.shiro.authc.credential.DefaultPasswordService;
import org.apache.shiro.crypto.hash.DefaultHashService;
import org.apache.shiro.crypto.hash.Sha512Hash;
import org.apache.shiro.subject.Subject;
/**
*
@ -30,4 +32,15 @@ public class SecurityUtil {
return encryptedPassword;
}
public static void checkPermission(Permission permission, SecurityError error) throws AccountSecurityException {
Subject currentUser = SecurityUtils.getSubject();
if ((currentUser == null) || (!currentUser.isAuthenticated())) {
throw new AccountSecurityException(error); // TODO support special error for not logged in
}
String requiredPermissions = permission.getName();
if (!currentUser.isPermitted(requiredPermissions)) {
throw new AccountSecurityException(error);
}
}
}

View File

@ -0,0 +1,5 @@
# 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.
listall_denied=You are not allowed to list all applications

View File

@ -0,0 +1,5 @@
# 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.
listall_denied=Sie haben nicht die n\u00f6tige Rechte alle Applikationen aufzulisten

View File

@ -0,0 +1,5 @@
# 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.
listall_denied=User not allowed to list all applications