<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 12 (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:Helvetica;
        panose-1:2 11 6 4 2 2 2 2 2 4;}
@font-face
        {font-family:"Cambria Math";
        panose-1:2 4 5 3 5 4 6 3 2 4;}
@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";}
p.MsoAcetate, li.MsoAcetate, div.MsoAcetate
        {mso-style-priority:99;
        mso-style-link:"Balloon Text Char";
        margin:0in;
        margin-bottom:.0001pt;
        font-size:8.0pt;
        font-family:"Tahoma","sans-serif";}
span.EmailStyle20
        {mso-style-type:personal;
        font-family:"Calibri","sans-serif";
        color:#1F497D;}
span.BalloonTextChar
        {mso-style-name:"Balloon Text Char";
        mso-style-priority:99;
        mso-style-link:"Balloon Text";
        font-family:"Tahoma","sans-serif";}
span.EmailStyle23
        {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:189882560;
        mso-list-template-ids:-174563476;}
@list l0:level1
        {mso-level-tab-stop:.5in;
        mso-level-number-position:left;
        text-indent:-.25in;}
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">Vojtech,<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">I agree with your formalized names:<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"><i><span style="font-size:11.0pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;color:#1F497D">Plugin Descriptor</span></i><span style="font-size:11.0pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;color:#1F497D"> is the JSON file containing plugin meta-data.
 The plugin descriptor may also contain the default configuration data.&nbsp; &nbsp;It is located in $DATADIR/ui-plugins.<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"><i><span style="font-size:11.0pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;color:#1F497D">Plugin Configuration</span></i><span style="font-size:11.0pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;color:#1F497D"> is the JSON file containing optional plugin
 configuration info.&nbsp; It is located in $CONFIGDIR/ui-plugins (unless the Plugin Descriptor contains an absolute path).<o:p></o:p></span></p>
<p class="MsoNormal"><i><span style="font-size:11.0pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;color:#1F497D"><o:p>&nbsp;</o:p></span></i></p>
<p class="MsoNormal"><i><span style="font-size:11.0pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;color:#1F497D">Plugin Definition</span></i><span style="font-size:11.0pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;color:#1F497D"> is the JavaScript object used by WebAdmin.&nbsp;
 In the current implementation, the Plugin Definition contains both the Plugin Descriptor and the Plugin Configuraion.<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"><i><span style="font-size:11.0pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;color:#1F497D">Plugin Source Page</span></i><span style="font-size:11.0pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;color:#1F497D"> is the HTML page used to invoke the plugin
 code and shall be referenced by the plugin descriptor’s “url” attribute.<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">I’ve implemented the config merging you’ve suggested:&nbsp; the structure in configFile gets merged with the structure of “config”, with the data in configFile winning
 in the case of duplicate key names.<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">BTW, the patch is against ovirt-engine &#43; 0001-WIP-UI-Plugins-PoC-revision-2.<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">Let me know what you think,<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">--Chris<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"><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"><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"><o:p>&nbsp;</o:p></span></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;"> Vojtech Szocs [mailto:vszocs@redhat.com]
<br>
<b>Sent:</b> Monday, August 27, 2012 9:49 AM<br>
<b>To:</b> Frantz, Chris<br>
<b>Cc:</b> engine-devel<br>
<b>Subject:</b> Re: UI Plugins configuration<o:p></o:p></span></p>
</div>
</div>
<p class="MsoNormal"><o:p>&nbsp;</o:p></p>
<div>
<p class="MsoNormal" style="margin-bottom:12.0pt"><span style="color:black">Hi Chris,<br>
<br>
</span><span style="font-size:11.0pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;color:#1F497D">&gt; Your assumption about the structure of the pluginDefinitions object is correct.&nbsp; It’s no longer a String-&gt;String mapping , but a String to Object mapping.</span><span style="color:black"><br>
<br>
Yes :) but maybe we could also formalize some terms, for example:<br>
<br>
<em>Plugin descriptor</em> is the JSON file that contains important plugin meta-data (including the plugin source page URL), e.g.
<i>/usr/share/ovirt-engine/ui-plugins/test.json<br>
</i><br>
<i>Plugin definition</i> is the JavaScript object representing plugin descriptor meta-data suitable for use on client (GWT WebAdmin). Plugin definition is embedded into WebAdmin host page within
<i>pluginDefinitions</i> object, and read by <i>PluginManager</i> during WebAdmin startup.<br>
<i><br>
Plugin configuration</i> is the JSON file that contains <strong>optional</strong> plugin configuration, e.g.
<i>/etc/ovirt-engine/ui-plugins/test-config.json</i><br>
<i><br>
</i>I think we can combine two things here:<br>
1) allow plugin authors to define standard (fallback) configuration directly inside plugin descriptor<br>
2) allow plugin users to override standard configuration by modifying dedicated plugin configuration file<br>
<br>
Finally, <i>plugin source page</i> is the HTML page used to invoke actual plugin code (this page is referenced by plugin descriptor's &quot;url&quot; attribute). Plugin source page can also load external resources required by the plugin, e.g. 3rd party JavaScript libraries,
 CSS, images, etc.<br>
<br>
</span><span style="font-size:11.0pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;color:#1F497D">&gt; I liked the original IIFE approach, except that it seemed that having additional static resources (jquery, images, html templates, etc) was going to be more cumbersome.&nbsp;
 I don’t think having the plugin author write a basic start.html is that big of a burden :).</span><span style="color:black"><br>
