Torsten,
Many thanks for the excellent information, I will ponder that.
More below, but one more question here :
Where does $r->internal_redirect "live" (in which package) ?
I am having trouble finding it.
Torsten Foertsch wrote:
On Sun 12 Oct 2008, André Warnier wrote:
In an attempt at being clever, I put the following code in the
handler :
unless ($r->is_initial_req) {
if (defined $r->prev) {
# we are in a subrequest. Just copy user from main
request. $r->user( $r->prev->user );
}
# Also disable authorization phase
$r->set_handlers(PerlAuthzHandler => undef);
return OK;
}
You have to distinguish between subrequests and internal redirects. The
former result from $r->lookup_uri, $r->lookup_file or similar (there
are a few more such functions in the C API) and internal redirects that
result from $r->internal_redirect (internal_fast_redirect() is not as
the name suggests an internal redirect but simply overrides the current
request). Subrequests are used for example by mod_rewrite, mod_include,
mod_negotiation to look for some characteristics of a document and
perhaps pull it in (run() it). Internal redirects are used in mod_cgi
when the CGI output indicates a status 200 (HTTP_OK) but also contains
a Location header. But the main usage of internal redirects is the
ErrorDocument.
Now, is_initial_req() checks if the current $r is the result of a
subrequest or the result of a internal redirect and returns false if
so. prev() returns the parent request if the current $r is the result
of an internal redirect and main() returns the main request if the
current $r is a subrequest. So, your code checks only for internal
redirects (ErrorDocument).
Now, have a look at httpd-2.x.y/server/request.c around line 170. You'll
see this piece of code:
/* Skip authn/authz if the parent or prior request passed the
* authn/authz,
* and that configuration didn't change (this requires
* optimized _walk()
* functions in map_to_storage that use the same merge results given
* identical input.) If the config changes, we must re-auth.
*/
if (r->main && (r->main->per_dir_config == r->per_dir_config)) {
r->user = r->main->user;
r->ap_auth_type = r->main->ap_auth_type;
}
else if (r->prev && (r->prev->per_dir_config == r->per_dir_config))
{
r->user = r->prev->user;
r->ap_auth_type = r->prev->ap_auth_type;
}
else {
switch (ap_satisfies(r)) {
case SATISFY_ALL:
case SATISFY_NOSPEC:
if ((access_status = ap_run_access_checker(r)) != 0) {
return decl_die(access_status, "check access", r);
...
Ok, I get it.
I have a little question related to the above, but not very urgent : why
the check on the configuration change ? what can change between a
request and a sub-request (or internal redirect) ?
You see, you are not the first who had had the idea of reusing an
established identity.
I did not think I would be.
If your subreq or internal redirect hits the same
Location or Directory container the AAA phases are completely skipped.
Maybe this is enough optimization if you shift a few directives around
in your httpd.conf.
I don't think so, because this is a really specific authentication
method, for a special case.
And I don't think that Apache will skip the mod_perl AAA phases, will it ?
If not, the code above shows you how to do it. But you must ask yourself
if it really is valid to reuse the identity. I believe, you can safely
inherit the identity from $r->main or $r->prev but you must not skip
the other 2 A's. If you can't it would mean you have one realm of
identities for the main request and another for the subreq. That, I'd
say, is a configuration error.
As a first stage of the AAA, for some Locations, there is a filtering on
the remote IP of the caller. Some IP's get an "automatic" user-id,
which can vary according to the IP. In some cases, this is authoritative
(no access unless you have the right IP), in some cases not (you get a
second chance). Some Locations don't have the IP filter, they always
get the second chance below. This IP filter is implemented as a
PerlAccessHandler. This is the main reason for trying to optimise,
because it is expensive : the IP of the caller must be compared to
several ranges of IP, not necessarily matching regular subnets.
The second step is a PerlAuthenHandler, which can re-direct to a login
page.
Then there is a PerlAuthenzHandler to check if this user is allowed to
access that resource.
It also combines with SSO, with some URL rewriting, and with trying to
control access to a Tomcat application behind the Apache.
The back-end for the authentication is a special DB system, whose access
for that is rather heavy, but required.
On the positive side, this is for a limited range of well-known
applications, for a limited public and for a reasonable number of
expected transactions/s.
So I am trying to wring out the optimisations I can, without going too far.
I started this module wanting to keep it "clean and lean and mean", but
as I discover more and more twists, it is getting to look like the
classical spaghetti bowl..
I am also, but on a separate thread, looking at tying this AAA stuff to
the $r->connection (with notes()).
I'm also having fun doing this, it's interesting.
The idea being that if we are in a sub-request, there is no point in
authenticating/authorizing it again, since the main request should
already do that, right ? Optimisation..
Now the above works very nicely, except in the case where, before
this handler gets called, there is an intervention by mod_rewrite. It
seems as if mod_rewrite makes the above fail, even when the rewrite
condition does not apply and the URL is considered as a
"pass-through".
I suspect that it is because mod_rewrite, no matter what, invoques
the original (or modified) URL as a sub-request of the original
request. This would cause the above to fail, because in such a case,
the above conditional code would be invoked, but there is no
$r->prev->user to be copied.
mod_rewrite doesn't make subrequests if not asked to. I know only of 2
ways to have mod_rewrite perform a subreq: %{LA-U:variable}
and %{LA-F:variable} in a RewriteCond.
This :
http://perl.apache.org/docs/2.0/api/Apache2/RequestUtil.html#C_is_initial_req_
may be missing ".. or an internal redirect" in a couple of places.