Apply the IRC namespace guards (forbid, confusable, frozen) to gRPC account writes

This commit is contained in:
Jean Chevronnet 2026-07-19 13:10:44 +00:00
parent 660362a34b
commit 18806c5fe7
No known key found for this signature in database
2 changed files with 31 additions and 0 deletions

View file

@ -343,6 +343,10 @@ impl Db {
self.confusable_check = on; self.confusable_check = on;
} }
pub fn confusable_check_enabled(&self) -> bool {
self.confusable_check
}
/// The network defence level (5 = normal, 1 = full lockdown). /// The network defence level (5 = normal, 1 = full lockdown).
pub fn defcon(&self) -> u8 { pub fn defcon(&self) -> u8 {
self.defcon self.defcon

View file

@ -13,15 +13,42 @@ impl Engine {
if self.db.exists(name) { if self.db.exists(name) {
return Err(AuthorityStatus::AlreadyExists); return Err(AuthorityStatus::AlreadyExists);
} }
// A website write must obey the same namespace guards as an IRC REGISTER, or
// it becomes a hole around them (a look-alike "аdmin" that impersonates, a
// FORBIDden/reserved nick, or a registration created while frozen).
if self.db.registrations_frozen() || self.db.readonly() {
return Err(AuthorityStatus::Invalid);
}
self.authority_name_ok(name)?;
if !self.reg_limiter.allow() { if !self.reg_limiter.allow() {
return Err(AuthorityStatus::RateLimited); return Err(AuthorityStatus::RateLimited);
} }
Ok(()) Ok(())
} }
// The namespace guards the IRC REGISTER path enforces (FORBID list + look-alike/
// confusable check), applied to every authority write so it can't create a name
// IRC would have rejected.
fn authority_name_ok(&self, name: &str) -> Result<(), AuthorityStatus> {
if self.db.is_forbidden(echo_api::ForbidKind::Nick, name).is_some() {
return Err(AuthorityStatus::Invalid);
}
if self.db.confusable_check_enabled() && echo_api::confusable_reason(name).is_some() {
return Err(AuthorityStatus::Invalid);
}
Ok(())
}
// Provision an account from pre-derived SCRAM verifiers (bulk backfill from // Provision an account from pre-derived SCRAM verifiers (bulk backfill from
// the authority). No email confirmation — the authority already vouches for it. // the authority). No email confirmation — the authority already vouches for it.
pub fn authority_provision(&mut self, name: &str, scram256: &str, scram512: &str, email: Option<String>) -> AuthorityStatus { pub fn authority_provision(&mut self, name: &str, scram256: &str, scram512: &str, email: Option<String>) -> AuthorityStatus {
// Backfill still can't mint a forbidden/look-alike name (skip only the
// frozen/rate-limit gates, which don't apply to an authority backfill).
if !self.db.exists(name) {
if let Err(status) = self.authority_name_ok(name) {
return status;
}
}
match self.db.provision_account(name, scram256, scram512, email) { match self.db.provision_account(name, scram256, scram512, email) {
Ok(()) => AuthorityStatus::Ok, Ok(()) => AuthorityStatus::Ok,
Err(RegError::Exists) => AuthorityStatus::AlreadyExists, Err(RegError::Exists) => AuthorityStatus::AlreadyExists,