<br>
You're right, for such additional plugin resources, even more configuration/parsing/logic would be required. Even though plugin authors need to write the plugin source page themselves, they have full control over it, which is a good thing in general.<br>
<br>
</span><span style="font-size:11.0pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;color:#1F497D">&gt; I agree that the plugin configuration was always going to be a resource (probably a local file) that the end user could customize.&nbsp; I’m not sure it I really needs to be
 separate from the plugin definition file (/usr/share/ovirt-engine/ui-plugins/test.json).&nbsp; I suppose it depends on how complex the configuration is going to be and on some of the implementation details surrounding the plugin definition file.</span><span style="color:black"><br>
<br>
Yeah, let's make the concept of the plugin configuration file optional for now (standard plugin configuration can be part of plugin descriptor).<br>
<br>
</span><span style="font-size:11.0pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;color:#1F497D">&gt; In my patch, I simply used Jackson to parse the file into a tree of JsonNodes. &nbsp;Should the plugin definition be a java object of some sort? (please please please don’t
 make me learn about java beans…).&nbsp; I stuck with the JsonNodes because Jackson makes them easy to work with and they’re really easy to re-serialize back to json to give to the webadmin.</span><span style="color:black"><br>
<br>
I think using Jackson's JSON representation in Java (JsonNode) is perfectly suitable in this situation. No need to have separate Java bean for that :)<br>
<br>
</span><span style="font-size:11.0pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;color:#1F497D">&gt; We should probably turn on JsonParser.Feature.ALLOW_COMMENTS.&nbsp; The definition and config files will difficult for end-users (or even developers) to understand without comments.</span><span style="color:black"><br>
<br>
Agreed.<br>
<br>
</span><span style="font-size:11.0pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;color:#1F497D">&gt; We need to formalize the structure of the plugin definition and decide which fields are mandatory and which are optional</span><span style="color:black"><br>
<br>
Sounds good, but I'd skip some attributes for now (enabled, apiVersion, author, license) for the sake of simplicity.<br>
<br>
As you wrote, when loading plugin descriptor, we should enforce mandatory attributes (name, version, url).<br>
<br>
As for plugin configuration, there could be two different attributes:<br>
- &quot;config&quot; for standard (fallback) plugin configuration (JSON object)<br>
- &quot;configFile&quot; for external plugin configuration file (path to file, relative to /etc/ovirt-engine/ui-plugins/), that overrides the standard configuration<br>
<br>
Note that when loading plugin descriptor, the loader should also &quot;merge&quot; the configuration together (custom config on top of standard config).<br>
<br>
</span><span style="font-size:11.0pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;color:#1F497D">&gt; I can work on the plugin Definition loader some more and make it enforce mandatory/optional fields. &nbsp;I’ll also investigate the directory climbing issue I mentioned in my
 previous mail.</span><span style="color:black"><br>
<br>
Sounds good! I was planning to incorporate your original patch in next PoC revision, but of course, you can work on the loader some more and send another patch :)<br>
<br>
For the directory climbing issue, see <i>&lt;OVIRT_ROOT&gt;/backend/manager/modules/root/src/main/java/org/ovirt/engine/core/FileServlet.java</i> (there's a method called isSane for dealing with such issue).<br>
<br>
</span><span style="font-size:11.0pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;color:#1F497D">&gt; Also, I’m curious how things are going to work when the “url” points to a foreign resource as the plugin start page.&nbsp; I don’t think the plugin’s iframe is going to be able
 to access parent.pluginApi.&nbsp; Perhaps there is some aspect of CORS that I don’t understand?</span><span style="color:black"><br>
<br>
When the plugin iframe references a resource on different origin (protocol, domain, port) than WebAdmin main page origin, JavaScript code running inside that iframe will not be able to access parent (top-level)
<i>pluginApi</i> object. You're right, the statement &quot;parent.pluginApi&quot; will not work, because of Same-Origin Policy enforced by the browser.<br>
<br>
CORS is just one alternative, see <a href="http://stackoverflow.com/questions/3076414/ways-to-circumvent-the-same-origin-policy">
http://stackoverflow.com/questions/3076414/ways-to-circumvent-the-same-origin-policy</a> for more. However, CORS needs to be supported by the browser (a special HTTP response header is used to tell that the iframe is allowed to access resources from another
 - WebAdmin main page - origin). We need to investigate this a bit more I guess.<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>
