We have a Pyramid application that uses LDAP for authentication. Currently we don't use pyramid_ldap
but custom code based on python_ldap
to connect to the LDAP server.
The code is in a Singleton class: the __init__
method (more precisely the method called during instance creation) calls ldap.initialize
to create the LDAPObject
and simple_bind_s
. Here is the code (the user can by pass the LDAP server by providing a local admin account):
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
The Singleton is created in the server startup function (so before any request) and the subsequent requests just retrieve the instance. We use the admin
account to login:
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
This works most of the time but sometimes LDAP operations fail with INSUFFICIENT_ACCESS
errors. The error happens when we restart the server for instance when doing local development (in which case we use pserve
with the reload
option) but also on the production server (where the services are managed through circus
and chaussette
-- we usually launch several processes). The only solution we found is to shutdown the server and start it again.
We are looking for clues on what's happening and how to solve it.
The login and password are correct since it works most of the time. AFAIU a problem in bind should raise an exception during server startup. I was wondering if the autoreload or the multiple processes could trigger such problems (since it usually works when restarting the server).
Some version information:
- OS: Ubuntu 12.04 or 14.04
- python: 2.7
- OpenLDAP: 2.4.28
- python_ldap: 2.4.25
Aucun commentaire:
Enregistrer un commentaire