Files
IdeA/crates/application/src/permission.rs
Blomios 27597eb64e feat(permissions): voie projection CLI (LP0→LP3) + checkpoint Codex/input
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>
2026-06-15 20:39:18 +02:00

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>,
}