3
0
mirror of https://github.com/jlu5/PyLink.git synced 2024-11-27 21: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 # Permissions work by mapping hostmasks or exttargets to list of permissions, allowing
# you to fine tune which users have access to which commands. # 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 # 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 # permission set defined by plugins, which usually correspond to the list below, but can be
# changed on every release. # changed on every release.
# This determines whether we should merge the plugin-default permissions with the ones specified # This determines whether we should merge plugins' built-in default permissions with the ones specified
# in the permissions: block. Disabling this allows you greater control over the permissions # 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 # 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! # were added for commands. Otherwise, commands that were available before may cease to function!
permissions_merge_defaults: true permissions_merge_defaults: true
@ -28,6 +28,10 @@ permissions:
- automode.manage.relay_owned - automode.manage.relay_owned
- automode.sync.relay_owned - automode.sync.relay_owned
- automode.list - automode.list
# These allow opers to manage Relay links on their network.
"$pylinkacc": "$pylinkacc":
# Those with an admin login in PyLink can do anything. # 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 import utils, world, conf
from pylinkirc.log import log from pylinkirc.log import log
from pylinkirc.coremods import control from pylinkirc.coremods import control
from pylinkirc.coremods import permissions
### GLOBAL (statekeeping) VARIABLES ### GLOBAL (statekeeping) VARIABLES
relayusers = defaultdict(dict) relayusers = defaultdict(dict)
@ -20,6 +21,11 @@ save_delay = conf.conf['bot'].get('save_delay', 300)
db = {} db = {}
dbname = utils.getDatabaseName('pylinkrelay') dbname = utils.getDatabaseName('pylinkrelay')
default_permissions = {"*!*@*": ['relay.linked'],
"$ircop": ['relay.create', 'relay.linkacl*',
'relay.destroy', 'relay.link', 'relay.delink',
'relay.claim']}
### INTERNAL FUNCTIONS ### INTERNAL FUNCTIONS
def initializeAll(irc): def initializeAll(irc):
@ -53,6 +59,8 @@ def main(irc=None):
log.debug('relay.main: scheduling export loop') log.debug('relay.main: scheduling export loop')
permissions.addDefaultPermissions(default_permissions)
if irc is not None: if irc is not None:
# irc is defined when the plugin is reloaded. Otherwise, it means that we've just started the # 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. # server. Iterate over all connected networks and initialize their relay users.
@ -77,10 +85,13 @@ def die(sourceirc):
relayservers.clear() relayservers.clear()
relayusers.clear() relayusers.clear()
# 3) Export the relay links database. # 3) Unload our permissions.
permissions.removeDefaultPermissions(default_permissions)
# 4) Export the relay links database.
exportDB() exportDB()
# 4) Kill the scheduling for any other exports. # 5) Kill the scheduling for any other exports.
global exportdb_timer global exportdb_timer
if exportdb_timer: if exportdb_timer:
log.debug("Relay: cancelling exportDB timer thread %s due to die()", threading.get_ident()) 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) irc.reply('Error: You must be in %r to complete this operation.' % channel)
return return
irc.checkAuthenticated(source) permissions.checkPermissions(irc, source, ['relay.create'])
# Check to see whether the channel requested is already part of a different # Check to see whether the channel requested is already part of a different
# relay. # relay.
@ -1601,12 +1612,12 @@ def destroy(irc, source, args):
irc.reply('Error: Invalid channel %r.' % channel) irc.reply('Error: Invalid channel %r.' % channel)
return return
# Check for different permissions based on whether we're destroying a local channel or
# a remote one.
if network == irc.name: if network == irc.name:
# If we're destroying a channel on the current network, only oper is needed. permissions.checkPermissions(irc, source, ['relay.destroy'])
irc.checkAuthenticated(source)
else: else:
# Otherwise, we'll need to be logged in as admin. permissions.checkPermissions(irc, source, ['relay.destroy.remote'])
irc.checkAuthenticated(source, allowOper=False)
entry = (network, channel) 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) irc.reply('Error: You must be in %r to complete this operation.' % localchan)
return return
irc.checkAuthenticated(source) permissions.checkPermissions(irc, source, ['relay.link'])
if remotenet not in world.networkobjects: if remotenet not in world.networkobjects:
irc.reply('Error: No network named %r exists.' % remotenet) irc.reply('Error: No network named %r exists.' % remotenet)
@ -1727,7 +1738,9 @@ def delink(irc, source, args):
remotenet = args[1] remotenet = args[1]
except IndexError: except IndexError:
remotenet = None remotenet = None
irc.checkAuthenticated(source)
permissions.checkPermissions(irc, source, ['relay.delink'])
if not utils.isChannel(channel): if not utils.isChannel(channel):
irc.reply('Error: Invalid channel %r.' % channel) irc.reply('Error: Invalid channel %r.' % channel)
return 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. 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.""" 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)." missingargs = "Error: Not enough arguments. Needs 2-3: subcommand (ALLOW/DENY/LIST), channel, remote network (for ALLOW/DENY)."
irc.checkAuthenticated(source)
try: try:
cmd = args[0].lower() cmd = args[0].lower()
channel = irc.toLower(args[1]) channel = irc.toLower(args[1])
@ -1858,10 +1871,12 @@ def linkacl(irc, source, args):
irc.reply('Error: No such relay %r exists.' % channel) irc.reply('Error: No such relay %r exists.' % channel)
return return
if cmd == 'list': 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)') s = 'Blocked networks for \x02%s\x02: \x02%s\x02' % (channel, ', '.join(db[relay]['blocked_nets']) or '(empty)')
irc.reply(s) irc.reply(s)
return return
permissions.checkPermissions(irc, source, ['relay.linkacl'])
try: try:
remotenet = args[2] remotenet = args[2]
except IndexError: except IndexError:
@ -1922,7 +1937,7 @@ def save(irc, source, args):
"""takes no arguments. """takes no arguments.
Saves the relay database to disk.""" Saves the relay database to disk."""
irc.checkAuthenticated(source) permissions.checkPermissions(irc, source, ['relay.savedb'])
exportDB() exportDB()
irc.reply('Done.') 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. 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.""" 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: try:
channel = irc.toLower(args[0]) channel = irc.toLower(args[0])
except IndexError: except IndexError:
irc.reply("Error: Not enough arguments. Needs 1-2: channel, list of networks (optional).") irc.reply("Error: Not enough arguments. Needs 1-2: channel, list of networks (optional).")
return return
permissions.checkPermissions(irc, source, ['relay.claim'])
# We override getRelay() here to limit the search to the current network. # We override getRelay() here to limit the search to the current network.
relay = (irc.name, channel) relay = (irc.name, channel)
if relay not in db: if relay not in db: