passbolt-browser-extension 5.3.2 → 5.4.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 (173) hide show
  1. package/CHANGELOG.md +58 -1
  2. package/README.md +2 -2
  3. package/RELEASE_NOTES.md +8 -30
  4. package/crowdin.yml +1 -0
  5. package/package.json +4 -3
  6. package/src/all/_locales/cs/messages.json +10 -0
  7. package/src/all/background_page/controller/InformMenuController/InformMenuController.js +3 -3
  8. package/src/all/background_page/controller/auth/redirectPostLoginController.js +57 -0
  9. package/src/all/background_page/controller/auth/redirectPostLoginController.test.js +82 -0
  10. package/src/all/background_page/controller/auth/redirectToAdminWorkspaceController.js +50 -0
  11. package/src/all/background_page/controller/auth/redirectToAdminWorkspaceController.test.js +45 -0
  12. package/src/all/background_page/controller/comment/createCommentController.js +3 -3
  13. package/src/all/background_page/controller/comment/createCommentController.test.js +5 -5
  14. package/src/all/background_page/controller/comment/deleteCommentController.js +3 -3
  15. package/src/all/background_page/controller/comment/deleteCommentController.test.js +3 -3
  16. package/src/all/background_page/controller/comment/getCommentsByRessourceIdController.js +3 -3
  17. package/src/all/background_page/controller/comment/getCommentsByRessourceidController.test.js +2 -2
  18. package/src/all/background_page/controller/import/importResourcesFileController.test.js +23 -23
  19. package/src/all/background_page/controller/metadata/enableEncryptedMetadataForExistingInstanceController.js +54 -0
  20. package/src/all/background_page/controller/metadata/enableEncryptedMetadataForExistingInstanceController.test.js +54 -0
  21. package/src/all/background_page/controller/metadata/enableMetadataSetupSettingsController.js +54 -0
  22. package/src/all/background_page/controller/metadata/enableMetadataSetupSettingsController.test.js +64 -0
  23. package/src/all/background_page/controller/metadata/findAllNonDeletedMetadataKeysController.js +2 -3
  24. package/src/all/background_page/controller/metadata/findMetadataGettingStartedSettingsController.js +50 -0
  25. package/src/all/background_page/controller/metadata/findMetadataGettingStartedSettingsController.test.js +33 -0
  26. package/src/all/background_page/controller/metadata/findMetadataSetupSettingsController.js +50 -0
  27. package/src/all/background_page/controller/metadata/findMetadataSetupSettingsController.test.js +42 -0
  28. package/src/all/background_page/controller/metadata/keepCleartextMetadataForExistingInstanceController.js +51 -0
  29. package/src/all/background_page/controller/metadata/keepCleartextMetadataForExistingInstanceController.test.js +47 -0
  30. package/src/all/background_page/controller/permission/FindAcoPermissionsForDisplayController.js +1 -0
  31. package/src/all/background_page/controller/quickaccess/consumeInProgressCreationResourceController.js +53 -0
  32. package/src/all/background_page/controller/quickaccess/consumeInProgressCreationResourceController.test.js +40 -0
  33. package/src/all/background_page/controller/quickaccess/prepareResourceController.js +64 -0
  34. package/src/all/background_page/controller/quickaccess/prepareResourceController.test.js +73 -0
  35. package/src/all/background_page/controller/resource/resourceDeleteController.js +67 -0
  36. package/src/all/background_page/controller/resource/resourceDeleteController.test.js +114 -0
  37. package/src/all/background_page/controller/resourceLocalStorage/resourceUpdateLocalStorageController.js +1 -1
  38. package/src/all/background_page/controller/resourceLocalStorage/resourceUpdateLocalStorageController.test.js +5 -1
  39. package/src/all/background_page/controller/setup/signInSetupController.js +0 -10
  40. package/src/all/background_page/controller/setup/signInSetupController.test.js +11 -12
  41. package/src/all/background_page/controller/sso/saveSsoSettingsAsDraftController.test.data.js +1 -0
  42. package/src/all/background_page/controller/webIntegration/webIntegrationController.js +1 -1
  43. package/src/all/background_page/event/appEvents.js +47 -0
  44. package/src/all/background_page/event/authEvents.js +4 -8
  45. package/src/all/background_page/event/informMenuEvents.js +31 -0
  46. package/src/all/background_page/event/quickAccessEvents.js +18 -23
  47. package/src/all/background_page/event/recoverEvents.js +12 -0
  48. package/src/all/background_page/event/resourceEvents.js +4 -11
  49. package/src/all/background_page/event/setupEvents.js +55 -0
  50. package/src/all/background_page/model/comment/{commentModel.js → commentService.js} +6 -2
  51. package/src/all/background_page/model/comment/commentService.test.js +98 -0
  52. package/src/all/background_page/model/entity/actionLog/actionLogsCollection.js +3 -3
  53. package/src/all/background_page/model/entity/actionLog/permissionsUpdatedActionLogEntity.js +4 -0
  54. package/src/all/background_page/model/entity/import/importResourcesFileEntity.test.data.js +3 -3
  55. package/src/all/background_page/model/entity/organizationSettings/organizationSettingsEntity.test.data.js +4 -0
  56. package/src/all/background_page/model/entity/permission/actionLog/updatedPermissionEntity.test.data.js +23 -0
  57. package/src/all/background_page/model/entity/permission/actionLog/updatedPermissionEntity.test.js +18 -33
  58. package/src/all/background_page/model/entity/permission/actionLog/updatedPermissionsCollection.js +71 -2
  59. package/src/all/background_page/model/entity/permission/actionLog/updatedPermissionsCollection.test.js +204 -0
  60. package/src/all/background_page/model/entity/permission/permissionsCollection.js +78 -0
  61. package/src/all/background_page/model/entity/permission/permissionsCollection.test.js +139 -7
  62. package/src/all/background_page/model/entity/plaintext/plaintextEntity.js +9 -0
  63. package/src/all/background_page/model/entity/resource/external/externalResourceEntity.js +65 -8
  64. package/src/all/background_page/model/entity/resource/external/externalResourceEntity.test.data.js +5 -4
  65. package/src/all/background_page/model/entity/resource/external/externalResourceEntity.test.js +72 -16
  66. package/src/all/background_page/model/entity/resource/external/externalResourcesCollection.test.js +2 -1
  67. package/src/all/background_page/model/entity/totp/externalTotpEntity.js +2 -2
  68. package/src/all/background_page/model/entity/totp/totpEntity.test.js +1 -1
  69. package/src/all/background_page/model/export/resources/csvRowComposer/csv1PasswordRowComposer.test.js +2 -2
  70. package/src/all/background_page/model/export/resources/csvRowComposer/csv1passwordRowComposer.js +5 -1
  71. package/src/all/background_page/model/export/resources/csvRowComposer/csvBitWardenRowComposer.js +9 -1
  72. package/src/all/background_page/model/export/resources/csvRowComposer/csvBitWardenRowComposer.test.js +6 -4
  73. package/src/all/background_page/model/export/resources/csvRowComposer/csvChromiumRowComposer.js +5 -1
  74. package/src/all/background_page/model/export/resources/csvRowComposer/csvChromiumRowComposer.test.js +3 -2
  75. package/src/all/background_page/model/export/resources/csvRowComposer/csvDashlaneRowComposer.js +5 -1
  76. package/src/all/background_page/model/export/resources/csvRowComposer/csvDashlaneRowComposer.test.js +2 -3
  77. package/src/all/background_page/model/export/resources/csvRowComposer/csvKdbxRowComposer.js +3 -1
  78. package/src/all/background_page/model/export/resources/csvRowComposer/csvKdbxRowComposer.test.js +6 -4
  79. package/src/all/background_page/model/export/resources/csvRowComposer/csvLastPassRowComposer.js +5 -1
  80. package/src/all/background_page/model/export/resources/csvRowComposer/csvLastPassRowComposer.test.js +2 -3
  81. package/src/all/background_page/model/export/resources/csvRowComposer/csvLogMeOnceRowComposer.js +5 -1
  82. package/src/all/background_page/model/export/resources/csvRowComposer/csvLogMeOnceRowComposer.test.js +2 -3
  83. package/src/all/background_page/model/export/resources/csvRowComposer/csvMozillaPlatformRowComposer.js +6 -2
  84. package/src/all/background_page/model/export/resources/csvRowComposer/csvMozillaPlatformRowComposer.test.js +2 -2
  85. package/src/all/background_page/model/export/resources/csvRowComposer/csvNordpassRowComposer.js +5 -1
  86. package/src/all/background_page/model/export/resources/csvRowComposer/csvNordpassRowComposer.test.js +2 -2
  87. package/src/all/background_page/model/export/resources/csvRowComposer/csvSafariRowComposer.js +5 -1
  88. package/src/all/background_page/model/export/resources/csvRowComposer/csvSafariRowComposer.test.js +2 -2
  89. package/src/all/background_page/model/export/resources/resourcesKdbxExporter.js +44 -2
  90. package/src/all/background_page/model/export/resources/resourcesKdbxExporter.test.js +24 -3
  91. package/src/all/background_page/model/import/resources/csvRowParser/abstractCsvRowParser.js +1 -1
  92. package/src/all/background_page/model/import/resources/csvRowParser/csv1PasswordRowParser.js +5 -3
  93. package/src/all/background_page/model/import/resources/csvRowParser/csv1PasswordRowParser.test.js +3 -2
  94. package/src/all/background_page/model/import/resources/csvRowParser/csvBitWardenRowParser.js +22 -3
  95. package/src/all/background_page/model/import/resources/csvRowParser/csvBitWardenRowParser.test.js +92 -4
  96. package/src/all/background_page/model/import/resources/csvRowParser/csvChromiumRowParser.js +4 -2
  97. package/src/all/background_page/model/import/resources/csvRowParser/csvChromiumRowParser.test.js +2 -2
  98. package/src/all/background_page/model/import/resources/csvRowParser/csvDashlaneRowParser.js +4 -2
  99. package/src/all/background_page/model/import/resources/csvRowParser/csvDashlaneRowParser.test.js +3 -2
  100. package/src/all/background_page/model/import/resources/csvRowParser/csvKdbxRowParser.js +5 -3
  101. package/src/all/background_page/model/import/resources/csvRowParser/csvKdbxRowParser.test.js +4 -4
  102. package/src/all/background_page/model/import/resources/csvRowParser/csvLastPassRowParser.js +4 -2
  103. package/src/all/background_page/model/import/resources/csvRowParser/csvLastPassRowParser.test.js +2 -2
  104. package/src/all/background_page/model/import/resources/csvRowParser/csvLogMeOnceRowParser.js +5 -2
  105. package/src/all/background_page/model/import/resources/csvRowParser/csvLogMeOnceRowParser.test.js +2 -2
  106. package/src/all/background_page/model/import/resources/csvRowParser/csvMozillaPlatformRowParser.js +5 -3
  107. package/src/all/background_page/model/import/resources/csvRowParser/csvMozillaPlatformRowParser.test.js +2 -2
  108. package/src/all/background_page/model/import/resources/csvRowParser/csvNordpassRowParser.js +4 -2
  109. package/src/all/background_page/model/import/resources/csvRowParser/csvNordpassRowParser.test.js +2 -2
  110. package/src/all/background_page/model/import/resources/csvRowParser/csvSafariRowParser.js +4 -2
  111. package/src/all/background_page/model/import/resources/csvRowParser/csvSafariRowParser.test.js +2 -2
  112. package/src/all/background_page/model/import/resources/kdbx/kdbx-custom-fields-with-uris.kdbx +0 -0
  113. package/src/all/background_page/model/import/resources/kdbx/kdbx-multiple-uris-with-33-entries.kdbx +0 -0
  114. package/src/all/background_page/model/import/resources/kdbx/kdbx-multiple-uris.kdbx +0 -0
  115. package/src/all/background_page/model/import/resources/kdbx/kdbx-with-protected-custom-fields.kdbx +0 -0
  116. package/src/all/background_page/model/import/resources/resourcesCsvImportParser.test.js +1 -1
  117. package/src/all/background_page/model/import/resources/resourcesKdbxImportParser.js +124 -41
  118. package/src/all/background_page/model/import/resources/resourcesKdbxImportParser.test.js +133 -1
  119. package/src/all/background_page/model/import/resourcesImportParser.test.js +0 -1
  120. package/src/all/background_page/model/resource/resourceModel.js +0 -68
  121. package/src/all/background_page/service/api/comment/commentApiService.test.js +1 -1
  122. package/src/all/background_page/service/api/metadata/metadataSetupSettingsApiService.js +40 -0
  123. package/src/all/background_page/service/api/metadata/metadataSetupSettingsApiService.test.js +54 -0
  124. package/src/all/background_page/service/api/setup/setupService.js +2 -2
  125. package/src/all/background_page/service/api/setup/setupService.test.js +132 -0
  126. package/src/all/background_page/service/local_storage/resourceLocalStorage.js +25 -1
  127. package/src/all/background_page/service/local_storage/resourceLocalStorage.test.js +54 -0
  128. package/src/all/background_page/service/metadata/configureMetadataSettingsService.js +100 -0
  129. package/src/all/background_page/service/metadata/configureMetadataSettingsService.test.js +265 -0
  130. package/src/all/background_page/service/metadata/createMetadataKeyService.js +1 -1
  131. package/src/all/background_page/service/metadata/decryptMetadataPrivateKeysService.js +3 -19
  132. package/src/all/background_page/service/metadata/decryptMetadataService.js +5 -3
  133. package/src/all/background_page/service/metadata/decryptMetadataService.test.js +31 -24
  134. package/src/all/background_page/service/metadata/encryptMetadataService.js +2 -18
  135. package/src/all/background_page/service/metadata/findAndUpdateMetadataKeysSessionStorageService.js +5 -2
  136. package/src/all/background_page/service/metadata/findAndUpdateMetadataKeysSessionStorageService.test.js +4 -6
  137. package/src/all/background_page/service/metadata/findMetadataKeysService.js +8 -12
  138. package/src/all/background_page/service/metadata/findMetadataKeysService.test.js +21 -47
  139. package/src/all/background_page/service/metadata/findMetadataSetupSettingsService.js +45 -0
  140. package/src/all/background_page/service/metadata/findMetadataSetupSettingsService.test.js +68 -0
  141. package/src/all/background_page/service/metadata/generateMetadataKeyService.js +1 -1
  142. package/src/all/background_page/service/metadata/verifyOrTrustMetadataKeyService.test.js +16 -0
  143. package/src/all/background_page/service/passphrase/getPassphraseService.js +13 -0
  144. package/src/all/background_page/service/permission/findPermissionsService.js +3 -1
  145. package/src/all/background_page/service/resource/delete/deleteResourceService.js +60 -0
  146. package/src/all/background_page/service/resource/delete/deleteResourceService.test.js +75 -0
  147. package/src/all/background_page/service/resource/export/exportResourcesService.js +22 -0
  148. package/src/all/background_page/service/resource/export/exportResourcesService.test.js +48 -1
  149. package/src/all/background_page/service/resource/import/ImportResourcesService.js +34 -3
  150. package/src/all/background_page/service/resource/import/ImportResourcesService.test.js +55 -13
  151. package/src/all/background_page/service/sessionKey/decryptSessionKeysBundlesService.js +2 -18
  152. package/src/all/background_page/service/sessionKey/encryptSessionKeysBundlesService.js +1 -17
  153. package/src/all/locales/cs-CZ/common.json +130 -0
  154. package/src/all/locales/de-DE/common.json +11 -5
  155. package/src/all/locales/en-UK/common.json +6 -0
  156. package/src/all/locales/es-ES/common.json +6 -0
  157. package/src/all/locales/fr-FR/common.json +6 -0
  158. package/src/all/locales/it-IT/common.json +6 -0
  159. package/src/all/locales/ja-JP/common.json +6 -0
  160. package/src/all/locales/ko-KR/common.json +6 -0
  161. package/src/all/locales/lt-LT/common.json +6 -0
  162. package/src/all/locales/nl-NL/common.json +6 -0
  163. package/src/all/locales/pl-PL/common.json +6 -0
  164. package/src/all/locales/pt-BR/common.json +6 -0
  165. package/src/all/locales/ro-RO/common.json +6 -0
  166. package/src/all/locales/ru-RU/common.json +6 -0
  167. package/src/all/locales/sl-SI/common.json +6 -0
  168. package/src/all/locales/sv-SE/common.json +6 -0
  169. package/src/all/locales/uk-UA/common.json +6 -0
  170. package/src/chrome/manifest.json +1 -1
  171. package/src/chrome-mv3/manifest.json +1 -1
  172. package/src/firefox/manifest.json +1 -1
  173. package/src/safari/manifest.json +1 -1
