From d9e8f1a43a4cb0cea61d96bf6e78cd8e32cb1e1d Mon Sep 17 00:00:00 2001 From: "Kai A. Hiller" Date: Mon, 2 Dec 2024 16:27:20 +0100 Subject: [PATCH] Add recaptcha_{private,public}_key_path config opt --- changelog.d/17984.feature | 1 + .../configuration/config_documentation.md | 32 +++++++++++++++++++ synapse/config/captcha.py | 26 ++++++++++++++- tests/config/test_load.py | 10 ++++++ 4 files changed, 68 insertions(+), 1 deletion(-) create mode 100644 changelog.d/17984.feature diff --git a/changelog.d/17984.feature b/changelog.d/17984.feature new file mode 100644 index 0000000000..609f3a7cd8 --- /dev/null +++ b/changelog.d/17984.feature @@ -0,0 +1 @@ +Add `recaptcha_private_key_path` and `recaptcha_public_key_path` config option. \ No newline at end of file diff --git a/docs/usage/configuration/config_documentation.md b/docs/usage/configuration/config_documentation.md index 7a48d76bbb..c88f068236 100644 --- a/docs/usage/configuration/config_documentation.md +++ b/docs/usage/configuration/config_documentation.md @@ -2293,6 +2293,22 @@ Example configuration: ```yaml recaptcha_public_key: "YOUR_PUBLIC_KEY" ``` +--- +### `recaptcha_public_key_path` + +An alternative to [`recaptcha_public_key`](#recaptcha_public_key): +allows the public key to be specified in an external file. + +The file should be a plain text file, containing only the public key. +Synapse reads the public key from the given file once at startup. + +Example configuration: +```yaml +recaptcha_public_key_path: /path/to/key/file +``` + +_Added in Synapse 1.121.0._ + --- ### `recaptcha_private_key` @@ -2304,6 +2320,22 @@ Example configuration: ```yaml recaptcha_private_key: "YOUR_PRIVATE_KEY" ``` +--- +### `recaptcha_private_key_path` + +An alternative to [`recaptcha_private_key`](#recaptcha_private_key): +allows the private key to be specified in an external file. + +The file should be a plain text file, containing only the private key. +Synapse reads the private key from the given file once at startup. + +Example configuration: +```yaml +recaptcha_private_key_path: /path/to/key/file +``` + +_Added in Synapse 1.121.0._ + --- ### `enable_registration_captcha` diff --git a/synapse/config/captcha.py b/synapse/config/captcha.py index 84897c09c5..28b93a8458 100644 --- a/synapse/config/captcha.py +++ b/synapse/config/captcha.py @@ -23,7 +23,17 @@ from typing import Any from synapse.types import JsonDict -from ._base import Config, ConfigError +from ._base import Config, ConfigError, read_file + +CONFLICTING_RECAPTCHA_PRIVATE_KEY_OPTS_ERROR = """\ +You have configured both `recaptcha_private_key` and +`recaptcha_private_key_path`. These are mutually incompatible. +""" + +CONFLICTING_RECAPTCHA_PUBLIC_KEY_OPTS_ERROR = """\ +You have configured both `recaptcha_public_key` and `recaptcha_public_key_path`. +These are mutually incompatible. +""" class CaptchaConfig(Config): @@ -31,6 +41,13 @@ class CaptchaConfig(Config): def read_config(self, config: JsonDict, **kwargs: Any) -> None: recaptcha_private_key = config.get("recaptcha_private_key") + recaptcha_private_key_path = config.get("recaptcha_private_key_path") + if recaptcha_private_key_path: + if recaptcha_private_key: + raise ConfigError(CONFLICTING_RECAPTCHA_PRIVATE_KEY_OPTS_ERROR) + recaptcha_private_key = read_file( + recaptcha_private_key_path, ("recaptcha_private_key_path",) + ).strip() if recaptcha_private_key is not None and not isinstance( recaptcha_private_key, str ): @@ -38,6 +55,13 @@ class CaptchaConfig(Config): self.recaptcha_private_key = recaptcha_private_key recaptcha_public_key = config.get("recaptcha_public_key") + recaptcha_public_key_path = config.get("recaptcha_public_key_path") + if recaptcha_public_key_path: + if recaptcha_public_key: + raise ConfigError(CONFLICTING_RECAPTCHA_PUBLIC_KEY_OPTS_ERROR) + recaptcha_public_key = read_file( + recaptcha_public_key_path, ("recaptcha_public_key_path",) + ).strip() if recaptcha_public_key is not None and not isinstance( recaptcha_public_key, str ): diff --git a/tests/config/test_load.py b/tests/config/test_load.py index c5dee06af5..82b63b61c6 100644 --- a/tests/config/test_load.py +++ b/tests/config/test_load.py @@ -131,6 +131,8 @@ class ConfigLoadingFileTestCase(ConfigFileTestCase): [ "turn_shared_secret_path: /does/not/exist", "registration_shared_secret_path: /does/not/exist", + "recaptcha_private_key_path: /does/not/exist", + "recaptcha_public_key_path: /does/not/exist", *["redis:\n enabled: true\n password_path: /does/not/exist"] * (hiredis is not None), ] @@ -152,6 +154,14 @@ class ConfigLoadingFileTestCase(ConfigFileTestCase): "registration_shared_secret_path: {}", lambda c: c.registration.registration_shared_secret, ), + ( + "recaptcha_private_key_path: {}", + lambda c: c.captcha.recaptcha_private_key, + ), + ( + "recaptcha_public_key_path: {}", + lambda c: c.captcha.recaptcha_public_key, + ), *[ ( "redis:\n enabled: true\n password_path: {}",