Before I get too deeply into this, I should point out that the skeleton authentication handler employed here is taken directly from example 6-5 in Writing Apache Modules in Perl and C by Lincoln Stein and Doug MacEachern (otherwise know as the eagle book). (If you find the price daunting, you can generally find a used copy for a fraction of that at amazon.com or one of the sites that specialize in used books.) The actual details of implementing the handler are discussed much more explicitly here than in the book, but the skeleton handler is the same. This is an excellent book, and represents an excellent reference, especially to some of the more arcane elements of writing apache applications. I would suggest that it would be a good companion to these pages for those who wish to investigate any given element of what I am doing in more detail.
Regardless, with the implementation of even the most basic authentication handler, the architectural flexibility of mod-perl and apache begin to become apparent. As with most major customizations of apache, the introduction of an authentication handler begins with the specification of a <location></location> section in the httpd.conf to associate a type of action with a request for a given uri.
<Location /bb_app/system> AuthName "System Identification" AuthType Basic PerlAuthenHandler BB_UTIL::user require valid-user </Location>
In this instance, the location directives define for apache the characteristics of the handler. The AuthName directive defines a realm for which authentication applies.
The string specified in that directive can be seen as the title of the login widget to the left. (I am using Internet Explorer 6 on WinXP on the machine on which I took this screen shot.
Other browsers and/or operating systems will display a widget with a different appearence at some level.
At right, for example is the widget displayed by the mozilla browser on WinXP. Just looks a little different, same stuff. I could go on cranking in images of the same widget on all of the permutations of operating systems and browsers available on the set of machines available to me, but that runs me out to about 20 separate images, and I think you get the point.)
In the next directive the type of authentication is specified, in this case basic. Authentication types and the encryption of authentication information is a subject of some interest to which I will return at a later point. The basic authentication scheme specified here mashes the user name and password together and represented in base64, which is a way to encode information so it is not intelligle. While this would prevent a password from being readily read from the address window by someone peering over a shoulder, that string can be readily converted back to the plain text password if someone were to capture it with a packet sniffer and run it through the appropriate alogorithm. Be that as it may, basic authentication is fine as a starting point.
The next line in the location section, PerlAuthenHandler, tells apache to use perl to process the autnetication request and what code to use to process the request. The authors of the eagle bookcreated a module to be stored in the apache module directory, following the convention of storing apache-specific modules in that directory. Motivated by the desire to keep application-related material in a structure of its own, I put the same code in the user() subroutine in the BB_UTIL module. Note that as before, if a handler directive points to something other than a module that has a subroutine named handler(), that module must have been loaded in a PerlModule directive so the specific subroutine is visible to the interpreter as apache loads.
Finally, the require directive defines the set of acceptable users for resources in the specified location. The term used here, valid-user, essentially requires that whatever authentication routine is specified in the PerlAuthenHandler directive return 'OK' for a successful authentication. (Actually, the browser and server use codes for this kind of communcation. The Apache::Constants module is used to associate those codes with more understandable representations.) While it is possible to specify a more restrictive condition in this statement, valid-user is the most commonly-used. I may implement a more restrictive require statement when I start implementing system maintenance functions.
Once the apache server has been restarted with the httpd.conf file containing the location section with the new autnetication handler specification, any requests for the resources in that location must pass authentication, triggering the browser to display a login widget and illustrated above. Once the user name and password have been entered in that dialogue, the code in the authentication handler is executed. In this specific example, the code in the actual handler is about as close to non-existent as it could be and still be there.
sub user {
my $r=shift;
my ($res,$sent_pw) = $r->get_basic_auth_pw;
return $res if $res != OK;
my $user = $r->connection->user;
unless ($user and $sent_pw) {
$r->note_basic_auth_failure;
return AUTH_REQUIRED;
}
return OK;
}
After initializing the request object, the $res and $sent_pw scalars are populated with the two-element list return from the object's get_basic_auth_pw method. The first element of that list is the response from the client browser to the server's attempt to initiate basic authentication with the client. If the browser supports basic authentication, and at this point all browsers in widespread use do, with the possible exception of development versions, the response to that will be "OK". (While all browsers support this form of authentication, it is possible that a library supporting the browser has been overwritten or corrupted. If the response is not "OK" then the authentication process is aborted and the browser will display an error message. The next line, in classic perlish fashion, instantiates an instance of an Apache::Connection class with the request object and retrieves the connection's username from the structure accessed by that class.
The following line checks to make sure that something has been entered in both the username and password fields. If not the authorization required code is returned and a page like the one to the right is displayed, but if they have been supplied "OK" is returned, signifying successful authentication.The changes in the application code that take advantage of this start with the line in the header() subroutine's html code that defines the context that is used to generate the response to selection of the data entry and system maintenance option is now
<a href="/bb_app/system/entry/4" /href> Game Entry and System Maintenance </a>the only modification being the addition of "entry" to the URI. This has the effect of both referencing a resource in the location defined as requiring authentication and preserving the filename/additional path information used elsewhere. Note also that the location specified as protected is a subdirectory of the virtual directory /bb_app/ under which the elements of the application not requiring authentication are referenced. (Again, this is purely a construct of the <location></location> sections. There is no requirement that the directory referenced actually represent a subdirectory of the physical directory to which /bb_app/ is aliased, but if it is not that location would require its own alias statement.) In this fashion, non-authenticated sessions can freely roam the elements of the application that do not require authentication, and authenticated sessions can freely moved back and forth between locations requiring authentication and those not without re-entry of credentials, as long as the browser session persists. This is the behavior I desire, at least at this point. (Note that I am not suggesting that this is the only way to make that happen, after all, TMTOWTDI).
With the changes to the httpd.conf file and the incorporation of the authentication handler, the logic in the previous version, in which the $user_name scalar is checked to determine whether the user is logged in and the sel_form() subroutine is executed if not, is, in part, superfluous and redundant. I still have to get values for $num_forms and $game_id, but the user name is coming in elsewhere. (I will deal with getting game_id and num_forms in the next section. For now I have just defined those values statically.)
There are also some modest changes to upper-level state management and to entry into the data entry process that must be made. First, the user cookie is no longer relevant, as the user name is accessible through the connection object as described above.
Second, while some form of the sel_form() subroutine is going to come back in soon, with the values of $game_id and $num_forms defined statically the logic executed as the data entry process is executed simply checks for a session_id parameter, initializes session state variables if it is not there, and generates a page like the one displayed at left. Not too shabby, huh? The only changes required within the BB_APP_INTERFACE module involves adding "entry" to the form action parameter in the store() subroutine so on the next iteration the appropriate conditions are triggered, just as was the case with the specification of the target in the header html.
In the next section, I will add some interface changes that make a little more sense given the way this current section is set up. For example, it seems to me that I should probably give the user the right to choose things like num_forms under some sort of "User Preferences" section. Things like that.