gunnar-grpc 3.0.0

The tonic service implementations and the event bus behind gunnar's gRPC control plane.
Documentation
//! **A person's own keys are theirs to manage** (owner, 2026-10-04: the
//! console is a thin layer — it calls gunnar AS THE PERSON, and gunnar decides).
//!
//! Until this file, `Access.AddKey`, `ListKeys` and `RevokeKey` answered a
//! server-wide `Admin` only, even when the caller and the target were the same
//! principal. A person could therefore not add, list or revoke their own SSH
//! key as themselves, and the console filled that hole with its own admin key
//! plus an attestation it signed with a custody key it held for the person —
//! which defeats the very rule the attestation exists for ("an admin cannot
//! silently mint access as another person").
//!
//! The self-scope: a caller acting on ITS OWN principal may add a key (no
//! attestation owed — the call is already its own signed word,
//! `key_attestation::Requirement::CallerIsTarget`), list its keys, and revoke a
//! key that is its own. Everything about anybody else stays a server `Admin`'s,
//! and a refusal never says whose a key is.
//!
//! Driven through the real gate over real signatures, against the memory
//! control plane, exactly as `tests/auth.rs` is.

use std::sync::Arc;

use gunnar_grpc::auth::AuthGate;
use gunnar_grpc::{
    sign_credential_for, AccessService, AuthMode, Caller, ControlPlane, EventBus,
    MemoryControlPlane, ServerConfig, ServiceContext, CREDENTIAL_METADATA_KEY,
};
use gunnar_proto::access_server::Access as _;
use gunnar_proto::*;
use tonic::{Code, Request};

const NS: &str = "nordisk";
const REPO: &str = "gunnar";

fn mint() -> ssh_key::PrivateKey {
    ssh_key::PrivateKey::random(
        &mut rand::rand_core::UnwrapErr(rand::rngs::SysRng),
        ssh_key::Algorithm::Ed25519,
    )
    .expect("mint an ed25519 key")
}

fn openssh_of(key: &ssh_key::PrivateKey) -> String {
    key.public_key()
        .to_openssh()
        .expect("render the public key")
}

fn fingerprint_of(key: &ssh_key::PrivateKey) -> String {
    key.public_key()
        .fingerprint(ssh_key::HashAlg::Sha256)
        .to_string()
}

fn now_ms() -> i64 {
    std::time::SystemTime::now()
        .duration_since(std::time::UNIX_EPOCH)
        .expect("after 1970")
        .as_millis() as i64
}

struct Harness {
    plane: Arc<MemoryControlPlane>,
    ctx: ServiceContext,
}

impl Harness {
    fn new() -> Harness {
        let plane = Arc::new(MemoryControlPlane::with_repo(NS, REPO));
        let control: Arc<dyn ControlPlane> = plane.clone();
        let ctx = ServiceContext::new(
            control,
            Arc::new(EventBus::new(64)),
            ServerConfig::default(),
        );
        assert_eq!(ctx.config.auth, AuthMode::Required, "the shipped default");
        Harness { plane, ctx }
    }

    /// A principal with one freshly minted key and no grant at all.
    fn person(&self, name: &str) -> (String, ssh_key::PrivateKey) {
        let key = mint();
        let principal = self
            .plane
            .add_principal(name, name, PrincipalKind::User)
            .expect("principal added");
        self.plane
            .add_key(&principal.id, &openssh_of(&key), name, None, "", None)
            .expect("key registered");
        (principal.id, key)
    }

    /// The caller the real gate produces for this key.
    fn caller_for(&self, key: &ssh_key::PrivateKey, nonce: &str) -> Caller {
        let credential = sign_credential_for(key, now_ms(), nonce, "Admin.Health").expect("sign");
        let mut request = Request::new(());
        request.metadata_mut().insert(
            CREDENTIAL_METADATA_KEY,
            credential.to_header_value().parse().expect("ascii"),
        );
        AuthGate::new(Arc::clone(&self.ctx.control), self.ctx.config.auth)
            .authenticate_bound(request.metadata(), now_ms(), Some("Admin.Health"))
            .expect("a freshly signed credential from a registered key must verify")
    }

    fn authed<T>(&self, caller: &Caller, message: T) -> Request<T> {
        let mut request = Request::new(message);
        request.extensions_mut().insert(caller.clone());
        request
    }

    fn access(&self) -> AccessService {
        AccessService::new(self.ctx.clone())
    }

    fn keys_of(&self, principal: &str) -> Vec<String> {
        let mut keys: Vec<String> = self
            .plane
            .list_keys(Some(principal))
            .expect("list")
            .into_iter()
            .map(|k| k.fingerprint)
            .collect();
        keys.sort();
        keys
    }
}

fn add(principal: &str, key: &ssh_key::PrivateKey) -> AddKeyRequest {
    AddKeyRequest {
        principal_id: principal.to_owned(),
        openssh_public_key: openssh_of(key),
        comment: "laptop".to_owned(),
        expires_at_unix_ms: None,
        attestation: None,
    }
}

/// ★ **A person adds a key to themselves, with no server grant and no
/// attestation.** They already hold a key, so an ADMIN adding one would owe
/// their attestation; the person asking is their own consent.
#[tokio::test]
async fn a_person_adds_a_key_to_themselves_with_no_grant_and_no_attestation() {
    let harness = Harness::new();
    let (kim, kim_key) = harness.person("kim");
    let caller = harness.caller_for(&kim_key, "nonce-self-add-0001");
    let laptop = mint();

    let added = harness
        .access()
        .add_key(harness.authed(&caller, add(&kim, &laptop)))
        .await
        .expect("a person may add a key to their own principal")
        .into_inner()
        .key
        .expect("the key comes back");
    assert_eq!(added.principal_id, kim);
    assert_eq!(added.fingerprint, fingerprint_of(&laptop));
    let mut want = vec![fingerprint_of(&kim_key), fingerprint_of(&laptop)];
    want.sort();
    assert_eq!(harness.keys_of(&kim), want);
}

/// …and only to themselves: a key on somebody else is still a server
/// `Admin`'s act, and nothing lands.
#[tokio::test]
async fn a_person_may_not_add_a_key_to_anybody_else() {
    let harness = Harness::new();
    let (_kim, kim_key) = harness.person("kim");
    let (ola, ola_key) = harness.person("ola");
    let caller = harness.caller_for(&kim_key, "nonce-self-add-0002");

    let status = harness
        .access()
        .add_key(harness.authed(&caller, add(&ola, &mint())))
        .await
        .expect_err("a key on another person is not the caller's to add");
    assert_eq!(status.code(), Code::PermissionDenied, "{status:?}");
    assert_eq!(harness.keys_of(&ola), vec![fingerprint_of(&ola_key)]);
}

/// ★ **A person lists their own keys** — and not anybody else's, and not the
/// whole table.
#[tokio::test]
async fn a_person_lists_their_own_keys_and_nobody_elses() {
    let harness = Harness::new();
    let (kim, kim_key) = harness.person("kim");
    let (ola, _ola_key) = harness.person("ola");
    let caller = harness.caller_for(&kim_key, "nonce-self-list-001");

    let keys = harness
        .access()
        .list_keys(harness.authed(
            &caller,
            ListKeysRequest {
                principal_id: kim.clone(),
            },
        ))
        .await
        .expect("a person may list their own keys")
        .into_inner()
        .keys;
    assert_eq!(
        keys.iter()
            .map(|k| k.fingerprint.clone())
            .collect::<Vec<_>>(),
        vec![fingerprint_of(&kim_key)]
    );

    for (asked, what) in [
        (ola.as_str(), "another person's keys"),
        ("", "every key on the box"),
    ] {
        let status = harness
            .access()
            .list_keys(harness.authed(
                &caller,
                ListKeysRequest {
                    principal_id: asked.to_owned(),
                },
            ))
            .await
            .expect_err(what);
        assert_eq!(status.code(), Code::PermissionDenied, "{what}: {status:?}");
    }
}

