-
Notifications
You must be signed in to change notification settings - Fork 2.7k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Allow to map token roles to deployment-specific SecurityIdentity roles
- Loading branch information
1 parent
63ff7b0
commit 5090b79
Showing
10 changed files
with
380 additions
and
50 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
241 changes: 241 additions & 0 deletions
241
...c/test/java/io/quarkus/vertx/http/security/permission/HttpSecPolicyGrantingRolesTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,241 @@ | ||
package io.quarkus.vertx.http.security.permission; | ||
|
||
import static io.quarkus.vertx.http.security.permission.AbstractHttpSecurityPolicyGrantingPermissionsTest.AuthenticatedUserImpl.ADMIN; | ||
import static io.quarkus.vertx.http.security.permission.AbstractHttpSecurityPolicyGrantingPermissionsTest.AuthenticatedUserImpl.USER; | ||
|
||
import java.util.function.Supplier; | ||
|
||
import jakarta.annotation.security.RolesAllowed; | ||
import jakarta.enterprise.context.ApplicationScoped; | ||
import jakarta.enterprise.event.Observes; | ||
import jakarta.inject.Inject; | ||
|
||
import org.hamcrest.Matchers; | ||
import org.jboss.shrinkwrap.api.ShrinkWrap; | ||
import org.jboss.shrinkwrap.api.spec.JavaArchive; | ||
import org.junit.jupiter.api.Test; | ||
import org.junit.jupiter.api.extension.RegisterExtension; | ||
|
||
import io.quarkus.arc.Arc; | ||
import io.quarkus.security.AuthenticationFailedException; | ||
import io.quarkus.security.ForbiddenException; | ||
import io.quarkus.security.PermissionsAllowed; | ||
import io.quarkus.security.UnauthorizedException; | ||
import io.quarkus.security.identity.SecurityIdentity; | ||
import io.quarkus.security.runtime.SecurityIdentityAssociation; | ||
import io.quarkus.security.test.utils.TestIdentityController; | ||
import io.quarkus.security.test.utils.TestIdentityProvider; | ||
import io.quarkus.test.QuarkusUnitTest; | ||
import io.quarkus.vertx.http.runtime.security.QuarkusHttpUser; | ||
import io.quarkus.vertx.http.security.CustomPermission; | ||
import io.quarkus.vertx.http.security.CustomPermissionWithActions; | ||
import io.quarkus.vertx.http.security.permission.AbstractHttpSecurityPolicyGrantingPermissionsTest.AuthenticatedUser; | ||
import io.quarkus.vertx.http.security.permission.AbstractHttpSecurityPolicyGrantingPermissionsTest.AuthenticatedUserImpl; | ||
import io.restassured.RestAssured; | ||
import io.smallrye.mutiny.Uni; | ||
import io.vertx.core.Handler; | ||
import io.vertx.ext.web.Router; | ||
import io.vertx.ext.web.RoutingContext; | ||
|
||
public class HttpSecPolicyGrantingRolesTest { | ||
|
||
@RegisterExtension | ||
static QuarkusUnitTest test = new QuarkusUnitTest().setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class) | ||
.addClasses(TestIdentityController.class, TestIdentityProvider.class, RolesPathHandler.class, | ||
CDIBean.class, CustomPermission.class, CustomPermissionWithActions.class, AuthenticatedUser.class, | ||
AuthenticatedUserImpl.class) | ||
.addAsResource("conf/http-roles-grant-config.properties", "application.properties")); | ||
|
||
@Test | ||
public void mapRolesToRolesSecuredWithRolesAllowed() { | ||
assertSuccess(ADMIN, "/test/new-admin-roles-blocking"); | ||
assertSuccess(ADMIN, "/test/new-admin-roles"); | ||
assertSuccess(ADMIN, "/test/new-admin-roles2"); | ||
assertSuccess(ADMIN, "/test/old-admin-roles-blocking"); | ||
assertSuccess(ADMIN, "/test/old-admin-roles"); | ||
assertSuccess(ADMIN, "/test/multiple-new-roles-1"); | ||
assertSuccess(ADMIN, "/test/multiple-new-roles-2"); | ||
assertSuccess(ADMIN, "/test/multiple-new-roles-3"); | ||
assertForbidden(USER, "/test/new-admin-roles-blocking"); | ||
assertForbidden(USER, "/test/new-admin-roles"); | ||
assertForbidden(USER, "/test/new-admin-roles2"); | ||
assertForbidden(USER, "/test/old-admin-roles-blocking"); | ||
assertForbidden(USER, "/test/old-admin-roles"); | ||
assertSuccess(USER, "/test/multiple-new-roles-1"); | ||
assertSuccess(USER, "/test/multiple-new-roles-2"); | ||
assertSuccess(USER, "/test/multiple-new-roles-3"); | ||
} | ||
|
||
@Test | ||
public void mapRolesToRolesNoSecurityAnnotation() { | ||
assertSuccess(ADMIN, "/test/roles-allowed-path"); | ||
assertForbidden(ADMIN, "/test/always-denied"); | ||
assertForbidden(USER, "/test/roles-allowed-path"); | ||
assertForbidden(USER, "/test/always-denied"); | ||
} | ||
|
||
@Test | ||
public void mapRolesToBothPermissionsAndRoles() { | ||
assertSuccess(ADMIN, "/test/roles-and-perms-1"); | ||
assertSuccess(ADMIN, "/test/roles-and-perms-2"); | ||
assertForbidden(USER, "/test/roles-and-perms-1"); | ||
assertForbidden(USER, "/test/roles-and-perms-2"); | ||
} | ||
|
||
@ApplicationScoped | ||
public static class RolesPathHandler { | ||
|
||
@Inject | ||
CDIBean cdiBean; | ||
|
||
public void setup(@Observes Router router) { | ||
router.route("/test/new-admin-roles-blocking").blockingHandler(new RouteHandler(() -> { | ||
cdiBean.newRolesBlocking(); | ||
return Uni.createFrom().nullItem(); | ||
})); | ||
router.route("/test/new-admin-roles").handler(new RouteHandler(cdiBean::newRoles)); | ||
router.route("/test/new-admin-roles2").handler(new RouteHandler(cdiBean::newRoles2)); | ||
router.route("/test/old-admin-roles-blocking").blockingHandler(new RouteHandler(() -> { | ||
cdiBean.oldRolesBlocking(); | ||
return Uni.createFrom().nullItem(); | ||
})); | ||
router.route("/test/old-admin-roles").handler(new RouteHandler(cdiBean::oldRoles)); | ||
router.route("/test/multiple-new-roles-1").handler(new RouteHandler(cdiBean::multipleNewRoles1)); | ||
router.route("/test/multiple-new-roles-2").handler(new RouteHandler(cdiBean::multipleNewRoles2)); | ||
router.route("/test/multiple-new-roles-3").handler(new RouteHandler(cdiBean::multipleNewRoles3)); | ||
router.route("/test/roles-allowed-path").handler(new RouteHandler(cdiBean::rolesAllowedPath)); | ||
router.route("/test/always-denied").handler(new RouteHandler(cdiBean::alwaysDeniedByHttpSecPolicy)); | ||
router.route("/test/roles-and-perms-1").handler(new RouteHandler(cdiBean::rolesAndPermissions1)); | ||
router.route("/test/roles-and-perms-2").handler(new RouteHandler(cdiBean::rolesAndPermissions2)); | ||
} | ||
} | ||
|
||
private static final class RouteHandler implements Handler<RoutingContext> { | ||
|
||
private final Supplier<Uni<Void>> callService; | ||
|
||
private RouteHandler(Supplier<Uni<Void>> callService) { | ||
this.callService = callService; | ||
} | ||
|
||
@Override | ||
public void handle(RoutingContext event) { | ||
// activate context so that we can use CDI beans | ||
Arc.container().requestContext().activate(); | ||
// set identity used by security checks performed by standard security interceptors | ||
QuarkusHttpUser user = (QuarkusHttpUser) event.user(); | ||
Arc.container().instance(SecurityIdentityAssociation.class).get().setIdentity(user.getSecurityIdentity()); | ||
|
||
callService.get().subscribe().with(unused -> { | ||
String ret = user.getSecurityIdentity().getPrincipal().getName() + | ||
":" + event.normalizedPath(); | ||
event.response().end(ret); | ||
}, throwable -> { | ||
if (throwable instanceof UnauthorizedException) { | ||
event.response().setStatusCode(401); | ||
} else if (throwable instanceof ForbiddenException) { | ||
event.response().setStatusCode(403); | ||
} else { | ||
event.response().setStatusCode(500); | ||
} | ||
event.end(); | ||
}); | ||
} | ||
} | ||
|
||
private void assertSuccess(AuthenticatedUser user, String... paths) { | ||
user.authenticate(); | ||
for (var path : paths) { | ||
RestAssured | ||
.given() | ||
.auth() | ||
.basic(user.role(), user.role()) | ||
.get(path) | ||
.then() | ||
.statusCode(200) | ||
.body(Matchers.is(user.role() + ":" + path)); | ||
} | ||
} | ||
|
||
private void assertForbidden(AuthenticatedUser user, String... paths) { | ||
user.authenticate(); | ||
for (var path : paths) { | ||
RestAssured | ||
.given() | ||
.auth() | ||
.basic(user.role(), user.role()) | ||
.get(path) | ||
.then() | ||
.statusCode(403); | ||
} | ||
} | ||
|
||
@ApplicationScoped | ||
public static class CDIBean { | ||
|
||
@Inject | ||
SecurityIdentity identity; | ||
|
||
@RolesAllowed("Admin1") | ||
public void newRolesBlocking() { | ||
// NOTHING TO DO | ||
} | ||
|
||
@RolesAllowed("Admin1") | ||
public Uni<Void> newRoles() { | ||
return Uni.createFrom().nullItem(); | ||
} | ||
|
||
@RolesAllowed("Admin2") | ||
public Uni<Void> newRoles2() { | ||
return Uni.createFrom().nullItem(); | ||
} | ||
|
||
@RolesAllowed("admin") | ||
public void oldRolesBlocking() { | ||
// NOTHING TO DO | ||
} | ||
|
||
@RolesAllowed("admin") | ||
public Uni<Void> oldRoles() { | ||
return Uni.createFrom().nullItem(); | ||
} | ||
|
||
@RolesAllowed("Janet") | ||
public Uni<Void> multipleNewRoles1() { | ||
return Uni.createFrom().nullItem(); | ||
} | ||
|
||
@RolesAllowed("Monica") | ||
public Uni<Void> multipleNewRoles2() { | ||
return Uni.createFrom().nullItem(); | ||
} | ||
|
||
@RolesAllowed("Robin") | ||
public Uni<Void> multipleNewRoles3() { | ||
return Uni.createFrom().nullItem(); | ||
} | ||
|
||
@RolesAllowed("Admin3") | ||
public Uni<Void> rolesAndPermissions1() { | ||
return Uni.createFrom().nullItem(); | ||
} | ||
|
||
@PermissionsAllowed("jump") | ||
public Uni<Void> rolesAndPermissions2() { | ||
return Uni.createFrom().nullItem(); | ||
} | ||
|
||
public Uni<Void> rolesAllowedPath() { | ||
if (identity.hasRole("Admin1")) { | ||
return Uni.createFrom().nullItem(); | ||
} else { | ||
return Uni.createFrom().failure(AuthenticationFailedException::new); | ||
} | ||
} | ||
|
||
public Uni<Void> alwaysDeniedByHttpSecPolicy() { | ||
return Uni.createFrom().failure(IllegalAccessException::new); | ||
} | ||
} | ||
} |
27 changes: 27 additions & 0 deletions
27
extensions/vertx-http/deployment/src/test/resources/conf/http-roles-grant-config.properties
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
quarkus.http.auth.basic=true | ||
quarkus.http.auth.policy.t1.roles.admin=Admin1 | ||
quarkus.http.auth.permission.t1.paths=/* | ||
quarkus.http.auth.permission.t1.policy=t1 | ||
quarkus.http.auth.policy.t2.roles.admin=Admin2 | ||
quarkus.http.auth.permission.t2.paths=/* | ||
quarkus.http.auth.permission.t2.policy=t2 | ||
quarkus.http.auth.policy.t3.roles.user=Janet,Robin | ||
quarkus.http.auth.policy.t3.roles.admin=Janet,Robin | ||
quarkus.http.auth.permission.t3.paths=/test/multiple-new-roles-1 | ||
quarkus.http.auth.permission.t3.policy=t3 | ||
quarkus.http.auth.policy.t4.roles.user=Monica,Robin | ||
quarkus.http.auth.policy.t4.roles.admin=Monica,Robin | ||
quarkus.http.auth.permission.t4.paths=/test/multiple-new-roles-2,/test/multiple-new-roles-3 | ||
quarkus.http.auth.permission.t4.policy=t4 | ||
quarkus.http.auth.policy.t5.roles-allowed=admin | ||
quarkus.http.auth.policy.t5.roles.admin=Admin1 | ||
quarkus.http.auth.permission.t5.paths=/test/roles-allowed-path | ||
quarkus.http.auth.permission.t5.policy=t5 | ||
quarkus.http.auth.policy.t6.roles-allowed=Admin1 | ||
quarkus.http.auth.policy.t6.roles.admin=Admin1 | ||
quarkus.http.auth.permission.t6.paths=/test/always-denied | ||
quarkus.http.auth.permission.t6.policy=t6 | ||
quarkus.http.auth.policy.t7.roles.admin=Admin3 | ||
quarkus.http.auth.policy.t7.permissions.admin=jump | ||
quarkus.http.auth.permission.t7.paths=/test/roles-and-perms-1,/test/roles-and-perms-2 | ||
quarkus.http.auth.permission.t7.policy=t7 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.