diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/otp-policy.html b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/otp-policy.html
index 6f08e96c90..85ad20bb88 100755
--- a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/otp-policy.html
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/otp-policy.html
@@ -59,6 +59,15 @@
Keycloak
instance cannot be reused.
+ */
public void close() {
client.close();
}
diff --git a/integration/admin-client/src/main/java/org/keycloak/admin/client/KeycloakBuilder.java b/integration/admin-client/src/main/java/org/keycloak/admin/client/KeycloakBuilder.java
new file mode 100644
index 0000000000..c4a1c3315d
--- /dev/null
+++ b/integration/admin-client/src/main/java/org/keycloak/admin/client/KeycloakBuilder.java
@@ -0,0 +1,107 @@
+package org.keycloak.admin.client;
+
+import org.jboss.resteasy.client.jaxrs.ResteasyClient;
+import org.jboss.resteasy.client.jaxrs.ResteasyClientBuilder;
+
+/**
+ * Provides a {@link Keycloak} client builder with the ability to customize the underlying
+ * {@link ResteasyClient RESTEasy client} used to communicate with the Keycloak server.
+ *
+ * Example usage with a connection pool size of 20:
+ * + *+ * Keycloak keycloak = KeycloakBuilder.builder() + * .serverUrl("https:/sso.example.com/auth") + * .realm("realm") + * .username("user") + * .password("pass") + * .clientId("client") + * .clientSecret("secret") + * .resteasyClient(new ResteasyClientBuilder().connectionPoolSize(20).build()) + * .build(); + *+ * + * @author Scott Rossillo + * @see ResteasyClientBuilder + */ +public class KeycloakBuilder { + private String serverUrl; + private String realm; + private String username; + private String password; + private String clientId; + private String clientSecret; + private ResteasyClient resteasyClient; + + public KeycloakBuilder serverUrl(String serverUrl) { + this.serverUrl = serverUrl; + return this; + } + + public KeycloakBuilder realm(String realm) { + this.realm = realm; + return this; + } + + public KeycloakBuilder username(String username) { + this.username = username; + return this; + } + + public KeycloakBuilder password(String password) { + this.password = password; + return this; + } + + public KeycloakBuilder clientId(String clientId) { + this.clientId = clientId; + return this; + } + + public KeycloakBuilder clientSecret(String clientSecret) { + this.clientSecret = clientSecret; + return this; + } + + public KeycloakBuilder resteasyClient(ResteasyClient resteasyClient) { + this.resteasyClient = resteasyClient; + return this; + } + + /** + * Builds a new Keycloak client from this builder. + */ + public Keycloak build() { + if (serverUrl == null) { + throw new IllegalStateException("serverUrl required"); + } + + if (realm == null) { + throw new IllegalStateException("realm required"); + } + + if (username == null) { + throw new IllegalStateException("username required"); + } + + if (password == null) { + throw new IllegalStateException("password required"); + } + + if (clientId == null) { + throw new IllegalStateException("clientId required"); + } + + return new Keycloak(serverUrl, realm, username, password, clientId, clientSecret, resteasyClient); + } + + private KeycloakBuilder() { + } + + /** + * Returns a new Keycloak builder. + */ + public static KeycloakBuilder builder() { + return new KeycloakBuilder(); + } +} diff --git a/integration/spring-security/src/main/java/org/keycloak/adapters/springsecurity/facade/WrappedHttpServletRequest.java b/integration/spring-security/src/main/java/org/keycloak/adapters/springsecurity/facade/WrappedHttpServletRequest.java index e2e5ba40f5..c72888fb0c 100755 --- a/integration/spring-security/src/main/java/org/keycloak/adapters/springsecurity/facade/WrappedHttpServletRequest.java +++ b/integration/spring-security/src/main/java/org/keycloak/adapters/springsecurity/facade/WrappedHttpServletRequest.java @@ -64,6 +64,12 @@ class WrappedHttpServletRequest implements Request { @Override public Cookie getCookie(String cookieName) { + javax.servlet.http.Cookie[] cookies = request.getCookies(); + + if (cookies == null) { + return null; + } + for (javax.servlet.http.Cookie cookie : request.getCookies()) { if (cookie.getName().equals(cookieName)) { return new Cookie(cookie.getName(), cookie.getValue(), cookie.getVersion(), cookie.getDomain(), cookie.getPath()); diff --git a/integration/spring-security/src/test/java/org/keycloak/adapters/springsecurity/facade/WrappedHttpServletRequestTest.java b/integration/spring-security/src/test/java/org/keycloak/adapters/springsecurity/facade/WrappedHttpServletRequestTest.java index 91ec35bfa3..80d682e633 100644 --- a/integration/spring-security/src/test/java/org/keycloak/adapters/springsecurity/facade/WrappedHttpServletRequestTest.java +++ b/integration/spring-security/src/test/java/org/keycloak/adapters/springsecurity/facade/WrappedHttpServletRequestTest.java @@ -24,10 +24,11 @@ public class WrappedHttpServletRequestTest { private static final String QUERY_PARM_2 = "code2"; private WrappedHttpServletRequest request; + private MockHttpServletRequest mockHttpServletRequest; @Before public void setUp() throws Exception { - MockHttpServletRequest mockHttpServletRequest = new MockHttpServletRequest(); + mockHttpServletRequest = new MockHttpServletRequest(); request = new WrappedHttpServletRequest(mockHttpServletRequest); mockHttpServletRequest.setMethod(REQUEST_METHOD); @@ -75,6 +76,13 @@ public class WrappedHttpServletRequestTest { assertNotNull(request.getCookie(COOKIE_NAME)); } + @Test + public void testGetCookieCookiesNull() throws Exception + { + mockHttpServletRequest.setCookies(null); + request.getCookie(COOKIE_NAME); + } + @Test public void testGetHeader() throws Exception { String header = request.getHeader(HEADER_SINGLE_VALUE); diff --git a/model/api/src/main/java/org/keycloak/migration/migrators/MigrateTo1_6_0.java b/model/api/src/main/java/org/keycloak/migration/migrators/MigrateTo1_6_0.java index 6712c081b5..d668ae0444 100644 --- a/model/api/src/main/java/org/keycloak/migration/migrators/MigrateTo1_6_0.java +++ b/model/api/src/main/java/org/keycloak/migration/migrators/MigrateTo1_6_0.java @@ -1,8 +1,23 @@ +/* + * Copyright 2015 Red Hat Inc. and/or its affiliates and other contributors + * as indicated by the @author tags. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ package org.keycloak.migration.migrators; import java.util.List; -import org.keycloak.Config; import org.keycloak.migration.MigrationProvider; import org.keycloak.migration.ModelVersion; import org.keycloak.models.*; @@ -52,10 +67,14 @@ public class MigrateTo1_6_0 { } ClientModel adminConsoleClient = realm.getClientByClientId(Constants.ADMIN_CONSOLE_CLIENT_ID); - if (adminConsoleClient != null) { + if ((adminConsoleClient != null) && !localeMapperAdded(adminConsoleClient)) { adminConsoleClient.addProtocolMapper(localeMapper); } } } + private boolean localeMapperAdded(ClientModel adminConsoleClient) { + return adminConsoleClient.getProtocolMapperByName("openid-connect", "locale") != null; + } + } diff --git a/services/src/main/java/org/keycloak/protocol/ProtocolMapperUtils.java b/services/src/main/java/org/keycloak/protocol/ProtocolMapperUtils.java index 829e8635e5..c1a9938547 100755 --- a/services/src/main/java/org/keycloak/protocol/ProtocolMapperUtils.java +++ b/services/src/main/java/org/keycloak/protocol/ProtocolMapperUtils.java @@ -1,12 +1,13 @@ package org.keycloak.protocol; -import org.keycloak.models.KeycloakSessionFactory; +import org.keycloak.models.KeycloakSession; import org.keycloak.models.ProtocolMapperModel; import org.keycloak.models.UserModel; -import org.keycloak.representations.AccessToken; +import org.keycloak.protocol.oidc.OIDCLoginProtocol; +import org.keycloak.protocol.oidc.OIDCLoginProtocolFactory; +import org.keycloak.provider.ProviderFactory; import java.lang.reflect.Method; -import java.util.List; /** * @author Bill Burke @@ -59,4 +60,25 @@ public class ProtocolMapperUtils { } } + + /** + * Find the builtin locale mapper. + * + * @param session A KeycloakSession + * @return The builtin locale mapper. + */ + public static ProtocolMapperModel findLocaleMapper(KeycloakSession session) { + ProtocolMapperModel found = null; + for (ProviderFactory p : session.getKeycloakSessionFactory().getProviderFactories(LoginProtocol.class)) { + LoginProtocolFactory factory = (LoginProtocolFactory) p; + for (ProtocolMapperModel mapper : factory.getBuiltinMappers()) { + if (mapper.getName().equals(OIDCLoginProtocolFactory.LOCALE) && mapper.getProtocol().equals(OIDCLoginProtocol.LOGIN_PROTOCOL)) { + found = mapper; + break; + } + } + if (found != null) break; + } + return found; + } } 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 958cd3e3aa..06c19ac369 100755 --- a/services/src/main/java/org/keycloak/services/managers/RealmManager.java +++ b/services/src/main/java/org/keycloak/services/managers/RealmManager.java @@ -1,3 +1,19 @@ +/* + * Copyright 2015 Red Hat Inc. and/or its affiliates and other contributors + * as indicated by the @author tags. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ package org.keycloak.services.managers; import org.jboss.logging.Logger; @@ -33,6 +49,8 @@ import org.keycloak.timer.TimerProvider; import java.util.Collections; import java.util.HashSet; import java.util.List; +import org.keycloak.models.ProtocolMapperModel; +import org.keycloak.protocol.ProtocolMapperUtils; /** * Per request object @@ -124,6 +142,9 @@ public class RealmManager implements RealmImporter { adminConsole.addRedirectUri(baseUrl + "/*"); adminConsole.setFullScopeAllowed(false); + ProtocolMapperModel localeMapper = ProtocolMapperUtils.findLocaleMapper(session); + if (localeMapper != null) adminConsole.addProtocolMapper(localeMapper); + RoleModel adminRole; if (realm.getName().equals(Config.getAdminRealm())) { adminRole = realm.getRole(AdminRoles.ADMIN); @@ -194,7 +215,7 @@ public class RealmManager implements RealmImporter { if(rep.getEnabledEventTypes() != null) { realm.setEnabledEventTypes(new HashSet