Get locale from token via builtin mapper.
This commit is contained in:
parent
121f8e9174
commit
302d0b58cc
9 changed files with 132 additions and 34 deletions
|
@ -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.account.freemarker;
|
||||
|
||||
import java.io.IOException;
|
||||
|
@ -199,9 +215,8 @@ public class FreeMarkerAccountProvider implements AccountProvider {
|
|||
BrowserSecurityHeaderSetup.headers(builder, realm);
|
||||
|
||||
String keycloakLocaleCookiePath = Urls.localeCookiePath(baseUri, realm.getName());
|
||||
String ngTranslateCookiePath = Urls.ngTranslateLocaleCookiePath(baseUri, realm.getName());
|
||||
|
||||
LocaleHelper.updateLocaleCookie(builder, locale, realm, uriInfo, keycloakLocaleCookiePath, ngTranslateCookiePath);
|
||||
LocaleHelper.updateLocaleCookie(builder, locale, realm, uriInfo, keycloakLocaleCookiePath);
|
||||
return builder.build();
|
||||
} catch (FreeMarkerException e) {
|
||||
logger.error("Failed to process template", e);
|
||||
|
|
|
@ -1,6 +1,21 @@
|
|||
/*
|
||||
* 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.freemarker;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
|
||||
|
@ -15,7 +30,6 @@ import java.util.*;
|
|||
*/
|
||||
public class LocaleHelper {
|
||||
public final static String LOCALE_COOKIE = "KEYCLOAK_LOCALE";
|
||||
public final static String NG_LOCALE_COOKIE = "NG_TRANSLATE_LANG_KEY";
|
||||
public static final String UI_LOCALES_PARAM = "ui_locales";
|
||||
public static final String KC_LOCALE_PARAM = "kc_locale";
|
||||
|
||||
|
@ -94,14 +108,12 @@ public class LocaleHelper {
|
|||
Locale locale,
|
||||
RealmModel realm,
|
||||
UriInfo uriInfo,
|
||||
String keycloakLocaleCookiePath,
|
||||
String ngTranslateLocaleCookiePath) {
|
||||
String path) {
|
||||
if (locale == null) {
|
||||
return;
|
||||
}
|
||||
boolean secure = realm.getSslRequired().isRequired(uriInfo.getRequestUri().getHost());
|
||||
builder.cookie(new NewCookie(LocaleHelper.LOCALE_COOKIE, locale.toLanguageTag(), keycloakLocaleCookiePath, null, null, 31536000, secure),
|
||||
new NewCookie(LocaleHelper.NG_LOCALE_COOKIE, "%22" + locale.toLanguageTag() + "%22", ngTranslateLocaleCookiePath, null, null, 31536000, secure));
|
||||
builder.cookie(new NewCookie(LocaleHelper.LOCALE_COOKIE, locale.toLanguageTag(), path, null, null, 31536000, secure));
|
||||
}
|
||||
|
||||
public static Locale findLocale(Set<String> supportedLocales, String ... localeStrings) {
|
||||
|
|
|
@ -24,7 +24,6 @@
|
|||
<script src="${resourceUrl}/lib/angular/angular-cookies.js"></script>
|
||||
<script src="${resourceUrl}/lib/angular/angular-sanitize.js"></script>
|
||||
<script src="${resourceUrl}/lib/angular/angular-translate.js"></script>
|
||||
<script src="${resourceUrl}/lib/angular/angular-translate-storage-cookie.js"></script>
|
||||
<script src="${resourceUrl}/lib/angular/angular-translate-loader-url.js"></script>
|
||||
<script src="${resourceUrl}/lib/angular/ui-bootstrap-tpls-0.11.0.js"></script>
|
||||
|
||||
|
|
|
@ -52,12 +52,18 @@ module.factory('authInterceptor', function($q, Auth) {
|
|||
};
|
||||
});
|
||||
|
||||
module.config(function($translateProvider) {
|
||||
module.config(['$translateProvider', function($translateProvider) {
|
||||
$translateProvider.useSanitizeValueStrategy('sanitizeParameters');
|
||||
$translateProvider.preferredLanguage('en');
|
||||
$translateProvider.useCookieStorage();
|
||||
|
||||
var locale = auth.authz.idTokenParsed.locale;
|
||||
if (locale !== undefined) {
|
||||
$translateProvider.preferredLanguage(locale);
|
||||
} else {
|
||||
$translateProvider.preferredLanguage('en');
|
||||
}
|
||||
|
||||
$translateProvider.useUrlLoader('messages.json');
|
||||
});
|
||||
}]);
|
||||
|
||||
module.config([ '$routeProvider', function($routeProvider) {
|
||||
$routeProvider
|
||||
|
|
|
@ -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.login.freemarker;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
|
@ -277,9 +293,8 @@ public class FreeMarkerLoginFormsProvider implements LoginFormsProvider {
|
|||
builder.header(entry.getKey(), entry.getValue());
|
||||
}
|
||||
|
||||
String keycloakLocaleCookiePath = Urls.localeCookiePath(baseUri, realm.getName());
|
||||
String ngTranslateCookiePath = Urls.ngTranslateLocaleCookiePath(baseUri, realm.getName());
|
||||
LocaleHelper.updateLocaleCookie(builder, locale, realm, uriInfo, keycloakLocaleCookiePath, ngTranslateCookiePath);
|
||||
String cookiePath = Urls.localeCookiePath(baseUri, realm.getName());
|
||||
LocaleHelper.updateLocaleCookie(builder, locale, realm, uriInfo, cookiePath);
|
||||
|
||||
return builder.build();
|
||||
} catch (FreeMarkerException e) {
|
||||
|
@ -379,10 +394,8 @@ public class FreeMarkerLoginFormsProvider implements LoginFormsProvider {
|
|||
builder.header(entry.getKey(), entry.getValue());
|
||||
}
|
||||
|
||||
String keycloakLocaleCookiePath = Urls.localeCookiePath(baseUri, realm.getName());
|
||||
String ngTranslateCookiePath = Urls.ngTranslateLocaleCookiePath(baseUri, realm.getName());
|
||||
|
||||
LocaleHelper.updateLocaleCookie(builder, locale, realm, uriInfo, keycloakLocaleCookiePath, ngTranslateCookiePath);
|
||||
String cookiePath = Urls.localeCookiePath(baseUri, realm.getName());
|
||||
LocaleHelper.updateLocaleCookie(builder, locale, realm, uriInfo, cookiePath);
|
||||
return builder.build();
|
||||
} catch (FreeMarkerException e) {
|
||||
logger.error("Failed to process template", e);
|
||||
|
@ -391,26 +404,32 @@ public class FreeMarkerLoginFormsProvider implements LoginFormsProvider {
|
|||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Response createLogin() {
|
||||
return createResponse(LoginFormsPages.LOGIN);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Response createPasswordReset() {
|
||||
return createResponse(LoginFormsPages.LOGIN_RESET_PASSWORD);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Response createLoginTotp() {
|
||||
return createResponse(LoginFormsPages.LOGIN_TOTP);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Response createRegistration() {
|
||||
return createResponse(LoginFormsPages.REGISTER);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Response createInfoPage() {
|
||||
return createResponse(LoginFormsPages.INFO);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Response createErrorPage() {
|
||||
if (status == null) {
|
||||
status = Response.Status.INTERNAL_SERVER_ERROR;
|
||||
|
@ -418,7 +437,7 @@ public class FreeMarkerLoginFormsProvider implements LoginFormsProvider {
|
|||
return createResponse(LoginFormsPages.ERROR);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Response createOAuthGrant(ClientSessionModel clientSession) {
|
||||
this.clientSession = clientSession;
|
||||
return createResponse(LoginFormsPages.OAUTH_GRANT);
|
||||
|
@ -502,11 +521,13 @@ public class FreeMarkerLoginFormsProvider implements LoginFormsProvider {
|
|||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FreeMarkerLoginFormsProvider setUser(UserModel user) {
|
||||
this.user = user;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FreeMarkerLoginFormsProvider setFormData(MultivaluedMap<String, String> formData) {
|
||||
this.formData = formData;
|
||||
return this;
|
||||
|
|
|
@ -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.protocol.oidc;
|
||||
|
||||
import org.keycloak.constants.KerberosConstants;
|
||||
|
@ -19,6 +35,7 @@ import java.util.ArrayList;
|
|||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.keycloak.protocol.oidc.mappers.UserAttributeMapper;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
|
@ -32,12 +49,14 @@ public class OIDCLoginProtocolFactory extends AbstractLoginProtocolFactory {
|
|||
public static final String GIVEN_NAME = "given name";
|
||||
public static final String FAMILY_NAME = "family name";
|
||||
public static final String FULL_NAME = "full name";
|
||||
public static final String LOCALE = "locale";
|
||||
public static final String USERNAME_CONSENT_TEXT = "${username}";
|
||||
public static final String EMAIL_CONSENT_TEXT = "${email}";
|
||||
public static final String EMAIL_VERIFIED_CONSENT_TEXT = "${emailVerified}";
|
||||
public static final String GIVEN_NAME_CONSENT_TEXT = "${givenName}";
|
||||
public static final String FAMILY_NAME_CONSENT_TEXT = "${familyName}";
|
||||
public static final String FULL_NAME_CONSENT_TEXT = "${fullName}";
|
||||
public static final String LOCALE_CONSENT_TEXT = "${locale}";
|
||||
|
||||
|
||||
@Override
|
||||
|
@ -95,6 +114,12 @@ public class OIDCLoginProtocolFactory extends AbstractLoginProtocolFactory {
|
|||
false, EMAIL_VERIFIED_CONSENT_TEXT,
|
||||
true, true);
|
||||
builtins.add(model);
|
||||
model = UserAttributeMapper.createClaimMapper(LOCALE,
|
||||
"locale",
|
||||
"locale", "String",
|
||||
false, LOCALE_CONSENT_TEXT,
|
||||
true, true, false);
|
||||
builtins.add(model);
|
||||
|
||||
ProtocolMapperModel fullName = new ProtocolMapperModel();
|
||||
fullName.setName(FULL_NAME);
|
||||
|
|
|
@ -208,15 +208,6 @@ public class Urls {
|
|||
return realmBase(baseUri).path(realmName).build().getRawPath();
|
||||
}
|
||||
|
||||
public static String ngTranslateLocaleCookiePath(URI baseUri, String realmName) {
|
||||
// I'm only using using localeCookiePath to get the /auth part of the path.
|
||||
// I can't assume the URL starts with "/auth". Keycloak could be installed
|
||||
// as root context. Typically, the angular-translate cookie path needs to be
|
||||
// /auth/admin/{realmName}/console/
|
||||
String basePath = localeCookiePath(baseUri, realmName);
|
||||
return basePath.substring(0, basePath.indexOf("realms")) + "admin/" + realmName + "/console/";
|
||||
}
|
||||
|
||||
public static URI themeRoot(URI baseUri) {
|
||||
return themeBase(baseUri).path(Version.RESOURCES_VERSION).build();
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
@ -49,7 +65,9 @@ import javax.ws.rs.core.UriInfo;
|
|||
import java.net.URI;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
import org.keycloak.freemarker.LocaleHelper;
|
||||
|
||||
/**
|
||||
* Stateless object that manages authentication
|
||||
|
@ -393,6 +411,9 @@ public class AuthenticationManager {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
handleLoginLocale(realm, userSession, request, uriInfo);
|
||||
|
||||
// refresh the cookies!
|
||||
createLoginCookie(realm, userSession.getUser(), userSession, uriInfo, clientConnection);
|
||||
if (userSession.getState() != UserSessionModel.State.LOGGED_IN) userSession.setState(UserSessionModel.State.LOGGED_IN);
|
||||
|
@ -406,6 +427,17 @@ public class AuthenticationManager {
|
|||
|
||||
}
|
||||
|
||||
// If a locale has been set on the login screen, associate that locale with the user
|
||||
private static void handleLoginLocale(RealmModel realm, UserSessionModel userSession,
|
||||
HttpRequest request, UriInfo uriInfo) {
|
||||
Cookie localeCookie = request.getHttpHeaders().getCookies().get(LocaleHelper.LOCALE_COOKIE);
|
||||
if (localeCookie == null) return;
|
||||
|
||||
UserModel user = userSession.getUser();
|
||||
Locale locale = LocaleHelper.getLocale(realm, user, uriInfo, request.getHttpHeaders());
|
||||
user.setSingleAttribute(UserModel.LOCALE, locale.toLanguageTag());
|
||||
}
|
||||
|
||||
public static Response nextActionAfterAuthentication(KeycloakSession session, UserSessionModel userSession, ClientSessionModel clientSession,
|
||||
ClientConnection clientConnection,
|
||||
HttpRequest request, UriInfo uriInfo, EventBuilder event) {
|
||||
|
|
|
@ -17,9 +17,6 @@
|
|||
|
||||
package org.keycloak.services.resources.admin;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FilenameFilter;
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
|
@ -44,7 +41,7 @@ public class AdminMessagesLoader {
|
|||
Properties messages = allMessages.get(allMessagesKey);
|
||||
if (messages != null) return messages;
|
||||
|
||||
Locale locale = new Locale(strLocale);
|
||||
Locale locale = Locale.forLanguageTag(strLocale);
|
||||
messages = theme.getMessages("admin-messages", locale);
|
||||
if (messages == null) return new Properties();
|
||||
|
||||
|
|
Loading…
Reference in a new issue