File Coverage

blib/lib/Apache/AuthCookie.pm
Criterion Covered Total %
statement 10 12 83.3
branch n/a
condition n/a
subroutine 4 4 100.0
pod n/a
total 14 16 87.5


line stmt bran cond sub pod time code
1             package Apache::AuthCookie;
2              
3 1     1   6 use strict;
  1         3  
  1         39  
4              
5 1     1   8 use Carp;
  1         3  
  1         106  
6 1     1   1700 use CGI::Util ();
  1         8708  
  1         39  
7 1     1   3012 use mod_perl qw(1.07 StackedHandlers MethodHandlers Authen Authz);
  0            
  0            
8             use Apache::Constants qw(:common M_GET M_POST FORBIDDEN REDIRECT);
9             use vars qw($VERSION);
10              
11             # $Id: AuthCookie.pm,v 2.16 2001/06/01 15:50:27 mschout Exp $
12             $VERSION = '3.00';
13              
14             sub recognize_user ($$) {
15             my ($self, $r) = @_;
16             my $debug = $r->dir_config("AuthCookieDebug") || 0;
17             my ($auth_type, $auth_name) = ($r->auth_type, $r->auth_name);
18             return unless $auth_type && $auth_name;
19             return unless $r->header_in('Cookie');
20              
21             my ($cookie) = $r->header_in('Cookie') =~ /${auth_type}_${auth_name}=([^;]+)/;
22             $r->log_error("cookie ${auth_type}_${auth_name} is $cookie") if $debug >= 2;
23             return unless $cookie;
24              
25             if (my ($user) = $auth_type->authen_ses_key($r, $cookie)) {
26             $r->log_error("user is $user") if $debug >= 2;
27             $r->connection->user($user);
28             }
29             return OK;
30             }
31              
32              
33             sub login ($$) {
34             my ($self, $r) = @_;
35             my $debug = $r->dir_config("AuthCookieDebug") || 0;
36              
37             my ($auth_type, $auth_name) = ($r->auth_type, $r->auth_name);
38             my %args = $r->method eq 'POST' ? $r->content : $r->args;
39             unless (exists $args{'destination'}) {
40             $r->log_error("No key 'destination' found in posted data");
41             return SERVER_ERROR;
42             }
43            
44             # Get the credentials from the data posted by the client
45             my @credentials;
46             while (exists $args{"credential_" . ($#credentials + 1)}) {
47             $r->log_error("credential_" . ($#credentials + 1) . " " .
48             $args{"credential_" . ($#credentials + 1)}) if ($debug >= 2);
49             push(@credentials, $args{"credential_" . ($#credentials + 1)});
50             }
51            
52             # Exchange the credentials for a session key.
53             my $ses_key = $self->authen_cred($r, @credentials);
54             $r->log_error("ses_key " . $ses_key) if ($debug >= 2);
55              
56             $self->send_cookie($ses_key);
57              
58             if ($r->method eq 'POST') {
59             $r->method('GET');
60             $r->method_number(M_GET);
61             $r->headers_in->unset('Content-Length');
62             }
63             unless ($r->dir_config("${auth_name}Cache")) {
64             $r->no_cache(1);
65             $r->err_header_out("Pragma" => "no-cache");
66             }
67             $r->header_out("Location" => $args{'destination'});
68             return REDIRECT;
69             }
70              
71             sub logout($$) {
72             my ($self,$r) = @_;
73             my $debug = $r->dir_config("AuthCookieDebug") || 0;
74            
75             my ($auth_type, $auth_name) = ($r->auth_type, $r->auth_name);
76            
77             # Send the Set-Cookie header to expire the auth cookie.
78             my $str = $self->cookie_string( request => $r,
79             key => "$auth_type\_$auth_name",
80             value => '',
81             expires => 'Mon, 21-May-1971 00:00:00 GMT' );
82             $r->err_headers_out->add("Set-Cookie" => "$str");
83             $r->log_error("set_cookie " . $r->err_header_out("Set-Cookie")) if $debug >= 2;
84             unless ($r->dir_config("${auth_name}Cache")) {
85             $r->no_cache(1);
86             $r->err_header_out("Pragma" => "no-cache");
87             }
88              
89             #my %args = $r->args;
90             #if (exists $args{'redirect'}) {
91             # $r->err_header_out("Location" => $args{'redirect'});
92             # return REDIRECT;
93             #} else {
94             # $r->status(200);
95             # return OK;
96             #}
97             }
98              
99             sub authenticate ($$) {
100             my ($auth_type, $r) = @_;
101             my ($authen_script, $auth_user);
102             my $debug = $r->dir_config("AuthCookieDebug") || 0;
103            
104             $r->log_error("auth_type " . $auth_type) if ($debug >= 3);
105             return OK unless $r->is_initial_req; # Only authenticate the first internal request
106            
107             if ($r->auth_type ne $auth_type) {
108             # This location requires authentication because we are being called,
109             # but we don't handle this AuthType.
110             $r->log_error("AuthType mismatch: $auth_type =/= ".$r->auth_type) if $debug >= 3;
111             return DECLINED;
112             }
113              
114             # Ok, the AuthType is $auth_type which we handle, what's the authentication
115             # realm's name?
116             my $auth_name = $r->auth_name;
117             $r->log_error("auth_name " . $auth_name) if $debug >= 2;
118             unless ($auth_name) {
119             $r->log_reason("AuthName not set, AuthType=$auth_type", $r->uri);
120             return SERVER_ERROR;
121             }
122              
123             # Get the Cookie header. If there is a session key for this realm, strip
124             # off everything but the value of the cookie.
125             my ($ses_key_cookie) = ($r->header_in("Cookie") || "") =~ /$auth_type\_$auth_name=([^;]+)/;
126             $ses_key_cookie = "" unless defined($ses_key_cookie);
127              
128             $r->log_error("ses_key_cookie " . $ses_key_cookie) if ($debug >= 1);
129             $r->log_error("uri " . $r->uri) if ($debug >= 2);
130              
131             if ($ses_key_cookie) {
132             if ($auth_user = $auth_type->authen_ses_key($r, $ses_key_cookie)) {
133             # We have a valid session key, so we return with an OK value.
134             # Tell the rest of Apache what the authentication method and
135             # user is.
136              
137             $r->connection->auth_type($auth_type);
138             $r->connection->user($auth_user);
139             $r->log_error("user authenticated as $auth_user") if $debug >= 1;
140            
141             # Returning $TICKET to the environment so you can customize webpages
142             # Based on authentication level.
143             $r->subprocess_env('TICKET', $ses_key_cookie);
144              
145             return OK;
146             } else {
147             # There was a session key set, but it's invalid for some reason. So,
148             # remove it from the client now so when the credential data is posted
149             # we act just like it's a new session starting.
150            
151             my $str = $auth_type->cookie_string(
152             request => $r,
153             key => "$auth_type\_$auth_name",
154             value => '',
155             expires => 'Mon, 21-May-1971 00:00:00 GMT'
156             );
157             $r->err_headers_out->add("Set-Cookie" => "$str");
158             $r->log_error("set_cookie " . $r->err_header_out("Set-Cookie")) if $debug >= 2;
159             #$r->subprocess_env('AuthCookieReason', 'Bad Cookie');
160              
161             # Instead of 'Bad Cookie', lets return something more useful.
162             # $ses_key_cookie has a unique value if ERROR, but undef if ! ERROR.
163             $r->subprocess_env('AuthCookieReason', $ses_key_cookie) if $ses_key_cookie =~ /ERROR/;
164             $r->subprocess_env('AuthCookieReason', 'ERROR! Your session has expired, or your login does not have the proper access level for this webpage.') if $ses_key_cookie !~ /ERROR/;
165             }
166             } else {
167             #$r->subprocess_env('AuthCookieReason', 'no_cookie');
168              
169             # Instead of 'no_cookie, let's return something more useful.
170             $r->subprocess_env('AuthCookieReason', 'Please enter your user name and password.');
171             }
172              
173             # They aren't authenticated, and they tried to get a protected
174             # document. Send them the authen form.
175             return $auth_type->login_form;
176             }
177            
178              
179             sub login_form {
180             my $r = Apache->request or die "no request";
181             my $auth_name = $r->auth_name;
182              
183             # There should be a PerlSetVar directive that gives us the URI of
184             # the script to execute for the login form.
185            
186             my $authen_script;
187             unless ($authen_script = $r->dir_config($auth_name . "LoginScript")) {
188             $r->log_reason("PerlSetVar '${auth_name}LoginScript' not set", $r->uri);
189             return SERVER_ERROR;
190             }
191             #$r->log_error("Redirecting to $authen_script");
192             $r->custom_response(FORBIDDEN, $authen_script);
193            
194             return FORBIDDEN;
195             }
196              
197             sub authorize ($$) {
198             my ($auth_type, $r) = @_;
199             my $debug = $r->dir_config("AuthCookieDebug") || 0;
200            
201             return OK unless $r->is_initial_req; #only the first internal request
202            
203             if ($r->auth_type ne $auth_type) {
204             $r->log_error($auth_type . " auth type is " .
205             $r->auth_type) if ($debug >= 3);
206             return DECLINED;
207             }
208            
209             my $reqs_arr = $r->requires or return DECLINED;
210            
211             my $user = $r->connection->user;
212             unless ($user) {
213             # user is either undef or =0 which means the authentication failed
214             $r->log_reason("No user authenticated", $r->uri);
215             return FORBIDDEN;
216             }
217            
218             my ($forbidden);
219             foreach my $req (@$reqs_arr) {
220             my ($requirement, $args) = split /\s+/, $req->{requirement}, 2;
221             $args = '' unless defined $args;
222             $r->log_error("requirement := $requirement, $args") if $debug >= 2;
223            
224             next if $requirement eq 'valid-user';
225             if($requirement eq 'user') {
226             next if $args =~ m/\b$user\b/;
227             $forbidden = 1;
228             next;
229             }
230              
231             # Call a custom method
232             my $ret_val = $auth_type->$requirement($r, $args);
233             $r->log_error("$auth_type->$requirement returned $ret_val") if $debug >= 3;
234             next if $ret_val == OK;
235              
236             # Nothing succeeded, deny access to this user.
237             $forbidden = 1;
238             last;
239             }
240             #return $forbidden ? FORBIDDEN : OK;
241              
242             # Was returning generic Apache FORBIDDEN here. We want to return to login.pl with error message.
243             $r->subprocess_env('AuthCookieReason', 'ERROR! Your login does not have the proper permission for this webpage.') if $forbidden;
244             $r->log_error("AuthCookie, ERROR! Login not in list for this directory using require user ...") if $forbidden;
245             return $auth_type->login_form if $forbidden;
246              
247             return OK;
248             }
249              
250             sub send_cookie {
251             my ($self, $ses_key) = @_;
252             my $r = Apache->request();
253              
254             my ($auth_type, $auth_name) = ($r->auth_type, $r->auth_name);
255             my $cookie = $self->cookie_string( request => $r,
256             key => "$auth_type\_$auth_name",
257             value => $ses_key );
258             $r->err_header_out("Set-Cookie" => $cookie);
259             }
260              
261             sub cookie_string {
262             my $self = shift;
263              
264             # if passed 3 args, we have old-style call.
265             if (scalar(@_) == 3) {
266             carp "deprecated old style call to ".__PACKAGE__."::cookie_string()";
267             my ($r, $key, $value) = @_;
268             return $self->cookie_string(request=>$r, key=>$key, value=>$value);
269             }
270             # otherwise assume named parameters.
271             my %p = @_;
272             for (qw/request key/) {
273             croak "missing required parameter $_" unless defined $p{$_};
274             }
275             # its okay if value is undef here.
276              
277             my $r = $p{request};
278              
279             my $string = sprintf '%s=%s', @p{'key','value'};
280              
281             my $auth_name = $r->auth_name;
282              
283             if (my $expires = $p{expires} || $r->dir_config("${auth_name}Expires")) {
284             $expires = CGI::Util::expires($expires);
285             $string .= "; expires=$expires";
286             }
287              
288             if (my $path = $r->dir_config("${auth_name}Path")) {
289             $string .= "; path=$path";
290             }
291             #$r->log_error("Attribute ${auth_name}Path not set") unless $path;
292              
293             if (my $domain = $r->dir_config("${auth_name}Domain")) {
294             $string .= "; domain=$domain";
295             }
296              
297             $string .= '; secure' if $r->dir_config("${auth_name}Secure");
298              
299             return $string;
300             }
301              
302             sub key {
303             my $self = shift;
304             my $r = Apache->request;
305              
306             my $allcook = ($r->header_in("Cookie") || "");
307             my ($type, $name) = ($r->auth_type, $r->auth_name);
308             return ($allcook =~ /(?:^|\s)${type}_$name=([^;]*)/)[0];
309             }
310              
311             1;
312              
313             __END__