Skip to content

Commit

Permalink
Merge pull request quarkusio#29549 from michalvavrik/feature/allow-cu…
Browse files Browse the repository at this point in the history
…stomize-403-from-http-policy

Allow to customize response body of 403 issued by HTTP policy
  • Loading branch information
sberyozkin authored Nov 30, 2022
2 parents c361d8c + 07a8803 commit 3500cdf
Show file tree
Hide file tree
Showing 4 changed files with 171 additions and 2 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package io.quarkus.resteasy.test.security;

import static io.quarkus.resteasy.test.security.ProactiveAuthHttpPolicyCustomForbiddenExMapperTest.CustomForbiddenExceptionMapper.CUSTOM_FORBIDDEN_EXCEPTION_MAPPER;
import static javax.ws.rs.core.Response.Status.FORBIDDEN;
import static org.hamcrest.Matchers.equalTo;

import java.util.function.Supplier;

import javax.annotation.Priority;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Priorities;
import javax.ws.rs.core.Response;
import javax.ws.rs.ext.ExceptionMapper;
import javax.ws.rs.ext.Provider;

import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.asset.StringAsset;
import org.jboss.shrinkwrap.api.spec.JavaArchive;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;

import io.quarkus.security.ForbiddenException;
import io.quarkus.security.test.utils.TestIdentityController;
import io.quarkus.security.test.utils.TestIdentityProvider;
import io.quarkus.test.QuarkusUnitTest;
import io.restassured.RestAssured;

public class ProactiveAuthHttpPolicyCustomForbiddenExMapperTest {

private static final String PROPERTIES = "quarkus.http.auth.basic=true\n" +
"quarkus.http.auth.policy.user-policy.roles-allowed=user\n" +
"quarkus.http.auth.permission.roles.paths=/secured\n" +
"quarkus.http.auth.permission.roles.policy=user-policy";

@RegisterExtension
static QuarkusUnitTest test = new QuarkusUnitTest().setArchiveProducer(new Supplier<>() {
@Override
public JavaArchive get() {
return ShrinkWrap.create(JavaArchive.class)
.addClasses(TestIdentityProvider.class, TestIdentityController.class, CustomForbiddenExceptionMapper.class)
.addAsResource(new StringAsset(PROPERTIES), "application.properties");
}
});

@BeforeAll
public static void setup() {
TestIdentityController.resetRoles().add("a d m i n", "a d m i n", "a d m i n");
}

@Test
public void testDeniedAccessAdminResource() {
RestAssured.given()
.auth().basic("a d m i n", "a d m i n")
.when().get("/secured")
.then()
.statusCode(403)
.body(equalTo(CUSTOM_FORBIDDEN_EXCEPTION_MAPPER));
}

@Path("/secured")
public static class SecuredResource {

@GET
public String get() {
throw new IllegalStateException();
}

}

@Priority(Priorities.USER)
@Provider
public static class CustomForbiddenExceptionMapper implements ExceptionMapper<ForbiddenException> {

public static final String CUSTOM_FORBIDDEN_EXCEPTION_MAPPER = CustomForbiddenExceptionMapper.class.getName();

@Override
public Response toResponse(ForbiddenException e) {
return Response.status(FORBIDDEN).entity(CUSTOM_FORBIDDEN_EXCEPTION_MAPPER).build();
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import io.quarkus.security.AuthenticationCompletionException;
import io.quarkus.security.AuthenticationFailedException;
import io.quarkus.security.AuthenticationRedirectException;
import io.quarkus.security.ForbiddenException;
import io.quarkus.vertx.http.runtime.HttpConfiguration;
import io.quarkus.vertx.http.runtime.security.HttpSecurityRecorder.DefaultAuthFailureHandler;
import io.quarkus.vertx.http.runtime.security.QuarkusHttpUser;
Expand Down Expand Up @@ -91,7 +92,8 @@ public Handler<RoutingContext> vertxFailureHandler(Supplier<Vertx> vertx, Execut
public void handle(RoutingContext request) {
if (request.failure() instanceof AuthenticationFailedException
|| request.failure() instanceof AuthenticationCompletionException
|| request.failure() instanceof AuthenticationRedirectException) {
|| request.failure() instanceof AuthenticationRedirectException
|| request.failure() instanceof ForbiddenException) {
super.handle(request);
} else {
request.next();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package io.quarkus.resteasy.reactive.server.test.security;

import static javax.ws.rs.core.Response.Status.FORBIDDEN;
import static org.hamcrest.Matchers.equalTo;

import java.util.function.Supplier;

import javax.ws.rs.GET;
import javax.ws.rs.Path;

import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.asset.StringAsset;
import org.jboss.shrinkwrap.api.spec.JavaArchive;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;

import io.quarkus.security.ForbiddenException;
import io.quarkus.security.test.utils.TestIdentityController;
import io.quarkus.security.test.utils.TestIdentityProvider;
import io.quarkus.test.QuarkusUnitTest;
import io.quarkus.vertx.web.Route;
import io.restassured.RestAssured;
import io.vertx.core.http.HttpServerResponse;

public class ProactiveAuthHttpPolicyForbiddenHandlerTest {

private static final String PROPERTIES = "quarkus.http.auth.basic=true\n" +
"quarkus.http.auth.policy.user-policy.roles-allowed=user\n" +
"quarkus.http.auth.permission.roles.paths=/secured\n" +
"quarkus.http.auth.permission.roles.policy=user-policy";

@RegisterExtension
static QuarkusUnitTest test = new QuarkusUnitTest().setArchiveProducer(new Supplier<>() {
@Override
public JavaArchive get() {
return ShrinkWrap.create(JavaArchive.class)
.addClasses(TestIdentityProvider.class, TestIdentityController.class, CustomForbiddenFailureHandler.class)
.addAsResource(new StringAsset(PROPERTIES), "application.properties");
}
});

@BeforeAll
public static void setup() {
TestIdentityController.resetRoles().add("a d m i n", "a d m i n", "a d m i n");
}

@Test
public void testDeniedAccessAdminResource() {
RestAssured.given()
.auth().basic("a d m i n", "a d m i n")
.when().get("/secured")
.then()
.statusCode(403)
.body(equalTo(CustomForbiddenFailureHandler.CUSTOM_FORBIDDEN_EXCEPTION_MAPPER));
}

@Path("/secured")
public static class SecuredResource {

@GET
public String get() {
throw new IllegalStateException();
}

}

/**
* Use failure handler as when proactive security is enabled, JAX-RS exception mappers won't do.
*/
public static final class CustomForbiddenFailureHandler {

public static final String CUSTOM_FORBIDDEN_EXCEPTION_MAPPER = CustomForbiddenFailureHandler.class.getName();

@Route(type = Route.HandlerType.FAILURE)
void handle(ForbiddenException e, HttpServerResponse response) {
response.setStatusCode(FORBIDDEN.getStatusCode()).end(CUSTOM_FORBIDDEN_EXCEPTION_MAPPER);
}

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import io.quarkus.runtime.BlockingOperationControl;
import io.quarkus.runtime.ExecutorRecorder;
import io.quarkus.security.AuthenticationFailedException;
import io.quarkus.security.ForbiddenException;
import io.quarkus.security.identity.IdentityProviderManager;
import io.quarkus.security.identity.SecurityIdentity;
import io.quarkus.security.spi.runtime.AuthorizationController;
Expand Down Expand Up @@ -186,7 +187,7 @@ public void onFailure(Throwable failure) {
}
});
} else {
routingContext.fail(403);
routingContext.fail(new ForbiddenException());
}
}

Expand Down

0 comments on commit 3500cdf

Please sign in to comment.