File Coverage

blib/lib/CGI/Application/Plugin/Authentication/Driver.pm
Criterion Covered Total %
statement 68 68 100.0
branch 20 20 100.0
condition n/a
subroutine 12 12 100.0
pod 9 9 100.0
total 109 109 100.0


line stmt bran cond sub pod time code
1             package CGI::Application::Plugin::Authentication::Driver;
2              
3 23     23   2774 use strict;
  23         50  
  23         892  
4 23     23   128 use warnings;
  23         48  
  23         5755  
5             our $VERSION = '0.20';
6              
7 23     23   166 use UNIVERSAL::require;
  23         56  
  23         337  
8              
9             =head1 NAME
10              
11             CGI::Application::Plugin::Authentication::Driver - Base module for building driver classes
12             for CGI::Application::Plugin::Authentication
13              
14             =head1 VERSION
15              
16             This document describes CGI::Application::Plugin::Authentication::Driver version 0.20
17              
18             =head1 SYNOPSIS
19              
20             package CGI::Application::Plugin::Authentication::Driver::MyDriver;
21             use base qw(CGI::Application::Plugin::Authentication::Driver);
22              
23             sub verify_credentials {
24             my $self = shift;
25             my @credentials = @_;
26              
27             if ( >>> Validate Credentials <<< ) {
28             return $credentials[0];
29             }
30             return;
31             }
32              
33              
34             =head1 DESCRIPTION
35              
36             This module is a base class for all driver classes for the L
37             plugin. Each driver class is required to provide only one method to validate the given credentials.
38             Normally only two credentials will be passed in (username and password), but you can configure the plugin
39             to handle any number of credentials (for example you may require the user to enter a group name, or domain name
40             as well as a username and password).
41              
42              
43             =head1 FIELD FILTERS
44              
45             It is quite common for passwords to be stored using some form of one way encryption. Unix crypt being the
46             old standard in the Unix community, however MD5 or SHA1 hashes are more popular today. In order to
47             simplify the validation routines some methods have been provided to help test these passwords. When
48             configuring a Driver (and if the driver supports it), you can specify which fields are encoded, and which
49             method is used for the encoding by specifying a filter on the field in question.
50              
51             CREDENTIALS => ['authen_username', 'authen_password'],
52             DRIVERS => [ 'DBI',
53             DSN => '...',
54             TABLE => 'users',
55             CONSTRAINTS => {
56             username => '__CREDENTIAL_1__',
57             'MD5:password' => '__CREDENTIAL_2__',
58             }
59             ],
60              
61             Here we are saying that the password field is encoded using an MD5 hash, and should be checked accordingly.
62              
63             =head2 Filter options
64              
65             Some of the filters may have multiple forms. For example there are three forms of MD5 hashes: binary, base64 and hex.
66             You can specify these extra options by using an underscore to separate it from the filter name.
67              
68             'MD5_base64:password'
69              
70              
71             =head2 Chained Filters
72              
73             it is possible to chain multiple filters. This can be useful if your MD5 strings are stored in hex format. Hex numbers are
74             case insensitive, so the may be stored in either upper or lower case. To make this consistent, you can MD5 encode the
75             password first, and then upper case the results. The filters are applied from the inside out:
76              
77             'uc:MD5_hex:password'
78              
79             =head2 Custom Filters
80              
81             If your field is encoded using a custom technique, then you can provide a custom filter function. This can be
82             be done by providing a FILTERS option that contains a hash of filter subroutines that are keyed by their name.
83             You can then use the filter name on any of the fields as if it was a builtin filter.
84              
85             CREDENTIALS => ['authen_username', 'authen_password'],
86             DRIVERS => [ 'DBI',
87             DSN => '...',
88             TABLE => 'users',
89             CONSTRAINTS => {
90             username => '__CREDENTIAL_1__',
91             'rot13:password' => '__CREDENTIAL_2__',
92             }
93             FILTERS => { rot13 => \&rot13_filter },
94             ],
95              
96             sub rot13_filter {
97             my $param = shift;
98             my $value = shift;
99             $value =~ tr/A-Za-z/N-ZA-Mn-za-m/;
100             return $value;
101             }
102              
103             Please see the documentation for the driver that you are using to make sure that it supports encoded
104             fields.
105              
106              
107             =head2 Builtin Filters
108              
109             Here is a list of the filters that are provided with this module:
110              
111             =over 4
112              
113             =item crypt - provided by perl C function
114              
115             =item MD5 - requires Digest::MD5
116              
117             =item SHA1 - requires Digest::SHA1
118              
119             =item uc - provided by the perl C function
120              
121             =item lc - provided by the perl C function
122              
123             =item trim - removed whitespace from the start and end of the field
124              
125             =back
126              
127              
128             =head1 METHODS
129              
130             =head2 new
131              
132             This is a constructor that can create a new Driver object. It requires an Authentication object as its
133             first parameter, and any number of other parameters that will be used as options depending on which
134             Driver object is being created. You shouldn't need to call this as the Authentication plugin takes care
135             of creating Driver objects.
136              
137             =cut
138              
139             sub new {
140 194     194 1 336 my $class = shift;
141 194         453 my $self = {};
142 194         345 my $authen = shift;
143 194         7735 my @options = @_;
144              
145 194         871 bless $self, $class;
146 194         737 $self->{authen} = $authen;
147 194         632 Scalar::Util::weaken($self->{authen}); # weaken circular reference
148 194         782 $self->{options} = \@options;
149 194         858 $self->initialize;
150 194         1021 return $self;
151             }
152              
153             =head2 initialize
154              
155             This method will be called right after a new Driver object is created. So any startup customizations
156             can be dealt with here.
157              
158             =cut
159              
160             sub initialize {
161 194     194 1 387 my $self = shift;
162             # override this in the subclass if you need it
163 194         376 return;
164             }
165              
166             =head2 options
167              
168             This will return a list of options that were provided when this driver was configured by the user.
169              
170             =cut
171              
172 186     186 1 335 sub options { return (@{$_[0]->{options}}) }
  186         1027  
