Make ldap2 class work as a client library as well.

Move the user-private group caching code out of the global config and
determine the value the first time it is needed.

Renamed global_init() back to get_schema() and make it take an optional
connection. This solves the problem of being able to do all operations
with a simple bind instead of GSSAPI.

Moved the global get_syntax() into a class method so that a schema
can be passed in.

If a schema wasn't loaded during the module import then it is loaded
when the connection is created (so we have the credntials needed for
binding).

ticket 63
This commit is contained in:
Rob Crittenden 2010-08-18 18:43:11 -04:00
parent 221351809b
commit 6049a25848
2 changed files with 49 additions and 37 deletions

View File

@ -123,7 +123,7 @@ def has_managed_entries(host_name, dm_password):
conn = ldap2(shared_instance=False, ldap_uri=ldapuri, base_dn='cn=config') conn = ldap2(shared_instance=False, ldap_uri=ldapuri, base_dn='cn=config')
conn.connect(bind_dn='cn=Directory Manager', bind_pw=dm_password) conn.connect(bind_dn='cn=Directory Manager', bind_pw=dm_password)
(dn, attrs) = conn.get_entry('cn=Managed Entries,cn=plugins', (dn, attrs) = conn.get_entry('cn=Managed Entries,cn=plugins',
['*']) ['*'], time_limit=2, size_limit=3000)
return True return True
except errors.NotFound: except errors.NotFound:
return False return False

View File

