* looking for arch@arch.thinkmo.de--2003-archives/moin--main--1.5--patch-361 to compare with
* comparing to arch@arch.thinkmo.de--2003-archives/moin--main--1.5--patch-361
M  MoinMoin/auth.py
M  MoinMoin/multiconfig.py
M  MoinMoin/request.py
M  MoinMoin/user.py
M  MoinMoin/userform.py
A  MoinMoin/util/securitystring.py

* modified files

--- orig/MoinMoin/auth.py
+++ mod/MoinMoin/auth.py
@@ -45,6 +45,7 @@
 
 import Cookie
 from MoinMoin import user
+from MoinMoin.util import securitystring
 
 def moin_cookie(request, **kw):
     """ authenticate via the MOIN_ID cookie """
@@ -54,17 +55,17 @@
         u = user.User(request, name=name, password=password,
                       auth_method='login_userpassword')
         if u.valid:
+            # Update the User security_string,
+            #  If he don't have security_string.
+            try:
+                u.security_string
+            except AttributeError:
+                u.security_string = securitystring.gen(30)
+                u.save()
             request.user = u # needed by setCookie
             request.setCookie()
             return u, False
         return None, True
-
-    if kw.get('logout'):
-        # clear the cookie in the browser and locally. Does not
-        # check if we have a valid user logged, just make sure we
-        # don't have one after this call.
-        request.deleteCookie()
-        return None, True
     
     try:
         cookie = Cookie.SimpleCookie(request.saved_cookie)
@@ -72,23 +73,59 @@
         # 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,
+        # Use security_string to handle the Cookie.
+        u = user.User(request, hmac_uid_string=cookie['MOIN_ID'].value,
                       auth_method='moin_cookie', auth_attribs=())
         if u.valid:
-            return u, False
+            if kw.get('logout'):
+                # FrankieChow: Why Does not check it?
+                # I think need to check it, or pass it to another auth handle.
+                #
+                ### clear the cookie in the browser and locally. Does not
+                ### check if we have a valid user logged, just make sure we
+                ### don't have one after this call.
+                request.deleteCookie()
+                # When the user do global logout then change the
+                # security_string. ( in here. All logout is global logout. )
+                u.security_string = securitystring.gen(30)
+                u.save()
+                return None, True
+            else:
+                return u, False
+
+        # If the brower don't have MOIN_ID cookie, just delete the cookie.
+        if kw.get('logout'):
+            request.deleteCookie()
+            return None, True
+            
     return None, True
 
 
+def moin_url(request, **kw):
+    # The url syntax is like this: action=userform&uid=
+    action = request.form.get('action',[None])[0]
+    uid = request.form.get('uid',[None])[0]
+    user_obj = user.User(request)
+    if action == 'userform' :
+        u = user.User(request, auth_method='moin_url', hmac_uid_string=uid )
+        if u.valid:
+            u.security_string = securitystring.gen(30)
+            u.save()
+            request.user = u
+            request.setCookie()
+            return u, False
+    return None, True
+
 #
 #   idea: maybe we should call back to the request object like:
 #         username, password, authenticated, authtype = request.getUserPassAuth()
-#	 WhoEver   geheim    false          basic      (twisted, doityourself pw check)
-#	 WhoEver   None      true           basic/...  (apache)
-#	 
+#         WhoEver   geheim    false          basic      (twisted, doityourself pw check)
+#         WhoEver   None      true           basic/...  (apache)
+#         
 #        thus, the server specific code would stay in request object implementation.
 #
 #     THIS IS NOT A WIKI PAGE ;-)
-	 
+         
 def http(request, **kw):
     """ authenticate via http basic/digest/ntlm auth """
     from MoinMoin.request import RequestTwisted, RequestCLI


--- orig/MoinMoin/multiconfig.py
+++ mod/MoinMoin/multiconfig.py
@@ -172,7 +172,7 @@
     actions_excluded = [] # ['DeletePage', 'AttachFile', 'RenamePage']
     allow_xslt = 0
     attachments = None # {'dir': path, 'url': url-prefix}
