diff --git a/testsuite/integration/elytron-oidc-client/src/test/java/org/wildfly/test/integration/elytron/oidc/client/KeycloakConfiguration.java b/testsuite/integration/elytron-oidc-client/src/test/java/org/wildfly/test/integration/elytron/oidc/client/KeycloakConfiguration.java index f3011322990d..5da1e4860801 100644 --- a/testsuite/integration/elytron-oidc-client/src/test/java/org/wildfly/test/integration/elytron/oidc/client/KeycloakConfiguration.java +++ b/testsuite/integration/elytron-oidc-client/src/test/java/org/wildfly/test/integration/elytron/oidc/client/KeycloakConfiguration.java @@ -20,7 +20,9 @@ import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.List; +import java.util.Map; import org.keycloak.representations.AccessTokenResponse; import org.keycloak.representations.idm.ClientRepresentation; @@ -45,6 +47,14 @@ public class KeycloakConfiguration { public static final String ALICE_PASSWORD = "alice123+"; public static final String BOB = "bob"; public static final String BOB_PASSWORD = "bob123+"; + public static final String ALLOWED_ORIGIN = "http://somehost"; + + public enum ClientAppType { + OIDC_CLIENT, + DIRECT_ACCESS_GRANT_OIDC_CLIENT, + BEARER_ONLY_CLIENT, + CORS_CLIENT + } /** * Configure RealmRepresentation as follows: @@ -58,7 +68,7 @@ public class KeycloakConfiguration { * */ public static RealmRepresentation getRealmRepresentation(final String realmName, String clientSecret, - String clientHostName, int clientPort, String... clientApps) { + String clientHostName, int clientPort, Map clientApps) { return createRealm(realmName, clientSecret, clientHostName, clientPort, clientApps); } @@ -74,8 +84,21 @@ public static String getAdminAccessToken(String authServerUrl) { .as(AccessTokenResponse.class).getToken(); } + public static String getAccessToken(String authServerUrl, String realmName, String username, String password, String clientId, String clientSecret) { + return RestAssured + .given() + .param("grant_type", "password") + .param("username", username) + .param("password", password) + .param("client_id", clientId) + .param("client_secret", clientSecret) + .when() + .post(authServerUrl + "/realms/" + realmName + "/protocol/openid-connect/token") + .as(AccessTokenResponse.class).getToken(); + } + private static RealmRepresentation createRealm(String name, String clientSecret, - String clientHostName, int clientPort, String... clientApps) { + String clientHostName, int clientPort, Map clientApps) { RealmRepresentation realm = new RealmRepresentation(); realm.setRealm(name); @@ -94,8 +117,21 @@ private static RealmRepresentation createRealm(String name, String clientSecret, realm.getRoles().getRealm().add(new RoleRepresentation(USER_ROLE, null, false)); realm.getRoles().getRealm().add(new RoleRepresentation(JBOSS_ADMIN_ROLE, null, false)); - for (String clientApp : clientApps) { - realm.getClients().add(createWebAppClient(clientApp, clientSecret, clientHostName, clientPort, clientApp)); + for (String clientApp : clientApps.keySet()) { + ClientAppType clientAppType = clientApps.get(clientApp); + switch (clientAppType) { + case DIRECT_ACCESS_GRANT_OIDC_CLIENT: + realm.getClients().add(createWebAppClient(clientApp, clientSecret, clientHostName, clientPort, clientApp, true)); + break; + case BEARER_ONLY_CLIENT: + realm.getClients().add(createBearerOnlyClient(clientApp)); + break; + case CORS_CLIENT: + realm.getClients().add(createWebAppClient(clientApp, clientSecret, clientHostName, clientPort, clientApp, true, ALLOWED_ORIGIN)); + break; + default: + realm.getClients().add(createWebAppClient(clientApp, clientSecret, clientHostName, clientPort, clientApp, false)); + } } realm.getUsers().add(createUser(ALICE, ALICE_PASSWORD, Arrays.asList(USER_ROLE, JBOSS_ADMIN_ROLE))); @@ -103,7 +139,13 @@ private static RealmRepresentation createRealm(String name, String clientSecret, return realm; } - private static ClientRepresentation createWebAppClient(String clientId, String clientSecret, String clientHostName, int clientPort, String clientApp) { + private static ClientRepresentation createWebAppClient(String clientId, String clientSecret, String clientHostName, int clientPort, + String clientApp, boolean directAccessGrantEnabled) { + return createWebAppClient(clientId, clientSecret, clientHostName, clientPort, clientApp, directAccessGrantEnabled, null); + } + + private static ClientRepresentation createWebAppClient(String clientId, String clientSecret, String clientHostName, int clientPort, + String clientApp, boolean directAccessGrantEnabled, String allowedOrigin) { ClientRepresentation client = new ClientRepresentation(); client.setClientId(clientId); client.setPublicClient(false); @@ -111,6 +153,18 @@ private static ClientRepresentation createWebAppClient(String clientId, String c //client.setRedirectUris(Arrays.asList("*")); client.setRedirectUris(Arrays.asList("http://" + clientHostName + ":" + clientPort + "/" + clientApp + "/*")); client.setEnabled(true); + client.setDirectAccessGrantsEnabled(directAccessGrantEnabled); + if (allowedOrigin != null) { + client.setWebOrigins(Collections.singletonList(allowedOrigin)); + } + return client; + } + + private static ClientRepresentation createBearerOnlyClient(String clientId) { + ClientRepresentation client = new ClientRepresentation(); + client.setClientId(clientId); + client.setBearerOnly(true); + client.setEnabled(true); return client; } diff --git a/testsuite/integration/elytron-oidc-client/src/test/java/org/wildfly/test/integration/elytron/oidc/client/OidcBaseTest.java b/testsuite/integration/elytron-oidc-client/src/test/java/org/wildfly/test/integration/elytron/oidc/client/OidcBaseTest.java index b0074375623a..ef8ff390c173 100644 --- a/testsuite/integration/elytron-oidc-client/src/test/java/org/wildfly/test/integration/elytron/oidc/client/OidcBaseTest.java +++ b/testsuite/integration/elytron-oidc-client/src/test/java/org/wildfly/test/integration/elytron/oidc/client/OidcBaseTest.java @@ -25,6 +25,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.junit.Assume.assumeTrue; +import static org.wildfly.test.integration.elytron.oidc.client.KeycloakConfiguration.ALLOWED_ORIGIN; import java.io.IOException; import java.net.HttpURLConnection; @@ -34,16 +35,19 @@ import java.util.LinkedList; import java.util.List; +import org.apache.http.Header; import org.apache.http.HttpResponse; import org.apache.http.NameValuePair; import org.apache.http.client.CookieStore; import org.apache.http.client.HttpClient; import org.apache.http.client.entity.UrlEncodedFormEntity; import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpOptions; import org.apache.http.client.methods.HttpPost; import org.apache.http.client.utils.HttpClientUtils; import org.apache.http.impl.client.BasicCookieStore; import org.apache.http.impl.client.BasicResponseHandler; +import org.apache.http.impl.client.HttpClients; import org.apache.http.impl.client.LaxRedirectStrategy; import org.apache.http.message.BasicNameValuePair; import org.apache.http.protocol.BasicHttpContext; @@ -62,6 +66,7 @@ import org.junit.Test; import org.keycloak.representations.idm.RealmRepresentation; import org.testcontainers.DockerClientFactory; +import org.wildfly.common.iteration.CodePointIterator; import org.wildfly.security.jose.util.JsonSerialization; import io.restassured.RestAssured; @@ -85,6 +90,26 @@ public abstract class OidcBaseTest { public static final String AUTH_SERVER_URL_APP = "AuthServerUrlOidcApp"; public static final String WRONG_PROVIDER_URL_APP = "WrongProviderUrlOidcApp"; public static final String WRONG_SECRET_APP = "WrongSecretOidcApp"; + public static final String DIRECT_ACCCESS_GRANT_ENABLED_CLIENT = "DirectAccessGrantEnabledClient"; + public static final String BEARER_ONLY_AUTH_SERVER_URL_APP = "AuthServerUrlBearerOnlyApp"; + public static final String BEARER_ONLY_PROVIDER_URL_APP = "ProviderUrlBearerOnlyApp"; + public static final String BASIC_AUTH_PROVIDER_URL_APP = "BasicAuthProviderUrlApp"; + public static final String CORS_PROVIDER_URL_APP = "CorsApp"; + private static final String WRONG_PASSWORD = "WRONG_PASSWORD"; + private static final String ACCESS_CONTROL_ALLOW_ORIGIN = "Access-Control-Allow-Origin"; + private static final String ACCESS_CONTROL_ALLOW_CREDENTIALS = "Access-Control-Allow-Credentials"; + private static final String ACCESS_CONTROL_ALLOW_METHODS = "Access-Control-Allow-Methods"; + private static final String ACCESS_CONTROL_ALLOW_HEADERS = "Access-Control-Allow-Headers"; + static final String ORIGIN = "Origin"; + static final String ACCESS_CONTROL_REQUEST_METHOD = "Access-Control-Request-Method"; + static final String ACCESS_CONTROL_REQUEST_HEADERS = "Access-Control-Request-Headers"; + public static final String CORS_CLIENT = "CorsClient"; + + private enum BearerAuthType { + BEARER, + QUERY_PARAM, + BASIC + } private static boolean isDockerAvailable() { try { @@ -163,6 +188,161 @@ public void testWrongClientSecret() throws Exception { loginToApp(WRONG_SECRET_APP, org.wildfly.test.integration.elytron.oidc.client.KeycloakConfiguration.ALICE, org.wildfly.test.integration.elytron.oidc.client.KeycloakConfiguration.ALICE_PASSWORD, HttpURLConnection.HTTP_FORBIDDEN, null); } + /** + * Tests that use a bearer token. + */ + + @Test + @OperateOnDeployment(BEARER_ONLY_AUTH_SERVER_URL_APP) + public void testSucessfulBearerOnlyAuthenticationWithAuthServerUrl() throws Exception { + performBearerAuthentication(BEARER_ONLY_AUTH_SERVER_URL_APP, KeycloakConfiguration.ALICE, KeycloakConfiguration.ALICE_PASSWORD, + SimpleServlet.RESPONSE_BODY, null, BearerAuthType.BEARER, DIRECT_ACCCESS_GRANT_ENABLED_CLIENT, CLIENT_SECRET); + } + + @Test + @OperateOnDeployment(BEARER_ONLY_PROVIDER_URL_APP) + public void testSucessfulBearerOnlyAuthenticationWithProviderUrl() throws Exception { + performBearerAuthentication(BEARER_ONLY_PROVIDER_URL_APP, KeycloakConfiguration.ALICE, KeycloakConfiguration.ALICE_PASSWORD, + SimpleServlet.RESPONSE_BODY, null, BearerAuthType.BEARER, DIRECT_ACCCESS_GRANT_ENABLED_CLIENT, CLIENT_SECRET); + } + + @Test + @OperateOnDeployment(BEARER_ONLY_PROVIDER_URL_APP) + public void testWrongToken() throws Exception { + String wrongToken = "eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJrNmhQYTdHdmdrajdFdlhLeFAtRjFLZkNSUk85Q3kwNC04YzFqTERWOXNrIn0.eyJleHAiOjE2NTc2NjExODksImlhdCI6MTY1NzY2MTEyOSwianRpIjoiZThiZGQ3MWItYTA2OC00Mjc3LTkyY2UtZWJkYmU2MDVkMzBhIiwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo4MDgwL3JlYWxtcy9tYXN0ZXIiLCJhdWQiOlsibXlyZWFsbS1yZWFsbSIsIm1hc3Rlci1yZWFsbSIsImFjY291bnQiXSwic3ViIjoiZTliOGE2OWItM2RlNy00ZDYzLWFjYmItMmYyNTRhMDM1MjVkIiwidHlwIjoiQmVhcmVyIiwiYXpwIjoidGVzdC13ZWJhcHAiLCJzZXNzaW9uX3N0YXRlIjoiMTQ1OTdhMmUtOGM1Ni00YzkwLWI3NjAtZWFjYzczNWU1Zjc1IiwiYWNyIjoiMSIsInJlYWxtX2FjY2VzcyI6eyJyb2xlcyI6WyJjcmVhdGUtcmVhbG0iLCJkZWZhdWx0LXJvbGVzLW1hc3RlciIsIm9mZmxpbmVfYWNjZXNzIiwiYWRtaW4iLCJ1bWFfYXV0aG9yaXphdGlvbiIsInVzZXIiXX0sInJlc291cmNlX2FjY2VzcyI6eyJteXJlYWxtLXJlYWxtIjp7InJvbGVzIjpbInZpZXctcmVhbG0iLCJ2aWV3LWlkZW50aXR5LXByb3ZpZGVycyIsIm1hbmFnZS1pZGVudGl0eS1wcm92aWRlcnMiLCJpbXBlcnNvbmF0aW9uIiwiY3JlYXRlLWNsaWVudCIsIm1hbmFnZS11c2VycyIsInF1ZXJ5LXJlYWxtcyIsInZpZXctYXV0aG9yaXphdGlvbiIsInF1ZXJ5LWNsaWVudHMiLCJxdWVyeS11c2VycyIsIm1hbmFnZS1ldmVudHMiLCJtYW5hZ2UtcmVhbG0iLCJ2aWV3LWV2ZW50cyIsInZpZXctdXNlcnMiLCJ2aWV3LWNsaWVudHMiLCJtYW5hZ2UtYXV0aG9yaXphdGlvbiIsIm1hbmFnZS1jbGllbnRzIiwicXVlcnktZ3JvdXBzIl19LCJtYXN0ZXItcmVhbG0iOnsicm9sZXMiOlsidmlldy1yZWFsbSIsInZpZXctaWRlbnRpdHktcHJvdmlkZXJzIiwibWFuYWdlLWlkZW50aXR5LXByb3ZpZGVycyIsImltcGVyc29uYXRpb24iLCJjcmVhdGUtY2xpZW50IiwibWFuYWdlLXVzZXJzIiwicXVlcnktcmVhbG1zIiwidmlldy1hdXRob3JpemF0aW9uIiwicXVlcnktY2xpZW50cyIsInF1ZXJ5LXVzZXJzIiwibWFuYWdlLWV2ZW50cyIsIm1hbmFnZS1yZWFsbSIsInZpZXctZXZlbnRzIiwidmlldy11c2VycyIsInZpZXctY2xpZW50cyIsIm1hbmFnZS1hdXRob3JpemF0aW9uIiwibWFuYWdlLWNsaWVudHMiLCJxdWVyeS1ncm91cHMiXX0sImFjY291bnQiOnsicm9sZXMiOlsibWFuYWdlLWFjY291bnQiLCJtYW5hZ2UtYWNjb3VudC1saW5rcyIsInZpZXctcHJvZmlsZSJdfX0sInNjb3BlIjoiZW1haWwgcHJvZmlsZSIsInNpZCI6IjE0NTk3YTJlLThjNTYtNGM5MC1iNzYwLWVhY2M3MzVlNWY3NSIsImVtYWlsX3ZlcmlmaWVkIjpmYWxzZSwicHJlZmVycmVkX3VzZXJuYW1lIjoiYWxpY2UifQ.hVj6SG-aTcDYhifdljpiBcz4ShCHej3h_4-82rgX0s_oJ-En68Cqt-_DgJLtMdr6dW_gQFFCPYBJfEGvZ8L6b_TwzbdLxyrQrKTOpeG0KJ8VAFlbWum9B1vvES_sav1Gj1sQHlV621EaLISYz7pnknuQEvrB7liJFRRjN9SH30AsAJy6nmKTDHGZ6Eegkveqd_7POaKfsHS3Z0-SGyL5GClXv9yZ1l5Y4VH-rrMUztLPCFH5bJ319-m-7sgizvV-C2EcM37XVAtPRVQbJNRW0wVmLEJKMuLYVnjS1Wn5eU_qnBvVMEaENNG3TzNd6b4YmxMFHFf9tnkb3wkDzdrRTA"; + performBearerAuthentication(BEARER_ONLY_PROVIDER_URL_APP, KeycloakConfiguration.ALICE, KeycloakConfiguration.ALICE_PASSWORD, + SimpleServlet.RESPONSE_BODY, wrongToken, BearerAuthType.BEARER); + } + + @Test + @OperateOnDeployment(BEARER_ONLY_PROVIDER_URL_APP) + public void testInvalidToken() throws Exception { + performBearerAuthentication(BEARER_ONLY_PROVIDER_URL_APP, KeycloakConfiguration.ALICE, KeycloakConfiguration.ALICE_PASSWORD, + SimpleServlet.RESPONSE_BODY, "INVALID_TOKEN", BearerAuthType.BEARER); + } + + @Test + @OperateOnDeployment(BEARER_ONLY_AUTH_SERVER_URL_APP) + public void testNoTokenProvidedWithAuthServerUrl() throws Exception { + accessAppWithoutToken(BEARER_ONLY_AUTH_SERVER_URL_APP, false, true, TEST_REALM); + } + + @Test + @OperateOnDeployment(BEARER_ONLY_PROVIDER_URL_APP) + public void testNoTokenProvidedWithProviderUrl() throws Exception { + accessAppWithoutToken(BEARER_ONLY_PROVIDER_URL_APP, false, true); + } + + @Test + @OperateOnDeployment(PROVIDER_URL_APP) + public void testTokenProvidedBearerOnlyNotSet() throws Exception { + performBearerAuthentication(PROVIDER_URL_APP, KeycloakConfiguration.ALICE, KeycloakConfiguration.ALICE_PASSWORD, + SimpleServlet.RESPONSE_BODY, null, BearerAuthType.BEARER, DIRECT_ACCCESS_GRANT_ENABLED_CLIENT, CLIENT_SECRET); + } + + @Test + @OperateOnDeployment(PROVIDER_URL_APP) + public void testTokenNotProvidedBearerOnlyNotSet() throws Exception { + // ensure the regular OIDC flow takes place + accessAppWithoutToken(PROVIDER_URL_APP, false, false,null, HttpURLConnection.HTTP_OK, SimpleServlet.RESPONSE_BODY); + } + + /** + * Tests that pass the bearer token to use via an access_token query param. + */ + + @Test + @OperateOnDeployment(BEARER_ONLY_PROVIDER_URL_APP) + public void testValidTokenViaQueryParameter() throws Exception { + performBearerAuthentication(BEARER_ONLY_PROVIDER_URL_APP, KeycloakConfiguration.ALICE, KeycloakConfiguration.ALICE_PASSWORD, + SimpleServlet.RESPONSE_BODY, null, BearerAuthType.QUERY_PARAM, DIRECT_ACCCESS_GRANT_ENABLED_CLIENT, CLIENT_SECRET); + } + + @Test + @OperateOnDeployment(BEARER_ONLY_PROVIDER_URL_APP) + public void testWrongTokenViaQueryParameter() throws Exception { + String wrongToken = "eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJrNmhQYTdHdmdrajdFdlhLeFAtRjFLZkNSUk85Q3kwNC04YzFqTERWOXNrIn0.eyJleHAiOjE2NTc2NjExODksImlhdCI6MTY1NzY2MTEyOSwianRpIjoiZThiZGQ3MWItYTA2OC00Mjc3LTkyY2UtZWJkYmU2MDVkMzBhIiwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo4MDgwL3JlYWxtcy9tYXN0ZXIiLCJhdWQiOlsibXlyZWFsbS1yZWFsbSIsIm1hc3Rlci1yZWFsbSIsImFjY291bnQiXSwic3ViIjoiZTliOGE2OWItM2RlNy00ZDYzLWFjYmItMmYyNTRhMDM1MjVkIiwidHlwIjoiQmVhcmVyIiwiYXpwIjoidGVzdC13ZWJhcHAiLCJzZXNzaW9uX3N0YXRlIjoiMTQ1OTdhMmUtOGM1Ni00YzkwLWI3NjAtZWFjYzczNWU1Zjc1IiwiYWNyIjoiMSIsInJlYWxtX2FjY2VzcyI6eyJyb2xlcyI6WyJjcmVhdGUtcmVhbG0iLCJkZWZhdWx0LXJvbGVzLW1hc3RlciIsIm9mZmxpbmVfYWNjZXNzIiwiYWRtaW4iLCJ1bWFfYXV0aG9yaXphdGlvbiIsInVzZXIiXX0sInJlc291cmNlX2FjY2VzcyI6eyJteXJlYWxtLXJlYWxtIjp7InJvbGVzIjpbInZpZXctcmVhbG0iLCJ2aWV3LWlkZW50aXR5LXByb3ZpZGVycyIsIm1hbmFnZS1pZGVudGl0eS1wcm92aWRlcnMiLCJpbXBlcnNvbmF0aW9uIiwiY3JlYXRlLWNsaWVudCIsIm1hbmFnZS11c2VycyIsInF1ZXJ5LXJlYWxtcyIsInZpZXctYXV0aG9yaXphdGlvbiIsInF1ZXJ5LWNsaWVudHMiLCJxdWVyeS11c2VycyIsIm1hbmFnZS1ldmVudHMiLCJtYW5hZ2UtcmVhbG0iLCJ2aWV3LWV2ZW50cyIsInZpZXctdXNlcnMiLCJ2aWV3LWNsaWVudHMiLCJtYW5hZ2UtYXV0aG9yaXphdGlvbiIsIm1hbmFnZS1jbGllbnRzIiwicXVlcnktZ3JvdXBzIl19LCJtYXN0ZXItcmVhbG0iOnsicm9sZXMiOlsidmlldy1yZWFsbSIsInZpZXctaWRlbnRpdHktcHJvdmlkZXJzIiwibWFuYWdlLWlkZW50aXR5LXByb3ZpZGVycyIsImltcGVyc29uYXRpb24iLCJjcmVhdGUtY2xpZW50IiwibWFuYWdlLXVzZXJzIiwicXVlcnktcmVhbG1zIiwidmlldy1hdXRob3JpemF0aW9uIiwicXVlcnktY2xpZW50cyIsInF1ZXJ5LXVzZXJzIiwibWFuYWdlLWV2ZW50cyIsIm1hbmFnZS1yZWFsbSIsInZpZXctZXZlbnRzIiwidmlldy11c2VycyIsInZpZXctY2xpZW50cyIsIm1hbmFnZS1hdXRob3JpemF0aW9uIiwibWFuYWdlLWNsaWVudHMiLCJxdWVyeS1ncm91cHMiXX0sImFjY291bnQiOnsicm9sZXMiOlsibWFuYWdlLWFjY291bnQiLCJtYW5hZ2UtYWNjb3VudC1saW5rcyIsInZpZXctcHJvZmlsZSJdfX0sInNjb3BlIjoiZW1haWwgcHJvZmlsZSIsInNpZCI6IjE0NTk3YTJlLThjNTYtNGM5MC1iNzYwLWVhY2M3MzVlNWY3NSIsImVtYWlsX3ZlcmlmaWVkIjpmYWxzZSwicHJlZmVycmVkX3VzZXJuYW1lIjoiYWxpY2UifQ.hVj6SG-aTcDYhifdljpiBcz4ShCHej3h_4-82rgX0s_oJ-En68Cqt-_DgJLtMdr6dW_gQFFCPYBJfEGvZ8L6b_TwzbdLxyrQrKTOpeG0KJ8VAFlbWum9B1vvES_sav1Gj1sQHlV621EaLISYz7pnknuQEvrB7liJFRRjN9SH30AsAJy6nmKTDHGZ6Eegkveqd_7POaKfsHS3Z0-SGyL5GClXv9yZ1l5Y4VH-rrMUztLPCFH5bJ319-m-7sgizvV-C2EcM37XVAtPRVQbJNRW0wVmLEJKMuLYVnjS1Wn5eU_qnBvVMEaENNG3TzNd6b4YmxMFHFf9tnkb3wkDzdrRTA"; + performBearerAuthentication(BEARER_ONLY_PROVIDER_URL_APP, KeycloakConfiguration.ALICE, KeycloakConfiguration.ALICE_PASSWORD, + SimpleServlet.RESPONSE_BODY, wrongToken, BearerAuthType.QUERY_PARAM); + } + + @Test + @OperateOnDeployment(BEARER_ONLY_PROVIDER_URL_APP) + public void testInvalidTokenViaQueryParameter() throws Exception { + performBearerAuthentication(BEARER_ONLY_PROVIDER_URL_APP, KeycloakConfiguration.ALICE, KeycloakConfiguration.ALICE_PASSWORD, + SimpleServlet.RESPONSE_BODY, "INVALID_TOKEN", BearerAuthType.QUERY_PARAM); + } + + /** + * Tests that rely on obtaining the bearer token to use from credentials obtained from basic auth. + */ + + @Test + @OperateOnDeployment(BEARER_ONLY_PROVIDER_URL_APP) + public void testBasicAuthenticationWithoutEnableBasicAuthSet() throws Exception { + accessAppWithoutToken(BEARER_ONLY_PROVIDER_URL_APP, true, true, KeycloakConfiguration.ALICE, KeycloakConfiguration.ALICE_PASSWORD); + } + + @Test + @OperateOnDeployment(PROVIDER_URL_APP) + public void testBasicAuthenticationWithoutEnableBasicAuthSetAndWithoutBearerOnlySet() throws Exception { + // ensure the regular OIDC flow takes place + accessAppWithoutToken(PROVIDER_URL_APP, true, false, null, HttpURLConnection.HTTP_OK, SimpleServlet.RESPONSE_BODY); + } + + @Test + @OperateOnDeployment(BASIC_AUTH_PROVIDER_URL_APP) + public void testValidCredentialsBasicAuthentication() throws Exception { + performBearerAuthentication(BASIC_AUTH_PROVIDER_URL_APP, KeycloakConfiguration.ALICE, KeycloakConfiguration.ALICE_PASSWORD, + SimpleServlet.RESPONSE_BODY, null, BearerAuthType.BASIC); + } + + @Test + @OperateOnDeployment(BASIC_AUTH_PROVIDER_URL_APP) + public void testInvalidCredentialsBasicAuthentication() throws Exception { + accessAppWithoutToken(BASIC_AUTH_PROVIDER_URL_APP, true, true, KeycloakConfiguration.ALICE, WRONG_PASSWORD); + } + + /** + * Tests that simulate CORS preflight requests. + */ + + @Test + @OperateOnDeployment(CORS_PROVIDER_URL_APP) + public void testCorsRequestWithEnableCors() throws Exception { + performBearerAuthenticationWithCors(CORS_PROVIDER_URL_APP, KeycloakConfiguration.ALICE, KeycloakConfiguration.ALICE_PASSWORD, + SimpleServlet.RESPONSE_BODY, null, CORS_CLIENT, CLIENT_SECRET, ALLOWED_ORIGIN, true); + } + + @Test + @OperateOnDeployment(CORS_PROVIDER_URL_APP) + public void testCorsRequestWithEnableCorsWithWrongToken() throws Exception { + String wrongToken = "eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJrNmhQYTdHdmdrajdFdlhLeFAtRjFLZkNSUk85Q3kwNC04YzFqTERWOXNrIn0.eyJleHAiOjE2NTc2NjExODksImlhdCI6MTY1NzY2MTEyOSwianRpIjoiZThiZGQ3MWItYTA2OC00Mjc3LTkyY2UtZWJkYmU2MDVkMzBhIiwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo4MDgwL3JlYWxtcy9tYXN0ZXIiLCJhdWQiOlsibXlyZWFsbS1yZWFsbSIsIm1hc3Rlci1yZWFsbSIsImFjY291bnQiXSwic3ViIjoiZTliOGE2OWItM2RlNy00ZDYzLWFjYmItMmYyNTRhMDM1MjVkIiwidHlwIjoiQmVhcmVyIiwiYXpwIjoidGVzdC13ZWJhcHAiLCJzZXNzaW9uX3N0YXRlIjoiMTQ1OTdhMmUtOGM1Ni00YzkwLWI3NjAtZWFjYzczNWU1Zjc1IiwiYWNyIjoiMSIsInJlYWxtX2FjY2VzcyI6eyJyb2xlcyI6WyJjcmVhdGUtcmVhbG0iLCJkZWZhdWx0LXJvbGVzLW1hc3RlciIsIm9mZmxpbmVfYWNjZXNzIiwiYWRtaW4iLCJ1bWFfYXV0aG9yaXphdGlvbiIsInVzZXIiXX0sInJlc291cmNlX2FjY2VzcyI6eyJteXJlYWxtLXJlYWxtIjp7InJvbGVzIjpbInZpZXctcmVhbG0iLCJ2aWV3LWlkZW50aXR5LXByb3ZpZGVycyIsIm1hbmFnZS1pZGVudGl0eS1wcm92aWRlcnMiLCJpbXBlcnNvbmF0aW9uIiwiY3JlYXRlLWNsaWVudCIsIm1hbmFnZS11c2VycyIsInF1ZXJ5LXJlYWxtcyIsInZpZXctYXV0aG9yaXphdGlvbiIsInF1ZXJ5LWNsaWVudHMiLCJxdWVyeS11c2VycyIsIm1hbmFnZS1ldmVudHMiLCJtYW5hZ2UtcmVhbG0iLCJ2aWV3LWV2ZW50cyIsInZpZXctdXNlcnMiLCJ2aWV3LWNsaWVudHMiLCJtYW5hZ2UtYXV0aG9yaXphdGlvbiIsIm1hbmFnZS1jbGllbnRzIiwicXVlcnktZ3JvdXBzIl19LCJtYXN0ZXItcmVhbG0iOnsicm9sZXMiOlsidmlldy1yZWFsbSIsInZpZXctaWRlbnRpdHktcHJvdmlkZXJzIiwibWFuYWdlLWlkZW50aXR5LXByb3ZpZGVycyIsImltcGVyc29uYXRpb24iLCJjcmVhdGUtY2xpZW50IiwibWFuYWdlLXVzZXJzIiwicXVlcnktcmVhbG1zIiwidmlldy1hdXRob3JpemF0aW9uIiwicXVlcnktY2xpZW50cyIsInF1ZXJ5LXVzZXJzIiwibWFuYWdlLWV2ZW50cyIsIm1hbmFnZS1yZWFsbSIsInZpZXctZXZlbnRzIiwidmlldy11c2VycyIsInZpZXctY2xpZW50cyIsIm1hbmFnZS1hdXRob3JpemF0aW9uIiwibWFuYWdlLWNsaWVudHMiLCJxdWVyeS1ncm91cHMiXX0sImFjY291bnQiOnsicm9sZXMiOlsibWFuYWdlLWFjY291bnQiLCJtYW5hZ2UtYWNjb3VudC1saW5rcyIsInZpZXctcHJvZmlsZSJdfX0sInNjb3BlIjoiZW1haWwgcHJvZmlsZSIsInNpZCI6IjE0NTk3YTJlLThjNTYtNGM5MC1iNzYwLWVhY2M3MzVlNWY3NSIsImVtYWlsX3ZlcmlmaWVkIjpmYWxzZSwicHJlZmVycmVkX3VzZXJuYW1lIjoiYWxpY2UifQ.hVj6SG-aTcDYhifdljpiBcz4ShCHej3h_4-82rgX0s_oJ-En68Cqt-_DgJLtMdr6dW_gQFFCPYBJfEGvZ8L6b_TwzbdLxyrQrKTOpeG0KJ8VAFlbWum9B1vvES_sav1Gj1sQHlV621EaLISYz7pnknuQEvrB7liJFRRjN9SH30AsAJy6nmKTDHGZ6Eegkveqd_7POaKfsHS3Z0-SGyL5GClXv9yZ1l5Y4VH-rrMUztLPCFH5bJ319-m-7sgizvV-C2EcM37XVAtPRVQbJNRW0wVmLEJKMuLYVnjS1Wn5eU_qnBvVMEaENNG3TzNd6b4YmxMFHFf9tnkb3wkDzdrRTA"; + performBearerAuthenticationWithCors(CORS_PROVIDER_URL_APP, KeycloakConfiguration.ALICE, KeycloakConfiguration.ALICE_PASSWORD, + SimpleServlet.RESPONSE_BODY, wrongToken, CORS_CLIENT, CLIENT_SECRET, ALLOWED_ORIGIN, true); + } + + @Test + @OperateOnDeployment(CORS_PROVIDER_URL_APP) + public void testCorsRequestWithEnableCorsWithInvalidToken() throws Exception { + performBearerAuthenticationWithCors(CORS_PROVIDER_URL_APP, KeycloakConfiguration.ALICE, KeycloakConfiguration.ALICE_PASSWORD, + SimpleServlet.RESPONSE_BODY, "INVALID_TOKEN", CORS_CLIENT, CLIENT_SECRET, ALLOWED_ORIGIN, true); + } + + @Test + @OperateOnDeployment(CORS_PROVIDER_URL_APP) + public void testCorsRequestWithEnableCorsWithInvalidOrigin() throws Exception { + performBearerAuthenticationWithCors(CORS_PROVIDER_URL_APP, KeycloakConfiguration.ALICE, KeycloakConfiguration.ALICE_PASSWORD, + SimpleServlet.RESPONSE_BODY, null, CORS_CLIENT, CLIENT_SECRET, "http://invalidorigin", true); + } + + @Test + @OperateOnDeployment(BEARER_ONLY_PROVIDER_URL_APP) + public void testCorsRequestWithoutEnableCors() throws Exception { + performBearerAuthenticationWithCors(BEARER_ONLY_PROVIDER_URL_APP, KeycloakConfiguration.ALICE, KeycloakConfiguration.ALICE_PASSWORD, + SimpleServlet.RESPONSE_BODY, null, CORS_CLIENT, CLIENT_SECRET, ALLOWED_ORIGIN, false); + } + public static void loginToApp(String appName, String username, String password, int expectedStatusCode, String expectedText) throws Exception { loginToApp(appName, username, password, expectedStatusCode, expectedText, true); } @@ -198,6 +378,195 @@ public static void loginToApp(String appName, String username, String password, } } + private void performBearerAuthentication(String appName, String username, String password, + String clientPageText, String bearerToken, BearerAuthType bearerAuthType) throws Exception { + performBearerAuthentication(appName, username, password, clientPageText, bearerToken, bearerAuthType, null, null); + + } + + private void performBearerAuthentication(String appName, String username, String password, + String clientPageText, String bearerToken, BearerAuthType bearerAuthType, + String clientId, String clientSecret) throws Exception { + HttpClient httpClient = HttpClients.createDefault(); + HttpGet getMethod; + HttpContext context = new BasicHttpContext(); + HttpResponse response; + URI requestUri; + switch (bearerAuthType) { + case QUERY_PARAM: + if (bearerToken == null) { + // obtain a bearer token and then try accessing the endpoint with a query param specified + requestUri = new URL("http", TestSuiteEnvironment.getHttpAddress(), TestSuiteEnvironment.getHttpPort(), + "/" + appName + SimpleSecuredServlet.SERVLET_PATH + "?access_token=" + + KeycloakConfiguration.getAccessToken(KEYCLOAK_CONTAINER.getAuthServerUrl(), TEST_REALM, username, + password, clientId, clientSecret)).toURI(); + } else { + // try accessing the endpoint with the given bearer token specified using a query param + requestUri = new URL("http", TestSuiteEnvironment.getHttpAddress(), TestSuiteEnvironment.getHttpPort(), + "/" + appName + SimpleSecuredServlet.SERVLET_PATH + "?access_token=" + bearerToken).toURI(); + } + getMethod = new HttpGet(requestUri); + break; + case BASIC: + requestUri = new URL("http", TestSuiteEnvironment.getHttpAddress(), TestSuiteEnvironment.getHttpPort(), + "/" + appName + SimpleSecuredServlet.SERVLET_PATH).toURI(); + getMethod = new HttpGet(requestUri); + getMethod.addHeader("Authorization", "Basic " + CodePointIterator.ofString(username + ":" + password).asUtf8().base64Encode().drainToString()); + break; + default: // BEARER + requestUri = new URL("http", TestSuiteEnvironment.getHttpAddress(), TestSuiteEnvironment.getHttpPort(), + "/" + appName + SimpleSecuredServlet.SERVLET_PATH).toURI(); + getMethod = new HttpGet(requestUri); + if (bearerToken == null) { + // obtain a bearer token and then try accessing the endpoint with the Authorization header specified + getMethod.addHeader("Authorization", "Bearer " + KeycloakConfiguration.getAccessToken(KEYCLOAK_CONTAINER.getAuthServerUrl(), TEST_REALM, username, + password, clientId, clientSecret)); + } else { + // try accessing the endpoint with the given bearer token specified using the Authorization header + getMethod.addHeader("Authorization", "Bearer " + bearerToken); + } + break; + } + response = httpClient.execute(getMethod, context); + try { + int statusCode = response.getStatusLine().getStatusCode(); + if (bearerToken == null) { + assertEquals(HttpURLConnection.HTTP_OK, statusCode); + String responseString = new BasicResponseHandler().handleResponse(response); + assertTrue(responseString.contains(clientPageText)); + } else { + assertTrue("Expected code == UNAUTHORIZED but got " + statusCode + " for request=" + requestUri, statusCode == HttpURLConnection.HTTP_UNAUTHORIZED); + } + } finally { + HttpClientUtils.closeQuietly(response); + } + } + + private void performBearerAuthenticationWithCors(String appName, String username, String password, + String clientPageText, String bearerToken, + String clientId, String clientSecret, String originHeader, boolean corsEnabled) throws Exception { + URI requestUri = new URL("http", TestSuiteEnvironment.getHttpAddress(), TestSuiteEnvironment.getHttpPort(), + "/" + appName + SimpleSecuredServlet.SERVLET_PATH).toURI(); + HttpClient httpClient = HttpClients.createDefault(); + HttpOptions optionsMethod = new HttpOptions(requestUri); + HttpContext context = new BasicHttpContext(); + HttpResponse response; + optionsMethod.addHeader(ORIGIN, originHeader); + optionsMethod.addHeader(ACCESS_CONTROL_REQUEST_HEADERS, "authorization"); + optionsMethod.addHeader(ACCESS_CONTROL_REQUEST_METHOD, "GET"); + response = httpClient.execute(optionsMethod, context); + + try { + + int statusCode = response.getStatusLine().getStatusCode(); + if (corsEnabled) { + assertEquals(HttpURLConnection.HTTP_OK, statusCode); + assertTrue(Boolean.valueOf(response.getFirstHeader(ACCESS_CONTROL_ALLOW_CREDENTIALS).getValue())); + assertEquals("authorization", response.getFirstHeader(ACCESS_CONTROL_ALLOW_HEADERS).getValue()); + assertEquals("GET", response.getFirstHeader(ACCESS_CONTROL_ALLOW_METHODS).getValue()); + assertEquals(originHeader, response.getFirstHeader(ACCESS_CONTROL_ALLOW_ORIGIN).getValue()); + + + HttpGet getMethod = new HttpGet(requestUri); + getMethod.addHeader(ORIGIN, originHeader); + if (bearerToken == null) { + // obtain a bearer token and then try accessing the endpoint with the Authorization header specified + getMethod.addHeader("Authorization", "Bearer " + KeycloakConfiguration.getAccessToken(KEYCLOAK_CONTAINER.getAuthServerUrl(), TEST_REALM, username, + password, clientId, clientSecret)); + } else { + // try accessing the endpoint with the given bearer token specified using the Authorization header + getMethod.addHeader("Authorization", "Bearer " + bearerToken); + } + + response = httpClient.execute(getMethod, context); + + statusCode = response.getStatusLine().getStatusCode(); + if (bearerToken == null) { + if (originHeader.equals(ALLOWED_ORIGIN)) { + assertEquals(HttpURLConnection.HTTP_OK, statusCode); + String responseString = new BasicResponseHandler().handleResponse(response); + assertTrue(responseString.contains(clientPageText)); + } else { + assertEquals(HttpURLConnection.HTTP_FORBIDDEN, statusCode); + } + } else { + assertTrue("Expected code == UNAUTHORIZED but got " + statusCode + " for request=" + requestUri, statusCode == HttpURLConnection.HTTP_UNAUTHORIZED); + } + } else { + assertEquals(HttpURLConnection.HTTP_UNAUTHORIZED, statusCode); + Header authenticateHeader = response.getFirstHeader("WWW-Authenticate"); + assertEquals("Bearer", authenticateHeader.getValue()); + } + } finally { + HttpClientUtils.closeQuietly(response); + } + } + + private void accessAppWithoutToken(String appName, boolean includeBasicHeader, boolean bearerOnlyOrEnableBasicConfigured) throws Exception { + accessAppWithoutToken(appName, includeBasicHeader, bearerOnlyOrEnableBasicConfigured, null); + } + + private void accessAppWithoutToken(String appName, boolean includeBasicHeader, boolean bearerOnlyOrEnableBasicConfigured, String realm) throws Exception { + accessAppWithoutToken(appName, includeBasicHeader, bearerOnlyOrEnableBasicConfigured, realm, -1, null); + } + + private void accessAppWithoutToken(String appName, boolean includeBasicHeader, boolean bearerOnlyOrEnableBasicConfigured, String realm, int expectedStatusCode, String expectedText) throws Exception { + accessAppWithoutToken(appName, includeBasicHeader, bearerOnlyOrEnableBasicConfigured, realm, expectedStatusCode, expectedText, null, null); + } + + private void accessAppWithoutToken(String appName, boolean includeBasicHeader, boolean bearerOnlyOrEnableBasicConfigured, String username, String password) throws Exception { + accessAppWithoutToken(appName, includeBasicHeader, bearerOnlyOrEnableBasicConfigured, null, -1, null, username, password); + } + + private void accessAppWithoutToken(String appName, boolean includeBasicHeader, boolean bearerOnlyOrEnableBasicConfigured, String realm, int expectedStatusCode, String expectedText, + String username, String password) throws Exception { + final URI requestUri = new URL("http", TestSuiteEnvironment.getHttpAddress(), TestSuiteEnvironment.getHttpPort(), + "/" + appName + SimpleSecuredServlet.SERVLET_PATH).toURI(); + CookieStore store = new BasicCookieStore(); + HttpClient httpClient = TestHttpClientUtils.promiscuousCookieHttpClientBuilder() + .setDefaultCookieStore(store) + .setRedirectStrategy(new LaxRedirectStrategy()) + .build(); + HttpGet getMethod = new HttpGet(requestUri); + if (includeBasicHeader) { + getMethod.addHeader("Authorization", "Basic " + CodePointIterator.ofString(username + ":" + password).asUtf8().base64Encode().drainToString()); + } + HttpContext context = new BasicHttpContext(); + + HttpResponse response = httpClient.execute(getMethod, context); + try { + int statusCode = response.getStatusLine().getStatusCode(); + if (bearerOnlyOrEnableBasicConfigured) { + assertTrue("Expected code == UNAUTHORIZED but got " + statusCode + " for request=" + requestUri, statusCode == HttpURLConnection.HTTP_UNAUTHORIZED); + Header authenticateHeader = response.getFirstHeader("WWW-Authenticate"); + String authenticateValue = authenticateHeader.getValue(); + if (password != null && password.equals(WRONG_PASSWORD)) { + assertTrue(authenticateValue.startsWith("Bearer error=\"" + "no_token" + "\"")); + assertTrue(authenticateValue.contains("error_description")); + assertTrue(authenticateValue.contains(String.valueOf(HttpURLConnection.HTTP_UNAUTHORIZED))); + } else if (realm != null) { + assertEquals("Bearer realm=\"" + TEST_REALM + "\"", authenticateValue); + } else { + assertEquals("Bearer", authenticateValue); + } + } else { + // no token provided and bearer-only is not configured, should end up in the OIDC flow + assertTrue("Expected code == OK but got " + statusCode + " for request=" + requestUri, statusCode == HttpURLConnection.HTTP_OK); + Form keycloakLoginForm = new Form(response); + HttpResponse afterLoginClickResponse = simulateClickingOnButton(httpClient, keycloakLoginForm, + KeycloakConfiguration.ALICE, KeycloakConfiguration.ALICE_PASSWORD, "Sign In"); + afterLoginClickResponse.getEntity().getContent(); + assertEquals(expectedStatusCode, afterLoginClickResponse.getStatusLine().getStatusCode()); + if (expectedText != null) { + String responseString = new BasicResponseHandler().handleResponse(afterLoginClickResponse); + assertTrue(responseString.contains(expectedText)); + } + } + } finally { + HttpClientUtils.closeQuietly(response); + } + } + public static class KeycloakSetup implements ServerSetupTask { @Override diff --git a/testsuite/integration/elytron-oidc-client/src/test/java/org/wildfly/test/integration/elytron/oidc/client/deployment/OidcWithDeploymentConfigTest.java b/testsuite/integration/elytron-oidc-client/src/test/java/org/wildfly/test/integration/elytron/oidc/client/deployment/OidcWithDeploymentConfigTest.java index 1c4cb6dd14ba..bdee0e3fd96b 100644 --- a/testsuite/integration/elytron-oidc-client/src/test/java/org/wildfly/test/integration/elytron/oidc/client/deployment/OidcWithDeploymentConfigTest.java +++ b/testsuite/integration/elytron-oidc-client/src/test/java/org/wildfly/test/integration/elytron/oidc/client/deployment/OidcWithDeploymentConfigTest.java @@ -25,6 +25,9 @@ import static org.jboss.as.test.integration.management.util.ModelUtil.createOpNode; import static org.wildfly.test.integration.elytron.oidc.client.KeycloakConfiguration.getRealmRepresentation; +import java.util.HashMap; +import java.util.Map; + import org.jboss.arquillian.container.test.api.Deployer; import org.jboss.arquillian.container.test.api.Deployment; import org.jboss.arquillian.container.test.api.RunAsClient; @@ -72,7 +75,27 @@ public class OidcWithDeploymentConfigTest extends OidcBaseTest { private static final String MISSING_EXPRESSION_APP = "MissingExpressionOidcApp"; private static final String OIDC_JSON_WITH_MISSING_EXPRESSION_FILE = "OidcWithMissingExpression.json"; - private static final String[] APP_NAMES = new String[] {PROVIDER_URL_APP, AUTH_SERVER_URL_APP, WRONG_PROVIDER_URL_APP, WRONG_SECRET_APP, MISSING_EXPRESSION_APP}; + private static final String BEARER_ONLY_WITH_AUTH_SERVER_URL_FILE = "BearerOnlyWithAuthServerUrl.json"; + + private static final String BEARER_ONLY_WITH_PROVIDER_URL_FILE = "BearerOnlyWithProviderUrl.json"; + private static final String BASIC_AUTH_WITH_PROVIDER_URL_FILE = "BasicAuthWithProviderUrl.json"; + private static final String CORS_WITH_PROVIDER_URL_FILE = "CorsWithProviderUrl.json"; + + private static Map APP_NAMES; + static { + APP_NAMES = new HashMap<>(); + APP_NAMES.put(PROVIDER_URL_APP, KeycloakConfiguration.ClientAppType.OIDC_CLIENT); + APP_NAMES.put(AUTH_SERVER_URL_APP, KeycloakConfiguration.ClientAppType.OIDC_CLIENT); + APP_NAMES.put(WRONG_PROVIDER_URL_APP, KeycloakConfiguration.ClientAppType.OIDC_CLIENT); + APP_NAMES.put(WRONG_SECRET_APP, KeycloakConfiguration.ClientAppType.OIDC_CLIENT); + APP_NAMES.put(MISSING_EXPRESSION_APP, KeycloakConfiguration.ClientAppType.OIDC_CLIENT); + APP_NAMES.put(DIRECT_ACCCESS_GRANT_ENABLED_CLIENT, KeycloakConfiguration.ClientAppType.DIRECT_ACCESS_GRANT_OIDC_CLIENT); + APP_NAMES.put(BEARER_ONLY_AUTH_SERVER_URL_APP, KeycloakConfiguration.ClientAppType.BEARER_ONLY_CLIENT); + APP_NAMES.put(BEARER_ONLY_PROVIDER_URL_APP, KeycloakConfiguration.ClientAppType.BEARER_ONLY_CLIENT); + APP_NAMES.put(BASIC_AUTH_PROVIDER_URL_APP, KeycloakConfiguration.ClientAppType.BEARER_ONLY_CLIENT); + APP_NAMES.put(CORS_PROVIDER_URL_APP, KeycloakConfiguration.ClientAppType.BEARER_ONLY_CLIENT); + APP_NAMES.put(CORS_CLIENT, KeycloakConfiguration.ClientAppType.CORS_CLIENT); + } @ArquillianResource protected static Deployer deployer; @@ -122,6 +145,42 @@ public static WebArchive createMissingExpressionDeployment() { .addAsWebInfResource(OidcWithDeploymentConfigTest.class.getPackage(), OIDC_JSON_WITH_MISSING_EXPRESSION_FILE, "oidc.json"); } + @Deployment(name = BEARER_ONLY_AUTH_SERVER_URL_APP, managed = false, testable = false) + public static WebArchive createBearerOnlyAuthServerUrlDeployment() { + return ShrinkWrap.create(WebArchive.class, BEARER_ONLY_AUTH_SERVER_URL_APP + ".war") + .addClasses(SimpleServlet.class) + .addClasses(SimpleSecuredServlet.class) + .addAsWebInfResource(OidcWithDeploymentConfigTest.class.getPackage(), OIDC_WITHOUT_SUBSYSTEM_CONFIG_WEB_XML, "web.xml") + .addAsWebInfResource(OidcWithDeploymentConfigTest.class.getPackage(), BEARER_ONLY_WITH_AUTH_SERVER_URL_FILE, "oidc.json"); + } + + @Deployment(name = BEARER_ONLY_PROVIDER_URL_APP, managed = false, testable = false) + public static WebArchive createBearerOnlyProviderUrlDeployment() { + return ShrinkWrap.create(WebArchive.class, BEARER_ONLY_PROVIDER_URL_APP + ".war") + .addClasses(SimpleServlet.class) + .addClasses(SimpleSecuredServlet.class) + .addAsWebInfResource(OidcWithDeploymentConfigTest.class.getPackage(), OIDC_WITHOUT_SUBSYSTEM_CONFIG_WEB_XML, "web.xml") + .addAsWebInfResource(OidcWithDeploymentConfigTest.class.getPackage(), BEARER_ONLY_WITH_PROVIDER_URL_FILE, "oidc.json"); + } + + @Deployment(name = BASIC_AUTH_PROVIDER_URL_APP, managed = false, testable = false) + public static WebArchive createBasicAuthProviderUrlDeployment() { + return ShrinkWrap.create(WebArchive.class, BASIC_AUTH_PROVIDER_URL_APP + ".war") + .addClasses(SimpleServlet.class) + .addClasses(SimpleSecuredServlet.class) + .addAsWebInfResource(OidcWithDeploymentConfigTest.class.getPackage(), OIDC_WITHOUT_SUBSYSTEM_CONFIG_WEB_XML, "web.xml") + .addAsWebInfResource(OidcWithDeploymentConfigTest.class.getPackage(), BASIC_AUTH_WITH_PROVIDER_URL_FILE, "oidc.json"); + } + + @Deployment(name = CORS_PROVIDER_URL_APP, managed = false, testable = false) + public static WebArchive createCorsProviderUrlDeployment() { + return ShrinkWrap.create(WebArchive.class, CORS_PROVIDER_URL_APP + ".war") + .addClasses(SimpleServlet.class) + .addClasses(SimpleSecuredServlet.class) + .addAsWebInfResource(OidcWithDeploymentConfigTest.class.getPackage(), OIDC_WITHOUT_SUBSYSTEM_CONFIG_WEB_XML, "web.xml") + .addAsWebInfResource(OidcWithDeploymentConfigTest.class.getPackage(), CORS_WITH_PROVIDER_URL_FILE, "oidc.json"); + } + @Test @InSequence(1) public void testWrongPasswordWithProviderUrl() throws Exception { @@ -138,28 +197,46 @@ public void testSucessfulAuthenticationWithProviderUrl() throws Exception { @Test @InSequence(3) public void testWrongRoleWithProviderUrl() throws Exception { + super.testWrongRoleWithProviderUrl(); + } + + @Test + @InSequence(4) + public void testTokenProvidedBearerOnlyNotSet() throws Exception { + super.testTokenProvidedBearerOnlyNotSet(); + } + + @Test + @InSequence(5) + public void testTokenNotProvidedBearerOnlyNotSet() throws Exception { + super.testTokenNotProvidedBearerOnlyNotSet(); + } + + @Test + @InSequence(6) + public void testBasicAuthenticationWithoutEnableBasicAuthSetAndWithoutBearerOnlySet() throws Exception { try { - super.testWrongRoleWithProviderUrl(); + super.testBasicAuthenticationWithoutEnableBasicAuthSetAndWithoutBearerOnlySet(); } finally { deployer.undeploy(PROVIDER_URL_APP); } } @Test - @InSequence(4) + @InSequence(7) public void testWrongPasswordWithAuthServerUrl() throws Exception { deployer.deploy(AUTH_SERVER_URL_APP); super.testWrongPasswordWithAuthServerUrl(); } @Test - @InSequence(5) + @InSequence(8) public void testSucessfulAuthenticationWithAuthServerUrl() throws Exception { super.testSucessfulAuthenticationWithAuthServerUrl(); } @Test - @InSequence(6) + @InSequence(9) public void testWrongRoleWithAuthServerUrl() throws Exception { try { super.testWrongRoleWithAuthServerUrl(); @@ -198,6 +275,129 @@ public void testMissingExpression() throws Exception { } } + @Test + @InSequence(10) + public void testSucessfulBearerOnlyAuthenticationWithAuthServerUrl() throws Exception { + deployer.deploy(BEARER_ONLY_AUTH_SERVER_URL_APP); + super.testSucessfulBearerOnlyAuthenticationWithAuthServerUrl(); + + } + + @Test + @InSequence(11) + public void testNoTokenProvidedWithAuthServerUrl() throws Exception { + try { + super.testNoTokenProvidedWithAuthServerUrl(); + } finally { + deployer.undeploy(BEARER_ONLY_AUTH_SERVER_URL_APP); + } + } + + @Test + @InSequence(12) + public void testSucessfulBearerOnlyAuthenticationWithProviderUrl() throws Exception { + deployer.deploy(BEARER_ONLY_PROVIDER_URL_APP); + super.testSucessfulBearerOnlyAuthenticationWithProviderUrl(); + } + + @Test + @InSequence(13) + public void testWrongToken() throws Exception { + super.testWrongToken(); + } + + @Test + @InSequence(14) + public void testInvalidToken() throws Exception { + super.testInvalidToken(); + } + + @Test + @InSequence(15) + public void testNoTokenProvidedWithProviderUrl() throws Exception { + super.testNoTokenProvidedWithProviderUrl(); + } + + @Test + @InSequence(16) + public void testValidTokenViaQueryParameter() throws Exception { + super.testValidTokenViaQueryParameter(); + } + + @Test + @InSequence(17) + public void testWrongTokenViaQueryParameter() throws Exception { + super.testWrongTokenViaQueryParameter(); + } + + @Test + @InSequence(18) + public void testInvalidTokenViaQueryParameter() throws Exception { + super.testInvalidTokenViaQueryParameter(); + } + + @Test + @InSequence(19) + public void testBasicAuthenticationWithoutEnableBasicAuthSet() throws Exception { + super.testBasicAuthenticationWithoutEnableBasicAuthSet(); + } + + @Test + @InSequence(20) + public void testCorsRequestWithoutEnableCors() throws Exception { + try { + super.testCorsRequestWithoutEnableCors(); + } finally { + deployer.undeploy(BEARER_ONLY_PROVIDER_URL_APP); + } + } + + @Test + @InSequence(21) + public void testValidCredentialsBasicAuthentication() throws Exception { + deployer.deploy(BASIC_AUTH_PROVIDER_URL_APP); + super.testValidCredentialsBasicAuthentication(); + } + + @Test + @InSequence(22) + public void testInvalidCredentialsBasicAuthentication() throws Exception { + try { + super.testInvalidCredentialsBasicAuthentication(); + } finally { + deployer.undeploy(BASIC_AUTH_PROVIDER_URL_APP); + } + } + + @Test + @InSequence(23) + public void testCorsRequestWithEnableCors() throws Exception { + deployer.deploy(CORS_PROVIDER_URL_APP); + super.testCorsRequestWithEnableCors(); + } + + @Test + @InSequence(24) + public void testCorsRequestWithEnableCorsWithWrongToken() throws Exception { + super.testCorsRequestWithEnableCorsWithWrongToken(); + } + + @Test + @InSequence(25) + public void testCorsRequestWithEnableCorsWithInvalidToken() throws Exception { + super.testCorsRequestWithEnableCorsWithInvalidToken(); + } + + @Test + @InSequence(26) + public void testCorsRequestWithEnableCorsWithInvalidOrigin() throws Exception { + try { + super.testCorsRequestWithEnableCorsWithInvalidOrigin(); + } finally { + deployer.undeploy(CORS_PROVIDER_URL_APP); + } + } + static class KeycloakAndSystemPropertySetup extends KeycloakSetup { @Override diff --git a/testsuite/integration/elytron-oidc-client/src/test/java/org/wildfly/test/integration/elytron/oidc/client/subsystem/OidcWithSubsystemConfigTest.java b/testsuite/integration/elytron-oidc-client/src/test/java/org/wildfly/test/integration/elytron/oidc/client/subsystem/OidcWithSubsystemConfigTest.java index 684dc2259073..4f1f1af16584 100644 --- a/testsuite/integration/elytron-oidc-client/src/test/java/org/wildfly/test/integration/elytron/oidc/client/subsystem/OidcWithSubsystemConfigTest.java +++ b/testsuite/integration/elytron-oidc-client/src/test/java/org/wildfly/test/integration/elytron/oidc/client/subsystem/OidcWithSubsystemConfigTest.java @@ -26,6 +26,8 @@ import static org.wildfly.test.integration.elytron.oidc.client.KeycloakConfiguration.getRealmRepresentation; import java.net.HttpURLConnection; +import java.util.HashMap; +import java.util.Map; import org.jboss.arquillian.container.test.api.Deployment; import org.jboss.arquillian.container.test.api.OperateOnDeployment; @@ -63,11 +65,26 @@ public class OidcWithSubsystemConfigTest extends OidcBaseTest { private static final String SUBSYSTEM_OVERRIDE_APP = "SubsystemOverrideOidcApp"; private static final String OIDC_JSON_WITH_SUBSYSTEM_OVERRIDE_FILE = "OidcWithSubsystemOverride.json"; private static final String KEYCLOAK_PROVIDER = "keycloak"; - private static final String[] APP_NAMES = new String[] {PROVIDER_URL_APP, AUTH_SERVER_URL_APP, WRONG_PROVIDER_URL_APP, WRONG_SECRET_APP, SUBSYSTEM_OVERRIDE_APP}; + private static Map APP_NAMES; private static final String SECURE_DEPLOYMENT_ADDRESS = "subsystem=" + ElytronOidcExtension.SUBSYSTEM_NAME + "/secure-deployment="; private static final String PROVIDER_ADDRESS = "subsystem=" + ElytronOidcExtension.SUBSYSTEM_NAME + "/provider="; private static final String REALM_ADDRESS = "subsystem=" + ElytronOidcExtension.SUBSYSTEM_NAME + "/realm="; + static { + APP_NAMES = new HashMap<>(); + APP_NAMES.put(PROVIDER_URL_APP, KeycloakConfiguration.ClientAppType.OIDC_CLIENT); + APP_NAMES.put(AUTH_SERVER_URL_APP, KeycloakConfiguration.ClientAppType.OIDC_CLIENT); + APP_NAMES.put(WRONG_PROVIDER_URL_APP, KeycloakConfiguration.ClientAppType.OIDC_CLIENT); + APP_NAMES.put(WRONG_SECRET_APP, KeycloakConfiguration.ClientAppType.OIDC_CLIENT); + APP_NAMES.put(SUBSYSTEM_OVERRIDE_APP, KeycloakConfiguration.ClientAppType.OIDC_CLIENT); + APP_NAMES.put(DIRECT_ACCCESS_GRANT_ENABLED_CLIENT, KeycloakConfiguration.ClientAppType.DIRECT_ACCESS_GRANT_OIDC_CLIENT); + APP_NAMES.put(BEARER_ONLY_AUTH_SERVER_URL_APP, KeycloakConfiguration.ClientAppType.BEARER_ONLY_CLIENT); + APP_NAMES.put(BEARER_ONLY_PROVIDER_URL_APP, KeycloakConfiguration.ClientAppType.BEARER_ONLY_CLIENT); + APP_NAMES.put(BASIC_AUTH_PROVIDER_URL_APP, KeycloakConfiguration.ClientAppType.BEARER_ONLY_CLIENT); + APP_NAMES.put(CORS_PROVIDER_URL_APP, KeycloakConfiguration.ClientAppType.BEARER_ONLY_CLIENT); + APP_NAMES.put(CORS_CLIENT, KeycloakConfiguration.ClientAppType.CORS_CLIENT); + } + @Deployment(name = PROVIDER_URL_APP) public static WebArchive createProviderUrlDeployment() { return ShrinkWrap.create(WebArchive.class, PROVIDER_URL_APP + ".war") @@ -104,6 +121,34 @@ public static WebArchive createSubsystemOverrideDeployment() { .addAsWebInfResource(OidcWithSubsystemConfigTest.class.getPackage(), OIDC_JSON_WITH_SUBSYSTEM_OVERRIDE_FILE, "oidc.json"); // has bad provider url } + @Deployment(name = BEARER_ONLY_AUTH_SERVER_URL_APP) + public static WebArchive createBearerOnlyAuthServerUrlDeployment() { + return ShrinkWrap.create(WebArchive.class, BEARER_ONLY_AUTH_SERVER_URL_APP + ".war") + .addClasses(SimpleServlet.class) + .addClasses(SimpleSecuredServlet.class); + } + + @Deployment(name = BEARER_ONLY_PROVIDER_URL_APP) + public static WebArchive createBearerOnlyProviderUrlDeployment() { + return ShrinkWrap.create(WebArchive.class, BEARER_ONLY_PROVIDER_URL_APP + ".war") + .addClasses(SimpleServlet.class) + .addClasses(SimpleSecuredServlet.class); + } + + @Deployment(name = BASIC_AUTH_PROVIDER_URL_APP) + public static WebArchive createBasicAuthProviderUrlDeployment() { + return ShrinkWrap.create(WebArchive.class, BASIC_AUTH_PROVIDER_URL_APP + ".war") + .addClasses(SimpleServlet.class) + .addClasses(SimpleSecuredServlet.class); + } + + @Deployment(name = CORS_PROVIDER_URL_APP) + public static WebArchive createCorsProviderUrlDeployment() { + return ShrinkWrap.create(WebArchive.class, CORS_PROVIDER_URL_APP + ".war") + .addClasses(SimpleServlet.class) + .addClasses(SimpleSecuredServlet.class); + } + @Test @OperateOnDeployment(SUBSYSTEM_OVERRIDE_APP) public void testSubsystemOverride() throws Exception { @@ -182,14 +227,53 @@ public void setup(ManagementClient managementClient, String containerId) throws operation.get("secret").set("secret"); Utils.applyUpdate(operation, client); + operation = createOpNode(SECURE_DEPLOYMENT_ADDRESS + BEARER_ONLY_AUTH_SERVER_URL_APP + ".war", ModelDescriptionConstants.ADD); + operation.get("resource").set(BEARER_ONLY_AUTH_SERVER_URL_APP); + operation.get("public-client").set(false); + operation.get("realm").set(TEST_REALM); + operation.get("ssl-required").set("EXTERNAL"); + operation.get("bearer-only").set("true"); + Utils.applyUpdate(operation, client); + + operation = createOpNode(SECURE_DEPLOYMENT_ADDRESS + BEARER_ONLY_PROVIDER_URL_APP + ".war", ModelDescriptionConstants.ADD); + operation.get("client-id").set(BEARER_ONLY_PROVIDER_URL_APP); + operation.get("public-client").set(false); + operation.get("provider").set(KEYCLOAK_PROVIDER); + operation.get("ssl-required").set("EXTERNAL"); + operation.get("bearer-only").set("true"); + Utils.applyUpdate(operation, client); + + operation = createOpNode(SECURE_DEPLOYMENT_ADDRESS + BASIC_AUTH_PROVIDER_URL_APP + ".war", ModelDescriptionConstants.ADD); + operation.get("client-id").set(DIRECT_ACCCESS_GRANT_ENABLED_CLIENT); + operation.get("public-client").set(false); + operation.get("provider").set(KEYCLOAK_PROVIDER); + operation.get("ssl-required").set("EXTERNAL"); + operation.get("enable-basic-auth").set("true"); + Utils.applyUpdate(operation, client); + + operation = createOpNode(SECURE_DEPLOYMENT_ADDRESS + BASIC_AUTH_PROVIDER_URL_APP + ".war/credential=secret", ModelDescriptionConstants.ADD); + operation.get("secret").set("secret"); + Utils.applyUpdate(operation, client); + + operation = createOpNode(SECURE_DEPLOYMENT_ADDRESS + CORS_PROVIDER_URL_APP + ".war", ModelDescriptionConstants.ADD); + operation.get("client-id").set(CORS_PROVIDER_URL_APP); + operation.get("public-client").set(false); + operation.get("provider").set(KEYCLOAK_PROVIDER); + operation.get("ssl-required").set("EXTERNAL"); + operation.get("bearer-only").set("true"); + operation.get("enable-cors").set("true"); + Utils.applyUpdate(operation, client); + ServerReload.executeReloadAndWaitForCompletion(managementClient); } @Override public void tearDown(ManagementClient managementClient, String containerId) throws Exception { ModelControllerClient client = managementClient.getControllerClient(); - for (String appName : APP_NAMES) { - removeSecureDeployment(client, appName); + for (String appName : APP_NAMES.keySet()) { + if (! appName.equals(CORS_CLIENT) && ! appName.equals(DIRECT_ACCCESS_GRANT_ENABLED_CLIENT)) { + removeSecureDeployment(client, appName); + } } removeProvider(client, KEYCLOAK_PROVIDER); diff --git a/testsuite/integration/elytron-oidc-client/src/test/resources/org/wildfly/test/integration/elytron/oidc/client/deployment/BasicAuthWithProviderUrl.json b/testsuite/integration/elytron-oidc-client/src/test/resources/org/wildfly/test/integration/elytron/oidc/client/deployment/BasicAuthWithProviderUrl.json new file mode 100644 index 000000000000..bcd746bc422e --- /dev/null +++ b/testsuite/integration/elytron-oidc-client/src/test/resources/org/wildfly/test/integration/elytron/oidc/client/deployment/BasicAuthWithProviderUrl.json @@ -0,0 +1,9 @@ +{ + "client-id" : "DirectAccessGrantEnabledClient", + "provider-url" : "${oidc.provider.url}", + "ssl-required" : "EXTERNAL", + "enable-basic-auth" : "true", + "credentials" : { + "secret" : "secret" + } +} diff --git a/testsuite/integration/elytron-oidc-client/src/test/resources/org/wildfly/test/integration/elytron/oidc/client/deployment/BearerOnlyWithAuthServerUrl.json b/testsuite/integration/elytron-oidc-client/src/test/resources/org/wildfly/test/integration/elytron/oidc/client/deployment/BearerOnlyWithAuthServerUrl.json new file mode 100644 index 000000000000..596ed5a23fa4 --- /dev/null +++ b/testsuite/integration/elytron-oidc-client/src/test/resources/org/wildfly/test/integration/elytron/oidc/client/deployment/BearerOnlyWithAuthServerUrl.json @@ -0,0 +1,7 @@ +{ + "realm" : "WildFly", + "resource" : "AuthServerUrlBearerOnlyApp", + "auth-server-url" : "${oidc.auth.server.url}", + "ssl-required" : "EXTERNAL", + "bearer-only" : "true" +} diff --git a/testsuite/integration/elytron-oidc-client/src/test/resources/org/wildfly/test/integration/elytron/oidc/client/deployment/BearerOnlyWithProviderUrl.json b/testsuite/integration/elytron-oidc-client/src/test/resources/org/wildfly/test/integration/elytron/oidc/client/deployment/BearerOnlyWithProviderUrl.json new file mode 100644 index 000000000000..dc6622e53541 --- /dev/null +++ b/testsuite/integration/elytron-oidc-client/src/test/resources/org/wildfly/test/integration/elytron/oidc/client/deployment/BearerOnlyWithProviderUrl.json @@ -0,0 +1,7 @@ +{ + "client-id" : "ProviderUrlBearerOnlyApp", + "public-client" : "false", + "provider-url" : "${oidc.provider.url}", + "ssl-required" : "EXTERNAL", + "bearer-only" : "true" +} diff --git a/testsuite/integration/elytron-oidc-client/src/test/resources/org/wildfly/test/integration/elytron/oidc/client/deployment/CorsWithProviderUrl.json b/testsuite/integration/elytron-oidc-client/src/test/resources/org/wildfly/test/integration/elytron/oidc/client/deployment/CorsWithProviderUrl.json new file mode 100644 index 000000000000..8bd14836173b --- /dev/null +++ b/testsuite/integration/elytron-oidc-client/src/test/resources/org/wildfly/test/integration/elytron/oidc/client/deployment/CorsWithProviderUrl.json @@ -0,0 +1,8 @@ +{ + "client-id" : "DirectAccessGrantEnabledClient", + "public-client" : "false", + "provider-url" : "${oidc.provider.url}", + "ssl-required" : "EXTERNAL", + "enable-cors" : "true", + "bearer-only" : "true" +}