@@ -23,7 +23,7 @@ class CsvLogMeOnceRowParser extends AbstractCsvRowParser {
23
23
  static get mapping() {
24
24
  return {
25
25
  "name": "name",
26
- "uri": "url",
26
+ "uris": "url",
27
27
  "description": "note",
28
28
  "folder_parent_path": "group",
29
29
  "username": "username",
@@ -44,10 +44,13 @@ class CsvLogMeOnceRowParser extends AbstractCsvRowParser {
44
44
  const externalResourceDto = {};
45
45
 
46
46
  for (const propertyName in this.mapping) {
47
- if (data[this.mapping[propertyName]]) {
47
+ if (propertyName === "uris") {
48
+ externalResourceDto[propertyName] = [data[this.mapping[propertyName]]];
49
+ } else if (data[this.mapping[propertyName]]) {
48
50
  externalResourceDto[propertyName] = data[this.mapping[propertyName]];
49
51
  }
50
52
  }
53
+
51
54
  resourceTypesCollection.filterByResourceTypeVersion(metadataTypesSettings.defaultResourceTypes);
52
55
  const scores = ResourcesTypeImportParser.getScores(externalResourceDto, resourceTypesCollection);
53
56
  let resourceType = ResourcesTypeImportParser.findMatchingResourceType(resourceTypesCollection, scores);
@@ -68,7 +68,7 @@ describe("CsvLogMeOnceRowParser", () => {
68
68
  const expectedEntity = new ExternalResourceEntity({
69
69
  name: data.name,
70
70
  username: data.username,
71
- uri: data.url,
71
+ uris: [data.url],
72
72
  resource_type_id: expectedResourceType.id,
73
73
  secret_clear: data.password,
74
74
  description: data.note,
@@ -105,7 +105,7 @@ describe("CsvLogMeOnceRowParser", () => {
105
105
  const expectedEntity = new ExternalResourceEntity({
106
106
  name: data.name,
107
107
  username: data.username,
108
- uri: data.url,
108
+ uris: [data.url],
109
109
  resource_type_id: expectedResourceType.id,
110
110
  secret_clear: data.password,
111
111
  description: data.note,
@@ -24,7 +24,7 @@ class CsvMozillaPlatformRowParser extends AbstractCsvRowParser {
24
24
  return {
25
25
  "name": "url",
26
26
  "username": "username",
27
- "uri": "url",
27
+ "uris": "url",
28
28
  "secret_clear": "password",
29
29
  };
30
30
  }
@@ -41,7 +41,9 @@ class CsvMozillaPlatformRowParser extends AbstractCsvRowParser {
41
41
  const externalResourceDto = {};
42
42
 
43
43
  for (const propertyName in this.mapping) {
44
- if (data[this.mapping[propertyName]]) {
44
+ if (propertyName === "uris") {
45
+ externalResourceDto[propertyName] = [data[this.mapping[propertyName]]];
46
+ } else if (data[this.mapping[propertyName]]) {
45
47
  externalResourceDto[propertyName] = data[this.mapping[propertyName]];
46
48
  }
47
49
  }
@@ -56,7 +58,7 @@ class CsvMozillaPlatformRowParser extends AbstractCsvRowParser {
56
58
  }
57
59
  if (!resourceType) {
58
60
  //Fallback default content type not supported
59
- resourceType = ResourcesTypeImportParser.fallbackDefaulResourceType(resourceTypesCollection, scores);
61
+ resourceType = ResourcesTypeImportParser.fallbackDefaulResourceType(resourceTypesCollection, metadataTypesSettings);
60
62
  importEntity.importResourcesErrors.push(new ImportError("Imported with default content type", externalResourceDto, new Error("No resource type associated to this row.")));
61
63
  }
62
64
  }
@@ -65,7 +65,7 @@ describe("CsvMozillaPlatformRowParser", () => {
65
65
  const expectedEntity = new ExternalResourceEntity({
66
66
  name: data.name,
67
67
  username: data.username,
68
- uri: data.url,
68
+ uris: [data.url],
69
69
  resource_type_id: expectedResourceType.id,
70
70
  secret_clear: data.password,
71
71
  });
@@ -98,7 +98,7 @@ describe("CsvMozillaPlatformRowParser", () => {
98
98
  const expectedEntity = new ExternalResourceEntity({
99
99
  name: data.name,
100
100
  username: data.username,
101
- uri: data.url,
101
+ uris: [data.url],
102
102
  resource_type_id: expectedResourceType.id,
103
103
  secret_clear: data.password,
104
104
  });
@@ -23,7 +23,7 @@ class CsvNordpassRowParser extends AbstractCsvRowParser {
23
23
  static get mapping() {
24
24
  return {
25
25
  "name": "name",
26
- "uri": "url",
26
+ "uris": "url",
27
27
  "username": "username",
28
28
  "secret_clear": "password",
29
29
  "description": "note",
@@ -57,7 +57,9 @@ class CsvNordpassRowParser extends AbstractCsvRowParser {
57
57
  const externalResourceDto = {};
58
58
 
59
59
  for (const propertyName in this.mapping) {
60
- if (data[this.mapping[propertyName]]) {
60
+ if (propertyName === "uris") {
61
+ externalResourceDto[propertyName] = [data[this.mapping[propertyName]]];
62
+ } else if (data[this.mapping[propertyName]]) {
61
63
  externalResourceDto[propertyName] = data[this.mapping[propertyName]];
62
64
  }
63
65
  }
@@ -66,7 +66,7 @@ describe("CsvNordpassRowParser", () => {
66
66
  const expectedEntity = new ExternalResourceEntity({
67
67
  name: data.name,
68
68
  username: data.username,
69
- uri: data.url,
69
+ uris: [data.url],
70
70
  resource_type_id: expectedResourceType.id,
71
71
  secret_clear: data.password,
72
72
  description: data.note,
@@ -102,7 +102,7 @@ describe("CsvNordpassRowParser", () => {
102
102
  const expectedEntity = new ExternalResourceEntity({
103
103
  name: data.name,
104
104
  username: data.username,
105
- uri: data.url,
105
+ uris: [data.url],
106
106
  resource_type_id: expectedResourceType.id,
107
107
  secret_clear: data.password,
108
108
  description: data.note,
@@ -23,7 +23,7 @@ class CsvSafariRowParser extends AbstractCsvRowParser {
23
23
  static get mapping() {
24
24
  return {
25
25
  "name": "Title",
26
- "uri": "URL",
26
+ "uris": "URL",
27
27
  "username": "Username",
28
28
  "secret_clear": "Password",
29
29
  "description": "Notes",
@@ -42,7 +42,9 @@ class CsvSafariRowParser extends AbstractCsvRowParser {
42
42
  static parse(data, importEntity, resourceTypesCollection, metadataTypesSettings) {
43
43
  const externalResourceDto = {};
44
44
  for (const propertyName in this.mapping) {
45
- if (data[this.mapping[propertyName]]) {
45
+ if (propertyName === "uris") {
46
+ externalResourceDto[propertyName] = [data[this.mapping[propertyName]]];
47
+ } else if (data[this.mapping[propertyName]]) {
46
48
  externalResourceDto[propertyName] = data[this.mapping[propertyName]];
47
49
  }
48
50
  }
@@ -65,7 +65,7 @@ describe("CsvSafariRowParser", () => {
65
65
  const expectedEntity = new ExternalResourceEntity({
66
66
  name: data.Title,
67
67
  username: data.Username,
68
- uri: data.URL,
68
+ uris: [data.URL],
69
69
  resource_type_id: expectedResourceType.id,
70
70
  secret_clear: data.Password,
71
71
  description: data.Notes,
@@ -99,7 +99,7 @@ describe("CsvSafariRowParser", () => {
99
99
  const expectedEntity = new ExternalResourceEntity({
100
100
  name: data.Title,
101
101
  username: data.Username,
102
- uri: data.URL,
102
+ uris: [data.URL],
103
103
  resource_type_id: expectedResourceType.id,
104
104
  secret_clear: data.Password,
105
105
  description: data.Notes,
@@ -315,7 +315,7 @@ describe("ResourcesCsvImportParser", () => {
315
315
  return Object.assign({
316
316
  name: `Password ${num}`,
317
317
  username: `username${num}`,
318
- uri: `https://url${num}.com`,
318
+ uris: [`https://url${num}.com`],
319
319
  description: `Description ${num}`,
320
320
  secret_clear: `Secret ${num}`,
321
321
  folder_parent_path: ``,
@@ -17,6 +17,11 @@ import * as kdbxweb from 'kdbxweb';
17
17
  import ExternalTotpEntity from "../../entity/totp/externalTotpEntity";
18
18
  import ResourcesTypeImportParser from "./resourcesTypeImportParser";
19
19
  import {ICON_TYPE_KEEPASS_ICON_SET} from "passbolt-styleguide/src/shared/models/entity/resource/metadata/IconEntity";
20
+ import {CUSTOM_FIELD_TYPE} from "passbolt-styleguide/src/shared/models/entity/customField/customFieldEntity";
21
+ import {v4 as uuidv4} from "uuid";
22
+
23
+ const KDBX_SUPPORTED_FIELDS = ['Title', 'URL', 'UserName', 'Notes', 'otp', 'TimeOtp-Secret-Base32', 'TimeOtp-Algorithm', 'TimeOtp-Length', 'TimeOtp-Period', 'Password'];
24
+
20
25
 
21
26
  class ResourcesKdbxImportParser {
22
27
  /**
@@ -126,7 +131,6 @@ class ResourcesKdbxImportParser {
126
131
  parseResource(kdbxEntry) {
127
132
  const externalResourceDto = {
128
133
  name: kdbxEntry.fields.get('Title') ? kdbxEntry.fields.get('Title').trim() : "",
129
- uri: kdbxEntry.fields.get('URL') ? kdbxEntry.fields.get('URL').trim() : "",
130
134
  username: kdbxEntry.fields.get('UserName') ? kdbxEntry.fields.get('UserName').trim() : "",
131
135
  description: kdbxEntry.fields.get('Notes') ? kdbxEntry.fields.get('Notes').trim() : "",
132
136
  folder_parent_path: this.getKdbxEntryPath(kdbxEntry),
@@ -137,44 +141,14 @@ class ResourcesKdbxImportParser {
137
141
  if (typeof kdbxEntry.fields.get('Password') === 'object') {
138
142
  externalResourceDto.secret_clear = kdbxEntry.fields.get('Password').getText();
139
143
  }
140
-
141
144
  try {
142
- const totp = this.getTotp(kdbxEntry);
143
- if (totp) {
144
- externalResourceDto.totp = totp;
145
- }
146
-
147
- this.resourceTypesCollection.filterByResourceTypeVersion(this.metadataTypesSettings.defaultResourceTypes);
148
-
149
- const scores = ResourcesTypeImportParser.getScores(externalResourceDto, this.resourceTypesCollection);
150
-
151
- let resourceType = ResourcesTypeImportParser.findMatchingResourceType(this.resourceTypesCollection, scores);
152
-
153
- if (!resourceType) {
154
- resourceType = ResourcesTypeImportParser.findPartialResourceType(this.resourceTypesCollection, scores);
155
- if (resourceType) {
156
- this.importEntity.importResourcesErrors.push(new ImportError("Resource partially imported", externalResourceDto));
157
- }
158
- if (!resourceType) {
159
- //Fallback default content type not supported
160
- resourceType = ResourcesTypeImportParser.fallbackDefaulResourceType(this.resourceTypesCollection, this.metadataTypesSettings);
161
- this.importEntity.importResourcesErrors.push(new ImportError("Content type not supported but imported with default resource type", externalResourceDto));
162
- }
163
- }
164
-
165
- if ((kdbxEntry.bgColor || kdbxEntry.icon) && resourceType.isV5()) {
166
- externalResourceDto.icon = {};
167
-
168
- if (kdbxEntry.icon) {
169
- externalResourceDto.icon.type = ICON_TYPE_KEEPASS_ICON_SET;
170
- externalResourceDto.icon.value = kdbxEntry.icon;
171
- }
145
+ this.parseUris(kdbxEntry, externalResourceDto);
146
+ this.parseTotp(kdbxEntry, externalResourceDto);
172
147
 
173
- if (kdbxEntry.bgColor) {
174
- externalResourceDto.icon.background_color = kdbxEntry.bgColor;
175
- }
176
- }
148
+ const resourceType = this.getResourceType(externalResourceDto);
177
149
 
150
+ this.parseIcon(kdbxEntry, externalResourceDto, resourceType);
151
+ this.parseCustomFields(kdbxEntry, externalResourceDto, resourceType);
178
152
  //resourceType should never be empty to not block end user
179
153
  externalResourceDto.resource_type_id = resourceType.id;
180
154
 
@@ -189,20 +163,129 @@ class ResourcesKdbxImportParser {
189
163
  }
190
164
 
191
165
  /**
192
- * Get the totp
166
+ * Parse URIs from a kdbx entry
167
+ * @param {KdbxEntry} kdbxEntry The kdbx entry
168
+ * @param {ExternalResourceDto} externalResourceDto The external resource dto
169
+ * @private
170
+ * @return {void}
171
+ */
172
+ parseUris(kdbxEntry, externalResourceDto) {
173
+ const uri = kdbxEntry.fields.get('URL') ? kdbxEntry.fields.get('URL').trim() : "";
174
+ const additionalUris = [uri];
175
+
176
+ let additionalEntriesUris = [...kdbxEntry.fields.entries()]
177
+ .filter(([key]) => key.startsWith('KP2A_URL'))
178
+ .map(([, value]) => (value));
179
+
180
+ if (additionalEntriesUris.length > 31) {
181
+ this.importEntity.importResourcesErrors.push(new ImportError(
182
+ "Resource has more than 32 URIs, only the first 32 will be imported",
183
+ externalResourceDto
184
+ ));
185
+ }
186
+ additionalEntriesUris = additionalEntriesUris.slice(0, 31);
187
+
188
+ for (const additionalUri of additionalEntriesUris) {
189
+ additionalUris.push(additionalUri.trim());
190
+ }
191
+
192
+ externalResourceDto.uris = additionalUris;
193
+ }
194
+ /**
195
+ * Parse the icon of the kdbx entry
196
+ * @param {kdbxweb.KdbxEntry} kdbxEntry
197
+ * @param {ExternalResourceDto} externalResourceDto
198
+ * @param {ResourceTypeEntity} resourceType
199
+ * @private
200
+ * @returns {void}
201
+ */
202
+ parseIcon(kdbxEntry, externalResourceDto, resourceType) {
203
+ if ((kdbxEntry.bgColor || kdbxEntry.icon) && resourceType.isV5()) {
204
+ externalResourceDto.icon = {};
205
+
206
+ if (kdbxEntry.icon) {
207
+ externalResourceDto.icon.type = ICON_TYPE_KEEPASS_ICON_SET;
208
+ externalResourceDto.icon.value = kdbxEntry.icon;
209
+ }
210
+
211
+ if (kdbxEntry.bgColor) {
212
+ externalResourceDto.icon.background_color = kdbxEntry.bgColor;
213
+ }
214
+ }
215
+ }
216
+
217
+ /**
218
+ * Parse the custom fields of the kdbx entry
219
+ * @param {kdbxEntry} kdbxEntry
220
+ * @param {ExternalResourceDto} externalResourceDto
221
+ * @param {ResourceTypeEntity} resourceType
222
+ * @private
223
+ * @returns {void}
224
+ */
225
+ parseCustomFields(kdbxEntry, externalResourceDto, resourceType) {
226
+ if (resourceType.isV5()) {
227
+ const customFields = [];
228
+ kdbxEntry.fields.forEach((value, key) => {
229
+ if (!KDBX_SUPPORTED_FIELDS.includes(key) && !key.startsWith('KP2A_URL')) {
230
+ const customFieldValue = typeof value === 'string' ? value : value.getText();
231
+ customFields.push({
232
+ id: uuidv4(),
233
+ type: CUSTOM_FIELD_TYPE.TEXT,
234
+ metadata_key: key,
235
+ secret_value: customFieldValue
236
+ });
237
+ }
238
+ });
239
+ if (customFields.length > 0) {
240
+ externalResourceDto.custom_fields = customFields;
241
+ }
242
+ }
243
+ }
244
+
245
+ /**
246
+ * parse the totp of the kdbx entry
193
247
  * @param {kdbxweb.KdbxEntry} kdbxEntry
194
- * @return {*}
248
+ * @param {ExternalResourceDto} externalResourceDto
249
+ * @private
250
+ * @returns {void}
195
251
  */
196
- getTotp(kdbxEntry) {
252
+ parseTotp(kdbxEntry, externalResourceDto) {
197
253
  if (kdbxEntry.fields.get('otp')) {
198
254
  const totpUrl = typeof kdbxEntry.fields.get('otp') === 'object' ? kdbxEntry.fields.get('otp').getText() : kdbxEntry.fields.get('otp');
199
255
  const totpUrlDecoded = new URL(decodeURIComponent(totpUrl));
200
256
  const totp = ExternalTotpEntity.createTotpFromUrl(totpUrlDecoded);
201
- return totp.toDto();
257
+ externalResourceDto.totp = totp.toDto();
202
258
  } else if (typeof kdbxEntry.fields.get('TimeOtp-Secret-Base32') === 'object') {
203
259
  const totp = ExternalTotpEntity.createTotpFromKdbxWindows(kdbxEntry.fields);
204
- return totp.toDto();
260
+ externalResourceDto.totp = totp.toDto();
261
+ }
262
+ }
263
+
264
+ /**
265
+ * Get the resource type
266
+ * @param {ExternalResourceDto} externalResourceDto
267
+ * @return {ResourceTypeDto}
268
+ */
269
+ getResourceType(externalResourceDto) {
270
+ this.resourceTypesCollection.filterByResourceTypeVersion(this.metadataTypesSettings.defaultResourceTypes);
271
+
272
+ const scores = ResourcesTypeImportParser.getScores(externalResourceDto, this.resourceTypesCollection);
273
+
274
+ let resourceType = ResourcesTypeImportParser.findMatchingResourceType(this.resourceTypesCollection, scores);
275
+
276
+ if (resourceType) {
277
+ return resourceType;
278
+ }
279
+
280
+ resourceType = ResourcesTypeImportParser.findPartialResourceType(this.resourceTypesCollection, scores);
281
+ if (resourceType) {
282
+ this.importEntity.importResourcesErrors.push(new ImportError("Resource partially imported", externalResourceDto));
283
+ } else {
284
+ //Fallback default content type not supported
285
+ resourceType = ResourcesTypeImportParser.fallbackDefaulResourceType(this.resourceTypesCollection, this.metadataTypesSettings);
286
+ this.importEntity.importResourcesErrors.push(new ImportError("Content type not supported but imported with default resource type", externalResourceDto));
205
287
  }
288
+ return resourceType;
206
289
  }
207
290
 
208
291
  /**
@@ -129,7 +129,7 @@ describe("ResourcesKdbxImportParser", () => {
129
129
  return Object.assign({
130
130
  name: `Password ${num}`,
131
131
  username: `username${num}`,
132
- uri: `https://url${num}.com`,
132
+ uris: [`https://url${num}.com`],
133
133
  description: `Description ${num}`,
134
134
  secret_clear: `Secret ${num}`,
135
135
  folder_parent_path: ``,
@@ -355,6 +355,138 @@ describe("ResourcesKdbxImportParser", () => {
355
355
  expect(importEntity.importResources.items[1]._icon).toBeUndefined();
356
356
  });
357
357
 
358
+
359
+ it("should import the multiple uris", async() => {
360
+ expect.assertions(2);
361
+
362
+ const metadataTypesSettings = new MetadataTypesSettingsEntity(defaultMetadataTypesSettingsV6Dto());
363
+
364
+ const file = fs.readFileSync("./src/all/background_page/model/import/resources/kdbx/kdbx-multiple-uris.kdbx", {encoding: 'base64'});
365
+ const importDto = {
366
+ "ref": "import-ref",
367
+ "file_type": "kdbx",
368
+ "file": file,
369
+ "options": {
370
+ "credentials": {
371
+ "password": "passbolt"
372
+ }
373
+ }
374
+ };
375
+ const importEntity = new ImportResourcesFileEntity(importDto);
376
+ const parser = new ResourcesKdbxImportParser(importEntity, resourceTypesCollection, metadataTypesSettings);
377
+ await parser.parseImport();
378
+
379
+ // Assert resources
380
+ expect(importEntity.importResources.items).toHaveLength(1);
381
+ expect(importEntity.importResources.items[0].uris).toEqual([
382
+ 'https://main.url',
383
+ 'https://additional1.url',
384
+ 'https://additional2.url',
385
+ 'https://additional3.url'
386
+ ]);
387
+ });
388
+
389
+ it("should import max 32 multiple uris", async() => {
390
+ expect.assertions(3);
391
+
392
+ const metadataTypesSettings = new MetadataTypesSettingsEntity(defaultMetadataTypesSettingsV6Dto());
393
+
394
+ const file = fs.readFileSync("./src/all/background_page/model/import/resources/kdbx/kdbx-multiple-uris-with-33-entries.kdbx", {encoding: 'base64'});
395
+ const importDto = {
396
+ "ref": "import-ref",
397
+ "file_type": "kdbx",
398
+ "file": file,
399
+ "options": {
400
+ "credentials": {
401
+ "password": "passbolt"
402
+ }
403
+ }
404
+ };
405
+ const importEntity = new ImportResourcesFileEntity(importDto);
406
+ const parser = new ResourcesKdbxImportParser(importEntity, resourceTypesCollection, metadataTypesSettings);
407
+ await parser.parseImport();
408
+
409
+ // Assert resources
410
+ expect(importEntity.importResources.items).toHaveLength(1);
411
+ expect(importEntity.importResources.items[0].uris).toHaveLength(32);
412
+ expect(importEntity.importResourcesErrors).toHaveLength(1);
413
+ });
414
+
415
+ it("should import custom fields", async() => {
416
+ expect.assertions(4);
417
+
418
+ const metadataTypesSettings = new MetadataTypesSettingsEntity(defaultMetadataTypesSettingsV6Dto());
419
+
420
+ const file = fs.readFileSync("./src/all/background_page/model/import/resources/kdbx/kdbx-custom-fields-with-uris.kdbx", {encoding: 'base64'});
421
+ const importDto = {
422
+ "ref": "import-ref",
423
+ "file_type": "kdbx",
424
+ "file": file,
425
+ "options": {
426
+ "credentials": {
427
+ "password": "passbolt"
428
+ }
429
+ }
430
+ };
431
+ const importEntity = new ImportResourcesFileEntity(importDto);
432
+ const parser = new ResourcesKdbxImportParser(importEntity, resourceTypesCollection, metadataTypesSettings);
433
+ await parser.parseImport();
434
+
435
+ // Assert resources
436
+ expect(importEntity.importResources.items).toHaveLength(1);
437
+ expect(importEntity.importResources.items[0].customFields).toHaveLength(3);
438
+ expect(importEntity.importResources.items[0].uris).toHaveLength(3);
439
+ expect(importEntity.importResourcesErrors).toHaveLength(0);
440
+ });
441
+
442
+ it("should import custom fields with protected values", async() => {
443
+ expect.assertions(4);
444
+
445
+ const metadataTypesSettings = new MetadataTypesSettingsEntity(defaultMetadataTypesSettingsV6Dto());
446
+
447
+ const file = fs.readFileSync("./src/all/background_page/model/import/resources/kdbx/kdbx-with-protected-custom-fields.kdbx", {encoding: 'base64'});
448
+ const importDto = {
449
+ "ref": "import-ref",
450
+ "file_type": "kdbx",
451
+ "file": file,
452
+ "options": {
453
+ "credentials": {
454
+ "password": "passbolt"
455
+ }
456
+ }
457
+ };
458
+ const importEntity = new ImportResourcesFileEntity(importDto);
459
+ const parser = new ResourcesKdbxImportParser(importEntity, resourceTypesCollection, metadataTypesSettings);
460
+ await parser.parseImport();
461
+
462
+ // Assert resources
463
+ expect(importEntity.importResources.items).toHaveLength(1);
464
+ expect(importEntity.importResources.items[0].customFields).toHaveLength(1);
465
+ expect(importEntity.importResources.items[0].uris).toHaveLength(1);
466
+ expect(importEntity.importResourcesErrors).toHaveLength(1);
467
+ });
468
+
469
+ it("should not import custom fields if the default is v4", async() => {
470
+ expect.assertions(2);
471
+ const file = fs.readFileSync("./src/all/background_page/model/import/resources/kdbx/kdbx-custom-fields-with-uris.kdbx", {encoding: 'base64'});
472
+ const importDto = {
473
+ "ref": "import-ref",
474
+ "file_type": "kdbx",
475
+ "file": file,
476
+ "options": {
477
+ "credentials": {
478
+ "password": "passbolt"
479
+ }
480
+ }
481
+ };
482
+ const importEntity = new ImportResourcesFileEntity(importDto);
483
+ const parser = new ResourcesKdbxImportParser(importEntity, resourceTypesCollection, metadataTypesSettings);
484
+ await parser.parseImport();
485
+
486
+ // Assert resources
487
+ expect(importEntity.importResources.items).toHaveLength(1);
488
+ expect(importEntity.importResources.items[0].customFields).toBeNull();
489
+ });
358
490
  it("should not import the icon if the default is v4", async() => {
359
491
  expect.assertions(3);
360
492
  const file = fs.readFileSync("./src/all/background_page/model/import/resources/kdbx/kdbx-protected-with-color-and-icon.kdbx", {encoding: 'base64'});
@@ -18,7 +18,6 @@ import ResourcesImportParser from "./resourcesImportParser";
18
18
  import ImportResourcesFileEntity from "../entity/import/importResourcesFileEntity";
19
19
  import ResourcesCsvImportParser from "./resources/resourcesCsvImportParser";
20
20
 
21
-
22
21
  describe("ResourcesImportParser", () => {
23
22
  it("should be able to parse CSV file", async() => {
24
23
  const file = "VGl0bGUsVXNlcm5hbWUsVVJMLFBhc3N3b3JkLE5vdGVzLEdyb3VwClBhc3N3b3JkIDEsdXNlcm5hbWUxLGh0dHBzOi8vdXJsMS5jb20sU2VjcmV0IDEsRGVzY3JpcHRpb24gMSxGb2xkZXIgMS9Gb2xkZXIgMgpQYXNzd29yZCAyLHVzZXJuYW1lMixodHRwczovL3VybDIuY29tLFNlY3JldCAyLERlc2NyaXB0aW9uIDIsRm9sZGVyIDEKUGFzc3dvcmQgMyx1c2VybmFtZTMsaHR0cHM6Ly91cmwzLmNvbSxTZWNyZXQgMyxEZXNjcmlwdGlvbiAzLEZvbGRlciAzL0ZvbGRlciA0ClBhc3N3b3JkIDQsdXNlcm5hbWU0LGh0dHBzOi8vdXJsNC5jb20sU2VjcmV0IDQsRGVzY3JpcHRpb24gNCxGb2xkZXIgMi9Gb2xkZXIgMQ==";
@@ -19,9 +19,7 @@ import ResourceEntity from "../entity/resource/resourceEntity";
19
19
  import PermissionChangesCollection from "../entity/permission/change/permissionChangesCollection";
20
20
  import ResourceService from "../../service/api/resource/resourceService";
21
21
  import PlaintextEntity from "../entity/plaintext/plaintextEntity";
22
- import splitBySize from "../../utils/array/splitBySize";
23
22
 
24
- const BULK_OPERATION_SIZE = 5;
25
23
  const MAX_LENGTH_PLAINTEXT = 4096;
26
24
 
27
25
  class ResourceModel {
@@ -199,72 +197,6 @@ class ResourceModel {
199
197
  await ResourceLocalStorage.updateResourcesCollection(resourcesCollection);
200
198
  }
201
199
 
202
- /**
203
- * Delete a resource using Passbolt API and remove the resource from the local storage
204
- *
205
- * @param {string} resourceId The resource id
206
- * @returns {Promise<void>}
207
- */
208
- async delete(resourceId) {
209
- await this.resourceService.delete(resourceId);
210
- await ResourceLocalStorage.delete(resourceId);
211
- }
212
-
213
- /*
214
- * ==============================================================
215
- * Bulk operations
216
- * ==============================================================
217
- */
218
-
219
- /**
220
- * Delete a bulk of resources
221
- * @param {Array<string>} resourcesIds collection The list of uuids to delete
222
- * @param {{successCallback: function, errorCallback: function}?} callbacks The intermediate operation callbacks
223
- * @returns {Promise<array<*>>}
224
- */
225
- async bulkDelete(resourcesIds, callbacks) {
226
- let result = [];
227
-
228
- // Parallelize the operations by chunk of BULK_OPERATION_SIZE operations.
229
- const chunks = splitBySize(resourcesIds, BULK_OPERATION_SIZE);
230
- for (const chunkIndex in chunks) {
231
- const chunk = chunks[chunkIndex];
232
- const promises = chunk.map(async(resourceId, mapIndex) => {
233
- const collectionIndex = (chunkIndex * BULK_OPERATION_SIZE) + mapIndex;
234
- return this._bulkDelete_deleteResource(resourceId, collectionIndex, callbacks);
235
- });
236
-
237
- const bulkPromises = await Promise.allSettled(promises);
238
- const intermediateResult = bulkPromises.map(promiseResult => promiseResult.value);
239
- result = [...result, ...intermediateResult];
240
- }
241
-
242
- return result;
243
- }
244
-
245
- /**
246
- * Delete a resource for the bulkDelete function.
247
- * @param {string} resourceId The resource to delete
248
- * @param {int} collectionIndex The index of the resource in the initial collection
249
- * @param {{successCallback: function, errorCallback: function}?} callbacks The intermediate operation callbacks
250
- * @returns {Promise<ResourceEntity|Error>}
251
- * @private
252
- */
253
- async _bulkDelete_deleteResource(resourceId, collectionIndex, callbacks) {
254
- callbacks = callbacks || {};
255
- const successCallback = callbacks.successCallback || (() => {});
256
- const errorCallback = callbacks.errorCallback || (() => {});
257
-
258
- try {
259
- await this.delete(resourceId);
260
- successCallback(collectionIndex);
261
- } catch (error) {
262
- console.error(error);
263
- errorCallback(error, collectionIndex);
264
- throw error;
265
- }
266
- }
267
-
268
200
  /*
269
201
  * ==============================================================
270
202
  * Secret plaintext serialization
@@ -25,7 +25,7 @@ import PassboltServiceUnavailableError from "passbolt-styleguide/src/shared/lib/
25
25
  import CommentEntity from "../../../model/entity/comment/commentEntity";
26
26
  import {mockApiResponseError} from "../../../../../../test/mocks/mockApiResponse";
27
27
 
28
- describe.only("ActionLogService", () => {
28
+ describe("ActionLogService", () => {
29
29
  let apiClientOptions, account;
30
30
  beforeEach(async() => {
31
31
  enableFetchMocks();