fix: retain byte command transport and explicit authorization fixtures
This commit is contained in:
@@ -5047,6 +5047,65 @@ mod tests {
|
||||
|
||||
const SAVED_SUBJEKTIV_SYSTEM_PROMPT: &str = "Committed saved Subject Worker system prompt";
|
||||
|
||||
// Substituted Host transport for restore/controller component tests. This
|
||||
// fixture has exactly the two activation grants requested by the saved
|
||||
// Manifest; it grants no protected operations or resources. It does not
|
||||
// exercise authenticated source derivation, Subject policy, or revocation
|
||||
// in the real Workspace Host.
|
||||
fn saved_subjektiv_authorization_fixture(
|
||||
input: server_api::authorization::OperationAuthorizationRequest,
|
||||
) -> Result<server_api::authorization::OperationAuthorizationResponse, &'static str> {
|
||||
use server_api::authorization::{OperationAuthorizationResponse, PermissionResource};
|
||||
if input.validate().is_ok()
|
||||
&& matches!(input.operation.as_str(), "subjektiv" | "workdir_catalog")
|
||||
&& input.resource == PermissionResource::Feature
|
||||
{
|
||||
Ok(OperationAuthorizationResponse {
|
||||
scope: Some(PermissionResource::Feature),
|
||||
})
|
||||
} else {
|
||||
Err("saved Subject fixture has no matching exact Feature grant")
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn saved_subjektiv_authorization_fixture_grants_only_exact_feature_activation() {
|
||||
use server_api::authorization::{OperationAuthorizationRequest, PermissionResource};
|
||||
for operation in ["subjektiv", "workdir_catalog"] {
|
||||
let response = saved_subjektiv_authorization_fixture(OperationAuthorizationRequest {
|
||||
operation: operation.into(),
|
||||
resource: PermissionResource::Feature,
|
||||
})
|
||||
.unwrap();
|
||||
assert_eq!(response.scope, Some(PermissionResource::Feature));
|
||||
}
|
||||
for (operation, resource) in [
|
||||
("ticket", PermissionResource::Feature),
|
||||
("subjektiv.journal", PermissionResource::Feature),
|
||||
("subjektiv.*", PermissionResource::Feature),
|
||||
(
|
||||
"subjektiv",
|
||||
PermissionResource::Workspace {
|
||||
workspace_id: "workspace-saved-subjektiv".into(),
|
||||
},
|
||||
),
|
||||
(
|
||||
"subjektiv.journal",
|
||||
PermissionResource::Workspace {
|
||||
workspace_id: "workspace-saved-subjektiv".into(),
|
||||
},
|
||||
),
|
||||
] {
|
||||
assert!(
|
||||
saved_subjektiv_authorization_fixture(OperationAuthorizationRequest {
|
||||
operation: operation.into(),
|
||||
resource,
|
||||
})
|
||||
.is_err()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn saved_subjektiv_policy(
|
||||
root: &Path,
|
||||
worker_ref: &WorkerRef,
|
||||
@@ -5211,6 +5270,25 @@ mod tests {
|
||||
}))
|
||||
}),
|
||||
);
|
||||
let app = app.route(
|
||||
"/api/w/workspace-saved-subjektiv/authorization/check",
|
||||
axum::routing::post(
|
||||
|axum::Json(input): axum::Json<
|
||||
server_api::authorization::OperationAuthorizationRequest,
|
||||
>| async move {
|
||||
match saved_subjektiv_authorization_fixture(input) {
|
||||
Ok(response) => (
|
||||
axum::http::StatusCode::OK,
|
||||
axum::Json(serde_json::to_value(response).unwrap()),
|
||||
),
|
||||
Err(message) => (
|
||||
axum::http::StatusCode::FORBIDDEN,
|
||||
axum::Json(serde_json::json!({ "message": message })),
|
||||
),
|
||||
}
|
||||
},
|
||||
),
|
||||
);
|
||||
tokio::spawn(async move {
|
||||
axum::serve(listener, app).await.unwrap();
|
||||
});
|
||||
@@ -6865,10 +6943,10 @@ mod tests {
|
||||
working_directory_id: workdir_id,
|
||||
capabilities: WorkdirSessionCapabilities::READ_ONLY,
|
||||
}];
|
||||
let client = WorkerWorkspaceContext::local_filesystem(Some(
|
||||
WorkspaceId::new("workspace-a").unwrap(),
|
||||
))
|
||||
.client_handle();
|
||||
// Direct local-router capability test: no Workspace Host transport is
|
||||
// installed. Use an explicit standalone binding, not an unavailable
|
||||
// Workspace-bound client pretending to authorize operations.
|
||||
let client = WorkerWorkspaceContext::no_workspace().client_handle();
|
||||
|
||||
let router = restored_workdir_router(&attachments, &logical, &[], client).unwrap();
|
||||
|
||||
@@ -7473,6 +7551,91 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
// Generic controller tests explicitly substitute the Host's non-Subject
|
||||
// legacy decision. No Subject authority is modeled here; all other requests
|
||||
// still use the original transport and its Backend/provider checks.
|
||||
#[derive(Debug)]
|
||||
struct LegacyNonSubjectAuthorizationClient(Arc<dyn WorkspaceClient>);
|
||||
|
||||
impl WorkspaceClient for LegacyNonSubjectAuthorizationClient {
|
||||
fn workspace_id(&self) -> Option<&str> {
|
||||
self.0.workspace_id()
|
||||
}
|
||||
|
||||
fn kind(&self) -> &str {
|
||||
self.0.kind()
|
||||
}
|
||||
|
||||
fn is_available(&self) -> bool {
|
||||
self.0.is_available()
|
||||
}
|
||||
|
||||
fn execute(
|
||||
&self,
|
||||
request: worker::WorkspaceRequest,
|
||||
) -> Result<worker::WorkspaceResponse, worker::WorkspaceClientError> {
|
||||
if request.method == worker::WorkspaceRequestMethod::Post
|
||||
&& request.path
|
||||
== format!(
|
||||
"/api/w/{}/authorization/check",
|
||||
self.workspace_id()
|
||||
.expect("legacy fixture needs Workspace identity")
|
||||
)
|
||||
{
|
||||
let input: server_api::authorization::OperationAuthorizationRequest =
|
||||
serde_json::from_str(request.body.as_deref().unwrap_or_default()).map_err(
|
||||
|error| worker::WorkspaceClientError::Request(error.to_string()),
|
||||
)?;
|
||||
input
|
||||
.validate()
|
||||
.map_err(worker::WorkspaceClientError::Request)?;
|
||||
return Ok(worker::WorkspaceResponse {
|
||||
status: 200,
|
||||
body: serde_json::to_string(
|
||||
&server_api::authorization::OperationAuthorizationResponse { scope: None },
|
||||
)
|
||||
.unwrap(),
|
||||
});
|
||||
}
|
||||
self.0.execute(request)
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn legacy_non_subject_authorization_fixture_preserves_other_transport_failures() {
|
||||
use server_api::authorization::PermissionResource;
|
||||
let client = LegacyNonSubjectAuthorizationClient(
|
||||
WorkerWorkspaceContext::local_filesystem(Some(WorkspaceId::new("ws-test").unwrap()))
|
||||
.client_handle(),
|
||||
);
|
||||
assert_eq!(
|
||||
client
|
||||
.authorize_operation_scope("workdir_catalog", &PermissionResource::Feature)
|
||||
.unwrap(),
|
||||
None,
|
||||
);
|
||||
assert_eq!(
|
||||
client
|
||||
.authorize_operation_scope(
|
||||
"ticket.create",
|
||||
&PermissionResource::Workspace {
|
||||
workspace_id: "ws-test".into(),
|
||||
}
|
||||
)
|
||||
.unwrap(),
|
||||
None,
|
||||
);
|
||||
assert!(
|
||||
client
|
||||
.authorize_operation("ticket.*", &PermissionResource::Feature)
|
||||
.is_err()
|
||||
);
|
||||
assert!(matches!(
|
||||
client.execute(worker::WorkspaceRequest::get("/api/w/ws-test/tickets")),
|
||||
Err(worker::WorkspaceClientError::Unavailable(_)),
|
||||
));
|
||||
}
|
||||
|
||||
struct MockFactory {
|
||||
client: MockClient,
|
||||
runtime_base: PathBuf,
|
||||
@@ -7538,6 +7701,22 @@ mod tests {
|
||||
None,
|
||||
None,
|
||||
);
|
||||
let workspace_context = if workspace_context.client().is_available()
|
||||
&& workspace_context.workspace_id().is_some()
|
||||
{
|
||||
assert!(
|
||||
!request.request.subjektiv_attached,
|
||||
"generic legacy fixture must never authorize a Subject Worker"
|
||||
);
|
||||
WorkerWorkspaceContext::with_client(
|
||||
workspace_context.workspace_id().cloned(),
|
||||
Arc::new(LegacyNonSubjectAuthorizationClient(
|
||||
workspace_context.client_handle(),
|
||||
)),
|
||||
)
|
||||
} else {
|
||||
workspace_context
|
||||
};
|
||||
let workspace_client = workspace_context.client_handle();
|
||||
self.observed_workspace_clients.lock().unwrap().push((
|
||||
workspace_client.kind().to_string(),
|
||||
|
||||
Reference in New Issue
Block a user