"""Module that provides generation of upload data.""" import json import uuid from botocore.exceptions import ClientError from asset_transcoder import config from asset_transcoder.connectors import sts from asset_transcoder.models import asset_upload as asset_upload_model from asset_transcoder.utils import api_utils from asset_transcoder.utils import exceptions as exc 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, filename): """Modify the IAM policy to limit uploads to bucket and filename. Args: filename: Name of the file to put. Returns: dict: The IAM policy """ resource = 'arn:aws:s3:::{bucket}/{filename}.*'.format(bucket=bucket, filename=filename) return { 'Version': '2012-10-17', 'Statement': [ { 'Effect': 'Allow', 'Action': [ 's3:PutObject', 's3:PutObjectAcl', 's3:PutObjectTagging', 's3:PutObjectVersionAcl', 's3:PutObjectVersionTagging', ], '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: dict: a dict containing credentials. """ sts_client = sts.get_sts_client() if not sts_client: raise exc.OwsError('sts_unavailable') try: policy = _prepare_iam_policy_s3(config.INPUT_ASSETS_BUCKET_NAME, filename) sts_response = sts_client.assume_role( RoleArn=config.UPLOAD_ROLE_ARN, RoleSessionName=filename, DurationSeconds=duration, Policy=json.dumps(policy) ) return _to_dict(sts_response) except ClientError as error: error_response = error.response error_code = error_response['Error']['Code'] error_message = error_response['Error']['Message'] error_status = error_response['ResponseMetadata']['HTTPStatusCode'] raise exc.OwsError( '{}-{}'.format(error_code, error_message), error_status ) def _generate_filename(): unique_name = uuid.uuid4() underscored_name = str(unique_name).replace('-', '_') return underscored_name def get_upload_permission(): """Generate data required for asset upload. Returns: dict: Contains information for uploading. """ filename = _generate_filename() credentials = get_s3_token(filename) permission = { 'filename': filename, 'credentials': credentials } new_asset = asset_upload_model.create_asset_upload({ 'user_id': api_utils.get_user_id(), 'filename': filename }) permission['asset_upload_id'] = new_asset['id'] permission['bucket'] = config.INPUT_ASSETS_BUCKET_NAME return permission