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.

This commit is contained in:
Nanne Baars 2017-02-05 21:54:07 +01:00
parent d25700434e
commit ae82df3fb4
12 changed files with 145 additions and 41 deletions

View File

@ -32,6 +32,7 @@ package org.owasp.webgoat;
import com.google.common.collect.Sets; import com.google.common.collect.Sets;
import org.owasp.webgoat.i18n.Messages; import org.owasp.webgoat.i18n.Messages;
import org.owasp.webgoat.i18n.PluginMessages;
import org.owasp.webgoat.session.Course; import org.owasp.webgoat.session.Course;
import org.owasp.webgoat.session.LabelDebugger; import org.owasp.webgoat.session.LabelDebugger;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@ -117,8 +118,13 @@ public class MvcConfiguration extends WebMvcConfigurerAdapter {
} }
@Bean @Bean
public Messages messageSource() { public PluginMessages pluginMessages(Messages messages) {
Messages messages = new Messages(localeResolver()); return new PluginMessages(messages);
}
@Bean
public Messages messageSource(LocaleResolver localeResolver) {
Messages messages = new Messages(localeResolver);
messages.setBasename("classpath:/i18n/messages"); messages.setBasename("classpath:/i18n/messages");
return messages; return messages;
} }

View File

@ -34,6 +34,7 @@ import com.fasterxml.jackson.annotation.JsonInclude;
import lombok.SneakyThrows; import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.catalina.Context; import org.apache.catalina.Context;
import org.owasp.webgoat.i18n.PluginMessages;
import org.owasp.webgoat.plugins.PluginClassLoader; import org.owasp.webgoat.plugins.PluginClassLoader;
import org.owasp.webgoat.plugins.PluginEndpointPublisher; import org.owasp.webgoat.plugins.PluginEndpointPublisher;
import org.owasp.webgoat.plugins.PluginsExtractor; import org.owasp.webgoat.plugins.PluginsExtractor;
@ -91,8 +92,8 @@ public class WebGoat extends SpringBootServletInitializer {
} }
@Bean @Bean
public PluginsExtractor pluginsLoader(@Qualifier("pluginTargetDirectory") File pluginTargetDirectory, PluginClassLoader classLoader) { public PluginsExtractor pluginsLoader(@Qualifier("pluginTargetDirectory") File pluginTargetDirectory, PluginClassLoader classLoader, PluginMessages messages) {
return new PluginsExtractor(pluginTargetDirectory, classLoader); return new PluginsExtractor(pluginTargetDirectory, classLoader, messages);
} }
@Bean @Bean

View File

