Skip to content

Commit 2496dc1

Browse files
committed
allow language system to be loaded from multiple files. closes mitreid-connect#817 closes mitreid-connect#876
1 parent e255fc1 commit 2496dc1

File tree

5 files changed

+117
-27
lines changed

5 files changed

+117
-27
lines changed

openid-connect-common/src/main/java/org/mitre/openid/connect/config/ConfigurationPropertiesBean.java

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
*******************************************************************************/
1717
package org.mitre.openid.connect.config;
1818

19+
import java.util.List;
1920
import java.util.Locale;
2021

2122
import javax.annotation.PostConstruct;
@@ -25,6 +26,9 @@
2526
import org.springframework.beans.factory.BeanCreationException;
2627
import org.springframework.util.StringUtils;
2728

29+
import com.google.common.collect.Lists;
30+
import com.google.gson.Gson;
31+
2832

2933

3034
/**
@@ -55,6 +59,8 @@ public class ConfigurationPropertiesBean {
5559
private boolean forceHttps = false; // by default we just log a warning for HTTPS deployment
5660

5761
private Locale locale = Locale.ENGLISH; // we default to the english translation
62+
63+
private List<String> languageNamespaces = Lists.newArrayList("messages");
5864

5965
public boolean dualClient = false;
6066

@@ -67,7 +73,7 @@ public ConfigurationPropertiesBean() {
6773
* @throws HttpsUrlRequiredException
6874
*/
6975
@PostConstruct
70-
public void checkForHttps() {
76+
public void checkConfigConsistency() {
7177
if (!StringUtils.startsWithIgnoreCase(issuer, "https")) {
7278
if (this.forceHttps) {
7379
logger.error("Configured issuer url is not using https scheme. Server will be shut down!");
@@ -77,6 +83,10 @@ public void checkForHttps() {
7783
logger.warn("\n\n**\n** WARNING: Configured issuer url is not using https scheme.\n**\n\n");
7884
}
7985
}
86+
87+
if (languageNamespaces == null || languageNamespaces.isEmpty()) {
88+
logger.error("No configured language namespaces! Text rendering will fail!");
89+
}
8090
}
8191

8292
/**
@@ -171,7 +181,21 @@ public void setLocale(Locale locale) {
171181
this.locale = locale;
172182
}
173183

174-
/**
184+
/**
185+
* @return the languageNamespaces
186+
*/
187+
public List<String> getLanguageNamespaces() {
188+
return languageNamespaces;
189+
}
190+
191+
/**
192+
* @param languageNamespaces the languageNamespaces to set
193+
*/
194+
public void setLanguageNamespaces(List<String> languageNamespaces) {
195+
this.languageNamespaces = languageNamespaces;
196+
}
197+
198+
/**
175199
* @return true if dual client is configured, otherwise false
176200
*/
177201
public boolean isDualClient() {
@@ -184,4 +208,19 @@ public boolean isDualClient() {
184208
public void setDualClient(boolean dualClient) {
185209
this.dualClient = dualClient;
186210
}
211+
212+
/**
213+
* Get the list of namespaces as a JSON string
214+
* @return
215+
*/
216+
public String getLanguageNamespacesString() {
217+
return new Gson().toJson(getLanguageNamespaces());
218+
}
219+
220+
/**
221+
* Get the default namespace (first in the nonempty list)
222+
*/
223+
public String getDefaultLanguageNamespace() {
224+
return getLanguageNamespaces().get(0);
225+
}
187226
}

openid-connect-common/src/test/java/org/mitre/openid/connect/config/ConfigurationPropertiesBeanTest.java

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ public void testCheckForHttpsIssuerHttpDefaultFlag() {
6363
// leave as default, which is unset/false
6464
try {
6565
bean.setIssuer("http://localhost:8080/openid-connect-server/");
66-
bean.checkForHttps();
66+
bean.checkConfigConsistency();
6767
} catch (BeanCreationException e) {
6868
fail("Unexpected BeanCreationException for http issuer with default forceHttps, message:" + e.getMessage());
6969
}
@@ -77,7 +77,7 @@ public void testCheckForHttpsIssuerHttpFalseFlag() {
7777
try {
7878
bean.setIssuer("http://localhost:8080/openid-connect-server/");
7979
bean.setForceHttps(false);
80-
bean.checkForHttps();
80+
bean.checkConfigConsistency();
8181
} catch (BeanCreationException e) {
8282
fail("Unexpected BeanCreationException for http issuer with forceHttps=false, message:" + e.getMessage());
8383
}
@@ -90,7 +90,7 @@ public void testCheckForHttpsIssuerHttpTrueFlag() {
9090
// set to true
9191
bean.setIssuer("http://localhost:8080/openid-connect-server/");
9292
bean.setForceHttps(true);
93-
bean.checkForHttps();
93+
bean.checkConfigConsistency();
9494
}
9595

9696
@Test
@@ -100,7 +100,7 @@ public void testCheckForHttpsIssuerHttpsDefaultFlag() {
100100
// leave as default, which is unset/false
101101
try {
102102
bean.setIssuer("https://localhost:8080/openid-connect-server/");
103-
bean.checkForHttps();
103+
bean.checkConfigConsistency();
104104
} catch (BeanCreationException e) {
105105
fail("Unexpected BeanCreationException for https issuer with default forceHttps, message:" + e.getMessage());
106106
}
@@ -114,7 +114,7 @@ public void testCheckForHttpsIssuerHttpsFalseFlag() {
114114
try {
115115
bean.setIssuer("https://localhost:8080/openid-connect-server/");
116116
bean.setForceHttps(false);
117-
bean.checkForHttps();
117+
bean.checkConfigConsistency();
118118
} catch (BeanCreationException e) {
119119
fail("Unexpected BeanCreationException for https issuer with forceHttps=false, message:" + e.getMessage());
120120
}
@@ -128,7 +128,7 @@ public void testCheckForHttpsIssuerHttpsTrueFlag() {
128128
try {
129129
bean.setIssuer("https://localhost:8080/openid-connect-server/");
130130
bean.setForceHttps(true);
131-
bean.checkForHttps();
131+
bean.checkConfigConsistency();
132132
} catch (BeanCreationException e) {
133133
fail("Unexpected BeanCreationException for https issuer with forceHttps=true, message:" + e.getMessage());
134134
}

openid-connect-server-webapp/src/main/webapp/WEB-INF/server-config.xml

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,18 @@
4848

4949
<!-- This property sets the locale for server text -->
5050
<!-- <property name="locale" value="sv" /> -->
51-
51+
52+
<!-- This property sets the set of namespaces for language translation files. The default is "messages". These are checked in the order presented here. -->
53+
<!--
54+
<property name="languageNamespaces">
55+
<list>
56+
<value>foo</value>
57+
<value>bar</value>
58+
<value>messages</value>
59+
</list>
60+
</property>
61+
-->
62+
5263
<!-- This property indicates if a dynamically registered client supports dual flows, such as client_credentials
5364
at the same time with authorization_code or implicit -->
5465
<!-- <property name="dualClient" value="true" /> -->

openid-connect-server-webapp/src/main/webapp/WEB-INF/tags/header.tag

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,12 @@
4040
$.i18n.init({
4141
fallbackLng: "en",
4242
lng: "${config.locale}",
43-
resGetPath: "resources/js/locale/__lng__/messages.json"
43+
resGetPath: "resources/js/locale/__lng__/__ns__.json",
44+
ns: {
45+
namespaces: ${config.languageNamespacesString},
46+
defaultNs: '${config.defaultLanguageNamespace}'
47+
},
48+
fallbackNS: ${config.languageNamespacesString}
4449
});
4550
moment.locale("${config.locale}");
4651
// safely set the title of the application

openid-connect-server/src/main/java/org/mitre/openid/connect/config/JsonMessageSource.java

Lines changed: 52 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,16 @@
2121
import java.io.IOException;
2222
import java.io.InputStreamReader;
2323
import java.text.MessageFormat;
24+
import java.util.ArrayList;
2425
import java.util.HashMap;
2526
import java.util.Iterator;
27+
import java.util.List;
2628
import java.util.Locale;
2729
import java.util.Map;
2830

2931
import org.slf4j.Logger;
3032
import org.slf4j.LoggerFactory;
33+
import org.springframework.beans.factory.annotation.Autowired;
3134
import org.springframework.context.support.AbstractMessageSource;
3235
import org.springframework.core.io.Resource;
3336

@@ -50,19 +53,22 @@ public class JsonMessageSource extends AbstractMessageSource {
5053

5154
private Locale fallbackLocale = new Locale("en"); // US English is the fallback language
5255

53-
private Map<Locale, JsonObject> languageMaps = new HashMap<>();
54-
56+
private Map<Locale, List<JsonObject>> languageMaps = new HashMap<>();
57+
58+
@Autowired
59+
private ConfigurationPropertiesBean config;
60+
5561
@Override
5662
protected MessageFormat resolveCode(String code, Locale locale) {
5763

58-
JsonObject lang = getLanguageMap(locale);
64+
List<JsonObject> langs = getLanguageMap(locale);
5965

60-
String value = getValue(code, lang);
66+
String value = getValue(code, langs);
6167

6268
if (value == null) {
6369
// if we haven't found anything, try the default locale
64-
lang = getLanguageMap(fallbackLocale);
65-
value = getValue(code, lang);
70+
langs = getLanguageMap(fallbackLocale);
71+
value = getValue(code, langs);
6672
}
6773

6874
if (value == null) {
@@ -76,6 +82,31 @@ protected MessageFormat resolveCode(String code, Locale locale) {
7682
}
7783

7884
/**
85+
* Get a value from the set of maps, taking the first match in order
86+
* @param code
87+
* @param langs
88+
* @return
89+
*/
90+
private String getValue(String code, List<JsonObject> langs) {
91+
if (langs == null || langs.isEmpty()) {
92+
// no language maps, nothing to look up
93+
return null;
94+
}
95+
96+
for (JsonObject lang : langs) {
97+
String value = getValue(code, lang);
98+
if (value != null) {
99+
// short circuit out of here if we find a match, otherwise keep going through the list
100+
return value;
101+
}
102+
}
103+
104+
// if we didn't find anything return null
105+
return null;
106+
}
107+
108+
/**
109+
* Get a value from a single map
79110
* @param code
80111
* @param locale
81112
* @param lang
@@ -126,20 +157,24 @@ private String getValue(String code, JsonObject lang) {
126157
* @param locale
127158
* @return
128159
*/
129-
private JsonObject getLanguageMap(Locale locale) {
160+
private List<JsonObject> getLanguageMap(Locale locale) {
130161

131162
if (!languageMaps.containsKey(locale)) {
132163
try {
133-
String filename = locale.getLanguage() + File.separator + "messages.json";
134-
135-
Resource r = getBaseDirectory().createRelative(filename);
136-
137-
logger.info("No locale loaded, trying to load from " + r);
138-
139-
JsonParser parser = new JsonParser();
140-
JsonObject obj = (JsonObject) parser.parse(new InputStreamReader(r.getInputStream(), "UTF-8"));
141-
142-
languageMaps.put(locale, obj);
164+
List<JsonObject> set = new ArrayList<>();
165+
for (String namespace : config.getLanguageNamespaces()) {
166+
String filename = locale.getLanguage() + File.separator + namespace + ".json";
167+
168+
Resource r = getBaseDirectory().createRelative(filename);
169+
170+
logger.info("No locale loaded, trying to load from " + r);
171+
172+
JsonParser parser = new JsonParser();
173+
JsonObject obj = (JsonObject) parser.parse(new InputStreamReader(r.getInputStream(), "UTF-8"));
174+
175+
set.add(obj);
176+
}
177+
languageMaps.put(locale, set);
143178
} catch (JsonIOException | JsonSyntaxException | IOException e) {
144179
logger.error("Unable to load locale", e);
145180
}

0 commit comments

Comments
 (0)