Codex Handbook
core/src/config/resolved_permission_profile.rs 309 lines
use codex_config::Constrained;use codex_config::ConstraintResult;use codex_protocol::models::ActivePermissionProfile;use codex_protocol::models::BUILT_IN_PERMISSION_PROFILE_DANGER_FULL_ACCESS;use codex_protocol::models::BUILT_IN_PERMISSION_PROFILE_READ_ONLY;use codex_protocol::models::BUILT_IN_PERMISSION_PROFILE_WORKSPACE;use codex_protocol::models::PermissionProfile;use codex_utils_absolute_path::AbsolutePathBuf;#[derive(Debug, Clone, Copy, PartialEq, Eq)]pub(crate) enum BuiltInPermissionProfileId {    ReadOnly,    Workspace,    DangerFullAccess,}impl BuiltInPermissionProfileId {    fn from_str(id: &str) -> Option<Self> {        match id {            BUILT_IN_PERMISSION_PROFILE_READ_ONLY => Some(Self::ReadOnly),            BUILT_IN_PERMISSION_PROFILE_WORKSPACE => Some(Self::Workspace),            BUILT_IN_PERMISSION_PROFILE_DANGER_FULL_ACCESS => Some(Self::DangerFullAccess),            _ => None,        }    }    fn as_str(self) -> &'static str {        match self {            Self::ReadOnly => BUILT_IN_PERMISSION_PROFILE_READ_ONLY,            Self::Workspace => BUILT_IN_PERMISSION_PROFILE_WORKSPACE,            Self::DangerFullAccess => BUILT_IN_PERMISSION_PROFILE_DANGER_FULL_ACCESS,        }    }}#[derive(Debug, Clone, PartialEq, Eq)]pub(crate) enum ResolvedPermissionProfile {    Legacy(LegacyPermissionProfile),    BuiltIn(BuiltInPermissionProfile),    Named(NamedPermissionProfile),}/// Trusted snapshot of a resolved permission profile.////// This is a bridge for already-resolved session/config state. It keeps the/// concrete `PermissionProfile`, optional active profile id, and/// profile-defined workspace roots together so `Permissions` can validate and/// install them atomically. It is not a resolver: callers that are handling/// user-selected profile ids should resolve those ids through config instead/// of constructing this type directly.#[derive(Debug, Clone, PartialEq, Eq)]pub struct PermissionProfileSnapshot {    resolved_permission_profile: ResolvedPermissionProfile,}#[derive(Debug, Clone, PartialEq, Eq)]pub(crate) struct LegacyPermissionProfile {    permission_profile: PermissionProfile,}#[derive(Debug, Clone, PartialEq, Eq)]pub(crate) struct BuiltInPermissionProfile {    id: BuiltInPermissionProfileId,    extends: Option<String>,    permission_profile: PermissionProfile,    profile_workspace_roots: Vec<AbsolutePathBuf>,}#[derive(Debug, Clone, PartialEq, Eq)]pub(crate) struct NamedPermissionProfile {    id: String,    extends: Option<String>,    permission_profile: PermissionProfile,    profile_workspace_roots: Vec<AbsolutePathBuf>,}impl ResolvedPermissionProfile {    pub(crate) fn from_active_profile(        permission_profile: PermissionProfile,        active_permission_profile: Option<ActivePermissionProfile>,        profile_workspace_roots: Vec<AbsolutePathBuf>,    ) -> Self {        let Some(active_permission_profile) = active_permission_profile else {            return Self::legacy(permission_profile);        };        let ActivePermissionProfile { id, extends } = active_permission_profile;        if let Some(built_in_id) = BuiltInPermissionProfileId::from_str(&id) {            Self::BuiltIn(BuiltInPermissionProfile {                id: built_in_id,                extends,                permission_profile,                profile_workspace_roots,            })        } else {            Self::Named(NamedPermissionProfile {                id,                extends,                permission_profile,                profile_workspace_roots,            })        }    }    pub(crate) fn legacy(permission_profile: PermissionProfile) -> Self {        Self::Legacy(LegacyPermissionProfile { permission_profile })    }    pub(crate) fn permission_profile(&self) -> &PermissionProfile {        match self {            Self::Legacy(profile) => &profile.permission_profile,            Self::BuiltIn(profile) => &profile.permission_profile,            Self::Named(profile) => &profile.permission_profile,        }    }    pub(crate) fn active_permission_profile(&self) -> Option<ActivePermissionProfile> {        match self {            Self::Legacy(_) => None,            Self::BuiltIn(profile) => Some(ActivePermissionProfile {                id: profile.id.as_str().to_string(),                extends: profile.extends.clone(),            }),            Self::Named(profile) => Some(ActivePermissionProfile {                id: profile.id.clone(),                extends: profile.extends.clone(),            }),        }    }    pub(crate) fn profile_workspace_roots(&self) -> &[AbsolutePathBuf] {        match self {            Self::Legacy(_) => &[],            Self::BuiltIn(profile) => &profile.profile_workspace_roots,            Self::Named(profile) => &profile.profile_workspace_roots,        }    }}impl PermissionProfileSnapshot {    /// Create a snapshot with no active profile id.    ///    /// Prefer this only for legacy data or local overrides that genuinely do    /// not have a named/built-in profile identity. Using this for a built-in or    /// named profile will intentionally clear the active profile metadata.    pub fn legacy(permission_profile: PermissionProfile) -> Self {        Self {            resolved_permission_profile: ResolvedPermissionProfile::legacy(permission_profile),        }    }    /// Create a snapshot for a known active profile id.    ///    /// Use this only after a trusted caller has already resolved the active id    /// to the supplied concrete `PermissionProfile`. This constructor does not    /// verify that the id and profile match; `Permissions` will still enforce    /// configured permission constraints when the snapshot is installed.    pub fn active(        permission_profile: PermissionProfile,        active_permission_profile: ActivePermissionProfile,    ) -> Self {        Self::active_with_profile_workspace_roots(            permission_profile,            active_permission_profile,            Vec::new(),        )    }    /// Create a snapshot for a known active profile id with profile roots.    ///    /// As with `active`, the caller is responsible for passing the concrete    /// profile and active id that were resolved together. Use this variant when    /// the selected profile declared workspace roots that should remain    /// distinct from turn-scoped runtime workspace roots.    pub fn active_with_profile_workspace_roots(        permission_profile: PermissionProfile,        active_permission_profile: ActivePermissionProfile,        profile_workspace_roots: Vec<AbsolutePathBuf>,    ) -> Self {        Self {            resolved_permission_profile: ResolvedPermissionProfile::from_active_profile(                permission_profile,                Some(active_permission_profile),                profile_workspace_roots,            ),        }    }    /// Reconstruct a trusted snapshot from session state.    ///    /// This is intended for session responses emitted by core, where the    /// concrete profile and active profile id were captured together. Avoid    /// using this as a shortcut for arbitrary user input because mismatched    /// arguments can still misrepresent the active profile identity.    pub fn from_session_snapshot(        permission_profile: PermissionProfile,        active_permission_profile: Option<ActivePermissionProfile>,    ) -> Self {        match active_permission_profile {            Some(active_permission_profile) => {                Self::active(permission_profile, active_permission_profile)            }            None => Self::legacy(permission_profile),        }    }    /// Borrow the concrete permission profile captured in this snapshot.    pub fn permission_profile(&self) -> &PermissionProfile {        self.resolved_permission_profile.permission_profile()    }    /// Return the active profile id captured in this snapshot, if any.    pub fn active_permission_profile(&self) -> Option<ActivePermissionProfile> {        self.resolved_permission_profile.active_permission_profile()    }    /// Borrow profile-declared workspace roots captured in this snapshot.    pub fn profile_workspace_roots(&self) -> &[AbsolutePathBuf] {        self.resolved_permission_profile.profile_workspace_roots()    }    pub(crate) fn into_resolved_permission_profile(self) -> ResolvedPermissionProfile {        self.resolved_permission_profile    }}#[derive(Debug, Clone, PartialEq)]pub(crate) struct PermissionProfileState {    resolved_permission_profile: Constrained<ResolvedPermissionProfile>,}impl PermissionProfileState {    pub(crate) fn from_constrained_legacy(        constrained_permission_profile: Constrained<PermissionProfile>,    ) -> ConstraintResult<Self> {        let resolved =            ResolvedPermissionProfile::legacy(constrained_permission_profile.get().clone());        Self::from_constrained_resolved(constrained_permission_profile, resolved)    }    pub(crate) fn from_constrained_active_profile(        constrained_permission_profile: Constrained<PermissionProfile>,        active_permission_profile: Option<ActivePermissionProfile>,        profile_workspace_roots: Vec<AbsolutePathBuf>,    ) -> ConstraintResult<Self> {        let resolved = ResolvedPermissionProfile::from_active_profile(            constrained_permission_profile.get().clone(),            active_permission_profile,            profile_workspace_roots,        );        Self::from_constrained_resolved(constrained_permission_profile, resolved)    }    pub(crate) fn from_constrained_resolved(        constrained_permission_profile: Constrained<PermissionProfile>,        resolved_permission_profile: ResolvedPermissionProfile,    ) -> ConstraintResult<Self> {        let permission_profile_constraint = constrained_permission_profile;        let resolved_permission_profile = Constrained::new(            resolved_permission_profile,            move |candidate: &ResolvedPermissionProfile| {                permission_profile_constraint.can_set(candidate.permission_profile())            },        )?;        Ok(Self {            resolved_permission_profile,        })    }    pub(crate) fn permission_profile(&self) -> &PermissionProfile {        self.resolved_permission_profile.get().permission_profile()    }    pub(crate) fn active_permission_profile(&self) -> Option<ActivePermissionProfile> {        self.resolved_permission_profile            .get()            .active_permission_profile()    }    pub(crate) fn profile_workspace_roots(&self) -> &[AbsolutePathBuf] {        self.resolved_permission_profile            .get()            .profile_workspace_roots()    }    pub(crate) fn can_set_legacy_permission_profile(        &self,        permission_profile: &PermissionProfile,    ) -> ConstraintResult<()> {        let candidate = ResolvedPermissionProfile::legacy(permission_profile.clone());        self.resolved_permission_profile.can_set(&candidate)    }    pub(crate) fn set_legacy_permission_profile(        &mut self,        permission_profile: PermissionProfile,    ) -> ConstraintResult<()> {        self.resolved_permission_profile            .set(ResolvedPermissionProfile::legacy(permission_profile))    }    pub(crate) fn set_permission_profile_snapshot(        &mut self,        snapshot: PermissionProfileSnapshot,    ) -> ConstraintResult<()> {        self.resolved_permission_profile            .set(snapshot.into_resolved_permission_profile())    }}