3
0
mirror of https://github.com/jlu5/PyLink.git synced 2024-11-23 19:19:31 +01:00

permissions, automode: work on default permissions & add example permissions config (#190)

- Fix possible type errors in add/removeDefaultPermissions by converting permlist values to sets.
- Fix wrong permission string being checked in automode.<command>.#channel
- automode: register and unregister default permissions on load/unload.
- permissions: add an 'also_show' argument to checkPermissions(), to display alternative permissions that weren't directly checked.
This commit is contained in:
James Lu 2016-08-25 11:41:37 -07:00
parent 03a780f397
commit f890ddac1b
3 changed files with 57 additions and 8 deletions

View File

@ -7,7 +7,7 @@ import threading
# Global variables: these store mappings of hostmasks/exttargets to lists of permissions each target has. # Global variables: these store mappings of hostmasks/exttargets to lists of permissions each target has.
default_permissions = defaultdict(set) default_permissions = defaultdict(set)
permissions = defaultdict(set) permissions = defaultdict(set, {'$pylinkacc': {'*'}})
# Only allow one thread to change the permissions index at once. # Only allow one thread to change the permissions index at once.
permissions_lock = threading.Lock() permissions_lock = threading.Lock()
@ -23,7 +23,12 @@ def resetPermissions():
with permissions_lock: with permissions_lock:
global permissions global permissions
log.debug('permissions.resetPermissions: old perm list: %s', permissions) log.debug('permissions.resetPermissions: old perm list: %s', permissions)
permissions = conf.conf.get('permissions', default_permissions)
if not conf.conf.get('permissions_merge_defaults', True):
log.debug('permissions.resetPermissions: clearing perm list due to permissions_merge_defaults set False.')
permissions.clear()
permissions.update(conf.conf.get('permissions', default_permissions))
log.debug('permissions.resetPermissions: new perm list: %s', permissions) log.debug('permissions.resetPermissions: new perm list: %s', permissions)
def addDefaultPermissions(perms): def addDefaultPermissions(perms):
@ -31,16 +36,16 @@ def addDefaultPermissions(perms):
with permissions_lock: with permissions_lock:
global permissions global permissions
for target, permlist in perms.items(): for target, permlist in perms.items():
permissions[target] |= permlist permissions[target] |= set(permlist)
def removeDefaultPermissions(perms): def removeDefaultPermissions(perms):
"""Remove default permissions from the index.""" """Remove default permissions from the index."""
with permissions_lock: with permissions_lock:
global permissions global permissions
for target, permlist in perms.items(): for target, permlist in perms.items():
permissions[target] -= permlist permissions[target] -= set(permlist)
def checkPermissions(irc, uid, perms): def checkPermissions(irc, uid, perms, also_show=[]):
""" """
Checks permissions of the caller. If the caller has any of the permissions listed in perms, Checks permissions of the caller. If the caller has any of the permissions listed in perms,
this function returns True. Otherwise, NotAuthorizedError is raised. this function returns True. Otherwise, NotAuthorizedError is raised.
@ -58,7 +63,7 @@ def checkPermissions(irc, uid, perms):
if any(irc.matchHost(perm, p) for p in perms): if any(irc.matchHost(perm, p) for p in perms):
return True return True
raise utils.NotAuthorizedError("You are missing one of the following permissions: %s" % raise utils.NotAuthorizedError("You are missing one of the following permissions: %s" %
(', '.join(perms))) (', '.join(perms+also_show)))
# This is called on first import. # This is called on first import.

35
example-permissions.yml Normal file
View File

@ -0,0 +1,35 @@
# This file is an example of the permissions system in PyLink. Should you wish,
# you may copy the contents of this file and paste it into the configuration you're
# using.
# Permissions work by mapping hostmasks or exttargets to list of permissions, allowing
# you to fine tune which users have access to which commands.
# The permissions API is new, and optional for plugins. Currently, only Automode uses it.
# If you do not specify any permissions block in your configuration, PyLink will default to a
# permission set defined by plugins, which usually correspond to the list below, but can be
# changed on every release.
# This determines whether we should merge the plugin-default permissions with the ones specified
# in the permissions: block. Disabling this allows you greater control over the permissions
# PyLink gives, but you should check this file on every major update to see if any new permissions
# were added for commands. Otherwise, commands that were available before may cease to function!
permissions_merge_defaults: true
permissions:
# Note: It is a good idea to quote any exttargets or hostmasks so the configuration parser knows
# they are raw strings.
"$ircop":
# The default set of Automode permissions allow you to manage any channels you own in Relay.
# If Relay is not loaded, this check will fail. This has the ability of allowing local opers
# to manage their channels, but not abusing Automode to hack modes in other networks' relay
# channels.
- automode.manage.relay_owned
- automode.sync.relay_owned
"*!*@*":
# Everyone can use /msg Automode list on any channel.
- automode.list
"$pylinkacc":
# Those with an admin login in PyLink can do anything.
- "*"

View File

@ -24,6 +24,10 @@ exportdb_timer = None
save_delay = conf.conf['bot'].get('save_delay', 300) save_delay = conf.conf['bot'].get('save_delay', 300)
# The default set of Automode permissions.
default_permissions = {"$ircop": ['automode.manage.relay_owned', 'automode.sync.relay_owned'],
"*!*@*": ['automode.list']}
def loadDB(): def loadDB():
"""Loads the Automode database, silently creating a new one if this fails.""" """Loads the Automode database, silently creating a new one if this fails."""
global db global db
@ -66,6 +70,9 @@ def main(irc=None):
# Schedule periodic exports of the automode database. # Schedule periodic exports of the automode database.
scheduleExport(starting=True) scheduleExport(starting=True)
# Register our permissions.
permissions.addDefaultPermissions(default_permissions)
# Queue joins to all channels where Automode has entries. # Queue joins to all channels where Automode has entries.
for entry in db: for entry in db:
netname, channel = entry.split('#', 1) netname, channel = entry.split('#', 1)
@ -94,6 +101,7 @@ def die(sourceirc):
log.debug("Automode: cancelling exportDB timer thread %s due to die()", threading.get_ident()) log.debug("Automode: cancelling exportDB timer thread %s due to die()", threading.get_ident())
exportdb_timer.cancel() exportdb_timer.cancel()
permissions.removeDefaultPermissions(default_permissions)
utils.unregisterService('automode') utils.unregisterService('automode')
def checkAccess(irc, uid, channel, command): def checkAccess(irc, uid, channel, command):
@ -111,10 +119,11 @@ def checkAccess(irc, uid, channel, command):
baseperm = 'automode.%s' % command baseperm = 'automode.%s' % command
try: try:
# First, check the catch all and channel permissions. # First, check the catch all and channel permissions.
return permissions.checkPermissions(irc, uid, [baseperm, baseperm+'.*', '%s.%s' % (command, channel)]) perms = [baseperm, baseperm+'.*', '%s.%s' % (baseperm, channel)]
return permissions.checkPermissions(irc, uid, perms)
except utils.NotAuthorizedError: except utils.NotAuthorizedError:
log.debug('(%s) Automode: falling back to automode.%s.relay_owned', irc.name, command) log.debug('(%s) Automode: falling back to automode.%s.relay_owned', irc.name, command)
permissions.checkPermissions(irc, uid, [baseperm+'.relay_owned']) permissions.checkPermissions(irc, uid, [baseperm+'.relay_owned'], also_show=perms)
relay = world.plugins.get('relay') relay = world.plugins.get('relay')
if relay is None: if relay is None: