feat(projects): add governed DSAR coverage

This commit is contained in:
2026-08-21 13:28:07 +02:00
parent 4bebb4211a
commit de8ec81c47
3 changed files with 728 additions and 2 deletions
+59 -2
View File
@@ -37,6 +37,10 @@ from govoplan_core.core.views import ViewSurface
from govoplan_core.db.base import Base
from govoplan_projects.backend.acl import ProjectScopeAclProvider
from govoplan_projects.backend.db import models as project_models
from govoplan_projects.backend.dsar_provider import (
PROJECTS_DSAR_CAPABILITY,
ProjectsDsarProvider,
)
from govoplan_projects.backend.search_source import create_projects_search_source
from govoplan_projects.backend.service import (
CAPABILITY_PROJECTS_REGISTRY,
@@ -130,6 +134,10 @@ def _registry(context: ModuleContext) -> SqlProjectRegistry:
return SqlProjectRegistry()
def _dsar_provider(_context: ModuleContext) -> ProjectsDsarProvider:
return ProjectsDsarProvider()
def _tenant_summary(session, tenant_id: str) -> dict[str, int]:
counts = {
kind: count
@@ -339,8 +347,12 @@ manifest = ModuleManifest(
),
provides_interfaces=(
ModuleInterfaceProvider(name="projects.registry", version="0.1.0"),
ModuleInterfaceProvider(name=PROJECTS_DSAR_CAPABILITY, version="0.1.0"),
),
capability_factories={CAPABILITY_PROJECTS_REGISTRY: _registry},
capability_factories={
CAPABILITY_PROJECTS_REGISTRY: _registry,
PROJECTS_DSAR_CAPABILITY: _dsar_provider,
},
capability_documentation={
CAPABILITY_PROJECTS_REGISTRY: CapabilityDocumentation(
label="Projects registry",
@@ -350,6 +362,14 @@ manifest = ModuleManifest(
),
contract_version="0.1.0",
),
PROJECTS_DSAR_CAPABILITY: CapabilityDocumentation(
label="Projects data-subject request provider",
summary=(
"Exports exact project memberships and minimized lifecycle attribution "
"without project payload content."
),
contract_version="0.1.0",
),
},
search_sources=(
SearchSourceProviderRegistration(
@@ -389,7 +409,44 @@ manifest = ModuleManifest(
ProjectScopeAclProvider("milestone"),
),
tenant_summary_providers=(_tenant_summary,),
documentation=DOCUMENTATION,
documentation=(
DocumentationTopic(
id="projects.data-subject-requests",
title="Project data-subject requests",
summary=(
"Export project participation and accountable activity without project content."
),
body=(
"Projects correlates exact account and identity membership grants, plus "
"an explicitly supplied function-assignment reference. It separately "
"matches exact account, identity, and membership identifiers used for "
"creation and lifecycle attribution. Searches can narrow to one project, "
"portfolio, or milestone, but an object identifier alone never establishes "
"a subject. Membership exports include role, permissions, active state, "
"and source revision. Titles, search text, planning payloads, event payloads, "
"request hashes, idempotency keys, and unrelated members are excluded. "
"Membership removal requires manual access and ownership review; immutable "
"creation, revision, and event attribution remains retained governance evidence."
),
layer="configured",
documentation_types=("admin", "user"),
audience=("user", "operator", "module_admin", "auditor"),
related_modules=("core", "access", "tasks", "audit"),
order=90,
metadata={
"help_contexts": [
"projects.workspace",
"privacy.data-subject-requests",
],
"consequence_classes": {
"export_project_membership": "Returns exact subject grants without unrelated members.",
"review_membership_removal": "Requires ownership and access-continuity review.",
"retain_project_attribution": "Preserves immutable lifecycle accountability.",
},
},
),
*DOCUMENTATION,
),
architecture=ARCHITECTURE,
)