1
0
mirror of https://github.com/bitwarden/jslib synced 2026-01-05 01:53:13 +00:00

Merge remote-tracking branch 'origin/master' into hotfix/bwdc-api-key-refresh

This commit is contained in:
Robyn MacCallum
2021-12-10 16:45:10 -05:00
147 changed files with 5441 additions and 4473 deletions

28
.github/PULL_REQUEST_TEMPLATE.md vendored Normal file
View File

@@ -0,0 +1,28 @@
## Type of change
- [ ] Bug fix
- [ ] New feature development
- [ ] Tech debt (refactoring, code cleanup, dependency upgrades, etc)
- [ ] Build/deploy pipeline (DevOps)
- [ ] Other
## Objective
<!--Describe what the purpose of this PR is. For example: what bug you're fixing or what new feature you're adding-->
## Code changes
<!--Explain the changes you've made to each file or major component. This should help the reviewer understand your changes-->
<!--Also refer to any related changes or PRs in other repositories-->
* **file.ext:** Description of what was changed and why
## Testing requirements
<!--What functionality requires testing by QA? This includes testing new behavior and regression testing-->
## Before you submit
- [ ] I have checked for **linting** errors (`npm run lint`) (required)
- [ ] I have added **unit tests** where it makes sense to do so (encouraged but not required)
- [ ] This change requires a **documentation update** (notify the documentation team)
- [ ] This change has particular **deployment requirements** (notify the DevOps team)

View File

@@ -1,10 +1,12 @@
---
name: Build
on: push
jobs:
cloc:
runs-on: ubuntu-latest
name: CLOC
runs-on: ubuntu-20.04
steps:
- name: Checkout repo
@@ -18,13 +20,14 @@ jobs:
- name: Print lines of code
run: cloc --include-lang TypeScript,JavaScript,HTML,Sass,CSS --vcs git
build:
build:
name: Build jslib
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [windows-latest, macos-latest, ubuntu-latest]
os: [windows-2019, macos-10.15, ubuntu-20.04]
steps:
- name: Set up Node
@@ -65,3 +68,48 @@ jobs:
with:
name: test-coverage
path: coverage/
- name: Run Node tests
run: npm run test:node
check-failures:
name: Check for failures
if: always()
runs-on: ubuntu-20.04
needs:
- cloc
- build
steps:
- name: Check if any job failed
if: ${{ (github.ref == 'refs/heads/master') || (github.ref == 'refs/heads/rc') }}
env:
CLOC_STATUS: ${{ needs.cloc.result }}
BUILD_STATUS: ${{ needs.build.result }}
run: |
if [ "$CLOC_STATUS" = "failure" ]; then
exit 1
elif [ "$BUILD_STATUS" = "failure" ]; then
exit 1
fi
- name: Login to Azure - Prod Subscription
uses: Azure/login@77f1b2e3fb80c0e8645114159d17008b8a2e475a
if: failure()
with:
creds: ${{ secrets.AZURE_PROD_KV_CREDENTIALS }}
- name: Retrieve secrets
id: retrieve-secrets
uses: Azure/get-keyvault-secrets@80ccd3fafe5662407cc2e55f202ee34bfff8c403
if: failure()
with:
keyvault: "bitwarden-prod-kv"
secrets: "devops-alerts-slack-webhook-url"
- name: Notify Slack on failure
uses: act10ns/slack@e4e71685b9b239384b0f676a63c32367f59c2522 # v1.2.2
if: failure()
env:
SLACK_WEBHOOK_URL: ${{ steps.retrieve-secrets.outputs.devops-alerts-slack-webhook-url }}
with:
status: ${{ job.status }}

View File

