From a89bd0b334ea5741f3cb4db7bb09f17ad4f3bc19 Mon Sep 17 00:00:00 2001 From: Zodiac Date: Fri, 14 Feb 2025 11:02:13 -0800 Subject: [PATCH] added permission plugin --- plugins/services/permissions.py | 199 ++++++++++++++++++++++++++++++++ 1 file changed, 199 insertions(+) create mode 100644 plugins/services/permissions.py diff --git a/plugins/services/permissions.py b/plugins/services/permissions.py new file mode 100644 index 0000000..346c062 --- /dev/null +++ b/plugins/services/permissions.py @@ -0,0 +1,199 @@ +# permissions.py +# -*- coding: utf-8 -*- +""" +A plugin for irc3 that provides a permission system using TinyDB. + +This plugin allows bot administrators to manage command permissions +using a TinyDB database. It includes commands for adding, removing, +and listing permissions, as well as a policy system to enforce them. + +Author: Your Name +License: MIT +""" + +from irc3 import plugin +from irc3.plugins.command import command +from tinydb import TinyDB, Query +import fnmatch +from ircstyle import style # Import only the style function + + +@plugin +class TinyDBPermissions: + """A plugin for managing command permissions using TinyDB.""" + + def __init__(self, bot): + """ + Initialize the TinyDBPermissions plugin. + + Args: + bot: The irc3 bot instance. + """ + self.bot = bot + # Initialize TinyDB with a JSON file for storing permissions + self.permission_db = TinyDB('permissions.json') + # Expose the database to the bot context for other components to use + self.bot.permission_db = self.permission_db + self.bot.log.info("TinyDB permissions plugin initialized") + + +class TinyDBPolicy: + """Permission policy using TinyDB for storage.""" + + def __init__(self, bot): + """ + Initialize the TinyDBPolicy. + + Args: + bot: The irc3 bot instance. + """ + self.bot = bot + self.bot.log.debug("Initializing TinyDB policy") + + def has_permission(self, client_mask, permission): + """ + Check if a client has the required permission. + + Args: + client_mask: The client's hostmask (e.g., "user!ident@host"). + permission: The permission to check (e.g., "admin"). + + Returns: + bool: True if the client has the permission, False otherwise. + """ + # If no permission is required, allow access + if permission is None: + return True + + # Query the TinyDB database for permissions + User = Query() + entries = self.bot.permission_db.search(User.permission.test( + lambda p: p == permission or p == 'all_permissions' + )) + + # Check if the client's mask matches any entry in the database + for entry in entries: + if fnmatch.fnmatch(client_mask, entry['mask']): + return True + return False + + def __call__(self, predicates, meth, client, target, args, **kwargs): + """ + Enforce permissions for a command. + + Args: + predicates: Command metadata (e.g., name, permission). + meth: The command method to execute. + client: The client issuing the command. + target: The target of the command (channel or user). + args: Arguments passed to the command. + kwargs: Additional keyword arguments. + """ + # Get the command name from predicates or method name + cmd_name = predicates.get('name', meth.__name__) + + # Check if the client has the required permission + if self.has_permission(str(client), predicates.get('permission')): + # Execute the command if permission is granted + return meth(client, target, args) + + # Notify the client if they don't have permission + error_message = style( + f"You are not allowed to use the '{cmd_name}' command", + fg='red', bold=True + ) + self.bot.privmsg(client.nick, error_message) + + +@command(permission='admin') +def addperm(bot, mask, target, args): + """ + Add a permission to a user. + + Usage: + %%addperm + """ + # Access the TinyDB database + db = bot.permission_db + user_mask = args[''] + perm = args[''] + + # Insert the new permission into the database + db.insert({'mask': user_mask, 'permission': perm}) + + # Notify the user that the permission was added + success_message = style( + f"Added permission '{perm}' for mask '{user_mask}'", + fg='green', bold=True + ) + bot.privmsg(target, success_message) + bot.log.info(f"Added permission '{perm}' for mask '{user_mask}'") + + +@command(permission='admin') +def delperm(bot, mask, target, args): + """ + Remove a permission from a user. + + Usage: + %%delperm + """ + # Access the TinyDB database + db = bot.permission_db + User = Query() + user_mask = args[''] + perm = args[''] + + # Remove the permission from the database + removed = db.remove((User.mask == user_mask) & (User.permission == perm)) + + # Notify the user how many permissions were removed + success_message = style( + f"Removed {len(removed)} permission(s)", + fg='green', bold=True + ) + bot.privmsg(target, success_message) + bot.log.info(f"Removed {len(removed)} permission(s) for mask '{user_mask}'") + +@command(permission='admin') +def listperms(bot, mask, target, args): + """ + List all permissions or for a specific mask. + + Usage: + %%listperms [] + """ + # Access the TinyDB database + db = bot.permission_db + User = Query() + + # Use the provided mask or default to '*' (all masks) + mask_filter = args[''] or '*' + + # Convert shell-style mask to a valid regex pattern + regex_pattern = fnmatch.translate(mask_filter) + # Remove the flags added by fnmatch.translate for compatibility + regex_pattern = regex_pattern.rsplit('(?ms)')[0].rstrip('\\Z') + + # Search for permissions matching the converted regex pattern + entries = db.search(User.mask.matches(regex_pattern)) + + # Notify the user if no permissions are found + if not entries: + error_message = style("No permissions found", fg='red', bold=True) + bot.privmsg(target, error_message) + bot.log.debug(f"No permissions found for mask '{mask_filter}'") + return + + # List all matching permissions + for entry in entries: + permission_message = style( + f"Mask: {entry['mask']}, Permission: {entry['permission']}", + fg='blue', bold=True + ) + bot.privmsg(target, permission_message) + bot.log.debug(f"Listed permissions for mask '{mask_filter}'") + +# To use this plugin, add the following to bot config: +# [irc3.plugins.command] +# guard = permissions.TinyDBPolicy \ No newline at end of file