com.sap.cloud.security.ams.spring.adapter.PolicyDecisionPointPermissionEvaluator Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of spring-ams Show documentation
Show all versions of spring-ams Show documentation
Client Library for integrating Spring applications with SAP Authorization Management Service (AMS)
/************************************************************************
* © 2019-2023 SAP SE or an SAP affiliate company. All rights reserved. *
************************************************************************/
package com.sap.cloud.security.ams.spring.adapter;
import static org.springframework.web.bind.annotation.ValueConstants.DEFAULT_NONE;
import com.sap.cloud.security.ams.api.Principal;
import com.sap.cloud.security.ams.dcl.client.el.AttributeName;
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.language.DataControlLanguageScanner;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.lang.Nullable;
import org.springframework.util.ObjectUtils;
import java.util.List;
import java.util.Objects;
import java.util.Set;
class PolicyDecisionPointPermissionEvaluator {
private static final Logger LOGGER = LoggerFactory.getLogger(PolicyDecisionPointPermissionEvaluator.class);
public static final String MESSAGE_ATTRIBUTE_FORMAT = "An attribute shall have the " +
"following format: :=";
private static final String AUTHORITY_RESOURCE_PROPERTY = "sap.security.authorization.dcl.authority-resource";
private static final String AUTHORITY_RESOURCE = System.getProperty(AUTHORITY_RESOURCE_PROPERTY, "").trim();
private static final String NULL_VALUE = "null";
private PolicyDecisionPointPermissionEvaluator() {
} // hide implicit public one
/**
* Calls {@code PolicyDecisionPoint} whether principal is allowed to perform the
* action on resource. It maps the attributes from SPEL to typed values and adds
* it to {@code Attributes}.
*
* @param policyDecisionPoint
* the decision point
* @param principal
* the principal for which the permissions are requested
* @param resource
* the resource to access
* @param action
* describes the action on resource, e.g. read / write
* @param attributes
* an optional array of attributes (can be skipped)
* @return true when principal is authorized
* @throws NullPointerException
* in case policyDecisionPoint or principal is null
* @throws IllegalArgumentException
* in case no action and no resource and no attributes is provided
*/
static boolean hasPermission(PolicyDecisionPoint policyDecisionPoint, Principal principal,
String resource,
String action, String... attributes) {
Objects.requireNonNull(policyDecisionPoint, "Can not check permission as policyDecisionPoint is null.");
Objects.requireNonNull(principal, "Can not check permission as principal is null / anonymous.");
if (Objects.isNull(action) && Objects.isNull(resource)
&& (Objects.isNull(attributes) || attributes.length == 0)) {
throw new IllegalArgumentException(
"Can not check permission when action, resource and attributes are unspecified.");
}
Attributes input = principal.getAttributes()
.setAction(action)
.setResource(resource);
if (!ObjectUtils.isEmpty(attributes)) {
DataControlLanguageScanner scanner = DataControlLanguageScanner.create();
for (String attribute : attributes) {
AttributeName attributeName = parseAttributeName(scanner, attribute);
String attributeType = parseAttributeValueType(scanner);
Object attributeValue = parseAttributeObject(scanner, attributeType);
if (attributeValue == null)
break;
input.setValue(attributeName, attributeValue);
}
}
boolean isAuthorized = policyDecisionPoint.allow(input);
LOGGER.debug("Received policyDecisionPoint.allow({}): {}", input, isAuthorized);
LOGGER.debug(
"Is {} authorized to perform action '{}' on resource '{}' and attributes '{}' ? {}",
principal, action, resource, attributes, isAuthorized);
return isAuthorized;
}
/**
* Calls {@code PolicyDecisionPoint} whether principal is allowed to perform the
* action on resource. It maps the attributes from SPEL to typed values and adds
* it to {@code Attributes}.
*
* @param policyDecisionPoint
* the decision point
* @param principal
* the principal for which the permissions are requested
* @param action
* describes the action on resource, e.g. read / write
* @return true when principal is authorized
* @throws NullPointerException
* in case policyDecisionPoint or principal is null
* @throws IllegalArgumentException
* in case no action and no resource and no attributes is provided
*/
static boolean hasPermission(PolicyDecisionPoint policyDecisionPoint, Principal principal,
String action) {
Objects.requireNonNull(policyDecisionPoint, "Can not check permission as policyDecisionPoint is null.");
Objects.requireNonNull(principal, "Can not check permission as principal is null.");
Objects.requireNonNull(action, "Can not check permission as action is null.");
Attributes input = principal.getAttributes()
.setAction(action);
if (!AUTHORITY_RESOURCE.isEmpty()) {
input.setResource(AUTHORITY_RESOURCE);
}
boolean isAuthorized = policyDecisionPoint.allow(input);
LOGGER.debug("Received policyDecisionPoint.allow({}): {}", input, isAuthorized);
return isAuthorized;
}
private static AttributeName parseAttributeName(DataControlLanguageScanner scanner, String stringAttribute) {
scanner.input(stringAttribute);
scanner.skipWhitespace();
AttributeName attributeName = scanAttributeName(scanner);
scanner.throwOnErrror();
scanner.skipWhitespace();
if (!scanner.consume(':')) {
throw new IllegalArgumentException("Missing attribute type separator ':'. " + MESSAGE_ATTRIBUTE_FORMAT);
}
scanner.skipWhitespace();
return attributeName;
}
private static String parseAttributeValueType(DataControlLanguageScanner scanner) {
String attributeType = scanner.scanIdentifier();
if (!scanner.isOk()) {
throw new IllegalArgumentException("Missing attribute type. " + MESSAGE_ATTRIBUTE_FORMAT);
}
if (!scanner.consume('=')) {
throw new IllegalArgumentException("Missing attribute value separator '='. " + MESSAGE_ATTRIBUTE_FORMAT);
}
return attributeType;
}
@Nullable
private static Object parseAttributeObject(DataControlLanguageScanner scanner, String attributeType) {
if (scanner.getRemainingCount() == 0) {
return null;
}
String attributeValue = scanner.getRemainingInput();
switch (attributeType) {
case "string":
if (DEFAULT_NONE.equals(attributeValue)) {
return null;
}
return attributeValue;
case "boolean":
if (NULL_VALUE.equals(attributeValue)) {
return null;
}
return Boolean.valueOf(attributeValue);
case "number":
if (NULL_VALUE.equals(attributeValue)) {
return null;
}
try {
return Integer.parseInt(attributeValue);
} catch (NumberFormatException e) {
return Double.parseDouble(attributeValue);
}
default:
throw new IllegalArgumentException(
"Attribute type '" + attributeType
+ "' is not supported, use one of these: string, number or boolean.");
}
}
static private AttributeName scanAttributeName(DataControlLanguageScanner scanner) {
List segments = scanner.scanQualifiedName(true);
if (segments != null) {
AttributeName result = AttributeName.create(segments);
segments.clear();
return result;
}
return null;
}
/**
* This is a special case, that was required with Spring integration as part of
* Security Configuration. The list of potential policies should never be
* exposed!
*
* @param policyDecisionPoint
* the decision point
* @param principal
* the principal for which the permissions are requested
* @return potential permission or an empty set
* @throws NullPointerException
* in case policyDecisionPoint or principal is null
*/
static Set getPotentialPermissions(PolicyDecisionPoint policyDecisionPoint, Principal principal) {
Objects.requireNonNull(policyDecisionPoint,
"Can not derive potential permissions as policyDecisionPoint is null.");
Objects.requireNonNull(principal, "Can not derive potential permissions as principal is null.");
PolicyDecisionPointDclAuthoritiesExtractor extractor = new PolicyDecisionPointDclAuthoritiesExtractor(principal)
.policyDecisionPoint(policyDecisionPoint);
return extractor.derivePotentialAuthorities();
}
static boolean hasPartialPermission(final String springAuthority, final String action, final String resource) {
LOGGER.debug("hasPartialPermission({}, {}, {})", springAuthority, action, resource);
DclAuthority dclAuthority = DclAuthority.parseSpringAuthority(springAuthority);
if (Objects.isNull(dclAuthority)
|| Objects.isNull(dclAuthority.getAction())
|| Objects.isNull(dclAuthority.getResource())) {
return false;
}
DataControlLanguageScanner scanner = DataControlLanguageScanner.create();
scanner.input(Objects.nonNull(action) ? action : "*");
String requestedAction = scanner.scanIdentifierOrAny();
if (requestedAction == null) {
return false;
}
if (requestedAction.equals(dclAuthority.getAction())
|| dclAuthority.getAction().equals("*")
|| requestedAction.equals("*")) {
scanner.input(Objects.nonNull(resource) ? resource : "*");
String requestedResource = scanner.scanIdentifierOrAny();
if (requestedResource == null) {
return false;
}
return requestedResource.equals(dclAuthority.getResource())
|| dclAuthority.getResource().equals("*")
|| requestedResource.equals("*");
}
return false;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy