Remove deprecated auth code
* Remove nova.auth.manager -- AuthManager, User, Role, Project, etc. * Remove nova.auth.dbdriver * Remove nova.auth.ldapdriver * Remove nova.auth.signer * Remove arbitrary scripts and schemas in nova/auth/ * Remove nova/auth/novarc.template * Remove or update affected tests * Related to bp remove-deprecated-auth Change-Id: Ide0fefd0ddf79ae1b3bb74cb242c2893575839e7
This commit is contained in:
parent
8226531088
commit
90e1eb9fd5
@ -75,7 +75,6 @@ if os.path.exists(os.path.join(POSSIBLE_TOPDIR, 'nova', '__init__.py')):
|
||||
gettext.install('nova', unicode=1)
|
||||
|
||||
from nova.api.ec2 import ec2utils
|
||||
from nova.auth import manager
|
||||
from nova.compat import flagfile
|
||||
from nova.compute import instance_types
|
||||
from nova.compute import rpcapi as compute_rpcapi
|
||||
@ -129,9 +128,6 @@ def param2id(object_id):
|
||||
class VpnCommands(object):
|
||||
"""Class for managing VPNs."""
|
||||
|
||||
def __init__(self):
|
||||
self.manager = manager.AuthManager()
|
||||
|
||||
@args('--project', dest="project_id", metavar='<Project name>',
|
||||
help='Project name')
|
||||
@args('--ip', dest="ip", metavar='<IP Address>', help='IP Address')
|
||||
@ -228,9 +224,6 @@ def _db_error(caught_exception):
|
||||
class ProjectCommands(object):
|
||||
"""Class for managing projects."""
|
||||
|
||||
def __init__(self):
|
||||
self.manager = manager.AuthManager()
|
||||
|
||||
def quota(self, project_id, key=None, value=None):
|
||||
"""Set or display quotas for project"""
|
||||
ctxt = context.get_admin_context()
|
||||
|
@ -1,232 +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.
|
||||
|
||||
"""
|
||||
Auth driver using the DB as its backend.
|
||||
"""
|
||||
|
||||
from nova import context
|
||||
from nova import db
|
||||
from nova import exception
|
||||
|
||||
|
||||
class DbDriver(object):
|
||||
"""DB Auth driver
|
||||
|
||||
Defines enter and exit and therefore supports the with/as syntax.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
"""Imports the LDAP module"""
|
||||
pass
|
||||
|
||||
def __enter__(self):
|
||||
return self
|
||||
|
||||
def __exit__(self, exc_type, exc_value, traceback):
|
||||
pass
|
||||
|
||||
def get_user(self, uid):
|
||||
"""Retrieve user by id"""
|
||||
user = db.user_get(context.get_admin_context(), uid)
|
||||
return self._db_user_to_auth_user(user)
|
||||
|
||||
def get_user_from_access_key(self, access):
|
||||
"""Retrieve user by access key"""
|
||||
user = db.user_get_by_access_key(context.get_admin_context(), access)
|
||||
return self._db_user_to_auth_user(user)
|
||||
|
||||
def get_project(self, pid):
|
||||
"""Retrieve project by id"""
|
||||
project = db.project_get(context.get_admin_context(), pid)
|
||||
return self._db_project_to_auth_projectuser(project)
|
||||
|
||||
def get_users(self):
|
||||
"""Retrieve list of users"""
|
||||
return [self._db_user_to_auth_user(user)
|
||||
for user in db.user_get_all(context.get_admin_context())]
|
||||
|
||||
def get_projects(self, uid=None):
|
||||
"""Retrieve list of projects"""
|
||||
if uid:
|
||||
result = db.project_get_by_user(context.get_admin_context(), uid)
|
||||
else:
|
||||
result = db.project_get_all(context.get_admin_context())
|
||||
return [self._db_project_to_auth_projectuser(proj) for proj in result]
|
||||
|
||||
def create_user(self, name, access_key, secret_key, is_admin):
|
||||
"""Create a user"""
|
||||
values = {'id': name,
|
||||
'access_key': access_key,
|
||||
'secret_key': secret_key,
|
||||
'is_admin': is_admin}
|
||||
try:
|
||||
user_ref = db.user_create(context.get_admin_context(), values)
|
||||
return self._db_user_to_auth_user(user_ref)
|
||||
except (exception.Duplicate, exception.DBError) as e:
|
||||
raise exception.UserExists(user=name)
|
||||
|
||||
def _db_user_to_auth_user(self, user_ref):
|
||||
return {'id': user_ref['id'],
|
||||
'name': user_ref['id'],
|
||||
'access': user_ref['access_key'],
|
||||
'secret': user_ref['secret_key'],
|
||||
'admin': user_ref['is_admin']}
|
||||
|
||||
def _db_project_to_auth_projectuser(self, project_ref):
|
||||
member_ids = [member['id'] for member in project_ref['members']]
|
||||
return {'id': project_ref['id'],
|
||||
'name': project_ref['name'],
|
||||
'project_manager_id': project_ref['project_manager'],
|
||||
'description': project_ref['description'],
|
||||
'member_ids': member_ids}
|
||||
|
||||
def create_project(self, name, manager_uid,
|
||||
description=None, member_uids=None):
|
||||
"""Create a project"""
|
||||
manager = db.user_get(context.get_admin_context(), manager_uid)
|
||||
|
||||
# description is a required attribute
|
||||
if description is None:
|
||||
description = name
|
||||
|
||||
# First, we ensure that all the given users exist before we go
|
||||
# on to create the project. This way we won't have to destroy
|
||||
# the project again because a user turns out to be invalid.
|
||||
members = set([manager])
|
||||
if member_uids is not None:
|
||||
for member_uid in member_uids:
|
||||
member = db.user_get(context.get_admin_context(), member_uid)
|
||||
members.add(member)
|
||||
|
||||
values = {'id': name,
|
||||
'name': name,
|
||||
'project_manager': manager['id'],
|
||||
'description': description}
|
||||
|
||||
try:
|
||||
project = db.project_create(context.get_admin_context(), values)
|
||||
except exception.DBError:
|
||||
raise exception.ProjectExists(project=name)
|
||||
|
||||
for member in members:
|
||||
db.project_add_member(context.get_admin_context(),
|
||||
project['id'],
|
||||
member['id'])
|
||||
|
||||
# This looks silly, but ensures that the members element has been
|
||||
# correctly populated
|
||||
project_ref = db.project_get(context.get_admin_context(),
|
||||
project['id'])
|
||||
return self._db_project_to_auth_projectuser(project_ref)
|
||||
|
||||
def modify_project(self, project_id, manager_uid=None, description=None):
|
||||
"""Modify an existing project"""
|
||||
if not manager_uid and not description:
|
||||
return
|
||||
values = {}
|
||||
if manager_uid:
|
||||
manager = db.user_get(context.get_admin_context(), manager_uid)
|
||||
values['project_manager'] = manager['id']
|
||||
if description:
|
||||
values['description'] = description
|
||||
|
||||
db.project_update(context.get_admin_context(), project_id, values)
|
||||
if not self.is_in_project(manager_uid, project_id):
|
||||
self.add_to_project(manager_uid, project_id)
|
||||
|
||||
def add_to_project(self, uid, project_id):
|
||||
"""Add user to project"""
|
||||
user, project = self._validate_user_and_project(uid, project_id)
|
||||
db.project_add_member(context.get_admin_context(),
|
||||
project['id'],
|
||||
user['id'])
|
||||
|
||||
def remove_from_project(self, uid, project_id):
|
||||
"""Remove user from project"""
|
||||
user, project = self._validate_user_and_project(uid, project_id)
|
||||
db.project_remove_member(context.get_admin_context(),
|
||||
project['id'],
|
||||
user['id'])
|
||||
|
||||
def is_in_project(self, uid, project_id):
|
||||
"""Check if user is in project"""
|
||||
user, project = self._validate_user_and_project(uid, project_id)
|
||||
return user in project.members
|
||||
|
||||
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
|
||||
"""
|
||||
|
||||
return role in self.get_user_roles(uid, project_id)
|
||||
|
||||
def add_role(self, uid, role, project_id=None):
|
||||
"""Add role for user (or user and project)"""
|
||||
if not project_id:
|
||||
db.user_add_role(context.get_admin_context(), uid, role)
|
||||
return
|
||||
db.user_add_project_role(context.get_admin_context(),
|
||||
uid, project_id, role)
|
||||
|
||||
def remove_role(self, uid, role, project_id=None):
|
||||
"""Remove role for user (or user and project)"""
|
||||
if not project_id:
|
||||
db.user_remove_role(context.get_admin_context(), uid, role)
|
||||
return
|
||||
db.user_remove_project_role(context.get_admin_context(),
|
||||
uid, project_id, role)
|
||||
|
||||
def get_user_roles(self, uid, project_id=None):
|
||||
"""Retrieve list of roles for user (or user and project)"""
|
||||
if project_id is None:
|
||||
roles = db.user_get_roles(context.get_admin_context(), uid)
|
||||
return roles
|
||||
else:
|
||||
roles = db.user_get_roles_for_project(context.get_admin_context(),
|
||||
uid, project_id)
|
||||
return roles
|
||||
|
||||
def delete_user(self, id):
|
||||
"""Delete a user"""
|
||||
user = db.user_get(context.get_admin_context(), id)
|
||||
db.user_delete(context.get_admin_context(), user['id'])
|
||||
|
||||
def delete_project(self, project_id):
|
||||
"""Delete a project"""
|
||||
db.project_delete(context.get_admin_context(), project_id)
|
||||
|
||||
def modify_user(self, uid, access_key=None, secret_key=None, admin=None):
|
||||
"""Modify an existing user"""
|
||||
if not access_key and not secret_key and admin is None:
|
||||
return
|
||||
values = {}
|
||||
if access_key:
|
||||
values['access_key'] = access_key
|
||||
if secret_key:
|
||||
values['secret_key'] = secret_key
|
||||
if admin is not None:
|
||||
values['is_admin'] = admin
|
||||
db.user_update(context.get_admin_context(), uid, values)
|
||||
|
||||
def _validate_user_and_project(self, user_id, project_id):
|
||||
user = db.user_get(context.get_admin_context(), user_id)
|
||||
project = db.project_get(context.get_admin_context(), project_id)
|
||||
return user, project
|
@ -1,746 +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.
|
||||
|
||||
"""
|
||||
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
|
||||
public methods.
|
||||
"""
|
||||
|
||||
import functools
|
||||
import sys
|
||||
|
||||
from nova import exception
|
||||
from nova import flags
|
||||
from nova.openstack.common import cfg
|
||||
from nova.openstack.common import log as logging
|
||||
|
||||
|
||||
ldap_opts = [
|
||||
cfg.IntOpt('ldap_schema_version',
|
||||
default=2,
|
||||
help='Current version of the LDAP schema'),
|
||||
cfg.StrOpt('ldap_url',
|
||||
default='ldap://localhost',
|
||||
help='Point this at your ldap server'),
|
||||
cfg.StrOpt('ldap_password',
|
||||
default='changeme',
|
||||
help='LDAP password'),
|
||||
cfg.StrOpt('ldap_user_dn',
|
||||
default='cn=Manager,dc=example,dc=com',
|
||||
help='DN of admin user'),
|
||||
cfg.StrOpt('ldap_user_id_attribute',
|
||||
default='uid',
|
||||
help='Attribute to use as id'),
|
||||
cfg.StrOpt('ldap_user_name_attribute',
|
||||
default='cn',
|
||||
help='Attribute to use as name'),
|
||||
cfg.StrOpt('ldap_user_unit',
|
||||
default='Users',
|
||||
help='OID for Users'),
|
||||
cfg.StrOpt('ldap_user_subtree',
|
||||
default='ou=Users,dc=example,dc=com',
|
||||
help='OU for Users'),
|
||||
cfg.BoolOpt('ldap_user_modify_only',
|
||||
default=False,
|
||||
help='Modify user attributes instead of creating/deleting'),
|
||||
cfg.StrOpt('ldap_project_subtree',
|
||||
default='ou=Groups,dc=example,dc=com',
|
||||
help='OU for Projects'),
|
||||
cfg.StrOpt('role_project_subtree',
|
||||
default='ou=Groups,dc=example,dc=com',
|
||||
help='OU for Roles'),
|
||||
|
||||
# NOTE(vish): mapping with these flags is necessary because we're going
|
||||
# to tie in to an existing ldap schema
|
||||
cfg.StrOpt('ldap_cloudadmin',
|
||||
default='cn=cloudadmins,ou=Groups,dc=example,dc=com',
|
||||
help='cn for Cloud Admins'),
|
||||
cfg.StrOpt('ldap_itsec',
|
||||
default='cn=itsec,ou=Groups,dc=example,dc=com',
|
||||
help='cn for ItSec'),
|
||||
cfg.StrOpt('ldap_sysadmin',
|
||||
default='cn=sysadmins,ou=Groups,dc=example,dc=com',
|
||||
help='cn for Sysadmins'),
|
||||
cfg.StrOpt('ldap_netadmin',
|
||||
default='cn=netadmins,ou=Groups,dc=example,dc=com',
|
||||
help='cn for NetAdmins'),
|
||||
cfg.StrOpt('ldap_developer',
|
||||
default='cn=developers,ou=Groups,dc=example,dc=com',
|
||||
help='cn for Developers'),
|
||||
]
|
||||
|
||||
FLAGS = flags.FLAGS
|
||||
FLAGS.register_opts(ldap_opts)
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
if FLAGS.memcached_servers:
|
||||
import memcache
|
||||
else:
|
||||
from nova.common import memorycache as memcache
|
||||
|
||||
|
||||
# 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.
|
||||
|
||||
|
||||
def _clean(attr):
|
||||
"""Clean attr for insertion into ldap"""
|
||||
if attr is None:
|
||||
return None
|
||||
if isinstance(attr, unicode):
|
||||
return str(attr)
|
||||
return attr
|
||||
|
||||
|
||||
def sanitize(fn):
|
||||
"""Decorator to sanitize all args"""
|
||||
@functools.wraps(fn)
|
||||
def _wrapped(self, *args, **kwargs):
|
||||
args = [_clean(x) for x in args]
|
||||
kwargs = dict((k, _clean(v)) for (k, v) in kwargs)
|
||||
return fn(self, *args, **kwargs)
|
||||
_wrapped.func_name = fn.func_name
|
||||
return _wrapped
|
||||
|
||||
|
||||
class LDAPWrapper(object):
|
||||
def __init__(self, ldap, url, user, password):
|
||||
self.ldap = ldap
|
||||
self.url = url
|
||||
self.user = user
|
||||
self.password = password
|
||||
self.conn = None
|
||||
|
||||
def __wrap_reconnect(f):
|
||||
def inner(self, *args, **kwargs):
|
||||
if self.conn is None:
|
||||
self.connect()
|
||||
return f(self.conn)(*args, **kwargs)
|
||||
else:
|
||||
try:
|
||||
return f(self.conn)(*args, **kwargs)
|
||||
except self.ldap.SERVER_DOWN:
|
||||
self.connect()
|
||||
return f(self.conn)(*args, **kwargs)
|
||||
return inner
|
||||
|
||||
def connect(self):
|
||||
try:
|
||||
self.conn = self.ldap.initialize(self.url)
|
||||
self.conn.simple_bind_s(self.user, self.password)
|
||||
except self.ldap.SERVER_DOWN:
|
||||
self.conn = None
|
||||
raise
|
||||
|
||||
search_s = __wrap_reconnect(lambda conn: conn.search_s)
|
||||
add_s = __wrap_reconnect(lambda conn: conn.add_s)
|
||||
delete_s = __wrap_reconnect(lambda conn: conn.delete_s)
|
||||
modify_s = __wrap_reconnect(lambda conn: conn.modify_s)
|
||||
|
||||
|
||||
class LdapDriver(object):
|
||||
"""Ldap Auth driver
|
||||
|
||||
Defines enter and exit and therefore supports the with/as syntax.
|
||||
"""
|
||||
|
||||
project_pattern = '(owner=*)'
|
||||
isadmin_attribute = 'isNovaAdmin'
|
||||
project_attribute = 'owner'
|
||||
project_objectclass = 'groupOfNames'
|
||||
conn = None
|
||||
mc = None
|
||||
|
||||
def __init__(self):
|
||||
"""Imports the LDAP module"""
|
||||
self.ldap = __import__('ldap')
|
||||
if FLAGS.ldap_schema_version == 1:
|
||||
LdapDriver.project_pattern = '(objectclass=novaProject)'
|
||||
LdapDriver.isadmin_attribute = 'isAdmin'
|
||||
LdapDriver.project_attribute = 'projectManager'
|
||||
LdapDriver.project_objectclass = 'novaProject'
|
||||
self.__cache = None
|
||||
if LdapDriver.conn is None:
|
||||
LdapDriver.conn = LDAPWrapper(self.ldap, FLAGS.ldap_url,
|
||||
FLAGS.ldap_user_dn,
|
||||
FLAGS.ldap_password)
|
||||
if LdapDriver.mc is None:
|
||||
LdapDriver.mc = memcache.Client(FLAGS.memcached_servers, debug=0)
|
||||
|
||||
def __enter__(self):
|
||||
# TODO(yorik-sar): Should be per-request cache, not per-driver-request
|
||||
self.__cache = {}
|
||||
return self
|
||||
|
||||
def __exit__(self, exc_type, exc_value, traceback):
|
||||
self.__cache = None
|
||||
return False
|
||||
|
||||
def __local_cache(key_fmt): # pylint: disable=E0213
|
||||
"""Wrap function to cache its result in self.__cache.
|
||||
Works only with functions with one fixed argument.
|
||||
"""
|
||||
def do_wrap(fn):
|
||||
@functools.wraps(fn)
|
||||
def inner(self, arg, **kwargs):
|
||||
cache_key = key_fmt % (arg,)
|
||||
try:
|
||||
res = self.__cache[cache_key]
|
||||
LOG.debug('Local cache hit for %s by key %s' %
|
||||
(fn.__name__, cache_key))
|
||||
return res
|
||||
except KeyError:
|
||||
res = fn(self, arg, **kwargs)
|
||||
self.__cache[cache_key] = res
|
||||
return res
|
||||
return inner
|
||||
return do_wrap
|
||||
|
||||
@sanitize
|
||||
@__local_cache('uid_user-%s')
|
||||
def get_user(self, uid):
|
||||
"""Retrieve user by id"""
|
||||
attr = self.__get_ldap_user(uid)
|
||||
if attr is None:
|
||||
raise exception.LDAPUserNotFound(user_id=uid)
|
||||
return self.__to_user(attr)
|
||||
|
||||
@sanitize
|
||||
def get_user_from_access_key(self, access):
|
||||
"""Retrieve user by access key"""
|
||||
cache_key = 'uak_dn_%s' % (access,)
|
||||
user_dn = self.mc.get(cache_key)
|
||||
if user_dn:
|
||||
user = self.__to_user(
|
||||
self.__find_object(user_dn, scope=self.ldap.SCOPE_BASE))
|
||||
if user:
|
||||
if user['access'] == access:
|
||||
return user
|
||||
else:
|
||||
self.mc.set(cache_key, None)
|
||||
query = '(accessKey=%s)' % access
|
||||
dn = FLAGS.ldap_user_subtree
|
||||
user_obj = self.__find_object(dn, query)
|
||||
user = self.__to_user(user_obj)
|
||||
if user:
|
||||
self.mc.set(cache_key, user_obj['dn'][0])
|
||||
return user
|
||||
|
||||
@sanitize
|
||||
@__local_cache('pid_project-%s')
|
||||
def get_project(self, pid):
|
||||
"""Retrieve project by id"""
|
||||
dn = self.__project_to_dn(pid, search=False)
|
||||
attr = self.__find_object(dn, LdapDriver.project_pattern,
|
||||
scope=self.ldap.SCOPE_BASE)
|
||||
return self.__to_project(attr)
|
||||
|
||||
@sanitize
|
||||
def get_users(self):
|
||||
"""Retrieve list of users"""
|
||||
attrs = self.__find_objects(FLAGS.ldap_user_subtree,
|
||||
'(objectclass=novaUser)')
|
||||
users = []
|
||||
for attr in attrs:
|
||||
user = self.__to_user(attr)
|
||||
if user is not None:
|
||||
users.append(user)
|
||||
return users
|
||||
|
||||
@sanitize
|
||||
def get_projects(self, uid=None):
|
||||
"""Retrieve list of projects"""
|
||||
pattern = LdapDriver.project_pattern
|
||||
if uid:
|
||||
pattern = "(&%s(member=%s))" % (pattern, self.__uid_to_dn(uid))
|
||||
attrs = self.__find_objects(FLAGS.ldap_project_subtree,
|
||||
pattern)
|
||||
return [self.__to_project(attr) for attr in attrs]
|
||||
|
||||
@sanitize
|
||||
def create_user(self, name, access_key, secret_key, is_admin):
|
||||
"""Create a user"""
|
||||
if self.__user_exists(name):
|
||||
raise exception.LDAPUserExists(user=name)
|
||||
if FLAGS.ldap_user_modify_only:
|
||||
if self.__ldap_user_exists(name):
|
||||
# Retrieve user by name
|
||||
user = self.__get_ldap_user(name)
|
||||
# Entry could be malformed, test for missing attrs.
|
||||
# Malformed entries are useless, replace attributes found.
|
||||
attr = []
|
||||
if 'secretKey' in user.keys():
|
||||
attr.append((self.ldap.MOD_REPLACE, 'secretKey',
|
||||
[secret_key]))
|
||||
else:
|
||||
attr.append((self.ldap.MOD_ADD, 'secretKey',
|
||||
[secret_key]))
|
||||
if 'accessKey' in user.keys():
|
||||
attr.append((self.ldap.MOD_REPLACE, 'accessKey',
|
||||
[access_key]))
|
||||
else:
|
||||
attr.append((self.ldap.MOD_ADD, 'accessKey',
|
||||
[access_key]))
|
||||
if LdapDriver.isadmin_attribute in user.keys():
|
||||
attr.append((self.ldap.MOD_REPLACE,
|
||||
LdapDriver.isadmin_attribute,
|
||||
[str(is_admin).upper()]))
|
||||
else:
|
||||
attr.append((self.ldap.MOD_ADD,
|
||||
LdapDriver.isadmin_attribute,
|
||||
[str(is_admin).upper()]))
|
||||
self.conn.modify_s(self.__uid_to_dn(name), attr)
|
||||
return self.get_user(name)
|
||||
else:
|
||||
raise exception.LDAPUserNotFound(user_id=name)
|
||||
else:
|
||||
attr = [
|
||||
('objectclass', ['person',
|
||||
'organizationalPerson',
|
||||
'inetOrgPerson',
|
||||
'novaUser']),
|
||||
('ou', [FLAGS.ldap_user_unit]),
|
||||
(FLAGS.ldap_user_id_attribute, [name]),
|
||||
('sn', [name]),
|
||||
(FLAGS.ldap_user_name_attribute, [name]),
|
||||
('secretKey', [secret_key]),
|
||||
('accessKey', [access_key]),
|
||||
(LdapDriver.isadmin_attribute, [str(is_admin).upper()]),
|
||||
]
|
||||
self.conn.add_s(self.__uid_to_dn(name), attr)
|
||||
return self.__to_user(dict(attr))
|
||||
|
||||
@sanitize
|
||||
def create_project(self, name, manager_uid,
|
||||
description=None, member_uids=None):
|
||||
"""Create a project"""
|
||||
if self.__project_exists(name):
|
||||
raise exception.ProjectExists(project=name)
|
||||
if not self.__user_exists(manager_uid):
|
||||
raise exception.LDAPUserNotFound(user_id=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 is not None:
|
||||
for member_uid in member_uids:
|
||||
if not self.__user_exists(member_uid):
|
||||
raise exception.LDAPUserNotFound(user_id=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', [LdapDriver.project_objectclass]),
|
||||
('cn', [name]),
|
||||
('description', [description]),
|
||||
(LdapDriver.project_attribute, [manager_dn]),
|
||||
('member', members)]
|
||||
dn = self.__project_to_dn(name, search=False)
|
||||
self.conn.add_s(dn, attr)
|
||||
return self.__to_project(dict(attr))
|
||||
|
||||
@sanitize
|
||||
def modify_project(self, project_id, manager_uid=None, description=None):
|
||||
"""Modify an existing project"""
|
||||
if not manager_uid and not description:
|
||||
return
|
||||
attr = []
|
||||
if manager_uid:
|
||||
if not self.__user_exists(manager_uid):
|
||||
raise exception.LDAPUserNotFound(user_id=manager_uid)
|
||||
manager_dn = self.__uid_to_dn(manager_uid)
|
||||
attr.append((self.ldap.MOD_REPLACE, LdapDriver.project_attribute,
|
||||
manager_dn))
|
||||
if description:
|
||||
attr.append((self.ldap.MOD_REPLACE, 'description', description))
|
||||
dn = self.__project_to_dn(project_id)
|
||||
self.conn.modify_s(dn, attr)
|
||||
if not self.is_in_project(manager_uid, project_id):
|
||||
self.add_to_project(manager_uid, project_id)
|
||||
|
||||
@sanitize
|
||||
def add_to_project(self, uid, project_id):
|
||||
"""Add user to project"""
|
||||
dn = self.__project_to_dn(project_id)
|
||||
return self.__add_to_group(uid, dn)
|
||||
|
||||
@sanitize
|
||||
def remove_from_project(self, uid, project_id):
|
||||
"""Remove user from project"""
|
||||
dn = self.__project_to_dn(project_id)
|
||||
return self.__remove_from_group(uid, dn)
|
||||
|
||||
@sanitize
|
||||
def is_in_project(self, uid, project_id):
|
||||
"""Check if user is in project"""
|
||||
dn = self.__project_to_dn(project_id)
|
||||
return self.__is_in_group(uid, dn)
|
||||
|
||||
@sanitize
|
||||
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)
|
||||
|
||||
@sanitize
|
||||
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
|
||||
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)
|
||||
|
||||
@sanitize
|
||||
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)
|
||||
|
||||
@sanitize
|
||||
def get_user_roles(self, uid, project_id=None):
|
||||
"""Retrieve list of roles for user (or user and project)"""
|
||||
if project_id is None:
|
||||
# NOTE(vish): This is unneccesarily slow, but since we can't
|
||||
# guarantee that the global roles are located
|
||||
# together in the ldap tree, we're doing this version.
|
||||
roles = []
|
||||
for role in FLAGS.allowed_roles:
|
||||
role_dn = self.__role_to_dn(role)
|
||||
if self.__is_in_group(uid, role_dn):
|
||||
roles.append(role)
|
||||
return roles
|
||||
else:
|
||||
project_dn = self.__project_to_dn(project_id)
|
||||
query = ('(&(&(objectclass=groupOfNames)(!%s))(member=%s))' %
|
||||
(LdapDriver.project_pattern, self.__uid_to_dn(uid)))
|
||||
roles = self.__find_objects(project_dn, query)
|
||||
return [role['cn'][0] for role in roles]
|
||||
|
||||
@sanitize
|
||||
def delete_user(self, uid):
|
||||
"""Delete a user"""
|
||||
if not self.__user_exists(uid):
|
||||
raise exception.LDAPUserNotFound(user_id=uid)
|
||||
self.__remove_from_all(uid)
|
||||
if FLAGS.ldap_user_modify_only:
|
||||
# Delete attributes
|
||||
attr = []
|
||||
# Retrieve user by name
|
||||
user = self.__get_ldap_user(uid)
|
||||
if 'secretKey' in user.keys():
|
||||
attr.append((self.ldap.MOD_DELETE, 'secretKey',
|
||||
user['secretKey']))
|
||||
if 'accessKey' in user.keys():
|
||||
attr.append((self.ldap.MOD_DELETE, 'accessKey',
|
||||
user['accessKey']))
|
||||
if LdapDriver.isadmin_attribute in user.keys():
|
||||
attr.append((self.ldap.MOD_DELETE,
|
||||
LdapDriver.isadmin_attribute,
|
||||
user[LdapDriver.isadmin_attribute]))
|
||||
self.conn.modify_s(self.__uid_to_dn(uid), attr)
|
||||
else:
|
||||
# Delete entry
|
||||
self.conn.delete_s(self.__uid_to_dn(uid))
|
||||
|
||||
@sanitize
|
||||
def delete_project(self, project_id):
|
||||
"""Delete a project"""
|
||||
project_dn = self.__project_to_dn(project_id)
|
||||
self.__delete_roles(project_dn)
|
||||
self.__delete_group(project_dn)
|
||||
|
||||
@sanitize
|
||||
def modify_user(self, uid, access_key=None, secret_key=None, admin=None):
|
||||
"""Modify an existing user"""
|
||||
if not access_key and not secret_key and admin is None:
|
||||
return
|
||||
attr = []
|
||||
if access_key:
|
||||
attr.append((self.ldap.MOD_REPLACE, 'accessKey', access_key))
|
||||
if secret_key:
|
||||
attr.append((self.ldap.MOD_REPLACE, 'secretKey', secret_key))
|
||||
if admin is not None:
|
||||
attr.append((self.ldap.MOD_REPLACE, LdapDriver.isadmin_attribute,
|
||||
str(admin).upper()))
|
||||
self.conn.modify_s(self.__uid_to_dn(uid), attr)
|
||||
|
||||
def __user_exists(self, uid):
|
||||
"""Check if user exists"""
|
||||
try:
|
||||
return self.get_user(uid) is not None
|
||||
except exception.LDAPUserNotFound:
|
||||
return False
|
||||
|
||||
def __ldap_user_exists(self, uid):
|
||||
"""Check if the user exists in ldap"""
|
||||
return self.__get_ldap_user(uid) is not None
|
||||
|
||||
def __project_exists(self, project_id):
|
||||
"""Check if project exists"""
|
||||
return self.get_project(project_id) is not None
|
||||
|
||||
@__local_cache('uid_attrs-%s')
|
||||
def __get_ldap_user(self, uid):
|
||||
"""Retrieve LDAP user entry by id"""
|
||||
dn = FLAGS.ldap_user_subtree
|
||||
query = ('(&(%s=%s)(objectclass=novaUser))' %
|
||||
(FLAGS.ldap_user_id_attribute, uid))
|
||||
return self.__find_object(dn, query)
|
||||
|
||||
def __find_object(self, dn, query=None, scope=None):
|
||||
"""Find an object by dn and query"""
|
||||
objects = self.__find_objects(dn, query, scope)
|
||||
if len(objects) == 0:
|
||||
return None
|
||||
return objects[0]
|
||||
|
||||
def __find_dns(self, dn, query=None, scope=None):
|
||||
"""Find dns by query"""
|
||||
if scope is None:
|
||||
# One of the flags is 0!
|
||||
scope = self.ldap.SCOPE_SUBTREE
|
||||
try:
|
||||
res = self.conn.search_s(dn, scope, query)
|
||||
except self.ldap.NO_SUCH_OBJECT:
|
||||
return []
|
||||
# Just return the DNs
|
||||
return [dn for dn, _attributes in res]
|
||||
|
||||
def __find_objects(self, dn, query=None, scope=None):
|
||||
"""Find objects by query"""
|
||||
if scope is None:
|
||||
# One of the flags is 0!
|
||||
scope = self.ldap.SCOPE_SUBTREE
|
||||
if query is None:
|
||||
query = "(objectClass=*)"
|
||||
try:
|
||||
res = self.conn.search_s(dn, scope, query)
|
||||
except self.ldap.NO_SUCH_OBJECT:
|
||||
return []
|
||||
# Just return the attributes
|
||||
# FIXME(yorik-sar): Whole driver should be refactored to
|
||||
# prevent this hack
|
||||
res1 = []
|
||||
for dn, attrs in res:
|
||||
attrs['dn'] = [dn]
|
||||
res1.append(attrs)
|
||||
return res1
|
||||
|
||||
def __find_role_dns(self, tree):
|
||||
"""Find dns of role objects in given tree"""
|
||||
query = ('(&(objectclass=groupOfNames)(!%s))' %
|
||||
LdapDriver.project_pattern)
|
||||
return self.__find_dns(tree, query)
|
||||
|
||||
def __find_group_dns_with_member(self, tree, uid):
|
||||
"""Find dns of group objects in a given tree that contain member"""
|
||||
query = ('(&(objectclass=groupOfNames)(member=%s))' %
|
||||
self.__uid_to_dn(uid))
|
||||
dns = self.__find_dns(tree, query)
|
||||
return dns
|
||||
|
||||
def __group_exists(self, dn):
|
||||
"""Check if group exists"""
|
||||
query = '(objectclass=groupOfNames)'
|
||||
return self.__find_object(dn, query) is not None
|
||||
|
||||
def __role_to_dn(self, role, project_id=None):
|
||||
"""Convert role to corresponding dn"""
|
||||
if project_id is None:
|
||||
return FLAGS["ldap_%s" % role]
|
||||
else:
|
||||
project_dn = self.__project_to_dn(project_id)
|
||||
return 'cn=%s,%s' % (role, project_dn)
|
||||
|
||||
def __create_group(self, group_dn, name, uid,
|
||||
description, member_uids=None):
|
||||
"""Create a group"""
|
||||
if self.__group_exists(group_dn):
|
||||
raise exception.LDAPGroupExists(group=name)
|
||||
members = []
|
||||
if member_uids is not None:
|
||||
for member_uid in member_uids:
|
||||
if not self.__user_exists(member_uid):
|
||||
raise exception.LDAPUserNotFound(user_id=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):
|
||||
"""Check if user is in group"""
|
||||
if not self.__user_exists(uid):
|
||||
raise exception.LDAPUserNotFound(user_id=uid)
|
||||
if not self.__group_exists(group_dn):
|
||||
return False
|
||||
res = self.__find_object(group_dn,
|
||||
'(member=%s)' % self.__uid_to_dn(uid),
|
||||
self.ldap.SCOPE_BASE)
|
||||
return res is not None
|
||||
|
||||
def __add_to_group(self, uid, group_dn):
|
||||
"""Add user to group"""
|
||||
if not self.__user_exists(uid):
|
||||
raise exception.LDAPUserNotFound(user_id=uid)
|
||||
if not self.__group_exists(group_dn):
|
||||
raise exception.LDAPGroupNotFound(group_id=group_dn)
|
||||
if self.__is_in_group(uid, group_dn):
|
||||
raise exception.LDAPMembershipExists(uid=uid, group_dn=group_dn)
|
||||
attr = [(self.ldap.MOD_ADD, 'member', self.__uid_to_dn(uid))]
|
||||
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.LDAPGroupNotFound(group_id=group_dn)
|
||||
if not self.__user_exists(uid):
|
||||
raise exception.LDAPUserNotFound(user_id=uid)
|
||||
if not self.__is_in_group(uid, group_dn):
|
||||
raise exception.LDAPGroupMembershipNotFound(user_id=uid,
|
||||
group_id=group_dn)
|
||||
# NOTE(vish): remove user from group and any sub_groups
|
||||
sub_dns = self.__find_group_dns_with_member(group_dn, uid)
|
||||
for sub_dn in sub_dns:
|
||||
self.__safe_remove_from_group(uid, sub_dn)
|
||||
|
||||
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))]
|
||||
try:
|
||||
self.conn.modify_s(group_dn, attr)
|
||||
except self.ldap.OBJECT_CLASS_VIOLATION:
|
||||
LOG.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):
|
||||
"""Remove user from all roles and projects"""
|
||||
if not self.__user_exists(uid):
|
||||
raise exception.LDAPUserNotFound(user_id=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(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(uid, project_dn)
|
||||
|
||||
def __delete_group(self, group_dn):
|
||||
"""Delete Group"""
|
||||
if not self.__group_exists(group_dn):
|
||||
raise exception.LDAPGroupNotFound(group_id=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_project(self, attr):
|
||||
"""Convert ldap attributes to Project object"""
|
||||
if attr is None:
|
||||
return None
|
||||
member_dns = attr.get('member', [])
|
||||
return {
|
||||
'id': attr['cn'][0],
|
||||
'name': attr['cn'][0],
|
||||
'project_manager_id':
|
||||
self.__dn_to_uid(attr[LdapDriver.project_attribute][0]),
|
||||
'description': attr.get('description', [None])[0],
|
||||
'member_ids': [self.__dn_to_uid(x) for x in member_dns]}
|
||||
|
||||
@__local_cache('uid_dn-%s')
|
||||
def __uid_to_dn(self, uid, search=True):
|
||||
"""Convert uid to dn"""
|
||||
# By default return a generated DN
|
||||
userdn = (FLAGS.ldap_user_id_attribute + '=%s,%s'
|
||||
% (uid, FLAGS.ldap_user_subtree))
|
||||
if search:
|
||||
query = ('%s=%s' % (FLAGS.ldap_user_id_attribute, uid))
|
||||
user = self.__find_dns(FLAGS.ldap_user_subtree, query)
|
||||
if len(user) > 0:
|
||||
userdn = user[0]
|
||||
return userdn
|
||||
|
||||
@__local_cache('pid_dn-%s')
|
||||
def __project_to_dn(self, pid, search=True):
|
||||
"""Convert pid to dn"""
|
||||
# By default return a generated DN
|
||||
projectdn = ('cn=%s,%s' % (pid, FLAGS.ldap_project_subtree))
|
||||
if search:
|
||||
query = ('(&(cn=%s)%s)' % (pid, LdapDriver.project_pattern))
|
||||
project = self.__find_dns(FLAGS.ldap_project_subtree, query)
|
||||
if len(project) > 0:
|
||||
projectdn = project[0]
|
||||
return projectdn
|
||||
|
||||
@staticmethod
|
||||
def __to_user(attr):
|
||||
"""Convert ldap attributes to User object"""
|
||||
if attr is None:
|
||||
return None
|
||||
if ('accessKey' in attr.keys() and 'secretKey' in attr.keys() and
|
||||
LdapDriver.isadmin_attribute in attr.keys()):
|
||||
return {
|
||||
'id': attr[FLAGS.ldap_user_id_attribute][0],
|
||||
'name': attr[FLAGS.ldap_user_name_attribute][0],
|
||||
'access': attr['accessKey'][0],
|
||||
'secret': attr['secretKey'][0],
|
||||
'admin': (attr[LdapDriver.isadmin_attribute][0] == 'TRUE')}
|
||||
else:
|
||||
return None
|
||||
|
||||
@__local_cache('dn_uid-%s')
|
||||
def __dn_to_uid(self, dn):
|
||||
"""Convert user dn to uid"""
|
||||
query = '(objectclass=novaUser)'
|
||||
user = self.__find_object(dn, query, scope=self.ldap.SCOPE_BASE)
|
||||
return user[FLAGS.ldap_user_id_attribute][0]
|
||||
|
||||
|
||||
class FakeLdapDriver(LdapDriver):
|
||||
"""Fake Ldap Auth driver"""
|
||||
|
||||
def __init__(self):
|
||||
import nova.auth.fakeldap
|
||||
sys.modules['ldap'] = nova.auth.fakeldap
|
||||
super(FakeLdapDriver, self).__init__()
|
@ -1,857 +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.
|
||||
|
||||
"""
|
||||
WARNING: This code is deprecated and will be removed.
|
||||
Keystone is the recommended solution for auth management.
|
||||
|
||||
Nova authentication management
|
||||
"""
|
||||
|
||||
import os
|
||||
import string # pylint: disable=W0402
|
||||
import uuid
|
||||
import zipfile
|
||||
|
||||
from nova.auth import signer
|
||||
from nova import context
|
||||
from nova import crypto
|
||||
from nova import db
|
||||
from nova import exception
|
||||
from nova import flags
|
||||
from nova.openstack.common import cfg
|
||||
from nova.openstack.common import importutils
|
||||
from nova.openstack.common import log as logging
|
||||
from nova import utils
|
||||
|
||||
|
||||
auth_opts = [
|
||||
cfg.ListOpt('allowed_roles',
|
||||
default=[
|
||||
'cloudadmin',
|
||||
'itsec',
|
||||
'sysadmin',
|
||||
'netadmin',
|
||||
'developer'
|
||||
],
|
||||
help='Allowed roles for project'),
|
||||
|
||||
# NOTE(vish): a user with one of these roles will be a superuser and
|
||||
# have access to all api commands
|
||||
cfg.ListOpt('superuser_roles',
|
||||
default=['cloudadmin'],
|
||||
help='Roles that ignore authorization 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
|
||||
cfg.ListOpt('global_roles',
|
||||
default=['cloudadmin', 'itsec'],
|
||||
help='Roles that apply to all projects'),
|
||||
|
||||
cfg.StrOpt('credentials_template',
|
||||
default='$pybasedir/nova/auth/novarc.template',
|
||||
help='Template for creating users rc file'),
|
||||
cfg.StrOpt('vpn_client_template',
|
||||
default='$pybasedir/nova/cloudpipe/client.ovpn.template',
|
||||
help='Template for creating users vpn file'),
|
||||
cfg.StrOpt('credential_vpn_file',
|
||||
default='nova-vpn.conf',
|
||||
help='Filename of certificate in credentials zip'),
|
||||
cfg.StrOpt('credential_key_file',
|
||||
default='pk.pem',
|
||||
help='Filename of private key in credentials zip'),
|
||||
cfg.StrOpt('credential_cert_file',
|
||||
default='cert.pem',
|
||||
help='Filename of certificate in credentials zip'),
|
||||
cfg.StrOpt('credential_rc_file',
|
||||
default='%src',
|
||||
help='Filename of rc in credentials zip %s will be replaced by '
|
||||
'name of the region (nova by default)'),
|
||||
cfg.StrOpt('auth_driver',
|
||||
default='nova.auth.dbdriver.DbDriver',
|
||||
help='Driver that auth manager uses'),
|
||||
]
|
||||
|
||||
FLAGS = flags.FLAGS
|
||||
FLAGS.register_opts(auth_opts)
|
||||
|
||||
flags.DECLARE('osapi_compute_listen_port', 'nova.service')
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
if FLAGS.memcached_servers:
|
||||
import memcache
|
||||
else:
|
||||
from nova.common import memorycache as memcache
|
||||
|
||||
|
||||
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):
|
||||
"""Safely 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 parameters.
|
||||
"""
|
||||
if isinstance(obj, cls):
|
||||
return obj.id
|
||||
else:
|
||||
return obj
|
||||
|
||||
|
||||
class User(AuthBase):
|
||||
"""Object representing a user
|
||||
|
||||
The following attributes are defined:
|
||||
|
||||
``id``
|
||||
A system identifier for the user. A string (for LDAP)
|
||||
``name``
|
||||
The user name, potentially in some more friendly format
|
||||
``access``
|
||||
The 'username' for EC2 authentication
|
||||
``secret``
|
||||
The 'password' for EC2 authenticatoin
|
||||
``admin``
|
||||
???
|
||||
"""
|
||||
|
||||
def __init__(self, id, name, access, secret, admin):
|
||||
AuthBase.__init__(self)
|
||||
assert isinstance(id, basestring)
|
||||
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 __repr__(self):
|
||||
return "User('%s', '%s')" % (self.id, self.name)
|
||||
|
||||
|
||||
class Project(AuthBase):
|
||||
"""Represents a Project returned from the datastore"""
|
||||
|
||||
def __init__(self, id, name, project_manager_id, description, member_ids):
|
||||
AuthBase.__init__(self)
|
||||
self.id = id
|
||||
self.name = name
|
||||
self.project_manager_id = project_manager_id
|
||||
self.description = description
|
||||
self.member_ids = member_ids
|
||||
|
||||
@property
|
||||
def project_manager(self):
|
||||
return AuthManager().get_user(self.project_manager_id)
|
||||
|
||||
@property
|
||||
def vpn_ip(self):
|
||||
ip, _port = AuthManager().get_project_vpn_data(self)
|
||||
return ip
|
||||
|
||||
@property
|
||||
def 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)
|
||||
|
||||
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')" % (self.id, self.name)
|
||||
|
||||
|
||||
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 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
|
||||
mc = None
|
||||
|
||||
def __new__(cls, *args, **kwargs):
|
||||
"""Returns the AuthManager singleton"""
|
||||
if not cls._instance or ('new' in kwargs and kwargs['new']):
|
||||
cls._instance = super(AuthManager, cls).__new__(cls)
|
||||
return cls._instance
|
||||
|
||||
def __init__(self, driver=None, *args, **kwargs):
|
||||
"""Inits the driver from parameter or flag
|
||||
|
||||
__init__ is run every time AuthManager() is called, so we only
|
||||
reset the driver if it is not set or a new driver is specified.
|
||||
"""
|
||||
self.network_manager = importutils.import_object(FLAGS.network_manager)
|
||||
if driver or not getattr(self, 'driver', None):
|
||||
self.driver = importutils.import_class(driver or FLAGS.auth_driver)
|
||||
if AuthManager.mc is None:
|
||||
AuthManager.mc = memcache.Client(FLAGS.memcached_servers, debug=0)
|
||||
|
||||
def authenticate(self, access, signature, params, verb='GET',
|
||||
server_string='127.0.0.1:8773', path='/',
|
||||
check_type='ec2', headers=None):
|
||||
"""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 check_type: str
|
||||
:param check_type: Type of signature to check. 'ec2' for EC2, 's3' for
|
||||
S3. Any other value will cause signature not to be
|
||||
checked.
|
||||
|
||||
:type headers: list
|
||||
:param headers: HTTP headers passed with the request (only needed for
|
||||
s3 signature checks)
|
||||
|
||||
:rtype: tuple (User, Project)
|
||||
:return: User and project that the request represents.
|
||||
"""
|
||||
# TODO(vish): check for valid timestamp
|
||||
(access_key, _sep, project_id) = access.partition(':')
|
||||
|
||||
LOG.debug(_('Looking up user: %r'), access_key)
|
||||
user = self.get_user_from_access_key(access_key)
|
||||
LOG.debug('user: %r', user)
|
||||
if user is None:
|
||||
LOG.audit(_("Failed authorization for access key %s"), access_key)
|
||||
raise exception.AccessKeyNotFound(access_key=access_key)
|
||||
|
||||
# NOTE(vish): if we stop using project name as id we need better
|
||||
# logic to find a default project for user
|
||||
if project_id == '':
|
||||
LOG.debug(_("Using project name = user name (%s)"), user.name)
|
||||
project_id = user.name
|
||||
|
||||
project = self.get_project(project_id)
|
||||
if project is None:
|
||||
pjid = project_id
|
||||
uname = user.name
|
||||
LOG.audit(_("failed authorization: no project named %(pjid)s"
|
||||
" (user=%(uname)s)") % locals())
|
||||
raise exception.ProjectNotFound(project_id=project_id)
|
||||
if not self.is_admin(user) and not self.is_project_member(user,
|
||||
project):
|
||||
uname = user.name
|
||||
uid = user.id
|
||||
pjname = project.name
|
||||
pjid = project.id
|
||||
LOG.audit(_("Failed authorization: user %(uname)s not admin"
|
||||
" and not member of project %(pjname)s") % locals())
|
||||
raise exception.ProjectMembershipNotFound(project_id=pjid,
|
||||
user_id=uid)
|
||||
if check_type == 's3':
|
||||
sign = signer.Signer(user.secret.encode())
|
||||
expected_signature = sign.s3_authorization(headers, verb, path)
|
||||
LOG.debug(_('user.secret: %s'), user.secret)
|
||||
LOG.debug(_('expected_signature: %s'), expected_signature)
|
||||
LOG.debug(_('signature: %s'), signature)
|
||||
if not utils.strcmp_const_time(signature, expected_signature):
|
||||
LOG.audit(_("Invalid signature for user %s"), user.name)
|
||||
raise exception.InvalidSignature(signature=signature,
|
||||
user=user)
|
||||
elif check_type == 'ec2':
|
||||
# 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)
|
||||
LOG.debug(_('user.secret: %s'), user.secret)
|
||||
LOG.debug(_('expected_signature: %s'), expected_signature)
|
||||
LOG.debug(_('signature: %s'), signature)
|
||||
if not utils.strcmp_const_time(signature, expected_signature):
|
||||
(addr_str, port_str) = utils.parse_server_string(server_string)
|
||||
# If the given server_string contains port num, try without it.
|
||||
if port_str != '':
|
||||
host_only_signature = signer.Signer(
|
||||
user.secret.encode()).generate(params, verb,
|
||||
addr_str, path)
|
||||
LOG.debug(_('host_only_signature: %s'),
|
||||
host_only_signature)
|
||||
if utils.strcmp_const_time(signature, host_only_signature):
|
||||
return (user, project)
|
||||
LOG.audit(_("Invalid signature for user %s"), user.name)
|
||||
raise exception.InvalidSignature(signature=signature,
|
||||
user=user)
|
||||
return (user, project)
|
||||
|
||||
def get_access_key(self, user, project):
|
||||
"""Get an access key that includes user and project"""
|
||||
if not isinstance(user, User):
|
||||
user = self.get_user(user)
|
||||
return "%s:%s" % (user.access, Project.safe_id(project))
|
||||
|
||||
def is_superuser(self, user):
|
||||
"""Checks for superuser status, allowing user to bypass authorization
|
||||
|
||||
: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 _build_mc_key(self, user, role, project=None):
|
||||
key_parts = ['rolecache', User.safe_id(user), str(role)]
|
||||
if project:
|
||||
key_parts.append(Project.safe_id(project))
|
||||
return utils.utf8('-'.join(key_parts))
|
||||
|
||||
def _clear_mc_key(self, user, role, project=None):
|
||||
# NOTE(anthony): it would be better to delete the key
|
||||
self.mc.set(self._build_mc_key(user, role, project), None)
|
||||
|
||||
def _has_role(self, user, role, project=None):
|
||||
mc_key = self._build_mc_key(user, role, project)
|
||||
rslt = self.mc.get(mc_key)
|
||||
if rslt is None:
|
||||
with self.driver() as drv:
|
||||
rslt = drv.has_role(user, role, project)
|
||||
self.mc.set(mc_key, rslt)
|
||||
return rslt
|
||||
else:
|
||||
return rslt
|
||||
|
||||
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.
|
||||
"""
|
||||
if role == 'projectmanager':
|
||||
if not project:
|
||||
raise exception.NovaException(_("Must specify project"))
|
||||
return self.is_project_manager(user, project)
|
||||
|
||||
global_role = self._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 self._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.
|
||||
"""
|
||||
if role not in FLAGS.allowed_roles:
|
||||
raise exception.UserRoleNotFound(role_id=role)
|
||||
if project is not None and role in FLAGS.global_roles:
|
||||
raise exception.GlobalRoleNotAllowed(role_id=role)
|
||||
uid = User.safe_id(user)
|
||||
pid = Project.safe_id(project)
|
||||
if project:
|
||||
LOG.audit(_("Adding role %(role)s to user %(uid)s"
|
||||
" in project %(pid)s") % locals())
|
||||
else:
|
||||
LOG.audit(_("Adding sitewide role %(role)s to user %(uid)s")
|
||||
% locals())
|
||||
with self.driver() as drv:
|
||||
self._clear_mc_key(uid, role, pid)
|
||||
drv.add_role(uid, role, pid)
|
||||
|
||||
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.
|
||||
"""
|
||||
uid = User.safe_id(user)
|
||||
pid = Project.safe_id(project)
|
||||
if project:
|
||||
LOG.audit(_("Removing role %(role)s from user %(uid)s"
|
||||
" on project %(pid)s") % locals())
|
||||
else:
|
||||
LOG.audit(_("Removing sitewide role %(role)s"
|
||||
" from user %(uid)s") % locals())
|
||||
with self.driver() as drv:
|
||||
self._clear_mc_key(uid, role, pid)
|
||||
drv.remove_role(uid, role, pid)
|
||||
|
||||
@staticmethod
|
||||
def get_roles(project_roles=True):
|
||||
"""Get list of allowed roles"""
|
||||
if project_roles:
|
||||
return list(set(FLAGS.allowed_roles) - set(FLAGS.global_roles))
|
||||
else:
|
||||
return FLAGS.allowed_roles
|
||||
|
||||
def get_user_roles(self, user, project=None):
|
||||
"""Get user global or per-project roles"""
|
||||
with self.driver() as drv:
|
||||
return drv.get_user_roles(User.safe_id(user),
|
||||
Project.safe_id(project))
|
||||
|
||||
def get_active_roles(self, user, project=None):
|
||||
"""Get all active roles for context"""
|
||||
if project:
|
||||
roles = FLAGS.allowed_roles + ['projectmanager']
|
||||
else:
|
||||
roles = FLAGS.global_roles
|
||||
return [role for role in roles if self.has_role(user, role, project)]
|
||||
|
||||
def get_project(self, pid):
|
||||
"""Get project object by id"""
|
||||
with self.driver() as drv:
|
||||
project_dict = drv.get_project(pid)
|
||||
if project_dict:
|
||||
return Project(**project_dict)
|
||||
|
||||
def get_projects(self, user=None):
|
||||
"""Retrieves list of projects, optionally filtered by user"""
|
||||
with self.driver() as drv:
|
||||
project_list = drv.get_projects(User.safe_id(user))
|
||||
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
|
||||
|
||||
: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]
|
||||
with self.driver() as drv:
|
||||
project_dict = drv.create_project(name,
|
||||
User.safe_id(manager_user),
|
||||
description,
|
||||
member_users)
|
||||
if project_dict:
|
||||
LOG.audit(_("Created project %(name)s with"
|
||||
" manager %(manager_user)s") % locals())
|
||||
project = Project(**project_dict)
|
||||
return project
|
||||
|
||||
def modify_project(self, project, manager_user=None, description=None):
|
||||
"""Modify a project
|
||||
|
||||
:type name: Project or project_id
|
||||
:param project: The project to modify.
|
||||
|
||||
:type manager_user: User or uid
|
||||
:param manager_user: This user will be the new project manager.
|
||||
|
||||
:type description: str
|
||||
:param project: This will be the new description of the project.
|
||||
|
||||
"""
|
||||
LOG.audit(_("modifying project %s"), Project.safe_id(project))
|
||||
if manager_user:
|
||||
manager_user = User.safe_id(manager_user)
|
||||
with self.driver() as drv:
|
||||
drv.modify_project(Project.safe_id(project),
|
||||
manager_user,
|
||||
description)
|
||||
|
||||
def add_to_project(self, user, project):
|
||||
"""Add user to project"""
|
||||
uid = User.safe_id(user)
|
||||
pid = Project.safe_id(project)
|
||||
LOG.audit(_("Adding user %(uid)s to project %(pid)s") % locals())
|
||||
with self.driver() 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"""
|
||||
uid = User.safe_id(user)
|
||||
pid = Project.safe_id(project)
|
||||
LOG.audit(_("Remove user %(uid)s from project %(pid)s") % locals())
|
||||
with self.driver() as drv:
|
||||
return drv.remove_from_project(uid, pid)
|
||||
|
||||
@staticmethod
|
||||
def get_project_vpn_data(project):
|
||||
"""Gets vpn ip and port for project
|
||||
|
||||
: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.
|
||||
"""
|
||||
|
||||
networks = db.project_get_networks(context.get_admin_context(),
|
||||
Project.safe_id(project), False)
|
||||
if not networks:
|
||||
return (None, None)
|
||||
|
||||
# TODO(tr3buchet): not sure what you guys plan on doing with this
|
||||
# but it's possible for a project to have multiple sets of vpn data
|
||||
# for now I'm just returning the first one
|
||||
network = networks[0]
|
||||
return (network['vpn_public_address'],
|
||||
network['vpn_public_port'])
|
||||
|
||||
def delete_project(self, project):
|
||||
"""Deletes a project"""
|
||||
LOG.audit(_("Deleting project %s"), Project.safe_id(project))
|
||||
with self.driver() as drv:
|
||||
drv.delete_project(Project.safe_id(project))
|
||||
|
||||
def get_user(self, uid):
|
||||
"""Retrieves a user by id"""
|
||||
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() 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() as drv:
|
||||
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
|
||||
|
||||
:type name: str
|
||||
:param name: Name of the user to create.
|
||||
|
||||
: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 specified 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 is None:
|
||||
access = str(uuid.uuid4())
|
||||
if secret is None:
|
||||
secret = str(uuid.uuid4())
|
||||
with self.driver() as drv:
|
||||
user_dict = drv.create_user(name, access, secret, admin)
|
||||
if user_dict:
|
||||
rv = User(**user_dict)
|
||||
rvname = rv.name
|
||||
rvadmin = rv.admin
|
||||
LOG.audit(_("Created user %(rvname)s"
|
||||
" (admin: %(rvadmin)r)") % locals())
|
||||
return rv
|
||||
|
||||
def delete_user(self, user):
|
||||
"""Deletes a user
|
||||
|
||||
Additionally deletes all users key_pairs"""
|
||||
uid = User.safe_id(user)
|
||||
LOG.audit(_("Deleting user %s"), uid)
|
||||
db.key_pair_destroy_all_by_user(context.get_admin_context(),
|
||||
uid)
|
||||
with self.driver() as drv:
|
||||
drv.delete_user(uid)
|
||||
|
||||
def modify_user(self, user, access_key=None, secret_key=None, admin=None):
|
||||
"""Modify credentials for a user"""
|
||||
uid = User.safe_id(user)
|
||||
if access_key:
|
||||
LOG.audit(_("Access Key change for user %s"), uid)
|
||||
if secret_key:
|
||||
LOG.audit(_("Secret Key change for user %s"), uid)
|
||||
if admin is not None:
|
||||
LOG.audit(_("Admin status set to %(admin)r"
|
||||
" for user %(uid)s") % locals())
|
||||
with self.driver() as drv:
|
||||
drv.modify_user(uid, access_key, secret_key, admin)
|
||||
|
||||
def get_credentials(self, user, project=None, use_dmz=True):
|
||||
"""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)
|
||||
private_key, signed_cert = crypto.generate_x509_cert(user.id, pid)
|
||||
|
||||
with utils.tempdir() as tmpdir:
|
||||
zf = os.path.join(tmpdir, "temp.zip")
|
||||
zippy = zipfile.ZipFile(zf, 'w')
|
||||
if use_dmz and FLAGS.region_list:
|
||||
regions = {}
|
||||
for item in FLAGS.region_list:
|
||||
region, _sep, region_host = item.partition("=")
|
||||
regions[region] = region_host
|
||||
else:
|
||||
regions = {'nova': FLAGS.ec2_host}
|
||||
for region, host in regions.iteritems():
|
||||
rc = self.__generate_rc(user,
|
||||
pid,
|
||||
use_dmz,
|
||||
host)
|
||||
zippy.writestr(FLAGS.credential_rc_file % region, rc)
|
||||
|
||||
zippy.writestr(FLAGS.credential_key_file, private_key)
|
||||
zippy.writestr(FLAGS.credential_cert_file, signed_cert)
|
||||
|
||||
(vpn_ip, vpn_port) = self.get_project_vpn_data(project)
|
||||
if vpn_ip:
|
||||
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)
|
||||
zippy.writestr(FLAGS.credential_vpn_file, config)
|
||||
else:
|
||||
LOG.warn(_("No vpn data for project %s"), pid)
|
||||
|
||||
zippy.writestr(FLAGS.ca_file, crypto.fetch_ca(pid))
|
||||
zippy.close()
|
||||
with open(zf, 'rb') as f:
|
||||
read_buffer = f.read()
|
||||
|
||||
return read_buffer
|
||||
|
||||
def get_environment_rc(self, user, project=None, use_dmz=True):
|
||||
"""Get environment rc 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)
|
||||
return self.__generate_rc(user, pid, use_dmz)
|
||||
|
||||
@staticmethod
|
||||
def __generate_rc(user, pid, use_dmz=True, host=None):
|
||||
"""Generate rc file for user"""
|
||||
if use_dmz:
|
||||
ec2_host = FLAGS.ec2_dmz_host
|
||||
else:
|
||||
ec2_host = FLAGS.ec2_host
|
||||
# NOTE(vish): Always use the dmz since it is used from inside the
|
||||
# instance
|
||||
s3_host = FLAGS.s3_dmz
|
||||
if host:
|
||||
s3_host = host
|
||||
ec2_host = host
|
||||
rc = open(FLAGS.credentials_template).read()
|
||||
# NOTE(vish): Deprecated auth uses an access key, no auth uses a
|
||||
# the user_id in place of it.
|
||||
if FLAGS.auth_strategy == 'deprecated':
|
||||
access = user.access
|
||||
else:
|
||||
access = user.id
|
||||
rc = rc % {'access': access,
|
||||
'project': pid,
|
||||
'secret': user.secret,
|
||||
'ec2': '%s://%s:%s%s' % (FLAGS.ec2_scheme,
|
||||
ec2_host,
|
||||
FLAGS.ec2_port,
|
||||
FLAGS.ec2_path),
|
||||
's3': 'http://%s:%s' % (s3_host, FLAGS.s3_port),
|
||||
'os': '%s://%s:%s%s' % (FLAGS.osapi_scheme,
|
||||
ec2_host,
|
||||
FLAGS.osapi_compute_listen_port,
|
||||
FLAGS.osapi_path),
|
||||
'user': user.name,
|
||||
'nova': FLAGS.ca_file,
|
||||
'cert': FLAGS.credential_cert_file,
|
||||
'key': FLAGS.credential_key_file}
|
||||
return rc
|
@ -1,50 +0,0 @@
|
||||
#
|
||||
# Person object for Nova
|
||||
# inetorgperson with extra attributes
|
||||
# Schema version: 2
|
||||
# Authors: Vishvananda Ishaya <vishvananda@gmail.com>
|
||||
# Ryan Lane <rlane@wikimedia.org>
|
||||
#
|
||||
#
|
||||
|
||||
# using internet experimental oid arc as per BP64 3.1
|
||||
objectidentifier novaSchema 1.3.6.1.3.1.666.666
|
||||
objectidentifier novaAttrs novaSchema:3
|
||||
objectidentifier novaOCs novaSchema:4
|
||||
|
||||
attributetype (
|
||||
novaAttrs:1
|
||||
NAME 'accessKey'
|
||||
DESC 'Key for accessing data'
|
||||
EQUALITY caseIgnoreMatch
|
||||
SUBSTR caseIgnoreSubstringsMatch
|
||||
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
|
||||
SINGLE-VALUE
|
||||
)
|
||||
|
||||
attributetype (
|
||||
novaAttrs:2
|
||||
NAME 'secretKey'
|
||||
DESC 'Secret key'
|
||||
EQUALITY caseIgnoreMatch
|
||||
SUBSTR caseIgnoreSubstringsMatch
|
||||
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
|
||||
SINGLE-VALUE
|
||||
)
|
||||
|
||||
attributetype (
|
||||
novaAttrs:4
|
||||
NAME 'isNovaAdmin'
|
||||
DESC 'Is user a nova administrator?'
|
||||
EQUALITY booleanMatch
|
||||
SYNTAX 1.3.6.1.4.1.1466.115.121.1.7
|
||||
SINGLE-VALUE
|
||||
)
|
||||
|
||||
objectClass (
|
||||
novaOCs:1
|
||||
NAME 'novaUser'
|
||||
DESC 'access and secret keys'
|
||||
AUXILIARY
|
||||
MAY ( accessKey $ secretKey $ isNovaAdmin )
|
||||
)
|
@ -1,13 +0,0 @@
|
||||
#
|
||||
# Person object for Nova
|
||||
# inetorgperson with extra attributes
|
||||
# Schema version: 2
|
||||
# Authors: Vishvananda Ishaya <vishvananda@gmail.com>
|
||||
# Ryan Lane <rlane@wikimedia.org>
|
||||
#
|
||||
# using internet experimental oid arc as per BP64 3.1
|
||||
dn: cn=schema
|
||||
attributeTypes: ( 1.3.6.1.3.1.666.666.3.1 NAME 'accessKey' DESC 'Key for accessing data' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE )
|
||||
attributeTypes: ( 1.3.6.1.3.1.666.666.3.2 NAME 'secretKey' DESC 'Secret key' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE )
|
||||
attributeTypes: ( 1.3.6.1.3.1.666.666.3.4 NAME 'isNovaAdmin' DESC 'Is user a nova administrator?' EQUALITY booleanMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 SINGLE-VALUE )
|
||||
objectClasses: ( 1.3.6.1.3.1.666.666.4.1 NAME 'novaUser' DESC 'access and secret keys' SUP top AUXILIARY MAY ( accessKey $ secretKey $ isNovaAdmin ) )
|
@ -1,19 +0,0 @@
|
||||
NOVARC=$(readlink -f "${BASH_SOURCE:-${0}}" 2>/dev/null) ||
|
||||
NOVARC=$(python -c 'import os,sys; print os.path.abspath(os.path.realpath(sys.argv[1]))' "${BASH_SOURCE:-${0}}")
|
||||
NOVA_KEY_DIR=${NOVARC%%/*}
|
||||
export EC2_ACCESS_KEY="%(access)s:%(project)s"
|
||||
export EC2_SECRET_KEY="%(secret)s"
|
||||
export EC2_URL="%(ec2)s"
|
||||
export S3_URL="%(s3)s"
|
||||
export EC2_USER_ID=42 # nova does not use user id, but bundling requires it
|
||||
export EC2_PRIVATE_KEY=${NOVA_KEY_DIR}/%(key)s
|
||||
export EC2_CERT=${NOVA_KEY_DIR}/%(cert)s
|
||||
export NOVA_CERT=${NOVA_KEY_DIR}/%(nova)s
|
||||
export EUCALYPTUS_CERT=${NOVA_CERT} # euca-bundle-image seems to require this set
|
||||
alias ec2-bundle-image="ec2-bundle-image --cert ${EC2_CERT} --privatekey ${EC2_PRIVATE_KEY} --user 42 --ec2cert ${NOVA_CERT}"
|
||||
alias ec2-upload-bundle="ec2-upload-bundle -a ${EC2_ACCESS_KEY} -s ${EC2_SECRET_KEY} --url ${S3_URL} --ec2cert ${NOVA_CERT}"
|
||||
export NOVA_API_KEY="%(access)s"
|
||||
export NOVA_USERNAME="%(user)s"
|
||||
export NOVA_PROJECT_ID="%(project)s"
|
||||
export NOVA_URL="%(os)s"
|
||||
export NOVA_VERSION="1.1"
|
@ -1,118 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
# 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.
|
||||
# LDAP INSTALL SCRIPT - IS IDEMPOTENT, does not scrub users
|
||||
|
||||
apt-get install -y ldap-utils python-ldap openjdk-6-jre
|
||||
|
||||
if [ ! -d "/usr/opendj" ]
|
||||
then
|
||||
# TODO(rlane): Wikimedia Foundation is the current package maintainer.
|
||||
# After the package is included in Ubuntu's channel, change this.
|
||||
wget http://apt.wikimedia.org/wikimedia/pool/main/o/opendj/opendj_2.4.0-7_amd64.deb
|
||||
dpkg -i opendj_2.4.0-7_amd64.deb
|
||||
fi
|
||||
|
||||
abspath=`dirname "$(cd "${0%/*}" 2>/dev/null; echo "$PWD"/"${0##*/}")"`
|
||||
schemapath='/var/opendj/instance/config/schema'
|
||||
cp $abspath/openssh-lpk_sun.schema $schemapath/97-openssh-lpk_sun.ldif
|
||||
cp $abspath/nova_sun.schema $schemapath/98-nova_sun.ldif
|
||||
chown opendj:opendj $schemapath/98-nova_sun.ldif
|
||||
|
||||
cat >/etc/ldap/ldap.conf <<LDAP_CONF_EOF
|
||||
# LDAP Client Settings
|
||||
URI ldap://localhost
|
||||
BASE dc=example,dc=com
|
||||
BINDDN cn=Directory Manager
|
||||
SIZELIMIT 0
|
||||
TIMELIMIT 0
|
||||
LDAP_CONF_EOF
|
||||
|
||||
cat >/etc/ldap/base.ldif <<BASE_LDIF_EOF
|
||||
# This is the root of the directory tree
|
||||
dn: dc=example,dc=com
|
||||
description: Example.Com, your trusted non-existent corporation.
|
||||
dc: example
|
||||
o: Example.Com
|
||||
objectClass: top
|
||||
objectClass: dcObject
|
||||
objectClass: organization
|
||||
|
||||
# Subtree for users
|
||||
dn: ou=Users,dc=example,dc=com
|
||||
ou: Users
|
||||
description: Users
|
||||
objectClass: organizationalUnit
|
||||
|
||||
# Subtree for groups
|
||||
dn: ou=Groups,dc=example,dc=com
|
||||
ou: Groups
|
||||
description: Groups
|
||||
objectClass: organizationalUnit
|
||||
|
||||
# Subtree for system accounts
|
||||
dn: ou=System,dc=example,dc=com
|
||||
ou: System
|
||||
description: Special accounts used by software applications.
|
||||
objectClass: organizationalUnit
|
||||
|
||||
# Special Account for Authentication:
|
||||
dn: uid=authenticate,ou=System,dc=example,dc=com
|
||||
uid: authenticate
|
||||
ou: System
|
||||
description: Special account for authenticating users
|
||||
userPassword: {MD5}TLnIqASP0CKUR3/LGkEZGg==
|
||||
objectClass: account
|
||||
objectClass: simpleSecurityObject
|
||||
|
||||
# create the sysadmin entry
|
||||
|
||||
dn: cn=developers,ou=Groups,dc=example,dc=com
|
||||
objectclass: groupOfNames
|
||||
cn: developers
|
||||
description: IT admin group
|
||||
member: uid=admin,ou=Users,dc=example,dc=com
|
||||
|
||||
dn: cn=sysadmins,ou=Groups,dc=example,dc=com
|
||||
objectclass: groupOfNames
|
||||
cn: sysadmins
|
||||
description: IT admin group
|
||||
member: uid=admin,ou=Users,dc=example,dc=com
|
||||
|
||||
dn: cn=netadmins,ou=Groups,dc=example,dc=com
|
||||
objectclass: groupOfNames
|
||||
cn: netadmins
|
||||
description: Network admin group
|
||||
member: uid=admin,ou=Users,dc=example,dc=com
|
||||
|
||||
dn: cn=cloudadmins,ou=Groups,dc=example,dc=com
|
||||
objectclass: groupOfNames
|
||||
cn: cloudadmins
|
||||
description: Cloud admin group
|
||||
member: uid=admin,ou=Users,dc=example,dc=com
|
||||
|
||||
dn: cn=itsec,ou=Groups,dc=example,dc=com
|
||||
objectclass: groupOfNames
|
||||
cn: itsec
|
||||
description: IT security users group
|
||||
member: uid=admin,ou=Users,dc=example,dc=com
|
||||
BASE_LDIF_EOF
|
||||
|
||||
/etc/init.d/opendj stop
|
||||
su - opendj -c '/usr/opendj/setup -i -b "dc=example,dc=com" -l /etc/ldap/base.ldif -S -w changeme -O -n --noPropertiesFile'
|
||||
/etc/init.d/opendj start
|
@ -1,19 +0,0 @@
|
||||
#
|
||||
# LDAP Public Key Patch schema for use with openssh-ldappubkey
|
||||
# Author: Eric AUGE <eau@phear.org>
|
||||
#
|
||||
# Based on the proposal of : Mark Ruijter
|
||||
#
|
||||
|
||||
|
||||
# octetString SYNTAX
|
||||
attributetype ( 1.3.6.1.4.1.24552.500.1.1.1.13 NAME 'sshPublicKey'
|
||||
DESC 'MANDATORY: OpenSSH Public key'
|
||||
EQUALITY octetStringMatch
|
||||
SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 )
|
||||
|
||||
# printableString SYNTAX yes|no
|
||||
objectclass ( 1.3.6.1.4.1.24552.500.1.1.2.0 NAME 'ldapPublicKey' SUP top AUXILIARY
|
||||
DESC 'MANDATORY: OpenSSH LPK objectclass'
|
||||
MAY ( sshPublicKey $ uid )
|
||||
)
|
@ -1,10 +0,0 @@
|
||||
#
|
||||
# LDAP Public Key Patch schema for use with openssh-ldappubkey
|
||||
# Author: Eric AUGE <eau@phear.org>
|
||||
#
|
||||
# Schema for Sun Directory Server.
|
||||
# Based on the original schema, modified by Stefan Fischer.
|
||||
#
|
||||
dn: cn=schema
|
||||
attributeTypes: ( 1.3.6.1.4.1.24552.500.1.1.1.13 NAME 'sshPublicKey' DESC 'MANDATORY: OpenSSH Public key' EQUALITY octetStringMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 )
|
||||
objectClasses: ( 1.3.6.1.4.1.24552.500.1.1.2.0 NAME 'ldapPublicKey' SUP top AUXILIARY DESC 'MANDATORY: OpenSSH LPK objectclass' MAY ( sshPublicKey $ uid ) )
|
@ -1,177 +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.
|
||||
#
|
||||
# PORTIONS OF THIS FILE ARE FROM:
|
||||
# http://code.google.com/p/boto
|
||||
# Copyright (c) 2006-2009 Mitch Garnaat http://garnaat.org/
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a
|
||||
# copy of this software and associated documentation files (the
|
||||
# "Software"), to deal in the Software without restriction, including
|
||||
# without limitation the rights to use, copy, modify, merge, publish, dis-
|
||||
# tribute, sublicense, and/or sell copies of the Software, and to permit
|
||||
# persons to whom the Software is furnished to do so, subject to the fol-
|
||||
# lowing conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included
|
||||
# in all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABIL-
|
||||
# ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
|
||||
# SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
# IN THE SOFTWARE.
|
||||
|
||||
"""
|
||||
Utility class for parsing signed AMI manifests.
|
||||
"""
|
||||
|
||||
import base64
|
||||
import hashlib
|
||||
import hmac
|
||||
import urllib
|
||||
|
||||
try:
|
||||
# NOTE(vish): for new boto
|
||||
import boto
|
||||
# for boto.utils
|
||||
import boto.provider
|
||||
# NOTE(vish): for old boto
|
||||
import boto.utils
|
||||
except ImportError:
|
||||
boto = None
|
||||
|
||||
from nova import exception
|
||||
from nova.openstack.common import log as logging
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Signer(object):
|
||||
"""Hacked up code from boto/connection.py"""
|
||||
|
||||
def __init__(self, secret_key):
|
||||
self.hmac = hmac.new(secret_key, digestmod=hashlib.sha1)
|
||||
if hashlib.sha256:
|
||||
self.hmac_256 = hmac.new(secret_key, digestmod=hashlib.sha256)
|
||||
else:
|
||||
self.hmac_256 = None
|
||||
|
||||
def s3_authorization(self, headers, verb, path):
|
||||
"""Generate S3 authorization string."""
|
||||
if not boto:
|
||||
raise exception.NovaException('boto is not installed')
|
||||
c_string = boto.utils.canonical_string(verb, path, headers)
|
||||
hmac_copy = self.hmac.copy()
|
||||
hmac_copy.update(c_string)
|
||||
b64_hmac = base64.encodestring(hmac_copy.digest()).strip()
|
||||
return b64_hmac
|
||||
|
||||
def generate(self, params, verb, server_string, path):
|
||||
"""Generate auth string according to what SignatureVersion is given.
|
||||
|
||||
The signature method must be SHA1 or SHA256.
|
||||
|
||||
"""
|
||||
if params['SignatureVersion'] == '0':
|
||||
return self._calc_signature_0(params)
|
||||
if params['SignatureVersion'] == '1':
|
||||
return self._calc_signature_1(params)
|
||||
if params['SignatureVersion'] == '2':
|
||||
return self._calc_signature_2(params, verb, server_string, path)
|
||||
raise exception.NovaException('Unknown Signature Version: %s' %
|
||||
params['SignatureVersion'])
|
||||
|
||||
@staticmethod
|
||||
def _get_utf8_value(value):
|
||||
"""Get the UTF8-encoded version of a value."""
|
||||
if not isinstance(value, str) and not isinstance(value, unicode):
|
||||
value = str(value)
|
||||
if isinstance(value, unicode):
|
||||
return value.encode('utf-8')
|
||||
else:
|
||||
return value
|
||||
|
||||
def _calc_signature_0(self, params):
|
||||
"""Generate AWS signature version 0 string."""
|
||||
s = params['Action'] + params['Timestamp']
|
||||
self.hmac.update(s)
|
||||
keys = params.keys()
|
||||
keys.sort(cmp=lambda x, y: cmp(x.lower(), y.lower()))
|
||||
pairs = []
|
||||
for key in keys:
|
||||
val = self._get_utf8_value(params[key])
|
||||
pairs.append(key + '=' + urllib.quote(val))
|
||||
return base64.b64encode(self.hmac.digest())
|
||||
|
||||
def _calc_signature_1(self, params):
|
||||
"""Generate AWS signature version 1 string."""
|
||||
keys = params.keys()
|
||||
keys.sort(cmp=lambda x, y: cmp(x.lower(), y.lower()))
|
||||
pairs = []
|
||||
for key in keys:
|
||||
self.hmac.update(key)
|
||||
val = self._get_utf8_value(params[key])
|
||||
self.hmac.update(val)
|
||||
pairs.append(key + '=' + urllib.quote(val))
|
||||
return base64.b64encode(self.hmac.digest())
|
||||
|
||||
def _calc_signature_2(self, params, verb, server_string, path):
|
||||
"""Generate AWS signature version 2 string."""
|
||||
LOG.debug('using _calc_signature_2')
|
||||
string_to_sign = '%s\n%s\n%s\n' % (verb, server_string, path)
|
||||
|
||||
if 'SignatureMethod' not in params:
|
||||
raise exception.NovaException('No SignatureMethod specified')
|
||||
|
||||
if params['SignatureMethod'] == 'HmacSHA256':
|
||||
if not self.hmac_256:
|
||||
msg = _('SHA256 not supported on this server')
|
||||
raise exception.NovaException(msg)
|
||||
current_hmac = self.hmac_256
|
||||
elif params['SignatureMethod'] == 'HmacSHA1':
|
||||
current_hmac = self.hmac
|
||||
else:
|
||||
raise exception.NovaException('SignatureMethod %s not supported'
|
||||
% params['SignatureMethod'])
|
||||
|
||||
keys = params.keys()
|
||||
keys.sort()
|
||||
pairs = []
|
||||
for key in keys:
|
||||
val = self._get_utf8_value(params[key])
|
||||
val = urllib.quote(val, safe='-_~')
|
||||
pairs.append(urllib.quote(key, safe='') + '=' + val)
|
||||
qs = '&'.join(pairs)
|
||||
LOG.debug('query string: %s', qs)
|
||||
string_to_sign += qs
|
||||
LOG.debug('string_to_sign: %s', string_to_sign)
|
||||
current_hmac.update(string_to_sign)
|
||||
b64 = base64.b64encode(current_hmac.digest())
|
||||
LOG.debug('len(b64)=%d', len(b64))
|
||||
LOG.debug('base64 encoded digest: %s', b64)
|
||||
return b64
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
print Signer('foo').generate({'SignatureVersion': '2',
|
||||
'SignatureMethod': 'HmacSHA256'},
|
||||
'get', 'server', '/foo')
|
@ -1,154 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
# 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.
|
||||
# LDAP INSTALL SCRIPT - SHOULD BE IDEMPOTENT, but it SCRUBS all USERS
|
||||
|
||||
apt-get install -y slapd ldap-utils python-ldap
|
||||
|
||||
abspath=`dirname "$(cd "${0%/*}" 2>/dev/null; echo "$PWD"/"${0##*/}")"`
|
||||
cp $abspath/openssh-lpk_openldap.schema /etc/ldap/schema/openssh-lpk_openldap.schema
|
||||
cp $abspath/nova_openldap.schema /etc/ldap/schema/nova.schema
|
||||
|
||||
mv /etc/ldap/slapd.conf /etc/ldap/slapd.conf.orig
|
||||
cat >/etc/ldap/slapd.conf <<SLAPD_CONF_EOF
|
||||
# slapd.conf - Configuration file for LDAP SLAPD
|
||||
##########
|
||||
# Basics #
|
||||
##########
|
||||
include /etc/ldap/schema/core.schema
|
||||
include /etc/ldap/schema/cosine.schema
|
||||
include /etc/ldap/schema/inetorgperson.schema
|
||||
include /etc/ldap/schema/nova.schema
|
||||
pidfile /var/run/slapd/slapd.pid
|
||||
argsfile /var/run/slapd/slapd.args
|
||||
loglevel none
|
||||
modulepath /usr/lib/ldap
|
||||
# modulepath /usr/local/libexec/openldap
|
||||
moduleload back_hdb
|
||||
##########################
|
||||
# Database Configuration #
|
||||
##########################
|
||||
database hdb
|
||||
suffix "dc=example,dc=com"
|
||||
rootdn "cn=Manager,dc=example,dc=com"
|
||||
rootpw changeme
|
||||
directory /var/lib/ldap
|
||||
# directory /usr/local/var/openldap-data
|
||||
index objectClass,cn eq
|
||||
########
|
||||
# ACLs #
|
||||
########
|
||||
access to attrs=userPassword
|
||||
by anonymous auth
|
||||
by self write
|
||||
by * none
|
||||
access to *
|
||||
by self write
|
||||
by * none
|
||||
SLAPD_CONF_EOF
|
||||
|
||||
mv /etc/ldap/ldap.conf /etc/ldap/ldap.conf.orig
|
||||
|
||||
cat >/etc/ldap/ldap.conf <<LDAP_CONF_EOF
|
||||
# LDAP Client Settings
|
||||
URI ldap://localhost
|
||||
BASE dc=example,dc=com
|
||||
BINDDN cn=Manager,dc=example,dc=com
|
||||
SIZELIMIT 0
|
||||
TIMELIMIT 0
|
||||
LDAP_CONF_EOF
|
||||
|
||||
cat >/etc/ldap/base.ldif <<BASE_LDIF_EOF
|
||||
# This is the root of the directory tree
|
||||
dn: dc=example,dc=com
|
||||
description: Example.Com, your trusted non-existent corporation.
|
||||
dc: example
|
||||
o: Example.Com
|
||||
objectClass: top
|
||||
objectClass: dcObject
|
||||
objectClass: organization
|
||||
|
||||
# Subtree for users
|
||||
dn: ou=Users,dc=example,dc=com
|
||||
ou: Users
|
||||
description: Users
|
||||
objectClass: organizationalUnit
|
||||
|
||||
# Subtree for groups
|
||||
dn: ou=Groups,dc=example,dc=com
|
||||
ou: Groups
|
||||
description: Groups
|
||||
objectClass: organizationalUnit
|
||||
|
||||
# Subtree for system accounts
|
||||
dn: ou=System,dc=example,dc=com
|
||||
ou: System
|
||||
description: Special accounts used by software applications.
|
||||
objectClass: organizationalUnit
|
||||
|
||||
# Special Account for Authentication:
|
||||
dn: uid=authenticate,ou=System,dc=example,dc=com
|
||||
uid: authenticate
|
||||
ou: System
|
||||
description: Special account for authenticating users
|
||||
userPassword: {MD5}TLnIqASP0CKUR3/LGkEZGg==
|
||||
objectClass: account
|
||||
objectClass: simpleSecurityObject
|
||||
|
||||
# create the sysadmin entry
|
||||
|
||||
dn: cn=developers,ou=Groups,dc=example,dc=com
|
||||
objectclass: groupOfNames
|
||||
cn: developers
|
||||
description: IT admin group
|
||||
member: uid=admin,ou=Users,dc=example,dc=com
|
||||
|
||||
dn: cn=sysadmins,ou=Groups,dc=example,dc=com
|
||||
objectclass: groupOfNames
|
||||
cn: sysadmins
|
||||
description: IT admin group
|
||||
member: uid=admin,ou=Users,dc=example,dc=com
|
||||
|
||||
dn: cn=netadmins,ou=Groups,dc=example,dc=com
|
||||
objectclass: groupOfNames
|
||||
cn: netadmins
|
||||
description: Network admin group
|
||||
member: uid=admin,ou=Users,dc=example,dc=com
|
||||
|
||||
dn: cn=cloudadmins,ou=Groups,dc=example,dc=com
|
||||
objectclass: groupOfNames
|
||||
cn: cloudadmins
|
||||
description: Cloud admin group
|
||||
member: uid=admin,ou=Users,dc=example,dc=com
|
||||
|
||||
dn: cn=itsec,ou=Groups,dc=example,dc=com
|
||||
objectclass: groupOfNames
|
||||
cn: itsec
|
||||
description: IT security users group
|
||||
member: uid=admin,ou=Users,dc=example,dc=com
|
||||
BASE_LDIF_EOF
|
||||
|
||||
/etc/init.d/slapd stop
|
||||
rm -rf /var/lib/ldap/*
|
||||
rm -rf /etc/ldap/slapd.d/*
|
||||
slaptest -f /etc/ldap/slapd.conf -F /etc/ldap/slapd.d
|
||||
cp /usr/share/slapd/DB_CONFIG /var/lib/ldap/DB_CONFIG
|
||||
slapadd -v -l /etc/ldap/base.ldif
|
||||
chown -R openldap:openldap /etc/ldap/slapd.d
|
||||
chown -R openldap:openldap /var/lib/ldap
|
||||
/etc/init.d/slapd start
|
@ -20,7 +20,6 @@ from nova import flags
|
||||
|
||||
FLAGS = flags.FLAGS
|
||||
|
||||
flags.DECLARE('auth_driver', 'nova.auth.manager')
|
||||
flags.DECLARE('compute_scheduler_driver', 'nova.scheduler.multi')
|
||||
flags.DECLARE('fake_network', 'nova.network.manager')
|
||||
flags.DECLARE('iscsi_num_targets', 'nova.volume.driver')
|
||||
@ -32,7 +31,6 @@ flags.DECLARE('volume_driver', 'nova.volume.manager')
|
||||
|
||||
def set_defaults(conf):
|
||||
conf.set_default('api_paste_config', '$state_path/etc/nova/api-paste.ini')
|
||||
conf.set_default('auth_driver', 'nova.auth.dbdriver.DbDriver')
|
||||
conf.set_default('compute_driver', 'nova.virt.fake.FakeDriver')
|
||||
conf.set_default('connection_type', 'fake')
|
||||
conf.set_default('fake_network', True)
|
||||
|
@ -1,132 +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 webob
|
||||
|
||||
from nova.api import ec2
|
||||
from nova.auth import manager
|
||||
from nova import context
|
||||
from nova import flags
|
||||
from nova import test
|
||||
|
||||
FLAGS = flags.FLAGS
|
||||
|
||||
|
||||
class FakeControllerClass(object):
|
||||
pass
|
||||
|
||||
|
||||
class FakeApiRequest(object):
|
||||
def __init__(self, action):
|
||||
self.controller = FakeControllerClass()
|
||||
self.action = action
|
||||
|
||||
|
||||
class AccessTestCase(test.TestCase):
|
||||
def _env_for(self, ctxt, action):
|
||||
env = {}
|
||||
env['nova.context'] = ctxt
|
||||
env['ec2.request'] = FakeApiRequest(action)
|
||||
return env
|
||||
|
||||
def setUp(self):
|
||||
super(AccessTestCase, self).setUp()
|
||||
um = manager.AuthManager()
|
||||
# Make test users
|
||||
self.testadmin = um.create_user('testadmin')
|
||||
self.testpmsys = um.create_user('testpmsys')
|
||||
self.testnet = um.create_user('testnet')
|
||||
self.testsys = um.create_user('testsys')
|
||||
# Assign some rules
|
||||
um.add_role('testadmin', 'cloudadmin')
|
||||
um.add_role('testpmsys', 'sysadmin')
|
||||
um.add_role('testnet', 'netadmin')
|
||||
um.add_role('testsys', 'sysadmin')
|
||||
|
||||
# Make a test project
|
||||
self.project = um.create_project('testproj',
|
||||
'testpmsys',
|
||||
'a test project',
|
||||
['testpmsys', 'testnet', 'testsys'])
|
||||
self.project.add_role(self.testnet, 'netadmin')
|
||||
self.project.add_role(self.testsys, 'sysadmin')
|
||||
#user is set in each test
|
||||
|
||||
def noopWSGIApp(environ, start_response):
|
||||
start_response('200 OK', [])
|
||||
return ['']
|
||||
|
||||
self.mw = ec2.Authorizer(noopWSGIApp)
|
||||
self.mw.action_roles = {'FakeControllerClass': {
|
||||
'_allow_all': ['all'],
|
||||
'_allow_none': [],
|
||||
'_allow_project_manager': ['projectmanager'],
|
||||
'_allow_sys_and_net': ['sysadmin', 'netadmin'],
|
||||
'_allow_sysadmin': ['sysadmin']}}
|
||||
|
||||
def tearDown(self):
|
||||
um = manager.AuthManager()
|
||||
# Delete the test project
|
||||
um.delete_project('testproj')
|
||||
# Delete the test user
|
||||
um.delete_user('testadmin')
|
||||
um.delete_user('testpmsys')
|
||||
um.delete_user('testnet')
|
||||
um.delete_user('testsys')
|
||||
super(AccessTestCase, self).tearDown()
|
||||
|
||||
def response_status(self, user, methodName):
|
||||
roles = manager.AuthManager().get_active_roles(user, self.project)
|
||||
ctxt = context.RequestContext(user.id,
|
||||
self.project.id,
|
||||
is_admin=user.is_admin(),
|
||||
roles=roles)
|
||||
environ = self._env_for(ctxt, methodName)
|
||||
req = webob.Request.blank('/', environ)
|
||||
resp = req.get_response(self.mw)
|
||||
return resp.status_int
|
||||
|
||||
def shouldAllow(self, user, methodName):
|
||||
self.assertEqual(200, self.response_status(user, methodName))
|
||||
|
||||
def shouldDeny(self, user, methodName):
|
||||
self.assertEqual(401, self.response_status(user, methodName))
|
||||
|
||||
def test_allow_all(self):
|
||||
users = [self.testadmin, self.testpmsys, self.testnet, self.testsys]
|
||||
for user in users:
|
||||
self.shouldAllow(user, '_allow_all')
|
||||
|
||||
def test_allow_none(self):
|
||||
self.shouldAllow(self.testadmin, '_allow_none')
|
||||
users = [self.testpmsys, self.testnet, self.testsys]
|
||||
for user in users:
|
||||
self.shouldDeny(user, '_allow_none')
|
||||
|
||||
def test_allow_project_manager(self):
|
||||
for user in [self.testadmin, self.testpmsys]:
|
||||
self.shouldAllow(user, '_allow_project_manager')
|
||||
for user in [self.testnet, self.testsys]:
|
||||
self.shouldDeny(user, '_allow_project_manager')
|
||||
|
||||
def test_allow_sys_and_net(self):
|
||||
for user in [self.testadmin, self.testnet, self.testsys]:
|
||||
self.shouldAllow(user, '_allow_sys_and_net')
|
||||
# denied because it doesn't have the per project sysadmin
|
||||
for user in [self.testpmsys]:
|
||||
self.shouldDeny(user, '_allow_sys_and_net')
|
@ -1,422 +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 unittest
|
||||
|
||||
from nova.auth import fakeldap
|
||||
from nova.auth import manager
|
||||
from nova import exception
|
||||
from nova import flags
|
||||
from nova.openstack.common import log as logging
|
||||
from nova import test
|
||||
|
||||
FLAGS = flags.FLAGS
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class user_generator(object):
|
||||
def __init__(self, manager, **user_state):
|
||||
if 'name' not in user_state:
|
||||
user_state['name'] = 'test1'
|
||||
self.manager = manager
|
||||
self.user = manager.create_user(**user_state)
|
||||
|
||||
def __enter__(self):
|
||||
return self.user
|
||||
|
||||
def __exit__(self, value, type, trace):
|
||||
self.manager.delete_user(self.user)
|
||||
|
||||
|
||||
class project_generator(object):
|
||||
def __init__(self, manager, **project_state):
|
||||
if 'name' not in project_state:
|
||||
project_state['name'] = 'testproj'
|
||||
if 'manager_user' not in project_state:
|
||||
project_state['manager_user'] = 'test1'
|
||||
self.manager = manager
|
||||
self.project = manager.create_project(**project_state)
|
||||
|
||||
def __enter__(self):
|
||||
return self.project
|
||||
|
||||
def __exit__(self, value, type, trace):
|
||||
self.manager.delete_project(self.project)
|
||||
|
||||
|
||||
class user_and_project_generator(object):
|
||||
def __init__(self, manager, user_state=None, project_state=None):
|
||||
if not user_state:
|
||||
user_state = {}
|
||||
if not project_state:
|
||||
project_state = {}
|
||||
|
||||
self.manager = manager
|
||||
if 'name' not in user_state:
|
||||
user_state['name'] = 'test1'
|
||||
if 'name' not in project_state:
|
||||
project_state['name'] = 'testproj'
|
||||
if 'manager_user' not in project_state:
|
||||
project_state['manager_user'] = 'test1'
|
||||
self.user = manager.create_user(**user_state)
|
||||
self.project = manager.create_project(**project_state)
|
||||
|
||||
def __enter__(self):
|
||||
return (self.user, self.project)
|
||||
|
||||
def __exit__(self, value, type, trace):
|
||||
self.manager.delete_user(self.user)
|
||||
self.manager.delete_project(self.project)
|
||||
|
||||
|
||||
class _AuthManagerBaseTestCase(test.TestCase):
|
||||
|
||||
user_not_found_type = exception.UserNotFound
|
||||
|
||||
def setUp(self):
|
||||
super(_AuthManagerBaseTestCase, self).setUp()
|
||||
self.flags(auth_driver=self.auth_driver,
|
||||
compute_driver='nova.virt.fake.FakeDriver')
|
||||
self.manager = manager.AuthManager(new=True)
|
||||
self.manager.mc.cache = {}
|
||||
|
||||
def test_create_and_find_user(self):
|
||||
with user_generator(self.manager):
|
||||
self.assert_(self.manager.get_user('test1'))
|
||||
|
||||
def test_create_and_find_with_properties(self):
|
||||
with user_generator(self.manager, name="herbert", secret="classified",
|
||||
access="private-party"):
|
||||
u = self.manager.get_user('herbert')
|
||||
self.assertEqual('herbert', u.id)
|
||||
self.assertEqual('herbert', u.name)
|
||||
self.assertEqual('classified', u.secret)
|
||||
self.assertEqual('private-party', u.access)
|
||||
|
||||
def test_create_user_twice(self):
|
||||
self.manager.create_user('test-1')
|
||||
self.assertRaises(exception.UserExists, self.manager.create_user,
|
||||
'test-1')
|
||||
|
||||
def test_signature_is_valid(self):
|
||||
with user_generator(self.manager, name='admin', secret='admin',
|
||||
access='admin'):
|
||||
with project_generator(self.manager, name="admin",
|
||||
manager_user='admin'):
|
||||
accesskey = 'admin:admin'
|
||||
expected_result = (self.manager.get_user('admin'),
|
||||
self.manager.get_project('admin'))
|
||||
# captured sig and query string using boto 1.9b/euca2ools 1.2
|
||||
sig = 'd67Wzd9Bwz8xid9QU+lzWXcF2Y3tRicYABPJgrqfrwM='
|
||||
auth_params = {'AWSAccessKeyId': 'admin:admin',
|
||||
'Action': 'DescribeAvailabilityZones',
|
||||
'SignatureMethod': 'HmacSHA256',
|
||||
'SignatureVersion': '2',
|
||||
'Timestamp': '2011-04-22T11:29:29',
|
||||
'Version': '2009-11-30'}
|
||||
self.assertTrue(expected_result, self.manager.authenticate(
|
||||
accesskey,
|
||||
sig,
|
||||
auth_params,
|
||||
'GET',
|
||||
'127.0.0.1:8773',
|
||||
'/services/Cloud/'))
|
||||
# captured sig and query string using RightAWS 1.10.0
|
||||
sig = 'ECYLU6xdFG0ZqRVhQybPJQNJ5W4B9n8fGs6+/fuGD2c='
|
||||
auth_params = {'AWSAccessKeyId': 'admin:admin',
|
||||
'Action': 'DescribeAvailabilityZones',
|
||||
'SignatureMethod': 'HmacSHA256',
|
||||
'SignatureVersion': '2',
|
||||
'Timestamp': '2011-04-22T11:29:49.000Z',
|
||||
'Version': '2008-12-01'}
|
||||
self.assertTrue(expected_result, self.manager.authenticate(
|
||||
accesskey,
|
||||
sig,
|
||||
auth_params,
|
||||
'GET',
|
||||
'127.0.0.1',
|
||||
'/services/Cloud'))
|
||||
|
||||
def test_can_get_credentials(self):
|
||||
self.flags(auth_strategy='deprecated')
|
||||
st = {'access': 'access', 'secret': 'secret'}
|
||||
with user_and_project_generator(self.manager, user_state=st) as (u, p):
|
||||
credentials = self.manager.get_environment_rc(u, p)
|
||||
LOG.debug(credentials)
|
||||
self.assertTrue('export EC2_ACCESS_KEY="access:testproj"\n'
|
||||
in credentials)
|
||||
self.assertTrue('export EC2_SECRET_KEY="secret"\n' in credentials)
|
||||
|
||||
def test_can_list_users(self):
|
||||
with user_generator(self.manager):
|
||||
with user_generator(self.manager, name="test2"):
|
||||
users = self.manager.get_users()
|
||||
self.assert_(filter(lambda u: u.id == 'test1', users))
|
||||
self.assert_(filter(lambda u: u.id == 'test2', users))
|
||||
self.assert_(not filter(lambda u: u.id == 'test3', users))
|
||||
|
||||
def test_can_add_and_remove_user_role(self):
|
||||
with user_generator(self.manager):
|
||||
self.assertFalse(self.manager.has_role('test1', 'itsec'))
|
||||
self.manager.add_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_can_create_and_get_project(self):
|
||||
with user_and_project_generator(self.manager) as (u, p):
|
||||
self.assert_(self.manager.get_user('test1'))
|
||||
self.assert_(self.manager.get_user('test1'))
|
||||
self.assert_(self.manager.get_project('testproj'))
|
||||
|
||||
def test_can_list_projects(self):
|
||||
with user_and_project_generator(self.manager):
|
||||
with project_generator(self.manager, name="testproj2"):
|
||||
projects = self.manager.get_projects()
|
||||
self.assert_(filter(lambda p: p.name == 'testproj', projects))
|
||||
self.assert_(filter(lambda p: p.name == 'testproj2', projects))
|
||||
self.assert_(not filter(lambda p: p.name == 'testproj3',
|
||||
projects))
|
||||
|
||||
def test_can_create_and_get_project_with_attributes(self):
|
||||
with user_generator(self.manager):
|
||||
with project_generator(self.manager, description='A test project'):
|
||||
project = self.manager.get_project('testproj')
|
||||
self.assertEqual('A test project', project.description)
|
||||
|
||||
def test_can_create_project_with_manager(self):
|
||||
with user_and_project_generator(self.manager) as (user, project):
|
||||
self.assertEqual('test1', project.project_manager_id)
|
||||
self.assertTrue(self.manager.is_project_manager(user, project))
|
||||
|
||||
def test_can_create_project_twice(self):
|
||||
with user_and_project_generator(self.manager) as (user1, project):
|
||||
self.assertRaises(exception.ProjectExists,
|
||||
self.manager.create_project, "testproj", "test1")
|
||||
|
||||
def test_create_project_assigns_manager_to_members(self):
|
||||
with user_and_project_generator(self.manager) as (user, project):
|
||||
self.assertTrue(self.manager.is_project_member(user, project))
|
||||
|
||||
def test_create_project_with_manager_and_members(self):
|
||||
with user_generator(self.manager, name='test2') as user2:
|
||||
with user_and_project_generator(self.manager,
|
||||
project_state={'member_users': ['test2']}) as (user1, project):
|
||||
self.assertTrue(self.manager.is_project_member(
|
||||
user1, project))
|
||||
self.assertTrue(self.manager.is_project_member(
|
||||
user2, project))
|
||||
|
||||
def test_create_project_with_manager_and_missing_members(self):
|
||||
self.assertRaises(self.user_not_found_type,
|
||||
self.manager.create_project, "testproj", "test1",
|
||||
member_users="test2")
|
||||
|
||||
def test_no_extra_project_members(self):
|
||||
with user_generator(self.manager, name='test2') as baduser:
|
||||
with user_and_project_generator(self.manager) as (user, project):
|
||||
self.assertFalse(self.manager.is_project_member(baduser,
|
||||
project))
|
||||
|
||||
def test_no_extra_project_managers(self):
|
||||
with user_generator(self.manager, name='test2') as baduser:
|
||||
with user_and_project_generator(self.manager) as (user, project):
|
||||
self.assertFalse(self.manager.is_project_manager(baduser,
|
||||
project))
|
||||
|
||||
def test_can_add_user_to_project(self):
|
||||
with user_generator(self.manager, name='test2') as user:
|
||||
with user_and_project_generator(self.manager) as (_user, project):
|
||||
self.manager.add_to_project(user, project)
|
||||
project = self.manager.get_project('testproj')
|
||||
self.assertTrue(self.manager.is_project_member(user, project))
|
||||
|
||||
def test_can_remove_user_from_project(self):
|
||||
with user_generator(self.manager, name='test2') as user:
|
||||
with user_and_project_generator(self.manager) as (_user, project):
|
||||
self.manager.add_to_project(user, project)
|
||||
project = self.manager.get_project('testproj')
|
||||
self.assertTrue(self.manager.is_project_member(user, project))
|
||||
self.manager.remove_from_project(user, project)
|
||||
project = self.manager.get_project('testproj')
|
||||
self.assertFalse(self.manager.is_project_member(user, project))
|
||||
|
||||
def test_can_add_remove_user_with_role(self):
|
||||
with user_generator(self.manager, name='test2') as user:
|
||||
with user_and_project_generator(self.manager) as (_user, project):
|
||||
# NOTE(todd): after modifying users you must reload project
|
||||
self.manager.add_to_project(user, project)
|
||||
project = self.manager.get_project('testproj')
|
||||
self.manager.add_role(user, 'developer', project)
|
||||
self.assertTrue(self.manager.is_project_member(user, project))
|
||||
self.manager.remove_from_project(user, project)
|
||||
project = self.manager.get_project('testproj')
|
||||
self.assertFalse(self.manager.has_role(user, 'developer',
|
||||
project))
|
||||
self.assertFalse(self.manager.is_project_member(user, project))
|
||||
|
||||
def test_adding_role_to_project_is_ignored_unless_added_to_user(self):
|
||||
with user_and_project_generator(self.manager) as (user, project):
|
||||
self.assertFalse(self.manager.has_role(user, 'sysadmin', project))
|
||||
self.manager.add_role(user, 'sysadmin', project)
|
||||
# NOTE(todd): it will still show up in get_user_roles(u, project)
|
||||
self.assertFalse(self.manager.has_role(user, 'sysadmin', project))
|
||||
self.manager.add_role(user, 'sysadmin')
|
||||
self.assertTrue(self.manager.has_role(user, 'sysadmin', project))
|
||||
|
||||
def test_add_user_role_doesnt_infect_project_roles(self):
|
||||
with user_and_project_generator(self.manager) as (user, project):
|
||||
self.assertFalse(self.manager.has_role(user, 'sysadmin', project))
|
||||
self.manager.add_role(user, 'sysadmin')
|
||||
self.assertFalse(self.manager.has_role(user, 'sysadmin', project))
|
||||
|
||||
def test_can_list_user_roles(self):
|
||||
with user_and_project_generator(self.manager) as (user, project):
|
||||
self.manager.add_role(user, 'sysadmin')
|
||||
roles = self.manager.get_user_roles(user)
|
||||
self.assertTrue('sysadmin' in roles)
|
||||
self.assertFalse('netadmin' in roles)
|
||||
|
||||
def test_can_list_project_roles(self):
|
||||
with user_and_project_generator(self.manager) as (user, project):
|
||||
self.manager.add_role(user, 'sysadmin')
|
||||
self.manager.add_role(user, 'sysadmin', project)
|
||||
self.manager.add_role(user, 'netadmin', project)
|
||||
project_roles = self.manager.get_user_roles(user, project)
|
||||
self.assertTrue('sysadmin' in project_roles)
|
||||
self.assertTrue('netadmin' in project_roles)
|
||||
# has role should be false user-level role is missing
|
||||
self.assertFalse(self.manager.has_role(user, 'netadmin', project))
|
||||
|
||||
def test_can_remove_user_roles(self):
|
||||
with user_and_project_generator(self.manager) as (user, project):
|
||||
self.manager.add_role(user, 'sysadmin')
|
||||
self.assertTrue(self.manager.has_role(user, 'sysadmin'))
|
||||
self.manager.remove_role(user, 'sysadmin')
|
||||
self.assertFalse(self.manager.has_role(user, 'sysadmin'))
|
||||
|
||||
def test_removing_user_role_hides_it_from_project(self):
|
||||
with user_and_project_generator(self.manager) as (user, project):
|
||||
self.manager.add_role(user, 'sysadmin')
|
||||
self.manager.add_role(user, 'sysadmin', project)
|
||||
self.assertTrue(self.manager.has_role(user, 'sysadmin', project))
|
||||
self.manager.remove_role(user, 'sysadmin')
|
||||
self.assertFalse(self.manager.has_role(user, 'sysadmin', project))
|
||||
|
||||
def test_can_remove_project_role_but_keep_user_role(self):
|
||||
with user_and_project_generator(self.manager) as (user, project):
|
||||
self.manager.add_role(user, 'sysadmin')
|
||||
self.manager.add_role(user, 'sysadmin', project)
|
||||
self.assertTrue(self.manager.has_role(user, 'sysadmin'))
|
||||
self.manager.remove_role(user, 'sysadmin', project)
|
||||
self.assertFalse(self.manager.has_role(user, 'sysadmin', project))
|
||||
self.assertTrue(self.manager.has_role(user, 'sysadmin'))
|
||||
|
||||
def test_can_retrieve_project_by_user(self):
|
||||
with user_and_project_generator(self.manager) as (user, project):
|
||||
self.assertEqual(1, len(self.manager.get_projects('test1')))
|
||||
|
||||
def test_can_modify_project(self):
|
||||
with user_and_project_generator(self.manager):
|
||||
with user_generator(self.manager, name='test2'):
|
||||
self.manager.modify_project('testproj', 'test2', 'new desc')
|
||||
project = self.manager.get_project('testproj')
|
||||
self.assertEqual('test2', project.project_manager_id)
|
||||
self.assertEqual('new desc', project.description)
|
||||
|
||||
def test_can_call_modify_project_but_do_nothing(self):
|
||||
with user_and_project_generator(self.manager):
|
||||
self.manager.modify_project('testproj')
|
||||
project = self.manager.get_project('testproj')
|
||||
self.assertEqual('test1', project.project_manager_id)
|
||||
self.assertEqual('testproj', project.description)
|
||||
|
||||
def test_modify_project_adds_new_manager(self):
|
||||
with user_and_project_generator(self.manager):
|
||||
with user_generator(self.manager, name='test2'):
|
||||
self.manager.modify_project('testproj', 'test2', 'new desc')
|
||||
project = self.manager.get_project('testproj')
|
||||
self.assertTrue('test2' in project.member_ids)
|
||||
|
||||
def test_create_project_with_missing_user(self):
|
||||
with user_generator(self.manager):
|
||||
self.assertRaises(self.user_not_found_type,
|
||||
self.manager.create_project, 'testproj',
|
||||
'not_real')
|
||||
|
||||
def test_can_delete_project(self):
|
||||
with user_generator(self.manager):
|
||||
self.manager.create_project('testproj', 'test1')
|
||||
self.assert_(self.manager.get_project('testproj'))
|
||||
self.manager.delete_project('testproj')
|
||||
projectlist = self.manager.get_projects()
|
||||
self.assert_(not filter(lambda p: p.name == 'testproj',
|
||||
projectlist))
|
||||
|
||||
def test_can_delete_user(self):
|
||||
self.manager.create_user('test1')
|
||||
self.assert_(self.manager.get_user('test1'))
|
||||
self.manager.delete_user('test1')
|
||||
userlist = self.manager.get_users()
|
||||
self.assert_(not filter(lambda u: u.id == 'test1', userlist))
|
||||
|
||||
def test_can_modify_users(self):
|
||||
with user_generator(self.manager):
|
||||
self.manager.modify_user('test1', 'access', 'secret', True)
|
||||
user = self.manager.get_user('test1')
|
||||
self.assertEqual('access', user.access)
|
||||
self.assertEqual('secret', user.secret)
|
||||
self.assertTrue(user.is_admin())
|
||||
|
||||
def test_can_call_modify_user_but_do_nothing(self):
|
||||
with user_generator(self.manager):
|
||||
old_user = self.manager.get_user('test1')
|
||||
self.manager.modify_user('test1')
|
||||
user = self.manager.get_user('test1')
|
||||
self.assertEqual(old_user.access, user.access)
|
||||
self.assertEqual(old_user.secret, user.secret)
|
||||
self.assertEqual(old_user.is_admin(), user.is_admin())
|
||||
|
||||
def test_get_nonexistent_user_raises_notfound_exception(self):
|
||||
self.assertRaises(exception.NotFound,
|
||||
self.manager.get_user,
|
||||
'joeblow')
|
||||
|
||||
|
||||
class AuthManagerLdapTestCase(_AuthManagerBaseTestCase):
|
||||
auth_driver = 'nova.auth.ldapdriver.FakeLdapDriver'
|
||||
user_not_found_type = exception.LDAPUserNotFound
|
||||
|
||||
def test_reconnect_on_server_failure(self):
|
||||
self.manager.get_users()
|
||||
fakeldap.server_fail = True
|
||||
try:
|
||||
self.assertRaises(fakeldap.SERVER_DOWN, self.manager.get_users)
|
||||
finally:
|
||||
fakeldap.server_fail = False
|
||||
self.manager.get_users()
|
||||
|
||||
|
||||
class AuthManagerDbTestCase(_AuthManagerBaseTestCase):
|
||||
auth_driver = 'nova.auth.dbdriver.DbDriver'
|
||||
user_not_found_type = exception.UserNotFound
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# TODO(anotherjesse): Implement use_fake as an option
|
||||
unittest.main()
|
@ -1,112 +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.
|
||||
|
||||
"""Tests for Signer."""
|
||||
|
||||
from nova.auth import signer
|
||||
from nova import exception
|
||||
from nova import test
|
||||
|
||||
|
||||
class ClassWithStrRepr(object):
|
||||
def __repr__(self):
|
||||
return 'A string representation'
|
||||
|
||||
|
||||
class SignerTestCase(test.TestCase):
|
||||
def setUp(self):
|
||||
super(SignerTestCase, self).setUp()
|
||||
self.signer = signer.Signer('uV3F3YluFJax1cknvbcGwgjvx4QpvB+leU8dUj2o')
|
||||
|
||||
# S3 Authorization Signing input & output examples taken from here:
|
||||
# http://docs.amazonwebservices.com/AmazonS3/latest/dev/
|
||||
def test_s3_authorization_get(self):
|
||||
self.assertEquals('xXjDGYUmKxnwqr5KXNPGldn5LbA=',
|
||||
self.signer.s3_authorization(
|
||||
{'Date': 'Tue, 27 Mar 2007 19:36:42 +0000'},
|
||||
'GET',
|
||||
'/johnsmith/photos/puppy.jpg'))
|
||||
|
||||
def test_s3_authorization_put(self):
|
||||
self.assertEquals('hcicpDDvL9SsO6AkvxqmIWkmOuQ=',
|
||||
self.signer.s3_authorization(
|
||||
{'Date': 'Tue, 27 Mar 2007 21:15:45 +0000',
|
||||
'Content-Length': '94328',
|
||||
'Content-Type': 'image/jpeg'},
|
||||
'PUT',
|
||||
'/johnsmith/photos/puppy.jpg'))
|
||||
|
||||
def test_generate_HmacSHA256(self):
|
||||
self.assertEquals('clXalhbLZXxEuI32OoX+OeXsN6Mr2q4jzGyIDAr4RZg=',
|
||||
self.signer.generate(
|
||||
{'SignatureMethod': 'HmacSHA256',
|
||||
'SignatureVersion': '2'},
|
||||
'GET', 'server', '/foo'))
|
||||
|
||||
def test_generate_HmacSHA1(self):
|
||||
self.assertEquals('uJTByiDIcgB65STrS5i2egQgd+U=',
|
||||
self.signer.generate({'SignatureVersion': '2',
|
||||
'SignatureMethod': 'HmacSHA1'},
|
||||
'GET', 'server', '/foo'))
|
||||
|
||||
def test_generate_invalid_signature_method_defined(self):
|
||||
self.assertRaises(exception.NovaException,
|
||||
self.signer.generate,
|
||||
{'SignatureVersion': '2',
|
||||
'SignatureMethod': 'invalid_method'},
|
||||
'GET', 'server', '/foo')
|
||||
|
||||
def test_generate_no_signature_method_defined(self):
|
||||
self.assertRaises(exception.NovaException,
|
||||
self.signer.generate,
|
||||
{'SignatureVersion': '2'},
|
||||
'GET', 'server', '/foo')
|
||||
|
||||
def test_generate_HmacSHA256_missing_hashlib_sha256(self):
|
||||
# Stub out haslib.sha256
|
||||
import hashlib
|
||||
self.stubs.Set(hashlib, 'sha256', None)
|
||||
|
||||
# Create Signer again now that hashlib.sha256 is None
|
||||
self.signer = signer.Signer(
|
||||
'uV3F3YluFJax1cknvbcGwgjvx4QpvB+leU8dUj2o')
|
||||
self.assertRaises(exception.NovaException,
|
||||
self.signer.generate,
|
||||
{'SignatureVersion': '2',
|
||||
'SignatureMethod': 'HmacSHA256'},
|
||||
'GET', 'server', '/foo')
|
||||
|
||||
def test_generate_with_unicode_param(self):
|
||||
self.assertEquals('clXalhbLZXxEuI32OoX+OeXsN6Mr2q4jzGyIDAr4RZg=',
|
||||
self.signer.generate({'SignatureVersion': u'2',
|
||||
'SignatureMethod': 'HmacSHA256'},
|
||||
'GET', 'server', '/foo'))
|
||||
|
||||
def test_generate_with_non_string_or_unicode_param(self):
|
||||
self.assertEquals('99IAgCkhTR2aMTgRobnzKGuNxVFSdb7vlQRvnj3Urqk=',
|
||||
self.signer.generate(
|
||||
{'AnotherParam': ClassWithStrRepr(),
|
||||
'SignatureVersion': '2',
|
||||
'SignatureMethod': 'HmacSHA256'},
|
||||
'GET', 'server', '/foo'))
|
||||
|
||||
def test_generate_unknown_version(self):
|
||||
self.assertRaises(exception.NovaException,
|
||||
self.signer.generate,
|
||||
{'SignatureMethod': 'HmacSHA256', 'SignatureVersion': '9'},
|
||||
'GET', 'server', '/foo')
|
Loading…
Reference in New Issue
Block a user