File Coverage

blib/lib/PlugAuth/Routes.pm
Criterion Covered Total %
statement 21 21 100.0
branch n/a
condition n/a
subroutine 7 7 100.0
pod n/a
total 28 28 100.0


line stmt bran cond sub pod time code
1             package PlugAuth::Routes;
2              
3             # ABSTRACT: routes for plugauth
4             our $VERSION = '0.38'; # VERSION
5              
6              
7             # There may be external authentication for these routes, i.e. using
8             # this CI to determine who can check/update other's access.
9              
10 41     41   19548 use strict;
  41         205  
  41         1463  
11 41     41   275 use warnings;
  41         163  
  41         2110  
12 41     41   277 use Log::Log4perl qw/:easy/;
  41         130  
  41         412  
13 41     41   41276 use Mojo::ByteStream qw/b/;
  41         121  
  41         3087  
14 41     41   6698 use Clustericious::RouteBuilder;
  41         110699  
  41         369  
15 41     41   14460 use Clustericious::Config;
  41         391  
  41         1272  
16 41     41   278 use List::Util qw( uniq );
  41         105  
  41         104227  
17              
18              
19             get '/' => sub { shift->welcome } => 'index';
20             get '/index' => sub { shift->welcome };
21              
22              
23             # Check authentication for a user (http basic auth protocol).
24             get '/auth' => sub {
25             my $self = shift;
26             my $auth = $self->req->headers->authorization or do {
27             $self->res->headers->www_authenticate('Basic "ACPS"');
28             $self->render_message('please authenticate', 401);
29             return;
30             };
31             my ($method,$str) = split / /,$auth;
32             my ($user,$pw) = split /:/, b($str)->b64_decode;
33              
34             if($self->auth->check_credentials($user,$pw))
35             {
36             $self->render_message('ok');
37             INFO "Authentication succeeded for user $user";
38             }
39             else
40             {
41             $self->render_message('not ok', 403);
42             INFO "Authentication failed for user $user";
43             }
44             };
45              
46              
47             # Check authorization for a user to perform $action on $resource.
48             get '/authz/user/#user/#action/(*resource)' => { resource => '/' } => sub {
49             my $c = shift;
50             # Ok iff the user is in a group for which $action on $resource is allowed.
51             my ($user,$resource,$action) = map $c->stash($_), qw/user resource action/;
52             $resource =~ s{^/?}{/};
53             TRACE "Checking authorization for $user to perform $action on $resource...";
54             my $found = $c->authz->can_user_action_resource($user,$action,$resource);
55             if ($found)
56             {
57             TRACE "Authorization succeeded ($found)";
58             return $c->render_message('ok');
59             }
60             TRACE "Authorization failed";
61             $c->render_message("unauthorized : $user cannot $action $resource", 403);
62             };
63              
64              
65             # Given a user, an action and a regex, return a list of resources
66             # on which $user can do $action, where each resource matches that regex.
67             get '/authz/resources/#user/#action/(*resourceregex)' => sub {
68             my $c = shift;
69             my ($user,$action,$resourceregex) = map $c->stash($_), qw/user action resourceregex/;
70             TRACE "Checking $user, $action, $resourceregex";
71             $resourceregex = qr[$resourceregex];
72             my @resources;
73             for my $resource ($c->authz->match_resources($resourceregex))
74             {
75             TRACE "Checking resource $resource";
76             push @resources, $resource if $c->authz->can_user_action_resource($user,$action,$resource);
77             }
78             $c->stash->{autodata} = [sort @resources];
79             };
80              
81              
82             # Return a list of all defined actions
83             get '/actions' => sub {
84             my($self) = @_;
85             $self->stash->{autodata} = [ $self->authz->actions ];
86             };
87              
88              
89             # All the groups for a user :
90             get '/groups/#user' => sub {
91             my $c = shift;
92             my $groups = $c->authz->groups_for_user($c->stash('user'));
93             $c->render_message('not ok', 404) unless defined $groups;
94             $c->stash->{autodata} = $groups;
95             };
96              
97              
98             # Given a host and a tag (e.g. "trusted") return true if that host has
99             # that tag.
100             get '/host/#host/:tag' => sub {
101             my $c = shift;
102             my ($host,$tag) = map $c->stash($_), qw/host tag/;
103             if ($c->authz->host_has_tag($host,$tag))
104             {
105             TRACE "Host $host has tag $tag";
106             return $c->render_message('ok', 200);
107             }
108             TRACE "Host $host does not have tag $tag";
109             return $c->render_message('not ok', 403);
110             };
111              
112              
113             get '/user' => sub {
114             my $c = shift;
115             $c->stash->{autodata} = [ uniq sort $c->auth->all_users ];
116             };
117              
118              
119             get '/group' => sub {
120             my $c = shift;
121             $c->stash->{autodata} = [ $c->authz->all_groups ];
122             };
123              
124              
125             get '/users/:group' => sub {
126             my $c = shift;
127             my $users = $c->authz->users_in_group($c->stash('group'));
128             $c->render_message('not ok', 404) unless defined $users;
129             $c->stash->{autodata} = $users;
130             };
131              
132             authenticate;
133             authorize 'accounts';
134              
135              
136             post '/user' => sub {
137             my $c = shift;
138             $c->parse_autodata;
139             my $user = $c->stash->{autodata}->{user};
140             my $password = $c->stash->{autodata}->{password} || '';
141             my $groups = $c->stash->{autodata}->{groups};
142             delete $c->stash->{autodata};
143            
144             my $method = 'create_user';
145             my $cb;
146            
147             my $auth_plugin = $c->auth;
148            
149             if(defined $groups)
150             {
151             $method = 'create_user_cb';
152             $auth_plugin = $c->auth->_find_create_user_cb;
153             return $c->render_message('not ok', 501)
154             unless defined $auth_plugin;
155             $cb = sub {
156             foreach my $group (split /\s*,\s*/, $groups)
157             {
158             my $users = $c->app->authz->add_user_to_group($group, $user);
159             $c->app->emit(create_group => {
160             admin => $c->stash('user'),
161             group => $group,
162             users => $users,
163             });
164             }
165             };
166             }
167            
168             if($auth_plugin->$method($user, $password, $cb))
169             {
170             $c->render_message('ok', 200);
171             $c->app->emit('user_list_changed'); # deprecated, but documented in a previous version
172             $c->app->emit(create_user => {
173             admin => $c->stash('user'),
174             user => $user,
175             });
176             }
177             else
178             {
179             $c->render_message('not ok', 403);
180             }
181             };
182              
183              
184             del '/user/#username' => sub {
185             my $c = shift;
186             my $user = $c->param('username');
187             if($c->auth->delete_user($user))
188             {
189             $c->render_message('ok', 200);
190             $c->app->emit('user_list_changed'); # deprecated, but documented in a previous version
191             $c->app->emit(delete_user => {
192             admin => $c->stash('user'),
193             user => $user,
194             });
195             }
196             else
197             {
198             $c->render_message('not ok', 404);
199             }
200             };
201              
202              
203             post '/group' => sub {
204             my $c = shift;
205             $c->parse_autodata;
206             my $group = $c->stash->{autodata}->{group};
207             my $users = $c->stash->{autodata}->{users};
208             delete $c->stash->{autodata};
209             if($c->authz->create_group($group, $users))
210             {
211             $c->render_message('ok', 200);
212             $c->app->emit(create_group => {
213             admin => $c->stash('user'),
214             group => $group,
215             users => $users,
216             });
217             }
218             else
219             {
220             $c->render_message('not ok', 403);
221             }
222             };
223              
224              
225             del '/group/:group' => sub {
226             my $c = shift;
227             my $group = $c->param('group');
228             if($c->authz->delete_group($group))
229             {
230             $c->render_message('ok', 200);
231             $c->app->emit(delete_group => {
232             admin => $c->stash('user'),
233             group => $group,
234             });
235             }
236             else
237             {
238             $c->render_message('not ok', 404);
239             }
240             };
241              
242              
243             post '/group/:group' => sub {
244             my $c = shift;
245             $c->parse_autodata;
246             my $users = $c->stash->{autodata}->{users};
247             my $group = $c->param('group');
248             delete $c->stash->{autodata};
249             if($c->authz->update_group($group, $users))
250             {
251             $c->render_message('ok', 200);
252             $c->app->emit(update_group => {
253             admin => $c->stash('user'),
254             group => $group,
255             users => $users,
256             });
257             }
258             else
259             {
260             $c->render_message('not ok', 404);
261             }
262             };
263              
264              
265             post '/group/:group/#username' => sub {
266             my($c) = @_;
267             my $group = $c->stash('group');
268             my $user = $c->stash('username');
269             if(my $users = $c->authz->add_user_to_group($group, $user))
270             {
271             $c->render_message('ok', 200);
272             $c->app->emit(update_group => {
273             admin => $c->stash('user'),
274             group => $group,
275             users => $users,
276             });
277             }
278             else
279             {
280             $c->render_message('not ok', 404);
281             }
282             };
283              
284              
285             del '/group/:group/#username' => sub {
286             my($c) = @_;
287             my $group = $c->stash('group');
288             my $user = $c->stash('username');
289             if(my $users = $c->authz->remove_user_from_group($group, $user))
290             {
291             $c->render_message('ok', 200);
292             $c->app->emit(update_group => {
293             admin => $c->stash('user'),
294             group => $group,
295             users => $users,
296             });
297             }
298             else
299             {
300             $c->render_message('not ok', 404);
301             }
302             };
303              
304              
305             post '/grant/#group/:action1/(*resource)' => { resource => '/' } => sub {
306             my $c = shift;
307             my($group, $action, $resource) = map { $c->stash($_) } qw( group action1 resource );
308             $resource =~ s/\.(json|yml)$//;
309             if($c->authz->grant($group, $action, $resource))
310             {
311             $c->render_message('ok', 200);
312             $c->app->emit(grant => {
313             admin => $c->stash('user'),
314             group => $group,
315             action => $action,
316             resource => $resource,
317             });
318             }
319             else
320             {
321             $c->render_message('not ok', 404);
322             }
323             };
324              
325              
326             del '/grant/#group/:action1/(*resource)' => { resource => '/' } => sub {
327             my($c) = @_;
328             my($group, $action, $resource) = map { $c->stash($_) } qw( group action1 resource );
329             $resource =~ s/\.(json|yml)$//;
330             if($c->authz->revoke($group, $action, $resource))
331             {
332             $c->render_message('ok', 200);
333             $c->app->emit(revoke => {
334             admin => $c->stash('user'),
335             group => $group,
336             action => $action,
337             resource => $resource,
338             });
339             }
340             else
341             {
342             $c->render_message('not ok', 404);
343             }
344             };
345              
346              
347             get '/grant' => sub {
348             my($c) = @_;
349             $c->stash->{autodata} = $c->authz->granted;
350             };
351              
352              
353             authenticate;
354             authorize 'change_password';
355              
356             post '/user/#username' => sub {
357             my($c) = @_;
358             $c->parse_autodata;
359             my $user = $c->param('username');
360             my $password = eval { $c->stash->{autodata}->{password} } || '';
361             delete $c->stash->{autodata};
362             if($c->auth->change_password($user, $password))
363             {
364             $c->render_message('ok', 200);
365             $c->app->emit(change_password => { admin => $c->stash('user'), user => $user });
366             }
367             else
368             {
369             $c->render_message('not ok', 403);
370             }
371             };
372              
373             1;
374              
375             __END__
376              
377             =pod
378              
379             =encoding UTF-8
380              
381             =head1 NAME
382              
383             PlugAuth::Routes - routes for plugauth
384              
385             =head1 VERSION
386              
387             version 0.38
388              
389             =head1 DESCRIPTION
390              
391             This module defines the HTTP URL routes provided by L<PlugAuth>.
392             This document uses Mojolicious conventions to describe routes,
393             see L<Mojolicious::Guides::Routing> for details.
394              
395             =head1 ROUTES
396              
397             =head2 Public routes
398              
399             These routes work for unauthenticated and unauthorized users.
400              
401             =head3 GET /
402              
403             Returns the string "welcome to plug auth"
404              
405             =head3 GET /auth
406              
407             =over 4
408              
409             =item * if username and password provided using BASIC authentication and are correct
410              
411             Return 200 ok
412              
413             =item * if username and password provided using BASIC authentication but are not correct
414              
415             Return 403 not ok
416              
417             =item * if username and password are not provided using BASIC authentication
418              
419             Return 401 please authenticate
420              
421             =back
422              
423             =head3 GET /authz/user/#user/#action/(*resource)
424              
425             =over 4
426              
427             =item * if the given user (#user) is permitted to perform the given action (#action) on the given resource (*resource)
428              
429             Return 200 ok
430              
431             =item * otherwise
432              
433             Return 403 "unauthorized : $user cannot $action $resource"
434              
435             =back
436              
437             =head3 GET /authz/resources/#user/#action/(*resourceregex)
438              
439             Returns a list of resources that the given user (#user) is permitted to perform
440             action (#action) on. The regex is used to filter the results (*resourceregex).
441              
442             =head3 GET /actions
443              
444             Return a list of actions that PlugAuth knows about.
445              
446             =head3 GET /groups/#user
447              
448             Return a list of groups that the given user (#user) belongs to.
449              
450             Returns 404 not ok if the user does not exist.
451              
452             =head3 GET /host/#host/:tag
453              
454             =over 4
455              
456             =item * if the given host (#host) has the given tag (:tag)
457              
458             return 200 ok
459              
460             =item * otherwise
461              
462             return 403 not ok
463              
464             =back
465              
466             =head3 GET /user
467              
468             Returns a list of all users that PlugAuth knows about.
469              
470             =head3 GET /group
471              
472             Returns a list of all groups that PlugAuth knows about.
473              
474             =head3 GET /users/:group
475              
476             Returns the list of users that belong to the given group (:group)
477              
478             =head2 Accounts Routes
479              
480             These routes are available to users authenticates and authorized to perform
481             the 'accounts' action. They will return
482              
483             =over 4
484              
485             =item * 401
486              
487             If no credentials are provided
488              
489             =item * 403
490              
491             If the user is unauthorized.
492              
493             =item * 503
494              
495             If the PlugAuth server cannot reach itself or the delegated PlugAuth server.
496              
497             =back
498              
499             =head3 POST /user
500              
501             Create a user. The C<username> and C<password> are provided autodata arguments
502             (JSON, YAML, form data, etc).
503              
504             If supported by your authentication plugin (requires C<create_user_cb> to be
505             implemented see L<PlugAuth::Plugin::Auth> for details) You may also optionally
506             include C<groups> as an autodata argument, which specifies the list of groups
507             to which the new user should belong. C<groups> should be a comma separated
508             list stored as a string.
509              
510             Emits event 'create_user' on success
511              
512             $app->on(create_user => sub {
513             my($event, $hash) = @_;
514             my $admin = $hash->{admin}; # user who created the group
515             my $user = $hash->{user};
516             });
517              
518             =head3 DELETE /user/#user
519              
520             Delete the given user (#user). Returns 200 ok on success, 404 not ok on failure.
521              
522             Emits event 'delete_user' on success
523              
524             $app->on(delete_user => sub {
525             my($event, $hash) = @_;
526             my $admin = $hash->{admin}; # user who created the group
527             my $user = $hash->{user};
528             });
529              
530             =head3 POST /group
531              
532             Create a group. The C<group> name and list of C<users> are provided as autodata
533             arguments (JSON, YAML, form data etc). Returns 200 ok on success, 403 not ok
534             on failure.
535              
536             Emits event 'create_group' on success
537              
538             $app->on(create_group => sub {
539             my($event, $hash) = @_;
540             my $admin = $hash->{admin}; # user who created the group
541             my $group = $hash->{group};
542             my $users = $hash->{users};
543             });
544              
545             =head3 DELETE /group/:group
546              
547             Delete the given group (:group). Returns 200 ok on success, 403 not ok on failure.
548              
549             Emits event 'delete_group' on success
550              
551             $app->on(delete_group => sub {
552             my($event, $hash) = @_;
553             my $admin = $hash->{admin}; # user who deleted the group
554             my $group = $hash->{group};
555             });
556              
557             =head3 POST /group/:group
558              
559             Update the list of users belonging to the given group (:group). The list
560             of C<users> is provided as an autodata argument (JSON, YAML, form data etc.).
561             Returns 200 ok on success, 404 not ok on failure.
562              
563             Emits event 'update_group' on success
564              
565             $app->on(update_group => sub {
566             my($event, $hash) = @_;
567             my $admin = $hash->{admin}; # user who updated the group
568             my $group = $hash->{group};
569             my $users = $hash->{users};
570             });
571              
572             =head3 POST /group/:group/#username
573              
574             Add the given user (#username) to the given group (:group).
575             Returns 200 ok on success, 404 not ok on failure.
576              
577             Emits event 'update_group' (see route for POST /group/:group for
578             an example).
579              
580             =head3 DELETE /group/:group/#username
581              
582             Remove the given user (#username) from the given group (:group).
583             Returns 200 ok on success, 404 not ok on failure.
584              
585             Emits event 'update_group' (see route for POST /group/:group for
586             an example).
587              
588             =head3 POST /grant/#group/:action1/(*resource)
589              
590             Grant access to the given group (#group) so they can perform the given action (:action1)
591             on the given resource (*resource). Returns 200 ok on success, 404 not ok on failure.
592              
593             Emits event 'grant' on success
594              
595             $app->on(grant => sub {
596             my($event, $hash) = @_;
597             my $admin = $hash->{admin}; # user who did the granting
598             my $group = $hash->{group};
599             my $action = $hash->{action};
600             my $resource = $hash->{resource};
601             });
602              
603             =head3 DELETE /grant/#group/:action1/(*resource)
604              
605             Revoke permission to the given group (#group) to perform the given action (:action1) on
606             the given resource (*resource). Returns 200 ok on success, 404 not ok on failure.
607              
608             (the action is specified in the route as action1 because action is reserved by
609             L<Mojolicious>).
610              
611             Emits event 'revoke' on success
612              
613             $app->on(revoke => sub {
614             my($event, $hash) = @_;
615             my $admin = $hash->{admin}; # user who did the revoking
616             my $group = $hash->{group};
617             my $action = $hash->{action};
618             my $resource = $hash->{resource};
619             });
620              
621             =head3 GET /grant
622              
623             Get the list of granted permissions.
624              
625             =head2 Change Password routes
626              
627             These routes are available to users authenticates and authorized to perform
628             the 'change_password' action. They will return
629              
630             =over 4
631              
632             =item * 401
633              
634             If no credentials are provided
635              
636             =item * 403
637              
638             If the user is unauthorized.
639              
640             =item * 503
641              
642             If the PlugAuth server cannot reach itself or the delegated PlugAuth server.
643              
644             =back
645              
646             =head3 POST /user/#user
647              
648             Change the password of the given user (#user). The C<password> is provided as
649             an autodata argument (JSON, YAML, form data, etc.). Returns 200 ok on success,
650             403 not ok on failure.
651              
652             Emits event 'change_password' on success
653              
654             $app->on(change_password => sub {
655             my($event, $hash) = @_;
656             my $admin = $hash->{admin}; # user who changed the password
657             my $user = $hash->{user}; # user whos password is changed
658             });
659              
660             =head1 SEE ALSO
661              
662             L<PlugAuth>
663              
664             =head1 AUTHOR
665              
666             Graham Ollis <gollis@sesda3.com>
667              
668             =head1 COPYRIGHT AND LICENSE
669              
670             This software is copyright (c) 2012 by NASA GSFC.
671              
672             This is free software; you can redistribute it and/or modify it under
673             the same terms as the Perl 5 programming language system itself.
674              
675             =cut