Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

appservice: Add {as,hs}_token_path config options #18071

Open
wants to merge 1 commit into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.d/18071.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add appservice config options `as_token_path` and `hs_token_path`.
48 changes: 41 additions & 7 deletions synapse/config/appservice.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,20 @@
from synapse.appservice import ApplicationService
from synapse.types import JsonDict, UserID

from ._base import Config, ConfigError
from ._base import Config, ConfigError, read_file

logger = logging.getLogger(__name__)

CONFLICTING_AS_TOKEN_OPTS_ERROR = """\
You have configured both `as_token` and `as_token_path`.
These are mutually incompatible.
"""

CONFLICTING_HS_TOKEN_OPTS_ERROR = """\
You have configured both `hs_token` and `hs_token_path`.
These are mutually incompatible.
"""


class AppServiceConfig(Config):
section = "appservice"
Expand Down Expand Up @@ -105,13 +115,37 @@ def load_appservices(
def _load_appservice(
hostname: str, as_info: JsonDict, config_filename: str
) -> ApplicationService:
required_string_fields = ["id", "as_token", "hs_token", "sender_localpart"]
for field in required_string_fields:
if not isinstance(as_info.get(field), str):
required_fields = ["id", "sender_localpart"]
for field in required_fields:
if field not in as_info:
raise KeyError("Required field: '%s' (%s)" % (field, config_filename))
string_fields = [
"id",
"sender_localpart",
"as_token",
"as_token_path",
"hs_token",
"hs_token_path",
]
for field in string_fields:
if not isinstance(as_info.get(field, ""), (str, type(None))):
raise KeyError(
"Required string field: '%s' (%s)" % (field, config_filename)
"Field must be a string: '%s' (%s)" % (field, config_filename)
)

if token_path := as_info.get("as_token_path"):
if as_info.get("as_token"):
raise ConfigError(CONFLICTING_AS_TOKEN_OPTS_ERROR)
token = read_file(token_path, ("as_token_path",)).strip()
else:
token = as_info["as_token"]

hs_token = as_info.get("hs_token")
if hs_token_path := as_info.get("hs_token_path"):
if "hs_token" in as_info:
raise ConfigError(CONFLICTING_HS_TOKEN_OPTS_ERROR)
hs_token = read_file(hs_token_path, ("hs_token_path",)).strip()

# 'url' must either be a string or explicitly null, not missing
# to avoid accidentally turning off push for ASes.
if not isinstance(as_info.get("url"), str) and as_info.get("url", "") is not None:
Expand Down Expand Up @@ -196,10 +230,10 @@ def _load_appservice(
)

return ApplicationService(
token=as_info["as_token"],
token=token,
url=as_info["url"],
namespaces=as_info["namespaces"],
hs_token=as_info["hs_token"],
hs_token=hs_token,
sender=user_id,
id=as_info["id"],
protocols=protocols,
Expand Down
37 changes: 37 additions & 0 deletions tests/config/test_load.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@

from synapse.config import ConfigError
from synapse.config._base import RootConfig
from synapse.config.appservice import _load_appservice
from synapse.config.homeserver import HomeServerConfig

from tests.config.utils import ConfigFileTestCase
Expand Down Expand Up @@ -179,3 +180,39 @@ def test_secret_files_existing(
config = HomeServerConfig.load_config("", ["-c", self.config_file])

self.assertEqual(get_secret(config), b"53C237")

def test_secret_files_appservice(self) -> None:
with tempfile.NamedTemporaryFile(buffering=0) as token_file:
token_file.write(b"53C237")

as_info = {
"id": "fake-as-id",
"url": "example.org",
"as_token_path": token_file.name,
"hs_token_path": token_file.name,
"sender_localpart": "not-real-part",
"namespaces": {},
}

appservice = _load_appservice(
hostname="example.org",
as_info=as_info,
config_filename="mock-config-file.name",
)

self.assertEqual(appservice.token, "53C237")
self.assertEqual(appservice.hs_token, "53C237")

with self.assertRaises(ConfigError):
_load_appservice(
hostname="example.org",
as_info={**as_info, "as_token_path": "/does/not/exist"},
config_filename="mock-config-file.name",
)

with self.assertRaises(ConfigError):
_load_appservice(
hostname="example.org",
as_info={**as_info, "hs_token_path": "/does/not/exist"},
config_filename="mock-config-file.name",
)
Loading