changeset 2471:572f8ce1adff

Merge main.
author Karol 'grzywacz' Nowak <grzywacz@sul.uni.lodz.pl>
date Fri, 20 Jul 2007 13:18:46 +0200
parents f15b9bf952d4 (current diff) c8a3a74953f6 (diff)
children 639a122cbc54
files MoinMoin/userprefs/prefs.py
diffstat 7 files changed, 121 insertions(+), 45 deletions(-) [+]
line wrap: on
line diff
--- a/MoinMoin/action/newaccount.py	Fri Jul 20 02:57:58 2007 +0200
+++ b/MoinMoin/action/newaccount.py	Fri Jul 20 13:18:46 2007 +0200
@@ -43,6 +43,12 @@
     password = form.get('password', [''])[0]
     password2 = form.get('password2', [''])[0]
 
+    pw_checker = request.cfg.password_checker
+    if pw_checker:
+        pw_error = pw_checker(theuser.name, password)
+        if pw_error:
+            return _("Password not acceptable: %s") % pw_error
+
     # Check if password is given and matches with password repeat
     if password != password2:
         return _("Passwords don't match!")
--- a/MoinMoin/config/multiconfig.py	Fri Jul 20 02:57:58 2007 +0200
+++ b/MoinMoin/config/multiconfig.py	Fri Jul 20 13:18:46 2007 +0200
@@ -15,6 +15,9 @@
 from MoinMoin import config, error, util, wikiutil
 import MoinMoin.auth as authmodule
 import MoinMoin.events as events
+from MoinMoin.events import PageChangedEvent, PageRenamedEvent
+from MoinMoin.events import PageDeletedEvent, PageCopiedEvent
+from MoinMoin.events import PageRevertedEvent, FileAttachedEvent
 from MoinMoin import session
 from MoinMoin.packages import packLine
 from MoinMoin.security import AccessControlList
@@ -205,9 +208,6 @@
 class DefaultConfig:
     """ default config values """
 
-    # internal dict for plugin `modules' lists
-    _site_plugin_lists = {}
-
     # setting DesktopEdition = True gives all local users special powers - ONLY use for MMDE style usage!
     DesktopEdition = False
 
@@ -433,6 +433,51 @@
         'view':        ({}, _("View"), "view"),
         }
 
+
+    def password_checker(username, password):
+        """ Check if a password is secure enough.
+            First (and in any case), we use a built-in check to get rid of the
+            worst passwords. If there is cracklib installed, we use it for
+            additional checks.
+            If you don't want to check passwords, use password_checker = None.
+
+            @return: None if there is no problem with the password,
+                     some string with an error msg, if the password is problematic.
+        """
+        try:
+            # in any case, do a very simple built-in check to avoid the worst passwords
+            if len(password) < 6:
+                raise ValueError("Password too short!")
+
+            username_lower = username.lower()
+            password_lower = password.lower()
+            if username in password or password in username or \
+               username_lower in password_lower or password_lower in username_lower:
+                raise ValueError("Password too easy (containment)")
+
+            keyboards = (ur"`1234567890-=qwertyuiop[]\asdfghjkl;'zxcvbnm,./", # US kbd
+                        ) # add more keyboards!
+            for kbd in keyboards:
+                rev_kbd = kbd[::-1]
+                if password in kbd or password in rev_kbd or \
+                   password_lower in kbd or password_lower in rev_kbd:
+                    raise ValueError("Password too easy (kbd sequence)")
+            try:
+                # to use advanced checking, you need to install python-crack,
+                # cracklib-runtime (dict processing) and do not forget to
+                # initialize the crack dicts!
+                import crack
+                # instead of some "old password" we give the username to check
+                # whether the password is too similar to the username
+                crack.VeryFascistCheck(password, username) # raises ValueError on bad passwords
+            except ImportError:
+                pass
+            return None
+        except ValueError, err:
+            return str(err)
+
+    password_checker = staticmethod(password_checker)
+
     quicklinks_default = [] # preload user quicklinks with this page list
     refresh = None # (minimum_delay, type), e.g.: (2, 'internal')
     rss_cache = 60 # suggested caching time for RecentChanges RSS, in seconds
