Builds for
1 pipeline
passed
in
29 minutes 56 seconds
feat: add s3 events lambda function
deprecate cloudfront from file download due to cache issue add lambda function which triggered when s3 put events update file size to database when upload update file name in s3 metadata when rename
Showing
7 changed files
with
59 additions
and
29 deletions
... | @@ -23,7 +23,7 @@ BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) | ... | @@ -23,7 +23,7 @@ BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) |
23 | SECRET_KEY = 'wfs076redt^-5_*dw6!_dqz*2z!a_#()33y@r_q7&+4r_40h9$' | 23 | SECRET_KEY = 'wfs076redt^-5_*dw6!_dqz*2z!a_#()33y@r_q7&+4r_40h9$' |
24 | 24 | ||
25 | # SECURITY WARNING: don't run with debug turned on in production! | 25 | # SECURITY WARNING: don't run with debug turned on in production! |
26 | -DEBUG = True | 26 | +DEBUG = False |
27 | 27 | ||
28 | ALLOWED_HOSTS = ['127.0.0.1', 'khubox-api.khunet.net'] | 28 | ALLOWED_HOSTS = ['127.0.0.1', 'khubox-api.khunet.net'] |
29 | 29 | ||
... | @@ -49,7 +49,7 @@ MIDDLEWARE = [ | ... | @@ -49,7 +49,7 @@ MIDDLEWARE = [ |
49 | 'django.contrib.auth.middleware.AuthenticationMiddleware', | 49 | 'django.contrib.auth.middleware.AuthenticationMiddleware', |
50 | 'django.contrib.messages.middleware.MessageMiddleware', | 50 | 'django.contrib.messages.middleware.MessageMiddleware', |
51 | 'django.middleware.clickjacking.XFrameOptionsMiddleware', | 51 | 'django.middleware.clickjacking.XFrameOptionsMiddleware', |
52 | - 'khubox.auth.AuthMiddleware', | 52 | + 'khubox.middlewares.auth.AuthMiddleware', |
53 | ] | 53 | ] |
54 | 54 | ||
55 | ROOT_URLCONF = 'config.urls' | 55 | ROOT_URLCONF = 'config.urls' |
... | @@ -129,9 +129,6 @@ STATIC_URL = '/static/' | ... | @@ -129,9 +129,6 @@ STATIC_URL = '/static/' |
129 | 129 | ||
130 | # Custom Settings | 130 | # Custom Settings |
131 | S3_BUCKET = 'khubox-files' | 131 | S3_BUCKET = 'khubox-files' |
132 | -CDN_PATH = 'https://khubox-files.khunet.net' | ||
133 | -CLOUDFRONT_KEY_ID = 'APKAJ3FOBWI34OZJTXJQ' | ||
134 | -CLOUDFRONT_KEY_PRIVATE = '-----BEGIN RSA PRIVATE KEY-----\nMIIEowIBAAKCAQEA18VtzURs+fQev5L00LRwRbJaObQI4kfJCIsOE7eWSOqq4Akh\nA7fI6vs3z8orXBvgc+k6GgAHrIdNwckxoQuTsCxrTDm1104qy1T4JkVxkIBYHJgH\nGzKUloK5IqdmcYbOK7IQeHJ2gR9Mv/3oKUytJSsrbM9k4oLrsxGpyEuJeHIg28aP\nwhoVWmBGcPu48l4aYAZEVY7LZRJSOQ9y7Lf8FS1u7Xtw1P91gEaqrqVXqRWY02C8\nsixpJJuiAPnM3rpcpVNlAaPdDkWmaWYJoJDOlce7Dmx1a9Ckr24krM//vpEljurC\nGml0AsHpL8LE9msM5VA+miCxCz/K+wDgm2xvvQIDAQABAoIBAFmP3pLceyuJVBYK\n5smWjB+x91eKTkG2sFB2f8JZau0bUxApWeXULHa1DiaW8UaLX7BdN7vBFW5cvz7X\nx1zklEoFNghuz/btwD+kJlikbI4hZ/F+fTyh0yFiY3xp5dDrtrpWcBW+1UeleVMc\nDnjOFfSepajFsUeANlue0k2MZSRz34s2T2scV5ZkooqdXddYUF/wDhefYm6uvCgI\nPyvY/mbJTyhte/xagY/m6yzk5gxgad0qP2ZZrHhLLMlJ/GEZToWDxD2xUei61NQT\nFFc5ZutkAE6fVb3I4SJUBSX5fl0tTMz4Aak1GP2phMhjZyjYnQMq8kvL4BNFb7gp\nary8W/UCgYEA+eKkfjjlPsEx6yHMhD7pAwy/MpUqJmF5LMxIG+qfd+GZMQ2oucn5\npUAwBHP7BD6E9H7/7jdjnCiO+iPrzM9vNLfqsdCtPWzoFYJp/6Fv002uX8seNYvJ\nQyQqrM85LYIghhnkcmJMA8GR/Iu5ZEeE2BkAl9T2EKclzmB62d/ki6MCgYEA3Q0V\nz08IEwSJW+jEsOM+XGg2YkNqCVKGQD9n4CPx0TFVJxfqFl2nVwlN2hfrlJLUQ9+l\nfXnS5AW3tE88t9we+ea0saJZEqqlm/rGsfTV/twS9cWSgvG5fTzhUbu9/ElMU29L\nmydQfWTvCup7zCuQtgwM5ZRtPwuKsI8urUg6zR8CgYAt0coZvvMCI8i0dbkbkrGF\nNqQkcUeOTBc9CKQ8QjRFdh9x6DBFCOz2ySNE3cNsTs5wSo1BL/Ta4HD/GvEU2ABr\nKUImor3xYnPX5dbr4b0wgLD1rbf3V49q+Um98C1q086E6GCEPNP1aFwNc81lvtt0\nCHmcXZdVDGEZS4WbR7uPgwKBgBO/moY12lPQoPDsH75p3uVkjg9DVJLWo5XT1FTr\nASyeSqw+b7Rl05BsDV+BqZNRdtNFhMRsANJMTHg4aAVJDh9nZBdGmMyZIEiKI/w8\nEm49fRgl+YvnSpoMuViS/EswxTfjBo8q+P7q6IxCHKNF9Ry+gNx14TizsEVL1XC3\ntkEjAoGBAMyp7wdPobJMXcclRVq6rqHs9OMcnZAveVKyxNgDbZu4OB5X4xTxGEYT\nNZQ0MFf/HcwlnH7797gVQeqF9dlqUJYe+Fc8lc/Rcwta/4R5uMgri9t8RKN91YKF\nUUFBsDEkWlkoAmfPkcrrq9cLJlmSNt3ehQj4p5iAJwoVBXXa++PO\n-----END RSA PRIVATE KEY-----' | ||
135 | 132 | ||
136 | 133 | ||
137 | # Cors | 134 | # Cors | ... | ... |
1 | import boto3 | 1 | import boto3 |
2 | -import datetime | ||
3 | -from botocore.signers import CloudFrontSigner | ||
4 | -from cryptography.hazmat.backends import default_backend | ||
5 | -from cryptography.hazmat.primitives import hashes, serialization | ||
6 | -from cryptography.hazmat.primitives.asymmetric import padding | ||
7 | from django.conf import settings | 2 | from django.conf import settings |
8 | 3 | ||
9 | 4 | ||
10 | -def rsa_signer(message): | 5 | +def sign_download(file_id): |
11 | - private_key = serialization.load_pem_private_key( | 6 | + s3 = boto3.client('s3') |
12 | - settings.CLOUDFRONT_KEY_PRIVATE.encode('ascii'), | 7 | + signed_url = s3.generate_presigned_url( |
13 | - password=None, | 8 | + 'get_object', |
14 | - backend=default_backend() | 9 | + Params={'Bucket': settings.S3_BUCKET, 'Key': file_id}, |
10 | + ExpiresIn=3600, | ||
11 | + HttpMethod='GET' | ||
15 | ) | 12 | ) |
16 | - return private_key.sign(message, padding.PKCS1v15(), hashes.SHA1()) | ||
17 | - | ||
18 | - | ||
19 | -def sign_download(url): | ||
20 | - expire_date = datetime.datetime.utcnow() + datetime.timedelta(hours=1) | ||
21 | - cloudfront_signer = CloudFrontSigner(settings.CLOUDFRONT_KEY_ID, rsa_signer) | ||
22 | - signed_url = cloudfront_signer.generate_presigned_url(url, date_less_than=expire_date) | ||
23 | return signed_url | 13 | return signed_url |
24 | 14 | ||
25 | 15 | ||
... | @@ -52,3 +42,13 @@ def s3_copy(file_id, new_file_id): | ... | @@ -52,3 +42,13 @@ def s3_copy(file_id, new_file_id): |
52 | } | 42 | } |
53 | obj = bucket.Object(str(new_file_id)) | 43 | obj = bucket.Object(str(new_file_id)) |
54 | obj.copy(copy_source) | 44 | obj.copy(copy_source) |
45 | + | ||
46 | + | ||
47 | +def s3_update_and_return_size(file_id, name): | ||
48 | + s3 = boto3.resource('s3') | ||
49 | + s3_object = s3.Object(settings.S3_BUCKET, file_id) | ||
50 | + s3_object.copy_from(CopySource={'Bucket': settings.S3_BUCKET, 'Key': file_id}, | ||
51 | + ContentType='application/octet-stream', | ||
52 | + ContentDisposition='attachment; filename=%s' % name, | ||
53 | + MetadataDirective='REPLACE') | ||
54 | + return s3_object.content_length | ... | ... |
khubox-api/khubox/lambda.py
0 → 100644
1 | +import os | ||
2 | +import django | ||
3 | +from .aws import s3_delete, s3_update_and_return_size | ||
4 | +from .models import File | ||
5 | + | ||
6 | +os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings") | ||
7 | +django.setup() | ||
8 | + | ||
9 | + | ||
10 | +def process_upload(event, context): | ||
11 | + file_id = event['Records'][0]['s3']['object']['key'] | ||
12 | + file = File.objects.filter(id=file_id, deleted_at__isnull=True) | ||
13 | + | ||
14 | + # File Gone | ||
15 | + if len(file) == 0: | ||
16 | + s3_delete([file_id]) | ||
17 | + return | ||
18 | + | ||
19 | + # Update | ||
20 | + file[0].size = s3_update_and_return_size(file_id, file[0].name) | ||
21 | + file[0].save() |
1 | import json | 1 | import json |
2 | import uuid | 2 | import uuid |
3 | -from django.conf import settings | ||
4 | from django.utils import timezone | 3 | from django.utils import timezone |
5 | -from ..aws import sign_upload, sign_download, s3_copy, s3_delete | 4 | +from pathvalidate import sanitize_filename |
5 | +from ..aws import sign_upload, sign_download, s3_copy, s3_delete, s3_update_and_return_size | ||
6 | from ..models import File, GroupUser | 6 | from ..models import File, GroupUser |
7 | 7 | ||
8 | 8 | ||
... | @@ -91,7 +91,7 @@ def create(request): | ... | @@ -91,7 +91,7 @@ def create(request): |
91 | owner_group_id=parent[0].owner_group_id, | 91 | owner_group_id=parent[0].owner_group_id, |
92 | uploader_id=request.user_id, | 92 | uploader_id=request.user_id, |
93 | type=received['type'], | 93 | type=received['type'], |
94 | - name=received['name'], | 94 | + name=sanitize_filename(received['name']), |
95 | size=0, | 95 | size=0, |
96 | created_at=timezone.now() | 96 | created_at=timezone.now() |
97 | ) | 97 | ) |
... | @@ -178,8 +178,7 @@ def find_item(request, file_id): | ... | @@ -178,8 +178,7 @@ def find_item(request, file_id): |
178 | 178 | ||
179 | # Return File | 179 | # Return File |
180 | if file[0].type == 'file': | 180 | if file[0].type == 'file': |
181 | - download_url = '%s/%s' % (settings.CDN_PATH, file[0].id) | 181 | + download_url = sign_download(file[0].id) |
182 | - download_url = sign_download(download_url) | ||
183 | data = { | 182 | data = { |
184 | 'id': file[0].id, | 183 | 'id': file[0].id, |
185 | 'parent_id': file[0].parent_id, | 184 | 'parent_id': file[0].parent_id, |
... | @@ -273,7 +272,8 @@ def update_item(request, file_id): | ... | @@ -273,7 +272,8 @@ def update_item(request, file_id): |
273 | if 'name' in received: | 272 | if 'name' in received: |
274 | if received['name'] == '': | 273 | if received['name'] == '': |
275 | return {'result': False, 'error': '이름을 제대로 입력해주세요.'} | 274 | return {'result': False, 'error': '이름을 제대로 입력해주세요.'} |
276 | - file[0].name = received['name'] | 275 | + file[0].name = sanitize_filename(received['name']) |
276 | + s3_update_and_return_size(file_id, file[0].name) | ||
277 | if 'parent_id' in received: | 277 | if 'parent_id' in received: |
278 | file[0].parent_id = received['parent_id'] | 278 | file[0].parent_id = received['parent_id'] |
279 | if 'is_public' in received: | 279 | if 'is_public' in received: | ... | ... |
... | @@ -17,6 +17,7 @@ idna==2.9 | ... | @@ -17,6 +17,7 @@ idna==2.9 |
17 | importlib-metadata==1.6.0 | 17 | importlib-metadata==1.6.0 |
18 | jmespath==0.10.0 | 18 | jmespath==0.10.0 |
19 | -e git+http://khuhub.khu.ac.kr/2016104129/kappa.git@1b0e17bb6da7460d9d494828c682a5ef66736aa3#egg=kappa | 19 | -e git+http://khuhub.khu.ac.kr/2016104129/kappa.git@1b0e17bb6da7460d9d494828c682a5ef66736aa3#egg=kappa |
20 | +pathvalidate==2.3.0 | ||
20 | pip-tools==5.1.2 | 21 | pip-tools==5.1.2 |
21 | placebo==0.9.0 | 22 | placebo==0.9.0 |
22 | pycparser==2.20 | 23 | pycparser==2.20 | ... | ... |
... | @@ -10,6 +10,17 @@ | ... | @@ -10,6 +10,17 @@ |
10 | "vpc_config": { | 10 | "vpc_config": { |
11 | "SubnetIds": ["subnet-02d51e69"], | 11 | "SubnetIds": ["subnet-02d51e69"], |
12 | "SecurityGroupIds": ["sg-09c30d37d6a504f5c"] | 12 | "SecurityGroupIds": ["sg-09c30d37d6a504f5c"] |
13 | - } | 13 | + }, |
14 | + "events": [ | ||
15 | + { | ||
16 | + "function": "khubox.lambda.process_upload", | ||
17 | + "event_source": { | ||
18 | + "arn": "arn:aws:s3:::khubox-files", | ||
19 | + "events": [ | ||
20 | + "s3:ObjectCreated:Put" | ||
21 | + ] | ||
22 | + } | ||
23 | + } | ||
24 | + ] | ||
14 | } | 25 | } |
15 | } | 26 | } | ... | ... |
-
Please register or login to post a comment