diff options
author | reivilibre <oliverw@matrix.org> | 2022-05-27 10:44:51 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-05-27 09:44:51 +0000 |
commit | 7b88f5a107ce9751365f9f2393521ef3d62afde8 (patch) | |
tree | b93a748112b6ea0c0dc038f9755f0ee0683de67f /synapse | |
parent | Improve URL previews by not including the content of media tags in the genera... (diff) | |
download | synapse-7b88f5a107ce9751365f9f2393521ef3d62afde8.tar.xz |
Add an option allowing users to use their password to reauthenticate even though password authentication is disabled. (#12883)
Diffstat (limited to 'synapse')
-rw-r--r-- | synapse/config/auth.py | 17 | ||||
-rw-r--r-- | synapse/handlers/auth.py | 29 |
2 files changed, 35 insertions, 11 deletions
diff --git a/synapse/config/auth.py b/synapse/config/auth.py index bb417a2359..265a554a5d 100644 --- a/synapse/config/auth.py +++ b/synapse/config/auth.py @@ -29,7 +29,18 @@ class AuthConfig(Config): if password_config is None: password_config = {} - self.password_enabled = password_config.get("enabled", True) + passwords_enabled = password_config.get("enabled", True) + # 'only_for_reauth' allows users who have previously set a password to use it, + # even though passwords would otherwise be disabled. + passwords_for_reauth_only = passwords_enabled == "only_for_reauth" + + self.password_enabled_for_login = ( + passwords_enabled and not passwords_for_reauth_only + ) + self.password_enabled_for_reauth = ( + passwords_for_reauth_only or passwords_enabled + ) + self.password_localdb_enabled = password_config.get("localdb_enabled", True) self.password_pepper = password_config.get("pepper", "") @@ -46,7 +57,9 @@ class AuthConfig(Config): def generate_config_section(self, **kwargs: Any) -> str: return """\ password_config: - # Uncomment to disable password login + # Uncomment to disable password login. + # Set to `only_for_reauth` to permit reauthentication for users that + # have passwords and are already logged in. # #enabled: false diff --git a/synapse/handlers/auth.py b/synapse/handlers/auth.py index 1b9050ea96..fbafbbee6b 100644 --- a/synapse/handlers/auth.py +++ b/synapse/handlers/auth.py @@ -210,7 +210,8 @@ class AuthHandler: self.hs = hs # FIXME better possibility to access registrationHandler later? self.macaroon_gen = hs.get_macaroon_generator() - self._password_enabled = hs.config.auth.password_enabled + self._password_enabled_for_login = hs.config.auth.password_enabled_for_login + self._password_enabled_for_reauth = hs.config.auth.password_enabled_for_reauth self._password_localdb_enabled = hs.config.auth.password_localdb_enabled self._third_party_rules = hs.get_third_party_event_rules() @@ -387,13 +388,13 @@ class AuthHandler: return params, session_id async def _get_available_ui_auth_types(self, user: UserID) -> Iterable[str]: - """Get a list of the authentication types this user can use""" + """Get a list of the user-interactive authentication types this user can use.""" ui_auth_types = set() # if the HS supports password auth, and the user has a non-null password, we # support password auth - if self._password_localdb_enabled and self._password_enabled: + if self._password_localdb_enabled and self._password_enabled_for_reauth: lookupres = await self._find_user_id_and_pwd_hash(user.to_string()) if lookupres: _, password_hash = lookupres @@ -402,7 +403,7 @@ class AuthHandler: # also allow auth from password providers for t in self.password_auth_provider.get_supported_login_types().keys(): - if t == LoginType.PASSWORD and not self._password_enabled: + if t == LoginType.PASSWORD and not self._password_enabled_for_reauth: continue ui_auth_types.add(t) @@ -710,7 +711,7 @@ class AuthHandler: return res # fall back to the v1 login flow - canonical_id, _ = await self.validate_login(authdict) + canonical_id, _ = await self.validate_login(authdict, is_reauth=True) return canonical_id def _get_params_recaptcha(self) -> dict: @@ -1064,7 +1065,7 @@ class AuthHandler: Returns: Whether users on this server are allowed to change or set a password """ - return self._password_enabled and self._password_localdb_enabled + return self._password_enabled_for_login and self._password_localdb_enabled def get_supported_login_types(self) -> Iterable[str]: """Get a the login types supported for the /login API @@ -1089,9 +1090,9 @@ class AuthHandler: # that comes first, where it's present. if LoginType.PASSWORD in types: types.remove(LoginType.PASSWORD) - if self._password_enabled: + if self._password_enabled_for_login: types.insert(0, LoginType.PASSWORD) - elif self._password_localdb_enabled and self._password_enabled: + elif self._password_localdb_enabled and self._password_enabled_for_login: types.insert(0, LoginType.PASSWORD) return types @@ -1100,6 +1101,7 @@ class AuthHandler: self, login_submission: Dict[str, Any], ratelimit: bool = False, + is_reauth: bool = False, ) -> Tuple[str, Optional[Callable[["LoginResponse"], Awaitable[None]]]]: """Authenticates the user for the /login API @@ -1110,6 +1112,9 @@ class AuthHandler: login_submission: the whole of the login submission (including 'type' and other relevant fields) ratelimit: whether to apply the failed_login_attempt ratelimiter + is_reauth: whether this is part of a User-Interactive Authorisation + flow to reauthenticate for a privileged action (rather than a + new login) Returns: A tuple of the canonical user id, and optional callback to be called once the access token and device id are issued @@ -1132,8 +1137,14 @@ class AuthHandler: # special case to check for "password" for the check_password interface # for the auth providers password = login_submission.get("password") + if login_type == LoginType.PASSWORD: - if not self._password_enabled: + if is_reauth: + passwords_allowed_here = self._password_enabled_for_reauth + else: + passwords_allowed_here = self._password_enabled_for_login + + if not passwords_allowed_here: raise SynapseError(400, "Password login has been disabled.") if not isinstance(password, str): raise SynapseError(400, "Bad parameter: password", Codes.INVALID_PARAM) |