diff --git a/server-spi/src/main/java/org/keycloak/locale/LocaleSelectorProvider.java b/server-spi/src/main/java/org/keycloak/locale/LocaleSelectorProvider.java index bae4e72303..e6597d3e6d 100644 --- a/server-spi/src/main/java/org/keycloak/locale/LocaleSelectorProvider.java +++ b/server-spi/src/main/java/org/keycloak/locale/LocaleSelectorProvider.java @@ -16,12 +16,10 @@ */ package org.keycloak.locale; - import org.keycloak.models.RealmModel; import org.keycloak.models.UserModel; import org.keycloak.provider.Provider; -import javax.ws.rs.core.UriInfo; import java.util.Locale; public interface LocaleSelectorProvider extends Provider { @@ -34,16 +32,9 @@ public interface LocaleSelectorProvider extends Provider { /** * Resolve the locale which should be used for the request - * * @param user * @return */ - Locale resolveLocale(UserModel user); - - void updateUsersLocale(UserModel user, String locale); - - void updateLocaleCookie(RealmModel realm, String locale, UriInfo uriInfo); - - void expireLocaleCookie(RealmModel realm, UriInfo uriInfo); + Locale resolveLocale(RealmModel realm, UserModel user); } \ No newline at end of file diff --git a/server-spi/src/main/java/org/keycloak/locale/LocaleUpdaterProvider.java b/server-spi/src/main/java/org/keycloak/locale/LocaleUpdaterProvider.java new file mode 100644 index 0000000000..1571376009 --- /dev/null +++ b/server-spi/src/main/java/org/keycloak/locale/LocaleUpdaterProvider.java @@ -0,0 +1,14 @@ +package org.keycloak.locale; + +import org.keycloak.models.UserModel; +import org.keycloak.provider.Provider; + +public interface LocaleUpdaterProvider extends Provider { + + void updateUsersLocale(UserModel user, String locale); + + void updateLocaleCookie(String locale); + + void expireLocaleCookie(); + +} diff --git a/server-spi/src/main/java/org/keycloak/locale/LocaleUpdaterProviderFactory.java b/server-spi/src/main/java/org/keycloak/locale/LocaleUpdaterProviderFactory.java new file mode 100644 index 0000000000..bdc0193240 --- /dev/null +++ b/server-spi/src/main/java/org/keycloak/locale/LocaleUpdaterProviderFactory.java @@ -0,0 +1,22 @@ +/* + * Copyright 2018 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * 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.locale; + +import org.keycloak.provider.ProviderFactory; + +public interface LocaleUpdaterProviderFactory extends ProviderFactory { +} diff --git a/server-spi/src/main/java/org/keycloak/locale/LocaleUpdaterSPI.java b/server-spi/src/main/java/org/keycloak/locale/LocaleUpdaterSPI.java new file mode 100644 index 0000000000..3d38ed08fa --- /dev/null +++ b/server-spi/src/main/java/org/keycloak/locale/LocaleUpdaterSPI.java @@ -0,0 +1,45 @@ +/* + * Copyright 2018 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * 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.locale; + +import org.keycloak.provider.Provider; +import org.keycloak.provider.ProviderFactory; +import org.keycloak.provider.Spi; + +public class LocaleUpdaterSPI implements Spi { + + @Override + public boolean isInternal() { + return false; + } + + @Override + public String getName() { + return "localeUpdater"; + } + + @Override + public Class getProviderClass() { + return LocaleUpdaterProvider.class; + } + + @Override + public Class getProviderFactoryClass() { + return LocaleUpdaterProviderFactory.class; + } + +} diff --git a/server-spi/src/main/resources/META-INF/services/org.keycloak.provider.Spi b/server-spi/src/main/resources/META-INF/services/org.keycloak.provider.Spi index c8ee6d107d..bdef3ed12c 100755 --- a/server-spi/src/main/resources/META-INF/services/org.keycloak.provider.Spi +++ b/server-spi/src/main/resources/META-INF/services/org.keycloak.provider.Spi @@ -33,6 +33,7 @@ # org.keycloak.locale.LocaleSelectorSPI +org.keycloak.locale.LocaleUpdaterSPI org.keycloak.storage.UserStorageProviderSpi org.keycloak.theme.ThemeResourceSpi org.keycloak.theme.ThemeSelectorSpi diff --git a/services/src/main/java/org/keycloak/authentication/requiredactions/UpdateUserLocaleAction.java b/services/src/main/java/org/keycloak/authentication/requiredactions/UpdateUserLocaleAction.java index 295a6c682c..0f79d43ec8 100644 --- a/services/src/main/java/org/keycloak/authentication/requiredactions/UpdateUserLocaleAction.java +++ b/services/src/main/java/org/keycloak/authentication/requiredactions/UpdateUserLocaleAction.java @@ -5,6 +5,7 @@ import org.keycloak.authentication.RequiredActionContext; import org.keycloak.authentication.RequiredActionFactory; import org.keycloak.authentication.RequiredActionProvider; import org.keycloak.locale.LocaleSelectorProvider; +import org.keycloak.locale.LocaleUpdaterProvider; import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSessionFactory; import org.keycloak.models.UserModel; @@ -20,15 +21,17 @@ public class UpdateUserLocaleAction implements RequiredActionProvider, RequiredA public void evaluateTriggers(RequiredActionContext context) { String userRequestedLocale = context.getAuthenticationSession().getAuthNote(LocaleSelectorProvider.USER_REQUEST_LOCALE); if (userRequestedLocale != null) { - LocaleSelectorProvider provider = context.getSession().getProvider(LocaleSelectorProvider.class); - provider.updateUsersLocale(context.getUser(), userRequestedLocale); + LocaleUpdaterProvider updater = context.getSession().getProvider(LocaleUpdaterProvider.class); + updater.updateUsersLocale(context.getUser(), userRequestedLocale); } else { String userLocale = context.getUser().getFirstAttribute(UserModel.LOCALE); - LocaleSelectorProvider provider = context.getSession().getProvider(LocaleSelectorProvider.class); + if (userLocale != null) { - provider.updateLocaleCookie(context.getRealm(), userLocale, context.getUriInfo()); + LocaleUpdaterProvider updater = context.getSession().getProvider(LocaleUpdaterProvider.class); + updater.updateLocaleCookie(userLocale); } else { - provider.expireLocaleCookie(context.getRealm(), context.getUriInfo()); + LocaleUpdaterProvider updater = context.getSession().getProvider(LocaleUpdaterProvider.class); + updater.expireLocaleCookie(); } } } diff --git a/services/src/main/java/org/keycloak/locale/DefaultLocaleSelectorProvider.java b/services/src/main/java/org/keycloak/locale/DefaultLocaleSelectorProvider.java index e2c488c36b..1b435ddd28 100644 --- a/services/src/main/java/org/keycloak/locale/DefaultLocaleSelectorProvider.java +++ b/services/src/main/java/org/keycloak/locale/DefaultLocaleSelectorProvider.java @@ -43,8 +43,7 @@ public class DefaultLocaleSelectorProvider implements LocaleSelectorProvider { } @Override - public Locale resolveLocale(UserModel user) { - RealmModel realm = session.getContext().getRealm(); + public Locale resolveLocale(RealmModel realm, UserModel user) { HttpHeaders requestHeaders = session.getContext().getRequestHeaders(); AuthenticationSessionModel session = this.session.getContext().getAuthenticationSession(); @@ -65,29 +64,6 @@ public class DefaultLocaleSelectorProvider implements LocaleSelectorProvider { return Locale.ENGLISH; } - public void updateUsersLocale(UserModel user, String locale) { - if (!locale.equals(user.getFirstAttribute("locale"))) { - try { - user.setSingleAttribute(UserModel.LOCALE, locale); - updateLocaleCookie(session.getContext().getRealm(), locale, session.getContext().getUri()); - } catch (ReadOnlyException e) { - logger.debug("Attempt to store 'locale' attribute to read only user model. Ignoring exception", e); - } - } - logger.debugv("Setting locale for user {0} to {1}", user.getUsername(), locale); - } - - public void updateLocaleCookie(RealmModel realm, String locale, UriInfo uriInfo) { - boolean secure = realm.getSslRequired().isRequired(uriInfo.getRequestUri().getHost()); - CookieHelper.addCookie(LocaleSelectorProvider.LOCALE_COOKIE, locale, AuthenticationManager.getRealmCookiePath(realm, uriInfo), null, null, -1, secure, true); - logger.debugv("Updating locale cookie to {0}", locale); - } - - public void expireLocaleCookie(RealmModel realm, UriInfo uriInfo) { - boolean secure = realm.getSslRequired().isRequired(uriInfo.getRequestUri().getHost()); - CookieHelper.addCookie(LocaleSelectorProvider.LOCALE_COOKIE, "", AuthenticationManager.getRealmCookiePath(realm, uriInfo), null, "Expiring cookie", 0, secure, true); - } - private Locale getUserLocale(RealmModel realm, AuthenticationSessionModel session, UserModel user, HttpHeaders requestHeaders) { Locale locale; diff --git a/services/src/main/java/org/keycloak/locale/DefaultLocaleUpdaterProvider.java b/services/src/main/java/org/keycloak/locale/DefaultLocaleUpdaterProvider.java new file mode 100644 index 0000000000..e00fd6a64c --- /dev/null +++ b/services/src/main/java/org/keycloak/locale/DefaultLocaleUpdaterProvider.java @@ -0,0 +1,75 @@ +/* + * Copyright 2018 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * 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.locale; + +import org.jboss.logging.Logger; +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.RealmModel; +import org.keycloak.models.UserModel; +import org.keycloak.services.managers.AuthenticationManager; +import org.keycloak.services.util.CookieHelper; +import org.keycloak.storage.ReadOnlyException; + +import javax.ws.rs.core.UriInfo; + +public class DefaultLocaleUpdaterProvider implements LocaleUpdaterProvider { + + private static final Logger logger = Logger.getLogger(LocaleSelectorProvider.class); + + private KeycloakSession session; + + public DefaultLocaleUpdaterProvider(KeycloakSession session) { + this.session = session; + } + + @Override + public void updateUsersLocale(UserModel user, String locale) { + if (!locale.equals(user.getFirstAttribute("locale"))) { + try { + user.setSingleAttribute(UserModel.LOCALE, locale); + updateLocaleCookie(locale); + } catch (ReadOnlyException e) { + logger.debug("Attempt to store 'locale' attribute to read only user model. Ignoring exception", e); + } + } + logger.debugv("Setting locale for user {0} to {1}", user.getUsername(), locale); + } + + @Override + public void updateLocaleCookie(String locale) { + RealmModel realm = session.getContext().getRealm(); + UriInfo uriInfo = session.getContext().getUri(); + + boolean secure = realm.getSslRequired().isRequired(uriInfo.getRequestUri().getHost()); + CookieHelper.addCookie(LocaleSelectorProvider.LOCALE_COOKIE, locale, AuthenticationManager.getRealmCookiePath(realm, uriInfo), null, null, -1, secure, true); + logger.debugv("Updating locale cookie to {0}", locale); + } + + @Override + public void expireLocaleCookie() { + RealmModel realm = session.getContext().getRealm(); + UriInfo uriInfo = session.getContext().getUri(); + + boolean secure = realm.getSslRequired().isRequired(session.getContext().getConnection()); + CookieHelper.addCookie(LocaleSelectorProvider.LOCALE_COOKIE, "", AuthenticationManager.getRealmCookiePath(realm, uriInfo), null, "Expiring cookie", 0, secure, true); + } + + @Override + public void close() { + } + +} diff --git a/services/src/main/java/org/keycloak/locale/DefaultLocaleUpdaterProviderFactory.java b/services/src/main/java/org/keycloak/locale/DefaultLocaleUpdaterProviderFactory.java new file mode 100644 index 0000000000..b526bc416e --- /dev/null +++ b/services/src/main/java/org/keycloak/locale/DefaultLocaleUpdaterProviderFactory.java @@ -0,0 +1,47 @@ +/* + * Copyright 2018 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * 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.locale; + +import org.keycloak.Config; +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.KeycloakSessionFactory; + +public class DefaultLocaleUpdaterProviderFactory implements LocaleUpdaterProviderFactory { + + @Override + public LocaleUpdaterProvider create(KeycloakSession session) { + return new DefaultLocaleUpdaterProvider(session); + } + + @Override + public void init(Config.Scope config) { + } + + @Override + public void postInit(KeycloakSessionFactory factory) { + } + + @Override + public void close() { + } + + @Override + public String getId() { + return "default"; + } + +} diff --git a/services/src/main/java/org/keycloak/services/DefaultKeycloakContext.java b/services/src/main/java/org/keycloak/services/DefaultKeycloakContext.java index f842be6707..9958331a19 100755 --- a/services/src/main/java/org/keycloak/services/DefaultKeycloakContext.java +++ b/services/src/main/java/org/keycloak/services/DefaultKeycloakContext.java @@ -129,7 +129,7 @@ public class DefaultKeycloakContext implements KeycloakContext { @Override public Locale resolveLocale(UserModel user) { - return session.getProvider(LocaleSelectorProvider.class).resolveLocale(user); + return session.getProvider(LocaleSelectorProvider.class).resolveLocale(getRealm(), user); } @Override diff --git a/services/src/main/java/org/keycloak/services/resources/LoginActionsService.java b/services/src/main/java/org/keycloak/services/resources/LoginActionsService.java index dd269eed58..905f385fe1 100755 --- a/services/src/main/java/org/keycloak/services/resources/LoginActionsService.java +++ b/services/src/main/java/org/keycloak/services/resources/LoginActionsService.java @@ -48,6 +48,8 @@ import org.keycloak.events.EventBuilder; import org.keycloak.events.EventType; import org.keycloak.exceptions.TokenNotActiveException; import org.keycloak.locale.LocaleSelectorProvider; +import org.keycloak.locale.LocaleSelectorSPI; +import org.keycloak.locale.LocaleUpdaterProvider; import org.keycloak.models.ActionTokenKeyModel; import org.keycloak.models.AuthenticationFlowModel; import org.keycloak.models.ClientModel; @@ -269,8 +271,9 @@ public class LoginActionsService { String locale = session.getContext().getUri().getQueryParameters().getFirst(LocaleSelectorProvider.KC_LOCALE_PARAM); if (locale != null) { authSession.setAuthNote(LocaleSelectorProvider.USER_REQUEST_LOCALE, locale); - LocaleSelectorProvider localeSelectorProvider = session.getProvider(LocaleSelectorProvider.class); - localeSelectorProvider.updateLocaleCookie(realm, locale, session.getContext().getUri()); + + LocaleUpdaterProvider localeUpdater = session.getProvider(LocaleUpdaterProvider.class); + localeUpdater.updateLocaleCookie(locale); } } } diff --git a/services/src/main/java/org/keycloak/services/resources/account/AccountFormService.java b/services/src/main/java/org/keycloak/services/resources/account/AccountFormService.java index 6ce97de0f0..9b562f909f 100755 --- a/services/src/main/java/org/keycloak/services/resources/account/AccountFormService.java +++ b/services/src/main/java/org/keycloak/services/resources/account/AccountFormService.java @@ -40,6 +40,7 @@ import org.keycloak.forms.account.AccountPages; import org.keycloak.forms.account.AccountProvider; import org.keycloak.forms.login.LoginFormsProvider; import org.keycloak.locale.LocaleSelectorProvider; +import org.keycloak.locale.LocaleUpdaterProvider; import org.keycloak.models.AccountRoles; import org.keycloak.models.AuthenticatedClientSessionModel; import org.keycloak.models.ClientModel; @@ -225,8 +226,8 @@ public class AccountFormService extends AbstractSecuredLocalService { String locale = session.getContext().getUri().getQueryParameters().getFirst(LocaleSelectorProvider.KC_LOCALE_PARAM); if (locale != null) { - LocaleSelectorProvider localeSelectorProvider = session.getProvider(LocaleSelectorProvider.class); - localeSelectorProvider.updateUsersLocale(auth.getUser(), locale); + LocaleUpdaterProvider updater = session.getProvider(LocaleUpdaterProvider.class); + updater.updateUsersLocale(auth.getUser(), locale); } return account.createResponse(page); diff --git a/services/src/main/resources/META-INF/services/org.keycloak.locale.LocaleUpdaterProviderFactory b/services/src/main/resources/META-INF/services/org.keycloak.locale.LocaleUpdaterProviderFactory new file mode 100644 index 0000000000..ef2dd65925 --- /dev/null +++ b/services/src/main/resources/META-INF/services/org.keycloak.locale.LocaleUpdaterProviderFactory @@ -0,0 +1,18 @@ +# +# Copyright 2016 Red Hat, Inc. and/or its affiliates +# and other contributors as indicated by the @author tags. +# +# 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. +# + +org.keycloak.locale.DefaultLocaleUpdaterProviderFactory \ No newline at end of file