diff --git a/examples/paste/nova-api-paste.ini b/examples/paste/nova-api-paste.ini index 56992013c5..9e76dc299a 100644 --- a/examples/paste/nova-api-paste.ini +++ b/examples/paste/nova-api-paste.ini @@ -37,7 +37,7 @@ paste.filter_factory = nova.api.ec2:RequestLogging.factory paste.filter_factory = nova.api.ec2:Lockout.factory [filter:totoken] -paste.filter_factory = nova.api.ec2:ToToken.factory +paste.filter_factory = keystone.middleware.ec2_token:EC2Token.factory [filter:ec2noauth] paste.filter_factory = nova.api.ec2:NoAuth.factory @@ -113,7 +113,7 @@ paste.app_factory = nova.api.openstack.versions:Versions.factory ########## [filter:keystonecontext] -paste.filter_factory = nova.api.auth:KeystoneContext.factory +paste.filter_factory = keystone.middleware.nova_keystone_context:NovaKeystoneContext.factory [filter:authtoken] paste.filter_factory = keystone.middleware.auth_token:filter_factory @@ -125,4 +125,3 @@ auth_port = 5001 auth_protocol = http auth_uri = http://127.0.0.1:5000/ admin_token = 999888777666 - diff --git a/keystone/middleware/ec2_token.py b/keystone/middleware/ec2_token.py new file mode 100644 index 0000000000..d7d762d81b --- /dev/null +++ b/keystone/middleware/ec2_token.py @@ -0,0 +1,89 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2010 United States Government as represented by the +# Administrator of the National Aeronautics and Space Administration. +# 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. +""" +Starting point for routing EC2 requests. + +""" + +from urlparse import urlparse + +from eventlet.green import httplib +import webob.dec +import webob.exc + +from nova import flags +from nova import utils +from nova import wsgi + + +FLAGS = flags.FLAGS +flags.DEFINE_string('keystone_ec2_url', + 'http://localhost:5000/v2.0/ec2tokens', + 'URL to get token from ec2 request.') + + +class EC2Token(wsgi.Middleware): + """Authenticate an EC2 request with keystone and convert to token.""" + + @webob.dec.wsgify(RequestClass=wsgi.Request) + def __call__(self, req): + # Read request signature and access id. + try: + signature = req.params['Signature'] + access = req.params['AWSAccessKeyId'] + except KeyError: + raise webob.exc.HTTPBadRequest() + + # Make a copy of args for authentication and signature verification. + auth_params = dict(req.params) + # Not part of authentication args + auth_params.pop('Signature') + + # Authenticate the request. + creds = {'ec2Credentials': {'access': access, + 'signature': signature, + 'host': req.host, + 'verb': req.method, + 'path': req.path, + 'params': auth_params, + }} + creds_json = utils.dumps(creds) + headers = {'Content-Type': 'application/json'} + + # Disable "has no x member" pylint error + # for httplib and urlparse + # pylint: disable-msg=E1101 + o = urlparse(FLAGS.keystone_ec1_url) + if o.scheme == "http": + conn = httplib.HTTPConnection(o.netloc) + else: + conn = httplib.HTTPSConnection(o.netloc) + conn.request('POST', o.path, body=creds_json, headers=headers) + response = conn.getresponse().read() + conn.close() + + # NOTE(vish): We could save a call to keystone by + # having keystone return token, tenant, + # user, and roles from this call. + result = utils.loads(response) + # TODO(vish): check for errors + + token_id = result['auth']['token']['id'] + # Authenticated! + req.headers['X-Auth-Token'] = token_id + return self.application diff --git a/keystone/middleware/nova_keystone_context.py b/keystone/middleware/nova_keystone_context.py new file mode 100644 index 0000000000..646ba9bdc9 --- /dev/null +++ b/keystone/middleware/nova_keystone_context.py @@ -0,0 +1,62 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright (c) 2011 OpenStack, LLC +# +# 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. +""" +Nova Auth Middleware. + +""" + +import webob.dec +import webob.exc + +from nova import context +from nova import flags +from nova import wsgi + + +FLAGS = flags.FLAGS +flags.DECLARE('use_forwarded_for', 'nova.api.auth') + + +class NovaKeystoneContext(wsgi.Middleware): + """Make a request context from keystone headers""" + + @webob.dec.wsgify(RequestClass=wsgi.Request) + def __call__(self, req): + try: + user_id = req.headers['X_USER'] + except KeyError: + return webob.exc.HTTPUnauthorized() + # get the roles + roles = [r.strip() for r in req.headers.get('X_ROLE', '').split(',')] + project_id = req.headers['X_TENANT'] + # Get the auth token + auth_token = req.headers.get('X_AUTH_TOKEN', + req.headers.get('X_STORAGE_TOKEN')) + + # Build a context, including the auth_token... + remote_address = getattr(req, 'remote_address', '127.0.0.1') + remote_address = req.remote_addr + if FLAGS.use_forwarded_for: + remote_address = req.headers.get('X-Forwarded-For', remote_address) + ctx = context.RequestContext(user_id, + project_id, + roles=roles, + auth_token=auth_token, + strategy='keystone', + remote_address=remote_address) + + req.environ['nova.context'] = ctx + return self.application