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

Commit

Permalink
Fix logout devices on password reset
Browse files Browse the repository at this point in the history
  • Loading branch information
weeman1337 committed Jan 18, 2023
1 parent 6291321 commit 7e6100b
Show file tree
Hide file tree
Showing 3 changed files with 77 additions and 41 deletions.
4 changes: 4 additions & 0 deletions src/PasswordReset.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
1 change: 1 addition & 0 deletions src/components/structures/auth/ForgotPassword.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
113 changes: 72 additions & 41 deletions test/components/structures/auth/ForgotPassword-test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -121,17 +126,17 @@ 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()", () => {
expect(onLoginClick).toHaveBeenCalled();
});
});

describe("when entering a non-email value", () => {
describe("and entering a non-email value", () => {
beforeEach(async () => {
await typeIntoField("Email address", "not en email");
});
Expand All @@ -141,27 +146,27 @@ 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", () => {
expect(screen.getByText("This email address was not found")).toBeInTheDocument();
});
});

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", () => {
Expand All @@ -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({});
Expand All @@ -183,21 +188,21 @@ describe("<ForgotPassword>", () => {
serverIsAlive: false,
serverDeadError: "server down",
});
await clickButton("Send email");
await click(screen.getByText("Send email"));
});

it("should show the server error", () => {
expect(screen.queryByText("server down")).toBeInTheDocument();
});
});

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", () => {
Expand All @@ -210,19 +215,19 @@ 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", () => {
expect(screen.queryByText("Enter your email to reset password")).toBeInTheDocument();
});
});

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);
});
Expand All @@ -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");
Expand All @@ -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);
Expand All @@ -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", () => {
Expand All @@ -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", () => {
Expand Down Expand Up @@ -316,33 +319,25 @@ 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();
});

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", () => {
Expand All @@ -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
Expand All @@ -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,
);
});
});
});
});
});
Expand Down

0 comments on commit 7e6100b

Please sign in to comment.