|
|
|
|
@@ -2,17 +2,23 @@ from __future__ import annotations
|
|
|
|
|
|
|
|
|
|
import copy
|
|
|
|
|
import fnmatch
|
|
|
|
|
import json
|
|
|
|
|
import re
|
|
|
|
|
from dataclasses import dataclass, field
|
|
|
|
|
from typing import Any, Iterable
|
|
|
|
|
|
|
|
|
|
from sqlalchemy import and_, or_
|
|
|
|
|
from sqlalchemy import and_, or_, text
|
|
|
|
|
from sqlalchemy.orm import Session
|
|
|
|
|
|
|
|
|
|
from govoplan_core.admin.governance import get_system_settings
|
|
|
|
|
from govoplan_core.core.optional import reraise_unless_missing_package
|
|
|
|
|
from govoplan_core.db.models import Group, Tenant, User, UserGroupMembership
|
|
|
|
|
from govoplan_mail.backend.db.models import MailServerProfile
|
|
|
|
|
from govoplan_core.admin.settings import get_system_settings
|
|
|
|
|
from govoplan_core.core.access import CAPABILITY_ACCESS_DIRECTORY, AccessDirectory
|
|
|
|
|
from govoplan_core.core.campaigns import (
|
|
|
|
|
CAPABILITY_CAMPAIGNS_MAIL_POLICY_CONTEXT,
|
|
|
|
|
CampaignMailPolicyContext,
|
|
|
|
|
CampaignMailPolicyContextProvider,
|
|
|
|
|
)
|
|
|
|
|
from govoplan_core.core.runtime import get_registry
|
|
|
|
|
from govoplan_mail.backend.db.models import MailProfilePolicy, MailServerProfile
|
|
|
|
|
from govoplan_mail.backend.config import ImapConfig, SmtpConfig
|
|
|
|
|
from govoplan_core.security.secrets import decrypt_secret, encrypt_secret
|
|
|
|
|
|
|
|
|
|
@@ -49,15 +55,6 @@ def default_mail_policy_lower_level_limits() -> dict[str, bool]:
|
|
|
|
|
return {key: True for key in MAIL_POLICY_LIMIT_KEYS}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _campaign_model():
|
|
|
|
|
try:
|
|
|
|
|
from govoplan_campaign.backend.db.models import Campaign
|
|
|
|
|
except ModuleNotFoundError as exc:
|
|
|
|
|
reraise_unless_missing_package(exc, "govoplan_campaign")
|
|
|
|
|
raise MailProfileError("Campaign module is not installed") from exc
|
|
|
|
|
return Campaign
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@dataclass(slots=True)
|
|
|
|
|
class EffectiveCredentialPolicy:
|
|
|
|
|
inherit: bool = True
|
|
|
|
|
@@ -240,6 +237,174 @@ def _policy_from_attr(value: Any) -> dict[str, Any]:
|
|
|
|
|
return normalize_mail_profile_policy(value if isinstance(value, dict) else {})
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _json_object(value: Any) -> dict[str, Any]:
|
|
|
|
|
if isinstance(value, dict):
|
|
|
|
|
return value
|
|
|
|
|
if isinstance(value, str) and value.strip():
|
|
|
|
|
try:
|
|
|
|
|
parsed = json.loads(value)
|
|
|
|
|
except json.JSONDecodeError:
|
|
|
|
|
return {}
|
|
|
|
|
return parsed if isinstance(parsed, dict) else {}
|
|
|
|
|
return {}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _access_directory() -> AccessDirectory | None:
|
|
|
|
|
registry = get_registry()
|
|
|
|
|
if registry is None or not hasattr(registry, "has_capability") or not registry.has_capability(CAPABILITY_ACCESS_DIRECTORY):
|
|
|
|
|
return None
|
|
|
|
|
capability = registry.require_capability(CAPABILITY_ACCESS_DIRECTORY)
|
|
|
|
|
if not isinstance(capability, AccessDirectory):
|
|
|
|
|
raise MailProfileError("Access directory capability is invalid")
|
|
|
|
|
return capability
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _campaign_policy_provider() -> CampaignMailPolicyContextProvider | None:
|
|
|
|
|
registry = get_registry()
|
|
|
|
|
if registry is None or not hasattr(registry, "has_capability") or not registry.has_capability(CAPABILITY_CAMPAIGNS_MAIL_POLICY_CONTEXT):
|
|
|
|
|
return None
|
|
|
|
|
capability = registry.require_capability(CAPABILITY_CAMPAIGNS_MAIL_POLICY_CONTEXT)
|
|
|
|
|
if not isinstance(capability, CampaignMailPolicyContextProvider):
|
|
|
|
|
raise MailProfileError("Campaign mail policy context capability is invalid")
|
|
|
|
|
return capability
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _campaign_policy_context(session: Session, *, tenant_id: str, campaign_id: str) -> CampaignMailPolicyContext:
|
|
|
|
|
provider = _campaign_policy_provider()
|
|
|
|
|
if provider is None:
|
|
|
|
|
raise MailProfileError("Campaign module is not available")
|
|
|
|
|
context = provider.get_campaign_mail_policy_context(session, tenant_id=tenant_id, campaign_id=campaign_id)
|
|
|
|
|
if context is None:
|
|
|
|
|
raise MailProfileError("Campaign not found for mail-server profile policy")
|
|
|
|
|
return context
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _legacy_tenant_settings(session: Session, tenant_id: str) -> dict[str, Any] | None:
|
|
|
|
|
row = session.execute(text("select settings from tenants where id = :tenant_id"), {"tenant_id": tenant_id}).first()
|
|
|
|
|
return _json_object(row[0]) if row else None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _legacy_subject_policy(session: Session, *, table_name: str, tenant_id: str, scope_id: str) -> dict[str, Any] | None:
|
|
|
|
|
if table_name not in {"users", "groups"}:
|
|
|
|
|
raise MailProfileError("Unsupported mail policy subject")
|
|
|
|
|
row = session.execute(
|
|
|
|
|
text(f"select mail_profile_policy from {table_name} where id = :scope_id and tenant_id = :tenant_id"),
|
|
|
|
|
{"scope_id": scope_id, "tenant_id": tenant_id},
|
|
|
|
|
).first()
|
|
|
|
|
return _json_object(row[0]) if row else None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _tenant_exists(session: Session, tenant_id: str) -> bool:
|
|
|
|
|
return _legacy_tenant_settings(session, tenant_id) is not None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _user_exists(session: Session, *, tenant_id: str, user_id: str) -> bool:
|
|
|
|
|
directory = _access_directory()
|
|
|
|
|
if directory is not None:
|
|
|
|
|
user = directory.get_user(user_id)
|
|
|
|
|
return bool(user and user.tenant_id == tenant_id and user.status == "active")
|
|
|
|
|
return _legacy_subject_policy(session, table_name="users", tenant_id=tenant_id, scope_id=user_id) is not None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _group_exists(session: Session, *, tenant_id: str, group_id: str) -> bool:
|
|
|
|
|
directory = _access_directory()
|
|
|
|
|
if directory is not None:
|
|
|
|
|
group = directory.get_group(group_id)
|
|
|
|
|
return bool(group and group.tenant_id == tenant_id and group.status == "active")
|
|
|
|
|
return _legacy_subject_policy(session, table_name="groups", tenant_id=tenant_id, scope_id=group_id) is not None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _policy_scope_key(*, tenant_id: str, scope_type: str, scope_id: str | None) -> tuple[str | None, str, str | None]:
|
|
|
|
|
clean_scope = _normalize_scope_type(scope_type)
|
|
|
|
|
if clean_scope == "system":
|
|
|
|
|
return None, clean_scope, None
|
|
|
|
|
if clean_scope == "tenant":
|
|
|
|
|
return tenant_id, clean_scope, scope_id or tenant_id
|
|
|
|
|
return tenant_id, clean_scope, scope_id
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _mail_policy_row(
|
|
|
|
|
session: Session,
|
|
|
|
|
*,
|
|
|
|
|
tenant_id: str,
|
|
|
|
|
scope_type: str,
|
|
|
|
|
scope_id: str | None,
|
|
|
|
|
) -> MailProfilePolicy | None:
|
|
|
|
|
row_tenant_id, row_scope_type, row_scope_id = _policy_scope_key(tenant_id=tenant_id, scope_type=scope_type, scope_id=scope_id)
|
|
|
|
|
query = session.query(MailProfilePolicy).filter(MailProfilePolicy.scope_type == row_scope_type)
|
|
|
|
|
query = query.filter(MailProfilePolicy.tenant_id.is_(None) if row_tenant_id is None else MailProfilePolicy.tenant_id == row_tenant_id)
|
|
|
|
|
query = query.filter(MailProfilePolicy.scope_id.is_(None) if row_scope_id is None else MailProfilePolicy.scope_id == row_scope_id)
|
|
|
|
|
return query.one_or_none()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _legacy_mail_profile_policy(
|
|
|
|
|
session: Session,
|
|
|
|
|
*,
|
|
|
|
|
tenant_id: str,
|
|
|
|
|
scope_type: str,
|
|
|
|
|
scope_id: str | None,
|
|
|
|
|
) -> dict[str, Any] | None:
|
|
|
|
|
clean_scope = _normalize_scope_type(scope_type)
|
|
|
|
|
if clean_scope == "system":
|
|
|
|
|
return _policy_from_settings(get_system_settings(session).settings or {})
|
|
|
|
|
if clean_scope == "tenant":
|
|
|
|
|
if scope_id and scope_id != tenant_id:
|
|
|
|
|
return None
|
|
|
|
|
settings = _legacy_tenant_settings(session, tenant_id)
|
|
|
|
|
return _policy_from_settings(settings or {}) if settings is not None else None
|
|
|
|
|
if not scope_id:
|
|
|
|
|
return None
|
|
|
|
|
if clean_scope == "user":
|
|
|
|
|
legacy = _legacy_subject_policy(session, table_name="users", tenant_id=tenant_id, scope_id=scope_id)
|
|
|
|
|
return _policy_from_attr(legacy) if legacy is not None else None
|
|
|
|
|
if clean_scope == "group":
|
|
|
|
|
legacy = _legacy_subject_policy(session, table_name="groups", tenant_id=tenant_id, scope_id=scope_id)
|
|
|
|
|
return _policy_from_attr(legacy) if legacy is not None else None
|
|
|
|
|
try:
|
|
|
|
|
context = _campaign_policy_context(session, tenant_id=tenant_id, campaign_id=scope_id)
|
|
|
|
|
except MailProfileError:
|
|
|
|
|
return None
|
|
|
|
|
return _policy_from_attr(context.mail_profile_policy)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _read_mail_profile_policy(
|
|
|
|
|
session: Session,
|
|
|
|
|
*,
|
|
|
|
|
tenant_id: str,
|
|
|
|
|
scope_type: str,
|
|
|
|
|
scope_id: str | None = None,
|
|
|
|
|
) -> dict[str, Any] | None:
|
|
|
|
|
clean_scope = _normalize_scope_type(scope_type)
|
|
|
|
|
if clean_scope != "campaign":
|
|
|
|
|
row = _mail_policy_row(session, tenant_id=tenant_id, scope_type=clean_scope, scope_id=scope_id)
|
|
|
|
|
if row is not None:
|
|
|
|
|
return _policy_from_attr(row.policy)
|
|
|
|
|
return _legacy_mail_profile_policy(session, tenant_id=tenant_id, scope_type=clean_scope, scope_id=scope_id)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _write_mail_profile_policy(
|
|
|
|
|
session: Session,
|
|
|
|
|
*,
|
|
|
|
|
tenant_id: str,
|
|
|
|
|
scope_type: str,
|
|
|
|
|
scope_id: str | None,
|
|
|
|
|
policy: dict[str, Any],
|
|
|
|
|
) -> dict[str, Any]:
|
|
|
|
|
row_tenant_id, row_scope_type, row_scope_id = _policy_scope_key(tenant_id=tenant_id, scope_type=scope_type, scope_id=scope_id)
|
|
|
|
|
row = _mail_policy_row(session, tenant_id=tenant_id, scope_type=row_scope_type, scope_id=row_scope_id)
|
|
|
|
|
if row is None:
|
|
|
|
|
row = MailProfilePolicy(
|
|
|
|
|
tenant_id=row_tenant_id,
|
|
|
|
|
scope_type=row_scope_type,
|
|
|
|
|
scope_id=row_scope_id,
|
|
|
|
|
policy=policy,
|
|
|
|
|
)
|
|
|
|
|
else:
|
|
|
|
|
row.policy = policy
|
|
|
|
|
session.add(row)
|
|
|
|
|
return policy
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _mail_policy_source_fields(policy: dict[str, Any], *, baseline: bool = False) -> list[str]:
|
|
|
|
|
fields: list[str] = []
|
|
|
|
|
@@ -373,15 +538,13 @@ def _owner_context(
|
|
|
|
|
campaign_id: str | None = None,
|
|
|
|
|
owner_user_id: str | None = None,
|
|
|
|
|
owner_group_id: str | None = None,
|
|
|
|
|
) -> tuple[Any | None, str | None, str | None]:
|
|
|
|
|
campaign = None
|
|
|
|
|
) -> tuple[CampaignMailPolicyContext | None, str | None, str | None]:
|
|
|
|
|
campaign_context = None
|
|
|
|
|
if campaign_id:
|
|
|
|
|
campaign = session.get(_campaign_model(), campaign_id)
|
|
|
|
|
if campaign is None or campaign.tenant_id != tenant_id:
|
|
|
|
|
raise MailProfileError("Campaign not found for mail-server profile policy")
|
|
|
|
|
owner_user_id = campaign.owner_user_id
|
|
|
|
|
owner_group_id = campaign.owner_group_id
|
|
|
|
|
return campaign, owner_user_id, owner_group_id
|
|
|
|
|
campaign_context = _campaign_policy_context(session, tenant_id=tenant_id, campaign_id=campaign_id)
|
|
|
|
|
owner_user_id = campaign_context.owner_user_id
|
|
|
|
|
owner_group_id = campaign_context.owner_group_id
|
|
|
|
|
return campaign_context, owner_user_id, owner_group_id
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def effective_mail_profile_policy(
|
|
|
|
|
@@ -399,22 +562,22 @@ def effective_mail_profile_policy(
|
|
|
|
|
owner_user_id=owner_user_id,
|
|
|
|
|
owner_group_id=owner_group_id,
|
|
|
|
|
)
|
|
|
|
|
tenant = session.get(Tenant, tenant_id)
|
|
|
|
|
if tenant is None:
|
|
|
|
|
tenant_policy = _read_mail_profile_policy(session, tenant_id=tenant_id, scope_type="tenant", scope_id=tenant_id)
|
|
|
|
|
if tenant_policy is None:
|
|
|
|
|
raise MailProfileError("Tenant not found for mail-server profile policy")
|
|
|
|
|
|
|
|
|
|
policy = EffectiveMailProfilePolicy()
|
|
|
|
|
system_settings = get_system_settings(session)
|
|
|
|
|
_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")
|
|
|
|
|
system_policy = _read_mail_profile_policy(session, tenant_id=tenant_id, scope_type="system") or {}
|
|
|
|
|
_merge_policy(policy, system_policy, source="system", label="System", baseline=True)
|
|
|
|
|
_merge_policy(policy, tenant_policy, 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", source_id=user.id, label="Owner user")
|
|
|
|
|
user_policy = _read_mail_profile_policy(session, tenant_id=tenant_id, scope_type="user", scope_id=owner_user_id)
|
|
|
|
|
if user_policy is not None:
|
|
|
|
|
_merge_policy(policy, user_policy, source="user", source_id=owner_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", source_id=group.id, label="Owner group")
|
|
|
|
|
group_policy = _read_mail_profile_policy(session, tenant_id=tenant_id, scope_type="group", scope_id=owner_group_id)
|
|
|
|
|
if group_policy is not None:
|
|
|
|
|
_merge_policy(policy, group_policy, source="group", source_id=owner_group_id, label="Owner group")
|
|
|
|
|
if campaign is not None:
|
|
|
|
|
_merge_policy(policy, _policy_from_attr(campaign.mail_profile_policy), source="campaign", source_id=campaign.id, label="Campaign")
|
|
|
|
|
return policy
|
|
|
|
|
@@ -434,8 +597,8 @@ def effective_mail_profile_policy_for_scope(
|
|
|
|
|
return effective_mail_profile_policy(session, tenant_id=tenant_id, campaign_id=campaign_id)
|
|
|
|
|
if clean_scope == "system":
|
|
|
|
|
policy = EffectiveMailProfilePolicy()
|
|
|
|
|
system_settings = get_system_settings(session)
|
|
|
|
|
_merge_policy(policy, _policy_from_settings(system_settings.settings or {}), source="system", label="System", baseline=True)
|
|
|
|
|
system_policy = _read_mail_profile_policy(session, tenant_id=tenant_id, scope_type="system") or {}
|
|
|
|
|
_merge_policy(policy, system_policy, source="system", label="System", baseline=True)
|
|
|
|
|
return policy
|
|
|
|
|
if clean_scope == "tenant":
|
|
|
|
|
return effective_mail_profile_policy(session, tenant_id=tenant_id)
|
|
|
|
|
@@ -460,33 +623,31 @@ def parent_mail_profile_policy(
|
|
|
|
|
scope_id: str | None = None,
|
|
|
|
|
) -> EffectiveMailProfilePolicy:
|
|
|
|
|
clean_scope = _normalize_scope_type(scope_type)
|
|
|
|
|
tenant = session.get(Tenant, tenant_id)
|
|
|
|
|
if tenant is None:
|
|
|
|
|
tenant_policy = _read_mail_profile_policy(session, tenant_id=tenant_id, scope_type="tenant", scope_id=tenant_id)
|
|
|
|
|
if tenant_policy is None:
|
|
|
|
|
raise MailProfileError("Tenant not found for mail-server profile policy")
|
|
|
|
|
|
|
|
|
|
policy = EffectiveMailProfilePolicy()
|
|
|
|
|
system_settings = get_system_settings(session)
|
|
|
|
|
_merge_policy(policy, _policy_from_settings(system_settings.settings or {}), source="system", label="System", baseline=True)
|
|
|
|
|
system_policy = _read_mail_profile_policy(session, tenant_id=tenant_id, scope_type="system") or {}
|
|
|
|
|
_merge_policy(policy, system_policy, source="system", label="System", baseline=True)
|
|
|
|
|
if clean_scope == "tenant":
|
|
|
|
|
return policy
|
|
|
|
|
|
|
|
|
|
_merge_policy(policy, _policy_from_settings(tenant.settings or {}), source="tenant", source_id=tenant.id, label="Tenant")
|
|
|
|
|
_merge_policy(policy, tenant_policy, source="tenant", source_id=tenant_id, label="Tenant")
|
|
|
|
|
if clean_scope in {"user", "group"}:
|
|
|
|
|
return policy
|
|
|
|
|
|
|
|
|
|
if clean_scope != "campaign" or not scope_id:
|
|
|
|
|
return policy
|
|
|
|
|
campaign = session.get(_campaign_model(), scope_id)
|
|
|
|
|
if campaign is None or campaign.tenant_id != tenant_id:
|
|
|
|
|
raise MailProfileError("Campaign policy not found")
|
|
|
|
|
campaign = _campaign_policy_context(session, tenant_id=tenant_id, campaign_id=scope_id)
|
|
|
|
|
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", source_id=user.id, label="Owner user")
|
|
|
|
|
user_policy = _read_mail_profile_policy(session, tenant_id=tenant_id, scope_type="user", scope_id=campaign.owner_user_id)
|
|
|
|
|
if user_policy is not None:
|
|
|
|
|
_merge_policy(policy, user_policy, source="user", source_id=campaign.owner_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", source_id=group.id, label="Owner group")
|
|
|
|
|
group_policy = _read_mail_profile_policy(session, tenant_id=tenant_id, scope_type="group", scope_id=campaign.owner_group_id)
|
|
|
|
|
if group_policy is not None:
|
|
|
|
|
_merge_policy(policy, group_policy, source="group", source_id=campaign.owner_group_id, label="Owner group")
|
|
|
|
|
return policy
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@@ -580,22 +741,18 @@ def _scope_tuple_for_create(
|
|
|
|
|
target_id = clean_scope_id or user_id
|
|
|
|
|
if not target_id:
|
|
|
|
|
raise MailProfileError("User-scoped mail-server profiles require a user scope_id")
|
|
|
|
|
user = session.get(User, target_id)
|
|
|
|
|
if user is None or user.tenant_id != tenant_id:
|
|
|
|
|
if not _user_exists(session, tenant_id=tenant_id, user_id=target_id):
|
|
|
|
|
raise MailProfileError("User scope is not part of the active tenant")
|
|
|
|
|
return tenant_id, clean_scope, target_id
|
|
|
|
|
if clean_scope == "group":
|
|
|
|
|
if not clean_scope_id:
|
|
|
|
|
raise MailProfileError("Group-scoped mail-server profiles require a group scope_id")
|
|
|
|
|
group = session.get(Group, clean_scope_id)
|
|
|
|
|
if group is None or group.tenant_id != tenant_id:
|
|
|
|
|
if not _group_exists(session, tenant_id=tenant_id, group_id=clean_scope_id):
|
|
|
|
|
raise MailProfileError("Group scope is not part of the active tenant")
|
|
|
|
|
return tenant_id, clean_scope, clean_scope_id
|
|
|
|
|
if not clean_scope_id:
|
|
|
|
|
raise MailProfileError("Campaign-scoped mail-server profiles require a campaign scope_id")
|
|
|
|
|
campaign = session.get(_campaign_model(), clean_scope_id)
|
|
|
|
|
if campaign is None or campaign.tenant_id != tenant_id:
|
|
|
|
|
raise MailProfileError("Campaign scope is not part of the active tenant")
|
|
|
|
|
_campaign_policy_context(session, tenant_id=tenant_id, campaign_id=clean_scope_id)
|
|
|
|
|
return tenant_id, clean_scope, clean_scope_id
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@@ -647,7 +804,7 @@ def _profile_visible_filter(tenant_id: str):
|
|
|
|
|
return or_(MailServerProfile.scope_type == "system", MailServerProfile.tenant_id == tenant_id)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _context_profile_clauses(campaign: Any, policy: EffectiveMailProfilePolicy) -> list[Any]:
|
|
|
|
|
def _context_profile_clauses(campaign: CampaignMailPolicyContext, policy: EffectiveMailProfilePolicy) -> list[Any]:
|
|
|
|
|
clauses: list[Any] = [
|
|
|
|
|
MailServerProfile.scope_type == "system",
|
|
|
|
|
and_(MailServerProfile.tenant_id == campaign.tenant_id, MailServerProfile.scope_type == "tenant"),
|
|
|
|
|
@@ -674,7 +831,7 @@ def _profile_allowed_for_context(
|
|
|
|
|
*,
|
|
|
|
|
tenant_id: str,
|
|
|
|
|
policy: EffectiveMailProfilePolicy,
|
|
|
|
|
campaign: Any | None = None,
|
|
|
|
|
campaign: CampaignMailPolicyContext | None = None,
|
|
|
|
|
owner_user_id: str | None = None,
|
|
|
|
|
owner_group_id: str | None = None,
|
|
|
|
|
) -> bool:
|
|
|
|
|
@@ -711,9 +868,7 @@ def list_mail_server_profiles(
|
|
|
|
|
campaign_id: str | None = None,
|
|
|
|
|
) -> list[MailServerProfile]:
|
|
|
|
|
if campaign_id:
|
|
|
|
|
campaign = session.get(_campaign_model(), campaign_id)
|
|
|
|
|
if campaign is None or campaign.tenant_id != tenant_id:
|
|
|
|
|
raise MailProfileError("Campaign not found for mail-server profiles")
|
|
|
|
|
campaign = _campaign_policy_context(session, tenant_id=tenant_id, campaign_id=campaign_id)
|
|
|
|
|
policy = effective_mail_profile_policy(session, tenant_id=tenant_id, campaign_id=campaign.id)
|
|
|
|
|
query = session.query(MailServerProfile).filter(or_(*_context_profile_clauses(campaign, policy)))
|
|
|
|
|
if not include_inactive:
|
|
|
|
|
@@ -762,9 +917,7 @@ def ensure_mail_profile_allowed_for_campaign(
|
|
|
|
|
profile_id: str,
|
|
|
|
|
require_active: bool = True,
|
|
|
|
|
) -> MailServerProfile:
|
|
|
|
|
campaign = session.get(_campaign_model(), campaign_id)
|
|
|
|
|
if campaign is None or campaign.tenant_id != tenant_id:
|
|
|
|
|
raise MailProfileError("Campaign not found for mail-server profile policy")
|
|
|
|
|
campaign = _campaign_policy_context(session, tenant_id=tenant_id, campaign_id=campaign_id)
|
|
|
|
|
profile = get_mail_server_profile(session, tenant_id=tenant_id, profile_id=profile_id, require_active=require_active)
|
|
|
|
|
policy = effective_mail_profile_policy(session, tenant_id=tenant_id, campaign_id=campaign.id)
|
|
|
|
|
if not _profile_allowed_for_context(
|
|
|
|
|
@@ -1165,27 +1318,25 @@ def get_mail_profile_policy(
|
|
|
|
|
) -> dict[str, Any]:
|
|
|
|
|
clean_scope = _normalize_scope_type(scope_type)
|
|
|
|
|
if clean_scope == "system":
|
|
|
|
|
return _policy_from_settings(get_system_settings(session).settings or {})
|
|
|
|
|
return _read_mail_profile_policy(session, tenant_id=tenant_id, scope_type="system") or {}
|
|
|
|
|
if clean_scope == "tenant":
|
|
|
|
|
tenant = session.get(Tenant, scope_id or tenant_id)
|
|
|
|
|
if tenant is None or tenant.id != tenant_id:
|
|
|
|
|
policy = _read_mail_profile_policy(session, tenant_id=tenant_id, scope_type="tenant", scope_id=scope_id or tenant_id)
|
|
|
|
|
if policy is None:
|
|
|
|
|
raise MailProfileError("Tenant policy not found")
|
|
|
|
|
return _policy_from_settings(tenant.settings or {})
|
|
|
|
|
return policy
|
|
|
|
|
if not scope_id:
|
|
|
|
|
raise MailProfileError(f"{clean_scope.capitalize()} mail profile policy requires scope_id")
|
|
|
|
|
if clean_scope == "user":
|
|
|
|
|
user = session.get(User, scope_id)
|
|
|
|
|
if user is None or user.tenant_id != tenant_id:
|
|
|
|
|
policy = _read_mail_profile_policy(session, tenant_id=tenant_id, scope_type="user", scope_id=scope_id)
|
|
|
|
|
if policy is None:
|
|
|
|
|
raise MailProfileError("User policy not found")
|
|
|
|
|
return _policy_from_attr(user.mail_profile_policy)
|
|
|
|
|
return policy
|
|
|
|
|
if clean_scope == "group":
|
|
|
|
|
group = session.get(Group, scope_id)
|
|
|
|
|
if group is None or group.tenant_id != tenant_id:
|
|
|
|
|
policy = _read_mail_profile_policy(session, tenant_id=tenant_id, scope_type="group", scope_id=scope_id)
|
|
|
|
|
if policy is None:
|
|
|
|
|
raise MailProfileError("Group policy not found")
|
|
|
|
|
return _policy_from_attr(group.mail_profile_policy)
|
|
|
|
|
campaign = session.get(_campaign_model(), scope_id)
|
|
|
|
|
if campaign is None or campaign.tenant_id != tenant_id:
|
|
|
|
|
raise MailProfileError("Campaign policy not found")
|
|
|
|
|
return policy
|
|
|
|
|
campaign = _campaign_policy_context(session, tenant_id=tenant_id, campaign_id=scope_id)
|
|
|
|
|
return _policy_from_attr(campaign.mail_profile_policy)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@@ -1231,42 +1382,28 @@ def set_mail_profile_policy(
|
|
|
|
|
normalized,
|
|
|
|
|
)
|
|
|
|
|
if clean_scope == "system":
|
|
|
|
|
item = get_system_settings(session)
|
|
|
|
|
settings_payload = dict(item.settings or {})
|
|
|
|
|
settings_payload[MAIL_PROFILE_POLICY_SETTINGS_KEY] = normalized
|
|
|
|
|
item.settings = settings_payload
|
|
|
|
|
session.add(item)
|
|
|
|
|
return normalized
|
|
|
|
|
return _write_mail_profile_policy(session, tenant_id=tenant_id, scope_type="system", scope_id=None, policy=normalized)
|
|
|
|
|
if clean_scope == "tenant":
|
|
|
|
|
tenant = session.get(Tenant, scope_id or tenant_id)
|
|
|
|
|
if tenant is None or tenant.id != tenant_id:
|
|
|
|
|
if (scope_id and scope_id != tenant_id) or not _tenant_exists(session, tenant_id):
|
|
|
|
|
raise MailProfileError("Tenant policy not found")
|
|
|
|
|
settings_payload = dict(tenant.settings or {})
|
|
|
|
|
settings_payload[MAIL_PROFILE_POLICY_SETTINGS_KEY] = normalized
|
|
|
|
|
tenant.settings = settings_payload
|
|
|
|
|
session.add(tenant)
|
|
|
|
|
return normalized
|
|
|
|
|
return _write_mail_profile_policy(session, tenant_id=tenant_id, scope_type="tenant", scope_id=tenant_id, policy=normalized)
|
|
|
|
|
if not scope_id:
|
|
|
|
|
raise MailProfileError(f"{clean_scope.capitalize()} mail profile policy requires scope_id")
|
|
|
|
|
if clean_scope == "user":
|
|
|
|
|
user = session.get(User, scope_id)
|
|
|
|
|
if user is None or user.tenant_id != tenant_id:
|
|
|
|
|
if not _user_exists(session, tenant_id=tenant_id, user_id=scope_id):
|
|
|
|
|
raise MailProfileError("User policy not found")
|
|
|
|
|
user.mail_profile_policy = normalized
|
|
|
|
|
session.add(user)
|
|
|
|
|
return normalized
|
|
|
|
|
return _write_mail_profile_policy(session, tenant_id=tenant_id, scope_type="user", scope_id=scope_id, policy=normalized)
|
|
|
|
|
if clean_scope == "group":
|
|
|
|
|
group = session.get(Group, scope_id)
|
|
|
|
|
if group is None or group.tenant_id != tenant_id:
|
|
|
|
|
if not _group_exists(session, tenant_id=tenant_id, group_id=scope_id):
|
|
|
|
|
raise MailProfileError("Group policy not found")
|
|
|
|
|
group.mail_profile_policy = normalized
|
|
|
|
|
session.add(group)
|
|
|
|
|
return normalized
|
|
|
|
|
campaign = session.get(_campaign_model(), scope_id)
|
|
|
|
|
if campaign is None or campaign.tenant_id != tenant_id:
|
|
|
|
|
raise MailProfileError("Campaign policy not found")
|
|
|
|
|
campaign.mail_profile_policy = normalized
|
|
|
|
|
session.add(campaign)
|
|
|
|
|
return _write_mail_profile_policy(session, tenant_id=tenant_id, scope_type="group", scope_id=scope_id, policy=normalized)
|
|
|
|
|
provider = _campaign_policy_provider()
|
|
|
|
|
if provider is None:
|
|
|
|
|
raise MailProfileError("Campaign module is not available")
|
|
|
|
|
try:
|
|
|
|
|
provider.set_campaign_mail_profile_policy(session, tenant_id=tenant_id, campaign_id=scope_id, policy=normalized)
|
|
|
|
|
except ValueError as exc:
|
|
|
|
|
raise MailProfileError("Campaign policy not found") from exc
|
|
|
|
|
return normalized
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@@ -1354,10 +1491,11 @@ def mail_profile_id_from_campaign_json(raw_json: dict[str, Any] | None) -> str |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def group_ids_for_user(session: Session, *, tenant_id: str, user_id: str) -> list[str]:
|
|
|
|
|
return [
|
|
|
|
|
row.group_id
|
|
|
|
|
for row in session.query(UserGroupMembership).filter(
|
|
|
|
|
UserGroupMembership.tenant_id == tenant_id,
|
|
|
|
|
UserGroupMembership.user_id == user_id,
|
|
|
|
|
)
|
|
|
|
|
]
|
|
|
|
|
directory = _access_directory()
|
|
|
|
|
if directory is not None:
|
|
|
|
|
return [group.id for group in directory.groups_for_user(user_id, tenant_id=tenant_id)]
|
|
|
|
|
rows = session.execute(
|
|
|
|
|
text("select group_id from user_group_memberships where tenant_id = :tenant_id and user_id = :user_id"),
|
|
|
|
|
{"tenant_id": tenant_id, "user_id": user_id},
|
|
|
|
|
).all()
|
|
|
|
|
return [row[0] for row in rows]
|
|
|
|
|
|