From 5bb1eaf6aa7a72457838c7f707bffc2f79aceb2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Loegel?= Date: Thu, 21 Sep 2023 18:07:09 +0200 Subject: [PATCH] PB-27793 - As a user I should be able to transfer my account to the mobile app --- .../crypto/getUserPrivateKeyController.js | 63 ++++++++++++++++++ .../getUserPrivateKeyController.test.js | 64 +++++++++++++++++++ .../background_page/event/keyringEvents.js | 17 +---- 3 files changed, 130 insertions(+), 14 deletions(-) create mode 100644 src/all/background_page/controller/crypto/getUserPrivateKeyController.js create mode 100644 src/all/background_page/controller/crypto/getUserPrivateKeyController.test.js diff --git a/src/all/background_page/controller/crypto/getUserPrivateKeyController.js b/src/all/background_page/controller/crypto/getUserPrivateKeyController.js new file mode 100644 index 00000000..322ab13f --- /dev/null +++ b/src/all/background_page/controller/crypto/getUserPrivateKeyController.js @@ -0,0 +1,63 @@ +/** + * Passbolt ~ Open source password manager for teams + * Copyright (c) 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) Passbolt SA (https://www.passbolt.com) + * @license https://opensource.org/licenses/AGPL-3.0 AGPL License + * @link https://www.passbolt.com Passbolt(tm) + * @since 4.3.0 + */ + +import Keyring from "../../model/keyring"; +import i18n from "../../sdk/i18n"; +import GpgKeyError from "../../error/GpgKeyError"; +import GetPassphraseService from "../../service/passphrase/getPassphraseService"; + +class GetUserPrivateKeyController { + /** + * GetUserPrivateKeyController constructor + * @param {Worker} worker + * @param {string} requestId uuid + */ + constructor(worker, requestId, account) { + this.worker = worker; + this.requestId = requestId; + this.keyring = new Keyring(); + this.getPassphraseService = new GetPassphraseService(account); + } + + /** + * Wrapper of exec function to run it with worker. + * + * @returns {Promise} + */ + async _exec() { + try { + const armoredEncryptedPrivateKey = await this.exec(); + this.worker.port.emit(this.requestId, "SUCCESS", armoredEncryptedPrivateKey); + } catch (error) { + console.error(error); + this.worker.port.emit(this.requestId, 'ERROR', error); + } + } + /** + * Returns the current user's armored private key after requesting its passphrase. + * + * @returns {Promise} + */ + async exec() { + await this.getPassphraseService.requestPassphrase(this.worker); + const keyring = new Keyring(); + const privateKeyInfo = keyring.findPrivate(); + if (!privateKeyInfo) { + throw new GpgKeyError(i18n.t('Private key not found.')); + } + return privateKeyInfo.armoredKey; + } +} + +export default GetUserPrivateKeyController; diff --git a/src/all/background_page/controller/crypto/getUserPrivateKeyController.test.js b/src/all/background_page/controller/crypto/getUserPrivateKeyController.test.js new file mode 100644 index 00000000..c22fd21a --- /dev/null +++ b/src/all/background_page/controller/crypto/getUserPrivateKeyController.test.js @@ -0,0 +1,64 @@ +/** + * Passbolt ~ Open source password manager for teams + * Copyright (c) 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) Passbolt SA (https://www.passbolt.com) + * @license https://opensource.org/licenses/AGPL-3.0 AGPL License + * @link https://www.passbolt.com Passbolt(tm) + * @since 4.3.0 + */ +import {enableFetchMocks} from "jest-fetch-mock"; +import {pgpKeys} from "../../../../../test/fixtures/pgpKeys/keys"; +import MockExtension from "../../../../../test/mocks/mockExtension"; +import Keyring from '../../model/keyring'; +import GetGpgKeyInfoService from "../../service/crypto/getGpgKeyInfoService"; +import {v4 as uuidv4} from "uuid"; +import {OpenpgpAssertion} from "../../utils/openpgp/openpgpAssertions"; +import GetUserPrivateKeyController from "./getUserPrivateKeyController"; +import GpgKeyError from "../../error/GpgKeyError"; +import AccountEntity from "../../model/entity/account/accountEntity"; +import {adminAccountDto} from "../../model/entity/account/accountEntity.test.data"; + +const keyring = new Keyring(); +jest.mock("../../service/passphrase/getPassphraseService"); + +beforeAll(() => { + enableFetchMocks(); +}); + +describe("GetUserPrivateKeyController", () => { + const account = new AccountEntity(adminAccountDto()); + it(`Should return the user's armored encrypted private key`, async() => { + expect.assertions(1); + const userId = uuidv4(); + await MockExtension.withConfiguredAccount(); + await keyring.importPrivate(pgpKeys.ada.private, userId); + + const controller = new GetUserPrivateKeyController(null, null, account); + controller.getPassphraseService.requestPassphrase.mockResolvedValue(pgpKeys.ada.passphrase); + const armoredPrivateKey = await controller.exec(); + + const adaPrivateKey = await OpenpgpAssertion.readKeyOrFail(pgpKeys.ada.private); + const adaKeyInfo = await GetGpgKeyInfoService.getKeyInfo(adaPrivateKey); + + const returnedKey = await OpenpgpAssertion.readKeyOrFail(armoredPrivateKey); + const returnedKeyInfo = await GetGpgKeyInfoService.getKeyInfo(returnedKey); + + expect(returnedKeyInfo.toDto()).toStrictEqual(adaKeyInfo.toDto()); + }); + + it(`Should throw an exception if the private key does not exist`, async() => { + expect.assertions(1); + const controller = new GetUserPrivateKeyController(null, null, account); + + try { + await controller.exec(); + } catch (error) { + expect(error).toStrictEqual(new GpgKeyError('Private key not found.')); + } + }); +}); diff --git a/src/all/background_page/event/keyringEvents.js b/src/all/background_page/event/keyringEvents.js index ebbc45b4..8491ee7a 100644 --- a/src/all/background_page/event/keyringEvents.js +++ b/src/all/background_page/event/keyringEvents.js @@ -5,13 +5,12 @@ * @copyright (c) 2019 Passbolt SA * @licence GNU Affero General Public License http://www.gnu.org/licenses/agpl-3.0.en.html */ -import Keyring from "../model/keyring"; -import GetPassphraseService from "../service/passphrase/getPassphraseService"; import CheckPassphraseController from "../controller/crypto/checkPassphraseController"; import GetUserKeyInfoController from "../controller/crypto/getUserKeyInfoController"; import GetKeyInfoController from "../controller/crypto/getKeyInfoController"; import DownloadUserPublicKeyController from "../controller/crypto/downloadUserPublicKeyController"; import DownloadUserPrivateKeyController from "../controller/crypto/downloadUserPrivateKeyController"; +import GetUserPrivateKeyController from "../controller/crypto/getUserPrivateKeyController"; const listen = function(worker, _, account) { /* @@ -97,18 +96,8 @@ const listen = function(worker, _, account) { * @param requestId {uuid} The request identifier */ worker.port.on('passbolt.keyring.get-private-key', async requestId => { - try { - const getPassphraseService = new GetPassphraseService(account); - await getPassphraseService.request(worker); - const keyring = new Keyring(); - const privateKeyInfo = keyring.findPrivate(); - if (!privateKeyInfo) { - throw new Error('Private key not found.'); - } - worker.port.emit(requestId, 'SUCCESS', privateKeyInfo.armoredKey); - } catch (error) { - worker.port.emit(requestId, 'ERROR', error); - } + const controller = new GetUserPrivateKeyController(worker, requestId, account); + await controller._exec(); }); }; export const KeyringEvents = {listen};