passbolt-browser-extension 4.6.2 → 4.7.0-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (129) hide show
  1. package/.gitlab-ci/jobs/build.yml +3 -3
  2. package/.gitlab-ci/jobs/publish.yml +10 -0
  3. package/.gitlab-ci/jobs/test.yml +4 -4
  4. package/.gitlab-ci/scripts/bin/publish_npm.sh +24 -0
  5. package/.gitlab-ci/scripts/lib/version-check.sh +16 -0
  6. package/.gitlab-ci.yml +2 -2
  7. package/doc/worker-port-lfecycle.md +19 -0
  8. package/package.json +5 -4
  9. package/src/all/background_page/controller/account/updatePrivateKeyController.js +2 -1
  10. package/src/all/background_page/controller/accountRecovery/accountRecoveryValidatePublicKeyController.js +3 -2
  11. package/src/all/background_page/controller/accountRecovery/accountRecoveryValidatePublicKeyController.test.js +1 -1
  12. package/src/all/background_page/controller/accountRecovery/recoverAccountController.js +2 -0
  13. package/src/all/background_page/controller/accountRecovery/reviewRequestController.test.js +1 -1
  14. package/src/all/background_page/controller/auth/authCheckStatus.test.data.js +28 -0
  15. package/src/all/background_page/controller/auth/authCheckStatusController.js +19 -5
  16. package/src/all/background_page/controller/auth/authCheckStatusController.test.js +94 -0
  17. package/src/all/background_page/controller/auth/authIsMfaRequiredController.js +17 -9
  18. package/src/all/background_page/controller/auth/authIsMfaRequiredController.test.js +54 -0
  19. package/src/all/background_page/controller/auth/authLoginController.js +20 -8
  20. package/src/all/background_page/controller/auth/authLoginController.test.js +46 -29
  21. package/src/all/background_page/controller/auth/authVerifyServerKeyController.js +40 -25
  22. package/src/all/background_page/controller/auth/authVerifyServerKeyController.test.js +64 -11
  23. package/src/all/background_page/controller/auth/getServerKeyController.js +54 -0
  24. package/src/all/background_page/controller/auth/replaceServerKeyController.js +59 -0
  25. package/src/all/background_page/controller/informCallToActionController/informCallToActionController.js +3 -22
  26. package/src/all/background_page/controller/recover/importRecoverPrivateKeyController.js +8 -6
  27. package/src/all/background_page/controller/recover/importRecoverPrivateKeyController.test.js +11 -29
  28. package/src/all/background_page/controller/recover/startRecoverController.js +3 -3
  29. package/src/all/background_page/controller/setup/importSetupPrivateKeyController.js +6 -6
  30. package/src/all/background_page/controller/setup/importSetupPrivateKeyController.test.js +13 -30
  31. package/src/all/background_page/controller/setup/signInSetupController.js +13 -3
  32. package/src/all/background_page/controller/setup/signInSetupController.test.js +13 -6
  33. package/src/all/background_page/controller/setup/startSetupController.js +3 -3
  34. package/src/all/background_page/controller/sso/ssoAuthenticationController.js +13 -3
  35. package/src/all/background_page/controller/sso/ssoAuthenticationController.test.js +34 -27
  36. package/src/all/background_page/controller/{toolbarController.js → toolbarService.js} +8 -58
  37. package/src/all/background_page/controller/{toolbarController.test.js → toolbarService.test.js} +20 -51
  38. package/src/all/background_page/controller/userPassphrasePolicies/saveUserPassphrasePoliciesController.test.js +2 -2
  39. package/src/all/background_page/event/appBootstrapEvents.js +10 -8
  40. package/src/all/background_page/event/appEvents.js +0 -3
  41. package/src/all/background_page/event/authEvents.js +10 -45
  42. package/src/all/background_page/event/informCallToActionEvents.js +4 -8
  43. package/src/all/background_page/event/recoverEvents.js +1 -1
  44. package/src/all/background_page/event/secretEvents.js +0 -13
  45. package/src/all/background_page/event/setupEvents.js +1 -1
  46. package/src/all/background_page/index.js +29 -26
  47. package/src/all/background_page/model/auth/authModel.js +5 -98
  48. package/src/all/background_page/model/auth/authModel.test.js +8 -9
  49. package/src/all/background_page/model/entity/account/accountEntity.test.data.js +3 -4
  50. package/src/all/background_page/model/entity/worker/workerEntity.js +20 -3
  51. package/src/all/background_page/model/gpgAuthHeader.test.data.js +88 -0
  52. package/src/all/background_page/model/user.js +0 -11
  53. package/src/all/background_page/pagemod/appBootstrapPagemod.js +5 -6
  54. package/src/all/background_page/pagemod/appBootstrapPagemod.test.js +4 -3
  55. package/src/all/background_page/pagemod/appPagemod.js +4 -3
  56. package/src/all/background_page/pagemod/appPagemod.test.js +3 -3
  57. package/src/all/background_page/pagemod/pagemodManager.test.js +3 -2
  58. package/src/all/background_page/sdk/port/portManager.js +1 -1
  59. package/src/all/background_page/service/accountRecovery/validateOrganizationPublicKeyService.js +97 -0
  60. package/src/all/background_page/service/{api/accountRecovery/accountRecoveryOrganizationPolicyService.test.data.js → accountRecovery/validateOrganizationPublicKeyService.test.data.js} +1 -1
  61. package/src/all/background_page/service/accountRecovery/validateOrganizationPublicKeyService.test.js +69 -0
  62. package/src/all/background_page/service/alarm/globalAlarmService.js +38 -0
  63. package/src/all/background_page/service/api/accountRecovery/accountRecoveryOrganizationPolicyService.js +0 -75
  64. package/src/all/background_page/service/api/auth/authLoginService.js +82 -0
  65. package/src/all/background_page/service/api/auth/authLoginService.test.js +104 -0
  66. package/src/all/background_page/service/api/auth/authVerifyServerKeyService.js +79 -0
  67. package/src/all/background_page/service/api/auth/authVerifyServerKeyService.test.js +74 -0
  68. package/src/all/background_page/service/auth/authVerifyLoginChallengeService.js +49 -0
  69. package/src/all/background_page/service/auth/authVerifyLoginChallengeService.test.js +59 -0
  70. package/src/all/background_page/service/auth/authVerifyServerChallengeService.js +53 -0
  71. package/src/all/background_page/service/auth/authVerifyServerChallengeService.test.js +71 -0
  72. package/src/all/background_page/service/auth/checkAuthStatusService.js +52 -0
  73. package/src/all/background_page/service/auth/checkAuthStatusService.test.js +123 -0
  74. package/src/all/background_page/service/auth/decryptUserAuthTokenService.js +55 -0
  75. package/src/all/background_page/service/auth/decryptUserAuthTokenService.test.data.js +19 -0
  76. package/src/all/background_page/service/auth/decryptUserAuthTokenService.test.js +79 -0
  77. package/src/all/background_page/service/auth/postLoginService.js +40 -0
  78. package/src/all/background_page/service/auth/postLoginService.test.js +96 -0
  79. package/src/all/background_page/service/auth/postLogoutService.js +45 -0
  80. package/src/all/background_page/service/auth/postLogoutService.test.js +104 -0
  81. package/src/all/background_page/service/auth/startLoopAuthSessionCheckService.js +28 -34
  82. package/src/all/background_page/service/auth/startLoopAuthSessionCheckService.test.js +28 -39
  83. package/src/all/background_page/service/authenticationStatusService.js +71 -0
  84. package/src/all/background_page/service/authenticationStatusService.test.js +58 -0
  85. package/src/all/background_page/service/cache/resourceInProgressCache.service.js +5 -42
  86. package/src/all/background_page/service/cache/resourceInProgressCache.service.test.js +8 -8
  87. package/src/all/background_page/service/extension/onExtensionUpdateAvailableService.js +86 -0
  88. package/src/all/background_page/service/extension/onExtensionUpdateAvailableService.test.js +171 -0
  89. package/src/all/background_page/service/localStorage/localStorageService.js +2 -0
  90. package/src/all/background_page/service/local_storage/AccountLocalStorage.test.js +3 -1
  91. package/src/all/background_page/service/local_storage/authStatusLocalStorage.js +17 -21
  92. package/src/all/background_page/service/passphrase/getPassphraseService.js +5 -1
  93. package/src/all/background_page/service/sessionStorage/userMeSessionStorageService.test.js +10 -3
  94. package/src/all/background_page/service/sessionStorage/workersSessionStorage.js +11 -0
  95. package/src/all/background_page/service/sessionStorage/workersSessionStorage.test.js +3 -2
  96. package/src/all/background_page/service/session_storage/keepSessionAliveService.js +82 -0
  97. package/src/all/background_page/service/session_storage/keepSessionAliveService.test.js +138 -0
  98. package/src/all/background_page/service/session_storage/passphraseStorageService.js +27 -106
  99. package/src/all/background_page/service/session_storage/passphraseStorageService.test.js +24 -64
  100. package/src/all/background_page/service/systemRequirementService/systemRequirementService.js +4 -3
  101. package/src/all/background_page/service/tab/tabService.js +59 -16
  102. package/src/all/background_page/service/tab/tabService.test.js +114 -49
  103. package/src/all/background_page/service/worker/workerService.js +79 -69
  104. package/src/all/background_page/service/worker/workerService.test.js +103 -43
  105. package/src/all/background_page/utils/promise/promiseTimeoutService.js +3 -39
  106. package/src/all/background_page/utils/promise/promiseTimeoutService.test.js +19 -27
  107. package/src/all/contentScripts/js/app/BrowserIntegration.js +7 -0
  108. package/src/all/webAccessibleResources/js/app/App.js +7 -0
  109. package/src/all/webAccessibleResources/js/lib/port.js +16 -1
  110. package/src/all/webAccessibleResources/js/lib/port.test.js +20 -0
  111. package/src/chrome/manifest.json +1 -1
  112. package/src/chrome-mv3/index.js +14 -8
  113. package/src/chrome-mv3/manifest.json +1 -1
  114. package/src/firefox/manifest.json +1 -1
  115. package/src/safari/manifest.json +1 -1
  116. package/test/fixtures/pgpKeys/keys.js +5 -0
  117. package/test/mocks/mockAlarms.js +134 -4
  118. package/test/mocks/mockApiResponse.js +11 -0
  119. package/test/mocks/mockExtension.js +4 -1
  120. package/test/mocks/mockWebExtensionPolyfill.js +1 -0
  121. package/src/all/background_page/controller/auth/authIsAuthenticatedController.js +0 -44
  122. package/src/all/background_page/controller/auth/authenticationEventController.js +0 -67
  123. package/src/all/background_page/controller/extension/onExtensionUpdateAvailableController.js +0 -56
  124. package/src/all/background_page/controller/extension/onExtensionUpdateAvailableController.test.js +0 -76
  125. package/src/all/background_page/model/gpgauth.js +0 -347
  126. package/src/all/background_page/service/api/accountRecovery/accountRecoveryOrganizationPolicyService.test.js +0 -59
  127. package/src/all/background_page/service/api/auth/authService.js +0 -161
  128. package/src/all/background_page/service/api/auth/authService.test.js +0 -103
  129. package/src/all/background_page/service/auth.js +0 -78
