parent
e255fc1a10
commit
2496dc114c
|
@ -16,6 +16,7 @@
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
package org.mitre.openid.connect.config;
|
package org.mitre.openid.connect.config;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
|
||||||
import javax.annotation.PostConstruct;
|
import javax.annotation.PostConstruct;
|
||||||
|
@ -25,6 +26,9 @@ import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.beans.factory.BeanCreationException;
|
import org.springframework.beans.factory.BeanCreationException;
|
||||||
import org.springframework.util.StringUtils;
|
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 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 Locale locale = Locale.ENGLISH; // we default to the english translation
|
||||||
|
|
||||||
|
private List<String> languageNamespaces = Lists.newArrayList("messages");
|
||||||
|
|
||||||
public boolean dualClient = false;
|
public boolean dualClient = false;
|
||||||
|
|
||||||
|
@ -67,7 +73,7 @@ public class ConfigurationPropertiesBean {
|
||||||
* @throws HttpsUrlRequiredException
|
* @throws HttpsUrlRequiredException
|
||||||
*/
|
*/
|
||||||
@PostConstruct
|
@PostConstruct
|
||||||
public void checkForHttps() {
|
public void checkConfigConsistency() {
|
||||||
if (!StringUtils.startsWithIgnoreCase(issuer, "https")) {
|
if (!StringUtils.startsWithIgnoreCase(issuer, "https")) {
|
||||||
if (this.forceHttps) {
|
if (this.forceHttps) {
|
||||||
logger.error("Configured issuer url is not using https scheme. Server will be shut down!");
|
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");
|
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;
|
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
|
* @return true if dual client is configured, otherwise false
|
||||||
*/
|
*/
|
||||||
public boolean isDualClient() {
|
public boolean isDualClient() {
|
||||||
|
@ -184,4 +208,19 @@ public class ConfigurationPropertiesBean {
|
||||||
public void setDualClient(boolean dualClient) {
|
public void setDualClient(boolean dualClient) {
|
||||||
this.dualClient = 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
|
// leave as default, which is unset/false
|
||||||
try {
|
try {
|
||||||
bean.setIssuer("http://localhost:8080/openid-connect-server/");
|
bean.setIssuer("http://localhost:8080/openid-connect-server/");
|
||||||
bean.checkForHttps();
|
bean.checkConfigConsistency();
|
||||||
} catch (BeanCreationException e) {
|
} catch (BeanCreationException e) {
|
||||||
fail("Unexpected BeanCreationException for http issuer with default forceHttps, message:" + e.getMessage());
|
fail("Unexpected BeanCreationException for http issuer with default forceHttps, message:" + e.getMessage());
|
||||||
}
|
}
|
||||||
|
@ -77,7 +77,7 @@ public class ConfigurationPropertiesBeanTest {
|
||||||
try {
|
try {
|
||||||
bean.setIssuer("http://localhost:8080/openid-connect-server/");
|
bean.setIssuer("http://localhost:8080/openid-connect-server/");
|
||||||
bean.setForceHttps(false);
|
bean.setForceHttps(false);
|
||||||
bean.checkForHttps();
|
bean.checkConfigConsistency();
|
||||||
} catch (BeanCreationException e) {
|
} catch (BeanCreationException e) {
|
||||||
fail("Unexpected BeanCreationException for http issuer with forceHttps=false, message:" + e.getMessage());
|
fail("Unexpected BeanCreationException for http issuer with forceHttps=false, message:" + e.getMessage());
|
||||||
}
|
}
|
||||||
|
@ -90,7 +90,7 @@ public class ConfigurationPropertiesBeanTest {
|
||||||
// set to true
|
// set to true
|
||||||
bean.setIssuer("http://localhost:8080/openid-connect-server/");
|
bean.setIssuer("http://localhost:8080/openid-connect-server/");
|
||||||
bean.setForceHttps(true);
|
bean.setForceHttps(true);
|
||||||
bean.checkForHttps();
|
bean.checkConfigConsistency();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -100,7 +100,7 @@ public class ConfigurationPropertiesBeanTest {
|
||||||
// leave as default, which is unset/false
|
// leave as default, which is unset/false
|
||||||
try {
|
try {
|
||||||
bean.setIssuer("https://localhost:8080/openid-connect-server/");
|
bean.setIssuer("https://localhost:8080/openid-connect-server/");
|
||||||
bean.checkForHttps();
|
bean.checkConfigConsistency();
|
||||||
} catch (BeanCreationException e) {
|
} catch (BeanCreationException e) {
|
||||||
fail("Unexpected BeanCreationException for https issuer with default forceHttps, message:" + e.getMessage());
|
fail("Unexpected BeanCreationException for https issuer with default forceHttps, message:" + e.getMessage());
|
||||||
}
|
}
|
||||||
|
@ -114,7 +114,7 @@ public class ConfigurationPropertiesBeanTest {
|
||||||
try {
|
try {
|
||||||
bean.setIssuer("https://localhost:8080/openid-connect-server/");
|
bean.setIssuer("https://localhost:8080/openid-connect-server/");
|
||||||
bean.setForceHttps(false);
|
bean.setForceHttps(false);
|
||||||
bean.checkForHttps();
|
bean.checkConfigConsistency();
|
||||||
} catch (BeanCreationException e) {
|
} catch (BeanCreationException e) {
|
||||||
fail("Unexpected BeanCreationException for https issuer with forceHttps=false, message:" + e.getMessage());
|
fail("Unexpected BeanCreationException for https issuer with forceHttps=false, message:" + e.getMessage());
|
||||||
}
|
}
|
||||||
|
@ -128,7 +128,7 @@ public class ConfigurationPropertiesBeanTest {
|
||||||
try {
|
try {
|
||||||
bean.setIssuer("https://localhost:8080/openid-connect-server/");
|
bean.setIssuer("https://localhost:8080/openid-connect-server/");
|
||||||
bean.setForceHttps(true);
|
bean.setForceHttps(true);
|
||||||
bean.checkForHttps();
|
bean.checkConfigConsistency();
|
||||||
} catch (BeanCreationException e) {
|
} catch (BeanCreationException e) {
|
||||||
fail("Unexpected BeanCreationException for https issuer with forceHttps=true, message:" + e.getMessage());
|
fail("Unexpected BeanCreationException for https issuer with forceHttps=true, message:" + e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,7 +48,18 @@
|
||||||
|
|
||||||
<!-- This property sets the locale for server text -->
|
<!-- This property sets the locale for server text -->
|
||||||
<!-- <property name="locale" value="sv" /> -->
|
<!-- <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
|
<!-- This property indicates if a dynamically registered client supports dual flows, such as client_credentials
|
||||||
at the same time with authorization_code or implicit -->
|
at the same time with authorization_code or implicit -->
|
||||||
<!-- <property name="dualClient" value="true" /> -->
|
<!-- <property name="dualClient" value="true" /> -->
|
||||||
|
|
|
@ -40,7 +40,12 @@
|
||||||
$.i18n.init({
|
$.i18n.init({
|
||||||
fallbackLng: "en",
|
fallbackLng: "en",
|
||||||
lng: "${config.locale}",
|
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}");
|
moment.locale("${config.locale}");
|
||||||
// safely set the title of the application
|
// safely set the title of the application
|
||||||
|
|
|
@ -21,13 +21,16 @@ import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStreamReader;
|
import java.io.InputStreamReader;
|
||||||
import java.text.MessageFormat;
|
import java.text.MessageFormat;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.context.support.AbstractMessageSource;
|
import org.springframework.context.support.AbstractMessageSource;
|
||||||
import org.springframework.core.io.Resource;
|
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 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
|
@Override
|
||||||
protected MessageFormat resolveCode(String code, Locale locale) {
|
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 (value == null) {
|
||||||
// if we haven't found anything, try the default locale
|
// if we haven't found anything, try the default locale
|
||||||
lang = getLanguageMap(fallbackLocale);
|
langs = getLanguageMap(fallbackLocale);
|
||||||
value = getValue(code, lang);
|
value = getValue(code, langs);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (value == null) {
|
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 code
|
||||||
* @param locale
|
* @param locale
|
||||||
* @param lang
|
* @param lang
|
||||||
|
@ -126,20 +157,24 @@ public class JsonMessageSource extends AbstractMessageSource {
|
||||||
* @param locale
|
* @param locale
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
private JsonObject getLanguageMap(Locale locale) {
|
private List<JsonObject> getLanguageMap(Locale locale) {
|
||||||
|
|
||||||
if (!languageMaps.containsKey(locale)) {
|
if (!languageMaps.containsKey(locale)) {
|
||||||
try {
|
try {
|
||||||
String filename = locale.getLanguage() + File.separator + "messages.json";
|
List<JsonObject> set = new ArrayList<>();
|
||||||
|
for (String namespace : config.getLanguageNamespaces()) {
|
||||||
Resource r = getBaseDirectory().createRelative(filename);
|
String filename = locale.getLanguage() + File.separator + namespace + ".json";
|
||||||
|
|
||||||
logger.info("No locale loaded, trying to load from " + r);
|
Resource r = getBaseDirectory().createRelative(filename);
|
||||||
|
|
||||||
JsonParser parser = new JsonParser();
|
logger.info("No locale loaded, trying to load from " + r);
|
||||||
JsonObject obj = (JsonObject) parser.parse(new InputStreamReader(r.getInputStream(), "UTF-8"));
|
|
||||||
|
JsonParser parser = new JsonParser();
|
||||||
languageMaps.put(locale, obj);
|
JsonObject obj = (JsonObject) parser.parse(new InputStreamReader(r.getInputStream(), "UTF-8"));
|
||||||
|
|
||||||
|
set.add(obj);
|
||||||
|
}
|
||||||
|
languageMaps.put(locale, set);
|
||||||
} catch (JsonIOException | JsonSyntaxException | IOException e) {
|
} catch (JsonIOException | JsonSyntaxException | IOException e) {
|
||||||
logger.error("Unable to load locale", e);
|
logger.error("Unable to load locale", e);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue