· 6 years ago · Jun 04, 2019, 08:46 PM
1import boto3
2from botocore.exceptions import ClientError
3from constants import StatusCode
4
5from logger import CVLogger
6
7
8def fix_path(path):
9 """Fixes the leading and trailing slash in the path
10 """
11 # case: multiple leading slash
12 if path and path[0] == '/':
13 path = path.lstrip('/')
14 # case: multiple trailing slash
15 if path and path[-1] == '/':
16 path = path.rstrip('/')
17 # case: add the trailing slash
18 if path and path[-1] != '/':
19 path = path + '/'
20
21 return path
22
23
24class S3Wrapper(object):
25
26 def __init__(self, timeout):
27 self.timeout = timeout
28 self.logger = CVLogger()
29
30 def validate(self, region, access_key, secret_key, bucket_name, bucket_path=None):
31 self.logger.info(
32 'S3Wrapper validate begin, region {0}, access_key {1}, bucket_name {2}, bucket_path {3}'.format(
33 region, access_key, bucket_name, bucket_path))
34 try:
35 client = boto3.client('s3', aws_access_key_id=access_key, aws_secret_access_key=secret_key)
36 # check if the bucket exist
37 try:
38 client.head_bucket(Bucket=bucket_name)
39 except ClientError as e:
40 # We could get 403 as well. DO NOT care for auth failure at this point.
41 # In case of strict access policy above call to get the bucket is restricted.
42 # Check for invalid bucket case only
43 if e.response['Error']['Code'] == '404':
44 self.logger.info('Invalid BucketName: {0}, {1}'.format(bucket_name, e.message))
45 return {'Error': 'Invalid BucketName'}, StatusCode.INVALID_BUCKET_NAME
46
47 # validate bucket path
48 bucket_path = fix_path(bucket_path)
49 if bucket_path:
50 try:
51 client.get_object(Bucket=bucket_name, Key=bucket_path)
52 except ClientError as e:
53 # case: Credentials error
54 if e.response['Error']['Code'] in ['InvalidAccessKeyId', 'SignatureDoesNotMatch']:
55 self.logger.info('Invalid credentials for Access Key: {0}, {1}'.format(access_key, e.message))
56 return {'Error': 'Invalid Credentials'}, StatusCode.INVALID_CREDENTIALS
57 # case: Bucket path invalid
58 self.logger.info('Invalid Bucket Path: {0}, {1}'.format(bucket_path, e.message))
59 return {'Error': 'Invalid Bucket Path'}, StatusCode.INVALID_BUCKET_PATH
60
61 # validate bucket region
62 # special case: Location constraint is not set for us-east-1 buckets
63 location = client.get_bucket_location(Bucket=bucket_name)
64 if (location['LocationConstraint'] is None and region != 'us-east-1') \
65 or (location['LocationConstraint'] is not None and location['LocationConstraint'] != region):
66 self.logger.info('Bucket: {0} not found in region: {1}'.format(bucket_name, region))
67 return {'Error': 'Invalid Region for Bucket'}, StatusCode.INVALID_BUCKET_REGION
68
69 return {'Success': 'Operation successful'}, StatusCode.SUCCESS
70 except ClientError as e:
71 if e.response['Error']['Code'] in ['InvalidAccessKeyId', 'SignatureDoesNotMatch']:
72 self.logger.info('Invalid credentials for Access Key: {0}, {1}'.format(access_key, e.message))
73 return {'Error': 'Invalid Credentials'}, StatusCode.INVALID_CREDENTIALS
74 # case: Cannot obtain the region due to strict bucket policy, let user know
75 if e.response['Error']['Code'] == 'AccessDenied':
76 self.logger.info('Insufficient region access permissions for Access Key: {0}, {1}'.format(access_key, e.message))
77 return {'Error': 'Insufficient Region Access Permissions. Please allow bucket location access for this user in the bucket policy'}, StatusCode.INSUFFICIENT_PERMISSIONS
78 # case: Some unknown error
79 self.logger.info('Unknown Client error occured, {0}'.format(e.message or e))
80 except Exception as e:
81 self.logger.info('Unknown error occured, {0}'.format(e.message or e))
82 finally:
83 self.logger.info('S3Wrapper validate success')
84
85 return {'Error': 'Internal Server Error'}, StatusCode.INTERNAL_SERVER_ERROR