blob: 82a4193a644cd3c99595a9a48c3b408759b04717 [file] [log] [blame]
Akron479994e2018-07-02 13:21:44 +02001/**
2 * The plugin system is based
3 * on registered widgets (iframes) from
4 * foreign services.
5 * The server component spawns new iframes and
6 * listens to them.
7 *
8 * @author Nils Diewald
9 */
10
Akrona6c32b92018-07-02 18:39:42 +020011define(["plugin/widget", "util"], function (widgetClass) {
Akron479994e2018-07-02 13:21:44 +020012 "use strict";
13
Akrona6c32b92018-07-02 18:39:42 +020014 // Contains all widgets to address with
15 // messages to them
16 var widgets = {};
17
Akrone8e2c952018-07-04 13:43:12 +020018 // This is a counter to limit acceptable incoming messages
19 // to a certain amount. For every message, this counter will
20 // be decreased (down to 0), for every second this will be
21 // increased (up to 100).
22 // Once a widget surpasses the limit, it will be killed
23 // and called suspicious.
24 var maxMessages = 100;
25 var limits = {};
26
27 // TODO:
28 // It may be useful to establish a watcher that pings
29 // all widgets every second to see if it is still alive.
30
Akron479994e2018-07-02 13:21:44 +020031 return {
32
33 /**
34 * Create new plugin management system
35 */
36 create : function () {
37 return Object.create(this)._init();
38 },
39
40 /*
41 * Initialize the plugin manager by establishing
42 * the global 'message' hook.
43 */
44 _init : function () {
45
Akrone8e2c952018-07-04 13:43:12 +020046 // TODO:
47 // It is better to establish the listener
48 // only in case there is a widget
49
Akron479994e2018-07-02 13:21:44 +020050 var that = this;
Akronb43c8c62018-07-04 18:27:28 +020051 this._listener = this._receiveMsg.bind(that);
52 window.addEventListener("message", this._listener);
Akrona99315e2018-07-03 22:56:45 +020053
54 // Every second increase the limits of all registered widgets
55 var myTimer = setInterval(function () {
56 for (var i in limits) {
57 if (limits[i]++ >= maxMessages) {
58 limits[i] = maxMessages;
59 }
60 }
61 }, 1000);
Akron479994e2018-07-02 13:21:44 +020062 return this;
63 },
64
65 /**
Akrone8e2c952018-07-04 13:43:12 +020066 * Open a new widget as a child to a certain element
Akron479994e2018-07-02 13:21:44 +020067 */
68 addWidget : function (element, src) {
69
Akrona6c32b92018-07-02 18:39:42 +020070 // Create a unique random ID per widget
71 var id = 'id-' + this._randomID();
72
73 // Create a new widget
74 var widget = widgetClass.create(src, id);
75
76 // Store the widget based on the identifier
77 widgets[id] = widget;
Akrona99315e2018-07-03 22:56:45 +020078 limits[id] = maxMessages;
Akrona6c32b92018-07-02 18:39:42 +020079
80 // Open widget in frontend
81 element.appendChild(
82 widget.element()
83 );
Akronb43c8c62018-07-04 18:27:28 +020084
85 return id;
Akron479994e2018-07-02 13:21:44 +020086 },
87
Akrone8e2c952018-07-04 13:43:12 +020088 // Receive a call from an embedded iframe.
89 // The handling needs to be very careful,
90 // as this can easily become a security nightmare.
Akron479994e2018-07-02 13:21:44 +020091 _receiveMsg : function (e) {
92 // Get event data
93 var d = e.data;
94
Akrona99315e2018-07-03 22:56:45 +020095 // If no data given - fail
96 // (probably check that it's an assoc array)
97 if (!d)
98 return;
99
100 // e.origin is probably set and okay - CHECK!
Akron479994e2018-07-02 13:21:44 +0200101
Akrona99315e2018-07-03 22:56:45 +0200102 // Get origin ID
103 var id = d["originID"];
104
105 // If no origin ID given - fail
106 if (!id)
107 return;
108
Akrona6c32b92018-07-02 18:39:42 +0200109 // Get the widget
Akrona99315e2018-07-03 22:56:45 +0200110 var widget = widgets[id];
Akrona6c32b92018-07-02 18:39:42 +0200111
112 // If the addressed widget does not exist - fail
113 if (!widget)
114 return;
115
Akrona99315e2018-07-03 22:56:45 +0200116 // Check for message limits
117 if (limits[id]-- < 0) {
Akrone8e2c952018-07-04 13:43:12 +0200118
119 // Kill widget
Akronc0a2da82018-07-04 15:27:37 +0200120 KorAP.log(0, 'Suspicious action by widget', widget.src);
Akrona99315e2018-07-03 22:56:45 +0200121 widget.shutdown();
122 delete limits[id];
123 delete widgets[id];
124 return;
125 };
Akrona6c32b92018-07-02 18:39:42 +0200126
Akron479994e2018-07-02 13:21:44 +0200127 // Resize the iframe
128 if (d.action === 'resize') {
Akrona6c32b92018-07-02 18:39:42 +0200129 widget.resize(d);
Akron479994e2018-07-02 13:21:44 +0200130 }
131
132 // Log message from iframe
133 else if (d.action === 'log') {
Akronc0a2da82018-07-04 15:27:37 +0200134 KorAP.log(d.code, d.msg, widget.src);
Akrona6c32b92018-07-02 18:39:42 +0200135 };
136
137 // TODO:
138 // Close
Akron479994e2018-07-02 13:21:44 +0200139 },
140
Akrona6c32b92018-07-02 18:39:42 +0200141 // Get a random identifier
142 _randomID : function () {
143 return randomID(20);
Akronb43c8c62018-07-04 18:27:28 +0200144 },
145
146 // Destructor, just for testing scenarios
147 destroy : function () {
148 limits = {};
149 widgets = {};
150 window.removeEventListener("message", this._listener);
Akron479994e2018-07-02 13:21:44 +0200151 }
152 }
153});