File Coverage

blib/lib/CAS/User.pm
Criterion Covered Total %
statement 32 561 5.7
branch 1 316 0.3
condition 1 106 0.9
subroutine 10 31 32.2
pod 6 20 30.0
total 50 1034 4.8


line stmt bran cond sub pod time code
1             package CAS::User;
2              
3             =head1 NAME
4              
5             CAS::User - Creates user objects for accessing and modifying user data.
6              
7             =head1 SYNOPSIS
8              
9             use CAS::User;
10              
11             my $user = CAS::User->new(%userinfo);
12             die "Couldn't create new user" if $user == ERROR;
13            
14             my $user = CAS::User->load({ID => 1234567654});
15             die "Couldn't load user." if $user == ERROR;
16            
17             Or even better error reporting where appropriate:
18              
19             if (! ref $user && $user == ERROR) {
20             my @errors = warning_notes();
21             die "Failed to load user:\n\t" . join("\n\t",@errors) . "\n";
22             } # if error
23              
24             =head1 ABSTRACT
25              
26             Generate user objects for either new or existing users. The object returned
27             is used to manage that users data, such as Password, Username, address, etc.
28              
29             =head1 DESCRIPTION
30              
31             Generate user objects for either new or existing users. The object returned
32             is used to manage that users data, such as Password, Username, address, etc.
33              
34             Currently only the CAS core Users and UserInfo tables are handled. Some
35             handling of client user tables will be added once this is core part is
36             functional. Set, get and validate methods are provided for the core tables, for
37             the client tables only set and get are provided - it is the clients
38             responsibility to validate their specific user information.
39              
40             =head2 EXPORT
41              
42             None by default.
43              
44             =cut
45              
46 5     5   22810 use 5.008;
  5         20  
  5         199  
47 5     5   32 use strict;
  5         12  
  5         294  
48 5     5   565 use CAS;
  5         11  
  5         164  
49 5     5   30 use CAS::Config;
  5         10  
  5         111  
50 5     5   29 use Scalar::Util qw(blessed);
  5         17  
  5         293  
51              
52             # otherwise constants don't get exported
53             #use base qw(CAS::Messaging);
54 5     5   28 use CAS::Messaging;
  5         8  
  5         1117  
55             our @ISA = qw(CAS::Messaging);
56              
57 5     5   96 use Carp qw(cluck confess croak carp);
  5         11  
  5         306  
58 5     5   5995 use Mail::Sendmail;
  5         93962  
  5         714  
59             our $AUTOLOAD = '';
60              
61 5     5   46 use Data::Dumper;
  5         14  
  5         47531  
