PlugIt is a framework enhancing the portability and integration of web services requiring a user interface.
We use this framework at EBU to access web services on the EBU.io platform.
This is a draft of the protocol and implementation. Expect issues (and report them) !
Copyright (c) 2013, EBU All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
-
Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
-
Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
-
Neither the name of the EBU nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Contact the EBU (Michael Barroco, [email protected]) if you are in need of special licence terms/ distribution rights different from the BSD 3.0.
- Michael Barroco @barroco
- Maximilien Cuony @the-glu
(pip install ...
)
- django==1.5.1
- flask
- requests
- python-dateutil
- poster
- python-crypto
This repository contains an hors-de-la-boite django application to access a PlugIt server. No setup should be needed (django has to be installed however).
Use cd Simple Django client
and python manage.py runserver
to run the project. The interface is available at http://127.0.0.1:8000/
It's possible to change the default location of the PlugIt server (http://127.0.0.1:5000) in the app/settings.py file.
The flask server implement a PlugIt server.
server.py is the main flask file, providing different call to the framework, generated from actions defined in actions.py. Basic configuration is available at the beginning of the file. Otherwise, this file should not be edited !
Use cd Simple Flask server
and python server.py
to run the server.
DEBUG
: Boolean. Set to True to active flask debuggingPI_META_CACHE
: Number of seconds to ask the EBUio server to cache the meta information. Set by default to 0 if DEBUG is True, 5 minutes if DEBUG is False.PI_BASE_URL
: String. The base URL to access the PlugIt API. It's possible to use a different URL (eg. '/plugIt/') to have others flask methods for another API using the same server.py. Must end with a /PI_ALLOWED_NETWORKS
: Array of subnets. PlugIt call will be restricted to thoses networks. Eg:['127.0.0.1/32']
(Single ip),['0.0.0.0/0']
(Everyone),['192.168.42.0/24']
(Everyone with ip 192.168.42.X)PI_API_USERDATA
: Array of string. Properties allowed about the current userPI_API_ORGAMODE
: If true, work in Orga mode (next section)PI_API_REALUSERS
: If true, work with real users. Exclusiv with PIAPI_ORGAMODE (don't active both !). You need to setup a database and usepython manage.py syncdb
to create it. Administration is available @ http://127.0.0.1:8000/admin/
In this mode, the system works with a current organization. For each request an ebuio_orgapk, with the unique ID of the current organization is send.
The user can change the organization based on his list of organizations. It's possible to restrict access using only_orga_member_user and only_orga_admin_user.
only_user are for the current project, only_orga_user for the current organization.
The user can implement his actions in the actions.py file. Each action is a defined like this
@action(route="/test", template="test.html")
def test(request):
return {"hello": "Test", "data": request.args.get('data', '')}</code>
It's possible to use various additional decorators to specify how the meta about the action is generated:
@only_logged_user()
: Make action available only for logged users@only_member_user()
: Make action available only for members users@only_admin_user()
: Make action available only for admins users@only_orga_member_user()
: Make action available only for members of the current orga@only_orga_admin_user()
: Make action available only for admins of the current orga@cache(time=0, byUser=None)
: Specify how and how long the action should be cached@user_info(props=[])
: Specify the list of properties requested about the user@json_only
: Specify the action return only json@no_template
: Specify no master template should be used
Again, the server.py file take care of responding to /meta/, /template/ and /action/ call. The function in actions.py will be called when needed. The request is passed as the first parameters to actions.
To redirect the user, you can use the object PlugItRedirect in utils: return PlugItRedirect("/action")
or `return PlugItRedirect("http://google.ch", no_prefix=True)
The media folder contains all media files
The template folder contains all templates
Each template can extend the template plugIt/base.html. The template integrates basic JavaScript and CSS libraries such as jQuery, Bootstrap and integrate into the generic EBUio interface.
Templates use the django templating language. The documentation is available at https://docs.djangoproject.com/en/1.5/topics/templates/
An ebuio_u object is provided, with the current user.
A ebuio_baseUrl value is provided, with the root URL to use for all URL generation (others actions, medias, etc..)
You can use the template tag plugitInclude
in the library plugit_tags
to include another template inside a template. The argument to plugItInclude must be an action. The action won't be called but his template will be used. The context is preserved.
You can use the template tag plugitGetUser
in the library plugit_tags
to load information about an user using his pk. Example: {% plugitGetUser media.user_id as tmpuser %}{{tmpuser.username}}
Each method returns a JSON object, except for /template/ and /media/ calls.
If you use our Flask server, those method are automatically implemented !
/ping is used to test access to the application. The server must reply with an HTTP 200 response and the object {data: OptionalData} if everything is ok. The OptionalData must be returned if provided, as send by the client or with a blank string if the parameter wasn't set.
/version is used to get the current version of the server API. The server must reply with an HTTP 200 response and the object {result: 'Ok', version: '1', protocol: 'EBUio-PlugIt'}, for the current version, if everything is ok.
/mail is used by the PlugIt client to send back to the server mails reply from users. He will send back, using a POST request the response_id provided with the API call to send the mail.
The project can define multiples actions, and they are triggered from the templates. There is no definition of the available actions. The default action, called when the user arrive on the project page on EBUio is defined by a blank string. (root)
This call returns information about a specific action. The server must reply with an HTTP 200 response (or a 404 if the action doesn't exist) with an object. Properties of this object are:
- template_tag : String. The current version of the template. This value must change if the template associated with the action change.
- json_only : Boolean. Optional, default to False. If set to True, return the json directly to the browser, without using a template.
- no_template : Boolean. Optional, default to False. If set to True, return the template directly to the browser, without using a master template.
- only_logged_user : Boolean. Optional, default to False. True if the user must be authenticated on EBUio to call the action.
- only_member_user : Boolean. Optional, default to False. True if the user must be in the project group on EBUio to call the action.
- only_admin_user : Boolean. Optional, default to False. True if the user must be an administrator of the project on EBUio to call the action.
- cache_time : Integer. Optional, default to 0. Time, in seconds, on how long the page should be cached on the EBUio side.
- cache_by_user : Boolean. Optional, default to only_logged_user parameter. Set to true if the page must be cached by user. Useful if the page change based on the current user.
- user_info : List of string. Optional, default to []. List of user properties EBUio add to each request. Example properties, the full list isn't defined yet: username, email, first_name, last_name. NB: It's also possible to access this using the API.
The server should set the Expire: Date HTTP header. EBIio will cache the result of the called based on this header. If this header isn't set, a timeout of 5 minutes will be used.
This call returns the template for a specific action (no JSON !). 200 HTTP status code must be used if everything is ok, or an HTTP 404 if the action doesn't exist.
This call execute on the server the specific action. If a POST method is used on the EBOio side, the request is done to the server using the same method, otherwise using GET.
POST data and URLs parameters are forwarded to the server side, including files, but parameters begging by ebuio_ are removed.
EBUio add to parameters (Using GET or POST data, depending of the method of the request) each parameter requested about the user in ebuio_u_ parameters. One should be careful about lengths of those requests.
The server should reply with an HTTP 200 status code (or 404 if the action doesn't exist, 403 if the user hasn't right to call the action). The returned JSON object is forwarded to the template.
It's possible to redirect the client using the header EbuIo-PlugIt-Redirect
. PlugIt automaticaly append the ebuio_baseUrl. To avoid this, set the header EbuIo-PlugIt-Redirect-NoPrefix
to True
.
It's possible to send a file using the header EbuIo-PlugIt-ItAFile
. The content is send to the user, using the same content-type. If any, the Content-Disposition header is also forwarder.
This call return a specific media on the server side. Each request on EBUio side on /media/* is forwarded to the server and returned to the client. No caching is used, but a 1 hour Cache-Control header is set by EBUio-
The API is available at /plugIt/ebuio_api/ . See the API root page using your browser for details.
A small python class (PlugItAPI) is available in plugit_api.py, methods are also detailled on the API root page.
It's possible to send mail using the API. All users reply to the mail, if keeping the same subject and send will a response_id will be send back to the PlugIt server using the /mail call. The response_id is secured in the subject and can be trusted (users cannot generate generic response_id).
The management task check_mail is used to check mails and should be runned inside a cron job on the PlugIt client. Relevent configuration (INCOMING_MAIL
and MAIL_SENDER
) should also be correct.
ProxyMode is a special mode and has main differences with normal PlugIt behavior.
In proxyMode, there is no rendering done by the PlugIt client, and no methods (/meta/
, /template/
, /action/
) to implement. The client just forward the call from the user request to your server.
The PlugIt server should send back the HTML content who will be displayed to the client. Except for the inclusion of the result inside the plugIt webpage, nothing is else is done.
The PlugIt client will send informations about the client and the request using HTTP headers. All headers begin by X-Plugit-
.
EBUio need the presence of a CSRF token on each POST request (as implemented by django) for security issues.
The plugIt client parse the response of the plugIt server and replace the special token {~__PLUGIT_CSRF_TOKEN__~}
by the current CSRF token.
If you want to make a post request, use the following snipet in your form:
<input type="hidden" name="csrfmiddlewaretoken" value="{~__PLUGIT_CSRF_TOKEN__~}"/>
Redirect are handled as usual, using the EbuIo-PlugIt-Redirect
header.
Medias are handled as usual, using the special /media/
path.
If you need to send back the result from the plugIt server directly to the client, without a template, you can set the EbuIo-PlugIt-NoTemplate
header (to any value).
It's possible to set value in the user session, using the ebuio-plugit-setsession-<key>
header. The plugit client will send back requests with the X-Plugitsession-<key>
value.
Using the Simple Flask server utils, it's possble to return a PlugItSetSession
object, builded with the value to return (who can be anything normal to return, like a PlugItRedirect or a standart dict) and a dict of key/value to set in the user session. The get_session_from_request
helper function can also be used on a flask request object to extract a dict of key/value from the current user session.