import type { IJMAPClient } from '@/lib/jmap/client-interface'; import type { PolicySubject } from './policy-scopes'; type SubjectClient = Pick; /** * Who a signed-in client is for the domain and group scopes: its login * address and the addresses of the group accounts in its session. Stalwart * names group accounts by their address (local-data/org-features-facts.md * ยง1). A mailbox another user shared is also a non-personal account, so the * principal type decides; when the server won't say, every non-personal * account counts, which only matters if an admin put a person's address in a * group scope. */ export async function policySubjectForClient(client: SubjectClient | null): Promise { if (!client || typeof client.getUsername !== 'function') return null; const email = client.getUsername()?.trim().toLowerCase() ?? ''; if (!email) return null; // A client without a session yet (or a stand-in) has no shared accounts. const shared = (typeof client.getSharedAccounts === 'function' ? client.getSharedAccounts() : []) .filter((account) => !account.isPrimary && client.getAccountInfo?.(account.id)?.isPersonal === false) .filter((account) => account.name.includes('@')); const types = shared.length > 0 && typeof client.getPrincipalTypes === 'function' ? await client.getPrincipalTypes(shared.map((account) => account.id)).catch(() => null) : null; const groups = shared .filter((account) => !types || types[account.id] === 'group') .map((account) => account.name.trim().toLowerCase()) .sort(); return { email, groups }; }