File Coverage

blib/lib/Mojolicious/Plugin/AccessControl.pm
Criterion Covered Total %
statement 61 62 98.3
branch 22 28 78.5
condition 10 14 71.4
subroutine 12 12 100.0
pod 1 1 100.0
total 106 117 90.6


line stmt bran cond sub pod time code
1             package Mojolicious::Plugin::AccessControl;
2 2     2   28476 use strict;
  2         4  
  2         88  
3 2     2   13 use warnings;
  2         4  
  2         115  
4             our $VERSION = '0.04';
5              
6 2     2   941 use Mojo::Base 'Mojolicious::Plugin';
  2         12487  
  2         19  
7 2     2   5388 use Net::CIDR::Lite;
  2         10137  
  2         87  
8 2     2   162 use Carp ();
  2         7  
  2         574  
9              
10 2     2   17 use constant CONDITION_NAME => 'access';
  2         5  
  2         2345  
11              
12             sub register {
13 1     1 1 64 my ( $self, $app ) = @_;
14              
15             $app->routes->add_condition(
16             CONDITION_NAME() => sub {
17 24     24   1112214 my ( $r, $c, $cap, $args ) = @_;
18 24   50     130 $args ||= [];
19              
20 24         50 my $opt = {};
21 24         118 my @rule_list = @$args;
22              
23 24 100       86 if ( ref $args->[0] eq 'HASH' ) {
24 3         8 $opt = $args->[0];
25              
26             # Remove option ref from rule list
27 3         12 @rule_list = splice(@$args, 1);
28             }
29              
30 24 50 66     117 if ( $opt->{on_deny} && ref $opt->{on_deny} ne 'CODE' ) {
31 0         0 Carp::croak "on_deny must be a CODEREF";
32             }
33              
34 24 100       159 $opt->{cache} = 1 unless ( defined $opt->{cache} );
35 24 50 66     184 my $rules = ( $opt->{cache} )
36             ? ( $r->{ __PACKAGE__ . '._rules' } ||= $self->_rules(@rule_list) ) # caches to Mojolicious::Routes::Route
37             : $self->_rules(@rule_list);
38              
39 24         70 for my $rule (@$rules) {
40 24         44 my ( $check, $allow ) = @{$rule};
  24         59  
41              
42 24         63 my $result = $check->($c);
43 24 100 66     1328 if ( defined $result && $result ) {
44              
45             # denied
46 21 100 100     298 if ( !$allow && $opt->{on_deny} ) {
47 3         13 $opt->{on_deny}->($c);
48             }
49              
50 21         3587 return $allow;
51             }
52             }
53              
54 3         43 return 1;
55             }
56 1         31 );
57             }
58              
59             sub _rules {
60 8     8   24 my ($self, @args) = @_;
61              
62 8         17 my @rules;
63 8         30 for ( my $i = 0; $i < @args; $i += 2 ) {
64 11         33 my ( $allowing, $rule ) = ( $args[$i], $args[ $i + 1 ] );
65              
66 11 50       71 Carp::croak "must be allow or deny"
67             unless $allowing =~ /^(allow|deny)$/;
68              
69 11 100       32 $allowing = ( $allowing eq 'allow' ) ? 1 : 0;
70 11         18 my $check = $rule;
71              
72 11 100       73 if ( $rule =~ /^ALL$/i ) {
    100          
    100          
73 3     3   17 $check = sub {1};
  3         6  
74             }
75             elsif ( $rule =~ /[A-Z]$/i ) {
76             $check = sub {
77 6     6   29 my $host = $_[0]->req->env->{'REMOTE_HOST'};
78 6 50       484 return unless defined $host; # skip
79 6         122 return $host =~ /^(.*\.)?\Q${rule}\E$/;
80 2         23 };
81             }
82             elsif ( ref($rule) ne 'CODE' ) {
83 5         125 my $cidr = Net::CIDR::Lite->new();
84 5         73 $cidr->add_any($rule);
85              
86             $check = sub {
87 12     12   286 my $addr = $_[0]->tx->remote_address;
88 12 50       968 if ( defined $addr ) {
89 12 50       67 return ( $cidr->find($addr) ) ? 1 : 0;
90             }
91 5         732 };
92             }
93              
94 11         59 push @rules, [ $check => $allowing ];
95             }
96              
97 8         47 return \@rules;
98             }
99              
100             1;
101             __END__