Add persisted account store with NickServ REGISTER/IDENTIFY
Accounts are an append-only JSONL event log (state is the fold of the log) with argon2id-hashed passwords. NickServ gains REGISTER <password> [email] and IDENTIFY <password>; the engine tracks UID->nick from the burst so a command resolves to the sender's current nick. Classic and ircd-agnostic — the cap-based account-registration forward is the next step.
This commit is contained in:
parent
7929f5f9f4
commit
9285105afd
11 changed files with 348 additions and 41 deletions
|
|
@ -1,4 +1,5 @@
|
|||
use crate::engine::service::{Service, ServiceCtx};
|
||||
use crate::engine::db::{Db, RegError};
|
||||
use crate::engine::service::{Sender, Service, ServiceCtx};
|
||||
|
||||
pub struct NickServ {
|
||||
pub uid: String,
|
||||
|
|
@ -15,10 +16,37 @@ impl Service for NickServ {
|
|||
"Nickname Services"
|
||||
}
|
||||
|
||||
fn on_command(&mut self, from: &str, args: &[&str], ctx: &mut ServiceCtx) {
|
||||
fn on_command(&mut self, from: &Sender, args: &[&str], ctx: &mut ServiceCtx, db: &mut Db) {
|
||||
let me = self.uid.as_str();
|
||||
match args.first().map(|s| s.to_ascii_uppercase()).as_deref() {
|
||||
Some("HELP") => ctx.notice(self.uid(), from, "Commands: REGISTER, IDENTIFY (coming soon)."),
|
||||
Some(other) => ctx.notice(self.uid(), from, format!("Unknown command: {}. Try HELP.", other)),
|
||||
Some("REGISTER") => {
|
||||
// REGISTER <password> [email] — registers the sender's current nick.
|
||||
let Some(password) = args.get(1) else {
|
||||
ctx.notice(me, from.uid, "Syntax: REGISTER <password> [email]");
|
||||
return;
|
||||
};
|
||||
let email = args.get(2).map(|s| s.to_string());
|
||||
match db.register(from.nick, password, email) {
|
||||
Ok(()) => ctx.notice(me, from.uid, format!("Nickname \x02{}\x02 is now registered.", from.nick)),
|
||||
Err(RegError::Exists) => ctx.notice(me, from.uid, format!("Nickname \x02{}\x02 is already registered.", from.nick)),
|
||||
Err(RegError::Internal) => ctx.notice(me, from.uid, "Registration failed, please try again later."),
|
||||
}
|
||||
}
|
||||
Some("IDENTIFY") => {
|
||||
let Some(password) = args.get(1) else {
|
||||
ctx.notice(me, from.uid, "Syntax: IDENTIFY <password>");
|
||||
return;
|
||||
};
|
||||
if db.verify(from.nick, password) {
|
||||
ctx.notice(me, from.uid, format!("You are now identified for \x02{}\x02.", from.nick));
|
||||
} else {
|
||||
ctx.notice(me, from.uid, "Invalid password.");
|
||||
}
|
||||
}
|
||||
Some("HELP") => {
|
||||
ctx.notice(me, from.uid, "Commands: REGISTER <password> [email], IDENTIFY <password>.")
|
||||
}
|
||||
Some(other) => ctx.notice(me, from.uid, format!("Unknown command: {}. Try HELP.", other)),
|
||||
None => {}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue