"""Cloud role-name classifiers. Two small predicates shared by the lifecycle router (filters dropdowns + refuses unsafe grants) and the lifecycle service (resolves billing- alert recipients): * :func:`is_admin_role` — true when the role's key/display-name reads as an administrative privilege. Used to hide power roles from the self-service request flow and to refuse non-admin callers from granting them. * :func:`is_billing_role` — true when the role exists for billing/cost visibility (Azure "Cost Management Reader", GCP "roles/billing.viewer", AWS "Billing", Aliyun "AliyunBillingFullAccess", Tencent "QcloudFinanceFullAccess", generic "*finance*"/"*财务*"/"*账单*" custom roles). Used to resolve the "billing owner" set when fanning out a budget alert — alerts go to the scope owner plus everyone holding a billing-class grant on that scope. Substring match is intentional: cloud catalogues are huge and admins mint custom roles named "Tidb Billing Reader" / "财务核对员" all the time. A keyword sieve covers them without a per-provider allow-list. Patterns are case-insensitive and walk both ``role_key`` (which may be an ARN or numeric id with no human-readable name) and the display name together — either matching is enough. """ from __future__ import annotations import re # Anything that reads as administrative authority. Captures Azure # "*Administrator", AWS "*AdminAccess", Aliyun "*管理员", custom roles # named "Tidb Admin", etc. ``\badmin\b`` would miss "Administrator", so # we match the substring "admin" and explicitly add the Chinese token. _ADMIN_PATTERNS = [ re.compile(r"admin", re.IGNORECASE), re.compile(r"管理员"), ] # Allowlist of role names that LOOK administrative but are intentionally # exposed to the self-service request flow. Cloud / product teams mint # bounded "admin" roles whose privilege is scoped to one product # (e.g. AWS ``DBaaS-Prod-Admin-Role`` is the operator role for the # DBaaS-Prod sub-account, not a tenant-wide admin), and forcing every # request through IT-admin defeats self-service for those roles. # # Match is **exact** (case-insensitive) on either ``role_key`` or # ``display_name`` after stripping whitespace — substring matching # would let a malicious sibling like ``DBaaS-Prod-Admin-Role-Plus`` # slip through unreviewed. Keep this list short and explicitly # justified — every entry is a deliberate exception to the default # defensive stance. _ADMIN_NAME_ALLOWLIST: frozenset[str] = frozenset({ "dbaas-prod-admin-role", }) def _allowlisted(role_key: str | None, display_name: str | None) -> bool: for t in (role_key, display_name): if t and t.strip().lower() in _ADMIN_NAME_ALLOWLIST: return True return False # Billing / cost / finance keywords. Matches: # * GCP roles/billing.viewer / billing.admin # * Azure "Cost Management Reader" / "Billing Reader" # * AWS "Billing" / "BillingViewAccess" # * Aliyun "AliyunBillingFullAccess" / "AliyunBSSReadOnlyAccess" # * Tencent "QcloudFinanceFullAccess" # * any custom role with 财务/账单/计费 in its display name _BILLING_PATTERNS = [ re.compile(r"billing", re.IGNORECASE), re.compile(r"\bcost\b", re.IGNORECASE), # \b avoids matching "costing" re.compile(r"finance", re.IGNORECASE), re.compile(r"bss", re.IGNORECASE), # Aliyun BSS = Billing Support System re.compile(r"账单"), re.compile(r"计费"), re.compile(r"财务"), ] def _matches_any(patterns: list[re.Pattern[str]], *texts: str | None) -> bool: for t in texts: if not t: continue for p in patterns: if p.search(t): return True return False def is_admin_role(role_key: str | None, display_name: str | None = None) -> bool: """True iff the role reads as administrative on either name field. Names in :data:`_ADMIN_NAME_ALLOWLIST` are exempt — bounded "admin" roles whose privilege is scoped to a single product (and therefore are intentionally self-serviceable) bypass the substring sieve. """ if _allowlisted(role_key, display_name): return False return _matches_any(_ADMIN_PATTERNS, role_key, display_name) def is_billing_role(role_key: str | None, display_name: str | None = None) -> bool: """True iff the role appears to grant billing/cost visibility.""" return _matches_any(_BILLING_PATTERNS, role_key, display_name) __all__ = ["is_admin_role", "is_billing_role"]