From 37cacaa4b1944fc4faf814d19bd01225557aa7ab Mon Sep 17 00:00:00 2001 From: Morten Tokle Date: Thu, 4 Apr 2019 15:00:51 +0200 Subject: Introduce SecurityContext --- .../restapi/filter/AthenzRoleFilter.java | 29 +++---------- .../filter/ControllerAuthorizationFilter.java | 8 ++-- .../filter/ControllerAuthorizationFilterTest.java | 49 ++++++++++------------ 3 files changed, 31 insertions(+), 55 deletions(-) (limited to 'controller-server/src') diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/filter/AthenzRoleFilter.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/filter/AthenzRoleFilter.java index fee6bb7c44c..aabb658b7c1 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/filter/AthenzRoleFilter.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/filter/AthenzRoleFilter.java @@ -16,11 +16,11 @@ import com.yahoo.vespa.athenz.api.AthenzPrincipal; import com.yahoo.vespa.athenz.client.zms.ZmsClientException; import com.yahoo.vespa.hosted.controller.Controller; import com.yahoo.vespa.hosted.controller.TenantController; -import com.yahoo.vespa.hosted.controller.athenz.ApplicationAction; -import com.yahoo.vespa.hosted.controller.athenz.impl.AthenzFacade; import com.yahoo.vespa.hosted.controller.api.role.Role; import com.yahoo.vespa.hosted.controller.api.role.RoleMembership; -import com.yahoo.vespa.hosted.controller.api.role.RolePrincipal; +import com.yahoo.vespa.hosted.controller.api.role.SecurityContext; +import com.yahoo.vespa.hosted.controller.athenz.ApplicationAction; +import com.yahoo.vespa.hosted.controller.athenz.impl.AthenzFacade; import com.yahoo.vespa.hosted.controller.tenant.AthenzTenant; import com.yahoo.vespa.hosted.controller.tenant.Tenant; import com.yahoo.vespa.hosted.controller.tenant.UserTenant; @@ -32,7 +32,6 @@ import java.util.Set; import java.util.logging.Logger; import static com.yahoo.vespa.hosted.controller.athenz.HostedAthenzIdentities.SCREWDRIVER_DOMAIN; -import static java.util.Objects.requireNonNull; /** * Enriches the request principal with roles from Athenz. @@ -59,8 +58,8 @@ public class AthenzRoleFilter extends CorsRequestFilterBase { // TODO: No need f protected Optional filterRequest(DiscFilterRequest request) { try { AthenzPrincipal athenzPrincipal = (AthenzPrincipal) request.getUserPrincipal(); - request.setUserPrincipal(new AthenzRolePrincipal(athenzPrincipal.getIdentity(), - membership(athenzPrincipal, request.getUri()))); + request.setAttribute(SecurityContext.ATTRIBUTE_NAME, new SecurityContext(athenzPrincipal, + membership(athenzPrincipal, request.getUri()))); return Optional.empty(); } catch (Exception e) { @@ -113,22 +112,4 @@ public class AthenzRoleFilter extends CorsRequestFilterBase { // TODO: No need f throw new RuntimeException("Failed to authorize operation: (" + e.getMessage() + ")", e); } } - - - private static class AthenzRolePrincipal extends AthenzPrincipal implements RolePrincipal { - - private final RoleMembership roles; - - private AthenzRolePrincipal(AthenzIdentity athenzIdentity, RoleMembership roles) { - super(requireNonNull(athenzIdentity)); - this.roles = requireNonNull(roles); - } - - @Override - public RoleMembership roles() { - return roles; - } - - } - } diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/filter/ControllerAuthorizationFilter.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/filter/ControllerAuthorizationFilter.java index 8c473bcaaec..a1a586b689d 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/filter/ControllerAuthorizationFilter.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/filter/ControllerAuthorizationFilter.java @@ -13,7 +13,7 @@ import com.yahoo.vespa.hosted.controller.Controller; import com.yahoo.vespa.hosted.controller.api.role.Action; import com.yahoo.vespa.hosted.controller.api.role.Role; import com.yahoo.vespa.hosted.controller.api.role.RoleMembership; -import com.yahoo.vespa.hosted.controller.api.role.RolePrincipal; +import com.yahoo.vespa.hosted.controller.api.role.SecurityContext; import java.security.Principal; import java.util.Optional; @@ -47,7 +47,9 @@ public class ControllerAuthorizationFilter extends CorsRequestFilterBase { public Optional filterRequest(DiscFilterRequest request) { try { Principal principal = request.getUserPrincipal(); - if ( ! (principal instanceof RolePrincipal)) + Optional securityContext = Optional.ofNullable((SecurityContext)request.getAttribute(SecurityContext.ATTRIBUTE_NAME)); + + if (securityContext.isEmpty()) return Optional.of(new ErrorResponse(Response.Status.FORBIDDEN, "Access denied")); Action action = Action.from(HttpRequest.Method.valueOf(request.getMethod())); @@ -56,7 +58,7 @@ public class ControllerAuthorizationFilter extends CorsRequestFilterBase { if (Role.everyone.limitedTo(system).allows(action, request.getUri())) return Optional.empty(); - RoleMembership roles = ((RolePrincipal) principal).roles(); + RoleMembership roles = securityContext.get().roles(); if (roles.allows(action, request.getUri())) return Optional.empty(); } diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/filter/ControllerAuthorizationFilterTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/filter/ControllerAuthorizationFilterTest.java index 459320741a9..13c5d219878 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/filter/ControllerAuthorizationFilterTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/filter/ControllerAuthorizationFilterTest.java @@ -7,15 +7,13 @@ import com.yahoo.config.provision.SystemName; import com.yahoo.jdisc.http.HttpRequest.Method; import com.yahoo.jdisc.http.filter.DiscFilterRequest; import com.yahoo.vespa.hosted.controller.ControllerTester; -import com.yahoo.vespa.hosted.controller.restapi.ApplicationRequestToDiscFilterRequestWrapper; import com.yahoo.vespa.hosted.controller.api.role.Role; -import com.yahoo.vespa.hosted.controller.api.role.RoleMembership; -import com.yahoo.vespa.hosted.controller.api.role.RolePrincipal; +import com.yahoo.vespa.hosted.controller.api.role.SecurityContext; +import com.yahoo.vespa.hosted.controller.restapi.ApplicationRequestToDiscFilterRequestWrapper; import org.junit.Test; import java.io.IOException; import java.io.UncheckedIOException; -import java.security.Principal; import java.util.Optional; import java.util.Set; @@ -37,40 +35,35 @@ public class ControllerAuthorizationFilterTest { public void operator() { ControllerTester tester = new ControllerTester(); ControllerAuthorizationFilter filter = createFilter(tester); - RolePrincipal operatorPrincipal = new RolePrincipal() { - @Override public RoleMembership roles() { return Role.hostedOperator.limitedTo(tester.controller().system()); } - @Override public String getName() { return "operator"; } - }; - assertIsAllowed(invokeFilter(filter, createRequest(Method.POST, "/zone/v2/path", operatorPrincipal))); - assertIsAllowed(invokeFilter(filter, createRequest(Method.PUT, "/application/v4/user", operatorPrincipal))); - assertIsAllowed(invokeFilter(filter, createRequest(Method.GET, "/zone/v1/path", operatorPrincipal))); + SecurityContext securityContext = new SecurityContext(() -> "operator", Role.hostedOperator.limitedTo(tester.controller().system())); + + assertIsAllowed(invokeFilter(filter, createRequest(Method.POST, "/zone/v2/path", securityContext))); + assertIsAllowed(invokeFilter(filter, createRequest(Method.PUT, "/application/v4/user", securityContext))); + assertIsAllowed(invokeFilter(filter, createRequest(Method.GET, "/zone/v1/path", securityContext))); } @Test public void unprivileged() { ControllerTester tester = new ControllerTester(); - RolePrincipal everyonePrincipal = new RolePrincipal() { - @Override public RoleMembership roles() { return Role.everyone.limitedTo(tester.controller().system()); } - @Override public String getName() { return "user"; } - }; + SecurityContext securityContext = new SecurityContext(() -> "user", Role.everyone.limitedTo(tester.controller().system())); + ControllerAuthorizationFilter filter = createFilter(tester); - assertIsForbidden(invokeFilter(filter, createRequest(Method.POST, "/zone/v2/path", everyonePrincipal))); - assertIsAllowed(invokeFilter(filter, createRequest(Method.PUT, "/application/v4/user", everyonePrincipal))); - assertIsAllowed(invokeFilter(filter, createRequest(Method.GET, "/zone/v1/path", everyonePrincipal))); + assertIsForbidden(invokeFilter(filter, createRequest(Method.POST, "/zone/v2/path", securityContext))); + assertIsAllowed(invokeFilter(filter, createRequest(Method.PUT, "/application/v4/user", securityContext))); + assertIsAllowed(invokeFilter(filter, createRequest(Method.GET, "/zone/v1/path", securityContext))); } @Test public void unprivilegedInPublic() { ControllerTester tester = new ControllerTester(); tester.zoneRegistry().setSystemName(SystemName.Public); - RolePrincipal everyonePrincipal = new RolePrincipal() { - @Override public RoleMembership roles() { return Role.everyone.limitedTo(tester.controller().system()); } - @Override public String getName() { return "user"; } - }; + + SecurityContext securityContext = new SecurityContext(() -> "user", Role.everyone.limitedTo(tester.controller().system())); + ControllerAuthorizationFilter filter = createFilter(tester); - assertIsForbidden(invokeFilter(filter, createRequest(Method.POST, "/zone/v2/path", everyonePrincipal))); - assertIsForbidden(invokeFilter(filter, createRequest(Method.PUT, "/application/v4/user", everyonePrincipal))); - assertIsAllowed(invokeFilter(filter, createRequest(Method.GET, "/zone/v1/path", everyonePrincipal))); + assertIsForbidden(invokeFilter(filter, createRequest(Method.POST, "/zone/v2/path", securityContext))); + assertIsForbidden(invokeFilter(filter, createRequest(Method.PUT, "/application/v4/user", securityContext))); + assertIsAllowed(invokeFilter(filter, createRequest(Method.GET, "/zone/v1/path", securityContext))); } private static void assertIsAllowed(Optional response) { @@ -96,8 +89,9 @@ public class ControllerAuthorizationFilterTest { .map(response -> new AuthorizationResponse(response.getStatus(), getErrorMessage(responseHandlerMock))); } - private static DiscFilterRequest createRequest(Method method, String path, Principal principal) { - Request request = new Request(path, new byte[0], Request.Method.valueOf(method.name()), principal); + private static DiscFilterRequest createRequest(Method method, String path, SecurityContext securityContext) { + Request request = new Request(path, new byte[0], Request.Method.valueOf(method.name()), securityContext.principal()); + request.getAttributes().put(SecurityContext.ATTRIBUTE_NAME, securityContext); return new ApplicationRequestToDiscFilterRequestWrapper(request); } @@ -118,5 +112,4 @@ public class ControllerAuthorizationFilterTest { this.message = message; } } - } -- cgit v1.2.3