- Introduced user registration

- Now using Spring Boot for classloading, this way local development does not need to restart the complete server
- Fixed all kinds of dependencies on the names of the lessons necessary to keep in mind during the creation of a lesson.
- Simplied loading of resources, by adding resource mappings in MvcConfig.
- Refactored plugin loading, now only one class is left for loading the lessons.
This commit is contained in:
Nanne Baars
2017-02-25 12:15:07 +01:00
parent 9b86aaba05
commit 259fd19c1b
221 changed files with 1179 additions and 1083 deletions

View File

@ -34,25 +34,20 @@ import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import org.asciidoctor.Asciidoctor;
import org.owasp.webgoat.i18n.Language;
import org.springframework.util.StringUtils;
import org.thymeleaf.TemplateProcessingParameters;
import org.thymeleaf.resourceresolver.IResourceResolver;
import org.thymeleaf.templateresolver.TemplateResolver;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Map;
import java.util.Optional;
import java.util.function.Predicate;
import static org.asciidoctor.Asciidoctor.Factory.create;
/**
* Thymeleaf resolver for AsciiDoc used in the lesson, can be used as follows inside a lesson file:
*
* <p>
* <code>
* <div th:replace="doc:AccessControlMatrix_plan.adoc"></div>
* <div th:replace="doc:AccessControlMatrix_plan.adoc"></div>
* </code>
*/
public class AsciiDoctorTemplateResolver extends TemplateResolver {
@ -80,34 +75,26 @@ public class AsciiDoctorTemplateResolver extends TemplateResolver {
@Override
public InputStream getResourceAsStream(TemplateProcessingParameters params, String resourceName) {
InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream(computeResourceName(resourceName));
try {
Optional<Path> adocFile = resolveAdocFile(resourceName);
if (adocFile.isPresent()) {
try (FileReader reader = new FileReader(adocFile.get().toFile())) {
StringWriter writer = new StringWriter();
asciidoctor.convert(reader, writer, createAttributes());
return new ByteArrayInputStream(writer.getBuffer().toString().getBytes());
}
}
return new ByteArrayInputStream(new byte[0]);
StringWriter writer = new StringWriter();
asciidoctor.convert(new InputStreamReader(is), writer, createAttributes());
return new ByteArrayInputStream(writer.getBuffer().toString().getBytes());
} catch (IOException e) {
//no html yet
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;
/**
* The resource name is for example HttpBasics_content1.adoc. This is always located in the following directory:
* <code>plugin/HttpBasics/lessonPlans/en/HttpBasics_content1.adoc</code>
*/
private String computeResourceName(String resourceName) {
return String.format("lessonPlans/%s/%s", language.getLocale().getLanguage(), resourceName);
}
private Map<String, Object> createAttributes() {
Map<String, Object> attributes = Maps.newHashMap();
attributes.put("source-highlighter", "coderay");
@ -119,14 +106,6 @@ public class AsciiDoctorTemplateResolver extends TemplateResolver {
return options;
}
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)
.filter(Files::isRegularFile)
.filter(p -> p.toString().endsWith(resourceName))
.filter(languageFilter).findFirst();
}
@Override
public String getName() {
return "adocResourceResolver";

View File

@ -30,16 +30,19 @@
*/
package org.owasp.webgoat;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.google.common.io.Files;
import com.google.common.io.ByteStreams;
import lombok.SneakyThrows;
import org.springframework.core.io.ResourceLoader;
import org.thymeleaf.TemplateProcessingParameters;
import org.thymeleaf.resourceresolver.IResourceResolver;
import org.thymeleaf.templateresolver.TemplateResolver;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.Map;
/**
* Dynamically resolve a lesson. In the html file this can be invoked as:
@ -48,15 +51,18 @@ import java.io.InputStream;
* <div th:case="true" th:replace="lesson:__${lesson.class.simpleName}__"></div>
* </code>
*
* Thymeleaf will invoke this resolver based on the prefix and this implementqtion will resolve the html in the plugins directory
* Thymeleaf will invoke this resolver based on the prefix and this implementation will resolve the html in the plugins directory
*/
public class LessonTemplateResolver extends TemplateResolver {
private final static String PREFIX = "lesson:";
private final File pluginTargetDirectory;
private ResourceLoader resourceLoader;
private Map<String, byte[]> resources = Maps.newHashMap();
public LessonTemplateResolver(File pluginTargetDirectory) {
public LessonTemplateResolver(File pluginTargetDirectory, ResourceLoader resourceLoader) {
this.pluginTargetDirectory = pluginTargetDirectory;
this.resourceLoader = resourceLoader;
setResourceResolver(new LessonResourceResolver());
setResolvablePatterns(Sets.newHashSet(PREFIX + "*"));
}
@ -70,17 +76,14 @@ public class LessonTemplateResolver extends TemplateResolver {
private class LessonResourceResolver implements IResourceResolver {
@Override
@SneakyThrows
public InputStream getResourceAsStream(TemplateProcessingParameters params, String resourceName) {
File lesson = new File(pluginTargetDirectory, "/plugin/" + resourceName + "/html/" + resourceName + ".html");
if (lesson != null) {
try {
return new ByteArrayInputStream(Files.toByteArray(lesson));
} catch (IOException e) {
//no html yet
return new ByteArrayInputStream(new byte[0]);
}
byte[] resource = resources.get(resourceName);
if (resource == null) {
resource = ByteStreams.toByteArray(resourceLoader.getResource("classpath:/html/" + resourceName + ".html").getInputStream());
resources.put(resourceName, resource);
}
return null;
return new ByteArrayInputStream(resource);
}
@Override

View File

@ -1,32 +1,32 @@
/**
*************************************************************************************************
*
*
* ************************************************************************************************
* <p>
* <p>
* 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 - 20014 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.
*
* @author WebGoat
* @since October 28, 2003
* @version $Id: $Id
* @since October 28, 2003
*/
package org.owasp.webgoat;
@ -39,8 +39,11 @@ import org.owasp.webgoat.session.LabelDebugger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.ApplicationContext;
import org.springframework.context.MessageSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ResourceLoader;
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
import org.springframework.web.servlet.LocaleResolver;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
@ -70,6 +73,7 @@ public class MvcConfiguration extends WebMvcConfigurerAdapter {
registry.addViewController("/start.mvc").setViewName("main_new");
}
@Bean
public TemplateResolver springThymeleafTemplateResolver(ApplicationContext applicationContext) {
SpringResourceTemplateResolver resolver = new SpringResourceTemplateResolver();
@ -82,8 +86,8 @@ public class MvcConfiguration extends WebMvcConfigurerAdapter {
}
@Bean
public LessonTemplateResolver lessonTemplateResolver() {
LessonTemplateResolver resolver = new LessonTemplateResolver(pluginTargetDirectory);
public LessonTemplateResolver lessonTemplateResolver(ResourceLoader resourceLoader) {
LessonTemplateResolver resolver = new LessonTemplateResolver(pluginTargetDirectory, resourceLoader);
resolver.setOrder(2);
resolver.setCacheable(false);
return resolver;
@ -92,7 +96,7 @@ public class MvcConfiguration extends WebMvcConfigurerAdapter {
@Bean
public AsciiDoctorTemplateResolver asciiDoctorTemplateResolver(Language language) {
AsciiDoctorTemplateResolver resolver = new AsciiDoctorTemplateResolver(pluginTargetDirectory, language);
resolver.setCacheable(true);
resolver.setCacheable(false);
resolver.setOrder(3);
return resolver;
}
@ -116,11 +120,18 @@ public class MvcConfiguration extends WebMvcConfigurerAdapter {
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/plugin_lessons/**").addResourceLocations("file:///" + pluginTargetDirectory.toString() + "/");
//registry.addResourceHandler("/images/**").addResourceLocations("classpath:/plugin/VulnerableComponents/images/");
registry.addResourceHandler("/images/**").addResourceLocations("classpath:/images/");
registry.addResourceHandler("/lesson_js/**").addResourceLocations("classpath:/js/");
registry.addResourceHandler("/lesson_css/**").addResourceLocations("classpath:/css/");
super.addResourceHandlers(registry);
}
@Bean
public PluginMessages pluginMessages(Messages messages, Language language) {
return new PluginMessages(messages, language);
PluginMessages pluginMessages = new PluginMessages(messages, language);
pluginMessages.setBasenames("i18n/WebGoatLabels");
return pluginMessages;
}
@Bean
@ -131,7 +142,7 @@ public class MvcConfiguration extends WebMvcConfigurerAdapter {
@Bean
public Messages messageSource(Language language) {
Messages messages = new Messages(language);
messages.setBasename("classpath:/i18n/messages");
messages.setBasename("classpath:i18n/messages");
return messages;
}

View File

@ -34,13 +34,9 @@ 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;
import org.owasp.webgoat.plugins.PluginsLoader;
import org.owasp.webgoat.session.*;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@ -86,16 +82,6 @@ public class WebGoat extends SpringBootServletInitializer {
return new File(webgoatHome);
}
@Bean
public PluginClassLoader pluginClassLoader() {
return new PluginClassLoader(PluginClassLoader.class.getClassLoader());
}
@Bean
public PluginsExtractor pluginsLoader(@Qualifier("pluginTargetDirectory") File pluginTargetDirectory, PluginClassLoader classLoader, PluginMessages messages) {
return new PluginsExtractor(pluginTargetDirectory, classLoader, messages);
}
@Bean
@Scope(value = "session", proxyMode = ScopedProxyMode.TARGET_CLASS)
public WebSession webSession(WebgoatContext webgoatContext) {
@ -114,8 +100,8 @@ public class WebGoat extends SpringBootServletInitializer {
}
@Bean
public Course course(PluginsExtractor extractor, PluginEndpointPublisher pluginEndpointPublisher) {
return new PluginsLoader(extractor, pluginEndpointPublisher).loadPlugins();
public Course course(PluginEndpointPublisher pluginEndpointPublisher) {
return new PluginsLoader(pluginEndpointPublisher).loadPlugins();
}
@Bean

View File

@ -1,6 +1,6 @@
/**
*************************************************************************************************
* ************************************************************************************************
* This file is part of WebGoat, an Open Web Application Security Project utility. For details,
* please see http://www.owasp.org/
* <p>
@ -25,11 +25,13 @@
* <p>
*
* @author WebGoat
* @since December 12, 2015
* @version $Id: $Id
* @since December 12, 2015
*/
package org.owasp.webgoat;
import lombok.AllArgsConstructor;
import org.owasp.webgoat.users.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@ -45,16 +47,20 @@ import org.springframework.security.core.userdetails.UserDetailsService;
* Security configuration for WebGoat.
*/
@Configuration
@AllArgsConstructor
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
private final UserService userDetailsService;
@Override
protected void configure(HttpSecurity http) throws Exception {
ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry security = http
.authorizeRequests()
.antMatchers("/css/**", "/images/**", "/js/**", "fonts/**", "/plugins/**").permitAll()
.antMatchers("/css/**", "/images/**", "/js/**", "fonts/**", "/plugins/**", "/registration", "/register.mvc").permitAll()
.antMatchers("/servlet/AdminServlet/**").hasAnyRole("WEBGOAT_ADMIN", "SERVER_ADMIN") //
.antMatchers("/JavaSource/**").hasRole("SERVER_ADMIN") //
.anyRequest().hasAnyRole("WEBGOAT_USER", "WEBGOAT_ADMIN", "SERVER_ADMIN");
.anyRequest().authenticated();
security.and()
.formLogin()
.loginPage("/login")
@ -79,15 +85,12 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("guest").password("guest").roles("WEBGOAT_USER").and() //
.withUser("webgoat").password("webgoat").roles("WEBGOAT_ADMIN").and() //
.withUser("server").password("server").roles("SERVER_ADMIN");
auth.userDetailsService(userDetailsService); //.passwordEncoder(bCryptPasswordEncoder());
}
@Bean
@Override
public UserDetailsService userDetailsServiceBean() throws Exception {
return super.userDetailsServiceBean();
return userDetailsService;
}
}

View File

@ -25,35 +25,10 @@
package org.owasp.webgoat.assignments;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.actuate.endpoint.mvc.MvcEndpoint;
import java.io.File;
public abstract class Endpoint implements MvcEndpoint {
@Autowired
@Qualifier("pluginTargetDirectory")
private File pluginDirectory;
/**
* The directory of the plugin directory in which the lessons resides, so if you want to access the lesson 'ClientSideFiltering' you will
* need to:
*
* <code>
* File lessonDirectory = new File(getPluginDirectory(), "ClientSideFiltering");
* </code>
*
* The directory structure of the lesson is exactly the same as the directory structure in the plugins project.
*
* @return the top level
*/
protected File getPluginDirectory() {
return new File(this.pluginDirectory, "plugin");
}
@Override
public final boolean isSensitive() {
return false;

View File

@ -55,7 +55,7 @@ public class Welcome {
* @param request a {@link javax.servlet.http.HttpServletRequest} object.
* @return a {@link org.springframework.web.servlet.ModelAndView} object.
*/
@RequestMapping(path = "welcome.mvc", method = RequestMethod.GET)
@RequestMapping(path = {"welcome.mvc", "/"}, method = RequestMethod.GET)
public ModelAndView welcome(HttpServletRequest request) {
// set the welcome attribute

View File

@ -27,29 +27,45 @@ 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.net.URL;
import java.util.Enumeration;
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.
* Message resource bundle for plugins.
*
* @author nbaars
* @date 2/4/17
*/
public class PluginMessages extends ReloadableResourceBundleMessageSource {
private static final String PROPERTIES_SUFFIX = ".properties";
private Language language;
public PluginMessages(Messages messages, Language language) {
this.language = language;
this.setParentMessageSource(messages);
this.setBasename("WebGoatLabels");
}
@Override
@SneakyThrows
protected PropertiesHolder refreshProperties(String filename, PropertiesHolder propHolder) {
Properties properties = new Properties();
long lastModified = System.currentTimeMillis();
Enumeration<URL> resources = Thread.currentThread().getContextClassLoader().getResources(filename + PROPERTIES_SUFFIX);
while (resources.hasMoreElements()) {
URL resource = resources.nextElement();
String sourcePath = resource.toURI().toString().replace(PROPERTIES_SUFFIX, "");
PropertiesHolder holder = super.refreshProperties(sourcePath, propHolder);
properties.putAll(holder.getProperties());
}
return new PropertiesHolder(properties, lastModified);
}
public Properties getMessages() {
return getMergedProperties(language.getLocale()).getProperties();
}
@ -61,20 +77,4 @@ public class PluginMessages extends ReloadableResourceBundleMessageSource {
public String getMessage(String code, String defaultValue, Object... args) {
return super.getMessage(code, args, defaultValue, language.getLocale());
}
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

@ -1,72 +0,0 @@
/*
* 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.plugins;
import lombok.SneakyThrows;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.util.Properties;
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 MessagePropertyMerger {
private final File targetDirectory;
public MessagePropertyMerger(File targetDirectory) {
this.targetDirectory = targetDirectory;
}
@SneakyThrows
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");
}
}
}

View File

@ -1,132 +0,0 @@
package org.owasp.webgoat.plugins;
import com.google.common.base.Optional;
import com.google.common.collect.Lists;
import lombok.Getter;
import org.owasp.webgoat.assignments.AssignmentEndpoint;
import org.owasp.webgoat.assignments.AssignmentHints;
import org.owasp.webgoat.assignments.AssignmentPath;
import org.owasp.webgoat.assignments.Endpoint;
import org.owasp.webgoat.lessons.AbstractLesson;
import org.owasp.webgoat.lessons.Assignment;
import org.owasp.webgoat.lessons.NewLesson;
import org.springframework.util.StringUtils;
import java.io.File;
import java.nio.file.Path;
import java.util.List;
import static java.util.stream.Collectors.toList;
import static org.owasp.webgoat.plugins.PluginFileUtils.fileEndsWith;
/**
* <p>Plugin class.</p>
*
* @author dm
* @version $Id: $Id
*/
public class Plugin {
@Getter
private final String originationJar;
private PluginClassLoader classLoader;
private Class<NewLesson> newLesson;
@Getter
private List<Class<AssignmentEndpoint>> assignments = Lists.newArrayList();
@Getter
private List<Class<Endpoint>> endpoints = Lists.newArrayList();
private List<File> pluginFiles = Lists.newArrayList();
public Plugin(PluginClassLoader classLoader, String originatingJar) {
this.classLoader = classLoader;
this.originationJar = originatingJar;
}
/**
* <p>findLesson.</p>
*
* @param classes a {@link java.util.List} object.
*/
public void findLesson(List<String> classes) {
for (String clazzName : classes) {
findLesson(clazzName);
}
}
private void findLesson(String name) {
String realClassName = StringUtils.trimLeadingCharacter(name, '/').replaceAll("/", ".").replaceAll(".class", "");
try {
Class clazz = classLoader.loadClass(realClassName);
if (NewLesson.class.isAssignableFrom(clazz)) {
this.newLesson = clazz;
}
} catch (ClassNotFoundException ce) {
throw new PluginLoadingFailure("Class " + realClassName + " listed in jar but unable to load the class.", ce);
}
}
public void findEndpoints(List<String> classes) {
for (String clazzName : classes) {
String realClassName = StringUtils.trimLeadingCharacter(clazzName, '/').replaceAll("/", ".").replaceAll(".class", "");
try {
Class clazz = classLoader.loadClass(realClassName);
if (AssignmentEndpoint.class.isAssignableFrom(clazz)) {
this.assignments.add(clazz);
} else
if (Endpoint.class.isAssignableFrom(clazz)) {
this.endpoints.add(clazz);
}
} catch (ClassNotFoundException ce) {
throw new PluginLoadingFailure("Class " + realClassName + " listed in jar but unable to load the class.", ce);
}
}
}
/**
* <p>loadFiles.</p>
*
* @param file a {@link java.nio.file.Path} object.
*/
public void loadFiles(Path file) {
if (fileEndsWith(file, ".css", ".jsp", ".js")) {
pluginFiles.add(file.toFile());
}
}
/**
* Lesson is optional, it is also possible that the supplied jar contains only helper classes.
*
* @return a {@link com.google.common.base.Optional} object.
*/
public Optional<AbstractLesson> getLesson() {
try {
if (newLesson != null) {
AbstractLesson lesson = newLesson.newInstance();
lesson.setAssignments(createAssignment(assignments));
return Optional.of(lesson);
}
} catch (IllegalAccessException | InstantiationException e) {
throw new PluginLoadingFailure("Unable to instantiate the lesson " + newLesson.getName(), e);
}
return Optional.absent();
}
private List<Assignment> createAssignment(List<Class<AssignmentEndpoint>> endpoints) {
return endpoints.stream().map(e -> new Assignment(e.getSimpleName(), getPath(e), getHints(e))).collect(toList());
}
private String getPath(Class<AssignmentEndpoint> e) {
return e.getAnnotationsByType(AssignmentPath.class)[0].value();
}
private List<String> getHints(Class<AssignmentEndpoint> e) {
if (e.isAnnotationPresent(AssignmentHints.class)) {
return Lists.newArrayList(e.getAnnotationsByType(AssignmentHints.class)[0].value());
}
return Lists.newArrayList();
}
}

View File

@ -1,16 +0,0 @@
package org.owasp.webgoat.plugins;
import java.net.URL;
import java.net.URLClassLoader;
public class PluginClassLoader extends URLClassLoader {
public PluginClassLoader(ClassLoader parent) {
super(new URL[] {}, parent);
}
@Override
public void addURL(URL url) {
super.addURL(url);
}
}

View File

@ -1,6 +1,7 @@
package org.owasp.webgoat.plugins;
import lombok.extern.slf4j.Slf4j;
import org.owasp.webgoat.assignments.Endpoint;
import org.springframework.beans.factory.annotation.Autowire;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
@ -9,6 +10,8 @@ import org.springframework.boot.actuate.endpoint.mvc.MvcEndpoint;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.AbstractApplicationContext;
import java.util.List;
/**
* ************************************************************************************************
* This file is part of WebGoat, an Open Web Application Security Project utility. For details,
@ -47,9 +50,8 @@ public class PluginEndpointPublisher {
this.applicationContext = (AbstractApplicationContext) applicationContext;
}
public void publish(Plugin plugin) {
plugin.getAssignments().forEach(e -> publishEndpoint(e));
plugin.getEndpoints().forEach(e -> publishEndpoint(e));
public void publish(List<Class<Endpoint>> endpoints) {
endpoints.forEach(e -> publishEndpoint(e));
}
private void publishEndpoint(Class<? extends MvcEndpoint> e) {

View File

@ -1,123 +0,0 @@
package org.owasp.webgoat.plugins;
import com.google.common.collect.Lists;
import com.google.common.io.Files;
import org.apache.commons.fileupload.util.Streams;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
/**
* Extract the jar file and place them in the system temp directory in the folder webgoat and collect the files
* and classes.
*
* @author dm
* @version $Id: $Id
*/
public class PluginExtractor {
private final List<String> classes = Lists.newArrayList();
private final List<Path> files = new ArrayList<>();
/**
* <p>extractJarFile.</p>
*
* @param archive a {@link java.io.File} object.
* @param targetDirectory a {@link java.io.File} object.
* @return a {@link org.owasp.webgoat.plugins.Plugin} object.
* @throws java.io.IOException if any.
*/
public Plugin extractJarFile(final File archive, final File targetDirectory, PluginClassLoader cl) throws IOException {
ZipFile zipFile = new ZipFile(archive);
Plugin plugin = new Plugin(cl, zipFile.getName());
try {
Enumeration<? extends ZipEntry> entries = zipFile.entries();
while (entries.hasMoreElements()) {
final ZipEntry zipEntry = entries.nextElement();
if (shouldProcessFile(zipEntry)) {
boolean processed = processClassFile(zipFile, zipEntry, targetDirectory);
if (!processed) {
processed = processPropertyFile(zipFile, zipEntry, targetDirectory);
}
if (!processed) {
processFile(plugin, zipFile, zipEntry, targetDirectory);
}
}
}
} finally {
plugin.findLesson(this.classes);
plugin.findEndpoints(this.classes);
zipFile.close();
}
return plugin;
}
private void processFile(Plugin plugin, ZipFile zipFile, ZipEntry zipEntry, File targetDirectory)
throws IOException {
final File targetFile = new File(targetDirectory, zipEntry.getName());
copyFile(zipFile, zipEntry, targetFile, false);
plugin.loadFiles(targetFile.toPath());
}
private boolean processPropertyFile(ZipFile zipFile, ZipEntry zipEntry, File targetDirectory)
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;
}
return false;
}
private boolean processClassFile(ZipFile zipFile, ZipEntry zipEntry, File targetDirectory) throws IOException {
if (zipEntry.getName().endsWith(".class")) {
classes.add(zipEntry.getName());
final File targetFile = new File(targetDirectory, zipEntry.getName());
copyFile(zipFile, zipEntry, targetFile, false);
return true;
}
return false;
}
private boolean shouldProcessFile(ZipEntry zipEntry) {
return !zipEntry.isDirectory() && !zipEntry.getName().startsWith("META-INF");
}
private File copyFile(ZipFile zipFile, ZipEntry zipEntry, File targetFile, boolean append) throws IOException {
Files.createParentDirs(targetFile);
try (FileOutputStream fos = new FileOutputStream(targetFile, append)) {
Streams.copy(zipFile.getInputStream(zipEntry), fos, true);
}
return targetFile;
}
/**
* <p>Getter for the field <code>classes</code>.</p>
*
* @return a {@link java.util.List} object.
*/
public List<String> getClasses() {
return this.classes;
}
/**
* <p>Getter for the field <code>files</code>.</p>
*
* @return a {@link java.util.List} object.
*/
public List<Path> getFiles() {
return this.files;
}
}

View File

@ -1,107 +0,0 @@
package org.owasp.webgoat.plugins;
import com.google.common.base.Preconditions;
import lombok.experimental.UtilityClass;
import org.apache.commons.io.IOUtils;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Collection;
/**
* <p>PluginFileUtils class.</p>
*
* @version $Id: $Id
* @author dm
*/
@UtilityClass
public class PluginFileUtils {
/**
* <p>fileEndsWith.</p>
*
* @param p a {@link java.nio.file.Path} object.
* @param s a {@link java.lang.String} object.
* @return a boolean.
*/
public static boolean fileEndsWith(Path p, String s) {
return p.getFileName().toString().endsWith(s);
}
/**
* <p>fileEndsWith.</p>
*
* @param p a {@link java.nio.file.Path} object.
* @param suffixes a {@link java.lang.String} object.
* @return a boolean.
*/
public static boolean fileEndsWith(Path p, String... suffixes) {
for (String suffix : suffixes) {
if (fileEndsWith(p, suffix)) {
return true;
}
}
return false;
}
/**
* <p>hasParentDirectoryWithName.</p>
*
* @param p a {@link java.nio.file.Path} object.
* @param s a {@link java.lang.String} object.
* @return a boolean.
*/
public static boolean hasParentDirectoryWithName(Path p, String s) {
if (p == null || p.getParent() == null || p.getParent().equals(p.getRoot())) {
return false;
}
if (p.getParent().getFileName().toString().equals(s)) {
return true;
}
return hasParentDirectoryWithName(p.getParent(), s);
}
/**
* <p>replaceInFiles.</p>
*
* @param replace a {@link java.lang.String} object.
* @param with a {@link java.lang.String} object.
* @param files a {@link java.util.Collection} object.
* @throws java.io.IOException if any.
*/
public static void replaceInFiles(String replace, String with, Collection<File> files) throws IOException {
Preconditions.checkNotNull(replace);
Preconditions.checkNotNull(with);
Preconditions.checkNotNull(files);
for (File file : files) {
replaceInFile(replace, with, file);
}
}
/**
* <p>replaceInFile.</p>
*
* @param replace a {@link java.lang.String} object.
* @param with a {@link java.lang.String} object.
* @param file a {@link java.nio.file.Path} object.
* @throws java.io.IOException if any.
*/
public static void replaceInFile(String replace, String with, File file) throws IOException {
Preconditions.checkNotNull(replace);
Preconditions.checkNotNull(with);
Preconditions.checkNotNull(file);
String fileAsString = "";
try (FileInputStream fis = new FileInputStream(file);) {
fileAsString = IOUtils.toString(fis, StandardCharsets.UTF_8.name());
fileAsString = fileAsString.replaceAll(replace, with);
}
Files.write(file.toPath(), fileAsString.getBytes());
}
}

View File

@ -0,0 +1,46 @@
package org.owasp.webgoat.plugins;
import lombok.AllArgsConstructor;
import lombok.Getter;
import org.owasp.webgoat.assignments.AssignmentEndpoint;
import org.owasp.webgoat.assignments.Endpoint;
import org.owasp.webgoat.lessons.NewLesson;
import java.net.URL;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
/**
* Plugin resource
*
* @author nbaars
* @since 3/4/17.
*/
@AllArgsConstructor
@Getter
public class PluginResource {
private final URL location;
private final List<Class> classes;
public Optional<Class> getLesson() {
return classes.stream().filter(c -> c.getSuperclass() == NewLesson.class).findFirst();
}
public List<Class<Endpoint>> getEndpoints() {
return classes.stream().
filter(c -> c.getSuperclass() == AssignmentEndpoint.class || c.getSuperclass() == Endpoint.class).
map(c -> (Class<Endpoint>)c).
collect(Collectors.toList());
}
public List<Class<AssignmentEndpoint>> getAssignments() {
return classes.stream().
filter(c -> c.getSuperclass() == AssignmentEndpoint.class).
map(c -> (Class<AssignmentEndpoint>)c).
collect(Collectors.toList());
}
}

View File

@ -1,162 +0,0 @@
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.*;
import java.net.URL;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.Enumeration;
import java.util.List;
import java.util.concurrent.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
/**
* <p>PluginsLoader class.</p>
*
* @author dm
* @version $Id: $Id
*/
@Slf4j
public class PluginsExtractor {
private static final String WEBGOAT_PLUGIN_EXTENSION = "jar";
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, PluginMessages messages) {
this.classLoader = pluginClassLoader;
this.pluginTargetDirectory = pluginTargetDirectory;
this.messages = messages;
}
/**
* <p>loadPlugins.</p>
*
* @return a {@link java.util.List} object.
*/
public List<Plugin> loadPlugins() {
List<Plugin> plugins = Lists.newArrayList();
try {
URL location = this.getClass().getProtectionDomain().getCodeSource().getLocation();
log.trace("Determining whether we run as standalone jar or as directory...");
if (ResourceUtils.isFileURL(location)) {
log.trace("Running from directory, copying lessons from {}", location.toString());
extractToTargetDirectoryFromExplodedDirectory(ResourceUtils.getFile(location));
} else {
log.trace("Running from standalone jar, extracting lessons from {}", location.toString());
extractToTargetDirectoryFromJarFile(ResourceUtils.getFile(ResourceUtils.extractJarFileURL(location)));
}
List<URL> jars = listJars();
plugins = processPlugins(jars);
} catch (Exception e) {
log.error("Loading plugins failed", e);
}
return plugins;
}
private void extractToTargetDirectoryFromJarFile(File jarFile) throws IOException {
ZipFile jar = new ZipFile(jarFile);
Enumeration<? extends ZipEntry> entries = jar.entries();
while (entries.hasMoreElements()) {
ZipEntry zipEntry = entries.nextElement();
if (zipEntry.getName().contains("plugin_lessons") && zipEntry.getName().endsWith(".jar")) {
unpack(jar, zipEntry);
}
}
}
private void unpack(ZipFile jar, ZipEntry zipEntry) throws IOException {
try (InputStream inputStream = jar.getInputStream(zipEntry)) {
String name = zipEntry.getName();
if (name.lastIndexOf("/") != -1) {
name = name.substring(name.lastIndexOf("/") + 1);
}
try (OutputStream outputStream = new FileOutputStream(new File(pluginTargetDirectory, name))) {
byte[] buffer = new byte[BUFFER_SIZE];
int bytesRead = -1;
while ((bytesRead = inputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, bytesRead);
}
outputStream.flush();
}
}
log.trace("Extracting {} to {}", jar.getName(), pluginTargetDirectory);
}
private void extractToTargetDirectoryFromExplodedDirectory(File directory) throws IOException {
Files.walkFileTree(directory.toPath(), new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
if (dir.endsWith("plugin_lessons")) {
log.trace("Copying {} to {}", dir.toString(), pluginTargetDirectory);
FileUtils.copyDirectory(dir.toFile(), pluginTargetDirectory);
}
return FileVisitResult.CONTINUE;
}
});
}
private List<URL> listJars() throws Exception {
final List<URL> jars = Lists.newArrayList();
Files.walkFileTree(Paths.get(pluginTargetDirectory.toURI()), new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
if (PluginFileUtils.fileEndsWith(file, WEBGOAT_PLUGIN_EXTENSION)) {
jars.add(file.toUri().toURL());
log.trace("Found jar file at location: {}", file.toString());
}
return FileVisitResult.CONTINUE;
}
});
return jars;
}
private List<Plugin> processPlugins(List<URL> jars) throws Exception {
final ExecutorService executorService = Executors.newFixedThreadPool(10);
try {
final List<Plugin> plugins = Lists.newArrayList();
final CompletionService<Plugin> completionService = new ExecutorCompletionService<>(executorService);
final List<Callable<Plugin>> callables = extractJars(jars);
callables.forEach(s -> completionService.submit(s));
int n = callables.size();
for (int i = 0; i < n; i++) {
Plugin plugin = completionService.take().get();
if (plugin.getLesson().isPresent()) {
log.trace("Plugin jar '{}' contains a lesson, loading into WebGoat...", plugin.getOriginationJar());
plugins.add(plugin);
} else {
log.trace("Plugin jar: '{}' does not contain a lesson not processing as a plugin (can be a utility jar)",
plugin.getOriginationJar());
}
}
messages.addPluginMessageBundles(new File(pluginTargetDirectory, "plugin/i18n"));
return plugins;
} finally {
executorService.shutdown();
}
}
private List<Callable<Plugin>> extractJars(List<URL> jars) {
List<Callable<Plugin>> extractorCallables = Lists.newArrayList();
for (final URL jar : jars) {
classLoader.addURL(jar);
extractorCallables.add(() -> {
PluginExtractor extractor = new PluginExtractor();
return extractor.extractJarFile(ResourceUtils.getFile(jar), pluginTargetDirectory, classLoader);
});
}
return extractorCallables;
}
}

View File

@ -1,13 +1,29 @@
package org.owasp.webgoat.plugins;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import lombok.AllArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.owasp.webgoat.assignments.AssignmentEndpoint;
import org.owasp.webgoat.assignments.AssignmentHints;
import org.owasp.webgoat.assignments.AssignmentPath;
import org.owasp.webgoat.lessons.AbstractLesson;
import org.owasp.webgoat.lessons.Assignment;
import org.owasp.webgoat.lessons.NewLesson;
import org.owasp.webgoat.session.Course;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
import org.springframework.core.type.filter.RegexPatternTypeFilter;
import java.net.URL;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import static java.util.stream.Collectors.toList;
/**
* ************************************************************************************************
@ -42,7 +58,6 @@ import java.util.List;
@Slf4j
public class PluginsLoader {
private final PluginsExtractor extractor;
private final PluginEndpointPublisher pluginEndpointPublisher;
/**
@ -50,11 +65,15 @@ public class PluginsLoader {
*/
public Course loadPlugins() {
List<AbstractLesson> lessons = Lists.newArrayList();
for (Plugin plugin : extractor.loadPlugins()) {
for (PluginResource plugin : findPluginResources()) {
try {
NewLesson lesson = (NewLesson) plugin.getLesson().get();
Class lessonClazz = plugin.getLesson()
.orElseThrow(() -> new PluginLoadingFailure("Plugin resource does not contain lesson"));
NewLesson lesson = (NewLesson) lessonClazz.newInstance();
List<Class<AssignmentEndpoint>> assignments = plugin.getAssignments();
lesson.setAssignments(createAssignment(assignments));
lessons.add(lesson);
pluginEndpointPublisher.publish(plugin);
pluginEndpointPublisher.publish(plugin.getEndpoints());
} catch (Exception e) {
log.error("Error in loadLessons: ", e);
}
@ -67,4 +86,43 @@ public class PluginsLoader {
return new Course(lessons);
}
private List<Assignment> createAssignment(List<Class<AssignmentEndpoint>> endpoints) {
return endpoints.stream().map(e -> new Assignment(e.getSimpleName(), getPath(e), getHints(e))).collect(toList());
}
private String getPath(Class<AssignmentEndpoint> e) {
return e.getAnnotationsByType(AssignmentPath.class)[0].value();
}
private List<String> getHints(Class<AssignmentEndpoint> e) {
if (e.isAnnotationPresent(AssignmentHints.class)) {
return Lists.newArrayList(e.getAnnotationsByType(AssignmentHints.class)[0].value());
}
return Lists.newArrayList();
}
@SneakyThrows
public List<PluginResource> findPluginResources() {
final ClassPathScanningCandidateComponentProvider provider = new ClassPathScanningCandidateComponentProvider(false);
provider.addIncludeFilter(new RegexPatternTypeFilter(Pattern.compile(".*")));
final Set<BeanDefinition> classes = provider.findCandidateComponents("org.owasp.webgoat.plugin");
Map<URL, List<Class>> pluginClasses = Maps.newHashMap();
for (BeanDefinition bean : classes) {
Class<?> clazz = Class.forName(bean.getBeanClassName());
URL location = clazz.getProtectionDomain().getCodeSource().getLocation();
List<Class> classFiles = pluginClasses.get(location);
if (classFiles == null) {
classFiles = Lists.newArrayList(clazz);
} else {
classFiles.add(clazz);
}
pluginClasses.put(location, classFiles);
}
return pluginClasses.entrySet().parallelStream()
.map(e -> new PluginResource(e.getKey(), e.getValue()))
.collect(Collectors.toList());
}
}

View File

@ -6,11 +6,12 @@ import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.owasp.webgoat.lessons.AbstractLesson;
import org.owasp.webgoat.lessons.Assignment;
import org.springframework.util.FileCopyUtils;
import org.springframework.util.SerializationUtils;
import org.springframework.core.serializer.DefaultDeserializer;
import org.springframework.core.serializer.DefaultSerializer;
import java.io.File;
import java.util.HashMap;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.util.Map;
import java.util.stream.Collectors;
@ -50,7 +51,6 @@ public class UserTracker {
private final String webgoatHome;
private final String user;
private Map<String, LessonTracker> storage = new HashMap<>();
public UserTracker(final String webgoatHome, final String user) {
this.webgoatHome = webgoatHome;
@ -64,53 +64,72 @@ public class UserTracker {
* @return the optional lesson tracker
*/
public LessonTracker getLessonTracker(AbstractLesson lesson) {
return getLessonTracker(load(), lesson);
}
/**
* Returns the lesson tracker for a specific lesson if available.
*
* @param lesson the lesson
* @return the optional lesson tracker
*/
public LessonTracker getLessonTracker(Map<String, LessonTracker> storage, AbstractLesson lesson) {
LessonTracker lessonTracker = storage.get(lesson.getTitle());
if (lessonTracker == null) {
lessonTracker = new LessonTracker(lesson);
storage.put(lesson.getTitle(), lessonTracker);
save(storage);
}
return lessonTracker;
}
public void assignmentSolved(AbstractLesson lesson, String assignmentName) {
LessonTracker lessonTracker = getLessonTracker(lesson);
Map<String, LessonTracker> storage = load();
LessonTracker lessonTracker = storage.get(lesson.getTitle());
lessonTracker.incrementAttempts();
lessonTracker.assignmentSolved(assignmentName);
save();
save(storage);
}
public void assignmentFailed(AbstractLesson lesson) {
LessonTracker lessonTracker = getLessonTracker(lesson);
Map<String, LessonTracker> storage = load();
LessonTracker lessonTracker = storage.get(lesson.getTitle());
lessonTracker.incrementAttempts();
save();
save(storage);
}
public void load() {
public Map<String, LessonTracker> load() {
File file = new File(webgoatHome, user + ".progress");
if (file.exists() && file.isFile()) {
try {
this.storage = (Map<String, LessonTracker>) SerializationUtils.deserialize(FileCopyUtils.copyToByteArray(file));
DefaultDeserializer deserializer = new DefaultDeserializer(Thread.currentThread().getContextClassLoader());
return (Map<String, LessonTracker>) deserializer.deserialize(new FileInputStream(file));
} catch (Exception e) {
log.error("Unable to read the progress file, creating a new one...");
this.storage = Maps.newHashMap();
}
}
return Maps.newHashMap();
}
@SneakyThrows
private void save() {
private void save(Map<String, LessonTracker> storage) {
File file = new File(webgoatHome, user + ".progress");
FileCopyUtils.copy(SerializationUtils.serialize(this.storage), file);
DefaultSerializer serializer = new DefaultSerializer();
serializer.serialize(storage, new FileOutputStream(file));
}
public void reset(AbstractLesson al) {
getLessonTracker(al).reset();
save();
Map<String, LessonTracker> storage = load();
LessonTracker lessonTracker = getLessonTracker(storage, al);
lessonTracker.reset();
save(storage);
}
public int numberOfLessonsSolved() {
int numberOfLessonsSolved = 0;
Map<String, LessonTracker> storage = load();
for (LessonTracker lessonTracker : storage.values()) {
if (lessonTracker.isLessonSolved()) {
numberOfLessonsSolved = numberOfLessonsSolved + 1;
@ -121,6 +140,7 @@ public class UserTracker {
public int numberOfAssignmentsSolved() {
int numberOfAssignmentsSolved = 0;
Map<String, LessonTracker> storage = load();
for (LessonTracker lessonTracker : storage.values()) {
Map<Assignment, Boolean> lessonOverview = lessonTracker.getLessonOverview();
numberOfAssignmentsSolved = lessonOverview.values().stream().filter(b -> b).collect(Collectors.counting()).intValue();

View File

@ -0,0 +1,71 @@
package org.owasp.webgoat.session;
import lombok.Getter;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Transient;
import java.util.Collection;
import java.util.Collections;
/**
* @author nbaars
* @since 3/19/17.
*/
@Getter
@Entity
public class WebGoatUser implements UserDetails {
public static final String ROLE_USER = "WEBGOAT_USER";
public static final String ROLE_ADMIN = "WEBGOAT_ADMIN";
@Id
private String username;
private String password;
private String role = ROLE_USER;
@Transient
private User user;
protected WebGoatUser() {
}
public WebGoatUser(String username, String password) {
this.username = username;
this.password = password;
createUser();
}
public void createUser() {
this.user = new User(username, password, getAuthorities());
}
public Collection<? extends GrantedAuthority> getAuthorities() {
return Collections.singleton(new SimpleGrantedAuthority(getRole()));
}
@Override
public boolean isAccountNonExpired() {
return this.user.isAccountNonExpired();
}
@Override
public boolean isAccountNonLocked() {
return this.user.isAccountNonLocked();
}
@Override
public boolean isCredentialsNonExpired() {
return this.user.isCredentialsNonExpired();
}
@Override
public boolean isEnabled() {
return this.user.isEnabled();
}
}

View File

@ -3,7 +3,6 @@ package org.owasp.webgoat.session;
import lombok.extern.slf4j.Slf4j;
import org.owasp.webgoat.lessons.AbstractLesson;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.User;
import java.sql.Connection;
import java.sql.SQLException;
@ -40,10 +39,9 @@ import java.sql.SQLException;
@Slf4j
public class WebSession {
private final User currentUser;
private final WebGoatUser currentUser;
private final WebgoatContext webgoatContext;
private AbstractLesson currentLesson;
private UserTracker userTracker;
/**
* Constructor for the WebSession object
@ -52,7 +50,7 @@ public class WebSession {
*/
public WebSession(WebgoatContext webgoatContext) {
this.webgoatContext = webgoatContext;
this.currentUser = (User) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
this.currentUser = (WebGoatUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
}
/**

View File

@ -0,0 +1,61 @@
package org.owasp.webgoat.users;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.owasp.webgoat.session.WebGoatUser;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import javax.validation.Valid;
/**
* @author nbaars
* @since 3/19/17.
*/
@Controller
@AllArgsConstructor
@Slf4j
public class RegistrationController {
private UserValidator userValidator;
private UserService userService;
private AuthenticationManager authenticationManager;
@GetMapping("/registration")
public String showForm(UserForm userForm) {
return "registration";
}
@PostMapping("/register.mvc")
public String registration(@ModelAttribute("userForm") @Valid UserForm userForm, BindingResult bindingResult) {
userValidator.validate(userForm, bindingResult);
if (bindingResult.hasErrors()) {
return "registration";
}
userService.addUser(userForm.getUsername(), userForm.getPassword());
autologin(userForm.getUsername(), userForm.getPassword());
return "redirect:/attack";
}
private void autologin(String username, String password) {
WebGoatUser user = userService.loadUserByUsername(username);
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(user, password, user.getAuthorities());
authenticationManager.authenticate(usernamePasswordAuthenticationToken);
if (usernamePasswordAuthenticationToken.isAuthenticated()) {
SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
log.debug("Login for {} successfully!", username);
}
}
}

View File

@ -0,0 +1,28 @@
package org.owasp.webgoat.users;
import lombok.Getter;
import lombok.Setter;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
/**
* @author nbaars
* @since 3/19/17.
*/
@Getter
@Setter
public class UserForm {
@NotNull
@Size(min=6, max=10)
private String username;
@NotNull
@Size(min=6, max=10)
private String password;
@NotNull
@Size(min=6, max=10)
private String matchingPassword;
@NotNull
private String agree;
}

View File

@ -0,0 +1,13 @@
package org.owasp.webgoat.users;
import org.owasp.webgoat.session.WebGoatUser;
import org.springframework.data.repository.CrudRepository;
/**
* @author nbaars
* @since 3/19/17.
*/
public interface UserRepository extends CrudRepository<WebGoatUser, Long> {
WebGoatUser findByUsername(String username);
}

View File

@ -0,0 +1,29 @@
package org.owasp.webgoat.users;
import lombok.AllArgsConstructor;
import org.owasp.webgoat.session.WebGoatUser;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
/**
* @author nbaars
* @since 3/19/17.
*/
@Service
@AllArgsConstructor
public class UserService implements UserDetailsService {
private final UserRepository userRepository;
@Override
public WebGoatUser loadUserByUsername(String username) throws UsernameNotFoundException {
WebGoatUser webGoatUser = userRepository.findByUsername(username);
webGoatUser.createUser();
return webGoatUser;
}
public void addUser(String username, String password) {
userRepository.save(new WebGoatUser(username, password));
}
}

View File

@ -0,0 +1,34 @@
package org.owasp.webgoat.users;
import org.springframework.stereotype.Component;
import org.springframework.validation.Errors;
import org.springframework.validation.Validator;
/**
* @author nbaars
* @since 3/19/17.
*/
@Component
public class UserValidator implements Validator {
// @Autowired
// private UserService userService;
@Override
public boolean supports(Class<?> aClass) {
return UserForm.class.equals(aClass);
}
@Override
public void validate(Object o, Errors errors) {
UserForm userForm = (UserForm) o;
// if (userService.findByUsername(userForm.getUsername()) != null) {
// errors.rejectValue("username", "Duplicate.userForm.username");
// }
if (!userForm.getMatchingPassword().equals(userForm.getPassword())) {
errors.rejectValue("matchingPassword", "password.diff");
}
}
}