diff --git a/crates/gateway/src/access.rs b/crates/gateway/src/access.rs index 6d30354..c675362 100644 --- a/crates/gateway/src/access.rs +++ b/crates/gateway/src/access.rs @@ -93,8 +93,7 @@ impl AccessControl { self.roles.write().await.insert(role.agent_name.clone(), role); } - /// Get an agent's role. - #[allow(dead_code)] + /// Get an agent's role. Called by `GET /access/roles/{agent}`. pub async fn get_role(&self, agent: &str) -> Option { self.roles.read().await.get(agent).cloned() } @@ -152,7 +151,9 @@ impl AccessControl { log[start..].iter().rev().cloned().collect() } - #[allow(dead_code)] + /// Reports whether access-control enforcement is active. + /// Called by `GET /access/enabled` — ops tooling / dashboards poll + /// this to confirm the auth posture of the running gateway. pub fn is_enabled(&self) -> bool { self.enabled } diff --git a/crates/gateway/src/access_service.rs b/crates/gateway/src/access_service.rs index b0dd672..1bf3158 100644 --- a/crates/gateway/src/access_service.rs +++ b/crates/gateway/src/access_service.rs @@ -1,6 +1,6 @@ use axum::{ Json, Router, - extract::{Query, State}, + extract::{Path, Query, State}, http::StatusCode, response::IntoResponse, routing::{get, post}, @@ -13,6 +13,12 @@ pub fn router(ac: AccessControl) -> Router { Router::new() .route("/roles", get(list_roles)) .route("/roles", post(set_role)) + // Scrum iter 11 / P13-001 finding: get_role was #[allow(dead_code)] + // because nothing called it — dead until exposed. Route activates it. + // Returns 404 when the agent isn't registered so clients can + // distinguish "missing role" from "access denied." + .route("/roles/{agent}", get(get_role)) + .route("/enabled", get(enabled_status)) .route("/audit", get(query_audit)) .route("/check", post(check_access)) .with_state(ac) @@ -60,3 +66,17 @@ async fn check_access( "allowed": allowed, })) } + +async fn get_role( + State(ac): State, + Path(agent): Path, +) -> impl IntoResponse { + match ac.get_role(&agent).await { + Some(role) => Ok(Json(role)), + None => Err((StatusCode::NOT_FOUND, format!("no role registered for agent '{agent}'"))), + } +} + +async fn enabled_status(State(ac): State) -> impl IntoResponse { + Json(serde_json::json!({ "enabled": ac.is_enabled() })) +}