From a3cdb35e0526edce7ffc1b1137544ee050da470b Mon Sep 17 00:00:00 2001 From: Mark Veidemanis Date: Sat, 30 May 2020 21:40:10 +0100 Subject: [PATCH] Implement registration and confirmation of nicks --- commands/confirm.py | 27 +++++++++++++++++++++ commands/reg.py | 27 +++++++++++++++++++++ conf/example/config.json | 1 + conf/example/irc.json | 9 +++++++ conf/help.json | 6 +++-- core/bot.py | 27 ++++++++++++++------- modules/alias.py | 4 +--- modules/network.py | 11 ++++++++- modules/regproc.py | 52 ++++++++++++++++++++++++++++++++++++++++ 9 files changed, 149 insertions(+), 15 deletions(-) create mode 100644 commands/confirm.py create mode 100644 commands/reg.py create mode 100644 conf/example/irc.json create mode 100644 modules/regproc.py diff --git a/commands/confirm.py b/commands/confirm.py new file mode 100644 index 0000000..93af4d0 --- /dev/null +++ b/commands/confirm.py @@ -0,0 +1,27 @@ +import main +from modules import regproc + +class ConfirmCommand: + def __init__(self, *args): + self.confirm(*args) + + def confirm(self, addr, authed, data, obj, spl, success, failure, info, incUsage, length): + if authed: + if length == 4: + if not spl[1] in main.network.keys(): + failure("No such network: %s" % spl[1]) + return + if not spl[2].isdigit(): + failure("Must be a number, not %s" % spl[2]) + return + if not int(spl[2]) in main.network[spl[1]].relays.keys(): + failure("No such relay on %s: %s" % (spl[2], spl[1])) + return + regproc.confirmAccount(spl[1], int(spl[2]), spl[3]) + success("Requested confirmation on %s - %s with token %s" % (spl[1], spl[2], spl[3])) + return + else: + incUsage("confirm") + return + else: + incUsage(None) diff --git a/commands/reg.py b/commands/reg.py new file mode 100644 index 0000000..406dda4 --- /dev/null +++ b/commands/reg.py @@ -0,0 +1,27 @@ +import main +from modules import regproc + +class RegCommand: + def __init__(self, *args): + self.reg(*args) + + def reg(self, addr, authed, data, obj, spl, success, failure, info, incUsage, length): + if authed: + if length == 3: + if not spl[1] in main.network.keys(): + failure("No such network: %s" % spl[1]) + return + if not spl[2].isdigit(): + failure("Must be a number, not %s" % spl[2]) + return + if not int(spl[2]) in main.network[spl[1]].relays.keys(): + failure("No such relay on %s: %s" % (spl[2], spl[1])) + return + regproc.registerAccount(spl[1], int(spl[2])) + success("Requested registration on %s - %s" % (spl[1], spl[2])) + return + else: + incUsage("reg") + return + else: + incUsage(None) diff --git a/conf/example/config.json b/conf/example/config.json index 7026833..a3dfecc 100644 --- a/conf/example/config.json +++ b/conf/example/config.json @@ -15,6 +15,7 @@ "RedisSocket": "/tmp/redis.sock", "UsePassword": true, "ConnectOnCreate": false, + "AutoReg": false, "Debug": false, "Relay": { "Host": "127.0.0.1", diff --git a/conf/example/irc.json b/conf/example/irc.json new file mode 100644 index 0000000..e624aa8 --- /dev/null +++ b/conf/example/irc.json @@ -0,0 +1,9 @@ +{ + "_": { + "register": true, + "entity": "NickServ", + "email": "{nickname}@example.com", + "register": "REGISTER {password} {email}", + "confirm": "CONFIRM {token}" + } +} diff --git a/conf/help.json b/conf/help.json index f97448f..1b44f76 100644 --- a/conf/help.json +++ b/conf/help.json @@ -13,7 +13,7 @@ "load": "load <(file)|list|all>", "dist": "dist", "loadmod": "loadmod ", - "msg": "msg ", + "msg": "msg ", "mon": "mon -h", "chans": "chans [ ...]", "users": "users [ ...]", @@ -28,5 +28,7 @@ "admall": "admall ", "swho": "swho []", "list": "list ", - "exec": "exec " + "exec": "exec ", + "reg": "reg ", + "confirm": "confirm " } diff --git a/core/bot.py b/core/bot.py index 2ba40ff..90b5baf 100644 --- a/core/bot.py +++ b/core/bot.py @@ -14,6 +14,7 @@ from modules import userinfo from modules import counters from modules import monitor from modules import chankeep +from modules import regproc from core.relay import sendRelayNotification from utils.dedup import dedup @@ -97,12 +98,12 @@ class IRCRelay(IRCClient): sendAll("%s: relay password mismatch" % self.num) def sendStage2(self): - if not self.stage2 == None: # [["user", {"sasl": ["message1", "message2"]}], []] - if not len(self.stage2) == 0: - user = self.stage2[0].pop(0) - commands = self.stage2[0].pop(0) - del self.stage2[0] - deliverRelayCommands(self.num, commands, user, self.stage2) + # [["user", {"sasl": ["message1", "message2"]}], []] + if not len(self.stage2) == 0: + user = self.stage2[0].pop(0) + commands = self.stage2[0].pop(0) + del self.stage2[0] + deliverRelayCommands(self.num, commands, user, self.stage2) def signedOn(self): if not self.isconnected: @@ -115,7 +116,8 @@ class IRCRelay(IRCClient): reactor.callLater(sleeptime, self.msg, main.config["Tweaks"]["ZNC"]["Prefix"]+i, x) sleeptime += increment increment += 0.8 - reactor.callLater(sleeptime, self.sendStage2) + if not self.stage2 == None: + reactor.callLater(sleeptime, self.sendStage2) reactor.callLater(sleeptime+5, self.transport.loseConnection) return @@ -392,6 +394,9 @@ class IRCBot(IRCClient): return d def list(self, noargs=False, nocheck=False): + if not main.network[self.net].relays[self.num]["registered"]: + debug("Will not send LIST, unregistered: %s - %i" % (self.net, self.num)) + return if self.listAttempted: debug("List request dropped, already asked for LIST - %s - %i" % (self.net, self.num)) return @@ -448,7 +453,7 @@ class IRCBot(IRCClient): else: if self.listRetried: self.listRetried = False - debug("List received after retry - defaulting to simple list syntax") + debug("List received after retry - defaulting to simple list syntax: %s - %i" % (self.net, self.num)) self.listSimple = True def got_list(self, listinfo): @@ -464,6 +469,10 @@ class IRCBot(IRCClient): if not self.isconnected: # we don't care about log("endpoint connected: %s - %i" % (self.net, self.num)) self.isconnected = True + if not main.network[self.net].relays[self.num]["registered"]: + if main.config["AutoReg"]: + regproc.registerAccount(self.net, self.num) + debug("Attempting to register: %s - %i" % (self.net, self.num)) for i in options: if i.startswith("CHANLIMIT"): if ":" in i: @@ -673,7 +682,7 @@ class IRCBotFactory(ReconnectingClientFactory): error = reason.getErrorMessage() log("%s - %i: connection failed: %s" % (self.net, self.num, error)) if not self.relay: - sendAll("%s -%s: connection failed: %s" % (self.net, self.num, error)) + sendAll("%s - %s: connection failed: %s" % (self.net, self.num, error)) sendRelayNotification({"type": "conn", "net": self.net, "num": self.num, "status": "failed", "message": error}) self.retry(connector) #ReconnectingClientFactory.clientConnectionFailed(self, connector, reason) diff --git a/modules/alias.py b/modules/alias.py index e05950d..4a3949f 100644 --- a/modules/alias.py +++ b/modules/alias.py @@ -63,6 +63,4 @@ def generate_alias(): if rand == 3 or rand == 4: realname = realname.capitalize() - password = generate_password() - - return {"nick": nick, "altnick": altnick, "ident": ident, "realname": realname, "password": password} + return {"nick": nick, "altnick": altnick, "ident": ident, "realname": realname} diff --git a/modules/network.py b/modules/network.py index 9e235a1..5df2577 100644 --- a/modules/network.py +++ b/modules/network.py @@ -27,10 +27,19 @@ class Network: self.last += 1 elif num == self.last: self.last += 1 + registered = False + if self.net in main.irc.keys(): + if "register" in main.irc[self.net].keys(): + if not main.irc[self.net]["register"]: + registered = True + # Don't need to register if it's been disabled in definitions, + # so we'll pretend we already did + self.relays[num] = { "enabled": main.config["ConnectOnCreate"], "net": self.net, - "id": num + "id": num, + "registered": registered } password = alias.generate_password() if not num in main.alias.keys(): diff --git a/modules/regproc.py b/modules/regproc.py new file mode 100644 index 0000000..f1296bf --- /dev/null +++ b/modules/regproc.py @@ -0,0 +1,52 @@ +import main +from modules import provision +from utils.logging.log import * + +def registerAccount(net, num): + alias = main.alias[num] + nickname = alias["nick"] + username = nickname+"/"+net + password = main.network[net].aliases[num]["password"] + if net in main.irc.keys(): + inst = main.irc[net] + else: + inst = main.irc["_"] + + if not inst["register"]: + error("Cannot register for %s: function disabled" % (net)) + return False + entity = inst["entity"] + email = inst["email"] + cmd = inst["register"] + email = email.replace("{nickname}", nickname) + cmd = cmd.replace("{password}", password) + cmd = cmd.replace("{email}", email) + name = net+str(num) + main.IRCPool[name].msg(entity, cmd) + +def confirmAccount(net, num, token): + if net in main.irc.keys(): + inst = main.irc[net] + else: + inst = main.irc["_"] + entity = inst["entity"] + cmd = inst["confirm"] + cmd = cmd.replace("{token}", token) + name = net+str(num) + main.IRCPool[name].msg(entity, cmd) + enableAuthentication(net, num) + +def enableAuthentication(net, num): + obj = main.network[net] + nick = main.alias[num]["nick"] + security = obj.security + auth = obj.auth + password = obj.aliases[num]["password"] + + uname = main.alias[num]["nick"]+"/"+net + provision.provisionAuthenticationData(num, nick, net, security, auth, password) # Set up for auth + if obj.relays[num]["registered"]: + warn("Authentication already enabled: %s - %i" % (net, num)) + obj.relays[num]["registered"] = True + main.saveConf("network") + main.IRCPool[net+str(num)].msg(main.config["Tweaks"]["ZNC"]["Prefix"]+"status", "Jump")