Hi,
I've encountered some weird behaviour in the ZPatterns Transactional class when
I was trying to write a User Source for the Login Manager Product.

I'm using Zope 2.2.0 with LoginManager 0.8.7a1 and ZPatterns 0.4.1snap1

The problem is that _unregister seems to be trying to delete the _v_registered
attribute of an object that doesn't have it set whenever I return None from
authenticateUser(). The weird thing is that the object seems to be
_register()ed and _v_registered appears to be set to 1 in _register(), but when
_unregister() is called it has the value 'None' again.

I've attached my code that's trying to implement the UserSource.

here is what my modified _register and _unregister functions look like:

    def _register(self):
        f=open('/tmp/zopelog', 'a', 0)
        f.write('Register %s(%s)\n' % (self.id, str(self)))
        f.write('Before: %s._v_registered=%s\n' % (self.id, getattr(self, 
'_v_registered', 'N/A')))
        for i in traceback.format_stack():
            f.write(i)
        if self._v_registered: return
        get_transaction().register(Reporter(self))
        self._v_registered = 1
        f.write('After: %s._v_registered=%s\n' % (self.id, getattr(self, 
'_v_registered', 'N/A')))
        f.close()

    def _unregister(self):
        f=open('/tmp/zopelog', 'a', 0)
        f.write('Unregister %s(%s)\nBefore: %s._v_registered=%s\n' \
                % (self.id, str(self), self.id, getattr(self, '_v_registered', 'N/A')))
        f.close()
        del self._v_registered

and this is a sample log:

Register UserSource(<NisUserSource instance at 85ab2e8>)
Before: UserSource._v_registered=None
  File "/home/picard/bpe/Zope-2.2.0-src/ZServer/PubCore/ZServerPublisher.py", line 95, 
in __init__
    response=response)
  File "/home/picard/bpe/Zope-2.2.0-src/lib/python/ZPublisher/Publish.py", line 222, 
in publish_module
    response = publish(request, module_name, after_list, debug=debug)
  File "/home/picard/bpe/Zope-2.2.0-src/lib/python/ZPublisher/Publish.py", line 162, 
in publish
    object=request.traverse(path, validated_hook=validated_hook)
  File "/home/picard/bpe/Zope-2.2.0-src/lib/python/ZPublisher/BaseRequest.py", line 
427, in traverse
    else: user=v(request, auth, roles)
  File 
"/home/picard/bpe/Zope-2.2.0-src/lib/python/Products/LoginManager/LoginManager.py", 
line 110, in validate
    user = _DefaultAuth.findLogin(self, request, auth, user, roles)
  File 
"/home/picard/bpe/Zope-2.2.0-src/lib/python/Products/LoginManager/LoginMethods.py", 
line 147, in findLogin
    user = manager.getItem(name)
  File 
"/home/picard/bpe/Zope-2.2.0-src/lib/python/Products/LoginManager/LoginManager.py", 
line 65, in getItem
    user = source.__of__(self).getItem(name)
  File "/home/picard/bpe/Zope-2.2.0-src/lib/python/Products/ZPatterns/Rack.py", line 
61, in getItem
    self._registerCanonical(k,item) # XXX Should we cache non-existence?
  File 
"/home/picard/bpe/Zope-2.2.0-src/lib/python/Products/ZPatterns/DataManagers.py", line 
52, in _registerCanonical
    self._register()
  File 
"/home/picard/bpe/Zope-2.2.0-src/lib/python/Products/ZPatterns/Transactions.py", line 
47, in _register
    for i in traceback.format_stack():
After: UserSource._v_registered=1
Unregister UserSource(<NisUserSource instance at 85ab2e8>)
Before: UserSource._v_registered=None
from Products.ZPatterns.PlugIns import PlugIn
from Products.LoginManager.UserSources import BasicUserSource,LoginUser
from Products.LoginManager.LoginMethods import LoginMethod
from Products.LoginManager import LoginManager
import nis, string, crypt
from Products.ZPatterns.PlugIns import defaultConstructors

class NisUserSource(BasicUserSource, PlugIn):

    __plugin_kind__ = "User Source"
    meta_type = "NIS User Source"

    f=open('/tmp/zopelog', 'a', 0)
    i=0

    def dbg(self, str):
        self.f.write('[%03d] %s\n' % (self.i, str))
        self.i = self.i + 1

    def retrieveItem(self, name):
        self.dbg('retrieveItem: %s' % name)
        try: passwd=nis.match(name, 'passwd.byname')
        except nis.error: return None
        user=LoginUser(name)
        user._setRack(self)
        self.dbg('retrieveItem: %s ok.' % user.getUserName())
        return user
    
    def authenticateUser(self, user, password, REQUEST=None):
        self.dbg('authenticateUser: %s %s' % (user, 'password'))
        name=user.getUserName()
        if name is None: return None
        try: passwd=nis.match(name, 'passwd.byname')
        except nis.error: return None
        crypted=string.split(passwd, ':')[1]
        test=crypt.crypt(password, crypted)
        self.dbg('%s == %s: %d' % (crypted, test, crypted==test))
        return crypted==test
    
    def rolesForUser(self, user):
        if user.getUserName() == 'bpe':
            return ['Write Access']
        else:
            return ['Read Access']
    
    def domainsForUser(self, user):
        return []

def initialize(context):
    context.registerPlugInClass(
        NisUserSource,
        permission = 'Add NIS User Source',
        constructors = defaultConstructors(NisUserSource, globals()),
        )

Reply via email to