[Zope] Re: Z2.log user name problem

Paul Tiemann pault@center7.com
20 Sep 2002 11:26:04 -0600


> I'm using out-of-the-box user authentication with 
> CookieCrumbler.  Everything seems fine except that 
> the identified user in the Z2.log file is wrong.

I've got a "fix" for your problem.  It wouldn't have
been my first choice to do it the way I did, but it
works, and it's quicker than my other options were...

(Some history)
I had the same problem with cookie-based authentication.
The root of the problem lies in the log() method of the 
file ZServer/medusa/http_server.py.  That method uses the
'Authorization' header of the request to determine the
name of the user.  Due to the architecture of the internal
objects used to represent the request at this level (not
the "REQUEST" we're usually used to using, but another
lower-level request object) the 'Authorization' header
is a read-only field.  I believe it's the __get_item__
and __set_item__ methods that are custom for that request
object, so that all attempts to "set" the Authorization
HTTP header will just be dumped into the list of response 
headers.  Since I could see no other way to get around the
problem by making a change in the 'CookieCrumbler', or in my 
case 'LDAPUserFolder' products, I decided to just fix the
problem by monkey-patching the log() method in the 
http_server.py file itself.  

To do that, you can do something like this:

1) Add two lines like these to the top of http_server.py
   for the import you'll need below to make parsing
   the cookies easier.

# PAUL DID THIS SHAMEFUL THING
from ZPublisher.HTTPRequest import parse_cookie

2) Down near line 290, you have the part that determines
   the name that will go to the Z2.log file.  Here, you 
   see 'name' being set to 'Anonymous', then there is 
   an 'if auth is not None:' block which determines 
   the name from the "Authorization" header.  In my case,
   I added an 'else:' block below the if which has this
   dirty patch of code:

try:
  auth_cookie_name = "my_auth" # probably '__ac'?
  cookie = None
  try:
    cookies = {}
    header_value = self.get_header("Cookie")
    if header_value:
      parse_cookie(header_value, cookies)
      cookie = cookies.get(auth_cookie_name, None)
  except:
    name = "Anonymous"

  if cookie is not None: 
    cookie = unquote( cookie )
    try:
      cookie = base64.decodestring( cookie )
      name, password = tuple( cookie.split( ':', 1 ) )
    except: name = "Unknown (bad auth cookie)"
except:
  name = "Failure!"

Note that the way I solved the problem is "brittle" because
should you ever change your auth_cookie_name, and forget 
to change it in http_server.py as well, you'll stop
seeing correct values in your logs.  

One more important note:  Since you have more than one
acl_users, you might want to put the 'Cookie' based 
name-fetch code before the 'Authorization' header based
stuff.  It would be possible for you to log in as admin,
then use the cookie-based login page to authenticate as
a normal user, in which case your browser will be sending
the 'Authorization' header, as well as the cookie, and 
that would cause the standard acl_users username to be 
logged...

> I assume there is some method which needs to be called 
> to update the username for logging purposes when the 
> login is upgraded, but I've been unable to find it.  Or 
> is this a problem caused by the lack of a CookieCrumbler 
> in the root folder....

I tried to find a method like the one you're describing, and
one alternative to my solution would have been to add that
kind of facility to the low-level request object, and make 
different changes to http_server.py so it would get the 
user's name from that new variable instead of from the 
Authorization header, but I couldn't find it, and it 
didn't appear that CookieCrumbler was doing that either...

If there is an alternative solution that is more desirable
than my own, I would love to hear about it...  Maybe someone
on the list knows another way...

Good luck, I hope I've helped,
;) Paul