[Checkins] SVN: Products.PluggableAuthService/branches/maurits-login-transform/Products/PluggableAuthService/ Added updateAllLoginNames to PAS and the ZODBUserManager.

Maurits van Rees cvs-admin at zope.org
Fri Jan 4 20:04:13 UTC 2013


Log message for revision 129017:
  Added updateAllLoginNames to PAS and the ZODBUserManager.
  
  This is now called when a relevant change is made to the login_transform property.
  

Changed:
  U   Products.PluggableAuthService/branches/maurits-login-transform/Products/PluggableAuthService/PluggableAuthService.py
  U   Products.PluggableAuthService/branches/maurits-login-transform/Products/PluggableAuthService/interfaces/authservice.py
  U   Products.PluggableAuthService/branches/maurits-login-transform/Products/PluggableAuthService/interfaces/plugins.py
  U   Products.PluggableAuthService/branches/maurits-login-transform/Products/PluggableAuthService/plugins/ZODBUserManager.py

-=-
Modified: Products.PluggableAuthService/branches/maurits-login-transform/Products/PluggableAuthService/PluggableAuthService.py
===================================================================
--- Products.PluggableAuthService/branches/maurits-login-transform/Products/PluggableAuthService/PluggableAuthService.py	2013-01-04 16:22:08 UTC (rev 129016)
+++ Products.PluggableAuthService/branches/maurits-login-transform/Products/PluggableAuthService/PluggableAuthService.py	2013-01-04 20:04:12 UTC (rev 129017)
@@ -1105,6 +1105,16 @@
             return
         return transform
 
+    security.declarePrivate( '_setPropValue' )
+    def _setPropValue(self, id, value):
+        if id == 'login_transform':
+            orig_value = getattr(self, id)
+        super(PluggableAuthService, self)._setPropValue(id, value)
+        if id == 'login_transform' and value and value != orig_value:
+            logger.info("login_transform changed from %r to %r. "
+                        "Updating existing login names.", orig_value, value)
+            self.updateAllLoginNames()
+
     security.declarePublic( 'lower' )
     def lower( self, value ):
         """ Transform for login name.
@@ -1299,7 +1309,25 @@
             else:
                 logger.info("login name changed to: %r", login_name)
 
+    security.declareProtected( ManageUsers, 'updateLoginName')
+    def updateAllLoginNames(self, quit_on_first_error=True):
+        """Update login names of all users to their canonical value.
 
+        This should be done after changing the login_transform
+        property of PAS.
+
+        You can set quit_on_first_error to False to report all errors
+        before quitting with an error.  This can be useful if you want
+        to know how many problems there are, if any.
+        """
+        plugins = self._getOb('plugins')
+        updaters = plugins.listPlugins(IUpdateLoginNamePlugin)
+        for updater_id, updater in updaters:
+            # Note: do not swallow any exceptions here.
+            updater.updateAllLoginNames(
+                quit_on_first_error=quit_on_first_error)
+
+
 classImplements( PluggableAuthService
                , (IPluggableAuthService, IObjectManager, IPropertyManager)
                )

Modified: Products.PluggableAuthService/branches/maurits-login-transform/Products/PluggableAuthService/interfaces/authservice.py
===================================================================
--- Products.PluggableAuthService/branches/maurits-login-transform/Products/PluggableAuthService/interfaces/authservice.py	2013-01-04 16:22:08 UTC (rev 129016)
+++ Products.PluggableAuthService/branches/maurits-login-transform/Products/PluggableAuthService/interfaces/authservice.py	2013-01-04 20:04:12 UTC (rev 129017)
@@ -223,7 +223,18 @@
         """Update own login name of authenticated user.
         """
 
+    def updateAllLoginNames(quit_on_first_error=True):
+        """Update login names of all users to their canonical value.
 
+        This should be done after changing the login_transform
+        property of PAS.
+
+        You can set quit_on_first_error to False to report all errors
+        before quitting with an error.  This can be useful if you want
+        to know how many problems there are, if any.
+        """
+
+
 # The IMutableUserFolder and IEnumerableFolder are not supported
 # out-of-the-box by the pluggable authentication service.  These
 # interfaces describe contracts that other standard Zope user folders

Modified: Products.PluggableAuthService/branches/maurits-login-transform/Products/PluggableAuthService/interfaces/plugins.py
===================================================================
--- Products.PluggableAuthService/branches/maurits-login-transform/Products/PluggableAuthService/interfaces/plugins.py	2013-01-04 16:22:08 UTC (rev 129016)
+++ Products.PluggableAuthService/branches/maurits-login-transform/Products/PluggableAuthService/interfaces/plugins.py	2013-01-04 20:04:12 UTC (rev 129017)
@@ -277,6 +277,17 @@
         """
 
 
