Show Contents Previous Page Next Page Chapter 6 - Authentication and Authorization / Authorization Handlers A dissatisfying feature of Apache::AuthzGender is that when an unauthorized user finally gives up and presses the cancel button, Apache displays the generic "Unauthorized" error page without providing any indication of why the user was refused access. Fortunately this is easy to fix with a custom error response. We can call the request object's custom_response() method to display a custom error message, an HTML page, or the output of a CGI script when the Another problem with Apache::AuthzGender is that it uses a nonstandard way to configure the authorization scheme. The standard authorization schemes use a require directive as in: require group authors At the cost of making our module slightly more complicated, we can accommodate this too, allowing access to the protected directory to be adjusted by any of the following directives: require gender F # allow females require user Webmaster Jeff # allow Webmaster or Jeff require valid-user # allow any valid user Example 6-10 shows an improved Apache::AuthzGender that implements these changes. The big task is to recover and process the list of require directives. To retrieve the directives, we call the request object's requires() method. This method returns an array reference corresponding to all of the require directives in the current directory and its parents. Rather than being a simple string, however, each member of this array is actually a hash reference containing two keys: method_mask and requirement. The requirement key is easy to understand. It's simply all the text to the right of the require directive (excluding comments). You'll process this text according to your own rules. There's nothing magical about the keywords user, group, or valid-user.
The method_mask key is harder to explain. It consists of a bit mask indicating what methods the require statement should be applied to. This mask is set when there are one or more <LIMIT> sections in the directory's configuration. The GET, PUT, POST, and DELETE methods correspond to the first through fourth bits of the mask (counting from the right). For example, a require directive contained within a <LIMIT GET POST> section will have a method mask equal to binary 0101, or decimal 5. If no <LIMIT> section is present, the method mask will be -1 (all bits set, all methods restricted). You can test for particular bits using the method number constants defined in the :methods section of Apache::Constants. For example, to test whether the current mask applies to POST requests, you could write a piece of code like this one (assuming that the current requires() is in if ($_->{method_mask} & (1 << M_POST)) { warn "Current requirements apply to POST"; }
In practice, you rarely have to worry about the method mask within your own authorization modules because In the example given earlier, the array reference returned by requires() would look like this: [ { requirement => 'gender F', method_mask => -1 }, { requirement => 'user Webmaster Jeff', method_mask => -1 }, { requirement => 'valid-user', method_mask => -1 } ]
The revised module begins by calling the request object's requires() method and storing it in a lexical variable my $r = shift; my $requires = $r->requires; return DECLINED unless $requires; If requires() returns undef, it means that no require statements were present, so we decline to handle the transaction. (This shouldn't actually happen, but it doesn't hurt to make sure.) The script then recovers the user's name and guesses his or her gender, as before. Next we begin our custom error message: my $explanation = <<END; <TITLE>Unauthorized</TITLE> <H1>You Are Not Authorized to Access This Page</H1> Access to this page is limited to: <OL> END The message will be in a text/html page, so we're free to use HTML formatting. The error warns that the user is unauthorized, followed by a numbered list of the requirements that the user must meet in order to gain access to the page (Figure 6-2). This will help us confirm that the requirement processing is working correctly. Figure 6-2. The custom error message generated by Apache::AuthzGender specifically lists the requirements that the user has failed to satisfy.
Now we process the requirements one by one by looping over the array contained in for my $entry (@$requires) { my($requirement, @rest) = split /\s+/, $entry->{requirement};
For each requirement, we extract the text of the require directive and split it on whitespace into the requirement type and its arguments. For example, the line if (lc $requirement eq 'user') { foreach (@rest) { return OK if $user eq $_; } $explanation .= "<LI>Users @rest.\n"; } If the requirement equals gender, we loop through its arguments looking
to see whether the user's gender is correct and again return elsif (lc $requirement eq 'gender') { foreach (@rest) { return OK if $guessed_gender eq uc $_; } $explanation .= "<LI>People of the @G{@rest} persuasion.\n"; }
Otherwise, if the requirement equals valid-user, then we simply return elsif (lc $requirement eq 'valid-user') { return OK; } } $explanation .= "</OL>";
As we process each require directive, we add a line of explanation to the custom error string. We never use this error string if any of the requirements are satisfied, but if we fall through to the end of the loop, we complete the ordered list and set the explanation as the response for $r->custom_response(AUTH_REQUIRED, $explanation);
The module ends by noting and logging the failure, and returning an $r->note_basic_auth_failure; $r->log_reason("user $user: not authorized", $r->filename); return AUTH_REQUIRED; } The logic of this module places a logical OR between the requirements. The user is allowed access to the site if any of the require statements is satisfied, which is consistent with the way Apache handles authorization in its standard modules. However, you can easily modify the logic so that all requirements must be met in order to allow the user access. Example 6-10. An Improved Apache::AuthzGender package Apache::AuthzGender2; use strict; use Text::GenderFromName qw(gender); use Apache::Constants qw(:common); my %G = ('M' => "male", 'F' => "female"); sub handler { my $r = shift; my $requires = $r->requires; return DECLINED unless $requires; my $user = ucfirst lc $r->connection->user; my $guessed_gender = uc(gender($user)) || 'M'; my $explanation = <<END; <TITLE>Unauthorized</TITLE> <H1>You Are Not Authorized to Access This Page</H1> Access to this page is limited to: <OL> END for my $entry (@$requires) { my($requirement, @rest) = split /\s+/, $entry->{requirement}; if (lc $requirement eq 'user') { foreach (@rest) { return OK if $user eq $_; } $explanation .= "<LI>Users @rest.\n"; } elsif (lc $requirement eq 'gender') { foreach (@rest) { return OK if $guessed_gender eq uc $_; } $explanation .= "<LI>People of the @G{@rest} persuasion.\n"; $explanation .= "</OL>"; $r->custom_response(AUTH_REQUIRED, $explanation); $r->note_basic_auth_failure; $r->log_reason("user $user: not authorized", $r->filename); return AUTH_REQUIRED; } 1; __END__Show Contents Previous Page Next Page Copyright © 1999 by O'Reilly & Associates, Inc. |
HIVE: All information for read only. Please respect copyright! |