allow language system to be loaded from multiple files. closes #817 closes #876

pull/988/head
Justin Richer 9 years ago
parent e255fc1a10
commit 2496dc114c

@ -16,6 +16,7 @@
*******************************************************************************/
package org.mitre.openid.connect.config;
import java.util.List;
import java.util.Locale;
import javax.annotation.PostConstruct;
@ -25,6 +26,9 @@ import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.util.StringUtils;
import com.google.common.collect.Lists;
import com.google.gson.Gson;
/**
@ -55,6 +59,8 @@ public class ConfigurationPropertiesBean {
private boolean forceHttps = false; // by default we just log a warning for HTTPS deployment
private Locale locale = Locale.ENGLISH; // we default to the english translation
private List<String> languageNamespaces = Lists.newArrayList("messages");
public boolean dualClient = false;
@ -67,7 +73,7 @@ public class ConfigurationPropertiesBean {
* @throws HttpsUrlRequiredException
*/
@PostConstruct
public void checkForHttps() {
public void checkConfigConsistency() {
if (!StringUtils.startsWithIgnoreCase(issuer, "https")) {
if (this.forceHttps) {
logger.error("Configured issuer url is not using https scheme. Server will be shut down!");
@ -77,6 +83,10 @@ public class ConfigurationPropertiesBean {
logger.warn("\n\n**\n** WARNING: Configured issuer url is not using https scheme.\n**\n\n");
}
}
if (languageNamespaces == null || languageNamespaces.isEmpty()) {
logger.error("No configured language namespaces! Text rendering will fail!");
}
}
/**
@ -171,7 +181,21 @@ public class ConfigurationPropertiesBean {
this.locale = locale;
}
/**
/**
* @return the languageNamespaces
*/
public List<String> getLanguageNamespaces() {
return languageNamespaces;
}
/**
* @param languageNamespaces the languageNamespaces to set
*/
public void setLanguageNamespaces(List<String> languageNamespaces) {
this.languageNamespaces = languageNamespaces;
}
/**
* @return true if dual client is configured, otherwise false
*/
public boolean isDualClient() {
@ -184,4 +208,19 @@ public class ConfigurationPropertiesBean {
public void setDualClient(boolean dualClient) {
this.dualClient = dualClient;
}
/**
* Get the list of namespaces as a JSON string
* @return
*/
public String getLanguageNamespacesString() {
return new Gson().toJson(getLanguageNamespaces());
}
/**
* Get the default namespace (first in the nonempty list)
*/
public String getDefaultLanguageNamespace() {
return getLanguageNamespaces().get(0);
}
}

@ -63,7 +63,7 @@ public class ConfigurationPropertiesBeanTest {
// leave as default, which is unset/false
try {
bean.setIssuer("http://localhost:8080/openid-connect-server/");
bean.checkForHttps();
bean.checkConfigConsistency();
} catch (BeanCreationException e) {
fail("Unexpected BeanCreationException for http issuer with default forceHttps, message:" + e.getMessage());
}
@ -77,7 +77,7 @@ public class ConfigurationPropertiesBeanTest {
try {
bean.setIssuer("http://localhost:8080/openid-connect-server/");
bean.setForceHttps(false);
bean.checkForHttps();
bean.checkConfigConsistency();
} catch (BeanCreationException e) {
fail("Unexpected BeanCreationException for http issuer with forceHttps=false, message:" + e.getMessage());
}
@ -90,7 +90,7 @@ public class ConfigurationPropertiesBeanTest {
// set to true
bean.setIssuer("http://localhost:8080/openid-connect-server/");
bean.setForceHttps(true);
bean.checkForHttps();
bean.checkConfigConsistency();
}
@Test
@ -100,7 +100,7 @@ public class ConfigurationPropertiesBeanTest {
// leave as default, which is unset/false
try {
bean.setIssuer("https://localhost:8080/openid-connect-server/");
bean.checkForHttps();
bean.checkConfigConsistency();
} catch (BeanCreationException e) {
fail("Unexpected BeanCreationException for https issuer with default forceHttps, message:" + e.getMessage());
}
@ -114,7 +114,7 @@ public class ConfigurationPropertiesBeanTest {
try {
bean.setIssuer("https://localhost:8080/openid-connect-server/");
bean.setForceHttps(false);
bean.checkForHttps();
bean.checkConfigConsistency();
} catch (BeanCreationException e) {
fail("Unexpected BeanCreationException for https issuer with forceHttps=false, message:" + e.getMessage());
}
@ -128,7 +128,7 @@ public class ConfigurationPropertiesBeanTest {
try {
bean.setIssuer("https://localhost:8080/openid-connect-server/");
bean.setForceHttps(true);
bean.checkForHttps();
bean.checkConfigConsistency();
} catch (BeanCreationException e) {
fail("Unexpected BeanCreationException for https issuer with forceHttps=true, message:" + e.getMessage());
}

@ -48,7 +48,18 @@
<!-- This property sets the locale for server text -->
<!-- <property name="locale" value="sv" /> -->
<!-- This property sets the set of namespaces for language translation files. The default is "messages". These are checked in the order presented here. -->
<!--
<property name="languageNamespaces">
<list>
<value>foo</value>
<value>bar</value>
<value>messages</value>
</list>
</property>
-->
<!-- This property indicates if a dynamically registered client supports dual flows, such as client_credentials
at the same time with authorization_code or implicit -->
<!-- <property name="dualClient" value="true" /> -->

@ -40,7 +40,12 @@
$.i18n.init({
fallbackLng: "en",
lng: "${config.locale}",
resGetPath: "resources/js/locale/__lng__/messages.json"
resGetPath: "resources/js/locale/__lng__/__ns__.json",
ns: {
namespaces: ${config.languageNamespacesString},
defaultNs: '${config.defaultLanguageNamespace}'
},
fallbackNS: ${config.languageNamespacesString}
});
moment.locale("${config.locale}");
// safely set the title of the application

@ -21,13 +21,16 @@ import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.support.AbstractMessageSource;
import org.springframework.core.io.Resource;
@ -50,19 +53,22 @@ public class JsonMessageSource extends AbstractMessageSource {
private Locale fallbackLocale = new Locale("en"); // US English is the fallback language
private Map<Locale, JsonObject> languageMaps = new HashMap<>();
private Map<Locale, List<JsonObject>> languageMaps = new HashMap<>();
@Autowired
private ConfigurationPropertiesBean config;
@Override
protected MessageFormat resolveCode(String code, Locale locale) {
JsonObject lang = getLanguageMap(locale);
List<JsonObject> langs = getLanguageMap(locale);
String value = getValue(code, lang);
String value = getValue(code, langs);
if (value == null) {
// if we haven't found anything, try the default locale
lang = getLanguageMap(fallbackLocale);
value = getValue(code, lang);
langs = getLanguageMap(fallbackLocale);
value = getValue(code, langs);
}
if (value == null) {
@ -76,6 +82,31 @@ public class JsonMessageSource extends AbstractMessageSource {
}
/**
* Get a value from the set of maps, taking the first match in order
* @param code
* @param langs
* @return
*/
private String getValue(String code, List<JsonObject> langs) {
if (langs == null || langs.isEmpty()) {
// no language maps, nothing to look up
return null;
}
for (JsonObject lang : langs) {
String value = getValue(code, lang);
if (value != null) {
// short circuit out of here if we find a match, otherwise keep going through the list
return value;
}
}
// if we didn't find anything return null
return null;
}
/**
* Get a value from a single map
* @param code
* @param locale
* @param lang
@ -126,20 +157,24 @@ public class JsonMessageSource extends AbstractMessageSource {
* @param locale
* @return
*/
private JsonObject getLanguageMap(Locale locale) {
private List<JsonObject> getLanguageMap(Locale locale) {
if (!languageMaps.containsKey(locale)) {
try {
String filename = locale.getLanguage() + File.separator + "messages.json";
Resource r = getBaseDirectory().createRelative(filename);
logger.info("No locale loaded, trying to load from " + r);
JsonParser parser = new JsonParser();
JsonObject obj = (JsonObject) parser.parse(new InputStreamReader(r.getInputStream(), "UTF-8"));
languageMaps.put(locale, obj);
List<JsonObject> set = new ArrayList<>();
for (String namespace : config.getLanguageNamespaces()) {
String filename = locale.getLanguage() + File.separator + namespace + ".json";
Resource r = getBaseDirectory().createRelative(filename);
logger.info("No locale loaded, trying to load from " + r);
JsonParser parser = new JsonParser();
JsonObject obj = (JsonObject) parser.parse(new InputStreamReader(r.getInputStream(), "UTF-8"));
set.add(obj);
}
languageMaps.put(locale, set);
} catch (JsonIOException | JsonSyntaxException | IOException e) {
logger.error("Unable to load locale", e);
}

Loading…
Cancel
Save