split up the services and kvs backends
This commit is contained in:
parent
909012a63e
commit
308a766b5b
|
@ -0,0 +1 @@
|
|||
from keystone.catalog.core import *
|
|
@ -0,0 +1,42 @@
|
|||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
|
||||
from keystone.common import kvs
|
||||
|
||||
|
||||
class KvsCatalog(kvs.Base):
|
||||
# Public interface
|
||||
def get_catalog(self, user_id, tenant_id, metadata=None):
|
||||
return self.db.get('catalog-%s-%s' % (tenant_id, user_id))
|
||||
|
||||
def get_service(self, service_id):
|
||||
return self.db.get('service-%s' % service_id)
|
||||
|
||||
def list_services(self):
|
||||
return self.db.get('service_list', [])
|
||||
|
||||
def create_service(self, service_id, service):
|
||||
self.db.set('service-%s' % service_id, service)
|
||||
service_list = set(self.db.get('service_list', []))
|
||||
service_list.add(service_id)
|
||||
self.db.set('service_list', list(service_list))
|
||||
return service
|
||||
|
||||
def update_service(self, service_id, service):
|
||||
self.db.set('service-%s' % service_id, service)
|
||||
return service
|
||||
|
||||
def delete_service(self, service_id):
|
||||
self.db.delete('service-%s' % service_id)
|
||||
service_list = set(self.db.get('service_list', []))
|
||||
service_list.remove(service_id)
|
||||
self.db.set('service_list', list(service_list))
|
||||
return None
|
||||
|
||||
# Private interface
|
||||
def _create_catalog(self, user_id, tenant_id, data):
|
||||
self.db.set('catalog-%s-%s' % (tenant_id, user_id), data)
|
||||
return data
|
||||
|
||||
|
||||
|
|
@ -1,15 +1,15 @@
|
|||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
from keystone import config
|
||||
from keystone import logging
|
||||
from keystone.backends import kvs
|
||||
from keystone.common import logging
|
||||
from keystone.catalog.backends import kvs
|
||||
|
||||
|
||||
CONF = config.CONF
|
||||
config.register_str('template_file', group='catalog')
|
||||
|
||||
|
||||
class TemplatedCatalog(kvs.KvsCatalog):
|
||||
class TemplatedCatalog(kvs.Catalog):
|
||||
"""A backend that generates endpoints for the Catalog based on templates.
|
||||
|
||||
It is usually configured via config entries that look like:
|
||||
|
|
|
@ -3,7 +3,11 @@
|
|||
"""Main entry point into the Catalog service."""
|
||||
|
||||
from keystone import config
|
||||
from keystone import manager
|
||||
from keystone import identity
|
||||
from keystone import token
|
||||
from keystone import policy
|
||||
from keystone.common import manager
|
||||
from keystone.common import wsgi
|
||||
|
||||
|
||||
CONF = config.CONF
|
||||
|
@ -22,3 +26,38 @@ class Manager(manager.Manager):
|
|||
|
||||
def __init__(self):
|
||||
super(Manager, self).__init__(CONF.catalog.driver)
|
||||
|
||||
|
||||
class ServiceController(wsgi.Application):
|
||||
def __init__(self):
|
||||
self.catalog_api = Manager()
|
||||
self.identity_api = identity.Manager()
|
||||
self.token_api = token.Manager()
|
||||
self.policy_api = policy.Manager()
|
||||
super(ServiceController, self).__init__()
|
||||
|
||||
# CRUD extensions
|
||||
# NOTE(termie): this OS-KSADM stuff is not very consistent
|
||||
def get_services(self, context):
|
||||
service_list = self.catalog_api.list_services(context)
|
||||
service_refs = [self.catalog_api.get_service(context, x)
|
||||
for x in service_list]
|
||||
return {'OS-KSADM:services': service_refs}
|
||||
|
||||
def get_service(self, context, service_id):
|
||||
service_ref = self.catalog_api.get_service(context, service_id)
|
||||
if not service_ref:
|
||||
raise webob.exc.HTTPNotFound()
|
||||
return {'OS-KSADM:service': service_ref}
|
||||
|
||||
def delete_service(self, context, service_id):
|
||||
service_ref = self.catalog_api.delete_service(context, service_id)
|
||||
|
||||
def create_service(self, context, OS_KSADM_service):
|
||||
service_id = uuid.uuid4().hex
|
||||
service_ref = OS_KSADM_service.copy()
|
||||
service_ref['id'] = service_id
|
||||
new_service_ref = self.catalog_api.create_service(
|
||||
context, service_id, service_ref)
|
||||
return {'OS-KSADM:service': new_service_ref}
|
||||
|
||||
|
|
|
@ -12,291 +12,10 @@ class DictKvs(dict):
|
|||
INMEMDB = DictKvs()
|
||||
|
||||
|
||||
class KvsIdentity(object):
|
||||
class Base(object):
|
||||
def __init__(self, db=None):
|
||||
if db is None:
|
||||
db = INMEMDB
|
||||
elif type(db) is type({}):
|
||||
db = DictKvs(db)
|
||||
self.db = db
|
||||
|
||||
# Public interface
|
||||
def authenticate(self, user_id=None, tenant_id=None, password=None):
|
||||
"""Authenticate based on a user, tenant and password.
|
||||
|
||||
Expects the user object to have a password field and the tenant to be
|
||||
in the list of tenants on the user.
|
||||
|
||||
"""
|
||||
user_ref = self.get_user(user_id)
|
||||
tenant_ref = None
|
||||
metadata_ref = None
|
||||
if not user_ref or user_ref.get('password') != password:
|
||||
raise AssertionError('Invalid user / password')
|
||||
if tenant_id and tenant_id not in user_ref['tenants']:
|
||||
raise AssertionError('Invalid tenant')
|
||||
|
||||
tenant_ref = self.get_tenant(tenant_id)
|
||||
if tenant_ref:
|
||||
metadata_ref = self.get_metadata(user_id, tenant_id)
|
||||
else:
|
||||
metadata_ref = {}
|
||||
return (user_ref, tenant_ref, metadata_ref)
|
||||
|
||||
def get_tenant(self, tenant_id):
|
||||
tenant_ref = self.db.get('tenant-%s' % tenant_id)
|
||||
return tenant_ref
|
||||
|
||||
def get_tenant_by_name(self, tenant_name):
|
||||
tenant_ref = self.db.get('tenant_name-%s' % tenant_name)
|
||||
return tenant_ref
|
||||
|
||||
def get_user(self, user_id):
|
||||
user_ref = self.db.get('user-%s' % user_id)
|
||||
return user_ref
|
||||
|
||||
def get_user_by_name(self, user_name):
|
||||
user_ref = self.db.get('user_name-%s' % user_name)
|
||||
return user_ref
|
||||
|
||||
def get_metadata(self, user_id, tenant_id):
|
||||
return self.db.get('metadata-%s-%s' % (tenant_id, user_id))
|
||||
|
||||
def get_role(self, role_id):
|
||||
role_ref = self.db.get('role-%s' % role_id)
|
||||
return role_ref
|
||||
|
||||
def list_users(self):
|
||||
user_ids = self.db.get('user_list', [])
|
||||
return [self.get_user(x) for x in user_ids]
|
||||
|
||||
def list_roles(self):
|
||||
role_ids = self.db.get('role_list', [])
|
||||
return [self.get_role(x) for x in role_ids]
|
||||
|
||||
# These should probably be part of the high-level API
|
||||
def add_user_to_tenant(self, tenant_id, user_id):
|
||||
user_ref = self.get_user(user_id)
|
||||
tenants = set(user_ref.get('tenants', []))
|
||||
tenants.add(tenant_id)
|
||||
user_ref['tenants'] = list(tenants)
|
||||
self.update_user(user_id, user_ref)
|
||||
|
||||
def remove_user_from_tenant(self, tenant_id, user_id):
|
||||
user_ref = self.get_user(user_id)
|
||||
tenants = set(user_ref.get('tenants', []))
|
||||
tenants.remove(tenant_id)
|
||||
user_ref['tenants'] = list(tenants)
|
||||
self.update_user(user_id, user_ref)
|
||||
|
||||
def get_tenants_for_user(self, user_id):
|
||||
user_ref = self.get_user(user_id)
|
||||
return user_ref.get('tenants', [])
|
||||
|
||||
def get_roles_for_user_and_tenant(self, user_id, tenant_id):
|
||||
metadata_ref = self.get_metadata(user_id, tenant_id)
|
||||
if not metadata_ref:
|
||||
metadata_ref = {}
|
||||
return metadata_ref.get('roles', [])
|
||||
|
||||
def add_role_to_user_and_tenant(self, user_id, tenant_id, role_id):
|
||||
metadata_ref = self.get_metadata(user_id, tenant_id)
|
||||
if not metadata_ref:
|
||||
metadata_ref = {}
|
||||
roles = set(metadata_ref.get('roles', []))
|
||||
roles.add(role_id)
|
||||
metadata_ref['roles'] = list(roles)
|
||||
self.update_metadata(user_id, tenant_id, metadata_ref)
|
||||
|
||||
def remove_role_from_user_and_tenant(self, user_id, tenant_id, role_id):
|
||||
metadata_ref = self.get_metadata(user_id, tenant_id)
|
||||
if not metadata_ref:
|
||||
metadata_ref = {}
|
||||
roles = set(metadata_ref.get('roles', []))
|
||||
roles.remove(role_id)
|
||||
metadata_ref['roles'] = list(roles)
|
||||
self.update_metadata(user_id, tenant_id, metadata_ref)
|
||||
|
||||
# CRUD
|
||||
def create_user(self, user_id, user):
|
||||
self.db.set('user-%s' % user_id, user)
|
||||
self.db.set('user_name-%s' % user['name'], user)
|
||||
user_list = set(self.db.get('user_list', []))
|
||||
user_list.add(user_id)
|
||||
self.db.set('user_list', list(user_list))
|
||||
return user
|
||||
|
||||
def update_user(self, user_id, user):
|
||||
# get the old name and delete it too
|
||||
old_user = self.db.get('user-%s' % user_id)
|
||||
self.db.delete('user_name-%s' % old_user['name'])
|
||||
self.db.set('user-%s' % user_id, user)
|
||||
self.db.set('user_name-%s' % user['name'], user)
|
||||
return user
|
||||
|
||||
def delete_user(self, user_id):
|
||||
old_user = self.db.get('user-%s' % user_id)
|
||||
self.db.delete('user_name-%s' % old_user['name'])
|
||||
self.db.delete('user-%s' % user_id)
|
||||
user_list = set(self.db.get('user_list', []))
|
||||
user_list.remove(user_id)
|
||||
self.db.set('user_list', list(user_list))
|
||||
return None
|
||||
|
||||
def create_tenant(self, tenant_id, tenant):
|
||||
self.db.set('tenant-%s' % tenant_id, tenant)
|
||||
self.db.set('tenant_name-%s' % tenant['name'], tenant)
|
||||
return tenant
|
||||
|
||||
def update_tenant(self, tenant_id, tenant):
|
||||
# get the old name and delete it too
|
||||
old_tenant = self.db.get('tenant-%s' % tenant_id)
|
||||
self.db.delete('tenant_name-%s' % old_tenant['name'])
|
||||
self.db.set('tenant-%s' % tenant_id, tenant)
|
||||
self.db.set('tenant_name-%s' % tenant['name'], tenant)
|
||||
return tenant
|
||||
|
||||
def delete_tenant(self, tenant_id):
|
||||
old_tenant = self.db.get('tenant-%s' % tenant_id)
|
||||
self.db.delete('tenant_name-%s' % old_tenant['name'])
|
||||
self.db.delete('tenant-%s' % tenant_id)
|
||||
return None
|
||||
|
||||
def create_metadata(self, user_id, tenant_id, metadata):
|
||||
self.db.set('metadata-%s-%s' % (tenant_id, user_id), metadata)
|
||||
return metadata
|
||||
|
||||
def update_metadata(self, user_id, tenant_id, metadata):
|
||||
self.db.set('metadata-%s-%s' % (tenant_id, user_id), metadata)
|
||||
return metadata
|
||||
|
||||
def delete_metadata(self, user_id, tenant_id):
|
||||
self.db.delete('metadata-%s-%s' % (tenant_id, user_id))
|
||||
return None
|
||||
|
||||
def create_role(self, role_id, role):
|
||||
self.db.set('role-%s' % role_id, role)
|
||||
role_list = set(self.db.get('role_list', []))
|
||||
role_list.add(role_id)
|
||||
self.db.set('role_list', list(role_list))
|
||||
return role
|
||||
|
||||
def update_role(self, role_id, role):
|
||||
self.db.set('role-%s' % role_id, role)
|
||||
return role
|
||||
|
||||
def delete_role(self, role_id):
|
||||
self.db.delete('role-%s' % role_id)
|
||||
role_list = set(self.db.get('role_list', []))
|
||||
role_list.remove(role_id)
|
||||
self.db.set('role_list', list(role_list))
|
||||
return None
|
||||
|
||||
|
||||
class KvsToken(object):
|
||||
def __init__(self, db=None):
|
||||
if db is None:
|
||||
db = INMEMDB
|
||||
elif type(db) is type({}):
|
||||
db = DictKvs(db)
|
||||
self.db = db
|
||||
|
||||
# Public interface
|
||||
def get_token(self, token_id):
|
||||
return self.db.get('token-%s' % token_id)
|
||||
|
||||
def create_token(self, token_id, data):
|
||||
self.db.set('token-%s' % token_id, data)
|
||||
return data
|
||||
|
||||
def delete_token(self, token_id):
|
||||
return self.db.delete('token-%s' % token_id)
|
||||
|
||||
|
||||
class KvsCatalog(object):
|
||||
def __init__(self, db=None):
|
||||
if db is None:
|
||||
db = INMEMDB
|
||||
elif type(db) is type({}):
|
||||
db = DictKvs(db)
|
||||
self.db = db
|
||||
|
||||
# Public interface
|
||||
def get_catalog(self, user_id, tenant_id, metadata=None):
|
||||
return self.db.get('catalog-%s-%s' % (tenant_id, user_id))
|
||||
|
||||
def get_service(self, service_id):
|
||||
return self.db.get('service-%s' % service_id)
|
||||
|
||||
def list_services(self):
|
||||
return self.db.get('service_list', [])
|
||||
|
||||
def create_service(self, service_id, service):
|
||||
self.db.set('service-%s' % service_id, service)
|
||||
service_list = set(self.db.get('service_list', []))
|
||||
service_list.add(service_id)
|
||||
self.db.set('service_list', list(service_list))
|
||||
return service
|
||||
|
||||
def update_service(self, service_id, service):
|
||||
self.db.set('service-%s' % service_id, service)
|
||||
return service
|
||||
|
||||
def delete_service(self, service_id):
|
||||
self.db.delete('service-%s' % service_id)
|
||||
service_list = set(self.db.get('service_list', []))
|
||||
service_list.remove(service_id)
|
||||
self.db.set('service_list', list(service_list))
|
||||
return None
|
||||
|
||||
# Private interface
|
||||
def _create_catalog(self, user_id, tenant_id, data):
|
||||
self.db.set('catalog-%s-%s' % (tenant_id, user_id), data)
|
||||
return data
|
||||
|
||||
|
||||
class KvsPolicy(object):
|
||||
def __init__(self, db=None):
|
||||
if db is None:
|
||||
db = INMEMDB
|
||||
elif type(db) is type({}):
|
||||
db = DictKvs(db)
|
||||
self.db = db
|
||||
|
||||
def can_haz(self, target, action, credentials):
|
||||
pass
|
||||
|
||||
|
||||
class KvsEc2(object):
|
||||
def __init__(self, db=None):
|
||||
if db is None:
|
||||
db = INMEMDB
|
||||
elif type(db) is type({}):
|
||||
db = DictKvs(db)
|
||||
self.db = db
|
||||
|
||||
# Public interface
|
||||
def get_credential(self, credential_id):
|
||||
credential_ref = self.db.get('credential-%s' % credential_id)
|
||||
return credential_ref
|
||||
|
||||
def list_credentials(self, user_id):
|
||||
credential_ids = self.db.get('credential_list', [])
|
||||
rv = [self.get_credential(x) for x in credential_ids]
|
||||
return [x for x in rv if x['user_id'] == user_id]
|
||||
|
||||
# CRUD
|
||||
def create_credential(self, credential_id, credential):
|
||||
self.db.set('credential-%s' % credential_id, credential)
|
||||
credential_list = set(self.db.get('credential_list', []))
|
||||
credential_list.add(credential_id)
|
||||
self.db.set('credential_list', list(credential_list))
|
||||
return credential
|
||||
|
||||
def delete_credential(self, credential_id):
|
||||
old_credential = self.db.get('credential-%s' % credential_id)
|
||||
self.db.delete('credential-%s' % credential_id)
|
||||
credential_list = set(self.db.get('credential_list', []))
|
||||
credential_list.remove(credential_id)
|
||||
self.db.set('credential_list', list(credential_list))
|
||||
return None
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
import functools
|
||||
|
||||
from keystone import config
|
||||
from keystone import utils
|
||||
from keystone.common import utils
|
||||
|
||||
|
||||
class Manager(object):
|
||||
|
|
|
@ -25,7 +25,7 @@ import subprocess
|
|||
import sys
|
||||
import urllib
|
||||
|
||||
from keystone import logging
|
||||
from keystone.common import logging
|
||||
|
||||
|
||||
def import_class(import_str):
|
||||
|
|
|
@ -81,7 +81,7 @@ class Request(webob.Request):
|
|||
pass
|
||||
|
||||
|
||||
class Application(object):
|
||||
class BaseApplication(object):
|
||||
"""Base WSGI application wrapper. Subclasses need to implement __call__."""
|
||||
|
||||
@classmethod
|
||||
|
@ -146,6 +146,60 @@ class Application(object):
|
|||
raise NotImplementedError('You must implement __call__')
|
||||
|
||||
|
||||
class Application(wsgi.BaseApplication):
|
||||
@webob.dec.wsgify
|
||||
def __call__(self, req):
|
||||
arg_dict = req.environ['wsgiorg.routing_args'][1]
|
||||
action = arg_dict['action']
|
||||
del arg_dict['action']
|
||||
del arg_dict['controller']
|
||||
logging.debug('arg_dict: %s', arg_dict)
|
||||
|
||||
context = req.environ.get('openstack.context', {})
|
||||
# allow middleware up the stack to override the params
|
||||
params = {}
|
||||
if 'openstack.params' in req.environ:
|
||||
params = req.environ['openstack.params']
|
||||
params.update(arg_dict)
|
||||
|
||||
# TODO(termie): do some basic normalization on methods
|
||||
method = getattr(self, action)
|
||||
|
||||
# NOTE(vish): make sure we have no unicode keys for py2.6.
|
||||
params = self._normalize_dict(params)
|
||||
result = method(context, **params)
|
||||
|
||||
if result is None or type(result) is str or type(result) is unicode:
|
||||
return result
|
||||
elif isinstance(result, webob.exc.WSGIHTTPException):
|
||||
return result
|
||||
|
||||
return self._serialize(result)
|
||||
|
||||
def _serialize(self, result):
|
||||
return json.dumps(result, cls=utils.SmarterEncoder)
|
||||
|
||||
def _normalize_arg(self, arg):
|
||||
return str(arg).replace(':', '_').replace('-', '_')
|
||||
|
||||
def _normalize_dict(self, d):
|
||||
return dict([(self._normalize_arg(k), v)
|
||||
for (k, v) in d.iteritems()])
|
||||
|
||||
def assert_admin(self, context):
|
||||
if not context['is_admin']:
|
||||
user_token_ref = self.token_api.get_token(
|
||||
context=context, token_id=context['token_id'])
|
||||
creds = user_token_ref['metadata'].copy()
|
||||
creds['user_id'] = user_token_ref['user'].get('id')
|
||||
creds['tenant_id'] = user_token_ref['tenant'].get('id')
|
||||
print creds
|
||||
# Accept either is_admin or the admin role
|
||||
assert self.policy_api.can_haz(context,
|
||||
('is_admin:1', 'roles:admin'),
|
||||
creds)
|
||||
|
||||
|
||||
class Middleware(Application):
|
||||
"""Base WSGI middleware.
|
||||
|
||||
|
@ -310,6 +364,31 @@ class Router(object):
|
|||
return app
|
||||
|
||||
|
||||
class ComposingRouter(Router):
|
||||
def __init__(self, mapper=None, routers=None):
|
||||
if mapper is None:
|
||||
mapper = routes.Mapper()
|
||||
if routers is None:
|
||||
routers = []
|
||||
for router in routers:
|
||||
router.add_routes(mapper)
|
||||
super(ComposingRouter, self).__init__(mapper)
|
||||
|
||||
|
||||
class ComposableRouter(object):
|
||||
"""Router that supports use by ComposingRouter."""
|
||||
|
||||
def __init__(self, mapper=None):
|
||||
if mapper is None:
|
||||
mapper = routes.Mapper()
|
||||
self.add_routes(mapper)
|
||||
super(ComposableRouter, self).__init__(mapper)
|
||||
|
||||
def add_routes(self, mapper):
|
||||
"""Add routes to given mapper."""
|
||||
pass
|
||||
|
||||
|
||||
class ExtensionRouter(Router):
|
||||
"""A router that allows extensions to supplement or overwrite routes.
|
||||
|
||||
|
@ -317,10 +396,13 @@ class ExtensionRouter(Router):
|
|||
"""
|
||||
def __init__(self, application, mapper):
|
||||
self.application = application
|
||||
|
||||
self.add_routes(mapper)
|
||||
mapper.connect('{path_info:.*}', controller=self.application)
|
||||
super(ExtensionRouter, self).__init__(mapper)
|
||||
|
||||
def add_routes(self, mapper):
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def factory(cls, global_config, **local_config):
|
||||
"""Used for paste app factories in paste.deploy config files.
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
from keystone.contrib.admin_crud.core import *
|
|
@ -0,0 +1,150 @@
|
|||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
from keystone import catalog
|
||||
from keystone import identity
|
||||
from keystone.common import wsgi
|
||||
|
||||
|
||||
class CrudExtension(wsgi.ExtensionRouter):
|
||||
"""Previously known as the OS-KSADM extension.
|
||||
|
||||
Provides a bunch of CRUD operations for internal data types.
|
||||
|
||||
"""
|
||||
|
||||
def add_routes(self, mapper):
|
||||
tenant_controller = identity.TenantController()
|
||||
user_controller = identity.UserController()
|
||||
role_controller = identity.RoleController()
|
||||
service_controller = catalog.ServiceController()
|
||||
|
||||
# Tenant Operations
|
||||
mapper.connect("/tenants", controller=tenant_controller,
|
||||
action="create_tenant",
|
||||
conditions=dict(method=["POST"]))
|
||||
mapper.connect("/tenants/{tenant_id}",
|
||||
controller=tenant_controller,
|
||||
action="update_tenant",
|
||||
conditions=dict(method=["PUT"]))
|
||||
mapper.connect("/tenants/{tenant_id}",
|
||||
controller=tenant_controller,
|
||||
action="delete_tenant",
|
||||
conditions=dict(method=["DELETE"]))
|
||||
mapper.connect("/tenants/{tenant_id}/users",
|
||||
controller=user_controller,
|
||||
action="get_tenant_users",
|
||||
conditions=dict(method=["GET"]))
|
||||
|
||||
# User Operations
|
||||
mapper.connect("/users",
|
||||
controller=user_controller,
|
||||
action="get_users",
|
||||
conditions=dict(method=["GET"]))
|
||||
mapper.connect("/users",
|
||||
controller=user_controller,
|
||||
action="create_user",
|
||||
conditions=dict(method=["POST"]))
|
||||
# NOTE(termie): not in diablo
|
||||
mapper.connect("/users/{user_id}",
|
||||
controller=user_controller,
|
||||
action="update_user",
|
||||
conditions=dict(method=["PUT"]))
|
||||
mapper.connect("/users/{user_id}",
|
||||
controller=user_controller,
|
||||
action="delete_user",
|
||||
conditions=dict(method=["DELETE"]))
|
||||
|
||||
# COMPAT(diablo): the copy with no OS-KSADM is from diablo
|
||||
mapper.connect("/users/{user_id}/password",
|
||||
controller=user_controller,
|
||||
action="set_user_password",
|
||||
conditions=dict(method=["PUT"]))
|
||||
mapper.connect("/users/{user_id}/OS-KSADM/password",
|
||||
controller=user_controller,
|
||||
action="set_user_password",
|
||||
conditions=dict(method=["PUT"]))
|
||||
|
||||
# COMPAT(diablo): the copy with no OS-KSADM is from diablo
|
||||
mapper.connect("/users/{user_id}/tenant",
|
||||
controller=user_controller,
|
||||
action="update_user_tenant",
|
||||
conditions=dict(method=["PUT"]))
|
||||
mapper.connect("/users/{user_id}/OS-KSADM/tenant",
|
||||
controller=user_controller,
|
||||
action="update_user_tenant",
|
||||
conditions=dict(method=["PUT"]))
|
||||
|
||||
# COMPAT(diablo): the copy with no OS-KSADM is from diablo
|
||||
mapper.connect("/users/{user_id}/enabled",
|
||||
controller=user_controller,
|
||||
action="set_user_enabled",
|
||||
conditions=dict(method=["PUT"]))
|
||||
mapper.connect("/users/{user_id}/OS-KSADM/enabled",
|
||||
controller=user_controller,
|
||||
action="set_user_enabled",
|
||||
conditions=dict(method=["PUT"]))
|
||||
|
||||
# User Roles
|
||||
mapper.connect("/users/{user_id}/roles/OS-KSADM/{role_id}",
|
||||
controller=role_controller, action="add_role_to_user",
|
||||
conditions=dict(method=["PUT"]))
|
||||
mapper.connect("/users/{user_id}/roles/OS-KSADM/{role_id}",
|
||||
controller=role_controller, action="delete_role_from_user",
|
||||
conditions=dict(method=["DELETE"]))
|
||||
|
||||
# COMPAT(diablo): User Roles
|
||||
mapper.connect("/users/{user_id}/roleRefs",
|
||||
controller=role_controller, action="get_role_refs",
|
||||
conditions=dict(method=["GET"]))
|
||||
mapper.connect("/users/{user_id}/roleRefs",
|
||||
controller=role_controller, action="create_role_ref",
|
||||
conditions=dict(method=["POST"]))
|
||||
mapper.connect("/users/{user_id}/roleRefs/{role_ref_id}",
|
||||
controller=role_controller, action="delete_role_ref",
|
||||
conditions=dict(method=["DELETE"]))
|
||||
|
||||
# User-Tenant Roles
|
||||
mapper.connect(
|
||||
"/tenants/{tenant_id}/users/{user_id}/roles/OS-KSADM/{role_id}",
|
||||
controller=role_controller, action="add_role_to_user",
|
||||
conditions=dict(method=["PUT"]))
|
||||
mapper.connect(
|
||||
"/tenants/{tenant_id}/users/{user_id}/roles/OS-KSADM/{role_id}",
|
||||
controller=role_controller, action="delete_role_from_user",
|
||||
conditions=dict(method=["DELETE"]))
|
||||
|
||||
# Service Operations
|
||||
mapper.connect("/OS-KSADM/services",
|
||||
controller=service_controller,
|
||||
action="get_services",
|
||||
conditions=dict(method=["GET"]))
|
||||
mapper.connect("/OS-KSADM/services",
|
||||
controller=service_controller,
|
||||
action="create_service",
|
||||
conditions=dict(method=["POST"]))
|
||||
mapper.connect("/OS-KSADM/services/{service_id}",
|
||||
controller=service_controller,
|
||||
action="delete_service",
|
||||
conditions=dict(method=["DELETE"]))
|
||||
mapper.connect("/OS-KSADM/services/{service_id}",
|
||||
controller=service_controller,
|
||||
action="get_service",
|
||||
conditions=dict(method=["GET"]))
|
||||
|
||||
# Role Operations
|
||||
mapper.connect("/OS-KSADM/roles",
|
||||
controller=role_controller,
|
||||
action="create_role",
|
||||
conditions=dict(method=["POST"]))
|
||||
mapper.connect("/OS-KSADM/roles",
|
||||
controller=role_controller,
|
||||
action="get_roles",
|
||||
conditions=dict(method=["GET"]))
|
||||
mapper.connect("/OS-KSADM/roles/{role_id}",
|
||||
controller=role_controller,
|
||||
action="get_role",
|
||||
conditions=dict(method=["GET"]))
|
||||
mapper.connect("/OS-KSADM/roles/{role_id}",
|
||||
controller=role_controller,
|
||||
action="delete_role",
|
||||
conditions=dict(method=["DELETE"]))
|
|
@ -0,0 +1 @@
|
|||
from keystone.contrib.ec2.core import *
|
|
@ -0,0 +1,32 @@
|
|||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
from keystone.common import kvs
|
||||
|
||||
|
||||
class Ec2(kvs.Base):
|
||||
# Public interface
|
||||
def get_credential(self, credential_id):
|
||||
credential_ref = self.db.get('credential-%s' % credential_id)
|
||||
return credential_ref
|
||||
|
||||
def list_credentials(self, user_id):
|
||||
credential_ids = self.db.get('credential_list', [])
|
||||
rv = [self.get_credential(x) for x in credential_ids]
|
||||
return [x for x in rv if x['user_id'] == user_id]
|
||||
|
||||
# CRUD
|
||||
def create_credential(self, credential_id, credential):
|
||||
self.db.set('credential-%s' % credential_id, credential)
|
||||
credential_list = set(self.db.get('credential_list', []))
|
||||
credential_list.add(credential_id)
|
||||
self.db.set('credential_list', list(credential_list))
|
||||
return credential
|
||||
|
||||
def delete_credential(self, credential_id):
|
||||
old_credential = self.db.get('credential-%s' % credential_id)
|
||||
self.db.delete('credential-%s' % credential_id)
|
||||
credential_list = set(self.db.get('credential_list', []))
|
||||
credential_list.remove(credential_id)
|
||||
self.db.set('credential_list', list(credential_list))
|
||||
return None
|
||||
|
|
@ -2,8 +2,13 @@
|
|||
|
||||
"""Main entry point into the EC2 Credentials service."""
|
||||
|
||||
from keystone import catalog
|
||||
from keystone import config
|
||||
from keystone import manager
|
||||
from keystone import identity
|
||||
from keystone import policy
|
||||
from keystone import token
|
||||
from keystone.common import manager
|
||||
from keystone.common import wsgi
|
||||
|
||||
|
||||
CONF = config.CONF
|
||||
|
@ -22,3 +27,131 @@ class Manager(manager.Manager):
|
|||
|
||||
def __init__(self):
|
||||
super(Manager, self).__init__(CONF.ec2.driver)
|
||||
|
||||
|
||||
class Ec2Extension(wsgi.ExtensionRouter):
|
||||
def add_routes(self, mapper)
|
||||
ec2_controller = Ec2Controller()
|
||||
# validation
|
||||
mapper.connect('/ec2tokens',
|
||||
controller=ec2_controller,
|
||||
action='authenticate_ec2',
|
||||
conditions=dict(method=['POST']))
|
||||
|
||||
# crud
|
||||
mapper.connect('/users/{user_id}/credentials/OS-EC2',
|
||||
controller=ec2_controller,
|
||||
action='create_credential',
|
||||
conditions=dict(method=['POST']))
|
||||
mapper.connect('/users/{user_id}/credentials/OS-EC2',
|
||||
controller=ec2_controller,
|
||||
action='get_credentials',
|
||||
conditions=dict(method=['GET']))
|
||||
mapper.connect('/users/{user_id}/credentials/OS-EC2/{credential_id}',
|
||||
controller=ec2_controller,
|
||||
action='get_credential',
|
||||
conditions=dict(method=['GET']))
|
||||
mapper.connect('/users/{user_id}/credentials/OS-EC2/{credential_id}',
|
||||
controller=ec2_controller,
|
||||
action='delete_credential',
|
||||
conditions=dict(method=['DELETE']))
|
||||
|
||||
|
||||
class Ec2Controller(wsgi.Application):
|
||||
def __init__(self):
|
||||
self.catalog_api = catalog.Manager()
|
||||
self.identity_api = identity.Manager()
|
||||
self.token_api = token.Manager()
|
||||
self.policy_api = policy.Manager()
|
||||
self.ec2_api = Manager()
|
||||
super(Ec2Controller, self).__init__()
|
||||
|
||||
def authenticate_ec2(self, context, credentials=None,
|
||||
ec2Credentials=None):
|
||||
"""Validate a signed EC2 request and provide a token."""
|
||||
# NOTE(termie): backwards compat hack
|
||||
if not credentials and ec2Credentials:
|
||||
credentials = ec2Credentials
|
||||
creds_ref = self.ec2_api.get_credential(context,
|
||||
credentials['access'])
|
||||
|
||||
signer = utils.Signer(creds_ref['secret'])
|
||||
signature = signer.generate(credentials)
|
||||
if signature == credentials['signature']:
|
||||
pass
|
||||
# NOTE(vish): Some libraries don't use the port when signing
|
||||
# requests, so try again without port.
|
||||
elif ':' in credentials['signature']:
|
||||
hostname, _port = credentials['host'].split(":")
|
||||
credentials['host'] = hostname
|
||||
signature = signer.generate(credentials)
|
||||
if signature != credentials.signature:
|
||||
# TODO(termie): proper exception
|
||||
raise Exception("Not Authorized")
|
||||
else:
|
||||
raise Exception("Not Authorized")
|
||||
|
||||
# TODO(termie): don't create new tokens every time
|
||||
# TODO(termie): this is copied from TokenController.authenticate
|
||||
token_id = uuid.uuid4().hex
|
||||
tenant_ref = self.identity_api.get_tenant(creds_ref['tenant_id'])
|
||||
user_ref = self.identity_api.get_user(creds_ref['user_id'])
|
||||
metadata_ref = self.identity_api.get_metadata(
|
||||
context=context,
|
||||
user_id=user_ref['id'],
|
||||
tenant_id=tenant_ref['id'])
|
||||
catalog_ref = self.catalog_api.get_catalog(
|
||||
context=context,
|
||||
user_id=user_ref['id'],
|
||||
tenant_id=tenant_ref['id'],
|
||||
metadata=metadata_ref)
|
||||
|
||||
token_ref = self.token_api.create_token(
|
||||
context, token_id, dict(expires='',
|
||||
id=token_id,
|
||||
user=user_ref,
|
||||
tenant=tenant_ref,
|
||||
metadata=metadata_ref))
|
||||
|
||||
# TODO(termie): optimize this call at some point and put it into the
|
||||
# the return for metadata
|
||||
# fill out the roles in the metadata
|
||||
roles_ref = []
|
||||
for role_id in metadata_ref.get('roles', []):
|
||||
roles_ref.append(self.identity_api.get_role(context, role_id))
|
||||
|
||||
# TODO(termie): make this a util function or something
|
||||
# TODO(termie): i don't think the ec2 middleware currently expects a
|
||||
# full return, but it contains a note saying that it
|
||||
# would be better to expect a full return
|
||||
return TokenController._format_authenticate(
|
||||
self, token_ref, roles_ref, catalog_ref)
|
||||
|
||||
def create_credential(self, context, user_id, tenant_id):
|
||||
# TODO(termie): validate that this request is valid for given user
|
||||
# tenant
|
||||
cred_ref = {'user_id': user_id,
|
||||
'tenant_id': tenant_id,
|
||||
'access': uuid.uuid4().hex,
|
||||
'secret': uuid.uuid4().hex}
|
||||
self.ec2_api.create_credential(context, cred_ref['access'], cred_ref)
|
||||
return {'credential': cred_ref}
|
||||
|
||||
def get_credentials(self, context, user_id):
|
||||
"""List credentials for the given user_id."""
|
||||
# TODO(termie): validate that this request is valid for given user
|
||||
# tenant
|
||||
return {'credentials': self.ec2_api.list_credentials(context, user_id)}
|
||||
|
||||
def get_credential(self, context, user_id, credential_id):
|
||||
# TODO(termie): validate that this request is valid for given user
|
||||
# tenant
|
||||
return {'credential': self.ec2_api.get_credential(context,
|
||||
credential_id)}
|
||||
|
||||
def delete_credential(self, context, user_id, credential_id):
|
||||
# TODO(termie): validate that this request is valid for given user
|
||||
# tenant
|
||||
return self.ec2_api.delete_credential(context, credential_id)
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
from keystone.identity.core import *
|
|
@ -0,0 +1,178 @@
|
|||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
from keystone.common import kvs
|
||||
|
||||
|
||||
class Identity(kvs.Base):
|
||||
# Public interface
|
||||
def authenticate(self, user_id=None, tenant_id=None, password=None):
|
||||
"""Authenticate based on a user, tenant and password.
|
||||
|
||||
Expects the user object to have a password field and the tenant to be
|
||||
in the list of tenants on the user.
|
||||
|
||||
"""
|
||||
user_ref = self.get_user(user_id)
|
||||
tenant_ref = None
|
||||
metadata_ref = None
|
||||
if not user_ref or user_ref.get('password') != password:
|
||||
raise AssertionError('Invalid user / password')
|
||||
if tenant_id and tenant_id not in user_ref['tenants']:
|
||||
raise AssertionError('Invalid tenant')
|
||||
|
||||
tenant_ref = self.get_tenant(tenant_id)
|
||||
if tenant_ref:
|
||||
metadata_ref = self.get_metadata(user_id, tenant_id)
|
||||
else:
|
||||
metadata_ref = {}
|
||||
return (user_ref, tenant_ref, metadata_ref)
|
||||
|
||||
def get_tenant(self, tenant_id):
|
||||
tenant_ref = self.db.get('tenant-%s' % tenant_id)
|
||||
return tenant_ref
|
||||
|
||||
def get_tenant_by_name(self, tenant_name):
|
||||
tenant_ref = self.db.get('tenant_name-%s' % tenant_name)
|
||||
return tenant_ref
|
||||
|
||||
def get_user(self, user_id):
|
||||
user_ref = self.db.get('user-%s' % user_id)
|
||||
return user_ref
|
||||
|
||||
def get_user_by_name(self, user_name):
|
||||
user_ref = self.db.get('user_name-%s' % user_name)
|
||||
return user_ref
|
||||
|
||||
def get_metadata(self, user_id, tenant_id):
|
||||
return self.db.get('metadata-%s-%s' % (tenant_id, user_id))
|
||||
|
||||
def get_role(self, role_id):
|
||||
role_ref = self.db.get('role-%s' % role_id)
|
||||
return role_ref
|
||||
|
||||
def list_users(self):
|
||||
user_ids = self.db.get('user_list', [])
|
||||
return [self.get_user(x) for x in user_ids]
|
||||
|
||||
def list_roles(self):
|
||||
role_ids = self.db.get('role_list', [])
|
||||
return [self.get_role(x) for x in role_ids]
|
||||
|
||||
# These should probably be part of the high-level API
|
||||
def add_user_to_tenant(self, tenant_id, user_id):
|
||||
user_ref = self.get_user(user_id)
|
||||
tenants = set(user_ref.get('tenants', []))
|
||||
tenants.add(tenant_id)
|
||||
user_ref['tenants'] = list(tenants)
|
||||
self.update_user(user_id, user_ref)
|
||||
|
||||
def remove_user_from_tenant(self, tenant_id, user_id):
|
||||
user_ref = self.get_user(user_id)
|
||||
tenants = set(user_ref.get('tenants', []))
|
||||
tenants.remove(tenant_id)
|
||||
user_ref['tenants'] = list(tenants)
|
||||
self.update_user(user_id, user_ref)
|
||||
|
||||
def get_tenants_for_user(self, user_id):
|
||||
user_ref = self.get_user(user_id)
|
||||
return user_ref.get('tenants', [])
|
||||
|
||||
def get_roles_for_user_and_tenant(self, user_id, tenant_id):
|
||||
metadata_ref = self.get_metadata(user_id, tenant_id)
|
||||
if not metadata_ref:
|
||||
metadata_ref = {}
|
||||
return metadata_ref.get('roles', [])
|
||||
|
||||
def add_role_to_user_and_tenant(self, user_id, tenant_id, role_id):
|
||||
metadata_ref = self.get_metadata(user_id, tenant_id)
|
||||
if not metadata_ref:
|
||||
metadata_ref = {}
|
||||
roles = set(metadata_ref.get('roles', []))
|
||||
roles.add(role_id)
|
||||
metadata_ref['roles'] = list(roles)
|
||||
self.update_metadata(user_id, tenant_id, metadata_ref)
|
||||
|
||||
def remove_role_from_user_and_tenant(self, user_id, tenant_id, role_id):
|
||||
metadata_ref = self.get_metadata(user_id, tenant_id)
|
||||
if not metadata_ref:
|
||||
metadata_ref = {}
|
||||
roles = set(metadata_ref.get('roles', []))
|
||||
roles.remove(role_id)
|
||||
metadata_ref['roles'] = list(roles)
|
||||
self.update_metadata(user_id, tenant_id, metadata_ref)
|
||||
|
||||
# CRUD
|
||||
def create_user(self, user_id, user):
|
||||
self.db.set('user-%s' % user_id, user)
|
||||
self.db.set('user_name-%s' % user['name'], user)
|
||||
user_list = set(self.db.get('user_list', []))
|
||||
user_list.add(user_id)
|
||||
self.db.set('user_list', list(user_list))
|
||||
return user
|
||||
|
||||
def update_user(self, user_id, user):
|
||||
# get the old name and delete it too
|
||||
old_user = self.db.get('user-%s' % user_id)
|
||||
self.db.delete('user_name-%s' % old_user['name'])
|
||||
self.db.set('user-%s' % user_id, user)
|
||||
self.db.set('user_name-%s' % user['name'], user)
|
||||
return user
|
||||
|
||||
def delete_user(self, user_id):
|
||||
old_user = self.db.get('user-%s' % user_id)
|
||||
self.db.delete('user_name-%s' % old_user['name'])
|
||||
self.db.delete('user-%s' % user_id)
|
||||
user_list = set(self.db.get('user_list', []))
|
||||
user_list.remove(user_id)
|
||||
self.db.set('user_list', list(user_list))
|
||||
return None
|
||||
|
||||
def create_tenant(self, tenant_id, tenant):
|
||||
self.db.set('tenant-%s' % tenant_id, tenant)
|
||||
self.db.set('tenant_name-%s' % tenant['name'], tenant)
|
||||
return tenant
|
||||
|
||||
def update_tenant(self, tenant_id, tenant):
|
||||
# get the old name and delete it too
|
||||
old_tenant = self.db.get('tenant-%s' % tenant_id)
|
||||
self.db.delete('tenant_name-%s' % old_tenant['name'])
|
||||
self.db.set('tenant-%s' % tenant_id, tenant)
|
||||
self.db.set('tenant_name-%s' % tenant['name'], tenant)
|
||||
return tenant
|
||||
|
||||
def delete_tenant(self, tenant_id):
|
||||
old_tenant = self.db.get('tenant-%s' % tenant_id)
|
||||
self.db.delete('tenant_name-%s' % old_tenant['name'])
|
||||
self.db.delete('tenant-%s' % tenant_id)
|
||||
return None
|
||||
|
||||
def create_metadata(self, user_id, tenant_id, metadata):
|
||||
self.db.set('metadata-%s-%s' % (tenant_id, user_id), metadata)
|
||||
return metadata
|
||||
|
||||
def update_metadata(self, user_id, tenant_id, metadata):
|
||||
self.db.set('metadata-%s-%s' % (tenant_id, user_id), metadata)
|
||||
return metadata
|
||||
|
||||
def delete_metadata(self, user_id, tenant_id):
|
||||
self.db.delete('metadata-%s-%s' % (tenant_id, user_id))
|
||||
return None
|
||||
|
||||
def create_role(self, role_id, role):
|
||||
self.db.set('role-%s' % role_id, role)
|
||||
role_list = set(self.db.get('role_list', []))
|
||||
role_list.add(role_id)
|
||||
self.db.set('role_list', list(role_list))
|
||||
return role
|
||||
|
||||
def update_role(self, role_id, role):
|
||||
self.db.set('role-%s' % role_id, role)
|
||||
return role
|
||||
|
||||
def delete_role(self, role_id):
|
||||
self.db.delete('role-%s' % role_id)
|
||||
role_list = set(self.db.get('role_list', []))
|
||||
role_list.remove(role_id)
|
||||
self.db.set('role_list', list(role_list))
|
||||
return None
|
||||
|
|
@ -22,3 +22,296 @@ class Manager(manager.Manager):
|
|||
|
||||
def __init__(self):
|
||||
super(Manager, self).__init__(CONF.identity.driver)
|
||||
|
||||
|
||||
class PublicRouter(wsgi.ComposableRouter):
|
||||
def add_routes(self, mapper):
|
||||
tenant_controller = TenantController()
|
||||
mapper.connect('/tenants',
|
||||
controller=tenant_controller,
|
||||
action='get_tenants_for_token',
|
||||
conditions=dict(methods=['GET']))
|
||||
|
||||
|
||||
class AdminRouter(wsgi.ComposableRouter):
|
||||
def add_routes(self, mapper):
|
||||
# Tenant Operations
|
||||
tenant_controller = TenantController()
|
||||
mapper.connect('/tenants',
|
||||
controller=tenant_controller,
|
||||
action='get_tenants_for_token',
|
||||
conditions=dict(method=['GET']))
|
||||
mapper.connect('/tenants/{tenant_id}',
|
||||
controller=tenant_controller,
|
||||
action='get_tenant',
|
||||
conditions=dict(method=['GET']))
|
||||
|
||||
# User Operations
|
||||
user_controller = UserController()
|
||||
mapper.connect('/users/{user_id}',
|
||||
controller=user_controller,
|
||||
action='get_user',
|
||||
conditions=dict(method=['GET']))
|
||||
|
||||
# Role Operations
|
||||
roles_controller = RoleController()
|
||||
mapper.connect('/tenants/{tenant_id}/users/{user_id}/roles',
|
||||
controller=roles_controller,
|
||||
action='get_user_roles',
|
||||
conditions=dict(method=['GET']))
|
||||
mapper.connect('/users/{user_id}/roles',
|
||||
controller=user_controller,
|
||||
action='get_user_roles',
|
||||
conditions=dict(method=['GET']))
|
||||
|
||||
|
||||
class TenantController(Application):
|
||||
def __init__(self):
|
||||
self.identity_api = identity.Manager()
|
||||
self.policy_api = policy.Manager()
|
||||
self.token_api = token.Manager()
|
||||
super(TenantController, self).__init__()
|
||||
|
||||
def get_tenants_for_token(self, context, **kw):
|
||||
"""Get valid tenants for token based on token used to authenticate.
|
||||
|
||||
Pulls the token from the context, validates it and gets the valid
|
||||
tenants for the user in the token.
|
||||
|
||||
Doesn't care about token scopedness.
|
||||
|
||||
"""
|
||||
token_ref = self.token_api.get_token(context=context,
|
||||
token_id=context['token_id'])
|
||||
assert token_ref is not None
|
||||
|
||||
user_ref = token_ref['user']
|
||||
tenant_ids = self.identity_api.get_tenants_for_user(
|
||||
context, user_ref['id'])
|
||||
tenant_refs = []
|
||||
for tenant_id in tenant_ids:
|
||||
tenant_refs.append(self.identity_api.get_tenant(
|
||||
context=context,
|
||||
tenant_id=tenant_id))
|
||||
return self._format_tenants_for_token(tenant_refs)
|
||||
|
||||
def get_tenant(self, context, tenant_id):
|
||||
# TODO(termie): this stuff should probably be moved to middleware
|
||||
if not context['is_admin']:
|
||||
user_token_ref = self.token_api.get_token(
|
||||
context=context, token_id=context['token_id'])
|
||||
creds = user_token_ref['metadata'].copy()
|
||||
creds['user_id'] = user_token_ref['user'].get('id')
|
||||
creds['tenant_id'] = user_token_ref['tenant'].get('id')
|
||||
# Accept either is_admin or the admin role
|
||||
assert self.policy_api.can_haz(context,
|
||||
('is_admin:1', 'roles:admin'),
|
||||
creds)
|
||||
|
||||
tenant = self.identity_api.get_tenant(context, tenant_id)
|
||||
if not tenant:
|
||||
return webob.exc.HTTPNotFound()
|
||||
return {'tenant': tenant}
|
||||
|
||||
# CRUD Extension
|
||||
def create_tenant(self, context, tenant):
|
||||
tenant_ref = self._normalize_dict(tenant)
|
||||
self.assert_admin(context)
|
||||
tenant_id = (tenant_ref.get('id')
|
||||
and tenant_ref.get('id')
|
||||
or uuid.uuid4().hex)
|
||||
tenant_ref['id'] = tenant_id
|
||||
|
||||
tenant = self.identity_api.create_tenant(
|
||||
context, tenant_id, tenant_ref)
|
||||
return {'tenant': tenant}
|
||||
|
||||
def update_tenant(self, context, tenant_id, tenant):
|
||||
self.assert_admin(context)
|
||||
tenant_ref = self.identity_api.update_tenant(
|
||||
context, tenant_id, tenant)
|
||||
return {'tenant': tenant_ref}
|
||||
|
||||
def delete_tenant(self, context, tenant_id, **kw):
|
||||
self.assert_admin(context)
|
||||
self.identity_api.delete_tenant(context, tenant_id)
|
||||
|
||||
def get_tenant_users(self, context, **kw):
|
||||
self.assert_admin(context)
|
||||
raise NotImplementedError()
|
||||
|
||||
def _format_tenants_for_token(self, tenant_refs):
|
||||
for x in tenant_refs:
|
||||
x['enabled'] = True
|
||||
o = {'tenants': tenant_refs,
|
||||
'tenants_links': []}
|
||||
return o
|
||||
|
||||
|
||||
class UserController(Application):
|
||||
def __init__(self):
|
||||
self.catalog_api = catalog.Manager()
|
||||
self.identity_api = identity.Manager()
|
||||
self.policy_api = policy.Manager()
|
||||
self.token_api = token.Manager()
|
||||
super(UserController, self).__init__()
|
||||
|
||||
def get_user(self, context, user_id):
|
||||
self.assert_admin(context)
|
||||
user_ref = self.identity_api.get_user(context, user_id)
|
||||
if not user_ref:
|
||||
raise webob.exc.HTTPNotFound()
|
||||
return {'user': user_ref}
|
||||
|
||||
def get_users(self, context):
|
||||
# NOTE(termie): i can't imagine that this really wants all the data
|
||||
# about every single user in the system...
|
||||
self.assert_admin(context)
|
||||
user_refs = self.identity_api.list_users(context)
|
||||
return {'users': user_refs}
|
||||
|
||||
# CRUD extension
|
||||
def create_user(self, context, user):
|
||||
user = self._normalize_dict(user)
|
||||
self.assert_admin(context)
|
||||
tenant_id = user.get('tenantId', None)
|
||||
user_id = uuid.uuid4().hex
|
||||
user_ref = user.copy()
|
||||
user_ref['id'] = user_id
|
||||
new_user_ref = self.identity_api.create_user(
|
||||
context, user_id, user_ref)
|
||||
if tenant_id:
|
||||
self.identity_api.add_user_to_tenant(tenant_id, user_id)
|
||||
return {'user': new_user_ref}
|
||||
|
||||
# NOTE(termie): this is really more of a patch than a put
|
||||
def update_user(self, context, user_id, user):
|
||||
self.assert_admin(context)
|
||||
user_ref = self.identity_api.get_user(context, user_id)
|
||||
del user['id']
|
||||
user_ref.update(user)
|
||||
self.identity_api.update_user(context, user_id, user_ref)
|
||||
return {'user': user_ref}
|
||||
|
||||
def delete_user(self, context, user_id):
|
||||
self.assert_admin(context)
|
||||
self.identity_api.delete_user(context, user_id)
|
||||
|
||||
def set_user_enabled(self, context, user_id, user):
|
||||
return self.update_user(context, user_id, user)
|
||||
|
||||
def set_user_password(self, context, user_id, user):
|
||||
return self.update_user(context, user_id, user)
|
||||
|
||||
def update_user_tenant(self, context, user_id, user):
|
||||
"""Update the default tenant."""
|
||||
# ensure that we're a member of that tenant
|
||||
tenant_id = user.get('tenantId')
|
||||
self.identity_api.add_user_to_tenant(context, tenant_id, user_id)
|
||||
return self.update_user(context, user_id, user)
|
||||
|
||||
|
||||
class RoleController(Application):
|
||||
def __init__(self):
|
||||
self.catalog_api = catalog.Manager()
|
||||
self.identity_api = identity.Manager()
|
||||
self.token_api = token.Manager()
|
||||
self.policy_api = policy.Manager()
|
||||
super(RoleController, self).__init__()
|
||||
|
||||
def get_user_roles(self, context, user_id, tenant_id=None):
|
||||
raise NotImplemented()
|
||||
|
||||
# CRUD extension
|
||||
def get_role(self, context, role_id):
|
||||
self.assert_admin(context)
|
||||
role_ref = self.identity_api.get_role(context, role_id)
|
||||
if not role_ref:
|
||||
raise webob.exc.HTTPNotFound()
|
||||
return {'role': role_ref}
|
||||
|
||||
def create_role(self, context, role):
|
||||
role = self._normalize_dict(role)
|
||||
self.assert_admin(context)
|
||||
role_id = uuid.uuid4().hex
|
||||
role['id'] = role_id
|
||||
role_ref = self.identity_api.create_role(context, role_id, role)
|
||||
return {'role': role_ref}
|
||||
|
||||
def delete_role(self, context, role_id):
|
||||
self.assert_admin(context)
|
||||
role_ref = self.identity_api.delete_role(context, role_id)
|
||||
|
||||
def get_roles(self, context):
|
||||
self.assert_admin(context)
|
||||
roles = self.identity_api.list_roles(context)
|
||||
# TODO(termie): probably inefficient at some point
|
||||
return {'roles': roles}
|
||||
|
||||
# COMPAT(diablo): CRUD extension
|
||||
def get_role_refs(self, context, user_id):
|
||||
"""Ultimate hack to get around having to make role_refs first-class.
|
||||
|
||||
This will basically iterate over the various roles the user has in
|
||||
all tenants the user is a member of and create fake role_refs where
|
||||
the id encodes the user-tenant-role information so we can look
|
||||
up the appropriate data when we need to delete them.
|
||||
|
||||
"""
|
||||
self.assert_admin(context)
|
||||
user_ref = self.identity_api.get_user(context, user_id)
|
||||
tenant_ids = self.identity_api.get_tenants_for_user(context, user_id)
|
||||
o = []
|
||||
for tenant_id in tenant_ids:
|
||||
role_ids = self.identity_api.get_roles_for_user_and_tenant(
|
||||
context, user_id, tenant_id)
|
||||
for role_id in role_ids:
|
||||
ref = {'roleId': role_id,
|
||||
'tenantId': tenant_id,
|
||||
'userId': user_id}
|
||||
ref['id'] = urllib.urlencode(ref)
|
||||
o.append(ref)
|
||||
return {'roles': o}
|
||||
|
||||
def create_role_ref(self, context, user_id, role):
|
||||
"""This is actually used for adding a user to a tenant.
|
||||
|
||||
In the legacy data model adding a user to a tenant required setting
|
||||
a role.
|
||||
|
||||
"""
|
||||
self.assert_admin(context)
|
||||
# TODO(termie): for now we're ignoring the actual role
|
||||
tenant_id = role.get('tenantId')
|
||||
role_id = role.get('roleId')
|
||||
self.identity_api.add_user_to_tenant(context, tenant_id, user_id)
|
||||
self.identity_api.add_role_to_user_and_tenant(
|
||||
context, user_id, tenant_id, role_id)
|
||||
role_ref = self.identity_api.get_role(context, role_id)
|
||||
return {'role': role_ref}
|
||||
|
||||
def delete_role_ref(self, context, user_id, role_ref_id):
|
||||
"""This is actually used for deleting a user from a tenant.
|
||||
|
||||
In the legacy data model removing a user from a tenant required
|
||||
deleting a role.
|
||||
|
||||
To emulate this, we encode the tenant and role in the role_ref_id,
|
||||
and if this happens to be the last role for the user-tenant pair,
|
||||
we remove the user from the tenant.
|
||||
|
||||
"""
|
||||
self.assert_admin(context)
|
||||
# TODO(termie): for now we're ignoring the actual role
|
||||
role_ref_ref = urlparse.parse_qs(role_ref_id)
|
||||
tenant_id = role_ref_ref.get('tenantId')[0]
|
||||
role_id = role_ref_ref.get('roleId')[0]
|
||||
self.identity_api.remove_role_from_user_and_tenant(
|
||||
context, user_id, tenant_id, role_id)
|
||||
roles = self.identity_api.get_roles_for_user_and_tenant(
|
||||
context, user_id, tenant_id)
|
||||
if not roles:
|
||||
self.identity_api.remove_user_from_tenant(
|
||||
context, tenant_id, user_id)
|
||||
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
|
||||
import logging
|
||||
from keystone.common import logging
|
||||
|
||||
|
||||
class TrivialTrue(object):
|
||||
|
|
|
@ -10,70 +10,15 @@ import webob.dec
|
|||
import webob.exc
|
||||
|
||||
from keystone import catalog
|
||||
from keystone import ec2
|
||||
from keystone import identity
|
||||
from keystone import logging
|
||||
from keystone import policy
|
||||
from keystone import token
|
||||
from keystone import utils
|
||||
from keystone import wsgi
|
||||
from keystone.common import logging
|
||||
from keystone.common import utils
|
||||
from keystone.common import wsgi
|
||||
|
||||
|
||||
class Application(wsgi.Application):
|
||||
@webob.dec.wsgify
|
||||
def __call__(self, req):
|
||||
arg_dict = req.environ['wsgiorg.routing_args'][1]
|
||||
action = arg_dict['action']
|
||||
del arg_dict['action']
|
||||
del arg_dict['controller']
|
||||
logging.debug('arg_dict: %s', arg_dict)
|
||||
|
||||
context = req.environ.get('openstack.context', {})
|
||||
# allow middleware up the stack to override the params
|
||||
params = {}
|
||||
if 'openstack.params' in req.environ:
|
||||
params = req.environ['openstack.params']
|
||||
params.update(arg_dict)
|
||||
|
||||
# TODO(termie): do some basic normalization on methods
|
||||
method = getattr(self, action)
|
||||
|
||||
# NOTE(vish): make sure we have no unicode keys for py2.6.
|
||||
params = self._normalize_dict(params)
|
||||
result = method(context, **params)
|
||||
|
||||
if result is None or type(result) is str or type(result) is unicode:
|
||||
return result
|
||||
elif isinstance(result, webob.exc.WSGIHTTPException):
|
||||
return result
|
||||
|
||||
return self._serialize(result)
|
||||
|
||||
def _serialize(self, result):
|
||||
return json.dumps(result, cls=utils.SmarterEncoder)
|
||||
|
||||
def _normalize_arg(self, arg):
|
||||
return str(arg).replace(':', '_').replace('-', '_')
|
||||
|
||||
def _normalize_dict(self, d):
|
||||
return dict([(self._normalize_arg(k), v)
|
||||
for (k, v) in d.iteritems()])
|
||||
|
||||
def assert_admin(self, context):
|
||||
if not context['is_admin']:
|
||||
user_token_ref = self.token_api.get_token(
|
||||
context=context, token_id=context['token_id'])
|
||||
creds = user_token_ref['metadata'].copy()
|
||||
creds['user_id'] = user_token_ref['user'].get('id')
|
||||
creds['tenant_id'] = user_token_ref['tenant'].get('id')
|
||||
print creds
|
||||
# Accept either is_admin or the admin role
|
||||
assert self.policy_api.can_haz(context,
|
||||
('is_admin:1', 'roles:admin'),
|
||||
creds)
|
||||
|
||||
|
||||
class AdminRouter(wsgi.Router):
|
||||
class AdminRouter(wsgi.ComposingRouter):
|
||||
def __init__(self):
|
||||
mapper = routes.Mapper()
|
||||
|
||||
|
@ -92,34 +37,6 @@ class AdminRouter(wsgi.Router):
|
|||
action='endpoints',
|
||||
conditions=dict(method=['GET']))
|
||||
|
||||
# Tenant Operations
|
||||
tenant_controller = TenantController()
|
||||
mapper.connect('/tenants',
|
||||
controller=tenant_controller,
|
||||
action='get_tenants_for_token',
|
||||
conditions=dict(method=['GET']))
|
||||
mapper.connect('/tenants/{tenant_id}',
|
||||
controller=tenant_controller,
|
||||
action='get_tenant',
|
||||
conditions=dict(method=['GET']))
|
||||
|
||||
# User Operations
|
||||
user_controller = UserController()
|
||||
mapper.connect('/users/{user_id}',
|
||||
controller=user_controller,
|
||||
action='get_user',
|
||||
conditions=dict(method=['GET']))
|
||||
|
||||
# Role Operations
|
||||
roles_controller = RoleController()
|
||||
mapper.connect('/tenants/{tenant_id}/users/{user_id}/roles',
|
||||
controller=roles_controller,
|
||||
action='get_user_roles',
|
||||
conditions=dict(method=['GET']))
|
||||
mapper.connect('/users/{user_id}/roles',
|
||||
controller=user_controller,
|
||||
action='get_user_roles',
|
||||
conditions=dict(method=['GET']))
|
||||
|
||||
# Miscellaneous Operations
|
||||
version_controller = VersionController()
|
||||
|
@ -133,11 +50,12 @@ class AdminRouter(wsgi.Router):
|
|||
controller=extensions_controller,
|
||||
action='get_extensions_info',
|
||||
conditions=dict(method=['GET']))
|
||||
|
||||
super(AdminRouter, self).__init__(mapper)
|
||||
identity_router = identity.AdminRouter()
|
||||
routers = [identity_router]
|
||||
super(AdminRouter, self).__init__(mapper, identity_router)
|
||||
|
||||
|
||||
class PublicRouter(wsgi.Router):
|
||||
class PublicRouter(wsgi.ComposingRouter):
|
||||
def __init__(self):
|
||||
mapper = routes.Mapper()
|
||||
|
||||
|
@ -153,13 +71,6 @@ class PublicRouter(wsgi.Router):
|
|||
action='authenticate',
|
||||
conditions=dict(method=['POST']))
|
||||
|
||||
# Tenant Operations
|
||||
tenant_controller = TenantController()
|
||||
mapper.connect('/tenants',
|
||||
controller=tenant_controller,
|
||||
action='get_tenants_for_token',
|
||||
conditions=dict(methods=['GET']))
|
||||
|
||||
# Miscellaneous
|
||||
version_controller = VersionController()
|
||||
mapper.connect('/',
|
||||
|
@ -174,289 +85,13 @@ class PublicRouter(wsgi.Router):
|
|||
action='get_extensions_info',
|
||||
conditions=dict(method=['GET']))
|
||||
|
||||
super(PublicRouter, self).__init__(mapper)
|
||||
identity_router = identity.PublicRouter()
|
||||
routers = [identity_router]
|
||||
|
||||
super(PublicRouter, self).__init__(mapper, routers)
|
||||
|
||||
|
||||
class AdminCrudExtension(wsgi.ExtensionRouter):
|
||||
"""Previously known as the OS-KSADM extension.
|
||||
|
||||
Provides a bunch of CRUD operations for internal data types.
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, application):
|
||||
mapper = routes.Mapper()
|
||||
tenant_controller = TenantController()
|
||||
user_controller = UserController()
|
||||
role_controller = RoleController()
|
||||
service_controller = ServiceController()
|
||||
|
||||
# Tenant Operations
|
||||
mapper.connect("/tenants", controller=tenant_controller,
|
||||
action="create_tenant",
|
||||
conditions=dict(method=["POST"]))
|
||||
mapper.connect("/tenants/{tenant_id}",
|
||||
controller=tenant_controller,
|
||||
action="update_tenant",
|
||||
conditions=dict(method=["PUT"]))
|
||||
mapper.connect("/tenants/{tenant_id}",
|
||||
controller=tenant_controller,
|
||||
action="delete_tenant",
|
||||
conditions=dict(method=["DELETE"]))
|
||||
mapper.connect("/tenants/{tenant_id}/users",
|
||||
controller=user_controller,
|
||||
action="get_tenant_users",
|
||||
conditions=dict(method=["GET"]))
|
||||
|
||||
# User Operations
|
||||
mapper.connect("/users",
|
||||
controller=user_controller,
|
||||
action="get_users",
|
||||
conditions=dict(method=["GET"]))
|
||||
mapper.connect("/users",
|
||||
controller=user_controller,
|
||||
action="create_user",
|
||||
conditions=dict(method=["POST"]))
|
||||
# NOTE(termie): not in diablo
|
||||
mapper.connect("/users/{user_id}",
|
||||
controller=user_controller,
|
||||
action="update_user",
|
||||
conditions=dict(method=["PUT"]))
|
||||
mapper.connect("/users/{user_id}",
|
||||
controller=user_controller,
|
||||
action="delete_user",
|
||||
conditions=dict(method=["DELETE"]))
|
||||
|
||||
# COMPAT(diablo): the copy with no OS-KSADM is from diablo
|
||||
mapper.connect("/users/{user_id}/password",
|
||||
controller=user_controller,
|
||||
action="set_user_password",
|
||||
conditions=dict(method=["PUT"]))
|
||||
mapper.connect("/users/{user_id}/OS-KSADM/password",
|
||||
controller=user_controller,
|
||||
action="set_user_password",
|
||||
conditions=dict(method=["PUT"]))
|
||||
|
||||
# COMPAT(diablo): the copy with no OS-KSADM is from diablo
|
||||
mapper.connect("/users/{user_id}/tenant",
|
||||
controller=user_controller,
|
||||
action="update_user_tenant",
|
||||
conditions=dict(method=["PUT"]))
|
||||
mapper.connect("/users/{user_id}/OS-KSADM/tenant",
|
||||
controller=user_controller,
|
||||
action="update_user_tenant",
|
||||
conditions=dict(method=["PUT"]))
|
||||
|
||||
# COMPAT(diablo): the copy with no OS-KSADM is from diablo
|
||||
mapper.connect("/users/{user_id}/enabled",
|
||||
controller=user_controller,
|
||||
action="set_user_enabled",
|
||||
conditions=dict(method=["PUT"]))
|
||||
mapper.connect("/users/{user_id}/OS-KSADM/enabled",
|
||||
controller=user_controller,
|
||||
action="set_user_enabled",
|
||||
conditions=dict(method=["PUT"]))
|
||||
|
||||
# User Roles
|
||||
mapper.connect("/users/{user_id}/roles/OS-KSADM/{role_id}",
|
||||
controller=role_controller, action="add_role_to_user",
|
||||
conditions=dict(method=["PUT"]))
|
||||
mapper.connect("/users/{user_id}/roles/OS-KSADM/{role_id}",
|
||||
controller=role_controller, action="delete_role_from_user",
|
||||
conditions=dict(method=["DELETE"]))
|
||||
|
||||
# COMPAT(diablo): User Roles
|
||||
mapper.connect("/users/{user_id}/roleRefs",
|
||||
controller=role_controller, action="get_role_refs",
|
||||
conditions=dict(method=["GET"]))
|
||||
mapper.connect("/users/{user_id}/roleRefs",
|
||||
controller=role_controller, action="create_role_ref",
|
||||
conditions=dict(method=["POST"]))
|
||||
mapper.connect("/users/{user_id}/roleRefs/{role_ref_id}",
|
||||
controller=role_controller, action="delete_role_ref",
|
||||
conditions=dict(method=["DELETE"]))
|
||||
|
||||
# User-Tenant Roles
|
||||
mapper.connect(
|
||||
"/tenants/{tenant_id}/users/{user_id}/roles/OS-KSADM/{role_id}",
|
||||
controller=role_controller, action="add_role_to_user",
|
||||
conditions=dict(method=["PUT"]))
|
||||
mapper.connect(
|
||||
"/tenants/{tenant_id}/users/{user_id}/roles/OS-KSADM/{role_id}",
|
||||
controller=role_controller, action="delete_role_from_user",
|
||||
conditions=dict(method=["DELETE"]))
|
||||
|
||||
# Service Operations
|
||||
mapper.connect("/OS-KSADM/services",
|
||||
controller=service_controller,
|
||||
action="get_services",
|
||||
conditions=dict(method=["GET"]))
|
||||
mapper.connect("/OS-KSADM/services",
|
||||
controller=service_controller,
|
||||
action="create_service",
|
||||
conditions=dict(method=["POST"]))
|
||||
mapper.connect("/OS-KSADM/services/{service_id}",
|
||||
controller=service_controller,
|
||||
action="delete_service",
|
||||
conditions=dict(method=["DELETE"]))
|
||||
mapper.connect("/OS-KSADM/services/{service_id}",
|
||||
controller=service_controller,
|
||||
action="get_service",
|
||||
conditions=dict(method=["GET"]))
|
||||
|
||||
# Role Operations
|
||||
mapper.connect("/OS-KSADM/roles",
|
||||
controller=role_controller,
|
||||
action="create_role",
|
||||
conditions=dict(method=["POST"]))
|
||||
mapper.connect("/OS-KSADM/roles",
|
||||
controller=role_controller,
|
||||
action="get_roles",
|
||||
conditions=dict(method=["GET"]))
|
||||
mapper.connect("/OS-KSADM/roles/{role_id}",
|
||||
controller=role_controller,
|
||||
action="get_role",
|
||||
conditions=dict(method=["GET"]))
|
||||
mapper.connect("/OS-KSADM/roles/{role_id}",
|
||||
controller=role_controller,
|
||||
action="delete_role",
|
||||
conditions=dict(method=["DELETE"]))
|
||||
|
||||
super(AdminCrudExtension, self).__init__(
|
||||
application, mapper)
|
||||
|
||||
|
||||
class Ec2Extension(wsgi.ExtensionRouter):
|
||||
def __init__(self, application):
|
||||
mapper = routes.Mapper()
|
||||
ec2_controller = Ec2Controller()
|
||||
|
||||
# validation
|
||||
mapper.connect('/ec2tokens',
|
||||
controller=ec2_controller,
|
||||
action='authenticate_ec2',
|
||||
conditions=dict(method=['POST']))
|
||||
|
||||
# crud
|
||||
mapper.connect('/users/{user_id}/credentials/OS-EC2',
|
||||
controller=ec2_controller,
|
||||
action='create_credential',
|
||||
conditions=dict(method=['POST']))
|
||||
mapper.connect('/users/{user_id}/credentials/OS-EC2',
|
||||
controller=ec2_controller,
|
||||
action='get_credentials',
|
||||
conditions=dict(method=['GET']))
|
||||
mapper.connect('/users/{user_id}/credentials/OS-EC2/{credential_id}',
|
||||
controller=ec2_controller,
|
||||
action='get_credential',
|
||||
conditions=dict(method=['GET']))
|
||||
mapper.connect('/users/{user_id}/credentials/OS-EC2/{credential_id}',
|
||||
controller=ec2_controller,
|
||||
action='delete_credential',
|
||||
conditions=dict(method=['DELETE']))
|
||||
|
||||
super(Ec2Extension, self).__init__(application, mapper)
|
||||
|
||||
|
||||
class Ec2Controller(Application):
|
||||
def __init__(self):
|
||||
self.catalog_api = catalog.Manager()
|
||||
self.identity_api = identity.Manager()
|
||||
self.token_api = token.Manager()
|
||||
self.policy_api = policy.Manager()
|
||||
self.ec2_api = ec2.Manager()
|
||||
super(Ec2Controller, self).__init__()
|
||||
|
||||
def authenticate_ec2(self, context, credentials=None,
|
||||
ec2Credentials=None):
|
||||
"""Validate a signed EC2 request and provide a token."""
|
||||
# NOTE(termie): backwards compat hack
|
||||
if not credentials and ec2Credentials:
|
||||
credentials = ec2Credentials
|
||||
creds_ref = self.ec2_api.get_credential(context,
|
||||
credentials['access'])
|
||||
|
||||
signer = utils.Signer(creds_ref['secret'])
|
||||
signature = signer.generate(credentials)
|
||||
if signature == credentials['signature']:
|
||||
pass
|
||||
# NOTE(vish): Some libraries don't use the port when signing
|
||||
# requests, so try again without port.
|
||||
elif ':' in credentials['signature']:
|
||||
hostname, _port = credentials['host'].split(":")
|
||||
credentials['host'] = hostname
|
||||
signature = signer.generate(credentials)
|
||||
if signature != credentials.signature:
|
||||
# TODO(termie): proper exception
|
||||
raise Exception("Not Authorized")
|
||||
else:
|
||||
raise Exception("Not Authorized")
|
||||
|
||||
# TODO(termie): don't create new tokens every time
|
||||
# TODO(termie): this is copied from TokenController.authenticate
|
||||
token_id = uuid.uuid4().hex
|
||||
tenant_ref = self.identity_api.get_tenant(creds_ref['tenant_id'])
|
||||
user_ref = self.identity_api.get_user(creds_ref['user_id'])
|
||||
metadata_ref = self.identity_api.get_metadata(
|
||||
context=context,
|
||||
user_id=user_ref['id'],
|
||||
tenant_id=tenant_ref['id'])
|
||||
catalog_ref = self.catalog_api.get_catalog(
|
||||
context=context,
|
||||
user_id=user_ref['id'],
|
||||
tenant_id=tenant_ref['id'],
|
||||
metadata=metadata_ref)
|
||||
|
||||
token_ref = self.token_api.create_token(
|
||||
context, token_id, dict(expires='',
|
||||
id=token_id,
|
||||
user=user_ref,
|
||||
tenant=tenant_ref,
|
||||
metadata=metadata_ref))
|
||||
|
||||
# TODO(termie): optimize this call at some point and put it into the
|
||||
# the return for metadata
|
||||
# fill out the roles in the metadata
|
||||
roles_ref = []
|
||||
for role_id in metadata_ref.get('roles', []):
|
||||
roles_ref.append(self.identity_api.get_role(context, role_id))
|
||||
|
||||
# TODO(termie): make this a util function or something
|
||||
# TODO(termie): i don't think the ec2 middleware currently expects a
|
||||
# full return, but it contains a note saying that it
|
||||
# would be better to expect a full return
|
||||
return TokenController._format_authenticate(
|
||||
self, token_ref, roles_ref, catalog_ref)
|
||||
|
||||
def create_credential(self, context, user_id, tenant_id):
|
||||
# TODO(termie): validate that this request is valid for given user
|
||||
# tenant
|
||||
cred_ref = {'user_id': user_id,
|
||||
'tenant_id': tenant_id,
|
||||
'access': uuid.uuid4().hex,
|
||||
'secret': uuid.uuid4().hex}
|
||||
self.ec2_api.create_credential(context, cred_ref['access'], cred_ref)
|
||||
return {'credential': cred_ref}
|
||||
|
||||
def get_credentials(self, context, user_id):
|
||||
"""List credentials for the given user_id."""
|
||||
# TODO(termie): validate that this request is valid for given user
|
||||
# tenant
|
||||
return {'credentials': self.ec2_api.list_credentials(context, user_id)}
|
||||
|
||||
def get_credential(self, context, user_id, credential_id):
|
||||
# TODO(termie): validate that this request is valid for given user
|
||||
# tenant
|
||||
return {'credential': self.ec2_api.get_credential(context,
|
||||
credential_id)}
|
||||
|
||||
def delete_credential(self, context, user_id, credential_id):
|
||||
# TODO(termie): validate that this request is valid for given user
|
||||
# tenant
|
||||
return self.ec2_api.delete_credential(context, credential_id)
|
||||
|
||||
|
||||
class NoopController(Application):
|
||||
class NoopController(wsgi.Application):
|
||||
def __init__(self):
|
||||
super(NoopController, self).__init__()
|
||||
|
||||
|
@ -464,7 +99,7 @@ class NoopController(Application):
|
|||
return {}
|
||||
|
||||
|
||||
class TokenController(Application):
|
||||
class TokenController(wsgi.Application):
|
||||
def __init__(self):
|
||||
self.catalog_api = catalog.Manager()
|
||||
self.identity_api = identity.Manager()
|
||||
|
@ -693,291 +328,7 @@ class TokenController(Application):
|
|||
return services.values()
|
||||
|
||||
|
||||
class TenantController(Application):
|
||||
def __init__(self):
|
||||
self.identity_api = identity.Manager()
|
||||
self.policy_api = policy.Manager()
|
||||
self.token_api = token.Manager()
|
||||
super(TenantController, self).__init__()
|
||||
|
||||
def get_tenants_for_token(self, context, **kw):
|
||||
"""Get valid tenants for token based on token used to authenticate.
|
||||
|
||||
Pulls the token from the context, validates it and gets the valid
|
||||
tenants for the user in the token.
|
||||
|
||||
Doesn't care about token scopedness.
|
||||
|
||||
"""
|
||||
token_ref = self.token_api.get_token(context=context,
|
||||
token_id=context['token_id'])
|
||||
assert token_ref is not None
|
||||
|
||||
user_ref = token_ref['user']
|
||||
tenant_ids = self.identity_api.get_tenants_for_user(
|
||||
context, user_ref['id'])
|
||||
tenant_refs = []
|
||||
for tenant_id in tenant_ids:
|
||||
tenant_refs.append(self.identity_api.get_tenant(
|
||||
context=context,
|
||||
tenant_id=tenant_id))
|
||||
return self._format_tenants_for_token(tenant_refs)
|
||||
|
||||
def get_tenant(self, context, tenant_id):
|
||||
# TODO(termie): this stuff should probably be moved to middleware
|
||||
if not context['is_admin']:
|
||||
user_token_ref = self.token_api.get_token(
|
||||
context=context, token_id=context['token_id'])
|
||||
creds = user_token_ref['metadata'].copy()
|
||||
creds['user_id'] = user_token_ref['user'].get('id')
|
||||
creds['tenant_id'] = user_token_ref['tenant'].get('id')
|
||||
# Accept either is_admin or the admin role
|
||||
assert self.policy_api.can_haz(context,
|
||||
('is_admin:1', 'roles:admin'),
|
||||
creds)
|
||||
|
||||
tenant = self.identity_api.get_tenant(context, tenant_id)
|
||||
if not tenant:
|
||||
return webob.exc.HTTPNotFound()
|
||||
return {'tenant': tenant}
|
||||
|
||||
# CRUD Extension
|
||||
def create_tenant(self, context, tenant):
|
||||
tenant_ref = self._normalize_dict(tenant)
|
||||
self.assert_admin(context)
|
||||
tenant_id = (tenant_ref.get('id')
|
||||
and tenant_ref.get('id')
|
||||
or uuid.uuid4().hex)
|
||||
tenant_ref['id'] = tenant_id
|
||||
|
||||
tenant = self.identity_api.create_tenant(
|
||||
context, tenant_id, tenant_ref)
|
||||
return {'tenant': tenant}
|
||||
|
||||
def update_tenant(self, context, tenant_id, tenant):
|
||||
self.assert_admin(context)
|
||||
tenant_ref = self.identity_api.update_tenant(
|
||||
context, tenant_id, tenant)
|
||||
return {'tenant': tenant_ref}
|
||||
|
||||
def delete_tenant(self, context, tenant_id, **kw):
|
||||
self.assert_admin(context)
|
||||
self.identity_api.delete_tenant(context, tenant_id)
|
||||
|
||||
def get_tenant_users(self, context, **kw):
|
||||
self.assert_admin(context)
|
||||
raise NotImplementedError()
|
||||
|
||||
def _format_tenants_for_token(self, tenant_refs):
|
||||
for x in tenant_refs:
|
||||
x['enabled'] = True
|
||||
o = {'tenants': tenant_refs,
|
||||
'tenants_links': []}
|
||||
return o
|
||||
|
||||
|
||||
class UserController(Application):
|
||||
def __init__(self):
|
||||
self.catalog_api = catalog.Manager()
|
||||
self.identity_api = identity.Manager()
|
||||
self.policy_api = policy.Manager()
|
||||
self.token_api = token.Manager()
|
||||
super(UserController, self).__init__()
|
||||
|
||||
def get_user(self, context, user_id):
|
||||
self.assert_admin(context)
|
||||
user_ref = self.identity_api.get_user(context, user_id)
|
||||
if not user_ref:
|
||||
raise webob.exc.HTTPNotFound()
|
||||
return {'user': user_ref}
|
||||
|
||||
def get_users(self, context):
|
||||
# NOTE(termie): i can't imagine that this really wants all the data
|
||||
# about every single user in the system...
|
||||
self.assert_admin(context)
|
||||
user_refs = self.identity_api.list_users(context)
|
||||
return {'users': user_refs}
|
||||
|
||||
# CRUD extension
|
||||
def create_user(self, context, user):
|
||||
user = self._normalize_dict(user)
|
||||
self.assert_admin(context)
|
||||
tenant_id = user.get('tenantId', None)
|
||||
user_id = uuid.uuid4().hex
|
||||
user_ref = user.copy()
|
||||
user_ref['id'] = user_id
|
||||
new_user_ref = self.identity_api.create_user(
|
||||
context, user_id, user_ref)
|
||||
if tenant_id:
|
||||
self.identity_api.add_user_to_tenant(tenant_id, user_id)
|
||||
return {'user': new_user_ref}
|
||||
|
||||
# NOTE(termie): this is really more of a patch than a put
|
||||
def update_user(self, context, user_id, user):
|
||||
self.assert_admin(context)
|
||||
user_ref = self.identity_api.get_user(context, user_id)
|
||||
del user['id']
|
||||
user_ref.update(user)
|
||||
self.identity_api.update_user(context, user_id, user_ref)
|
||||
return {'user': user_ref}
|
||||
|
||||
def delete_user(self, context, user_id):
|
||||
self.assert_admin(context)
|
||||
self.identity_api.delete_user(context, user_id)
|
||||
|
||||
def set_user_enabled(self, context, user_id, user):
|
||||
return self.update_user(context, user_id, user)
|
||||
|
||||
def set_user_password(self, context, user_id, user):
|
||||
return self.update_user(context, user_id, user)
|
||||
|
||||
def update_user_tenant(self, context, user_id, user):
|
||||
"""Update the default tenant."""
|
||||
# ensure that we're a member of that tenant
|
||||
tenant_id = user.get('tenantId')
|
||||
self.identity_api.add_user_to_tenant(context, tenant_id, user_id)
|
||||
return self.update_user(context, user_id, user)
|
||||
|
||||
|
||||
class RoleController(Application):
|
||||
def __init__(self):
|
||||
self.catalog_api = catalog.Manager()
|
||||
self.identity_api = identity.Manager()
|
||||
self.token_api = token.Manager()
|
||||
self.policy_api = policy.Manager()
|
||||
super(RoleController, self).__init__()
|
||||
|
||||
def get_user_roles(self, context, user_id, tenant_id=None):
|
||||
raise NotImplemented()
|
||||
|
||||
# CRUD extension
|
||||
def get_role(self, context, role_id):
|
||||
self.assert_admin(context)
|
||||
role_ref = self.identity_api.get_role(context, role_id)
|
||||
if not role_ref:
|
||||
raise webob.exc.HTTPNotFound()
|
||||
return {'role': role_ref}
|
||||
|
||||
def create_role(self, context, role):
|
||||
role = self._normalize_dict(role)
|
||||
self.assert_admin(context)
|
||||
role_id = uuid.uuid4().hex
|
||||
role['id'] = role_id
|
||||
role_ref = self.identity_api.create_role(context, role_id, role)
|
||||
return {'role': role_ref}
|
||||
|
||||
def delete_role(self, context, role_id):
|
||||
self.assert_admin(context)
|
||||
role_ref = self.identity_api.delete_role(context, role_id)
|
||||
|
||||
def get_roles(self, context):
|
||||
self.assert_admin(context)
|
||||
roles = self.identity_api.list_roles(context)
|
||||
# TODO(termie): probably inefficient at some point
|
||||
return {'roles': roles}
|
||||
|
||||
# COMPAT(diablo): CRUD extension
|
||||
def get_role_refs(self, context, user_id):
|
||||
"""Ultimate hack to get around having to make role_refs first-class.
|
||||
|
||||
This will basically iterate over the various roles the user has in
|
||||
all tenants the user is a member of and create fake role_refs where
|
||||
the id encodes the user-tenant-role information so we can look
|
||||
up the appropriate data when we need to delete them.
|
||||
|
||||
"""
|
||||
self.assert_admin(context)
|
||||
user_ref = self.identity_api.get_user(context, user_id)
|
||||
tenant_ids = self.identity_api.get_tenants_for_user(context, user_id)
|
||||
o = []
|
||||
for tenant_id in tenant_ids:
|
||||
role_ids = self.identity_api.get_roles_for_user_and_tenant(
|
||||
context, user_id, tenant_id)
|
||||
for role_id in role_ids:
|
||||
ref = {'roleId': role_id,
|
||||
'tenantId': tenant_id,
|
||||
'userId': user_id}
|
||||
ref['id'] = urllib.urlencode(ref)
|
||||
o.append(ref)
|
||||
return {'roles': o}
|
||||
|
||||
def create_role_ref(self, context, user_id, role):
|
||||
"""This is actually used for adding a user to a tenant.
|
||||
|
||||
In the legacy data model adding a user to a tenant required setting
|
||||
a role.
|
||||
|
||||
"""
|
||||
self.assert_admin(context)
|
||||
# TODO(termie): for now we're ignoring the actual role
|
||||
tenant_id = role.get('tenantId')
|
||||
role_id = role.get('roleId')
|
||||
self.identity_api.add_user_to_tenant(context, tenant_id, user_id)
|
||||
self.identity_api.add_role_to_user_and_tenant(
|
||||
context, user_id, tenant_id, role_id)
|
||||
role_ref = self.identity_api.get_role(context, role_id)
|
||||
return {'role': role_ref}
|
||||
|
||||
def delete_role_ref(self, context, user_id, role_ref_id):
|
||||
"""This is actually used for deleting a user from a tenant.
|
||||
|
||||
In the legacy data model removing a user from a tenant required
|
||||
deleting a role.
|
||||
|
||||
To emulate this, we encode the tenant and role in the role_ref_id,
|
||||
and if this happens to be the last role for the user-tenant pair,
|
||||
we remove the user from the tenant.
|
||||
|
||||
"""
|
||||
self.assert_admin(context)
|
||||
# TODO(termie): for now we're ignoring the actual role
|
||||
role_ref_ref = urlparse.parse_qs(role_ref_id)
|
||||
tenant_id = role_ref_ref.get('tenantId')[0]
|
||||
role_id = role_ref_ref.get('roleId')[0]
|
||||
self.identity_api.remove_role_from_user_and_tenant(
|
||||
context, user_id, tenant_id, role_id)
|
||||
roles = self.identity_api.get_roles_for_user_and_tenant(
|
||||
context, user_id, tenant_id)
|
||||
if not roles:
|
||||
self.identity_api.remove_user_from_tenant(
|
||||
context, tenant_id, user_id)
|
||||
|
||||
|
||||
class ServiceController(Application):
|
||||
def __init__(self):
|
||||
self.catalog_api = catalog.Manager()
|
||||
self.identity_api = identity.Manager()
|
||||
self.token_api = token.Manager()
|
||||
self.policy_api = policy.Manager()
|
||||
super(ServiceController, self).__init__()
|
||||
|
||||
# CRUD extensions
|
||||
# NOTE(termie): this OS-KSADM stuff is not very consistent
|
||||
def get_services(self, context):
|
||||
service_list = self.catalog_api.list_services(context)
|
||||
service_refs = [self.catalog_api.get_service(context, x)
|
||||
for x in service_list]
|
||||
return {'OS-KSADM:services': service_refs}
|
||||
|
||||
def get_service(self, context, service_id):
|
||||
service_ref = self.catalog_api.get_service(context, service_id)
|
||||
if not service_ref:
|
||||
raise webob.exc.HTTPNotFound()
|
||||
return {'OS-KSADM:service': service_ref}
|
||||
|
||||
def delete_service(self, context, service_id):
|
||||
service_ref = self.catalog_api.delete_service(context, service_id)
|
||||
|
||||
def create_service(self, context, OS_KSADM_service):
|
||||
service_id = uuid.uuid4().hex
|
||||
service_ref = OS_KSADM_service.copy()
|
||||
service_ref['id'] = service_id
|
||||
new_service_ref = self.catalog_api.create_service(
|
||||
context, service_id, service_ref)
|
||||
return {'OS-KSADM:service': new_service_ref}
|
||||
|
||||
|
||||
class VersionController(Application):
|
||||
class VersionController(wsgi.Application):
|
||||
def __init__(self):
|
||||
super(VersionController, self).__init__()
|
||||
|
||||
|
@ -985,7 +336,7 @@ class VersionController(Application):
|
|||
raise NotImplemented()
|
||||
|
||||
|
||||
class ExtensionsController(Application):
|
||||
class ExtensionsController(wsgi.Application):
|
||||
def __init__(self):
|
||||
super(ExtensionsController, self).__init__()
|
||||
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
from keystone.token.core import *
|
|
@ -0,0 +1,15 @@
|
|||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
from keystone.common import kvs
|
||||
|
||||
class KvsToken(kvs.Base):
|
||||
# Public interface
|
||||
def get_token(self, token_id):
|
||||
return self.db.get('token-%s' % token_id)
|
||||
|
||||
def create_token(self, token_id, data):
|
||||
self.db.set('token-%s' % token_id, data)
|
||||
return data
|
||||
|
||||
def delete_token(self, token_id):
|
||||
return self.db.delete('token-%s' % token_id)
|
Loading…
Reference in New Issue