feat: enforce custom appearance policy
This commit is contained in:
@@ -1013,7 +1013,8 @@ def update_profile(
|
|||||||
next_settings["ui"] = UserUiPreferences().model_dump()
|
next_settings["ui"] = UserUiPreferences().model_dump()
|
||||||
else:
|
else:
|
||||||
appearance = _effective_appearance_info(session, tenant=context.tenant, user=context.user)
|
appearance = _effective_appearance_info(session, tenant=context.tenant, user=context.user)
|
||||||
stored_palette = _user_ui_preferences(context.user.settings).palette
|
stored_preferences = _user_ui_preferences(context.user.settings)
|
||||||
|
stored_palette = stored_preferences.palette
|
||||||
if (
|
if (
|
||||||
appearance.locked
|
appearance.locked
|
||||||
and payload.ui_preferences.palette is not None
|
and payload.ui_preferences.palette is not None
|
||||||
@@ -1023,6 +1024,17 @@ def update_profile(
|
|||||||
status_code=status.HTTP_422_UNPROCESSABLE_CONTENT,
|
status_code=status.HTTP_422_UNPROCESSABLE_CONTENT,
|
||||||
detail="The effective appearance policy locks the palette.",
|
detail="The effective appearance policy locks the palette.",
|
||||||
)
|
)
|
||||||
|
requested_overrides = payload.ui_preferences.appearance_overrides
|
||||||
|
if (
|
||||||
|
"appearance_overrides" in payload.ui_preferences.model_fields_set
|
||||||
|
and not appearance.custom_overrides_allowed
|
||||||
|
and requested_overrides is not None
|
||||||
|
and requested_overrides != stored_preferences.appearance_overrides
|
||||||
|
):
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_422_UNPROCESSABLE_CONTENT,
|
||||||
|
detail="The effective appearance policy does not allow personal custom overrides.",
|
||||||
|
)
|
||||||
if (
|
if (
|
||||||
payload.ui_preferences.navigation is not None
|
payload.ui_preferences.navigation is not None
|
||||||
and payload.ui_preferences.navigation.locked
|
and payload.ui_preferences.navigation.locked
|
||||||
|
|||||||
@@ -304,13 +304,18 @@ ACCESS_DOCUMENTATION: tuple[DocumentationTopic, ...] = (
|
|||||||
DocumentationTopic(
|
DocumentationTopic(
|
||||||
id="access.reference.effective-appearance",
|
id="access.reference.effective-appearance",
|
||||||
title="Understand the effective appearance source",
|
title="Understand the effective appearance source",
|
||||||
summary="The authenticated profile explains whether the active palette comes from the user, tenant, system, or a policy lock.",
|
summary="The authenticated profile explains the effective palette, policy lock, and governed personal token overrides.",
|
||||||
body=(
|
body=(
|
||||||
"An explicit personal palette normally wins over tenant and system defaults. "
|
"An explicit personal palette normally wins over tenant and system defaults. "
|
||||||
"Resetting it stores inheritance, not a copy of the current default. A tenant "
|
"Resetting it stores inheritance, not a copy of the current default. A tenant "
|
||||||
"policy lock suppresses personal choices; a system lock suppresses both tenant "
|
"policy lock suppresses personal choices; a system lock suppresses both tenant "
|
||||||
"and personal choices. Access enforces the lock during profile writes and returns "
|
"and personal choices. Access enforces the lock during profile writes and returns "
|
||||||
"the effective palette, source, inherited value, and lock state on full profile responses. "
|
"the effective palette, source, inherited value, and lock state on full profile responses. "
|
||||||
|
"Advanced accent, surface, and status overrides are accepted only when the system opts in, "
|
||||||
|
"the tenant does not block them, and neither palette scope is locked. Both light and dark "
|
||||||
|
"documents are versioned and validated atomically for hexadecimal values, WCAG AA paired "
|
||||||
|
"contrast, and distinct status colors. Invalid or disallowed documents are never partially "
|
||||||
|
"applied; users may still remove an inactive stored override to return to inheritance."
|
||||||
),
|
),
|
||||||
layer="always",
|
layer="always",
|
||||||
documentation_types=("admin", "user"),
|
documentation_types=("admin", "user"),
|
||||||
|
|||||||
Reference in New Issue
Block a user