KEYCLOAK-13116 Fix backwards compatilbity changes in LocaleSelectorSPI

This commit is contained in:
stianst 2020-03-02 15:00:53 +01:00 committed by Stian Thorgersen
parent 8ed355a5fe
commit bcb542d9cc
13 changed files with 241 additions and 45 deletions

View file

@ -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);
}

View file

@ -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();
}

View file

@ -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<LocaleUpdaterProvider> {
}

View file

@ -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<? extends Provider> getProviderClass() {
return LocaleUpdaterProvider.class;
}
@Override
public Class<? extends ProviderFactory> getProviderFactoryClass() {
return LocaleUpdaterProviderFactory.class;
}
}

View file

@ -33,6 +33,7 @@
#
org.keycloak.locale.LocaleSelectorSPI
org.keycloak.locale.LocaleUpdaterSPI
org.keycloak.storage.UserStorageProviderSpi
org.keycloak.theme.ThemeResourceSpi
org.keycloak.theme.ThemeSelectorSpi

View file

@ -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();
}
}
}

View file

@ -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;

View file

@ -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() {
}
}

View file

@ -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";
}
}

View file

@ -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

View file

@ -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);
}
}
}

View file

@ -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);

View file

@ -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