feat(cases): enforce purpose-bound case governance
Module Package Release / publish-packages (push) Successful in 12s
Module Package Release / publish-packages (push) Successful in 12s
This commit is contained in:
@@ -17,15 +17,19 @@ CASE_ACCESS_SUBJECT_KINDS = frozenset(
|
||||
{
|
||||
"account",
|
||||
"identity",
|
||||
"membership",
|
||||
"group",
|
||||
"role",
|
||||
"function",
|
||||
"function_assignment",
|
||||
"organization_unit",
|
||||
"service_account",
|
||||
"delegation",
|
||||
}
|
||||
)
|
||||
CASE_ACCESS_PERMISSIONS = frozenset({"read", "update", "share", "admin"})
|
||||
DEFAULT_CASE_ACCESS_PURPOSE = "cases.casework"
|
||||
MAX_CASE_ACCESS_PURPOSES = 100
|
||||
|
||||
|
||||
@dataclass(frozen=True, slots=True)
|
||||
@@ -33,6 +37,7 @@ class CaseGrant:
|
||||
subject_kind: str
|
||||
subject_id: str
|
||||
permissions: tuple[str, ...] = ("read",)
|
||||
allowed_purposes: tuple[str, ...] = (DEFAULT_CASE_ACCESS_PURPOSE,)
|
||||
|
||||
def __post_init__(self) -> None:
|
||||
if self.subject_kind not in CASE_ACCESS_SUBJECT_KINDS:
|
||||
@@ -43,12 +48,29 @@ class CaseGrant:
|
||||
if not normalized or set(normalized) - CASE_ACCESS_PERMISSIONS:
|
||||
raise InstitutionalContextError("Case access permissions are invalid.")
|
||||
object.__setattr__(self, "permissions", normalized)
|
||||
purposes = tuple(
|
||||
dict.fromkeys(
|
||||
str(value).strip() for value in self.allowed_purposes if str(value).strip()
|
||||
)
|
||||
)
|
||||
if not purposes:
|
||||
raise InstitutionalContextError(
|
||||
"Case access grants require at least one allowed purpose."
|
||||
)
|
||||
if len(purposes) > MAX_CASE_ACCESS_PURPOSES or any(
|
||||
len(value) > 255 for value in purposes
|
||||
):
|
||||
raise InstitutionalContextError(
|
||||
"Case access grant purposes exceed the supported bounds."
|
||||
)
|
||||
object.__setattr__(self, "allowed_purposes", purposes)
|
||||
|
||||
def to_dict(self) -> dict[str, object]:
|
||||
return {
|
||||
"subject_kind": self.subject_kind,
|
||||
"subject_id": self.subject_id,
|
||||
"permissions": list(self.permissions),
|
||||
"allowed_purposes": list(self.allowed_purposes),
|
||||
}
|
||||
|
||||
@classmethod
|
||||
@@ -58,10 +80,18 @@ class CaseGrant:
|
||||
raise InstitutionalContextError(
|
||||
"Case access permissions must be a list."
|
||||
)
|
||||
raw_purposes = value.get(
|
||||
"allowed_purposes", (DEFAULT_CASE_ACCESS_PURPOSE,)
|
||||
)
|
||||
if not isinstance(raw_purposes, (list, tuple)):
|
||||
raise InstitutionalContextError(
|
||||
"Case access purposes must be a list."
|
||||
)
|
||||
return cls(
|
||||
subject_kind=_text(value, "subject_kind"),
|
||||
subject_id=_text(value, "subject_id"),
|
||||
permissions=tuple(str(item) for item in raw_permissions),
|
||||
allowed_purposes=tuple(str(item) for item in raw_purposes),
|
||||
)
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user