changeset 767:607678fc0524

merge session changeset (thanks to Nick Phillips)
author Thomas Waldmann <tw AT waldmann-edv DOT de>
date Thu, 08 Jun 2006 11:23:53 +0200
parents 28408e50205a (current diff) a6e0252a3052 (diff)
children a463b24b01e3
files MoinMoin/auth.py
diffstat 1 files changed, 141 insertions(+), 17 deletions(-) [+]
line wrap: on
line diff
--- a/MoinMoin/auth.py	Thu Jun 08 11:04:43 2006 +0200
+++ b/MoinMoin/auth.py	Thu Jun 08 11:23:53 2006 +0200
@@ -53,27 +53,27 @@
     request.log("auth.log: name=%s login=%r logout=%r user_obj=%r" % (username, login, logout, user_obj))
     return user_obj, True
 
-# some cookie functions used by moin_cookie auth
-def makeCookie(request, moin_id, maxage, expires):
-    """ calculate a MOIN_ID cookie """
+# some cookie functions used by moin_cookie and moin_session auth
+def makeCookie(request, cookie_name, cookie_string, maxage, expires):
+    """ create an appropriate cookie """
     c = Cookie.SimpleCookie()
     cfg = request.cfg
-    c['MOIN_ID'] = moin_id
-    c['MOIN_ID']['max-age'] = maxage
+    c[cookie_name] = cookie_string
+    c[cookie_name]['max-age'] = maxage
     if cfg.cookie_domain:
-        c['MOIN_ID']['domain'] = cfg.cookie_domain
+        c[cookie_name]['domain'] = cfg.cookie_domain
     if cfg.cookie_path:
-        c['MOIN_ID']['path'] = cfg.cookie_path
+        c[cookie_name]['path'] = cfg.cookie_path
     else:
         path = request.getScriptname()
         if not path:
             path = '/'
-        c['MOIN_ID']['path'] = path
+        c[cookie_name]['path'] = path
     # Set expires for older clients
-    c['MOIN_ID']['expires'] = request.httpDate(when=expires, rfc='850')        
+    c[cookie_name]['expires'] = request.httpDate(when=expires, rfc='850')        
     return c.output()
 
-def setCookie(request, u):
+def setCookie(request, u, cookie_name='MOIN_ID', cookie_string=None):
     """ Set cookie for the user obj u
     
     cfg.cookie_lifetime and the user 'remember_me' setting set the
@@ -85,6 +85,10 @@
      > 0    n hours, or forever if user checked 'remember_me'
      < 0    -n hours, ignoring user 'remember_me' setting
     """
+    if cookie_string == None:
+        # For moin_cookie
+        cookie_string = u.id
+    
     # Calculate cookie maxage and expires
     lifetime = int(request.cfg.cookie_lifetime) * 3600 
     forever = 10*365*24*3600 # 10 years
@@ -100,13 +104,30 @@
         maxage = (-lifetime)
     expires = now + maxage
     
-    cookie = makeCookie(request, u.id, maxage, expires)
+    cookie = makeCookie(request, cookie_name, cookie_string, maxage, expires)
     # Set cookie
     request.setHttpHeader(cookie)
     # IMPORTANT: Prevent caching of current page and cookie
     request.disableHttpCaching()
 
-def deleteCookie(request):
+def setSessionCookie(request, u):
+    """ Set moin_session cookie for user obj u
+    """
+    import base64
+    import hmac
+    cfg = request.cfg
+    cookie_name = 'MOIN_ID'
+    if hasattr(cfg, 'moin_session_cookie_name'):
+        cookie_name = cfg.moin_session_cookie_name
+    enc_username = base64.encodestring(u.auth_username)
+    enc_id = base64.encodestring(u.id)
+    # XXX - should include expiry!
+    cookie_body = "username=%s:id=%s" % (enc_username, enc_id)
+    cookie_hmac = hmac.new(cfg.moin_session_secret, cookie_body).hexdigest()
+    cookie_string = ':'.join([cookie_hmac, cookie_body])
+    setCookie(request, u, cookie_name, cookie_string)
+
+def deleteCookie(request, cookie_name='MOIN_ID'):
     """ Delete the user cookie by sending expired cookie with null value
 
     According to http://www.cse.ohio-state.edu/cgi-bin/rfc/rfc2109.html#sec-4.2.2
@@ -115,11 +136,11 @@
 
     Finally, delete the saved cookie and create a new user based on the new settings.
     """
-    moin_id = ''
+    cookie_string = ''
     maxage = 0
     # Set expires to one year ago for older clients
     expires = time.time() - (3600 * 24 * 365) # 1 year ago
-    cookie = makeCookie(request, moin_id, maxage, expires) 
+    cookie = makeCookie(request, cookie_name, cookie_string, maxage, expires) 
     # Set cookie
     request.setHttpHeader(cookie)
     # IMPORTANT: Prevent caching of current page and cookie        
@@ -132,13 +153,23 @@
     login = kw.get('login')
     logout = kw.get('logout')
     user_obj = kw.get('user_obj')
+
+    cfg = request.cfg
+    verbose = False
+    if hasattr(cfg,'moin_cookie_verbose'):
+        verbose = cfg.moin_cookie_verbose
+    
     #request.log("auth.moin_cookie: name=%s login=%r logout=%r user_obj=%r" % (username, login, logout, user_obj))
+
     if login:
