From 32aa1f463c88f16243d6840583e7a2bdd01f512d Mon Sep 17 00:00:00 2001 From: chrystinne Date: Fri, 15 May 2026 16:37:09 -0400 Subject: [PATCH 01/15] Add disable and restore access buttons for private AWS projects. --- .../console/manage_published_project.html | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/physionet-django/console/templates/console/manage_published_project.html b/physionet-django/console/templates/console/manage_published_project.html index eee35cf0e..454f7849b 100644 --- a/physionet-django/console/templates/console/manage_published_project.html +++ b/physionet-django/console/templates/console/manage_published_project.html @@ -457,6 +457,29 @@
AWS
Delete project files + {% if project.aws.is_private %} + {% if project.aws.access_points.exists %} + + {% else %} + + {% endif %} + {% endif %} {% endif %}

From 2ab0e58acd0eb78e7ccaf32a511128b8c4b06e33 Mon Sep 17 00:00:00 2001 From: chrystinne Date: Fri, 15 May 2026 16:37:40 -0400 Subject: [PATCH 02/15] Add handlers for disabling and restoring AWS access. --- physionet-django/console/views.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/physionet-django/console/views.py b/physionet-django/console/views.py index c12994015..5abf763c2 100644 --- a/physionet-django/console/views.py +++ b/physionet-django/console/views.py @@ -88,6 +88,8 @@ check_s3_bucket_exists, has_s3_credentials, delete_project_files_from_s3, + disable_project_access_in_s3, + restore_project_access_in_s3, ) from django.core.management import call_command @@ -1175,6 +1177,21 @@ def manage_published_project(request, project_slug, version): # 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 'aws-disable-access' in request.POST and has_s3_credentials(): + if any(get_associated_tasks(project, read_only=False)): + messages.error(request, 'Project has tasks pending.') + else: + disable_project_access_in_s3(project) + messages.success(request, 'Access to project files has been disabled.') + # Redirect required to avoid rendering with stale AWS instance + return redirect('manage_published_project', project_slug=project_slug, version=version) + elif 'aws-restore-access' in request.POST and has_s3_credentials(): + if any(get_associated_tasks(project, read_only=False)): + messages.error(request, 'Project has tasks pending.') + else: + restore_project_access_in_s3(project) + messages.success(request, 'Access to project files has been restored.') + 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(): From 5d7960f1ff67c2c7ed92c3abec5f277a8790d679 Mon Sep 17 00:00:00 2001 From: chrystinne Date: Fri, 15 May 2026 16:38:15 -0400 Subject: [PATCH 03/15] Add support for disabling and restoring S3 access points. --- physionet-django/project/cloud/s3.py | 41 ++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/physionet-django/project/cloud/s3.py b/physionet-django/project/cloud/s3.py index dce7213b8..210e09098 100644 --- a/physionet-django/project/cloud/s3.py +++ b/physionet-django/project/cloud/s3.py @@ -1618,3 +1618,44 @@ def delete_project_access_points(project): # Deletes AWS, AWSAccessPoint, and AWSAccessPointUser # from our database as well via CASCADE project.aws.delete() + + +def disable_project_access_in_s3(project): + """ + Temporarily disable access to a private project by deleting its S3 access points + without removing the project files from the bucket. + + For public projects, per-project access cannot be disabled without deleting + the files, since the bucket policy applies to the entire bucket. + + Args: + project (PublishedProject): The project whose access will be disabled. + """ + if not project.aws.is_private: + return + if not check_s3_bucket_exists(project): + return + + 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 + ) + # Only delete AWSAccessPoint records, not the AWS instance itself + project.aws.access_points.all().delete() + + +def restore_project_access_in_s3(project): + """ + Restore access to a private project by recreating its S3 access points. + + Args: + project (PublishedProject): The project whose access will be restored. + """ + if not project.aws.is_private: + return + if not check_s3_bucket_exists(project): + return + + initialize_access_points(project) From d2d4c301e7717aa25702ae016418e60c4214b0de Mon Sep 17 00:00:00 2001 From: chrystinne Date: Fri, 15 May 2026 16:53:40 -0400 Subject: [PATCH 04/15] Rename 'Delete project files' button to 'Delete files'. --- .../console/templates/console/manage_published_project.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/physionet-django/console/templates/console/manage_published_project.html b/physionet-django/console/templates/console/manage_published_project.html index 454f7849b..a789a06ec 100644 --- a/physionet-django/console/templates/console/manage_published_project.html +++ b/physionet-django/console/templates/console/manage_published_project.html @@ -455,7 +455,7 @@
AWS
{% endif %} type="submit"> - Delete project files + Delete files {% if project.aws.is_private %} {% if project.aws.access_points.exists %} From 8ba9405526a2c161343fb7faff621e6a171885ee Mon Sep 17 00:00:00 2001 From: chrystinne Date: Fri, 15 May 2026 16:59:27 -0400 Subject: [PATCH 05/15] Update button colors for AWS cloud management actions. --- .../console/templates/console/manage_published_project.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/physionet-django/console/templates/console/manage_published_project.html b/physionet-django/console/templates/console/manage_published_project.html index a789a06ec..f4a8a977a 100644 --- a/physionet-django/console/templates/console/manage_published_project.html +++ b/physionet-django/console/templates/console/manage_published_project.html @@ -448,7 +448,7 @@
AWS
{% endif %} {% if project.aws.sent_files %} - - - - {% endif %} + {% if s3_uri %} +
  • + Download the files using AWS command line tools: +
    aws s3 sync {{ s3_uri }} DESTINATION
    +
  • + {% elif show_aws_configuration_link %} +
  • + To download the files using AWS command line tools, first + configure your AWS credentials. +
  • + {% elif not project.aws.access_disabled and not user_in_access_point_policy %} +
  • + Click to access the files using AWS command line tools: +
    + {% csrf_token %} + +
    +
  • + {% endif %} {% endif %} From 8fc4f0bc44d75c19e21168cf644539ef6805ee1f Mon Sep 17 00:00:00 2001 From: chrystinne Date: Fri, 22 May 2026 14:35:01 -0400 Subject: [PATCH 11/15] Use access_disabled flag to control disable and restore access buttons. --- .../console/manage_published_project.html | 22 ++++++++++--------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/physionet-django/console/templates/console/manage_published_project.html b/physionet-django/console/templates/console/manage_published_project.html index f4a8a977a..a1d1ad0a5 100644 --- a/physionet-django/console/templates/console/manage_published_project.html +++ b/physionet-django/console/templates/console/manage_published_project.html @@ -458,16 +458,18 @@
    AWS
    Delete files {% if project.aws.is_private %} - {% if project.aws.access_points.exists %} - + {% if not project.aws.access_disabled %} + {% if project.aws.access_points.exists %} + + {% endif %} {% else %}