// hosts/codex/enterprise-auth.ts — SAML/OIDC passthrough identity + RBAC gate (SPEC-592)
// Codex enterprise passes identity via OPENAI_USER_ID, OPENAI_USER_EMAIL,
// OPENAI_USER_ROLES (comma-separated), and OPENAI_ENTERPRISE_TENANT env vars.
import { appendAuditEntry } from './adapter.js';
// Role hierarchy: owner > admin > member > viewer
const ROLE_HIERARCHY = {
    owner: 4,
    admin: 3,
    member: 2,
    viewer: 1,
};
/**
 * Parse SAML/OIDC identity from Codex enterprise environment variables.
 * Returns null if no identity context is available (non-enterprise or unauthenticated).
 */
export function parseCodexIdentity() {
    const userId = process.env.OPENAI_USER_ID;
    const tenantId = process.env.OPENAI_ENTERPRISE_TENANT;
    if (!userId || !tenantId) {
        return null;
    }
    const rawRoles = process.env.OPENAI_USER_ROLES ?? 'member';
    const roles = parseRoles(rawRoles);
    return {
        userId,
        email: process.env.OPENAI_USER_EMAIL,
        roles,
        tenantId,
    };
}
/**
 * Evaluate role-based access gate for a tool invocation.
 * Logs the decision to audit_log_export regardless of outcome.
 * Returns false if identity is missing or role is insufficient.
 */
export function evaluateRoleGate(projectPath, toolName, requiredRole, identity) {
    const decidedAt = new Date().toISOString();
    if (!identity) {
        const gate = {
            toolName,
            identity: {
                userId: 'anonymous',
                roles: [],
                tenantId: 'unknown',
            },
            requiredRole,
            decision: 'deny',
            decidedAt,
            denyReason: 'No authenticated identity available',
        };
        logGateDecision(projectPath, gate);
        return gate;
    }
    const requiredLevel = ROLE_HIERARCHY[requiredRole];
    const hasRole = identity.roles.some((role) => ROLE_HIERARCHY[role] >= requiredLevel);
    const gate = {
        toolName,
        identity,
        requiredRole,
        decision: hasRole ? 'allow' : 'deny',
        decidedAt,
        denyReason: hasRole
            ? undefined
            : `User roles [${identity.roles.join(', ')}] insufficient for required role "${requiredRole}"`,
    };
    logGateDecision(projectPath, gate);
    return gate;
}
/**
 * Check if a given identity satisfies a required role.
 * Convenience wrapper around evaluateRoleGate for boolean checks.
 */
export function hasRequiredRole(identity, requiredRole) {
    const requiredLevel = ROLE_HIERARCHY[requiredRole];
    return identity.roles.some((role) => ROLE_HIERARCHY[role] >= requiredLevel);
}
// ---------------------------------------------------------------------------
// Private helpers
// ---------------------------------------------------------------------------
function parseRoles(raw) {
    const valid = ['owner', 'admin', 'member', 'viewer'];
    return raw
        .split(',')
        .map((r) => r.trim().toLowerCase())
        .filter((r) => valid.includes(r));
}
function logGateDecision(projectPath, gate) {
    appendAuditEntry(projectPath, 'codex_rbac_gate', {
        toolName: gate.toolName,
        userId: gate.identity.userId,
        tenantId: gate.identity.tenantId,
        requiredRole: gate.requiredRole,
        userRoles: gate.identity.roles,
        decision: gate.decision,
        denyReason: gate.denyReason,
        decidedAt: gate.decidedAt,
    });
}
//# sourceMappingURL=enterprise-auth.js.map