@@ -452,7 +497,15 @@
     stylesheets = [] # list of tuples (media, csshref) to insert after theme css, before user css
     _subscribable_events = None # A list of event types that user can subscribe to
     subscribed_pages_default = [] # preload user subscribed pages with this page list
-    subscribed_events_default = [] # preload user subscribed events with this list
+    email_subscribed_events_default = [
+        PageChangedEvent.__name__,
+        PageRenamedEvent.__name__,
+        PageDeletedEvent.__name__,
+        PageCopiedEvent.__name__,
+        PageRevertedEvent.__name__,
+        FileAttachedEvent.__name__,
+    ]
+    jabber_subscribed_events_default = []
     superuser = [] # list of unicode user names that have super powers :)
     supplementation_page = False
     supplementation_page_name = u'Discussion'
@@ -552,11 +605,10 @@
                               'show_fancy_diff':     1,
                               'wikiname_add_spaces': 0,
                               'remember_me':         1,
-                              'want_trivial':        0,
                              }
 
     # don't let the user change those
-    # user_checkbox_disable = ['disabled', 'want_trivial']
+    # user_checkbox_disable = ['disabled']
     user_checkbox_disable = []
 
     # remove those checkboxes:
@@ -670,6 +722,10 @@
                 self.chart_options = None
 
         # post process
+
+        # internal dict for plugin `modules' lists
+        self._site_plugin_lists = {}
+
         # we replace any string placeholders with config values
         # e.g u'%(page_front_page)s' % self
         self.navi_bar = [elem % self for elem in self.navi_bar]
--- a/MoinMoin/user.py	Fri Jul 20 02:57:58 2007 +0200
+++ b/MoinMoin/user.py	Fri Jul 20 13:18:46 2007 +0200
@@ -329,8 +329,8 @@
         self.datetime_fmt = ""
         self.quicklinks = self._cfg.quicklinks_default
         self.subscribed_pages = self._cfg.subscribed_pages_default
-        self.email_subscribed_events = self._cfg.subscribed_events_default
-        self.jabber_subscribed_events = []
+        self.email_subscribed_events = self._cfg.email_subscribed_events_default
+        self.jabber_subscribed_events = self._cfg.jabber_subscribed_events_default
         self.theme_name = self._cfg.theme_default
         self.editor_default = self._cfg.editor_default
         self.editor_ui = self._cfg.editor_ui
--- a/MoinMoin/userprefs/changepass.py	Fri Jul 20 02:57:58 2007 +0200
+++ b/MoinMoin/userprefs/changepass.py	Fri Jul 20 13:18:46 2007 +0200
@@ -35,12 +35,13 @@
 
     def handle_form(self):
         _ = self._
-        form = self.request.form
+        request = self.request
+        form = request.form
 
         if form.has_key('cancel'):
             return
 
-        if self.request.request_method != 'POST':
+        if request.request_method != 'POST':
             return
 
         password = form.get('password', [''])[0]
@@ -50,15 +51,19 @@
         if password != password2:
             return _("Passwords don't match!")
 
-        # Encode password
-        if password and not password.startswith('{SHA}'):
-            try:
-                self.request.user.enc_password = user.encodePassword(password)
-                self.request.user.save()
-                return _("Your password has been changed.")
-            except UnicodeError, err:
-                # Should never happen
-                return "Can't encode password: %s" % str(err)
+        pw_checker = request.cfg.password_checker
+        if pw_checker:
+            pw_error = pw_checker(request.user.name, password)
+            if pw_error:
+                return _("Password not acceptable: %s") % pw_error
+
+        try:
+            self.request.user.enc_password = user.encodePassword(password)
+            self.request.user.save()
+            return _("Your password has been changed.")
+        except UnicodeError, err:
+            # Should never happen
+            return "Can't encode password: %s" % str(err)
 
 
     def create_form(self, create_only=False, recover_only=False):
--- a/MoinMoin/userprefs/prefs.py	Fri Jul 20 02:57:58 2007 +0200
+++ b/MoinMoin/userprefs/prefs.py	Fri Jul 20 13:18:46 2007 +0200
@@ -103,7 +103,7 @@
 
         if not 'jid' in theuser.auth_attribs:
             # try to get the jid
