ch.puzzle.itc.mobiliar.business.security.control.PermissionService.java Source code

Java tutorial

Introduction

Here is the source code for ch.puzzle.itc.mobiliar.business.security.control.PermissionService.java

Source

/*
 * AMW - Automated Middleware allows you to manage the configurations of
 * your Java EE applications on an unlimited number of different environments
 * with various versions, including the automated deployment of those apps.
 * Copyright (C) 2013-2016 by Puzzle ITC
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package ch.puzzle.itc.mobiliar.business.security.control;

import ch.puzzle.itc.mobiliar.business.deploy.entity.DeploymentEntity;
import ch.puzzle.itc.mobiliar.business.deploy.entity.DeploymentState;
import ch.puzzle.itc.mobiliar.business.environment.entity.ContextEntity;
import ch.puzzle.itc.mobiliar.business.resourcegroup.entity.ResourceEntity;
import ch.puzzle.itc.mobiliar.business.resourcegroup.entity.ResourceGroupEntity;
import ch.puzzle.itc.mobiliar.business.resourcegroup.entity.ResourceTypeEntity;
import ch.puzzle.itc.mobiliar.business.security.entity.*;
import ch.puzzle.itc.mobiliar.common.exception.NotAuthorizedException;
import ch.puzzle.itc.mobiliar.common.util.DefaultResourceTypeDefinition;
import org.apache.commons.lang.StringUtils;

import javax.ejb.SessionContext;
import javax.ejb.Stateless;
import javax.inject.Inject;
import java.io.Serializable;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Logger;

import static ch.puzzle.itc.mobiliar.business.security.entity.Action.ALL;
import static ch.puzzle.itc.mobiliar.business.security.entity.Action.UPDATE;

@Stateless
public class PermissionService implements Serializable {

    @Inject
    PermissionRepository permissionRepository;

    @Inject
    Logger log;

    @Inject
    SessionContext sessionContext;

    // map containing only Roles with Restrictions which have the DEPLOYMENT-Permission (non legacy)
    static Map<String, List<RestrictionDTO>> deployableRolesWithRestrictions;
    // map containing Roles with Restrictions (legacy & non legacy)
    static Map<String, List<RestrictionDTO>> rolesWithRestrictions;
    // map containing UserRestrictions with Restrictions (non legacy)
    static Map<String, List<RestrictionEntity>> userRestrictions;

    Map<String, List<RestrictionDTO>> getDeployableRoles() {
        boolean isReload = permissionRepository.isReloadDeployableRoleList();
        if (deployableRolesWithRestrictions == null || isReload) {
            Map<String, List<RestrictionDTO>> tmpDeployableRolesWithRestrictions = new HashMap<>();
            for (RoleEntity role : permissionRepository.getDeployableRoles()) {
                addPermission(tmpDeployableRolesWithRestrictions, role);
            }
            deployableRolesWithRestrictions = Collections.unmodifiableMap(tmpDeployableRolesWithRestrictions);
            if (isReload) {
                permissionRepository.setReloadDeployableRoleList(false);
            }
        }
        return deployableRolesWithRestrictions;
    }

    /**
     * Checks if the caller is allowed to see Deployments
     */
    public boolean hasPermissionToSeeDeployment() {
        for (Map.Entry<String, List<RestrictionDTO>> entry : getDeployableRoles().entrySet()) {
            if (sessionContext.isCallerInRole(entry.getKey())) {
                return true;
            }
        }
        return hasUserRestriction(Permission.DEPLOYMENT.name(), null, null, null, null);
    }

    /**
     * Checks if the caller is allowed to create (re-)Deployments
     */
    public boolean hasPermissionToCreateDeployment() {
        for (Map.Entry<String, List<RestrictionDTO>> entry : getDeployableRoles().entrySet()) {
            if (sessionContext.isCallerInRole(entry.getKey())) {
                for (RestrictionDTO restrictionDTO : entry.getValue()) {
                    if (restrictionDTO.getRestriction().getAction().equals(Action.CREATE)
                            || restrictionDTO.getRestriction().getAction().equals(Action.ALL)) {
                        return true;
                    }
                }
            }
        }
        return hasUserRestriction(Permission.DEPLOYMENT.name(), null, Action.CREATE, null, null);
    }

    /**
     * Checks if the caller is allowed to edit Deployments
     */
    public boolean hasPermissionToEditDeployment() {
        for (Map.Entry<String, List<RestrictionDTO>> entry : getDeployableRoles().entrySet()) {
            if (sessionContext.isCallerInRole(entry.getKey())) {
                for (RestrictionDTO restrictionDTO : entry.getValue()) {
                    if (restrictionDTO.getRestriction().getAction().equals(Action.UPDATE)
                            || restrictionDTO.getRestriction().getAction().equals(Action.ALL)) {
                        return true;
                    }
                }
            }
        }
        return hasUserRestriction(Permission.DEPLOYMENT.name(), null, Action.UPDATE, null, null);
    }

    /**
     * Returns all available Roles with their Restrictions
     * Legacy Permissions are mapped to the new Permission/Restriction model
     *
     * @return Map key=Role.name, value=RestrictionDTOs
     */
    public Map<String, List<RestrictionDTO>> getPermissions() {
        boolean isReload = permissionRepository.isReloadRolesAndPermissionsList();
        if (rolesWithRestrictions == null || isReload) {
            Map<String, List<RestrictionDTO>> tmpRolesWithRestrictions = new HashMap<>();
            // add new permissions with restriction
            if (permissionRepository.getRolesWithRestrictions() != null) {
                for (RoleEntity role : permissionRepository.getRolesWithRestrictions()) {
                    addPermission(tmpRolesWithRestrictions, role);
                }
            }
            //make immutable
            for (String roleName : tmpRolesWithRestrictions.keySet()) {
                List<RestrictionDTO> restrictions = tmpRolesWithRestrictions.get(roleName);
                tmpRolesWithRestrictions.put(roleName, Collections.unmodifiableList(restrictions));
            }
            rolesWithRestrictions = Collections.unmodifiableMap(tmpRolesWithRestrictions);

            if (isReload) {
                permissionRepository.setReloadRolesAndPermissionsList(false);
            }
        }
        return rolesWithRestrictions;
    }

    public List<RestrictionEntity> getUserRestrictionsForLoggedInUser() {
        return getUserRestrictions(getCurrentUserName());
    }

    /**
     * Returns a (cached) list of all Restrictions assigned to a specific UserRestriction
     *
     * @param userName
     */
    public List<RestrictionEntity> getUserRestrictions(String userName) {
        if (permissionRepository.isReloadUserRestrictionsList() || userRestrictions == null) {
            userRestrictions = new ConcurrentHashMap<>();
        }
        if (!userRestrictions.containsKey(userName)) {
            userRestrictions.put(userName,
                    Collections.unmodifiableList(permissionRepository.getUserWithRestrictions(userName)));
            if (permissionRepository.isReloadUserRestrictionsList()) {
                permissionRepository.setReloadUserRestrictionsList(false);
            }
        }
        return userRestrictions.get(userName);
    }

    /**
     * Returns a list of all available Restrictions assigned to UserRestriction
     *
     * @return List<RestrictionEntity>
     */
    public List<RestrictionEntity> getAllUserRestrictions() {
        return permissionRepository.getUsersWithRestrictions();
    }

    private void addPermission(Map<String, List<RestrictionDTO>> tmpRolesWithRestrictions, RoleEntity role) {
        String roleName = role.getName();
        if (!tmpRolesWithRestrictions.containsKey(roleName)) {
            tmpRolesWithRestrictions.put(roleName, new ArrayList<RestrictionDTO>());
        }
        for (RestrictionEntity res : role.getRestrictions()) {
            // add restriction
            tmpRolesWithRestrictions.get(roleName).add(new RestrictionDTO(res));
        }
    }

    /**
     * Checks if a user has a role or a restriction with a certain Permission no matter for which Actions
     * Useful for displaying/hiding navigation elements in views
     * The specific Action required has to be checked when the action is involved (button)
     *
     * @param permission
     */
    public boolean hasPermission(Permission permission) {
        return hasRole(permission.name(), null, null, null, null)
                || hasUserRestriction(permission.name(), null, null, null, null);
    }

    /**
     * Checks if a user has a role or a restriction with a certain Permission and Action
     * Useful for displaying/hiding navigation elements in views
     *
     * @param permission
     */
    public boolean hasPermission(Permission permission, Action action) {
        return hasRole(permission.name(), null, action, null, null)
                || hasUserRestriction(permission.name(), null, action, null, null);
    }

    public boolean hasPermission(Permission permission, Action action, ResourceTypeEntity resourceType) {
        return hasRole(permission.name(), null, action, null, resourceType)
                || hasUserRestriction(permission.name(), null, action, null, resourceType);
    }

    /**
     * Checks if a user has a role or a restriction with a certain Permission
     *
     * @param permission the required Permission
     * @param context the requested Context (null = irrelevant)
     * @param action the required Action
     * @param resourceGroup the requested resourceGroup (null = irrelevant)
     * @param resourceType the requested resourceType (null = irrelevant)
     */
    public boolean hasPermission(Permission permission, ContextEntity context, Action action,
            ResourceGroupEntity resourceGroup, ResourceTypeEntity resourceType) {
        return hasRole(permission.name(), context, action, resourceGroup, resourceType)
                || hasUserRestriction(permission.name(), context, action, resourceGroup, resourceType);
    }

    /**
     * Checks if a user has a role or a restriction with a certain Permission on ALL Contexts
     * => context MUST NOT be restricted to a specific environment
     *
     * @param permission the required Permission
     * @param action the required Action
     * @param resourceGroup the requested resourceGroup (null = irrelevant)
     * @param resourceType the requested resourceType (null = irrelevant)
     */
    public boolean hasPermissionOnAllContext(Permission permission, Action action,
            ResourceGroupEntity resourceGroup, ResourceTypeEntity resourceType) {
        return hasRoleOnAllContext(permission.name(), action, resourceGroup, resourceType)
                || hasUserRestrictionOnAllContext(permission.name(), action, resourceGroup, resourceType);
    }

    /**
     * Checks if given permission is available. If not a exception is created with error message containing extraInfo part.
     *
     * @param permission
     * @param extraInfo
     */
    public void checkPermissionAndFireException(Permission permission, String extraInfo) {
        if (!hasPermission(permission)) {
            throwNotAuthorizedException(extraInfo);
        }
    }

    /**
     * Checks if given permission is available. If not a exception is created with error message containing extraInfo part.
     *
     * @param permission
     * @param extraInfo
     */
    public void checkPermissionAndFireException(Permission permission, Action action, String extraInfo) {
        if (!hasPermission(permission, action)) {
            throwNotAuthorizedException(extraInfo);
        }
    }

    /**
     * Checks if given permission is available. If not a exception is created with error message containing extraInfo part.
     *
     * @param permission
     * @param context
     * @param action
     * @param resourceGroup
     * @param resourceType
     * @param extraInfo
     */
    public void checkPermissionAndFireException(Permission permission, ContextEntity context, Action action,
            ResourceGroupEntity resourceGroup, ResourceTypeEntity resourceType, String extraInfo) {
        if (!hasPermission(permission, context, action, resourceGroup, resourceType)) {
            throwNotAuthorizedException(extraInfo);
        }
    }

    public void throwNotAuthorizedException(String extraInfo) {
        String errorMessage = "Not Authorized!";
        if (StringUtils.isNotEmpty(extraInfo)) {
            errorMessage += " You're not allowed to " + extraInfo + "!";
        }
        throw new NotAuthorizedException(errorMessage);
    }

    public boolean hasPermissionForDeploymentUpdate(DeploymentEntity deployment) {
        return hasPermissionAndActionForDeploymentOnContext(deployment.getContext(),
                deployment.getResource().getResourceGroup(), Action.UPDATE);
    }

    public boolean hasPermissionForDeploymentCreation(DeploymentEntity deployment) {
        return hasPermissionAndActionForDeploymentOnContext(deployment.getContext(),
                deployment.getResource().getResourceGroup(), Action.CREATE);
    }

    public boolean hasPermissionForDeploymentReject(DeploymentEntity deployment) {
        return hasPermissionAndActionForDeploymentOnContext(deployment.getContext(),
                deployment.getResource().getResourceGroup(), Action.DELETE);
    }

    public boolean hasPermissionForCancelDeployment(DeploymentEntity deployment) {
        return getCurrentUserName().equals(deployment.getDeploymentRequestUser())
                || (deployment.getDeploymentConfirmed() != null && deployment.getDeploymentConfirmed()
                        && hasPermissionAndActionForDeploymentOnContext(deployment.getContext(),
                                deployment.getResource().getResourceGroup(), Action.UPDATE));
    }

    /**
     * Checks if the caller is allowed to perform the requested action for specific ResourceGroup on the specific Environment
     * Note: Both, Permission/Restriction by Group and by User are checked
     *
     * @param context
     * @param resourceGroup
     * @param action
     */
    public boolean hasPermissionAndActionForDeploymentOnContext(ContextEntity context,
            ResourceGroupEntity resourceGroup, Action action) {
        if (context != null && sessionContext != null) {
            List<String> allowedRoles = new ArrayList<>();
            String permissionName = Permission.DEPLOYMENT.name();
            if (deployableRolesWithRestrictions == null) {
                getDeployableRoles();
            }
            for (Map.Entry<String, List<RestrictionDTO>> entry : deployableRolesWithRestrictions.entrySet()) {
                matchPermissionsAndContext(permissionName, action, context, resourceGroup,
                        resourceGroup.getResourceType(), allowedRoles, entry);
            }
            for (String roleName : allowedRoles) {
                if (sessionContext.isCallerInRole(roleName)) {
                    return true;
                }
            }
            return hasUserRestriction(permissionName, context, action, resourceGroup, null);
        }
        return false;
    }

    private boolean hasRole(String permissionName, ContextEntity context, Action action,
            ResourceGroupEntity resourceGroup, ResourceTypeEntity resourceType) {
        if (sessionContext != null) {
            List<String> allowedRoles = new ArrayList<>();
            Set<Map.Entry<String, List<RestrictionDTO>>> entries = getPermissions().entrySet();

            if (resourceType == null && resourceGroup != null) {
                resourceType = resourceGroup.getResourceType();
            }

            for (Map.Entry<String, List<RestrictionDTO>> entry : entries) {
                // context null means no check on context required - so any context is ok
                if (context == null) {
                    matchPermissions(permissionName, action, resourceGroup, resourceType, allowedRoles, entry);
                } else {
                    matchPermissionsAndContext(permissionName, action, context, resourceGroup, resourceType,
                            allowedRoles, entry);
                }
            }
            for (String roleName : allowedRoles) {
                if (sessionContext.isCallerInRole(roleName)) {
                    return true;
                }
            }
        }
        return false;
    }

    private boolean hasRoleOnAllContext(String permissionName, Action action, ResourceGroupEntity resourceGroup,
            ResourceTypeEntity resourceType) {
        if (sessionContext != null) {
            List<String> allowedRoles = new ArrayList<>();
            Set<Map.Entry<String, List<RestrictionDTO>>> entries = getPermissions().entrySet();

            if (resourceType == null && resourceGroup != null) {
                resourceType = resourceGroup.getResourceType();
            }

            for (Map.Entry<String, List<RestrictionDTO>> entry : entries) {
                matchPermissionsAndContext(permissionName, action, null, resourceGroup, resourceType, allowedRoles,
                        entry);
            }
            for (String roleName : allowedRoles) {
                if (sessionContext.isCallerInRole(roleName)) {
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * Checks if the logged-in user has a Restriction with the required Permission for a specific Context, Action,
     * ResourceGroup and/or ResourceType
     * => will skip context check if context is null
     *
     * @param permissionName
     * @param context
     * @param action
     * @param resourceGroup
     * @param resourceType
     */
    private boolean hasUserRestriction(String permissionName, ContextEntity context, Action action,
            ResourceGroupEntity resourceGroup, ResourceTypeEntity resourceType) {
        if (sessionContext == null) {
            return false;
        }

        for (RestrictionEntity restrictionEntity : getUserRestrictions(getCurrentUserName())) {
            if (restrictionEntity.getPermission().getValue().equals(permissionName)) {
                // context null means no check on context required - so any context is ok
                if (context == null) {
                    if (hasRequiredUserRestriction(action, resourceGroup, resourceType, restrictionEntity)) {
                        return true;
                    }
                }
                if (hasRequiredUserRestrictionOnAllContext(context, action, resourceGroup, resourceType,
                        restrictionEntity)) {
                    return true;
                }
            }
        }

        return false;
    }

    /**
     * Checks if the logged-in user has a Restriction with the required Permission for a specific Action, ResourceGroup
     * and/or ResourceType ON ALL context
     * => will return false if the user restriction is restricted to a specific context
     *
     * @param permissionName
     * @param action
     * @param resourceGroup
     * @param resourceType
     */
    private boolean hasUserRestrictionOnAllContext(String permissionName, Action action,
            ResourceGroupEntity resourceGroup, ResourceTypeEntity resourceType) {
        if (sessionContext == null) {
            return false;
        }
        getUserRestrictions(getCurrentUserName());
        if (!userRestrictions.get(getCurrentUserName()).isEmpty()) {
            for (RestrictionEntity restrictionEntity : userRestrictions.get(getCurrentUserName())) {
                if (restrictionEntity.getPermission().getValue().equals(permissionName)) {
                    return hasRequiredUserRestrictionOnAllContext(null, action, resourceGroup, resourceType,
                            restrictionEntity);
                }
            }
        }
        return false;
    }

    /**
     * Checks whether a Role has the Permission perform a certain Action
     * If so, it adds the role to the list of the allowed roles
     *
     * @param permissionName
     * @param action
     * @param resourceGroup
     * @param resourceType
     * @param allowedRoles
     * @param entry
     */
    private void matchPermissions(String permissionName, Action action, ResourceGroupEntity resourceGroup,
            ResourceTypeEntity resourceType, List<String> allowedRoles,
            Map.Entry<String, List<RestrictionDTO>> entry) {
        String roleName = entry.getKey();
        for (RestrictionDTO restrictionDTO : entry.getValue()) {
            if (restrictionDTO.getPermissionName().equals(permissionName)
                    && hasPermissionForAction(restrictionDTO.getRestriction(), action)
                    && hasPermissionForResource(restrictionDTO.getRestriction(), resourceGroup)
                    && hasPermissionForResourceType(restrictionDTO.getRestriction(), resourceType)
                    && hasPermissionForDefaultResourceType(restrictionDTO.getRestriction(), resourceType)) {
                allowedRoles.add(roleName);
            }
        }
    }

    /**
     * Checks whether a Role has the Permission perform a certain Action with a specific ResourceGroup on a specific Context (or on its parent)
     * If so, it adds the role to the list of the allowed roles
     *
     * @param permissionName
     * @param action
     * @param context
     * @param resourceGroup
     * @param allowedRoles
     * @param entry
     */
    private void matchPermissionsAndContext(String permissionName, Action action, ContextEntity context,
            ResourceGroupEntity resourceGroup, ResourceTypeEntity resourceType, List<String> allowedRoles,
            Map.Entry<String, List<RestrictionDTO>> entry) {
        for (RestrictionDTO restrictionDTO : entry.getValue()) {
            if (restrictionDTO.getPermissionName().equals(permissionName)) {
                checkContextAndActionAndResource(context, action, resourceGroup, resourceType, allowedRoles, entry,
                        restrictionDTO.getRestriction());
            }
        }
    }

    /**
     * Checks if a Role is allowed to perform a certain Action with a specific ResourceGroup on a specific Context (or on its parent)
     * If so, it adds the role to the list of the allowed roles
     *
     * @param context
     * @param action
     * @param resource
     * @param resourceType
     * @param allowedRoles
     * @param entry
     * @param restriction
     */
    private void checkContextAndActionAndResource(ContextEntity context, Action action,
            ResourceGroupEntity resource, ResourceTypeEntity resourceType, List<String> allowedRoles,
            Map.Entry<String, List<RestrictionDTO>> entry, RestrictionEntity restriction) {
        if (hasPermissionForContext(restriction, context) && hasPermissionForAction(restriction, action)
                && hasPermissionForResource(restriction, resource)
                && hasPermissionForResourceType(restriction, resourceType)
                && hasPermissionForDefaultResourceType(restriction, resourceType)) {
            allowedRoles.add(entry.getKey());
        } else if (context != null && context.getParent() != null) {
            checkContextAndActionAndResource(context.getParent(), action, resource, resourceType, allowedRoles,
                    entry, restriction);
        }
    }

    /**
     * Checks if a User is allowed to perform a certain Action with a specific ResourceGroup on a specific Context (or on its parent)
     *
     * @param context
     * @param action
     * @param resource
     * @param resourceType
     * @param restriction
     */
    private boolean hasRequiredUserRestrictionOnAllContext(ContextEntity context, Action action,
            ResourceGroupEntity resource, ResourceTypeEntity resourceType, RestrictionEntity restriction) {
        if (hasPermissionForContext(restriction, context) && hasPermissionForAction(restriction, action)
                && hasPermissionForResource(restriction, resource)
                && hasPermissionForResourceType(restriction, resourceType)
                && hasPermissionForDefaultResourceType(restriction, resourceType)) {
            return true;
        } else if (context != null && context.getParent() != null) {
            if (hasRequiredUserRestrictionOnAllContext(context.getParent(), action, resource, resourceType,
                    restriction)) {
                return true;
            }
        }
        return false;
    }

    /**
     * Checks if a User is allowed to perform a certain Action with a specific ResourceGroup/ResourceType
     *
     * @param action
     * @param resource
     * @param resourceType
     * @param restriction
     */
    private boolean hasRequiredUserRestriction(Action action, ResourceGroupEntity resource,
            ResourceTypeEntity resourceType, RestrictionEntity restriction) {
        return (hasPermissionForAction(restriction, action) && hasPermissionForResource(restriction, resource)
                && hasPermissionForResourceType(restriction, resourceType)
                && hasPermissionForDefaultResourceType(restriction, resourceType));
    }

    /**
     * Checks if a Restriction gives permission for a specific Context
     * No Context on Restriction means all Contexts are allowed
     *
     * @param restriction
     * @param context
     */
    private boolean hasPermissionForContext(RestrictionEntity restriction, ContextEntity context) {
        return restriction.getContext() == null
                || (context != null && restriction.getContext().getId().equals(context.getId()));
    }

    /**
     * Checks if a Restriction gives permission for a specific Context or its parent
     * No Context on Restriction means all Contexts are allowed
     *
     * @param restriction
     * @param context
     */
    private boolean hasPermissionForContextOrForParent(RestrictionEntity restriction, ContextEntity context) {
        return restriction.getContext() == null || (context != null
                && (restriction.getContext().getId().equals(context.getId()) || (context.getParent() != null
                        && restriction.getContext().getId().equals(context.getParent().getId()))));
    }

    /**
     * Checks if a Restriction gives permission for a specific Action
     *
     * @param restriction
     * @param action
     */
    private boolean hasPermissionForAction(RestrictionEntity restriction, Action action) {
        return action == null || restriction.getAction().equals(action) || restriction.getAction().equals(ALL);
    }

    /**
     * Checks if a Restriction gives permission for a specific ResourceGroup
     * No Resource on Restriction means all ResourceGroups are allowed
     *
     * @param restriction
     * @param resourceGroup
     */
    private boolean hasPermissionForResource(RestrictionEntity restriction, ResourceGroupEntity resourceGroup) {
        return resourceGroup == null || restriction.getResourceGroup() == null
                || restriction.getResourceGroup().getId().equals(resourceGroup.getId());
    }

    /**
     * Checks if a Restriction gives permission for a specific ResourceType
     * No ResourceType on Restriction means all ResourceTypes are allowed
     *
     * @param restriction
     * @param resourceType
     */
    private boolean hasPermissionForResourceType(RestrictionEntity restriction, ResourceTypeEntity resourceType) {
        if (resourceType == null || restriction.getResourceType() == null) {
            return true;
        }
        if (restriction.getResourceType().getId().equals(resourceType.getId())) {
            return true;
        }
        return resourceType.getParentResourceType() != null
                && restriction.getResourceType().getId().equals(resourceType.getParentResourceType().getId());
    }

    /**
     * Checks if a Restriction gives permission for a specific (Default)ResourceType
     * No DefaultResourceType on Restriction means all ResourceTypes (including DefaultResourceTypes) are allowed
     *
     * @param restriction
     * @param resourceType
     */
    private boolean hasPermissionForDefaultResourceType(RestrictionEntity restriction,
            ResourceTypeEntity resourceType) {
        // Default and non DefaultTypes are allowed
        if (resourceType == null || restriction.getResourceTypePermission().equals(ResourceTypePermission.ANY)) {
            return true;
        }
        // Only DefaultTypes are allowed
        if (restriction.getResourceTypePermission().equals(ResourceTypePermission.DEFAULT_ONLY)
                && DefaultResourceTypeDefinition.contains(resourceType.getName())) {
            return true;
        }
        // Only non DefaultTypes are allowed
        return restriction.getResourceTypePermission().equals(ResourceTypePermission.NON_DEFAULT_ONLY)
                && !DefaultResourceTypeDefinition.contains(resourceType.getName());
    }

    /**
     * Check if the user can delete instances of ResourceTypes
     *
     * @param resourceType
     */
    public boolean hasPermissionToRemoveInstanceOfResType(ResourceTypeEntity resourceType) {
        return hasPermission(Permission.RESOURCE, Action.DELETE, resourceType);
    }

    /**
     * Checks if a user is allowed to add a Relation to a Resource
     *
     * @param resourceEntity
     * @param context
     */
    public boolean hasPermissionToAddRelation(ResourceEntity resourceEntity, ContextEntity context) {
        if (resourceEntity != null && resourceEntity.getResourceType() != null) {
            return hasPermission(Permission.RESOURCE, context, Action.UPDATE, resourceEntity.getResourceGroup(),
                    null);
        }
        if (resourceEntity != null && resourceEntity.getResourceType() == null) {
            return false;
        }
        return hasPermission(Permission.RESOURCE, UPDATE);
    }

    /**
     * Checks if user is allowed to add a Relation to a ResourceType
     *
     * @param resourceTypeEntity
     */
    public boolean hasPermissionToAddRelatedResourceType(ResourceTypeEntity resourceTypeEntity) {
        if (resourceTypeEntity != null) {
            if (hasPermission(Permission.RESOURCETYPE, null, Action.UPDATE, null, resourceTypeEntity)) {
                return true;
            }
        }
        return false;
    }

    /**
     * Checks if the user may delete a Resource relationship
     */
    public boolean hasPermissionToDeleteRelation(ResourceEntity resourceEntity, ContextEntity context) {
        if (resourceEntity != null && resourceEntity.getResourceType() != null) {
            if (hasPermission(Permission.RESOURCE, context, Action.UPDATE, resourceEntity.getResourceGroup(),
                    null)) {
                return true;
            }
        }
        return false;
    }

    /**
     * Checks if the user may delete a ResourceType relationship
     */
    public boolean hasPermissionToDeleteRelationType(ResourceTypeEntity resourceTypeEntity) {
        return hasPermission(Permission.RESOURCETYPE, null, Action.UPDATE, null, resourceTypeEntity);
    }

    /**
     * Checks if user may create or edit Templates of Resources
     *
     * @param resource
     * @param isTestingMode
     */
    public boolean hasPermissionToAddResourceTemplate(ResourceEntity resource, boolean isTestingMode) {
        // ok if user has update permission on the Resource, context is always global, so we set it to null to omit the check
        if (hasPermission(Permission.RESOURCE_TEMPLATE, null, Action.CREATE, resource.getResourceGroup(), null)) {
            return true;
        }
        return isTestingMode && hasPermission(Permission.SHAKEDOWN_TEST_MODE);
    }

    public boolean hasPermissionToUpdateResourceTemplate(ResourceEntity resource, boolean isTestingMode) {
        // ok if user has update permission on the Resource, context is always global, so we set it to null to omit the check
        if (hasPermission(Permission.RESOURCE_TEMPLATE, null, Action.UPDATE, resource.getResourceGroup(), null)) {
            return true;
        }
        return isTestingMode && hasPermission(Permission.SHAKEDOWN_TEST_MODE);
    }

    /**
     * Checks if user may create or edit Templates of ResourceTypes
     *
     * @param resourceType
     * @param isTestingMode
     */
    public boolean hasPermissionToAddResourceTypeTemplate(ResourceTypeEntity resourceType, boolean isTestingMode) {
        // ok if user has update permission on the ResourceType, context is always global, so we set it to null to omit the check
        if (hasPermission(Permission.RESOURCETYPE_TEMPLATE, null, Action.CREATE, null, resourceType)) {
            return true;
        }
        return resourceType != null && isTestingMode && hasPermission(Permission.SHAKEDOWN_TEST_MODE);
    }

    public boolean hasPermissionToUpdateResourceTypeTemplate(ResourceTypeEntity resourceType,
            boolean isTestingMode) {
        // ok if user has update permission on the ResourceType, context is always global, so we set it to null to omit the check
        if (hasPermission(Permission.RESOURCETYPE_TEMPLATE, null, Action.UPDATE, null, resourceType)) {
            return true;
        }
        return resourceType != null && isTestingMode && hasPermission(Permission.SHAKEDOWN_TEST_MODE);
    }

    /**
     * Diese Methode gibt den Username zurck
     */
    public String getCurrentUserName() {
        return sessionContext.getCallerPrincipal() != null ? sessionContext.getCallerPrincipal().getName() : null;
    }

    /**
     * Returns a list of ALL Restrictions of the caller (both, Restrictions by User and Role)
     */
    public List<RestrictionEntity> getAllCallerRestrictions() {
        List<RestrictionEntity> restrictions = new ArrayList<>();
        Map<String, List<RestrictionDTO>> roleWithRestrictions = getPermissions();
        for (String roleName : roleWithRestrictions.keySet()) {
            if (sessionContext.isCallerInRole(roleName)) {
                for (RestrictionDTO restrictionDTO : roleWithRestrictions.get(roleName)) {
                    restrictions.add(restrictionDTO.getRestriction());
                }
            }
        }
        List<RestrictionEntity> userRestrictions = getUserRestrictions(getCurrentUserName());
        restrictions.addAll(userRestrictions);
        return restrictions;
    }

    /**
     * Checks if the caller has the required rights to delegate a specific Restriction to another user
     * The caller must have
     * <li>a PERMISSION_DELEGATION Permission</li>
     * <li>a similar Restriction as the one he wants to delegate</li>
     *
     * @param permission to be delegated
     * @param resourceGroup allowed by the permission to be delegated
     * @param resourceType allowed by the permission to be delegated
     * @param context allowed by the permission to be delegated
     * @param action allowed by the permission to be delegated
     */
    public boolean hasPermissionToDelegatePermission(Permission permission, ResourceGroupEntity resourceGroup,
            ResourceTypeEntity resourceType, ContextEntity context, Action action) {
        if (hasPermission(Permission.PERMISSION_DELEGATION)) {
            // specific
            if (context != null && action != null && (resourceGroup != null || resourceType != null)) {
                return hasPermission(permission, context, action, resourceGroup, resourceType);
            } else {
                List<RestrictionEntity> callerRestrictions = getAllCallerRestrictions();
                for (RestrictionEntity restriction : callerRestrictions) {
                    if (restriction.getPermission().getValue().equals(permission.name())) {
                        int score = 0;
                        if (hasPermissionForContextOrForParent(restriction, context)) {
                            ++score;
                        }
                        if (restriction.getAction().equals(Action.ALL) || restriction.getAction().equals(action)) {
                            ++score;
                        }
                        if (restriction.getResourceGroup() == null
                                || restriction.getResourceGroup().equals(resourceGroup)) {
                            ++score;
                        }
                        if (restriction.getResourceType() == null
                                || restriction.getResourceType().equals(resourceType)) {
                            ++score;
                        }
                        if (score == 4) {
                            return true;
                        }
                    }
                }
            }
        }
        return false;
    }

    /**
     * Checks if the caller already has a Restriction similar to the given Restriction
     * Returns true if he already has a similar, equal or less restrictive Restriction
     * Returns false if he does not have a similar or just one that is more restrictive than the given one
     *
     * @param newRestriction
     */
    public boolean callerHasIdenticalOrMoreGeneralRestriction(RestrictionEntity newRestriction) {
        List<RestrictionEntity> similarRestrictions = new ArrayList<>();
        for (RestrictionEntity restriction : getAllCallerRestrictions()) {
            if (restriction.getPermission().getValue().equals(newRestriction.getPermission().getValue())) {
                checkSimilarRestrictions(newRestriction.getPermission().getValue(), newRestriction.getAction(),
                        newRestriction.getContext(), newRestriction.getResourceGroup(),
                        newRestriction.getResourceType(), similarRestrictions, restriction);
            }
        }
        return aMoreGeneralRestrictionExists(newRestriction, similarRestrictions);
    }

    /**
     * Checks if a Restriction similar to the given Restriction already exists
     * Returns true if a similar, equal or less restrictive Restriction exists
     * Returns false if no similar or one that is more restrictive than the given one exists
     *
     * @param newRestriction
     */
    public boolean identicalOrMoreGeneralRestrictionExists(RestrictionEntity newRestriction) {
        if (newRestriction.getRole() != null) {
            return hasSimilarRoleRestriction(newRestriction);
        } else if (newRestriction.getUser() != null) {
            return hasSimilarUserRestriction(newRestriction);
        }
        return false;
    }

    private boolean hasSimilarRoleRestriction(RestrictionEntity newRestriction) {
        List<RestrictionEntity> similarRestrictions = new ArrayList<>();
        Set<Map.Entry<String, List<RestrictionDTO>>> entries = getPermissions().entrySet();
        for (Map.Entry<String, List<RestrictionDTO>> entry : entries) {
            if (entry.getKey().equals(newRestriction.getRole().getName())) {
                for (RestrictionDTO restrictionDTO : entry.getValue()) {
                    if (restrictionDTO.getPermissionName().equals(newRestriction.getPermission().getValue())) {
                        checkSimilarRestrictions(newRestriction.getPermission().getValue(),
                                newRestriction.getAction(), newRestriction.getContext(),
                                newRestriction.getResourceGroup(), newRestriction.getResourceType(),
                                similarRestrictions, restrictionDTO.getRestriction());
                    }
                }
                return aMoreGeneralRestrictionExists(newRestriction, similarRestrictions);
            }
        }
        return false;
    }

    private boolean hasSimilarUserRestriction(RestrictionEntity newRestriction) {
        List<RestrictionEntity> similarRestrictions = new ArrayList<>();
        for (RestrictionEntity restrictionEntity : getUserRestrictions(newRestriction.getUser().getName())) {
            if (restrictionEntity.getPermission().getValue().equals(newRestriction.getPermission().getValue())) {
                checkSimilarRestrictions(newRestriction.getPermission().getValue(), newRestriction.getAction(),
                        newRestriction.getContext(), newRestriction.getResourceGroup(),
                        newRestriction.getResourceType(), similarRestrictions, restrictionEntity);
            }
        }
        return aMoreGeneralRestrictionExists(newRestriction, similarRestrictions);
    }

    /**
     * Checks whether an existing Restriction is similar to the one which should be created
     * If so, it adds the existing Restriction to the list of the similar Restrictions
     *
     * @param permissionName of the Restriction to be created
     * @param action of the Restriction to be created
     * @param context of the Restriction to be created
     * @param resourceGroup allowed by the Restriction to be created
     * @param similarRestrictions a list containing similar RestrictionEntities
     * @param existingRestriction an existing RestrictionEntity to which the other will be compared to
     */
    private void checkSimilarRestrictions(String permissionName, Action action, ContextEntity context,
            ResourceGroupEntity resourceGroup, ResourceTypeEntity resourceType,
            List<RestrictionEntity> similarRestrictions, RestrictionEntity existingRestriction) {
        if (permissionName.equals(existingRestriction.getPermission().getValue())) {
            similarByContextAndActionAndResource(context, action, resourceGroup, resourceType, similarRestrictions,
                    existingRestriction);
        }
    }

    /**
     * Checks if an existing Restriction is allowed to perform the same Action on the same ResourceGroup on the same Context (or on its parent) as the one to be created
     * If so, it adds the existing Restriction to the list of the similar Restrictions
     *
     * @param context of the Restriction to be created
     * @param action of the Restriction to be created
     * @param resource allowed by the Restriction to be created
     * @param resourceType allowed by the Restriction to be created
     * @param similarRestrictions a list containing similar RestrictionEntities
     * @param existingRestriction an existing RestrictionEntity to which the other will be compared to
     */
    private void similarByContextAndActionAndResource(ContextEntity context, Action action,
            ResourceGroupEntity resource, ResourceTypeEntity resourceType,
            List<RestrictionEntity> similarRestrictions, RestrictionEntity existingRestriction) {
        if (hasPermissionForContext(existingRestriction, context)
                && hasPermissionForAction(existingRestriction, action)
                && hasPermissionForResource(existingRestriction, resource)
                && hasPermissionForResourceType(existingRestriction, resourceType)
                && hasPermissionForDefaultResourceType(existingRestriction, resourceType)) {
            similarRestrictions.add(existingRestriction);
        } else if (context != null && context.getParent() != null) {
            similarByContextAndActionAndResource(context.getParent(), action, resource, resourceType,
                    similarRestrictions, existingRestriction);
        }
    }

    /**
     * Checks if a more general Restriction (one who grants more rights) exists
     *
     * @param newRestriction
     * @param similarRestrictions
     */
    private boolean aMoreGeneralRestrictionExists(RestrictionEntity newRestriction,
            Collection<RestrictionEntity> similarRestrictions) {
        if (similarRestrictions.size() > 0) {
            for (RestrictionEntity existingRestriction : similarRestrictions) {
                if (isMoreSpecificRestriction(newRestriction, existingRestriction)) {
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * Checks if restrictionEntityOne is more specific (grants less rights) than restrictionEntityTwo
     *
     * @param restrictionEntityOne
     * @param restrictionEntityTwo
     */
    private boolean isMoreSpecificRestriction(RestrictionEntity restrictionEntityOne,
            RestrictionEntity restrictionEntityTwo) {
        // allow update of existing - do not compare with itself
        if (restrictionEntityOne.getId() != null
                && restrictionEntityOne.getId().equals(restrictionEntityTwo.getId())) {
            return false;
        }
        if (restrictionEntityOne.getAction().equals(Action.ALL)
                && !restrictionEntityTwo.getAction().equals(Action.ALL)) {
            return false;
        }
        if (restrictionEntityOne.getResourceGroup() == null && restrictionEntityTwo.getResourceGroup() != null) {
            return false;
        }
        if (restrictionEntityOne.getResourceType() == null && restrictionEntityTwo.getResourceType() != null) {
            return false;
        }
        if (restrictionEntityOne.getResourceTypePermission().equals(ResourceTypePermission.ANY)
                && !restrictionEntityTwo.getResourceTypePermission().equals(ResourceTypePermission.ANY)) {
            return false;
        }
        return true;
    }

}