kcode-k1-access-discovery 0.2.0

Concrete K1 access discovery owner for the access projection
Documentation
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());
}