+    def updateAllLoginNames(quit_on_first_error=True):
+        """Update login names of all users to their canonical value.
+
+        This should be done after changing the login_transform
+        property of PAS.
+
+        You can set quit_on_first_error to False to report all errors
+        before quitting with an error.  This can be useful if you want
+        to know how many problems there are, if any.
+        """
+
 class IValidationPlugin( Interface ):
 
     """ Specify allowable values for user properties.

Modified: Products.PluggableAuthService/branches/maurits-login-transform/Products/PluggableAuthService/plugins/ZODBUserManager.py
===================================================================
--- Products.PluggableAuthService/branches/maurits-login-transform/Products/PluggableAuthService/plugins/ZODBUserManager.py	2013-01-04 16:22:08 UTC (rev 129016)
+++ Products.PluggableAuthService/branches/maurits-login-transform/Products/PluggableAuthService/plugins/ZODBUserManager.py	2013-01-04 20:04:12 UTC (rev 129017)
@@ -16,6 +16,7 @@
 $Id$
 """
 import copy
+import logging
 try:
     from hashlib import sha1 as sha
 except:
@@ -48,7 +49,9 @@
 from Products.PluggableAuthService.utils import createViewName
 from Products.PluggableAuthService.utils import csrf_only
 
+logger = logging.getLogger('PluggableAuthService')
 
+
 class IZODBUserManager(Interface):
     """ Marker interface.
     """
@@ -329,6 +332,62 @@
             self._login_to_userid[login_name] = user_id
             self._userid_to_login[user_id] = login_name
 
+    security.declarePrivate('updateAllLoginNames')
+    def updateAllLoginNames(self, quit_on_first_error=True):
+        # Update all login names to their canonical value.  This
+        # should be done after changing the login_transform property
+        # of pas.  You can set quit_on_first_error to False to report
+        # all errors before quitting with an error.  This can be
+        # useful if you want to know how many problems there are, if
+        # any.
+        pas = self._getPAS()
+        transform = pas._get_login_transform_method()
+        if not transform:
+            logger.warn("PAS has a non-existing, empty or wrong "
+                        "login_transform property.")
+            return
+
+        # Make a fresh mapping, as we do not want to add or remove
+        # items to the original mapping while we are iterating over
+        # it.
+        new_login_to_userid = OOBTree()
+        errors = []
+        for old_login_name, user_id in self._login_to_userid.items():
+            new_login_name = transform(old_login_name)
+            if new_login_name in new_login_to_userid:
+                logger.error("User id %s: login name %r already taken.",
+                             user_id, new_login_name)
+                errors.append(new_login_name)
+                if quit_on_first_error:
+                    break
+            new_login_to_userid[new_login_name] = user_id
+            if new_login_name != old_login_name:
+                self._userid_to_login[user_id] = new_login_name
+                # Also, remove from the cache
+                view_name = createViewName('enumerateUsers', user_id)
+                self.ZCacheable_invalidate(view_name=view_name)
+                logger.info("User id %s: changed login name from %r to %r.",
+                            user_id, old_login_name, new_login_name)
+
+        # If there were errors, we do not want to save any changes.
+        if errors:
+            logger.error("There were %d errors when updating login names. "
+                         "quit_on_first_error was %r", len(errors),
+                         quit_on_first_error)
+            # Make sure the exception we raise is not swallowed.
+            self._dont_swallow_my_exceptions = True
+            raise ValueError("Transformed login names are not unique: %s." %
+                             ', '.join(errors))
+
+        # Make sure we did not lose any users.
+        assert(len(self._login_to_userid.keys())
+               == len(new_login_to_userid.keys()))
+        # Empty the main cache.
+        view_name = createViewName('enumerateUsers')
+        self.ZCacheable_invalidate(view_name=view_name)
+        # Store the new login mapping.
+        self._login_to_userid = new_login_to_userid
+
     security.declarePrivate( 'removeUser' )
     def removeUser( self, user_id ):
 



More information about the checkins mailing list