File Coverage

blib/lib/WWW/Suffit/AuthDB/Realm.pm
Criterion Covered Total %
statement 26 143 18.1
branch 3 154 1.9
condition 2 91 2.2
subroutine 8 16 50.0
pod 2 2 100.0
total 41 406 10.1


line stmt bran cond sub pod time code
1             package WWW::Suffit::AuthDB::Realm;
2 3     3   23 use strict;
  3         9  
  3         149  
3 3     3   21 use utf8;
  3         6  
  3         19  
4              
5             =encoding utf8
6              
7             =head1 NAME
8              
9             WWW::Suffit::AuthDB::Realm - WWW::Suffit::AuthDB realm class
10              
11             =head1 SYNOPSIS
12              
13             use WWW::Suffit::AuthDB::Realm;
14              
15             =head1 DESCRIPTION
16              
17             This module provides AuthDB realm methods
18              
19             =head1 ATTRIBUTES
20              
21             This class implements the following attributes
22              
23             =head2 cached
24              
25             $realm = $realm->cached( 12345.123456789 );
26             my $cached = $realm->cached;
27              
28             Sets or returns time of caching realm data
29              
30             Default: 0
31              
32             =head2 cachekey
33              
34             $realm = $realm->cachekey( 'abcdef1234567890' );
35             my $cachekey = $realm->cachekey;
36              
37             Sets or returns the cache key string
38              
39             =head2 description
40              
41             $realm->description('Root page');
42             my $description = $realm->description;
43              
44             Sets and returns description of the realm
45              
46             =head2 error
47              
48             $realm = $realm->error( 'Oops' );
49             my $error = $realm->error;
50              
51             Sets or returns error string
52              
53             =head2 expires
54              
55             $realm = $realm->expires( 300 );
56             my $expires = $realm->expires;
57              
58             Sets or returns cache/object expiration time in seconds
59              
60             Default: 300 (5 min)
61              
62             =head2 id
63              
64             $realm = $realm->id( 2 );
65             my $id = $realm->id;
66              
67             Sets or returns id of realm
68              
69             Default: 0
70              
71             =head2 is_cached
72              
73             This attribute returns true if the realm data was cached
74              
75             Default: false
76              
77             =head2 realm
78              
79             $realm->realm('string');
80             my $real_string = $realm->realm;
81              
82             Sets and returns realm string of the realm object
83              
84             =head2 realmname
85              
86             $realm->realmname('root');
87             my $realmname = $realm->realmname;
88              
89             Sets and returns realmname of the realm object
90              
91             =head2 requirements
92              
93             $realm->requirements(['@alice', '%wheel']);
94             my $requirements = $relam->requirements; # ['@alice', '%wheel']
95              
96             Sets and returns groups and users of realm (array of users and groups)
97              
98             B Usernames should be prefixed with "B<@>", group names should be prefixed with "B<%>"
99              
100             =head2 requires_users
101              
102             my $reqs = $relam->requires; # [ {user => 'alice'}, { group => 'wheel'} ]
103              
104             Returns list of requiremets (as array ref) that allows access to specified realm
105              
106             =head2 satisfy
107              
108             $realm->satisfy('Any');
109             my $satisfy = $realm->satisfy;
110              
111             Sets and returns the satisfy policy (All, Any) of the realm object
112              
113             =head1 METHODS
114              
115             This class inherits all methods from L and implements the following new ones
116              
117             =head2 is_valid
118              
119             $realm->is_valid or die "Incorrect realm";
120              
121             Returns boolean status of realm's data
122              
123             =head2 mark
124              
125             Marks object as cached
126              
127             =head1 HISTORY
128              
129             See C file
130              
131             =head1 TO DO
132              
133             See C file
134              
135             =head1 SEE ALSO
136              
137             L, L
138              
139             =head1 AUTHOR
140              
141             Serż Minus (Sergey Lepenkov) L Eabalama@cpan.orgE
142              
143             =head1 COPYRIGHT
144              
145             Copyright (C) 1998-2026 D&D Corporation
146              
147             =head1 LICENSE
148              
149             This program is distributed under the terms of the Artistic License Version 2.0
150              
151             See the C file or L for details
152              
153             =cut
154              
155 3     3   231 use Mojo::Base -base;
  3         8  
  3         22  
156              
157 3     3   937 use Mojo::Util qw/steady_time/;
  3         8  
  3         281  
158              
159 3     3   24 use Acrux::RefUtil qw/is_integer/;
  3         5  
  3         188  
160              
161 3     3   4357 use Net::IP qw//;
  3         295980  
  3         204  
162              
163 3     3   37 use Socket qw/inet_aton AF_INET/;
  3         7  
  3         11059  
