feat: connect workspace access lifecycle to admin api
This commit is contained in:
@@ -7,8 +7,9 @@ use crate::{
|
||||
auth::{require_session, require_workspace_session},
|
||||
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,
|
||||
create_invitation, create_platform_api_key, delete_invitation, delete_membership,
|
||||
delete_platform_api_key, delete_workspace, export_workspace, list_invitations,
|
||||
list_memberships, list_platform_api_keys, revoke_platform_api_key, update_membership,
|
||||
},
|
||||
agents::{
|
||||
create_agent, delete_agent, get_agent, get_agent_version, list_agents, publish_agent,
|
||||
@@ -94,11 +95,16 @@ pub fn build_app(state: AppState) -> Router {
|
||||
)
|
||||
.route("/auth-profiles/{auth_profile_id}", get(get_auth_profile))
|
||||
.route("/members", get(list_memberships))
|
||||
.route(
|
||||
"/members/{user_id}",
|
||||
axum::routing::patch(update_membership).delete(delete_membership),
|
||||
)
|
||||
.route(
|
||||
"/invitations",
|
||||
get(list_invitations).post(create_invitation),
|
||||
)
|
||||
.route("/invitations/{invitation_id}", delete(delete_invitation))
|
||||
.route("/export", get(export_workspace))
|
||||
.route(
|
||||
"/platform-api-keys",
|
||||
get(list_platform_api_keys).post(create_platform_api_key),
|
||||
@@ -127,7 +133,9 @@ pub fn build_app(state: AppState) -> Router {
|
||||
let workspace_scoped_router = Router::new()
|
||||
.route(
|
||||
"/workspaces/{workspace_id}",
|
||||
get(get_workspace).patch(update_workspace),
|
||||
get(get_workspace)
|
||||
.patch(update_workspace)
|
||||
.delete(delete_workspace),
|
||||
)
|
||||
.nest("/workspaces/{workspace_id}", workspace_router)
|
||||
.layer(middleware::from_fn_with_state(
|
||||
@@ -765,6 +773,89 @@ mod tests {
|
||||
assert_eq!(delete_key_status, reqwest::StatusCode::NO_CONTENT);
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
#[serial]
|
||||
async fn manages_workspace_access_lifecycle() {
|
||||
let registry = test_registry().await;
|
||||
let storage_root = test_storage_root("workspace_access");
|
||||
let base_url = spawn_admin_api(build_test_app(registry.clone(), storage_root)).await;
|
||||
let client = authorized_client(&base_url).await;
|
||||
|
||||
let second_user_id = registry
|
||||
.upsert_bootstrap_user("operator-2@crank.local", "Operator Two", "external-hash")
|
||||
.await
|
||||
.unwrap();
|
||||
registry
|
||||
.ensure_membership(
|
||||
&WorkspaceId::new(DEFAULT_WORKSPACE_ID),
|
||||
&second_user_id,
|
||||
MembershipRole::Viewer,
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let updated_members = assert_success_json(
|
||||
client
|
||||
.patch(format!("{base_url}/members/{}", second_user_id.as_str()))
|
||||
.json(&json!({ "role": "admin" }))
|
||||
.send()
|
||||
.await
|
||||
.unwrap(),
|
||||
)
|
||||
.await;
|
||||
let updated_member = updated_members["items"]
|
||||
.as_array()
|
||||
.unwrap()
|
||||
.iter()
|
||||
.find(|item| item["user"]["id"] == second_user_id.as_str())
|
||||
.unwrap();
|
||||
assert_eq!(updated_member["role"], "admin");
|
||||
|
||||
let exported = assert_success_json(
|
||||
client
|
||||
.get(format!("{base_url}/export"))
|
||||
.send()
|
||||
.await
|
||||
.unwrap(),
|
||||
)
|
||||
.await;
|
||||
assert_eq!(
|
||||
exported["workspace"]["workspace"]["id"],
|
||||
DEFAULT_WORKSPACE_ID
|
||||
);
|
||||
assert_eq!(exported["memberships"].as_array().unwrap().len(), 2);
|
||||
|
||||
let delete_member_status = client
|
||||
.delete(format!("{base_url}/members/{}", second_user_id.as_str()))
|
||||
.send()
|
||||
.await
|
||||
.unwrap()
|
||||
.status();
|
||||
assert_eq!(delete_member_status, reqwest::StatusCode::NO_CONTENT);
|
||||
|
||||
let delete_workspace_status = client
|
||||
.delete(base_url.as_ref())
|
||||
.send()
|
||||
.await
|
||||
.unwrap()
|
||||
.status();
|
||||
assert_eq!(delete_workspace_status, reqwest::StatusCode::NO_CONTENT);
|
||||
|
||||
let root_url = base_url
|
||||
.as_ref()
|
||||
.split("/api/admin/workspaces/")
|
||||
.next()
|
||||
.unwrap();
|
||||
let missing_workspace = client
|
||||
.get(format!(
|
||||
"{root_url}/api/admin/workspaces/{DEFAULT_WORKSPACE_ID}"
|
||||
))
|
||||
.send()
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(missing_workspace.status(), reqwest::StatusCode::FORBIDDEN);
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
#[serial]
|
||||
async fn updates_profile_and_changes_password() {
|
||||
|
||||
Reference in New Issue
Block a user