From 3b242615f0ffd1992fb2aed02c25cb639bef1500 Mon Sep 17 00:00:00 2001 From: Matthew Curran Date: Fri, 21 Aug 2026 12:44:25 -0400 Subject: [PATCH 1/9] fix: Allow any valid user access to public Dataset through SDK. --- gateway/sds_gateway/api_methods/models.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/gateway/sds_gateway/api_methods/models.py b/gateway/sds_gateway/api_methods/models.py index 398629f6..f9e49f59 100644 --- a/gateway/sds_gateway/api_methods/models.py +++ b/gateway/sds_gateway/api_methods/models.py @@ -1455,6 +1455,12 @@ def get_user_permission_level( if permission: return permission.permission_level + # if we get to here then check if the item is public + if item_type in item_models: + model_class = item_models[ItemType(item_type)] + if model_class.objects.filter(uuid=item_uuid).exists(): + return PermissionLevel.VIEWER + return None @classmethod From f894e9cf4e89e217d46c361adc324e0936989b7a Mon Sep 17 00:00:00 2001 From: Matthew Curran Date: Fri, 21 Aug 2026 12:53:36 -0400 Subject: [PATCH 2/9] fix: Forgot the is_public condition. --- gateway/sds_gateway/api_methods/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gateway/sds_gateway/api_methods/models.py b/gateway/sds_gateway/api_methods/models.py index f9e49f59..55c9a7af 100644 --- a/gateway/sds_gateway/api_methods/models.py +++ b/gateway/sds_gateway/api_methods/models.py @@ -1458,7 +1458,7 @@ def get_user_permission_level( # if we get to here then check if the item is public if item_type in item_models: model_class = item_models[ItemType(item_type)] - if model_class.objects.filter(uuid=item_uuid).exists(): + if model_class.objects.filter(uuid=item_uuid, is_public=True).exists(): return PermissionLevel.VIEWER return None From 3bb0f38f97cc73109efb4bc897a8c7cbc84f7679 Mon Sep 17 00:00:00 2001 From: Matthew Curran Date: Fri, 21 Aug 2026 12:58:04 -0400 Subject: [PATCH 3/9] fix: Also forgot the is_deleted condition. --- gateway/sds_gateway/api_methods/models.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/gateway/sds_gateway/api_methods/models.py b/gateway/sds_gateway/api_methods/models.py index 55c9a7af..fd37bb58 100644 --- a/gateway/sds_gateway/api_methods/models.py +++ b/gateway/sds_gateway/api_methods/models.py @@ -1458,7 +1458,9 @@ def get_user_permission_level( # if we get to here then check if the item is public if item_type in item_models: model_class = item_models[ItemType(item_type)] - if model_class.objects.filter(uuid=item_uuid, is_public=True).exists(): + if model_class.objects.filter( + uuid=item_uuid, is_public=True, is_deleted=False + ).exists(): return PermissionLevel.VIEWER return None From 3d323a17603707dbdbe8b4a836269eaf44677d68 Mon Sep 17 00:00:00 2001 From: Matthew Curran Date: Fri, 21 Aug 2026 13:18:25 -0400 Subject: [PATCH 4/9] fix: Extend is_public check to user file access. --- .../sds_gateway/api_methods/utils/asset_access_control.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/gateway/sds_gateway/api_methods/utils/asset_access_control.py b/gateway/sds_gateway/api_methods/utils/asset_access_control.py index 4b48c2bd..370cdb05 100644 --- a/gateway/sds_gateway/api_methods/utils/asset_access_control.py +++ b/gateway/sds_gateway/api_methods/utils/asset_access_control.py @@ -77,7 +77,7 @@ def user_has_access_to_file(user, file: File) -> bool: # Check if file's captures are accessible (directly shared, owned by user) user_has_access_to_capture = any( - capture.owner == user or capture.uuid in shared_captures + capture.owner == user or capture.uuid in shared_captures or capture.is_public for capture in file_captures ) @@ -88,7 +88,9 @@ def user_has_access_to_file(user, file: File) -> bool: capture, include_deleted=False ) if any( - dataset.owner == user or dataset.uuid in shared_datasets + dataset.owner == user + or dataset.uuid in shared_datasets + or dataset.is_public for dataset in capture_datasets ): user_has_access_to_capture = True @@ -97,7 +99,7 @@ def user_has_access_to_file(user, file: File) -> bool: # Use centralized function to get datasets (handles both M2M and FK) file_datasets = relationship_utils.get_file_datasets(file, include_deleted=False) user_has_access_to_dataset = any( - dataset.owner == user or dataset.uuid in shared_datasets + dataset.owner == user or dataset.uuid in shared_datasets or dataset.is_public for dataset in file_datasets ) From 199e050580fa2906e98659ff897ee218421b4baf Mon Sep 17 00:00:00 2001 From: Matthew Curran Date: Fri, 21 Aug 2026 13:51:23 -0400 Subject: [PATCH 5/9] fix: Add is_public condition to user capture access function. --- gateway/sds_gateway/api_methods/utils/asset_access_control.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gateway/sds_gateway/api_methods/utils/asset_access_control.py b/gateway/sds_gateway/api_methods/utils/asset_access_control.py index 370cdb05..7f7f7bbe 100644 --- a/gateway/sds_gateway/api_methods/utils/asset_access_control.py +++ b/gateway/sds_gateway/api_methods/utils/asset_access_control.py @@ -42,7 +42,7 @@ def user_has_access_to_capture(user, capture: Capture) -> bool: # Check if any dataset is owned by user or in shared_datasets user_has_access_to_dataset = any( - dataset.owner == user or dataset.uuid in shared_datasets + dataset.owner == user or dataset.uuid in shared_datasets or dataset.is_public for dataset in capture_datasets ) From ea7aaf7d40ecad9f53744f2b062f0f7b82e07d43 Mon Sep 17 00:00:00 2001 From: Matthew Curran Date: Fri, 21 Aug 2026 14:44:46 -0400 Subject: [PATCH 6/9] fix: Expand is_public conditions to accessible files and captures functions. --- gateway/sds_gateway/api_methods/utils/asset_access_control.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/gateway/sds_gateway/api_methods/utils/asset_access_control.py b/gateway/sds_gateway/api_methods/utils/asset_access_control.py index 7f7f7bbe..c84e8bde 100644 --- a/gateway/sds_gateway/api_methods/utils/asset_access_control.py +++ b/gateway/sds_gateway/api_methods/utils/asset_access_control.py @@ -153,6 +153,7 @@ def get_accessible_files_queryset(user): | # Capture directly shared with user Q(captures__uuid__in=captures_shared_with_user) + | Q(captures__is_public=True) | # Capture part of dataset that is shared with user (via M2M) Q( @@ -164,6 +165,7 @@ def get_accessible_files_queryset(user): | # Dataset directly shared with user Q(captures__datasets__uuid__in=datasets_shared_with_user) + | Q(captures__datasets__is_public=True) ) ) ) @@ -202,6 +204,7 @@ def get_accessible_files_queryset(user): | # Dataset directly shared with user Q(datasets__uuid__in=datasets_shared_with_user) + | Q(datasets__is_public=True) ) ) | @@ -268,6 +271,7 @@ def get_accessible_captures_queryset(user): | # Dataset directly shared with user Q(datasets__uuid__in=datasets_shared_with_user) + | Q(datasets__is_public=True) ) ) | From cc7845f8a67348ff99c5756a7e68dcb2c205287c Mon Sep 17 00:00:00 2001 From: Matthew Curran Date: Mon, 24 Aug 2026 15:08:24 -0400 Subject: [PATCH 7/9] gwy: Unit test for public files/captures/datasets in API functions. --- .../tests/test_asset_access_control.py | 55 +++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/gateway/sds_gateway/api_methods/tests/test_asset_access_control.py b/gateway/sds_gateway/api_methods/tests/test_asset_access_control.py index a5c7f67f..4257aac9 100644 --- a/gateway/sds_gateway/api_methods/tests/test_asset_access_control.py +++ b/gateway/sds_gateway/api_methods/tests/test_asset_access_control.py @@ -324,6 +324,61 @@ def test_get_accessible_files_queryset_via_dataset_m2m(self): assert file1 not in other_files assert file2 in other_files # File owner always has access + def test_public_m2m_assets_are_in_accessible_querysets(self): + """Public M2M relationships grant access without an explicit share.""" + # Generated test via Cursor + public_dataset = Dataset.objects.create( + name="Public Dataset", + owner=self.owner, + description="Public test dataset", + is_public=True, + ) + public_capture = Capture.objects.create( + name="Public Capture", + owner=self.owner, + capture_type=CaptureType.RadioHound, + index_name="public-captures-rh", + top_level_dir="public-capture-dir", + is_public=True, + ) + capture_in_public_dataset = Capture.objects.create( + name="Capture in Public Dataset", + owner=self.owner, + capture_type=CaptureType.RadioHound, + index_name="public-dataset-captures-rh", + top_level_dir="public-dataset-capture-dir", + ) + capture_in_public_dataset.datasets.add(public_dataset) + + file_in_public_capture = File.objects.create( + name="public-capture-file.h5", + owner=self.owner, + size=1000, + ) + file_in_public_capture.captures.add(public_capture) + + file_in_capture_in_public_dataset = File.objects.create( + name="public-dataset-capture-file.h5", + owner=self.owner, + size=1000, + ) + file_in_capture_in_public_dataset.captures.add(capture_in_public_dataset) + + file_in_public_dataset = File.objects.create( + name="public-dataset-file.h5", + owner=self.owner, + size=1000, + ) + file_in_public_dataset.datasets.add(public_dataset) + + accessible_files = get_accessible_files_queryset(self.other_user) + accessible_captures = get_accessible_captures_queryset(self.other_user) + + assert file_in_public_capture in accessible_files + assert file_in_capture_in_public_dataset in accessible_files + assert file_in_public_dataset in accessible_files + assert capture_in_public_dataset in accessible_captures + # Queryset tests with both FK and M2M def test_get_accessible_files_queryset_via_both_relationships(self): """Test get_accessible_files_queryset with both FK and M2M relationships.""" From 09f09f4bda053faa73871e2f7918c5698f43ae94 Mon Sep 17 00:00:00 2001 From: Matthew Curran Date: Mon, 24 Aug 2026 15:28:11 -0400 Subject: [PATCH 8/9] gwy: Make access queries more explicit around is_public. --- .../api_methods/utils/asset_access_control.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/gateway/sds_gateway/api_methods/utils/asset_access_control.py b/gateway/sds_gateway/api_methods/utils/asset_access_control.py index c84e8bde..a114664a 100644 --- a/gateway/sds_gateway/api_methods/utils/asset_access_control.py +++ b/gateway/sds_gateway/api_methods/utils/asset_access_control.py @@ -111,10 +111,10 @@ def get_accessible_files_queryset(user): Get a queryset of files that the user has access to. A user has access to files if: - 1. They own the file directly, OR + 1. They own the file directly, OR is_public=True OR 2. The file is part of a capture that is shared with them, OR 3. The file is part of a dataset that is shared with them, OR - 4. The file is part of a capture that is part of a shared dataset + 4. The file is part of a capture that is part of a shared dataset, OR Args: user: The user to check access for @@ -142,6 +142,8 @@ def get_accessible_files_queryset(user): # test_file_owner_has_access access_query |= Q(owner=user) + access_query |= Q(is_public=True) + # 2. Files part of captures that are shared with the user # EXPAND: Support both M2M (captures) and FK (capture) relationships capture_shared_query = ( @@ -231,7 +233,7 @@ def get_accessible_captures_queryset(user): A user has access to captures if: 1. They own the capture directly, OR - 2. The capture is shared with them, OR + 2. The capture is shared with them, OR is_public=True OR 3. The capture is part of a dataset that is shared with them Args: @@ -260,6 +262,8 @@ def get_accessible_captures_queryset(user): # 2. Captures directly shared with the user access_query |= Q(uuid__in=captures_shared_with_user) + access_query |= Q(is_public=True) + # 3. Captures part of datasets that are shared with the user # EXPAND: Support both M2M (datasets) and FK (dataset) relationships dataset_shared_query = ( From 5ad9ea18327e8f67d4aa17b9c6d30193a1bb7360 Mon Sep 17 00:00:00 2001 From: Matthew Curran Date: Mon, 24 Aug 2026 15:38:05 -0400 Subject: [PATCH 9/9] gwy: Add File.is_public to return of user_has_access_to_file. --- .../sds_gateway/api_methods/utils/asset_access_control.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/gateway/sds_gateway/api_methods/utils/asset_access_control.py b/gateway/sds_gateway/api_methods/utils/asset_access_control.py index a114664a..f2c989c9 100644 --- a/gateway/sds_gateway/api_methods/utils/asset_access_control.py +++ b/gateway/sds_gateway/api_methods/utils/asset_access_control.py @@ -103,7 +103,12 @@ def user_has_access_to_file(user, file: File) -> bool: for dataset in file_datasets ) - return user_owns_file or user_has_access_to_capture or user_has_access_to_dataset + return ( + user_owns_file + or user_has_access_to_capture + or user_has_access_to_dataset + or file.is_public + ) def get_accessible_files_queryset(user):