使用 LDAP 时出现不稳定 INSUFFICIENT_ACCESS 错误

Erratic INSUFFICIENT_ACCESS error when using LDAP

我们有一个使用 LDAP 进行身份验证的 Pyramid 应用程序。目前我们不使用 pyramid_ldap,而是使用基于 python_ldap 的自定义代码来连接到 LDAP 服务器。

代码在单例中 class:__init__ 方法(更准确地说是实例创建期间调用的方法)调用 ldap.initialize 来创建 LDAPObjectsimple_bind_s。这是代码(用户可以通过提供本地管理员帐户来绕过 LDAP 服务器):

class Singleton(object):
    '''
    Implements the singleton pattern. A class deriving from ``Singleton`` can
    have only one instance. The first instanciation will create an object and
    other instanciations return the same object. Note that the :py:meth:`__init__`
    method (if any) is still called at each instanciation (on the same object).
    Therefore, :py:class:`Singleton` derived classes should define
    :py:meth:`__singleton_init__`
    instead of :py:meth:`__init__` because the former is only called once.
    '''
    @classmethod
    def get_instance(cls):
        try:
            return getattr(cls, '_singleton_instance')
        except AttributeError:
            msg = "Class %s has not been initialized" % cls.__name__
            raise ValueError(msg)

    def __new__(cls, *args, **kwargs):
        if '_singleton_instance' not in cls.__dict__:
            cls._singleton_instance = super(Singleton, cls).__new__(cls)
            singleton_init = getattr(cls._singleton_instance,
                                 '__singleton_init__', None)
            if singleton_init is not None:
                singleton_init(*args, **kwargs)
        return cls._singleton_instance

    def __init__(self, *args, **kwargs):
        '''
        The __init__ method of :py:class:`Singleton` derived class should do nothing.
        Derived classes must define :py:meth:`__singleton_init__` instead of __init__.
        '''

    def __singleton_init__(self, *args, **kwargs):
        super(Singleton, self).__init__(*args, **kwargs)

class UsersAndGroups(Singleton):
    """
    Class used to query the LDAP directory.
    """
    def __singleton_init__(self, admin_login, admin_password,
                           server, ldap_admin_dn, ldap_password, users_dn,
                           groups_dn):
        self.admin_login = admin_login
        self.admin_password = admin_password
        self.server = server
        self.ldap_admin_dn = ldap_admin_dn
        self.ldap_password = ldap_password
        self.users_dn = users_dn
        self.groups_dn = groups_dn

        # Check
        if admin_login and (not admin_password):
            raise ValueError('You must specify a password for the local admin')
        self.has_local_admin = (admin_login) and (admin_password)
        if (not self.server) and (not self.has_local_admin):
            raise ValueError(
                'You must specify an LDAP server or a local admin')

        # Connect to LDAP server
        if self.server:
            self.ldap_connection = ldap.initialize(self.server)
            self.ldap_connection.simple_bind_s(self.ldap_admin_dn,
                                               self.ldap_password)
        else:
           self.ldap_connection = None

Singleton 是在服务器启动函数中创建的(因此在任何请求之前),后续请求仅检索实例。我们使用admin账号登录:

def main(global_config, **settings):

    # Create routes and so on    
    # Get configuration for LDAP connection from app.registry.settings

    # Create the singleton to connect to LDAP
    app.users_groups = UsersAndGroups(admin_login, admin_password, ldap_server,
                                      ldap_admin_dn, ldap_password, users_dn,
                                      groups_dn)

    return app

这在大多数情况下都有效,但有时 LDAP 操作会失败并出现 INSUFFICIENT_ACCESS 错误。当我们重新启动服务器时会发生错误,例如在进行本地开发时(在这种情况下我们使用 pservereload 选项)以及在生产服务器上(通过 [=23 管理服务) =] 和 chaussette——我们通常会启动多个进程)。我们找到的唯一解决方案是关闭服务器并重新启动它。

我们正在寻找有关正在发生的事情以及如何解决它的线索。

登录名和密码是正确的,因为它大部分时间都有效。 AFAIU 绑定中的问题应该在服务器启动期间引发异常。我想知道 autoreload 或多个进程是否会触发此类问题(因为它通常在重新启动服务器时有效)。

部分版本信息:

我意识到错误来自代码中的其他地方。