diff --git a/README.rst b/README.rst index d78646819a..344be5a927 100644 --- a/README.rst +++ b/README.rst @@ -91,8 +91,9 @@ Diagram: keystone_compat_flows.sdx_ Still To Do ----------- -* Dev and testing setups would do well with some user/tenant/etc CRUD, for the - KVS backends at least. -* Fixture loading functionality would also be killer tests and dev. -* LDAP backend. -* Keystone import. + * Dev and testing setups would do well with some user/tenant/etc CRUD, for the + KVS backends at least. + * Fixture loading functionality would also be killer tests and dev. + * LDAP backend. + * Keystone import. + * Admin-only interface diff --git a/keystonelight/backends/kvs.py b/keystonelight/backends/kvs.py index a9221124b6..caa6e4c3dc 100644 --- a/keystonelight/backends/kvs.py +++ b/keystonelight/backends/kvs.py @@ -6,25 +6,36 @@ class DictKvs(dict): def delete(self, key): del self[key] + INMEMDB = DictKvs() + class KvsIdentity(object): def __init__(self, options, 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 extras_ref = None - if user_ref['password'] != password: + 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') - if tenant_id and tenant_id in user_ref['tenants']: - tenant_ref = self.get_tenant(tenant_id) - extras_ref = self.get_extras(user_id, tenant_id) + tenant_ref = self.get_tenant(tenant_id) + extras_ref = self.get_extras(user_id, tenant_id) return (user_ref, tenant_ref, extras_ref) def get_tenant(self, tenant_id): @@ -61,6 +72,8 @@ class KvsToken(object): def __init__(self, options, db=None): if db is None: db = INMEMDB + elif type(db) is type({}): + db = DictKvs(db) self.db = db # Public interface @@ -79,12 +92,15 @@ class KvsCatalog(object): def __init__(self, options, 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, extras=None): - return self.db.get('catalog-%s' % tenant_id) + return self.db.get('catalog-%s-%s' % (tenant_id, user_id)) # Private interface def _create_catalog(self, user_id, tenant_id, data): - self.db.set('catalog-%s' % tenant_id, data) + self.db.set('catalog-%s-%s' % (tenant_id, user_id), data) + return data diff --git a/keystonelight/keystone_compat.py b/keystonelight/keystone_compat.py index 86746d74eb..cf99244c04 100644 --- a/keystonelight/keystone_compat.py +++ b/keystonelight/keystone_compat.py @@ -17,7 +17,6 @@ class KeystoneRouter(wsgi.Router): self.options = options self.keystone_controller = KeystoneController(options) - mapper = routes.Mapper() mapper.connect('/v2.0/tokens', controller=self.keystone_controller, diff --git a/keystonelight/service.py b/keystonelight/service.py index c607f8aa7c..1db788698a 100644 --- a/keystonelight/service.py +++ b/keystonelight/service.py @@ -198,3 +198,8 @@ def identity_app_factory(global_conf, **local_conf): conf.update(local_conf) return Router(conf) + +def app_factory(global_conf, **local_conf): + conf = global_conf.copy() + conf.update(local_conf) + return Router(conf) diff --git a/keystonelight/test.py b/keystonelight/test.py index a710203f11..9a24883c67 100644 --- a/keystonelight/test.py +++ b/keystonelight/test.py @@ -1,6 +1,8 @@ import ConfigParser +import logging import os import unittest +import subprocess import sys from paste import deploy @@ -24,13 +26,16 @@ def checkout_vendor(repo, rev): name = name[:-4] revdir = os.path.join(VENDOR, '%s-%s' % (name, rev.replace('/', '_'))) + try: + if not os.path.exists(revdir): + utils.git('clone', repo, revdir) - if not os.path.exists(revdir): - utils.git('clone', repo, revdir) - - cd(revdir) - utils.git('pull') - utils.git('checkout', '-q', rev) + cd(revdir) + utils.git('pull') + utils.git('checkout', '-q', rev) + except subprocess.CalledProcessError as e: + logging.warning('Failed to checkout %s', repo) + pass return revdir diff --git a/tests/default.conf b/tests/default.conf new file mode 100644 index 0000000000..fa6ac28b52 --- /dev/null +++ b/tests/default.conf @@ -0,0 +1,19 @@ +[DEFAULT] +catalog_driver = keystonelight.backends.kvs.KvsCatalog +identity_driver = keystonelight.backends.kvs.KvsIdentity +token_driver = keystonelight.backends.kvs.KvsToken + +[filter:debug] +paste.filter_factory = keystonelight.wsgi:Debug.factory + +[filter:token_auth] +paste.filter_factory = keystonelight.service:TokenAuthMiddleware.factory + +[filter:json_body] +paste.filter_factory = keystonelight.service:JsonBodyMiddleware.factory + +[app:keystonelight] +paste.app_factory = keystonelight.service:app_factory + +[pipeline:main] +pipeline = token_auth json_body debug keystonelight diff --git a/tests/test_backend_kvs.py b/tests/test_backend_kvs.py new file mode 100644 index 0000000000..3561a13c5b --- /dev/null +++ b/tests/test_backend_kvs.py @@ -0,0 +1,157 @@ +import uuid + +from keystonelight import models +from keystonelight import test +from keystonelight.backends import kvs + + +class KvsIdentity(test.TestCase): + def setUp(self): + super(KvsIdentity, self).setUp() + options = self.appconfig('default') + self.identity_api = kvs.KvsIdentity(options=options, db={}) + self._load_fixtures() + + def _load_fixtures(self): + self.tenant_bar = self.identity_api._create_tenant( + 'bar', + models.Tenant(id='bar', name='BAR')) + self.user_foo = self.identity_api._create_user( + 'foo', + models.User(id='foo', + name='FOO', + password='foo2', + tenants=[self.tenant_bar['id']])) + self.extras_foobar = self.identity_api._create_extras( + 'foo', 'bar', + {'extra': 'extra'}) + + def test_authenticate_bad_user(self): + self.assertRaises(AssertionError, + self.identity_api.authenticate, + user_id=self.user_foo['id'] + 'WRONG', + tenant_id=self.tenant_bar['id'], + password=self.user_foo['password']) + + def test_authenticate_bad_password(self): + self.assertRaises(AssertionError, + self.identity_api.authenticate, + user_id=self.user_foo['id'], + tenant_id=self.tenant_bar['id'], + password=self.user_foo['password'] + 'WRONG') + + def test_authenticate_invalid_tenant(self): + self.assertRaises(AssertionError, + self.identity_api.authenticate, + user_id=self.user_foo['id'], + tenant_id=self.tenant_bar['id'] + 'WRONG', + password=self.user_foo['password']) + + def test_authenticate_no_tenant(self): + user_ref, tenant_ref, extras_ref = self.identity_api.authenticate( + user_id=self.user_foo['id'], + password=self.user_foo['password']) + self.assertDictEquals(user_ref, self.user_foo) + self.assert_(tenant_ref is None) + self.assert_(extras_ref is None) + + def test_authenticate(self): + user_ref, tenant_ref, extras_ref = self.identity_api.authenticate( + user_id=self.user_foo['id'], + tenant_id=self.tenant_bar['id'], + password=self.user_foo['password']) + self.assertDictEquals(user_ref, self.user_foo) + self.assertDictEquals(tenant_ref, self.tenant_bar) + self.assertDictEquals(extras_ref, self.extras_foobar) + + def test_get_tenant_bad_tenant(self): + tenant_ref = self.identity_api.get_tenant( + tenant_id=self.tenant_bar['id'] + 'WRONG') + self.assert_(tenant_ref is None) + + def test_get_tenant(self): + tenant_ref = self.identity_api.get_tenant(tenant_id=self.tenant_bar['id']) + self.assertDictEquals(tenant_ref, self.tenant_bar) + + def test_get_tenant_by_name_bad_tenant(self): + tenant_ref = self.identity_api.get_tenant( + tenant_id=self.tenant_bar['name'] + 'WRONG') + self.assert_(tenant_ref is None) + + def test_get_tenant_by_name(self): + tenant_ref = self.identity_api.get_tenant_by_name( + tenant_name=self.tenant_bar['name']) + self.assertDictEquals(tenant_ref, self.tenant_bar) + + def test_get_user_bad_user(self): + user_ref = self.identity_api.get_user( + user_id=self.user_foo['id'] + 'WRONG') + self.assert_(user_ref is None) + + def test_get_user(self): + user_ref = self.identity_api.get_user(user_id=self.user_foo['id']) + self.assertDictEquals(user_ref, self.user_foo) + + def test_get_extras_bad_user(self): + extras_ref = self.identity_api.get_extras( + user_id=self.user_foo['id'] + 'WRONG', + tenant_id=self.tenant_bar['id']) + self.assert_(extras_ref is None) + + def test_get_extras_bad_tenant(self): + extras_ref = self.identity_api.get_extras( + user_id=self.user_foo['id'], + tenant_id=self.tenant_bar['id'] + 'WRONG') + self.assert_(extras_ref is None) + + def test_get_extras(self): + extras_ref = self.identity_api.get_extras( + user_id=self.user_foo['id'], + tenant_id=self.tenant_bar['id']) + self.assertDictEquals(extras_ref, self.extras_foobar) + + +class KvsToken(test.TestCase): + def setUp(self): + super(KvsToken, self).setUp() + options = self.appconfig('default') + self.token_api = kvs.KvsToken(options=options, db={}) + + def test_token_crud(self): + token_id = uuid.uuid4().hex + data = {'id': token_id, + 'a': 'b'} + data_ref = self.token_api.create_token(token_id, data) + self.assertDictEquals(data_ref, data) + + new_data_ref = self.token_api.get_token(token_id) + self.assertEquals(new_data_ref, data) + + self.token_api.delete_token(token_id) + deleted_data_ref = self.token_api.get_token(token_id) + self.assert_(deleted_data_ref is None) + + +class KvsCatalog(test.TestCase): + def setUp(self): + super(KvsCatalog, self).setUp() + options = self.appconfig('default') + self.catalog_api = kvs.KvsCatalog(options=options, db={}) + self._load_fixtures() + + def _load_fixtures(self): + self.catalog_foobar = self.catalog_api._create_catalog( + 'foo', 'bar', + {'RegionFoo': {'service_bar': {'foo': 'bar'}}}) + + def test_get_catalog_bad_user(self): + catalog_ref = self.catalog_api.get_catalog('foo' + 'WRONG', 'bar') + self.assert_(catalog_ref is None) + + def test_get_catalog_bad_tenant(self): + catalog_ref = self.catalog_api.get_catalog('foo', 'bar' + 'WRONG') + self.assert_(catalog_ref is None) + + def test_get_catalog(self): + catalog_ref = self.catalog_api.get_catalog('foo', 'bar') + self.assertDictEquals(catalog_ref, self.catalog_foobar)