1515
1616import logging
1717import xml .etree .ElementTree as ET
18- from typing import AnyStr , Dict , Optional , Tuple
18+ from typing import Dict , Optional , Tuple
1919
2020from six .moves import urllib
2121
@@ -48,26 +48,47 @@ def __init__(self, hs):
4848
4949 self ._http_client = hs .get_proxied_http_client ()
5050
51- def _build_service_param (self , client_redirect_url : AnyStr ) -> str :
51+ def _build_service_param (self , args : Dict [str , str ]) -> str :
52+ """
53+ Generates a value to use as the "service" parameter when redirecting or
54+ querying the CAS service.
55+
56+ Args:
57+ args: Additional arguments to include in the final redirect URL.
58+
59+ Returns:
60+ The URL to use as a "service" parameter.
61+ """
5262 return "%s%s?%s" % (
5363 self ._cas_service_url ,
5464 "/_matrix/client/r0/login/cas/ticket" ,
55- urllib .parse .urlencode ({ "redirectUrl" : client_redirect_url } ),
65+ urllib .parse .urlencode (args ),
5666 )
5767
58- async def _handle_cas_response (
59- self , request : SynapseRequest , cas_response_body : str , client_redirect_url : str
60- ) -> None :
68+ async def _validate_ticket (
69+ self , ticket : str , service_args : Dict [ str , str ]
70+ ) -> Tuple [ str , Optional [ str ]] :
6171 """
62- Retrieves the user and display name from the CAS response and continues with the authentication .
72+ Validate a CAS ticket with the server, parse the response, and return the user and display name .
6373
6474 Args:
65- request: The original client request.
66- cas_response_body: The response from the CAS server.
67- client_redirect_url: The URl to redirect the client to when
68- everything is done.
75+ ticket: The CAS ticket from the client.
76+ service_args: Additional arguments to include in the service URL.
77+ Should be the same as those passed to `get_redirect_url`.
6978 """
70- user , attributes = self ._parse_cas_response (cas_response_body )
79+ uri = self ._cas_server_url + "/proxyValidate"
80+ args = {
81+ "ticket" : ticket ,
82+ "service" : self ._build_service_param (service_args ),
83+ }
84+ try :
85+ body = await self ._http_client .get_raw (uri , args )
86+ except PartialDownloadError as pde :
87+ # Twisted raises this error if the connection is closed,
88+ # even if that's being used old-http style to signal end-of-data
89+ body = pde .response
90+
91+ user , attributes = self ._parse_cas_response (body )
7192 displayname = attributes .pop (self ._cas_displayname_attribute , None )
7293
7394 for required_attribute , required_value in self ._cas_required_attributes .items ():
@@ -82,7 +103,7 @@ async def _handle_cas_response(
82103 if required_value != actual_value :
83104 raise LoginError (401 , "Unauthorized" , errcode = Codes .UNAUTHORIZED )
84105
85- await self . _on_successful_auth ( user , request , client_redirect_url , displayname )
106+ return user , displayname
86107
87108 def _parse_cas_response (
88109 self , cas_response_body : str
@@ -127,78 +148,74 @@ def _parse_cas_response(
127148 )
128149 return user , attributes
129150
130- async def _on_successful_auth (
131- self ,
132- username : str ,
133- request : SynapseRequest ,
134- client_redirect_url : str ,
135- user_display_name : Optional [str ] = None ,
136- ) -> None :
137- """Called once the user has successfully authenticated with the SSO.
138-
139- Registers the user if necessary, and then returns a redirect (with
140- a login token) to the client.
151+ def get_redirect_url (self , service_args : Dict [str , str ]) -> str :
152+ """
153+ Generates a URL for the CAS server where the client should be redirected.
141154
142155 Args:
143- username: the remote user id. We'll map this onto
144- something sane for a MXID localpath.
156+ service_args: Additional arguments to include in the final redirect URL.
145157
146- request: the incoming request from the browser. We'll
147- respond to it with a redirect.
158+ Returns:
159+ The URL to redirect the client to.
160+ """
161+ args = urllib .parse .urlencode (
162+ {"service" : self ._build_service_param (service_args )}
163+ )
148164
149- client_redirect_url: the redirect_url the client gave us when
150- it first started the process.
165+ return "%s/login?%s" % (self ._cas_server_url , args )
151166
152- user_display_name: if set, and we have to register a new user,
153- we will set their displayname to this.
167+ async def handle_ticket (
168+ self ,
169+ request : SynapseRequest ,
170+ ticket : str ,
171+ client_redirect_url : Optional [str ],
172+ session : Optional [str ],
173+ ) -> None :
154174 """
155- localpart = map_username_to_mxid_localpart (username )
156- user_id = UserID (localpart , self ._hostname ).to_string ()
157- registered_user_id = await self ._auth_handler .check_user_exists (user_id )
158- if not registered_user_id :
159- registered_user_id = await self ._registration_handler .register_user (
160- localpart = localpart , default_display_name = user_display_name
161- )
175+ Called once the user has successfully authenticated with the SSO.
176+ Validates a CAS ticket sent by the client and completes the auth process.
162177
163- self . _auth_handler . complete_sso_login (
164- registered_user_id , request , client_redirect_url
165- )
178+ If the user interactive authentication session is provided, marks the
179+ UI Auth session as complete, then returns an HTML page notifying the
180+ user they are done.
166181
167- def handle_redirect_request (self , client_redirect_url : bytes ) -> bytes :
168- """
169- Generates a URL to the CAS server where the client should be redirected.
182+ Otherwise, this registers the user if necessary, and then returns a
183+ redirect (with a login token) to the client.
170184
171185 Args:
172- client_redirect_url: The final URL the client should go to after the
173- user has negotiated SSO .
186+ request: the incoming request from the browser. We'll
187+ respond to it with a redirect or an HTML page .
174188
175- Returns:
176- The URL to redirect to.
177- """
178- args = urllib .parse .urlencode (
179- {"service" : self ._build_service_param (client_redirect_url )}
180- )
189+ ticket: The CAS ticket provided by the client.
181190
182- return ("%s/login?%s" % (self ._cas_server_url , args )).encode ("ascii" )
191+ client_redirect_url: the redirectUrl parameter from the `/cas/ticket` HTTP request, if given.
192+ This should be the same as the redirectUrl from the original `/login/sso/redirect` request.
183193
184- async def handle_ticket_request (
185- self , request : SynapseRequest , client_redirect_url : str , ticket : str
186- ) -> None :
194+ session: The session parameter from the `/cas/ticket` HTTP request, if given.
195+ This should be the UI Auth session id.
187196 """
188- Validates a CAS ticket sent by the client for login/registration.
197+ args = {}
198+ if client_redirect_url :
199+ args ["redirectUrl" ] = client_redirect_url
200+ if session :
201+ args ["session" ] = session
202+ username , user_display_name = await self ._validate_ticket (ticket , args )
189203
190- On a successful request, writes a redirect to the request.
191- """
192- uri = self ._cas_server_url + "/proxyValidate"
193- args = {
194- "ticket" : ticket ,
195- "service" : self ._build_service_param (client_redirect_url ),
196- }
197- try :
198- body = await self ._http_client .get_raw (uri , args )
199- except PartialDownloadError as pde :
200- # Twisted raises this error if the connection is closed,
201- # even if that's being used old-http style to signal end-of-data
202- body = pde .response
204+ localpart = map_username_to_mxid_localpart (username )
205+ user_id = UserID (localpart , self ._hostname ).to_string ()
206+ registered_user_id = await self ._auth_handler .check_user_exists (user_id )
203207
204- await self ._handle_cas_response (request , body , client_redirect_url )
208+ if session :
209+ self ._auth_handler .complete_sso_ui_auth (
210+ registered_user_id , session , request ,
211+ )
212+
213+ else :
214+ if not registered_user_id :
215+ registered_user_id = await self ._registration_handler .register_user (
216+ localpart = localpart , default_display_name = user_display_name
217+ )
218+
219+ self ._auth_handler .complete_sso_login (
220+ registered_user_id , request , client_redirect_url
221+ )
0 commit comments