use super::*;
fn write_app(root: &Path, db_url: &str) {
write_files(
root,
&[
(
"main.toml",
&format!(
"[server]\nbase_path = \"/api\"\n\n[database]\nurl = \"{db_url}\"\n\n\
[organization]\n\
global_admin_role = \"member@org_class=staff\"\n"
),
),
(
"resources/note.toml",
"[resource]\nname = \"note\"\n\n[permissions]\nlist = \"member\"\n\
create = \"member\"\n\n[fields.title]\ntype = \"string\"\n",
),
],
);
}
macro_rules! register {
($app:expr, $email:expr) => {{
let created = read_json(
test::call_service(
&$app,
req_json(
"POST",
"/api/auth/register",
json!({ "email": $email, "password": "pw" }),
),
)
.await,
)
.await;
(
created["user"]["id"].as_str().unwrap().to_string(),
created["token"].as_str().unwrap().to_string(),
)
}};
}
#[ntex::test]
async fn an_org_admin_may_act_as_a_member_of_their_own_organisation() {
let db = TempDatabase::create("impersonate").await;
let root = temp_dir("impersonate");
write_app(&root, &db.url);
let state = load_state(&root).await;
let app = init_http_app!(state);
let (_boss_id, boss_token) = register!(app, "boss@example.com");
let (member_id, member_token) = register!(app, "member@example.com");
let org = read_json(
test::call_service(
&app,
bearer(
test::TestRequest::post()
.uri("/api/organization")
.header(CONTENT_TYPE, "application/json")
.set_payload(json!({ "name": "Acme" }).to_string()),
&boss_token,
)
.to_request(),
)
.await,
)
.await;
let org_id = org["id"].as_str().unwrap().to_string();
let elsewhere = read_json(
test::call_service(
&app,
bearer(
test::TestRequest::post()
.uri("/api/organization")
.header(CONTENT_TYPE, "application/json")
.set_payload(json!({ "name": "Their side project" }).to_string()),
&member_token,
)
.to_request(),
)
.await,
)
.await;
let elsewhere_id = elsewhere["id"].as_str().unwrap().to_string();
let impersonate = |token: &str, active: Option<&str>, body: Value| {
let mut req = test::TestRequest::post()
.uri("/api/auth/impersonate")
.header(CONTENT_TYPE, "application/json")
.set_payload(body.to_string());
if let Some(active) = active {
req = req.header("x-organization", active.to_string());
}
bearer(req, token).to_request()
};
let refused = test::call_service(
&app,
impersonate(
&boss_token,
Some(&org_id),
json!({ "user_id": member_id.clone() }),
),
)
.await;
assert_eq!(refused.status().as_u16(), 403);
state
.db
.raw_json(
&format!(
"INSERT INTO {} (id, organization_id, user_id, role) \
VALUES (gen_random_uuid(), $1::uuid, $2::uuid, 'member')",
state.table("membership").unwrap()
),
&[
Value::String(org_id.clone()),
Value::String(member_id.clone()),
],
)
.await
.unwrap();
let borrowed = read_json(
test::call_service(
&app,
impersonate(
&boss_token,
Some(&org_id),
json!({ "user_id": member_id.clone() }),
),
)
.await,
)
.await;
assert_eq!(borrowed["user_id"], member_id);
assert_eq!(borrowed["organization_id"], org_id);
let borrowed_token = borrowed["token"].as_str().unwrap().to_string();
let me = read_json(
test::call_service(
&app,
bearer(
test::TestRequest::get().uri("/api/auth/me"),
&borrowed_token,
)
.to_request(),
)
.await,
)
.await;
assert_eq!(me["user_id"], member_id);
assert!(me["impersonator"].as_str().is_some());
assert_eq!(me["organization_id"], org_id);
let kept = test::call_service(
&app,
bearer(
test::TestRequest::post()
.uri("/api/note")
.header("x-organization", elsewhere_id.clone())
.header(CONTENT_TYPE, "application/json")
.set_payload(json!({ "title": "Private to them" }).to_string()),
&member_token,
)
.to_request(),
)
.await;
assert_eq!(kept.status().as_u16(), 201);
let theirs = read_json(
test::call_service(
&app,
bearer(
test::TestRequest::get()
.uri("/api/note")
.header("x-organization", elsewhere_id.clone()),
&member_token,
)
.to_request(),
)
.await,
)
.await;
assert_eq!(theirs.as_array().unwrap().len(), 1);
let steered = read_json(
test::call_service(
&app,
bearer(
test::TestRequest::get()
.uri("/api/note")
.header("x-organization", elsewhere_id.clone()),
&borrowed_token,
)
.to_request(),
)
.await,
)
.await;
assert!(steered.as_array().unwrap().is_empty());
let nested = test::call_service(
&app,
impersonate(
&borrowed_token,
Some(&org_id),
json!({ "user_id": member_id.clone() }),
),
)
.await;
assert_eq!(nested.status().as_u16(), 409);
let restored = read_json(
test::call_service(
&app,
bearer(
test::TestRequest::post().uri("/api/auth/impersonate/stop"),
&borrowed_token,
)
.to_request(),
)
.await,
)
.await;
assert_eq!(restored["impersonator"], Value::Null);
let restored_token = restored["token"].as_str().unwrap().to_string();
let me = read_json(
test::call_service(
&app,
bearer(
test::TestRequest::get().uri("/api/auth/me"),
&restored_token,
)
.to_request(),
)
.await,
)
.await;
assert_eq!(me["user_id"], restored["user_id"]);
assert_eq!(me["impersonator"], Value::Null);
let refused = test::call_service(
&app,
impersonate(
&member_token,
Some(&org_id),
json!({ "user_id": member_id.clone() }),
),
)
.await;
assert!(matches!(refused.status().as_u16(), 400 | 403));
}
#[ntex::test]
async fn only_a_global_admin_may_act_as_a_stranger() {
let db = TempDatabase::create("impersonateany").await;
let root = temp_dir("impersonateany");
write_app(&root, &db.url);
let state = load_state(&root).await;
let app = init_http_app!(state);
let (_staff_id, staff_token) = register!(app, "staff@example.com");
let (stranger_id, stranger_token) = register!(app, "stranger@example.com");
let ops = read_json(
test::call_service(
&app,
bearer(
test::TestRequest::post()
.uri("/api/organization")
.header(CONTENT_TYPE, "application/json")
.set_payload(json!({ "name": "Ops" }).to_string()),
&staff_token,
)
.to_request(),
)
.await,
)
.await;
let ops_id = ops["id"].as_str().unwrap().to_string();
let borrow = |token: &str, target: &str| {
bearer(
test::TestRequest::post()
.uri("/api/auth/impersonate")
.header(CONTENT_TYPE, "application/json")
.set_payload(json!({ "user_id": target }).to_string()),
token,
)
.to_request()
};
let refused = test::call_service(&app, borrow(&staff_token, &stranger_id)).await;
assert_eq!(refused.status().as_u16(), 403);
state
.db
.raw_json(
&format!(
"UPDATE {} SET org_class = 'staff' WHERE id = $1::uuid",
state.table("organization").unwrap()
),
&[Value::String(ops_id.clone())],
)
.await
.unwrap();
let borrowed =
read_json(test::call_service(&app, borrow(&staff_token, &stranger_id)).await).await;
assert_eq!(borrowed["user_id"], stranger_id);
assert_eq!(borrowed["organization_id"], Value::Null);
let refused = test::call_service(&app, borrow(&stranger_token, &stranger_id)).await;
assert!(matches!(refused.status().as_u16(), 400 | 403));
let borrowed_token = borrowed["token"].as_str().unwrap().to_string();
let me = read_json(
test::call_service(
&app,
bearer(
test::TestRequest::get().uri("/api/auth/me"),
&borrowed_token,
)
.to_request(),
)
.await,
)
.await;
assert_eq!(me["global_admin"], false);
}