Add CSP compliance to plugins

Change-Id: Ia13082bbad5348c8cbb5f5cb976bfe32c9fe0a27
diff --git a/Changes b/Changes
index 98225df..80b1bc0 100755
--- a/Changes
+++ b/Changes
@@ -15,6 +15,8 @@
         - Introduce X-Frame-Options header.
         - Introduce X-XSS-Protection header.
         - Support CSP in notifications framework.
+        - Fetch plugin configs from JSON file to be
+          CSP compliant.
 
 0.40 2020-12-17
         - Modernize ES and fix in-loops.
diff --git a/dev/js/src/api.js b/dev/js/src/api.js
index 75a4e68..cfc0625 100644
--- a/dev/js/src/api.js
+++ b/dev/js/src/api.js
@@ -107,7 +107,15 @@
   	let url  =  KorAP.URL + "/corpus?cq=" + encodeURIComponent(cq);
   	KorAP.API.getJSON(url, cb, "CorpusInfo: " + cq);
   };
-  
+
+
+  /**
+   * Retrieve a list of all plugin objects to
+   * establish in the frontend.
+   */
+  KorAP.API.getPluginList = function (url, cb) {
+    KorAP.API.getJSON(url, cb, "Plugin-List")
+  };
 
   /**
    * General method to retrieve JSON information
diff --git a/dev/js/src/init.js b/dev/js/src/init.js
index ef6dfcf..4f6074f 100644
--- a/dev/js/src/init.js
+++ b/dev/js/src/init.js
@@ -436,28 +436,35 @@
     /**
      * Initialize Plugin registry.
      */
-    let p = KorAP.Plugins;
-    if (p && p.length > 0) {
-      // Load Plugin Server first
-      KorAP.Plugin = pluginClass.create();
+    let pe;
+    if (pe = d.getElementById("kalamar-plugins")) {
+      let url = pe.getAttribute('data-plugins');
+      if (url !== undefined) {
+        KorAP.API.getPluginList(url, function (json) {
+          if (json && json.length > 0) {
+            // Load Plugin Server first
+            KorAP.Plugin = pluginClass.create();
 
-      // Add services container to head
-      d.head.appendChild(KorAP.Plugin.element());
+            // Add services container to head
+            d.head.appendChild(KorAP.Plugin.element());
 
-      // Add pipe form
-      KorAP.Pipe = pipeClass.create();
-      d.getElementById("searchform").appendChild(KorAP.Pipe.element());
-
-      try {
-      
-        // Register all plugins
-        p.forEach(i => KorAP.Plugin.register(i));
-      }
-      catch (e) {
-        KorAP.log(0, e);
-      }
+            // Add pipe form
+            KorAP.Pipe = pipeClass.create();
+            d.getElementById("searchform").appendChild(KorAP.Pipe.element());
+            
+            try {
+              
+              // Register all plugins
+              json.forEach(i => KorAP.Plugin.register(i));
+            }
+            catch (e) {
+              KorAP.log(0, e);
+            }
+          }
+        });
+      };
     };
-
+      
     return obj;
   });
   
diff --git a/lib/Kalamar/Plugin/Plugins.pm b/lib/Kalamar/Plugin/Plugins.pm
index 5e2e4a0..73dcdbe 100644
--- a/lib/Kalamar/Plugin/Plugins.pm
+++ b/lib/Kalamar/Plugin/Plugins.pm
@@ -1,5 +1,6 @@
 package Kalamar::Plugin::Plugins;
 use Mojo::Base 'Mojolicious::Plugin';
