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

com.sap.cloud.security.ams.capsupport.UserInfoProvider Maven / Gradle / Ivy

Go to download

Client Library for integrating SAP CAP applications with SAP Authorization Management Service (AMS)

The newest version!
/************************************************************************
 * © 2019-2023 SAP SE or an SAP affiliate company. All rights reserved. *
 ************************************************************************/
package com.sap.cloud.security.ams.capsupport;

import java.util.*;
import java.util.concurrent.TimeUnit;

import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.sap.cds.services.request.ModifiableUserInfo;
import com.sap.cds.services.request.UserInfo;
import com.sap.cds.services.utils.CdsErrorStatuses;
import com.sap.cds.services.utils.ErrorStatusException;
import com.sap.cloud.security.ams.dcl.client.el.AttributeName;
import com.sap.cloud.security.ams.dcl.client.el.Call;
import com.sap.cloud.security.ams.dcl.client.el.FilterClause;
import com.sap.cloud.security.ams.dcl.client.el.Visitor;
import com.sap.cloud.security.ams.dcl.client.pdp.Attributes;
import com.sap.cloud.security.ams.dcl.client.pdp.PolicyDecisionPoint;
import com.sap.cloud.security.ams.dcl.client.pdp.PolicyEvaluationException;

/**
 * The AMS UserInfoProvider retrieves roles assigned to the user by AMS policies using
 * `GRANT {role} ON $SCOPES` and adds them to the current UserInfo.
 */
@Deprecated
public class UserInfoProvider implements com.sap.cds.services.runtime.UserInfoProvider { // NOSONAR

    private static final Logger logger = LoggerFactory.getLogger(UserInfoProvider.class);

    private com.sap.cds.services.runtime.UserInfoProvider previous;

    private final PolicyDecisionPoint pdp;
    private final TokenServices tokenServices;
    private final Cache> roleCache;
    
    public UserInfoProvider(PolicyDecisionPoint pdp, TokenServices tokenServices) {
        this(pdp, tokenServices, CacheBuilder.newBuilder().expireAfterWrite(20, TimeUnit.SECONDS).maximumSize(10000).build());
    }

    public UserInfoProvider(PolicyDecisionPoint pdp, TokenServices tokenServices, Cache> cache) {
        this.pdp = pdp;
        this.tokenServices = tokenServices;
        this.roleCache = cache;
    }

    @Override
    public void setPrevious(com.sap.cds.services.runtime.UserInfoProvider previous) {
        this.previous = previous;
    }

    @Override
    public UserInfo get() {
        logger.debug("Get enhanced user info.");

        UserInfo previousUser;
        if (previous != null) {
            previousUser = previous.get();
        } else {
            return null;
        }

        if (previousUser != null && previousUser.getId() != null) {

            ModifiableUserInfo amsUserInfo = tokenServices.getModeDependingUserInfo(previousUser);

            String tenant = TokenServices.getTenant(amsUserInfo);
            String userId = TokenServices.getScimId(amsUserInfo);
            String cacheKey = tenant + userId;

            logger.debug("User tenant:{}, user id:{}", tenant, userId);

            Set userRoles;

            userRoles = roleCache.getIfPresent(cacheKey);
            if (userRoles == null) {
                try {
                    FilterClause filterClause = pdp
                            .allowFilterClause(createCallAttributes(tenant, userId));

                    logger.debug("PDP response:{}", filterClause);

                    RoleExtractingVisitor v = new RoleExtractingVisitor();
                    Visitor.accept(v, filterClause);
                    userRoles = v.getResult();
                    roleCache.put(cacheKey, userRoles);
                } catch (PolicyEvaluationException e) {
                    throw new ErrorStatusException(CdsErrorStatuses.EVENT_FORBIDDEN, e.getMessage());
                }
            }
            userRoles.forEach(amsUserInfo::addRole);
            return amsUserInfo;
        } else {
            logger.debug("No previous user info or userId found. Check the authentication and configuration of the UserInfoProvider chain.");
        }
        return previousUser;
    }

    private static final List SCOPES_UNKNOWNS = Collections.singletonList(Attributes.Names.DCL_ACTION);
    private static final List SCOPES_IGNORES = Arrays.asList(Attributes.Names.APP, Attributes.Names.ENV);

    private Attributes createCallAttributes(String zoneId, String userId) {
        Attributes result = CapSupportServices.createBaseCallAttributes(zoneId, userId);
        result.setResource("$SCOPES");
        result.setIgnores(SCOPES_IGNORES);
        result.setUnknowns(SCOPES_UNKNOWNS);
        return result;
    }

    static class RoleExtractingVisitor extends Visitor {
        private static final String[] CAP_PSEUDO_ROLES = new String[] { "any", "authenticated-user", "internal-user", "system-user" };
        
        private final Set roles;

        RoleExtractingVisitor() {
            roles = new HashSet<>();
        }

        public Set getResult() {
            return this.roles;
        }

        @Override
        public void visit(Call it) {
            super.visit(it);
            if (Call.Names.EQ.equals(it.getName()) && it.getArgumentCount() == 2) {
                Object arg0 = it.getArgument(0);
                Object arg1 = it.getArgument(1);
                String role = null;
                if (Attributes.Names.DCL_ACTION.equals(arg0)) {
                    role = (String) arg1;
                } else if (Attributes.Names.DCL_ACTION.equals(arg1)) {
                    role = (String) arg0;
                }
                if (role != null && !isPseudoRole(role)) {
                    roles.add(role);
                }
            }
        }

        private static boolean isPseudoRole(String role) {
            return Arrays.asList(CAP_PSEUDO_ROLES).contains(role.toLowerCase());
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy