diff options
Diffstat (limited to 'http2irc.py')
| -rw-r--r-- | http2irc.py | 52 |
1 files changed, 35 insertions, 17 deletions
diff --git a/http2irc.py b/http2irc.py index 730820c..31ae084 100644 --- a/http2irc.py +++ b/http2irc.py @@ -154,7 +154,7 @@ class Config(dict): raise InvalidConfig(f'Invalid map key {key!r}') if not isinstance(map_, collections.abc.Mapping): raise InvalidConfig(f'Invalid map for {key!r}') - if any(x not in ('webpath', 'ircchannel', 'auth', 'module', 'moduleargs', 'overlongmode') for x in map_): + if any(x not in ('webpath', 'ircchannel', 'auth', 'postauth', 'getauth', 'module', 'moduleargs', 'overlongmode') for x in map_): raise InvalidConfig(f'Unknown key(s) found in map {key!r}') if 'webpath' not in map_: @@ -180,11 +180,19 @@ class Config(dict): if len(map_['ircchannel']) > 200: raise InvalidConfig(f'Invalid map {key!r} IRC channel: too long') + # For backward compatibility, 'auth' gets treated as 'postauth' if 'auth' in map_: - if map_['auth'] is not False and not isinstance(map_['auth'], str): - raise InvalidConfig(f'Invalid map {key!r} auth: must be false or a string') - if isinstance(map_['auth'], str) and ':' not in map_['auth']: - raise InvalidConfig(f'Invalid map {key!r} auth: must contain a colon') + if 'postauth' in map_: + raise InvalidConfig(f'auth and postauth are aliases and cannot be used together') + map_['postauth'] = map_['auth'] + del map_['auth'] + for k in ('postauth', 'getauth'): + if k not in map_: + continue + if map_[k] is not False and not isinstance(map_[k], str): + raise InvalidConfig(f'Invalid map {key!r} {k}: must be false or a string') + if isinstance(map_[k], str) and ':' not in map_[k]: + raise InvalidConfig(f'Invalid map {key!r} {k}: must contain a colon') if 'module' in map_: # If the path is relative, try to evaluate it relative to either the config file or this file; some modules are in the repo, but this also allows overriding them. @@ -217,8 +225,10 @@ class Config(dict): for key, map_ in obj['maps'].items(): # webpath is already set above for duplicate checking # ircchannel is set above for validation - if 'auth' not in map_: - map_['auth'] = False + if 'postauth' not in map_: + map_['postauth'] = False + if 'getauth' not in map_: + map_['getauth'] = False if 'module' not in map_: map_['module'] = None if 'moduleargs' not in map_: @@ -937,7 +947,9 @@ class WebServer: self.ircClient = ircClient self.config = config - self._paths = {} # '/path' => ('#channel', auth, module, moduleargs) where auth is either False (no authentication) or the HTTP header value for basic auth + self._paths = {} + # '/path' => ('#channel', postauth, getauth, module, moduleargs, overlongmode) + # {post,get}auth are either False (access denied) or the HTTP header value for basic auth self._app = aiohttp.web.Application() self._app.add_routes([ @@ -951,7 +963,14 @@ class WebServer: self.stopEvent = None def update_config(self, config): - self._paths = {map_['webpath']: (map_['ircchannel'], f'Basic {base64.b64encode(map_["auth"].encode("utf-8")).decode("utf-8")}' if map_['auth'] else False, map_['module'], map_['moduleargs'], map_['overlongmode']) for map_ in config['maps'].values()} + self._paths = {map_['webpath']: ( + map_['ircchannel'], + f'Basic {base64.b64encode(map_["postauth"].encode("utf-8")).decode("utf-8")}' if map_['postauth'] else False, + f'Basic {base64.b64encode(map_["getauth"].encode("utf-8")).decode("utf-8")}' if map_['getauth'] else False, + map_['module'], + map_['moduleargs'], + map_['overlongmode'] + ) for map_ in config['maps'].values()} needRebind = self.config['web'] != config['web'] self.config = config if needRebind: @@ -981,15 +1000,14 @@ class WebServer: except KeyError: self.logger.info(f'Bad request {id(request)}: no path {request.path!r}') raise aiohttp.web.HTTPNotFound() - auth = pathConfig[1] - if auth: - authHeader = request.headers.get('Authorization') - if not authHeader or authHeader != auth: - self.logger.info(f'Bad request {id(request)}: authentication failed: {authHeader!r} != {auth}') - raise aiohttp.web.HTTPForbidden() + auth = pathConfig[1] if request.method == 'POST' else pathConfig[2] + authHeader = request.headers.get('Authorization') + if not authHeader or not auth or authHeader != auth: + self.logger.info(f'Bad request {id(request)}: authentication failed: {authHeader!r} != {auth}') + raise aiohttp.web.HTTPForbidden() return (await func(request, *pathConfig)) - async def post(self, request, channel, auth, module, moduleargs, overlongmode): + async def post(self, request, channel, postauth, getauth, module, moduleargs, overlongmode): if module is not None: self.logger.debug(f'Processing request {id(request)} using {module!r}') try: @@ -1026,7 +1044,7 @@ class WebServer: raise aiohttp.web.HTTPBadRequest() return message - async def get(self, request, channel, auth, module, moduleargs, overlongmode): + async def get(self, request, channel, postauth, getauth, module, moduleargs, overlongmode): self.logger.info(f'Subscribing listener from request {id(request)} for {channel}') queue = self.irc2httpBroadcaster.subscribe(channel) response = aiohttp.web.StreamResponse() |