+use Mojo::JSON qw'decode_json';
 
 # Register the plugin
 sub register {
@@ -15,25 +16,36 @@
 
     # Read default plugins file
     my $default = Mojo::File->new($param->{default_plugins});
-    my $json_array = $default->slurp;
+    my $json_array = decode_json $default->slurp;
 
     # If any scripts are defined
     if ($json_array) {
 
       # TODO:
-      #   Make this CSP (#72) compliant.
+      #   Add user registered plugins as a path
+
+      # TODO:
+      #   Add sources to CORS.
 
       # Add default plugins, if exist
+      $app->routes->get('/settings/plugin/list.json')->to(
+        cb => sub {
+          my $c = shift;
+          $c->res->headers->cache_control('no-cache');
+          $c->render(
+            json => $json_array
+          );
+        }
+      )->name('plugin_list');
+
       $app->content_block(
         scripts => {
-          inline => "<script>//<![CDATA[\nKorAP.Plugins=" . $json_array . "\n//]]></script>"
+          inline => q!<span id="kalamar-plugins" ! .
+            q!data-plugins="<%== url_for 'plugin_list' %>"></span>!
         }
       );
     };
   };
-
-  # TODO:
-  #   Add user registered plugins as a path
 };
 
 
@@ -73,7 +85,7 @@
 
 =head2 COPYRIGHT AND LICENSE
 
-Copyright (C) 2020, L<IDS Mannheim|http://www.ids-mannheim.de/>
+Copyright (C) 2021, L<IDS Mannheim|http://www.ids-mannheim.de/>
 Author: L<Nils Diewald|http://nils-diewald.de/>
 
 Kalamar is developed as part of the L<KorAP|http://korap.ids-mannheim.de/>
diff --git a/package.json b/package.json
index 0678630..838d8b6 100755
--- a/package.json
+++ b/package.json
@@ -2,7 +2,7 @@
   "name": "Kalamar",
   "description": "Mojolicious-based Frontend for KorAP",
   "license": "BSD-2-Clause",
-  "version": "0.41.0",
+  "version": "0.41.1",
   "pluginVersion": "0.2.2",
   "engines": {
     "node": ">=6.0.0"
diff --git a/t/plugin/plugins.t b/t/plugin/plugins.t
index fa5ae91..eb8b2a2 100644
--- a/t/plugin/plugins.t
+++ b/t/plugin/plugins.t
@@ -10,16 +10,16 @@
 
 $temp->spurt(<<SCRIPT);
 [{
-  'name' : 'Export',
-  'desc' : 'Exports Kalamar results',
-  'embed' : [{
-    'panel' : 'result',
-    'title' : 'exports KWICs and snippets',
-    'icon' : "\uf019",
-    'classes' : ['button-icon','plugin'],
-    'onClick' : {
-      'action' : 'addWidget',
-      'template' : 'http://localhost:7777/res/export.html'
+  "name" : "Export",
+  "desc" : "Exports Kalamar results",
+  "embed" : [{
+    "panel" : "result",
+    "title" : "exports KWICs and snippets",
+    "icon" : "\uf019",
+    "classes" : ["button-icon","plugin"],
+    "onClick" : {
+      "action" : "addWidget",
+      "template" : "http://localhost:7777/res/export.html"
     }
   }]
 }]
@@ -31,8 +31,17 @@
 
 $t->get_ok('/')
   ->text_is('h1 span', 'KorAP - Corpus Analysis Platform')
-  ->content_like(qr!KorAP\.Plugins\s*=\s*\[!)
-  ->content_like(qr!<script>\/\/<\!\[CDATA\[!)
+  ->content_unlike(qr!KorAP\.Plugins\s*=\s*\[!)
+  ->content_unlike(qr!<script>\/\/<\!\[CDATA\[!)
+  ->content_like(qr!<span id="kalamar-plugins" data-plugins="/settings/plugin/list\.json"></span>!)
+  ;
+
+$t->get_ok('/settings/plugin/list.json')
+  ->status_is(200)
+  ->header_is('Content-Type','application/json;charset=UTF-8')
+  ->content_unlike(qr!KorAP\.Plugins=!)
+  ->content_like(qr!button-icon!)
+  ->json_is('/0/embed/0/title','exports KWICs and snippets')
   ;
 
 done_testing;