package org.pac4j.vertx; import io.vertx.core.Handler; import io.vertx.core.http.HttpClient; import io.vertx.core.http.HttpClientRequest; import io.vertx.core.http.HttpClientResponse; import io.vertx.core.http.HttpMethod; import io.vertx.core.logging.Logger; import io.vertx.core.logging.LoggerFactory; import org.junit.Test; import org.pac4j.core.client.Clients; import org.pac4j.core.context.Pac4jConstants; import org.pac4j.vertx.handler.impl.LogoutHandler; import org.pac4j.vertx.handler.impl.LogoutHandlerOptions; import org.pac4j.vertx.handler.impl.CallbackHandlerOptions; import org.pac4j.vertx.handler.impl.SecurityHandlerOptions; import java.util.Arrays; import java.util.concurrent.TimeUnit; import java.util.function.Consumer; import static org.pac4j.vertx.TestConstants.FORBIDDEN_BODY; /** * @author Jeremy Prime * @since 2.0.0 */ @SuppressWarnings("RedundantThrows") public class StatefulPac4jAuthHandlerSingleProfileIntegrationTest extends StatefulPac4jAuthHandlerIntegrationTestBase { private static final String LOGOUT_URL_FOR_CLIENT = "/logout?url=/"; private static final Logger LOG = LoggerFactory.getLogger(StatefulPac4jAuthHandlerSingleProfileIntegrationTest.class); @Test public void testSuccessfulOAuth2LoginWithoutAuthorities() throws Exception { LOG.info("testSuccessfulOAuth2LoginWithoutAuthorities"); LOG.debug("Starting auth provider mimic"); startOAuthProviderMimic("testUser1"); // Start a web server with no required authorities (i.e. only authentication required) for the secured resource startWebServer(TEST_OAUTH2_SUCCESS_URL, optionsWithBothNamesProvided(), callbackHandlerOptions(), null); loginSuccessfullyExpectingAuthorizedUser(Void -> testComplete()); await(2, TimeUnit.SECONDS); } @Test public void testSuccessfulOAuth2LoginWithoutAuthorizerName() throws Exception { // This should let any user access a protected resource - we'll set up with a user who doesn't have // adequate permissions if the authorizer is used startOAuthProviderMimic("testUser2"); final String[] permissions = { "permission1", "permission2" }; startWebServer(TEST_OAUTH2_SUCCESS_URL, new SecurityHandlerOptions().setClients(TEST_CLIENT_NAME), callbackHandlerOptions(), Arrays.asList(permissions)); loginSuccessfullyExpectingAuthorizedUser(Void -> testComplete()); await(1, TimeUnit.SECONDS); } @Test public void testSuccessfulOAuth2LoginWithInsufficientAuthorities() throws Exception { startOAuthProviderMimic("testUser2"); final String[] permissions = { "permission1", "permission2" }; startWebServer(TEST_OAUTH2_SUCCESS_URL, optionsWithBothNamesProvided(), callbackHandlerOptions(), Arrays.asList(permissions)); loginSuccessfullyExpectingUnauthorizedUser(Void -> testComplete()); await(1, TimeUnit.SECONDS); } @Test public void testSuccessfulOAuth2LoginWithSufficientAuthorities() throws Exception { startOAuthProviderMimic("testUser2"); final String[] permissions = {"permission1"}; startWebServer(TEST_OAUTH2_SUCCESS_URL, optionsWithBothNamesProvided(), callbackHandlerOptions(), Arrays.asList(permissions)); loginSuccessfullyExpectingAuthorizedUser(Void -> testComplete()); await(1, TimeUnit.SECONDS); } // Test that subsequent access following successful login doesn't require another set of redirects, assuming session // is maintained @Test public void testSubsequentAccessFollowingSuccessfulLogin() throws Exception { startOAuthProviderMimic("testUser1"); // Start a web server with no required authorities (i.e. only authentication required) for the secured resource startWebServer(TEST_OAUTH2_SUCCESS_URL, optionsWithBothNamesProvided(), callbackHandlerOptions(), null); HttpClient client = vertx.createHttpClient(); loginSuccessfullyExpectingAuthorizedUser(client, Void -> { final HttpClientRequest successfulRequest = client.get(8080, "localhost", "/private/success.html"); getSessionCookie().ifPresent(cookie -> successfulRequest.putHeader("cookie", cookie)); successfulRequest.handler(resp -> { assertEquals(200, resp.statusCode()); validateInitialLoginSuccessResponse(resp, v -> testComplete()); }).end(); }); await(1, TimeUnit.SECONDS); } @Test public void testLogoutRequiresSubsequentReauthentication() throws Exception { startOAuthProviderMimic("testUser1"); // Start a web server with no required authorities (i.e. only authentication required) for the secured resource startWebServer(TEST_OAUTH2_SUCCESS_URL, optionsWithBothNamesProvided(), callbackHandlerOptions(), null, (router, config) -> router.route(HttpMethod.GET, "/logout") .handler(new LogoutHandler(vertx, sessionStore, new LogoutHandlerOptions(), config))); HttpClient client = vertx.createHttpClient(); loginSuccessfullyExpectingAuthorizedUser(client, Void -> { LOG.info("Successfully logged in, now about to logout"); logout(client, response -> { // We don't need to actually bother to redirect to the url, we've validated that we're directed to following // logout, what we do need to do, is with our session established, try and connect to a protected url again // and ensure we testGet a redirect to the auth provider as expected final HttpClientRequest successfulRequest = client.get(8080, "localhost", "/private/success.html"); getSessionCookie().ifPresent(cookie -> successfulRequest.putHeader("cookie", cookie)); successfulRequest.handler(resp -> { assertEquals(302, resp.statusCode()); final String redirectToUrl = resp.getHeader("location"); LOG.info("RedirectTo: " + redirectToUrl); // Check we're redirecting to a url derived from the auth provider url we passed to the web server assertTrue(redirectToUrl.startsWith(TEST_OAUTH2_SUCCESS_URL)); testComplete(); }) .end(); }); }); await(1, TimeUnit.SECONDS); } @Override protected CallbackHandlerOptions callbackHandlerOptions() { return new CallbackHandlerOptions().setDefaultUrl(Pac4jConstants.DEFAULT_URL).setMultiProfile(false); } @Override protected Clients clients(final String baseAuthUrl) { return new Clients(oAuth2Client(baseAuthUrl), testOAuth1Client()); } private void loginSuccessfullyExpectingUnauthorizedUser(final Consumer<Void> subsequentActions) throws Exception { loginSuccessfully(finalRedirectResponse -> { assertEquals(403, finalRedirectResponse.statusCode()); finalRedirectResponse.bodyHandler(body -> { assertEquals(FORBIDDEN_BODY, body.toString()); subsequentActions.accept(null); }); }); } private void loginSuccessfully(final Handler<HttpClientResponse> finalResponseHandler) throws Exception { HttpClient client = vertx.createHttpClient(); loginSuccessfully(client, finalResponseHandler); } private void logout(final HttpClient client, final Handler<HttpClientResponse> postLogoutActions) { final HttpClientRequest logoutRequest = client.get(8080, "localhost", LOGOUT_URL_FOR_CLIENT); getSessionCookie().ifPresent(cookie -> logoutRequest.putHeader("cookie", cookie)); logoutRequest.handler(response -> { assertEquals(302, response.statusCode()); final String redirectToUrl = response.getHeader("location"); assertEquals(redirectToUrl, "/"); postLogoutActions.handle(response); }).end(); } }