@@ -9,40 +9,39 @@
"version": "0.0.0",
"license": "GPL-3.0",
"dependencies": {
"@angular/animations": "^11.2.11",
"@angular/cdk": "^11.2.10",
"@angular/common": "^11.2.11",
"@angular/compiler": "^11.2.11",
"@angular/core": "^11.2.11",
"@angular/forms": "^11.2.11",
"@angular/platform-browser": "^11.2.11",
"@angular/platform-browser-dynamic": "^11.2.11",
"@angular/router": "^11.2.11",
"@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": "6.6.7",
"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.1.5"
"typescript": "4.3.5"
}
},
"../common": {
"name": "@bitwarden/jslib-common",
"version": "0.0.0",
"license": "GPL-3.0",
"dependencies": {
"@microsoft/signalr": "3.1.13",
"@microsoft/signalr-protocol-msgpack": "3.1.13",
"@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": "^0.10.0",
"papaparse": "^5.3.0",
"rxjs": "6.6.7",
"rxjs": "^7.4.0",
"tldjs": "^2.3.1",
"zxcvbn": "^4.4.2"
},
@@ -54,92 +53,111 @@
"@types/tldjs": "^2.3.0",
"@types/zxcvbn": "^4.4.1",
"rimraf": "^3.0.2",
"typescript": "4.1.5"
"typescript": "4.3.5"
}
},
"node_modules/@angular/animations": {
"version": "11.2.14",
"resolved": "https://registry.npmjs.org/@angular/animations/-/animations-11.2.14.tgz",
"integrity": "sha512-Heq/nNrCmb3jbkusu+BQszOecfFI/31Oxxj+CDQkqqYpBcswk6bOJLoEE472o+vmgxaXbgeflU9qbIiCQhpMFA==",
"version": "12.2.14",
"resolved": "https://registry.npmjs.org/@angular/animations/-/animations-12.2.14.tgz",
"integrity": "sha512-1BR5u32auVePvXNNP96DB2008V+Ku0OGqeZQl2h4XA9xzES/Zk5WllIJZXqRmWMRBVARfXsfb0RdMty9gcaVjA==",
"dependencies": {
"tslib": "^2.0.0"
"tslib": "^2.2.0"
},
"engines": {
"node": "^12.14.1 || >=14.0.0"
},
"peerDependencies": {
"@angular/core": "11.2.14"
"@angular/core": "12.2.14"
}
},
"node_modules/@angular/cdk": {
"version": "11.2.13",
"resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-11.2.13.tgz",
"integrity": "sha512-FkE4iCwoLbQxLDUOjV1I7M/6hmpyb7erAjEdWgch7nGRNxF1hqX5Bqf1lvLFKPNCbx5NRI5K7YVAdIUQUR8vug==",
"version": "12.2.13",
"resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-12.2.13.tgz",
"integrity": "sha512-zSKRhECyFqhingIeyRInIyTvYErt4gWo+x5DQr0b7YLUbU8DZSwWnG4w76Ke2s4U8T7ry1jpJBHoX/e8YBpGMg==",
"dependencies": {
"tslib": "^2.0.0"
"tslib": "^2.2.0"
},
"optionalDependencies": {
"parse5": "^5.0.0"
},
"peerDependencies": {
"@angular/common": "^11.0.0 || ^12.0.0-0",
"@angular/core": "^11.0.0 || ^12.0.0-0"
"@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": "11.2.14",
"resolved": "https://registry.npmjs.org/@angular/common/-/common-11.2.14.tgz",
"integrity": "sha512-ZSLV/3j7eCTyLf/8g4yBFLWySjiLz3vLJAGWscYoUpnJWMnug1VRu6zoF/COxCbtORgE+Wz6K0uhfS6MziBGVw==",
"version": "12.2.14",
"resolved": "https://registry.npmjs.org/@angular/common/-/common-12.2.14.tgz",
"integrity": "sha512-ffYUYdwZETmFJw0AcWY30WsaWBhJxj/zSmFXWjgEGEGZH56zwbbNwfMZOYZ1jz4haAVxGu+TdXsOl2yMGzN7jQ==",
"dependencies": {
"tslib": "^2.0.0"
"tslib": "^2.2.0"
},
"engines": {
"node": "^12.14.1 || >=14.0.0"
},
"peerDependencies": {
"@angular/core": "11.2.14",
"rxjs": "^6.5.3"
"@angular/core": "12.2.14",
"rxjs": "^6.5.3 || ^7.0.0"
}
},
"node_modules/@angular/compiler": {
"version": "11.2.14",
"resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-11.2.14.tgz",
"integrity": "sha512-XBOK3HgA+/y6Cz7kOX4zcJYmgJ264XnfcbXUMU2cD7Ac+mbNhLPKohWrEiSWalfcjnpf5gRfufQrQP7lpAGu0A==",
"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.0.0"
"tslib": "^2.2.0"
},
"engines": {
"node": "^12.14.1 || >=14.0.0"
}
},
"node_modules/@angular/core": {
"version": "11.2.14",
"resolved": "https://registry.npmjs.org/@angular/core/-/core-11.2.14.tgz",
"integrity": "sha512-vpR4XqBGitk1Faph37CSpemwIYTmJ3pdIVNoHKP6jLonpWu+0azkchf0f7oD8/2ivj2F81opcIw0tcsy/D/5Vg==",
"version": "12.2.14",
"resolved": "https://registry.npmjs.org/@angular/core/-/core-12.2.14.tgz",
"integrity": "sha512-dlVk7yqUHL2R/eCmM8LsWuxhEBfzg0y1zHt0UqCuFwlCoiw+IG4HFy4OlZEUw9NUEZJSv0aDv3sWqxLkvK5vvg==",
"dependencies": {
"tslib": "^2.0.0"
"tslib": "^2.2.0"
},
"engines": {
"node": "^12.14.1 || >=14.0.0"
},
"peerDependencies": {
"rxjs": "^6.5.3",
"zone.js": "^0.10.2 || ^0.11.3"
"rxjs": "^6.5.3 || ^7.0.0",
"zone.js": "~0.11.4"
}
},
"node_modules/@angular/forms": {
"version": "11.2.14",
"resolved": "https://registry.npmjs.org/@angular/forms/-/forms-11.2.14.tgz",
"integrity": "sha512-4LWqY6KEIk1AZQFnk+4PJSOCamlD4tumuVN06gO4D0dZo9Cx+GcvW6pM6N0CPubRvPs3sScCnu20WT11HNWC1w==",
"version": "12.2.14",
"resolved": "https://registry.npmjs.org/@angular/forms/-/forms-12.2.14.tgz",
"integrity": "sha512-/9/gSJUBXVRVdRnzgJnALAQZYJATuGDMkFC9ms9DEMG4PMAhe9x4if1lJjN6noz5RAom3qNuVBNWaYAPUxlcBQ==",
"dependencies": {
"tslib": "^2.0.0"
"tslib": "^2.2.0"
},
"engines": {
"node": "^12.14.1 || >=14.0.0"
},
"peerDependencies": {
"@angular/common": "11.2.14",
"@angular/core": "11.2.14",
"@angular/platform-browser": "11.2.14",
"rxjs": "^6.5.3"
"@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": "11.2.14",
"resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-11.2.14.tgz",
"integrity": "sha512-fb7b7ss/gRoP8wLAN17W62leMgjynuyjEPU2eUoAAazsG9f2cgM+z3rK29GYncDVyYQxZUZYnjSqvL6GSXx86A==",
"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.0.0"
"tslib": "^2.2.0"
},
"engines": {
"node": "^12.14.1 || >=14.0.0"
},
"peerDependencies": {
"@angular/animations": "11.2.14",
"@angular/common": "11.2.14",
"@angular/core": "11.2.14"
"@angular/animations": "12.2.14",
"@angular/common": "12.2.14",
"@angular/core": "12.2.14"
},
"peerDependenciesMeta": {
"@angular/animations": {
@@ -148,31 +166,37 @@
}
},
"node_modules/@angular/platform-browser-dynamic": {
"version": "11.2.14",
"resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-11.2.14.tgz",
"integrity": "sha512-TWTPdFs6iBBcp+/YMsgCRQwdHpWGq8KjeJDJ2tfatGgBD3Gqt2YaHOMST1zPW6RkrmupytTejuVqXzeaKWFxuw==",
"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.0.0"
"tslib": "^2.2.0"
},
"engines": {
"node": "^12.14.1 || >=14.0.0"
},
"peerDependencies": {
"@angular/common": "11.2.14",
"@angular/compiler": "11.2.14",
"@angular/core": "11.2.14",
"@angular/platform-browser": "11.2.14"
"@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": "11.2.14",
"resolved": "https://registry.npmjs.org/@angular/router/-/router-11.2.14.tgz",
"integrity": "sha512-3aYBmj+zrEL9yf/ntIQxHIYaWShZOBKP3U07X2mX+TPMpGlvHDnR7L6bWhQVZwewzMMz7YVR16ldg50IFuAlfA==",
"version": "12.2.14",
"resolved": "https://registry.npmjs.org/@angular/router/-/router-12.2.14.tgz",
"integrity": "sha512-yP5grSnqBvc4vNhtYdcxDgDYIebUKs5f0xyFkUJM5030UnQ0CV45tBsSxHMkQbPZucIfOuxpRy8xy5+4GizuwQ==",
"dependencies": {
"tslib": "^2.0.0"
"tslib": "^2.2.0"
},
"engines": {
"node": "^12.14.1 || >=14.0.0"
},
"peerDependencies": {
"@angular/common": "11.2.14",
"@angular/core": "11.2.14",
"@angular/platform-browser": "11.2.14",
"rxjs": "^6.5.3"
"@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": {
@@ -219,9 +243,9 @@
"dev": true
},
"node_modules/glob": {
"version": "7.1.7",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz",
"integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==",
"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",
@@ -311,20 +335,17 @@
}
},
"node_modules/rxjs": {
"version": "6.6.7",
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz",
"integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==",
"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": "^1.9.0"
},
"engines": {
"npm": ">=2.0.0"
"tslib": "~2.1.0"
}
},
"node_modules/rxjs/node_modules/tslib": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
"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",
@@ -339,14 +360,14 @@
}
},
"node_modules/tslib": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.2.0.tgz",
"integrity": "sha512-gS9GVHRU+RGn5KQM2rllAlR3dU6m7AcpJKdtH8gFvQiC4Otgk98XnmMU+nZenHt/+VhnBPWwgrJsyrdcw6i23w=="
"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.1.5",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.1.5.tgz",
"integrity": "sha512-6OSu9PTIzmn9TCDiovULTnET6BgXtDYL4Gg4szY+cGsc3JP1dQL8qvE8kShTRx1NIw4Q9IBHlwODjkjWEtMUyA==",
"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",
@@ -373,83 +394,83 @@
},
"dependencies": {
"@angular/animations": {
"version": "11.2.14",
"resolved": "https://registry.npmjs.org/@angular/animations/-/animations-11.2.14.tgz",
"integrity": "sha512-Heq/nNrCmb3jbkusu+BQszOecfFI/31Oxxj+CDQkqqYpBcswk6bOJLoEE472o+vmgxaXbgeflU9qbIiCQhpMFA==",
"version": "12.2.14",
"resolved": "https://registry.npmjs.org/@angular/animations/-/animations-12.2.14.tgz",
"integrity": "sha512-1BR5u32auVePvXNNP96DB2008V+Ku0OGqeZQl2h4XA9xzES/Zk5WllIJZXqRmWMRBVARfXsfb0RdMty9gcaVjA==",
"requires": {
"tslib": "^2.0.0"
"tslib": "^2.2.0"
}
},
"@angular/cdk": {
"version": "11.2.13",
"resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-11.2.13.tgz",
"integrity": "sha512-FkE4iCwoLbQxLDUOjV1I7M/6hmpyb7erAjEdWgch7nGRNxF1hqX5Bqf1lvLFKPNCbx5NRI5K7YVAdIUQUR8vug==",
"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.0.0"
"tslib": "^2.2.0"
}
},
"@angular/common": {
"version": "11.2.14",
"resolved": "https://registry.npmjs.org/@angular/common/-/common-11.2.14.tgz",
"integrity": "sha512-ZSLV/3j7eCTyLf/8g4yBFLWySjiLz3vLJAGWscYoUpnJWMnug1VRu6zoF/COxCbtORgE+Wz6K0uhfS6MziBGVw==",
"version": "12.2.14",
"resolved": "https://registry.npmjs.org/@angular/common/-/common-12.2.14.tgz",
"integrity": "sha512-ffYUYdwZETmFJw0AcWY30WsaWBhJxj/zSmFXWjgEGEGZH56zwbbNwfMZOYZ1jz4haAVxGu+TdXsOl2yMGzN7jQ==",
"requires": {
"tslib": "^2.0.0"
"tslib": "^2.2.0"
}
},
"@angular/compiler": {
"version": "11.2.14",
"resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-11.2.14.tgz",
"integrity": "sha512-XBOK3HgA+/y6Cz7kOX4zcJYmgJ264XnfcbXUMU2cD7Ac+mbNhLPKohWrEiSWalfcjnpf5gRfufQrQP7lpAGu0A==",
"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.0.0"
"tslib": "^2.2.0"
}
},
"@angular/core": {
"version": "11.2.14",
"resolved": "https://registry.npmjs.org/@angular/core/-/core-11.2.14.tgz",
"integrity": "sha512-vpR4XqBGitk1Faph37CSpemwIYTmJ3pdIVNoHKP6jLonpWu+0azkchf0f7oD8/2ivj2F81opcIw0tcsy/D/5Vg==",
"version": "12.2.14",
"resolved": "https://registry.npmjs.org/@angular/core/-/core-12.2.14.tgz",
"integrity": "sha512-dlVk7yqUHL2R/eCmM8LsWuxhEBfzg0y1zHt0UqCuFwlCoiw+IG4HFy4OlZEUw9NUEZJSv0aDv3sWqxLkvK5vvg==",
"requires": {
"tslib": "^2.0.0"
"tslib": "^2.2.0"
}
},
"@angular/forms": {
"version": "11.2.14",
"resolved": "https://registry.npmjs.org/@angular/forms/-/forms-11.2.14.tgz",
"integrity": "sha512-4LWqY6KEIk1AZQFnk+4PJSOCamlD4tumuVN06gO4D0dZo9Cx+GcvW6pM6N0CPubRvPs3sScCnu20WT11HNWC1w==",
"version": "12.2.14",
"resolved": "https://registry.npmjs.org/@angular/forms/-/forms-12.2.14.tgz",
"integrity": "sha512-/9/gSJUBXVRVdRnzgJnALAQZYJATuGDMkFC9ms9DEMG4PMAhe9x4if1lJjN6noz5RAom3qNuVBNWaYAPUxlcBQ==",
"requires": {
"tslib": "^2.0.0"
"tslib": "^2.2.0"
}
},
"@angular/platform-browser": {
"version": "11.2.14",
"resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-11.2.14.tgz",
"integrity": "sha512-fb7b7ss/gRoP8wLAN17W62leMgjynuyjEPU2eUoAAazsG9f2cgM+z3rK29GYncDVyYQxZUZYnjSqvL6GSXx86A==",
"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.0.0"
"tslib": "^2.2.0"
}
},
"@angular/platform-browser-dynamic": {
"version": "11.2.14",
"resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-11.2.14.tgz",
"integrity": "sha512-TWTPdFs6iBBcp+/YMsgCRQwdHpWGq8KjeJDJ2tfatGgBD3Gqt2YaHOMST1zPW6RkrmupytTejuVqXzeaKWFxuw==",
"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.0.0"
"tslib": "^2.2.0"
}
},
"@angular/router": {
"version": "11.2.14",
"resolved": "https://registry.npmjs.org/@angular/router/-/router-11.2.14.tgz",
"integrity": "sha512-3aYBmj+zrEL9yf/ntIQxHIYaWShZOBKP3U07X2mX+TPMpGlvHDnR7L6bWhQVZwewzMMz7YVR16ldg50IFuAlfA==",
"version": "12.2.14",
"resolved": "https://registry.npmjs.org/@angular/router/-/router-12.2.14.tgz",
"integrity": "sha512-yP5grSnqBvc4vNhtYdcxDgDYIebUKs5f0xyFkUJM5030UnQ0CV45tBsSxHMkQbPZucIfOuxpRy8xy5+4GizuwQ==",
"requires": {
"tslib": "^2.0.0"
"tslib": "^2.2.0"
}
},
"@bitwarden/jslib-common": {
"version": "file:../common",
"requires": {
"@microsoft/signalr": "3.1.13",
"@microsoft/signalr-protocol-msgpack": "3.1.13",
"@microsoft/signalr": "5.0.10",
"@microsoft/signalr-protocol-msgpack": "5.0.10",
"@types/lunr": "^2.3.3",
"@types/node": "^14.17.1",
"@types/node-forge": "^0.9.7",
@@ -462,9 +483,9 @@
"node-forge": "^0.10.0",
"papaparse": "^5.3.0",
"rimraf": "^3.0.2",
"rxjs": "6.6.7",
"rxjs": "^7.4.0",
"tldjs": "^2.3.1",
"typescript": "4.1.5",
"typescript": "4.3.5",
"zxcvbn": "^4.4.2"
}
},
@@ -507,9 +528,9 @@
"dev": true
},
"glob": {
"version": "7.1.7",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz",
"integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==",
"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",
@@ -581,17 +602,17 @@
}
},
"rxjs": {
"version": "6.6.7",
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz",
"integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==",
"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": "^1.9.0"
"tslib": "~2.1.0"
},
"dependencies": {
"tslib": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.1.0.tgz",
"integrity": "sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A=="
}
}
},
@@ -604,14 +625,14 @@
}
},
"tslib": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.2.0.tgz",
"integrity": "sha512-gS9GVHRU+RGn5KQM2rllAlR3dU6m7AcpJKdtH8gFvQiC4Otgk98XnmMU+nZenHt/+VhnBPWwgrJsyrdcw6i23w=="
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz",
"integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw=="
},
"typescript": {
"version": "4.1.5",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.1.5.tgz",
"integrity": "sha512-6OSu9PTIzmn9TCDiovULTnET6BgXtDYL4Gg4szY+cGsc3JP1dQL8qvE8kShTRx1NIw4Q9IBHlwODjkjWEtMUyA==",
"version": "4.3.5",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.3.5.tgz",
"integrity": "sha512-DqQgihaQ9cUrskJo9kIyW/+g0Vxsk8cDtZ52a3NGh0YNTfpUSArXSohyUGnvbPazEPLu398C0UxmKSOrPumUzA==",
"dev": true
},
"wrappy": {

View File

@@ -22,21 +22,21 @@
"devDependencies": {
"@types/duo_web_sdk": "^2.7.1",
"rimraf": "^3.0.2",
"typescript": "4.1.5"
"typescript": "4.3.5"
},
"dependencies": {
"@angular/animations": "^11.2.11",
"@angular/cdk": "^11.2.10",
"@angular/common": "^11.2.11",
"@angular/compiler": "^11.2.11",
"@angular/core": "^11.2.11",
"@angular/forms": "^11.2.11",
"@angular/platform-browser": "^11.2.11",
"@angular/platform-browser-dynamic": "^11.2.11",
"@angular/router": "^11.2.11",
"@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": "6.6.7",
"rxjs": "^7.4.0",
"tldjs": "^2.3.1",
"zone.js": "0.11.4"
}

View File

@@ -1,6 +1,8 @@
import {
Directive,
Input,
OnChanges,
SimpleChanges,
} from '@angular/core';
import {
@@ -18,13 +20,17 @@ import { CipherType } from 'jslib-common/enums/cipherType';
import { EventType } from 'jslib-common/enums/eventType';
import { FieldType } from 'jslib-common/enums/fieldType';
import { Utils } from 'jslib-common/misc/utils';
@Directive()
export class AddEditCustomFieldsComponent {
export class AddEditCustomFieldsComponent implements OnChanges {
@Input() cipher: CipherView;
@Input() thisCipherType: CipherType;
@Input() editMode: boolean;
addFieldType: FieldType = FieldType.Text;
addFieldTypeOptions: any[];
addFieldLinkedTypeOption: any;
linkedFieldOptions: any[] = [];
cipherType = CipherType;
@@ -37,6 +43,17 @@ export class AddEditCustomFieldsComponent {
{ name: i18nService.t('cfTypeHidden'), value: FieldType.Hidden },
{ name: i18nService.t('cfTypeBoolean'), value: FieldType.Boolean },
];
this.addFieldLinkedTypeOption = { name: this.i18nService.t('cfTypeLinked'), value: FieldType.Linked };
}
ngOnChanges(changes: SimpleChanges) {
if (changes.thisCipherType != null) {
this.setLinkedFieldOptions();
if (!changes.thisCipherType.firstChange) {
this.resetCipherLinkedFields();
}
}
}
addField() {
@@ -48,6 +65,10 @@ export class AddEditCustomFieldsComponent {
f.type = this.addFieldType;
f.newField = true;
if (f.type === FieldType.Linked) {
f.linkedId = this.linkedFieldOptions[0].value;
}
this.cipher.fields.push(f);
}
@@ -73,4 +94,31 @@ export class AddEditCustomFieldsComponent {
drop(event: CdkDragDrop<string[]>) {
moveItemInArray(this.cipher.fields, event.previousIndex, event.currentIndex);
}
private setLinkedFieldOptions() {
if (this.cipher.linkedFieldOptions == null) {
return;
}
const options: any = [];
this.cipher.linkedFieldOptions.forEach((linkedFieldOption, id) =>
options.push({ name: this.i18nService.t(linkedFieldOption.i18nKey), value: id }));
this.linkedFieldOptions = options.sort(Utils.getSortFunction(this.i18nService, 'name'));
}
private resetCipherLinkedFields() {
if (this.cipher.fields == null || this.cipher.fields.length === 0) {
return;
}
// Delete any Linked custom fields if the item type does not support them
if (this.cipher.linkedFieldOptions == null) {
this.cipher.fields = this.cipher.fields.filter(f => f.type !== FieldType.Linked);
return;
}
this.cipher.fields
.filter(f => f.type === FieldType.Linked)
.forEach(f => f.linkedId = this.linkedFieldOptions[0].value);
}
}

View File

@@ -20,7 +20,9 @@ import { CollectionService } from 'jslib-common/abstractions/collection.service'
import { EventService } from 'jslib-common/abstractions/event.service';
import { FolderService } from 'jslib-common/abstractions/folder.service';
import { I18nService } from 'jslib-common/abstractions/i18n.service';
import { LogService } from 'jslib-common/abstractions/log.service';
import { MessagingService } from 'jslib-common/abstractions/messaging.service';
import { PasswordRepromptService } from 'jslib-common/abstractions/passwordReprompt.service';
import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service';
import { PolicyService } from 'jslib-common/abstractions/policy.service';
import { StateService } from 'jslib-common/abstractions/state.service';
@@ -79,6 +81,7 @@ export class AddEditComponent implements OnInit {
currentDate = new Date();
allowPersonal = true;
reprompt: boolean = false;
canUseReprompt: boolean = true;
protected writeableCollections: CollectionView[];
private previousCipherId: string;
@@ -88,7 +91,8 @@ export class AddEditComponent implements OnInit {
protected auditService: AuditService, protected stateService: StateService,
protected userService: UserService, protected collectionService: CollectionService,
protected messagingService: MessagingService, protected eventService: EventService,
protected policyService: PolicyService) {
protected policyService: PolicyService, protected passwordRepromptService: PasswordRepromptService,
private logService: LogService) {
this.typeOptions = [
{ name: i18nService.t('typeLogin'), value: CipherType.Login },
{ name: i18nService.t('typeCard'), value: CipherType.Card },
@@ -150,21 +154,29 @@ export class AddEditComponent implements OnInit {
}
async init() {
const myEmail = await this.userService.getEmail();
this.ownershipOptions.push({ name: myEmail, value: null });
if (this.ownershipOptions.length) {
this.ownershipOptions = [];
}
if (await this.policyService.policyAppliesToUser(PolicyType.PersonalOwnership)) {
this.allowPersonal = false;
} else {
const myEmail = await this.userService.getEmail();
this.ownershipOptions.push({ name: myEmail, value: null });
}
const orgs = await this.userService.getAllOrganizations();
orgs.sort(Utils.getSortFunction(this.i18nService, 'name')).forEach(o => {
if (o.enabled && o.status === OrganizationUserStatusType.Confirmed) {
this.ownershipOptions.push({ name: o.name, value: o.id });
}
});
if (this.allowPersonal && await this.policyService.policyAppliesToUser(PolicyType.PersonalOwnership)) {
this.allowPersonal = false;
this.ownershipOptions.splice(0, 1);
if (!this.allowPersonal) {
this.organizationId = this.ownershipOptions[0].value;
}
this.writeableCollections = await this.loadCollections();
this.canUseReprompt = await this.passwordRepromptService.enabled();
}
async load() {
@@ -280,7 +292,9 @@ export class AddEditComponent implements OnInit {
this.onSavedCipher.emit(this.cipher);
this.messagingService.send(this.editMode && !this.cloneMode ? 'editedCipher' : 'addedCipher');
return true;
} catch { }
} catch (e) {
this.logService.error(e);
}
return false;
}
@@ -343,7 +357,9 @@ export class AddEditComponent implements OnInit {
this.i18nService.t(this.cipher.isDeleted ? 'permanentlyDeletedItem' : 'deletedItem'));
this.onDeletedCipher.emit(this.cipher);
this.messagingService.send(this.cipher.isDeleted ? 'permanentlyDeletedCipher' : 'deletedCipher');
} catch { }
} catch (e) {
this.logService.error(e);
}
return true;
}
@@ -366,7 +382,9 @@ export class AddEditComponent implements OnInit {
this.platformUtilsService.showToast('success', null, this.i18nService.t('restoredItem'));
this.onRestoredCipher.emit(this.cipher);
this.messagingService.send('restoredCipher');
} catch { }
} catch (e) {
this.logService.error(e);
}
return true;
}

View File

@@ -10,6 +10,7 @@ import { ApiService } from 'jslib-common/abstractions/api.service';
import { CipherService } from 'jslib-common/abstractions/cipher.service';
import { CryptoService } from 'jslib-common/abstractions/crypto.service';
import { I18nService } from 'jslib-common/abstractions/i18n.service';
import { LogService } from 'jslib-common/abstractions/log.service';
import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service';
import { UserService } from 'jslib-common/abstractions/user.service';
@@ -38,7 +39,7 @@ export class AttachmentsComponent implements OnInit {
constructor(protected cipherService: CipherService, protected i18nService: I18nService,
protected cryptoService: CryptoService, protected userService: UserService,
protected platformUtilsService: PlatformUtilsService, protected apiService: ApiService,
protected win: Window) { }
protected win: Window, private logService: LogService) { }
async ngOnInit() {
await this.init();
@@ -71,7 +72,9 @@ export class AttachmentsComponent implements OnInit {
this.cipher = await this.cipherDomain.decrypt();
this.platformUtilsService.showToast('success', null, this.i18nService.t('attachmentSaved'));
this.onUploadedAttachment.emit();
} catch { }
} catch (e) {
this.logService.error(e);
}
// reset file input
// ref: https://stackoverflow.com/a/20552042
@@ -100,7 +103,9 @@ export class AttachmentsComponent implements OnInit {
if (i > -1) {
this.cipher.attachments.splice(i, 1);
}
} catch { }
} catch (e) {
this.logService.error(e);
}
this.deletePromises[attachment.id] = null;
this.onDeletedAttachment.emit();
@@ -226,7 +231,9 @@ export class AttachmentsComponent implements OnInit {
a.downloading = false;
});
await this.reuploadPromises[attachment.id];
} catch { }
} catch (e) {
this.logService.error(e);
}
}
protected loadCipher() {

View File

@@ -0,0 +1,138 @@
import {
Component,
Input,
OnChanges,
OnInit,
} from '@angular/core';
import { DomSanitizer } from '@angular/platform-browser';
import { CryptoFunctionService } from 'jslib-common/abstractions/cryptoFunction.service';
import { StateService } from 'jslib-common/abstractions/state.service';
import { Utils } from 'jslib-common/misc/utils';
@Component({
selector: 'app-avatar',
template: '<img [src]="sanitizer.bypassSecurityTrustResourceUrl(src)" title="{{data}}" ' +
'[ngClass]="{\'rounded-circle\': circle}">',
})
export class AvatarComponent implements OnChanges, OnInit {
@Input() data: string;
@Input() email: string;
@Input() size = 45;
@Input() charCount = 2;
@Input() textColor = '#ffffff';
@Input() fontSize = 20;
@Input() fontWeight = 300;
@Input() dynamic = false;
@Input() circle = false;
src: string;
constructor(public sanitizer: DomSanitizer, private cryptoFunctionService: CryptoFunctionService,
private stateService: StateService) { }
ngOnInit() {
if (!this.dynamic) {
this.generate();
}
}
ngOnChanges() {
if (this.dynamic) {
this.generate();
}
}
private async generate() {
const enableGravatars = await this.stateService.get<boolean>('enableGravatars');
if (enableGravatars && this.email != null) {
const hashBytes = await this.cryptoFunctionService.hash(this.email.toLowerCase().trim(), 'md5');
const hash = Utils.fromBufferToHex(hashBytes).toLowerCase();
this.src = 'https://www.gravatar.com/avatar/' + hash + '?s=' + this.size + '&r=pg&d=retro';
} else {
let chars: string = null;
const upperData = this.data.toUpperCase();
if (this.charCount > 1) {
chars = this.getFirstLetters(upperData, this.charCount);
}
if (chars == null) {
chars = this.unicodeSafeSubstring(upperData, this.charCount);
}
// If the chars contain an emoji, only show it.
if (chars.match(Utils.regexpEmojiPresentation)) {
chars = chars.match(Utils.regexpEmojiPresentation)[0];
}
const charObj = this.getCharText(chars);
const color = this.stringToColor(upperData);
const svg = this.getSvg(this.size, color);
svg.appendChild(charObj);
const html = window.document.createElement('div').appendChild(svg).outerHTML;
const svgHtml = window.btoa(unescape(encodeURIComponent(html)));
this.src = 'data:image/svg+xml;base64,' + svgHtml;
}
}
private stringToColor(str: string): string {
let hash = 0;
for (let i = 0; i < str.length; i++) {
// tslint:disable-next-line
hash = str.charCodeAt(i) + ((hash << 5) - hash);
}
let color = '#';
for (let i = 0; i < 3; i++) {
// tslint:disable-next-line
const value = (hash >> (i * 8)) & 0xFF;
color += ('00' + value.toString(16)).substr(-2);
}
return color;
}
private getFirstLetters(data: string, count: number): string {
const parts = data.split(' ');
if (parts.length > 1) {
let text = '';
for (let i = 0; i < count; i++) {
text += this.unicodeSafeSubstring(parts[i], 1);
}
return text;
}
return null;
}
private getSvg(size: number, color: string): HTMLElement {
const svgTag = window.document.createElement('svg');
svgTag.setAttribute('xmlns', 'http://www.w3.org/2000/svg');
svgTag.setAttribute('pointer-events', 'none');
svgTag.setAttribute('width', size.toString());
svgTag.setAttribute('height', size.toString());
svgTag.style.backgroundColor = color;
svgTag.style.width = size + 'px';
svgTag.style.height = size + 'px';
return svgTag;
}
private getCharText(character: string): HTMLElement {
const textTag = window.document.createElement('text');
textTag.setAttribute('text-anchor', 'middle');
textTag.setAttribute('y', '50%');
textTag.setAttribute('x', '50%');
textTag.setAttribute('dy', '0.35em');
textTag.setAttribute('pointer-events', 'auto');
textTag.setAttribute('fill', this.textColor);
textTag.setAttribute('font-family', '"Open Sans","Helvetica Neue",Helvetica,Arial,' +
'sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol"');
textTag.textContent = character;
textTag.style.fontWeight = this.fontWeight.toString();
textTag.style.fontSize = this.fontSize + 'px';
return textTag;
}
private unicodeSafeSubstring(str: string, count: number) {
const characters = str.match(/./ug);
return characters != null ? characters.slice(0, count).join('') : '';
}
}

View File

@@ -1,4 +1,5 @@
<div class="callout callout-{{calloutStyle}}" [ngClass]="{'clickable': clickable}" role="alert">
<div #callout class="callout callout-{{calloutStyle}}" [ngClass]="{'clickable': clickable}"
[attr.role]="useAlertRole ? 'alert' : null">
<h3 class="callout-heading" *ngIf="title">
<i class="fa {{icon}}" *ngIf="icon" aria-hidden="true"></i>
{{title}}

View File

@@ -19,6 +19,7 @@ export class CalloutComponent implements OnInit {
@Input() clickable: boolean;
@Input() enforcedPolicyOptions: MasterPasswordPolicyOptions;
@Input() enforcedPolicyMessage: string;
@Input() useAlertRole = false;
calloutStyle: string;

View File

@@ -9,6 +9,7 @@ import {
import { CipherService } from 'jslib-common/abstractions/cipher.service';
import { CollectionService } from 'jslib-common/abstractions/collection.service';
import { I18nService } from 'jslib-common/abstractions/i18n.service';
import { LogService } from 'jslib-common/abstractions/log.service';
import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service';
import { CipherView } from 'jslib-common/models/view/cipherView';
@@ -30,7 +31,7 @@ export class CollectionsComponent implements OnInit {
protected cipherDomain: Cipher;
constructor(protected collectionService: CollectionService, protected platformUtilsService: PlatformUtilsService,
protected i18nService: I18nService, protected cipherService: CipherService) { }
protected i18nService: I18nService, protected cipherService: CipherService, private logService: LogService) { }
async ngOnInit() {
await this.load();
@@ -65,7 +66,9 @@ export class CollectionsComponent implements OnInit {
await this.formPromise;
this.onSavedCollections.emit();
this.platformUtilsService.showToast('success', null, this.i18nService.t('editedItem'));
} catch { }
} catch (e) {
this.logService.error(e);
}
}
protected loadCipher() {

View File

@@ -6,7 +6,6 @@ import {
import { EnvironmentService } from 'jslib-common/abstractions/environment.service';
import { I18nService } from 'jslib-common/abstractions/i18n.service';
import { NotificationsService } from 'jslib-common/abstractions/notifications.service';
import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service';
@Directive()
@@ -20,7 +19,6 @@ export class EnvironmentComponent {
notificationsUrl: string;
baseUrl: string;
showCustom = false;
enterpriseUrl: string;
constructor(protected platformUtilsService: PlatformUtilsService, protected environmentService: EnvironmentService,
protected i18nService: I18nService) {
@@ -33,7 +31,6 @@ export class EnvironmentComponent {
this.identityUrl = urls.identity || '';
this.iconsUrl = urls.icons || '';
this.notificationsUrl = urls.notifications || '';
this.enterpriseUrl = urls.enterprise || '';
}
async submit() {
@@ -44,7 +41,6 @@ export class EnvironmentComponent {
webVault: this.webVaultUrl,
icons: this.iconsUrl,
notifications: this.notificationsUrl,
enterprise: this.enterpriseUrl,
});
// re-set urls since service can change them, ex: prefixing https://
@@ -54,7 +50,6 @@ export class EnvironmentComponent {
this.webVaultUrl = resUrls.webVault;
this.iconsUrl = resUrls.icons;
this.notificationsUrl = resUrls.notifications;
this.enterpriseUrl = resUrls.enterprise;
this.platformUtilsService.showToast('success', null, this.i18nService.t('environmentSaved'));
this.saved();

View File

@@ -4,13 +4,16 @@ import {
OnInit,
Output,
} from '@angular/core';
import { FormBuilder } from '@angular/forms';
import { CryptoService } from 'jslib-common/abstractions/crypto.service';
import { EventService } from 'jslib-common/abstractions/event.service';
import { ExportService } from 'jslib-common/abstractions/export.service';
import { I18nService } from 'jslib-common/abstractions/i18n.service';
import { LogService } from 'jslib-common/abstractions/log.service';
import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service';
import { PolicyService } from 'jslib-common/abstractions/policy.service';
import { UserVerificationService } from 'jslib-common/abstractions/userVerification.service';
import { EventType } from 'jslib-common/enums/eventType';
import { PolicyType } from 'jslib-common/enums/policyType';
@@ -20,14 +23,24 @@ export class ExportComponent implements OnInit {
@Output() onSaved = new EventEmitter();
formPromise: Promise<string>;
masterPassword: string;
format: 'json' | 'encrypted_json' | 'csv' = 'json';
showPassword = false;
disabledByPolicy: boolean = false;
exportForm = this.fb.group({
format: ['json'],
secret: [''],
});
formatOptions = [
{ name: '.json', value: 'json' },
{ name: '.csv', value: 'csv' },
{ name: '.json (Encrypted)', value: 'encrypted_json' },
];
constructor(protected cryptoService: CryptoService, protected i18nService: I18nService,
protected platformUtilsService: PlatformUtilsService, protected exportService: ExportService,
protected eventService: EventService, private policyService: PolicyService, protected win: Window) { }
protected eventService: EventService, private policyService: PolicyService, protected win: Window,
private logService: LogService, private userVerificationService: UserVerificationService,
private fb: FormBuilder) { }
async ngOnInit() {
await this.checkExportDisabled();
@@ -35,6 +48,9 @@ export class ExportComponent implements OnInit {
async checkExportDisabled() {
this.disabledByPolicy = await this.policyService.policyAppliesToUser(PolicyType.DisablePersonalVaultExport);
if (this.disabledByPolicy) {
this.exportForm.disable();
}
}
get encryptedFormat() {
@@ -47,29 +63,28 @@ export class ExportComponent implements OnInit {
return;
}
if (this.masterPassword == null || this.masterPassword === '') {
this.platformUtilsService.showToast('error', this.i18nService.t('errorOccurred'),
this.i18nService.t('invalidMasterPassword'));
return;
}
const acceptedWarning = await this.warningDialog();
if (!acceptedWarning) {
return;
}
const passwordValid = await this.cryptoService.compareAndUpdateKeyHash(this.masterPassword, null);
if (passwordValid) {
try {
this.formPromise = this.getExportData();
const data = await this.formPromise;
this.downloadFile(data);
this.saved();
await this.collectEvent();
} catch { }
} else {
this.platformUtilsService.showToast('error', this.i18nService.t('errorOccurred'),
this.i18nService.t('invalidMasterPassword'));
const secret = this.exportForm.get('secret').value;
try {
await this.userVerificationService.verifyUser(secret);
} catch (e) {
this.platformUtilsService.showToast('error', this.i18nService.t('errorOccurred'), e.message);
return;
}
try {
this.formPromise = this.getExportData();
const data = await this.formPromise;
this.downloadFile(data);
this.saved();
await this.collectEvent();
this.exportForm.get('secret').setValue('');
} catch (e) {
this.logService.error(e);
}
}
@@ -89,11 +104,6 @@ export class ExportComponent implements OnInit {
}
}
togglePassword() {
this.showPassword = !this.showPassword;
document.getElementById('masterPassword').focus();
}
protected saved() {
this.onSaved.emit();
}
@@ -119,6 +129,10 @@ export class ExportComponent implements OnInit {
await this.eventService.collect(EventType.User_ClientExportedVault);
}
get format() {
return this.exportForm.get('format').value;
}
private downloadFile(csv: string): void {
const fileName = this.getFileName();
this.platformUtilsService.saveFile(this.win, csv, { type: 'text/plain' }, fileName);

View File

@@ -8,6 +8,7 @@ import {
import { FolderService } from 'jslib-common/abstractions/folder.service';
import { I18nService } from 'jslib-common/abstractions/i18n.service';
import { LogService } from 'jslib-common/abstractions/log.service';
import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service';
import { FolderView } from 'jslib-common/models/view/folderView';
@@ -25,7 +26,7 @@ export class FolderAddEditComponent implements OnInit {
deletePromise: Promise<any>;
constructor(protected folderService: FolderService, protected i18nService: I18nService,
protected platformUtilsService: PlatformUtilsService) { }
protected platformUtilsService: PlatformUtilsService, private logService: LogService) { }
async ngOnInit() {
await this.init();
@@ -46,7 +47,9 @@ export class FolderAddEditComponent implements OnInit {
this.i18nService.t(this.editMode ? 'editedFolder' : 'addedFolder'));
this.onSavedFolder.emit(this.folder);
return true;
} catch { }
} catch (e) {
this.logService.error(e);
}
return false;
}
@@ -64,7 +67,9 @@ export class FolderAddEditComponent implements OnInit {
await this.deletePromise;
this.platformUtilsService.showToast('success', null, this.i18nService.t('deletedFolder'));
this.onDeletedFolder.emit(this.folder);
} catch { }
} catch (e) {
this.logService.error(e);
}
return true;
}

View File

@@ -4,6 +4,7 @@ import { PasswordHintRequest } from 'jslib-common/models/request/passwordHintReq
import { ApiService } from 'jslib-common/abstractions/api.service';
import { I18nService } from 'jslib-common/abstractions/i18n.service';
import { LogService } from 'jslib-common/abstractions/log.service';
import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service';
export class HintComponent {
@@ -14,7 +15,8 @@ export class HintComponent {
protected onSuccessfulSubmit: () => void;
constructor(protected router: Router, protected i18nService: I18nService,
protected apiService: ApiService, protected platformUtilsService: PlatformUtilsService) { }
protected apiService: ApiService, protected platformUtilsService: PlatformUtilsService,
private logService: LogService) { }
async submit() {
if (this.email == null || this.email === '') {
@@ -37,6 +39,8 @@ export class HintComponent {
} else if (this.router != null) {
this.router.navigate([this.successRoute]);
}
} catch { }
} catch (e) {
this.logService.error(e);
}
}
}

View File

@@ -96,7 +96,9 @@ export class IconComponent implements OnChanges {
try {
this.image = this.iconsUrl + '/' + Utils.getHostname(hostnameUri) + '/icon.png';
this.fallbackImage = 'images/fa-globe.png';
} catch (e) { }
} catch (e) {
// Ignore error since the fallback icon will be shown if image is null.
}
}
} else {
this.image = null;

View File

@@ -1,10 +1,13 @@
import { Directive, OnInit } from '@angular/core';
import { Directive, NgZone, OnInit } from '@angular/core';
import { Router } from '@angular/router';
import { take } from 'rxjs/operators';
import { ApiService } from 'jslib-common/abstractions/api.service';
import { CryptoService } from 'jslib-common/abstractions/crypto.service';
import { EnvironmentService } from 'jslib-common/abstractions/environment.service';
import { I18nService } from 'jslib-common/abstractions/i18n.service';
import { KeyConnectorService } from 'jslib-common/abstractions/keyConnector.service';
import { LogService } from 'jslib-common/abstractions/log.service';
import { MessagingService } from 'jslib-common/abstractions/messaging.service';
import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service';
import { StateService } from 'jslib-common/abstractions/state.service';
@@ -17,7 +20,7 @@ import { ConstantsService } from 'jslib-common/services/constants.service';
import { EncString } from 'jslib-common/models/domain/encString';
import { SymmetricCryptoKey } from 'jslib-common/models/domain/symmetricCryptoKey';
import { PasswordVerificationRequest } from 'jslib-common/models/request/passwordVerificationRequest';
import { SecretVerificationRequest } from 'jslib-common/models/request/secretVerificationRequest';
import { Utils } from 'jslib-common/misc/utils';
@@ -35,6 +38,7 @@ export class LockComponent implements OnInit {
supportsBiometric: boolean;
biometricLock: boolean;
biometricText: string;
hideInput: boolean;
protected successRoute: string = 'vault';
protected onSuccessfulSubmit: () => void;
@@ -47,7 +51,8 @@ export class LockComponent implements OnInit {
protected userService: UserService, protected cryptoService: CryptoService,
protected storageService: StorageService, protected vaultTimeoutService: VaultTimeoutService,
protected environmentService: EnvironmentService, protected stateService: StateService,
protected apiService: ApiService) { }
protected apiService: ApiService, private logService: LogService,
private keyConnectorService: KeyConnectorService, protected ngZone: NgZone) { }
async ngOnInit() {
this.pinSet = await this.vaultTimeoutService.isPinLockSet();
@@ -57,6 +62,13 @@ export class LockComponent implements OnInit {
(await this.cryptoService.hasKeyStored('biometric') || !this.platformUtilsService.supportsSecureStorage());
this.biometricText = await this.storageService.get(ConstantsService.biometricText);
this.email = await this.userService.getEmail();
const usesKeyConnector = await this.keyConnectorService.getUsesKeyConnector();
this.hideInput = usesKeyConnector && !this.pinLock;
// Users with key connector and without biometric or pin has no MP to unlock using
if (usesKeyConnector && !(this.biometricLock || this.pinLock)) {
await this.vaultTimeoutService.logOut();
}
const webVaultUrl = this.environmentService.getWebVaultUrl();
const vaultUrl = webVaultUrl === 'https://vault.bitwarden.com' ? 'https://bitwarden.com' : webVaultUrl;
@@ -118,7 +130,7 @@ export class LockComponent implements OnInit {
if (storedKeyHash != null) {
passwordValid = await this.cryptoService.compareAndUpdateKeyHash(this.masterPassword, key);
} else {
const request = new PasswordVerificationRequest();
const request = new SecretVerificationRequest();
const serverKeyHash = await this.cryptoService.hashPassword(this.masterPassword, key,
HashPurpose.ServerAuthorization);
request.masterPasswordHash = serverKeyHash;
@@ -129,7 +141,9 @@ export class LockComponent implements OnInit {
const localKeyHash = await this.cryptoService.hashPassword(this.masterPassword, key,
HashPurpose.LocalAuthorization);
await this.cryptoService.setKeyHash(localKeyHash);
} catch { }
} catch (e) {
this.logService.error(e);
}
}
if (passwordValid) {
@@ -156,7 +170,7 @@ export class LockComponent implements OnInit {
}
}
async unlockBiometric() {
async unlockBiometric(): Promise<boolean> {
if (!this.biometricLock) {
return;
}
@@ -166,11 +180,18 @@ export class LockComponent implements OnInit {
if (success) {
await this.doContinue();
}
return success;
}
togglePassword() {
this.showPassword = !this.showPassword;
document.getElementById(this.pinLock ? 'pin' : 'masterPassword').focus();
const input = document.getElementById(this.pinLock ? 'pin' : 'masterPassword');
if (this.ngZone.isStable) {
input.focus();
} else {
this.ngZone.onStable.pipe(take(1)).subscribe(() => input.focus());
}
}
private async setKeyAndContinue(key: SymmetricCryptoKey) {

View File

@@ -1,17 +1,21 @@
import {
Directive,
Input,
NgZone,
OnInit,
} from '@angular/core';
import { Router } from '@angular/router';
import { take } from 'rxjs/operators';
import { AuthResult } from 'jslib-common/models/domain/authResult';
import { AuthService } from 'jslib-common/abstractions/auth.service';
import { CryptoFunctionService } from 'jslib-common/abstractions/cryptoFunction.service';
import { EnvironmentService } from 'jslib-common/abstractions/environment.service';
import { I18nService } from 'jslib-common/abstractions/i18n.service';
import { LogService } from 'jslib-common/abstractions/log.service';
import { PasswordGenerationService } from 'jslib-common/abstractions/passwordGeneration.service';
import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service';
import { StateService } from 'jslib-common/abstractions/state.service';
@@ -49,7 +53,8 @@ export class LoginComponent extends CaptchaProtectedComponent implements OnInit
platformUtilsService: PlatformUtilsService, i18nService: I18nService,
protected stateService: StateService, environmentService: EnvironmentService,
protected passwordGenerationService: PasswordGenerationService,
protected cryptoFunctionService: CryptoFunctionService, private storageService: StorageService) {
protected cryptoFunctionService: CryptoFunctionService, private storageService: StorageService,
protected logService: LogService, protected ngZone: NgZone) {
super(environmentService, i18nService, platformUtilsService);
}
@@ -123,12 +128,18 @@ export class LoginComponent extends CaptchaProtectedComponent implements OnInit
this.router.navigate([this.successRoute]);
}
}
} catch { }
} catch (e) {
this.logService.error(e);
}
}
togglePassword() {
this.showPassword = !this.showPassword;
document.getElementById('masterPassword').focus();
if (this.ngZone.isStable) {
document.getElementById('masterPassword').focus();
} else {
this.ngZone.onStable.pipe(take(1)).subscribe(() => document.getElementById('masterPassword').focus());
}
}
async launchSsoBrowser(clientId: string, ssoRedirectUri: string) {

View File

@@ -10,6 +10,11 @@ import {
ViewContainerRef
} from '@angular/core';
import {
ConfigurableFocusTrap,
ConfigurableFocusTrapFactory,
} from '@angular/cdk/a11y';
import { ModalService } from '../../services/modal.service';
import { ModalRef } from './modal.ref';
@@ -26,8 +31,11 @@ export class DynamicModalComponent implements AfterViewInit, OnDestroy {
childComponentType: Type<any>;
setComponentParameters: (component: any) => void;
private focusTrap: ConfigurableFocusTrap;
constructor(private modalService: ModalService, private cd: ChangeDetectorRef,
private el: ElementRef<HTMLElement>, public modalRef: ModalRef) {}
private el: ElementRef<HTMLElement>, private focusTrapFactory: ConfigurableFocusTrapFactory,
public modalRef: ModalRef) { }
ngAfterViewInit() {
this.loadChildComponent(this.childComponentType);
@@ -37,6 +45,10 @@ export class DynamicModalComponent implements AfterViewInit, OnDestroy {
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>) {
@@ -50,9 +62,15 @@ export class DynamicModalComponent implements AfterViewInit, OnDestroy {
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();
}
}

View File

@@ -6,14 +6,14 @@ 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<any>; // Start showing modal
onShown: Observable<any>; // Modal is fully visible
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<any>();
private readonly _onShown = new Subject<any>();
private readonly _onShow = new Subject<void>();
private readonly _onShown = new Subject<void>();
private lastResult: any;
constructor() {

View File

@@ -2,8 +2,8 @@ import { Directive, OnInit } from '@angular/core';
import { ApiService } from 'jslib-common/abstractions/api.service';
import { I18nService } from 'jslib-common/abstractions/i18n.service';
import { LogService } from 'jslib-common/abstractions/log.service';
import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service';
import { TokenService } from 'jslib-common/abstractions/token.service';
import { UserService } from 'jslib-common/abstractions/user.service';
@Directive()
@@ -13,7 +13,7 @@ export class PremiumComponent implements OnInit {
refreshPromise: Promise<any>;
constructor(protected i18nService: I18nService, protected platformUtilsService: PlatformUtilsService,
protected apiService: ApiService, protected userService: UserService) { }
protected apiService: ApiService, protected userService: UserService, private logService: LogService) { }
async ngOnInit() {
this.isPremium = await this.userService.canAccessPremium();
@@ -25,7 +25,9 @@ export class PremiumComponent implements OnInit {
await this.refreshPromise;
this.platformUtilsService.showToast('success', null, this.i18nService.t('refreshComplete'));
this.isPremium = await this.userService.canAccessPremium();
} catch { }
} catch (e) {
this.logService.error(e);
}
}
async purchase() {

View File

@@ -10,6 +10,7 @@ import { AuthService } from 'jslib-common/abstractions/auth.service';
import { CryptoService } from 'jslib-common/abstractions/crypto.service';
import { EnvironmentService } from 'jslib-common/abstractions/environment.service';
import { I18nService } from 'jslib-common/abstractions/i18n.service';
import { LogService } from 'jslib-common/abstractions/log.service';
import { PasswordGenerationService } from 'jslib-common/abstractions/passwordGeneration.service';
import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service';
import { StateService } from 'jslib-common/abstractions/state.service';
@@ -39,7 +40,8 @@ export class RegisterComponent extends CaptchaProtectedComponent implements OnIn
i18nService: I18nService, protected cryptoService: CryptoService,
protected apiService: ApiService, protected stateService: StateService,
platformUtilsService: PlatformUtilsService,
protected passwordGenerationService: PasswordGenerationService, environmentService: EnvironmentService) {
protected passwordGenerationService: PasswordGenerationService, environmentService: EnvironmentService,
protected logService: LogService) {
super(environmentService, i18nService, platformUtilsService);
this.showTerms = !platformUtilsService.isSelfHost();
}
@@ -158,7 +160,9 @@ export class RegisterComponent extends CaptchaProtectedComponent implements OnIn
}
this.platformUtilsService.showToast('success', null, this.i18nService.t('newAccountCreated'));
this.router.navigate([this.successRoute], { queryParams: { email: this.email } });
} catch { }
} catch (e) {
this.logService.error(e);
}
}
togglePassword(confirmField: boolean) {

View File

@@ -0,0 +1,77 @@
import {
Directive,
OnInit,
} from '@angular/core';
import { Router } from '@angular/router';
import { ApiService } from 'jslib-common/abstractions/api.service';
import { I18nService } from 'jslib-common/abstractions/i18n.service';
import { KeyConnectorService } from 'jslib-common/abstractions/keyConnector.service';
import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service';
import { StorageService } from 'jslib-common/abstractions/storage.service';
import { SyncService } from 'jslib-common/abstractions/sync.service';
import { UserService } from 'jslib-common/abstractions/user.service';
import { ConstantsService } from 'jslib-common/services/constants.service';
import { Organization } from 'jslib-common/models/domain/organization';
@Directive()
export class RemovePasswordComponent implements OnInit {
actionPromise: Promise<any>;
continuing: boolean = false;
leaving: boolean = false;
loading: boolean = true;
organization: Organization;
email: string;
constructor(private router: Router, private userService: UserService,
private apiService: ApiService, private syncService: SyncService,
private platformUtilsService: PlatformUtilsService, private i18nService: I18nService,
private keyConnectorService: KeyConnectorService, private storageService: StorageService) { }
async ngOnInit() {
this.organization = await this.keyConnectorService.getManagingOrganization();
this.email = await this.userService.getEmail();
await this.syncService.fullSync(false);
this.loading = false;
}
async convert() {
this.continuing = true;
this.actionPromise = this.keyConnectorService.migrateUser();
try {
await this.actionPromise;
this.platformUtilsService.showToast('success', null, this.i18nService.t('removedMasterPassword'));
await this.keyConnectorService.removeConvertAccountRequired();
this.router.navigate(['']);
} catch (e) {
this.platformUtilsService.showToast('error', this.i18nService.t('errorOccurred'), e.message);
}
}
async leave() {
const confirmed = await this.platformUtilsService.showDialog(
this.i18nService.t('leaveOrganizationConfirmation'), this.organization.name,
this.i18nService.t('yes'), this.i18nService.t('no'), 'warning');
if (!confirmed) {
return false;
}
try {
this.leaving = true;
this.actionPromise = this.apiService.postLeaveOrganization(this.organization.id).then(() => {
return this.syncService.fullSync(true);
});
await this.actionPromise;
this.platformUtilsService.showToast('success', null, this.i18nService.t('leftOrganization'));
await this.keyConnectorService.removeConvertAccountRequired();
this.router.navigate(['']);
} catch (e) {
this.platformUtilsService.showToast('error', this.i18nService.t('errorOccurred'), e);
}
}
}

View File

@@ -12,6 +12,7 @@ import { SendType } from 'jslib-common/enums/sendType';
import { EnvironmentService } from 'jslib-common/abstractions/environment.service';
import { I18nService } from 'jslib-common/abstractions/i18n.service';
import { LogService } from 'jslib-common/abstractions/log.service';
import { MessagingService } from 'jslib-common/abstractions/messaging.service';
import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service';
import { PolicyService } from 'jslib-common/abstractions/policy.service';
@@ -57,7 +58,8 @@ export class AddEditComponent implements OnInit {
constructor(protected i18nService: I18nService, protected platformUtilsService: PlatformUtilsService,
protected environmentService: EnvironmentService, protected datePipe: DatePipe,
protected sendService: SendService, protected userService: UserService,
protected messagingService: MessagingService, protected policyService: PolicyService) {
protected messagingService: MessagingService, protected policyService: PolicyService,
private logService: LogService) {
this.typeOptions = [
{ name: i18nService.t('sendTypeFile'), value: SendType.File },
{ name: i18nService.t('sendTypeText'), value: SendType.Text },
@@ -191,7 +193,9 @@ export class AddEditComponent implements OnInit {
try {
await this.formPromise;
return true;
} catch { }
} catch (e) {
this.logService.error(e);
}
return false;
}
@@ -218,7 +222,9 @@ export class AddEditComponent implements OnInit {
await this.load();
this.onDeletedSend.emit(this.send);
return true;
} catch { }
} catch (e) {
this.logService.error(e);
}
return false;
}

View File

@@ -183,6 +183,12 @@ export class EffluxDatesComponent implements OnInit {
return this.safariTimePresetOptions(DateField.ExpriationDate);
}
private get nextWeek(): Date {
const nextWeek = new Date();
nextWeek.setDate(nextWeek.getDate() + 7);
return nextWeek;
}
constructor(protected i18nService: I18nService, protected platformUtilsService: PlatformUtilsService,
protected datePipe: DatePipe) {
}
@@ -244,6 +250,15 @@ export class EffluxDatesComponent implements OnInit {
} else {
this.selectedDeletionDatePreset.setValue(DatePreset.SevenDays);
this.selectedExpirationDatePreset.setValue(DatePreset.Never);
switch (this.browserPath) {
case BrowserPath.Safari:
this.fallbackDeletionDate.setValue(this.nextWeek.toISOString().slice(0, 10));
this.fallbackDeletionTime.setValue(this.safariTimePresetOptions(DateField.DeletionDate)[1].twentyFourHour);
break;
default:
break;
}
}
}

View File

@@ -4,7 +4,6 @@ import {
OnInit,
} from '@angular/core';
import { OrganizationUserStatusType } from 'jslib-common/enums/organizationUserStatusType';
import { PolicyType } from 'jslib-common/enums/policyType';
import { SendType } from 'jslib-common/enums/sendType';
@@ -12,6 +11,7 @@ import { SendView } from 'jslib-common/models/view/sendView';
import { EnvironmentService } from 'jslib-common/abstractions/environment.service';
import { I18nService } from 'jslib-common/abstractions/i18n.service';
import { LogService } from 'jslib-common/abstractions/log.service';
import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service';
import { PolicyService } from 'jslib-common/abstractions/policy.service';
import { SearchService } from 'jslib-common/abstractions/search.service';
@@ -48,7 +48,8 @@ export class SendComponent implements OnInit {
constructor(protected sendService: SendService, protected i18nService: I18nService,
protected platformUtilsService: PlatformUtilsService, protected environmentService: EnvironmentService,
protected ngZone: NgZone, protected searchService: SearchService,
protected policyService: PolicyService, protected userService: UserService) { }
protected policyService: PolicyService, protected userService: UserService,
private logService: LogService) { }
async ngOnInit() {
this.disableSend = await this.policyService.policyAppliesToUser(PolicyType.DisableSend);
@@ -129,7 +130,9 @@ export class SendComponent implements OnInit {
this.platformUtilsService.showToast('success', null, this.i18nService.t('removedPassword'));
await this.load();
}
} catch { }
} catch (e) {
this.logService.error(e);
}
this.actionPromise = null;
}
@@ -156,7 +159,9 @@ export class SendComponent implements OnInit {
this.platformUtilsService.showToast('success', null, this.i18nService.t('deletedSend'));
await this.refresh();
}
} catch { }
} catch (e) {
this.logService.error(e);
}
this.actionPromise = null;
return true;
}

