gunnar-core 3.0.0

gunnar's repository model, identity table, ACL and path sandbox. gunnar-ssh, gunnar-grpc and gunnar-cli all sit on it.
Documentation
//! **The operator sees aggregates only** (owner's enterprise list,
//! 2026-10-05; the 2026-10-02 decision: "the operator sees only aggregates
//! (size, room), not names or events, unless the account admin grants
//! access").
//!
//! Enforced in the ACL itself, so every door that asks `AuthTable::may` —
//! SSH, HTTP, the gRPC gate — asks the same question: a SERVER-scope grant
//! stops at an account's door. What reaches inside an account is the account
//! itself, its employees' grants, a grant the account admin made on its own
//! namespace or repositories — and, for a while, an operator access the
//! account admin APPROVED.

mod common;

use common::Fixture;
use gunnar_core::{
    Grant, HashKind, Level, OperatorAccessState, Principal, PrincipalKind, Scope, Visibility,
};

const NOW: i64 = 1_791_000_000_000;
const HOUR_MS: i64 = 3_600_000;

struct World {
    fx: Fixture,
    site: gunnar_core::Repo,
    public_site: gunnar_core::Repo,
    forge: gunnar_core::Repo,
    own: gunnar_core::Repo,
}

/// `op` is the estate's operator (a person, server Admin) with its own
/// account; `acme` is a customer with an employee `bob`; `nordisk` is a
/// namespace no principal owns (the operator's own forge namespaces).
fn world() -> World {
    let fx = Fixture::new();
    for (id, kind) in [
        ("op", PrincipalKind::User),
        ("acme", PrincipalKind::User),
        ("carol", PrincipalKind::User),
        ("staff", PrincipalKind::Group),
        ("robot", PrincipalKind::Machine),
    ] {
        fx.auth
            .add_principal(Principal::new(id, id, kind).unwrap())
            .unwrap();
    }
    fx.auth.grant(Grant::server("op", Level::Admin)).unwrap();
    fx.auth.grant(Grant::server("staff", Level::Admin)).unwrap();
    fx.auth.add_to_group("staff", "carol").unwrap();
    fx.auth.grant(Grant::server("robot", Level::Admin)).unwrap();
    fx.auth
        .enroll_employee(
            "acme",
            Principal::new("bob", "Bob", PrincipalKind::User).unwrap(),
        )
        .unwrap();
    let site = fx.repo("acme", "site");
    let public_site = fx.repo_with("acme", "www", HashKind::Sha256, Visibility::Public);
    let forge = fx.repo("nordisk", "gunnar");
    let own = fx.repo("op", "notes");
    World {
        fx,
        site,
        public_site,
        forge,
        own,
    }
}

#[test]
fn a_server_grant_stops_at_an_accounts_door() {
    let w = world();
    let auth = &w.fx.auth;
    for level in [Level::Read, Level::Write, Level::Admin] {
        assert!(
            !auth.may("op", &w.site, level).unwrap(),
            "the operator {level:?} acme/site"
        );
        assert!(
            !auth.may_namespace("op", "acme", level).unwrap(),
            "the operator {level:?} namespace acme"
        );
        // Through a group's server grant, the same: carol is a person.
        assert!(
            !auth.may("carol", &w.site, level).unwrap(),
            "a server grant through a group, {level:?}"
        );
        // A MACHINE's server grant keeps its reach: replication pushing a
        // tenant's data to its standby, the console's relay — the estate's
        // plumbing, not a person looking.
        assert!(
            auth.may("robot", &w.site, level).unwrap(),
            "a machine's server grant, {level:?}"
        );
    }
    // Everything that is not a customer's account is the operator's, as before.
    assert!(
        auth.may("op", &w.forge, Level::Admin).unwrap(),
        "a namespace no principal owns"
    );
    assert!(auth.may_namespace("op", "nordisk", Level::Admin).unwrap());
    assert!(
        auth.may("op", &w.own, Level::Admin).unwrap(),
        "the operator's own account"
    );
    assert!(
        auth.may_server("op", Level::Admin).unwrap(),
        "the server itself"
    );
    // Public is public, for the operator as for anybody registered.
    assert!(auth.may("op", &w.public_site, Level::Read).unwrap());
    assert!(!auth.may("op", &w.public_site, Level::Write).unwrap());
    // The account's own people are untouched.
    assert!(auth.may("acme", &w.site, Level::Admin).unwrap());
    assert!(
        auth.may("bob", &w.site, Level::Read).unwrap(),
        "an employee reads its account"
    );
}

#[test]
fn a_grant_the_account_admin_made_still_reaches() {
    let w = world();
    let auth = &w.fx.auth;
    auth.grant(Grant {
        principal: "op".into(),
        scope: Scope::Repo(gunnar_core::RepoRef::new("acme", "site").unwrap()),
        level: Level::Read,
    })
    .unwrap();
    assert!(
        auth.may("op", &w.site, Level::Read).unwrap(),
        "a repository grant on acme/site"
    );
    assert!(!auth.may("op", &w.site, Level::Write).unwrap());
}

#[test]
fn who_is_shielded_from_whom() {
    let w = world();
    let auth = &w.fx.auth;
    assert!(auth.shielded("op", "acme").unwrap());
    assert!(
        auth.shielded("op", "acme/team").unwrap(),
        "a nested namespace is the account's"
    );
    assert!(!auth.shielded("acme", "acme").unwrap());
    assert!(
        !auth.shielded("bob", "acme").unwrap(),
        "an employee is inside"
    );
    assert!(
        !auth.shielded("op", "nordisk").unwrap(),
        "no account owns nordisk"
    );
    assert!(!auth.shielded("op", "op").unwrap());
    assert_eq!(
        auth.account_of_namespace("acme/team").unwrap().as_deref(),
        Some("acme")
    );
    assert_eq!(auth.account_of_namespace("nordisk").unwrap(), None);
    assert_eq!(
        auth.account_of_principal("bob").unwrap().as_deref(),
        Some("acme")
    );
    assert_eq!(
        auth.account_of_principal("acme").unwrap().as_deref(),
        Some("acme")
    );
    assert_eq!(
        auth.account_of_principal("robot").unwrap(),
        None,
        "a machine is no account"
    );
    assert!(auth.is_account_admin("acme", "acme").unwrap());
    assert!(!auth.is_account_admin("bob", "acme").unwrap());
    assert!(
        !auth.is_account_admin("op", "acme").unwrap(),
        "server Admin is not the account's admin"
    );
    auth.grant(Grant::namespace("bob", "acme", Level::Admin))
        .unwrap();
    assert!(
        auth.is_account_admin("bob", "acme").unwrap(),
        "an admin the account admin appointed"
    );
}

#[test]
fn the_account_admin_approves_a_time_limited_look_and_takes_it_back() {
    let w = world();
    let auth = &w.fx.auth;
    let asked = auth
        .request_operator_access("acme", "op", "ticket 4711: a push hangs", 60, NOW)
        .unwrap();
    assert_eq!(asked.state, OperatorAccessState::Pending);
    assert_eq!(asked.code.len(), 7, "a code for the eye: {asked:?}");
    assert!(
        !auth.may_at("op", &w.site, Level::Read, NOW + 1).unwrap(),
        "asking is not having"
    );
    // Asking again while it waits answers the same request.
    let again = auth
        .request_operator_access("acme", "op", "again", 60, NOW + 5)
        .unwrap();
    assert_eq!(again.id, asked.id);

    // Only the account's admin decides: not the employee, not the operator.
    assert!(auth
        .decide_operator_access(&asked.id, "bob", true, None, NOW + 10)
        .is_err());
    assert!(auth
        .decide_operator_access(&asked.id, "op", true, None, NOW + 10)
        .is_err());
    let granted = auth
        .decide_operator_access(&asked.id, "acme", true, None, NOW + 10)
        .unwrap();
    assert_eq!(granted.state, OperatorAccessState::Approved);
    assert_eq!(
        granted.granted_until_unix_ms,
        NOW + 10 + HOUR_MS,
        "the minutes asked for"
    );
    assert_eq!(granted.decided_by, "acme");

    assert!(
        auth.may_at("op", &w.site, Level::Read, NOW + 20).unwrap(),
        "the approved look reads"
    );
    assert!(
        !auth.may_at("op", &w.site, Level::Write, NOW + 20).unwrap(),
        "and only reads"
    );
    assert!(!auth.shielded_at("op", "acme", NOW + 20).unwrap());
    // It ends on its own.
    assert!(
        !auth
            .may_at("op", &w.site, Level::Read, NOW + 10 + HOUR_MS + 1)
            .unwrap(),
        "expired"
    );

    // And the account admin can end it early.
    let asked = auth
        .request_operator_access("acme", "op", "once more", 30, NOW + 2 * HOUR_MS)
        .unwrap();
    assert_ne!(
        asked.id, granted.id,
        "a new request after the last one ended"
    );
    auth.decide_operator_access(&asked.id, "acme", true, Some(15), NOW + 2 * HOUR_MS)
        .unwrap();
    assert!(auth
        .may_at("op", &w.site, Level::Read, NOW + 2 * HOUR_MS + 1)
        .unwrap());
    let revoked = auth
        .revoke_operator_access(&asked.id, "acme", NOW + 2 * HOUR_MS + 2)
        .unwrap();
    assert_eq!(revoked.state, OperatorAccessState::Revoked);
    assert!(!auth
        .may_at("op", &w.site, Level::Read, NOW + 2 * HOUR_MS + 3)
        .unwrap());

    let rows = auth.operator_access(Some("acme")).unwrap();
    assert_eq!(rows.len(), 1, "one row per (account, operator): the latest");
    assert!(auth.operator_access(Some("globex")).unwrap().is_empty());
}

#[test]
fn a_refused_or_unanswered_request_grants_nothing_and_limits_hold() {
    let w = world();
    let auth = &w.fx.auth;
    let asked = auth
        .request_operator_access("acme", "op", "why", 60, NOW)
        .unwrap();
    let refused = auth
        .decide_operator_access(&asked.id, "acme", false, None, NOW + 1)
        .unwrap();
    assert_eq!(refused.state, OperatorAccessState::Refused);
    assert!(!auth.may_at("op", &w.site, Level::Read, NOW + 2).unwrap());
    assert!(
        auth.decide_operator_access(&asked.id, "acme", true, None, NOW + 3)
            .is_err(),
        "decided once"
    );

    // An unanswered request lapses.
    let asked = auth
        .request_operator_access("acme", "op", "why", 60, NOW + 10)
        .unwrap();
    let late = NOW + 10 + gunnar_core::OPERATOR_REQUEST_TTL_MS + 1;
    assert!(
        auth.decide_operator_access(&asked.id, "acme", true, None, late)
            .is_err(),
        "too late"
    );

    // A look is bounded: at most a week, and a reason is required.
    assert!(
        auth.request_operator_access("acme", "op", "", 60, NOW)
            .is_err(),
        "no reason"
    );
    assert!(auth
        .request_operator_access(
            "acme",
            "op",
            "why",
            gunnar_core::OPERATOR_ACCESS_MAX_MINUTES + 1,
            NOW
        )
        .is_err());
    // Only into an account, and never into your own.
    assert!(
        auth.request_operator_access("nordisk", "op", "why", 60, NOW)
            .is_err(),
        "not an account"
    );
    assert!(
        auth.request_operator_access("op", "op", "why", 60, NOW)
            .is_err(),
        "your own"
    );
}

#[test]
fn removing_the_account_removes_its_grants() {
    let w = world();
    let auth = &w.fx.auth;
    let asked = auth
        .request_operator_access("acme", "op", "why", 60, NOW)
        .unwrap();
    auth.decide_operator_access(&asked.id, "acme", true, None, NOW)
        .unwrap();
    auth.remove_principal("acme").unwrap();
    assert!(auth.operator_access(Some("acme")).unwrap().is_empty());
}

/// **The box's own first identity is the estate, not an account**: the one
/// bootstrap enrolled (the installer's admin key) keeps its server reach, as
/// a machine does, and its namespace is the estate's own — the operator's, as
/// a namespace no person owns is. Every OTHER person is an account.
#[test]
fn the_boxs_first_identity_is_the_estate_and_no_account() {
    let fx = Fixture::new();
    fx.auth
        .enroll_first_user("estate", common::KAT_ED25519)
        .unwrap();
    for id in ["op", "acme"] {
        fx.auth
            .add_principal(Principal::new(id, id, PrincipalKind::User).unwrap())
            .unwrap();
    }
    fx.auth.grant(Grant::server("op", Level::Admin)).unwrap();
    let seed = fx.repo("estate", "seed");
    let site = fx.repo("acme", "site");
    assert!(
        fx.auth.may("op", &seed, Level::Admin).unwrap(),
        "the estate's namespace is the operator's"
    );
    assert!(
        fx.auth.may("estate", &site, Level::Read).unwrap(),
        "the installer's key is the estate's plumbing"
    );
    assert!(
        !fx.auth.may("op", &site, Level::Read).unwrap(),
        "a person's account is still closed to the operator"
    );
    assert_eq!(fx.auth.account_of_namespace("estate").unwrap(), None);
    assert_eq!(
        fx.auth.account_of_namespace("acme").unwrap().as_deref(),
        Some("acme")
    );
}