Fix PostgreSQL file visibility queries

This commit is contained in:
2026-08-04 01:04:39 +02:00
parent d8ae506ff8
commit 04882f1628
2 changed files with 34 additions and 6 deletions
+16 -6
View File
@@ -596,21 +596,31 @@ def _asset_visibility_query_for_user(
if owner_type == "group" and owner_id: if owner_type == "group" and owner_id:
query = query.filter(FileAsset.owner_group_id == owner_id) query = query.filter(FileAsset.owner_group_id == owner_id)
if campaign_id: if campaign_id:
query = query.join(FileShare, FileShare.file_asset_id == FileAsset.id).filter( campaign_share = exists().where(
FileShare.tenant_id == tenant_id, FileShare.tenant_id == tenant_id,
FileShare.file_asset_id == FileAsset.id,
FileShare.target_type == "campaign", FileShare.target_type == "campaign",
FileShare.target_id == campaign_id, FileShare.target_id == campaign_id,
effective_file_share_clause(), effective_file_share_clause(),
) )
query = query.filter(campaign_share)
elif not is_admin and not owner_type: elif not is_admin and not owner_type:
group_ids = user_group_ids(session, tenant_id=tenant_id, user_id=user_id) group_ids = user_group_ids(session, tenant_id=tenant_id, user_id=user_id)
query = query.outerjoin(FileShare, FileShare.file_asset_id == FileAsset.id).filter( active_share = exists().where(
FileShare.tenant_id == tenant_id,
FileShare.file_asset_id == FileAsset.id,
effective_file_share_clause(),
or_(
(FileShare.target_type == "user") & (FileShare.target_id == user_id),
(FileShare.target_type == "group") & (FileShare.target_id.in_(group_ids)),
(FileShare.target_type == "tenant") & (FileShare.target_id == tenant_id),
)
)
query = query.filter(
or_( or_(
(FileAsset.owner_type == "user") & (FileAsset.owner_user_id == user_id), (FileAsset.owner_type == "user") & (FileAsset.owner_user_id == user_id),
(FileAsset.owner_type == "group") & (FileAsset.owner_group_id.in_(group_ids)), (FileAsset.owner_type == "group") & (FileAsset.owner_group_id.in_(group_ids)),
effective_file_share_clause() & (FileShare.target_type == "user") & (FileShare.target_id == user_id), active_share,
effective_file_share_clause() & (FileShare.target_type == "group") & (FileShare.target_id.in_(group_ids)),
effective_file_share_clause() & (FileShare.target_type == "tenant") & (FileShare.target_id == tenant_id),
) )
) )
if path_prefix: if path_prefix:
@@ -639,7 +649,7 @@ def _asset_visibility_query_for_user(
CampaignAttachmentUse.use_stage == "sent", CampaignAttachmentUse.use_stage == "sent",
) )
query = query.filter(sent_use_exists if audit_relevant else ~sent_use_exists) query = query.filter(sent_use_exists if audit_relevant else ~sent_use_exists)
return query.distinct() return query
def count_assets_for_user( def count_assets_for_user(
+18
View File
@@ -5,6 +5,7 @@ from datetime import timedelta
from unittest.mock import patch from unittest.mock import patch
from sqlalchemy import create_engine from sqlalchemy import create_engine
from sqlalchemy.dialects import postgresql
from sqlalchemy.orm import sessionmaker from sqlalchemy.orm import sessionmaker
from govoplan_access.backend.db.models import Account, Group, User from govoplan_access.backend.db.models import Account, Group, User
@@ -13,6 +14,7 @@ from govoplan_core.db.base import Base
from govoplan_files.backend.db.models import FileAsset, FileShare from govoplan_files.backend.db.models import FileAsset, FileShare
from govoplan_files.backend.storage.common import FileStorageError, utcnow from govoplan_files.backend.storage.common import FileStorageError, utcnow
from govoplan_files.backend.storage.files import ( from govoplan_files.backend.storage.files import (
_asset_visibility_query_for_user,
get_asset_for_user, get_asset_for_user,
list_file_shares, list_file_shares,
revoke_file_share, revoke_file_share,
@@ -214,6 +216,22 @@ class FileShareLifecycleTests(unittest.TestCase):
expires_at=utcnow() - timedelta(seconds=1), expires_at=utcnow() - timedelta(seconds=1),
) )
@patch(
"govoplan_files.backend.storage.files.user_group_ids",
return_value=[],
)
def test_visibility_query_is_postgresql_json_safe(self, _groups) -> None:
query = _asset_visibility_query_for_user(
self.session,
tenant_id=TENANT_ID,
user_id=RECIPIENT_ID,
)
compiled = str(query.statement.compile(dialect=postgresql.dialect()))
self.assertNotIn("SELECT DISTINCT", compiled.upper())
self.assertIn("EXISTS", compiled.upper())
if __name__ == "__main__": if __name__ == "__main__":
unittest.main() unittest.main()