62              
63             our $VERSION = '0.60_5';
64              
65             # Config fields that subclasses of core should be able to get and set
66             # Bitmasked with get permission = 1, set = 2, both = 3
67             # all fields in the supplimental users table are set to 3 without
68             # internal validation
69             my %fields = (
70             ID => 1,
71             Username => 1,
72             Password => 1,
73             Firstname => 3,
74             Lastname => 3,
75             Email => 3,
76             Phone => 3,
77             Address1 => 3,
78             Address2 => 3,
79             City => 3,
80             State => 3,
81             Country => 3,
82             Zip => 3,
83             Disabled => 3,
84             dbh => 1,
85             user_info_fields => 1,
86             supl_user_info_fields => 1,
87             admin_email => 1,
88             debug => 3,
89             );
90              
91             =head2 new
92              
93             Creates user object for a user not yet registered in the CAS system. Invoking
94             this contructer will generate an object to use for validating new user
95             information and entering a user in the database. When invoked it requires a
96             Username and Password for the user, which will be validated. If those pass
97             validation the user is registered in the database and the object is returned.
98              
99             This object can now be used to validate additional user data and add it to the
100             users record. It is highly recommended that you require the users First and Last
101             names and any contact information you want be provided with the Username,
102             Password, etc. and that you record all those (that validate) immediately after
103             getting the user object back.
104              
105             Please note
106              
107             PARAMETERS:
108              
109             Username: The Username the user will use for logging into the system. Usernames
110             are therefor unique in the database.
111              
112             Password: The Password the user will use when logging in. It is highly
113             recommended you verify the Password before set it by having a user enter it
114             twice and compare the two entries.
115              
116             Email: An Email address for the user. This Email address will be used by the
117             system to send Emails to the user for important system notifications, such as
118             registration notification, systemwide administrative messages, etc. Since Email
119             addresses are required to be unique within the system, this also discourages
120             users from registering multiple times.
121              
122             CLIENT: The client the user is registering from.
123              
124             OPTIONS:
125              
126             GROUP: The default initial group for the user. If not provided the default
127             group for the client will be used, or, if that is not defined, the general
128             default group as set in the CAS config file will be used.
129              
130             =cut
131             sub new {
132 1     1 1 36 my $proto = shift;
133 1   33     11 my $class = ref($proto) || $proto;
134 1         3 my $HR_params = shift;
135 1 50       7 croak("Parameters not passed as a hashref")
136             unless ref($HR_params) eq 'HASH';
137            
138 1         13 my $config = CAS::Config->load($HR_params);
139 0           $config->{_permitted} = \%fields;
140 0           my $self = bless ($config,$class);
141 0           $self->_clear_result;
142 0           my $dbh = $self->{dbh}; # can't autoload yet - no ID in self
143            
144             # Determine debug level and activate warnings and diagnostics if
145             # appropriate
146 0   0       my $debug = $HR_params->{debug} || $self->{debug} || 0;
147 0 0         $^W++ if $debug;
148 0 0 0       (require diagnostics && import diagnostics) if $debug > 2;
149 0           $self->{debug} = $debug;
150            
151            
152 0 0         error("No database connection!") unless $dbh->ping;
153            
154 0           my $valid_Username = $self->validate_Username($HR_params);
155 0           my $valid_Password = $self->validate_Password($HR_params);
156 0           my $valid_Email = $self->validate_Email($HR_params);
157            
158 0 0 0       unless (defined $valid_Username && defined $valid_Password
      0        
159             && defined $valid_Email) {
160 0           $self->error("Some required parameters missing or invalid: "
161             . $self->messages);
162             } # Username or Password were invalid format
163            
164            
165             # check to see if Username is already used
166 0           my $Quser = $dbh->quote($HR_params->{Username});
167 0           my $already_used = $dbh->selectrow_array("SELECT User FROM
168             Users WHERE Username = $Quser");
169 0 0         $self->error('Problem checking if Username already used: '
170             . $dbh->errstr) if $dbh->err;
171 0 0         $self->error("Username $Quser is already used.") if ($already_used);
172            
173             # check if email already used
174 0           my $QEmail = $dbh->quote($HR_params->{Email});
175 0           my $email_used = $dbh->selectrow_array("SELECT ID
176             FROM UserInfo WHERE Email = $QEmail");
177 0 0         $self->error('Problem checking if Email already used: '
178             . $dbh->errstr) if $dbh->err;
179 0 0         $self->error("Email $QEmail is already registered.") if ($email_used);
180            
181             # add user to database and set user ID in object
182 0           my $set_vals = '';
183 0           foreach my $field (keys %{$self->user_info_fields}) {
  0            
184 0   0       my $value = $HR_params->{$field} || undef;
185 0 0         if ($value) {
186 0           my $validation_method = "validate_$field";
187 0 0         if ($self->can($validation_method)) {
188 0           $value = $self->$validation_method($value);
189             }
190 0 0         unless (defined $value) {
191 0           $self->_set_result(CONTINUE, "Value for optional field $field "
192             . 'invalid or undefined, skipped.');
193 0           next;
194             } # don't set invalid fields
195            
196 0           $self->{$field} = $value;
197 0           my $Qval = $dbh->quote($value);
198 0           $set_vals .= ", $field = $Qval";
199             } # if value for field provided
200             } # for each possible field
201 0           $dbh->do("INSERT INTO UserInfo SET regdate = CURRENT_DATE
202             $set_vals");
203 0 0         $self->error('Problem registering user with [$set_vals]: '
204             . $dbh->errstr) if $dbh->err;
205            
206 0   0       my $id = $dbh->selectrow_array("SELECT LAST_INSERT_ID()")
207             || $HR_params->{ID};
208 0 0         $self->error('No ID returned by database?!') unless $id;
209 0           $self->{ID} = $id;
210 0           $self->{Username} = $HR_params->{Username};
211            
212            
213             # add any other user data provided for suplimental table
214 0   0       my $supl_tbl = $self->{client}{Supplemental_User_Table} || undef;
215 0 0         if ($supl_tbl) {
216 0           $set_vals = "User = $self->{ID}";
217 0           foreach my $field (keys %{$self->supl_user_info_fields}) {
  0            
218 0   0       my $value = $HR_params->{$field} || undef;
219 0 0         if ($value) {
220 0           my $validation_method = "validate_$field";
221 0 0         if ($self->can($validation_method)) {
222 0           $value = $self->$validation_method($value);
223             }
224 0 0         unless (defined $value) {
225 0           $self->_set_result(CONTINUE, 'Value for optional field '
226             . "$field invalid or undefined, skipped.");
227 0           next;
228             } # don't set invalid fields
229            
230 0           $self->{$field} = $value;
231 0           my $Qval = $dbh->quote($value);
232 0           $set_vals .= ", $field = $Qval";
233             } # if value for field provided
234             } # for each possible field
235 0           $dbh->do("INSERT INTO UserInfo SET User = $self->{ID}, $set_vals");
236 0 0         $self->error('Problem entering users Email and generating User ID: '
237             . $dbh->errstr) if $dbh->err;
238             } # if registering client has suplimental table for users
239            
240             # my $krb5 = '';
241             # if ($HR_params->{KRB5}) {
242             # $krb5 = ", KRB5 = " . $dbh->quote($HR_params->{KRB5});
243             # } # set KRB5 value if supplied
244             # $dbh->do("INSERT INTO Users SET User = $id,
245             # Username = $QUsername, Password = $Qpass $krb5");
246             # error('Problem registering user in the Users table: '
247             # . $dbh->errstr) if $dbh->err;
248            
249            
250             # add to users table
251 0           my $QUsername = $dbh->quote($HR_params->{Username});
252 0           my $cryptpass = $self->crypt_pass($HR_params->{Password});
253 0           my $Qpass = $dbh->quote($cryptpass);
254 0           $dbh->do("INSERT INTO Users (User, Username, Password)
255             VALUES ($self->{ID},$QUsername,$Qpass)");
256 0 0         $self->error('Problem adding user to default group: '
257             . $dbh->errstr) if $dbh->err;
258            
259            
260             # add to default group
261 0   0       my $group = $HR_params->{GROUP} || $self->{client}{Default_Group}
262             || $self->{DEFAULT_GROUP} || undef;
263 0 0         $self->error('Could not determine initial group for user')
264             unless defined $group;
265 0           $dbh->do("INSERT INTO Groups (User,GroupID) VALUES ($self->{ID},$group)");
266 0 0         $self->error('Problem adding user to default group: '
267             . $dbh->errstr) if $dbh->err;
268            
269             # OK, it looks like the user was added to DB without any real prpblems.
270             # Now lets load the real user object
271 0           my $user = $self->load({ID => $self->{ID},
272             CLIENT_ID => $self->{client}{ID}, CONFIG => $HR_params->{CONFIG}});
273 0 0         $self->error('Was not able to create user object for new user, '
274             . "ID = $self->{ID}") unless ref($user) eq $class;
275            
276             # 'inherit' selfs messages
277 0           unshift(@{$user->{messages}},$self->messages);
  0            
278            
279             # FIX ME!!!
280             # and email notification
281             # $user->new_user_email();
282            
283 0           $self->_set_result(CREATED,"User object created and returned.");
284 0           return $user;
285             } # new
286              
287              
288             =head2 load
289              
290             Returns a user object which can be used to access and update user data. Will
291             emit errors if fields that are expected not to be null (such as First Name)
292             are.
293              
294             PARAMETERS:
295              
296             ID: The ID of the user.
297              
298             or
299              
300             Username: The users unique Username.
301              
302             =cut
303             sub load {
304 0     0 1   my $proto = shift;
305 0   0       my $class = ref($proto) || $proto;
306            
307 0           my $HR_params = shift;
308 0 0         die("Parameters not passed as a hashref")
309             unless ref($HR_params) eq 'HASH';
310 0           my $config = CAS::Config->load($HR_params);
311 0           $config->{_permitted} = \%fields;
312 0           my $self = bless ($config,$class);
313 0           $self->_clear_result;
314 0           my $dbh = $self->{dbh}; # can't autoload yet - no ID in self
315            
316 0   0       my $debug = $HR_params->{'DEBUG'} || $self->{debug} || 0;
317 0 0         $^W++ if $debug;
318 0 0 0       (require diagnostics && import diagnostics) if $debug > 2;
319 0           $self->{debug} = $debug;
320            
321 0 0 0       unless ($HR_params->{ID} || $HR_params->{Username}) {
    0          
322 0           $self->error("Either the user ID or Username are required.");
323             } # unless unique identifier provided
324            
325             # get ID if Username provided
326             elsif ($HR_params->{Username}) {
327 0           my $Quser = $dbh->quote($HR_params->{Username});
328 0           $HR_params->{ID} = $dbh->selectrow_array("SELECT User FROM
329             Users WHERE Username = $Quser");
330 0 0         $self->error('Problem getting user id: ' . $dbh->errstr)
331             if $dbh->err;
332            
333 0 0         $self->error("Username $Quser not found in database.")
334             unless $HR_params->{ID};
335             } # if usename
336            
337 0   0       my $supl_tbl = $self->{client}{Supplemental_User_Table} || undef;
338 0 0         if (defined $supl_tbl) {
339 0           foreach my $sup_field (keys %{$self->supl_user_info_fields}) {
  0            
340 0           $self->{_permitted}{$sup_field} = 3;
341             }
342             }
343 0           $self->{ID} = $HR_params->{ID};
344 0           my $rc = $self->_fetch_user_data();
345 0 0         $self->error('Problem loading user data: ' . $self->messages)
346             unless defined $rc;
347            
348            
349 0           $self->_set_result(CREATED,"User loaded from DB and object returned.");
350 0           return $self;
351             } # load
352              
353              
354              
355             # populate user object with user data - used to (re)load user data from db
356             sub _fetch_user_data {
357 0     0     my $self = shift;
358            
359 0           my $dbh = $self->dbh;
360            
361 0           my $getdat = join(", ",keys %{$self->user_info_fields});
  0            
362 0           my $HR_userinfo = $dbh->selectrow_hashref("SELECT $getdat
363             FROM UserInfo WHERE ID = $self->{ID}");
364 0 0         $self->error("Problem getting user info: " . $dbh->errstr)
365             if $dbh->err;
366            
367             # email is required, so we'll make an assumption here
368 0 0         unless ($HR_userinfo->{Email}) {
369 0           $self->_set_result(ERROR,"No user info found for $self->{ID}?");
370 0           return undef;
371             }
372            
373 0           map { $self->{$_} = $HR_userinfo->{$_} } keys %{$HR_userinfo};
  0            
  0            
374            
375 0   0       my $table = $self->{client}{Supplemental_User_Table} || undef;
376 0 0         if (defined $table) {
377 0           $getdat = join(", ",keys %{$self->supl_user_info_fields});
  0            
378 0           $HR_userinfo = $dbh->selectrow_hashref("SELECT $getdat
379             FROM $table WHERE User = $self->{ID}");
380 0 0         $self->error("Problem getting user info: " . $dbh->errstr)
381             if $dbh->err;
382            
383 0           map { $self->{$_} = $HR_userinfo->{$_} } keys %{$HR_userinfo};
  0            
  0            
384             } # if there is a suplimental user info table
385            
386 0           $self->_set_result(OK,"Fetched user data");
387 0           return OK
388             } # fetch_user_data
389              
390              
391             =head2 save
392              
393             Saves the current state of the user.
394              
395             !! Currently does not handle client table data !!
396              
397             =cut
398             sub save {
399 0     0 1   my $self = shift;
400 0 0         $self->_clear_result unless __PACKAGE__ eq caller;
401 0           my $dbh = $self->dbh;
402            
403             # cheerfully return if there's nothing to save
404             # yeah - this prevents people from setting values
405             # by hand and avoiding the validation check in set! >:-}
406             # hmm ... unless they figure out to set {changed}
407             # maybe that should be a separate private variable?
408 0 0         unless (scalar(keys %{$self->{changed}})) {
  0            
409 0           $self->_set_result(OK,"Nothing to save - all done");
410 0           return OK ;
411             }
412            
413 0           my @updates = ();
414             # first update UserInfo fields
415 0           foreach my $field (keys %{$self->user_info_fields}) {
  0            
416 0 0         next unless $self->{changed}{$field};
417            
418 0           my $Qval = $dbh->quote($self->{$field});
419 0           push(@updates,"$field = $Qval");
420             } # for each possible field
421            
422 0 0         if (@updates) {
423 0           my $updates = join(', ', @updates);
424 0           $dbh->do("UPDATE UserInfo SET $updates WHERE ID = $self->{ID} LIMIT 1");
425 0 0         $self->error('Problem updateing user info ($updates): '
426             . $dbh->errstr) if $dbh->err;
427             } # if there are updates besides password
428            
429 0   0       my $table = $self->{client}{Supplemental_User_Table} || undef;
430 0 0         if (defined $table) {
431 0           @updates = ();
432             # first update UserInfo fields
433 0           foreach my $field (keys %{$self->supl_user_info_fields}) {
  0            
434 0 0         next unless $self->{changed}{$field};
435            
436 0           my $Qval = $dbh->quote($self->{$field});
437 0           push(@updates,"$field = $Qval");
438             } # for each possible field
439            
440 0 0         if (@updates) {
441 0           my $updates = join(', ', @updates);
442 0           $dbh->do("UPDATE $table SET $updates
443             WHERE User = $self->{ID} LIMIT 1");
444 0 0         $self->error('Problem updateing user info ($updates): '
445             . $dbh->errstr) if $dbh->err;
446             } # if there are updates besides password
447             } # if there is a suplimental user info table
448            
449 0 0         unless ($self->{changed}{Password}) {
450 0           $self->_set_result(OK,"All changes saved.");
451 0           return OK ;
452             }
453            
454             # handle password separately
455 0           my $Qpass = $dbh->quote($self->{Password});
456 0           $dbh->do("UPDATE Users SET Password = $Qpass
457             WHERE User = $self->{ID} LIMIT 1");
458 0 0         $self->error('Problem updating password: ' . $dbh->errstr)
459             if $dbh->err;
460            
461 0           $self->_set_result(OK,"All changes saved.");
462 0           return OK;
463             } # save
464              
465              
466              
467             =head2 disable
468              
469             Mark a user as diabled. Authentication will be denied.
470              
471             =cut
472             sub disable {
473 0     0 1   my $self = shift;
474 0 0         $self->_clear_result unless __PACKAGE__ eq caller;
475 0           my $dbh = $self->dbh;
476            
477 0           $dbh->do("UPDATE Users SET Disabled = 'Yes'
478             WHERE User = $self->{ID} LIMIT 1");
479 0 0         $self->error("Problem disabling user: " . $dbh->errstr)
480             if $dbh->err;
481            
482 0           $self->_set_result(OK,"User disabled");
483 0           return OK;
484             } # disable
485              
486              
487             =head2 enable
488              
489             Reset disabled flag to 'No'.
490              
491             =cut
492             sub enable {
493 0     0 1   my $self = shift;
494 0 0         $self->_clear_result unless __PACKAGE__ eq caller;
495 0           my $dbh = $self->dbh;
496            
497 0           $dbh->do("UPDATE Users SET Disabled = 'No'
498             WHERE User = $self->{ID} LIMIT 1");
499 0 0         $self->error("Problem enabling user: " . $dbh->errstr)
500             if $dbh->err;
501            
502 0           $self->_set_result(OK,"User enabled");
503 0           return OK;
504             } # enable
505              
506              
507              
508             =head2 Accessor, Mutator and Validation methods
509              
510             Methods in this catagory are provided to access and alter the user data. The
511             following list describes the user attributes which these methods all work on.
512             Altering the values of user attributes with the set_ methods does B
513             change them in the database. Call $user->save; to make any changes permanant.
514             Further, calling a set_ method automatically invokes the validate_ method,
515             so there is no need to validate before setting, unless you want to just catch
516             and handle errors yourself (to regenerate a form with failed fields highlightes
517             for instance).
518              
519             =over 4
520              
521             =item Username [A-Za-z0-9_'-.@]{5,50}
522              
523             A textual key uniquely indicating one user. This value is supplied by the user
524             when they register and will function as the name with which they log in to the
525             system. This is usually a short login handle, such as the common first initial
526             last name combination (squinlan), however certain sites may wish to require
527             users to usa they're email address as a username. While the system allows
528             the use of an email address as a username, it is up to the client to modify the
529             user registration interface appropriately.
530              
531             Once registered this field may I be altered via set_Username.
532              
533             =item Password [^;\s|><]{6,16}
534              
535             A text string containing almost any plain ASCII non-whitespace text characters.
536             The system
537             can optionally require however that the password contain at least one upper
538             case, one lower case, one number and one non-alphanumeric character by setting
539             the optional STRICT parameter to true.
540              
541             Please note that the plain password string is I stored in the database.
542             Passwords are encrypted before they are stored in the databas.
543              
544             =item Firstname [\w-' ]{2,20}
545              
546             The users first name.
547              
548             =item Lastname [\w-' ]{2,30}
549              
550             The users last name.
551              
552             =item Email [\w-.@]{6,50}
553              
554             A valid email address for the user. The validation measures only examine the
555             email to see if it looks valid. However when a new user registers an email is
556             sent to the address provided with the from and reply-to fields set to the
557             client administrators email address, so they should recieve bounce
558             notifications.
559              
560             =item Phone [\d-. )(]{3,20}
561              
562             A contact phone number for the user.
563              
564             =item Address1 [\w-.# ]{6,100}
565              
566             The first address line to be used if a physical letter or package is to be sent
567             to the user.
568              
569             =item Address2 [\w-.# ]{6,100}
570              
571             The second address line to be used if a physical letter or package is to be
572             sent to the user.
573              
574             =item City [\w-. ]{2,30}
575              
576             The city for the users mailing address.
577              
578             =item State [\w-.]{2,20}
579              
580             The state for the users mailing address.
581              
582             =item Country [\w-. ]{2,30}
583              
584             The country for the users mailing address.
585              
586             =item Zip [0-9-]{5,10}
587              
588             The users zip code.
589              
590             =back
591              
592             =head2 validate_
593              
594             These methods make sure that the suplied information meets system requirements
595             most of which are not enforced by the database. Such as forbidding certain
596             characters or requiring a minimum length. If the specific data is determined
597             to be 'invalid' then the FORBIDDEN staus code is returned.
598              
599             All the set_ methods call validation before setting, so there is generally no
600             need to call the validation yourself unless you are setting multiple fields at
601             the same time and want them all handled in an all-or-nothing manner so want to
602             pre-validate them.
603              
604             =cut
605              
606             # all of this validation is currently really just some sanity checks on input
607             # to see if something basically appropriate was provided. I expect to convert
608             # DB module to a Class::DBI type at some point, and may place some of this
609             # validation checks as constraints.
610              
611              
612             sub validate_Username {
613 0     0 0   my $self = shift;
614 0           my $value = shift;
615 0 0         $self->_clear_result unless __PACKAGE__ eq caller;
616            
617 0 0         if (ref($value) eq 'HASH') {
618 0   0       $value = $value->{Username} || undef;
619             }
620            
621 0 0         unless (defined $value) {
622 0           $self->_set_result(BAD_REQUEST, "No Username provided for validation.");
623 0           return undef;
624             } # if value not provided
625            
626 0           my $errors = 0;
627            
628 0 0         if (length($value) < 3) {
    0          
629 0           $self->_set_result(CONTINUE,
630             "Username ($value) was missing or too short.");
631 0           $errors++;
632             } # Username too short
633            
634             elsif (length($value) > 50) {
635 0           $self->_set_result(CONTINUE, "Username $value) was too long.");
636 0           $errors++;
637             } # Username too long
638            
639             # we allow [@.-] to allow Emails to be used as Usernames
640 0           (my @bad_characters) = $value =~ /([^\w\'-.@]+)/g;
641 0 0         if (@bad_characters) {
642 0           $self->_set_result(CONTINUE,
643             "Username contains illegal characters (@bad_characters)");
644 0           $errors++;
645             } # check for invalid characters
646            
647            
648 0 0         if ($errors) {
649 0           $self->_set_result(NOT_MODIFIED,
650             "Username does not appear to be valid, unchanged.");
651 0           return undef;
652             } # if errors
653 0           $self->_set_result(OK,"Username is valid");
654 0           return $value;
655             } # validate_Username
656              
657              
658             sub validate_Password {
659 0     0 0   my $self = shift;
660 0           my $value = shift;
661 0 0         $self->_clear_result unless __PACKAGE__ eq caller;
662            
663 0           my $strict = 0;
664 0 0         if (ref($value) eq 'HASH') {
665 0   0       $strict = $value->{STRICT} || 0;
666 0   0       $value = $value->{Password} || undef;
667             }
668            
669 0 0         unless (defined $value) {
670 0           $self->_set_result(BAD_REQUEST, "No Password provided for validation.");
671 0           return undef;
672             } # if value not provided
673            
674            
675 0 0         $self->gripe("Password = $value") if $self->debug > 1;
676            
677 0           my $errors = 0;
678 0 0         if (length($value) < 6) {
    0          
679 0           $self->_set_result(CONTINUE, "Password was missing or too short.");
680 0           $errors++;
681             } # Password too short
682            
683             elsif (length($value) > 16) {
684 0           $self->_set_result(CONTINUE, "Password was too long.");
685 0           $errors++;
686             } # Password too long
687            
688 0           (my @bad_characters) = $value =~ /([;\s\|><]+)/g;
689 0 0         if (@bad_characters) {
690 0           $self->_set_result(CONTINUE,
691             "Password contains illegal characters (@bad_characters)");
692 0           $errors++;
693             } # check for invalid characters
694            
695            
696 0 0         if ($strict) {
697 0 0 0       unless ( $value =~ /\d/
      0        
      0        
698             && $value =~ /[A-Z]/
699             && $value =~ /[a-z]/
700             && $value =~ /[^\w]/) {
701 0           $self->_set_result(CONTINUE,
702             "Password ($value) does not pass strict criteria.");
703 0           $errors++;
704             } # unless requirements met
705             } # if 'strict' passwords required make sure a range of character types used
706            
707 0 0         if ($errors) {
708 0           $self->_set_result(NOT_MODIFIED,
709             "Password does not appear to be valid, unchanged.");
710 0           return undef;
711             } # if errors
712 0           $self->_set_result(OK,"validate_Password returned OK");
713 0           return $value;
714             } # validate_Password
715              
716              
717             sub validate_Firstname {
718 0     0 0   my $self = shift;
719 0           my $value = shift;
720 0 0         $self->_clear_result unless __PACKAGE__ eq caller;
721            
722 0 0         if (ref($value) eq 'HASH') {
723 0   0       $value = $value->{Firstname} || undef;
724             }
725            
726 0 0         unless (defined $value) {
727 0           $self->_set_result(BAD_REQUEST,
728             "No Firstname provided for validation.");
729 0           return undef;
730             } # if value not provided
731            
732 0           my $errors = 0;
733            
734            
735 0 0 0       if (length($value) < 2 || $value !~ /\w+/) {
    0          
736 0           $self->_set_result(CONTINUE,
737             'First Name appears to be missing or too short.');
738 0           $errors++;
739             } # field missing or too short
740             elsif (length($value) > 20) {
741 0           $self->_set_result(CONTINUE, 'First Name appears to be too long.');
742 0           $errors++;
743             } # field too long
744            
745 0           (my @bad_characters) = $value =~ /([^\w\-\' ]+)/g;
746 0 0         if (@bad_characters) {
747 0           $self->_set_result(CONTINUE,
748             "First Name contains invalid characters (@bad_characters).");
749 0           $errors++;
750             } # unless minimally valid
751            
752            
753 0 0         if ($errors) {
754 0           $self->_set_result(NOT_MODIFIED,
755             "First Name does not appear to be valid, unchanged.");
756 0           return undef;
757             } # if errors
758 0           $self->_set_result(OK,"validate_Firstname returned OK");
759 0           return $value;
760             } # validate_Firstname
761              
762              
763             sub validate_Lastname {
764 0     0 0   my $self = shift;
765 0           my $value = shift;
766 0 0         $self->_clear_result unless __PACKAGE__ eq caller;
767            
768 0 0         if (ref($value) eq 'HASH') {
769 0   0       $value = $value->{Lastname} || undef;
770             }
771            
772 0 0         unless (defined $value) {
773 0           $self->_set_result(BAD_REQUEST,
774             "No Lastname provided for validation.");
775 0           return undef;
776             } # if value not provided
777            
778 0           my $errors = 0;
779            
780            
781 0 0 0       if (length($value) < 2 || $value !~ /\w+/) {
    0          
782 0           $self->_set_result(CONTINUE,
783             'Last Name appears to be missing or too short.');
784 0           $errors++;
785             } # field missing or too short
786             elsif (length($value) > 30) {
787 0           $self->_set_result(CONTINUE, 'Last Name appears to be too long.');
788 0           $errors++;
789             } # field too long
790            
791 0           (my @bad_characters) = $value =~ /([^\w\-\' ]+)/g;
792 0 0         if (@bad_characters) {
793 0           $self->_set_result(CONTINUE,
794             "Last Name contains invalid characters (@bad_characters).");
795 0           $errors++;
796             } # unless minimally valid
797            
798            
799 0 0         if ($errors) {
800 0           $self->_set_result(NOT_MODIFIED,
801             "Last Name does not appear to be valid, unchanged.");
802 0           return undef;
803             } # if errors
804 0           $self->_set_result(OK,"validate_Lastname returned OK");
805 0           return $value;
806             } # validate_Lastname
807              
808              
809             sub validate_Phone {
810 0     0 0   my $self = shift;
811 0           my $value = shift;
812 0 0         $self->_clear_result unless __PACKAGE__ eq caller;
813            
814 0 0         if (ref($value) eq 'HASH') {
815 0   0       $value = $value->{Phone} || undef;
816             }
817            
818 0 0         unless (defined $value) {
819 0           $self->_set_result(BAD_REQUEST,
820             "No Phone provided for validation.");
821 0           return undef;
822             } # if value not provided
823            
824 0           my $errors = 0;
825            
826            
827 0 0 0       if (length($value) < 3 || $value !~ /\d+/) {
    0          
828 0           $self->_set_result(CONTINUE,
829             'Phone Number appears to be missing or too short.');
830 0           $errors++;
831             } # field missing or too short
832             elsif (length($value) > 20) {
833 0           $self->_set_result(CONTINUE, 'Phone Number appears to be too long.');
834 0           $errors++;
835             } # field too long
836            
837 0           (my @bad_characters) = $value =~ /([^\d\-. )(]+)/g;
838 0 0         if (@bad_characters) {
839 0           $self->_set_result(CONTINUE,
840             "Phone # contains invalid characters (@bad_characters).");
841 0           $errors++;
842             } # unless phone # minimally valid
843            
844 0 0         if ($errors) {
845 0           $self->_set_result(NOT_MODIFIED,
846             "Phone does not appear to be valid, unchanged.");
847 0           return undef;
848             } # if errors
849 0           $self->_set_result(OK,"validate_Phone returned OK");
850 0           return $value;
851             } # validate_Phone
852              
853              
854             sub validate_Email {
855 0     0 0   my $self = shift;
856 0           my $value = shift;
857 0 0         $self->_clear_result unless __PACKAGE__ eq caller;
858            
859 0 0         if (ref($value) eq 'HASH') {
860 0   0       $value = $value->{Email} || undef;
861             }
862            
863 0 0         unless (defined $value) {
864 0           $self->_set_result(BAD_REQUEST,
865             "No Email provided for validation.");
866 0           return undef;
867             } # if value not provided
868            
869 0           my $errors = 0;
870 0 0 0       if (length($value) < 6 || $value !~ /\w{2}/) {
    0          
871 0           $self->_set_result(CONTINUE,
872             'Email Address appears to be missing or too short.');
873 0           $errors++;
874             } # field missing or too short
875             elsif (length($value) > 50) {
876 0           $self->_set_result(CONTINUE, 'Email Address appears to be too long.');
877 0           $errors++;
878             } # field too long
879            
880 0           (my @bad_characters) = $value =~ /([^\w\-.\@]+)/g;
881 0 0         if (@bad_characters) {
882 0           $self->_set_result(CONTINUE,
883             "Email '$value' contains invalid characters (@bad_characters).");
884 0           $errors++;
885             } # if bad characrters
886            
887 0 0         unless ($value =~ /[\w\-.]+\@[\w\-.]+\.[\w\-.]{2}/) {
888 0           $self->_set_result(CONTINUE,
889             "Email provided does not appear to be a valid format.");
890 0           $errors++;
891             } # unless Email # minimally valid
892            
893            
894 0 0         if ($errors) {
895 0           $self->_set_result(NOT_MODIFIED,
896             "Email does not appear to be valid, unchanged.");
897 0           return undef;
898             } # if errors
899 0           $self->_set_result(OK,"validate_Email returned OK");
900 0           return $value;
901             } # validate_Email
902              
903              
904             sub validate_Address1 {
905 0     0 0   my $self = shift;
906 0           my $value = shift;
907 0 0         $self->_clear_result unless __PACKAGE__ eq caller;
908            
909 0 0         if (ref($value) eq 'HASH') {
910 0   0       $value = $value->{Address1} || undef;
911             }
912            
913 0 0         unless (defined $value) {
914 0           $self->_set_result(BAD_REQUEST,
915             "No Address1 provided for validation.");
916 0           return undef;
917             } # if value not provided
918            
919 0           my $errors = 0;
920            
921            
922 0 0 0       if (length($value) < 6 || $value !~ /\w+/) {
    0          
923 0           $self->_set_result(CONTINUE,
924             'Address line 1 appears to be missing or too short.');
925 0           $errors++;
926             } # field missing or too short
927             elsif (length($value) > 100) {
928 0           $self->_set_result(CONTINUE,
929             'Address line 1 appears to be too long.');
930 0           $errors++;
931             } # field too long
932            
933 0           (my @bad_chars) = $value =~ /([^\w\-.# ]+)/g;
934 0 0         if (@bad_chars) {
935 0           $self->_set_result(CONTINUE,
936             "Address line 1 contains bad characters (@bad_chars).");
937 0           $errors++;
938             } # line contains bad characters
939            
940            
941 0 0         if ($errors) {
942 0           $self->_set_result(NOT_MODIFIED,
943             "Address1 does not appear to be valid, unchanged.");
944 0           return undef;
945             } # if errors
946 0           $self->_set_result(OK,"validate_Address1 returned OK");
947 0           return $value;
948             } # validate_Address1
949              
950              
951             sub validate_Address2 {
952 0     0 0   my $self = shift;
953 0           my $value = shift;
954 0 0         $self->_clear_result unless __PACKAGE__ eq caller;
955            
956 0 0         if (ref($value) eq 'HASH') {
957 0   0       $value = $value->{Address2} || undef;
958             }
959            
960 0 0         unless (defined $value) {
961 0           $self->_set_result(BAD_REQUEST,
962             "No Address2 provided for validation.");
963 0           return undef;
964             } # if value not provided
965            
966 0           my $errors = 0;
967            
968            
969 0 0 0       if (length($value) < 6 || $value !~ /\w+/) {
    0          
970 0           $self->_set_result(CONTINUE,
971             'Address line 2 appears to be missing or too short.');
972 0           $errors++;
973             } # field missing or too short
974             elsif (length($value) > 100) {
975 0           $self->_set_result(CONTINUE,
976             'Address line 2 appears to be too long.');
977 0           $errors++;
978             } # field too long
979            
980 0           (my @bad_chars) = $value =~ /([^\w\-.# ]+)/g;
981 0 0         if (@bad_chars) {
982 0           $self->_set_result(CONTINUE,
983             "Address line 2 contains bad characters (@bad_chars).");
984 0           $errors++;
985             } # line contains bad characters
986            
987            
988 0 0         if ($errors) {
989 0           $self->_set_result(NOT_MODIFIED,
990             "Address2 does not appear to be valid, unchanged.");
991 0           return undef;
992             } # if errors
993 0           $self->_set_result(OK,"validate_Address2 returned OK");
994 0           return $value;
995             } # validate_Address2
996              
997              
998             sub validate_City {
999 0     0 0   my $self = shift;
1000 0           my $value = shift;
1001 0 0         $self->_clear_result unless __PACKAGE__ eq caller;
1002            
1003 0 0         if (ref($value) eq 'HASH') {
1004 0   0       $value = $value->{City} || undef;
1005             }
1006            
1007 0 0         unless (defined $value) {
1008 0           $self->_set_result(BAD_REQUEST,
1009             "No City provided for validation.");
1010 0           return undef;
1011             } # if value not provided
1012            
1013 0           my $errors = 0;
1014            
1015            
1016 0 0 0       if (length($value) < 2 || $value !~ /\w+/) {
    0          
1017 0           $self->_set_result(CONTINUE,
1018             'City appears to be missing or too short.');
1019 0           $errors++;
1020             } # field missing or too short
1021             elsif (length($value) > 30) {
1022 0           $self->_set_result(CONTINUE, 'City appears to be too long.');
1023 0           $errors++;
1024             } # field too long
1025            
1026 0           (my @bad_chars) = $value =~ /([^\w\-. ]+)/g;
1027 0 0         if (@bad_chars) {
1028 0           $self->_set_result(CONTINUE,
1029             "City contains bad characters (@bad_chars).");
1030 0           $errors++;
1031             } # line contains bad characters
1032            
1033            
1034 0 0         if ($errors) {
1035 0           $self->_set_result(NOT_MODIFIED,
1036             "City does not appear to be valid, unchanged.");
1037 0           return undef;
1038             } # if errors
1039 0           $self->_set_result(OK,"validate_City returned OK");
1040 0           return $value;
1041             } # validate_City
1042              
1043              
1044             sub validate_State {
1045 0     0 0   my $self = shift;
1046 0           my $value = shift;
1047 0 0         $self->_clear_result unless __PACKAGE__ eq caller;
1048            
1049 0 0         if (ref($value) eq 'HASH') {
1050 0   0       $value = $value->{State} || undef;
1051             }
1052            
1053 0 0         unless (defined $value) {
1054 0           $self->_set_result(BAD_REQUEST,
1055             "No State provided for validation.");
1056 0           return undef;
1057             } # if value not provided
1058            
1059 0           my $errors = 0;
1060            
1061            
1062 0 0 0       if (length($value) < 2 || $value !~ /\w+/) {
    0          
1063 0           $self->_set_result(CONTINUE,
1064             'State appears to be missing or too short.');
1065 0           $errors++;
1066             } # field missing or too short
1067             elsif (length($value) > 20) {
1068 0           $self->_set_result(CONTINUE, 'State appears to be too long.');
1069 0           $errors++;
1070             } # field too long
1071            
1072 0           (my @bad_chars) = $value =~ /([^\w\-.]+)/g;
1073 0 0         if (@bad_chars) {
1074 0           $self->_set_result(CONTINUE,
1075             "State contains bad characters (@bad_chars).");
1076 0           $errors++;
1077             } # line contains bad characters
1078            
1079            
1080 0 0         if ($errors) {
1081 0           $self->_set_result(NOT_MODIFIED,
1082             "State does not appear to be valid, unchanged.");
1083 0           return undef;
1084             } # if errors
1085 0           $self->_set_result(OK,"validate_State returned OK");
1086 0           return $value;
1087             } # validate_State
1088              
1089              
1090             sub validate_Country {
1091 0     0 0   my $self = shift;
1092 0           my $value = shift;
1093 0 0         $self->_clear_result unless __PACKAGE__ eq caller;
1094            
1095 0 0         if (ref($value) eq 'HASH') {
1096 0   0       $value = $value->{Country} || undef;
1097             }
1098            
1099 0 0         unless (defined $value) {
1100 0           $self->_set_result(BAD_REQUEST,
1101             "No Country provided for validation.");
1102 0           return undef;
1103             } # if value not provided
1104            
1105 0           my $errors = 0;
1106            
1107            
1108 0 0 0       if (length($value) < 2 || $value !~ /\w+/) {
    0          
1109 0           $self->_set_result(CONTINUE,
1110             'Country appears to be missing or too short.');
1111 0           $errors++;
1112             } # field missing or too short
1113             elsif (length($value) > 30) {
1114 0           $self->_set_result(CONTINUE, 'Country appears to be too long.');
1115 0           $errors++;
1116             } # field too long
1117            
1118 0           (my @bad_chars) = $value =~ /([^\w\-. ]+)/g;
1119 0 0         if (@bad_chars) {
1120 0           $self->_set_result(CONTINUE,
1121             "Country contains bad characters (@bad_chars).");
1122 0           $errors++;
1123             } # line contains bad characters
1124            
1125            
1126 0 0         if ($errors) {
1127 0           $self->_set_result(NOT_MODIFIED,
1128             "Country does not appear to be valid, unchanged.");
1129 0           return undef;
1130             } # if errors
1131 0           $self->_set_result(OK,"validate_Country returned OK");
1132 0           return $value;
1133             } # validate_Country
1134              
1135              
1136             sub validate_Zip {
1137 0     0 0   my $self = shift;
1138 0           my $value = shift;
1139 0 0         $self->_clear_result unless __PACKAGE__ eq caller;
1140            
1141 0 0         if (ref($value) eq 'HASH') {
1142 0   0       $value = $value->{Zip} || undef;
1143             }
1144            
1145 0 0         unless (defined $value) {
1146 0           $self->_set_result(BAD_REQUEST,
1147             "No Zip provided for validation.");
1148 0           return undef;
1149             } # if value not provided
1150            
1151 0           my $errors = 0;
1152            
1153            
1154 0 0         if ($value !~ /\d{5}/) {
    0          
1155 0           $self->_set_result(CONTINUE,
1156             'Zip appears to be missing or too short.');
1157 0           $errors++;
1158             } # field missing or too short
1159             elsif (length($value) > 10) {
1160 0           $self->_set_result(CONTINUE, 'Zip appears to be too long.');
1161 0           $errors++;
1162             } # field too long
1163            
1164 0           (my @bad_chars) = $value =~ /([^[0-9]\-]+)/g;
1165 0 0         if (@bad_chars) {
1166 0           $self->_set_result(CONTINUE,
1167             "Zip contains bad characters (@bad_chars).");
1168 0           $errors++;
1169             } # line contains bad characters
1170            
1171            
1172 0 0         if ($errors) {
1173 0           $self->_set_result(NOT_MODIFIED,
1174             "Zip does not appear to be valid, unchanged.");
1175 0           return undef;
1176             } # if errors
1177 0           $self->_set_result(OK,"validate_Zip returned OK");
1178 0           return $value;
1179             } # validate_Zip
1180              
1181              
1182             sub new_user_email {
1183 0     0 0   my $self = shift;
1184 0           my $HR_params = shift;
1185 0 0         $self->error("Parameters not passed as a hashref")
1186             unless ref($HR_params) eq 'HASH';
1187            
1188 0           my @call = caller(1);
1189 0           $call[1] =~ s{.+/}{};
1190 0           my $name = $self->{Firstname} . ' ' . $self->{Lastname};
1191            
1192 0           my $notify_emails
1193             = $self->{CLIENTS}{$HR_params->{CLIENT}}{New_User_Notification_Email};
1194 0           my $client_name = $self->{CLIENTS}{$HR_params->{CLIENT}}{Name};
1195            
1196 0 0         warn "CLIENT = $HR_params->{CLIENT}\n" . Dumper($self->{CLIENTS})
1197             if $self->{debug} > 1;
1198            
1199 0           my $message = <
1200             $client_name, Central Authorization Server
1201              
1202             Dear $name,
1203             You have registered with username $self->{Username} and were added to
1204             the default user group. If you did not register, please reply to this email
1205             and notify the administrator immediately.
1206              
1207             Although this email is sent to indicate that you have successfully
1208             registered on the $client_name Central Authorization Server, you may still
1209             require special permissions to be set. You should notify your administrative
1210             contact for this system to request appropriate access be granted. In most
1211             cases, simply replying to all on this email and entering your request at the
1212             top will initiate the required actions.
1213              
1214             Thank you!
1215              
1216             Mail generated for $call[1] by CAS::User V$VERSION
1217              
1218             BODY
1219            
1220 0           my $from = $self->{ADMIN_EMAIL};
1221            
1222 0           my %mail = (
1223             To => $self->{Email},
1224             Cc => $notify_emails ,
1225             From => $from,
1226             Message => $message,
1227             smtp => 'darwin.bu.edu',
1228             Subject => "$self->{Username} registered with $client_name CAS",
1229             );
1230 0 0         sendmail(%mail) or $self->error("Mail error: $Mail::Sendmail::error");
1231 0           $self->_set_result(OK,"new_user_email");
1232             } # new_user_email
1233              
1234              
1235             sub Password {
1236 0     0 1   my $self = shift;
1237 0           my $class = blessed($self);
1238 0 0         $self->error("Not a method call") unless $class;
1239 0 0         $self->_clear_result unless __PACKAGE__ eq caller;
1240 0 0         $self->error('No user ID found in self?!') unless $self->{ID};
1241            
1242 0 0         if (@_) {
1243 0 0         error('No user ID found in self?!') unless $self->{ID};
1244            
1245 0           my $value = $self->validate_Password(@_);
1246 0 0         return undef unless defined $value;
1247            
1248 0           $self->{Password} = $self->crypt_pass($value);
1249 0           $self->{changed}{Password} = 1;
1250             } # if setting password
1251            
1252 0           $self->_set_result(OK,"Password was set");
1253 0           return $self->{Password};
1254             } # set_Password
1255              
1256              
1257             sub crypt_pass {
1258 0     0 0   my $self = shift;
1259 0   0       my $passwd = shift || '';
1260            
1261 0           my @salt = ('a' .. 'z', 0 .. 9, '/', 'A' .. 'Z', '.');
1262 0           my $salt = join('', (@salt[int(rand($#salt)), int(rand($#salt))]));
1263            
1264 0 0         if ($passwd) {
1265 0           $self->_set_result(OK,"crypt_pass");
1266 0           return crypt($passwd,$salt);
1267             } # if we were provided a password, just encrypt
1268            
1269 0           my @chars = ('*', '_', '-', @salt, '#', '!', '@');
1270 0           my $word;
1271 0           foreach (0 .. int(rand(2))+6) { $word .= $chars[int(rand($#chars))] };
  0            
1272            
1273 0           $self->_set_result(OK,"passgen returned OK");
1274 0           return ($word,crypt($word,$salt));
1275             } # passgen
1276              
1277              
1278             # only setting username and password need special handling and all the rest
1279             # are in UserInfo
1280             sub AUTOLOAD {
1281 0     0     my $self = shift;
1282 0 0         return if ($AUTOLOAD =~ /DESTROY/);
1283            
1284 0           my $class = blessed($self);
1285 0 0         $self->error("Not a method call") unless $class;
1286            
1287             # confess("What is going on with $class!!!");
1288            
1289 0 0         $self->_clear_result unless __PACKAGE__ eq caller;
1290            
1291             # nice idea - to many calls in new and load though
1292             # $self->error("No user ID found in self ($class) at $AUTOLOAD?!")
1293             # if (caller[0])[3] ne 'new' and ! $self->{ID};
1294            
1295 0           my $name = $AUTOLOAD;
1296 0           $name =~ s/.*://; # strip fully-qualified portion
1297            
1298 0 0         unless (exists $self->{_permitted}->{$name} ) {
1299 0           $self->error("Can't access `$name' field in class $class");
1300             } # unless access to the data feild is permitted
1301            
1302 0 0         if (@_) {
1303 0 0         $self->error("Not allowed to set $name")
1304             unless $self->{_permitted}{$name} & 2;
1305            
1306             # Simple attributes only accept a value as the only argument
1307             # some attributes may require more, but those should all be
1308             # handled by the attributes validation method, which returns
1309             # the value to set id valid
1310 0           my $value = $_[0];
1311            
1312 0           my $validation_method = "validate_$name";
1313 0 0         if ($self->can($validation_method)) {
1314             # validation methods return the value if valid
1315 0           $value = $self->$validation_method(@_);
1316 0 0         unless (defined $value) {
1317 0           $self->_set_result(NOT_MODIFIED,
1318             "$name invalid, attribute not changed");
1319 0           return undef;
1320             }
1321             } # if attribute requires validation
1322            
1323 0           $self->{changed}{$name} = 1;
1324 0           $self->{$name} = $value;
1325 0           $self->_set_result(OK,"Set $name");
1326 0           return $self->{$name};
1327             } # if a new value supplied
1328            
1329             else {
1330 0 0         $self->error("Not allowed to fetch $name")
1331             unless $self->{_permitted}{$name} & 1;
1332 0           $self->_set_result(OK,"Fetched $name");
1333 0           return $self->{$name};
1334             } # else just return current value
1335             } # AUTOLOAD
1336              
1337              
1338             1;
1339             __END__