"""Model representation of temporary AWS token.""" import json from botocore.exceptions import ClientError from oto import response from pricing import config from pricing.connectors import sts from sentry_sdk import capture_exception def _handle_client_error_response(error_response, error): error_code = error_response['Error']['Code'] error_message = error_response['Error']['Message'] error_status = error_response['ResponseMetadata']['HTTPStatusCode'] capture_exception(error) return response.create_error_response( code=error_code, message=error_message, status=error_status) def _to_dict(sts_response): """Format boto3 sts client response. Args: sts_response (dict): boto3 response. Returns: dict: our convention for an AWS token. """ credentials = sts_response['Credentials'] return { 'token': credentials['SessionToken'], 'aws_access_key_id': credentials['AccessKeyId'], 'aws_secret_access_key': credentials['SecretAccessKey'], 'expiration': str(credentials['Expiration']) } def _prepare_iam_policy_s3(bucket): """Modify the IAM policy to limit uploads to bucket. Args: bucket: Name of the bucket to put. Returns: dict: The IAM policy """ resource = 'arn:aws:s3:::{bucket}/*'.format(bucket=bucket) return { 'Version': '2012-10-17', 'Statement': [ { 'Effect': 'Allow', 'Action': [ 's3:PutObject' ], 'Resource': resource } ] } def get_s3_token(filename, duration=config.STS_TOKEN_DURATION): """Generate session token for S3 Upload. Args: filename (string): Name of the file, sans extension to be uploaded. duration (int): session token lifetime, in seconds. Default is 900 seconds - 15 minutes. Returns: response.Response: containing credentials. """ sts_client = sts.get_sts_client() if not sts_client: return response.create_fatal_response('sts_unavailable') try: policy = _prepare_iam_policy_s3(f'{config.PRICING_BUCKET}/{config.PRICING_FOLDER}') sts_response = sts_client.assume_role( RoleArn=config.IAM_ROLE, RoleSessionName=filename, DurationSeconds=duration, Policy=json.dumps(policy) ) credentials = _to_dict(sts_response) return response.Response(credentials) except ClientError as e: return _handle_client_error_response(e.response, e)