@ -25,7 +25,7 @@
package org.owasp.webgoat.assignments; package org.owasp.webgoat.assignments;
import lombok.Getter; 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.UserSessionData;
import org.owasp.webgoat.session.UserTracker; import org.owasp.webgoat.session.UserTracker;
import org.owasp.webgoat.session.WebSession; import org.owasp.webgoat.session.WebSession;
@ -50,7 +50,7 @@ public abstract class AssignmentEndpoint extends Endpoint {
private UserSessionData userSessionData; private UserSessionData userSessionData;
@Getter @Getter
@Autowired @Autowired
private Messages messages; private PluginMessages messages;
//// TODO: 11/13/2016 events better fit? //// TODO: 11/13/2016 events better fit?
protected AttackResult trackProgress(AttackResult attackResult) { protected AttackResult trackProgress(AttackResult attackResult) {

View File

@ -27,7 +27,7 @@ package org.owasp.webgoat.assignments;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Getter; import lombok.Getter;
import org.owasp.webgoat.i18n.Messages; import org.owasp.webgoat.i18n.PluginMessages;
@AllArgsConstructor @AllArgsConstructor
public class AttackResult { public class AttackResult {
@ -35,13 +35,13 @@ public class AttackResult {
public static class AttackResultBuilder { public static class AttackResultBuilder {
private boolean lessonCompleted; private boolean lessonCompleted;
private Messages messages; private PluginMessages messages;
private Object[] feedbackArgs; private Object[] feedbackArgs;
private String feedbackResourceBundleKey; private String feedbackResourceBundleKey;
private String output; private String output;
private Object[] outputArgs; private Object[] outputArgs;
public AttackResultBuilder(Messages messages) { public AttackResultBuilder(PluginMessages messages) {
this.messages = messages; this.messages = messages;
} }
@ -84,7 +84,7 @@ public class AttackResult {
private String output; private String output;
public static AttackResultBuilder builder(Messages messages) { public static AttackResultBuilder builder(PluginMessages messages) {
return new AttackResultBuilder(messages); return new AttackResultBuilder(messages);
} }

View File

@ -46,6 +46,7 @@ public class Messages extends ReloadableResourceBundleMessageSource {
/** /**
* Gets all messages for presented Locale. * Gets all messages for presented Locale.
*
* @return all messages * @return all messages
*/ */
public Properties getMessages() { public Properties getMessages() {
@ -64,4 +65,6 @@ public class Messages extends ReloadableResourceBundleMessageSource {
return localeResolver.resolveLocale(((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest()); return localeResolver.resolveLocale(((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest());
} }
} }

View File

@ -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/
* <p>
* Copyright (c) 2002 - 2017 Bruce Mayhew
* <p>
* 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.
* <p>
* 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.
* <p>
* 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.
* <p>
* Getting Source ==============
* <p>
* Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software
* projects.
* <p>
*/
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();
}
});
}
}

View File

@ -24,49 +24,49 @@
*/ */
package org.owasp.webgoat.plugins; package org.owasp.webgoat.plugins;
import com.google.common.primitives.Bytes;
import lombok.SneakyThrows; import lombok.SneakyThrows;
import java.io.File; import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.nio.file.Files; import java.io.InputStream;
import java.nio.file.Path;
import java.util.Properties; 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 * Merges the main message.properties with the plugins WebGoatLabels
*/ */
public class MessagePropertiesMerger { public class MessagePropertyMerger {
private final File targetDirectory; private final File targetDirectory;
public MessagePropertiesMerger(File targetDirectory) { public MessagePropertyMerger(File targetDirectory) {
this.targetDirectory = targetDirectory; this.targetDirectory = targetDirectory;
} }
@SneakyThrows @SneakyThrows
public void mergeAllLanguage() { public void merge(ZipFile zipFile, ZipEntry zipEntry) {
try(Stream<Path> paths = Files.walk(new File(targetDirectory, "plugin/i18n/").toPath())) {
paths.filter(Files::isRegularFile).forEach(filePath -> merge(filePath));
}
}
@SneakyThrows
public void merge(Path propertyFile) {
Properties messageProperties = new Properties(); Properties messageProperties = new Properties();
String messagePropertyFileName = propertyFile.getFileName().toString().replace("WebGoatLabels", "messages"); try (InputStream zis = zipFile.getInputStream(zipEntry)) {
messageProperties.load(Thread.currentThread().getContextClassLoader().getResourceAsStream("i18n/" + messagePropertyFileName)); messageProperties.load(zis);
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 Properties messagesFromHome = new Properties();
private void preparePropertyFile(Path propertyFile) { File pluginMessageFiles = new File(targetDirectory, zipEntry.getName());
byte[] lines = Files.readAllBytes(propertyFile); if (pluginMessageFiles.exists()) {
lines = Bytes.concat(lines, System.lineSeparator().getBytes()); try (FileInputStream fis = new FileInputStream(pluginMessageFiles)) {
Files.write(propertyFile, lines); messagesFromHome.load(fis);
}
}
messageProperties.putAll(messagesFromHome);
createParentDirs(pluginMessageFiles);
try (FileOutputStream fos = new FileOutputStream(pluginMessageFiles)) {
messageProperties.store(fos, "Plugin message properties");
}
} }
} }

View File

@ -71,6 +71,9 @@ public class PluginExtractor {
throws IOException { throws IOException {
if (zipEntry.getName().endsWith(".properties")) { if (zipEntry.getName().endsWith(".properties")) {
final File targetFile = new File(targetDirectory, zipEntry.getName()); 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); copyFile(zipFile, zipEntry, targetFile, true);
return true; return true;
} }
@ -99,6 +102,7 @@ public class PluginExtractor {
return targetFile; return targetFile;
} }
/** /**
* <p>Getter for the field <code>classes</code>.</p> * <p>Getter for the field <code>classes</code>.</p>
* *

View File

@ -3,6 +3,7 @@ package org.owasp.webgoat.plugins;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils; import org.apache.commons.io.FileUtils;
import org.owasp.webgoat.i18n.PluginMessages;
import org.springframework.util.ResourceUtils; import org.springframework.util.ResourceUtils;
import java.io.*; import java.io.*;
@ -28,10 +29,12 @@ public class PluginsExtractor {
private static final int BUFFER_SIZE = 32 * 1024; private static final int BUFFER_SIZE = 32 * 1024;
private final File pluginTargetDirectory; private final File pluginTargetDirectory;
private final PluginClassLoader classLoader; 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.classLoader = pluginClassLoader;
this.pluginTargetDirectory = pluginTargetDirectory; this.pluginTargetDirectory = pluginTargetDirectory;
this.messages = messages;
} }
/** /**
@ -137,7 +140,7 @@ public class PluginsExtractor {
plugin.getOriginationJar()); plugin.getOriginationJar());
} }
} }
new MessagePropertiesMerger(pluginTargetDirectory).mergeAllLanguage(); messages.addPluginMessageBundles(new File(pluginTargetDirectory, "plugin/i18n"));
return plugins; return plugins;
} finally { } finally {
executorService.shutdown(); executorService.shutdown();

View File

@ -31,6 +31,7 @@ package org.owasp.webgoat.service;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.owasp.webgoat.i18n.Messages; import org.owasp.webgoat.i18n.Messages;
import org.owasp.webgoat.i18n.PluginMessages;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
@ -60,6 +61,7 @@ public class LabelService {
public static final String URL_LABELS_MVC = "/service/labels.mvc"; public static final String URL_LABELS_MVC = "/service/labels.mvc";
private LocaleResolver localeResolver; private LocaleResolver localeResolver;
private Messages messages; 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 * 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); ((SessionLocaleResolver)localeResolver).setDefaultLocale(locale);
log.debug("Language provided: {} leads to Locale: {}", lang, 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);
} }
} }

View File

@ -94,7 +94,7 @@
<!--<i class="fa fa-users"></i>--> <!--<i class="fa fa-users"></i>-->
<!--</button>--> <!--</button>-->
</div> </div>
<button type="button" id="about-button" class="btn btn-default right_nav_button" title="#{about}" <button type="button" id="about-button" class="btn btn-default right_nav_button" th:title="#{about}"
data-toggle="modal" data-target="#about-modal"> data-toggle="modal" data-target="#about-modal">
<i class="fa fa-info"></i> <i class="fa fa-info"></i>
</button> </button>

View File

@ -27,6 +27,7 @@ package org.owasp.webgoat.assignments;
import org.mockito.Mock; import org.mockito.Mock;
import org.owasp.webgoat.i18n.Messages; import org.owasp.webgoat.i18n.Messages;
import org.owasp.webgoat.i18n.PluginMessages;
import org.owasp.webgoat.session.UserSessionData; import org.owasp.webgoat.session.UserSessionData;
import org.owasp.webgoat.session.UserTracker; import org.owasp.webgoat.session.UserTracker;
import org.owasp.webgoat.session.WebSession; import org.owasp.webgoat.session.WebSession;
@ -60,13 +61,14 @@ public class AssignmentEndpointTest {
return Locale.ENGLISH; return Locale.ENGLISH;
} }
}; };
protected PluginMessages pluginMessages = new PluginMessages(messages);
public void init(AssignmentEndpoint a) { public void init(AssignmentEndpoint a) {
messages.setBasenames("classpath:/i18n/messages", "classpath:/plugin/i18n/WebGoatLabels"); messages.setBasenames("classpath:/i18n/messages", "classpath:/plugin/i18n/WebGoatLabels");
ReflectionTestUtils.setField(a, "userTracker", userTracker); ReflectionTestUtils.setField(a, "userTracker", userTracker);
ReflectionTestUtils.setField(a, "userSessionData", userSessionData); ReflectionTestUtils.setField(a, "userSessionData", userSessionData);
ReflectionTestUtils.setField(a, "webSession", webSession); ReflectionTestUtils.setField(a, "webSession", webSession);
ReflectionTestUtils.setField(a, "messages", messages); ReflectionTestUtils.setField(a, "messages", pluginMessages);
} }
} }