#[test]
fn test_unregistered_actors_cannot_author_control_records() {
let dir = tempfile::tempdir().unwrap();
let rules = test_rules();
let mut writer = LogWriter::open(dir.path(), &rules).unwrap();
let mut state = State::default();
let (request_id, cap_id) =
setup_request_and_capability(&mut writer, &rules, &mut state, "shell");
let (action_id, _) = writer
.commit(
action_proposal_with_authority(request_id, "shell", &[cap_id]),
&rules,
&mut state,
)
.unwrap();
let (result_id, _) = writer
.commit(result_proposal(action_id), &rules, &mut state)
.unwrap();
let stranger = |type_: AuthorType| Author {
id: "stranger".into(),
type_,
signature: None,
};
let mut refusal = refusal_request_proposal(request_id);
refusal.author = stranger(AuthorType::User);
let (_, v) = writer.commit(refusal, &rules, &mut state).unwrap();
assert_eq!(v.result, VerdictResult::Reject);
assert_eq!(v.reason, Some(ReasonCode::AuthorRoleInvalid));
assert!(state.active_requests.contains(&request_id));
let mut summary = summary_proposal(&[result_id]);
summary.author = stranger(AuthorType::Provider);
let (_, v) = writer.commit(summary, &rules, &mut state).unwrap();
assert_eq!(v.result, VerdictResult::Reject);
assert_eq!(v.reason, Some(ReasonCode::AuthorRoleInvalid));
let mut usage = usage_proposal(request_id, result_id, UsageOutcome::Done);
usage.author = stranger(AuthorType::Provider);
usage.data = encode(&UsageData {
actor: "stranger".into(),
used_record: request_id,
consuming_record: result_id,
role: "context".into(),
outcome: UsageOutcome::Done,
})
.unwrap();
let (_, v) = writer.commit(usage, &rules, &mut state).unwrap();
assert_eq!(v.result, VerdictResult::Reject);
assert_eq!(v.reason, Some(ReasonCode::AuthorRoleInvalid));
let mut retraction = retraction_proposal(result_id);
retraction.author = stranger(AuthorType::User);
let (_, v) = writer.commit(retraction, &rules, &mut state).unwrap();
assert_eq!(v.result, VerdictResult::Reject);
assert_eq!(v.reason, Some(ReasonCode::AuthorRoleInvalid));
}
#[test]
fn test_action_must_match_request_scope() {
let dir = tempfile::tempdir().unwrap();
let rules = test_rules();
let mut writer = LogWriter::open(dir.path(), &rules).unwrap();
let mut state = State::default();
let (request_id, _) = writer
.commit(request_proposal(), &rules, &mut state)
.unwrap();
let scope_b: [u8; 32] = [77u8; 32];
let (cap_id, v) = writer
.commit(
Proposal {
space: SPACE,
thread: THREAD,
author: human_author(),
kind: Kind::Capability,
schema: schema_id(SCHEMA_CAPABILITY),
data: encode(&CapabilityData {
actor_id: ACTOR.into(),
action_class: "shell".into(),
scope: scope_b,
mode: CapabilityMode::Auto,
expiry: None,
})
.unwrap(),
refs: vec![],
},
&rules,
&mut state,
)
.unwrap();
assert_eq!(v.result, VerdictResult::Accept);
let (_, v) = writer
.commit(
Proposal {
space: SPACE,
thread: THREAD,
author: provider_author(),
kind: Kind::Action,
schema: schema_id(SCHEMA_ACTION),
data: encode(&ActionData {
request_id,
action_class: "shell".into(),
scope: scope_b,
exec_mode: ExecMode::Internal,
params: serde_json::json!({}),
})
.unwrap(),
refs: vec![Ref {
type_: RefType::Require,
target: cap_id,
}],
},
&rules,
&mut state,
)
.unwrap();
assert_eq!(v.result, VerdictResult::Reject);
assert_eq!(v.reason, Some(ReasonCode::InvalidPayload));
}
#[test]
fn test_exact_approval_scope_must_match_action() {
let dir = tempfile::tempdir().unwrap();
let rules = test_rules();
let mut writer = LogWriter::open(dir.path(), &rules).unwrap();
let mut state = State::default();
let (request_id, _) = writer
.commit(request_proposal(), &rules, &mut state)
.unwrap();
let (cap_id, _) = writer
.commit(
capability_proposal(ACTOR, "shell", CapabilityMode::Ask),
&rules,
&mut state,
)
.unwrap();
let action_data = ActionData {
request_id,
action_class: "shell".into(),
scope: SCOPE,
exec_mode: ExecMode::Internal,
params: serde_json::json!({}),
};
let target_hash = sha256_canonical(&(&ACTOR.to_string(), &action_data)).unwrap();
let (approval_id, v) = writer
.commit(
Proposal {
space: SPACE,
thread: THREAD,
author: human_author(),
kind: Kind::Approval,
schema: schema_id(SCHEMA_APPROVAL),
data: encode(&ApprovalData {
target_action: Some(target_hash),
action_class: None,
scope: [88u8; 32],
actor_id: Some(ACTOR.into()),
expiry: None,
})
.unwrap(),
refs: vec![],
},
&rules,
&mut state,
)
.unwrap();
assert_eq!(v.result, VerdictResult::Accept);
let (_, v) = writer
.commit(
action_proposal_with_authority(request_id, "shell", &[cap_id, approval_id]),
&rules,
&mut state,
)
.unwrap();
assert_eq!(v.result, VerdictResult::Reject);
assert_eq!(v.reason, Some(ReasonCode::ApprovalMissing));
}