Jalon vert regroupant deux chantiers entrelacés dans le working tree, indissociables au niveau fichier mais tous deux verts (cargo test --workspace + tests frontend permissions au vert). Permissions — voie « projection CLI » (advisory), complète : - LP0 domaine pur : modèle PermissionSet/EffectivePermissions, resolve deny-wins + postures Allow<Ask<Deny (crates/domain/src/permission.rs). - LP1 store : FsPermissionStore (.ideai/permissions.json). - LP2 use cases : Get/Update project, Update agent override, Resolve. - LP3 projecteurs Claude/Codex (settings.local.json / config.toml), câblage launch-path + PermissionProjectorRegistry, nettoyage des fichiers Replace orphelins au swap de profil (LP3-4), composition root + commandes Tauri, UI PermissionsPanel (projet + override agent). - ports.rs : PermissionStore + FileSystem::remove_file (cleanup au swap). Reste ouvert (hors scope, marqué dans le code) : LP4 enforcement OS airtight (Landlock fichiers) + résumé de permissions injecté. Inclut aussi le chantier Codex/input/sessions structurées en cours (McpConfigStrategy, StructuredAdapter, gestion d'input) partageant les mêmes fichiers (lifecycle.rs, commands.rs, dto.rs, state.rs). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
151 lines
4.3 KiB
Rust
151 lines
4.3 KiB
Rust
//! Permission use cases.
|
|
//!
|
|
//! This module stays at the application boundary: it loads the project
|
|
//! permission document through [`PermissionStore`], applies simple mutations, and
|
|
//! delegates all merge semantics to the pure domain model.
|
|
|
|
use std::sync::Arc;
|
|
|
|
use domain::ports::PermissionStore;
|
|
use domain::{AgentId, EffectivePermissions, PermissionSet, Project, ProjectPermissions};
|
|
|
|
use crate::error::AppError;
|
|
|
|
/// Reads the full project permission document.
|
|
pub struct GetProjectPermissions {
|
|
store: Arc<dyn PermissionStore>,
|
|
}
|
|
|
|
impl GetProjectPermissions {
|
|
/// Builds the use case.
|
|
#[must_use]
|
|
pub fn new(store: Arc<dyn PermissionStore>) -> Self {
|
|
Self { store }
|
|
}
|
|
|
|
/// Executes the read.
|
|
pub async fn execute(
|
|
&self,
|
|
input: GetProjectPermissionsInput,
|
|
) -> Result<GetProjectPermissionsOutput, AppError> {
|
|
let permissions = self.store.load_permissions(&input.project).await?;
|
|
Ok(GetProjectPermissionsOutput { permissions })
|
|
}
|
|
}
|
|
|
|
/// Input for [`GetProjectPermissions`].
|
|
pub struct GetProjectPermissionsInput {
|
|
/// Target project.
|
|
pub project: Project,
|
|
}
|
|
|
|
/// Output for [`GetProjectPermissions`].
|
|
pub struct GetProjectPermissionsOutput {
|
|
/// Persisted permission document.
|
|
pub permissions: ProjectPermissions,
|
|
}
|
|
|
|
/// Replaces the project default policy.
|
|
pub struct UpdateProjectPermissions {
|
|
store: Arc<dyn PermissionStore>,
|
|
}
|
|
|
|
impl UpdateProjectPermissions {
|
|
/// Builds the use case.
|
|
#[must_use]
|
|
pub fn new(store: Arc<dyn PermissionStore>) -> Self {
|
|
Self { store }
|
|
}
|
|
|
|
/// Executes the mutation.
|
|
pub async fn execute(
|
|
&self,
|
|
input: UpdateProjectPermissionsInput,
|
|
) -> Result<GetProjectPermissionsOutput, AppError> {
|
|
let mut doc = self.store.load_permissions(&input.project).await?;
|
|
doc.set_project_defaults(input.permissions);
|
|
self.store.save_permissions(&input.project, &doc).await?;
|
|
Ok(GetProjectPermissionsOutput { permissions: doc })
|
|
}
|
|
}
|
|
|
|
/// Input for [`UpdateProjectPermissions`].
|
|
pub struct UpdateProjectPermissionsInput {
|
|
/// Target project.
|
|
pub project: Project,
|
|
/// New project default policy. `None` removes project defaults.
|
|
pub permissions: Option<PermissionSet>,
|
|
}
|
|
|
|
/// Replaces one agent override.
|
|
pub struct UpdateAgentPermissions {
|
|
store: Arc<dyn PermissionStore>,
|
|
}
|
|
|
|
impl UpdateAgentPermissions {
|
|
/// Builds the use case.
|
|
#[must_use]
|
|
pub fn new(store: Arc<dyn PermissionStore>) -> Self {
|
|
Self { store }
|
|
}
|
|
|
|
/// Executes the mutation.
|
|
pub async fn execute(
|
|
&self,
|
|
input: UpdateAgentPermissionsInput,
|
|
) -> Result<GetProjectPermissionsOutput, AppError> {
|
|
let mut doc = self.store.load_permissions(&input.project).await?;
|
|
doc.set_agent_permissions(input.agent_id, input.permissions);
|
|
self.store.save_permissions(&input.project, &doc).await?;
|
|
Ok(GetProjectPermissionsOutput { permissions: doc })
|
|
}
|
|
}
|
|
|
|
/// Input for [`UpdateAgentPermissions`].
|
|
pub struct UpdateAgentPermissionsInput {
|
|
/// Target project.
|
|
pub project: Project,
|
|
/// Target agent.
|
|
pub agent_id: AgentId,
|
|
/// New agent policy. `None` removes the override.
|
|
pub permissions: Option<PermissionSet>,
|
|
}
|
|
|
|
/// Resolves effective permissions for one agent.
|
|
pub struct ResolveAgentPermissions {
|
|
store: Arc<dyn PermissionStore>,
|
|
}
|
|
|
|
impl ResolveAgentPermissions {
|
|
/// Builds the use case.
|
|
#[must_use]
|
|
pub fn new(store: Arc<dyn PermissionStore>) -> Self {
|
|
Self { store }
|
|
}
|
|
|
|
/// Executes the resolution.
|
|
pub async fn execute(
|
|
&self,
|
|
input: ResolveAgentPermissionsInput,
|
|
) -> Result<ResolveAgentPermissionsOutput, AppError> {
|
|
let doc = self.store.load_permissions(&input.project).await?;
|
|
Ok(ResolveAgentPermissionsOutput {
|
|
effective: doc.resolve_for(input.agent_id),
|
|
})
|
|
}
|
|
}
|
|
|
|
/// Input for [`ResolveAgentPermissions`].
|
|
pub struct ResolveAgentPermissionsInput {
|
|
/// Target project.
|
|
pub project: Project,
|
|
/// Target agent.
|
|
pub agent_id: AgentId,
|
|
}
|
|
|
|
/// Output for [`ResolveAgentPermissions`].
|
|
pub struct ResolveAgentPermissionsOutput {
|
|
/// Resolved policy, or `None` when neither project nor agent policy exists.
|
|
pub effective: Option<EffectivePermissions>,
|
|
}
|