From 9becfa9897cebf1bf1256b7be78704eaf63ddecc Mon Sep 17 00:00:00 2001 From: Luke Rogers Date: Thu, 1 Aug 2013 19:03:40 +1200 Subject: [PATCH] New permissions system :D --- core/config.py | 13 +++++-- plugins/admin.py | 66 +++++++----------------------------- plugins/factoids.py | 8 ++--- plugins/sieve.py | 75 +++++++++++++++++++++++++++++++++++++++++ plugins/yahooanswers.py | 4 +-- 5 files changed, 103 insertions(+), 63 deletions(-) create mode 100644 plugins/sieve.py diff --git a/core/config.py b/core/config.py index 76884a4..c3e7e34 100755 --- a/core/config.py +++ b/core/config.py @@ -41,7 +41,15 @@ if not os.path.exists('config'): "twitter_access_secret": "", "wunderground": "" }, - "plugins": + "permission_groups": { + "admins": ["addfactoid", "delfactoid", "ignore", "botcontrol"], + "moderators": ["addfactoid", "delfactoid", "ignore"] + }, + "permission_users": { + "admins": ["example!user@example.com"], + "moderators": ["exampleb!user@example.com"] + }, + plugins": { "factoids": { @@ -56,8 +64,7 @@ if not os.path.exists('config'): [ "mypass", "mysecret" - ], - "admins": ["myname@myhost"] + ] }''') + '\n') print "Config generated!" print "Please edit the config now!" diff --git a/plugins/admin.py b/plugins/admin.py index 1e23e6d..fccbce2 100755 --- a/plugins/admin.py +++ b/plugins/admin.py @@ -6,50 +6,8 @@ import time import subprocess -@hook.command(adminonly=True) -def addadmin(inp, notice=None, bot=None, config=None): - "addadmin -- Make an admin. " \ - "(you can add multiple admins at once)" - targets = inp.split() - for target in targets: - if target in bot.config["admins"]: - notice("%s is already an admin." % target) - else: - notice("%s is now an admin." % target) - bot.config["admins"].append(target) - bot.config["admins"].sort() - json.dump(bot.config, open('config', 'w'), sort_keys=True, indent=2) - return - - -@hook.command(adminonly=True) -def deladmin(inp, notice=None, bot=None, config=None): - "deladmin -- Make a non-admin." \ - "(you can delete multiple admins at once)" - targets = inp.split() - for target in targets: - if target in bot.config["admins"]: - notice("%s is no longer an admin." % target) - bot.config["admins"].remove(target) - bot.config["admins"].sort() - json.dump(bot.config, open('config', 'w'), sort_keys=True, indent=2) - else: - notice("%s is not an admin." % target) - return - - -@hook.command(autohelp=False) -def admins(inp, notice=None, bot=None): - "admins -- Lists bot's admins." - if bot.config["admins"]: - notice("Admins are: %s." % ", ".join(bot.config["admins"])) - else: - notice("There are no users with admin powers.") - return - - -@hook.command("quit", autohelp=False, adminonly=True) -@hook.command(autohelp=False, adminonly=True) +@hook.command("quit", autohelp=False, permissions=["botcontrol"]) +@hook.command(autohelp=False, permissions=["botcontrol"]) def stop(inp, nick=None, conn=None): "stop [reason] -- Kills the bot with [reason] as its quit message." if inp: @@ -60,7 +18,7 @@ def stop(inp, nick=None, conn=None): os.execl("./cloudbot", "cloudbot", "stop") -@hook.command(autohelp=False, adminonly=True) +@hook.command(autohelp=False, permissions=["botcontrol"]) def restart(inp, nick=None, conn=None): "restart [reason] -- Restarts the bot with [reason] as its quit message." if inp: @@ -71,20 +29,20 @@ def restart(inp, nick=None, conn=None): os.execl("./cloudbot", "cloudbot", "restart") -@hook.command(autohelp=False, adminonly=True) +@hook.command(autohelp=False, permissions=["botcontrol"]) def clearlogs(inp, input=None): "clearlogs -- Clears the bots log(s)." subprocess.call(["./cloudbot", "clear"]) -@hook.command(adminonly=True) +@hook.command(permissions=["botcontrol"]) def join(inp, conn=None, notice=None): "join -- Joins ." notice("Attempting to join %s..." % inp) conn.join(inp) -@hook.command(autohelp=False, adminonly=True) +@hook.command(autohelp=False, permissions=["botcontrol"]) def part(inp, conn=None, chan=None, notice=None): "part -- Leaves ." \ "If [channel] is blank the bot will leave the " \ @@ -97,7 +55,7 @@ def part(inp, conn=None, chan=None, notice=None): conn.part(target) -@hook.command(autohelp=False, adminonly=True) +@hook.command(autohelp=False, permissions=["botcontrol"]) def cycle(inp, conn=None, chan=None, notice=None): "cycle -- Cycles ." \ "If [channel] is blank the bot will cycle the " \ @@ -111,7 +69,7 @@ def cycle(inp, conn=None, chan=None, notice=None): conn.join(target) -@hook.command(adminonly=True) +@hook.command(permissions=["botcontrol"]) def nick(inp, input=None, notice=None, conn=None): "nick -- Changes the bots nickname to ." if not re.match("^[A-Za-z0-9_|.-\]\[]*$", inp.lower()): @@ -121,14 +79,14 @@ def nick(inp, input=None, notice=None, conn=None): conn.set_nick(inp) -@hook.command(adminonly=True) +@hook.command(permissions=["botcontrol"]) def raw(inp, conn=None, notice=None): "raw -- Sends a RAW IRC command." notice("Raw command sent.") conn.send(inp) -@hook.command(adminonly=True) +@hook.command(permissions=["botcontrol"]) def say(inp, conn=None, chan=None, notice=None): "say [channel] -- Makes the bot say in [channel]. " \ "If [channel] is blank the bot will say the in the channel " \ @@ -149,8 +107,8 @@ def say(inp, conn=None, chan=None, notice=None): conn.send(out) -@hook.command("act", adminonly=True) -@hook.command(adminonly=True) +@hook.command("act", permissions=["botcontrol"]) +@hook.command(permissions=["botcontrol"]) def me(inp, conn=None, chan=None, notice=None): "me [channel] -- Makes the bot act out in [channel]. " \ "If [channel] is blank the bot will act the in the channel the " \ diff --git a/plugins/factoids.py b/plugins/factoids.py index e3d2fbc..0e6077b 100755 --- a/plugins/factoids.py +++ b/plugins/factoids.py @@ -32,8 +32,8 @@ def get_memory(db, word): return None -@hook.command("r", adminonly=True) -@hook.command(adminonly=True) +@hook.command("r", permissions=["addfactoid"]) +@hook.command(permissions=["addfactoid"]) def remember(inp, nick='', db=None, say=None, input=None, notice=None): "remember [+] -- Remembers with . Add +" " to to append." @@ -74,8 +74,8 @@ def remember(inp, nick='', db=None, say=None, input=None, notice=None): % (data, word, word)) -@hook.command("f", adminonly=True) -@hook.command(adminonly=True) +@hook.command("f", permissions=["delfactoid"]) +@hook.command(permissions=["delfactoid"]) def forget(inp, db=None, input=None, notice=None): "forget -- Forgets a remembered ." diff --git a/plugins/sieve.py b/plugins/sieve.py new file mode 100644 index 0000000..5bb5ffe --- /dev/null +++ b/plugins/sieve.py @@ -0,0 +1,75 @@ +import re + +from util import hook, text +from fnmatch import fnmatch + + +@hook.sieve +def sieve_suite(bot, input, func, kind, args): + if input.command == 'PRIVMSG' and\ + input.nick.endswith('bot') and args.get('ignorebots', True): + return None + + if kind == "command": + if input.trigger in bot.config.get('disabled_commands', []): + return None + + fn = re.match(r'^plugins.(.+).py$', func._filename) + disabled = bot.config.get('disabled_plugins', []) + if fn and fn.group(1).lower() in disabled: + return None + + acl = bot.config.get('acls', {}).get(func.__name__) + if acl: + if 'deny-except' in acl: + allowed_channels = map(unicode.lower, acl['deny-except']) + if input.chan.lower() not in allowed_channels: + return None + if 'allow-except' in acl: + denied_channels = map(unicode.lower, acl['allow-except']) + if input.chan.lower() in denied_channels: + return None + + # shim so plugins using the old "adminonly" permissions format still work + if args.get('adminonly', False): + args['perms'] = ["adminonly"] + + + if args.get('permissions', False): + groups = bot.config.get("permission_groups", []) + group_users = bot.config.get("permission_users", []) + + allowed_permissions = args.get('permissions', []) + + + allowed_groups = [] + + # loop over every group + for name, permissions in groups.iteritems(): + # loop over every permission the command allows + for permission in allowed_permissions: + # see if the group has that permission + if permission in permissions: + # if so, add the group name to the allowed_groups list + allowed_groups.append(name) + + + if not allowed_groups: + print "Something is wrong. A hook requires {} but" \ + " there are no groups with that permission!".format(str(allowed_permissions)) + + mask = input.mask.lower() + + # make all masks lowercase + for group, masks in group_users.iteritems(): + group_users[group] = [_mask.lower() for _mask in masks] + + for group in allowed_groups: + for pattern in group_users[group]: + if fnmatch(mask, pattern): + return input + + input.notice("Sorry, you are not allowed to use this command.") + return None + + return input diff --git a/plugins/yahooanswers.py b/plugins/yahooanswers.py index cc72c97..9e07ac8 100644 --- a/plugins/yahooanswers.py +++ b/plugins/yahooanswers.py @@ -3,7 +3,7 @@ from util import hook, web, text @hook.command def answer(inp): - ".answer -- find the answer to a question on Yahoo! Answers" + "answer -- find the answer to a question on Yahoo! Answers" query = "SELECT Subject, ChosenAnswer, Link FROM answers.search WHERE query=@query LIMIT 1" result = web.query(query, {"query": inp.strip()}).one() @@ -13,4 +13,4 @@ def answer(inp): # we split the answer and .join() it to remove newlines/extra spaces answer = text.truncate_str(' '.join(result["ChosenAnswer"].split()), 80) - return u'\x02{}\x02 "{}" - {}'.format(result["Subject"], answer, short_url) \ No newline at end of file + return u'\x02{}\x02 "{}" - {}'.format(result["Subject"], answer, short_url)