changeset 815:150bf5552d18

merge with main
author Franz Pletz <fpletz AT franz-pletz DOT org>
date Sat, 10 Jun 2006 00:17:19 +0200
parents cc933a874037 (current diff) bc1e460db2c8 (diff)
children 6c9fa6bc2ed5
files MoinMoin/_tests/test_wikiacl.py MoinMoin/multiconfig.py MoinMoin/wikiutil.py
diffstat 5 files changed, 455 insertions(+), 266 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MoinMoin/_tests/test_security.py	Sat Jun 10 00:17:19 2006 +0200
@@ -0,0 +1,243 @@
+# -*- coding: iso-8859-1 -*-
+"""
+    MoinMoin - MoinMoin.security Tests
+
+    @copyright: 2003-2004 by Jürgen Hermann <jh@web.de>
+    @license: GNU GPL, see COPYING for details.
+"""
+
+import unittest
+from MoinMoin._tests import TestConfig
+from MoinMoin import config, security, _tests
+
+acliter = security.ACLStringIterator
+
+class ACLStringIteratorTestCase(unittest.TestCase):
+    
+    def setUp(self):
+        self.config = TestConfig(self.request,
+                                 defaults=['acl_rights_valid', 'acl_rights_before'])
+                
+    def tearDown(self):
+        del self.config
+        
+    def testEmpty(self):
+        """ security: empty acl string raise StopIteration """
+        iter = acliter(self.request.cfg.acl_rights_valid, '')
+        self.failUnlessRaises(StopIteration, iter.next)
+
+    def testWhiteSpace(self):
+        """ security: white space acl string raise StopIteration """
+        iter = acliter(self.request.cfg.acl_rights_valid, '       ')
+        self.failUnlessRaises(StopIteration, iter.next)
+            
+    def testDefault(self):
+        """ security: default meta acl """
+        iter = acliter(self.request.cfg.acl_rights_valid, 'Default Default')
+        for mod, entries, rights in iter:
+            self.assertEqual(entries, ['Default'])
+            self.assertEqual(rights, [])
+                
+    def testEmptyRights(self):
+        """ security: empty rights """    
+        iter = acliter(self.request.cfg.acl_rights_valid, 'WikiName:')
+        mod, entries, rights = iter.next()
+        self.assertEqual(entries, ['WikiName'])
+        self.assertEqual(rights, [])
+
+    def testSingleWikiNameSingleWrite(self):
+        """ security: single wiki name, single right """
+        iter = acliter(self.request.cfg.acl_rights_valid, 'WikiName:read')
+        mod, entries, rights = iter.next()
+        self.assertEqual(entries, ['WikiName'])
+        self.assertEqual(rights, ['read'])
+
+    def testMultipleWikiNameAndRights(self):
+        """ security: multiple wiki names and rights """
+        iter = acliter(self.request.cfg.acl_rights_valid, 'UserOne,UserTwo:read,write')
+        mod, entries, rights = iter.next()
+        self.assertEqual(entries, ['UserOne', 'UserTwo'])
+        self.assertEqual(rights, ['read', 'write'])      
+        
+    def testMultipleEntries(self):
+        """ security: multiple entries """
+        iter = acliter(self.request.cfg.acl_rights_valid, 'UserOne:read,write UserTwo:read All:')
+        mod, entries, rights = iter.next()
+        self.assertEqual(entries, ['UserOne'])
+        self.assertEqual(rights, ['read', 'write'])      
+        mod, entries, rights = iter.next()
+        self.assertEqual(entries, ['UserTwo'])
+        self.assertEqual(rights, ['read'])      
+        mod, entries, rights = iter.next()
+        self.assertEqual(entries, ['All'])
+        self.assertEqual(rights, [])      
+       
+    def testNameWithSpaces(self):
+        """ security: single name with spaces """
+        iter = acliter(self.request.cfg.acl_rights_valid, 'user one:read')
+        mod, entries, rights = iter.next()
+        self.assertEqual(entries, ['user one'])
+        self.assertEqual(rights, ['read'])
+
+    def testMultipleWikiNameAndRights(self):
+        """ security: multiple names with spaces """
+        iter = acliter(self.request.cfg.acl_rights_valid, 'user one,user two:read')
+        mod, entries, rights = iter.next()
+        self.assertEqual(entries, ['user one', 'user two'])
+        self.assertEqual(rights, ['read'])      
+        
+    def testMultipleEntriesWithSpaces(self):
+        """ security: multiple entries with spaces """
+        iter = acliter(self.request.cfg.acl_rights_valid, 'user one:read,write user two:read')
+        mod, entries, rights = iter.next()
+        self.assertEqual(entries, ['user one'])
+        self.assertEqual(rights, ['read', 'write'])      
+        mod, entries, rights = iter.next()
+        self.assertEqual(entries, ['user two'])
+        self.assertEqual(rights, ['read'])      
+         
+    def testMixedNames(self):
+        """ security: mixed wiki names and names with spaces """
+        iter = acliter(self.request.cfg.acl_rights_valid, 'UserOne,user two:read,write user three,UserFour:read')
+        mod, entries, rights = iter.next()
+        self.assertEqual(entries, ['UserOne', 'user two'])
+        self.assertEqual(rights, ['read', 'write'])      
+        mod, entries, rights = iter.next()
+        self.assertEqual(entries, ['user three', 'UserFour'])
+        self.assertEqual(rights, ['read'])      
+
+    def testModifier(self):
+        """ security: acl modifiers """
+        iter = acliter(self.request.cfg.acl_rights_valid, '+UserOne:read -UserTwo:')
+        mod, entries, rights = iter.next()
+        self.assertEqual(mod, '+')
+        self.assertEqual(entries, ['UserOne'])
+        self.assertEqual(rights, ['read'])
+        mod, entries, rights = iter.next()
+        self.assertEqual(mod, '-')
+        self.assertEqual(entries, ['UserTwo'])
+        self.assertEqual(rights, [])
+        
+    def testIgnoreInvalidACL(self):
+        """ security: ignore invalid acl
+
+        The last part of this acl can not be parsed. If it ends with :
+        then it will be parsed as one name with spaces.
+        """
+        iter = acliter(self.request.cfg.acl_rights_valid, 'UserOne:read user two is ignored')
+        mod, entries, rights = iter.next()
+        self.assertEqual(entries, ['UserOne'])
+        self.assertEqual(rights, ['read'])
+        self.failUnlessRaises(StopIteration, iter.next)
+        
+    def testEmptyNamesWithRight(self):
+        """ security: empty names with rights
+
+        The documents does not talk about this case, may() should ignore
+        the rights because there is no entry.
+        """
+        iter = acliter(self.request.cfg.acl_rights_valid, 'UserOne:read :read All:')
+        mod, entries, rights = iter.next()
+        self.assertEqual(entries, ['UserOne'])
+        self.assertEqual(rights, ['read'])
+        mod, entries, rights = iter.next()
+        self.assertEqual(entries, [])
+        self.assertEqual(rights, ['read'])        
+        mod, entries, rights = iter.next()
+        self.assertEqual(entries, ['All'])
+        self.assertEqual(rights, [])
+
+    def testIgnodeInvalidRights(self):
+        """ security: ignore rights not in acl_rights_valid """
+        iter = acliter(self.request.cfg.acl_rights_valid, 'UserOne:read,sing,write,drink,sleep')
+        mod, entries, rights = iter.next()
+        self.assertEqual(rights, ['read', 'write'])        
+
+    def testBadGuy(self):
+        """ security: bad guy may not allowed anything
+
+        This test was failing on the apply acl rights test.
+        """
+        iter = acliter(self.request.cfg.acl_rights_valid, 'UserOne:read,write BadGuy: All:read')
+        mod, entries, rights = iter.next()
+        mod, entries, rights = iter.next()
+        self.assertEqual(entries, ['BadGuy'])
+        self.assertEqual(rights, [])
+
+    def testAllowExtraWhitespace(self):
+        """ security: allow extra white space between entries """
+        iter = acliter(self.request.cfg.acl_rights_valid, 'UserOne,user two:read,write   user three,UserFour:read  All:')
+        mod, entries, rights = iter.next()
+        self.assertEqual(entries, ['UserOne', 'user two'])
+        self.assertEqual(rights, ['read', 'write'])      
+        mod, entries, rights = iter.next()
+        self.assertEqual(entries, ['user three', 'UserFour'])
+        self.assertEqual(rights, ['read'])      
+        mod, entries, rights = iter.next()
+        self.assertEqual(entries, ['All'])
+        self.assertEqual(rights, [])            
+       
+
+class AclTestCase(unittest.TestCase):
+    """ security: testing access control list
+
+    TO DO: test unknown user?
+    """
+    def setUp(self):
+        # Backup user
+        self.config = TestConfig(self.request, defaults=['acl_rights_valid', 'acl_rights_before'])
+        self.savedUser = self.request.user.name
+        
+    def tearDown(self):
+        # Restore user
+        self.request.user.name = self.savedUser
+        del self.config
+        
+    def testApplyACLByUser(self):
+        """ security: applying acl by user name"""
+        # This acl string...
+        acl_rights = [
+            "Admin1,Admin2:read,write,delete,revert,admin  "
+            "Admin3:read,write,admin  "
+            "JoeDoe:read,write  "
+            "name with spaces,another one:read,write  "
+            "CamelCase,extended name:read,write  "
+            "BadGuy:  "
+            "All:read  "
+            ]
+        acl = security.AccessControlList(self.request, acl_rights)
+
+        # Should apply these rights:
+        users = (
+            # user,                 rights
+            # CamelCase names
+            ('Admin1',              ('read', 'write', 'admin', 'revert', 'delete')),
+            ('Admin2',              ('read', 'write', 'admin', 'revert', 'delete')),
+            ('Admin3',              ('read', 'write', 'admin')),
+            ('JoeDoe',              ('read', 'write')),
+            ('SomeGuy',             ('read',)),
+            # Extended names or mix of extended and CamelCase
+            ('name with spaces',    ('read','write',)),
+            ('another one',         ('read','write',)),
+            ('CamelCase',           ('read','write',)),
+            ('extended name',       ('read','write',)),
+            # Blocking bad guys
+            ('BadGuy',              ()),
+            # All other users - every one not mentioned in the acl lines
+            ('All',                 ('read',)),
+            ('Anonymous',           ('read',)),
+            )       
+
+        # Check rights
+        for user, may in users:
+            mayNot = [right for right in self.request.cfg.acl_rights_valid
+                      if right not in may]
+            # User should have these rights...
+            for right in may:
+                self.assert_(acl.may(self.request, user, right),
+                    '"%(user)s" should be allowed to "%(right)s"' % locals())
+            # But NOT these:
+            for right in mayNot:
+                self.failIf(acl.may(self.request, user, right),
+                    '"%(user)s" should NOT be allowed to "%(right)s"' % locals())
+            
--- a/MoinMoin/_tests/test_wikiacl.py	Sat Jun 10 00:14:06 2006 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,243 +0,0 @@
-# -*- coding: iso-8859-1 -*-
-"""
-    MoinMoin - MoinMoin.security Tests
-
-    @copyright: 2003-2004 by Jürgen Hermann <jh@web.de>
-    @license: GNU GPL, see COPYING for details.
-"""
-
-import unittest
-from MoinMoin._tests import TestConfig
-from MoinMoin import config, security, _tests
-
-acliter = security.ACLStringIterator
-
-class ACLStringIteratorTestCase(unittest.TestCase):
-    
-    def setUp(self):
-        self.config = TestConfig(self.request,
-                                 defaults=['acl_rights_valid', 'acl_rights_before'])
-                
-    def tearDown(self):
-        del self.config
-        
-    def testEmpty(self):
-        """ security: empty acl string raise StopIteration """
-        iter = acliter(self.request.cfg.acl_rights_valid, '')
-        self.failUnlessRaises(StopIteration, iter.next)
-
-    def testWhiteSpace(self):
-        """ security: white space acl string raise StopIteration """
-        iter = acliter(self.request.cfg.acl_rights_valid, '       ')
-        self.failUnlessRaises(StopIteration, iter.next)
-            
-    def testDefault(self):
-        """ security: default meta acl """
-        iter = acliter(self.request.cfg.acl_rights_valid, 'Default Default')
-        for mod, entries, rights in iter:
-            self.assertEqual(entries, ['Default'])
-            self.assertEqual(rights, [])
-                
-    def testEmptyRights(self):
-        """ security: empty rights """    
-        iter = acliter(self.request.cfg.acl_rights_valid, 'WikiName:')
-        mod, entries, rights = iter.next()
-        self.assertEqual(entries, ['WikiName'])
-        self.assertEqual(rights, [])
-
-    def testSingleWikiNameSingleWrite(self):
-        """ security: single wiki name, single right """
-        iter = acliter(self.request.cfg.acl_rights_valid, 'WikiName:read')
-        mod, entries, rights = iter.next()
-        self.assertEqual(entries, ['WikiName'])
-        self.assertEqual(rights, ['read'])
-
-    def testMultipleWikiNameAndRights(self):
-        """ security: multiple wiki names and rights """
-        iter = acliter(self.request.cfg.acl_rights_valid, 'UserOne,UserTwo:read,write')
-        mod, entries, rights = iter.next()
-        self.assertEqual(entries, ['UserOne', 'UserTwo'])
-        self.assertEqual(rights, ['read', 'write'])      
-        
-    def testMultipleEntries(self):
-        """ security: multiple entries """
-        iter = acliter(self.request.cfg.acl_rights_valid, 'UserOne:read,write UserTwo:read All:')
-        mod, entries, rights = iter.next()
-        self.assertEqual(entries, ['UserOne'])
-        self.assertEqual(rights, ['read', 'write'])      
-        mod, entries, rights = iter.next()
-        self.assertEqual(entries, ['UserTwo'])
-        self.assertEqual(rights, ['read'])      
-        mod, entries, rights = iter.next()
-        self.assertEqual(entries, ['All'])
-        self.assertEqual(rights, [])      
-       
-    def testNameWithSpaces(self):
-        """ security: single name with spaces """
-        iter = acliter(self.request.cfg.acl_rights_valid, 'user one:read')
-        mod, entries, rights = iter.next()
-        self.assertEqual(entries, ['user one'])
-        self.assertEqual(rights, ['read'])
-
-    def testMultipleWikiNameAndRights(self):
-        """ security: multiple names with spaces """
-        iter = acliter(self.request.cfg.acl_rights_valid, 'user one,user two:read')
-        mod, entries, rights = iter.next()
-        self.assertEqual(entries, ['user one', 'user two'])
-        self.assertEqual(rights, ['read'])      
-        
-    def testMultipleEntriesWithSpaces(self):
-        """ security: multiple entries with spaces """
-        iter = acliter(self.request.cfg.acl_rights_valid, 'user one:read,write user two:read')
-        mod, entries, rights = iter.next()
-        self.assertEqual(entries, ['user one'])
-        self.assertEqual(rights, ['read', 'write'])      
-        mod, entries, rights = iter.next()
-        self.assertEqual(entries, ['user two'])
-        self.assertEqual(rights, ['read'])      
-         
-    def testMixedNames(self):
-        """ security: mixed wiki names and names with spaces """
-        iter = acliter(self.request.cfg.acl_rights_valid, 'UserOne,user two:read,write user three,UserFour:read')
-        mod, entries, rights = iter.next()
-        self.assertEqual(entries, ['UserOne', 'user two'])
-        self.assertEqual(rights, ['read', 'write'])      
-        mod, entries, rights = iter.next()
-        self.assertEqual(entries, ['user three', 'UserFour'])
-        self.assertEqual(rights, ['read'])      
-
-    def testModifier(self):
-        """ security: acl modifiers """
-        iter = acliter(self.request.cfg.acl_rights_valid, '+UserOne:read -UserTwo:')
-        mod, entries, rights = iter.next()
-        self.assertEqual(mod, '+')
-        self.assertEqual(entries, ['UserOne'])
-        self.assertEqual(rights, ['read'])
-        mod, entries, rights = iter.next()
-        self.assertEqual(mod, '-')
-        self.assertEqual(entries, ['UserTwo'])
-        self.assertEqual(rights, [])
-        
-    def testIgnoreInvalidACL(self):
-        """ security: ignore invalid acl
-
-        The last part of this acl can not be parsed. If it ends with :
-        then it will be parsed as one name with spaces.
-        """
-        iter = acliter(self.request.cfg.acl_rights_valid, 'UserOne:read user two is ignored')
-        mod, entries, rights = iter.next()
-        self.assertEqual(entries, ['UserOne'])
-        self.assertEqual(rights, ['read'])
-        self.failUnlessRaises(StopIteration, iter.next)
-        
-    def testEmptyNamesWithRight(self):
-        """ security: empty names with rights
-
-        The documents does not talk about this case, may() should ignore
-        the rights because there is no entry.
-        """
-        iter = acliter(self.request.cfg.acl_rights_valid, 'UserOne:read :read All:')
-        mod, entries, rights = iter.next()
-        self.assertEqual(entries, ['UserOne'])
-        self.assertEqual(rights, ['read'])
-        mod, entries, rights = iter.next()
-        self.assertEqual(entries, [])
-        self.assertEqual(rights, ['read'])        
-        mod, entries, rights = iter.next()
-        self.assertEqual(entries, ['All'])
-        self.assertEqual(rights, [])
-
-    def testIgnodeInvalidRights(self):
-        """ security: ignore rights not in acl_rights_valid """
-        iter = acliter(self.request.cfg.acl_rights_valid, 'UserOne:read,sing,write,drink,sleep')
-        mod, entries, rights = iter.next()
-        self.assertEqual(rights, ['read', 'write'])        
-
-    def testBadGuy(self):
-        """ security: bad guy may not allowed anything
-
-        This test was failing on the apply acl rights test.
-        """
-        iter = acliter(self.request.cfg.acl_rights_valid, 'UserOne:read,write BadGuy: All:read')
-        mod, entries, rights = iter.next()
-        mod, entries, rights = iter.next()
-        self.assertEqual(entries, ['BadGuy'])
-        self.assertEqual(rights, [])
-
-    def testAllowExtraWhitespace(self):
-        """ security: allow extra white space between entries """
-        iter = acliter(self.request.cfg.acl_rights_valid, 'UserOne,user two:read,write   user three,UserFour:read  All:')
-        mod, entries, rights = iter.next()
-        self.assertEqual(entries, ['UserOne', 'user two'])
-        self.assertEqual(rights, ['read', 'write'])      
-        mod, entries, rights = iter.next()
-        self.assertEqual(entries, ['user three', 'UserFour'])
-        self.assertEqual(rights, ['read'])      
-        mod, entries, rights = iter.next()
-        self.assertEqual(entries, ['All'])
-        self.assertEqual(rights, [])            
-       
-
-class AclTestCase(unittest.TestCase):
-    """ security: testing access control list
-
-    TO DO: test unknown user?
-    """
-    def setUp(self):
-        # Backup user
-        self.config = TestConfig(self.request, defaults=['acl_rights_valid', 'acl_rights_before'])
-        self.savedUser = self.request.user.name
-        
-    def tearDown(self):
-        # Restore user
-        self.request.user.name = self.savedUser
-        del self.config
-        
-    def testApplyACLByUser(self):
-        """ security: applying acl by user name"""
-        # This acl string...
-        acl_rights = [
-            "Admin1,Admin2:read,write,delete,revert,admin  "
-            "Admin3:read,write,admin  "
-            "JoeDoe:read,write  "
-            "name with spaces,another one:read,write  "
-            "CamelCase,extended name:read,write  "
-            "BadGuy:  "
-            "All:read  "
-            ]
-        acl = security.AccessControlList(self.request, acl_rights)
-
-        # Should apply these rights:
-        users = (
-            # user,                 rights
-            # CamelCase names
-            ('Admin1',              ('read', 'write', 'admin', 'revert', 'delete')),
-            ('Admin2',              ('read', 'write', 'admin', 'revert', 'delete')),
-            ('Admin3',              ('read', 'write', 'admin')),
-            ('JoeDoe',              ('read', 'write')),
-            ('SomeGuy',             ('read',)),
-            # Extended names or mix of extended and CamelCase
-            ('name with spaces',    ('read','write',)),
-            ('another one',         ('read','write',)),
-            ('CamelCase',           ('read','write',)),
-            ('extended name',       ('read','write',)),
-            # Blocking bad guys
-            ('BadGuy',              ()),
-            # All other users - every one not mentioned in the acl lines
-            ('All',                 ('read',)),
-            ('Anonymous',           ('read',)),
-            )       
-
-        # Check rights
-        for user, may in users:
-            mayNot = [right for right in self.request.cfg.acl_rights_valid
-                      if right not in may]
-            # User should have these rights...
-            for right in may:
-                self.assert_(acl.may(self.request, user, right),
-                    '"%(user)s" should be allowed to "%(right)s"' % locals())
-            # But NOT these:
-            for right in mayNot:
-                self.failIf(acl.may(self.request, user, right),
-                    '"%(user)s" should NOT be allowed to "%(right)s"' % locals())
-            
--- a/MoinMoin/auth/__init__.py	Sat Jun 10 00:14:06 2006 +0200
+++ b/MoinMoin/auth/__init__.py	Sat Jun 10 00:17:19 2006 +0200
@@ -34,7 +34,11 @@
     must not get changed by the user using the UserPreferences form.
     It also gives a kw arg "auth_method" that tells the name of the auth
     method that authentified the user.
