use kcode_k1_access_discovery::AccessDiscovery;
use kcode_k1_access_store::StoredAccess;
use kcode_k1_access_types::{
AccessContext, AccessId, AccessPolicy, Authority, FilteredAuthorities, GroupId, ModelId,
ProfileId, SubsystemId, Target, TxId, UserId, ViewerSubject,
};
use tempfile::tempdir;
fn tx(byte: u8) -> TxId {
TxId::from_bytes([byte; 12])
}
fn access(byte: u8) -> AccessId {
AccessId::new(tx(byte))
}
fn profile(byte: u8) -> ProfileId {
ProfileId::new(tx(byte))
}
fn user(byte: u8) -> UserId {
UserId::from_tx_id(tx(byte))
}
fn group(byte: u8) -> GroupId {
GroupId::new(tx(byte))
}
fn model(byte: u8) -> ModelId {
ModelId::from_bytes([byte; 32])
}
fn subsystem(name: &str) -> SubsystemId {
SubsystemId::from_str(name).unwrap()
}
fn context(user: UserId, filtered: Vec<Authority>) -> AccessContext {
AccessContext::new(
user,
model(240),
FilteredAuthorities::new(filtered).unwrap(),
)
.unwrap()
}
fn policy(
authority: Authority,
editors: Vec<Authority>,
viewers: Vec<ViewerSubject>,
) -> AccessPolicy {
AccessPolicy::new(authority, editors, viewers).unwrap()
}
fn record(byte: u8, subsystem: SubsystemId, policy: AccessPolicy) -> StoredAccess {
StoredAccess::new(
access(byte),
Target::new(subsystem, vec![byte]),
profile(byte),
tx(byte.wrapping_add(100)),
policy,
)
}
fn commit_record(discovery: &AccessDiscovery, record: &StoredAccess) {
discovery
.commit(
record.access_id().txid(),
Some((record.access_id(), record.policy())),
)
.unwrap();
}
#[test]
fn fans_out_non_model_subjects_and_preserves_first_discovery_order() {
let root = tempdir().unwrap();
let discovery = AccessDiscovery::open(root.path()).unwrap();
let subsystem = subsystem("access-test");
let authority = user(1);
let editor = user(2);
let viewer = user(3);
let first_group = group(10);
let second_group = group(20);
let (a, b, c, d) = (access(31), access(34), access(32), access(33));
let records = vec![
record(
31,
subsystem,
policy(
Authority::User(authority),
vec![Authority::User(editor), Authority::Group(first_group)],
vec![
ViewerSubject::User(viewer),
ViewerSubject::Group(second_group),
ViewerSubject::Model(model(4)),
],
),
),
record(
34,
subsystem,
policy(Authority::User(authority), vec![], vec![]),
),
record(
32,
subsystem,
policy(
Authority::Group(first_group),
vec![],
vec![ViewerSubject::Group(second_group)],
),
),
record(
33,
subsystem,
policy(Authority::Group(second_group), vec![], vec![]),
),
];
for index in [0, 2, 3, 1] {
commit_record(&discovery, &records[index]);
}
let visible = context(authority, vec![]);
assert_eq!(
discovery.list_user(&visible, &records, subsystem).unwrap(),
vec![a, b]
);
assert_eq!(
discovery
.list_user(&context(editor, vec![]), &records, subsystem)
.unwrap(),
vec![a]
);
assert_eq!(
discovery
.list_user(&context(viewer, vec![]), &records, subsystem)
.unwrap(),
vec![a]
);
assert_eq!(
discovery
.list_group(&visible, &records, first_group, subsystem)
.unwrap(),
vec![a, c]
);
assert_eq!(
discovery
.list_group(&visible, &records, second_group, subsystem)
.unwrap(),
vec![a, c, d]
);
assert_eq!(
discovery
.list_user_groups(
&visible,
&records,
&[second_group, first_group, second_group],
subsystem
)
.unwrap(),
vec![
(a, Target::new(subsystem, vec![31])),
(b, Target::new(subsystem, vec![34])),
(c, Target::new(subsystem, vec![32])),
(d, Target::new(subsystem, vec![33]))
]
);
assert!(discovery.complete(&records).unwrap());
}
#[test]
fn applies_exact_authority_filters_without_hiding_personal_authority() {
let root = tempdir().unwrap();
let discovery = AccessDiscovery::open(root.path()).unwrap();
let subsystem = subsystem("filter-test");
let member = user(5);
let owner = user(6);
let team = group(7);
let (personal, other, group_owned) = (access(41), access(42), access(43));
let records = vec![
record(
41,
subsystem,
policy(
Authority::User(member),
vec![],
vec![ViewerSubject::Group(team)],
),
),
record(
42,
subsystem,
policy(
Authority::User(owner),
vec![],
vec![ViewerSubject::User(member), ViewerSubject::Group(team)],
),
),
record(
43,
subsystem,
policy(
Authority::Group(team),
vec![],
vec![ViewerSubject::User(member)],
),
),
];
for record in &records {
commit_record(&discovery, record);
}
let user_filtered = context(member, vec![Authority::User(owner)]);
assert_eq!(
discovery
.list_user(&user_filtered, &records, subsystem)
.unwrap(),
vec![personal, group_owned]
);
assert_eq!(
discovery
.list_group(&user_filtered, &records, team, subsystem)
.unwrap(),
vec![personal, group_owned]
);
let group_filtered = context(member, vec![Authority::Group(team)]);
assert_eq!(
discovery
.list_user(&group_filtered, &records, subsystem)
.unwrap(),
vec![personal, other]
);
assert_eq!(
discovery
.list_group(&group_filtered, &records, team, subsystem)
.unwrap(),
vec![personal, other]
);
assert_eq!(
discovery
.list_user_groups(
&context(member, vec![Authority::User(owner), Authority::Group(team)]),
&records,
&[team],
subsystem
)
.unwrap(),
vec![(personal, Target::new(subsystem, vec![41]))]
);
}
#[test]
fn missing_rejects_nonvisible_records_and_tracks_fanout() {
let root = tempdir().unwrap();
let discovery = AccessDiscovery::open(root.path()).unwrap();
let other_subsystem = subsystem("other-test");
let subsystem = subsystem("missing-test");
let owner = user(8);
let records = [record(
51,
subsystem,
policy(Authority::User(owner), vec![], vec![]),
)];
let visible = context(owner, vec![]);
assert!(
discovery
.missing(&visible, &records, access(51), subsystem)
.unwrap()
);
assert!(
!discovery
.missing(&visible, &records, access(52), subsystem)
.unwrap()
);
assert!(
!discovery
.missing(&visible, &records, access(51), other_subsystem)
.unwrap()
);
assert!(
!discovery
.missing(
&context(user(9), vec![Authority::User(owner)]),
&records,
access(51),
subsystem
)
.unwrap()
);
commit_record(&discovery, &records[0]);
assert!(
!discovery
.missing(&visible, &records, access(51), subsystem)
.unwrap()
);
}
#[test]
fn checkpoint_complete_and_discard_have_distinct_lifecycle_effects() {
let root = tempdir().unwrap();
let discovery = AccessDiscovery::open(root.path()).unwrap();
let subsystem = subsystem("lifecycle-test");
let owner = user(11);
let records = [record(
81,
subsystem,
policy(Authority::User(owner), vec![], vec![]),
)];
assert_eq!(discovery.checkpoint().unwrap(), None);
discovery.commit(tx(80), None).unwrap();
assert_eq!(discovery.checkpoint().unwrap(), Some(tx(80)));
assert!(
discovery
.list_user(&context(owner, vec![]), &records, subsystem)
.unwrap()
.is_empty()
);
assert!(!discovery.complete(&records).unwrap());
commit_record(&discovery, &records[0]);
assert_eq!(discovery.checkpoint().unwrap(), Some(tx(81)));
assert!(discovery.complete(&records).unwrap());
discovery.discard().unwrap();
assert!(discovery.checkpoint().is_err());
drop(discovery);
let reopened = AccessDiscovery::open(root.path()).unwrap();
assert_eq!(reopened.checkpoint().unwrap(), None);
assert!(!reopened.complete(&records).unwrap());
}