From 6acd149e5fdea61e9f379e513b921de40530893f Mon Sep 17 00:00:00 2001 From: Mario Zupan Date: Wed, 23 Nov 2016 13:55:58 +0100 Subject: [PATCH] Issue #265: Created LabelService to support UI localization --- ...ReloadableResourceMessageBundleSource.java | 24 +++++++ .../org/owasp/webgoat/i18n/LabelProvider.java | 30 +++++++-- .../owasp/webgoat/service/LabelService.java | 63 +++++++++++++++++++ .../owasp/webgoat/util/LabelProviderTest.java | 14 +++++ 4 files changed, 125 insertions(+), 6 deletions(-) create mode 100644 webgoat-container/src/main/java/org/owasp/webgoat/i18n/ExposedReloadableResourceMessageBundleSource.java create mode 100644 webgoat-container/src/main/java/org/owasp/webgoat/service/LabelService.java diff --git a/webgoat-container/src/main/java/org/owasp/webgoat/i18n/ExposedReloadableResourceMessageBundleSource.java b/webgoat-container/src/main/java/org/owasp/webgoat/i18n/ExposedReloadableResourceMessageBundleSource.java new file mode 100644 index 000000000..bfa3d1ff8 --- /dev/null +++ b/webgoat-container/src/main/java/org/owasp/webgoat/i18n/ExposedReloadableResourceMessageBundleSource.java @@ -0,0 +1,24 @@ +package org.owasp.webgoat.i18n; + +import org.springframework.context.support.ReloadableResourceBundleMessageSource; + +import java.util.Locale; +import java.util.Properties; + +/** + *

ExposedReloadableResourceMessageBundleSource class.

+ * Extends the reloadable message source with a way to get all messages + * + * @author zupzup + */ + +public class ExposedReloadableResourceMessageBundleSource extends ReloadableResourceBundleMessageSource { + /** + * Gets all messages for presented Locale. + * @param locale user request's locale + * @return all messages + */ + public Properties getMessages(Locale locale) { + return getMergedProperties(locale).getProperties(); + } +} diff --git a/webgoat-container/src/main/java/org/owasp/webgoat/i18n/LabelProvider.java b/webgoat-container/src/main/java/org/owasp/webgoat/i18n/LabelProvider.java index c1fa62ae0..79057df49 100644 --- a/webgoat-container/src/main/java/org/owasp/webgoat/i18n/LabelProvider.java +++ b/webgoat-container/src/main/java/org/owasp/webgoat/i18n/LabelProvider.java @@ -9,9 +9,7 @@ import org.springframework.stereotype.Component; import java.net.MalformedURLException; import java.nio.file.Path; -import java.util.Arrays; -import java.util.List; -import java.util.Locale; +import java.util.*; /** @@ -52,8 +50,8 @@ public class LabelProvider { private static final List SUPPORTED = Arrays.asList(Locale.GERMAN, Locale.FRENCH, Locale.ENGLISH, Locale.forLanguageTag("ru")); - private final ReloadableResourceBundleMessageSource labels = new ReloadableResourceBundleMessageSource(); - private static final ReloadableResourceBundleMessageSource pluginLabels = new ReloadableResourceBundleMessageSource(); + private final ExposedReloadableResourceMessageBundleSource labels = new ExposedReloadableResourceMessageBundleSource(); + private static final ExposedReloadableResourceMessageBundleSource pluginLabels = new ExposedReloadableResourceMessageBundleSource(); /** *

Constructor for LabelProvider.

@@ -104,7 +102,27 @@ public class LabelProvider { } private Locale useLocaleOrFallbackToEnglish(Locale locale) { - return SUPPORTED.contains(locale) ? Locale.ENGLISH : locale; + return SUPPORTED.contains(locale) ? locale : Locale.ENGLISH; + } + + /** + *

getLabels.

+ * Returns a merged map of all the labels for a specified language or the + * default language, if the given language is not supported + * + * @param locale The Locale to get all the labels for + * @return A Map of all properties with their values + */ + public Map getLabels(Locale locale) { + Properties messages = labels.getMessages(locale); + messages.putAll(pluginLabels.getMessages(useLocaleOrFallbackToEnglish(locale))); + Map labelsMap = new HashMap<>(); + for (Map.Entry entry : messages.entrySet()) { + if (entry.getKey() != null && entry.getValue() != null) { + labelsMap.put(entry.getKey().toString(), entry.getValue().toString()); + } + } + return labelsMap; } } diff --git a/webgoat-container/src/main/java/org/owasp/webgoat/service/LabelService.java b/webgoat-container/src/main/java/org/owasp/webgoat/service/LabelService.java new file mode 100644 index 000000000..508d4813c --- /dev/null +++ b/webgoat-container/src/main/java/org/owasp/webgoat/service/LabelService.java @@ -0,0 +1,63 @@ +package org.owasp.webgoat.service; + +import org.owasp.webgoat.i18n.LabelProvider; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Controller; +import org.springframework.util.StringUtils; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseBody; + +import javax.servlet.http.HttpServletRequest; +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; + + +/** + *

LabelService class.

+ * + * @author zupzup + */ + +@Controller +public class LabelService { + + private static final String URL_LABELS_MVC = "/service/labels.mvc"; + + private static final Logger logger = LoggerFactory.getLogger(LabelService.class); + + @Autowired + private LabelProvider labelProvider; + + /** + * Fetches labels for given language + * If no language is provided, the language is determined from the request headers + * Otherwise, fall back to default language + * + * @param lang the language to fetch labels for (optional) + * @return a map of labels + * @throws Exception + */ + @RequestMapping(path = URL_LABELS_MVC, produces = MediaType.APPLICATION_JSON_VALUE) + public @ResponseBody + ResponseEntity> fetchLabels(@RequestParam(value = "lang", required = false) String lang, HttpServletRequest request) throws Exception { + Locale locale; + if (StringUtils.isEmpty(lang)) { + logger.debug("No language provided, determining from request headers"); + locale = request.getLocale(); + if (locale != null) { + logger.debug("Locale set to {}", locale); + } + } else { + locale = Locale.forLanguageTag(lang); + logger.debug("Language provided: {} leads to Locale: {}", lang, locale); + } + return new ResponseEntity<>(labelProvider.getLabels(locale), HttpStatus.OK); + } +} diff --git a/webgoat-container/src/test/java/org/owasp/webgoat/util/LabelProviderTest.java b/webgoat-container/src/test/java/org/owasp/webgoat/util/LabelProviderTest.java index 79f2b7f74..8b5874519 100644 --- a/webgoat-container/src/test/java/org/owasp/webgoat/util/LabelProviderTest.java +++ b/webgoat-container/src/test/java/org/owasp/webgoat/util/LabelProviderTest.java @@ -17,4 +17,18 @@ public class LabelProviderTest { "Congratulations. You have successfully completed this lesson.")); } + @Test + public void shouldFallBackToEnglishIfLanguageNotSupported() { + LabelProvider labelProvider = new LabelProvider(); + assertThat(labelProvider.get(Locale.CHINESE, "LessonCompleted"), CoreMatchers.equalTo( + "Congratulations. You have successfully completed this lesson.")); + } + + @Test + public void shouldUseProvidedLanguageIfSupported() { + LabelProvider labelProvider = new LabelProvider(); + assertThat(labelProvider.get(Locale.GERMAN, "RestartLesson"), CoreMatchers.equalTo( + "Lektion neu beginnen")); + } + } \ No newline at end of file