diff --git a/nova/auth/fakeldap.py b/nova/auth/fakeldap.py index 27dde314..0e95972f 100644 --- a/nova/auth/fakeldap.py +++ b/nova/auth/fakeldap.py @@ -21,37 +21,31 @@ Fake LDAP server for test harnesses. """ -import logging +import json from nova import datastore -SCOPE_SUBTREE = 1 +SCOPE_SUBTREE = 2 MOD_ADD = 0 MOD_DELETE = 1 -SUBS = { - 'groupOfNames': ['novaProject'] -} - class NO_SUCH_OBJECT(Exception): pass def initialize(uri): - return FakeLDAP(uri) + return FakeLDAP() class FakeLDAP(object): - def __init__(self, _uri): - self.keeper = datastore.Keeper('fakeldap') - if self.keeper['objects'] is None: - self.keeper['objects'] = {} def simple_bind_s(self, dn, password): + """This method is ignored, but provided for compatibility""" pass def unbind_s(self): + """This method is ignored, but provided for compatibility""" pass def _paren_groups(self, source): @@ -67,6 +61,7 @@ class FakeLDAP(object): count -= 1 if count == 0: result.append(source[start:pos+1]) + return result def _match_query(self, query, attrs): inner = query[1:-1] @@ -83,63 +78,84 @@ class FakeLDAP(object): return self._match(k, v, attrs) def _subs(self, v): - if v in SUBS: - return [v] + SUBS[v] + subs = { + 'groupOfNames': ['novaProject'] + } + if v in subs: + return [v] + subs[v] return [v] def _match(self, k, v, attrs): if attrs.has_key(k): for v in self._subs(v): - if (v in attrs[k]): + if v in attrs[k]: return True return False + def search_s(self, dn, scope, query=None, fields=None): - #logging.debug("searching for %s" % dn) - filtered = {} - d = self.keeper['objects'] or {} - for cn, attrs in d.iteritems(): - if cn[-len(dn):] == dn: - filtered[cn] = attrs - objects = filtered - if query: - objects = {} - for cn, attrs in filtered.iteritems(): - if self._match_query(query, attrs): - objects[cn] = attrs - if objects == {}: + """search for all matching objects under dn using the query + only SCOPE_SUBTREE is supported. + """ + if scope != SCOPE_SUBTREE: + raise NotImplementedError(str(scope)) + redis = datastore.Redis.instance() + keys = redis.keys(self._redis_prefix + '*' + dn) + objects = [] + for key in keys: + # get the attributes from redis + attrs = redis.hgetall(key) + # turn the values from redis into lists + attrs = dict([(k, self._from_json(v)) + for k, v in attrs.iteritems()]) + # filter the objects by query + if not query or self._match_query(query, attrs): + # filter the attributes by fields + attrs = dict([(k, v) for k, v in attrs.iteritems() + if not fields or k in fields]) + objects.append((key[len(self._redis_prefix):], attrs)) + if objects == []: raise NO_SUCH_OBJECT() - return objects.items() + return objects - def add_s(self, cn, attr): - #logging.debug("adding %s" % cn) - stored = {} - for k, v in attr: - if type(v) is list: - stored[k] = v - else: - stored[k] = [v] - d = self.keeper['objects'] - d[cn] = stored - self.keeper['objects'] = d + @property + def _redis_prefix(self): + return 'ldap:' - def delete_s(self, cn): - logging.debug("deleting %s" % cn) - d = self.keeper['objects'] - del d[cn] - self.keeper['objects'] = d + def _from_json(self, encoded): + """Convert attribute values from json representation.""" + # return as simple strings instead of unicode strings + return [str(x) for x in json.loads(encoded)] - def modify_s(self, cn, attr): - logging.debug("modifying %s" % cn) - d = self.keeper['objects'] - for cmd, k, v in attr: - logging.debug("command %s" % cmd) + def _to_json(self, unencoded): + """Convert attribute values into json representation.""" + # all values are returned as lists from ldap + return json.dumps(list(unencoded)) + + def add_s(self, dn, attr): + """Add an object with the specified attributes at dn.""" + key = self._redis_prefix + dn + + value_dict = dict([(k, self._to_json(v)) for k, v in attr]) + datastore.Redis.instance().hmset(key, value_dict) + + def delete_s(self, dn): + """Remove the ldap object at specified dn.""" + datastore.Redis.instance().delete(self._redis_prefix + dn) + + def modify_s(self, dn, attrs): + """Modify the object at dn using the attribute list. + attr is a list of tuples in the following form: + ([MOD_ADD | MOD_DELETE], attribute, value) + """ + redis = datastore.Redis.instance() + key = self._redis_prefix + dn + + for cmd, k, v in attrs: + values = self._from_json(redis.hget(key, k)) if cmd == MOD_ADD: - d[cn][k].append(v) + values.append(v) else: - d[cn][k].remove(v) - self.keeper['objects'] = d - - - + values.remove(v) + values = redis.hset(key, k, self._to_json(values))