@@ -1,6 +1,6 @@
1
1
  build:
2
2
  stage: build
3
- image: node:18
3
+ image: ${CI_DEPENDENCY_PROXY_DIRECT_GROUP_IMAGE_PREFIX}/node:18
4
4
  extends: .rules
5
5
  artifacts:
6
6
  when: always
@@ -22,7 +22,7 @@ build:
22
22
 
23
23
  build_mv3:
24
24
  stage: build
25
- image: node:18
25
+ image: ${CI_DEPENDENCY_PROXY_DIRECT_GROUP_IMAGE_PREFIX}/node:18
26
26
  extends: .rules
27
27
  artifacts:
28
28
  when: always
@@ -39,4 +39,4 @@ build_mv3:
39
39
  echo "Sending slack build notification"
40
40
  echo "================================"
41
41
  bash ./.gitlab-ci/scripts/bin/slack-status-messages.sh ":jigsaw: A new wild MV3 browser extension appeared! $CI_COMMIT_TAG" "$CI_PROJECT_URL/-/jobs/$CI_JOB_ID/artifacts/browse/dist/"
42
- fi
42
+ fi
@@ -36,3 +36,13 @@ publish-edge:
36
36
  echo "Sending slack build notification"
37
37
  echo "================================"
38
38
  bash ./.gitlab-ci/scripts/bin/slack-status-messages.sh ":rocket: passbolt-edge-extension $CI_COMMIT_TAG has been published!" "$CI_PROJECT_URL/-/jobs/$CI_JOB_ID"
