diff --git a/core-jaxrs/pom.xml b/core-jaxrs/pom.xml index 2ca0392b71..0a403056d5 100755 --- a/core-jaxrs/pom.xml +++ b/core-jaxrs/pom.xml @@ -19,11 +19,6 @@ resteasy-jaxrs provided - - org.jboss.resteasy - resteasy-client - provided - org.keycloak keycloak-core diff --git a/examples/demo-template/database-service/pom.xml b/examples/demo-template/database-service/pom.xml index f85e1c5a52..a86ebe85d8 100755 --- a/examples/demo-template/database-service/pom.xml +++ b/examples/demo-template/database-service/pom.xml @@ -24,11 +24,6 @@ - - org.jboss.resteasy - resteasy-client - provided - org.jboss.resteasy resteasy-jaxrs diff --git a/forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/FreeMarkerLoginForms.java b/forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/FreeMarkerLoginForms.java index 94ab78420c..925b38f250 100755 --- a/forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/FreeMarkerLoginForms.java +++ b/forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/FreeMarkerLoginForms.java @@ -117,7 +117,10 @@ public class FreeMarkerLoginForms implements LoginForms { UriBuilder uriBuilder = UriBuilder.fromUri(requestURI); for (String k : queryParameterMap.keySet()) { - uriBuilder.replaceQueryParam(k, queryParameterMap.get(k).toArray()); + + Object[] objects = queryParameterMap.get(k).toArray(); + if (objects.length == 1 && objects[0] == null) continue; // + uriBuilder.replaceQueryParam(k, objects); } if (accessCode != null) { diff --git a/integration/jaxrs-oauth-client/pom.xml b/integration/jaxrs-oauth-client/pom.xml index f4243d8add..7530219d55 100755 --- a/integration/jaxrs-oauth-client/pom.xml +++ b/integration/jaxrs-oauth-client/pom.xml @@ -14,14 +14,22 @@ + + org.jboss.resteasy + jaxrs-api + ${resteasy.version.latest} + provided + org.jboss.resteasy resteasy-jaxrs + ${resteasy.version.latest} provided org.jboss.resteasy resteasy-client + ${resteasy.version.latest} provided @@ -43,6 +51,7 @@ org.jboss.resteasy resteasy-jackson-provider + ${resteasy.version.latest} provided diff --git a/model/tests/src/test/java/org/keycloak/model/test/AuthProvidersExternalModelTest.java b/model/tests/src/test/java/org/keycloak/model/test/AuthProvidersExternalModelTest.java index 1311440c10..58bb2947e7 100755 --- a/model/tests/src/test/java/org/keycloak/model/test/AuthProvidersExternalModelTest.java +++ b/model/tests/src/test/java/org/keycloak/model/test/AuthProvidersExternalModelTest.java @@ -6,9 +6,9 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import javax.ws.rs.core.MultivaluedHashMap; import javax.ws.rs.core.MultivaluedMap; +import org.jboss.resteasy.specimpl.MultivaluedMapImpl; import org.jboss.resteasy.spi.ResteasyProviderFactory; import org.junit.Assert; import org.junit.Before; @@ -186,7 +186,7 @@ public class AuthProvidersExternalModelTest extends AbstractModelTest { } public static MultivaluedMap createFormData(String username, String password) { - MultivaluedMap formData = new MultivaluedHashMap(); + MultivaluedMap formData = new MultivaluedMapImpl(); formData.add("username", username); formData.add(CredentialRepresentation.PASSWORD, password); return formData; diff --git a/model/tests/src/test/java/org/keycloak/model/test/AuthenticationManagerTest.java b/model/tests/src/test/java/org/keycloak/model/test/AuthenticationManagerTest.java index 533837c1f1..b6f841358d 100755 --- a/model/tests/src/test/java/org/keycloak/model/test/AuthenticationManagerTest.java +++ b/model/tests/src/test/java/org/keycloak/model/test/AuthenticationManagerTest.java @@ -1,5 +1,6 @@ package org.keycloak.model.test; +import org.jboss.resteasy.specimpl.MultivaluedMapImpl; import org.junit.Assert; import org.junit.Before; import org.junit.Test; @@ -13,7 +14,6 @@ import org.keycloak.representations.idm.CredentialRepresentation; import org.keycloak.services.managers.AuthenticationManager; import org.keycloak.services.managers.AuthenticationManager.AuthenticationStatus; -import javax.ws.rs.core.MultivaluedHashMap; import javax.ws.rs.core.MultivaluedMap; import java.util.Arrays; @@ -154,7 +154,7 @@ public class AuthenticationManagerTest extends AbstractModelTest { realm.updateCredential(user, credential); - formData = new MultivaluedHashMap(); + formData = new MultivaluedMapImpl(); formData.add("username", "test"); formData.add(CredentialRepresentation.PASSWORD, "password"); diff --git a/pom.xml b/pom.xml index e0553b7aea..c600c6bc42 100755 --- a/pom.xml +++ b/pom.xml @@ -13,7 +13,9 @@ 4.1.2 - 3.0.6.Final + 2.3.7.Final + 2.3.7.Final + 3.0.8.Final 1.0.0.Final 2.6.0.CR1 1.0.2.Final diff --git a/server/pom.xml b/server/pom.xml index 9cd98b6053..da9cbbfe55 100755 --- a/server/pom.xml +++ b/server/pom.xml @@ -184,11 +184,6 @@ resteasy-multipart-provider ${resteasy.version} - - org.jboss.resteasy - resteasy-client - ${resteasy.version} - org.jboss.resteasy async-http-servlet-3.0 diff --git a/services/pom.xml b/services/pom.xml index 03ff09f3e5..a0cad7b0ff 100755 --- a/services/pom.xml +++ b/services/pom.xml @@ -93,6 +93,11 @@ ${project.version} provided + + org.jboss.spec.javax.servlet + jboss-servlet-api_3.0_spec + provided + org.jboss.logging jboss-logging @@ -122,11 +127,6 @@ jaxrs-api provided - - org.jboss.resteasy - resteasy-client - provided - org.jboss.resteasy resteasy-crypto @@ -147,11 +147,6 @@ jackson-mapper-asl provided - - org.jboss.spec.javax.servlet - jboss-servlet-api_3.0_spec - provided - org.codehaus.jackson jackson-xc diff --git a/services/src/main/java/org/keycloak/services/ForbiddenException.java b/services/src/main/java/org/keycloak/services/ForbiddenException.java new file mode 100755 index 0000000000..5e1365d18c --- /dev/null +++ b/services/src/main/java/org/keycloak/services/ForbiddenException.java @@ -0,0 +1,48 @@ +package org.keycloak.services; + +import org.jboss.resteasy.spi.LoggableFailure; + +import javax.ws.rs.core.Response; + +/** + * To provide a typed exception for Forbidden (This doesn't exist in Resteasy 2.3.7) + */ +public class ForbiddenException extends LoggableFailure +{ + public ForbiddenException() + { + super(403); + } + + public ForbiddenException(String s) + { + super(s, 403); + } + + public ForbiddenException(String s, Response response) + { + super(s, response); + } + + public ForbiddenException(String s, Throwable throwable, Response response) + { + super(s, throwable, response); + } + + public ForbiddenException(String s, Throwable throwable) + { + super(s, throwable, 403); + } + + public ForbiddenException(Throwable throwable) + { + super(throwable, 403); + } + + public ForbiddenException(Throwable throwable, Response response) + { + super(throwable, response); + } + + +} \ No newline at end of file diff --git a/services/src/main/java/org/keycloak/services/filters/ClientConnectionFilter.java b/services/src/main/java/org/keycloak/services/filters/ClientConnectionFilter.java index d74a150bb8..7f9e74dfbd 100755 --- a/services/src/main/java/org/keycloak/services/filters/ClientConnectionFilter.java +++ b/services/src/main/java/org/keycloak/services/filters/ClientConnectionFilter.java @@ -18,7 +18,6 @@ import java.io.IOException; public class ClientConnectionFilter implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { - //To change body of implemented methods use File | Settings | File Templates. } @Override @@ -44,6 +43,5 @@ public class ClientConnectionFilter implements Filter { @Override public void destroy() { - //To change body of implemented methods use File | Settings | File Templates. } } diff --git a/services/src/main/java/org/keycloak/services/managers/AccessCodeEntry.java b/services/src/main/java/org/keycloak/services/managers/AccessCodeEntry.java index 6b9e50c645..abbc793f2d 100755 --- a/services/src/main/java/org/keycloak/services/managers/AccessCodeEntry.java +++ b/services/src/main/java/org/keycloak/services/managers/AccessCodeEntry.java @@ -1,5 +1,6 @@ package org.keycloak.services.managers; +import org.jboss.resteasy.specimpl.MultivaluedMapImpl; import org.keycloak.models.ClientModel; import org.keycloak.models.RealmModel; import org.keycloak.models.RoleModel; @@ -8,7 +9,6 @@ import org.keycloak.models.UserModel.RequiredAction; import org.keycloak.representations.AccessToken; import org.keycloak.util.Time; -import javax.ws.rs.core.MultivaluedHashMap; import javax.ws.rs.core.MultivaluedMap; import java.util.ArrayList; import java.util.List; @@ -35,7 +35,7 @@ public class AccessCodeEntry { protected Set requiredActions; protected ClientModel client; protected List realmRolesRequested = new ArrayList(); - MultivaluedMap resourceRolesRequested = new MultivaluedHashMap(); + MultivaluedMap resourceRolesRequested = new MultivaluedMapImpl(); public boolean isExpired() { return expiration != 0 && Time.currentTime() > expiration; diff --git a/services/src/main/java/org/keycloak/services/managers/AppAuthManager.java b/services/src/main/java/org/keycloak/services/managers/AppAuthManager.java index ae4f40d97b..7f4e95240a 100755 --- a/services/src/main/java/org/keycloak/services/managers/AppAuthManager.java +++ b/services/src/main/java/org/keycloak/services/managers/AppAuthManager.java @@ -3,6 +3,7 @@ package org.keycloak.services.managers; import org.jboss.resteasy.logging.Logger; import org.jboss.resteasy.spi.HttpResponse; import org.jboss.resteasy.spi.ResteasyProviderFactory; +import org.jboss.resteasy.spi.UnauthorizedException; import org.keycloak.RSATokenVerifier; import org.keycloak.VerificationException; import org.keycloak.jose.jws.JWSBuilder; @@ -15,8 +16,7 @@ import org.keycloak.models.UserModel; import org.keycloak.provider.ProviderSession; import org.keycloak.representations.AccessToken; -import javax.ws.rs.BadRequestException; -import javax.ws.rs.NotAuthorizedException; +import org.jboss.resteasy.spi.BadRequestException; import javax.ws.rs.core.Cookie; import javax.ws.rs.core.HttpHeaders; import javax.ws.rs.core.NewCookie; @@ -48,30 +48,30 @@ public class AppAuthManager extends AuthenticationManager { } if (!verifiedCode) { logger.debug("unverified access code"); - throw new BadRequestException(); + throw new BadRequestException("unverified access code"); } String key = input.readContentAsString(); AccessCodeEntry accessCode = tokenManager.pullAccessCode(key); if (accessCode == null) { logger.debug("bad access code"); - throw new BadRequestException(); + throw new BadRequestException("bad access code"); } if (accessCode.isExpired()) { logger.debug("access code expired"); - throw new BadRequestException(); + throw new BadRequestException("access code expired"); } if (!accessCode.getToken().isActive()) { logger.debug("access token expired"); - throw new BadRequestException(); + throw new BadRequestException("access token expired"); } if (!accessCode.getRealm().getId().equals(realm.getId())) { logger.debug("bad realm"); - throw new BadRequestException(); + throw new BadRequestException("bad realm"); } if (!client.getClientId().equals(accessCode.getClient().getClientId())) { logger.debug("bad client"); - throw new BadRequestException(); + throw new BadRequestException("bad client"); } return createLoginCookie(realm, accessCode.getUser(), accessCode.getClient(), cookieName, uri.getRawPath(), false); @@ -140,39 +140,39 @@ public class AppAuthManager extends AuthenticationManager { private Auth authenticateBearerToken(RealmModel realm, HttpHeaders headers) { String tokenString; - String authHeader = headers.getHeaderString(HttpHeaders.AUTHORIZATION); + String authHeader = headers.getRequestHeaders().getFirst(HttpHeaders.AUTHORIZATION); if (authHeader == null) { return null; } else { String[] split = authHeader.trim().split("\\s+"); - if (split == null || split.length != 2) throw new NotAuthorizedException("Bearer"); - if (!split[0].equalsIgnoreCase("Bearer")) throw new NotAuthorizedException("Bearer"); + if (split == null || split.length != 2) throw new UnauthorizedException("Bearer"); + if (!split[0].equalsIgnoreCase("Bearer")) throw new UnauthorizedException("Bearer"); tokenString = split[1]; } try { AccessToken token = RSATokenVerifier.verifyToken(tokenString, realm.getPublicKey(), realm.getName()); if (!token.isActive()) { - throw new NotAuthorizedException("token_expired"); + throw new UnauthorizedException("token_expired"); } UserModel user = realm.getUserById(token.getSubject()); if (user == null || !user.isEnabled()) { - throw new NotAuthorizedException("invalid_user"); + throw new UnauthorizedException("invalid_user"); } ClientModel client = null; if (token.getIssuedFor() != null) { client = realm.findClient(token.getIssuedFor()); if (client == null || !client.isEnabled()) { - throw new NotAuthorizedException("invalid_user"); + throw new UnauthorizedException("invalid_user"); } } return new Auth(token, user, client); } catch (VerificationException e) { logger.error("Failed to verify token", e); - throw new NotAuthorizedException("invalid_token"); + throw new UnauthorizedException("invalid_token"); } } 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 7852781f3f..3be892c040 100755 --- a/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java +++ b/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java @@ -87,7 +87,7 @@ public class AuthenticationManager { maxAge = realm.getCentralLoginLifespan(); logger.info("createLoginCookie maxAge: " + maxAge); } - NewCookie cookie = new NewCookie(cookieName, encoded, cookiePath, null, null, maxAge, secureOnly, true); + NewCookie cookie = new NewCookie(cookieName, encoded, cookiePath, null, null, maxAge, secureOnly);// todo httponly , true); return cookie; } @@ -95,7 +95,7 @@ public class AuthenticationManager { String path = getIdentityCookiePath(realm, uriInfo); boolean secureOnly = !realm.isSslNotRequired(); // remember me cookie should be persistent - NewCookie cookie = new NewCookie(KEYCLOAK_REMEMBER_ME, "true", path, null, null, realm.getCentralLoginLifespan(), secureOnly, true); + NewCookie cookie = new NewCookie(KEYCLOAK_REMEMBER_ME, "true", path, null, null, realm.getCentralLoginLifespan(), secureOnly);// todo httponly , true); return cookie; } diff --git a/services/src/main/java/org/keycloak/services/managers/ResourceAdminManager.java b/services/src/main/java/org/keycloak/services/managers/ResourceAdminManager.java index 24f3ee41f9..e87de5e062 100755 --- a/services/src/main/java/org/keycloak/services/managers/ResourceAdminManager.java +++ b/services/src/main/java/org/keycloak/services/managers/ResourceAdminManager.java @@ -1,7 +1,9 @@ package org.keycloak.services.managers; -import org.jboss.resteasy.client.jaxrs.ResteasyClient; -import org.jboss.resteasy.client.jaxrs.ResteasyClientBuilder; +import org.apache.http.client.HttpClient; +import org.jboss.resteasy.client.ClientRequest; +import org.jboss.resteasy.client.ClientResponse; +import org.jboss.resteasy.client.core.executors.ApacheHttpClient4Executor; import org.jboss.resteasy.logging.Logger; import org.keycloak.TokenIdGenerator; import org.keycloak.adapters.AdapterConstants; @@ -14,10 +16,12 @@ import org.keycloak.representations.adapters.action.SessionStats; import org.keycloak.representations.adapters.action.SessionStatsAction; import org.keycloak.representations.adapters.action.UserStats; import org.keycloak.representations.adapters.action.UserStatsAction; +import org.keycloak.services.util.HttpClientBuilder; import org.keycloak.util.Time; -import javax.ws.rs.client.Entity; +import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; +import javax.ws.rs.core.UriBuilder; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -30,44 +34,59 @@ public class ResourceAdminManager { protected static Logger logger = Logger.getLogger(ResourceAdminManager.class); public SessionStats getSessionStats(RealmModel realm, ApplicationModel application, boolean users) { - ResteasyClient client = new ResteasyClientBuilder() - .disableTrustManager() // todo fix this, should have a trust manager or a good default - .build(); + ApacheHttpClient4Executor executor = createExecutor(); try { - return getSessionStats(realm, application, users, client); + return getSessionStats(realm, application, users, executor); } finally { - client.close(); + executor.getHttpClient().getConnectionManager().shutdown(); } } - public SessionStats getSessionStats(RealmModel realm, ApplicationModel application, boolean users, ResteasyClient client) { + public static ApacheHttpClient4Executor createExecutor() { + HttpClient client = new HttpClientBuilder() + .disableTrustManager() // todo fix this, should have a trust manager or a good default + .build(); + return new ApacheHttpClient4Executor(client); + } + + public SessionStats getSessionStats(RealmModel realm, ApplicationModel application, boolean users, ApacheHttpClient4Executor client) { String managementUrl = application.getManagementUrl(); if (managementUrl != null) { SessionStatsAction adminAction = new SessionStatsAction(TokenIdGenerator.generateId(), Time.currentTime() + 30, application.getName()); adminAction.setListUsers(users); String token = new TokenManager().encodeToken(realm, adminAction); logger.info("session stats for application: {0} url: {1}", application.getName(), managementUrl); - Response response = client.target(managementUrl).path(AdapterConstants.K_GET_SESSION_STATS).request().post(Entity.text(token)); - if (response.getStatus() != 200) { - logger.warn("Failed to get stats: " + response.getStatus()); - return null; + ClientRequest request = client.createRequest(UriBuilder.fromUri(managementUrl).path(AdapterConstants.K_GET_SESSION_STATS).build().toString()); + ClientResponse response = null; + try { + response = request.body(MediaType.TEXT_PLAIN_TYPE, token).post(SessionStats.class); + } catch (Exception e) { + throw new RuntimeException(e); } - SessionStats stats = response.readEntity(SessionStats.class); - - // replace with username - if (users && stats.getUsers() != null) { - Map newUsers = new HashMap(); - for (Map.Entry entry : stats.getUsers().entrySet()) { - UserModel user = realm.getUserById(entry.getKey()); - if (user == null) continue; - newUsers.put(user.getLoginName(), entry.getValue()); - + try { + if (response.getStatus() != 200) { + logger.warn("Failed to get stats: " + response.getStatus()); + return null; } - stats.setUsers(newUsers); + SessionStats stats = response.getEntity(); + + // replace with username + if (users && stats.getUsers() != null) { + Map newUsers = new HashMap(); + for (Map.Entry entry : stats.getUsers().entrySet()) { + UserModel user = realm.getUserById(entry.getKey()); + if (user == null) continue; + newUsers.put(user.getLoginName(), entry.getValue()); + + } + stats.setUsers(newUsers); + } + return stats; + } finally { + response.releaseConnection(); } - return stats; } else { logger.info("no management url."); return null; @@ -76,32 +95,41 @@ public class ResourceAdminManager { } public UserStats getUserStats(RealmModel realm, ApplicationModel application, UserModel user) { - ResteasyClient client = new ResteasyClientBuilder() - .disableTrustManager() // todo fix this, should have a trust manager or a good default - .build(); + ApacheHttpClient4Executor executor = createExecutor(); try { - return getUserStats(realm, application, user, client); + return getUserStats(realm, application, user, executor); } finally { - client.close(); + executor.getHttpClient().getConnectionManager().shutdown(); } } - public UserStats getUserStats(RealmModel realm, ApplicationModel application, UserModel user, ResteasyClient client) { + public UserStats getUserStats(RealmModel realm, ApplicationModel application, UserModel user, ApacheHttpClient4Executor client) { String managementUrl = application.getManagementUrl(); if (managementUrl != null) { UserStatsAction adminAction = new UserStatsAction(TokenIdGenerator.generateId(), Time.currentTime() + 30, application.getName(), user.getId()); String token = new TokenManager().encodeToken(realm, adminAction); logger.info("session stats for application: {0} url: {1}", application.getName(), managementUrl); - Response response = client.target(managementUrl).path(AdapterConstants.K_GET_USER_STATS).request().post(Entity.text(token)); - if (response.getStatus() != 200) { - logger.warn("Failed to get stats: " + response.getStatus()); - return null; + ClientRequest request = client.createRequest(UriBuilder.fromUri(managementUrl).path(AdapterConstants.K_GET_USER_STATS).build().toString()); + ClientResponse response = null; + try { + response = request.body(MediaType.TEXT_PLAIN_TYPE, token).post(UserStats.class); + } catch (Exception e) { + throw new RuntimeException(e); + } + + try { + if (response.getStatus() != 200) { + logger.warn("Failed to get stats: " + response.getStatus()); + return null; + } + UserStats stats = response.getEntity(); + return stats; + } finally { + response.releaseConnection(); } - UserStats stats = response.readEntity(UserStats.class); - return stats; } else { logger.info("no management url."); return null; @@ -110,64 +138,67 @@ public class ResourceAdminManager { } public void logoutUser(RealmModel realm, UserModel user) { - ResteasyClient client = new ResteasyClientBuilder() - .disableTrustManager() // todo fix this, should have a trust manager or a good default - .build(); + ApacheHttpClient4Executor executor = createExecutor(); try { // don't set user notBefore as we don't want a database hit on a user driven logout List resources = realm.getApplications(); logger.debug("logging out {0} resources ", resources.size()); for (ApplicationModel resource : resources) { - logoutApplication(realm, resource, user.getId(), client, 0); + logoutApplication(realm, resource, user.getId(), executor, 0); } } finally { - client.close(); + executor.getHttpClient().getConnectionManager().shutdown(); } } public void logoutAll(RealmModel realm) { - ResteasyClient client = new ResteasyClientBuilder() - .disableTrustManager() // todo fix this, should have a trust manager or a good default - .build(); + ApacheHttpClient4Executor executor = createExecutor(); try { realm.setNotBefore(Time.currentTime()); List resources = realm.getApplications(); logger.debug("logging out {0} resources ", resources.size()); for (ApplicationModel resource : resources) { - logoutApplication(realm, resource, null, client, realm.getNotBefore()); + logoutApplication(realm, resource, null, executor, realm.getNotBefore()); } } finally { - client.close(); + executor.getHttpClient().getConnectionManager().shutdown(); } } public void logoutApplication(RealmModel realm, ApplicationModel resource, String user) { - ResteasyClient client = new ResteasyClientBuilder() - .disableTrustManager() // todo fix this, should have a trust manager or a good default - .build(); + ApacheHttpClient4Executor executor = createExecutor(); try { resource.setNotBefore(Time.currentTime()); - logoutApplication(realm, resource, user, client, resource.getNotBefore()); + logoutApplication(realm, resource, user, executor, resource.getNotBefore()); } finally { - client.close(); + executor.getHttpClient().getConnectionManager().shutdown(); } } - protected boolean logoutApplication(RealmModel realm, ApplicationModel resource, String user, ResteasyClient client, int notBefore) { + protected boolean logoutApplication(RealmModel realm, ApplicationModel resource, String user, ApacheHttpClient4Executor client, int notBefore) { String managementUrl = resource.getManagementUrl(); if (managementUrl != null) { LogoutAction adminAction = new LogoutAction(TokenIdGenerator.generateId(), Time.currentTime() + 30, resource.getName(), user, notBefore); String token = new TokenManager().encodeToken(realm, adminAction); logger.info("logout user: {0} resource: {1} url: {2}", user, resource.getName(), managementUrl); - Response response = client.target(managementUrl).path(AdapterConstants.K_LOGOUT).request().post(Entity.text(token)); - boolean success = response.getStatus() == 204; - response.close(); - logger.info("logout success."); - return success; + ClientRequest request = client.createRequest(UriBuilder.fromUri(managementUrl).path(AdapterConstants.K_LOGOUT).build().toString()); + ClientResponse response = null; + try { + response = request.body(MediaType.TEXT_PLAIN_TYPE, token).post(UserStats.class); + } catch (Exception e) { + throw new RuntimeException(e); + } + try { + boolean success = response.getStatus() == 204; + logger.info("logout success."); + return success; + } finally { + response.releaseConnection(); + } } else { logger.info("Can't logout" + resource.getName() + " no mgmt url."); return false; @@ -175,44 +206,50 @@ public class ResourceAdminManager { } public void pushRealmRevocationPolicy(RealmModel realm) { - ResteasyClient client = new ResteasyClientBuilder() - .disableTrustManager() // todo fix this, should have a trust manager or a good default - .build(); + ApacheHttpClient4Executor executor = createExecutor(); try { for (ApplicationModel application : realm.getApplications()) { - pushRevocationPolicy(realm, application, realm.getNotBefore(), client); + pushRevocationPolicy(realm, application, realm.getNotBefore(), executor); } } finally { - client.close(); + executor.getHttpClient().getConnectionManager().shutdown(); } } public void pushApplicationRevocationPolicy(RealmModel realm, ApplicationModel application) { - ResteasyClient client = new ResteasyClientBuilder() - .disableTrustManager() // todo fix this, should have a trust manager or a good default - .build(); + ApacheHttpClient4Executor executor = createExecutor(); try { - pushRevocationPolicy(realm, application, application.getNotBefore(), client); + pushRevocationPolicy(realm, application, application.getNotBefore(), executor); } finally { - client.close(); + executor.getHttpClient().getConnectionManager().shutdown(); } } - protected boolean pushRevocationPolicy(RealmModel realm, ApplicationModel resource, int notBefore, ResteasyClient client) { + protected boolean pushRevocationPolicy(RealmModel realm, ApplicationModel resource, int notBefore, ApacheHttpClient4Executor client) { if (notBefore <= 0) return false; String managementUrl = resource.getManagementUrl(); if (managementUrl != null) { PushNotBeforeAction adminAction = new PushNotBeforeAction(TokenIdGenerator.generateId(), Time.currentTime() + 30, resource.getName(), notBefore); String token = new TokenManager().encodeToken(realm, adminAction); logger.info("pushRevocation resource: {0} url: {1}", resource.getName(), managementUrl); - Response response = client.target(managementUrl).path(AdapterConstants.K_PUSH_NOT_BEFORE).request().post(Entity.text(token)); - boolean success = response.getStatus() == 204; - response.close(); - logger.info("pushRevocation success."); - return success; + ClientRequest request = client.createRequest(UriBuilder.fromUri(managementUrl).path(AdapterConstants.K_PUSH_NOT_BEFORE).build().toString()); + ClientResponse response = null; + try { + response = request.body(MediaType.TEXT_PLAIN_TYPE, token).post(); + } catch (Exception e) { + throw new RuntimeException(e); + } + + try { + boolean success = response.getStatus() == 204; + logger.info("pushRevocation success."); + return success; + } finally { + response.releaseConnection(); + } } else { logger.info("no management URL for application: " + resource.getName()); return false; diff --git a/services/src/main/java/org/keycloak/services/managers/TokenManager.java b/services/src/main/java/org/keycloak/services/managers/TokenManager.java index 73688b875b..0c158075b0 100755 --- a/services/src/main/java/org/keycloak/services/managers/TokenManager.java +++ b/services/src/main/java/org/keycloak/services/managers/TokenManager.java @@ -1,6 +1,7 @@ package org.keycloak.services.managers; import org.jboss.resteasy.logging.Logger; +import org.jboss.resteasy.specimpl.MultivaluedMapImpl; import org.keycloak.OAuthErrorException; import org.keycloak.audit.Audit; import org.keycloak.audit.Details; @@ -20,7 +21,6 @@ import org.keycloak.representations.IDToken; import org.keycloak.representations.RefreshToken; import org.keycloak.util.Time; -import javax.ws.rs.core.MultivaluedHashMap; import javax.ws.rs.core.MultivaluedMap; import java.io.IOException; import java.io.UnsupportedEncodingException; @@ -186,7 +186,7 @@ public class TokenManager { } public AccessToken createClientAccessToken(String scopeParam, RealmModel realm, ClientModel client, UserModel user) { - return createClientAccessToken(scopeParam, realm, client, user, new LinkedList(), new MultivaluedHashMap()); + return createClientAccessToken(scopeParam, realm, client, user, new LinkedList(), new MultivaluedMapImpl()); } public AccessToken createClientAccessToken(String scopeParam, RealmModel realm, ClientModel client, UserModel user, List realmRolesRequested, MultivaluedMap resourceRolesRequested) { 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 2530f77fba..5bf2a38bd5 100755 --- a/services/src/main/java/org/keycloak/services/resources/AccountService.java +++ b/services/src/main/java/org/keycloak/services/resources/AccountService.java @@ -22,6 +22,7 @@ package org.keycloak.services.resources; import org.jboss.resteasy.logging.Logger; +import org.jboss.resteasy.spi.BadRequestException; import org.jboss.resteasy.spi.HttpRequest; import org.keycloak.OAuth2Constants; import org.keycloak.account.Account; @@ -46,6 +47,7 @@ import org.keycloak.models.UserModel; import org.keycloak.models.utils.TimeBasedOTP; import org.keycloak.representations.idm.CredentialRepresentation; import org.keycloak.provider.ProviderSession; +import org.keycloak.services.ForbiddenException; import org.keycloak.services.managers.AppAuthManager; import org.keycloak.services.managers.Auth; import org.keycloak.services.managers.ModelToRepresentation; @@ -62,9 +64,7 @@ import org.keycloak.authentication.AuthProviderStatus; import org.keycloak.authentication.AuthenticationProviderException; import org.keycloak.authentication.AuthenticationProviderManager; -import javax.ws.rs.BadRequestException; import javax.ws.rs.Consumes; -import javax.ws.rs.ForbiddenException; import javax.ws.rs.GET; import javax.ws.rs.OPTIONS; import javax.ws.rs.POST; @@ -443,11 +443,11 @@ public class AccountService { } if (code == null) { logger.debug("code not specified"); - throw new BadRequestException(); + throw new BadRequestException("code not specified"); } if (state == null) { logger.debug("state not specified"); - throw new BadRequestException(); + throw new BadRequestException("state not specified"); } URI accountUri = Urls.accountBase(uriInfo.getBaseUri()).path("/").build(realm.getName()); 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 30f62946e3..e122f073de 100755 --- a/services/src/main/java/org/keycloak/services/resources/Cors.java +++ b/services/src/main/java/org/keycloak/services/resources/Cors.java @@ -66,7 +66,7 @@ public class Cors { } public Response build() { - String origin = request.getHttpHeaders().getHeaderString(ORIGIN); + String origin = request.getHttpHeaders().getRequestHeaders().getFirst(ORIGIN); if (origin == null) { return response.build(); } diff --git a/services/src/main/java/org/keycloak/services/resources/RealmsResource.java b/services/src/main/java/org/keycloak/services/resources/RealmsResource.java index b9447185aa..cea5959277 100755 --- a/services/src/main/java/org/keycloak/services/resources/RealmsResource.java +++ b/services/src/main/java/org/keycloak/services/resources/RealmsResource.java @@ -1,6 +1,8 @@ package org.keycloak.services.resources; import org.jboss.resteasy.logging.Logger; +import org.jboss.resteasy.spi.NotFoundException; +import org.jboss.resteasy.spi.ResteasyProviderFactory; import org.keycloak.audit.Audit; import org.keycloak.models.ApplicationModel; import org.keycloak.models.Constants; @@ -14,10 +16,8 @@ import org.keycloak.services.managers.RealmManager; import org.keycloak.services.managers.SocialRequestManager; import org.keycloak.services.managers.TokenManager; -import javax.ws.rs.NotFoundException; import javax.ws.rs.Path; import javax.ws.rs.PathParam; -import javax.ws.rs.container.ResourceContext; import javax.ws.rs.core.Context; import javax.ws.rs.core.HttpHeaders; import javax.ws.rs.core.UriBuilder; @@ -37,8 +37,10 @@ public class RealmsResource { @Context protected HttpHeaders headers; + /* @Context protected ResourceContext resourceContext; + */ @Context protected KeycloakSession session; @@ -68,7 +70,8 @@ public class RealmsResource { Audit audit = new AuditManager(realm, providers, clientConnection).createAudit(); AuthenticationManager authManager = new AuthenticationManager(providers); TokenService tokenService = new TokenService(realm, tokenManager, audit, authManager); - resourceContext.initResource(tokenService); + ResteasyProviderFactory.getInstance().injectProperties(tokenService); + //resourceContext.initResource(tokenService); return tokenService; } @@ -88,12 +91,13 @@ public class RealmsResource { ApplicationModel application = realm.getApplicationNameMap().get(Constants.ACCOUNT_MANAGEMENT_APP); if (application == null || !application.isEnabled()) { logger.debug("account management not enabled"); - throw new NotFoundException(); + throw new NotFoundException("account management not enabled"); } Audit audit = new AuditManager(realm, providers, clientConnection).createAudit(); AccountService accountService = new AccountService(realm, application, tokenManager, socialRequestManager, audit); - resourceContext.initResource(accountService); + ResteasyProviderFactory.getInstance().injectProperties(accountService); + //resourceContext.initResource(accountService); accountService.init(); return accountService; } @@ -103,7 +107,8 @@ public class RealmsResource { RealmManager realmManager = new RealmManager(session); RealmModel realm = locateRealm(name, realmManager); PublicRealmResource realmResource = new PublicRealmResource(realm); - resourceContext.initResource(realmResource); + ResteasyProviderFactory.getInstance().injectProperties(realmResource); + //resourceContext.initResource(realmResource); return realmResource; } diff --git a/services/src/main/java/org/keycloak/services/resources/SocialResource.java b/services/src/main/java/org/keycloak/services/resources/SocialResource.java index 2ca085ef77..b59a28cc6b 100755 --- a/services/src/main/java/org/keycloak/services/resources/SocialResource.java +++ b/services/src/main/java/org/keycloak/services/resources/SocialResource.java @@ -22,6 +22,7 @@ package org.keycloak.services.resources; import org.jboss.resteasy.logging.Logger; +import org.jboss.resteasy.specimpl.MultivaluedMapImpl; import org.jboss.resteasy.spi.HttpRequest; import org.keycloak.OAuth2Constants; import org.keycloak.audit.Audit; @@ -59,10 +60,9 @@ import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.QueryParam; -import javax.ws.rs.container.ResourceContext; import javax.ws.rs.core.Context; import javax.ws.rs.core.HttpHeaders; -import javax.ws.rs.core.MultivaluedHashMap; +import javax.ws.rs.core.MultivaluedMap; import javax.ws.rs.core.Response; import javax.ws.rs.core.Response.Status; import javax.ws.rs.core.UriBuilder; @@ -90,8 +90,10 @@ public class SocialResource { @Context private HttpRequest request; + /* @Context ResourceContext resourceContext; + */ @Context protected KeycloakSession session; @@ -166,7 +168,7 @@ public class SocialResource { try { socialUser = provider.processCallback(config, callback); } catch (SocialAccessDeniedException e) { - MultivaluedHashMap queryParms = new MultivaluedHashMap(); + MultivaluedMap queryParms = new MultivaluedMapImpl(); queryParms.putSingle(OAuth2Constants.CLIENT_ID, clientId); queryParms.putSingle(OAuth2Constants.STATE, state); queryParms.putSingle(OAuth2Constants.SCOPE, scope); @@ -216,7 +218,7 @@ public class SocialResource { logger.debug("Social provider " + provider.getId() + " linked with user " + authenticatedUser.getLoginName()); audit.success(); - return Response.status(Status.FOUND).location(UriBuilder.fromUri(redirectUri).build()).build(); + return Response.status(302).location(UriBuilder.fromUri(redirectUri).build()).build(); } if (user == null) { 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 a0cad95100..c00b09cd05 100755 --- a/services/src/main/java/org/keycloak/services/resources/TokenService.java +++ b/services/src/main/java/org/keycloak/services/resources/TokenService.java @@ -2,8 +2,12 @@ package org.keycloak.services.resources; import org.jboss.resteasy.annotations.cache.NoCache; import org.jboss.resteasy.logging.Logger; +import org.jboss.resteasy.spi.BadRequestException; import org.jboss.resteasy.spi.HttpRequest; import org.jboss.resteasy.spi.HttpResponse; +import org.jboss.resteasy.spi.NotAcceptableException; +import org.jboss.resteasy.spi.ResteasyProviderFactory; +import org.jboss.resteasy.spi.UnauthorizedException; import org.keycloak.OAuth2Constants; import org.keycloak.OAuthErrorException; import org.keycloak.audit.Audit; @@ -26,6 +30,7 @@ import org.keycloak.representations.AccessToken; import org.keycloak.representations.AccessTokenResponse; import org.keycloak.representations.idm.CredentialRepresentation; import org.keycloak.services.ClientConnection; +import org.keycloak.services.ForbiddenException; import org.keycloak.services.managers.AccessCodeEntry; import org.keycloak.services.managers.AuthenticationManager; import org.keycloak.services.managers.AuthenticationManager.AuthenticationStatus; @@ -40,19 +45,14 @@ import org.keycloak.authentication.AuthenticationProviderManager; import org.keycloak.util.BasicAuthHelper; import org.keycloak.util.Time; -import javax.ws.rs.BadRequestException; import javax.ws.rs.Consumes; -import javax.ws.rs.ForbiddenException; import javax.ws.rs.GET; import javax.ws.rs.HeaderParam; -import javax.ws.rs.NotAcceptableException; -import javax.ws.rs.NotAuthorizedException; import javax.ws.rs.OPTIONS; import javax.ws.rs.POST; import javax.ws.rs.Path; import javax.ws.rs.Produces; import javax.ws.rs.QueryParam; -import javax.ws.rs.container.ResourceContext; import javax.ws.rs.core.Context; import javax.ws.rs.core.HttpHeaders; import javax.ws.rs.core.MediaType; @@ -103,8 +103,10 @@ public class TokenService { @Context protected ProviderSession providerSession; + /* @Context protected ResourceContext resourceContext; + */ private ResourceAdminManager resourceAdminManager = new ResourceAdminManager(); @@ -169,17 +171,17 @@ public class TokenService { String username = form.getFirst(AuthenticationManager.FORM_USERNAME); if (username == null) { audit.error(Errors.USERNAME_MISSING); - throw new NotAuthorizedException("No username"); + throw new UnauthorizedException("No username"); } audit.detail(Details.USERNAME, username); if (!realm.isEnabled()) { audit.error(Errors.REALM_DISABLED); - throw new NotAuthorizedException("Disabled realm"); + throw new UnauthorizedException("Disabled realm"); } if (authManager.authenticateForm(clientConnection, realm, form) != AuthenticationStatus.SUCCESS) { audit.error(Errors.INVALID_USER_CREDENTIALS); - throw new NotAuthorizedException("Auth failed"); + throw new UnauthorizedException("Auth failed"); } UserModel user = realm.getUser(form.getFirst(AuthenticationManager.FORM_USERNAME)); @@ -218,7 +220,7 @@ public class TokenService { error.put(OAuth2Constants.ERROR, e.getError()); if (e.getDescription() != null) error.put(OAuth2Constants.ERROR_DESCRIPTION, e.getDescription()); audit.error(Errors.INVALID_TOKEN); - throw new BadRequestException(Response.status(Response.Status.BAD_REQUEST).entity(error).type("application/json").build(), e); + throw new BadRequestException("OAuth Error", e, Response.status(Response.Status.BAD_REQUEST).entity(error).type("application/json").build()); } AccessTokenResponse res = tokenManager.responseBuilder(realm, client, audit) @@ -318,7 +320,9 @@ public class TokenService { @Path("auth/request/login-actions") public RequiredActionsService getRequiredActionsService() { RequiredActionsService service = new RequiredActionsService(realm, tokenManager, audit); - resourceContext.initResource(service); + ResteasyProviderFactory.getInstance().injectProperties(service); + + //resourceContext.initResource(service); return service; } @@ -451,7 +455,7 @@ public class TokenService { if (!realm.isEnabled()) { audit.error(Errors.REALM_DISABLED); - throw new NotAuthorizedException("Realm not enabled"); + throw new UnauthorizedException("Realm not enabled"); } String code = formData.getFirst(OAuth2Constants.CODE); @@ -539,7 +543,7 @@ public class TokenService { if (authorizationHeader != null) { String[] usernameSecret = BasicAuthHelper.parseHeader(authorizationHeader); if (usernameSecret == null) { - throw new NotAuthorizedException("Bad Authorization header", "Basic realm=\"" + realm.getName() + "\""); + throw new UnauthorizedException("Bad Authorization header", Response.status(401).header(HttpHeaders.WWW_AUTHENTICATE, "Basic realm=\"" + realm.getName() + "\"").build()); } client_id = usernameSecret[0]; clientSecret = usernameSecret[1]; diff --git a/services/src/main/java/org/keycloak/services/resources/admin/AdminService.java b/services/src/main/java/org/keycloak/services/resources/admin/AdminService.java index 98d2c63d84..bc8e4d494d 100755 --- a/services/src/main/java/org/keycloak/services/resources/admin/AdminService.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/AdminService.java @@ -5,6 +5,9 @@ import org.jboss.resteasy.annotations.cache.NoCache; import org.jboss.resteasy.logging.Logger; import org.jboss.resteasy.spi.HttpRequest; import org.jboss.resteasy.spi.HttpResponse; +import org.jboss.resteasy.spi.NotFoundException; +import org.jboss.resteasy.spi.ResteasyProviderFactory; +import org.jboss.resteasy.spi.UnauthorizedException; import org.keycloak.OAuth2Constants; import org.keycloak.jaxrs.JaxrsOAuthClient; import org.keycloak.models.AdminRoles; @@ -24,12 +27,9 @@ import org.keycloak.services.resources.TokenService; import org.keycloak.services.resources.flows.Flows; import javax.ws.rs.GET; -import javax.ws.rs.NotAuthorizedException; -import javax.ws.rs.NotFoundException; import javax.ws.rs.Path; import javax.ws.rs.Produces; import javax.ws.rs.QueryParam; -import javax.ws.rs.container.ResourceContext; import javax.ws.rs.core.Context; import javax.ws.rs.core.HttpHeaders; import javax.ws.rs.core.NewCookie; @@ -63,8 +63,10 @@ public class AdminService { @Context protected KeycloakSession session; + /* @Context protected ResourceContext resourceContext; + */ @Context protected Providers providers; @@ -157,7 +159,7 @@ public class AdminService { RealmManager realmManager = new RealmManager(session); RealmModel realm = getAdminstrationRealm(realmManager); if (realm == null) - throw new NotFoundException(); + throw new NotFoundException("No realm found"); Auth auth = authManager.authenticateCookie(realm, headers); if (auth == null) { return Response.status(401).build(); @@ -174,7 +176,7 @@ public class AdminService { RealmManager realmManager = new RealmManager(session); RealmModel realm = getAdminstrationRealm(realmManager); if (realm == null) - throw new NotFoundException(); + throw new NotFoundException("No realm found"); Auth auth = authManager.authenticateCookie(realm, headers); if (auth == null) { logger.debug("No auth cookie"); @@ -255,14 +257,15 @@ public class AdminService { RealmManager realmManager = new RealmManager(session); RealmModel adminRealm = getAdminstrationRealm(realmManager); if (adminRealm == null) - throw new NotFoundException(); + throw new NotFoundException("Admin realm not found"); Auth auth = authManager.authenticate(adminRealm, headers); if (auth == null) { - throw new NotAuthorizedException("Bearer"); + throw new UnauthorizedException("Bearer"); } RealmsAdminResource adminResource = new RealmsAdminResource(auth, tokenManager); - resourceContext.initResource(adminResource); + ResteasyProviderFactory.getInstance().injectProperties(adminResource); + //resourceContext.initResource(adminResource); return adminResource; } @@ -271,18 +274,19 @@ public class AdminService { RealmManager realmManager = new RealmManager(session); RealmModel adminRealm = getAdminstrationRealm(realmManager); if (adminRealm == null) - throw new NotFoundException(); + throw new NotFoundException("Admin realm not found"); Auth auth = authManager.authenticate(adminRealm, headers); UserModel admin = auth.getUser(); if (admin == null) { - throw new NotAuthorizedException("Bearer"); + throw new UnauthorizedException("Bearer"); } ApplicationModel adminConsole = adminRealm.getApplicationNameMap().get(Constants.ADMIN_CONSOLE_APPLICATION); if (adminConsole == null) { - throw new NotFoundException(); + throw new NotFoundException("Admin console application not found"); } ServerInfoAdminResource adminResource = new ServerInfoAdminResource(); - resourceContext.initResource(adminResource); + ResteasyProviderFactory.getInstance().injectProperties(adminResource); + //resourceContext.initResource(adminResource); return adminResource; } diff --git a/services/src/main/java/org/keycloak/services/resources/admin/ApplicationResource.java b/services/src/main/java/org/keycloak/services/resources/admin/ApplicationResource.java index 085bbec8ba..e0aedb08a8 100755 --- a/services/src/main/java/org/keycloak/services/resources/admin/ApplicationResource.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/ApplicationResource.java @@ -2,6 +2,7 @@ package org.keycloak.services.resources.admin; import org.jboss.resteasy.annotations.cache.NoCache; import org.jboss.resteasy.logging.Logger; +import org.jboss.resteasy.spi.NotFoundException; import org.keycloak.models.ApplicationModel; import org.keycloak.models.KeycloakSession; import org.keycloak.models.RealmModel; @@ -22,7 +23,6 @@ import javax.ws.rs.Consumes; import javax.ws.rs.DELETE; import javax.ws.rs.DefaultValue; import javax.ws.rs.GET; -import javax.ws.rs.NotFoundException; import javax.ws.rs.POST; import javax.ws.rs.PUT; import javax.ws.rs.Path; @@ -237,7 +237,7 @@ public class ApplicationResource { auth.requireManage(); UserModel user = realm.getUser(username); if (user == null) { - throw new NotFoundException(); + throw new NotFoundException("User not found"); } new ResourceAdminManager().logoutApplication(realm, application, user.getId()); } diff --git a/services/src/main/java/org/keycloak/services/resources/admin/ApplicationsResource.java b/services/src/main/java/org/keycloak/services/resources/admin/ApplicationsResource.java index 6387e00c50..ec236cf585 100755 --- a/services/src/main/java/org/keycloak/services/resources/admin/ApplicationsResource.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/ApplicationsResource.java @@ -2,6 +2,8 @@ package org.keycloak.services.resources.admin; import org.jboss.resteasy.annotations.cache.NoCache; import org.jboss.resteasy.logging.Logger; +import org.jboss.resteasy.spi.NotFoundException; +import org.jboss.resteasy.spi.ResteasyProviderFactory; import org.keycloak.models.ApplicationModel; import org.keycloak.models.KeycloakSession; import org.keycloak.models.RealmModel; @@ -12,12 +14,10 @@ import org.keycloak.services.resources.flows.Flows; import javax.ws.rs.Consumes; import javax.ws.rs.GET; -import javax.ws.rs.NotFoundException; import javax.ws.rs.POST; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; -import javax.ws.rs.container.ResourceContext; import javax.ws.rs.core.Context; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; @@ -34,9 +34,6 @@ public class ApplicationsResource { protected RealmModel realm; private RealmAuth auth; - @Context - protected ResourceContext resourceContext; - @Context protected KeycloakSession session; @@ -90,7 +87,8 @@ public class ApplicationsResource { throw new NotFoundException("Could not find application: " + name); } ApplicationResource applicationResource = new ApplicationResource(realm, auth, applicationModel, session); - resourceContext.initResource(applicationResource); + ResteasyProviderFactory.getInstance().injectProperties(applicationResource); + //resourceContext.initResource(applicationResource); return applicationResource; } diff --git a/services/src/main/java/org/keycloak/services/resources/admin/OAuthClientResource.java b/services/src/main/java/org/keycloak/services/resources/admin/OAuthClientResource.java index 752e9976ce..5481ae8545 100755 --- a/services/src/main/java/org/keycloak/services/resources/admin/OAuthClientResource.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/OAuthClientResource.java @@ -2,6 +2,7 @@ package org.keycloak.services.resources.admin; import org.jboss.resteasy.annotations.cache.NoCache; import org.jboss.resteasy.logging.Logger; +import org.jboss.resteasy.spi.NotFoundException; import org.keycloak.models.KeycloakSession; import org.keycloak.models.OAuthClientModel; import org.keycloak.models.RealmModel; @@ -16,7 +17,6 @@ import org.keycloak.util.JsonSerialization; import javax.ws.rs.Consumes; import javax.ws.rs.DELETE; import javax.ws.rs.GET; -import javax.ws.rs.NotFoundException; import javax.ws.rs.POST; import javax.ws.rs.PUT; import javax.ws.rs.Path; diff --git a/services/src/main/java/org/keycloak/services/resources/admin/OAuthClientsResource.java b/services/src/main/java/org/keycloak/services/resources/admin/OAuthClientsResource.java index 75e7d6443f..3fdcd16691 100755 --- a/services/src/main/java/org/keycloak/services/resources/admin/OAuthClientsResource.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/OAuthClientsResource.java @@ -2,6 +2,8 @@ package org.keycloak.services.resources.admin; import org.jboss.resteasy.annotations.cache.NoCache; import org.jboss.resteasy.logging.Logger; +import org.jboss.resteasy.spi.NotFoundException; +import org.jboss.resteasy.spi.ResteasyProviderFactory; import org.keycloak.models.KeycloakSession; import org.keycloak.models.OAuthClientModel; import org.keycloak.models.RealmModel; @@ -10,12 +12,10 @@ import org.keycloak.services.managers.OAuthClientManager; import javax.ws.rs.Consumes; import javax.ws.rs.GET; -import javax.ws.rs.NotFoundException; import javax.ws.rs.POST; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; -import javax.ws.rs.container.ResourceContext; import javax.ws.rs.core.Context; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; @@ -33,8 +33,11 @@ public class OAuthClientsResource { protected KeycloakSession session; + /* @Context protected ResourceContext resourceContext; + + */ private RealmAuth auth; public OAuthClientsResource(RealmModel realm, RealmAuth auth, KeycloakSession session) { @@ -81,10 +84,11 @@ public class OAuthClientsResource { OAuthClientModel oauth = realm.getOAuthClientById(id); if (oauth == null) { - throw new NotFoundException(); + throw new NotFoundException("OAuth Client not found"); } OAuthClientResource oAuthClientResource = new OAuthClientResource(realm, auth, oauth, session); - resourceContext.initResource(oAuthClientResource); + ResteasyProviderFactory.getInstance().injectProperties(oAuthClientResource); + //resourceContext.initResource(oAuthClientResource); return oAuthClientResource; } diff --git a/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java b/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java index 48a926a744..2e564371d7 100755 --- a/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java @@ -2,6 +2,8 @@ package org.keycloak.services.resources.admin; import org.jboss.resteasy.annotations.cache.NoCache; import org.jboss.resteasy.logging.Logger; +import org.jboss.resteasy.spi.NotFoundException; +import org.jboss.resteasy.spi.ResteasyProviderFactory; import org.keycloak.audit.AuditProvider; import org.keycloak.audit.Event; import org.keycloak.audit.EventQuery; @@ -18,7 +20,6 @@ import org.keycloak.services.managers.ResourceAdminManager; import org.keycloak.services.managers.TokenManager; import javax.ws.rs.*; -import javax.ws.rs.container.ResourceContext; import javax.ws.rs.core.Context; import javax.ws.rs.core.MediaType; import java.util.HashMap; @@ -35,8 +36,10 @@ public class RealmAdminResource { protected RealmModel realm; private TokenManager tokenManager; + /* @Context protected ResourceContext resourceContext; + */ @Context protected KeycloakSession session; @@ -55,14 +58,16 @@ public class RealmAdminResource { @Path("applications") public ApplicationsResource getApplications() { ApplicationsResource applicationsResource = new ApplicationsResource(realm, auth); - resourceContext.initResource(applicationsResource); + ResteasyProviderFactory.getInstance().injectProperties(applicationsResource); + //resourceContext.initResource(applicationsResource); return applicationsResource; } @Path("oauth-clients") public OAuthClientsResource getOAuthClients() { OAuthClientsResource oauth = new OAuthClientsResource(realm, auth, session); - resourceContext.initResource(oauth); + ResteasyProviderFactory.getInstance().injectProperties(oauth); + //resourceContext.initResource(oauth); return oauth; } @@ -101,21 +106,23 @@ public class RealmAdminResource { auth.requireManage(); if (!new RealmManager(session).removeRealm(realm)) { - throw new NotFoundException(); + throw new NotFoundException("Realm doesn't exist"); } } @Path("users") public UsersResource users() { UsersResource users = new UsersResource(realm, auth, tokenManager); - resourceContext.initResource(users); + ResteasyProviderFactory.getInstance().injectProperties(users); + //resourceContext.initResource(users); return users; } @Path("roles-by-id") public RoleByIdResource rolesById() { RoleByIdResource resource = new RoleByIdResource(realm, auth); - resourceContext.initResource(resource); + ResteasyProviderFactory.getInstance().injectProperties(resource); + //resourceContext.initResource(resource); return resource; } diff --git a/services/src/main/java/org/keycloak/services/resources/admin/RealmAuth.java b/services/src/main/java/org/keycloak/services/resources/admin/RealmAuth.java old mode 100644 new mode 100755 index e8b8e64a04..e6ea9a1625 --- a/services/src/main/java/org/keycloak/services/resources/admin/RealmAuth.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/RealmAuth.java @@ -1,9 +1,11 @@ package org.keycloak.services.resources.admin; import org.keycloak.models.AdminRoles; +import org.keycloak.services.ForbiddenException; import org.keycloak.services.managers.Auth; -import javax.ws.rs.ForbiddenException; +import javax.ws.rs.WebApplicationException; + /** * @author Stian Thorgersen diff --git a/services/src/main/java/org/keycloak/services/resources/admin/RealmsAdminResource.java b/services/src/main/java/org/keycloak/services/resources/admin/RealmsAdminResource.java index 49d5198b01..a20d149603 100755 --- a/services/src/main/java/org/keycloak/services/resources/admin/RealmsAdminResource.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/RealmsAdminResource.java @@ -4,6 +4,8 @@ import org.jboss.resteasy.annotations.cache.NoCache; import org.jboss.resteasy.logging.Logger; import org.jboss.resteasy.plugins.providers.multipart.InputPart; import org.jboss.resteasy.plugins.providers.multipart.MultipartFormDataInput; +import org.jboss.resteasy.spi.NotFoundException; +import org.jboss.resteasy.spi.ResteasyProviderFactory; import org.jboss.resteasy.util.GenericType; import org.keycloak.models.AdminRoles; import org.keycloak.models.ApplicationModel; @@ -11,6 +13,7 @@ import org.keycloak.models.KeycloakSession; import org.keycloak.models.RealmModel; import org.keycloak.models.RoleModel; import org.keycloak.representations.idm.RealmRepresentation; +import org.keycloak.services.ForbiddenException; import org.keycloak.services.managers.Auth; import org.keycloak.services.managers.ModelToRepresentation; import org.keycloak.services.managers.RealmManager; @@ -18,7 +21,6 @@ import org.keycloak.services.managers.TokenManager; import org.keycloak.services.resources.flows.Flows; import javax.ws.rs.*; -import javax.ws.rs.container.ResourceContext; import javax.ws.rs.core.CacheControl; import javax.ws.rs.core.Context; import javax.ws.rs.core.HttpHeaders; @@ -51,8 +53,10 @@ public class RealmsAdminResource { noCache.setNoCache(true); } + /* @Context protected ResourceContext resourceContext; + */ @Context protected KeycloakSession session; @@ -158,7 +162,8 @@ public class RealmsAdminResource { RealmAuth realmAuth = new RealmAuth(auth, AdminRoles.getAdminApp(realm)); RealmAdminResource adminResource = new RealmAdminResource(realmAuth, realm, tokenManager); - resourceContext.initResource(adminResource); + ResteasyProviderFactory.getInstance().injectProperties(adminResource); + //resourceContext.initResource(adminResource); return adminResource; } diff --git a/services/src/main/java/org/keycloak/services/resources/admin/RoleByIdResource.java b/services/src/main/java/org/keycloak/services/resources/admin/RoleByIdResource.java index 501a725c13..b4ec35a50f 100755 --- a/services/src/main/java/org/keycloak/services/resources/admin/RoleByIdResource.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/RoleByIdResource.java @@ -2,6 +2,7 @@ package org.keycloak.services.resources.admin; import org.jboss.resteasy.annotations.cache.NoCache; import org.jboss.resteasy.logging.Logger; +import org.jboss.resteasy.spi.NotFoundException; import org.keycloak.models.ApplicationModel; import org.keycloak.models.Constants; import org.keycloak.models.OAuthClientModel; @@ -13,7 +14,6 @@ import org.keycloak.representations.idm.RoleRepresentation; import javax.ws.rs.Consumes; import javax.ws.rs.DELETE; import javax.ws.rs.GET; -import javax.ws.rs.NotFoundException; import javax.ws.rs.POST; import javax.ws.rs.PUT; import javax.ws.rs.Path; diff --git a/services/src/main/java/org/keycloak/services/resources/admin/RoleContainerResource.java b/services/src/main/java/org/keycloak/services/resources/admin/RoleContainerResource.java index 5bbd44b6fd..55f4f4c807 100755 --- a/services/src/main/java/org/keycloak/services/resources/admin/RoleContainerResource.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/RoleContainerResource.java @@ -1,6 +1,7 @@ package org.keycloak.services.resources.admin; import org.jboss.resteasy.annotations.cache.NoCache; +import org.jboss.resteasy.spi.NotFoundException; import org.keycloak.models.Constants; import org.keycloak.models.RealmModel; import org.keycloak.models.RoleContainerModel; @@ -12,7 +13,6 @@ import org.keycloak.services.resources.flows.Flows; import javax.ws.rs.Consumes; import javax.ws.rs.DELETE; import javax.ws.rs.GET; -import javax.ws.rs.NotFoundException; import javax.ws.rs.POST; import javax.ws.rs.PUT; import javax.ws.rs.Path; @@ -41,7 +41,6 @@ public class RoleContainerResource extends RoleResource { this.roleContainer = roleContainer; } - @Path("") @GET @NoCache @Produces("application/json") @@ -58,7 +57,6 @@ public class RoleContainerResource extends RoleResource { return roles; } - @Path("") @POST @Consumes("application/json") public Response createRole(final @Context UriInfo uriInfo, final RoleRepresentation rep) { @@ -69,7 +67,7 @@ public class RoleContainerResource extends RoleResource { } RoleModel role = roleContainer.addRole(rep.getName()); if (role == null) { - throw new NotFoundException(); + throw new NotFoundException("Role not found"); } role.setDescription(rep.getDescription()); return Response.created(uriInfo.getAbsolutePathBuilder().path(role.getName()).build()).build(); diff --git a/services/src/main/java/org/keycloak/services/resources/admin/RoleResource.java b/services/src/main/java/org/keycloak/services/resources/admin/RoleResource.java index 5eb952fc2e..8ef17d8e27 100755 --- a/services/src/main/java/org/keycloak/services/resources/admin/RoleResource.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/RoleResource.java @@ -1,12 +1,12 @@ package org.keycloak.services.resources.admin; +import org.jboss.resteasy.spi.NotFoundException; import org.keycloak.models.ApplicationModel; import org.keycloak.models.RealmModel; import org.keycloak.models.RoleModel; import org.keycloak.representations.idm.RoleRepresentation; import org.keycloak.services.managers.ModelToRepresentation; -import javax.ws.rs.NotFoundException; import java.util.Collections; import java.util.HashSet; import java.util.List; @@ -29,7 +29,7 @@ public abstract class RoleResource { protected void deleteRole(RoleModel role) { if (!role.getContainer().removeRole(role)) { - throw new NotFoundException(); + throw new NotFoundException("Role not found"); } } diff --git a/services/src/main/java/org/keycloak/services/resources/admin/ScopeMappedResource.java b/services/src/main/java/org/keycloak/services/resources/admin/ScopeMappedResource.java index 6e7dbe4b65..549f1546ad 100755 --- a/services/src/main/java/org/keycloak/services/resources/admin/ScopeMappedResource.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/ScopeMappedResource.java @@ -1,6 +1,7 @@ package org.keycloak.services.resources.admin; import org.jboss.resteasy.annotations.cache.NoCache; +import org.jboss.resteasy.spi.NotFoundException; import org.keycloak.models.ApplicationModel; import org.keycloak.models.ClientModel; import org.keycloak.models.KeycloakSession; @@ -16,7 +17,6 @@ import org.keycloak.services.managers.RealmManager; import javax.ws.rs.Consumes; import javax.ws.rs.DELETE; import javax.ws.rs.GET; -import javax.ws.rs.NotFoundException; import javax.ws.rs.POST; import javax.ws.rs.Path; import javax.ws.rs.PathParam; @@ -108,7 +108,7 @@ public class ScopeMappedResource { for (RoleRepresentation role : roles) { RoleModel roleModel = realm.getRoleById(role.getId()); if (roleModel == null) { - throw new NotFoundException(); + throw new NotFoundException("Role not found"); } realm.addScopeMapping(client, roleModel); } @@ -132,7 +132,7 @@ public class ScopeMappedResource { for (RoleRepresentation role : roles) { RoleModel roleModel = realm.getRoleById(role.getId()); if (roleModel == null) { - throw new NotFoundException(); + throw new NotFoundException("Role not found"); } realm.deleteScopeMapping(client, roleModel); } @@ -149,7 +149,7 @@ public class ScopeMappedResource { ApplicationModel app = realm.getApplicationByName(appName); if (app == null) { - throw new NotFoundException(); + throw new NotFoundException("Role not found"); } Set mappings = app.getApplicationScopeMappings(client); @@ -169,13 +169,13 @@ public class ScopeMappedResource { ApplicationModel app = realm.getApplicationByName(appName); if (app == null) { - throw new NotFoundException(); + throw new NotFoundException("Application not found"); } for (RoleRepresentation role : roles) { RoleModel roleModel = app.getRole(role.getName()); if (roleModel == null) { - throw new NotFoundException(); + throw new NotFoundException("Role not found"); } realm.addScopeMapping(client, roleModel); } @@ -191,7 +191,7 @@ public class ScopeMappedResource { ApplicationModel app = realm.getApplicationByName(appName); if (app == null) { - throw new NotFoundException(); + throw new NotFoundException("Application not found"); } if (roles == null) { @@ -204,7 +204,7 @@ public class ScopeMappedResource { for (RoleRepresentation role : roles) { RoleModel roleModel = app.getRole(role.getName()); if (roleModel == null) { - throw new NotFoundException(); + throw new NotFoundException("Role not found"); } realm.deleteScopeMapping(client, roleModel); } diff --git a/services/src/main/java/org/keycloak/services/resources/admin/UsersResource.java b/services/src/main/java/org/keycloak/services/resources/admin/UsersResource.java index 038a9de98c..857d995768 100755 --- a/services/src/main/java/org/keycloak/services/resources/admin/UsersResource.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/UsersResource.java @@ -2,6 +2,8 @@ package org.keycloak.services.resources.admin; import org.jboss.resteasy.annotations.cache.NoCache; import org.jboss.resteasy.logging.Logger; +import org.jboss.resteasy.spi.BadRequestException; +import org.jboss.resteasy.spi.NotFoundException; import org.keycloak.models.ApplicationModel; import org.keycloak.models.ClientModel; import org.keycloak.models.Constants; @@ -27,18 +29,15 @@ import org.keycloak.services.resources.flows.Flows; import org.keycloak.services.resources.flows.Urls; import org.keycloak.util.Time; -import javax.ws.rs.BadRequestException; import javax.ws.rs.Consumes; import javax.ws.rs.DELETE; import javax.ws.rs.GET; -import javax.ws.rs.NotFoundException; import javax.ws.rs.POST; import javax.ws.rs.PUT; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.QueryParam; -import javax.ws.rs.container.ResourceContext; import javax.ws.rs.core.Context; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; @@ -73,8 +72,10 @@ public class UsersResource { @Context protected UriInfo uriInfo; + /* @Context protected ResourceContext resourceContext; + */ @Context protected KeycloakSession session; @@ -88,7 +89,7 @@ public class UsersResource { UserModel user = realm.getUser(username); if (user == null) { - throw new NotFoundException(); + throw new NotFoundException("User not found"); } updateUserFromRep(user, rep); } @@ -103,7 +104,7 @@ public class UsersResource { } UserModel user = realm.addUser(rep.getUsername()); if (user == null) { - throw new NotFoundException(); + throw new NotFoundException("User not found"); } updateUserFromRep(user, rep); @@ -148,7 +149,7 @@ public class UsersResource { UserModel user = realm.getUser(username); if (user == null) { - throw new NotFoundException(); + throw new NotFoundException("User not found"); } return ModelToRepresentation.toRepresentation(user); } @@ -162,7 +163,7 @@ public class UsersResource { auth.requireView(); UserModel user = realm.getUser(username); if (user == null) { - throw new NotFoundException(); + throw new NotFoundException("User not found"); } Map stats = new HashMap(); for (ApplicationModel applicationModel : realm.getApplications()) { @@ -179,7 +180,7 @@ public class UsersResource { auth.requireManage(); UserModel user = realm.getUser(username); if (user == null) { - throw new NotFoundException(); + throw new NotFoundException("User not found"); } // set notBefore so that user will be forced to log in. user.setNotBefore(Time.currentTime()); @@ -248,7 +249,7 @@ public class UsersResource { UserModel user = realm.getUser(username); if (user == null) { - throw new NotFoundException(); + throw new NotFoundException("User not found"); } MappingsRepresentation all = new MappingsRepresentation(); @@ -293,7 +294,7 @@ public class UsersResource { UserModel user = realm.getUser(username); if (user == null) { - throw new NotFoundException(); + throw new NotFoundException("User not found"); } Set realmMappings = realm.getRealmRoleMappings(user); @@ -314,13 +315,13 @@ public class UsersResource { logger.debug("** addRealmRoleMappings: {0}", roles); UserModel user = realm.getUser(username); if (user == null) { - throw new NotFoundException(); + throw new NotFoundException("User not found"); } for (RoleRepresentation role : roles) { RoleModel roleModel = realm.getRole(role.getName()); if (roleModel == null || !roleModel.getId().equals(role.getId())) { - throw new NotFoundException(); + throw new NotFoundException("Role not found"); } realm.grantRole(user, roleModel); } @@ -337,7 +338,7 @@ public class UsersResource { logger.debug("deleteRealmRoleMappings"); UserModel user = realm.getUser(username); if (user == null) { - throw new NotFoundException(); + throw new NotFoundException("User not found"); } if (roles == null) { @@ -350,7 +351,7 @@ public class UsersResource { for (RoleRepresentation role : roles) { RoleModel roleModel = realm.getRole(role.getName()); if (roleModel == null || !roleModel.getId().equals(role.getId())) { - throw new NotFoundException(); + throw new NotFoundException("Role not found"); } realm.deleteRoleMapping(user, roleModel); } @@ -368,13 +369,13 @@ public class UsersResource { UserModel user = realm.getUser(username); if (user == null) { - throw new NotFoundException(); + throw new NotFoundException("User not found"); } ApplicationModel application = realm.getApplicationByName(appName); if (application == null) { - throw new NotFoundException(); + throw new NotFoundException("Application not found"); } Set mappings = application.getApplicationRoleMappings(user); @@ -395,19 +396,19 @@ public class UsersResource { logger.debug("addApplicationRoleMapping"); UserModel user = realm.getUser(username); if (user == null) { - throw new NotFoundException(); + throw new NotFoundException("User not found"); } ApplicationModel application = realm.getApplicationByName(appName); if (application == null) { - throw new NotFoundException(); + throw new NotFoundException("Application not found"); } for (RoleRepresentation role : roles) { RoleModel roleModel = application.getRole(role.getName()); if (roleModel == null || !roleModel.getId().equals(role.getId())) { - throw new NotFoundException(); + throw new NotFoundException("Role not found"); } realm.grantRole(user, roleModel); } @@ -422,13 +423,13 @@ public class UsersResource { UserModel user = realm.getUser(username); if (user == null) { - throw new NotFoundException(); + throw new NotFoundException("User not found"); } ApplicationModel application = realm.getApplicationByName(appName); if (application == null) { - throw new NotFoundException(); + throw new NotFoundException("Application not found"); } if (roles == null) { @@ -445,7 +446,7 @@ public class UsersResource { for (RoleRepresentation role : roles) { RoleModel roleModel = application.getRole(role.getName()); if (roleModel == null || !roleModel.getId().equals(role.getId())) { - throw new NotFoundException(); + throw new NotFoundException("Role not found"); } realm.deleteRoleMapping(user, roleModel); } @@ -460,10 +461,10 @@ public class UsersResource { UserModel user = realm.getUser(username); if (user == null) { - throw new NotFoundException(); + throw new NotFoundException("User not found"); } if (pass == null || pass.getValue() == null || !CredentialRepresentation.PASSWORD.equals(pass.getType())) { - throw new BadRequestException(); + throw new BadRequestException("No password provided"); } UserCredentialModel cred = RealmManager.fromRepresentation(pass); @@ -479,7 +480,7 @@ public class UsersResource { UserModel user = realm.getUser(username); if (user == null) { - throw new NotFoundException(); + throw new NotFoundException("User not found"); } user.setTotp(false); @@ -493,7 +494,7 @@ public class UsersResource { UserModel user = realm.getUser(username); if (user == null) { - throw new NotFoundException(); + throw new NotFoundException("User not found"); } if (user.getEmail() == null) { diff --git a/services/src/main/java/org/keycloak/services/resources/flows/SocialRedirectFlows.java b/services/src/main/java/org/keycloak/services/resources/flows/SocialRedirectFlows.java old mode 100644 new mode 100755 index f30a92336a..c9c5f44f10 --- a/services/src/main/java/org/keycloak/services/resources/flows/SocialRedirectFlows.java +++ b/services/src/main/java/org/keycloak/services/resources/flows/SocialRedirectFlows.java @@ -46,6 +46,6 @@ public class SocialRedirectFlows { AuthRequest authRequest = socialProvider.getAuthUrl(config); RequestDetails socialRequest = socialRequestBuilder.putSocialAttributes(authRequest.getAttributes()).build(); socialRequestManager.addRequest(authRequest.getId(), socialRequest); - return Response.status(Response.Status.FOUND).location(authRequest.getAuthUri()).build(); + return Response.status(302).location(authRequest.getAuthUri()).build(); } } diff --git a/services/src/main/java/org/keycloak/services/util/HttpClientBuilder.java b/services/src/main/java/org/keycloak/services/util/HttpClientBuilder.java new file mode 100755 index 0000000000..374131ffcd --- /dev/null +++ b/services/src/main/java/org/keycloak/services/util/HttpClientBuilder.java @@ -0,0 +1,314 @@ +package org.keycloak.services.util; + +import org.apache.http.client.HttpClient; +import org.apache.http.conn.ClientConnectionManager; +import org.apache.http.conn.scheme.PlainSocketFactory; +import org.apache.http.conn.scheme.Scheme; +import org.apache.http.conn.scheme.SchemeRegistry; +import org.apache.http.conn.ssl.AllowAllHostnameVerifier; +import org.apache.http.conn.ssl.BrowserCompatHostnameVerifier; +import org.apache.http.conn.ssl.SSLSocketFactory; +import org.apache.http.conn.ssl.StrictHostnameVerifier; +import org.apache.http.conn.ssl.X509HostnameVerifier; +import org.apache.http.impl.client.DefaultHttpClient; +import org.apache.http.impl.conn.SingleClientConnManager; +import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager; +import org.apache.http.params.BasicHttpParams; +import org.apache.http.params.HttpConnectionParams; +import org.keycloak.representations.adapters.config.AdapterConfig; +import org.keycloak.util.EnvUtil; +import org.keycloak.util.KeystoreUtil; + +import javax.net.ssl.HostnameVerifier; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLException; +import javax.net.ssl.SSLSession; +import javax.net.ssl.SSLSocket; +import javax.net.ssl.TrustManager; +import javax.net.ssl.X509TrustManager; +import java.io.IOException; +import java.security.KeyStore; +import java.security.SecureRandom; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; +import java.util.concurrent.TimeUnit; + +/** + * Abstraction for creating HttpClients. Allows SSL configuration. + * + * @author Bill Burke + * @version $Revision: 1 $ + */ +public class HttpClientBuilder { + public static enum HostnameVerificationPolicy { + /** + * Hostname verification is not done on the server's certificate + */ + ANY, + /** + * Allows wildcards in subdomain names i.e. *.foo.com + */ + WILDCARD, + /** + * CN must match hostname connecting to + */ + STRICT + } + + + /** + * @author Bill Burke + * @version $Revision: 1 $ + */ + private static class PassthroughTrustManager implements X509TrustManager { + public void checkClientTrusted(X509Certificate[] chain, + String authType) throws CertificateException { + } + + public void checkServerTrusted(X509Certificate[] chain, + String authType) throws CertificateException { + } + + public X509Certificate[] getAcceptedIssuers() { + return null; + } + } + + protected KeyStore truststore; + protected KeyStore clientKeyStore; + protected String clientPrivateKeyPassword; + protected boolean disableTrustManager; + protected HostnameVerificationPolicy policy = HostnameVerificationPolicy.WILDCARD; + protected SSLContext sslContext; + protected int connectionPoolSize = 100; + protected int maxPooledPerRoute = 0; + protected long connectionTTL = -1; + protected TimeUnit connectionTTLUnit = TimeUnit.MILLISECONDS; + protected HostnameVerifier verifier = null; + protected long socketTimeout = -1; + protected TimeUnit socketTimeoutUnits = TimeUnit.MILLISECONDS; + protected long establishConnectionTimeout = -1; + protected TimeUnit establishConnectionTimeoutUnits = TimeUnit.MILLISECONDS; + + + /** + * Socket inactivity timeout + * + * @param timeout + * @param unit + * @return + */ + public HttpClientBuilder socketTimeout(long timeout, TimeUnit unit) + { + this.socketTimeout = timeout; + this.socketTimeoutUnits = unit; + return this; + } + + /** + * When trying to make an initial socket connection, what is the timeout? + * + * @param timeout + * @param unit + * @return + */ + public HttpClientBuilder establishConnectionTimeout(long timeout, TimeUnit unit) + { + this.establishConnectionTimeout = timeout; + this.establishConnectionTimeoutUnits = unit; + return this; + } + + public HttpClientBuilder connectionTTL(long ttl, TimeUnit unit) { + this.connectionTTL = ttl; + this.connectionTTLUnit = unit; + return this; + } + + public HttpClientBuilder maxPooledPerRoute(int maxPooledPerRoute) { + this.maxPooledPerRoute = maxPooledPerRoute; + return this; + } + + public HttpClientBuilder connectionPoolSize(int connectionPoolSize) { + this.connectionPoolSize = connectionPoolSize; + return this; + } + + /** + * Disable trust management and hostname verification. NOTE this is a security + * hole, so only set this option if you cannot or do not want to verify the identity of the + * host you are communicating with. + */ + public HttpClientBuilder disableTrustManager() { + this.disableTrustManager = true; + return this; + } + + /** + * SSL policy used to verify hostnames + * + * @param policy + * @return + */ + public HttpClientBuilder hostnameVerification(HostnameVerificationPolicy policy) { + this.policy = policy; + return this; + } + + + public HttpClientBuilder sslContext(SSLContext sslContext) { + this.sslContext = sslContext; + return this; + } + + public HttpClientBuilder trustStore(KeyStore truststore) { + this.truststore = truststore; + return this; + } + + public HttpClientBuilder keyStore(KeyStore keyStore, String password) { + this.clientKeyStore = keyStore; + this.clientPrivateKeyPassword = password; + return this; + } + + public HttpClientBuilder keyStore(KeyStore keyStore, char[] password) { + this.clientKeyStore = keyStore; + this.clientPrivateKeyPassword = new String(password); + return this; + } + + + static class VerifierWrapper implements X509HostnameVerifier { + protected HostnameVerifier verifier; + + VerifierWrapper(HostnameVerifier verifier) { + this.verifier = verifier; + } + + @Override + public void verify(String host, SSLSocket ssl) throws IOException { + if (!verifier.verify(host, ssl.getSession())) throw new SSLException("Hostname verification failure"); + } + + @Override + public void verify(String host, X509Certificate cert) throws SSLException { + throw new SSLException("This verification path not implemented"); + } + + @Override + public void verify(String host, String[] cns, String[] subjectAlts) throws SSLException { + throw new SSLException("This verification path not implemented"); + } + + @Override + public boolean verify(String s, SSLSession sslSession) { + return verifier.verify(s, sslSession); + } + } + + public HttpClient build() { + X509HostnameVerifier verifier = null; + if (this.verifier != null) verifier = new VerifierWrapper(this.verifier); + else { + switch (policy) { + case ANY: + verifier = new AllowAllHostnameVerifier(); + break; + case WILDCARD: + verifier = new BrowserCompatHostnameVerifier(); + break; + case STRICT: + verifier = new StrictHostnameVerifier(); + break; + } + } + try { + SSLSocketFactory sslsf = null; + SSLContext theContext = sslContext; + if (disableTrustManager) { + theContext = SSLContext.getInstance("SSL"); + theContext.init(null, new TrustManager[]{new PassthroughTrustManager()}, + new SecureRandom()); + verifier = new AllowAllHostnameVerifier(); + sslsf = new SSLSocketFactory(theContext, verifier); + } else if (theContext != null) { + sslsf = new SSLSocketFactory(theContext, verifier); + } else if (clientKeyStore != null || truststore != null) { + sslsf = new SSLSocketFactory(SSLSocketFactory.TLS, clientKeyStore, clientPrivateKeyPassword, truststore, null, verifier); + } else { + final SSLContext tlsContext = SSLContext.getInstance(SSLSocketFactory.TLS); + tlsContext.init(null, null, null); + sslsf = new SSLSocketFactory(tlsContext, verifier); + } + SchemeRegistry registry = new SchemeRegistry(); + registry.register( + new Scheme("http", 80, PlainSocketFactory.getSocketFactory())); + Scheme httpsScheme = new Scheme("https", 443, sslsf); + registry.register(httpsScheme); + ClientConnectionManager cm = null; + if (connectionPoolSize > 0) { + ThreadSafeClientConnManager tcm = new ThreadSafeClientConnManager(registry, connectionTTL, connectionTTLUnit); + tcm.setMaxTotal(connectionPoolSize); + if (maxPooledPerRoute == 0) maxPooledPerRoute = connectionPoolSize; + tcm.setDefaultMaxPerRoute(maxPooledPerRoute); + cm = tcm; + + } else { + cm = new SingleClientConnManager(registry); + } + BasicHttpParams params = new BasicHttpParams(); + if (socketTimeout > -1) + { + HttpConnectionParams.setSoTimeout(params, (int) socketTimeoutUnits.toMillis(socketTimeout)); + + } + if (establishConnectionTimeout > -1) + { + HttpConnectionParams.setConnectionTimeout(params, (int)establishConnectionTimeoutUnits.toMillis(establishConnectionTimeout)); + } + return new DefaultHttpClient(cm, params); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + public HttpClient build(AdapterConfig adapterConfig) { + String truststorePath = adapterConfig.getTruststore(); + if (truststorePath != null) { + truststorePath = EnvUtil.replace(truststorePath); + String truststorePassword = adapterConfig.getTruststorePassword(); + try { + this.truststore = KeystoreUtil.loadKeyStore(truststorePath, truststorePassword); + } catch (Exception e) { + throw new RuntimeException("Failed to load truststore", e); + } + } + String clientKeystore = adapterConfig.getClientKeystore(); + if (clientKeystore != null) { + clientKeystore = EnvUtil.replace(clientKeystore); + String clientKeystorePassword = adapterConfig.getClientKeystorePassword(); + try { + KeyStore clientCertKeystore = KeystoreUtil.loadKeyStore(clientKeystore, clientKeystorePassword); + keyStore(clientCertKeystore, clientKeystorePassword); + } catch (Exception e) { + throw new RuntimeException("Failed to load keystore", e); + } + } + int size = 10; + if (adapterConfig.getConnectionPoolSize() > 0) + size = adapterConfig.getConnectionPoolSize(); + HttpClientBuilder.HostnameVerificationPolicy policy = HttpClientBuilder.HostnameVerificationPolicy.WILDCARD; + if (adapterConfig.isAllowAnyHostname()) + policy = HttpClientBuilder.HostnameVerificationPolicy.ANY; + connectionPoolSize(size); + hostnameVerification(policy); + if (adapterConfig.isDisableTrustManager()) { + disableTrustManager(); + } else { + trustStore(truststore); + } + return build(); + } +} \ No newline at end of file diff --git a/testsuite/integration/pom.xml b/testsuite/integration/pom.xml index f785747bf5..72b8768bdc 100755 --- a/testsuite/integration/pom.xml +++ b/testsuite/integration/pom.xml @@ -28,6 +28,55 @@ org.jboss.spec.javax.servlet jboss-servlet-api_3.1_spec + + org.jboss.resteasy + jaxrs-api + ${resteasy.version.latest} + + + org.jboss.resteasy + resteasy-jaxrs + ${resteasy.version.latest} + + + log4j + log4j + + + org.slf4j + slf4j-api + + + org.slf4j + slf4j-simple + + + + + org.jboss.resteasy + resteasy-client + ${resteasy.version.latest} + + + org.jboss.resteasy + resteasy-crypto + ${resteasy.version.latest} + + + org.jboss.resteasy + resteasy-multipart-provider + ${resteasy.version.latest} + + + org.jboss.resteasy + resteasy-jackson-provider + ${resteasy.version.latest} + + + org.jboss.resteasy + resteasy-undertow + ${resteasy.version.latest} + org.bouncycastle bcprov-jdk16 @@ -211,48 +260,6 @@ log4j log4j - - org.jboss.resteasy - resteasy-jaxrs - - - log4j - log4j - - - org.slf4j - slf4j-api - - - org.slf4j - slf4j-simple - - - - - org.jboss.resteasy - jaxrs-api - - - org.jboss.resteasy - resteasy-client - - - org.jboss.resteasy - resteasy-crypto - - - org.jboss.resteasy - resteasy-multipart-provider - - - org.jboss.resteasy - resteasy-jackson-provider - - - org.jboss.resteasy - resteasy-undertow - io.undertow undertow-servlet diff --git a/testsuite/performance/pom.xml b/testsuite/performance/pom.xml index 8ecfa2dc9d..0b14c76ab3 100755 --- a/testsuite/performance/pom.xml +++ b/testsuite/performance/pom.xml @@ -54,11 +54,6 @@ jaxrs-api provided - - org.jboss.resteasy - resteasy-client - provided - org.apache.jmeter ApacheJMeter_java