mod common;
use common::*;
use serde_json::{json, Value};
fn open_operations() -> Value {
json!({
"find": { "*": true },
"count": { "*": true },
"get": { "*": true },
"create": { "*": true },
"update": { "*": true },
"delete": { "*": true },
"addField": { "*": true },
})
}
fn clp_with_acl(acl: Value) -> Value {
let mut block = open_operations();
block["ACL"] = acl;
block
}
async fn declare_private_class(host: &str, class: &str) {
let created = post(
host,
"/schemas",
&As::master(),
&json!({
"className": class,
"fields": { "title": { "type": "String" } },
"classLevelPermissions":
clp_with_acl(json!({ "currentUser": { "read": true, "write": true } })),
}),
)
.await;
assert_eq!(created.status, 200, "{}", created.raw);
}
async fn create_object(host: &str, class: &str, token: &str) -> String {
let created = post(
host,
&format!("/classes/{class}"),
&As::user(token),
&json!({ "title": "x" }),
)
.await;
assert_eq!(created.status, 201, "{}", created.raw);
created.body["objectId"]
.as_str()
.expect("objectId")
.to_string()
}
#[tokio::test]
#[ignore = "needs MongoDB on 127.0.0.1:27017"]
async fn the_create_response_carries_the_generated_acl() {
let server = boot().await;
let host = &server.host;
declare_private_class(host, "Echoed").await;
let (a_id, a) = signup(host, "echo-owner", "pw").await;
let created = post(
host,
"/classes/Echoed",
&As::user(&a),
&json!({ "title": "x" }),
)
.await;
assert_eq!(created.status, 201, "{}", created.raw);
assert_eq!(
created.body["ACL"],
json!({ a_id: { "read": true, "write": true } }),
"the caller has no other way to learn its object's permissions: {}",
created.raw
);
let anon = post(
host,
"/classes/Echoed",
&As::anonymous(),
&json!({ "title": "y" }),
)
.await;
assert_eq!(anon.status, 201, "{}", anon.raw);
assert_eq!(anon.body["ACL"], json!({}), "{}", anon.raw);
let open = post(
host,
"/schemas",
&As::master(),
&json!({
"className": "EchoedOpen",
"fields": { "title": { "type": "String" } },
"classLevelPermissions": open_operations(),
}),
)
.await;
assert_eq!(open.status, 200, "{}", open.raw);
let plain = post(
host,
"/classes/EchoedOpen",
&As::user(&a),
&json!({ "title": "z" }),
)
.await;
assert_eq!(plain.status, 201, "{}", plain.raw);
assert!(plain.body.get("ACL").is_none(), "{}", plain.raw);
}
async fn find_all(host: &str, class: &str, who: &As) -> Vec<Value> {
let response = get(host, &format!("/classes/{class}"), who).await;
assert_eq!(response.status, 200, "{}", response.raw);
response.results()
}
#[tokio::test]
#[ignore = "needs MongoDB on 127.0.0.1:27017"]
async fn a_declared_default_acl_isolates_the_creator() {
let server = boot().await;
let host = &server.host;
declare_private_class(host, "Private").await;
let (a_id, a) = signup(host, "acl-owner", "pw").await;
let (_, b) = signup(host, "acl-stranger", "pw").await;
let object_id = create_object(host, "Private", &a).await;
let mine = find_all(host, "Private", &As::user(&a)).await;
assert_eq!(mine.len(), 1, "the creator must see its own object");
assert_eq!(mine[0]["objectId"], json!(object_id));
let updated = put(
host,
&format!("/classes/Private/{object_id}"),
&As::user(&a),
&json!({ "title": "y" }),
)
.await;
assert_eq!(
updated.status, 200,
"_wperm is written separately from _rperm and can be wrong on its own: {}",
updated.raw
);
assert!(
find_all(host, "Private", &As::user(&b)).await.is_empty(),
"0.2.0 returned this object to every caller"
);
let denied = put(
host,
&format!("/classes/Private/{object_id}"),
&As::user(&b),
&json!({ "title": "z" }),
)
.await;
assert_eq!(denied.status, 404, "{}", denied.raw);
assert_eq!(denied.code(), Some(101));
let one = get(
host,
&format!("/classes/Private/{object_id}"),
&As::user(&a),
)
.await;
assert_eq!(one.status, 200, "{}", one.raw);
assert_eq!(
one.body["ACL"],
json!({ a_id.clone(): { "read": true, "write": true } }),
"currentUser must resolve to the caller and the literal key must be gone"
);
}
#[tokio::test]
#[ignore = "needs MongoDB on 127.0.0.1:27017"]
async fn a_class_with_no_declared_acl_still_creates_public_rows() {
let server = boot().await;
let host = &server.host;
let created = post(
host,
"/schemas",
&As::master(),
&json!({
"className": "Open",
"fields": { "title": { "type": "String" } },
"classLevelPermissions": open_operations(),
}),
)
.await;
assert_eq!(created.status, 200, "{}", created.raw);
let (_, a) = signup(host, "open-owner", "pw").await;
let (_, b) = signup(host, "open-stranger", "pw").await;
create_object(host, "Open", &a).await;
assert_eq!(find_all(host, "Open", &As::user(&b)).await.len(), 1);
}
#[tokio::test]
#[ignore = "needs MongoDB on 127.0.0.1:27017"]
async fn the_default_applies_on_create_and_never_on_update() {
let server = boot().await;
let host = &server.host;
declare_private_class(host, "Shared").await;
let (a_id, a) = signup(host, "shared-owner", "pw").await;
let (b_id, b) = signup(host, "shared-reader", "pw").await;
let created = post(
host,
"/classes/Shared",
&As::user(&a),
&json!({
"title": "x",
"ACL": {
a_id.clone(): { "read": true, "write": true },
b_id.clone(): { "read": true },
},
}),
)
.await;
assert_eq!(created.status, 201, "{}", created.raw);
let object_id = created.body["objectId"].as_str().expect("objectId");
assert_eq!(
find_all(host, "Shared", &As::user(&b)).await.len(),
1,
"a client-supplied ACL must win over the class default on create"
);
let updated = put(
host,
&format!("/classes/Shared/{object_id}"),
&As::user(&a),
&json!({ "title": "y" }),
)
.await;
assert_eq!(updated.status, 200, "{}", updated.raw);
assert_eq!(
find_all(host, "Shared", &As::user(&b)).await.len(),
1,
"the explicitly set ACL must survive an unrelated update"
);
}
#[tokio::test]
#[ignore = "needs MongoDB on 127.0.0.1:27017"]
async fn a_declared_public_acl_leaves_rows_public() {
let server = boot().await;
let host = &server.host;
let created = post(
host,
"/schemas",
&As::master(),
&json!({
"className": "Public",
"fields": { "title": { "type": "String" } },
"classLevelPermissions":
clp_with_acl(json!({ "*": { "read": true, "write": true } })),
}),
)
.await;
assert_eq!(created.status, 200, "{}", created.raw);
let (_, a) = signup(host, "public-owner", "pw").await;
let (_, b) = signup(host, "public-stranger", "pw").await;
let object_id = create_object(host, "Public", &a).await;
assert_eq!(find_all(host, "Public", &As::user(&b)).await.len(), 1);
let one = get(host, &format!("/classes/Public/{object_id}"), &As::user(&b)).await;
assert!(
one.body.get("ACL").is_none(),
"no ACL columns are written, so the row has no ACL key at all: {}",
one.raw
);
}
#[tokio::test]
#[ignore = "needs MongoDB on 127.0.0.1:27017"]
async fn an_anonymous_create_produces_a_row_only_master_can_read() {
let server = boot().await;
let host = &server.host;
declare_private_class(host, "Orphan").await;
let created = post(
host,
"/classes/Orphan",
&As::anonymous(),
&json!({ "title": "x" }),
)
.await;
assert_eq!(created.status, 201, "{}", created.raw);
assert!(find_all(host, "Orphan", &As::anonymous()).await.is_empty());
let (_, b) = signup(host, "orphan-stranger", "pw").await;
assert!(find_all(host, "Orphan", &As::user(&b)).await.is_empty());
assert_eq!(find_all(host, "Orphan", &As::master()).await.len(), 1);
}