From e6fb74fa550e997b13ae42763a862097091055d7 Mon Sep 17 00:00:00 2001 From: Daniel Kvist Date: Wed, 30 Mar 2016 22:07:11 +0200 Subject: [PATCH] Implementation of developer controls to reload plugins and set label debugging from the GUI. Ref: webgoat/webgoat#93 --- .../webgoat/service/LabelDebugService.java | 50 +++++++++++++--- .../webgoat/service/PluginReloadService.java | 22 ++++--- .../owasp/webgoat/session/LabelDebugger.java | 25 ++++++-- .../main/webapp/WEB-INF/pages/main_new.jsp | 7 +++ .../src/main/webapp/css/main.css | 18 ++++++ .../js/goatApp/controller/LessonController.js | 4 ++ .../js/goatApp/model/LabelDebugModel.js | 39 ++++++++++++ .../js/goatApp/model/PluginReloadModel.js | 19 ++++++ .../js/goatApp/view/DeveloperControlsView.js | 60 +++++++++++++++++++ .../webgoat/session/LabelDebuggerTest.java | 51 ++++++++++++++++ 10 files changed, 277 insertions(+), 18 deletions(-) create mode 100644 webgoat-container/src/main/webapp/js/goatApp/model/LabelDebugModel.js create mode 100644 webgoat-container/src/main/webapp/js/goatApp/model/PluginReloadModel.js create mode 100644 webgoat-container/src/main/webapp/js/goatApp/view/DeveloperControlsView.js create mode 100644 webgoat-container/src/test/java/org/owasp/webgoat/session/LabelDebuggerTest.java diff --git a/webgoat-container/src/main/java/org/owasp/webgoat/service/LabelDebugService.java b/webgoat-container/src/main/java/org/owasp/webgoat/service/LabelDebugService.java index d73f7e274..c00fc20f9 100644 --- a/webgoat-container/src/main/java/org/owasp/webgoat/service/LabelDebugService.java +++ b/webgoat-container/src/main/java/org/owasp/webgoat/service/LabelDebugService.java @@ -29,18 +29,23 @@ */ package org.owasp.webgoat.service; +import java.util.HashMap; +import java.util.Map; + import org.owasp.webgoat.session.LabelDebugger; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; /** - *

PluginReloadService class.

+ *

LabelDebugService class.

