from __future__ import annotations from dataclasses import dataclass from importlib.util import find_spec CONNECTOR_PROVIDER_CAPABILITY = "files.connector.providers" @dataclass(frozen=True, slots=True) class ConnectorProviderDescriptor: provider: str label: str protocol: str implemented: bool browse_supported: bool import_supported: bool optional_dependency: str | None permission_model: str sync_strategy: str conflict_strategy: str preview_strategy: str audit_events: tuple[str, ...] notes: str | None = None def to_response(self) -> dict[str, object]: return { "provider": self.provider, "label": self.label, "protocol": self.protocol, "implemented": self.implemented, "installed": self.installed, "browse_supported": self.browse_supported, "import_supported": self.import_supported, "optional_dependency": self.optional_dependency, "permission_model": self.permission_model, "sync_strategy": self.sync_strategy, "conflict_strategy": self.conflict_strategy, "preview_strategy": self.preview_strategy, "audit_events": list(self.audit_events), "notes": self.notes, } @property def installed(self) -> bool: if self.optional_dependency is None: return self.implemented return find_spec(self.optional_dependency) is not None def connector_provider_descriptors() -> tuple[ConnectorProviderDescriptor, ...]: freeze_model = "Imported or synced connector files become managed GovOPlaN files with frozen blob/version provenance." governed_permissions = "GovOPlaN profile scope and connector policy are enforced before browse/import/sync; remote ACLs are still evaluated by the upstream service." return ( ConnectorProviderDescriptor( provider="seafile", label="Seafile", protocol="seafile-api", implemented=True, browse_supported=True, import_supported=True, optional_dependency=None, permission_model=governed_permissions, sync_strategy="On-demand browse/import/sync; sync updates the managed file version when source content changes and never mutates the remote source.", conflict_strategy="Managed import/sync uses existing files conflict_strategy handling: reject, overwrite, or rename.", preview_strategy="Previews are generated from the frozen managed file after import or sync.", audit_events=("files.connector.imported", "files.connector.synced", "files.connector.accessed"), notes="Native account-token API for libraries, directories, file detail, and download-link import; WebDAV opt-in remains available.", ), ConnectorProviderDescriptor( provider="nextcloud", label="Nextcloud", protocol="webdav", implemented=True, browse_supported=True, import_supported=True, optional_dependency=None, permission_model=governed_permissions, sync_strategy="On-demand WebDAV browse/import/sync; sync updates the managed file version when source content changes and never mutates the remote source.", conflict_strategy="Managed import/sync uses existing files conflict_strategy handling: reject, overwrite, or rename.", preview_strategy="Previews are generated from the frozen managed file after import or sync.", audit_events=("files.connector.imported", "files.connector.synced", "files.connector.accessed"), notes="Uses the configured Nextcloud WebDAV endpoint, basic auth, or bearer token profile credentials.", ), ConnectorProviderDescriptor( provider="webdav", label="WebDAV", protocol="webdav", implemented=True, browse_supported=True, import_supported=True, optional_dependency=None, permission_model=governed_permissions, sync_strategy="On-demand WebDAV browse/import/sync; sync updates the managed file version when source content changes and never mutates the remote source.", conflict_strategy="Managed import/sync uses existing files conflict_strategy handling: reject, overwrite, or rename.", preview_strategy="Previews are generated from the frozen managed file after import or sync.", audit_events=("files.connector.imported", "files.connector.synced", "files.connector.accessed"), notes="Generic WebDAV provider for standards-compatible stores.", ), ConnectorProviderDescriptor( provider="smb", label="SMB", protocol="smb", implemented=True, browse_supported=True, import_supported=True, optional_dependency="smbprotocol", permission_model=governed_permissions, sync_strategy="On-demand browse/import/sync over administrator-declared shares; sync updates managed versions and never mutates the remote share.", conflict_strategy="Managed import/sync uses existing files conflict_strategy handling after download.", preview_strategy="Previews are generated from the frozen managed file after import or sync, not directly from the share.", audit_events=("files.connector.imported", "files.connector.synced", "files.connector.accessed"), notes="Requires the optional smbprotocol dependency and an smb://server[:port]/share[/path] profile endpoint.", ), ConnectorProviderDescriptor( provider="nfs", label="NFS", protocol="nfs", implemented=False, browse_supported=False, import_supported=False, optional_dependency=None, permission_model=governed_permissions, sync_strategy="Planned optional provider over administrator-mounted paths or a sidecar service.", conflict_strategy="Will use managed import conflict_strategy handling after download.", preview_strategy="Will preview from frozen managed file after import, not directly from the mount.", audit_events=("files.connector.imported", "files.connector.synced", "files.connector.accessed"), notes="Kept optional because NFS availability is deployment and host-kernel dependent.", ), ConnectorProviderDescriptor( provider="local", label="Local filesystem", protocol="file", implemented=False, browse_supported=False, import_supported=False, optional_dependency=None, permission_model="Local connector access should be restricted to administrator-declared roots plus GovOPlaN profile and policy checks.", sync_strategy="Planned optional provider; managed storage local backend already exists separately.", conflict_strategy=freeze_model, preview_strategy="Will preview from frozen managed file after import or sync.", audit_events=("files.connector.imported", "files.connector.synced", "files.connector.accessed"), notes="Separate from the existing managed-file local storage backend.", ), )