3
0
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:
James Lu 2016-04-30 16:54:11 -07:00
parent 4bd4c23a08
commit 2fe2e9c8c4
9 changed files with 75 additions and 75 deletions

View File

@ -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',

View File

@ -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.

View File

@ -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:

View File

@ -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:

View File

@ -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():

View File

@ -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]

View File

@ -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:])

View File

@ -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:

View File

@ -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.