+        if verbose: request.log("moin_cookie performing login action")
         u = user.User(request, name=username, password=password,
                       auth_method='login_userpassword')
         if u.valid:
+            if verbose: request.log("moin_cookie got valid user...")
             setCookie(request, u)
             return u, True # we make continuing possible, e.g. for smbmount
+        if verbose: request.log("moin_cookie not valid, previous valid=%d." % user_obj.valid)
         return user_obj, True
 
     try:
@@ -164,6 +195,99 @@
     return user_obj, True
 
 
+def moin_session(request, **kw):
+    """
+    Authenticate via cookie.
+    We don't handle initial logins (except to set the appropriate
+    cookie), just ongoing sessions, and logout. Use another method
+    for initial login.
+    """
+    import hmac
+    import base64
+    
+    username = kw.get('name')
+    login = kw.get('login')
+    logout = kw.get('logout')
+    user_obj = kw.get('user_obj')
+
+    cfg = request.cfg
+    verbose = False
+    cookie_name = 'MOIN_ID'
+    if hasattr(cfg,'moin_session_verbose'):
+        verbose = cfg.moin_session_verbose
+    if hasattr(cfg, 'moin_session_cookie_name'):
+        cookie_name = cfg.moin_session_cookie_name
+    
+    if verbose: request.log("auth.moin_session: name=%s login=%r logout=%r user_obj=%r" % (username, login, logout, user_obj))
+
+    if login:
+        if verbose: request.log("moin_session performing login action")
+
+        # Has any other method successfully authenticated?
+        if user_obj != None and user_obj.valid:
+            # Yes - set up session cookie
+            if verbose: request.log("moin_session got valid user from previous auth method, setting cookie...")
+            if verbose: request.log("moin_session got auth_username %s." % user_obj.auth_username)
+            setSessionCookie(request, user_obj)
+            return user_obj, True # we make continuing possible, e.g. for smbmount
+        else:
+            # No other method succeeded, so allow continuation...
+            # XXX Cookie clear here???
+            if verbose: request.log("moin_session did not get valid user from previous auth method, doing nothing")
+            return user_obj, True
+
+    try:
+        if verbose: request.log("trying to get cookie...")
+        cookie = Cookie.SimpleCookie(request.saved_cookie)
+    except Cookie.CookieError:
+        # ignore invalid cookies, else user can't relogin
+        if verbose: request.log("caught Cookie.CookieError")
+        cookie = None
+
+    if not (cookie != None and cookie.has_key(cookie_name)):
+        # No valid cookie
+        if verbose: request.log("either no cookie or no %s key" % cookie_name)
+        return user_obj, True
+    
+    try:
+        cookie_hmac, cookie_body = cookie[cookie_name].value.split(':',1)
+    except ValueError:
+        # Invalid cookie
+        if verbose: request.log("invalid cookie format: (%s)" % cookie[cookie_name].value)
+        return user_obj, True
+    
+    if cookie_hmac != hmac.new(cfg.moin_session_secret, cookie_body).hexdigest():
+        # Invalid cookie
+        # XXX Cookie clear here???
+        if verbose: request.log("cookie recovered had invalid hmac")
+        return user_obj, True
+        
+    # We can trust cookie
+    if verbose: request.log("Cookie OK, authenticated.")
+    params = { 'username': '', 'id': '' }
+    cookie_pairs = cookie_body.split(":")
+    for key, value in [pair.split("=",1) for pair in cookie_pairs]:
+        params[key] = value
+    # XXX Should check expiry from cookie
+    # XXX Should name be in auth_attribs?
+    u = user.User(request,
+                  id=base64.decodestring(params['id']),
+                  auth_username=base64.decodestring(params['username']),
+                  auth_method='moin_session',
+                  auth_attribs=()
+                  )
+        
+    if logout:
+        if verbose: request.log("Logout requested, setting u invalid and 'deleting' cookie")
+        u.valid = 0 # just make user invalid, but remember him
+        deleteCookie(request, cookie_name)
+        return u, True # we return a invalidated user object, so that
+                       # following auth methods can get the name of
+                       # the user who logged out
+    setSessionCookie(request, u) # refreshes cookie lifetime
+    return u, True # use True to get other methods called, too
+
+
 def http(request, **kw):
     """ authenticate via http basic/digest/ntlm auth """
     from MoinMoin.request import TWISTED, CLI
@@ -329,7 +453,7 @@
 
     cfg = request.cfg
     verbose = cfg.ldap_verbose
-    
+
     if verbose: request.log("got name=%s login=%r logout=%r" % (username, login, logout))
     
     # we just intercept login and logout for ldap, other requests have to be
@@ -386,7 +510,7 @@
                 aliasname = sn
             aliasname = aliasname.decode(coding)
             
-            u = user.User(request, auth_username=username, password=password, auth_method='ldap', auth_attribs=('name', 'password', 'email', 'mailto_author',))
+            u = user.User(request, auth_username=username, password="{SHA}NotStored", auth_method='ldap', auth_attribs=('name', 'password', 'email', 'mailto_author',))
             u.name = username
             u.aliasname = aliasname
             u.email = email
@@ -403,7 +527,7 @@
 
     if u:
         u.create_or_update(True)
-    return user_obj, True # moin_cookie has to set the cookie and return the user obj
+    return u, True # moin_cookie has to set the cookie and return the user obj
 
 
 def interwiki(request, **kw):