feat(permissions): expose network permission state (#103)

This commit is contained in:
2026-07-25 23:05:55 +02:00
parent e8731834f4
commit 3047dc9195
31 changed files with 2080 additions and 94 deletions

View File

@ -14,19 +14,21 @@ use application::{
CloseProjectInput, CreateAgentInput, CreateLayoutInput, CreateMemoryInput, CreateSkillInput,
DeleteAgentInput, DeleteEmbedderProfileInput, DeleteLayoutInput, DeleteMemoryInput,
DeleteSkillInput, DeleteTemplateInput, DetectAgentDriftInput, GetMemoryInput,
GetProjectWorkStateInput, GitBranchesInput, GitCheckoutInput, GitCommitInput, GitGraphInput,
GitInitInput, GitLogInput, GitStagePathInput, GitStatusInput, InspectConversationInput,
LaunchAgentInput, ListAgentsInput, ListDevicesInput, ListLayoutsInput, ListMemoriesInput,
ListResumableAgentsInput, ListSkillsInput, LiveSessions, LoadLayoutInput, McpRuntime,
MutateLayoutInput, OpenProjectInput, ReadAgentContextInput, ReadConversationPageInput,
ReadMcpToolPermissionsInput, ReadMemoryIndexInput, ReadProjectContextInput, RecallMemoryInput,
ReconcileLayoutsInput, ReconcileLiveStateInput, RenameDeviceInput, RenameLayoutInput,
ResolveAgentPermissionsInput, ResolveMemoryLinksInput, RevokeDeviceInput,
GetProjectSystemPermissionsInput, GetProjectWorkStateInput, GitBranchesInput, GitCheckoutInput,
GitCommitInput, GitGraphInput, GitInitInput, GitLogInput, GitStagePathInput, GitStatusInput,
InspectConversationInput, LaunchAgentInput, ListAgentsInput, ListDevicesInput,
ListLayoutsInput, ListMemoriesInput, ListResumableAgentsInput, ListSkillsInput, LiveSessions,
LoadLayoutInput, McpRuntime, MutateLayoutInput, OpenProjectInput, ReadAgentContextInput,
ReadConversationPageInput, ReadMcpToolPermissionsInput, ReadMemoryIndexInput,
ReadProjectContextInput, RecallMemoryInput, ReconcileLayoutsInput, ReconcileLiveStateInput,
RenameDeviceInput, RenameLayoutInput, ResolveAgentPermissionsInput,
ResolveAgentSystemPermissionsInput, ResolveMemoryLinksInput, RevokeDeviceInput,
RotateConversationLogInput, SetActiveLayoutInput, SnapshotRunningAgentsInput,
StopLiveAgentInput, SyncAgentWithTemplateInput, UnassignSkillFromAgentInput,
UpdateAgentContextInput, UpdateAgentMcpToolPermissionsInput, UpdateAgentPermissionsInput,
UpdateMemoryInput, UpdateProjectContextInput, UpdateProjectMcpToolPermissionsInput,
UpdateProjectPermissionsInput, UpdateSkillInput,
UpdateAgentSystemPermissionsInput, UpdateMemoryInput, UpdateProjectContextInput,
UpdateProjectMcpToolPermissionsInput, UpdateProjectPermissionsInput,
UpdateProjectSystemPermissionsInput, UpdateSkillInput,
};
use domain::ports::ModelServerRuntime;
use domain::ports::PtyHandle;
@ -52,19 +54,22 @@ use crate::dto::{
MemoryDto, MemoryIndexDto, MemoryLinksDto, MemoryListDto, ModelServerConfigDto,
ModelServerConfigListDto, OpenCodeProviderListDto, OpenTerminalRequestDto,
PreviewModelServerCommandDto, ProfileDto, ProfileListDto, ProjectDto, ProjectListDto,
ProjectMcpToolPermissionsDto, ProjectPermissionsDto, ProjectWorkStateDto,
ReadAgentContextResponseDto, ReadConversationPageRequestDto, ReattachChatDto,
ReattachResultDto, RecallMemoryRequestDto, RenameLayoutRequestDto, ReplyChunk,
ResizeTerminalRequestDto, ResolveAgentPermissionsRequestDto, ResumableAgentListDto,
SaveEmbedderProfileRequestDto, SaveModelServerRequestDto,
ProjectMcpToolPermissionsDto, ProjectPermissionsDto, ProjectSystemPermissionsDto,
ProjectWorkStateDto, ReadAgentContextResponseDto, ReadConversationPageRequestDto,
ReattachChatDto, ReattachResultDto, RecallMemoryRequestDto, RenameLayoutRequestDto, ReplyChunk,
ResizeTerminalRequestDto, ResolveAgentPermissionsRequestDto,
ResolveAgentSystemPermissionsRequestDto, ResolvedAgentSystemPermissionsDto,
ResumableAgentListDto, SaveEmbedderProfileRequestDto, SaveModelServerRequestDto,
SaveOpenCodeProviderProfileRequestDto, SaveProfileRequestDto, SetActiveLayoutRequestDto,
SetActiveLayoutResultDto, SkillDto, SkillListDto, StopLiveAgentRequestDto,
StopLiveAgentResponseDto, SyncAgentWithTemplateRequestDto, SyncResultDto, TemplateDto,
TemplateListDto, TerminalClosedDto, TerminalSessionDto, TurnPageDto, UnassignSkillRequestDto,
UpdateAgentContextRequestDto, UpdateAgentMcpToolPermissionsRequestDto,
UpdateAgentPermissionsRequestDto, UpdateMemoryRequestDto, UpdateProjectContextRequestDto,
UpdateAgentPermissionsRequestDto, UpdateAgentSystemPermissionsRequestDto,
UpdateMemoryRequestDto, UpdateProjectContextRequestDto,
UpdateProjectMcpToolPermissionsRequestDto, UpdateProjectPermissionsRequestDto,
UpdateSkillRequestDto, UpdateTemplateRequestDto, WriteTerminalRequestDto,
UpdateProjectSystemPermissionsRequestDto, UpdateSkillRequestDto, UpdateTemplateRequestDto,
WriteTerminalRequestDto,
};
use crate::embedded_server::{
EmbeddedServerStatusDto, ServerExposurePreviewDto, ServerExposureSettingsDto,
@ -549,6 +554,87 @@ pub async fn resolve_agent_permissions(
.map_err(ErrorDto::from)
}
/// `get_project_system_permissions` — read `.ideai/system-permissions.json`.
///
/// # Errors
/// Returns an [`ErrorDto`] on invalid project id or store failure.
#[tauri::command]
pub async fn get_project_system_permissions(
project_id: String,
state: State<'_, AppState>,
) -> Result<ProjectSystemPermissionsDto, ErrorDto> {
let project = resolve_project(&project_id, &state).await?;
state
.get_project_system_permissions
.execute(GetProjectSystemPermissionsInput { project })
.await
.map(|out| ProjectSystemPermissionsDto(out.permissions))
.map_err(ErrorDto::from)
}
/// `update_project_system_permissions` — replace project default system permissions.
///
/// # Errors
/// Returns an [`ErrorDto`] on invalid project id or store failure.
#[tauri::command]
pub async fn update_project_system_permissions(
request: UpdateProjectSystemPermissionsRequestDto,
state: State<'_, AppState>,
) -> Result<ProjectSystemPermissionsDto, ErrorDto> {
let project = resolve_project(&request.project_id, &state).await?;
state
.update_project_system_permissions
.execute(UpdateProjectSystemPermissionsInput {
project,
permissions: request.permissions,
})
.await
.map(|out| ProjectSystemPermissionsDto(out.permissions))
.map_err(ErrorDto::from)
}
/// `update_agent_system_permissions` — replace or remove one agent system override.
///
/// # Errors
/// Returns an [`ErrorDto`] on invalid ids or store failure.
#[tauri::command]
pub async fn update_agent_system_permissions(
request: UpdateAgentSystemPermissionsRequestDto,
state: State<'_, AppState>,
) -> Result<ProjectSystemPermissionsDto, ErrorDto> {
let project = resolve_project(&request.project_id, &state).await?;
let agent_id = parse_agent_id(&request.agent_id)?;
state
.update_agent_system_permissions
.execute(UpdateAgentSystemPermissionsInput {
project,
agent_id,
permissions: request.permissions,
})
.await
.map(|out| ProjectSystemPermissionsDto(out.permissions))
.map_err(ErrorDto::from)
}
/// `resolve_agent_system_permissions` — resolve wanted plus runtime-constrained system permissions.
///
/// # Errors
/// Returns an [`ErrorDto`] on invalid ids or store/probe failure.
#[tauri::command]
pub async fn resolve_agent_system_permissions(
request: ResolveAgentSystemPermissionsRequestDto,
state: State<'_, AppState>,
) -> Result<ResolvedAgentSystemPermissionsDto, ErrorDto> {
let project = resolve_project(&request.project_id, &state).await?;
let agent_id = parse_agent_id(&request.agent_id)?;
state
.resolve_agent_system_permissions
.execute(ResolveAgentSystemPermissionsInput { project, agent_id })
.await
.map(|out| ResolvedAgentSystemPermissionsDto(out.permissions))
.map_err(ErrorDto::from)
}
/// `get_mcp_tool_permissions` — read `.ideai/mcp-tool-permissions.json` plus catalogue.
///
/// # Errors

View File

@ -229,6 +229,10 @@ pub fn run() {
commands::update_project_permissions,
commands::update_agent_permissions,
commands::resolve_agent_permissions,
commands::get_project_system_permissions,
commands::update_project_system_permissions,
commands::update_agent_system_permissions,
commands::resolve_agent_system_permissions,
commands::get_mcp_tool_permissions,
commands::update_project_mcp_tool_permissions,
commands::update_agent_mcp_tool_permissions,

View File

@ -0,0 +1,69 @@
use app_tauri_lib::dto::{
ProjectSystemPermissionsDto, ResolvedAgentSystemPermissionsDto,
UpdateProjectSystemPermissionsRequestDto,
};
use domain::{
NetworkPolicy, ProjectSystemPermissions, ResolvedAgentSystemPermissions, RuntimeLock,
RuntimeLockState, SystemPermissionControl, SystemPermissionControlMode, SystemPermissionSet,
};
use serde_json::json;
#[test]
fn project_system_permissions_dto_serializes_network_policy_contract() {
let dto = ProjectSystemPermissionsDto(ProjectSystemPermissions::new(
Some(SystemPermissionSet::new(Some(NetworkPolicy::Ask))),
vec![],
));
let value = serde_json::to_value(dto).unwrap();
assert_eq!(
value,
json!({
"version": 1,
"projectDefault": {
"network": "ask",
},
})
);
}
#[test]
fn update_project_system_permissions_request_deserializes_allow_deny_ask() {
let dto: UpdateProjectSystemPermissionsRequestDto = serde_json::from_value(json!({
"projectId": "project",
"permissions": {
"network": "allow",
},
}))
.unwrap();
assert_eq!(
dto.permissions.and_then(|permissions| permissions.network),
Some(NetworkPolicy::Allow)
);
}
#[test]
fn resolved_agent_system_permissions_dto_carries_runtime_lock_and_read_only_control() {
let dto = ResolvedAgentSystemPermissionsDto(ResolvedAgentSystemPermissions {
wanted: Some(NetworkPolicy::Allow),
effective: NetworkPolicy::Deny,
runtime_lock: RuntimeLock {
state: RuntimeLockState::Locked,
source: Some("external-runtime".to_owned()),
reason: Some("not inspectable".to_owned()),
},
control: SystemPermissionControl {
mode: SystemPermissionControlMode::ReadOnly,
reason: Some("not editable".to_owned()),
},
});
let value = serde_json::to_value(dto).unwrap();
assert_eq!(value["wanted"], "allow");
assert_eq!(value["effective"], "deny");
assert_eq!(value["runtimeLock"]["state"], "locked");
assert_eq!(value["control"]["mode"], "readOnly");
}