173              
174             =head2 authen
175              
176             This will return the underlying L object. In most cases it will
177             not be necessary to access this.
178              
179             =cut
180              
181 1     1 1 5 sub authen { return $_[0]->{authen} }
182              
183             =head2 find_option
184              
185             This method will search the Driver options for a specific key and return
186             the value it finds.
187              
188             =cut
189              
190             sub find_option {
191 12     12 1 1349 my $self = shift;
192 12         20 my $key = shift;
193 12         50 my @options = $self->options;
194 12         19 my $marker = 0;
195 12         26 foreach my $option (@options) {
196 20 100       51 if ($marker) {
    100          
197 7         27 return $option;
198             } elsif ($option eq $key) {
199             # We need the next element
200 7         13 $marker = 1;
201             }
202             }
203 5         19 return;
204             }
205              
206             =head2 verify_credentials
207              
208             This method needs to be provided by the driver class. It needs to be an object method that accepts a list of
209             credentials, and will verify that the credentials are valid, and return a username that will be used to identify
210             this login (usually you will just return the value of the first credential, however you are not bound to that)..
211              
212             =cut
213              
214             sub verify_credentials {
215 1     1 1 17 die "verify_credentials must be implemented in the subclass";
216             }
217              
218             =head2 filter
219              
220             This method can be used to filter a field (usually password fields) using a number of standard or
221             custom encoding techniques. See the section on Builtin Filters above to see what filters are available
222             When using a custom filter, you will need to provide a FILTERS option in the configuration of the DRIVER (See the
223             section on FIELD FILTERS above for an example). By default, if no filter is specified, it is
224             returned as is. This means that you can run all fields through this function even if they
225             don't have any filters to simplify the driver code.
226              
227             my $filtered = $self->filter('MD5_hex:password', 'foobar');
228              
229             - or -
230              
231             # custom filter
232             my $filtered = $self->filter('foobar:password', 'foo');
233              
234             - or -
235              
236             # effectively a noop
237             my $filtered = $self->filter('username', 'foo');
238              
239              
240              
241             =cut
242              
243             sub filter {
244 159     159 1 1049 my $self = shift;
245 159         183 my $field = shift;
246 159         222 my $plain = shift;
247 159         500 my @other = shift;
248              
249 159 100       563 return unless defined $plain;
250              
251 149         1186 my @filters = split /\:/, $field;
252 149         273 my $fieldname = pop @filters;
253              
254 149         204 my $filtered = $plain;
255 149         427 foreach my $filter (reverse @filters) {
256              
257 45         191 my ($filter_name, $param) = split /_/, $filter;
258 45         122 my $class = 'CGI::Application::Plugin::Authentication::Driver::Filter::' . lc $filter_name;
259 45 100       305 if ( $class->require ) {
260             # found a filter
261 39         1616 $filtered = $class->filter( $param, $filtered, @other );
262             } else {
263             # see if the configuration has a custom filter defined
264 6         574 my $custom_filters = $self->find_option('FILTERS');
265 6 100       14 if ( $custom_filters ) {
266 4 100       22 die "the FILTERS configuration option must be a hashref"
267             unless ref( $custom_filters ) eq 'HASH';
268 3 100       10 if ( $custom_filters->{$filter_name} ) {
269 2 100       17 die "the '$filter' filter listed in FILTERS must be a subroutine reference"
270             unless ref( $custom_filters->{$filter_name} ) eq 'CODE';
271 1         5 $filtered = $custom_filters->{$filter_name}->( $param, $filtered, @other );
272             } else {
273 1         13 die "No filter found for '$filter_name'";
274             }
275             } else {
276 2         24 die "No filters found for '$filter'";
277             }
278             }
279             }
280 140         622 return $filtered;
281             }
282              
283             =head2 check_filtered
284              
285             This method can be used to test filtered fields (usually password fields) against a number of standard or
286             custom encoding techniques. The following encoding techniques are provided: plain, MD5, SHA1, crypt.
287             When using a custom encoder, you will need to provide it in the configuration of the DRIVERS (See the
288             section on ENCODED PASSWORDS above for an example). By default, if no encoding is specified, it is
289             assumed to be 'plain'. This means that you can run all fields through this function even if they
290             don't have any encoding to simplify the driver code.
291              
292             my $verified = $self->check_filtered('MD5:password', 'foobar', 'OFj2IjCsPJFfMAxmQxLGPw');
293              
294             - or -
295              
296             # custom encoder
297             my $verified = $self->check_filtered('foobar:password', 'foo', 'bar');
298              
299             - or -
300              
301             # a field that isn't filtered (effectively just checks for equality on second and third args)
302             my $verified = $self->check_filtered('username', 'foobar', 'foobar');
303             my $verified = $self->check_filtered('plain:username', 'foobar', 'foobar');
304              
305             =cut
306              
307             sub check_filtered {
308 16     16 1 24 my $self = shift;
309 16         23 my $field = shift;
310 16         20 my $plain = shift;
311 16         31 my $filtered = shift;
312              
313 16 100       42 return ($self->filter($field, $plain, $filtered) eq $filtered) ? 1 : 0;
314             }
315              
316             =head2 strip_field_names
317              
318             This method will take a field name (or list of names) and strip off the leading encoding type.
319             For example if you passed in 'MD5:password' the method would return 'password'.
320              
321             my $fieldname = $self->strip_field_names('MD5:password');
322              
323             =cut
324              
325             sub strip_field_names {
326 162     162 1 4241 my $self = shift;
327 162         273 my @fields = @_;
328              
329 162         296 foreach (@fields) {
330 172         580 s/^.*://;
331             }
332 162 100       455 if (wantarray()) {
333 37         161 return @fields;
334             } else {
335 125         399 return $fields[0];
336             }
337             }
338              
339              
340             =head1 SEE ALSO
341              
342             L, perl(1)
343              
344              
345             =head1 AUTHOR
346              
347             Cees Hek
348              
349              
350             =head1 LICENCE AND COPYRIGHT
351              
352             Copyright (c) 2005, SiteSuite. All rights reserved.
353              
354             This module is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
355              
356              
357             =head1 DISCLAIMER OF WARRANTY
358              
359             BECAUSE THIS SOFTWARE IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE SOFTWARE, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE SOFTWARE "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE SOFTWARE IS WITH YOU. SHOULD THE SOFTWARE PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR, OR CORRECTION.
360              
361             IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE SOFTWARE AS PERMITTED BY THE ABOVE LICENCE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE SOFTWARE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE SOFTWARE TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
362              
363             =cut
364              
365             1;