View File

@@ -4,6 +4,8 @@ import {
Router
} from '@angular/router';
import { first } from 'rxjs/operators';
import { ApiService } from 'jslib-common/abstractions/api.service';
import { CryptoService } from 'jslib-common/abstractions/crypto.service';
import { I18nService } from 'jslib-common/abstractions/i18n.service';
@@ -25,7 +27,6 @@ import { ChangePasswordComponent as BaseChangePasswordComponent } from './change
import { HashPurpose } from 'jslib-common/enums/hashPurpose';
import { KdfType } from 'jslib-common/enums/kdfType';
import { PolicyType } from 'jslib-common/enums/policyType';
import { Utils } from 'jslib-common/misc/utils';
@@ -53,14 +54,10 @@ export class SetPasswordComponent extends BaseChangePasswordComponent {
await this.syncService.fullSync(true);
this.syncLoading = false;
const queryParamsSub = this.route.queryParams.subscribe(async qParams => {
this.route.queryParams.pipe(first()).subscribe(async qParams => {
if (qParams.identifier != null) {
this.identifier = qParams.identifier;
}
if (queryParamsSub != null) {
queryParamsSub.unsubscribe();
}
});
// Automatic Enrollment Detection

View File

@@ -1,6 +1,10 @@
import { Directive } from '@angular/core';
import {
Directive,
OnInit
} from '@angular/core';
import { CryptoService } from 'jslib-common/abstractions/crypto.service';
import { KeyConnectorService } from 'jslib-common/abstractions/keyConnector.service';
import { StorageService } from 'jslib-common/abstractions/storage.service';
import { UserService } from 'jslib-common/abstractions/user.service';
import { VaultTimeoutService } from 'jslib-common/abstractions/vaultTimeout.service';
@@ -12,14 +16,20 @@ import { Utils } from 'jslib-common/misc/utils';
import { ModalRef } from './modal/modal.ref';
@Directive()
export class SetPinComponent {
export class SetPinComponent implements OnInit {
pin = '';
showPin = false;
masterPassOnRestart = true;
showMasterPassOnRestart = true;
constructor(private modalRef: ModalRef, private cryptoService: CryptoService, private userService: UserService,
private storageService: StorageService, private vaultTimeoutService: VaultTimeoutService) { }
private storageService: StorageService, private vaultTimeoutService: VaultTimeoutService,
private keyConnectorService: KeyConnectorService) { }
async ngOnInit() {
this.showMasterPassOnRestart = this.masterPassOnRestart = !await this.keyConnectorService.getUsesKeyConnector();
}
toggleVisibility() {
this.showPin = !this.showPin;

View File

@@ -11,6 +11,7 @@ import { OrganizationUserStatusType } from 'jslib-common/enums/organizationUserS
import { CipherService } from 'jslib-common/abstractions/cipher.service';
import { CollectionService } from 'jslib-common/abstractions/collection.service';
import { I18nService } from 'jslib-common/abstractions/i18n.service';
import { LogService } from 'jslib-common/abstractions/log.service';
import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service';
import { UserService } from 'jslib-common/abstractions/user.service';
@@ -35,7 +36,7 @@ export class ShareComponent implements OnInit {
constructor(protected collectionService: CollectionService, protected platformUtilsService: PlatformUtilsService,
protected i18nService: I18nService, protected userService: UserService,
protected cipherService: CipherService) { }
protected cipherService: CipherService, private logService: LogService) { }
async ngOnInit() {
await this.load();
@@ -88,7 +89,9 @@ export class ShareComponent implements OnInit {
});
await this.formPromise;
return true;
} catch { }
} catch (e) {
this.logService.error(e);
}
return false;
}

View File

@@ -4,11 +4,14 @@ import {
Router,
} from '@angular/router';
import { first } from 'rxjs/operators';
import { ApiService } from 'jslib-common/abstractions/api.service';
import { AuthService } from 'jslib-common/abstractions/auth.service';
import { CryptoFunctionService } from 'jslib-common/abstractions/cryptoFunction.service';
import { EnvironmentService } from 'jslib-common/abstractions/environment.service';
import { I18nService } from 'jslib-common/abstractions/i18n.service';
import { LogService } from 'jslib-common/abstractions/log.service';
import { PasswordGenerationService } from 'jslib-common/abstractions/passwordGeneration.service';
import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service';
import { StateService } from 'jslib-common/abstractions/state.service';
@@ -47,17 +50,17 @@ export class SsoComponent {
protected storageService: StorageService, protected stateService: StateService,
protected platformUtilsService: PlatformUtilsService, protected apiService: ApiService,
protected cryptoFunctionService: CryptoFunctionService, protected environmentService: EnvironmentService,
protected passwordGenerationService: PasswordGenerationService) { }
protected passwordGenerationService: PasswordGenerationService, protected logService: LogService) { }
async ngOnInit() {
const queryParamsSub = this.route.queryParams.subscribe(async qParams => {
this.route.queryParams.pipe(first()).subscribe(async qParams => {
if (qParams.code != null && qParams.state != null) {
const codeVerifier = await this.storageService.get<string>(ConstantsService.ssoCodeVerifierKey);
const state = await this.storageService.get<string>(ConstantsService.ssoStateKey);
await this.storageService.remove(ConstantsService.ssoCodeVerifierKey);
await this.storageService.remove(ConstantsService.ssoStateKey);
if (qParams.code != null && codeVerifier != null && state != null && this.checkState(state, qParams.state)) {
await this.logIn(qParams.code, codeVerifier, this.getOrgIdentiferFromState(qParams.state));
await this.logIn(qParams.code, codeVerifier, this.getOrgIdentifierFromState(qParams.state));
}
} else if (qParams.clientId != null && qParams.redirectUri != null && qParams.state != null &&
qParams.codeChallenge != null) {
@@ -66,9 +69,6 @@ export class SsoComponent {
this.codeChallenge = qParams.codeChallenge;
this.clientId = qParams.clientId;
}
if (queryParamsSub != null) {
queryParamsSub.unsubscribe();
}
});
}
@@ -140,7 +140,7 @@ export class SsoComponent {
private async logIn(code: string, codeVerifier: string, orgIdFromState: string) {
this.loggingIn = true;
try {
this.formPromise = this.authService.logInSso(code, codeVerifier, this.redirectUri);
this.formPromise = this.authService.logInSso(code, codeVerifier, this.redirectUri, orgIdFromState);
const response = await this.formPromise;
if (response.twoFactor) {
if (this.onSuccessfulLoginTwoFactorNavigate != null) {
@@ -181,11 +181,16 @@ export class SsoComponent {
this.router.navigate([this.successRoute]);
}
}
} catch { }
} catch (e) {
this.logService.error(e);
if (e.message === 'Unable to reach key connector') {
this.platformUtilsService.showToast('error', null, this.i18nService.t('ssoKeyConnectorUnavailable'));
}
}
this.loggingIn = false;
}
private getOrgIdentiferFromState(state: string): string {
private getOrgIdentifierFromState(state: string): string {
if (state === null || state === undefined) {
return null;
}

View File

@@ -0,0 +1,85 @@
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">&times;</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,
},
},
],
};
}
}

View File

@@ -17,6 +17,7 @@ import { ApiService } from 'jslib-common/abstractions/api.service';
import { AuthService } from 'jslib-common/abstractions/auth.service';
import { EnvironmentService } from 'jslib-common/abstractions/environment.service';
import { I18nService } from 'jslib-common/abstractions/i18n.service';
import { LogService } from 'jslib-common/abstractions/log.service';
import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service';
import { StateService } from 'jslib-common/abstractions/state.service';
import { StorageService } from 'jslib-common/abstractions/storage.service';
@@ -57,7 +58,8 @@ export class TwoFactorComponent implements OnInit, OnDestroy {
protected i18nService: I18nService, protected apiService: ApiService,
protected platformUtilsService: PlatformUtilsService, protected win: Window,
protected environmentService: EnvironmentService, protected stateService: StateService,
protected storageService: StorageService, protected route: ActivatedRoute) {
protected storageService: StorageService, protected route: ActivatedRoute,
protected logService: LogService) {
this.webAuthnSupported = this.platformUtilsService.supportsWebAuthn(win);
}
@@ -209,14 +211,18 @@ export class TwoFactorComponent implements OnInit, OnDestroy {
}
try {
const request = new TwoFactorEmailRequest(this.authService.email, this.authService.masterPasswordHash);
const request = new TwoFactorEmailRequest();
request.email = this.authService.email;
request.masterPasswordHash = this.authService.masterPasswordHash;
this.emailPromise = this.apiService.postTwoFactorEmail(request);
await this.emailPromise;
if (doToast) {
this.platformUtilsService.showToast('success', null,
this.i18nService.t('verificationCodeEmailSent', this.twoFactorEmail));
}
} catch { }
} catch (e) {
this.logService.error(e);
}
this.emailPromise = null;
}

