From fc304d4b25b93d6ecc1a5b9bc51a7554f29ff4d3 Mon Sep 17 00:00:00 2001 From: Mark Veidemanis Date: Mon, 18 Mar 2019 21:01:28 +0000 Subject: [PATCH] Implement the relay channel and command for generating tokens --- commands/token.py | 56 ++++++++++++++++++ conf/example/config.json | 7 ++- conf/example/tokens.json | 1 + conf/help.json | 3 +- conf/tokens.json | 7 +++ core/relay.py | 120 +++++++++++++++++++++++++++++++++++++++ main.py | 2 + threshold | 9 +++ 8 files changed, 203 insertions(+), 2 deletions(-) create mode 100644 commands/token.py create mode 100644 conf/example/tokens.json create mode 100644 conf/tokens.json create mode 100644 core/relay.py diff --git a/commands/token.py b/commands/token.py new file mode 100644 index 0000000..cd00e38 --- /dev/null +++ b/commands/token.py @@ -0,0 +1,56 @@ +import main +from yaml import dump +from uuid import uuid4 + +class Token: + def __init__(self, *args): + self.token(*args) + + def token(self, addr, authed, data, obj, spl, success, failure, info, incUsage, length): + if authed: + if length == 2: + if spl[1] == "list": + info(dump(main.tokens)) + return + else: + incUsage("token") + return + elif length == 3: + if spl[1] == "del": + if spl[2] in main.tokens.keys(): + del main.tokens[spl[2]] + main.saveConf("tokens") + success("Successfully removed token: %s" % spl[2]) + return + else: + failure("No such token") + return + else: + incUsage("token") + return + elif length == 4: + if spl[1] == "add": + if not spl[2] in main.tokens.keys(): + if spl[3] in ["relay"]: # more to come! + main.tokens[spl[2]] = {"hello": str(uuid4()), + "usage": spl[3], + "counter": str(uuid4()), + } + main.saveConf("tokens") + success("Successfully created token %s:" % spl[2]) + info(dump(main.tokens[spl[2]])) + return + else: + incUsage("token") + return + else: + failure("Token already exists: %s" % spl[2]) + return + else: + incUsage("token") + return + else: + incUsage("token") + return + else: + incUsage(None) diff --git a/conf/example/config.json b/conf/example/config.json index 2b2959d..27e79fb 100644 --- a/conf/example/config.json +++ b/conf/example/config.json @@ -4,12 +4,17 @@ "Address": "127.0.0.1", "UseSSL": true }, + "Relay": { + "Enabled": true, + "Port": 13868, + "Address": "127.0.0.1", + "UseSSL": true + }, "Key": "key.pem", "Certificate": "cert.pem", "RedisSocket": "/tmp/redis.sock", "UsePassword": true, "ConnectOnCreate": false, - "RelayPassword": "s", "Notifications": { "Highlight": true, "Connection": true, diff --git a/conf/example/tokens.json b/conf/example/tokens.json new file mode 100644 index 0000000..0967ef4 --- /dev/null +++ b/conf/example/tokens.json @@ -0,0 +1 @@ +{} diff --git a/conf/help.json b/conf/help.json index 2ecefc7..13ee310 100644 --- a/conf/help.json +++ b/conf/help.json @@ -24,5 +24,6 @@ "relay": "relay [ ]", "network": "network [
]", "provision": "provision []", - "cmd": "cmd " + "cmd": "cmd ", + "token": "token [] []" } diff --git a/conf/tokens.json b/conf/tokens.json new file mode 100644 index 0000000..d93cbde --- /dev/null +++ b/conf/tokens.json @@ -0,0 +1,7 @@ +{ + "a": { + "hello": "9814a659-9241-4389-8569-8bb23c69aea5", + "usage": "relay", + "counter": "98375c39-c421-42a1-ab48-b566587513b9" + } +} \ No newline at end of file diff --git a/core/relay.py b/core/relay.py new file mode 100644 index 0000000..a0d2881 --- /dev/null +++ b/core/relay.py @@ -0,0 +1,120 @@ +from twisted.internet.protocol import Protocol, Factory, ClientFactory +from json import dumps, loads +import main +from utils.logging.log import * + +class Relay(Protocol): + def __init__(self, addr): + self.addr = addr + self.authed = False + self.subscriptions = [] + + def send(self, data): + data += "\r\n" + data = data.encode("utf-8", "replace") + self.transport.write(data) + + def sendErr(self, data): + self.send(dumps({"type": "error", "reason": data})) + return + + def sendMsg(self, data): + self.send(dumps(data)) + + def dataReceived(self, data): + data = data.decode("utf-8", "replace") + try: + parsed = loads(data) + except: + self.sendErr("MALFORMED") + return + if not "type" in parsed.keys(): + self.sendErr("NOTYPE") + return + if parsed["type"] == "hello": + if set(["key", "hello"]).issubset(set(parsed)): + self.handleHello(parsed) + else: + self.sendErr("WRONGFIELDS") + return + return + elif parsed["type"] == "control": + if "subscribe" in parsed.keys(): + self.handleSubscribe(parsed["subscribe"]) + return + elif "unsubscribe" in parsed.keys(): + self.handleUnsubscribe(parsed["unsubscribe"]) + return + else: + self.sendErr("UNCLEAR") + return + else: + self.sendErr("UNCLEAR") + return + + def handleSubscribe(self, lst): + if not isinstance(lst, list): + self.sendErr("NOTLIST") + return + for i in lst: + if not i in ["msg", "notice", "action", "who", "part", "join", "kick", "quit", "nick", "topic", "mode"]: + self.sendErr("NONEXISTANT") + return + if i in self.subscriptions: + self.sendErr("SUBSCRIBED") + return + self.subscriptions.append(i) + self.sendMsg({"type": "success"}) + return + + def handleUnubscribe(self, lst): + if not isinstance(lst, list): + self.sendErr("NOTLIST") + return + for i in lst: + if not i in ["msg", "notice", "action", "who", "part", "join", "kick", "quit", "nick", "topic", "mode"]: + self.sendErr("NONEXISTANT") + return + if not i in self.subscriptions: + self.sendErr("NOTSUBSCRIBED") + return + del self.subscriptions[i] + self.sendMsg({"type": "success"}) + return + + def handleHello(self, parsed): + if parsed["key"] in main.tokens.keys(): + if parsed["hello"] == main.tokens[parsed["key"]]["hello"] and main.tokens[parsed["key"]]["usage"] == "relay": + self.sendMsg({"type": "hello", "hello": main.tokens[parsed["key"]]["counter"]}) + self.authed = True + else: + self.transport.loseConnection() + return + else: + self.sendErr("NOKEY") + return + + def connectionMade(self): + log("Connection from %s:%s" % (self.addr.host, self.addr.port)) + #self.send("Greetings.") + + def connectionLost(self, reason): + self.authed = False + log("Relay connection lost from %s:%s -- %s" % (self.addr.host, self.addr.port, reason.getErrorMessage())) + if self.addr in main.relayConnections.keys(): + del main.relayConnections[self.addr] + else: + warn("Tried to remove a non-existant relay connection.") + +class RelayFactory(Factory): + def buildProtocol(self, addr): + entry = Relay(addr) + main.relayConnections[addr] = entry + return entry + + def send(self, addr, data): + if addr in main.relayConnections.keys(): + connection = main.relayConnections[addr] + connection.send(data) + else: + return diff --git a/main.py b/main.py index 05c8deb..8797afa 100644 --- a/main.py +++ b/main.py @@ -17,9 +17,11 @@ filemap = { "alias": ["alias.json", "alias details"], "relay": ["relay.json", "relay list"], "network": ["network.json", "network list"], + "tokens": ["tokens.json", "authentication tokens"], } connections = {} +relayConnections = {} IRCPool = {} ReactorPool = {} FactoryPool = {} diff --git a/threshold b/threshold index 5ca234a..ddba8d5 100755 --- a/threshold +++ b/threshold @@ -13,6 +13,7 @@ from utils.logging.log import * from utils.loaders.command_loader import loadCommands from core.helper import startBot from core.server import Server, ServerFactory +from core.relay import Relay, RelayFactory loadCommands() @@ -24,6 +25,14 @@ if __name__ == "__main__": else: reactor.listenTCP(main.config["Listener"]["Port"], listener, interface=main.config["Listener"]["Address"]) log("Threshold running on %s:%s" % (main.config["Listener"]["Address"], main.config["Listener"]["Port"])) + if main.config["Relay"]["Enabled"]: + relay = RelayFactory() + if main.config["Relay"]["UseSSL"] == True: + reactor.listenSSL(main.config["Relay"]["Port"], relay, DefaultOpenSSLContextFactory(main.certPath+main.config["Key"], main.certPath+main.config["Certificate"]), interface=main.config["Relay"]["Address"]) + log("Threshold relay running with SSL on %s:%s" % (main.config["Relay"]["Address"], main.config["Relay"]["Port"])) + else: + reactor.listenTCP(main.config["Relay"]["Port"], relay, interface=main.config["Relay"]["Address"]) + log("Threshold relay running on %s:%s" % (main.config["Relay"]["Address"], main.config["Relay"]["Port"])) for i in main.pool.keys(): if not "enabled" in main.pool[i]: continue