diff --git a/webgoat-container/src/main/java/org/owasp/webgoat/LessonEndpointProvider.java b/webgoat-container/src/main/java/org/owasp/webgoat/LessonEndpointProvider.java
deleted file mode 100644
index f0c7354bb..000000000
--- a/webgoat-container/src/main/java/org/owasp/webgoat/LessonEndpointProvider.java
+++ /dev/null
@@ -1,66 +0,0 @@
-package org.owasp.webgoat;
-
-import org.owasp.webgoat.lessons.LessonEndpointMapping;
-import org.owasp.webgoat.plugins.PluginClassLoader;
-import org.springframework.beans.factory.BeanFactory;
-import org.springframework.beans.factory.ListableBeanFactory;
-import org.springframework.beans.factory.config.ConfigurableBeanFactory;
-import org.springframework.beans.factory.support.DefaultListableBeanFactory;
-import org.springframework.boot.actuate.endpoint.mvc.MvcEndpoint;
-import org.springframework.context.ApplicationContext;
-import org.springframework.context.annotation.AnnotationConfigApplicationContext;
-import org.springframework.context.annotation.ClassPathBeanDefinitionScanner;
-import org.springframework.core.type.filter.AnnotationTypeFilter;
-
-import java.util.Map;
-
-/**
- * Each lesson can define an endpoint which can support the lesson. So for example if you create a lesson which uses JavaScript and
- * needs to call out to the server to fetch data you can define an endpoint in that lesson. WebGoat will pick up this endpoint and
- * Spring will publish it.
- *
- * Find all the defined endpoints in the lessons and register those endpoints in the Spring context so later on the
- * Actuator will pick them up and expose them as real endpoints.
- *
- * We use the Actuator here so we don't have to do all the hard work ourselves (endpoint strategy pattern etc) so in a
- * lesson you can just define a subclass of LessonEndpoint which this class will publish as an endpoint. So we can
- * dynamically load endpoints from our plugins.
- */
-public class LessonEndpointProvider {
-
- private final String pluginBasePackage;
- private final ApplicationContext parentContext;
- private final PluginClassLoader classLoader;
- private ListableBeanFactory context;
- private DefaultListableBeanFactory providedBeans;
- private BeanFactory beanFactory;
-
-
- public LessonEndpointProvider(String pluginBasePackage, ApplicationContext parentContext, BeanFactory beanFactory, PluginClassLoader cl) {
- this.pluginBasePackage = pluginBasePackage;
- this.parentContext = parentContext;
- this.providedBeans = new DefaultListableBeanFactory(this.parentContext.getParentBeanFactory());
- this.beanFactory = beanFactory;
- this.classLoader = cl;
- }
-
- public void registerEndpoints() {
- if (context == null) {
- AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
- context.setParent(parentContext);
- context.setClassLoader(classLoader);
-
- ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(context, false);
- scanner.addIncludeFilter(new AnnotationTypeFilter(LessonEndpointMapping.class));
- scanner.scan(pluginBasePackage);
- context.refresh();
-
- Map beansOfType = context.getBeansOfType(MvcEndpoint.class);
- ConfigurableBeanFactory configurableBeanFactory = (ConfigurableBeanFactory) beanFactory;
- beansOfType.forEach((k, v) -> {
- configurableBeanFactory.registerSingleton(k, v);
- });
- this.context = context;
- }
- }
-}
diff --git a/webgoat-container/src/main/java/org/owasp/webgoat/WebGoat.java b/webgoat-container/src/main/java/org/owasp/webgoat/WebGoat.java
index 4f5c0dd1b..f14333f62 100644
--- a/webgoat-container/src/main/java/org/owasp/webgoat/WebGoat.java
+++ b/webgoat-container/src/main/java/org/owasp/webgoat/WebGoat.java
@@ -30,6 +30,7 @@
*/
package org.owasp.webgoat;
+import org.owasp.webgoat.plugins.Plugin;
import org.owasp.webgoat.plugins.PluginClassLoader;
import org.owasp.webgoat.plugins.PluginsLoader;
import org.owasp.webgoat.session.Course;
@@ -37,8 +38,11 @@ import org.owasp.webgoat.session.UserTracker;
import org.owasp.webgoat.session.WebSession;
import org.owasp.webgoat.session.WebgoatContext;
import org.owasp.webgoat.session.WebgoatProperties;
-import org.springframework.beans.factory.BeanFactory;
+import org.springframework.beans.factory.annotation.Autowire;
import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.beans.factory.config.BeanDefinition;
+import org.springframework.beans.factory.support.DefaultListableBeanFactory;
+import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
@@ -48,9 +52,11 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.PropertySource;
import org.springframework.context.annotation.Scope;
import org.springframework.context.annotation.ScopedProxyMode;
+import org.springframework.context.support.AbstractApplicationContext;
import javax.servlet.ServletContext;
import java.io.File;
+import java.util.List;
@SpringBootApplication
@PropertySource("classpath:/webgoat.properties")
@@ -88,22 +94,29 @@ public class WebGoat extends SpringBootServletInitializer {
return new WebSession(course, webgoatContext, context);
}
- @Bean
- public LessonEndpointProvider lessonEndpointProvider(ApplicationContext applicationContext, BeanFactory factory, PluginClassLoader cl) {
- LessonEndpointProvider lessonEndpointProvider = new LessonEndpointProvider("org.owasp.webgoat", applicationContext, factory, cl);
- return lessonEndpointProvider;
- }
-
@Bean
public Course course(PluginsLoader pluginsLoader, WebgoatContext webgoatContext, ServletContext context, WebgoatProperties webgoatProperties,
- LessonEndpointProvider endpointProvider) {
+ ApplicationContext applicationContext) {
Course course = new Course(webgoatProperties);
course.loadCourses(webgoatContext, context, "/");
- course.loadLessonFromPlugin(pluginsLoader.loadPlugins());
- endpointProvider.registerEndpoints();
+ List plugins = pluginsLoader.loadPlugins();
+ course.loadLessonFromPlugin(plugins);
+ plugins.forEach(p -> publishEndpointsWithSpring(p, (AbstractApplicationContext)applicationContext));
return course;
}
+ private void publishEndpointsWithSpring(Plugin plugin, AbstractApplicationContext applicationContext) {
+ plugin.getLessonEndpoints().forEach(e -> {
+ try {
+ BeanDefinition beanDefinition = new RootBeanDefinition(e, Autowire.BY_TYPE.value(), true);
+ DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) applicationContext.getBeanFactory();
+ beanFactory.registerBeanDefinition(beanDefinition.getBeanClassName(), beanDefinition);
+ } catch (Exception ex) {
+ logger.warn("Failed to register " + e.getSimpleName() + " as endpoint with Spring, skipping...");
+ }
+ });
+ }
+
@Bean
public UserTracker userTracker() {
UserTracker userTracker = UserTracker.instance();
diff --git a/webgoat-container/src/main/java/org/owasp/webgoat/lessons/model/AttackResult.java b/webgoat-container/src/main/java/org/owasp/webgoat/lessons/model/AttackResult.java
index 787c96b80..95af8dacf 100644
--- a/webgoat-container/src/main/java/org/owasp/webgoat/lessons/model/AttackResult.java
+++ b/webgoat-container/src/main/java/org/owasp/webgoat/lessons/model/AttackResult.java
@@ -39,11 +39,11 @@ public class AttackResult {
return AttackResult.success("Congratulations");
}
- public static AttackResult success(String output) {
+ public static AttackResult success(String feedback) {
AttackResult attackResult = new AttackResult();
attackResult.lessonCompleted = true;
- attackResult.feedback = "Congratulations";
- attackResult.output = output;
+ attackResult.feedback = feedback;
+ attackResult.output = "";
return attackResult;
}
diff --git a/webgoat-container/src/main/java/org/owasp/webgoat/plugins/Plugin.java b/webgoat-container/src/main/java/org/owasp/webgoat/plugins/Plugin.java
index 5a9772523..59fb67201 100644
--- a/webgoat-container/src/main/java/org/owasp/webgoat/plugins/Plugin.java
+++ b/webgoat-container/src/main/java/org/owasp/webgoat/plugins/Plugin.java
@@ -1,17 +1,13 @@
package org.owasp.webgoat.plugins;
-import com.fasterxml.jackson.core.type.TypeReference;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
import com.google.common.base.Optional;
import com.google.common.collect.Lists;
-import org.apache.commons.io.FileUtils;
import org.owasp.webgoat.lessons.AbstractLesson;
+import org.owasp.webgoat.lessons.LessonEndpoint;
import org.owasp.webgoat.lessons.NewLesson;
import org.springframework.util.StringUtils;
import java.io.File;
-import java.io.IOException;
import java.nio.file.Path;
import java.util.HashMap;
import java.util.List;
@@ -35,6 +31,7 @@ public class Plugin {
private Class lesson;
private YmlBasedLesson ymlBasedLesson; //TODO REMOVE!
private Class newLesson;
+ private List> lessonEndpoints = Lists.newArrayList();
private Map solutionLanguageFiles = new HashMap<>();
private Map lessonPlansLanguageFiles = new HashMap<>();
private List pluginFiles = Lists.newArrayList();
@@ -44,6 +41,10 @@ public class Plugin {
this.classLoader = classLoader;
}
+ public List> getLessonEndpoints() {
+ return this.lessonEndpoints;
+ }
+
/**
* findLesson.
*
@@ -71,28 +72,20 @@ public class Plugin {
} catch (ClassNotFoundException ce) {
throw new PluginLoadingFailure("Class " + realClassName + " listed in jar but unable to load the class.", ce);
}
-
- //TODO remove
- readYmlLessonConfiguration();
}
- private void readYmlLessonConfiguration() {
- java.util.Optional ymlFile = this.pluginFiles.stream().filter(f -> f.getName().endsWith(".yml")).findFirst();
- if (ymlFile.isPresent()) {
+ public void findEndpoints(List classes) {
+ for (String clazzName : classes) {
+ String realClassName = StringUtils.trimLeadingCharacter(clazzName, '/').replaceAll("/", ".").replaceAll(".class", "");
+
try {
- String ymlStr = FileUtils.readFileToString(ymlFile.get());
- ObjectMapper mapper = new ObjectMapper(new YAMLFactory());
- Map ymlAsMap = mapper.readValue(ymlStr, new TypeReference