File Coverage

blib/lib/CAS.pm
Criterion Covered Total %
statement 27 165 16.3
branch 2 94 2.1
condition 6 29 20.6
subroutine 8 12 66.6
pod 4 4 100.0
total 47 304 15.4


line stmt bran cond sub pod time code
1             package CAS;
2              
3 5     5   122409 use strict;
  5         11  
  5         595  
4              
5             =head1 NAME
6              
7             CAS - Central Authorization Server
8              
9             =head1 VERSION
10              
11             Version 0.89
12              
13             =cut
14              
15             our $VERSION = '0.89';
16              
17             =head1 SYNOPSIS
18              
19             CAS is intended to provide cross project (client) and cross platform
20             authentication and authorization services. CAS allows a user to have a single
21             username and password, which can be granted access to 0 or more different
22             clients. Even fine grained access controls can be granted differently
23             for any and all of the different clients that use CAS. The central object to
24             CAS is client based, and can be used to manage multiple users.
25            
26             use CAS;
27              
28             my $client = CAS->new({CLIENT_ID => $id});
29              
30             or
31              
32             my $client = new CAS({CLIENT_NAME => 'Project Foo'});
33              
34             my $session = $client->authenticate({USERNAME => 'foo',
35             PASSWORD => 'foobar'});
36             my $can_do = $client->authorize({USER => $session,
37             RESOURCE => 'resource1', MASK => 'create'});
38              
39             Code problems and mis-configurations should cause the call to die. Otherwise
40             methods return undef on failure. Processing statements are stored in the
41             calling objects message stack, which is reset with every method call.
42              
43             unless (defined $session) { die($client->messages) }
44              
45             =head1 DESCRIPTION
46              
47             CAS provides a set of tools for accessing a central user database, allowing
48             a single username and password to be used by multiple applications & sites
49             (clients). Permissions can be granted however finely or loosely the developer
50             finds useful. The system also stores some very basic session information,
51             providing some very minimal usage auditing. A separate distribution,
52             CAS-Apache2, provides a mod_perl 2 application for protecting web sites from
53             CAS.
54              
55              
56             =head2 USAGE OVERVIEW
57              
58             You first must create a CAS client object. Clients are defined in the database
59             in advance by the CAS administrator. You will need to know the client ID, name
60             or domain, all of which need to be unique to each client.
61              
62             Examples:
63              
64             my $client = CAS->new({CLIENT_ID => 2});
65              
66             or
67              
68             my $client = CAS->new({CLIENT_NAME => 'Project Foo'});
69              
70             You can fetch information about the client from this object if needed. But its
71             main purpose is to authenticate users and check their authorizations. As the
72             users can be granted access to any client, the specific client used to create
73             this object doesn't matter if you just want to authenticate the user.
74              
75             my $session = $client->authenticate({});
76              
77             The session token is a unique identifier for the particular session. It can be
78             returned to the application as a key for session tracking, allowing for
79             persistent login sessions and such. It is also used to identify the user when
80             checking authorization.
81              
82             my $is_authorized = $client->authorize({SESSION => $session,
83             RESOURCE => $request, MASK => 8});
84              
85              
86             The session token can also be used to fetch a user object, which remembers the
87             client under which it was created.
88              
89             my $user = $client->user($session);
90              
91             This user object, L, can be used to get information about the
92             user. Security of the session token and its use is left to the discretion of
93             the caller.
94              
95             =head2 CLIENT OBJECT ATTRIBUTES
96              
97             =over 4
98              
99             =item user_info_fields
100              
101             Returns a hash reference containing the field names in the UserInfo table.
102            
103             =item supl_user_info_fields
104              
105             Returns a hash reference containing the field names in
106             the clients supplemental_user_table, if defined.
107            
108             =item supplemental_user_table
109              
110             The name of the clients supplemental user table.
111            
112             =item admin_email
113              
114             The email address for the user designated as the administrator of the client.
115              
116             =item debug
117              
118             The debug level for the client object. The default level is determined by the
119             CAS configuration file. This is the only CAS client object attribute which can
120             be set.
121              
122             $client->debug(2);
123              
124             =item id
125              
126             The ID of the client.
127              
128             =item name
129              
130             The name of the client.
131              
132             =item default_group
133              
134             The default group assigned to new users registering through the client.
135              
136             =item domain
137              
138             The domain of the client. This can be used to allow a local interface to
139             determine what client to assign based on the IP or such of a remote connection.
140              
141             =item base_path
142              
143             The base path for this clients application(s) or work space. Primarilly used
144             for websites where the project area defined for the client is a subsection of
145             a website.
146              
147             =item description
148              
149             A description of the client.
150              
151             =item cookie_name
152              
153             Primarilly used by CAS-Apache2 for determining the name of the cookie in whcih
154             to store or fetch the session token.
155              
156             =item timeout
157              
158             The period of incativitiy after which a user is forced to re-authenticate.
159              
160             =back
161              
162             =head2 MESSAGING
163              
164             All methods produce some internal messages while processing. When a method is
165             first invoked on a CAS object, any old messages are cleared out and its initial
166             result code is set to ERROR (so that if anything unexpected happens it has the
167             result we would want - ERROR).
168              
169             There are a wide variety of possible result codes that a method could use.
170             L The specific ones that a method might set are described in
171             the methods specific documentation. However there are three that are the most
172             common, ERROR, BAD_REQUEST and OK which we will use in the following examples.
173             The status is set to ERROR both when a method first starts and on non-fatal but
174             still critical problems. BAD_REQUEST is generally set when a method call was
175             properly constructed, but required parameters were missing or in an invalid
176             format. OK is usually the status set after it has completed its job
177             sucsesfully, just before returning.
178              
179             =head3 Messaging methods
180              
181             =over 4
182              
183             =item response_is
184              
185             Used to check the status set by the last method called on the object:
186              
187             $client->response_is('STATUS_NAME');
188              
189             =item response_code
190              
191             Returns the status set by the last method called on the object (as text):
192              
193             my $status = $client->response_code;
194              
195             =item messages
196              
197             Returns all the messages generated by the last method called on the object. If
198             called in list context returns a list of the messages. If called in scalar
199             context returns a string, starting with the class name of the object, followed
200             by all the messages generated joined on "; ".
201              
202             my $messages = $client->messages;
203              
204             =back
205              
206             Be sure to see L for more details.
207              
208             =head3 Example
209              
210             Calling authentication with the USERNAME missing:
211              
212             %args = get_user_credentials();
213             my $session = $client->authenticate(\%args);
214             unless (defined $session) {
215             if ($client->response_is('BAD_REQUEST')) {
216             warn "Can't authenticate - missing required arguments: "
217             . $client->messages;
218             # try get_user_credentials again?
219             } # if bad request
220             else {
221             my $status = $client->response_code;
222             die "Problem with authentication - Status: $status, Messages: " .
223             . $client->messages;
224             } # something else went wrong?
225             } # unless session token returned
226              
227              
228             =head2 FUTURE PLANS
229              
230             Here is the BIG wish list for CAS. For more humble feature requests, see
231             L
232              
233              
234             =over 4
235              
236             =item XML/YAML/SOAP/JSON
237              
238             I'd like to have optional handlers for accepting and replying to requests
239             through one or more data exchange formats. Most likely I'll do this not through
240             this core distribution, but through special mod_perl handlers under the
241             L distribution. This will be the way through which not only
242             remote applications access CAS from a different system (other than browsers
243             accessing local pages), but also how any other languages could potentially
244             use CAS authentication and authorization from a central database.
245              
246             =item LDAP & Kerberos
247              
248             It would be great to have optional plugins or such that extend CAS to work
249             seemlessly along side both LDAP and Kerberos. An earlier incarnation of this
250             system actually did interact with Kerberos. If a user regestered with their
251             kerberos username and password, CAS verified authentication from then on
252             against Kerberos. It even fetched some user info from the Kerberos server
253             using ph. The schema still has fields for indicating if a user record relates
254             to a kerberos or ldap system, but there is no functionality at this time for
255             such.
256              
257             =back
258              
259             =cut
260              
261              
262              
263 5     5   31 use Scalar::Util qw(blessed);
  5         9  
  5         455  
