fix: align permission management with integrated Worker and chat operations
This commit is contained in:
@@ -247,7 +247,7 @@ pub fn subject_permission_operations() -> Vec<PermissionOperationDescriptor> {
|
||||
(
|
||||
"worker.create",
|
||||
"Create background Workers",
|
||||
"Use existing Profile-resolved Worker creation with explicit execution grants. Control and reply without duplicate target permissions await T-803.",
|
||||
"Use Profile-resolved Worker creation with explicit execution grants. Directional Backend grants separately control the created Worker and its message-only reply.",
|
||||
),
|
||||
(
|
||||
"drive.capabilities",
|
||||
@@ -342,12 +342,12 @@ pub fn subject_permission_operations() -> Vec<PermissionOperationDescriptor> {
|
||||
(
|
||||
"ticket.assignment.assign",
|
||||
"Delegate Ticket",
|
||||
"Delegate to an authorized Orchestrator principal; does not start a Worker. Subject assignment awaits T-778.",
|
||||
"Assign to an authorized Subject; current body, Ticket targets and execution grants are checked separately. Assignment does not start a Worker.",
|
||||
),
|
||||
(
|
||||
"ticket.assignment.clear",
|
||||
"Release Ticket delegation",
|
||||
"Release the observed current Orchestrator delegation; does not stop Workers.",
|
||||
"Release the observed current Subject assignment; does not stop Workers.",
|
||||
),
|
||||
(
|
||||
"ticket.assignment.update",
|
||||
@@ -817,6 +817,21 @@ pub fn subject_permission_operations() -> Vec<PermissionOperationDescriptor> {
|
||||
),
|
||||
],
|
||||
);
|
||||
add(
|
||||
K::Subject,
|
||||
&[
|
||||
(
|
||||
"chat.read",
|
||||
"Read Subject conversations",
|
||||
"Read conversations belonging to the current Subject body; membership and current ownership remain required.",
|
||||
),
|
||||
(
|
||||
"chat.send",
|
||||
"Send Subject message",
|
||||
"Explicitly publish to a permitted conversation as the current Subject body. Background Worker messaging does not grant publication.",
|
||||
),
|
||||
],
|
||||
);
|
||||
// The same exact operation may target either independent Memory domain. This
|
||||
// adds accepted kinds, never cross-kind containment or a second descriptor.
|
||||
for descriptor in &mut catalog {
|
||||
@@ -843,9 +858,8 @@ pub fn subject_permission_operations() -> Vec<PermissionOperationDescriptor> {
|
||||
resource_kinds: vec![K::Workspace, K::Ticket, K::MergeRequest, K::Job],
|
||||
availability: PermissionOperationAvailability::Available,
|
||||
});
|
||||
// T-803 owns operation-only control and name migration. No Worker resource
|
||||
// kinds are offered here: adding a per-target setting would duplicate Backend
|
||||
// control grants and dropping an old target would widen authority.
|
||||
// Operation use is Workspace-bound but never grants a target. Legacy Worker
|
||||
// scopes stay exact; management does not convert or discard their limits.
|
||||
for (operation, label) in [
|
||||
("worker.messaging", "Message Workers"),
|
||||
("worker.send_input", "Submit to Worker"),
|
||||
@@ -858,8 +872,8 @@ pub fn subject_permission_operations() -> Vec<PermissionOperationDescriptor> {
|
||||
catalog.push(PermissionOperationDescriptor {
|
||||
operation: operation.into(), label: label.into(),
|
||||
description: "Operation use permission and directional Backend target control grants are separate. Acceptance does not prove processing or success; lifecycle state conditions still apply.".into(),
|
||||
resource_kinds: vec![],
|
||||
availability: PermissionOperationAvailability::Unavailable { reason: "T-803 connection pending: current control still requires target-specific Worker permissions and uses worker.notify/worker.result. Existing restricted entries are preserved without conversion.".into() },
|
||||
resource_kinds: vec![K::WorkerOperation],
|
||||
availability: PermissionOperationAvailability::Available,
|
||||
});
|
||||
}
|
||||
for (operation, label) in [
|
||||
|
||||
@@ -115,5 +115,28 @@ fn catalog_wire_has_no_worker_target_editors_and_no_implicit_feature_grants() {
|
||||
.iter()
|
||||
.find(|d| d["operation"] == "worker.messaging")
|
||||
.unwrap();
|
||||
assert_eq!(messaging["availability"]["kind"], "unavailable");
|
||||
assert_eq!(messaging["availability"]["kind"], "available");
|
||||
assert_eq!(messaging["resource_kinds"], json!(["worker_operation"]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn managed_worker_operations_accept_only_current_operation_use_in_each_lane() {
|
||||
for name in [
|
||||
"worker.messaging",
|
||||
"worker.send_input",
|
||||
"worker.cancel",
|
||||
"worker.stop",
|
||||
"worker.restore",
|
||||
"worker.remove",
|
||||
"worker.observe",
|
||||
] {
|
||||
let p = permission(name, json!({"kind":"worker_operation","workspace_id":"w"}));
|
||||
assert!(
|
||||
validate_managed_subject_permissions(None, &update(vec![p.clone()], vec![p])).is_ok()
|
||||
);
|
||||
}
|
||||
for name in ["worker.notify", "worker.result", "worker.future"] {
|
||||
let p = permission(name, json!({"kind":"worker_operation","workspace_id":"w"}));
|
||||
assert!(validate_managed_subject_permissions(None, &update(vec![p], vec![])).is_err());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,14 +11,14 @@ mod chat_delegation_connected;
|
||||
mod chat_tests;
|
||||
#[path = "subject_job_model_supply_tests.rs"]
|
||||
mod subject_job_model_supply;
|
||||
#[path = "subject_permission_management_tests.rs"]
|
||||
mod subject_permission_management;
|
||||
#[path = "ticket_worker_messaging_connected_tests.rs"]
|
||||
mod ticket_worker_messaging_connected;
|
||||
#[path = "worker_messaging_connected_tests.rs"]
|
||||
mod worker_messaging_connected;
|
||||
#[path = "worker_operation_authorization_tests.rs"]
|
||||
mod worker_operation_authorization;
|
||||
#[path = "subject_permission_management_tests.rs"]
|
||||
mod subject_permission_management;
|
||||
use agen::tool::{Tool, ToolExecutionContext, ToolOutput};
|
||||
use server_api::authorization::{
|
||||
PermissionResource, ScopedPermission, SubjectPermissions, SubjectPermissionsUpdate,
|
||||
|
||||
@@ -71,8 +71,7 @@ fn update(screen: &Value, operation: &str, execution: Value, delegation: Value)
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn management_read_exposes_capability_and_catalog_without_enabling_pending_worker_operations()
|
||||
{
|
||||
async fn management_read_exposes_operation_use_without_duplicate_worker_target_grants() {
|
||||
let f = ManagementFixture::new().await;
|
||||
let screen = f.read().await;
|
||||
assert!(screen["permissions"].is_null());
|
||||
@@ -95,13 +94,7 @@ async fn management_read_exposes_capability_and_catalog_without_enabling_pending
|
||||
.iter()
|
||||
.find(|entry| entry["operation"] == "worker.messaging")
|
||||
.unwrap();
|
||||
assert_eq!(messaging["availability"]["kind"], "unavailable");
|
||||
assert!(
|
||||
!messaging["availability"]["reason"]
|
||||
.as_str()
|
||||
.unwrap()
|
||||
.is_empty()
|
||||
);
|
||||
assert_eq!(messaging["availability"]["kind"], "available");
|
||||
for name in [
|
||||
"worker.messaging",
|
||||
"worker.send_input",
|
||||
@@ -116,13 +109,13 @@ async fn management_read_exposes_capability_and_catalog_without_enabling_pending
|
||||
.find(|entry| entry["operation"] == name)
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
operation["availability"]["kind"], "unavailable",
|
||||
"pending Worker contracts: {operation}"
|
||||
operation["availability"]["kind"], "available",
|
||||
"implemented Worker contracts: {operation}"
|
||||
);
|
||||
assert_eq!(
|
||||
operation["resource_kinds"],
|
||||
json!([]),
|
||||
"do not offer duplicate target control grants"
|
||||
json!(["worker_operation"]),
|
||||
"operation use must not offer duplicate target control grants"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user