package org.pac4j.vertx; import io.vertx.core.Handler; import io.vertx.core.buffer.Buffer; import io.vertx.core.http.HttpServer; import io.vertx.core.json.JsonObject; import io.vertx.core.logging.Logger; import io.vertx.core.logging.LoggerFactory; import io.vertx.ext.auth.User; import io.vertx.ext.web.Router; import io.vertx.ext.web.RoutingContext; import io.vertx.test.core.VertxTestBase; import org.hamcrest.Matcher; import org.pac4j.core.authorization.authorizer.Authorizer; import org.pac4j.core.authorization.authorizer.RequireAllPermissionsAuthorizer; import org.pac4j.core.context.session.SessionStore; import org.pac4j.vertx.context.session.VertxSessionStore; import org.pac4j.vertx.profile.TestOAuth2Profile; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.function.Consumer; import static org.pac4j.vertx.TestConstants.FORBIDDEN_BODY; import static org.pac4j.vertx.TestConstants.UNAUTHORIZED_BODY; /** * @author Jeremy Prime * @since 2.0.0 */ public abstract class Pac4jAuthHandlerIntegrationTestBase extends VertxTestBase { private static final Logger LOG = LoggerFactory.getLogger(Pac4jAuthHandlerIntegrationTestBase.class); static final String EXCLUDED_PROTECTED_RESOURCE_URL = "/private/public/success.html"; static final String UNPROTECTED_RESOURCE_BODY = "Unprotected resource"; static final String TEST_CLIENT_NAME = "TestOAuth2Client"; static final String REQUIRE_ALL_AUTHORIZER = "requireAllAuthorizer"; protected SessionStore<VertxWebContext> sessionStore = new VertxSessionStore(null); /** * Expose assertThat publicly to enable convenient use of kotlin trait in multi-profile tests * @param actual - the actual value of the value * @param matcher - matcher relating to expected value or possible values * @param <T> - type of the value we're matching */ public <T> void assertThat(T actual, Matcher<T> matcher) { super.assertThat(actual, matcher); } protected void startWebServer(Router router, Handler<RoutingContext> authHandler) throws Exception { HttpServer server = vertx.createHttpServer(); router.route("/private/*").handler(authHandler); router.route(EXCLUDED_PROTECTED_RESOURCE_URL).handler(rc -> rc.response().end(UNPROTECTED_RESOURCE_BODY)); router.route("/private/success.html").handler(loginSuccessHandler()); // Spit out the user router.route().failureHandler(rc -> { final int statusCode = rc.statusCode(); rc.response().setStatusCode(statusCode > 0 ? statusCode : 500); // use status code 500 in the event that vert.x hasn't set one, switch (rc.response().getStatusCode()) { case 401: rc.response().end(UNAUTHORIZED_BODY); break; case 403: rc.response().end(FORBIDDEN_BODY); break; default: LOG.error("Unexpected error in request handling", rc.failure()); rc.response().end("Unexpected error"); } }); CountDownLatch latch = new CountDownLatch(1); server.requestHandler(router::accept).listen(8080, asyncResult -> { if (asyncResult.succeeded()) { latch.countDown(); } else { fail("Http server failed to start so test could not proceed"); } }); assertTrue(latch.await(1L, TimeUnit.SECONDS)); } private Handler<RoutingContext> loginSuccessHandler() { // Just write out the routing context's user principal, we can then validate against this return rc -> { LOG.info("Login success handler called"); final User user = rc.user(); final JsonObject json = user != null ? user.principal() : new JsonObject(); rc.response().end(json.encodePrettily()); }; } protected Map<String, Authorizer> authorizers(final List<String> permissions) { return new HashMap<String, Authorizer>() {{ put(REQUIRE_ALL_AUTHORIZER, authorizer(permissions)); }}; } private RequireAllPermissionsAuthorizer<TestOAuth2Profile> authorizer(final List<String> permissions) { final RequireAllPermissionsAuthorizer<TestOAuth2Profile> authorizer = new RequireAllPermissionsAuthorizer<>(); authorizer.setElements(permissions); return authorizer; } static Consumer<Buffer> validateJsonBody(final Consumer<JsonObject> jsonValidator) { return body -> { final JsonObject json = new JsonObject(body.toString()); jsonValidator.accept(json); }; } }