Skip to content
This repository has been archived by the owner on Apr 26, 2024. It is now read-only.

Commit

Permalink
Handle "registration_enabled" parameter for CAS (#16262)
Browse files Browse the repository at this point in the history
Similar to OIDC, CAS providers can now disable registration such
that only existing users are able to login via SSO.
  • Loading branch information
agrimpard authored Sep 6, 2023
1 parent 32fb264 commit fe69e7f
Show file tree
Hide file tree
Showing 5 changed files with 30 additions and 0 deletions.
1 change: 1 addition & 0 deletions changelog.d/16262.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add the ability to enable/disable registrations when in the CAS flow. Contributed by Aurélien Grimpard.
7 changes: 7 additions & 0 deletions docs/usage/configuration/config_documentation.md
Original file line number Diff line number Diff line change
Expand Up @@ -3430,6 +3430,12 @@ Has the following sub-options:
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).
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:
```yaml
Expand All @@ -3441,6 +3447,7 @@ cas_config:
required_attributes:
userGroup: "staff"
department: None
enable_registration: true
```
---
### `sso`
Expand Down
3 changes: 3 additions & 0 deletions synapse/config/cas.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ def read_config(self, config: JsonDict, **kwargs: Any) -> None:
required_attributes
)

self.cas_enable_registration = cas_config.get("enable_registration", True)

self.idp_name = cas_config.get("idp_name", "CAS")
self.idp_icon = cas_config.get("idp_icon")
self.idp_brand = cas_config.get("idp_brand")
Expand All @@ -67,6 +69,7 @@ def read_config(self, config: JsonDict, **kwargs: Any) -> None:
self.cas_protocol_version = None
self.cas_displayname_attribute = None
self.cas_required_attributes = []
self.cas_enable_registration = False


# CAS uses a legacy required attributes mapping, not the one provided by
Expand Down
2 changes: 2 additions & 0 deletions synapse/handlers/cas.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ def __init__(self, hs: "HomeServer"):
self._cas_protocol_version = hs.config.cas.cas_protocol_version
self._cas_displayname_attribute = hs.config.cas.cas_displayname_attribute
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()

Expand Down Expand Up @@ -395,4 +396,5 @@ async def grandfather_existing_users() -> Optional[str]:
client_redirect_url,
cas_response_to_user_attributes,
grandfather_existing_users,
registration_enabled=self._cas_enable_registration,
)
17 changes: 17 additions & 0 deletions tests/handlers/test_cas.py
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,23 @@ def test_required_attributes(self) -> 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:
"""Returns a mock which will stand in as a SynapseRequest"""
Expand Down

0 comments on commit fe69e7f

Please sign in to comment.