mirror of https://github.com/vector-im/riot-web
				
				
				
			Fix logout devices on password reset (#9925)
							parent
							
								
									62913218d2
								
							
						
					
					
						commit
						70d3d03c15
					
				|  | @ -104,6 +104,10 @@ export default class PasswordReset { | |||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     public setLogoutDevices(logoutDevices: boolean): void { | ||||
|         this.logoutDevices = logoutDevices; | ||||
|     } | ||||
| 
 | ||||
|     public async setNewPassword(password: string): Promise<void> { | ||||
|         this.password = password; | ||||
|         await this.checkEmailLinkClicked(); | ||||
|  |  | |||
|  | @ -258,6 +258,7 @@ export default class ForgotPassword extends React.Component<Props, State> { | |||
|         } | ||||
| 
 | ||||
|         this.phase = Phase.ResettingPassword; | ||||
|         this.reset.setLogoutDevices(this.state.logoutDevices); | ||||
| 
 | ||||
|         try { | ||||
|             await this.reset.setNewPassword(this.state.password); | ||||
|  |  | |||
|  | @ -49,12 +49,17 @@ describe("<ForgotPassword>", () => { | |||
|         }); | ||||
|     }; | ||||
| 
 | ||||
|     const clickButton = async (label: string): Promise<void> => { | ||||
|     const click = async (element: Element): Promise<void> => { | ||||
|         await act(async () => { | ||||
|             await userEvent.click(screen.getByText(label), { delay: null }); | ||||
|             await userEvent.click(element, { delay: null }); | ||||
|         }); | ||||
|     }; | ||||
| 
 | ||||
|     const waitForDialog = async (): Promise<void> => { | ||||
|         await flushPromisesWithFakeTimers(); | ||||
|         await flushPromisesWithFakeTimers(); | ||||
|     }; | ||||
| 
 | ||||
|     const itShouldCloseTheDialogAndShowThePasswordInput = (): void => { | ||||
|         it("should close the dialog and show the password input", () => { | ||||
|             expect(screen.queryByText("Verify your email to continue")).not.toBeInTheDocument(); | ||||
|  | @ -121,9 +126,9 @@ describe("<ForgotPassword>", () => { | |||
|             }); | ||||
|         }); | ||||
| 
 | ||||
|         describe("when clicking »Sign in instead«", () => { | ||||
|         describe("and clicking »Sign in instead«", () => { | ||||
|             beforeEach(async () => { | ||||
|                 await clickButton("Sign in instead"); | ||||
|                 await click(screen.getByText("Sign in instead")); | ||||
|             }); | ||||
| 
 | ||||
|             it("should call onLoginClick()", () => { | ||||
|  | @ -131,7 +136,7 @@ describe("<ForgotPassword>", () => { | |||
|             }); | ||||
|         }); | ||||
| 
 | ||||
|         describe("when entering a non-email value", () => { | ||||
|         describe("and entering a non-email value", () => { | ||||
|             beforeEach(async () => { | ||||
|                 await typeIntoField("Email address", "not en email"); | ||||
|             }); | ||||
|  | @ -141,13 +146,13 @@ describe("<ForgotPassword>", () => { | |||
|             }); | ||||
|         }); | ||||
| 
 | ||||
|         describe("when submitting an unknown email", () => { | ||||
|         describe("and submitting an unknown email", () => { | ||||
|             beforeEach(async () => { | ||||
|                 await typeIntoField("Email address", testEmail); | ||||
|                 mocked(client).requestPasswordEmailToken.mockRejectedValue({ | ||||
|                     errcode: "M_THREEPID_NOT_FOUND", | ||||
|                 }); | ||||
|                 await clickButton("Send email"); | ||||
|                 await click(screen.getByText("Send email")); | ||||
|             }); | ||||
| 
 | ||||
|             it("should show an email not found message", () => { | ||||
|  | @ -155,13 +160,13 @@ describe("<ForgotPassword>", () => { | |||
|             }); | ||||
|         }); | ||||
| 
 | ||||
|         describe("when a connection error occurs", () => { | ||||
|         describe("and a connection error occurs", () => { | ||||
|             beforeEach(async () => { | ||||
|                 await typeIntoField("Email address", testEmail); | ||||
|                 mocked(client).requestPasswordEmailToken.mockRejectedValue({ | ||||
|                     name: "ConnectionError", | ||||
|                 }); | ||||
|                 await clickButton("Send email"); | ||||
|                 await click(screen.getByText("Send email")); | ||||
|             }); | ||||
| 
 | ||||
|             it("should show an info about that", () => { | ||||
|  | @ -174,7 +179,7 @@ describe("<ForgotPassword>", () => { | |||
|             }); | ||||
|         }); | ||||
| 
 | ||||
|         describe("when the server liveness check fails", () => { | ||||
|         describe("and the server liveness check fails", () => { | ||||
|             beforeEach(async () => { | ||||
|                 await typeIntoField("Email address", testEmail); | ||||
|                 mocked(AutoDiscoveryUtils.validateServerConfigWithStaticUrls).mockRejectedValue({}); | ||||
|  | @ -183,7 +188,7 @@ describe("<ForgotPassword>", () => { | |||
|                     serverIsAlive: false, | ||||
|                     serverDeadError: "server down", | ||||
|                 }); | ||||
|                 await clickButton("Send email"); | ||||
|                 await click(screen.getByText("Send email")); | ||||
|             }); | ||||
| 
 | ||||
|             it("should show the server error", () => { | ||||
|  | @ -191,13 +196,13 @@ describe("<ForgotPassword>", () => { | |||
|             }); | ||||
|         }); | ||||
| 
 | ||||
|         describe("when submitting an known email", () => { | ||||
|         describe("and submitting an known email", () => { | ||||
|             beforeEach(async () => { | ||||
|                 await typeIntoField("Email address", testEmail); | ||||
|                 mocked(client).requestPasswordEmailToken.mockResolvedValue({ | ||||
|                     sid: testSid, | ||||
|                 }); | ||||
|                 await clickButton("Send email"); | ||||
|                 await click(screen.getByText("Send email")); | ||||
|             }); | ||||
| 
 | ||||
|             it("should send the mail and show the check email view", () => { | ||||
|  | @ -210,9 +215,9 @@ describe("<ForgotPassword>", () => { | |||
|                 expect(screen.getByText(testEmail)).toBeInTheDocument(); | ||||
|             }); | ||||
| 
 | ||||
|             describe("when clicking re-enter email", () => { | ||||
|             describe("and clicking »Re-enter email address«", () => { | ||||
|                 beforeEach(async () => { | ||||
|                     await clickButton("Re-enter email address"); | ||||
|                     await click(screen.getByText("Re-enter email address")); | ||||
|                 }); | ||||
| 
 | ||||
|                 it("go back to the email input", () => { | ||||
|  | @ -220,9 +225,9 @@ describe("<ForgotPassword>", () => { | |||
|                 }); | ||||
|             }); | ||||
| 
 | ||||
|             describe("when clicking resend email", () => { | ||||
|             describe("and clicking »Resend«", () => { | ||||
|                 beforeEach(async () => { | ||||
|                     await userEvent.click(screen.getByText("Resend"), { delay: null }); | ||||
|                     await click(screen.getByText("Resend")); | ||||
|                     // the message is shown after some time
 | ||||
|                     jest.advanceTimersByTime(500); | ||||
|                 }); | ||||
|  | @ -237,16 +242,16 @@ describe("<ForgotPassword>", () => { | |||
|                 }); | ||||
|             }); | ||||
| 
 | ||||
|             describe("when clicking next", () => { | ||||
|             describe("and clicking »Next«", () => { | ||||
|                 beforeEach(async () => { | ||||
|                     await clickButton("Next"); | ||||
|                     await click(screen.getByText("Next")); | ||||
|                 }); | ||||
| 
 | ||||
|                 it("should show the password input view", () => { | ||||
|                     expect(screen.getByText("Reset your password")).toBeInTheDocument(); | ||||
|                 }); | ||||
| 
 | ||||
|                 describe("when entering different passwords", () => { | ||||
|                 describe("and entering different passwords", () => { | ||||
|                     beforeEach(async () => { | ||||
|                         await typeIntoField("New Password", testPassword); | ||||
|                         await typeIntoField("Confirm new password", testPassword + "asd"); | ||||
|  | @ -257,7 +262,7 @@ describe("<ForgotPassword>", () => { | |||
|                     }); | ||||
|                 }); | ||||
| 
 | ||||
|                 describe("when entering a new password", () => { | ||||
|                 describe("and entering a new password", () => { | ||||
|                     beforeEach(async () => { | ||||
|                         mocked(client.setPassword).mockRejectedValue({ httpStatus: 401 }); | ||||
|                         await typeIntoField("New Password", testPassword); | ||||
|  | @ -273,7 +278,7 @@ describe("<ForgotPassword>", () => { | |||
|                                     retry_after_ms: (13 * 60 + 37) * 1000, | ||||
|                                 }, | ||||
|                             }); | ||||
|                             await clickButton("Reset password"); | ||||
|                             await click(screen.getByText("Reset password")); | ||||
|                         }); | ||||
| 
 | ||||
|                         it("should show the rate limit error message", () => { | ||||
|  | @ -285,10 +290,8 @@ describe("<ForgotPassword>", () => { | |||
| 
 | ||||
|                     describe("and submitting it", () => { | ||||
|                         beforeEach(async () => { | ||||
|                             await clickButton("Reset password"); | ||||
|                             // double flush promises for the modal to appear
 | ||||
|                             await flushPromisesWithFakeTimers(); | ||||
|                             await flushPromisesWithFakeTimers(); | ||||
|                             await click(screen.getByText("Reset password")); | ||||
|                             await waitForDialog(); | ||||
|                         }); | ||||
| 
 | ||||
|                         it("should send the new password and show the click validation link dialog", () => { | ||||
|  | @ -316,9 +319,7 @@ describe("<ForgotPassword>", () => { | |||
|                                 await act(async () => { | ||||
|                                     await userEvent.click(screen.getByTestId("dialog-background"), { delay: null }); | ||||
|                                 }); | ||||
|                                 // double flush promises for the modal to disappear
 | ||||
|                                 await flushPromisesWithFakeTimers(); | ||||
|                                 await flushPromisesWithFakeTimers(); | ||||
|                                 await waitForDialog(); | ||||
|                             }); | ||||
| 
 | ||||
|                             itShouldCloseTheDialogAndShowThePasswordInput(); | ||||
|  | @ -326,23 +327,17 @@ describe("<ForgotPassword>", () => { | |||
| 
 | ||||
|                         describe("and dismissing the dialog", () => { | ||||
|                             beforeEach(async () => { | ||||
|                                 await act(async () => { | ||||
|                                     await userEvent.click(screen.getByLabelText("Close dialog"), { delay: null }); | ||||
|                                 }); | ||||
|                                 // double flush promises for the modal to disappear
 | ||||
|                                 await flushPromisesWithFakeTimers(); | ||||
|                                 await flushPromisesWithFakeTimers(); | ||||
|                                 await click(screen.getByLabelText("Close dialog")); | ||||
|                                 await waitForDialog(); | ||||
|                             }); | ||||
| 
 | ||||
|                             itShouldCloseTheDialogAndShowThePasswordInput(); | ||||
|                         }); | ||||
| 
 | ||||
|                         describe("when clicking re-enter email", () => { | ||||
|                         describe("and clicking »Re-enter email address«", () => { | ||||
|                             beforeEach(async () => { | ||||
|                                 await clickButton("Re-enter email address"); | ||||
|                                 // double flush promises for the modal to disappear
 | ||||
|                                 await flushPromisesWithFakeTimers(); | ||||
|                                 await flushPromisesWithFakeTimers(); | ||||
|                                 await click(screen.getByText("Re-enter email address")); | ||||
|                                 await waitForDialog(); | ||||
|                             }); | ||||
| 
 | ||||
|                             it("should close the dialog and go back to the email input", () => { | ||||
|  | @ -351,7 +346,7 @@ describe("<ForgotPassword>", () => { | |||
|                             }); | ||||
|                         }); | ||||
| 
 | ||||
|                         describe("when validating the link from the mail", () => { | ||||
|                         describe("and validating the link from the mail", () => { | ||||
|                             beforeEach(async () => { | ||||
|                                 mocked(client.setPassword).mockResolvedValue({}); | ||||
|                                 // be sure the next set password attempt was sent
 | ||||
|  | @ -369,6 +364,42 @@ describe("<ForgotPassword>", () => { | |||
|                             }); | ||||
|                         }); | ||||
|                     }); | ||||
| 
 | ||||
|                     describe("and clicking »Sign out of all devices« and »Reset password«", () => { | ||||
|                         beforeEach(async () => { | ||||
|                             await click(screen.getByText("Sign out of all devices")); | ||||
|                             await click(screen.getByText("Reset password")); | ||||
|                             await waitForDialog(); | ||||
|                         }); | ||||
| 
 | ||||
|                         it("should show the sign out warning dialog", async () => { | ||||
|                             expect( | ||||
|                                 screen.getByText( | ||||
|                                     "Signing out your devices will delete the message encryption keys stored on them, making encrypted chat history unreadable.", | ||||
|                                 ), | ||||
|                             ).toBeInTheDocument(); | ||||
| 
 | ||||
|                             // confirm dialog
 | ||||
|                             await click(screen.getByText("Continue")); | ||||
| 
 | ||||
|                             // expect setPassword with logoutDevices = true
 | ||||
|                             expect(client.setPassword).toHaveBeenCalledWith( | ||||
|                                 { | ||||
|                                     type: "m.login.email.identity", | ||||
|                                     threepid_creds: { | ||||
|                                         client_secret: expect.any(String), | ||||
|                                         sid: testSid, | ||||
|                                     }, | ||||
|                                     threepidCreds: { | ||||
|                                         client_secret: expect.any(String), | ||||
|                                         sid: testSid, | ||||
|                                     }, | ||||
|                                 }, | ||||
|                                 testPassword, | ||||
|                                 true, | ||||
|                             ); | ||||
|                         }); | ||||
|                     }); | ||||
|                 }); | ||||
|             }); | ||||
|         }); | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue
	
	 Michael Weimann
						Michael Weimann