passbolt-styleguide
Version:
Passbolt styleguide contains common styling assets used by the different sites, plugin, etc.
517 lines (454 loc) • 24.9 kB
JavaScript
/**
* Passbolt ~ Open source password manager for teams
* Copyright (c) 2022 Passbolt SA (https://www.passbolt.com)
*
* Licensed under GNU Affero General Public License version 3 of the or any later version.
* For full copyright and license information, please see the LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) 2022 Passbolt SA (https://www.passbolt.com)
* @license https://opensource.org/licenses/AGPL-3.0 AGPL License
* @link https://www.passbolt.com Passbolt(tm)
* @since 3.6.0
*/
import mockComponentSetState from "../../test/mock/components/React/mockSetState";
import { mockUserAgent } from "jest-useragent-mock";
import { AuthenticationSetupContextProvider, AuthenticationSetupWorkflowStates } from "./AuthenticationSetupContext";
import { defaultProps, withAccountRecoveryEnabled } from "./AuthenticationSetupContext.test.data";
import GpgKeyError from "../../lib/Error/GpgKeyError";
import InvalidMasterPasswordError from "../../lib/Error/InvalidMasterPasswordError";
import { enableMetadataSetupSettingsDto } from "../../../shared/models/entity/metadata/metadataSetupSettingsEntity.test.data";
import { defaultAdminUserDto, defaultUserDto } from "../../../shared/models/entity/user/userEntity.test.data";
beforeEach(() => {
jest.resetModules();
jest.clearAllMocks();
});
describe("AuthenticationSetupContextProvider", () => {
describe("AuthenticationSetupContextProvider::constructor", () => {
it("The machine state should be by default set to: LOADING", async () => {
const props = defaultProps();
const contextProvider = new AuthenticationSetupContextProvider(props);
mockComponentSetState(contextProvider);
expect.assertions(1);
expect(contextProvider.state.state).toEqual(AuthenticationSetupWorkflowStates.LOADING);
});
});
describe("AuthenticationSetupContextProvider::initialize", () => {
it("When the extension was just installed and the browser is Chrome, the machine state should be set to: INTRODUCE_EXTENSION", async () => {
mockUserAgent("chrome");
const props = defaultProps();
props.context.port.addRequestListener(
"passbolt.setup.is-first-install",
jest.fn(() => Promise.resolve(true)),
);
const contextProvider = new AuthenticationSetupContextProvider(props);
mockComponentSetState(contextProvider);
expect.assertions(3);
await contextProvider.initialize();
expect(props.context.port.requestListeners["passbolt.setup.is-first-install"]).toHaveBeenCalled();
expect(props.context.port.requestListeners["passbolt.setup.start"]).toHaveBeenCalled();
expect(contextProvider.state.state).toEqual(AuthenticationSetupWorkflowStates.INTRODUCE_EXTENSION);
});
it("When the extension was just installed and the browser is Firefox, the machine state should be set to: GENERATE_GPG_KEY", async () => {
mockUserAgent("firefox");
const props = defaultProps();
props.context.port.addRequestListener(
"passbolt.setup.is-first-install",
jest.fn(() => Promise.resolve(true)),
);
const contextProvider = new AuthenticationSetupContextProvider(props);
mockComponentSetState(contextProvider);
expect.assertions(3);
await contextProvider.initialize();
expect(props.context.port.requestListeners["passbolt.setup.is-first-install"]).toHaveBeenCalled();
expect(props.context.port.requestListeners["passbolt.setup.start"]).toHaveBeenCalled();
expect(contextProvider.state.state).toEqual(AuthenticationSetupWorkflowStates.GENERATE_GPG_KEY);
});
it("When the extension was already installed and the browser is Chrome, the machine state should be set to: GENERATE_GPG_KEY", async () => {
mockUserAgent("chrome");
const props = defaultProps();
const contextProvider = new AuthenticationSetupContextProvider(props);
mockComponentSetState(contextProvider);
expect.assertions(3);
await contextProvider.initialize();
expect(props.context.port.requestListeners["passbolt.setup.is-first-install"]).toHaveBeenCalled();
expect(props.context.port.requestListeners["passbolt.setup.start"]).toHaveBeenCalled();
expect(contextProvider.state.state).toEqual(AuthenticationSetupWorkflowStates.GENERATE_GPG_KEY);
});
});
describe("AuthenticationSetupContextProvider::goToGenerateGpgKey", () => {
it("When the go to succeed, the machine state should be set to: GENERATE_GPG_KEY", async () => {
const props = defaultProps();
const contextProvider = new AuthenticationSetupContextProvider(props);
mockComponentSetState(contextProvider);
expect.assertions(1);
await contextProvider.initialize();
await contextProvider.goToGenerateGpgKey();
expect(contextProvider.state.state).toEqual(AuthenticationSetupWorkflowStates.GENERATE_GPG_KEY);
});
});
describe("AuthenticationSetupContextProvider::generateGpgKey", () => {
it("When the gpg key is created, the machine state should be set to: DOWNLOAD_RECOVERY_KIT", async () => {
const props = defaultProps();
const contextProvider = new AuthenticationSetupContextProvider(props);
mockComponentSetState(contextProvider);
expect.assertions(2);
await contextProvider.initialize();
await contextProvider.generateGpgKey("passphrase");
expect(props.context.port.requestListeners["passbolt.setup.generate-key"]).toHaveBeenCalledWith(
{ passphrase: "passphrase" },
undefined,
);
expect(contextProvider.state.state).toEqual(AuthenticationSetupWorkflowStates.DOWNLOAD_RECOVERY_KIT);
});
it("When an error occurred during the gpg key creation, the machine state should be set to: UNEXPECTED_ERROR", async () => {
const props = defaultProps();
props.context.port.addRequestListener(
"passbolt.setup.generate-key",
jest.fn(() => Promise.reject(new Error("Unexpected error"))),
);
const contextProvider = new AuthenticationSetupContextProvider(props);
mockComponentSetState(contextProvider);
expect.assertions(3);
await contextProvider.initialize();
await contextProvider.generateGpgKey("passphrase");
expect(props.context.port.requestListeners["passbolt.setup.generate-key"]).toHaveBeenCalledWith(
{ passphrase: "passphrase" },
undefined,
);
expect(contextProvider.state.state).toEqual(AuthenticationSetupWorkflowStates.UNEXPECTED_ERROR);
expect(contextProvider.state.error.message).toEqual("Unexpected error");
});
});
describe("AuthenticationSetupContextProvider::downloadRecoveryKit", () => {
it("The the recovery kit is downloaded with success, the machine state should remain set to the state it was before the operation", async () => {
const props = defaultProps();
const contextProvider = new AuthenticationSetupContextProvider(props);
mockComponentSetState(contextProvider);
expect.assertions(2);
await contextProvider.initialize();
const expectedWorkflowState = contextProvider.state.state;
await contextProvider.downloadRecoveryKit();
expect(props.context.port.requestListeners["passbolt.setup.download-recovery-kit"]).toHaveBeenCalled();
expect(contextProvider.state.state).toEqual(expectedWorkflowState);
});
it("When an error occurred during the recovery kit download, the machine state should be set to: UNEXPECTED_ERROR", async () => {
const props = defaultProps();
props.context.port.addRequestListener(
"passbolt.setup.download-recovery-kit",
jest.fn(() => Promise.reject(new Error("Unexpected error"))),
);
const contextProvider = new AuthenticationSetupContextProvider(props);
mockComponentSetState(contextProvider);
expect.assertions(3);
await contextProvider.initialize();
await contextProvider.downloadRecoveryKit();
expect(props.context.port.requestListeners["passbolt.setup.download-recovery-kit"]).toHaveBeenCalled();
expect(contextProvider.state.state).toEqual(AuthenticationSetupWorkflowStates.UNEXPECTED_ERROR);
expect(contextProvider.state.error.message).toEqual("Unexpected error");
});
});
describe("AuthenticationSetupContextProvider::downloadRecoveryKit", () => {
it("Given the account recovery policy is not set or disabled and the the recovery kit is downloaded, the machine state should be set to: CHOOSE_SECURITY_TOKEN", async () => {
const props = defaultProps();
const contextProvider = new AuthenticationSetupContextProvider(props);
mockComponentSetState(contextProvider);
expect.assertions(1);
await contextProvider.initialize();
await contextProvider.handleRecoveryKitDownloaded();
expect(contextProvider.state.state).toEqual(AuthenticationSetupWorkflowStates.CHOOSE_SECURITY_TOKEN);
});
it("Given the account recovery is enabled and the the recovery kit is downloaded, the machine state should be set to: CHOOSE_ACCOUNT_RECOVERY_PREFERENCE", async () => {
const props = withAccountRecoveryEnabled(defaultProps());
const contextProvider = new AuthenticationSetupContextProvider(props);
mockComponentSetState(contextProvider);
expect.assertions(1);
await contextProvider.initialize();
await contextProvider.handleRecoveryKitDownloaded();
expect(contextProvider.state.state).toEqual(AuthenticationSetupWorkflowStates.CHOOSE_ACCOUNT_RECOVERY_PREFERENCE);
});
});
describe("AuthenticationSetupContextProvider::goToImportGpgKey", () => {
it("When the go to succeed, the machine state should be set to: IMPORT_GPG_KEY", async () => {
const props = defaultProps();
const contextProvider = new AuthenticationSetupContextProvider(props);
mockComponentSetState(contextProvider);
expect.assertions(1);
await contextProvider.initialize();
await contextProvider.goToImportGpgKey();
expect(contextProvider.state.state).toEqual(AuthenticationSetupWorkflowStates.IMPORT_GPG_KEY);
});
});
describe("AuthenticationSetupContextProvider::importGpgKey", () => {
it("When the gpg key is imported with success, the machine state should be set to: VALIDATE_PASSPHRASE", async () => {
const props = defaultProps();
const contextProvider = new AuthenticationSetupContextProvider(props);
mockComponentSetState(contextProvider);
expect.assertions(2);
await contextProvider.initialize();
await contextProvider.importGpgKey("armored_key");
expect(props.context.port.requestListeners["passbolt.setup.import-key"]).toHaveBeenCalledWith(
"armored_key",
undefined,
);
expect(contextProvider.state.state).toEqual(AuthenticationSetupWorkflowStates.VALIDATE_PASSPHRASE);
});
it("When an invalid gpg key fails the import, the error should be rethrown and the machine state should remain on: IMPORT_GPG_KEY", async () => {
const props = defaultProps();
props.context.port.addRequestListener(
"passbolt.setup.import-key",
jest.fn(() => Promise.reject(new GpgKeyError())),
);
const contextProvider = new AuthenticationSetupContextProvider(props);
mockComponentSetState(contextProvider);
expect.assertions(3);
await contextProvider.initialize();
await contextProvider.goToImportGpgKey();
try {
await contextProvider.importGpgKey("armored_key");
expect(false).toBeTruthy();
} catch (error) {
expect(props.context.port.requestListeners["passbolt.setup.import-key"]).toHaveBeenCalledWith(
"armored_key",
undefined,
);
expect(contextProvider.state.state).toEqual(AuthenticationSetupWorkflowStates.IMPORT_GPG_KEY);
expect(error.name).toEqual("GpgKeyError");
}
});
it("When an error occurred during the gpg key import, the machine state should be set to: UNEXPECTED_ERROR", async () => {
const props = defaultProps();
props.context.port.addRequestListener(
"passbolt.setup.import-key",
jest.fn(() => Promise.reject(new Error("Unexpected error"))),
);
const contextProvider = new AuthenticationSetupContextProvider(props);
mockComponentSetState(contextProvider);
expect.assertions(3);
await contextProvider.initialize();
await contextProvider.importGpgKey("armored_key");
expect(props.context.port.requestListeners["passbolt.setup.import-key"]).toHaveBeenCalledWith(
"armored_key",
undefined,
);
expect(contextProvider.state.state).toEqual(AuthenticationSetupWorkflowStates.UNEXPECTED_ERROR);
expect(contextProvider.state.error.message).toEqual("Unexpected error");
});
});
describe("AuthenticationSetupContextProvider::checkPassphrase", () => {
it("When a passphrase check succeed the machine state should be set to: CHOOSE_SECURITY_TOKEN", async () => {
const props = defaultProps();
const contextProvider = new AuthenticationSetupContextProvider(props);
mockComponentSetState(contextProvider);
expect.assertions(2);
await contextProvider.initialize();
await contextProvider.importGpgKey();
await contextProvider.checkPassphrase("passphrase");
expect(props.context.port.requestListeners["passbolt.setup.verify-passphrase"]).toHaveBeenCalledWith(
"passphrase",
undefined,
);
expect(contextProvider.state.state).toEqual(AuthenticationSetupWorkflowStates.CHOOSE_SECURITY_TOKEN);
});
it("When a passphrase check succeed and account recovery policy is enabled the machine state should be set to: CHOOSE_ACCOUNT_RECOVERY_PREFERENCE", async () => {
const props = withAccountRecoveryEnabled(defaultProps());
const contextProvider = new AuthenticationSetupContextProvider(props);
mockComponentSetState(contextProvider);
expect.assertions(2);
await contextProvider.initialize();
await contextProvider.importGpgKey();
await contextProvider.checkPassphrase("passphrase");
expect(props.context.port.requestListeners["passbolt.setup.verify-passphrase"]).toHaveBeenCalledWith(
"passphrase",
undefined,
);
expect(contextProvider.state.state).toEqual(AuthenticationSetupWorkflowStates.CHOOSE_ACCOUNT_RECOVERY_PREFERENCE);
});
it("When a passphrase check succeed and accountRecovery feature flag is disabled but the server sends an enabled account recovery organization policy, the machine state should be set to: CHOOSE_SECURITY_TOKEN", async () => {
const props = withAccountRecoveryEnabled(defaultProps());
props.context.siteSettings.canIUse = jest.fn(() => false);
const contextProvider = new AuthenticationSetupContextProvider(props);
mockComponentSetState(contextProvider);
expect.assertions(2);
await contextProvider.initialize();
await contextProvider.importGpgKey();
await contextProvider.checkPassphrase("passphrase");
expect(props.context.port.requestListeners["passbolt.setup.verify-passphrase"]).toHaveBeenCalledWith(
"passphrase",
undefined,
);
expect(contextProvider.state.state).toEqual(AuthenticationSetupWorkflowStates.CHOOSE_SECURITY_TOKEN);
});
it("When a wrong passphrase is requested to be checked, the error should be rethrown and the machine state should remain on: SIGN_IN", async () => {
const props = defaultProps();
props.context.port.addRequestListener(
"passbolt.setup.verify-passphrase",
jest.fn(() => Promise.reject(new InvalidMasterPasswordError())),
);
const contextProvider = new AuthenticationSetupContextProvider(props);
mockComponentSetState(contextProvider);
expect.assertions(2);
await contextProvider.initialize();
await contextProvider.importGpgKey();
try {
await contextProvider.checkPassphrase("passphrase");
expect(false).toBeTruthy();
} catch (error) {
expect(contextProvider.state.state).toEqual(AuthenticationSetupWorkflowStates.VALIDATE_PASSPHRASE);
expect(error.name).toEqual("InvalidMasterPasswordError");
}
});
it("If an unexpected error occurred the machine state should be set to: UNEXPECTED_ERROR", async () => {
const props = defaultProps();
props.context.port.addRequestListener(
"passbolt.setup.verify-passphrase",
jest.fn(() => Promise.reject(new Error("Unexpected error"))),
);
const contextProvider = new AuthenticationSetupContextProvider(props);
mockComponentSetState(contextProvider);
expect.assertions(2);
await contextProvider.initialize();
await contextProvider.checkPassphrase("passphrase");
expect(contextProvider.state.state).toEqual(AuthenticationSetupWorkflowStates.UNEXPECTED_ERROR);
expect(contextProvider.state.error.message).toEqual("Unexpected error");
});
});
describe("AuthenticationSetupContextProvider::chooseAccountRecoveryPreference", () => {
it("When the account recovery preferences are set with success, the machine state should be set to: CHOOSE_SECURITY_TOKEN", async () => {
const props = defaultProps();
const contextProvider = new AuthenticationSetupContextProvider(props);
mockComponentSetState(contextProvider);
expect.assertions(2);
await contextProvider.initialize();
await contextProvider.chooseAccountRecoveryPreference("approved");
expect(
props.context.port.requestListeners["passbolt.setup.set-account-recovery-user-setting"],
).toHaveBeenCalledWith("approved", undefined);
expect(contextProvider.state.state).toEqual(AuthenticationSetupWorkflowStates.CHOOSE_SECURITY_TOKEN);
});
it("When the account recovery preferences fails, the machine state should be set to: UNEXPECTED_ERROR", async () => {
const props = defaultProps();
props.context.port.addRequestListener(
"passbolt.setup.set-account-recovery-user-setting",
jest.fn(() => Promise.reject(new Error("Unexpected error"))),
);
const contextProvider = new AuthenticationSetupContextProvider(props);
mockComponentSetState(contextProvider);
expect.assertions(3);
await contextProvider.initialize();
await contextProvider.chooseAccountRecoveryPreference("approved");
expect(
props.context.port.requestListeners["passbolt.setup.set-account-recovery-user-setting"],
).toHaveBeenCalledWith("approved", undefined);
expect(contextProvider.state.state).toEqual(AuthenticationSetupWorkflowStates.UNEXPECTED_ERROR);
expect(contextProvider.state.error.message).toEqual("Unexpected error");
});
});
describe("AuthenticationSetupContextProvider::chooseSecurityToken", () => {
it("When the security token preferences are set with success, the machine state should be set to: CHECKING_POST_SETUP_METADATA_TASKS", async () => {
const props = defaultProps();
const contextProvider = new AuthenticationSetupContextProvider(props);
props.context.port.addRequestListener(
"passbolt.users.find-logged-in-user",
jest.fn(async () => defaultAdminUserDto()),
);
mockComponentSetState(contextProvider);
expect.assertions(4);
await contextProvider.initialize();
await contextProvider.chooseSecurityToken({ color: "black", textColor: "red" });
expect(props.context.port.requestListeners["passbolt.setup.set-security-token"]).toHaveBeenCalledWith(
{ color: "black", textColor: "red" },
undefined,
);
expect(props.context.port.requestListeners["passbolt.setup.complete"]).toHaveBeenCalled();
expect(props.context.port.requestListeners["passbolt.setup.sign-in"]).toHaveBeenCalledWith(false, undefined);
expect(contextProvider.state.state).toEqual(AuthenticationSetupWorkflowStates.CHECKING_POST_SETUP_METADATA_TASKS);
});
it("When the security token preferences are set with success and the current user is not an admin, the machine state should be set to: SIGNING_IN", async () => {
const props = defaultProps();
const contextProvider = new AuthenticationSetupContextProvider(props);
props.context.port.addRequestListener(
"passbolt.users.find-logged-in-user",
jest.fn(async () => defaultUserDto({}, { withRole: true })),
);
mockComponentSetState(contextProvider);
expect.assertions(4);
await contextProvider.initialize();
await contextProvider.chooseSecurityToken({ color: "black", textColor: "red" });
expect(props.context.port.requestListeners["passbolt.setup.set-security-token"]).toHaveBeenCalledWith(
{ color: "black", textColor: "red" },
undefined,
);
expect(props.context.port.requestListeners["passbolt.setup.complete"]).toHaveBeenCalled();
expect(props.context.port.requestListeners["passbolt.setup.sign-in"]).toHaveBeenCalledWith(false, undefined);
expect(contextProvider.state.state).toEqual(AuthenticationSetupWorkflowStates.SIGNING_IN);
});
it("When the security token preferences cannot be set, the machine state should be set to: UNEXPECTED_ERROR", async () => {
const props = defaultProps();
props.context.port.addRequestListener(
"passbolt.setup.set-security-token",
jest.fn(() => Promise.reject(new Error("Unexpected error"))),
);
const contextProvider = new AuthenticationSetupContextProvider(props);
mockComponentSetState(contextProvider);
expect.assertions(3);
await contextProvider.initialize();
await contextProvider.chooseSecurityToken({ color: "black", textColor: "red" });
expect(props.context.port.requestListeners["passbolt.setup.set-security-token"]).toHaveBeenCalledWith(
{ color: "black", textColor: "red" },
undefined,
);
expect(contextProvider.state.state).toEqual(AuthenticationSetupWorkflowStates.UNEXPECTED_ERROR);
expect(contextProvider.state.error.message).toEqual("Unexpected error");
});
});
describe("AuthenticationSetupContextProvider::runPostSetupProcess", () => {
it("When the setup is done, the user is signed in and metadata needs to be enabled, the machine state should be set to: ENABLING_METADATA_ENCRYPTION", async () => {
expect.assertions(5);
const props = defaultProps();
props.context.port.addRequestListener(
"passbolt.metadata.find-setup-settings",
jest.fn(async () => enableMetadataSetupSettingsDto()),
);
props.context.port.addRequestListener(
"passbolt.users.find-logged-in-user",
jest.fn(async () => defaultAdminUserDto()),
);
const contextProvider = new AuthenticationSetupContextProvider(props);
mockComponentSetState(contextProvider);
await contextProvider.initialize();
await contextProvider.chooseSecurityToken({ color: "black", textColor: "red" });
expect(props.context.port.requestListeners["passbolt.setup.set-security-token"]).toHaveBeenCalledWith(
{ color: "black", textColor: "red" },
undefined,
);
expect(props.context.port.requestListeners["passbolt.setup.complete"]).toHaveBeenCalled();
expect(props.context.port.requestListeners["passbolt.setup.sign-in"]).toHaveBeenCalledWith(false, undefined);
expect(props.context.port.requestListeners["passbolt.users.find-logged-in-user"]).toHaveBeenCalledTimes(1);
expect(contextProvider.state.state).toEqual(AuthenticationSetupWorkflowStates.ENABLING_METADATA_ENCRYPTION);
});
it("When the setup is done, the user is signed but metadata cannot be enabled: UNEXPECTED_METADATA_ENCRYPTION_ENABLEMENT_ERROR", async () => {
expect.assertions(1);
const props = defaultProps();
props.context.port.addRequestListener(
"passbolt.metadata.find-setup-settings",
jest.fn(async () => enableMetadataSetupSettingsDto()),
);
props.context.port.addRequestListener(
"passbolt.metadata.enable",
jest.fn(() => Promise.reject(new Error("Unexpected error"))),
);
props.context.port.addRequestListener(
"passbolt.users.find-logged-in-user",
jest.fn(async () => defaultAdminUserDto()),
);
const contextProvider = new AuthenticationSetupContextProvider(props);
mockComponentSetState(contextProvider);
await contextProvider.initialize();
await contextProvider.chooseSecurityToken({ color: "black", textColor: "red" });
expect(contextProvider.state.state).toEqual(
AuthenticationSetupWorkflowStates.UNEXPECTED_METADATA_ENCRYPTION_ENABLEMENT_ERROR,
);
});
});
});