View File

@@ -3,10 +3,12 @@ import { Directive } from '@angular/core';
import { ApiService } from 'jslib-common/abstractions/api.service';
import { CryptoService } from 'jslib-common/abstractions/crypto.service';
import { I18nService } from 'jslib-common/abstractions/i18n.service';
import { LogService } from 'jslib-common/abstractions/log.service';
import { MessagingService } from 'jslib-common/abstractions/messaging.service';
import { PasswordGenerationService } from 'jslib-common/abstractions/passwordGeneration.service';
import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service';
import { PolicyService } from 'jslib-common/abstractions/policy.service';
import { SyncService } from 'jslib-common/abstractions/sync.service';
import { UserService } from 'jslib-common/abstractions/user.service';
import { ChangePasswordComponent as BaseChangePasswordComponent } from './change-password.component';
@@ -29,11 +31,17 @@ export class UpdateTempPasswordComponent extends BaseChangePasswordComponent {
constructor(i18nService: I18nService, platformUtilsService: PlatformUtilsService,
passwordGenerationService: PasswordGenerationService, policyService: PolicyService,
cryptoService: CryptoService, userService: UserService,
messagingService: MessagingService, private apiService: ApiService) {
messagingService: MessagingService, private apiService: ApiService,
private syncService: SyncService, private logService: LogService) {
super(i18nService, cryptoService, messagingService, userService, passwordGenerationService,
platformUtilsService, policyService);
}
async ngOnInit() {
await this.syncService.fullSync(true);
super.ngOnInit();
}
togglePassword(confirmField: boolean) {
this.showPassword = !this.showPassword;
document.getElementById(confirmField ? 'masterPasswordRetype' : 'masterPassword').focus();
@@ -70,7 +78,9 @@ export class UpdateTempPasswordComponent extends BaseChangePasswordComponent {
const newEncKey = await this.cryptoService.remakeEncKey(newKey, userEncKey);
await this.performSubmitActions(newPasswordHash, newKey, newEncKey);
} catch { }
} catch (e) {
this.logService.error(e);
}
}
async performSubmitActions(masterPasswordHash: string, key: SymmetricCryptoKey,
@@ -92,6 +102,8 @@ export class UpdateTempPasswordComponent extends BaseChangePasswordComponent {
} else {
this.messagingService.send('logout');
}
} catch { }
} catch (e) {
this.logService.error(e);
}
}
}