* * @author nbaars * @version $Id: $Id @@ -48,21 +53,52 @@ import org.springframework.web.bind.annotation.ResponseBody; @Controller public class LabelDebugService extends BaseService { + private static final String URL_DEBUG_LABELS_MVC = "/debug/labels.mvc"; + private static final String KEY_ENABLED = "enabled"; + private static final String KEY_SUCCESS = "success"; + private static final Logger logger = LoggerFactory.getLogger(LabelDebugService.class); @Autowired private LabelDebugger labelDebugger; + /** - * Reload all the plugins + * Checks if debugging of labels is enabled or disabled * * @return a {@link org.springframework.http.ResponseEntity} object. */ - @RequestMapping(value = "/debug/labels.mvc") + @RequestMapping(value = URL_DEBUG_LABELS_MVC, produces = MediaType.APPLICATION_JSON_VALUE) public @ResponseBody - //todo parse params to add enable / disable - ResponseEntity reloadPlugins() { - labelDebugger.enable(); - return new ResponseEntity("Label debugger enabled refresh the WebGoat page!",HttpStatus.OK); + ResponseEntity> checkDebuggingStatus() { + logger.debug("Checking label debugging, it is " + labelDebugger.isEnabled()); // FIXME parameterize + Map result = createResponse(labelDebugger.isEnabled()); + return new ResponseEntity>(result, HttpStatus.OK); + } + + /** + * Sets the enabled flag on the label debugger to the given parameter + * + * @return a {@link org.springframework.http.ResponseEntity} object. + * @throws Exception + */ + @RequestMapping(value = URL_DEBUG_LABELS_MVC, produces = MediaType.APPLICATION_JSON_VALUE, params = KEY_ENABLED) + public @ResponseBody + ResponseEntity> setDebuggingStatus(@RequestParam("enabled") Boolean enabled) throws Exception { + logger.debug("Setting label debugging to " + labelDebugger.isEnabled()); // FIXME parameterize + Map result = createResponse(enabled); + labelDebugger.setEnabled(enabled); + return new ResponseEntity>(result, HttpStatus.OK); + } + + /** + * @param enabled + * @return a {@link java.util.Map} object. + */ + private Map createResponse(Boolean enabled) { + Map result = new HashMap(); + result.put(KEY_SUCCESS, Boolean.TRUE); + result.put(KEY_ENABLED, enabled); + return result; } } diff --git a/webgoat-container/src/main/java/org/owasp/webgoat/service/PluginReloadService.java b/webgoat-container/src/main/java/org/owasp/webgoat/service/PluginReloadService.java index 64e8fc50e..6355318aa 100644 --- a/webgoat-container/src/main/java/org/owasp/webgoat/service/PluginReloadService.java +++ b/webgoat-container/src/main/java/org/owasp/webgoat/service/PluginReloadService.java @@ -29,19 +29,23 @@ */ package org.owasp.webgoat.service; +import java.nio.file.Paths; +import java.util.HashMap; +import java.util.Map; + +import javax.servlet.http.HttpSession; + import org.owasp.webgoat.plugins.PluginsLoader; import org.owasp.webgoat.session.WebSession; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; -import javax.servlet.http.HttpSession; -import java.nio.file.Paths; - /** *

PluginReloadService class.

* @@ -59,16 +63,20 @@ public class PluginReloadService extends BaseService { * @param session a {@link javax.servlet.http.HttpSession} object. * @return a {@link org.springframework.http.ResponseEntity} object. */ - @RequestMapping(value = "/reloadplugins.mvc") + @RequestMapping(value = "/reloadplugins.mvc", produces = MediaType.APPLICATION_JSON_VALUE) public @ResponseBody - ResponseEntity reloadPlugins(HttpSession session) { + ResponseEntity> reloadPlugins(HttpSession session) { WebSession webSession = (WebSession) session.getAttribute(WebSession.SESSION); + logger.debug("Loading plugins into cache"); String pluginPath = session.getServletContext().getRealPath("plugin_lessons"); String targetPath = session.getServletContext().getRealPath("plugin_extracted"); new PluginsLoader(Paths.get(pluginPath), Paths.get(targetPath)).copyJars(); - webSession.getCourse().loadLessonFromPlugin(session.getServletContext()); - return new ResponseEntity("Plugins reload refresh the WebGoat page!",HttpStatus.OK); + + Map result = new HashMap(); + result.put("success", true); + result.put("message", "Plugins reloaded"); + return new ResponseEntity>(result, HttpStatus.OK); } } diff --git a/webgoat-container/src/main/java/org/owasp/webgoat/session/LabelDebugger.java b/webgoat-container/src/main/java/org/owasp/webgoat/session/LabelDebugger.java index 0e199755b..23470dfc8 100644 --- a/webgoat-container/src/main/java/org/owasp/webgoat/session/LabelDebugger.java +++ b/webgoat-container/src/main/java/org/owasp/webgoat/session/LabelDebugger.java @@ -10,7 +10,7 @@ import java.io.Serializable; */ public class LabelDebugger implements Serializable { - private boolean isEnabled = false; + private boolean enabled = false; /** *

isEnabled.

@@ -18,14 +18,31 @@ public class LabelDebugger implements Serializable { * @return a boolean. */ public boolean isEnabled() { - return isEnabled; + return enabled; } /** - *

enable.

+ *

Enables label debugging

*/ public void enable() { - this.isEnabled = true; + this.enabled = true; + } + + /** + *

Disables label debugging

+ */ + public void disable() { + this.enabled = false; + } + + /** + *

Sets the status to enabled

+ * @param enabled + * @throws Exception if enabled is null + */ + public void setEnabled(Boolean enabled) throws Exception { + if(enabled == null) throw new Exception("Cannot set enabled to null"); + this.enabled = enabled; } } diff --git a/webgoat-container/src/main/webapp/WEB-INF/pages/main_new.jsp b/webgoat-container/src/main/webapp/WEB-INF/pages/main_new.jsp index 990737a3e..b601f15f2 100644 --- a/webgoat-container/src/main/webapp/WEB-INF/pages/main_new.jsp +++ b/webgoat-container/src/main/webapp/WEB-INF/pages/main_new.jsp @@ -142,6 +142,13 @@

Params

+
+

Developer controls

+
+
+
+ +
diff --git a/webgoat-container/src/main/webapp/css/main.css b/webgoat-container/src/main/webapp/css/main.css index c81b51679..b7dc144ef 100644 --- a/webgoat-container/src/main/webapp/css/main.css +++ b/webgoat-container/src/main/webapp/css/main.css @@ -765,6 +765,24 @@ cookie-container { padding-left:3px; } +.developer-controls-table { + width: 100%; +} + +.developer-controls-table td { + text-align: right; +} + +.developer-controls-table a { + color: #e84c3d +} + + +#menu-container a, +.developer-controls-table a { + cursor: pointer; +} + /* ========================================================================== MENU / Sidebar ========================================================================== */ diff --git a/webgoat-container/src/main/webapp/js/goatApp/controller/LessonController.js b/webgoat-container/src/main/webapp/js/goatApp/controller/LessonController.js index 43964bbe4..0276fd79d 100644 --- a/webgoat-container/src/main/webapp/js/goatApp/controller/LessonController.js +++ b/webgoat-container/src/main/webapp/js/goatApp/controller/LessonController.js @@ -11,6 +11,7 @@ define(['jquery', 'goatApp/view/CookieView', 'goatApp/view/ParamView', 'goatApp/model/ParamModel', + 'goatApp/view/DeveloperControlsView', 'goatApp/support/GoatUtils', 'goatApp/view/UserAndInfoView', 'goatApp/view/MenuButtonView', @@ -30,6 +31,7 @@ define(['jquery', CookieView, ParamView, ParamModel, + DeveloperControlsView, GoatUtils, UserAndInfoView, MenuButtonView, @@ -116,6 +118,8 @@ define(['jquery', this.sourceView = new SourceView(); this.lessonHintView = new HintView(); this.cookieView = new CookieView(); + this.developerControlsView = new DeveloperControlsView(); + //TODO: instantiate model with values (not sure why was not working before) var paramModel = new ParamModel({}); paramModel.set('scrParam',this.lessonContent.get('scrParam')); diff --git a/webgoat-container/src/main/webapp/js/goatApp/model/LabelDebugModel.js b/webgoat-container/src/main/webapp/js/goatApp/model/LabelDebugModel.js new file mode 100644 index 000000000..dbaa7dd09 --- /dev/null +++ b/webgoat-container/src/main/webapp/js/goatApp/model/LabelDebugModel.js @@ -0,0 +1,39 @@ +define([ + 'backbone'], + function( + Backbone) { + return Backbone.Model.extend({ + id: 'label-status', + url: 'service/debug/labels.mvc', + + label: '', + labels: { + enable: 'Enable label debugging', + disable: 'Disable label debugging' + }, + + initialize: function() { + this.load(); + }, + + fetch: function(options) { + options || (options = {}); + var data = (options.data || {}); + if(this.enabled != undefined) { + options.data = { enabled: !this.enabled }; + } + return Backbone.Collection.prototype.fetch.call(this, options); + }, + + load: function () { + this.fetch().then(this.labelStatusLoaded.bind(this)); + }, + + labelStatusLoaded: function(data) { + this.enabled = data.enabled; + this.label = this.enabled ? this.labels['disable'] : this.labels['enable']; + this.trigger('plugins:loaded', this, data); + } + + }); +}); diff --git a/webgoat-container/src/main/webapp/js/goatApp/model/PluginReloadModel.js b/webgoat-container/src/main/webapp/js/goatApp/model/PluginReloadModel.js new file mode 100644 index 000000000..d10d583fa --- /dev/null +++ b/webgoat-container/src/main/webapp/js/goatApp/model/PluginReloadModel.js @@ -0,0 +1,19 @@ +define([ + 'backbone'], + function( + Backbone) { + return Backbone.Model.extend({ + url: 'service/reloadplugins.mvc', + id: 'reload-plugins', + label: 'Reload plugins', + + load: function () { + this.fetch().then(this.pluginsLoaded.bind(this)); + }, + + pluginsLoaded: function(data) { + this.trigger('plugins:loaded', this, data); + } + + }); +}); diff --git a/webgoat-container/src/main/webapp/js/goatApp/view/DeveloperControlsView.js b/webgoat-container/src/main/webapp/js/goatApp/view/DeveloperControlsView.js new file mode 100644 index 000000000..04d8157c2 --- /dev/null +++ b/webgoat-container/src/main/webapp/js/goatApp/view/DeveloperControlsView.js @@ -0,0 +1,60 @@ +define(['jquery', + 'underscore', + 'backbone', + 'goatApp/model/PluginReloadModel', + 'goatApp/model/LabelDebugModel'], +function( + $, + _, + Backbone, + PluginReloadModel, + LabelDebugModel) { + return Backbone.View.extend({ + el: '#developer-controls', + + onControlClick: function(model) { + $('#' + model.id).find('td').text('Loading...'); + model.load(); + }, + + onPluginsLoaded: function(model) { + window.location.href = 'welcome.mvc'; + }, + + onLabelsLoaded: function(model) { + $('#' + model.id).find('td').text('Enabled: ' + model.enabled); + this.models[1] = model; + this.render(); + }, + + initialize: function(options) { + this.models = [new PluginReloadModel(), new LabelDebugModel()]; + this.listenTo(this.models[0], 'plugins:loaded', this.onPluginsLoaded); + this.listenTo(this.models[1], 'plugins:loaded', this.onLabelsLoaded); + this.render(); + }, + + render: function() { + this.$el.html(''); + var table = $('',{'class':'developer-controls-table table-nonfluid'}); + var self = this; + _.each(this.models, function(model) { + var newRow = $('', { id: model.id }); + var headerCell = $('
') + var statusCell = $('') + + var link = $('', { + 'text': model.label, + 'title': model.label + }); + link.click(_.bind(self.onControlClick, self, model)); + + newRow.append(headerCell.append(link)); + newRow.append(statusCell); + table.append(newRow); + }); + + this.$el.append(table); + } + }); +}); diff --git a/webgoat-container/src/test/java/org/owasp/webgoat/session/LabelDebuggerTest.java b/webgoat-container/src/test/java/org/owasp/webgoat/session/LabelDebuggerTest.java new file mode 100644 index 000000000..15b9e27ff --- /dev/null +++ b/webgoat-container/src/test/java/org/owasp/webgoat/session/LabelDebuggerTest.java @@ -0,0 +1,51 @@ +package org.owasp.webgoat.session; + +import org.junit.Assert; +import org.junit.Test; + + +public class LabelDebuggerTest { + + @Test + public void testSetEnabledTrue() throws Exception { + LabelDebugger ld = new LabelDebugger(); + ld.setEnabled(true); + Assert.assertTrue(ld.isEnabled()); + } + + @Test + public void testSetEnabledFalse() throws Exception { + LabelDebugger ld = new LabelDebugger(); + ld.setEnabled(false); + Assert.assertFalse(ld.isEnabled()); + } + + @Test + public void testSetEnabledNullThrowsException() { + LabelDebugger ld = new LabelDebugger(); + try { + ld.setEnabled(true); + } catch (Exception e) { + // We want to end up here + return; + } + Assert.fail(); + } + + @Test + public void testEnableIsTrue() { + LabelDebugger ld = new LabelDebugger(); + ld.enable(); + Assert.assertTrue(ld.isEnabled()); + } + + @Test + public void testDisableIsFalse() { + LabelDebugger ld = new LabelDebugger(); + ld.disable(); + Assert.assertFalse(ld.isEnabled()); + } + + + +}