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
@@ -43,7 +43,11 @@ class CsvNordpassRowComposer extends AbstractRowComposer {
43
43
  const row = {};
44
44
  const externalResourceDto = externalResourceEntity.toDto();
45
45
  for (const propertyName in this.mapping) {
46
- row[this.mapping[propertyName]] = externalResourceDto[propertyName] || "";
46
+ if (propertyName === "uris") {
47
+ row[this.mapping[propertyName]] = externalResourceDto.uris?.length > 0 ? externalResourceDto.uris[0] : "";
48
+ } else {
49
+ row[this.mapping[propertyName]] = externalResourceDto[propertyName] || "";
50
+ }
47
51
  }
48
52
  return row;
49
53
  }
@@ -18,7 +18,7 @@ describe("CsvNorpassRowComposer", () => {
18
18
  const dto = {
19
19
  "name": "Password 1",
20
20
  "username": "Username 1",
21
- "uri": "https://url1.com",
21
+ "uris": ["https://url1.com"],
22
22
  "secret_clear": "Secret 1",
23
23
  "description": "Description 1",
24
24
  "folder_parent_path": "Folder 1"
@@ -28,7 +28,7 @@ describe("CsvNorpassRowComposer", () => {
28
28
  expect(csvRow).toBeInstanceOf(Object);
29
29
  expect(csvRow.name).toEqual(externalResourceEntity.name);
30
30
  expect(csvRow.username).toEqual(externalResourceEntity.username);
31
- expect(csvRow.url).toEqual(externalResourceEntity.uri);
31
+ expect(csvRow.url).toEqual(externalResourceEntity.uris[0]);
32
32
  expect(csvRow.password).toEqual(externalResourceEntity.secretClear);
33
33
  expect(csvRow.note).toEqual(externalResourceEntity.description);
34
34
  expect(csvRow.folder).toEqual(externalResourceEntity.folderParentPath);
@@ -43,7 +43,11 @@ class CsvSafariRowComposer extends AbstractRowComposer {
43
43
  const row = {};
44
44
  const externalResourceDto = externalResourceEntity.toDto();
45
45
  for (const propertyName in this.mapping) {
46
- row[this.mapping[propertyName]] = externalResourceDto[propertyName] || "";
46
+ if (propertyName === "uris") {
47
+ row[this.mapping[propertyName]] = externalResourceDto.uris?.length > 0 ? externalResourceDto.uris[0] : "";
48
+ } else {
49
+ row[this.mapping[propertyName]] = externalResourceDto[propertyName] || "";
50
+ }
47
51
  }
48
52
  return row;
49
53
  }
@@ -18,7 +18,7 @@ describe("CsvSafariRowComposer", () => {
18
18
  const dto = {
19
19
  "name": "Password 1",
20
20
  "username": "Username 1",
21
- "uri": "https://url1.com",
21
+ "uris": ["https://url1.com"],
22
22
  "secret_clear": "Secret 1",
23
23
  "description": "Description 1",
24
24
  "folder_parent_path": "Folder 1"
@@ -28,7 +28,7 @@ describe("CsvSafariRowComposer", () => {
28
28
  expect(csvRow).toBeInstanceOf(Object);
29
29
  expect(csvRow.Title).toEqual(externalResourceEntity.name);
30
30
  expect(csvRow.Username).toEqual(externalResourceEntity.username);
31
- expect(csvRow.URL).toEqual(externalResourceEntity.uri);
31
+ expect(csvRow.URL).toEqual(externalResourceEntity.uris[0]);
32
32
  expect(csvRow.Password).toEqual(externalResourceEntity.secretClear);
33
33
  expect(csvRow.Notes).toEqual(externalResourceEntity.description);
34
34
  });
@@ -94,7 +94,8 @@ class ResourcesKdbxExporter {
94
94
  if (externalResourceEntity.totp) {
95
95
  this.setTotpField(kdbxEntry, externalResourceEntity);
96
96
  }
97
- kdbxEntry.fields.set('URL', externalResourceEntity.uri);
97
+ this.setUrisFields(kdbxEntry, externalResourceEntity);
98
+ this.setCustomFields(kdbxEntry, externalResourceEntity);
98
99
  kdbxEntry.fields.set('Notes', externalResourceEntity.description);
99
100
 
100
101
  if (externalResourceEntity.expired) {
@@ -106,6 +107,48 @@ class ResourcesKdbxExporter {
106
107
  kdbxEntry.times.expires = false;
107
108
  }
108
109
 
110
+ this.setIconField(kdbxEntry, externalResourceEntity);
111
+ }
112
+
113
+ /**
114
+ * Set the custom fields according to the kdbx format
115
+ * @param {kdbxweb.KdbxEntry} kdbxEntry
116
+ * @param {ExternalResourceEntity} externalResourceEntity
117
+ * @returns {void}
118
+ */
119
+ setCustomFields(kdbxEntry, externalResourceEntity) {
120
+ if (externalResourceEntity.customFields) {
121
+ externalResourceEntity.customFields.items.forEach(customField => {
122
+ kdbxEntry.fields.set(customField.key, kdbxweb.ProtectedValue.fromString(customField.value));
123
+ });
124
+ }
125
+ }
126
+
127
+ /**
128
+ * Set the URL fields according to the kdbx format
129
+ * @param {kdbxweb.KdbxEntry} kdbxEntry
130
+ * @param {ExternalResourceEntity} externalResourceEntity
131
+ * @returns {void}
132
+ */
133
+ setUrisFields(kdbxEntry, externalResourceEntity) {
134
+ if (externalResourceEntity.uris && externalResourceEntity.uris.length > 0) {
135
+ kdbxEntry.fields.set('URL', externalResourceEntity.uris[0]);
136
+ if (externalResourceEntity.uris.length > 1) {
137
+ kdbxEntry.fields.set('KP2A_URL', externalResourceEntity.uris[1]);
138
+ for (let i = 2; i < externalResourceEntity.uris.length; i++) {
139
+ kdbxEntry.fields.set(`KP2A_URL_${i}`, externalResourceEntity.uris[i]);
140
+ }
141
+ }
142
+ }
143
+ }
144
+
145
+ /**
146
+ * Set the icon fields according to the kdbx format
147
+ * @param {kdbxweb.KdbxEntry} kdbxEntry
148
+ * @param {ExternalResourceEntity} externalResourceEntity
149
+ * @returns {void}
150
+ */
151
+ setIconField(kdbxEntry, externalResourceEntity) {
109
152
  if (!externalResourceEntity.icon) {
110
153
  return;
111
154
  }
@@ -131,7 +174,6 @@ class ResourcesKdbxExporter {
131
174
  switch (this.exportEntity.format) {
132
175
  case ExportResourcesFileEntity.FORMAT_KDBX: {
133
176
  kdbxEntry.fields.set('TimeOtp-Secret-Base32', kdbxweb.ProtectedValue.fromString(totp.secretKey));
134
- // Adapt algorithm to match keepass windows
135
177
  const algorithm = `HMAC-${totp.algorithm.substring(0, 3)}-${totp.algorithm.substring(3)}`;
136
178
  kdbxEntry.fields.set('TimeOtp-Algorithm', algorithm);
137
179
  kdbxEntry.fields.set('TimeOtp-Length', totp.digits.toString());
@@ -20,6 +20,7 @@ import ExportResourcesFileEntity from "../../entity/export/exportResourcesFileEn
20
20
  import fs from "fs";
21
21
  import {defaultTotpDto} from "../../entity/totp/totpDto.test.data";
22
22
  import {defaultIconDto} from "passbolt-styleguide/src/shared/models/entity/resource/metadata/iconEntity.test.data";
23
+ import {defaultCustomFieldsCollection} from "passbolt-styleguide/src/shared/models/entity/customField/customFieldsCollection.test.data";
23
24
 
24
25
  global.kdbxweb = kdbxweb;
25
26
  kdbxweb.CryptoEngine.argon2 = argon2;
@@ -30,11 +31,12 @@ describe("ResourcesKdbxExporter", () => {
30
31
  id: `7f077753-0835-4054-92ee-556660ea04a${num}`,
31
32
  name: `Password ${num}`,
32
33
  username: `username${num}`,
33
- uri: `https://url${num}.com`,
34
+ uris: [`https://url${num}.com`, `https://alturl${num}.com`, `https://extraurl${num}.com`],
34
35
  description: `Description ${num}`,
35
36
  secret_clear: `Secret ${num}`,
36
37
  folder_parent_path: '',
37
38
  totp: defaultTotpDto(),
39
+ custom_fields: defaultCustomFieldsCollection(),
38
40
  expired: null,
39
41
  }, data);
40
42
  }
@@ -65,7 +67,7 @@ describe("ResourcesKdbxExporter", () => {
65
67
  });
66
68
 
67
69
  it("should export resources and folders for keepass windows", async() => {
68
- expect.assertions(20);
70
+ expect.assertions(25);
69
71
 
70
72
  const now = new Date();
71
73
  now.setMilliseconds(0);
@@ -121,6 +123,16 @@ describe("ResourcesKdbxExporter", () => {
121
123
  expect(digits).toEqual("6");
122
124
  expect(period).toEqual("30");
123
125
 
126
+ expect(password1.fields.get('URL')).toEqual("https://url1.com");
127
+ expect(password1.fields.get('KP2A_URL')).toEqual("https://alturl1.com");
128
+ expect(password1.fields.get('KP2A_URL_2')).toEqual("https://extraurl1.com");
129
+
130
+ const customFields1 = password3.fields.get('Key 0').getText();
131
+ const customFields2 = password3.fields.get('Key 1').getText();
132
+
133
+ expect(customFields1).toEqual("Value 0");
134
+ expect(customFields2).toEqual("Value 1");
135
+
124
136
  expect(password4.fields.get('Title')).toEqual("Password 4");
125
137
  expect(password4.times.expires).toStrictEqual(true);
126
138
  expect(password4.times.expiryTime).toStrictEqual(new Date(now));
@@ -167,7 +179,7 @@ describe("ResourcesKdbxExporter", () => {
167
179
  });
168
180
 
169
181
  it("should export resources and folders for other keepass", async() => {
170
- expect.assertions(17);
182
+ expect.assertions(22);
171
183
 
172
184
  const now = new Date();
173
185
  now.setMilliseconds(0);
@@ -217,6 +229,15 @@ describe("ResourcesKdbxExporter", () => {
217
229
  const totp = password1.fields.get('otp').getText();
218
230
  expect(totp).toEqual("otpauth://totp/Password%201%3Ausername1?secret=DAV3DS4ERAAF5QGH&issuer=https%253A%252F%252Furl1.com&algorithm=SHA1&digits=6&period=30");
219
231
 
232
+ expect(password1.fields.get('URL')).toEqual("https://url1.com");
233
+ expect(password1.fields.get('KP2A_URL')).toEqual("https://alturl1.com");
234
+ expect(password1.fields.get('KP2A_URL_2')).toEqual("https://extraurl1.com");
235
+ const customFields1 = password3.fields.get('Key 0').getText();
236
+ const customFields2 = password3.fields.get('Key 1').getText();
237
+
238
+ expect(customFields1).toEqual("Value 0");
239
+ expect(customFields2).toEqual("Value 1");
240
+
220
241
  expect(password4.fields.get('Title')).toEqual("Password 4");
221
242
  expect(password4.times.expires).toStrictEqual(true);
222
243
  expect(password4.times.expiryTime).toStrictEqual(new Date(now));
@@ -50,7 +50,7 @@ class AbstractCsvRowParser {
50
50
  }
51
51
 
52
52
  // Check how many optional properties the row parser match
53
- const optionalFields = ["username", "uri", "description", "folder_parent_path"];
53
+ const optionalFields = ["username", "uris", "description", "folder_parent_path"];
54
54
  const countOptionalFieldsReducer = (count, fieldName) => (csvHasField(fieldName) ? ++count : count);
55
55
  const countOptionalFields = optionalFields.reduce(countOptionalFieldsReducer, 0);
56
56
 
@@ -24,7 +24,7 @@ class Csv1PasswordRowParser extends AbstractCsvRowParser {
24
24
  return {
25
25
  "name": "Title",
26
26
  "username": "Username",
27
- "uri": "Url",
27
+ "uris": "Url",
28
28
  "secret_clear": "Password",
29
29
  "description": "Notes",
30
30
  "folder_parent_path": "Type"
@@ -43,7 +43,9 @@ class Csv1PasswordRowParser extends AbstractCsvRowParser {
43
43
  const externalResourceDto = {};
44
44
 
45
45
  for (const propertyName in this.mapping) {
46
- if (data[this.mapping[propertyName]]) {
46
+ if (propertyName === "uris") {
47
+ externalResourceDto[propertyName] = [data[this.mapping[propertyName]]];
48
+ } else if (data[this.mapping[propertyName]]) {
47
49
  externalResourceDto[propertyName] = data[this.mapping[propertyName]];
48
50
  }
49
51
  }
@@ -59,7 +61,7 @@ class Csv1PasswordRowParser extends AbstractCsvRowParser {
59
61
  }
60
62
  if (!resourceType) {
61
63
  //Fallback default content type not supported
62
- resourceType = ResourcesTypeImportParser.fallbackDefaulResourceType(resourceTypesCollection, scores);
64
+ resourceType = ResourcesTypeImportParser.fallbackDefaulResourceType(resourceTypesCollection, metadataTypesSettings);
63
65
  importEntity.importResourcesErrors.push(new ImportError("Imported with default content type", externalResourceDto, new Error("No resource type associated to this row.")));
64
66
  }
65
67
  }
@@ -65,7 +65,7 @@ describe("Csv1PasswordRowParser", () => {
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,
@@ -77,6 +77,7 @@ describe("Csv1PasswordRowParser", () => {
77
77
  expect(externalResourceEntity).toBeInstanceOf(ExternalResourceEntity);
78
78
  expect(externalResourceEntity.toDto()).toEqual(expectedEntity.toDto());
79
79
  });
80
+
80
81
  it("parses resource of type default-v5 with all properties from csv row", () => {
81
82
  expect.assertions(2);
82
83
 
@@ -101,7 +102,7 @@ describe("Csv1PasswordRowParser", () => {
101
102
  const expectedEntity = new ExternalResourceEntity({
102
103
  name: data.Title,
103
104
  username: data.Username,
104
- uri: data.Url,
105
+ uris: [data.Url],
105
106
  resource_type_id: expectedResourceType.id,
106
107
  secret_clear: data.Password,
107
108
  description: data.Notes,
@@ -14,6 +14,7 @@ import ExternalResourceEntity from "../../../entity/resource/external/externalRe
14
14
  import ResourcesTypeImportParser from "../resourcesTypeImportParser";
15
15
  import AbstractCsvRowParser from "./abstractCsvRowParser";
16
16
  import ImportError from "../../../../error/importError";
17
+ import ExternalTotpEntity from "../../../entity/totp/externalTotpEntity";
17
18
 
18
19
  class CsvBitWardenRowParser extends AbstractCsvRowParser {
19
20
  /**
@@ -24,10 +25,11 @@ class CsvBitWardenRowParser extends AbstractCsvRowParser {
24
25
  return {
25
26
  "name": "name",
26
27
  "username": "login_username",
27
- "uri": "login_uri",
28
+ "uris": "login_uri",
28
29
  "secret_clear": "login_password",
29
30
  "description": "notes",
30
- "folder_parent_path": "folder"
31
+ "folder_parent_path": "folder",
32
+ "totp": "login_totp"
31
33
  };
32
34
  }
33
35
 
@@ -43,7 +45,13 @@ class CsvBitWardenRowParser extends AbstractCsvRowParser {
43
45
  const externalResourceDto = {};
44
46
  for (const propertyName in this.mapping) {
45
47
  if (data[this.mapping[propertyName]]) {
46
- externalResourceDto[propertyName] = data[this.mapping[propertyName]];
48
+ if (propertyName === "uris") {
49
+ externalResourceDto[propertyName] = data[this.mapping[propertyName]] ? data[this.mapping[propertyName]].split(",") : [];
50
+ } else if (propertyName.toLowerCase() === "totp") {
51
+ externalResourceDto.totp = this.parseTotp(data[this.mapping[propertyName]]);
52
+ } else {
53
+ externalResourceDto[propertyName] = data[this.mapping[propertyName]];
54
+ }
47
55
  }
48
56
  }
49
57
  resourceTypesCollection.filterByResourceTypeVersion(metadataTypesSettings.defaultResourceTypes);
@@ -66,6 +74,17 @@ class CsvBitWardenRowParser extends AbstractCsvRowParser {
66
74
 
67
75
  return new ExternalResourceEntity(externalResourceDto);
68
76
  }
77
+
78
+ /**
79
+ * Parse the TOTP
80
+ * @param {string} totpUrl
81
+ * @return {{secret_key: *, period: *, digits: *, algorithm: *}}
82
+ */
83
+ static parseTotp(totpUrl) {
84
+ const totpUrlDecoded = new URL(decodeURIComponent(totpUrl));
85
+ const totp = ExternalTotpEntity.createTotpFromUrl(totpUrlDecoded);
86
+ return totp.toDto();
87
+ }
69
88
  }
70
89
 
71
90
  export default CsvBitWardenRowParser;
@@ -18,7 +18,7 @@ import {
18
18
  import ResourceTypesCollection from "passbolt-styleguide/src/shared/models/entity/resourceType/resourceTypesCollection";
19
19
  import {defaultMetadataTypesSettingsV4Dto, defaultMetadataTypesSettingsV50FreshDto} from "passbolt-styleguide/src/shared/models/entity/metadata/metadataTypesSettingsEntity.test.data";
20
20
  import MetadataTypesSettingsEntity from "passbolt-styleguide/src/shared/models/entity/metadata/metadataTypesSettingsEntity";
21
- import {RESOURCE_TYPE_PASSWORD_AND_DESCRIPTION_SLUG, RESOURCE_TYPE_V5_DEFAULT_SLUG} from "passbolt-styleguide/src/shared/models/entity/resourceType/resourceTypeSchemasDefinition";
21
+ import {RESOURCE_TYPE_PASSWORD_AND_DESCRIPTION_SLUG, RESOURCE_TYPE_PASSWORD_DESCRIPTION_TOTP_SLUG, RESOURCE_TYPE_V5_DEFAULT_SLUG, RESOURCE_TYPE_V5_DEFAULT_TOTP_SLUG} from "passbolt-styleguide/src/shared/models/entity/resourceType/resourceTypeSchemasDefinition";
22
22
  import BinaryConvert from "../../../../utils/format/binaryConvert";
23
23
  import ImportResourcesFileEntity from "../../../entity/import/importResourcesFileEntity";
24
24
 
@@ -49,7 +49,7 @@ describe("CsvBitWardenRowParser", () => {
49
49
  const data = {
50
50
  "name": "Password 1",
51
51
  "login_username": "Username 1",
52
- "login_uri": "https://url1.com",
52
+ "login_uri": "https://url1.com,https://url2.com,https://url3.com",
53
53
  "login_password": "Secret 1",
54
54
  "notes": "Description 1",
55
55
  "folder": "Folder 1"
@@ -67,7 +67,7 @@ describe("CsvBitWardenRowParser", () => {
67
67
  const expectedEntity = new ExternalResourceEntity({
68
68
  name: data.name,
69
69
  username: data.login_username,
70
- uri: data.login_uri,
70
+ uris: data.login_uri.split(","),
71
71
  resource_type_id: expectedResourceType.id,
72
72
  secret_clear: data.login_password,
73
73
  description: data.notes,
@@ -103,11 +103,99 @@ describe("CsvBitWardenRowParser", () => {
103
103
  const expectedEntity = new ExternalResourceEntity({
104
104
  name: data.name,
105
105
  username: data.login_username,
106
- uri: data.login_uri,
106
+ uris: [data.login_uri],
107
+ resource_type_id: expectedResourceType.id,
108
+ secret_clear: data.login_password,
109
+ description: data.notes,
110
+ folder_parent_path: data.folder,
111
+ });
112
+
113
+ const externalResourceEntity = CsvBitWardenRowParser.parse(data, importEntity, resourceTypesCollection, metadataTypesSettings);
114
+
115
+ expect(externalResourceEntity).toBeInstanceOf(ExternalResourceEntity);
116
+ expect(externalResourceEntity.toDto()).toEqual(expectedEntity.toDto());
117
+ });
118
+
119
+ it("parses resource of type v5-default-with-totp with all properties from csv row", () => {
120
+ expect.assertions(2);
121
+
122
+ const data = {
123
+ "name": "Password 1",
124
+ "login_username": "Username 1",
125
+ "login_uri": "https://url1.com",
126
+ "login_password": "Secret 1",
127
+ "notes": "Description 1",
128
+ "folder": "Folder 1",
129
+ "login_totp": "otpauth://totp/test.com%20%3A%20admin%40passbolt.com:admin%40passbolt.com?secret=TJSNMLGTCYOEMXZG&period=30&digits=6&issuer=test.com%20%3A%20admin%40passbolt.com"
130
+ };
131
+
132
+ const importDto = {
133
+ "ref": "import-ref",
134
+ "file_type": "csv",
135
+ "file": btoa(BinaryConvert.toBinary(data))
136
+ };
137
+ const importEntity = new ImportResourcesFileEntity(importDto);
138
+ const resourceTypesCollection = new ResourceTypesCollection(resourceTypesCollectionDto());
139
+ const metadataTypesSettings = new MetadataTypesSettingsEntity(defaultMetadataTypesSettingsV50FreshDto());
140
+ const expectedResourceType = resourceTypesCollection.items.find(resourceType => resourceType.slug === RESOURCE_TYPE_V5_DEFAULT_TOTP_SLUG);
141
+ const expectedEntity = new ExternalResourceEntity({
142
+ name: data.name,
143
+ username: data.login_username,
144
+ uris: [data.login_uri],
145
+ resource_type_id: expectedResourceType.id,
146
+ secret_clear: data.login_password,
147
+ description: data.notes,
148
+ folder_parent_path: data.folder,
149
+ totp: {
150
+ period: 30,
151
+ digits: 6,
152
+ algorithm: "SHA1",
153
+ secret_key: "TJSNMLGTCYOEMXZG"
154
+ }
155
+ });
156
+
157
+ const externalResourceEntity = CsvBitWardenRowParser.parse(data, importEntity, resourceTypesCollection, metadataTypesSettings);
158
+
159
+ expect(externalResourceEntity).toBeInstanceOf(ExternalResourceEntity);
160
+ expect(externalResourceEntity.toDto()).toEqual(expectedEntity.toDto());
161
+ });
162
+
163
+ it("parses resource of type password-description-totp with all properties from csv row", () => {
164
+ expect.assertions(2);
165
+
166
+ const data = {
167
+ "name": "Password 1",
168
+ "login_username": "Username 1",
169
+ "login_uri": "https://url1.com",
170
+ "login_password": "Secret 1",
171
+ "notes": "Description 1",
172
+ "folder": "Folder 1",
173
+ "login_totp": "otpauth://totp/test.com%20%3A%20admin%40passbolt.com:admin%40passbolt.com?secret=TJSNMLGTCYOEMXZG&period=30&digits=6&issuer=test.com%20%3A%20admin%40passbolt.com"
174
+ };
175
+
176
+ const importDto = {
177
+ "ref": "import-ref",
178
+ "file_type": "csv",
179
+ "file": btoa(BinaryConvert.toBinary(data))
180
+ };
181
+ const importEntity = new ImportResourcesFileEntity(importDto);
182
+ const resourceTypesCollection = new ResourceTypesCollection(resourceTypesCollectionDto());
183
+ const metadataTypesSettings = new MetadataTypesSettingsEntity(defaultMetadataTypesSettingsV4Dto());
184
+ const expectedResourceType = resourceTypesCollection.items.find(resourceType => resourceType.slug === RESOURCE_TYPE_PASSWORD_DESCRIPTION_TOTP_SLUG);
185
+ const expectedEntity = new ExternalResourceEntity({
186
+ name: data.name,
187
+ username: data.login_username,
188
+ uris: [data.login_uri],
107
189
  resource_type_id: expectedResourceType.id,
108
190
  secret_clear: data.login_password,
109
191
  description: data.notes,
110
192
  folder_parent_path: data.folder,
193
+ totp: {
194
+ period: 30,
195
+ digits: 6,
196
+ algorithm: "SHA1",
197
+ secret_key: "TJSNMLGTCYOEMXZG"
198
+ }
111
199
  });
112
200
 
113
201
  const externalResourceEntity = CsvBitWardenRowParser.parse(data, importEntity, resourceTypesCollection, metadataTypesSettings);
@@ -23,7 +23,7 @@ class CsvChromiumRowParser 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
  };
@@ -40,7 +40,9 @@ class CsvChromiumRowParser extends AbstractCsvRowParser {
40
40
  static parse(data, importEntity, resourceTypesCollection, metadataTypesSettings) {
41
41
  const externalResourceDto = {};
42
42
  for (const propertyName in this.mapping) {
43
- if (data[this.mapping[propertyName]]) {
43
+ if (propertyName === "uris") {
44
+ externalResourceDto[propertyName] = [data[this.mapping[propertyName]]];
45
+ } else if (data[this.mapping[propertyName]]) {
44
46
  externalResourceDto[propertyName] = data[this.mapping[propertyName]];
45
47
  }
46
48
  }
@@ -65,7 +65,7 @@ describe("CsvChromiumRowParser", () => {
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("CsvChromiumRowParser", () => {
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
  });
@@ -28,7 +28,7 @@ class CsvDashlaneRowParser extends AbstractCsvRowParser {
28
28
  "name": "title",
29
29
  "secret_clear": "password",
30
30
  "description": "note",
31
- "uri": "url",
31
+ "uris": "url",
32
32
  "category": "category",
33
33
  "otpSecret": "otpSecret"
34
34
  };
@@ -46,7 +46,9 @@ class CsvDashlaneRowParser extends AbstractCsvRowParser {
46
46
  const externalResourceDto = {};
47
47
 
48
48
  for (const propertyName in this.mapping) {
49
- if (data[this.mapping[propertyName]]) {
49
+ if (propertyName === "uris") {
50
+ externalResourceDto[propertyName] = [data[this.mapping[propertyName]]];
51
+ } else if (data[this.mapping[propertyName]]) {
50
52
  externalResourceDto[propertyName] = data[this.mapping[propertyName]];
51
53
  }
52
54
  }
@@ -66,7 +66,7 @@ describe("CsvDashlaneRowParser", () => {
66
66
  const expectedEntity = new ExternalResourceEntity({
67
67
  name: data.title,
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,
@@ -101,7 +101,7 @@ describe("CsvDashlaneRowParser", () => {
101
101
  const expectedEntity = new ExternalResourceEntity({
102
102
  name: data.title,
103
103
  username: data.username,
104
- uri: data.url,
104
+ uris: [data.url],
105
105
  resource_type_id: expectedResourceType.id,
106
106
  secret_clear: data.password,
107
107
  description: data.note,
@@ -113,3 +113,4 @@ describe("CsvDashlaneRowParser", () => {
113
113
  expect(externalResourceEntity.toDto()).toEqual(expectedEntity.toDto());
114
114
  });
115
115
  });
116
+
@@ -25,7 +25,7 @@ class CsvKdbxRowParser extends AbstractCsvRowParser {
25
25
  return {
26
26
  "name": "Title",
27
27
  "username": "Username",
28
- "uri": "URL",
28
+ "uris": "URL",
29
29
  "secret_clear": "Password",
30
30
  "description": "Notes",
31
31
  "folder_parent_path": "Group",
@@ -46,13 +46,16 @@ class CsvKdbxRowParser extends AbstractCsvRowParser {
46
46
 
47
47
  for (const propertyName in this.mapping) {
48
48
  if (data[this.mapping[propertyName]]) {
49
- if (propertyName.toLowerCase() === "totp") {
49
+ if (propertyName === "uris") {
50
+ externalResourceDto[propertyName] = [data[this.mapping[propertyName]]];
51
+ } else if (propertyName.toLowerCase() === "totp") {
50
52
  externalResourceDto.totp = this.parseTotp(data[this.mapping[propertyName]]);
51
53
  } else {
52
54
  externalResourceDto[propertyName] = data[this.mapping[propertyName]];
53
55
  }
54
56
  }
55
57
  }
58
+
56
59
  resourceTypesCollection.filterByResourceTypeVersion(metadataTypesSettings.defaultResourceTypes);
57
60
  const scores = ResourcesTypeImportParser.getScores(externalResourceDto, resourceTypesCollection);
58
61
  let resourceType = ResourcesTypeImportParser.findMatchingResourceType(resourceTypesCollection, scores);
@@ -63,7 +66,6 @@ class CsvKdbxRowParser extends AbstractCsvRowParser {
63
66
  importEntity.importResourcesErrors.push(new ImportError("Resource partially imported", externalResourceDto, new Error("We used the closest resource type supported.")));
64
67
  }
65
68
  if (!resourceType) {
66
- //Fallback default content type not supported
67
69
  resourceType = ResourcesTypeImportParser.fallbackDefaulResourceType(resourceTypesCollection, metadataTypesSettings);
68
70
  importEntity.importResourcesErrors.push(new ImportError("Imported with default content type", externalResourceDto, new Error("No resource type associated to this row.")));
69
71
  }
@@ -64,7 +64,7 @@ describe("CsvKdbxRowParser", () => {
64
64
  const expectedEntity = new ExternalResourceEntity({
65
65
  name: data.Title,
66
66
  username: data.Username,
67
- uri: data.URL,
67
+ uris: [data.URL],
68
68
  resource_type_id: expectedResourceType.id,
69
69
  secret_clear: data.Password,
70
70
  description: data.Notes,
@@ -101,7 +101,7 @@ describe("CsvKdbxRowParser", () => {
101
101
  const expectedEntity = new ExternalResourceEntity({
102
102
  name: data.Title,
103
103
  username: data.Username,
104
- uri: data.URL,
104
+ uris: [data.URL],
105
105
  resource_type_id: expectedResourceType.id,
106
106
  secret_clear: data.Password,
107
107
  description: data.Notes,
@@ -138,7 +138,7 @@ describe("CsvKdbxRowParser", () => {
138
138
  const expectedEntity = new ExternalResourceEntity({
139
139
  name: data.Title,
140
140
  username: data.Username,
141
- uri: data.URL,
141
+ uris: [data.URL],
142
142
  resource_type_id: expectedResourceType.id,
143
143
  secret_clear: data.Password,
144
144
  description: data.Notes,
@@ -181,7 +181,7 @@ describe("CsvKdbxRowParser", () => {
181
181
  const expectedEntity = new ExternalResourceEntity({
182
182
  name: data.Title,
183
183
  username: data.Username,
184
- uri: data.URL,
184
+ uris: [data.URL],
185
185
  resource_type_id: expectedResourceType.id,
186
186
  secret_clear: data.Password,
187
187
  description: data.Notes,
@@ -22,7 +22,7 @@ class CsvLastPassRowParser extends AbstractCsvRowParser {
22
22
  */
23
23
  static get mapping() {
24
24
  return {
25
- "uri": "url",
25
+ "uris": "url",
26
26
  "username": "username",
27
27
  "secret_clear": "password",
28
28
  "totp": "totp",
@@ -45,7 +45,9 @@ class CsvLastPassRowParser extends AbstractCsvRowParser {
45
45
  const externalResourceDto = {};
46
46
 
47
47
  for (const propertyName in this.mapping) {
48
- if (data[this.mapping[propertyName]]) {
48
+ if (propertyName === "uris") {
49
+ externalResourceDto[propertyName] = [data[this.mapping[propertyName]]];
50
+ } else if (data[this.mapping[propertyName]]) {
49
51
  externalResourceDto[propertyName] = data[this.mapping[propertyName]];
50
52
  }
51
53
  }
@@ -65,7 +65,7 @@ describe("CsvLastPassRowParser", () => {
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
  description: data.extra,
@@ -102,7 +102,7 @@ describe("CsvLastPassRowParser", () => {
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.extra,