diff options
Diffstat (limited to 'keystone/api/_shared/EC2_S3_Resource.py')
-rw-r--r-- | keystone/api/_shared/EC2_S3_Resource.py | 75 |
1 files changed, 70 insertions, 5 deletions
diff --git a/keystone/api/_shared/EC2_S3_Resource.py b/keystone/api/_shared/EC2_S3_Resource.py index a1f139116..2e348bcec 100644 --- a/keystone/api/_shared/EC2_S3_Resource.py +++ b/keystone/api/_shared/EC2_S3_Resource.py @@ -12,18 +12,22 @@ # Common base resource for EC2 and S3 Authentication +import datetime import sys from oslo_serialization import jsonutils +from oslo_utils import timeutils import six from werkzeug import exceptions from keystone.common import provider_api from keystone.common import utils +import keystone.conf from keystone import exception as ks_exceptions from keystone.i18n import _ from keystone.server import flask as ks_flask +CONF = keystone.conf.CONF PROVIDERS = provider_api.ProviderAPIs CRED_TYPE_EC2 = 'ec2' @@ -39,6 +43,31 @@ class ResourceBase(ks_flask.ResourceBase): # the ABC module. raise NotImplementedError() + @staticmethod + def _check_timestamp(credentials): + timestamp = ( + # AWS Signature v1/v2 + credentials.get('params', {}).get('Timestamp') or + # AWS Signature v4 + credentials.get('headers', {}).get('X-Amz-Date') or + credentials.get('params', {}).get('X-Amz-Date') + ) + if not timestamp: + # If the signed payload doesn't include a timestamp then the signer + # must have intentionally left it off + return + try: + timestamp = timeutils.parse_isotime(timestamp) + timestamp = timeutils.normalize_time(timestamp) + except Exception as e: + raise ks_exceptions.Unauthorized( + _('Credential timestamp is invalid: %s') % e) + auth_ttl = datetime.timedelta(minutes=CONF.credential.auth_ttl) + current_time = timeutils.normalize_time(timeutils.utcnow()) + if current_time > timestamp + auth_ttl: + raise ks_exceptions.Unauthorized( + _('Credential is expired')) + def handle_authenticate(self): # TODO(morgan): convert this dirty check to JSON Schema validation # this mirrors the previous behavior of the webob system where an @@ -86,7 +115,9 @@ class ResourceBase(ks_flask.ResourceBase): project_id=cred.get('project_id'), access=loaded.get('access'), secret=loaded.get('secret'), - trust_id=loaded.get('trust_id') + trust_id=loaded.get('trust_id'), + app_cred_id=loaded.get('app_cred_id'), + access_token_id=loaded.get('access_token_id') ) # validate the signature @@ -107,8 +138,35 @@ class ResourceBase(ks_flask.ResourceBase): ks_exceptions.Unauthorized(e), sys.exc_info()[2]) - roles = PROVIDERS.assignment_api.get_roles_for_user_and_project( - user_ref['id'], project_ref['id']) + self._check_timestamp(credentials) + + trustee_user_id = None + auth_context = None + if cred_data['trust_id']: + trust = PROVIDERS.trust_api.get_trust(cred_data['trust_id']) + roles = [r['id'] for r in trust['roles']] + # NOTE(cmurphy): if this credential was created using a + # trust-scoped token with impersonation, the user_id will be for + # the trustor, not the trustee. In this case, issuing a + # trust-scoped token to the trustor will fail. In order to get a + # trust-scoped token, use the user ID of the trustee. With + # impersonation, the resulting token will still be for the trustor. + # Without impersonation, the token will be for the trustee. + if trust['impersonation'] is True: + trustee_user_id = trust['trustee_user_id'] + elif cred_data['app_cred_id']: + ac_client = PROVIDERS.application_credential_api + app_cred = ac_client.get_application_credential( + cred_data['app_cred_id']) + roles = [r['id'] for r in app_cred['roles']] + elif cred_data['access_token_id']: + access_token = PROVIDERS.oauth_api.get_access_token( + cred_data['access_token_id']) + roles = jsonutils.loads(access_token['role_ids']) + auth_context = {'access_token_id': cred_data['access_token_id']} + else: + roles = PROVIDERS.assignment_api.get_roles_for_user_and_project( + user_ref['id'], project_ref['id']) if not roles: raise ks_exceptions.Unauthorized(_('User not valid for project.')) @@ -119,7 +177,14 @@ class ResourceBase(ks_flask.ResourceBase): method_names = ['ec2credential'] + if trustee_user_id: + user_id = trustee_user_id + else: + user_id = user_ref['id'] token = PROVIDERS.token_provider_api.issue_token( - user_id=user_ref['id'], method_names=method_names, - project_id=project_ref['id']) + user_id=user_id, method_names=method_names, + project_id=project_ref['id'], + trust_id=cred_data['trust_id'], + app_cred_id=cred_data['app_cred_id'], + auth_context=auth_context) return token |