Merge patch 16002

This commit is contained in:
Vishvananda Ishaya
2010-07-02 10:29:55 -05:00
2 changed files with 285 additions and 153 deletions

View File

@@ -18,128 +18,207 @@
# 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 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
class OBJECT_CLASS_VIOLATION(Exception):
pass
def initialize(uri):
return FakeLDAP(uri)
return FakeLDAP()
def _match_query(query, attrs):
"""Match an ldap query to an attribute dictionary.
&, |, and ! are supported in the query. 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 = _paren_groups(inner[1:])
return _match_query(l, attrs) and _match_query(r, attrs)
if inner.startswith('|'):
# cut off the |
l, r = _paren_groups(inner[1:])
return _match_query(l, attrs) or _match_query(r, attrs)
if inner.startswith('!'):
# cut off the ! and the nested parentheses
return not _match_query(query[2:-1], attrs)
(k, sep, v) = inner.partition('=')
return _match(k, v, attrs)
def _paren_groups(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(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 = _subs(v)
for value in values:
if value in attrs[k]:
return True
return False
def _subs(value):
"""Returns a list of subclass strings.
The strings represent 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]
def _from_json(encoded):
"""Convert attribute values from json representation.
Args:
encoded -- a json encoded string
Returns a list of strings
"""
return [str(x) for x in json.loads(encoded)]
def _to_json(unencoded):
"""Convert attribute values into json representation.
Args:
unencoded -- an unencoded string or list of strings. If it
is a single string, it will be converted into a list.
Returns a json string
"""
return json.dumps(list(unencoded))
class FakeLDAP(object):
def __init__(self, _uri):
self.keeper = datastore.Keeper('fakeldap')
if self.keeper['objects'] is None:
self.keeper['objects'] = {}
#TODO(vish): refactor this class to use a wrapper instead of accessing
# redis directly
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):
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])
def add_s(self, dn, attr):
"""Add an object with the specified attributes at dn."""
key = "%s%s" % (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, _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("%s%s" % (self.__redis_prefix, dn))
def _subs(self, v):
if v in SUBS:
return [v] + SUBS[v]
return [v]
def modify_s(self, dn, attrs):
"""Modify the object at dn using the attribute list.
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
Args:
dn -- a dn
attrs -- a list of tuples in the following form:
([MOD_ADD | MOD_DELETE], attribute, value)
"""
redis = datastore.Redis.instance()
key = "%s%s" % (self.__redis_prefix, dn)
for cmd, k, v in attrs:
values = _from_json(redis.hget(key, k))
if cmd == MOD_ADD:
values.append(v)
else:
values.remove(v)
values = redis.hset(key, k, _to_json(values))
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.
Args:
dn -- dn to search under
scope -- only SCOPE_SUBTREE is supported
query -- query to filter objects by
fields -- fields to return. Returns all fields if not specified
"""
if scope != SCOPE_SUBTREE:
raise NotImplementedError(str(scope))
redis = datastore.Redis.instance()
keys = redis.keys("%s*%s" % (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, _from_json(v))
for k, v in attrs.iteritems()])
# filter the objects by query
if not query or _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()
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
def delete_s(self, cn):
logging.debug("deleting %s" % cn)
d = self.keeper['objects']
del d[cn]
self.keeper['objects'] = d
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)
if cmd == MOD_ADD:
d[cn][k].append(v)
else:
d[cn][k].remove(v)
self.keeper['objects'] = d
return objects
@property
def __redis_prefix(self):
return 'ldap:'

View File

@@ -52,15 +52,21 @@ from nova import objectstore # for flags
FLAGS = flags.FLAGS
flags.DEFINE_string('ldap_url', 'ldap://localhost', 'Point this at your ldap server')
flags.DEFINE_string('ldap_url', 'ldap://localhost',
'Point this at your ldap server')
flags.DEFINE_string('ldap_password', 'changeme', 'LDAP password')
flags.DEFINE_string('user_dn', 'cn=Manager,dc=example,dc=com', 'DN of admin user')
flags.DEFINE_string('user_dn', 'cn=Manager,dc=example,dc=com',
'DN of admin user')
flags.DEFINE_string('user_unit', 'Users', 'OID for Users')
flags.DEFINE_string('user_ldap_subtree', 'ou=Users,dc=example,dc=com', 'OU for Users')
flags.DEFINE_string('project_ldap_subtree', 'ou=Groups,dc=example,dc=com', 'OU for Projects')
flags.DEFINE_string('role_ldap_subtree', 'ou=Groups,dc=example,dc=com', 'OU for Roles')
flags.DEFINE_string('user_ldap_subtree', 'ou=Users,dc=example,dc=com',
'OU for Users')
flags.DEFINE_string('project_ldap_subtree', 'ou=Groups,dc=example,dc=com',
'OU for Projects')
flags.DEFINE_string('role_ldap_subtree', 'ou=Groups,dc=example,dc=com',
'OU for Roles')
# mapping with these flags is necessary because we're going to tie in to an existing ldap schema
# NOTE(vish): mapping with these flags is necessary because we're going
# to tie in to an existing ldap schema
flags.DEFINE_string('ldap_cloudadmin',
'cn=cloudadmins,ou=Groups,dc=example,dc=com', 'cn for Cloud Admins')
flags.DEFINE_string('ldap_itsec',
@@ -72,11 +78,15 @@ flags.DEFINE_string('ldap_netadmin',
flags.DEFINE_string('ldap_developer',
'cn=developers,ou=Groups,dc=example,dc=com', 'cn for Developers')
# a user with one of these roles will be a superuser and have access to all api commands
flags.DEFINE_list('superuser_roles', ['cloudadmin'], 'roles that ignore rbac checking completely')
# NOTE(vish): a user with one of these roles will be a superuser and
# have access to all api commands
flags.DEFINE_list('superuser_roles', ['cloudadmin'],
'roles that ignore rbac checking completely')
# a user with one of these roles will have it for every project, even if he or she is not a member of the project
flags.DEFINE_list('global_roles', ['cloudadmin', 'itsec'], 'roles that apply to all projects')
# NOTE(vish): a user with one of these roles will have it for every
# project, even if he or she is not a member of the project
flags.DEFINE_list('global_roles', ['cloudadmin', 'itsec'],
'roles that apply to all projects')
flags.DEFINE_string('credentials_template',
utils.abspath('auth/novarc.template'),
@@ -90,15 +100,20 @@ flags.DEFINE_string('credential_cert_file', 'cert.pem',
'Filename of certificate in credentials zip')
flags.DEFINE_string('credential_rc_file', 'novarc',
'Filename of rc in credentials zip')
flags.DEFINE_string('vpn_ip', '127.0.0.1', 'Public IP for the cloudpipe VPN servers')
flags.DEFINE_string('vpn_ip', '127.0.0.1',
'Public IP for the cloudpipe VPN servers')
class AuthBase(object):
@classmethod
def safe_id(cls, obj):
"""this method will return the id of the object if the object is of this class, otherwise
it will return the original object. This allows methods to accept objects or
ids as paramaters"""
"""Safe get object id.
This method will return the id of the object if the object
is of this class, otherwise it will return the original object.
This allows methods to accept objects or ids as paramaters.
"""
if isinstance(obj, cls):
return obj.id
else:
@@ -195,7 +210,8 @@ class User(AuthBase):
return UserManager.instance().get_key_pairs(self.id)
def __repr__(self):
return "User('%s', '%s', '%s', '%s', %s)" % (self.id, self.name, self.access, self.secret, self.admin)
return "User('%s', '%s', '%s', '%s', %s)" % (
self.id, self.name, self.access, self.secret, self.admin)
class KeyPair(AuthBase):
def __init__(self, id, owner_id, public_key, fingerprint):
@@ -209,7 +225,8 @@ class KeyPair(AuthBase):
return UserManager.instance().delete_key_pair(self.owner, self.name)
def __repr__(self):
return "KeyPair('%s', '%s', '%s', '%s')" % (self.id, self.owner_id, self.public_key, self.fingerprint)
return "KeyPair('%s', '%s', '%s', '%s')" % (
self.id, self.owner_id, self.public_key, self.fingerprint)
class Group(AuthBase):
"""id and name are currently the same"""
@@ -223,7 +240,8 @@ class Group(AuthBase):
return User.safe_id(user) in self.member_ids
def __repr__(self):
return "Group('%s', '%s', %s)" % (self.id, self.description, self.member_ids)
return "Group('%s', '%s', %s)" % (
self.id, self.description, self.member_ids)
class Project(Group):
def __init__(self, id, project_manager_id, description, member_ids):
@@ -298,7 +316,9 @@ class Project(Group):
return UserManager.instance().generate_x509_cert(user, self)
def __repr__(self):
return "Project('%s', '%s', '%s', %s)" % (self.id, self.project_manager_id, self.description, self.member_ids)
return "Project('%s', '%s', '%s', %s)" % (
self.id, self.project_manager_id,
self.description, self.member_ids)
class UserManager(object):
def __init__(self):
@@ -322,7 +342,9 @@ class UserManager(object):
except: pass
return cls._instance
def authenticate(self, access, signature, params, verb='GET', server_string='127.0.0.1:8773', path='/', verify_signature=True):
def authenticate(self, access, signature, params, verb='GET',
server_string='127.0.0.1:8773', path='/',
verify_signature=True):
# TODO: Check for valid timestamp
(access_key, sep, project_name) = access.partition(':')
@@ -334,12 +356,16 @@ class UserManager(object):
project = self.get_project(project_name)
if project == None:
raise exception.NotFound('No project called %s could be found' % project_name)
raise exception.NotFound('No project called %s could be found' %
project_name)
if not user.is_admin() and not project.has_member(user):
raise exception.NotFound('User %s is not a member of project %s' % (user.id, project.id))
raise exception.NotFound('User %s is not a member of project %s' %
(user.id, project.id))
if verify_signature:
# hmac can't handle unicode, so encode ensures that secret isn't unicode
expected_signature = signer.Signer(user.secret.encode()).generate(params, verb, server_string, path)
# NOTE(vish): hmac can't handle unicode, so encode ensures that
# secret isn't unicode
expected_signature = signer.Signer(user.secret.encode()).generate(
params, verb, server_string, path)
logging.debug('user.secret: %s', user.secret)
logging.debug('expected_signature: %s', expected_signature)
logging.debug('signature: %s', signature)
@@ -369,17 +395,21 @@ class UserManager(object):
def add_role(self, user, role, project=None):
with LDAPWrapper() as conn:
return conn.add_role(User.safe_id(user), role, Project.safe_id(project))
return conn.add_role(User.safe_id(user), role,
Project.safe_id(project))
def remove_role(self, user, role, project=None):
with LDAPWrapper() as conn:
return conn.remove_role(User.safe_id(user), role, Project.safe_id(project))
return conn.remove_role(User.safe_id(user), role,
Project.safe_id(project))
def create_project(self, name, manager_user, description=None, member_users=None):
def create_project(self, name, manager_user,
description=None, member_users=None):
if member_users:
member_users = [User.safe_id(u) for u in member_users]
with LDAPWrapper() as conn:
return conn.create_project(name, User.safe_id(manager_user), description, member_users)
return conn.create_project(name, User.safe_id(manager_user),
description, member_users)
def get_projects(self):
with LDAPWrapper() as conn:
@@ -392,7 +422,8 @@ class UserManager(object):
def add_to_project(self, user, project):
with LDAPWrapper() as conn:
return conn.add_to_project(User.safe_id(user), Project.safe_id(project))
return conn.add_to_project(User.safe_id(user),
Project.safe_id(project))
def is_project_manager(self, user, project):
if not isinstance(project, Project):
@@ -408,7 +439,8 @@ class UserManager(object):
def remove_from_project(self, user, project):
with LDAPWrapper() as conn:
return conn.remove_from_project(User.safe_id(user), Project.safe_id(project))
return conn.remove_from_project(User.safe_id(user),
Project.safe_id(project))
def delete_project(self, project):
with LDAPWrapper() as conn:
@@ -426,7 +458,8 @@ class UserManager(object):
with LDAPWrapper() as conn:
return conn.find_users()
def create_user(self, user, access=None, secret=None, admin=False, create_project=True):
def create_user(self, user, access=None, secret=None,
admin=False, create_project=True):
if access == None: access = str(uuid.uuid4())
if secret == None: secret = str(uuid.uuid4())
with LDAPWrapper() as conn:
@@ -503,9 +536,12 @@ class LDAPWrapper(object):
def connect(self):
""" connect to ldap as admin user """
if FLAGS.fake_users:
self.NO_SUCH_OBJECT = fakeldap.NO_SUCH_OBJECT
self.OBJECT_CLASS_VIOLATION = fakeldap.OBJECT_CLASS_VIOLATION
self.conn = fakeldap.initialize(FLAGS.ldap_url)
else:
assert(ldap.__name__ != 'fakeldap')
self.NO_SUCH_OBJECT = ldap.NO_SUCH_OBJECT
self.OBJECT_CLASS_VIOLATION = ldap.OBJECT_CLASS_VIOLATION
self.conn = ldap.initialize(FLAGS.ldap_url)
self.conn.simple_bind_s(self.user, self.passwd)
@@ -518,7 +554,7 @@ class LDAPWrapper(object):
def find_dns(self, dn, query=None):
try:
res = self.conn.search_s(dn, ldap.SCOPE_SUBTREE, query)
except Exception:
except self.NO_SUCH_OBJECT:
return []
# just return the DNs
return [dn for dn, attributes in res]
@@ -526,25 +562,29 @@ class LDAPWrapper(object):
def find_objects(self, dn, query = None):
try:
res = self.conn.search_s(dn, ldap.SCOPE_SUBTREE, query)
except Exception:
except self.NO_SUCH_OBJECT:
return []
# just return the attributes
return [attributes for dn, attributes in res]
def find_users(self):
attrs = self.find_objects(FLAGS.user_ldap_subtree, '(objectclass=novaUser)')
attrs = self.find_objects(FLAGS.user_ldap_subtree,
'(objectclass=novaUser)')
return [self.__to_user(attr) for attr in attrs]
def find_key_pairs(self, uid):
attrs = self.find_objects(self.__uid_to_dn(uid), '(objectclass=novaKeyPair)')
attrs = self.find_objects(self.__uid_to_dn(uid),
'(objectclass=novaKeyPair)')
return [self.__to_key_pair(uid, attr) for attr in attrs]
def find_projects(self):
attrs = self.find_objects(FLAGS.project_ldap_subtree, '(objectclass=novaProject)')
attrs = self.find_objects(FLAGS.project_ldap_subtree,
'(objectclass=novaProject)')
return [self.__to_project(attr) for attr in attrs]
def find_roles(self, tree):
attrs = self.find_objects(tree, '(&(objectclass=groupOfNames)(!(objectclass=NovaProject)))')
attrs = self.find_objects(tree,
'(&(objectclass=groupOfNames)(!(objectclass=novaProject)))')
return [self.__to_group(attr) for attr in attrs]
def find_group_dns_with_member(self, tree, uid):
@@ -554,7 +594,8 @@ class LDAPWrapper(object):
return dns
def find_user(self, uid):
attr = self.find_object(self.__uid_to_dn(uid), '(objectclass=novaUser)')
attr = self.find_object(self.__uid_to_dn(uid),
'(objectclass=novaUser)')
return self.__to_user(attr)
def find_key_pair(self, uid, key_name):
@@ -611,11 +652,14 @@ class LDAPWrapper(object):
self.conn.add_s(self.__uid_to_dn(name), attr)
return self.__to_user(dict(attr))
def create_project(self, name, manager_uid, description=None, member_uids=None):
def create_project(self, name, manager_uid,
description=None, member_uids=None):
if self.project_exists(name):
raise exception.Duplicate("Project can't be created because project %s already exists" % name)
raise exception.Duplicate("Project can't be created because "
"project %s already exists" % name)
if not self.user_exists(manager_uid):
raise exception.NotFound("Project can't be created because manager %s doesn't exist" % manager_uid)
raise exception.NotFound("Project can't be created because "
"manager %s doesn't exist" % manager_uid)
manager_dn = self.__uid_to_dn(manager_uid)
# description is a required attribute
if description is None:
@@ -624,7 +668,8 @@ class LDAPWrapper(object):
if member_uids != None:
for member_uid in member_uids:
if not self.user_exists(member_uid):
raise exception.NotFound("Project can't be created because user %s doesn't exist" % member_uid)
raise exception.NotFound("Project can't be created "
"because user %s doesn't exist" % member_uid)
members.append(self.__uid_to_dn(member_uid))
# always add the manager as a member because members is required
if not manager_dn in members:
@@ -655,16 +700,21 @@ class LDAPWrapper(object):
if project_id == None:
return FLAGS.__getitem__("ldap_%s" % role).value
else:
return 'cn=%s,cn=%s,%s' % (role, project_id, FLAGS.project_ldap_subtree)
return 'cn=%s,cn=%s,%s' % (role,
project_id,
FLAGS.project_ldap_subtree)
def __create_group(self, group_dn, name, uid, description, member_uids = None):
def __create_group(self, group_dn, name, uid,
description, member_uids = None):
if self.group_exists(name):
raise exception.Duplicate("Group can't be created because group %s already exists" % name)
raise exception.Duplicate("Group can't be created because "
"group %s already exists" % name)
members = []
if member_uids != None:
for member_uid in member_uids:
if not self.user_exists(member_uid):
raise exception.NotFound("Group can't be created because user %s doesn't exist" % member_uid)
raise exception.NotFound("Group can't be created "
"because user %s doesn't exist" % member_uid)
members.append(self.__uid_to_dn(member_uid))
dn = self.__uid_to_dn(uid)
if not dn in members:
@@ -693,15 +743,12 @@ class LDAPWrapper(object):
def remove_role(self, uid, role, project_id=None):
role_dn = self.__role_to_dn(role, project_id)
try:
return self.remove_from_group(uid, role_dn)
except Exception, ex:
print type(ex), ex
return self.remove_from_group(uid, role_dn)
def is_in_group(self, uid, group_dn):
if not self.user_exists(uid):
raise exception.NotFound("User %s can't be searched in group becuase the user doesn't exist" % (uid,))
raise exception.NotFound("User %s can't be searched in group "
"becuase the user doesn't exist" % (uid,))
if not self.group_exists(group_dn):
return False
res = self.find_object(group_dn,
@@ -710,11 +757,14 @@ class LDAPWrapper(object):
def add_to_group(self, uid, group_dn):
if not self.user_exists(uid):
raise exception.NotFound("User %s can't be added to the group becuase the user doesn't exist" % (uid,))
raise exception.NotFound("User %s can't be added to the group "
"becuase the user doesn't exist" % (uid,))
if not self.group_exists(group_dn):
raise exception.NotFound("The group at dn %s doesn't exist" % (group_dn,))
raise exception.NotFound("The group at dn %s doesn't exist" %
(group_dn,))
if self.is_in_group(uid, group_dn):
raise exception.Duplicate("User %s is already a member of the group %s" % (uid, group_dn))
raise exception.Duplicate("User %s is already a member of "
"the group %s" % (uid, group_dn))
attr = [
(ldap.MOD_ADD, 'member', self.__uid_to_dn(uid))
]
@@ -722,11 +772,14 @@ class LDAPWrapper(object):
def remove_from_group(self, uid, group_dn):
if not self.group_exists(group_dn):
raise exception.NotFound("The group at dn %s doesn't exist" % (group_dn,))
raise exception.NotFound("The group at dn %s doesn't exist" %
(group_dn,))
if not self.user_exists(uid):
raise exception.NotFound("User %s can't be removed from the group because the user doesn't exist" % (uid,))
raise exception.NotFound("User %s can't be removed from the "
"group because the user doesn't exist" % (uid,))
if not self.is_in_group(uid, group_dn):
raise exception.NotFound("User %s is not a member of the group" % (uid,))
raise exception.NotFound("User %s is not a member of the group" %
(uid,))
self._safe_remove_from_group(group_dn, uid)
def _safe_remove_from_group(self, group_dn, uid):
@@ -734,14 +787,15 @@ class LDAPWrapper(object):
attr = [(ldap.MOD_DELETE, 'member', self.__uid_to_dn(uid))]
try:
self.conn.modify_s(group_dn, attr)
except ldap.OBJECT_CLASS_VIOLATION:
except self.OBJECT_CLASS_VIOLATION:
logging.debug("Attempted to remove the last member of a group. "
"Deleting the group at %s instead." % group_dn )
self.delete_group(group_dn)
def remove_from_all(self, uid):
if not self.user_exists(uid):
raise exception.NotFound("User %s can't be removed from all because the user doesn't exist" % (uid,))
raise exception.NotFound("User %s can't be removed from all "
"because the user doesn't exist" % (uid,))
dn = self.__uid_to_dn(uid)
role_dns = self.find_group_dns_with_member(
FLAGS.role_ldap_subtree, uid)
@@ -794,9 +848,8 @@ class LDAPWrapper(object):
def delete_roles(self, project_dn):
roles = self.find_roles(project_dn)
if roles != None:
for role in roles:
self.delete_group('cn=%s,%s' % (role.id, project_dn))
for role in roles:
self.delete_group('cn=%s,%s' % (role.id, project_dn))
def delete_project(self, name):
project_dn = 'cn=%s,%s' % (name, FLAGS.project_ldap_subtree)