mirror of
https://github.com/jlu5/PyLink.git
synced 2024-11-01 01:09:22 +01:00
core: move isOper, checkAuthenticated to Irc (#199)
This commit is contained in:
parent
4bd4c23a08
commit
2fe2e9c8c4
37
classes.py
37
classes.py
@ -15,6 +15,7 @@ import threading
|
||||
import ssl
|
||||
import hashlib
|
||||
from copy import deepcopy
|
||||
import inspect
|
||||
|
||||
from log import *
|
||||
import world
|
||||
@ -26,6 +27,13 @@ import structures
|
||||
class ProtocolError(Exception):
|
||||
pass
|
||||
|
||||
class NotAuthenticatedError(Exception):
|
||||
"""
|
||||
Exception raised by checkAuthenticated() when a user fails authentication
|
||||
requirements.
|
||||
"""
|
||||
pass
|
||||
|
||||
### Internal classes (users, servers, channels)
|
||||
|
||||
class Irc():
|
||||
@ -469,7 +477,7 @@ class Irc():
|
||||
for func in world.commands[cmd]:
|
||||
try:
|
||||
func(self, source, cmd_args)
|
||||
except utils.NotAuthenticatedError:
|
||||
except NotAuthenticatedError:
|
||||
self.msg(self.called_by or source, 'Error: You are not authorized to perform this operation.')
|
||||
except Exception as e:
|
||||
log.exception('Unhandled exception caught in command %r', cmd)
|
||||
@ -848,6 +856,33 @@ class Irc():
|
||||
|
||||
return '%s!%s@%s' % (nick, ident, host)
|
||||
|
||||
def isOper(self, uid, allowAuthed=True, allowOper=True):
|
||||
"""
|
||||
Returns whether the given user has operator status on PyLink. This can be achieved
|
||||
by either identifying to PyLink as admin (if allowAuthed is True),
|
||||
or having user mode +o set (if allowOper is True). At least one of
|
||||
allowAuthed or allowOper must be True for this to give any meaningful
|
||||
results.
|
||||
"""
|
||||
if uid in self.users:
|
||||
if allowOper and ("o", None) in self.users[uid].modes:
|
||||
return True
|
||||
elif allowAuthed and self.users[uid].identified:
|
||||
return True
|
||||
return False
|
||||
|
||||
def checkAuthenticated(self, uid, allowAuthed=True, allowOper=True):
|
||||
"""
|
||||
Checks whether the given user has operator status on PyLink, raising
|
||||
NotAuthenticatedError and logging the access denial if not.
|
||||
"""
|
||||
lastfunc = inspect.stack()[1][3]
|
||||
if not self.isOper(uid, allowAuthed=allowAuthed, allowOper=allowOper):
|
||||
log.warning('(%s) Access denied for %s calling %r', self.name,
|
||||
self.getHostmask(uid), lastfunc)
|
||||
raise NotAuthenticatedError("You are not authenticated!")
|
||||
return True
|
||||
|
||||
class IrcUser():
|
||||
"""PyLink IRC user class."""
|
||||
def __init__(self, nick, ts, uid, ident='null', host='null',
|
||||
|
@ -221,7 +221,7 @@ def shutdown(irc, source, args):
|
||||
"""takes no arguments.
|
||||
|
||||
Exits PyLink by disconnecting all networks."""
|
||||
utils.checkAuthenticated(irc, source, allowOper=False)
|
||||
irc.checkAuthenticated(source, allowOper=False)
|
||||
u = irc.users[source]
|
||||
|
||||
log.info('(%s) SHUTDOWN requested by "%s!%s@%s", exiting...', irc.name, u.nick,
|
||||
@ -233,7 +233,7 @@ def load(irc, source, args):
|
||||
"""<plugin name>.
|
||||
|
||||
Loads a plugin from the plugin folder."""
|
||||
utils.checkAuthenticated(irc, source, allowOper=False)
|
||||
irc.checkAuthenticated(source, allowOper=False)
|
||||
try:
|
||||
name = args[0]
|
||||
except IndexError:
|
||||
@ -262,7 +262,7 @@ def unload(irc, source, args):
|
||||
"""<plugin name>.
|
||||
|
||||
Unloads a currently loaded plugin."""
|
||||
utils.checkAuthenticated(irc, source, allowOper=False)
|
||||
irc.checkAuthenticated(source, allowOper=False)
|
||||
try:
|
||||
name = args[0]
|
||||
except IndexError:
|
||||
@ -386,7 +386,7 @@ def rehash(irc, source, args):
|
||||
|
||||
Reloads the configuration file for PyLink, (dis)connecting added/removed networks.
|
||||
Plugins must be manually reloaded."""
|
||||
utils.checkAuthenticated(irc, source, allowOper=False)
|
||||
irc.checkAuthenticated(source, allowOper=False)
|
||||
try:
|
||||
_rehash()
|
||||
except Exception as e: # Something went wrong, abort.
|
||||
|
@ -16,7 +16,7 @@ def spawnclient(irc, source, args):
|
||||
|
||||
Admin-only. Spawns the specified PseudoClient on the PyLink server.
|
||||
Note: this doesn't check the validity of any fields you give it!"""
|
||||
utils.checkAuthenticated(irc, source, allowOper=False)
|
||||
irc.checkAuthenticated(source, allowOper=False)
|
||||
try:
|
||||
nick, ident, host = args[:3]
|
||||
except ValueError:
|
||||
@ -29,7 +29,7 @@ def quit(irc, source, args):
|
||||
"""<target> [<reason>]
|
||||
|
||||
Admin-only. Quits the PyLink client with nick <target>, if one exists."""
|
||||
utils.checkAuthenticated(irc, source, allowOper=False)
|
||||
irc.checkAuthenticated(source, allowOper=False)
|
||||
|
||||
try:
|
||||
nick = args[0]
|
||||
@ -55,7 +55,7 @@ def joinclient(irc, source, args):
|
||||
"""[<target>] <channel1>,[<channel2>], etc.
|
||||
|
||||
Admin-only. Joins <target>, the nick of a PyLink client, to a comma-separated list of channels. If <target> is not given, it defaults to the main PyLink client."""
|
||||
utils.checkAuthenticated(irc, source, allowOper=False)
|
||||
irc.checkAuthenticated(source, allowOper=False)
|
||||
|
||||
try:
|
||||
# Check if the first argument is an existing PyLink client. If it is not,
|
||||
@ -100,7 +100,7 @@ def nick(irc, source, args):
|
||||
"""[<target>] <newnick>
|
||||
|
||||
Admin-only. Changes the nick of <target>, a PyLink client, to <newnick>. If <target> is not given, it defaults to the main PyLink client."""
|
||||
utils.checkAuthenticated(irc, source, allowOper=False)
|
||||
irc.checkAuthenticated(source, allowOper=False)
|
||||
|
||||
try:
|
||||
nick = args[0]
|
||||
@ -134,7 +134,7 @@ def part(irc, source, args):
|
||||
"""[<target>] <channel1>,[<channel2>],... [<reason>]
|
||||
|
||||
Admin-only. Parts <target>, the nick of a PyLink client, from a comma-separated list of channels. If <target> is not given, it defaults to the main PyLink client."""
|
||||
utils.checkAuthenticated(irc, source, allowOper=False)
|
||||
irc.checkAuthenticated(source, allowOper=False)
|
||||
|
||||
try:
|
||||
nick = args[0]
|
||||
@ -180,7 +180,7 @@ def msg(irc, source, args):
|
||||
"""[<source>] <target> <text>
|
||||
|
||||
Admin-only. Sends message <text> from <source>, where <source> is the nick of a PyLink client. If <source> is not given, it defaults to the main PyLink client."""
|
||||
utils.checkAuthenticated(irc, source, allowOper=False)
|
||||
irc.checkAuthenticated(source, allowOper=False)
|
||||
|
||||
# Because we want the source nick to be optional, this argument parsing gets a bit tricky.
|
||||
try:
|
||||
|
@ -18,7 +18,7 @@ def status(irc, source, args):
|
||||
irc.reply('You are identified as \x02%s\x02.' % identified)
|
||||
else:
|
||||
irc.reply('You are not identified as anyone.')
|
||||
irc.reply('Operator access: \x02%s\x02' % bool(utils.isOper(irc, source)))
|
||||
irc.reply('Operator access: \x02%s\x02' % bool(irc.isOper(source)))
|
||||
|
||||
def listcommands(irc, source, args):
|
||||
"""takes no arguments.
|
||||
@ -81,7 +81,7 @@ def showuser(irc, source, args):
|
||||
u = irc.nickToUid(target) or target
|
||||
# Only show private info if the person is calling 'showuser' on themselves,
|
||||
# or is an oper.
|
||||
verbose = utils.isOper(irc, source) or u == source
|
||||
verbose = irc.isOper(source) or u == source
|
||||
if u not in irc.users:
|
||||
irc.reply('Error: Unknown user %r.' % target)
|
||||
return
|
||||
@ -127,7 +127,7 @@ def showchan(irc, source, args):
|
||||
f = lambda s: irc.msg(source, s)
|
||||
c = irc.channels[channel]
|
||||
# Only show verbose info if caller is oper or is in the target channel.
|
||||
verbose = source in c.users or utils.isOper(irc, source)
|
||||
verbose = source in c.users or irc.isOper(source)
|
||||
secret = ('s', None) in c.modes
|
||||
if secret and not verbose:
|
||||
# Hide secret channels from normal users.
|
||||
@ -178,7 +178,7 @@ def loglevel(irc, source, args):
|
||||
|
||||
Sets the log level to the given <level>. <level> must be either DEBUG, INFO, WARNING, ERROR, or CRITICAL.
|
||||
If no log level is given, shows the current one."""
|
||||
utils.checkAuthenticated(irc, source, allowOper=False)
|
||||
irc.checkAuthenticated(source, allowOper=False)
|
||||
try:
|
||||
level = args[0].upper()
|
||||
try:
|
||||
|
@ -21,7 +21,7 @@ def _exec(irc, source, args):
|
||||
|
||||
Admin-only. Executes <code> in the current PyLink instance. This command performs backslash escaping of characters, so things like \\n and \\ will work.
|
||||
\x02**WARNING: THIS CAN BE DANGEROUS IF USED IMPROPERLY!**\x02"""
|
||||
utils.checkAuthenticated(irc, source, allowOper=False)
|
||||
irc.checkAuthenticated(source, allowOper=False)
|
||||
|
||||
# Allow using \n in the code, while escaping backslashes correctly otherwise.
|
||||
args = bytes(' '.join(args), 'utf-8').decode("unicode_escape")
|
||||
@ -40,7 +40,7 @@ def _eval(irc, source, args):
|
||||
|
||||
Admin-only. Evaluates the given Python expression and returns the result.
|
||||
\x02**WARNING: THIS CAN BE DANGEROUS IF USED IMPROPERLY!**\x02"""
|
||||
utils.checkAuthenticated(irc, source, allowOper=False)
|
||||
irc.checkAuthenticated(source, allowOper=False)
|
||||
|
||||
args = ' '.join(args)
|
||||
if not args.strip():
|
||||
@ -58,7 +58,7 @@ def raw(irc, source, args):
|
||||
|
||||
Admin-only. Sends raw text to the uplink IRC server.
|
||||
\x02**WARNING: THIS CAN BREAK YOUR NETWORK IF USED IMPROPERLY!**\x02"""
|
||||
utils.checkAuthenticated(irc, source, allowOper=False)
|
||||
irc.checkAuthenticated(source, allowOper=False)
|
||||
|
||||
args = ' '.join(args)
|
||||
if not args.strip():
|
||||
@ -77,7 +77,7 @@ def inject(irc, source, args):
|
||||
|
||||
Admin-only. Injects raw text into the running PyLink protocol module, replying with the hook data returned.
|
||||
\x02**WARNING: THIS CAN BREAK YOUR NETWORK IF USED IMPROPERLY!**\x02"""
|
||||
utils.checkAuthenticated(irc, source, allowOper=False)
|
||||
irc.checkAuthenticated(source, allowOper=False)
|
||||
|
||||
args = ' '.join(args)
|
||||
if not args.strip():
|
||||
|
@ -16,7 +16,7 @@ def disconnect(irc, source, args):
|
||||
|
||||
Disconnects the network <network>. When all networks are disconnected, PyLink will automatically exit.
|
||||
Note: This does not affect the autoreconnect settings of any network, so the network will likely just reconnect unless autoconnect is disabled (see the 'autoconnect' command)."""
|
||||
utils.checkAuthenticated(irc, source, allowOper=False)
|
||||
irc.checkAuthenticated(source, allowOper=False)
|
||||
try:
|
||||
netname = args[0]
|
||||
network = world.networkobjects[netname]
|
||||
@ -39,7 +39,7 @@ def connect(irc, source, args):
|
||||
"""<network>
|
||||
|
||||
Initiates a connection to the network <network>."""
|
||||
utils.checkAuthenticated(irc, source, allowOper=False)
|
||||
irc.checkAuthenticated(source, allowOper=False)
|
||||
try:
|
||||
netname = args[0]
|
||||
network = world.networkobjects[netname]
|
||||
@ -69,7 +69,7 @@ def autoconnect(irc, source, args):
|
||||
|
||||
Sets the autoconnect time for <network> to <seconds>.
|
||||
You can disable autoconnect for a network by setting <seconds> to a negative value."""
|
||||
utils.checkAuthenticated(irc, source)
|
||||
irc.checkAuthenticated(source)
|
||||
try:
|
||||
netname = args[0]
|
||||
seconds = float(args[1])
|
||||
@ -91,7 +91,7 @@ def remote(irc, source, args):
|
||||
"""<network> <command>
|
||||
|
||||
Runs <command> on the remote network <network>. No replies are sent back due to protocol limitations."""
|
||||
utils.checkAuthenticated(irc, source, allowOper=False)
|
||||
irc.checkAuthenticated(source, allowOper=False)
|
||||
|
||||
try:
|
||||
netname = args[0]
|
||||
|
@ -22,7 +22,7 @@ def checkban(irc, source, args):
|
||||
"""<banmask (nick!user@host or user@host)> [<nick or hostmask to check>]
|
||||
|
||||
Oper only. If a nick or hostmask is given, return whether the given banmask will match it. Otherwise, returns a list of connected users that would be affected by such a ban, up to 50 results."""
|
||||
utils.checkAuthenticated(irc, source, allowOper=False)
|
||||
irc.checkAuthenticated(source, allowOper=False)
|
||||
|
||||
if ircmatch is None:
|
||||
irc.reply("Error: missing ircmatch module (install it via 'pip install ircmatch').")
|
||||
@ -87,7 +87,7 @@ def jupe(irc, source, args):
|
||||
Oper-only, jupes the given server."""
|
||||
|
||||
# Check that the caller is either opered or logged in as admin.
|
||||
utils.checkAuthenticated(irc, source)
|
||||
irc.checkAuthenticated(source)
|
||||
|
||||
try:
|
||||
servername = args[0]
|
||||
@ -114,7 +114,7 @@ def kick(irc, source, args):
|
||||
"""<source> <channel> <user> [<reason>]
|
||||
|
||||
Admin only. Kicks <user> from <channel> via <source>, where <source> is either the nick of a PyLink client or the SID of a PyLink server."""
|
||||
utils.checkAuthenticated(irc, source, allowOper=False)
|
||||
irc.checkAuthenticated(source, allowOper=False)
|
||||
try:
|
||||
sourcenick = args[0]
|
||||
channel = args[1]
|
||||
@ -155,7 +155,7 @@ def kill(irc, source, args):
|
||||
"""<source> <target> [<reason>]
|
||||
|
||||
Admin only. Kills <target> via <source>, where <source> is either the nick of a PyLink client or the SID of a PyLink server."""
|
||||
utils.checkAuthenticated(irc, source, allowOper=False)
|
||||
irc.checkAuthenticated(source, allowOper=False)
|
||||
try:
|
||||
sourcenick = args[0]
|
||||
target = args[1]
|
||||
@ -194,7 +194,7 @@ def mode(irc, source, args):
|
||||
Oper-only, sets modes <modes> on the target channel."""
|
||||
|
||||
# Check that the caller is either opered or logged in as admin.
|
||||
utils.checkAuthenticated(irc, source)
|
||||
irc.checkAuthenticated(source)
|
||||
|
||||
try:
|
||||
target, modes = args[0], args[1:]
|
||||
@ -232,7 +232,7 @@ def topic(irc, source, args):
|
||||
"""<channel> <topic>
|
||||
|
||||
Admin only. Updates the topic in a channel."""
|
||||
utils.checkAuthenticated(irc, source, allowOper=False)
|
||||
irc.checkAuthenticated(source, allowOper=False)
|
||||
try:
|
||||
channel = args[0]
|
||||
topic = ' '.join(args[1:])
|
||||
|
@ -1235,7 +1235,7 @@ def create(irc, source, args):
|
||||
if source not in irc.channels[channel].users:
|
||||
irc.reply('Error: You must be in %r to complete this operation.' % channel)
|
||||
return
|
||||
utils.checkAuthenticated(irc, source)
|
||||
irc.checkAuthenticated(source)
|
||||
|
||||
# Check to see whether the channel requested is already part of a different
|
||||
# relay.
|
||||
@ -1276,10 +1276,10 @@ def destroy(irc, source, args):
|
||||
|
||||
if network == irc.name:
|
||||
# If we're destroying a channel on the current network, only oper is needed.
|
||||
utils.checkAuthenticated(irc, source)
|
||||
irc.checkAuthenticated(source)
|
||||
else:
|
||||
# Otherwise, we'll need to be logged in as admin.
|
||||
utils.checkAuthenticated(irc, source, allowOper=False)
|
||||
irc.checkAuthenticated(source, allowOper=False)
|
||||
|
||||
entry = (network, channel)
|
||||
|
||||
@ -1320,7 +1320,7 @@ def link(irc, source, args):
|
||||
if source not in irc.channels[localchan].users:
|
||||
irc.reply('Error: You must be in %r to complete this operation.' % localchan)
|
||||
return
|
||||
utils.checkAuthenticated(irc, source)
|
||||
irc.checkAuthenticated(source)
|
||||
if remotenet not in world.networkobjects:
|
||||
irc.reply('Error: No network named %r exists.' % remotenet)
|
||||
return
|
||||
@ -1363,7 +1363,7 @@ def delink(irc, source, args):
|
||||
remotenet = args[1]
|
||||
except IndexError:
|
||||
remotenet = None
|
||||
utils.checkAuthenticated(irc, source)
|
||||
irc.checkAuthenticated(source)
|
||||
if not utils.isChannel(channel):
|
||||
irc.reply('Error: Invalid channel %r.' % channel)
|
||||
return
|
||||
@ -1421,7 +1421,7 @@ def linked(irc, source, args):
|
||||
if ('s', None) in c.modes or ('p', None) in c.modes:
|
||||
# Only show secret channels to opers, and tag them with
|
||||
# [secret].
|
||||
if utils.isOper(irc, source):
|
||||
if irc.isOper(source):
|
||||
s += '\x02[secret]\x02 '
|
||||
else:
|
||||
continue
|
||||
@ -1434,7 +1434,7 @@ def linked(irc, source, args):
|
||||
|
||||
irc.msg(source, s)
|
||||
|
||||
if utils.isOper(irc, source):
|
||||
if irc.isOper(source):
|
||||
s = ''
|
||||
|
||||
# If the caller is an oper, we can show the hostmasks of people
|
||||
@ -1460,7 +1460,7 @@ def linkacl(irc, source, args):
|
||||
Allows blocking / unblocking certain networks from linking to a relay, 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)."
|
||||
utils.checkAuthenticated(irc, source)
|
||||
irc.checkAuthenticated(source)
|
||||
try:
|
||||
cmd = args[0].lower()
|
||||
channel = utils.toLower(irc, args[1])
|
||||
@ -1530,7 +1530,7 @@ def showuser(irc, source, args):
|
||||
relay = getRelay((irc.name, ch))
|
||||
if relay:
|
||||
relaychannels.append(''.join(relay))
|
||||
if relaychannels and (utils.isOper(irc, source) or u == source):
|
||||
if relaychannels and (irc.isOper(source) or u == source):
|
||||
irc.msg(source, "\x02Relay channels\x02: %s" % ' '.join(relaychannels))
|
||||
|
||||
@utils.add_cmd
|
||||
@ -1538,7 +1538,7 @@ def save(irc, source, args):
|
||||
"""takes no arguments.
|
||||
|
||||
Saves the relay database to disk."""
|
||||
utils.checkAuthenticated(irc, source)
|
||||
irc.checkAuthenticated(source)
|
||||
exportDB()
|
||||
irc.reply('Done.')
|
||||
|
||||
@ -1549,7 +1549,7 @@ 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."""
|
||||
utils.checkAuthenticated(irc, source)
|
||||
irc.checkAuthenticated(source)
|
||||
try:
|
||||
channel = utils.toLower(irc, args[0])
|
||||
except IndexError:
|
||||
|
35
utils.py
35
utils.py
@ -7,7 +7,6 @@ framework.
|
||||
|
||||
import string
|
||||
import re
|
||||
import inspect
|
||||
import importlib
|
||||
import os
|
||||
|
||||
@ -15,13 +14,6 @@ from log import log
|
||||
import world
|
||||
import conf
|
||||
|
||||
class NotAuthenticatedError(Exception):
|
||||
"""
|
||||
Exception raised by checkAuthenticated() when a user fails authentication
|
||||
requirements.
|
||||
"""
|
||||
pass
|
||||
|
||||
class IncrementalUIDGenerator():
|
||||
"""
|
||||
Incremental UID Generator module, adapted from InspIRCd source:
|
||||
@ -132,33 +124,6 @@ def applyModes(irc, target, changedmodes):
|
||||
log.warning("(%s) utils.applyModes is deprecated. Use irc.applyModes() instead!", irc.name)
|
||||
return irc.applyModes(target, changedmodes)
|
||||
|
||||
def isOper(irc, uid, allowAuthed=True, allowOper=True):
|
||||
"""
|
||||
Returns whether the given user has operator status on PyLink. This can be achieved
|
||||
by either identifying to PyLink as admin (if allowAuthed is True),
|
||||
or having user mode +o set (if allowOper is True). At least one of
|
||||
allowAuthed or allowOper must be True for this to give any meaningful
|
||||
results.
|
||||
"""
|
||||
if uid in irc.users:
|
||||
if allowOper and ("o", None) in irc.users[uid].modes:
|
||||
return True
|
||||
elif allowAuthed and irc.users[uid].identified:
|
||||
return True
|
||||
return False
|
||||
|
||||
def checkAuthenticated(irc, uid, allowAuthed=True, allowOper=True):
|
||||
"""
|
||||
Checks whether the given user has operator status on PyLink, raising
|
||||
NotAuthenticatedError and logging the access denial if not.
|
||||
"""
|
||||
lastfunc = inspect.stack()[1][3]
|
||||
if not isOper(irc, uid, allowAuthed=allowAuthed, allowOper=allowOper):
|
||||
log.warning('(%s) Access denied for %s calling %r', irc.name,
|
||||
getHostmask(irc, uid), lastfunc)
|
||||
raise NotAuthenticatedError("You are not authenticated!")
|
||||
return True
|
||||
|
||||
def loadModuleFromFolder(name, folder):
|
||||
"""
|
||||
Imports and returns a module, if existing, from a specific folder.
|
||||
|
Loading…
Reference in New Issue
Block a user