Simplify code.

Per Termie's suggestion, share as much as possible with ec2 module and
instantiate s3 module from it.
This commit is contained in:
Chmouel Boudjnah 2012-01-26 01:37:37 +00:00
parent fcea15df2a
commit b1cd21426c
2 changed files with 49 additions and 94 deletions

View File

@ -26,8 +26,10 @@ from keystone import catalog
from keystone import config
from keystone import identity
from keystone import policy
from keystone import service
from keystone import token
from keystone.common import manager
from keystone.common import utils
from keystone.common import wsgi
@ -52,7 +54,7 @@ class Ec2Extension(wsgi.ExtensionRouter):
# validation
mapper.connect('/ec2tokens',
controller=ec2_controller,
action='authenticate_ec2',
action='authenticate',
conditions=dict(method=['POST']))
# crud
@ -83,7 +85,24 @@ class Ec2Controller(wsgi.Application):
self.ec2_api = Manager()
super(Ec2Controller, self).__init__()
def authenticate_ec2(self, context, credentials=None,
def check_signature(self, creds_ref, credentials):
signer = utils.Signer(creds_ref['secret'])
signature = signer.generate(credentials)
if signature == credentials['signature']:
return
# NOTE(vish): Some libraries don't use the port when signing
# requests, so try again without port.
elif ':' in credentials['signature']:
hostname, _port = credentials['host'].split(":")
credentials['host'] = hostname
signature = signer.generate(credentials)
if signature != credentials.signature:
# TODO(termie): proper exception
raise Exception("Not Authorized")
else:
raise Exception("Not Authorized")
def authenticate(self, context, credentials=None,
ec2Credentials=None):
"""Validate a signed EC2 request and provide a token.
@ -113,27 +132,17 @@ class Ec2Controller(wsgi.Application):
creds_ref = self.ec2_api.get_credential(context,
credentials['access'])
signer = utils.Signer(creds_ref['secret'])
signature = signer.generate(credentials)
if signature == credentials['signature']:
pass
# NOTE(vish): Some libraries don't use the port when signing
# requests, so try again without port.
elif ':' in credentials['signature']:
hostname, _port = credentials['host'].split(":")
credentials['host'] = hostname
signature = signer.generate(credentials)
if signature != credentials.signature:
# TODO(termie): proper exception
raise Exception("Not Authorized")
else:
raise Exception("Not Authorized")
self.check_signature(creds_ref, credentials)
# TODO(termie): don't create new tokens every time
# TODO(termie): this is copied from TokenController.authenticate
token_id = uuid.uuid4().hex
tenant_ref = self.identity_api.get_tenant(creds_ref['tenant_id'])
user_ref = self.identity_api.get_user(creds_ref['user_id'])
tenant_ref = self.identity_api.get_tenant(
context=context,
tenant_id=creds_ref['tenant_id'])
user_ref = self.identity_api.get_user(
context=context,
user_id=creds_ref['user_id'])
metadata_ref = self.identity_api.get_metadata(
context=context,
user_id=user_ref['id'],
@ -162,8 +171,9 @@ class Ec2Controller(wsgi.Application):
# TODO(termie): i don't think the ec2 middleware currently expects a
# full return, but it contains a note saying that it
# would be better to expect a full return
return TokenController._format_authenticate(
self, token_ref, roles_ref, catalog_ref)
token_controller = service.TokenController()
return token_controller._format_authenticate(
token_ref, roles_ref, catalog_ref)
def create_credential(self, context, user_id, tenant_id):
"""Create a secret/access pair for use with ec2 style auth.

View File

@ -5,94 +5,39 @@
TODO-DOCS
"""
import uuid
import base64
import hmac
from hashlib import sha1
from keystone import catalog
from keystone import config
from keystone import identity
from keystone import policy
from keystone import token
from keystone import service
from keystone.common import wsgi
from keystone.contrib.ec2 import Manager as EC2Manager
from keystone.contrib import ec2
CONF = config.CONF
def check_signature(creds_ref, credentials):
signature = credentials['signature']
msg = base64.urlsafe_b64decode(str(credentials['token']))
key = str(creds_ref['secret'])
signed = base64.encodestring(hmac.new(key, msg, sha1).digest()).strip()
if signature == signed:
pass
else:
raise Exception("Not Authorized")
class S3Extension(wsgi.ExtensionRouter):
def add_routes(self, mapper):
s3_controller = S3Controller()
controller = ec2.Ec2Controller()
controller.check_signature = check_signature
# validation
mapper.connect('/s3tokens',
controller=s3_controller,
action='authenticate_s3',
controller=controller,
action='authenticate',
conditions=dict(method=['POST']))
# No need CRUD stuff since we are sharing keystone.contrib.ec2
# infos.
class S3Controller(wsgi.Application):
def __init__(self):
self.catalog_api = catalog.Manager()
self.identity_api = identity.Manager()
self.token_api = token.Manager()
self.policy_api = policy.Manager()
self.ec2_api = EC2Manager()
super(S3Controller, self).__init__()
def authenticate_s3(self, context, credentials=None):
"""Validate a signed S3 request and provide a token.
TODO-DOCS::
:param context: standard context
:param credentials: dict of s3 signature
:returns: token: openstack token equivalent to access key along
with the corresponding service catalog and roles
"""
creds_ref = self.ec2_api.get_credential(context,
credentials['access'])
msg = base64.urlsafe_b64decode(str(credentials['token']))
key = str(creds_ref['secret'])
s = base64.encodestring(hmac.new(key, msg, sha1).digest()).strip()
signature = credentials['signature']
if signature == s:
pass
else:
raise Exception("Not Authorized")
token_id = uuid.uuid4().hex
tenant_ref = self.identity_api.get_tenant(context,
creds_ref['tenant_id'])
user_ref = self.identity_api.get_user(context,
creds_ref['user_id'])
metadata_ref = self.identity_api.get_metadata(
context=context,
user_id=user_ref['id'],
tenant_id=tenant_ref['id'])
catalog_ref = self.catalog_api.get_catalog(
context=context,
user_id=user_ref['id'],
tenant_id=tenant_ref['id'],
metadata=metadata_ref)
token_ref = self.token_api.create_token(
context, token_id, dict(expires='',
id=token_id,
user=user_ref,
tenant=tenant_ref,
metadata=metadata_ref))
roles_ref = []
for role_id in metadata_ref.get('roles', []):
roles_ref.append(self.identity_api.get_role(context, role_id))
token_controller = service.TokenController()
return token_controller._format_authenticate(
token_ref, roles_ref, catalog_ref)