summaryrefslogtreecommitdiff
path: root/keystone/api/_shared/EC2_S3_Resource.py
diff options
context:
space:
mode:
Diffstat (limited to 'keystone/api/_shared/EC2_S3_Resource.py')
-rw-r--r--keystone/api/_shared/EC2_S3_Resource.py75
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