vbv/server/vbv_lernwelt/files/integrations.py

131 lines
3.8 KiB
Python

from functools import lru_cache
from typing import Any, Dict
import boto3
from attrs import define
from vbv_lernwelt.files.utils import assert_settings
@define
class S3Credentials:
access_key_id: str
secret_access_key: str
region_name: str
bucket_name: str
default_acl: str
presigned_expiry: int
max_size: int
@lru_cache
def s3_get_credentials() -> S3Credentials:
required_config = assert_settings(
[
"AWS_S3_ACCESS_KEY_ID",
"AWS_S3_SECRET_ACCESS_KEY",
"AWS_S3_REGION_NAME",
"AWS_STORAGE_BUCKET_NAME",
"AWS_DEFAULT_ACL",
"AWS_PRESIGNED_EXPIRY",
"FILE_MAX_SIZE",
],
"S3 credentials not found.",
)
return S3Credentials(
access_key_id=required_config["AWS_S3_ACCESS_KEY_ID"],
secret_access_key=required_config["AWS_S3_SECRET_ACCESS_KEY"],
region_name=required_config["AWS_S3_REGION_NAME"],
bucket_name=required_config["AWS_STORAGE_BUCKET_NAME"],
default_acl=required_config["AWS_DEFAULT_ACL"],
presigned_expiry=required_config["AWS_PRESIGNED_EXPIRY"],
max_size=required_config["FILE_MAX_SIZE"],
)
def s3_get_client():
credentials = s3_get_credentials()
# This is needed until https://github.com/boto/boto3/issues/3015 is fixed
s3 = boto3.client("s3", region_name=credentials.region_name)
endpoint_url = s3.meta.endpoint_url
return boto3.client(
service_name="s3",
aws_access_key_id=credentials.access_key_id,
aws_secret_access_key=credentials.secret_access_key,
region_name=credentials.region_name,
endpoint_url=endpoint_url,
)
def s3_generate_presigned_post(
*, file_path: str, file_type: str, file_name: str
) -> Dict[str, Any]:
credentials = s3_get_credentials()
s3_client = s3_get_client()
acl = credentials.default_acl
expires_in = credentials.presigned_expiry
"""
TODO: Create a type for the presigned_data
It looks like this:
{
'fields': {
'Content-Type': 'image/png',
'acl': 'private',
'key': 'files/<hash>.png',
'policy': 'some-long-base64-string',
'x-amz-algorithm': 'AWS4-HMAC-SHA256',
'x-amz-credential': 'AKIASOZLZI5FJDJ6XTSZ/20220405/eu-central-1/s3/aws4_request',
'x-amz-date': '20220405T114912Z',
'x-amz-signature': '<hash>',
},
'url': 'https://django-styleguide-example.s3.amazonaws.com/'
}
"""
presigned_data = s3_client.generate_presigned_post(
credentials.bucket_name,
file_path,
Fields={
"acl": acl,
"Content-Type": file_type,
"Content-Disposition": f"attachment; filename={file_name}",
},
Conditions=[
{"acl": acl},
{"Content-Type": file_type},
# As an example, allow file size up to 10 MiB
# More on conditions, here:
# https://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-HTTPPOSTConstructPolicy.html
["content-length-range", 1, credentials.max_size],
["starts-with", "$Content-Disposition", ""],
],
ExpiresIn=expires_in,
)
return presigned_data
def s3_generate_presigned_url(*, file_path: str) -> str:
credentials = s3_get_credentials()
s3_client = s3_get_client()
return s3_client.generate_presigned_url(
"get_object",
Params={"Bucket": credentials.bucket_name, "Key": file_path},
ExpiresIn=credentials.presigned_expiry,
)
def s3_delete_file(*, file_path: str):
credentials = s3_get_credentials()
s3_client = s3_get_client()
s3_client.delete_object(
Bucket=credentials.bucket_name,
Key=file_path,
)