mod common;
use common::Fixture;
use gunnar_core::model::PrincipalKind;
use gunnar_core::{Grant, Level, Principal, RepoRef, Scope, Visibility};
fn principal(fx: &Fixture, id: &str) {
fx.auth
.add_principal(Principal::new(id, id, PrincipalKind::User).expect("legal id"))
.expect("add principal");
}
#[test]
fn a_read_principal_is_refused_write_and_an_admin_is_allowed() {
let fx = Fixture::new();
let repo = fx.repo("acme", "widget");
let path = repo.path();
principal(&fx, "reader");
principal(&fx, "boss");
fx.auth
.grant(Grant::repo("reader", path.clone(), Level::Read))
.expect("grant");
fx.auth
.grant(Grant::repo("boss", path.clone(), Level::Admin))
.expect("grant");
assert!(fx.auth.may("reader", &repo, Level::Read).expect("acl"));
assert!(!fx.auth.may("reader", &repo, Level::Write).expect("acl"));
assert!(!fx.auth.may("reader", &repo, Level::Admin).expect("acl"));
assert!(fx.auth.may("boss", &repo, Level::Read).expect("acl"));
assert!(fx.auth.may("boss", &repo, Level::Write).expect("acl"));
assert!(fx.auth.may("boss", &repo, Level::Admin).expect("acl"));
let err = fx
.auth
.require("reader", &repo, Level::Write)
.expect_err("must be denied");
assert!(err.is_denied(), "{err}");
assert!(format!("{err}").contains("acme/widget"), "{err}");
fx.auth
.require("boss", &repo, Level::Write)
.expect("the admin is not denied");
}
#[test]
fn a_writer_may_fetch_and_push_but_not_administer() {
let fx = Fixture::new();
let repo = fx.repo("acme", "widget");
principal(&fx, "dev");
fx.auth
.grant(Grant::repo("dev", repo.path(), Level::Write))
.expect("grant");
assert!(fx.auth.may("dev", &repo, Level::Read).expect("acl"));
assert!(fx.auth.may("dev", &repo, Level::Write).expect("acl"));
assert!(!fx.auth.may("dev", &repo, Level::Admin).expect("acl"));
}
#[test]
fn an_ungranted_or_unknown_principal_may_do_nothing() {
let fx = Fixture::new();
let repo = fx.repo("acme", "widget");
principal(&fx, "stranger");
for level in [Level::Read, Level::Write, Level::Admin] {
assert!(!fx.auth.may("stranger", &repo, level).expect("acl"));
assert!(!fx.auth.may("does-not-exist", &repo, level).expect("acl"));
assert!(!fx.auth.may("", &repo, level).expect("acl"));
}
}
#[test]
fn a_repo_grant_does_not_reach_another_repo() {
let fx = Fixture::new();
let mine = fx.repo("acme", "mine");
let yours = fx.repo("acme", "yours");
principal(&fx, "dev");
fx.auth
.grant(Grant::repo("dev", mine.path(), Level::Write))
.expect("grant");
assert!(fx.auth.may("dev", &mine, Level::Write).expect("acl"));
assert!(!fx.auth.may("dev", &yours, Level::Read).expect("acl"));
}
#[test]
fn a_namespace_grant_reaches_every_repo_in_that_namespace_and_no_other() {
let fx = Fixture::new();
let one = fx.repo("acme", "one");
let two = fx.repo("acme", "two");
let elsewhere = fx.repo("other", "three");
principal(&fx, "team");
fx.auth
.grant(Grant::namespace("team", "acme", Level::Write))
.expect("grant");
assert!(fx.auth.may("team", &one, Level::Write).expect("acl"));
assert!(fx.auth.may("team", &two, Level::Write).expect("acl"));
assert!(!fx.auth.may("team", &elsewhere, Level::Read).expect("acl"));
assert!(fx
.auth
.may_namespace("team", "acme", Level::Write)
.expect("acl"));
assert!(!fx
.auth
.may_namespace("team", "other", Level::Read)
.expect("acl"));
}
#[test]
fn a_namespace_grant_does_not_inherit_into_a_nested_namespace() {
let fx = Fixture::new();
let outer = fx.repo("team", "outer");
let inner = fx.repo("team/sub", "inner");
principal(&fx, "lead");
fx.auth
.grant(Grant::namespace("lead", "team", Level::Admin))
.expect("grant");
assert!(fx.auth.may("lead", &outer, Level::Admin).expect("acl"));
assert!(!fx.auth.may("lead", &inner, Level::Read).expect("acl"));
fx.auth
.grant(Grant::namespace("lead", "team/sub", Level::Read))
.expect("grant");
assert!(fx.auth.may("lead", &inner, Level::Read).expect("acl"));
assert!(!fx.auth.may("lead", &inner, Level::Write).expect("acl"));
}
#[test]
fn a_server_admin_reaches_every_namespace_and_repo() {
let fx = Fixture::new();
let repo = fx.repo("acme", "widget");
let elsewhere = fx.repo("other", "thing");
principal(&fx, "root");
fx.auth
.grant(Grant::server("root", Level::Admin))
.expect("grant");
assert!(fx.auth.may("root", &repo, Level::Admin).expect("acl"));
assert!(fx.auth.may("root", &elsewhere, Level::Write).expect("acl"));
assert!(fx.auth.may_server("root", Level::Admin).expect("acl"));
assert!(fx
.auth
.may_namespace("root", "anything", Level::Admin)
.expect("acl"));
principal(&fx, "repo-boss");
fx.auth
.grant(Grant::repo("repo-boss", repo.path(), Level::Admin))
.expect("grant");
assert!(!fx.auth.may_server("repo-boss", Level::Read).expect("acl"));
assert!(!fx
.auth
.may_namespace("repo-boss", "acme", Level::Read)
.expect("acl"));
}
#[test]
fn public_visibility_grants_read_and_never_write() {
let fx = Fixture::new();
let repo = fx.repo_with(
"acme",
"open",
gunnar_core::HashKind::Sha256,
Visibility::Public,
);
principal(&fx, "anyone");
assert!(fx.auth.may("anyone", &repo, Level::Read).expect("acl"));
assert!(!fx.auth.may("anyone", &repo, Level::Write).expect("acl"));
assert!(!fx.auth.may("anyone", &repo, Level::Admin).expect("acl"));
assert!(!fx.auth.may("ghost", &repo, Level::Read).expect("acl"));
fx.catalog
.set_visibility(&repo.path(), Visibility::Private)
.expect("set");
let repo = fx.catalog.open(&repo.path()).expect("reopen");
assert!(!fx.auth.may("anyone", &repo, Level::Read).expect("acl"));
}
#[test]
fn the_anonymous_principal_may_read_public_and_nothing_else() {
let fx = Fixture::new();
let open = fx.repo_with(
"acme",
"open",
gunnar_core::HashKind::Sha256,
Visibility::Public,
);
let shut = fx.repo_with(
"acme",
"shut",
gunnar_core::HashKind::Sha256,
Visibility::Private,
);
let anon = gunnar_core::ANONYMOUS_PRINCIPAL_ID;
assert!(
fx.auth.may(anon, &open, Level::Read).expect("acl"),
"an anonymous caller must be able to READ a public repository. That is the whole of \
what `Repo.visibility` is for"
);
assert!(!fx.auth.may(anon, &open, Level::Write).expect("acl"));
assert!(!fx.auth.may(anon, &open, Level::Admin).expect("acl"));
for level in [Level::Read, Level::Write, Level::Admin] {
assert!(
!fx.auth.may(anon, &shut, level).expect("acl"),
"anonymous reached a PRIVATE repository at {level:?}"
);
}
for level in [Level::Read, Level::Write, Level::Admin] {
assert!(!fx.auth.may_namespace(anon, "acme", level).expect("acl"));
assert!(!fx.auth.may_server(anon, level).expect("acl"));
}
fx.catalog
.set_visibility(&open.path(), Visibility::Private)
.expect("set");
fx.catalog
.set_visibility(&shut.path(), Visibility::Public)
.expect("set");
let open = fx.catalog.open(&open.path()).expect("reopen");
let shut = fx.catalog.open(&shut.path()).expect("reopen");
assert!(!fx.auth.may(anon, &open, Level::Read).expect("acl"));
assert!(fx.auth.may(anon, &shut, Level::Read).expect("acl"));
}
#[test]
fn the_anonymous_id_is_reserved_and_can_hold_neither_key_nor_grant() {
let fx = Fixture::new();
let repo = fx.repo("acme", "widget");
let anon = gunnar_core::ANONYMOUS_PRINCIPAL_ID;
let err = Principal::new(anon, "me", PrincipalKind::User)
.expect_err("`anonymous` must not be constructible as an ordinary principal");
assert!(format!("{err}").contains("reserved"), "{err}");
let err = fx
.auth
.add_principal(Principal::anonymous())
.expect_err("`anonymous` must not be registrable");
assert!(format!("{err}").contains("reserved"), "{err}");
const KEY: &str =
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMW4aqVr75w+3DWorOCbfwl5XCDCq1yCQ6puzU4qzol2 anon";
let err = fx
.auth
.register_key(anon, KEY, None)
.expect_err("no key may ever authenticate AS anonymous");
assert!(format!("{err}").contains("no such principal"), "{err}");
let err = fx
.auth
.grant(Grant::repo(anon, repo.path(), Level::Admin))
.expect_err("anonymous must not be grantable");
assert!(format!("{err}").contains("no such principal"), "{err}");
principal(&fx, "ordinary");
fx.auth
.register_key("ordinary", KEY, None)
.expect("an ordinary principal takes a key");
fx.auth
.grant(Grant::repo("ordinary", repo.path(), Level::Admin))
.expect("an ordinary principal takes a grant");
assert!(fx.auth.may("ordinary", &repo, Level::Admin).expect("acl"));
assert!(fx.auth.principal(anon).expect("read").is_none());
assert!(fx.auth.grants(Some(anon)).expect("read").is_empty());
assert!(
fx.auth
.keys()
.expect("read")
.iter()
.all(|key| key.principal_id != anon),
"a key is registered to the anonymous principal"
);
}
#[test]
fn the_anonymous_principal_value_is_what_the_acl_is_keyed_on() {
let anon = Principal::anonymous();
assert_eq!(anon.id, gunnar_core::ANONYMOUS_PRINCIPAL_ID);
assert_eq!(anon.kind, PrincipalKind::Anonymous);
assert!(anon.is_anonymous());
let ordinary = Principal::new("dev", "dev", PrincipalKind::User).expect("legal");
assert!(!ordinary.is_anonymous());
assert_ne!(ordinary.kind, PrincipalKind::Anonymous);
}
#[test]
fn a_downgrade_is_a_downgrade() {
let fx = Fixture::new();
let repo = fx.repo("acme", "widget");
let path = repo.path();
principal(&fx, "dev");
fx.auth
.grant(Grant::repo("dev", path.clone(), Level::Admin))
.expect("grant");
assert!(fx.auth.may("dev", &repo, Level::Admin).expect("acl"));
fx.auth
.grant(Grant::repo("dev", path.clone(), Level::Read))
.expect("grant");
assert_eq!(fx.auth.grants(Some("dev")).expect("grants").len(), 1);
assert!(fx.auth.may("dev", &repo, Level::Read).expect("acl"));
assert!(!fx.auth.may("dev", &repo, Level::Write).expect("acl"));
assert!(fx
.auth
.revoke_grant("dev", &Scope::Repo(path.clone()))
.expect("revoke"));
assert!(!fx.auth.may("dev", &repo, Level::Read).expect("acl"));
assert!(!fx
.auth
.revoke_grant("dev", &Scope::Repo(path))
.expect("revoke again"));
}
#[test]
fn removing_a_principal_removes_its_grants() {
let fx = Fixture::new();
let repo = fx.repo("acme", "widget");
principal(&fx, "leaver");
fx.auth
.grant(Grant::repo("leaver", repo.path(), Level::Admin))
.expect("grant");
assert!(fx.auth.may("leaver", &repo, Level::Admin).expect("acl"));
fx.auth.remove_principal("leaver").expect("remove");
assert!(fx.auth.grants(None).expect("grants").is_empty());
assert!(!fx.auth.may("leaver", &repo, Level::Read).expect("acl"));
}
#[test]
fn a_grant_to_an_unknown_principal_is_refused() {
let fx = Fixture::new();
let repo = fx.repo("acme", "widget");
let err = fx
.auth
.grant(Grant::repo("ghost", repo.path(), Level::Admin))
.expect_err("must refuse");
assert!(format!("{err}").contains("no such principal"), "{err}");
assert!(fx.auth.grants(None).expect("grants").is_empty());
}
#[test]
fn levels_are_ordered() {
assert!(Level::Read < Level::Write);
assert!(Level::Write < Level::Admin);
assert_eq!(
[Level::Admin, Level::Read, Level::Write].iter().max(),
Some(&Level::Admin)
);
}
#[test]
fn scopes_cover_what_they_say() {
let fx = Fixture::new();
let repo = fx.repo("acme", "widget");
assert!(Scope::Server.covers(&repo));
assert!(Scope::Namespace("acme".into()).covers(&repo));
assert!(!Scope::Namespace("acm".into()).covers(&repo));
assert!(Scope::Repo(RepoRef::new("acme", "widget").unwrap()).covers(&repo));
assert!(!Scope::Repo(RepoRef::new("acme", "widgets").unwrap()).covers(&repo));
assert!(!Scope::Repo(RepoRef::new("acme", "widget").unwrap()).covers_namespace("acme"));
}