summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authortermie <github@anarkystic.com>2011-06-20 17:56:20 -0700
committertermie <github@anarkystic.com>2011-06-20 17:56:20 -0700
commit419c2cb95f5ce0c515fd8636d90065dfcf784c8c (patch)
tree65b70cc0a34133bcfe3dde0f3ec3a6067fb314ee
downloadkeystone-419c2cb95f5ce0c515fd8636d90065dfcf784c8c.tar.gz
keystone-419c2cb95f5ce0c515fd8636d90065dfcf784c8c.tar.xz
keystone-419c2cb95f5ce0c515fd8636d90065dfcf784c8c.zip
initial
-rw-r--r--README.rst31
-rw-r--r--keystonelight/__init__.py0
-rw-r--r--keystonelight/backends/__init__.py0
-rw-r--r--keystonelight/backends/pam.py21
-rw-r--r--keystonelight/identity.py28
-rw-r--r--keystonelight/keystone_compat.py38
-rw-r--r--keystonelight/service.py70
-rw-r--r--keystonelight/token.py16
-rw-r--r--keystonelight/utils.py39
-rw-r--r--tools/pip-requires2
10 files changed, 245 insertions, 0 deletions
diff --git a/README.rst b/README.rst
new file mode 100644
index 00000000..c6b57552
--- /dev/null
+++ b/README.rst
@@ -0,0 +1,31 @@
+
+
+General expected data model:
+
+ ( tenants >--< users ) --< roles
+
+ Tenants and Users have a many-to-many relationship.
+ A given Tenant-User pair can have many Roles.
+
+
+Tenant Schema:
+ id: something big and unique
+ name: something displayable
+ .. created_at: datetime
+ .. deleted_at: datetime
+
+User Schema:
+ id: something big and unique
+ name: something displayable
+ .. created_at: datetime
+ .. deleted_at: datetime
+
+
+General service model:
+
+ (1) a web service with an API
+ (2) a variety of backend storage schemes for tenant-user pairs.
+ (3) a simple token datastore
+
+
+
diff --git a/keystonelight/__init__.py b/keystonelight/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/keystonelight/__init__.py
diff --git a/keystonelight/backends/__init__.py b/keystonelight/backends/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/keystonelight/backends/__init__.py
diff --git a/keystonelight/backends/pam.py b/keystonelight/backends/pam.py
new file mode 100644
index 00000000..8896c930
--- /dev/null
+++ b/keystonelight/backends/pam.py
@@ -0,0 +1,21 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+from __future__ import absolute_imports
+
+import pam
+
+
+class PamIdentity(object):
+ """Very basic identity based on PAM.
+
+ Tenant is always the same as User, root user has admin role.
+ """
+
+ def authenticate(self, username, password):
+ if pam.authenticate(username, password):
+ extras = {}
+ if username == 'root':
+ extras['is_admin'] == True
+ # NOTE(termie): (tenant, user, extras)
+ return (username, username, extras)
+
diff --git a/keystonelight/identity.py b/keystonelight/identity.py
new file mode 100644
index 00000000..ee2345aa
--- /dev/null
+++ b/keystonelight/identity.py
@@ -0,0 +1,28 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# these will be the basic data types for tenants and users
+# backends will make use of them to return something that conforms to their apis
+
+
+import hflags as flags
+
+from keystonelight import utils
+
+
+FLAGS = flags.FLAGS
+flags.DEFINE_string('identity_driver',
+ 'keystonelight.backends.dummy.DummyIdentity',
+ 'identity driver to handle identity requests')
+
+
+class IdentityManager(object):
+ def __init__(self):
+ self.driver = utils.import_object(FLAGS.identity_driver)
+
+
+ def authenticate(self, context, **kwargs):
+ """Passthru authentication to the identity driver.
+
+ This call will basically just result in getting a token.
+ """
+ return self.driver.authenticate(**kwargs)
diff --git a/keystonelight/keystone_compat.py b/keystonelight/keystone_compat.py
new file mode 100644
index 00000000..680c9d93
--- /dev/null
+++ b/keystonelight/keystone_compat.py
@@ -0,0 +1,38 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# this is the web service frontend that emulates keystone
+from keystonelight import service
+
+def _token_to_keystone(token):
+ return {'id': token['id'],
+ 'expires': token.get('expires', '')
+
+
+class KeystoneIdentityController(service.IdentityController):
+ def authenticate(self, context, **kwargs):
+ token = super(KeystoneIdentityController, self).authenticate(
+ context, **kwargs)
+ return {'auth': {'token': _token_to_keystone(token),
+ 'serviceCatalog': SERVICE_CATALOG}}
+
+
+class KeystoneTokenController(service.TokenController):
+ def validate_token(self, context, token_id):
+ token = super(KeystoneTokenController, self).validate_token(
+ context, token_id)
+ # TODO(termie): munge into keystone format
+
+ tenants = [{'tenantId': token['tenant']['id'],
+ 'name': token['tenant']['name']}]
+ roles = []
+ if token['extras'].get('is_admin'):
+ roles.append({
+ 'id': 1,
+ 'href': 'https://.openstack.org/identity/v2.0/roles/admin',
+ 'tenantId': token['tenant']['id']})
+
+ return {'auth': {'token': _token_to_keystone(token),
+ 'user': {'groups': {'group': tenants},
+ 'roleRefs': {'roleRef': roles}
+ 'username': token['user']['name'],
+ 'tenantId': token['tenant']['id']}}}
diff --git a/keystonelight/service.py b/keystonelight/service.py
new file mode 100644
index 00000000..e436cebc
--- /dev/null
+++ b/keystonelight/service.py
@@ -0,0 +1,70 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# this is the web service frontend
+
+import hflags as flags
+
+from keystonelight import wsgi
+
+
+FLAGS = flags.FLAGS
+
+
+class TokenController(wsgi.Controller):
+ """Validate and pass through calls to TokenManager."""
+
+ def __init__(self):
+ self.token_api = token.Manager()
+
+ def validate_token(self, context, token_id):
+ token = self.validate_token(context, token_id)
+ return token
+
+
+class IdentityController(wsgi.Controller):
+ """Validate and pass calls through to IdentityManager.
+
+ IdentityManager will also pretty much just pass calls through to
+ a specific driver.
+ """
+
+ def __init__(self):
+ self.identity_api = identity.Manager()
+ self.token_api = token.Manager()
+
+ def authenticate(self, context, **kwargs):
+ tenant, user, extras = self.identity_api.authenticate(context, **kwargs)
+ token = self.token_api.create_token(context,
+ tenant=tenant,
+ user=user,
+ extras=extras)
+ return token
+
+
+
+class Router(object):
+ def __init__(self):
+ token_controller = TokenController()
+ identity_controller = IdentityController()
+
+ mapper.connect('/v2.0/token', controller=identity_controller,
+ action='authenticate')
+ mapper.connect('/v2.0/token/{token_id}', controller=token_controller,
+ action='revoke_token',
+ conditions=dict(method=['DELETE']))
+
+
+class AdminRouter(object):
+ def __init__(self):
+ token_controller = TokenController()
+ identity_controller = IdentityController()
+
+ mapper.connect('/v2.0/token', controller=identity_controller,
+ action='authenticate')
+ mapper.connect('/v2.0/token/{token_id}', controller=token_controller,
+ action='validate_token',
+ conditions=dict(method=['GET']))
+ mapper.connect('/v2.0/token/{token_id}', controller=token_controller,
+ action='revoke_token',
+ conditions=dict(method=['DELETE']))
+
diff --git a/keystonelight/token.py b/keystonelight/token.py
new file mode 100644
index 00000000..ffdd4c4c
--- /dev/null
+++ b/keystonelight/token.py
@@ -0,0 +1,16 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# the token interfaces
+
+from keystonelight import identity
+
+class TokenManager(object):
+ def create_token(self, context, data):
+ pass
+
+ def validate_token(self, context, token_id):
+ """Return info for a token if it is valid."""
+ pass
+
+ def revoke_token(self, context, token_id):
+ pass
diff --git a/keystonelight/utils.py b/keystonelight/utils.py
new file mode 100644
index 00000000..406ad19e
--- /dev/null
+++ b/keystonelight/utils.py
@@ -0,0 +1,39 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2010 United States Government as represented by the
+# Administrator of the National Aeronautics and Space Administration.
+# Copyright 2011 Justin Santa Barbara
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+
+def import_class(import_str):
+ """Returns a class from a string including module and class."""
+ mod_str, _sep, class_str = import_str.rpartition('.')
+ try:
+ __import__(mod_str)
+ return getattr(sys.modules[mod_str], class_str)
+ except (ImportError, ValueError, AttributeError), exc:
+ LOG.debug(_('Inner Exception: %s'), exc)
+ raise exception.ClassNotFound(class_name=class_str)
+
+
+def import_object(import_str):
+ """Returns an object including a module or module and class."""
+ try:
+ __import__(import_str)
+ return sys.modules[import_str]
+ except ImportError:
+ cls = import_class(import_str)
+ return cls()
diff --git a/tools/pip-requires b/tools/pip-requires
new file mode 100644
index 00000000..14d62412
--- /dev/null
+++ b/tools/pip-requires
@@ -0,0 +1,2 @@
+hflags
+pam==0.1.4