diff --git a/changelog.d/18262.feature b/changelog.d/18262.feature new file mode 100644 index 00000000000..c8249faa762 --- /dev/null +++ b/changelog.d/18262.feature @@ -0,0 +1 @@ +Add option to allow registrations that begin with `_`. Contributed by `_` (@hex5f). diff --git a/docs/usage/configuration/config_documentation.md b/docs/usage/configuration/config_documentation.md index d2d282f2037..65b6d737550 100644 --- a/docs/usage/configuration/config_documentation.md +++ b/docs/usage/configuration/config_documentation.md @@ -2887,6 +2887,20 @@ Example configuration: inhibit_user_in_use_error: true ``` --- +### `allow_underscore_prefixed_registration` + +Whether users are allowed to register with a underscore-prefixed localpart. +By default, AppServices use prefixes like `_example` to namespace their +associated ghost users. If turned on, this may result in clashes or confusion. +Useful when provisioning users from an external identity provider. + +Defaults to false. + +Example configuration: +```yaml +allow_underscore_prefixed_registration: false +``` +--- ## User session management --- ### `session_lifetime` diff --git a/synapse/config/registration.py b/synapse/config/registration.py index 3cf70316560..8adf21079ef 100644 --- a/synapse/config/registration.py +++ b/synapse/config/registration.py @@ -162,6 +162,10 @@ def read_config( "disable_msisdn_registration", False ) + self.allow_underscore_prefixed_localpart = config.get( + "allow_underscore_prefixed_localpart", False + ) + session_lifetime = config.get("session_lifetime") if session_lifetime is not None: session_lifetime = self.parse_duration(session_lifetime) diff --git a/synapse/handlers/register.py b/synapse/handlers/register.py index ecfea175c75..3e863499819 100644 --- a/synapse/handlers/register.py +++ b/synapse/handlers/register.py @@ -159,7 +159,10 @@ async def check_username( if not localpart: raise SynapseError(400, "User ID cannot be empty", Codes.INVALID_USERNAME) - if localpart[0] == "_": + if ( + localpart[0] == "_" + and not self.hs.config.registration.allow_underscore_prefixed_localpart + ): raise SynapseError( 400, "User ID may not begin with _", Codes.INVALID_USERNAME ) diff --git a/tests/handlers/test_register.py b/tests/handlers/test_register.py index 92487692dba..39705d5e664 100644 --- a/tests/handlers/test_register.py +++ b/tests/handlers/test_register.py @@ -588,6 +588,29 @@ def test_register_not_support_user(self) -> None: d = self.store.is_support_user(user_id) self.assertFalse(self.get_success(d)) + def test_underscore_localpart_rejected_by_default(self) -> None: + for invalid_user_id in ("_", "_prefixed"): + with self.subTest(invalid_user_id=invalid_user_id): + self.get_failure( + self.handler.register_user(localpart=invalid_user_id), + SynapseError, + ) + + @override_config( + { + "allow_underscore_prefixed_localpart": "true", + } + ) + def test_underscore_localpart_allowed_if_configured(self) -> None: + for valid_user_id in ("_", "_prefixed"): + with self.subTest(valid_user_id=valid_user_id): + user_id = self.get_success( + self.handler.register_user( + localpart=valid_user_id, + ), + ) + self.assertEqual(user_id, f"@{valid_user_id}:test") + def test_invalid_user_id(self) -> None: invalid_user_id = "^abcd" self.get_failure(