<div>
<p class="MsoNormal" style="margin-bottom:12.0pt"><b><span style="font-family:&quot;Helvetica&quot;,&quot;sans-serif&quot;;color:black">From:
</span></b><span style="font-family:&quot;Helvetica&quot;,&quot;sans-serif&quot;;color:black">&quot;Chris Frantz&quot; &lt;<a href="mailto:Chris.Frantz@hp.com">Chris.Frantz@hp.com</a>&gt;<br>
<b>To: </b>&quot;Vojtech Szocs&quot; &lt;<a href="mailto:vszocs@redhat.com">vszocs@redhat.com</a>&gt;<br>
<b>Cc: </b>&quot;engine-devel&quot; &lt;<a href="mailto:engine-devel@ovirt.org">engine-devel@ovirt.org</a>&gt;<br>
<b>Sent: </b>Thursday, August 23, 2012 5:12:02 PM<br>
<b>Subject: </b>RE: UI Plugins configuration<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">Vojtech,</span><span style="color:black"><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">&nbsp;</span><span style="color:black"><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">Your assumption about the structure of the pluginDefinitions object is correct.&nbsp; It’s no longer a String-&gt;String mapping , but a String to Object mapping.</span><span style="color:black"><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">&nbsp;</span><span style="color:black"><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">I liked the original IIFE approach, except that it seemed that having additional static resources (jquery, images, html templates, etc) was going to be more
 cumbersome.&nbsp; I don’t think having the plugin author write a basic start.html is that big of a burden :).</span><span style="color:black"><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">&nbsp;</span><span style="color:black"><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">I agree that the plugin configuration was always going to be a resource (probably a local file) that the end user could customize.&nbsp; I’m not sure it I really
 needs to be separate from the plugin definition file (/usr/share/ovirt-engine/ui-plugins/test.json).&nbsp; I suppose it depends on how complex the configuration is going to be and on some of the implementation details surrounding the plugin definition file.</span><span style="color:black"><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">&nbsp;</span><span style="color:black"><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">In my patch, I simply used Jackson to parse the file into a tree of JsonNodes. &nbsp;Should the plugin definition be a java object of some sort? (please please please
 don’t make me learn about java beans…).&nbsp; I stuck with the JsonNodes because Jackson makes them easy to work with and they’re really easy to re-serialize back to json to give to the webadmin.</span><span style="color:black"><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">&nbsp;</span><span style="color:black"><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">We should probably turn on JsonParser.Feature.ALLOW_COMMENTS.&nbsp; The definition and config files will difficult for end-users (or even developers) to understand
 without comments.</span><span style="color:black"><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">&nbsp;</span><span style="color:black"><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">We need to formalize the structure of the plugin definition and decide which fields are mandatory and which are optional:</span><span style="color:black"><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">&nbsp;</span><span style="color:black"><o:p></o:p></span></p>
