File Coverage

blib/lib/Authen/Users.pm
Criterion Covered Total %
statement 18 197 9.1
branch 0 88 0.0
condition 0 10 0.0
subroutine 6 34 17.6
pod 23 23 100.0
total 47 352 13.3


line stmt bran cond sub pod time code
1             package Authen::Users;
2              
3             require 5.004;
4              
5 1     1   46529 use strict;
  1         2  
  1         42  
6 1     1   4 use warnings;
  1         2  
  1         71  
7 1     1   5 use Carp;
  1         6  
  1         218  
8 1     1   3761 use DBI;
  1         23187  
  1         85  
9 1     1   19340 use Digest::SHA qw(sha1_base64 sha256_base64 sha384_base64 sha512_base64);
  1         14117  
  1         164  
10 1     1   15 use vars qw($VERSION);
  1         2  
  1         5705  
11             $VERSION = '0.17';
12              
13             sub new {
14 0     0 1   my ( $class, %args ) = @_;
15 0           my $self = {};
16 0           bless( $self, $class );
17 0           foreach
18             my $k (qw( dbtype dbname create dbuser dbpass dbhost authen_table digest))
19             {
20 0 0         $self->{$k} = $args{$k} if $args{$k};
21             }
22 0 0         $self->{dbname}
23             or croak "Cannot set up Auth::Users without a dbname: $self->{dbname}.";
24 0 0         $self->{dbtype} = 'SQLite' unless $self->{dbtype};
25 0   0       $self->{authentication} = $self->{authen_table} || 'authentication';
26 0 0         $self->{make_salt} = 1 unless $args{NO_SALT};
27 0   0       my $algo = $self->{digest} || 1;
28 0 0         if ( $algo == 256 ) {
    0          
    0          
29 0     0     $self->{sha} = sub { sha256_base64(shift) }
30 0           }
31             elsif ( $algo == 384 ) {
32 0     0     $self->{sha} = sub { sha384_base64(shift) }
33 0           }
34             elsif ( $algo == 512 ) {
35 0     0     $self->{sha} = sub { sha512_base64(shift) }
36 0           }
37             else {
38 0     0     $self->{sha} = sub { sha1_base64(shift) }
39 0           }
40 0           $self->{_error} = ''; # internal error message used by error() func
41 0           $self->{sqlparams} = { PrintError => 0, RaiseError => 1, AutoCommit => 1 };
42 0 0         if ( $self->{dbtype} =~ /^MySQL/i ) {
43              
44             # MySQL
45 0           $self->{dsn} = "dbi:mysql:database=$self->{dbname}";
46 0 0         $self->{dsn} .= ";host=$self->{dbhost}" if $self->{dbhost};
47 0 0         $self->{dbh} = DBI->connect(
48             $self->{dsn}, $self->{dbuser},
49             $self->{dbpass}, $self->{sqlparams}
50             )
51             or croak "Can't connect to MySQL database as $self->{dsn} with "
52             . "user $self->{dbuser} and given password and $self->{sqlparams}: "
53             . DBI->errstr;
54             }
55             else {
56              
57             # SQLite is the default
58 0           $self->{dsn} = "dbi:SQLite:dbname=$self->{dbname}";
59 0 0         $self->{dbh} = DBI->connect( $self->{dsn}, $self->{sqlparams} )
60             or croak "Can't connect to SQLite database as $self->{dsn} with "
61             . "$self->{sqlparams}: "
62             . DBI->errstr;
63             }
64              
65             # check if table exists
66 0           my $sth_tab = $self->{dbh}->table_info();
67 0           my $need_table = 1;
68 0           while ( my $tbl = $sth_tab->fetchrow_hashref ) {
69 0 0         $need_table = 0 if $tbl->{TABLE_NAME} eq $self->{authentication};
70             }
71 0 0         if ($need_table) {
72 0 0         unless ( $self->{create} ) {
73 0           croak
74             "No table in database, and create not specified for new Authen::Users";
75             }
76              
77             # try to create the table
78 0           my $ok_create = $self->{dbh}->do(<
79             CREATE TABLE $self->{authentication}
80             ( groop VARCHAR(15), user VARCHAR(30), password VARCHAR(60),
81             fullname VARCHAR(40), email VARCHAR(40), question VARCHAR(120),
82             answer VARCHAR(80), created VARCHAR(12), modified VARCHAR(12),
83             pw_timestamp VARCHAR(12), salt VARCHAR(10), gukey VARCHAR (46) UNIQUE )
84             ST_H
85 0 0         carp("Could not make table") unless $ok_create;
86             }
87 0           return $self;
88             }
89              
90             sub authenticate {
91 0     0 1   my ( $self, $group, $user, $password ) = @_;
92 0           my $password_sth = $self->{dbh}->prepare(<
93             SELECT password, salt FROM $self->{authentication} WHERE groop = ? AND user = ?
94             ST_H
95 0           $password_sth->execute( $group, $user );
96 0           my $row = $password_sth->fetchrow_arrayref;
97 0 0         if ($row) {
98 0           my $stored_pw_digest = $row->[0];
99 0           my $salt = $row->[1];
100 0 0         my $user_pw_digest = ($salt)
101             ? $self->{sha}->($password, $salt)
102             : $self->{sha}->($password);
103 0 0         return 1 if $user_pw_digest eq $stored_pw_digest;
104             }
105 0           return;
106             }
107              
108             sub add_user {
109 0     0 1   my ( $self, $group, $user, $password, $fullname, $email, $question,
110             $answer ) = @_;
111 0 0         $self->validate( $group, $user, $password ) or return;
112 0 0         $self->not_in_table( $group, $user ) or return;
113 0           my $r;
114 0           my $salt = 0;
115 0 0         if($self->{make_salt}) {
116 0           $salt = $self->{sha}->( time + rand(10000) );
117 0           $salt = substr( $salt, -8 );
118 0           my $password_sha = $self->{sha}->($password, $salt);
119 0           my $insert_sth = $self->{dbh}->prepare(<
120             INSERT INTO $self->{authentication}
121             (groop, user, password, fullname, email, question, answer,
122             created, modified, pw_timestamp, salt, gukey)
123             VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
124             ST_H
125 0           my $t = time;
126 0           $r = $insert_sth->execute( $group, $user, $password_sha,
127             $fullname, $email, $question, $answer, $t, $t, $t, $salt,
128             _g_u_key( $group, $user ) );
129             }
130             else {
131 0           my $password_sha = $self->{sha}->($password);
132 0           my $insert_sth = $self->{dbh}->prepare(<
133             INSERT INTO $self->{authentication}
134             (groop, user, password, fullname, email, question, answer,
135             created, modified, pw_timestamp, gukey)
136             VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
137             ST_H
138 0           my $t = time;
139 0           $r = $insert_sth->execute( $group, $user, $password_sha,
140             $fullname, $email, $question, $answer, $t, $t, $t,
141             _g_u_key( $group, $user ) );
142             }
143 0 0 0       return 1 if $r and $r == 1;
144 0           $self->{_error} = $self->{dbh}->errstr;
145 0           return;
146             }
147              
148 0     0 1   sub user_add { shift->add_user(@_) }
149              
150             sub update_user_all {
151 0     0 1   my ( $self, $group, $user, $password, $fullname, $email, $question,
152             $answer ) = @_;
153 0 0         $self->validate( $group, $user, $password ) or return;
154 0           my $salt = 0;
155 0 0         if($self->{make_salt}) {
156 0           $salt = $self->{sha}->( time + rand(10000) );
157 0           $salt = substr( $salt, -8 );
158 0           my $password_sha = $self->{sha}->($password, $salt);
159 0           my $update_all_sth = $self->{dbh}->prepare(<
160             UPDATE $self->{authentication} SET password = ?, fullname = ?, email = ?,
161             question = ?, answer = ? , modified = ?, pw_timestamp = ?, salt = ?, gukey = ?
162             WHERE groop = ? AND user = ?
163             ST_H
164 0           my $t = time;
165 0 0         return 1
166             if $update_all_sth->execute(
167             $password_sha, $fullname, $email, $question, $answer,
168             $t, $t, $salt, _g_u_key( $group, $user ), $group, $user
169             );
170             }
171             else {
172 0           my $password_sha = $self->{sha}->{password};
173 0           my $update_all_sth = $self->{dbh}->prepare(<
174             UPDATE $self->{authentication} SET password = ?, fullname = ?, email = ?,
175             question = ?, answer = ? , modified = ?, pw_timestamp = ?, gukey = ?
176             WHERE groop = ? AND user = ?
177             ST_H
178 0           my $t = time;
179 0 0         return 1
180             if $update_all_sth->execute(
181             $password_sha, $fullname, $email, $question, $answer,
182             $t, $t, _g_u_key( $group, $user ), $group, $user
183             );
184             }
185 0           return;
186             }
187              
188             sub update_user_password {
189 0     0 1   my ( $self, $group, $user, $password ) = @_;
190 0 0         $self->validate( $group, $user, $password ) or return;
191 0           my $salt = 0;
192 0 0         if($self->{make_salt}) {
193 0           $salt = $self->{sha}->( time + rand(10000) );
194 0           $salt = substr( $salt, -8 );
195 0           my $password_sha = $self->{sha}->($password, $salt);
196 0           my $update_pw_sth = $self->{dbh}->prepare(<
197             UPDATE $self->{authentication} SET password = ?, modified = ?, pw_timestamp = ?,
198             salt = ?
199             WHERE groop = ? AND user = ?
200             ST_H
201 0           my $t = time;
202 0 0         return 1
203             if $update_pw_sth->execute( $password_sha,
204             $t, $t, $salt, $group, $user );
205             }
206             else {
207 0           my $password_sha = $self->{sha}->{password};
208 0           my $update_pw_sth = $self->{dbh}->prepare(<
209             UPDATE $self->{authentication} SET password = ?, modified = ?, pw_timestamp = ?
210             WHERE groop = ? AND user = ?
211             ST_H
212 0           my $t = time;
213 0 0         return 1
214             if $update_pw_sth->execute( $password_sha,
215             $t, $t, $group, $user );
216             }
217 0           return;
218             }
219              
220             sub update_user_fullname {
221 0     0 1   my ( $self, $group, $user, $fullname ) = @_;
222 0           my $update_fullname_sth = $self->{dbh}->prepare(<
223             UPDATE $self->{authentication} SET fullname = ?, modified = ?,
224             WHERE groop = ? AND user = ?
225             ST_H
226 0           my $t = time;
227 0 0         return 1 if $update_fullname_sth->execute( $fullname, $t, $group, $user );
228 0           return;
229             }
230              
231             sub update_user_email {
232 0     0 1   my ( $self, $group, $user, $email ) = @_;
233 0           my $update_email_sth = $self->{dbh}->prepare(<
234             UPDATE $self->{authentication} SET email = ? , modified = ?,
235             WHERE groop = ? AND user = ?
236             ST_H
237 0           my $t = time;
238 0 0         return 1 if $update_email_sth->execute( $email, $t, $group, $user );
239 0           return;
240             }
241              
242             sub update_user_question_answer {
243 0     0 1   my ( $self, $group, $user, $question, $answer ) = @_;
244 0           my $update_additional_sth = $self->{dbh}->prepare(<
245             UPDATE $self->{authentication} SET question = ?, answer = ? ,
246             modified = ? WHERE groop = ? AND user = ?
247             ST_H
248 0           my $t = time;
249 0 0         return 1
250             if $update_additional_sth->execute( $question, $answer, $t, $group,
251             $user );
252 0           return;
253             }
254              
255             sub delete_user {
256 0     0 1   my ( $self, $group, $user ) = @_;
257 0           my $delete_sth = $self->{dbh}->prepare(<
258             DELETE FROM $self->{authentication} WHERE groop = ? AND user = ?
259             ST_H
260 0 0         return 1 if $delete_sth->execute( $group, $user );
261 0           return;
262             }
263              
264             sub count_group {
265 0     0 1   my ( $self, $group ) = @_;
266 0           my $count_sth = $self->{dbh}->prepare(<
267             SELECT COUNT(password) FROM $self->{authentication} WHERE groop = ?
268             ST_H
269 0           $count_sth->execute($group);
270 0           my $nrows = $count_sth->fetchrow_arrayref->[0];
271 0 0         $nrows = 0 if $nrows < 0;
272 0           return $nrows;
273             }
274              
275             sub get_group_members {
276 0     0 1   my ( $self, $group ) = @_;
277 0           my ( $row, @members );
278 0           my $members_sth = $self->{dbh}->prepare(<
279             SELECT user FROM $self->{authentication} WHERE groop = ?
280             ST_H
281 0           $members_sth->execute($group);
282 0           while ( $row = $members_sth->fetch ) { push @members, $row->[0] }
  0            
283 0           return \@members;
284             }
285              
286             sub user_info {
287              
288             # returns an arrayref:
289             # [ groop, user, password, fullname, email,
290             # question, answer, created, modified, pw_timestamp, salt, gukey ]
291 0     0 1   my ( $self, $group, $user ) = @_;
292 0           my $user_sth = $self->{dbh}->prepare(<
293             SELECT * FROM $self->{authentication} WHERE groop = ? AND user = ?
294             ST_H
295 0           $user_sth->execute( $group, $user );
296 0           return $user_sth->fetch;
297             }
298              
299             sub user_info_hashref {
300              
301             # returns a hashref:
302             # {groop => $group, user => $user, password => $password, etc. }
303 0     0 1   my ( $self, $group, $user ) = @_;
304 0           my $user_sth = $self->{dbh}->prepare(<
305             SELECT * FROM $self->{authentication} WHERE groop = ? AND user = ?
306             ST_H
307 0           $user_sth->execute( $group, $user );
308 0           return $user_sth->fetchrow_hashref;
309             }
310              
311             sub get_user_fullname {
312 0     0 1   my ( $self, $group, $user ) = @_;
313 0           my $row = $self->user_info( $group, $user );
314 0 0         return $row->[3] if $row;
315 0           return;
316             }
317              
318             sub get_user_email {
319 0     0 1   my ( $self, $group, $user ) = @_;
320 0           my $row = $self->user_info( $group, $user );
321 0 0         return $row->[4] if $row;
322 0           return;
323             }
324              
325             sub get_user_question_answer {
326 0     0 1   my ( $self, $group, $user ) = @_;
327 0           my $row = $self->user_info( $group, $user );
328 0 0         return ( $row->[5], $row->[6] ) if $row;
329 0           return;
330             }
331              
332             sub get_password_change_time {
333 0     0 1   my ( $self, $group, $user ) = @_;
334 0           my $row = $self->user_info( $group, $user );
335 0 0         return $row->[9] if $row;
336 0           return;
337             }
338              
339             sub errstr {
340 0     0 1   my $self = shift;
341 0           return $self->{dbh}->errstr;
342             }
343              
344             sub error {
345 0     0 1   my $self = shift;
346 0   0       return $self->{_error} || $self->{dbh}->errstr;
347             }
348              
349             # validation routine for adding users, etc.
350             sub validate {
351 0     0 1   my ( $self, $group, $user, $password ) = @_;
352 0 0         unless ($group) {
353 0           $self->{_error} = "Group is not defined.";
354 0           return;
355             }
356 0 0         unless ($user) {
357 0           $self->{_error} = "Username is not defined.";
358 0           return;
359             }
360 0 0         unless ($password) {
361 0           $self->{_error} = "Password is not defined.";
362 0           return;
363             }
364 0           return 1;
365             }
366              
367             # assistance functions
368              
369             sub not_in_table {
370 0     0 1   my ( $self, $group, $user ) = @_;
371 0           my $unique_sth = $self->{dbh}->prepare(<
372             SELECT password FROM $self->{authentication} WHERE gukey = ?
373             ST_H
374 0           $unique_sth->execute( _g_u_key( $group, $user ) );
375 0           my @row = $unique_sth->fetchrow_array;
376 0 0         return if @row;
377 0           return 1;
378             }
379              
380             sub is_in_table {
381 0     0 1   my ( $self, $group, $user ) = @_;
382 0 0         return if $self->not_in_table( $group, $user );
383 0           return 1;
384             }
385              
386             #end of public interface
387             # internal use--not for object use (no $self argument)
388              
389             sub _g_u_key {
390 0     0     my ( $group, $user ) = @_;
391 0           return $group . '|' . $user;
392             }
393              
394             =head1 NAME
395              
396             Authen::Users - DBI Based User Authentication
397              
398             =head1 DESCRIPTION
399              
400             General password authentication using DBI-capable databases. Currently supports
401             MySQL and SQLite databases. The default is to use a SQLite database to store
402             and access user information.
403              
404             This module is not an authentication protocol. For that see something such as
405             Authen::AuthenDBI.
406              
407             =head1 RATIONALE
408              
409             After several web sites were written which required ongoing DBI or .htpassword
410             file tweaking for user authentication, it seemed we needed a default user
411             password database that would contain not only the user name and password but
412             also such things as the information needed to reset lost passwords.
413             Thus, this module, designed to be as much as possible a drop-in for your
414             website authorization scripting needs.
415              
416             =head1 SYNOPSIS
417              
418             use Authen::Users;
419              
420             my $authen = new Athen::Users(dbtype => 'SQLite', dbname => 'mydbname');
421              
422             // for backward compatibility use the call below:
423             my $authen = new Athen::Users(
424             dbtype => 'SQLite', dbname => 'mydbname', NO_SALT => 1 );
425              
426              
427             my $a_ok = $authen->authenticate($group, $user, $password);
428              
429             my $result = $authen->add_user(
430             $group, $user, $password, $fullname, $email, $question, $answer);
431              
432             =head1 METHODS
433              
434             =over 4
435              
436             =item B
437              
438             Create a new Authen::Users object.
439              
440             my $authen = new Authen::Users(dbname => 'Authentication');
441              
442             =over 4
443              
444             Defaults are dbname => SQLIte, authen_table => authentication,
445             create => 0 (off), digest => SHA1.
446              
447             =back
448              
449             my $authen = new Authen::Users( dbtype => 'SQLite', dbname => 'authen.db',
450             create => 1, authen_table => 'authen_table', digest => 512 );
451              
452             my $authen = new Authen::Users(
453             dbtype => 'MySQL', dbname => 'authen.db',
454             dbpass => 'myPW', authen_table => 'authen_table',
455             dbhost => 'mysql.server.org', digest => 256 );
456              
457             Takes a hash of arguments:
458              
459             =over 4
460              
461             =item B
462              
463             The type of database. Currently supports 'SQLite' and 'MySQL'.
464             Defaults to SQLite.
465              
466             =item B
467              
468             The name of the database. Required for all database types.
469              
470             =item B
471              
472             The name of the table containing the user data.
473            
474            
475             NOTE: If this is omitted, defaults to a table called 'authentication' in the
476             database. If the argument 'create' is passed with a true value, and the
477             authen_table argument is omitted, then a new empty table called 'authentication'
478             will be created in the database.
479              
480             The SQL compatible table is currently as follows:
481              
482             =over 8
483              
484             =item C
485            
486             Group of the user. This may signify authorization (trust) level, or could
487             be used to allow one authorization database to serve several applications
488             or sites.
489              
490             =item C
491            
492             User name
493              
494             =item C
495              
496             Password, as SHA digest
497              
498             =item C
499              
500             Full name of user
501              
502             =item C
503              
504             User email
505              
506             =item C
507              
508             Challenge question
509              
510             =item C
511              
512             Challenge answer
513              
514             =item C
515              
516             Row insertion timestamp
517              
518             =item C
519              
520             Row modification timestamp
521              
522             =item C
523              
524             Password modification timestamp
525              
526             =item C
527              
528             Internal use: key made of user and group--kept unique
529              
530             =back
531              
532             =over 4
533            
534             For convenience, the database has fields to store for each user an email
535             address and a question and answer for user verification if a password is lost.
536              
537             =back
538              
539             =item B
540              
541             If true in value, causes the named authen_table to be created if it was not
542             already present when the database was opened.
543              
544             =item B
545              
546             The password for the account. Not used by SQLite. Sometimes needed otherwise.
547              
548             =item B
549              
550             The name of the host for the database. Not used by SQLite.
551             Needed if the database is hosted on a remote server.
552              
553             =item B
554              
555             The algorithm used for the SHA digest. Currently supports SHA1 and SHA2.
556             B to SHA1. Supported values are 1 for SHA1, 256 for SHA-256,
557             384 for SHA-384, and 512 for SHA-512. See documentation for Digest::SHA for
558             more information. Given that in most cases SHA1 is much more random than most
559             user-typed passwords, any of the above algorithms should be more than
560             sufficient, since most attacks on passwords would likely be dictionary-based,
561             not purely brute force. In recognition that some uses of the package might use
562             long, random passwords, there is the option of up to 512-bit SHA2 digests.
563              
564             =back
565              
566             =item B
567              
568             In version 0.16 and above, a random salt is added to the digest in order
569             to partially defeat hacking passwords with pre-computed rainbow tables.
570             To use later versions of Authen::Users with older SQL tables created by
571             previous versions, you MUST specify the named parameter NO_SALT => 1
572             in your call to new.
573              
574             =item B
575              
576             =item B
577              
578             Authenticate a user. Users may have the same user name as long as they are not
579             also in the same authentication group. Therefore, the user's group should be
580             included in all calls to authenticate the user by password. Passwords are
581             stored as SHA digests, so the authentication is of the digests.
582              
583             =item B
584              
585             =item B
586              
587              
588             Add a user to the database. Synonym: B.
589              
590             The arguments are as follows:
591              
592             $authen->add_user($group, $user, $password, $fullname, $email, $question, $answer)
593             or die $authen->error;
594              
595             =over 4
596              
597             =item B
598             Scalar. The group of users. Used to classify authorizations, etc.
599             User names may be the same if the groups are different, but in any given group
600             the users must have unique names.
601              
602             =item B
603              
604             Scalar. User name.
605              
606             =item B
607              
608             Scalar. SHA digest of user's password.
609              
610             =item B
611              
612             Scalar. The user's 'real' or full name.
613              
614             =item B
615              
616             Scalar. User's email address.
617              
618             =item B
619              
620             Scalar. A question used, for example, for identifying the user if they lose their password.
621              
622             =item B
623              
624             Scalar. The correct answer to $question.
625              
626             =back
627              
628             Note: it is up to the user of the module to determine how the fields after group, user, and
629             password fields are used, or if they are used at all.
630              
631             =item B
632              
633             Update all fields for a given group and user:
634              
635             $authen->update_user_all($group, $user, $password, $fullname, $email, $question, $answer) or die "Could not update $user: " . $authen->errstr();
636              
637             =item B
638              
639             $authen->update_user_password($group, $user, $password)
640             or die "Cannot update password for group $group and user $user: $authen->errstr";
641              
642             Update the password.
643              
644             =item B
645              
646             $authen->update_user_fullname($group, $user, $fullname)
647             or die "Cannot update fullname for group $group and user $user: $authen->errstr";
648              
649             Update the full name.
650              
651             =item B
652              
653             $authen->update_user_email($group, $user, $email)
654             or die "Cannot update email for group $group and user $user: $authen->errstr";
655              
656             Update the email address.
657              
658             =item B
659              
660             $authen->update_user_question_answer($group, $user, $question, $answer) or die "Cannot update question and answer for group $group and user $user: $authen->errstr";
661              
662             Update the challenge question and its answer.
663              
664             =item B
665              
666             $authen->delete_user($group, $user) or die "Cannot delete user in group $group with username $user: $authen->errstr";
667              
668             Delete the user entry.
669              
670             =item B
671              
672             $authen->count_group($group) or die "Cannot count group $group: $authen->errstr";
673              
674             Return the number of entries in group $group.
675              
676             =item B
677              
678             $authen->get_group_members($group) or die "Cannot retrieve list of group $group: $authen->errstr";
679              
680             Return a reference to a list of the user members of group $group.
681              
682             =item B
683              
684             $authen->user_info($group, $user) or die "Cannot retrieve information about $user in group $group: $authen->errstr";
685              
686             Return a reference to a list of the information about $user in $group.
687              
688             =item B
689              
690             my $href = $authen->user_info_hashref($group, $user) or die "Cannot retrieve information about $user in group $group: $authen->errstr";
691             print "The email for $user in $group is $href->{email}";
692              
693             Return a reference to a hash of the information about $user in $group, with the field
694             names as keys of the hash.
695              
696             =item B
697              
698             $authen->get_user_fullname($group, $user) or die "Cannot retrieve full name of $user in group $group: $authen->errstr";
699              
700             Return the user full name entry.
701              
702             =item B
703              
704             $authen->get_user_email($group, $user) or die "Cannot retrieve email of $user in group $group: $authen->errstr";
705              
706             Return the user email entry.
707              
708             =item B
709              
710             $authen->get_user_question_answer($group, $user) or die "Cannot retrieve question and answer for $user in group $group: $authen->errstr";
711              
712             Return the user question and answer entries.
713              
714             =item B
715              
716             $authen->get_password_change_time($group, $user)
717             or die "Cannot retrieve password timestamp for $user in group $group: $authen->errstr";
718              
719             There is a timestamp associated with changes in passwords. This may be used to
720             expire passwords that need to be periodically changed. The logic used to do
721             password expiration, if any, is up to the code using the module.
722              
723             =item B
724              
725             print $auth->errstr();
726              
727             Returns the last database error, if any.
728              
729             =item B
730              
731             print $auth->error;
732              
733             Returns the last class internal error message, if any; if none, returns the
734             last database DBI error, if any.
735              
736             =item B
737              
738             $auth->not_in_table($group, $user);
739              
740             True if $user in group $group is NOT already an entry.
741             Useful to rule out an existing user name when adding a user.
742              
743             =item B
744              
745             $auth->is_in_table($group, $user);
746              
747             True if $user in group $group is already in the database.
748              
749             =item B
750              
751             $auth->validate($group, $user, $password);
752              
753             True if the item is a valid entry; internal use
754              
755             =back
756              
757             =head1 BUGS
758              
759             On installation, "make test" may fail if Perl support for MySql or SQLite is
760             installed, but the database itself is not running or is otherwise not available
761             for use by the installing user. MySql by default has a 'test' database which is
762             required under "make test." "Forcing" installation may work around this.
763              
764             =head1 AUTHOR
765              
766             William Herrera (wherrera@skylightview.com)
767              
768             =head1 SUPPORT
769              
770             Questions, feature requests and bug reports should go to wherrera@skylightview.com
771              
772             =head1 COPYRIGHT
773              
774             Copyright (C) 2004, 2008 William Hererra. All Rights Reserved.
775              
776             This module is free software; you can redistribute it and/or modify it
777             under the same terms as Perl itself.
778              
779             =cut
780              
781             1;