From 7f691b463c62ae60909e3c0b27d03e4ba5625ab3 Mon Sep 17 00:00:00 2001 From: Stian Thorgersen Date: Sun, 3 Nov 2013 13:33:22 +0000 Subject: [PATCH] KEYCLOAK-75 Retrieve user profile --- .../managers/AuthenticationManager.java | 88 +++++++-- .../services/managers/RealmManager.java | 8 +- .../services/resources/AccountService.java | 49 +++-- .../org/keycloak/services/resources/Cors.java | 69 ++++++- .../services/resources/TokenService.java | 2 +- testsuite/integration/pom.xml | 4 + .../testsuite/account/ProfileTest.java | 174 ++++++++++++++++++ .../org/keycloak/testsuite/rule/WebRule.java | 1 + 8 files changed, 359 insertions(+), 36 deletions(-) create mode 100644 testsuite/integration/src/test/java/org/keycloak/testsuite/account/ProfileTest.java diff --git a/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java b/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java index 8ffbf94dee..5fe44751f0 100755 --- a/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java +++ b/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java @@ -52,24 +52,27 @@ public class AuthenticationManager { String cookieName = KEYCLOAK_IDENTITY_COOKIE; URI uri = RealmsResource.realmBaseUrl(uriInfo).build(realm.getId()); String cookiePath = uri.getPath(); - return createLoginCookie(realm, user, cookieName, cookiePath); + return createLoginCookie(realm, user, null, cookieName, cookiePath); } public NewCookie createSaasIdentityCookie(RealmModel realm, UserModel user, UriInfo uriInfo) { String cookieName = SaasService.SAAS_IDENTITY_COOKIE; URI uri = SaasService.saasCookiePath(uriInfo).build(); String cookiePath = uri.getPath(); - return createLoginCookie(realm, user, cookieName, cookiePath); + return createLoginCookie(realm, user, null, cookieName, cookiePath); } - public NewCookie createAccountIdentityCookie(RealmModel realm, UserModel user, URI uri) { + public NewCookie createAccountIdentityCookie(RealmModel realm, UserModel user, UserModel client, URI uri) { String cookieName = AccountService.ACCOUNT_IDENTITY_COOKIE; String cookiePath = uri.getPath(); - return createLoginCookie(realm, user, cookieName, cookiePath); + return createLoginCookie(realm, user, client, cookieName, cookiePath); } - protected NewCookie createLoginCookie(RealmModel realm, UserModel user, String cookieName, String cookiePath) { + protected NewCookie createLoginCookie(RealmModel realm, UserModel user, UserModel client, String cookieName, String cookiePath) { SkeletonKeyToken identityToken = createIdentityToken(realm, user.getLoginName()); + if (client != null) { + identityToken.issuedFor(client.getLoginName()); + } String encoded = encodeToken(realm, identityToken); boolean secureOnly = !realm.isSslNotRequired(); logger.debug("creatingLoginCookie - name: {0} path: {1}", cookieName, cookiePath); @@ -123,15 +126,17 @@ public class AuthenticationManager { public UserModel authenticateIdentityCookie(RealmModel realm, UriInfo uriInfo, HttpHeaders headers) { String cookieName = KEYCLOAK_IDENTITY_COOKIE; - return authenticateIdentityCookie(realm, uriInfo, headers, cookieName); + Auth auth = authenticateIdentityCookie(realm, uriInfo, headers, cookieName); + return auth != null ? auth.getUser() : null; } public UserModel authenticateSaasIdentityCookie(RealmModel realm, UriInfo uriInfo, HttpHeaders headers) { String cookieName = SaasService.SAAS_IDENTITY_COOKIE; - return authenticateIdentityCookie(realm, uriInfo, headers, cookieName); + Auth auth = authenticateIdentityCookie(realm, uriInfo, headers, cookieName); + return auth != null ? auth.getUser() : null; } - public UserModel authenticateAccountIdentityCookie(RealmModel realm, UriInfo uriInfo, HttpHeaders headers) { + public Auth authenticateAccountIdentityCookie(RealmModel realm, UriInfo uriInfo, HttpHeaders headers) { String cookieName = AccountService.ACCOUNT_IDENTITY_COOKIE; return authenticateIdentityCookie(realm, uriInfo, headers, cookieName); } @@ -140,11 +145,19 @@ public class AuthenticationManager { UserModel user = authenticateSaasIdentityCookie(realm, uriInfo, headers); if (user != null) return user; + Auth auth = authenticateBearerToken(realm, headers); + return auth != null ? auth.getUser() : null; + } + + public Auth authenticateAccountIdentity(RealmModel realm, UriInfo uriInfo, HttpHeaders headers) { + Auth auth = authenticateAccountIdentityCookie(realm, uriInfo, headers); + if (auth != null) return auth; + return authenticateBearerToken(realm, headers); } - protected UserModel authenticateIdentityCookie(RealmModel realm, UriInfo uriInfo, HttpHeaders headers, String cookieName) { + protected Auth authenticateIdentityCookie(RealmModel realm, UriInfo uriInfo, HttpHeaders headers, String cookieName) { Cookie cookie = headers.getCookies().get(cookieName); if (cookie == null) { logger.debug("authenticateCookie could not find cookie: {0}", cookieName); @@ -159,13 +172,28 @@ public class AuthenticationManager { expireIdentityCookie(realm, uriInfo); return null; } + + Auth auth = new Auth(); + UserModel user = realm.getUser(token.getPrincipal()); if (user == null || !user.isEnabled()) { logger.debug("Unknown user in identity cookie"); expireIdentityCookie(realm, uriInfo); return null; } - return user; + auth.setUser(user); + + if (token.getIssuedFor() != null) { + UserModel client = realm.getUser(token.getIssuedFor()); + if (client == null || !client.isEnabled()) { + logger.debug("Unknown client in identity cookie"); + expireIdentityCookie(realm, uriInfo); + return null; + } + auth.setClient(client); + } + + return auth; } catch (VerificationException e) { logger.debug("Failed to verify identity cookie", e); expireIdentityCookie(realm, uriInfo); @@ -173,11 +201,11 @@ public class AuthenticationManager { return null; } - public UserModel authenticateBearerToken(RealmModel realm, HttpHeaders headers) { + public Auth authenticateBearerToken(RealmModel realm, HttpHeaders headers) { String tokenString = null; String authHeader = headers.getHeaderString(HttpHeaders.AUTHORIZATION); if (authHeader == null) { - throw new NotAuthorizedException("Bearer"); + return null; } else { String[] split = authHeader.trim().split("\\s+"); if (split == null || split.length != 2) throw new NotAuthorizedException("Bearer"); @@ -191,11 +219,24 @@ public class AuthenticationManager { if (!token.isActive()) { throw new NotAuthorizedException("token_expired"); } + + Auth auth = new Auth(); + UserModel user = realm.getUser(token.getPrincipal()); if (user == null || !user.isEnabled()) { throw new NotAuthorizedException("invalid_user"); } - return user; + auth.setUser(user); + + if (token.getIssuedFor() != null) { + UserModel client = realm.getUser(token.getIssuedFor()); + if (client == null || !client.isEnabled()) { + throw new NotAuthorizedException("invalid_user"); + } + auth.setClient(client); + } + + return auth; } catch (VerificationException e) { logger.error("Failed to verify token", e); throw new NotAuthorizedException("invalid_token"); @@ -267,4 +308,25 @@ public class AuthenticationManager { SUCCESS, ACCOUNT_DISABLED, ACTIONS_REQUIRED, INVALID_USER, INVALID_CREDENTIALS, MISSING_PASSWORD, MISSING_TOTP, FAILED } + public static class Auth { + private UserModel user; + private UserModel client; + + public UserModel getUser() { + return user; + } + + public UserModel getClient() { + return client; + } + + void setUser(UserModel user) { + this.user = user; + } + + void setClient(UserModel client) { + this.client = client; + } + } + } diff --git a/services/src/main/java/org/keycloak/services/managers/RealmManager.java b/services/src/main/java/org/keycloak/services/managers/RealmManager.java index 1387f12fee..14220b27d8 100755 --- a/services/src/main/java/org/keycloak/services/managers/RealmManager.java +++ b/services/src/main/java/org/keycloak/services/managers/RealmManager.java @@ -388,9 +388,11 @@ public class RealmManager { rep.setLastName(user.getLastName()); rep.setFirstName(user.getFirstName()); rep.setEmail(user.getEmail()); - Map attrs = new HashMap(); - attrs.putAll(user.getAttributes()); - rep.setAttributes(attrs); + if (user.getAttributes() != null && !user.getAttributes().isEmpty()) { + Map attrs = new HashMap(); + attrs.putAll(user.getAttributes()); + rep.setAttributes(attrs); + } return rep; } diff --git a/services/src/main/java/org/keycloak/services/resources/AccountService.java b/services/src/main/java/org/keycloak/services/resources/AccountService.java index 88572713f4..ac4fabf2c9 100755 --- a/services/src/main/java/org/keycloak/services/resources/AccountService.java +++ b/services/src/main/java/org/keycloak/services/resources/AccountService.java @@ -23,6 +23,7 @@ package org.keycloak.services.resources; import java.net.URI; import java.util.HashSet; +import java.util.List; import java.util.Set; import javax.ws.rs.*; @@ -39,6 +40,7 @@ import org.keycloak.AbstractOAuthClient; import org.keycloak.jaxrs.JaxrsOAuthClient; import org.keycloak.models.*; import org.keycloak.representations.idm.CredentialRepresentation; +import org.keycloak.representations.idm.UserRepresentation; import org.keycloak.services.email.EmailSender; import org.keycloak.services.managers.AccessCodeEntry; import org.keycloak.services.managers.AuthenticationManager; @@ -89,18 +91,32 @@ public class AccountService { } private Response forwardToPage(String path, String template) { - UserModel user = getUser(false); - if (user != null) { - return Flows.forms(realm, request, uriInfo).setUser(user).forwardToForm(template); + AuthenticationManager.Auth auth = getAuth(false); + if (auth != null) { + return Flows.forms(realm, request, uriInfo).setUser(auth.getUser()).forwardToForm(template); } else { return login(path); } } + @Path("") + @OPTIONS + public Response accountPreflight() { + return Cors.add(request, Response.ok()).auth().preflight().build(); + } + @Path("") @GET public Response accountPage() { - return forwardToPage(null, Pages.ACCOUNT); + List types = headers.getAcceptableMediaTypes(); + if (types.contains(MediaType.WILDCARD_TYPE) || (types.contains(MediaType.TEXT_HTML_TYPE))) { + return forwardToPage(null, Pages.ACCOUNT); + } else if (types.contains(MediaType.APPLICATION_JSON_TYPE)) { + AuthenticationManager.Auth auth = getAuth(true); + return Cors.add(request, Response.ok(RealmManager.toRepresentation(auth.getUser()))).auth().allowedOrigins(auth.getClient()).build(); + } else { + return Response.notAcceptable(Variant.VariantListBuilder.newInstance().mediaTypes(MediaType.TEXT_HTML_TYPE, MediaType.APPLICATION_JSON_TYPE).build()).build(); + } } @Path("social") @@ -131,8 +147,8 @@ public class AccountService { @POST @Consumes(MediaType.APPLICATION_FORM_URLENCODED) public Response processAccountUpdate(final MultivaluedMap formData) { - - UserModel user = getUser(true); + AuthenticationManager.Auth auth = getAuth(true); + UserModel user = auth.getUser(); String error = Validation.validateUpdateProfileForm(formData); if (error != null) { @@ -150,7 +166,9 @@ public class AccountService { @Path("totp-remove") @GET public Response processTotpRemove() { - UserModel user = getUser(true); + AuthenticationManager.Auth auth = getAuth(true); + UserModel user = auth.getUser(); + user.setTotp(false); return Flows.forms(realm, request, uriInfo).setError("successTotpRemoved").setErrorType(FormFlows.MessageType.SUCCESS) .setUser(user).forwardToTotp(); @@ -160,7 +178,8 @@ public class AccountService { @POST @Consumes(MediaType.APPLICATION_FORM_URLENCODED) public Response processTotpUpdate(final MultivaluedMap formData) { - UserModel user = getUser(true); + AuthenticationManager.Auth auth = getAuth(true); + UserModel user = auth.getUser(); String totp = formData.getFirst("totp"); String totpSecret = formData.getFirst("totpSecret"); @@ -187,7 +206,8 @@ public class AccountService { @POST @Consumes(MediaType.APPLICATION_FORM_URLENCODED) public Response processPasswordUpdate(final MultivaluedMap formData) { - UserModel user = getUser(true); + AuthenticationManager.Auth auth = getAuth(true); + UserModel user = auth.getUser(); FormFlows forms = Flows.forms(realm, request, uriInfo).setUser(user); @@ -288,7 +308,7 @@ public class AccountService { } URI redirectUri = redirectBuilder.build(realm.getId()); - NewCookie cookie = authManager.createAccountIdentityCookie(realm, accessCode.getUser(), Urls.accountBase(uriInfo.getBaseUri()).build(realm.getId())); + NewCookie cookie = authManager.createAccountIdentityCookie(realm, accessCode.getUser(), client, Urls.accountBase(uriInfo.getBaseUri()).build(realm.getId())); return Response.status(302).cookie(cookie).location(redirectUri).build(); } finally { authManager.expireCookie(AbstractOAuthClient.OAUTH_TOKEN_REQUEST_STATE, uriInfo.getAbsolutePath().getPath()); @@ -318,11 +338,12 @@ public class AccountService { return oauth.redirect(uriInfo, accountUri.toString(), path); } - private UserModel getUser(boolean required) { - UserModel user = authManager.authenticateAccountIdentityCookie(realm, uriInfo, headers); - if (user == null && required) { + private AuthenticationManager.Auth getAuth(boolean required) { + AuthenticationManager.Auth auth = authManager.authenticateAccountIdentity(realm, uriInfo, headers); + if (auth == null && required) { throw new ForbiddenException(); } - return user; + return auth; } + } diff --git a/services/src/main/java/org/keycloak/services/resources/Cors.java b/services/src/main/java/org/keycloak/services/resources/Cors.java index 8c28d6fa20..42c34d1896 100644 --- a/services/src/main/java/org/keycloak/services/resources/Cors.java +++ b/services/src/main/java/org/keycloak/services/resources/Cors.java @@ -1,20 +1,37 @@ package org.keycloak.services.resources; import java.util.Set; +import java.util.concurrent.TimeUnit; import javax.ws.rs.core.Response; import javax.ws.rs.core.Response.ResponseBuilder; import org.jboss.resteasy.spi.HttpRequest; +import org.keycloak.models.UserModel; /** * @author Stian Thorgersen */ public class Cors { + public static final long DEFAULT_MAX_AGE = TimeUnit.HOURS.toSeconds(1); + public static final String DEFAULT_ALLOW_METHODS = "GET, OPTIONS"; + + public static final String ORIGIN = "Origin"; + + public static final String ACCESS_CONTROL_ALLOW_ORIGIN = "Access-Control-Allow-Origin"; + public static final String ACCESS_CONTROL_ALLOW_METHODS = "Access-Control-Allow-Methods"; + public static final String ACCESS_CONTROL_ALLOW_HEADERS = "Access-Control-Allow-Headers"; + public static final String ACCESS_CONTROL_ALLOW_CREDENTIALS = "Access-Control-Allow-Credentials"; + public static final String ACCESS_CONTROL_MAX_AGE = "Access-Control-Max-Age"; + private HttpRequest request; private ResponseBuilder response; private Set allowedOrigins; + private String[] allowedMethods; + + private boolean preflight; + private boolean auth; public Cors(HttpRequest request, ResponseBuilder response) { this.request = request; @@ -25,18 +42,60 @@ public class Cors { return new Cors(request, response); } - public Cors allowedOrigins(Set allowedOrigins) { - this.allowedOrigins = allowedOrigins; + public Cors preflight() { + preflight = true; + return this; + } + + public Cors auth() { + auth = true; + return this; + } + + public Cors allowedOrigins(UserModel client) { + if (client != null) { + allowedOrigins = client.getWebOrigins(); + } + return this; + } + + public Cors allowedMethods(String... allowedMethods) { + this.allowedMethods = allowedMethods; return this; } public Response build() { - String origin = request.getHttpHeaders().getHeaderString("Origin"); - if (origin == null || allowedOrigins == null || (!allowedOrigins.contains(origin))) { + String origin = request.getHttpHeaders().getHeaderString(ORIGIN); + if (origin == null) { return response.build(); } - response.header("Access-Control-Allow-Origin", origin); + if (!preflight && (allowedOrigins == null || !allowedOrigins.contains(origin))) { + return response.build(); + } + + response.header(ACCESS_CONTROL_ALLOW_ORIGIN, origin); + + if (allowedMethods != null) { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < allowedMethods.length; i++) { + if (i > 0) { + sb.append(", "); + } + sb.append(allowedMethods[i]); + } + response.header(ACCESS_CONTROL_ALLOW_METHODS, sb.toString()); + } else { + response.header(ACCESS_CONTROL_ALLOW_METHODS, DEFAULT_ALLOW_METHODS); + } + + response.header(ACCESS_CONTROL_ALLOW_CREDENTIALS, Boolean.toString(auth)); + if (auth) { + response.header(ACCESS_CONTROL_ALLOW_HEADERS, "Authorization"); + } + + response.header(ACCESS_CONTROL_MAX_AGE, DEFAULT_MAX_AGE); + return response.build(); } diff --git a/services/src/main/java/org/keycloak/services/resources/TokenService.java b/services/src/main/java/org/keycloak/services/resources/TokenService.java index f118bc4427..7388b61667 100755 --- a/services/src/main/java/org/keycloak/services/resources/TokenService.java +++ b/services/src/main/java/org/keycloak/services/resources/TokenService.java @@ -413,7 +413,7 @@ public class TokenService { logger.debug("accessRequest SUCCESS"); AccessTokenResponse res = accessTokenResponse(realm.getPrivateKey(), accessCode.getToken()); - return Cors.add(request, Response.ok(res)).allowedOrigins(client.getWebOrigins()).build(); + return Cors.add(request, Response.ok(res)).allowedOrigins(client).build(); } protected AccessTokenResponse accessTokenResponse(PrivateKey privateKey, SkeletonKeyToken token) { diff --git a/testsuite/integration/pom.xml b/testsuite/integration/pom.xml index 0e121c7026..a411b48d76 100644 --- a/testsuite/integration/pom.xml +++ b/testsuite/integration/pom.xml @@ -189,6 +189,10 @@ org.seleniumhq.selenium selenium-java + + org.seleniumhq.selenium + selenium-chrome-driver + org.mongodb mongo-java-driver diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/account/ProfileTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/account/ProfileTest.java new file mode 100644 index 0000000000..cdcef48079 --- /dev/null +++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/account/ProfileTest.java @@ -0,0 +1,174 @@ +package org.keycloak.testsuite.account; + +import org.apache.commons.io.IOUtils; +import org.apache.http.HttpHeaders; +import org.apache.http.HttpResponse; +import org.apache.http.client.HttpClient; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.impl.client.DefaultHttpClient; +import org.json.JSONObject; +import org.junit.ClassRule; +import org.junit.Rule; +import org.junit.Test; +import org.keycloak.models.ApplicationModel; +import org.keycloak.models.RealmModel; +import org.keycloak.models.UserModel; +import org.keycloak.services.managers.RealmManager; +import org.keycloak.testsuite.Constants; +import org.keycloak.testsuite.OAuthClient; +import org.keycloak.testsuite.pages.AccountUpdateProfilePage; +import org.keycloak.testsuite.pages.LoginPage; +import org.keycloak.testsuite.rule.KeycloakRule; +import org.keycloak.testsuite.rule.WebResource; +import org.keycloak.testsuite.rule.WebRule; +import org.openqa.selenium.JavascriptExecutor; +import org.openqa.selenium.WebDriver; + +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.UriBuilder; +import java.io.IOException; +import java.net.URI; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +/** + * @author Stian Thorgersen + */ +public class ProfileTest { + + @ClassRule + public static KeycloakRule keycloakRule = new KeycloakRule(new KeycloakRule.KeycloakSetup() { + @Override + public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) { + UserModel user = appRealm.getUser("test-user@localhost"); + user.setFirstName("First"); + user.setLastName("Last"); + user.setAttribute("key1", "value1"); + user.setAttribute("key2", "value2"); + + for (ApplicationModel app : appRealm.getApplications()) { + if (app.getName().equals("test-app")) { + app.getApplicationUser().addWebOrigin("http://localtest.me:8081"); + } + } + } + }); + + @Rule + public WebRule webRule = new WebRule(this); + + @WebResource + protected WebDriver driver; + + @WebResource + protected OAuthClient oauth; + + @WebResource + protected AccountUpdateProfilePage profilePage; + + @WebResource + protected LoginPage loginPage; + + @Test + public void getProfile() throws Exception { + oauth.doLogin("test-user@localhost", "password"); + + String code = oauth.getCurrentQuery().get("code"); + String token = oauth.doAccessTokenRequest(code, "password").getAccessToken(); + + HttpResponse response = doGetProfile(token, null); + assertEquals(200, response.getStatusLine().getStatusCode()); + JSONObject profile = new JSONObject(IOUtils.toString(response.getEntity().getContent())); + + assertEquals("test-user@localhost", profile.getString("username")); + assertEquals("test-user@localhost", profile.getString("email")); + assertEquals("First", profile.getString("firstName")); + assertEquals("Last", profile.getString("lastName")); + + JSONObject attributes = profile.getJSONObject("attributes"); + assertEquals(2, attributes.length()); + assertEquals("value1", attributes.getString("key1")); + assertEquals("value2", attributes.getString("key2")); + } + + @Test + public void getProfileCors() throws Exception { + oauth.doLogin("test-user@localhost", "password"); + + String code = oauth.getCurrentQuery().get("code"); + String token = oauth.doAccessTokenRequest(code, "password").getAccessToken(); + + driver.navigate().to("http://localtest.me:8081/app"); + + String[] response = doGetProfileJs(token); + assertEquals("200", response[0]); + } + + @Test + public void getProfileCorsInvalidOrigin() throws Exception { + oauth.doLogin("test-user@localhost", "password"); + + String code = oauth.getCurrentQuery().get("code"); + String token = oauth.doAccessTokenRequest(code, "password").getAccessToken(); + + driver.navigate().to("http://invalid.localtest.me:8081"); + + try { + doGetProfileJs(token); + fail("Expected failure"); + } catch (Throwable t) { + } + } + + @Test + public void getProfileCookieAuth() throws Exception { + profilePage.open(); + loginPage.login("test-user@localhost", "password"); + + String[] response = doGetProfileJs(null); + assertEquals("200", response[0]); + + JSONObject profile = new JSONObject(response[1]); + assertEquals("test-user@localhost", profile.getString("username")); + } + + @Test + public void getProfileNoAuth() throws Exception { + HttpResponse response = doGetProfile(null, null); + assertEquals(403, response.getStatusLine().getStatusCode()); + } + + private URI getAccountURI() { + return UriBuilder.fromUri(Constants.AUTH_SERVER_ROOT + "/rest/realms/" + oauth.getRealm() + "/account").build(); + } + + private HttpResponse doGetProfile(String token, String origin) throws IOException { + HttpClient client = new DefaultHttpClient(); + HttpGet get = new HttpGet(UriBuilder.fromUri(getAccountURI()).build()); + if (token != null) { + get.setHeader(HttpHeaders.AUTHORIZATION, "bearer " + token); + } + if (origin != null) { + get.setHeader("Origin", origin); + } + get.setHeader(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON); + return client.execute(get); + } + + private String[] doGetProfileJs(String token) { + StringBuilder sb = new StringBuilder(); + sb.append("var req = new XMLHttpRequest();\n"); + sb.append("req.open('GET', '" + getAccountURI().toString() + "', false);\n"); + if (token != null) { + sb.append("req.setRequestHeader('Authorization', 'Bearer " + token + "');\n"); + } + sb.append("req.setRequestHeader('Accept', 'application/json');\n"); + sb.append("req.send(null);\n"); + sb.append("return req.status + '///' + req.responseText;\n"); + + JavascriptExecutor js = (JavascriptExecutor) driver; + String response = (String) js.executeScript(sb.toString()); + return response.split("///"); + } +} diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/rule/WebRule.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/rule/WebRule.java index 88301c5417..ccdca44f5d 100644 --- a/testsuite/integration/src/test/java/org/keycloak/testsuite/rule/WebRule.java +++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/rule/WebRule.java @@ -55,6 +55,7 @@ public class WebRule extends ExternalResource { if (browser.equals("htmlunit")) { HtmlUnitDriver d = new HtmlUnitDriver(); + d.getWebClient().getOptions().setJavaScriptEnabled(true); d.getWebClient().getOptions().setCssEnabled(false); driver = d; } else if (browser.equals("chrome")) {