def test_decode(self): self.assertEqual(decode(b'foo'), u'foo') self.assertEqual(decode((b'foo', u'bar')), (u'foo', u'bar')) self.assertEqual(decode({b'foo': b'bar'}), {u'foo': u'bar'}) self.assertEqual(decode(b'fo\xe4'), b'fo\xe4') node = BaseNode() node.allow_non_node_childs = True node[b'foo'] = b'\xc3\xa4' self.assertEqual(decode(node), {u'foo': u'\xe4'})
def __iter__(self): if self.name is None: return cookie = '' while True: try: res = self.ldap_session.search( scope=ONELEVEL, baseDN=encode(self.DN), attrlist=[''], page_size=self._page_size, cookie=cookie, ) except NO_SUCH_OBJECT: # happens if not persisted yet res = list() if isinstance(res, tuple): res, cookie = res for dn, _ in res: key = decode(explode_dn(dn)[0]) # do not yield if node is supposed to be deleted if key not in self._deleted_children: yield key if not cookie: break # also yield keys of children not persisted yet. for key in self._added_children: yield key
def __repr__(self): dn = self.DN or u'(dn not set)' if self.parent is None: ret = u'<{} - {}>'.format(dn, self.changed) else: ret = u'<{}:{} - {}>'.format(dn, self.name, self.changed) return decode(ret.encode('ascii', 'replace'))
def getGroupById(self, group_id): """ Returns the portal_groupdata-ish object for a group corresponding to this id. None if group does not exist here! """ groups = self.groups if not groups or group_id not in self.getGroupIds(): return None ugmgroup = self.groups[group_id] title = ugmgroup.attrs.get('title', None) group = PloneGroup(ugmgroup.id, title).__of__(self) pas = self._getPAS() plugins = pas.plugins # add properties for propfinder_id, propfinder in \ plugins.listPlugins(pas_interfaces.IPropertiesPlugin): data = propfinder.getPropertiesForUser(group, None) if not data: continue group.addPropertysheet(propfinder_id, decode(data)) # add subgroups group._addGroups( pas._getGroupsForPrincipal(group, None, plugins=plugins)) # add roles for rolemaker_id, rolemaker in \ plugins.listPlugins(pas_interfaces.IRolesPlugin): roles = rolemaker.getRolesForPrincipal(group, None) if not roles: continue group._addRoles(roles) return group
def __setitem__(_next, self, key, val): if val in [u'', b'', UNSET]: if key in self: del self[key] return if not self.is_binary(key): val = decode(val) key = ensure_text(key) _next(self, key, val) self._set_attrs_modified()
def DN(self): # For one and the same entry, ldap will always return the same DN. # However, depending on the individual syntax definition of the DN's # components there might be a multitude of strings that equal the same # DN, e.g. for cn: # 'cn=foo bar' == 'cn=foo bar' -> True if self.parent: return self.parent.child_dn(self.name) if self.name: # We should not have a name if we are not a root node. return decode(self.name) return u''
def _calculate_key(self, dn, attrs): if self._key_attr == 'rdn': # explode_dn is ldap world key = decode(explode_dn(encode(dn))[0]) else: key = attrs[self._key_attr] if isinstance(key, list): if len(key) != 1: msg = u"Expected one value for '%s' " % (self._key_attr,) msg += u"not %s: '%s'." % (len(key), key) raise KeyError(msg) key = key[0] return key
def _calculate_key(self, dn, attrs): if self._key_attr == 'rdn': # explode_dn is ldap world key = explode_dn(encode(dn))[0] else: key = attrs[self._key_attr] if isinstance(key, list): if len(key) != 1: msg = u"Expected one value for '%s' " % (self._key_attr,) msg += u"not %s: '%s'." % (len(key), key) raise KeyError(msg) key = key[0] return decode(key)
def DN(self): """ ATTENTION: For one and the same entry, ldap will always return the same DN. However, depending on the individual syntax definition of the DN's components there might be a multitude of strings that equal the same DN, e.g. for cn: 'cn=foo bar' == 'cn=foo bar' -> True """ if self.parent is not None: return self.parent.child_dn(self.name) elif self.name is not None: # We should not have a name if we are not a root node. return decode(self.name) else: return u''
def __delitem__(self, key): # do not delete immediately. Just mark LDAPNode to be deleted. if isinstance(key, str): key = decode(key) # value not persistent yet, remove from storage and add list if key in self._added_children: del self.storage[key] self._added_children.remove(key) self.changed = False return val = self[key] val._action = ACTION_DELETE # this will also trigger the changed chain val.changed = True self._deleted_children.add(key)
def __getitem__(self, key): """Here nodes are created for keys, if they do not exist already """ if isinstance(key, str): key = decode(key) if not self._keys: self._load_keys() if self._keys[key] is not None: return self.storage[key] val = self.child_factory() val._ldap_session = self.ldap_session val.__name__ = key val.__parent__ = self self.storage[key] = self._keys[key] = val return val
def __getitem__(self, key): """Here nodes are created for keys, if they do not exist already """ if isinstance(key, str): key = decode(key) if not self._keys: self._load_keys() if self._keys[key]: return self.storage[key] val = self.child_factory() val._ldap_session = self.ldap_session val.__name__ = key val.__parent__ = self self.storage[key] = val self._keys[key] = True return val
def __delitem__(self, key): """Do not delete immediately. Just mark LDAPNode to be deleted and remove key from self._keys. """ if isinstance(key, str): key = decode(key) val = self[key] val._action = ACTION_DELETE # this will also trigger the changed chain val.changed = True del self._keys[key] try: self._deleted.append(val) except AttributeError: self._deleted = list() self._deleted.append(val)
def _perform(self, function, *args, **kwargs): """Try to perform the given function with the given argument. If LDAP directory is down, bind again and retry given function. XXX: * Improve retry logic in LDAPSession * Extend LDAPSession object to handle Fallback server(s) """ # XXX BUG: # It is complete wrong to encode/ decode in here every string # LDAP handles binary data too and so fetching or setting binary # data fails. We need to refactor this part. args = encode(args) kwargs = encode(kwargs) if self._communicator._con is None: self._communicator.bind() return decode(function(*args, **kwargs))
def __init__(self, principal, plugin): """Instanciate LDAPUserPropertySheet. @param principal: user id or group id @param plugin: LDAPPlugin instance """ # do not set any non-pickable attribute here, i.e. acquisition wrapped self._plugin = aq_base(plugin) self._properties = dict() self._attrmap = dict() self._ldapprincipal_id = principal.getId() if self._ldapprincipal_id in plugin.users: pcfg = ILDAPUsersConfig(plugin) self._ldapprincipal_type = 'users' else: pcfg = ILDAPGroupsConfig(plugin) self._ldapprincipal_type = 'groups' for k, v in pcfg.attrmap.items(): if k in ['rdn', 'id']: # XXX: maybe 'login' should be editable if existent ?? continue self._attrmap[k] = v ldapprincipal = self._get_ldap_principal() request = getRequest() # XXX: tmp - load props each time they are accessed. if not request or not request.get('_ldap_props_reloaded'): ldapprincipal.attrs.context.load() if request: request['_ldap_props_reloaded'] = 1 for key in self._attrmap: self._properties[key] = ldapprincipal.attrs.get(key, '') if 'fullname' in self._properties: self._properties['fullname'] = ' '.join( map(str.strip, reversed(self._properties['fullname'].split(',')))) UserPropertySheet.__init__(self, principal.getId(), schema=None, **decode(self._properties))
def __getitem__(self, key): # nodes are created for keys, if they do not already exist in memory if isinstance(key, str): key = decode(key) try: return self.storage[key] except KeyError: val = self.child_factory() val.__name__ = key val.__parent__ = self try: res = self.ldap_session.search( scope=BASE, baseDN=val.DN.encode('utf-8'), attrlist=[''], # no need for attrs ) # remember DN val._dn = res[0][0] val._ldap_session = self.ldap_session self.storage[key] = val return val except (NO_SUCH_OBJECT, INVALID_DN_SYNTAX): raise KeyError(key)
def _perform(self, function, *args, **kwargs): """Try to perform the given function with the given argument. If LDAP directory is down, bind again and retry given function. XXX: * Improve retry logic in LDAPSession * Extend LDAPSession object to handle Fallback server(s) """ args = encode(args) kwargs = encode(kwargs) if self._communicator._con is None: self._communicator.bind() # XXX: it seems except block is never reached, call of # self._communicator.bind() above already raises error if # communication fails. And why server down? #try: # return decode(function(*args, **kwargs)) #except ldap.SERVER_DOWN: # self._communicator.bind() # return decode(function(*args, **kwargs)) return decode(function(*args, **kwargs))
def __setitem__(self, key, val): if isinstance(key, str): key = decode(key) if not isinstance(val, LDAPNode): # create one from whatever we got # XXX: raise KeyError instead of trying to create node val = self._create_suitable_node(val) val.__name__ = key val.__parent__ = self val._dn = self.child_dn(key) val._ldap_session = self.ldap_session try: self.ldap_session.search( scope=BASE, baseDN=val.DN.encode('utf-8'), attrlist=[''], # no need for attrs ) except (NO_SUCH_OBJECT, INVALID_DN_SYNTAX): # the value is not yet in the directory val._action = ACTION_ADD val.changed = True self.changed = True self._added_children.add(key) rdn, rdn_val = key.split('=') if rdn not in val.attrs: val._notify_suppress = True val.attrs[rdn] = rdn_val val._notify_suppress = False self.storage[key] = val if self.child_defaults: for k, v in self.child_defaults.items(): if k in val.attrs: # skip default if attribute already exists continue if callable(v): v = v(self, key) val.attrs[k] = v
def __setitem__(_next, self, key, val): if not self.is_binary(key): val = decode(val) key = decode(key) _next(self, key, val) self._set_attrs_modified()
def child_dn(self, key): return decode(self._child_dns[key])
def search(self, queryFilter=None, criteria=None, attrlist=None, relation=None, relation_node=None, exact_match=False, or_search=False, or_keys=None, or_values=None, page_size=None, cookie=None): attrset = set(attrlist or []) attrset.discard('dn') # fetch also the key attribute if not self._key_attr == 'rdn': attrset.add(self._key_attr) # Create queryFilter from all filter definitions # filter for this search ANDed with the default filters defined on self search_filter = LDAPFilter(queryFilter) search_filter &= LDAPDictFilter(criteria, or_search=or_search, or_keys=or_keys, or_values=or_values, ) _filter = LDAPFilter(self.search_filter) _filter &= LDAPDictFilter(self.search_criteria) _filter &= search_filter # relation filters if relation_node is None: relation_node = self relations = [relation, self.search_relation] for relation in relations: if not relation: continue if isinstance(relation, LDAPRelationFilter): _filter &= relation else: _filter &= LDAPRelationFilter(relation_node, relation) # XXX: Is it really good to filter out entries without the key attr or # would it be better to fail? (see also __iter__ secondary key) if self._key_attr != 'rdn' and self._key_attr not in _filter: _filter &= '(%s=*)' % (self._key_attr,) # perform the backend search matches = self.ldap_session.search( str(_filter), self.search_scope, baseDN=encode(self.DN), force_reload=self._reload, attrlist=list(attrset), page_size=page_size, cookie=cookie, ) if type(matches) is tuple: matches, cookie = matches # XXX: Is ValueError appropriate? # XXX: why do we need to fail at all? shouldn't this be about # substring vs equality match? if exact_match and len(matches) > 1: raise ValueError(u"Exact match asked but result not unique") if exact_match and len(matches) == 0: raise ValueError(u"Exact match asked but result length is zero") # extract key and desired attributes res = [] for dn, attrs in matches: key = self._calculate_key(dn, attrs) if attrlist is not None: resattr = dict() for k, v in attrs.iteritems(): if k in attrlist: if self.attrs.is_binary(k): resattr[decode(k)] = v else: resattr[decode(k)] = decode(v) if 'dn' in attrlist: resattr[u'dn'] = decode(dn) res.append((key, resattr)) else: res.append(key) if cookie is not None: return (res, cookie) return res
def _get_baseDN(self): baseDN = self._communicator.baseDN baseDN = decode(baseDN) return baseDN
def _set_baseDN(self, baseDN): if isinstance(baseDN, str): # make sure its utf8 baseDN = decode(baseDN) baseDN = encode(baseDN) self._communicator.baseDN = baseDN
def __setitem__(self, key, val): if isinstance(key, str): key = decode(key) # XXX: scope is search scope, why not add children? # feels like trying to add security the wrong place #if self.search_scope is BASE: # raise NotImplementedError( # u"Seriously? Adding with scope == BASE?") if self._key_attr != 'rdn' and self._rdn_attr is None: raise RuntimeError( u"Adding with key != rdn needs _rdn_attr to be set.") if not isinstance(val, LDAPNode): # create one from whatever we got val = self._create_suitable_node(val) # At this point we need to have an LDAPNode as val if self._key_attr != 'rdn': val.attrs[self._key_attr] = key if val.attrs.get(self._rdn_attr) is None: raise ValueError( u"'%s' needed in node attributes for rdn." % \ (self._rdn_attr,)) else: # set rdn attr if not present rdn, rdn_val = key.split('=') if not rdn in val.attrs: val._notify_suppress = True val.attrs[rdn] = rdn_val val._notify_suppress = False val.__name__ = key val.__parent__ = self val._ldap_session = self.ldap_session if self._keys is None: self._load_keys() try: # a value with key is already in the directory self._keys[key] except KeyError: # the value is not yet in the directory val._action = ACTION_ADD val.changed = True self.changed = True self.storage[key] = val self._keys[key] = val if self._key_attr == 'rdn': rdn = key else: rdn = '%s=%s' % (self._rdn_attr, val.attrs[self._rdn_attr]) self._child_dns[key] = ','.join((rdn, self.DN)) if self.child_defaults: for k, v in self.child_defaults.items(): if k in val.attrs: # skip default if attribute already exists continue if callable(v): v = v(self, key) val.attrs[k] = v
def __getitem__(_next, self, key): if isinstance(key, str): key = decode(key) return _next(self, key)
def search(self, queryFilter=None, criteria=None, attrlist=None, relation=None, relation_node=None, exact_match=False, or_search=False, or_keys=None, or_values=None, page_size=None, cookie=None, get_nodes=False): attrset = set(attrlist or []) attrset.discard('dn') attrset.discard('rdn') # Create queryFilter from all filter definitions # filter for this search ANDed with the default filters defined on self search_filter = LDAPFilter(queryFilter) search_filter &= LDAPDictFilter( criteria, or_search=or_search, or_keys=or_keys, or_values=or_values ) _filter = LDAPFilter(self.search_filter) _filter &= LDAPDictFilter(self.search_criteria) _filter &= search_filter # relation filters if relation_node is None: relation_node = self relations = [relation, self.search_relation] for relation in relations: if not relation: continue if isinstance(relation, LDAPRelationFilter): _filter &= relation else: _filter &= LDAPRelationFilter(relation_node, relation) # perform the backend search logger.debug("LDAP search with filter: \n{0}".format(_filter)) matches = self.ldap_session.search( str(_filter), self.search_scope, baseDN=self.DN, force_reload=self._reload, attrlist=list(attrset), page_size=page_size, cookie=cookie ) if type(matches) is tuple: matches, cookie = matches # check exact match if exact_match and len(matches) > 1: raise ValueError(u"Exact match asked but result not unique") if exact_match and len(matches) == 0: raise ValueError(u"Exact match asked but result length is zero") # extract key and desired attributes res = [] for dn, attrs in matches: dn = decode(dn) if attrlist is not None: resattr = dict() for k, v in six.iteritems(attrs): if k in attrlist: # Check binary binary attribute directly from root # data to avoid initing attrs for a simple search. if k in self.root._binary_attributes: resattr[decode(k)] = v else: resattr[decode(k)] = decode(v) if 'dn' in attrlist: resattr[u'dn'] = dn if 'rdn' in attrlist: rdn = explode_dn(dn)[0] resattr[u'rdn'] = decode(rdn) if get_nodes: res.append((self.node_by_dn(dn, strict=True), resattr)) else: res.append((dn, resattr)) else: if get_nodes: res.append(self.node_by_dn(dn, strict=True)) else: res.append(dn) if cookie is not None: return (res, cookie) return res
def __setitem__(self, key, val): if isinstance(key, str): key = decode(key) # XXX: scope is search scope, why not add children? # feels like trying to add security the wrong place #if self.search_scope is BASE: # raise NotImplementedError( # u"Seriously? Adding with scope == BASE?") if self._key_attr != 'rdn' and self._rdn_attr is None: raise RuntimeError( u"Adding with key != rdn needs _rdn_attr to be set.") if not isinstance(val, LDAPNode): # create one from whatever we got val = self._create_suitable_node(val) # At this point we need to have an LDAPNode as val if self._key_attr != 'rdn': val.attrs[self._key_attr] = key if val.attrs.get(self._rdn_attr) is None: raise ValueError( u"'%s' needed in node attributes for rdn." % \ (self._rdn_attr,)) else: # set rdn attr if not present rdn, rdn_val = key.split('=') if not rdn in val.attrs: val._notify_suppress = True val.attrs[rdn] = rdn_val val._notify_suppress = False val.__name__ = key val.__parent__ = self val._ldap_session = self.ldap_session if self._keys is None: self._load_keys() try: # a value with key is already in the directory self._keys[key] except KeyError: # the value is not yet in the directory val._action = ACTION_ADD val.changed = True self.changed = True self.storage[key] = val self._keys[key] = True if self._key_attr == 'rdn': rdn = key else: rdn = '%s=%s' % (self._rdn_attr, val.attrs[self._rdn_attr]) self._child_dns[key] = ','.join((rdn, self.DN)) if self.child_defaults: for k, v in self.child_defaults.items(): if k in val.attrs: # skip default if attribute already exists continue if callable(v): v = v(self, key) val.attrs[k] = v
def child_dn(self, key): # return child DN for key if self._dn: return u','.join([decode(key), decode(self._dn)]) return u','.join([decode(key), decode(self.name)])
def __delitem__(_next, self, key): if isinstance(key, str): key = decode(key) _next(self, key)
def __setitem__(_next, self, key, val): if isinstance(key, str): key = decode(key) if isinstance(val, str): val = decode(val) return _next(self, key, val)