Provide Server-side service to support UI localization #265

- Now also enabled for adoc
This commit is contained in:
Nanne Baars 2017-02-07 21:54:34 +01:00 committed by Nanne Baars
parent 0de569339c
commit 2d6235e4f0
9 changed files with 122 additions and 54 deletions

View File

@ -1,6 +1,6 @@
/** /**
************************************************************************************************* * ************************************************************************************************
* This file is part of WebGoat, an Open Web Application Security Project utility. For details, * This file is part of WebGoat, an Open Web Application Security Project utility. For details,
* please see http://www.owasp.org/ * please see http://www.owasp.org/
* <p> * <p>
@ -25,28 +25,26 @@
* <p> * <p>
* *
* @author WebGoat * @author WebGoat
* @since December 12, 2015
* @version $Id: $Id * @version $Id: $Id
* @since December 12, 2015
*/ */
package org.owasp.webgoat; package org.owasp.webgoat;
import com.google.common.collect.Maps; import com.google.common.collect.Maps;
import com.google.common.collect.Sets; import com.google.common.collect.Sets;
import org.asciidoctor.Asciidoctor; import org.asciidoctor.Asciidoctor;
import org.owasp.webgoat.i18n.Language;
import org.springframework.util.StringUtils;
import org.thymeleaf.TemplateProcessingParameters; import org.thymeleaf.TemplateProcessingParameters;
import org.thymeleaf.resourceresolver.IResourceResolver; import org.thymeleaf.resourceresolver.IResourceResolver;
import org.thymeleaf.templateresolver.TemplateResolver; import org.thymeleaf.templateresolver.TemplateResolver;
import java.io.ByteArrayInputStream; import java.io.*;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringWriter;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.Map; import java.util.Map;
import java.util.Optional; import java.util.Optional;
import java.util.function.Predicate;
import static org.asciidoctor.Asciidoctor.Factory.create; import static org.asciidoctor.Asciidoctor.Factory.create;
@ -62,9 +60,12 @@ public class AsciiDoctorTemplateResolver extends TemplateResolver {
private static final Asciidoctor asciidoctor = create(); private static final Asciidoctor asciidoctor = create();
private static final String PREFIX = "doc:"; private static final String PREFIX = "doc:";
private final File pluginTargetDirectory; private final File pluginTargetDirectory;
private final Language language;
public AsciiDoctorTemplateResolver(File pluginTargetDirectory) { public AsciiDoctorTemplateResolver(File pluginTargetDirectory, Language language) {
this.pluginTargetDirectory = pluginTargetDirectory; this.pluginTargetDirectory = pluginTargetDirectory;
this.language = language;
setResourceResolver(new AdocResourceResolver()); setResourceResolver(new AdocResourceResolver());
setResolvablePatterns(Sets.newHashSet(PREFIX + "*")); setResolvablePatterns(Sets.newHashSet(PREFIX + "*"));
} }
@ -80,7 +81,7 @@ public class AsciiDoctorTemplateResolver extends TemplateResolver {
@Override @Override
public InputStream getResourceAsStream(TemplateProcessingParameters params, String resourceName) { public InputStream getResourceAsStream(TemplateProcessingParameters params, String resourceName) {
try { try {
Optional<Path> adocFile = find(pluginTargetDirectory.toPath(), resourceName); Optional<Path> adocFile = resolveAdocFile(resourceName);
if (adocFile.isPresent()) { if (adocFile.isPresent()) {
try (FileReader reader = new FileReader(adocFile.get().toFile())) { try (FileReader reader = new FileReader(adocFile.get().toFile())) {
StringWriter writer = new StringWriter(); StringWriter writer = new StringWriter();
@ -93,6 +94,18 @@ public class AsciiDoctorTemplateResolver extends TemplateResolver {
//no html yet //no html yet
return new ByteArrayInputStream(new byte[0]); return new ByteArrayInputStream(new byte[0]);
} }
}
private Optional<Path> resolveAdocFile(String resourceName) throws IOException {
Optional<Path> path = Optional.empty();
if (language.getLocale() != null) {
path = find(pluginTargetDirectory.toPath(), resourceName, language.getLocale().toString());
}
if (!path.isPresent()) {
path = find(pluginTargetDirectory.toPath(), resourceName, null);
}
return path;
} }
private Map<String, Object> createAttributes() { private Map<String, Object> createAttributes() {
@ -106,10 +119,12 @@ public class AsciiDoctorTemplateResolver extends TemplateResolver {
return options; return options;
} }
private Optional<Path> find(Path path, String resourceName) throws IOException { private Optional<Path> find(Path path, String resourceName, String language) throws IOException {
Predicate<Path> languageFilter = p -> StringUtils.hasText(language) ? p.getParent().getFileName().toString().equals(language) : true;
return Files.walk(path) return Files.walk(path)
.filter(Files::isRegularFile) .filter(Files::isRegularFile)
.filter(p -> p.toString().endsWith(resourceName)).findFirst(); .filter(p -> p.toString().endsWith(resourceName))
.filter(languageFilter).findFirst();
} }
@Override @Override
@ -117,4 +132,5 @@ public class AsciiDoctorTemplateResolver extends TemplateResolver {
return "adocResourceResolver"; return "adocResourceResolver";
} }
} }
} }

View File

@ -31,6 +31,7 @@
package org.owasp.webgoat; package org.owasp.webgoat;
import com.google.common.collect.Sets; import com.google.common.collect.Sets;
import org.owasp.webgoat.i18n.Language;
import org.owasp.webgoat.i18n.Messages; import org.owasp.webgoat.i18n.Messages;
import org.owasp.webgoat.i18n.PluginMessages; import org.owasp.webgoat.i18n.PluginMessages;
import org.owasp.webgoat.session.Course; import org.owasp.webgoat.session.Course;
@ -89,8 +90,8 @@ public class MvcConfiguration extends WebMvcConfigurerAdapter {
} }
@Bean @Bean
public AsciiDoctorTemplateResolver asciiDoctorTemplateResolver() { public AsciiDoctorTemplateResolver asciiDoctorTemplateResolver(Language language) {
AsciiDoctorTemplateResolver resolver = new AsciiDoctorTemplateResolver(pluginTargetDirectory); AsciiDoctorTemplateResolver resolver = new AsciiDoctorTemplateResolver(pluginTargetDirectory, language);
resolver.setCacheable(true); resolver.setCacheable(true);
resolver.setOrder(3); resolver.setOrder(3);
return resolver; return resolver;
@ -118,13 +119,18 @@ public class MvcConfiguration extends WebMvcConfigurerAdapter {
} }
@Bean @Bean
public PluginMessages pluginMessages(Messages messages) { public PluginMessages pluginMessages(Messages messages, Language language) {
return new PluginMessages(messages); return new PluginMessages(messages, language);
} }
@Bean @Bean
public Messages messageSource(LocaleResolver localeResolver) { public Language language(LocaleResolver localeResolver) {
Messages messages = new Messages(localeResolver); return new Language(localeResolver);
}
@Bean
public Messages messageSource(Language language) {
Messages messages = new Messages(language);
messages.setBasename("classpath:/i18n/messages"); messages.setBasename("classpath:/i18n/messages");
return messages; return messages;
} }

View File

@ -0,0 +1,51 @@
/*
* 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.AllArgsConstructor;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.servlet.LocaleResolver;
import java.util.Locale;
/**
* Wrapper around the LocaleResolver from Spring so we do not need to bother with passing the HttpRequest object
* when asking for a Locale.
*
* @author nbaars
* @date 2/7/17
*/
@AllArgsConstructor
public class Language {
private final LocaleResolver localeResolver;
public Locale getLocale() {
return localeResolver.resolveLocale(((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest());
}
}

View File

@ -26,11 +26,7 @@ package org.owasp.webgoat.i18n;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import org.springframework.context.support.ReloadableResourceBundleMessageSource; import org.springframework.context.support.ReloadableResourceBundleMessageSource;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.servlet.LocaleResolver;
import java.util.Locale;
import java.util.Properties; import java.util.Properties;
/** /**
@ -42,7 +38,7 @@ import java.util.Properties;
@AllArgsConstructor @AllArgsConstructor
public class Messages extends ReloadableResourceBundleMessageSource { public class Messages extends ReloadableResourceBundleMessageSource {
private final LocaleResolver localeResolver; private final Language language;
/** /**
* Gets all messages for presented Locale. * Gets all messages for presented Locale.
@ -50,21 +46,14 @@ public class Messages extends ReloadableResourceBundleMessageSource {
* @return all messages * @return all messages
*/ */
public Properties getMessages() { public Properties getMessages() {
return getMergedProperties(resolveLocale()).getProperties(); return getMergedProperties(language.getLocale()).getProperties();
} }
public String getMessage(String code, Object... args) { public String getMessage(String code, Object... args) {
return getMessage(code, args, resolveLocale()); return getMessage(code, args, language.getLocale());
} }
public String getMessage(String code, String defaultValue, Object... args) { public String getMessage(String code, String defaultValue, Object... args) {
return super.getMessage(code, args, defaultValue, resolveLocale()); return super.getMessage(code, args, defaultValue, language.getLocale());
} }
protected Locale resolveLocale() {
return localeResolver.resolveLocale(((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest());
}
} }

View File

@ -43,23 +43,23 @@ import java.util.Properties;
*/ */
public class PluginMessages extends ReloadableResourceBundleMessageSource { public class PluginMessages extends ReloadableResourceBundleMessageSource {
private Messages messages; private Language language;
public PluginMessages(Messages messages) { public PluginMessages(Messages messages, Language language) {
this.messages = messages; this.language = language;
this.setParentMessageSource(messages); this.setParentMessageSource(messages);
} }
public Properties getMessages() { public Properties getMessages() {
return getMergedProperties(messages.resolveLocale()).getProperties(); return getMergedProperties(language.getLocale()).getProperties();
} }
public String getMessage(String code, Object... args) { public String getMessage(String code, Object... args) {
return getMessage(code, args, messages.resolveLocale()); return getMessage(code, args, language.getLocale());
} }
public String getMessage(String code, String defaultValue, Object... args) { public String getMessage(String code, String defaultValue, Object... args) {
return super.getMessage(code, args, defaultValue, messages.resolveLocale()); return super.getMessage(code, args, defaultValue, language.getLocale());
} }
public void addPluginMessageBundles(final File i18nPluginDirectory) { public void addPluginMessageBundles(final File i18nPluginDirectory) {

View File

@ -14,6 +14,7 @@ security.enable-csrf=false
spring.devtools.restart.enabled=false spring.devtools.restart.enabled=false
spring.resources.cache-period=0 spring.resources.cache-period=0
spring.thymeleaf.cache=false
webgoat.user.directory=${user.home}/.webgoat/ webgoat.user.directory=${user.home}/.webgoat/
webgoat.build.version=@project.version@ webgoat.build.version=@project.version@

View File

@ -26,16 +26,15 @@
package org.owasp.webgoat.assignments; package org.owasp.webgoat.assignments;
import org.mockito.Mock; import org.mockito.Mock;
import org.owasp.webgoat.i18n.Language;
import org.owasp.webgoat.i18n.Messages; import org.owasp.webgoat.i18n.Messages;
import org.owasp.webgoat.i18n.PluginMessages; 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;
import org.springframework.test.util.ReflectionTestUtils; import org.springframework.test.util.ReflectionTestUtils;
import org.springframework.web.servlet.LocaleResolver; import org.springframework.web.servlet.i18n.FixedLocaleResolver;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Locale; import java.util.Locale;
public class AssignmentEndpointTest { public class AssignmentEndpointTest {
@ -46,22 +45,14 @@ public class AssignmentEndpointTest {
protected WebSession webSession; protected WebSession webSession;
@Mock @Mock
protected UserSessionData userSessionData; protected UserSessionData userSessionData;
protected Messages messages = new Messages(new LocaleResolver() { private Language language = new Language(new FixedLocaleResolver()){
@Override @Override
public Locale resolveLocale(HttpServletRequest request) { public Locale getLocale() {
return Locale.ENGLISH;
}
@Override
public void setLocale(HttpServletRequest request, HttpServletResponse response, Locale locale) {
}}){
@Override
protected Locale resolveLocale() {
return Locale.ENGLISH; return Locale.ENGLISH;
} }
}; };
protected PluginMessages pluginMessages = new PluginMessages(messages); protected Messages messages = new Messages(language);
protected PluginMessages pluginMessages = new PluginMessages(messages, language);
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");

View File

@ -0,0 +1,8 @@
Voer je naam in the input field below and press "Go!" to submit. The server will accept the request, reverse the input and display it back to the user, illustrating the basics of handling an HTTP request.
The user should become familiar with the features of WebGoat by manipulating the above buttons to view hints, show the HTTP request parameters, the HTTP request cookies, and the Java source code. You may also try using OWASP ZAP Attack Proxy to see the HTTP data.
== Try It!
Enter your name in the input field below and press "Go!" to submit. The server will accept the request, reverse the input and display it back to the user, illustrating the basics of handling an HTTP request.

View File

@ -68,6 +68,12 @@
<version>4.1.3.RELEASE</version> <version>4.1.3.RELEASE</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<!-- Temporarily -->
<dependency>
<groupId>com.thoughtworks.xstream</groupId>
<artifactId>xstream</artifactId>
<version>1.4.7</version>
</dependency>
</dependencies> </dependencies>
<dependencyManagement> <dependencyManagement>
<dependencies> <dependencies>