From a3ca587654095ffd4b97103302fb0744e505e332 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Mon, 19 Jul 2010 13:19:26 -0500 Subject: Massive refactor of users.py Split users.py into manager.py and ldpadriver.py Added tons of docstrings Cleaned up public methods Simplified manager singleton handling --- nova/auth/ldapdriver.py | 428 ++++++++++++++++ nova/auth/manager.py | 741 ++++++++++++++++++++++++++++ nova/auth/rbac.py | 2 +- nova/auth/users.py | 974 ------------------------------------- nova/cloudpipe/api.py | 2 +- nova/cloudpipe/pipelib.py | 4 +- nova/compute/network.py | 14 +- nova/endpoint/admin.py | 14 +- nova/endpoint/api.py | 4 +- nova/endpoint/cloud.py | 6 +- nova/endpoint/rackspace.py | 6 +- nova/tests/access_unittest.py | 6 +- nova/tests/api_unittest.py | 4 +- nova/tests/auth_unittest.py | 207 ++++++++ nova/tests/cloud_unittest.py | 12 +- nova/tests/network_unittest.py | 4 +- nova/tests/objectstore_unittest.py | 6 +- nova/tests/users_unittest.py | 207 -------- 18 files changed, 1418 insertions(+), 1223 deletions(-) create mode 100644 nova/auth/ldapdriver.py create mode 100644 nova/auth/manager.py delete mode 100644 nova/auth/users.py create mode 100644 nova/tests/auth_unittest.py delete mode 100644 nova/tests/users_unittest.py (limited to 'nova') diff --git a/nova/auth/ldapdriver.py b/nova/auth/ldapdriver.py new file mode 100644 index 000000000..49443c99a --- /dev/null +++ b/nova/auth/ldapdriver.py @@ -0,0 +1,428 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2010 United States Government as represented by the +# Administrator of the National Aeronautics and Space Administration. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +""" +Auth driver for ldap + +It should be easy to create a replacement for this driver supporting +other backends by creating another class that exposes the same +public methods. +""" + +import logging + +from nova import exception +from nova import flags +from nova.auth import manager + +try: + import ldap +except Exception, e: + from nova.auth import fakeldap as ldap +# NOTE(vish): this import is so we can use fakeldap even when real ldap +# is installed. +from nova.auth import fakeldap + +FLAGS = flags.FLAGS +flags.DEFINE_string('ldap_url', 'ldap://localhost', + 'Point this at your ldap server') +flags.DEFINE_string('ldap_password', 'changeme', 'LDAP password') +flags.DEFINE_string('ldap_user_dn', 'cn=Manager,dc=example,dc=com', + 'DN of admin user') +flags.DEFINE_string('ldap_user_unit', 'Users', 'OID for Users') +flags.DEFINE_string('ldap_user_subtree', 'ou=Users,dc=example,dc=com', + 'OU for Users') +flags.DEFINE_string('ldap_project_subtree', 'ou=Groups,dc=example,dc=com', + 'OU for Projects') +flags.DEFINE_string('role_project_subtree', 'ou=Groups,dc=example,dc=com', + 'OU for Roles') + +# 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', + 'cn=itsec,ou=Groups,dc=example,dc=com', 'cn for ItSec') +flags.DEFINE_string('ldap_sysadmin', + 'cn=sysadmins,ou=Groups,dc=example,dc=com', 'cn for Sysadmins') +flags.DEFINE_string('ldap_netadmin', + 'cn=netadmins,ou=Groups,dc=example,dc=com', 'cn for NetAdmins') +flags.DEFINE_string('ldap_developer', + 'cn=developers,ou=Groups,dc=example,dc=com', 'cn for Developers') + + +class LdapDriver(object): + def __enter__(self): + """Creates the connection to LDAP""" + 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: + 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(FLAGS.ldap_user_dn, FLAGS.ldap_password) + return self + + def __exit__(self, type, value, traceback): + """Destroys the connection to LDAP""" + self.conn.unbind_s() + return False + + def get_user(self, uid): + attr = self.__find_object(self.__uid_to_dn(uid), + '(objectclass=novaUser)') + return self.__to_user(attr) + + def get_user_from_access_key(self, access): + query = '(accessKey=%s)' % access + dn = FLAGS.ldap_user_subtree + return self.__to_user(self.__find_object(dn, query)) + + def get_key_pair(self, uid, key_name): + dn = 'cn=%s,%s' % (key_name, + self.__uid_to_dn(uid)) + attr = self.__find_object(dn, '(objectclass=novaKeyPair)') + return self.__to_key_pair(uid, attr) + + def get_project(self, name): + dn = 'cn=%s,%s' % (name, + FLAGS.ldap_project_subtree) + attr = self.__find_object(dn, '(objectclass=novaProject)') + return self.__to_project(attr) + + def get_users(self): + attrs = self.__find_objects(FLAGS.ldap_user_subtree, + '(objectclass=novaUser)') + return [self.__to_user(attr) for attr in attrs] + + def get_key_pairs(self, uid): + attrs = self.__find_objects(self.__uid_to_dn(uid), + '(objectclass=novaKeyPair)') + return [self.__to_key_pair(uid, attr) for attr in attrs] + + def get_projects(self): + attrs = self.__find_objects(FLAGS.ldap_project_subtree, + '(objectclass=novaProject)') + return [self.__to_project(attr) for attr in attrs] + + def create_user(self, name, access_key, secret_key, is_admin): + if self.__user_exists(name): + raise exception.Duplicate("LDAP user %s already exists" % name) + attr = [ + ('objectclass', ['person', + 'organizationalPerson', + 'inetOrgPerson', + 'novaUser']), + ('ou', [FLAGS.ldap_user_unit]), + ('uid', [name]), + ('sn', [name]), + ('cn', [name]), + ('secretKey', [secret_key]), + ('accessKey', [access_key]), + ('isAdmin', [str(is_admin).upper()]), + ] + self.conn.add_s(self.__uid_to_dn(name), attr) + return self.__to_user(dict(attr)) + + def create_key_pair(self, uid, key_name, public_key, fingerprint): + """create's a public key in the directory underneath the user""" + # TODO(vish): possibly refactor this to store keys in their own ou + # and put dn reference in the user object + attr = [ + ('objectclass', ['novaKeyPair']), + ('cn', [key_name]), + ('sshPublicKey', [public_key]), + ('keyFingerprint', [fingerprint]), + ] + self.conn.add_s('cn=%s,%s' % (key_name, + self.__uid_to_dn(uid)), + attr) + return self.__to_key_pair(uid, dict(attr)) + + 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) + if not self.__user_exists(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: + description = name + members = [] + 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) + 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: + members.append(manager_dn) + attr = [ + ('objectclass', ['novaProject']), + ('cn', [name]), + ('description', [description]), + ('projectManager', [manager_dn]), + ('member', members) + ] + self.conn.add_s('cn=%s,%s' % (name, FLAGS.ldap_project_subtree), attr) + return self.__to_project(dict(attr)) + + def add_to_project(self, uid, project_id): + dn = 'cn=%s,%s' % (project_id, FLAGS.ldap_project_subtree) + return self.__add_to_group(uid, dn) + + def remove_from_project(self, uid, project_id): + dn = 'cn=%s,%s' % (project_id, FLAGS.ldap_project_subtree) + return self.__remove_from_group(uid, dn) + + def is_in_project(self, uid, project_id): + dn = 'cn=%s,%s' % (project_id, FLAGS.ldap_project_subtree) + return self.__is_in_group(uid, dn) + + def has_role(self, uid, role, project_id=None): + role_dn = self.__role_to_dn(role, project_id) + return self.__is_in_group(uid, role_dn) + + def add_role(self, uid, role, project_id=None): + role_dn = self.__role_to_dn(role, project_id) + if not self.__group_exists(role_dn): + # create the role if it doesn't exist + description = '%s role for %s' % (role, project_id) + self.__create_group(role_dn, role, uid, description) + else: + return self.__add_to_group(uid, role_dn) + + def remove_role(self, uid, role, project_id=None): + role_dn = self.__role_to_dn(role, project_id) + return self.__remove_from_group(uid, role_dn) + + def delete_user(self, uid): + if not self.__user_exists(uid): + raise exception.NotFound("User %s doesn't exist" % uid) + self.__delete_key_pairs(uid) + self.__remove_from_all(uid) + self.conn.delete_s('uid=%s,%s' % (uid, + FLAGS.ldap_user_subtree)) + + def delete_key_pair(self, uid, key_name): + if not self.__key_pair_exists(uid, key_name): + raise exception.NotFound("Key Pair %s doesn't exist for user %s" % + (key_name, uid)) + self.conn.delete_s('cn=%s,uid=%s,%s' % (key_name, uid, + FLAGS.ldap_user_subtree)) + + def delete_project(self, name): + project_dn = 'cn=%s,%s' % (name, FLAGS.ldap_project_subtree) + self.__delete_roles(project_dn) + self.__delete_group(project_dn) + + def __user_exists(self, name): + return self.get_user(name) != None + + def __key_pair_exists(self, uid, key_name): + return self.get_key_pair(uid, key_name) != None + + def __project_exists(self, name): + return self.get_project(name) != None + + def __find_object(self, dn, query = None): + objects = self.__find_objects(dn, query) + if len(objects) == 0: + return None + return objects[0] + + def __find_dns(self, dn, query=None): + try: + res = self.conn.search_s(dn, ldap.SCOPE_SUBTREE, query) + except self.NO_SUCH_OBJECT: + return [] + # just return the DNs + return [dn for dn, attributes in res] + + def __find_objects(self, dn, query = None): + try: + res = self.conn.search_s(dn, ldap.SCOPE_SUBTREE, query) + except self.NO_SUCH_OBJECT: + return [] + # just return the attributes + return [attributes for dn, attributes in res] + + def __find_role_dns(self, tree): + return self.__find_dns(tree, + '(&(objectclass=groupOfNames)(!(objectclass=novaProject)))') + + def __find_group_dns_with_member(self, tree, uid): + dns = self.__find_dns(tree, + '(&(objectclass=groupOfNames)(member=%s))' % + self.__uid_to_dn(uid)) + return dns + + def __group_exists(self, dn): + return self.__find_object(dn, '(objectclass=groupOfNames)') != None + + def __delete_key_pairs(self, uid): + keys = self.get_key_pairs(uid) + if keys != None: + for key in keys: + self.delete_key_pair(uid, key.name) + + def __role_to_dn(self, role, project_id=None): + if project_id == None: + return FLAGS.__getitem__("ldap_%s" % role).value + else: + return 'cn=%s,cn=%s,%s' % (role, + project_id, + FLAGS.ldap_project_subtree) + + def __create_group(self, group_dn, name, uid, + description, member_uids = None): + if self.__group_exists(group_dn): + 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) + members.append(self.__uid_to_dn(member_uid)) + dn = self.__uid_to_dn(uid) + if not dn in members: + members.append(dn) + attr = [ + ('objectclass', ['groupOfNames']), + ('cn', [name]), + ('description', [description]), + ('member', members) + ] + self.conn.add_s(group_dn, attr) + + 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,)) + if not self.__group_exists(group_dn): + return False + res = self.__find_object(group_dn, + '(member=%s)' % self.__uid_to_dn(uid)) + return res != None + + 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,)) + if not self.__group_exists(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)) + attr = [ + (ldap.MOD_ADD, 'member', self.__uid_to_dn(uid)) + ] + self.conn.modify_s(group_dn, attr) + + 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,)) + 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,)) + if not self.__is_in_group(uid, group_dn): + 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): + # FIXME(vish): what if deleted user is a project manager? + attr = [(ldap.MOD_DELETE, 'member', self.__uid_to_dn(uid))] + try: + self.conn.modify_s(group_dn, attr) + 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,)) + dn = self.__uid_to_dn(uid) + role_dns = self.__find_group_dns_with_member( + FLAGS.role_project_subtree, uid) + for role_dn in role_dns: + self.__safe_remove_from_group(role_dn, uid) + project_dns = self.__find_group_dns_with_member( + FLAGS.ldap_project_subtree, uid) + for project_dn in project_dns: + self.__safe_remove_from_group(project_dn, uid) + + def __delete_group(self, group_dn): + if not self.__group_exists(group_dn): + raise exception.NotFound("Group at dn %s doesn't exist" % group_dn) + self.conn.delete_s(group_dn) + + def __delete_roles(self, project_dn): + for role_dn in self.__find_role_dns(project_dn): + self.__delete_group(role_dn) + + def __to_user(self, attr): + if attr == None: + return None + return manager.User( + id = attr['uid'][0], + name = attr['cn'][0], + access = attr['accessKey'][0], + secret = attr['secretKey'][0], + admin = (attr['isAdmin'][0] == 'TRUE') + ) + + def __to_key_pair(self, owner, attr): + if attr == None: + return None + return manager.KeyPair( + id = attr['cn'][0], + owner_id = owner, + public_key = attr['sshPublicKey'][0], + fingerprint = attr['keyFingerprint'][0], + ) + + def __to_project(self, attr): + if attr == None: + return None + member_dns = attr.get('member', []) + return manager.Project( + id = attr['cn'][0], + project_manager_id = self.__dn_to_uid(attr['projectManager'][0]), + description = attr.get('description', [None])[0], + member_ids = [self.__dn_to_uid(x) for x in member_dns] + ) + + def __dn_to_uid(self, dn): + return dn.split(',')[0].split('=')[1] + + def __uid_to_dn(self, dn): + return 'uid=%s,%s' % (dn, FLAGS.ldap_user_subtree) + diff --git a/nova/auth/manager.py b/nova/auth/manager.py new file mode 100644 index 000000000..0b5039684 --- /dev/null +++ b/nova/auth/manager.py @@ -0,0 +1,741 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2010 United States Government as represented by the +# Administrator of the National Aeronautics and Space Administration. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +""" +Nova authentication management +""" + +import logging +import os +import shutil +import string +import tempfile +import uuid +import zipfile + +from nova import crypto +from nova import datastore +from nova import exception +from nova import flags +from nova import objectstore # for flags +from nova import signer +from nova import utils +from nova.auth import ldapdriver +FLAGS = flags.FLAGS + +# 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') + +# 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'), + 'Template for creating users rc file') +flags.DEFINE_string('vpn_client_template', + utils.abspath('cloudpipe/client.ovpn.template'), + 'Template for creating users vpn file') +flags.DEFINE_string('credential_key_file', 'pk.pem', + 'Filename of private key in credentials zip') +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_integer('vpn_start_port', 1000, + 'Start port for the cloudpipe VPN servers') +flags.DEFINE_integer('vpn_end_port', 2000, + 'End port for the cloudpipe VPN servers') + +flags.DEFINE_string('credential_cert_subject', + '/C=US/ST=California/L=MountainView/O=AnsoLabs/' + 'OU=NovaDev/CN=%s-%s', + 'Subject for certificate for users') + +flags.DEFINE_string('vpn_ip', '127.0.0.1', + 'Public IP for the cloudpipe VPN servers') + + +class AuthBase(object): + """Base class for objects relating to auth + + Objects derived from this class should be stupid data objects with + an id member. They may optionally contain methods that delegate to + AuthManager, but should not implement logic themselves. + """ + @classmethod + def safe_id(cls, obj): + """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: + return obj + + +class User(AuthBase): + """Object representing a user""" + def __init__(self, id, name, access, secret, admin): + self.id = id + self.name = name + self.access = access + self.secret = secret + self.admin = admin + + def is_superuser(self): + return AuthManager().is_superuser(self) + + def is_admin(self): + return AuthManager().is_admin(self) + + def has_role(self, role): + return AuthManager().has_role(self, role) + + def add_role(self, role): + return AuthManager().add_role(self, role) + + def remove_role(self, role): + return AuthManager().remove_role(self, role) + + def is_project_member(self, project): + return AuthManager().is_project_member(self, project) + + def is_project_manager(self, project): + return AuthManager().is_project_manager(self, project) + + def generate_key_pair(self, name): + return AuthManager().generate_key_pair(self.id, name) + + def create_key_pair(self, name, public_key, fingerprint): + return AuthManager().create_key_pair(self.id, + name, + public_key, + fingerprint) + + def get_key_pair(self, name): + return AuthManager().get_key_pair(self.id, name) + + def delete_key_pair(self, name): + return AuthManager().delete_key_pair(self.id, name) + + def get_key_pairs(self): + return AuthManager().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) + + +class KeyPair(AuthBase): + """Represents an ssh key returned from the datastore + + Even though this object is named KeyPair, only the public key and + fingerprint is stored. The user's private key is not saved. + """ + def __init__(self, id, owner_id, public_key, fingerprint): + self.id = id + self.name = id + self.owner_id = owner_id + self.public_key = public_key + self.fingerprint = fingerprint + + def __repr__(self): + return "KeyPair('%s', '%s', '%s', '%s')" % (self.id, + self.owner_id, + self.public_key, + self.fingerprint) + + +class Project(AuthBase): + """Represents a Project returned from the datastore""" + def __init__(self, id, project_manager_id, description, member_ids): + self.project_manager_id = project_manager_id + self.id = id + self.name = id + self.description = description + self.member_ids = member_ids + + @property + def project_manager(self): + return AuthManager().get_user(self.project_manager_id) + + def has_manager(self, user): + return AuthManager().is_project_manager(user, self) + + def has_member(self, user): + return AuthManager().is_project_member(user, self) + + def add_role(self, user, role): + return AuthManager().add_role(user, role, self) + + def remove_role(self, user, role): + return AuthManager().remove_role(user, role, self) + + def has_role(self, user, role): + return AuthManager().has_role(user, role, self) + + def get_credentials(self, user): + return AuthManager().get_credentials(user, self) + + def __repr__(self): + return "Project('%s', '%s', '%s', %s)" % (self.id, + self.project_manager_id, + self.description, + self.member_ids) + + +class NoMorePorts(exception.Error): + pass + + +class Vpn(datastore.BasicModel): + """Manages vpn ips and ports for projects""" + def __init__(self, project_id): + self.project_id = project_id + super(Vpn, self).__init__() + + @property + def identifier(self): + return self.project_id + + @classmethod + def create(cls, project_id): + # TODO(vish): get list of vpn ips from redis + port = cls.find_free_port_for_ip(FLAGS.vpn_ip) + vpn = cls(project_id) + # save ip for project + vpn['project'] = project_id + vpn['ip'] = FLAGS.vpn_ip + vpn['port'] = port + vpn.save() + return vpn + + @classmethod + def find_free_port_for_ip(cls, ip): + # TODO(vish): these redis commands should be generalized and + # placed into a base class. Conceptually, it is + # similar to an association, but we are just + # storing a set of values instead of keys that + # should be turned into objects. + redis = datastore.Redis.instance() + key = 'ip:%s:ports' % ip + # TODO(vish): these ports should be allocated through an admin + # command instead of a flag + if (not redis.exists(key) and + not redis.exists(cls._redis_association_name('ip', ip))): + for i in range(FLAGS.vpn_start_port, FLAGS.vpn_end_port + 1): + redis.sadd(key, i) + + port = redis.spop(key) + if not port: + raise NoMorePorts() + return port + + @classmethod + def num_ports_for_ip(cls, ip): + return datastore.Redis.instance().scard('ip:%s:ports' % ip) + + @property + def ip(self): + return self['ip'] + + @property + def port(self): + return int(self['port']) + + def save(self): + self.associate_with('ip', self.ip) + super(Vpn, self).save() + + def destroy(self): + self.unassociate_with('ip', self.ip) + datastore.Redis.instance().sadd('ip:%s:ports' % self.ip, self.port) + super(Vpn, self).destroy() + + +class AuthManager(object): + """Manager Singleton for dealing with Users, Projects, and Keypairs + + Methods accept objects or ids. + + AuthManager uses a driver object to make requests to the data backend. + See ldapdriver.LdapDriver for reference. + + AuthManager also manages associated data related to Auth objects that + need to be more accessible, such as vpn ips and ports. + """ + _instance=None + def __new__(cls, *args, **kwargs): + if not cls._instance: + cls._instance = super(AuthManager, cls).__new__( + cls, *args, **kwargs) + return cls._instance + + def __init__(self, *args, **kwargs): + self.driver_class = kwargs.get('driver_class', ldapdriver.LdapDriver) + if FLAGS.fake_tests: + try: + self.create_user('fake', 'fake', 'fake') + except: pass + try: + self.create_user('user', 'user', 'user') + except: pass + try: + self.create_user('admin', 'admin', 'admin', True) + except: pass + + def authenticate(self, access, signature, params, verb='GET', + server_string='127.0.0.1:8773', path='/', + verify_signature=True): + """Authenticates AWS request using access key and signature + + If the project is not specified, attempts to authenticate to + a project with the same name as the user. This way, older tools + that have no project knowledge will still work. + + @type access: str + @param access: Access key for user in the form "access:project". + + @type signature: str + @param signature: Signature of the request. + + @type params: list of str + @param params: Web paramaters used for the signature. + + @type verb: str + @param verb: Web request verb ('GET' or 'POST'). + + @type server_string: str + @param server_string: Web request server string. + + @type path: str + @param path: Web request path. + + @type verify_signature: bool + @param verify_signature: Whether to verify the signature. + + @rtype: tuple (User, Project) + @return: User and project that the request represents. + """ + # TODO(vish): check for valid timestamp + (access_key, sep, project_name) = access.partition(':') + + user = self.get_user_from_access_key(access_key) + if user == None: + raise exception.NotFound('No user found for access key %s' % + access_key) + if project_name is '': + project_name = user.name + + project = self.get_project(project_name) + if project == None: + raise exception.NotFound('No project called %s could be found' % + project_name) + if not self.is_admin(user) and not self.is_project_member(user, + project): + raise exception.NotFound('User %s is not a member of project %s' % + (user.id, project.id)) + if verify_signature: + # 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) + if signature != expected_signature: + raise exception.NotAuthorized('Signature does not match') + return (user, project) + + def is_superuser(self, user): + """Checks for superuser status, allowing user to bypass rbac + + @type user: User or uid + @param user: User to check. + + @rtype: bool + @return: True for superuser. + """ + if not isinstance(user, User): + user = self.get_user(user) + # NOTE(vish): admin flag on user represents superuser + if user.admin: + return True + for role in FLAGS.superuser_roles: + if self.has_role(user, role): + return True + + def is_admin(self, user): + """Checks for admin status, allowing user to access all projects + + @type user: User or uid + @param user: User to check. + + @rtype: bool + @return: True for admin. + """ + if not isinstance(user, User): + user = self.get_user(user) + if self.is_superuser(user): + return True + for role in FLAGS.global_roles: + if self.has_role(user, role): + return True + + def has_role(self, user, role, project=None): + """Checks existence of role for user + + If project is not specified, checks for a global role. If project + is specified, checks for the union of the global role and the + project role. + + Role 'projectmanager' only works for projects and simply checks to + see if the user is the project_manager of the specified project. It + is the same as calling is_project_manager(user, project). + + @type user: User or uid + @param user: User to check. + + @type role: str + @param role: Role to check. + + @type project: Project or project_id + @param project: Project in which to look for local role. + + @rtype: bool + @return: True if the user has the role. + """ + with self.driver_class() as drv: + if role == 'projectmanager': + if not project: + raise exception.Error("Must specify project") + return self.is_project_manager(user, project) + + global_role = drv.has_role(User.safe_id(user), + role, + None) + if not global_role: + return global_role + + if not project or role in FLAGS.global_roles: + return global_role + + return drv.has_role(User.safe_id(user), + role, + Project.safe_id(project)) + + def add_role(self, user, role, project=None): + """Adds role for user + + If project is not specified, adds a global role. If project + is specified, adds a local role. + + The 'projectmanager' role is special and can't be added or removed. + + @type user: User or uid + @param user: User to which to add role. + + @type role: str + @param role: Role to add. + + @type project: Project or project_id + @param project: Project in which to add local role. + """ + with self.driver_class() as drv: + drv.add_role(User.safe_id(user), role, Project.safe_id(project)) + + def remove_role(self, user, role, project=None): + """Removes role for user + + If project is not specified, removes a global role. If project + is specified, removes a local role. + + The 'projectmanager' role is special and can't be added or removed. + + @type user: User or uid + @param user: User from which to remove role. + + @type role: str + @param role: Role to remove. + + @type project: Project or project_id + @param project: Project in which to remove local role. + """ + with self.driver_class() as drv: + drv.remove_role(User.safe_id(user), role, Project.safe_id(project)) + + def create_project(self, name, manager_user, + description=None, member_users=None): + """Create a project + + @type name: str + @param name: Name of the project to create. The name will also be + used as the project id. + + @type manager_user: User or uid + @param manager_user: This user will be the project manager. + + @type description: str + @param project: Description of the project. If no description is + specified, the name of the project will be used. + + @type member_users: list of User or uid + @param: Initial project members. The project manager will always be + added as a member, even if he isn't specified in this list. + + @rtype: Project + @return: The new project. + """ + if member_users: + member_users = [User.safe_id(u) for u in member_users] + # NOTE(vish): try to associate a vpn ip and port first because + # if it throws an exception, we save having to + # create and destroy a project + Vpn.create(name) + with self.driver_class() as drv: + return drv.create_project(name, + User.safe_id(manager_user), + description, + member_users) + + def get_projects(self): + """Retrieves list of all projects""" + with self.driver_class() as drv: + return drv.get_projects() + + + def get_project(self, project): + """Get project object by id""" + with self.driver_class() as drv: + return drv.get_project(Project.safe_id(project)) + + def add_to_project(self, user, project): + """Add user to project""" + with self.driver_class() as drv: + return drv.add_to_project(User.safe_id(user), + Project.safe_id(project)) + + def is_project_manager(self, user, project): + """Checks if user is project manager""" + if not isinstance(project, Project): + project = self.get_project(project) + return User.safe_id(user) == project.project_manager_id + + def is_project_member(self, user, project): + """Checks to see if user is a member of project""" + if not isinstance(project, Project): + project = self.get_project(project) + return User.safe_id(user) in project.member_ids + + def remove_from_project(self, user, project): + """Removes a user from a project""" + with self.driver_class() as drv: + return drv.remove_from_project(User.safe_id(user), + Project.safe_id(project)) + + def delete_project(self, project): + """Deletes a project""" + with self.driver_class() as drv: + return drv.delete_project(Project.safe_id(project)) + + def get_user(self, uid): + """Retrieves a user by id""" + with self.driver_class() as drv: + return drv.get_user(uid) + + def get_user_from_access_key(self, access_key): + """Retrieves a user by access key""" + with self.driver_class() as drv: + return drv.get_user_from_access_key(access_key) + + def get_users(self): + """Retrieves a list of all users""" + with self.driver_class() as drv: + return drv.get_users() + + def create_user(self, user, access=None, secret=None, + admin=False, create_project=True): + """Creates a user + + @type user: str + @param name: Name of the user to create. The name will also be + used as the user id. + + @type access: str + @param access: Access Key (defaults to a random uuid) + + @type secret: str + @param secret: Secret Key (defaults to a random uuid) + + @type admin: bool + @param admin: Whether to set the admin flag. The admin flag gives + superuser status regardless of roles specifed for the user. + + @type create_project: bool + @param: Whether to create a project for the user with the same name. + + @rtype: User + @return: The new user. + """ + if access == None: access = str(uuid.uuid4()) + if secret == None: secret = str(uuid.uuid4()) + with self.driver_class() as drv: + user = User.safe_id(user) + result = drv.create_user(user, access, secret, admin) + if create_project: + # NOTE(vish): if the project creation fails, we delete + # the user and return an exception + try: + drv.create_project(user, user, user) + except Exception: + with self.driver_class() as drv: + drv.delete_user(user) + raise + return result + + def delete_user(self, user, delete_project=True): + """Deletes a user""" + with self.driver_class() as drv: + user = User.safe_id(user) + if delete_project: + try: + drv.delete_project(user) + except exception.NotFound: + pass + drv.delete_user(user) + + def generate_key_pair(self, user, key_name): + """Generates a key pair for a user + + Generates a public and private key, stores the public key using the + key_name, and returns the private key and fingerprint. + + @type user: User or uid + @param user: User for which to create key pair. + + @type key_name: str + @param key_name: Name to use for the generated KeyPair. + + @rtype: tuple (private_key, fingerprint) + @return: A tuple containing the private_key and fingerprint. + """ + # NOTE(vish): generating key pair is slow so check for legal + # creation before creating keypair + uid = User.safe_id(user) + with self.driver_class() as drv: + if not drv.get_user(uid): + raise exception.NotFound("User %s doesn't exist" % user) + if drv.get_key_pair(uid, key_name): + raise exception.Duplicate("The keypair %s already exists" + % key_name) + private_key, public_key, fingerprint = crypto.generate_key_pair() + self.create_key_pair(uid, key_name, public_key, fingerprint) + return private_key, fingerprint + + def create_key_pair(self, user, key_name, public_key, fingerprint): + """Creates a key pair for user""" + with self.driver_class() as drv: + return drv.create_key_pair(User.safe_id(user), key_name, + public_key, fingerprint) + + def get_key_pair(self, user, key_name): + """Retrieves a key pair for user""" + with self.driver_class() as drv: + return drv.get_key_pair(User.safe_id(user), key_name) + + def get_key_pairs(self, user): + """Retrieves all key pairs for user""" + with self.driver_class() as drv: + return drv.get_key_pairs(User.safe_id(user)) + + def delete_key_pair(self, user, key_name): + """Deletes a key pair for user""" + with self.driver_class() as drv: + drv.delete_key_pair(User.safe_id(user), key_name) + + def get_credentials(self, user, project=None): + """Get credential zip for user in project""" + if not isinstance(user, User): + user = self.get_user(user) + if project is None: + project = user.id + pid = Project.safe_id(project) + rc = self.__generate_rc(user.access, user.secret, pid) + private_key, signed_cert = self.__generate_x509_cert(user.id, pid) + + vpn = Vpn(pid) + configfile = open(FLAGS.vpn_client_template,"r") + s = string.Template(configfile.read()) + configfile.close() + config = s.substitute(keyfile=FLAGS.credential_key_file, + certfile=FLAGS.credential_cert_file, + ip=vpn.ip, + port=vpn.port) + + tmpdir = tempfile.mkdtemp() + zf = os.path.join(tmpdir, "temp.zip") + zippy = zipfile.ZipFile(zf, 'w') + zippy.writestr(FLAGS.credential_rc_file, rc) + zippy.writestr(FLAGS.credential_key_file, private_key) + zippy.writestr(FLAGS.credential_cert_file, signed_cert) + zippy.writestr("nebula-client.conf", config) + zippy.writestr(FLAGS.ca_file, crypto.fetch_ca(user.id)) + zippy.close() + with open(zf, 'rb') as f: + buffer = f.read() + + shutil.rmtree(tmpdir) + return buffer + + def __generate_rc(self, access, secret, pid): + """Generate rc file for user""" + rc = open(FLAGS.credentials_template).read() + rc = rc % { 'access': access, + 'project': pid, + 'secret': secret, + 'ec2': FLAGS.ec2_url, + 's3': 'http://%s:%s' % (FLAGS.s3_host, FLAGS.s3_port), + 'nova': FLAGS.ca_file, + 'cert': FLAGS.credential_cert_file, + 'key': FLAGS.credential_key_file, + } + return rc + + def __generate_x509_cert(self, uid, pid): + """Generate x509 cert for user""" + (private_key, csr) = crypto.generate_x509_cert( + self.__cert_subject(uid)) + # TODO(joshua): This should be async call back to the cloud controller + signed_cert = crypto.sign_csr(csr, pid) + return (private_key, signed_cert) + + def __cert_subject(self, uid): + """Helper to generate cert subject""" + return FLAGS.credential_cert_subject % (uid, utils.isotime()) diff --git a/nova/auth/rbac.py b/nova/auth/rbac.py index 9e2bb830c..7fab9419f 100644 --- a/nova/auth/rbac.py +++ b/nova/auth/rbac.py @@ -17,7 +17,7 @@ # under the License. from nova import exception -from nova.auth import users +from nova.auth import manager def allow(*roles): diff --git a/nova/auth/users.py b/nova/auth/users.py deleted file mode 100644 index fc08dc34d..000000000 --- a/nova/auth/users.py +++ /dev/null @@ -1,974 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2010 United States Government as represented by the -# Administrator of the National Aeronautics and Space Administration. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -""" -Nova users and user management, including RBAC hooks. -""" - -import datetime -import logging -import os -import shutil -import signer -import string -import tempfile -import uuid -import zipfile - -try: - import ldap -except Exception, e: - import fakeldap as ldap - -import fakeldap - -# TODO(termie): clean up these imports -from nova import datastore -from nova import exception -from nova import flags -from nova import crypto -from nova import utils -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_password', 'changeme', 'LDAP password') -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') - -# 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', - 'cn=itsec,ou=Groups,dc=example,dc=com', 'cn for ItSec') -flags.DEFINE_string('ldap_sysadmin', - 'cn=sysadmins,ou=Groups,dc=example,dc=com', 'cn for Sysadmins') -flags.DEFINE_string('ldap_netadmin', - 'cn=netadmins,ou=Groups,dc=example,dc=com', 'cn for NetAdmins') -flags.DEFINE_string('ldap_developer', - 'cn=developers,ou=Groups,dc=example,dc=com', 'cn for Developers') - -# 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') - -# 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'), - 'Template for creating users rc file') -flags.DEFINE_string('vpn_client_template', - utils.abspath('cloudpipe/client.ovpn.template'), - 'Template for creating users vpn file') -flags.DEFINE_string('credential_key_file', 'pk.pem', - 'Filename of private key in credentials zip') -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_integer('vpn_start_port', 1000, - 'Start port for the cloudpipe VPN servers') -flags.DEFINE_integer('vpn_end_port', 2000, - 'End port for the cloudpipe VPN servers') - -flags.DEFINE_string('credential_cert_subject', - '/C=US/ST=California/L=MountainView/O=AnsoLabs/' - 'OU=NovaDev/CN=%s-%s', - 'Subject for certificate for users') - -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): - """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: - return obj - - -class User(AuthBase): - """id and name are currently the same""" - def __init__(self, id, name, access, secret, admin): - self.id = id - self.name = name - self.access = access - self.secret = secret - self.admin = admin - - def is_superuser(self): - """allows user to bypass rbac completely""" - if self.admin: - return True - for role in FLAGS.superuser_roles: - if self.has_role(role): - return True - - def is_admin(self): - """allows user to see objects from all projects""" - if self.is_superuser(): - return True - for role in FLAGS.global_roles: - if self.has_role(role): - return True - - def has_role(self, role): - return UserManager.instance().has_role(self, role) - - def add_role(self, role): - return UserManager.instance().add_role(self, role) - - def remove_role(self, role): - return UserManager.instance().remove_role(self, role) - - def is_project_member(self, project): - return UserManager.instance().is_project_member(self, project) - - def is_project_manager(self, project): - return UserManager.instance().is_project_manager(self, project) - - def generate_rc(self, project=None): - if project is None: - project = self.id - rc = open(FLAGS.credentials_template).read() - rc = rc % { 'access': self.access, - 'project': project, - 'secret': self.secret, - 'ec2': FLAGS.ec2_url, - 's3': 'http://%s:%s' % (FLAGS.s3_host, FLAGS.s3_port), - 'nova': FLAGS.ca_file, - 'cert': FLAGS.credential_cert_file, - 'key': FLAGS.credential_key_file, - } - return rc - - def generate_key_pair(self, name): - return UserManager.instance().generate_key_pair(self.id, name) - - def create_key_pair(self, name, public_key, fingerprint): - return UserManager.instance().create_key_pair(self.id, - name, - public_key, - fingerprint) - - def get_key_pair(self, name): - return UserManager.instance().get_key_pair(self.id, name) - - def delete_key_pair(self, name): - return UserManager.instance().delete_key_pair(self.id, name) - - def get_key_pairs(self): - 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) - - -class KeyPair(AuthBase): - def __init__(self, id, owner_id, public_key, fingerprint): - self.id = id - self.name = id - self.owner_id = owner_id - self.public_key = public_key - self.fingerprint = fingerprint - - def delete(self): - 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) - - -class Group(AuthBase): - """id and name are currently the same""" - def __init__(self, id, description = None, member_ids = None): - self.id = id - self.name = id - self.description = description - self.member_ids = member_ids - - def has_member(self, user): - return User.safe_id(user) in self.member_ids - - def __repr__(self): - 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): - self.project_manager_id = project_manager_id - super(Project, self).__init__(id, description, member_ids) - - @property - def project_manager(self): - return UserManager.instance().get_user(self.project_manager_id) - - def has_manager(self, user): - return User.safe_id(user) == self.project_manager_id - - def add_role(self, user, role): - return UserManager.instance().add_role(user, role, self) - - def remove_role(self, user, role): - return UserManager.instance().remove_role(user, role, self) - - def has_role(self, user, role): - return UserManager.instance().has_role(user, role, self) - - @property - def vpn_ip(self): - return Vpn(self.id).ip - - @property - def vpn_port(self): - return Vpn(self.id).port - - def get_credentials(self, user): - if not isinstance(user, User): - user = UserManager.instance().get_user(user) - rc = user.generate_rc(self.id) - private_key, signed_cert = self.generate_x509_cert(user) - - configfile = open(FLAGS.vpn_client_template,"r") - s = string.Template(configfile.read()) - configfile.close() - config = s.substitute(keyfile=FLAGS.credential_key_file, - certfile=FLAGS.credential_cert_file, - ip=self.vpn_ip, - port=self.vpn_port) - - tmpdir = tempfile.mkdtemp() - zf = os.path.join(tmpdir, "temp.zip") - zippy = zipfile.ZipFile(zf, 'w') - zippy.writestr(FLAGS.credential_rc_file, rc) - zippy.writestr(FLAGS.credential_key_file, private_key) - zippy.writestr(FLAGS.credential_cert_file, signed_cert) - zippy.writestr("nebula-client.conf", config) - zippy.writestr(FLAGS.ca_file, crypto.fetch_ca(self.id)) - zippy.close() - with open(zf, 'rb') as f: - buffer = f.read() - - shutil.rmtree(tmpdir) - return buffer - - def generate_x509_cert(self, user): - 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) - - -class NoMorePorts(exception.Error): - pass - - -class Vpn(datastore.BasicModel): - def __init__(self, project_id): - self.project_id = project_id - super(Vpn, self).__init__() - - @property - def identifier(self): - return self.project_id - - @classmethod - def create(cls, project_id): - # TODO(vish): get list of vpn ips from redis - port = cls.find_free_port_for_ip(FLAGS.vpn_ip) - vpn = cls(project_id) - # save ip for project - vpn['project'] = project_id - vpn['ip'] = FLAGS.vpn_ip - vpn['port'] = port - vpn.save() - return vpn - - @classmethod - def find_free_port_for_ip(cls, ip): - # TODO(vish): these redis commands should be generalized and - # placed into a base class. Conceptually, it is - # similar to an association, but we are just - # storing a set of values instead of keys that - # should be turned into objects. - redis = datastore.Redis.instance() - key = 'ip:%s:ports' % ip - # TODO(vish): these ports should be allocated through an admin - # command instead of a flag - if (not redis.exists(key) and - not redis.exists(cls._redis_association_name('ip', ip))): - for i in range(FLAGS.vpn_start_port, FLAGS.vpn_end_port + 1): - redis.sadd(key, i) - - port = redis.spop(key) - if not port: - raise NoMorePorts() - return port - - @classmethod - def num_ports_for_ip(cls, ip): - return datastore.Redis.instance().scard('ip:%s:ports' % ip) - - @property - def ip(self): - return self['ip'] - - @property - def port(self): - return int(self['port']) - - def save(self): - self.associate_with('ip', self.ip) - super(Vpn, self).save() - - def destroy(self): - self.unassociate_with('ip', self.ip) - datastore.Redis.instance().sadd('ip:%s:ports' % self.ip, self.port) - super(Vpn, self).destroy() - - -class UserManager(object): - def __init__(self): - if hasattr(self.__class__, '_instance'): - raise Exception('Attempted to instantiate singleton') - - @classmethod - def instance(cls): - if not hasattr(cls, '_instance'): - inst = UserManager() - cls._instance = inst - if FLAGS.fake_users: - try: - inst.create_user('fake', 'fake', 'fake') - except: pass - try: - inst.create_user('user', 'user', 'user') - except: pass - try: - inst.create_user('admin', 'admin', 'admin', True) - except: pass - return cls._instance - - 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(':') - - user = self.get_user_from_access_key(access_key) - if user == None: - raise exception.NotFound('No user found for access key %s' % - access_key) - if project_name is '': - project_name = user.name - - project = self.get_project(project_name) - if project == None: - 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)) - if verify_signature: - # 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) - if signature != expected_signature: - raise exception.NotAuthorized('Signature does not match') - return (user, project) - - def has_role(self, user, role, project=None): - with LDAPWrapper() as conn: - if role == 'projectmanager': - if not project: - raise exception.Error("Must specify project") - return self.is_project_manager(user, project) - - global_role = conn.has_role(User.safe_id(user), - role, - None) - if not global_role: - return global_role - - if not project or role in FLAGS.global_roles: - return global_role - - return conn.has_role(User.safe_id(user), - role, - Project.safe_id(project)) - - 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)) - - 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)) - - 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] - # NOTE(vish): try to associate a vpn ip and port first because - # if it throws an exception, we save having to - # create and destroy a project - Vpn.create(name) - with LDAPWrapper() as conn: - return conn.create_project(name, - User.safe_id(manager_user), - description, - member_users) - - - def get_projects(self): - with LDAPWrapper() as conn: - return conn.find_projects() - - - def get_project(self, project): - with LDAPWrapper() as conn: - return conn.find_project(Project.safe_id(project)) - - def add_to_project(self, user, project): - with LDAPWrapper() as conn: - 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): - project = self.get_project(project) - return project.has_manager(user) - - def is_project_member(self, user, project): - if isinstance(project, Project): - return project.has_member(user) - else: - with LDAPWrapper() as conn: - return conn.is_in_project(User.safe_id(user), project) - - def remove_from_project(self, user, project): - with LDAPWrapper() as conn: - return conn.remove_from_project(User.safe_id(user), - Project.safe_id(project)) - - def delete_project(self, project): - with LDAPWrapper() as conn: - return conn.delete_project(Project.safe_id(project)) - - def get_user(self, uid): - with LDAPWrapper() as conn: - return conn.find_user(uid) - - def get_user_from_access_key(self, access_key): - with LDAPWrapper() as conn: - return conn.find_user_by_access_key(access_key) - - def get_users(self): - with LDAPWrapper() as conn: - return conn.find_users() - - 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: - user = User.safe_id(user) - result = conn.create_user(user, access, secret, admin) - if create_project: - # NOTE(vish): if the project creation fails, we delete - # the user and return an exception - try: - conn.create_project(user, user, user) - except Exception: - with LDAPWrapper() as conn: - conn.delete_user(user) - raise - return result - - def delete_user(self, user, delete_project=True): - with LDAPWrapper() as conn: - user = User.safe_id(user) - if delete_project: - try: - conn.delete_project(user) - except exception.NotFound: - pass - conn.delete_user(user) - - def generate_key_pair(self, user, key_name): - # generating key pair is slow so delay generation - # until after check - user = User.safe_id(user) - with LDAPWrapper() as conn: - if not conn.user_exists(user): - raise exception.NotFound("User %s doesn't exist" % user) - if conn.key_pair_exists(user, key_name): - raise exception.Duplicate("The keypair %s already exists" - % key_name) - private_key, public_key, fingerprint = crypto.generate_key_pair() - self.create_key_pair(User.safe_id(user), key_name, - public_key, fingerprint) - return private_key, fingerprint - - def create_key_pair(self, user, key_name, public_key, fingerprint): - with LDAPWrapper() as conn: - return conn.create_key_pair(User.safe_id(user), key_name, - public_key, fingerprint) - - def get_key_pair(self, user, key_name): - with LDAPWrapper() as conn: - return conn.find_key_pair(User.safe_id(user), key_name) - - def get_key_pairs(self, user): - with LDAPWrapper() as conn: - return conn.find_key_pairs(User.safe_id(user)) - - def delete_key_pair(self, user, key_name): - with LDAPWrapper() as conn: - conn.delete_key_pair(User.safe_id(user), key_name) - - def generate_x509_cert(self, user, project): - (private_key, csr) = crypto.generate_x509_cert( - self.__cert_subject(User.safe_id(user))) - # TODO - This should be async call back to the cloud controller - signed_cert = crypto.sign_csr(csr, Project.safe_id(project)) - return (private_key, signed_cert) - - def __cert_subject(self, uid): - # FIXME(ja) - this should be pulled from a global configuration - return FLAGS.credential_cert_subject % (uid, utils.isotime()) - - -class LDAPWrapper(object): - def __init__(self): - self.user = FLAGS.user_dn - self.passwd = FLAGS.ldap_password - - def __enter__(self): - self.connect() - return self - - def __exit__(self, type, value, traceback): - self.conn.unbind_s() - return False - - 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: - 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) - - def find_object(self, dn, query = None): - objects = self.find_objects(dn, query) - if len(objects) == 0: - return None - return objects[0] - - def find_dns(self, dn, query=None): - try: - res = self.conn.search_s(dn, ldap.SCOPE_SUBTREE, query) - except self.NO_SUCH_OBJECT: - return [] - # just return the DNs - return [dn for dn, attributes in res] - - def find_objects(self, dn, query = None): - try: - res = self.conn.search_s(dn, ldap.SCOPE_SUBTREE, query) - 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)') - 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)') - 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)') - return [self.__to_project(attr) for attr in attrs] - - def find_roles(self, tree): - 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): - dns = self.find_dns(tree, - '(&(objectclass=groupOfNames)(member=%s))' % - self.__uid_to_dn(uid)) - return dns - - def find_user(self, uid): - attr = self.find_object(self.__uid_to_dn(uid), - '(objectclass=novaUser)') - return self.__to_user(attr) - - def find_key_pair(self, uid, key_name): - dn = 'cn=%s,%s' % (key_name, - self.__uid_to_dn(uid)) - attr = self.find_object(dn, '(objectclass=novaKeyPair)') - return self.__to_key_pair(uid, attr) - - def find_group(self, dn): - """uses dn directly instead of custructing it from name""" - attr = self.find_object(dn, '(objectclass=groupOfNames)') - return self.__to_group(attr) - - def find_project(self, name): - dn = 'cn=%s,%s' % (name, - FLAGS.project_ldap_subtree) - attr = self.find_object(dn, '(objectclass=novaProject)') - return self.__to_project(attr) - - def user_exists(self, name): - return self.find_user(name) != None - - def key_pair_exists(self, uid, key_name): - return self.find_key_pair(uid, key_name) != None - - def project_exists(self, name): - return self.find_project(name) != None - - def group_exists(self, dn): - return self.find_group(dn) != None - - def delete_key_pairs(self, uid): - keys = self.find_key_pairs(uid) - if keys != None: - for key in keys: - self.delete_key_pair(uid, key.name) - - def create_user(self, name, access_key, secret_key, is_admin): - if self.user_exists(name): - raise exception.Duplicate("LDAP user %s already exists" % name) - attr = [ - ('objectclass', ['person', - 'organizationalPerson', - 'inetOrgPerson', - 'novaUser']), - ('ou', [FLAGS.user_unit]), - ('uid', [name]), - ('sn', [name]), - ('cn', [name]), - ('secretKey', [secret_key]), - ('accessKey', [access_key]), - ('isAdmin', [str(is_admin).upper()]), - ] - 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): - if self.project_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) - manager_dn = self.__uid_to_dn(manager_uid) - # description is a required attribute - if description is None: - description = name - members = [] - 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) - 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: - members.append(manager_dn) - attr = [ - ('objectclass', ['novaProject']), - ('cn', [name]), - ('description', [description]), - ('projectManager', [manager_dn]), - ('member', members) - ] - self.conn.add_s('cn=%s,%s' % (name, FLAGS.project_ldap_subtree), attr) - return self.__to_project(dict(attr)) - - def add_to_project(self, uid, project_id): - dn = 'cn=%s,%s' % (project_id, FLAGS.project_ldap_subtree) - return self.add_to_group(uid, dn) - - def remove_from_project(self, uid, project_id): - dn = 'cn=%s,%s' % (project_id, FLAGS.project_ldap_subtree) - return self.remove_from_group(uid, dn) - - def is_in_project(self, uid, project_id): - dn = 'cn=%s,%s' % (project_id, FLAGS.project_ldap_subtree) - return self.is_in_group(uid, dn) - - def __role_to_dn(self, role, project_id=None): - 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) - - def __create_group(self, group_dn, name, uid, - description, member_uids = None): - if self.group_exists(group_dn): - 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) - members.append(self.__uid_to_dn(member_uid)) - dn = self.__uid_to_dn(uid) - if not dn in members: - members.append(dn) - attr = [ - ('objectclass', ['groupOfNames']), - ('cn', [name]), - ('description', [description]), - ('member', members) - ] - self.conn.add_s(group_dn, attr) - return self.__to_group(dict(attr)) - - def has_role(self, uid, role, project_id=None): - role_dn = self.__role_to_dn(role, project_id) - return self.is_in_group(uid, role_dn) - - def add_role(self, uid, role, project_id=None): - role_dn = self.__role_to_dn(role, project_id) - if not self.group_exists(role_dn): - # create the role if it doesn't exist - description = '%s role for %s' % (role, project_id) - self.__create_group(role_dn, role, uid, description) - else: - return self.add_to_group(uid, role_dn) - - def remove_role(self, uid, role, project_id=None): - role_dn = self.__role_to_dn(role, project_id) - 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,)) - if not self.group_exists(group_dn): - return False - res = self.find_object(group_dn, - '(member=%s)' % self.__uid_to_dn(uid)) - return res != None - - 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,)) - if not self.group_exists(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)) - attr = [ - (ldap.MOD_ADD, 'member', self.__uid_to_dn(uid)) - ] - self.conn.modify_s(group_dn, attr) - - 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,)) - 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,)) - if not self.is_in_group(uid, group_dn): - 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): - # FIXME(vish): what if deleted user is a project manager? - attr = [(ldap.MOD_DELETE, 'member', self.__uid_to_dn(uid))] - try: - self.conn.modify_s(group_dn, attr) - 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,)) - dn = self.__uid_to_dn(uid) - role_dns = self.find_group_dns_with_member( - FLAGS.role_ldap_subtree, uid) - for role_dn in role_dns: - self._safe_remove_from_group(role_dn, uid) - project_dns = self.find_group_dns_with_member( - FLAGS.project_ldap_subtree, uid) - for project_dn in project_dns: - self._safe_remove_from_group(project_dn, uid) - - def create_key_pair(self, uid, key_name, public_key, fingerprint): - """create's a public key in the directory underneath the user""" - # TODO(vish): possibly refactor this to store keys in their own ou - # and put dn reference in the user object - attr = [ - ('objectclass', ['novaKeyPair']), - ('cn', [key_name]), - ('sshPublicKey', [public_key]), - ('keyFingerprint', [fingerprint]), - ] - self.conn.add_s('cn=%s,%s' % (key_name, - self.__uid_to_dn(uid)), - attr) - return self.__to_key_pair(uid, dict(attr)) - - def find_user_by_access_key(self, access): - query = '(accessKey=%s)' % access - dn = FLAGS.user_ldap_subtree - return self.__to_user(self.find_object(dn, query)) - - def delete_user(self, uid): - if not self.user_exists(uid): - raise exception.NotFound("User %s doesn't exist" % uid) - self.delete_key_pairs(uid) - self.remove_from_all(uid) - self.conn.delete_s('uid=%s,%s' % (uid, - FLAGS.user_ldap_subtree)) - - def delete_key_pair(self, uid, key_name): - if not self.key_pair_exists(uid, key_name): - raise exception.NotFound("Key Pair %s doesn't exist for user %s" % - (key_name, uid)) - self.conn.delete_s('cn=%s,uid=%s,%s' % (key_name, uid, - FLAGS.user_ldap_subtree)) - - def delete_group(self, group_dn): - if not self.group_exists(group_dn): - raise exception.NotFound("Group at dn %s doesn't exist" % group_dn) - self.conn.delete_s(group_dn) - - def delete_roles(self, project_dn): - roles = self.find_roles(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) - self.delete_roles(project_dn) - self.delete_group(project_dn) - - def __to_user(self, attr): - if attr == None: - return None - return User( - id = attr['uid'][0], - name = attr['cn'][0], - access = attr['accessKey'][0], - secret = attr['secretKey'][0], - admin = (attr['isAdmin'][0] == 'TRUE') - ) - - def __to_key_pair(self, owner, attr): - if attr == None: - return None - return KeyPair( - id = attr['cn'][0], - owner_id = owner, - public_key = attr['sshPublicKey'][0], - fingerprint = attr['keyFingerprint'][0], - ) - - def __to_group(self, attr): - if attr == None: - return None - member_dns = attr.get('member', []) - return Group( - id = attr['cn'][0], - description = attr.get('description', [None])[0], - member_ids = [self.__dn_to_uid(x) for x in member_dns] - ) - - def __to_project(self, attr): - if attr == None: - return None - member_dns = attr.get('member', []) - return Project( - id = attr['cn'][0], - project_manager_id = self.__dn_to_uid(attr['projectManager'][0]), - description = attr.get('description', [None])[0], - member_ids = [self.__dn_to_uid(x) for x in member_dns] - ) - - def __dn_to_uid(self, dn): - return dn.split(',')[0].split('=')[1] - - def __uid_to_dn(self, dn): - return 'uid=%s,%s' % (dn, FLAGS.user_ldap_subtree) diff --git a/nova/cloudpipe/api.py b/nova/cloudpipe/api.py index a5f78a16d..0bffe9aa3 100644 --- a/nova/cloudpipe/api.py +++ b/nova/cloudpipe/api.py @@ -25,7 +25,7 @@ import tornado.web import urllib from nova import crypto -from nova.auth import users +from nova.auth import manager _log = logging.getLogger("api") diff --git a/nova/cloudpipe/pipelib.py b/nova/cloudpipe/pipelib.py index 63f7ae222..5b0ed3471 100644 --- a/nova/cloudpipe/pipelib.py +++ b/nova/cloudpipe/pipelib.py @@ -31,7 +31,7 @@ import zipfile from nova import exception from nova import flags from nova import utils -from nova.auth import users +from nova.auth import manager from nova.endpoint import api @@ -44,7 +44,7 @@ flags.DEFINE_string('boot_script_template', class CloudPipe(object): def __init__(self, cloud_controller): self.controller = cloud_controller - self.manager = users.UserManager.instance() + self.manager = manager.AuthManager() def launch_vpn_instance(self, project_id): logging.debug( "Launching VPN for %s" % (project_id)) diff --git a/nova/compute/network.py b/nova/compute/network.py index 90d6b2dc6..370e2bf44 100644 --- a/nova/compute/network.py +++ b/nova/compute/network.py @@ -29,7 +29,7 @@ from nova import datastore from nova import exception from nova import flags from nova import utils -from nova.auth import users +from nova.auth import manager from nova.compute import exception as compute_exception from nova.compute import linux_net @@ -209,11 +209,11 @@ class BaseNetwork(datastore.BasicModel): @property def user(self): - return users.UserManager.instance().get_user(self['user_id']) + return manager.AuthManager().get_user(self['user_id']) @property def project(self): - return users.UserManager.instance().get_project(self['project_id']) + return manager.AuthManager().get_project(self['project_id']) @property def _hosts_key(self): @@ -511,7 +511,7 @@ def get_vlan_for_project(project_id): if not known_vlans.has_key(vstr): return Vlan.create(project_id, vnum) old_project_id = known_vlans[vstr] - if not users.UserManager.instance().get_project(old_project_id): + if not manager.AuthManager().get_project(old_project_id): vlan = Vlan.lookup(old_project_id) if vlan: # NOTE(todd): This doesn't check for vlan id match, because @@ -537,7 +537,7 @@ def get_network_by_interface(iface, security_group='default'): def get_network_by_address(address): logging.debug("Get Network By Address: %s" % address) - for project in users.UserManager.instance().get_projects(): + for project in manager.AuthManager().get_projects(): net = get_project_network(project.id) if address in net.assigned: logging.debug("Found %s in %s" % (address, project.id)) @@ -577,7 +577,7 @@ def get_project_network(project_id, security_group='default'): """ get a project's private network, allocating one if needed """ # TODO(todd): It looks goofy to get a project from a UserManager. # Refactor to still use the LDAP backend, but not User specific. - project = users.UserManager.instance().get_project(project_id) + project = manager.AuthManager().get_project(project_id) if not project: raise exception.Error("Project %s doesn't exist, uhoh." % project_id) @@ -587,5 +587,5 @@ def get_project_network(project_id, security_group='default'): def restart_nets(): """ Ensure the network for each user is enabled""" - for project in users.UserManager.instance().get_projects(): + for project in manager.AuthManager().get_projects(): get_project_network(project.id).express() diff --git a/nova/endpoint/admin.py b/nova/endpoint/admin.py index b97a6727f..55a8e4238 100644 --- a/nova/endpoint/admin.py +++ b/nova/endpoint/admin.py @@ -22,7 +22,7 @@ Admin API controller, exposed through http via the api worker. import base64 -from nova.auth import users +from nova.auth import manager from nova.compute import model def user_dict(user, base64_file=None): @@ -69,18 +69,18 @@ class AdminController(object): @admin_only def describe_user(self, _context, name, **_kwargs): """Returns user data, including access and secret keys.""" - return user_dict(users.UserManager.instance().get_user(name)) + return user_dict(manager.AuthManager().get_user(name)) @admin_only def describe_users(self, _context, **_kwargs): """Returns all users - should be changed to deal with a list.""" return {'userSet': - [user_dict(u) for u in users.UserManager.instance().get_users()] } + [user_dict(u) for u in manager.AuthManager().get_users()] } @admin_only def register_user(self, _context, name, **_kwargs): """Creates a new user, and returns generated credentials.""" - return user_dict(users.UserManager.instance().create_user(name)) + return user_dict(manager.AuthManager().create_user(name)) @admin_only def deregister_user(self, _context, name, **_kwargs): @@ -88,7 +88,7 @@ class AdminController(object): Should throw an exception if the user has instances, volumes, or buckets remaining. """ - users.UserManager.instance().delete_user(name) + manager.AuthManager().delete_user(name) return True @@ -100,8 +100,8 @@ class AdminController(object): """ if project is None: project = name - project = users.UserManager.instance().get_project(project) - user = users.UserManager.instance().get_user(name) + project = manager.AuthManager().get_project(project) + user = manager.AuthManager().get_user(name) return user_dict(user, base64.b64encode(project.get_credentials(user))) @admin_only diff --git a/nova/endpoint/api.py b/nova/endpoint/api.py index 79a2aaddb..78a18b9ea 100755 --- a/nova/endpoint/api.py +++ b/nova/endpoint/api.py @@ -35,7 +35,7 @@ from nova import crypto from nova import exception from nova import flags from nova import utils -from nova.auth import users +from nova.auth import manager import nova.cloudpipe.api from nova.endpoint import cloud @@ -266,7 +266,7 @@ class APIRequestHandler(tornado.web.RequestHandler): # Authenticate the request. try: - (user, project) = users.UserManager.instance().authenticate( + (user, project) = manager.AuthManager().authenticate( access, signature, auth_params, diff --git a/nova/endpoint/cloud.py b/nova/endpoint/cloud.py index 3b7b4804b..8eac1ce4a 100644 --- a/nova/endpoint/cloud.py +++ b/nova/endpoint/cloud.py @@ -35,7 +35,7 @@ from nova import flags from nova import rpc from nova import utils from nova.auth import rbac -from nova.auth import users +from nova.auth import manager from nova.compute import model from nova.compute import network from nova.compute import node @@ -48,9 +48,9 @@ FLAGS = flags.FLAGS flags.DEFINE_string('cloud_topic', 'cloud', 'the topic clouds listen on') def _gen_key(user_id, key_name): - """ Tuck this into UserManager """ + """ Tuck this into AuthManager """ try: - manager = users.UserManager.instance() + manager = manager.AuthManager() private_key, fingerprint = manager.generate_key_pair(user_id, key_name) except Exception as ex: return {'exception': ex} diff --git a/nova/endpoint/rackspace.py b/nova/endpoint/rackspace.py index 9208ddab7..605f9b8e0 100644 --- a/nova/endpoint/rackspace.py +++ b/nova/endpoint/rackspace.py @@ -34,7 +34,7 @@ from nova import exception from nova import flags from nova import rpc from nova import utils -from nova.auth import users +from nova.auth import manager from nova.compute import model from nova.compute import network from nova.endpoint import images @@ -78,11 +78,11 @@ class Api(object): def build_context(self, env): rv = {} if env.has_key("HTTP_X_AUTH_TOKEN"): - rv['user'] = users.UserManager.instance().get_user_from_access_key( + rv['user'] = manager.AuthManager().get_user_from_access_key( env['HTTP_X_AUTH_TOKEN'] ) if rv['user']: - rv['project'] = users.UserManager.instance().get_project( + rv['project'] = manager.AuthManager().get_project( rv['user'].name ) return rv diff --git a/nova/tests/access_unittest.py b/nova/tests/access_unittest.py index 8500dd0cb..832a4b279 100644 --- a/nova/tests/access_unittest.py +++ b/nova/tests/access_unittest.py @@ -22,7 +22,7 @@ import logging from nova import exception from nova import flags from nova import test -from nova.auth.users import UserManager +from nova.auth import manager from nova.auth import rbac @@ -35,7 +35,7 @@ class AccessTestCase(test.BaseTestCase): super(AccessTestCase, self).setUp() FLAGS.fake_libvirt = True FLAGS.fake_storage = True - um = UserManager.instance() + um = manager.AuthManager() # Make test users try: self.testadmin = um.create_user('testadmin') @@ -79,7 +79,7 @@ class AccessTestCase(test.BaseTestCase): #user is set in each test def tearDown(self): - um = UserManager.instance() + um = manager.AuthManager() # Delete the test project um.delete_project('testproj') # Delete the test user diff --git a/nova/tests/api_unittest.py b/nova/tests/api_unittest.py index e5e2afe26..5c26192bd 100644 --- a/nova/tests/api_unittest.py +++ b/nova/tests/api_unittest.py @@ -26,7 +26,7 @@ from twisted.internet import defer from nova import flags from nova import test -from nova.auth import users +from nova.auth import manager from nova.endpoint import api from nova.endpoint import cloud @@ -150,7 +150,7 @@ class ApiEc2TestCase(test.BaseTestCase): def setUp(self): super(ApiEc2TestCase, self).setUp() - self.users = users.UserManager.instance() + self.users = manager.AuthManager() self.cloud = cloud.CloudController() self.host = '127.0.0.1' diff --git a/nova/tests/auth_unittest.py b/nova/tests/auth_unittest.py new file mode 100644 index 000000000..000f6bf17 --- /dev/null +++ b/nova/tests/auth_unittest.py @@ -0,0 +1,207 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2010 United States Government as represented by the +# Administrator of the National Aeronautics and Space Administration. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import logging +from M2Crypto import BIO +from M2Crypto import RSA +from M2Crypto import X509 +import unittest + +from nova import crypto +from nova import flags +from nova import test +from nova.auth import manager +from nova.endpoint import cloud + +FLAGS = flags.FLAGS + + +class AuthTestCase(test.BaseTestCase): + flush_db = False + def setUp(self): + super(AuthTestCase, self).setUp() + self.flags(fake_libvirt=True, + fake_storage=True) + self.users = manager.AuthManager() + + def test_001_can_create_users(self): + self.users.create_user('test1', 'access', 'secret') + self.users.create_user('test2') + + def test_002_can_get_user(self): + user = self.users.get_user('test1') + + def test_003_can_retreive_properties(self): + user = self.users.get_user('test1') + self.assertEqual('test1', user.id) + self.assertEqual('access', user.access) + self.assertEqual('secret', user.secret) + + def test_004_signature_is_valid(self): + #self.assertTrue(self.users.authenticate( **boto.generate_url ... ? ? ? )) + pass + #raise NotImplementedError + + def test_005_can_get_credentials(self): + return + credentials = self.users.get_user('test1').get_credentials() + self.assertEqual(credentials, + 'export EC2_ACCESS_KEY="access"\n' + + 'export EC2_SECRET_KEY="secret"\n' + + 'export EC2_URL="http://127.0.0.1:8773/services/Cloud"\n' + + 'export S3_URL="http://127.0.0.1:3333/"\n' + + 'export EC2_USER_ID="test1"\n') + + def test_006_test_key_storage(self): + user = self.users.get_user('test1') + user.create_key_pair('public', 'key', 'fingerprint') + key = user.get_key_pair('public') + self.assertEqual('key', key.public_key) + self.assertEqual('fingerprint', key.fingerprint) + + def test_007_test_key_generation(self): + user = self.users.get_user('test1') + private_key, fingerprint = user.generate_key_pair('public2') + key = RSA.load_key_string(private_key, callback=lambda: None) + bio = BIO.MemoryBuffer() + public_key = user.get_key_pair('public2').public_key + key.save_pub_key_bio(bio) + converted = crypto.ssl_pub_to_ssh_pub(bio.read()) + # assert key fields are equal + self.assertEqual(public_key.split(" ")[1].strip(), + converted.split(" ")[1].strip()) + + def test_008_can_list_key_pairs(self): + keys = self.users.get_user('test1').get_key_pairs() + self.assertTrue(filter(lambda k: k.name == 'public', keys)) + self.assertTrue(filter(lambda k: k.name == 'public2', keys)) + + def test_009_can_delete_key_pair(self): + self.users.get_user('test1').delete_key_pair('public') + keys = self.users.get_user('test1').get_key_pairs() + self.assertFalse(filter(lambda k: k.name == 'public', keys)) + + def test_010_can_list_users(self): + users = self.users.get_users() + logging.warn(users) + self.assertTrue(filter(lambda u: u.id == 'test1', users)) + + def test_101_can_add_user_role(self): + self.assertFalse(self.users.has_role('test1', 'itsec')) + self.users.add_role('test1', 'itsec') + self.assertTrue(self.users.has_role('test1', 'itsec')) + + def test_199_can_remove_user_role(self): + self.assertTrue(self.users.has_role('test1', 'itsec')) + self.users.remove_role('test1', 'itsec') + self.assertFalse(self.users.has_role('test1', 'itsec')) + + def test_201_can_create_project(self): + project = self.users.create_project('testproj', 'test1', 'A test project', ['test1']) + self.assertTrue(filter(lambda p: p.name == 'testproj', self.users.get_projects())) + self.assertEqual(project.name, 'testproj') + self.assertEqual(project.description, 'A test project') + self.assertEqual(project.project_manager_id, 'test1') + self.assertTrue(project.has_member('test1')) + + def test_202_user1_is_project_member(self): + self.assertTrue(self.users.get_user('test1').is_project_member('testproj')) + + def test_203_user2_is_not_project_member(self): + self.assertFalse(self.users.get_user('test2').is_project_member('testproj')) + + def test_204_user1_is_project_manager(self): + self.assertTrue(self.users.get_user('test1').is_project_manager('testproj')) + + def test_205_user2_is_not_project_manager(self): + self.assertFalse(self.users.get_user('test2').is_project_manager('testproj')) + + def test_206_can_add_user_to_project(self): + self.users.add_to_project('test2', 'testproj') + self.assertTrue(self.users.get_project('testproj').has_member('test2')) + + def test_208_can_remove_user_from_project(self): + self.users.remove_from_project('test2', 'testproj') + self.assertFalse(self.users.get_project('testproj').has_member('test2')) + + def test_209_can_generate_x509(self): + # MUST HAVE RUN CLOUD SETUP BY NOW + self.cloud = cloud.CloudController() + self.cloud.setup() + private_key, signed_cert_string = self.users.get_project('testproj').generate_x509_cert('test1') + logging.debug(signed_cert_string) + + # Need to verify that it's signed by the right intermediate CA + full_chain = crypto.fetch_ca(project_id='testproj', chain=True) + int_cert = crypto.fetch_ca(project_id='testproj', chain=False) + cloud_cert = crypto.fetch_ca() + logging.debug("CA chain:\n\n =====\n%s\n\n=====" % full_chain) + signed_cert = X509.load_cert_string(signed_cert_string) + chain_cert = X509.load_cert_string(full_chain) + int_cert = X509.load_cert_string(int_cert) + cloud_cert = X509.load_cert_string(cloud_cert) + self.assertTrue(signed_cert.verify(chain_cert.get_pubkey())) + self.assertTrue(signed_cert.verify(int_cert.get_pubkey())) + + if not FLAGS.use_intermediate_ca: + self.assertTrue(signed_cert.verify(cloud_cert.get_pubkey())) + else: + self.assertFalse(signed_cert.verify(cloud_cert.get_pubkey())) + + def test_210_can_add_project_role(self): + project = self.users.get_project('testproj') + self.assertFalse(project.has_role('test1', 'sysadmin')) + self.users.add_role('test1', 'sysadmin') + self.assertFalse(project.has_role('test1', 'sysadmin')) + project.add_role('test1', 'sysadmin') + self.assertTrue(project.has_role('test1', 'sysadmin')) + + def test_211_can_remove_project_role(self): + project = self.users.get_project('testproj') + self.assertTrue(project.has_role('test1', 'sysadmin')) + project.remove_role('test1', 'sysadmin') + self.assertFalse(project.has_role('test1', 'sysadmin')) + self.users.remove_role('test1', 'sysadmin') + self.assertFalse(project.has_role('test1', 'sysadmin')) + + def test_212_vpn_ip_and_port_looks_valid(self): + project = self.users.get_project('testproj') + self.assert_(project.vpn_ip) + self.assert_(project.vpn_port >= FLAGS.vpn_start_port) + self.assert_(project.vpn_port <= FLAGS.vpn_end_port) + + def test_213_too_many_vpns(self): + for i in xrange(users.Vpn.num_ports_for_ip(FLAGS.vpn_ip)): + users.Vpn.create("vpnuser%s" % i) + self.assertRaises(users.NoMorePorts, users.Vpn.create, "boom") + + def test_299_can_delete_project(self): + self.users.delete_project('testproj') + self.assertFalse(filter(lambda p: p.name == 'testproj', self.users.get_projects())) + + def test_999_can_delete_users(self): + self.users.delete_user('test1') + users = self.users.get_users() + self.assertFalse(filter(lambda u: u.id == 'test1', users)) + self.users.delete_user('test2') + self.assertEqual(self.users.get_user('test2'), None) + + +if __name__ == "__main__": + # TODO: Implement use_fake as an option + unittest.main() diff --git a/nova/tests/cloud_unittest.py b/nova/tests/cloud_unittest.py index b8614fdc8..3abef28a1 100644 --- a/nova/tests/cloud_unittest.py +++ b/nova/tests/cloud_unittest.py @@ -27,7 +27,7 @@ from xml.etree import ElementTree from nova import flags from nova import rpc from nova import test -from nova.auth import users +from nova.auth import manager from nova.compute import node from nova.endpoint import api from nova.endpoint import cloud @@ -61,15 +61,15 @@ class CloudTestCase(test.BaseTestCase): self.injected.append(self.node_consumer.attach_to_tornado(self.ioloop)) try: - users.UserManager.instance().create_user('admin', 'admin', 'admin') + manager.AuthManager().create_user('admin', 'admin', 'admin') except: pass - admin = users.UserManager.instance().get_user('admin') - project = users.UserManager.instance().create_project('proj', 'admin', 'proj') + admin = manager.AuthManager().get_user('admin') + project = manager.AuthManager().create_project('proj', 'admin', 'proj') self.context = api.APIRequestContext(handler=None,project=project,user=admin) def tearDown(self): - users.UserManager.instance().delete_project('proj') - users.UserManager.instance().delete_user('admin') + manager.AuthManager().delete_project('proj') + manager.AuthManager().delete_user('admin') def test_console_output(self): if FLAGS.fake_libvirt: diff --git a/nova/tests/network_unittest.py b/nova/tests/network_unittest.py index a822cc1d9..fd0e64724 100644 --- a/nova/tests/network_unittest.py +++ b/nova/tests/network_unittest.py @@ -26,7 +26,7 @@ from nova import test from nova import exception from nova.compute.exception import NoMoreAddresses from nova.compute import network -from nova.auth import users +from nova.auth import manager from nova import utils @@ -38,7 +38,7 @@ class NetworkTestCase(test.TrialTestCase): fake_network=True, network_size=32) logging.getLogger().setLevel(logging.DEBUG) - self.manager = users.UserManager.instance() + self.manager = manager.AuthManager() self.dnsmasq = FakeDNSMasq() try: self.manager.create_user('netuser', 'netuser', 'netuser') diff --git a/nova/tests/objectstore_unittest.py b/nova/tests/objectstore_unittest.py index f47ca7f00..85bcd7c67 100644 --- a/nova/tests/objectstore_unittest.py +++ b/nova/tests/objectstore_unittest.py @@ -26,7 +26,7 @@ import tempfile from nova import flags from nova import objectstore from nova import test -from nova.auth import users +from nova.auth import manager FLAGS = flags.FLAGS @@ -57,7 +57,7 @@ class ObjectStoreTestCase(test.BaseTestCase): ca_path=os.path.join(os.path.dirname(__file__), 'CA')) logging.getLogger().setLevel(logging.DEBUG) - self.um = users.UserManager.instance() + self.um = manager.AuthManager() try: self.um.create_user('user1') except: pass @@ -177,7 +177,7 @@ class ObjectStoreTestCase(test.BaseTestCase): # FLAGS.images_path = os.path.join(tempdir, 'images') # FLAGS.ca_path = os.path.join(os.path.dirname(__file__), 'CA') # -# self.users = users.UserManager.instance() +# self.users = manager.AuthManager() # self.app = handler.Application(self.users) # # self.host = '127.0.0.1' diff --git a/nova/tests/users_unittest.py b/nova/tests/users_unittest.py deleted file mode 100644 index 301721075..000000000 --- a/nova/tests/users_unittest.py +++ /dev/null @@ -1,207 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2010 United States Government as represented by the -# Administrator of the National Aeronautics and Space Administration. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import logging -from M2Crypto import BIO -from M2Crypto import RSA -from M2Crypto import X509 -import unittest - -from nova import crypto -from nova import flags -from nova import test -from nova.auth import users -from nova.endpoint import cloud - -FLAGS = flags.FLAGS - - -class UserTestCase(test.BaseTestCase): - flush_db = False - def setUp(self): - super(UserTestCase, self).setUp() - self.flags(fake_libvirt=True, - fake_storage=True) - self.users = users.UserManager.instance() - - def test_001_can_create_users(self): - self.users.create_user('test1', 'access', 'secret') - self.users.create_user('test2') - - def test_002_can_get_user(self): - user = self.users.get_user('test1') - - def test_003_can_retreive_properties(self): - user = self.users.get_user('test1') - self.assertEqual('test1', user.id) - self.assertEqual('access', user.access) - self.assertEqual('secret', user.secret) - - def test_004_signature_is_valid(self): - #self.assertTrue(self.users.authenticate( **boto.generate_url ... ? ? ? )) - pass - #raise NotImplementedError - - def test_005_can_get_credentials(self): - return - credentials = self.users.get_user('test1').get_credentials() - self.assertEqual(credentials, - 'export EC2_ACCESS_KEY="access"\n' + - 'export EC2_SECRET_KEY="secret"\n' + - 'export EC2_URL="http://127.0.0.1:8773/services/Cloud"\n' + - 'export S3_URL="http://127.0.0.1:3333/"\n' + - 'export EC2_USER_ID="test1"\n') - - def test_006_test_key_storage(self): - user = self.users.get_user('test1') - user.create_key_pair('public', 'key', 'fingerprint') - key = user.get_key_pair('public') - self.assertEqual('key', key.public_key) - self.assertEqual('fingerprint', key.fingerprint) - - def test_007_test_key_generation(self): - user = self.users.get_user('test1') - private_key, fingerprint = user.generate_key_pair('public2') - key = RSA.load_key_string(private_key, callback=lambda: None) - bio = BIO.MemoryBuffer() - public_key = user.get_key_pair('public2').public_key - key.save_pub_key_bio(bio) - converted = crypto.ssl_pub_to_ssh_pub(bio.read()) - # assert key fields are equal - self.assertEqual(public_key.split(" ")[1].strip(), - converted.split(" ")[1].strip()) - - def test_008_can_list_key_pairs(self): - keys = self.users.get_user('test1').get_key_pairs() - self.assertTrue(filter(lambda k: k.name == 'public', keys)) - self.assertTrue(filter(lambda k: k.name == 'public2', keys)) - - def test_009_can_delete_key_pair(self): - self.users.get_user('test1').delete_key_pair('public') - keys = self.users.get_user('test1').get_key_pairs() - self.assertFalse(filter(lambda k: k.name == 'public', keys)) - - def test_010_can_list_users(self): - users = self.users.get_users() - logging.warn(users) - self.assertTrue(filter(lambda u: u.id == 'test1', users)) - - def test_101_can_add_user_role(self): - self.assertFalse(self.users.has_role('test1', 'itsec')) - self.users.add_role('test1', 'itsec') - self.assertTrue(self.users.has_role('test1', 'itsec')) - - def test_199_can_remove_user_role(self): - self.assertTrue(self.users.has_role('test1', 'itsec')) - self.users.remove_role('test1', 'itsec') - self.assertFalse(self.users.has_role('test1', 'itsec')) - - def test_201_can_create_project(self): - project = self.users.create_project('testproj', 'test1', 'A test project', ['test1']) - self.assertTrue(filter(lambda p: p.name == 'testproj', self.users.get_projects())) - self.assertEqual(project.name, 'testproj') - self.assertEqual(project.description, 'A test project') - self.assertEqual(project.project_manager_id, 'test1') - self.assertTrue(project.has_member('test1')) - - def test_202_user1_is_project_member(self): - self.assertTrue(self.users.get_user('test1').is_project_member('testproj')) - - def test_203_user2_is_not_project_member(self): - self.assertFalse(self.users.get_user('test2').is_project_member('testproj')) - - def test_204_user1_is_project_manager(self): - self.assertTrue(self.users.get_user('test1').is_project_manager('testproj')) - - def test_205_user2_is_not_project_manager(self): - self.assertFalse(self.users.get_user('test2').is_project_manager('testproj')) - - def test_206_can_add_user_to_project(self): - self.users.add_to_project('test2', 'testproj') - self.assertTrue(self.users.get_project('testproj').has_member('test2')) - - def test_208_can_remove_user_from_project(self): - self.users.remove_from_project('test2', 'testproj') - self.assertFalse(self.users.get_project('testproj').has_member('test2')) - - def test_209_can_generate_x509(self): - # MUST HAVE RUN CLOUD SETUP BY NOW - self.cloud = cloud.CloudController() - self.cloud.setup() - private_key, signed_cert_string = self.users.get_project('testproj').generate_x509_cert('test1') - logging.debug(signed_cert_string) - - # Need to verify that it's signed by the right intermediate CA - full_chain = crypto.fetch_ca(project_id='testproj', chain=True) - int_cert = crypto.fetch_ca(project_id='testproj', chain=False) - cloud_cert = crypto.fetch_ca() - logging.debug("CA chain:\n\n =====\n%s\n\n=====" % full_chain) - signed_cert = X509.load_cert_string(signed_cert_string) - chain_cert = X509.load_cert_string(full_chain) - int_cert = X509.load_cert_string(int_cert) - cloud_cert = X509.load_cert_string(cloud_cert) - self.assertTrue(signed_cert.verify(chain_cert.get_pubkey())) - self.assertTrue(signed_cert.verify(int_cert.get_pubkey())) - - if not FLAGS.use_intermediate_ca: - self.assertTrue(signed_cert.verify(cloud_cert.get_pubkey())) - else: - self.assertFalse(signed_cert.verify(cloud_cert.get_pubkey())) - - def test_210_can_add_project_role(self): - project = self.users.get_project('testproj') - self.assertFalse(project.has_role('test1', 'sysadmin')) - self.users.add_role('test1', 'sysadmin') - self.assertFalse(project.has_role('test1', 'sysadmin')) - project.add_role('test1', 'sysadmin') - self.assertTrue(project.has_role('test1', 'sysadmin')) - - def test_211_can_remove_project_role(self): - project = self.users.get_project('testproj') - self.assertTrue(project.has_role('test1', 'sysadmin')) - project.remove_role('test1', 'sysadmin') - self.assertFalse(project.has_role('test1', 'sysadmin')) - self.users.remove_role('test1', 'sysadmin') - self.assertFalse(project.has_role('test1', 'sysadmin')) - - def test_212_vpn_ip_and_port_looks_valid(self): - project = self.users.get_project('testproj') - self.assert_(project.vpn_ip) - self.assert_(project.vpn_port >= FLAGS.vpn_start_port) - self.assert_(project.vpn_port <= FLAGS.vpn_end_port) - - def test_213_too_many_vpns(self): - for i in xrange(users.Vpn.num_ports_for_ip(FLAGS.vpn_ip)): - users.Vpn.create("vpnuser%s" % i) - self.assertRaises(users.NoMorePorts, users.Vpn.create, "boom") - - def test_299_can_delete_project(self): - self.users.delete_project('testproj') - self.assertFalse(filter(lambda p: p.name == 'testproj', self.users.get_projects())) - - def test_999_can_delete_users(self): - self.users.delete_user('test1') - users = self.users.get_users() - self.assertFalse(filter(lambda u: u.id == 'test1', users)) - self.users.delete_user('test2') - self.assertEqual(self.users.get_user('test2'), None) - - -if __name__ == "__main__": - # TODO: Implement use_fake as an option - unittest.main() -- cgit From bc524d362391c22ece2c2b24d11239837fe5db39 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Mon, 19 Jul 2010 17:10:25 -0500 Subject: LdapDriver cleanup: docstrings and parameter ordering --- nova/auth/ldapdriver.py | 61 +++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 56 insertions(+), 5 deletions(-) (limited to 'nova') diff --git a/nova/auth/ldapdriver.py b/nova/auth/ldapdriver.py index 49443c99a..21c87a576 100644 --- a/nova/auth/ldapdriver.py +++ b/nova/auth/ldapdriver.py @@ -67,6 +67,10 @@ flags.DEFINE_string('ldap_developer', class LdapDriver(object): + """Ldap Auth driver + + Defines enter and exit and therefore supports the with/as syntax. + """ def __enter__(self): """Creates the connection to LDAP""" if FLAGS.fake_users: @@ -86,43 +90,51 @@ class LdapDriver(object): return False def get_user(self, uid): + """Retrieve user by id""" attr = self.__find_object(self.__uid_to_dn(uid), '(objectclass=novaUser)') return self.__to_user(attr) def get_user_from_access_key(self, access): + """Retrieve user by access key""" query = '(accessKey=%s)' % access dn = FLAGS.ldap_user_subtree return self.__to_user(self.__find_object(dn, query)) def get_key_pair(self, uid, key_name): + """Retrieve key pair by uid and key name""" dn = 'cn=%s,%s' % (key_name, self.__uid_to_dn(uid)) attr = self.__find_object(dn, '(objectclass=novaKeyPair)') return self.__to_key_pair(uid, attr) def get_project(self, name): + """Retrieve project by name""" dn = 'cn=%s,%s' % (name, FLAGS.ldap_project_subtree) attr = self.__find_object(dn, '(objectclass=novaProject)') return self.__to_project(attr) def get_users(self): + """Retrieve list of users""" attrs = self.__find_objects(FLAGS.ldap_user_subtree, '(objectclass=novaUser)') return [self.__to_user(attr) for attr in attrs] def get_key_pairs(self, uid): + """Retrieve list of key pairs""" attrs = self.__find_objects(self.__uid_to_dn(uid), '(objectclass=novaKeyPair)') return [self.__to_key_pair(uid, attr) for attr in attrs] def get_projects(self): + """Retrieve list of projects""" attrs = self.__find_objects(FLAGS.ldap_project_subtree, '(objectclass=novaProject)') return [self.__to_project(attr) for attr in attrs] def create_user(self, name, access_key, secret_key, is_admin): + """Create a user""" if self.__user_exists(name): raise exception.Duplicate("LDAP user %s already exists" % name) attr = [ @@ -142,7 +154,7 @@ class LdapDriver(object): return self.__to_user(dict(attr)) def create_key_pair(self, uid, key_name, public_key, fingerprint): - """create's a public key in the directory underneath the user""" + """Create a key pair""" # TODO(vish): possibly refactor this to store keys in their own ou # and put dn reference in the user object attr = [ @@ -158,6 +170,7 @@ class LdapDriver(object): def create_project(self, name, manager_uid, description=None, member_uids=None): + """Create a project""" if self.__project_exists(name): raise exception.Duplicate("Project can't be created because " "project %s already exists" % name) @@ -189,22 +202,31 @@ class LdapDriver(object): return self.__to_project(dict(attr)) def add_to_project(self, uid, project_id): + """Add user to project""" dn = 'cn=%s,%s' % (project_id, FLAGS.ldap_project_subtree) return self.__add_to_group(uid, dn) def remove_from_project(self, uid, project_id): + """Remove user from project""" dn = 'cn=%s,%s' % (project_id, FLAGS.ldap_project_subtree) return self.__remove_from_group(uid, dn) def is_in_project(self, uid, project_id): + """Check if user is in project""" dn = 'cn=%s,%s' % (project_id, FLAGS.ldap_project_subtree) return self.__is_in_group(uid, dn) def has_role(self, uid, role, project_id=None): + """Check if user has role + + If project is specified, it checks for local role, otherwise it + checks for global role + """ role_dn = self.__role_to_dn(role, project_id) return self.__is_in_group(uid, role_dn) def add_role(self, uid, role, project_id=None): + """Add role for user (or user and project)""" role_dn = self.__role_to_dn(role, project_id) if not self.__group_exists(role_dn): # create the role if it doesn't exist @@ -214,10 +236,12 @@ class LdapDriver(object): return self.__add_to_group(uid, role_dn) def remove_role(self, uid, role, project_id=None): + """Remove role for user (or user and project)""" role_dn = self.__role_to_dn(role, project_id) return self.__remove_from_group(uid, role_dn) def delete_user(self, uid): + """Delete a user""" if not self.__user_exists(uid): raise exception.NotFound("User %s doesn't exist" % uid) self.__delete_key_pairs(uid) @@ -226,6 +250,7 @@ class LdapDriver(object): FLAGS.ldap_user_subtree)) def delete_key_pair(self, uid, key_name): + """Delete a key pair""" if not self.__key_pair_exists(uid, key_name): raise exception.NotFound("Key Pair %s doesn't exist for user %s" % (key_name, uid)) @@ -233,26 +258,33 @@ class LdapDriver(object): FLAGS.ldap_user_subtree)) def delete_project(self, name): + """Delete a project""" project_dn = 'cn=%s,%s' % (name, FLAGS.ldap_project_subtree) self.__delete_roles(project_dn) self.__delete_group(project_dn) def __user_exists(self, name): + """Check if user exists""" return self.get_user(name) != None def __key_pair_exists(self, uid, key_name): + """Check if key pair exists""" + return self.get_user(uid) != None return self.get_key_pair(uid, key_name) != None def __project_exists(self, name): + """Check if project exists""" return self.get_project(name) != None def __find_object(self, dn, query = None): + """Find an object by dn and query""" objects = self.__find_objects(dn, query) if len(objects) == 0: return None return objects[0] def __find_dns(self, dn, query=None): + """Find dns by query""" try: res = self.conn.search_s(dn, ldap.SCOPE_SUBTREE, query) except self.NO_SUCH_OBJECT: @@ -261,6 +293,7 @@ class LdapDriver(object): return [dn for dn, attributes in res] def __find_objects(self, dn, query = None): + """Find objects by query""" try: res = self.conn.search_s(dn, ldap.SCOPE_SUBTREE, query) except self.NO_SUCH_OBJECT: @@ -269,25 +302,30 @@ class LdapDriver(object): return [attributes for dn, attributes in res] def __find_role_dns(self, tree): + """Find dns of role objects in given tree""" return self.__find_dns(tree, '(&(objectclass=groupOfNames)(!(objectclass=novaProject)))') def __find_group_dns_with_member(self, tree, uid): + """Find dns of group objects in a given tree that contain member""" dns = self.__find_dns(tree, '(&(objectclass=groupOfNames)(member=%s))' % self.__uid_to_dn(uid)) return dns def __group_exists(self, dn): + """Check if group exists""" return self.__find_object(dn, '(objectclass=groupOfNames)') != None def __delete_key_pairs(self, uid): + """Delete all key pairs for user""" keys = self.get_key_pairs(uid) if keys != None: for key in keys: self.delete_key_pair(uid, key.name) def __role_to_dn(self, role, project_id=None): + """Convert role to corresponding dn""" if project_id == None: return FLAGS.__getitem__("ldap_%s" % role).value else: @@ -297,6 +335,7 @@ class LdapDriver(object): def __create_group(self, group_dn, name, uid, description, member_uids = None): + """Create a group""" if self.__group_exists(group_dn): raise exception.Duplicate("Group can't be created because " "group %s already exists" % name) @@ -319,6 +358,7 @@ class LdapDriver(object): self.conn.add_s(group_dn, attr) def __is_in_group(self, uid, group_dn): + """Check if user is in group""" if not self.__user_exists(uid): raise exception.NotFound("User %s can't be searched in group " "becuase the user doesn't exist" % (uid,)) @@ -329,6 +369,7 @@ class LdapDriver(object): return res != None def __add_to_group(self, uid, group_dn): + """Add user to group""" 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,)) @@ -344,6 +385,7 @@ class LdapDriver(object): self.conn.modify_s(group_dn, attr) def __remove_from_group(self, uid, group_dn): + """Remove user from group""" if not self.__group_exists(group_dn): raise exception.NotFound("The group at dn %s doesn't exist" % (group_dn,)) @@ -353,9 +395,10 @@ class LdapDriver(object): if not self.__is_in_group(uid, group_dn): raise exception.NotFound("User %s is not a member of the group" % (uid,)) - self.__safe_remove_from_group(group_dn, uid) + self.__safe_remove_from_group(uid, group_dn) - def __safe_remove_from_group(self, group_dn, uid): + def __safe_remove_from_group(self, uid, group_dn): + """Remove user from group, deleting group if user is last member""" # FIXME(vish): what if deleted user is a project manager? attr = [(ldap.MOD_DELETE, 'member', self.__uid_to_dn(uid))] try: @@ -366,6 +409,7 @@ class LdapDriver(object): self.__delete_group(group_dn) def __remove_from_all(self, uid): + """Remove user from all roles and projects""" if not self.__user_exists(uid): raise exception.NotFound("User %s can't be removed from all " "because the user doesn't exist" % (uid,)) @@ -373,22 +417,25 @@ class LdapDriver(object): role_dns = self.__find_group_dns_with_member( FLAGS.role_project_subtree, uid) for role_dn in role_dns: - self.__safe_remove_from_group(role_dn, uid) + self.__safe_remove_from_group(uid, role_dn) project_dns = self.__find_group_dns_with_member( FLAGS.ldap_project_subtree, uid) for project_dn in project_dns: - self.__safe_remove_from_group(project_dn, uid) + self.__safe_remove_from_group(uid, role_dn) def __delete_group(self, group_dn): + """Delete Group""" if not self.__group_exists(group_dn): raise exception.NotFound("Group at dn %s doesn't exist" % group_dn) self.conn.delete_s(group_dn) def __delete_roles(self, project_dn): + """Delete all roles for project""" for role_dn in self.__find_role_dns(project_dn): self.__delete_group(role_dn) def __to_user(self, attr): + """Convert ldap attributes to User object""" if attr == None: return None return manager.User( @@ -400,6 +447,7 @@ class LdapDriver(object): ) def __to_key_pair(self, owner, attr): + """Convert ldap attributes to KeyPair object""" if attr == None: return None return manager.KeyPair( @@ -410,6 +458,7 @@ class LdapDriver(object): ) def __to_project(self, attr): + """Convert ldap attributes to Project object""" if attr == None: return None member_dns = attr.get('member', []) @@ -421,8 +470,10 @@ class LdapDriver(object): ) def __dn_to_uid(self, dn): + """Convert user dn to uid""" return dn.split(',')[0].split('=')[1] def __uid_to_dn(self, dn): + """Convert uid to dn""" return 'uid=%s,%s' % (dn, FLAGS.ldap_user_subtree) -- cgit From 57ff625ec300bcc10b701b57aa75f989fbaf1679 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Mon, 19 Jul 2010 20:20:41 -0500 Subject: More docstrings, don't autocreate projects --- nova/auth/ldapdriver.py | 8 +++-- nova/auth/manager.py | 96 ++++++++++++++++++++++++------------------------- 2 files changed, 52 insertions(+), 52 deletions(-) (limited to 'nova') diff --git a/nova/auth/ldapdriver.py b/nova/auth/ldapdriver.py index 21c87a576..89c4defda 100644 --- a/nova/auth/ldapdriver.py +++ b/nova/auth/ldapdriver.py @@ -108,9 +108,9 @@ class LdapDriver(object): attr = self.__find_object(dn, '(objectclass=novaKeyPair)') return self.__to_key_pair(uid, attr) - def get_project(self, name): - """Retrieve project by name""" - dn = 'cn=%s,%s' % (name, + def get_project(self, pid): + """Retrieve project by id""" + dn = 'cn=%s,%s' % (pid, FLAGS.ldap_project_subtree) attr = self.__find_object(dn, '(objectclass=novaProject)') return self.__to_project(attr) @@ -452,6 +452,7 @@ class LdapDriver(object): return None return manager.KeyPair( id = attr['cn'][0], + name = attr['cn'][0], owner_id = owner, public_key = attr['sshPublicKey'][0], fingerprint = attr['keyFingerprint'][0], @@ -464,6 +465,7 @@ class LdapDriver(object): member_dns = attr.get('member', []) return manager.Project( id = attr['cn'][0], + name = attr['cn'][0], project_manager_id = self.__dn_to_uid(attr['projectManager'][0]), description = attr.get('description', [None])[0], member_ids = [self.__dn_to_uid(x) for x in member_dns] diff --git a/nova/auth/manager.py b/nova/auth/manager.py index 0b5039684..87cfd9a91 100644 --- a/nova/auth/manager.py +++ b/nova/auth/manager.py @@ -159,26 +159,27 @@ class KeyPair(AuthBase): Even though this object is named KeyPair, only the public key and fingerprint is stored. The user's private key is not saved. """ - def __init__(self, id, owner_id, public_key, fingerprint): + def __init__(self, id, name, owner_id, public_key, fingerprint): self.id = id - self.name = id + self.name = name self.owner_id = owner_id self.public_key = public_key self.fingerprint = fingerprint 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', '%s')" % (self.id, + self.name, + self.owner_id, + self.public_key, + self.fingerprint) class Project(AuthBase): """Represents a Project returned from the datastore""" - def __init__(self, id, project_manager_id, description, member_ids): - self.project_manager_id = project_manager_id + def __init__(self, id, name, project_manager_id, description, member_ids): self.id = id - self.name = id + self.name = name + self.project_manager_id = project_manager_id self.description = description self.member_ids = member_ids @@ -205,10 +206,11 @@ class Project(AuthBase): return AuthManager().get_credentials(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', %s)" % (self.id, + self.name, + self.project_manager_id, + self.description, + self.member_ids) class NoMorePorts(exception.Error): @@ -223,10 +225,16 @@ class Vpn(datastore.BasicModel): @property def identifier(self): + """Identifier used for key in redis""" return self.project_id @classmethod def create(cls, project_id): + """Creates a vpn for project + + This method finds a free ip and port and stores the associated + values in the datastore. + """ # TODO(vish): get list of vpn ips from redis port = cls.find_free_port_for_ip(FLAGS.vpn_ip) vpn = cls(project_id) @@ -239,6 +247,7 @@ class Vpn(datastore.BasicModel): @classmethod def find_free_port_for_ip(cls, ip): + """Finds a free port for a given ip from the redis set""" # TODO(vish): these redis commands should be generalized and # placed into a base class. Conceptually, it is # similar to an association, but we are just @@ -260,21 +269,26 @@ class Vpn(datastore.BasicModel): @classmethod def num_ports_for_ip(cls, ip): + """Calculates the number of free ports for a given ip""" return datastore.Redis.instance().scard('ip:%s:ports' % ip) @property def ip(self): + """The ip assigned to the project""" return self['ip'] @property def port(self): + """The port assigned to the project""" return int(self['port']) def save(self): + """Saves the association to the given ip""" self.associate_with('ip', self.ip) super(Vpn, self).save() def destroy(self): + """Cleans up datastore and adds port back to pool""" self.unassociate_with('ip', self.ip) datastore.Redis.instance().sadd('ip:%s:ports' % self.ip, self.port) super(Vpn, self).destroy() @@ -345,19 +359,22 @@ class AuthManager(object): @return: User and project that the request represents. """ # TODO(vish): check for valid timestamp - (access_key, sep, project_name) = access.partition(':') + (access_key, sep, project_id) = access.partition(':') user = self.get_user_from_access_key(access_key) if user == None: raise exception.NotFound('No user found for access key %s' % access_key) - if project_name is '': - project_name = user.name - project = self.get_project(project_name) + # NOTE(vish): if we stop using project name as id we need better + # logic to find a default project for user + if project_id is '': + project_id = user.name + + project = self.get_project(project_id) if project == None: raise exception.NotFound('No project called %s could be found' % - project_name) + project_id) if not self.is_admin(user) and not self.is_project_member(user, project): raise exception.NotFound('User %s is not a member of project %s' % @@ -521,9 +538,9 @@ class AuthManager(object): Vpn.create(name) with self.driver_class() as drv: return drv.create_project(name, - User.safe_id(manager_user), - description, - member_users) + User.safe_id(manager_user), + description, + member_users) def get_projects(self): """Retrieves list of all projects""" @@ -531,10 +548,10 @@ class AuthManager(object): return drv.get_projects() - def get_project(self, project): + def get_project(self, pid): """Get project object by id""" with self.driver_class() as drv: - return drv.get_project(Project.safe_id(project)) + return drv.get_project(pid) def add_to_project(self, user, project): """Add user to project""" @@ -580,13 +597,11 @@ class AuthManager(object): with self.driver_class() as drv: return drv.get_users() - def create_user(self, user, access=None, secret=None, - admin=False, create_project=True): + def create_user(self, name, access=None, secret=None, admin=False): """Creates a user - @type user: str - @param name: Name of the user to create. The name will also be - used as the user id. + @type name: str + @param name: Name of the user to create. @type access: str @param access: Access Key (defaults to a random uuid) @@ -607,29 +622,12 @@ class AuthManager(object): if access == None: access = str(uuid.uuid4()) if secret == None: secret = str(uuid.uuid4()) with self.driver_class() as drv: - user = User.safe_id(user) - result = drv.create_user(user, access, secret, admin) - if create_project: - # NOTE(vish): if the project creation fails, we delete - # the user and return an exception - try: - drv.create_project(user, user, user) - except Exception: - with self.driver_class() as drv: - drv.delete_user(user) - raise - return result - - def delete_user(self, user, delete_project=True): + return drv.create_user(name, access, secret, admin) + + def delete_user(self, user): """Deletes a user""" with self.driver_class() as drv: - user = User.safe_id(user) - if delete_project: - try: - drv.delete_project(user) - except exception.NotFound: - pass - drv.delete_user(user) + drv.delete_user(User.safe_id(user)) def generate_key_pair(self, user, key_name): """Generates a key pair for a user -- cgit From 0f9be756f44e831545bf5c31606e0419b61d6ddd Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Tue, 20 Jul 2010 09:15:36 -0500 Subject: Test cleanup, make driver return dictionaries and construct objects in manager --- nova/auth/ldapdriver.py | 45 +++++++++--------- nova/auth/manager.py | 105 +++++++++++++++++++++++++---------------- nova/tests/api_unittest.py | 23 ++++----- nova/tests/auth_unittest.py | 95 +++++++++++++++++++------------------ nova/tests/network_unittest.py | 79 ++++++++++++++++--------------- 5 files changed, 187 insertions(+), 160 deletions(-) (limited to 'nova') diff --git a/nova/auth/ldapdriver.py b/nova/auth/ldapdriver.py index 89c4defda..d330ae729 100644 --- a/nova/auth/ldapdriver.py +++ b/nova/auth/ldapdriver.py @@ -28,7 +28,6 @@ import logging from nova import exception from nova import flags -from nova.auth import manager try: import ldap @@ -322,7 +321,7 @@ class LdapDriver(object): keys = self.get_key_pairs(uid) if keys != None: for key in keys: - self.delete_key_pair(uid, key.name) + self.delete_key_pair(uid, key['name']) def __role_to_dn(self, role, project_id=None): """Convert role to corresponding dn""" @@ -438,38 +437,38 @@ class LdapDriver(object): """Convert ldap attributes to User object""" if attr == None: return None - return manager.User( - id = attr['uid'][0], - name = attr['cn'][0], - access = attr['accessKey'][0], - secret = attr['secretKey'][0], - admin = (attr['isAdmin'][0] == 'TRUE') - ) + return { + 'id': attr['uid'][0], + 'name': attr['cn'][0], + 'access': attr['accessKey'][0], + 'secret': attr['secretKey'][0], + 'admin': (attr['isAdmin'][0] == 'TRUE') + } def __to_key_pair(self, owner, attr): """Convert ldap attributes to KeyPair object""" if attr == None: return None - return manager.KeyPair( - id = attr['cn'][0], - name = attr['cn'][0], - owner_id = owner, - public_key = attr['sshPublicKey'][0], - fingerprint = attr['keyFingerprint'][0], - ) + return { + 'id': attr['cn'][0], + 'name': attr['cn'][0], + 'owner_id': owner, + 'public_key': attr['sshPublicKey'][0], + 'fingerprint': attr['keyFingerprint'][0], + } def __to_project(self, attr): """Convert ldap attributes to Project object""" if attr == None: return None member_dns = attr.get('member', []) - return manager.Project( - id = attr['cn'][0], - name = attr['cn'][0], - project_manager_id = self.__dn_to_uid(attr['projectManager'][0]), - description = attr.get('description', [None])[0], - member_ids = [self.__dn_to_uid(x) for x in member_dns] - ) + return { + 'id': attr['cn'][0], + 'name': attr['cn'][0], + 'project_manager_id': self.__dn_to_uid(attr['projectManager'][0]), + 'description': attr.get('description', [None])[0], + 'member_ids': [self.__dn_to_uid(x) for x in member_dns] + } def __dn_to_uid(self, dn): """Convert user dn to uid""" diff --git a/nova/auth/manager.py b/nova/auth/manager.py index 87cfd9a91..2facffe51 100644 --- a/nova/auth/manager.py +++ b/nova/auth/manager.py @@ -33,9 +33,9 @@ from nova import datastore from nova import exception from nova import flags from nova import objectstore # for flags -from nova import signer from nova import utils from nova.auth import ldapdriver +from nova.auth import signer FLAGS = flags.FLAGS # NOTE(vish): a user with one of these roles will be a superuser and @@ -187,6 +187,14 @@ class Project(AuthBase): def project_manager(self): return AuthManager().get_user(self.project_manager_id) + @property + def vpn_ip(self): + return AuthManager().get_project_vpn_ip(self) + + @property + def vpn_port(self): + return AuthManager().get_project_vpn_port(self) + def has_manager(self, user): return AuthManager().is_project_manager(user, self) @@ -314,16 +322,6 @@ class AuthManager(object): def __init__(self, *args, **kwargs): self.driver_class = kwargs.get('driver_class', ldapdriver.LdapDriver) - if FLAGS.fake_tests: - try: - self.create_user('fake', 'fake', 'fake') - except: pass - try: - self.create_user('user', 'user', 'user') - except: pass - try: - self.create_user('admin', 'admin', 'admin', True) - except: pass def authenticate(self, access, signature, params, verb='GET', server_string='127.0.0.1:8773', path='/', @@ -508,6 +506,21 @@ class AuthManager(object): with self.driver_class() as drv: drv.remove_role(User.safe_id(user), role, Project.safe_id(project)) + def get_project(self, pid): + """Get project object by id""" + with self.driver_class() as drv: + project_dict = drv.get_project(pid) + if project_dict: + return Project(**project_dict) + + def get_projects(self): + """Retrieves list of all projects""" + with self.driver_class() as drv: + project_list = drv.get_projects() + if not project_list: + return [] + return [Project(**project_dict) for project_dict in project_list] + def create_project(self, name, manager_user, description=None, member_users=None): """Create a project @@ -532,26 +545,14 @@ class AuthManager(object): """ if member_users: member_users = [User.safe_id(u) for u in member_users] - # NOTE(vish): try to associate a vpn ip and port first because - # if it throws an exception, we save having to - # create and destroy a project - Vpn.create(name) with self.driver_class() as drv: - return drv.create_project(name, - User.safe_id(manager_user), - description, - member_users) - - def get_projects(self): - """Retrieves list of all projects""" - with self.driver_class() as drv: - return drv.get_projects() - - - def get_project(self, pid): - """Get project object by id""" - with self.driver_class() as drv: - return drv.get_project(pid) + project_dict = drv.create_project(name, + User.safe_id(manager_user), + description, + member_users) + if project_dict: + Vpn.create(project_dict['id']) + return Project(**project_dict) def add_to_project(self, user, project): """Add user to project""" @@ -577,6 +578,12 @@ class AuthManager(object): return drv.remove_from_project(User.safe_id(user), Project.safe_id(project)) + def get_project_vpn_ip(self, project): + return Vpn(Project.safe_id(project)).ip + + def get_project_vpn_port(self, project): + return Vpn(Project.safe_id(project)).port + def delete_project(self, project): """Deletes a project""" with self.driver_class() as drv: @@ -585,17 +592,24 @@ class AuthManager(object): def get_user(self, uid): """Retrieves a user by id""" with self.driver_class() as drv: - return drv.get_user(uid) + user_dict = drv.get_user(uid) + if user_dict: + return User(**user_dict) def get_user_from_access_key(self, access_key): """Retrieves a user by access key""" with self.driver_class() as drv: - return drv.get_user_from_access_key(access_key) + user_dict = drv.get_user_from_access_key(access_key) + if user_dict: + return User(**user_dict) def get_users(self): """Retrieves a list of all users""" with self.driver_class() as drv: - return drv.get_users() + user_list = drv.get_users() + if not user_list: + return [] + return [User(**user_dict) for user_dict in user_list] def create_user(self, name, access=None, secret=None, admin=False): """Creates a user @@ -622,7 +636,9 @@ class AuthManager(object): if access == None: access = str(uuid.uuid4()) if secret == None: secret = str(uuid.uuid4()) with self.driver_class() as drv: - return drv.create_user(name, access, secret, admin) + user_dict = drv.create_user(name, access, secret, admin) + if user_dict: + return User(**user_dict) def delete_user(self, user): """Deletes a user""" @@ -660,18 +676,27 @@ class AuthManager(object): def create_key_pair(self, user, key_name, public_key, fingerprint): """Creates a key pair for user""" with self.driver_class() as drv: - return drv.create_key_pair(User.safe_id(user), key_name, - public_key, fingerprint) + kp_dict = drv.create_key_pair(User.safe_id(user), + key_name, + public_key, + fingerprint) + if kp_dict: + return KeyPair(**kp_dict) def get_key_pair(self, user, key_name): """Retrieves a key pair for user""" with self.driver_class() as drv: - return drv.get_key_pair(User.safe_id(user), key_name) + kp_dict = drv.get_key_pair(User.safe_id(user), key_name) + if kp_dict: + return KeyPair(**kp_dict) def get_key_pairs(self, user): """Retrieves all key pairs for user""" with self.driver_class() as drv: - return drv.get_key_pairs(User.safe_id(user)) + kp_list = drv.get_key_pairs(User.safe_id(user)) + if not kp_list: + return [] + return [KeyPair(**kp_dict) for kp_dict in kp_list] def delete_key_pair(self, user, key_name): """Deletes a key pair for user""" @@ -686,7 +711,7 @@ class AuthManager(object): project = user.id pid = Project.safe_id(project) rc = self.__generate_rc(user.access, user.secret, pid) - private_key, signed_cert = self.__generate_x509_cert(user.id, pid) + private_key, signed_cert = self._generate_x509_cert(user.id, pid) vpn = Vpn(pid) configfile = open(FLAGS.vpn_client_template,"r") @@ -726,7 +751,7 @@ class AuthManager(object): } return rc - def __generate_x509_cert(self, uid, pid): + def _generate_x509_cert(self, uid, pid): """Generate x509 cert for user""" (private_key, csr) = crypto.generate_x509_cert( self.__cert_subject(uid)) diff --git a/nova/tests/api_unittest.py b/nova/tests/api_unittest.py index 5c26192bd..4477a1fe6 100644 --- a/nova/tests/api_unittest.py +++ b/nova/tests/api_unittest.py @@ -150,7 +150,7 @@ class ApiEc2TestCase(test.BaseTestCase): def setUp(self): super(ApiEc2TestCase, self).setUp() - self.users = manager.AuthManager() + self.manager = manager.AuthManager() self.cloud = cloud.CloudController() self.host = '127.0.0.1' @@ -175,25 +175,22 @@ class ApiEc2TestCase(test.BaseTestCase): def test_describe_instances(self): self.expect_http() self.mox.ReplayAll() - try: - self.users.create_user('fake', 'fake', 'fake') - except Exception, _err: - pass # User may already exist + user = self.manager.create_user('fake', 'fake', 'fake') + project = self.manager.create_project('fake', 'fake', 'fake') self.assertEqual(self.ec2.get_all_instances(), []) - self.users.delete_user('fake') + self.manager.delete_project(project) + self.manager.delete_user(user) def test_get_all_key_pairs(self): self.expect_http() self.mox.ReplayAll() keyname = "".join(random.choice("sdiuisudfsdcnpaqwertasd") for x in range(random.randint(4, 8))) - try: - self.users.create_user('fake', 'fake', 'fake') - except Exception, _err: - pass # User may already exist - self.users.generate_key_pair('fake', keyname) + user = self.manager.create_user('fake', 'fake', 'fake') + project = self.manager.create_project('fake', 'fake', 'fake') + self.manager.generate_key_pair(user.id, keyname) rv = self.ec2.get_all_key_pairs() self.assertTrue(filter(lambda k: k.name == keyname, rv)) - self.users.delete_user('fake') - + self.manager.delete_project(project) + self.manager.delete_user(user) diff --git a/nova/tests/auth_unittest.py b/nova/tests/auth_unittest.py index 000f6bf17..0cd377b70 100644 --- a/nova/tests/auth_unittest.py +++ b/nova/tests/auth_unittest.py @@ -37,29 +37,29 @@ class AuthTestCase(test.BaseTestCase): super(AuthTestCase, self).setUp() self.flags(fake_libvirt=True, fake_storage=True) - self.users = manager.AuthManager() + self.manager = manager.AuthManager() def test_001_can_create_users(self): - self.users.create_user('test1', 'access', 'secret') - self.users.create_user('test2') + self.manager.create_user('test1', 'access', 'secret') + self.manager.create_user('test2') def test_002_can_get_user(self): - user = self.users.get_user('test1') + user = self.manager.get_user('test1') def test_003_can_retreive_properties(self): - user = self.users.get_user('test1') + user = self.manager.get_user('test1') self.assertEqual('test1', user.id) self.assertEqual('access', user.access) self.assertEqual('secret', user.secret) def test_004_signature_is_valid(self): - #self.assertTrue(self.users.authenticate( **boto.generate_url ... ? ? ? )) + #self.assertTrue(self.manager.authenticate( **boto.generate_url ... ? ? ? )) pass #raise NotImplementedError def test_005_can_get_credentials(self): return - credentials = self.users.get_user('test1').get_credentials() + credentials = self.manager.get_user('test1').get_credentials() self.assertEqual(credentials, 'export EC2_ACCESS_KEY="access"\n' + 'export EC2_SECRET_KEY="secret"\n' + @@ -68,14 +68,14 @@ class AuthTestCase(test.BaseTestCase): 'export EC2_USER_ID="test1"\n') def test_006_test_key_storage(self): - user = self.users.get_user('test1') + user = self.manager.get_user('test1') user.create_key_pair('public', 'key', 'fingerprint') key = user.get_key_pair('public') self.assertEqual('key', key.public_key) self.assertEqual('fingerprint', key.fingerprint) def test_007_test_key_generation(self): - user = self.users.get_user('test1') + user = self.manager.get_user('test1') private_key, fingerprint = user.generate_key_pair('public2') key = RSA.load_key_string(private_key, callback=lambda: None) bio = BIO.MemoryBuffer() @@ -87,71 +87,71 @@ class AuthTestCase(test.BaseTestCase): converted.split(" ")[1].strip()) def test_008_can_list_key_pairs(self): - keys = self.users.get_user('test1').get_key_pairs() + keys = self.manager.get_user('test1').get_key_pairs() self.assertTrue(filter(lambda k: k.name == 'public', keys)) self.assertTrue(filter(lambda k: k.name == 'public2', keys)) def test_009_can_delete_key_pair(self): - self.users.get_user('test1').delete_key_pair('public') - keys = self.users.get_user('test1').get_key_pairs() + self.manager.get_user('test1').delete_key_pair('public') + keys = self.manager.get_user('test1').get_key_pairs() self.assertFalse(filter(lambda k: k.name == 'public', keys)) def test_010_can_list_users(self): - users = self.users.get_users() + users = self.manager.get_users() logging.warn(users) self.assertTrue(filter(lambda u: u.id == 'test1', users)) def test_101_can_add_user_role(self): - self.assertFalse(self.users.has_role('test1', 'itsec')) - self.users.add_role('test1', 'itsec') - self.assertTrue(self.users.has_role('test1', 'itsec')) + self.assertFalse(self.manager.has_role('test1', 'itsec')) + self.manager.add_role('test1', 'itsec') + self.assertTrue(self.manager.has_role('test1', 'itsec')) def test_199_can_remove_user_role(self): - self.assertTrue(self.users.has_role('test1', 'itsec')) - self.users.remove_role('test1', 'itsec') - self.assertFalse(self.users.has_role('test1', 'itsec')) + self.assertTrue(self.manager.has_role('test1', 'itsec')) + self.manager.remove_role('test1', 'itsec') + self.assertFalse(self.manager.has_role('test1', 'itsec')) def test_201_can_create_project(self): - project = self.users.create_project('testproj', 'test1', 'A test project', ['test1']) - self.assertTrue(filter(lambda p: p.name == 'testproj', self.users.get_projects())) + project = self.manager.create_project('testproj', 'test1', 'A test project', ['test1']) + self.assertTrue(filter(lambda p: p.name == 'testproj', self.manager.get_projects())) self.assertEqual(project.name, 'testproj') self.assertEqual(project.description, 'A test project') self.assertEqual(project.project_manager_id, 'test1') self.assertTrue(project.has_member('test1')) def test_202_user1_is_project_member(self): - self.assertTrue(self.users.get_user('test1').is_project_member('testproj')) + self.assertTrue(self.manager.get_user('test1').is_project_member('testproj')) def test_203_user2_is_not_project_member(self): - self.assertFalse(self.users.get_user('test2').is_project_member('testproj')) + self.assertFalse(self.manager.get_user('test2').is_project_member('testproj')) def test_204_user1_is_project_manager(self): - self.assertTrue(self.users.get_user('test1').is_project_manager('testproj')) + self.assertTrue(self.manager.get_user('test1').is_project_manager('testproj')) def test_205_user2_is_not_project_manager(self): - self.assertFalse(self.users.get_user('test2').is_project_manager('testproj')) + self.assertFalse(self.manager.get_user('test2').is_project_manager('testproj')) def test_206_can_add_user_to_project(self): - self.users.add_to_project('test2', 'testproj') - self.assertTrue(self.users.get_project('testproj').has_member('test2')) + self.manager.add_to_project('test2', 'testproj') + self.assertTrue(self.manager.get_project('testproj').has_member('test2')) def test_208_can_remove_user_from_project(self): - self.users.remove_from_project('test2', 'testproj') - self.assertFalse(self.users.get_project('testproj').has_member('test2')) + self.manager.remove_from_project('test2', 'testproj') + self.assertFalse(self.manager.get_project('testproj').has_member('test2')) def test_209_can_generate_x509(self): # MUST HAVE RUN CLOUD SETUP BY NOW self.cloud = cloud.CloudController() self.cloud.setup() - private_key, signed_cert_string = self.users.get_project('testproj').generate_x509_cert('test1') - logging.debug(signed_cert_string) + _key, cert_str = self.manager._generate_x509_cert('test1', 'testproj') + logging.debug(cert_str) # Need to verify that it's signed by the right intermediate CA full_chain = crypto.fetch_ca(project_id='testproj', chain=True) int_cert = crypto.fetch_ca(project_id='testproj', chain=False) cloud_cert = crypto.fetch_ca() logging.debug("CA chain:\n\n =====\n%s\n\n=====" % full_chain) - signed_cert = X509.load_cert_string(signed_cert_string) + signed_cert = X509.load_cert_string(cert_str) chain_cert = X509.load_cert_string(full_chain) int_cert = X509.load_cert_string(int_cert) cloud_cert = X509.load_cert_string(cloud_cert) @@ -164,42 +164,45 @@ class AuthTestCase(test.BaseTestCase): self.assertFalse(signed_cert.verify(cloud_cert.get_pubkey())) def test_210_can_add_project_role(self): - project = self.users.get_project('testproj') + project = self.manager.get_project('testproj') self.assertFalse(project.has_role('test1', 'sysadmin')) - self.users.add_role('test1', 'sysadmin') + self.manager.add_role('test1', 'sysadmin') self.assertFalse(project.has_role('test1', 'sysadmin')) project.add_role('test1', 'sysadmin') self.assertTrue(project.has_role('test1', 'sysadmin')) def test_211_can_remove_project_role(self): - project = self.users.get_project('testproj') + project = self.manager.get_project('testproj') self.assertTrue(project.has_role('test1', 'sysadmin')) project.remove_role('test1', 'sysadmin') self.assertFalse(project.has_role('test1', 'sysadmin')) - self.users.remove_role('test1', 'sysadmin') + self.manager.remove_role('test1', 'sysadmin') self.assertFalse(project.has_role('test1', 'sysadmin')) def test_212_vpn_ip_and_port_looks_valid(self): - project = self.users.get_project('testproj') + project = self.manager.get_project('testproj') self.assert_(project.vpn_ip) self.assert_(project.vpn_port >= FLAGS.vpn_start_port) self.assert_(project.vpn_port <= FLAGS.vpn_end_port) def test_213_too_many_vpns(self): - for i in xrange(users.Vpn.num_ports_for_ip(FLAGS.vpn_ip)): - users.Vpn.create("vpnuser%s" % i) - self.assertRaises(users.NoMorePorts, users.Vpn.create, "boom") + vpns = [] + for i in xrange(manager.Vpn.num_ports_for_ip(FLAGS.vpn_ip)): + vpns.append(manager.Vpn.create("vpnuser%s" % i)) + self.assertRaises(manager.NoMorePorts, manager.Vpn.create, "boom") + for vpn in vpns: + vpn.destroy() def test_299_can_delete_project(self): - self.users.delete_project('testproj') - self.assertFalse(filter(lambda p: p.name == 'testproj', self.users.get_projects())) + self.manager.delete_project('testproj') + self.assertFalse(filter(lambda p: p.name == 'testproj', self.manager.get_projects())) def test_999_can_delete_users(self): - self.users.delete_user('test1') - users = self.users.get_users() + self.manager.delete_user('test1') + users = self.manager.get_users() self.assertFalse(filter(lambda u: u.id == 'test1', users)) - self.users.delete_user('test2') - self.assertEqual(self.users.get_user('test2'), None) + self.manager.delete_user('test2') + self.assertEqual(self.manager.get_user('test2'), None) if __name__ == "__main__": diff --git a/nova/tests/network_unittest.py b/nova/tests/network_unittest.py index 68cd488be..0e1b55065 100644 --- a/nova/tests/network_unittest.py +++ b/nova/tests/network_unittest.py @@ -39,59 +39,61 @@ class NetworkTestCase(test.TrialTestCase): logging.getLogger().setLevel(logging.DEBUG) self.manager = manager.AuthManager() self.dnsmasq = FakeDNSMasq() - try: - self.manager.create_user('netuser', 'netuser', 'netuser') - except: pass + self.user = self.manager.create_user('netuser', 'netuser', 'netuser') + self.projects = [] + self.projects.append(self.manager.create_project('netuser', + 'netuser', + 'netuser')) for i in range(0, 6): name = 'project%s' % i - if not self.manager.get_project(name): - self.manager.create_project(name, 'netuser', name) + self.projects.append(self.manager.create_project(name, + 'netuser', + name)) self.network = network.PublicNetworkController() def tearDown(self): super(NetworkTestCase, self).tearDown() - for i in range(0, 6): - name = 'project%s' % i - self.manager.delete_project(name) - self.manager.delete_user('netuser') + for project in self.projects: + self.manager.delete_project(project) + self.manager.delete_user(self.user) def test_public_network_allocation(self): pubnet = IPy.IP(flags.FLAGS.public_range) - address = self.network.allocate_ip("netuser", "project0", "public") + address = self.network.allocate_ip(self.user.id, self.projects[0].id, "public") self.assertTrue(IPy.IP(address) in pubnet) self.assertTrue(IPy.IP(address) in self.network.network) def test_allocate_deallocate_ip(self): address = network.allocate_ip( - "netuser", "project0", utils.generate_mac()) + self.user.id, self.projects[0].id, utils.generate_mac()) logging.debug("Was allocated %s" % (address)) - net = network.get_project_network("project0", "default") - self.assertEqual(True, is_in_project(address, "project0")) + net = network.get_project_network(self.projects[0].id, "default") + self.assertEqual(True, is_in_project(address, self.projects[0].id)) mac = utils.generate_mac() hostname = "test-host" self.dnsmasq.issue_ip(mac, address, hostname, net.bridge_name) rv = network.deallocate_ip(address) # Doesn't go away until it's dhcp released - self.assertEqual(True, is_in_project(address, "project0")) + self.assertEqual(True, is_in_project(address, self.projects[0].id)) self.dnsmasq.release_ip(mac, address, hostname, net.bridge_name) - self.assertEqual(False, is_in_project(address, "project0")) + self.assertEqual(False, is_in_project(address, self.projects[0].id)) def test_range_allocation(self): mac = utils.generate_mac() secondmac = utils.generate_mac() hostname = "test-host" address = network.allocate_ip( - "netuser", "project0", mac) + self.user.id, self.projects[0].id, mac) secondaddress = network.allocate_ip( - "netuser", "project1", secondmac) - net = network.get_project_network("project0", "default") - secondnet = network.get_project_network("project1", "default") + self.user, "project1", secondmac) + net = network.get_project_network(self.projects[0].id, "default") + secondnet = network.get_project_network(self.projects[1].id, "default") - self.assertEqual(True, is_in_project(address, "project0")) - self.assertEqual(True, is_in_project(secondaddress, "project1")) - self.assertEqual(False, is_in_project(address, "project1")) + self.assertEqual(True, is_in_project(address, self.projects[0].id)) + self.assertEqual(True, is_in_project(secondaddress, self.projects[1].id)) + self.assertEqual(False, is_in_project(address, self.projects[1].id)) # Addresses are allocated before they're issued self.dnsmasq.issue_ip(mac, address, hostname, net.bridge_name) @@ -100,34 +102,34 @@ class NetworkTestCase(test.TrialTestCase): rv = network.deallocate_ip(address) self.dnsmasq.release_ip(mac, address, hostname, net.bridge_name) - self.assertEqual(False, is_in_project(address, "project0")) + self.assertEqual(False, is_in_project(address, self.projects[0].id)) # First address release shouldn't affect the second - self.assertEqual(True, is_in_project(secondaddress, "project1")) + self.assertEqual(True, is_in_project(secondaddress, self.projects[1].id)) rv = network.deallocate_ip(secondaddress) self.dnsmasq.release_ip(secondmac, secondaddress, hostname, secondnet.bridge_name) - self.assertEqual(False, is_in_project(secondaddress, "project1")) + self.assertEqual(False, is_in_project(secondaddress, self.projects[1].id)) def test_subnet_edge(self): - secondaddress = network.allocate_ip("netuser", "project0", + secondaddress = network.allocate_ip(self.user.id, self.projects[0].id, utils.generate_mac()) hostname = "toomany-hosts" - for project in range(1,5): - project_id = "project%s" % (project) + for i in range(1,5): + project_id = self.projects[i].id mac = utils.generate_mac() mac2 = utils.generate_mac() mac3 = utils.generate_mac() address = network.allocate_ip( - "netuser", project_id, mac) + self.user, project_id, mac) address2 = network.allocate_ip( - "netuser", project_id, mac2) + self.user, project_id, mac2) address3 = network.allocate_ip( - "netuser", project_id, mac3) - self.assertEqual(False, is_in_project(address, "project0")) - self.assertEqual(False, is_in_project(address2, "project0")) - self.assertEqual(False, is_in_project(address3, "project0")) + self.user, project_id, mac3) + self.assertEqual(False, is_in_project(address, self.projects[0].id)) + self.assertEqual(False, is_in_project(address2, self.projects[0].id)) + self.assertEqual(False, is_in_project(address3, self.projects[0].id)) rv = network.deallocate_ip(address) rv = network.deallocate_ip(address2) rv = network.deallocate_ip(address3) @@ -135,7 +137,7 @@ class NetworkTestCase(test.TrialTestCase): self.dnsmasq.release_ip(mac, address, hostname, net.bridge_name) self.dnsmasq.release_ip(mac2, address2, hostname, net.bridge_name) self.dnsmasq.release_ip(mac3, address3, hostname, net.bridge_name) - net = network.get_project_network("project0", "default") + net = network.get_project_network(self.projects[0].id, "default") rv = network.deallocate_ip(secondaddress) self.dnsmasq.release_ip(mac, address, hostname, net.bridge_name) @@ -150,22 +152,23 @@ class NetworkTestCase(test.TrialTestCase): Network size is 32, there are 5 addresses reserved for VPN. So we should get 23 usable addresses """ - net = network.get_project_network("project0", "default") + net = network.get_project_network(self.projects[0].id, "default") hostname = "toomany-hosts" macs = {} addresses = {} for i in range(0, 22): macs[i] = utils.generate_mac() - addresses[i] = network.allocate_ip("netuser", "project0", macs[i]) + addresses[i] = network.allocate_ip(self.user.id, self.projects[0].id, macs[i]) self.dnsmasq.issue_ip(macs[i], addresses[i], hostname, net.bridge_name) - self.assertRaises(NoMoreAddresses, network.allocate_ip, "netuser", "project0", utils.generate_mac()) + self.assertRaises(NoMoreAddresses, network.allocate_ip, self.user.id, self.projects[0].id, utils.generate_mac()) for i in range(0, 22): rv = network.deallocate_ip(addresses[i]) self.dnsmasq.release_ip(macs[i], addresses[i], hostname, net.bridge_name) def is_in_project(address, project_id): + print address, list(network.get_project_network(project_id).list_addresses()) return address in network.get_project_network(project_id).list_addresses() def _get_project_addresses(project_id): -- cgit From 79b5ab9a9e18fdee3d65311b6ff16cc39d7d2513 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Tue, 20 Jul 2010 09:22:53 -0500 Subject: network unittest clean up --- nova/tests/network_unittest.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'nova') diff --git a/nova/tests/network_unittest.py b/nova/tests/network_unittest.py index 0e1b55065..237750d7f 100644 --- a/nova/tests/network_unittest.py +++ b/nova/tests/network_unittest.py @@ -87,7 +87,7 @@ class NetworkTestCase(test.TrialTestCase): address = network.allocate_ip( self.user.id, self.projects[0].id, mac) secondaddress = network.allocate_ip( - self.user, "project1", secondmac) + self.user, self.projects[1].id, secondmac) net = network.get_project_network(self.projects[0].id, "default") secondnet = network.get_project_network(self.projects[1].id, "default") @@ -168,7 +168,6 @@ class NetworkTestCase(test.TrialTestCase): self.dnsmasq.release_ip(macs[i], addresses[i], hostname, net.bridge_name) def is_in_project(address, project_id): - print address, list(network.get_project_network(project_id).list_addresses()) return address in network.get_project_network(project_id).list_addresses() def _get_project_addresses(project_id): -- cgit From cb702cb1a88ec94577c5871ab0402471dac0ec7c Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Tue, 20 Jul 2010 14:09:53 -0500 Subject: Cleanup per suggestions Move ugly import statement to avoid try except Vpn ip and port returns none if vpn isn't allocated get_credentials returns exception if vpn isn't allocated Flag for using vpns --- nova/auth/ldapdriver.py | 32 +++++++++++--------------------- nova/auth/manager.py | 37 +++++++++++++++++++++++++++---------- 2 files changed, 38 insertions(+), 31 deletions(-) (limited to 'nova') diff --git a/nova/auth/ldapdriver.py b/nova/auth/ldapdriver.py index d330ae729..4ba09517c 100644 --- a/nova/auth/ldapdriver.py +++ b/nova/auth/ldapdriver.py @@ -29,14 +29,6 @@ import logging from nova import exception from nova import flags -try: - import ldap -except Exception, e: - from nova.auth import fakeldap as ldap -# NOTE(vish): this import is so we can use fakeldap even when real ldap -# is installed. -from nova.auth import fakeldap - FLAGS = flags.FLAGS flags.DEFINE_string('ldap_url', 'ldap://localhost', 'Point this at your ldap server') @@ -73,13 +65,11 @@ class LdapDriver(object): def __enter__(self): """Creates the connection to LDAP""" 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) + from nova.auth import fakeldap as ldap else: - self.NO_SUCH_OBJECT = ldap.NO_SUCH_OBJECT - self.OBJECT_CLASS_VIOLATION = ldap.OBJECT_CLASS_VIOLATION - self.conn = ldap.initialize(FLAGS.ldap_url) + import ldap + self.ldap = ldap + self.conn = self.ldap.initialize(FLAGS.ldap_url) self.conn.simple_bind_s(FLAGS.ldap_user_dn, FLAGS.ldap_password) return self @@ -285,8 +275,8 @@ class LdapDriver(object): def __find_dns(self, dn, query=None): """Find dns by query""" try: - res = self.conn.search_s(dn, ldap.SCOPE_SUBTREE, query) - except self.NO_SUCH_OBJECT: + res = self.conn.search_s(dn, self.ldap.SCOPE_SUBTREE, query) + except self.ldap.NO_SUCH_OBJECT: return [] # just return the DNs return [dn for dn, attributes in res] @@ -294,8 +284,8 @@ class LdapDriver(object): def __find_objects(self, dn, query = None): """Find objects by query""" try: - res = self.conn.search_s(dn, ldap.SCOPE_SUBTREE, query) - except self.NO_SUCH_OBJECT: + res = self.conn.search_s(dn, self.ldap.SCOPE_SUBTREE, query) + except self.ldap.NO_SUCH_OBJECT: return [] # just return the attributes return [attributes for dn, attributes in res] @@ -379,7 +369,7 @@ class LdapDriver(object): 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)) + (self.ldap.MOD_ADD, 'member', self.__uid_to_dn(uid)) ] self.conn.modify_s(group_dn, attr) @@ -399,10 +389,10 @@ class LdapDriver(object): def __safe_remove_from_group(self, uid, group_dn): """Remove user from group, deleting group if user is last member""" # FIXME(vish): what if deleted user is a project manager? - attr = [(ldap.MOD_DELETE, 'member', self.__uid_to_dn(uid))] + attr = [(self.ldap.MOD_DELETE, 'member', self.__uid_to_dn(uid))] try: self.conn.modify_s(group_dn, attr) - except self.OBJECT_CLASS_VIOLATION: + except self.ldap.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) diff --git a/nova/auth/manager.py b/nova/auth/manager.py index 2facffe51..3496ea161 100644 --- a/nova/auth/manager.py +++ b/nova/auth/manager.py @@ -41,13 +41,15 @@ FLAGS = flags.FLAGS # 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') + 'Roles that ignore rbac checking completely') # 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') + 'Roles that apply to all projects') + +flags.DEFINE_bool('use_vpn', True, 'Support per-project vpns') flags.DEFINE_string('credentials_template', utils.abspath('auth/novarc.template'), 'Template for creating users rc file') @@ -189,11 +191,13 @@ class Project(AuthBase): @property def vpn_ip(self): - return AuthManager().get_project_vpn_ip(self) + ip, port = AuthManager().get_project_vpn_data(self) + return ip @property def vpn_port(self): - return AuthManager().get_project_vpn_port(self) + ip, port = AuthManager().get_project_vpn_data(self) + return port def has_manager(self, user): return AuthManager().is_project_manager(user, self) @@ -551,7 +555,8 @@ class AuthManager(object): description, member_users) if project_dict: - Vpn.create(project_dict['id']) + if FLAGS.use_vpn: + Vpn.create(project_dict['id']) return Project(**project_dict) def add_to_project(self, user, project): @@ -578,11 +583,20 @@ class AuthManager(object): return drv.remove_from_project(User.safe_id(user), Project.safe_id(project)) - def get_project_vpn_ip(self, project): - return Vpn(Project.safe_id(project)).ip + def get_project_vpn_data(self, project): + """Gets vpn ip and port for project - def get_project_vpn_port(self, project): - return Vpn(Project.safe_id(project)).port + @type project: Project or project_id + @param project: Project from which to get associated vpn data + + @rvalue: tuple of (str, str) + @return: A tuple containing (ip, port) or None, None if vpn has + not been allocated for user. + """ + vpn = Vpn.lookup(Project.safe_id(project)) + if not vpn: + return None, None + return (vpn.ip, vpn.port) def delete_project(self, project): """Deletes a project""" @@ -713,7 +727,10 @@ class AuthManager(object): rc = self.__generate_rc(user.access, user.secret, pid) private_key, signed_cert = self._generate_x509_cert(user.id, pid) - vpn = Vpn(pid) + vpn = Vpn.lookup(pid) + if not vpn: + raise exception.Error("No vpn data allocated for project %s" % + project.name) configfile = open(FLAGS.vpn_client_template,"r") s = string.Template(configfile.read()) configfile.close() -- cgit From aea63a32542ea2534513532b645491687e48367b Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Tue, 20 Jul 2010 14:29:49 -0500 Subject: Move self.ldap to global ldap to make changes easier if we ever implement settings --- nova/auth/ldapdriver.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'nova') diff --git a/nova/auth/ldapdriver.py b/nova/auth/ldapdriver.py index 4ba09517c..a94b219d6 100644 --- a/nova/auth/ldapdriver.py +++ b/nova/auth/ldapdriver.py @@ -64,12 +64,12 @@ class LdapDriver(object): """ def __enter__(self): """Creates the connection to LDAP""" + global ldap if FLAGS.fake_users: from nova.auth import fakeldap as ldap else: import ldap - self.ldap = ldap - self.conn = self.ldap.initialize(FLAGS.ldap_url) + self.conn = ldap.initialize(FLAGS.ldap_url) self.conn.simple_bind_s(FLAGS.ldap_user_dn, FLAGS.ldap_password) return self @@ -275,8 +275,8 @@ class LdapDriver(object): def __find_dns(self, dn, query=None): """Find dns by query""" try: - res = self.conn.search_s(dn, self.ldap.SCOPE_SUBTREE, query) - except self.ldap.NO_SUCH_OBJECT: + res = self.conn.search_s(dn, ldap.SCOPE_SUBTREE, query) + except ldap.NO_SUCH_OBJECT: return [] # just return the DNs return [dn for dn, attributes in res] @@ -284,8 +284,8 @@ class LdapDriver(object): def __find_objects(self, dn, query = None): """Find objects by query""" try: - res = self.conn.search_s(dn, self.ldap.SCOPE_SUBTREE, query) - except self.ldap.NO_SUCH_OBJECT: + res = self.conn.search_s(dn, ldap.SCOPE_SUBTREE, query) + except ldap.NO_SUCH_OBJECT: return [] # just return the attributes return [attributes for dn, attributes in res] @@ -369,7 +369,7 @@ class LdapDriver(object): raise exception.Duplicate("User %s is already a member of " "the group %s" % (uid, group_dn)) attr = [ - (self.ldap.MOD_ADD, 'member', self.__uid_to_dn(uid)) + (ldap.MOD_ADD, 'member', self.__uid_to_dn(uid)) ] self.conn.modify_s(group_dn, attr) @@ -389,10 +389,10 @@ class LdapDriver(object): def __safe_remove_from_group(self, uid, group_dn): """Remove user from group, deleting group if user is last member""" # FIXME(vish): what if deleted user is a project manager? - attr = [(self.ldap.MOD_DELETE, 'member', self.__uid_to_dn(uid))] + attr = [(ldap.MOD_DELETE, 'member', self.__uid_to_dn(uid))] try: self.conn.modify_s(group_dn, attr) - except self.ldap.OBJECT_CLASS_VIOLATION: + except ldap.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) -- cgit From 898102508a1c2a1087b3ffce36b3fb890f5d3775 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Wed, 21 Jul 2010 19:56:08 -0500 Subject: refactoring of imports for fakeldapdriver --- nova/auth/fakeldapdriver.py | 32 ++++++++++++++++++++++++++ nova/auth/ldapdriver.py | 27 +++++++++++----------- nova/auth/manager.py | 47 ++++++++++++++++++++------------------ nova/flags.py | 1 - nova/tests/cloud_unittest.py | 3 +-- nova/tests/fake_flags.py | 2 +- nova/tests/model_unittest.py | 3 +-- nova/tests/node_unittest.py | 3 +-- nova/tests/objectstore_unittest.py | 3 +-- nova/tests/real_flags.py | 1 - 10 files changed, 75 insertions(+), 47 deletions(-) create mode 100644 nova/auth/fakeldapdriver.py (limited to 'nova') diff --git a/nova/auth/fakeldapdriver.py b/nova/auth/fakeldapdriver.py new file mode 100644 index 000000000..833548c79 --- /dev/null +++ b/nova/auth/fakeldapdriver.py @@ -0,0 +1,32 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2010 United States Government as represented by the +# Administrator of the National Aeronautics and Space Administration. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +""" +Fake Auth driver for ldap + +""" + +from nova.auth import ldapdriver + +class AuthDriver(ldapdriver.AuthDriver): + """Ldap Auth driver + + Defines enter and exit and therefore supports the with/as syntax. + """ + def __init__(self): + self.ldap = __import__('nova.auth.fakeldap', fromlist=True) diff --git a/nova/auth/ldapdriver.py b/nova/auth/ldapdriver.py index a94b219d6..beab97e49 100644 --- a/nova/auth/ldapdriver.py +++ b/nova/auth/ldapdriver.py @@ -57,19 +57,18 @@ flags.DEFINE_string('ldap_developer', 'cn=developers,ou=Groups,dc=example,dc=com', 'cn for Developers') -class LdapDriver(object): +class AuthDriver(object): """Ldap Auth driver Defines enter and exit and therefore supports the with/as syntax. """ + def __init__(self): + """Imports the LDAP module""" + self.ldap = __import__('ldap') + def __enter__(self): """Creates the connection to LDAP""" - global ldap - if FLAGS.fake_users: - from nova.auth import fakeldap as ldap - else: - import ldap - self.conn = ldap.initialize(FLAGS.ldap_url) + self.conn = self.ldap.initialize(FLAGS.ldap_url) self.conn.simple_bind_s(FLAGS.ldap_user_dn, FLAGS.ldap_password) return self @@ -275,8 +274,8 @@ class LdapDriver(object): def __find_dns(self, dn, query=None): """Find dns by query""" try: - res = self.conn.search_s(dn, ldap.SCOPE_SUBTREE, query) - except ldap.NO_SUCH_OBJECT: + res = self.conn.search_s(dn, self.ldap.SCOPE_SUBTREE, query) + except self.ldap.NO_SUCH_OBJECT: return [] # just return the DNs return [dn for dn, attributes in res] @@ -284,8 +283,8 @@ class LdapDriver(object): def __find_objects(self, dn, query = None): """Find objects by query""" try: - res = self.conn.search_s(dn, ldap.SCOPE_SUBTREE, query) - except ldap.NO_SUCH_OBJECT: + res = self.conn.search_s(dn, self.ldap.SCOPE_SUBTREE, query) + except self.ldap.NO_SUCH_OBJECT: return [] # just return the attributes return [attributes for dn, attributes in res] @@ -369,7 +368,7 @@ class LdapDriver(object): 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)) + (self.ldap.MOD_ADD, 'member', self.__uid_to_dn(uid)) ] self.conn.modify_s(group_dn, attr) @@ -389,10 +388,10 @@ class LdapDriver(object): def __safe_remove_from_group(self, uid, group_dn): """Remove user from group, deleting group if user is last member""" # FIXME(vish): what if deleted user is a project manager? - attr = [(ldap.MOD_DELETE, 'member', self.__uid_to_dn(uid))] + attr = [(self.ldap.MOD_DELETE, 'member', self.__uid_to_dn(uid))] try: self.conn.modify_s(group_dn, attr) - except ldap.OBJECT_CLASS_VIOLATION: + except self.ldap.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) diff --git a/nova/auth/manager.py b/nova/auth/manager.py index 3496ea161..130bed7c2 100644 --- a/nova/auth/manager.py +++ b/nova/auth/manager.py @@ -34,7 +34,6 @@ from nova import exception from nova import flags from nova import objectstore # for flags from nova import utils -from nova.auth import ldapdriver from nova.auth import signer FLAGS = flags.FLAGS @@ -76,6 +75,8 @@ flags.DEFINE_string('credential_cert_subject', flags.DEFINE_string('vpn_ip', '127.0.0.1', 'Public IP for the cloudpipe VPN servers') +flags.DEFINE_string('auth_driver', 'fakeldapdriver', + 'Driver that auth manager uses') class AuthBase(object): """Base class for objects relating to auth @@ -312,7 +313,7 @@ class AuthManager(object): Methods accept objects or ids. AuthManager uses a driver object to make requests to the data backend. - See ldapdriver.LdapDriver for reference. + See ldapdriver for reference. AuthManager also manages associated data related to Auth objects that need to be more accessible, such as vpn ips and ports. @@ -325,7 +326,9 @@ class AuthManager(object): return cls._instance def __init__(self, *args, **kwargs): - self.driver_class = kwargs.get('driver_class', ldapdriver.LdapDriver) + """Imports the driver module and saves the Driver class""" + mod = __import__(FLAGS.auth_driver, fromlist=True) + self.driver = mod.AuthDriver def authenticate(self, access, signature, params, verb='GET', server_string='127.0.0.1:8773', path='/', @@ -451,7 +454,7 @@ class AuthManager(object): @rtype: bool @return: True if the user has the role. """ - with self.driver_class() as drv: + with self.driver() as drv: if role == 'projectmanager': if not project: raise exception.Error("Must specify project") @@ -487,7 +490,7 @@ class AuthManager(object): @type project: Project or project_id @param project: Project in which to add local role. """ - with self.driver_class() as drv: + with self.driver() as drv: drv.add_role(User.safe_id(user), role, Project.safe_id(project)) def remove_role(self, user, role, project=None): @@ -507,19 +510,19 @@ class AuthManager(object): @type project: Project or project_id @param project: Project in which to remove local role. """ - with self.driver_class() as drv: + with self.driver() as drv: drv.remove_role(User.safe_id(user), role, Project.safe_id(project)) def get_project(self, pid): """Get project object by id""" - with self.driver_class() as drv: + with self.driver() as drv: project_dict = drv.get_project(pid) if project_dict: return Project(**project_dict) def get_projects(self): """Retrieves list of all projects""" - with self.driver_class() as drv: + with self.driver() as drv: project_list = drv.get_projects() if not project_list: return [] @@ -549,7 +552,7 @@ class AuthManager(object): """ if member_users: member_users = [User.safe_id(u) for u in member_users] - with self.driver_class() as drv: + with self.driver() as drv: project_dict = drv.create_project(name, User.safe_id(manager_user), description, @@ -561,7 +564,7 @@ class AuthManager(object): def add_to_project(self, user, project): """Add user to project""" - with self.driver_class() as drv: + with self.driver() as drv: return drv.add_to_project(User.safe_id(user), Project.safe_id(project)) @@ -579,7 +582,7 @@ class AuthManager(object): def remove_from_project(self, user, project): """Removes a user from a project""" - with self.driver_class() as drv: + with self.driver() as drv: return drv.remove_from_project(User.safe_id(user), Project.safe_id(project)) @@ -600,26 +603,26 @@ class AuthManager(object): def delete_project(self, project): """Deletes a project""" - with self.driver_class() as drv: + with self.driver() as drv: return drv.delete_project(Project.safe_id(project)) def get_user(self, uid): """Retrieves a user by id""" - with self.driver_class() as drv: + with self.driver() as drv: user_dict = drv.get_user(uid) if user_dict: return User(**user_dict) def get_user_from_access_key(self, access_key): """Retrieves a user by access key""" - with self.driver_class() as drv: + with self.driver() as drv: user_dict = drv.get_user_from_access_key(access_key) if user_dict: return User(**user_dict) def get_users(self): """Retrieves a list of all users""" - with self.driver_class() as drv: + with self.driver() as drv: user_list = drv.get_users() if not user_list: return [] @@ -649,14 +652,14 @@ class AuthManager(object): """ if access == None: access = str(uuid.uuid4()) if secret == None: secret = str(uuid.uuid4()) - with self.driver_class() as drv: + with self.driver() as drv: user_dict = drv.create_user(name, access, secret, admin) if user_dict: return User(**user_dict) def delete_user(self, user): """Deletes a user""" - with self.driver_class() as drv: + with self.driver() as drv: drv.delete_user(User.safe_id(user)) def generate_key_pair(self, user, key_name): @@ -677,7 +680,7 @@ class AuthManager(object): # NOTE(vish): generating key pair is slow so check for legal # creation before creating keypair uid = User.safe_id(user) - with self.driver_class() as drv: + with self.driver() as drv: if not drv.get_user(uid): raise exception.NotFound("User %s doesn't exist" % user) if drv.get_key_pair(uid, key_name): @@ -689,7 +692,7 @@ class AuthManager(object): def create_key_pair(self, user, key_name, public_key, fingerprint): """Creates a key pair for user""" - with self.driver_class() as drv: + with self.driver() as drv: kp_dict = drv.create_key_pair(User.safe_id(user), key_name, public_key, @@ -699,14 +702,14 @@ class AuthManager(object): def get_key_pair(self, user, key_name): """Retrieves a key pair for user""" - with self.driver_class() as drv: + with self.driver() as drv: kp_dict = drv.get_key_pair(User.safe_id(user), key_name) if kp_dict: return KeyPair(**kp_dict) def get_key_pairs(self, user): """Retrieves all key pairs for user""" - with self.driver_class() as drv: + with self.driver() as drv: kp_list = drv.get_key_pairs(User.safe_id(user)) if not kp_list: return [] @@ -714,7 +717,7 @@ class AuthManager(object): def delete_key_pair(self, user, key_name): """Deletes a key pair for user""" - with self.driver_class() as drv: + with self.driver() as drv: drv.delete_key_pair(User.safe_id(user), key_name) def get_credentials(self, user, project=None): diff --git a/nova/flags.py b/nova/flags.py index 06ea1e007..3ad6a3ad5 100644 --- a/nova/flags.py +++ b/nova/flags.py @@ -46,7 +46,6 @@ DEFINE_bool('fake_libvirt', False, DEFINE_bool('verbose', False, 'show debug output') DEFINE_boolean('fake_rabbit', False, 'use a fake rabbit') DEFINE_bool('fake_network', False, 'should we use fake network devices and addresses') -DEFINE_bool('fake_users', False, 'use fake users') DEFINE_string('rabbit_host', 'localhost', 'rabbit host') DEFINE_integer('rabbit_port', 5672, 'rabbit port') DEFINE_string('rabbit_userid', 'guest', 'rabbit userid') diff --git a/nova/tests/cloud_unittest.py b/nova/tests/cloud_unittest.py index 3abef28a1..741973201 100644 --- a/nova/tests/cloud_unittest.py +++ b/nova/tests/cloud_unittest.py @@ -40,8 +40,7 @@ class CloudTestCase(test.BaseTestCase): def setUp(self): super(CloudTestCase, self).setUp() self.flags(fake_libvirt=True, - fake_storage=True, - fake_users=True) + fake_storage=True) self.conn = rpc.Connection.instance() logging.getLogger().setLevel(logging.DEBUG) diff --git a/nova/tests/fake_flags.py b/nova/tests/fake_flags.py index d32f40d8f..57575b44b 100644 --- a/nova/tests/fake_flags.py +++ b/nova/tests/fake_flags.py @@ -24,5 +24,5 @@ FLAGS.fake_libvirt = True FLAGS.fake_storage = True FLAGS.fake_rabbit = True FLAGS.fake_network = True -FLAGS.fake_users = True +FLAGS.auth_driver = 'nova.auth.fakeldapdriver' FLAGS.verbose = True diff --git a/nova/tests/model_unittest.py b/nova/tests/model_unittest.py index 1bd7e527f..1b94e5798 100644 --- a/nova/tests/model_unittest.py +++ b/nova/tests/model_unittest.py @@ -35,8 +35,7 @@ class ModelTestCase(test.TrialTestCase): def setUp(self): super(ModelTestCase, self).setUp() self.flags(fake_libvirt=True, - fake_storage=True, - fake_users=True) + fake_storage=True) def tearDown(self): model.Instance('i-test').destroy() diff --git a/nova/tests/node_unittest.py b/nova/tests/node_unittest.py index 93942d79e..55c957696 100644 --- a/nova/tests/node_unittest.py +++ b/nova/tests/node_unittest.py @@ -58,8 +58,7 @@ class NodeConnectionTestCase(test.TrialTestCase): logging.getLogger().setLevel(logging.DEBUG) super(NodeConnectionTestCase, self).setUp() self.flags(fake_libvirt=True, - fake_storage=True, - fake_users=True) + fake_storage=True) self.node = node.Node() def create_instance(self): diff --git a/nova/tests/objectstore_unittest.py b/nova/tests/objectstore_unittest.py index 85bcd7c67..1703adb62 100644 --- a/nova/tests/objectstore_unittest.py +++ b/nova/tests/objectstore_unittest.py @@ -51,8 +51,7 @@ os.makedirs(os.path.join(oss_tempdir, 'buckets')) class ObjectStoreTestCase(test.BaseTestCase): def setUp(self): super(ObjectStoreTestCase, self).setUp() - self.flags(fake_users=True, - buckets_path=os.path.join(oss_tempdir, 'buckets'), + self.flags(buckets_path=os.path.join(oss_tempdir, 'buckets'), images_path=os.path.join(oss_tempdir, 'images'), ca_path=os.path.join(os.path.dirname(__file__), 'CA')) logging.getLogger().setLevel(logging.DEBUG) diff --git a/nova/tests/real_flags.py b/nova/tests/real_flags.py index 9e106f227..f054a8f19 100644 --- a/nova/tests/real_flags.py +++ b/nova/tests/real_flags.py @@ -24,5 +24,4 @@ FLAGS.fake_libvirt = False FLAGS.fake_storage = False FLAGS.fake_rabbit = False FLAGS.fake_network = False -FLAGS.fake_users = False FLAGS.verbose = False -- cgit From 5066e1f55fa672f6b6eec1523b5334e6fe9609a2 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Wed, 21 Jul 2010 21:54:50 -0500 Subject: added todo for ABC --- nova/auth/ldapdriver.py | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'nova') diff --git a/nova/auth/ldapdriver.py b/nova/auth/ldapdriver.py index beab97e49..0535977af 100644 --- a/nova/auth/ldapdriver.py +++ b/nova/auth/ldapdriver.py @@ -57,6 +57,10 @@ flags.DEFINE_string('ldap_developer', 'cn=developers,ou=Groups,dc=example,dc=com', 'cn for Developers') +# TODO(vish): make an abstract base class with the same public methods +# to define a set interface for AuthDrivers. I'm delaying +# creating this now because I'm expecting an auth refactor +# in which we may want to change the interface a bit more. class AuthDriver(object): """Ldap Auth driver -- cgit From 87e27afec0c7b683ee35f842abdaccea954f2fba Mon Sep 17 00:00:00 2001 From: Monty Taylor Date: Sat, 24 Jul 2010 18:06:22 -0700 Subject: Updated sphinx layout to a two-dir layout like swift. Updated a doc string to get rid of a Sphinx warning. --- nova/compute/disk.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'nova') diff --git a/nova/compute/disk.py b/nova/compute/disk.py index 08a22556e..5749d4c6a 100644 --- a/nova/compute/disk.py +++ b/nova/compute/disk.py @@ -40,7 +40,8 @@ def partition(infile, outfile, local_bytes=0, local_type='ext2', execute=None): formatted as ext2. In the diagram below, dashes represent drive sectors. - 0 a b c d e + +-----+------. . .-------+------. . .------+ + | 0 a| b c|d e| +-----+------. . .-------+------. . .------+ | mbr | primary partiton | local partition | +-----+------. . .-------+------. . .------+ -- cgit From ad2250ac0080ca35b1fd2747e3f4d0ff07bc90be Mon Sep 17 00:00:00 2001 From: Ewan Mellor Date: Sun, 25 Jul 2010 17:40:41 +0100 Subject: Replace hardcoded "nova" with FLAGS.control_exchange. --- nova/rpc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova') diff --git a/nova/rpc.py b/nova/rpc.py index ef463e84b..5610ea124 100644 --- a/nova/rpc.py +++ b/nova/rpc.py @@ -242,7 +242,7 @@ def send_message(topic, message, wait=True): consumer.register_callback(generic_response) publisher = messaging.Publisher(connection=Connection.instance(), - exchange="nova", + exchange=FLAGS.control_exchange, exchange_type="topic", routing_key=topic) publisher.send(message) -- cgit From f7962c73aa9835c76857005ab56f512fbc9eebfd Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Sun, 25 Jul 2010 11:20:09 -0700 Subject: More Cleanup of code Moved code in AuthManager init to new so it isn't called multiple times Changed AuthManager flag to specify class name as well as module name Added exception for missing auth_driver Changed import to use "recommended" style for nested imports http://docs.python.org/dev/library/functions.html#__import__ --- nova/auth/fakeldapdriver.py | 32 -------------------------------- nova/auth/ldapdriver.py | 12 ++++++++++-- nova/auth/manager.py | 22 ++++++++++++++++------ nova/tests/fake_flags.py | 2 +- nova/tests/network_unittest.py | 2 +- 5 files changed, 28 insertions(+), 42 deletions(-) delete mode 100644 nova/auth/fakeldapdriver.py (limited to 'nova') diff --git a/nova/auth/fakeldapdriver.py b/nova/auth/fakeldapdriver.py deleted file mode 100644 index 833548c79..000000000 --- a/nova/auth/fakeldapdriver.py +++ /dev/null @@ -1,32 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2010 United States Government as represented by the -# Administrator of the National Aeronautics and Space Administration. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -""" -Fake Auth driver for ldap - -""" - -from nova.auth import ldapdriver - -class AuthDriver(ldapdriver.AuthDriver): - """Ldap Auth driver - - Defines enter and exit and therefore supports the with/as syntax. - """ - def __init__(self): - self.ldap = __import__('nova.auth.fakeldap', fromlist=True) diff --git a/nova/auth/ldapdriver.py b/nova/auth/ldapdriver.py index 0535977af..1591c88e9 100644 --- a/nova/auth/ldapdriver.py +++ b/nova/auth/ldapdriver.py @@ -17,7 +17,7 @@ # under the License. """ -Auth driver for ldap +Auth driver for ldap. Includes FakeLdapDriver. It should be easy to create a replacement for this driver supporting other backends by creating another class that exposes the same @@ -25,6 +25,7 @@ public methods. """ import logging +import sys from nova import exception from nova import flags @@ -61,7 +62,7 @@ flags.DEFINE_string('ldap_developer', # to define a set interface for AuthDrivers. I'm delaying # creating this now because I'm expecting an auth refactor # in which we may want to change the interface a bit more. -class AuthDriver(object): +class LdapDriver(object): """Ldap Auth driver Defines enter and exit and therefore supports the with/as syntax. @@ -471,3 +472,10 @@ class AuthDriver(object): """Convert uid to dn""" return 'uid=%s,%s' % (dn, FLAGS.ldap_user_subtree) + +class FakeLdapDriver(LdapDriver): + """Fake Ldap Auth driver""" + def __init__(self): + __import__('nova.auth.fakeldap') + self.ldap = sys.modules['nova.auth.fakeldap'] + diff --git a/nova/auth/manager.py b/nova/auth/manager.py index 130bed7c2..32c2f9e02 100644 --- a/nova/auth/manager.py +++ b/nova/auth/manager.py @@ -24,6 +24,7 @@ import logging import os import shutil import string +import sys import tempfile import uuid import zipfile @@ -75,7 +76,7 @@ flags.DEFINE_string('credential_cert_subject', flags.DEFINE_string('vpn_ip', '127.0.0.1', 'Public IP for the cloudpipe VPN servers') -flags.DEFINE_string('auth_driver', 'fakeldapdriver', +flags.DEFINE_string('auth_driver', 'nova.auth.ldapdriver.AuthDriver', 'Driver that auth manager uses') class AuthBase(object): @@ -320,16 +321,25 @@ class AuthManager(object): """ _instance=None def __new__(cls, *args, **kwargs): + """Returns the AuthManager singleton with driver set + + __init__ is run every time AuthManager() is called, so we need to do + any constructor related stuff here. The driver that is specified + in the flagfile is loaded here. + """ if not cls._instance: cls._instance = super(AuthManager, cls).__new__( cls, *args, **kwargs) + mod_str, sep, driver_str = FLAGS.auth_driver.rpartition('.') + try: + mod = __import__(mod_str) + cls._instance.driver = getattr(sys.modules[mod_str], + driver_str) + except (ImportError, AttributeError): + raise exception.Error('Auth driver %s cannot be found' + % FLAGS.auth_driver) return cls._instance - def __init__(self, *args, **kwargs): - """Imports the driver module and saves the Driver class""" - mod = __import__(FLAGS.auth_driver, fromlist=True) - self.driver = mod.AuthDriver - def authenticate(self, access, signature, params, verb='GET', server_string='127.0.0.1:8773', path='/', verify_signature=True): diff --git a/nova/tests/fake_flags.py b/nova/tests/fake_flags.py index 57575b44b..304f24841 100644 --- a/nova/tests/fake_flags.py +++ b/nova/tests/fake_flags.py @@ -24,5 +24,5 @@ FLAGS.fake_libvirt = True FLAGS.fake_storage = True FLAGS.fake_rabbit = True FLAGS.fake_network = True -FLAGS.auth_driver = 'nova.auth.fakeldapdriver' +FLAGS.auth_driver = 'nova.auth.ldapdriver.FakeLdapDriver' FLAGS.verbose = True diff --git a/nova/tests/network_unittest.py b/nova/tests/network_unittest.py index 12840e736..9e17bf155 100644 --- a/nova/tests/network_unittest.py +++ b/nova/tests/network_unittest.py @@ -37,7 +37,7 @@ class NetworkTestCase(test.TrialTestCase): self.flags(fake_libvirt=True, fake_storage=True, fake_network=True, - auth_driver='nova.auth.fakeldapdriver', + auth_driver='nova.auth.ldapdriver.FakeLdapDriver', network_size=32) logging.getLogger().setLevel(logging.DEBUG) self.manager = manager.AuthManager() -- cgit From 3233f7a964564fba9ec88c277d566eebed50d12a Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Sun, 25 Jul 2010 11:23:24 -0700 Subject: removed unused assignment --- nova/auth/manager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova') diff --git a/nova/auth/manager.py b/nova/auth/manager.py index 32c2f9e02..93330790b 100644 --- a/nova/auth/manager.py +++ b/nova/auth/manager.py @@ -332,7 +332,7 @@ class AuthManager(object): cls, *args, **kwargs) mod_str, sep, driver_str = FLAGS.auth_driver.rpartition('.') try: - mod = __import__(mod_str) + __import__(mod_str) cls._instance.driver = getattr(sys.modules[mod_str], driver_str) except (ImportError, AttributeError): -- cgit From ffe52b8660123335e425c52eb3bebba2e3d2e42f Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Mon, 26 Jul 2010 14:48:08 -0700 Subject: default flag file full path --- nova/compute/linux_net.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova') diff --git a/nova/compute/linux_net.py b/nova/compute/linux_net.py index 48e07da66..861ce779b 100644 --- a/nova/compute/linux_net.py +++ b/nova/compute/linux_net.py @@ -29,7 +29,7 @@ from nova import flags FLAGS=flags.FLAGS flags.DEFINE_string('dhcpbridge_flagfile', - '/etc/nova-dhcpbridge.conf', + '/etc/nova/nova-dhcpbridge.conf', 'location of flagfile for dhcpbridge') def execute(cmd, addl_env=None): -- cgit From be176f06fd03ddb6c25b40e4f2ee71981f47c724 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Mon, 26 Jul 2010 16:03:33 -0700 Subject: fix auth_driver flag to default to usable driver --- nova/auth/manager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova') diff --git a/nova/auth/manager.py b/nova/auth/manager.py index 93330790b..bc373fd26 100644 --- a/nova/auth/manager.py +++ b/nova/auth/manager.py @@ -76,7 +76,7 @@ flags.DEFINE_string('credential_cert_subject', flags.DEFINE_string('vpn_ip', '127.0.0.1', 'Public IP for the cloudpipe VPN servers') -flags.DEFINE_string('auth_driver', 'nova.auth.ldapdriver.AuthDriver', +flags.DEFINE_string('auth_driver', 'nova.auth.ldapdriver.FakeLdapDriver', 'Driver that auth manager uses') class AuthBase(object): -- cgit From a2cf8a6f6038062cf343322acdbde66456b73dfb Mon Sep 17 00:00:00 2001 From: "jaypipes@gmail.com" <> Date: Mon, 26 Jul 2010 23:28:59 -0400 Subject: Fixes bug#610140. Thanks to Vish and Muharem for the patch --- nova/tests/api_unittest.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'nova') diff --git a/nova/tests/api_unittest.py b/nova/tests/api_unittest.py index e5e2afe26..45ae50b2e 100644 --- a/nova/tests/api_unittest.py +++ b/nova/tests/api_unittest.py @@ -43,7 +43,11 @@ def boto_to_tornado(method, path, headers, data, host, connection=None): connection should be a FakeTornadoHttpConnection instance """ - headers = httpserver.HTTPHeaders() + try: + headers = httpserver.HTTPHeaders() + except AttributeError: + from tornado import httputil + headers = httputil.HTTPHeaders() for k, v in headers.iteritems(): headers[k] = v -- cgit