-    auth = [authmodule.moin_cookie]
+    auth = [authmodule.moin_cookie, authmodule.moin_url]
     
     backup_compression = 'gz'
     backup_users = []


--- orig/MoinMoin/request.py
+++ mod/MoinMoin/request.py
@@ -9,7 +9,7 @@
 
 import os, time, sys, cgi, StringIO
 from MoinMoin import config, wikiutil, user
-from MoinMoin.util import MoinMoinNoFooter, IsWin9x
+from MoinMoin.util import MoinMoinNoFooter, IsWin9x, securitystring
 
 # Timing ---------------------------------------------------------------
 
@@ -1216,7 +1216,16 @@
         # Set the cookie
         from Cookie import SimpleCookie
         c = SimpleCookie()
-        c['MOIN_ID'] = self.user.id
+        # Modify the Cookie String Syntax.
+        # Keep the self.user.id in Cookie.
+        #   1. easy for auth.
+        #   2. and don't need to care the 
+        #       securitystring.make_security_key(self, security_string, self.user.id)
+        #      is unique.
+        c['MOIN_ID'] = '%s%s%s' %(
+           securitystring.make_security_key(self, self.user.security_string, self.user.id),
+           securitystring.luck(self),
+           self.user.id )
         c['MOIN_ID']['max-age'] = maxage
         if self.cfg.cookie_domain:
             c['MOIN_ID']['domain'] = self.cfg.cookie_domain


--- orig/MoinMoin/user.py
+++ mod/MoinMoin/user.py
@@ -17,7 +17,7 @@
 PICKLE_PROTOCOL = pickle.HIGHEST_PROTOCOL
 
 from MoinMoin import config, caching, wikiutil
-from MoinMoin.util import datetime, filesys
+from MoinMoin.util import datetime, filesys, securitystring
 
 
 def getUserList(request):
@@ -191,7 +191,9 @@
 class User:
     """A MoinMoin User"""
 
-    def __init__(self, request, id=None, name="", password=None, auth_username="", **kw):
+    # FrankieChow: I think keep the hmac_uid_string auth in User,
+    #  It is better then other, just alter more core code.
+    def __init__(self, request, id=None, name="", password=None, auth_username="", hmac_uid_string=None, **kw):
         """ Initialize User object
         
         @param request: the request object
@@ -255,6 +257,7 @@
         # attrs not saved to profile
         self._request = request
         self._trail = []
+        self._hmac_uid_string = hmac_uid_string
 
         # we got an already authenticated username:
         check_pass = 0
@@ -272,6 +275,11 @@
                 self.load_from_id(1)
             else:
                 self.id = self.make_id()
+        elif self._hmac_uid_string:
+            try:
+                self._hmac_string, self.id = self._hmac_uid_string.split( securitystring.luck(self._request) )[:2]
+            except: pass
+            self.load_from_id(check_pass=0, check_hmac_string=1)
         else:
             self.id = self.make_id()
 
@@ -331,7 +339,9 @@
         """
         return os.path.exists(self.__filename())
 
-    def load_from_id(self, check_pass=0):
+    # In User Class, here is handle User Auth.
+    #   So change it to require the check_hmac_string.
+    def load_from_id(self, check_pass=0, check_hmac_string=0):
         """ Load user account data from disk.
 
         Can only load user data if the id number is already known.
@@ -375,6 +385,17 @@
                 return
             else:
                 self.trusted = 1
+        
+        # If User haven't security_string, Don't handle in here,
+        #    Just handle in auth.py in moin_cookie after the user done passd auth.
+        if check_hmac_string:
+            if not user_data.has_key('security_string'):
+                return
+            valid, changed = self._validateHmacString(user_data)
+            if not valid: 
+                return
+            else:
+                self.trusted = 1
 
         # Remove ignored checkbox values from user data
         for key, label in self._cfg.user_checkbox_fields:
@@ -488,6 +509,11 @@
         # No encoded password match, this must be wrong password
         return False, False
 
+    def _validateHmacString(self, data):
+        if self._hmac_string == securitystring.make_security_key(self._request, data['security_string'], self.id):
+            return True, False
+        return False, False
+
     def save(self):
         """ Save user account data to user account file on disk.
 
