mirror of
https://git.anonymousland.org/anonymousland/synapse-product.git
synced 2025-07-31 18:18:34 -04:00
Combine the SSO Redirect Servlets (#9015)
* Implement CasHandler.handle_redirect_request ... to make it match OidcHandler and SamlHandler * Clean up interface for OidcHandler.handle_redirect_request Make it accept `client_redirect_url=None`. * Clean up interface for `SamlHandler.handle_redirect_request` ... bring it into line with CAS and OIDC by making it take a Request parameter, move the magic for `client_redirect_url` for UIA into the handler, and fix the return type to be a `str` rather than a `bytes`. * Define a common protocol for SSO auth provider impls * Give SsoIdentityProvider an ID and register them * Combine the SSO Redirect servlets Now that the SsoHandler knows about the identity providers, we can combine the various *RedirectServlets into a single implementation which delegates to the right IdP. * changelog
This commit is contained in:
parent
31b1905e13
commit
d2c616a413
8 changed files with 174 additions and 113 deletions
|
@ -12,15 +12,16 @@
|
|||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
import abc
|
||||
import logging
|
||||
from typing import TYPE_CHECKING, Awaitable, Callable, Dict, List, Optional
|
||||
|
||||
import attr
|
||||
from typing_extensions import NoReturn
|
||||
from typing_extensions import NoReturn, Protocol
|
||||
|
||||
from twisted.web.http import Request
|
||||
|
||||
from synapse.api.errors import RedirectException, SynapseError
|
||||
from synapse.api.errors import Codes, RedirectException, SynapseError
|
||||
from synapse.http.server import respond_with_html
|
||||
from synapse.http.site import SynapseRequest
|
||||
from synapse.types import JsonDict, UserID, contains_invalid_mxid_characters
|
||||
|
@ -40,6 +41,53 @@ class MappingException(Exception):
|
|||
"""
|
||||
|
||||
|
||||
class SsoIdentityProvider(Protocol):
|
||||
"""Abstract base class to be implemented by SSO Identity Providers
|
||||
|
||||
An Identity Provider, or IdP, is an external HTTP service which authenticates a user
|
||||
to say whether they should be allowed to log in, or perform a given action.
|
||||
|
||||
Synapse supports various implementations of IdPs, including OpenID Connect, SAML,
|
||||
and CAS.
|
||||
|
||||
The main entry point is `handle_redirect_request`, which should return a URI to
|
||||
redirect the user's browser to the IdP's authentication page.
|
||||
|
||||
Each IdP should be registered with the SsoHandler via
|
||||
`hs.get_sso_handler().register_identity_provider()`, so that requests to
|
||||
`/_matrix/client/r0/login/sso/redirect` can be correctly dispatched.
|
||||
"""
|
||||
|
||||
@property
|
||||
@abc.abstractmethod
|
||||
def idp_id(self) -> str:
|
||||
"""A unique identifier for this SSO provider
|
||||
|
||||
Eg, "saml", "cas", "github"
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
async def handle_redirect_request(
|
||||
self,
|
||||
request: SynapseRequest,
|
||||
client_redirect_url: Optional[bytes],
|
||||
ui_auth_session_id: Optional[str] = None,
|
||||
) -> str:
|
||||
"""Handle an incoming request to /login/sso/redirect
|
||||
|
||||
Args:
|
||||
request: the incoming HTTP request
|
||||
client_redirect_url: the URL that we should redirect the
|
||||
client to after login (or None for UI Auth).
|
||||
ui_auth_session_id: The session ID of the ongoing UI Auth (or
|
||||
None if this is a login).
|
||||
|
||||
Returns:
|
||||
URL to redirect to
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
@attr.s
|
||||
class UserAttributes:
|
||||
# the localpart of the mxid that the mapper has assigned to the user.
|
||||
|
@ -100,6 +148,14 @@ class SsoHandler:
|
|||
# a map from session id to session data
|
||||
self._username_mapping_sessions = {} # type: Dict[str, UsernameMappingSession]
|
||||
|
||||
# map from idp_id to SsoIdentityProvider
|
||||
self._identity_providers = {} # type: Dict[str, SsoIdentityProvider]
|
||||
|
||||
def register_identity_provider(self, p: SsoIdentityProvider):
|
||||
p_id = p.idp_id
|
||||
assert p_id not in self._identity_providers
|
||||
self._identity_providers[p_id] = p
|
||||
|
||||
def render_error(
|
||||
self,
|
||||
request: Request,
|
||||
|
@ -124,6 +180,32 @@ class SsoHandler:
|
|||
)
|
||||
respond_with_html(request, code, html)
|
||||
|
||||
async def handle_redirect_request(
|
||||
self, request: SynapseRequest, client_redirect_url: bytes,
|
||||
) -> str:
|
||||
"""Handle a request to /login/sso/redirect
|
||||
|
||||
Args:
|
||||
request: incoming HTTP request
|
||||
client_redirect_url: the URL that we should redirect the
|
||||
client to after login.
|
||||
|
||||
Returns:
|
||||
the URI to redirect to
|
||||
"""
|
||||
if not self._identity_providers:
|
||||
raise SynapseError(
|
||||
400, "Homeserver not configured for SSO.", errcode=Codes.UNRECOGNIZED
|
||||
)
|
||||
|
||||
# if we only have one auth provider, redirect to it directly
|
||||
if len(self._identity_providers) == 1:
|
||||
ap = next(iter(self._identity_providers.values()))
|
||||
return await ap.handle_redirect_request(request, client_redirect_url)
|
||||
|
||||
# otherwise, we have a configuration error
|
||||
raise Exception("Multiple SSO identity providers have been configured!")
|
||||
|
||||
async def get_sso_user_by_remote_user_id(
|
||||
self, auth_provider_id: str, remote_user_id: str
|
||||
) -> Optional[str]:
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue