From ae82df3fb4355c3ee5abb87c2bac48f687724bbb Mon Sep 17 00:00:00 2001 From: Nanne Baars Date: Sun, 5 Feb 2017 21:54:07 +0100 Subject: [PATCH] Fixed issue with loading messages in different language. As a standalone jar you can write properties back to messages.properties, this approach worked when you run with exploded classpath (target/classes etc). However failed when running inside Docker container. --- .../org/owasp/webgoat/MvcConfiguration.java | 10 ++- .../main/java/org/owasp/webgoat/WebGoat.java | 5 +- .../assignments/AssignmentEndpoint.java | 4 +- .../webgoat/assignments/AttackResult.java | 8 +- .../java/org/owasp/webgoat/i18n/Messages.java | 3 + .../owasp/webgoat/i18n/PluginMessages.java | 80 +++++++++++++++++++ ...Merger.java => MessagePropertyMerger.java} | 52 ++++++------ .../webgoat/plugins/PluginExtractor.java | 4 + .../webgoat/plugins/PluginsExtractor.java | 7 +- .../owasp/webgoat/service/LabelService.java | 7 +- .../main/resources/templates/main_new.html | 2 +- .../assignments/AssignmentEndpointTest.java | 4 +- 12 files changed, 145 insertions(+), 41 deletions(-) create mode 100644 webgoat-container/src/main/java/org/owasp/webgoat/i18n/PluginMessages.java rename webgoat-container/src/main/java/org/owasp/webgoat/plugins/{MessagePropertiesMerger.java => MessagePropertyMerger.java} (53%) diff --git a/webgoat-container/src/main/java/org/owasp/webgoat/MvcConfiguration.java b/webgoat-container/src/main/java/org/owasp/webgoat/MvcConfiguration.java index 96940764a..13ff7139d 100644 --- a/webgoat-container/src/main/java/org/owasp/webgoat/MvcConfiguration.java +++ b/webgoat-container/src/main/java/org/owasp/webgoat/MvcConfiguration.java @@ -32,6 +32,7 @@ package org.owasp.webgoat; import com.google.common.collect.Sets; import org.owasp.webgoat.i18n.Messages; +import org.owasp.webgoat.i18n.PluginMessages; import org.owasp.webgoat.session.Course; import org.owasp.webgoat.session.LabelDebugger; import org.springframework.beans.factory.annotation.Autowired; @@ -117,8 +118,13 @@ public class MvcConfiguration extends WebMvcConfigurerAdapter { } @Bean - public Messages messageSource() { - Messages messages = new Messages(localeResolver()); + public PluginMessages pluginMessages(Messages messages) { + return new PluginMessages(messages); + } + + @Bean + public Messages messageSource(LocaleResolver localeResolver) { + Messages messages = new Messages(localeResolver); messages.setBasename("classpath:/i18n/messages"); return messages; } diff --git a/webgoat-container/src/main/java/org/owasp/webgoat/WebGoat.java b/webgoat-container/src/main/java/org/owasp/webgoat/WebGoat.java index f2ac1365c..588cf51a7 100644 --- a/webgoat-container/src/main/java/org/owasp/webgoat/WebGoat.java +++ b/webgoat-container/src/main/java/org/owasp/webgoat/WebGoat.java @@ -34,6 +34,7 @@ import com.fasterxml.jackson.annotation.JsonInclude; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; import org.apache.catalina.Context; +import org.owasp.webgoat.i18n.PluginMessages; import org.owasp.webgoat.plugins.PluginClassLoader; import org.owasp.webgoat.plugins.PluginEndpointPublisher; import org.owasp.webgoat.plugins.PluginsExtractor; @@ -91,8 +92,8 @@ public class WebGoat extends SpringBootServletInitializer { } @Bean - public PluginsExtractor pluginsLoader(@Qualifier("pluginTargetDirectory") File pluginTargetDirectory, PluginClassLoader classLoader) { - return new PluginsExtractor(pluginTargetDirectory, classLoader); + public PluginsExtractor pluginsLoader(@Qualifier("pluginTargetDirectory") File pluginTargetDirectory, PluginClassLoader classLoader, PluginMessages messages) { + return new PluginsExtractor(pluginTargetDirectory, classLoader, messages); } @Bean diff --git a/webgoat-container/src/main/java/org/owasp/webgoat/assignments/AssignmentEndpoint.java b/webgoat-container/src/main/java/org/owasp/webgoat/assignments/AssignmentEndpoint.java index 260fc3c6f..f44b1cfd0 100644 --- a/webgoat-container/src/main/java/org/owasp/webgoat/assignments/AssignmentEndpoint.java +++ b/webgoat-container/src/main/java/org/owasp/webgoat/assignments/AssignmentEndpoint.java @@ -25,7 +25,7 @@ package org.owasp.webgoat.assignments; import lombok.Getter; -import org.owasp.webgoat.i18n.Messages; +import org.owasp.webgoat.i18n.PluginMessages; import org.owasp.webgoat.session.UserSessionData; import org.owasp.webgoat.session.UserTracker; import org.owasp.webgoat.session.WebSession; @@ -50,7 +50,7 @@ public abstract class AssignmentEndpoint extends Endpoint { private UserSessionData userSessionData; @Getter @Autowired - private Messages messages; + private PluginMessages messages; //// TODO: 11/13/2016 events better fit? protected AttackResult trackProgress(AttackResult attackResult) { diff --git a/webgoat-container/src/main/java/org/owasp/webgoat/assignments/AttackResult.java b/webgoat-container/src/main/java/org/owasp/webgoat/assignments/AttackResult.java index 207575c8c..4cf1dbad8 100644 --- a/webgoat-container/src/main/java/org/owasp/webgoat/assignments/AttackResult.java +++ b/webgoat-container/src/main/java/org/owasp/webgoat/assignments/AttackResult.java @@ -27,7 +27,7 @@ package org.owasp.webgoat.assignments; import lombok.AllArgsConstructor; import lombok.Getter; -import org.owasp.webgoat.i18n.Messages; +import org.owasp.webgoat.i18n.PluginMessages; @AllArgsConstructor public class AttackResult { @@ -35,13 +35,13 @@ public class AttackResult { public static class AttackResultBuilder { private boolean lessonCompleted; - private Messages messages; + private PluginMessages messages; private Object[] feedbackArgs; private String feedbackResourceBundleKey; private String output; private Object[] outputArgs; - public AttackResultBuilder(Messages messages) { + public AttackResultBuilder(PluginMessages messages) { this.messages = messages; } @@ -84,7 +84,7 @@ public class AttackResult { private String output; - public static AttackResultBuilder builder(Messages messages) { + public static AttackResultBuilder builder(PluginMessages messages) { return new AttackResultBuilder(messages); } diff --git a/webgoat-container/src/main/java/org/owasp/webgoat/i18n/Messages.java b/webgoat-container/src/main/java/org/owasp/webgoat/i18n/Messages.java index d44fbedd8..527a69b93 100644 --- a/webgoat-container/src/main/java/org/owasp/webgoat/i18n/Messages.java +++ b/webgoat-container/src/main/java/org/owasp/webgoat/i18n/Messages.java @@ -46,6 +46,7 @@ public class Messages extends ReloadableResourceBundleMessageSource { /** * Gets all messages for presented Locale. + * * @return all messages */ public Properties getMessages() { @@ -64,4 +65,6 @@ public class Messages extends ReloadableResourceBundleMessageSource { return localeResolver.resolveLocale(((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest()); } + + } diff --git a/webgoat-container/src/main/java/org/owasp/webgoat/i18n/PluginMessages.java b/webgoat-container/src/main/java/org/owasp/webgoat/i18n/PluginMessages.java new file mode 100644 index 000000000..6db37dd0e --- /dev/null +++ b/webgoat-container/src/main/java/org/owasp/webgoat/i18n/PluginMessages.java @@ -0,0 +1,80 @@ +/* + * This file is part of WebGoat, an Open Web Application Security Project utility. For details, + * please see http://www.owasp.org/ + *

+ * Copyright (c) 2002 - 2017 Bruce Mayhew + *

+ * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + *

+ * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + *

+ * You should have received a copy of the GNU General Public License along with this program; if + * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + *

+ * Getting Source ============== + *

+ * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software + * projects. + *

+ */ + +package org.owasp.webgoat.i18n; + +import lombok.SneakyThrows; +import org.springframework.context.support.ReloadableResourceBundleMessageSource; +import org.springframework.core.io.Resource; +import org.springframework.core.io.ResourceLoader; +import org.springframework.core.io.UrlResource; + +import java.io.File; +import java.util.Properties; + +/** + * Message resource bundle for plugins. The files is created after startup during the init of the plugins so we + * need to load this file through a ResourceLoader instead of location on the classpath. + * + * @author nbaars + * @date 2/4/17 + */ +public class PluginMessages extends ReloadableResourceBundleMessageSource { + + private Messages messages; + + public PluginMessages(Messages messages) { + this.messages = messages; + this.setParentMessageSource(messages); + } + + public Properties getMessages() { + return getMergedProperties(messages.resolveLocale()).getProperties(); + } + + public String getMessage(String code, Object... args) { + return getMessage(code, args, messages.resolveLocale()); + } + + public String getMessage(String code, String defaultValue, Object... args) { + return super.getMessage(code, args, defaultValue, messages.resolveLocale()); + } + + public void addPluginMessageBundles(final File i18nPluginDirectory) { + this.setBasename("WebGoatLabels"); + this.setResourceLoader(new ResourceLoader() { + @Override + @SneakyThrows + public Resource getResource(String location) { + return new UrlResource(new File(i18nPluginDirectory, location).toURI()); + } + + @Override + public ClassLoader getClassLoader() { + return Thread.currentThread().getContextClassLoader(); + } + }); + } +} diff --git a/webgoat-container/src/main/java/org/owasp/webgoat/plugins/MessagePropertiesMerger.java b/webgoat-container/src/main/java/org/owasp/webgoat/plugins/MessagePropertyMerger.java similarity index 53% rename from webgoat-container/src/main/java/org/owasp/webgoat/plugins/MessagePropertiesMerger.java rename to webgoat-container/src/main/java/org/owasp/webgoat/plugins/MessagePropertyMerger.java index 7983614a8..04760d0bf 100644 --- a/webgoat-container/src/main/java/org/owasp/webgoat/plugins/MessagePropertiesMerger.java +++ b/webgoat-container/src/main/java/org/owasp/webgoat/plugins/MessagePropertyMerger.java @@ -24,49 +24,49 @@ */ package org.owasp.webgoat.plugins; -import com.google.common.primitives.Bytes; import lombok.SneakyThrows; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; -import java.nio.file.Files; -import java.nio.file.Path; +import java.io.InputStream; import java.util.Properties; -import java.util.stream.Stream; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; + +import static com.google.common.io.Files.createParentDirs; /** * Merges the main message.properties with the plugins WebGoatLabels */ -public class MessagePropertiesMerger { +public class MessagePropertyMerger { private final File targetDirectory; - public MessagePropertiesMerger(File targetDirectory) { + public MessagePropertyMerger(File targetDirectory) { this.targetDirectory = targetDirectory; } @SneakyThrows - public void mergeAllLanguage() { - try(Stream paths = Files.walk(new File(targetDirectory, "plugin/i18n/").toPath())) { - paths.filter(Files::isRegularFile).forEach(filePath -> merge(filePath)); + public void merge(ZipFile zipFile, ZipEntry zipEntry) { + Properties messageProperties = new Properties(); + try (InputStream zis = zipFile.getInputStream(zipEntry)) { + messageProperties.load(zis); + } + + Properties messagesFromHome = new Properties(); + File pluginMessageFiles = new File(targetDirectory, zipEntry.getName()); + if (pluginMessageFiles.exists()) { + try (FileInputStream fis = new FileInputStream(pluginMessageFiles)) { + messagesFromHome.load(fis); + } + } + + messageProperties.putAll(messagesFromHome); + + createParentDirs(pluginMessageFiles); + try (FileOutputStream fos = new FileOutputStream(pluginMessageFiles)) { + messageProperties.store(fos, "Plugin message properties"); } } - - @SneakyThrows - public void merge(Path propertyFile) { - Properties messageProperties = new Properties(); - String messagePropertyFileName = propertyFile.getFileName().toString().replace("WebGoatLabels", "messages"); - messageProperties.load(Thread.currentThread().getContextClassLoader().getResourceAsStream("i18n/" + messagePropertyFileName)); - preparePropertyFile(propertyFile); - messageProperties.load(new FileInputStream(propertyFile.toFile())); - messageProperties.store(new FileOutputStream(new File(Thread.currentThread().getContextClassLoader().getResource("i18n/" + messagePropertyFileName).toURI())), "WebGoat message properties"); - } - - @SneakyThrows - private void preparePropertyFile(Path propertyFile) { - byte[] lines = Files.readAllBytes(propertyFile); - lines = Bytes.concat(lines, System.lineSeparator().getBytes()); - Files.write(propertyFile, lines); - } } diff --git a/webgoat-container/src/main/java/org/owasp/webgoat/plugins/PluginExtractor.java b/webgoat-container/src/main/java/org/owasp/webgoat/plugins/PluginExtractor.java index fef9b13d6..084a3a87e 100644 --- a/webgoat-container/src/main/java/org/owasp/webgoat/plugins/PluginExtractor.java +++ b/webgoat-container/src/main/java/org/owasp/webgoat/plugins/PluginExtractor.java @@ -71,6 +71,9 @@ public class PluginExtractor { throws IOException { if (zipEntry.getName().endsWith(".properties")) { final File targetFile = new File(targetDirectory, zipEntry.getName()); + if ("WebGoatLabels.properties".equals(targetFile.getName())) { + new MessagePropertyMerger(targetDirectory).merge(zipFile, zipEntry); + } copyFile(zipFile, zipEntry, targetFile, true); return true; } @@ -99,6 +102,7 @@ public class PluginExtractor { return targetFile; } + /** *

Getter for the field classes.

* diff --git a/webgoat-container/src/main/java/org/owasp/webgoat/plugins/PluginsExtractor.java b/webgoat-container/src/main/java/org/owasp/webgoat/plugins/PluginsExtractor.java index 177ffd483..d18ed7c50 100644 --- a/webgoat-container/src/main/java/org/owasp/webgoat/plugins/PluginsExtractor.java +++ b/webgoat-container/src/main/java/org/owasp/webgoat/plugins/PluginsExtractor.java @@ -3,6 +3,7 @@ package org.owasp.webgoat.plugins; import com.google.common.collect.Lists; import lombok.extern.slf4j.Slf4j; import org.apache.commons.io.FileUtils; +import org.owasp.webgoat.i18n.PluginMessages; import org.springframework.util.ResourceUtils; import java.io.*; @@ -28,10 +29,12 @@ public class PluginsExtractor { private static final int BUFFER_SIZE = 32 * 1024; private final File pluginTargetDirectory; private final PluginClassLoader classLoader; + private final PluginMessages messages; - public PluginsExtractor(File pluginTargetDirectory, PluginClassLoader pluginClassLoader) { + public PluginsExtractor(File pluginTargetDirectory, PluginClassLoader pluginClassLoader, PluginMessages messages) { this.classLoader = pluginClassLoader; this.pluginTargetDirectory = pluginTargetDirectory; + this.messages = messages; } /** @@ -137,7 +140,7 @@ public class PluginsExtractor { plugin.getOriginationJar()); } } - new MessagePropertiesMerger(pluginTargetDirectory).mergeAllLanguage(); + messages.addPluginMessageBundles(new File(pluginTargetDirectory, "plugin/i18n")); return plugins; } finally { executorService.shutdown(); 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 index 81ecf0b97..e42904b6a 100644 --- a/webgoat-container/src/main/java/org/owasp/webgoat/service/LabelService.java +++ b/webgoat-container/src/main/java/org/owasp/webgoat/service/LabelService.java @@ -31,6 +31,7 @@ package org.owasp.webgoat.service; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.owasp.webgoat.i18n.Messages; +import org.owasp.webgoat.i18n.PluginMessages; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; @@ -60,6 +61,7 @@ public class LabelService { public static final String URL_LABELS_MVC = "/service/labels.mvc"; private LocaleResolver localeResolver; private Messages messages; + private PluginMessages pluginMessages; /** * We use Springs session locale resolver which also gives us the option to change the local later on. For @@ -82,6 +84,9 @@ public class LabelService { ((SessionLocaleResolver)localeResolver).setDefaultLocale(locale); log.debug("Language provided: {} leads to Locale: {}", lang, locale); } - return new ResponseEntity<>(messages.getMessages(), HttpStatus.OK); + Properties allProperties = new Properties(); + allProperties.putAll(messages.getMessages()); + allProperties.putAll(pluginMessages.getMessages()); + return new ResponseEntity<>(allProperties, HttpStatus.OK); } } diff --git a/webgoat-container/src/main/resources/templates/main_new.html b/webgoat-container/src/main/resources/templates/main_new.html index cb86fdbd3..7553ef8b2 100644 --- a/webgoat-container/src/main/resources/templates/main_new.html +++ b/webgoat-container/src/main/resources/templates/main_new.html @@ -94,7 +94,7 @@ - diff --git a/webgoat-container/src/test/java/org/owasp/webgoat/assignments/AssignmentEndpointTest.java b/webgoat-container/src/test/java/org/owasp/webgoat/assignments/AssignmentEndpointTest.java index 8c5ae41be..3b4885db1 100644 --- a/webgoat-container/src/test/java/org/owasp/webgoat/assignments/AssignmentEndpointTest.java +++ b/webgoat-container/src/test/java/org/owasp/webgoat/assignments/AssignmentEndpointTest.java @@ -27,6 +27,7 @@ package org.owasp.webgoat.assignments; import org.mockito.Mock; import org.owasp.webgoat.i18n.Messages; +import org.owasp.webgoat.i18n.PluginMessages; import org.owasp.webgoat.session.UserSessionData; import org.owasp.webgoat.session.UserTracker; import org.owasp.webgoat.session.WebSession; @@ -60,13 +61,14 @@ public class AssignmentEndpointTest { return Locale.ENGLISH; } }; + protected PluginMessages pluginMessages = new PluginMessages(messages); public void init(AssignmentEndpoint a) { messages.setBasenames("classpath:/i18n/messages", "classpath:/plugin/i18n/WebGoatLabels"); ReflectionTestUtils.setField(a, "userTracker", userTracker); ReflectionTestUtils.setField(a, "userSessionData", userSessionData); ReflectionTestUtils.setField(a, "webSession", webSession); - ReflectionTestUtils.setField(a, "messages", messages); + ReflectionTestUtils.setField(a, "messages", pluginMessages); } } \ No newline at end of file