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 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;
}

View File

@ -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

View File

@ -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) {

View File

@ -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);
}

View File

@ -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());
}
}

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;
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<Path> 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);
}
}

View File

@ -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;
}
/**
* <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 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();

View File

@ -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);
}
}

View File

@ -94,7 +94,7 @@
<!--<i class="fa fa-users"></i>-->
<!--</button>-->
</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">
<i class="fa fa-info"></i>
</button>

View File

@ -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);
}
}