39
+
40
+ publish-to-npmjs:
41
+ stage: publish
42
+ image: ${CI_DEPENDENCY_PROXY_DIRECT_GROUP_IMAGE_PREFIX}/node:18
43
+ rules:
44
+ - if: "$CI_COMMIT_TAG"
45
+ script:
46
+ - |
47
+ bash .gitlab-ci/scripts/bin/publish_npm.sh
48
+ bash ./.gitlab-ci/scripts/bin/slack-status-messages.sh ":rocket: passbolt-browser-extension $CI_COMMIT_TAG has been published in https://www.npmjs.com/package/passbolt-browser-extension" "$CI_PROJECT_URL/-/jobs/$CI_JOB_ID"
@@ -1,11 +1,11 @@
1
1
  tester:
2
2
  stage: test
3
- image: node:18
3
+ image: ${CI_DEPENDENCY_PROXY_DIRECT_GROUP_IMAGE_PREFIX}/node:18
4
4
  coverage: /Lines\s* [:] ([\d\.]+)%/
5
5
  extends: .rules
6
6
  script:
7
7
  - npm ci
8
- - npm run test:coverage
8
+ - npm run test:ci:coverage
9
9
  artifacts:
10
10
  when: always
11
11
  reports:
@@ -17,7 +17,7 @@ tester:
17
17
 
18
18
  linter:
19
19
  stage: test
20
- image: node:18
20
+ image: ${CI_DEPENDENCY_PROXY_DIRECT_GROUP_IMAGE_PREFIX}/node:18
21
21
  extends: .rules
22
22
  script:
23
23
  - npm ci
@@ -29,4 +29,4 @@ audit:
29
29
  image: node:18
30
30
  extends: .rules
31
31
  script:
32
- - npm audit
32
+ - npm audit
@@ -0,0 +1,24 @@
1
+ #!/usr/bin/env bash
2
+
3
+ # shellcheck disable=SC1091
4
+
5
+ set -eu
6
+
7
+ CI_SCRIPTS_DIR=$(dirname "$0")/..
8
+
9
+ # shellcheck source=.gitlab-ci/scripts/lib/version-check.sh
10
+ source "$CI_SCRIPTS_DIR"/lib/version-check.sh
11
+
12
+ echo //registry.npmjs.org/:_authToken="$NPM_PUBLISH_TOKEN" > .npmrc
13
+ echo email="$NPM_PUBLISH_EMAIL" >> .npmrc
14
+ echo always-auth=true >> .npmrc
15
+
16
+ if is_release_candidate "$CI_COMMIT_TAG"; then
17
+ npm publish --tag next
18
+ elif is_release_alpha "$CI_COMMIT_TAG"; then
19
+ npm publish --tag alpha
20
+ elif is_release_beta "$CI_COMMIT_TAG"; then
21
+ npm publish --tag beta
22
+ else
23
+ npm publish
24
+ fi
@@ -13,6 +13,22 @@ function is_release_candidate () {
13
13
  return 0
14
14
  }
