mirror of
https://github.com/bitwarden/directory-connector
synced 2025-12-05 23:53:21 +00:00
Copy jslib into Directory Connector [TI-6] (#262)
This commit is contained in:
@@ -1,9 +1,10 @@
|
|||||||
dist
|
dist
|
||||||
build
|
build
|
||||||
build-cli
|
build-cli
|
||||||
jslib
|
|
||||||
webpack.cli.js
|
webpack.cli.js
|
||||||
webpack.main.js
|
webpack.main.js
|
||||||
webpack.renderer.js
|
webpack.renderer.js
|
||||||
|
|
||||||
**/node_modules
|
**/node_modules
|
||||||
|
|
||||||
|
**/jest.config.js
|
||||||
|
|||||||
4
.gitmodules
vendored
4
.gitmodules
vendored
@@ -1,4 +0,0 @@
|
|||||||
[submodule "jslib"]
|
|
||||||
path = jslib
|
|
||||||
url = https://github.com/bitwarden/jslib.git
|
|
||||||
branch = master
|
|
||||||
@@ -3,8 +3,6 @@ build
|
|||||||
build-cli
|
build-cli
|
||||||
dist
|
dist
|
||||||
|
|
||||||
jslib
|
|
||||||
|
|
||||||
# External libraries / auto synced locales
|
# External libraries / auto synced locales
|
||||||
src/locales
|
src/locales
|
||||||
|
|
||||||
|
|||||||
1
jslib
1
jslib
Submodule jslib deleted from 6bcadc4f40
9
jslib/.gitignore
vendored
Normal file
9
jslib/.gitignore
vendored
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
.vs
|
||||||
|
.idea
|
||||||
|
node_modules
|
||||||
|
npm-debug.log
|
||||||
|
vwd.webinfo
|
||||||
|
*.crx
|
||||||
|
*.pem
|
||||||
|
dist
|
||||||
|
coverage
|
||||||
3
jslib/.prettierrc.json
Normal file
3
jslib/.prettierrc.json
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"printWidth": 100
|
||||||
|
}
|
||||||
17
jslib/angular/jest.config.js
Normal file
17
jslib/angular/jest.config.js
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
const { pathsToModuleNameMapper } = require("ts-jest/utils");
|
||||||
|
|
||||||
|
const { compilerOptions } = require("./tsconfig");
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
name: "angular",
|
||||||
|
displayName: "angular tests",
|
||||||
|
preset: "jest-preset-angular",
|
||||||
|
testMatch: ["**/+(*.)+(spec).+(ts)"],
|
||||||
|
setupFilesAfterEnv: ["<rootDir>/spec/test.ts"],
|
||||||
|
collectCoverage: true,
|
||||||
|
coverageReporters: ["html", "lcov"],
|
||||||
|
coverageDirectory: "coverage",
|
||||||
|
moduleNameMapper: pathsToModuleNameMapper(compilerOptions?.paths || {}, {
|
||||||
|
prefix: "<rootDir>/",
|
||||||
|
}),
|
||||||
|
};
|
||||||
654
jslib/angular/package-lock.json
generated
Normal file
654
jslib/angular/package-lock.json
generated
Normal file
@@ -0,0 +1,654 @@
|
|||||||
|
{
|
||||||
|
"name": "@bitwarden/jslib-angular",
|
||||||
|
"version": "0.0.0",
|
||||||
|
"lockfileVersion": 2,
|
||||||
|
"requires": true,
|
||||||
|
"packages": {
|
||||||
|
"": {
|
||||||
|
"name": "@bitwarden/jslib-angular",
|
||||||
|
"version": "0.0.0",
|
||||||
|
"license": "GPL-3.0",
|
||||||
|
"dependencies": {
|
||||||
|
"@angular/animations": "^12.2.13",
|
||||||
|
"@angular/cdk": "^12.2.13",
|
||||||
|
"@angular/common": "^12.2.13",
|
||||||
|
"@angular/compiler": "^12.2.13",
|
||||||
|
"@angular/core": "^12.2.13",
|
||||||
|
"@angular/forms": "^12.2.13",
|
||||||
|
"@angular/platform-browser": "^12.2.13",
|
||||||
|
"@angular/platform-browser-dynamic": "^12.2.13",
|
||||||
|
"@angular/router": "^12.2.13",
|
||||||
|
"@bitwarden/jslib-common": "file:../common",
|
||||||
|
"duo_web_sdk": "git+https://github.com/duosecurity/duo_web_sdk.git",
|
||||||
|
"rxjs": "^7.4.0",
|
||||||
|
"tldjs": "^2.3.1",
|
||||||
|
"zone.js": "0.11.4"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/duo_web_sdk": "^2.7.1",
|
||||||
|
"rimraf": "^3.0.2",
|
||||||
|
"typescript": "4.3.5"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"../common": {
|
||||||
|
"name": "@bitwarden/jslib-common",
|
||||||
|
"version": "0.0.0",
|
||||||
|
"license": "GPL-3.0",
|
||||||
|
"dependencies": {
|
||||||
|
"@microsoft/signalr": "5.0.10",
|
||||||
|
"@microsoft/signalr-protocol-msgpack": "5.0.10",
|
||||||
|
"big-integer": "1.6.48",
|
||||||
|
"browser-hrtime": "^1.1.8",
|
||||||
|
"lunr": "^2.3.9",
|
||||||
|
"node-forge": "^1.2.1",
|
||||||
|
"papaparse": "^5.3.0",
|
||||||
|
"rxjs": "^7.4.0",
|
||||||
|
"tldjs": "^2.3.1",
|
||||||
|
"zxcvbn": "^4.4.2"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/lunr": "^2.3.3",
|
||||||
|
"@types/node": "^16.11.12",
|
||||||
|
"@types/node-forge": "^1.0.1",
|
||||||
|
"@types/papaparse": "^5.2.5",
|
||||||
|
"@types/tldjs": "^2.3.0",
|
||||||
|
"@types/zxcvbn": "^4.4.1",
|
||||||
|
"rimraf": "^3.0.2",
|
||||||
|
"typescript": "4.3.5"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@angular/animations": {
|
||||||
|
"version": "12.2.14",
|
||||||
|
"resolved": "https://registry.npmjs.org/@angular/animations/-/animations-12.2.14.tgz",
|
||||||
|
"integrity": "sha512-1BR5u32auVePvXNNP96DB2008V+Ku0OGqeZQl2h4XA9xzES/Zk5WllIJZXqRmWMRBVARfXsfb0RdMty9gcaVjA==",
|
||||||
|
"dependencies": {
|
||||||
|
"tslib": "^2.2.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^12.14.1 || >=14.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@angular/core": "12.2.14"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@angular/cdk": {
|
||||||
|
"version": "12.2.13",
|
||||||
|
"resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-12.2.13.tgz",
|
||||||
|
"integrity": "sha512-zSKRhECyFqhingIeyRInIyTvYErt4gWo+x5DQr0b7YLUbU8DZSwWnG4w76Ke2s4U8T7ry1jpJBHoX/e8YBpGMg==",
|
||||||
|
"dependencies": {
|
||||||
|
"tslib": "^2.2.0"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"parse5": "^5.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@angular/common": "^12.0.0 || ^13.0.0-0",
|
||||||
|
"@angular/core": "^12.0.0 || ^13.0.0-0",
|
||||||
|
"rxjs": "^6.5.3 || ^7.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@angular/common": {
|
||||||
|
"version": "12.2.14",
|
||||||
|
"resolved": "https://registry.npmjs.org/@angular/common/-/common-12.2.14.tgz",
|
||||||
|
"integrity": "sha512-ffYUYdwZETmFJw0AcWY30WsaWBhJxj/zSmFXWjgEGEGZH56zwbbNwfMZOYZ1jz4haAVxGu+TdXsOl2yMGzN7jQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"tslib": "^2.2.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^12.14.1 || >=14.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@angular/core": "12.2.14",
|
||||||
|
"rxjs": "^6.5.3 || ^7.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@angular/compiler": {
|
||||||
|
"version": "12.2.14",
|
||||||
|
"resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-12.2.14.tgz",
|
||||||
|
"integrity": "sha512-dwmZi+n66IUzRFlGWu9mjXq170ZEsaDvlNLZzaPgs6vZTa4Kt7PWvIF/Y7TMvnVv/uqNG6kOhfmOkf6rfz1Gjg==",
|
||||||
|
"dependencies": {
|
||||||
|
"tslib": "^2.2.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^12.14.1 || >=14.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@angular/core": {
|
||||||
|
"version": "12.2.14",
|
||||||
|
"resolved": "https://registry.npmjs.org/@angular/core/-/core-12.2.14.tgz",
|
||||||
|
"integrity": "sha512-dlVk7yqUHL2R/eCmM8LsWuxhEBfzg0y1zHt0UqCuFwlCoiw+IG4HFy4OlZEUw9NUEZJSv0aDv3sWqxLkvK5vvg==",
|
||||||
|
"dependencies": {
|
||||||
|
"tslib": "^2.2.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^12.14.1 || >=14.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"rxjs": "^6.5.3 || ^7.0.0",
|
||||||
|
"zone.js": "~0.11.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@angular/forms": {
|
||||||
|
"version": "12.2.14",
|
||||||
|
"resolved": "https://registry.npmjs.org/@angular/forms/-/forms-12.2.14.tgz",
|
||||||
|
"integrity": "sha512-/9/gSJUBXVRVdRnzgJnALAQZYJATuGDMkFC9ms9DEMG4PMAhe9x4if1lJjN6noz5RAom3qNuVBNWaYAPUxlcBQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"tslib": "^2.2.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^12.14.1 || >=14.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@angular/common": "12.2.14",
|
||||||
|
"@angular/core": "12.2.14",
|
||||||
|
"@angular/platform-browser": "12.2.14",
|
||||||
|
"rxjs": "^6.5.3 || ^7.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@angular/platform-browser": {
|
||||||
|
"version": "12.2.14",
|
||||||
|
"resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-12.2.14.tgz",
|
||||||
|
"integrity": "sha512-fWcE2rnJ3ZCISa1oPfsIDV7FBZBoLFEdDuMXAiDYqDPKvF/E5U5nHrS+K4SlLAi094bMobtTOReNWl/Ienniyw==",
|
||||||
|
"dependencies": {
|
||||||
|
"tslib": "^2.2.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^12.14.1 || >=14.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@angular/animations": "12.2.14",
|
||||||
|
"@angular/common": "12.2.14",
|
||||||
|
"@angular/core": "12.2.14"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@angular/animations": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@angular/platform-browser-dynamic": {
|
||||||
|
"version": "12.2.14",
|
||||||
|
"resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-12.2.14.tgz",
|
||||||
|
"integrity": "sha512-0NPF7mS91Tct8rBmOLZPmnLSuS4kbLHXo6eTgrg80OC0vlzBiQwGDVW4X3KncCoX9CpevaGJCdSMc+uPNsFOUQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"tslib": "^2.2.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^12.14.1 || >=14.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@angular/common": "12.2.14",
|
||||||
|
"@angular/compiler": "12.2.14",
|
||||||
|
"@angular/core": "12.2.14",
|
||||||
|
"@angular/platform-browser": "12.2.14"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@angular/router": {
|
||||||
|
"version": "12.2.14",
|
||||||
|
"resolved": "https://registry.npmjs.org/@angular/router/-/router-12.2.14.tgz",
|
||||||
|
"integrity": "sha512-yP5grSnqBvc4vNhtYdcxDgDYIebUKs5f0xyFkUJM5030UnQ0CV45tBsSxHMkQbPZucIfOuxpRy8xy5+4GizuwQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"tslib": "^2.2.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^12.14.1 || >=14.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@angular/common": "12.2.14",
|
||||||
|
"@angular/core": "12.2.14",
|
||||||
|
"@angular/platform-browser": "12.2.14",
|
||||||
|
"rxjs": "^6.5.3 || ^7.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@bitwarden/jslib-common": {
|
||||||
|
"resolved": "../common",
|
||||||
|
"link": true
|
||||||
|
},
|
||||||
|
"node_modules/@types/duo_web_sdk": {
|
||||||
|
"version": "2.7.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/duo_web_sdk/-/duo_web_sdk-2.7.1.tgz",
|
||||||
|
"integrity": "sha512-DePanZjFww36yGSxXwC8B3AsjrrDuPxEcufeh4gTqVsUMpCYByxjX4PERiYZdW0typzKSt9E4I14PPp+PrSIQA==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"node_modules/balanced-match": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
||||||
|
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"node_modules/brace-expansion": {
|
||||||
|
"version": "1.1.11",
|
||||||
|
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
|
||||||
|
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"balanced-match": "^1.0.0",
|
||||||
|
"concat-map": "0.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/concat-map": {
|
||||||
|
"version": "0.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
|
||||||
|
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"node_modules/duo_web_sdk": {
|
||||||
|
"version": "2.7.0",
|
||||||
|
"resolved": "git+ssh://git@github.com/duosecurity/duo_web_sdk.git#378e855ce4a1de1d1b2f7fd60465e564b3e9fbda",
|
||||||
|
"license": "SEE LICENSE IN LICENSE"
|
||||||
|
},
|
||||||
|
"node_modules/fs.realpath": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
|
||||||
|
"integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"node_modules/glob": {
|
||||||
|
"version": "7.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz",
|
||||||
|
"integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"fs.realpath": "^1.0.0",
|
||||||
|
"inflight": "^1.0.4",
|
||||||
|
"inherits": "2",
|
||||||
|
"minimatch": "^3.0.4",
|
||||||
|
"once": "^1.3.0",
|
||||||
|
"path-is-absolute": "^1.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "*"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/isaacs"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/inflight": {
|
||||||
|
"version": "1.0.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
|
||||||
|
"integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"once": "^1.3.0",
|
||||||
|
"wrappy": "1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/inherits": {
|
||||||
|
"version": "2.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
|
||||||
|
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"node_modules/minimatch": {
|
||||||
|
"version": "3.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
|
||||||
|
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"brace-expansion": "^1.1.7"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/once": {
|
||||||
|
"version": "1.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
||||||
|
"integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"wrappy": "1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/parse5": {
|
||||||
|
"version": "5.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz",
|
||||||
|
"integrity": "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==",
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"node_modules/path-is-absolute": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
|
||||||
|
"integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
|
||||||
|
"dev": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/punycode": {
|
||||||
|
"version": "1.4.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz",
|
||||||
|
"integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4="
|
||||||
|
},
|
||||||
|
"node_modules/rimraf": {
|
||||||
|
"version": "3.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
|
||||||
|
"integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"glob": "^7.1.3"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"rimraf": "bin.js"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/isaacs"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/rxjs": {
|
||||||
|
"version": "7.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.4.0.tgz",
|
||||||
|
"integrity": "sha512-7SQDi7xeTMCJpqViXh8gL/lebcwlp3d831F05+9B44A4B0WfsEwUQHR64gsH1kvJ+Ep/J9K2+n1hVl1CsGN23w==",
|
||||||
|
"dependencies": {
|
||||||
|
"tslib": "~2.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/rxjs/node_modules/tslib": {
|
||||||
|
"version": "2.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.1.0.tgz",
|
||||||
|
"integrity": "sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A=="
|
||||||
|
},
|
||||||
|
"node_modules/tldjs": {
|
||||||
|
"version": "2.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/tldjs/-/tldjs-2.3.1.tgz",
|
||||||
|
"integrity": "sha512-W/YVH/QczLUxVjnQhFC61Iq232NWu3TqDdO0S/MtXVz4xybejBov4ud+CIwN9aYqjOecEqIy0PscGkwpG9ZyTw==",
|
||||||
|
"hasInstallScript": true,
|
||||||
|
"dependencies": {
|
||||||
|
"punycode": "^1.4.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/tslib": {
|
||||||
|
"version": "2.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz",
|
||||||
|
"integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw=="
|
||||||
|
},
|
||||||
|
"node_modules/typescript": {
|
||||||
|
"version": "4.3.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.3.5.tgz",
|
||||||
|
"integrity": "sha512-DqQgihaQ9cUrskJo9kIyW/+g0Vxsk8cDtZ52a3NGh0YNTfpUSArXSohyUGnvbPazEPLu398C0UxmKSOrPumUzA==",
|
||||||
|
"dev": true,
|
||||||
|
"bin": {
|
||||||
|
"tsc": "bin/tsc",
|
||||||
|
"tsserver": "bin/tsserver"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4.2.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/wrappy": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
|
||||||
|
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"node_modules/zone.js": {
|
||||||
|
"version": "0.11.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.11.4.tgz",
|
||||||
|
"integrity": "sha512-DDh2Ab+A/B+9mJyajPjHFPWfYU1H+pdun4wnnk0OcQTNjem1XQSZ2CDW+rfZEUDjv5M19SBqAkjZi0x5wuB5Qw==",
|
||||||
|
"dependencies": {
|
||||||
|
"tslib": "^2.0.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@angular/animations": {
|
||||||
|
"version": "12.2.14",
|
||||||
|
"resolved": "https://registry.npmjs.org/@angular/animations/-/animations-12.2.14.tgz",
|
||||||
|
"integrity": "sha512-1BR5u32auVePvXNNP96DB2008V+Ku0OGqeZQl2h4XA9xzES/Zk5WllIJZXqRmWMRBVARfXsfb0RdMty9gcaVjA==",
|
||||||
|
"requires": {
|
||||||
|
"tslib": "^2.2.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@angular/cdk": {
|
||||||
|
"version": "12.2.13",
|
||||||
|
"resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-12.2.13.tgz",
|
||||||
|
"integrity": "sha512-zSKRhECyFqhingIeyRInIyTvYErt4gWo+x5DQr0b7YLUbU8DZSwWnG4w76Ke2s4U8T7ry1jpJBHoX/e8YBpGMg==",
|
||||||
|
"requires": {
|
||||||
|
"parse5": "^5.0.0",
|
||||||
|
"tslib": "^2.2.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@angular/common": {
|
||||||
|
"version": "12.2.14",
|
||||||
|
"resolved": "https://registry.npmjs.org/@angular/common/-/common-12.2.14.tgz",
|
||||||
|
"integrity": "sha512-ffYUYdwZETmFJw0AcWY30WsaWBhJxj/zSmFXWjgEGEGZH56zwbbNwfMZOYZ1jz4haAVxGu+TdXsOl2yMGzN7jQ==",
|
||||||
|
"requires": {
|
||||||
|
"tslib": "^2.2.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@angular/compiler": {
|
||||||
|
"version": "12.2.14",
|
||||||
|
"resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-12.2.14.tgz",
|
||||||
|
"integrity": "sha512-dwmZi+n66IUzRFlGWu9mjXq170ZEsaDvlNLZzaPgs6vZTa4Kt7PWvIF/Y7TMvnVv/uqNG6kOhfmOkf6rfz1Gjg==",
|
||||||
|
"requires": {
|
||||||
|
"tslib": "^2.2.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@angular/core": {
|
||||||
|
"version": "12.2.14",
|
||||||
|
"resolved": "https://registry.npmjs.org/@angular/core/-/core-12.2.14.tgz",
|
||||||
|
"integrity": "sha512-dlVk7yqUHL2R/eCmM8LsWuxhEBfzg0y1zHt0UqCuFwlCoiw+IG4HFy4OlZEUw9NUEZJSv0aDv3sWqxLkvK5vvg==",
|
||||||
|
"requires": {
|
||||||
|
"tslib": "^2.2.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@angular/forms": {
|
||||||
|
"version": "12.2.14",
|
||||||
|
"resolved": "https://registry.npmjs.org/@angular/forms/-/forms-12.2.14.tgz",
|
||||||
|
"integrity": "sha512-/9/gSJUBXVRVdRnzgJnALAQZYJATuGDMkFC9ms9DEMG4PMAhe9x4if1lJjN6noz5RAom3qNuVBNWaYAPUxlcBQ==",
|
||||||
|
"requires": {
|
||||||
|
"tslib": "^2.2.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@angular/platform-browser": {
|
||||||
|
"version": "12.2.14",
|
||||||
|
"resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-12.2.14.tgz",
|
||||||
|
"integrity": "sha512-fWcE2rnJ3ZCISa1oPfsIDV7FBZBoLFEdDuMXAiDYqDPKvF/E5U5nHrS+K4SlLAi094bMobtTOReNWl/Ienniyw==",
|
||||||
|
"requires": {
|
||||||
|
"tslib": "^2.2.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@angular/platform-browser-dynamic": {
|
||||||
|
"version": "12.2.14",
|
||||||
|
"resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-12.2.14.tgz",
|
||||||
|
"integrity": "sha512-0NPF7mS91Tct8rBmOLZPmnLSuS4kbLHXo6eTgrg80OC0vlzBiQwGDVW4X3KncCoX9CpevaGJCdSMc+uPNsFOUQ==",
|
||||||
|
"requires": {
|
||||||
|
"tslib": "^2.2.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@angular/router": {
|
||||||
|
"version": "12.2.14",
|
||||||
|
"resolved": "https://registry.npmjs.org/@angular/router/-/router-12.2.14.tgz",
|
||||||
|
"integrity": "sha512-yP5grSnqBvc4vNhtYdcxDgDYIebUKs5f0xyFkUJM5030UnQ0CV45tBsSxHMkQbPZucIfOuxpRy8xy5+4GizuwQ==",
|
||||||
|
"requires": {
|
||||||
|
"tslib": "^2.2.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@bitwarden/jslib-common": {
|
||||||
|
"version": "file:../common",
|
||||||
|
"requires": {
|
||||||
|
"@microsoft/signalr": "5.0.10",
|
||||||
|
"@microsoft/signalr-protocol-msgpack": "5.0.10",
|
||||||
|
"@types/lunr": "^2.3.3",
|
||||||
|
"@types/node": "^16.11.12",
|
||||||
|
"@types/node-forge": "^1.0.1",
|
||||||
|
"@types/papaparse": "^5.2.5",
|
||||||
|
"@types/tldjs": "^2.3.0",
|
||||||
|
"@types/zxcvbn": "^4.4.1",
|
||||||
|
"big-integer": "1.6.48",
|
||||||
|
"browser-hrtime": "^1.1.8",
|
||||||
|
"lunr": "^2.3.9",
|
||||||
|
"node-forge": "^1.2.1",
|
||||||
|
"papaparse": "^5.3.0",
|
||||||
|
"rimraf": "^3.0.2",
|
||||||
|
"rxjs": "^7.4.0",
|
||||||
|
"tldjs": "^2.3.1",
|
||||||
|
"typescript": "4.3.5",
|
||||||
|
"zxcvbn": "^4.4.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@types/duo_web_sdk": {
|
||||||
|
"version": "2.7.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/duo_web_sdk/-/duo_web_sdk-2.7.1.tgz",
|
||||||
|
"integrity": "sha512-DePanZjFww36yGSxXwC8B3AsjrrDuPxEcufeh4gTqVsUMpCYByxjX4PERiYZdW0typzKSt9E4I14PPp+PrSIQA==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"balanced-match": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
||||||
|
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"brace-expansion": {
|
||||||
|
"version": "1.1.11",
|
||||||
|
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
|
||||||
|
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"balanced-match": "^1.0.0",
|
||||||
|
"concat-map": "0.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"concat-map": {
|
||||||
|
"version": "0.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
|
||||||
|
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"duo_web_sdk": {
|
||||||
|
"version": "git+ssh://git@github.com/duosecurity/duo_web_sdk.git#378e855ce4a1de1d1b2f7fd60465e564b3e9fbda",
|
||||||
|
"from": "duo_web_sdk@git+https://github.com/duosecurity/duo_web_sdk.git"
|
||||||
|
},
|
||||||
|
"fs.realpath": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
|
||||||
|
"integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"glob": {
|
||||||
|
"version": "7.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz",
|
||||||
|
"integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"fs.realpath": "^1.0.0",
|
||||||
|
"inflight": "^1.0.4",
|
||||||
|
"inherits": "2",
|
||||||
|
"minimatch": "^3.0.4",
|
||||||
|
"once": "^1.3.0",
|
||||||
|
"path-is-absolute": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"inflight": {
|
||||||
|
"version": "1.0.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
|
||||||
|
"integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"once": "^1.3.0",
|
||||||
|
"wrappy": "1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"inherits": {
|
||||||
|
"version": "2.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
|
||||||
|
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"minimatch": {
|
||||||
|
"version": "3.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
|
||||||
|
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"brace-expansion": "^1.1.7"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"once": {
|
||||||
|
"version": "1.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
||||||
|
"integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"wrappy": "1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"parse5": {
|
||||||
|
"version": "5.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz",
|
||||||
|
"integrity": "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==",
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"path-is-absolute": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
|
||||||
|
"integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"punycode": {
|
||||||
|
"version": "1.4.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz",
|
||||||
|
"integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4="
|
||||||
|
},
|
||||||
|
"rimraf": {
|
||||||
|
"version": "3.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
|
||||||
|
"integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"glob": "^7.1.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"rxjs": {
|
||||||
|
"version": "7.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.4.0.tgz",
|
||||||
|
"integrity": "sha512-7SQDi7xeTMCJpqViXh8gL/lebcwlp3d831F05+9B44A4B0WfsEwUQHR64gsH1kvJ+Ep/J9K2+n1hVl1CsGN23w==",
|
||||||
|
"requires": {
|
||||||
|
"tslib": "~2.1.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"tslib": {
|
||||||
|
"version": "2.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.1.0.tgz",
|
||||||
|
"integrity": "sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A=="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"tldjs": {
|
||||||
|
"version": "2.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/tldjs/-/tldjs-2.3.1.tgz",
|
||||||
|
"integrity": "sha512-W/YVH/QczLUxVjnQhFC61Iq232NWu3TqDdO0S/MtXVz4xybejBov4ud+CIwN9aYqjOecEqIy0PscGkwpG9ZyTw==",
|
||||||
|
"requires": {
|
||||||
|
"punycode": "^1.4.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"tslib": {
|
||||||
|
"version": "2.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz",
|
||||||
|
"integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw=="
|
||||||
|
},
|
||||||
|
"typescript": {
|
||||||
|
"version": "4.3.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.3.5.tgz",
|
||||||
|
"integrity": "sha512-DqQgihaQ9cUrskJo9kIyW/+g0Vxsk8cDtZ52a3NGh0YNTfpUSArXSohyUGnvbPazEPLu398C0UxmKSOrPumUzA==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"wrappy": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
|
||||||
|
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"zone.js": {
|
||||||
|
"version": "0.11.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.11.4.tgz",
|
||||||
|
"integrity": "sha512-DDh2Ab+A/B+9mJyajPjHFPWfYU1H+pdun4wnnk0OcQTNjem1XQSZ2CDW+rfZEUDjv5M19SBqAkjZi0x5wuB5Qw==",
|
||||||
|
"requires": {
|
||||||
|
"tslib": "^2.0.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
41
jslib/angular/package.json
Normal file
41
jslib/angular/package.json
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
{
|
||||||
|
"name": "@bitwarden/jslib-angular",
|
||||||
|
"version": "0.0.0",
|
||||||
|
"description": "Common code used across Bitwarden JavaScript projects.",
|
||||||
|
"keywords": [
|
||||||
|
"bitwarden"
|
||||||
|
],
|
||||||
|
"author": "Bitwarden Inc.",
|
||||||
|
"homepage": "https://bitwarden.com",
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/bitwarden/jslib"
|
||||||
|
},
|
||||||
|
"license": "GPL-3.0",
|
||||||
|
"scripts": {
|
||||||
|
"clean": "rimraf dist/**/*",
|
||||||
|
"build": "npm run clean && tsc",
|
||||||
|
"build:watch": "npm run clean && tsc -watch"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/duo_web_sdk": "^2.7.1",
|
||||||
|
"rimraf": "^3.0.2",
|
||||||
|
"typescript": "4.3.5"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@angular/animations": "^12.2.13",
|
||||||
|
"@angular/cdk": "^12.2.13",
|
||||||
|
"@angular/common": "^12.2.13",
|
||||||
|
"@angular/compiler": "^12.2.13",
|
||||||
|
"@angular/core": "^12.2.13",
|
||||||
|
"@angular/forms": "^12.2.13",
|
||||||
|
"@angular/platform-browser": "^12.2.13",
|
||||||
|
"@angular/platform-browser-dynamic": "^12.2.13",
|
||||||
|
"@angular/router": "^12.2.13",
|
||||||
|
"@bitwarden/jslib-common": "file:../common",
|
||||||
|
"duo_web_sdk": "git+https://github.com/duosecurity/duo_web_sdk.git",
|
||||||
|
"rxjs": "^7.4.0",
|
||||||
|
"tldjs": "^2.3.1",
|
||||||
|
"zone.js": "0.11.4"
|
||||||
|
}
|
||||||
|
}
|
||||||
28
jslib/angular/spec/test.ts
Normal file
28
jslib/angular/spec/test.ts
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
import { webcrypto } from "crypto";
|
||||||
|
import "jest-preset-angular/setup-jest";
|
||||||
|
|
||||||
|
Object.defineProperty(window, "CSS", { value: null });
|
||||||
|
Object.defineProperty(window, "getComputedStyle", {
|
||||||
|
value: () => {
|
||||||
|
return {
|
||||||
|
display: "none",
|
||||||
|
appearance: ["-webkit-appearance"],
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
Object.defineProperty(document, "doctype", {
|
||||||
|
value: "<!DOCTYPE html>",
|
||||||
|
});
|
||||||
|
Object.defineProperty(document.body.style, "transform", {
|
||||||
|
value: () => {
|
||||||
|
return {
|
||||||
|
enumerable: true,
|
||||||
|
configurable: true,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
Object.defineProperty(window, "crypto", {
|
||||||
|
value: webcrypto,
|
||||||
|
});
|
||||||
35
jslib/angular/src/components/callout.component.html
Normal file
35
jslib/angular/src/components/callout.component.html
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
<div
|
||||||
|
#callout
|
||||||
|
class="callout callout-{{ calloutStyle }}"
|
||||||
|
[ngClass]="{ clickable: clickable }"
|
||||||
|
[attr.role]="useAlertRole ? 'alert' : null"
|
||||||
|
>
|
||||||
|
<h3 class="callout-heading" *ngIf="title">
|
||||||
|
<i class="bwi {{ icon }}" *ngIf="icon" aria-hidden="true"></i>
|
||||||
|
{{ title }}
|
||||||
|
</h3>
|
||||||
|
<div class="enforced-policy-options" *ngIf="enforcedPolicyOptions">
|
||||||
|
{{ enforcedPolicyMessage }}
|
||||||
|
<ul>
|
||||||
|
<li *ngIf="enforcedPolicyOptions?.minComplexity > 0">
|
||||||
|
{{ "policyInEffectMinComplexity" | i18n: getPasswordScoreAlertDisplay() }}
|
||||||
|
</li>
|
||||||
|
<li *ngIf="enforcedPolicyOptions?.minLength > 0">
|
||||||
|
{{ "policyInEffectMinLength" | i18n: enforcedPolicyOptions?.minLength.toString() }}
|
||||||
|
</li>
|
||||||
|
<li *ngIf="enforcedPolicyOptions?.requireUpper">
|
||||||
|
{{ "policyInEffectUppercase" | i18n }}
|
||||||
|
</li>
|
||||||
|
<li *ngIf="enforcedPolicyOptions?.requireLower">
|
||||||
|
{{ "policyInEffectLowercase" | i18n }}
|
||||||
|
</li>
|
||||||
|
<li *ngIf="enforcedPolicyOptions?.requireNumbers">
|
||||||
|
{{ "policyInEffectNumbers" | i18n }}
|
||||||
|
</li>
|
||||||
|
<li *ngIf="enforcedPolicyOptions?.requireSpecial">
|
||||||
|
{{ "policyInEffectSpecial" | i18n: "!@#$%^&*" }}
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<ng-content></ng-content>
|
||||||
|
</div>
|
||||||
78
jslib/angular/src/components/callout.component.ts
Normal file
78
jslib/angular/src/components/callout.component.ts
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
import { Component, Input, OnInit } from "@angular/core";
|
||||||
|
|
||||||
|
import { I18nService } from "jslib-common/abstractions/i18n.service";
|
||||||
|
import { MasterPasswordPolicyOptions } from "jslib-common/models/domain/masterPasswordPolicyOptions";
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: "app-callout",
|
||||||
|
templateUrl: "callout.component.html",
|
||||||
|
})
|
||||||
|
export class CalloutComponent implements OnInit {
|
||||||
|
@Input() type = "info";
|
||||||
|
@Input() icon: string;
|
||||||
|
@Input() title: string;
|
||||||
|
@Input() clickable: boolean;
|
||||||
|
@Input() enforcedPolicyOptions: MasterPasswordPolicyOptions;
|
||||||
|
@Input() enforcedPolicyMessage: string;
|
||||||
|
@Input() useAlertRole = false;
|
||||||
|
|
||||||
|
calloutStyle: string;
|
||||||
|
|
||||||
|
constructor(private i18nService: I18nService) {}
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
this.calloutStyle = this.type;
|
||||||
|
|
||||||
|
if (this.enforcedPolicyMessage === undefined) {
|
||||||
|
this.enforcedPolicyMessage = this.i18nService.t("masterPasswordPolicyInEffect");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.type === "warning" || this.type === "danger") {
|
||||||
|
if (this.type === "danger") {
|
||||||
|
this.calloutStyle = "danger";
|
||||||
|
}
|
||||||
|
if (this.title === undefined) {
|
||||||
|
this.title = this.i18nService.t("warning");
|
||||||
|
}
|
||||||
|
if (this.icon === undefined) {
|
||||||
|
this.icon = "bwi-exclamation-triangle";
|
||||||
|
}
|
||||||
|
} else if (this.type === "error") {
|
||||||
|
this.calloutStyle = "danger";
|
||||||
|
if (this.title === undefined) {
|
||||||
|
this.title = this.i18nService.t("error");
|
||||||
|
}
|
||||||
|
if (this.icon === undefined) {
|
||||||
|
this.icon = "bwi-error";
|
||||||
|
}
|
||||||
|
} else if (this.type === "tip") {
|
||||||
|
this.calloutStyle = "success";
|
||||||
|
if (this.title === undefined) {
|
||||||
|
this.title = this.i18nService.t("tip");
|
||||||
|
}
|
||||||
|
if (this.icon === undefined) {
|
||||||
|
this.icon = "bwi-lightbulb";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getPasswordScoreAlertDisplay() {
|
||||||
|
if (this.enforcedPolicyOptions == null) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
let str: string;
|
||||||
|
switch (this.enforcedPolicyOptions.minComplexity) {
|
||||||
|
case 4:
|
||||||
|
str = this.i18nService.t("strong");
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
str = this.i18nService.t("good");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
str = this.i18nService.t("weak");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return str + " (" + this.enforcedPolicyOptions.minComplexity + ")";
|
||||||
|
}
|
||||||
|
}
|
||||||
63
jslib/angular/src/components/environment.component.ts
Normal file
63
jslib/angular/src/components/environment.component.ts
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
import { Directive, EventEmitter, Output } from "@angular/core";
|
||||||
|
|
||||||
|
import { EnvironmentService } from "jslib-common/abstractions/environment.service";
|
||||||
|
import { I18nService } from "jslib-common/abstractions/i18n.service";
|
||||||
|
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
|
||||||
|
|
||||||
|
@Directive()
|
||||||
|
export class EnvironmentComponent {
|
||||||
|
@Output() onSaved = new EventEmitter();
|
||||||
|
|
||||||
|
iconsUrl: string;
|
||||||
|
identityUrl: string;
|
||||||
|
apiUrl: string;
|
||||||
|
webVaultUrl: string;
|
||||||
|
notificationsUrl: string;
|
||||||
|
baseUrl: string;
|
||||||
|
showCustom = false;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
protected platformUtilsService: PlatformUtilsService,
|
||||||
|
protected environmentService: EnvironmentService,
|
||||||
|
protected i18nService: I18nService
|
||||||
|
) {
|
||||||
|
const urls = this.environmentService.getUrls();
|
||||||
|
|
||||||
|
this.baseUrl = urls.base || "";
|
||||||
|
this.webVaultUrl = urls.webVault || "";
|
||||||
|
this.apiUrl = urls.api || "";
|
||||||
|
this.identityUrl = urls.identity || "";
|
||||||
|
this.iconsUrl = urls.icons || "";
|
||||||
|
this.notificationsUrl = urls.notifications || "";
|
||||||
|
}
|
||||||
|
|
||||||
|
async submit() {
|
||||||
|
const resUrls = await this.environmentService.setUrls({
|
||||||
|
base: this.baseUrl,
|
||||||
|
api: this.apiUrl,
|
||||||
|
identity: this.identityUrl,
|
||||||
|
webVault: this.webVaultUrl,
|
||||||
|
icons: this.iconsUrl,
|
||||||
|
notifications: this.notificationsUrl,
|
||||||
|
});
|
||||||
|
|
||||||
|
// re-set urls since service can change them, ex: prefixing https://
|
||||||
|
this.baseUrl = resUrls.base;
|
||||||
|
this.apiUrl = resUrls.api;
|
||||||
|
this.identityUrl = resUrls.identity;
|
||||||
|
this.webVaultUrl = resUrls.webVault;
|
||||||
|
this.iconsUrl = resUrls.icons;
|
||||||
|
this.notificationsUrl = resUrls.notifications;
|
||||||
|
|
||||||
|
this.platformUtilsService.showToast("success", null, this.i18nService.t("environmentSaved"));
|
||||||
|
this.saved();
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleCustom() {
|
||||||
|
this.showCustom = !this.showCustom;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected saved() {
|
||||||
|
this.onSaved.emit();
|
||||||
|
}
|
||||||
|
}
|
||||||
11
jslib/angular/src/components/icon.component.html
Normal file
11
jslib/angular/src/components/icon.component.html
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
<div class="icon" aria-hidden="true">
|
||||||
|
<img
|
||||||
|
[src]="image"
|
||||||
|
appFallbackSrc="{{ fallbackImage }}"
|
||||||
|
*ngIf="imageEnabled && image"
|
||||||
|
alt=""
|
||||||
|
decoding="async"
|
||||||
|
loading="lazy"
|
||||||
|
/>
|
||||||
|
<i class="bwi bwi-fw bwi-lg {{ icon }}" *ngIf="!imageEnabled || !image"></i>
|
||||||
|
</div>
|
||||||
112
jslib/angular/src/components/icon.component.ts
Normal file
112
jslib/angular/src/components/icon.component.ts
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
import { Component, Input, OnChanges } from "@angular/core";
|
||||||
|
|
||||||
|
import { EnvironmentService } from "jslib-common/abstractions/environment.service";
|
||||||
|
import { StateService } from "jslib-common/abstractions/state.service";
|
||||||
|
import { CipherType } from "jslib-common/enums/cipherType";
|
||||||
|
import { Utils } from "jslib-common/misc/utils";
|
||||||
|
import { CipherView } from "jslib-common/models/view/cipherView";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides a mapping from supported card brands to
|
||||||
|
* the filenames of icon that should be present in images/cards folder of clients.
|
||||||
|
*/
|
||||||
|
const cardIcons: Record<string, string> = {
|
||||||
|
Visa: "card-visa",
|
||||||
|
Mastercard: "card-mastercard",
|
||||||
|
Amex: "card-amex",
|
||||||
|
Discover: "card-discover",
|
||||||
|
"Diners Club": "card-diners-club",
|
||||||
|
JCB: "card-jcb",
|
||||||
|
Maestro: "card-maestro",
|
||||||
|
UnionPay: "card-union-pay",
|
||||||
|
};
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: "app-vault-icon",
|
||||||
|
templateUrl: "icon.component.html",
|
||||||
|
})
|
||||||
|
export class IconComponent implements OnChanges {
|
||||||
|
@Input() cipher: CipherView;
|
||||||
|
icon: string;
|
||||||
|
image: string;
|
||||||
|
fallbackImage: string;
|
||||||
|
imageEnabled: boolean;
|
||||||
|
|
||||||
|
private iconsUrl: string;
|
||||||
|
|
||||||
|
constructor(environmentService: EnvironmentService, private stateService: StateService) {
|
||||||
|
this.iconsUrl = environmentService.getIconsUrl();
|
||||||
|
}
|
||||||
|
|
||||||
|
async ngOnChanges() {
|
||||||
|
// Components may be re-used when using cdk-virtual-scroll. Which puts the component in a weird state,
|
||||||
|
// to avoid this we reset all state variables.
|
||||||
|
this.image = null;
|
||||||
|
this.fallbackImage = null;
|
||||||
|
this.imageEnabled = !(await this.stateService.getDisableFavicon());
|
||||||
|
this.load();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected load() {
|
||||||
|
switch (this.cipher.type) {
|
||||||
|
case CipherType.Login:
|
||||||
|
this.icon = "bwi-globe";
|
||||||
|
this.setLoginIcon();
|
||||||
|
break;
|
||||||
|
case CipherType.SecureNote:
|
||||||
|
this.icon = "bwi-sticky-note";
|
||||||
|
break;
|
||||||
|
case CipherType.Card:
|
||||||
|
this.icon = "bwi-credit-card";
|
||||||
|
this.setCardIcon();
|
||||||
|
break;
|
||||||
|
case CipherType.Identity:
|
||||||
|
this.icon = "bwi-id-card";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private setLoginIcon() {
|
||||||
|
if (this.cipher.login.uri) {
|
||||||
|
let hostnameUri = this.cipher.login.uri;
|
||||||
|
let isWebsite = false;
|
||||||
|
|
||||||
|
if (hostnameUri.indexOf("androidapp://") === 0) {
|
||||||
|
this.icon = "bwi-android";
|
||||||
|
this.image = null;
|
||||||
|
} else if (hostnameUri.indexOf("iosapp://") === 0) {
|
||||||
|
this.icon = "bwi-apple";
|
||||||
|
this.image = null;
|
||||||
|
} else if (
|
||||||
|
this.imageEnabled &&
|
||||||
|
hostnameUri.indexOf("://") === -1 &&
|
||||||
|
hostnameUri.indexOf(".") > -1
|
||||||
|
) {
|
||||||
|
hostnameUri = "http://" + hostnameUri;
|
||||||
|
isWebsite = true;
|
||||||
|
} else if (this.imageEnabled) {
|
||||||
|
isWebsite = hostnameUri.indexOf("http") === 0 && hostnameUri.indexOf(".") > -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.imageEnabled && isWebsite) {
|
||||||
|
try {
|
||||||
|
this.image = this.iconsUrl + "/" + Utils.getHostname(hostnameUri) + "/icon.png";
|
||||||
|
this.fallbackImage = "images/bwi-globe.png";
|
||||||
|
} catch (e) {
|
||||||
|
// Ignore error since the fallback icon will be shown if image is null.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.image = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private setCardIcon() {
|
||||||
|
const brand = this.cipher.card.brand;
|
||||||
|
if (this.imageEnabled && brand in cardIcons) {
|
||||||
|
this.icon = "credit-card-icon " + cardIcons[brand];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,79 @@
|
|||||||
|
import { ConfigurableFocusTrap, ConfigurableFocusTrapFactory } from "@angular/cdk/a11y";
|
||||||
|
import {
|
||||||
|
AfterViewInit,
|
||||||
|
ChangeDetectorRef,
|
||||||
|
Component,
|
||||||
|
ComponentRef,
|
||||||
|
ElementRef,
|
||||||
|
OnDestroy,
|
||||||
|
Type,
|
||||||
|
ViewChild,
|
||||||
|
ViewContainerRef,
|
||||||
|
} from "@angular/core";
|
||||||
|
|
||||||
|
import { ModalService } from "../../services/modal.service";
|
||||||
|
|
||||||
|
import { ModalRef } from "./modal.ref";
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: "app-modal",
|
||||||
|
template: "<ng-template #modalContent></ng-template>",
|
||||||
|
})
|
||||||
|
export class DynamicModalComponent implements AfterViewInit, OnDestroy {
|
||||||
|
componentRef: ComponentRef<any>;
|
||||||
|
|
||||||
|
@ViewChild("modalContent", { read: ViewContainerRef, static: true })
|
||||||
|
modalContentRef: ViewContainerRef;
|
||||||
|
|
||||||
|
childComponentType: Type<any>;
|
||||||
|
setComponentParameters: (component: any) => void;
|
||||||
|
|
||||||
|
private focusTrap: ConfigurableFocusTrap;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private modalService: ModalService,
|
||||||
|
private cd: ChangeDetectorRef,
|
||||||
|
private el: ElementRef<HTMLElement>,
|
||||||
|
private focusTrapFactory: ConfigurableFocusTrapFactory,
|
||||||
|
public modalRef: ModalRef
|
||||||
|
) {}
|
||||||
|
|
||||||
|
ngAfterViewInit() {
|
||||||
|
this.loadChildComponent(this.childComponentType);
|
||||||
|
if (this.setComponentParameters != null) {
|
||||||
|
this.setComponentParameters(this.componentRef.instance);
|
||||||
|
}
|
||||||
|
this.cd.detectChanges();
|
||||||
|
|
||||||
|
this.modalRef.created(this.el.nativeElement);
|
||||||
|
this.focusTrap = this.focusTrapFactory.create(
|
||||||
|
this.el.nativeElement.querySelector(".modal-dialog")
|
||||||
|
);
|
||||||
|
if (this.el.nativeElement.querySelector("[appAutoFocus]") == null) {
|
||||||
|
this.focusTrap.focusFirstTabbableElementWhenReady();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
loadChildComponent(componentType: Type<any>) {
|
||||||
|
const componentFactory = this.modalService.resolveComponentFactory(componentType);
|
||||||
|
|
||||||
|
this.modalContentRef.clear();
|
||||||
|
this.componentRef = this.modalContentRef.createComponent(componentFactory);
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy() {
|
||||||
|
if (this.componentRef) {
|
||||||
|
this.componentRef.destroy();
|
||||||
|
}
|
||||||
|
this.focusTrap.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
close() {
|
||||||
|
this.modalRef.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
getFocus() {
|
||||||
|
const autoFocusEl = this.el.nativeElement.querySelector("[appAutoFocus]") as HTMLElement;
|
||||||
|
autoFocusEl?.focus();
|
||||||
|
}
|
||||||
|
}
|
||||||
10
jslib/angular/src/components/modal/modal-injector.ts
Normal file
10
jslib/angular/src/components/modal/modal-injector.ts
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
import { InjectFlags, InjectionToken, Injector, Type } from "@angular/core";
|
||||||
|
|
||||||
|
export class ModalInjector implements Injector {
|
||||||
|
constructor(private _parentInjector: Injector, private _additionalTokens: WeakMap<any, any>) {}
|
||||||
|
|
||||||
|
get<T>(token: Type<T> | InjectionToken<T>, notFoundValue?: T, flags?: InjectFlags): T;
|
||||||
|
get(token: any, notFoundValue?: any, flags?: any) {
|
||||||
|
return this._additionalTokens.get(token) ?? this._parentInjector.get<any>(token, notFoundValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
50
jslib/angular/src/components/modal/modal.ref.ts
Normal file
50
jslib/angular/src/components/modal/modal.ref.ts
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
import { Observable, Subject } from "rxjs";
|
||||||
|
import { first } from "rxjs/operators";
|
||||||
|
|
||||||
|
export class ModalRef {
|
||||||
|
onCreated: Observable<HTMLElement>; // Modal added to the DOM.
|
||||||
|
onClose: Observable<any>; // Initiated close.
|
||||||
|
onClosed: Observable<any>; // Modal was closed (Remove element from DOM)
|
||||||
|
onShow: Observable<void>; // Start showing modal
|
||||||
|
onShown: Observable<void>; // Modal is fully visible
|
||||||
|
|
||||||
|
private readonly _onCreated = new Subject<HTMLElement>();
|
||||||
|
private readonly _onClose = new Subject<any>();
|
||||||
|
private readonly _onClosed = new Subject<any>();
|
||||||
|
private readonly _onShow = new Subject<void>();
|
||||||
|
private readonly _onShown = new Subject<void>();
|
||||||
|
private lastResult: any;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.onCreated = this._onCreated.asObservable();
|
||||||
|
this.onClose = this._onClose.asObservable();
|
||||||
|
this.onClosed = this._onClosed.asObservable();
|
||||||
|
this.onShow = this._onShow.asObservable();
|
||||||
|
this.onShown = this._onShow.asObservable();
|
||||||
|
}
|
||||||
|
|
||||||
|
show() {
|
||||||
|
this._onShow.next();
|
||||||
|
}
|
||||||
|
|
||||||
|
shown() {
|
||||||
|
this._onShown.next();
|
||||||
|
}
|
||||||
|
|
||||||
|
close(result?: any) {
|
||||||
|
this.lastResult = result;
|
||||||
|
this._onClose.next(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
closed() {
|
||||||
|
this._onClosed.next(this.lastResult);
|
||||||
|
}
|
||||||
|
|
||||||
|
created(el: HTMLElement) {
|
||||||
|
this._onCreated.next(el);
|
||||||
|
}
|
||||||
|
|
||||||
|
onClosedPromise(): Promise<any> {
|
||||||
|
return this.onClosed.pipe(first()).toPromise();
|
||||||
|
}
|
||||||
|
}
|
||||||
41
jslib/angular/src/components/password-reprompt.component.ts
Normal file
41
jslib/angular/src/components/password-reprompt.component.ts
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
import { Directive } from "@angular/core";
|
||||||
|
|
||||||
|
import { CryptoService } from "jslib-common/abstractions/crypto.service";
|
||||||
|
import { I18nService } from "jslib-common/abstractions/i18n.service";
|
||||||
|
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
|
||||||
|
|
||||||
|
import { ModalRef } from "./modal/modal.ref";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used to verify the user's Master Password for the "Master Password Re-prompt" feature only.
|
||||||
|
* See UserVerificationComponent for any other situation where you need to verify the user's identity.
|
||||||
|
*/
|
||||||
|
@Directive()
|
||||||
|
export class PasswordRepromptComponent {
|
||||||
|
showPassword = false;
|
||||||
|
masterPassword = "";
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private modalRef: ModalRef,
|
||||||
|
private cryptoService: CryptoService,
|
||||||
|
private platformUtilsService: PlatformUtilsService,
|
||||||
|
private i18nService: I18nService
|
||||||
|
) {}
|
||||||
|
|
||||||
|
togglePassword() {
|
||||||
|
this.showPassword = !this.showPassword;
|
||||||
|
}
|
||||||
|
|
||||||
|
async submit() {
|
||||||
|
if (!(await this.cryptoService.compareAndUpdateKeyHash(this.masterPassword, null))) {
|
||||||
|
this.platformUtilsService.showToast(
|
||||||
|
"error",
|
||||||
|
this.i18nService.t("errorOccurred"),
|
||||||
|
this.i18nService.t("invalidMasterPassword")
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.modalRef.close(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
95
jslib/angular/src/components/toastr.component.ts
Normal file
95
jslib/angular/src/components/toastr.component.ts
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
import { animate, state, style, transition, trigger } from "@angular/animations";
|
||||||
|
import { CommonModule } from "@angular/common";
|
||||||
|
import { Component, ModuleWithProviders, NgModule } from "@angular/core";
|
||||||
|
import {
|
||||||
|
DefaultNoComponentGlobalConfig,
|
||||||
|
GlobalConfig,
|
||||||
|
Toast as BaseToast,
|
||||||
|
ToastPackage,
|
||||||
|
ToastrService,
|
||||||
|
TOAST_CONFIG,
|
||||||
|
} from "ngx-toastr";
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: "[toast-component2]",
|
||||||
|
template: `
|
||||||
|
<button
|
||||||
|
*ngIf="options.closeButton"
|
||||||
|
(click)="remove()"
|
||||||
|
type="button"
|
||||||
|
class="toast-close-button"
|
||||||
|
aria-label="Close"
|
||||||
|
>
|
||||||
|
<span aria-hidden="true">×</span>
|
||||||
|
</button>
|
||||||
|
<div class="icon">
|
||||||
|
<i></i>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div *ngIf="title" [class]="options.titleClass" [attr.aria-label]="title">
|
||||||
|
{{ title }} <ng-container *ngIf="duplicatesCount">[{{ duplicatesCount + 1 }}]</ng-container>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
*ngIf="message && options.enableHtml"
|
||||||
|
role="alertdialog"
|
||||||
|
aria-live="polite"
|
||||||
|
[class]="options.messageClass"
|
||||||
|
[innerHTML]="message"
|
||||||
|
></div>
|
||||||
|
<div
|
||||||
|
*ngIf="message && !options.enableHtml"
|
||||||
|
role="alertdialog"
|
||||||
|
aria-live="polite"
|
||||||
|
[class]="options.messageClass"
|
||||||
|
[attr.aria-label]="message"
|
||||||
|
>
|
||||||
|
{{ message }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div *ngIf="options.progressBar">
|
||||||
|
<div class="toast-progress" [style.width]="width + '%'"></div>
|
||||||
|
</div>
|
||||||
|
`,
|
||||||
|
animations: [
|
||||||
|
trigger("flyInOut", [
|
||||||
|
state("inactive", style({ opacity: 0 })),
|
||||||
|
state("active", style({ opacity: 1 })),
|
||||||
|
state("removed", style({ opacity: 0 })),
|
||||||
|
transition("inactive => active", animate("{{ easeTime }}ms {{ easing }}")),
|
||||||
|
transition("active => removed", animate("{{ easeTime }}ms {{ easing }}")),
|
||||||
|
]),
|
||||||
|
],
|
||||||
|
preserveWhitespaces: false,
|
||||||
|
})
|
||||||
|
export class BitwardenToast extends BaseToast {
|
||||||
|
constructor(protected toastrService: ToastrService, public toastPackage: ToastPackage) {
|
||||||
|
super(toastrService, toastPackage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const BitwardenToastGlobalConfig: GlobalConfig = {
|
||||||
|
...DefaultNoComponentGlobalConfig,
|
||||||
|
toastComponent: BitwardenToast,
|
||||||
|
};
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [CommonModule],
|
||||||
|
declarations: [BitwardenToast],
|
||||||
|
exports: [BitwardenToast],
|
||||||
|
})
|
||||||
|
export class BitwardenToastModule {
|
||||||
|
static forRoot(config: Partial<GlobalConfig> = {}): ModuleWithProviders<BitwardenToastModule> {
|
||||||
|
return {
|
||||||
|
ngModule: BitwardenToastModule,
|
||||||
|
providers: [
|
||||||
|
{
|
||||||
|
provide: TOAST_CONFIG,
|
||||||
|
useValue: {
|
||||||
|
default: BitwardenToastGlobalConfig,
|
||||||
|
config: config,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
<ng-container *ngIf="!usesKeyConnector">
|
||||||
|
<label for="masterPassword">{{ "masterPass" | i18n }}</label>
|
||||||
|
<input
|
||||||
|
id="masterPassword"
|
||||||
|
type="password"
|
||||||
|
name="MasterPasswordHash"
|
||||||
|
class="form-control"
|
||||||
|
[formControl]="secret"
|
||||||
|
required
|
||||||
|
appAutofocus
|
||||||
|
appInputVerbatim
|
||||||
|
/>
|
||||||
|
<small class="form-text text-muted">{{ "confirmIdentity" | i18n }}</small>
|
||||||
|
</ng-container>
|
||||||
|
<ng-container *ngIf="usesKeyConnector">
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="d-block">{{ "sendVerificationCode" | i18n }}</label>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="btn btn-outline-secondary"
|
||||||
|
(click)="requestOTP()"
|
||||||
|
[disabled]="disableRequestOTP"
|
||||||
|
>
|
||||||
|
{{ "sendCode" | i18n }}
|
||||||
|
</button>
|
||||||
|
<span class="ml-2 text-success" role="alert" @sent *ngIf="sentCode">
|
||||||
|
<i class="bwi bwi-check-circle" aria-hidden="true"></i>
|
||||||
|
{{ "codeSent" | i18n }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="verificationCode">{{ "verificationCode" | i18n }}</label>
|
||||||
|
<input
|
||||||
|
id="verificationCode"
|
||||||
|
type="input"
|
||||||
|
name="verificationCode"
|
||||||
|
class="form-control"
|
||||||
|
[formControl]="secret"
|
||||||
|
required
|
||||||
|
appAutofocus
|
||||||
|
appInputVerbatim
|
||||||
|
/>
|
||||||
|
<small class="form-text text-muted">{{ "confirmIdentity" | i18n }}</small>
|
||||||
|
</div>
|
||||||
|
</ng-container>
|
||||||
96
jslib/angular/src/components/user-verification.component.ts
Normal file
96
jslib/angular/src/components/user-verification.component.ts
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
import { animate, style, transition, trigger } from "@angular/animations";
|
||||||
|
import { Component, OnInit } from "@angular/core";
|
||||||
|
import { ControlValueAccessor, FormControl, NG_VALUE_ACCESSOR } from "@angular/forms";
|
||||||
|
|
||||||
|
import { KeyConnectorService } from "jslib-common/abstractions/keyConnector.service";
|
||||||
|
import { UserVerificationService } from "jslib-common/abstractions/userVerification.service";
|
||||||
|
import { VerificationType } from "jslib-common/enums/verificationType";
|
||||||
|
import { Verification } from "jslib-common/types/verification";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used for general-purpose user verification throughout the app.
|
||||||
|
* Collects the user's master password, or if they are using Key Connector, prompts for an OTP via email.
|
||||||
|
* This is exposed to the parent component via the ControlValueAccessor interface (e.g. bind it to a FormControl).
|
||||||
|
* Use UserVerificationService to verify the user's input.
|
||||||
|
*/
|
||||||
|
@Component({
|
||||||
|
selector: "app-user-verification",
|
||||||
|
templateUrl: "user-verification.component.html",
|
||||||
|
providers: [
|
||||||
|
{
|
||||||
|
provide: NG_VALUE_ACCESSOR,
|
||||||
|
multi: true,
|
||||||
|
useExisting: UserVerificationComponent,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
animations: [
|
||||||
|
trigger("sent", [
|
||||||
|
transition(":enter", [style({ opacity: 0 }), animate("100ms", style({ opacity: 1 }))]),
|
||||||
|
]),
|
||||||
|
],
|
||||||
|
})
|
||||||
|
export class UserVerificationComponent implements ControlValueAccessor, OnInit {
|
||||||
|
usesKeyConnector = false;
|
||||||
|
disableRequestOTP = false;
|
||||||
|
sentCode = false;
|
||||||
|
|
||||||
|
secret = new FormControl("");
|
||||||
|
|
||||||
|
private onChange: (value: Verification) => void;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private keyConnectorService: KeyConnectorService,
|
||||||
|
private userVerificationService: UserVerificationService
|
||||||
|
) {}
|
||||||
|
|
||||||
|
async ngOnInit() {
|
||||||
|
this.usesKeyConnector = await this.keyConnectorService.getUsesKeyConnector();
|
||||||
|
this.processChanges(this.secret.value);
|
||||||
|
|
||||||
|
this.secret.valueChanges.subscribe((secret: string) => this.processChanges(secret));
|
||||||
|
}
|
||||||
|
|
||||||
|
async requestOTP() {
|
||||||
|
if (this.usesKeyConnector) {
|
||||||
|
this.disableRequestOTP = true;
|
||||||
|
try {
|
||||||
|
await this.userVerificationService.requestOTP();
|
||||||
|
this.sentCode = true;
|
||||||
|
} finally {
|
||||||
|
this.disableRequestOTP = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
writeValue(obj: any): void {
|
||||||
|
this.secret.setValue(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
registerOnChange(fn: any): void {
|
||||||
|
this.onChange = fn;
|
||||||
|
}
|
||||||
|
|
||||||
|
registerOnTouched(fn: any): void {
|
||||||
|
// Not implemented
|
||||||
|
}
|
||||||
|
|
||||||
|
setDisabledState?(isDisabled: boolean): void {
|
||||||
|
this.disableRequestOTP = isDisabled;
|
||||||
|
if (isDisabled) {
|
||||||
|
this.secret.disable();
|
||||||
|
} else {
|
||||||
|
this.secret.enable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private processChanges(secret: string) {
|
||||||
|
if (this.onChange == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.onChange({
|
||||||
|
type: this.usesKeyConnector ? VerificationType.OTP : VerificationType.MasterPassword,
|
||||||
|
secret: secret,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
23
jslib/angular/src/directives/a11y-title.directive.ts
Normal file
23
jslib/angular/src/directives/a11y-title.directive.ts
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
import { Directive, ElementRef, Input, Renderer2 } from "@angular/core";
|
||||||
|
|
||||||
|
@Directive({
|
||||||
|
selector: "[appA11yTitle]",
|
||||||
|
})
|
||||||
|
export class A11yTitleDirective {
|
||||||
|
@Input() set appA11yTitle(title: string) {
|
||||||
|
this.title = title;
|
||||||
|
}
|
||||||
|
|
||||||
|
private title: string;
|
||||||
|
|
||||||
|
constructor(private el: ElementRef, private renderer: Renderer2) {}
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
if (!this.el.nativeElement.hasAttribute("title")) {
|
||||||
|
this.renderer.setAttribute(this.el.nativeElement, "title", this.title);
|
||||||
|
}
|
||||||
|
if (!this.el.nativeElement.hasAttribute("aria-label")) {
|
||||||
|
this.renderer.setAttribute(this.el.nativeElement, "aria-label", this.title);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
49
jslib/angular/src/directives/api-action.directive.ts
Normal file
49
jslib/angular/src/directives/api-action.directive.ts
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
import { Directive, ElementRef, Input, OnChanges } from "@angular/core";
|
||||||
|
|
||||||
|
import { LogService } from "jslib-common/abstractions/log.service";
|
||||||
|
import { ErrorResponse } from "jslib-common/models/response/errorResponse";
|
||||||
|
|
||||||
|
import { ValidationService } from "../services/validation.service";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides error handling, in particular for any error returned by the server in an api call.
|
||||||
|
* Attach it to a <form> element and provide the name of the class property that will hold the api call promise.
|
||||||
|
* e.g. <form [appApiAction]="this.formPromise">
|
||||||
|
* Any errors/rejections that occur will be intercepted and displayed as error toasts.
|
||||||
|
*/
|
||||||
|
@Directive({
|
||||||
|
selector: "[appApiAction]",
|
||||||
|
})
|
||||||
|
export class ApiActionDirective implements OnChanges {
|
||||||
|
@Input() appApiAction: Promise<any>;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private el: ElementRef,
|
||||||
|
private validationService: ValidationService,
|
||||||
|
private logService: LogService
|
||||||
|
) {}
|
||||||
|
|
||||||
|
ngOnChanges(changes: any) {
|
||||||
|
if (this.appApiAction == null || this.appApiAction.then == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.el.nativeElement.loading = true;
|
||||||
|
|
||||||
|
this.appApiAction.then(
|
||||||
|
(response: any) => {
|
||||||
|
this.el.nativeElement.loading = false;
|
||||||
|
},
|
||||||
|
(e: any) => {
|
||||||
|
this.el.nativeElement.loading = false;
|
||||||
|
|
||||||
|
if ((e as ErrorResponse).captchaRequired) {
|
||||||
|
this.logService.error("Captcha required error response: " + e.getSingleMessage());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.logService?.error(`Received API exception: ${e}`);
|
||||||
|
this.validationService.showError(e);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
27
jslib/angular/src/directives/autofocus.directive.ts
Normal file
27
jslib/angular/src/directives/autofocus.directive.ts
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
import { Directive, ElementRef, Input, NgZone } from "@angular/core";
|
||||||
|
import { take } from "rxjs/operators";
|
||||||
|
|
||||||
|
import { Utils } from "jslib-common/misc/utils";
|
||||||
|
|
||||||
|
@Directive({
|
||||||
|
selector: "[appAutofocus]",
|
||||||
|
})
|
||||||
|
export class AutofocusDirective {
|
||||||
|
@Input() set appAutofocus(condition: boolean | string) {
|
||||||
|
this.autofocus = condition === "" || condition === true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private autofocus: boolean;
|
||||||
|
|
||||||
|
constructor(private el: ElementRef, private ngZone: NgZone) {}
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
if (!Utils.isMobileBrowser && this.autofocus) {
|
||||||
|
if (this.ngZone.isStable) {
|
||||||
|
this.el.nativeElement.focus();
|
||||||
|
} else {
|
||||||
|
this.ngZone.onStable.pipe(take(1)).subscribe(() => this.el.nativeElement.focus());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
12
jslib/angular/src/directives/blur-click.directive.ts
Normal file
12
jslib/angular/src/directives/blur-click.directive.ts
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
import { Directive, ElementRef, HostListener } from "@angular/core";
|
||||||
|
|
||||||
|
@Directive({
|
||||||
|
selector: "[appBlurClick]",
|
||||||
|
})
|
||||||
|
export class BlurClickDirective {
|
||||||
|
constructor(private el: ElementRef) {}
|
||||||
|
|
||||||
|
@HostListener("click") onClick() {
|
||||||
|
this.el.nativeElement.blur();
|
||||||
|
}
|
||||||
|
}
|
||||||
59
jslib/angular/src/directives/box-row.directive.ts
Normal file
59
jslib/angular/src/directives/box-row.directive.ts
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
import { Directive, ElementRef, HostListener, OnInit } from "@angular/core";
|
||||||
|
|
||||||
|
@Directive({
|
||||||
|
selector: "[appBoxRow]",
|
||||||
|
})
|
||||||
|
export class BoxRowDirective implements OnInit {
|
||||||
|
el: HTMLElement = null;
|
||||||
|
formEls: Element[];
|
||||||
|
|
||||||
|
constructor(elRef: ElementRef) {
|
||||||
|
this.el = elRef.nativeElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.formEls = Array.from(
|
||||||
|
this.el.querySelectorAll('input:not([type="hidden"]), select, textarea')
|
||||||
|
);
|
||||||
|
this.formEls.forEach((formEl) => {
|
||||||
|
formEl.addEventListener(
|
||||||
|
"focus",
|
||||||
|
() => {
|
||||||
|
this.el.classList.add("active");
|
||||||
|
},
|
||||||
|
false
|
||||||
|
);
|
||||||
|
|
||||||
|
formEl.addEventListener(
|
||||||
|
"blur",
|
||||||
|
() => {
|
||||||
|
this.el.classList.remove("active");
|
||||||
|
},
|
||||||
|
false
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@HostListener("click", ["$event"]) onClick(event: Event) {
|
||||||
|
const target = event.target as HTMLElement;
|
||||||
|
if (
|
||||||
|
target !== this.el &&
|
||||||
|
!target.classList.contains("progress") &&
|
||||||
|
!target.classList.contains("progress-bar")
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.formEls.length > 0) {
|
||||||
|
const formEl = this.formEls[0] as HTMLElement;
|
||||||
|
if (formEl.tagName.toLowerCase() === "input") {
|
||||||
|
const inputEl = formEl as HTMLInputElement;
|
||||||
|
if (inputEl.type != null && inputEl.type.toLowerCase() === "checkbox") {
|
||||||
|
inputEl.click();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
formEl.focus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
14
jslib/angular/src/directives/fallback-src.directive.ts
Normal file
14
jslib/angular/src/directives/fallback-src.directive.ts
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import { Directive, ElementRef, HostListener, Input } from "@angular/core";
|
||||||
|
|
||||||
|
@Directive({
|
||||||
|
selector: "[appFallbackSrc]",
|
||||||
|
})
|
||||||
|
export class FallbackSrcDirective {
|
||||||
|
@Input("appFallbackSrc") appFallbackSrc: string;
|
||||||
|
|
||||||
|
constructor(private el: ElementRef) {}
|
||||||
|
|
||||||
|
@HostListener("error") onError() {
|
||||||
|
this.el.nativeElement.src = this.appFallbackSrc;
|
||||||
|
}
|
||||||
|
}
|
||||||
10
jslib/angular/src/directives/stop-click.directive.ts
Normal file
10
jslib/angular/src/directives/stop-click.directive.ts
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
import { Directive, HostListener } from "@angular/core";
|
||||||
|
|
||||||
|
@Directive({
|
||||||
|
selector: "[appStopClick]",
|
||||||
|
})
|
||||||
|
export class StopClickDirective {
|
||||||
|
@HostListener("click", ["$event"]) onClick($event: MouseEvent) {
|
||||||
|
$event.preventDefault();
|
||||||
|
}
|
||||||
|
}
|
||||||
10
jslib/angular/src/directives/stop-prop.directive.ts
Normal file
10
jslib/angular/src/directives/stop-prop.directive.ts
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
import { Directive, HostListener } from "@angular/core";
|
||||||
|
|
||||||
|
@Directive({
|
||||||
|
selector: "[appStopProp]",
|
||||||
|
})
|
||||||
|
export class StopPropDirective {
|
||||||
|
@HostListener("click", ["$event"]) onClick($event: MouseEvent) {
|
||||||
|
$event.stopPropagation();
|
||||||
|
}
|
||||||
|
}
|
||||||
14
jslib/angular/src/pipes/i18n.pipe.ts
Normal file
14
jslib/angular/src/pipes/i18n.pipe.ts
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import { Pipe, PipeTransform } from "@angular/core";
|
||||||
|
|
||||||
|
import { I18nService } from "jslib-common/abstractions/i18n.service";
|
||||||
|
|
||||||
|
@Pipe({
|
||||||
|
name: "i18n",
|
||||||
|
})
|
||||||
|
export class I18nPipe implements PipeTransform {
|
||||||
|
constructor(private i18nService: I18nService) {}
|
||||||
|
|
||||||
|
transform(id: string, p1?: string, p2?: string, p3?: string): string {
|
||||||
|
return this.i18nService.t(id, p1, p2, p3);
|
||||||
|
}
|
||||||
|
}
|
||||||
41
jslib/angular/src/pipes/search-ciphers.pipe.ts
Normal file
41
jslib/angular/src/pipes/search-ciphers.pipe.ts
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
import { Pipe, PipeTransform } from "@angular/core";
|
||||||
|
|
||||||
|
import { CipherView } from "jslib-common/models/view/cipherView";
|
||||||
|
|
||||||
|
@Pipe({
|
||||||
|
name: "searchCiphers",
|
||||||
|
})
|
||||||
|
export class SearchCiphersPipe implements PipeTransform {
|
||||||
|
transform(ciphers: CipherView[], searchText: string, deleted = false): CipherView[] {
|
||||||
|
if (ciphers == null || ciphers.length === 0) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (searchText == null || searchText.length < 2) {
|
||||||
|
return ciphers.filter((c) => {
|
||||||
|
return deleted !== c.isDeleted;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
searchText = searchText.trim().toLowerCase();
|
||||||
|
return ciphers.filter((c) => {
|
||||||
|
if (deleted !== c.isDeleted) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (c.name != null && c.name.toLowerCase().indexOf(searchText) > -1) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (searchText.length >= 8 && c.id.startsWith(searchText)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (c.subTitle != null && c.subTitle.toLowerCase().indexOf(searchText) > -1) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (c.login && c.login.uri != null && c.login.uri.toLowerCase().indexOf(searchText) > -1) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
170
jslib/angular/src/scss/bwicons/fonts/bwi-font.svg
Normal file
170
jslib/angular/src/scss/bwicons/fonts/bwi-font.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 262 KiB |
BIN
jslib/angular/src/scss/bwicons/fonts/bwi-font.ttf
Normal file
BIN
jslib/angular/src/scss/bwicons/fonts/bwi-font.ttf
Normal file
Binary file not shown.
BIN
jslib/angular/src/scss/bwicons/fonts/bwi-font.woff
Normal file
BIN
jslib/angular/src/scss/bwicons/fonts/bwi-font.woff
Normal file
Binary file not shown.
BIN
jslib/angular/src/scss/bwicons/fonts/bwi-font.woff2
Normal file
BIN
jslib/angular/src/scss/bwicons/fonts/bwi-font.woff2
Normal file
Binary file not shown.
250
jslib/angular/src/scss/bwicons/styles/style.scss
Normal file
250
jslib/angular/src/scss/bwicons/styles/style.scss
Normal file
@@ -0,0 +1,250 @@
|
|||||||
|
$icomoon-font-family: "bwi-font" !default;
|
||||||
|
$icomoon-font-path: "~@bitwarden/jslib-angular/src/scss/bwicons/fonts/" !default;
|
||||||
|
|
||||||
|
// New font sheet? Update the font-face information below
|
||||||
|
@font-face {
|
||||||
|
font-family: "#{$icomoon-font-family}";
|
||||||
|
src: url($icomoon-font-path + "bwi-font.svg") format("svg"),
|
||||||
|
url($icomoon-font-path + "bwi-font.ttf") format("truetype"),
|
||||||
|
url($icomoon-font-path + "bwi-font.woff") format("woff"),
|
||||||
|
url($icomoon-font-path + "bwi-font.woff2") format("woff2");
|
||||||
|
font-weight: normal;
|
||||||
|
font-style: normal;
|
||||||
|
font-display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Base Class
|
||||||
|
.bwi {
|
||||||
|
/* use !important to prevent issues with browser extensions that change fonts */
|
||||||
|
font-family: "#{$icomoon-font-family}" !important;
|
||||||
|
speak: never;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: normal;
|
||||||
|
font-variant: normal;
|
||||||
|
text-transform: none;
|
||||||
|
line-height: 1;
|
||||||
|
display: inline-block;
|
||||||
|
/* Better Font Rendering */
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fixed Width Icons
|
||||||
|
.bwi-fw {
|
||||||
|
width: calc(18em / 14);
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sizing Changes
|
||||||
|
.bwi-sm {
|
||||||
|
font-size: 0.875em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bwi-lg {
|
||||||
|
font-size: calc(4em / 3);
|
||||||
|
line-height: calc(3em / 4);
|
||||||
|
vertical-align: -15%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bwi-2x {
|
||||||
|
font-size: 2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bwi-3x {
|
||||||
|
font-size: 3em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bwi-4x {
|
||||||
|
font-size: 4em;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Spin Animations
|
||||||
|
.bwi-spin {
|
||||||
|
animation: bwi-spin 2s infinite linear;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes bwi-spin {
|
||||||
|
0% {
|
||||||
|
transform: rotate(0deg);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: rotate(359deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// List Icons
|
||||||
|
.bwi-ul {
|
||||||
|
padding-left: 0;
|
||||||
|
margin-left: calc(30em / 14);
|
||||||
|
list-style-type: none;
|
||||||
|
> li {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.bwi-li {
|
||||||
|
position: absolute;
|
||||||
|
left: calc(-30em / 14);
|
||||||
|
width: calc(30em / 14);
|
||||||
|
top: calc(2em / 14);
|
||||||
|
text-align: center;
|
||||||
|
&.bwi-lg {
|
||||||
|
left: calc(-30em / 14) + calc(4em / 14);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rotation
|
||||||
|
.bwi-rotate-270 {
|
||||||
|
transform: rotate(270deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
// For new icons - add their glyph name and value to the map below
|
||||||
|
$icons: (
|
||||||
|
"save-changes": "\e988",
|
||||||
|
"browser": "\e985",
|
||||||
|
"mobile": "\e986",
|
||||||
|
"cli": "\e987",
|
||||||
|
"providers": "\e983",
|
||||||
|
"vault": "\e984",
|
||||||
|
"folder-closed-f": "\e982",
|
||||||
|
"rocket": "\e9ee",
|
||||||
|
"ellipsis-h": "\e9ef",
|
||||||
|
"ellipsis-v": "\e9f0",
|
||||||
|
"safari": "\e974",
|
||||||
|
"opera": "\e975",
|
||||||
|
"firefox": "\e976",
|
||||||
|
"edge": "\e977",
|
||||||
|
"chrome": "\e978",
|
||||||
|
"star-f": "\e979",
|
||||||
|
"arrow-circle-up": "\e97a",
|
||||||
|
"arrow-circle-right": "\e97b",
|
||||||
|
"arrow-circle-left": "\e97c",
|
||||||
|
"arrow-circle-down": "\e97d",
|
||||||
|
"undo": "\e97e",
|
||||||
|
"bolt": "\e97f",
|
||||||
|
"puzzle": "\e980",
|
||||||
|
"rss": "\e973",
|
||||||
|
"dbl-angle-left": "\e970",
|
||||||
|
"dbl-angle-right": "\e971",
|
||||||
|
"hamburger": "\e972",
|
||||||
|
"bw-folder-open-f": "\e93e",
|
||||||
|
"desktop": "\e96a",
|
||||||
|
"angle-left": "\e96b",
|
||||||
|
"user": "\e900",
|
||||||
|
"user-f": "\e901",
|
||||||
|
"key": "\e902",
|
||||||
|
"share-square": "\e903",
|
||||||
|
"hashtag": "\e904",
|
||||||
|
"clone": "\e905",
|
||||||
|
"list-alt": "\e906",
|
||||||
|
"id-card": "\e907",
|
||||||
|
"credit-card": "\e908",
|
||||||
|
"globe": "\e909",
|
||||||
|
"sticky-note": "\e90a",
|
||||||
|
"folder": "\e90b",
|
||||||
|
"lock": "\e90c",
|
||||||
|
"lock-f": "\e90d",
|
||||||
|
"generate": "\e90e",
|
||||||
|
"generate-f": "\e90f",
|
||||||
|
"cog": "\e910",
|
||||||
|
"cog-f": "\e911",
|
||||||
|
"check-circle": "\e912",
|
||||||
|
"eye": "\e913",
|
||||||
|
"pencil-square": "\e914",
|
||||||
|
"bookmark": "\e915",
|
||||||
|
"files": "\e916",
|
||||||
|
"trash": "\e917",
|
||||||
|
"plus": "\e918",
|
||||||
|
"star": "\e919",
|
||||||
|
"list": "\e91a",
|
||||||
|
"angle-right": "\e91b",
|
||||||
|
"external-link": "\e91c",
|
||||||
|
"refresh": "\e91d",
|
||||||
|
"search": "\e91f",
|
||||||
|
"filter": "\e920",
|
||||||
|
"plus-circle": "\e921",
|
||||||
|
"user-circle": "\e922",
|
||||||
|
"question-circle": "\e923",
|
||||||
|
"cogs": "\e924",
|
||||||
|
"minus-circle": "\e925",
|
||||||
|
"send": "\e926",
|
||||||
|
"send-f": "\e927",
|
||||||
|
"download": "\e928",
|
||||||
|
"pencil": "\e929",
|
||||||
|
"sign-out": "\e92a",
|
||||||
|
"share": "\e92b",
|
||||||
|
"clock": "\e92c",
|
||||||
|
"angle-down": "\e92d",
|
||||||
|
"caret-down": "\e92e",
|
||||||
|
"square": "\e92f",
|
||||||
|
"collection": "\e930",
|
||||||
|
"bank": "\e931",
|
||||||
|
"shield": "\e932",
|
||||||
|
"stop": "\e933",
|
||||||
|
"plus-square": "\e934",
|
||||||
|
"save": "\e935",
|
||||||
|
"sign-in": "\e936",
|
||||||
|
"spinner": "\e937",
|
||||||
|
"dollar": "\e939",
|
||||||
|
"check": "\e93a",
|
||||||
|
"check-square": "\e93b",
|
||||||
|
"minus-square": "\e93c",
|
||||||
|
"close": "\e93d",
|
||||||
|
"share-arrow": "\e96c",
|
||||||
|
"paperclip": "\e93f",
|
||||||
|
"bitcoin": "\e940",
|
||||||
|
"cut": "\e941",
|
||||||
|
"frown": "\e942",
|
||||||
|
"folder-open": "\e943",
|
||||||
|
"bug": "\e946",
|
||||||
|
"chain-broken": "\e947",
|
||||||
|
"dashboard": "\e948",
|
||||||
|
"envelope": "\e949",
|
||||||
|
"exclamation-circle": "\e94a",
|
||||||
|
"exclamation-triangle": "\e94b",
|
||||||
|
"caret-right": "\e94c",
|
||||||
|
"file-pdf": "\e94e",
|
||||||
|
"file-text": "\e94f",
|
||||||
|
"info-circle": "\e952",
|
||||||
|
"lightbulb": "\e953",
|
||||||
|
"link": "\e954",
|
||||||
|
"linux": "\e956",
|
||||||
|
"long-arrow-right": "\e957",
|
||||||
|
"money": "\e958",
|
||||||
|
"play": "\e959",
|
||||||
|
"reddit": "\e95a",
|
||||||
|
"refresh-tab": "\e95b",
|
||||||
|
"sitemap": "\e95c",
|
||||||
|
"sliders": "\e95d",
|
||||||
|
"tag": "\e95e",
|
||||||
|
"thumb-tack": "\e95f",
|
||||||
|
"thumbs-up": "\e960",
|
||||||
|
"unlock": "\e962",
|
||||||
|
"users": "\e963",
|
||||||
|
"wrench": "\e965",
|
||||||
|
"ban": "\e967",
|
||||||
|
"camera": "\e968",
|
||||||
|
"chevron-up": "\e969",
|
||||||
|
"eye-slash": "\e96d",
|
||||||
|
"file": "\e96e",
|
||||||
|
"paste": "\e96f",
|
||||||
|
"github": "\e950",
|
||||||
|
"facebook": "\e94d",
|
||||||
|
"paypal": "\e938",
|
||||||
|
"google": "\e951",
|
||||||
|
"linkedin": "\e955",
|
||||||
|
"discourse": "\e91e",
|
||||||
|
"twitter": "\e961",
|
||||||
|
"youtube": "\e966",
|
||||||
|
"windows": "\e964",
|
||||||
|
"apple": "\e945",
|
||||||
|
"android": "\e944",
|
||||||
|
"error": "\e981",
|
||||||
|
"numbered-list": "\e989",
|
||||||
|
);
|
||||||
|
|
||||||
|
@each $name, $glyph in $icons {
|
||||||
|
.bwi-#{$name}:before {
|
||||||
|
content: $glyph;
|
||||||
|
}
|
||||||
|
}
|
||||||
44
jslib/angular/src/scss/icons.scss
Normal file
44
jslib/angular/src/scss/icons.scss
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
$card-icons-base: "~@bitwarden/jslib-angular/src/images/cards/";
|
||||||
|
$card-icons: (
|
||||||
|
"visa": $card-icons-base + "visa-light.png",
|
||||||
|
"amex": $card-icons-base + "amex-light.png",
|
||||||
|
"diners-club": $card-icons-base + "diners_club-light.png",
|
||||||
|
"discover": $card-icons-base + "discover-light.png",
|
||||||
|
"jcb": $card-icons-base + "jcb-light.png",
|
||||||
|
"maestro": $card-icons-base + "maestro-light.png",
|
||||||
|
"mastercard": $card-icons-base + "mastercard-light.png",
|
||||||
|
"union-pay": $card-icons-base + "union_pay-light.png",
|
||||||
|
);
|
||||||
|
|
||||||
|
$card-icons-dark: (
|
||||||
|
"visa": $card-icons-base + "visa-dark.png",
|
||||||
|
"amex": $card-icons-base + "amex-dark.png",
|
||||||
|
"diners-club": $card-icons-base + "diners_club-dark.png",
|
||||||
|
"discover": $card-icons-base + "discover-dark.png",
|
||||||
|
"jcb": $card-icons-base + "jcb-dark.png",
|
||||||
|
"maestro": $card-icons-base + "maestro-dark.png",
|
||||||
|
"mastercard": $card-icons-base + "mastercard-dark.png",
|
||||||
|
"union-pay": $card-icons-base + "union_pay-dark.png",
|
||||||
|
);
|
||||||
|
|
||||||
|
.credit-card-icon {
|
||||||
|
display: block; // Resolves the parent container being slighly to big
|
||||||
|
height: 19px;
|
||||||
|
width: 24px;
|
||||||
|
background-size: contain;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
}
|
||||||
|
|
||||||
|
@each $name, $url in $card-icons {
|
||||||
|
.card-#{$name} {
|
||||||
|
background-image: url("#{$url}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@each $theme in $dark-icon-themes {
|
||||||
|
@each $name, $url in $card-icons-dark {
|
||||||
|
.#{$theme} .card-#{$name} {
|
||||||
|
background-image: url("#{$url}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
89
jslib/angular/src/scss/webfonts.css
Normal file
89
jslib/angular/src/scss/webfonts.css
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
@font-face {
|
||||||
|
font-family: "Open Sans";
|
||||||
|
font-style: italic;
|
||||||
|
font-weight: 300;
|
||||||
|
font-display: auto;
|
||||||
|
src: url(webfonts/Open_Sans-italic-300.woff) format("woff");
|
||||||
|
unicode-range: U+0-10FFFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: "Open Sans";
|
||||||
|
font-style: italic;
|
||||||
|
font-weight: 400;
|
||||||
|
font-display: auto;
|
||||||
|
src: url(webfonts/Open_Sans-italic-400.woff) format("woff");
|
||||||
|
unicode-range: U+0-10FFFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: "Open Sans";
|
||||||
|
font-style: italic;
|
||||||
|
font-weight: 600;
|
||||||
|
font-display: auto;
|
||||||
|
src: url(webfonts/Open_Sans-italic-600.woff) format("woff");
|
||||||
|
unicode-range: U+0-10FFFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: "Open Sans";
|
||||||
|
font-style: italic;
|
||||||
|
font-weight: 700;
|
||||||
|
font-display: auto;
|
||||||
|
src: url(webfonts/Open_Sans-italic-700.woff) format("woff");
|
||||||
|
unicode-range: U+0-10FFFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: "Open Sans";
|
||||||
|
font-style: italic;
|
||||||
|
font-weight: 800;
|
||||||
|
font-display: auto;
|
||||||
|
src: url(webfonts/Open_Sans-italic-800.woff) format("woff");
|
||||||
|
unicode-range: U+0-10FFFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: "Open Sans";
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 300;
|
||||||
|
font-display: auto;
|
||||||
|
src: url(webfonts/Open_Sans-normal-300.woff) format("woff");
|
||||||
|
unicode-range: U+0-10FFFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: "Open Sans";
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
font-display: auto;
|
||||||
|
src: url(webfonts/Open_Sans-normal-400.woff) format("woff");
|
||||||
|
unicode-range: U+0-10FFFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: "Open Sans";
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 600;
|
||||||
|
font-display: auto;
|
||||||
|
src: url(webfonts/Open_Sans-normal-600.woff) format("woff");
|
||||||
|
unicode-range: U+0-10FFFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: "Open Sans";
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 700;
|
||||||
|
font-display: auto;
|
||||||
|
src: url(webfonts/Open_Sans-normal-700.woff) format("woff");
|
||||||
|
unicode-range: U+0-10FFFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: "Open Sans";
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 800;
|
||||||
|
font-display: auto;
|
||||||
|
src: url(webfonts/Open_Sans-normal-800.woff) format("woff");
|
||||||
|
unicode-range: U+0-10FFFF;
|
||||||
|
}
|
||||||
BIN
jslib/angular/src/scss/webfonts/Open_Sans-italic-300.woff
Normal file
BIN
jslib/angular/src/scss/webfonts/Open_Sans-italic-300.woff
Normal file
Binary file not shown.
BIN
jslib/angular/src/scss/webfonts/Open_Sans-italic-400.woff
Normal file
BIN
jslib/angular/src/scss/webfonts/Open_Sans-italic-400.woff
Normal file
Binary file not shown.
BIN
jslib/angular/src/scss/webfonts/Open_Sans-italic-600.woff
Normal file
BIN
jslib/angular/src/scss/webfonts/Open_Sans-italic-600.woff
Normal file
Binary file not shown.
BIN
jslib/angular/src/scss/webfonts/Open_Sans-italic-700.woff
Normal file
BIN
jslib/angular/src/scss/webfonts/Open_Sans-italic-700.woff
Normal file
Binary file not shown.
BIN
jslib/angular/src/scss/webfonts/Open_Sans-italic-800.woff
Normal file
BIN
jslib/angular/src/scss/webfonts/Open_Sans-italic-800.woff
Normal file
Binary file not shown.
BIN
jslib/angular/src/scss/webfonts/Open_Sans-normal-300.woff
Normal file
BIN
jslib/angular/src/scss/webfonts/Open_Sans-normal-300.woff
Normal file
Binary file not shown.
BIN
jslib/angular/src/scss/webfonts/Open_Sans-normal-400.woff
Normal file
BIN
jslib/angular/src/scss/webfonts/Open_Sans-normal-400.woff
Normal file
Binary file not shown.
BIN
jslib/angular/src/scss/webfonts/Open_Sans-normal-600.woff
Normal file
BIN
jslib/angular/src/scss/webfonts/Open_Sans-normal-600.woff
Normal file
Binary file not shown.
BIN
jslib/angular/src/scss/webfonts/Open_Sans-normal-700.woff
Normal file
BIN
jslib/angular/src/scss/webfonts/Open_Sans-normal-700.woff
Normal file
Binary file not shown.
BIN
jslib/angular/src/scss/webfonts/Open_Sans-normal-800.woff
Normal file
BIN
jslib/angular/src/scss/webfonts/Open_Sans-normal-800.woff
Normal file
Binary file not shown.
45
jslib/angular/src/services/auth-guard.service.ts
Normal file
45
jslib/angular/src/services/auth-guard.service.ts
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
import { Injectable } from "@angular/core";
|
||||||
|
import { ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot } from "@angular/router";
|
||||||
|
|
||||||
|
import { KeyConnectorService } from "jslib-common/abstractions/keyConnector.service";
|
||||||
|
import { MessagingService } from "jslib-common/abstractions/messaging.service";
|
||||||
|
import { StateService } from "jslib-common/abstractions/state.service";
|
||||||
|
import { VaultTimeoutService } from "jslib-common/abstractions/vaultTimeout.service";
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class AuthGuardService implements CanActivate {
|
||||||
|
constructor(
|
||||||
|
private vaultTimeoutService: VaultTimeoutService,
|
||||||
|
private router: Router,
|
||||||
|
private messagingService: MessagingService,
|
||||||
|
private keyConnectorService: KeyConnectorService,
|
||||||
|
private stateService: StateService
|
||||||
|
) {}
|
||||||
|
|
||||||
|
async canActivate(route: ActivatedRouteSnapshot, routerState: RouterStateSnapshot) {
|
||||||
|
const isAuthed = await this.stateService.getIsAuthenticated();
|
||||||
|
if (!isAuthed) {
|
||||||
|
this.messagingService.send("authBlocked");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const locked = await this.vaultTimeoutService.isLocked();
|
||||||
|
if (locked) {
|
||||||
|
if (routerState != null) {
|
||||||
|
this.messagingService.send("lockedUrl", { url: routerState.url });
|
||||||
|
}
|
||||||
|
this.router.navigate(["lock"], { queryParams: { promptBiometric: true } });
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
!routerState.url.includes("remove-password") &&
|
||||||
|
(await this.keyConnectorService.getConvertAccountRequired())
|
||||||
|
) {
|
||||||
|
this.router.navigate(["/remove-password"]);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
6
jslib/angular/src/services/broadcaster.service.ts
Normal file
6
jslib/angular/src/services/broadcaster.service.ts
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
import { Injectable } from "@angular/core";
|
||||||
|
|
||||||
|
import { BroadcasterService as BaseBroadcasterService } from "jslib-common/services/broadcaster.service";
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class BroadcasterService extends BaseBroadcasterService {}
|
||||||
492
jslib/angular/src/services/jslib-services.module.ts
Normal file
492
jslib/angular/src/services/jslib-services.module.ts
Normal file
@@ -0,0 +1,492 @@
|
|||||||
|
import { Injector, LOCALE_ID, NgModule } from "@angular/core";
|
||||||
|
|
||||||
|
import { ApiService as ApiServiceAbstraction } from "jslib-common/abstractions/api.service";
|
||||||
|
import { AppIdService as AppIdServiceAbstraction } from "jslib-common/abstractions/appId.service";
|
||||||
|
import { AuditService as AuditServiceAbstraction } from "jslib-common/abstractions/audit.service";
|
||||||
|
import { AuthService as AuthServiceAbstraction } from "jslib-common/abstractions/auth.service";
|
||||||
|
import { BroadcasterService as BroadcasterServiceAbstraction } from "jslib-common/abstractions/broadcaster.service";
|
||||||
|
import { CipherService as CipherServiceAbstraction } from "jslib-common/abstractions/cipher.service";
|
||||||
|
import { CollectionService as CollectionServiceAbstraction } from "jslib-common/abstractions/collection.service";
|
||||||
|
import { CryptoService as CryptoServiceAbstraction } from "jslib-common/abstractions/crypto.service";
|
||||||
|
import { CryptoFunctionService as CryptoFunctionServiceAbstraction } from "jslib-common/abstractions/cryptoFunction.service";
|
||||||
|
import { EnvironmentService as EnvironmentServiceAbstraction } from "jslib-common/abstractions/environment.service";
|
||||||
|
import { EventService as EventServiceAbstraction } from "jslib-common/abstractions/event.service";
|
||||||
|
import { ExportService as ExportServiceAbstraction } from "jslib-common/abstractions/export.service";
|
||||||
|
import { FileUploadService as FileUploadServiceAbstraction } from "jslib-common/abstractions/fileUpload.service";
|
||||||
|
import { FolderService as FolderServiceAbstraction } from "jslib-common/abstractions/folder.service";
|
||||||
|
import { I18nService as I18nServiceAbstraction } from "jslib-common/abstractions/i18n.service";
|
||||||
|
import { KeyConnectorService as KeyConnectorServiceAbstraction } from "jslib-common/abstractions/keyConnector.service";
|
||||||
|
import { LogService } from "jslib-common/abstractions/log.service";
|
||||||
|
import { MessagingService as MessagingServiceAbstraction } from "jslib-common/abstractions/messaging.service";
|
||||||
|
import { NotificationsService as NotificationsServiceAbstraction } from "jslib-common/abstractions/notifications.service";
|
||||||
|
import { OrganizationService as OrganizationServiceAbstraction } from "jslib-common/abstractions/organization.service";
|
||||||
|
import { PasswordGenerationService as PasswordGenerationServiceAbstraction } from "jslib-common/abstractions/passwordGeneration.service";
|
||||||
|
import { PasswordRepromptService as PasswordRepromptServiceAbstraction } from "jslib-common/abstractions/passwordReprompt.service";
|
||||||
|
import { PlatformUtilsService as PlatformUtilsServiceAbstraction } from "jslib-common/abstractions/platformUtils.service";
|
||||||
|
import { PolicyService as PolicyServiceAbstraction } from "jslib-common/abstractions/policy.service";
|
||||||
|
import { ProviderService as ProviderServiceAbstraction } from "jslib-common/abstractions/provider.service";
|
||||||
|
import { SearchService as SearchServiceAbstraction } from "jslib-common/abstractions/search.service";
|
||||||
|
import { SendService as SendServiceAbstraction } from "jslib-common/abstractions/send.service";
|
||||||
|
import { SettingsService as SettingsServiceAbstraction } from "jslib-common/abstractions/settings.service";
|
||||||
|
import { StateService as StateServiceAbstraction } from "jslib-common/abstractions/state.service";
|
||||||
|
import { StateMigrationService as StateMigrationServiceAbstraction } from "jslib-common/abstractions/stateMigration.service";
|
||||||
|
import { StorageService as StorageServiceAbstraction } from "jslib-common/abstractions/storage.service";
|
||||||
|
import { SyncService as SyncServiceAbstraction } from "jslib-common/abstractions/sync.service";
|
||||||
|
import { TokenService as TokenServiceAbstraction } from "jslib-common/abstractions/token.service";
|
||||||
|
import { TotpService as TotpServiceAbstraction } from "jslib-common/abstractions/totp.service";
|
||||||
|
import { TwoFactorService as TwoFactorServiceAbstraction } from "jslib-common/abstractions/twoFactor.service";
|
||||||
|
import { UserVerificationService as UserVerificationServiceAbstraction } from "jslib-common/abstractions/userVerification.service";
|
||||||
|
import { UsernameGenerationService as UsernameGenerationServiceAbstraction } from "jslib-common/abstractions/usernameGeneration.service";
|
||||||
|
import { VaultTimeoutService as VaultTimeoutServiceAbstraction } from "jslib-common/abstractions/vaultTimeout.service";
|
||||||
|
import { StateFactory } from "jslib-common/factories/stateFactory";
|
||||||
|
import { Account } from "jslib-common/models/domain/account";
|
||||||
|
import { GlobalState } from "jslib-common/models/domain/globalState";
|
||||||
|
import { ApiService } from "jslib-common/services/api.service";
|
||||||
|
import { AppIdService } from "jslib-common/services/appId.service";
|
||||||
|
import { AuditService } from "jslib-common/services/audit.service";
|
||||||
|
import { AuthService } from "jslib-common/services/auth.service";
|
||||||
|
import { CipherService } from "jslib-common/services/cipher.service";
|
||||||
|
import { CollectionService } from "jslib-common/services/collection.service";
|
||||||
|
import { ConsoleLogService } from "jslib-common/services/consoleLog.service";
|
||||||
|
import { CryptoService } from "jslib-common/services/crypto.service";
|
||||||
|
import { EnvironmentService } from "jslib-common/services/environment.service";
|
||||||
|
import { EventService } from "jslib-common/services/event.service";
|
||||||
|
import { ExportService } from "jslib-common/services/export.service";
|
||||||
|
import { FileUploadService } from "jslib-common/services/fileUpload.service";
|
||||||
|
import { FolderService } from "jslib-common/services/folder.service";
|
||||||
|
import { KeyConnectorService } from "jslib-common/services/keyConnector.service";
|
||||||
|
import { NotificationsService } from "jslib-common/services/notifications.service";
|
||||||
|
import { OrganizationService } from "jslib-common/services/organization.service";
|
||||||
|
import { PasswordGenerationService } from "jslib-common/services/passwordGeneration.service";
|
||||||
|
import { PolicyService } from "jslib-common/services/policy.service";
|
||||||
|
import { ProviderService } from "jslib-common/services/provider.service";
|
||||||
|
import { SearchService } from "jslib-common/services/search.service";
|
||||||
|
import { SendService } from "jslib-common/services/send.service";
|
||||||
|
import { SettingsService } from "jslib-common/services/settings.service";
|
||||||
|
import { StateService } from "jslib-common/services/state.service";
|
||||||
|
import { StateMigrationService } from "jslib-common/services/stateMigration.service";
|
||||||
|
import { SyncService } from "jslib-common/services/sync.service";
|
||||||
|
import { TokenService } from "jslib-common/services/token.service";
|
||||||
|
import { TotpService } from "jslib-common/services/totp.service";
|
||||||
|
import { TwoFactorService } from "jslib-common/services/twoFactor.service";
|
||||||
|
import { UserVerificationService } from "jslib-common/services/userVerification.service";
|
||||||
|
import { UsernameGenerationService } from "jslib-common/services/usernameGeneration.service";
|
||||||
|
import { VaultTimeoutService } from "jslib-common/services/vaultTimeout.service";
|
||||||
|
import { WebCryptoFunctionService } from "jslib-common/services/webCryptoFunction.service";
|
||||||
|
|
||||||
|
import { AuthGuardService } from "./auth-guard.service";
|
||||||
|
import { BroadcasterService } from "./broadcaster.service";
|
||||||
|
import { LockGuardService } from "./lock-guard.service";
|
||||||
|
import { ModalService } from "./modal.service";
|
||||||
|
import { PasswordRepromptService } from "./passwordReprompt.service";
|
||||||
|
import { UnauthGuardService } from "./unauth-guard.service";
|
||||||
|
import { ValidationService } from "./validation.service";
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
declarations: [],
|
||||||
|
providers: [
|
||||||
|
{ provide: "WINDOW", useValue: window },
|
||||||
|
{
|
||||||
|
provide: LOCALE_ID,
|
||||||
|
useFactory: (i18nService: I18nServiceAbstraction) => i18nService.translationLocale,
|
||||||
|
deps: [I18nServiceAbstraction],
|
||||||
|
},
|
||||||
|
ValidationService,
|
||||||
|
AuthGuardService,
|
||||||
|
UnauthGuardService,
|
||||||
|
LockGuardService,
|
||||||
|
ModalService,
|
||||||
|
{
|
||||||
|
provide: AppIdServiceAbstraction,
|
||||||
|
useClass: AppIdService,
|
||||||
|
deps: [StorageServiceAbstraction],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
provide: AuditServiceAbstraction,
|
||||||
|
useClass: AuditService,
|
||||||
|
deps: [CryptoFunctionServiceAbstraction, ApiServiceAbstraction],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
provide: AuthServiceAbstraction,
|
||||||
|
useClass: AuthService,
|
||||||
|
deps: [
|
||||||
|
CryptoServiceAbstraction,
|
||||||
|
ApiServiceAbstraction,
|
||||||
|
TokenServiceAbstraction,
|
||||||
|
AppIdServiceAbstraction,
|
||||||
|
PlatformUtilsServiceAbstraction,
|
||||||
|
MessagingServiceAbstraction,
|
||||||
|
LogService,
|
||||||
|
KeyConnectorServiceAbstraction,
|
||||||
|
EnvironmentServiceAbstraction,
|
||||||
|
StateServiceAbstraction,
|
||||||
|
TwoFactorServiceAbstraction,
|
||||||
|
I18nServiceAbstraction,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
provide: CipherServiceAbstraction,
|
||||||
|
useFactory: (
|
||||||
|
cryptoService: CryptoServiceAbstraction,
|
||||||
|
settingsService: SettingsServiceAbstraction,
|
||||||
|
apiService: ApiServiceAbstraction,
|
||||||
|
fileUploadService: FileUploadServiceAbstraction,
|
||||||
|
i18nService: I18nServiceAbstraction,
|
||||||
|
injector: Injector,
|
||||||
|
logService: LogService,
|
||||||
|
stateService: StateServiceAbstraction
|
||||||
|
) =>
|
||||||
|
new CipherService(
|
||||||
|
cryptoService,
|
||||||
|
settingsService,
|
||||||
|
apiService,
|
||||||
|
fileUploadService,
|
||||||
|
i18nService,
|
||||||
|
() => injector.get(SearchServiceAbstraction),
|
||||||
|
logService,
|
||||||
|
stateService
|
||||||
|
),
|
||||||
|
deps: [
|
||||||
|
CryptoServiceAbstraction,
|
||||||
|
SettingsServiceAbstraction,
|
||||||
|
ApiServiceAbstraction,
|
||||||
|
FileUploadServiceAbstraction,
|
||||||
|
I18nServiceAbstraction,
|
||||||
|
Injector, // TODO: Get rid of this circular dependency!
|
||||||
|
LogService,
|
||||||
|
StateServiceAbstraction,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
provide: FolderServiceAbstraction,
|
||||||
|
useClass: FolderService,
|
||||||
|
deps: [
|
||||||
|
CryptoServiceAbstraction,
|
||||||
|
ApiServiceAbstraction,
|
||||||
|
I18nServiceAbstraction,
|
||||||
|
CipherServiceAbstraction,
|
||||||
|
StateServiceAbstraction,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{ provide: LogService, useFactory: () => new ConsoleLogService(false) },
|
||||||
|
{
|
||||||
|
provide: CollectionServiceAbstraction,
|
||||||
|
useClass: CollectionService,
|
||||||
|
deps: [CryptoServiceAbstraction, I18nServiceAbstraction, StateServiceAbstraction],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
provide: EnvironmentServiceAbstraction,
|
||||||
|
useClass: EnvironmentService,
|
||||||
|
deps: [StateServiceAbstraction],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
provide: TotpServiceAbstraction,
|
||||||
|
useClass: TotpService,
|
||||||
|
deps: [CryptoFunctionServiceAbstraction, LogService, StateServiceAbstraction],
|
||||||
|
},
|
||||||
|
{ provide: TokenServiceAbstraction, useClass: TokenService, deps: [StateServiceAbstraction] },
|
||||||
|
{
|
||||||
|
provide: CryptoServiceAbstraction,
|
||||||
|
useClass: CryptoService,
|
||||||
|
deps: [
|
||||||
|
CryptoFunctionServiceAbstraction,
|
||||||
|
PlatformUtilsServiceAbstraction,
|
||||||
|
LogService,
|
||||||
|
StateServiceAbstraction,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
provide: PasswordGenerationServiceAbstraction,
|
||||||
|
useClass: PasswordGenerationService,
|
||||||
|
deps: [CryptoServiceAbstraction, PolicyServiceAbstraction, StateServiceAbstraction],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
provide: UsernameGenerationServiceAbstraction,
|
||||||
|
useClass: UsernameGenerationService,
|
||||||
|
deps: [CryptoServiceAbstraction, StateServiceAbstraction],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
provide: ApiServiceAbstraction,
|
||||||
|
useFactory: (
|
||||||
|
tokenService: TokenServiceAbstraction,
|
||||||
|
platformUtilsService: PlatformUtilsServiceAbstraction,
|
||||||
|
environmentService: EnvironmentServiceAbstraction,
|
||||||
|
messagingService: MessagingServiceAbstraction,
|
||||||
|
appIdService: AppIdServiceAbstraction
|
||||||
|
) =>
|
||||||
|
new ApiService(
|
||||||
|
tokenService,
|
||||||
|
platformUtilsService,
|
||||||
|
environmentService,
|
||||||
|
appIdService,
|
||||||
|
async (expired: boolean) => messagingService.send("logout", { expired: expired })
|
||||||
|
),
|
||||||
|
deps: [
|
||||||
|
TokenServiceAbstraction,
|
||||||
|
PlatformUtilsServiceAbstraction,
|
||||||
|
EnvironmentServiceAbstraction,
|
||||||
|
MessagingServiceAbstraction,
|
||||||
|
AppIdServiceAbstraction,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
provide: FileUploadServiceAbstraction,
|
||||||
|
useClass: FileUploadService,
|
||||||
|
deps: [LogService, ApiServiceAbstraction],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
provide: SyncServiceAbstraction,
|
||||||
|
useFactory: (
|
||||||
|
apiService: ApiServiceAbstraction,
|
||||||
|
settingsService: SettingsServiceAbstraction,
|
||||||
|
folderService: FolderServiceAbstraction,
|
||||||
|
cipherService: CipherServiceAbstraction,
|
||||||
|
cryptoService: CryptoServiceAbstraction,
|
||||||
|
collectionService: CollectionServiceAbstraction,
|
||||||
|
messagingService: MessagingServiceAbstraction,
|
||||||
|
policyService: PolicyServiceAbstraction,
|
||||||
|
sendService: SendServiceAbstraction,
|
||||||
|
logService: LogService,
|
||||||
|
keyConnectorService: KeyConnectorServiceAbstraction,
|
||||||
|
stateService: StateServiceAbstraction,
|
||||||
|
organizationService: OrganizationServiceAbstraction,
|
||||||
|
providerService: ProviderServiceAbstraction
|
||||||
|
) =>
|
||||||
|
new SyncService(
|
||||||
|
apiService,
|
||||||
|
settingsService,
|
||||||
|
folderService,
|
||||||
|
cipherService,
|
||||||
|
cryptoService,
|
||||||
|
collectionService,
|
||||||
|
messagingService,
|
||||||
|
policyService,
|
||||||
|
sendService,
|
||||||
|
logService,
|
||||||
|
keyConnectorService,
|
||||||
|
stateService,
|
||||||
|
organizationService,
|
||||||
|
providerService,
|
||||||
|
async (expired: boolean) => messagingService.send("logout", { expired: expired })
|
||||||
|
),
|
||||||
|
deps: [
|
||||||
|
ApiServiceAbstraction,
|
||||||
|
SettingsServiceAbstraction,
|
||||||
|
FolderServiceAbstraction,
|
||||||
|
CipherServiceAbstraction,
|
||||||
|
CryptoServiceAbstraction,
|
||||||
|
CollectionServiceAbstraction,
|
||||||
|
MessagingServiceAbstraction,
|
||||||
|
PolicyServiceAbstraction,
|
||||||
|
SendServiceAbstraction,
|
||||||
|
LogService,
|
||||||
|
KeyConnectorServiceAbstraction,
|
||||||
|
StateServiceAbstraction,
|
||||||
|
OrganizationServiceAbstraction,
|
||||||
|
ProviderServiceAbstraction,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{ provide: BroadcasterServiceAbstraction, useClass: BroadcasterService },
|
||||||
|
{
|
||||||
|
provide: SettingsServiceAbstraction,
|
||||||
|
useClass: SettingsService,
|
||||||
|
deps: [StateServiceAbstraction],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
provide: VaultTimeoutServiceAbstraction,
|
||||||
|
useFactory: (
|
||||||
|
cipherService: CipherServiceAbstraction,
|
||||||
|
folderService: FolderServiceAbstraction,
|
||||||
|
collectionService: CollectionServiceAbstraction,
|
||||||
|
cryptoService: CryptoServiceAbstraction,
|
||||||
|
platformUtilsService: PlatformUtilsServiceAbstraction,
|
||||||
|
messagingService: MessagingServiceAbstraction,
|
||||||
|
searchService: SearchServiceAbstraction,
|
||||||
|
tokenService: TokenServiceAbstraction,
|
||||||
|
policyService: PolicyServiceAbstraction,
|
||||||
|
keyConnectorService: KeyConnectorServiceAbstraction,
|
||||||
|
stateService: StateServiceAbstraction
|
||||||
|
) =>
|
||||||
|
new VaultTimeoutService(
|
||||||
|
cipherService,
|
||||||
|
folderService,
|
||||||
|
collectionService,
|
||||||
|
cryptoService,
|
||||||
|
platformUtilsService,
|
||||||
|
messagingService,
|
||||||
|
searchService,
|
||||||
|
tokenService,
|
||||||
|
policyService,
|
||||||
|
keyConnectorService,
|
||||||
|
stateService,
|
||||||
|
null,
|
||||||
|
async (userId?: string) =>
|
||||||
|
messagingService.send("logout", { expired: false, userId: userId })
|
||||||
|
),
|
||||||
|
deps: [
|
||||||
|
CipherServiceAbstraction,
|
||||||
|
FolderServiceAbstraction,
|
||||||
|
CollectionServiceAbstraction,
|
||||||
|
CryptoServiceAbstraction,
|
||||||
|
PlatformUtilsServiceAbstraction,
|
||||||
|
MessagingServiceAbstraction,
|
||||||
|
SearchServiceAbstraction,
|
||||||
|
TokenServiceAbstraction,
|
||||||
|
PolicyServiceAbstraction,
|
||||||
|
KeyConnectorServiceAbstraction,
|
||||||
|
StateServiceAbstraction,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
provide: StateServiceAbstraction,
|
||||||
|
useFactory: (
|
||||||
|
storageService: StorageServiceAbstraction,
|
||||||
|
secureStorageService: StorageServiceAbstraction,
|
||||||
|
logService: LogService,
|
||||||
|
stateMigrationService: StateMigrationServiceAbstraction
|
||||||
|
) =>
|
||||||
|
new StateService(
|
||||||
|
storageService,
|
||||||
|
secureStorageService,
|
||||||
|
logService,
|
||||||
|
stateMigrationService,
|
||||||
|
new StateFactory(GlobalState, Account)
|
||||||
|
),
|
||||||
|
deps: [
|
||||||
|
StorageServiceAbstraction,
|
||||||
|
"SECURE_STORAGE",
|
||||||
|
LogService,
|
||||||
|
StateMigrationServiceAbstraction,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
provide: StateMigrationServiceAbstraction,
|
||||||
|
useFactory: (
|
||||||
|
storageService: StorageServiceAbstraction,
|
||||||
|
secureStorageService: StorageServiceAbstraction
|
||||||
|
) =>
|
||||||
|
new StateMigrationService(
|
||||||
|
storageService,
|
||||||
|
secureStorageService,
|
||||||
|
new StateFactory(GlobalState, Account)
|
||||||
|
),
|
||||||
|
deps: [StorageServiceAbstraction, "SECURE_STORAGE"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
provide: ExportServiceAbstraction,
|
||||||
|
useClass: ExportService,
|
||||||
|
deps: [
|
||||||
|
FolderServiceAbstraction,
|
||||||
|
CipherServiceAbstraction,
|
||||||
|
ApiServiceAbstraction,
|
||||||
|
CryptoServiceAbstraction,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
provide: SearchServiceAbstraction,
|
||||||
|
useClass: SearchService,
|
||||||
|
deps: [CipherServiceAbstraction, LogService, I18nServiceAbstraction],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
provide: NotificationsServiceAbstraction,
|
||||||
|
useFactory: (
|
||||||
|
syncService: SyncServiceAbstraction,
|
||||||
|
appIdService: AppIdServiceAbstraction,
|
||||||
|
apiService: ApiServiceAbstraction,
|
||||||
|
vaultTimeoutService: VaultTimeoutServiceAbstraction,
|
||||||
|
environmentService: EnvironmentServiceAbstraction,
|
||||||
|
messagingService: MessagingServiceAbstraction,
|
||||||
|
logService: LogService,
|
||||||
|
stateService: StateServiceAbstraction
|
||||||
|
) =>
|
||||||
|
new NotificationsService(
|
||||||
|
syncService,
|
||||||
|
appIdService,
|
||||||
|
apiService,
|
||||||
|
vaultTimeoutService,
|
||||||
|
environmentService,
|
||||||
|
async () => messagingService.send("logout", { expired: true }),
|
||||||
|
logService,
|
||||||
|
stateService
|
||||||
|
),
|
||||||
|
deps: [
|
||||||
|
SyncServiceAbstraction,
|
||||||
|
AppIdServiceAbstraction,
|
||||||
|
ApiServiceAbstraction,
|
||||||
|
VaultTimeoutServiceAbstraction,
|
||||||
|
EnvironmentServiceAbstraction,
|
||||||
|
MessagingServiceAbstraction,
|
||||||
|
LogService,
|
||||||
|
StateServiceAbstraction,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
provide: CryptoFunctionServiceAbstraction,
|
||||||
|
useClass: WebCryptoFunctionService,
|
||||||
|
deps: ["WINDOW"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
provide: EventServiceAbstraction,
|
||||||
|
useClass: EventService,
|
||||||
|
deps: [
|
||||||
|
ApiServiceAbstraction,
|
||||||
|
CipherServiceAbstraction,
|
||||||
|
StateServiceAbstraction,
|
||||||
|
LogService,
|
||||||
|
OrganizationServiceAbstraction,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
provide: PolicyServiceAbstraction,
|
||||||
|
useClass: PolicyService,
|
||||||
|
deps: [StateServiceAbstraction, OrganizationServiceAbstraction, ApiServiceAbstraction],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
provide: SendServiceAbstraction,
|
||||||
|
useClass: SendService,
|
||||||
|
deps: [
|
||||||
|
CryptoServiceAbstraction,
|
||||||
|
ApiServiceAbstraction,
|
||||||
|
FileUploadServiceAbstraction,
|
||||||
|
I18nServiceAbstraction,
|
||||||
|
CryptoFunctionServiceAbstraction,
|
||||||
|
StateServiceAbstraction,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
provide: KeyConnectorServiceAbstraction,
|
||||||
|
useClass: KeyConnectorService,
|
||||||
|
deps: [
|
||||||
|
StateServiceAbstraction,
|
||||||
|
CryptoServiceAbstraction,
|
||||||
|
ApiServiceAbstraction,
|
||||||
|
TokenServiceAbstraction,
|
||||||
|
LogService,
|
||||||
|
OrganizationServiceAbstraction,
|
||||||
|
CryptoFunctionServiceAbstraction,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
provide: UserVerificationServiceAbstraction,
|
||||||
|
useClass: UserVerificationService,
|
||||||
|
deps: [CryptoServiceAbstraction, I18nServiceAbstraction, ApiServiceAbstraction],
|
||||||
|
},
|
||||||
|
{ provide: PasswordRepromptServiceAbstraction, useClass: PasswordRepromptService },
|
||||||
|
{
|
||||||
|
provide: OrganizationServiceAbstraction,
|
||||||
|
useClass: OrganizationService,
|
||||||
|
deps: [StateServiceAbstraction],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
provide: ProviderServiceAbstraction,
|
||||||
|
useClass: ProviderService,
|
||||||
|
deps: [StateServiceAbstraction],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
provide: TwoFactorServiceAbstraction,
|
||||||
|
useClass: TwoFactorService,
|
||||||
|
deps: [I18nServiceAbstraction, PlatformUtilsServiceAbstraction],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
export class JslibServicesModule {}
|
||||||
29
jslib/angular/src/services/lock-guard.service.ts
Normal file
29
jslib/angular/src/services/lock-guard.service.ts
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
import { Injectable } from "@angular/core";
|
||||||
|
import { CanActivate, Router } from "@angular/router";
|
||||||
|
|
||||||
|
import { StateService } from "jslib-common/abstractions/state.service";
|
||||||
|
import { VaultTimeoutService } from "jslib-common/abstractions/vaultTimeout.service";
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class LockGuardService implements CanActivate {
|
||||||
|
protected homepage = "vault";
|
||||||
|
protected loginpage = "login";
|
||||||
|
constructor(
|
||||||
|
private vaultTimeoutService: VaultTimeoutService,
|
||||||
|
private router: Router,
|
||||||
|
private stateService: StateService
|
||||||
|
) {}
|
||||||
|
|
||||||
|
async canActivate() {
|
||||||
|
if (await this.vaultTimeoutService.isLocked()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const redirectUrl = (await this.stateService.getIsAuthenticated())
|
||||||
|
? [this.homepage]
|
||||||
|
: [this.loginpage];
|
||||||
|
|
||||||
|
this.router.navigate(redirectUrl);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
180
jslib/angular/src/services/modal.service.ts
Normal file
180
jslib/angular/src/services/modal.service.ts
Normal file
@@ -0,0 +1,180 @@
|
|||||||
|
import {
|
||||||
|
ApplicationRef,
|
||||||
|
ComponentFactory,
|
||||||
|
ComponentFactoryResolver,
|
||||||
|
ComponentRef,
|
||||||
|
EmbeddedViewRef,
|
||||||
|
Injectable,
|
||||||
|
Injector,
|
||||||
|
Type,
|
||||||
|
ViewContainerRef,
|
||||||
|
} from "@angular/core";
|
||||||
|
import { first } from "rxjs/operators";
|
||||||
|
|
||||||
|
import { DynamicModalComponent } from "../components/modal/dynamic-modal.component";
|
||||||
|
import { ModalInjector } from "../components/modal/modal-injector";
|
||||||
|
import { ModalRef } from "../components/modal/modal.ref";
|
||||||
|
|
||||||
|
export class ModalConfig<D = any> {
|
||||||
|
data?: D;
|
||||||
|
allowMultipleModals = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class ModalService {
|
||||||
|
protected modalList: ComponentRef<DynamicModalComponent>[] = [];
|
||||||
|
|
||||||
|
// Lazy loaded modules are not available in componentFactoryResolver,
|
||||||
|
// therefore modules needs to manually initialize their resolvers.
|
||||||
|
private factoryResolvers: Map<Type<any>, ComponentFactoryResolver> = new Map();
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private componentFactoryResolver: ComponentFactoryResolver,
|
||||||
|
private applicationRef: ApplicationRef,
|
||||||
|
private injector: Injector
|
||||||
|
) {
|
||||||
|
document.addEventListener("keyup", (event) => {
|
||||||
|
if (event.key === "Escape" && this.modalCount > 0) {
|
||||||
|
this.topModal.instance.close();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
get modalCount() {
|
||||||
|
return this.modalList.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
private get topModal() {
|
||||||
|
return this.modalList[this.modalCount - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
async openViewRef<T>(
|
||||||
|
componentType: Type<T>,
|
||||||
|
viewContainerRef: ViewContainerRef,
|
||||||
|
setComponentParameters: (component: T) => void = null
|
||||||
|
): Promise<[ModalRef, T]> {
|
||||||
|
const [modalRef, modalComponentRef] = this.openInternal(componentType, null, false);
|
||||||
|
modalComponentRef.instance.setComponentParameters = setComponentParameters;
|
||||||
|
|
||||||
|
viewContainerRef.insert(modalComponentRef.hostView);
|
||||||
|
|
||||||
|
await modalRef.onCreated.pipe(first()).toPromise();
|
||||||
|
|
||||||
|
return [modalRef, modalComponentRef.instance.componentRef.instance];
|
||||||
|
}
|
||||||
|
|
||||||
|
open(componentType: Type<any>, config?: ModalConfig) {
|
||||||
|
if (!(config?.allowMultipleModals ?? false) && this.modalCount > 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line
|
||||||
|
const [modalRef, _] = this.openInternal(componentType, config, true);
|
||||||
|
|
||||||
|
return modalRef;
|
||||||
|
}
|
||||||
|
|
||||||
|
registerComponentFactoryResolver<T>(
|
||||||
|
componentType: Type<T>,
|
||||||
|
componentFactoryResolver: ComponentFactoryResolver
|
||||||
|
): void {
|
||||||
|
this.factoryResolvers.set(componentType, componentFactoryResolver);
|
||||||
|
}
|
||||||
|
|
||||||
|
resolveComponentFactory<T>(componentType: Type<T>): ComponentFactory<T> {
|
||||||
|
if (this.factoryResolvers.has(componentType)) {
|
||||||
|
return this.factoryResolvers.get(componentType).resolveComponentFactory(componentType);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.componentFactoryResolver.resolveComponentFactory(componentType);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected openInternal(
|
||||||
|
componentType: Type<any>,
|
||||||
|
config?: ModalConfig,
|
||||||
|
attachToDom?: boolean
|
||||||
|
): [ModalRef, ComponentRef<DynamicModalComponent>] {
|
||||||
|
const [modalRef, componentRef] = this.createModalComponent(config);
|
||||||
|
componentRef.instance.childComponentType = componentType;
|
||||||
|
|
||||||
|
if (attachToDom) {
|
||||||
|
this.applicationRef.attachView(componentRef.hostView);
|
||||||
|
const domElem = (componentRef.hostView as EmbeddedViewRef<any>).rootNodes[0] as HTMLElement;
|
||||||
|
document.body.appendChild(domElem);
|
||||||
|
}
|
||||||
|
|
||||||
|
modalRef.onClosed.pipe(first()).subscribe(() => {
|
||||||
|
if (attachToDom) {
|
||||||
|
this.applicationRef.detachView(componentRef.hostView);
|
||||||
|
}
|
||||||
|
componentRef.destroy();
|
||||||
|
|
||||||
|
this.modalList.pop();
|
||||||
|
if (this.modalCount > 0) {
|
||||||
|
this.topModal.instance.getFocus();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.setupHandlers(modalRef);
|
||||||
|
|
||||||
|
this.modalList.push(componentRef);
|
||||||
|
|
||||||
|
return [modalRef, componentRef];
|
||||||
|
}
|
||||||
|
|
||||||
|
protected setupHandlers(modalRef: ModalRef) {
|
||||||
|
let backdrop: HTMLElement = null;
|
||||||
|
|
||||||
|
// Add backdrop, setup [data-dismiss] handler.
|
||||||
|
modalRef.onCreated.pipe(first()).subscribe((el) => {
|
||||||
|
document.body.classList.add("modal-open");
|
||||||
|
|
||||||
|
const modalEl: HTMLElement = el.querySelector(".modal");
|
||||||
|
const dialogEl = modalEl.querySelector(".modal-dialog") as HTMLElement;
|
||||||
|
|
||||||
|
backdrop = document.createElement("div");
|
||||||
|
backdrop.className = "modal-backdrop fade";
|
||||||
|
backdrop.style.zIndex = `${this.modalCount}040`;
|
||||||
|
modalEl.prepend(backdrop);
|
||||||
|
|
||||||
|
dialogEl.addEventListener("click", (e: Event) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
});
|
||||||
|
dialogEl.style.zIndex = `${this.modalCount}050`;
|
||||||
|
|
||||||
|
const modals = Array.from(
|
||||||
|
el.querySelectorAll('.modal-backdrop, .modal *[data-dismiss="modal"]')
|
||||||
|
);
|
||||||
|
for (const closeElement of modals) {
|
||||||
|
closeElement.addEventListener("click", () => {
|
||||||
|
modalRef.close();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// onClose is used in Web to hook into bootstrap. On other projects we pipe it directly to closed.
|
||||||
|
modalRef.onClose.pipe(first()).subscribe(() => {
|
||||||
|
modalRef.closed();
|
||||||
|
|
||||||
|
if (this.modalCount === 0) {
|
||||||
|
document.body.classList.remove("modal-open");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
protected createModalComponent(
|
||||||
|
config: ModalConfig
|
||||||
|
): [ModalRef, ComponentRef<DynamicModalComponent>] {
|
||||||
|
const modalRef = new ModalRef();
|
||||||
|
|
||||||
|
const map = new WeakMap();
|
||||||
|
map.set(ModalConfig, config);
|
||||||
|
map.set(ModalRef, modalRef);
|
||||||
|
|
||||||
|
const componentFactory =
|
||||||
|
this.componentFactoryResolver.resolveComponentFactory(DynamicModalComponent);
|
||||||
|
const componentRef = componentFactory.create(new ModalInjector(this.injector, map));
|
||||||
|
|
||||||
|
return [modalRef, componentRef];
|
||||||
|
}
|
||||||
|
}
|
||||||
45
jslib/angular/src/services/passwordReprompt.service.ts
Normal file
45
jslib/angular/src/services/passwordReprompt.service.ts
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
import { Injectable } from "@angular/core";
|
||||||
|
|
||||||
|
import { KeyConnectorService } from "jslib-common/abstractions/keyConnector.service";
|
||||||
|
import { PasswordRepromptService as PasswordRepromptServiceAbstraction } from "jslib-common/abstractions/passwordReprompt.service";
|
||||||
|
|
||||||
|
import { PasswordRepromptComponent } from "../components/password-reprompt.component";
|
||||||
|
|
||||||
|
import { ModalService } from "./modal.service";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used to verify the user's Master Password for the "Master Password Re-prompt" feature only.
|
||||||
|
* See UserVerificationService for any other situation where you need to verify the user's identity.
|
||||||
|
*/
|
||||||
|
@Injectable()
|
||||||
|
export class PasswordRepromptService implements PasswordRepromptServiceAbstraction {
|
||||||
|
protected component = PasswordRepromptComponent;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private modalService: ModalService,
|
||||||
|
private keyConnectorService: KeyConnectorService
|
||||||
|
) {}
|
||||||
|
|
||||||
|
protectedFields() {
|
||||||
|
return ["TOTP", "Password", "H_Field", "Card Number", "Security Code"];
|
||||||
|
}
|
||||||
|
|
||||||
|
async showPasswordPrompt() {
|
||||||
|
if (!(await this.enabled())) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ref = this.modalService.open(this.component, { allowMultipleModals: true });
|
||||||
|
|
||||||
|
if (ref == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await ref.onClosedPromise();
|
||||||
|
return result === true;
|
||||||
|
}
|
||||||
|
|
||||||
|
async enabled() {
|
||||||
|
return !(await this.keyConnectorService.getUsesKeyConnector());
|
||||||
|
}
|
||||||
|
}
|
||||||
29
jslib/angular/src/services/unauth-guard.service.ts
Normal file
29
jslib/angular/src/services/unauth-guard.service.ts
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
import { Injectable } from "@angular/core";
|
||||||
|
import { CanActivate, Router } from "@angular/router";
|
||||||
|
|
||||||
|
import { StateService } from "jslib-common/abstractions/state.service";
|
||||||
|
import { VaultTimeoutService } from "jslib-common/abstractions/vaultTimeout.service";
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class UnauthGuardService implements CanActivate {
|
||||||
|
protected homepage = "vault";
|
||||||
|
constructor(
|
||||||
|
private vaultTimeoutService: VaultTimeoutService,
|
||||||
|
private router: Router,
|
||||||
|
private stateService: StateService
|
||||||
|
) {}
|
||||||
|
|
||||||
|
async canActivate() {
|
||||||
|
const isAuthed = await this.stateService.getIsAuthenticated();
|
||||||
|
if (isAuthed) {
|
||||||
|
const locked = await this.vaultTimeoutService.isLocked();
|
||||||
|
if (locked) {
|
||||||
|
this.router.navigate(["lock"]);
|
||||||
|
} else {
|
||||||
|
this.router.navigate([this.homepage]);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
38
jslib/angular/src/services/validation.service.ts
Normal file
38
jslib/angular/src/services/validation.service.ts
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
import { Injectable } from "@angular/core";
|
||||||
|
|
||||||
|
import { I18nService } from "jslib-common/abstractions/i18n.service";
|
||||||
|
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
|
||||||
|
import { ErrorResponse } from "jslib-common/models/response/errorResponse";
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class ValidationService {
|
||||||
|
constructor(
|
||||||
|
private i18nService: I18nService,
|
||||||
|
private platformUtilsService: PlatformUtilsService
|
||||||
|
) {}
|
||||||
|
|
||||||
|
showError(data: any): string[] {
|
||||||
|
const defaultErrorMessage = this.i18nService.t("unexpectedError");
|
||||||
|
let errors: string[] = [];
|
||||||
|
|
||||||
|
if (data != null && typeof data === "string") {
|
||||||
|
errors.push(data);
|
||||||
|
} else if (data == null || typeof data !== "object") {
|
||||||
|
errors.push(defaultErrorMessage);
|
||||||
|
} else if (data.validationErrors != null) {
|
||||||
|
errors = errors.concat((data as ErrorResponse).getAllMessages());
|
||||||
|
} else {
|
||||||
|
errors.push(data.message ? data.message : defaultErrorMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (errors.length === 1) {
|
||||||
|
this.platformUtilsService.showToast("error", this.i18nService.t("errorOccurred"), errors[0]);
|
||||||
|
} else if (errors.length > 1) {
|
||||||
|
this.platformUtilsService.showToast("error", this.i18nService.t("errorOccurred"), errors, {
|
||||||
|
timeout: 5000 * errors.length,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return errors;
|
||||||
|
}
|
||||||
|
}
|
||||||
11
jslib/angular/tsconfig.json
Normal file
11
jslib/angular/tsconfig.json
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"extends": "../shared/tsconfig",
|
||||||
|
"compilerOptions": {
|
||||||
|
"paths": {
|
||||||
|
"jslib-common/*": ["../common/src/*"],
|
||||||
|
"jslib-angular/*": ["./src/"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"include": ["src", "spec"],
|
||||||
|
"exclude": ["node_modules", "dist"]
|
||||||
|
}
|
||||||
3
jslib/angular/tsconfig.spec.json
Normal file
3
jslib/angular/tsconfig.spec.json
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"extends": "./tsconfig.json"
|
||||||
|
}
|
||||||
18
jslib/common/jest.config.js
Normal file
18
jslib/common/jest.config.js
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
const { pathsToModuleNameMapper } = require("ts-jest/utils");
|
||||||
|
|
||||||
|
const { compilerOptions } = require("./tsconfig");
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
name: "common",
|
||||||
|
displayName: "common jslib tests",
|
||||||
|
preset: "ts-jest",
|
||||||
|
testEnvironment: "jsdom",
|
||||||
|
testMatch: ["**/+(*.)+(spec).+(ts)"],
|
||||||
|
setupFilesAfterEnv: ["<rootDir>/spec/test.ts"],
|
||||||
|
collectCoverage: true,
|
||||||
|
coverageReporters: ["html", "lcov"],
|
||||||
|
coverageDirectory: "coverage",
|
||||||
|
moduleNameMapper: pathsToModuleNameMapper(compilerOptions?.paths || {}, {
|
||||||
|
prefix: "<rootDir>/",
|
||||||
|
}),
|
||||||
|
};
|
||||||
956
jslib/common/package-lock.json
generated
Normal file
956
jslib/common/package-lock.json
generated
Normal file
@@ -0,0 +1,956 @@
|
|||||||
|
{
|
||||||
|
"name": "@bitwarden/jslib-common",
|
||||||
|
"version": "0.0.0",
|
||||||
|
"lockfileVersion": 2,
|
||||||
|
"requires": true,
|
||||||
|
"packages": {
|
||||||
|
"": {
|
||||||
|
"name": "@bitwarden/jslib-common",
|
||||||
|
"version": "0.0.0",
|
||||||
|
"license": "GPL-3.0",
|
||||||
|
"dependencies": {
|
||||||
|
"@microsoft/signalr": "5.0.10",
|
||||||
|
"@microsoft/signalr-protocol-msgpack": "5.0.10",
|
||||||
|
"big-integer": "1.6.48",
|
||||||
|
"browser-hrtime": "^1.1.8",
|
||||||
|
"lunr": "^2.3.9",
|
||||||
|
"node-forge": "^1.2.1",
|
||||||
|
"papaparse": "^5.3.0",
|
||||||
|
"rxjs": "^7.4.0",
|
||||||
|
"tldjs": "^2.3.1",
|
||||||
|
"zxcvbn": "^4.4.2"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/lunr": "^2.3.3",
|
||||||
|
"@types/node": "^16.11.12",
|
||||||
|
"@types/node-forge": "^1.0.1",
|
||||||
|
"@types/papaparse": "^5.2.5",
|
||||||
|
"@types/tldjs": "^2.3.0",
|
||||||
|
"@types/zxcvbn": "^4.4.1",
|
||||||
|
"rimraf": "^3.0.2",
|
||||||
|
"typescript": "4.3.5"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@microsoft/signalr": {
|
||||||
|
"version": "5.0.10",
|
||||||
|
"resolved": "https://registry.npmjs.org/@microsoft/signalr/-/signalr-5.0.10.tgz",
|
||||||
|
"integrity": "sha512-7jg6s/cmULyeVvt5/bTB4N9T30HvAF1S06hL+nPcQMODXcclRo34Zcli/dfTLR8lCX31/cVEOmVgxXBOVRQ+Dw==",
|
||||||
|
"dependencies": {
|
||||||
|
"abort-controller": "^3.0.0",
|
||||||
|
"eventsource": "^1.0.7",
|
||||||
|
"fetch-cookie": "^0.7.3",
|
||||||
|
"node-fetch": "^2.6.0",
|
||||||
|
"ws": "^6.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@microsoft/signalr-protocol-msgpack": {
|
||||||
|
"version": "5.0.10",
|
||||||
|
"resolved": "https://registry.npmjs.org/@microsoft/signalr-protocol-msgpack/-/signalr-protocol-msgpack-5.0.10.tgz",
|
||||||
|
"integrity": "sha512-HqZiNLyjYP1ONeLgYUjFBUsnhxSp5CW4AW8InsLI7lyAXZl2drUhkiBxf3xK9UsTErO1+9r5sdaYdSmUY8nx9A==",
|
||||||
|
"dependencies": {
|
||||||
|
"@microsoft/signalr": ">=5.0.10",
|
||||||
|
"msgpack5": "^4.5.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@types/lunr": {
|
||||||
|
"version": "2.3.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/lunr/-/lunr-2.3.4.tgz",
|
||||||
|
"integrity": "sha512-j4x4XJwZvorEUbA519VdQ5b9AOU9TSvfi8tvxMAfP8XzNLtFex7A8vFQwqOx3WACbV0KMXbACV3cZl4/gynQ7g==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"node_modules/@types/node": {
|
||||||
|
"version": "16.11.26",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.26.tgz",
|
||||||
|
"integrity": "sha512-GZ7bu5A6+4DtG7q9GsoHXy3ALcgeIHP4NnL0Vv2wu0uUB/yQex26v0tf6/na1mm0+bS9Uw+0DFex7aaKr2qawQ==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"node_modules/@types/node-forge": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/node-forge/-/node-forge-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-96ELNKv9tQJ19afdBUiM5iDw7OYEc53iUc51gAPR2aGaqRsO1DBROjqgZRjZa1tkPj7TnEOR0EnyAX6iryGkzA==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@types/node": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@types/papaparse": {
|
||||||
|
"version": "5.3.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/papaparse/-/papaparse-5.3.2.tgz",
|
||||||
|
"integrity": "sha512-BNbCHJkTE4RwmAFkCxEalET4mDvGr/1ld7ZtQ4i/laWI/iiVt+GL07stdvufle4KfywyvloqqpIiJscXNCrKxA==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@types/node": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@types/tldjs": {
|
||||||
|
"version": "2.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/tldjs/-/tldjs-2.3.1.tgz",
|
||||||
|
"integrity": "sha512-BQR04zLE0ve2eNrqxXw/Qp/f6LxvNrj/4A8ZgdQi3SzbBqxFhleI7N4DS/mSjDnODrUaEGgoWg4grAZR1kVj8w==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"node_modules/@types/zxcvbn": {
|
||||||
|
"version": "4.4.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/zxcvbn/-/zxcvbn-4.4.1.tgz",
|
||||||
|
"integrity": "sha512-3NoqvZC2W5gAC5DZbTpCeJ251vGQmgcWIHQJGq2J240HY6ErQ9aWKkwfoKJlHLx+A83WPNTZ9+3cd2ILxbvr1w==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"node_modules/abort-controller": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==",
|
||||||
|
"dependencies": {
|
||||||
|
"event-target-shim": "^5.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.5"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/async-limiter": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ=="
|
||||||
|
},
|
||||||
|
"node_modules/balanced-match": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
||||||
|
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"node_modules/big-integer": {
|
||||||
|
"version": "1.6.48",
|
||||||
|
"resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.48.tgz",
|
||||||
|
"integrity": "sha512-j51egjPa7/i+RdiRuJbPdJ2FIUYYPhvYLjzoYbcMMm62ooO6F94fETG4MTs46zPAF9Brs04OajboA/qTGuz78w==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/bl": {
|
||||||
|
"version": "2.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/bl/-/bl-2.2.1.tgz",
|
||||||
|
"integrity": "sha512-6Pesp1w0DEX1N550i/uGV/TqucVL4AM/pgThFSN/Qq9si1/DF9aIHs1BxD8V/QU0HoeHO6cQRTAuYnLPKq1e4g==",
|
||||||
|
"dependencies": {
|
||||||
|
"readable-stream": "^2.3.5",
|
||||||
|
"safe-buffer": "^5.1.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/brace-expansion": {
|
||||||
|
"version": "1.1.11",
|
||||||
|
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
|
||||||
|
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"balanced-match": "^1.0.0",
|
||||||
|
"concat-map": "0.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/browser-hrtime": {
|
||||||
|
"version": "1.1.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/browser-hrtime/-/browser-hrtime-1.1.8.tgz",
|
||||||
|
"integrity": "sha512-kzXheikaJsBtzUBlyVtPIY5r0soQePzjwVwT4IlDpU2RvfB5Py52gpU98M77rgqMCheoSSZvrcrdj3t6cZ3suA=="
|
||||||
|
},
|
||||||
|
"node_modules/concat-map": {
|
||||||
|
"version": "0.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
|
||||||
|
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"node_modules/core-util-is": {
|
||||||
|
"version": "1.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz",
|
||||||
|
"integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ=="
|
||||||
|
},
|
||||||
|
"node_modules/es6-denodeify": {
|
||||||
|
"version": "0.1.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/es6-denodeify/-/es6-denodeify-0.1.5.tgz",
|
||||||
|
"integrity": "sha1-MdTV/pxVA+ElRgQ5MQ4WoqPznB8="
|
||||||
|
},
|
||||||
|
"node_modules/event-target-shim": {
|
||||||
|
"version": "5.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz",
|
||||||
|
"integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/eventsource": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/eventsource/-/eventsource-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-VSJjT5oCNrFvCS6igjzPAt5hBzQ2qPBFIbJ03zLI9SE0mxwZpMw6BfJrbFHm1a141AavMEB8JHmBhWAd66PfCg==",
|
||||||
|
"dependencies": {
|
||||||
|
"original": "^1.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.12.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/fetch-cookie": {
|
||||||
|
"version": "0.7.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/fetch-cookie/-/fetch-cookie-0.7.3.tgz",
|
||||||
|
"integrity": "sha512-rZPkLnI8x5V+zYAiz8QonAHsTb4BY+iFowFBI1RFn0zrO343AVp9X7/yUj/9wL6Ef/8fLls8b/vGtzUvmyAUGA==",
|
||||||
|
"dependencies": {
|
||||||
|
"es6-denodeify": "^0.1.1",
|
||||||
|
"tough-cookie": "^2.3.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/fs.realpath": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
|
||||||
|
"integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"node_modules/glob": {
|
||||||
|
"version": "7.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz",
|
||||||
|
"integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"fs.realpath": "^1.0.0",
|
||||||
|
"inflight": "^1.0.4",
|
||||||
|
"inherits": "2",
|
||||||
|
"minimatch": "^3.0.4",
|
||||||
|
"once": "^1.3.0",
|
||||||
|
"path-is-absolute": "^1.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "*"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/isaacs"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/inflight": {
|
||||||
|
"version": "1.0.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
|
||||||
|
"integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"once": "^1.3.0",
|
||||||
|
"wrappy": "1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/inherits": {
|
||||||
|
"version": "2.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
|
||||||
|
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
|
||||||
|
},
|
||||||
|
"node_modules/isarray": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
|
||||||
|
"integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
|
||||||
|
},
|
||||||
|
"node_modules/lunr": {
|
||||||
|
"version": "2.3.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz",
|
||||||
|
"integrity": "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow=="
|
||||||
|
},
|
||||||
|
"node_modules/minimatch": {
|
||||||
|
"version": "3.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
|
||||||
|
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"brace-expansion": "^1.1.7"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/msgpack5": {
|
||||||
|
"version": "4.5.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/msgpack5/-/msgpack5-4.5.1.tgz",
|
||||||
|
"integrity": "sha512-zC1vkcliryc4JGlL6OfpHumSYUHWFGimSI+OgfRCjTFLmKA2/foR9rMTOhWiqfOrfxJOctrpWPvrppf8XynJxw==",
|
||||||
|
"dependencies": {
|
||||||
|
"bl": "^2.0.1",
|
||||||
|
"inherits": "^2.0.3",
|
||||||
|
"readable-stream": "^2.3.6",
|
||||||
|
"safe-buffer": "^5.1.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/node-fetch": {
|
||||||
|
"version": "2.6.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz",
|
||||||
|
"integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"whatwg-url": "^5.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "4.x || >=6.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"encoding": "^0.1.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"encoding": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/node-forge": {
|
||||||
|
"version": "1.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.2.1.tgz",
|
||||||
|
"integrity": "sha512-Fcvtbb+zBcZXbTTVwqGA5W+MKBj56UjVRevvchv5XrcyXbmNdesfZL37nlcWOfpgHhgmxApw3tQbTr4CqNmX4w==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 6.13.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/once": {
|
||||||
|
"version": "1.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
||||||
|
"integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"wrappy": "1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/original": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/original/-/original-1.0.2.tgz",
|
||||||
|
"integrity": "sha512-hyBVl6iqqUOJ8FqRe+l/gS8H+kKYjrEndd5Pm1MfBtsEKA038HkkdbAl/72EAXGyonD/PFsvmVG+EvcIpliMBg==",
|
||||||
|
"dependencies": {
|
||||||
|
"url-parse": "^1.4.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/papaparse": {
|
||||||
|
"version": "5.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/papaparse/-/papaparse-5.3.1.tgz",
|
||||||
|
"integrity": "sha512-Dbt2yjLJrCwH2sRqKFFJaN5XgIASO9YOFeFP8rIBRG2Ain8mqk5r1M6DkfvqEVozVcz3r3HaUGw253hA1nLIcA=="
|
||||||
|
},
|
||||||
|
"node_modules/path-is-absolute": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
|
||||||
|
"integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
|
||||||
|
"dev": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/process-nextick-args": {
|
||||||
|
"version": "2.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
|
||||||
|
"integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="
|
||||||
|
},
|
||||||
|
"node_modules/psl": {
|
||||||
|
"version": "1.8.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz",
|
||||||
|
"integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ=="
|
||||||
|
},
|
||||||
|
"node_modules/punycode": {
|
||||||
|
"version": "1.4.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz",
|
||||||
|
"integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4="
|
||||||
|
},
|
||||||
|
"node_modules/querystringify": {
|
||||||
|
"version": "2.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz",
|
||||||
|
"integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ=="
|
||||||
|
},
|
||||||
|
"node_modules/readable-stream": {
|
||||||
|
"version": "2.3.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
|
||||||
|
"integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
|
||||||
|
"dependencies": {
|
||||||
|
"core-util-is": "~1.0.0",
|
||||||
|
"inherits": "~2.0.3",
|
||||||
|
"isarray": "~1.0.0",
|
||||||
|
"process-nextick-args": "~2.0.0",
|
||||||
|
"safe-buffer": "~5.1.1",
|
||||||
|
"string_decoder": "~1.1.1",
|
||||||
|
"util-deprecate": "~1.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/readable-stream/node_modules/safe-buffer": {
|
||||||
|
"version": "5.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
|
||||||
|
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
|
||||||
|
},
|
||||||
|
"node_modules/requires-port": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
|
||||||
|
"integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8="
|
||||||
|
},
|
||||||
|
"node_modules/rimraf": {
|
||||||
|
"version": "3.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
|
||||||
|
"integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"glob": "^7.1.3"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"rimraf": "bin.js"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/isaacs"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/rxjs": {
|
||||||
|
"version": "7.5.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.5.5.tgz",
|
||||||
|
"integrity": "sha512-sy+H0pQofO95VDmFLzyaw9xNJU4KTRSwQIGM6+iG3SypAtCiLDzpeG8sJrNCWn2Up9km+KhkvTdbkrdy+yzZdw==",
|
||||||
|
"dependencies": {
|
||||||
|
"tslib": "^2.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/safe-buffer": {
|
||||||
|
"version": "5.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
|
||||||
|
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/feross"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "patreon",
|
||||||
|
"url": "https://www.patreon.com/feross"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "consulting",
|
||||||
|
"url": "https://feross.org/support"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"node_modules/string_decoder": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
|
||||||
|
"dependencies": {
|
||||||
|
"safe-buffer": "~5.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/string_decoder/node_modules/safe-buffer": {
|
||||||
|
"version": "5.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
|
||||||
|
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
|
||||||
|
},
|
||||||
|
"node_modules/tldjs": {
|
||||||
|
"version": "2.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/tldjs/-/tldjs-2.3.1.tgz",
|
||||||
|
"integrity": "sha512-W/YVH/QczLUxVjnQhFC61Iq232NWu3TqDdO0S/MtXVz4xybejBov4ud+CIwN9aYqjOecEqIy0PscGkwpG9ZyTw==",
|
||||||
|
"hasInstallScript": true,
|
||||||
|
"dependencies": {
|
||||||
|
"punycode": "^1.4.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/tough-cookie": {
|
||||||
|
"version": "2.5.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz",
|
||||||
|
"integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==",
|
||||||
|
"dependencies": {
|
||||||
|
"psl": "^1.1.28",
|
||||||
|
"punycode": "^2.1.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/tough-cookie/node_modules/punycode": {
|
||||||
|
"version": "2.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
|
||||||
|
"integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/tr46": {
|
||||||
|
"version": "0.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
|
||||||
|
"integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o="
|
||||||
|
},
|
||||||
|
"node_modules/tslib": {
|
||||||
|
"version": "2.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz",
|
||||||
|
"integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw=="
|
||||||
|
},
|
||||||
|
"node_modules/typescript": {
|
||||||
|
"version": "4.3.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.3.5.tgz",
|
||||||
|
"integrity": "sha512-DqQgihaQ9cUrskJo9kIyW/+g0Vxsk8cDtZ52a3NGh0YNTfpUSArXSohyUGnvbPazEPLu398C0UxmKSOrPumUzA==",
|
||||||
|
"dev": true,
|
||||||
|
"bin": {
|
||||||
|
"tsc": "bin/tsc",
|
||||||
|
"tsserver": "bin/tsserver"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4.2.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/url-parse": {
|
||||||
|
"version": "1.5.10",
|
||||||
|
"resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz",
|
||||||
|
"integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"querystringify": "^2.1.1",
|
||||||
|
"requires-port": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/util-deprecate": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
||||||
|
"integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
|
||||||
|
},
|
||||||
|
"node_modules/webidl-conversions": {
|
||||||
|
"version": "3.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
|
||||||
|
"integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE="
|
||||||
|
},
|
||||||
|
"node_modules/whatwg-url": {
|
||||||
|
"version": "5.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
|
||||||
|
"integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=",
|
||||||
|
"dependencies": {
|
||||||
|
"tr46": "~0.0.3",
|
||||||
|
"webidl-conversions": "^3.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/wrappy": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
|
||||||
|
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"node_modules/ws": {
|
||||||
|
"version": "6.2.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/ws/-/ws-6.2.2.tgz",
|
||||||
|
"integrity": "sha512-zmhltoSR8u1cnDsD43TX59mzoMZsLKqUweyYBAIvTngR3shc0W6aOZylZmq/7hqyVxPdi+5Ud2QInblgyE72fw==",
|
||||||
|
"dependencies": {
|
||||||
|
"async-limiter": "~1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/zxcvbn": {
|
||||||
|
"version": "4.4.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/zxcvbn/-/zxcvbn-4.4.2.tgz",
|
||||||
|
"integrity": "sha1-KOwXzwl0PtyrBW3dixsGJizHPDA="
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@microsoft/signalr": {
|
||||||
|
"version": "5.0.10",
|
||||||
|
"resolved": "https://registry.npmjs.org/@microsoft/signalr/-/signalr-5.0.10.tgz",
|
||||||
|
"integrity": "sha512-7jg6s/cmULyeVvt5/bTB4N9T30HvAF1S06hL+nPcQMODXcclRo34Zcli/dfTLR8lCX31/cVEOmVgxXBOVRQ+Dw==",
|
||||||
|
"requires": {
|
||||||
|
"abort-controller": "^3.0.0",
|
||||||
|
"eventsource": "^1.0.7",
|
||||||
|
"fetch-cookie": "^0.7.3",
|
||||||
|
"node-fetch": "^2.6.0",
|
||||||
|
"ws": "^6.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@microsoft/signalr-protocol-msgpack": {
|
||||||
|
"version": "5.0.10",
|
||||||
|
"resolved": "https://registry.npmjs.org/@microsoft/signalr-protocol-msgpack/-/signalr-protocol-msgpack-5.0.10.tgz",
|
||||||
|
"integrity": "sha512-HqZiNLyjYP1ONeLgYUjFBUsnhxSp5CW4AW8InsLI7lyAXZl2drUhkiBxf3xK9UsTErO1+9r5sdaYdSmUY8nx9A==",
|
||||||
|
"requires": {
|
||||||
|
"@microsoft/signalr": ">=5.0.10",
|
||||||
|
"msgpack5": "^4.5.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@types/lunr": {
|
||||||
|
"version": "2.3.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/lunr/-/lunr-2.3.4.tgz",
|
||||||
|
"integrity": "sha512-j4x4XJwZvorEUbA519VdQ5b9AOU9TSvfi8tvxMAfP8XzNLtFex7A8vFQwqOx3WACbV0KMXbACV3cZl4/gynQ7g==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"@types/node": {
|
||||||
|
"version": "16.11.26",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.26.tgz",
|
||||||
|
"integrity": "sha512-GZ7bu5A6+4DtG7q9GsoHXy3ALcgeIHP4NnL0Vv2wu0uUB/yQex26v0tf6/na1mm0+bS9Uw+0DFex7aaKr2qawQ==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"@types/node-forge": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/node-forge/-/node-forge-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-96ELNKv9tQJ19afdBUiM5iDw7OYEc53iUc51gAPR2aGaqRsO1DBROjqgZRjZa1tkPj7TnEOR0EnyAX6iryGkzA==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"@types/node": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@types/papaparse": {
|
||||||
|
"version": "5.3.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/papaparse/-/papaparse-5.3.2.tgz",
|
||||||
|
"integrity": "sha512-BNbCHJkTE4RwmAFkCxEalET4mDvGr/1ld7ZtQ4i/laWI/iiVt+GL07stdvufle4KfywyvloqqpIiJscXNCrKxA==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"@types/node": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@types/tldjs": {
|
||||||
|
"version": "2.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/tldjs/-/tldjs-2.3.1.tgz",
|
||||||
|
"integrity": "sha512-BQR04zLE0ve2eNrqxXw/Qp/f6LxvNrj/4A8ZgdQi3SzbBqxFhleI7N4DS/mSjDnODrUaEGgoWg4grAZR1kVj8w==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"@types/zxcvbn": {
|
||||||
|
"version": "4.4.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/zxcvbn/-/zxcvbn-4.4.1.tgz",
|
||||||
|
"integrity": "sha512-3NoqvZC2W5gAC5DZbTpCeJ251vGQmgcWIHQJGq2J240HY6ErQ9aWKkwfoKJlHLx+A83WPNTZ9+3cd2ILxbvr1w==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"abort-controller": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==",
|
||||||
|
"requires": {
|
||||||
|
"event-target-shim": "^5.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"async-limiter": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ=="
|
||||||
|
},
|
||||||
|
"balanced-match": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
||||||
|
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"big-integer": {
|
||||||
|
"version": "1.6.48",
|
||||||
|
"resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.48.tgz",
|
||||||
|
"integrity": "sha512-j51egjPa7/i+RdiRuJbPdJ2FIUYYPhvYLjzoYbcMMm62ooO6F94fETG4MTs46zPAF9Brs04OajboA/qTGuz78w=="
|
||||||
|
},
|
||||||
|
"bl": {
|
||||||
|
"version": "2.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/bl/-/bl-2.2.1.tgz",
|
||||||
|
"integrity": "sha512-6Pesp1w0DEX1N550i/uGV/TqucVL4AM/pgThFSN/Qq9si1/DF9aIHs1BxD8V/QU0HoeHO6cQRTAuYnLPKq1e4g==",
|
||||||
|
"requires": {
|
||||||
|
"readable-stream": "^2.3.5",
|
||||||
|
"safe-buffer": "^5.1.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"brace-expansion": {
|
||||||
|
"version": "1.1.11",
|
||||||
|
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
|
||||||
|
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"balanced-match": "^1.0.0",
|
||||||
|
"concat-map": "0.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"browser-hrtime": {
|
||||||
|
"version": "1.1.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/browser-hrtime/-/browser-hrtime-1.1.8.tgz",
|
||||||
|
"integrity": "sha512-kzXheikaJsBtzUBlyVtPIY5r0soQePzjwVwT4IlDpU2RvfB5Py52gpU98M77rgqMCheoSSZvrcrdj3t6cZ3suA=="
|
||||||
|
},
|
||||||
|
"concat-map": {
|
||||||
|
"version": "0.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
|
||||||
|
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"core-util-is": {
|
||||||
|
"version": "1.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz",
|
||||||
|
"integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ=="
|
||||||
|
},
|
||||||
|
"es6-denodeify": {
|
||||||
|
"version": "0.1.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/es6-denodeify/-/es6-denodeify-0.1.5.tgz",
|
||||||
|
"integrity": "sha1-MdTV/pxVA+ElRgQ5MQ4WoqPznB8="
|
||||||
|
},
|
||||||
|
"event-target-shim": {
|
||||||
|
"version": "5.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz",
|
||||||
|
"integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ=="
|
||||||
|
},
|
||||||
|
"eventsource": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/eventsource/-/eventsource-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-VSJjT5oCNrFvCS6igjzPAt5hBzQ2qPBFIbJ03zLI9SE0mxwZpMw6BfJrbFHm1a141AavMEB8JHmBhWAd66PfCg==",
|
||||||
|
"requires": {
|
||||||
|
"original": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"fetch-cookie": {
|
||||||
|
"version": "0.7.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/fetch-cookie/-/fetch-cookie-0.7.3.tgz",
|
||||||
|
"integrity": "sha512-rZPkLnI8x5V+zYAiz8QonAHsTb4BY+iFowFBI1RFn0zrO343AVp9X7/yUj/9wL6Ef/8fLls8b/vGtzUvmyAUGA==",
|
||||||
|
"requires": {
|
||||||
|
"es6-denodeify": "^0.1.1",
|
||||||
|
"tough-cookie": "^2.3.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"fs.realpath": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
|
||||||
|
"integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"glob": {
|
||||||
|
"version": "7.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz",
|
||||||
|
"integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"fs.realpath": "^1.0.0",
|
||||||
|
"inflight": "^1.0.4",
|
||||||
|
"inherits": "2",
|
||||||
|
"minimatch": "^3.0.4",
|
||||||
|
"once": "^1.3.0",
|
||||||
|
"path-is-absolute": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"inflight": {
|
||||||
|
"version": "1.0.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
|
||||||
|
"integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"once": "^1.3.0",
|
||||||
|
"wrappy": "1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"inherits": {
|
||||||
|
"version": "2.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
|
||||||
|
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
|
||||||
|
},
|
||||||
|
"isarray": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
|
||||||
|
"integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
|
||||||
|
},
|
||||||
|
"lunr": {
|
||||||
|
"version": "2.3.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz",
|
||||||
|
"integrity": "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow=="
|
||||||
|
},
|
||||||
|
"minimatch": {
|
||||||
|
"version": "3.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
|
||||||
|
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"brace-expansion": "^1.1.7"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"msgpack5": {
|
||||||
|
"version": "4.5.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/msgpack5/-/msgpack5-4.5.1.tgz",
|
||||||
|
"integrity": "sha512-zC1vkcliryc4JGlL6OfpHumSYUHWFGimSI+OgfRCjTFLmKA2/foR9rMTOhWiqfOrfxJOctrpWPvrppf8XynJxw==",
|
||||||
|
"requires": {
|
||||||
|
"bl": "^2.0.1",
|
||||||
|
"inherits": "^2.0.3",
|
||||||
|
"readable-stream": "^2.3.6",
|
||||||
|
"safe-buffer": "^5.1.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node-fetch": {
|
||||||
|
"version": "2.6.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz",
|
||||||
|
"integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==",
|
||||||
|
"requires": {
|
||||||
|
"whatwg-url": "^5.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node-forge": {
|
||||||
|
"version": "1.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.2.1.tgz",
|
||||||
|
"integrity": "sha512-Fcvtbb+zBcZXbTTVwqGA5W+MKBj56UjVRevvchv5XrcyXbmNdesfZL37nlcWOfpgHhgmxApw3tQbTr4CqNmX4w=="
|
||||||
|
},
|
||||||
|
"once": {
|
||||||
|
"version": "1.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
||||||
|
"integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"wrappy": "1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/original/-/original-1.0.2.tgz",
|
||||||
|
"integrity": "sha512-hyBVl6iqqUOJ8FqRe+l/gS8H+kKYjrEndd5Pm1MfBtsEKA038HkkdbAl/72EAXGyonD/PFsvmVG+EvcIpliMBg==",
|
||||||
|
"requires": {
|
||||||
|
"url-parse": "^1.4.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"papaparse": {
|
||||||
|
"version": "5.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/papaparse/-/papaparse-5.3.1.tgz",
|
||||||
|
"integrity": "sha512-Dbt2yjLJrCwH2sRqKFFJaN5XgIASO9YOFeFP8rIBRG2Ain8mqk5r1M6DkfvqEVozVcz3r3HaUGw253hA1nLIcA=="
|
||||||
|
},
|
||||||
|
"path-is-absolute": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
|
||||||
|
"integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"process-nextick-args": {
|
||||||
|
"version": "2.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
|
||||||
|
"integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="
|
||||||
|
},
|
||||||
|
"psl": {
|
||||||
|
"version": "1.8.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz",
|
||||||
|
"integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ=="
|
||||||
|
},
|
||||||
|
"punycode": {
|
||||||
|
"version": "1.4.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz",
|
||||||
|
"integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4="
|
||||||
|
},
|
||||||
|
"querystringify": {
|
||||||
|
"version": "2.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz",
|
||||||
|
"integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ=="
|
||||||
|
},
|
||||||
|
"readable-stream": {
|
||||||
|
"version": "2.3.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
|
||||||
|
"integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
|
||||||
|
"requires": {
|
||||||
|
"core-util-is": "~1.0.0",
|
||||||
|
"inherits": "~2.0.3",
|
||||||
|
"isarray": "~1.0.0",
|
||||||
|
"process-nextick-args": "~2.0.0",
|
||||||
|
"safe-buffer": "~5.1.1",
|
||||||
|
"string_decoder": "~1.1.1",
|
||||||
|
"util-deprecate": "~1.0.1"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"safe-buffer": {
|
||||||
|
"version": "5.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
|
||||||
|
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"requires-port": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
|
||||||
|
"integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8="
|
||||||
|
},
|
||||||
|
"rimraf": {
|
||||||
|
"version": "3.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
|
||||||
|
"integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"glob": "^7.1.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"rxjs": {
|
||||||
|
"version": "7.5.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.5.5.tgz",
|
||||||
|
"integrity": "sha512-sy+H0pQofO95VDmFLzyaw9xNJU4KTRSwQIGM6+iG3SypAtCiLDzpeG8sJrNCWn2Up9km+KhkvTdbkrdy+yzZdw==",
|
||||||
|
"requires": {
|
||||||
|
"tslib": "^2.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"safe-buffer": {
|
||||||
|
"version": "5.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
|
||||||
|
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="
|
||||||
|
},
|
||||||
|
"string_decoder": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
|
||||||
|
"requires": {
|
||||||
|
"safe-buffer": "~5.1.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"safe-buffer": {
|
||||||
|
"version": "5.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
|
||||||
|
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"tldjs": {
|
||||||
|
"version": "2.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/tldjs/-/tldjs-2.3.1.tgz",
|
||||||
|
"integrity": "sha512-W/YVH/QczLUxVjnQhFC61Iq232NWu3TqDdO0S/MtXVz4xybejBov4ud+CIwN9aYqjOecEqIy0PscGkwpG9ZyTw==",
|
||||||
|
"requires": {
|
||||||
|
"punycode": "^1.4.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"tough-cookie": {
|
||||||
|
"version": "2.5.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz",
|
||||||
|
"integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==",
|
||||||
|
"requires": {
|
||||||
|
"psl": "^1.1.28",
|
||||||
|
"punycode": "^2.1.1"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"punycode": {
|
||||||
|
"version": "2.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
|
||||||
|
"integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A=="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"tr46": {
|
||||||
|
"version": "0.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
|
||||||
|
"integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o="
|
||||||
|
},
|
||||||
|
"tslib": {
|
||||||
|
"version": "2.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz",
|
||||||
|
"integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw=="
|
||||||
|
},
|
||||||
|
"typescript": {
|
||||||
|
"version": "4.3.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.3.5.tgz",
|
||||||
|
"integrity": "sha512-DqQgihaQ9cUrskJo9kIyW/+g0Vxsk8cDtZ52a3NGh0YNTfpUSArXSohyUGnvbPazEPLu398C0UxmKSOrPumUzA==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"url-parse": {
|
||||||
|
"version": "1.5.10",
|
||||||
|
"resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz",
|
||||||
|
"integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==",
|
||||||
|
"requires": {
|
||||||
|
"querystringify": "^2.1.1",
|
||||||
|
"requires-port": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"util-deprecate": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
||||||
|
"integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
|
||||||
|
},
|
||||||
|
"webidl-conversions": {
|
||||||
|
"version": "3.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
|
||||||
|
"integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE="
|
||||||
|
},
|
||||||
|
"whatwg-url": {
|
||||||
|
"version": "5.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
|
||||||
|
"integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=",
|
||||||
|
"requires": {
|
||||||
|
"tr46": "~0.0.3",
|
||||||
|
"webidl-conversions": "^3.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"wrappy": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
|
||||||
|
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"ws": {
|
||||||
|
"version": "6.2.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/ws/-/ws-6.2.2.tgz",
|
||||||
|
"integrity": "sha512-zmhltoSR8u1cnDsD43TX59mzoMZsLKqUweyYBAIvTngR3shc0W6aOZylZmq/7hqyVxPdi+5Ud2QInblgyE72fw==",
|
||||||
|
"requires": {
|
||||||
|
"async-limiter": "~1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"zxcvbn": {
|
||||||
|
"version": "4.4.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/zxcvbn/-/zxcvbn-4.4.2.tgz",
|
||||||
|
"integrity": "sha1-KOwXzwl0PtyrBW3dixsGJizHPDA="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
42
jslib/common/package.json
Normal file
42
jslib/common/package.json
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
{
|
||||||
|
"name": "@bitwarden/jslib-common",
|
||||||
|
"version": "0.0.0",
|
||||||
|
"description": "Common code used across Bitwarden JavaScript projects.",
|
||||||
|
"keywords": [
|
||||||
|
"bitwarden"
|
||||||
|
],
|
||||||
|
"author": "Bitwarden Inc.",
|
||||||
|
"homepage": "https://bitwarden.com",
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/bitwarden/jslib"
|
||||||
|
},
|
||||||
|
"license": "GPL-3.0",
|
||||||
|
"scripts": {
|
||||||
|
"clean": "rimraf dist/**/*",
|
||||||
|
"build": "npm run clean && tsc",
|
||||||
|
"build:watch": "npm run clean && tsc -watch"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/lunr": "^2.3.3",
|
||||||
|
"@types/node": "^16.11.12",
|
||||||
|
"@types/node-forge": "^1.0.1",
|
||||||
|
"@types/papaparse": "^5.2.5",
|
||||||
|
"@types/tldjs": "^2.3.0",
|
||||||
|
"@types/zxcvbn": "^4.4.1",
|
||||||
|
"rimraf": "^3.0.2",
|
||||||
|
"typescript": "4.3.5"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@microsoft/signalr": "5.0.10",
|
||||||
|
"@microsoft/signalr-protocol-msgpack": "5.0.10",
|
||||||
|
"big-integer": "1.6.48",
|
||||||
|
"browser-hrtime": "^1.1.8",
|
||||||
|
"lunr": "^2.3.9",
|
||||||
|
"node-forge": "^1.2.1",
|
||||||
|
"papaparse": "^5.3.0",
|
||||||
|
"rxjs": "^7.4.0",
|
||||||
|
"tldjs": "^2.3.1",
|
||||||
|
"zxcvbn": "^4.4.2"
|
||||||
|
}
|
||||||
|
}
|
||||||
83
jslib/common/spec/domain/attachment.spec.ts
Normal file
83
jslib/common/spec/domain/attachment.spec.ts
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
import Substitute, { Arg } from "@fluffy-spoon/substitute";
|
||||||
|
|
||||||
|
import { CryptoService } from "jslib-common/abstractions/crypto.service";
|
||||||
|
import { AttachmentData } from "jslib-common/models/data/attachmentData";
|
||||||
|
import { Attachment } from "jslib-common/models/domain/attachment";
|
||||||
|
import { SymmetricCryptoKey } from "jslib-common/models/domain/symmetricCryptoKey";
|
||||||
|
import { ContainerService } from "jslib-common/services/container.service";
|
||||||
|
|
||||||
|
import { makeStaticByteArray, mockEnc } from "../utils";
|
||||||
|
|
||||||
|
describe("Attachment", () => {
|
||||||
|
let data: AttachmentData;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
data = {
|
||||||
|
id: "id",
|
||||||
|
url: "url",
|
||||||
|
fileName: "fileName",
|
||||||
|
key: "key",
|
||||||
|
size: "1100",
|
||||||
|
sizeName: "1.1 KB",
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Convert from empty", () => {
|
||||||
|
const data = new AttachmentData();
|
||||||
|
const attachment = new Attachment(data);
|
||||||
|
|
||||||
|
expect(attachment).toEqual({
|
||||||
|
id: null,
|
||||||
|
url: null,
|
||||||
|
size: undefined,
|
||||||
|
sizeName: null,
|
||||||
|
key: null,
|
||||||
|
fileName: null,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Convert", () => {
|
||||||
|
const attachment = new Attachment(data);
|
||||||
|
|
||||||
|
expect(attachment).toEqual({
|
||||||
|
size: "1100",
|
||||||
|
id: "id",
|
||||||
|
url: "url",
|
||||||
|
sizeName: "1.1 KB",
|
||||||
|
fileName: { encryptedString: "fileName", encryptionType: 0 },
|
||||||
|
key: { encryptedString: "key", encryptionType: 0 },
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("toAttachmentData", () => {
|
||||||
|
const attachment = new Attachment(data);
|
||||||
|
expect(attachment.toAttachmentData()).toEqual(data);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Decrypt", async () => {
|
||||||
|
const attachment = new Attachment();
|
||||||
|
attachment.id = "id";
|
||||||
|
attachment.url = "url";
|
||||||
|
attachment.size = "1100";
|
||||||
|
attachment.sizeName = "1.1 KB";
|
||||||
|
attachment.key = mockEnc("key");
|
||||||
|
attachment.fileName = mockEnc("fileName");
|
||||||
|
|
||||||
|
const cryptoService = Substitute.for<CryptoService>();
|
||||||
|
cryptoService.getOrgKey(null).resolves(null);
|
||||||
|
cryptoService.decryptToBytes(Arg.any(), Arg.any()).resolves(makeStaticByteArray(32));
|
||||||
|
|
||||||
|
(window as any).bitwardenContainerService = new ContainerService(cryptoService);
|
||||||
|
|
||||||
|
const view = await attachment.decrypt(null);
|
||||||
|
|
||||||
|
expect(view).toEqual({
|
||||||
|
id: "id",
|
||||||
|
url: "url",
|
||||||
|
size: "1100",
|
||||||
|
sizeName: "1.1 KB",
|
||||||
|
fileName: "fileName",
|
||||||
|
key: expect.any(SymmetricCryptoKey),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
73
jslib/common/spec/domain/card.spec.ts
Normal file
73
jslib/common/spec/domain/card.spec.ts
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
import { CardData } from "jslib-common/models/data/cardData";
|
||||||
|
import { Card } from "jslib-common/models/domain/card";
|
||||||
|
|
||||||
|
import { mockEnc } from "../utils";
|
||||||
|
|
||||||
|
describe("Card", () => {
|
||||||
|
let data: CardData;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
data = {
|
||||||
|
cardholderName: "encHolder",
|
||||||
|
brand: "encBrand",
|
||||||
|
number: "encNumber",
|
||||||
|
expMonth: "encMonth",
|
||||||
|
expYear: "encYear",
|
||||||
|
code: "encCode",
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Convert from empty", () => {
|
||||||
|
const data = new CardData();
|
||||||
|
const card = new Card(data);
|
||||||
|
|
||||||
|
expect(card).toEqual({
|
||||||
|
cardholderName: null,
|
||||||
|
brand: null,
|
||||||
|
number: null,
|
||||||
|
expMonth: null,
|
||||||
|
expYear: null,
|
||||||
|
code: null,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Convert", () => {
|
||||||
|
const card = new Card(data);
|
||||||
|
|
||||||
|
expect(card).toEqual({
|
||||||
|
cardholderName: { encryptedString: "encHolder", encryptionType: 0 },
|
||||||
|
brand: { encryptedString: "encBrand", encryptionType: 0 },
|
||||||
|
number: { encryptedString: "encNumber", encryptionType: 0 },
|
||||||
|
expMonth: { encryptedString: "encMonth", encryptionType: 0 },
|
||||||
|
expYear: { encryptedString: "encYear", encryptionType: 0 },
|
||||||
|
code: { encryptedString: "encCode", encryptionType: 0 },
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("toCardData", () => {
|
||||||
|
const card = new Card(data);
|
||||||
|
expect(card.toCardData()).toEqual(data);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Decrypt", async () => {
|
||||||
|
const card = new Card();
|
||||||
|
card.cardholderName = mockEnc("cardHolder");
|
||||||
|
card.brand = mockEnc("brand");
|
||||||
|
card.number = mockEnc("number");
|
||||||
|
card.expMonth = mockEnc("expMonth");
|
||||||
|
card.expYear = mockEnc("expYear");
|
||||||
|
card.code = mockEnc("code");
|
||||||
|
|
||||||
|
const view = await card.decrypt(null);
|
||||||
|
|
||||||
|
expect(view).toEqual({
|
||||||
|
_brand: "brand",
|
||||||
|
_number: "number",
|
||||||
|
_subTitle: null,
|
||||||
|
cardholderName: "cardHolder",
|
||||||
|
code: "code",
|
||||||
|
expMonth: "expMonth",
|
||||||
|
expYear: "expYear",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
599
jslib/common/spec/domain/cipher.spec.ts
Normal file
599
jslib/common/spec/domain/cipher.spec.ts
Normal file
@@ -0,0 +1,599 @@
|
|||||||
|
import Substitute, { Arg } from "@fluffy-spoon/substitute";
|
||||||
|
|
||||||
|
import { CipherRepromptType } from "jslib-common/enums/cipherRepromptType";
|
||||||
|
import { CipherType } from "jslib-common/enums/cipherType";
|
||||||
|
import { FieldType } from "jslib-common/enums/fieldType";
|
||||||
|
import { SecureNoteType } from "jslib-common/enums/secureNoteType";
|
||||||
|
import { UriMatchType } from "jslib-common/enums/uriMatchType";
|
||||||
|
import { CipherData } from "jslib-common/models/data/cipherData";
|
||||||
|
import { Card } from "jslib-common/models/domain/card";
|
||||||
|
import { Cipher } from "jslib-common/models/domain/cipher";
|
||||||
|
import { Identity } from "jslib-common/models/domain/identity";
|
||||||
|
import { Login } from "jslib-common/models/domain/login";
|
||||||
|
import { SecureNote } from "jslib-common/models/domain/secureNote";
|
||||||
|
import { CardView } from "jslib-common/models/view/cardView";
|
||||||
|
import { IdentityView } from "jslib-common/models/view/identityView";
|
||||||
|
import { LoginView } from "jslib-common/models/view/loginView";
|
||||||
|
|
||||||
|
import { mockEnc } from "../utils";
|
||||||
|
|
||||||
|
describe("Cipher DTO", () => {
|
||||||
|
it("Convert from empty CipherData", () => {
|
||||||
|
const data = new CipherData();
|
||||||
|
const cipher = new Cipher(data);
|
||||||
|
|
||||||
|
expect(cipher).toEqual({
|
||||||
|
id: null,
|
||||||
|
userId: null,
|
||||||
|
organizationId: null,
|
||||||
|
folderId: null,
|
||||||
|
name: null,
|
||||||
|
notes: null,
|
||||||
|
type: undefined,
|
||||||
|
favorite: undefined,
|
||||||
|
organizationUseTotp: undefined,
|
||||||
|
edit: undefined,
|
||||||
|
viewPassword: true,
|
||||||
|
revisionDate: null,
|
||||||
|
collectionIds: undefined,
|
||||||
|
localData: null,
|
||||||
|
deletedDate: null,
|
||||||
|
reprompt: undefined,
|
||||||
|
attachments: null,
|
||||||
|
fields: null,
|
||||||
|
passwordHistory: null,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("LoginCipher", () => {
|
||||||
|
let cipherData: CipherData;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
cipherData = {
|
||||||
|
id: "id",
|
||||||
|
organizationId: "orgId",
|
||||||
|
folderId: "folderId",
|
||||||
|
userId: "userId",
|
||||||
|
edit: true,
|
||||||
|
viewPassword: true,
|
||||||
|
organizationUseTotp: true,
|
||||||
|
favorite: false,
|
||||||
|
revisionDate: "2022-01-31T12:00:00.000Z",
|
||||||
|
type: CipherType.Login,
|
||||||
|
name: "EncryptedString",
|
||||||
|
notes: "EncryptedString",
|
||||||
|
deletedDate: null,
|
||||||
|
reprompt: CipherRepromptType.None,
|
||||||
|
login: {
|
||||||
|
uris: [{ uri: "EncryptedString", match: UriMatchType.Domain }],
|
||||||
|
username: "EncryptedString",
|
||||||
|
password: "EncryptedString",
|
||||||
|
passwordRevisionDate: "2022-01-31T12:00:00.000Z",
|
||||||
|
totp: "EncryptedString",
|
||||||
|
autofillOnPageLoad: false,
|
||||||
|
},
|
||||||
|
passwordHistory: [
|
||||||
|
{ password: "EncryptedString", lastUsedDate: "2022-01-31T12:00:00.000Z" },
|
||||||
|
],
|
||||||
|
attachments: [
|
||||||
|
{
|
||||||
|
id: "a1",
|
||||||
|
url: "url",
|
||||||
|
size: "1100",
|
||||||
|
sizeName: "1.1 KB",
|
||||||
|
fileName: "file",
|
||||||
|
key: "EncKey",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "a2",
|
||||||
|
url: "url",
|
||||||
|
size: "1100",
|
||||||
|
sizeName: "1.1 KB",
|
||||||
|
fileName: "file",
|
||||||
|
key: "EncKey",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
name: "EncryptedString",
|
||||||
|
value: "EncryptedString",
|
||||||
|
type: FieldType.Text,
|
||||||
|
linkedId: null,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "EncryptedString",
|
||||||
|
value: "EncryptedString",
|
||||||
|
type: FieldType.Hidden,
|
||||||
|
linkedId: null,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Convert", () => {
|
||||||
|
const cipher = new Cipher(cipherData);
|
||||||
|
|
||||||
|
expect(cipher).toEqual({
|
||||||
|
id: "id",
|
||||||
|
userId: "userId",
|
||||||
|
organizationId: "orgId",
|
||||||
|
folderId: "folderId",
|
||||||
|
name: { encryptedString: "EncryptedString", encryptionType: 0 },
|
||||||
|
notes: { encryptedString: "EncryptedString", encryptionType: 0 },
|
||||||
|
type: 1,
|
||||||
|
favorite: false,
|
||||||
|
organizationUseTotp: true,
|
||||||
|
edit: true,
|
||||||
|
viewPassword: true,
|
||||||
|
revisionDate: new Date("2022-01-31T12:00:00.000Z"),
|
||||||
|
collectionIds: undefined,
|
||||||
|
localData: null,
|
||||||
|
deletedDate: null,
|
||||||
|
reprompt: 0,
|
||||||
|
login: {
|
||||||
|
passwordRevisionDate: new Date("2022-01-31T12:00:00.000Z"),
|
||||||
|
autofillOnPageLoad: false,
|
||||||
|
username: { encryptedString: "EncryptedString", encryptionType: 0 },
|
||||||
|
password: { encryptedString: "EncryptedString", encryptionType: 0 },
|
||||||
|
totp: { encryptedString: "EncryptedString", encryptionType: 0 },
|
||||||
|
uris: [{ match: 0, uri: { encryptedString: "EncryptedString", encryptionType: 0 } }],
|
||||||
|
},
|
||||||
|
attachments: [
|
||||||
|
{
|
||||||
|
fileName: { encryptedString: "file", encryptionType: 0 },
|
||||||
|
id: "a1",
|
||||||
|
key: { encryptedString: "EncKey", encryptionType: 0 },
|
||||||
|
size: "1100",
|
||||||
|
sizeName: "1.1 KB",
|
||||||
|
url: "url",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fileName: { encryptedString: "file", encryptionType: 0 },
|
||||||
|
id: "a2",
|
||||||
|
key: { encryptedString: "EncKey", encryptionType: 0 },
|
||||||
|
size: "1100",
|
||||||
|
sizeName: "1.1 KB",
|
||||||
|
url: "url",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
linkedId: null,
|
||||||
|
name: { encryptedString: "EncryptedString", encryptionType: 0 },
|
||||||
|
type: 0,
|
||||||
|
value: { encryptedString: "EncryptedString", encryptionType: 0 },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
linkedId: null,
|
||||||
|
name: { encryptedString: "EncryptedString", encryptionType: 0 },
|
||||||
|
type: 1,
|
||||||
|
value: { encryptedString: "EncryptedString", encryptionType: 0 },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
passwordHistory: [
|
||||||
|
{
|
||||||
|
lastUsedDate: new Date("2022-01-31T12:00:00.000Z"),
|
||||||
|
password: { encryptedString: "EncryptedString", encryptionType: 0 },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("toCipherData", () => {
|
||||||
|
const cipher = new Cipher(cipherData);
|
||||||
|
expect(cipher.toCipherData("userId")).toEqual(cipherData);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Decrypt", async () => {
|
||||||
|
const cipher = new Cipher();
|
||||||
|
cipher.id = "id";
|
||||||
|
cipher.organizationId = "orgId";
|
||||||
|
cipher.folderId = "folderId";
|
||||||
|
cipher.edit = true;
|
||||||
|
cipher.viewPassword = true;
|
||||||
|
cipher.organizationUseTotp = true;
|
||||||
|
cipher.favorite = false;
|
||||||
|
cipher.revisionDate = new Date("2022-01-31T12:00:00.000Z");
|
||||||
|
cipher.type = CipherType.Login;
|
||||||
|
cipher.name = mockEnc("EncryptedString");
|
||||||
|
cipher.notes = mockEnc("EncryptedString");
|
||||||
|
cipher.deletedDate = null;
|
||||||
|
cipher.reprompt = CipherRepromptType.None;
|
||||||
|
|
||||||
|
const loginView = new LoginView();
|
||||||
|
loginView.username = "username";
|
||||||
|
loginView.password = "password";
|
||||||
|
|
||||||
|
const login = Substitute.for<Login>();
|
||||||
|
login.decrypt(Arg.any(), Arg.any()).resolves(loginView);
|
||||||
|
cipher.login = login;
|
||||||
|
|
||||||
|
const cipherView = await cipher.decrypt();
|
||||||
|
|
||||||
|
expect(cipherView).toMatchObject({
|
||||||
|
id: "id",
|
||||||
|
organizationId: "orgId",
|
||||||
|
folderId: "folderId",
|
||||||
|
name: "EncryptedString",
|
||||||
|
notes: "EncryptedString",
|
||||||
|
type: 1,
|
||||||
|
favorite: false,
|
||||||
|
organizationUseTotp: true,
|
||||||
|
edit: true,
|
||||||
|
viewPassword: true,
|
||||||
|
login: loginView,
|
||||||
|
attachments: null,
|
||||||
|
fields: null,
|
||||||
|
passwordHistory: null,
|
||||||
|
collectionIds: undefined,
|
||||||
|
revisionDate: new Date("2022-01-31T12:00:00.000Z"),
|
||||||
|
deletedDate: null,
|
||||||
|
reprompt: 0,
|
||||||
|
localData: undefined,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("SecureNoteCipher", () => {
|
||||||
|
let cipherData: CipherData;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
cipherData = {
|
||||||
|
id: "id",
|
||||||
|
organizationId: "orgId",
|
||||||
|
folderId: "folderId",
|
||||||
|
userId: "userId",
|
||||||
|
edit: true,
|
||||||
|
viewPassword: true,
|
||||||
|
organizationUseTotp: true,
|
||||||
|
favorite: false,
|
||||||
|
revisionDate: "2022-01-31T12:00:00.000Z",
|
||||||
|
type: CipherType.SecureNote,
|
||||||
|
name: "EncryptedString",
|
||||||
|
notes: "EncryptedString",
|
||||||
|
deletedDate: null,
|
||||||
|
reprompt: CipherRepromptType.None,
|
||||||
|
secureNote: {
|
||||||
|
type: SecureNoteType.Generic,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Convert", () => {
|
||||||
|
const cipher = new Cipher(cipherData);
|
||||||
|
|
||||||
|
expect(cipher).toEqual({
|
||||||
|
id: "id",
|
||||||
|
userId: "userId",
|
||||||
|
organizationId: "orgId",
|
||||||
|
folderId: "folderId",
|
||||||
|
name: { encryptedString: "EncryptedString", encryptionType: 0 },
|
||||||
|
notes: { encryptedString: "EncryptedString", encryptionType: 0 },
|
||||||
|
type: 2,
|
||||||
|
favorite: false,
|
||||||
|
organizationUseTotp: true,
|
||||||
|
edit: true,
|
||||||
|
viewPassword: true,
|
||||||
|
revisionDate: new Date("2022-01-31T12:00:00.000Z"),
|
||||||
|
collectionIds: undefined,
|
||||||
|
localData: null,
|
||||||
|
deletedDate: null,
|
||||||
|
reprompt: 0,
|
||||||
|
secureNote: { type: SecureNoteType.Generic },
|
||||||
|
attachments: null,
|
||||||
|
fields: null,
|
||||||
|
passwordHistory: null,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("toCipherData", () => {
|
||||||
|
const cipher = new Cipher(cipherData);
|
||||||
|
expect(cipher.toCipherData("userId")).toEqual(cipherData);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Decrypt", async () => {
|
||||||
|
const cipher = new Cipher();
|
||||||
|
cipher.id = "id";
|
||||||
|
cipher.organizationId = "orgId";
|
||||||
|
cipher.folderId = "folderId";
|
||||||
|
cipher.edit = true;
|
||||||
|
cipher.viewPassword = true;
|
||||||
|
cipher.organizationUseTotp = true;
|
||||||
|
cipher.favorite = false;
|
||||||
|
cipher.revisionDate = new Date("2022-01-31T12:00:00.000Z");
|
||||||
|
cipher.type = CipherType.SecureNote;
|
||||||
|
cipher.name = mockEnc("EncryptedString");
|
||||||
|
cipher.notes = mockEnc("EncryptedString");
|
||||||
|
cipher.deletedDate = null;
|
||||||
|
cipher.reprompt = CipherRepromptType.None;
|
||||||
|
cipher.secureNote = new SecureNote();
|
||||||
|
cipher.secureNote.type = SecureNoteType.Generic;
|
||||||
|
|
||||||
|
const cipherView = await cipher.decrypt();
|
||||||
|
|
||||||
|
expect(cipherView).toMatchObject({
|
||||||
|
id: "id",
|
||||||
|
organizationId: "orgId",
|
||||||
|
folderId: "folderId",
|
||||||
|
name: "EncryptedString",
|
||||||
|
notes: "EncryptedString",
|
||||||
|
type: 2,
|
||||||
|
favorite: false,
|
||||||
|
organizationUseTotp: true,
|
||||||
|
edit: true,
|
||||||
|
viewPassword: true,
|
||||||
|
secureNote: { type: 0 },
|
||||||
|
attachments: null,
|
||||||
|
fields: null,
|
||||||
|
passwordHistory: null,
|
||||||
|
collectionIds: undefined,
|
||||||
|
revisionDate: new Date("2022-01-31T12:00:00.000Z"),
|
||||||
|
deletedDate: null,
|
||||||
|
reprompt: 0,
|
||||||
|
localData: undefined,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("CardCipher", () => {
|
||||||
|
let cipherData: CipherData;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
cipherData = {
|
||||||
|
id: "id",
|
||||||
|
organizationId: "orgId",
|
||||||
|
folderId: "folderId",
|
||||||
|
userId: "userId",
|
||||||
|
edit: true,
|
||||||
|
viewPassword: true,
|
||||||
|
organizationUseTotp: true,
|
||||||
|
favorite: false,
|
||||||
|
revisionDate: "2022-01-31T12:00:00.000Z",
|
||||||
|
type: CipherType.Card,
|
||||||
|
name: "EncryptedString",
|
||||||
|
notes: "EncryptedString",
|
||||||
|
deletedDate: null,
|
||||||
|
reprompt: CipherRepromptType.None,
|
||||||
|
card: {
|
||||||
|
cardholderName: "EncryptedString",
|
||||||
|
brand: "EncryptedString",
|
||||||
|
number: "EncryptedString",
|
||||||
|
expMonth: "EncryptedString",
|
||||||
|
expYear: "EncryptedString",
|
||||||
|
code: "EncryptedString",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Convert", () => {
|
||||||
|
const cipher = new Cipher(cipherData);
|
||||||
|
|
||||||
|
expect(cipher).toEqual({
|
||||||
|
id: "id",
|
||||||
|
userId: "userId",
|
||||||
|
organizationId: "orgId",
|
||||||
|
folderId: "folderId",
|
||||||
|
name: { encryptedString: "EncryptedString", encryptionType: 0 },
|
||||||
|
notes: { encryptedString: "EncryptedString", encryptionType: 0 },
|
||||||
|
type: 3,
|
||||||
|
favorite: false,
|
||||||
|
organizationUseTotp: true,
|
||||||
|
edit: true,
|
||||||
|
viewPassword: true,
|
||||||
|
revisionDate: new Date("2022-01-31T12:00:00.000Z"),
|
||||||
|
collectionIds: undefined,
|
||||||
|
localData: null,
|
||||||
|
deletedDate: null,
|
||||||
|
reprompt: 0,
|
||||||
|
card: {
|
||||||
|
cardholderName: { encryptedString: "EncryptedString", encryptionType: 0 },
|
||||||
|
brand: { encryptedString: "EncryptedString", encryptionType: 0 },
|
||||||
|
number: { encryptedString: "EncryptedString", encryptionType: 0 },
|
||||||
|
expMonth: { encryptedString: "EncryptedString", encryptionType: 0 },
|
||||||
|
expYear: { encryptedString: "EncryptedString", encryptionType: 0 },
|
||||||
|
code: { encryptedString: "EncryptedString", encryptionType: 0 },
|
||||||
|
},
|
||||||
|
attachments: null,
|
||||||
|
fields: null,
|
||||||
|
passwordHistory: null,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("toCipherData", () => {
|
||||||
|
const cipher = new Cipher(cipherData);
|
||||||
|
expect(cipher.toCipherData("userId")).toEqual(cipherData);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Decrypt", async () => {
|
||||||
|
const cipher = new Cipher();
|
||||||
|
cipher.id = "id";
|
||||||
|
cipher.organizationId = "orgId";
|
||||||
|
cipher.folderId = "folderId";
|
||||||
|
cipher.edit = true;
|
||||||
|
cipher.viewPassword = true;
|
||||||
|
cipher.organizationUseTotp = true;
|
||||||
|
cipher.favorite = false;
|
||||||
|
cipher.revisionDate = new Date("2022-01-31T12:00:00.000Z");
|
||||||
|
cipher.type = CipherType.Card;
|
||||||
|
cipher.name = mockEnc("EncryptedString");
|
||||||
|
cipher.notes = mockEnc("EncryptedString");
|
||||||
|
cipher.deletedDate = null;
|
||||||
|
cipher.reprompt = CipherRepromptType.None;
|
||||||
|
|
||||||
|
const cardView = new CardView();
|
||||||
|
cardView.cardholderName = "cardholderName";
|
||||||
|
cardView.number = "4111111111111111";
|
||||||
|
|
||||||
|
const card = Substitute.for<Card>();
|
||||||
|
card.decrypt(Arg.any(), Arg.any()).resolves(cardView);
|
||||||
|
cipher.card = card;
|
||||||
|
|
||||||
|
const cipherView = await cipher.decrypt();
|
||||||
|
|
||||||
|
expect(cipherView).toMatchObject({
|
||||||
|
id: "id",
|
||||||
|
organizationId: "orgId",
|
||||||
|
folderId: "folderId",
|
||||||
|
name: "EncryptedString",
|
||||||
|
notes: "EncryptedString",
|
||||||
|
type: 3,
|
||||||
|
favorite: false,
|
||||||
|
organizationUseTotp: true,
|
||||||
|
edit: true,
|
||||||
|
viewPassword: true,
|
||||||
|
card: cardView,
|
||||||
|
attachments: null,
|
||||||
|
fields: null,
|
||||||
|
passwordHistory: null,
|
||||||
|
collectionIds: undefined,
|
||||||
|
revisionDate: new Date("2022-01-31T12:00:00.000Z"),
|
||||||
|
deletedDate: null,
|
||||||
|
reprompt: 0,
|
||||||
|
localData: undefined,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("IdentityCipher", () => {
|
||||||
|
let cipherData: CipherData;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
cipherData = {
|
||||||
|
id: "id",
|
||||||
|
organizationId: "orgId",
|
||||||
|
folderId: "folderId",
|
||||||
|
userId: "userId",
|
||||||
|
edit: true,
|
||||||
|
viewPassword: true,
|
||||||
|
organizationUseTotp: true,
|
||||||
|
favorite: false,
|
||||||
|
revisionDate: "2022-01-31T12:00:00.000Z",
|
||||||
|
type: CipherType.Identity,
|
||||||
|
name: "EncryptedString",
|
||||||
|
notes: "EncryptedString",
|
||||||
|
deletedDate: null,
|
||||||
|
reprompt: CipherRepromptType.None,
|
||||||
|
identity: {
|
||||||
|
title: "EncryptedString",
|
||||||
|
firstName: "EncryptedString",
|
||||||
|
middleName: "EncryptedString",
|
||||||
|
lastName: "EncryptedString",
|
||||||
|
address1: "EncryptedString",
|
||||||
|
address2: "EncryptedString",
|
||||||
|
address3: "EncryptedString",
|
||||||
|
city: "EncryptedString",
|
||||||
|
state: "EncryptedString",
|
||||||
|
postalCode: "EncryptedString",
|
||||||
|
country: "EncryptedString",
|
||||||
|
company: "EncryptedString",
|
||||||
|
email: "EncryptedString",
|
||||||
|
phone: "EncryptedString",
|
||||||
|
ssn: "EncryptedString",
|
||||||
|
username: "EncryptedString",
|
||||||
|
passportNumber: "EncryptedString",
|
||||||
|
licenseNumber: "EncryptedString",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Convert", () => {
|
||||||
|
const cipher = new Cipher(cipherData);
|
||||||
|
|
||||||
|
expect(cipher).toEqual({
|
||||||
|
id: "id",
|
||||||
|
userId: "userId",
|
||||||
|
organizationId: "orgId",
|
||||||
|
folderId: "folderId",
|
||||||
|
name: { encryptedString: "EncryptedString", encryptionType: 0 },
|
||||||
|
notes: { encryptedString: "EncryptedString", encryptionType: 0 },
|
||||||
|
type: 4,
|
||||||
|
favorite: false,
|
||||||
|
organizationUseTotp: true,
|
||||||
|
edit: true,
|
||||||
|
viewPassword: true,
|
||||||
|
revisionDate: new Date("2022-01-31T12:00:00.000Z"),
|
||||||
|
collectionIds: undefined,
|
||||||
|
localData: null,
|
||||||
|
deletedDate: null,
|
||||||
|
reprompt: 0,
|
||||||
|
identity: {
|
||||||
|
title: { encryptedString: "EncryptedString", encryptionType: 0 },
|
||||||
|
firstName: { encryptedString: "EncryptedString", encryptionType: 0 },
|
||||||
|
middleName: { encryptedString: "EncryptedString", encryptionType: 0 },
|
||||||
|
lastName: { encryptedString: "EncryptedString", encryptionType: 0 },
|
||||||
|
address1: { encryptedString: "EncryptedString", encryptionType: 0 },
|
||||||
|
address2: { encryptedString: "EncryptedString", encryptionType: 0 },
|
||||||
|
address3: { encryptedString: "EncryptedString", encryptionType: 0 },
|
||||||
|
city: { encryptedString: "EncryptedString", encryptionType: 0 },
|
||||||
|
state: { encryptedString: "EncryptedString", encryptionType: 0 },
|
||||||
|
postalCode: { encryptedString: "EncryptedString", encryptionType: 0 },
|
||||||
|
country: { encryptedString: "EncryptedString", encryptionType: 0 },
|
||||||
|
company: { encryptedString: "EncryptedString", encryptionType: 0 },
|
||||||
|
email: { encryptedString: "EncryptedString", encryptionType: 0 },
|
||||||
|
phone: { encryptedString: "EncryptedString", encryptionType: 0 },
|
||||||
|
ssn: { encryptedString: "EncryptedString", encryptionType: 0 },
|
||||||
|
username: { encryptedString: "EncryptedString", encryptionType: 0 },
|
||||||
|
passportNumber: { encryptedString: "EncryptedString", encryptionType: 0 },
|
||||||
|
licenseNumber: { encryptedString: "EncryptedString", encryptionType: 0 },
|
||||||
|
},
|
||||||
|
attachments: null,
|
||||||
|
fields: null,
|
||||||
|
passwordHistory: null,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("toCipherData", () => {
|
||||||
|
const cipher = new Cipher(cipherData);
|
||||||
|
expect(cipher.toCipherData("userId")).toEqual(cipherData);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Decrypt", async () => {
|
||||||
|
const cipher = new Cipher();
|
||||||
|
cipher.id = "id";
|
||||||
|
cipher.organizationId = "orgId";
|
||||||
|
cipher.folderId = "folderId";
|
||||||
|
cipher.edit = true;
|
||||||
|
cipher.viewPassword = true;
|
||||||
|
cipher.organizationUseTotp = true;
|
||||||
|
cipher.favorite = false;
|
||||||
|
cipher.revisionDate = new Date("2022-01-31T12:00:00.000Z");
|
||||||
|
cipher.type = CipherType.Identity;
|
||||||
|
cipher.name = mockEnc("EncryptedString");
|
||||||
|
cipher.notes = mockEnc("EncryptedString");
|
||||||
|
cipher.deletedDate = null;
|
||||||
|
cipher.reprompt = CipherRepromptType.None;
|
||||||
|
|
||||||
|
const identityView = new IdentityView();
|
||||||
|
identityView.firstName = "firstName";
|
||||||
|
identityView.lastName = "lastName";
|
||||||
|
|
||||||
|
const identity = Substitute.for<Identity>();
|
||||||
|
identity.decrypt(Arg.any(), Arg.any()).resolves(identityView);
|
||||||
|
cipher.identity = identity;
|
||||||
|
|
||||||
|
const cipherView = await cipher.decrypt();
|
||||||
|
|
||||||
|
expect(cipherView).toMatchObject({
|
||||||
|
id: "id",
|
||||||
|
organizationId: "orgId",
|
||||||
|
folderId: "folderId",
|
||||||
|
name: "EncryptedString",
|
||||||
|
notes: "EncryptedString",
|
||||||
|
type: 4,
|
||||||
|
favorite: false,
|
||||||
|
organizationUseTotp: true,
|
||||||
|
edit: true,
|
||||||
|
viewPassword: true,
|
||||||
|
identity: identityView,
|
||||||
|
attachments: null,
|
||||||
|
fields: null,
|
||||||
|
passwordHistory: null,
|
||||||
|
collectionIds: undefined,
|
||||||
|
revisionDate: new Date("2022-01-31T12:00:00.000Z"),
|
||||||
|
deletedDate: null,
|
||||||
|
reprompt: 0,
|
||||||
|
localData: undefined,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
66
jslib/common/spec/domain/collection.spec.ts
Normal file
66
jslib/common/spec/domain/collection.spec.ts
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
import { CollectionData } from "jslib-common/models/data/collectionData";
|
||||||
|
import { Collection } from "jslib-common/models/domain/collection";
|
||||||
|
|
||||||
|
import { mockEnc } from "../utils";
|
||||||
|
|
||||||
|
describe("Collection", () => {
|
||||||
|
let data: CollectionData;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
data = {
|
||||||
|
id: "id",
|
||||||
|
organizationId: "orgId",
|
||||||
|
name: "encName",
|
||||||
|
externalId: "extId",
|
||||||
|
readOnly: true,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Convert from empty", () => {
|
||||||
|
const data = new CollectionData({} as any);
|
||||||
|
const card = new Collection(data);
|
||||||
|
|
||||||
|
expect(card).toEqual({
|
||||||
|
externalId: null,
|
||||||
|
hidePasswords: null,
|
||||||
|
id: null,
|
||||||
|
name: null,
|
||||||
|
organizationId: null,
|
||||||
|
readOnly: null,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Convert", () => {
|
||||||
|
const collection = new Collection(data);
|
||||||
|
|
||||||
|
expect(collection).toEqual({
|
||||||
|
id: "id",
|
||||||
|
organizationId: "orgId",
|
||||||
|
name: { encryptedString: "encName", encryptionType: 0 },
|
||||||
|
externalId: "extId",
|
||||||
|
readOnly: true,
|
||||||
|
hidePasswords: null,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Decrypt", async () => {
|
||||||
|
const collection = new Collection();
|
||||||
|
collection.id = "id";
|
||||||
|
collection.organizationId = "orgId";
|
||||||
|
collection.name = mockEnc("encName");
|
||||||
|
collection.externalId = "extId";
|
||||||
|
collection.readOnly = false;
|
||||||
|
collection.hidePasswords = false;
|
||||||
|
|
||||||
|
const view = await collection.decrypt();
|
||||||
|
|
||||||
|
expect(view).toEqual({
|
||||||
|
externalId: "extId",
|
||||||
|
hidePasswords: false,
|
||||||
|
id: "id",
|
||||||
|
name: "encName",
|
||||||
|
organizationId: "orgId",
|
||||||
|
readOnly: false,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
195
jslib/common/spec/domain/encString.spec.ts
Normal file
195
jslib/common/spec/domain/encString.spec.ts
Normal file
@@ -0,0 +1,195 @@
|
|||||||
|
import Substitute, { Arg } from "@fluffy-spoon/substitute";
|
||||||
|
|
||||||
|
import { CryptoService } from "jslib-common/abstractions/crypto.service";
|
||||||
|
import { EncryptionType } from "jslib-common/enums/encryptionType";
|
||||||
|
import { EncString } from "jslib-common/models/domain/encString";
|
||||||
|
import { SymmetricCryptoKey } from "jslib-common/models/domain/symmetricCryptoKey";
|
||||||
|
import { ContainerService } from "jslib-common/services/container.service";
|
||||||
|
|
||||||
|
describe("EncString", () => {
|
||||||
|
afterEach(() => {
|
||||||
|
(window as any).bitwardenContainerService = undefined;
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("Rsa2048_OaepSha256_B64", () => {
|
||||||
|
it("constructor", () => {
|
||||||
|
const encString = new EncString(EncryptionType.Rsa2048_OaepSha256_B64, "data");
|
||||||
|
|
||||||
|
expect(encString).toEqual({
|
||||||
|
data: "data",
|
||||||
|
encryptedString: "3.data",
|
||||||
|
encryptionType: 3,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("parse existing", () => {
|
||||||
|
it("valid", () => {
|
||||||
|
const encString = new EncString("3.data");
|
||||||
|
|
||||||
|
expect(encString).toEqual({
|
||||||
|
data: "data",
|
||||||
|
encryptedString: "3.data",
|
||||||
|
encryptionType: 3,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("invalid", () => {
|
||||||
|
const encString = new EncString("3.data|test");
|
||||||
|
|
||||||
|
expect(encString).toEqual({
|
||||||
|
encryptedString: "3.data|test",
|
||||||
|
encryptionType: 3,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("decrypt", () => {
|
||||||
|
const encString = new EncString(EncryptionType.Rsa2048_OaepSha256_B64, "data");
|
||||||
|
|
||||||
|
const cryptoService = Substitute.for<CryptoService>();
|
||||||
|
cryptoService.getOrgKey(null).resolves(null);
|
||||||
|
cryptoService.decryptToUtf8(encString, Arg.any()).resolves("decrypted");
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
(window as any).bitwardenContainerService = new ContainerService(cryptoService);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("decrypts correctly", async () => {
|
||||||
|
const decrypted = await encString.decrypt(null);
|
||||||
|
|
||||||
|
expect(decrypted).toBe("decrypted");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("result should be cached", async () => {
|
||||||
|
const decrypted = await encString.decrypt(null);
|
||||||
|
cryptoService.received(1).decryptToUtf8(Arg.any(), Arg.any());
|
||||||
|
|
||||||
|
expect(decrypted).toBe("decrypted");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("AesCbc256_B64", () => {
|
||||||
|
it("constructor", () => {
|
||||||
|
const encString = new EncString(EncryptionType.AesCbc256_B64, "data", "iv");
|
||||||
|
|
||||||
|
expect(encString).toEqual({
|
||||||
|
data: "data",
|
||||||
|
encryptedString: "0.iv|data",
|
||||||
|
encryptionType: 0,
|
||||||
|
iv: "iv",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("parse existing", () => {
|
||||||
|
it("valid", () => {
|
||||||
|
const encString = new EncString("0.iv|data");
|
||||||
|
|
||||||
|
expect(encString).toEqual({
|
||||||
|
data: "data",
|
||||||
|
encryptedString: "0.iv|data",
|
||||||
|
encryptionType: 0,
|
||||||
|
iv: "iv",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("invalid", () => {
|
||||||
|
const encString = new EncString("0.iv|data|mac");
|
||||||
|
|
||||||
|
expect(encString).toEqual({
|
||||||
|
encryptedString: "0.iv|data|mac",
|
||||||
|
encryptionType: 0,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("AesCbc256_HmacSha256_B64", () => {
|
||||||
|
it("constructor", () => {
|
||||||
|
const encString = new EncString(EncryptionType.AesCbc256_HmacSha256_B64, "data", "iv", "mac");
|
||||||
|
|
||||||
|
expect(encString).toEqual({
|
||||||
|
data: "data",
|
||||||
|
encryptedString: "2.iv|data|mac",
|
||||||
|
encryptionType: 2,
|
||||||
|
iv: "iv",
|
||||||
|
mac: "mac",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("valid", () => {
|
||||||
|
const encString = new EncString("2.iv|data|mac");
|
||||||
|
|
||||||
|
expect(encString).toEqual({
|
||||||
|
data: "data",
|
||||||
|
encryptedString: "2.iv|data|mac",
|
||||||
|
encryptionType: 2,
|
||||||
|
iv: "iv",
|
||||||
|
mac: "mac",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("invalid", () => {
|
||||||
|
const encString = new EncString("2.iv|data");
|
||||||
|
|
||||||
|
expect(encString).toEqual({
|
||||||
|
encryptedString: "2.iv|data",
|
||||||
|
encryptionType: 2,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Exit early if null", () => {
|
||||||
|
const encString = new EncString(null);
|
||||||
|
|
||||||
|
expect(encString).toEqual({
|
||||||
|
encryptedString: null,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("decrypt", () => {
|
||||||
|
it("throws exception when bitwarden container not initialized", async () => {
|
||||||
|
const encString = new EncString(null);
|
||||||
|
|
||||||
|
expect.assertions(1);
|
||||||
|
try {
|
||||||
|
await encString.decrypt(null);
|
||||||
|
} catch (e) {
|
||||||
|
expect(e.message).toEqual("global bitwardenContainerService not initialized.");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it("handles value it can't decrypt", async () => {
|
||||||
|
const encString = new EncString(null);
|
||||||
|
|
||||||
|
const cryptoService = Substitute.for<CryptoService>();
|
||||||
|
cryptoService.getOrgKey(null).resolves(null);
|
||||||
|
cryptoService.decryptToUtf8(encString, Arg.any()).throws("error");
|
||||||
|
|
||||||
|
(window as any).bitwardenContainerService = new ContainerService(cryptoService);
|
||||||
|
|
||||||
|
const decrypted = await encString.decrypt(null);
|
||||||
|
|
||||||
|
expect(decrypted).toBe("[error: cannot decrypt]");
|
||||||
|
|
||||||
|
expect(encString).toEqual({
|
||||||
|
decryptedValue: "[error: cannot decrypt]",
|
||||||
|
encryptedString: null,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("passes along key", async () => {
|
||||||
|
const encString = new EncString(null);
|
||||||
|
const key = Substitute.for<SymmetricCryptoKey>();
|
||||||
|
|
||||||
|
const cryptoService = Substitute.for<CryptoService>();
|
||||||
|
cryptoService.getOrgKey(null).resolves(null);
|
||||||
|
|
||||||
|
(window as any).bitwardenContainerService = new ContainerService(cryptoService);
|
||||||
|
|
||||||
|
await encString.decrypt(null, key);
|
||||||
|
|
||||||
|
cryptoService.received().decryptToUtf8(encString, key);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
64
jslib/common/spec/domain/field.spec.ts
Normal file
64
jslib/common/spec/domain/field.spec.ts
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
import { FieldType } from "jslib-common/enums/fieldType";
|
||||||
|
import { FieldData } from "jslib-common/models/data/fieldData";
|
||||||
|
import { Field } from "jslib-common/models/domain/field";
|
||||||
|
|
||||||
|
import { mockEnc } from "../utils";
|
||||||
|
|
||||||
|
describe("Field", () => {
|
||||||
|
let data: FieldData;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
data = {
|
||||||
|
type: FieldType.Text,
|
||||||
|
name: "encName",
|
||||||
|
value: "encValue",
|
||||||
|
linkedId: null,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Convert from empty", () => {
|
||||||
|
const data = new FieldData();
|
||||||
|
const field = new Field(data);
|
||||||
|
|
||||||
|
expect(field).toEqual({
|
||||||
|
type: undefined,
|
||||||
|
name: null,
|
||||||
|
value: null,
|
||||||
|
linkedId: undefined,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Convert", () => {
|
||||||
|
const field = new Field(data);
|
||||||
|
|
||||||
|
expect(field).toEqual({
|
||||||
|
type: FieldType.Text,
|
||||||
|
name: { encryptedString: "encName", encryptionType: 0 },
|
||||||
|
value: { encryptedString: "encValue", encryptionType: 0 },
|
||||||
|
linkedId: null,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("toFieldData", () => {
|
||||||
|
const field = new Field(data);
|
||||||
|
expect(field.toFieldData()).toEqual(data);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Decrypt", async () => {
|
||||||
|
const field = new Field();
|
||||||
|
field.type = FieldType.Text;
|
||||||
|
field.name = mockEnc("encName");
|
||||||
|
field.value = mockEnc("encValue");
|
||||||
|
|
||||||
|
const view = await field.decrypt(null);
|
||||||
|
|
||||||
|
expect(view).toEqual({
|
||||||
|
type: 0,
|
||||||
|
name: "encName",
|
||||||
|
value: "encValue",
|
||||||
|
newField: false,
|
||||||
|
showCount: false,
|
||||||
|
showValue: false,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
42
jslib/common/spec/domain/folder.spec.ts
Normal file
42
jslib/common/spec/domain/folder.spec.ts
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
import { FolderData } from "jslib-common/models/data/folderData";
|
||||||
|
import { Folder } from "jslib-common/models/domain/folder";
|
||||||
|
|
||||||
|
import { mockEnc } from "../utils";
|
||||||
|
|
||||||
|
describe("Folder", () => {
|
||||||
|
let data: FolderData;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
data = {
|
||||||
|
id: "id",
|
||||||
|
userId: "userId",
|
||||||
|
name: "encName",
|
||||||
|
revisionDate: "2022-01-31T12:00:00.000Z",
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Convert", () => {
|
||||||
|
const field = new Folder(data);
|
||||||
|
|
||||||
|
expect(field).toEqual({
|
||||||
|
id: "id",
|
||||||
|
name: { encryptedString: "encName", encryptionType: 0 },
|
||||||
|
revisionDate: new Date("2022-01-31T12:00:00.000Z"),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Decrypt", async () => {
|
||||||
|
const folder = new Folder();
|
||||||
|
folder.id = "id";
|
||||||
|
folder.name = mockEnc("encName");
|
||||||
|
folder.revisionDate = new Date("2022-01-31T12:00:00.000Z");
|
||||||
|
|
||||||
|
const view = await folder.decrypt();
|
||||||
|
|
||||||
|
expect(view).toEqual({
|
||||||
|
id: "id",
|
||||||
|
name: "encName",
|
||||||
|
revisionDate: new Date("2022-01-31T12:00:00.000Z"),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
134
jslib/common/spec/domain/identity.spec.ts
Normal file
134
jslib/common/spec/domain/identity.spec.ts
Normal file
@@ -0,0 +1,134 @@
|
|||||||
|
import { IdentityData } from "jslib-common/models/data/identityData";
|
||||||
|
import { Identity } from "jslib-common/models/domain/identity";
|
||||||
|
|
||||||
|
import { mockEnc } from "../utils";
|
||||||
|
|
||||||
|
describe("Identity", () => {
|
||||||
|
let data: IdentityData;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
data = {
|
||||||
|
title: "enctitle",
|
||||||
|
firstName: "encfirstName",
|
||||||
|
middleName: "encmiddleName",
|
||||||
|
lastName: "enclastName",
|
||||||
|
address1: "encaddress1",
|
||||||
|
address2: "encaddress2",
|
||||||
|
address3: "encaddress3",
|
||||||
|
city: "enccity",
|
||||||
|
state: "encstate",
|
||||||
|
postalCode: "encpostalCode",
|
||||||
|
country: "enccountry",
|
||||||
|
company: "enccompany",
|
||||||
|
email: "encemail",
|
||||||
|
phone: "encphone",
|
||||||
|
ssn: "encssn",
|
||||||
|
username: "encusername",
|
||||||
|
passportNumber: "encpassportNumber",
|
||||||
|
licenseNumber: "enclicenseNumber",
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Convert from empty", () => {
|
||||||
|
const data = new IdentityData();
|
||||||
|
const identity = new Identity(data);
|
||||||
|
|
||||||
|
expect(identity).toEqual({
|
||||||
|
address1: null,
|
||||||
|
address2: null,
|
||||||
|
address3: null,
|
||||||
|
city: null,
|
||||||
|
company: null,
|
||||||
|
country: null,
|
||||||
|
email: null,
|
||||||
|
firstName: null,
|
||||||
|
lastName: null,
|
||||||
|
licenseNumber: null,
|
||||||
|
middleName: null,
|
||||||
|
passportNumber: null,
|
||||||
|
phone: null,
|
||||||
|
postalCode: null,
|
||||||
|
ssn: null,
|
||||||
|
state: null,
|
||||||
|
title: null,
|
||||||
|
username: null,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Convert", () => {
|
||||||
|
const identity = new Identity(data);
|
||||||
|
|
||||||
|
expect(identity).toEqual({
|
||||||
|
title: { encryptedString: "enctitle", encryptionType: 0 },
|
||||||
|
firstName: { encryptedString: "encfirstName", encryptionType: 0 },
|
||||||
|
middleName: { encryptedString: "encmiddleName", encryptionType: 0 },
|
||||||
|
lastName: { encryptedString: "enclastName", encryptionType: 0 },
|
||||||
|
address1: { encryptedString: "encaddress1", encryptionType: 0 },
|
||||||
|
address2: { encryptedString: "encaddress2", encryptionType: 0 },
|
||||||
|
address3: { encryptedString: "encaddress3", encryptionType: 0 },
|
||||||
|
city: { encryptedString: "enccity", encryptionType: 0 },
|
||||||
|
state: { encryptedString: "encstate", encryptionType: 0 },
|
||||||
|
postalCode: { encryptedString: "encpostalCode", encryptionType: 0 },
|
||||||
|
country: { encryptedString: "enccountry", encryptionType: 0 },
|
||||||
|
company: { encryptedString: "enccompany", encryptionType: 0 },
|
||||||
|
email: { encryptedString: "encemail", encryptionType: 0 },
|
||||||
|
phone: { encryptedString: "encphone", encryptionType: 0 },
|
||||||
|
ssn: { encryptedString: "encssn", encryptionType: 0 },
|
||||||
|
username: { encryptedString: "encusername", encryptionType: 0 },
|
||||||
|
passportNumber: { encryptedString: "encpassportNumber", encryptionType: 0 },
|
||||||
|
licenseNumber: { encryptedString: "enclicenseNumber", encryptionType: 0 },
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("toIdentityData", () => {
|
||||||
|
const identity = new Identity(data);
|
||||||
|
expect(identity.toIdentityData()).toEqual(data);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Decrypt", async () => {
|
||||||
|
const identity = new Identity();
|
||||||
|
|
||||||
|
identity.title = mockEnc("mockTitle");
|
||||||
|
identity.firstName = mockEnc("mockFirstName");
|
||||||
|
identity.middleName = mockEnc("mockMiddleName");
|
||||||
|
identity.lastName = mockEnc("mockLastName");
|
||||||
|
identity.address1 = mockEnc("mockAddress1");
|
||||||
|
identity.address2 = mockEnc("mockAddress2");
|
||||||
|
identity.address3 = mockEnc("mockAddress3");
|
||||||
|
identity.city = mockEnc("mockCity");
|
||||||
|
identity.state = mockEnc("mockState");
|
||||||
|
identity.postalCode = mockEnc("mockPostalCode");
|
||||||
|
identity.country = mockEnc("mockCountry");
|
||||||
|
identity.company = mockEnc("mockCompany");
|
||||||
|
identity.email = mockEnc("mockEmail");
|
||||||
|
identity.phone = mockEnc("mockPhone");
|
||||||
|
identity.ssn = mockEnc("mockSsn");
|
||||||
|
identity.username = mockEnc("mockUsername");
|
||||||
|
identity.passportNumber = mockEnc("mockPassportNumber");
|
||||||
|
identity.licenseNumber = mockEnc("mockLicenseNumber");
|
||||||
|
|
||||||
|
const view = await identity.decrypt(null);
|
||||||
|
|
||||||
|
expect(view).toEqual({
|
||||||
|
_firstName: "mockFirstName",
|
||||||
|
_lastName: "mockLastName",
|
||||||
|
_subTitle: null,
|
||||||
|
address1: "mockAddress1",
|
||||||
|
address2: "mockAddress2",
|
||||||
|
address3: "mockAddress3",
|
||||||
|
city: "mockCity",
|
||||||
|
company: "mockCompany",
|
||||||
|
country: "mockCountry",
|
||||||
|
email: "mockEmail",
|
||||||
|
licenseNumber: "mockLicenseNumber",
|
||||||
|
middleName: "mockMiddleName",
|
||||||
|
passportNumber: "mockPassportNumber",
|
||||||
|
phone: "mockPhone",
|
||||||
|
postalCode: "mockPostalCode",
|
||||||
|
ssn: "mockSsn",
|
||||||
|
state: "mockState",
|
||||||
|
title: "mockTitle",
|
||||||
|
username: "mockUsername",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
101
jslib/common/spec/domain/login.spec.ts
Normal file
101
jslib/common/spec/domain/login.spec.ts
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
import Substitute, { Arg } from "@fluffy-spoon/substitute";
|
||||||
|
|
||||||
|
import { UriMatchType } from "jslib-common/enums/uriMatchType";
|
||||||
|
import { LoginData } from "jslib-common/models/data/loginData";
|
||||||
|
import { Login } from "jslib-common/models/domain/login";
|
||||||
|
import { LoginUri } from "jslib-common/models/domain/loginUri";
|
||||||
|
import { LoginUriView } from "jslib-common/models/view/loginUriView";
|
||||||
|
|
||||||
|
import { mockEnc } from "../utils";
|
||||||
|
|
||||||
|
describe("Login DTO", () => {
|
||||||
|
it("Convert from empty LoginData", () => {
|
||||||
|
const data = new LoginData();
|
||||||
|
const login = new Login(data);
|
||||||
|
|
||||||
|
expect(login).toEqual({
|
||||||
|
passwordRevisionDate: null,
|
||||||
|
autofillOnPageLoad: undefined,
|
||||||
|
username: null,
|
||||||
|
password: null,
|
||||||
|
totp: null,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Convert from full LoginData", () => {
|
||||||
|
const data: LoginData = {
|
||||||
|
uris: [{ uri: "uri", match: UriMatchType.Domain }],
|
||||||
|
username: "username",
|
||||||
|
password: "password",
|
||||||
|
passwordRevisionDate: "2022-01-31T12:00:00.000Z",
|
||||||
|
totp: "123",
|
||||||
|
autofillOnPageLoad: false,
|
||||||
|
};
|
||||||
|
const login = new Login(data);
|
||||||
|
|
||||||
|
expect(login).toEqual({
|
||||||
|
passwordRevisionDate: new Date("2022-01-31T12:00:00.000Z"),
|
||||||
|
autofillOnPageLoad: false,
|
||||||
|
username: { encryptedString: "username", encryptionType: 0 },
|
||||||
|
password: { encryptedString: "password", encryptionType: 0 },
|
||||||
|
totp: { encryptedString: "123", encryptionType: 0 },
|
||||||
|
uris: [{ match: 0, uri: { encryptedString: "uri", encryptionType: 0 } }],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Initialize without LoginData", () => {
|
||||||
|
const login = new Login();
|
||||||
|
|
||||||
|
expect(login).toEqual({});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Decrypts correctly", async () => {
|
||||||
|
const loginUri = Substitute.for<LoginUri>();
|
||||||
|
const loginUriView = new LoginUriView();
|
||||||
|
loginUriView.uri = "decrypted uri";
|
||||||
|
loginUri.decrypt(Arg.any()).resolves(loginUriView);
|
||||||
|
|
||||||
|
const login = new Login();
|
||||||
|
login.uris = [loginUri];
|
||||||
|
login.username = mockEnc("encrypted username");
|
||||||
|
login.password = mockEnc("encrypted password");
|
||||||
|
login.passwordRevisionDate = new Date("2022-01-31T12:00:00.000Z");
|
||||||
|
login.totp = mockEnc("encrypted totp");
|
||||||
|
login.autofillOnPageLoad = true;
|
||||||
|
|
||||||
|
const loginView = await login.decrypt(null);
|
||||||
|
expect(loginView).toEqual({
|
||||||
|
username: "encrypted username",
|
||||||
|
password: "encrypted password",
|
||||||
|
passwordRevisionDate: new Date("2022-01-31T12:00:00.000Z"),
|
||||||
|
totp: "encrypted totp",
|
||||||
|
uris: [
|
||||||
|
{
|
||||||
|
match: null,
|
||||||
|
_uri: "decrypted uri",
|
||||||
|
_domain: null,
|
||||||
|
_hostname: null,
|
||||||
|
_host: null,
|
||||||
|
_canLaunch: null,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
autofillOnPageLoad: true,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Converts from LoginData and back", () => {
|
||||||
|
const data: LoginData = {
|
||||||
|
uris: [{ uri: "uri", match: UriMatchType.Domain }],
|
||||||
|
username: "username",
|
||||||
|
password: "password",
|
||||||
|
passwordRevisionDate: "2022-01-31T12:00:00.000Z",
|
||||||
|
totp: "123",
|
||||||
|
autofillOnPageLoad: false,
|
||||||
|
};
|
||||||
|
const login = new Login(data);
|
||||||
|
|
||||||
|
const loginData = login.toLoginData();
|
||||||
|
|
||||||
|
expect(loginData).toEqual(data);
|
||||||
|
});
|
||||||
|
});
|
||||||
57
jslib/common/spec/domain/loginUri.spec.ts
Normal file
57
jslib/common/spec/domain/loginUri.spec.ts
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
import { UriMatchType } from "jslib-common/enums/uriMatchType";
|
||||||
|
import { LoginUriData } from "jslib-common/models/data/loginUriData";
|
||||||
|
import { LoginUri } from "jslib-common/models/domain/loginUri";
|
||||||
|
|
||||||
|
import { mockEnc } from "../utils";
|
||||||
|
|
||||||
|
describe("LoginUri", () => {
|
||||||
|
let data: LoginUriData;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
data = {
|
||||||
|
uri: "encUri",
|
||||||
|
match: UriMatchType.Domain,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Convert from empty", () => {
|
||||||
|
const data = new LoginUriData();
|
||||||
|
const loginUri = new LoginUri(data);
|
||||||
|
|
||||||
|
expect(loginUri).toEqual({
|
||||||
|
match: null,
|
||||||
|
uri: null,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Convert", () => {
|
||||||
|
const loginUri = new LoginUri(data);
|
||||||
|
|
||||||
|
expect(loginUri).toEqual({
|
||||||
|
match: 0,
|
||||||
|
uri: { encryptedString: "encUri", encryptionType: 0 },
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("toLoginUriData", () => {
|
||||||
|
const loginUri = new LoginUri(data);
|
||||||
|
expect(loginUri.toLoginUriData()).toEqual(data);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Decrypt", async () => {
|
||||||
|
const loginUri = new LoginUri();
|
||||||
|
loginUri.match = UriMatchType.Exact;
|
||||||
|
loginUri.uri = mockEnc("uri");
|
||||||
|
|
||||||
|
const view = await loginUri.decrypt(null);
|
||||||
|
|
||||||
|
expect(view).toEqual({
|
||||||
|
_canLaunch: null,
|
||||||
|
_domain: null,
|
||||||
|
_host: null,
|
||||||
|
_hostname: null,
|
||||||
|
_uri: "uri",
|
||||||
|
match: 3,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
51
jslib/common/spec/domain/password.spec.ts
Normal file
51
jslib/common/spec/domain/password.spec.ts
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
import { PasswordHistoryData } from "jslib-common/models/data/passwordHistoryData";
|
||||||
|
import { Password } from "jslib-common/models/domain/password";
|
||||||
|
|
||||||
|
import { mockEnc } from "../utils";
|
||||||
|
|
||||||
|
describe("Password", () => {
|
||||||
|
let data: PasswordHistoryData;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
data = {
|
||||||
|
password: "encPassword",
|
||||||
|
lastUsedDate: "2022-01-31T12:00:00.000Z",
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Convert from empty", () => {
|
||||||
|
const data = new PasswordHistoryData();
|
||||||
|
const password = new Password(data);
|
||||||
|
|
||||||
|
expect(password).toMatchObject({
|
||||||
|
password: null,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Convert", () => {
|
||||||
|
const password = new Password(data);
|
||||||
|
|
||||||
|
expect(password).toEqual({
|
||||||
|
password: { encryptedString: "encPassword", encryptionType: 0 },
|
||||||
|
lastUsedDate: new Date("2022-01-31T12:00:00.000Z"),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("toPasswordHistoryData", () => {
|
||||||
|
const password = new Password(data);
|
||||||
|
expect(password.toPasswordHistoryData()).toEqual(data);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Decrypt", async () => {
|
||||||
|
const password = new Password();
|
||||||
|
password.password = mockEnc("password");
|
||||||
|
password.lastUsedDate = new Date("2022-01-31T12:00:00.000Z");
|
||||||
|
|
||||||
|
const view = await password.decrypt(null);
|
||||||
|
|
||||||
|
expect(view).toEqual({
|
||||||
|
password: "password",
|
||||||
|
lastUsedDate: new Date("2022-01-31T12:00:00.000Z"),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
46
jslib/common/spec/domain/secureNote.spec.ts
Normal file
46
jslib/common/spec/domain/secureNote.spec.ts
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
import { SecureNoteType } from "jslib-common/enums/secureNoteType";
|
||||||
|
import { SecureNoteData } from "jslib-common/models/data/secureNoteData";
|
||||||
|
import { SecureNote } from "jslib-common/models/domain/secureNote";
|
||||||
|
|
||||||
|
describe("SecureNote", () => {
|
||||||
|
let data: SecureNoteData;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
data = {
|
||||||
|
type: SecureNoteType.Generic,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Convert from empty", () => {
|
||||||
|
const data = new SecureNoteData();
|
||||||
|
const secureNote = new SecureNote(data);
|
||||||
|
|
||||||
|
expect(secureNote).toEqual({
|
||||||
|
type: undefined,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Convert", () => {
|
||||||
|
const secureNote = new SecureNote(data);
|
||||||
|
|
||||||
|
expect(secureNote).toEqual({
|
||||||
|
type: 0,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("toSecureNoteData", () => {
|
||||||
|
const secureNote = new SecureNote(data);
|
||||||
|
expect(secureNote.toSecureNoteData()).toEqual(data);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Decrypt", async () => {
|
||||||
|
const secureNote = new SecureNote();
|
||||||
|
secureNote.type = SecureNoteType.Generic;
|
||||||
|
|
||||||
|
const view = await secureNote.decrypt(null);
|
||||||
|
|
||||||
|
expect(view).toEqual({
|
||||||
|
type: 0,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
144
jslib/common/spec/domain/send.spec.ts
Normal file
144
jslib/common/spec/domain/send.spec.ts
Normal file
@@ -0,0 +1,144 @@
|
|||||||
|
import Substitute, { Arg, SubstituteOf } from "@fluffy-spoon/substitute";
|
||||||
|
|
||||||
|
import { CryptoService } from "jslib-common/abstractions/crypto.service";
|
||||||
|
import { SendType } from "jslib-common/enums/sendType";
|
||||||
|
import { SendData } from "jslib-common/models/data/sendData";
|
||||||
|
import { EncString } from "jslib-common/models/domain/encString";
|
||||||
|
import { Send } from "jslib-common/models/domain/send";
|
||||||
|
import { SendText } from "jslib-common/models/domain/sendText";
|
||||||
|
import { ContainerService } from "jslib-common/services/container.service";
|
||||||
|
|
||||||
|
import { makeStaticByteArray, mockEnc } from "../utils";
|
||||||
|
|
||||||
|
describe("Send", () => {
|
||||||
|
let data: SendData;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
data = {
|
||||||
|
id: "id",
|
||||||
|
accessId: "accessId",
|
||||||
|
userId: "userId",
|
||||||
|
type: SendType.Text,
|
||||||
|
name: "encName",
|
||||||
|
notes: "encNotes",
|
||||||
|
text: {
|
||||||
|
text: "encText",
|
||||||
|
hidden: true,
|
||||||
|
},
|
||||||
|
file: null,
|
||||||
|
key: "encKey",
|
||||||
|
maxAccessCount: null,
|
||||||
|
accessCount: 10,
|
||||||
|
revisionDate: "2022-01-31T12:00:00.000Z",
|
||||||
|
expirationDate: "2022-01-31T12:00:00.000Z",
|
||||||
|
deletionDate: "2022-01-31T12:00:00.000Z",
|
||||||
|
password: "password",
|
||||||
|
disabled: false,
|
||||||
|
hideEmail: true,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Convert from empty", () => {
|
||||||
|
const data = new SendData();
|
||||||
|
const send = new Send(data);
|
||||||
|
|
||||||
|
expect(send).toEqual({
|
||||||
|
id: null,
|
||||||
|
accessId: null,
|
||||||
|
userId: null,
|
||||||
|
type: undefined,
|
||||||
|
name: null,
|
||||||
|
notes: null,
|
||||||
|
text: undefined,
|
||||||
|
file: undefined,
|
||||||
|
key: null,
|
||||||
|
maxAccessCount: undefined,
|
||||||
|
accessCount: undefined,
|
||||||
|
revisionDate: null,
|
||||||
|
expirationDate: null,
|
||||||
|
deletionDate: null,
|
||||||
|
password: undefined,
|
||||||
|
disabled: undefined,
|
||||||
|
hideEmail: undefined,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Convert", () => {
|
||||||
|
const send = new Send(data);
|
||||||
|
|
||||||
|
expect(send).toEqual({
|
||||||
|
id: "id",
|
||||||
|
accessId: "accessId",
|
||||||
|
userId: "userId",
|
||||||
|
type: SendType.Text,
|
||||||
|
name: { encryptedString: "encName", encryptionType: 0 },
|
||||||
|
notes: { encryptedString: "encNotes", encryptionType: 0 },
|
||||||
|
text: {
|
||||||
|
text: { encryptedString: "encText", encryptionType: 0 },
|
||||||
|
hidden: true,
|
||||||
|
},
|
||||||
|
key: { encryptedString: "encKey", encryptionType: 0 },
|
||||||
|
maxAccessCount: null,
|
||||||
|
accessCount: 10,
|
||||||
|
revisionDate: new Date("2022-01-31T12:00:00.000Z"),
|
||||||
|
expirationDate: new Date("2022-01-31T12:00:00.000Z"),
|
||||||
|
deletionDate: new Date("2022-01-31T12:00:00.000Z"),
|
||||||
|
password: "password",
|
||||||
|
disabled: false,
|
||||||
|
hideEmail: true,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Decrypt", async () => {
|
||||||
|
const text = Substitute.for<SendText>();
|
||||||
|
text.decrypt(Arg.any()).resolves("textView" as any);
|
||||||
|
|
||||||
|
const send = new Send();
|
||||||
|
send.id = "id";
|
||||||
|
send.accessId = "accessId";
|
||||||
|
send.userId = "userId";
|
||||||
|
send.type = SendType.Text;
|
||||||
|
send.name = mockEnc("name");
|
||||||
|
send.notes = mockEnc("notes");
|
||||||
|
send.text = text;
|
||||||
|
send.key = mockEnc("key");
|
||||||
|
send.accessCount = 10;
|
||||||
|
send.revisionDate = new Date("2022-01-31T12:00:00.000Z");
|
||||||
|
send.expirationDate = new Date("2022-01-31T12:00:00.000Z");
|
||||||
|
send.deletionDate = new Date("2022-01-31T12:00:00.000Z");
|
||||||
|
send.password = "password";
|
||||||
|
send.disabled = false;
|
||||||
|
send.hideEmail = true;
|
||||||
|
|
||||||
|
const cryptoService = Substitute.for<CryptoService>();
|
||||||
|
cryptoService.decryptToBytes(send.key, null).resolves(makeStaticByteArray(32));
|
||||||
|
cryptoService.makeSendKey(Arg.any()).resolves("cryptoKey" as any);
|
||||||
|
|
||||||
|
(window as any).bitwardenContainerService = new ContainerService(cryptoService);
|
||||||
|
|
||||||
|
const view = await send.decrypt();
|
||||||
|
|
||||||
|
text.received(1).decrypt("cryptoKey" as any);
|
||||||
|
(send.name as SubstituteOf<EncString>).received(1).decrypt(null, "cryptoKey" as any);
|
||||||
|
|
||||||
|
expect(view).toMatchObject({
|
||||||
|
id: "id",
|
||||||
|
accessId: "accessId",
|
||||||
|
name: "name",
|
||||||
|
notes: "notes",
|
||||||
|
type: 0,
|
||||||
|
key: expect.anything(),
|
||||||
|
cryptoKey: "cryptoKey",
|
||||||
|
file: expect.anything(),
|
||||||
|
text: "textView",
|
||||||
|
maxAccessCount: undefined,
|
||||||
|
accessCount: 10,
|
||||||
|
revisionDate: new Date("2022-01-31T12:00:00.000Z"),
|
||||||
|
expirationDate: new Date("2022-01-31T12:00:00.000Z"),
|
||||||
|
deletionDate: new Date("2022-01-31T12:00:00.000Z"),
|
||||||
|
password: "password",
|
||||||
|
disabled: false,
|
||||||
|
hideEmail: true,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
84
jslib/common/spec/domain/sendAccess.spec.ts
Normal file
84
jslib/common/spec/domain/sendAccess.spec.ts
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
import Substitute, { Arg } from "@fluffy-spoon/substitute";
|
||||||
|
|
||||||
|
import { SendType } from "jslib-common/enums/sendType";
|
||||||
|
import { SendAccess } from "jslib-common/models/domain/sendAccess";
|
||||||
|
import { SendText } from "jslib-common/models/domain/sendText";
|
||||||
|
import { SendAccessResponse } from "jslib-common/models/response/sendAccessResponse";
|
||||||
|
|
||||||
|
import { mockEnc } from "../utils";
|
||||||
|
|
||||||
|
describe("SendAccess", () => {
|
||||||
|
let request: SendAccessResponse;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
request = {
|
||||||
|
id: "id",
|
||||||
|
type: SendType.Text,
|
||||||
|
name: "encName",
|
||||||
|
file: null,
|
||||||
|
text: {
|
||||||
|
text: "encText",
|
||||||
|
hidden: true,
|
||||||
|
},
|
||||||
|
expirationDate: new Date("2022-01-31T12:00:00.000Z"),
|
||||||
|
creatorIdentifier: "creatorIdentifier",
|
||||||
|
} as SendAccessResponse;
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Convert from empty", () => {
|
||||||
|
const request = new SendAccessResponse({});
|
||||||
|
const sendAccess = new SendAccess(request);
|
||||||
|
|
||||||
|
expect(sendAccess).toEqual({
|
||||||
|
id: null,
|
||||||
|
type: undefined,
|
||||||
|
name: null,
|
||||||
|
creatorIdentifier: null,
|
||||||
|
expirationDate: null,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Convert", () => {
|
||||||
|
const sendAccess = new SendAccess(request);
|
||||||
|
|
||||||
|
expect(sendAccess).toEqual({
|
||||||
|
id: "id",
|
||||||
|
type: 0,
|
||||||
|
name: { encryptedString: "encName", encryptionType: 0 },
|
||||||
|
text: {
|
||||||
|
hidden: true,
|
||||||
|
text: { encryptedString: "encText", encryptionType: 0 },
|
||||||
|
},
|
||||||
|
expirationDate: new Date("2022-01-31T12:00:00.000Z"),
|
||||||
|
creatorIdentifier: "creatorIdentifier",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Decrypt", async () => {
|
||||||
|
const sendAccess = new SendAccess();
|
||||||
|
sendAccess.id = "id";
|
||||||
|
sendAccess.type = SendType.Text;
|
||||||
|
sendAccess.name = mockEnc("name");
|
||||||
|
|
||||||
|
const text = Substitute.for<SendText>();
|
||||||
|
text.decrypt(Arg.any()).resolves({} as any);
|
||||||
|
sendAccess.text = text;
|
||||||
|
|
||||||
|
sendAccess.expirationDate = new Date("2022-01-31T12:00:00.000Z");
|
||||||
|
sendAccess.creatorIdentifier = "creatorIdentifier";
|
||||||
|
|
||||||
|
const view = await sendAccess.decrypt(null);
|
||||||
|
|
||||||
|
text.received(1).decrypt(Arg.any());
|
||||||
|
|
||||||
|
expect(view).toEqual({
|
||||||
|
id: "id",
|
||||||
|
type: 0,
|
||||||
|
name: "name",
|
||||||
|
text: {},
|
||||||
|
file: expect.anything(),
|
||||||
|
expirationDate: new Date("2022-01-31T12:00:00.000Z"),
|
||||||
|
creatorIdentifier: "creatorIdentifier",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
57
jslib/common/spec/domain/sendFile.spec.ts
Normal file
57
jslib/common/spec/domain/sendFile.spec.ts
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
import { SendFileData } from "jslib-common/models/data/sendFileData";
|
||||||
|
import { SendFile } from "jslib-common/models/domain/sendFile";
|
||||||
|
|
||||||
|
import { mockEnc } from "../utils";
|
||||||
|
|
||||||
|
describe("SendFile", () => {
|
||||||
|
let data: SendFileData;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
data = {
|
||||||
|
id: "id",
|
||||||
|
size: "1100",
|
||||||
|
sizeName: "1.1 KB",
|
||||||
|
fileName: "encFileName",
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Convert from empty", () => {
|
||||||
|
const data = new SendFileData();
|
||||||
|
const sendFile = new SendFile(data);
|
||||||
|
|
||||||
|
expect(sendFile).toEqual({
|
||||||
|
fileName: null,
|
||||||
|
id: null,
|
||||||
|
size: undefined,
|
||||||
|
sizeName: null,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Convert", () => {
|
||||||
|
const sendFile = new SendFile(data);
|
||||||
|
|
||||||
|
expect(sendFile).toEqual({
|
||||||
|
id: "id",
|
||||||
|
size: "1100",
|
||||||
|
sizeName: "1.1 KB",
|
||||||
|
fileName: { encryptedString: "encFileName", encryptionType: 0 },
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Decrypt", async () => {
|
||||||
|
const sendFile = new SendFile();
|
||||||
|
sendFile.id = "id";
|
||||||
|
sendFile.size = "1100";
|
||||||
|
sendFile.sizeName = "1.1 KB";
|
||||||
|
sendFile.fileName = mockEnc("fileName");
|
||||||
|
|
||||||
|
const view = await sendFile.decrypt(null);
|
||||||
|
|
||||||
|
expect(view).toEqual({
|
||||||
|
fileName: "fileName",
|
||||||
|
id: "id",
|
||||||
|
size: "1100",
|
||||||
|
sizeName: "1.1 KB",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
47
jslib/common/spec/domain/sendText.spec.ts
Normal file
47
jslib/common/spec/domain/sendText.spec.ts
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
import { SendTextData } from "jslib-common/models/data/sendTextData";
|
||||||
|
import { SendText } from "jslib-common/models/domain/sendText";
|
||||||
|
|
||||||
|
import { mockEnc } from "../utils";
|
||||||
|
|
||||||
|
describe("SendText", () => {
|
||||||
|
let data: SendTextData;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
data = {
|
||||||
|
text: "encText",
|
||||||
|
hidden: false,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Convert from empty", () => {
|
||||||
|
const data = new SendTextData();
|
||||||
|
const secureNote = new SendText(data);
|
||||||
|
|
||||||
|
expect(secureNote).toEqual({
|
||||||
|
hidden: undefined,
|
||||||
|
text: null,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Convert", () => {
|
||||||
|
const secureNote = new SendText(data);
|
||||||
|
|
||||||
|
expect(secureNote).toEqual({
|
||||||
|
hidden: false,
|
||||||
|
text: { encryptedString: "encText", encryptionType: 0 },
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Decrypt", async () => {
|
||||||
|
const secureNote = new SendText();
|
||||||
|
secureNote.text = mockEnc("text");
|
||||||
|
secureNote.hidden = true;
|
||||||
|
|
||||||
|
const view = await secureNote.decrypt(null);
|
||||||
|
|
||||||
|
expect(view).toEqual({
|
||||||
|
text: "text",
|
||||||
|
hidden: true,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
69
jslib/common/spec/domain/symmetricCryptoKey.spec.ts
Normal file
69
jslib/common/spec/domain/symmetricCryptoKey.spec.ts
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
import { EncryptionType } from "jslib-common/enums/encryptionType";
|
||||||
|
import { SymmetricCryptoKey } from "jslib-common/models/domain/symmetricCryptoKey";
|
||||||
|
|
||||||
|
import { makeStaticByteArray } from "../utils";
|
||||||
|
|
||||||
|
describe("SymmetricCryptoKey", () => {
|
||||||
|
it("errors if no key", () => {
|
||||||
|
const t = () => {
|
||||||
|
new SymmetricCryptoKey(null);
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(t).toThrowError("Must provide key");
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("guesses encKey from key length", () => {
|
||||||
|
it("AesCbc256_B64", () => {
|
||||||
|
const key = makeStaticByteArray(32);
|
||||||
|
const cryptoKey = new SymmetricCryptoKey(key);
|
||||||
|
|
||||||
|
expect(cryptoKey).toEqual({
|
||||||
|
encKey: key,
|
||||||
|
encKeyB64: "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8=",
|
||||||
|
encType: 0,
|
||||||
|
key: key,
|
||||||
|
keyB64: "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8=",
|
||||||
|
macKey: null,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("AesCbc128_HmacSha256_B64", () => {
|
||||||
|
const key = makeStaticByteArray(32);
|
||||||
|
const cryptoKey = new SymmetricCryptoKey(key, EncryptionType.AesCbc128_HmacSha256_B64);
|
||||||
|
|
||||||
|
expect(cryptoKey).toEqual({
|
||||||
|
encKey: key.slice(0, 16),
|
||||||
|
encKeyB64: "AAECAwQFBgcICQoLDA0ODw==",
|
||||||
|
encType: 1,
|
||||||
|
key: key,
|
||||||
|
keyB64: "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8=",
|
||||||
|
macKey: key.slice(16, 32),
|
||||||
|
macKeyB64: "EBESExQVFhcYGRobHB0eHw==",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("AesCbc256_HmacSha256_B64", () => {
|
||||||
|
const key = makeStaticByteArray(64);
|
||||||
|
const cryptoKey = new SymmetricCryptoKey(key);
|
||||||
|
|
||||||
|
expect(cryptoKey).toEqual({
|
||||||
|
encKey: key.slice(0, 32),
|
||||||
|
encKeyB64: "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8=",
|
||||||
|
encType: 2,
|
||||||
|
key: key,
|
||||||
|
keyB64:
|
||||||
|
"AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4vMDEyMzQ1Njc4OTo7PD0+Pw==",
|
||||||
|
macKey: key.slice(32, 64),
|
||||||
|
macKeyB64: "ICEiIyQlJicoKSorLC0uLzAxMjM0NTY3ODk6Ozw9Pj8=",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("unknown length", () => {
|
||||||
|
const t = () => {
|
||||||
|
new SymmetricCryptoKey(makeStaticByteArray(30));
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(t).toThrowError("Unable to determine encType.");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
31
jslib/common/spec/importers/bitwardenJsonImporter.spec.ts
Normal file
31
jslib/common/spec/importers/bitwardenJsonImporter.spec.ts
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
import { Substitute, SubstituteOf } from "@fluffy-spoon/substitute";
|
||||||
|
|
||||||
|
import { CryptoService } from "jslib-common/abstractions/crypto.service";
|
||||||
|
import { I18nService } from "jslib-common/abstractions/i18n.service";
|
||||||
|
import { BitwardenJsonImporter } from "jslib-common/importers/bitwardenJsonImporter";
|
||||||
|
|
||||||
|
import { data as passwordProtectedData } from "./testData/bitwardenJson/passwordProtected.json";
|
||||||
|
|
||||||
|
describe("bitwarden json importer", () => {
|
||||||
|
let sut: BitwardenJsonImporter;
|
||||||
|
let cryptoService: SubstituteOf<CryptoService>;
|
||||||
|
let i18nService: SubstituteOf<I18nService>;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
cryptoService = Substitute.for<CryptoService>();
|
||||||
|
i18nService = Substitute.for<I18nService>();
|
||||||
|
|
||||||
|
sut = new BitwardenJsonImporter(cryptoService, i18nService);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should fail if password is needed", async () => {
|
||||||
|
expect((await sut.parse(passwordProtectedData)).success).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return password needed error message", async () => {
|
||||||
|
const expected = "Password required error message";
|
||||||
|
i18nService.t("importPasswordRequired").returns(expected);
|
||||||
|
|
||||||
|
expect((await sut.parse(passwordProtectedData)).errorMessage).toEqual(expected);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,113 @@
|
|||||||
|
import Substitute, { Arg, SubstituteOf } from "@fluffy-spoon/substitute";
|
||||||
|
|
||||||
|
import { CryptoService } from "jslib-common/abstractions/crypto.service";
|
||||||
|
import { I18nService } from "jslib-common/abstractions/i18n.service";
|
||||||
|
import { KdfType } from "jslib-common/enums/kdfType";
|
||||||
|
import { BitwardenPasswordProtectedImporter } from "jslib-common/importers/bitwardenPasswordProtectedImporter";
|
||||||
|
import { Utils } from "jslib-common/misc/utils";
|
||||||
|
import { ImportResult } from "jslib-common/models/domain/importResult";
|
||||||
|
|
||||||
|
import { data as emptyDecryptedData } from "./testData/bitwardenJson/empty.json";
|
||||||
|
|
||||||
|
describe("BitwardenPasswordProtectedImporter", () => {
|
||||||
|
let importer: BitwardenPasswordProtectedImporter;
|
||||||
|
let cryptoService: SubstituteOf<CryptoService>;
|
||||||
|
let i18nService: SubstituteOf<I18nService>;
|
||||||
|
const password = Utils.newGuid();
|
||||||
|
const result = new ImportResult();
|
||||||
|
let jDoc: {
|
||||||
|
encrypted?: boolean;
|
||||||
|
passwordProtected?: boolean;
|
||||||
|
salt?: string;
|
||||||
|
kdfIterations?: any;
|
||||||
|
kdfType?: any;
|
||||||
|
encKeyValidation_DO_NOT_EDIT?: string;
|
||||||
|
data?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
cryptoService = Substitute.for<CryptoService>();
|
||||||
|
i18nService = Substitute.for<I18nService>();
|
||||||
|
|
||||||
|
jDoc = {
|
||||||
|
encrypted: true,
|
||||||
|
passwordProtected: true,
|
||||||
|
salt: "c2FsdA==",
|
||||||
|
kdfIterations: 100000,
|
||||||
|
kdfType: KdfType.PBKDF2_SHA256,
|
||||||
|
encKeyValidation_DO_NOT_EDIT: Utils.newGuid(),
|
||||||
|
data: Utils.newGuid(),
|
||||||
|
};
|
||||||
|
|
||||||
|
result.success = true;
|
||||||
|
importer = new BitwardenPasswordProtectedImporter(cryptoService, i18nService, password);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("Required Json Data", () => {
|
||||||
|
it("succeeds with default jdoc", async () => {
|
||||||
|
cryptoService.decryptToUtf8(Arg.any(), Arg.any()).resolves(emptyDecryptedData);
|
||||||
|
|
||||||
|
expect((await importer.parse(JSON.stringify(jDoc))).success).toEqual(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("fails if encrypted === false", async () => {
|
||||||
|
jDoc.encrypted = false;
|
||||||
|
expect((await importer.parse(JSON.stringify(jDoc))).success).toEqual(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("fails if encrypted === null", async () => {
|
||||||
|
jDoc.encrypted = null;
|
||||||
|
expect((await importer.parse(JSON.stringify(jDoc))).success).toEqual(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("fails if passwordProtected === false", async () => {
|
||||||
|
jDoc.passwordProtected = false;
|
||||||
|
expect((await importer.parse(JSON.stringify(jDoc))).success).toEqual(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("fails if passwordProtected === null", async () => {
|
||||||
|
jDoc.passwordProtected = null;
|
||||||
|
expect((await importer.parse(JSON.stringify(jDoc))).success).toEqual(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("fails if salt === null", async () => {
|
||||||
|
jDoc.salt = null;
|
||||||
|
expect((await importer.parse(JSON.stringify(jDoc))).success).toEqual(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("fails if kdfIterations === null", async () => {
|
||||||
|
jDoc.kdfIterations = null;
|
||||||
|
expect((await importer.parse(JSON.stringify(jDoc))).success).toEqual(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("fails if kdfIterations is not a number", async () => {
|
||||||
|
jDoc.kdfIterations = "not a number";
|
||||||
|
expect((await importer.parse(JSON.stringify(jDoc))).success).toEqual(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("fails if kdfType === null", async () => {
|
||||||
|
jDoc.kdfType = null;
|
||||||
|
expect((await importer.parse(JSON.stringify(jDoc))).success).toEqual(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("fails if kdfType is not a string", async () => {
|
||||||
|
jDoc.kdfType = "not a valid kdf type";
|
||||||
|
expect((await importer.parse(JSON.stringify(jDoc))).success).toEqual(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("fails if kdfType is not a known kdfType", async () => {
|
||||||
|
jDoc.kdfType = -1;
|
||||||
|
expect((await importer.parse(JSON.stringify(jDoc))).success).toEqual(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("fails if encKeyValidation_DO_NOT_EDIT === null", async () => {
|
||||||
|
jDoc.encKeyValidation_DO_NOT_EDIT = null;
|
||||||
|
expect((await importer.parse(JSON.stringify(jDoc))).success).toEqual(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("fails if data === null", async () => {
|
||||||
|
jDoc.data = null;
|
||||||
|
expect((await importer.parse(JSON.stringify(jDoc))).success).toEqual(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
367
jslib/common/spec/importers/dashlaneCsvImporter.spec.ts
Normal file
367
jslib/common/spec/importers/dashlaneCsvImporter.spec.ts
Normal file
@@ -0,0 +1,367 @@
|
|||||||
|
import { CipherType } from "jslib-common/enums/cipherType";
|
||||||
|
import { DashlaneCsvImporter as Importer } from "jslib-common/importers/dashlaneImporters/dashlaneCsvImporter";
|
||||||
|
|
||||||
|
import { credentialsData } from "./testData/dashlaneCsv/credentials.csv";
|
||||||
|
import { identityData } from "./testData/dashlaneCsv/id.csv";
|
||||||
|
import { multiplePersonalInfoData } from "./testData/dashlaneCsv/multiplePersonalInfo.csv";
|
||||||
|
import { paymentsData } from "./testData/dashlaneCsv/payments.csv";
|
||||||
|
import { personalInfoData } from "./testData/dashlaneCsv/personalInfo.csv";
|
||||||
|
import { secureNoteData } from "./testData/dashlaneCsv/securenotes.csv";
|
||||||
|
|
||||||
|
describe("Dashlane CSV Importer", () => {
|
||||||
|
let importer: Importer;
|
||||||
|
beforeEach(() => {
|
||||||
|
importer = new Importer();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should parse login records", async () => {
|
||||||
|
const result = await importer.parse(credentialsData);
|
||||||
|
expect(result != null).toBe(true);
|
||||||
|
|
||||||
|
const cipher = result.ciphers.shift();
|
||||||
|
expect(cipher.name).toEqual("example.com");
|
||||||
|
expect(cipher.login.username).toEqual("jdoe");
|
||||||
|
expect(cipher.login.password).toEqual("somePassword");
|
||||||
|
expect(cipher.login.totp).toEqual("someTOTPSeed");
|
||||||
|
expect(cipher.login.uris.length).toEqual(1);
|
||||||
|
const uriView = cipher.login.uris.shift();
|
||||||
|
expect(uriView.uri).toEqual("https://www.example.com");
|
||||||
|
expect(cipher.notes).toEqual("some note for example.com");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should parse an item and create a folder", async () => {
|
||||||
|
const result = await importer.parse(credentialsData);
|
||||||
|
|
||||||
|
expect(result).not.toBeNull();
|
||||||
|
expect(result.success).toBe(true);
|
||||||
|
expect(result.folders.length).toBe(1);
|
||||||
|
expect(result.folders[0].name).toBe("Entertainment");
|
||||||
|
expect(result.folderRelationships[0]).toEqual([0, 0]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should parse payment records", async () => {
|
||||||
|
const result = await importer.parse(paymentsData);
|
||||||
|
|
||||||
|
expect(result).not.toBeNull();
|
||||||
|
expect(result.success).toBe(true);
|
||||||
|
expect(result.ciphers.length).toBe(2);
|
||||||
|
|
||||||
|
// Account
|
||||||
|
const cipher = result.ciphers.shift();
|
||||||
|
expect(cipher.type).toBe(CipherType.Card);
|
||||||
|
expect(cipher.name).toBe("John's savings account");
|
||||||
|
expect(cipher.card.brand).toBeNull();
|
||||||
|
expect(cipher.card.cardholderName).toBe("John Doe");
|
||||||
|
expect(cipher.card.number).toBe("accountNumber");
|
||||||
|
expect(cipher.card.code).toBeNull();
|
||||||
|
expect(cipher.card.expMonth).toBeNull();
|
||||||
|
expect(cipher.card.expYear).toBeNull();
|
||||||
|
|
||||||
|
expect(cipher.fields.length).toBe(4);
|
||||||
|
|
||||||
|
expect(cipher.fields[0].name).toBe("type");
|
||||||
|
expect(cipher.fields[0].value).toBe("bank");
|
||||||
|
|
||||||
|
expect(cipher.fields[1].name).toBe("routing_number");
|
||||||
|
expect(cipher.fields[1].value).toBe("routingNumber");
|
||||||
|
|
||||||
|
expect(cipher.fields[2].name).toBe("country");
|
||||||
|
expect(cipher.fields[2].value).toBe("US");
|
||||||
|
|
||||||
|
expect(cipher.fields[3].name).toBe("issuing_bank");
|
||||||
|
expect(cipher.fields[3].value).toBe("US-ALLY");
|
||||||
|
|
||||||
|
// CreditCard
|
||||||
|
const cipher2 = result.ciphers.shift();
|
||||||
|
expect(cipher2.type).toBe(CipherType.Card);
|
||||||
|
expect(cipher2.name).toBe("John Doe");
|
||||||
|
expect(cipher2.card.brand).toBe("Visa");
|
||||||
|
expect(cipher2.card.cardholderName).toBe("John Doe");
|
||||||
|
expect(cipher2.card.number).toBe("41111111111111111");
|
||||||
|
expect(cipher2.card.code).toBe("123");
|
||||||
|
expect(cipher2.card.expMonth).toBe("01");
|
||||||
|
expect(cipher2.card.expYear).toBe("23");
|
||||||
|
|
||||||
|
expect(cipher2.fields.length).toBe(2);
|
||||||
|
|
||||||
|
expect(cipher2.fields[0].name).toBe("type");
|
||||||
|
expect(cipher2.fields[0].value).toBe("credit_card");
|
||||||
|
|
||||||
|
expect(cipher2.fields[1].name).toBe("country");
|
||||||
|
expect(cipher2.fields[1].value).toBe("US");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should parse ids records", async () => {
|
||||||
|
const result = await importer.parse(identityData);
|
||||||
|
|
||||||
|
expect(result).not.toBeNull();
|
||||||
|
expect(result.success).toBe(true);
|
||||||
|
|
||||||
|
// Type card
|
||||||
|
const cipher = result.ciphers.shift();
|
||||||
|
expect(cipher.type).toBe(CipherType.Identity);
|
||||||
|
expect(cipher.name).toBe("John Doe card");
|
||||||
|
expect(cipher.identity.fullName).toBe("John Doe");
|
||||||
|
expect(cipher.identity.firstName).toBe("John");
|
||||||
|
expect(cipher.identity.middleName).toBeNull();
|
||||||
|
expect(cipher.identity.lastName).toBe("Doe");
|
||||||
|
expect(cipher.identity.licenseNumber).toBe("123123123");
|
||||||
|
|
||||||
|
expect(cipher.fields.length).toBe(3);
|
||||||
|
|
||||||
|
expect(cipher.fields[0].name).toEqual("type");
|
||||||
|
expect(cipher.fields[0].value).toEqual("card");
|
||||||
|
|
||||||
|
expect(cipher.fields[1].name).toEqual("issue_date");
|
||||||
|
expect(cipher.fields[1].value).toEqual("2022-1-30");
|
||||||
|
|
||||||
|
expect(cipher.fields[2].name).toEqual("expiration_date");
|
||||||
|
expect(cipher.fields[2].value).toEqual("2032-1-30");
|
||||||
|
|
||||||
|
// Type passport
|
||||||
|
const cipher2 = result.ciphers.shift();
|
||||||
|
expect(cipher2.type).toBe(CipherType.Identity);
|
||||||
|
expect(cipher2.name).toBe("John Doe passport");
|
||||||
|
expect(cipher2.identity.fullName).toBe("John Doe");
|
||||||
|
expect(cipher2.identity.firstName).toBe("John");
|
||||||
|
expect(cipher2.identity.middleName).toBeNull();
|
||||||
|
expect(cipher2.identity.lastName).toBe("Doe");
|
||||||
|
expect(cipher2.identity.passportNumber).toBe("123123123");
|
||||||
|
|
||||||
|
expect(cipher2.fields.length).toBe(4);
|
||||||
|
|
||||||
|
expect(cipher2.fields[0].name).toEqual("type");
|
||||||
|
expect(cipher2.fields[0].value).toEqual("passport");
|
||||||
|
expect(cipher2.fields[1].name).toEqual("issue_date");
|
||||||
|
expect(cipher2.fields[1].value).toEqual("2022-1-30");
|
||||||
|
expect(cipher2.fields[2].name).toEqual("expiration_date");
|
||||||
|
expect(cipher2.fields[2].value).toEqual("2032-1-30");
|
||||||
|
expect(cipher2.fields[3].name).toEqual("place_of_issue");
|
||||||
|
expect(cipher2.fields[3].value).toEqual("somewhere in Germany");
|
||||||
|
|
||||||
|
// Type license
|
||||||
|
const cipher3 = result.ciphers.shift();
|
||||||
|
expect(cipher3.type).toBe(CipherType.Identity);
|
||||||
|
expect(cipher3.name).toBe("John Doe license");
|
||||||
|
expect(cipher3.identity.fullName).toBe("John Doe");
|
||||||
|
expect(cipher3.identity.firstName).toBe("John");
|
||||||
|
expect(cipher3.identity.middleName).toBeNull();
|
||||||
|
expect(cipher3.identity.lastName).toBe("Doe");
|
||||||
|
expect(cipher3.identity.licenseNumber).toBe("1234556");
|
||||||
|
expect(cipher3.identity.state).toBe("DC");
|
||||||
|
|
||||||
|
expect(cipher3.fields.length).toBe(3);
|
||||||
|
expect(cipher3.fields[0].name).toEqual("type");
|
||||||
|
expect(cipher3.fields[0].value).toEqual("license");
|
||||||
|
expect(cipher3.fields[1].name).toEqual("issue_date");
|
||||||
|
expect(cipher3.fields[1].value).toEqual("2022-8-10");
|
||||||
|
expect(cipher3.fields[2].name).toEqual("expiration_date");
|
||||||
|
expect(cipher3.fields[2].value).toEqual("2022-10-10");
|
||||||
|
|
||||||
|
// Type social_security
|
||||||
|
const cipher4 = result.ciphers.shift();
|
||||||
|
expect(cipher4.type).toBe(CipherType.Identity);
|
||||||
|
expect(cipher4.name).toBe("John Doe social_security");
|
||||||
|
expect(cipher4.identity.fullName).toBe("John Doe");
|
||||||
|
expect(cipher4.identity.firstName).toBe("John");
|
||||||
|
expect(cipher4.identity.middleName).toBeNull();
|
||||||
|
expect(cipher4.identity.lastName).toBe("Doe");
|
||||||
|
expect(cipher4.identity.ssn).toBe("123123123");
|
||||||
|
|
||||||
|
expect(cipher4.fields.length).toBe(1);
|
||||||
|
expect(cipher4.fields[0].name).toEqual("type");
|
||||||
|
expect(cipher4.fields[0].value).toEqual("social_security");
|
||||||
|
|
||||||
|
// Type tax_number
|
||||||
|
const cipher5 = result.ciphers.shift();
|
||||||
|
expect(cipher5.type).toBe(CipherType.Identity);
|
||||||
|
expect(cipher5.name).toBe("tax_number");
|
||||||
|
expect(cipher5.identity.licenseNumber).toBe("123123123");
|
||||||
|
|
||||||
|
expect(cipher5.fields.length).toBe(1);
|
||||||
|
expect(cipher5.fields[0].name).toEqual("type");
|
||||||
|
expect(cipher5.fields[0].value).toEqual("tax_number");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should parse secureNote records", async () => {
|
||||||
|
const result = await importer.parse(secureNoteData);
|
||||||
|
|
||||||
|
expect(result).not.toBeNull();
|
||||||
|
expect(result.success).toBe(true);
|
||||||
|
expect(result.ciphers.length).toBe(1);
|
||||||
|
|
||||||
|
const cipher = result.ciphers.shift();
|
||||||
|
expect(cipher.type).toBe(CipherType.SecureNote);
|
||||||
|
expect(cipher.name).toBe("01");
|
||||||
|
expect(cipher.notes).toBe("test");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should parse personal information records (multiple identities)", async () => {
|
||||||
|
const result = await importer.parse(multiplePersonalInfoData);
|
||||||
|
|
||||||
|
expect(result).not.toBeNull();
|
||||||
|
expect(result.success).toBe(true);
|
||||||
|
expect(result.ciphers.length).toBe(6);
|
||||||
|
|
||||||
|
// name
|
||||||
|
const cipher = result.ciphers.shift();
|
||||||
|
expect(cipher.type).toBe(CipherType.SecureNote);
|
||||||
|
expect(cipher.name).toBe("MR John Doe");
|
||||||
|
|
||||||
|
expect(cipher.fields.length).toBe(7);
|
||||||
|
expect(cipher.fields[0].name).toEqual("type");
|
||||||
|
expect(cipher.fields[0].value).toEqual("name");
|
||||||
|
expect(cipher.fields[1].name).toEqual("title");
|
||||||
|
expect(cipher.fields[1].value).toEqual("MR");
|
||||||
|
expect(cipher.fields[2].name).toEqual("first_name");
|
||||||
|
expect(cipher.fields[2].value).toEqual("John");
|
||||||
|
expect(cipher.fields[3].name).toEqual("last_name");
|
||||||
|
expect(cipher.fields[3].value).toEqual("Doe");
|
||||||
|
expect(cipher.fields[4].name).toEqual("login");
|
||||||
|
expect(cipher.fields[4].value).toEqual("jdoe");
|
||||||
|
expect(cipher.fields[5].name).toEqual("date_of_birth");
|
||||||
|
expect(cipher.fields[5].value).toEqual("2022-01-30");
|
||||||
|
expect(cipher.fields[6].name).toEqual("place_of_birth");
|
||||||
|
expect(cipher.fields[6].value).toEqual("world");
|
||||||
|
|
||||||
|
// email
|
||||||
|
const cipher2 = result.ciphers.shift();
|
||||||
|
expect(cipher2.type).toBe(CipherType.SecureNote);
|
||||||
|
expect(cipher2.name).toBe("Johns email");
|
||||||
|
|
||||||
|
expect(cipher2.fields.length).toBe(4);
|
||||||
|
expect(cipher2.fields[0].name).toEqual("type");
|
||||||
|
expect(cipher2.fields[0].value).toEqual("email");
|
||||||
|
expect(cipher2.fields[1].name).toEqual("email");
|
||||||
|
expect(cipher2.fields[1].value).toEqual("jdoe@example.com");
|
||||||
|
expect(cipher2.fields[2].name).toEqual("email_type");
|
||||||
|
expect(cipher2.fields[2].value).toEqual("personal");
|
||||||
|
expect(cipher2.fields[3].name).toEqual("item_name");
|
||||||
|
expect(cipher2.fields[3].value).toEqual("Johns email");
|
||||||
|
|
||||||
|
// number
|
||||||
|
const cipher3 = result.ciphers.shift();
|
||||||
|
expect(cipher3.type).toBe(CipherType.SecureNote);
|
||||||
|
expect(cipher3.name).toBe("John's number");
|
||||||
|
|
||||||
|
expect(cipher3.fields.length).toBe(3);
|
||||||
|
expect(cipher3.fields[0].name).toEqual("type");
|
||||||
|
expect(cipher3.fields[0].value).toEqual("number");
|
||||||
|
expect(cipher3.fields[1].name).toEqual("item_name");
|
||||||
|
expect(cipher3.fields[1].value).toEqual("John's number");
|
||||||
|
expect(cipher3.fields[2].name).toEqual("phone_number");
|
||||||
|
expect(cipher3.fields[2].value).toEqual("+49123123123");
|
||||||
|
|
||||||
|
// address
|
||||||
|
const cipher4 = result.ciphers.shift();
|
||||||
|
expect(cipher4.type).toBe(CipherType.SecureNote);
|
||||||
|
expect(cipher4.name).toBe("John's home address");
|
||||||
|
|
||||||
|
expect(cipher4.fields.length).toBe(12);
|
||||||
|
expect(cipher4.fields[0].name).toEqual("type");
|
||||||
|
expect(cipher4.fields[0].value).toEqual("address");
|
||||||
|
expect(cipher4.fields[1].name).toEqual("item_name");
|
||||||
|
expect(cipher4.fields[1].value).toEqual("John's home address");
|
||||||
|
expect(cipher4.fields[2].name).toEqual("address");
|
||||||
|
expect(cipher4.fields[2].value).toEqual("1 some street");
|
||||||
|
expect(cipher4.fields[3].name).toEqual("country");
|
||||||
|
expect(cipher4.fields[3].value).toEqual("de");
|
||||||
|
expect(cipher4.fields[4].name).toEqual("state");
|
||||||
|
expect(cipher4.fields[4].value).toEqual("DE-0-NW");
|
||||||
|
expect(cipher4.fields[5].name).toEqual("city");
|
||||||
|
expect(cipher4.fields[5].value).toEqual("some city");
|
||||||
|
expect(cipher4.fields[6].name).toEqual("zip");
|
||||||
|
expect(cipher4.fields[6].value).toEqual("123123");
|
||||||
|
expect(cipher4.fields[7].name).toEqual("address_recipient");
|
||||||
|
expect(cipher4.fields[7].value).toEqual("John");
|
||||||
|
expect(cipher4.fields[8].name).toEqual("address_building");
|
||||||
|
expect(cipher4.fields[8].value).toEqual("1");
|
||||||
|
expect(cipher4.fields[9].name).toEqual("address_apartment");
|
||||||
|
expect(cipher4.fields[9].value).toEqual("1");
|
||||||
|
expect(cipher4.fields[10].name).toEqual("address_floor");
|
||||||
|
expect(cipher4.fields[10].value).toEqual("1");
|
||||||
|
expect(cipher4.fields[11].name).toEqual("address_door_code");
|
||||||
|
expect(cipher4.fields[11].value).toEqual("123");
|
||||||
|
|
||||||
|
// website
|
||||||
|
const cipher5 = result.ciphers.shift();
|
||||||
|
expect(cipher5.type).toBe(CipherType.SecureNote);
|
||||||
|
expect(cipher5.name).toBe("Website");
|
||||||
|
|
||||||
|
expect(cipher5.fields.length).toBe(3);
|
||||||
|
expect(cipher5.fields[0].name).toEqual("type");
|
||||||
|
expect(cipher5.fields[0].value).toEqual("website");
|
||||||
|
expect(cipher5.fields[1].name).toEqual("item_name");
|
||||||
|
expect(cipher5.fields[1].value).toEqual("Website");
|
||||||
|
expect(cipher5.fields[2].name).toEqual("url");
|
||||||
|
expect(cipher5.fields[2].value).toEqual("website.com");
|
||||||
|
|
||||||
|
// 2nd name/identity
|
||||||
|
const cipher6 = result.ciphers.shift();
|
||||||
|
expect(cipher6.type).toBe(CipherType.SecureNote);
|
||||||
|
expect(cipher6.name).toBe("Mrs Jane Doe");
|
||||||
|
|
||||||
|
expect(cipher6.fields.length).toBe(7);
|
||||||
|
expect(cipher6.fields[0].name).toEqual("type");
|
||||||
|
expect(cipher6.fields[0].value).toEqual("name");
|
||||||
|
expect(cipher6.fields[1].name).toEqual("title");
|
||||||
|
expect(cipher6.fields[1].value).toEqual("Mrs");
|
||||||
|
expect(cipher6.fields[2].name).toEqual("first_name");
|
||||||
|
expect(cipher6.fields[2].value).toEqual("Jane");
|
||||||
|
expect(cipher6.fields[3].name).toEqual("last_name");
|
||||||
|
expect(cipher6.fields[3].value).toEqual("Doe");
|
||||||
|
expect(cipher6.fields[4].name).toEqual("login");
|
||||||
|
expect(cipher6.fields[4].value).toEqual("jdoe");
|
||||||
|
expect(cipher6.fields[5].name).toEqual("date_of_birth");
|
||||||
|
expect(cipher6.fields[5].value).toEqual("2022-01-30");
|
||||||
|
expect(cipher6.fields[6].name).toEqual("place_of_birth");
|
||||||
|
expect(cipher6.fields[6].value).toEqual("earth");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should combine personal information records to one identity if only one identity present", async () => {
|
||||||
|
const result = await importer.parse(personalInfoData);
|
||||||
|
|
||||||
|
expect(result).not.toBeNull();
|
||||||
|
expect(result.success).toBe(true);
|
||||||
|
|
||||||
|
const cipher = result.ciphers.shift();
|
||||||
|
expect(cipher.type).toBe(CipherType.Identity);
|
||||||
|
expect(cipher.name).toBe("MR John Doe");
|
||||||
|
expect(cipher.identity.fullName).toBe("MR John Doe");
|
||||||
|
expect(cipher.identity.title).toBe("MR");
|
||||||
|
expect(cipher.identity.firstName).toBe("John");
|
||||||
|
expect(cipher.identity.middleName).toBeNull();
|
||||||
|
expect(cipher.identity.lastName).toBe("Doe");
|
||||||
|
expect(cipher.identity.username).toBe("jdoe");
|
||||||
|
expect(cipher.identity.email).toBe("jdoe@example.com");
|
||||||
|
expect(cipher.identity.phone).toBe("+49123123123");
|
||||||
|
|
||||||
|
expect(cipher.fields.length).toBe(9);
|
||||||
|
expect(cipher.fields[0].name).toBe("date_of_birth");
|
||||||
|
expect(cipher.fields[0].value).toBe("2022-01-30");
|
||||||
|
|
||||||
|
expect(cipher.fields[1].name).toBe("place_of_birth");
|
||||||
|
expect(cipher.fields[1].value).toBe("world");
|
||||||
|
|
||||||
|
expect(cipher.fields[2].name).toBe("email_type");
|
||||||
|
expect(cipher.fields[2].value).toBe("personal");
|
||||||
|
|
||||||
|
expect(cipher.fields[3].name).toBe("address_recipient");
|
||||||
|
expect(cipher.fields[3].value).toBe("John");
|
||||||
|
|
||||||
|
expect(cipher.fields[4].name).toBe("address_building");
|
||||||
|
expect(cipher.fields[4].value).toBe("1");
|
||||||
|
|
||||||
|
expect(cipher.fields[5].name).toBe("address_apartment");
|
||||||
|
expect(cipher.fields[5].value).toBe("1");
|
||||||
|
|
||||||
|
expect(cipher.fields[6].name).toBe("address_floor");
|
||||||
|
expect(cipher.fields[6].value).toBe("1");
|
||||||
|
|
||||||
|
expect(cipher.fields[7].name).toBe("address_door_code");
|
||||||
|
expect(cipher.fields[7].value).toBe("123");
|
||||||
|
|
||||||
|
expect(cipher.fields[8].name).toBe("url");
|
||||||
|
expect(cipher.fields[8].value).toBe("website.com");
|
||||||
|
});
|
||||||
|
});
|
||||||
74
jslib/common/spec/importers/firefoxCsvImporter.spec.ts
Normal file
74
jslib/common/spec/importers/firefoxCsvImporter.spec.ts
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
import { FirefoxCsvImporter as Importer } from "jslib-common/importers/firefoxCsvImporter";
|
||||||
|
import { CipherView } from "jslib-common/models/view/cipherView";
|
||||||
|
import { LoginUriView } from "jslib-common/models/view/loginUriView";
|
||||||
|
import { LoginView } from "jslib-common/models/view/loginView";
|
||||||
|
|
||||||
|
import { data as firefoxAccountsData } from "./testData/firefoxCsv/firefoxAccountsData.csv";
|
||||||
|
import { data as simplePasswordData } from "./testData/firefoxCsv/simplePasswordData.csv";
|
||||||
|
|
||||||
|
const CipherData = [
|
||||||
|
{
|
||||||
|
title: "should parse password",
|
||||||
|
csv: simplePasswordData,
|
||||||
|
expected: Object.assign(new CipherView(), {
|
||||||
|
id: null,
|
||||||
|
organizationId: null,
|
||||||
|
folderId: null,
|
||||||
|
name: "example.com",
|
||||||
|
login: Object.assign(new LoginView(), {
|
||||||
|
username: "foo",
|
||||||
|
password: "bar",
|
||||||
|
uris: [
|
||||||
|
Object.assign(new LoginUriView(), {
|
||||||
|
uri: "https://example.com",
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
notes: null,
|
||||||
|
type: 1,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'should skip "chrome://FirefoxAccounts"',
|
||||||
|
csv: firefoxAccountsData,
|
||||||
|
expected: Object.assign(new CipherView(), {
|
||||||
|
id: null,
|
||||||
|
organizationId: null,
|
||||||
|
folderId: null,
|
||||||
|
name: "example.com",
|
||||||
|
login: Object.assign(new LoginView(), {
|
||||||
|
username: "foo",
|
||||||
|
password: "bar",
|
||||||
|
uris: [
|
||||||
|
Object.assign(new LoginUriView(), {
|
||||||
|
uri: "https://example.com",
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
notes: null,
|
||||||
|
type: 1,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
describe("Firefox CSV Importer", () => {
|
||||||
|
CipherData.forEach((data) => {
|
||||||
|
it(data.title, async () => {
|
||||||
|
const importer = new Importer();
|
||||||
|
const result = await importer.parse(data.csv);
|
||||||
|
expect(result != null).toBe(true);
|
||||||
|
expect(result.ciphers.length).toBeGreaterThan(0);
|
||||||
|
|
||||||
|
const cipher = result.ciphers.shift();
|
||||||
|
let property: keyof typeof data.expected;
|
||||||
|
for (property in data.expected) {
|
||||||
|
// eslint-disable-next-line
|
||||||
|
if (data.expected.hasOwnProperty(property)) {
|
||||||
|
// eslint-disable-next-line
|
||||||
|
expect(cipher.hasOwnProperty(property)).toBe(true);
|
||||||
|
expect(cipher[property]).toEqual(data.expected[property]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
77
jslib/common/spec/importers/fsecureFskImporter.spec.ts
Normal file
77
jslib/common/spec/importers/fsecureFskImporter.spec.ts
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
import { FSecureFskImporter as Importer } from "jslib-common/importers/fsecureFskImporter";
|
||||||
|
|
||||||
|
const TestDataWithStyleSetToWebsite: string = JSON.stringify({
|
||||||
|
data: {
|
||||||
|
"8d58b5cf252dd06fbd98f5289e918ab1": {
|
||||||
|
color: "#00baff",
|
||||||
|
reatedDate: 1609302913,
|
||||||
|
creditCvv: "",
|
||||||
|
creditExpiry: "",
|
||||||
|
creditNumber: "",
|
||||||
|
favorite: 0,
|
||||||
|
modifiedDate: 1609302913,
|
||||||
|
notes: "note",
|
||||||
|
password: "word",
|
||||||
|
passwordList: [],
|
||||||
|
passwordModifiedDate: 1609302913,
|
||||||
|
rev: 1,
|
||||||
|
service: "My first pass",
|
||||||
|
style: "website",
|
||||||
|
type: 1,
|
||||||
|
url: "https://bitwarden.com",
|
||||||
|
username: "pass",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const TestDataWithStyleSetToGlobe: string = JSON.stringify({
|
||||||
|
data: {
|
||||||
|
"8d58b5cf252dd06fbd98f5289e918ab1": {
|
||||||
|
color: "#00baff",
|
||||||
|
reatedDate: 1609302913,
|
||||||
|
creditCvv: "",
|
||||||
|
creditExpiry: "",
|
||||||
|
creditNumber: "",
|
||||||
|
favorite: 0,
|
||||||
|
modifiedDate: 1609302913,
|
||||||
|
notes: "note",
|
||||||
|
password: "word",
|
||||||
|
passwordList: [],
|
||||||
|
passwordModifiedDate: 1609302913,
|
||||||
|
rev: 1,
|
||||||
|
service: "My first pass",
|
||||||
|
style: "globe",
|
||||||
|
type: 1,
|
||||||
|
url: "https://bitwarden.com",
|
||||||
|
username: "pass",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("FSecure FSK Importer", () => {
|
||||||
|
it("should parse data with style set to website", async () => {
|
||||||
|
const importer = new Importer();
|
||||||
|
const result = await importer.parse(TestDataWithStyleSetToWebsite);
|
||||||
|
expect(result != null).toBe(true);
|
||||||
|
|
||||||
|
const cipher = result.ciphers.shift();
|
||||||
|
expect(cipher.login.username).toEqual("pass");
|
||||||
|
expect(cipher.login.password).toEqual("word");
|
||||||
|
expect(cipher.login.uris.length).toEqual(1);
|
||||||
|
const uriView = cipher.login.uris.shift();
|
||||||
|
expect(uriView.uri).toEqual("https://bitwarden.com");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should parse data with style set to globe", async () => {
|
||||||
|
const importer = new Importer();
|
||||||
|
const result = await importer.parse(TestDataWithStyleSetToGlobe);
|
||||||
|
expect(result != null).toBe(true);
|
||||||
|
|
||||||
|
const cipher = result.ciphers.shift();
|
||||||
|
expect(cipher.login.username).toEqual("pass");
|
||||||
|
expect(cipher.login.password).toEqual("word");
|
||||||
|
expect(cipher.login.uris.length).toEqual(1);
|
||||||
|
const uriView = cipher.login.uris.shift();
|
||||||
|
expect(uriView.uri).toEqual("https://bitwarden.com");
|
||||||
|
});
|
||||||
|
});
|
||||||
189
jslib/common/spec/importers/keepass2XmlImporter.spec.ts
Normal file
189
jslib/common/spec/importers/keepass2XmlImporter.spec.ts
Normal file
@@ -0,0 +1,189 @@
|
|||||||
|
import { KeePass2XmlImporter as Importer } from "jslib-common/importers/keepass2XmlImporter";
|
||||||
|
|
||||||
|
const TestData = `<?xml version="1.0" encoding="utf-8" standalone="yes"?>
|
||||||
|
<KeePassFile>
|
||||||
|
<Meta>
|
||||||
|
<Generator>KeePass</Generator>
|
||||||
|
<DatabaseName />
|
||||||
|
<DatabaseNameChanged>2016-12-31T21:33:52Z</DatabaseNameChanged>
|
||||||
|
<DatabaseDescription />
|
||||||
|
<DatabaseDescriptionChanged>2016-12-31T21:33:52Z</DatabaseDescriptionChanged>
|
||||||
|
<DefaultUserName />
|
||||||
|
<DefaultUserNameChanged>2016-12-31T21:33:52Z</DefaultUserNameChanged>
|
||||||
|
<MaintenanceHistoryDays>365</MaintenanceHistoryDays>
|
||||||
|
<Color />
|
||||||
|
<MasterKeyChanged>2016-12-31T21:33:59Z</MasterKeyChanged>
|
||||||
|
<MasterKeyChangeRec>-1</MasterKeyChangeRec>
|
||||||
|
<MasterKeyChangeForce>-1</MasterKeyChangeForce>
|
||||||
|
<MemoryProtection>
|
||||||
|
<ProtectTitle>False</ProtectTitle>
|
||||||
|
<ProtectUserName>False</ProtectUserName>
|
||||||
|
<ProtectPassword>True</ProtectPassword>
|
||||||
|
<ProtectURL>False</ProtectURL>
|
||||||
|
<ProtectNotes>False</ProtectNotes>
|
||||||
|
</MemoryProtection>
|
||||||
|
<RecycleBinEnabled>True</RecycleBinEnabled>
|
||||||
|
<RecycleBinUUID>AAAAAAAAAAAAAAAAAAAAAA==</RecycleBinUUID>
|
||||||
|
<RecycleBinChanged>2016-12-31T21:33:52Z</RecycleBinChanged>
|
||||||
|
<EntryTemplatesGroup>AAAAAAAAAAAAAAAAAAAAAA==</EntryTemplatesGroup>
|
||||||
|
<EntryTemplatesGroupChanged>2016-12-31T21:33:52Z</EntryTemplatesGroupChanged>
|
||||||
|
<HistoryMaxItems>10</HistoryMaxItems>
|
||||||
|
<HistoryMaxSize>6291456</HistoryMaxSize>
|
||||||
|
<LastSelectedGroup>AAAAAAAAAAAAAAAAAAAAAA==</LastSelectedGroup>
|
||||||
|
<LastTopVisibleGroup>AAAAAAAAAAAAAAAAAAAAAA==</LastTopVisibleGroup>
|
||||||
|
<Binaries />
|
||||||
|
<CustomData />
|
||||||
|
</Meta>
|
||||||
|
<Root>
|
||||||
|
<Group>
|
||||||
|
<UUID>KvS57lVwl13AfGFLwkvq4Q==</UUID>
|
||||||
|
<Name>Root</Name>
|
||||||
|
<Notes />
|
||||||
|
<IconID>48</IconID>
|
||||||
|
<Times>
|
||||||
|
<CreationTime>2016-12-31T21:33:52Z</CreationTime>
|
||||||
|
<LastModificationTime>2016-12-31T21:33:52Z</LastModificationTime>
|
||||||
|
<LastAccessTime>2017-01-01T22:58:00Z</LastAccessTime>
|
||||||
|
<ExpiryTime>2016-12-31T21:33:52Z</ExpiryTime>
|
||||||
|
<Expires>False</Expires>
|
||||||
|
<UsageCount>1</UsageCount>
|
||||||
|
<LocationChanged>2016-12-31T21:33:52Z</LocationChanged>
|
||||||
|
</Times>
|
||||||
|
<IsExpanded>True</IsExpanded>
|
||||||
|
<DefaultAutoTypeSequence />
|
||||||
|
<EnableAutoType>null</EnableAutoType>
|
||||||
|
<EnableSearching>null</EnableSearching>
|
||||||
|
<LastTopVisibleEntry>AAAAAAAAAAAAAAAAAAAAAA==</LastTopVisibleEntry>
|
||||||
|
<Group>
|
||||||
|
<UUID>P0ParXgGMBW6caOL2YrhqQ==</UUID>
|
||||||
|
<Name>Folder2</Name>
|
||||||
|
<Notes>a note about the folder</Notes>
|
||||||
|
<IconID>48</IconID>
|
||||||
|
<Times>
|
||||||
|
<CreationTime>2016-12-31T21:43:30Z</CreationTime>
|
||||||
|
<LastModificationTime>2016-12-31T21:43:43Z</LastModificationTime>
|
||||||
|
<LastAccessTime>2017-01-01T22:58:00Z</LastAccessTime>
|
||||||
|
<ExpiryTime>2016-12-31T21:43:30Z</ExpiryTime>
|
||||||
|
<Expires>False</Expires>
|
||||||
|
<UsageCount>1</UsageCount>
|
||||||
|
<LocationChanged>2016-12-31T21:43:43Z</LocationChanged>
|
||||||
|
</Times>
|
||||||
|
<IsExpanded>True</IsExpanded>
|
||||||
|
<DefaultAutoTypeSequence />
|
||||||
|
<EnableAutoType>null</EnableAutoType>
|
||||||
|
<EnableSearching>null</EnableSearching>
|
||||||
|
<LastTopVisibleEntry>AAAAAAAAAAAAAAAAAAAAAA==</LastTopVisibleEntry>
|
||||||
|
<Entry>
|
||||||
|
<UUID>fAa543oYlgnJKkhKag5HLw==</UUID>
|
||||||
|
<IconID>1</IconID>
|
||||||
|
<ForegroundColor />
|
||||||
|
<BackgroundColor />
|
||||||
|
<OverrideURL />
|
||||||
|
<Tags />
|
||||||
|
<Times>
|
||||||
|
<CreationTime>2016-12-31T21:34:13Z</CreationTime>
|
||||||
|
<LastModificationTime>2016-12-31T21:40:23Z</LastModificationTime>
|
||||||
|
<LastAccessTime>2016-12-31T21:40:23Z</LastAccessTime>
|
||||||
|
<ExpiryTime>2016-12-31T21:34:13Z</ExpiryTime>
|
||||||
|
<Expires>False</Expires>
|
||||||
|
<UsageCount>0</UsageCount>
|
||||||
|
<LocationChanged>2016-12-31T21:43:48Z</LocationChanged>
|
||||||
|
</Times>
|
||||||
|
<String>
|
||||||
|
<Key>att2</Key>
|
||||||
|
<Value>att2value</Value>
|
||||||
|
</String>
|
||||||
|
<String>
|
||||||
|
<Key>attr1</Key>
|
||||||
|
<Value>att1value
|
||||||
|
|
||||||
|
line1
|
||||||
|
line2</Value>
|
||||||
|
</String>
|
||||||
|
<String>
|
||||||
|
<Key>Notes</Key>
|
||||||
|
<Value>This is a note!!!
|
||||||
|
|
||||||
|
line1
|
||||||
|
line2</Value>
|
||||||
|
</String>
|
||||||
|
<String>
|
||||||
|
<Key>Password</Key>
|
||||||
|
<Value ProtectInMemory="True">googpass</Value>
|
||||||
|
</String>
|
||||||
|
<String>
|
||||||
|
<Key>Title</Key>
|
||||||
|
<Value>Google</Value>
|
||||||
|
</String>
|
||||||
|
<String>
|
||||||
|
<Key>URL</Key>
|
||||||
|
<Value>google.com</Value>
|
||||||
|
</String>
|
||||||
|
<String>
|
||||||
|
<Key>UserName</Key>
|
||||||
|
<Value>googleuser</Value>
|
||||||
|
</String>
|
||||||
|
<AutoType>
|
||||||
|
<Enabled>True</Enabled>
|
||||||
|
<DataTransferObfuscation>0</DataTransferObfuscation>
|
||||||
|
</AutoType>
|
||||||
|
<History>
|
||||||
|
<Entry>
|
||||||
|
<UUID>fAa543oYlgnJKkhKag5HLw==</UUID>
|
||||||
|
<IconID>0</IconID>
|
||||||
|
<ForegroundColor />
|
||||||
|
<BackgroundColor />
|
||||||
|
<OverrideURL />
|
||||||
|
<Tags />
|
||||||
|
<Times>
|
||||||
|
<CreationTime>2016-12-31T21:34:13Z</CreationTime>
|
||||||
|
<LastModificationTime>2016-12-31T21:34:40Z</LastModificationTime>
|
||||||
|
<LastAccessTime>2016-12-31T21:34:40Z</LastAccessTime>
|
||||||
|
<ExpiryTime>2016-12-31T21:34:13Z</ExpiryTime>
|
||||||
|
<Expires>False</Expires>
|
||||||
|
<UsageCount>0</UsageCount>
|
||||||
|
<LocationChanged>2016-12-31T21:34:40Z</LocationChanged>
|
||||||
|
</Times>
|
||||||
|
<String>
|
||||||
|
<Key>Notes</Key>
|
||||||
|
<Value>This is a note!!!
|
||||||
|
|
||||||
|
line1
|
||||||
|
line2</Value>
|
||||||
|
</String>
|
||||||
|
<String>
|
||||||
|
<Key>Password</Key>
|
||||||
|
<Value ProtectInMemory="True">googpass</Value>
|
||||||
|
</String>
|
||||||
|
<String>
|
||||||
|
<Key>Title</Key>
|
||||||
|
<Value>Google</Value>
|
||||||
|
</String>
|
||||||
|
<String>
|
||||||
|
<Key>URL</Key>
|
||||||
|
<Value>google.com</Value>
|
||||||
|
</String>
|
||||||
|
<String>
|
||||||
|
<Key>UserName</Key>
|
||||||
|
<Value>googleuser</Value>
|
||||||
|
</String>
|
||||||
|
<AutoType>
|
||||||
|
<Enabled>True</Enabled>
|
||||||
|
<DataTransferObfuscation>0</DataTransferObfuscation>
|
||||||
|
</AutoType>
|
||||||
|
</Entry>
|
||||||
|
</History>
|
||||||
|
</Entry>
|
||||||
|
</Group>
|
||||||
|
</Group>
|
||||||
|
<DeletedObjects />
|
||||||
|
</Root>
|
||||||
|
</KeePassFile>`;
|
||||||
|
|
||||||
|
describe("KeePass2 Xml Importer", () => {
|
||||||
|
it("should parse XML data", async () => {
|
||||||
|
const importer = new Importer();
|
||||||
|
const result = await importer.parse(TestData);
|
||||||
|
expect(result != null).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
108
jslib/common/spec/importers/keeperJsonImporter.spec.ts
Normal file
108
jslib/common/spec/importers/keeperJsonImporter.spec.ts
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
import { KeeperJsonImporter as Importer } from "jslib-common/importers/keeperImporters/keeperJsonImporter";
|
||||||
|
import { Utils } from "jslib-common/misc/utils";
|
||||||
|
|
||||||
|
import { testData as TestData } from "./testData/keeperJson/testData";
|
||||||
|
|
||||||
|
describe("Keeper Json Importer", () => {
|
||||||
|
const testDataJson = JSON.stringify(TestData);
|
||||||
|
|
||||||
|
let importer: Importer;
|
||||||
|
beforeEach(() => {
|
||||||
|
importer = new Importer();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should parse login data", async () => {
|
||||||
|
const result = await importer.parse(testDataJson);
|
||||||
|
expect(result != null).toBe(true);
|
||||||
|
|
||||||
|
const cipher = result.ciphers.shift();
|
||||||
|
expect(cipher.name).toEqual("Bank Account 1");
|
||||||
|
expect(cipher.login.username).toEqual("customer1234");
|
||||||
|
expect(cipher.login.password).toEqual("4813fJDHF4239fdk");
|
||||||
|
expect(cipher.login.uris.length).toEqual(1);
|
||||||
|
const uriView = cipher.login.uris.shift();
|
||||||
|
expect(uriView.uri).toEqual("https://chase.com");
|
||||||
|
expect(cipher.notes).toEqual("These are some notes.");
|
||||||
|
|
||||||
|
const cipher2 = result.ciphers.shift();
|
||||||
|
expect(cipher2.name).toEqual("Bank Account 2");
|
||||||
|
expect(cipher2.login.username).toEqual("mybankusername");
|
||||||
|
expect(cipher2.login.password).toEqual("w4k4k193f$^&@#*%2");
|
||||||
|
expect(cipher2.login.uris.length).toEqual(1);
|
||||||
|
const uriView2 = cipher2.login.uris.shift();
|
||||||
|
expect(uriView2.uri).toEqual("https://amex.com");
|
||||||
|
expect(cipher2.notes).toEqual("Some great information here.");
|
||||||
|
|
||||||
|
const cipher3 = result.ciphers.shift();
|
||||||
|
expect(cipher3.name).toEqual("Some Account");
|
||||||
|
expect(cipher3.login.username).toEqual("someUserName");
|
||||||
|
expect(cipher3.login.password).toEqual("w4k4k1wergf$^&@#*%2");
|
||||||
|
expect(cipher3.notes).toBeNull();
|
||||||
|
expect(cipher3.fields).toBeNull();
|
||||||
|
expect(cipher3.login.uris.length).toEqual(1);
|
||||||
|
const uriView3 = cipher3.login.uris.shift();
|
||||||
|
expect(uriView3.uri).toEqual("https://example.com");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should import TOTP when present", async () => {
|
||||||
|
const result = await importer.parse(testDataJson);
|
||||||
|
expect(result != null).toBe(true);
|
||||||
|
|
||||||
|
const cipher = result.ciphers.shift();
|
||||||
|
expect(cipher.login.totp).toBeNull();
|
||||||
|
|
||||||
|
// 2nd Cipher
|
||||||
|
const cipher2 = result.ciphers.shift();
|
||||||
|
expect(cipher2.login.totp).toEqual(
|
||||||
|
"otpauth://totp/Amazon:me@company.com?secret=JBSWY3DPEHPK3PXP&issuer=Amazon&algorithm=SHA1&digits=6&period=30"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should parse custom fields", async () => {
|
||||||
|
const result = await importer.parse(testDataJson);
|
||||||
|
expect(result != null).toBe(true);
|
||||||
|
|
||||||
|
const cipher = result.ciphers.shift();
|
||||||
|
expect(cipher.fields.length).toBe(1);
|
||||||
|
expect(cipher.fields[0].name).toEqual("Account Number");
|
||||||
|
expect(cipher.fields[0].value).toEqual("123-456-789");
|
||||||
|
|
||||||
|
// 2nd Cipher
|
||||||
|
const cipher2 = result.ciphers.shift();
|
||||||
|
expect(cipher2.fields.length).toBe(2);
|
||||||
|
expect(cipher2.fields[0].name).toEqual("Security Group");
|
||||||
|
expect(cipher2.fields[0].value).toEqual("Public");
|
||||||
|
|
||||||
|
expect(cipher2.fields[1].name).toEqual("IP Address");
|
||||||
|
expect(cipher2.fields[1].value).toEqual("12.45.67.8");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should create folders and assigned ciphers to them", async () => {
|
||||||
|
const result = await importer.parse(testDataJson);
|
||||||
|
expect(result != null).toBe(true);
|
||||||
|
|
||||||
|
const folders = result.folders;
|
||||||
|
expect(folders.length).toBe(2);
|
||||||
|
expect(folders[0].name).toBe("Optional Private Folder 1");
|
||||||
|
expect(folders[1].name).toBe("My Customer 1");
|
||||||
|
|
||||||
|
expect(result.folderRelationships[0]).toEqual([0, 0]);
|
||||||
|
expect(result.folderRelationships[1]).toEqual([1, 0]);
|
||||||
|
expect(result.folderRelationships[2]).toEqual([1, 1]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should create collections if part of an organization", async () => {
|
||||||
|
importer.organizationId = Utils.newGuid();
|
||||||
|
const result = await importer.parse(testDataJson);
|
||||||
|
expect(result != null).toBe(true);
|
||||||
|
|
||||||
|
const collections = result.collections;
|
||||||
|
expect(collections.length).toBe(2);
|
||||||
|
expect(collections[0].name).toBe("Optional Private Folder 1");
|
||||||
|
expect(collections[1].name).toBe("My Customer 1");
|
||||||
|
|
||||||
|
expect(result.collectionRelationships[0]).toEqual([0, 0]);
|
||||||
|
expect(result.collectionRelationships[1]).toEqual([1, 0]);
|
||||||
|
expect(result.collectionRelationships[2]).toEqual([1, 1]);
|
||||||
|
});
|
||||||
|
});
|
||||||
202
jslib/common/spec/importers/lastpassCsvImporter.spec.ts
Normal file
202
jslib/common/spec/importers/lastpassCsvImporter.spec.ts
Normal file
@@ -0,0 +1,202 @@
|
|||||||
|
import { CipherType } from "jslib-common/enums/cipherType";
|
||||||
|
import { FieldType } from "jslib-common/enums/fieldType";
|
||||||
|
import { LastPassCsvImporter as Importer } from "jslib-common/importers/lastpassCsvImporter";
|
||||||
|
import { ImportResult } from "jslib-common/models/domain/importResult";
|
||||||
|
import { CipherView } from "jslib-common/models/view/cipherView";
|
||||||
|
import { FieldView } from "jslib-common/models/view/fieldView";
|
||||||
|
|
||||||
|
function baseExcept(result: ImportResult) {
|
||||||
|
expect(result).not.toBeNull();
|
||||||
|
expect(result.success).toBe(true);
|
||||||
|
expect(result.ciphers.length).toBe(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
function expectLogin(cipher: CipherView) {
|
||||||
|
expect(cipher.type).toBe(CipherType.Login);
|
||||||
|
|
||||||
|
expect(cipher.name).toBe("example.com");
|
||||||
|
expect(cipher.notes).toBe("super secure notes");
|
||||||
|
expect(cipher.login.uri).toBe("http://example.com");
|
||||||
|
expect(cipher.login.username).toBe("someUser");
|
||||||
|
expect(cipher.login.password).toBe("myPassword");
|
||||||
|
expect(cipher.login.totp).toBe("Y64VEVMBTSXCYIWRSHRNDZW62MPGVU2G");
|
||||||
|
}
|
||||||
|
|
||||||
|
const CipherData = [
|
||||||
|
{
|
||||||
|
title: "should parse expiration date",
|
||||||
|
csv: `url,username,password,extra,name,grouping,fav
|
||||||
|
http://sn,,,"NoteType:Credit Card
|
||||||
|
Name on Card:John Doe
|
||||||
|
Type:
|
||||||
|
Number:1234567812345678
|
||||||
|
Security Code:123
|
||||||
|
Start Date:October,2017
|
||||||
|
Expiration Date:June,2020
|
||||||
|
Notes:some text
|
||||||
|
",Credit-card,,0`,
|
||||||
|
expected: Object.assign(new CipherView(), {
|
||||||
|
id: null,
|
||||||
|
organizationId: null,
|
||||||
|
folderId: null,
|
||||||
|
name: "Credit-card",
|
||||||
|
notes: "some text\n",
|
||||||
|
type: 3,
|
||||||
|
card: {
|
||||||
|
cardholderName: "John Doe",
|
||||||
|
number: "1234567812345678",
|
||||||
|
code: "123",
|
||||||
|
expYear: "2020",
|
||||||
|
expMonth: "6",
|
||||||
|
},
|
||||||
|
fields: [
|
||||||
|
Object.assign(new FieldView(), {
|
||||||
|
name: "Start Date",
|
||||||
|
value: "October,2017",
|
||||||
|
type: FieldType.Text,
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "should parse blank card note",
|
||||||
|
csv: `url,username,password,extra,name,grouping,fav
|
||||||
|
http://sn,,,"NoteType:Credit Card
|
||||||
|
Name on Card:
|
||||||
|
Type:
|
||||||
|
Number:
|
||||||
|
Security Code:
|
||||||
|
Start Date:,
|
||||||
|
Expiration Date:,
|
||||||
|
Notes:",empty,,0`,
|
||||||
|
expected: Object.assign(new CipherView(), {
|
||||||
|
id: null,
|
||||||
|
organizationId: null,
|
||||||
|
folderId: null,
|
||||||
|
name: "empty",
|
||||||
|
notes: null,
|
||||||
|
type: 3,
|
||||||
|
card: {
|
||||||
|
expMonth: undefined,
|
||||||
|
},
|
||||||
|
fields: [
|
||||||
|
Object.assign(new FieldView(), {
|
||||||
|
name: "Start Date",
|
||||||
|
value: ",",
|
||||||
|
type: FieldType.Text,
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "should parse card expiration date w/ no exp year",
|
||||||
|
csv: `url,username,password,extra,name,grouping,fav
|
||||||
|
http://sn,,,"NoteType:Credit Card
|
||||||
|
Name on Card:John Doe
|
||||||
|
Type:Visa
|
||||||
|
Number:1234567887654321
|
||||||
|
Security Code:321
|
||||||
|
Start Date:,
|
||||||
|
Expiration Date:January,
|
||||||
|
Notes:",noyear,,0`,
|
||||||
|
expected: Object.assign(new CipherView(), {
|
||||||
|
id: null,
|
||||||
|
organizationId: null,
|
||||||
|
folderId: null,
|
||||||
|
name: "noyear",
|
||||||
|
notes: null,
|
||||||
|
type: 3,
|
||||||
|
card: {
|
||||||
|
cardholderName: "John Doe",
|
||||||
|
number: "1234567887654321",
|
||||||
|
code: "321",
|
||||||
|
expMonth: "1",
|
||||||
|
},
|
||||||
|
fields: [
|
||||||
|
Object.assign(new FieldView(), {
|
||||||
|
name: "Type",
|
||||||
|
value: "Visa",
|
||||||
|
type: FieldType.Text,
|
||||||
|
}),
|
||||||
|
Object.assign(new FieldView(), {
|
||||||
|
name: "Start Date",
|
||||||
|
value: ",",
|
||||||
|
type: FieldType.Text,
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "should parse card expiration date w/ no month",
|
||||||
|
csv: `url,username,password,extra,name,grouping,fav
|
||||||
|
http://sn,,,"NoteType:Credit Card
|
||||||
|
Name on Card:John Doe
|
||||||
|
Type:Mastercard
|
||||||
|
Number:8765432112345678
|
||||||
|
Security Code:987
|
||||||
|
Start Date:,
|
||||||
|
Expiration Date:,2020
|
||||||
|
Notes:",nomonth,,0`,
|
||||||
|
expected: Object.assign(new CipherView(), {
|
||||||
|
id: null,
|
||||||
|
organizationId: null,
|
||||||
|
folderId: null,
|
||||||
|
name: "nomonth",
|
||||||
|
notes: null,
|
||||||
|
type: 3,
|
||||||
|
card: {
|
||||||
|
cardholderName: "John Doe",
|
||||||
|
number: "8765432112345678",
|
||||||
|
code: "987",
|
||||||
|
expYear: "2020",
|
||||||
|
expMonth: undefined,
|
||||||
|
},
|
||||||
|
fields: [
|
||||||
|
Object.assign(new FieldView(), {
|
||||||
|
name: "Type",
|
||||||
|
value: "Mastercard",
|
||||||
|
type: FieldType.Text,
|
||||||
|
}),
|
||||||
|
Object.assign(new FieldView(), {
|
||||||
|
name: "Start Date",
|
||||||
|
value: ",",
|
||||||
|
type: FieldType.Text,
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
describe("Lastpass CSV Importer", () => {
|
||||||
|
CipherData.forEach((data) => {
|
||||||
|
it(data.title, async () => {
|
||||||
|
const importer = new Importer();
|
||||||
|
const result = await importer.parse(data.csv);
|
||||||
|
expect(result != null).toBe(true);
|
||||||
|
expect(result.ciphers.length).toBeGreaterThan(0);
|
||||||
|
|
||||||
|
const cipher = result.ciphers.shift();
|
||||||
|
let property: keyof typeof data.expected;
|
||||||
|
for (property in data.expected) {
|
||||||
|
// eslint-disable-next-line
|
||||||
|
if (data.expected.hasOwnProperty(property)) {
|
||||||
|
// eslint-disable-next-line
|
||||||
|
expect(cipher.hasOwnProperty(property)).toBe(true);
|
||||||
|
expect(cipher[property]).toEqual(data.expected[property]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should parse login with totp", async () => {
|
||||||
|
const input = `url,username,password,totp,extra,name,grouping,fav
|
||||||
|
http://example.com,someUser,myPassword,Y64VEVMBTSXCYIWRSHRNDZW62MPGVU2G,super secure notes,example.com,,0`;
|
||||||
|
|
||||||
|
const importer = new Importer();
|
||||||
|
const result = await importer.parse(input);
|
||||||
|
baseExcept(result);
|
||||||
|
|
||||||
|
const cipher = result.ciphers[0];
|
||||||
|
expectLogin(cipher);
|
||||||
|
});
|
||||||
|
});
|
||||||
633
jslib/common/spec/importers/mykiCsvImporter.spec.ts
Normal file
633
jslib/common/spec/importers/mykiCsvImporter.spec.ts
Normal file
@@ -0,0 +1,633 @@
|
|||||||
|
import { CipherType } from "jslib-common/enums/cipherType";
|
||||||
|
import { MykiCsvImporter as Importer } from "jslib-common/importers/mykiCsvImporter";
|
||||||
|
import { CipherView } from "jslib-common/models/view/cipherView";
|
||||||
|
|
||||||
|
import { userAccountData } from "./testData/mykiCsv/UserAccount.csv";
|
||||||
|
import { userCreditCardData } from "./testData/mykiCsv/UserCreditCard.csv";
|
||||||
|
import { userIdCardData } from "./testData/mykiCsv/UserIdCard.csv";
|
||||||
|
import { userIdentityData } from "./testData/mykiCsv/UserIdentity.csv";
|
||||||
|
import { userNoteData } from "./testData/mykiCsv/UserNote.csv";
|
||||||
|
import { userTwoFaData } from "./testData/mykiCsv/UserTwofa.csv";
|
||||||
|
|
||||||
|
function expectDriversLicense(cipher: CipherView) {
|
||||||
|
expect(cipher.type).toBe(CipherType.Identity);
|
||||||
|
expect(cipher.name).toBe("Joe User's nickname");
|
||||||
|
expect(cipher.notes).toBe("Additional information");
|
||||||
|
|
||||||
|
expect(cipher.identity.fullName).toBe("Joe M User");
|
||||||
|
expect(cipher.identity.firstName).toBe("Joe");
|
||||||
|
expect(cipher.identity.middleName).toBe("M");
|
||||||
|
expect(cipher.identity.lastName).toBe("User");
|
||||||
|
expect(cipher.identity.licenseNumber).toBe("123456");
|
||||||
|
expect(cipher.identity.country).toBe("United States");
|
||||||
|
|
||||||
|
expect(cipher.fields.length).toBe(5);
|
||||||
|
|
||||||
|
expect(cipher.fields[0].name).toEqual("status");
|
||||||
|
expect(cipher.fields[0].value).toEqual("active");
|
||||||
|
|
||||||
|
expect(cipher.fields[1].name).toEqual("tags");
|
||||||
|
expect(cipher.fields[1].value).toEqual("someTag");
|
||||||
|
|
||||||
|
expect(cipher.fields[2].name).toEqual("idType");
|
||||||
|
expect(cipher.fields[2].value).toEqual("Driver's License");
|
||||||
|
|
||||||
|
expect(cipher.fields[3].name).toEqual("idIssuanceDate");
|
||||||
|
expect(cipher.fields[3].value).toEqual("02/02/2022");
|
||||||
|
|
||||||
|
expect(cipher.fields[4].name).toEqual("idExpirationDate");
|
||||||
|
expect(cipher.fields[4].value).toEqual("02/02/2024");
|
||||||
|
}
|
||||||
|
|
||||||
|
function expectPassport(cipher: CipherView) {
|
||||||
|
expect(cipher.type).toBe(CipherType.Identity);
|
||||||
|
expect(cipher.name).toBe("Passport ID card");
|
||||||
|
expect(cipher.notes).toBe("Additional information field");
|
||||||
|
|
||||||
|
expect(cipher.identity.fullName).toBe("Joe M User");
|
||||||
|
expect(cipher.identity.firstName).toBe("Joe");
|
||||||
|
expect(cipher.identity.middleName).toBe("M");
|
||||||
|
expect(cipher.identity.lastName).toBe("User");
|
||||||
|
expect(cipher.identity.passportNumber).toBe("1234567");
|
||||||
|
expect(cipher.identity.country).toBe("United States");
|
||||||
|
|
||||||
|
expect(cipher.fields.length).toBe(5);
|
||||||
|
|
||||||
|
expect(cipher.fields[0].name).toEqual("status");
|
||||||
|
expect(cipher.fields[0].value).toEqual("active");
|
||||||
|
|
||||||
|
expect(cipher.fields[1].name).toEqual("tags");
|
||||||
|
expect(cipher.fields[1].value).toEqual("someTag");
|
||||||
|
|
||||||
|
expect(cipher.fields[2].name).toEqual("idType");
|
||||||
|
expect(cipher.fields[2].value).toEqual("Passport");
|
||||||
|
|
||||||
|
expect(cipher.fields[3].name).toEqual("idIssuanceDate");
|
||||||
|
expect(cipher.fields[3].value).toEqual("03/07/2022");
|
||||||
|
|
||||||
|
expect(cipher.fields[4].name).toEqual("idExpirationDate");
|
||||||
|
expect(cipher.fields[4].value).toEqual("03/07/2028");
|
||||||
|
}
|
||||||
|
|
||||||
|
function expectSocialSecurity(cipher: CipherView) {
|
||||||
|
expect(cipher.type).toBe(CipherType.Identity);
|
||||||
|
expect(cipher.name).toBe("Social Security ID card");
|
||||||
|
expect(cipher.notes).toBe("Additional information field text");
|
||||||
|
|
||||||
|
expect(cipher.identity.fullName).toBe("Joe M User");
|
||||||
|
expect(cipher.identity.firstName).toBe("Joe");
|
||||||
|
expect(cipher.identity.middleName).toBe("M");
|
||||||
|
expect(cipher.identity.lastName).toBe("User");
|
||||||
|
expect(cipher.identity.ssn).toBe("123455678");
|
||||||
|
expect(cipher.identity.country).toBe("United States");
|
||||||
|
|
||||||
|
expect(cipher.fields.length).toBe(5);
|
||||||
|
|
||||||
|
expect(cipher.fields[0].name).toEqual("status");
|
||||||
|
expect(cipher.fields[0].value).toEqual("active");
|
||||||
|
|
||||||
|
expect(cipher.fields[1].name).toEqual("tags");
|
||||||
|
expect(cipher.fields[1].value).toEqual("someTag");
|
||||||
|
|
||||||
|
expect(cipher.fields[2].name).toEqual("idType");
|
||||||
|
expect(cipher.fields[2].value).toEqual("Social Security");
|
||||||
|
|
||||||
|
expect(cipher.fields[3].name).toEqual("idIssuanceDate");
|
||||||
|
expect(cipher.fields[3].value).toEqual("03/07/2022");
|
||||||
|
|
||||||
|
expect(cipher.fields[4].name).toEqual("idExpirationDate");
|
||||||
|
expect(cipher.fields[4].value).toEqual("03/07/2028");
|
||||||
|
}
|
||||||
|
|
||||||
|
function expectIdCard(cipher: CipherView) {
|
||||||
|
expect(cipher.type).toBe(CipherType.Identity);
|
||||||
|
expect(cipher.name).toBe("ID card type ID card");
|
||||||
|
expect(cipher.notes).toBe("Additional Information field text");
|
||||||
|
|
||||||
|
expect(cipher.identity.fullName).toBe("Joe M User");
|
||||||
|
expect(cipher.identity.firstName).toBe("Joe");
|
||||||
|
expect(cipher.identity.middleName).toBe("M");
|
||||||
|
expect(cipher.identity.lastName).toBe("User");
|
||||||
|
expect(cipher.identity.licenseNumber).toBe("1234566");
|
||||||
|
expect(cipher.identity.country).toBe("United States");
|
||||||
|
|
||||||
|
expect(cipher.fields.length).toBe(5);
|
||||||
|
|
||||||
|
expect(cipher.fields[0].name).toEqual("status");
|
||||||
|
expect(cipher.fields[0].value).toEqual("active");
|
||||||
|
|
||||||
|
expect(cipher.fields[1].name).toEqual("tags");
|
||||||
|
expect(cipher.fields[1].value).toEqual("someTag");
|
||||||
|
|
||||||
|
expect(cipher.fields[2].name).toEqual("idType");
|
||||||
|
expect(cipher.fields[2].value).toEqual("ID Card");
|
||||||
|
|
||||||
|
expect(cipher.fields[3].name).toEqual("idIssuanceDate");
|
||||||
|
expect(cipher.fields[3].value).toEqual("03/07/2022");
|
||||||
|
|
||||||
|
expect(cipher.fields[4].name).toEqual("idExpirationDate");
|
||||||
|
expect(cipher.fields[4].value).toEqual("03/07/2028");
|
||||||
|
}
|
||||||
|
|
||||||
|
function expectTaxNumber(cipher: CipherView) {
|
||||||
|
expect(cipher.type).toBe(CipherType.Identity);
|
||||||
|
expect(cipher.name).toBe("Tax number ID card");
|
||||||
|
expect(cipher.notes).toBe("Additinoal information text field");
|
||||||
|
|
||||||
|
expect(cipher.identity.fullName).toBe("Joe M User");
|
||||||
|
expect(cipher.identity.firstName).toBe("Joe");
|
||||||
|
expect(cipher.identity.middleName).toBe("M");
|
||||||
|
expect(cipher.identity.lastName).toBe("User");
|
||||||
|
expect(cipher.identity.licenseNumber).toBe("12345678");
|
||||||
|
expect(cipher.identity.country).toBe("United States");
|
||||||
|
|
||||||
|
expect(cipher.fields.length).toBe(5);
|
||||||
|
|
||||||
|
expect(cipher.fields[0].name).toEqual("status");
|
||||||
|
expect(cipher.fields[0].value).toEqual("active");
|
||||||
|
|
||||||
|
expect(cipher.fields[1].name).toEqual("tags");
|
||||||
|
expect(cipher.fields[1].value).toEqual("someTag");
|
||||||
|
|
||||||
|
expect(cipher.fields[2].name).toEqual("idType");
|
||||||
|
expect(cipher.fields[2].value).toEqual("Tax Number");
|
||||||
|
|
||||||
|
expect(cipher.fields[3].name).toEqual("idIssuanceDate");
|
||||||
|
expect(cipher.fields[3].value).toEqual("03/07/2022");
|
||||||
|
|
||||||
|
expect(cipher.fields[4].name).toEqual("idExpirationDate");
|
||||||
|
expect(cipher.fields[4].value).toEqual("03/07/2028");
|
||||||
|
}
|
||||||
|
|
||||||
|
function expectBankAccount(cipher: CipherView) {
|
||||||
|
expect(cipher.type).toBe(CipherType.Identity);
|
||||||
|
expect(cipher.name).toBe("Bank account ID card");
|
||||||
|
expect(cipher.notes).toBe("Additional text information here");
|
||||||
|
|
||||||
|
expect(cipher.identity.fullName).toBe("Joe M User");
|
||||||
|
expect(cipher.identity.firstName).toBe("Joe");
|
||||||
|
expect(cipher.identity.middleName).toBe("M");
|
||||||
|
expect(cipher.identity.lastName).toBe("User");
|
||||||
|
expect(cipher.identity.licenseNumber).toBe("12344556677");
|
||||||
|
expect(cipher.identity.country).toBe("United States");
|
||||||
|
|
||||||
|
expect(cipher.fields.length).toBe(5);
|
||||||
|
|
||||||
|
expect(cipher.fields[0].name).toEqual("status");
|
||||||
|
expect(cipher.fields[0].value).toEqual("active");
|
||||||
|
|
||||||
|
expect(cipher.fields[1].name).toEqual("tags");
|
||||||
|
expect(cipher.fields[1].value).toEqual("someTag");
|
||||||
|
|
||||||
|
expect(cipher.fields[2].name).toEqual("idType");
|
||||||
|
expect(cipher.fields[2].value).toEqual("Bank Account");
|
||||||
|
|
||||||
|
expect(cipher.fields[3].name).toEqual("idIssuanceDate");
|
||||||
|
expect(cipher.fields[3].value).toEqual("03/07/2022");
|
||||||
|
|
||||||
|
expect(cipher.fields[4].name).toEqual("idExpirationDate");
|
||||||
|
expect(cipher.fields[4].value).toEqual("03/07/2028");
|
||||||
|
}
|
||||||
|
|
||||||
|
function expectInsuranceCard(cipher: CipherView) {
|
||||||
|
expect(cipher.type).toBe(CipherType.Identity);
|
||||||
|
expect(cipher.name).toBe("Insurance card ID card");
|
||||||
|
expect(cipher.notes).toBe("Additional information text goes here");
|
||||||
|
|
||||||
|
expect(cipher.identity.fullName).toBe("Joe M User");
|
||||||
|
expect(cipher.identity.firstName).toBe("Joe");
|
||||||
|
expect(cipher.identity.middleName).toBe("M");
|
||||||
|
expect(cipher.identity.lastName).toBe("User");
|
||||||
|
expect(cipher.identity.licenseNumber).toBe("123456677");
|
||||||
|
expect(cipher.identity.country).toBe("United States");
|
||||||
|
|
||||||
|
expect(cipher.fields.length).toBe(5);
|
||||||
|
|
||||||
|
expect(cipher.fields[0].name).toEqual("status");
|
||||||
|
expect(cipher.fields[0].value).toEqual("active");
|
||||||
|
|
||||||
|
expect(cipher.fields[1].name).toEqual("tags");
|
||||||
|
expect(cipher.fields[1].value).toEqual("someTag");
|
||||||
|
|
||||||
|
expect(cipher.fields[2].name).toEqual("idType");
|
||||||
|
expect(cipher.fields[2].value).toEqual("Insurance Card");
|
||||||
|
|
||||||
|
expect(cipher.fields[3].name).toEqual("idIssuanceDate");
|
||||||
|
expect(cipher.fields[3].value).toEqual("03/07/2022");
|
||||||
|
|
||||||
|
expect(cipher.fields[4].name).toEqual("idExpirationDate");
|
||||||
|
expect(cipher.fields[4].value).toEqual("03/07/2022");
|
||||||
|
}
|
||||||
|
|
||||||
|
function expectHealthCard(cipher: CipherView) {
|
||||||
|
expect(cipher.type).toBe(CipherType.Identity);
|
||||||
|
expect(cipher.name).toBe("Health card Id card");
|
||||||
|
expect(cipher.notes).toBe("More info");
|
||||||
|
|
||||||
|
expect(cipher.identity.fullName).toBe("Joe M User");
|
||||||
|
expect(cipher.identity.firstName).toBe("Joe");
|
||||||
|
expect(cipher.identity.middleName).toBe("M");
|
||||||
|
expect(cipher.identity.lastName).toBe("User");
|
||||||
|
expect(cipher.identity.licenseNumber).toBe("1234670");
|
||||||
|
expect(cipher.identity.country).toBe("United States");
|
||||||
|
|
||||||
|
expect(cipher.fields.length).toBe(5);
|
||||||
|
|
||||||
|
expect(cipher.fields[0].name).toEqual("status");
|
||||||
|
expect(cipher.fields[0].value).toEqual("active");
|
||||||
|
|
||||||
|
expect(cipher.fields[1].name).toEqual("tags");
|
||||||
|
expect(cipher.fields[1].value).toEqual("someTag");
|
||||||
|
|
||||||
|
expect(cipher.fields[2].name).toEqual("idType");
|
||||||
|
expect(cipher.fields[2].value).toEqual("Health Card");
|
||||||
|
|
||||||
|
expect(cipher.fields[3].name).toEqual("idIssuanceDate");
|
||||||
|
expect(cipher.fields[3].value).toEqual("03/07/2022");
|
||||||
|
|
||||||
|
expect(cipher.fields[4].name).toEqual("idExpirationDate");
|
||||||
|
expect(cipher.fields[4].value).toEqual("03/07/2028");
|
||||||
|
}
|
||||||
|
|
||||||
|
function expectMembershipCard(cipher: CipherView) {
|
||||||
|
expect(cipher.type).toBe(CipherType.Identity);
|
||||||
|
expect(cipher.name).toBe("Membership ID card");
|
||||||
|
expect(cipher.notes).toBe("Add'l info");
|
||||||
|
|
||||||
|
expect(cipher.identity.fullName).toBe("Joe M User");
|
||||||
|
expect(cipher.identity.firstName).toBe("Joe");
|
||||||
|
expect(cipher.identity.middleName).toBe("M");
|
||||||
|
expect(cipher.identity.lastName).toBe("User");
|
||||||
|
expect(cipher.identity.licenseNumber).toBe("12345709");
|
||||||
|
expect(cipher.identity.country).toBe("United States");
|
||||||
|
|
||||||
|
expect(cipher.fields.length).toBe(5);
|
||||||
|
|
||||||
|
expect(cipher.fields[0].name).toEqual("status");
|
||||||
|
expect(cipher.fields[0].value).toEqual("active");
|
||||||
|
|
||||||
|
expect(cipher.fields[1].name).toEqual("tags");
|
||||||
|
expect(cipher.fields[1].value).toEqual("someTag");
|
||||||
|
|
||||||
|
expect(cipher.fields[2].name).toEqual("idType");
|
||||||
|
expect(cipher.fields[2].value).toEqual("Membership");
|
||||||
|
|
||||||
|
expect(cipher.fields[3].name).toEqual("idIssuanceDate");
|
||||||
|
expect(cipher.fields[3].value).toEqual("03/07/2022");
|
||||||
|
|
||||||
|
expect(cipher.fields[4].name).toEqual("idExpirationDate");
|
||||||
|
expect(cipher.fields[4].value).toEqual("03/07/2028");
|
||||||
|
}
|
||||||
|
|
||||||
|
function expectDatabase(cipher: CipherView) {
|
||||||
|
expect(cipher.type).toBe(CipherType.Identity);
|
||||||
|
expect(cipher.name).toBe("Database ID card");
|
||||||
|
expect(cipher.notes).toBe("Addin't info");
|
||||||
|
|
||||||
|
expect(cipher.identity.fullName).toBe("Joe M User");
|
||||||
|
expect(cipher.identity.firstName).toBe("Joe");
|
||||||
|
expect(cipher.identity.middleName).toBe("M");
|
||||||
|
expect(cipher.identity.lastName).toBe("User");
|
||||||
|
expect(cipher.identity.licenseNumber).toBe("12345089u");
|
||||||
|
expect(cipher.identity.country).toBe("United States");
|
||||||
|
|
||||||
|
expect(cipher.fields.length).toBe(5);
|
||||||
|
|
||||||
|
expect(cipher.fields[0].name).toEqual("status");
|
||||||
|
expect(cipher.fields[0].value).toEqual("active");
|
||||||
|
|
||||||
|
expect(cipher.fields[1].name).toEqual("tags");
|
||||||
|
expect(cipher.fields[1].value).toEqual("someTag");
|
||||||
|
|
||||||
|
expect(cipher.fields[2].name).toEqual("idType");
|
||||||
|
expect(cipher.fields[2].value).toEqual("Database");
|
||||||
|
|
||||||
|
expect(cipher.fields[3].name).toEqual("idIssuanceDate");
|
||||||
|
expect(cipher.fields[3].value).toEqual("03/07/2022");
|
||||||
|
|
||||||
|
expect(cipher.fields[4].name).toEqual("idExpirationDate");
|
||||||
|
expect(cipher.fields[4].value).toEqual("03/07/2028");
|
||||||
|
}
|
||||||
|
|
||||||
|
function expectOutdoorLicense(cipher: CipherView) {
|
||||||
|
expect(cipher.type).toBe(CipherType.Identity);
|
||||||
|
expect(cipher.name).toBe("Outdoor license ID card");
|
||||||
|
expect(cipher.notes).toBe("Additional info");
|
||||||
|
|
||||||
|
expect(cipher.identity.fullName).toBe("Joe M User");
|
||||||
|
expect(cipher.identity.firstName).toBe("Joe");
|
||||||
|
expect(cipher.identity.middleName).toBe("M");
|
||||||
|
expect(cipher.identity.lastName).toBe("User");
|
||||||
|
expect(cipher.identity.licenseNumber).toBe("123890090");
|
||||||
|
expect(cipher.identity.country).toBe("United States");
|
||||||
|
|
||||||
|
expect(cipher.fields.length).toBe(5);
|
||||||
|
|
||||||
|
expect(cipher.fields[0].name).toEqual("status");
|
||||||
|
expect(cipher.fields[0].value).toEqual("active");
|
||||||
|
|
||||||
|
expect(cipher.fields[1].name).toEqual("tags");
|
||||||
|
expect(cipher.fields[1].value).toEqual("someTag");
|
||||||
|
|
||||||
|
expect(cipher.fields[2].name).toEqual("idType");
|
||||||
|
expect(cipher.fields[2].value).toEqual("Outdoor License");
|
||||||
|
|
||||||
|
expect(cipher.fields[3].name).toEqual("idIssuanceDate");
|
||||||
|
expect(cipher.fields[3].value).toEqual("03/07/2022");
|
||||||
|
|
||||||
|
expect(cipher.fields[4].name).toEqual("idExpirationDate");
|
||||||
|
expect(cipher.fields[4].value).toEqual("03/07/2028");
|
||||||
|
}
|
||||||
|
|
||||||
|
function expectRewardProgram(cipher: CipherView) {
|
||||||
|
expect(cipher.type).toBe(CipherType.Identity);
|
||||||
|
expect(cipher.name).toBe("Reward program Id card");
|
||||||
|
expect(cipher.notes).toBe("1234890");
|
||||||
|
|
||||||
|
expect(cipher.identity.fullName).toBe("Joe M User");
|
||||||
|
expect(cipher.identity.firstName).toBe("Joe");
|
||||||
|
expect(cipher.identity.middleName).toBe("M");
|
||||||
|
expect(cipher.identity.lastName).toBe("User");
|
||||||
|
expect(cipher.identity.licenseNumber).toBe("12345890b");
|
||||||
|
expect(cipher.identity.country).toBe("United States");
|
||||||
|
|
||||||
|
expect(cipher.fields.length).toBe(5);
|
||||||
|
|
||||||
|
expect(cipher.fields[0].name).toEqual("status");
|
||||||
|
expect(cipher.fields[0].value).toEqual("active");
|
||||||
|
|
||||||
|
expect(cipher.fields[1].name).toEqual("tags");
|
||||||
|
expect(cipher.fields[1].value).toEqual("someTag");
|
||||||
|
|
||||||
|
expect(cipher.fields[2].name).toEqual("idType");
|
||||||
|
expect(cipher.fields[2].value).toEqual("Reward Program");
|
||||||
|
|
||||||
|
expect(cipher.fields[3].name).toEqual("idIssuanceDate");
|
||||||
|
expect(cipher.fields[3].value).toEqual("03/07/2022");
|
||||||
|
|
||||||
|
expect(cipher.fields[4].name).toEqual("idExpirationDate");
|
||||||
|
expect(cipher.fields[4].value).toEqual("03/07/2028");
|
||||||
|
}
|
||||||
|
|
||||||
|
function expectSoftwareLicense(cipher: CipherView) {
|
||||||
|
expect(cipher.type).toBe(CipherType.Identity);
|
||||||
|
expect(cipher.name).toBe("Software license ID card");
|
||||||
|
expect(cipher.notes).toBe(
|
||||||
|
"It seems like the fields don't change, which makes it pretty useless that they have so many ID card types."
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(cipher.identity.fullName).toBe("Joe M User");
|
||||||
|
expect(cipher.identity.firstName).toBe("Joe");
|
||||||
|
expect(cipher.identity.middleName).toBe("M");
|
||||||
|
expect(cipher.identity.lastName).toBe("User");
|
||||||
|
expect(cipher.identity.licenseNumber).toBe("1234567c");
|
||||||
|
expect(cipher.identity.country).toBe("United States");
|
||||||
|
|
||||||
|
expect(cipher.fields.length).toBe(5);
|
||||||
|
|
||||||
|
expect(cipher.fields[0].name).toEqual("status");
|
||||||
|
expect(cipher.fields[0].value).toEqual("active");
|
||||||
|
|
||||||
|
expect(cipher.fields[1].name).toEqual("tags");
|
||||||
|
expect(cipher.fields[1].value).toEqual("someTag");
|
||||||
|
|
||||||
|
expect(cipher.fields[2].name).toEqual("idType");
|
||||||
|
expect(cipher.fields[2].value).toEqual("Software License");
|
||||||
|
|
||||||
|
expect(cipher.fields[3].name).toEqual("idIssuanceDate");
|
||||||
|
expect(cipher.fields[3].value).toEqual("03/07/2022");
|
||||||
|
|
||||||
|
expect(cipher.fields[4].name).toEqual("idExpirationDate");
|
||||||
|
expect(cipher.fields[4].value).toEqual("03/07/2028");
|
||||||
|
}
|
||||||
|
|
||||||
|
function expectTourVisa(cipher: CipherView) {
|
||||||
|
expect(cipher.type).toBe(CipherType.Identity);
|
||||||
|
expect(cipher.name).toBe("Tour visa ID card");
|
||||||
|
expect(cipher.notes).toBe("Additional Informaion text");
|
||||||
|
|
||||||
|
expect(cipher.identity.fullName).toBe("Joe M User");
|
||||||
|
expect(cipher.identity.firstName).toBe("Joe");
|
||||||
|
expect(cipher.identity.middleName).toBe("M");
|
||||||
|
expect(cipher.identity.lastName).toBe("User");
|
||||||
|
expect(cipher.identity.licenseNumber).toBe("123456lkhj");
|
||||||
|
expect(cipher.identity.country).toBe("United States");
|
||||||
|
|
||||||
|
expect(cipher.fields.length).toBe(5);
|
||||||
|
|
||||||
|
expect(cipher.fields[0].name).toEqual("status");
|
||||||
|
expect(cipher.fields[0].value).toEqual("active");
|
||||||
|
|
||||||
|
expect(cipher.fields[1].name).toEqual("tags");
|
||||||
|
expect(cipher.fields[1].value).toEqual("someTag");
|
||||||
|
|
||||||
|
expect(cipher.fields[2].name).toEqual("idType");
|
||||||
|
expect(cipher.fields[2].value).toEqual("Tour Visa");
|
||||||
|
|
||||||
|
expect(cipher.fields[3].name).toEqual("idIssuanceDate");
|
||||||
|
expect(cipher.fields[3].value).toEqual("03/07/2022");
|
||||||
|
|
||||||
|
expect(cipher.fields[4].name).toEqual("idExpirationDate");
|
||||||
|
expect(cipher.fields[4].value).toEqual("03/07/2028");
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("Myki CSV Importer", () => {
|
||||||
|
let importer: Importer;
|
||||||
|
beforeEach(() => {
|
||||||
|
importer = new Importer();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should parse userAccount records", async () => {
|
||||||
|
const result = await importer.parse(userAccountData);
|
||||||
|
expect(result != null).toBe(true);
|
||||||
|
|
||||||
|
const cipher = result.ciphers.shift();
|
||||||
|
|
||||||
|
expect(cipher.name).toEqual("PasswordNickname");
|
||||||
|
expect(cipher.login.username).toEqual("user.name@email.com");
|
||||||
|
expect(cipher.login.password).toEqual("abc123");
|
||||||
|
expect(cipher.login.totp).toEqual("someTOTPSeed");
|
||||||
|
expect(cipher.login.uris.length).toEqual(1);
|
||||||
|
const uriView = cipher.login.uris.shift();
|
||||||
|
expect(uriView.uri).toEqual("http://www.google.com");
|
||||||
|
expect(cipher.notes).toEqual("This is the additional information text.");
|
||||||
|
|
||||||
|
expect(cipher.fields.length).toBe(2);
|
||||||
|
|
||||||
|
expect(cipher.fields[0].name).toBe("status");
|
||||||
|
expect(cipher.fields[0].value).toBe("active");
|
||||||
|
|
||||||
|
expect(cipher.fields[1].name).toBe("tags");
|
||||||
|
expect(cipher.fields[1].value).toBe("someTag");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should parse userTwoFa records", async () => {
|
||||||
|
const result = await importer.parse(userTwoFaData);
|
||||||
|
expect(result != null).toBe(true);
|
||||||
|
|
||||||
|
const cipher = result.ciphers.shift();
|
||||||
|
|
||||||
|
expect(cipher.name).toEqual("2FA nickname");
|
||||||
|
expect(cipher.login.username).toBeNull();
|
||||||
|
expect(cipher.login.password).toBeNull();
|
||||||
|
expect(cipher.login.totp).toBe("someTOTPSeed");
|
||||||
|
expect(cipher.notes).toEqual("Additional information field content.");
|
||||||
|
|
||||||
|
expect(cipher.fields.length).toBe(2);
|
||||||
|
|
||||||
|
expect(cipher.fields[0].name).toBe("status");
|
||||||
|
expect(cipher.fields[0].value).toBe("active");
|
||||||
|
|
||||||
|
expect(cipher.fields[1].name).toBe("tags");
|
||||||
|
expect(cipher.fields[1].value).toBe("someTag");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should parse creditCard records", async () => {
|
||||||
|
const result = await importer.parse(userCreditCardData);
|
||||||
|
|
||||||
|
expect(result).not.toBeNull();
|
||||||
|
expect(result.success).toBe(true);
|
||||||
|
expect(result.ciphers.length).toBe(1);
|
||||||
|
|
||||||
|
const cipher = result.ciphers.shift();
|
||||||
|
expect(cipher.type).toBe(CipherType.Card);
|
||||||
|
expect(cipher.name).toBe("Visa test card");
|
||||||
|
expect(cipher.card.brand).toBe("Visa");
|
||||||
|
expect(cipher.card.cardholderName).toBe("Joe User");
|
||||||
|
expect(cipher.card.number).toBe("4111111111111111");
|
||||||
|
expect(cipher.card.code).toBe("222");
|
||||||
|
expect(cipher.card.expMonth).toBe("04");
|
||||||
|
expect(cipher.card.expYear).toBe("24");
|
||||||
|
|
||||||
|
expect(cipher.notes).toBe("This is the additional information field");
|
||||||
|
|
||||||
|
expect(cipher.fields.length).toBe(2);
|
||||||
|
|
||||||
|
expect(cipher.fields[0].name).toBe("status");
|
||||||
|
expect(cipher.fields[0].value).toBe("active");
|
||||||
|
|
||||||
|
expect(cipher.fields[1].name).toBe("tags");
|
||||||
|
expect(cipher.fields[1].value).toBe("someTag");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should parse identity records", async () => {
|
||||||
|
const result = await importer.parse(userIdentityData);
|
||||||
|
|
||||||
|
expect(result).not.toBeNull();
|
||||||
|
expect(result.success).toBe(true);
|
||||||
|
|
||||||
|
const cipher = result.ciphers.shift();
|
||||||
|
expect(cipher.type).toBe(CipherType.Identity);
|
||||||
|
expect(cipher.name).toBe("Joe User's nickname");
|
||||||
|
expect(cipher.identity.fullName).toBe("Mr Joe M User");
|
||||||
|
expect(cipher.identity.title).toBe("Mr");
|
||||||
|
expect(cipher.identity.firstName).toBe("Joe");
|
||||||
|
expect(cipher.identity.middleName).toBe("M");
|
||||||
|
expect(cipher.identity.lastName).toBe("User");
|
||||||
|
expect(cipher.identity.email).toBe("joe.user@email.com");
|
||||||
|
|
||||||
|
expect(cipher.identity.address1).toBe("1 Example House");
|
||||||
|
expect(cipher.identity.address2).toBe("Suite 300");
|
||||||
|
|
||||||
|
expect(cipher.identity.city).toBe("Portland");
|
||||||
|
expect(cipher.identity.postalCode).toBe("04101");
|
||||||
|
expect(cipher.identity.country).toBe("United States");
|
||||||
|
|
||||||
|
expect(cipher.fields.length).toBe(4);
|
||||||
|
|
||||||
|
expect(cipher.fields[0].name).toEqual("status");
|
||||||
|
expect(cipher.fields[0].value).toEqual("active");
|
||||||
|
|
||||||
|
expect(cipher.fields[1].name).toBe("tags");
|
||||||
|
expect(cipher.fields[1].value).toBe("someTag");
|
||||||
|
|
||||||
|
expect(cipher.fields[2].name).toEqual("gender");
|
||||||
|
expect(cipher.fields[2].value).toEqual("Male");
|
||||||
|
|
||||||
|
expect(cipher.fields[3].name).toEqual("number");
|
||||||
|
expect(cipher.fields[3].value).toEqual("2223334444");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should parse secureNote records", async () => {
|
||||||
|
const result = await importer.parse(userNoteData);
|
||||||
|
|
||||||
|
expect(result).not.toBeNull();
|
||||||
|
expect(result.success).toBe(true);
|
||||||
|
expect(result.ciphers.length).toBe(1);
|
||||||
|
|
||||||
|
const cipher = result.ciphers.shift();
|
||||||
|
expect(cipher.type).toBe(CipherType.SecureNote);
|
||||||
|
expect(cipher.name).toBe("The title of a secure note");
|
||||||
|
expect(cipher.notes).toBe("The content of a secure note. Lorem ipsum, etc.");
|
||||||
|
|
||||||
|
expect(cipher.fields.length).toBe(1);
|
||||||
|
|
||||||
|
expect(cipher.fields[0].name).toBe("status");
|
||||||
|
expect(cipher.fields[0].value).toBe("active");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should parse idCard records", async () => {
|
||||||
|
const result = await importer.parse(userIdCardData);
|
||||||
|
|
||||||
|
expect(result).not.toBeNull();
|
||||||
|
expect(result.success).toBe(true);
|
||||||
|
|
||||||
|
expect(result.ciphers.length).toBe(14);
|
||||||
|
|
||||||
|
// Driver's license
|
||||||
|
const cipher = result.ciphers.shift();
|
||||||
|
expectDriversLicense(cipher);
|
||||||
|
|
||||||
|
// Passport
|
||||||
|
const cipher2 = result.ciphers.shift();
|
||||||
|
expectPassport(cipher2);
|
||||||
|
|
||||||
|
// Social Security
|
||||||
|
const cipher3 = result.ciphers.shift();
|
||||||
|
expectSocialSecurity(cipher3);
|
||||||
|
|
||||||
|
// Id Card
|
||||||
|
const cipher4 = result.ciphers.shift();
|
||||||
|
expectIdCard(cipher4);
|
||||||
|
|
||||||
|
// Tax Number
|
||||||
|
const cipher5 = result.ciphers.shift();
|
||||||
|
expectTaxNumber(cipher5);
|
||||||
|
|
||||||
|
// Bank Account
|
||||||
|
const cipher6 = result.ciphers.shift();
|
||||||
|
expectBankAccount(cipher6);
|
||||||
|
|
||||||
|
// Insurance card
|
||||||
|
const cipher7 = result.ciphers.shift();
|
||||||
|
expectInsuranceCard(cipher7);
|
||||||
|
|
||||||
|
// Health card
|
||||||
|
const cipher8 = result.ciphers.shift();
|
||||||
|
expectHealthCard(cipher8);
|
||||||
|
|
||||||
|
// Membership card
|
||||||
|
const cipher9 = result.ciphers.shift();
|
||||||
|
expectMembershipCard(cipher9);
|
||||||
|
|
||||||
|
// Database card
|
||||||
|
const cipher10 = result.ciphers.shift();
|
||||||
|
expectDatabase(cipher10);
|
||||||
|
|
||||||
|
// Outdoor license
|
||||||
|
const cipher11 = result.ciphers.shift();
|
||||||
|
expectOutdoorLicense(cipher11);
|
||||||
|
|
||||||
|
// Reward program
|
||||||
|
const cipher12 = result.ciphers.shift();
|
||||||
|
expectRewardProgram(cipher12);
|
||||||
|
|
||||||
|
// Software license
|
||||||
|
const cipher13 = result.ciphers.shift();
|
||||||
|
expectSoftwareLicense(cipher13);
|
||||||
|
|
||||||
|
// Tour visa
|
||||||
|
const cipher14 = result.ciphers.shift();
|
||||||
|
expectTourVisa(cipher14);
|
||||||
|
});
|
||||||
|
});
|
||||||
181
jslib/common/spec/importers/nordpassCsvImporter.spec.ts
Normal file
181
jslib/common/spec/importers/nordpassCsvImporter.spec.ts
Normal file
@@ -0,0 +1,181 @@
|
|||||||
|
import { CipherType } from "jslib-common/enums/cipherType";
|
||||||
|
import { SecureNoteType } from "jslib-common/enums/secureNoteType";
|
||||||
|
import { NordPassCsvImporter as Importer } from "jslib-common/importers/nordpassCsvImporter";
|
||||||
|
import { CipherView } from "jslib-common/models/view/cipherView";
|
||||||
|
import { IdentityView } from "jslib-common/models/view/identityView";
|
||||||
|
|
||||||
|
import { data as creditCardData } from "./testData/nordpassCsv/nordpass.card.csv";
|
||||||
|
import { data as identityData } from "./testData/nordpassCsv/nordpass.identity.csv";
|
||||||
|
import { data as loginData } from "./testData/nordpassCsv/nordpass.login.csv";
|
||||||
|
import { data as secureNoteData } from "./testData/nordpassCsv/nordpass.secureNote.csv";
|
||||||
|
|
||||||
|
const namesTestData = [
|
||||||
|
{
|
||||||
|
title: "Given #fullName should set firstName",
|
||||||
|
fullName: "MyFirstName",
|
||||||
|
expected: Object.assign(new IdentityView(), {
|
||||||
|
firstName: "MyFirstName",
|
||||||
|
middleName: null,
|
||||||
|
lastName: null,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Given #fullName should set first- and lastName",
|
||||||
|
fullName: "MyFirstName MyLastName",
|
||||||
|
expected: Object.assign(new IdentityView(), {
|
||||||
|
firstName: "MyFirstName",
|
||||||
|
middleName: null,
|
||||||
|
lastName: "MyLastName",
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Given #fullName should set first-, middle and lastName",
|
||||||
|
fullName: "MyFirstName MyMiddleName MyLastName",
|
||||||
|
expected: Object.assign(new IdentityView(), {
|
||||||
|
firstName: "MyFirstName",
|
||||||
|
middleName: "MyMiddleName",
|
||||||
|
lastName: "MyLastName",
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Given #fullName should set first-, middle and lastName with Jr",
|
||||||
|
fullName: "MyFirstName MyMiddleName MyLastName Jr",
|
||||||
|
expected: Object.assign(new IdentityView(), {
|
||||||
|
firstName: "MyFirstName",
|
||||||
|
middleName: "MyMiddleName",
|
||||||
|
lastName: "MyLastName Jr",
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Given #fullName should set first-, middle and lastName with Jr and III",
|
||||||
|
fullName: "MyFirstName MyMiddleName MyLastName Jr III",
|
||||||
|
expected: Object.assign(new IdentityView(), {
|
||||||
|
firstName: "MyFirstName",
|
||||||
|
middleName: "MyMiddleName",
|
||||||
|
lastName: "MyLastName Jr III",
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
function expectLogin(cipher: CipherView) {
|
||||||
|
expect(cipher.type).toBe(CipherType.Login);
|
||||||
|
|
||||||
|
expect(cipher.name).toBe("SomeVaultItemName");
|
||||||
|
expect(cipher.notes).toBe("Some note for the VaultItem");
|
||||||
|
expect(cipher.login.uri).toBe("https://example.com");
|
||||||
|
expect(cipher.login.username).toBe("hello@bitwarden.com");
|
||||||
|
expect(cipher.login.password).toBe("someStrongPassword");
|
||||||
|
}
|
||||||
|
|
||||||
|
function expectCreditCard(cipher: CipherView) {
|
||||||
|
expect(cipher.type).toBe(CipherType.Card);
|
||||||
|
|
||||||
|
expect(cipher.name).toBe("SomeVisa");
|
||||||
|
expect(cipher.card.brand).toBe("Visa");
|
||||||
|
expect(cipher.card.cardholderName).toBe("SomeHolder");
|
||||||
|
expect(cipher.card.number).toBe("4024007103939509");
|
||||||
|
expect(cipher.card.code).toBe("123");
|
||||||
|
expect(cipher.card.expMonth).toBe("1");
|
||||||
|
expect(cipher.card.expYear).toBe("22");
|
||||||
|
}
|
||||||
|
|
||||||
|
function expectIdentity(cipher: CipherView) {
|
||||||
|
expect(cipher.type).toBe(CipherType.Identity);
|
||||||
|
|
||||||
|
expect(cipher.name).toBe("SomeTitle");
|
||||||
|
expect(cipher.identity.fullName).toBe("MyFirstName MyMiddleName MyLastName");
|
||||||
|
expect(cipher.identity.firstName).toBe("MyFirstName");
|
||||||
|
expect(cipher.identity.middleName).toBe("MyMiddleName");
|
||||||
|
expect(cipher.identity.lastName).toBe("MyLastName");
|
||||||
|
expect(cipher.identity.email).toBe("hello@bitwarden.com");
|
||||||
|
expect(cipher.identity.phone).toBe("123456789");
|
||||||
|
|
||||||
|
expect(cipher.identity.address1).toBe("Test street 123");
|
||||||
|
expect(cipher.identity.address2).toBe("additional addressinfo");
|
||||||
|
expect(cipher.identity.postalCode).toBe("123456");
|
||||||
|
expect(cipher.identity.city).toBe("Cologne");
|
||||||
|
expect(cipher.identity.state).toBe("North-Rhine-Westphalia");
|
||||||
|
expect(cipher.identity.country).toBe("GERMANY");
|
||||||
|
expect(cipher.notes).toBe("SomeNoteToMyIdentity");
|
||||||
|
}
|
||||||
|
|
||||||
|
function expectSecureNote(cipher: CipherView) {
|
||||||
|
expect(cipher.type).toBe(CipherType.SecureNote);
|
||||||
|
|
||||||
|
expect(cipher.name).toBe("MySuperSecureNoteTitle");
|
||||||
|
expect(cipher.secureNote.type).toBe(SecureNoteType.Generic);
|
||||||
|
expect(cipher.notes).toBe("MySuperSecureNote");
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("NordPass CSV Importer", () => {
|
||||||
|
let importer: Importer;
|
||||||
|
beforeEach(() => {
|
||||||
|
importer = new Importer();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should parse login records", async () => {
|
||||||
|
const result = await importer.parse(loginData);
|
||||||
|
|
||||||
|
expect(result).not.toBeNull();
|
||||||
|
expect(result.success).toBe(true);
|
||||||
|
expect(result.ciphers.length).toBe(1);
|
||||||
|
const cipher = result.ciphers[0];
|
||||||
|
expectLogin(cipher);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should parse credit card records", async () => {
|
||||||
|
const result = await importer.parse(creditCardData);
|
||||||
|
|
||||||
|
expect(result).not.toBeNull();
|
||||||
|
expect(result.success).toBe(true);
|
||||||
|
expect(result.ciphers.length).toBe(1);
|
||||||
|
const cipher = result.ciphers[0];
|
||||||
|
expectCreditCard(cipher);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should parse identity records", async () => {
|
||||||
|
const result = await importer.parse(
|
||||||
|
identityData.replace("#fullName", "MyFirstName MyMiddleName MyLastName")
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(result).not.toBeNull();
|
||||||
|
expect(result.success).toBe(true);
|
||||||
|
expect(result.ciphers.length).toBe(1);
|
||||||
|
const cipher = result.ciphers[0];
|
||||||
|
expectIdentity(cipher);
|
||||||
|
});
|
||||||
|
|
||||||
|
namesTestData.forEach((data) => {
|
||||||
|
it(data.title.replace("#fullName", data.fullName), async () => {
|
||||||
|
const result = await importer.parse(identityData.replace("#fullName", data.fullName));
|
||||||
|
|
||||||
|
expect(result).not.toBeNull();
|
||||||
|
expect(result.success).toBe(true);
|
||||||
|
expect(result.ciphers.length).toBe(1);
|
||||||
|
const cipher = result.ciphers[0];
|
||||||
|
expect(cipher.identity.firstName).toBe(data.expected.firstName);
|
||||||
|
expect(cipher.identity.middleName).toBe(data.expected.middleName);
|
||||||
|
expect(cipher.identity.lastName).toBe(data.expected.lastName);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should parse secureNote records", async () => {
|
||||||
|
const result = await importer.parse(secureNoteData);
|
||||||
|
|
||||||
|
expect(result).not.toBeNull();
|
||||||
|
expect(result.success).toBe(true);
|
||||||
|
expect(result.ciphers.length).toBe(1);
|
||||||
|
const cipher = result.ciphers[0];
|
||||||
|
expectSecureNote(cipher);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should parse an item and create a folder", async () => {
|
||||||
|
const result = await importer.parse(secureNoteData);
|
||||||
|
|
||||||
|
expect(result).not.toBeNull();
|
||||||
|
expect(result.success).toBe(true);
|
||||||
|
expect(result.folders.length).toBe(1);
|
||||||
|
const folder = result.folders[0];
|
||||||
|
expect(folder.name).toBe("notesFolder");
|
||||||
|
});
|
||||||
|
});
|
||||||
527
jslib/common/spec/importers/onepassword1PifImporter.spec.ts
Normal file
527
jslib/common/spec/importers/onepassword1PifImporter.spec.ts
Normal file
@@ -0,0 +1,527 @@
|
|||||||
|
import { FieldType } from "jslib-common/enums/fieldType";
|
||||||
|
import { OnePassword1PifImporter as Importer } from "jslib-common/importers/onepasswordImporters/onepassword1PifImporter";
|
||||||
|
|
||||||
|
const TestData: string =
|
||||||
|
"***aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee***\n" +
|
||||||
|
JSON.stringify({
|
||||||
|
uuid: "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
|
||||||
|
updatedAt: 1486071244,
|
||||||
|
securityLevel: "SL5",
|
||||||
|
contentsHash: "aaaaaaaa",
|
||||||
|
title: "Imported Entry",
|
||||||
|
location: "https://www.google.com",
|
||||||
|
secureContents: {
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
value: "user@test.net",
|
||||||
|
id: "email-input",
|
||||||
|
name: "email",
|
||||||
|
type: "T",
|
||||||
|
designation: "username",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: "myservicepassword",
|
||||||
|
id: "password-input",
|
||||||
|
name: "password",
|
||||||
|
type: "P",
|
||||||
|
designation: "password",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
sections: [
|
||||||
|
{
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
k: "concealed",
|
||||||
|
n: "AAAAAAAAAAAABBBBBBBBBBBCCCCCCCCC",
|
||||||
|
v: "console-password-123",
|
||||||
|
t: "console password",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
title: "Admin Console",
|
||||||
|
name: "admin_console",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
passwordHistory: [
|
||||||
|
{
|
||||||
|
value: "old-password",
|
||||||
|
time: 1447791421,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
URLs: [
|
||||||
|
{
|
||||||
|
label: "website",
|
||||||
|
url: "https://www.google.com",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
txTimestamp: 1508941334,
|
||||||
|
createdAt: 1390426636,
|
||||||
|
typeName: "webforms.WebForm",
|
||||||
|
});
|
||||||
|
|
||||||
|
const WindowsOpVaultTestData = JSON.stringify({
|
||||||
|
category: "001",
|
||||||
|
created: 1544823719,
|
||||||
|
hmac: "NtyBmTTPOb88HV3JUKPx1xl/vcMhac9kvCfe/NtszY0=",
|
||||||
|
k: "**REMOVED LONG LINE FOR LINTER** -Kyle",
|
||||||
|
tx: 1553395669,
|
||||||
|
updated: 1553395669,
|
||||||
|
uuid: "528AB076FB5F4FBF960884B8E01619AC",
|
||||||
|
overview: {
|
||||||
|
title: "Google",
|
||||||
|
URLs: [
|
||||||
|
{
|
||||||
|
u: "google.com",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
url: "google.com",
|
||||||
|
ps: 26,
|
||||||
|
ainfo: "googluser",
|
||||||
|
},
|
||||||
|
details: {
|
||||||
|
passwordHistory: [
|
||||||
|
{
|
||||||
|
value: "oldpass1",
|
||||||
|
time: 1553394449,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: "oldpass2",
|
||||||
|
time: 1553394457,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: "oldpass3",
|
||||||
|
time: 1553394458,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: "oldpass4",
|
||||||
|
time: 1553394459,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: "oldpass5",
|
||||||
|
time: 1553394460,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: "oldpass6",
|
||||||
|
time: 1553394461,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
type: "T",
|
||||||
|
id: "username",
|
||||||
|
name: "username",
|
||||||
|
value: "googluser",
|
||||||
|
designation: "username",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "P",
|
||||||
|
id: "password",
|
||||||
|
name: "password",
|
||||||
|
value: "12345678901",
|
||||||
|
designation: "password",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
notesPlain: "This is a note\r\n\r\nline1\r\nline2",
|
||||||
|
sections: [
|
||||||
|
{
|
||||||
|
title: "test",
|
||||||
|
name: "1214FD88CD30405D9EED14BEB4D61B60",
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
k: "string",
|
||||||
|
n: "6CC3BD77482D4559A4B8BB2D360F821B",
|
||||||
|
v: "fgfg",
|
||||||
|
t: "fgggf",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
k: "concealed",
|
||||||
|
n: "5CFE7BCAA1DF4578BBF7EB508959BFF3",
|
||||||
|
v: "dfgdfgfdg",
|
||||||
|
t: "pwfield",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const IdentityTestData = JSON.stringify({
|
||||||
|
uuid: "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
|
||||||
|
updatedAt: 1553365894,
|
||||||
|
securityLevel: "SL5",
|
||||||
|
contentsHash: "eeeeeeee",
|
||||||
|
title: "Test Identity",
|
||||||
|
secureContents: {
|
||||||
|
lastname: "Fritzenberger",
|
||||||
|
zip: "223344",
|
||||||
|
birthdate_dd: "11",
|
||||||
|
homephone: "+49 333 222 111",
|
||||||
|
company: "Web Inc.",
|
||||||
|
firstname: "Frank",
|
||||||
|
birthdate_mm: "3",
|
||||||
|
country: "de",
|
||||||
|
sex: "male",
|
||||||
|
sections: [
|
||||||
|
{
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
k: "string",
|
||||||
|
inputTraits: {
|
||||||
|
autocapitalization: "Words",
|
||||||
|
},
|
||||||
|
n: "firstname",
|
||||||
|
v: "Frank",
|
||||||
|
a: {
|
||||||
|
guarded: "yes",
|
||||||
|
},
|
||||||
|
t: "first name",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
k: "string",
|
||||||
|
inputTraits: {
|
||||||
|
autocapitalization: "Words",
|
||||||
|
},
|
||||||
|
n: "initial",
|
||||||
|
v: "MD",
|
||||||
|
a: {
|
||||||
|
guarded: "yes",
|
||||||
|
},
|
||||||
|
t: "initial",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
k: "string",
|
||||||
|
inputTraits: {
|
||||||
|
autocapitalization: "Words",
|
||||||
|
},
|
||||||
|
n: "lastname",
|
||||||
|
v: "Fritzenberger",
|
||||||
|
a: {
|
||||||
|
guarded: "yes",
|
||||||
|
},
|
||||||
|
t: "last name",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
k: "menu",
|
||||||
|
v: "male",
|
||||||
|
n: "sex",
|
||||||
|
a: {
|
||||||
|
guarded: "yes",
|
||||||
|
},
|
||||||
|
t: "sex",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
k: "date",
|
||||||
|
v: 1552305660,
|
||||||
|
n: "birthdate",
|
||||||
|
a: {
|
||||||
|
guarded: "yes",
|
||||||
|
},
|
||||||
|
t: "birth date",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
k: "string",
|
||||||
|
inputTraits: {
|
||||||
|
autocapitalization: "Words",
|
||||||
|
},
|
||||||
|
n: "occupation",
|
||||||
|
v: "Engineer",
|
||||||
|
a: {
|
||||||
|
guarded: "yes",
|
||||||
|
},
|
||||||
|
t: "occupation",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
k: "string",
|
||||||
|
inputTraits: {
|
||||||
|
autocapitalization: "Words",
|
||||||
|
},
|
||||||
|
n: "company",
|
||||||
|
v: "Web Inc.",
|
||||||
|
a: {
|
||||||
|
guarded: "yes",
|
||||||
|
},
|
||||||
|
t: "company",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
k: "string",
|
||||||
|
inputTraits: {
|
||||||
|
autocapitalization: "Words",
|
||||||
|
},
|
||||||
|
n: "department",
|
||||||
|
v: "IT",
|
||||||
|
a: {
|
||||||
|
guarded: "yes",
|
||||||
|
},
|
||||||
|
t: "department",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
k: "string",
|
||||||
|
inputTraits: {
|
||||||
|
autocapitalization: "Words",
|
||||||
|
},
|
||||||
|
n: "jobtitle",
|
||||||
|
v: "Developer",
|
||||||
|
a: {
|
||||||
|
guarded: "yes",
|
||||||
|
},
|
||||||
|
t: "job title",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
title: "Identification",
|
||||||
|
name: "name",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
k: "address",
|
||||||
|
inputTraits: {
|
||||||
|
autocapitalization: "Sentences",
|
||||||
|
},
|
||||||
|
n: "address",
|
||||||
|
v: {
|
||||||
|
street: "Mainstreet 1",
|
||||||
|
city: "Berlin",
|
||||||
|
country: "de",
|
||||||
|
zip: "223344",
|
||||||
|
},
|
||||||
|
a: {
|
||||||
|
guarded: "yes",
|
||||||
|
},
|
||||||
|
t: "address",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
k: "phone",
|
||||||
|
v: "+49 001 222 333 44",
|
||||||
|
n: "defphone",
|
||||||
|
a: {
|
||||||
|
guarded: "yes",
|
||||||
|
},
|
||||||
|
t: "default phone",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
k: "phone",
|
||||||
|
v: "+49 333 222 111",
|
||||||
|
n: "homephone",
|
||||||
|
a: {
|
||||||
|
guarded: "yes",
|
||||||
|
},
|
||||||
|
t: "home",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
k: "phone",
|
||||||
|
n: "cellphone",
|
||||||
|
a: {
|
||||||
|
guarded: "yes",
|
||||||
|
},
|
||||||
|
t: "mobile",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
k: "phone",
|
||||||
|
n: "busphone",
|
||||||
|
a: {
|
||||||
|
guarded: "yes",
|
||||||
|
},
|
||||||
|
t: "business",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
title: "Address",
|
||||||
|
name: "address",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
k: "string",
|
||||||
|
n: "username",
|
||||||
|
a: {
|
||||||
|
guarded: "yes",
|
||||||
|
},
|
||||||
|
t: "username",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
k: "string",
|
||||||
|
n: "reminderq",
|
||||||
|
t: "reminder question",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
k: "string",
|
||||||
|
n: "remindera",
|
||||||
|
t: "reminder answer",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
k: "string",
|
||||||
|
inputTraits: {
|
||||||
|
keyboard: "EmailAddress",
|
||||||
|
},
|
||||||
|
n: "email",
|
||||||
|
v: "test@web.de",
|
||||||
|
a: {
|
||||||
|
guarded: "yes",
|
||||||
|
},
|
||||||
|
t: "email",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
k: "string",
|
||||||
|
n: "website",
|
||||||
|
inputTraits: {
|
||||||
|
keyboard: "URL",
|
||||||
|
},
|
||||||
|
t: "website",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
k: "string",
|
||||||
|
n: "icq",
|
||||||
|
t: "ICQ",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
k: "string",
|
||||||
|
n: "skype",
|
||||||
|
t: "skype",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
k: "string",
|
||||||
|
n: "aim",
|
||||||
|
t: "AOL/AIM",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
k: "string",
|
||||||
|
n: "yahoo",
|
||||||
|
t: "Yahoo",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
k: "string",
|
||||||
|
n: "msn",
|
||||||
|
t: "MSN",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
k: "string",
|
||||||
|
n: "forumsig",
|
||||||
|
t: "forum signature",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
title: "Internet Details",
|
||||||
|
name: "internet",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Related Items",
|
||||||
|
name: "linked items",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
initial: "MD",
|
||||||
|
address1: "Mainstreet 1",
|
||||||
|
city: "Berlin",
|
||||||
|
jobtitle: "Developer",
|
||||||
|
occupation: "Engineer",
|
||||||
|
department: "IT",
|
||||||
|
email: "test@web.de",
|
||||||
|
birthdate_yy: "2019",
|
||||||
|
homephone_local: "+49 333 222 111",
|
||||||
|
defphone_local: "+49 001 222 333 44",
|
||||||
|
defphone: "+49 001 222 333 44",
|
||||||
|
},
|
||||||
|
txTimestamp: 1553365894,
|
||||||
|
createdAt: 1553364679,
|
||||||
|
typeName: "identities.Identity",
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("1Password 1Pif Importer", () => {
|
||||||
|
it("should parse data", async () => {
|
||||||
|
const importer = new Importer();
|
||||||
|
const result = await importer.parse(TestData);
|
||||||
|
expect(result != null).toBe(true);
|
||||||
|
|
||||||
|
const cipher = result.ciphers.shift();
|
||||||
|
expect(cipher.login.username).toEqual("user@test.net");
|
||||||
|
expect(cipher.login.password).toEqual("myservicepassword");
|
||||||
|
expect(cipher.login.uris.length).toEqual(1);
|
||||||
|
const uriView = cipher.login.uris.shift();
|
||||||
|
expect(uriView.uri).toEqual("https://www.google.com");
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create concealed field as "hidden" type', async () => {
|
||||||
|
const importer = new Importer();
|
||||||
|
const result = await importer.parse(TestData);
|
||||||
|
expect(result != null).toBe(true);
|
||||||
|
|
||||||
|
const ciphers = result.ciphers;
|
||||||
|
expect(ciphers.length).toEqual(1);
|
||||||
|
|
||||||
|
const cipher = ciphers.shift();
|
||||||
|
const fields = cipher.fields;
|
||||||
|
expect(fields.length).toEqual(1);
|
||||||
|
|
||||||
|
const field = fields.shift();
|
||||||
|
expect(field.name).toEqual("console password");
|
||||||
|
expect(field.value).toEqual("console-password-123");
|
||||||
|
expect(field.type).toEqual(FieldType.Hidden);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should create identity records", async () => {
|
||||||
|
const importer = new Importer();
|
||||||
|
const result = await importer.parse(IdentityTestData);
|
||||||
|
expect(result != null).toBe(true);
|
||||||
|
const cipher = result.ciphers.shift();
|
||||||
|
expect(cipher.name).toEqual("Test Identity");
|
||||||
|
|
||||||
|
const identity = cipher.identity;
|
||||||
|
expect(identity.firstName).toEqual("Frank");
|
||||||
|
expect(identity.middleName).toEqual("MD");
|
||||||
|
expect(identity.lastName).toEqual("Fritzenberger");
|
||||||
|
expect(identity.company).toEqual("Web Inc.");
|
||||||
|
expect(identity.address1).toEqual("Mainstreet 1");
|
||||||
|
expect(identity.country).toEqual("DE");
|
||||||
|
expect(identity.city).toEqual("Berlin");
|
||||||
|
expect(identity.postalCode).toEqual("223344");
|
||||||
|
expect(identity.phone).toEqual("+49 001 222 333 44");
|
||||||
|
expect(identity.email).toEqual("test@web.de");
|
||||||
|
|
||||||
|
// remaining fields as custom fields
|
||||||
|
expect(cipher.fields.length).toEqual(6);
|
||||||
|
const fields = cipher.fields;
|
||||||
|
expect(fields[0].name).toEqual("sex");
|
||||||
|
expect(fields[0].value).toEqual("male");
|
||||||
|
expect(fields[1].name).toEqual("birth date");
|
||||||
|
expect(fields[1].value).toEqual("Mon, 11 Mar 2019 12:01:00 GMT");
|
||||||
|
expect(fields[2].name).toEqual("occupation");
|
||||||
|
expect(fields[2].value).toEqual("Engineer");
|
||||||
|
expect(fields[3].name).toEqual("department");
|
||||||
|
expect(fields[3].value).toEqual("IT");
|
||||||
|
expect(fields[4].name).toEqual("job title");
|
||||||
|
expect(fields[4].value).toEqual("Developer");
|
||||||
|
expect(fields[5].name).toEqual("home");
|
||||||
|
expect(fields[5].value).toEqual("+49 333 222 111");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should create password history", async () => {
|
||||||
|
const importer = new Importer();
|
||||||
|
const result = await importer.parse(TestData);
|
||||||
|
const cipher = result.ciphers.shift();
|
||||||
|
|
||||||
|
expect(cipher.passwordHistory.length).toEqual(1);
|
||||||
|
const ph = cipher.passwordHistory.shift();
|
||||||
|
expect(ph.password).toEqual("old-password");
|
||||||
|
expect(ph.lastUsedDate.toISOString()).toEqual("2015-11-17T20:17:01.000Z");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should create password history from windows opvault 1pif format", async () => {
|
||||||
|
const importer = new Importer();
|
||||||
|
const result = await importer.parse(WindowsOpVaultTestData);
|
||||||
|
const cipher = result.ciphers.shift();
|
||||||
|
|
||||||
|
expect(cipher.passwordHistory.length).toEqual(5);
|
||||||
|
let ph = cipher.passwordHistory.shift();
|
||||||
|
expect(ph.password).toEqual("oldpass6");
|
||||||
|
expect(ph.lastUsedDate.toISOString()).toEqual("2019-03-24T02:27:41.000Z");
|
||||||
|
ph = cipher.passwordHistory.shift();
|
||||||
|
expect(ph.password).toEqual("oldpass5");
|
||||||
|
expect(ph.lastUsedDate.toISOString()).toEqual("2019-03-24T02:27:40.000Z");
|
||||||
|
ph = cipher.passwordHistory.shift();
|
||||||
|
expect(ph.password).toEqual("oldpass4");
|
||||||
|
expect(ph.lastUsedDate.toISOString()).toEqual("2019-03-24T02:27:39.000Z");
|
||||||
|
ph = cipher.passwordHistory.shift();
|
||||||
|
expect(ph.password).toEqual("oldpass3");
|
||||||
|
expect(ph.lastUsedDate.toISOString()).toEqual("2019-03-24T02:27:38.000Z");
|
||||||
|
ph = cipher.passwordHistory.shift();
|
||||||
|
expect(ph.password).toEqual("oldpass2");
|
||||||
|
expect(ph.lastUsedDate.toISOString()).toEqual("2019-03-24T02:27:37.000Z");
|
||||||
|
});
|
||||||
|
});
|
||||||
689
jslib/common/spec/importers/onepassword1PuxImporter.spec.ts
Normal file
689
jslib/common/spec/importers/onepassword1PuxImporter.spec.ts
Normal file
@@ -0,0 +1,689 @@
|
|||||||
|
import { CipherType } from "jslib-common/enums/cipherType";
|
||||||
|
import { FieldType } from "jslib-common/enums/fieldType";
|
||||||
|
import { SecureNoteType } from "jslib-common/enums/secureNoteType";
|
||||||
|
import { OnePassword1PuxImporter as Importer } from "jslib-common/importers/onepasswordImporters/onepassword1PuxImporter";
|
||||||
|
import { Utils } from "jslib-common/misc/utils";
|
||||||
|
import { FieldView } from "jslib-common/models/view/fieldView";
|
||||||
|
|
||||||
|
import { APICredentialsData } from "./testData/onePassword1Pux/APICredentials";
|
||||||
|
import { BankAccountData } from "./testData/onePassword1Pux/BankAccount";
|
||||||
|
import { CreditCardData } from "./testData/onePassword1Pux/CreditCard";
|
||||||
|
import { DatabaseData } from "./testData/onePassword1Pux/Database";
|
||||||
|
import { DriversLicenseData } from "./testData/onePassword1Pux/DriversLicense";
|
||||||
|
import { EmailAccountData } from "./testData/onePassword1Pux/EmailAccount";
|
||||||
|
import { EmailFieldData } from "./testData/onePassword1Pux/Emailfield";
|
||||||
|
import { EmailFieldOnIdentityData } from "./testData/onePassword1Pux/EmailfieldOnIdentity";
|
||||||
|
import { EmailFieldOnIdentityPrefilledData } from "./testData/onePassword1Pux/EmailfieldOnIdentity_Prefilled";
|
||||||
|
import { IdentityData } from "./testData/onePassword1Pux/IdentityData";
|
||||||
|
import { LoginData } from "./testData/onePassword1Pux/LoginData";
|
||||||
|
import { MedicalRecordData } from "./testData/onePassword1Pux/MedicalRecord";
|
||||||
|
import { MembershipData } from "./testData/onePassword1Pux/Membership";
|
||||||
|
import { OnePuxExampleFile } from "./testData/onePassword1Pux/Onepux_example";
|
||||||
|
import { OutdoorLicenseData } from "./testData/onePassword1Pux/OutdoorLicense";
|
||||||
|
import { PassportData } from "./testData/onePassword1Pux/Passport";
|
||||||
|
import { PasswordData } from "./testData/onePassword1Pux/Password";
|
||||||
|
import { RewardsProgramData } from "./testData/onePassword1Pux/RewardsProgram";
|
||||||
|
import { SSNData } from "./testData/onePassword1Pux/SSN";
|
||||||
|
import { SanitizedExport } from "./testData/onePassword1Pux/SanitizedExport";
|
||||||
|
import { SecureNoteData } from "./testData/onePassword1Pux/SecureNote";
|
||||||
|
import { ServerData } from "./testData/onePassword1Pux/Server";
|
||||||
|
import { SoftwareLicenseData } from "./testData/onePassword1Pux/SoftwareLicense";
|
||||||
|
import { WirelessRouterData } from "./testData/onePassword1Pux/WirelessRouter";
|
||||||
|
|
||||||
|
function validateCustomField(fields: FieldView[], fieldName: string, expectedValue: any) {
|
||||||
|
expect(fields).toBeDefined();
|
||||||
|
const customField = fields.find((f) => f.name === fieldName);
|
||||||
|
expect(customField).toBeDefined();
|
||||||
|
|
||||||
|
expect(customField.value).toEqual(expectedValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("1Password 1Pux Importer", () => {
|
||||||
|
const OnePuxExampleFileJson = JSON.stringify(OnePuxExampleFile);
|
||||||
|
const LoginDataJson = JSON.stringify(LoginData);
|
||||||
|
const CreditCardDataJson = JSON.stringify(CreditCardData);
|
||||||
|
const IdentityDataJson = JSON.stringify(IdentityData);
|
||||||
|
const SecureNoteDataJson = JSON.stringify(SecureNoteData);
|
||||||
|
const SanitizedExportJson = JSON.stringify(SanitizedExport);
|
||||||
|
|
||||||
|
it("should parse login data", async () => {
|
||||||
|
const importer = new Importer();
|
||||||
|
const result = await importer.parse(LoginDataJson);
|
||||||
|
expect(result != null).toBe(true);
|
||||||
|
|
||||||
|
const cipher = result.ciphers.shift();
|
||||||
|
|
||||||
|
expect(cipher.type).toEqual(CipherType.Login);
|
||||||
|
expect(cipher.name).toEqual("eToro");
|
||||||
|
|
||||||
|
expect(cipher.login.username).toEqual("username123123123@gmail.com");
|
||||||
|
expect(cipher.login.password).toEqual("password!");
|
||||||
|
expect(cipher.login.uris.length).toEqual(1);
|
||||||
|
expect(cipher.login.uri).toEqual("https://www.fakesite.com");
|
||||||
|
expect(cipher.login.totp).toEqual("otpseed777");
|
||||||
|
|
||||||
|
// remaining fields as custom fields
|
||||||
|
expect(cipher.fields.length).toEqual(3);
|
||||||
|
validateCustomField(cipher.fields, "terms", "false");
|
||||||
|
validateCustomField(cipher.fields, "policies", "true");
|
||||||
|
validateCustomField(cipher.fields, "cyqyggt2otns6tbbqtsl6w2ceu", "username123123");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should parse notes", async () => {
|
||||||
|
const importer = new Importer();
|
||||||
|
const result = await importer.parse(OnePuxExampleFileJson);
|
||||||
|
expect(result != null).toBe(true);
|
||||||
|
|
||||||
|
const cipher = result.ciphers.shift();
|
||||||
|
expect(cipher.notes).toEqual("This is a note. *bold*! _italic_!");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should set favourite if favIndex equals 1", async () => {
|
||||||
|
const importer = new Importer();
|
||||||
|
const result = await importer.parse(OnePuxExampleFileJson);
|
||||||
|
expect(result != null).toBe(true);
|
||||||
|
|
||||||
|
const cipher = result.ciphers.shift();
|
||||||
|
expect(cipher.favorite).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should handle custom boolean fields", async () => {
|
||||||
|
const importer = new Importer();
|
||||||
|
const result = await importer.parse(LoginDataJson);
|
||||||
|
expect(result != null).toBe(true);
|
||||||
|
|
||||||
|
const ciphers = result.ciphers;
|
||||||
|
expect(ciphers.length).toEqual(1);
|
||||||
|
|
||||||
|
const cipher = ciphers.shift();
|
||||||
|
expect(cipher.fields[0].name).toEqual("terms");
|
||||||
|
expect(cipher.fields[0].value).toEqual("false");
|
||||||
|
expect(cipher.fields[0].type).toBe(FieldType.Boolean);
|
||||||
|
|
||||||
|
expect(cipher.fields[1].name).toEqual("policies");
|
||||||
|
expect(cipher.fields[1].value).toEqual("true");
|
||||||
|
expect(cipher.fields[1].type).toBe(FieldType.Boolean);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should add fields of type email as custom fields", async () => {
|
||||||
|
const importer = new Importer();
|
||||||
|
const EmailFieldDataJson = JSON.stringify(EmailFieldData);
|
||||||
|
const result = await importer.parse(EmailFieldDataJson);
|
||||||
|
expect(result != null).toBe(true);
|
||||||
|
|
||||||
|
const ciphers = result.ciphers;
|
||||||
|
expect(ciphers.length).toEqual(1);
|
||||||
|
const cipher = ciphers.shift();
|
||||||
|
|
||||||
|
expect(cipher.fields[0].name).toEqual("reg_email");
|
||||||
|
expect(cipher.fields[0].value).toEqual("kriddler@nullvalue.test");
|
||||||
|
expect(cipher.fields[0].type).toBe(FieldType.Text);
|
||||||
|
|
||||||
|
expect(cipher.fields[1].name).toEqual("provider");
|
||||||
|
expect(cipher.fields[1].value).toEqual("myEmailProvider");
|
||||||
|
expect(cipher.fields[1].type).toBe(FieldType.Text);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create concealed field as "hidden" type', async () => {
|
||||||
|
const importer = new Importer();
|
||||||
|
const result = await importer.parse(OnePuxExampleFileJson);
|
||||||
|
expect(result != null).toBe(true);
|
||||||
|
|
||||||
|
const ciphers = result.ciphers;
|
||||||
|
expect(ciphers.length).toEqual(1);
|
||||||
|
|
||||||
|
const cipher = ciphers.shift();
|
||||||
|
const fields = cipher.fields;
|
||||||
|
expect(fields.length).toEqual(1);
|
||||||
|
|
||||||
|
const field = fields.shift();
|
||||||
|
expect(field.name).toEqual("PIN");
|
||||||
|
expect(field.value).toEqual("12345");
|
||||||
|
expect(field.type).toEqual(FieldType.Hidden);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should create password history", async () => {
|
||||||
|
const importer = new Importer();
|
||||||
|
const result = await importer.parse(OnePuxExampleFileJson);
|
||||||
|
const cipher = result.ciphers.shift();
|
||||||
|
|
||||||
|
expect(cipher.passwordHistory.length).toEqual(1);
|
||||||
|
const ph = cipher.passwordHistory.shift();
|
||||||
|
expect(ph.password).toEqual("12345password");
|
||||||
|
expect(ph.lastUsedDate.toISOString()).toEqual("2016-03-18T17:32:35.000Z");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should create credit card records", async () => {
|
||||||
|
const importer = new Importer();
|
||||||
|
const result = await importer.parse(CreditCardDataJson);
|
||||||
|
expect(result != null).toBe(true);
|
||||||
|
const cipher = result.ciphers.shift();
|
||||||
|
expect(cipher.name).toEqual("Parent's Credit Card");
|
||||||
|
expect(cipher.notes).toEqual("My parents' credit card.");
|
||||||
|
|
||||||
|
const card = cipher.card;
|
||||||
|
expect(card.cardholderName).toEqual("Fred Engels");
|
||||||
|
expect(card.number).toEqual("6011111111111117");
|
||||||
|
expect(card.code).toEqual("1312");
|
||||||
|
expect(card.brand).toEqual("Discover");
|
||||||
|
expect(card.expMonth).toEqual("12");
|
||||||
|
expect(card.expYear).toEqual("2099");
|
||||||
|
|
||||||
|
// remaining fields as custom fields
|
||||||
|
expect(cipher.fields.length).toEqual(12);
|
||||||
|
validateCustomField(cipher.fields, "txbzvwzpck7ejhfres3733rbpm", "card");
|
||||||
|
validateCustomField(cipher.fields, "cashLimit", "$500");
|
||||||
|
validateCustomField(cipher.fields, "creditLimit", "$1312");
|
||||||
|
validateCustomField(cipher.fields, "validFrom", "200101");
|
||||||
|
validateCustomField(cipher.fields, "bank", "Some bank");
|
||||||
|
validateCustomField(cipher.fields, "phoneLocal", "123456");
|
||||||
|
validateCustomField(cipher.fields, "phoneTollFree", "0800123456");
|
||||||
|
validateCustomField(cipher.fields, "phoneIntl", "+49123456");
|
||||||
|
validateCustomField(cipher.fields, "website", "somebank.com");
|
||||||
|
validateCustomField(cipher.fields, "pin", "1234");
|
||||||
|
validateCustomField(cipher.fields, "interest", "1%");
|
||||||
|
validateCustomField(cipher.fields, "issuenumber", "123456");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should create identity records", async () => {
|
||||||
|
const importer = new Importer();
|
||||||
|
const result = await importer.parse(IdentityDataJson);
|
||||||
|
expect(result != null).toBe(true);
|
||||||
|
const cipher = result.ciphers.shift();
|
||||||
|
expect(cipher.name).toEqual("George Engels");
|
||||||
|
|
||||||
|
const identity = cipher.identity;
|
||||||
|
expect(identity.firstName).toEqual("George");
|
||||||
|
expect(identity.middleName).toEqual("S");
|
||||||
|
expect(identity.lastName).toEqual("Engels");
|
||||||
|
expect(identity.company).toEqual("Acme Inc.");
|
||||||
|
expect(identity.address1).toEqual("1312 Main St.");
|
||||||
|
expect(identity.country).toEqual("US");
|
||||||
|
expect(identity.state).toEqual("California");
|
||||||
|
expect(identity.city).toEqual("Atlantis");
|
||||||
|
expect(identity.postalCode).toEqual("90210");
|
||||||
|
expect(identity.phone).toEqual("4565555555");
|
||||||
|
expect(identity.email).toEqual("gengels@nullvalue.test");
|
||||||
|
expect(identity.username).toEqual("gengels");
|
||||||
|
|
||||||
|
// remaining fields as custom fields
|
||||||
|
expect(cipher.fields.length).toEqual(17);
|
||||||
|
validateCustomField(cipher.fields, "sex", "male");
|
||||||
|
validateCustomField(cipher.fields, "birthdate", "Thu, 01 Jan 1981 12:01:00 GMT");
|
||||||
|
validateCustomField(cipher.fields, "occupation", "Steel Worker");
|
||||||
|
validateCustomField(cipher.fields, "department", "QA");
|
||||||
|
validateCustomField(cipher.fields, "jobtitle", "Quality Assurance Manager");
|
||||||
|
validateCustomField(cipher.fields, "homephone", "4575555555");
|
||||||
|
validateCustomField(cipher.fields, "cellphone", "4585555555");
|
||||||
|
validateCustomField(cipher.fields, "busphone", "4595555555");
|
||||||
|
validateCustomField(cipher.fields, "reminderq", "Who's a super cool guy?");
|
||||||
|
validateCustomField(cipher.fields, "remindera", "Me, buddy.");
|
||||||
|
validateCustomField(cipher.fields, "website", "cv.gengels.nullvalue.test");
|
||||||
|
validateCustomField(cipher.fields, "icq", "12345678");
|
||||||
|
validateCustomField(cipher.fields, "skype", "skypeisbad1619");
|
||||||
|
validateCustomField(cipher.fields, "aim", "aollol@lololol.aol.com");
|
||||||
|
validateCustomField(cipher.fields, "yahoo", "sk8rboi13@yah00.com");
|
||||||
|
validateCustomField(cipher.fields, "msn", "msnothankyou@msn&m&m.com");
|
||||||
|
validateCustomField(cipher.fields, "forumsig", "super cool guy");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("emails fields on identity types should be added to the identity email field", async () => {
|
||||||
|
const importer = new Importer();
|
||||||
|
const EmailFieldOnIdentityDataJson = JSON.stringify(EmailFieldOnIdentityData);
|
||||||
|
const result = await importer.parse(EmailFieldOnIdentityDataJson);
|
||||||
|
expect(result != null).toBe(true);
|
||||||
|
|
||||||
|
const ciphers = result.ciphers;
|
||||||
|
expect(ciphers.length).toEqual(1);
|
||||||
|
const cipher = ciphers.shift();
|
||||||
|
|
||||||
|
const identity = cipher.identity;
|
||||||
|
expect(identity.email).toEqual("gengels@nullvalue.test");
|
||||||
|
|
||||||
|
expect(cipher.fields[0].name).toEqual("provider");
|
||||||
|
expect(cipher.fields[0].value).toEqual("myEmailProvider");
|
||||||
|
expect(cipher.fields[0].type).toBe(FieldType.Text);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("emails fields on identity types should be added to custom fields if identity.email has been filled", async () => {
|
||||||
|
const importer = new Importer();
|
||||||
|
const EmailFieldOnIdentityPrefilledDataJson = JSON.stringify(EmailFieldOnIdentityPrefilledData);
|
||||||
|
const result = await importer.parse(EmailFieldOnIdentityPrefilledDataJson);
|
||||||
|
expect(result != null).toBe(true);
|
||||||
|
|
||||||
|
const ciphers = result.ciphers;
|
||||||
|
expect(ciphers.length).toEqual(1);
|
||||||
|
const cipher = ciphers.shift();
|
||||||
|
|
||||||
|
const identity = cipher.identity;
|
||||||
|
expect(identity.email).toEqual("gengels@nullvalue.test");
|
||||||
|
|
||||||
|
expect(cipher.fields[0].name).toEqual("2nd_email");
|
||||||
|
expect(cipher.fields[0].value).toEqual("kriddler@nullvalue.test");
|
||||||
|
expect(cipher.fields[0].type).toBe(FieldType.Text);
|
||||||
|
|
||||||
|
expect(cipher.fields[1].name).toEqual("provider");
|
||||||
|
expect(cipher.fields[1].value).toEqual("myEmailProvider");
|
||||||
|
expect(cipher.fields[1].type).toBe(FieldType.Text);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should parse category 005 - Password (Legacy)", async () => {
|
||||||
|
const importer = new Importer();
|
||||||
|
const jsonString = JSON.stringify(PasswordData);
|
||||||
|
const result = await importer.parse(jsonString);
|
||||||
|
expect(result != null).toBe(true);
|
||||||
|
const cipher = result.ciphers.shift();
|
||||||
|
expect(cipher.type).toEqual(CipherType.Login);
|
||||||
|
expect(cipher.name).toEqual("SuperSecret Password");
|
||||||
|
expect(cipher.notes).toEqual("SuperSecret Password Notes");
|
||||||
|
|
||||||
|
expect(cipher.login.password).toEqual("GBq[AGb]4*Si3tjwuab^");
|
||||||
|
expect(cipher.login.uri).toEqual("https://n0t.y0ur.n0rm4l.w3bs1t3");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should parse category 100 - SoftwareLicense", async () => {
|
||||||
|
const importer = new Importer();
|
||||||
|
const jsonString = JSON.stringify(SoftwareLicenseData);
|
||||||
|
const result = await importer.parse(jsonString);
|
||||||
|
expect(result != null).toBe(true);
|
||||||
|
const cipher = result.ciphers.shift();
|
||||||
|
expect(cipher.type).toEqual(CipherType.SecureNote);
|
||||||
|
expect(cipher.name).toEqual("Limux Product Key");
|
||||||
|
expect(cipher.notes).toEqual("My Software License");
|
||||||
|
|
||||||
|
expect(cipher.fields.length).toEqual(13);
|
||||||
|
validateCustomField(cipher.fields, "product_version", "5.10.1000");
|
||||||
|
validateCustomField(cipher.fields, "reg_code", "265453-13457355-847327");
|
||||||
|
validateCustomField(cipher.fields, "reg_name", "Kay Riddler");
|
||||||
|
validateCustomField(cipher.fields, "reg_email", "kriddler@nullvalue.test");
|
||||||
|
validateCustomField(cipher.fields, "company", "Riddles and Jigsaw Puzzles GmbH");
|
||||||
|
validateCustomField(
|
||||||
|
cipher.fields,
|
||||||
|
"download_link",
|
||||||
|
"https://limuxcompany.nullvalue.test/5.10.1000/isos"
|
||||||
|
);
|
||||||
|
validateCustomField(cipher.fields, "publisher_name", "Limux Software and Hardware");
|
||||||
|
validateCustomField(cipher.fields, "publisher_website", "https://limuxcompany.nullvalue.test/");
|
||||||
|
validateCustomField(cipher.fields, "retail_price", "$999");
|
||||||
|
validateCustomField(cipher.fields, "support_email", "support@nullvalue.test");
|
||||||
|
validateCustomField(cipher.fields, "order_date", "Thu, 01 Apr 2021 12:01:00 GMT");
|
||||||
|
validateCustomField(cipher.fields, "order_number", "594839");
|
||||||
|
validateCustomField(cipher.fields, "order_total", "$1086.59");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should parse category 101 - BankAccount", async () => {
|
||||||
|
const importer = new Importer();
|
||||||
|
const jsonString = JSON.stringify(BankAccountData);
|
||||||
|
const result = await importer.parse(jsonString);
|
||||||
|
expect(result != null).toBe(true);
|
||||||
|
const cipher = result.ciphers.shift();
|
||||||
|
expect(cipher.type).toEqual(CipherType.Card);
|
||||||
|
expect(cipher.name).toEqual("Bank Account");
|
||||||
|
expect(cipher.notes).toEqual("My Bank Account");
|
||||||
|
|
||||||
|
expect(cipher.card.cardholderName).toEqual("Cool Guy");
|
||||||
|
|
||||||
|
expect(cipher.fields.length).toEqual(9);
|
||||||
|
validateCustomField(cipher.fields, "bankName", "Super Credit Union");
|
||||||
|
validateCustomField(cipher.fields, "accountType", "checking");
|
||||||
|
validateCustomField(cipher.fields, "routingNo", "111000999");
|
||||||
|
validateCustomField(cipher.fields, "accountNo", "192837465918273645");
|
||||||
|
validateCustomField(cipher.fields, "swift", "123456");
|
||||||
|
validateCustomField(cipher.fields, "iban", "DE12 123456");
|
||||||
|
validateCustomField(cipher.fields, "telephonePin", "5555");
|
||||||
|
validateCustomField(cipher.fields, "branchPhone", "9399399933");
|
||||||
|
validateCustomField(cipher.fields, "branchAddress", "1 Fifth Avenue");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should parse category 102 - Database", async () => {
|
||||||
|
const importer = new Importer();
|
||||||
|
const jsonString = JSON.stringify(DatabaseData);
|
||||||
|
const result = await importer.parse(jsonString);
|
||||||
|
expect(result != null).toBe(true);
|
||||||
|
const cipher = result.ciphers.shift();
|
||||||
|
|
||||||
|
expect(cipher.type).toEqual(CipherType.Login);
|
||||||
|
expect(cipher.name).toEqual("Database");
|
||||||
|
expect(cipher.notes).toEqual("My Database");
|
||||||
|
|
||||||
|
const login = cipher.login;
|
||||||
|
expect(login.username).toEqual("cooldbuser");
|
||||||
|
expect(login.password).toEqual("^+kTjhLaN7wVPAhGU)*J");
|
||||||
|
|
||||||
|
expect(cipher.fields.length).toEqual(7);
|
||||||
|
validateCustomField(cipher.fields, "database_type", "postgresql");
|
||||||
|
validateCustomField(cipher.fields, "hostname", "my.secret.db.server");
|
||||||
|
validateCustomField(cipher.fields, "port", "1337");
|
||||||
|
validateCustomField(cipher.fields, "database", "user_database");
|
||||||
|
validateCustomField(cipher.fields, "sid", "ASDIUFU-283234");
|
||||||
|
validateCustomField(cipher.fields, "alias", "cdbu");
|
||||||
|
validateCustomField(cipher.fields, "options", "ssh");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should parse category 103 - Drivers license", async () => {
|
||||||
|
const importer = new Importer();
|
||||||
|
const jsonString = JSON.stringify(DriversLicenseData);
|
||||||
|
const result = await importer.parse(jsonString);
|
||||||
|
expect(result != null).toBe(true);
|
||||||
|
const cipher = result.ciphers.shift();
|
||||||
|
expect(cipher.name).toEqual("Michael Scarn");
|
||||||
|
expect(cipher.subTitle).toEqual("Michael Scarn");
|
||||||
|
expect(cipher.notes).toEqual("My Driver's License");
|
||||||
|
|
||||||
|
const identity = cipher.identity;
|
||||||
|
expect(identity.firstName).toEqual("Michael");
|
||||||
|
expect(identity.middleName).toBeNull();
|
||||||
|
expect(identity.lastName).toEqual("Scarn");
|
||||||
|
expect(identity.address1).toEqual("2120 Mifflin Rd.");
|
||||||
|
expect(identity.state).toEqual("Pennsylvania");
|
||||||
|
expect(identity.country).toEqual("United States");
|
||||||
|
expect(identity.licenseNumber).toEqual("12345678901");
|
||||||
|
|
||||||
|
expect(cipher.fields.length).toEqual(6);
|
||||||
|
validateCustomField(cipher.fields, "birthdate", "Sun, 01 Jan 1978 12:01:00 GMT");
|
||||||
|
validateCustomField(cipher.fields, "sex", "male");
|
||||||
|
validateCustomField(cipher.fields, "height", "5'11\"");
|
||||||
|
validateCustomField(cipher.fields, "class", "C");
|
||||||
|
validateCustomField(cipher.fields, "conditions", "B");
|
||||||
|
validateCustomField(cipher.fields, "expiry_date", "203012");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should parse category 104 - Outdoor License", async () => {
|
||||||
|
const importer = new Importer();
|
||||||
|
const jsonString = JSON.stringify(OutdoorLicenseData);
|
||||||
|
const result = await importer.parse(jsonString);
|
||||||
|
expect(result != null).toBe(true);
|
||||||
|
const cipher = result.ciphers.shift();
|
||||||
|
|
||||||
|
expect(cipher.type).toEqual(CipherType.Identity);
|
||||||
|
expect(cipher.name).toEqual("Harvest License");
|
||||||
|
expect(cipher.subTitle).toEqual("Cash Bandit");
|
||||||
|
expect(cipher.notes).toEqual("My Outdoor License");
|
||||||
|
|
||||||
|
const identity = cipher.identity;
|
||||||
|
expect(identity.firstName).toEqual("Cash");
|
||||||
|
expect(identity.middleName).toBeNull();
|
||||||
|
expect(identity.lastName).toEqual("Bandit");
|
||||||
|
expect(identity.state).toEqual("Washington");
|
||||||
|
expect(identity.country).toEqual("United States of America");
|
||||||
|
|
||||||
|
expect(cipher.fields.length).toEqual(4);
|
||||||
|
validateCustomField(cipher.fields, "valid_from", "Thu, 01 Apr 2021 12:01:00 GMT");
|
||||||
|
validateCustomField(cipher.fields, "expires", "Fri, 01 Apr 2044 12:01:00 GMT");
|
||||||
|
validateCustomField(cipher.fields, "game", "Bananas,blueberries,corn");
|
||||||
|
validateCustomField(cipher.fields, "quota", "100/each");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should parse category 105 - Membership", async () => {
|
||||||
|
const importer = new Importer();
|
||||||
|
const jsonString = JSON.stringify(MembershipData);
|
||||||
|
const result = await importer.parse(jsonString);
|
||||||
|
expect(result != null).toBe(true);
|
||||||
|
const cipher = result.ciphers.shift();
|
||||||
|
|
||||||
|
expect(cipher.type).toEqual(CipherType.Identity);
|
||||||
|
expect(cipher.name).toEqual("Library Card");
|
||||||
|
|
||||||
|
const identity = cipher.identity;
|
||||||
|
expect(identity.firstName).toEqual("George");
|
||||||
|
expect(identity.middleName).toBeNull();
|
||||||
|
expect(identity.lastName).toEqual("Engels");
|
||||||
|
expect(identity.company).toEqual("National Public Library");
|
||||||
|
expect(identity.phone).toEqual("9995555555");
|
||||||
|
|
||||||
|
expect(cipher.fields.length).toEqual(5);
|
||||||
|
validateCustomField(cipher.fields, "website", "https://npl.nullvalue.gov.test");
|
||||||
|
validateCustomField(cipher.fields, "member_since", "199901");
|
||||||
|
validateCustomField(cipher.fields, "expiry_date", "203412");
|
||||||
|
validateCustomField(cipher.fields, "membership_no", "64783862");
|
||||||
|
validateCustomField(cipher.fields, "pin", "19191");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should parse category 106 - Passport", async () => {
|
||||||
|
const importer = new Importer();
|
||||||
|
const jsonString = JSON.stringify(PassportData);
|
||||||
|
const result = await importer.parse(jsonString);
|
||||||
|
expect(result != null).toBe(true);
|
||||||
|
const cipher = result.ciphers.shift();
|
||||||
|
|
||||||
|
expect(cipher.type).toEqual(CipherType.Identity);
|
||||||
|
expect(cipher.name).toEqual("Mr. Globewide");
|
||||||
|
|
||||||
|
const identity = cipher.identity;
|
||||||
|
expect(identity.firstName).toEqual("David");
|
||||||
|
expect(identity.middleName).toBeNull();
|
||||||
|
expect(identity.lastName).toEqual("Global");
|
||||||
|
expect(identity.passportNumber).toEqual("76436847");
|
||||||
|
|
||||||
|
expect(cipher.fields.length).toEqual(8);
|
||||||
|
validateCustomField(cipher.fields, "type", "US Passport");
|
||||||
|
validateCustomField(cipher.fields, "sex", "female");
|
||||||
|
validateCustomField(cipher.fields, "nationality", "International");
|
||||||
|
validateCustomField(cipher.fields, "issuing_authority", "Department of State");
|
||||||
|
validateCustomField(cipher.fields, "birthdate", "Fri, 01 Apr 1983 12:01:00 GMT");
|
||||||
|
validateCustomField(cipher.fields, "birthplace", "A cave somewhere in Maine");
|
||||||
|
validateCustomField(cipher.fields, "issue_date", "Wed, 01 Jan 2020 12:01:00 GMT");
|
||||||
|
validateCustomField(cipher.fields, "expiry_date", "Sat, 01 Jan 2050 12:01:00 GMT");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should parse category 107 - RewardsProgram", async () => {
|
||||||
|
const importer = new Importer();
|
||||||
|
const jsonString = JSON.stringify(RewardsProgramData);
|
||||||
|
const result = await importer.parse(jsonString);
|
||||||
|
expect(result != null).toBe(true);
|
||||||
|
const cipher = result.ciphers.shift();
|
||||||
|
|
||||||
|
expect(cipher.type).toEqual(CipherType.Identity);
|
||||||
|
expect(cipher.name).toEqual("Retail Reward Thing");
|
||||||
|
|
||||||
|
const identity = cipher.identity;
|
||||||
|
expect(identity.firstName).toEqual("Chef");
|
||||||
|
expect(identity.middleName).toBeNull();
|
||||||
|
expect(identity.lastName).toEqual("Coldroom");
|
||||||
|
expect(identity.company).toEqual("Super Cool Store Co.");
|
||||||
|
|
||||||
|
expect(cipher.fields.length).toEqual(7);
|
||||||
|
validateCustomField(cipher.fields, "membership_no", "member-29813569");
|
||||||
|
validateCustomField(cipher.fields, "pin", "99913");
|
||||||
|
validateCustomField(cipher.fields, "additional_no", "additional member id");
|
||||||
|
validateCustomField(cipher.fields, "member_since", "202101");
|
||||||
|
validateCustomField(cipher.fields, "customer_service_phone", "123456");
|
||||||
|
validateCustomField(cipher.fields, "reservations_phone", "123456");
|
||||||
|
validateCustomField(cipher.fields, "website", "supercoolstore.com");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should parse category 108 - SSN", async () => {
|
||||||
|
const importer = new Importer();
|
||||||
|
const jsonString = JSON.stringify(SSNData);
|
||||||
|
const result = await importer.parse(jsonString);
|
||||||
|
expect(result != null).toBe(true);
|
||||||
|
const cipher = result.ciphers.shift();
|
||||||
|
expect(cipher.name).toEqual("SSN");
|
||||||
|
|
||||||
|
const identity = cipher.identity;
|
||||||
|
expect(identity.firstName).toEqual("Jack");
|
||||||
|
expect(identity.middleName).toBeNull();
|
||||||
|
expect(identity.lastName).toEqual("Judd");
|
||||||
|
expect(identity.ssn).toEqual("131-216-1900");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should parse category 109 - WirelessRouter", async () => {
|
||||||
|
const importer = new Importer();
|
||||||
|
const jsonString = JSON.stringify(WirelessRouterData);
|
||||||
|
const result = await importer.parse(jsonString);
|
||||||
|
expect(result != null).toBe(true);
|
||||||
|
const cipher = result.ciphers.shift();
|
||||||
|
|
||||||
|
expect(cipher.type).toEqual(CipherType.Login);
|
||||||
|
expect(cipher.name).toEqual("Wireless Router");
|
||||||
|
expect(cipher.notes).toEqual("My Wifi Router Config");
|
||||||
|
|
||||||
|
expect(cipher.login.password).toEqual("BqatGTVQ9TCN72tLbjrsHqkb");
|
||||||
|
|
||||||
|
expect(cipher.fields.length).toEqual(7);
|
||||||
|
validateCustomField(cipher.fields, "name", "pixel 2Xl");
|
||||||
|
validateCustomField(cipher.fields, "server", "127.0.0.1");
|
||||||
|
validateCustomField(cipher.fields, "airport_id", "some airportId");
|
||||||
|
validateCustomField(cipher.fields, "network_name", "some network name");
|
||||||
|
validateCustomField(cipher.fields, "wireless_security", "WPA");
|
||||||
|
validateCustomField(cipher.fields, "wireless_password", "wifipassword");
|
||||||
|
validateCustomField(cipher.fields, "disk_password", "diskpassword");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should parse category 110 - Server", async () => {
|
||||||
|
const importer = new Importer();
|
||||||
|
const jsonString = JSON.stringify(ServerData);
|
||||||
|
const result = await importer.parse(jsonString);
|
||||||
|
expect(result != null).toBe(true);
|
||||||
|
const cipher = result.ciphers.shift();
|
||||||
|
|
||||||
|
expect(cipher.type).toEqual(CipherType.Login);
|
||||||
|
expect(cipher.name).toEqual("Super Cool Server");
|
||||||
|
expect(cipher.notes).toEqual("My Server");
|
||||||
|
|
||||||
|
expect(cipher.login.username).toEqual("frankly-notsure");
|
||||||
|
expect(cipher.login.password).toEqual("*&YHJI87yjy78u");
|
||||||
|
expect(cipher.login.uri).toEqual("https://coolserver.nullvalue.test");
|
||||||
|
|
||||||
|
expect(cipher.fields.length).toEqual(7);
|
||||||
|
validateCustomField(
|
||||||
|
cipher.fields,
|
||||||
|
"admin_console_url",
|
||||||
|
"https://coolserver.nullvalue.test/admin"
|
||||||
|
);
|
||||||
|
validateCustomField(cipher.fields, "admin_console_username", "frankly-idontknowwhatimdoing");
|
||||||
|
validateCustomField(cipher.fields, "admin_console_password", "^%RY&^YUiju8iUYHJI(U");
|
||||||
|
validateCustomField(cipher.fields, "name", "Private Hosting Provider Inc.");
|
||||||
|
validateCustomField(cipher.fields, "website", "https://phpi.nullvalue.test");
|
||||||
|
validateCustomField(
|
||||||
|
cipher.fields,
|
||||||
|
"support_contact_url",
|
||||||
|
"https://phpi.nullvalue.test/support"
|
||||||
|
);
|
||||||
|
validateCustomField(cipher.fields, "support_contact_phone", "8882569382");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should parse category 111 - EmailAccount", async () => {
|
||||||
|
const importer = new Importer();
|
||||||
|
const jsonString = JSON.stringify(EmailAccountData);
|
||||||
|
const result = await importer.parse(jsonString);
|
||||||
|
expect(result != null).toBe(true);
|
||||||
|
const cipher = result.ciphers.shift();
|
||||||
|
|
||||||
|
expect(cipher.type).toEqual(CipherType.SecureNote);
|
||||||
|
expect(cipher.name).toEqual("Email Config");
|
||||||
|
expect(cipher.notes).toEqual("My Email Config");
|
||||||
|
|
||||||
|
expect(cipher.fields.length).toEqual(17);
|
||||||
|
validateCustomField(cipher.fields, "pop_type", "either");
|
||||||
|
validateCustomField(cipher.fields, "pop_username", "someuser@nullvalue.test");
|
||||||
|
validateCustomField(cipher.fields, "pop_server", "mailserver.nullvalue.test");
|
||||||
|
validateCustomField(cipher.fields, "pop_port", "587");
|
||||||
|
validateCustomField(cipher.fields, "pop_password", "u1jsf<UI*&YU&^T");
|
||||||
|
validateCustomField(cipher.fields, "pop_security", "TLS");
|
||||||
|
validateCustomField(cipher.fields, "pop_authentication", "kerberos_v5");
|
||||||
|
validateCustomField(cipher.fields, "smtp_server", "mailserver.nullvalue.test");
|
||||||
|
validateCustomField(cipher.fields, "smtp_port", "589");
|
||||||
|
validateCustomField(cipher.fields, "smtp_username", "someuser@nullvalue.test");
|
||||||
|
validateCustomField(cipher.fields, "smtp_password", "(*1674%^UIUJ*UI(IUI8u98uyy");
|
||||||
|
validateCustomField(cipher.fields, "smtp_security", "TLS");
|
||||||
|
validateCustomField(cipher.fields, "smtp_authentication", "password");
|
||||||
|
validateCustomField(cipher.fields, "provider", "Telum");
|
||||||
|
validateCustomField(cipher.fields, "provider_website", "https://telum.nullvalue.test");
|
||||||
|
validateCustomField(cipher.fields, "phone_local", "2346666666");
|
||||||
|
validateCustomField(cipher.fields, "phone_tollfree", "18005557777");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should parse category 112 - API Credentials", async () => {
|
||||||
|
const importer = new Importer();
|
||||||
|
const jsonString = JSON.stringify(APICredentialsData);
|
||||||
|
const result = await importer.parse(jsonString);
|
||||||
|
expect(result != null).toBe(true);
|
||||||
|
const cipher = result.ciphers.shift();
|
||||||
|
|
||||||
|
expect(cipher.type).toEqual(CipherType.Login);
|
||||||
|
expect(cipher.name).toEqual("API Credential");
|
||||||
|
expect(cipher.notes).toEqual("My API Credential");
|
||||||
|
|
||||||
|
expect(cipher.login.username).toEqual("apiuser@nullvalue.test");
|
||||||
|
expect(cipher.login.password).toEqual("apiapiapiapiapiapiappy");
|
||||||
|
expect(cipher.login.uri).toEqual("http://not.your.everyday.hostname");
|
||||||
|
|
||||||
|
expect(cipher.fields.length).toEqual(4);
|
||||||
|
validateCustomField(cipher.fields, "type", "jwt");
|
||||||
|
validateCustomField(cipher.fields, "filename", "filename.jwt");
|
||||||
|
validateCustomField(cipher.fields, "validFrom", "Mon, 04 Apr 2011 12:01:00 GMT");
|
||||||
|
validateCustomField(cipher.fields, "expires", "Tue, 01 Apr 2031 12:01:00 GMT");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should create secure notes", async () => {
|
||||||
|
const importer = new Importer();
|
||||||
|
const result = await importer.parse(SecureNoteDataJson);
|
||||||
|
expect(result != null).toBe(true);
|
||||||
|
const cipher = result.ciphers.shift();
|
||||||
|
|
||||||
|
expect(cipher.name).toEqual("Secure Note #1");
|
||||||
|
expect(cipher.notes).toEqual(
|
||||||
|
"This is my secure note. \n\nLorem ipsum expecto patronum. \nThe quick brown fox jumped over the lazy dog."
|
||||||
|
);
|
||||||
|
expect(cipher.secureNote.type).toEqual(SecureNoteType.Generic);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should parse category 113 - Medical Record", async () => {
|
||||||
|
const importer = new Importer();
|
||||||
|
const jsonString = JSON.stringify(MedicalRecordData);
|
||||||
|
const result = await importer.parse(jsonString);
|
||||||
|
expect(result != null).toBe(true);
|
||||||
|
const cipher = result.ciphers.shift();
|
||||||
|
expect(cipher.type).toEqual(CipherType.SecureNote);
|
||||||
|
expect(cipher.name).toEqual("Some Health Record");
|
||||||
|
expect(cipher.notes).toEqual("Some notes about my medical history");
|
||||||
|
expect(cipher.secureNote.type).toEqual(SecureNoteType.Generic);
|
||||||
|
|
||||||
|
expect(cipher.fields.length).toEqual(8);
|
||||||
|
validateCustomField(cipher.fields, "date", "Sat, 01 Jan 2022 12:01:00 GMT");
|
||||||
|
validateCustomField(cipher.fields, "location", "some hospital/clinic");
|
||||||
|
validateCustomField(cipher.fields, "healthcareprofessional", "Some Doctor");
|
||||||
|
validateCustomField(cipher.fields, "patient", "Me");
|
||||||
|
validateCustomField(cipher.fields, "reason", "unwell");
|
||||||
|
validateCustomField(cipher.fields, "medication", "Insuline");
|
||||||
|
validateCustomField(cipher.fields, "dosage", "1");
|
||||||
|
validateCustomField(cipher.fields, "notes", "multiple times a day");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should create folders", async () => {
|
||||||
|
const importer = new Importer();
|
||||||
|
const result = await importer.parse(SanitizedExportJson);
|
||||||
|
expect(result != null).toBe(true);
|
||||||
|
|
||||||
|
const folders = result.folders;
|
||||||
|
expect(folders.length).toBe(5);
|
||||||
|
expect(folders[0].name).toBe("Movies");
|
||||||
|
expect(folders[1].name).toBe("Finance");
|
||||||
|
expect(folders[2].name).toBe("Travel");
|
||||||
|
expect(folders[3].name).toBe("Education");
|
||||||
|
expect(folders[4].name).toBe("Starter Kit");
|
||||||
|
|
||||||
|
// Check that ciphers have a folder assigned to them
|
||||||
|
expect(result.ciphers.filter((c) => c.folderId === folders[0].id).length).toBeGreaterThan(0);
|
||||||
|
expect(result.ciphers.filter((c) => c.folderId === folders[1].id).length).toBeGreaterThan(0);
|
||||||
|
expect(result.ciphers.filter((c) => c.folderId === folders[2].id).length).toBeGreaterThan(0);
|
||||||
|
expect(result.ciphers.filter((c) => c.folderId === folders[3].id).length).toBeGreaterThan(0);
|
||||||
|
expect(result.ciphers.filter((c) => c.folderId === folders[4].id).length).toBeGreaterThan(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should create collections if part of an organization", async () => {
|
||||||
|
const importer = new Importer();
|
||||||
|
importer.organizationId = Utils.newGuid();
|
||||||
|
const result = await importer.parse(SanitizedExportJson);
|
||||||
|
expect(result != null).toBe(true);
|
||||||
|
|
||||||
|
const collections = result.collections;
|
||||||
|
expect(collections.length).toBe(5);
|
||||||
|
expect(collections[0].name).toBe("Movies");
|
||||||
|
expect(collections[1].name).toBe("Finance");
|
||||||
|
expect(collections[2].name).toBe("Travel");
|
||||||
|
expect(collections[3].name).toBe("Education");
|
||||||
|
expect(collections[4].name).toBe("Starter Kit");
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
import { CipherType } from "jslib-common/enums/cipherType";
|
||||||
|
import { OnePasswordMacCsvImporter as Importer } from "jslib-common/importers/onepasswordImporters/onepasswordMacCsvImporter";
|
||||||
|
import { CipherView } from "jslib-common/models/view/cipherView";
|
||||||
|
|
||||||
|
import { data as creditCardData } from "./testData/onePasswordCsv/creditCard.mac.csv";
|
||||||
|
import { data as identityData } from "./testData/onePasswordCsv/identity.mac.csv";
|
||||||
|
import { data as multiTypeData } from "./testData/onePasswordCsv/multipleItems.mac.csv";
|
||||||
|
|
||||||
|
function expectIdentity(cipher: CipherView) {
|
||||||
|
expect(cipher.type).toBe(CipherType.Identity);
|
||||||
|
|
||||||
|
expect(cipher.identity).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
firstName: "first name",
|
||||||
|
middleName: "mi",
|
||||||
|
lastName: "last name",
|
||||||
|
username: "userNam3",
|
||||||
|
company: "bitwarden",
|
||||||
|
phone: "8005555555",
|
||||||
|
email: "email@bitwarden.com",
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(cipher.notes).toContain("address\ncity state zip\nUnited States");
|
||||||
|
}
|
||||||
|
|
||||||
|
function expectCreditCard(cipher: CipherView) {
|
||||||
|
expect(cipher.type).toBe(CipherType.Card);
|
||||||
|
|
||||||
|
expect(cipher.card).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
number: "4111111111111111",
|
||||||
|
code: "111",
|
||||||
|
cardholderName: "test",
|
||||||
|
expMonth: "1",
|
||||||
|
expYear: "2030",
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("1Password mac CSV Importer", () => {
|
||||||
|
it("should parse identity records", async () => {
|
||||||
|
const importer = new Importer();
|
||||||
|
const result = await importer.parse(identityData);
|
||||||
|
|
||||||
|
expect(result).not.toBeNull();
|
||||||
|
expect(result.success).toBe(true);
|
||||||
|
expect(result.ciphers.length).toBe(1);
|
||||||
|
const cipher = result.ciphers[0];
|
||||||
|
expectIdentity(cipher);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should parse credit card records", async () => {
|
||||||
|
const importer = new Importer();
|
||||||
|
const result = await importer.parse(creditCardData);
|
||||||
|
|
||||||
|
expect(result).not.toBeNull();
|
||||||
|
expect(result.success).toBe(true);
|
||||||
|
expect(result.ciphers.length).toBe(1);
|
||||||
|
const cipher = result.ciphers[0];
|
||||||
|
expectCreditCard(cipher);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should parse csv's with multiple record type", async () => {
|
||||||
|
const importer = new Importer();
|
||||||
|
const result = await importer.parse(multiTypeData);
|
||||||
|
|
||||||
|
expect(result).not.toBeNull();
|
||||||
|
expect(result.success).toBe(true);
|
||||||
|
expect(result.ciphers.length).toBe(4);
|
||||||
|
expectIdentity(result.ciphers[1]);
|
||||||
|
expectCreditCard(result.ciphers[2]);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,87 @@
|
|||||||
|
import { CipherType } from "jslib-common/enums/cipherType";
|
||||||
|
import { FieldType } from "jslib-common/enums/fieldType";
|
||||||
|
import { OnePasswordWinCsvImporter as Importer } from "jslib-common/importers/onepasswordImporters/onepasswordWinCsvImporter";
|
||||||
|
import { CipherView } from "jslib-common/models/view/cipherView";
|
||||||
|
import { FieldView } from "jslib-common/models/view/fieldView";
|
||||||
|
|
||||||
|
import { data as creditCardData } from "./testData/onePasswordCsv/creditCard.windows.csv";
|
||||||
|
import { data as identityData } from "./testData/onePasswordCsv/identity.windows.csv";
|
||||||
|
import { data as multiTypeData } from "./testData/onePasswordCsv/multipleItems.windows.csv";
|
||||||
|
|
||||||
|
function expectIdentity(cipher: CipherView) {
|
||||||
|
expect(cipher.type).toBe(CipherType.Identity);
|
||||||
|
|
||||||
|
expect(cipher.identity).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
firstName: "first name",
|
||||||
|
middleName: "mi",
|
||||||
|
lastName: "last name",
|
||||||
|
username: "userNam3",
|
||||||
|
company: "bitwarden",
|
||||||
|
phone: "8005555555",
|
||||||
|
email: "email@bitwarden.com",
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(cipher.fields).toEqual(
|
||||||
|
expect.arrayContaining([
|
||||||
|
Object.assign(new FieldView(), {
|
||||||
|
type: FieldType.Text,
|
||||||
|
name: "address",
|
||||||
|
value: "address city state zip us",
|
||||||
|
}),
|
||||||
|
])
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function expectCreditCard(cipher: CipherView) {
|
||||||
|
expect(cipher.type).toBe(CipherType.Card);
|
||||||
|
|
||||||
|
expect(cipher.card).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
number: "4111111111111111",
|
||||||
|
code: "111",
|
||||||
|
cardholderName: "test",
|
||||||
|
expMonth: "1",
|
||||||
|
expYear: "1970",
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("1Password windows CSV Importer", () => {
|
||||||
|
let importer: Importer;
|
||||||
|
beforeEach(() => {
|
||||||
|
importer = new Importer();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should parse identity records", async () => {
|
||||||
|
const result = await importer.parse(identityData);
|
||||||
|
|
||||||
|
expect(result).not.toBeNull();
|
||||||
|
expect(result.success).toBe(true);
|
||||||
|
expect(result.ciphers.length).toBe(1);
|
||||||
|
const cipher = result.ciphers[0];
|
||||||
|
expectIdentity(cipher);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should parse credit card records", async () => {
|
||||||
|
const result = await importer.parse(creditCardData);
|
||||||
|
|
||||||
|
expect(result).not.toBeNull();
|
||||||
|
expect(result.success).toBe(true);
|
||||||
|
expect(result.ciphers.length).toBe(1);
|
||||||
|
const cipher = result.ciphers[0];
|
||||||
|
expectCreditCard(cipher);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should parse csv's with multiple record types", async () => {
|
||||||
|
const result = await importer.parse(multiTypeData);
|
||||||
|
|
||||||
|
expect(result).not.toBeNull();
|
||||||
|
expect(result.success).toBe(true);
|
||||||
|
expect(result.ciphers.length).toBe(4);
|
||||||
|
|
||||||
|
expectIdentity(result.ciphers[1]);
|
||||||
|
expectCreditCard(result.ciphers[2]);
|
||||||
|
});
|
||||||
|
});
|
||||||
74
jslib/common/spec/importers/safariCsvImporter.spec.ts
Normal file
74
jslib/common/spec/importers/safariCsvImporter.spec.ts
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
import { SafariCsvImporter as Importer } from "jslib-common/importers/safariCsvImporter";
|
||||||
|
import { CipherView } from "jslib-common/models/view/cipherView";
|
||||||
|
import { LoginUriView } from "jslib-common/models/view/loginUriView";
|
||||||
|
import { LoginView } from "jslib-common/models/view/loginView";
|
||||||
|
|
||||||
|
import { data as oldSimplePasswordData } from "./testData/safariCsv/oldSimplePasswordData.csv";
|
||||||
|
import { data as simplePasswordData } from "./testData/safariCsv/simplePasswordData.csv";
|
||||||
|
|
||||||
|
const CipherData = [
|
||||||
|
{
|
||||||
|
title: "should parse URLs in new CSV format",
|
||||||
|
csv: simplePasswordData,
|
||||||
|
expected: Object.assign(new CipherView(), {
|
||||||
|
id: null,
|
||||||
|
organizationId: null,
|
||||||
|
folderId: null,
|
||||||
|
name: "example.com (example_user)",
|
||||||
|
login: Object.assign(new LoginView(), {
|
||||||
|
username: "example_user",
|
||||||
|
password: "example_p@ssword",
|
||||||
|
uris: [
|
||||||
|
Object.assign(new LoginUriView(), {
|
||||||
|
uri: "https://example.com",
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
totp: "otpauth://totp/test?secret=examplesecret",
|
||||||
|
}),
|
||||||
|
notes: "Example note\nMore notes on new line",
|
||||||
|
type: 1,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "should parse URLs in old CSV format",
|
||||||
|
csv: oldSimplePasswordData,
|
||||||
|
expected: Object.assign(new CipherView(), {
|
||||||
|
id: null,
|
||||||
|
organizationId: null,
|
||||||
|
folderId: null,
|
||||||
|
name: "example.com (example_user)",
|
||||||
|
login: Object.assign(new LoginView(), {
|
||||||
|
username: "example_user",
|
||||||
|
password: "example_p@ssword",
|
||||||
|
uris: [
|
||||||
|
Object.assign(new LoginUriView(), {
|
||||||
|
uri: "https://example.com",
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
type: 1,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
describe("Safari CSV Importer", () => {
|
||||||
|
CipherData.forEach((data) => {
|
||||||
|
it(data.title, async () => {
|
||||||
|
const importer = new Importer();
|
||||||
|
const result = await importer.parse(data.csv);
|
||||||
|
expect(result != null).toBe(true);
|
||||||
|
expect(result.ciphers.length).toBeGreaterThan(0);
|
||||||
|
|
||||||
|
const cipher = result.ciphers.shift();
|
||||||
|
let property: keyof typeof data.expected;
|
||||||
|
for (property in data.expected) {
|
||||||
|
// eslint-disable-next-line
|
||||||
|
if (data.expected.hasOwnProperty(property)) {
|
||||||
|
// eslint-disable-next-line
|
||||||
|
expect(cipher.hasOwnProperty(property)).toBe(true);
|
||||||
|
expect(cipher[property]).toEqual(data.expected[property]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export const data = '{"encrypted":false,"folders":[],"items":[]}';
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
export const data = `{
|
||||||
|
"encrypted": true,
|
||||||
|
"passwordProtected": true,
|
||||||
|
"salt": "Oy0xcgVRzxQ+9NpB5GLehw==",
|
||||||
|
"kdfIterations": 100000,
|
||||||
|
"kdfType": 0,
|
||||||
|
"encKeyValidation_DO_NOT_EDIT": "2.sZs4Jc1HW9rhABzRRYR/gQ==|8kTDaDxafulnybpWoqVX8RAybhVRTr+dffNjms271Y7amQmIE1VSMwLbk+b2vxZb|IqOo6oXQtmv/Xb/GHDi42XG9c9ILePYtP5qq584VWcg=",
|
||||||
|
"data": "2.D0AXAf7G/XIwq6EC7A0Suw==|4w+m0wHRo25y1T1Syh5wdAUyF8voqEy54waMEsbnK0Nzee959w54ru5D1NntvxZL4HFqkQLyR6jCFkn5g40f+MGJgihS/wvf4NcJJfLiiFo6MEDOQNBkxw7ZBGuHiKfVuBO5u36JgzQtZ8lyFaduGxFszuF5c+URiE9PDh9jY0//poVgHKwuLZuYFIW+f7h6T+shUWK0ya11lcHn/B/CA2xiI+YiKdNZreJrwN0yslpJ/f+MrOzagvftRjt0GNkwveCtwcYUw/zFvqvibUpKeHcRiXs8SaGoHJ5RTm69FbJ7C5tnLwoVT89Af156uvRAXV7yAC4oPcbU/3TGb6hqYosvi1QNyaqG3M9gxS6+AK0C4yWuNbMLDEr+MWiw0SWLVMKQEkCZ4oM+oTCx52otW3+2V9I8Pv3KmmhkvVvE4wBdweOJeRX53Tf5ySkmpIhCfzj6JMmxO+nmTXIhWnJChr4hPVh+ixv1GQK5thIPTCMXmAtXoTIFUx1KWjS6LjOdi2hKQueVI+XZjf0qnY2vTMxRg0ZsLBA2znQTx+DSEqumORb5T/lV73pWZiCNePSAE2msOm7tep+lm4O/VCViCfXjITAY196syhOK0XnhxJvPALchZY8sYRAfuw6hHoDiVr+JUieRoI7eUrhXBp+D6Py9TL/dS/rHe+C2Zhx+xwx2NfGt+xEp8ZAOOCxgZ0UTeSA/abm0Oz7tJIK1n26acQrgbr7rMeBymAX+5L5OWlwI1hGgEBfj6W0rrbSXf3VMfaFXZ5UsXi1VhzQmU3LyWENoDeImXFQj6zMbUSfcVwLsG5Fg8Ee/kO/wJPfG5BO51+/vFqQj6AkaMEcwg5xNrObHYfQ/DMhIn7YDM2zdzbNTdhnobGkz6YRKFPCgFe3EmIEPEpeh9S3eKE9C7MQsrR8jVSiseR/FipJLsN+W7iOwzeXdwxUFlC/0a98bTKvdrbMgNi6ZVXykHY/t2UyEGpxZGTHoZwhX01kiQrwzC4/+v/676ldxPluO9GY7MtrLveCDsiyBz15u43IGHayDEBNT0rqrOKLYmfzwCWoahRLZQrSmepe/FXqgPqRfyWc/Ro+w3sT9dXUkx3B5xxWgSyABowPV48yBUSJuefhKTpqgzkU+LzhNnWHjnxJzzQ2/|IhlRjnyhIoDM85qHX/bY2zaIU5YaRO/iFVTQDd3uFDo="
|
||||||
|
}`;
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
export const credentialsData = `username,username2,username3,title,password,note,url,category,otpSecret
|
||||||
|
jdoe,,,example.com,somePassword,some note for example.com,https://www.example.com,Entertainment,someTOTPSeed`;
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
export const identityData = `type,number,name,issue_date,expiration_date,place_of_issue,state
|
||||||
|
card,123123123,John Doe,2022-1-30,2032-1-30,,
|
||||||
|
passport,123123123,John Doe,2022-1-30,2032-1-30,somewhere in Germany,
|
||||||
|
license,1234556,John Doe,2022-8-10,2022-10-10,,DC
|
||||||
|
social_security,123123123,John Doe,,,,
|
||||||
|
tax_number,123123123,,,,,`;
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
export const multiplePersonalInfoData = `type,title,first_name,middle_name,last_name,login,date_of_birth,place_of_birth,email,email_type,item_name,phone_number,address,country,state,city,zip,address_recipient,address_building,address_apartment,address_floor,address_door_code,job_title,url
|
||||||
|
name,MR,John,,Doe,jdoe,2022-01-30,world,,,,,,,,,,,,,,,,
|
||||||
|
email,,,,,,,,jdoe@example.com,personal,Johns email,,,,,,,,,,,,,
|
||||||
|
number,,,,,,,,,,John's number,+49123123123,,,,,,,,,,,,
|
||||||
|
address,,,,,,,,,,John's home address,,1 some street,de,DE-0-NW,some city,123123,John,1,1,1,123,,
|
||||||
|
website,,,,,,,,,,Website,,,,,,,,,,,,,website.com
|
||||||
|
name,Mrs,Jane,,Doe,jdoe,2022-01-30,earth,,,,,,,,,,,,,,,,`;
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
export const paymentsData = `type,account_name,account_holder,cc_number,code,expiration_month,expiration_year,routing_number,account_number,country,issuing_bank
|
||||||
|
bank,John's savings account,John Doe,,,,,routingNumber,accountNumber,US,US-ALLY
|
||||||
|
credit_card,John Doe,,41111111111111111,123,01,2023,,,US,`;
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user