/// ★ **A person revokes a key that is theirs** — and a key that is not theirs
/// is refused exactly as a key that does not exist is, so the refusal is no
/// oracle for whose a fingerprint is.
#[tokio::test]
async fn a_person_revokes_their_own_key_and_the_refusal_for_anothers_names_nobody() {
    let harness = Harness::new();
    let (kim, kim_key) = harness.person("kim");
    let (ola, ola_key) = harness.person("ola");
    let laptop = mint();
    harness
        .plane
        .add_key(&kim, &openssh_of(&laptop), "laptop", None, &kim, None)
        .expect("a second key of kim's");
    let caller = harness.caller_for(&kim_key, "nonce-self-revoke-1");

    harness
        .access()
        .revoke_key(harness.authed(
            &caller,
            RevokeKeyRequest {
                fingerprint: fingerprint_of(&laptop),
            },
        ))
        .await
        .expect("a person may revoke their own key");
    assert_eq!(harness.keys_of(&kim), vec![fingerprint_of(&kim_key)]);

    let theirs = harness
        .access()
        .revoke_key(harness.authed(
            &caller,
            RevokeKeyRequest {
                fingerprint: fingerprint_of(&ola_key),
            },
        ))
        .await
        .expect_err("another person's key is not the caller's to revoke");
    let nobodys = harness
        .access()
        .revoke_key(harness.authed(
            &caller,
            RevokeKeyRequest {
                fingerprint: "SHA256:no-such-key-anywhere".to_owned(),
            },
        ))
        .await
        .expect_err("a key that does not exist is not the caller's either");
    assert_eq!(theirs.code(), Code::PermissionDenied, "{theirs:?}");
    assert_eq!(nobodys.code(), Code::PermissionDenied, "{nobodys:?}");
    assert!(
        !theirs.message().contains(&ola),
        "the refusal says whose the key is: {}",
        theirs.message()
    );
    assert_eq!(
        theirs.message(),
        nobodys.message(),
        "another person's key is told apart from no key at all"
    );
    assert_eq!(harness.keys_of(&ola), vec![fingerprint_of(&ola_key)]);
}

/// A person may revoke the very key the call is signed with: it is theirs, and
/// the call that removes it is already authenticated. What they lose is the
/// next call — which is what they asked for.
#[tokio::test]
async fn a_person_may_revoke_the_key_they_are_signing_with() {
    let harness = Harness::new();
    let (kim, kim_key) = harness.person("kim");
    let caller = harness.caller_for(&kim_key, "nonce-self-revoke-2");
    harness
        .access()
        .revoke_key(harness.authed(
            &caller,
            RevokeKeyRequest {
                fingerprint: fingerprint_of(&kim_key),
            },
        ))
        .await
        .expect("the signing key is the caller's own");
    assert!(harness.keys_of(&kim).is_empty());
}

/// An administrator's reach is unchanged: a server `Admin` adds a key to
/// another person who holds one — and still owes that person's attestation.
#[tokio::test]
async fn an_admin_adding_to_another_still_owes_their_attestation() {
    let harness = Harness::new();
    let (admin, admin_key) = harness.person("admin");
    harness.plane.grant_server(&admin, GrantLevel::Admin);
    let (kim, kim_key) = harness.person("kim");
    let caller = harness.caller_for(&admin_key, "nonce-admin-add-001");

    let status = harness
        .access()
        .add_key(harness.authed(&caller, add(&kim, &mint())))
        .await
        .expect_err("kim holds a key, so an admin's add owes kim's attestation");
    assert_ne!(status.code(), Code::Ok);
    assert_eq!(harness.keys_of(&kim), vec![fingerprint_of(&kim_key)]);
}