feat(postbox): enforce unified inbox separation

This commit is contained in:
2026-08-20 04:22:29 +02:00
parent 174ee97719
commit 41ea8d8e23
16 changed files with 1029 additions and 25 deletions
+35
View File
@@ -5,6 +5,7 @@ from typing import Any, Literal
from pydantic import BaseModel, Field, model_validator
from govoplan_postbox.backend.grouping_policies import PostboxGroupingPolicyMode
from govoplan_postbox.backend.protection_profiles import (
POSTBOX_MANAGED_ENVELOPE_PROFILE,
POSTBOX_PLAINTEXT_PROFILE,
@@ -58,6 +59,7 @@ class PostboxDirectoryItem(BaseModel):
key_epoch: int = Field(default=1, ge=1)
encryption_vault_id: str | None = None
protection_policy: dict[str, Any] = Field(default_factory=dict)
grouping_policy: dict[str, Any] = Field(default_factory=dict)
access: PostboxAccessDecisionResponse | None = None
resource_revision: int = Field(default=1, ge=1)
etag: str | None = None
@@ -465,6 +467,16 @@ class PostboxProtectionPolicyPayload(BaseModel):
return self
class PostboxGroupingPolicyPayload(BaseModel):
mode: PostboxGroupingPolicyMode = "allow"
reason: str | None = Field(default=None, max_length=1000)
@model_validator(mode="after")
def normalize_reason(self) -> "PostboxGroupingPolicyPayload":
self.reason = self.reason.strip() if self.reason else None
return self
class PostboxExactCreateRequest(BaseModel):
name: str = Field(min_length=1, max_length=500)
description: str | None = None
@@ -478,6 +490,9 @@ class PostboxExactCreateRequest(BaseModel):
protection_policy: PostboxProtectionPolicyPayload = Field(
default_factory=PostboxProtectionPolicyPayload
)
grouping_policy: PostboxGroupingPolicyPayload = Field(
default_factory=PostboxGroupingPolicyPayload
)
@model_validator(mode="after")
def validate_encryption(self) -> "PostboxExactCreateRequest":
@@ -498,6 +513,11 @@ class PostboxProtectionPolicyUpdateRequest(BaseModel):
protection_policy: PostboxProtectionPolicyPayload
class PostboxGroupingPolicyUpdateRequest(BaseModel):
base_revision: int = Field(ge=1)
grouping_policy: PostboxGroupingPolicyPayload
class PostboxTemplateRevisionPayload(BaseModel):
function_type_id: str | None = Field(default=None, max_length=36)
scope_kind: Literal["tenant", "unit", "subtree", "unit_type"] = "tenant"
@@ -522,6 +542,9 @@ class PostboxTemplateRevisionPayload(BaseModel):
protection_policy: PostboxProtectionPolicyPayload = Field(
default_factory=PostboxProtectionPolicyPayload
)
grouping_policy: PostboxGroupingPolicyPayload = Field(
default_factory=PostboxGroupingPolicyPayload
)
routing_policy: PostboxRoutingPolicyPayload = Field(
default_factory=PostboxRoutingPolicyPayload
)
@@ -861,10 +884,22 @@ class PostboxGroupingUpdateRequest(PostboxGroupingPayload):
base_revision: int = Field(ge=1)
class PostboxGroupingConstraintItem(BaseModel):
code: Literal[
"source_requires_separation",
"classification_separation_required",
]
mode: PostboxGroupingPolicyMode
postbox_id: str
reason: str | None = None
enforced_by: Literal["postbox_configuration"] = "postbox_configuration"
class PostboxGroupingItem(PostboxGroupingPayload):
id: str
total_count: int = Field(default=0, ge=0)
unread_count: int = Field(default=0, ge=0)
constraints: list[PostboxGroupingConstraintItem] = Field(default_factory=list)
resource_revision: int = Field(ge=1)
etag: str
created_at: datetime