View File

@@ -0,0 +1,25 @@
<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="fa fa-check-circle-o" 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>

View File

@@ -0,0 +1,105 @@
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';
@Component({
selector: 'app-verify-master-password',
templateUrl: 'verify-master-password.component.html',
providers: [
{
provide: NG_VALUE_ACCESSOR,
multi: true,
useExisting: VerifyMasterPasswordComponent,
},
],
animations: [
trigger('sent', [
transition(':enter', [
style({ opacity: 0 }),
animate('100ms', style({ opacity: 1 })),
]),
]),
],
})
export class VerifyMasterPasswordComponent implements ControlValueAccessor, OnInit {
usesKeyConnector: boolean = false;
disableRequestOTP: boolean = false;
sentCode: boolean = 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 => 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,
});
}
}

View File

@@ -21,6 +21,7 @@ import { CipherService } from 'jslib-common/abstractions/cipher.service';
import { CryptoService } from 'jslib-common/abstractions/crypto.service';
import { EventService } from 'jslib-common/abstractions/event.service';
import { I18nService } from 'jslib-common/abstractions/i18n.service';
import { LogService } from 'jslib-common/abstractions/log.service';
import { PasswordRepromptService } from 'jslib-common/abstractions/passwordReprompt.service';
import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service';
import { TokenService } from 'jslib-common/abstractions/token.service';
@@ -31,7 +32,6 @@ import { ErrorResponse } from 'jslib-common/models/response/errorResponse';
import { AttachmentView } from 'jslib-common/models/view/attachmentView';
import { CipherView } from 'jslib-common/models/view/cipherView';
import { FieldView } from 'jslib-common/models/view/fieldView';
import { LoginUriView } from 'jslib-common/models/view/loginUriView';
const BroadcasterSubscriptionId = 'ViewComponent';
@@ -69,7 +69,7 @@ export class ViewComponent implements OnDestroy, OnInit {
protected broadcasterService: BroadcasterService, protected ngZone: NgZone,
protected changeDetectorRef: ChangeDetectorRef, protected userService: UserService,
protected eventService: EventService, protected apiService: ApiService,
protected passwordRepromptService: PasswordRepromptService) { }
protected passwordRepromptService: PasswordRepromptService, private logService: LogService) { }
ngOnInit() {
this.broadcasterService.subscribe(BroadcasterSubscriptionId, (message: any) => {
@@ -159,7 +159,9 @@ export class ViewComponent implements OnDestroy, OnInit {
this.platformUtilsService.showToast('success', null,
this.i18nService.t(this.cipher.isDeleted ? 'permanentlyDeletedItem' : 'deletedItem'));
this.onDeletedCipher.emit(this.cipher);
} catch { }
} catch (e) {
this.logService.error(e);
}
return true;
}
@@ -180,7 +182,9 @@ export class ViewComponent implements OnDestroy, OnInit {
await this.restoreCipher();
this.platformUtilsService.showToast('success', null, this.i18nService.t('restoredItem'));
this.onRestoredCipher.emit(this.cipher);
} catch { }
} catch (e) {
this.logService.error(e);
}
return true;
}

View File

@@ -2,8 +2,11 @@ import {
Directive,
ElementRef,
Input,
NgZone,
} from '@angular/core';
import { take } from 'rxjs/operators';
import { Utils } from 'jslib-common/misc/utils';
@Directive({
@@ -16,11 +19,15 @@ export class AutofocusDirective {
private autofocus: boolean;
constructor(private el: ElementRef) { }
constructor(private el: ElementRef, private ngZone: NgZone) { }
ngOnInit() {
if (!Utils.isMobileBrowser && this.autofocus) {
this.el.nativeElement.focus();
if (this.ngZone.isStable) {
this.el.nativeElement.focus();
} else {
this.ngZone.onStable.pipe(take(1)).subscribe(() => this.el.nativeElement.focus());
}
}
}
}

View File

@@ -6,6 +6,7 @@ import {
RouterStateSnapshot,
} from '@angular/router';
import { KeyConnectorService } from 'jslib-common/abstractions/keyConnector.service';
import { MessagingService } from 'jslib-common/abstractions/messaging.service';
import { UserService } from 'jslib-common/abstractions/user.service';
import { VaultTimeoutService } from 'jslib-common/abstractions/vaultTimeout.service';
@@ -13,7 +14,7 @@ import { VaultTimeoutService } from 'jslib-common/abstractions/vaultTimeout.serv
@Injectable()
export class AuthGuardService implements CanActivate {
constructor(private vaultTimeoutService: VaultTimeoutService, private userService: UserService,
private router: Router, private messagingService: MessagingService) { }
private router: Router, private messagingService: MessagingService, private keyConnectorService: KeyConnectorService) { }
async canActivate(route: ActivatedRouteSnapshot, routerState: RouterStateSnapshot) {
const isAuthed = await this.userService.isAuthenticated();
@@ -31,6 +32,11 @@ export class AuthGuardService implements CanActivate {
return false;
}
if (!routerState.url.includes('remove-password') && await this.keyConnectorService.getConvertAccountRequired()) {
this.router.navigate(['/remove-password']);
return false;
}
return true;
}
}

View File

@@ -0,0 +1,391 @@
import {
Injector,
LOCALE_ID,
NgModule,
} from '@angular/core';
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 { PasswordGenerationService } from 'jslib-common/services/passwordGeneration.service';
import { PolicyService } from 'jslib-common/services/policy.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 { SyncService } from 'jslib-common/services/sync.service';
import { TokenService } from 'jslib-common/services/token.service';
import { TotpService } from 'jslib-common/services/totp.service';
import { UserService } from 'jslib-common/services/user.service';
import { UserVerificationService } from 'jslib-common/services/userVerification.service';
import { VaultTimeoutService } from 'jslib-common/services/vaultTimeout.service';
import { WebCryptoFunctionService } from 'jslib-common/services/webCryptoFunction.service';
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, Urls } 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 {
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 { 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 { 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 { UserService as UserServiceAbstraction } from 'jslib-common/abstractions/user.service';
import { UserVerificationService as UserVerificationServiceAbstraction } from 'jslib-common/abstractions/userVerification.service';
import { VaultTimeoutService as VaultTimeoutServiceAbstraction } from 'jslib-common/abstractions/vaultTimeout.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,
UserServiceAbstraction,
TokenServiceAbstraction,
AppIdServiceAbstraction,
I18nServiceAbstraction,
PlatformUtilsServiceAbstraction,
MessagingServiceAbstraction,
VaultTimeoutServiceAbstraction,
LogService,
CryptoFunctionServiceAbstraction,
EnvironmentServiceAbstraction,
KeyConnectorServiceAbstraction,
],
},
{
provide: CipherServiceAbstraction,
useFactory: (cryptoService: CryptoServiceAbstraction, userService: UserServiceAbstraction,
settingsService: SettingsServiceAbstraction, apiService: ApiServiceAbstraction,
fileUploadService: FileUploadServiceAbstraction, storageService: StorageServiceAbstraction,
i18nService: I18nServiceAbstraction, injector: Injector, logService: LogService) =>
new CipherService(cryptoService, userService, settingsService, apiService, fileUploadService,
storageService, i18nService, () => injector.get(SearchServiceAbstraction), logService),
deps: [
CryptoServiceAbstraction,
UserServiceAbstraction,
SettingsServiceAbstraction,
ApiServiceAbstraction,
FileUploadServiceAbstraction,
StorageServiceAbstraction,
I18nServiceAbstraction,
Injector, // TODO: Get rid of this circular dependency!
LogService,
],
},
{
provide: FolderServiceAbstraction,
useClass: FolderService,
deps: [
CryptoServiceAbstraction,
UserServiceAbstraction,
ApiServiceAbstraction,
StorageServiceAbstraction,
I18nServiceAbstraction,
CipherServiceAbstraction,
],
},
{ provide: LogService, useFactory: () => new ConsoleLogService(false) },
{
provide: CollectionServiceAbstraction,
useClass: CollectionService,
deps: [
CryptoServiceAbstraction,
UserServiceAbstraction,
StorageServiceAbstraction,
I18nServiceAbstraction,
],
},
{
provide: EnvironmentServiceAbstraction,
useClass: EnvironmentService,
deps: [StorageServiceAbstraction],
},
{
provide: TotpServiceAbstraction,
useClass: TotpService,
deps: [
StorageServiceAbstraction,
CryptoFunctionServiceAbstraction,
LogService,
],
},
{ provide: TokenServiceAbstraction, useClass: TokenService, deps: [StorageServiceAbstraction] },
{
provide: CryptoServiceAbstraction,
useClass: CryptoService,
deps: [
StorageServiceAbstraction,
'SECURE_STORAGE',
CryptoFunctionServiceAbstraction,
PlatformUtilsServiceAbstraction,
LogService,
],
},
{
provide: PasswordGenerationServiceAbstraction,
useClass: PasswordGenerationService,
deps: [
CryptoServiceAbstraction,
StorageServiceAbstraction,
PolicyServiceAbstraction,
],
},
{
provide: ApiServiceAbstraction,
useFactory: (tokenService: TokenServiceAbstraction, platformUtilsService: PlatformUtilsServiceAbstraction,
environmentService: EnvironmentServiceAbstraction, messagingService: MessagingServiceAbstraction) =>
new ApiService(tokenService, platformUtilsService, environmentService,
async (expired: boolean) => messagingService.send('logout', { expired: expired })),
deps: [
TokenServiceAbstraction,
PlatformUtilsServiceAbstraction,
EnvironmentServiceAbstraction,
MessagingServiceAbstraction,
],
},
{
provide: FileUploadServiceAbstraction,
useClass: FileUploadService,
deps: [
LogService,
ApiServiceAbstraction,
],
},
{
provide: SyncServiceAbstraction,
useFactory: (userService: UserServiceAbstraction, apiService: ApiServiceAbstraction,
settingsService: SettingsServiceAbstraction, folderService: FolderServiceAbstraction,
cipherService: CipherServiceAbstraction, cryptoService: CryptoServiceAbstraction,
collectionService: CollectionServiceAbstraction, storageService: StorageServiceAbstraction,
messagingService: MessagingServiceAbstraction, policyService: PolicyServiceAbstraction,
sendService: SendServiceAbstraction, logService: LogService, tokenService: TokenService,
keyConnectorService: KeyConnectorServiceAbstraction) => new SyncService(userService, apiService,
settingsService, folderService, cipherService, cryptoService, collectionService, storageService,
messagingService, policyService, sendService, logService, tokenService, keyConnectorService,
async (expired: boolean) => messagingService.send('logout', { expired: expired })),
deps: [
UserServiceAbstraction,
ApiServiceAbstraction,
SettingsServiceAbstraction,
FolderServiceAbstraction,
CipherServiceAbstraction,
CryptoServiceAbstraction,
CollectionServiceAbstraction,
StorageServiceAbstraction,
MessagingServiceAbstraction,
PolicyServiceAbstraction,
SendServiceAbstraction,
LogService,
TokenServiceAbstraction,
KeyConnectorServiceAbstraction,
],
},
{
provide: UserServiceAbstraction,
useClass: UserService,
deps: [TokenServiceAbstraction, StorageServiceAbstraction],
},
{ provide: BroadcasterServiceAbstraction, useClass: BroadcasterService },
{
provide: SettingsServiceAbstraction,
useClass: SettingsService,
deps: [UserServiceAbstraction, StorageServiceAbstraction],
},
{
provide: VaultTimeoutServiceAbstraction,
useFactory: (cipherService: CipherServiceAbstraction, folderService: FolderServiceAbstraction,
collectionService: CollectionServiceAbstraction, cryptoService: CryptoServiceAbstraction,
platformUtilsService: PlatformUtilsServiceAbstraction, storageService: StorageServiceAbstraction,
messagingService: MessagingServiceAbstraction, searchService: SearchServiceAbstraction,
userService: UserServiceAbstraction, tokenService: TokenServiceAbstraction,
policyService: PolicyServiceAbstraction, keyConnectorService: KeyConnectorServiceAbstraction) =>
new VaultTimeoutService(cipherService, folderService, collectionService, cryptoService,
platformUtilsService, storageService, messagingService, searchService, userService, tokenService,
policyService, keyConnectorService, null,
async () => messagingService.send('logout', { expired: false })),
deps: [
CipherServiceAbstraction,
FolderServiceAbstraction,
CollectionServiceAbstraction,
CryptoServiceAbstraction,
PlatformUtilsServiceAbstraction,
StorageServiceAbstraction,
MessagingServiceAbstraction,
SearchServiceAbstraction,
UserServiceAbstraction,
TokenServiceAbstraction,
PolicyServiceAbstraction,
],
},
{ provide: StateServiceAbstraction, useClass: StateService },
{
provide: ExportServiceAbstraction,
useClass: ExportService,
deps: [
FolderServiceAbstraction,
CipherServiceAbstraction,
ApiServiceAbstraction,
CryptoServiceAbstraction,
],
},
{
provide: SearchServiceAbstraction,
useClass: SearchService,
deps: [
CipherServiceAbstraction,
LogService,
I18nServiceAbstraction,
],
},
{
provide: NotificationsServiceAbstraction,
useFactory: (userService: UserServiceAbstraction, syncService: SyncServiceAbstraction,
appIdService: AppIdServiceAbstraction, apiService: ApiServiceAbstraction,
vaultTimeoutService: VaultTimeoutServiceAbstraction, environmentService: EnvironmentServiceAbstraction,
messagingService: MessagingServiceAbstraction, logService: LogService) =>
new NotificationsService(userService, syncService, appIdService, apiService, vaultTimeoutService,
environmentService, async () => messagingService.send('logout', { expired: true }), logService),
deps: [
UserServiceAbstraction,
SyncServiceAbstraction,
AppIdServiceAbstraction,
ApiServiceAbstraction,
VaultTimeoutServiceAbstraction,
EnvironmentServiceAbstraction,
MessagingServiceAbstraction,
LogService,
],
},
{
provide: CryptoFunctionServiceAbstraction,
useClass: WebCryptoFunctionService,
deps: ['WINDOW', PlatformUtilsServiceAbstraction],
},
{
provide: EventServiceAbstraction,
useClass: EventService,
deps: [
StorageServiceAbstraction,
ApiServiceAbstraction,
UserServiceAbstraction,
CipherServiceAbstraction,
LogService,
],
},
{
provide: PolicyServiceAbstraction,
useClass: PolicyService,
deps: [
UserServiceAbstraction,
StorageServiceAbstraction,
ApiServiceAbstraction,
],
},
{
provide: SendServiceAbstraction,
useClass: SendService,
deps: [
CryptoServiceAbstraction,
UserServiceAbstraction,
ApiServiceAbstraction,
FileUploadServiceAbstraction,
StorageServiceAbstraction,
I18nServiceAbstraction,
CryptoFunctionServiceAbstraction,
],
},
{
provide: KeyConnectorServiceAbstraction,
useClass: KeyConnectorService,
deps: [
StorageServiceAbstraction,
UserServiceAbstraction,
CryptoServiceAbstraction,
ApiServiceAbstraction,
TokenServiceAbstraction,
LogService,
],
},
{
provide: UserVerificationServiceAbstraction,
useClass: UserVerificationService,
deps: [
CryptoServiceAbstraction,
I18nServiceAbstraction,
ApiServiceAbstraction,
],
},
{ provide: PasswordRepromptServiceAbstraction, useClass: PasswordRepromptService },
],
})
export class JslibServicesModule {
}

View File

