All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.icthh.xm.commons.permission.service.rolestrategy.SingleRoleStrategy Maven / Gradle / Ivy

The newest version!
package com.icthh.xm.commons.permission.service.rolestrategy;

import com.icthh.xm.commons.exceptions.SkipPermissionException;
import com.icthh.xm.commons.permission.access.ResourceFactory;
import com.icthh.xm.commons.permission.access.subject.Subject;
import com.icthh.xm.commons.permission.constants.RoleConstant;
import com.icthh.xm.commons.permission.domain.EnvironmentVariable;
import com.icthh.xm.commons.permission.domain.Permission;
import com.icthh.xm.commons.permission.domain.ReactionStrategy;
import com.icthh.xm.commons.permission.service.AuthenticationSecurityExpressionMethods;
import com.icthh.xm.commons.permission.service.PermissionEvaluationContextBuilder;
import com.icthh.xm.commons.permission.service.PermissionService;
import com.icthh.xm.commons.permission.service.RoleService;
import com.icthh.xm.commons.permission.service.translator.SpelTranslator;
import com.icthh.xm.commons.security.XmAuthenticationContext;
import com.icthh.xm.commons.security.XmAuthenticationContextHolder;
import com.icthh.xm.commons.tenant.TenantContextHolder;
import com.icthh.xm.commons.tenant.TenantContextUtils;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.event.Level;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.spel.SpelNode;
import org.springframework.expression.spel.standard.SpelExpression;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Component;

import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;

import static java.lang.String.format;
import static org.apache.commons.lang3.StringUtils.EMPTY;

@Slf4j
@RequiredArgsConstructor
@Component("singleRoleStrategy")
public class SingleRoleStrategy implements RoleStrategy {

    private static final String ERROR_ROLE_IS_UNDEFINED = "Role is undefined";
    private static final String LOG_KEY = "log";

    private final XmAuthenticationContextHolder xmAuthenticationContextHolder;
    private final TenantContextHolder tenantContextHolder;
    private final PermissionService permissionService;
    private final ResourceFactory resourceFactory;
    private final RoleService roleService;
    private final PermissionEvaluationContextBuilder permissionEvaluationContextBuilder;

    /**
     * Check permission for role and privilege key only.
     * @param authentication the authentication
     * @param privilege the privilege key
     * @return true if permitted
     */
    public boolean hasPermission(Authentication authentication,
        Object privilege) {
        return checkRole(authentication, privilege, true)
            || checkPermission(authentication, null, privilege, false, true);
    }

    /**
     * Check permission for role, privilege key and resource condition.
     * @param authentication the authentication
     * @param resource the resource
     * @param privilege the privilege key
     * @return true if permitted
     */
    public boolean hasPermission(Authentication authentication,
        Object resource,
        Object privilege) {
        boolean logPermission = isLogPermission(resource);
        return checkRole(authentication, privilege, logPermission)
            || checkPermission(authentication, resource, privilege, true, logPermission);
    }

    /**
     * Check permission for role, privilege key, new resource and old resource.
     * @param authentication the authentication
     * @param resource the old resource
     * @param resourceType the resource type
     * @param privilege the privilege key
     * @return true if permitted
     */
    @SuppressWarnings("unchecked")
    public boolean hasPermission(Authentication authentication,
        Serializable resource,
        String resourceType,
        Object privilege) {
        boolean logPermission = isLogPermission(resource);
        if (checkRole(authentication, privilege, logPermission)) {
            return true;
        }
        if (resource != null) {
            Object resourceId = ((Map) resource).get("id");
            if (resourceId != null) {
                ((Map) resource).put(resourceType,
                    resourceFactory.getResource(resourceId, resourceType));
            }
        }
        return checkPermission(authentication, resource, privilege, true, logPermission);
    }

