feat: add platform access foundation

This commit is contained in:
a.tolmachev
2026-03-29 23:17:05 +03:00
parent 9fb69c1571
commit 587584a8bf
19 changed files with 1058 additions and 34 deletions
+133 -1
View File
@@ -5,6 +5,10 @@ use axum::{
use crate::{
routes::{
access::{
create_invitation, create_platform_api_key, delete_invitation, delete_platform_api_key,
list_invitations, list_memberships, list_platform_api_keys, revoke_platform_api_key,
},
agents::{
create_agent, get_agent, get_agent_version, list_agents, publish_agent,
save_agent_bindings,
@@ -73,7 +77,28 @@ pub fn build_app(state: AppState) -> Router {
"/auth-profiles",
get(list_auth_profiles).post(create_auth_profile),
)
.route("/auth-profiles/{auth_profile_id}", get(get_auth_profile));
.route("/auth-profiles/{auth_profile_id}", get(get_auth_profile))
.route("/members", get(list_memberships))
.route(
"/invitations",
get(list_invitations).post(create_invitation),
)
.route(
"/invitations/{invitation_id}",
axum::routing::delete(delete_invitation),
)
.route(
"/platform-api-keys",
get(list_platform_api_keys).post(create_platform_api_key),
)
.route(
"/platform-api-keys/{key_id}/revoke",
post(revoke_platform_api_key),
)
.route(
"/platform-api-keys/{key_id}",
axum::routing::delete(delete_platform_api_key),
);
let admin_router = Router::new()
.route("/workspaces", get(list_workspaces).post(create_workspace))
@@ -262,6 +287,113 @@ mod tests {
assert_eq!(published["published_version"], 1);
}
#[tokio::test]
async fn manages_platform_access_resources() {
let registry = test_registry().await;
let storage_root = test_storage_root("platform_access");
let base_url = spawn_admin_api(build_test_app(registry, storage_root)).await;
let client = reqwest::Client::new();
let members = client
.get(format!("{base_url}/members"))
.send()
.await
.unwrap()
.json::<Value>()
.await
.unwrap();
let created_invitation = client
.post(format!("{base_url}/invitations"))
.json(&json!({
"email": "operator@example.com",
"role": "operator"
}))
.send()
.await
.unwrap()
.json::<Value>()
.await
.unwrap();
let invitation_id = created_invitation["invitation"]["invitation"]["id"]
.as_str()
.unwrap()
.to_owned();
let invitations = client
.get(format!("{base_url}/invitations"))
.send()
.await
.unwrap()
.json::<Value>()
.await
.unwrap();
let created_key = client
.post(format!("{base_url}/platform-api-keys"))
.json(&json!({
"name": "workspace-operator",
"scopes": ["read", "write"]
}))
.send()
.await
.unwrap()
.json::<Value>()
.await
.unwrap();
let key_id = created_key["api_key"]["api_key"]["id"]
.as_str()
.unwrap()
.to_owned();
let listed_keys = client
.get(format!("{base_url}/platform-api-keys"))
.send()
.await
.unwrap()
.json::<Value>()
.await
.unwrap();
let revoke_status = client
.post(format!("{base_url}/platform-api-keys/{key_id}/revoke"))
.send()
.await
.unwrap()
.status();
let delete_invitation_status = client
.delete(format!("{base_url}/invitations/{invitation_id}"))
.send()
.await
.unwrap()
.status();
let delete_key_status = client
.delete(format!("{base_url}/platform-api-keys/{key_id}"))
.send()
.await
.unwrap()
.status();
assert_eq!(members["items"][0]["role"], "owner");
assert_eq!(
created_invitation["invitation"]["invitation"]["status"],
"pending"
);
assert!(
created_invitation["invite_token"]
.as_str()
.unwrap()
.starts_with("invite_")
);
assert_eq!(
invitations["items"][0]["invitation"]["email"],
"operator@example.com"
);
assert_eq!(
listed_keys["items"][0]["api_key"]["name"],
"workspace-operator"
);
assert!(created_key["secret"].as_str().unwrap().starts_with("crk_"));
assert_eq!(revoke_status, reqwest::StatusCode::NO_CONTENT);
assert_eq!(delete_invitation_status, reqwest::StatusCode::NO_CONTENT);
assert_eq!(delete_key_status, reqwest::StatusCode::NO_CONTENT);
}
#[tokio::test]
async fn creates_publishes_and_tests_graphql_operation() {
let registry = test_registry().await;