diff --git a/src/govoplan_files/backend/db/models.py b/src/govoplan_files/backend/db/models.py index 90e572e..05639f4 100644 --- a/src/govoplan_files/backend/db/models.py +++ b/src/govoplan_files/backend/db/models.py @@ -19,7 +19,7 @@ class FileBlob(Base, TimestampMixin): __table_args__ = (UniqueConstraint("tenant_id", "checksum_sha256", "size_bytes", name="uq_file_blobs_tenant_checksum_size"),) id: Mapped[str] = mapped_column(String(36), primary_key=True, default=new_uuid) - tenant_id: Mapped[str] = mapped_column(ForeignKey("tenancy_tenants.id", ondelete="CASCADE"), nullable=False, index=True) + tenant_id: Mapped[str] = mapped_column(String(36), nullable=False, index=True) storage_backend: Mapped[str] = mapped_column(String(50), nullable=False) storage_bucket: Mapped[str | None] = mapped_column(String(255)) storage_key: Mapped[str] = mapped_column(String(1000), nullable=False) @@ -50,7 +50,7 @@ class FileFolder(Base, TimestampMixin): ) id: Mapped[str] = mapped_column(String(36), primary_key=True, default=new_uuid) - tenant_id: Mapped[str] = mapped_column(ForeignKey("tenancy_tenants.id", ondelete="CASCADE"), nullable=False, index=True) + tenant_id: Mapped[str] = mapped_column(String(36), nullable=False, index=True) owner_type: Mapped[str] = mapped_column(String(20), nullable=False, index=True) owner_user_id: Mapped[str | None] = mapped_column(ForeignKey("access_users.id", ondelete="SET NULL"), nullable=True, index=True) owner_group_id: Mapped[str | None] = mapped_column(ForeignKey("access_groups.id", ondelete="SET NULL"), nullable=True, index=True) @@ -64,7 +64,7 @@ class FileAsset(Base, TimestampMixin): __tablename__ = "file_assets" id: Mapped[str] = mapped_column(String(36), primary_key=True, default=new_uuid) - tenant_id: Mapped[str] = mapped_column(ForeignKey("tenancy_tenants.id", ondelete="CASCADE"), nullable=False, index=True) + tenant_id: Mapped[str] = mapped_column(String(36), nullable=False, index=True) owner_type: Mapped[str] = mapped_column(String(20), nullable=False, index=True) owner_user_id: Mapped[str | None] = mapped_column(ForeignKey("access_users.id", ondelete="SET NULL"), nullable=True, index=True) owner_group_id: Mapped[str | None] = mapped_column(ForeignKey("access_groups.id", ondelete="SET NULL"), nullable=True, index=True) @@ -82,7 +82,7 @@ class FileVersion(Base, TimestampMixin): __table_args__ = (UniqueConstraint("file_asset_id", "version_number", name="uq_file_versions_asset_number"),) id: Mapped[str] = mapped_column(String(36), primary_key=True, default=new_uuid) - tenant_id: Mapped[str] = mapped_column(ForeignKey("tenancy_tenants.id", ondelete="CASCADE"), nullable=False, index=True) + tenant_id: Mapped[str] = mapped_column(String(36), nullable=False, index=True) file_asset_id: Mapped[str] = mapped_column(ForeignKey("file_assets.id", ondelete="CASCADE"), nullable=False, index=True) blob_id: Mapped[str] = mapped_column(ForeignKey("file_blobs.id", ondelete="RESTRICT"), nullable=False, index=True) version_number: Mapped[int] = mapped_column(Integer, nullable=False) @@ -99,7 +99,7 @@ class FileShare(Base, TimestampMixin): __table_args__ = (UniqueConstraint("file_asset_id", "target_type", "target_id", "revoked_at", name="uq_file_shares_active_target"),) id: Mapped[str] = mapped_column(String(36), primary_key=True, default=new_uuid) - tenant_id: Mapped[str] = mapped_column(ForeignKey("tenancy_tenants.id", ondelete="CASCADE"), nullable=False, index=True) + tenant_id: Mapped[str] = mapped_column(String(36), nullable=False, index=True) file_asset_id: Mapped[str] = mapped_column(ForeignKey("file_assets.id", ondelete="CASCADE"), nullable=False, index=True) target_type: Mapped[str] = mapped_column(String(20), nullable=False, index=True) target_id: Mapped[str] = mapped_column(String(36), nullable=False, index=True) @@ -115,7 +115,7 @@ class FileConnectorProfile(Base, TimestampMixin): ) id: Mapped[str] = mapped_column(String(255), primary_key=True) - tenant_id: Mapped[str | None] = mapped_column(ForeignKey("tenancy_tenants.id", ondelete="CASCADE"), nullable=True, index=True) + tenant_id: Mapped[str | None] = mapped_column(String(36), nullable=True, index=True) scope_type: Mapped[str] = mapped_column(String(20), default="tenant", nullable=False, index=True) scope_id: Mapped[str | None] = mapped_column(String(36), nullable=True, index=True) label: Mapped[str] = mapped_column(String(255), nullable=False) @@ -145,7 +145,7 @@ class FileConnectorCredential(Base, TimestampMixin): ) id: Mapped[str] = mapped_column(String(255), primary_key=True) - tenant_id: Mapped[str | None] = mapped_column(ForeignKey("tenancy_tenants.id", ondelete="CASCADE"), nullable=True, index=True) + tenant_id: Mapped[str | None] = mapped_column(String(36), nullable=True, index=True) scope_type: Mapped[str] = mapped_column(String(20), default="tenant", nullable=False, index=True) scope_id: Mapped[str | None] = mapped_column(String(36), nullable=True, index=True) label: Mapped[str] = mapped_column(String(255), nullable=False) @@ -172,7 +172,7 @@ class FileConnectorPolicy(Base, TimestampMixin): ) id: Mapped[str] = mapped_column(String(36), primary_key=True, default=new_uuid) - tenant_id: Mapped[str | None] = mapped_column(ForeignKey("tenancy_tenants.id", ondelete="CASCADE"), nullable=True, index=True) + tenant_id: Mapped[str | None] = mapped_column(String(36), nullable=True, index=True) scope_type: Mapped[str] = mapped_column(String(20), nullable=False, index=True) scope_id: Mapped[str | None] = mapped_column(String(36), nullable=True, index=True) policy: Mapped[dict[str, Any]] = mapped_column(JSON, default=dict, nullable=False) @@ -201,7 +201,7 @@ class FileConnectorSpace(Base, TimestampMixin): ) id: Mapped[str] = mapped_column(String(36), primary_key=True, default=new_uuid) - tenant_id: Mapped[str] = mapped_column(ForeignKey("tenancy_tenants.id", ondelete="CASCADE"), nullable=False, index=True) + tenant_id: Mapped[str] = mapped_column(String(36), nullable=False, index=True) owner_type: Mapped[str] = mapped_column(String(20), nullable=False, index=True) owner_user_id: Mapped[str | None] = mapped_column(ForeignKey("access_users.id", ondelete="SET NULL"), nullable=True, index=True) owner_group_id: Mapped[str | None] = mapped_column(ForeignKey("access_groups.id", ondelete="SET NULL"), nullable=True, index=True) @@ -223,7 +223,7 @@ class CampaignAttachmentUse(Base, TimestampMixin): __table_args__ = (UniqueConstraint("campaign_job_id", "file_version_id", "filename_used", "use_stage", name="uq_campaign_attachment_uses_job_file_stage"),) id: Mapped[str] = mapped_column(String(36), primary_key=True, default=new_uuid) - tenant_id: Mapped[str] = mapped_column(ForeignKey("tenancy_tenants.id", ondelete="CASCADE"), nullable=False, index=True) + tenant_id: Mapped[str] = mapped_column(String(36), nullable=False, index=True) campaign_id: Mapped[str] = mapped_column(ForeignKey("campaigns.id", ondelete="CASCADE"), nullable=False, index=True) campaign_version_id: Mapped[str] = mapped_column(ForeignKey("campaign_versions.id", ondelete="CASCADE"), nullable=False, index=True) campaign_job_id: Mapped[str | None] = mapped_column(ForeignKey("campaign_jobs.id", ondelete="SET NULL"), nullable=True, index=True) diff --git a/src/govoplan_files/backend/manifest.py b/src/govoplan_files/backend/manifest.py index d461992..b47668d 100644 --- a/src/govoplan_files/backend/manifest.py +++ b/src/govoplan_files/backend/manifest.py @@ -2,6 +2,7 @@ from __future__ import annotations from pathlib import Path +from govoplan_core.core.access import CAPABILITY_AUTH_PERMISSION_EVALUATOR, CAPABILITY_AUTH_PRINCIPAL_RESOLVER from govoplan_core.core.module_guards import drop_table_retirement_provider, persistent_table_uninstall_guard from govoplan_core.core.modules import FrontendModule, MigrationSpec, ModuleContext, ModuleManifest, NavItem, PermissionDefinition, RoleTemplate from govoplan_core.db.base import Base @@ -101,7 +102,7 @@ manifest = ModuleManifest( id="files", name="Files", version="0.1.6", - dependencies=("access",), + required_capabilities=(CAPABILITY_AUTH_PRINCIPAL_RESOLVER, CAPABILITY_AUTH_PERMISSION_EVALUATOR), optional_dependencies=(), permissions=PERMISSIONS, route_factory=_files_router, diff --git a/src/govoplan_files/backend/migrations/versions/4f5a6b7c8d9e_file_connector_spaces.py b/src/govoplan_files/backend/migrations/versions/4f5a6b7c8d9e_file_connector_spaces.py index ade96a1..7e61434 100644 --- a/src/govoplan_files/backend/migrations/versions/4f5a6b7c8d9e_file_connector_spaces.py +++ b/src/govoplan_files/backend/migrations/versions/4f5a6b7c8d9e_file_connector_spaces.py @@ -16,9 +16,14 @@ branch_labels = None depends_on = None +def _scope_fk_target(tables: set[str]) -> str: + return "core_scopes.id" if "core_scopes" in tables else "tenancy_tenants.id" + + def upgrade() -> None: inspector = sa.inspect(op.get_bind()) tables = set(inspector.get_table_names()) + scope_fk_target = _scope_fk_target(tables) if "file_connector_spaces" not in tables: op.create_table( "file_connector_spaces", @@ -43,7 +48,7 @@ def upgrade() -> None: sa.ForeignKeyConstraint(["created_by_user_id"], ["access_users.id"], name=op.f("fk_file_connector_spaces_created_by_user_id_users"), ondelete="SET NULL"), sa.ForeignKeyConstraint(["owner_group_id"], ["access_groups.id"], name=op.f("fk_file_connector_spaces_owner_group_id_groups"), ondelete="SET NULL"), sa.ForeignKeyConstraint(["owner_user_id"], ["access_users.id"], name=op.f("fk_file_connector_spaces_owner_user_id_users"), ondelete="SET NULL"), - sa.ForeignKeyConstraint(["tenant_id"], ["tenancy_tenants.id"], name=op.f("fk_file_connector_spaces_tenant_id_tenants"), ondelete="CASCADE"), + sa.ForeignKeyConstraint(["tenant_id"], [scope_fk_target], name=op.f("fk_file_connector_spaces_tenant_id_scopes"), ondelete="CASCADE"), sa.PrimaryKeyConstraint("id", name=op.f("pk_file_connector_spaces")), ) diff --git a/src/govoplan_files/backend/migrations/versions/5a6b7c8d9e0f_file_connector_profiles.py b/src/govoplan_files/backend/migrations/versions/5a6b7c8d9e0f_file_connector_profiles.py index 1172a4c..c2cee8b 100644 --- a/src/govoplan_files/backend/migrations/versions/5a6b7c8d9e0f_file_connector_profiles.py +++ b/src/govoplan_files/backend/migrations/versions/5a6b7c8d9e0f_file_connector_profiles.py @@ -16,8 +16,14 @@ branch_labels = None depends_on = None +def _scope_fk_target(inspector) -> str: + tables = set(inspector.get_table_names()) + return "core_scopes.id" if "core_scopes" in tables else "tenancy_tenants.id" + + def upgrade() -> None: inspector = sa.inspect(op.get_bind()) + scope_fk_target = _scope_fk_target(inspector) if "file_connector_profiles" not in inspector.get_table_names(): op.create_table( "file_connector_profiles", @@ -45,7 +51,7 @@ def upgrade() -> None: sa.Column("created_at", sa.DateTime(timezone=True), nullable=False), sa.Column("updated_at", sa.DateTime(timezone=True), nullable=False), sa.ForeignKeyConstraint(["created_by_user_id"], ["access_users.id"], name=op.f("fk_file_connector_profiles_created_by_user_id_users"), ondelete="SET NULL"), - sa.ForeignKeyConstraint(["tenant_id"], ["tenancy_tenants.id"], name=op.f("fk_file_connector_profiles_tenant_id_tenants"), ondelete="CASCADE"), + sa.ForeignKeyConstraint(["tenant_id"], [scope_fk_target], name=op.f("fk_file_connector_profiles_tenant_id_scopes"), ondelete="CASCADE"), sa.ForeignKeyConstraint(["updated_by_user_id"], ["access_users.id"], name=op.f("fk_file_connector_profiles_updated_by_user_id_users"), ondelete="SET NULL"), sa.PrimaryKeyConstraint("id", name=op.f("pk_file_connector_profiles")), ) diff --git a/src/govoplan_files/backend/migrations/versions/6b7c8d9e0f1a_file_connector_credentials.py b/src/govoplan_files/backend/migrations/versions/6b7c8d9e0f1a_file_connector_credentials.py index 5323454..c3f996a 100644 --- a/src/govoplan_files/backend/migrations/versions/6b7c8d9e0f1a_file_connector_credentials.py +++ b/src/govoplan_files/backend/migrations/versions/6b7c8d9e0f1a_file_connector_credentials.py @@ -16,9 +16,14 @@ branch_labels = None depends_on = None +def _scope_fk_target(table_names: set[str]) -> str: + return "core_scopes.id" if "core_scopes" in table_names else "tenancy_tenants.id" + + def upgrade() -> None: inspector = sa.inspect(op.get_bind()) - table_names = inspector.get_table_names() + table_names = set(inspector.get_table_names()) + scope_fk_target = _scope_fk_target(table_names) if "file_connector_profiles" in table_names: columns = {item["name"] for item in inspector.get_columns("file_connector_profiles")} @@ -53,7 +58,7 @@ def upgrade() -> None: sa.Column("created_at", sa.DateTime(timezone=True), nullable=False), sa.Column("updated_at", sa.DateTime(timezone=True), nullable=False), sa.ForeignKeyConstraint(["created_by_user_id"], ["access_users.id"], name=op.f("fk_file_connector_credentials_created_by_user_id_users"), ondelete="SET NULL"), - sa.ForeignKeyConstraint(["tenant_id"], ["tenancy_tenants.id"], name=op.f("fk_file_connector_credentials_tenant_id_tenants"), ondelete="CASCADE"), + sa.ForeignKeyConstraint(["tenant_id"], [scope_fk_target], name=op.f("fk_file_connector_credentials_tenant_id_scopes"), ondelete="CASCADE"), sa.ForeignKeyConstraint(["updated_by_user_id"], ["access_users.id"], name=op.f("fk_file_connector_credentials_updated_by_user_id_users"), ondelete="SET NULL"), sa.PrimaryKeyConstraint("id", name=op.f("pk_file_connector_credentials")), ) diff --git a/src/govoplan_files/backend/migrations/versions/a7b8c9d0e1f3_file_connector_policies.py b/src/govoplan_files/backend/migrations/versions/a7b8c9d0e1f3_file_connector_policies.py index b66de23..5d332f3 100644 --- a/src/govoplan_files/backend/migrations/versions/a7b8c9d0e1f3_file_connector_policies.py +++ b/src/govoplan_files/backend/migrations/versions/a7b8c9d0e1f3_file_connector_policies.py @@ -16,8 +16,14 @@ branch_labels = None depends_on = None +def _scope_fk_target(inspector) -> str: + tables = set(inspector.get_table_names()) + return "core_scopes.id" if "core_scopes" in tables else "tenancy_tenants.id" + + def upgrade() -> None: inspector = sa.inspect(op.get_bind()) + scope_fk_target = _scope_fk_target(inspector) if "file_connector_policies" not in inspector.get_table_names(): op.create_table( "file_connector_policies", @@ -31,7 +37,7 @@ def upgrade() -> None: sa.Column("created_at", sa.DateTime(timezone=True), nullable=False), sa.Column("updated_at", sa.DateTime(timezone=True), nullable=False), sa.ForeignKeyConstraint(["created_by_user_id"], ["access_users.id"], name=op.f("fk_file_connector_policies_created_by_user_id_users"), ondelete="SET NULL"), - sa.ForeignKeyConstraint(["tenant_id"], ["tenancy_tenants.id"], name=op.f("fk_file_connector_policies_tenant_id_tenants"), ondelete="CASCADE"), + sa.ForeignKeyConstraint(["tenant_id"], [scope_fk_target], name=op.f("fk_file_connector_policies_tenant_id_scopes"), ondelete="CASCADE"), sa.ForeignKeyConstraint(["updated_by_user_id"], ["access_users.id"], name=op.f("fk_file_connector_policies_updated_by_user_id_users"), ondelete="SET NULL"), sa.PrimaryKeyConstraint("id", name=op.f("pk_file_connector_policies")), sa.UniqueConstraint("tenant_id", "scope_type", "scope_id", name="uq_file_connector_policies_scope"), diff --git a/src/govoplan_files/backend/router.py b/src/govoplan_files/backend/router.py index 3d8af5e..ae4754c 100644 --- a/src/govoplan_files/backend/router.py +++ b/src/govoplan_files/backend/router.py @@ -14,7 +14,7 @@ from starlette.background import BackgroundTask from sqlalchemy import func, or_ from sqlalchemy.orm import Session -from govoplan_access.auth import ApiPrincipal, has_scope, require_any_scope, require_scope +from govoplan_core.auth import ApiPrincipal, has_scope, require_any_scope, require_scope from govoplan_core.api.v1.schemas import DeltaDeletedItem from govoplan_core.audit.logging import audit_from_principal from govoplan_core.core.campaigns import CAPABILITY_CAMPAIGNS_ACCESS, CampaignAccessProvider