@ -103,7 +103,7 @@ def _handle_errors(e, **kw):
raise errors.DatabaseError(desc=desc, info=info) raise errors.DatabaseError(desc=desc, info=info)
def global_init(url): def get_schema(url, conn=None):
""" """
Perform global initialization when the module is loaded. Perform global initialization when the module is loaded.
@ -114,16 +114,20 @@ def global_init(url):
in-tree "lite" server then use the current ccache. If in the context of in-tree "lite" server then use the current ccache. If in the context of
Apache then create a new ccache and bind using the Apache HTTP service Apache then create a new ccache and bind using the Apache HTTP service
principal. principal.
If a connection is provided then it the credentials bound to it are
used. The connection is not closed when the request is done.
""" """
tmpdir = None tmpdir = None
upg = False has_conn = conn is not None
if not api.env.in_server or api.env.context not in ['lite', 'server']: if (not api.env.in_server or api.env.context not in ['lite', 'server']
and conn is None):
# The schema is only needed on the server side # The schema is only needed on the server side
return (None, None) return None
try: try:
if api.env.context == 'server': if api.env.context == 'server' and conn is None:
try: try:
# Create a new credentials cache for this Apache process # Create a new credentials cache for this Apache process
tmpdir = tempfile.mkdtemp(prefix = "tmp-") tmpdir = tempfile.mkdtemp(prefix = "tmp-")
@ -139,24 +143,18 @@ def global_init(url):
except krbV.Krb5Error, e: except krbV.Krb5Error, e:
raise StandardError('Unable to retrieve LDAP schema. Error initializing principal %s in %s: %s' % (principal.name, '/etc/httpd/conf/ipa.keytab', str(e))) raise StandardError('Unable to retrieve LDAP schema. Error initializing principal %s in %s: %s' % (principal.name, '/etc/httpd/conf/ipa.keytab', str(e)))
conn = _ldap.initialize(url) if conn is None:
conn.sasl_interactive_bind_s('', SASL_AUTH) conn = _ldap.initialize(url)
conn.sasl_interactive_bind_s('', SASL_AUTH)
schema_entry = conn.search_s( schema_entry = conn.search_s(
'cn=schema', _ldap.SCOPE_BASE, 'cn=schema', _ldap.SCOPE_BASE,
attrlist=['attributetypes', 'objectclasses'] attrlist=['attributetypes', 'objectclasses']
)[0] )[0]
try: if not has_conn:
upg_entry = conn.search_s( conn.unbind_s()
'cn=UPG Template, %s' % api.env.basedn, _ldap.SCOPE_BASE,
attrlist=['*']
)[0]
upg = True
except _ldap.NO_SUCH_OBJECT, e:
upg = False
conn.unbind_s()
except _ldap.SERVER_DOWN: except _ldap.SERVER_DOWN:
return (None, upg) return None
except _ldap.LDAPError, e: except _ldap.LDAPError, e:
desc = e.args[0]['desc'].strip() desc = e.args[0]['desc'].strip()
info = e.args[0].get('info', '').strip() info = e.args[0].get('info', '').strip()
@ -170,27 +168,16 @@ def global_init(url):
if tmpdir: if tmpdir:
shutil.rmtree(tmpdir) shutil.rmtree(tmpdir)
return (_ldap.schema.SubSchema(schema_entry[1]), upg) return _ldap.schema.SubSchema(schema_entry[1])
# cache schema and User-Private Groups when importing module # cache schema when importing module
try: try:
(_schema, _upg) = global_init(api.env.ldap_uri) _schema = get_schema(api.env.ldap_uri)
except AttributeError: except AttributeError:
_schema = None _schema = None
_upg = None
def get_syntax(attr, value):
global _schema
if not _schema:
return None
obj = _schema.get_obj(_ldap.schema.AttributeType, attr)
if obj is not None:
return obj.syntax
else:
return None
# The UPG setting will be cached the first time a module checks it
_upg = None
class ldap2(CrudBackend, Encoder): class ldap2(CrudBackend, Encoder):
""" """
@ -228,13 +215,14 @@ class ldap2(CrudBackend, Encoder):
def __init__(self, shared_instance=True, ldap_uri=None, base_dn=None, def __init__(self, shared_instance=True, ldap_uri=None, base_dn=None,
schema=None): schema=None):
global _schema
CrudBackend.__init__(self, shared_instance=shared_instance) CrudBackend.__init__(self, shared_instance=shared_instance)
Encoder.__init__(self) Encoder.__init__(self)
self.encoder_settings.encode_dict_keys = True self.encoder_settings.encode_dict_keys = True
self.encoder_settings.decode_dict_keys = True self.encoder_settings.decode_dict_keys = True
self.encoder_settings.decode_dict_vals_postprocess = False self.encoder_settings.decode_dict_vals_postprocess = False
self.encoder_settings.decode_dict_vals_table = self._SYNTAX_MAPPING self.encoder_settings.decode_dict_vals_table = self._SYNTAX_MAPPING
self.encoder_settings.decode_dict_vals_table_keygen = get_syntax self.encoder_settings.decode_dict_vals_table_keygen = self.get_syntax
self.encoder_settings.decode_postprocessor = lambda x: string.lower(x) self.encoder_settings.decode_postprocessor = lambda x: string.lower(x)
try: try:
self.ldap_uri = ldap_uri or api.env.ldap_uri self.ldap_uri = ldap_uri or api.env.ldap_uri
@ -253,6 +241,15 @@ class ldap2(CrudBackend, Encoder):
def __str__(self): def __str__(self):
return self.ldap_uri return self.ldap_uri
def get_syntax(self, attr, value):
if not self.schema:
return None
obj = self.schema.get_obj(_ldap.schema.AttributeType, attr)
if obj is not None:
return obj.syntax
else:
return None
@encode_args(2, 3, 'bind_dn', 'bind_pw') @encode_args(2, 3, 'bind_dn', 'bind_pw')
def create_connection(self, ccache=None, bind_dn='', bind_pw='', def create_connection(self, ccache=None, bind_dn='', bind_pw='',
tls_cacertfile=None, tls_certfile=None, tls_keyfile=None, tls_cacertfile=None, tls_certfile=None, tls_keyfile=None,
@ -272,6 +269,7 @@ class ldap2(CrudBackend, Encoder):
Extends backend.Connectible.create_connection. Extends backend.Connectible.create_connection.
""" """
global _schema
if tls_cacertfile is not None: if tls_cacertfile is not None:
_ldap.set_option(_ldap.OPT_X_TLS_CACERTFILE, tls_cacertfile) _ldap.set_option(_ldap.OPT_X_TLS_CACERTFILE, tls_cacertfile)
if tls_certfile is not None: if tls_certfile is not None:
@ -292,6 +290,9 @@ class ldap2(CrudBackend, Encoder):
conn.simple_bind_s(bind_dn, bind_pw) conn.simple_bind_s(bind_dn, bind_pw)
except _ldap.LDAPError, e: except _ldap.LDAPError, e:
_handle_errors(e, **{}) _handle_errors(e, **{})
if self.schema is None and _schema is None:
self.schema = get_schema(self.ldap_uri, conn)
return conn return conn
def destroy_connection(self): def destroy_connection(self):
@ -534,14 +535,15 @@ class ldap2(CrudBackend, Encoder):
filter = self.make_filter(search_kw, rules=self.MATCH_ALL) filter = self.make_filter(search_kw, rules=self.MATCH_ALL)
return self.find_entries(filter, attrs_list, base_dn)[0][0] return self.find_entries(filter, attrs_list, base_dn)[0][0]
def get_entry(self, dn, attrs_list=None, normalize=True): def get_entry(self, dn, attrs_list=None, time_limit=None,
size_limit=None, normalize=True):
""" """
Get entry (dn, entry_attrs) by dn. Get entry (dn, entry_attrs) by dn.
Keyword arguments: Keyword arguments:
attrs_list - list of attributes to return, all if None (default None) attrs_list - list of attributes to return, all if None (default None)
""" """
return self.find_entries(None, attrs_list, dn, self.SCOPE_BASE, normalize=normalize)[0][0] return self.find_entries(None, attrs_list, dn, self.SCOPE_BASE, time_limit=time_limit, size_limit=size_limit, normalize=normalize)[0][0]
def get_ipa_config(self): def get_ipa_config(self):
"""Returns the IPA configuration entry (dn, entry_attrs).""" """Returns the IPA configuration entry (dn, entry_attrs)."""
@ -561,6 +563,16 @@ class ldap2(CrudBackend, Encoder):
""" """
global _upg global _upg
if _upg is None:
try:
upg_entry = self.conn.search_s(
'cn=UPG Template, %s' % api.env.basedn, _ldap.SCOPE_BASE,
attrlist=['*']
)[0]
_upg = True
except _ldap.NO_SUCH_OBJECT, e:
_upg = False
return _upg return _upg
@encode_args(1, 2) @encode_args(1, 2)