summaryrefslogtreecommitdiffstats
path: root/nova
diff options
context:
space:
mode:
authorCerberus <matt.dietz@rackspace.com>2010-09-16 14:41:51 -0500
committerCerberus <matt.dietz@rackspace.com>2010-09-16 14:41:51 -0500
commit11b934f75ac4359b75f246fd9babfc3363a9a396 (patch)
tree98ae7309d2b08131d4f5471e8cb908f35b2b9ea8 /nova
parentc2c113917d3e354f85d473a6d646688b59abad17 (diff)
Replaced the existing Rackspace Auth Mechanism with one that mirrors the implementation in the design document.
Diffstat (limited to 'nova')
-rw-r--r--nova/api/rackspace/__init__.py52
-rw-r--r--nova/api/rackspace/auth.py37
-rw-r--r--nova/tests/api/rackspace/auth.py81
-rw-r--r--nova/tests/api/rackspace/test_helper.py52
4 files changed, 211 insertions, 11 deletions
diff --git a/nova/api/rackspace/__init__.py b/nova/api/rackspace/__init__.py
index b4d666d63..dbba97107 100644
--- a/nova/api/rackspace/__init__.py
+++ b/nova/api/rackspace/__init__.py
@@ -26,8 +26,10 @@ import time
import routes
import webob.dec
import webob.exc
+import webob
from nova import flags
+from nova import utils
from nova import wsgi
from nova.api.rackspace import flavors
from nova.api.rackspace import images
@@ -36,6 +38,10 @@ from nova.api.rackspace import sharedipgroups
from nova.auth import manager
+FLAGS = flags.FLAGS
+flags.DEFINE_string('nova_api_auth', 'nova.api.rackspace.auth.FakeAuth',
+ 'The auth mechanism to use for the Rackspace API implemenation')
+
class API(wsgi.Middleware):
"""WSGI entry point for all Rackspace API requests."""
@@ -47,23 +53,47 @@ class API(wsgi.Middleware):
class AuthMiddleware(wsgi.Middleware):
"""Authorize the rackspace API request or return an HTTP Forbidden."""
- #TODO(gundlach): isn't this the old Nova API's auth? Should it be replaced
- #with correct RS API auth?
+ def __init__(self, application):
+ self.auth_driver = utils.import_class(FLAGS.nova_api_auth)()
+ super(AuthMiddleware, self).__init__(application)
@webob.dec.wsgify
def __call__(self, req):
- context = {}
- if "HTTP_X_AUTH_TOKEN" in req.environ:
- context['user'] = manager.AuthManager().get_user_from_access_key(
- req.environ['HTTP_X_AUTH_TOKEN'])
- if context['user']:
- context['project'] = manager.AuthManager().get_project(
- context['user'].name)
- if "user" not in context:
- return webob.exc.HTTPForbidden()
+ if not req.headers.has_key("X-Auth-Token"):
+ return self.authenticate(req)
+
+ user = self.auth_driver.authorize_token(req.headers["X-Auth-Token"])
+
+ if not user:
+ return webob.exc.HTTPUnauthorized()
+ context = {'user':user}
req.environ['nova.context'] = context
return self.application
+ def authenticate(self, req):
+ # Unless the request is explicitly made against /<version>/ don't
+ # honor it
+ path_info = req.environ['wsgiorg.routing_args'][1]['path_info']
+ if path_info:
+ return webob.exc.HTTPUnauthorized()
+
+ if req.headers.has_key("X-Auth-User") and \
+ req.headers.has_key("X-Auth-Key"):
+ username, key = req.headers['X-Auth-User'], req.headers['X-Auth-Key']
+ token, user = self.auth_driver.authorize_user(username, key)
+ if user and token:
+ res = webob.Response()
+ res.headers['X-Auth-Token'] = token
+ res.headers['X-Server-Management-Url'] = \
+ user['server_management_url']
+ res.headers['X-Storage-Url'] = user['storage_url']
+ res.headers['X-CDN-Management-Url'] = user['cdn_management_url']
+ res.content_type = 'text/plain'
+ res.status = '204'
+ return res
+ else:
+ return webob.exc.HTTPUnauthorized()
+ return webob.exc.HTTPUnauthorized()
class APIRouter(wsgi.Router):
"""
diff --git a/nova/api/rackspace/auth.py b/nova/api/rackspace/auth.py
new file mode 100644
index 000000000..d2b5193c3
--- /dev/null
+++ b/nova/api/rackspace/auth.py
@@ -0,0 +1,37 @@
+import json
+from hashlib import sha1
+from nova import datastore
+
+class FakeAuth(object):
+ def __init__(self, store=datastore.Redis.instance):
+ self._store = store()
+ self.auth_hash = 'rs_fake_auth'
+ self._store.hsetnx(self.auth_hash, 'rs_last_id', 0)
+
+ def authorize_token(self, token):
+ user = self._store.hget(self.auth_hash, token)
+ if user:
+ return json.loads(user)
+ return None
+
+ def authorize_user(self, user, key):
+ token = sha1("%s_%s" % (user, key)).hexdigest()
+ user = self._store.hget(self.auth_hash, token)
+ if not user:
+ return None, None
+ else:
+ return token, json.loads(user)
+
+ def add_user(self, user, key):
+ last_id = self._store.hget(self.auth_hash, 'rs_last_id')
+ token = sha1("%s_%s" % (user, key)).hexdigest()
+ user = {
+ 'id':last_id,
+ 'cdn_management_url':'cdn_management_url',
+ 'storage_url':'storage_url',
+ 'server_management_url':'server_management_url'
+ }
+ new_user = self._store.hsetnx(self.auth_hash, token, json.dumps(user))
+ if new_user:
+ self._store.hincrby(self.auth_hash, 'rs_last_id')
+
diff --git a/nova/tests/api/rackspace/auth.py b/nova/tests/api/rackspace/auth.py
new file mode 100644
index 000000000..65264fae9
--- /dev/null
+++ b/nova/tests/api/rackspace/auth.py
@@ -0,0 +1,81 @@
+import webob
+import webob.dec
+import unittest
+import stubout
+import nova.api
+import nova.api.rackspace.auth
+from nova.tests.api.rackspace import test_helper
+
+class Test(unittest.TestCase):
+ def setUp(self):
+ self.stubs = stubout.StubOutForTesting()
+ self.stubs.Set(nova.api.rackspace.auth.FakeAuth, '__init__',
+ test_helper.fake_auth_init)
+ ds = test_helper.FakeRedis()
+ ds.hset(test_helper.auth_hash, 'rs_last_id', 0)
+
+ def tearDown(self):
+ self.stubs.UnsetAll()
+ test_helper.fake_data_store = {}
+
+ def test_authorize_user(self):
+ auth = nova.api.rackspace.auth.FakeAuth()
+ auth.add_user('herp', 'derp')
+
+ req = webob.Request.blank('/v1.0/')
+ req.headers['X-Auth-User'] = 'herp'
+ req.headers['X-Auth-Key'] = 'derp'
+ result = req.get_response(nova.api.API())
+ self.assertEqual(result.status, '204 No Content')
+ self.assertEqual(len(result.headers['X-Auth-Token']), 40)
+ self.assertEqual(result.headers['X-Server-Management-Url'],
+ "server_management_url")
+ self.assertEqual(result.headers['X-CDN-Management-Url'],
+ "cdn_management_url")
+ self.assertEqual(result.headers['X-Storage-Url'], "storage_url")
+
+ def test_authorize_token(self):
+ auth = nova.api.rackspace.auth.FakeAuth()
+ auth.add_user('herp', 'derp')
+
+ req = webob.Request.blank('/v1.0/')
+ req.headers['X-Auth-User'] = 'herp'
+ req.headers['X-Auth-Key'] = 'derp'
+ result = req.get_response(nova.api.API())
+ self.assertEqual(result.status, '204 No Content')
+ self.assertEqual(len(result.headers['X-Auth-Token']), 40)
+ self.assertEqual(result.headers['X-Server-Management-Url'],
+ "server_management_url")
+ self.assertEqual(result.headers['X-CDN-Management-Url'],
+ "cdn_management_url")
+ self.assertEqual(result.headers['X-Storage-Url'], "storage_url")
+
+ token = result.headers['X-Auth-Token']
+ self.stubs.Set(nova.api.rackspace, 'APIRouter',
+ test_helper.FakeRouter)
+ req = webob.Request.blank('/v1.0/fake')
+ req.headers['X-Auth-Token'] = token
+ result = req.get_response(nova.api.API())
+ self.assertEqual(result.status, '200 OK')
+ self.assertEqual(result.headers['X-Test-Success'], 'True')
+
+ def test_bad_user(self):
+ req = webob.Request.blank('/v1.0/')
+ req.headers['X-Auth-User'] = 'herp'
+ req.headers['X-Auth-Key'] = 'derp'
+ result = req.get_response(nova.api.API())
+ self.assertEqual(result.status, '401 Unauthorized')
+
+ def test_no_user(self):
+ req = webob.Request.blank('/v1.0/')
+ result = req.get_response(nova.api.API())
+ self.assertEqual(result.status, '401 Unauthorized')
+
+ def test_bad_token(self):
+ req = webob.Request.blank('/v1.0/')
+ req.headers['X-Auth-Token'] = 'baconbaconbacon'
+ result = req.get_response(nova.api.API())
+ self.assertEqual(result.status, '401 Unauthorized')
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/nova/tests/api/rackspace/test_helper.py b/nova/tests/api/rackspace/test_helper.py
new file mode 100644
index 000000000..578b1e841
--- /dev/null
+++ b/nova/tests/api/rackspace/test_helper.py
@@ -0,0 +1,52 @@
+import webob
+import webob.dec
+from nova.wsgi import Router
+
+fake_data_store = {}
+auth_hash = 'dummy_hash'
+
+class FakeRedis(object):
+ def __init__(self):
+ global fake_data_store
+ self.store = fake_data_store
+
+ def hsetnx(self, hash_name, key, value):
+ if not self.store.has_key(hash_name):
+ self.store[hash_name] = {}
+
+ if self.store[hash_name].has_key(key):
+ return 0
+ self.store[hash_name][key] = value
+ return 1
+
+ def hset(self, hash_name, key, value):
+ if not self.store.has_key(hash_name):
+ self.store[hash_name] = {}
+
+ self.store[hash_name][key] = value
+ return 1
+
+ def hget(self, hash_name, key):
+ if not self.store[hash_name].has_key(key):
+ return None
+ return self.store[hash_name][key]
+
+ def hincrby(self, hash_name, key, amount=1):
+ self.store[hash_name][key] += amount
+
+class FakeRouter(Router):
+ def __init__(self):
+ pass
+
+ @webob.dec.wsgify
+ def __call__(self, req):
+ res = webob.Response()
+ res.status = '200'
+ res.headers['X-Test-Success'] = 'True'
+ return res
+
+def fake_auth_init(self, store=FakeRedis):
+ global auth_hash
+ self._store = store()
+ self.auth_hash = auth_hash
+