@@ -22,19 +22,32 @@ export class ModalConfig<D = any> {
@Injectable()
export class ModalService {
protected modalCount = 0;
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) {}
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]> {
this.modalCount++;
const [modalRef, modalComponentRef] = this.openInternal(componentType, null, false);
modalComponentRef.instance.setComponentParameters = setComponentParameters;
@@ -49,7 +62,6 @@ export class ModalService {
if (!(config?.allowMultipleModals ?? false) && this.modalCount > 0) {
return;
}
this.modalCount++;
const [modalRef, _] = this.openInternal(componentType, config, true);
@@ -85,11 +97,17 @@ export class ModalService {
this.applicationRef.detachView(componentRef.hostView);
}
componentRef.destroy();
this.modalCount--;
this.modalList.pop();
if (this.modalCount > 0) {
this.topModal.instance.getFocus();
}
});
this.setupHandlers(modalRef);
this.modalList.push(componentRef);
return [modalRef, componentRef];
}
@@ -100,19 +118,20 @@ export class ModalService {
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`;
document.body.appendChild(backdrop);
modalEl.prepend(backdrop);
el.querySelector('.modal-dialog').addEventListener('click', (e: Event) => {
dialogEl.addEventListener('click', (e: Event) => {
e.stopPropagation();
});
dialogEl.style.zIndex = `${this.modalCount}050`;
const modalEl: HTMLElement = el.querySelector('.modal');
modalEl.style.zIndex = `${this.modalCount}050`;
const modals = Array.from(el.querySelectorAll('.modal, .modal *[data-dismiss="modal"]'));
const modals = Array.from(el.querySelectorAll('.modal-backdrop, .modal *[data-dismiss="modal"]'));
for (const closeElement of modals) {
closeElement.addEventListener('click', event => {
modalRef.close();
@@ -127,10 +146,6 @@ export class ModalService {
if (this.modalCount === 0) {
document.body.classList.remove('modal-open');
}
if (backdrop != null) {
document.body.removeChild(backdrop);
}
});
}

View File

@@ -1,5 +1,6 @@
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';
@@ -9,13 +10,17 @@ import { ModalService } from './modal.service';
export class PasswordRepromptService implements PasswordRepromptServiceAbstraction {
protected component = PasswordRepromptComponent;
constructor(private modalService: ModalService) { }
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) {
@@ -25,4 +30,8 @@ export class PasswordRepromptService implements PasswordRepromptServiceAbstracti
const result = await ref.onClosedPromise();
return result === true;
}
async enabled() {
return !await this.keyConnectorService.getUsesKeyConnector();
}
}

941
common/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -27,17 +27,17 @@
"@types/tldjs": "^2.3.0",
"@types/zxcvbn": "^4.4.1",
"rimraf": "^3.0.2",
"typescript": "4.1.5"
"typescript": "4.3.5"
},
"dependencies": {
"@microsoft/signalr": "3.1.13",
"@microsoft/signalr-protocol-msgpack": "3.1.13",
"@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": "^0.10.0",
"papaparse": "^5.3.0",
"rxjs": "6.6.7",
"rxjs": "^7.4.0",
"tldjs": "^2.3.1",
"zxcvbn": "^4.4.2"
}

View File

@@ -1,6 +1,7 @@
import { PolicyType } from '../enums/policyType';
import { SetKeyConnectorKeyRequest } from '../models/request/account/setKeyConnectorKeyRequest';
import { VerifyOTPRequest } from '../models/request/account/verifyOTPRequest';
import { EnvironmentUrls } from '../models/domain/environmentUrls';
import { AttachmentRequest } from '../models/request/attachmentRequest';
import { BitPayInvoiceRequest } from '../models/request/bitPayInvoiceRequest';
@@ -29,7 +30,11 @@ import { ImportCiphersRequest } from '../models/request/importCiphersRequest';
import { ImportDirectoryRequest } from '../models/request/importDirectoryRequest';
import { ImportOrganizationCiphersRequest } from '../models/request/importOrganizationCiphersRequest';
import { KdfRequest } from '../models/request/kdfRequest';
import { KeyConnectorUserKeyRequest } from '../models/request/keyConnectorUserKeyRequest';
import { KeysRequest } from '../models/request/keysRequest';
import { OrganizationSponsorshipCreateRequest } from '../models/request/organization/organizationSponsorshipCreateRequest';
import { OrganizationSponsorshipRedeemRequest } from '../models/request/organization/organizationSponsorshipRedeemRequest';
import { OrganizationSsoRequest } from '../models/request/organization/organizationSsoRequest';
import { OrganizationCreateRequest } from '../models/request/organizationCreateRequest';
import { OrganizationImportRequest } from '../models/request/organizationImportRequest';
import { OrganizationKeysRequest } from '../models/request/organizationKeysRequest';
@@ -48,7 +53,6 @@ import { OrganizationUserUpdateGroupsRequest } from '../models/request/organizat
import { OrganizationUserUpdateRequest } from '../models/request/organizationUserUpdateRequest';
import { PasswordHintRequest } from '../models/request/passwordHintRequest';
import { PasswordRequest } from '../models/request/passwordRequest';
import { PasswordVerificationRequest } from '../models/request/passwordVerificationRequest';
import { PaymentRequest } from '../models/request/paymentRequest';
import { PolicyRequest } from '../models/request/policyRequest';
import { PreloginRequest } from '../models/request/preloginRequest';
@@ -64,6 +68,7 @@ import { ProviderUserInviteRequest } from '../models/request/provider/providerUs
import { ProviderUserUpdateRequest } from '../models/request/provider/providerUserUpdateRequest';
import { RegisterRequest } from '../models/request/registerRequest';
import { SeatRequest } from '../models/request/seatRequest';
import { SecretVerificationRequest } from '../models/request/secretVerificationRequest';
import { SelectionReadOnlyRequest } from '../models/request/selectionReadOnlyRequest';
import { SendAccessRequest } from '../models/request/sendAccessRequest';
import { SendRequest } from '../models/request/sendRequest';
@@ -114,7 +119,9 @@ import {
import { IdentityCaptchaResponse } from '../models/response/identityCaptchaResponse';
import { IdentityTokenResponse } from '../models/response/identityTokenResponse';
import { IdentityTwoFactorResponse } from '../models/response/identityTwoFactorResponse';
import { KeyConnectorUserKeyResponse } from '../models/response/keyConnectorUserKeyResponse';
import { ListResponse } from '../models/response/listResponse';
import { OrganizationSsoResponse } from '../models/response/organization/organizationSsoResponse';
import { OrganizationAutoEnrollStatusResponse } from '../models/response/organizationAutoEnrollStatusResponse';
import { OrganizationKeysResponse } from '../models/response/organizationKeysResponse';
import { OrganizationResponse } from '../models/response/organizationResponse';
@@ -171,8 +178,9 @@ export abstract class ApiService {
postEmail: (request: EmailRequest) => Promise<any>;
postPassword: (request: PasswordRequest) => Promise<any>;
setPassword: (request: SetPasswordRequest) => Promise<any>;
postSecurityStamp: (request: PasswordVerificationRequest) => Promise<any>;
deleteAccount: (request: PasswordVerificationRequest) => Promise<any>;
postSetKeyConnectorKey: (request: SetKeyConnectorKeyRequest) => Promise<any>;
postSecurityStamp: (request: SecretVerificationRequest) => Promise<any>;
deleteAccount: (request: SecretVerificationRequest) => Promise<any>;
getAccountRevisionDate: () => Promise<number>;
postPasswordHint: (request: PasswordHintRequest) => Promise<any>;
postRegister: (request: RegisterRequest) => Promise<any>;
@@ -187,14 +195,16 @@ export abstract class ApiService {
postAccountKeys: (request: KeysRequest) => Promise<any>;
postAccountVerifyEmail: () => Promise<any>;
postAccountVerifyEmailToken: (request: VerifyEmailRequest) => Promise<any>;
postAccountVerifyPassword: (request: PasswordVerificationRequest) => Promise<any>;
postAccountVerifyPassword: (request: SecretVerificationRequest) => Promise<any>;
postAccountRecoverDelete: (request: DeleteRecoverRequest) => Promise<any>;
postAccountRecoverDeleteToken: (request: VerifyDeleteRecoverRequest) => Promise<any>;
postAccountKdf: (request: KdfRequest) => Promise<any>;
getEnterprisePortalSignInToken: () => Promise<string>;
postUserApiKey: (id: string, request: PasswordVerificationRequest) => Promise<ApiKeyResponse>;
postUserRotateApiKey: (id: string, request: PasswordVerificationRequest) => Promise<ApiKeyResponse>;
postUserApiKey: (id: string, request: SecretVerificationRequest) => Promise<ApiKeyResponse>;
postUserRotateApiKey: (id: string, request: SecretVerificationRequest) => Promise<ApiKeyResponse>;
putUpdateTempPassword: (request: UpdateTempPasswordRequest) => Promise<any>;
postAccountRequestOTP: () => Promise<void>;
postAccountVerifyOTP: (request: VerifyOTPRequest) => Promise<void>;
postConvertToKeyConnector: () => Promise<void>;
getFolder: (id: string) => Promise<FolderResponse>;
postFolder: (request: FolderRequest) => Promise<FolderResponse>;
@@ -236,7 +246,7 @@ export abstract class ApiService {
putShareCiphers: (request: CipherBulkShareRequest) => Promise<any>;
putCipherCollections: (id: string, request: CipherCollectionsRequest) => Promise<any>;
putCipherCollectionsAdmin: (id: string, request: CipherCollectionsRequest) => Promise<any>;
postPurgeCiphers: (request: PasswordVerificationRequest, organizationId?: string) => Promise<any>;
postPurgeCiphers: (request: SecretVerificationRequest, organizationId?: string) => Promise<any>;
postImportCiphers: (request: ImportCiphersRequest) => Promise<any>;
postImportOrganizationCiphers: (organizationId: string, request: ImportOrganizationCiphersRequest) => Promise<any>;
putDeleteCipher: (id: string) => Promise<any>;
@@ -325,15 +335,15 @@ export abstract class ApiService {
getTwoFactorProviders: () => Promise<ListResponse<TwoFactorProviderResponse>>;
getTwoFactorOrganizationProviders: (organizationId: string) => Promise<ListResponse<TwoFactorProviderResponse>>;
getTwoFactorAuthenticator: (request: PasswordVerificationRequest) => Promise<TwoFactorAuthenticatorResponse>;
getTwoFactorEmail: (request: PasswordVerificationRequest) => Promise<TwoFactorEmailResponse>;
getTwoFactorDuo: (request: PasswordVerificationRequest) => Promise<TwoFactorDuoResponse>;
getTwoFactorAuthenticator: (request: SecretVerificationRequest) => Promise<TwoFactorAuthenticatorResponse>;
getTwoFactorEmail: (request: SecretVerificationRequest) => Promise<TwoFactorEmailResponse>;
getTwoFactorDuo: (request: SecretVerificationRequest) => Promise<TwoFactorDuoResponse>;
getTwoFactorOrganizationDuo: (organizationId: string,
request: PasswordVerificationRequest) => Promise<TwoFactorDuoResponse>;
getTwoFactorYubiKey: (request: PasswordVerificationRequest) => Promise<TwoFactorYubiKeyResponse>;
getTwoFactorWebAuthn: (request: PasswordVerificationRequest) => Promise<TwoFactorWebAuthnResponse>;
getTwoFactorWebAuthnChallenge: (request: PasswordVerificationRequest) => Promise<ChallengeResponse>;
getTwoFactorRecover: (request: PasswordVerificationRequest) => Promise<TwoFactorRecoverResponse>;
request: SecretVerificationRequest) => Promise<TwoFactorDuoResponse>;
getTwoFactorYubiKey: (request: SecretVerificationRequest) => Promise<TwoFactorYubiKeyResponse>;
getTwoFactorWebAuthn: (request: SecretVerificationRequest) => Promise<TwoFactorWebAuthnResponse>;
getTwoFactorWebAuthnChallenge: (request: SecretVerificationRequest) => Promise<ChallengeResponse>;
getTwoFactorRecover: (request: SecretVerificationRequest) => Promise<TwoFactorRecoverResponse>;
putTwoFactorAuthenticator: (
request: UpdateTwoFactorAuthenticatorRequest) => Promise<TwoFactorAuthenticatorResponse>;
putTwoFactorEmail: (request: UpdateTwoFactorEmailRequest) => Promise<TwoFactorEmailResponse>;
@@ -373,14 +383,16 @@ export abstract class ApiService {
getOrganizationLicense: (id: string, installationId: string) => Promise<any>;
getOrganizationTaxInfo: (id: string) => Promise<TaxInfoResponse>;
getOrganizationAutoEnrollStatus: (identifier: string) => Promise<OrganizationAutoEnrollStatusResponse>;
getOrganizationSso: (id: string) => Promise<OrganizationSsoResponse>;
postOrganization: (request: OrganizationCreateRequest) => Promise<OrganizationResponse>;
putOrganization: (id: string, request: OrganizationUpdateRequest) => Promise<OrganizationResponse>;
putOrganizationTaxInfo: (id: string, request: OrganizationTaxInfoUpdateRequest) => Promise<any>;
postLeaveOrganization: (id: string) => Promise<any>;
postOrganizationLicense: (data: FormData) => Promise<OrganizationResponse>;
postOrganizationLicenseUpdate: (id: string, data: FormData) => Promise<any>;
postOrganizationApiKey: (id: string, request: PasswordVerificationRequest) => Promise<ApiKeyResponse>;
postOrganizationRotateApiKey: (id: string, request: PasswordVerificationRequest) => Promise<ApiKeyResponse>;
postOrganizationApiKey: (id: string, request: SecretVerificationRequest) => Promise<ApiKeyResponse>;
postOrganizationRotateApiKey: (id: string, request: SecretVerificationRequest) => Promise<ApiKeyResponse>;
postOrganizationSso: (id: string, request: OrganizationSsoRequest) => Promise<OrganizationSsoResponse>;
postOrganizationUpgrade: (id: string, request: OrganizationUpgradeRequest) => Promise<PaymentResponse>;
postOrganizationUpdateSubscription: (id: string, request: OrganizationSubscriptionUpdateRequest) => Promise<void>;
postOrganizationSeat: (id: string, request: SeatRequest) => Promise<PaymentResponse>;
@@ -389,7 +401,7 @@ export abstract class ApiService {
postOrganizationVerifyBank: (id: string, request: VerifyBankRequest) => Promise<any>;
postOrganizationCancel: (id: string) => Promise<any>;
postOrganizationReinstate: (id: string) => Promise<any>;
deleteOrganization: (id: string, request: PasswordVerificationRequest) => Promise<any>;
deleteOrganization: (id: string, request: SecretVerificationRequest) => Promise<any>;
getPlans: () => Promise<ListResponse<PlanResponse>>;
getTaxRates: () => Promise<ListResponse<TaxRateResponse>>;
getOrganizationKeys: (id: string) => Promise<OrganizationKeysResponse>;
@@ -442,4 +454,15 @@ export abstract class ApiService {
nativeFetch: (request: Request) => Promise<Response>;
preValidateSso: (identifier: string) => Promise<boolean>;
postCreateSponsorship: (sponsorshipOrgId: string, request: OrganizationSponsorshipCreateRequest) => Promise<void>;
deleteRevokeSponsorship: (sponsoringOrganizationId: string) => Promise<void>;
deleteRemoveSponsorship: (sponsoringOrgId: string) => Promise<void>;
postPreValidateSponsorshipToken: (sponsorshipToken: string) => Promise<boolean>;
postRedeemSponsorship: (sponsorshipToken: string, request: OrganizationSponsorshipRedeemRequest) => Promise<void>;
postResendSponsorshipOffer: (sponsoringOrgId: string) => Promise<void>;
getUserKeyFromKeyConnector: (keyConnectorUrl: string) => Promise<KeyConnectorUserKeyResponse>;
postUserKeyToKeyConnector: (keyConnectorUrl: string, request: KeyConnectorUserKeyRequest) => Promise<void>;
getKeyConnectorAlive: (keyConnectorUrl: string) => Promise<void>;
}

View File

@@ -15,7 +15,7 @@ export abstract class AuthService {
selectedTwoFactorProviderType: TwoFactorProviderType;
logIn: (email: string, masterPassword: string, captchaToken?: string) => Promise<AuthResult>;
logInSso: (code: string, codeVerifier: string, redirectUrl: string) => Promise<AuthResult>;
logInSso: (code: string, codeVerifier: string, redirectUrl: string, orgId: string) => Promise<AuthResult>;
logInApiKey: (clientId: string, clientSecret: string) => Promise<AuthResult>;
logInTwoFactor: (twoFactorProvider: TwoFactorProviderType, twoFactorToken: string,
remember?: boolean) => Promise<AuthResult>;

View File

@@ -8,7 +8,7 @@ export type Urls = {
icons?: string;
notifications?: string;
events?: string;
enterprise?: string;
keyConnector?: string;
};
export type PayPalConfig = {
@@ -21,13 +21,13 @@ export abstract class EnvironmentService {
hasBaseUrl: () => boolean;
getNotificationsUrl: () => string;
getEnterpriseUrl: () => string;
getWebVaultUrl: () => string;
getSendUrl: () => string;
getIconsUrl: () => string;
getApiUrl: () => string;
getIdentityUrl: () => string;
getEventsUrl: () => string;
getKeyConnectorUrl: () => string;
setUrlsFromStorage: () => Promise<void>;
setUrls: (urls: any, saveSettings?: boolean) => Promise<Urls>;
getUrls: () => Urls;

View File

@@ -0,0 +1,14 @@
import { Organization } from '../models/domain/organization';
export abstract class KeyConnectorService {
getAndSetKey: (url?: string) => Promise<void>;
getManagingOrganization: () => Promise<Organization>;
getUsesKeyConnector: () => Promise<boolean>;
migrateUser: () => Promise<void>;
userNeedsMigration: () => Promise<boolean>;
setUsesKeyConnector: (enabled: boolean) => Promise<void>;
setConvertAccountRequired: (status: boolean) => Promise<void>;
getConvertAccountRequired: () => Promise<boolean>;
removeConvertAccountRequired: () => Promise<void>;
clear: () => Promise<void>;
}

View File

@@ -1,4 +1,5 @@
export abstract class PasswordRepromptService {
protectedFields: () => string[];
showPasswordPrompt: () => Promise<boolean>;
enabled: () => Promise<boolean>;
}

View File

@@ -1,4 +1,9 @@
import { DeviceType } from '../enums/deviceType';
import { ThemeType } from '../enums/themeType';
interface ToastOptions {
timeout?: number;
}
export abstract class PlatformUtilsService {
identityClientId: string;
@@ -19,7 +24,7 @@ export abstract class PlatformUtilsService {
supportsWebAuthn: (win: Window) => boolean;
supportsDuo: () => boolean;
showToast: (type: 'error' | 'success' | 'warning' | 'info', title: string, text: string | string[],
options?: any) => void;
options?: ToastOptions) => void;
showDialog: (body: string, title?: string, confirmText?: string, cancelText?: string,
type?: string, bodyIsHtml?: boolean) => Promise<boolean>;
isDev: () => boolean;
@@ -28,7 +33,8 @@ export abstract class PlatformUtilsService {
readFromClipboard: (options?: any) => Promise<string>;
supportsBiometric: () => Promise<boolean>;
authenticateBiometric: () => Promise<boolean>;
getDefaultSystemTheme: () => Promise<'light' | 'dark'>;
onDefaultSystemThemeChange: (callback: ((theme: 'light' | 'dark') => unknown)) => unknown;
getDefaultSystemTheme: () => Promise<ThemeType.Light | ThemeType.Dark>;
onDefaultSystemThemeChange: (callback: ((theme: ThemeType.Light | ThemeType.Dark) => unknown)) => unknown;
getEffectiveTheme: () => Promise<ThemeType>;
supportsSecureStorage: () => boolean;
}

View File

@@ -26,4 +26,5 @@ export abstract class TokenService {
getName: () => string;
getPremium: () => boolean;
getIssuer: () => string;
getIsExternal: () => boolean;
}

View File

@@ -1,5 +1,6 @@
import { OrganizationData } from '../models/data/organizationData';
import { ProviderData } from '../models/data/providerData';
import { Organization } from '../models/domain/organization';
import { Provider } from '../models/domain/provider';
@@ -20,6 +21,7 @@ export abstract class UserService {
clear: () => Promise<any>;
isAuthenticated: () => Promise<boolean>;
canAccessPremium: () => Promise<boolean>;
canManageSponsorships: () => Promise<boolean>;
getOrganization: (id: string) => Promise<Organization>;
getOrganizationByIdentifier: (identifier: string) => Promise<Organization>;
getAllOrganizations: () => Promise<Organization[]>;

View File

@@ -0,0 +1,10 @@
import { SecretVerificationRequest } from '../models/request/secretVerificationRequest';
import { Verification } from '../types/verification';
export abstract class UserVerificationService {
buildRequest: <T extends SecretVerificationRequest> (verification: Verification,
requestClass?: new () => T, alreadyHashed?: boolean) => Promise<T>;
verifyUser: (verification: Verification) => Promise<boolean>;
requestOTP: () => Promise<void>;
}

View File

@@ -8,6 +8,7 @@ export enum EventType {
User_FailedLogIn2fa = 1006,
User_ClientExportedVault = 1007,
User_UpdatedTempPassword = 1008,
User_MigratedKeyToKeyConnector = 1009,
Cipher_Created = 1100,
Cipher_Updated = 1101,
@@ -46,11 +47,16 @@ export enum EventType {
OrganizationUser_ResetPassword_Withdraw = 1507,
OrganizationUser_AdminResetPassword = 1508,
OrganizationUser_ResetSsoLink = 1509,
OrganizationUser_FirstSsoLogin = 1510,
Organization_Updated = 1600,
Organization_PurgedVault = 1601,
// Organization_ClientExportedVault = 1602,
Organization_VaultAccessed = 1603,
Organization_EnabledSso = 1604,
Organization_DisabledSso = 1605,
Organization_EnabledKeyConnector = 1606,
Organization_DisabledKeyConnector = 1607,
Policy_Updated = 1700,

View File

@@ -2,4 +2,5 @@ export enum FieldType {
Text = 0,
Hidden = 1,
Boolean = 2,
Linked = 3,
}

View File

@@ -0,0 +1,40 @@
export type LinkedIdType = LoginLinkedId | CardLinkedId | IdentityLinkedId;
// LoginView
export enum LoginLinkedId {
Username = 100,
Password = 101,
}
// CardView
export enum CardLinkedId {
CardholderName = 300,
ExpMonth = 301,
ExpYear = 302,
Code = 303,
Brand = 304,
Number = 305,
}
// IdentityView
export enum IdentityLinkedId {
Title = 400,
MiddleName = 401,
Address1 = 402,
Address2 = 403,
Address3 = 404,
City = 405,
State = 406,
PostalCode = 407,
Country = 408,
Company = 409,
Email = 410,
Phone = 411,
Ssn = 412,
Username = 413,
PassportNumber = 414,
LicenseNumber = 415,
FirstName = 416,
LastName = 417,
FullName = 418,
}

View File

@@ -1,14 +1,27 @@
export enum Permissions {
AccessBusinessPortal,
AccessEventLogs,
AccessImportExport,
AccessReports,
/**
* @deprecated Sep 29 2021: This permission has been split out to `createNewCollections`, `editAnyCollection`, and
* `deleteAnyCollection`. It exists here for backwards compatibility with Server versions <= 1.43.0
*/
ManageAllCollections,
/**
* @deprecated Sep 29 2021: This permission has been split out to `editAssignedCollections` and
* `deleteAssignedCollections`. It exists here for backwards compatibility with Server versions <= 1.43.0
*/
ManageAssignedCollections,
ManageGroups,
ManageOrganization,
ManageOrganization ,
ManagePolicies,
ManageProvider,
ManageUsers,
ManageUsersPassword,
CreateNewCollections,
EditAnyCollection,
DeleteAnyCollection,
EditAssignedCollections,
DeleteAssignedCollections,
ManageSso,
}

View File

@@ -0,0 +1,3 @@
export enum PlanSponsorshipType {
FamiliesForEnterprise = 0,
}

View File

@@ -0,0 +1,7 @@
export enum ThemeType {
System = 'system',
Light = 'light',
Dark = 'dark',
Nord = 'nord',
SolarizedDark = 'solarizedDark',
}

View File

@@ -0,0 +1,4 @@
export enum VerificationType {
MasterPassword = 0,
OTP = 1,
}

View File

@@ -88,7 +88,7 @@ export abstract class BaseImporter {
}
protected parseCsv(data: string, header: boolean, options: any = {}): any[] {
const parseOptions = Object.assign({ header: header }, this.parseCsvOptions, options);
const parseOptions: papa.ParseConfig<string> = Object.assign({ header: header }, this.parseCsvOptions, options);
data = this.splitNewLine(data).join('\n').trim();
const result = papa.parse(data, parseOptions);
if (result.errors != null && result.errors.length > 0) {

View File

@@ -21,7 +21,9 @@ export class PasspackCsvImporter extends BaseImporter implements Importer {
try {
const t = JSON.parse(tagJson);
return this.getValueOrDefault(t.tag);
} catch { }
} catch {
// Ignore error
}
return null;
}).filter((t: string) => !this.isNullOrWhitespace(t)) : null;
@@ -72,7 +74,9 @@ export class PasspackCsvImporter extends BaseImporter implements Importer {
fieldsJson.extraFields.length > 0 ? fieldsJson.extraFields.map((fieldJson: string) => {
try {
return JSON.parse(fieldJson);
} catch { }
} catch {
// Ignore error
}
return null;
}) : null;
if (fields != null) {

View File

@@ -84,7 +84,9 @@ export class PasswordBossJsonImporter extends BaseImporter implements Importer {
const expDate = new Date(val);
cipher.card.expYear = expDate.getFullYear().toString();
cipher.card.expMonth = (expDate.getMonth() + 1).toString();
} catch { }
} catch {
// Ignore error
}
continue;
} else if (property === 'cardType') {
continue;

View File

@@ -43,7 +43,9 @@ export class RememBearCsvImporter extends BaseImporter implements Importer {
cipher.card.expMonth = expMonthNumber.toString();
}
}
} catch { }
} catch {
// Ignore error
}
try {
const expYear = this.getValueOrDefault(value.expiryYear);
if (expYear != null) {
@@ -52,7 +54,9 @@ export class RememBearCsvImporter extends BaseImporter implements Importer {
cipher.card.expYear = expYearNumber.toString();
}
}
} catch { }
} catch {
// Ignore error
}
const pin = this.getValueOrDefault(value.pin);
if (pin != null) {

View File

@@ -0,0 +1,29 @@
import { BaseImporter } from './baseImporter';
import { Importer } from './importer';
import { ImportResult } from '../models/domain/importResult';
export class SafariCsvImporter extends BaseImporter implements Importer {
parse(data: string): Promise<ImportResult> {
const result = new ImportResult();
const results = this.parseCsv(data, true);
if (results == null) {
result.success = false;
return Promise.resolve(result);
}
results.forEach(value => {
const cipher = this.initLoginCipher();
cipher.name = this.getValueOrDefault(value.Title, '--');
cipher.login.username = this.getValueOrDefault(value.Username);
cipher.login.password = this.getValueOrDefault(value.Password);
cipher.login.uris = this.makeUriArray(value.Url);
cipher.login.totp = this.getValueOrDefault(value.OTPAuth);
this.cleanupCipher(cipher);
result.ciphers.push(cipher);
});
result.success = true;
return Promise.resolve(result);
}
}

View File

@@ -47,7 +47,9 @@ export class TrueKeyCsvImporter extends BaseImporter implements Importer {
const expDate = new Date(value.expiryDate);
cipher.card.expYear = expDate.getFullYear().toString();
cipher.card.expMonth = (expDate.getMonth() + 1).toString();
} catch { }
} catch {
// Ignore error
}
}
} else if (value.kind !== 'login') {
cipher.type = CipherType.SecureNote;

View File

@@ -0,0 +1,28 @@
import { ItemView } from '../models/view/itemView';
import { LinkedIdType } from '../enums/linkedIdType';
export class LinkedMetadata {
constructor(readonly propertyKey: string, private readonly _i18nKey?: string) { }
get i18nKey() {
return this._i18nKey ?? this.propertyKey;
}
}
/**
* A decorator used to set metadata used by Linked custom fields. Apply it to a class property or getter to make it
* available as a Linked custom field option.
* @param id - A unique value that is saved in the Field model. It is used to look up the decorated class property.
* @param i18nKey - The i18n key used to describe the decorated class property in the UI. If it is null, then the name
* of the class property will be used as the i18n key.
*/
export function linkedFieldOption(id: LinkedIdType, i18nKey?: string) {
return (prototype: ItemView, propertyKey: string) => {
if (prototype.linkedFieldOptions == null) {
prototype.linkedFieldOptions = new Map<LinkedIdType, LinkedMetadata>();
}
prototype.linkedFieldOptions.set(id, new LinkedMetadata(propertyKey, i18nKey));
};
}

View File

@@ -219,7 +219,8 @@ export class Utils {
}
let httpUrl = uriString.startsWith('http://') || uriString.startsWith('https://');
if (!httpUrl && uriString.indexOf('://') < 0 && Utils.tldEndingRegex.test(uriString)) {
if (!httpUrl && uriString.indexOf('://') < 0 && Utils.tldEndingRegex.test(uriString) &&
uriString.indexOf('@') < 0) {
uriString = 'http://' + uriString;
httpUrl = true;
}
@@ -238,7 +239,9 @@ export class Utils {
const urlDomain = tldjs != null && tldjs.getDomain != null ? tldjs.getDomain(url.hostname) : null;
return urlDomain != null ? urlDomain : url.hostname;
} catch (e) { }
} catch (e) {
// Invalid domain, try another approach below.
}
}
try {
@@ -325,6 +328,10 @@ export class Utils {
return url;
}
static camelToPascalCase(s: string) {
return s.charAt(0).toUpperCase() + s.slice(1);
}
private static validIpAddress(ipString: string): boolean {
// tslint:disable-next-line
const ipRegex = /^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/;
@@ -363,7 +370,9 @@ export class Utils {
anchor.href = uriString;
return anchor as any;
}
} catch (e) { }
} catch (e) {
// Ignore error
}
return null;
}

View File

@@ -1,11 +1,13 @@
import { BaseResponse } from '../response/baseResponse';
import { FieldType } from '../../enums/fieldType';
import { LinkedIdType } from '../../enums/linkedIdType';
export class FieldApi extends BaseResponse {
name: string;
value: string;
type: FieldType;
linkedId: LinkedIdType;
constructor(data: any = null) {
super(data);
@@ -15,5 +17,6 @@ export class FieldApi extends BaseResponse {
this.type = this.getResponseProperty('Type');
this.name = this.getResponseProperty('Name');
this.value = this.getResponseProperty('Value');
this.linkedId = this.getResponseProperty('linkedId');
}
}

View File

@@ -1,12 +1,24 @@
import { BaseResponse } from '../response/baseResponse';
export class PermissionsApi extends BaseResponse {
accessBusinessPortal: boolean;
accessEventLogs: boolean;
accessImportExport: boolean;
accessReports: boolean;
/**
* @deprecated Sep 29 2021: This permission has been split out to `createNewCollections`, `editAnyCollection`, and
* `deleteAnyCollection`. It exists here for backwards compatibility with Server versions <= 1.43.0
*/
manageAllCollections: boolean;
createNewCollections: boolean;
editAnyCollection: boolean;
deleteAnyCollection: boolean;
/**
* @deprecated Sep 29 2021: This permission has been split out to `editAssignedCollections` and
* `deleteAssignedCollections`. It exists here for backwards compatibility with Server versions <= 1.43.0
*/
manageAssignedCollections: boolean;
editAssignedCollections: boolean;
deleteAssignedCollections: boolean;
manageCiphers: boolean;
manageGroups: boolean;
manageSso: boolean;
@@ -19,12 +31,20 @@ export class PermissionsApi extends BaseResponse {
if (data == null) {
return this;
}
this.accessBusinessPortal = this.getResponseProperty('AccessBusinessPortal');
this.accessEventLogs = this.getResponseProperty('AccessEventLogs');
this.accessImportExport = this.getResponseProperty('AccessImportExport');
this.accessReports = this.getResponseProperty('AccessReports');
// For backwards compatibility with Server <= 1.43.0
this.manageAllCollections = this.getResponseProperty('ManageAllCollections');
this.manageAssignedCollections = this.getResponseProperty('ManageAssignedCollections');
this.createNewCollections = this.getResponseProperty('CreateNewCollections');
this.editAnyCollection = this.getResponseProperty('EditAnyCollection');
this.deleteAnyCollection = this.getResponseProperty('DeleteAnyCollection');
this.editAssignedCollections = this.getResponseProperty('EditAssignedCollections');
this.deleteAssignedCollections = this.getResponseProperty('DeleteAssignedCollections');
this.manageCiphers = this.getResponseProperty('ManageCiphers');
this.manageGroups = this.getResponseProperty('ManageGroups');
this.manageSso = this.getResponseProperty('ManageSso');

View File

@@ -0,0 +1,118 @@
import { BaseResponse } from '../response/baseResponse';
enum SsoType {
OpenIdConnect = 1,
Saml2 = 2,
}
enum OpenIdConnectRedirectBehavior {
RedirectGet = 0,
FormPost = 1,
}
enum Saml2BindingType {
HttpRedirect = 1,
HttpPost = 2,
Artifact = 4,
}
enum Saml2NameIdFormat {
NotConfigured = 0,
Unspecified = 1,
EmailAddress = 2,
X509SubjectName = 3,
WindowsDomainQualifiedName = 4,
KerberosPrincipalName = 5,
EntityIdentifier = 6,
Persistent = 7,
Transient = 8,
}
enum Saml2SigningBehavior {
IfIdpWantAuthnRequestsSigned = 0,
Always = 1,
Never = 3,
}
export class SsoConfigApi extends BaseResponse {
configType: SsoType;
keyConnectorEnabled: boolean;
keyConnectorUrl: string;
// OpenId
authority: string;
clientId: string;
clientSecret: string;
metadataAddress: string;
redirectBehavior: OpenIdConnectRedirectBehavior;
getClaimsFromUserInfoEndpoint: boolean;
additionalScopes: string;
additionalUserIdClaimTypes: string;
additionalEmailClaimTypes: string;
additionalNameClaimTypes: string;
acrValues: string;
expectedReturnAcrValue: string;
// SAML
spNameIdFormat: Saml2NameIdFormat;
spOutboundSigningAlgorithm: string;
spSigningBehavior: Saml2SigningBehavior;
spMinIncomingSigningAlgorithm: boolean;
spWantAssertionsSigned: boolean;
spValidateCertificates: boolean;
idpEntityId: string;
idpBindingType: Saml2BindingType;
idpSingleSignOnServiceUrl: string;
idpSingleLogoutServiceUrl: string;
idpArtifactResolutionServiceUrl: string;
idpX509PublicCert: string;
idpOutboundSigningAlgorithm: string;
idpAllowUnsolicitedAuthnResponse: boolean;
idpDisableOutboundLogoutRequests: boolean;
idpWantAuthnRequestsSigned: boolean;
constructor(data: any = null) {
super(data);
if (data == null) {
return;
}
this.configType = this.getResponseProperty('ConfigType');
this.keyConnectorEnabled = this.getResponseProperty('KeyConnectorEnabled');
this.keyConnectorUrl = this.getResponseProperty('KeyConnectorUrl');
this.authority = this.getResponseProperty('Authority');
this.clientId = this.getResponseProperty('ClientId');
this.clientSecret = this.getResponseProperty('ClientSecret');
this.metadataAddress = this.getResponseProperty('MetadataAddress');
this.redirectBehavior = this.getResponseProperty('RedirectBehavior');
this.getClaimsFromUserInfoEndpoint = this.getResponseProperty('GetClaimsFromUserInfoEndpoint');
this.additionalScopes = this.getResponseProperty('AdditionalScopes');
this.additionalUserIdClaimTypes = this.getResponseProperty('AdditionalUserIdClaimTypes');
this.additionalEmailClaimTypes = this.getResponseProperty('AdditionalEmailClaimTypes');
this.additionalNameClaimTypes = this.getResponseProperty('AdditionalNameClaimTypes');
this.acrValues = this.getResponseProperty('AcrValues');
this.expectedReturnAcrValue = this.getResponseProperty('ExpectedReturnAcrValue');
this.spNameIdFormat = this.getResponseProperty('SpNameIdFormat');
this.spOutboundSigningAlgorithm = this.getResponseProperty('SpOutboundSigningAlgorithm');
this.spSigningBehavior = this.getResponseProperty('SpSigningBehavior');
this.spMinIncomingSigningAlgorithm = this.getResponseProperty('SpMinIncomingSigningAlgorithm');
this.spWantAssertionsSigned = this.getResponseProperty('SpWantAssertionsSigned');
this.spValidateCertificates = this.getResponseProperty('SpValidateCertificates');
this.idpEntityId = this.getResponseProperty('IdpEntityId');
this.idpBindingType = this.getResponseProperty('IdpBindingType');
this.idpSingleSignOnServiceUrl = this.getResponseProperty('IdpSingleSignOnServiceUrl');
this.idpSingleLogoutServiceUrl = this.getResponseProperty('IdpSingleLogoutServiceUrl');
this.idpArtifactResolutionServiceUrl = this.getResponseProperty('IdpArtifactResolutionServiceUrl');
this.idpX509PublicCert = this.getResponseProperty('IdpX509PublicCert');
this.idpOutboundSigningAlgorithm = this.getResponseProperty('IdpOutboundSigningAlgorithm');
this.idpAllowUnsolicitedAuthnResponse = this.getResponseProperty('IdpAllowUnsolicitedAuthnResponse');
this.idpDisableOutboundLogoutRequests = this.getResponseProperty('IdpDisableOutboundLogoutRequests');
this.idpWantAuthnRequestsSigned = this.getResponseProperty('IdpWantAuthnRequestsSigned');
}
}

View File

@@ -1,4 +1,5 @@
import { FieldType } from '../../enums/fieldType';
import { LinkedIdType } from '../../enums/linkedIdType';
import { FieldApi } from '../api/fieldApi';
@@ -6,6 +7,7 @@ export class FieldData {
type: FieldType;
name: string;
value: string;
linkedId: LinkedIdType;
constructor(response?: FieldApi) {
if (response == null) {
@@ -14,5 +16,6 @@ export class FieldData {
this.type = response.type;
this.name = response.name;
this.value = response.value;
this.linkedId = response.linkedId;
}
}

View File

@@ -2,6 +2,8 @@ import { ProfileOrganizationResponse } from '../response/profileOrganizationResp
import { OrganizationUserStatusType } from '../../enums/organizationUserStatusType';
import { OrganizationUserType } from '../../enums/organizationUserType';
import { ProductType } from '../../enums/productType';
import { PermissionsApi } from '../api/permissionsApi';
export class OrganizationData {
@@ -17,8 +19,8 @@ export class OrganizationData {
useTotp: boolean;
use2fa: boolean;
useApi: boolean;
useBusinessPortal: boolean;
useSso: boolean;
useKeyConnector: boolean;
useResetPassword: boolean;
selfHost: boolean;
usersGetPremium: boolean;
@@ -34,6 +36,11 @@ export class OrganizationData {
providerId: string;
providerName: string;
isProviderUser: boolean;
familySponsorshipFriendlyName: string;
familySponsorshipAvailable: boolean;
planProductType: ProductType;
keyConnectorEnabled: boolean;
keyConnectorUrl: string;
constructor(response: ProfileOrganizationResponse) {
this.id = response.id;
@@ -48,8 +55,8 @@ export class OrganizationData {
this.useTotp = response.useTotp;
this.use2fa = response.use2fa;
this.useApi = response.useApi;
this.useBusinessPortal = response.useBusinessPortal;
this.useSso = response.useSso;
this.useKeyConnector = response.useKeyConnector;
this.useResetPassword = response.useResetPassword;
this.selfHost = response.selfHost;
this.usersGetPremium = response.usersGetPremium;
@@ -64,5 +71,10 @@ export class OrganizationData {
this.hasPublicAndPrivateKeys = response.hasPublicAndPrivateKeys;
this.providerId = response.providerId;
this.providerName = response.providerName;
this.familySponsorshipFriendlyName = response.familySponsorshipFriendlyName;
this.familySponsorshipAvailable = response.familySponsorshipAvailable;
this.planProductType = response.planProductType;
this.keyConnectorEnabled = response.keyConnectorEnabled;
this.keyConnectorUrl = response.keyConnectorUrl;
}
}

View File

@@ -1,4 +1,5 @@
import { FieldType } from '../../enums/fieldType';
import { LinkedIdType } from '../../enums/linkedIdType';
import { FieldData } from '../data/fieldData';
@@ -12,6 +13,7 @@ export class Field extends Domain {
name: EncString;
value: EncString;
type: FieldType;
linkedId: LinkedIdType;
constructor(obj?: FieldData, alreadyEncrypted: boolean = false) {
super();
@@ -20,6 +22,7 @@ export class Field extends Domain {
}
this.type = obj.type;
this.linkedId = obj.linkedId;
this.buildDomainModel(this, obj, {
name: null,
value: null,
@@ -39,7 +42,8 @@ export class Field extends Domain {
name: null,
value: null,
type: null,
}, ['type']);
linkedId: null,
}, ['type', 'linkedId']);
return f;
}
}

View File

@@ -2,6 +2,7 @@ import { OrganizationData } from '../data/organizationData';
import { OrganizationUserStatusType } from '../../enums/organizationUserStatusType';
import { OrganizationUserType } from '../../enums/organizationUserType';
import { ProductType } from '../../enums/productType';
import { PermissionsApi } from '../api/permissionsApi';
@@ -18,8 +19,8 @@ export class Organization {
useTotp: boolean;
use2fa: boolean;
useApi: boolean;
useBusinessPortal: boolean;
useSso: boolean;
useKeyConnector: boolean;
useResetPassword: boolean;
selfHost: boolean;
usersGetPremium: boolean;
@@ -35,6 +36,11 @@ export class Organization {
providerId: string;
providerName: string;
isProviderUser: boolean;
familySponsorshipFriendlyName: string;
familySponsorshipAvailable: boolean;
planProductType: ProductType;
keyConnectorEnabled: boolean;
keyConnectorUrl: string;
constructor(obj?: OrganizationData) {
if (obj == null) {
@@ -53,8 +59,8 @@ export class Organization {
this.useTotp = obj.useTotp;
this.use2fa = obj.use2fa;
this.useApi = obj.useApi;
this.useBusinessPortal = obj.useBusinessPortal;
this.useSso = obj.useSso;
this.useKeyConnector = obj.useKeyConnector;
this.useResetPassword = obj.useResetPassword;
this.selfHost = obj.selfHost;
this.usersGetPremium = obj.usersGetPremium;
@@ -70,6 +76,11 @@ export class Organization {
this.providerId = obj.providerId;
this.providerName = obj.providerName;
this.isProviderUser = obj.isProviderUser;
this.familySponsorshipFriendlyName = obj.familySponsorshipFriendlyName;
this.familySponsorshipAvailable = obj.familySponsorshipAvailable;
this.planProductType = obj.planProductType;
this.keyConnectorEnabled = obj.keyConnectorEnabled;
this.keyConnectorUrl = obj.keyConnectorUrl;
}
get canAccess() {
@@ -92,10 +103,6 @@ export class Organization {
return this.type === OrganizationUserType.Owner || this.isProviderUser;
}
get canAccessBusinessPortal() {
return this.isAdmin || this.permissions.accessBusinessPortal;
}
get canAccessEventLogs() {
return this.isAdmin || this.permissions.accessEventLogs;
}
@@ -108,12 +115,32 @@ export class Organization {
return this.isAdmin || this.permissions.accessReports;
}
get canManageAllCollections() {
return this.isAdmin || this.permissions.manageAllCollections;
get canCreateNewCollections() {
return this.isManager || (this.permissions.createNewCollections ?? this.permissions.manageAllCollections);
}
get canManageAssignedCollections() {
return this.isManager || this.permissions.manageAssignedCollections;
get canEditAnyCollection() {
return this.isAdmin || (this.permissions.editAnyCollection ?? this.permissions.manageAllCollections);
}
get canDeleteAnyCollection() {
return this.isAdmin || (this.permissions.deleteAnyCollection ?? this.permissions.manageAllCollections);
}
get canViewAllCollections() {
return this.canCreateNewCollections || this.canEditAnyCollection || this.canDeleteAnyCollection;
}
get canEditAssignedCollections() {
return this.isManager || (this.permissions.editAssignedCollections ?? this.permissions.manageAssignedCollections);
}
get canDeleteAssignedCollections() {
return this.isManager || (this.permissions.deleteAssignedCollections ?? this.permissions.manageAssignedCollections);
}
get canViewAssignedCollections() {
return this.canDeleteAssignedCollections || this.canEditAssignedCollections;
}
get canManageGroups() {

View File

@@ -1,4 +1,5 @@
import { FieldType } from '../../enums/fieldType';
import { LinkedIdType } from '../../enums/linkedIdType';
import { FieldView } from '../view/fieldView';
@@ -18,6 +19,7 @@ export class Field {
view.type = req.type;
view.value = req.value;
view.name = req.name;
view.linkedId = req.linkedId;
return view;
}
@@ -25,12 +27,14 @@ export class Field {
domain.type = req.type;
domain.value = req.value != null ? new EncString(req.value) : null;
domain.name = req.name != null ? new EncString(req.name) : null;
domain.linkedId = req.linkedId;
return domain;
}
name: string;
value: string;
type: FieldType;
linkedId: LinkedIdType;
constructor(o?: FieldView | FieldDomain) {
if (o == null) {
@@ -45,5 +49,6 @@ export class Field {
this.value = o.value?.encryptedString;
}
this.type = o.type;
this.linkedId = o.linkedId;
}
}

View File

@@ -0,0 +1,19 @@
import { KeysRequest } from '../keysRequest';
import { KdfType } from '../../../enums/kdfType';
export class SetKeyConnectorKeyRequest {
key: string;
keys: KeysRequest;
kdf: KdfType;
kdfIterations: number;
orgIdentifier: string;
constructor(key: string, kdf: KdfType, kdfIterations: number, orgIdentifier: string, keys: KeysRequest) {
this.key = key;
this.kdf = kdf;
this.kdfIterations = kdfIterations;
this.orgIdentifier = orgIdentifier;
this.keys = keys;
}
}

View File

@@ -0,0 +1,7 @@
export class VerifyOTPRequest {
OTP: string;
constructor(OTP: string) {
this.OTP = OTP;
}
}

View File

@@ -119,6 +119,7 @@ export class CipherRequest {
field.type = f.type;
field.name = f.name ? f.name.encryptedString : null;
field.value = f.value ? f.value.encryptedString : null;
field.linkedId = f.linkedId;
return field;
});
}

View File

@@ -1,6 +1,6 @@
import { PasswordVerificationRequest } from './passwordVerificationRequest';
import { SecretVerificationRequest } from './secretVerificationRequest';
export class EmailTokenRequest extends PasswordVerificationRequest {
export class EmailTokenRequest extends SecretVerificationRequest {
newEmail: string;
masterPasswordHash: string;
}

View File

@@ -0,0 +1,7 @@
export class KeyConnectorUserKeyRequest {
key: string;
constructor(key: string) {
this.key = key;
}
}

View File

@@ -0,0 +1,7 @@
import { PlanSponsorshipType } from '../../../enums/planSponsorshipType';
export class OrganizationSponsorshipCreateRequest {
sponsoredEmail: string;
planSponsorshipType: PlanSponsorshipType;
friendlyName: string;
}

View File

@@ -0,0 +1,6 @@
import { PlanSponsorshipType } from '../../../enums/planSponsorshipType';
export class OrganizationSponsorshipRedeemRequest {
planSponsorshipType: PlanSponsorshipType;
sponsoredOrganizationId: string;
}

View File

@@ -0,0 +1,6 @@
import { SsoConfigApi } from '../../api/ssoConfigApi';
export class OrganizationSsoRequest {
enabled: boolean = false;
data: SsoConfigApi;
}

View File

@@ -1,6 +1,6 @@
import { PasswordVerificationRequest } from './passwordVerificationRequest';
import { SecretVerificationRequest } from './secretVerificationRequest';
export class PasswordRequest extends PasswordVerificationRequest {
export class PasswordRequest extends SecretVerificationRequest {
newMasterPasswordHash: string;
key: string;
}

View File

@@ -1,3 +0,0 @@
export class PasswordVerificationRequest {
masterPasswordHash: string;
}

View File

@@ -0,0 +1,4 @@
export class SecretVerificationRequest {
masterPasswordHash: string;
otp: string;
}

View File

@@ -1,11 +1,5 @@
import { PasswordVerificationRequest } from './passwordVerificationRequest';
import { SecretVerificationRequest } from './secretVerificationRequest';
export class TwoFactorEmailRequest extends PasswordVerificationRequest {
export class TwoFactorEmailRequest extends SecretVerificationRequest {
email: string;
constructor(email: string, masterPasswordHash: string) {
super();
this.masterPasswordHash = masterPasswordHash;
this.email = email;
}
}

View File

@@ -1,7 +1,7 @@
import { PasswordVerificationRequest } from './passwordVerificationRequest';
import { SecretVerificationRequest } from './secretVerificationRequest';
import { TwoFactorProviderType } from '../../enums/twoFactorProviderType';
export class TwoFactorProviderRequest extends PasswordVerificationRequest {
export class TwoFactorProviderRequest extends SecretVerificationRequest {
type: TwoFactorProviderType;
}

View File

@@ -1,6 +1,6 @@
import { PasswordVerificationRequest } from './passwordVerificationRequest';
import { SecretVerificationRequest } from './secretVerificationRequest';
export class TwoFactorRecoveryRequest extends PasswordVerificationRequest {
export class TwoFactorRecoveryRequest extends SecretVerificationRequest {
recoveryCode: string;
email: string;
}

View File

@@ -1,6 +1,6 @@
import { PasswordVerificationRequest } from './passwordVerificationRequest';
import { SecretVerificationRequest } from './secretVerificationRequest';
export class UpdateTwoFactorAuthenticatorRequest extends PasswordVerificationRequest {
export class UpdateTwoFactorAuthenticatorRequest extends SecretVerificationRequest {
token: string;
key: string;
}

View File

@@ -1,6 +1,6 @@
import { PasswordVerificationRequest } from './passwordVerificationRequest';
import { SecretVerificationRequest } from './secretVerificationRequest';
export class UpdateTwoFactorDuoRequest extends PasswordVerificationRequest {
export class UpdateTwoFactorDuoRequest extends SecretVerificationRequest {
integrationKey: string;
secretKey: string;
host: string;

View File

@@ -1,6 +1,6 @@
import { PasswordVerificationRequest } from './passwordVerificationRequest';
import { SecretVerificationRequest } from './secretVerificationRequest';
export class UpdateTwoFactorEmailRequest extends PasswordVerificationRequest {
export class UpdateTwoFactorEmailRequest extends SecretVerificationRequest {
token: string;
email: string;
}

View File

@@ -1,5 +1,5 @@
import { PasswordVerificationRequest } from './passwordVerificationRequest';
import { SecretVerificationRequest } from './secretVerificationRequest';
export class UpdateTwoFactorWebAuthnDeleteRequest extends PasswordVerificationRequest {
export class UpdateTwoFactorWebAuthnDeleteRequest extends SecretVerificationRequest {
id: number;
}

View File

@@ -1,6 +1,6 @@
import { PasswordVerificationRequest } from './passwordVerificationRequest';
import { SecretVerificationRequest } from './secretVerificationRequest';
export class UpdateTwoFactorWebAuthnRequest extends PasswordVerificationRequest {
export class UpdateTwoFactorWebAuthnRequest extends SecretVerificationRequest {
deviceResponse: PublicKeyCredential;
name: string;
id: number;

View File

@@ -1,6 +1,6 @@
import { PasswordVerificationRequest } from './passwordVerificationRequest';
import { SecretVerificationRequest } from './secretVerificationRequest';
export class UpdateTwoFactorYubioOtpRequest extends PasswordVerificationRequest {
export class UpdateTwoFactorYubioOtpRequest extends SecretVerificationRequest {
key1: string;
key2: string;
key3: string;

View File

@@ -15,6 +15,8 @@ export class IdentityTokenResponse extends BaseResponse {
kdf: KdfType;
kdfIterations: number;
forcePasswordReset: boolean;
apiUseKeyConnector: boolean;
keyConnectorUrl: string;
constructor(response: any) {
super(response);
@@ -30,5 +32,7 @@ export class IdentityTokenResponse extends BaseResponse {
this.kdf = this.getResponseProperty('Kdf');
this.kdfIterations = this.getResponseProperty('KdfIterations');
this.forcePasswordReset = this.getResponseProperty('ForcePasswordReset');
this.apiUseKeyConnector = this.getResponseProperty('ApiUseKeyConnector');
this.keyConnectorUrl = this.getResponseProperty('KeyConnectorUrl');
}
}

View File

@@ -0,0 +1,10 @@
import { BaseResponse } from './baseResponse';
export class KeyConnectorUserKeyResponse extends BaseResponse {
key: string;
constructor(response: any) {
super(response);
this.key = this.getResponseProperty('Key');
}
}

View File

@@ -0,0 +1,32 @@
import { SsoConfigApi } from '../../api/ssoConfigApi';
import { BaseResponse } from '../baseResponse';
export class OrganizationSsoResponse extends BaseResponse {
enabled: boolean;
data: SsoConfigApi;
urls: SsoUrls;
constructor(response: any) {
super(response);
this.enabled = this.getResponseProperty('Enabled');
this.data = new SsoConfigApi(this.getResponseProperty('Data'));
this.urls = new SsoUrls(this.getResponseProperty('Urls'));
}
}
class SsoUrls extends BaseResponse {
callbackPath: string;
signedOutCallbackPath: string;
spEntityId: string;
spMetadataUrl: string;
spAcsUrl: string;
constructor(response: any) {
super(response);
this.callbackPath = this.getResponseProperty('CallbackPath');
this.signedOutCallbackPath = this.getResponseProperty('SignedOutCallbackPath');
this.spEntityId = this.getResponseProperty('SpEntityId');
this.spMetadataUrl = this.getResponseProperty('SpMetadataUrl');
this.spAcsUrl = this.getResponseProperty('SpAcsUrl');
}
}

View File

@@ -32,12 +32,14 @@ export class OrganizationUserUserDetailsResponse extends OrganizationUserRespons
name: string;
email: string;
twoFactorEnabled: boolean;
usesKeyConnector: boolean;
constructor(response: any) {
super(response);
this.name = this.getResponseProperty('Name');
this.email = this.getResponseProperty('Email');
this.twoFactorEnabled = this.getResponseProperty('TwoFactorEnabled');
this.usesKeyConnector = this.getResponseProperty('UsesKeyConnector') ?? false;
}
}

View File

@@ -2,6 +2,7 @@ import { BaseResponse } from './baseResponse';
import { OrganizationUserStatusType } from '../../enums/organizationUserStatusType';
import { OrganizationUserType } from '../../enums/organizationUserType';
import { ProductType } from '../../enums/productType';
import { PermissionsApi } from '../api/permissionsApi';
export class ProfileOrganizationResponse extends BaseResponse {
@@ -14,8 +15,8 @@ export class ProfileOrganizationResponse extends BaseResponse {
useTotp: boolean;
use2fa: boolean;
useApi: boolean;
useBusinessPortal: boolean;
useSso: boolean;
useKeyConnector: boolean;
useResetPassword: boolean;
selfHost: boolean;
usersGetPremium: boolean;
@@ -34,6 +35,11 @@ export class ProfileOrganizationResponse extends BaseResponse {
userId: string;
providerId: string;
providerName: string;
familySponsorshipFriendlyName: string;
familySponsorshipAvailable: boolean;
planProductType: ProductType;
keyConnectorEnabled: boolean;
keyConnectorUrl: string;
constructor(response: any) {
super(response);
@@ -46,8 +52,8 @@ export class ProfileOrganizationResponse extends BaseResponse {
this.useTotp = this.getResponseProperty('UseTotp');
this.use2fa = this.getResponseProperty('Use2fa');
this.useApi = this.getResponseProperty('UseApi');
this.useBusinessPortal = this.getResponseProperty('UseBusinessPortal');
this.useSso = this.getResponseProperty('UseSso');
this.useKeyConnector = this.getResponseProperty('UseKeyConnector') ?? false;
this.useResetPassword = this.getResponseProperty('UseResetPassword');
this.selfHost = this.getResponseProperty('SelfHost');
this.usersGetPremium = this.getResponseProperty('UsersGetPremium');
@@ -66,5 +72,10 @@ export class ProfileOrganizationResponse extends BaseResponse {
this.userId = this.getResponseProperty('UserId');
this.providerId = this.getResponseProperty('ProviderId');
this.providerName = this.getResponseProperty('ProviderName');
this.familySponsorshipFriendlyName = this.getResponseProperty('FamilySponsorshipFriendlyName');
this.familySponsorshipAvailable = this.getResponseProperty('FamilySponsorshipAvailable');
this.planProductType = this.getResponseProperty('PlanProductType');
this.keyConnectorEnabled = this.getResponseProperty('KeyConnectorEnabled') ?? false;
this.keyConnectorUrl = this.getResponseProperty('KeyConnectorUrl');
}
}

Some files were not shown because too many files have changed in this diff Show More