From fcb1b7002ce9cc5d809e38f5c7c75b81911e6f9e Mon Sep 17 00:00:00 2001 From: Ronen Hilewicz Date: Mon, 14 Oct 2024 13:42:17 -0400 Subject: [PATCH] Add ability to provide additional resource context fields in Check middleware. --- .../com/aserto/example/JavaApplication.java | 6 +- .../com/aserto/authorizer/CheckConfig.java | 156 ++++++++++++++++-- .../authorizer/MethodAuthorization.java | 28 +++- .../mapper/resource/CheckResourceMapper.java | 55 ++++-- .../resource/PathParamsResourceMapper.java | 21 ++- 5 files changed, 214 insertions(+), 52 deletions(-) diff --git a/examples/java-application/src/main/java/com/aserto/example/JavaApplication.java b/examples/java-application/src/main/java/com/aserto/example/JavaApplication.java index 393eacc..944755a 100644 --- a/examples/java-application/src/main/java/com/aserto/example/JavaApplication.java +++ b/examples/java-application/src/main/java/com/aserto/example/JavaApplication.java @@ -7,8 +7,8 @@ @SpringBootApplication @ComponentScan("com.aserto") public class JavaApplication { - public static void main(String[] args) { - SpringApplication.run(JavaApplication.class, args); - } + public static void main(String[] args) { + SpringApplication.run(JavaApplication.class, args); + } } diff --git a/src/main/java/com/aserto/authorizer/CheckConfig.java b/src/main/java/com/aserto/authorizer/CheckConfig.java index ab18e99..b0caf2c 100644 --- a/src/main/java/com/aserto/authorizer/CheckConfig.java +++ b/src/main/java/com/aserto/authorizer/CheckConfig.java @@ -9,38 +9,158 @@ import com.aserto.authorizer.mapper.policy.PolicyMapper; import com.aserto.authorizer.mapper.policy.StaticPolicyMapper; import com.aserto.authorizer.mapper.resource.CheckResourceMapper; +import com.aserto.authorizer.mapper.resource.EmptyResourceMapper; +import com.aserto.authorizer.mapper.resource.ResourceMapper; public class CheckConfig { - private AuthzConfig authzCfg; + private final AuthzConfig authzCfg; - private ObjectTypeMapper objectTypeMapper; - private ObjectIdMapper objectIdMapper; - private RelationMapper relationMapper; - private PolicyMapper policyMapper; + private final ObjectTypeMapper objectTypeMapper; + private final ObjectIdMapper objectIdMapper; + private final RelationMapper relationMapper; + private final PolicyMapper policyMapper; + /** + * ResourceMapper for additional fields to be included in the resource context. + */ + private final ResourceMapper baseResourceMapper; - public CheckConfig(AuthzConfig authzCfg) { - // Clone the authz config because we will change it - this.authzCfg = new AuthzConfig(authzCfg); + static final String DEFAULT_POLICY = "rebac.check"; + + public CheckConfig( + AuthzConfig filterConfig, + String objectType, + String objectID, + String relation + ) { + this(filterConfig, objectType, objectID, relation, DEFAULT_POLICY); } - public CheckConfig(AuthzConfig filterConfig, String objectType, String objectID, String relation) { - this(filterConfig, objectType, objectID, relation, "rebac.check"); + public CheckConfig( + AuthzConfig filterConfig, + String objectType, + String objectID, + String relation, + String policy + ) { + this(filterConfig, objectType, objectID, relation, policy, new EmptyResourceMapper()); } - public CheckConfig(AuthzConfig filterConfig, String objectType, String objectID, String relation, String policy) { - this(filterConfig, new StaticObjectTypeMapper(objectType), new StaticObjectIdMapper(objectID), new StaticRelationMapper(relation), new StaticPolicyMapper(policy)); - } + public CheckConfig( + AuthzConfig filterConfig, + String objectType, + String objectID, + String relation, + ResourceMapper baseResourceMapper + ) { + this(filterConfig, objectType, objectID, relation, DEFAULT_POLICY, baseResourceMapper); + } + + public CheckConfig( + AuthzConfig filterConfig, + String objectType, + String objectID, + String relation, + String policy, + ResourceMapper baseResourceMapper + ) { + this( + filterConfig, + new StaticObjectTypeMapper(objectType), + new StaticObjectIdMapper(objectID), + new StaticRelationMapper(relation), + new StaticPolicyMapper(policy), + baseResourceMapper + ); + } + + public CheckConfig(AuthzConfig filterConfig, String objectType, ObjectIdMapper objectIdMapper, String relation) { + this(filterConfig, objectType, objectIdMapper, relation, DEFAULT_POLICY); + } + + public CheckConfig(AuthzConfig filterConfig, String objectType, ObjectIdMapper objectIdMapper, String relation, String policy) { + this( + filterConfig, + new StaticObjectTypeMapper(objectType), + objectIdMapper, + new StaticRelationMapper(relation), + new StaticPolicyMapper(policy), + new EmptyResourceMapper() + ); + } + + public CheckConfig( + AuthzConfig filterConfig, + String objectType, + ObjectIdMapper objectIdMapper, + String relation, + ResourceMapper baseResourceMapper + ) { + this( + filterConfig, + new StaticObjectTypeMapper(objectType), + objectIdMapper, + new StaticRelationMapper(relation), + new StaticPolicyMapper(DEFAULT_POLICY), + new EmptyResourceMapper() + ); + } + + public CheckConfig( + AuthzConfig filterConfig, + String objectType, + ObjectIdMapper objectIdMapper, + String relation, + String policy, + ResourceMapper baseResourceMapper + ) { + this( + filterConfig, + new StaticObjectTypeMapper(objectType), + objectIdMapper, + new StaticRelationMapper(relation), + new StaticPolicyMapper(policy), + baseResourceMapper + ); + } public CheckConfig(AuthzConfig filterConfig, ObjectTypeMapper objectTypeMapper, ObjectIdMapper objectIdMapper, RelationMapper relationMapper) { - this(filterConfig, objectTypeMapper, objectIdMapper, relationMapper, new StaticPolicyMapper("rebac.check")); - } + this(filterConfig, objectTypeMapper, objectIdMapper, relationMapper, new StaticPolicyMapper(DEFAULT_POLICY)); + } + + public CheckConfig( + AuthzConfig filterConfig, + ObjectTypeMapper objectTypeMapper, + ObjectIdMapper objectIdMapper, + RelationMapper relationMapper, + PolicyMapper policyMapper + ) { + this(filterConfig, objectTypeMapper, objectIdMapper, relationMapper, policyMapper, new EmptyResourceMapper()); + } + + public CheckConfig( + AuthzConfig filterConfig, + ObjectTypeMapper objectTypeMapper, + ObjectIdMapper objectIdMapper, + RelationMapper relationMapper, + ResourceMapper baseResourceMapper + ) { + this(filterConfig, objectTypeMapper, objectIdMapper, relationMapper, new StaticPolicyMapper(DEFAULT_POLICY), new EmptyResourceMapper()); + } - public CheckConfig(AuthzConfig filterConfig, ObjectTypeMapper objectTypeMapper, ObjectIdMapper objectIdMapper, RelationMapper relationMapper, PolicyMapper policyMapper) { + public CheckConfig( + AuthzConfig filterConfig, + ObjectTypeMapper objectTypeMapper, + ObjectIdMapper objectIdMapper, + RelationMapper relationMapper, + PolicyMapper policyMapper, + ResourceMapper baseResourceMapper + ) { this.authzCfg = new AuthzConfig(filterConfig); this.objectTypeMapper = objectTypeMapper; this.objectIdMapper = objectIdMapper; this.relationMapper = relationMapper; - this.policyMapper = policyMapper; + this.policyMapper = policyMapper; + this.baseResourceMapper = baseResourceMapper != null ? baseResourceMapper : new EmptyResourceMapper(); } public AsertoAuthorizationManager getAuthManager() { @@ -49,7 +169,7 @@ public AsertoAuthorizationManager getAuthManager() { public AuthzConfig getConfig() { authzCfg.setPolicyMapper(policyMapper); - authzCfg.setResourceMapper(new CheckResourceMapper(objectTypeMapper, objectIdMapper, relationMapper)); + authzCfg.setResourceMapper(new CheckResourceMapper(objectTypeMapper, objectIdMapper, relationMapper, baseResourceMapper)); return authzCfg; } diff --git a/src/main/java/com/aserto/authorizer/MethodAuthorization.java b/src/main/java/com/aserto/authorizer/MethodAuthorization.java index de9dfe3..4f45246 100644 --- a/src/main/java/com/aserto/authorizer/MethodAuthorization.java +++ b/src/main/java/com/aserto/authorizer/MethodAuthorization.java @@ -1,6 +1,5 @@ package com.aserto.authorizer; -import jakarta.servlet.http.HttpServletRequest; import org.springframework.security.authorization.AuthorizationDecision; import org.springframework.stereotype.Component; @@ -18,6 +17,10 @@ import com.aserto.authorizer.mapper.policy.PolicyMapper; import com.aserto.authorizer.mapper.policy.StaticPolicyMapper; import com.aserto.authorizer.mapper.resource.CheckResourceMapper; +import com.aserto.authorizer.mapper.resource.EmptyResourceMapper; +import com.aserto.authorizer.mapper.resource.ResourceMapper; + +import jakarta.servlet.http.HttpServletRequest; /* * This class provides methods to check if the current user is authorized to perform an action. @@ -25,19 +28,21 @@ */ @Component("check") class MethodAuthorization { - private AsertoAuthorizationManager asertoAuthzManager; - private HttpServletRequest httpRequest; + private final AsertoAuthorizationManager asertoAuthzManager; + private final HttpServletRequest httpRequest; private ObjectTypeMapper objectTypeMapper; private ObjectIdMapper objectIdMapper; private RelationMapper relationMapper; private SubjectTypeMapper subjectTypeMapper; private SubjectIdMapper subjectIdMapper; - private PolicyMapper policyMapper; + private PolicyMapper policyMapper; + private ResourceMapper baseResourceMapper; public MethodAuthorization(AuthzConfig authzCfg, HttpServletRequest httpRequest) { asertoAuthzManager = new AsertoAuthorizationManager(authzCfg); this.httpRequest = httpRequest; - this.policyMapper = new StaticPolicyMapper("rebac.check"); + this.policyMapper = new StaticPolicyMapper("rebac.check"); + this.baseResourceMapper = new EmptyResourceMapper(); } public MethodAuthorization objectType(String objectType) { @@ -100,10 +105,21 @@ public MethodAuthorization policyMapper(PolicyMapper policyMapper) { return this; } + public MethodAuthorization baseResourceMapper(ResourceMapper baseResourceMapper) { + this.baseResourceMapper = baseResourceMapper; + return this; + } + public boolean allowed() { validateFields(); - CheckResourceMapper checkResourceMapper = new CheckResourceMapper(objectTypeMapper, objectIdMapper, relationMapper, subjectTypeMapper); + CheckResourceMapper checkResourceMapper = new CheckResourceMapper( + objectTypeMapper, + objectIdMapper, + relationMapper, + subjectTypeMapper, + baseResourceMapper + ); AuthorizationDecision decision; if (subjectIdMapper != null && subjectTypeMapper != null) { diff --git a/src/main/java/com/aserto/authorizer/mapper/resource/CheckResourceMapper.java b/src/main/java/com/aserto/authorizer/mapper/resource/CheckResourceMapper.java index f925a07..1db6ceb 100644 --- a/src/main/java/com/aserto/authorizer/mapper/resource/CheckResourceMapper.java +++ b/src/main/java/com/aserto/authorizer/mapper/resource/CheckResourceMapper.java @@ -1,47 +1,70 @@ package com.aserto.authorizer.mapper.resource; +import java.util.Map; + import com.aserto.authorizer.mapper.check.object.ObjectIdMapper; import com.aserto.authorizer.mapper.check.object.ObjectTypeMapper; import com.aserto.authorizer.mapper.check.object.StaticObjectIdMapper; import com.aserto.authorizer.mapper.check.object.StaticObjectTypeMapper; import com.aserto.authorizer.mapper.check.relation.RelationMapper; import com.aserto.authorizer.mapper.check.relation.StaticRelationMapper; +import com.aserto.authorizer.mapper.check.subject.StaticSubjectTypeMapper; import com.aserto.authorizer.mapper.check.subject.SubjectTypeMapper; import com.google.protobuf.Value; -import jakarta.servlet.http.HttpServletRequest; -import java.util.HashMap; -import java.util.Map; +import jakarta.servlet.http.HttpServletRequest; public class CheckResourceMapper implements ResourceMapper { - private ObjectTypeMapper objectTypeMapper; - private ObjectIdMapper objectIdMapper; - private RelationMapper relationMapper; - - private SubjectTypeMapper subjectTypeMapper; + private final ObjectTypeMapper objectTypeMapper; + private final ObjectIdMapper objectIdMapper; + private final RelationMapper relationMapper; + private final SubjectTypeMapper subjectTypeMapper; + private final ResourceMapper baseResourceMapper; public CheckResourceMapper(String objectType, String objectId, String relation) { - this.objectTypeMapper = new StaticObjectTypeMapper(objectType); - this.objectIdMapper = new StaticObjectIdMapper(objectId); - this.relationMapper = new StaticRelationMapper(relation); + this(new StaticObjectTypeMapper(objectType),new StaticObjectIdMapper(objectId), new StaticRelationMapper(relation)); + } + + public CheckResourceMapper(String objectType, String objectId, String relation, String subjectType) { + this( + new StaticObjectTypeMapper(objectType), + new StaticObjectIdMapper(objectId), + new StaticRelationMapper(relation), + new StaticSubjectTypeMapper(subjectType), + new EmptyResourceMapper() + ); } public CheckResourceMapper(ObjectTypeMapper objectTypeMapper, ObjectIdMapper objectIdMapper, RelationMapper relationMapper) { - this.objectTypeMapper = objectTypeMapper; - this.objectIdMapper = objectIdMapper; - this.relationMapper = relationMapper; + this(objectTypeMapper, objectIdMapper, relationMapper, null, new EmptyResourceMapper()); + } + + public CheckResourceMapper( + ObjectTypeMapper objectTypeMapper, + ObjectIdMapper objectIdMapper, + RelationMapper relationMapper, + ResourceMapper baseResourceMapper + ) { + this(objectTypeMapper, objectIdMapper, relationMapper, null, baseResourceMapper); } - public CheckResourceMapper(ObjectTypeMapper objectTypeMapper, ObjectIdMapper objectIdMapper, RelationMapper relationMapper, SubjectTypeMapper subjectTypeMapper) { + public CheckResourceMapper( + ObjectTypeMapper objectTypeMapper, + ObjectIdMapper objectIdMapper, + RelationMapper relationMapper, + SubjectTypeMapper subjectTypeMapper, + ResourceMapper baseResourceMapper + ) { this.objectTypeMapper = objectTypeMapper; this.objectIdMapper = objectIdMapper; this.relationMapper = relationMapper; this.subjectTypeMapper = subjectTypeMapper; + this.baseResourceMapper = baseResourceMapper != null ? baseResourceMapper : new EmptyResourceMapper(); } @Override public Map getResource(HttpServletRequest request) throws ResourceMapperError { - Map resourceCtx = new HashMap<>(); + Map resourceCtx = baseResourceMapper.getResource(request); resourceCtx.put("object_type", Value.newBuilder().setStringValue(objectTypeMapper.getValue(request)).build()); resourceCtx.put("object_id", Value.newBuilder().setStringValue(objectIdMapper.getValue(request)).build()); resourceCtx.put("relation", Value.newBuilder().setStringValue(relationMapper.getValue(request)).build()); diff --git a/src/main/java/com/aserto/authorizer/mapper/resource/PathParamsResourceMapper.java b/src/main/java/com/aserto/authorizer/mapper/resource/PathParamsResourceMapper.java index 4281e29..7a67639 100644 --- a/src/main/java/com/aserto/authorizer/mapper/resource/PathParamsResourceMapper.java +++ b/src/main/java/com/aserto/authorizer/mapper/resource/PathParamsResourceMapper.java @@ -1,17 +1,18 @@ package com.aserto.authorizer.mapper.resource; -import com.google.protobuf.Value; -import jakarta.servlet.http.HttpServletRequest; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; + import org.springframework.util.AntPathMatcher; import org.springframework.web.method.HandlerMethod; import org.springframework.web.servlet.mvc.condition.PathPatternsRequestCondition; import org.springframework.web.servlet.mvc.method.RequestMappingInfo; import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import com.google.protobuf.Value; + +import jakarta.servlet.http.HttpServletRequest; public class PathParamsResourceMapper implements ResourceMapper { private final RequestMappingHandlerMapping handlerMapping; @@ -23,7 +24,7 @@ public PathParamsResourceMapper(RequestMappingHandlerMapping handlerMapping) { public Map getResource(HttpServletRequest request) throws ResourceMapperError { String uri = request.getRequestURI(); AntPathMatcher apm = new AntPathMatcher(); - String pattern = ""; + String pattern; for (Map.Entry mappingInfo : handlerMapping.getHandlerMethods().entrySet()) { PathPatternsRequestCondition pathPatternsCondition = mappingInfo.getKey().getPathPatternsCondition(); if (pathPatternsCondition == null) { @@ -51,7 +52,7 @@ private Map extractParamsMap(String pattern, String uri, AntPathM } public String[] getPathParams(String uri) { - List params = new ArrayList<>(); + ArrayList params = new ArrayList<>(); String[] tokens = uri.split("/"); for (String token : tokens) { if (token.startsWith("{") && token.endsWith("}")) { @@ -59,6 +60,8 @@ public String[] getPathParams(String uri) { } } - return params.toArray(new String[0]); + String[] pathParams = new String[params.size()]; + params.toArray(pathParams); + return pathParams; } }