File Coverage

lib/CallBackery.pm
Criterion Covered Total %
statement 75 82 91.4
branch 8 18 44.4
condition 2 6 33.3
subroutine 16 18 88.8
pod 1 1 100.0
total 102 125 81.6


line stmt bran cond sub pod time code
1             package CallBackery;
2              
3              
4             =head1 NAME
5              
6             CallBackery - Appliance Frontend Builder
7              
8             =head1 SYNOPSIS
9              
10             require Mojolicious::Commands;
11             Mojolicious::Commands->start_app('CallBackery');
12              
13             =head1 DESCRIPTION
14              
15             Configure the mojo engine to run our application logic as webrequests arrive.
16              
17             =head1 ATTRIBUTES
18              
19             =cut
20              
21             # load the two modules to have perl check them
22             # having a non-C locale for number will wreck all sorts of havoc
23             # when things get converted to string and back
24 1     1   243869 use POSIX qw(locale_h);
  1         2  
  1         9  
25             setlocale(LC_NUMERIC, "C");
26             setlocale(LC_TIME, "C");
27              
28 1     1   258 use Mojo::Base 'Mojolicious';
  1         4  
  1         9  
29 1     1   69580 use Mojolicious::Plugin::Qooxdoo;
  1         2478  
  1         8  
30 1     1   34 use Mojo::URL;
  1         2  
  1         7  
31 1     1   32 use Mojo::JSON;
  1         2  
  1         52  
32 1     1   4 use Mojo::Util qw(hmac_sha1_sum);
  1         1  
  1         36  
33 1     1   4 use Mojo::File qw(path);
  1         1  
  1         29  
34 1     1   3 use File::Basename;
  1         1  
  1         42  
35 1     1   612 use CallBackery::Config;
  1         3  
  1         7  
36 1     1   759 use CallBackery::Plugin::Doc;
  1         5  
  1         23  
37 1     1   950 use CallBackery::Database;
  1         29  
  1         10  
38 1     1   812 use CallBackery::User;
  1         3  
  1         10  
39 1     1   68 use Scalar::Util qw(weaken);
  1         2  
  1         2666  
