diff options
| author | Vishvananda Ishaya <vishvananda@gmail.com> | 2010-06-27 23:47:04 -0700 |
|---|---|---|
| committer | Vishvananda Ishaya <vishvananda@gmail.com> | 2010-06-27 23:47:04 -0700 |
| commit | 974ecad2092502cc6825ed5c4c7daa1b362f8a41 (patch) | |
| tree | 2ca042b49cba804e1d067c2c92c6cb5bb59a6c44 | |
| parent | 4bb9e67582b2e2b6302b889011baffb69cff79c3 (diff) | |
More Comments, cleanup, and reformatting
| -rw-r--r-- | nova/auth/fakeldap.py | 173 |
1 files changed, 98 insertions, 75 deletions
diff --git a/nova/auth/fakeldap.py b/nova/auth/fakeldap.py index 0e95972f2..eca41b1d3 100644 --- a/nova/auth/fakeldap.py +++ b/nova/auth/fakeldap.py @@ -18,7 +18,11 @@ # License for the specific language governing permissions and limitations # under the License. """ - Fake LDAP server for test harnesses. +Fake LDAP server for test harnesses. + +This class does very little error checking, and knows nothing about ldap +class definitions. It implements the minimum emulation of the python ldap +library to work with nova. """ import json @@ -41,57 +45,39 @@ def initialize(uri): class FakeLDAP(object): def simple_bind_s(self, dn, password): - """This method is ignored, but provided for compatibility""" + """This method is ignored, but provided for compatibility.""" pass def unbind_s(self): - """This method is ignored, but provided for compatibility""" + """This method is ignored, but provided for compatibility.""" pass - def _paren_groups(self, source): - count = 0 - start = 0 - result = [] - for pos in xrange(len(source)): - if source[pos] == '(': - if count == 0: - start = pos - count += 1 - if source[pos] == ')': - count -= 1 - if count == 0: - result.append(source[start:pos+1]) - return result + def add_s(self, dn, attr): + """Add an object with the specified attributes at dn.""" + key = self._redis_prefix + dn - def _match_query(self, query, attrs): - inner = query[1:-1] - if inner.startswith('&'): - l, r = self._paren_groups(inner[1:]) - return self._match_query(l, attrs) and self._match_query(r, attrs) - if inner.startswith('|'): - l, r = self._paren_groups(inner[1:]) - return self._match_query(l, attrs) or self._match_query(r, attrs) - if inner.startswith('!'): - return not self._match_query(query[2:-1], attrs) + value_dict = dict([(k, self.__to_json(v)) for k, v in attr]) + datastore.Redis.instance().hmset(key, value_dict) - (k, sep, v) = inner.partition('=') - return self._match(k, v, attrs) + def delete_s(self, dn): + """Remove the ldap object at specified dn.""" + datastore.Redis.instance().delete(self._redis_prefix + dn) - def _subs(self, 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]: - return True - return False + 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: + values.append(v) + else: + values.remove(v) + values = redis.hset(key, k, self.__to_json(values)) def search_s(self, dn, scope, query=None, fields=None): """search for all matching objects under dn using the query @@ -106,10 +92,10 @@ class FakeLDAP(object): # get the attributes from redis attrs = redis.hgetall(key) # turn the values from redis into lists - attrs = dict([(k, self._from_json(v)) + 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): + 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]) @@ -118,44 +104,81 @@ class FakeLDAP(object): raise NO_SUCH_OBJECT() return objects + def __match_query(self, query, attrs): + """Match an ldap query to an attribute dictionary. + &, |, and ! are supported + No syntax checking is performed, so malformed querys will + not work correctly. + """ + # cut off the parentheses + inner = query[1:-1] + if inner.startswith('&'): + # cut off the & + l, r = self.__paren_groups(inner[1:]) + return self.__match_query(l, attrs) and self.__match_query(r, attrs) + if inner.startswith('|'): + # cut off the | + l, r = self.__paren_groups(inner[1:]) + return self.__match_query(l, attrs) or self.__match_query(r, attrs) + if inner.startswith('!'): + # cut off the ! and the nested parentheses + return not self.__match_query(query[2:-1], attrs) + + (k, sep, v) = inner.partition('=') + return self.__match(k, v, attrs) + + def __paren_groups(self, source): + """Split a string into parenthesized groups.""" + count = 0 + start = 0 + result = [] + for pos in xrange(len(source)): + if source[pos] == '(': + if count == 0: + start = pos + count += 1 + if source[pos] == ')': + count -= 1 + if count == 0: + result.append(source[start:pos+1]) + return result + + def __match(self, k, v, attrs): + """Match a given key and value against an attribute list.""" + if k not in attrs: + return False + if k != "objectclass": + return v in attrs[k] + # it is an objectclass check, so check subclasses + values = self.__subs(v) + for value in values: + if value in attrs[k]: + return True + return False + + def __subs(self, value): + """Returns a list of strings representing the ldap objectclass plus + any subclasses that inherit from it. + Fakeldap doesn't know about the ldap object structure, so subclasses + need to be defined manually in the dictionary below + """ + subs = { + 'groupOfNames': ['novaProject'] + } + if value in subs: + return [value] + subs[value] + return [value] + @property def _redis_prefix(self): return 'ldap:' - def _from_json(self, encoded): + 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 _to_json(self, unencoded): + 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: - values.append(v) - else: - values.remove(v) - values = redis.hset(key, k, self._to_json(values)) - |