164              
165             has description => '';
166             has error => '';
167             has expires => 0;
168             has id => 0;
169             has realm => undef;
170             has realmname => undef;
171             has satisfy => undef;
172             has requirements=> sub { return {} }; # Segregated requirements
173             has is_cached => 0;
174             has cached => 0; # steady_time() of cached
175             has cachekey => '';
176              
177             sub is_valid {
178 1     1 1 16 my $self = shift;
179              
180             # No id found? -- is ok too
181 1 50       5 return 1 unless $self->id;
182              
183 1 50 33     11 unless (defined($self->realmname) && length($self->realmname)) {
184 0         0 $self->error("E1317: Incorrect realmname");
185 0         0 return 0;
186             }
187 1 50 33     25 if ($self->expires && $self->expires < time) {
188 0         0 $self->error("E1318: The realm data is expired");
189 0         0 return 0;
190             }
191              
192 1         9 return 1;
193             }
194             sub mark {
195 0     0 1   my $self = shift;
196 0   0       return $self->is_cached(1)->cached(shift || steady_time);
197             }
198              
199             sub _check_by_default {
200 0     0     my $self = shift;
201 0   0       my $reqs = $self->requirements->{'Default'} // [];
202 0           my $status = 0; # false by default
203 0           foreach my $r (@$reqs) {
204 0           my $ent = lc($r->{entity});
205 0 0         if ($ent eq 'allow') {
    0          
206 0           $status++;
207             } elsif ($ent eq 'deny') {
208 0           $status--;
209             }
210             }
211              
212 0 0         return ($status > 0) ? 1 : 0;
213             }
214             sub _check_by_usergroup {
215 0     0     my $self = shift;
216 0           my $username = shift;
217 0   0       my $groupnames = shift // []; # Is array of groups
218 0 0         $groupnames = [$groupnames] unless ref($groupnames);
219 0   0       my $reqs = $self->requirements->{'User/Group'} // [];
220 0           my $vu = 0; # false by default
221 0           my $status = 0; # false by default
222 0 0         return -1 unless scalar(@$reqs); # Skip if no requirements exists
223 0           foreach my $r (@$reqs) {
224 0           my $ent = lc($r->{entity});
225 0 0         if ($ent eq 'user') {
    0          
    0          
226 0 0         $status++ if _op('str', $username, $r->{op}, $r->{value});
227             } elsif ($ent eq 'group') {
228 0           foreach my $g (@$groupnames) {
229 0 0         $status++ if _op('str', $g, $r->{op}, $r->{value});
230             }
231             } elsif ($ent eq 'valid-user') {
232 0           $vu = 1;
233             }
234             }
235 0 0         return $status ? 1 : $vu;
236             }
237             sub _check_by_host {
238 0     0     my $self = shift;
239 0   0       my $ip = shift // '';
240 0   0       my $reqs = $self->requirements->{'Host'} // [];
241 0           my $status = 0; # false by default
242 0 0         return -1 unless scalar(@$reqs); # Skip if no requirements exists
243 0 0         return 0 unless length($ip); # No ip specified
244 0           foreach my $r (@$reqs) {
245 0           my $ent = lc($r->{entity});
246 0 0         if ($ent eq 'ip') {
    0          
247 0 0         $status++ if _op('ip', $ip, $r->{op}, $r->{value});
248             } elsif ($ent eq 'host') {
249 0   0       my $host = gethostbyaddr(inet_aton($ip), AF_INET) // '';
250 0 0         next unless length($host);
251 0 0         $status++ if _op('str', $host, $r->{op}, $r->{value});
252             }
253             }
254              
255 0           return $status;
256             }
257             sub _check_by_env {
258 0     0     my $self = shift;
259 0   0       my $reqs = $self->requirements->{'Env'} // [];
260 0           my $status = 0; # false by default
261 0 0         return -1 unless scalar(@$reqs); # Skip if no requirements exists
262 0           foreach my $r (@$reqs) {
263 0           my $varname = uc($r->{entity});
264 0 0         next unless length($varname);
265 0 0 0       my $varval = exists($ENV{$varname}) && defined($ENV{$varname}) ? $ENV{$varname} : '';
266 0 0         $status++ if _op(is_integer($varval) ? 'int' : 'str', $varval, $r->{op}, $r->{value});
    0          
267             }
268              
269 0           return $status;
270             }
271             sub _check_by_header {
272 0     0     my $self = shift;
273 0   0 0     my $cb = shift // sub { undef };
  0            
274 0 0 0       return 0 unless ref($cb) && ref($cb) eq 'CODE';
275 0   0       my $reqs = $self->requirements->{'Header'} // [];
276 0           my $status = 0; # false by default
277 0 0         return -1 unless scalar(@$reqs); # Skip if no requirements exists
278 0           foreach my $r (@$reqs) {
279 0           my $hkey = $r->{entity};
280 0 0         next unless length($hkey);
281 0   0       my $hval = $cb->($hkey) // '';
282 0 0         $status++ if _op(is_integer($hval) ? 'int' : 'str', $hval, $r->{op}, $r->{value});
    0          
283             }
284              
285 0           return $status;
286             }
287              
288             sub _op { # rule, test (from user), op, value (from db, requirements)
289 0   0 0     my $rule = shift || 'str'; # str, int, ip
290 0           my $tst = shift; # from user
291 0   0       my $op = shift || 'eq';
292 0           my $val = shift; # from db, requirements
293              
294             # IP
295 0           my ($subnet, $ip);
296 0 0         if ($rule eq 'ip') {
297 0 0         $subnet = Net::IP->new($val) or warn(sprintf("Incorrect Network/CIDR: %s", Net::IP::Error()));
298 0 0         $ip = Net::IP->new($tst) or warn(sprintf("Incorrect client IP: %s", Net::IP::Error()));
299 0 0 0       return 0 unless defined($subnet) && defined($ip);
300             }
301              
302             # Op
303 0 0         if ($op eq 'eq') { # operator => '==', title => 'equal to'
    0          
    0          
    0          
    0          
    0          
    0          
    0          
304 0 0         if ($rule eq 'str') {
    0          
    0          
305 0   0       return defined($tst) && defined($val) && $tst eq $val;
306             } elsif ($rule eq 'int') {
307 0   0       return is_integer($tst) && is_integer($val) && $tst == $val;
308             } elsif ($rule eq 'ip') {
309 0 0         return $subnet->overlaps($ip) ? 1 : 0;
310             }
311             } elsif ($op eq 'ne') { # operator => '!=', title => 'not equal'
312 0 0         if ($rule eq 'str') {
    0          
    0          
313 0   0       return defined($tst) && defined($val) && $tst ne $val;
314             } elsif ($rule eq 'int') {
315 0   0       return is_integer($tst) && is_integer($val) && $tst != $val;
316             } elsif ($rule eq 'ip') {
317 0 0         return $subnet->overlaps($ip) ? 0 : 1;
318             }
319             } elsif ($op eq 'gt') { # operator => '>', title => 'greater than'
320 0 0         if ($rule eq 'str') {
    0          
    0          
321 0   0       return defined($tst) && defined($val) && $tst gt $val;
322             } elsif ($rule eq 'int') {
323 0   0       return is_integer($tst) && is_integer($val) && $tst > $val;
324             } elsif ($rule eq 'ip') {
325 0 0         return $subnet->bincomp($op, $ip) ? 1 : 0;
326             }
327             } elsif ($op eq 'lt') { # operator => '<', title => 'less than'
328 0 0         if ($rule eq 'str') {
    0          
    0          
329 0   0       return defined($tst) && defined($val) && $tst lt $val;
330             } elsif ($rule eq 'int') {
331 0   0       return is_integer($tst) && is_integer($val) && $tst < $val;
332             } elsif ($rule eq 'ip') {
333 0 0         return $subnet->bincomp($op, $ip) ? 1 : 0;
334             }
335             } elsif ($op eq 'ge') { # operator => '>=', title => 'greater than or equal to'
336 0 0         if ($rule eq 'str') {
    0          
    0          
337 0   0       return defined($tst) && defined($val) && $tst ge $val;
338             } elsif ($rule eq 'int') {
339 0   0       return is_integer($tst) && is_integer($val) && $tst >= $val;
340             } elsif ($rule eq 'ip') {
341 0 0         return $subnet->bincomp($op, $ip) ? 1 : 0;
342             }
343             } elsif ($op eq 'le') { # operator => '<=', title => 'less than or equal to'
344 0 0         if ($rule eq 'str') {
    0          
    0          
345 0   0       return defined($tst) && defined($val) && $tst le $val;
346             } elsif ($rule eq 'int') {
347 0   0       return is_integer($tst) && is_integer($val) && $tst <= $val;
348             } elsif ($rule eq 'ip') {
349 0 0         return $subnet->bincomp($op, $ip) ? 1 : 0;
350             }
351             } elsif ($op eq 're') { # operator => '=~', title => 'regexp match'
352 0 0 0       return 0 unless defined($tst) && length($tst);
353 0 0 0       return 0 unless defined($val) && length($val);
354 0           my $vre = qr/$val/;
355 0 0         if ($rule eq 'str') {
    0          
    0          
356 0           return $tst =~ $vre;
357             } elsif ($rule eq 'int') {
358 0           return $tst =~ $vre;
359             } elsif ($rule eq 'ip') {
360 0           return $tst =~ $vre;
361             }
362             } elsif ($op eq 'rn') { # operator => '!~', title => 'regexp not match'
363 0 0 0       return 0 unless defined($tst) && length($tst);
364 0 0 0       return 0 unless defined($val) && length($val);
365 0           my $vre = qr/$val/;
366 0 0         if ($rule eq 'str') {
    0          
    0          
367 0           return $tst !~ $vre;
368             } elsif ($rule eq 'int') {
369 0           return $tst !~ $vre;
370             } elsif ($rule eq 'ip') {
371 0           return $tst =~ $vre;
372             }
373             }
374              
375 0           return 0; # False by default
376             }
377              
378             1;
379              
380             __END__