xtask/tests/there_is_one_identity_implementation.rs
//! **There is ONE identity implementation** (IDENTITY-HOST, owner GO 2026-10-27):
//! sign-up, login, the passkey ceremony, invites, the session store and step-up
//! are edda's `nornir-identity` (over `nornir-auth-webauthn`'s `ceremony` and
//! `nornir-identity-session`). A product implements `IdentityHost` and serves
//! `IdentityService`; it defines none of the pieces itself.
//!
//! This guard reads the product's source (every `src/**/*.rs` and every
//! `Cargo.toml`; tests, tooling, benches and vendored trees are not product code) and
//! names every definition of a known piece of an own implementation, by file
//! and line: the functions and types Gunnar, gunnar-ui, Holger and holger-ui
//! carried before the move, `webauthn-rs`, and a hand-built `HttpOnly` session
//! cookie. It reads this repository, and its console's beside it when that is
//! checked out (`../gunnar-ui`), and says which it read. No network.
use std::path::{Path, PathBuf};
/// The repositories this guard holds: this one, and its console when it is beside it.
const SUBJECTS: &[(&str, bool)] = &[(".", true), ("../gunnar-ui", false)];
/// `fn <name>(` or `fn <name><`: a function of an own implementation.
const FUNCTIONS: &[(&str, &str)] = &[
("sign_up", "sign-up is Identity::sign_up"),
("signup", "sign-up is Identity::sign_up"),
(
"web_challenge",
"a login's challenge is Identity::login_start",
),
("web_login", "a login is Identity::login_finish"),
(
"web_register",
"a passkey registration is Identity::redeem_finish",
),
(
"register_web_credential",
"a passkey registration is Identity::redeem_finish",
),
("login_start", "a login is the core's"),
("login_finish", "a login is the core's"),
("create_invite", "an invite is Identity::invite"),
("check_invite", "an invite is the core's"),
("revoke_invite", "an invite is the core's"),
("list_invites", "an invite is the core's"),
(
"redeem_invite",
"a redemption is Identity::redeem_start/finish",
),
("redeem_start", "a redemption is the core's"),
("redeem_finish", "a redemption is the core's"),
("mint_session", "a session is minted by the core"),
(
"verify_session",
"a session is verified by the core (Identity::session)",
),
("verify_step_up", "a step-up is Identity::step_up_finish"),
("step_up_start", "a step-up is the core's"),
("step_up_finish", "a step-up is the core's"),
("verify_assertion", "the ceremony is nornir-auth-webauthn's"),
(
"verify_attestation",
"the ceremony is nornir-auth-webauthn's",
),
("check_ceremony", "the ceremony is nornir-auth-webauthn's"),
(
"check_relying_party",
"the ceremony is nornir-auth-webauthn's",
),
("client_data", "the ceremony is nornir-auth-webauthn's"),
(
"take_registration",
"the ceremony is nornir-auth-webauthn's",
),
("take_assertion", "the ceremony is nornir-auth-webauthn's"),
(
"room_for_a_ceremony",
"the ceremony store is nornir-auth-webauthn's",
),
(
"legacy_credential",
"a passkey lives at the server, in the core's store",
),
(
"move_to_gunnar",
"a passkey lives at the server, in the core's store",
),
];
/// `struct|enum|trait|type <Name>`: a type of an own implementation.
const TYPES: &[(&str, &str)] = &[
("SessionKey", "a session is the core's opaque token"),
("SessionKeys", "a session is the core's opaque token"),
(
"SessionFloor",
"the floors are nornir-identity-session's Revocations",
),
(
"ConsoleEpoch",
"the floors are nornir-identity-session's Revocations",
),
("WebSessions", "a session is the core's"),
(
"CeremonyStore",
"the ceremony store is nornir-auth-webauthn's",
),
("Challenges", "the ceremony store is nornir-auth-webauthn's"),
("NewInvite", "an invite is nornir-identity-session's"),
("Invite", "an invite is nornir-identity-session's"),
("Revocations", "the floors are nornir-identity-session's"),
(
"HostCookie",
"the session cookie is nornir-identity-session's",
),
];
/// Text that is an own implementation wherever it stands.
const TEXT: &[(&str, &str)] = &[
(
"webauthn_rs",
"the ceremony is nornir-auth-webauthn's, never webauthn-rs",
),
(
"webauthn-rs",
"the ceremony is nornir-auth-webauthn's, never webauthn-rs",
),
(
"HttpOnly",
"the session cookie is nornir-identity-session's HostCookie",
),
];
/// The few lines that carry a known name and are NOT identity, each with its reason.
const NOT_IDENTITY: &[(&str, &str, &str)] = &[
(
"crates/gunnar-core/src/key_attestation.rs",
"fn verify_attestation",
"an sk-ssh security key's attestation (SSH keys), not the WebAuthn ceremony",
),
(
"/clans.rs",
"struct Invite",
"a clan invitation (membership of a clan), not an enrolment invite",
),
(
"crates/gunnar-ui-web/",
"struct Invite",
"the console page renders the invites the server answers (rendering only)",
),
(
"crates/gunnar-ui-web/",
"struct NewInvite",
"the console page renders the invites the server answers (rendering only)",
),
];
fn not_identity(rel: &str, what: &str) -> bool {
NOT_IDENTITY
.iter()
.any(|(path, name, _)| rel.contains(path) && what == *name)
}
fn root() -> PathBuf {
Path::new(env!("CARGO_MANIFEST_DIR"))
.parent()
.expect("xtask has a parent")
.to_path_buf()
}
/// Directories that are not product code: build output, tests, tooling, vendored and bench trees.
const NOT_PRODUCT: &[&str] = &[
"target",
"tests",
"vendor",
"benches",
"bench",
"examples",
"fixtures",
"xtask",
".git",
"node_modules",
"estate-tests",
"browser-tests",
"docs",
"installtest",
"testdata",
];
fn walk(dir: &Path, in_src: bool, out: &mut Vec<PathBuf>) {
let Ok(entries) = std::fs::read_dir(dir) else {
return;
};
for e in entries.flatten() {
let p = e.path();
let name = e.file_name().to_string_lossy().into_owned();
if p.is_dir() {
if !NOT_PRODUCT.contains(&name.as_str()) {
walk(&p, in_src || name == "src", out);
}
} else if (in_src && p.extension().is_some_and(|x| x == "rs")) || name == "Cargo.toml" {
out.push(p);
}
}
}
/// The product files of one repository: every `src/**/*.rs` and every `Cargo.toml`.
fn product_files(repo: &Path) -> Vec<PathBuf> {
let mut out = Vec::new();
walk(repo, false, &mut out);
out.sort();
out
}
fn defines(code: &str, keyword: &str, name: &str) -> bool {
let mut rest = code;
while let Some(at) = rest.find(keyword) {
let before_ok = at == 0
|| !rest.as_bytes()[at - 1].is_ascii_alphanumeric() && rest.as_bytes()[at - 1] != b'_';
let after = &rest[at + keyword.len()..];
if before_ok {
if let Some(tail) = after.strip_prefix(name) {
let next = tail.chars().next();
let boundary = match keyword {
"fn " => matches!(next, Some('(') | Some('<')),
_ => !next.is_some_and(|c| c.is_alphanumeric() || c == '_'),
};
if boundary {
return true;
}
}
}
rest = &rest[at + keyword.len()..];
}
false
}
/// Every own-implementation line under `repo`, as `path:line: what — why`.
fn findings(repo: &Path) -> Vec<String> {
let mut out = Vec::new();
for file in product_files(repo) {
let text = std::fs::read_to_string(&file).unwrap_or_default();
let rel = file
.strip_prefix(repo)
.unwrap_or(&file)
.display()
.to_string();
out.extend(findings_in(&rel, &text));
}
out
}
/// The own-implementation lines of one product file (`rel` is its path in the repository).
fn findings_in(rel: &str, text: &str) -> Vec<String> {
let rel = rel.to_string();
let mut out = Vec::new();
// a `#[cfg(test)]` module is a test, not product code: from the attribute to the
// brace that closes the module (`None` = outside one; `Some(depth)` = inside)
let mut test_mod: Option<i64> = None;
let mut cfg_test = false;
for (n, line) in text.lines().enumerate() {
let trimmed = line.trim();
if !rel.ends_with("Cargo.toml") {
if let Some(depth) = test_mod.as_mut() {
let code = line.split("//").next().unwrap_or("");
*depth += code.matches('{').count() as i64 - code.matches('}').count() as i64;
if *depth <= 0 && code.contains('}') {
test_mod = None;
}
continue;
}
if trimmed == "#[cfg(test)]" {
cfg_test = true;
continue;
}
if cfg_test {
cfg_test = false;
let item = trimmed.strip_prefix("pub ").unwrap_or(trimmed);
if item.starts_with("mod ") && item.contains('{') {
let code = line.split("//").next().unwrap_or("");
let depth = code.matches('{').count() as i64 - code.matches('}').count() as i64;
if depth > 0 {
test_mod = Some(depth);
}
continue;
}
}
}
let code = line.split("//").next().unwrap_or("");
let code = if rel.ends_with("Cargo.toml") {
line.split('#').next().unwrap_or("")
} else {
code
};
let mut hit = |what: String, why: &str| {
if !not_identity(&rel, &what) {
out.push(format!("{rel}:{}: {what}: {why}", n + 1));
}
};
if !rel.ends_with("Cargo.toml") {
for (name, why) in FUNCTIONS {
if defines(code, "fn ", name) {
hit(format!("fn {name}"), why);
}
}
for (name, why) in TYPES {
for keyword in ["struct ", "enum ", "trait ", "type "] {
if defines(code, keyword, name) {
hit(format!("{keyword}{name}"), why);
}
}
}
}
for (needle, why) in TEXT {
if code.contains(needle) {
hit(format!("`{needle}`"), why);
}
}
}
out
}
/// ★ The guard: no product file of this repository (or its console beside it)
/// defines a piece of identity edda's `nornir-identity` owns.
#[test]
fn there_is_one_identity_implementation() {
let mut all = Vec::new();
for (rel, required) in SUBJECTS {
let repo = root().join(rel);
if !repo.join("Cargo.toml").is_file() {
assert!(
!required,
"{}: the guard's own repository has no Cargo.toml",
repo.display()
);
eprintln!("there_is_one_identity_implementation: {rel} is not checked out beside this repository; it is held by its own copy of this guard");
continue;
}
let found = findings(&repo);
eprintln!("there_is_one_identity_implementation: read {} files of {rel}, {} own-implementation lines", product_files(&repo).len(), found.len());
all.extend(found.into_iter().map(|f| format!("{rel}/{f}")));
}
assert!(
all.is_empty(),
"identity is edda's nornir-identity (IDENTITY-HOST): these lines define their own piece of it ({}):\n{}",
all.len(),
all.join("\n")
);
}
/// A file's own `#[cfg(test)]` module is a test, not product code (CI-RED, 2026-10-30:
/// gunnar-ui's sso.rs asserts its cookie `contains("HttpOnly")` in its tests); the
/// product code before and after it is still read.
#[test]
fn the_scanner_leaves_a_files_test_module_alone() {
let src = "pub fn cookie() -> String { host_cookie() }\n\
#[cfg(test)]\n\
mod tests {\n \
#[test]\n \
fn a_cookie() {\n \
assert!(cookie().contains(\"HttpOnly\"));\n \
}\n\
}\n\
pub fn after() -> &'static str { \"HttpOnly\" }\n";
let found = findings_in("crates/x/src/lib.rs", src);
assert_eq!(found, vec!["crates/x/src/lib.rs:9: `HttpOnly`: the session cookie is nornir-identity-session's HostCookie".to_string()], "{found:?}");
let product = findings_in(
"crates/x/src/lib.rs",
"pub fn c() -> &'static str { \"; HttpOnly\" }\n",
);
assert_eq!(product.len(), 1, "product code is still read: {product:?}");
}
/// The scanner sees what it is meant to and nothing else.
#[test]
fn the_scanner_names_a_definition_and_not_a_call() {
assert!(defines(
" pub async fn web_login(&self) {",
"fn ",
"web_login"
));
assert!(defines(
"fn verify_session<T>(t: T)",
"fn ",
"verify_session"
));
assert!(
!defines("client.web_login(request).await", "fn ", "web_login"),
"a call is not a definition"
);
assert!(
!defines("fn web_login_page() {}", "fn ", "web_login"),
"another name"
);
assert!(defines("pub struct Invite {", "struct ", "Invite"));
assert!(
!defines("pub struct InviteAsk {", "struct ", "Invite"),
"another type"
);
assert!(
!defines("pub struct MyInvite {", "struct ", "Invite"),
"another type"
);
assert!(defines("pub type Challenges = X;", "type ", "Challenges"));
}