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

Relay: rework to use the permission system

This defines the following permissions:

Granted to opers by default:
- relay.create
- relay.destroy
- relay.claim
- relay.link
- relay.delink
- relay.linkacl.view
- relay.linkacl

Granted to all users by default:
- relay.linked

And the following which is not explicitly granted:
- relay.savedb

Closes #325.
This commit is contained in:
James Lu 2016-11-07 21:18:20 -08:00
parent 93ca62aa49
commit 2d20256ed8
3 changed files with 52 additions and 15 deletions

17
docs/relay-permissions.md Normal file
View File

@ -0,0 +1,17 @@
## Relay Permissions
Relay defines the following permissions, which can be customized by defining the `permissions:` configuration block (see [example-permissions.yml](../example-permissions.yml) for examples).
By default, the following permissions are given to opers:
- `relay.create`
- `relay.destroy`
- `relay.claim`
- `relay.link`
- `relay.delink`
- `relay.linkacl.view`
- `relay.linkacl`
The following permissions are given to all users:
- `relay.linked`

View File

@ -4,14 +4,14 @@
# 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.
# The permissions API is new, and optional for plugins. Currently, only Automode and Relay use 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
# This determines whether we should merge plugins' built-in default permissions with the ones specified
# in the following 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
@ -28,6 +28,10 @@ permissions:
- automode.manage.relay_owned
- automode.sync.relay_owned
- automode.list
# These allow opers to manage Relay links on their network.
"$pylinkacc":
# Those with an admin login in PyLink can do anything.
- "*"

View File

@ -8,6 +8,7 @@ from collections import defaultdict
from pylinkirc import utils, world, conf
from pylinkirc.log import log
from pylinkirc.coremods import control
from pylinkirc.coremods import permissions
### GLOBAL (statekeeping) VARIABLES
relayusers = defaultdict(dict)
@ -20,6 +21,11 @@ save_delay = conf.conf['bot'].get('save_delay', 300)
db = {}
dbname = utils.getDatabaseName('pylinkrelay')
default_permissions = {"*!*@*": ['relay.linked'],
"$ircop": ['relay.create', 'relay.linkacl*',
'relay.destroy', 'relay.link', 'relay.delink',
'relay.claim']}
### INTERNAL FUNCTIONS
def initializeAll(irc):
@ -53,6 +59,8 @@ def main(irc=None):
log.debug('relay.main: scheduling export loop')
permissions.addDefaultPermissions(default_permissions)
if irc is not None:
# irc is defined when the plugin is reloaded. Otherwise, it means that we've just started the
# server. Iterate over all connected networks and initialize their relay users.
@ -77,10 +85,13 @@ def die(sourceirc):
relayservers.clear()
relayusers.clear()
# 3) Export the relay links database.
# 3) Unload our permissions.
permissions.removeDefaultPermissions(default_permissions)
# 4) Export the relay links database.
exportDB()
# 4) Kill the scheduling for any other exports.
# 5) Kill the scheduling for any other exports.
global exportdb_timer
if exportdb_timer:
log.debug("Relay: cancelling exportDB timer thread %s due to die()", threading.get_ident())
@ -1562,7 +1573,7 @@ def create(irc, source, args):
irc.reply('Error: You must be in %r to complete this operation.' % channel)
return
irc.checkAuthenticated(source)
permissions.checkPermissions(irc, source, ['relay.create'])
# Check to see whether the channel requested is already part of a different
# relay.
@ -1601,12 +1612,12 @@ def destroy(irc, source, args):
irc.reply('Error: Invalid channel %r.' % channel)
return
# Check for different permissions based on whether we're destroying a local channel or
# a remote one.
if network == irc.name:
# If we're destroying a channel on the current network, only oper is needed.
irc.checkAuthenticated(source)
permissions.checkPermissions(irc, source, ['relay.destroy'])
else:
# Otherwise, we'll need to be logged in as admin.
irc.checkAuthenticated(source, allowOper=False)
permissions.checkPermissions(irc, source, ['relay.destroy.remote'])
entry = (network, channel)
@ -1670,7 +1681,7 @@ def link(irc, source, args):
irc.reply('Error: You must be in %r to complete this operation.' % localchan)
return
irc.checkAuthenticated(source)
permissions.checkPermissions(irc, source, ['relay.link'])
if remotenet not in world.networkobjects:
irc.reply('Error: No network named %r exists.' % remotenet)
@ -1727,7 +1738,9 @@ def delink(irc, source, args):
remotenet = args[1]
except IndexError:
remotenet = None
irc.checkAuthenticated(source)
permissions.checkPermissions(irc, source, ['relay.delink'])
if not utils.isChannel(channel):
irc.reply('Error: Invalid channel %r.' % channel)
return
@ -1843,7 +1856,7 @@ def linkacl(irc, source, args):
Allows blocking / unblocking certain networks from linking to a relayed channel, based on a blacklist.
LINKACL LIST returns a list of blocked networks for a channel, while the ALLOW and DENY subcommands allow manipulating this blacklist."""
missingargs = "Error: Not enough arguments. Needs 2-3: subcommand (ALLOW/DENY/LIST), channel, remote network (for ALLOW/DENY)."
irc.checkAuthenticated(source)
try:
cmd = args[0].lower()
channel = irc.toLower(args[1])
@ -1858,10 +1871,12 @@ def linkacl(irc, source, args):
irc.reply('Error: No such relay %r exists.' % channel)
return
if cmd == 'list':
permissions.checkPermissions(irc, source, ['relay.linkacl.view'])
s = 'Blocked networks for \x02%s\x02: \x02%s\x02' % (channel, ', '.join(db[relay]['blocked_nets']) or '(empty)')
irc.reply(s)
return
permissions.checkPermissions(irc, source, ['relay.linkacl'])
try:
remotenet = args[2]
except IndexError:
@ -1922,7 +1937,7 @@ def save(irc, source, args):
"""takes no arguments.
Saves the relay database to disk."""
irc.checkAuthenticated(source)
permissions.checkPermissions(irc, source, ['relay.savedb'])
exportDB()
irc.reply('Done.')
@ -1933,13 +1948,14 @@ def claim(irc, source, args):
Sets the CLAIM for a channel to a case-sensitive list of networks. If no list of networks is given, shows which networks have claim over the channel. A single hyphen (-) can also be given as a list of networks to remove claim from the channel entirely.
CLAIM is a way of enforcing network ownership for a channel, similarly to Janus. Unless the list is empty, only networks on the CLAIM list for a channel (plus the creating network) are allowed to override kicks, mode changes, and topic changes in it - attempts from other networks' opers to do this are simply blocked or reverted."""
irc.checkAuthenticated(source)
try:
channel = irc.toLower(args[0])
except IndexError:
irc.reply("Error: Not enough arguments. Needs 1-2: channel, list of networks (optional).")
return
permissions.checkPermissions(irc, source, ['relay.claim'])
# We override getRelay() here to limit the search to the current network.
relay = (irc.name, channel)
if relay not in db: