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",