264 5     5   2453 use CAS::Config;
  5         16  
  5         135  
265 5     5   3178 use CAS::User;
  5         16  
  5         329  
266 5     5   56 use Digest::MD5 qw(md5_hex);
  5         11  
  5         336  
267 5     5   30 use Carp qw(cluck confess croak carp);
  5         10  
  5         464  
268              
269             # otherwise constants don't get exported
270             #use base qw(CAS::Messaging);
271 5     5   34 use CAS::Messaging;
  5         11  
  5         13250  
272             our @ISA = qw(CAS::Messaging);
273             our $AUTOLOAD = '';
274              
275              
276             # Config fields that subclasses of core should be able to get and set
277             # Bitmasked with get permission = 1, set = 2, both = 3
278             my %fields = (
279             client => 1,
280             dbh => 1,
281             user_info_fields => 1,
282             supl_user_info_fields => 1,
283             admin_email => 1,
284             debug => 3,
285             id => 1,
286             name => 1,
287             supplemental_user_table => 1,
288             default_group => 1,
289             domain => 1,
290             base_path => 1,
291             description => 1,
292             cookie_name => 1,
293             timeout => 1,
294             );
295              
296              
297             =head1 METHODS
298              
299             =head2 new
300              
301             Create a new client object.
302              
303             PARAMETERS:
304              
305             CLIENT_ID: The database ID of the client which is seeking to connect to
306             CAS.
307              
308             CLIENT_NAME: The name of the client which is seeking to connect to
309             CAS.
310              
311             CLIENT_DOMAIN: The domain of the client which is seeking to connect to
312             CAS.
313              
314             You can use any one. If more than one is defined they are checked in the order
315             listed.
316              
317             OPTIONS:
318              
319             CONFIG: Alternate configuration file. Defaults to '/etc/CAS.yaml'.
320              
321             DEBUG: Set the DEBUG level for this object.
322              
323             =cut
324             sub new {
325 6     6 1 2216 my $proto = shift;
326 6   33     48 my $class = ref($proto) || $proto;
327 6         16 my $HR_params = shift;
328 6 50       27 croak("Parameters not passed as a hashref")
329             unless ref($HR_params) eq 'HASH';
330            
331 6 50 100     51 croak("No client key provided")
      66        
332             unless defined $HR_params->{CLIENT_ID} || $HR_params->{CLIENT_NAME}
333             || $HR_params->{CLIENT_DOMAIN};
334            
335             # load config
336 6         59 my $config = CAS::Config->load($HR_params);
337 0           $config->{_permitted} = \%fields;
338 0           $config->{_users} = {};
339            
340 0           my $self = bless ($config,$class);
341 0           $self->_set_result(CREATED,"CAS Client object sucesfully initiatied");
342 0           return $self;
343             } # new
344              
345              
346             =head2 authenticate
347              
348             This function is called to verify the username and password provided by the
349             user. It will imediatly return undef and set the response code to BAD_REQUEST
350             unless both the username and password were provided (well, technically,
351             evaluate to true). It then checks that the password provided matches the one
352             stored for that user.
353              
354             Perls crypt function is called using the suplied password as the word and the
355             password from the db as the salt. If the result matches the stored password,
356             access will be granted. A session key is generated using md5_hex and the user
357             ID and time are stored in the db on that key. Also stored are either the users
358             IP address (if supplied) or the root caller() otherwise.
359              
360             If authentication fails, NOT_FOUND is returned. If authentication succedes
361             the md5_hex key is returned. The key is intended
362             to be used by CAS as a session token for L after first
363             authenticated. Any error message can be found in $client->errstr.
364              
365             PARAMETERS:
366              
367             USERNAME: The username.
368              
369             PASSWORD: The users password.
370              
371             OPTIONS:
372              
373             IP: The remote connection IP. If present at authentication, the IP will be
374             required to be provided and match during any subsiquent authorization check.
375              
376             =cut
377             sub authenticate {
378 0     0 1   my $self = shift;
379 0 0         $self->error("Not a method call") unless blessed($self);
380 0           $self->_clear_result;
381            
382 0           my $HR_params = shift;
383 0 0         $self->error("Parameters not passed as a hashref")
384             unless ref($HR_params) eq 'HASH';
385 0   0       my $debug = $HR_params->{DEBUG} || $self->{DEBUG} || 0;
386 0           my $dbh = $self->dbh;
387            
388 0 0         warn("Checking authentication for $HR_params->{USERNAME}") if $debug;
389            
390 0 0         unless ($HR_params->{USERNAME}) {
391 0           $self->_set_result(BAD_REQUEST,"No username provided.");
392 0           return undef;
393             } # resource to check authorization against required
394            
395 0 0         unless ($HR_params->{PASSWORD}) {
396 0           $self->_set_result(BAD_REQUEST,"No password provided.");
397 0           return undef;
398             } # resource to check authorization against required
399            
400             # OK, now we have a username, lets check the suplied password
401 0           my $Quser = $dbh->quote($HR_params->{USERNAME});
402             # now get userID and password for username
403 0           my $HR_user = $dbh->selectrow_hashref("SELECT *
404             FROM Users WHERE Username = $Quser");
405 0 0         $self->error("Database error: " . $dbh->errstr) if $dbh->err;
406            
407 0 0         unless ($HR_user->{User}) {
408 0           $self->_set_result(NOT_FOUND,
409             "Invalid account, username $HR_params->{USERNAME} not found.");
410 0           return undef;
411             } # unless user id returned
412            
413 0 0         if ($HR_user->{Disabled} eq 'Yes') {
414 0           $self->_set_result(FORBIDDEN,"User has been disabled.");
415 0           return undef;
416             } # if user diasabled
417            
418            
419             # OK, the user exists and we should have all the information needed
420             # to authenticate
421 0 0         $self->gripe("Password valid?") if $debug > 1;
422 0 0         unless ($HR_user->{Password}
423             eq crypt($HR_params->{PASSWORD},$HR_user->{Password})) {
424 0           $self->_set_result(AUTH_REQUIRED,"Incorrect password.");
425 0           return undef;
426             } # unless password suplied matches users in db
427            
428             # OK, user authenticated, provide a session token
429 0 0         $self->gripe("Issue session token") if $debug;
430 0           my $now = localtime;
431 0           my $Skey = md5_hex("$0$HR_user->{Password}$HR_params->{USERNAME}$now");
432 0           my $Qkey = $dbh->quote($Skey);
433            
434             # now, stick seomthing into IP?
435 0           my $ip = $dbh->quote($HR_params->{IP});
436            
437 0           $dbh->do("INSERT INTO Session (ID, User, IP)
438             VALUES ($Qkey,$HR_user->{User},$ip)");
439 0 0         $self->error("Can't log user in: " . $dbh->errstr) if $dbh->err;
440            
441 0           $self->_set_result(OK,"User authenticated.");
442 0           return ($Skey);
443             } # authenticate
444              
445              
446             =head2 authorize
447              
448             This checks the database to see if the user is currently logged in and if they
449             are allowed to use the specified resource.
450              
451             PARAMETERS:
452              
453              
454             SESSION: The session token returned by CAS when the user was authenticated
455             and logged in. This is used to get the user information required for checking
456             that user is logged in and that their session has not timed out. ***SECURITY***
457             It is up to you to make sure that this value is kept private and secure during
458             the session.
459              
460             USER: Alias for SESSION.
461              
462             RESOURCE: This is the resource definition that will be checked in the
463             database.
464              
465             PERMISSIONS: This is the type of action you want to check if the user has
466             permission for relative to the RESOURCE. The allowed values are read, modify,
467             create and delete. Create refers to permision to create a new record which
468             uses the refered to resource as a foreign key, or is under the refered resource
469             'tree'.
470              
471             OPTIONS:
472              
473             MASK: This is an integer mask of permissions to be checked for the specified
474             RESOURCE. This can optionaly be used instead of PERMISSIONS, and is the only
475             way to specify requests on more than one type of permission at the same time.
476             The Values are 8 = read, 4 = modify, 2 = create, 1 = delete. To check for
477             multiple permissions at the same time simply sum all the permissions you want
478             to check. For example, to check for read and modify permision, provide 12 (8+4)
479             as the value for MASK. MASK overides PERMISSIONS if both are specified.
480              
481             MATCHKEY: A matchkey can be used to specify a specific element or key
482             match required. For example, RESOURCE my specify a particular table in a
483             database, with MATCHLEY specifying the primary key match required. Or if
484             RESOURCE was a web page, MATCHKEY may indicate a specific form element.
485              
486             IP: The remote IP of the user. If this was provided during authentication then
487             it is REQUIRED for authorization and the IP's must match.
488              
489             =cut
490             sub authorize {
491 0     0 1   my $self = shift;
492 0 0         $self->error("Not a method call") unless blessed($self);
493 0           $self->_clear_result;
494            
495 0           my $HR_params = shift;
496 0 0         $self->error("Parameters not passed as a hashref")
497             unless ref($HR_params) eq 'HASH';
498 0   0       my $debug = $HR_params->{DEBUG} || $self->debug || 0;
499 0           my $dbh = $self->dbh;
500            
501 0 0         warn("Checking authorization") if $debug;
502            
503 0 0         unless ($self->client->{ID}) {
504 0           $self->_set_result(ERROR,"Client object doesn't know its own ID?!");
505 0           return undef;
506             } # client required
507            
508 0 0         unless ($HR_params->{RESOURCE}) {
509 0           $self->_set_result(BAD_REQUEST,"No resource to authorize against "
510             . "provided.");
511 0           return undef;
512             } # resource to check authorization against required
513            
514 0   0       my $session = $HR_params->{SESSION} || $HR_params->{USER} || undef;
515 0 0 0       unless (defined $session && $session =~ /^\S{32}$/) {
516 0           $self->_set_result(BAD_REQUEST, "Missing or bad SESSION($session) "
517             . "for authorization on request $HR_params->{RESOURCE}");
518 0           return undef;
519             } # session token required
520            
521 0           my $qsession = $dbh->quote($session);
522            
523 0           my $logged_ip = $dbh->selectrow_array("SELECT IP
524             FROM Session WHERE ID = $qsession");
525 0 0         $self->error('Problem cheking for logged IP: ' . $dbh->errstr)
526             if $dbh->err;
527            
528             # if an IP was logged when authenticated, the provided IP must match
529 0 0 0       if ($logged_ip && $logged_ip ne $HR_params->{IP}) {
530 0           $self->_set_result(FORBIDDEN,
531             "Current IP ($HR_params->{IP}) does not match IP "
532             . "when you logged on ($logged_ip). This may indicate a 'man in "
533             . "the middle' security attack.");
534 0           return undef;
535             } # if IP & ip doesn't match
536            
537 0           my $timeout = $self->client->{Timeout};
538 0 0         unless ($timeout) {
539 0           $self->_set_result(ERROR,"Client object does not have a timeout?!");
540 0           return undef;
541             } # client required
542            
543 0           my $get_timediff = $dbh->prepare("SELECT unix_timestamp()
544             - unix_timestamp(TS) FROM Session WHERE ID = $qsession",
545             {RaiseError => 1});
546 0 0         $self->error("Problem preparing timediff statement: " . $dbh->errstr)
547             if $dbh->err;
548            
549 0           $get_timediff->execute();
550 0 0         $self->error("Problem executing timediff statement: " . $dbh->errstr)
551             if $dbh->err;
552            
553 0           my $timediff = $get_timediff->fetchrow_array();
554 0 0         $self->error("Problem fetching timediff: " . $dbh->errstr)
555             if $dbh->err;
556            
557 0 0         $self->gripe("Params appear in place, checking timeout: "
558             . "$timediff > $timeout") if $debug;
559 0           my $try = 2;
560 0 0         unless (defined $timediff) {
    0          
    0          
561 0           $self->_set_result(ERROR,
562             "Session ID $qsession not in database.");
563 0           return undef;
564             } # session token not found in db
565            
566             elsif ($timediff == 0) {
567 0           while ($timediff == 0) {
568 0           sleep(1);
569 0           $get_timediff->execute();
570 0 0         $self->error("Problem executing timediff statement: "
571             . $dbh->errstr) if $dbh->err;
572            
573 0           $timediff = $get_timediff->fetchrow_array();
574 0 0         $self->error("Problem fetching timediff: " . $dbh->errstr)
575             if $dbh->err;
576            
577 0 0         last if $try++ == 8;
578             } # while timediff not true
579            
580 0 0         unless ($timediff) {
581 0           $self->_set_result(FORBIDDEN,
582             "Problem resolving timeout for $qsession.");
583 0           return undef;
584             } # unless second query suceeded
585             } # session token not found
586            
587             elsif ($timediff > $timeout) {
588 0           $self->_set_result(AUTH_REQUIRED,"Session has timed out.");
589 0           return undef;
590             } # if session timed out
591            
592 0           $HR_params->{CLIENT} = $self->client->{ID};
593 0           $HR_params->{USER} = $self->user($session)->{ID};
594 0   0       $HR_params->{MATCHKEY} ||= '';
595 0 0         unless ($dbh->allowed($HR_params)) {
596 0           $self->_set_result(FORBIDDEN,
597             "User for session $qsession not authorized to access "
598             . "$HR_params->{RESOURCE},$HR_params->{MATCHKEY}:\n\t"
599             . $dbh->errstr);
600 0           return undef;
601             } # unless user has permision
602            
603 0           $dbh->do("UPDATE Session SET TS=NULL WHERE ID = $qsession");
604 0 0         $self->error("Problem updating timestamp for $qsession: " .
605             $dbh->errstr) if $dbh->err;
606            
607 0           $self->_set_result(OK,"User authenticated.");
608 0           return OK;
609             } # authorize
610              
611              
612             =head2 user
613              
614             Access the user object (L) for authenticated users. Method takes
615             a single argument, the authenticated users session token.
616              
617             =cut
618             sub user {
619 0     0 1   my $self = shift;
620 0 0         $self->error("Not a method call") unless blessed($self);
621 0           $self->_clear_result;
622 0           my $session = shift;
623            
624 0 0 0       unless (defined $session && $session =~ /^\S{32}$/) {
625 0           $self->_set_result(BAD_REQUEST, "Missing or bad SESSION($session) ");
626 0           return undef;
627             } # session token required
628            
629             # if we've already loaded up the user object, just return it
630 0 0         if ($self->{_users}{$session}) {
631 0           $self->_set_result(OK,"Cached user object returned.");
632 0           return $self->{_users}{$session};
633             } # if user already stored
634            
635 0           my $dbh = $self->dbh;
636 0           my $qsession = $dbh->quote($session);
637 0           my $id = $dbh->selectrow_array("SELECT User FROM Session
638             WHERE ID = $qsession");
639 0 0         $self->error("Problem getting user ID from $qsession: " .
640             $dbh->errstr) if $dbh->err;
641            
642             # should we be checking for old instances of the same user to delete?
643            
644 0           my $user = CAS::User->load({ID => $id, CLIENT_ID => $self->client->{ID},
645             CONFIG => $self->{conf_file}});
646 0 0 0       unless (defined $user && $user->response_is(CREATED)) {
647 0           $self->_set_result(ERROR,$user->messages);
648 0           return undef;
649             } # unless we were able to load user
650            
651 0           $self->{_users}{$session} = $user;
652 0           $self->_set_result(OK,"User object created and returned.");
653 0           return $user;
654             } # user
655              
656              
657              
658             # Allows fetching of certain CAS attributes
659             sub AUTOLOAD {
660 0     0     my $self = shift;
661 0 0         return if ($AUTOLOAD =~ /DESTROY/);
662 0           my $class = blessed($self);
663 0 0         $self->error("Not a method call") unless $class;
664 0           $self->_clear_result;
665            
666 0           my $name = $AUTOLOAD;
667 0           $name =~ s/.*://; # strip fully-qualified portion
668            
669 0 0         unless (exists $self->{_permitted}->{$name} ) {
670 0           $self->error("Can't access `$name' field in class $class");
671             } # unless access to the data feild is permitted
672            
673 0 0         if (@_) {
674 0 0         $self->error("Not allowed to set $name")
675             unless $self->{_permitted}{$name} & 2;
676             # update database
677            
678 0           $self->{$name} = $_[0];
679 0           return $self->{$name};
680             } # if a new value supplied
681             else {
682 0 0         $self->error("Not allowed to fetch $name")
683             unless $self->{_permitted}{$name} & 1;
684 0           return $self->{$name};
685             } # else just return current value
686             } # AUTOLOAD
687              
688              
689             =head1 INSTALLING
690              
691             There are a few steps you will need to handle before you can proceed to the
692             usual CPAN distribution make, make test, make install magic. Primarilly, you
693             need to create the CAS database before any tests beyond syntax checking will
694             pass.
695              
696             % tar -xzf CAS-x.xx.tar.gz
697             % cd CAS-x.xx
698             % pwd
699             /path/to/CAS-x.xx
700             % mysql -u root -p
701             password:
702             mysql> CREATE DATABASE CAS;
703             mysql> USE CAS;
704             mysql> source /path/to/CAS-x.xx/CAS.sql
705             mysql> GRANT ALL ON CAS.* TO CAS_query IDENTIFIED BY 'local_passwd'
706             mysql> GRANT ALL ON CAS.* TO CAS_query@localhost IDENTIFIED BY 'local_passwd'
707             mysql> exit
708             % perl Makefile.PL
709             % make
710             % make test
711             % make install
712              
713             When running Makefile.PL for the first time you will be asked a bunch of
714             questions. Answer them appropriately for your system. The DB_* items all
715             relate to the information you provided mysql when setting up the database. If
716             at any time you want to regenerate the configuration file, just delete it and
717             rerun Makefile.PL.
718              
719             =head1 AUTHOR
720              
721             Sean P. Quinlan, C<< >>
722              
723             =head1 development notes
724              
725             =head2 groups
726              
727             Groups are always associated with a client. However, groups from one
728             client can be granted permissions on any other client. Generally all
729             groups are owned by the CAS Admin client but it is possible to have admin
730             tools on another client and allow them to manage their own group(s). The
731             admin user for any client can alter/drop existing groups under that client.
732             Additionally groups can have a 'Owner' specified. This is generally a user
733             who also has rights to modify the group and add/remove members, but not to
734             delete it.
735              
736             =head1 BUGS
737              
738             Please report any bugs or feature requests to
739             C, or through the web interface at
740             L.
741             I will be notified, and then you'll automatically be notified of progress on
742             your bug as I make changes.
743              
744             =head1 HISTORY
745              
746             =over 8
747              
748             =item 0.01
749              
750             Original version; created by module-starter
751              
752             =item 0.1
753              
754             Initial code port from CAS. History below to .30_2 ported from CAS.
755              
756             =item 0.2
757              
758             Basic required functionality for check auths in place. Apache auth handlers done
759             as well as simple Login handler. Core tests written and passing, user tests of
760             Apache handlers pass basic required functionality.
761              
762             =item 0.21
763              
764             User module functional and all basic methods in place. No automated tests for it
765             yet but that will be my next task before moving on to the Apache handlers for
766             registering a new user and a user view edit account handler. Also started
767             working on the docs.
768              
769             =item 0.22
770              
771             Added tests for user object and disable/enable methods. Small additions to
772             docs, like fixing my email address in this package! ;)
773              
774             =item 0.23
775              
776             Most of the basic Apache stuff has been worked out. The CAS.yaml file was
777             expanded and commented. I made a CAS.conf for all our Apache config stuff so
778             admins can just Include it rather than edit the main conf. So far registering
779             a new user & logging are functional if not quite complete or pretty.
780              
781             =item 0.3_1
782              
783             The internals of this module are pretty stable now. I added the
784             krb5_authentication function and added code to check_authentication to check
785             krb5 auth if required in conf or specified in user table.
786              
787             =item 0.30_2
788              
789             Ported to stub distribution generated by module-starter. Split Apache and
790             core CAS functionality into two dists. Started removing krb5 support from core
791             modules. If I continue to support it, it will be as an optional extension.
792              
793             =item 0.40
794              
795             Entered heavy development - many change entries were not made. Guessing from
796             here to version .89
797              
798             =item 0.41
799              
800             Finished post-port cleanup. Added some very simple tests.
801              
802             =item 0.42
803              
804             Split out Messaging.pm and did a little more cleanup on CAS.pm
805              
806             =item 0.43
807              
808             Reworked parts of Messaging.pm, updated everything to use messaging.
809              
810             =item 0.44
811              
812             Did some code cleanup on Users.pm, improved AUTOLOADS, adding %allowed with
813             bitmasks. Added a few more tests, most of which fail.
814              
815             =item 0.50
816              
817             Started working on API and getting tests to pass. Small
818             adjustments to schema.
819              
820             =item 0.52
821              
822             Debugging. Tests passing.
823              
824             =item 0.60
825              
826             Completely changed object relations, making the CAS object all about the
827             client and adding user caching. User.pm is no longer a subclass of CAS and
828             authentication happens through the client object.
829              
830             =item 0.61
831              
832             Updated tests and made some changes to API based on working out tests.
833              
834             =item 0.80
835              
836             Wrote a slew more tests, got all the client and user tests passing.
837              
838             =item 0.81
839              
840             Added generation of CAS.yaml to Makefile.PL and wrote post_install.prl.
841              
842             =item 0.82
843              
844             Refined CAS.yaml generation some and tripled the number of tests.
845              
846             =item 0.83
847              
848             Got auth tests passing!
849              
850             =item 0.86
851              
852             All basic tests pass for existing funtionality. Can add, load, edit &
853             disable users. Client object can handle multiple users, caching user
854             objects by session token and authenticate and authorize against
855             permissions in database.
856              
857             =item 0.87
858              
859             Improved some error statements. Updated MANIFEST so the new modules were
860             included in the distribution. (d'oh!) Allowed caller to supply ID to
861             User->new to support installs where there is already a database of users
862             (or employees) where use of pre-existing IDs is important.
863              
864             =item 0.88
865              
866             Broke up Config.pm, mainly to separate database connection from load and to
867             use a database connection routine that captured the db password in a closure.
868             This was required to support CAS-Apache2, where storing the database
869             connection in the global client object caused 'Command out of sync' errors
870             on some otherwise valid setups.
871              
872             =item 0.89
873              
874             Updated the documentation. Made the fields in the clients table attributes of
875             the client object. Added some info on the caller to messages when debuging.
876              
877             =back
878              
879              
880             =head1 SUPPORT
881              
882             You can find documentation for this module with the perldoc command.
883              
884             perldoc CAS
885              
886             On most Unix systems you can probably also find the documentation under the
887             man pages.
888              
889             shell> man CAS
890              
891             Please join the CAS mailing list and suggest a final release name for
892             the package.
893              
894             http://mail.grendels-den.org/mailman/listinfo/CAS_grendels-den.org
895              
896             You can also look for information at:
897              
898             =over 4
899              
900             =item * AnnoCPAN: Annotated CPAN documentation
901              
902             L
903              
904             =item * CPAN Ratings
905              
906             L
907             =item * Search CPAN
908              
909             L
910              
911             =back
912              
913             =head2 BUGS
914              
915             For bugs, bug reporting and feature requests, see CPAN's request tracker
916              
917             L
918              
919              
920             =head1 ACKNOWLEDGEMENTS
921              
922             The Bioinformatics Group at Massachusetts General Hospital during my
923             tenure there for development assistance and advice, particularly the QA team
924             for banging on the project code.
925              
926              
927             =head1 COPYRIGHT & LICENSE
928              
929             Copyright 2004-2007 Sean P. Quinlan, all rights reserved.
930              
931             This program is free software; you can redistribute it and/or modify it
932             under the same terms as Perl itself.
933              
934             =cut
935              
936             1; # End of CAS