15
15
 
16
+ function is_release_alpha () {
17
+ local version=$1
18
+ if [[ ! $version =~ [0-9]+\.[0-9]+\.[0-9]+-alpha\.[0-9]+ ]];then
19
+ return 1
20
+ fi
21
+ return 0
22
+ }
23
+
24
+ function is_release_beta () {
25
+ local version=$1
26
+ if [[ ! $version =~ [0-9]+\.[0-9]+\.[0-9]+-beta\.[0-9]+ ]];then
27
+ return 1
28
+ fi
29
+ return 0
30
+ }
31
+
16
32
  function validate_config_version_and_api_tag () {
17
33
  local version_file="$1"
18
34
  local version
package/.gitlab-ci.yml CHANGED
@@ -1,4 +1,4 @@
1
- image: debian:stable-slim
1
+ image: ${CI_DEPENDENCY_PROXY_DIRECT_GROUP_IMAGE_PREFIX}/debian:stable-slim
2
2
 
3
3
  stages:
4
4
  - test
@@ -18,4 +18,4 @@ include:
18
18
  - local: "/.gitlab-ci/jobs/test.yml"
19
19
  - local: "/.gitlab-ci/jobs/review.yml"
20
20
  - local: "/.gitlab-ci/jobs/publish.yml"
21
- - local: ".gitlab-ci/jobs/release.yml"
21
+ - local: ".gitlab-ci/jobs/release.yml"
@@ -0,0 +1,19 @@
1
+ ```mermaid
2
+ stateDiagram
3
+ classDef badBadEvent fill:#f00,color:white,font-weight:bold,stroke-width:2px,stroke:yellow
4
+
5
+ [*] --> awaiting_connection: SW navigation url matching
6
+
7
+ awaiting_connection --> connected: CC opening port
8
+ connected --> disconnected: SW stopped
9
+ disconnected --> reconnecting: SW navigation url same origin
10
+ disconnected --> connected: CC reopening port
11
+ reconnecting --> connected: SW requesting CC port opening ||\nCC reopening port (following a user interaction)
12
+
13
+ awaiting_connection --> [*]: SW tab removed || \n SW navigation url diff origin
14
+ connected --> [*]: SW tab removed || \n SW navigation url diff origin
15
+ disconnected --> [*]: SW tab removed || \n SW navigation url diff origin
16
+ reconnecting --> [*]: SW tab removed || \n Cannot reconnect port || \n SW navigation url diff origin
17
+
18
+ class reconnecting badBadEvent
19
+ ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "passbolt-browser-extension",
3
- "version": "4.6.2",
3
+ "version": "4.7.0-alpha.1",
4
4
  "license": "AGPL-3.0",
5
5
  "copyright": "Copyright 2022 Passbolt SA",
6
6
  "description": "Passbolt web extension for the open source password manager for teams",
@@ -21,7 +21,7 @@
21
21
  "locutus": "~2.0.9",
22
22
  "openpgp": "^5.11.1",
23
23
  "papaparse": "^5.2.0",
24
- "passbolt-styleguide": "^4.6.3",
24
+ "passbolt-styleguide": "^4.7.0-alpha-4",
25
25
  "react": "17.0.2",
26
26
  "react-dom": "17.0.2",
27
27
  "secrets-passbolt": "github:passbolt/secrets.js#v2.0.1",
@@ -109,7 +109,8 @@
109
109
  "lint:eslint-fix": "eslint -c .eslintrc.json --ext js --ext jsx --fix src",
110
110
  "i18n:externalize": "i18next -c ./i18next-parser.config.js",
111
111
  "test": "npm run test:unit",
112
- "test:unit": "jest --no-cache ./src/all/ ./src/chrome-mv3/ --maxWorkers=4",
113
- "test:coverage": "jest --no-cache ./src/all/ ./src/chrome-mv3/ --maxWorkers=4 --coverage"
112
+ "test:unit": "jest --no-cache ./src/all/ ./src/chrome-mv3/",
113
+ "test:coverage": "jest --no-cache ./src/all/ ./src/chrome-mv3/ --coverage",
114
+ "test:ci:coverage": "npm run test:coverage -- --runInBand"
114
115
  }
115
116
  }
@@ -19,6 +19,7 @@ import SsoDataStorage from "../../service/indexedDB_storage/ssoDataStorage";
19
19
  import SsoKitServerPartModel from "../../model/sso/ssoKitServerPartModel";
20
20
  import PassboltApiFetchError from "passbolt-styleguide/src/shared/lib/Error/PassboltApiFetchError";
21
21
  import GenerateSsoKitService from "../../service/sso/generateSsoKitService";
22
+ import KeepSessionAliveService from "../../service/session_storage/keepSessionAliveService";
22
23
 
23
24
  const RECOVERY_KIT_FILENAME = "passbolt-recovery-kit.asc";
24
25
 
@@ -72,7 +73,7 @@ class UpdatePrivateKeyController {
72
73
  }
73
74
  await this.accountModel.updatePrivateKey(userPrivateArmoredKey);
74
75
  await PassphraseStorageService.flushPassphrase();
75
- if (PassphraseStorageService.isSessionKeptUntilLogOut()) {
76
+ if (KeepSessionAliveService.isStarted()) {
76
77
  await PassphraseStorageService.set(newPassphrase);
77
78
  }
78
79
  await FileService.saveFile(RECOVERY_KIT_FILENAME, userPrivateArmoredKey, "text/plain", this.worker.tab.id);
@@ -12,14 +12,15 @@
12
12
  * @since 3.6.0
13
13
  */
14
14
 
15
- import AccountRecoveryOrganizationPolicyService from "../../service/api/accountRecovery/accountRecoveryOrganizationPolicyService";
16
15
  import AccountRecoveryModel from "../../model/accountRecovery/accountRecoveryModel";
16
+ import ValidateOrganizationPublicKeyService from "../../service/accountRecovery/validateOrganizationPublicKeyService";
17
17
 
18
18
  class AccountRecoveryValidatePublicKeyController {
19
19
  constructor(worker, requestId, apiClientOptions) {
20
20
  this.worker = worker;
21
21
  this.requestId = requestId;
22
22
  this.accountRecoveryModel = new AccountRecoveryModel(apiClientOptions);
23
+ this.validateOrganizationPublicKeyService = new ValidateOrganizationPublicKeyService(apiClientOptions);
23
24
  }
24
25
 
25
26
  /**
@@ -46,7 +47,7 @@ class AccountRecoveryValidatePublicKeyController {
46
47
  */
47
48
  async exec(publicKeyToValidate) {
48
49
  const organizationPolicy = await this.accountRecoveryModel.findOrganizationPolicy();
49
- await AccountRecoveryOrganizationPolicyService.validatePublicKey(publicKeyToValidate, organizationPolicy.armoredKey);
50
+ await this.validateOrganizationPublicKeyService.validatePublicKey(publicKeyToValidate, organizationPolicy.armoredKey);
50
51
  }
51
52
  }
52
53
 
@@ -63,7 +63,7 @@ describe("AccountRecoveryValidatePublicKeyController", () => {
63
63
  {key: pgpKeys.account_recovery_organization_alternative.public, expectedError: new Error("The key is already being used, the organization recovery key must be a new one.")},
64
64
  {key: pgpKeys.betty.public, expectedError: new Error("The key is the current organization recovery key, you must provide a new one.")},
65
65
  ]).describe("Should throw an error when the key cannot be validated", scenario => {
66
- it(`Should throw an error whith the scenario: ${scenario.expectedError.message}`, async() => {
66
+ it(`Should throw an error with the scenario: ${scenario.expectedError.message}`, async() => {
67
67
  expect.assertions(1);
68
68
  mockFetch();
69
69
 
@@ -147,6 +147,7 @@ class RecoverAccountController {
147
147
  OpenpgpAssertion.assertPrivateKey(recoveredPrivateKey);
148
148
  this.account.userPrivateArmoredKey = recoveredPrivateKey.armor();
149
149
  this.account.userPublicArmoredKey = recoveredPrivateKey.toPublic().armor();
150
+ this.account.userKeyFingerprint = recoveredPrivateKey.getFingerprint().toUpperCase();
150
151
  await this.setupModel.completeRecover(this.account);
151
152
  }
152
153
 
@@ -178,6 +179,7 @@ class RecoverAccountController {
178
179
  _updateWorkerAccount(account) {
179
180
  this.account.userPublicArmoredKey = account.userPublicArmoredKey;
180
181
  this.account.userPrivateArmoredKey = account.userPrivateArmoredKey;
182
+ this.account.userKeyFingerprint = account.userKeyFingerprint;
181
183
  }
182
184
 
183
185
  /**
@@ -243,7 +243,7 @@ describe("ReviewRequestController", () => {
243
243
 
244
244
  it("Should assert the public key of the user making the account recovery is found.", async() => {
245
245
  expect.assertions(1);
246
- await MockExtension.withConfiguredAccount();
246
+ await MockExtension.withConfiguredAccount(pgpKeys.betty);
247
247
  // Mock API fetch account recovery organization policy response.
248
248
  fetch.doMockOnce(() => mockApiResponse(enabledAccountRecoveryOrganizationPolicyDto()));
249
249
  // Mock API get account recovery request.
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Passbolt ~ Open source password manager for teams
3
+ * Copyright (c) Passbolt SA (https://www.passbolt.com)
4
+ *
5
+ * Licensed under GNU Affero General Public License version 3 of the or any later version.
6
+ * For full copyright and license information, please see the LICENSE.txt
7
+ * Redistributions of files must retain the above copyright notice.
8
+ *
9
+ * @copyright Copyright (c) Passbolt SA (https://www.passbolt.com)
10
+ * @license https://opensource.org/licenses/AGPL-3.0 AGPL License
11
+ * @link https://www.passbolt.com Passbolt(tm)
12
+ * @since 4.7.0
13
+ */
14
+
15
+ export const userLoggedOutAuthStatus = () => ({
16
+ isAuthenticated: false,
17
+ isMfaRequired: false,
18
+ });
19
+
20
+ export const userLoggedInAuthStatus = () => ({
21
+ isAuthenticated: true,
22
+ isMfaRequired: false,
23
+ });
24
+
25
+ export const userRequireMfaAuthStatus = () => ({
26
+ isAuthenticated: true,
27
+ isMfaRequired: true,
28
+ });
@@ -11,24 +11,38 @@
11
11
  * @link https://www.passbolt.com Passbolt(tm)
12
12
  * @since 2.11.0
13
13
  */
14
- import GpgAuth from "../../model/gpgauth";
14
+ import CheckAuthStatusService from "../../service/auth/checkAuthStatusService";
15
15
 
16
16
  class AuthCheckStatusController {
17
17
  constructor(worker, requestId) {
18
18
  this.worker = worker;
19
19
  this.requestId = requestId;
20
- this.auth = new GpgAuth();
20
+ this.checkAuthStatusService = new CheckAuthStatusService();
21
21
  }
22
22
 
23
- async main() {
23
+ /**
24
+ * Controller executor.
25
+ * @param {boolean} [flushCache = true] should the cache be flushed before
26
+ * @returns {Promise<void>}
27
+ */
28
+ async _exec(flushCache = true) {
24
29
  try {
25
- const status = await this.auth.checkAuthStatus();
26
- this.worker.port.emit(this.requestId, 'SUCCESS', status);
30
+ const authStatus = await this.exec(flushCache);
31
+ this.worker.port.emit(this.requestId, 'SUCCESS', authStatus);
27
32
  } catch (error) {
28
33
  console.error(error);
29
34
  this.worker.port.emit(this.requestId, 'ERROR', error);
30
35
  }
31
36
  }
37
+
38
+ /**
39
+ * Controller executor.
40
+ * @param {boolean} flushCache should the cache be flushed before
41
+ * @returns {Promise<{isAuthenticated: {bool}, isMfaRequired: {bool}}>}
42
+ */
43
+ async exec(flushCache) {
44
+ return await this.checkAuthStatusService.checkAuthStatus(flushCache);
45
+ }
32
46
  }
33
47
 
34
48
  export default AuthCheckStatusController;
@@ -0,0 +1,94 @@
1
+ /**
2
+ * Passbolt ~ Open source password manager for teams
3
+ * Copyright (c) Passbolt SA (https://www.passbolt.com)
4
+ *
5
+ * Licensed under GNU Affero General Public License version 3 of the or any later version.
6
+ * For full copyright and license information, please see the LICENSE.txt
7
+ * Redistributions of files must retain the above copyright notice.
8
+ *
9
+ * @copyright Copyright (c) Passbolt SA (https://www.passbolt.com)
10
+ * @license https://opensource.org/licenses/AGPL-3.0 AGPL License
11
+ * @link https://www.passbolt.com Passbolt(tm)
12
+ * @since 4.7.0
13
+ */
14
+
15
+ import MfaAuthenticationRequiredError from "../../error/mfaAuthenticationRequiredError";
16
+ import AuthenticationStatusService from "../../service/authenticationStatusService";
17
+ import AuthStatusLocalStorage from "../../service/local_storage/authStatusLocalStorage";
18
+ import AuthCheckStatusController from "./authCheckStatusController";
19
+
20
+ beforeEach(() => {
21
+ jest.clearAllMocks();
22
+ });
23
+
24
+ describe("AuthCheckStatusController", () => {
25
+ it("should return the auth status matching the unauthanticated state", async() => {
26
+ expect.assertions(3);
27
+ jest.spyOn(AuthStatusLocalStorage, "get").mockImplementation(() => undefined);
28
+ jest.spyOn(AuthStatusLocalStorage, "flush");
29
+ jest.spyOn(AuthenticationStatusService, "isAuthenticated").mockImplementation(() => false);
30
+
31
+ const controller = new AuthCheckStatusController();
32
+ const authStatus = await controller.exec(true);
33
+
34
+ expect(AuthStatusLocalStorage.get).not.toHaveBeenCalled();
35
+ expect(AuthStatusLocalStorage.flush).not.toHaveBeenCalled();
36
+ expect(authStatus).toStrictEqual({
37
+ isAuthenticated: false,
38
+ isMfaRequired: false,
39
+ });
40
+ });
41
+
42
+ it("expects the user to be fully authenticated", async() => {
43
+ expect.assertions(3);
44
+ jest.spyOn(AuthStatusLocalStorage, "get").mockImplementation(() => undefined);
45
+ jest.spyOn(AuthStatusLocalStorage, "flush");
46
+ jest.spyOn(AuthenticationStatusService, "isAuthenticated").mockImplementation(() => true);
47
+
48
+ const controller = new AuthCheckStatusController();
49
+ const authStatus = await controller.exec(true);
50
+
51
+ expect(AuthStatusLocalStorage.get).not.toHaveBeenCalled();
52
+ expect(AuthStatusLocalStorage.flush).not.toHaveBeenCalled();
53
+ expect(authStatus).toStrictEqual({
54
+ isAuthenticated: true,
55
+ isMfaRequired: false,
56
+ });
57
+ });
58
+
59
+ it("expects the user to require MFA authentication", async() => {
60
+ expect.assertions(3);
61
+ jest.spyOn(AuthStatusLocalStorage, "get").mockImplementation(() => undefined);
62
+ jest.spyOn(AuthStatusLocalStorage, "flush");
63
+ jest.spyOn(AuthenticationStatusService, "isAuthenticated").mockImplementation(() => { throw new MfaAuthenticationRequiredError(); });
64
+
65
+ const controller = new AuthCheckStatusController();
66
+ const authStatus = await controller.exec(true);
67
+
68
+ expect(AuthStatusLocalStorage.get).not.toHaveBeenCalled();
69
+ expect(AuthStatusLocalStorage.flush).not.toHaveBeenCalled();
70
+ expect(authStatus).toStrictEqual({
71
+ isAuthenticated: true,
72
+ isMfaRequired: true,
73
+ });
74
+ });
75
+
76
+ it("should return the auth status from the local storage", async() => {
77
+ expect.assertions(4);
78
+ const expectedAuthStatus = {
79
+ isAuthenticated: false,
80
+ isMfaRequired: false,
81
+ };
82
+ jest.spyOn(AuthStatusLocalStorage, "get").mockImplementation(() => expectedAuthStatus);
83
+ jest.spyOn(AuthStatusLocalStorage, "flush");
84
+ jest.spyOn(AuthenticationStatusService, "isAuthenticated");
85
+
86
+ const controller = new AuthCheckStatusController();
87
+ const authStatus = await controller.exec(false);
88
+
89
+ expect(AuthStatusLocalStorage.get).toHaveBeenCalledTimes(1);
90
+ expect(AuthStatusLocalStorage.flush).not.toHaveBeenCalled();
91
+ expect(AuthenticationStatusService.isAuthenticated).not.toHaveBeenCalled();
92
+ expect(authStatus).toStrictEqual(expectedAuthStatus);
93
+ });
94
+ });
@@ -11,27 +11,35 @@
11
11
  * @link https://www.passbolt.com Passbolt(tm)
12
12
  * @since 2.11.0
13
13
  */
14
- import GpgAuth from "../../model/gpgauth";
14
+ import CheckAuthStatusService from "../../service/auth/checkAuthStatusService";
15
15
 
16
16
  class AuthIsMfaRequiredController {
17
17
  constructor(worker, requestId) {
18
18
  this.worker = worker;
19
19
  this.requestId = requestId;
20
- this.auth = new GpgAuth();
20
+ this.checkAuthStatusService = new CheckAuthStatusService();
21
21
  }
22
22
 
23
- async main() {
23
+ /**
24
+ * Execute the controller.
25
+ */
26
+ async _exec() {
24
27
  try {
25
- const isMfaRequired = await this.auth.isMfaRequired();
26
- if (isMfaRequired) {
27
- this.worker.port.emit(this.requestId, 'SUCCESS', true);
28
- } else {
29
- this.worker.port.emit(this.requestId, 'SUCCESS', false);
30
- }
28
+ const isMfaRequired = await this.exec();
29
+ this.worker.port.emit(this.requestId, 'SUCCESS', isMfaRequired);
31
30
  } catch (error) {
32
31
  this.worker.port.emit(this.requestId, 'ERROR', error);
33
32
  }
34
33
  }
34
+
35
+ /**
36
+ * Returns true if the current user needs to answer the MFA challenge to finish sign in
37
+ * @returns {Promise<boolean>}
38
+ */
39
+ async exec() {
40
+ const authStatus = await this.checkAuthStatusService.checkAuthStatus(true);
41
+ return authStatus.isMfaRequired;
42
+ }
35
43
  }
36
44
 
37
45
  export default AuthIsMfaRequiredController;
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Passbolt ~ Open source password manager for teams
3
+ * Copyright (c) Passbolt SA (https://www.passbolt.com)
4
+ *
5
+ * Licensed under GNU Affero General Public License version 3 of the or any later version.
6
+ * For full copyright and license information, please see the LICENSE.txt
7
+ * Redistributions of files must retain the above copyright notice.
8
+ *
9
+ * @copyright Copyright (c) Passbolt SA (https://www.passbolt.com)
10
+ * @license https://opensource.org/licenses/AGPL-3.0 AGPL License
11
+ * @link https://www.passbolt.com Passbolt(tm)
12
+ * @since 4.7.0
13
+ */
14
+
15
+ import {userLoggedInAuthStatus, userLoggedOutAuthStatus, userRequireMfaAuthStatus} from "./authCheckStatus.test.data";
16
+ import AuthIsMfaRequiredController from "./authIsMfaRequiredController";
17
+
18
+ beforeEach(() => {
19
+ jest.clearAllMocks();
20
+ });
21
+
22
+ describe("AuthIsMfaRequiredController", () => {
23
+ it("should return true if the user needs to authenticate with MFA", async() => {
24
+ expect.assertions(1);
25
+
26
+ const controller = new AuthIsMfaRequiredController();
27
+ jest.spyOn(controller.checkAuthStatusService, "checkAuthStatus").mockImplementation(async() => userRequireMfaAuthStatus());
28
+
29
+ const isMfaRequired = await controller.exec();
30
+ expect(isMfaRequired).toStrictEqual(true);
31
+ });
32
+
33
+ it("should return false if the user does not need to authenticate with MFA", async() => {
34
+ expect.assertions(1);
35
+
36
+ const controller = new AuthIsMfaRequiredController();
37
+ jest.spyOn(controller.checkAuthStatusService, "checkAuthStatus").mockImplementation(async() => userLoggedInAuthStatus());
38
+
39
+ const isMfaRequired = await controller.exec();
40
+ expect(isMfaRequired).toStrictEqual(false);
41
+ });
42
+
43
+ it("should return the MFA status part of the authentication status", async() => {
44
+ expect.assertions(1);
45
+
46
+ const controller = new AuthIsMfaRequiredController();
47
+
48
+ const authStatus = userLoggedOutAuthStatus();
49
+ jest.spyOn(controller.checkAuthStatusService, "checkAuthStatus").mockImplementation(async() => authStatus);
50
+
51
+ const isMfaRequired = await controller.exec();
52
+ expect(isMfaRequired).toStrictEqual(authStatus.isMfaRequired);
53
+ });
54
+ });
@@ -11,13 +11,16 @@
11
11
  * @link https://www.passbolt.com Passbolt(tm)
12
12
  * @since 3.9.0
13
13
  */
14
- import AuthModel from "../../model/auth/authModel";
15
14
  import UserAlreadyLoggedInError from "../../error/userAlreadyLoggedInError";
16
15
  import Keyring from "../../model/keyring";
17
16
  import CheckPassphraseService from "../../service/crypto/checkPassphraseService";
18
17
  import UpdateSsoCredentialsService from "../../service/account/updateSsoCredentialsService";
19
18
  import UserRememberMeLatestChoiceLocalStorage from "../../service/local_storage/userRememberMeLatestChoiceLocalStorage";
20
19
  import UserRememberMeLatestChoiceEntity from "../../model/entity/rememberMe/userRememberMeLatestChoiceEntity";
20
+ import PassphraseStorageService from "../../service/session_storage/passphraseStorageService";
21
+ import PostLoginService from "../../service/auth/postLoginService";
22
+ import AuthVerifyLoginChallengeService from "../../service/auth/authVerifyLoginChallengeService";
23
+ import KeepSessionAliveService from "../../service/session_storage/keepSessionAliveService";
21
24
 
22
25
  class AuthLoginController {
23
26
  /**
@@ -31,7 +34,7 @@ class AuthLoginController {
31
34
  this.worker = worker;
32
35
  this.requestId = requestId;
33
36
  this.account = account;
34
- this.authModel = new AuthModel(apiClientOptions);
37
+ this.authVerifyLoginChallengeService = new AuthVerifyLoginChallengeService(apiClientOptions);
35
38
  this.updateSsoCredentialsService = new UpdateSsoCredentialsService(apiClientOptions);
36
39
  this.checkPassphraseService = new CheckPassphraseService(new Keyring());
37
40
  this.userRememberMeLatestChoiceLocalStorage = new UserRememberMeLatestChoiceLocalStorage(account);
@@ -40,9 +43,9 @@ class AuthLoginController {
40
43
  /**
41
44
  * Wrapper of exec function to run it with worker.
42
45
  *
43
- * @param {uuid} requestId The request identifier
44
46
  * @param {string} passphrase The passphrase to decryt the private key
45
- * @param {boolean} rememberMe whether to remember the passphrase or not
47
+ * @param {boolean} remember whether to remember the passphrase or not
48
+ * @param {boolean} shouldRefreshCurrentTab should refresh the current tab
46
49
  * @return {Promise<void>}
47
50
  */
48
51
  async _exec(passphrase, remember, shouldRefreshCurrentTab = false) {
@@ -59,11 +62,9 @@ class AuthLoginController {
59
62
  * Attemps to sign in the current user.
60
63
  *
61
64
  * @param {string} passphrase The passphrase to decryt the private key
62
- * @param {string} rememberMe whether to remember the passphrase
65
+ * @param {boolean} rememberMe whether to remember the passphrase
63
66
  * @param {boolean} shouldRefreshCurrentTab Should the controller calls for a refresh of the current running tab, default false
64
67
  * (bool) false|undefined if should not remember
65
- * (integer) -1 if should remember for the session
66
- * (integer) duration in seconds to specify a specific duration
67
68
  * @return {Promise<void>}
68
69
  */
69
70
  async exec(passphrase, rememberMe, shouldRefreshCurrentTab) {
@@ -93,7 +94,18 @@ class AuthLoginController {
93
94
  }
94
95
 
95
96
  try {
96
- await this.authModel.login(passphrase, rememberMe);
97
+ await this.authVerifyLoginChallengeService.verifyAndValidateLoginChallenge(this.account.userKeyFingerprint, this.account.userPrivateArmoredKey, passphrase);
98
+ /*
99
+ * Post login operations
100
+ * MFA may not be complete yet, so no need to preload things here
101
+ */
102
+ if (rememberMe) {
103
+ await Promise.all([
104
+ PassphraseStorageService.set(passphrase, -1),
105
+ KeepSessionAliveService.start(),
106
+ ]);
107
+ }
108
+ await PostLoginService.exec();
97
109
  await this.registerRememberMeOption(rememberMe);
98
110
  } catch (error) {
99
111
  if (!(error instanceof UserAlreadyLoggedInError)) {