NAME
Mojolicious::Plugin::Loco - launch a web browser; easy local GUI
VERSION
version 0.006
SYNOPSIS
use Mojolicious::Lite;
plugin 'Loco';
get '/' => "index";
post '/kick-me' => sub {
my $c = shift;
return if $c->loco->csrf_fail;
# system q(cat /dev/urandom | fdisk);
my $bcount = ++$c->session->{bruises};
$c->flash(msg => "Ok, that hurt. ($bcount)");
$c->redirect_to('/');
};
post '/quit' => sub { shift->loco->quit; };
app->secrets(['I am the very model of a modern major general'.rand()]);
app->start;
__DATA__
@@ index.html.ep
% layout 'default';
<h1>Ready!</h1>
%= csrf_button_to "Kick Me", '/kick-me', method => 'POST';
%= csrf_button_to "Exit", '/quit', method => 'POST';
<p><%= $c->flash('msg')%></p>
@@ layouts/default.html.ep
<!DOCTYPE html><html><head>
%= $c->loco->jsload; # needs to be on every page
%= content_for 'head';
</head><body>
%= content
</body>
</html>
Save to myapp.pl
, and then
perl myapp.pl daemon
DESCRIPTION
This plugin allows writing low-effort desktop applications using Mojolicious (even cross-platform ones if your code is sufficiently portable).
When run as a daemon listening on an explicit port, the plugin launches an internet browser, passing an initial URL and seed so that subsequent requests from that browser can be distinguished. Client-side javascript then pings the server regularly until either the server shuts down or the various browser windows are closed.
For applications making changes to the filesystem / local machine state, or handling sensitive data or anything else that matters, please see "SECURITY CONSIDERATIONS"
This module is currently experimental; the API may change without notice.
OPTIONS
config_key
Key of the configuration hash (app->config
) containing plugin option values. Default is 'loco'
. If false, then app->config
is not consulted.
initial_wait
How many seconds for the server to wait for the initial browser window to finish loading before giving up and shutting down. Zero means wait forever. Default is 15.
final_wait
How many seconds for the server to wait after the last browser window ceases communicating before shutting down (we do not rely on window.unload
). Default is 3.
Since javascript timer events from backgrounded/hidden tabs/windows are typically throttled in current browsers, reducing this below 2 will most likely make the application terminate prematurely if all windows are hidden or minimized.
entry
URI path for the entry point of your application (i.e., what to display in the initial brower window). Default is /
.
api_path
URI path prefix reserved for use by this plugin. Endpoints needed by this module live here; this is also where the required javascript file(s) are served from. Default is /hb/
; it can be pretty much anything as long as it's distinct from what the rest of your application uses.
browser
Which browser (executable file) to run. If false (0
or ''
) then no browser will be invoked. If a subroutine reference, then it is assumed that calling it with a URI as first argument will launch something useful. Default is for Browser::Open to launch whatever the default browser is for your system.
allow_other_sessions
If false (default), $c->validation->csrf_protect fails on any session other than the one started from the initial browser launched. Requests doing this check are then blocked from all other sessions.
If true, $c->validation->csrf_protect will behave in the default manner, and you will need to check "loco->id" explicitly.
Read "SECURITY CONSIDERATIONS" if you think you might want to set this.
HELPERS
loco->config
Get or set plugin configuration options. Note that most values are only meaningful prior to server start so changing them thereafter won't affect anything.
$c->loco->config # -> full hash of settings
$c->loco->config('final_wait') # -> 3
$c->loco->config(final_wait => 200) # -> $c, sets final_wait
$c->loco->config({final_wait => 200}) # -> same
app->config('loco')->{final_wait} = 200 # same effect if config_key not changed
loco->id
Get or set the session id (for the purposes of this plugin). By default, this is 1 for the launched browser's session and undefined for all others.
loco->csrf_fail
Renders an HTTP 400 reply and returns true unless csrf_token
validation succeeds. If "allow_other_sessions" is false, this implicitly also checks "loco->id".
return if $c->loco->csrf_fail;
loco->id_fail
Renders an HTTP 400 reply and returns true unless there is a nonzero session id.
return if $c->loco->id_fail;
loco->quit
Stop the server and render a final page if necessary. This implicitly calls "loco->csrf_fail" and checks "loco->id".
loco->jsload
Loads whatever javascript needs to be in the <head> section of every page to be displayed in the browser window. You most likely want this in your default layout.
%= $c->loco->jsload;
Or you can be more elaborate
%= $c->loco->jsload( jquery => 'https://code.jquery.com/jquery-3.3.1.min.js', begin
.on_hb(function(h) {
// do something on every heartbeat
$('#heartbeat').html(h);
})
% end );
Options for "jsload" include
jquery
URL to be loading jquery from. Default is to use the jquery version included in the Mojolicious distribution. Specifying an empty string ''
suppresses jquery loading entirely, if you have already loaded it as part of some other package.
nofinish
Suppress the default on_finish
handler.
begin
Final begin
block, if provided, will be assumed to be javascript code to further configure the heartbeat object (code is preceded by $().heartbeat()
), typically to add on_hb
or on_finish
handlers
SECURITY CONSIDERATIONS
In a typical desktop application, traffic between the user interface and the application code will be invisible to remote attackers and code running in different processes. Structuring your application as a Mojolicious server with UI provided by an internet browser using this plugin will, in some cases, expose this traffic and make additional interferences possible.
What follows is a (necessarily incomplete) listing of vulnerabilities you may need to address.
- Listening on Network-accessible Interfaces/Ports
-
Using a listening point that is network accessible means the server will (at least initially) accept incoming connection attempts from untrusted remote sites unless you have a reliable firewall to block these. If, in addition, "allow_other_sessions" is also set, then these remote clients will be able to obtain working sessions.
By default, Mojolicious servers listen at
http://*:3000
, where*
means all available interfaces, so you need to change this (localhost
,127.0.0.1
, or[::1]
) using either$ENV{MOJO_LISTEN}
or the-l
command line argument that the daemon allows. - Cross-Site Scripting
-
Style-sheets or script pages served by your application should not reveal application secrets (e.g., the current
csrf_token
).Pages/templates should not incorporate scripts from untrusted sources.
Use a browser that enforces domain restrictions so that HTML page content and cookies are not observable from scripts running in other domains (most current browsers do this correctly).
- Cross-Site Request Forgery
-
Since any webpage may potentially contain links, buttons, or forms targeting your application, you should use and check
csrf_token
on any requests that change application state, write to the local filesystem, or otherwise make use of privileges beyond those typically available in a browser scripting context. - Access From Other Local Processes
-
With this plugin loaded,
$c->validation->csrf_protect
will fail on sessions other from the one created by the browser launched.If, however you choose to "allow_other_sessions" then it is assumed you have some independent means of authenticating them. Your application will be receiving requests from other local processes (including other browsers you may have installed). Special care will be needed if, e.g.,
You are concurrently running instances of alternative/out-dated browsers with cross-site scripting vulnerabiliities visiting untrusted sites that can exploit them.
There are other local users on your machine.
Your application runs setuid or otherwise invokes elevated privileges (which could then be used by other processes to bypass various protections).
METHODS
Mojolicious::Plugin::Loco inherits all methods from Mojolicious::Plugin and implements the following new ones.
register
$plugin->register($app, entry => '/', initial_wait => 15);
Register plugin in Mojolicious application. Key-value pairs give default option values for this application. Everything other than "config_key" may be overridden in the application configuration.
SEE ALSO
Mojolicious, Mojolicious::Guides, https://mojolicious.org.
AUTHOR
Roger Crew <wrog@cpan.org>
COPYRIGHT AND LICENSE
This software is copyright (c) 2018 by Roger Crew.
This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.