crates/monetize-server/src/auth.rs
//! The bearer. `nornir_service::auth_interceptor` is the interceptor (constant-time
//! compare, empty ⇒ open) and `nornir_service::resolve_token` the ladder, both
//! reused as-is — the suite's token ladder is `flag > $MONETIZE_SERVER_TOKEN >
//! token file`, and only the two literals are monetize's.
use std::path::Path;
use std::sync::Arc;
use crate::config::{OPERATOR_TOKEN_ENV, TOKEN_ENV};
pub use nornir_service::{auth_interceptor, TokenSource};
/// The ladder, walked once: `--token` > `$MONETIZE_SERVER_TOKEN` > `token_file`
/// (trimmed; an empty file falls through) > `""` — which the interceptor reads
/// as open, so `cli::serve` REFUSES it (stinky #9).
pub fn resolve(flag: Option<&str>, token_file: &Path) -> (String, TokenSource) {
nornir_service::resolve_token_whence(flag, TOKEN_ENV, Some(token_file))
}
/// The OPERATOR bearer's ladder, the same shape: `--operator-token` >
/// `$MONETIZE_SERVER_OPERATOR_TOKEN` > `<data_dir>/operator-token` > `""`
/// — which, unlike the service bearer's `""`, means CLOSED: see [`Operator`].
pub fn resolve_operator(flag: Option<&str>, file: &Path) -> (String, TokenSource) {
nornir_service::resolve_token_whence(flag, OPERATOR_TOKEN_ENV, Some(file))
}
/// **`SignupAdmin`'s door: the service bearer OR the operator bearer**, and
/// which one it was (g-refund-verb, 2026-09-28).
///
/// Every service here is behind ONE bearer, and the customer-facing console
/// backend holds it (it reaches `Signup` through it). `SignupAdmin` now also
/// carries `Refund` and `Settle`, which give a payment back, so that bearer
/// cannot be enough for them. This is the least that separates them: the
/// same interceptor as before for the service bearer (reused, constant-time),
/// and a request whose bearer [`nornir_service::token_matches`] the operator
/// bearer gets the [`Operator`] mark — which `Refund` / `Settle` demand and
/// the reads ignore. Nothing else changes: no other service sees the operator
/// bearer, and every existing caller of `SignupAdmin` keeps working.
///
/// An EMPTY operator bearer marks nobody — the money verbs are then refused
/// to every caller, including an open (dev) server's; a refund is never
/// "open". One equal to the service bearer is treated as empty too: two names
/// for one secret separate nothing.
pub fn signup_admin_interceptor(
token: Arc<String>,
operator: Arc<String>,
) -> impl Fn(tonic::Request<()>) -> Result<tonic::Request<()>, tonic::Status> + Clone {
let service = auth_interceptor(token.clone());
let operator = if operator.is_empty() || operator.as_str() == token.as_str() {
Arc::new(String::new())
} else {
operator
};
move |mut req: tonic::Request<()>| {
// A CONTROL caller — a per-call SSHSIG from one of this monetize's
// console principals, verified at the edge (`control::layer`) — is the
// operator's own console (monetize.<apex>): the money verbs are its.
if req
.extensions()
.get::<crate::control::ControlCaller>()
.is_some()
{
req.extensions_mut().insert(Operator);
return Ok(req);
}
let supplied = req
.metadata()
.get("authorization")
.and_then(|v| v.to_str().ok())
.and_then(|s| s.strip_prefix("Bearer "))
.unwrap_or("")
.to_owned();
if !operator.is_empty() && nornir_service::token_matches(&supplied, operator.as_str()) {
req.extensions_mut().insert(Operator);
return Ok(req);
}
service(req)
}
}
/// **Who stands behind an EMPTY actor ticket** — the extension a door puts on
/// a `Purchase` call, read by `services::purchase::check_actor`.
///
/// An empty ticket used to mean "the operator" by itself, which made every
/// in-process caller the operator too: the public purchase page (deleted since,
/// OD6) reached the service with no ticket and could buy for, and read the orders
/// of, any tenant. Now an empty ticket means nothing on its own; the caller is what
/// the door that let the request in says it is, and a request through no door
/// is nobody.
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum Caller {
/// The bearer (or the control signature) was checked: the operator, or a
/// console BFF that holds the bearer.
Door,
}
/// **The SITE an estate-facing call comes from**: the request carried a joined estate's own
/// bearer (minted at its enrolment, monetize-front `attach`), and this is that estate's id.
/// What routes a signup's money to the estate's own account (`Registry::route_for`).
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct CallerEstate(pub String);
/// **An estate-facing service's door**: a joined estate's own bearer marks the call
/// [`CallerEstate`]; anything else is the service bearer's door exactly as before.
pub fn estate_interceptor(
token: Arc<String>,
state: Arc<crate::state::AppState>,
) -> impl Fn(tonic::Request<()>) -> Result<tonic::Request<()>, tonic::Status> + Clone {
estate_or(state, crate::control::interceptor(token))
}
/// **An estate-facing door that also lets a joined estate in by its OWN bearer**: that
/// bearer marks the call [`CallerEstate`] — never the operator, never the service's own
/// door ([`Caller::Door`]) — and anything else goes to `inner` exactly as before.
pub fn estate_or<I>(
state: Arc<crate::state::AppState>,
inner: I,
) -> impl Fn(tonic::Request<()>) -> Result<tonic::Request<()>, tonic::Status> + Clone
where
I: Fn(tonic::Request<()>) -> Result<tonic::Request<()>, tonic::Status> + Clone,
{
move |mut req: tonic::Request<()>| {
if req
.extensions()
.get::<crate::control::ControlCaller>()
.is_none()
{
let offered = req
.metadata()
.get("authorization")
.and_then(|v| v.to_str().ok())
.and_then(|s| s.strip_prefix("Bearer "))
.unwrap_or("")
.to_owned();
if let Some(estate) = state.fleet.estate_of_bearer_at(&offered, state.now()) {
req.extensions_mut().insert(CallerEstate(estate));
return Ok(req);
}
}
inner(req)
}
}
/// **The request carried the OPERATOR bearer**, not the service one: the
/// extension [`signup_admin_interceptor`] puts on a `SignupAdmin` call, and
/// what `SignupAdmin.Refund` / `Settle` demand. A marker, never a credential.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Operator;
#[cfg(test)]
mod tests {
use super::*;
/// ★ **A joined estate's own bearer names the site the call comes from** (owner
/// 2026-10-11: gunnar.rs and test2.gunnar.rs on ONE box, each its own Stripe): its
/// bearer marks the call [`CallerEstate`]; the service bearer passes unmarked; anything
/// else is refused as before.
#[test]
fn an_estates_own_bearer_marks_the_call_with_its_estate() {
let state = Arc::new(crate::state::AppState::in_memory(
crate::registry::Registry::new(),
));
state.fleet.know_bearer(
&crate::fleet::bearer_digest("estate-bearer-of-test2-0123456789"),
"test2-gunnar-rs",
);
let door = estate_interceptor(Arc::new("service-bearer-0123456789abcdef".into()), state);
let call = |b: &str| {
let mut r = tonic::Request::new(());
r.metadata_mut()
.insert("authorization", format!("Bearer {b}").parse().unwrap());
door(r)
};
let marked =
call("estate-bearer-of-test2-0123456789").expect("an estate's bearer is let in");
assert_eq!(
marked.extensions().get::<CallerEstate>(),
Some(&CallerEstate("test2-gunnar-rs".into()))
);
let service =
call("service-bearer-0123456789abcdef").expect("the service bearer as before");
assert_eq!(service.extensions().get::<CallerEstate>(), None);
assert!(call("nobody").is_err());
}
/// ★ **An estate's own bearer opens every door its console calls at the ONE box** —
/// Products, Purchase, EstateOrder as Signup (owner 2026-10-11: the estate holds no service
/// bearer) — marked with its estate and NEVER as the operator or the service's own door:
/// a purchase it starts needs the account's ticket, a refund stays the operator's.
#[test]
fn an_estates_own_bearer_opens_the_estate_facing_doors_as_that_estate_only() {
let state = Arc::new(crate::state::AppState::in_memory(
crate::registry::Registry::new(),
));
state.fleet.know_bearer(
&crate::fleet::bearer_digest("estate-bearer-of-test2-0123456789"),
"test2-gunnar-rs",
);
let token = Arc::new("service-bearer-0123456789abcdef".to_string());
let call =
|door: &dyn Fn(tonic::Request<()>) -> Result<tonic::Request<()>, tonic::Status>,
b: &str| {
let mut r = tonic::Request::new(());
r.metadata_mut()
.insert("authorization", format!("Bearer {b}").parse().unwrap());
door(r)
};
let plain = estate_or(state.clone(), crate::control::interceptor(token.clone()));
let marked = call(&plain, "estate-bearer-of-test2-0123456789")
.expect("an estate's bearer is let in");
assert_eq!(
marked.extensions().get::<CallerEstate>(),
Some(&CallerEstate("test2-gunnar-rs".into()))
);
assert_eq!(
marked.extensions().get::<Caller>(),
None,
"never the service's own door"
);
let service =
call(&plain, "service-bearer-0123456789abcdef").expect("the service bearer as before");
assert_eq!(
(
service.extensions().get::<CallerEstate>(),
service.extensions().get::<Caller>()
),
(None, Some(&Caller::Door))
);
assert!(call(&plain, "nobody").is_err());
let orders = estate_or(
state.clone(),
signup_admin_interceptor(
token.clone(),
Arc::new("operator-bearer-0123456789abcdef".into()),
),
);
let marked = call(&orders, "estate-bearer-of-test2-0123456789")
.expect("an estate's bearer is let in");
assert_eq!(
marked.extensions().get::<CallerEstate>(),
Some(&CallerEstate("test2-gunnar-rs".into()))
);
assert!(
marked.extensions().get::<Operator>().is_none(),
"never the operator"
);
assert!(call(&orders, "operator-bearer-0123456789abcdef")
.unwrap()
.extensions()
.get::<Operator>()
.is_some());
assert!(
include_str!("lib.rs")
.matches("crate::auth::estate_or(")
.count()
>= 3,
"Products, Purchase and EstateOrder take it"
);
}
#[test]
fn the_ladder_prefers_the_flag_then_the_file_and_falls_through_an_empty_file() {
let dir = std::env::temp_dir().join(format!("monetize-auth-{}", std::process::id()));
std::fs::create_dir_all(&dir).unwrap();
let file = dir.join("token");
std::fs::write(&file, " from-file \n").unwrap();
assert_eq!(
resolve(Some("from-flag"), &file),
("from-flag".into(), TokenSource::Flag)
);
assert_eq!(
resolve(Some(""), &file),
("from-file".into(), TokenSource::File(file.clone()))
);
std::fs::write(&file, "\n").unwrap();
assert_eq!(resolve(None, &file), (String::new(), TokenSource::Nothing));
std::fs::remove_dir_all(&dir).unwrap();
}
}