from enum import StrEnum from typing import Iterable class FeishuUserRole(StrEnum): USER = "user" ADMIN = "admin" class FeishuUserStatus(StrEnum): ACTIVE = "active" DISABLED = "disabled" class FeishuCapability(StrEnum): PERSONAL_AI = "personal_ai" PERSONAL_DATA = "personal_data" PRIVATE_SUBSCRIPTION = "private_subscription" PERSONAL_MARKET = "personal_market" COMPANY_REPORTS = "company_reports" COMPANY_RULES = "company_rules" USER_ADMINISTRATION = "user_administration" GROUP_SUBSCRIPTION = "group_subscription" class FeishuUserAuditAction(StrEnum): REGISTER = "feishu.user.register" AUTHENTICATE = "feishu.user.authenticate" PERMISSION_DENIED = "feishu.permission.denied" LIST = "feishu.user.list" READ = "feishu.user.read" UPDATE = "feishu.user.update" UPDATE_DENIED = "feishu.user.update_denied" FEISHU_USER_CODE_PREFIX = "FSU" FEISHU_USER_TARGET_TYPE = "feishu-user" DEFAULT_FEISHU_USER_TIMEZONE = "Asia/Shanghai" LAST_ACTIVE_ADMIN_ERROR = "The last active Feishu administrator cannot be changed" FEISHU_USER_NOT_FOUND = "Feishu user not found" INVALID_FEISHU_IDENTITY = "tenant_key and open_id are required" INVALID_FEISHU_TIMEZONE = "Invalid IANA timezone" INVALID_QUIET_HOURS = "quiet_hours_start and quiet_hours_end must both be set or cleared" INVALID_ADMIN_IDENTITY = ( "FEISHU_ADMIN_IDENTITIES entries must use tenant_key:open_id format" ) _USER_CAPABILITIES = frozenset( { FeishuCapability.PERSONAL_AI, FeishuCapability.PERSONAL_DATA, FeishuCapability.PRIVATE_SUBSCRIPTION, FeishuCapability.PERSONAL_MARKET, } ) _ADMIN_CAPABILITIES = frozenset(FeishuCapability) def capabilities_for_role(role: str | FeishuUserRole) -> frozenset[FeishuCapability]: """Return the fixed capability set for a Feishu user role.""" if FeishuUserRole(role) == FeishuUserRole.ADMIN: return _ADMIN_CAPABILITIES return _USER_CAPABILITIES def parse_admin_identities( value: str | Iterable[str] | None, ) -> frozenset[tuple[str, str]]: """Parse exact tenant/open-id pairs used only for initial administrator creation.""" if value is None: return frozenset() entries = value.split(",") if isinstance(value, str) else value identities: set[tuple[str, str]] = set() for entry in entries: text = str(entry).strip() if not text: continue tenant_key, separator, open_id = text.partition(":") tenant_key = tenant_key.strip() open_id = open_id.strip() if ( not separator or not tenant_key or not open_id or ":" in open_id or len(tenant_key) > 128 or len(open_id) > 128 or any(character in text for character in "\r\n") ): raise ValueError(INVALID_ADMIN_IDENTITY) identities.add((tenant_key, open_id)) return frozenset(identities)