-            jid = wikiutil.clean_input(form.get('jid', [""])[0]).strip()
+            jid = wikiutil.clean_input(form.get('jid', [''])[0]).strip()
 
             jid_changed = theuser.jid != jid
             previous_jid = theuser.jid
--- a/MoinMoin/userprefs/suid.py	Fri Jul 20 02:57:58 2007 +0200
+++ b/MoinMoin/userprefs/suid.py	Fri Jul 20 13:18:46 2007 +0200
@@ -38,24 +38,19 @@
 
         if (wikiutil.checkTicket(self.request, self.request.form['ticket'][0]) and
             self.request.request_method == 'POST'):
-            su_user = form.get('selected_user', [''])[0]
-            uid = user.getUserId(self.request, su_user)
+            uid = form.get('selected_user', [''])[0]
             if not uid:
                 return _("No user selected")
-            if (not self.request._setuid_real_user is None
-                and uid == self.request._setuid_real_user.id):
-                del self.request.session['setuid']
-                self.request.user = self.request._setuid_real_user
-                self.request._setuid_real_user = None
-            else:
-                theuser = user.User(self.request, uid, auth_method='setuid')
-                # set valid to True so superusers can even switch
-                # to disable accounts
-                theuser.valid = True
-                self.request.session['setuid'] = uid
-                self.request._setuid_real_user = self.request.user
-                # now continue as the other user
-                self.request.user = theuser
+            theuser = user.User(self.request, uid, auth_method='setuid')
+            if not theuser or not theuser.exists():
+                return _("No user selected")
+            # set valid to True so superusers can even switch
+            # to disable accounts
+            theuser.valid = True
+            self.request.session['setuid'] = uid
+            self.request._setuid_real_user = self.request.user
+            # now continue as the other user
+            self.request.user = theuser
             return  _("You can now change the settings of the selected user account; log out to get back to your account.")
         else:
             return None
@@ -71,12 +66,19 @@
             realuid = self.request.user.id
         for uid in users:
             if uid != realuid:
-                name = user.User(self.request, id=uid).name # + '/' + uid # for debugging
-                options.append((name, name))
-        options.sort()
+                name = user.User(self.request, id=uid).name
+                options.append((uid, name))
+        options.sort(lambda x, y: cmp(x[1].lower(), y[1].lower()))
 
         size = min(5, len(options))
-        current_user = self.request.user.name
+        current_user = self.request.user.id
+
+        if not options:
+            _ = self._
+            self._only = True
+            return _("You are the only user.")
+
+        self._only = False
         return util.web.makeSelection('selected_user', options, current_user, size=size)
 
     def create_form(self):
@@ -88,9 +90,13 @@
         ticket = wikiutil.createTicket(self.request)
         self.make_row('Select User', [self._user_select()], valign="top")
         form.append(html.INPUT(type="hidden", name="ticket", value="%s" % ticket))
-        self.make_row('', [html.INPUT(type="submit", name="select_user",
-                                      value=_('Select User')),
-                           ' ',
-                           html.INPUT(type="submit", name="cancel",
-                                      value=_('Cancel'))])
+        if not self._only:
+            buttons = [html.INPUT(type="submit", name="select_user",
+                                  value=_('Select User')),
+                       ' ',]
+        else:
+            buttons = []
+        buttons.append(html.INPUT(type="submit", name="cancel",
+                                  value=_('Cancel')))
+        self.make_row('', buttons)
         return unicode(form)
--- a/docs/CHANGES	Fri Jul 20 02:57:58 2007 +0200
+++ b/docs/CHANGES	Fri Jul 20 13:18:46 2007 +0200
@@ -42,6 +42,9 @@
     * new authentication plugin system
     * new preferences plugin system, see MoinMoin/userprefs/__init__.py
     * new notification system with an optional jabber notification bot
+    * cfg.password_checker (default: use some simple builtin checks for too
+      easy passwords and, if available, python-crack).
+      Use password_checker = None to disable password checking.
 
   Bugfixes:
     * ...