engine: services-operator privilege foundation

A typed Priv enum (Auspex/Suspend/Admin) and a Copy Privs bitset carried on
Sender, instead of Anope's stringly-typed privilege namespace and its two
separate gating mechanisms. Opers are declared in TOML ([[oper]] account +
privs); the engine loads them and resolves the sender's privileges per command,
so a module just checks from.privs.has(Priv::X). First use: NickServ INFO
reveals hidden fields to an auspex oper. Unblocks SUSPEND, admin LIST, SASET,
GETEMAIL. Priv bitset test + end-to-end auspex engine test.
This commit is contained in:
Jean Chevronnet 2026-07-13 03:42:59 +00:00
parent 4600ee062c
commit f1415bedb2
No known key found for this signature in database
6 changed files with 139 additions and 6 deletions

View file

@ -1,8 +1,9 @@
use fedserv_api::{human_time, Store};
use fedserv_api::{human_time, Priv, Store};
use fedserv_api::{Sender, ServiceCtx};
// INFO [account]: show an account's registration details. The email is shown
// only to the account's own owner.
// INFO [account]: show an account's registration details. The email and other
// private fields are shown to the account's own owner, or to an oper with the
// auspex privilege.
pub fn handle(me: &str, from: &Sender, args: &[&str], ctx: &mut ServiceCtx, db: &dyn Store) {
let name = args.get(1).copied().unwrap_or(from.nick);
let Some(acct) = db.account(name) else {
@ -11,7 +12,8 @@ pub fn handle(me: &str, from: &Sender, args: &[&str], ctx: &mut ServiceCtx, db:
};
ctx.notice(me, from.uid, format!("Information for \x02{}\x02:", acct.name));
ctx.notice(me, from.uid, format!(" Registered : {}", human_time(acct.ts)));
if from.account == Some(acct.name.as_str()) {
let is_owner = from.account == Some(acct.name.as_str());
if is_owner || from.privs.has(Priv::Auspex) {
match &acct.email {
Some(email) if acct.verified => ctx.notice(me, from.uid, format!(" Email : {email}")),
Some(email) => ctx.notice(me, from.uid, format!(" Email : {email} (unconfirmed)")),