nickserv: INFO shows services role + GroupServ groups (config∪runtime oper privs, all catalogs)
All checks were successful
CI / check (push) Successful in 5m49s
All checks were successful
CI / check (push) Successful in 5m49s
This commit is contained in:
parent
e303350b2b
commit
44d45e9c68
10 changed files with 51 additions and 10 deletions
|
|
@ -2478,6 +2478,12 @@ pub trait NetView {
|
|||
fn oper_accounts(&self) -> Vec<String> {
|
||||
Vec::new()
|
||||
}
|
||||
// The services-operator privileges this account holds from the `[[oper]]` config
|
||||
// (NickServ INFO shows the tier). Empty when it isn't a config operator; runtime
|
||||
// OPER grants (Store::opers_list) are unioned in by the caller.
|
||||
fn config_oper_privs(&self, _account: &str) -> Privs {
|
||||
Privs::default()
|
||||
}
|
||||
fn uid_by_nick(&self, nick: &str) -> Option<&str>;
|
||||
fn nick_of(&self, uid: &str) -> Option<&str>;
|
||||
fn host_of(&self, uid: &str) -> Option<&str>;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
{
|
||||
" Services role : {role}": " Dienste-Rolle : {role}",
|
||||
" Groups : {list} (from GroupServ)": " Gruppen : {list} (von GroupServ)",
|
||||
"{count} year": "{count} Jahr",
|
||||
"{count} years": "{count} Jahre",
|
||||
"{count} month": "{count} Monat",
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
{
|
||||
" Services role : {role}": " Rol de servicios : {role}",
|
||||
" Groups : {list} (from GroupServ)": " Grupos : {list} (de GroupServ)",
|
||||
"{count} year": "{count} año",
|
||||
"{count} years": "{count} años",
|
||||
"{count} month": "{count} mes",
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
{
|
||||
" Services role : {role}": " Rol de servicios : {role}",
|
||||
" Groups : {list} (from GroupServ)": " Grupos : {list} (de GroupServ)",
|
||||
"{count} year": "{count} año",
|
||||
"{count} years": "{count} años",
|
||||
"{count} month": "{count} mes",
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
{
|
||||
" Services role : {role}": " Rôle services : {role}",
|
||||
" Groups : {list} (from GroupServ)": " Groupes : {list} (via GroupServ)",
|
||||
"{count} year": "{count} an",
|
||||
"{count} years": "{count} ans",
|
||||
"{count} month": "{count} mois",
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
{
|
||||
" Services role : {role}": " Cargo de serviços : {role}",
|
||||
" Groups : {list} (from GroupServ)": " Grupos : {list} (do GroupServ)",
|
||||
"{count} year": "{count} ano",
|
||||
"{count} years": "{count} anos",
|
||||
"{count} month": "{count} mês",
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
{
|
||||
" Services role : {role}": " Cargo de serviços : {role}",
|
||||
" Groups : {list} (from GroupServ)": " Grupos : {list} (do GroupServ)",
|
||||
"{count} year": "{count} ano",
|
||||
"{count} years": "{count} anos",
|
||||
"{count} month": "{count} mês",
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
use echo_api::{human_ago, human_time, NetView, Priv, ProfileField, Store};
|
||||
use echo_api::{human_ago, human_time, NetView, Priv, Privs, ProfileField, Store};
|
||||
use echo_api::{Sender, ServiceCtx};
|
||||
use echo_api::t;
|
||||
|
||||
|
|
@ -16,6 +16,21 @@ pub fn handle(me: &str, from: &Sender, args: &[&str], ctx: &mut ServiceCtx, net:
|
|||
ctx.notice(me, from.uid, t!(ctx, "Information for \x02{name}\x02:", name = acct.name));
|
||||
let age = human_ago(ctx.lang(), now_unix().saturating_sub(acct.ts));
|
||||
ctx.notice(me, from.uid, t!(ctx, " Registered : {when} ({age} ago)", when = human_time(acct.ts), age = age));
|
||||
// Services-operator role: the config-oper tier unioned with any live runtime OPER
|
||||
// grant. Public (WHOIS already reveals oper status); shown only when there is one.
|
||||
let now = now_unix();
|
||||
let mut privs = net.config_oper_privs(&acct.name);
|
||||
if let Some((_, names, _)) = db.opers_list().into_iter().find(|(a, _, exp)| a.eq_ignore_ascii_case(&acct.name) && exp.is_none_or(|e| e > now)) {
|
||||
privs = privs.union(Privs::from_names(&names));
|
||||
}
|
||||
if privs.any() {
|
||||
ctx.notice(me, from.uid, t!(ctx, " Services role : {role}", role = privs.names().join(", ")));
|
||||
}
|
||||
// GroupServ groups the account belongs to — public account identity.
|
||||
let groups = db.groups_of(&acct.name);
|
||||
if !groups.is_empty() {
|
||||
ctx.notice(me, from.uid, t!(ctx, " Groups : {list} (from GroupServ)", list = groups.join(" ")));
|
||||
}
|
||||
// Last-seen is public by default; SET HIDE STATUS keeps it to owner and opers.
|
||||
if privileged || !acct.hide_status {
|
||||
let last_seen = if net.uids_logged_into(&acct.name).is_empty() {
|
||||
|
|
|
|||
|
|
@ -350,7 +350,7 @@ impl Engine {
|
|||
|
||||
// Load the services-operator table (casefolded account -> privileges).
|
||||
pub fn set_opers(&mut self, opers: HashMap<String, Privs>) {
|
||||
self.network.set_config_opers(opers.keys().cloned().collect());
|
||||
self.network.set_config_opers(opers.clone());
|
||||
self.opers = opers;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ use std::time::{SystemTime, UNIX_EPOCH};
|
|||
|
||||
// The read-only network view a module sees; re-exported so the engine keeps
|
||||
// naming it locally.
|
||||
pub use echo_api::{ChanSeenView, HelpEntry, IncidentView, NetView, SeenView};
|
||||
pub use echo_api::{ChanSeenView, HelpEntry, IncidentView, NetView, Privs, SeenView};
|
||||
|
||||
// The most recent moderation/action incidents kept for LOGSEARCH.
|
||||
const INCIDENT_CAP: usize = 10_000;
|
||||
|
|
@ -52,10 +52,11 @@ pub struct Network {
|
|||
// Network-wide help index (service nick, blurb, per-command topics), built by
|
||||
// the engine at startup from every service so HelpServ can front it.
|
||||
pub help_catalog: Vec<(String, &'static str, &'static [HelpEntry])>,
|
||||
// Account names of the declarative config operators, mirrored from the engine
|
||||
// so services can enumerate staff (MemoServ STAFF). Runtime OPER grants live
|
||||
// in the store; a caller unions the two.
|
||||
config_opers: Vec<String>,
|
||||
// The declarative config operators and the privilege tier each holds, mirrored
|
||||
// from the engine so services can enumerate staff (MemoServ STAFF) and show a
|
||||
// role (NickServ INFO). Runtime OPER grants live in the store; a caller unions
|
||||
// the two.
|
||||
config_opers: HashMap<String, Privs>,
|
||||
}
|
||||
|
||||
// ChanFix scoring caps and rates.
|
||||
|
|
@ -296,8 +297,8 @@ impl Network {
|
|||
|
||||
// Mirror the engine's declarative config operators, so services can enumerate
|
||||
// staff. Called whenever the config oper set changes.
|
||||
pub fn set_config_opers(&mut self, accounts: Vec<String>) {
|
||||
self.config_opers = accounts;
|
||||
pub fn set_config_opers(&mut self, opers: HashMap<String, Privs>) {
|
||||
self.config_opers = opers;
|
||||
}
|
||||
|
||||
// Live sessions from `ip`.
|
||||
|
|
@ -849,7 +850,14 @@ impl Network {
|
|||
// with the seen record projected into a plain view.
|
||||
impl NetView for Network {
|
||||
fn oper_accounts(&self) -> Vec<String> {
|
||||
self.config_opers.clone()
|
||||
self.config_opers.keys().cloned().collect()
|
||||
}
|
||||
fn config_oper_privs(&self, account: &str) -> Privs {
|
||||
self.config_opers
|
||||
.iter()
|
||||
.find(|(a, _)| a.eq_ignore_ascii_case(account))
|
||||
.map(|(_, p)| *p)
|
||||
.unwrap_or_default()
|
||||
}
|
||||
fn uid_by_nick(&self, nick: &str) -> Option<&str> {
|
||||
Network::uid_by_nick(self, nick)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue