1818from http import HTTPStatus
1919
2020from synapse .api .constants import LoginType
21- from synapse .api .errors import Codes , SynapseError , ThreepidValidationError
21+ from synapse .api .errors import (
22+ Codes ,
23+ InteractiveAuthIncompleteError ,
24+ SynapseError ,
25+ ThreepidValidationError ,
26+ )
2227from synapse .config .emailconfig import ThreepidBehaviour
2328from synapse .http .server import finish_request , respond_with_html
2429from synapse .http .servlet import (
@@ -239,18 +244,12 @@ async def on_POST(self, request):
239244
240245 # we do basic sanity checks here because the auth layer will store these
241246 # in sessions. Pull out the new password provided to us.
242- if "new_password" in body :
243- new_password = body . pop ( "new_password" )
247+ new_password = body . pop ( "new_password" , None )
248+ if new_password is not None :
244249 if not isinstance (new_password , str ) or len (new_password ) > 512 :
245250 raise SynapseError (400 , "Invalid password" )
246251 self .password_policy_handler .validate_password (new_password )
247252
248- # If the password is valid, hash it and store it back on the body.
249- # This ensures that only the hashed password is handled everywhere.
250- if "new_password_hash" in body :
251- raise SynapseError (400 , "Unexpected property: new_password_hash" )
252- body ["new_password_hash" ] = await self .auth_handler .hash (new_password )
253-
254253 # there are two possibilities here. Either the user does not have an
255254 # access token, and needs to do a password reset; or they have one and
256255 # need to validate their identity.
@@ -263,23 +262,49 @@ async def on_POST(self, request):
263262
264263 if self .auth .has_access_token (request ):
265264 requester = await self .auth .get_user_by_req (request )
266- params = await self .auth_handler .validate_user_via_ui_auth (
267- requester ,
268- request ,
269- body ,
270- self .hs .get_ip_from_request (request ),
271- "modify your account password" ,
272- )
265+ try :
266+ params , session_id = await self .auth_handler .validate_user_via_ui_auth (
267+ requester ,
268+ request ,
269+ body ,
270+ self .hs .get_ip_from_request (request ),
271+ "modify your account password" ,
272+ )
273+ except InteractiveAuthIncompleteError as e :
274+ # The user needs to provide more steps to complete auth, but
275+ # they're not required to provide the password again.
276+ #
277+ # If a password is available now, hash the provided password and
278+ # store it for later.
279+ if new_password :
280+ password_hash = await self .auth_handler .hash (new_password )
281+ await self .auth_handler .set_session_data (
282+ e .session_id , "password_hash" , password_hash
283+ )
284+ raise
273285 user_id = requester .user .to_string ()
274286 else :
275287 requester = None
276- result , params , _ = await self .auth_handler .check_auth (
277- [[LoginType .EMAIL_IDENTITY ]],
278- request ,
279- body ,
280- self .hs .get_ip_from_request (request ),
281- "modify your account password" ,
282- )
288+ try :
289+ result , params , session_id = await self .auth_handler .check_ui_auth (
290+ [[LoginType .EMAIL_IDENTITY ]],
291+ request ,
292+ body ,
293+ self .hs .get_ip_from_request (request ),
294+ "modify your account password" ,
295+ )
296+ except InteractiveAuthIncompleteError as e :
297+ # The user needs to provide more steps to complete auth, but
298+ # they're not required to provide the password again.
299+ #
300+ # If a password is available now, hash the provided password and
301+ # store it for later.
302+ if new_password :
303+ password_hash = await self .auth_handler .hash (new_password )
304+ await self .auth_handler .set_session_data (
305+ e .session_id , "password_hash" , password_hash
306+ )
307+ raise
283308
284309 if LoginType .EMAIL_IDENTITY in result :
285310 threepid = result [LoginType .EMAIL_IDENTITY ]
@@ -304,12 +329,21 @@ async def on_POST(self, request):
304329 logger .error ("Auth succeeded but no known type! %r" , result .keys ())
305330 raise SynapseError (500 , "" , Codes .UNKNOWN )
306331
307- assert_params_in_dict (params , ["new_password_hash" ])
308- new_password_hash = params ["new_password_hash" ]
332+ # If we have a password in this request, prefer it. Otherwise, there
333+ # must be a password hash from an earlier request.
334+ if new_password :
335+ password_hash = await self .auth_handler .hash (new_password )
336+ else :
337+ password_hash = await self .auth_handler .get_session_data (
338+ session_id , "password_hash" , None
339+ )
340+ if not password_hash :
341+ raise SynapseError (400 , "Missing params: password" , Codes .MISSING_PARAM )
342+
309343 logout_devices = params .get ("logout_devices" , True )
310344
311345 await self ._set_password_handler .set_password (
312- user_id , new_password_hash , logout_devices , requester
346+ user_id , password_hash , logout_devices , requester
313347 )
314348
315349 return 200 , {}
0 commit comments