@@ -935,14 +961,18 @@
         from MoinMoin.util import mail
         _ = self._request.getText
 
+        # If MoinMoin use security_string logic to do url_auth. 
+        #     When use SSHA to disable the Login Password.
         text = '\n' + _("""\
 Login Name: %s
 
-Login Password: %s
-
-Login URL: %s/?action=userform&uid=%s
+Login URL: %s/?action=userform&uid=%s%s%s
 """, formatted=False) % (
-                        self.name, self.enc_password, self._request.getBaseURL(), self.id)
+                        self.name, self._request.getBaseURL(), 
+                        securitystring.make_security_key(self._request, self.security_string, self.id),
+                        securitystring.luck(self._request),
+                        self.id
+                        )
 
         text = _("""\
 Somebody has requested to submit your account data to this email address.


--- orig/MoinMoin/userform.py
+++ mod/MoinMoin/userform.py
@@ -8,7 +8,7 @@
 
 import string, time, re
 from MoinMoin import user, util, wikiutil
-from MoinMoin.util import web, mail, datetime
+from MoinMoin.util import web, mail, datetime, securitystring
 from MoinMoin.widget import html
 
 _debug = 0
@@ -77,6 +77,10 @@
             for uid in users:
                 theuser = user.User(self.request, uid)
                 if theuser.valid and theuser.email.lower() == email:
+                    # Change the security_string
+                    #    When the user request the account_sendmail.
+                    theuser.security_string = securitystring.gen(30)
+                    theuser.save()
                     msg = theuser.mailAccountData()
                     return wikiutil.escape(msg)
 
@@ -148,6 +152,8 @@
                     if thisuser.email == theuser.email and not thisuser.disabled:
                         return _("This email already belongs to somebody else.")
 
+            # Before create the user's profile, create the user's security_string.
+            theuser.security_string = securitystring.gen(30)
             # save data
             theuser.save()
             if form.has_key('create_and_mail'):


--- orig/MoinMoin/util/securitystring.py
+++ mod/MoinMoin/util/securitystring.py
@@ -0,0 +1,36 @@
+"""
+    MoinMoin - Handle the Security String
+    @copyright: (c) Bastian Blank, Florian Festi, Thomas Waldmann
+    @copyright: MoinMoin:FrankieChow
+    @license: GNU GPL, see COPYING for details.
+"""
+import os, hmac, string, random
+
+# Follow http://moinmoin.wikiwikiweb.de/MoinMoinBugs/Cookie_is_not_secure_enough
+# This code is write by Nir Soffer
+
+def gen(number):
+    # Make the number is more security.
+    random_number = random.randint(number/2,number)
+    safe = string.ascii_letters + string.digits + '_-'
+    return "%s" % (''.join([random.choice(safe) for i in range(random_number)]))
+
+###
+
+def luck(request):
+    # ':=:' is FrankieChow luck string. maybe you can change this to
+    #   self.cfg.site_luck_string
+    return ':=:'
+    
+def make_security_key(request, securitystring, userid):
+    """
+    Make the hmac value for
+      Key: securitystring
+      msg: userid
+
+    HOWTO set env['MOIN_SECURITY_STRING'] in cgi mode or mod_python mode.
+        Please see: http://httpd.apache.org/docs/1.3/mod/mod_env.html
+    """
+    if request.env.has_key('MOIN_SECURITY_STRING'):
+        return hmac.new( str(request.env['MOIN_SECURITY_STRING']) + securitystring, userid).hexdigest()
+    return hmac.new(securitystring, userid).hexdigest()
