<html xmlns:v="urn:schemas-microsoft-com:vml" xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:w="urn:schemas-microsoft-com:office:word" xmlns:m="http://schemas.microsoft.com/office/2004/12/omml" xmlns="http://www.w3.org/TR/REC-html40">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta name="Generator" content="Microsoft Word 14 (filtered medium)">
<!--[if !mso]><style>v\:* {behavior:url(#default#VML);}
o\:* {behavior:url(#default#VML);}
w\:* {behavior:url(#default#VML);}
.shape {behavior:url(#default#VML);}
</style><![endif]--><style><!--
/* Font Definitions */
@font-face
        {font-family:Calibri;
        panose-1:2 15 5 2 2 2 4 3 2 4;}
@font-face
        {font-family:Tahoma;
        panose-1:2 11 6 4 3 5 4 4 2 4;}
/* Style Definitions */
p.MsoNormal, li.MsoNormal, div.MsoNormal
        {margin:0in;
        margin-bottom:.0001pt;
        font-size:12.0pt;
        font-family:"Times New Roman","serif";}
a:link, span.MsoHyperlink
        {mso-style-priority:99;
        color:blue;
        text-decoration:underline;}
a:visited, span.MsoHyperlinkFollowed
        {mso-style-priority:99;
        color:purple;
        text-decoration:underline;}
p
        {mso-style-priority:99;
        margin:0in;
        margin-bottom:.0001pt;
        font-size:12.0pt;
        font-family:"Times New Roman","serif";}
span.EmailStyle19
        {mso-style-type:personal-reply;
        font-family:"Calibri","sans-serif";
        color:#1F497D;}
.MsoChpDefault
        {mso-style-type:export-only;
        font-size:10.0pt;}
@page WordSection1
        {size:8.5in 11.0in;
        margin:1.0in 1.0in 1.0in 1.0in;}
div.WordSection1
        {page:WordSection1;}
/* List Definitions */
@list l0
        {mso-list-id:306739626;
        mso-list-template-ids:-2112482452;}
ol
        {margin-bottom:0in;}
ul
        {margin-bottom:0in;}
--></style><!--[if gte mso 9]><xml>
<o:shapedefaults v:ext="edit" spidmax="1026" />
</xml><![endif]--><!--[if gte mso 9]><xml>
<o:shapelayout v:ext="edit">
<o:idmap v:ext="edit" data="1" />
</o:shapelayout></xml><![endif]-->
</head>
<body lang="EN-US" link="blue" vlink="purple">
<div class="WordSection1">
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;color:#1F497D">Thanks Chris and Vojtech for continuing this discussion.&nbsp; I think I’m missing the link between providing the plugin definition file and defining the plugins.&nbsp;
 If I want to add 3 main tabs and 6 context menus, do I provide 9 plugin definitions?&nbsp; Or do I provide 1 plugin definition with multiple “urls” where each one points to a distinct path?<o:p></o:p></span></p>
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;color:#1F497D"><o:p>&nbsp;</o:p></span></p>
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;color:#1F497D">If “url” is configured to point to an external application server hosting my plugin, what is the intent of “path”?&nbsp; For example, if I configure “url” to point
 to “https://10.10.10.10/myplugin/entrypoint.html” then presumably the application server will render the page it needs as a main tab or context menu.&nbsp; It would have no need for “path” since all dependencies would be resolved by the application server.<o:p></o:p></span></p>
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;color:#1F497D"><o:p>&nbsp;</o:p></span></p>
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;color:#1F497D">George<o:p></o:p></span></p>
<p class="MsoNormal"><a name="_MailEndCompose"><span style="font-size:11.0pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;color:#1F497D"><o:p>&nbsp;</o:p></span></a></p>
<div>
<div style="border:none;border-top:solid #B5C4DF 1.0pt;padding:3.0pt 0in 0in 0in">
<p class="MsoNormal"><b><span style="font-size:10.0pt;font-family:&quot;Tahoma&quot;,&quot;sans-serif&quot;">From:</span></b><span style="font-size:10.0pt;font-family:&quot;Tahoma&quot;,&quot;sans-serif&quot;"> engine-devel-bounces@ovirt.org [mailto:engine-devel-bounces@ovirt.org]
<b>On Behalf Of </b>Vojtech Szocs<br>
<b>Sent:</b> Thursday, August 23, 2012 8:14 AM<br>
<b>To:</b> Chris Frantz<br>
<b>Cc:</b> engine-devel<br>
<b>Subject:</b> Re: [Engine-devel] UI Plugins configuration<o:p></o:p></span></p>
</div>
</div>
<p class="MsoNormal"><o:p>&nbsp;</o:p></p>
<div>
<p class="MsoNormal"><span style="color:black">Hi Chris,<br>
<br>
thanks for taking the time to make this patch, these are some excellent ideas! (CC'ing engine-devel so that we can discuss this with other guys as well)<br>
<br>
First of all, I really like the way you designed plugin source page URLs (going through
<em>PluginSourcePageServlet</em>), e.g. &quot;/webadmin/webadmin/plugin/&lt;pluginName&gt;/&lt;pluginSourcePage&gt;.html&quot;, plus the concept of &quot;path&quot; JSON attribute.<br>
<br>
<i>WebadminDynamicHostingServlet</i> loads and caches all plugin definitions (<i>*.json</i> files), and directly embeds them into WebAdmin host page as
<i>pluginDefinitions</i> JavaScript object. I'm assuming that <i>pluginDefinitions</i> object will now look like this:<br>
<br>
</span><span style="font-family:&quot;Courier New&quot;;color:black">var pluginDefinitions = {<br>
&nbsp;&nbsp;&quot;test&quot;: {<br>
&nbsp;&nbsp; &nbsp;&quot;name&quot;: &quot;test&quot;,<br>
&nbsp;&nbsp; &nbsp;&quot;version&quot;: &quot;1.0&quot;,<br>
&nbsp;&nbsp; &nbsp;&quot;url&quot;: &quot;/webadmin/webadmin/plugin/test/foo.html&quot;,<br>
&nbsp;&nbsp; &nbsp;&quot;path&quot;: &quot;/tmp&quot;,<br>
&nbsp;&nbsp; &nbsp;&quot;config&quot;: {&quot;a&quot;:1, &quot;b&quot;:2, &quot;c&quot;:3}<br>
&nbsp;&nbsp;}<br>
}</span><span style="color:black"><br>
<br>
Originally, the <i>pluginDefinitions</i> object looked like this:<br>
</span><span style="font-family:&quot;Courier New&quot;;color:black"><br>
var pluginDefinitions = {<br>
&nbsp;&nbsp;&quot;test&quot;: &quot;/webadmin/webadmin/plugin/test/foo.html&quot; // Simple pluginName -&gt; pluginSourcePageUrl mappings<br>
}</span><span style="color:black"><br>
<br>
This is because PluginManager (WebAdmin) only needs <i>pluginName</i> (&quot;name&quot;) and
<i>pluginSourcePageUrl</i> (&quot;url&quot;) during startup, when creating plugin iframe. But this can be changed :)<br>
<br>
Plugin &quot;version&quot; makes sense, plus the plugin configuration object (&quot;config&quot;) can be useful directly on the client. Let me explain:<br>
<br>
Originally, plugin configuration was supposed to be passed to actual plugin code (through immediately-invoked-function-expression, or IIFE), just like this:<br>
</span><span style="font-family:&quot;Courier New&quot;;color:black"><br>
(function (pluginApi, pluginConfig) { // JavaScript IIFE<br>
&nbsp;&nbsp;// ... actual plugin code ...<br>
})(<br>
&nbsp;&nbsp;parent.pluginApi, /* reference to global pluginApi object */<br>
&nbsp;&nbsp;{&quot;a&quot;:1, &quot;b&quot;:2, &quot;c&quot;:3} /* embedded plugin configuration as JavaScript object */<br>
);</span><span style="color:black"><br>
<br>
The whole purpose of <i>PluginSourcePageServlet</i> was to &quot;wrap&quot; actual plugin code into HTML, so that users don't need to write HTML pages for their plugins manually.
<i>PluginSourcePageServlet</i> would handle any plugin dependencies (placed into HTML head), with actual plugin code being wrapped into IIFE, as shown above. Plugin configuration was meant to be stored in a separate file, e.g.
<i>&lt;pluginName&gt;-config.json</i>, so that users could change the default plugin configuration to suit their needs.<br>
<br>
Inspired by your patch, rather than reading/embedding plugin configuration when serving plugin HTML page (<i>PluginSourcePageServlet</i>), it's even better to have the plugin configuration embedded directly into WebAdmin host page, along with introducing new
<i>pluginApi</i> function to retrieve the plugin configuration object.<br>
<br>
Based on this, I suggest following modifications to the original concept:<br>
<br>
- modify original <i>pluginDefinitions</i> structure, from <i>pluginName -&gt; pluginSourcePageUrl</i>, to
<i>pluginName -&gt; pluginDefObject</i><br>
- <i>pluginDefObject</i> is basically a subset of physical plugin definition (<i>test.json</i>, see below), suitable for use on the client<br>
- add following attributes to <i>pluginDefObject</i>: <i>version</i>, <i>url</i>,
<i>config</i><br>
&nbsp;&nbsp;* note #1: <i>name</i> is not needed, since it's already the key of <i>pluginName -&gt; pluginDefObject</i> mapping<br>
&nbsp;&nbsp;* note #2: <i>path</i> is not needed on the client (more on this below)<br>
- introduce <i>pluginApi.config(pluginName)</i> function for plugins to retrieve their configuration object, and remove
<i>pluginConfig</i> parameter from main IIFE (as shown above)<br>
<br>
[a] Physical plugin definition file (JSON) might be located at oVirt &quot;DataDir&quot;, e.g.
<i>/usr/share/ovirt-engine/ui-plugins/test.json</i>, for example:<br>
</span><span style="font-family:&quot;Courier New&quot;;color:black"><br>
{<br>
&nbsp;&nbsp;&quot;name&quot;: &quot;test&quot;,<br>
&nbsp;&nbsp;&quot;version&quot;: &quot;1.0&quot;,<br>
&nbsp;&nbsp;&quot;url&quot;: &quot;/webadmin/webadmin/plugin/test/start.html&quot;,<br>
&nbsp;&nbsp;&quot;path&quot;: &quot;/tmp&quot;,<br>
&nbsp;&nbsp;&quot;config&quot;: &quot;test-config.json&quot;<br>
}</span><span style="color:black"><br>
<br>
[b] Plugin configuration file (JSON) might be located at oVirt &quot;ConfigDir&quot;, e.g. <i>
/etc/ovirt-engine/ui-plugins/test-config.json</i>, for example:<br>
</span><span style="font-family:&quot;Courier New&quot;;color:black"><br>
{<br>
&nbsp;&nbsp;&quot;a&quot;:1, &quot;b&quot;:2, &quot;c&quot;:3<br>
}</span><span style="color:black"><br>
<br>
[c] Finally, plugin static resources (plugin source page, actual plugin code, plugin dependencies, CSS/images, etc.) would be located at
<i>/tmp</i> (as shown in [a]), for example:<br>
</span><span style="font-family:&quot;Courier New&quot;;color:black"><br>
/tmp/start.html -&gt; plugin source page, used to load actual plugin code<br>
/tmp/test.js -&gt; actual plugin code<br>
/tmp/deps/jquery-min.js -&gt; simulate 3rd party plugin dependency</span><span style="color:black"><br>
<br>
For example:<br>
&quot;/webadmin/webadmin/plugin/test/start.html&quot; will be mapped to <i>/tmp/start.html</i><br>
&quot;/webadmin/webadmin/plugin/test/deps/jquery-min.js&quot; will be mapped to <i>/tmp/deps/jquery-min.js</i><br>
<br>
This approach has some pros and cons:<br>
(&#43;) plugin static resources can be served through <i>PluginSourcePageServlet</i> (pretty much like oVirt documentation resources, served through oVirt Engine root war's
<i>FileServlet</i>)<br>
(&#43;) plugin author has complete control over plugin source page<br>
(-) plugin author actually needs to write plugin source page<br>
<br>
Overall, I think this approach is better than the previous one (where <i>PluginSourcePageServlet</i> took care of rendering plugin source page, but sacrificed some flexibility).<br>
<br>
By the way, here's what would happen behind the scenes:<o:p></o:p></span></p>
<ol start="1" type="1">
<li class="MsoNormal" style="color:black;mso-margin-top-alt:auto;margin-bottom:12.0pt;mso-list:l0 level1 lfo1">
user requests WebAdmin host page, <i>WebadminDynamicHostingServlet</i> loads and caches all plugin definitions [a] &#43; plugin configurations [b] and constructs/embeds appropriate
<i>pluginDefinitions</i> JavaScript object<o:p></o:p></li><li class="MsoNormal" style="color:black;mso-margin-top-alt:auto;margin-bottom:12.0pt;mso-list:l0 level1 lfo1">
during WebAdmin startup, <i>PluginManager</i> registers the plugin (name/version/url/config), and creates/attaches the iframe to fetch plugin source page ansynchronously<o:p></o:p></li><li class="MsoNormal" style="color:black;mso-margin-top-alt:auto;margin-bottom:12.0pt;mso-list:l0 level1 lfo1">
<i>PluginSourcePageServlet</i> handles plugin source page request, resolves the correct path [c] and just streams the file content back to client<o:p></o:p></li></ol>
<p class="MsoNormal" style="margin-bottom:12.0pt"><span style="color:black">&gt; 1. &nbsp;The plugin configuration files should probably have an &quot;enabled&quot; field and an &quot;apiVersion&quot; field that should be examined to determine whether or not to use the plugin.<br>
<br>
Sounds good, we can implement these later on :)<br>
<br>
&gt; 2. &nbsp;I suspect the way I've modified PluginSourcePage makes it vulnerable to directory climbing attacks.<br>
<br>
Yes, but we can defend against these, restricting access only to plugin's &quot;path&quot; and its sub-directories.<br>
<br>
&gt; 3. &nbsp;Is /usr/share/ovirt-engine the right place for the plugin config files?<br>
<br>
I suppose you mean plugin definition files [a], cannot tell for sure, but we can change this anytime :)<br>
<br>
<br>
Chris, please let me know what you think, and again - many thanks for sending the patch!<br>
<br>
<br>
Regards,<br>
Vojtech<br>
<br>
<o:p></o:p></span></p>
<div class="MsoNormal" align="center" style="text-align:center"><span style="color:black">
<hr size="2" width="100%" align="center" id="zwchr">
</span></div>
<p class="MsoNormal" style="margin-bottom:12.0pt"><span style="color:black"><br>
From: &quot;Chris Frantz&quot; &lt;<a href="mailto:Chris.Frantz@hp.com">Chris.Frantz@hp.com</a>&gt;<br>
To: <a href="mailto:vszocs@redhat.com">vszocs@redhat.com</a><br>
Sent: Wednesday, August 22, 2012 7:56:45 PM<br>
Subject: UI Plugins configuration<br>
<br>
Vojtech,<br>
<br>
I decided to work on making the plugin patch a bit more configurable, following some of the ideas expressed by Itamar and others in the meeting yesterday. &nbsp;The attached patch is a simple first-attempt.<br>
<br>
Plugin configurations are stored in /usr/share/ovirt-engine/ui-plugins/*.json.<br>
<br>
Example:<br>
{<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&quot;name&quot;: &quot;test&quot;,<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&quot;version&quot;: &quot;1.0&quot;,<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&quot;url&quot;: &quot;/webadmin/webadmin/plugin/test/foo.html&quot;,<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&quot;path&quot;: &quot;/tmp&quot;,<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&quot;config&quot;: {&quot;a&quot;:1, &quot;b&quot;:2, &quot;c&quot;: 3}<br>
}<br>
<br>
The engine reads all of the *.json files in that directory to build the list of known plugins and gives that list to the webadmin.<br>
<br>
When webadmin loads a plugin, it requests the URL given in the plugin config file. &nbsp;The &quot;plugin&quot; URL is mapped to PluginSourcePage, which will translate the first part of the path (&quot;test&quot;) into whatever path is stored in pluginConfig (&quot;/tmp&quot;) in this case,
 and then serve the static file (e.g. &quot;/tmp/foo.html&quot;).<br>
<br>
I didn't use the renderPluginSourcePage() method in favor of just serving a static file, but I have no strong opinion on the matter. &nbsp;However, a plugin may want to store static resources at &quot;path&quot; and have the engine serve those resources. &nbsp;By just serving
 files through PluginSourcePage, we don't need any other servlets to provide those resources.<br>
<br>
There is still a bit of work to do:<br>
<br>
1. &nbsp;The plugin configuration files should probably have an &quot;enabled&quot; field and an &quot;apiVersion&quot; field that should be examined to determine whether or not to use the plugin.<br>
<br>
2. &nbsp;I suspect the way I've modified PluginSourcePage makes it vulnerable to directory climbing attacks.<br>
<br>
3. &nbsp;Is /usr/share/ovirt-engine the right place for the plugin config files?<br>
<br>
Let me know what you think,<br>
--Chris<o:p></o:p></span></p>
</div>
</div>
</body>
</html>