File Coverage

blib/lib/PlugAuth/Plugin/Audit.pm
Criterion Covered Total %
statement 84 87 96.5
branch 6 12 50.0
condition 3 8 37.5
subroutine 16 17 94.1
pod 0 2 0.0
total 109 126 86.5


line stmt bran cond sub pod time code
1             package PlugAuth::Plugin::Audit;
2              
3 1     1   6 use strict;
  1         2  
  1         30  
4 1     1   6 use warnings;
  1         3  
  1         35  
5 1     1   20 use 5.010001;
  1         3  
6 1     1   5 use Role::Tiny::With;
  1         2  
  1         45  
7 1     1   5 use Path::Class::Dir;
  1         31  
  1         26  
8 1     1   5 use Path::Class::File;
  1         2  
  1         23  
9 1     1   5 use File::Glob qw( bsd_glob );
  1         3  
  1         62  
10 1     1   5 use YAML::XS qw( Dump LoadFile );
  1         1  
  1         43  
11 1     1   4 use DateTime;
  1         2  
  1         827  
12              
13             with 'PlugAuth::Role::Plugin';
14              
15             # ABSTRACT: Audit log for authentication/authorization
16             our $VERSION = '0.38'; # VERSION
17              
18              
19             with 'PlugAuth::Role::Plugin';
20              
21             sub init
22             {
23 1     1 0 2 my($self) = @_;
24            
25             $self->app->routes->route('/audit')->name('audit_check')->get(sub {
26 1     1   9298 my($c) = @_;
27 1         65 my ($day,$month,$year) = (localtime(time))[3,4,5];
28 1         7 $year+=1900;
29 1         3 $month++;
30             $c->stash->{autodata} = {
31 1   50     22 today => join('-', $year, sprintf("%02d", $month), sprintf("%02d", $day)),
32             version => $PlugAuth::Plugin::Audit::VERSION // 'dev',
33             };
34 1         5 });
35            
36             $self->app->routes->route('/audit/today')->name('audit_today')->get(sub {
37 1     1   5158 my($c) = @_;
38 1         70 my ($day,$month,$year) = (localtime(time))[3,4,5];
39 1         8 $year+=1900;
40 1         4 $month++;
41 1         20 $c->redirect_to($c->url_for('audit', year => $year, month => sprintf("%02d", $month), day => sprintf("%02d", $day)));
42 1         433 });
43            
44             # TODO: provide an interface for this
45             # in Clustericious
46             my $auth = sub {
47 2     2   11229 my $c = shift;
48 2         20 my $plugin = $self->_self_auth_plugin;
49 2 50       38 return 1 unless defined $plugin;
50 0 0       0 return 0 unless $plugin->authenticate($c, 'ACPS');
51 0 0       0 return 0 unless $plugin->authorize($c, 'accounts', $c->req->url->path);
52 0         0 return 1;
53 1         266 };
54            
55       0     my $authz = sub {
56 1         2 };
57            
58             $self->app->routes->under->to({ cb => $auth })->route('/audit/:year/:month/:day')->name('audit')->get(sub {
59 2     2   443 my($c) = @_;
60 2         13 my $year = $c->stash('year');
61 2         45 my $month = $c->stash('month');
62 2         30 my $day = $c->stash('day');
63 2 50 33     88 return $c->render_message('not ok', 404)
      33        
64             unless $year =~ /^\d\d\d\d$/
65             && $month =~ /^\d\d?$/
66             && $day =~ /^\d\d?$/;
67 2         25 my $filename = $self->log_filename({ year => $year, month => $month, day => $day });
68 2 100       23 return $c->render_message('not ok', 404)
69             unless -r $filename;
70             my(@events) = map {
71 1         80 my $event = $_;
  1         224  
72 1         16 my $dt = DateTime->from_epoch( epoch => $event->{time} );
73 1         702 $dt->set_time_zone('local');
74 1         6307 $event->{time_epoch} = delete $event->{time};
75 1         11 $event->{time_human} = $dt->strftime("%a, %d %b %Y %H:%M:%S %z");
76 1         392 $event->{time_computer} = $dt->strftime("%Y-%m-%dT%H:%M:%S%z");
77 1         245 $event;
78             } LoadFile($filename->stringify);
79 1         16 $c->stash->{autodata} = \@events;
80 1         4 });
81            
82 1         503 my @event_names = qw(
83             create_user
84             delete_user
85             create_group
86             delete_group
87             update_group
88             grant
89             revoke
90             change_password
91             );
92            
93 1         2 foreach my $event_name (@event_names)
94             {
95             $self->app->on($event_name => sub {
96 1     1   43 my($app, $args) = @_;
97            
98 1         8 my %info = %$args;
99 1         6 $info{time} = time;
100 1         6 $info{event} = $event_name;
101 1         9 my $filename = $self->log_filename($info{time});
102 1         15 open(my $fh, '>>', $filename->stringify);
103 1         246 print $fh Dump(\%info);
104 1         68 close $fh;
105 8         44 });
106             }
107             }
108              
109             sub log_filename
110             {
111 3     3 0 14 my($self, $time) = @_;
112            
113 3         14 my($day, $month, $year);
114            
115 3 100       18 if(ref $time)
116             {
117 2         9 $day = $time->{day};
118 2         10 $month = $time->{month};
119 2         7 $year = $time->{year};
120             }
121             else
122             {
123 1         57 ($day,$month,$year) = (localtime($time))[3,4,5];
124 1         6 $year += 1900;
125 1         4 $month++;
126             }
127            
128 3         257 my $filename = Path::Class::File->new(
129             bsd_glob('~/.plugauth_plugin_audit'),
130             sprintf("%04d", $year),
131             sprintf("%02d", $month),
132             sprintf("%02d", $day),
133             'audit.log',
134             );
135 3         675 $filename->dir->mkpath(0,0700);
136 3         1351 $filename;
137             }
138              
139             1;
140              
141             __END__
142              
143             =pod
144              
145             =encoding UTF-8
146              
147             =head1 NAME
148              
149             PlugAuth::Plugin::Audit - Audit log for authentication/authorization
150              
151             =head1 VERSION
152              
153             version 0.38
154              
155             =head1 SYNOPSIS
156              
157             PlugAuth.conf:
158              
159             ---
160             plugins:
161             - PlugAuth::Plugin::Audit: {}
162              
163             =head1 ROUTES
164              
165             =head2 Public routes
166              
167             These routes work for unauthenticated and unauthorized users.
168              
169             =head3 GET /audit
170              
171             You can do a simple GET on this route to see if the plugin is loaded.
172             It will return a JSON string with the version of the plugin as the body
173             and 200 if the plugin is available, if not L<PlugAuth> will return 404.
174              
175             =head2 Accounts Routes
176              
177             These routes are available to users authenticates and authorized to perform
178             the 'accounts' action.
179              
180             =head3 GET /audit/:year/:month/:day
181              
182             Return the audit entries for the given day.
183              
184             =head3 GET /audit/today
185              
186             Redirects to the appropriate URL for today's audit log.
187              
188             =head1 AUTHOR
189              
190             Graham Ollis <gollis@sesda3.com>
191              
192             =head1 COPYRIGHT AND LICENSE
193              
194             This software is copyright (c) 2012 by NASA GSFC.
195              
196             This is free software; you can redistribute it and/or modify it under
197             the same terms as the Perl 5 programming language system itself.
198              
199             =cut