Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -447,6 +447,17 @@ <h5 class="card-title mt-3 mb-1">AWS</h5>
Upload files
{% endif %}
</button>
{% if project.aws.sent_files %}
<button class="btn btn-primary"
name="aws-delete-project-files" value="{{ project.slug }}"
{% if aws_upload_pending or rw_tasks %}
disabled="disabled"
{% endif %}
type="submit">
<span class="fa fa-trash"></span>
Delete project files
</button>
{% endif %}
</p>
</form>
{% endif %}
Expand Down
36 changes: 36 additions & 0 deletions physionet-django/console/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,8 @@
get_bucket_name,
check_s3_bucket_exists,
has_s3_credentials,
delete_project_files_from_s3,
create_s3_resource,
)

from django.core.management import call_command
Expand Down Expand Up @@ -1028,6 +1030,31 @@ def send_files_to_aws(pid):
project.aws.save()


@associated_task(PublishedProject, 'pid')
@background()
def delete_project_files_task(pid):
"""
Background task to delete project files from S3.
Called after access has already been revoked synchronously.
"""
project = PublishedProject.objects.get(id=pid)

s3 = create_s3_resource()
bucket_name = get_bucket_name(project)
prefix = f"{project.slug}/{project.version}/"
bucket = s3.Bucket(bucket_name)
bucket.objects.filter(Prefix=prefix).delete()

# Delete zip file if it was uploaded
if project.aws.sent_zip:
zip_key = os.path.join(f"{project.slug}/", project.zip_name(legacy=False))
bucket.Object(zip_key).delete()

# Delete the AWS record from the database
project.aws.sent_zip = False
project.aws.delete()


@console_permission_required('project.change_publishedproject')
def manage_doi_request(request, project):
"""
Expand Down Expand Up @@ -1165,6 +1192,15 @@ def manage_published_project(request, project_slug, version):
messages.error(request, 'Project has tasks pending.')
else:
aws_bucket_management(request, project, user)
elif 'aws-delete-project-files' in request.POST and has_s3_credentials():
if any(get_associated_tasks(project, read_only=False)):
messages.error(request, 'Project has tasks pending.')
else:
delete_project_files_from_s3(project)
messages.success(request, 'The project files are being deleted from S3')
# Redirect is required after deletion to avoid rendering the page with
# a stale AWS instance that no longer has a primary key in the database.
return redirect('manage_published_project', project_slug=project_slug, version=version)
elif 'platform' in request.POST:
data_access_form = forms.DataAccessForm(project=project, data=request.POST)
if data_access_form.is_valid():
Expand Down
33 changes: 32 additions & 1 deletion physionet-django/project/cloud/s3.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
from botocore.exceptions import ClientError
from django.conf import settings
from django.db.models import Q

from project.authorization.access import can_view_project_files
from project.models import PublishedProject, AWS, AccessPolicy, AWSAccessPoint, AWSAccessPointUser
from user.models import (
Expand Down Expand Up @@ -1570,3 +1569,35 @@ def create_s3_server_access_log_bucket():
}
),
)


def delete_project_files_from_s3(project):
"""
Immediately revoke access by deleting access points,
set project.aws.sent_files = False, then schedule
file deletion as a background task.
"""
# Import here to avoid circular import
from console.views import delete_project_files_task
if not check_s3_bucket_exists(project):
return

# Delete access points first to immediately revoke access
if project.aws.is_private:
s3control = create_s3_control_client()
for ap in project.aws.access_points.all():
s3control.delete_access_point(
AccountId=settings.AWS_ACCOUNT_ID,
Name=ap.name
)
project.aws.access_points.all().delete()

# Mark sent_files flag to False before deletion starts
project.aws.sent_files = False
project.aws.save()

# Schedule file deletion as a background task
delete_project_files_task(
project.id,
verbose_name='Delete S3 files - {}'.format(project)
)
Loading