diff --git a/example-config.yaml b/example-config.yaml index 5586aa8e..3c01c84e 100644 --- a/example-config.yaml +++ b/example-config.yaml @@ -75,8 +75,14 @@ metrics: # Manhole config. manhole: + # Whether or not opening the manhole is allowed. enabled: false + # The path for the unix socket. path: /var/tmp/mautrix-telegram.manhole + # The list of UIDs who can be added to the whitelist. + # If empty, any UIDs can be specified in the open-manhole command. + whitelist: + - 0 # Bridge config bridge: diff --git a/mautrix_telegram/commands/manhole.py b/mautrix_telegram/commands/manhole.py index 6b560da6..2c8f3abb 100644 --- a/mautrix_telegram/commands/manhole.py +++ b/mautrix_telegram/commands/manhole.py @@ -13,7 +13,7 @@ # # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . -from typing import Optional, Callable +from typing import Set, Callable import asyncio import sys import os @@ -33,20 +33,48 @@ from . import command_handler, CommandEvent, SECTION_ADMIN @dataclass class ManholeState: - server: Optional[asyncio.AbstractServer] = None - opened_by: Optional[UserID] = None - close: Optional[Callable[[], None]] = None + server: asyncio.AbstractServer + opened_by: UserID + close: Callable[[], None] + whitelist: Set[int] @command_handler(needs_auth=False, needs_admin=True, help_section=SECTION_ADMIN, - help_text="Open a manhole into the bridge.") -async def manhole(evt: CommandEvent) -> None: + help_text="Open a manhole into the bridge.", help_args="<_uid..._>") +async def open_manhole(evt: CommandEvent) -> None: if not evt.config["manhole.enabled"]: await evt.reply("The manhole has been disabled in the config.") return + elif len(evt.args) == 0: + await evt.reply("**Usage:** `$cmdprefix+sp open-manhole `") + return + + whitelist = set() + whitelist_whitelist = evt.config["manhole.whitelist"] + for arg in evt.args: + try: + uid = int(arg) + except ValueError: + await evt.reply(f"{arg} is not an integer.") + return + if whitelist_whitelist and uid not in whitelist_whitelist: + await evt.reply(f"{uid} is not in the list of allowed UIDs.") + return + whitelist.add(uid) if evt.bridge.manhole: - await evt.reply(f"There's an existing manhole opened by {evt.bridge.manhole.opened_by}") + added = [uid for uid in whitelist + if uid not in evt.bridge.manhole.whitelist] + evt.bridge.manhole.whitelist |= set(added) + if len(added) == 0: + await evt.reply(f"There's an existing manhole opened by {evt.bridge.manhole.opened_by}" + " and all the given UIDs are already whitelisted.") + else: + added_str = (f"{', '.join(str(uid) for uid in added[:-1])} and {added[-1]}" + if len(added) > 1 else added[0]) + await evt.reply(f"There's an existing manhole opened by {evt.bridge.manhole.opened_by}" + f". Added {added_str} to the whitelist.") + evt.log.info(f"{evt.sender.mxid} added {added_str} to the manhole whitelist.") return from ..portal import Portal @@ -63,10 +91,14 @@ async def manhole(evt: CommandEvent) -> None: f"and Telethon {__telethon_version__}\n\nManhole opened by {evt.sender.mxid}\n") path = evt.config["manhole.path"] - evt.log.info(f"{evt.sender.mxid} opened a manhole.") + wl_list = list(whitelist) + whitelist_str = (f"{', '.join(wl_list[:-1])} and {wl_list[-1]}" + if len(wl_list) > 1 else wl_list[0]) + evt.log.info(f"{evt.sender.mxid} opened a manhole with {whitelist_str} whitelisted.") server, close = await start_manhole(path=path, banner=banner, namespace=namespace, - loop=evt.loop) - evt.bridge.manhole = ManholeState(server=server, opened_by=evt.sender.mxid, close=close) + loop=evt.loop, whitelist=whitelist) + evt.bridge.manhole = ManholeState(server=server, opened_by=evt.sender.mxid, close=close, + whitelist=whitelist) await evt.reply(f"Opened manhole at unix://{path}") await server.wait_closed() evt.bridge.manhole = None diff --git a/mautrix_telegram/config.py b/mautrix_telegram/config.py index c59a6e1c..ded96573 100644 --- a/mautrix_telegram/config.py +++ b/mautrix_telegram/config.py @@ -76,6 +76,7 @@ class Config(BaseBridgeConfig): copy("manhole.enabled") copy("manhole.path") + copy("manhole.whitelist") copy("bridge.username_template") copy("bridge.alias_template") diff --git a/setup.py b/setup.py index 8fb78995..91b1d422 100644 --- a/setup.py +++ b/setup.py @@ -32,7 +32,7 @@ setuptools.setup( install_requires=[ "aiohttp>=3.0.1,<4", - "mautrix>=0.4.0.dev54,<0.5", + "mautrix>=0.4.0.dev55,<0.5", "SQLAlchemy>=1.2.3,<2", "alembic>=1.0.0,<2", "commonmark>=0.8.1,<1",