File Coverage

blib/lib/Apache/AppSamurai/AuthBase.pm
Criterion Covered Total %
statement 12 89 13.4
branch 0 42 0.0
condition 0 16 0.0
subroutine 4 13 30.7
pod 9 9 100.0
total 25 169 14.7


line stmt bran cond sub pod time code
1             # Apache::AppSamurai::AuthBase - AppSamurai authentication plugin base
2             # module.
3              
4             # $Id: AuthBase.pm,v 1.15 2008/04/30 21:40:05 pauldoom Exp $
5              
6             ##
7             # Copyright (c) 2008 Paul M. Hirsch (paul@voltagenoir.org).
8             # All rights reserved.
9             #
10             # This program is free software; you can redistribute it and/or modify it under
11             # the same terms as Perl itself.
12             ##
13              
14             # This is a base authentication wrapper for Apache::AppSamurai.
15             # AppSamurai can use one or more authentication methods to authenticate
16             # users. Each method, besides the special AuthServer method, requires
17             # a module name Apache::AppSamurai::. All AppSamurai::AuthXXX
18             # modules should be derived from this base module.
19              
20             package Apache::AppSamurai::AuthBase;
21 1     1   25353 use strict;
  1         4  
  1         43  
22 1     1   6 use warnings;
  1         3  
  1         35  
23              
24 1     1   6 use vars qw($VERSION);
  1         2  
  1         72  
25             $VERSION = substr(q$Revision: 1.15 $, 10, -1);
26              
27 1     1   7 use Carp;
  1         2  
  1         1221  
28              
29             sub new {
30 0     0 1   my $this = shift;
31 0   0       my $class = (ref($this) || ($this));
32 0           my $me = {};
33              
34 0           bless($me, $class);
35              
36             # Call Configure to fill out the $me->{conf} hash
37 0           $me->Configure(@_);
38 0           $me->{init} = 0;
39 0           $me->{errors} = [];
40              
41             # If the username and password were passed with the new request,
42             # process and return immediately.
43 0 0 0       if (defined($me->{conf}{user}) && defined($me->{conf}{pass})) {
44 0           return $me->Authenticate($me->{conf}{user}, $me->{conf}{pass});
45             }
46 0           return $me;
47             }
48              
49             #### OVERRIDE Configure(), Initialize(), and Authenticator() FOR NEW ####
50             #### AuthXXX MODULES ####
51              
52             # Configure the conf hash. If you want to override theses defaults, or
53             # add new ones, create your own Configure() in your AuthXXX module
54             sub Configure {
55 0     0 1   my $self = shift;
56             # Initial configuration. Put defaults here before the @_ args are
57             # pulled in.
58 0           $self->{conf} = { UserMin => 3,
59             UserMax => 256,
60             UserChars => '\w\d_\-\.',
61             UserStripWhite => 1,
62             UserUc => 0,
63             UserLc => 0,
64             PassMin => 4,
65             PassMax => 16384,
66             PassChars => '\w\d !\@\#\$\%\^\&\*:\,\.\?\-_=\+',
67             PassStripWhite => 0,
68             DefaultLogLevel => 'error',
69             @_,
70             };
71              
72 0           return 1;
73             }
74              
75              
76             # All setup should go here, including configuring authentication modules.
77             # This will be called by Authenticate() 99% of the time, but for some auth
78             # types, may be useful to be called in new() as well.
79             sub Initialize {
80 0     0 1   my $self = shift;
81             # May only be initialized once
82 0 0         ($self->{init} == 1) && (return 1);
83            
84             # Make sure we have config (in case someone tries to call us directly)
85 0 0 0       (defined($self->{conf}) && (scalar keys %{$self->{conf}})) || (croak "Could not initialize! Module not configured!");
  0            
86              
87             # Make sure to complain if something goes wrong
88 0           (1 == 1) || ($self->AddError("Failed to initialize in " . __PACKAGE__ . "::Initialize") && return 0);
89              
90             # Set this after successful initialization
91 0           $self->{init} = 1;
92 0           return 1;
93             }
94              
95              
96             # Perform the authentication. Returns a "yes" value (1, true) on success
97             # and "no" value (0, false, or undef) on failure. The function takes
98             # three arguments. 1) The object itself 2) The username 3) The password
99             # The username and password at this point have been through ALL checks in
100             # the module. (Valid chars, valid length, etc,)
101             sub Authenticator {
102 0     0 1   my $self = shift;
103 0           my $user = shift;
104 0           my $pass = shift;
105              
106             # Enter stuff here
107              
108             # DEFAULT DENY #
109 0           return 0;
110             }
111              
112             ###########################################################################
113              
114              
115             # The bulk of the work happens here, calling Initialize(), CheckInput(), and
116             # Authenticator(), and returning a true (1)/ false (0) status.
117             sub Authenticate {
118 0     0 1   my $self = shift;
119 0 0         (scalar(@_) == 2) || (croak 'Usage: $a->Authenticate($user, $pass);');
120 0           my ($user, $pass) = @_;
121            
122             ## DEFAULT DENY ##
123 0           my $authenticated = 0;
124              
125             # Check for clean input.
126 0 0 0       ($user = $self->CheckInputUser($user)) || ($self->AddError('warn', 'Invalid username') && return 0);
127 0 0 0       ($pass = $self->CheckInputPass($pass)) || ($self->AddError('warn', 'Invalid password') && return 0);
128              
129             # Initialize if not yet done
130 0 0         if (!$self->{init}) {
131 0 0         ($self->Initialize()) || (return 0);
132             }
133              
134             ## This is where the Authentication happens. Create your own overridden
135             # Authenticator functions today!
136 0 0         ($self->Authenticator($user, $pass)) && ($authenticated = 1);
137              
138 0           return $authenticated;
139             }
140              
141              
142             # Filter or reject the username. Return the username (with
143             # modifications, if needed) on success, or nothing on failure.
144             # CUSTOMIZE THIS TO ONLY ALLOW VALID USERNAMES FOR YOUR AUTH MODULE!
145             # BE CAREFUL IF YOU RETURN AN ALTERED USERNAME! Most bases should be covered
146             # by the various UserXXX config options, but add more as needed here.
147             sub CheckInputUser {
148 0     0 1   my $self = shift;
149 0   0       my $user = (shift || return undef);
150              
151             # Strip surrounding whitespace, if so configured
152 0 0         if ($self->{conf}{UserStripWhite}) {
153 0           $user =~ s/^\s*(.+?)\s*$/$1/;
154             }
155 0           my $ulen = length($user);
156            
157             # Check username against the list of valid username characters
158 0 0         unless ($user =~ /^([$self->{conf}{UserChars}]+)$/) {
159 0           $self->AddError('warn', 'Username contains invalid characters');
160 0           return undef;
161             }
162              
163             # Check for a valid username length.
164 0 0         if ($ulen < $self->{conf}{UserMin}) {
    0          
165 0           $self->AddError('warn', "Username too small ($ulen)");
166 0           return undef;
167             } elsif ($ulen > $self->{conf}{UserMax}) {
168 0           $self->AddError('warn', "Username too large ($ulen)");
169 0           return undef;
170             }
171              
172             # uc() or lc() if so configured.
173 0 0         if ($self->{conf}{UserUc}) {
    0          
174 0           $user = uc($user);
175             } elsif ($self->{conf}{UserLc}) {
176 0           $user = lc($user);
177             }
178              
179 0           return $user;
180             }
181              
182              
183             # Filter or reject the password. Return the password (with
184             # modifications, if needed) on success, or nothing on failure.
185             # CUSTOMIZE THIS TO ONLY ALLOW VALID PASSWORDS FOR YOUR AUTH MODULE!
186             # BE CAREFUL IF YOU RETURN AN ALTERED PASSWORD! In almost all cases,
187             # you should fail out instead of trying to help a user. No lc($pass)
188             # unless your backend authentication checker really is case insensitive.
189             sub CheckInputPass {
190 0     0 1   my $self = shift;
191 0   0       my $pass = (shift || return undef);
192              
193             # Strip surrounding whitespace, if so configured
194 0 0         if ($self->{conf}{PassStripWhite}) {
195 0           $pass =~ s/^\s*(.+?)\s*$/$1/;
196             }
197 0           my $plen = length($pass);
198              
199             # Check password against the list of valid password characters
200 0 0         unless ($pass =~ /^([$self->{conf}{PassChars}]+)$/) {
201 0           $self->AddError('warn', 'Password contains invalid characters');
202 0           return undef;
203             }
204              
205             # Check for a valid password length.
206 0 0         if ($plen < $self->{conf}{PassMin}) {
    0          
207 0           $self->AddError('warn', "Password too small ($plen)");
208 0           return undef;
209             } elsif ($plen > $self->{conf}{PassMax}) {
210 0           $self->AddError('warn', "Password too large ($plen)");
211 0           return undef;
212             }
213              
214 0           return $pass;
215             }
216              
217              
218             # Add error to the list
219             sub AddError {
220 0     0 1   my $self = shift;
221 0 0         if (scalar(@_) == 2) {
222 0           push(@{$self->{errors}}, [$_[0], ref($self) . ": " . $_[1]]);
  0            
223             } else {
224 0           push(@{$self->{errors}}, [$self->{conf}{DefaultLogLevel}, ref($self) . ": " . $_[0]]);
  0            
225             }
226 0           return 1;
227             }
228              
229             # Return an array of errors if there are any, or undef if there are not.
230             sub Errors {
231 0     0 1   my $self = shift;
232 0 0         if (scalar(@{$self->{errors}})) {
  0            
233 0           return $self->{errors};
234             }
235            
236 0           return undef;
237             }
238              
239             1; # End of Apache::AppSamurai::AuthBase
240              
241             __END__