Prepare v0.1.0 release

This commit is contained in:
2026-06-24 20:00:38 +02:00
parent a9d16a2c89
commit 97015e5a01
10 changed files with 409 additions and 79 deletions

View File

@@ -198,6 +198,40 @@ def _policy_from_attr(value: Any) -> dict[str, Any]:
return normalize_mail_profile_policy(value if isinstance(value, dict) else {})
def _mail_policy_source_fields(policy: dict[str, Any], *, baseline: bool = False) -> list[str]:
fields: list[str] = []
if _meaningful_allow_patterns(policy.get("allowed_profile_ids") or []):
fields.append("allowed_profile_ids")
for key in ("allow_user_profiles", "allow_group_profiles", "allow_campaign_profiles"):
if isinstance(policy.get(key), bool):
fields.append(key)
for key in ("smtp_credentials", "imap_credentials"):
credential = policy.get(key) or {}
if isinstance(credential, dict):
if isinstance(credential.get("inherit"), bool):
fields.append(f"{key}.inherit")
if isinstance(credential.get("allow_override"), bool):
fields.append(f"{key}.allow_override")
for kind in ("whitelist", "blacklist"):
rules = policy.get(kind) or {}
if isinstance(rules, dict):
for key in PROFILE_PATTERN_KEYS:
if _clean_string_list(rules.get(key, [])):
fields.append(f"{kind}.{key}")
if baseline and not fields:
fields.append("defaults")
return fields
def _mail_policy_source_step(source: str, source_id: str | None, label: str, policy: dict[str, Any], *, baseline: bool = False) -> dict[str, Any]:
return {
"scope_type": source,
"scope_id": source_id,
"label": label,
"applied_fields": _mail_policy_source_fields(policy, baseline=baseline),
}
def _merge_credential_policy(current: EffectiveCredentialPolicy, data: dict[str, Any], *, source: str) -> None:
inherit = data.get("inherit")
if isinstance(inherit, bool):
@@ -216,9 +250,9 @@ def _merge_credential_policy(current: EffectiveCredentialPolicy, data: dict[str,
current.allow_override = False
def _merge_policy(policy: EffectiveMailProfilePolicy, data: dict[str, Any], *, source: str) -> None:
def _merge_policy(policy: EffectiveMailProfilePolicy, data: dict[str, Any], *, source: str, source_id: str | None = None, label: str | None = None, baseline: bool = False) -> None:
normalized = normalize_mail_profile_policy(data)
policy.source_policies.append(normalized)
policy.source_policies.append(_mail_policy_source_step(source, source_id, label or source.capitalize(), normalized, baseline=baseline))
allowed_ids = _meaningful_allow_patterns(normalized.get("allowed_profile_ids") or [])
if allowed_ids:
policy.allowed_profile_id_sets.append(set(allowed_ids))
@@ -280,18 +314,18 @@ def effective_mail_profile_policy(
policy = EffectiveMailProfilePolicy()
system_settings = get_system_settings(session)
_merge_policy(policy, _policy_from_settings(system_settings.settings or {}), source="system")
_merge_policy(policy, _policy_from_settings(tenant.settings or {}), source="tenant")
_merge_policy(policy, _policy_from_settings(system_settings.settings or {}), source="system", label="System", baseline=True)
_merge_policy(policy, _policy_from_settings(tenant.settings or {}), source="tenant", source_id=tenant.id, label="Tenant")
if owner_user_id:
user = session.get(User, owner_user_id)
if user and user.tenant_id == tenant_id:
_merge_policy(policy, _policy_from_attr(user.mail_profile_policy), source="user")
_merge_policy(policy, _policy_from_attr(user.mail_profile_policy), source="user", source_id=user.id, label="Owner user")
if owner_group_id:
group = session.get(Group, owner_group_id)
if group and group.tenant_id == tenant_id:
_merge_policy(policy, _policy_from_attr(group.mail_profile_policy), source="group")
_merge_policy(policy, _policy_from_attr(group.mail_profile_policy), source="group", source_id=group.id, label="Owner group")
if campaign is not None:
_merge_policy(policy, _policy_from_attr(campaign.mail_profile_policy), source="campaign")
_merge_policy(policy, _policy_from_attr(campaign.mail_profile_policy), source="campaign", source_id=campaign.id, label="Campaign")
return policy
@@ -309,11 +343,11 @@ def parent_mail_profile_policy(
policy = EffectiveMailProfilePolicy()
system_settings = get_system_settings(session)
_merge_policy(policy, _policy_from_settings(system_settings.settings or {}), source="system")
_merge_policy(policy, _policy_from_settings(system_settings.settings or {}), source="system", label="System", baseline=True)
if clean_scope == "tenant":
return policy
_merge_policy(policy, _policy_from_settings(tenant.settings or {}), source="tenant")
_merge_policy(policy, _policy_from_settings(tenant.settings or {}), source="tenant", source_id=tenant.id, label="Tenant")
if clean_scope in {"user", "group"}:
return policy
@@ -325,11 +359,11 @@ def parent_mail_profile_policy(
if campaign.owner_user_id:
user = session.get(User, campaign.owner_user_id)
if user and user.tenant_id == tenant_id:
_merge_policy(policy, _policy_from_attr(user.mail_profile_policy), source="user")
_merge_policy(policy, _policy_from_attr(user.mail_profile_policy), source="user", source_id=user.id, label="Owner user")
if campaign.owner_group_id:
group = session.get(Group, campaign.owner_group_id)
if group and group.tenant_id == tenant_id:
_merge_policy(policy, _policy_from_attr(group.mail_profile_policy), source="group")
_merge_policy(policy, _policy_from_attr(group.mail_profile_policy), source="group", source_id=group.id, label="Owner group")
return policy
@@ -454,7 +488,7 @@ def _ensure_scope_allows_profile_creation(
if scope_type == "campaign":
policy = effective_mail_profile_policy(session, tenant_id=tenant_id, campaign_id=scope_id)
if not policy.allow_campaign_profiles:
raise MailProfileError("Campaign-scoped mail-server profiles are disabled by policy")
raise MailProfileError("Campaign-local mail profiles are disabled by policy")
return
if scope_type == "user":
policy = effective_mail_profile_policy(session, tenant_id=tenant_id, owner_user_id=scope_id)
@@ -694,6 +728,17 @@ def _assert_profile_credentials_allowed_for_server(profile: MailServerProfile, p
raise MailProfileError(f"{protocol.upper()} credentials must be supplied by this campaign or scope policy")
def _inline_transport_configured(value: Any) -> bool:
if not isinstance(value, dict):
return False
if value.get("enabled") is True:
return True
for key in ("host", "username", "password"):
if str(value.get(key) or "").strip():
return True
return False
def assert_campaign_mail_policy_allows_json(
session: Session,
*,
@@ -737,6 +782,12 @@ def assert_campaign_mail_policy_allows_json(
return
smtp = server.get("smtp") if isinstance(server, dict) and isinstance(server.get("smtp"), dict) else None
imap = server.get("imap") if isinstance(server, dict) and isinstance(server.get("imap"), dict) else None
if campaign_id:
policy = effective_mail_profile_policy(session, tenant_id=tenant_id, campaign_id=campaign_id)
if not policy.allow_campaign_profiles and (_inline_transport_configured(smtp) or _inline_transport_configured(imap)):
raise MailProfileError("Campaign-local inline mail settings are disabled by policy")
_assert_transport_values_allowed(policy, smtp, imap)
return
assert_mail_policy_allows_transport(
session,
tenant_id=tenant_id,