def __init__(self, props, cfg): context = LDAPNode(name=cfg.baseDN, props=props) context.search_filter = cfg.queryFilter context.search_scope = int(cfg.scope) context.child_defaults = dict() context.child_defaults['objectClass'] = cfg.objectClasses context.child_defaults.update(cfg.defaults) for oc in cfg.objectClasses: for key, val in creation_defaults.get(oc, dict()).items(): if key not in context.child_defaults: context.child_defaults[key] = val # if cfg.member_relation: # context.search_relation = cfg.member_relation self._rdn_attr = cfg.attrmap['rdn'] self._key_attr = cfg.attrmap['id'] if self._key_attr not in cfg.attrmap: cfg.attrmap[self._key_attr] = self._key_attr self._login_attr = None if cfg.attrmap.get('login') \ and cfg.attrmap['login'] != cfg.attrmap['id']: self._login_attr = cfg.attrmap['login'] self.expiresAttr = getattr(cfg, 'expiresAttr', None) self.expiresUnit = getattr(cfg, 'expiresUnit', None) self.principal_attrmap = cfg.attrmap self.principal_attraliaser = DictAliaser(cfg.attrmap, cfg.strict) self.context = context self.context.root.search_attrlist = list(sorted(set( self.principal_attrmap.values() + ['rdn', 'memberOf'] )))
def __init__(self, props, cfg): context = LDAPNode(name=cfg.baseDN, props=props) context.search_filter = cfg.queryFilter context.search_scope = int(cfg.scope) context.child_defaults = dict() context.child_defaults['objectClass'] = cfg.objectClasses if hasattr(cfg, 'defaults'): context.child_defaults.update(cfg.defaults) for oc in cfg.objectClasses: for key, val in creation_defaults.get(oc, dict()).items(): if not key in context.child_defaults: context.child_defaults[key] = val # XXX: make these attrs public context._key_attr = cfg.attrmap['id'] context._rdn_attr = cfg.attrmap['rdn'] #if cfg.member_relation: # context.search_relation = cfg.member_relation context._seckey_attrs = ('dn', ) if cfg.attrmap.get('login') \ and cfg.attrmap['login'] != cfg.attrmap['id']: context._seckey_attrs += (cfg.attrmap['login'], ) context._load_keys() self.expiresAttr = getattr(cfg, 'expiresAttr', None) self.expiresUnit = getattr(cfg, 'expiresUnit', None) self.principal_attrmap = cfg.attrmap self.principal_attraliaser = DictAliaser(cfg.attrmap, cfg.strict) self.context = context
def __init__(self, props, cfg): context = LDAPNode(name=cfg.baseDN, props=props) context.search_filter = cfg.queryFilter context.search_scope = int(cfg.scope) context.child_defaults = dict() context.child_defaults["objectClass"] = cfg.objectClasses context.child_defaults.update(cfg.defaults) for oc in cfg.objectClasses: for key, val in creation_defaults.get(oc, dict()).items(): if key not in context.child_defaults: context.child_defaults[key] = val # if cfg.member_relation: # context.search_relation = cfg.member_relation self._rdn_attr = cfg.attrmap["rdn"] self._key_attr = cfg.attrmap["id"] if self._key_attr not in cfg.attrmap: cfg.attrmap[self._key_attr] = self._key_attr self._login_attr = None if cfg.attrmap.get("login") and cfg.attrmap["login"] != cfg.attrmap["id"]: self._login_attr = cfg.attrmap["login"] self.expiresAttr = getattr(cfg, "expiresAttr", None) self.expiresUnit = getattr(cfg, "expiresUnit", None) self.principal_attrmap = cfg.attrmap self.principal_attraliaser = DictAliaser(cfg.attrmap, cfg.strict) self.context = context
def __init__(self, props, cfg): context = LDAPNode(name=cfg.baseDN, props=props) context.search_filter = cfg.queryFilter context.search_scope = int(cfg.scope) context.child_defaults = dict() context.child_defaults['objectClass'] = cfg.objectClasses if hasattr(cfg, 'defaults'): context.child_defaults.update(cfg.defaults) for oc in cfg.objectClasses: for key, val in creation_defaults.get(oc, dict()).items(): if not key in context.child_defaults: context.child_defaults[key] = val # XXX: make these attrs public context._key_attr = cfg.attrmap['id'] context._rdn_attr = cfg.attrmap['rdn'] #if cfg.member_relation: # context.search_relation = cfg.member_relation context._seckey_attrs = ('dn',) if cfg.attrmap.get('login') \ and cfg.attrmap['login'] != cfg.attrmap['id']: context._seckey_attrs += (cfg.attrmap['login'],) context._load_keys() self.expiresAttr = getattr(cfg, 'expiresAttr', None) self.expiresUnit = getattr(cfg, 'expiresUnit', None) self.principal_attrmap = cfg.attrmap self.principal_attraliaser = DictAliaser(cfg.attrmap, cfg.strict) self.context = context
def test_DictAliaser(self): # A dict aliaser takes a dictionary as base for aliasing da = DictAliaser([('alias1', 'key1'), ('alias2', 'key2')]) self.assertEqual(da.alias('key1'), 'alias1') self.assertEqual(da.unalias('alias2'), 'key2') # By default, aliasing is strict, which means that only key/value pairs # set in aliaser are valid err = self.expectError(KeyError, da.alias, 'foo') self.assertEqual(str(err), '\'foo\'') err = self.expectError(KeyError, da.unalias, 'foo') self.assertEqual(str(err), '\'foo\'') # By setting strict to False, inexistent keys are returned as fallback da = DictAliaser([('alias1', 'key1'), ('alias2', 'key2')], strict=False) self.assertEqual(da.alias('foo'), 'foo') self.assertEqual(da.unalias('foo'), 'foo')
def test_DictAliaser(self): # A dict aliaser takes a dictionary as base for aliasing da = DictAliaser([('alias1', 'key1'), ('alias2', 'key2')]) self.assertEqual(da.alias('key1'), 'alias1') self.assertEqual(da.unalias('alias2'), 'key2') # By default, aliasing is strict, which means that only key/value pairs # set in aliaser are valid err = self.expect_error(KeyError, da.alias, 'foo') self.assertEqual(str(err), '\'foo\'') err = self.expect_error(KeyError, da.unalias, 'foo') self.assertEqual(str(err), '\'foo\'') # By setting strict to False, inexistent keys are returned as fallback da = DictAliaser( [('alias1', 'key1'), ('alias2', 'key2')], strict=False ) self.assertEqual(da.alias('foo'), 'foo') self.assertEqual(da.unalias('foo'), 'foo')
class LDAPPrincipals(OdictStorage): principal_attrmap = default(None) principal_attraliaser = default(None) @override def __init__(self, props, cfg): context = LDAPNode(name=cfg.baseDN, props=props) context.search_filter = cfg.queryFilter context.search_scope = int(cfg.scope) context.child_defaults = dict() context.child_defaults["objectClass"] = cfg.objectClasses context.child_defaults.update(cfg.defaults) for oc in cfg.objectClasses: for key, val in creation_defaults.get(oc, dict()).items(): if key not in context.child_defaults: context.child_defaults[key] = val # if cfg.member_relation: # context.search_relation = cfg.member_relation self._rdn_attr = cfg.attrmap["rdn"] self._key_attr = cfg.attrmap["id"] if self._key_attr not in cfg.attrmap: cfg.attrmap[self._key_attr] = self._key_attr self._login_attr = None if cfg.attrmap.get("login") and cfg.attrmap["login"] != cfg.attrmap["id"]: self._login_attr = cfg.attrmap["login"] self.expiresAttr = getattr(cfg, "expiresAttr", None) self.expiresUnit = getattr(cfg, "expiresUnit", None) self.principal_attrmap = cfg.attrmap self.principal_attraliaser = DictAliaser(cfg.attrmap, cfg.strict) self.context = context @default def idbydn(self, dn, strict=False): """Return a principal's id for a given dn. Raise KeyError if not enlisted. """ # XXX: rename to id_by_dn # XXX: what was strict good for? remove # if strict: # raise KeyError(dn) try: search = self.context.ldap_session.search res = search(baseDN=dn.encode("utf-8"))[0] return res[1][self._key_attr][0].decode("utf-8") except ldap.NO_SUCH_OBJECT: raise KeyError(dn) @override @property def ids(self): return list(self.__iter__()) @default @locktree def __delitem__(self, key): principal = self[key] context = principal.context del context.parent[context.name] del self.storage[key] @default @locktree def __getitem__(self, key): key = decode_utf8(key) try: return self.storage[key] except KeyError: criteria = {self._key_attr: key} attrlist = ["rdn", self._key_attr] res = self.context.search(criteria=criteria, attrlist=attrlist) if not res: raise KeyError(key) if len(res) > 1: msg = u'More than one principal with id "{0}" found.' logger.warning(msg.format(key)) prdn = res[0][1]["rdn"] if prdn in self.context._deleted_children: raise KeyError(key) dn = res[0][0] # XXX: use explode_dn path = dn.split(",")[: len(self.context.DN.split(",")) * -1] context = self.context for rdn in reversed(path): context = context[rdn] principal = self.principal_factory(context, attraliaser=self.principal_attraliaser) principal.__name__ = key principal.__parent__ = self self.storage[key] = principal return principal @default @locktree def __iter__(self): attrlist = ["rdn", self._key_attr] for principal in self.context.batched_search(attrlist=attrlist): prdn = principal[1]["rdn"] if prdn in self.context._deleted_children: continue yield principal[1][self._key_attr][0] for principal in self.context._added_children: yield self.context[principal].attrs[self._key_attr] @default @locktree def __setitem__(self, name, value): if not isinstance(value, self.principal_factory): raise ValueError(u"Given value not instance of '{0}'".format(self.principal_factory.__name__)) # XXX: check if there is valid user context exists = False try: self[name] exists = True except KeyError: pass if exists: raise KeyError(u"Principal with id '{0}' already exists.".format(name)) value.__name__ = name value.__parent__ = self self.storage[name] = value @default @property def changed(self): return self.context.changed @default @locktree def invalidate(self, key=None): """Invalidate LDAPPrincipals. """ if key is None: self.context.invalidate() self.storage.clear() return try: principal = self.storage[key] principal.context.parent.invalidate(principal.context.name) del self.storage[key] except KeyError: pass @default @locktree def __call__(self): self.context() @default def _alias_dict(self, dct): ret = dict() for key, val in self.principal_attraliaser.iteritems(): for k, v in dct.iteritems(): if val == k: ret[key] = v return ret @default def _unalias_list(self, lst): if lst is None: return None unalias = self.principal_attraliaser.unalias return [unalias(x) for x in lst] @default def _unalias_dict(self, dct): if dct is None: return None unalias = self.principal_attraliaser.unalias unaliased_dct = dict([(unalias(key), val) for key, val in dct.iteritems()]) return unaliased_dct @default def search( self, criteria=None, attrlist=None, exact_match=False, or_search=False, or_keys=None, or_values=None, page_size=None, cookie=None, ): search_attrlist = [self._key_attr] if attrlist is not None and self._key_attr not in attrlist: search_attrlist += attrlist try: results = self.context.search( criteria=self._unalias_dict(criteria), attrlist=self._unalias_list(search_attrlist), exact_match=exact_match, or_search=or_search, or_keys=or_keys, or_values=or_values, page_size=page_size, cookie=cookie, ) except ldap.NO_SUCH_OBJECT: return [] if type(results) is tuple: results, cookie = results if attrlist is not None: _results = list() for _, att in results: user_id = att[self._key_attr][0] aliased = self._alias_dict(att) keys = aliased.keys() for key in keys: if key not in attrlist: del aliased[key] _results.append((user_id, aliased)) results = _results else: results = [att[self._key_attr][0] for _, att in results] if cookie is not None: return results, cookie return results @default @locktree def create(self, pid, **kw): # XXX: mechanism for defining a target container if scope is SUBTREE # create principal with LDAPNode as context context = LDAPNode() principal = self.principal_factory(context, attraliaser=self.principal_attraliaser) # ensure id on attributes kw["id"] = pid # avoid overwriting key attribute if given in kw if self._key_attr in kw: del kw[self._key_attr] # set additional attributes on principal for k, v in kw.items(): principal.attrs[k] = v # set principal to self self[pid] = principal # if setting principal has been successful, hook up principal context # to ldap tree rdn = u"{0}={1}".format(self._rdn_attr, principal.context.attrs[self._rdn_attr]) self.context[rdn] = context # return newly created principal return self[pid]
class LDAPPrincipals(OdictStorage): principal_attrmap = default(None) principal_attraliaser = default(None) @override def __init__(self, props, cfg): context = LDAPNode(name=cfg.baseDN, props=props) context.search_filter = cfg.queryFilter context.search_scope = int(cfg.scope) context.child_defaults = dict() context.child_defaults['objectClass'] = cfg.objectClasses context.child_defaults.update(cfg.defaults) for oc in cfg.objectClasses: for key, val in creation_defaults.get(oc, dict()).items(): if key not in context.child_defaults: context.child_defaults[key] = val # if cfg.member_relation: # context.search_relation = cfg.member_relation self._rdn_attr = cfg.attrmap['rdn'] self._key_attr = cfg.attrmap['id'] if self._key_attr not in cfg.attrmap: cfg.attrmap[self._key_attr] = self._key_attr self._login_attr = None if cfg.attrmap.get('login') \ and cfg.attrmap['login'] != cfg.attrmap['id']: self._login_attr = cfg.attrmap['login'] self.expiresAttr = getattr(cfg, 'expiresAttr', None) self.expiresUnit = getattr(cfg, 'expiresUnit', None) self.principal_attrmap = cfg.attrmap self.principal_attraliaser = DictAliaser(cfg.attrmap, cfg.strict) self.context = context self.context.root.search_attrlist = list(sorted(set( self.principal_attrmap.values() + ['rdn', 'memberOf'] ))) @default def idbydn(self, dn, strict=False): """Return a principal's id for a given dn. Raise KeyError if not enlisted. """ node = self.context.node_by_dn(dn, strict) node.attrs.load() return node.attrs[self._key_attr] @override @property def ids(self): return list(self.__iter__()) @default @locktree def __delitem__(self, key): principal = self[key] context = principal.context del context.parent[context.name] del self.storage[key] @default @locktree def __getitem__(self, key): try: return self.storage[key] except KeyError: criteria = {self._key_attr: key} attrlist = self.context.root.search_attrlist res = self.context.search(criteria=criteria, attrlist=attrlist) if not res: raise KeyError(key) if len(res) > 1: msg = 'More than one principal with id "{0}" found.' logger.warning(msg.format(key)) prdn = res[0][1]['rdn'] if prdn in self.context._deleted_children: raise KeyError(key) dn = res[0][0] # XXX: use explode_dn path = dn.split(',')[:len(self.context.DN.split(',')) * -1] context = self.context for rdn in reversed(path): if rdn not in context.storage: val = context.child_factory() val.__name__ = rdn val.__parent__ = context # remember DN val._dn = '{0:s},{1:s}'.format( rdn, context._dn or context.__name__) val._ldap_session = context.ldap_session context.storage[rdn] = val context = context[rdn] context.search_criteria = criteria principal = self.principal_factory( context, attraliaser=self.principal_attraliaser ) principal.__name__ = key principal.__parent__ = self self.storage[key] = principal return principal @default @locktree def __iter__(self): attrlist = self.context.root.search_attrlist for principal in self.context.batched_search(attrlist=attrlist): prdn = principal[1]['rdn'] if prdn in self.context._deleted_children: continue yield principal[1][self._key_attr][0] for principal in self.context._added_children: yield self.context[principal].attrs[self._key_attr] @default @locktree def __setitem__(self, name, value): if not isinstance(value, self.principal_factory): raise ValueError("Given value not instance of '{0}'".format( self.principal_factory.__name__ )) # XXX: check if there is valid user context exists = False try: self[name] exists = True except KeyError: pass if exists: raise KeyError( "Principal with id '{0}' already exists.".format(name) ) value.__name__ = name value.__parent__ = self self.storage[name] = value @default @property def changed(self): return self.context.changed @default @locktree def invalidate(self, key=None): """Invalidate LDAPPrincipals. """ if key is None: self.context.invalidate() self.storage.clear() return try: principal = self.storage[key] principal.context.parent.invalidate(principal.context.name) del self.storage[key] except KeyError: pass @default @locktree def __call__(self): self.context() @default def _alias_dict(self, dct): ret = dict() for key, val in self.principal_attraliaser.iteritems(): for k, v in dct.iteritems(): if val == k: ret[key] = v return ret @default def _unalias_list(self, lst): if lst is None: return None unalias = self.principal_attraliaser.unalias return [unalias(x) for x in lst] @default def _unalias_dict(self, dct): if dct is None: return None unalias = self.principal_attraliaser.unalias unaliased_dct = dict( [(unalias(key), val) for key, val in dct.iteritems()]) return unaliased_dct @default def search(self, criteria=None, attrlist=None, exact_match=False, or_search=False, or_keys=None, or_values=None, page_size=None, cookie=None): search_attrlist = [self._key_attr] if attrlist is not None: search_attrlist += attrlist try: results = self.context.search( criteria=self._unalias_dict(criteria), attrlist=self._unalias_list(search_attrlist), exact_match=exact_match, or_search=or_search, or_keys=or_keys, or_values=or_values, page_size=page_size, cookie=cookie ) except ldap.NO_SUCH_OBJECT: return [] if type(results) is tuple: results, cookie = results if attrlist is not None: _results = list() for _, att in results: try: user_id = att[self._key_attr][0] except (KeyError, IndexError): continue aliased = self._alias_dict(att) # append all matching aliases (in addition to attrlist oness) _results.append((user_id, aliased)) results = _results else: results = [att[self._key_attr][0] for _, att in results] if cookie is not None: return results, cookie return results @default @locktree def create(self, pid, **kw): # XXX: mechanism for defining a target container if scope is SUBTREE # create principal with LDAPNode as context context = LDAPNode() principal = self.principal_factory( context, attraliaser=self.principal_attraliaser ) # ensure id on attributes kw['id'] = pid # avoid overwriting key attribute if given in kw if self._key_attr in kw: del kw[self._key_attr] # set additional attributes on principal for k, v in kw.items(): principal.attrs[k] = v # set principal to self self[pid] = principal # if setting principal has been successful, hook up principal context # to ldap tree rdn = '{0}={1}'.format( self._rdn_attr, principal.context.attrs[self._rdn_attr] ) self.context[rdn] = context # return newly created principal return self[pid]
class LDAPPrincipals(OdictStorage): principal_attrmap = default(None) principal_attraliaser = default(None) @override def __init__(self, props, cfg): context = LDAPNode(name=cfg.baseDN, props=props) context.search_filter = cfg.queryFilter context.search_scope = int(cfg.scope) context.child_defaults = dict() context.child_defaults['objectClass'] = cfg.objectClasses if hasattr(cfg, 'defaults'): context.child_defaults.update(cfg.defaults) for oc in cfg.objectClasses: for key, val in creation_defaults.get(oc, dict()).items(): if not key in context.child_defaults: context.child_defaults[key] = val # XXX: make these attrs public context._key_attr = cfg.attrmap['id'] context._rdn_attr = cfg.attrmap['rdn'] #if cfg.member_relation: # context.search_relation = cfg.member_relation context._seckey_attrs = ('dn',) if cfg.attrmap.get('login') \ and cfg.attrmap['login'] != cfg.attrmap['id']: context._seckey_attrs += (cfg.attrmap['login'],) context._load_keys() self.expiresAttr = getattr(cfg, 'expiresAttr', None) self.expiresUnit = getattr(cfg, 'expiresUnit', None) self.principal_attrmap = cfg.attrmap self.principal_attraliaser = DictAliaser(cfg.attrmap, cfg.strict) self.context = context @default def idbydn(self, dn, strict=False): """Return a principal's id for a given dn. Raise KeyError if not enlisted. """ self.context.keys() idsbydn = self.context._seckeys['dn'] try: return idsbydn[dn] except KeyError: # It's possible that we got a different string resulting # in the same DN, as every components can have individual # comparison rules (see also node.ext.ldap._node.LDAPNode.DN). # We leave the job to LDAP and try again with the resulting DN. # # This was introduced because a customer has group # member attributes where the DN string differs. # # This would not be necessary, if an LDAP directory # is consistent, i.e does not use different strings to # talk about the same. # # Normalization of DN in python would also be a # solution, but requires implementation of all comparison # rules defined in schemas. Maybe we can retrieve them # from LDAP. if strict: raise KeyError(dn) search = self.context.ldap_session.search try: dn = search(baseDN=dn.encode('utf-8'))[0][0] except ldap.NO_SUCH_OBJECT: raise KeyError(dn) return idsbydn[dn.decode('utf-8')] @override @property def ids(self): return self.context.keys() @default @locktree def __delitem__(self, key): key = decode_utf8(key) del self.context[key] try: del self.storage[key] except KeyError: pass @default @locktree def __getitem__(self, key): key = decode_utf8(key) try: return self.storage[key] except KeyError: principal = self.principal_factory( self.context[key], attraliaser=self.principal_attraliaser) principal.__name__ = self.context[key].name principal.__parent__ = self self.storage[key] = principal return principal @default @locktree def __iter__(self): return self.context.__iter__() @default @locktree def __setitem__(self, name, vessel): """XXX: mechanism for defining a target container if search scope is SUBTREE """ try: attrs = vessel.attrs except AttributeError: raise ValueError(u"no attributes found, cannot convert.") if name in self: raise KeyError(u"Key already exists: '%s'." % (name,)) nextvessel = AttributedNode() nextvessel.__name__ = name nextvessel.attribute_access_for_attrs = False principal = self.principal_factory( nextvessel, attraliaser=self.principal_attraliaser) principal.__name__ = name principal.__parent__ = self # XXX: cache for key, val in attrs.iteritems(): principal.attrs[key] = val self.context[name] = nextvessel @default @property def changed(self): return self.context.changed @default @locktree def invalidate(self, key=None): """Invalidate LDAPPrincipals. """ key = decode_utf8(key) self.context.invalidate(key) if key is None: self.storage.clear() return try: del self.storage[key] except KeyError: pass @default @locktree def __call__(self): self.context() @default def _alias_dict(self, dct): # XXX: seem to be not reached at all #if dct is None: # return None # XXX: this code does not work if multiple keys map to same value #alias = self.principal_attraliaser.alias #aliased_dct = dict( # [(alias(key), val) for key, val in dct.iteritems()]) #return aliased_dct # XXX: generalization in aliaser needed ret = dict() for key, val in self.principal_attraliaser.iteritems(): for k, v in dct.iteritems(): if val == k: ret[key] = v return ret @default def _unalias_list(self, lst): if lst is None: return None unalias = self.principal_attraliaser.unalias return [unalias(x) for x in lst] @default def _unalias_dict(self, dct): if dct is None: return None unalias = self.principal_attraliaser.unalias unaliased_dct = dict( [(unalias(key), val) for key, val in dct.iteritems()]) return unaliased_dct @default def search(self, criteria=None, attrlist=None, exact_match=False, or_search=False, or_keys=None, or_values=None, page_size=None, cookie=None): results = self.context.search( criteria=self._unalias_dict(criteria), attrlist=self._unalias_list(attrlist), exact_match=exact_match, or_search=or_search, or_keys=or_keys, or_values=or_values, page_size=page_size, cookie=cookie ) if type(results) is tuple: results, cookie = results if attrlist is not None: results = [(uid, self._alias_dict(att)) for uid, att in results] if cookie is not None: return results, cookie return results @default @locktree def create(self, pid, **kw): vessel = AttributedNode() for k, v in kw.items(): vessel.attrs[k] = v self[pid] = vessel return self[pid]
class AliasDict(dict): aliaser = DictAliaser(data=(('foo', 'f00'), ('bar', 'b4r')))
class LDAPPrincipals(OdictStorage): principal_attrmap = default(None) principal_attraliaser = default(None) @override def __init__(self, props, cfg): context = LDAPNode(name=cfg.baseDN, props=props) context.search_filter = cfg.queryFilter context.search_scope = int(cfg.scope) context.child_defaults = dict() context.child_defaults['objectClass'] = cfg.objectClasses if hasattr(cfg, 'defaults'): context.child_defaults.update(cfg.defaults) for oc in cfg.objectClasses: for key, val in creation_defaults.get(oc, dict()).items(): if not key in context.child_defaults: context.child_defaults[key] = val # XXX: make these attrs public context._key_attr = cfg.attrmap['id'] context._rdn_attr = cfg.attrmap['rdn'] #if cfg.member_relation: # context.search_relation = cfg.member_relation context._seckey_attrs = ('dn', ) if cfg.attrmap.get('login') \ and cfg.attrmap['login'] != cfg.attrmap['id']: context._seckey_attrs += (cfg.attrmap['login'], ) context._load_keys() self.expiresAttr = getattr(cfg, 'expiresAttr', None) self.expiresUnit = getattr(cfg, 'expiresUnit', None) self.principal_attrmap = cfg.attrmap self.principal_attraliaser = DictAliaser(cfg.attrmap, cfg.strict) self.context = context @default def idbydn(self, dn, strict=False): """Return a principal's id for a given dn. Raise KeyError if not enlisted. """ self.context.keys() idsbydn = self.context._seckeys['dn'] try: return idsbydn[dn] except KeyError: # It's possible that we got a different string resulting # in the same DN, as every components can have individual # comparison rules (see also node.ext.ldap._node.LDAPNode.DN). # We leave the job to LDAP and try again with the resulting DN. # # This was introduced because a customer has group # member attributes where the DN string differs. # # This would not be necessary, if an LDAP directory # is consistent, i.e does not use different strings to # talk about the same. # # Normalization of DN in python would also be a # solution, but requires implementation of all comparison # rules defined in schemas. Maybe we can retrieve them # from LDAP. if strict: raise KeyError(dn) search = self.context.ldap_session.search try: dn = search(baseDN=dn.encode('utf-8'))[0][0] except ldap.NO_SUCH_OBJECT: raise KeyError(dn) return idsbydn[dn.decode('utf-8')] @override @property def ids(self): return self.context.keys() @default @locktree def __delitem__(self, key): key = decode_utf8(key) del self.context[key] try: del self.storage[key] except KeyError: pass @default @locktree def __getitem__(self, key): key = decode_utf8(key) try: return self.storage[key] except KeyError: principal = self.principal_factory( self.context[key], attraliaser=self.principal_attraliaser) principal.__name__ = self.context[key].name principal.__parent__ = self self.storage[key] = principal return principal @default @locktree def __iter__(self): return self.context.__iter__() @default @locktree def __setitem__(self, name, vessel): """XXX: mechanism for defining a target container if search scope is SUBTREE """ try: attrs = vessel.attrs except AttributeError: raise ValueError(u"no attributes found, cannot convert.") if name in self: raise KeyError(u"Key already exists: '%s'." % (name, )) nextvessel = AttributedNode() nextvessel.__name__ = name nextvessel.attribute_access_for_attrs = False principal = self.principal_factory( nextvessel, attraliaser=self.principal_attraliaser) principal.__name__ = name principal.__parent__ = self # XXX: cache for key, val in attrs.iteritems(): principal.attrs[key] = val self.context[name] = nextvessel @default @property def changed(self): return self.context.changed @default @locktree def invalidate(self, key=None): """Invalidate LDAPPrincipals. """ key = decode_utf8(key) self.context.invalidate(key) if key is None: self.storage.clear() return try: del self.storage[key] except KeyError: pass @default @locktree def __call__(self): self.context() @default def _alias_dict(self, dct): # XXX: seem to be not reached at all #if dct is None: # return None # XXX: this code does not work if multiple keys map to same value #alias = self.principal_attraliaser.alias #aliased_dct = dict( # [(alias(key), val) for key, val in dct.iteritems()]) #return aliased_dct # XXX: generalization in aliaser needed ret = dict() for key, val in self.principal_attraliaser.iteritems(): for k, v in dct.iteritems(): if val == k: ret[key] = v return ret @default def _unalias_list(self, lst): if lst is None: return None unalias = self.principal_attraliaser.unalias return [unalias(x) for x in lst] @default def _unalias_dict(self, dct): if dct is None: return None unalias = self.principal_attraliaser.unalias unaliased_dct = dict([(unalias(key), val) for key, val in dct.iteritems()]) return unaliased_dct @default def search(self, criteria=None, attrlist=None, exact_match=False, or_search=False, or_keys=None, or_values=None, page_size=None, cookie=None): results = self.context.search(criteria=self._unalias_dict(criteria), attrlist=self._unalias_list(attrlist), exact_match=exact_match, or_search=or_search, or_keys=or_keys, or_values=or_values, page_size=page_size, cookie=cookie) if type(results) is tuple: results, cookie = results if attrlist is not None: results = [(uid, self._alias_dict(att)) for uid, att in results] if cookie is not None: return results, cookie return results @default @locktree def create(self, pid, **kw): vessel = AttributedNode() for k, v in kw.items(): vessel.attrs[k] = v self[pid] = vessel return self[pid]