summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authortermie <github@anarkystic.com>2011-11-14 09:57:41 -0800
committertermie <github@anarkystic.com>2011-11-14 09:57:41 -0800
commit096eb3fa568f3dd14df3703c2d15a499ff4e5712 (patch)
tree99fadb8f6d7c09b907b375edee8f8abb091f8369
parentadbbe0147e9e726db2dc6f2c2d4e446fa589c5ba (diff)
parentf2e73bc9b20b26947980067bcf95c9989e37907d (diff)
downloadkeystone-096eb3fa568f3dd14df3703c2d15a499ff4e5712.tar.gz
keystone-096eb3fa568f3dd14df3703c2d15a499ff4e5712.tar.xz
keystone-096eb3fa568f3dd14df3703c2d15a499ff4e5712.zip
Merge branch 'crud' of github.com:termie/keystonelight into crud
-rwxr-xr-xbin/ksl1
-rw-r--r--keystonelight/client.py100
-rw-r--r--keystonelight/identity.py28
-rw-r--r--keystonelight/middleware.py94
-rw-r--r--keystonelight/service.py313
-rw-r--r--tests/default.conf37
-rw-r--r--tests/keystone_compat_diablo.conf6
-rw-r--r--tests/keystoneclient_compat_master.conf6
-rw-r--r--tests/test_identity_api.py75
9 files changed, 462 insertions, 198 deletions
diff --git a/bin/ksl b/bin/ksl
index 2005ee44..e8f2587a 100755
--- a/bin/ksl
+++ b/bin/ksl
@@ -33,7 +33,6 @@ class LoadData(BaseApp):
pass
-
CMDS = {'loaddata': LoadData,
}
diff --git a/keystonelight/client.py b/keystonelight/client.py
new file mode 100644
index 00000000..25128ba3
--- /dev/null
+++ b/keystonelight/client.py
@@ -0,0 +1,100 @@
+
+"""Client library for KeystoneLight API."""
+
+import json
+
+import httplib2
+import webob
+
+from keystonelight import service
+from keystonelight import wsgi
+
+
+URLMAP = service.URLMAP
+
+
+class Client(object):
+ def __init__(self, token=None):
+ self.token = token
+
+ def request(self, method, path, headers, body):
+ raise NotImplemented
+
+ def get(self, path, headers=None):
+ return self.request('GET', path=path, headers=headers)
+
+ def post(self, path, headers=None, body=None):
+ return self.request('POST', path=path, headers=headers, body=body)
+
+ def put(self, path, headers=None, body=None):
+ return self.request('PUT', path=path, headers=headers, body=body)
+
+ def _build_headers(self, headers=None):
+ if headers is None:
+ headers = {}
+
+ if self.token:
+ headers.setdefault('X-Auth-Token', self.token)
+
+ return headers
+
+ def __getattr__(self, key):
+ """Lazy way to define a bunch of dynamic urls based on URLMAP.
+
+ Turns something like
+
+ c.authenticate(user_id='foo', password='bar')
+
+ into
+
+ c.request('POST', '/token', body={'user_id': 'foo', 'password': 'bar'})
+
+ """
+ if key not in URLMAP:
+ raise AttributeError(key)
+
+ method, path = URLMAP[key]
+
+ def _internal(method_=method, path_=path, **kw):
+ path_ = path_ % kw
+ params = {'method': method_,
+ 'path': path_}
+ if method.lower() in ('put', 'post'):
+ params['body'] = kw
+ return self.request(**params)
+
+ setattr(self, key, _internal)
+
+ return getattr(self, key)
+
+
+class HttpClient(Client):
+ def __init__(self, endpoint=None, token=None):
+ self.endpoint = endpoint
+ super(HttpClient, self).__init__(token=token)
+
+ def request(self, method, path, headers=None, body=None):
+ if type(body) is type({}):
+ body = json.dumps(body)
+ headers = self._build_headers(headers)
+ h = httplib.Http()
+ resp, content = h.request(path, method=method, headers=headers, body=body)
+ return webob.Response(content, status=resp.status, headerlist=resp.headers)
+
+
+class TestClient(Client):
+ def __init__(self, app=None, token=None):
+ self.app = app
+ super(TestClient, self).__init__(token=token)
+
+ def request(self, method, path, headers=None, body=None):
+ if type(body) is type({}):
+ body = json.dumps(body)
+ headers = self._build_headers(headers)
+ req = wsgi.Request.blank(path)
+ req.method = method
+ for k, v in headers.iteritems():
+ req.headers[k] = v
+ if body:
+ req.body = body
+ return req.get_response(self.app)
diff --git a/keystonelight/identity.py b/keystonelight/identity.py
index a144e938..1b2a2f30 100644
--- a/keystonelight/identity.py
+++ b/keystonelight/identity.py
@@ -32,3 +32,31 @@ class Manager(object):
def get_extras(self, context, user_id, tenant_id):
return self.driver.get_extras(user_id, tenant_id)
+
+ # CRUD operations
+ def create_user(self, context, user_id, data):
+ return self.driver.create_user(user_id, data)
+
+ def update_user(self, context, user_id, data):
+ return self.driver.update_user(user_id, data)
+
+ def delete_user(self, context, user_id):
+ return self.driver.delete_user(user_id)
+
+ def create_tenant(self, context, tenant_id, data):
+ return self.driver.create_tenant(tenant_id, data)
+
+ def update_tenant(self, context, tenant_id, data):
+ return self.driver.update_tenant(tenant_id, data)
+
+ def delete_tenant(self, context, tenant_id):
+ return self.driver.delete_tenant(tenant_id)
+
+ def create_extras(self, context, user_id, tenant_id, data):
+ return self.driver.create_extras(user_id, tenant_id, data)
+
+ def update_extras(self, context, user_id, tenant_id, data):
+ return self.driver.update_extras(user_id, tenant_id, data)
+
+ def delete_extras(self, context, user_id, tenant_id):
+ return self.driver.delete_extras(user_id, tenant_id)
diff --git a/keystonelight/middleware.py b/keystonelight/middleware.py
new file mode 100644
index 00000000..29e655bd
--- /dev/null
+++ b/keystonelight/middleware.py
@@ -0,0 +1,94 @@
+import json
+
+from keystonelight import wsgi
+
+
+# Header used to transmit the auth token
+AUTH_TOKEN_HEADER = 'X-Auth-Token'
+
+
+# Environment variable used to pass the request context
+CONTEXT_ENV = 'openstack.context'
+
+
+# Environment variable used to pass the request params
+PARAMS_ENV = 'openstack.params'
+
+
+class TokenAuthMiddleware(wsgi.Middleware):
+ def process_request(self, request):
+ token = request.headers.get(AUTH_TOKEN_HEADER)
+ context = request.environ.get(CONTEXT_ENV, {})
+ context['token_id'] = token
+ request.environ[CONTEXT_ENV] = context
+
+
+class AdminTokenAuthMiddleware(wsgi.Middleware):
+ """A trivial filter that checks for a pre-defined admin token.
+
+ Sets 'is_admin' to true in the context, expected to be checked by
+ methods that are admin-only.
+
+ """
+
+ def process_request(self, request):
+ token = request.headers.get(AUTH_TOKEN_HEADER)
+ context = request.environ.get(CONTEXT_ENV, {})
+ context['is_admin'] = (token == self.options['admin_token'])
+ request.environ[CONTEXT_ENV] = context
+
+
+class PostParamsMiddleware(wsgi.Middleware):
+ """Middleware to allow method arguments to be passed as POST parameters.
+
+ Filters out the parameters `self`, `context` and anything beginning with
+ an underscore.
+
+ """
+
+ def process_request(self, request):
+ params_parsed = request.params
+ params = {}
+ for k, v in params_parsed.iteritems():
+ if k in ('self', 'context'):
+ continue
+ if k.startswith('_'):
+ continue
+ params[k] = v
+
+ request.environ[PARAMS_ENV] = params
+
+
+class JsonBodyMiddleware(wsgi.Middleware):
+ """Middleware to allow method arguments to be passed as serialized JSON.
+
+ Accepting arguments as JSON is useful for accepting data that may be more
+ complex than simple primitives.
+
+ In this case we accept it as urlencoded data under the key 'json' as in
+ json=<urlencoded_json> but this could be extended to accept raw JSON
+ in the POST body.
+
+ Filters out the parameters `self`, `context` and anything beginning with
+ an underscore.
+
+ """
+
+ def process_request(self, request):
+ #if 'json' not in request.params:
+ # return
+
+ params_json = request.body
+ if not params_json:
+ return
+
+ params_parsed = json.loads(params_json)
+ params = {}
+ for k, v in params_parsed.iteritems():
+ if k in ('self', 'context'):
+ continue
+ if k.startswith('_'):
+ continue
+ params[k] = v
+
+ request.environ[PARAMS_ENV] = params
diff --git a/keystonelight/service.py b/keystonelight/service.py
index 797a3415..682f5151 100644
--- a/keystonelight/service.py
+++ b/keystonelight/service.py
@@ -1,7 +1,3 @@
-# vim: tabstop=4 shiftwidth=4 softtabstop=4
-
-# this is the web service frontend
-
import json
import logging
@@ -15,206 +11,151 @@ from keystonelight import utils
from keystonelight import wsgi
-class BaseApplication(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 = dict([(str(k), v) for (k, v) in params.iteritems()])
- result = method(context, **params)
-
- if result is None or type(result) is str or type(result) is unicode:
- return result
-
- return json.dumps(result)
-
-
-class TokenAuthMiddleware(wsgi.Middleware):
- def process_request(self, request):
- token = request.headers.get('X-Auth-Token')
- context = request.environ.get('openstack.context', {})
- context['token_id'] = token
- request.environ['openstack.context'] = context
-
-
-class AdminTokenAuthMiddleware(wsgi.Middleware):
- """A trivial filter that checks for a pre-defined admin token.
-
- Sets 'is_admin' to true in the context, expected to be checked by
- methods that are admin-only.
-
- """
- def process_request(self, request):
- token = request.headers.get('X-Auth-Token')
- context = request.environ.get('openstack.context', {})
- context['is_admin'] = (token == self.options['admin_token'])
- request.environ['openstack.context'] = context
-
+HIGH_LEVEL_CALLS = {
+ 'authenticate': ('POST', '/tokens'),
+ 'get_tenants': ('GET', '/user/%(user_id)s/tenants'),
+ 'get_user': ('GET', '/user/%(user_id)s'),
+ 'get_tenant': ('GET', '/tenant/%(tenant_id)s'),
+ 'get_tenant_by_name': ('GET', '/tenant_name/%(tenant_name)s'),
+ 'get_extras': ('GET', '/extras/%(tenant_id)s-%(user_id)s'),
+ 'get_token': ('GET', '/token/%(token_id)s'),
+ }
+
+# NOTE(termie): creates are seperate from updates to allow duplication checks
+LOW_LEVEL_CALLS = {
+ # tokens
+ 'create_token': ('POST', '/token'),
+ 'delete_token': ('DELETE', '/token/%(token_id)s'),
+ # users
+ 'create_user': ('POST', '/user'),
+ 'update_user': ('PUT', '/user/%(user_id)s'),
+ 'delete_user': ('DELETE', '/user/%(user_id)s'),
+ # tenants
+ 'create_tenant': ('POST', '/tenant'),
+ 'update_tenant': ('PUT', '/tenant/%(tenant_id)s'),
+ 'delete_tenant': ('DELETE', '/tenant/%(tenant_id)s'),
+ # extras
+ # NOTE(termie): these separators are probably going to bite us eventually
+ 'create_extras': ('POST', '/extras'),
+ 'update_extras': ('PUT', '/extras/%(tenant_id)s-%(user_id)s'),
+ 'delete_extras': ('DELETE', '/extras/%(tenant_id)s-%(user_id)s'),
+ }
+
+
+URLMAP = HIGH_LEVEL_CALLS.copy()
+URLMAP.update(LOW_LEVEL_CALLS)
-class PostParamsMiddleware(wsgi.Middleware):
- """Middleware to allow method arguments to be passed as POST parameters.
- Filters out the parameters `self`, `context` and anything beginning with
- an underscore.
-
- """
-
- def process_request(self, request):
- params_parsed = request.params
- params = {}
- for k, v in params_parsed.iteritems():
- if k in ('self', 'context'):
- continue
- if k.startswith('_'):
- continue
- params[k] = v
-
- request.environ['openstack.params'] = params
-
-
-class JsonBodyMiddleware(wsgi.Middleware):
- """Middleware to allow method arguments to be passed as serialized JSON.
-
- Accepting arguments as JSON is useful for accepting data that may be more
- complex than simple primitives.
-
- In this case we accept it as urlencoded data under the key 'json' as in
- json=<urlencoded_json> but this could be extended to accept raw JSON
- in the POST body.
-
- Filters out the parameters `self`, `context` and anything beginning with
- an underscore.
+class BaseApplication(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)
- def process_request(self, request):
- #if 'json' not in request.params:
- # return
+ # TODO(termie): do some basic normalization on methods
+ method = getattr(self, action)
- params_json = request.body
- if not params_json:
- return
+ # NOTE(vish): make sure we have no unicode keys for py2.6.
+ params = dict([(str(k), v) for (k, v) in params.iteritems()])
+ result = method(context, **params)
- params_parsed = json.loads(params_json)
- params = {}
- for k, v in params_parsed.iteritems():
- if k in ('self', 'context'):
- continue
- if k.startswith('_'):
- continue
- params[k] = v
+ if result is None or type(result) is str or type(result) is unicode:
+ return result
- request.environ['openstack.params'] = params
+ return json.dumps(result)
class TokenController(BaseApplication):
- """Validate and pass through calls to TokenManager."""
+ """Validate and pass through calls to TokenManager."""
- def __init__(self, options):
- self.token_api = token.Manager(options=options)
- self.options = options
+ def __init__(self, options):
+ self.token_api = token.Manager(options=options)
+ self.options = options
- def validate_token(self, context, token_id):
- token_info = self.token_api.validate_token(context, token_id)
- if not token_info:
- raise webob.exc.HTTPUnauthorized()
- return token_info
+ def validate_token(self, context, token_id):
+ token_info = self.token_api.validate_token(context, token_id)
+ if not token_info:
+ raise webob.exc.HTTPUnauthorized()
+ return token_info
class IdentityController(BaseApplication):
- """Validate and pass calls through to IdentityManager.
-
- IdentityManager will also pretty much just pass calls through to
- a specific driver.
- """
-
- def __init__(self, options):
- self.identity_api = identity.Manager(options=options)
- self.token_api = token.Manager(options=options)
- self.options = options
-
- def authenticate(self, context, **kwargs):
- tenant, user, extras = self.identity_api.authenticate(context,
- **kwargs)
- token = self.token_api.create_token(context,
- dict(tenant=tenant,
- user=user,
- extras=extras))
- logging.debug('TOKEN: %s', token)
- return token
-
- def get_tenants(self, context):
- token_id = context.get('token')
- token = self.token_api.validate_token(context, token_id)
-
- return self.identity_api.get_tenants(context,
- user_id=token['user'])
+ """Validate and pass calls through to IdentityManager.
+
+ IdentityManager will also pretty much just pass calls through to
+ a specific driver.
+ """
+
+ def __init__(self, options):
+ self.identity_api = identity.Manager(options=options)
+ self.token_api = token.Manager(options=options)
+ self.options = options
+
+ def authenticate(self, context, **kwargs):
+ user_ref, tenant_ref, extras_ref = self.identity_api.authenticate(
+ context, **kwargs)
+ # TODO(termie): strip password from return values
+ token_ref = self.token_api.create_token(context,
+ dict(tenant=tenant_ref,
+ user=user_ref,
+ extras=extras_ref))
+ logging.debug('TOKEN: %s', token_ref)
+ return token_ref
+
+ def get_tenants(self, context, user_id=None):
+ token_id = context.get('token_id')
+ token_ref = self.token_api.get_token(context, token_id)
+ assert token_ref
+ assert token_ref['user']['id'] == user_id
+ tenants_ref = []
+ for tenant_id in token_ref['user']['tenants']:
+ tenants_ref.append(self.identity_api.get_tenant(context,
+ tenant_id))
+
+ return tenants_ref
class Router(wsgi.Router):
- def __init__(self, options):
- self.options = options
- token_controller = utils.import_object(
- options['token_controller'],
- options=options)
- identity_controller = utils.import_object(
- options['identity_controller'],
- options=options)
- mapper = routes.Mapper()
- mapper.connect('/v2.0/tokens', controller=identity_controller,
- action='authenticate')
- mapper.connect('/v2.0/tokens/{token_id}', controller=token_controller,
- action='revoke_token',
- conditions=dict(method=['DELETE']))
- mapper.connect("/v2.0/tenants", controller=identity_controller,
- action="get_tenants", conditions=dict(method=["GET"]))
- super(Router, self).__init__(mapper)
-
-
-class AdminRouter(wsgi.Router):
- def __init__(self, options):
- self.options = options
- token_controller = utils.import_object(
- options['token_controller'],
- options=options)
- identity_controller = utils.import_object(
- options['identity_controller'],
- options=options)
- mapper = routes.Mapper()
-
- mapper.connect('/v2.0/tokens', controller=identity_controller,
- action='authenticate')
- mapper.connect('/v2.0/tokens/{token_id}', controller=token_controller,
- action='validate_token',
- conditions=dict(method=['GET']))
- mapper.connect('/v2.0/tokens/{token_id}', controller=token_controller,
- action='revoke_token',
- conditions=dict(method=['DELETE']))
- super(AdminRouter, self).__init__(mapper)
-
-
-def identity_app_factory(global_conf, **local_conf):
- conf = global_conf.copy()
- conf.update(local_conf)
- return Router(conf)
+ def __init__(self, options):
+ self.options = options
+ self.identity_controller = IdentityController(options)
+ self.token_controller = TokenController(options)
+
+ mapper = self._build_map(URLMAP)
+ super(Router, self).__init__(mapper)
+
+ def _build_map(self, urlmap):
+ """Build a routes.Mapper based on URLMAP."""
+ mapper = routes.Mapper()
+ for k, v in urlmap.iteritems():
+ # NOTE(termie): hack
+ if 'token' in k:
+ controller = self.token_controller
+ else:
+ controller = self.identity_controller
+ action = k
+ method, path = v
+ path = path.replace('%(', '{').replace(')s', '}')
+
+ mapper.connect(path,
+ controller=controller,
+ action=action,
+ conditions=dict(method=[method]))
+
+ return mapper
def app_factory(global_conf, **local_conf):
- conf = global_conf.copy()
- conf.update(local_conf)
- return Router(conf)
+ conf = global_conf.copy()
+ conf.update(local_conf)
+ return Router(conf)
diff --git a/tests/default.conf b/tests/default.conf
index 68388b27..220a350f 100644
--- a/tests/default.conf
+++ b/tests/default.conf
@@ -1,23 +1,50 @@
[DEFAULT]
-catalog_driver = keystonelight.backends.kvs.KvsCatalog
+catalog_driver = keystonelight.backends.templated.TemplatedCatalog
identity_driver = keystonelight.backends.kvs.KvsIdentity
token_driver = keystonelight.backends.kvs.KvsToken
+public_port = 5000
admin_token = ADMIN
+# config for TemplatedCatalog, using camelCase because I don't want to do
+# translations for keystone compat
+catalog.RegionOne.identity.publicURL = http://localhost:$(public_port)s/v2.0
+catalog.RegionOne.identity.adminURL = http://localhost:$(public_port)s/v2.0
+catalog.RegionOne.identity.internalURL = http://localhost:$(public_port)s/v2.0
+catalog.RegionOne.identity.name = 'Identity Service'
+
+# fake compute service for now to help novaclient tests work
+compute_port = 3000
+catalog.RegionOne.compute.publicURL = http://localhost:$(compute_port)s/v1.1/$(tenant_id)s
+catalog.RegionOne.compute.adminURL = http://localhost:$(compute_port)s/v1.1/$(tenant_id)s
+catalog.RegionOne.compute.internalURL = http://localhost:$(compute_port)s/v1.1/$(tenant_id)s
+catalog.RegionOne.compute.name = 'Compute Service'
+
+
[filter:debug]
paste.filter_factory = keystonelight.wsgi:Debug.factory
[filter:token_auth]
-paste.filter_factory = keystonelight.service:TokenAuthMiddleware.factory
+paste.filter_factory = keystonelight.middleware:TokenAuthMiddleware.factory
[filter:admin_token_auth]
-paste.filter_factory = keystonelight.service:AdminTokenAuthMiddleware.factory
+paste.filter_factory = keystonelight.middleware:AdminTokenAuthMiddleware.factory
[filter:json_body]
-paste.filter_factory = keystonelight.service:JsonBodyMiddleware.factory
+paste.filter_factory = keystonelight.middleware:JsonBodyMiddleware.factory
[app:keystonelight]
paste.app_factory = keystonelight.service:app_factory
-[pipeline:main]
+[app:keystone]
+paste.app_factory = keystonelight.keystone_compat:app_factory
+
+[pipeline:keystone_api]
+pipeline = token_auth admin_token_auth json_body debug keystone
+
+[pipeline:keystonelight_api]
pipeline = token_auth admin_token_auth json_body debug keystonelight
+
+[composite:main]
+use = egg:Paste#urlmap
+/ = keystonelight_api
+/v2.0 = keystone_api
diff --git a/tests/keystone_compat_diablo.conf b/tests/keystone_compat_diablo.conf
index d9052631..94024df3 100644
--- a/tests/keystone_compat_diablo.conf
+++ b/tests/keystone_compat_diablo.conf
@@ -9,13 +9,13 @@ admin_token = ADMIN
paste.filter_factory = keystonelight.wsgi:Debug.factory
[filter:token_auth]
-paste.filter_factory = keystonelight.service:TokenAuthMiddleware.factory
+paste.filter_factory = keystonelight.middleware:TokenAuthMiddleware.factory
[filter:admin_token_auth]
-paste.filter_factory = keystonelight.service:AdminTokenAuthMiddleware.factory
+paste.filter_factory = keystonelight.middleware:AdminTokenAuthMiddleware.factory
[filter:json_body]
-paste.filter_factory = keystonelight.service:JsonBodyMiddleware.factory
+paste.filter_factory = keystonelight.middleware:JsonBodyMiddleware.factory
[app:keystone]
paste.app_factory = keystonelight.keystone_compat:app_factory
diff --git a/tests/keystoneclient_compat_master.conf b/tests/keystoneclient_compat_master.conf
index e006e821..e9861b6b 100644
--- a/tests/keystoneclient_compat_master.conf
+++ b/tests/keystoneclient_compat_master.conf
@@ -24,13 +24,13 @@ catalog.RegionOne.compute.name = 'Compute Service'
paste.filter_factory = keystonelight.wsgi:Debug.factory
[filter:token_auth]
-paste.filter_factory = keystonelight.service:TokenAuthMiddleware.factory
+paste.filter_factory = keystonelight.middleware:TokenAuthMiddleware.factory
[filter:admin_token_auth]
-paste.filter_factory = keystonelight.service:AdminTokenAuthMiddleware.factory
+paste.filter_factory = keystonelight.middleware:AdminTokenAuthMiddleware.factory
[filter:json_body]
-paste.filter_factory = keystonelight.service:JsonBodyMiddleware.factory
+paste.filter_factory = keystonelight.middleware:JsonBodyMiddleware.factory
[app:keystone]
paste.app_factory = keystonelight.keystone_compat:app_factory
diff --git a/tests/test_identity_api.py b/tests/test_identity_api.py
new file mode 100644
index 00000000..885d4314
--- /dev/null
+++ b/tests/test_identity_api.py
@@ -0,0 +1,75 @@
+import json
+import uuid
+
+from keystonelight import client
+from keystonelight import models
+from keystonelight import test
+from keystonelight import utils
+from keystonelight.backends import kvs
+
+
+class IdentityApi(test.TestCase):
+ def setUp(self):
+ super(IdentityApi, self).setUp()
+ self.options = self.appconfig('default')
+ app = self.loadapp('default')
+ self.app = app
+
+ self.identity_backend = utils.import_object(
+ self.options['identity_driver'], options=self.options)
+ self.token_backend = utils.import_object(
+ self.options['token_driver'], options=self.options)
+ self.catalog_backend = utils.import_object(
+ self.options['catalog_driver'], options=self.options)
+ self._load_fixtures()
+
+ def _load_fixtures(self):
+ self.tenant_bar = self.identity_backend._create_tenant(
+ 'bar',
+ models.Tenant(id='bar', name='BAR'))
+ self.user_foo = self.identity_backend._create_user(
+ 'foo',
+ models.User(id='foo',
+ name='FOO',
+ password='foo2',
+ tenants=[self.tenant_bar['id']]))
+ self.extras_foobar = self.identity_backend._create_extras(
+ 'foo', 'bar',
+ {'extra': 'extra'})
+
+ def _login(self):
+ c = client.TestClient(self.app)
+ post_data = {'user_id': self.user_foo['id'],
+ 'tenant_id': self.tenant_bar['id'],
+ 'password': self.user_foo['password']}
+ resp = c.post('/tokens', body=post_data)
+ token = json.loads(resp.body)
+ return token
+
+ def test_authenticate(self):
+ c = client.TestClient(self.app)
+ post_data = {'user_id': self.user_foo['id'],
+ 'tenant_id': self.tenant_bar['id'],
+ 'password': self.user_foo['password']}
+ resp = c.authenticate(**post_data)
+ data = json.loads(resp.body)
+ self.assertEquals(self.user_foo['id'], data['user']['id'])
+ self.assertEquals(self.tenant_bar['id'], data['tenant']['id'])
+ self.assertDictEquals(self.extras_foobar, data['extras'])
+
+ def test_authenticate_no_tenant(self):
+ c = client.TestClient(self.app)
+ post_data = {'user_id': self.user_foo['id'],
+ 'password': self.user_foo['password']}
+ resp = c.authenticate(**post_data)
+ data = json.loads(resp.body)
+ self.assertEquals(self.user_foo['id'], data['user']['id'])
+ self.assertEquals(None, data['tenant'])
+ self.assertEquals(None, data['extras'])
+
+ def test_get_tenants(self):
+ token = self._login()
+ c = client.TestClient(self.app, token['id'])
+ resp = c.get_tenants(user_id=self.user_foo['id'])
+ data = json.loads(resp.body)
+ self.assertDictEquals(self.tenant_bar, data[0])