//! 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, } impl GetProjectPermissions { /// Builds the use case. #[must_use] pub fn new(store: Arc) -> Self { Self { store } } /// Executes the read. pub async fn execute( &self, input: GetProjectPermissionsInput, ) -> Result { 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, } impl UpdateProjectPermissions { /// Builds the use case. #[must_use] pub fn new(store: Arc) -> Self { Self { store } } /// Executes the mutation. pub async fn execute( &self, input: UpdateProjectPermissionsInput, ) -> Result { 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, } /// Replaces one agent override. pub struct UpdateAgentPermissions { store: Arc, } impl UpdateAgentPermissions { /// Builds the use case. #[must_use] pub fn new(store: Arc) -> Self { Self { store } } /// Executes the mutation. pub async fn execute( &self, input: UpdateAgentPermissionsInput, ) -> Result { 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, } /// Resolves effective permissions for one agent. pub struct ResolveAgentPermissions { store: Arc, } impl ResolveAgentPermissions { /// Builds the use case. #[must_use] pub fn new(store: Arc) -> Self { Self { store } } /// Executes the resolution. pub async fn execute( &self, input: ResolveAgentPermissionsInput, ) -> Result { 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, }