Massive refactor of users.py
Split users.py into manager.py and ldpadriver.py Added tons of docstrings Cleaned up public methods Simplified manager singleton handling
This commit is contained in:
parent
b09e69c557
commit
a3ca587654
@ -29,7 +29,7 @@ from nova import flags
|
|||||||
from nova import rpc
|
from nova import rpc
|
||||||
from nova import server
|
from nova import server
|
||||||
from nova import utils
|
from nova import utils
|
||||||
from nova.auth import users
|
from nova.auth import manager
|
||||||
from nova.compute import model
|
from nova.compute import model
|
||||||
from nova.endpoint import admin
|
from nova.endpoint import admin
|
||||||
from nova.endpoint import api
|
from nova.endpoint import api
|
||||||
|
@ -27,7 +27,7 @@ import time
|
|||||||
|
|
||||||
from nova import flags
|
from nova import flags
|
||||||
from nova import utils
|
from nova import utils
|
||||||
from nova.auth import users
|
from nova.auth import manager
|
||||||
from nova.compute import model
|
from nova.compute import model
|
||||||
from nova.compute import network
|
from nova.compute import network
|
||||||
from nova.cloudpipe import pipelib
|
from nova.cloudpipe import pipelib
|
||||||
@ -42,7 +42,7 @@ class NetworkCommands(object):
|
|||||||
|
|
||||||
class VpnCommands(object):
|
class VpnCommands(object):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.manager = users.UserManager.instance()
|
self.manager = manager.AuthManager()
|
||||||
self.instdir = model.InstanceDirectory()
|
self.instdir = model.InstanceDirectory()
|
||||||
self.pipe = pipelib.CloudPipe(cloud.CloudController())
|
self.pipe = pipelib.CloudPipe(cloud.CloudController())
|
||||||
|
|
||||||
@ -90,7 +90,7 @@ class VpnCommands(object):
|
|||||||
|
|
||||||
class RoleCommands(object):
|
class RoleCommands(object):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.manager = users.UserManager.instance()
|
self.manager = manager.AuthManager()
|
||||||
|
|
||||||
def add(self, user, role, project=None):
|
def add(self, user, role, project=None):
|
||||||
"""adds role to user
|
"""adds role to user
|
||||||
@ -113,7 +113,7 @@ class RoleCommands(object):
|
|||||||
|
|
||||||
class UserCommands(object):
|
class UserCommands(object):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.manager = users.UserManager.instance()
|
self.manager = manager.AuthManager()
|
||||||
|
|
||||||
def __print_export(self, user):
|
def __print_export(self, user):
|
||||||
print 'export EC2_ACCESS_KEY=%s' % user.access
|
print 'export EC2_ACCESS_KEY=%s' % user.access
|
||||||
@ -153,7 +153,7 @@ class UserCommands(object):
|
|||||||
|
|
||||||
class ProjectCommands(object):
|
class ProjectCommands(object):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.manager = users.UserManager.instance()
|
self.manager = manager.AuthManager()
|
||||||
|
|
||||||
def add(self, project, user):
|
def add(self, project, user):
|
||||||
"""adds user to project
|
"""adds user to project
|
||||||
|
@ -28,7 +28,7 @@ from tornado import ioloop
|
|||||||
from nova import flags
|
from nova import flags
|
||||||
from nova import server
|
from nova import server
|
||||||
from nova import utils
|
from nova import utils
|
||||||
from nova.auth import users
|
from nova.auth import manager
|
||||||
from nova.objectstore import handler
|
from nova.objectstore import handler
|
||||||
|
|
||||||
|
|
||||||
@ -39,7 +39,7 @@ def main(argv):
|
|||||||
# FIXME: if this log statement isn't here, no logging
|
# FIXME: if this log statement isn't here, no logging
|
||||||
# appears from other files and app won't start daemonized
|
# appears from other files and app won't start daemonized
|
||||||
logging.debug('Started HTTP server on %s' % (FLAGS.s3_internal_port))
|
logging.debug('Started HTTP server on %s' % (FLAGS.s3_internal_port))
|
||||||
app = handler.Application(users.UserManager())
|
app = handler.Application(manager.AuthManager())
|
||||||
server = httpserver.HTTPServer(app)
|
server = httpserver.HTTPServer(app)
|
||||||
server.listen(FLAGS.s3_internal_port)
|
server.listen(FLAGS.s3_internal_port)
|
||||||
ioloop.IOLoop.instance().start()
|
ioloop.IOLoop.instance().start()
|
||||||
|
@ -28,14 +28,14 @@ from nova import flags
|
|||||||
from nova import rpc
|
from nova import rpc
|
||||||
from nova import server
|
from nova import server
|
||||||
from nova import utils
|
from nova import utils
|
||||||
from nova.auth import users
|
from nova.auth import manager
|
||||||
from nova.endpoint import rackspace
|
from nova.endpoint import rackspace
|
||||||
|
|
||||||
FLAGS = flags.FLAGS
|
FLAGS = flags.FLAGS
|
||||||
flags.DEFINE_integer('cc_port', 8773, 'cloud controller port')
|
flags.DEFINE_integer('cc_port', 8773, 'cloud controller port')
|
||||||
|
|
||||||
def main(_argv):
|
def main(_argv):
|
||||||
user_manager = users.UserManager()
|
user_manager = manager.AuthManager()
|
||||||
api_instance = rackspace.Api(user_manager)
|
api_instance = rackspace.Api(user_manager)
|
||||||
conn = rpc.Connection.instance()
|
conn = rpc.Connection.instance()
|
||||||
rpc_consumer = rpc.AdapterConsumer(connection=conn,
|
rpc_consumer = rpc.AdapterConsumer(connection=conn,
|
||||||
|
428
nova/auth/ldapdriver.py
Normal file
428
nova/auth/ldapdriver.py
Normal file
@ -0,0 +1,428 @@
|
|||||||
|
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||||
|
|
||||||
|
# Copyright 2010 United States Government as represented by the
|
||||||
|
# Administrator of the National Aeronautics and Space Administration.
|
||||||
|
# All Rights Reserved.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
|
||||||
|
"""
|
||||||
|
Auth driver for ldap
|
||||||
|
|
||||||
|
It should be easy to create a replacement for this driver supporting
|
||||||
|
other backends by creating another class that exposes the same
|
||||||
|
public methods.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from nova import exception
|
||||||
|
from nova import flags
|
||||||
|
from nova.auth import manager
|
||||||
|
|
||||||
|
try:
|
||||||
|
import ldap
|
||||||
|
except Exception, e:
|
||||||
|
from nova.auth import fakeldap as ldap
|
||||||
|
# NOTE(vish): this import is so we can use fakeldap even when real ldap
|
||||||
|
# is installed.
|
||||||
|
from nova.auth import fakeldap
|
||||||
|
|
||||||
|
FLAGS = flags.FLAGS
|
||||||
|
flags.DEFINE_string('ldap_url', 'ldap://localhost',
|
||||||
|
'Point this at your ldap server')
|
||||||
|
flags.DEFINE_string('ldap_password', 'changeme', 'LDAP password')
|
||||||
|
flags.DEFINE_string('ldap_user_dn', 'cn=Manager,dc=example,dc=com',
|
||||||
|
'DN of admin user')
|
||||||
|
flags.DEFINE_string('ldap_user_unit', 'Users', 'OID for Users')
|
||||||
|
flags.DEFINE_string('ldap_user_subtree', 'ou=Users,dc=example,dc=com',
|
||||||
|
'OU for Users')
|
||||||
|
flags.DEFINE_string('ldap_project_subtree', 'ou=Groups,dc=example,dc=com',
|
||||||
|
'OU for Projects')
|
||||||
|
flags.DEFINE_string('role_project_subtree', 'ou=Groups,dc=example,dc=com',
|
||||||
|
'OU for Roles')
|
||||||
|
|
||||||
|
# NOTE(vish): mapping with these flags is necessary because we're going
|
||||||
|
# to tie in to an existing ldap schema
|
||||||
|
flags.DEFINE_string('ldap_cloudadmin',
|
||||||
|
'cn=cloudadmins,ou=Groups,dc=example,dc=com', 'cn for Cloud Admins')
|
||||||
|
flags.DEFINE_string('ldap_itsec',
|
||||||
|
'cn=itsec,ou=Groups,dc=example,dc=com', 'cn for ItSec')
|
||||||
|
flags.DEFINE_string('ldap_sysadmin',
|
||||||
|
'cn=sysadmins,ou=Groups,dc=example,dc=com', 'cn for Sysadmins')
|
||||||
|
flags.DEFINE_string('ldap_netadmin',
|
||||||
|
'cn=netadmins,ou=Groups,dc=example,dc=com', 'cn for NetAdmins')
|
||||||
|
flags.DEFINE_string('ldap_developer',
|
||||||
|
'cn=developers,ou=Groups,dc=example,dc=com', 'cn for Developers')
|
||||||
|
|
||||||
|
|
||||||
|
class LdapDriver(object):
|
||||||
|
def __enter__(self):
|
||||||
|
"""Creates the connection to LDAP"""
|
||||||
|
if FLAGS.fake_users:
|
||||||
|
self.NO_SUCH_OBJECT = fakeldap.NO_SUCH_OBJECT
|
||||||
|
self.OBJECT_CLASS_VIOLATION = fakeldap.OBJECT_CLASS_VIOLATION
|
||||||
|
self.conn = fakeldap.initialize(FLAGS.ldap_url)
|
||||||
|
else:
|
||||||
|
self.NO_SUCH_OBJECT = ldap.NO_SUCH_OBJECT
|
||||||
|
self.OBJECT_CLASS_VIOLATION = ldap.OBJECT_CLASS_VIOLATION
|
||||||
|
self.conn = ldap.initialize(FLAGS.ldap_url)
|
||||||
|
self.conn.simple_bind_s(FLAGS.ldap_user_dn, FLAGS.ldap_password)
|
||||||
|
return self
|
||||||
|
|
||||||
|
def __exit__(self, type, value, traceback):
|
||||||
|
"""Destroys the connection to LDAP"""
|
||||||
|
self.conn.unbind_s()
|
||||||
|
return False
|
||||||
|
|
||||||
|
def get_user(self, uid):
|
||||||
|
attr = self.__find_object(self.__uid_to_dn(uid),
|
||||||
|
'(objectclass=novaUser)')
|
||||||
|
return self.__to_user(attr)
|
||||||
|
|
||||||
|
def get_user_from_access_key(self, access):
|
||||||
|
query = '(accessKey=%s)' % access
|
||||||
|
dn = FLAGS.ldap_user_subtree
|
||||||
|
return self.__to_user(self.__find_object(dn, query))
|
||||||
|
|
||||||
|
def get_key_pair(self, uid, key_name):
|
||||||
|
dn = 'cn=%s,%s' % (key_name,
|
||||||
|
self.__uid_to_dn(uid))
|
||||||
|
attr = self.__find_object(dn, '(objectclass=novaKeyPair)')
|
||||||
|
return self.__to_key_pair(uid, attr)
|
||||||
|
|
||||||
|
def get_project(self, name):
|
||||||
|
dn = 'cn=%s,%s' % (name,
|
||||||
|
FLAGS.ldap_project_subtree)
|
||||||
|
attr = self.__find_object(dn, '(objectclass=novaProject)')
|
||||||
|
return self.__to_project(attr)
|
||||||
|
|
||||||
|
def get_users(self):
|
||||||
|
attrs = self.__find_objects(FLAGS.ldap_user_subtree,
|
||||||
|
'(objectclass=novaUser)')
|
||||||
|
return [self.__to_user(attr) for attr in attrs]
|
||||||
|
|
||||||
|
def get_key_pairs(self, uid):
|
||||||
|
attrs = self.__find_objects(self.__uid_to_dn(uid),
|
||||||
|
'(objectclass=novaKeyPair)')
|
||||||
|
return [self.__to_key_pair(uid, attr) for attr in attrs]
|
||||||
|
|
||||||
|
def get_projects(self):
|
||||||
|
attrs = self.__find_objects(FLAGS.ldap_project_subtree,
|
||||||
|
'(objectclass=novaProject)')
|
||||||
|
return [self.__to_project(attr) for attr in attrs]
|
||||||
|
|
||||||
|
def create_user(self, name, access_key, secret_key, is_admin):
|
||||||
|
if self.__user_exists(name):
|
||||||
|
raise exception.Duplicate("LDAP user %s already exists" % name)
|
||||||
|
attr = [
|
||||||
|
('objectclass', ['person',
|
||||||
|
'organizationalPerson',
|
||||||
|
'inetOrgPerson',
|
||||||
|
'novaUser']),
|
||||||
|
('ou', [FLAGS.ldap_user_unit]),
|
||||||
|
('uid', [name]),
|
||||||
|
('sn', [name]),
|
||||||
|
('cn', [name]),
|
||||||
|
('secretKey', [secret_key]),
|
||||||
|
('accessKey', [access_key]),
|
||||||
|
('isAdmin', [str(is_admin).upper()]),
|
||||||
|
]
|
||||||
|
self.conn.add_s(self.__uid_to_dn(name), attr)
|
||||||
|
return self.__to_user(dict(attr))
|
||||||
|
|
||||||
|
def create_key_pair(self, uid, key_name, public_key, fingerprint):
|
||||||
|
"""create's a public key in the directory underneath the user"""
|
||||||
|
# TODO(vish): possibly refactor this to store keys in their own ou
|
||||||
|
# and put dn reference in the user object
|
||||||
|
attr = [
|
||||||
|
('objectclass', ['novaKeyPair']),
|
||||||
|
('cn', [key_name]),
|
||||||
|
('sshPublicKey', [public_key]),
|
||||||
|
('keyFingerprint', [fingerprint]),
|
||||||
|
]
|
||||||
|
self.conn.add_s('cn=%s,%s' % (key_name,
|
||||||
|
self.__uid_to_dn(uid)),
|
||||||
|
attr)
|
||||||
|
return self.__to_key_pair(uid, dict(attr))
|
||||||
|
|
||||||
|
def create_project(self, name, manager_uid,
|
||||||
|
description=None, member_uids=None):
|
||||||
|
if self.__project_exists(name):
|
||||||
|
raise exception.Duplicate("Project can't be created because "
|
||||||
|
"project %s already exists" % name)
|
||||||
|
if not self.__user_exists(manager_uid):
|
||||||
|
raise exception.NotFound("Project can't be created because "
|
||||||
|
"manager %s doesn't exist" % manager_uid)
|
||||||
|
manager_dn = self.__uid_to_dn(manager_uid)
|
||||||
|
# description is a required attribute
|
||||||
|
if description is None:
|
||||||
|
description = name
|
||||||
|
members = []
|
||||||
|
if member_uids != None:
|
||||||
|
for member_uid in member_uids:
|
||||||
|
if not self.__user_exists(member_uid):
|
||||||
|
raise exception.NotFound("Project can't be created "
|
||||||
|
"because user %s doesn't exist" % member_uid)
|
||||||
|
members.append(self.__uid_to_dn(member_uid))
|
||||||
|
# always add the manager as a member because members is required
|
||||||
|
if not manager_dn in members:
|
||||||
|
members.append(manager_dn)
|
||||||
|
attr = [
|
||||||
|
('objectclass', ['novaProject']),
|
||||||
|
('cn', [name]),
|
||||||
|
('description', [description]),
|
||||||
|
('projectManager', [manager_dn]),
|
||||||
|
('member', members)
|
||||||
|
]
|
||||||
|
self.conn.add_s('cn=%s,%s' % (name, FLAGS.ldap_project_subtree), attr)
|
||||||
|
return self.__to_project(dict(attr))
|
||||||
|
|
||||||
|
def add_to_project(self, uid, project_id):
|
||||||
|
dn = 'cn=%s,%s' % (project_id, FLAGS.ldap_project_subtree)
|
||||||
|
return self.__add_to_group(uid, dn)
|
||||||
|
|
||||||
|
def remove_from_project(self, uid, project_id):
|
||||||
|
dn = 'cn=%s,%s' % (project_id, FLAGS.ldap_project_subtree)
|
||||||
|
return self.__remove_from_group(uid, dn)
|
||||||
|
|
||||||
|
def is_in_project(self, uid, project_id):
|
||||||
|
dn = 'cn=%s,%s' % (project_id, FLAGS.ldap_project_subtree)
|
||||||
|
return self.__is_in_group(uid, dn)
|
||||||
|
|
||||||
|
def has_role(self, uid, role, project_id=None):
|
||||||
|
role_dn = self.__role_to_dn(role, project_id)
|
||||||
|
return self.__is_in_group(uid, role_dn)
|
||||||
|
|
||||||
|
def add_role(self, uid, role, project_id=None):
|
||||||
|
role_dn = self.__role_to_dn(role, project_id)
|
||||||
|
if not self.__group_exists(role_dn):
|
||||||
|
# create the role if it doesn't exist
|
||||||
|
description = '%s role for %s' % (role, project_id)
|
||||||
|
self.__create_group(role_dn, role, uid, description)
|
||||||
|
else:
|
||||||
|
return self.__add_to_group(uid, role_dn)
|
||||||
|
|
||||||
|
def remove_role(self, uid, role, project_id=None):
|
||||||
|
role_dn = self.__role_to_dn(role, project_id)
|
||||||
|
return self.__remove_from_group(uid, role_dn)
|
||||||
|
|
||||||
|
def delete_user(self, uid):
|
||||||
|
if not self.__user_exists(uid):
|
||||||
|
raise exception.NotFound("User %s doesn't exist" % uid)
|
||||||
|
self.__delete_key_pairs(uid)
|
||||||
|
self.__remove_from_all(uid)
|
||||||
|
self.conn.delete_s('uid=%s,%s' % (uid,
|
||||||
|
FLAGS.ldap_user_subtree))
|
||||||
|
|
||||||
|
def delete_key_pair(self, uid, key_name):
|
||||||
|
if not self.__key_pair_exists(uid, key_name):
|
||||||
|
raise exception.NotFound("Key Pair %s doesn't exist for user %s" %
|
||||||
|
(key_name, uid))
|
||||||
|
self.conn.delete_s('cn=%s,uid=%s,%s' % (key_name, uid,
|
||||||
|
FLAGS.ldap_user_subtree))
|
||||||
|
|
||||||
|
def delete_project(self, name):
|
||||||
|
project_dn = 'cn=%s,%s' % (name, FLAGS.ldap_project_subtree)
|
||||||
|
self.__delete_roles(project_dn)
|
||||||
|
self.__delete_group(project_dn)
|
||||||
|
|
||||||
|
def __user_exists(self, name):
|
||||||
|
return self.get_user(name) != None
|
||||||
|
|
||||||
|
def __key_pair_exists(self, uid, key_name):
|
||||||
|
return self.get_key_pair(uid, key_name) != None
|
||||||
|
|
||||||
|
def __project_exists(self, name):
|
||||||
|
return self.get_project(name) != None
|
||||||
|
|
||||||
|
def __find_object(self, dn, query = None):
|
||||||
|
objects = self.__find_objects(dn, query)
|
||||||
|
if len(objects) == 0:
|
||||||
|
return None
|
||||||
|
return objects[0]
|
||||||
|
|
||||||
|
def __find_dns(self, dn, query=None):
|
||||||
|
try:
|
||||||
|
res = self.conn.search_s(dn, ldap.SCOPE_SUBTREE, query)
|
||||||
|
except self.NO_SUCH_OBJECT:
|
||||||
|
return []
|
||||||
|
# just return the DNs
|
||||||
|
return [dn for dn, attributes in res]
|
||||||
|
|
||||||
|
def __find_objects(self, dn, query = None):
|
||||||
|
try:
|
||||||
|
res = self.conn.search_s(dn, ldap.SCOPE_SUBTREE, query)
|
||||||
|
except self.NO_SUCH_OBJECT:
|
||||||
|
return []
|
||||||
|
# just return the attributes
|
||||||
|
return [attributes for dn, attributes in res]
|
||||||
|
|
||||||
|
def __find_role_dns(self, tree):
|
||||||
|
return self.__find_dns(tree,
|
||||||
|
'(&(objectclass=groupOfNames)(!(objectclass=novaProject)))')
|
||||||
|
|
||||||
|
def __find_group_dns_with_member(self, tree, uid):
|
||||||
|
dns = self.__find_dns(tree,
|
||||||
|
'(&(objectclass=groupOfNames)(member=%s))' %
|
||||||
|
self.__uid_to_dn(uid))
|
||||||
|
return dns
|
||||||
|
|
||||||
|
def __group_exists(self, dn):
|
||||||
|
return self.__find_object(dn, '(objectclass=groupOfNames)') != None
|
||||||
|
|
||||||
|
def __delete_key_pairs(self, uid):
|
||||||
|
keys = self.get_key_pairs(uid)
|
||||||
|
if keys != None:
|
||||||
|
for key in keys:
|
||||||
|
self.delete_key_pair(uid, key.name)
|
||||||
|
|
||||||
|
def __role_to_dn(self, role, project_id=None):
|
||||||
|
if project_id == None:
|
||||||
|
return FLAGS.__getitem__("ldap_%s" % role).value
|
||||||
|
else:
|
||||||
|
return 'cn=%s,cn=%s,%s' % (role,
|
||||||
|
project_id,
|
||||||
|
FLAGS.ldap_project_subtree)
|
||||||
|
|
||||||
|
def __create_group(self, group_dn, name, uid,
|
||||||
|
description, member_uids = None):
|
||||||
|
if self.__group_exists(group_dn):
|
||||||
|
raise exception.Duplicate("Group can't be created because "
|
||||||
|
"group %s already exists" % name)
|
||||||
|
members = []
|
||||||
|
if member_uids != None:
|
||||||
|
for member_uid in member_uids:
|
||||||
|
if not self.__user_exists(member_uid):
|
||||||
|
raise exception.NotFound("Group can't be created "
|
||||||
|
"because user %s doesn't exist" % member_uid)
|
||||||
|
members.append(self.__uid_to_dn(member_uid))
|
||||||
|
dn = self.__uid_to_dn(uid)
|
||||||
|
if not dn in members:
|
||||||
|
members.append(dn)
|
||||||
|
attr = [
|
||||||
|
('objectclass', ['groupOfNames']),
|
||||||
|
('cn', [name]),
|
||||||
|
('description', [description]),
|
||||||
|
('member', members)
|
||||||
|
]
|
||||||
|
self.conn.add_s(group_dn, attr)
|
||||||
|
|
||||||
|
def __is_in_group(self, uid, group_dn):
|
||||||
|
if not self.__user_exists(uid):
|
||||||
|
raise exception.NotFound("User %s can't be searched in group "
|
||||||
|
"becuase the user doesn't exist" % (uid,))
|
||||||
|
if not self.__group_exists(group_dn):
|
||||||
|
return False
|
||||||
|
res = self.__find_object(group_dn,
|
||||||
|
'(member=%s)' % self.__uid_to_dn(uid))
|
||||||
|
return res != None
|
||||||
|
|
||||||
|
def __add_to_group(self, uid, group_dn):
|
||||||
|
if not self.__user_exists(uid):
|
||||||
|
raise exception.NotFound("User %s can't be added to the group "
|
||||||
|
"becuase the user doesn't exist" % (uid,))
|
||||||
|
if not self.__group_exists(group_dn):
|
||||||
|
raise exception.NotFound("The group at dn %s doesn't exist" %
|
||||||
|
(group_dn,))
|
||||||
|
if self.__is_in_group(uid, group_dn):
|
||||||
|
raise exception.Duplicate("User %s is already a member of "
|
||||||
|
"the group %s" % (uid, group_dn))
|
||||||
|
attr = [
|
||||||
|
(ldap.MOD_ADD, 'member', self.__uid_to_dn(uid))
|
||||||
|
]
|
||||||
|
self.conn.modify_s(group_dn, attr)
|
||||||
|
|
||||||
|
def __remove_from_group(self, uid, group_dn):
|
||||||
|
if not self.__group_exists(group_dn):
|
||||||
|
raise exception.NotFound("The group at dn %s doesn't exist" %
|
||||||
|
(group_dn,))
|
||||||
|
if not self.__user_exists(uid):
|
||||||
|
raise exception.NotFound("User %s can't be removed from the "
|
||||||
|
"group because the user doesn't exist" % (uid,))
|
||||||
|
if not self.__is_in_group(uid, group_dn):
|
||||||
|
raise exception.NotFound("User %s is not a member of the group" %
|
||||||
|
(uid,))
|
||||||
|
self.__safe_remove_from_group(group_dn, uid)
|
||||||
|
|
||||||
|
def __safe_remove_from_group(self, group_dn, uid):
|
||||||
|
# FIXME(vish): what if deleted user is a project manager?
|
||||||
|
attr = [(ldap.MOD_DELETE, 'member', self.__uid_to_dn(uid))]
|
||||||
|
try:
|
||||||
|
self.conn.modify_s(group_dn, attr)
|
||||||
|
except self.OBJECT_CLASS_VIOLATION:
|
||||||
|
logging.debug("Attempted to remove the last member of a group. "
|
||||||
|
"Deleting the group at %s instead." % group_dn )
|
||||||
|
self.__delete_group(group_dn)
|
||||||
|
|
||||||
|
def __remove_from_all(self, uid):
|
||||||
|
if not self.__user_exists(uid):
|
||||||
|
raise exception.NotFound("User %s can't be removed from all "
|
||||||
|
"because the user doesn't exist" % (uid,))
|
||||||
|
dn = self.__uid_to_dn(uid)
|
||||||
|
role_dns = self.__find_group_dns_with_member(
|
||||||
|
FLAGS.role_project_subtree, uid)
|
||||||
|
for role_dn in role_dns:
|
||||||
|
self.__safe_remove_from_group(role_dn, uid)
|
||||||
|
project_dns = self.__find_group_dns_with_member(
|
||||||
|
FLAGS.ldap_project_subtree, uid)
|
||||||
|
for project_dn in project_dns:
|
||||||
|
self.__safe_remove_from_group(project_dn, uid)
|
||||||
|
|
||||||
|
def __delete_group(self, group_dn):
|
||||||
|
if not self.__group_exists(group_dn):
|
||||||
|
raise exception.NotFound("Group at dn %s doesn't exist" % group_dn)
|
||||||
|
self.conn.delete_s(group_dn)
|
||||||
|
|
||||||
|
def __delete_roles(self, project_dn):
|
||||||
|
for role_dn in self.__find_role_dns(project_dn):
|
||||||
|
self.__delete_group(role_dn)
|
||||||
|
|
||||||
|
def __to_user(self, attr):
|
||||||
|
if attr == None:
|
||||||
|
return None
|
||||||
|
return manager.User(
|
||||||
|
id = attr['uid'][0],
|
||||||
|
name = attr['cn'][0],
|
||||||
|
access = attr['accessKey'][0],
|
||||||
|
secret = attr['secretKey'][0],
|
||||||
|
admin = (attr['isAdmin'][0] == 'TRUE')
|
||||||
|
)
|
||||||
|
|
||||||
|
def __to_key_pair(self, owner, attr):
|
||||||
|
if attr == None:
|
||||||
|
return None
|
||||||
|
return manager.KeyPair(
|
||||||
|
id = attr['cn'][0],
|
||||||
|
owner_id = owner,
|
||||||
|
public_key = attr['sshPublicKey'][0],
|
||||||
|
fingerprint = attr['keyFingerprint'][0],
|
||||||
|
)
|
||||||
|
|
||||||
|
def __to_project(self, attr):
|
||||||
|
if attr == None:
|
||||||
|
return None
|
||||||
|
member_dns = attr.get('member', [])
|
||||||
|
return manager.Project(
|
||||||
|
id = attr['cn'][0],
|
||||||
|
project_manager_id = self.__dn_to_uid(attr['projectManager'][0]),
|
||||||
|
description = attr.get('description', [None])[0],
|
||||||
|
member_ids = [self.__dn_to_uid(x) for x in member_dns]
|
||||||
|
)
|
||||||
|
|
||||||
|
def __dn_to_uid(self, dn):
|
||||||
|
return dn.split(',')[0].split('=')[1]
|
||||||
|
|
||||||
|
def __uid_to_dn(self, dn):
|
||||||
|
return 'uid=%s,%s' % (dn, FLAGS.ldap_user_subtree)
|
||||||
|
|
741
nova/auth/manager.py
Normal file
741
nova/auth/manager.py
Normal file
@ -0,0 +1,741 @@
|
|||||||
|
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||||
|
|
||||||
|
# Copyright 2010 United States Government as represented by the
|
||||||
|
# Administrator of the National Aeronautics and Space Administration.
|
||||||
|
# All Rights Reserved.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
|
||||||
|
"""
|
||||||
|
Nova authentication management
|
||||||
|
"""
|
||||||
|
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
import shutil
|
||||||
|
import string
|
||||||
|
import tempfile
|
||||||
|
import uuid
|
||||||
|
import zipfile
|
||||||
|
|
||||||
|
from nova import crypto
|
||||||
|
from nova import datastore
|
||||||
|
from nova import exception
|
||||||
|
from nova import flags
|
||||||
|
from nova import objectstore # for flags
|
||||||
|
from nova import signer
|
||||||
|
from nova import utils
|
||||||
|
from nova.auth import ldapdriver
|
||||||
|
FLAGS = flags.FLAGS
|
||||||
|
|
||||||
|
# NOTE(vish): a user with one of these roles will be a superuser and
|
||||||
|
# have access to all api commands
|
||||||
|
flags.DEFINE_list('superuser_roles', ['cloudadmin'],
|
||||||
|
'roles that ignore rbac checking completely')
|
||||||
|
|
||||||
|
# NOTE(vish): a user with one of these roles will have it for every
|
||||||
|
# project, even if he or she is not a member of the project
|
||||||
|
flags.DEFINE_list('global_roles', ['cloudadmin', 'itsec'],
|
||||||
|
'roles that apply to all projects')
|
||||||
|
|
||||||
|
flags.DEFINE_string('credentials_template',
|
||||||
|
utils.abspath('auth/novarc.template'),
|
||||||
|
'Template for creating users rc file')
|
||||||
|
flags.DEFINE_string('vpn_client_template',
|
||||||
|
utils.abspath('cloudpipe/client.ovpn.template'),
|
||||||
|
'Template for creating users vpn file')
|
||||||
|
flags.DEFINE_string('credential_key_file', 'pk.pem',
|
||||||
|
'Filename of private key in credentials zip')
|
||||||
|
flags.DEFINE_string('credential_cert_file', 'cert.pem',
|
||||||
|
'Filename of certificate in credentials zip')
|
||||||
|
flags.DEFINE_string('credential_rc_file', 'novarc',
|
||||||
|
'Filename of rc in credentials zip')
|
||||||
|
|
||||||
|
flags.DEFINE_integer('vpn_start_port', 1000,
|
||||||
|
'Start port for the cloudpipe VPN servers')
|
||||||
|
flags.DEFINE_integer('vpn_end_port', 2000,
|
||||||
|
'End port for the cloudpipe VPN servers')
|
||||||
|
|
||||||
|
flags.DEFINE_string('credential_cert_subject',
|
||||||
|
'/C=US/ST=California/L=MountainView/O=AnsoLabs/'
|
||||||
|
'OU=NovaDev/CN=%s-%s',
|
||||||
|
'Subject for certificate for users')
|
||||||
|
|
||||||
|
flags.DEFINE_string('vpn_ip', '127.0.0.1',
|
||||||
|
'Public IP for the cloudpipe VPN servers')
|
||||||
|
|
||||||
|
|
||||||
|
class AuthBase(object):
|
||||||
|
"""Base class for objects relating to auth
|
||||||
|
|
||||||
|
Objects derived from this class should be stupid data objects with
|
||||||
|
an id member. They may optionally contain methods that delegate to
|
||||||
|
AuthManager, but should not implement logic themselves.
|
||||||
|
"""
|
||||||
|
@classmethod
|
||||||
|
def safe_id(cls, obj):
|
||||||
|
"""Safe get object id
|
||||||
|
|
||||||
|
This method will return the id of the object if the object
|
||||||
|
is of this class, otherwise it will return the original object.
|
||||||
|
This allows methods to accept objects or ids as paramaters.
|
||||||
|
|
||||||
|
"""
|
||||||
|
if isinstance(obj, cls):
|
||||||
|
return obj.id
|
||||||
|
else:
|
||||||
|
return obj
|
||||||
|
|
||||||
|
|
||||||
|
class User(AuthBase):
|
||||||
|
"""Object representing a user"""
|
||||||
|
def __init__(self, id, name, access, secret, admin):
|
||||||
|
self.id = id
|
||||||
|
self.name = name
|
||||||
|
self.access = access
|
||||||
|
self.secret = secret
|
||||||
|
self.admin = admin
|
||||||
|
|
||||||
|
def is_superuser(self):
|
||||||
|
return AuthManager().is_superuser(self)
|
||||||
|
|
||||||
|
def is_admin(self):
|
||||||
|
return AuthManager().is_admin(self)
|
||||||
|
|
||||||
|
def has_role(self, role):
|
||||||
|
return AuthManager().has_role(self, role)
|
||||||
|
|
||||||
|
def add_role(self, role):
|
||||||
|
return AuthManager().add_role(self, role)
|
||||||
|
|
||||||
|
def remove_role(self, role):
|
||||||
|
return AuthManager().remove_role(self, role)
|
||||||
|
|
||||||
|
def is_project_member(self, project):
|
||||||
|
return AuthManager().is_project_member(self, project)
|
||||||
|
|
||||||
|
def is_project_manager(self, project):
|
||||||
|
return AuthManager().is_project_manager(self, project)
|
||||||
|
|
||||||
|
def generate_key_pair(self, name):
|
||||||
|
return AuthManager().generate_key_pair(self.id, name)
|
||||||
|
|
||||||
|
def create_key_pair(self, name, public_key, fingerprint):
|
||||||
|
return AuthManager().create_key_pair(self.id,
|
||||||
|
name,
|
||||||
|
public_key,
|
||||||
|
fingerprint)
|
||||||
|
|
||||||
|
def get_key_pair(self, name):
|
||||||
|
return AuthManager().get_key_pair(self.id, name)
|
||||||
|
|
||||||
|
def delete_key_pair(self, name):
|
||||||
|
return AuthManager().delete_key_pair(self.id, name)
|
||||||
|
|
||||||
|
def get_key_pairs(self):
|
||||||
|
return AuthManager().get_key_pairs(self.id)
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return "User('%s', '%s', '%s', '%s', %s)" % (self.id,
|
||||||
|
self.name,
|
||||||
|
self.access,
|
||||||
|
self.secret,
|
||||||
|
self.admin)
|
||||||
|
|
||||||
|
|
||||||
|
class KeyPair(AuthBase):
|
||||||
|
"""Represents an ssh key returned from the datastore
|
||||||
|
|
||||||
|
Even though this object is named KeyPair, only the public key and
|
||||||
|
fingerprint is stored. The user's private key is not saved.
|
||||||
|
"""
|
||||||
|
def __init__(self, id, owner_id, public_key, fingerprint):
|
||||||
|
self.id = id
|
||||||
|
self.name = id
|
||||||
|
self.owner_id = owner_id
|
||||||
|
self.public_key = public_key
|
||||||
|
self.fingerprint = fingerprint
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return "KeyPair('%s', '%s', '%s', '%s')" % (self.id,
|
||||||
|
self.owner_id,
|
||||||
|
self.public_key,
|
||||||
|
self.fingerprint)
|
||||||
|
|
||||||
|
|
||||||
|
class Project(AuthBase):
|
||||||
|
"""Represents a Project returned from the datastore"""
|
||||||
|
def __init__(self, id, project_manager_id, description, member_ids):
|
||||||
|
self.project_manager_id = project_manager_id
|
||||||
|
self.id = id
|
||||||
|
self.name = id
|
||||||
|
self.description = description
|
||||||
|
self.member_ids = member_ids
|
||||||
|
|
||||||
|
@property
|
||||||
|
def project_manager(self):
|
||||||
|
return AuthManager().get_user(self.project_manager_id)
|
||||||
|
|
||||||
|
def has_manager(self, user):
|
||||||
|
return AuthManager().is_project_manager(user, self)
|
||||||
|
|
||||||
|
def has_member(self, user):
|
||||||
|
return AuthManager().is_project_member(user, self)
|
||||||
|
|
||||||
|
def add_role(self, user, role):
|
||||||
|
return AuthManager().add_role(user, role, self)
|
||||||
|
|
||||||
|
def remove_role(self, user, role):
|
||||||
|
return AuthManager().remove_role(user, role, self)
|
||||||
|
|
||||||
|
def has_role(self, user, role):
|
||||||
|
return AuthManager().has_role(user, role, self)
|
||||||
|
|
||||||
|
def get_credentials(self, user):
|
||||||
|
return AuthManager().get_credentials(user, self)
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return "Project('%s', '%s', '%s', %s)" % (self.id,
|
||||||
|
self.project_manager_id,
|
||||||
|
self.description,
|
||||||
|
self.member_ids)
|
||||||
|
|
||||||
|
|
||||||
|
class NoMorePorts(exception.Error):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class Vpn(datastore.BasicModel):
|
||||||
|
"""Manages vpn ips and ports for projects"""
|
||||||
|
def __init__(self, project_id):
|
||||||
|
self.project_id = project_id
|
||||||
|
super(Vpn, self).__init__()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def identifier(self):
|
||||||
|
return self.project_id
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def create(cls, project_id):
|
||||||
|
# TODO(vish): get list of vpn ips from redis
|
||||||
|
port = cls.find_free_port_for_ip(FLAGS.vpn_ip)
|
||||||
|
vpn = cls(project_id)
|
||||||
|
# save ip for project
|
||||||
|
vpn['project'] = project_id
|
||||||
|
vpn['ip'] = FLAGS.vpn_ip
|
||||||
|
vpn['port'] = port
|
||||||
|
vpn.save()
|
||||||
|
return vpn
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def find_free_port_for_ip(cls, ip):
|
||||||
|
# TODO(vish): these redis commands should be generalized and
|
||||||
|
# placed into a base class. Conceptually, it is
|
||||||
|
# similar to an association, but we are just
|
||||||
|
# storing a set of values instead of keys that
|
||||||
|
# should be turned into objects.
|
||||||
|
redis = datastore.Redis.instance()
|
||||||
|
key = 'ip:%s:ports' % ip
|
||||||
|
# TODO(vish): these ports should be allocated through an admin
|
||||||
|
# command instead of a flag
|
||||||
|
if (not redis.exists(key) and
|
||||||
|
not redis.exists(cls._redis_association_name('ip', ip))):
|
||||||
|
for i in range(FLAGS.vpn_start_port, FLAGS.vpn_end_port + 1):
|
||||||
|
redis.sadd(key, i)
|
||||||
|
|
||||||
|
port = redis.spop(key)
|
||||||
|
if not port:
|
||||||
|
raise NoMorePorts()
|
||||||
|
return port
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def num_ports_for_ip(cls, ip):
|
||||||
|
return datastore.Redis.instance().scard('ip:%s:ports' % ip)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def ip(self):
|
||||||
|
return self['ip']
|
||||||
|
|
||||||
|
@property
|
||||||
|
def port(self):
|
||||||
|
return int(self['port'])
|
||||||
|
|
||||||
|
def save(self):
|
||||||
|
self.associate_with('ip', self.ip)
|
||||||
|
super(Vpn, self).save()
|
||||||
|
|
||||||
|
def destroy(self):
|
||||||
|
self.unassociate_with('ip', self.ip)
|
||||||
|
datastore.Redis.instance().sadd('ip:%s:ports' % self.ip, self.port)
|
||||||
|
super(Vpn, self).destroy()
|
||||||
|
|
||||||
|
|
||||||
|
class AuthManager(object):
|
||||||
|
"""Manager Singleton for dealing with Users, Projects, and Keypairs
|
||||||
|
|
||||||
|
Methods accept objects or ids.
|
||||||
|
|
||||||
|
AuthManager uses a driver object to make requests to the data backend.
|
||||||
|
See ldapdriver.LdapDriver for reference.
|
||||||
|
|
||||||
|
AuthManager also manages associated data related to Auth objects that
|
||||||
|
need to be more accessible, such as vpn ips and ports.
|
||||||
|
"""
|
||||||
|
_instance=None
|
||||||
|
def __new__(cls, *args, **kwargs):
|
||||||
|
if not cls._instance:
|
||||||
|
cls._instance = super(AuthManager, cls).__new__(
|
||||||
|
cls, *args, **kwargs)
|
||||||
|
return cls._instance
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
self.driver_class = kwargs.get('driver_class', ldapdriver.LdapDriver)
|
||||||
|
if FLAGS.fake_tests:
|
||||||
|
try:
|
||||||
|
self.create_user('fake', 'fake', 'fake')
|
||||||
|
except: pass
|
||||||
|
try:
|
||||||
|
self.create_user('user', 'user', 'user')
|
||||||
|
except: pass
|
||||||
|
try:
|
||||||
|
self.create_user('admin', 'admin', 'admin', True)
|
||||||
|
except: pass
|
||||||
|
|
||||||
|
def authenticate(self, access, signature, params, verb='GET',
|
||||||
|
server_string='127.0.0.1:8773', path='/',
|
||||||
|
verify_signature=True):
|
||||||
|
"""Authenticates AWS request using access key and signature
|
||||||
|
|
||||||
|
If the project is not specified, attempts to authenticate to
|
||||||
|
a project with the same name as the user. This way, older tools
|
||||||
|
that have no project knowledge will still work.
|
||||||
|
|
||||||
|
@type access: str
|
||||||
|
@param access: Access key for user in the form "access:project".
|
||||||
|
|
||||||
|
@type signature: str
|
||||||
|
@param signature: Signature of the request.
|
||||||
|
|
||||||
|
@type params: list of str
|
||||||
|
@param params: Web paramaters used for the signature.
|
||||||
|
|
||||||
|
@type verb: str
|
||||||
|
@param verb: Web request verb ('GET' or 'POST').
|
||||||
|
|
||||||
|
@type server_string: str
|
||||||
|
@param server_string: Web request server string.
|
||||||
|
|
||||||
|
@type path: str
|
||||||
|
@param path: Web request path.
|
||||||
|
|
||||||
|
@type verify_signature: bool
|
||||||
|
@param verify_signature: Whether to verify the signature.
|
||||||
|
|
||||||
|
@rtype: tuple (User, Project)
|
||||||
|
@return: User and project that the request represents.
|
||||||
|
"""
|
||||||
|
# TODO(vish): check for valid timestamp
|
||||||
|
(access_key, sep, project_name) = access.partition(':')
|
||||||
|
|
||||||
|
user = self.get_user_from_access_key(access_key)
|
||||||
|
if user == None:
|
||||||
|
raise exception.NotFound('No user found for access key %s' %
|
||||||
|
access_key)
|
||||||
|
if project_name is '':
|
||||||
|
project_name = user.name
|
||||||
|
|
||||||
|
project = self.get_project(project_name)
|
||||||
|
if project == None:
|
||||||
|
raise exception.NotFound('No project called %s could be found' %
|
||||||
|
project_name)
|
||||||
|
if not self.is_admin(user) and not self.is_project_member(user,
|
||||||
|
project):
|
||||||
|
raise exception.NotFound('User %s is not a member of project %s' %
|
||||||
|
(user.id, project.id))
|
||||||
|
if verify_signature:
|
||||||
|
# NOTE(vish): hmac can't handle unicode, so encode ensures that
|
||||||
|
# secret isn't unicode
|
||||||
|
expected_signature = signer.Signer(user.secret.encode()).generate(
|
||||||
|
params, verb, server_string, path)
|
||||||
|
logging.debug('user.secret: %s', user.secret)
|
||||||
|
logging.debug('expected_signature: %s', expected_signature)
|
||||||
|
logging.debug('signature: %s', signature)
|
||||||
|
if signature != expected_signature:
|
||||||
|
raise exception.NotAuthorized('Signature does not match')
|
||||||
|
return (user, project)
|
||||||
|
|
||||||
|
def is_superuser(self, user):
|
||||||
|
"""Checks for superuser status, allowing user to bypass rbac
|
||||||
|
|
||||||
|
@type user: User or uid
|
||||||
|
@param user: User to check.
|
||||||
|
|
||||||
|
@rtype: bool
|
||||||
|
@return: True for superuser.
|
||||||
|
"""
|
||||||
|
if not isinstance(user, User):
|
||||||
|
user = self.get_user(user)
|
||||||
|
# NOTE(vish): admin flag on user represents superuser
|
||||||
|
if user.admin:
|
||||||
|
return True
|
||||||
|
for role in FLAGS.superuser_roles:
|
||||||
|
if self.has_role(user, role):
|
||||||
|
return True
|
||||||
|
|
||||||
|
def is_admin(self, user):
|
||||||
|
"""Checks for admin status, allowing user to access all projects
|
||||||
|
|
||||||
|
@type user: User or uid
|
||||||
|
@param user: User to check.
|
||||||
|
|
||||||
|
@rtype: bool
|
||||||
|
@return: True for admin.
|
||||||
|
"""
|
||||||
|
if not isinstance(user, User):
|
||||||
|
user = self.get_user(user)
|
||||||
|
if self.is_superuser(user):
|
||||||
|
return True
|
||||||
|
for role in FLAGS.global_roles:
|
||||||
|
if self.has_role(user, role):
|
||||||
|
return True
|
||||||
|
|
||||||
|
def has_role(self, user, role, project=None):
|
||||||
|
"""Checks existence of role for user
|
||||||
|
|
||||||
|
If project is not specified, checks for a global role. If project
|
||||||
|
is specified, checks for the union of the global role and the
|
||||||
|
project role.
|
||||||
|
|
||||||
|
Role 'projectmanager' only works for projects and simply checks to
|
||||||
|
see if the user is the project_manager of the specified project. It
|
||||||
|
is the same as calling is_project_manager(user, project).
|
||||||
|
|
||||||
|
@type user: User or uid
|
||||||
|
@param user: User to check.
|
||||||
|
|
||||||
|
@type role: str
|
||||||
|
@param role: Role to check.
|
||||||
|
|
||||||
|
@type project: Project or project_id
|
||||||
|
@param project: Project in which to look for local role.
|
||||||
|
|
||||||
|
@rtype: bool
|
||||||
|
@return: True if the user has the role.
|
||||||
|
"""
|
||||||
|
with self.driver_class() as drv:
|
||||||
|
if role == 'projectmanager':
|
||||||
|
if not project:
|
||||||
|
raise exception.Error("Must specify project")
|
||||||
|
return self.is_project_manager(user, project)
|
||||||
|
|
||||||
|
global_role = drv.has_role(User.safe_id(user),
|
||||||
|
role,
|
||||||
|
None)
|
||||||
|
if not global_role:
|
||||||
|
return global_role
|
||||||
|
|
||||||
|
if not project or role in FLAGS.global_roles:
|
||||||
|
return global_role
|
||||||
|
|
||||||
|
return drv.has_role(User.safe_id(user),
|
||||||
|
role,
|
||||||
|
Project.safe_id(project))
|
||||||
|
|
||||||
|
def add_role(self, user, role, project=None):
|
||||||
|
"""Adds role for user
|
||||||
|
|
||||||
|
If project is not specified, adds a global role. If project
|
||||||
|
is specified, adds a local role.
|
||||||
|
|
||||||
|
The 'projectmanager' role is special and can't be added or removed.
|
||||||
|
|
||||||
|
@type user: User or uid
|
||||||
|
@param user: User to which to add role.
|
||||||
|
|
||||||
|
@type role: str
|
||||||
|
@param role: Role to add.
|
||||||
|
|
||||||
|
@type project: Project or project_id
|
||||||
|
@param project: Project in which to add local role.
|
||||||
|
"""
|
||||||
|
with self.driver_class() as drv:
|
||||||
|
drv.add_role(User.safe_id(user), role, Project.safe_id(project))
|
||||||
|
|
||||||
|
def remove_role(self, user, role, project=None):
|
||||||
|
"""Removes role for user
|
||||||
|
|
||||||
|
If project is not specified, removes a global role. If project
|
||||||
|
is specified, removes a local role.
|
||||||
|
|
||||||
|
The 'projectmanager' role is special and can't be added or removed.
|
||||||
|
|
||||||
|
@type user: User or uid
|
||||||
|
@param user: User from which to remove role.
|
||||||
|
|
||||||
|
@type role: str
|
||||||
|
@param role: Role to remove.
|
||||||
|
|
||||||
|
@type project: Project or project_id
|
||||||
|
@param project: Project in which to remove local role.
|
||||||
|
"""
|
||||||
|
with self.driver_class() as drv:
|
||||||
|
drv.remove_role(User.safe_id(user), role, Project.safe_id(project))
|
||||||
|
|
||||||
|
def create_project(self, name, manager_user,
|
||||||
|
description=None, member_users=None):
|
||||||
|
"""Create a project
|
||||||
|
|
||||||
|
@type name: str
|
||||||
|
@param name: Name of the project to create. The name will also be
|
||||||
|
used as the project id.
|
||||||
|
|
||||||
|
@type manager_user: User or uid
|
||||||
|
@param manager_user: This user will be the project manager.
|
||||||
|
|
||||||
|
@type description: str
|
||||||
|
@param project: Description of the project. If no description is
|
||||||
|
specified, the name of the project will be used.
|
||||||
|
|
||||||
|
@type member_users: list of User or uid
|
||||||
|
@param: Initial project members. The project manager will always be
|
||||||
|
added as a member, even if he isn't specified in this list.
|
||||||
|
|
||||||
|
@rtype: Project
|
||||||
|
@return: The new project.
|
||||||
|
"""
|
||||||
|
if member_users:
|
||||||
|
member_users = [User.safe_id(u) for u in member_users]
|
||||||
|
# NOTE(vish): try to associate a vpn ip and port first because
|
||||||
|
# if it throws an exception, we save having to
|
||||||
|
# create and destroy a project
|
||||||
|
Vpn.create(name)
|
||||||
|
with self.driver_class() as drv:
|
||||||
|
return drv.create_project(name,
|
||||||
|
User.safe_id(manager_user),
|
||||||
|
description,
|
||||||
|
member_users)
|
||||||
|
|
||||||
|
def get_projects(self):
|
||||||
|
"""Retrieves list of all projects"""
|
||||||
|
with self.driver_class() as drv:
|
||||||
|
return drv.get_projects()
|
||||||
|
|
||||||
|
|
||||||
|
def get_project(self, project):
|
||||||
|
"""Get project object by id"""
|
||||||
|
with self.driver_class() as drv:
|
||||||
|
return drv.get_project(Project.safe_id(project))
|
||||||
|
|
||||||
|
def add_to_project(self, user, project):
|
||||||
|
"""Add user to project"""
|
||||||
|
with self.driver_class() as drv:
|
||||||
|
return drv.add_to_project(User.safe_id(user),
|
||||||
|
Project.safe_id(project))
|
||||||
|
|
||||||
|
def is_project_manager(self, user, project):
|
||||||
|
"""Checks if user is project manager"""
|
||||||
|
if not isinstance(project, Project):
|
||||||
|
project = self.get_project(project)
|
||||||
|
return User.safe_id(user) == project.project_manager_id
|
||||||
|
|
||||||
|
def is_project_member(self, user, project):
|
||||||
|
"""Checks to see if user is a member of project"""
|
||||||
|
if not isinstance(project, Project):
|
||||||
|
project = self.get_project(project)
|
||||||
|
return User.safe_id(user) in project.member_ids
|
||||||
|
|
||||||
|
def remove_from_project(self, user, project):
|
||||||
|
"""Removes a user from a project"""
|
||||||
|
with self.driver_class() as drv:
|
||||||
|
return drv.remove_from_project(User.safe_id(user),
|
||||||
|
Project.safe_id(project))
|
||||||
|
|
||||||
|
def delete_project(self, project):
|
||||||
|
"""Deletes a project"""
|
||||||
|
with self.driver_class() as drv:
|
||||||
|
return drv.delete_project(Project.safe_id(project))
|
||||||
|
|
||||||
|
def get_user(self, uid):
|
||||||
|
"""Retrieves a user by id"""
|
||||||
|
with self.driver_class() as drv:
|
||||||
|
return drv.get_user(uid)
|
||||||
|
|
||||||
|
def get_user_from_access_key(self, access_key):
|
||||||
|
"""Retrieves a user by access key"""
|
||||||
|
with self.driver_class() as drv:
|
||||||
|
return drv.get_user_from_access_key(access_key)
|
||||||
|
|
||||||
|
def get_users(self):
|
||||||
|
"""Retrieves a list of all users"""
|
||||||
|
with self.driver_class() as drv:
|
||||||
|
return drv.get_users()
|
||||||
|
|
||||||
|
def create_user(self, user, access=None, secret=None,
|
||||||
|
admin=False, create_project=True):
|
||||||
|
"""Creates a user
|
||||||
|
|
||||||
|
@type user: str
|
||||||
|
@param name: Name of the user to create. The name will also be
|
||||||
|
used as the user id.
|
||||||
|
|
||||||
|
@type access: str
|
||||||
|
@param access: Access Key (defaults to a random uuid)
|
||||||
|
|
||||||
|
@type secret: str
|
||||||
|
@param secret: Secret Key (defaults to a random uuid)
|
||||||
|
|
||||||
|
@type admin: bool
|
||||||
|
@param admin: Whether to set the admin flag. The admin flag gives
|
||||||
|
superuser status regardless of roles specifed for the user.
|
||||||
|
|
||||||
|
@type create_project: bool
|
||||||
|
@param: Whether to create a project for the user with the same name.
|
||||||
|
|
||||||
|
@rtype: User
|
||||||
|
@return: The new user.
|
||||||
|
"""
|
||||||
|
if access == None: access = str(uuid.uuid4())
|
||||||
|
if secret == None: secret = str(uuid.uuid4())
|
||||||
|
with self.driver_class() as drv:
|
||||||
|
user = User.safe_id(user)
|
||||||
|
result = drv.create_user(user, access, secret, admin)
|
||||||
|
if create_project:
|
||||||
|
# NOTE(vish): if the project creation fails, we delete
|
||||||
|
# the user and return an exception
|
||||||
|
try:
|
||||||
|
drv.create_project(user, user, user)
|
||||||
|
except Exception:
|
||||||
|
with self.driver_class() as drv:
|
||||||
|
drv.delete_user(user)
|
||||||
|
raise
|
||||||
|
return result
|
||||||
|
|
||||||
|
def delete_user(self, user, delete_project=True):
|
||||||
|
"""Deletes a user"""
|
||||||
|
with self.driver_class() as drv:
|
||||||
|
user = User.safe_id(user)
|
||||||
|
if delete_project:
|
||||||
|
try:
|
||||||
|
drv.delete_project(user)
|
||||||
|
except exception.NotFound:
|
||||||
|
pass
|
||||||
|
drv.delete_user(user)
|
||||||
|
|
||||||
|
def generate_key_pair(self, user, key_name):
|
||||||
|
"""Generates a key pair for a user
|
||||||
|
|
||||||
|
Generates a public and private key, stores the public key using the
|
||||||
|
key_name, and returns the private key and fingerprint.
|
||||||
|
|
||||||
|
@type user: User or uid
|
||||||
|
@param user: User for which to create key pair.
|
||||||
|
|
||||||
|
@type key_name: str
|
||||||
|
@param key_name: Name to use for the generated KeyPair.
|
||||||
|
|
||||||
|
@rtype: tuple (private_key, fingerprint)
|
||||||
|
@return: A tuple containing the private_key and fingerprint.
|
||||||
|
"""
|
||||||
|
# NOTE(vish): generating key pair is slow so check for legal
|
||||||
|
# creation before creating keypair
|
||||||
|
uid = User.safe_id(user)
|
||||||
|
with self.driver_class() as drv:
|
||||||
|
if not drv.get_user(uid):
|
||||||
|
raise exception.NotFound("User %s doesn't exist" % user)
|
||||||
|
if drv.get_key_pair(uid, key_name):
|
||||||
|
raise exception.Duplicate("The keypair %s already exists"
|
||||||
|
% key_name)
|
||||||
|
private_key, public_key, fingerprint = crypto.generate_key_pair()
|
||||||
|
self.create_key_pair(uid, key_name, public_key, fingerprint)
|
||||||
|
return private_key, fingerprint
|
||||||
|
|
||||||
|
def create_key_pair(self, user, key_name, public_key, fingerprint):
|
||||||
|
"""Creates a key pair for user"""
|
||||||
|
with self.driver_class() as drv:
|
||||||
|
return drv.create_key_pair(User.safe_id(user), key_name,
|
||||||
|
public_key, fingerprint)
|
||||||
|
|
||||||
|
def get_key_pair(self, user, key_name):
|
||||||
|
"""Retrieves a key pair for user"""
|
||||||
|
with self.driver_class() as drv:
|
||||||
|
return drv.get_key_pair(User.safe_id(user), key_name)
|
||||||
|
|
||||||
|
def get_key_pairs(self, user):
|
||||||
|
"""Retrieves all key pairs for user"""
|
||||||
|
with self.driver_class() as drv:
|
||||||
|
return drv.get_key_pairs(User.safe_id(user))
|
||||||
|
|
||||||
|
def delete_key_pair(self, user, key_name):
|
||||||
|
"""Deletes a key pair for user"""
|
||||||
|
with self.driver_class() as drv:
|
||||||
|
drv.delete_key_pair(User.safe_id(user), key_name)
|
||||||
|
|
||||||
|
def get_credentials(self, user, project=None):
|
||||||
|
"""Get credential zip for user in project"""
|
||||||
|
if not isinstance(user, User):
|
||||||
|
user = self.get_user(user)
|
||||||
|
if project is None:
|
||||||
|
project = user.id
|
||||||
|
pid = Project.safe_id(project)
|
||||||
|
rc = self.__generate_rc(user.access, user.secret, pid)
|
||||||
|
private_key, signed_cert = self.__generate_x509_cert(user.id, pid)
|
||||||
|
|
||||||
|
vpn = Vpn(pid)
|
||||||
|
configfile = open(FLAGS.vpn_client_template,"r")
|
||||||
|
s = string.Template(configfile.read())
|
||||||
|
configfile.close()
|
||||||
|
config = s.substitute(keyfile=FLAGS.credential_key_file,
|
||||||
|
certfile=FLAGS.credential_cert_file,
|
||||||
|
ip=vpn.ip,
|
||||||
|
port=vpn.port)
|
||||||
|
|
||||||
|
tmpdir = tempfile.mkdtemp()
|
||||||
|
zf = os.path.join(tmpdir, "temp.zip")
|
||||||
|
zippy = zipfile.ZipFile(zf, 'w')
|
||||||
|
zippy.writestr(FLAGS.credential_rc_file, rc)
|
||||||
|
zippy.writestr(FLAGS.credential_key_file, private_key)
|
||||||
|
zippy.writestr(FLAGS.credential_cert_file, signed_cert)
|
||||||
|
zippy.writestr("nebula-client.conf", config)
|
||||||
|
zippy.writestr(FLAGS.ca_file, crypto.fetch_ca(user.id))
|
||||||
|
zippy.close()
|
||||||
|
with open(zf, 'rb') as f:
|
||||||
|
buffer = f.read()
|
||||||
|
|
||||||
|
shutil.rmtree(tmpdir)
|
||||||
|
return buffer
|
||||||
|
|
||||||
|
def __generate_rc(self, access, secret, pid):
|
||||||
|
"""Generate rc file for user"""
|
||||||
|
rc = open(FLAGS.credentials_template).read()
|
||||||
|
rc = rc % { 'access': access,
|
||||||
|
'project': pid,
|
||||||
|
'secret': secret,
|
||||||
|
'ec2': FLAGS.ec2_url,
|
||||||
|
's3': 'http://%s:%s' % (FLAGS.s3_host, FLAGS.s3_port),
|
||||||
|
'nova': FLAGS.ca_file,
|
||||||
|
'cert': FLAGS.credential_cert_file,
|
||||||
|
'key': FLAGS.credential_key_file,
|
||||||
|
}
|
||||||
|
return rc
|
||||||
|
|
||||||
|
def __generate_x509_cert(self, uid, pid):
|
||||||
|
"""Generate x509 cert for user"""
|
||||||
|
(private_key, csr) = crypto.generate_x509_cert(
|
||||||
|
self.__cert_subject(uid))
|
||||||
|
# TODO(joshua): This should be async call back to the cloud controller
|
||||||
|
signed_cert = crypto.sign_csr(csr, pid)
|
||||||
|
return (private_key, signed_cert)
|
||||||
|
|
||||||
|
def __cert_subject(self, uid):
|
||||||
|
"""Helper to generate cert subject"""
|
||||||
|
return FLAGS.credential_cert_subject % (uid, utils.isotime())
|
@ -17,7 +17,7 @@
|
|||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
from nova import exception
|
from nova import exception
|
||||||
from nova.auth import users
|
from nova.auth import manager
|
||||||
|
|
||||||
|
|
||||||
def allow(*roles):
|
def allow(*roles):
|
||||||
|
@ -1,974 +0,0 @@
|
|||||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
|
||||||
|
|
||||||
# Copyright 2010 United States Government as represented by the
|
|
||||||
# Administrator of the National Aeronautics and Space Administration.
|
|
||||||
# All Rights Reserved.
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
||||||
# not use this file except in compliance with the License. You may obtain
|
|
||||||
# a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
# License for the specific language governing permissions and limitations
|
|
||||||
# under the License.
|
|
||||||
|
|
||||||
"""
|
|
||||||
Nova users and user management, including RBAC hooks.
|
|
||||||
"""
|
|
||||||
|
|
||||||
import datetime
|
|
||||||
import logging
|
|
||||||
import os
|
|
||||||
import shutil
|
|
||||||
import signer
|
|
||||||
import string
|
|
||||||
import tempfile
|
|
||||||
import uuid
|
|
||||||
import zipfile
|
|
||||||
|
|
||||||
try:
|
|
||||||
import ldap
|
|
||||||
except Exception, e:
|
|
||||||
import fakeldap as ldap
|
|
||||||
|
|
||||||
import fakeldap
|
|
||||||
|
|
||||||
# TODO(termie): clean up these imports
|
|
||||||
from nova import datastore
|
|
||||||
from nova import exception
|
|
||||||
from nova import flags
|
|
||||||
from nova import crypto
|
|
||||||
from nova import utils
|
|
||||||
from nova import objectstore # for flags
|
|
||||||
|
|
||||||
FLAGS = flags.FLAGS
|
|
||||||
|
|
||||||
flags.DEFINE_string('ldap_url', 'ldap://localhost',
|
|
||||||
'Point this at your ldap server')
|
|
||||||
flags.DEFINE_string('ldap_password', 'changeme', 'LDAP password')
|
|
||||||
flags.DEFINE_string('user_dn', 'cn=Manager,dc=example,dc=com',
|
|
||||||
'DN of admin user')
|
|
||||||
flags.DEFINE_string('user_unit', 'Users', 'OID for Users')
|
|
||||||
flags.DEFINE_string('user_ldap_subtree', 'ou=Users,dc=example,dc=com',
|
|
||||||
'OU for Users')
|
|
||||||
flags.DEFINE_string('project_ldap_subtree', 'ou=Groups,dc=example,dc=com',
|
|
||||||
'OU for Projects')
|
|
||||||
flags.DEFINE_string('role_ldap_subtree', 'ou=Groups,dc=example,dc=com',
|
|
||||||
'OU for Roles')
|
|
||||||
|
|
||||||
# NOTE(vish): mapping with these flags is necessary because we're going
|
|
||||||
# to tie in to an existing ldap schema
|
|
||||||
flags.DEFINE_string('ldap_cloudadmin',
|
|
||||||
'cn=cloudadmins,ou=Groups,dc=example,dc=com', 'cn for Cloud Admins')
|
|
||||||
flags.DEFINE_string('ldap_itsec',
|
|
||||||
'cn=itsec,ou=Groups,dc=example,dc=com', 'cn for ItSec')
|
|
||||||
flags.DEFINE_string('ldap_sysadmin',
|
|
||||||
'cn=sysadmins,ou=Groups,dc=example,dc=com', 'cn for Sysadmins')
|
|
||||||
flags.DEFINE_string('ldap_netadmin',
|
|
||||||
'cn=netadmins,ou=Groups,dc=example,dc=com', 'cn for NetAdmins')
|
|
||||||
flags.DEFINE_string('ldap_developer',
|
|
||||||
'cn=developers,ou=Groups,dc=example,dc=com', 'cn for Developers')
|
|
||||||
|
|
||||||
# NOTE(vish): a user with one of these roles will be a superuser and
|
|
||||||
# have access to all api commands
|
|
||||||
flags.DEFINE_list('superuser_roles', ['cloudadmin'],
|
|
||||||
'roles that ignore rbac checking completely')
|
|
||||||
|
|
||||||
# NOTE(vish): a user with one of these roles will have it for every
|
|
||||||
# project, even if he or she is not a member of the project
|
|
||||||
flags.DEFINE_list('global_roles', ['cloudadmin', 'itsec'],
|
|
||||||
'roles that apply to all projects')
|
|
||||||
|
|
||||||
flags.DEFINE_string('credentials_template',
|
|
||||||
utils.abspath('auth/novarc.template'),
|
|
||||||
'Template for creating users rc file')
|
|
||||||
flags.DEFINE_string('vpn_client_template',
|
|
||||||
utils.abspath('cloudpipe/client.ovpn.template'),
|
|
||||||
'Template for creating users vpn file')
|
|
||||||
flags.DEFINE_string('credential_key_file', 'pk.pem',
|
|
||||||
'Filename of private key in credentials zip')
|
|
||||||
flags.DEFINE_string('credential_cert_file', 'cert.pem',
|
|
||||||
'Filename of certificate in credentials zip')
|
|
||||||
flags.DEFINE_string('credential_rc_file', 'novarc',
|
|
||||||
'Filename of rc in credentials zip')
|
|
||||||
|
|
||||||
flags.DEFINE_integer('vpn_start_port', 1000,
|
|
||||||
'Start port for the cloudpipe VPN servers')
|
|
||||||
flags.DEFINE_integer('vpn_end_port', 2000,
|
|
||||||
'End port for the cloudpipe VPN servers')
|
|
||||||
|
|
||||||
flags.DEFINE_string('credential_cert_subject',
|
|
||||||
'/C=US/ST=California/L=MountainView/O=AnsoLabs/'
|
|
||||||
'OU=NovaDev/CN=%s-%s',
|
|
||||||
'Subject for certificate for users')
|
|
||||||
|
|
||||||
flags.DEFINE_string('vpn_ip', '127.0.0.1',
|
|
||||||
'Public IP for the cloudpipe VPN servers')
|
|
||||||
|
|
||||||
|
|
||||||
class AuthBase(object):
|
|
||||||
@classmethod
|
|
||||||
def safe_id(cls, obj):
|
|
||||||
"""Safe get object id.
|
|
||||||
|
|
||||||
This method will return the id of the object if the object
|
|
||||||
is of this class, otherwise it will return the original object.
|
|
||||||
This allows methods to accept objects or ids as paramaters.
|
|
||||||
|
|
||||||
"""
|
|
||||||
if isinstance(obj, cls):
|
|
||||||
return obj.id
|
|
||||||
else:
|
|
||||||
return obj
|
|
||||||
|
|
||||||
|
|
||||||
class User(AuthBase):
|
|
||||||
"""id and name are currently the same"""
|
|
||||||
def __init__(self, id, name, access, secret, admin):
|
|
||||||
self.id = id
|
|
||||||
self.name = name
|
|
||||||
self.access = access
|
|
||||||
self.secret = secret
|
|
||||||
self.admin = admin
|
|
||||||
|
|
||||||
def is_superuser(self):
|
|
||||||
"""allows user to bypass rbac completely"""
|
|
||||||
if self.admin:
|
|
||||||
return True
|
|
||||||
for role in FLAGS.superuser_roles:
|
|
||||||
if self.has_role(role):
|
|
||||||
return True
|
|
||||||
|
|
||||||
def is_admin(self):
|
|
||||||
"""allows user to see objects from all projects"""
|
|
||||||
if self.is_superuser():
|
|
||||||
return True
|
|
||||||
for role in FLAGS.global_roles:
|
|
||||||
if self.has_role(role):
|
|
||||||
return True
|
|
||||||
|
|
||||||
def has_role(self, role):
|
|
||||||
return UserManager.instance().has_role(self, role)
|
|
||||||
|
|
||||||
def add_role(self, role):
|
|
||||||
return UserManager.instance().add_role(self, role)
|
|
||||||
|
|
||||||
def remove_role(self, role):
|
|
||||||
return UserManager.instance().remove_role(self, role)
|
|
||||||
|
|
||||||
def is_project_member(self, project):
|
|
||||||
return UserManager.instance().is_project_member(self, project)
|
|
||||||
|
|
||||||
def is_project_manager(self, project):
|
|
||||||
return UserManager.instance().is_project_manager(self, project)
|
|
||||||
|
|
||||||
def generate_rc(self, project=None):
|
|
||||||
if project is None:
|
|
||||||
project = self.id
|
|
||||||
rc = open(FLAGS.credentials_template).read()
|
|
||||||
rc = rc % { 'access': self.access,
|
|
||||||
'project': project,
|
|
||||||
'secret': self.secret,
|
|
||||||
'ec2': FLAGS.ec2_url,
|
|
||||||
's3': 'http://%s:%s' % (FLAGS.s3_host, FLAGS.s3_port),
|
|
||||||
'nova': FLAGS.ca_file,
|
|
||||||
'cert': FLAGS.credential_cert_file,
|
|
||||||
'key': FLAGS.credential_key_file,
|
|
||||||
}
|
|
||||||
return rc
|
|
||||||
|
|
||||||
def generate_key_pair(self, name):
|
|
||||||
return UserManager.instance().generate_key_pair(self.id, name)
|
|
||||||
|
|
||||||
def create_key_pair(self, name, public_key, fingerprint):
|
|
||||||
return UserManager.instance().create_key_pair(self.id,
|
|
||||||
name,
|
|
||||||
public_key,
|
|
||||||
fingerprint)
|
|
||||||
|
|
||||||
def get_key_pair(self, name):
|
|
||||||
return UserManager.instance().get_key_pair(self.id, name)
|
|
||||||
|
|
||||||
def delete_key_pair(self, name):
|
|
||||||
return UserManager.instance().delete_key_pair(self.id, name)
|
|
||||||
|
|
||||||
def get_key_pairs(self):
|
|
||||||
return UserManager.instance().get_key_pairs(self.id)
|
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
return "User('%s', '%s', '%s', '%s', %s)" % (
|
|
||||||
self.id, self.name, self.access, self.secret, self.admin)
|
|
||||||
|
|
||||||
|
|
||||||
class KeyPair(AuthBase):
|
|
||||||
def __init__(self, id, owner_id, public_key, fingerprint):
|
|
||||||
self.id = id
|
|
||||||
self.name = id
|
|
||||||
self.owner_id = owner_id
|
|
||||||
self.public_key = public_key
|
|
||||||
self.fingerprint = fingerprint
|
|
||||||
|
|
||||||
def delete(self):
|
|
||||||
return UserManager.instance().delete_key_pair(self.owner, self.name)
|
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
return "KeyPair('%s', '%s', '%s', '%s')" % (
|
|
||||||
self.id, self.owner_id, self.public_key, self.fingerprint)
|
|
||||||
|
|
||||||
|
|
||||||
class Group(AuthBase):
|
|
||||||
"""id and name are currently the same"""
|
|
||||||
def __init__(self, id, description = None, member_ids = None):
|
|
||||||
self.id = id
|
|
||||||
self.name = id
|
|
||||||
self.description = description
|
|
||||||
self.member_ids = member_ids
|
|
||||||
|
|
||||||
def has_member(self, user):
|
|
||||||
return User.safe_id(user) in self.member_ids
|
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
return "Group('%s', '%s', %s)" % (
|
|
||||||
self.id, self.description, self.member_ids)
|
|
||||||
|
|
||||||
|
|
||||||
class Project(Group):
|
|
||||||
def __init__(self, id, project_manager_id, description, member_ids):
|
|
||||||
self.project_manager_id = project_manager_id
|
|
||||||
super(Project, self).__init__(id, description, member_ids)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def project_manager(self):
|
|
||||||
return UserManager.instance().get_user(self.project_manager_id)
|
|
||||||
|
|
||||||
def has_manager(self, user):
|
|
||||||
return User.safe_id(user) == self.project_manager_id
|
|
||||||
|
|
||||||
def add_role(self, user, role):
|
|
||||||
return UserManager.instance().add_role(user, role, self)
|
|
||||||
|
|
||||||
def remove_role(self, user, role):
|
|
||||||
return UserManager.instance().remove_role(user, role, self)
|
|
||||||
|
|
||||||
def has_role(self, user, role):
|
|
||||||
return UserManager.instance().has_role(user, role, self)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def vpn_ip(self):
|
|
||||||
return Vpn(self.id).ip
|
|
||||||
|
|
||||||
@property
|
|
||||||
def vpn_port(self):
|
|
||||||
return Vpn(self.id).port
|
|
||||||
|
|
||||||
def get_credentials(self, user):
|
|
||||||
if not isinstance(user, User):
|
|
||||||
user = UserManager.instance().get_user(user)
|
|
||||||
rc = user.generate_rc(self.id)
|
|
||||||
private_key, signed_cert = self.generate_x509_cert(user)
|
|
||||||
|
|
||||||
configfile = open(FLAGS.vpn_client_template,"r")
|
|
||||||
s = string.Template(configfile.read())
|
|
||||||
configfile.close()
|
|
||||||
config = s.substitute(keyfile=FLAGS.credential_key_file,
|
|
||||||
certfile=FLAGS.credential_cert_file,
|
|
||||||
ip=self.vpn_ip,
|
|
||||||
port=self.vpn_port)
|
|
||||||
|
|
||||||
tmpdir = tempfile.mkdtemp()
|
|
||||||
zf = os.path.join(tmpdir, "temp.zip")
|
|
||||||
zippy = zipfile.ZipFile(zf, 'w')
|
|
||||||
zippy.writestr(FLAGS.credential_rc_file, rc)
|
|
||||||
zippy.writestr(FLAGS.credential_key_file, private_key)
|
|
||||||
zippy.writestr(FLAGS.credential_cert_file, signed_cert)
|
|
||||||
zippy.writestr("nebula-client.conf", config)
|
|
||||||
zippy.writestr(FLAGS.ca_file, crypto.fetch_ca(self.id))
|
|
||||||
zippy.close()
|
|
||||||
with open(zf, 'rb') as f:
|
|
||||||
buffer = f.read()
|
|
||||||
|
|
||||||
shutil.rmtree(tmpdir)
|
|
||||||
return buffer
|
|
||||||
|
|
||||||
def generate_x509_cert(self, user):
|
|
||||||
return UserManager.instance().generate_x509_cert(user, self)
|
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
return "Project('%s', '%s', '%s', %s)" % (
|
|
||||||
self.id, self.project_manager_id,
|
|
||||||
self.description, self.member_ids)
|
|
||||||
|
|
||||||
|
|
||||||
class NoMorePorts(exception.Error):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class Vpn(datastore.BasicModel):
|
|
||||||
def __init__(self, project_id):
|
|
||||||
self.project_id = project_id
|
|
||||||
super(Vpn, self).__init__()
|
|
||||||
|
|
||||||
@property
|
|
||||||
def identifier(self):
|
|
||||||
return self.project_id
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def create(cls, project_id):
|
|
||||||
# TODO(vish): get list of vpn ips from redis
|
|
||||||
port = cls.find_free_port_for_ip(FLAGS.vpn_ip)
|
|
||||||
vpn = cls(project_id)
|
|
||||||
# save ip for project
|
|
||||||
vpn['project'] = project_id
|
|
||||||
vpn['ip'] = FLAGS.vpn_ip
|
|
||||||
vpn['port'] = port
|
|
||||||
vpn.save()
|
|
||||||
return vpn
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def find_free_port_for_ip(cls, ip):
|
|
||||||
# TODO(vish): these redis commands should be generalized and
|
|
||||||
# placed into a base class. Conceptually, it is
|
|
||||||
# similar to an association, but we are just
|
|
||||||
# storing a set of values instead of keys that
|
|
||||||
# should be turned into objects.
|
|
||||||
redis = datastore.Redis.instance()
|
|
||||||
key = 'ip:%s:ports' % ip
|
|
||||||
# TODO(vish): these ports should be allocated through an admin
|
|
||||||
# command instead of a flag
|
|
||||||
if (not redis.exists(key) and
|
|
||||||
not redis.exists(cls._redis_association_name('ip', ip))):
|
|
||||||
for i in range(FLAGS.vpn_start_port, FLAGS.vpn_end_port + 1):
|
|
||||||
redis.sadd(key, i)
|
|
||||||
|
|
||||||
port = redis.spop(key)
|
|
||||||
if not port:
|
|
||||||
raise NoMorePorts()
|
|
||||||
return port
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def num_ports_for_ip(cls, ip):
|
|
||||||
return datastore.Redis.instance().scard('ip:%s:ports' % ip)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def ip(self):
|
|
||||||
return self['ip']
|
|
||||||
|
|
||||||
@property
|
|
||||||
def port(self):
|
|
||||||
return int(self['port'])
|
|
||||||
|
|
||||||
def save(self):
|
|
||||||
self.associate_with('ip', self.ip)
|
|
||||||
super(Vpn, self).save()
|
|
||||||
|
|
||||||
def destroy(self):
|
|
||||||
self.unassociate_with('ip', self.ip)
|
|
||||||
datastore.Redis.instance().sadd('ip:%s:ports' % self.ip, self.port)
|
|
||||||
super(Vpn, self).destroy()
|
|
||||||
|
|
||||||
|
|
||||||
class UserManager(object):
|
|
||||||
def __init__(self):
|
|
||||||
if hasattr(self.__class__, '_instance'):
|
|
||||||
raise Exception('Attempted to instantiate singleton')
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def instance(cls):
|
|
||||||
if not hasattr(cls, '_instance'):
|
|
||||||
inst = UserManager()
|
|
||||||
cls._instance = inst
|
|
||||||
if FLAGS.fake_users:
|
|
||||||
try:
|
|
||||||
inst.create_user('fake', 'fake', 'fake')
|
|
||||||
except: pass
|
|
||||||
try:
|
|
||||||
inst.create_user('user', 'user', 'user')
|
|
||||||
except: pass
|
|
||||||
try:
|
|
||||||
inst.create_user('admin', 'admin', 'admin', True)
|
|
||||||
except: pass
|
|
||||||
return cls._instance
|
|
||||||
|
|
||||||
def authenticate(self, access, signature, params, verb='GET',
|
|
||||||
server_string='127.0.0.1:8773', path='/',
|
|
||||||
verify_signature=True):
|
|
||||||
# TODO: Check for valid timestamp
|
|
||||||
(access_key, sep, project_name) = access.partition(':')
|
|
||||||
|
|
||||||
user = self.get_user_from_access_key(access_key)
|
|
||||||
if user == None:
|
|
||||||
raise exception.NotFound('No user found for access key %s' %
|
|
||||||
access_key)
|
|
||||||
if project_name is '':
|
|
||||||
project_name = user.name
|
|
||||||
|
|
||||||
project = self.get_project(project_name)
|
|
||||||
if project == None:
|
|
||||||
raise exception.NotFound('No project called %s could be found' %
|
|
||||||
project_name)
|
|
||||||
if not user.is_admin() and not project.has_member(user):
|
|
||||||
raise exception.NotFound('User %s is not a member of project %s' %
|
|
||||||
(user.id, project.id))
|
|
||||||
if verify_signature:
|
|
||||||
# NOTE(vish): hmac can't handle unicode, so encode ensures that
|
|
||||||
# secret isn't unicode
|
|
||||||
expected_signature = signer.Signer(user.secret.encode()).generate(
|
|
||||||
params, verb, server_string, path)
|
|
||||||
logging.debug('user.secret: %s', user.secret)
|
|
||||||
logging.debug('expected_signature: %s', expected_signature)
|
|
||||||
logging.debug('signature: %s', signature)
|
|
||||||
if signature != expected_signature:
|
|
||||||
raise exception.NotAuthorized('Signature does not match')
|
|
||||||
return (user, project)
|
|
||||||
|
|
||||||
def has_role(self, user, role, project=None):
|
|
||||||
with LDAPWrapper() as conn:
|
|
||||||
if role == 'projectmanager':
|
|
||||||
if not project:
|
|
||||||
raise exception.Error("Must specify project")
|
|
||||||
return self.is_project_manager(user, project)
|
|
||||||
|
|
||||||
global_role = conn.has_role(User.safe_id(user),
|
|
||||||
role,
|
|
||||||
None)
|
|
||||||
if not global_role:
|
|
||||||
return global_role
|
|
||||||
|
|
||||||
if not project or role in FLAGS.global_roles:
|
|
||||||
return global_role
|
|
||||||
|
|
||||||
return conn.has_role(User.safe_id(user),
|
|
||||||
role,
|
|
||||||
Project.safe_id(project))
|
|
||||||
|
|
||||||
def add_role(self, user, role, project=None):
|
|
||||||
with LDAPWrapper() as conn:
|
|
||||||
return conn.add_role(User.safe_id(user), role,
|
|
||||||
Project.safe_id(project))
|
|
||||||
|
|
||||||
def remove_role(self, user, role, project=None):
|
|
||||||
with LDAPWrapper() as conn:
|
|
||||||
return conn.remove_role(User.safe_id(user), role,
|
|
||||||
Project.safe_id(project))
|
|
||||||
|
|
||||||
def create_project(self, name, manager_user,
|
|
||||||
description=None, member_users=None):
|
|
||||||
if member_users:
|
|
||||||
member_users = [User.safe_id(u) for u in member_users]
|
|
||||||
# NOTE(vish): try to associate a vpn ip and port first because
|
|
||||||
# if it throws an exception, we save having to
|
|
||||||
# create and destroy a project
|
|
||||||
Vpn.create(name)
|
|
||||||
with LDAPWrapper() as conn:
|
|
||||||
return conn.create_project(name,
|
|
||||||
User.safe_id(manager_user),
|
|
||||||
description,
|
|
||||||
member_users)
|
|
||||||
|
|
||||||
|
|
||||||
def get_projects(self):
|
|
||||||
with LDAPWrapper() as conn:
|
|
||||||
return conn.find_projects()
|
|
||||||
|
|
||||||
|
|
||||||
def get_project(self, project):
|
|
||||||
with LDAPWrapper() as conn:
|
|
||||||
return conn.find_project(Project.safe_id(project))
|
|
||||||
|
|
||||||
def add_to_project(self, user, project):
|
|
||||||
with LDAPWrapper() as conn:
|
|
||||||
return conn.add_to_project(User.safe_id(user),
|
|
||||||
Project.safe_id(project))
|
|
||||||
|
|
||||||
def is_project_manager(self, user, project):
|
|
||||||
if not isinstance(project, Project):
|
|
||||||
project = self.get_project(project)
|
|
||||||
return project.has_manager(user)
|
|
||||||
|
|
||||||
def is_project_member(self, user, project):
|
|
||||||
if isinstance(project, Project):
|
|
||||||
return project.has_member(user)
|
|
||||||
else:
|
|
||||||
with LDAPWrapper() as conn:
|
|
||||||
return conn.is_in_project(User.safe_id(user), project)
|
|
||||||
|
|
||||||
def remove_from_project(self, user, project):
|
|
||||||
with LDAPWrapper() as conn:
|
|
||||||
return conn.remove_from_project(User.safe_id(user),
|
|
||||||
Project.safe_id(project))
|
|
||||||
|
|
||||||
def delete_project(self, project):
|
|
||||||
with LDAPWrapper() as conn:
|
|
||||||
return conn.delete_project(Project.safe_id(project))
|
|
||||||
|
|
||||||
def get_user(self, uid):
|
|
||||||
with LDAPWrapper() as conn:
|
|
||||||
return conn.find_user(uid)
|
|
||||||
|
|
||||||
def get_user_from_access_key(self, access_key):
|
|
||||||
with LDAPWrapper() as conn:
|
|
||||||
return conn.find_user_by_access_key(access_key)
|
|
||||||
|
|
||||||
def get_users(self):
|
|
||||||
with LDAPWrapper() as conn:
|
|
||||||
return conn.find_users()
|
|
||||||
|
|
||||||
def create_user(self, user, access=None, secret=None,
|
|
||||||
admin=False, create_project=True):
|
|
||||||
if access == None: access = str(uuid.uuid4())
|
|
||||||
if secret == None: secret = str(uuid.uuid4())
|
|
||||||
with LDAPWrapper() as conn:
|
|
||||||
user = User.safe_id(user)
|
|
||||||
result = conn.create_user(user, access, secret, admin)
|
|
||||||
if create_project:
|
|
||||||
# NOTE(vish): if the project creation fails, we delete
|
|
||||||
# the user and return an exception
|
|
||||||
try:
|
|
||||||
conn.create_project(user, user, user)
|
|
||||||
except Exception:
|
|
||||||
with LDAPWrapper() as conn:
|
|
||||||
conn.delete_user(user)
|
|
||||||
raise
|
|
||||||
return result
|
|
||||||
|
|
||||||
def delete_user(self, user, delete_project=True):
|
|
||||||
with LDAPWrapper() as conn:
|
|
||||||
user = User.safe_id(user)
|
|
||||||
if delete_project:
|
|
||||||
try:
|
|
||||||
conn.delete_project(user)
|
|
||||||
except exception.NotFound:
|
|
||||||
pass
|
|
||||||
conn.delete_user(user)
|
|
||||||
|
|
||||||
def generate_key_pair(self, user, key_name):
|
|
||||||
# generating key pair is slow so delay generation
|
|
||||||
# until after check
|
|
||||||
user = User.safe_id(user)
|
|
||||||
with LDAPWrapper() as conn:
|
|
||||||
if not conn.user_exists(user):
|
|
||||||
raise exception.NotFound("User %s doesn't exist" % user)
|
|
||||||
if conn.key_pair_exists(user, key_name):
|
|
||||||
raise exception.Duplicate("The keypair %s already exists"
|
|
||||||
% key_name)
|
|
||||||
private_key, public_key, fingerprint = crypto.generate_key_pair()
|
|
||||||
self.create_key_pair(User.safe_id(user), key_name,
|
|
||||||
public_key, fingerprint)
|
|
||||||
return private_key, fingerprint
|
|
||||||
|
|
||||||
def create_key_pair(self, user, key_name, public_key, fingerprint):
|
|
||||||
with LDAPWrapper() as conn:
|
|
||||||
return conn.create_key_pair(User.safe_id(user), key_name,
|
|
||||||
public_key, fingerprint)
|
|
||||||
|
|
||||||
def get_key_pair(self, user, key_name):
|
|
||||||
with LDAPWrapper() as conn:
|
|
||||||
return conn.find_key_pair(User.safe_id(user), key_name)
|
|
||||||
|
|
||||||
def get_key_pairs(self, user):
|
|
||||||
with LDAPWrapper() as conn:
|
|
||||||
return conn.find_key_pairs(User.safe_id(user))
|
|
||||||
|
|
||||||
def delete_key_pair(self, user, key_name):
|
|
||||||
with LDAPWrapper() as conn:
|
|
||||||
conn.delete_key_pair(User.safe_id(user), key_name)
|
|
||||||
|
|
||||||
def generate_x509_cert(self, user, project):
|
|
||||||
(private_key, csr) = crypto.generate_x509_cert(
|
|
||||||
self.__cert_subject(User.safe_id(user)))
|
|
||||||
# TODO - This should be async call back to the cloud controller
|
|
||||||
signed_cert = crypto.sign_csr(csr, Project.safe_id(project))
|
|
||||||
return (private_key, signed_cert)
|
|
||||||
|
|
||||||
def __cert_subject(self, uid):
|
|
||||||
# FIXME(ja) - this should be pulled from a global configuration
|
|
||||||
return FLAGS.credential_cert_subject % (uid, utils.isotime())
|
|
||||||
|
|
||||||
|
|
||||||
class LDAPWrapper(object):
|
|
||||||
def __init__(self):
|
|
||||||
self.user = FLAGS.user_dn
|
|
||||||
self.passwd = FLAGS.ldap_password
|
|
||||||
|
|
||||||
def __enter__(self):
|
|
||||||
self.connect()
|
|
||||||
return self
|
|
||||||
|
|
||||||
def __exit__(self, type, value, traceback):
|
|
||||||
self.conn.unbind_s()
|
|
||||||
return False
|
|
||||||
|
|
||||||
def connect(self):
|
|
||||||
""" connect to ldap as admin user """
|
|
||||||
if FLAGS.fake_users:
|
|
||||||
self.NO_SUCH_OBJECT = fakeldap.NO_SUCH_OBJECT
|
|
||||||
self.OBJECT_CLASS_VIOLATION = fakeldap.OBJECT_CLASS_VIOLATION
|
|
||||||
self.conn = fakeldap.initialize(FLAGS.ldap_url)
|
|
||||||
else:
|
|
||||||
self.NO_SUCH_OBJECT = ldap.NO_SUCH_OBJECT
|
|
||||||
self.OBJECT_CLASS_VIOLATION = ldap.OBJECT_CLASS_VIOLATION
|
|
||||||
self.conn = ldap.initialize(FLAGS.ldap_url)
|
|
||||||
self.conn.simple_bind_s(self.user, self.passwd)
|
|
||||||
|
|
||||||
def find_object(self, dn, query = None):
|
|
||||||
objects = self.find_objects(dn, query)
|
|
||||||
if len(objects) == 0:
|
|
||||||
return None
|
|
||||||
return objects[0]
|
|
||||||
|
|
||||||
def find_dns(self, dn, query=None):
|
|
||||||
try:
|
|
||||||
res = self.conn.search_s(dn, ldap.SCOPE_SUBTREE, query)
|
|
||||||
except self.NO_SUCH_OBJECT:
|
|
||||||
return []
|
|
||||||
# just return the DNs
|
|
||||||
return [dn for dn, attributes in res]
|
|
||||||
|
|
||||||
def find_objects(self, dn, query = None):
|
|
||||||
try:
|
|
||||||
res = self.conn.search_s(dn, ldap.SCOPE_SUBTREE, query)
|
|
||||||
except self.NO_SUCH_OBJECT:
|
|
||||||
return []
|
|
||||||
# just return the attributes
|
|
||||||
return [attributes for dn, attributes in res]
|
|
||||||
|
|
||||||
def find_users(self):
|
|
||||||
attrs = self.find_objects(FLAGS.user_ldap_subtree,
|
|
||||||
'(objectclass=novaUser)')
|
|
||||||
return [self.__to_user(attr) for attr in attrs]
|
|
||||||
|
|
||||||
def find_key_pairs(self, uid):
|
|
||||||
attrs = self.find_objects(self.__uid_to_dn(uid),
|
|
||||||
'(objectclass=novaKeyPair)')
|
|
||||||
return [self.__to_key_pair(uid, attr) for attr in attrs]
|
|
||||||
|
|
||||||
def find_projects(self):
|
|
||||||
attrs = self.find_objects(FLAGS.project_ldap_subtree,
|
|
||||||
'(objectclass=novaProject)')
|
|
||||||
return [self.__to_project(attr) for attr in attrs]
|
|
||||||
|
|
||||||
def find_roles(self, tree):
|
|
||||||
attrs = self.find_objects(tree,
|
|
||||||
'(&(objectclass=groupOfNames)(!(objectclass=novaProject)))')
|
|
||||||
return [self.__to_group(attr) for attr in attrs]
|
|
||||||
|
|
||||||
def find_group_dns_with_member(self, tree, uid):
|
|
||||||
dns = self.find_dns(tree,
|
|
||||||
'(&(objectclass=groupOfNames)(member=%s))' %
|
|
||||||
self.__uid_to_dn(uid))
|
|
||||||
return dns
|
|
||||||
|
|
||||||
def find_user(self, uid):
|
|
||||||
attr = self.find_object(self.__uid_to_dn(uid),
|
|
||||||
'(objectclass=novaUser)')
|
|
||||||
return self.__to_user(attr)
|
|
||||||
|
|
||||||
def find_key_pair(self, uid, key_name):
|
|
||||||
dn = 'cn=%s,%s' % (key_name,
|
|
||||||
self.__uid_to_dn(uid))
|
|
||||||
attr = self.find_object(dn, '(objectclass=novaKeyPair)')
|
|
||||||
return self.__to_key_pair(uid, attr)
|
|
||||||
|
|
||||||
def find_group(self, dn):
|
|
||||||
"""uses dn directly instead of custructing it from name"""
|
|
||||||
attr = self.find_object(dn, '(objectclass=groupOfNames)')
|
|
||||||
return self.__to_group(attr)
|
|
||||||
|
|
||||||
def find_project(self, name):
|
|
||||||
dn = 'cn=%s,%s' % (name,
|
|
||||||
FLAGS.project_ldap_subtree)
|
|
||||||
attr = self.find_object(dn, '(objectclass=novaProject)')
|
|
||||||
return self.__to_project(attr)
|
|
||||||
|
|
||||||
def user_exists(self, name):
|
|
||||||
return self.find_user(name) != None
|
|
||||||
|
|
||||||
def key_pair_exists(self, uid, key_name):
|
|
||||||
return self.find_key_pair(uid, key_name) != None
|
|
||||||
|
|
||||||
def project_exists(self, name):
|
|
||||||
return self.find_project(name) != None
|
|
||||||
|
|
||||||
def group_exists(self, dn):
|
|
||||||
return self.find_group(dn) != None
|
|
||||||
|
|
||||||
def delete_key_pairs(self, uid):
|
|
||||||
keys = self.find_key_pairs(uid)
|
|
||||||
if keys != None:
|
|
||||||
for key in keys:
|
|
||||||
self.delete_key_pair(uid, key.name)
|
|
||||||
|
|
||||||
def create_user(self, name, access_key, secret_key, is_admin):
|
|
||||||
if self.user_exists(name):
|
|
||||||
raise exception.Duplicate("LDAP user %s already exists" % name)
|
|
||||||
attr = [
|
|
||||||
('objectclass', ['person',
|
|
||||||
'organizationalPerson',
|
|
||||||
'inetOrgPerson',
|
|
||||||
'novaUser']),
|
|
||||||
('ou', [FLAGS.user_unit]),
|
|
||||||
('uid', [name]),
|
|
||||||
('sn', [name]),
|
|
||||||
('cn', [name]),
|
|
||||||
('secretKey', [secret_key]),
|
|
||||||
('accessKey', [access_key]),
|
|
||||||
('isAdmin', [str(is_admin).upper()]),
|
|
||||||
]
|
|
||||||
self.conn.add_s(self.__uid_to_dn(name), attr)
|
|
||||||
return self.__to_user(dict(attr))
|
|
||||||
|
|
||||||
def create_project(self, name, manager_uid,
|
|
||||||
description=None, member_uids=None):
|
|
||||||
if self.project_exists(name):
|
|
||||||
raise exception.Duplicate("Project can't be created because "
|
|
||||||
"project %s already exists" % name)
|
|
||||||
if not self.user_exists(manager_uid):
|
|
||||||
raise exception.NotFound("Project can't be created because "
|
|
||||||
"manager %s doesn't exist" % manager_uid)
|
|
||||||
manager_dn = self.__uid_to_dn(manager_uid)
|
|
||||||
# description is a required attribute
|
|
||||||
if description is None:
|
|
||||||
description = name
|
|
||||||
members = []
|
|
||||||
if member_uids != None:
|
|
||||||
for member_uid in member_uids:
|
|
||||||
if not self.user_exists(member_uid):
|
|
||||||
raise exception.NotFound("Project can't be created "
|
|
||||||
"because user %s doesn't exist" % member_uid)
|
|
||||||
members.append(self.__uid_to_dn(member_uid))
|
|
||||||
# always add the manager as a member because members is required
|
|
||||||
if not manager_dn in members:
|
|
||||||
members.append(manager_dn)
|
|
||||||
attr = [
|
|
||||||
('objectclass', ['novaProject']),
|
|
||||||
('cn', [name]),
|
|
||||||
('description', [description]),
|
|
||||||
('projectManager', [manager_dn]),
|
|
||||||
('member', members)
|
|
||||||
]
|
|
||||||
self.conn.add_s('cn=%s,%s' % (name, FLAGS.project_ldap_subtree), attr)
|
|
||||||
return self.__to_project(dict(attr))
|
|
||||||
|
|
||||||
def add_to_project(self, uid, project_id):
|
|
||||||
dn = 'cn=%s,%s' % (project_id, FLAGS.project_ldap_subtree)
|
|
||||||
return self.add_to_group(uid, dn)
|
|
||||||
|
|
||||||
def remove_from_project(self, uid, project_id):
|
|
||||||
dn = 'cn=%s,%s' % (project_id, FLAGS.project_ldap_subtree)
|
|
||||||
return self.remove_from_group(uid, dn)
|
|
||||||
|
|
||||||
def is_in_project(self, uid, project_id):
|
|
||||||
dn = 'cn=%s,%s' % (project_id, FLAGS.project_ldap_subtree)
|
|
||||||
return self.is_in_group(uid, dn)
|
|
||||||
|
|
||||||
def __role_to_dn(self, role, project_id=None):
|
|
||||||
if project_id == None:
|
|
||||||
return FLAGS.__getitem__("ldap_%s" % role).value
|
|
||||||
else:
|
|
||||||
return 'cn=%s,cn=%s,%s' % (role,
|
|
||||||
project_id,
|
|
||||||
FLAGS.project_ldap_subtree)
|
|
||||||
|
|
||||||
def __create_group(self, group_dn, name, uid,
|
|
||||||
description, member_uids = None):
|
|
||||||
if self.group_exists(group_dn):
|
|
||||||
raise exception.Duplicate("Group can't be created because "
|
|
||||||
"group %s already exists" % name)
|
|
||||||
members = []
|
|
||||||
if member_uids != None:
|
|
||||||
for member_uid in member_uids:
|
|
||||||
if not self.user_exists(member_uid):
|
|
||||||
raise exception.NotFound("Group can't be created "
|
|
||||||
"because user %s doesn't exist" % member_uid)
|
|
||||||
members.append(self.__uid_to_dn(member_uid))
|
|
||||||
dn = self.__uid_to_dn(uid)
|
|
||||||
if not dn in members:
|
|
||||||
members.append(dn)
|
|
||||||
attr = [
|
|
||||||
('objectclass', ['groupOfNames']),
|
|
||||||
('cn', [name]),
|
|
||||||
('description', [description]),
|
|
||||||
('member', members)
|
|
||||||
]
|
|
||||||
self.conn.add_s(group_dn, attr)
|
|
||||||
return self.__to_group(dict(attr))
|
|
||||||
|
|
||||||
def has_role(self, uid, role, project_id=None):
|
|
||||||
role_dn = self.__role_to_dn(role, project_id)
|
|
||||||
return self.is_in_group(uid, role_dn)
|
|
||||||
|
|
||||||
def add_role(self, uid, role, project_id=None):
|
|
||||||
role_dn = self.__role_to_dn(role, project_id)
|
|
||||||
if not self.group_exists(role_dn):
|
|
||||||
# create the role if it doesn't exist
|
|
||||||
description = '%s role for %s' % (role, project_id)
|
|
||||||
self.__create_group(role_dn, role, uid, description)
|
|
||||||
else:
|
|
||||||
return self.add_to_group(uid, role_dn)
|
|
||||||
|
|
||||||
def remove_role(self, uid, role, project_id=None):
|
|
||||||
role_dn = self.__role_to_dn(role, project_id)
|
|
||||||
return self.remove_from_group(uid, role_dn)
|
|
||||||
|
|
||||||
def is_in_group(self, uid, group_dn):
|
|
||||||
if not self.user_exists(uid):
|
|
||||||
raise exception.NotFound("User %s can't be searched in group "
|
|
||||||
"becuase the user doesn't exist" % (uid,))
|
|
||||||
if not self.group_exists(group_dn):
|
|
||||||
return False
|
|
||||||
res = self.find_object(group_dn,
|
|
||||||
'(member=%s)' % self.__uid_to_dn(uid))
|
|
||||||
return res != None
|
|
||||||
|
|
||||||
def add_to_group(self, uid, group_dn):
|
|
||||||
if not self.user_exists(uid):
|
|
||||||
raise exception.NotFound("User %s can't be added to the group "
|
|
||||||
"becuase the user doesn't exist" % (uid,))
|
|
||||||
if not self.group_exists(group_dn):
|
|
||||||
raise exception.NotFound("The group at dn %s doesn't exist" %
|
|
||||||
(group_dn,))
|
|
||||||
if self.is_in_group(uid, group_dn):
|
|
||||||
raise exception.Duplicate("User %s is already a member of "
|
|
||||||
"the group %s" % (uid, group_dn))
|
|
||||||
attr = [
|
|
||||||
(ldap.MOD_ADD, 'member', self.__uid_to_dn(uid))
|
|
||||||
]
|
|
||||||
self.conn.modify_s(group_dn, attr)
|
|
||||||
|
|
||||||
def remove_from_group(self, uid, group_dn):
|
|
||||||
if not self.group_exists(group_dn):
|
|
||||||
raise exception.NotFound("The group at dn %s doesn't exist" %
|
|
||||||
(group_dn,))
|
|
||||||
if not self.user_exists(uid):
|
|
||||||
raise exception.NotFound("User %s can't be removed from the "
|
|
||||||
"group because the user doesn't exist" % (uid,))
|
|
||||||
if not self.is_in_group(uid, group_dn):
|
|
||||||
raise exception.NotFound("User %s is not a member of the group" %
|
|
||||||
(uid,))
|
|
||||||
self._safe_remove_from_group(group_dn, uid)
|
|
||||||
|
|
||||||
def _safe_remove_from_group(self, group_dn, uid):
|
|
||||||
# FIXME(vish): what if deleted user is a project manager?
|
|
||||||
attr = [(ldap.MOD_DELETE, 'member', self.__uid_to_dn(uid))]
|
|
||||||
try:
|
|
||||||
self.conn.modify_s(group_dn, attr)
|
|
||||||
except self.OBJECT_CLASS_VIOLATION:
|
|
||||||
logging.debug("Attempted to remove the last member of a group. "
|
|
||||||
"Deleting the group at %s instead." % group_dn )
|
|
||||||
self.delete_group(group_dn)
|
|
||||||
|
|
||||||
def remove_from_all(self, uid):
|
|
||||||
if not self.user_exists(uid):
|
|
||||||
raise exception.NotFound("User %s can't be removed from all "
|
|
||||||
"because the user doesn't exist" % (uid,))
|
|
||||||
dn = self.__uid_to_dn(uid)
|
|
||||||
role_dns = self.find_group_dns_with_member(
|
|
||||||
FLAGS.role_ldap_subtree, uid)
|
|
||||||
for role_dn in role_dns:
|
|
||||||
self._safe_remove_from_group(role_dn, uid)
|
|
||||||
project_dns = self.find_group_dns_with_member(
|
|
||||||
FLAGS.project_ldap_subtree, uid)
|
|
||||||
for project_dn in project_dns:
|
|
||||||
self._safe_remove_from_group(project_dn, uid)
|
|
||||||
|
|
||||||
def create_key_pair(self, uid, key_name, public_key, fingerprint):
|
|
||||||
"""create's a public key in the directory underneath the user"""
|
|
||||||
# TODO(vish): possibly refactor this to store keys in their own ou
|
|
||||||
# and put dn reference in the user object
|
|
||||||
attr = [
|
|
||||||
('objectclass', ['novaKeyPair']),
|
|
||||||
('cn', [key_name]),
|
|
||||||
('sshPublicKey', [public_key]),
|
|
||||||
('keyFingerprint', [fingerprint]),
|
|
||||||
]
|
|
||||||
self.conn.add_s('cn=%s,%s' % (key_name,
|
|
||||||
self.__uid_to_dn(uid)),
|
|
||||||
attr)
|
|
||||||
return self.__to_key_pair(uid, dict(attr))
|
|
||||||
|
|
||||||
def find_user_by_access_key(self, access):
|
|
||||||
query = '(accessKey=%s)' % access
|
|
||||||
dn = FLAGS.user_ldap_subtree
|
|
||||||
return self.__to_user(self.find_object(dn, query))
|
|
||||||
|
|
||||||
def delete_user(self, uid):
|
|
||||||
if not self.user_exists(uid):
|
|
||||||
raise exception.NotFound("User %s doesn't exist" % uid)
|
|
||||||
self.delete_key_pairs(uid)
|
|
||||||
self.remove_from_all(uid)
|
|
||||||
self.conn.delete_s('uid=%s,%s' % (uid,
|
|
||||||
FLAGS.user_ldap_subtree))
|
|
||||||
|
|
||||||
def delete_key_pair(self, uid, key_name):
|
|
||||||
if not self.key_pair_exists(uid, key_name):
|
|
||||||
raise exception.NotFound("Key Pair %s doesn't exist for user %s" %
|
|
||||||
(key_name, uid))
|
|
||||||
self.conn.delete_s('cn=%s,uid=%s,%s' % (key_name, uid,
|
|
||||||
FLAGS.user_ldap_subtree))
|
|
||||||
|
|
||||||
def delete_group(self, group_dn):
|
|
||||||
if not self.group_exists(group_dn):
|
|
||||||
raise exception.NotFound("Group at dn %s doesn't exist" % group_dn)
|
|
||||||
self.conn.delete_s(group_dn)
|
|
||||||
|
|
||||||
def delete_roles(self, project_dn):
|
|
||||||
roles = self.find_roles(project_dn)
|
|
||||||
for role in roles:
|
|
||||||
self.delete_group('cn=%s,%s' % (role.id, project_dn))
|
|
||||||
|
|
||||||
def delete_project(self, name):
|
|
||||||
project_dn = 'cn=%s,%s' % (name, FLAGS.project_ldap_subtree)
|
|
||||||
self.delete_roles(project_dn)
|
|
||||||
self.delete_group(project_dn)
|
|
||||||
|
|
||||||
def __to_user(self, attr):
|
|
||||||
if attr == None:
|
|
||||||
return None
|
|
||||||
return User(
|
|
||||||
id = attr['uid'][0],
|
|
||||||
name = attr['cn'][0],
|
|
||||||
access = attr['accessKey'][0],
|
|
||||||
secret = attr['secretKey'][0],
|
|
||||||
admin = (attr['isAdmin'][0] == 'TRUE')
|
|
||||||
)
|
|
||||||
|
|
||||||
def __to_key_pair(self, owner, attr):
|
|
||||||
if attr == None:
|
|
||||||
return None
|
|
||||||
return KeyPair(
|
|
||||||
id = attr['cn'][0],
|
|
||||||
owner_id = owner,
|
|
||||||
public_key = attr['sshPublicKey'][0],
|
|
||||||
fingerprint = attr['keyFingerprint'][0],
|
|
||||||
)
|
|
||||||
|
|
||||||
def __to_group(self, attr):
|
|
||||||
if attr == None:
|
|
||||||
return None
|
|
||||||
member_dns = attr.get('member', [])
|
|
||||||
return Group(
|
|
||||||
id = attr['cn'][0],
|
|
||||||
description = attr.get('description', [None])[0],
|
|
||||||
member_ids = [self.__dn_to_uid(x) for x in member_dns]
|
|
||||||
)
|
|
||||||
|
|
||||||
def __to_project(self, attr):
|
|
||||||
if attr == None:
|
|
||||||
return None
|
|
||||||
member_dns = attr.get('member', [])
|
|
||||||
return Project(
|
|
||||||
id = attr['cn'][0],
|
|
||||||
project_manager_id = self.__dn_to_uid(attr['projectManager'][0]),
|
|
||||||
description = attr.get('description', [None])[0],
|
|
||||||
member_ids = [self.__dn_to_uid(x) for x in member_dns]
|
|
||||||
)
|
|
||||||
|
|
||||||
def __dn_to_uid(self, dn):
|
|
||||||
return dn.split(',')[0].split('=')[1]
|
|
||||||
|
|
||||||
def __uid_to_dn(self, dn):
|
|
||||||
return 'uid=%s,%s' % (dn, FLAGS.user_ldap_subtree)
|
|
@ -25,7 +25,7 @@ import tornado.web
|
|||||||
import urllib
|
import urllib
|
||||||
|
|
||||||
from nova import crypto
|
from nova import crypto
|
||||||
from nova.auth import users
|
from nova.auth import manager
|
||||||
|
|
||||||
|
|
||||||
_log = logging.getLogger("api")
|
_log = logging.getLogger("api")
|
||||||
|
@ -31,7 +31,7 @@ import zipfile
|
|||||||
from nova import exception
|
from nova import exception
|
||||||
from nova import flags
|
from nova import flags
|
||||||
from nova import utils
|
from nova import utils
|
||||||
from nova.auth import users
|
from nova.auth import manager
|
||||||
from nova.endpoint import api
|
from nova.endpoint import api
|
||||||
|
|
||||||
|
|
||||||
@ -44,7 +44,7 @@ flags.DEFINE_string('boot_script_template',
|
|||||||
class CloudPipe(object):
|
class CloudPipe(object):
|
||||||
def __init__(self, cloud_controller):
|
def __init__(self, cloud_controller):
|
||||||
self.controller = cloud_controller
|
self.controller = cloud_controller
|
||||||
self.manager = users.UserManager.instance()
|
self.manager = manager.AuthManager()
|
||||||
|
|
||||||
def launch_vpn_instance(self, project_id):
|
def launch_vpn_instance(self, project_id):
|
||||||
logging.debug( "Launching VPN for %s" % (project_id))
|
logging.debug( "Launching VPN for %s" % (project_id))
|
||||||
|
@ -29,7 +29,7 @@ from nova import datastore
|
|||||||
from nova import exception
|
from nova import exception
|
||||||
from nova import flags
|
from nova import flags
|
||||||
from nova import utils
|
from nova import utils
|
||||||
from nova.auth import users
|
from nova.auth import manager
|
||||||
from nova.compute import exception as compute_exception
|
from nova.compute import exception as compute_exception
|
||||||
from nova.compute import linux_net
|
from nova.compute import linux_net
|
||||||
|
|
||||||
@ -209,11 +209,11 @@ class BaseNetwork(datastore.BasicModel):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def user(self):
|
def user(self):
|
||||||
return users.UserManager.instance().get_user(self['user_id'])
|
return manager.AuthManager().get_user(self['user_id'])
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def project(self):
|
def project(self):
|
||||||
return users.UserManager.instance().get_project(self['project_id'])
|
return manager.AuthManager().get_project(self['project_id'])
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def _hosts_key(self):
|
def _hosts_key(self):
|
||||||
@ -511,7 +511,7 @@ def get_vlan_for_project(project_id):
|
|||||||
if not known_vlans.has_key(vstr):
|
if not known_vlans.has_key(vstr):
|
||||||
return Vlan.create(project_id, vnum)
|
return Vlan.create(project_id, vnum)
|
||||||
old_project_id = known_vlans[vstr]
|
old_project_id = known_vlans[vstr]
|
||||||
if not users.UserManager.instance().get_project(old_project_id):
|
if not manager.AuthManager().get_project(old_project_id):
|
||||||
vlan = Vlan.lookup(old_project_id)
|
vlan = Vlan.lookup(old_project_id)
|
||||||
if vlan:
|
if vlan:
|
||||||
# NOTE(todd): This doesn't check for vlan id match, because
|
# NOTE(todd): This doesn't check for vlan id match, because
|
||||||
@ -537,7 +537,7 @@ def get_network_by_interface(iface, security_group='default'):
|
|||||||
|
|
||||||
def get_network_by_address(address):
|
def get_network_by_address(address):
|
||||||
logging.debug("Get Network By Address: %s" % address)
|
logging.debug("Get Network By Address: %s" % address)
|
||||||
for project in users.UserManager.instance().get_projects():
|
for project in manager.AuthManager().get_projects():
|
||||||
net = get_project_network(project.id)
|
net = get_project_network(project.id)
|
||||||
if address in net.assigned:
|
if address in net.assigned:
|
||||||
logging.debug("Found %s in %s" % (address, project.id))
|
logging.debug("Found %s in %s" % (address, project.id))
|
||||||
@ -577,7 +577,7 @@ def get_project_network(project_id, security_group='default'):
|
|||||||
""" get a project's private network, allocating one if needed """
|
""" get a project's private network, allocating one if needed """
|
||||||
# TODO(todd): It looks goofy to get a project from a UserManager.
|
# TODO(todd): It looks goofy to get a project from a UserManager.
|
||||||
# Refactor to still use the LDAP backend, but not User specific.
|
# Refactor to still use the LDAP backend, but not User specific.
|
||||||
project = users.UserManager.instance().get_project(project_id)
|
project = manager.AuthManager().get_project(project_id)
|
||||||
if not project:
|
if not project:
|
||||||
raise exception.Error("Project %s doesn't exist, uhoh." %
|
raise exception.Error("Project %s doesn't exist, uhoh." %
|
||||||
project_id)
|
project_id)
|
||||||
@ -587,5 +587,5 @@ def get_project_network(project_id, security_group='default'):
|
|||||||
|
|
||||||
def restart_nets():
|
def restart_nets():
|
||||||
""" Ensure the network for each user is enabled"""
|
""" Ensure the network for each user is enabled"""
|
||||||
for project in users.UserManager.instance().get_projects():
|
for project in manager.AuthManager().get_projects():
|
||||||
get_project_network(project.id).express()
|
get_project_network(project.id).express()
|
||||||
|
@ -22,7 +22,7 @@ Admin API controller, exposed through http via the api worker.
|
|||||||
|
|
||||||
import base64
|
import base64
|
||||||
|
|
||||||
from nova.auth import users
|
from nova.auth import manager
|
||||||
from nova.compute import model
|
from nova.compute import model
|
||||||
|
|
||||||
def user_dict(user, base64_file=None):
|
def user_dict(user, base64_file=None):
|
||||||
@ -69,18 +69,18 @@ class AdminController(object):
|
|||||||
@admin_only
|
@admin_only
|
||||||
def describe_user(self, _context, name, **_kwargs):
|
def describe_user(self, _context, name, **_kwargs):
|
||||||
"""Returns user data, including access and secret keys."""
|
"""Returns user data, including access and secret keys."""
|
||||||
return user_dict(users.UserManager.instance().get_user(name))
|
return user_dict(manager.AuthManager().get_user(name))
|
||||||
|
|
||||||
@admin_only
|
@admin_only
|
||||||
def describe_users(self, _context, **_kwargs):
|
def describe_users(self, _context, **_kwargs):
|
||||||
"""Returns all users - should be changed to deal with a list."""
|
"""Returns all users - should be changed to deal with a list."""
|
||||||
return {'userSet':
|
return {'userSet':
|
||||||
[user_dict(u) for u in users.UserManager.instance().get_users()] }
|
[user_dict(u) for u in manager.AuthManager().get_users()] }
|
||||||
|
|
||||||
@admin_only
|
@admin_only
|
||||||
def register_user(self, _context, name, **_kwargs):
|
def register_user(self, _context, name, **_kwargs):
|
||||||
"""Creates a new user, and returns generated credentials."""
|
"""Creates a new user, and returns generated credentials."""
|
||||||
return user_dict(users.UserManager.instance().create_user(name))
|
return user_dict(manager.AuthManager().create_user(name))
|
||||||
|
|
||||||
@admin_only
|
@admin_only
|
||||||
def deregister_user(self, _context, name, **_kwargs):
|
def deregister_user(self, _context, name, **_kwargs):
|
||||||
@ -88,7 +88,7 @@ class AdminController(object):
|
|||||||
Should throw an exception if the user has instances,
|
Should throw an exception if the user has instances,
|
||||||
volumes, or buckets remaining.
|
volumes, or buckets remaining.
|
||||||
"""
|
"""
|
||||||
users.UserManager.instance().delete_user(name)
|
manager.AuthManager().delete_user(name)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@ -100,8 +100,8 @@ class AdminController(object):
|
|||||||
"""
|
"""
|
||||||
if project is None:
|
if project is None:
|
||||||
project = name
|
project = name
|
||||||
project = users.UserManager.instance().get_project(project)
|
project = manager.AuthManager().get_project(project)
|
||||||
user = users.UserManager.instance().get_user(name)
|
user = manager.AuthManager().get_user(name)
|
||||||
return user_dict(user, base64.b64encode(project.get_credentials(user)))
|
return user_dict(user, base64.b64encode(project.get_credentials(user)))
|
||||||
|
|
||||||
@admin_only
|
@admin_only
|
||||||
|
@ -35,7 +35,7 @@ from nova import crypto
|
|||||||
from nova import exception
|
from nova import exception
|
||||||
from nova import flags
|
from nova import flags
|
||||||
from nova import utils
|
from nova import utils
|
||||||
from nova.auth import users
|
from nova.auth import manager
|
||||||
import nova.cloudpipe.api
|
import nova.cloudpipe.api
|
||||||
from nova.endpoint import cloud
|
from nova.endpoint import cloud
|
||||||
|
|
||||||
@ -266,7 +266,7 @@ class APIRequestHandler(tornado.web.RequestHandler):
|
|||||||
|
|
||||||
# Authenticate the request.
|
# Authenticate the request.
|
||||||
try:
|
try:
|
||||||
(user, project) = users.UserManager.instance().authenticate(
|
(user, project) = manager.AuthManager().authenticate(
|
||||||
access,
|
access,
|
||||||
signature,
|
signature,
|
||||||
auth_params,
|
auth_params,
|
||||||
|
@ -35,7 +35,7 @@ from nova import flags
|
|||||||
from nova import rpc
|
from nova import rpc
|
||||||
from nova import utils
|
from nova import utils
|
||||||
from nova.auth import rbac
|
from nova.auth import rbac
|
||||||
from nova.auth import users
|
from nova.auth import manager
|
||||||
from nova.compute import model
|
from nova.compute import model
|
||||||
from nova.compute import network
|
from nova.compute import network
|
||||||
from nova.compute import node
|
from nova.compute import node
|
||||||
@ -48,9 +48,9 @@ FLAGS = flags.FLAGS
|
|||||||
flags.DEFINE_string('cloud_topic', 'cloud', 'the topic clouds listen on')
|
flags.DEFINE_string('cloud_topic', 'cloud', 'the topic clouds listen on')
|
||||||
|
|
||||||
def _gen_key(user_id, key_name):
|
def _gen_key(user_id, key_name):
|
||||||
""" Tuck this into UserManager """
|
""" Tuck this into AuthManager """
|
||||||
try:
|
try:
|
||||||
manager = users.UserManager.instance()
|
manager = manager.AuthManager()
|
||||||
private_key, fingerprint = manager.generate_key_pair(user_id, key_name)
|
private_key, fingerprint = manager.generate_key_pair(user_id, key_name)
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
return {'exception': ex}
|
return {'exception': ex}
|
||||||
|
@ -34,7 +34,7 @@ from nova import exception
|
|||||||
from nova import flags
|
from nova import flags
|
||||||
from nova import rpc
|
from nova import rpc
|
||||||
from nova import utils
|
from nova import utils
|
||||||
from nova.auth import users
|
from nova.auth import manager
|
||||||
from nova.compute import model
|
from nova.compute import model
|
||||||
from nova.compute import network
|
from nova.compute import network
|
||||||
from nova.endpoint import images
|
from nova.endpoint import images
|
||||||
@ -78,11 +78,11 @@ class Api(object):
|
|||||||
def build_context(self, env):
|
def build_context(self, env):
|
||||||
rv = {}
|
rv = {}
|
||||||
if env.has_key("HTTP_X_AUTH_TOKEN"):
|
if env.has_key("HTTP_X_AUTH_TOKEN"):
|
||||||
rv['user'] = users.UserManager.instance().get_user_from_access_key(
|
rv['user'] = manager.AuthManager().get_user_from_access_key(
|
||||||
env['HTTP_X_AUTH_TOKEN']
|
env['HTTP_X_AUTH_TOKEN']
|
||||||
)
|
)
|
||||||
if rv['user']:
|
if rv['user']:
|
||||||
rv['project'] = users.UserManager.instance().get_project(
|
rv['project'] = manager.AuthManager().get_project(
|
||||||
rv['user'].name
|
rv['user'].name
|
||||||
)
|
)
|
||||||
return rv
|
return rv
|
||||||
|
@ -22,7 +22,7 @@ import logging
|
|||||||
from nova import exception
|
from nova import exception
|
||||||
from nova import flags
|
from nova import flags
|
||||||
from nova import test
|
from nova import test
|
||||||
from nova.auth.users import UserManager
|
from nova.auth import manager
|
||||||
from nova.auth import rbac
|
from nova.auth import rbac
|
||||||
|
|
||||||
|
|
||||||
@ -35,7 +35,7 @@ class AccessTestCase(test.BaseTestCase):
|
|||||||
super(AccessTestCase, self).setUp()
|
super(AccessTestCase, self).setUp()
|
||||||
FLAGS.fake_libvirt = True
|
FLAGS.fake_libvirt = True
|
||||||
FLAGS.fake_storage = True
|
FLAGS.fake_storage = True
|
||||||
um = UserManager.instance()
|
um = manager.AuthManager()
|
||||||
# Make test users
|
# Make test users
|
||||||
try:
|
try:
|
||||||
self.testadmin = um.create_user('testadmin')
|
self.testadmin = um.create_user('testadmin')
|
||||||
@ -79,7 +79,7 @@ class AccessTestCase(test.BaseTestCase):
|
|||||||
#user is set in each test
|
#user is set in each test
|
||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
um = UserManager.instance()
|
um = manager.AuthManager()
|
||||||
# Delete the test project
|
# Delete the test project
|
||||||
um.delete_project('testproj')
|
um.delete_project('testproj')
|
||||||
# Delete the test user
|
# Delete the test user
|
||||||
|
@ -26,7 +26,7 @@ from twisted.internet import defer
|
|||||||
|
|
||||||
from nova import flags
|
from nova import flags
|
||||||
from nova import test
|
from nova import test
|
||||||
from nova.auth import users
|
from nova.auth import manager
|
||||||
from nova.endpoint import api
|
from nova.endpoint import api
|
||||||
from nova.endpoint import cloud
|
from nova.endpoint import cloud
|
||||||
|
|
||||||
@ -150,7 +150,7 @@ class ApiEc2TestCase(test.BaseTestCase):
|
|||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(ApiEc2TestCase, self).setUp()
|
super(ApiEc2TestCase, self).setUp()
|
||||||
|
|
||||||
self.users = users.UserManager.instance()
|
self.users = manager.AuthManager()
|
||||||
self.cloud = cloud.CloudController()
|
self.cloud = cloud.CloudController()
|
||||||
|
|
||||||
self.host = '127.0.0.1'
|
self.host = '127.0.0.1'
|
||||||
|
@ -25,19 +25,19 @@ import unittest
|
|||||||
from nova import crypto
|
from nova import crypto
|
||||||
from nova import flags
|
from nova import flags
|
||||||
from nova import test
|
from nova import test
|
||||||
from nova.auth import users
|
from nova.auth import manager
|
||||||
from nova.endpoint import cloud
|
from nova.endpoint import cloud
|
||||||
|
|
||||||
FLAGS = flags.FLAGS
|
FLAGS = flags.FLAGS
|
||||||
|
|
||||||
|
|
||||||
class UserTestCase(test.BaseTestCase):
|
class AuthTestCase(test.BaseTestCase):
|
||||||
flush_db = False
|
flush_db = False
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(UserTestCase, self).setUp()
|
super(AuthTestCase, self).setUp()
|
||||||
self.flags(fake_libvirt=True,
|
self.flags(fake_libvirt=True,
|
||||||
fake_storage=True)
|
fake_storage=True)
|
||||||
self.users = users.UserManager.instance()
|
self.users = manager.AuthManager()
|
||||||
|
|
||||||
def test_001_can_create_users(self):
|
def test_001_can_create_users(self):
|
||||||
self.users.create_user('test1', 'access', 'secret')
|
self.users.create_user('test1', 'access', 'secret')
|
@ -27,7 +27,7 @@ from xml.etree import ElementTree
|
|||||||
from nova import flags
|
from nova import flags
|
||||||
from nova import rpc
|
from nova import rpc
|
||||||
from nova import test
|
from nova import test
|
||||||
from nova.auth import users
|
from nova.auth import manager
|
||||||
from nova.compute import node
|
from nova.compute import node
|
||||||
from nova.endpoint import api
|
from nova.endpoint import api
|
||||||
from nova.endpoint import cloud
|
from nova.endpoint import cloud
|
||||||
@ -61,15 +61,15 @@ class CloudTestCase(test.BaseTestCase):
|
|||||||
self.injected.append(self.node_consumer.attach_to_tornado(self.ioloop))
|
self.injected.append(self.node_consumer.attach_to_tornado(self.ioloop))
|
||||||
|
|
||||||
try:
|
try:
|
||||||
users.UserManager.instance().create_user('admin', 'admin', 'admin')
|
manager.AuthManager().create_user('admin', 'admin', 'admin')
|
||||||
except: pass
|
except: pass
|
||||||
admin = users.UserManager.instance().get_user('admin')
|
admin = manager.AuthManager().get_user('admin')
|
||||||
project = users.UserManager.instance().create_project('proj', 'admin', 'proj')
|
project = manager.AuthManager().create_project('proj', 'admin', 'proj')
|
||||||
self.context = api.APIRequestContext(handler=None,project=project,user=admin)
|
self.context = api.APIRequestContext(handler=None,project=project,user=admin)
|
||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
users.UserManager.instance().delete_project('proj')
|
manager.AuthManager().delete_project('proj')
|
||||||
users.UserManager.instance().delete_user('admin')
|
manager.AuthManager().delete_user('admin')
|
||||||
|
|
||||||
def test_console_output(self):
|
def test_console_output(self):
|
||||||
if FLAGS.fake_libvirt:
|
if FLAGS.fake_libvirt:
|
||||||
|
@ -26,7 +26,7 @@ from nova import test
|
|||||||
from nova import exception
|
from nova import exception
|
||||||
from nova.compute.exception import NoMoreAddresses
|
from nova.compute.exception import NoMoreAddresses
|
||||||
from nova.compute import network
|
from nova.compute import network
|
||||||
from nova.auth import users
|
from nova.auth import manager
|
||||||
from nova import utils
|
from nova import utils
|
||||||
|
|
||||||
|
|
||||||
@ -38,7 +38,7 @@ class NetworkTestCase(test.TrialTestCase):
|
|||||||
fake_network=True,
|
fake_network=True,
|
||||||
network_size=32)
|
network_size=32)
|
||||||
logging.getLogger().setLevel(logging.DEBUG)
|
logging.getLogger().setLevel(logging.DEBUG)
|
||||||
self.manager = users.UserManager.instance()
|
self.manager = manager.AuthManager()
|
||||||
self.dnsmasq = FakeDNSMasq()
|
self.dnsmasq = FakeDNSMasq()
|
||||||
try:
|
try:
|
||||||
self.manager.create_user('netuser', 'netuser', 'netuser')
|
self.manager.create_user('netuser', 'netuser', 'netuser')
|
||||||
|
@ -26,7 +26,7 @@ import tempfile
|
|||||||
from nova import flags
|
from nova import flags
|
||||||
from nova import objectstore
|
from nova import objectstore
|
||||||
from nova import test
|
from nova import test
|
||||||
from nova.auth import users
|
from nova.auth import manager
|
||||||
|
|
||||||
|
|
||||||
FLAGS = flags.FLAGS
|
FLAGS = flags.FLAGS
|
||||||
@ -57,7 +57,7 @@ class ObjectStoreTestCase(test.BaseTestCase):
|
|||||||
ca_path=os.path.join(os.path.dirname(__file__), 'CA'))
|
ca_path=os.path.join(os.path.dirname(__file__), 'CA'))
|
||||||
logging.getLogger().setLevel(logging.DEBUG)
|
logging.getLogger().setLevel(logging.DEBUG)
|
||||||
|
|
||||||
self.um = users.UserManager.instance()
|
self.um = manager.AuthManager()
|
||||||
try:
|
try:
|
||||||
self.um.create_user('user1')
|
self.um.create_user('user1')
|
||||||
except: pass
|
except: pass
|
||||||
@ -177,7 +177,7 @@ class ObjectStoreTestCase(test.BaseTestCase):
|
|||||||
# FLAGS.images_path = os.path.join(tempdir, 'images')
|
# FLAGS.images_path = os.path.join(tempdir, 'images')
|
||||||
# FLAGS.ca_path = os.path.join(os.path.dirname(__file__), 'CA')
|
# FLAGS.ca_path = os.path.join(os.path.dirname(__file__), 'CA')
|
||||||
#
|
#
|
||||||
# self.users = users.UserManager.instance()
|
# self.users = manager.AuthManager()
|
||||||
# self.app = handler.Application(self.users)
|
# self.app = handler.Application(self.users)
|
||||||
#
|
#
|
||||||
# self.host = '127.0.0.1'
|
# self.host = '127.0.0.1'
|
||||||
|
@ -57,7 +57,7 @@ from nova.tests.node_unittest import *
|
|||||||
from nova.tests.objectstore_unittest import *
|
from nova.tests.objectstore_unittest import *
|
||||||
from nova.tests.process_unittest import *
|
from nova.tests.process_unittest import *
|
||||||
from nova.tests.storage_unittest import *
|
from nova.tests.storage_unittest import *
|
||||||
from nova.tests.users_unittest import *
|
from nova.tests.auth_unittest import *
|
||||||
from nova.tests.validator_unittest import *
|
from nova.tests.validator_unittest import *
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user