Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2012 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.
This service allows the creation of access/secret credentials used for the ec2 interop layer of OpenStack.
A user can create as many access/secret pairs, each of which map to a specific tenant. This is required because OpenStack supports a user belonging to multiple tenants, whereas the signatures created on ec2-style requests don't allow specification of which tenant the user wishs to act upon.
To complete the cycle, we provide a method that OpenStack services can use to validate a signature and get a corresponding openstack token. This token allows method calls to other services within the context the access/secret was created. As an example, nova requests keystone to validate the signature of a request, receives a token, and then makes a request to glance to list images needed to perform the requested task.
"""
"""Default pivot point for the EC2 Credentials backend.
See :mod:`keystone.common.manager.Manager` for more details on how this dynamically calls the backend.
"""
# validation '/ec2tokens', controller=ec2_controller, action='authenticate', conditions=dict(method=['POST']))
# crud '/users/{user_id}/credentials/OS-EC2', controller=ec2_controller, action='create_credential', conditions=dict(method=['POST'])) '/users/{user_id}/credentials/OS-EC2', controller=ec2_controller, action='get_credentials', conditions=dict(method=['GET'])) '/users/{user_id}/credentials/OS-EC2/{credential_id}', controller=ec2_controller, action='get_credential', conditions=dict(method=['GET'])) '/users/{user_id}/credentials/OS-EC2/{credential_id}', controller=ec2_controller, action='delete_credential', conditions=dict(method=['DELETE']))
signer = ec2_utils.Ec2Signer(creds_ref['secret']) signature = signer.generate(credentials) if utils.auth_str_equal(credentials['signature'], 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 not utils.auth_str_equal(credentials.signature, signature): raise exception.Unauthorized(message='Invalid EC2 signature.') else: raise exception.Unauthorized(message='EC2 signature not supplied.')
"""Validate a signed EC2 request and provide a token.
Other services (such as Nova) use this **admin** call to determine if a request they signed received is from a valid user.
If it is a valid signature, an openstack token that maps to the user/tenant is returned to the caller, along with all the other details returned from a normal token validation call.
The returned token is useful for making calls to other OpenStack services within the context of the request.
:param context: standard context :param credentials: dict of ec2 signature :param ec2Credentials: DEPRECATED dict of ec2 signature :returns: token: openstack token equivalent to access key along with the corresponding service catalog and roles """
# FIXME(ja): validate that a service token was used!
# NOTE(termie): backwards compat hack if not credentials and ec2Credentials: credentials = ec2Credentials
if 'access' not in credentials: raise exception.Unauthorized(message='EC2 signature not supplied.')
creds_ref = self._get_credentials(context, credentials['access']) 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_project( 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'], tenant_id=tenant_ref['id'])
# Validate that the auth info is valid and nothing is disabled token.validate_auth_info(self, context, user_ref, tenant_ref)
# TODO(termie): optimize this call at some point and put it into the # the return for metadata # fill out the roles in the metadata roles = metadata_ref.get('roles', []) if not roles: raise exception.Unauthorized(message='User not valid for tenant.') roles_ref = [self.identity_api.get_role(context, role_id) for role_id in roles]
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(id=token_id, user=user_ref, tenant=tenant_ref, metadata=metadata_ref))
# 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 token.controllers.Auth.format_authenticate( token_ref, roles_ref, catalog_ref)
"""Create a secret/access pair for use with ec2 style auth.
Generates a new set of credentials that map the the user/tenant pair.
:param context: standard context :param user_id: id of user :param tenant_id: id of tenant :returns: credential: dict of ec2 credential """ self._assert_identity(context, user_id)
'tenant_id': tenant_id, 'access': uuid.uuid4().hex, 'secret': uuid.uuid4().hex}
"""List all credentials for a user.
:param context: standard context :param user_id: id of user :returns: credentials: list of ec2 credential dicts """
"""Retrieve a user's access/secret pair by the access key.
Grab the full access/secret pair for a given access key.
:param context: standard context :param user_id: id of user :param credential_id: access key for credentials :returns: credential: dict of ec2 credential """
"""Delete a user's access/secret pair.
Used to revoke a user's access/secret pair
:param context: standard context :param user_id: id of user :param credential_id: access key for credentials :returns: bool: success """ self._assert_owner(context, user_id, credential_id)
"""Return credentials from an ID.
:param context: standard context :param credential_id: id of credential :raises exception.Unauthorized: when credential id is invalid :returns: credential: dict of ec2 credential. """ credential_id) raise exception.Unauthorized(message='EC2 access key not found.')
"""Check that the provided token belongs to the user.
:param context: standard context :param user_id: id of user :raises exception.Forbidden: when token is invalid
""" context=context, token_id=context['token_id']) except exception.TokenNotFound as e: raise exception.Unauthorized(e)
"""Wrap admin assertion error return statement.
:param context: standard context :returns: bool: success
"""
"""Ensure the provided user owns the credential.
:param context: standard context :param user_id: expected credential owner :param credential_id: id of credential object :raises exception.Forbidden: on failure
""" cred_ref = self.ec2_api.get_credential(context, credential_id) if not user_id == cred_ref['user_id']: raise exception.Forbidden('Credential belongs to another user')
"""Ensure a valid user id.
:param context: standard context :param user_id: expected credential owner :raises exception.UserNotFound: on failure
""" context=context, user_id=user_id) raise exception.UserNotFound(user_id=user_id)
"""Ensure a valid tenant id.
:param context: standard context :param tenant_id: expected tenant :raises exception.ProjectNotFound: on failure
""" context=context, tenant_id=tenant_id) raise exception.ProjectNotFound(project_id=tenant_id) |