    /**
     * Create condition with replaced subject variables.
     *
     * 

SpEL condition translated to SQL condition with replacing #returnObject to returnObject * and enriching #subject.* from Subject object (see {@link Subject}). * *

As an option, SpEL could be translated to SQL * via {@link SpelExpression} method {@code getAST()} * with traversing through {@link SpelNode} nodes and building SQL expression. * * @param authentication the authentication * @param privilegeKey the privilege key * @param translator the spel translator * @return condition if permitted, or null */ public String createCondition(Authentication authentication, Object privilegeKey, SpelTranslator translator) { if (!hasPermission(authentication, privilegeKey)) { throw new AccessDeniedException("Access is denied"); } String roleKey = getRoleKey(authentication); Permission permission = getPermission(roleKey, privilegeKey); Subject subject = getSubject(roleKey); if (!RoleConstant.SUPER_ADMIN.equals(roleKey) && permission != null && permission.getResourceCondition() != null) { return format("(%s)", translator.translate(permission.getResourceCondition().getExpressionString(), subject)); } return EMPTY; } @SuppressWarnings("unchecked") @SneakyThrows private boolean checkPermission(Authentication authentication, Object resource, Object privilegeKey, boolean checkCondition, boolean logPermission) { String roleKey = getRoleKey(authentication); Map resources = new HashMap<>(); if (resource != null) { resources.putAll((Map) resource); } resources.put("subject", getSubject(roleKey)); resources.put("oauth2", new AuthenticationSecurityExpressionMethods(authentication)); Map env = new HashMap<>(); env.put(EnvironmentVariable.IP.getName(), xmAuthenticationContextHolder .getContext().getRemoteAddress().orElse(null)); resources.put("env", env);//put some env variables in this map EvaluationContext context = permissionEvaluationContextBuilder.build(resources); Permission permission = getPermission(roleKey, privilegeKey); if (!isPermissionValid(permission)) { log(logPermission, Level.ERROR, "access denied: privilege={}, role={}, userKey={} due to privilege is not permitted", privilegeKey, roleKey, getUserKey()); return false; } boolean validCondition = true; if (!isConditionValid(permission.getEnvCondition(), context)) { log(logPermission, Level.ERROR, "access denied: privilege={}, role={}, userKey={} due to env condition: [{}] with context [{}]", privilegeKey, roleKey, getUserKey(), permission.getEnvCondition().getExpressionString(), resources); validCondition = false; } if (checkCondition && !isConditionValid(permission.getResourceCondition(), context)) { log(logPermission, Level.ERROR, "access denied: privilege={}, role={}, userKey={} due to env condition: [{}] with context [{}] " + "with context [{}]", privilegeKey, roleKey, getUserKey(), permission.getResourceCondition().getExpressionString(), resources); validCondition = false; } if (!validCondition && ReactionStrategy.SKIP.equals(permission.getReactionStrategy())) { throw new SkipPermissionException("Skip permission", permission.getRoleKey() + ":" + permission.getPrivilegeKey()); } else if (!validCondition) { return false; } log(logPermission, Level.INFO, "access granted: privilege={}, role={}, userKey={}", privilegeKey, roleKey, getUserKey()); return true; } private boolean checkRole(Authentication authentication, Object privilege, boolean logPermission) { String roleKey = getRoleKey(authentication); if (RoleConstant.SUPER_ADMIN.equals(roleKey)) { log(logPermission, Level.INFO, "access granted: privilege={}, role=SUPER-ADMIN, userKey={}", privilege, getUserKey()); return true; } if (!roleService.getRoles(TenantContextUtils.getRequiredTenantKeyValue(tenantContextHolder.getContext())) .containsKey(roleKey)) { log(logPermission, Level.ERROR, "access denied: privilege={}, role={}, userKey={} due to role is missing", privilege, roleKey, getUserKey()); throw new AccessDeniedException("Access is denied"); } return false; } private static boolean isConditionValid(Expression expression, EvaluationContext context) { boolean result; if (expression == null || StringUtils.isEmpty(expression.getExpressionString())) { result = true; } else { try { result = expression.getValue(context, Boolean.class); } catch (Exception e) { result = false; log.error("Exception while getting value ", e); } } return result; } private static boolean isPermissionValid(Permission permission) { boolean result = true; if (permission == null || permission.isDisabled()) { result = false; } return result; } private Subject getSubject(String roleKey) { XmAuthenticationContext authContext = xmAuthenticationContextHolder.getContext(); return new Subject(authContext.getLogin().orElse(null), authContext.getUserKey().orElse(null), roleKey); } private String getUserKey() { return xmAuthenticationContextHolder.getContext().getUserKey().orElse(null); } private static String getRoleKey(Authentication authentication) { return authentication.getAuthorities().stream() .findFirst() .orElseThrow(() -> new RuntimeException(ERROR_ROLE_IS_UNDEFINED)) .getAuthority(); } private Permission getPermission(String roleKey, Object privilegeKey) { return permissionService.getPermissions( TenantContextUtils.getRequiredTenantKeyValue(tenantContextHolder.getContext())) .get(roleKey + ":" + privilegeKey); } @SuppressWarnings("unchecked") private static boolean isLogPermission(Object resource) { if (resource != null && resource instanceof Map) { Map resourceMap = (Map) resource; Object logFlag = resourceMap.get(LOG_KEY); if (logFlag != null && logFlag instanceof Boolean) { resourceMap.remove(LOG_KEY); return (Boolean) logFlag; } } return true; } private static void log(boolean allowToLog, Level logLevel, String logMessage, Object... logArgs) { if (!allowToLog) { return; } switch (logLevel) { case INFO: log.info(logMessage, logArgs); break; case ERROR: log.error(logMessage, logArgs); break; default: break; } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy