"""This file and its contents are licensed under the Apache License 2.0. Please see the included NOTICE for copyright information and LICENSE for a copy of the license.
|
"""
|
import logging
|
import os
|
|
from botocore.exceptions import ClientError, ParamValidationError
|
from botocore.handlers import validate_bucket_name
|
from io_storages.s3.models import S3ExportStorage, S3ImportStorage
|
from io_storages.serializers import ExportStorageSerializer, ImportStorageSerializer
|
from rest_framework import serializers
|
from rest_framework.exceptions import ValidationError
|
|
logger = logging.getLogger(__name__)
|
|
|
class S3StorageSerializerMixin:
|
secure_fields = ['aws_access_key_id', 'aws_secret_access_key']
|
|
def to_representation(self, instance):
|
result = super().to_representation(instance)
|
for attr in self.secure_fields:
|
result.pop(attr)
|
return result
|
|
def validate_bucket(self, value):
|
if not value:
|
return value
|
try:
|
validate_bucket_name({'Bucket': value})
|
except ParamValidationError as exc:
|
raise ValidationError(exc.kwargs['report']) from exc
|
return value
|
|
def validate(self, data):
|
data = super().validate(data)
|
if not data.get('bucket', None):
|
return data
|
|
storage = self.instance
|
if storage:
|
for key, value in data.items():
|
setattr(storage, key, value)
|
else:
|
if 'id' in self.initial_data:
|
storage_object = self.Meta.model.objects.get(id=self.initial_data['id'])
|
for attr in self.secure_fields:
|
data[attr] = data.get(attr) or getattr(storage_object, attr)
|
storage = self.Meta.model(**data)
|
try:
|
storage.validate_connection()
|
except ParamValidationError:
|
raise ValidationError('Wrong credentials for S3 {bucket_name}'.format(bucket_name=storage.bucket))
|
except ClientError as e:
|
if (
|
e.response.get('Error').get('Code') in ['SignatureDoesNotMatch', '403']
|
or e.response.get('ResponseMetadata').get('HTTPStatusCode') == 403
|
):
|
raise ValidationError(
|
'Cannot connect to S3 {bucket_name} with specified AWS credentials'.format(
|
bucket_name=storage.bucket
|
)
|
)
|
if (
|
e.response.get('Error').get('Code') in ['NoSuchBucket', '404']
|
or e.response.get('ResponseMetadata').get('HTTPStatusCode') == 404
|
):
|
raise ValidationError('Cannot find bucket {bucket_name} in S3'.format(bucket_name=storage.bucket))
|
except TypeError as e:
|
logger.info(f'It seems access keys are incorrect: {e}', exc_info=True)
|
raise ValidationError('It seems access keys are incorrect')
|
except KeyError:
|
raise ValidationError(f'{storage.url_scheme}://{storage.bucket}/{storage.prefix} not found.')
|
return data
|
|
|
class S3ImportStorageSerializer(S3StorageSerializerMixin, ImportStorageSerializer):
|
type = serializers.ReadOnlyField(default=os.path.basename(os.path.dirname(__file__)))
|
presign = serializers.BooleanField(required=False, default=True)
|
|
class Meta:
|
model = S3ImportStorage
|
fields = '__all__'
|
|
|
class S3ExportStorageSerializer(S3StorageSerializerMixin, ExportStorageSerializer):
|
type = serializers.ReadOnlyField(default=os.path.basename(os.path.dirname(__file__)))
|
|
class Meta:
|
model = S3ExportStorage
|
fields = '__all__'
|