Handle "registration_enabled" parameter for CAS (#16262)

Similar to OIDC, CAS providers can now disable registration such
that only existing users are able to login via SSO.
This commit is contained in:
Aurélien Grimpard 2023-09-06 20:32:24 +02:00 committed by GitHub
parent 32fb264120
commit fe69e7f617
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 30 additions and 0 deletions

View file

@ -0,0 +1 @@
Add the ability to enable/disable registrations when in the CAS flow. Contributed by Aurélien Grimpard.

View file

@ -3430,6 +3430,12 @@ Has the following sub-options:
and the values must match the given value. Alternately if the given value and the values must match the given value. Alternately if the given value
is `None` then any value is allowed (the attribute just must exist). is `None` then any value is allowed (the attribute just must exist).
All of the listed attributes must match for the login to be permitted. All of the listed attributes must match for the login to be permitted.
* `enable_registration`: set to 'false' to disable automatic registration of new
users. This allows the CAS SSO flow to be limited to sign in only, rather than
automatically registering users that have a valid SSO login but do not have
a pre-registered account. Defaults to true.
*Added in Synapse 1.93.0.*
Example configuration: Example configuration:
```yaml ```yaml
@ -3441,6 +3447,7 @@ cas_config:
required_attributes: required_attributes:
userGroup: "staff" userGroup: "staff"
department: None department: None
enable_registration: true
``` ```
--- ---
### `sso` ### `sso`

View file

@ -57,6 +57,8 @@ class CasConfig(Config):
required_attributes required_attributes
) )
self.cas_enable_registration = cas_config.get("enable_registration", True)
self.idp_name = cas_config.get("idp_name", "CAS") self.idp_name = cas_config.get("idp_name", "CAS")
self.idp_icon = cas_config.get("idp_icon") self.idp_icon = cas_config.get("idp_icon")
self.idp_brand = cas_config.get("idp_brand") self.idp_brand = cas_config.get("idp_brand")
@ -67,6 +69,7 @@ class CasConfig(Config):
self.cas_protocol_version = None self.cas_protocol_version = None
self.cas_displayname_attribute = None self.cas_displayname_attribute = None
self.cas_required_attributes = [] self.cas_required_attributes = []
self.cas_enable_registration = False
# CAS uses a legacy required attributes mapping, not the one provided by # CAS uses a legacy required attributes mapping, not the one provided by

View file

@ -70,6 +70,7 @@ class CasHandler:
self._cas_protocol_version = hs.config.cas.cas_protocol_version self._cas_protocol_version = hs.config.cas.cas_protocol_version
self._cas_displayname_attribute = hs.config.cas.cas_displayname_attribute self._cas_displayname_attribute = hs.config.cas.cas_displayname_attribute
self._cas_required_attributes = hs.config.cas.cas_required_attributes self._cas_required_attributes = hs.config.cas.cas_required_attributes
self._cas_enable_registration = hs.config.cas.cas_enable_registration
self._http_client = hs.get_proxied_http_client() self._http_client = hs.get_proxied_http_client()
@ -395,4 +396,5 @@ class CasHandler:
client_redirect_url, client_redirect_url,
cas_response_to_user_attributes, cas_response_to_user_attributes,
grandfather_existing_users, grandfather_existing_users,
registration_enabled=self._cas_enable_registration,
) )

View file

@ -197,6 +197,23 @@ class CasHandlerTestCase(HomeserverTestCase):
auth_provider_session_id=None, auth_provider_session_id=None,
) )
@override_config({"cas_config": {"enable_registration": False}})
def test_map_cas_user_does_not_register_new_user(self) -> None:
"""Ensures new users are not registered if the enabled registration flag is disabled."""
# stub out the auth handler
auth_handler = self.hs.get_auth_handler()
auth_handler.complete_sso_login = AsyncMock() # type: ignore[method-assign]
cas_response = CasResponse("test_user", {})
request = _mock_request()
self.get_success(
self.handler._handle_cas_response(request, cas_response, "redirect_uri", "")
)
# check that the auth handler was not called as expected
auth_handler.complete_sso_login.assert_not_called()
def _mock_request() -> Mock: def _mock_request() -> Mock:
"""Returns a mock which will stand in as a SynapseRequest""" """Returns a mock which will stand in as a SynapseRequest"""