40              
41             our $VERSION = '0.56.7';
42              
43             =head2 config
44              
45             A hash pointer to the configuration object. See L for details.
46             The default configuration file is located in etc/callbackery.cfg. You can override the
47             path by setting the C environment variable.
48              
49             The config property is set automatically on startup.
50              
51             =cut
52              
53             has 'config' => sub {
54             my $app = shift;
55             my $conf = CallBackery::Config->new(
56             app => $app,
57             file => $ENV{CALLBACKERY_CONF} || $app->home->child('etc','callbackery.cfg')
58             );
59             };
60              
61             =head2 database
62              
63             An instance of L or a module with the same API.
64              
65             =cut
66              
67             has database => sub {
68             CallBackery::Database->new(app=>shift);
69             };
70              
71             has userObject => sub {
72             my $app = shift;
73             my $ obj = CallBackery::User->new(app=>$app,log=>$app->log);
74             $obj->{prototype} = 1;
75             return $obj;
76             };
77              
78             =head2 securityHeaders
79              
80             A hash of headers to set on every response to ask the webbrowser to
81             help us fight the bad guys.
82              
83             =cut
84              
85             has securityHeaders => sub { {
86             # prevent click jacking
87             'X-Frame-Options' => 'SAMEORIGIN',
88             # some magic browser based anti xss action
89             'X-XSS-Protection' => '1; mode=block',
90             # the browser should obej the servers settings regarding content-type
91             'X-Content-Type-Options' => 'nosniff',
92             # do not store our data ever
93             'Pragma' => 'private',
94             }};
95              
96             =head2 rpcServiceNamespace
97              
98             our rpc service namespace
99              
100             =cut
101              
102             has rpcServiceNamespace => 'CallBackery';
103              
104             =head2 rpcServiceController
105              
106             our rpc service controller
107              
108             =cut
109              
110             has rpcServiceController => 'Controller::RpcService';
111              
112             =head2 docIndex
113              
114             initial document to be presented on the doc link
115              
116             =cut
117              
118             has docIndex => __PACKAGE__ . '::Index';
119              
120             =head1 METHODS
121              
122             All the methods of L as well as:
123              
124             =cut
125              
126             =head2 startup
127              
128             Mojolicious calls the startup method at initialization time.
129              
130             =cut
131              
132             sub startup {
133 1     1 1 27488 my $app = shift;
134             # having a non-C locale for number will wreck all sorts of havoc
135             # when things get converted to string and back
136 1         7 setlocale(LC_NUMERIC, "C");
137 1         4 setlocale(LC_TIME, "C");
138 1         2 weaken($app);
139 1         5 $app->config->postProcessCfg();
140 1         8 my $gcfg = $app->config->cfgHash->{BACKEND};
141 1 50       19 if ($gcfg->{log_file}){
142 0 0       0 if (open my $file, '>>', $gcfg->{log_file}){
143 0         0 $app->log->handle($file);
144             }
145             else {
146 0         0 $app->log->debug("Opening $gcfg->{log_file}: $!");
147             }
148             }
149              
150             ## commands
151 1         3 unshift @{$app->commands->namespaces}, __PACKAGE__.'::Command';
  1         10  
152              
153 1 50       147 unshift @{$app->static->paths},
  1         29  
154             $app->home->rel_file('frontend').'/compiled/source/'
155             if $app->mode eq 'development'; # Router
156              
157             # properly figure your own path when running under fastcgi
158             $app->hook( before_dispatch => sub {
159 4     4   49960 my $c = shift;
160 4         21 my $reqEnv = $c->req->env;
161 4   33     113 my $uri = $reqEnv->{SCRIPT_URI} || $reqEnv->{REQUEST_URI};
162 4         9 my $path_info = $reqEnv->{PATH_INFO};
163 4 50 33     37 $uri =~ s|/?${path_info}$|/| if $path_info and $uri;
164 4 50       17 $c->req->url->base(Mojo::URL->new($uri)) if $uri;
165 1         75 });
166              
167             $app->hook( after_dispatch => sub {
168 4     4   16198 my $c = shift;
169             # not telling anyone that we are mojo
170 4         14 $c->res->headers->remove('Server');
171 4 50       132 my $securityHeaders = $c->can('securityHeaders') ? $c->securityHeaders : $app->securityHeaders;
172 4         67 for my $header ( keys %$securityHeaders){
173 16         460 $c->res->headers->header($header,$securityHeaders->{$header});
174             }
175 4 50       155 $c->res->headers->cache_control('no-cache, no-store, must-revalidate')
176             unless $c->req->url->path =~ m{/resource/.+};
177 1         43 });
178            
179 1 50       15 if (my $secrets = $app->config->secretFile) {
180 1 50       61 if (-r $secrets) {
181 1         11 $app->secrets([ path($app->config->secretFile)->slurp ]);
182             }
183             else {
184 0         0 $app->log->error("Cannot read secrets file $secrets. Please check permissions.");
185             }
186             }
187            
188 1         341 my $routes = $app->routes;
189              
190             $app->plugin('CallBackery::Plugin::Doc', {
191             root => '/doc',
192             index => $app->docIndex,
193             template => Mojo::Asset::File->new(
194 1         13 path=>dirname($INC{'CallBackery/Config.pm'}).'/templates/doc.html.ep',
195             )->slurp,
196             });
197              
198 1         15 $routes->any('/upload')->to(namespace => $app->rpcServiceNamespace, controller=>$app->rpcServiceController, action => 'handleUpload');
199 1         512 $routes->any('/download')->to(namespace => $app->rpcServiceNamespace, controller=>$app->rpcServiceController, action => 'handleDownload');
200              
201             # this is a dummy login screen, we use inside an iframe to trick the browser
202             # into storing our password for auto-fill. Since there is no standard for triggering the
203             # behavior, this is all a bit voodoo, sorry. -- tobi
204             $routes->get('/login')->to(cb => sub {
205 0     0   0 my $c = shift;
206 0         0 $c->render(data=><'html');
207            
208            
209            
210            
211             HTML
212 1         457 });
213             # second stage of the deception. the answer page for login must not be the same as the original page
214             # otherwise the browser assumes the login failed and does not offer to save the password.
215             $routes->post('/login')->to(cb => sub {
216 0     0   0 shift->render(data=><'html');
217            
218             HTML
219 1         453 });
220              
221              
222 1         439 $app->plugin('qooxdoo',{
223             path => '/QX-JSON-RPC',
224             namespace => $app->rpcServiceNamespace,
225             controller => $app->rpcServiceController,
226             });
227              
228              
229 1         1013 return 0;
230             }
231              
232             1;
233              
234             __END__