<p class="MsoNormal"><span style="font-family:&quot;Courier New&quot;;color:black">{</span><span style="color:black"><o:p></o:p></span></p>
<p class="MsoNormal"><span style="font-family:&quot;Courier New&quot;;color:black">&nbsp; # Mandatory fields: name, enabled, version, url, apiversion, author, license<br>
&nbsp; # Name of the plugin&nbsp; </span><span style="color:black"><o:p></o:p></span></p>
<p class="MsoNormal" style="margin-bottom:12.0pt"><span style="font-family:&quot;Courier New&quot;;color:black">&nbsp;&nbsp;&quot;name&quot;: &quot;test&quot;,</span><span style="color:black"><o:p></o:p></span></p>
<p class="MsoNormal"><span style="font-family:&quot;Courier New&quot;;color:black">&nbsp; # Whether or not plugin is enabed</span><span style="color:black"><o:p></o:p></span></p>
<p class="MsoNormal" style="margin-bottom:12.0pt"><span style="font-family:&quot;Courier New&quot;;color:black">&nbsp;&nbsp;&quot;enabled&quot;: true,</span><span style="color:black"><o:p></o:p></span></p>
<p class="MsoNormal"><span style="font-family:&quot;Courier New&quot;;color:black">&nbsp; # version of the plugin</span><span style="color:black"><o:p></o:p></span></p>
<p class="MsoNormal" style="margin-bottom:12.0pt"><span style="font-family:&quot;Courier New&quot;;color:black">&nbsp;&nbsp;&quot;version&quot;: &quot;1.0&quot;,</span><span style="color:black"><o:p></o:p></span></p>
<p class="MsoNormal"><span style="font-family:&quot;Courier New&quot;;color:black">&nbsp; # How to load the plugin</span><span style="color:black"><o:p></o:p></span></p>
<p class="MsoNormal" style="margin-bottom:12.0pt"><span style="font-family:&quot;Courier New&quot;;color:black">&nbsp;&nbsp;&quot;url&quot;: &quot;/webadmin/webadmin/plugin/test/start.html&quot;,</span><span style="color:black"><o:p></o:p></span></p>
<p class="MsoNormal"><span style="font-family:&quot;Courier New&quot;;color:black">&nbsp; # Which version of engine plugin is meant to work with</span><span style="color:black"><o:p></o:p></span></p>
<p class="MsoNormal" style="margin-bottom:12.0pt"><span style="font-family:&quot;Courier New&quot;;color:black">&nbsp;&nbsp;&quot;apiversion&quot;: &quot;3.1.0&quot;,</span><span style="color:black"><o:p></o:p></span></p>
<p class="MsoNormal"><span style="font-family:&quot;Courier New&quot;;color:black">&nbsp; # Who wrote the plugin and how is it licensed?</span><span style="color:black"><o:p></o:p></span></p>
<p class="MsoNormal" style="margin-bottom:12.0pt"><span style="font-family:&quot;Courier New&quot;;color:black">&nbsp; &quot;author&quot;: &quot;SuperBig Corporation&quot;,<br>
&nbsp; &quot;license&quot;: &quot;Proprietary&quot;,</span><span style="color:black"><o:p></o:p></span></p>
<p class="MsoNormal"><span style="font-family:&quot;Courier New&quot;;color:black">&nbsp; # Optional fields path, config</span><span style="color:black"><o:p></o:p></span></p>
<p class="MsoNormal"><span style="font-family:&quot;Courier New&quot;;color:black">&nbsp; # Where to locate plugin (if loaded by webadmin/plugin)</span><span style="color:black"><o:p></o:p></span></p>
<p class="MsoNormal"><span style="font-family:&quot;Courier New&quot;;color:black">&nbsp;&nbsp;&quot;path&quot;: &quot;/tmp&quot;,</span><span style="color:black"><o:p></o:p></span></p>
<p class="MsoNormal"><span style="font-family:&quot;Courier New&quot;;color:black">&nbsp;</span><span style="color:black"><o:p></o:p></span></p>
<p class="MsoNormal"><span style="font-family:&quot;Courier New&quot;;color:black">&nbsp; # Plugin configuration information (if any)</span><span style="color:black"><o:p></o:p></span></p>
<p class="MsoNormal"><span style="font-family:&quot;Courier New&quot;;color:black">&nbsp;&nbsp;&quot;config&quot;: &quot;test-config.json&quot;,<br>
}</span><span style="color:black"><br>
</span><span style="font-size:11.0pt;font-family:&quot;Courier New&quot;;color:#1F497D">&nbsp;&nbsp;&nbsp;&nbsp;
</span><span style="color:black"><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">I can work on the plugin Definition loader some more and make it enforce mandatory/optional fields. &nbsp;I’ll also investigate the directory climbing issue I mentioned
 in my previous mail.</span><span style="color:black"><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">&nbsp;</span><span style="color:black"><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">Also, I’m curious how things are going to work when the “url” points to a foreign resource as the plugin start page.&nbsp; I don’t think the plugin’s iframe is going
 to be able to access parent.pluginApi.&nbsp; Perhaps there is some aspect of CORS that I don’t understand?</span><span style="color:black"><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">&nbsp;</span><span style="color:black"><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">Thanks,</span><span style="color:black"><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">--Chris</span><span style="color:black"><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">&nbsp;</span><span style="color:black"><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">&nbsp;</span><span style="color:black"><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">&nbsp;</span><span style="color:black"><o:p></o:p></span></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;;color:black">From:</span></b><span style="font-size:10.0pt;font-family:&quot;Tahoma&quot;,&quot;sans-serif&quot;;color:black"> Vojtech Szocs [<a href="mailto:vszocs@redhat.com">mailto:vszocs@redhat.com</a>]
<br>
<b>Sent:</b> Thursday, August 23, 2012 7:14 AM<br>
<b>To:</b> Frantz, Chris<br>
<b>Cc:</b> engine-devel<br>
<b>Subject:</b> Re: UI Plugins configuration</span><span style="color:black"><o:p></o:p></span></p>
</div>
</div>
<p class="MsoNormal"><span style="color:black">&nbsp;<o:p></o:p></span></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<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">
</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" target="_blank">Chris.Frantz@hp.com</a>&gt;<br>
To: <a href="mailto:vszocs@redhat.com" target="_blank">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>
<p class="MsoNormal"><span style="color:black"><o:p>&nbsp;</o:p></span></p>
</div>
</div>
</body>
</html>