summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVishvananda Ishaya <vishvananda@gmail.com>2010-06-27 23:47:04 -0700
committerVishvananda Ishaya <vishvananda@gmail.com>2010-06-27 23:47:04 -0700
commit974ecad2092502cc6825ed5c4c7daa1b362f8a41 (patch)
tree2ca042b49cba804e1d067c2c92c6cb5bb59a6c44
parent4bb9e67582b2e2b6302b889011baffb69cff79c3 (diff)
More Comments, cleanup, and reformatting
-rw-r--r--nova/auth/fakeldap.py173
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))
-