État d'intégration confiné à la branche batch. Les tickets #119 (skills → capacités agent découvrables), #122 (override permissions par défaut), #131 (effort par agent/presets) et #132 (outil MCP d'édition du contexte projet) sont verts en périmètre. Le sprint plugins multi-fichiers ESM / persistance plugin-owned (#135/#136/#139) est co-implémenté dans les MÊMES fichiers de câblage (frontend/src/ports/index.ts, backend/src/lib.rs, domain/ports.rs, backend/dto.rs), inséparable sans staging interactif (indisponible ici). Commit unique volontaire : préserve l'état vert QA sans découpe hunk risquée. NON mergé vers develop tant que #137 (QA e2e plugins) n'est pas vert. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
157 lines
4.5 KiB
Rust
157 lines
4.5 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, PermissionShadowReport, 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),
|
|
shadowed: doc.shadow_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>,
|
|
/// Diagnostic report for agent-level allows shadowed by project defaults.
|
|
pub shadowed: PermissionShadowReport,
|
|
}
|