refactored so the extracting is not tangled with the loading the plugin
This commit is contained in:
		| @ -1,21 +1,43 @@ | |||||||
| package org.owasp.webgoat.plugins; | package org.owasp.webgoat.plugins; | ||||||
|  |  | ||||||
| import org.owasp.webgoat.lessons.AbstractLesson; | import org.owasp.webgoat.lessons.AbstractLesson; | ||||||
|  | import org.slf4j.Logger; | ||||||
|  | import org.slf4j.LoggerFactory; | ||||||
|  | import org.springframework.util.StringUtils; | ||||||
|  |  | ||||||
| import java.io.IOException; | import java.io.IOException; | ||||||
| import java.nio.charset.Charset; | import java.nio.charset.Charset; | ||||||
| import java.nio.file.Files; | import java.nio.file.Files; | ||||||
| import java.nio.file.Path; | import java.nio.file.Path; | ||||||
|  | import java.util.ArrayList; | ||||||
|  | import java.util.List; | ||||||
|  | import java.util.Map; | ||||||
|  |  | ||||||
| public class Plugin { | public class Plugin { | ||||||
|  |  | ||||||
|  |     private static final Logger logger = LoggerFactory.getLogger(Plugin.class); | ||||||
|     private final Class<AbstractLesson> lesson; |     private final Class<AbstractLesson> lesson; | ||||||
|     private final Path pluginDirectory; |     private final Path pluginDirectory; | ||||||
|  |  | ||||||
|  |     public static class PluginLoadingFailure extends RuntimeException { | ||||||
|  |  | ||||||
|  |         public PluginLoadingFailure(String message) { | ||||||
|  |             super(message); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|     public static class Builder { |     public static class Builder { | ||||||
|  |  | ||||||
|         private Path pluginDirectory; |         private Path pluginDirectory; | ||||||
|         private Class lesson; |         private Class lesson; | ||||||
|  |         private final List<String> loadedClasses = new ArrayList<String>(); | ||||||
|  |  | ||||||
|  |         public Builder loadClasses(Map<String, byte[]> classes) { | ||||||
|  |             for (Map.Entry<String, byte[]> clazz : classes.entrySet() ) { | ||||||
|  |                 loadClass(clazz.getKey(), clazz.getValue()); | ||||||
|  |             } | ||||||
|  |             return this; | ||||||
|  |         } | ||||||
|  |  | ||||||
|         public Builder loadClass(String name, byte[] classFile) { |         public Builder loadClass(String name, byte[] classFile) { | ||||||
|             ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); |             ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); | ||||||
| @ -26,20 +48,27 @@ public class Plugin { | |||||||
|                 if (AbstractLesson.class.isAssignableFrom(clazz)) { |                 if (AbstractLesson.class.isAssignableFrom(clazz)) { | ||||||
|                     this.lesson = clazz; |                     this.lesson = clazz; | ||||||
|                 } |                 } | ||||||
|  |                 loadedClasses.add(clazz.getName()); | ||||||
|             } catch (ClassNotFoundException e) { |             } catch (ClassNotFoundException e) { | ||||||
|                 e.printStackTrace(); |                 logger.error("Unable to load class {}", name); | ||||||
|             } |             } | ||||||
|             return this; |             return this; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         public Builder setBaseDirectory(Path pluginDirectory) { |         public Builder setBaseDirectory(Path pluginDirectory) { | ||||||
|             this.pluginDirectory = pluginDirectory; |             this.pluginDirectory = pluginDirectory; | ||||||
|  |             //Find necessary files flag if something went wrong plugin should complain | ||||||
|             return this; |             return this; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         public Plugin build() { |         public Plugin build() { | ||||||
|  |             if ( lesson == null ) { | ||||||
|  |                 throw new PluginLoadingFailure(String.format("Lesson class not found, following classes were detected in the plugin: %s", | ||||||
|  |                     StringUtils.collectionToCommaDelimitedString(loadedClasses))); | ||||||
|  |             } | ||||||
|             return new Plugin(this.lesson, pluginDirectory); |             return new Plugin(this.lesson, pluginDirectory); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public Plugin(Class<AbstractLesson> lesson, Path pluginDirectory) { |     public Plugin(Class<AbstractLesson> lesson, Path pluginDirectory) { | ||||||
| @ -52,9 +81,9 @@ public class Plugin { | |||||||
|         try { |         try { | ||||||
|             Files.readAllLines(lesson_plans.resolve(this.lesson.getSimpleName() + ".html"), Charset.defaultCharset()); |             Files.readAllLines(lesson_plans.resolve(this.lesson.getSimpleName() + ".html"), Charset.defaultCharset()); | ||||||
|         } catch (IOException e) { |         } catch (IOException e) { | ||||||
|             e.printStackTrace(); |             logger.error("No html found in directory {}", lesson_plans.toString()); | ||||||
|         } |         } | ||||||
|         return lesson_plans.resolve(this.lesson.getSimpleName() + ".html").toFile().toString(); |         return ""; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public Class<AbstractLesson> getLesson() { |     public Class<AbstractLesson> getLesson() { | ||||||
|  | |||||||
| @ -13,10 +13,5 @@ public class PluginClassLoader extends ClassLoader { | |||||||
|         return defineClass(name, classFile, 0, classFile.length); |         return defineClass(name, classFile, 0, classFile.length); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public static void main(String[] args) { |  | ||||||
|  |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|  |  | ||||||
| } | } | ||||||
|  |  | ||||||
|  | |||||||
| @ -13,60 +13,59 @@ import java.nio.file.Files; | |||||||
| import java.nio.file.Path; | import java.nio.file.Path; | ||||||
| import java.nio.file.Paths; | import java.nio.file.Paths; | ||||||
| import java.nio.file.SimpleFileVisitor; | import java.nio.file.SimpleFileVisitor; | ||||||
| import java.nio.file.StandardCopyOption; |  | ||||||
| import java.nio.file.attribute.BasicFileAttributes; | import java.nio.file.attribute.BasicFileAttributes; | ||||||
| import java.util.ArrayList; |  | ||||||
| import java.util.HashMap; | import java.util.HashMap; | ||||||
| import java.util.List; | import java.util.Map; | ||||||
|  |  | ||||||
|  | import static java.nio.file.StandardCopyOption.REPLACE_EXISTING; | ||||||
|  |  | ||||||
| /** | /** | ||||||
|  * Extract the zip file and place the files in a temp directory |  * Extract the wpf file and collect the classes to load and remember the base directory in which we extracted | ||||||
|  * |  * the files (lesson plans etc) | ||||||
|  * TODO: should only do the extraction of the zip file return should be the base directory of the extracted |  | ||||||
|  * plugin. The PluginLoader should take care of the loading |  | ||||||
|  * |  | ||||||
|  */ |  */ | ||||||
| public class PluginExtractor { | public class PluginExtractor { | ||||||
|  |  | ||||||
|     private static final String DIRECTORY = "webgoat"; |     private static final String DIRECTORY = "webgoat"; | ||||||
|     private final Path pluginArchive; |     private final Path pluginArchive; | ||||||
|     private final Logger logger = LoggerFactory.getLogger(getClass()); |     private final Logger logger = LoggerFactory.getLogger(getClass()); | ||||||
|     private final List<byte[]> classes = new ArrayList<byte[]>(); |     private final Map<String, byte[]> classes = new HashMap<String, byte[]>(); | ||||||
|  |     private Path baseDirectory; | ||||||
|  |  | ||||||
|     public PluginExtractor(Path pluginArchive) { |     public PluginExtractor(Path pluginArchive) { | ||||||
|         this.pluginArchive = pluginArchive; |         this.pluginArchive = pluginArchive; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     public void extract() { | ||||||
|     public Plugin extract() { |  | ||||||
|         final Plugin.Builder pluginBuilder = new Plugin.Builder(); |  | ||||||
|         FileSystem zip = null; |         FileSystem zip = null; | ||||||
|         try { |         try { | ||||||
|             zip = createZipFileSystem(); |             zip = createZipFileSystem(); | ||||||
|             final Path root = zip.getPath("/"); |             final Path root = zip.getPath("/"); | ||||||
|             final Path tmpDir = Paths.get(System.getProperty("java.io.tmpdir"), DIRECTORY); |             baseDirectory = Paths.get(System.getProperty("java.io.tmpdir"), DIRECTORY); | ||||||
|             pluginBuilder.setBaseDirectory(tmpDir); |  | ||||||
|             Files.walkFileTree(root, new SimpleFileVisitor<Path>() { |             Files.walkFileTree(root, new SimpleFileVisitor<Path>() { | ||||||
|                 @Override |                 @Override | ||||||
|                 public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { |                 public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { | ||||||
|                     if (file.toString().endsWith(".class")) { |                     if (file.toString().endsWith(".class")) { | ||||||
|                         ByteArrayOutputStream bos = new ByteArrayOutputStream(); |                         ByteArrayOutputStream bos = new ByteArrayOutputStream(); | ||||||
|                         Files.copy(file, bos); |                         Files.copy(file, bos); | ||||||
|                         pluginBuilder.loadClass(file.toString(), bos.toByteArray()); |                         classes.put(file.toString(), bos.toByteArray()); | ||||||
|                     } |                     } | ||||||
|  |                     Files.copy(file, Paths.get(baseDirectory.toString(), file.toString()), REPLACE_EXISTING); | ||||||
|                     Files.copy(file, Paths.get(tmpDir.toString(), file.toString()), StandardCopyOption.REPLACE_EXISTING); |  | ||||||
|                     return FileVisitResult.CONTINUE; |                     return FileVisitResult.CONTINUE; | ||||||
|                 } |                 } | ||||||
|             }); |             }); | ||||||
|             return pluginBuilder.build(); |  | ||||||
|         } catch (IOException io) { |         } catch (IOException io) { | ||||||
|             logger.error(String.format("Unable to extract: %s", pluginArchive.getFileName()), io); |             logger.error(String.format("Unable to extract: %s", pluginArchive.getFileName()), io); | ||||||
|         } finally { |         } finally { | ||||||
|             closeZipFileSystem(zip); |             closeZipFileSystem(zip); | ||||||
|         } |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|         return pluginBuilder.build(); |     public Map<String, byte[]> getClasses() { | ||||||
|  |         return this.classes; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public Path getBaseDirectory() { | ||||||
|  |         return this.baseDirectory; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     private FileSystem createZipFileSystem() throws IOException { |     private FileSystem createZipFileSystem() throws IOException { | ||||||
|  | |||||||
| @ -1,5 +1,8 @@ | |||||||
| package org.owasp.webgoat.plugins; | package org.owasp.webgoat.plugins; | ||||||
|  |  | ||||||
|  | import org.slf4j.Logger; | ||||||
|  | import org.slf4j.LoggerFactory; | ||||||
|  |  | ||||||
| import java.io.IOException; | import java.io.IOException; | ||||||
| import java.nio.file.FileVisitResult; | import java.nio.file.FileVisitResult; | ||||||
| import java.nio.file.Files; | import java.nio.file.Files; | ||||||
| @ -11,6 +14,7 @@ import java.util.List; | |||||||
|  |  | ||||||
| public class PluginsLoader { | public class PluginsLoader { | ||||||
|  |  | ||||||
|  |     private final Logger logger = LoggerFactory.getLogger(this.getClass()); | ||||||
|     private final Path path; |     private final Path path; | ||||||
|  |  | ||||||
|     public PluginsLoader(Path path) { |     public PluginsLoader(Path path) { | ||||||
| @ -24,13 +28,22 @@ public class PluginsLoader { | |||||||
|  |  | ||||||
|                 @Override |                 @Override | ||||||
|                 public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { |                 public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { | ||||||
|                     plugins.add(new PluginExtractor(file).extract()); |                     try { | ||||||
|  |                         PluginExtractor extractor = new PluginExtractor(file); | ||||||
|  |                         extractor.extract(); | ||||||
|  |                         Plugin.Builder builder = new Plugin.Builder(); | ||||||
|  |                         builder.loadClasses(extractor.getClasses()); | ||||||
|  |                         builder.setBaseDirectory(extractor.getBaseDirectory()); | ||||||
|  |                         plugins.add(builder.build()); | ||||||
|  |                     } catch (Plugin.PluginLoadingFailure e) { | ||||||
|  |                        logger.error("Unable to load plugin, continue reading others..."); | ||||||
|  |                     } | ||||||
|                     return FileVisitResult.CONTINUE; |                     return FileVisitResult.CONTINUE; | ||||||
|                 } |                 } | ||||||
|  |  | ||||||
|             }); |             }); | ||||||
|         } catch (IOException e) { |         } catch (IOException e) { | ||||||
|             e.printStackTrace(); |             logger.error("Loading plugins failed", e); | ||||||
|         } |         } | ||||||
|         return plugins; |         return plugins; | ||||||
|     } |     } | ||||||
|  | |||||||
		Reference in New Issue
	
	Block a user