-    
+
+    TODO: check against other cookie work (see wiki)  
+          kill unsecure MOIN_ID cookie?
+          reduce amount of XXX
+          
     @copyright: 2005-2006 Bastian Blank, Florian Festi, Thomas Waldmann
     @copyright: 2005-2006 MoinMoin:AlexanderSchremmer
     @license: GNU GPL, see COPYING for details.
@@ -43,6 +47,10 @@
 import time, Cookie
 from MoinMoin import user
 
+# cookie names
+MOIN_SESSION = 'MOIN_SESSION'
+MOIN_ID = 'MOIN_ID'
+
 def log(request, **kw):
     """ just log the call, do nothing else """
     username = kw.get('name')
@@ -53,27 +61,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,9 +93,13 @@
      > 0    n hours, or forever if user checked 'remember_me'
      < 0    -n hours, ignoring user 'remember_me' setting
     """
+    if cookie_string is 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
+    forever = 10 * 365 * 24 * 3600 # 10 years
     now = time.time()
     if not lifetime:
         maxage = forever
@@ -100,26 +112,40 @@
         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, hmac
+    cfg = request.cfg
+    cookie_name = MOIN_SESSION
+    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.cookie_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
-    Deleted cookie should have Max-Age=0. We also have expires
-    attribute, which is probably needed for older browsers.
+    Deleted cookie should have Max-Age=0. We also have expires attribute,
+    which is probably needed for older browsers.
 
     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 +158,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:
@@ -146,8 +182,8 @@
     except Cookie.CookieError:
         # ignore invalid cookies, else user can't relogin
         cookie = None
-    if cookie and cookie.has_key('MOIN_ID'):
-        u = user.User(request, id=cookie['MOIN_ID'].value,
+    if cookie and cookie.has_key(MOIN_ID):
+        u = user.User(request, id=cookie[MOIN_ID].value,
                       auth_method='moin_cookie', auth_attribs=())
 
         if logout:
@@ -164,6 +200,96 @@
     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, base64
+    
+    username = kw.get('name')
+    login = kw.get('login')
+    logout = kw.get('logout')
+    user_obj = kw.get('user_obj')
+
+    cfg = request.cfg
+    verbose = False
+    if hasattr(cfg, 'moin_session_verbose'):
+        verbose = cfg.moin_session_verbose
+
+    cookie_name = MOIN_SESSION
+    
+    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 is not 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 is not 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.cookie_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 the 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] = base64.decodestring(value) # assuming all values are base64 encoded
+    # XXX Should check expiry from cookie
+    # XXX Should name be in auth_attribs?
+    u = user.User(request,
+                  id=params['id'],
+                  auth_username=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
@@ -386,7 +512,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 +529,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):
@@ -521,3 +647,64 @@
                 return user, True # True to get other methods called, too
         return user_obj, True # continue with next method in auth list
 
+def mysql_group(request, **kw):
+    """ Authorize via MySQL group DB.
+    
+    We require an already-authenticated user_obj.
+    We don't worry about the type of request (login, logout, neither).
+    We just check user is part of authorized group.
+    """
+    import MySQLdb
+    
+    username = kw.get('name')
+#    login = kw.get('login')
+#    logout = kw.get('logout')
+    user_obj = kw.get('user_obj')
+
+    cfg = request.cfg
+    verbose = False
+
+    if hasattr(cfg, 'mysql_group_verbose'):
+        verbose = cfg.mysql_group_verbose
+    
+    if verbose: request.log("auth.mysql_group: name=%s user_obj=%r" % (username, user_obj))
+
+    # Has any other method successfully authenticated?
+    if user_obj is not None and user_obj.valid:
+        # Yes - we can do stuff!
+        if verbose: request.log("mysql_group got valid user from previous auth method, trying authz...")
+        if verbose: request.log("mysql_group got auth_username %s." % user_obj.auth_username)
+
+        # XXX Check auth_username for dodgy chars (should be none as it is authenticated, but...)
+
+        # OK, now check mysql!
+        try:
+            m = MySQLdb.connect(host=cfg.mysql_group_dbhost,
+                                user=cfg.mysql_group_dbuser,
+                                passwd=cfg.mysql_group_dbpass,
+                                db=cfg.mysql_group_dbname,
+                                )
+        except:
+            import sys
+            import traceback
+            info = sys.exc_info()
+            request.log("mysql_group: authorization failed due to exception connecting to DB, traceback follows...")
+            request.log(''.join(traceback.format_exception(*info)))
+            return None, False
+        
+        c = m.cursor()
+        c.execute(cfg.mysql_group_query, user_obj.auth_username)
+        results = c.fetchall()
+        if results:
+            # Checked out OK
+            if verbose: request.log("mysql_group got %d results -- authorized!" % len(results))
+            return user_obj, True # we make continuing possible, e.g. for smbmount
+        else:
+            if verbose: request.log("mysql_group did not get match from DB -- not authorized")
+            return None, False
+    else:
+        # No other method succeeded, so we cannot authorize -- must fail
+        if verbose: request.log("mysql_group did not get valid user from previous auth method, cannot authorize")
+        return None, False
+
+
--- a/MoinMoin/multiconfig.py	Sat Jun 10 00:14:06 2006 +0200
+++ b/MoinMoin/multiconfig.py	Sat Jun 10 00:17:19 2006 +0200
@@ -198,9 +198,11 @@
     chart_options = None
     
     config_check_enabled = 0
+
     cookie_domain = None # use '.domain.tld" for a farm with hosts in that domain
     cookie_path = None   # use '/wikifarm" for a farm with pathes below that path
     cookie_lifetime = 12 # 12 hours from now
+    cookie_secret = '1234' # secret value for crypting session cookie - you should change this :)
     
     data_dir = './data/'
     data_underlay_dir = './underlay/'
--- a/MoinMoin/wikiutil.py	Sat Jun 10 00:14:06 2006 +0200
+++ b/MoinMoin/wikiutil.py	Sat Jun 10 00:17:19 2006 +0200
@@ -812,7 +812,7 @@
             self.parse_filename(filename)
     
     def parse_filename(self, filename):
-        mtype, encoding = mimetypes.guess_type()
+        mtype, encoding = mimetypes.guess_type(filename)
         if mtype is None:
             mtype = 'application/octet-stream'
         self.parse_mimetype(mtype)