Compare commits
214 Commits
Author | SHA1 | Date |
---|---|---|
Oscar Hinton | d7674f5c91 | |
Jake Fink | 9852f2ec22 | |
Micaiah Martin | fec6838f90 | |
Patrick H. Lauke | 55a9403ee3 | |
Oscar Hinton | 817856bc82 | |
Micaiah Martin | 508292ae39 | |
Micaiah Martin | 00fd2ec03f | |
Oscar Hinton | 9a954710d9 | |
Oscar Hinton | a81c3c95a4 | |
Kyle Spearrin | 75470dc169 | |
Oscar Hinton | 18b5e4adfd | |
Patrick H. Lauke | 0396d682b1 | |
Thomas Rittson | ef60112855 | |
Thomas Rittson | b467206448 | |
Nils Fahldieck | 55b301c267 | |
github-actions[bot] | a5ebb9fb52 | |
github-actions[bot] | 2764c9610b | |
Oscar Hinton | 241e08b7ff | |
Oscar Hinton | 865e92c94c | |
Patrick H. Lauke | c3d0a529fb | |
Oscar Hinton | 1315b3c6cd | |
Dave Nicolson | 9f77dd9d09 | |
Thomas Rittson | 5082c7708a | |
dwbit | 10d35d863b | |
Kyle Spearrin | caafbc2b73 | |
Thomas Rittson | a4ca9bf64c | |
Joseph Flinn | 3862a19571 | |
Oscar Hinton | 8be88a731c | |
Oscar Hinton | 2b0d7ac72c | |
Matt Gibson | e0d7d2b43a | |
Oscar Hinton | 70db11e659 | |
Oscar Hinton | 2e3c89269d | |
github-actions[bot] | 31523bdf0e | |
Oscar Hinton | 6b6666cd0d | |
Oscar Hinton | be1ab221f4 | |
Oscar Hinton | 78986023e7 | |
Addison Beck | 14b9decf21 | |
github-actions[bot] | 0e9465601a | |
Oscar Hinton | 52bb77fb66 | |
Kyle Spearrin | b4cec5b46f | |
Robyn MacCallum | 72405ebe87 | |
Patrick H. Lauke | 4379274154 | |
Kyle Spearrin | 9e0cc45704 | |
Francesco De Feo | bc21703a2b | |
Patrick H. Lauke | 4ddeff2eee | |
Patrick H. Lauke | 75af5b94d4 | |
Oscar Hinton | cbadcccc85 | |
Kyle Spearrin | 21167301f1 | |
github-actions[bot] | ad6308eb48 | |
Oscar Hinton | fec023866e | |
Jake Fink | d2a7012d3f | |
Jake Fink | 6555312034 | |
github-actions[bot] | e2fe0c8b09 | |
Oscar Hinton | 3b2427e913 | |
Oscar Hinton | 37e9523f00 | |
github-actions[bot] | 09d7376f39 | |
Thomas Rittson | be0b6113e4 | |
github-actions[bot] | 75f11cdbf1 | |
github-actions[bot] | e5feda0fa6 | |
Micaiah Martin | 5ebed777c2 | |
Vince Grassia | e49d317d19 | |
Chad Scharf | 114d7455b8 | |
Vince Grassia | d1146acb1e | |
Micaiah Martin | 703d390566 | |
Thomas Rittson | c0170563f2 | |
Thomas Rittson | f46405a508 | |
Micaiah Martin | 6d429465fb | |
Matt Gibson | 81457f6c05 | |
github-actions[bot] | aee27f9570 | |
Joseph Flinn | f845bcf6c1 | |
Micaiah Martin | b134eba27b | |
Vincent Salucci | e84be59075 | |
github-actions[bot] | edc5245173 | |
Addison Beck | 7bad97dd82 | |
Oscar Hinton | 3071bec03f | |
Thomas Rittson | 80415f8cd5 | |
Daniel James Smith | 03c279865f | |
Addison Beck | 54025f269a | |
Thomas Rittson | 4ce5e5fbdc | |
Addison Beck | c738366eef | |
Micaiah Martin | 0114d96e18 | |
Micaiah Martin | 1eac8f0c0f | |
Daniel James Smith | 8880e9700e | |
github-actions[bot] | 2f289ebd1f | |
Oscar Hinton | 3aa52a5537 | |
Robyn MacCallum | 233f876bbb | |
Thomas Rittson | d7276850a2 | |
Jake Fink | e570551a5a | |
Vincent Salucci | 7401204b70 | |
github-actions[bot] | 685ffbcac5 | |
Chad Scharf | f9e6d13708 | |
Micaiah Martin | f798995764 | |
github-actions[bot] | f6298b2684 | |
Thomas Rittson | 3881223a73 | |
Matt Gibson | d2df8dacad | |
Addison Beck | 10ffdce5b9 | |
github-actions[bot] | d6d74e178c | |
Addison Beck | 2e09265d3a | |
Thomas Rittson | 659c9ea78d | |
Addison Beck | 884615c23a | |
Oscar Hinton | 2470d8ce25 | |
github-actions[bot] | 6b6468e061 | |
github-actions[bot] | 45b144361c | |
Addison Beck | 881bb3cb49 | |
Addison Beck | fd4c41b043 | |
Oscar Hinton | 7a8a78b4df | |
Oscar Hinton | f82e2fbb03 | |
Matt Gibson | e9a3c586f8 | |
Thomas Rittson | f54a614d6b | |
Thomas Rittson | 95b5d68566 | |
Thomas Rittson | 3ffb658db6 | |
github-actions[bot] | 530f25c88a | |
Joseph Flinn | 97d367dab8 | |
Micaiah Martin | 0a545c88b2 | |
github-actions[bot] | 0f6ee08dd5 | |
Addison Beck | b227ae13f6 | |
Addison Beck | 79f6a33596 | |
Addison Beck | 1e80c4335f | |
Vincent Salucci | c51b8523b7 | |
Joseph Flinn | bd2ed43498 | |
Oscar Hinton | 243afc9da0 | |
Thomas Rittson | 762e3f8198 | |
Thomas Rittson | 4e75a25492 | |
Addison Beck | 143a262743 | |
Addison Beck | c1a3178538 | |
Oscar Hinton | 166c459da4 | |
Daniel James Smith | 94b561382d | |
Daniel James Smith | 2b58861296 | |
Vincent Salucci | 3e8705d548 | |
Oscar Hinton | 1e877f6cf8 | |
Joseph Flinn | 9151fc0164 | |
Addison Beck | 03eed41d86 | |
Addison Beck | cad6e9481f | |
Daniel James Smith | 270411dff7 | |
Vincent Salucci | 7c68e3802e | |
Addison Beck | 48ff9f61ae | |
Addison Beck | 2b22a39d45 | |
Addison Beck | c1ba54f646 | |
Daniel James Smith | bb597e96a7 | |
github-actions[bot] | 6aa9e7611a | |
Oscar Hinton | 3c99920435 | |
Oscar Hinton | a7c5f1ad45 | |
Vincent Salucci | ec3c95d736 | |
Addison Beck | 058be7e895 | |
Addison Beck | ff7dd4ad8f | |
Addison Beck | 9271ec37b9 | |
Addison Beck | d80fdc8a78 | |
Addison Beck | 555ee1c230 | |
Addison Beck | c458b4d8a9 | |
Oscar Hinton | 7d46e5c145 | |
Addison Beck | ca41cdf8b5 | |
Addison Beck | d211b3fcd2 | |
Addison Beck | ef48ba1ae2 | |
Robyn MacCallum | 33704b016f | |
Oscar Hinton | a64273f829 | |
Daniel James Smith | cb4e6debf3 | |
github-actions[bot] | 2e7f8a127d | |
Thomas Rittson | c6eaf3a31e | |
Daniel James Smith | 37b03b09a1 | |
Oscar Hinton | d1c01a2bb0 | |
Oscar Hinton | 0edee78da1 | |
Addison Beck | 80c9196e44 | |
Addison Beck | 032d2be990 | |
Daniel James Smith | a684c102d7 | |
Daniel James Smith | 12bb8b060c | |
Addison Beck | b796fe094f | |
Daniel James Smith | 8b521aa35e | |
Oscar Hinton | 3cacf8a8c4 | |
Vince Grassia | dad1a95fb0 | |
github-actions[bot] | 5b6e5fc4fa | |
Oscar Hinton | 4644d03b28 | |
Daniel James Smith | abede3e5af | |
Daniel James Smith | 7818ffc2fb | |
Oscar Hinton | b6117d6801 | |
Addison Beck | 653ff8f45f | |
Daniel James Smith | 71c2fee574 | |
Daniel James Smith | d885e3296b | |
Addison Beck | f32b917a9f | |
Daniel James Smith | 2b64ec5375 | |
Oscar Hinton | f6f0bd2bfb | |
Oscar Hinton | 23a7072341 | |
Daniel James Smith | d64b00977c | |
github-actions[bot] | 695e8389d8 | |
Daniel James Smith | dd73a45f64 | |
Daniel James Smith | a1bbbcf4bf | |
Oscar Hinton | 27b1ee2ab3 | |
github-actions[bot] | f01856d34f | |
Robyn MacCallum | ef5e8e1e2a | |
Addison Beck | ed78a79042 | |
github-actions[bot] | 017a3bffd6 | |
Micaiah Martin | ad965e54e4 | |
Oscar Hinton | 6e69b61128 | |
Daniel James Smith | 406f4bf62c | |
Linus Aarnio | 83c3635932 | |
Micaiah Martin | 3c2094bb04 | |
Oscar Hinton | f7ae94199f | |
Oscar Hinton | 521feae535 | |
Oscar Hinton | b4df834b16 | |
Vince Grassia | b33d37e610 | |
Daniel James Smith | 3ad71103dd | |
github-actions[bot] | da296559c0 | |
Addison Beck | 0b306ca1a7 | |
Daniel James Smith | 5865f08b37 | |
github-actions[bot] | 74c9c1972f | |
Oscar Hinton | a10259ecab | |
github-actions[bot] | 9feb147654 | |
Vince Grassia | 822c791cf9 | |
Oscar Hinton | b70d2fb3c3 | |
Oscar Hinton | 5fd1da0c58 | |
github-actions[bot] | 2822d748f1 | |
Oscar Hinton | 04cfdb246d | |
Micaiah Martin | 00d57d0310 | |
Thomas Rittson | b6db41c26c | |
Thomas Rittson | b83058ecab |
|
@ -12,4 +12,4 @@ insert_final_newline = true
|
|||
[*.{js,ts,scss,html}]
|
||||
charset = utf-8
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
indent_size = 2
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
dist
|
||||
build
|
||||
jslib
|
||||
webpack.main.js
|
||||
webpack.renderer.js
|
||||
src/scripts/duo.js
|
||||
desktop_native
|
||||
|
||||
**/node_modules
|
|
@ -0,0 +1,32 @@
|
|||
{
|
||||
"root": true,
|
||||
"env": {
|
||||
"browser": true,
|
||||
"node": true
|
||||
},
|
||||
"extends": ["./jslib/shared/eslintrc.json"],
|
||||
"rules": {
|
||||
"import/order": [
|
||||
"error",
|
||||
{
|
||||
"alphabetize": {
|
||||
"order": "asc"
|
||||
},
|
||||
"newlines-between": "always",
|
||||
"pathGroups": [
|
||||
{
|
||||
"pattern": "jslib-*/**",
|
||||
"group": "external",
|
||||
"position": "after"
|
||||
},
|
||||
{
|
||||
"pattern": "src/**/*",
|
||||
"group": "parent",
|
||||
"position": "before"
|
||||
}
|
||||
],
|
||||
"pathGroupsExcludedImportTypes": ["builtin"]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
|
@ -0,0 +1,2 @@
|
|||
# Apply Prettier https://github.com/bitwarden/desktop/pull/1202
|
||||
521feae535d83166e620c3c28dfc3e7b0314a00e
|
|
@ -0,0 +1 @@
|
|||
* text=auto eol=lf
|
|
@ -6,7 +6,7 @@ body:
|
|||
attributes:
|
||||
value: |
|
||||
Thanks for taking the time to fill out this bug report!
|
||||
|
||||
|
||||
Please do not submit feature requests. The [Community Forums](https://community.bitwarden.com) has a section for submitting, voting for, and discussing product feature requests.
|
||||
- type: textarea
|
||||
id: reproduce
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
## Type of change
|
||||
|
||||
- [ ] Bug fix
|
||||
- [ ] New feature development
|
||||
- [ ] Tech debt (refactoring, code cleanup, dependency upgrades, etc)
|
||||
|
@ -6,27 +7,26 @@
|
|||
- [ ] 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
|
||||
- **file.ext:** Description of what was changed and why
|
||||
|
||||
## Screenshots
|
||||
|
||||
<!--Required for any UI changes. Delete if not applicable-->
|
||||
|
||||
|
||||
|
||||
## 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)
|
||||
- [ ] This change requires a **documentation update** (notify the documentation team)
|
||||
- [ ] This change has particular **deployment requirements** (notify the DevOps team)
|
||||
|
|
|
@ -5,7 +5,7 @@ on:
|
|||
workflow_dispatch:
|
||||
inputs: {}
|
||||
schedule:
|
||||
- cron: '0 0 * * 5'
|
||||
- cron: "0 0 * * 5"
|
||||
|
||||
jobs:
|
||||
crowdin-sync:
|
||||
|
@ -15,7 +15,7 @@ jobs:
|
|||
_CROWDIN_PROJECT_ID: "299360"
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f # v2.3.4
|
||||
uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f # v2.3.4
|
||||
|
||||
- name: Login to Azure
|
||||
uses: Azure/login@77f1b2e3fb80c0e8645114159d17008b8a2e475a
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
---
|
||||
name: Enforce PR labels
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [labeled, unlabeled, opened, edited, synchronize]
|
||||
jobs:
|
||||
enforce-label:
|
||||
name: EnforceLabel
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- name: Enforce Label
|
||||
uses: yogevbd/enforce-label-action@8d1e1709b1011e6d90400a0e6cf7c0b77aa5efeb
|
||||
with:
|
||||
BANNED_LABELS: "hold"
|
||||
BANNED_LABELS_DESCRIPTION: "PRs on hold cannot be merged"
|
|
@ -3,6 +3,16 @@ name: Release
|
|||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
release_type:
|
||||
description: 'Release Options'
|
||||
required: true
|
||||
default: 'Initial Release'
|
||||
type: choice
|
||||
options:
|
||||
- Initial Release
|
||||
- Redeploy
|
||||
- Dry Run
|
||||
|
||||
jobs:
|
||||
setup:
|
||||
|
@ -13,10 +23,11 @@ jobs:
|
|||
branch-name: ${{ steps.branch.outputs.branch-name }}
|
||||
steps:
|
||||
- name: Branch check
|
||||
if: ${{ github.event.inputs.release_type != 'Dry Run' }}
|
||||
run: |
|
||||
if [[ "$GITHUB_REF" != "refs/heads/rc" ]] && [[ "$GITHUB_REF" != "refs/heads/hotfix" ]]; then
|
||||
if [[ "$GITHUB_REF" != "refs/heads/rc" ]] && [[ "$GITHUB_REF" != "refs/heads/hotfix-rc" ]]; then
|
||||
echo "==================================="
|
||||
echo "[!] Can only release from the 'rc' or 'hotfix' branches"
|
||||
echo "[!] Can only release from the 'rc' or 'hotfix-rc' branches"
|
||||
echo "==================================="
|
||||
exit 1
|
||||
fi
|
||||
|
@ -39,7 +50,8 @@ jobs:
|
|||
echo "Latest version: $latest_ver"
|
||||
ver=${{ steps.retrieve-version.outputs.package_version }}
|
||||
echo "Version: $ver"
|
||||
if [ "$latest_ver" = "$ver" ]; then
|
||||
if [ "$latest_ver" = "$ver" ] && \
|
||||
[ "${{ github.event.inputs.release_type }}" == "Initial Release" ]; then
|
||||
echo "Version has not been bumped!"
|
||||
exit 1
|
||||
fi
|
||||
|
@ -50,42 +62,69 @@ jobs:
|
|||
BRANCH_NAME=$(basename ${{ github.ref }})
|
||||
echo "::set-output name=branch-name::$BRANCH_NAME"
|
||||
|
||||
- name: Login to Azure
|
||||
uses: Azure/login@ec3c14589bd3e9312b3cc8c41e6860e258df9010
|
||||
with:
|
||||
creds: ${{ secrets.AZURE_PROD_KV_CREDENTIALS }}
|
||||
|
||||
- name: Retrieve secrets
|
||||
id: retrieve-secrets
|
||||
uses: Azure/get-keyvault-secrets@b5c723b9ac7870c022b8c35befe620b7009b336f
|
||||
with:
|
||||
keyvault: "bitwarden-prod-kv"
|
||||
secrets: "aws-electron-access-id, aws-electron-access-key"
|
||||
|
||||
- name: Download all artifacts
|
||||
uses: bitwarden/gh-actions/download-artifacts@23433be15ed6fd046ce12b6889c5184a8d9c8783
|
||||
uses: bitwarden/gh-actions/download-artifacts@c1fa8e09871a860862d6bbe36184b06d2c7e35a8
|
||||
with:
|
||||
workflow: build.yml
|
||||
workflow_conclusion: success
|
||||
branch: ${{ steps.branch.outputs.branch-name }}
|
||||
path: ./artifacts
|
||||
|
||||
- name: Rename .pkg to .pkg.archive
|
||||
env:
|
||||
PKG_VERSION: ${{ steps.retrieve-version.outputs.package_version }}
|
||||
working-directory: ./artifacts
|
||||
run: mv Bitwarden-${{ env.PKG_VERSION }}-universal.pkg Bitwarden-${{ env.PKG_VERSION }}-universal.pkg.archive
|
||||
|
||||
- name: Publish artifacts to S3
|
||||
env:
|
||||
AWS_ACCESS_KEY_ID: ${{ steps.retrieve-secrets.outputs.aws-electron-access-id }}
|
||||
AWS_SECRET_ACCESS_KEY: ${{ steps.retrieve-secrets.outputs.aws-electron-access-key }}
|
||||
AWS_DEFAULT_REGION: 'us-west-2'
|
||||
run: |
|
||||
aws s3 cp ./artifacts s3://public-s3-electron-artifacts/desktop/ \
|
||||
--acl "public-read" \
|
||||
--recursive \
|
||||
--quiet
|
||||
|
||||
- name: Create release
|
||||
uses: ncipollo/release-action@95215a3cb6e6a1908b3c44e00b4fdb15548b1e09 # v2.8.5
|
||||
env:
|
||||
PKG_VERSION: ${{ steps.retrieve-version.outputs.package_version }}
|
||||
with:
|
||||
artifacts: "Bitwarden-${{ env.PKG_VERSION }}-amd64.deb,
|
||||
Bitwarden-${{ env.PKG_VERSION }}-x86_64.rpm,
|
||||
Bitwarden-${{ env.PKG_VERSION }}-x64.freebsd,
|
||||
bitwarden_${{ env.PKG_VERSION }}_amd64.snap,
|
||||
Bitwarden-${{ env.PKG_VERSION }}-x86_64.AppImage,
|
||||
latest-linux.yml,
|
||||
Bitwarden-Portable-${{ env.PKG_VERSION }}.exe,
|
||||
Bitwarden-Installer-${{ env.PKG_VERSION }}.exe,
|
||||
Bitwarden-${{ env.PKG_VERSION }}-ia32-store.appx,
|
||||
Bitwarden-${{ env.PKG_VERSION }}-ia32.appx,
|
||||
Bitwarden-${{ env.PKG_VERSION }}-ia32.nsis.7z,
|
||||
Bitwarden-${{ env.PKG_VERSION }}-x64-store.appx,
|
||||
Bitwarden-${{ env.PKG_VERSION }}-x64.appx,
|
||||
Bitwarden-${{ env.PKG_VERSION }}-x64.nsis.7z,
|
||||
Bitwarden-${{ env.PKG_VERSION }}-arm64-store.appx,
|
||||
Bitwarden-${{ env.PKG_VERSION }}-arm64.appx,
|
||||
Bitwarden-${{ env.PKG_VERSION }}-arm64.nsis.7z,
|
||||
bitwarden.${{ env.PKG_VERSION }}.nupkg,
|
||||
latest.yml,
|
||||
Bitwarden-${{ env.PKG_VERSION }}-universal-mac.zip,
|
||||
Bitwarden-${{ env.PKG_VERSION }}-universal.dmg,
|
||||
Bitwarden-${{ env.PKG_VERSION }}-universal.dmg.blockmap,
|
||||
latest-mac.yml,
|
||||
Bitwarden-${{ env.PKG_VERSION }}-universal.pkg"
|
||||
artifacts: "artifacts/Bitwarden-${{ env.PKG_VERSION }}-amd64.deb,
|
||||
artifacts/Bitwarden-${{ env.PKG_VERSION }}-x86_64.rpm,
|
||||
artifacts/Bitwarden-${{ env.PKG_VERSION }}-x64.freebsd,
|
||||
artifacts/bitwarden_${{ env.PKG_VERSION }}_amd64.snap,
|
||||
artifacts/Bitwarden-${{ env.PKG_VERSION }}-x86_64.AppImage,
|
||||
artifacts/Bitwarden-Portable-${{ env.PKG_VERSION }}.exe,
|
||||
artifacts/Bitwarden-Installer-${{ env.PKG_VERSION }}.exe,
|
||||
artifacts/Bitwarden-${{ env.PKG_VERSION }}-ia32-store.appx,
|
||||
artifacts/Bitwarden-${{ env.PKG_VERSION }}-ia32.appx,
|
||||
artifacts/Bitwarden-${{ env.PKG_VERSION }}-ia32.nsis.7z,
|
||||
artifacts/Bitwarden-${{ env.PKG_VERSION }}-x64-store.appx,
|
||||
artifacts/Bitwarden-${{ env.PKG_VERSION }}-x64.appx,
|
||||
artifacts/Bitwarden-${{ env.PKG_VERSION }}-x64.nsis.7z,
|
||||
artifacts/Bitwarden-${{ env.PKG_VERSION }}-arm64-store.appx,
|
||||
artifacts/Bitwarden-${{ env.PKG_VERSION }}-arm64.appx,
|
||||
artifacts/Bitwarden-${{ env.PKG_VERSION }}-arm64.nsis.7z,
|
||||
artifacts/bitwarden.${{ env.PKG_VERSION }}.nupkg,
|
||||
artifacts/Bitwarden-${{ env.PKG_VERSION }}-universal-mac.zip,
|
||||
artifacts/Bitwarden-${{ env.PKG_VERSION }}-universal.dmg,
|
||||
artifacts/Bitwarden-${{ env.PKG_VERSION }}-universal.dmg.blockmap,
|
||||
artifacts/Bitwarden-${{ env.PKG_VERSION }}-universal.pkg.archive"
|
||||
commit: ${{ github.sha }}
|
||||
tag: v${{ env.PKG_VERSION }}
|
||||
name: Version ${{ env.PKG_VERSION }}
|
||||
|
@ -93,7 +132,6 @@ jobs:
|
|||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
draft: true
|
||||
|
||||
|
||||
snap:
|
||||
name: Deploy Snap
|
||||
runs-on: ubuntu-20.04
|
||||
|
@ -104,16 +142,28 @@ jobs:
|
|||
- name: Checkout Repo
|
||||
uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f # v2.3.4
|
||||
|
||||
- name: Login to Azure
|
||||
uses: Azure/login@77f1b2e3fb80c0e8645114159d17008b8a2e475a
|
||||
with:
|
||||
creds: ${{ secrets.AZURE_PROD_KV_CREDENTIALS }}
|
||||
|
||||
- name: Retrieve secrets
|
||||
id: retrieve-secrets
|
||||
uses: Azure/get-keyvault-secrets@80ccd3fafe5662407cc2e55f202ee34bfff8c403
|
||||
with:
|
||||
keyvault: "bitwarden-prod-kv"
|
||||
secrets: "snapcraft-store-token"
|
||||
|
||||
- name: Install Snap
|
||||
uses: samuelmeuli/action-snapcraft@10d7d0a84d9d86098b19f872257df314b0bd8e2d # v1.2.0
|
||||
with:
|
||||
snapcraft_token: ${{ secrets.SNAP_TOKEN }}
|
||||
snapcraft_token: ${{ steps.retrieve-secrets.outputs.snapcraft-store-token }}
|
||||
|
||||
- name: Setup
|
||||
run: mkdir dist
|
||||
|
||||
- name: Download Snap artifact
|
||||
uses: bitwarden/gh-actions/download-artifacts@23433be15ed6fd046ce12b6889c5184a8d9c8783
|
||||
uses: bitwarden/gh-actions/download-artifacts@c1fa8e09871a860862d6bbe36184b06d2c7e35a8
|
||||
with:
|
||||
workflow: build.yml
|
||||
workflow_conclusion: success
|
||||
|
@ -121,15 +171,12 @@ jobs:
|
|||
artifacts: bitwarden_${{ env._PKG_VERSION }}_amd64.snap
|
||||
path: ./dist
|
||||
|
||||
- name: Test
|
||||
run: ls -alht dist
|
||||
|
||||
- name: Deploy to Snap Store
|
||||
if: ${{ github.event.inputs.release_type != 'Dry Run' }}
|
||||
run: |
|
||||
snapcraft upload dist/bitwarden_${{ env._PKG_VERSION }}_amd64.snap --release stable
|
||||
snapcraft logout
|
||||
|
||||
|
||||
choco:
|
||||
name: Deploy Choco
|
||||
runs-on: windows-2019
|
||||
|
@ -150,7 +197,7 @@ jobs:
|
|||
run: New-Item -ItemType directory -Path ./dist
|
||||
|
||||
- name: Download choco artifact
|
||||
uses: bitwarden/gh-actions/download-artifacts@23433be15ed6fd046ce12b6889c5184a8d9c8783
|
||||
uses: bitwarden/gh-actions/download-artifacts@c1fa8e09871a860862d6bbe36184b06d2c7e35a8
|
||||
with:
|
||||
workflow: build.yml
|
||||
workflow_conclusion: success
|
||||
|
@ -159,6 +206,7 @@ jobs:
|
|||
path: ./dist
|
||||
|
||||
- name: Push to Chocolatey
|
||||
if: ${{ github.event.inputs.release_type != 'Dry Run' }}
|
||||
shell: pwsh
|
||||
run: |
|
||||
cd dist
|
||||
|
|
|
@ -0,0 +1,65 @@
|
|||
---
|
||||
name: Version Bump
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
version_number:
|
||||
description: "New Version"
|
||||
required: true
|
||||
|
||||
jobs:
|
||||
bump_version:
|
||||
name: "Create version_bump_${{ github.event.inputs.version_number }} branch"
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- name: Checkout Branch
|
||||
uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579
|
||||
|
||||
- name: Create Version Branch
|
||||
run: |
|
||||
git switch -c version_bump_${{ github.event.inputs.version_number }}
|
||||
git push -u origin version_bump_${{ github.event.inputs.version_number }}
|
||||
|
||||
- name: Checkout Version Branch
|
||||
uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579
|
||||
with:
|
||||
ref: version_bump_${{ github.event.inputs.version_number }}
|
||||
|
||||
- name: Bump Version - Package
|
||||
uses: bitwarden/gh-actions/version-bump@03ad9a873c39cdc95dd8d77dbbda67f84db43945
|
||||
with:
|
||||
version: ${{ github.event.inputs.version_number }}
|
||||
file_path: "./src/package.json"
|
||||
|
||||
- name: Commit files
|
||||
run: |
|
||||
git config --local user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
||||
git config --local user.name "github-actions[bot]"
|
||||
git commit -m "Bumped version to ${{ github.event.inputs.version_number }}" -a
|
||||
|
||||
- name: Push changes
|
||||
run: git push -u origin version_bump_${{ github.event.inputs.version_number }}
|
||||
|
||||
- name: Create Version PR
|
||||
env:
|
||||
PR_BRANCH: "version_bump_${{ github.event.inputs.version_number }}"
|
||||
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
|
||||
BASE_BRANCH: master
|
||||
TITLE: "Bump version to ${{ github.event.inputs.version_number }}"
|
||||
run: |
|
||||
gh pr create --title "$TITLE" \
|
||||
--base "$BASE" \
|
||||
--head "$PR_BRANCH" \
|
||||
--label "version update" \
|
||||
--label "automated pr" \
|
||||
--body "
|
||||
## Type of change
|
||||
- [ ] Bug fix
|
||||
- [ ] New feature development
|
||||
- [ ] Tech debt (refactoring, code cleanup, dependency upgrades, etc)
|
||||
- [ ] Build/deploy pipeline (DevOps)
|
||||
- [X] Other
|
||||
|
||||
## Objective
|
||||
Automated version bump to ${{ github.event.inputs.version_number }}"
|
|
@ -0,0 +1,11 @@
|
|||
---
|
||||
name: Workflow Linter
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- .github/workflows/**
|
||||
|
||||
jobs:
|
||||
call-workflow:
|
||||
uses: bitwarden/gh-actions/.github/workflows/workflow-linter.yml@master
|
|
@ -0,0 +1 @@
|
|||
_
|
|
@ -0,0 +1,4 @@
|
|||
#!/bin/sh
|
||||
. "$(dirname "$0")/_/husky.sh"
|
||||
|
||||
npx lint-staged
|
|
@ -0,0 +1,14 @@
|
|||
# Build directories
|
||||
build
|
||||
dist
|
||||
dist-safari
|
||||
desktop_native
|
||||
|
||||
jslib
|
||||
|
||||
# External libraries / auto synced locales
|
||||
src/locales
|
||||
src/scripts/duo.js
|
||||
|
||||
# Github Workflows
|
||||
.github/workflows
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"printWidth": 100
|
||||
}
|
|
@ -10,9 +10,7 @@
|
|||
"windows": {
|
||||
"runtimeExecutable": "${workspaceRoot}/node_modules/.bin/electron.cmd"
|
||||
},
|
||||
"args": [
|
||||
"."
|
||||
]
|
||||
"args": ["."]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -6,17 +6,12 @@ Please visit our [Community Forums](https://community.bitwarden.com/) for genera
|
|||
|
||||
Here is how you can get involved:
|
||||
|
||||
* **Request a new feature:** Go to the [Feature Requests category](https://community.bitwarden.com/c/feature-requests/) of the Community Forums. Please search existing feature requests before making a new one
|
||||
|
||||
* **Write code for a new feature:** Make a new post in the [Github Contributions category](https://community.bitwarden.com/c/github-contributions/) of the Community Forums. Include a description of your proposed contribution, screeshots, and links to any relevant feature requests. This helps get feedback from the community and Bitwarden team members before you start writing code
|
||||
|
||||
* **Report a bug or submit a bugfix:** Use Github issues and pull requests
|
||||
|
||||
* **Write documentation:** Submit a pull request to the [Bitwarden help repository](https://github.com/bitwarden/help)
|
||||
|
||||
* **Help other users:** Go to the [User-to-User Support category](https://community.bitwarden.com/c/support/) on the Community Forums
|
||||
|
||||
* **Translate:** See the localization (l10n) section below
|
||||
- **Request a new feature:** Go to the [Feature Requests category](https://community.bitwarden.com/c/feature-requests/) of the Community Forums. Please search existing feature requests before making a new one
|
||||
- **Write code for a new feature:** Make a new post in the [Github Contributions category](https://community.bitwarden.com/c/github-contributions/) of the Community Forums. Include a description of your proposed contribution, screeshots, and links to any relevant feature requests. This helps get feedback from the community and Bitwarden team members before you start writing code
|
||||
- **Report a bug or submit a bugfix:** Use Github issues and pull requests
|
||||
- **Write documentation:** Submit a pull request to the [Bitwarden help repository](https://github.com/bitwarden/help)
|
||||
- **Help other users:** Go to the [Ask the Bitwarden Community category](https://community.bitwarden.com/c/support/) on the Community Forums
|
||||
- **Translate:** See the localization (l10n) section below
|
||||
|
||||
## Contributor Agreement
|
||||
|
||||
|
@ -24,9 +19,9 @@ Please sign the [Contributor Agreement](https://cla-assistant.io/bitwarden/deskt
|
|||
|
||||
## Pull Request Guidelines
|
||||
|
||||
* use `npm run lint` and fix any linting suggestions before submitting a pull request
|
||||
* commit any pull requests against the `master` branch
|
||||
* include a link to your Community Forums post
|
||||
- use `npm run lint` and fix any linting suggestions before submitting a pull request
|
||||
- commit any pull requests against the `master` branch
|
||||
- include a link to your Community Forums post
|
||||
|
||||
# Localization (l10n)
|
||||
|
||||
|
@ -36,6 +31,6 @@ We use a translation tool called [Crowdin](https://crowdin.com) to help manage o
|
|||
|
||||
If you are interested in helping translate the Bitwarden desktop app into another language (or make a translation correction), please register an account at Crowdin and join our project here: https://crowdin.com/project/bitwarden-desktop
|
||||
|
||||
If the language that you are interested in translating is not already listed, create a new account on Crowdin, join the project, and contact the project owner (https://crowdin.com/profile/kspearrin).
|
||||
If the language that you are interested in translating is not already listed, create a new account on Crowdin, join the project, and contact the project owner (https://crowdin.com/profile/dwbit).
|
||||
|
||||
You can read Crowdin's getting started guide for translators here: https://support.crowdin.com/crowdin-intro/
|
||||
|
|
50
README.md
|
@ -2,6 +2,10 @@
|
|||
[![Crowdin](https://d322cqt584bo4o.cloudfront.net/bitwarden-desktop/localized.svg)](https://crowdin.com/project/bitwarden-desktop)
|
||||
[![Join the chat at https://gitter.im/bitwarden/Lobby](https://badges.gitter.im/bitwarden/Lobby.svg)](https://gitter.im/bitwarden/Lobby)
|
||||
|
||||
> **Archived**
|
||||
>
|
||||
> This repository is archived, please go to https://github.com/bitwarden/clients for future development.
|
||||
|
||||
# Bitwarden Desktop Application
|
||||
|
||||
[![Platforms](https://imgur.com/SLv9paA.png "Windows, macOS, and Linux")](https://bitwarden.com/download/)
|
||||
|
@ -12,34 +16,60 @@ The Bitwarden desktop app is written using Electron and Angular. The application
|
|||
|
||||
# Build/Run
|
||||
|
||||
**Requirements**
|
||||
## Requirements
|
||||
|
||||
- [Node.js](https://nodejs.org) v14.17 or greater
|
||||
- NPM v7
|
||||
- Windows users: To compile the native node modules used in the app you will need the *Visual C++ toolset*, available through the standard Visual Studio installer. You will also need to install the *Microsoft Build Tools 2015* and *Windows 10 SDK 17134* as additional dependencies in the Visual Studio installer.
|
||||
- [Node.js](https://nodejs.org) v16.13.1 (LTS) or greater
|
||||
- NPM v8
|
||||
- Windows:
|
||||
- To compile the native node modules used in the app you will need the _Visual C++ toolset_, available through the standard Visual Studio installer. You will also need to install the _Microsoft Build Tools 2015_ and _Windows 10 SDK 17134_ as additional dependencies in the Visual Studio installer.
|
||||
- Linux:
|
||||
- The following packages `build-essential libsecret-1-dev libglib2.0-dev`
|
||||
|
||||
|
||||
**Run the app**
|
||||
## Run the app
|
||||
|
||||
```bash
|
||||
npm install
|
||||
npm ci
|
||||
npm run electron
|
||||
```
|
||||
|
||||
**Debug Native Messaging**
|
||||
### Debug Native Messaging
|
||||
|
||||
Native Messaging (communication with the browser extension) works by having the browser start a lightweight proxy application baked into our desktop binary. To setup an environment which allows
|
||||
for easy debugging you will need to build the application for distribution, i.e. `npm run dist:<platform>`, start the dist version and enable desktop integration. This will write some manifests
|
||||
to disk, Consult the [native manifests](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Native_manifests#Manifest_location) documentation for more details of the manifest
|
||||
format, and the exact locations for the different platforms. *Note* that disabling the desktop integration will delete the manifests, and the files will need to be updated again.
|
||||
format, and the exact locations for the different platforms. _Note_ that disabling the desktop integration will delete the manifests, and the files will need to be updated again.
|
||||
|
||||
The generated manifests are pre-configured with the production ID for the browser extensions. In order to use them with the development builds, the browser extension ID of the development build
|
||||
needs to be added to the `allowed_extensions` section of the manifest. These IDs are generated by the browser, and can be found in the extension settings within the browser.
|
||||
needs to be added to the `allowed_extensions` section of the manifest. These IDs are generated by the browser, and can be found in the extension settings within the browser.
|
||||
|
||||
It will then be possible to run the desktop application as usual using `npm run electron` and communicate with the browser.
|
||||
|
||||
# We're Hiring!
|
||||
|
||||
Interested in contributing in a big way? Consider joining our team! We're hiring for many positions. Please take a look at our [Careers page](https://bitwarden.com/careers/) to see what opportunities are currently open as well as what it's like to work at Bitwarden.
|
||||
|
||||
# Contribute
|
||||
|
||||
Code contributions are welcome! Please commit any pull requests against the `master` branch. Learn more about how to contribute by reading the [`CONTRIBUTING.md`](CONTRIBUTING.md) file.
|
||||
|
||||
Security audits and feedback are welcome. Please open an issue or email us privately if the report is sensitive in nature. You can read our security policy in the [`SECURITY.md`](SECURITY.md) file.
|
||||
|
||||
## Prettier
|
||||
|
||||
We recently migrated to using Prettier as code formatter. All previous branches will need to updated to avoid large merge conflicts using the following steps:
|
||||
|
||||
1. Check out your local Branch
|
||||
2. Run `git merge b4df834b16d4f5d4162a926a5a308bdb3ebc718b`
|
||||
3. Resolve any merge conflicts, commit.
|
||||
4. Run `npm run prettier`
|
||||
5. Commit
|
||||
6. Run `git merge -Xours 521feae535d83166e620c3c28dfc3e7b0314a00e`
|
||||
7. Push
|
||||
|
||||
### Git blame
|
||||
|
||||
We also recommend that you configure git to ignore the prettier revision using:
|
||||
|
||||
```bash
|
||||
git config blame.ignoreRevsFile .git-blame-ignore-revs
|
||||
```
|
||||
|
|
42
SECURITY.md
|
@ -1,39 +1,11 @@
|
|||
Bitwarden believes that working with security researchers across the globe is crucial to keeping our
|
||||
users safe. If you believe you've found a security issue in our product or service, we encourage you to
|
||||
notify us. We welcome working with you to resolve the issue promptly. Thanks in advance!
|
||||
Bitwarden believes that working with security researchers across the globe is crucial to keeping our users safe. If you believe you've found a security issue in our product or service, we encourage you to please submit a report through our [HackerOne Program](https://hackerone.com/bitwarden/). We welcome working with you to resolve the issue promptly. Thanks in advance!
|
||||
|
||||
# Disclosure Policy
|
||||
|
||||
- Let us know as soon as possible upon discovery of a potential security issue, and we'll make every
|
||||
effort to quickly resolve the issue.
|
||||
- Provide us a reasonable amount of time to resolve the issue before any disclosure to the public or a
|
||||
third-party. We may publicly disclose the issue before resolving it, if appropriate.
|
||||
- Make a good faith effort to avoid privacy violations, destruction of data, and interruption or
|
||||
degradation of our service. Only interact with accounts you own or with explicit permission of the
|
||||
account holder.
|
||||
- If you would like to encrypt your report, please use the PGP key with long ID
|
||||
`0xDE6887086F892325FEC04CC0D847525B6931381F` (available in the public keyserver pool).
|
||||
|
||||
# In-scope
|
||||
|
||||
- Security issues in any current release of Bitwarden. This includes the web vault, browser extension,
|
||||
and mobile apps (iOS and Android). Product downloads are available at https://bitwarden.com. Source
|
||||
code is available at https://github.com/bitwarden.
|
||||
|
||||
# Exclusions
|
||||
|
||||
The following bug classes are out-of scope:
|
||||
|
||||
- Bugs that are already reported on any of Bitwarden's issue trackers (https://github.com/bitwarden),
|
||||
or that we already know of. Note that some of our issue tracking is private.
|
||||
- Issues in an upstream software dependency (ex: Xamarin, ASP.NET) which are already reported to the
|
||||
upstream maintainer.
|
||||
- Attacks requiring physical access to a user's device.
|
||||
- Self-XSS
|
||||
- Issues related to software or protocols not under Bitwarden's control
|
||||
- Vulnerabilities in outdated versions of Bitwarden
|
||||
- Missing security best practices that do not directly lead to a vulnerability
|
||||
- Issues that do not have any impact on the general public
|
||||
- Let us know as soon as possible upon discovery of a potential security issue, and we'll make every effort to quickly resolve the issue.
|
||||
- Provide us a reasonable amount of time to resolve the issue before any disclosure to the public or a third-party. We may publicly disclose the issue before resolving it, if appropriate.
|
||||
- Make a good faith effort to avoid privacy violations, destruction of data, and interruption or degradation of our service. Only interact with accounts you own or with explicit permission of the account holder.
|
||||
- If you would like to encrypt your report, please use the PGP key with long ID `0xDE6887086F892325FEC04CC0D847525B6931381F` (available in the public keyserver pool).
|
||||
|
||||
While researching, we'd like to ask you to refrain from:
|
||||
|
||||
|
@ -42,4 +14,8 @@ While researching, we'd like to ask you to refrain from:
|
|||
- Social engineering (including phishing) of Bitwarden staff or contractors
|
||||
- Any physical attempts against Bitwarden property or data centers
|
||||
|
||||
# We want to help you!
|
||||
|
||||
If you have something that you feel is close to exploitation, or if you'd like some information regarding the internal API, or generally have any questions regarding the app that would help in your efforts, please email us at https://bitwarden.com/contact and ask for that information. As stated above, Bitwarden wants to help you find issues, and is more than willing to help.
|
||||
|
||||
Thank you for helping keep Bitwarden and our users safe!
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
target
|
||||
index.node
|
||||
**/node_modules
|
||||
**/.DS_Store
|
||||
npm-debug.log*
|
||||
*.node
|
|
@ -0,0 +1,946 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "0.7.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.55"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "159bb86af3a200e19a068f4224eae4c8bb2d0fa054c7e5d1cacd5cef95e684cd"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||
|
||||
[[package]]
|
||||
name = "bytes"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.73"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-expr"
|
||||
version = "0.10.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5e068cb2806bbc15b439846dc16c5f89f8599f2c3e4d73d4449d38f9b2f0b6c5"
|
||||
dependencies = [
|
||||
"smallvec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "codespan-reporting"
|
||||
version = "0.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e"
|
||||
dependencies = [
|
||||
"termcolor",
|
||||
"unicode-width",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "convert_case"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fb4a24b1aaf0fd0ce8b45161144d6f42cd91677fd5940fd431183eb023b3a2b8"
|
||||
|
||||
[[package]]
|
||||
name = "core-foundation"
|
||||
version = "0.9.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146"
|
||||
dependencies = [
|
||||
"core-foundation-sys",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "core-foundation-sys"
|
||||
version = "0.8.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc"
|
||||
|
||||
[[package]]
|
||||
name = "ctor"
|
||||
version = "0.1.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ccc0a48a9b826acdf4028595adc9db92caea352f7af011a3034acd172a52a0aa"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cxx"
|
||||
version = "1.0.66"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ce2295fe8865279f404147e9b2328e5af0ad11a2c016e58c13acfd48a07d8a55"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"cxxbridge-flags",
|
||||
"cxxbridge-macro",
|
||||
"link-cplusplus",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cxx-build"
|
||||
version = "1.0.66"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0aaaa055d4908326f1b4524b23ae53758019b806c0c4f382ea240982e9766b26"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"codespan-reporting",
|
||||
"once_cell",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"scratch",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cxxbridge-flags"
|
||||
version = "1.0.66"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0a670224c6686471df12560a0b97a08145082e70bd38e2b0b5383b79e46c3da7"
|
||||
|
||||
[[package]]
|
||||
name = "cxxbridge-macro"
|
||||
version = "1.0.66"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7b700096ca0dece28d9535fdb17ab784a8ae155d7f29d39c273643e6292c9620"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "desktop_native"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"core-foundation",
|
||||
"gio",
|
||||
"keytar",
|
||||
"libsecret",
|
||||
"napi",
|
||||
"napi-build",
|
||||
"napi-derive",
|
||||
"scopeguard",
|
||||
"security-framework",
|
||||
"security-framework-sys",
|
||||
"tokio",
|
||||
"widestring",
|
||||
"windows 0.32.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-channel"
|
||||
version = "0.3.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c3083ce4b914124575708913bca19bfe887522d6e2e6d0952943f5eac4a74010"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-core"
|
||||
version = "0.3.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0c09fd04b7e4073ac7156a9539b57a484a8ea920f79c7c675d05d289ab6110d3"
|
||||
|
||||
[[package]]
|
||||
name = "futures-executor"
|
||||
version = "0.3.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9420b90cfa29e327d0429f19be13e7ddb68fa1cccb09d65e5706b8c7a749b8a6"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-task",
|
||||
"futures-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-io"
|
||||
version = "0.3.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fc4045962a5a5e935ee2fdedaa4e08284547402885ab326734432bed5d12966b"
|
||||
|
||||
[[package]]
|
||||
name = "futures-task"
|
||||
version = "0.3.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "57c66a976bf5909d801bbef33416c41372779507e7a6b3a5e25e4749c58f776a"
|
||||
|
||||
[[package]]
|
||||
name = "futures-util"
|
||||
version = "0.3.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d8b7abd5d659d9b90c8cba917f6ec750a74e2dc23902ef9cd4cc8c8b22e6036a"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-task",
|
||||
"pin-project-lite",
|
||||
"pin-utils",
|
||||
"slab",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gio"
|
||||
version = "0.15.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "96efd8a1c00d890f6b45671916e165b5e43ccec61957d443aff6d7e44f62d348"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"futures-io",
|
||||
"gio-sys",
|
||||
"glib",
|
||||
"libc",
|
||||
"once_cell",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gio-sys"
|
||||
version = "0.15.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1d0fa5052773f5a56b8ae47dab09d040f5d9ce1311f4f99006e16e9a08269296"
|
||||
dependencies = [
|
||||
"glib-sys",
|
||||
"gobject-sys",
|
||||
"libc",
|
||||
"system-deps",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "glib"
|
||||
version = "0.15.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aa570813c504bdf7539a9400180c2dd4b789a819556fb86da7226d7d1b037b49"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"futures-executor",
|
||||
"futures-task",
|
||||
"glib-macros",
|
||||
"glib-sys",
|
||||
"gobject-sys",
|
||||
"libc",
|
||||
"once_cell",
|
||||
"smallvec",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "glib-macros"
|
||||
version = "0.15.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "41bfd8d227dead0829ac142454e97531b93f576d0805d779c42bfd799c65c572"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"heck",
|
||||
"proc-macro-crate",
|
||||
"proc-macro-error",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "glib-sys"
|
||||
version = "0.15.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f4366377bd56697de8aaee24e673c575d2694d72e7756324ded2b0428829a7b8"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"system-deps",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gobject-sys"
|
||||
version = "0.15.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "df6859463843c20cf3837e3a9069b6ab2051aeeadf4c899d33344f4aea83189a"
|
||||
dependencies = [
|
||||
"glib-sys",
|
||||
"libc",
|
||||
"system-deps",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9"
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.1.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "keytar"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d361c55fba09829ac620b040f5425bf239b1030c3d6820a84acac8da867dca4d"
|
||||
dependencies = [
|
||||
"keytar-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "keytar-sys"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fe908c6896705a1cb516cd6a5d956c63f08d95ace81b93253a98cd93e1e6a65a"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"cxx",
|
||||
"cxx-build",
|
||||
"pkg-config",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.119"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1bf2e165bb3457c8e098ea76f3e3bc9db55f87aa90d52d0e6be741470916aaa4"
|
||||
|
||||
[[package]]
|
||||
name = "libsecret"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d4af5a2342942fa42d706a424e9f9914287fb8317132750fd73a241140ac38c1"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"gio",
|
||||
"glib",
|
||||
"libc",
|
||||
"libsecret-sys",
|
||||
"once_cell",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libsecret-sys"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "287d2a0fcd95e4d7b0ac6fc9f802691a790d7e522138713b0cacebc4e63cab91"
|
||||
dependencies = [
|
||||
"gio-sys",
|
||||
"glib-sys",
|
||||
"gobject-sys",
|
||||
"libc",
|
||||
"pkg-config",
|
||||
"system-deps",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "link-cplusplus"
|
||||
version = "1.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f8cae2cd7ba2f3f63938b9c724475dfb7b9861b545a90324476324ed21dbc8c8"
|
||||
dependencies = [
|
||||
"cc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
version = "0.4.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "88943dd7ef4a2e5a4bfa2753aaab3013e34ce2533d1996fb18ef591e315e2b3b"
|
||||
dependencies = [
|
||||
"scopeguard",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a"
|
||||
|
||||
[[package]]
|
||||
name = "mio"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ba272f85fa0b41fc91872be579b3bbe0f56b792aa361a380eb669469f68dafb2"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"log",
|
||||
"miow",
|
||||
"ntapi",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "miow"
|
||||
version = "0.3.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b9f1c5b025cda876f66ef43a113f91ebc9f4ccef34843000e0adf6ebbab84e21"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "napi"
|
||||
version = "2.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "17ec66e60f000c78dd7c6215b6fa260e0591e09805024332bc5b3f55acc12244"
|
||||
dependencies = [
|
||||
"ctor",
|
||||
"lazy_static",
|
||||
"napi-sys",
|
||||
"tokio",
|
||||
"windows 0.30.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "napi-build"
|
||||
version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ebd4419172727423cf30351406c54f6cc1b354a2cfb4f1dba3e6cd07f6d5522b"
|
||||
|
||||
[[package]]
|
||||
name = "napi-derive"
|
||||
version = "2.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "74ac5287a5e94a8728fc82d16c5127acc5eb5b8ad6404ef5f82d6a4ce8d5bdd2"
|
||||
dependencies = [
|
||||
"convert_case",
|
||||
"napi-derive-backend",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "napi-derive-backend"
|
||||
version = "1.0.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "427f4f04525635cdf22005d1be62d6d671bcb5550d694a1efb480a315422b4af"
|
||||
dependencies = [
|
||||
"convert_case",
|
||||
"once_cell",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"regex",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "napi-sys"
|
||||
version = "2.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a385494dac3c52cbcacb393bb3b42669e7db8ab240c7ad5115f549eb061f2cc"
|
||||
|
||||
[[package]]
|
||||
name = "ntapi"
|
||||
version = "0.3.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c28774a7fd2fbb4f0babd8237ce554b73af68021b5f695a3cebd6c59bac0980f"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num_cpus"
|
||||
version = "1.13.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1"
|
||||
dependencies = [
|
||||
"hermit-abi",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "da32515d9f6e6e489d7bc9d84c71b060db7247dc035bbe44eac88cf87486d8d5"
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot"
|
||||
version = "0.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "87f5ec2493a61ac0506c0f4199f99070cbe83857b0337006a30f3e6719b8ef58"
|
||||
dependencies = [
|
||||
"lock_api",
|
||||
"parking_lot_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot_core"
|
||||
version = "0.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "28141e0cc4143da2443301914478dc976a61ffdb3f043058310c70df2fed8954"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"redox_syscall",
|
||||
"smallvec",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pin-project-lite"
|
||||
version = "0.2.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e280fbe77cc62c91527259e9442153f4688736748d24660126286329742b4c6c"
|
||||
|
||||
[[package]]
|
||||
name = "pin-utils"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
|
||||
|
||||
[[package]]
|
||||
name = "pkg-config"
|
||||
version = "0.3.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "58893f751c9b0412871a09abd62ecd2a00298c6c83befa223ef98c52aef40cbe"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-crate"
|
||||
version = "1.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e17d47ce914bf4de440332250b0edd23ce48c005f59fab39d3335866b114f11a"
|
||||
dependencies = [
|
||||
"thiserror",
|
||||
"toml",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-error"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
|
||||
dependencies = [
|
||||
"proc-macro-error-attr",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-error-attr"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.36"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029"
|
||||
dependencies = [
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "864d3e96a899863136fc6e99f3d7cae289dafe43bf2c5ac19b70df7210c0a145"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.2.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8383f39639269cde97d255a32bdb68c047337295414940c68bdd30c2e13203ff"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.5.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.6.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b"
|
||||
|
||||
[[package]]
|
||||
name = "scopeguard"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
|
||||
|
||||
[[package]]
|
||||
name = "scratch"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "96311ef4a16462c757bb6a39152c40f58f31cd2602a40fceb937e2bc34e6cbab"
|
||||
|
||||
[[package]]
|
||||
name = "security-framework"
|
||||
version = "2.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2dc14f172faf8a0194a3aded622712b0de276821addc574fa54fc0a1167e10dc"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"core-foundation",
|
||||
"core-foundation-sys",
|
||||
"libc",
|
||||
"security-framework-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "security-framework-sys"
|
||||
version = "2.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0160a13a177a45bfb43ce71c01580998474f556ad854dcbca936dd2841a5c556"
|
||||
dependencies = [
|
||||
"core-foundation-sys",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.136"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ce31e24b01e1e524df96f1c2fdd054405f8d7376249a5110886fb4b658484789"
|
||||
|
||||
[[package]]
|
||||
name = "signal-hook-registry"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "slab"
|
||||
version = "0.4.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9def91fd1e018fe007022791f865d0ccc9b3a0d5001e01aabb8b40e46000afb5"
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f2dd574626839106c320a323308629dcb1acfc96e32a8cba364ddc61ac23ee83"
|
||||
|
||||
[[package]]
|
||||
name = "socket2"
|
||||
version = "0.4.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "66d72b759436ae32898a2af0a14218dbf55efde3feeb170eb623637db85ee1e0"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.86"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a65b3f4ffa0092e9887669db0eae07941f023991ab58ea44da8fe8e2d511c6b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "system-deps"
|
||||
version = "6.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a1a45a1c4c9015217e12347f2a411b57ce2c4fc543913b14b6fe40483328e709"
|
||||
dependencies = [
|
||||
"cfg-expr",
|
||||
"heck",
|
||||
"pkg-config",
|
||||
"toml",
|
||||
"version-compare",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "termcolor"
|
||||
version = "1.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755"
|
||||
dependencies = [
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.30"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.30"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio"
|
||||
version = "1.17.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2af73ac49756f3f7c01172e34a23e5d0216f6c32333757c2c61feb2bbff5a5ee"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"libc",
|
||||
"memchr",
|
||||
"mio",
|
||||
"num_cpus",
|
||||
"once_cell",
|
||||
"parking_lot",
|
||||
"pin-project-lite",
|
||||
"signal-hook-registry",
|
||||
"socket2",
|
||||
"tokio-macros",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-macros"
|
||||
version = "1.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b557f72f448c511a979e2564e55d74e6c4432fc96ff4f6241bc6bded342643b7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
version = "0.5.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-width"
|
||||
version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
|
||||
|
||||
[[package]]
|
||||
name = "version-compare"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fe88247b92c1df6b6de80ddc290f3976dbdf2f5f5d3fd049a9fb598c6dd5ca73"
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
||||
|
||||
[[package]]
|
||||
name = "widestring"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "17882f045410753661207383517a6f62ec3dbeb6a4ed2acce01f0728238d1983"
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
||||
dependencies = [
|
||||
"winapi-i686-pc-windows-gnu",
|
||||
"winapi-x86_64-pc-windows-gnu",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-i686-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-util"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-x86_64-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
|
||||
[[package]]
|
||||
name = "windows"
|
||||
version = "0.30.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b749ebd2304aa012c5992d11a25d07b406bdbe5f79d371cb7a918ce501a19eb0"
|
||||
dependencies = [
|
||||
"windows_aarch64_msvc 0.30.0",
|
||||
"windows_i686_gnu 0.30.0",
|
||||
"windows_i686_msvc 0.30.0",
|
||||
"windows_x86_64_gnu 0.30.0",
|
||||
"windows_x86_64_msvc 0.30.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows"
|
||||
version = "0.32.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fbedf6db9096bc2364adce0ae0aa636dcd89f3c3f2cd67947062aaf0ca2a10ec"
|
||||
dependencies = [
|
||||
"windows_aarch64_msvc 0.32.0",
|
||||
"windows_i686_gnu 0.32.0",
|
||||
"windows_i686_msvc 0.32.0",
|
||||
"windows_x86_64_gnu 0.32.0",
|
||||
"windows_x86_64_msvc 0.32.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.32.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3df6e476185f92a12c072be4a189a0210dcdcf512a1891d6dff9edb874deadc6"
|
||||
dependencies = [
|
||||
"windows_aarch64_msvc 0.32.0",
|
||||
"windows_i686_gnu 0.32.0",
|
||||
"windows_i686_msvc 0.32.0",
|
||||
"windows_x86_64_gnu 0.32.0",
|
||||
"windows_x86_64_msvc 0.32.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.30.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "29277a4435d642f775f63c7d1faeb927adba532886ce0287bd985bffb16b6bca"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.32.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d8e92753b1c443191654ec532f14c199742964a061be25d77d7a96f09db20bf5"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.30.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1145e1989da93956c68d1864f32fb97c8f561a8f89a5125f6a2b7ea75524e4b8"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.32.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6a711c68811799e017b6038e0922cb27a5e2f43a2ddb609fe0b6f3eeda9de615"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.30.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d4a09e3a0d4753b73019db171c1339cd4362c8c44baf1bcea336235e955954a6"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.32.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "146c11bb1a02615db74680b32a68e2d61f553cc24c4eb5b4ca10311740e44172"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.30.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ca64fcb0220d58db4c119e050e7af03c69e6f4f415ef69ec1773d9aab422d5a"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.32.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c912b12f7454c6620635bbff3450962753834be2a594819bd5e945af18ec64bc"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.30.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "08cabc9f0066848fef4bc6a1c1668e6efce38b661d2aeec75d18d8617eebb5f1"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.32.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "504a2476202769977a040c6364301a3f65d0cc9e3fb08600b2bda150a0488316"
|
|
@ -0,0 +1,43 @@
|
|||
[package]
|
||||
edition = "2021"
|
||||
exclude = ["index.node"]
|
||||
license = "GPL-3.0"
|
||||
name = "desktop_native"
|
||||
version = "0.0.0"
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0"
|
||||
napi = {version = "2", features = ["async"]}
|
||||
napi-derive = "2"
|
||||
scopeguard = "1.1.0"
|
||||
tokio = {version = "1.17.0", features = ["full"]}
|
||||
|
||||
[build-dependencies]
|
||||
napi-build = "1"
|
||||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
widestring = "0.5.1"
|
||||
windows = {version = "0.32.0", features = [
|
||||
"alloc",
|
||||
"Foundation",
|
||||
"Storage_Streams",
|
||||
"Win32_Foundation",
|
||||
"Win32_Security_Credentials",
|
||||
]}
|
||||
|
||||
[target.'cfg(windows)'.dev-dependencies]
|
||||
keytar = "0.1.6"
|
||||
|
||||
[target.'cfg(target_os = "macos")'.dependencies]
|
||||
core-foundation = "0.9.3"
|
||||
security-framework = "2.6.1"
|
||||
security-framework-sys = "2.6.1"
|
||||
|
||||
[target.'cfg(target_os = "linux")'.dependencies]
|
||||
gio = "0.15.6"
|
||||
libsecret = "0.1.4"
|
|
@ -0,0 +1,22 @@
|
|||
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||
const child_process = require("child_process");
|
||||
const process = require("process");
|
||||
|
||||
let targets = [];
|
||||
switch (process.platform) {
|
||||
case "win32":
|
||||
targets = ["i686-pc-windows-msvc", "x86_64-pc-windows-msvc", "aarch64-pc-windows-msvc"];
|
||||
break;
|
||||
|
||||
case "darwin":
|
||||
targets = ["x86_64-apple-darwin", "aarch64-apple-darwin"];
|
||||
break;
|
||||
|
||||
default:
|
||||
targets = ['x86_64-unknown-linux-gnu'];
|
||||
break;
|
||||
}
|
||||
|
||||
targets.forEach(target => {
|
||||
child_process.execSync(`npm run build -- --target ${target}`, {stdio: 'inherit'});
|
||||
});
|
|
@ -0,0 +1,5 @@
|
|||
extern crate napi_build;
|
||||
|
||||
fn main() {
|
||||
napi_build::setup();
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
|
||||
/* auto-generated by NAPI-RS */
|
||||
|
||||
export namespace passwords {
|
||||
/** Fetch the stored password from the keychain. */
|
||||
export function getPassword(service: string, account: string): Promise<string>
|
||||
/** Fetch the stored password from the keychain that was stored with Keytar. */
|
||||
export function getPasswordKeytar(service: string, account: string): Promise<string>
|
||||
/** Save the password to the keychain. Adds an entry if none exists otherwise updates the existing entry. */
|
||||
export function setPassword(service: string, account: string, password: string): Promise<void>
|
||||
/** Delete the stored password from the keychain. */
|
||||
export function deletePassword(service: string, account: string): Promise<void>
|
||||
}
|
|
@ -0,0 +1,241 @@
|
|||
const { existsSync, readFileSync } = require('fs')
|
||||
const { join } = require('path')
|
||||
|
||||
const { platform, arch } = process
|
||||
|
||||
let nativeBinding = null
|
||||
let localFileExisted = false
|
||||
let loadError = null
|
||||
|
||||
function isMusl() {
|
||||
// For Node 10
|
||||
if (!process.report || typeof process.report.getReport !== 'function') {
|
||||
try {
|
||||
return readFileSync('/usr/bin/ldd', 'utf8').includes('musl')
|
||||
} catch (e) {
|
||||
return true
|
||||
}
|
||||
} else {
|
||||
const { glibcVersionRuntime } = process.report.getReport().header
|
||||
return !glibcVersionRuntime
|
||||
}
|
||||
}
|
||||
|
||||
switch (platform) {
|
||||
case 'android':
|
||||
switch (arch) {
|
||||
case 'arm64':
|
||||
localFileExisted = existsSync(join(__dirname, 'desktop_native.android-arm64.node'))
|
||||
try {
|
||||
if (localFileExisted) {
|
||||
nativeBinding = require('./desktop_native.android-arm64.node')
|
||||
} else {
|
||||
nativeBinding = require('@bitwarden/desktop_native-android-arm64')
|
||||
}
|
||||
} catch (e) {
|
||||
loadError = e
|
||||
}
|
||||
break
|
||||
case 'arm':
|
||||
localFileExisted = existsSync(join(__dirname, 'desktop_native.android-arm-eabi.node'))
|
||||
try {
|
||||
if (localFileExisted) {
|
||||
nativeBinding = require('./desktop_native.android-arm-eabi.node')
|
||||
} else {
|
||||
nativeBinding = require('@bitwarden/desktop_native-android-arm-eabi')
|
||||
}
|
||||
} catch (e) {
|
||||
loadError = e
|
||||
}
|
||||
break
|
||||
default:
|
||||
throw new Error(`Unsupported architecture on Android ${arch}`)
|
||||
}
|
||||
break
|
||||
case 'win32':
|
||||
switch (arch) {
|
||||
case 'x64':
|
||||
localFileExisted = existsSync(
|
||||
join(__dirname, 'desktop_native.win32-x64-msvc.node')
|
||||
)
|
||||
try {
|
||||
if (localFileExisted) {
|
||||
nativeBinding = require('./desktop_native.win32-x64-msvc.node')
|
||||
} else {
|
||||
nativeBinding = require('@bitwarden/desktop_native-win32-x64-msvc')
|
||||
}
|
||||
} catch (e) {
|
||||
loadError = e
|
||||
}
|
||||
break
|
||||
case 'ia32':
|
||||
localFileExisted = existsSync(
|
||||
join(__dirname, 'desktop_native.win32-ia32-msvc.node')
|
||||
)
|
||||
try {
|
||||
if (localFileExisted) {
|
||||
nativeBinding = require('./desktop_native.win32-ia32-msvc.node')
|
||||
} else {
|
||||
nativeBinding = require('@bitwarden/desktop_native-win32-ia32-msvc')
|
||||
}
|
||||
} catch (e) {
|
||||
loadError = e
|
||||
}
|
||||
break
|
||||
case 'arm64':
|
||||
localFileExisted = existsSync(
|
||||
join(__dirname, 'desktop_native.win32-arm64-msvc.node')
|
||||
)
|
||||
try {
|
||||
if (localFileExisted) {
|
||||
nativeBinding = require('./desktop_native.win32-arm64-msvc.node')
|
||||
} else {
|
||||
nativeBinding = require('@bitwarden/desktop_native-win32-arm64-msvc')
|
||||
}
|
||||
} catch (e) {
|
||||
loadError = e
|
||||
}
|
||||
break
|
||||
default:
|
||||
throw new Error(`Unsupported architecture on Windows: ${arch}`)
|
||||
}
|
||||
break
|
||||
case 'darwin':
|
||||
switch (arch) {
|
||||
case 'x64':
|
||||
localFileExisted = existsSync(join(__dirname, 'desktop_native.darwin-x64.node'))
|
||||
try {
|
||||
if (localFileExisted) {
|
||||
nativeBinding = require('./desktop_native.darwin-x64.node')
|
||||
} else {
|
||||
nativeBinding = require('@bitwarden/desktop_native-darwin-x64')
|
||||
}
|
||||
} catch (e) {
|
||||
loadError = e
|
||||
}
|
||||
break
|
||||
case 'arm64':
|
||||
localFileExisted = existsSync(
|
||||
join(__dirname, 'desktop_native.darwin-arm64.node')
|
||||
)
|
||||
try {
|
||||
if (localFileExisted) {
|
||||
nativeBinding = require('./desktop_native.darwin-arm64.node')
|
||||
} else {
|
||||
nativeBinding = require('@bitwarden/desktop_native-darwin-arm64')
|
||||
}
|
||||
} catch (e) {
|
||||
loadError = e
|
||||
}
|
||||
break
|
||||
default:
|
||||
throw new Error(`Unsupported architecture on macOS: ${arch}`)
|
||||
}
|
||||
break
|
||||
case 'freebsd':
|
||||
if (arch !== 'x64') {
|
||||
throw new Error(`Unsupported architecture on FreeBSD: ${arch}`)
|
||||
}
|
||||
localFileExisted = existsSync(join(__dirname, 'desktop_native.freebsd-x64.node'))
|
||||
try {
|
||||
if (localFileExisted) {
|
||||
nativeBinding = require('./desktop_native.freebsd-x64.node')
|
||||
} else {
|
||||
nativeBinding = require('@bitwarden/desktop_native-freebsd-x64')
|
||||
}
|
||||
} catch (e) {
|
||||
loadError = e
|
||||
}
|
||||
break
|
||||
case 'linux':
|
||||
switch (arch) {
|
||||
case 'x64':
|
||||
if (isMusl()) {
|
||||
localFileExisted = existsSync(
|
||||
join(__dirname, 'desktop_native.linux-x64-musl.node')
|
||||
)
|
||||
try {
|
||||
if (localFileExisted) {
|
||||
nativeBinding = require('./desktop_native.linux-x64-musl.node')
|
||||
} else {
|
||||
nativeBinding = require('@bitwarden/desktop_native-linux-x64-musl')
|
||||
}
|
||||
} catch (e) {
|
||||
loadError = e
|
||||
}
|
||||
} else {
|
||||
localFileExisted = existsSync(
|
||||
join(__dirname, 'desktop_native.linux-x64-gnu.node')
|
||||
)
|
||||
try {
|
||||
if (localFileExisted) {
|
||||
nativeBinding = require('./desktop_native.linux-x64-gnu.node')
|
||||
} else {
|
||||
nativeBinding = require('@bitwarden/desktop_native-linux-x64-gnu')
|
||||
}
|
||||
} catch (e) {
|
||||
loadError = e
|
||||
}
|
||||
}
|
||||
break
|
||||
case 'arm64':
|
||||
if (isMusl()) {
|
||||
localFileExisted = existsSync(
|
||||
join(__dirname, 'desktop_native.linux-arm64-musl.node')
|
||||
)
|
||||
try {
|
||||
if (localFileExisted) {
|
||||
nativeBinding = require('./desktop_native.linux-arm64-musl.node')
|
||||
} else {
|
||||
nativeBinding = require('@bitwarden/desktop_native-linux-arm64-musl')
|
||||
}
|
||||
} catch (e) {
|
||||
loadError = e
|
||||
}
|
||||
} else {
|
||||
localFileExisted = existsSync(
|
||||
join(__dirname, 'desktop_native.linux-arm64-gnu.node')
|
||||
)
|
||||
try {
|
||||
if (localFileExisted) {
|
||||
nativeBinding = require('./desktop_native.linux-arm64-gnu.node')
|
||||
} else {
|
||||
nativeBinding = require('@bitwarden/desktop_native-linux-arm64-gnu')
|
||||
}
|
||||
} catch (e) {
|
||||
loadError = e
|
||||
}
|
||||
}
|
||||
break
|
||||
case 'arm':
|
||||
localFileExisted = existsSync(
|
||||
join(__dirname, 'desktop_native.linux-arm-gnueabihf.node')
|
||||
)
|
||||
try {
|
||||
if (localFileExisted) {
|
||||
nativeBinding = require('./desktop_native.linux-arm-gnueabihf.node')
|
||||
} else {
|
||||
nativeBinding = require('@bitwarden/desktop_native-linux-arm-gnueabihf')
|
||||
}
|
||||
} catch (e) {
|
||||
loadError = e
|
||||
}
|
||||
break
|
||||
default:
|
||||
throw new Error(`Unsupported architecture on Linux: ${arch}`)
|
||||
}
|
||||
break
|
||||
default:
|
||||
throw new Error(`Unsupported OS: ${platform}, architecture: ${arch}`)
|
||||
}
|
||||
|
||||
if (!nativeBinding) {
|
||||
if (loadError) {
|
||||
throw loadError
|
||||
}
|
||||
throw new Error(`Failed to load native binding`)
|
||||
}
|
||||
|
||||
const { passwords } = nativeBinding
|
||||
|
||||
module.exports.passwords = passwords
|
|
@ -0,0 +1,41 @@
|
|||
{
|
||||
"name": "@bitwarden/desktop_native",
|
||||
"version": "0.1.0",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@bitwarden/desktop_native",
|
||||
"version": "0.1.0",
|
||||
"hasInstallScript": true,
|
||||
"license": "GPL-3.0",
|
||||
"devDependencies": {
|
||||
"@napi-rs/cli": "^2.6.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@napi-rs/cli": {
|
||||
"version": "2.6.2",
|
||||
"resolved": "https://registry.npmjs.org/@napi-rs/cli/-/cli-2.6.2.tgz",
|
||||
"integrity": "sha512-EmH+DQDEBUIoqMim0cc+X96ImtcIZLFjgW5WWORpzYnA9Ug7zNPO7jCLMhIQRd/p5AdWaXrT4SVXc/aip09rKQ==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"napi": "scripts/index.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/Brooooooklyn"
|
||||
}
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@napi-rs/cli": {
|
||||
"version": "2.6.2",
|
||||
"resolved": "https://registry.npmjs.org/@napi-rs/cli/-/cli-2.6.2.tgz",
|
||||
"integrity": "sha512-EmH+DQDEBUIoqMim0cc+X96ImtcIZLFjgW5WWORpzYnA9Ug7zNPO7jCLMhIQRd/p5AdWaXrT4SVXc/aip09rKQ==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
{
|
||||
"name": "@bitwarden/desktop_native",
|
||||
"version": "0.1.0",
|
||||
"description": "",
|
||||
"main": "index.node",
|
||||
"scripts": {
|
||||
"build": "napi build --release --platform",
|
||||
"build:debug": "napi build --platform",
|
||||
"build:cross-platform": "node build.js",
|
||||
"test": "cargo test"
|
||||
},
|
||||
"author": "",
|
||||
"license": "GPL-3.0",
|
||||
"devDependencies": {
|
||||
"@napi-rs/cli": "^2.6.2"
|
||||
},
|
||||
"napi": {
|
||||
"name": "desktop_native",
|
||||
"triples": {
|
||||
"defaults": true,
|
||||
"additional": [
|
||||
"x86_64-unknown-linux-musl",
|
||||
"aarch64-unknown-linux-gnu",
|
||||
"i686-pc-windows-msvc",
|
||||
"armv7-unknown-linux-gnueabihf",
|
||||
"aarch64-apple-darwin",
|
||||
"aarch64-unknown-linux-musl",
|
||||
"aarch64-pc-windows-msvc"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
#[macro_use]
|
||||
extern crate napi_derive;
|
||||
|
||||
mod password;
|
||||
|
||||
#[napi]
|
||||
pub mod passwords {
|
||||
/// Fetch the stored password from the keychain.
|
||||
#[napi]
|
||||
pub async fn get_password(service: String, account: String) -> napi::Result<String> {
|
||||
super::password::get_password(&service, &account)
|
||||
.map_err(|e| napi::Error::from_reason(e.to_string()))
|
||||
}
|
||||
|
||||
/// Fetch the stored password from the keychain that was stored with Keytar.
|
||||
#[napi]
|
||||
pub async fn get_password_keytar(service: String, account: String) -> napi::Result<String> {
|
||||
super::password::get_password_keytar(&service, &account)
|
||||
.map_err(|e| napi::Error::from_reason(e.to_string()))
|
||||
}
|
||||
|
||||
/// Save the password to the keychain. Adds an entry if none exists otherwise updates the existing entry.
|
||||
#[napi]
|
||||
pub async fn set_password(
|
||||
service: String,
|
||||
account: String,
|
||||
password: String,
|
||||
) -> napi::Result<()> {
|
||||
super::password::set_password(&service, &account, &password)
|
||||
.map_err(|e| napi::Error::from_reason(e.to_string()))
|
||||
}
|
||||
|
||||
/// Delete the stored password from the keychain.
|
||||
#[napi]
|
||||
pub async fn delete_password(service: String, account: String) -> napi::Result<()> {
|
||||
super::password::delete_password(&service, &account)
|
||||
.map_err(|e| napi::Error::from_reason(e.to_string()))
|
||||
}
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
use anyhow::Result;
|
||||
use security_framework::passwords::{
|
||||
delete_generic_password, get_generic_password, set_generic_password,
|
||||
};
|
||||
|
||||
pub fn get_password(service: &str, account: &str) -> Result<String> {
|
||||
let result = String::from_utf8(get_generic_password(&service, &account)?)?;
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
pub fn get_password_keytar(service: &str, account: &str) -> Result<String> {
|
||||
get_password(service, account)
|
||||
}
|
||||
|
||||
pub fn set_password(service: &str, account: &str, password: &str) -> Result<()> {
|
||||
let result = set_generic_password(&service, &account, password.as_bytes())?;
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
pub fn delete_password(service: &str, account: &str) -> Result<()> {
|
||||
let result = delete_generic_password(&service, &account)?;
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test() {
|
||||
scopeguard::defer!(delete_password("BitwardenTest", "BitwardenTest").unwrap_or({}););
|
||||
set_password("BitwardenTest", "BitwardenTest", "Random").unwrap();
|
||||
assert_eq!(
|
||||
"Random",
|
||||
get_password("BitwardenTest", "BitwardenTest").unwrap()
|
||||
);
|
||||
delete_password("BitwardenTest", "BitwardenTest").unwrap();
|
||||
|
||||
// Ensure password is deleted
|
||||
match get_password("BitwardenTest", "BitwardenTest") {
|
||||
Ok(_) => panic!("Got a result"),
|
||||
Err(e) => assert_eq!(
|
||||
"The specified item could not be found in the keychain.",
|
||||
e.to_string()
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_error_no_password() {
|
||||
match get_password("Unknown", "Unknown") {
|
||||
Ok(_) => panic!("Got a result"),
|
||||
Err(e) => assert_eq!(
|
||||
"The specified item could not be found in the keychain.",
|
||||
e.to_string()
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
#[cfg_attr(target_os = "linux", path = "unix.rs")]
|
||||
#[cfg_attr(target_os = "windows", path = "windows.rs")]
|
||||
#[cfg_attr(target_os = "macos", path = "macos.rs")]
|
||||
mod password;
|
||||
pub use password::*;
|
|
@ -0,0 +1,91 @@
|
|||
use anyhow::{anyhow, Result};
|
||||
use libsecret::{password_clear_sync, password_lookup_sync, password_store_sync, Schema};
|
||||
use std::collections::HashMap;
|
||||
|
||||
pub fn get_password(service: &str, account: &str) -> Result<String> {
|
||||
let res = password_lookup_sync(
|
||||
Some(&get_schema()),
|
||||
build_attributes(service, account),
|
||||
gio::Cancellable::NONE,
|
||||
)?;
|
||||
|
||||
match res {
|
||||
Some(s) => Ok(String::from(s)),
|
||||
None => Err(anyhow!("No password found")),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_password_keytar(service: &str, account: &str) -> Result<String> {
|
||||
get_password(service, account)
|
||||
}
|
||||
|
||||
pub fn set_password(service: &str, account: &str, password: &str) -> Result<()> {
|
||||
let result = password_store_sync(
|
||||
Some(&get_schema()),
|
||||
build_attributes(service, account),
|
||||
Some(&libsecret::COLLECTION_DEFAULT),
|
||||
&format!("{}/{}", service, account),
|
||||
password,
|
||||
gio::Cancellable::NONE,
|
||||
)?;
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
pub fn delete_password(service: &str, account: &str) -> Result<()> {
|
||||
let result = password_clear_sync(
|
||||
Some(&get_schema()),
|
||||
build_attributes(service, account),
|
||||
gio::Cancellable::NONE,
|
||||
)?;
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
fn get_schema() -> Schema {
|
||||
let mut attributes = std::collections::HashMap::new();
|
||||
attributes.insert("service", libsecret::SchemaAttributeType::String);
|
||||
attributes.insert("account", libsecret::SchemaAttributeType::String);
|
||||
|
||||
libsecret::Schema::new(
|
||||
"org.freedesktop.Secret.Generic",
|
||||
libsecret::SchemaFlags::NONE,
|
||||
attributes,
|
||||
)
|
||||
}
|
||||
|
||||
fn build_attributes<'a>(service: &'a str, account: &'a str) -> HashMap<&'a str, &'a str> {
|
||||
let mut attributes = HashMap::new();
|
||||
attributes.insert("service", service);
|
||||
attributes.insert("account", account);
|
||||
|
||||
attributes
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test() {
|
||||
scopeguard::defer!(delete_password("BitwardenTest", "BitwardenTest").unwrap_or({}););
|
||||
set_password("BitwardenTest", "BitwardenTest", "Random").unwrap();
|
||||
assert_eq!(
|
||||
"Random",
|
||||
get_password("BitwardenTest", "BitwardenTest").unwrap()
|
||||
);
|
||||
delete_password("BitwardenTest", "BitwardenTest").unwrap();
|
||||
|
||||
// Ensure password is deleted
|
||||
match get_password("BitwardenTest", "BitwardenTest") {
|
||||
Ok(_) => panic!("Got a result"),
|
||||
Err(e) => assert_eq!("No password found", e.to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_error_no_password() {
|
||||
match get_password("BitwardenTest", "BitwardenTest") {
|
||||
Ok(_) => panic!("Got a result"),
|
||||
Err(e) => assert_eq!("No password found", e.to_string()),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,180 @@
|
|||
use anyhow::{anyhow, Result};
|
||||
use widestring::{U16CString, U16String};
|
||||
use windows::Win32::{
|
||||
Foundation::{GetLastError, ERROR_NOT_FOUND, FILETIME, PWSTR, WIN32_ERROR},
|
||||
Security::Credentials::{
|
||||
CredDeleteW, CredFree, CredReadW, CredWriteW, CREDENTIALW, CRED_FLAGS,
|
||||
CRED_PERSIST_ENTERPRISE, CRED_TYPE_GENERIC,
|
||||
},
|
||||
};
|
||||
|
||||
const CRED_FLAGS_NONE: u32 = 0;
|
||||
|
||||
pub fn get_password<'a>(service: &str, account: &str) -> Result<String> {
|
||||
let target_name = U16CString::from_str(target_name(service, account))?;
|
||||
|
||||
let mut credential: *mut CREDENTIALW = std::ptr::null_mut();
|
||||
let credential_ptr = &mut credential;
|
||||
|
||||
let result = unsafe {
|
||||
CredReadW(
|
||||
PWSTR(target_name.as_ptr()),
|
||||
CRED_TYPE_GENERIC.0,
|
||||
CRED_FLAGS_NONE,
|
||||
credential_ptr,
|
||||
)
|
||||
};
|
||||
|
||||
scopeguard::defer!({
|
||||
unsafe { CredFree(credential as *mut _) };
|
||||
});
|
||||
|
||||
if !result.as_bool() {
|
||||
return Err(anyhow!(convert_error(unsafe { GetLastError() })));
|
||||
}
|
||||
|
||||
let password = unsafe {
|
||||
U16String::from_ptr(
|
||||
(*credential).CredentialBlob as *const u16,
|
||||
(*credential).CredentialBlobSize as usize / 2,
|
||||
)
|
||||
.to_string_lossy()
|
||||
};
|
||||
|
||||
Ok(String::from(password))
|
||||
}
|
||||
|
||||
// Remove this after sufficient releases
|
||||
pub fn get_password_keytar<'a>(service: &str, account: &str) -> Result<String> {
|
||||
let target_name = U16CString::from_str(target_name(service, account))?;
|
||||
|
||||
let mut credential: *mut CREDENTIALW = std::ptr::null_mut();
|
||||
let credential_ptr = &mut credential;
|
||||
|
||||
let result = unsafe {
|
||||
CredReadW(
|
||||
PWSTR(target_name.as_ptr()),
|
||||
CRED_TYPE_GENERIC.0,
|
||||
CRED_FLAGS_NONE,
|
||||
credential_ptr,
|
||||
)
|
||||
};
|
||||
|
||||
scopeguard::defer!({
|
||||
unsafe { CredFree(credential as *mut _) };
|
||||
});
|
||||
|
||||
if !result.as_bool() {
|
||||
return Err(anyhow!(unsafe { GetLastError() }.0.to_string()));
|
||||
}
|
||||
|
||||
let password = unsafe {
|
||||
std::str::from_utf8_unchecked(std::slice::from_raw_parts(
|
||||
(*credential).CredentialBlob,
|
||||
(*credential).CredentialBlobSize as usize,
|
||||
))
|
||||
};
|
||||
|
||||
Ok(String::from(password))
|
||||
}
|
||||
|
||||
pub fn set_password(service: &str, account: &str, password: &str) -> Result<()> {
|
||||
let target_name = U16CString::from_str(target_name(service, account))?;
|
||||
let user_name = U16CString::from_str(account)?;
|
||||
let last_written = FILETIME {
|
||||
dwLowDateTime: 0,
|
||||
dwHighDateTime: 0,
|
||||
};
|
||||
|
||||
let credential = U16CString::from_str(password)?;
|
||||
let credential_len = password.len() as u32 * 2;
|
||||
|
||||
let credential = CREDENTIALW {
|
||||
Flags: CRED_FLAGS(CRED_FLAGS_NONE),
|
||||
Type: CRED_TYPE_GENERIC,
|
||||
TargetName: PWSTR(target_name.as_ptr()),
|
||||
Comment: PWSTR::default(),
|
||||
LastWritten: last_written,
|
||||
CredentialBlobSize: credential_len,
|
||||
CredentialBlob: credential.as_ptr() as *mut u8,
|
||||
Persist: CRED_PERSIST_ENTERPRISE,
|
||||
AttributeCount: 0,
|
||||
Attributes: std::ptr::null_mut(),
|
||||
TargetAlias: PWSTR::default(),
|
||||
UserName: PWSTR(user_name.as_ptr()),
|
||||
};
|
||||
|
||||
let result = unsafe { CredWriteW(&credential, 0) };
|
||||
if !result.as_bool() {
|
||||
return Err(anyhow!(unsafe { GetLastError() }.0.to_string()));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn delete_password(service: &str, account: &str) -> Result<()> {
|
||||
let target_name = U16CString::from_str(target_name(service, account))?;
|
||||
|
||||
unsafe {
|
||||
CredDeleteW(
|
||||
PWSTR(target_name.as_ptr()),
|
||||
CRED_TYPE_GENERIC.0,
|
||||
CRED_FLAGS_NONE,
|
||||
)
|
||||
.ok()?
|
||||
};
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn target_name(service: &str, account: &str) -> String {
|
||||
format!("{}/{}", service, account)
|
||||
}
|
||||
|
||||
// Convert the internal WIN32 errors to descriptive messages
|
||||
fn convert_error(code: WIN32_ERROR) -> String {
|
||||
match code {
|
||||
ERROR_NOT_FOUND => String::from("Password not found."),
|
||||
_ => code.0.to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test() {
|
||||
scopeguard::defer!(delete_password("BitwardenTest", "BitwardenTest").unwrap_or({}););
|
||||
set_password("BitwardenTest", "BitwardenTest", "Random").unwrap();
|
||||
assert_eq!(
|
||||
"Random",
|
||||
get_password("BitwardenTest", "BitwardenTest").unwrap()
|
||||
);
|
||||
delete_password("BitwardenTest", "BitwardenTest").unwrap();
|
||||
|
||||
// Ensure password is deleted
|
||||
match get_password("BitwardenTest", "BitwardenTest") {
|
||||
Ok(_) => panic!("Got a result"),
|
||||
Err(e) => assert_eq!("Password not found.", e.to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_password_keytar() {
|
||||
scopeguard::defer!(delete_password("BitwardenTest", "BitwardenTest").unwrap_or({}););
|
||||
keytar::set_password("BitwardenTest", "BitwardenTest", "HelloFromKeytar").unwrap();
|
||||
assert_eq!(
|
||||
"HelloFromKeytar",
|
||||
get_password_keytar("BitwardenTest", "BitwardenTest").unwrap()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_error_no_password() {
|
||||
match get_password("BitwardenTest", "BitwardenTest") {
|
||||
Ok(_) => panic!("Got a result"),
|
||||
Err(e) => assert_eq!("Password not found.", e.to_string()),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,121 @@
|
|||
{
|
||||
"extraMetadata": { "name": "bitwarden" },
|
||||
"productName": "Bitwarden",
|
||||
"appId": "com.bitwarden.desktop",
|
||||
"buildDependenciesFromSource": true,
|
||||
"copyright": "Copyright © 2015-2022 Bitwarden Inc.",
|
||||
"directories": { "buildResources": "resources", "output": "dist", "app": "build" },
|
||||
"afterSign": "scripts/after-sign.js",
|
||||
"asarUnpack": ["**/*.node"],
|
||||
"files": ["**/*", "!**/node_modules/@bitwarden/desktop-native/**/*"],
|
||||
"publish": {
|
||||
"provider": "generic",
|
||||
"url": "https://artifacts.bitwarden.com/desktop"
|
||||
},
|
||||
"mac": {
|
||||
"electronUpdaterCompatibility": ">=0.0.1",
|
||||
"category": "public.app-category.productivity",
|
||||
"darkModeSupport": true,
|
||||
"gatekeeperAssess": false,
|
||||
"hardenedRuntime": true,
|
||||
"entitlements": "resources/entitlements.mac.plist",
|
||||
"entitlementsInherit": "resources/entitlements.mac.plist",
|
||||
"extendInfo": {
|
||||
"ITSAppUsesNonExemptEncryption": false,
|
||||
"CFBundleLocalizations": [
|
||||
"en",
|
||||
"cs",
|
||||
"da",
|
||||
"de",
|
||||
"es",
|
||||
"et",
|
||||
"fi",
|
||||
"fr",
|
||||
"hr",
|
||||
"hu",
|
||||
"id",
|
||||
"it",
|
||||
"ja",
|
||||
"nb",
|
||||
"nl",
|
||||
"pl",
|
||||
"pt-BR",
|
||||
"pt-PT",
|
||||
"ro",
|
||||
"ru",
|
||||
"sk",
|
||||
"sv",
|
||||
"tr",
|
||||
"uk",
|
||||
"vi",
|
||||
"zh-Hans",
|
||||
"zh-Hant"
|
||||
],
|
||||
"CFBundleDevelopmentRegion": "en"
|
||||
},
|
||||
"target": ["dmg", "zip"]
|
||||
},
|
||||
"win": {
|
||||
"electronUpdaterCompatibility": ">=0.0.1",
|
||||
"target": ["portable", "nsis-web", "appx"],
|
||||
"sign": "./sign.js",
|
||||
"extraResources": [
|
||||
{ "from": "node_modules/regedit/vbs", "to": "regedit/vbs", "filter": ["**/*"] },
|
||||
{ "from": "resources/native-messaging.bat", "to": "native-messaging.bat" }
|
||||
]
|
||||
},
|
||||
"linux": {
|
||||
"category": "Utility",
|
||||
"synopsis": "A secure and free password manager for all of your devices.",
|
||||
"target": ["deb", "freebsd", "rpm", "AppImage", "snap"],
|
||||
"desktop": { "Name": "Bitwarden", "Type": "Application", "GenericName": "Password Manager" }
|
||||
},
|
||||
"dmg": {
|
||||
"icon": "dmg.icns",
|
||||
"contents": [
|
||||
{ "x": 150, "y": 185, "type": "file" },
|
||||
{ "x": 390, "y": 180, "type": "link", "path": "/Applications" }
|
||||
],
|
||||
"window": { "width": 540, "height": 380 }
|
||||
},
|
||||
"mas": {
|
||||
"entitlements": "resources/entitlements.mas.plist",
|
||||
"entitlementsInherit": "resources/entitlements.mas.inherit.plist",
|
||||
"hardenedRuntime": false,
|
||||
"extendInfo": { "LSMinimumSystemVersion": "10.14.0" }
|
||||
},
|
||||
"nsisWeb": {
|
||||
"oneClick": false,
|
||||
"perMachine": false,
|
||||
"allowToChangeInstallationDirectory": false,
|
||||
"artifactName": "${productName}-Installer-${version}.${ext}",
|
||||
"uninstallDisplayName": "${productName}",
|
||||
"deleteAppDataOnUninstall": true
|
||||
},
|
||||
"portable": { "artifactName": "${productName}-Portable-${version}.${ext}" },
|
||||
"appx": {
|
||||
"artifactName": "${productName}-${version}-${arch}.${ext}",
|
||||
"backgroundColor": "#175DDC",
|
||||
"applicationId": "bitwardendesktop",
|
||||
"identityName": "8bitSolutionsLLC.bitwardendesktop",
|
||||
"publisher": "CN=14D52771-DE3C-4886-B8BF-825BA7690418",
|
||||
"publisherDisplayName": "8bit Solutions LLC",
|
||||
"languages": ["en-US"]
|
||||
},
|
||||
"deb": {
|
||||
"artifactName": "${productName}-${version}-${arch}.${ext}",
|
||||
"depends": ["libnotify4", "libxtst6", "libnss3", "libsecret-1-0", "libxss1"]
|
||||
},
|
||||
"appImage": {
|
||||
"artifactName": "${productName}-${version}-${arch}.${ext}"
|
||||
},
|
||||
"rpm": { "artifactName": "${productName}-${version}-${arch}.${ext}" },
|
||||
"freebsd": { "artifactName": "${productName}-${version}-${arch}.${ext}" },
|
||||
"snap": {
|
||||
"autoStart": true,
|
||||
"confinement": "strict",
|
||||
"plugs": ["default", "password-manager-service"],
|
||||
"stagePackages": ["default"]
|
||||
},
|
||||
"protocols": [{ "name": "Bitwarden", "schemes": ["bitwarden"] }]
|
||||
}
|
2
jslib
|
@ -1 +1 @@
|
|||
Subproject commit f4c66b2c8c243935bf25f689b16afaa5d6345f1b
|
||||
Subproject commit 80c834b52a8b00f88250a47a9bbd40c269fc2cba
|
274
package.json
|
@ -1,6 +1,5 @@
|
|||
{
|
||||
"name": "bitwarden",
|
||||
"productName": "Bitwarden",
|
||||
"name": "@bitwarden/desktop",
|
||||
"description": "A secure and free password manager for all of your devices.",
|
||||
"version": "0.0.0",
|
||||
"keywords": [
|
||||
|
@ -27,8 +26,8 @@
|
|||
"symlink:win": "rm -rf ./jslib && cmd /c mklink /J .\\jslib ..\\jslib",
|
||||
"symlink:mac": "npm run symlink:lin",
|
||||
"symlink:lin": "rm -rf ./jslib && ln -s ../jslib ./jslib",
|
||||
"lint": "tslint 'src/**/*.ts'",
|
||||
"lint:fix": "tslint 'src/**/*.ts' --fix",
|
||||
"lint": "eslint . && prettier --check .",
|
||||
"lint:fix": "eslint . --fix",
|
||||
"build": "concurrently -n Main,Rend -c yellow,cyan \"npm run build:main\" \"npm run build:renderer\"",
|
||||
"build:dev": "concurrently -n Main,Rend -c yellow,cyan \"npm run build:main:dev\" \"npm run build:renderer:dev\"",
|
||||
"build:main": "cross-env NODE_ENV=production webpack --config webpack.main.js",
|
||||
|
@ -37,6 +36,7 @@
|
|||
"build:renderer:dev": "cross-env NODE_ENV=development webpack --config webpack.renderer.js",
|
||||
"build:renderer:watch": "cross-env NODE_ENV=development webpack --config webpack.renderer.js --watch",
|
||||
"electron": "npm run build:main:dev && concurrently -k -n Main,Rend -c yellow,cyan \"electron --inspect=5858 ./build --watch\" \"npm run build:renderer:watch\"",
|
||||
"electron:ignore": "npm run build:main:dev && concurrently -k -n Main,Rend -c yellow,cyan \"electron --inspect=5858 --ignore-certificate-errors ./build --watch\" \"npm run build:renderer:watch\"",
|
||||
"clean:dist": "rimraf ./dist/*",
|
||||
"clean:l10n": "git push origin --delete l10n_master",
|
||||
"pack:dir": "npm run clean:dist && electron-builder --dir -p never",
|
||||
|
@ -58,241 +58,79 @@
|
|||
"publish:mac": "npm run build && npm run clean:dist && electron-builder --mac -p always",
|
||||
"publish:mac:mas": "npm run dist:mac:mas && npm run upload:mas",
|
||||
"publish:win": "npm run build && npm run clean:dist && electron-builder --win --x64 --arm64 --ia32 -p always -c.win.certificateSubjectName=\"8bit Solutions LLC\"",
|
||||
"upload:mas": "xcrun altool --upload-app --type osx --file \"$(find ./dist/mas-universal/Bitwarden*.pkg)\" --username $APPLE_ID_USERNAME --password $APPLE_ID_PASSWORD"
|
||||
},
|
||||
"build": {
|
||||
"appId": "com.bitwarden.desktop",
|
||||
"buildDependenciesFromSource": true,
|
||||
"copyright": "Copyright © 2015-2021 Bitwarden Inc.",
|
||||
"directories": {
|
||||
"buildResources": "resources",
|
||||
"output": "dist",
|
||||
"app": "build"
|
||||
},
|
||||
"afterSign": "scripts/after-sign.js",
|
||||
"mac": {
|
||||
"electronUpdaterCompatibility": ">=0.0.1",
|
||||
"category": "public.app-category.productivity",
|
||||
"darkModeSupport": true,
|
||||
"gatekeeperAssess": false,
|
||||
"hardenedRuntime": true,
|
||||
"entitlements": "resources/entitlements.mac.plist",
|
||||
"entitlementsInherit": "resources/entitlements.mac.plist",
|
||||
"extendInfo": {
|
||||
"ITSAppUsesNonExemptEncryption": false,
|
||||
"CFBundleLocalizations": [
|
||||
"en",
|
||||
"cs",
|
||||
"da",
|
||||
"de",
|
||||
"es",
|
||||
"et",
|
||||
"fi",
|
||||
"fr",
|
||||
"hr",
|
||||
"hu",
|
||||
"id",
|
||||
"it",
|
||||
"ja",
|
||||
"nb",
|
||||
"nl",
|
||||
"pl",
|
||||
"pt-BR",
|
||||
"pt-PT",
|
||||
"ro",
|
||||
"ru",
|
||||
"sk",
|
||||
"sv",
|
||||
"tr",
|
||||
"uk",
|
||||
"vi",
|
||||
"zh-Hans",
|
||||
"zh-Hant"
|
||||
],
|
||||
"CFBundleDevelopmentRegion": "en"
|
||||
},
|
||||
"target": [
|
||||
"dmg",
|
||||
"zip"
|
||||
]
|
||||
},
|
||||
"win": {
|
||||
"electronUpdaterCompatibility": ">=0.0.1",
|
||||
"target": [
|
||||
"portable",
|
||||
"nsis-web",
|
||||
"appx"
|
||||
],
|
||||
"sign": "./sign.js",
|
||||
"extraResources": [
|
||||
{
|
||||
"from": "node_modules/regedit/vbs",
|
||||
"to": "regedit/vbs",
|
||||
"filter": [
|
||||
"**/*"
|
||||
]
|
||||
},
|
||||
{
|
||||
"from": "resources/native-messaging.bat",
|
||||
"to": "native-messaging.bat"
|
||||
}
|
||||
]
|
||||
},
|
||||
"linux": {
|
||||
"category": "Utility",
|
||||
"synopsis": "A secure and free password manager for all of your devices.",
|
||||
"target": [
|
||||
"deb",
|
||||
"freebsd",
|
||||
"rpm",
|
||||
"AppImage",
|
||||
"snap"
|
||||
],
|
||||
"desktop": {
|
||||
"Name": "Bitwarden",
|
||||
"Type": "Application",
|
||||
"GenericName": "Password Manager"
|
||||
}
|
||||
},
|
||||
"dmg": {
|
||||
"icon": "dmg.icns",
|
||||
"contents": [
|
||||
{
|
||||
"x": 150,
|
||||
"y": 185,
|
||||
"type": "file"
|
||||
},
|
||||
{
|
||||
"x": 390,
|
||||
"y": 180,
|
||||
"type": "link",
|
||||
"path": "/Applications"
|
||||
}
|
||||
],
|
||||
"window": {
|
||||
"width": 540,
|
||||
"height": 380
|
||||
}
|
||||
},
|
||||
"mas": {
|
||||
"entitlements": "resources/entitlements.mas.plist",
|
||||
"entitlementsInherit": "resources/entitlements.mas.inherit.plist",
|
||||
"hardenedRuntime": false,
|
||||
"asarUnpack": [
|
||||
"node_modules/keytar"
|
||||
]
|
||||
},
|
||||
"nsisWeb": {
|
||||
"oneClick": false,
|
||||
"perMachine": true,
|
||||
"allowToChangeInstallationDirectory": true,
|
||||
"artifactName": "${productName}-Installer-${version}.${ext}",
|
||||
"uninstallDisplayName": "${productName}",
|
||||
"deleteAppDataOnUninstall": true
|
||||
},
|
||||
"portable": {
|
||||
"artifactName": "${productName}-Portable-${version}.${ext}"
|
||||
},
|
||||
"appx": {
|
||||
"artifactName": "${productName}-${version}-${arch}.${ext}",
|
||||
"backgroundColor": "#175DDC",
|
||||
"applicationId": "bitwardendesktop",
|
||||
"identityName": "8bitSolutionsLLC.bitwardendesktop",
|
||||
"publisher": "CN=14D52771-DE3C-4886-B8BF-825BA7690418",
|
||||
"publisherDisplayName": "8bit Solutions LLC",
|
||||
"languages": [
|
||||
"en-US"
|
||||
]
|
||||
},
|
||||
"deb": {
|
||||
"artifactName": "${productName}-${version}-${arch}.${ext}",
|
||||
"depends": [
|
||||
"libnotify4",
|
||||
"libxtst6",
|
||||
"libnss3",
|
||||
"libsecret-1-0",
|
||||
"libxss1"
|
||||
]
|
||||
},
|
||||
"appImage": {
|
||||
"artifactName": "${productName}-${version}-${arch}.${ext}"
|
||||
},
|
||||
"rpm": {
|
||||
"artifactName": "${productName}-${version}-${arch}.${ext}"
|
||||
},
|
||||
"freebsd": {
|
||||
"artifactName": "${productName}-${version}-${arch}.${ext}"
|
||||
},
|
||||
"snap": {
|
||||
"autoStart": true,
|
||||
"confinement": "strict",
|
||||
"plugs": [
|
||||
"default",
|
||||
"password-manager-service"
|
||||
],
|
||||
"stagePackages": [
|
||||
"default"
|
||||
],
|
||||
"publish": [
|
||||
"github"
|
||||
]
|
||||
},
|
||||
"protocols": [
|
||||
{
|
||||
"name": "Bitwarden",
|
||||
"schemes": [
|
||||
"bitwarden"
|
||||
]
|
||||
}
|
||||
]
|
||||
"publish:win:dev": "npm run build && npm run clean:dist && electron-builder --win --x64 --arm64 --ia32 -p always",
|
||||
"upload:mas": "xcrun altool --upload-app --type osx --file \"$(find ./dist/mas-universal/Bitwarden*.pkg)\" --username $APPLE_ID_USERNAME --password $APPLE_ID_PASSWORD",
|
||||
"prettier": "prettier --write .",
|
||||
"prepare": "husky install"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular/compiler-cli": "^11.2.10",
|
||||
"@ngtools/webpack": "^11.2.10",
|
||||
"@types/node": "^14.14.43",
|
||||
"@angular/compiler-cli": "^12.2.13",
|
||||
"@ngtools/webpack": "^12.2.13",
|
||||
"@types/node": "^16.11.12",
|
||||
"@types/node-ipc": "^9.1.4",
|
||||
"clean-webpack-plugin": "^3.0.0",
|
||||
"@typescript-eslint/eslint-plugin": "^5.12.1",
|
||||
"@typescript-eslint/parser": "^5.12.1",
|
||||
"clean-webpack-plugin": "^4.0.0",
|
||||
"concurrently": "^6.0.2",
|
||||
"copy-webpack-plugin": "^6.4.0",
|
||||
"copy-webpack-plugin": "^10.0.0",
|
||||
"cross-env": "^7.0.3",
|
||||
"css-loader": "^5.2.4",
|
||||
"del": "^6.0.0",
|
||||
"css-loader": "^6.5.1",
|
||||
"electron-builder": "22.11.7",
|
||||
"electron-notarize": "^1.1.1",
|
||||
"electron-rebuild": "^3.2.3",
|
||||
"electron-rebuild": "^3.2.5",
|
||||
"electron-reload": "^1.5.0",
|
||||
"file-loader": "^6.2.0",
|
||||
"font-awesome": "4.7.0",
|
||||
"html-loader": "^1.3.2",
|
||||
"html-webpack-plugin": "^4.5.1",
|
||||
"mini-css-extract-plugin": "^1.5.0",
|
||||
"node-loader": "^1.0.3",
|
||||
"eslint": "^8.9.0",
|
||||
"eslint-config-prettier": "^8.4.0",
|
||||
"eslint-import-resolver-typescript": "^2.5.0",
|
||||
"eslint-plugin-import": "^2.25.4",
|
||||
"html-loader": "^3.0.1",
|
||||
"html-webpack-plugin": "^5.5.0",
|
||||
"husky": "^7.0.4",
|
||||
"lint-staged": "^12.1.3",
|
||||
"mini-css-extract-plugin": "^2.4.5",
|
||||
"node-loader": "^2.0.0",
|
||||
"prettier": "^2.5.1",
|
||||
"rimraf": "^3.0.2",
|
||||
"sass": "^1.32.11",
|
||||
"sass-loader": "^10.1.1",
|
||||
"sass-loader": "^12.4.0",
|
||||
"tapable": "^1.1.3",
|
||||
"ts-loader": "^8.1.0",
|
||||
"ts-loader": "^9.2.5",
|
||||
"tsconfig-paths-webpack-plugin": "^3.5.1",
|
||||
"tslint": "~6.1.0",
|
||||
"tslint-loader": "^3.5.4",
|
||||
"typescript": "4.1.5",
|
||||
"webpack": "^4.46.0",
|
||||
"webpack-cli": "^4.6.0",
|
||||
"webpack-merge": "^5.7.3",
|
||||
"webpack-node-externals": "^3.0.0"
|
||||
"typescript": "4.3.5",
|
||||
"webpack": "^5.64.4",
|
||||
"webpack-cli": "^4.9.1",
|
||||
"webpack-merge": "^5.8.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@angular/animations": "^12.2.13",
|
||||
"@angular/cdk": "^12.2.13",
|
||||
"@angular/common": "^12.2.13",
|
||||
"@angular/compiler": "^12.2.13",
|
||||
"@angular/core": "^12.2.13",
|
||||
"@angular/forms": "^12.2.13",
|
||||
"@angular/platform-browser": "^12.2.13",
|
||||
"@angular/platform-browser-dynamic": "^12.2.13",
|
||||
"@angular/router": "^12.2.13",
|
||||
"@bitwarden/jslib-angular": "file:jslib/angular",
|
||||
"@bitwarden/jslib-common": "file:jslib/common",
|
||||
"@bitwarden/jslib-electron": "file:jslib/electron",
|
||||
"angular2-toaster": "^11.0.1",
|
||||
"@nodert-win10-rs4/windows.security.credentials.ui": "^0.4.4",
|
||||
"forcefocus": "^1.1.0",
|
||||
"keytar": "^7.9.0",
|
||||
"ngx-toastr": "14.1.4",
|
||||
"node-ipc": "^9.1.4",
|
||||
"nord": "^0.2.1",
|
||||
"regedit": "^3.0.3",
|
||||
"sweetalert2": "^10.16.6"
|
||||
"rxjs": "^7.4.0",
|
||||
"sweetalert2": "^10.16.6",
|
||||
"zone.js": "0.11.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": "~14",
|
||||
"npm": "~7"
|
||||
"node": "~16",
|
||||
"npm": "~8"
|
||||
},
|
||||
"lint-staged": {
|
||||
"./!(jslib)**": "prettier --ignore-unknown --write",
|
||||
"*.ts": "eslint --fix"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>com.apple.security.cs.allow-jit</key>
|
||||
<true/>
|
||||
<key>com.apple.security.cs.allow-unsigned-executable-memory</key>
|
||||
<true/>
|
||||
<key>com.apple.security.cs.disable-library-validation</key>
|
||||
|
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 9.4 KiB |
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 29 KiB |
Before Width: | Height: | Size: 717 B After Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 2.2 KiB |
Before Width: | Height: | Size: 33 KiB After Width: | Height: | Size: 29 KiB |
Before Width: | Height: | Size: 110 KiB After Width: | Height: | Size: 87 KiB |
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 2.2 KiB |
Before Width: | Height: | Size: 3.3 KiB After Width: | Height: | Size: 3.2 KiB |
Before Width: | Height: | Size: 110 KiB After Width: | Height: | Size: 87 KiB |
Before Width: | Height: | Size: 345 KiB After Width: | Height: | Size: 252 KiB |
After Width: | Height: | Size: 3.2 KiB |
After Width: | Height: | Size: 9.4 KiB |
After Width: | Height: | Size: 252 KiB |
Before Width: | Height: | Size: 4.7 KiB After Width: | Height: | Size: 9.4 KiB |
Before Width: | Height: | Size: 326 B After Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 8.6 KiB After Width: | Height: | Size: 29 KiB |
Before Width: | Height: | Size: 510 B After Width: | Height: | Size: 2.2 KiB |
Before Width: | Height: | Size: 707 B |
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 87 KiB |
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 3.2 KiB |
|
@ -1,53 +1,62 @@
|
|||
require('dotenv').config();
|
||||
const path = require('path');
|
||||
const fse = require('fs-extra');
|
||||
const { notarize } = require('electron-notarize');
|
||||
const { deepAssign } = require('builder-util');
|
||||
/* eslint-disable @typescript-eslint/no-var-requires, no-console */
|
||||
require("dotenv").config();
|
||||
const path = require("path");
|
||||
|
||||
const { deepAssign } = require("builder-util");
|
||||
const { notarize } = require("electron-notarize");
|
||||
const fse = require("fs-extra");
|
||||
|
||||
exports.default = run;
|
||||
|
||||
async function run(context) {
|
||||
console.log('## After sign');
|
||||
// console.log(context);
|
||||
console.log("## After sign");
|
||||
// console.log(context);
|
||||
|
||||
const appName = context.packager.appInfo.productFilename;
|
||||
const appPath = `${context.appOutDir}/${appName}.app`;
|
||||
const macBuild = context.electronPlatformName === 'darwin';
|
||||
const copyPlugIn = ['darwin', 'mas'].includes(context.electronPlatformName);
|
||||
const appName = context.packager.appInfo.productFilename;
|
||||
const appPath = `${context.appOutDir}/${appName}.app`;
|
||||
const macBuild = context.electronPlatformName === "darwin";
|
||||
const copyPlugIn = ["darwin", "mas"].includes(context.electronPlatformName);
|
||||
|
||||
if (copyPlugIn) {
|
||||
// Copy Safari plugin to work-around https://github.com/electron-userland/electron-builder/issues/5552
|
||||
const plugIn = path.join(__dirname, '../PlugIns');
|
||||
if (fse.existsSync(plugIn)) {
|
||||
fse.mkdirSync(path.join(appPath, 'Contents/PlugIns'));
|
||||
fse.copySync(path.join(plugIn, 'safari.appex'), path.join(appPath, 'Contents/PlugIns/safari.appex'));
|
||||
if (copyPlugIn) {
|
||||
// Copy Safari plugin to work-around https://github.com/electron-userland/electron-builder/issues/5552
|
||||
const plugIn = path.join(__dirname, "../PlugIns");
|
||||
if (fse.existsSync(plugIn)) {
|
||||
fse.mkdirSync(path.join(appPath, "Contents/PlugIns"));
|
||||
fse.copySync(
|
||||
path.join(plugIn, "safari.appex"),
|
||||
path.join(appPath, "Contents/PlugIns/safari.appex")
|
||||
);
|
||||
|
||||
// Resign to sign safari extension
|
||||
if (context.electronPlatformName === 'mas') {
|
||||
const masBuildOptions = deepAssign({}, context.packager.platformSpecificBuildOptions, context.packager.config.mas);
|
||||
if (context.targets.some(e => e.name === 'mas-dev')) {
|
||||
deepAssign(masBuildOptions, {
|
||||
type: 'development',
|
||||
});
|
||||
}
|
||||
if (context.packager.packagerOptions.prepackaged == null) {
|
||||
await context.packager.sign(appPath, context.appOutDir, masBuildOptions, context.arch);
|
||||
}
|
||||
} else {
|
||||
await context.packager.signApp(context, true);
|
||||
}
|
||||
// Resign to sign safari extension
|
||||
if (context.electronPlatformName === "mas") {
|
||||
const masBuildOptions = deepAssign(
|
||||
{},
|
||||
context.packager.platformSpecificBuildOptions,
|
||||
context.packager.config.mas
|
||||
);
|
||||
if (context.targets.some((e) => e.name === "mas-dev")) {
|
||||
deepAssign(masBuildOptions, {
|
||||
type: "development",
|
||||
});
|
||||
}
|
||||
if (context.packager.packagerOptions.prepackaged == null) {
|
||||
await context.packager.sign(appPath, context.appOutDir, masBuildOptions, context.arch);
|
||||
}
|
||||
} else {
|
||||
await context.packager.signApp(context, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (macBuild) {
|
||||
console.log('### Notarizing ' + appPath);
|
||||
const appleId = process.env.APPLE_ID_USERNAME || process.env.APPLEID;
|
||||
const appleIdPassword = process.env.APPLE_ID_PASSWORD || `@keychain:AC_PASSWORD`;
|
||||
return await notarize({
|
||||
appBundleId: 'com.bitwarden.desktop',
|
||||
appPath: appPath,
|
||||
appleId: appleId,
|
||||
appleIdPassword: appleIdPassword,
|
||||
});
|
||||
}
|
||||
if (macBuild) {
|
||||
console.log("### Notarizing " + appPath);
|
||||
const appleId = process.env.APPLE_ID_USERNAME || process.env.APPLEID;
|
||||
const appleIdPassword = process.env.APPLE_ID_PASSWORD || `@keychain:AC_PASSWORD`;
|
||||
return await notarize({
|
||||
appBundleId: "com.bitwarden.desktop",
|
||||
appPath: appPath,
|
||||
appleId: appleId,
|
||||
appleIdPassword: appleIdPassword,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
data
|
|
@ -0,0 +1,19 @@
|
|||
version: "3"
|
||||
|
||||
services:
|
||||
minio:
|
||||
image: minio/minio
|
||||
command: server /data --console-address ":9001"
|
||||
ports:
|
||||
- "9000:9000"
|
||||
- "9001:9001"
|
||||
# environment:
|
||||
# MINIO_ROOT_USER: minioadmin
|
||||
# MINIO_ROOT_PASSWORD: minioadmin
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:9000/minio/health/live"]
|
||||
interval: 30s
|
||||
timeout: 20s
|
||||
retries: 3
|
||||
volumes:
|
||||
- ./data:/data
|
31
sign.js
|
@ -1,22 +1,21 @@
|
|||
exports.default = async function(configuration) {
|
||||
if (
|
||||
parseInt(process.env.ELECTRON_BUILDER_SIGN) === 1 &&
|
||||
configuration.path.slice(-4) == ".exe"
|
||||
) {
|
||||
console.log(`[*] Signing file: ${configuration.path}`)
|
||||
/* eslint-disable @typescript-eslint/no-var-requires, no-console */
|
||||
|
||||
exports.default = async function (configuration) {
|
||||
if (parseInt(process.env.ELECTRON_BUILDER_SIGN) === 1 && configuration.path.slice(-4) == ".exe") {
|
||||
console.log(`[*] Signing file: ${configuration.path}`);
|
||||
require("child_process").execSync(
|
||||
`azuresigntool sign -v ` +
|
||||
`-kvu ${process.env.SIGNING_VAULT_URL} ` +
|
||||
`-kvi ${process.env.SIGNING_CLIENT_ID} ` +
|
||||
`-kvt ${process.env.SIGNING_TENANT_ID} ` +
|
||||
`-kvs ${process.env.SIGNING_CLIENT_SECRET} ` +
|
||||
`-kvc ${process.env.SIGNING_CERT_NAME} ` +
|
||||
`-fd ${configuration.hash} ` +
|
||||
`-du ${configuration.site} ` +
|
||||
`-tr http://timestamp.digicert.com ` +
|
||||
`${configuration.path}`,
|
||||
`-kvu ${process.env.SIGNING_VAULT_URL} ` +
|
||||
`-kvi ${process.env.SIGNING_CLIENT_ID} ` +
|
||||
`-kvt ${process.env.SIGNING_TENANT_ID} ` +
|
||||
`-kvs ${process.env.SIGNING_CLIENT_SECRET} ` +
|
||||
`-kvc ${process.env.SIGNING_CERT_NAME} ` +
|
||||
`-fd ${configuration.hash} ` +
|
||||
`-du ${configuration.site} ` +
|
||||
`-tr http://timestamp.digicert.com ` +
|
||||
`${configuration.path}`,
|
||||
{
|
||||
stdio: "inherit"
|
||||
stdio: "inherit",
|
||||
}
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,64 +1,93 @@
|
|||
<div class="modal fade" role="dialog" aria-modal="true" attr.aria-label="{{'settings' | i18n}}">
|
||||
<div class="modal-dialog" role="document">
|
||||
<form class="modal-content" (ngSubmit)="submit()">
|
||||
<div class="modal-body">
|
||||
<div class="box">
|
||||
<div class="box-header">
|
||||
{{'selfHostedEnvironment' | i18n}}
|
||||
</div>
|
||||
<div class="box-content">
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="baseUrl">{{'baseUrl' | i18n}}</label>
|
||||
<input id="baseUrl" type="text" name="BaseUrl" [(ngModel)]="baseUrl"
|
||||
placeholder="{{'ex' | i18n}} https://bitwarden.company.com">
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-footer">
|
||||
{{'selfHostedEnvironmentFooter' | i18n}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="box">
|
||||
<div class="box-header">
|
||||
<button type="button" (click)="toggleCustom()" appA11yTitle="{{'toggleVisibility' | i18n}}">
|
||||
<i class="fa fa-plus-square-o" [hidden]="showCustom" aria-hidden="true"></i>
|
||||
<i class="fa fa-minus-square-o" [hidden]="!showCustom" aria-hidden="true"></i>
|
||||
{{'customEnvironment' | i18n}}
|
||||
</button>
|
||||
</div>
|
||||
<div class="box-content" [hidden]="!showCustom">
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="webVaultUrl">{{'webVaultUrl' | i18n}}</label>
|
||||
<input id="webVaultUrl" type="text" name="WebVaultUrl" [(ngModel)]="webVaultUrl">
|
||||
</div>
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="apiUrl">{{'apiUrl' | i18n}}</label>
|
||||
<input id="apiUrl" type="text" name="ApiUrl" [(ngModel)]="apiUrl">
|
||||
</div>
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="identityUrl">{{'identityUrl' | i18n}}</label>
|
||||
<input id="identityUrl" type="text" name="IdentityUrl" [(ngModel)]="identityUrl">
|
||||
</div>
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="notificationsUrl">{{'notificationsUrl' | i18n}}</label>
|
||||
<input id="notificationsUrl" type="text" name="NotificationsUrl"
|
||||
[(ngModel)]="notificationsUrl">
|
||||
</div>
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="iconsUrl">{{'iconsUrl' | i18n}}</label>
|
||||
<input id="iconsUrl" type="text" name="IconsUrl" [(ngModel)]="iconsUrl">
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-footer" [hidden]="!showCustom">
|
||||
{{'customEnvironmentFooter' | i18n}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal fade" role="dialog" aria-modal="true" attr.aria-label="{{ 'settings' | i18n }}">
|
||||
<div class="modal-dialog" role="document">
|
||||
<form class="modal-content" (ngSubmit)="submit()">
|
||||
<div class="modal-body">
|
||||
<div class="box">
|
||||
<div class="box-header">
|
||||
{{ "selfHostedEnvironment" | i18n }}
|
||||
</div>
|
||||
<div class="box-content">
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="baseUrl">{{ "baseUrl" | i18n }}</label>
|
||||
<input
|
||||
id="baseUrl"
|
||||
type="text"
|
||||
name="BaseUrl"
|
||||
[(ngModel)]="baseUrl"
|
||||
placeholder="{{ 'ex' | i18n }} https://bitwarden.company.com"
|
||||
appInputVerbatim
|
||||
/>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button appBlurClick type="submit" class="primary" appA11yTitle="{{'save' | i18n}}">
|
||||
<i class="fa fa-save fa-lg fa-fw" aria-hidden="true"></i>
|
||||
</button>
|
||||
<button type="button" data-dismiss="modal">{{'close' | i18n}}</button>
|
||||
</div>
|
||||
<div class="box-footer">
|
||||
{{ "selfHostedEnvironmentFooter" | i18n }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="box">
|
||||
<div class="box-header">
|
||||
<button type="button" (click)="toggleCustom()" [attr.aria-expanded]="showCustom">
|
||||
<i class="bwi bwi-plus-square" [hidden]="showCustom" aria-hidden="true"></i>
|
||||
<i class="bwi bwi-minus-square" [hidden]="!showCustom" aria-hidden="true"></i>
|
||||
{{ "customEnvironment" | i18n }}
|
||||
</button>
|
||||
</div>
|
||||
<div class="box-content" [hidden]="!showCustom">
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="webVaultUrl">{{ "webVaultUrl" | i18n }}</label>
|
||||
<input
|
||||
id="webVaultUrl"
|
||||
type="text"
|
||||
name="WebVaultUrl"
|
||||
[(ngModel)]="webVaultUrl"
|
||||
appInputVerbatim
|
||||
/>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="apiUrl">{{ "apiUrl" | i18n }}</label>
|
||||
<input id="apiUrl" type="text" name="ApiUrl" [(ngModel)]="apiUrl" appInputVerbatim />
|
||||
</div>
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="identityUrl">{{ "identityUrl" | i18n }}</label>
|
||||
<input
|
||||
id="identityUrl"
|
||||
type="text"
|
||||
name="IdentityUrl"
|
||||
[(ngModel)]="identityUrl"
|
||||
appInputVerbatim
|
||||
/>
|
||||
</div>
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="notificationsUrl">{{ "notificationsUrl" | i18n }}</label>
|
||||
<input
|
||||
id="notificationsUrl"
|
||||
type="text"
|
||||
name="NotificationsUrl"
|
||||
[(ngModel)]="notificationsUrl"
|
||||
appInputVerbatim
|
||||
/>
|
||||
</div>
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="iconsUrl">{{ "iconsUrl" | i18n }}</label>
|
||||
<input
|
||||
id="iconsUrl"
|
||||
type="text"
|
||||
name="IconsUrl"
|
||||
[(ngModel)]="iconsUrl"
|
||||
appInputVerbatim
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-footer" [hidden]="!showCustom">
|
||||
{{ "customEnvironmentFooter" | i18n }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="submit" class="primary" appA11yTitle="{{ 'save' | i18n }}">
|
||||
<i class="bwi bwi-save-changes bwi-lg bwi-fw" aria-hidden="true"></i>
|
||||
</button>
|
||||
<button type="button" data-dismiss="modal">{{ "close" | i18n }}</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,18 +1,20 @@
|
|||
import { Component } from '@angular/core';
|
||||
import { Component } from "@angular/core";
|
||||
|
||||
import { EnvironmentService } from 'jslib-common/abstractions/environment.service';
|
||||
import { I18nService } from 'jslib-common/abstractions/i18n.service';
|
||||
import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service';
|
||||
|
||||
import { EnvironmentComponent as BaseEnvironmentComponent } from 'jslib-angular/components/environment.component';
|
||||
import { EnvironmentComponent as BaseEnvironmentComponent } from "jslib-angular/components/environment.component";
|
||||
import { EnvironmentService } from "jslib-common/abstractions/environment.service";
|
||||
import { I18nService } from "jslib-common/abstractions/i18n.service";
|
||||
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
|
||||
|
||||
@Component({
|
||||
selector: 'app-environment',
|
||||
templateUrl: 'environment.component.html',
|
||||
selector: "app-environment",
|
||||
templateUrl: "environment.component.html",
|
||||
})
|
||||
export class EnvironmentComponent extends BaseEnvironmentComponent {
|
||||
constructor(platformUtilsService: PlatformUtilsService, environmentService: EnvironmentService,
|
||||
i18nService: I18nService) {
|
||||
super(platformUtilsService, environmentService, i18nService);
|
||||
}
|
||||
constructor(
|
||||
platformUtilsService: PlatformUtilsService,
|
||||
environmentService: EnvironmentService,
|
||||
i18nService: I18nService
|
||||
) {
|
||||
super(platformUtilsService, environmentService, i18nService);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,23 +1,31 @@
|
|||
<form id="hint-page" #form (ngSubmit)="submit()" [appApiAction]="formPromise">
|
||||
<div class="content">
|
||||
<h1>{{'passwordHint' | i18n}}</h1>
|
||||
<div class="box last">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="email">{{'emailAddress' | i18n}}</label>
|
||||
<input id="email" type="text" name="Email" [(ngModel)]="email" required appAutofocus>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-footer">
|
||||
{{'enterEmailToGetHint' | i18n}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="buttons">
|
||||
<button type="submit" class="btn primary block" [disabled]="form.loading" appBlurClick>
|
||||
<b [hidden]="form.loading">{{'submit' | i18n}}</b>
|
||||
<i class="fa fa-spinner fa-spin" [hidden]="!form.loading" aria-hidden="true"></i>
|
||||
</button>
|
||||
<a routerLink="/login" class="btn block">{{'cancel' | i18n}}</a>
|
||||
<div class="content">
|
||||
<h1>{{ "passwordHint" | i18n }}</h1>
|
||||
<div class="box last">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="email">{{ "emailAddress" | i18n }}</label>
|
||||
<input
|
||||
id="email"
|
||||
type="text"
|
||||
name="Email"
|
||||
[(ngModel)]="email"
|
||||
required
|
||||
appAutofocus
|
||||
appInputVerbatim
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-footer">
|
||||
{{ "enterEmailToGetHint" | i18n }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="buttons">
|
||||
<button type="submit" class="btn primary block" [disabled]="form.loading">
|
||||
<b [hidden]="form.loading">{{ "submit" | i18n }}</b>
|
||||
<i class="bwi bwi-spinner bwi-spin" [hidden]="!form.loading" aria-hidden="true"></i>
|
||||
</button>
|
||||
<button type="button" routerLink="/login" class="btn block">{{ "cancel" | i18n }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
|
|
@ -1,20 +1,24 @@
|
|||
import { Component } from '@angular/core';
|
||||
import { Router } from '@angular/router';
|
||||
import { Component } 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 { LogService } from 'jslib-common/abstractions/log.service';
|
||||
import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service';
|
||||
|
||||
import { HintComponent as BaseHintComponent } from 'jslib-angular/components/hint.component';
|
||||
import { HintComponent as BaseHintComponent } from "jslib-angular/components/hint.component";
|
||||
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";
|
||||
|
||||
@Component({
|
||||
selector: 'app-hint',
|
||||
templateUrl: 'hint.component.html',
|
||||
selector: "app-hint",
|
||||
templateUrl: "hint.component.html",
|
||||
})
|
||||
export class HintComponent extends BaseHintComponent {
|
||||
constructor(router: Router, platformUtilsService: PlatformUtilsService,
|
||||
i18nService: I18nService, apiService: ApiService, logService: LogService) {
|
||||
super(router, i18nService, apiService, platformUtilsService, logService);
|
||||
}
|
||||
constructor(
|
||||
router: Router,
|
||||
platformUtilsService: PlatformUtilsService,
|
||||
i18nService: I18nService,
|
||||
apiService: ApiService,
|
||||
logService: LogService
|
||||
) {
|
||||
super(router, i18nService, apiService, platformUtilsService, logService);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,48 +1,75 @@
|
|||
<form id="lock-page" (ngSubmit)="submit()">
|
||||
<div class="content">
|
||||
<p aria-hidden="true"><i class="fa fa-lock fa-4x text-muted"></i></p>
|
||||
<p>{{'yourVaultIsLocked' | i18n}}</p>
|
||||
<div class="box last">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row box-content-row-flex" appBoxRow *ngIf="!hideInput">
|
||||
<div class="row-main" *ngIf="pinLock">
|
||||
<label for="pin">{{'pin' | i18n}}</label>
|
||||
<input id="pin" type="{{showPassword ? 'text' : 'password'}}" name="PIN" class="monospaced"
|
||||
[(ngModel)]="pin" required>
|
||||
</div>
|
||||
<div class="row-main" *ngIf="!pinLock">
|
||||
<label for="masterPassword">{{'masterPass' | i18n}}</label>
|
||||
<input id="masterPassword" type="{{showPassword ? 'text' : 'password'}}" name="MasterPassword"
|
||||
class="monospaced" [(ngModel)]="masterPassword" required>
|
||||
</div>
|
||||
<div class="action-buttons">
|
||||
<a class="row-btn" href="#" appStopClick appBlurClick role="button"
|
||||
appA11yTitle="{{'toggleVisibility' | i18n}}" (click)="togglePassword()">
|
||||
<i class="fa fa-lg" aria-hidden="true"
|
||||
[ngClass]="{'fa-eye': !showPassword, 'fa-eye-slash': showPassword}"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-footer">
|
||||
{{'loggedInAsOn' | i18n : email : webVaultHostname}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="buttons with-rows">
|
||||
<div class="buttons-row" *ngIf="supportsBiometric && biometricLock">
|
||||
<button type="button" class="btn block" [ngClass]="{'primary font-weight-bold': hideInput}"
|
||||
appBlurClick (click)="unlockBiometric()">
|
||||
{{biometricText | i18n}}
|
||||
</button>
|
||||
</div>
|
||||
<div class="buttons-row">
|
||||
<button type="submit" class="btn primary block" appBlurClick *ngIf="!hideInput">
|
||||
<i class="fa fa-unlock-alt" aria-hidden="true"></i> <b>{{'unlock' | i18n}}</b>
|
||||
</button>
|
||||
<button type="button" class="btn block" appBlurClick (click)="logOut()">
|
||||
{{'logOut' | i18n}}
|
||||
</button>
|
||||
</div>
|
||||
<div class="content">
|
||||
<p aria-hidden="true"><i class="bwi bwi-lock bwi-4x text-muted"></i></p>
|
||||
<p>{{ "yourVaultIsLocked" | i18n }}</p>
|
||||
<div class="box last">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row box-content-row-flex" appBoxRow *ngIf="!hideInput">
|
||||
<div class="row-main" *ngIf="pinLock">
|
||||
<label for="pin">{{ "pin" | i18n }}</label>
|
||||
<input
|
||||
id="pin"
|
||||
type="{{ showPassword ? 'text' : 'password' }}"
|
||||
name="PIN"
|
||||
class="monospaced"
|
||||
[(ngModel)]="pin"
|
||||
required
|
||||
appInputVerbatim
|
||||
/>
|
||||
</div>
|
||||
<div class="row-main" *ngIf="!pinLock">
|
||||
<label for="masterPassword">{{ "masterPass" | i18n }}</label>
|
||||
<input
|
||||
id="masterPassword"
|
||||
type="{{ showPassword ? 'text' : 'password' }}"
|
||||
name="MasterPassword"
|
||||
class="monospaced"
|
||||
[(ngModel)]="masterPassword"
|
||||
required
|
||||
appInputVerbatim
|
||||
/>
|
||||
</div>
|
||||
<div class="action-buttons">
|
||||
<button
|
||||
type="button"
|
||||
class="row-btn"
|
||||
appStopClick
|
||||
appA11yTitle="{{ 'toggleVisibility' | i18n }}"
|
||||
[attr.aria-pressed]="showPassword"
|
||||
(click)="togglePassword()"
|
||||
>
|
||||
<i
|
||||
class="bwi bwi-lg"
|
||||
aria-hidden="true"
|
||||
[ngClass]="{ 'bwi-eye': !showPassword, 'bwi-eye-slash': showPassword }"
|
||||
></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-footer">
|
||||
{{ "loggedInAsOn" | i18n: email:webVaultHostname }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="buttons with-rows">
|
||||
<div class="buttons-row" *ngIf="supportsBiometric && biometricLock">
|
||||
<button
|
||||
type="button"
|
||||
class="btn block"
|
||||
[ngClass]="{ 'primary font-weight-bold': hideInput }"
|
||||
(click)="unlockBiometric()"
|
||||
>
|
||||
{{ biometricText | i18n }}
|
||||
</button>
|
||||
</div>
|
||||
<div class="buttons-row">
|
||||
<button type="submit" class="btn primary block" *ngIf="!hideInput">
|
||||
<i class="bwi bwi-unlock" aria-hidden="true"></i> <b>{{ "unlock" | i18n }}</b>
|
||||
</button>
|
||||
<button type="button" class="btn block" (click)="logOut()">
|
||||
{{ "logOut" | i18n }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
|
|
@ -1,101 +1,107 @@
|
|||
import {
|
||||
Component,
|
||||
NgZone,
|
||||
OnDestroy,
|
||||
} from '@angular/core';
|
||||
import {
|
||||
ActivatedRoute,
|
||||
Router,
|
||||
} from '@angular/router';
|
||||
import { ipcRenderer } from 'electron';
|
||||
import { Component, NgZone, OnDestroy } from "@angular/core";
|
||||
import { ActivatedRoute, Router } from "@angular/router";
|
||||
import { ipcRenderer } from "electron";
|
||||
|
||||
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';
|
||||
import { StorageService } from 'jslib-common/abstractions/storage.service';
|
||||
import { UserService } from 'jslib-common/abstractions/user.service';
|
||||
import { VaultTimeoutService } from 'jslib-common/abstractions/vaultTimeout.service';
|
||||
import { LockComponent as BaseLockComponent } from "jslib-angular/components/lock.component";
|
||||
import { ApiService } from "jslib-common/abstractions/api.service";
|
||||
import { BroadcasterService } from "jslib-common/abstractions/broadcaster.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";
|
||||
import { VaultTimeoutService } from "jslib-common/abstractions/vaultTimeout.service";
|
||||
|
||||
import { BroadcasterService } from 'jslib-angular/services/broadcaster.service';
|
||||
|
||||
import { LockComponent as BaseLockComponent } from 'jslib-angular/components/lock.component';
|
||||
|
||||
import { ConstantsService } from 'jslib-common/services/constants.service';
|
||||
|
||||
const BroadcasterSubscriptionId = 'LockComponent';
|
||||
const BroadcasterSubscriptionId = "LockComponent";
|
||||
|
||||
@Component({
|
||||
selector: 'app-lock',
|
||||
templateUrl: 'lock.component.html',
|
||||
selector: "app-lock",
|
||||
templateUrl: "lock.component.html",
|
||||
})
|
||||
export class LockComponent extends BaseLockComponent implements OnDestroy {
|
||||
private deferFocus: boolean = null;
|
||||
private deferFocus: boolean = null;
|
||||
|
||||
constructor(router: Router, i18nService: I18nService,
|
||||
platformUtilsService: PlatformUtilsService, messagingService: MessagingService,
|
||||
userService: UserService, cryptoService: CryptoService,
|
||||
storageService: StorageService, vaultTimeoutService: VaultTimeoutService,
|
||||
environmentService: EnvironmentService, stateService: StateService,
|
||||
apiService: ApiService, private route: ActivatedRoute,
|
||||
private broadcasterService: BroadcasterService, private ngZone: NgZone,
|
||||
logService: LogService, keyConnectorService: KeyConnectorService) {
|
||||
super(router, i18nService, platformUtilsService, messagingService, userService, cryptoService,
|
||||
storageService, vaultTimeoutService, environmentService, stateService, apiService, logService,
|
||||
keyConnectorService);
|
||||
}
|
||||
constructor(
|
||||
router: Router,
|
||||
i18nService: I18nService,
|
||||
platformUtilsService: PlatformUtilsService,
|
||||
messagingService: MessagingService,
|
||||
cryptoService: CryptoService,
|
||||
vaultTimeoutService: VaultTimeoutService,
|
||||
environmentService: EnvironmentService,
|
||||
stateService: StateService,
|
||||
apiService: ApiService,
|
||||
private route: ActivatedRoute,
|
||||
private broadcasterService: BroadcasterService,
|
||||
ngZone: NgZone,
|
||||
logService: LogService,
|
||||
keyConnectorService: KeyConnectorService
|
||||
) {
|
||||
super(
|
||||
router,
|
||||
i18nService,
|
||||
platformUtilsService,
|
||||
messagingService,
|
||||
cryptoService,
|
||||
vaultTimeoutService,
|
||||
environmentService,
|
||||
stateService,
|
||||
apiService,
|
||||
logService,
|
||||
keyConnectorService,
|
||||
ngZone
|
||||
);
|
||||
}
|
||||
|
||||
async ngOnInit() {
|
||||
await super.ngOnInit();
|
||||
const autoPromptBiometric = !await this.storageService.get<boolean>(ConstantsService.disableAutoBiometricsPromptKey);
|
||||
async ngOnInit() {
|
||||
await super.ngOnInit();
|
||||
const autoPromptBiometric = !(await this.stateService.getNoAutoPromptBiometrics());
|
||||
|
||||
this.route.queryParams.subscribe(params => {
|
||||
if (this.supportsBiometric && params.promptBiometric && autoPromptBiometric) {
|
||||
setTimeout(async() => {
|
||||
if (await ipcRenderer.invoke('windowVisible')) {
|
||||
this.unlockBiometric();
|
||||
}
|
||||
}, 1000);
|
||||
this.route.queryParams.subscribe((params) => {
|
||||
if (this.supportsBiometric && params.promptBiometric && autoPromptBiometric) {
|
||||
setTimeout(async () => {
|
||||
if (await ipcRenderer.invoke("windowVisible")) {
|
||||
this.unlockBiometric();
|
||||
}
|
||||
}, 1000);
|
||||
}
|
||||
});
|
||||
this.broadcasterService.subscribe(BroadcasterSubscriptionId, async (message: any) => {
|
||||
this.ngZone.run(() => {
|
||||
switch (message.command) {
|
||||
case "windowHidden":
|
||||
this.onWindowHidden();
|
||||
break;
|
||||
case "windowIsFocused":
|
||||
if (this.deferFocus === null) {
|
||||
this.deferFocus = !message.windowIsFocused;
|
||||
if (!this.deferFocus) {
|
||||
this.focusInput();
|
||||
}
|
||||
} else if (this.deferFocus && message.windowIsFocused) {
|
||||
this.focusInput();
|
||||
this.deferFocus = false;
|
||||
}
|
||||
});
|
||||
this.broadcasterService.subscribe(BroadcasterSubscriptionId, async (message: any) => {
|
||||
this.ngZone.run(() => {
|
||||
switch (message.command) {
|
||||
case 'windowHidden':
|
||||
this.onWindowHidden();
|
||||
break;
|
||||
case 'windowIsFocused':
|
||||
if (this.deferFocus === null) {
|
||||
this.deferFocus = !message.windowIsFocused;
|
||||
if (!this.deferFocus) {
|
||||
this.focusInput();
|
||||
}
|
||||
} else if (this.deferFocus && message.windowIsFocused) {
|
||||
this.focusInput();
|
||||
this.deferFocus = false;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
}
|
||||
});
|
||||
});
|
||||
this.messagingService.send('getWindowIsFocused');
|
||||
}
|
||||
break;
|
||||
default:
|
||||
}
|
||||
});
|
||||
});
|
||||
this.messagingService.send("getWindowIsFocused");
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.broadcasterService.unsubscribe(BroadcasterSubscriptionId);
|
||||
}
|
||||
ngOnDestroy() {
|
||||
this.broadcasterService.unsubscribe(BroadcasterSubscriptionId);
|
||||
}
|
||||
|
||||
onWindowHidden() {
|
||||
this.showPassword = false;
|
||||
}
|
||||
onWindowHidden() {
|
||||
this.showPassword = false;
|
||||
}
|
||||
|
||||
private focusInput() {
|
||||
document.getElementById(this.pinLock ? 'pin' : 'masterPassword').focus();
|
||||
}
|
||||
private focusInput() {
|
||||
document.getElementById(this.pinLock ? "pin" : "masterPassword").focus();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,56 +1,104 @@
|
|||
<form id="login-page" #form (ngSubmit)="submit()" [appApiAction]="formPromise" attr.aria-hidden="{{showingModal}}">
|
||||
<div id="login-page">
|
||||
<div class="login-header">
|
||||
<button
|
||||
type="button"
|
||||
appStopClick
|
||||
(click)="settings()"
|
||||
class="environment-urls-settings-icon"
|
||||
attr.aria-label="{{ 'settings' | i18n }}"
|
||||
>
|
||||
<i class="bwi bwi-cog bwi-lg" aria-hidden="true"></i>
|
||||
{{ "settings" | i18n }}
|
||||
</button>
|
||||
</div>
|
||||
<form
|
||||
id="login-page"
|
||||
#form
|
||||
(ngSubmit)="submit()"
|
||||
[appApiAction]="formPromise"
|
||||
attr.aria-hidden="{{ showingModal }}"
|
||||
>
|
||||
<div id="content" class="content">
|
||||
<img class="logo-image" alt="Bitwarden">
|
||||
<p class="lead">{{'loginOrCreateNewAccount' | i18n}}</p>
|
||||
<div class="box last">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="email">{{'emailAddress' | i18n}}</label>
|
||||
<input id="email" type="text" name="Email" [(ngModel)]="email" required>
|
||||
</div>
|
||||
<div class="box-content-row box-content-row-flex" appBoxRow>
|
||||
<div class="row-main">
|
||||
<label for="masterPassword">{{'masterPass' | i18n}}</label>
|
||||
<input id="masterPassword" type="{{showPassword ? 'text' : 'password'}}" name="MasterPassword"
|
||||
class="monospaced" [(ngModel)]="masterPassword" required>
|
||||
</div>
|
||||
<div class="action-buttons">
|
||||
<a class="row-btn" href="#" appStopClick appBlurClick role="button"
|
||||
appA11yTitle="{{'toggleVisibility' | i18n}}" (click)="togglePassword()">
|
||||
<i class="fa fa-lg" aria-hidden="true"
|
||||
[ngClass]="{'fa-eye': !showPassword, 'fa-eye-slash': showPassword}"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-content-row" [hidden]="!showCaptcha()">
|
||||
<iframe id="hcaptcha_iframe" height="80"></iframe>
|
||||
</div>
|
||||
<img class="logo-image" alt="Bitwarden" />
|
||||
<p class="lead">{{ "loginOrCreateNewAccount" | i18n }}</p>
|
||||
<div class="box last">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="email">{{ "emailAddress" | i18n }}</label>
|
||||
<input
|
||||
id="email"
|
||||
type="text"
|
||||
name="Email"
|
||||
[(ngModel)]="email"
|
||||
required
|
||||
appInputVerbatim="false"
|
||||
/>
|
||||
</div>
|
||||
<div class="box-content-row box-content-row-flex" appBoxRow>
|
||||
<div class="row-main">
|
||||
<label for="masterPassword">{{ "masterPass" | i18n }}</label>
|
||||
<input
|
||||
id="masterPassword"
|
||||
type="{{ showPassword ? 'text' : 'password' }}"
|
||||
name="MasterPassword"
|
||||
class="monospaced"
|
||||
[(ngModel)]="masterPassword"
|
||||
required
|
||||
appInputVerbatim
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="buttons with-rows">
|
||||
<div class="buttons-row">
|
||||
<button type="submit" class="btn primary block" [disabled]="form.loading" appBlurClick>
|
||||
<b [hidden]="form.loading"><i class="fa fa-sign-in" aria-hidden="true"></i> {{'logIn' | i18n}}</b>
|
||||
<i class="fa fa-spinner fa-spin" [hidden]="!form.loading" aria-hidden="true"></i>
|
||||
</button>
|
||||
<a routerLink="/register" class="btn block">
|
||||
<i class="fa fa-pencil-square-o" aria-hidden="true"></i> {{'createAccount' | i18n}}
|
||||
</a>
|
||||
</div>
|
||||
<div class="buttons-row">
|
||||
<a (click)="launchSsoBrowser('desktop', 'bitwarden://sso-callback')" class="btn block">
|
||||
<i class="fa fa-bank" aria-hidden="true"></i> {{'enterpriseSingleSignOn' | i18n}}
|
||||
</a>
|
||||
<div class="action-buttons">
|
||||
<button
|
||||
type="button"
|
||||
class="row-btn"
|
||||
appStopClick
|
||||
appA11yTitle="{{ 'toggleVisibility' | i18n }}"
|
||||
[attr.aria-pressed]="showPassword"
|
||||
(click)="togglePassword()"
|
||||
>
|
||||
<i
|
||||
class="bwi bwi-lg"
|
||||
aria-hidden="true"
|
||||
[ngClass]="{ 'bwi-eye': !showPassword, 'bwi-eye-slash': showPassword }"
|
||||
></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sub-options">
|
||||
<a routerLink="/hint">{{'getMasterPasswordHint' | i18n}}</a>
|
||||
</div>
|
||||
<div class="box last" [hidden]="!showCaptcha()">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row">
|
||||
<iframe id="hcaptcha_iframe" height="80"></iframe>
|
||||
</div>
|
||||
</div>
|
||||
<a href="#" appStopClick (click)="settings()" class="settings-icon" attr.aria-label="{{'settings' | i18n}}">
|
||||
<i class="fa fa-cog fa-lg" aria-hidden="true"></i><span
|
||||
aria-hidden="true"> {{'settings' | i18n}}</span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="buttons with-rows">
|
||||
<div class="buttons-row">
|
||||
<button type="submit" class="btn primary block" [disabled]="form.loading">
|
||||
<b [hidden]="form.loading"
|
||||
><i class="bwi bwi-sign-in" aria-hidden="true"></i> {{ "logIn" | i18n }}</b
|
||||
>
|
||||
<i class="bwi bwi-spinner bwi-spin" [hidden]="!form.loading" aria-hidden="true"></i>
|
||||
</button>
|
||||
<button type="button" routerLink="/register" class="btn block">
|
||||
<i class="bwi bwi-pencil-square" aria-hidden="true"></i> {{ "createAccount" | i18n }}
|
||||
</button>
|
||||
</div>
|
||||
<div class="buttons-row">
|
||||
<button
|
||||
type="button"
|
||||
(click)="launchSsoBrowser('desktop', 'bitwarden://sso-callback')"
|
||||
class="btn block"
|
||||
>
|
||||
<i class="bwi bwi-bank" aria-hidden="true"></i> {{ "enterpriseSingleSignOn" | i18n }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sub-options">
|
||||
<button type="button" routerLink="/hint">{{ "getMasterPasswordHint" | i18n }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</form>
|
||||
</div>
|
||||
<ng-template #environment></ng-template>
|
||||
|
|
|
@ -1,113 +1,128 @@
|
|||
import {
|
||||
Component,
|
||||
NgZone,
|
||||
OnDestroy,
|
||||
ViewChild,
|
||||
ViewContainerRef,
|
||||
} from '@angular/core';
|
||||
import { Component, NgZone, OnDestroy, ViewChild, ViewContainerRef } from "@angular/core";
|
||||
import { Router } from "@angular/router";
|
||||
|
||||
import { Router } from '@angular/router';
|
||||
import { LoginComponent as BaseLoginComponent } from "jslib-angular/components/login.component";
|
||||
import { ModalService } from "jslib-angular/services/modal.service";
|
||||
import { AuthService } from "jslib-common/abstractions/auth.service";
|
||||
import { BroadcasterService } from "jslib-common/abstractions/broadcaster.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 { MessagingService } from "jslib-common/abstractions/messaging.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";
|
||||
import { SyncService } from "jslib-common/abstractions/sync.service";
|
||||
|
||||
import { EnvironmentComponent } from './environment.component';
|
||||
import { EnvironmentComponent } from "./environment.component";
|
||||
|
||||
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 { MessagingService } from 'jslib-common/abstractions/messaging.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';
|
||||
import { StorageService } from 'jslib-common/abstractions/storage.service';
|
||||
import { SyncService } from 'jslib-common/abstractions/sync.service';
|
||||
|
||||
import { BroadcasterService } from 'jslib-angular/services/broadcaster.service';
|
||||
import { ModalService } from 'jslib-angular/services/modal.service';
|
||||
|
||||
import { LoginComponent as BaseLoginComponent } from 'jslib-angular/components/login.component';
|
||||
|
||||
const BroadcasterSubscriptionId = 'LoginComponent';
|
||||
const BroadcasterSubscriptionId = "LoginComponent";
|
||||
|
||||
@Component({
|
||||
selector: 'app-login',
|
||||
templateUrl: 'login.component.html',
|
||||
selector: "app-login",
|
||||
templateUrl: "login.component.html",
|
||||
})
|
||||
export class LoginComponent extends BaseLoginComponent implements OnDestroy {
|
||||
@ViewChild('environment', { read: ViewContainerRef, static: true }) environmentModal: ViewContainerRef;
|
||||
@ViewChild("environment", { read: ViewContainerRef, static: true })
|
||||
environmentModal: ViewContainerRef;
|
||||
|
||||
showingModal = false;
|
||||
showingModal = false;
|
||||
|
||||
private deferFocus: boolean = null;
|
||||
protected alwaysRememberEmail = true;
|
||||
|
||||
constructor(authService: AuthService, router: Router, i18nService: I18nService,
|
||||
syncService: SyncService, private modalService: ModalService,
|
||||
platformUtilsService: PlatformUtilsService, stateService: StateService,
|
||||
environmentService: EnvironmentService, passwordGenerationService: PasswordGenerationService,
|
||||
cryptoFunctionService: CryptoFunctionService, storageService: StorageService,
|
||||
private broadcasterService: BroadcasterService, private ngZone: NgZone,
|
||||
private messagingService: MessagingService, logService: LogService) {
|
||||
super(authService, router, platformUtilsService, i18nService, stateService, environmentService,
|
||||
passwordGenerationService, cryptoFunctionService, storageService, logService);
|
||||
super.onSuccessfulLogin = () => {
|
||||
return syncService.fullSync(true);
|
||||
};
|
||||
}
|
||||
private deferFocus: boolean = null;
|
||||
|
||||
async ngOnInit() {
|
||||
await super.ngOnInit();
|
||||
this.broadcasterService.subscribe(BroadcasterSubscriptionId, async (message: any) => {
|
||||
this.ngZone.run(() => {
|
||||
switch (message.command) {
|
||||
case 'windowHidden':
|
||||
this.onWindowHidden();
|
||||
break;
|
||||
case 'windowIsFocused':
|
||||
if (this.deferFocus === null) {
|
||||
this.deferFocus = !message.windowIsFocused;
|
||||
if (!this.deferFocus) {
|
||||
this.focusInput();
|
||||
}
|
||||
} else if (this.deferFocus && message.windowIsFocused) {
|
||||
this.focusInput();
|
||||
this.deferFocus = false;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
}
|
||||
});
|
||||
});
|
||||
this.messagingService.send('getWindowIsFocused');
|
||||
}
|
||||
constructor(
|
||||
authService: AuthService,
|
||||
router: Router,
|
||||
i18nService: I18nService,
|
||||
syncService: SyncService,
|
||||
private modalService: ModalService,
|
||||
platformUtilsService: PlatformUtilsService,
|
||||
stateService: StateService,
|
||||
environmentService: EnvironmentService,
|
||||
passwordGenerationService: PasswordGenerationService,
|
||||
cryptoFunctionService: CryptoFunctionService,
|
||||
private broadcasterService: BroadcasterService,
|
||||
ngZone: NgZone,
|
||||
private messagingService: MessagingService,
|
||||
logService: LogService
|
||||
) {
|
||||
super(
|
||||
authService,
|
||||
router,
|
||||
platformUtilsService,
|
||||
i18nService,
|
||||
stateService,
|
||||
environmentService,
|
||||
passwordGenerationService,
|
||||
cryptoFunctionService,
|
||||
logService,
|
||||
ngZone
|
||||
);
|
||||
super.onSuccessfulLogin = () => {
|
||||
return syncService.fullSync(true);
|
||||
};
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.broadcasterService.unsubscribe(BroadcasterSubscriptionId);
|
||||
}
|
||||
|
||||
async settings() {
|
||||
const [modal, childComponent] = await this.modalService.openViewRef(EnvironmentComponent, this.environmentModal);
|
||||
|
||||
modal.onShown.subscribe(() => {
|
||||
this.showingModal = true;
|
||||
});
|
||||
modal.onClosed.subscribe(() => {
|
||||
this.showingModal = false;
|
||||
});
|
||||
|
||||
childComponent.onSaved.subscribe(() => {
|
||||
modal.close();
|
||||
});
|
||||
}
|
||||
|
||||
onWindowHidden() {
|
||||
this.showPassword = false;
|
||||
}
|
||||
|
||||
async submit() {
|
||||
await super.submit();
|
||||
if (this.captchaSiteKey) {
|
||||
const content = document.getElementById('content') as HTMLDivElement;
|
||||
content.setAttribute('style', 'width:335px');
|
||||
async ngOnInit() {
|
||||
await super.ngOnInit();
|
||||
this.broadcasterService.subscribe(BroadcasterSubscriptionId, async (message: any) => {
|
||||
this.ngZone.run(() => {
|
||||
switch (message.command) {
|
||||
case "windowHidden":
|
||||
this.onWindowHidden();
|
||||
break;
|
||||
case "windowIsFocused":
|
||||
if (this.deferFocus === null) {
|
||||
this.deferFocus = !message.windowIsFocused;
|
||||
if (!this.deferFocus) {
|
||||
this.focusInput();
|
||||
}
|
||||
} else if (this.deferFocus && message.windowIsFocused) {
|
||||
this.focusInput();
|
||||
this.deferFocus = false;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
}
|
||||
});
|
||||
});
|
||||
this.messagingService.send("getWindowIsFocused");
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.broadcasterService.unsubscribe(BroadcasterSubscriptionId);
|
||||
}
|
||||
|
||||
async settings() {
|
||||
const [modal, childComponent] = await this.modalService.openViewRef(
|
||||
EnvironmentComponent,
|
||||
this.environmentModal
|
||||
);
|
||||
|
||||
modal.onShown.subscribe(() => {
|
||||
this.showingModal = true;
|
||||
});
|
||||
modal.onClosed.subscribe(() => {
|
||||
this.showingModal = false;
|
||||
});
|
||||
|
||||
childComponent.onSaved.subscribe(() => {
|
||||
modal.close();
|
||||
});
|
||||
}
|
||||
|
||||
onWindowHidden() {
|
||||
this.showPassword = false;
|
||||
}
|
||||
|
||||
async submit() {
|
||||
await super.submit();
|
||||
if (this.captchaSiteKey) {
|
||||
const content = document.getElementById("content") as HTMLDivElement;
|
||||
content.setAttribute("style", "width:335px");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,70 +1,89 @@
|
|||
<div class="modal fade" role="dialog" aria-modal="true" aria-labelledby="premiumTitle">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-body">
|
||||
<div class="box">
|
||||
<div class="box-header" id="premiumTitle">
|
||||
{{'premiumMembership' | i18n}}
|
||||
</div>
|
||||
<div class="box-content box-content-padded">
|
||||
<div *ngIf="!isPremium">
|
||||
<p class="text-center lead">{{'premiumNotCurrentMember' | i18n}}</p>
|
||||
<p>{{'premiumSignUpAndGet' | i18n}}</p>
|
||||
<ul class="fa-ul">
|
||||
<li>
|
||||
<i class="fa-li fa fa-check text-success" aria-hidden="true"></i>
|
||||
{{'premiumSignUpStorage' | i18n}}
|
||||
</li>
|
||||
<li>
|
||||
<i class="fa-li fa fa-check text-success" aria-hidden="true"></i>
|
||||
{{'premiumSignUpTwoStep' | i18n}}
|
||||
</li>
|
||||
<li>
|
||||
<i class="fa-li fa fa-check text-success" aria-hidden="true"></i>
|
||||
{{'premiumSignUpReports' | i18n}}
|
||||
</li>
|
||||
<li>
|
||||
<i class="fa-li fa fa-check text-success" aria-hidden="true"></i>
|
||||
{{'premiumSignUpTotp' | i18n}}
|
||||
</li>
|
||||
<li>
|
||||
<i class="fa-li fa fa-check text-success" aria-hidden="true"></i>
|
||||
{{'premiumSignUpSupport' | i18n}}
|
||||
</li>
|
||||
<li>
|
||||
<i class="fa-li fa fa-check text-success" aria-hidden="true"></i>
|
||||
{{'premiumSignUpFuture' | i18n}}
|
||||
</li>
|
||||
</ul>
|
||||
<p class="text-center lead no-margin">
|
||||
{{'premiumPrice' | i18n : (price | currency:'$')}}
|
||||
</p>
|
||||
</div>
|
||||
<div *ngIf="isPremium">
|
||||
<p class="text-center lead">{{'premiumCurrentMember' | i18n}}</p>
|
||||
<p class="text-center">{{'premiumCurrentMemberThanks' | i18n}}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-body">
|
||||
<div class="box">
|
||||
<div class="box-header" id="premiumTitle">
|
||||
{{ "premiumMembership" | i18n }}
|
||||
</div>
|
||||
<div class="box-content box-content-padded">
|
||||
<div *ngIf="!isPremium">
|
||||
<p class="text-center lead">{{ "premiumNotCurrentMember" | i18n }}</p>
|
||||
<p>{{ "premiumSignUpAndGet" | i18n }}</p>
|
||||
<ul class="bwi-ul">
|
||||
<li>
|
||||
<i class="bwi bwi-li bwi-check text-success" aria-hidden="true"></i>
|
||||
{{ "premiumSignUpStorage" | i18n }}
|
||||
</li>
|
||||
<li>
|
||||
<i class="bwi bwi-li bwi-check text-success" aria-hidden="true"></i>
|
||||
{{ "premiumSignUpTwoStep" | i18n }}
|
||||
</li>
|
||||
<li>
|
||||
<i class="bwi bwi-li bwi-check text-success" aria-hidden="true"></i>
|
||||
{{ "premiumSignUpReports" | i18n }}
|
||||
</li>
|
||||
<li>
|
||||
<i class="bwi bwi-li bwi-check text-success" aria-hidden="true"></i>
|
||||
{{ "premiumSignUpTotp" | i18n }}
|
||||
</li>
|
||||
<li>
|
||||
<i class="bwi bwi-li bwi-check text-success" aria-hidden="true"></i>
|
||||
{{ "premiumSignUpSupport" | i18n }}
|
||||
</li>
|
||||
<li>
|
||||
<i class="bwi bwi-li bwi-check text-success" aria-hidden="true"></i>
|
||||
{{ "premiumSignUpFuture" | i18n }}
|
||||
</li>
|
||||
</ul>
|
||||
<p class="text-center lead no-margin">
|
||||
{{ "premiumPrice" | i18n: (price | currency: "$") }}
|
||||
</p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="primary" appBlurClick (click)="manage()" *ngIf="isPremium">
|
||||
<b>{{'premiumManage' | i18n}}</b>
|
||||
</button>
|
||||
<button #purchaseBtn type="button" class="primary" appBlurClick (click)="purchase()" *ngIf="!isPremium"
|
||||
[disabled]="purchaseBtn.loading">
|
||||
<b>{{'premiumPurchase' | i18n}}</b>
|
||||
</button>
|
||||
<button type="button" data-dismiss="modal">{{'close' | i18n}}</button>
|
||||
<div class="right" *ngIf="!isPremium">
|
||||
<button #refreshBtn type="button" appBlurClick (click)="refresh()" [disabled]="refreshBtn.loading"
|
||||
appA11yTitle="{{'premiumRefresh' | i18n}}" [appApiAction]="refreshPromise">
|
||||
<i class="fa fa-refresh fa-lg fa-fw" [hidden]="refreshBtn.loading" aria-hidden="true"></i>
|
||||
<i class="fa fa-spinner fa-spin fa-lg fa-fw" [hidden]="!refreshBtn.loading"
|
||||
aria-hidden="true"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div *ngIf="isPremium">
|
||||
<p class="text-center lead">{{ "premiumCurrentMember" | i18n }}</p>
|
||||
<p class="text-center">{{ "premiumCurrentMemberThanks" | i18n }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="primary" (click)="manage()" *ngIf="isPremium">
|
||||
<b>{{ "premiumManage" | i18n }}</b>
|
||||
</button>
|
||||
<button
|
||||
#purchaseBtn
|
||||
type="button"
|
||||
class="primary"
|
||||
(click)="purchase()"
|
||||
*ngIf="!isPremium"
|
||||
[disabled]="purchaseBtn.loading"
|
||||
>
|
||||
<b>{{ "premiumPurchase" | i18n }}</b>
|
||||
</button>
|
||||
<button type="button" data-dismiss="modal">{{ "close" | i18n }}</button>
|
||||
<div class="right" *ngIf="!isPremium">
|
||||
<button
|
||||
#refreshBtn
|
||||
type="button"
|
||||
(click)="refresh()"
|
||||
[disabled]="refreshBtn.loading"
|
||||
appA11yTitle="{{ 'premiumRefresh' | i18n }}"
|
||||
[appApiAction]="refreshPromise"
|
||||
>
|
||||
<i
|
||||
class="bwi bwi-refresh bwi-lg bwi-fw"
|
||||
[hidden]="refreshBtn.loading"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
<i
|
||||
class="bwi bwi-spinner bwi-spin bwi-lg bwi-fw"
|
||||
[hidden]="!refreshBtn.loading"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,21 +1,24 @@
|
|||
import { Component } from '@angular/core';
|
||||
import { Component } 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 { UserService } from 'jslib-common/abstractions/user.service';
|
||||
|
||||
import { PremiumComponent as BasePremiumComponent } from 'jslib-angular/components/premium.component';
|
||||
import { PremiumComponent as BasePremiumComponent } from "jslib-angular/components/premium.component";
|
||||
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 { StateService } from "jslib-common/abstractions/state.service";
|
||||
|
||||
@Component({
|
||||
selector: 'app-premium',
|
||||
templateUrl: 'premium.component.html',
|
||||
selector: "app-premium",
|
||||
templateUrl: "premium.component.html",
|
||||
})
|
||||
export class PremiumComponent extends BasePremiumComponent {
|
||||
constructor(i18nService: I18nService, platformUtilsService: PlatformUtilsService,
|
||||
apiService: ApiService, userService: UserService,
|
||||
logService: LogService) {
|
||||
super(i18nService, platformUtilsService, apiService, userService, logService);
|
||||
}
|
||||
constructor(
|
||||
i18nService: I18nService,
|
||||
platformUtilsService: PlatformUtilsService,
|
||||
apiService: ApiService,
|
||||
logService: LogService,
|
||||
stateService: StateService
|
||||
) {
|
||||
super(i18nService, platformUtilsService, apiService, logService, stateService);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,92 +1,148 @@
|
|||
<form id="register-page" #form (ngSubmit)="submit()" [appApiAction]="formPromise">
|
||||
<div class="content">
|
||||
<h1>{{'createAccount' | i18n}}</h1>
|
||||
<div class="box">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="email">{{'emailAddress' | i18n}}</label>
|
||||
<input id="email" type="text" name="Email" [(ngModel)]="email" required
|
||||
[appAutofocus]="email === ''">
|
||||
</div>
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<div class="box-content-row-flex">
|
||||
<div class="row-main">
|
||||
<label for="masterPassword">
|
||||
{{'masterPass' | i18n}}
|
||||
<strong class="sub-label text-{{masterPasswordScoreColor}}"
|
||||
*ngIf="masterPasswordScoreText">
|
||||
{{masterPasswordScoreText}}
|
||||
</strong>
|
||||
</label>
|
||||
<input id="masterPassword" type="{{showPassword ? 'text' : 'password'}}"
|
||||
name="MasterPassword" class="monospaced" [(ngModel)]="masterPassword" required
|
||||
[appAutofocus]="email !== ''" (input)="updatePasswordStrength()">
|
||||
</div>
|
||||
<div class="action-buttons">
|
||||
<a class="row-btn" href="#" appStopClick appBlurClick role="button"
|
||||
appA11yTitle="{{'toggleVisibility' | i18n}}" (click)="togglePassword(false)">
|
||||
<i class="fa fa-lg" aria-hidden="true"
|
||||
[ngClass]="{'fa-eye': !showPassword, 'fa-eye-slash': showPassword}"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="progress">
|
||||
<div class="progress-bar bg-{{masterPasswordScoreColor}}" role="progressbar" aria-valuenow="0"
|
||||
aria-valuemin="0" aria-valuemax="100" [ngStyle]="{width: (masterPasswordScoreWidth + '%')}"
|
||||
attr.aria-valuenow="{{masterPasswordScoreWidth}}"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-footer">
|
||||
{{'masterPassDesc' | i18n}}
|
||||
</div>
|
||||
<div class="content">
|
||||
<h1>{{ "createAccount" | i18n }}</h1>
|
||||
<div class="box">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="email">{{ "emailAddress" | i18n }}</label>
|
||||
<input
|
||||
id="email"
|
||||
type="text"
|
||||
name="Email"
|
||||
[(ngModel)]="email"
|
||||
required
|
||||
[appAutofocus]="email === ''"
|
||||
appInputVerbatim
|
||||
/>
|
||||
</div>
|
||||
<div class="box">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row box-content-row-flex" appBoxRow>
|
||||
<div class="row-main">
|
||||
<label for="masterPasswordRetype">{{'reTypeMasterPass' | i18n}}</label>
|
||||
<input id="masterPasswordRetype" type="{{showPassword ? 'text' : 'password'}}"
|
||||
name="MasterPasswordRetype" class="monospaced" [(ngModel)]="confirmMasterPassword" required>
|
||||
</div>
|
||||
<div class="action-buttons">
|
||||
<a class="row-btn" href="#" appStopClick appBlurClick role="button"
|
||||
appA11yTitle="{{'toggleVisibility' | i18n}}" (click)="togglePassword(true)">
|
||||
<i class="fa fa-lg" aria-hidden="true"
|
||||
[ngClass]="{'fa-eye': !showPassword, 'fa-eye-slash': showPassword}"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="hint">{{'masterPassHint' | i18n}}</label>
|
||||
<input id="hint" type="text" name="Hint" [(ngModel)]="hint">
|
||||
</div>
|
||||
<div class="box-content-row" [hidden]="!showCaptcha()">
|
||||
<iframe id="hcaptcha_iframe" height="80"></iframe>
|
||||
</div>
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<div class="box-content-row-flex">
|
||||
<div class="row-main">
|
||||
<label for="masterPassword">
|
||||
{{ "masterPass" | i18n }}
|
||||
<strong
|
||||
class="sub-label text-{{ masterPasswordScoreColor }}"
|
||||
*ngIf="masterPasswordScoreText"
|
||||
>
|
||||
{{ masterPasswordScoreText }}
|
||||
</strong>
|
||||
</label>
|
||||
<input
|
||||
id="masterPassword"
|
||||
type="{{ showPassword ? 'text' : 'password' }}"
|
||||
name="MasterPassword"
|
||||
class="monospaced"
|
||||
[(ngModel)]="masterPassword"
|
||||
required
|
||||
[appAutofocus]="email !== ''"
|
||||
(input)="updatePasswordStrength()"
|
||||
appInputVerbatim
|
||||
/>
|
||||
</div>
|
||||
<div class="box-footer">
|
||||
{{'masterPassHintDesc' | i18n}}
|
||||
<div class="action-buttons">
|
||||
<button
|
||||
type="button"
|
||||
class="row-btn"
|
||||
appStopClick
|
||||
appA11yTitle="{{ 'toggleVisibility' | i18n }}"
|
||||
[attr.aria-pressed]="showPassword"
|
||||
(click)="togglePassword(false)"
|
||||
>
|
||||
<i
|
||||
class="bwi bwi-lg"
|
||||
aria-hidden="true"
|
||||
[ngClass]="{ 'bwi-eye': !showPassword, 'bwi-eye-slash': showPassword }"
|
||||
></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="progress">
|
||||
<div
|
||||
class="progress-bar bg-{{ masterPasswordScoreColor }}"
|
||||
role="progressbar"
|
||||
aria-valuenow="0"
|
||||
aria-valuemin="0"
|
||||
aria-valuemax="100"
|
||||
[ngStyle]="{ width: masterPasswordScoreWidth + '%' }"
|
||||
attr.aria-valuenow="{{ masterPasswordScoreWidth }}"
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box last" *ngIf="showTerms">
|
||||
<div class="box-footer checkbox">
|
||||
<input type="checkbox" id="acceptPolicies" [(ngModel)]="acceptPolicies" name="AcceptPolicies">
|
||||
<label for="acceptPolicies">
|
||||
{{'acceptPolicies' | i18n}}<br>
|
||||
<a href="https://bitwarden.com/terms/" target="_blank"
|
||||
rel="noopener">{{'termsOfService' | i18n}}</a>,
|
||||
<a href="https://bitwarden.com/privacy/" target="_blank"
|
||||
rel="noopener">{{'privacyPolicy' | i18n}}</a>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="buttons">
|
||||
<button type="submit" class="btn primary block" [disabled]="form.loading" appBlurClick>
|
||||
<b [hidden]="form.loading">{{'submit' | i18n}}</b>
|
||||
<i class="fa fa-spinner fa-spin" [hidden]="!form.loading" aria-hidden="true"></i>
|
||||
</button>
|
||||
<a routerLink="/login" class="btn block">{{'cancel' | i18n}}</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-footer">
|
||||
{{ "masterPassDesc" | i18n }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="box">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row box-content-row-flex" appBoxRow>
|
||||
<div class="row-main">
|
||||
<label for="masterPasswordRetype">{{ "reTypeMasterPass" | i18n }}</label>
|
||||
<input
|
||||
id="masterPasswordRetype"
|
||||
type="{{ showPassword ? 'text' : 'password' }}"
|
||||
name="MasterPasswordRetype"
|
||||
class="monospaced"
|
||||
[(ngModel)]="confirmMasterPassword"
|
||||
required
|
||||
appInputVerbatim
|
||||
/>
|
||||
</div>
|
||||
<div class="action-buttons">
|
||||
<button
|
||||
type="button"
|
||||
class="row-btn"
|
||||
appStopClick
|
||||
appA11yTitle="{{ 'toggleVisibility' | i18n }}"
|
||||
[attr.aria-pressed]="showPassword"
|
||||
(click)="togglePassword(true)"
|
||||
>
|
||||
<i
|
||||
class="bwi bwi-lg"
|
||||
aria-hidden="true"
|
||||
[ngClass]="{ 'bwi-eye': !showPassword, 'bwi-eye-slash': showPassword }"
|
||||
></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="hint">{{ "masterPassHint" | i18n }}</label>
|
||||
<input id="hint" type="text" name="Hint" [(ngModel)]="hint" />
|
||||
</div>
|
||||
<div class="box-content-row" [hidden]="!showCaptcha()">
|
||||
<iframe id="hcaptcha_iframe" height="80"></iframe>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-footer">
|
||||
{{ "masterPassHintDesc" | i18n }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="box last" *ngIf="showTerms">
|
||||
<div class="box-footer checkbox">
|
||||
<input
|
||||
type="checkbox"
|
||||
id="acceptPolicies"
|
||||
[(ngModel)]="acceptPolicies"
|
||||
name="AcceptPolicies"
|
||||
/>
|
||||
<label for="acceptPolicies">
|
||||
{{ "acceptPolicies" | i18n }}<br />
|
||||
<a href="https://bitwarden.com/terms/" target="_blank" rel="noopener">{{
|
||||
"termsOfService" | i18n
|
||||
}}</a
|
||||
>,
|
||||
<a href="https://bitwarden.com/privacy/" target="_blank" rel="noopener">{{
|
||||
"privacyPolicy" | i18n
|
||||
}}</a>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="buttons">
|
||||
<button type="submit" class="btn primary block" [disabled]="form.loading">
|
||||
<b [hidden]="form.loading">{{ "submit" | i18n }}</b>
|
||||
<i class="bwi bwi-spinner bwi-spin" [hidden]="!form.loading" aria-hidden="true"></i>
|
||||
</button>
|
||||
<button type="button" routerLink="/login" class="btn block">{{ "cancel" | i18n }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
|
|
@ -1,62 +1,73 @@
|
|||
import {
|
||||
Component,
|
||||
NgZone,
|
||||
OnDestroy,
|
||||
OnInit,
|
||||
} from '@angular/core';
|
||||
import { Router } from '@angular/router';
|
||||
import { Component, NgZone, OnDestroy, OnInit } from "@angular/core";
|
||||
import { Router } from "@angular/router";
|
||||
|
||||
import { ApiService } from 'jslib-common/abstractions/api.service';
|
||||
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';
|
||||
import { RegisterComponent as BaseRegisterComponent } from "jslib-angular/components/register.component";
|
||||
import { ApiService } from "jslib-common/abstractions/api.service";
|
||||
import { AuthService } from "jslib-common/abstractions/auth.service";
|
||||
import { BroadcasterService } from "jslib-common/abstractions/broadcaster.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";
|
||||
|
||||
import { BroadcasterService } from 'jslib-angular/services/broadcaster.service';
|
||||
|
||||
import { RegisterComponent as BaseRegisterComponent } from 'jslib-angular/components/register.component';
|
||||
|
||||
const BroadcasterSubscriptionId = 'RegisterComponent';
|
||||
const BroadcasterSubscriptionId = "RegisterComponent";
|
||||
|
||||
@Component({
|
||||
selector: 'app-register',
|
||||
templateUrl: 'register.component.html',
|
||||
selector: "app-register",
|
||||
templateUrl: "register.component.html",
|
||||
})
|
||||
export class RegisterComponent extends BaseRegisterComponent implements OnInit, OnDestroy {
|
||||
constructor(authService: AuthService, router: Router,
|
||||
i18nService: I18nService, cryptoService: CryptoService,
|
||||
apiService: ApiService, stateService: StateService,
|
||||
platformUtilsService: PlatformUtilsService, passwordGenerationService: PasswordGenerationService,
|
||||
environmentService: EnvironmentService, private broadcasterService: BroadcasterService,
|
||||
private ngZone: NgZone, logService: LogService) {
|
||||
super(authService, router, i18nService, cryptoService, apiService, stateService, platformUtilsService,
|
||||
passwordGenerationService, environmentService, logService);
|
||||
}
|
||||
constructor(
|
||||
authService: AuthService,
|
||||
router: Router,
|
||||
i18nService: I18nService,
|
||||
cryptoService: CryptoService,
|
||||
apiService: ApiService,
|
||||
stateService: StateService,
|
||||
platformUtilsService: PlatformUtilsService,
|
||||
passwordGenerationService: PasswordGenerationService,
|
||||
environmentService: EnvironmentService,
|
||||
private broadcasterService: BroadcasterService,
|
||||
private ngZone: NgZone,
|
||||
logService: LogService
|
||||
) {
|
||||
super(
|
||||
authService,
|
||||
router,
|
||||
i18nService,
|
||||
cryptoService,
|
||||
apiService,
|
||||
stateService,
|
||||
platformUtilsService,
|
||||
passwordGenerationService,
|
||||
environmentService,
|
||||
logService
|
||||
);
|
||||
}
|
||||
|
||||
async ngOnInit() {
|
||||
this.broadcasterService.subscribe(BroadcasterSubscriptionId, async (message: any) => {
|
||||
this.ngZone.run(() => {
|
||||
switch (message.command) {
|
||||
case 'windowHidden':
|
||||
this.onWindowHidden();
|
||||
break;
|
||||
default:
|
||||
}
|
||||
});
|
||||
});
|
||||
async ngOnInit() {
|
||||
this.broadcasterService.subscribe(BroadcasterSubscriptionId, async (message: any) => {
|
||||
this.ngZone.run(() => {
|
||||
switch (message.command) {
|
||||
case "windowHidden":
|
||||
this.onWindowHidden();
|
||||
break;
|
||||
default:
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
super.ngOnInit();
|
||||
}
|
||||
super.ngOnInit();
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.broadcasterService.unsubscribe(BroadcasterSubscriptionId);
|
||||
}
|
||||
ngOnDestroy() {
|
||||
this.broadcasterService.unsubscribe(BroadcasterSubscriptionId);
|
||||
}
|
||||
|
||||
onWindowHidden() {
|
||||
this.showPassword = false;
|
||||
}
|
||||
onWindowHidden() {
|
||||
this.showPassword = false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,18 +1,26 @@
|
|||
<div id="remove-password-page" *ngIf="!loading">
|
||||
<div class="content">
|
||||
<h1>{{'removeMasterPassword' | i18n}}</h1>
|
||||
<p>{{'convertOrganizationEncryptionDesc' | i18n : organization.name}}</p>
|
||||
<div class="buttons">
|
||||
<button type="submit" class="btn primary block" [disabled]="actionPromise" appBlurClick
|
||||
(click)="convert()">
|
||||
<b [hidden]="continuing">{{'removeMasterPassword' | i18n}}</b>
|
||||
<i class="fa fa-spinner fa-spin" [hidden]="!continuing" aria-hidden="true"></i>
|
||||
</button>
|
||||
<button type="button" class="btn secondary block" [disabled]="actionPromise" appBlurClick
|
||||
(click)="leave()">
|
||||
<b [hidden]="leaving">{{'leaveOrganization' | i18n}}</b>
|
||||
<i class="fa fa-spinner fa-spin" [hidden]="!leaving" aria-hidden="true"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="content">
|
||||
<h1>{{ "removeMasterPassword" | i18n }}</h1>
|
||||
<p>{{ "convertOrganizationEncryptionDesc" | i18n: organization.name }}</p>
|
||||
<div class="buttons">
|
||||
<button
|
||||
type="submit"
|
||||
class="btn primary block"
|
||||
[disabled]="actionPromise"
|
||||
(click)="convert()"
|
||||
>
|
||||
<b [hidden]="continuing">{{ "removeMasterPassword" | i18n }}</b>
|
||||
<i class="bwi bwi-spinner bwi-spin" [hidden]="!continuing" aria-hidden="true"></i>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="btn secondary block"
|
||||
[disabled]="actionPromise"
|
||||
(click)="leave()"
|
||||
>
|
||||
<b [hidden]="leaving">{{ "leaveOrganization" | i18n }}</b>
|
||||
<i class="bwi bwi-spinner bwi-spin" [hidden]="!leaving" aria-hidden="true"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
import { Component } from '@angular/core';
|
||||
import { Component } from "@angular/core";
|
||||
|
||||
import { RemovePasswordComponent as BaseRemovePasswordComponent } from 'jslib-angular/components/remove-password.component';
|
||||
import { RemovePasswordComponent as BaseRemovePasswordComponent } from "jslib-angular/components/remove-password.component";
|
||||
|
||||
@Component({
|
||||
selector: 'app-remove-password',
|
||||
templateUrl: 'remove-password.component.html',
|
||||
selector: "app-remove-password",
|
||||
templateUrl: "remove-password.component.html",
|
||||
})
|
||||
export class RemovePasswordComponent extends BaseRemovePasswordComponent {
|
||||
}
|
||||
export class RemovePasswordComponent extends BaseRemovePasswordComponent {}
|
||||
|
|
|
@ -1,101 +1,157 @@
|
|||
<form id="set-password-page" #form>
|
||||
<div class="content">
|
||||
<img class="logo-image" alt="Bitwarden">
|
||||
<p class="lead">{{'setMasterPassword' | i18n}}</p>
|
||||
<div class="box text-center" *ngIf="syncLoading">
|
||||
<i class="fa fa-spinner fa-spin" title="{{'loading' | i18n}}" aria-hidden="true"></i>
|
||||
{{'loading' | i18n}}
|
||||
</div>
|
||||
<div *ngIf="!syncLoading">
|
||||
<div class="box">
|
||||
<app-callout type="tip">{{'ssoCompleteRegistration' | i18n}}</app-callout>
|
||||
<app-callout type="warning" title="{{'resetPasswordPolicyAutoEnroll' | i18n}}"
|
||||
*ngIf="resetPasswordAutoEnroll">
|
||||
{{'resetPasswordAutoEnrollInviteWarning' | i18n}}
|
||||
</app-callout>
|
||||
<app-callout type="info" [enforcedPolicyOptions]="enforcedPolicyOptions" *ngIf="enforcedPolicyOptions">
|
||||
</app-callout>
|
||||
</div>
|
||||
<form #form (ngSubmit)="submit()" [appApiAction]="formPromise" ngNativeValidate autocomplete="off">
|
||||
<div class="box">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<div class="box-content-row-flex">
|
||||
<div class="row-main">
|
||||
<label for="masterPassword">{{'masterPass' | i18n}}
|
||||
<strong class="sub-label text-{{masterPasswordScoreColor}}"
|
||||
*ngIf="masterPasswordScoreText">
|
||||
{{masterPasswordScoreText}}
|
||||
</strong>
|
||||
</label>
|
||||
<input id="masterPassword" type="{{showPassword ? 'text' : 'password'}}"
|
||||
name="MasterPassword" class="monospaced" [(ngModel)]="masterPassword" required
|
||||
(input)="updatePasswordStrength()" appInputVerbatim>
|
||||
</div>
|
||||
<div class="action-buttons">
|
||||
<a class="row-btn" href="#" appStopClick appBlurClick role="button"
|
||||
appA11yTitle="{{'toggleVisibility' | i18n}}" (click)="togglePassword(false)">
|
||||
<i class="fa fa-lg" aria-hidden="true"
|
||||
[ngClass]="{'fa-eye': !showPassword, 'fa-eye-slash': showPassword}"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="progress">
|
||||
<div class="progress-bar bg-{{masterPasswordScoreColor}}" role="progressbar"
|
||||
aria-valuenow="0" aria-valuemin="0" aria-valuemax="100"
|
||||
[ngStyle]="{width: (masterPasswordScoreWidth + '%')}"
|
||||
attr.aria-valuenow="{{masterPasswordScoreWidth}}">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-footer">
|
||||
{{'masterPassDesc' | i18n}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="box">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<div class="box-content-row-flex">
|
||||
<div class="row-main">
|
||||
<label for="masterPasswordRetype">{{'reTypeMasterPass' | i18n}}</label>
|
||||
<input id="masterPasswordRetype" type="password" name="MasterPasswordRetype"
|
||||
class="monospaced" [(ngModel)]="masterPasswordRetype" required appInputVerbatim
|
||||
autocomplete="new-password">
|
||||
</div>
|
||||
<div class="action-buttons">
|
||||
<a class="row-btn" href="#" appStopClick appBlurClick role="button"
|
||||
appA11yTitle="{{'toggleVisibility' | i18n}}" (click)="togglePassword(true)">
|
||||
<i class="fa fa-lg" aria-hidden="true"
|
||||
[ngClass]="{'fa-eye': !showPassword, 'fa-eye-slash': showPassword}"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box last">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="hint">{{'masterPassHint' | i18n}}</label>
|
||||
<input id="hint" type="text" name="Hint" [(ngModel)]="hint">
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-footer">
|
||||
{{'masterPassHintDesc' | i18n}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="buttons">
|
||||
<button type="submit" class="btn primary block" [disabled]="form.loading">
|
||||
<i *ngIf="form.loading" class="fa fa-spinner fa-spin" title="{{'loading' | i18n}}"
|
||||
aria-hidden="true"></i>
|
||||
<span>{{'submit' | i18n}}</span>
|
||||
</button>
|
||||
<button class="btn block" (click)="logOut()">
|
||||
<span>{{'logOut' | i18n}}</span>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="content">
|
||||
<img class="logo-image" alt="Bitwarden" />
|
||||
<p class="lead">{{ "setMasterPassword" | i18n }}</p>
|
||||
<div class="box text-center" *ngIf="syncLoading">
|
||||
<i class="bwi bwi-spinner bwi-spin" title="{{ 'loading' | i18n }}" aria-hidden="true"></i>
|
||||
{{ "loading" | i18n }}
|
||||
</div>
|
||||
<div *ngIf="!syncLoading">
|
||||
<div class="box">
|
||||
<app-callout type="tip">{{ "ssoCompleteRegistration" | i18n }}</app-callout>
|
||||
<app-callout
|
||||
type="warning"
|
||||
title="{{ 'resetPasswordPolicyAutoEnroll' | i18n }}"
|
||||
*ngIf="resetPasswordAutoEnroll"
|
||||
>
|
||||
{{ "resetPasswordAutoEnrollInviteWarning" | i18n }}
|
||||
</app-callout>
|
||||
<app-callout
|
||||
type="info"
|
||||
[enforcedPolicyOptions]="enforcedPolicyOptions"
|
||||
*ngIf="enforcedPolicyOptions"
|
||||
>
|
||||
</app-callout>
|
||||
</div>
|
||||
<form
|
||||
#form
|
||||
(ngSubmit)="submit()"
|
||||
[appApiAction]="formPromise"
|
||||
ngNativeValidate
|
||||
autocomplete="off"
|
||||
>
|
||||
<div class="box">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<div class="box-content-row-flex">
|
||||
<div class="row-main">
|
||||
<label for="masterPassword"
|
||||
>{{ "masterPass" | i18n }}
|
||||
<strong
|
||||
class="sub-label text-{{ masterPasswordScoreColor }}"
|
||||
*ngIf="masterPasswordScoreText"
|
||||
>
|
||||
{{ masterPasswordScoreText }}
|
||||
</strong>
|
||||
</label>
|
||||
<input
|
||||
id="masterPassword"
|
||||
type="{{ showPassword ? 'text' : 'password' }}"
|
||||
name="MasterPassword"
|
||||
class="monospaced"
|
||||
[(ngModel)]="masterPassword"
|
||||
required
|
||||
(input)="updatePasswordStrength()"
|
||||
appInputVerbatim
|
||||
/>
|
||||
</div>
|
||||
<div class="action-buttons">
|
||||
<button
|
||||
type="button"
|
||||
class="row-btn"
|
||||
appStopClick
|
||||
appA11yTitle="{{ 'toggleVisibility' | i18n }}"
|
||||
[attr.aria-pressed]="showPassword"
|
||||
(click)="togglePassword(false)"
|
||||
>
|
||||
<i
|
||||
class="bwi bwi-lg"
|
||||
aria-hidden="true"
|
||||
[ngClass]="{ 'bwi-eye': !showPassword, 'bwi-eye-slash': showPassword }"
|
||||
></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="progress">
|
||||
<div
|
||||
class="progress-bar bg-{{ masterPasswordScoreColor }}"
|
||||
role="progressbar"
|
||||
aria-valuenow="0"
|
||||
aria-valuemin="0"
|
||||
aria-valuemax="100"
|
||||
[ngStyle]="{ width: masterPasswordScoreWidth + '%' }"
|
||||
attr.aria-valuenow="{{ masterPasswordScoreWidth }}"
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-footer">
|
||||
{{ "masterPassDesc" | i18n }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="box">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<div class="box-content-row-flex">
|
||||
<div class="row-main">
|
||||
<label for="masterPasswordRetype">{{ "reTypeMasterPass" | i18n }}</label>
|
||||
<input
|
||||
id="masterPasswordRetype"
|
||||
type="password"
|
||||
name="MasterPasswordRetype"
|
||||
class="monospaced"
|
||||
[(ngModel)]="masterPasswordRetype"
|
||||
required
|
||||
appInputVerbatim
|
||||
autocomplete="new-password"
|
||||
/>
|
||||
</div>
|
||||
<div class="action-buttons">
|
||||
<button
|
||||
type="button"
|
||||
class="row-btn"
|
||||
appStopClick
|
||||
appA11yTitle="{{ 'toggleVisibility' | i18n }}"
|
||||
[attr.aria-pressed]="showPassword"
|
||||
(click)="togglePassword(true)"
|
||||
>
|
||||
<i
|
||||
class="bwi bwi-lg"
|
||||
aria-hidden="true"
|
||||
[ngClass]="{ 'bwi-eye': !showPassword, 'bwi-eye-slash': showPassword }"
|
||||
></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box last">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="hint">{{ "masterPassHint" | i18n }}</label>
|
||||
<input id="hint" type="text" name="Hint" [(ngModel)]="hint" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-footer">
|
||||
{{ "masterPassHintDesc" | i18n }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="buttons">
|
||||
<button type="submit" class="btn primary block" [disabled]="form.loading">
|
||||
<i
|
||||
*ngIf="form.loading"
|
||||
class="bwi bwi-spinner bwi-spin"
|
||||
title="{{ 'loading' | i18n }}"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
<span>{{ "submit" | i18n }}</span>
|
||||
</button>
|
||||
<button class="btn block" (click)="logOut()">
|
||||
<span>{{ "logOut" | i18n }}</span>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
|
|
@ -1,96 +1,104 @@
|
|||
import {
|
||||
Component,
|
||||
NgZone,
|
||||
OnDestroy,
|
||||
} from '@angular/core';
|
||||
import { Component, NgZone, OnDestroy } from "@angular/core";
|
||||
import { ActivatedRoute, Router } from "@angular/router";
|
||||
|
||||
import {
|
||||
ActivatedRoute,
|
||||
Router,
|
||||
} from '@angular/router';
|
||||
import { SetPasswordComponent as BaseSetPasswordComponent } from "jslib-angular/components/set-password.component";
|
||||
import { ApiService } from "jslib-common/abstractions/api.service";
|
||||
import { BroadcasterService } from "jslib-common/abstractions/broadcaster.service";
|
||||
import { CryptoService } from "jslib-common/abstractions/crypto.service";
|
||||
import { I18nService } from "jslib-common/abstractions/i18n.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 { StateService } from "jslib-common/abstractions/state.service";
|
||||
import { SyncService } from "jslib-common/abstractions/sync.service";
|
||||
|
||||
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 { 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 { BroadcasterService } from 'jslib-angular/services/broadcaster.service';
|
||||
|
||||
const BroadcasterSubscriptionId = 'SetPasswordComponent';
|
||||
|
||||
import {
|
||||
SetPasswordComponent as BaseSetPasswordComponent,
|
||||
} from 'jslib-angular/components/set-password.component';
|
||||
const BroadcasterSubscriptionId = "SetPasswordComponent";
|
||||
|
||||
@Component({
|
||||
selector: 'app-set-password',
|
||||
templateUrl: 'set-password.component.html',
|
||||
selector: "app-set-password",
|
||||
templateUrl: "set-password.component.html",
|
||||
})
|
||||
export class SetPasswordComponent extends BaseSetPasswordComponent implements OnDestroy {
|
||||
constructor(apiService: ApiService, i18nService: I18nService,
|
||||
cryptoService: CryptoService, messagingService: MessagingService,
|
||||
userService: UserService, passwordGenerationService: PasswordGenerationService,
|
||||
platformUtilsService: PlatformUtilsService, policyService: PolicyService, router: Router,
|
||||
syncService: SyncService, route: ActivatedRoute,
|
||||
private broadcasterService: BroadcasterService, private ngZone: NgZone) {
|
||||
super(i18nService, cryptoService, messagingService, userService, passwordGenerationService,
|
||||
platformUtilsService, policyService, router, apiService, syncService, route);
|
||||
}
|
||||
constructor(
|
||||
apiService: ApiService,
|
||||
i18nService: I18nService,
|
||||
cryptoService: CryptoService,
|
||||
messagingService: MessagingService,
|
||||
passwordGenerationService: PasswordGenerationService,
|
||||
platformUtilsService: PlatformUtilsService,
|
||||
policyService: PolicyService,
|
||||
router: Router,
|
||||
syncService: SyncService,
|
||||
route: ActivatedRoute,
|
||||
private broadcasterService: BroadcasterService,
|
||||
private ngZone: NgZone,
|
||||
stateService: StateService
|
||||
) {
|
||||
super(
|
||||
i18nService,
|
||||
cryptoService,
|
||||
messagingService,
|
||||
passwordGenerationService,
|
||||
platformUtilsService,
|
||||
policyService,
|
||||
router,
|
||||
apiService,
|
||||
syncService,
|
||||
route,
|
||||
stateService
|
||||
);
|
||||
}
|
||||
|
||||
get masterPasswordScoreWidth() {
|
||||
return this.masterPasswordScore == null ? 0 : (this.masterPasswordScore + 1) * 20;
|
||||
}
|
||||
get masterPasswordScoreWidth() {
|
||||
return this.masterPasswordScore == null ? 0 : (this.masterPasswordScore + 1) * 20;
|
||||
}
|
||||
|
||||
get masterPasswordScoreColor() {
|
||||
switch (this.masterPasswordScore) {
|
||||
case 4:
|
||||
return 'success';
|
||||
case 3:
|
||||
return 'primary';
|
||||
case 2:
|
||||
return 'warning';
|
||||
default:
|
||||
return 'danger';
|
||||
get masterPasswordScoreColor() {
|
||||
switch (this.masterPasswordScore) {
|
||||
case 4:
|
||||
return "success";
|
||||
case 3:
|
||||
return "primary";
|
||||
case 2:
|
||||
return "warning";
|
||||
default:
|
||||
return "danger";
|
||||
}
|
||||
}
|
||||
|
||||
get masterPasswordScoreText() {
|
||||
switch (this.masterPasswordScore) {
|
||||
case 4:
|
||||
return this.i18nService.t("strong");
|
||||
case 3:
|
||||
return this.i18nService.t("good");
|
||||
case 2:
|
||||
return this.i18nService.t("weak");
|
||||
default:
|
||||
return this.masterPasswordScore != null ? this.i18nService.t("weak") : null;
|
||||
}
|
||||
}
|
||||
|
||||
async ngOnInit() {
|
||||
await super.ngOnInit();
|
||||
this.broadcasterService.subscribe(BroadcasterSubscriptionId, async (message: any) => {
|
||||
this.ngZone.run(() => {
|
||||
switch (message.command) {
|
||||
case "windowHidden":
|
||||
this.onWindowHidden();
|
||||
break;
|
||||
default:
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
get masterPasswordScoreText() {
|
||||
switch (this.masterPasswordScore) {
|
||||
case 4:
|
||||
return this.i18nService.t('strong');
|
||||
case 3:
|
||||
return this.i18nService.t('good');
|
||||
case 2:
|
||||
return this.i18nService.t('weak');
|
||||
default:
|
||||
return this.masterPasswordScore != null ? this.i18nService.t('weak') : null;
|
||||
}
|
||||
}
|
||||
ngOnDestroy() {
|
||||
this.broadcasterService.unsubscribe(BroadcasterSubscriptionId);
|
||||
}
|
||||
|
||||
async ngOnInit() {
|
||||
await super.ngOnInit();
|
||||
this.broadcasterService.subscribe(BroadcasterSubscriptionId, async (message: any) => {
|
||||
this.ngZone.run(() => {
|
||||
switch (message.command) {
|
||||
case 'windowHidden':
|
||||
this.onWindowHidden();
|
||||
break;
|
||||
default:
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.broadcasterService.unsubscribe(BroadcasterSubscriptionId);
|
||||
}
|
||||
|
||||
onWindowHidden() {
|
||||
this.showPassword = false;
|
||||
}
|
||||
onWindowHidden() {
|
||||
this.showPassword = false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,195 +1,354 @@
|
|||
<div class="modal fade" role="dialog" aria-modal="true" attr.aria-label="{{'settings' | i18n}}">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-body form">
|
||||
<div class="box">
|
||||
<div class="box-header">
|
||||
{{'security' | i18n}}
|
||||
</div>
|
||||
<div class="box-content box-content-padded">
|
||||
<app-vault-timeout-input [vaultTimeouts]="vaultTimeouts" [formControl]="vaultTimeout" ngDefaultControl></app-vault-timeout-input>
|
||||
<div class="form-group">
|
||||
<label>{{'vaultTimeoutAction' | i18n}}</label>
|
||||
<div class="radio radio-mt-2">
|
||||
<label for="vaultTimeoutActionLock">
|
||||
<input type="radio" name="VaultTimeoutAction" id="vaultTimeoutActionLock"
|
||||
value="lock" [(ngModel)]="vaultTimeoutAction"
|
||||
(change)="saveVaultTimeoutOptions()">
|
||||
{{'lock' | i18n}}
|
||||
</label>
|
||||
</div>
|
||||
<small class="help-block">{{'vaultTimeoutActionLockDesc' | i18n}}</small>
|
||||
<div class="radio">
|
||||
<label for="vaultTimeoutActionLogOut">
|
||||
<input type="radio" name="VaultTimeoutAction" id="vaultTimeoutActionLogOut"
|
||||
value="logOut" [(ngModel)]="vaultTimeoutAction"
|
||||
(change)="saveVaultTimeoutOptions()">
|
||||
{{'logOut' | i18n}}
|
||||
</label>
|
||||
</div>
|
||||
<small class="help-block">{{'vaultTimeoutActionLogOutDesc' | i18n}}</small>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="checkbox">
|
||||
<label for="pin">
|
||||
<input id="pin" type="checkbox" name="PIN" [(ngModel)]="pin" (change)="updatePin()">
|
||||
{{'unlockWithPin' | i18n}}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group" *ngIf="supportsBiometric">
|
||||
<div class="checkbox">
|
||||
<label for="biometric">
|
||||
<input id="biometric" type="checkbox" name="biometric" [checked]="biometric" (change)="updateBiometric()">
|
||||
{{biometricText | i18n}}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group" *ngIf="supportsBiometric">
|
||||
<div class="checkbox">
|
||||
<label for="noAutoPromptBiometrics">
|
||||
<input id="noAutoPromptBiometrics" type="checkbox" name="noAutoPromptBiometrics" [(ngModel)]="noAutoPromptBiometrics"
|
||||
[disabled]="!biometric" (change)="updateNoAutoPromptBiometrics()">
|
||||
{{noAutoPromptBiometricsText | i18n}}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal fade" role="dialog" aria-modal="true" attr.aria-label="{{ 'settings' | i18n }}">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-body form">
|
||||
<div class="box">
|
||||
<div class="box-header">
|
||||
{{ "settingsTitle" | i18n: currentUserEmail }}
|
||||
</div>
|
||||
<div class="box-content box-content-padded">
|
||||
<h2>
|
||||
<button
|
||||
id="app-settings"
|
||||
type="button"
|
||||
class="box-header-expandable"
|
||||
(click)="showSecurity = !showSecurity"
|
||||
[attr.aria-expanded]="showSecurity"
|
||||
appAutofocus
|
||||
>
|
||||
{{ "security" | i18n }}
|
||||
<i
|
||||
*ngIf="!showSecurity"
|
||||
class="bwi bwi-angle-down bwi-sm icon"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
<i
|
||||
*ngIf="showSecurity"
|
||||
class="bwi bwi-chevron-up bwi-sm icon"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
</button>
|
||||
</h2>
|
||||
<ng-container *ngIf="showSecurity">
|
||||
<app-vault-timeout-input
|
||||
[vaultTimeouts]="vaultTimeouts"
|
||||
[formControl]="vaultTimeout"
|
||||
ngDefaultControl
|
||||
></app-vault-timeout-input>
|
||||
<div class="form-group">
|
||||
<label>{{ "vaultTimeoutAction" | i18n }}</label>
|
||||
<div class="radio radio-mt-2">
|
||||
<label for="vaultTimeoutActionLock">
|
||||
<input
|
||||
type="radio"
|
||||
name="VaultTimeoutAction"
|
||||
id="vaultTimeoutActionLock"
|
||||
value="lock"
|
||||
[(ngModel)]="vaultTimeoutAction"
|
||||
(change)="saveVaultTimeoutOptions()"
|
||||
/>
|
||||
{{ "lock" | i18n }}
|
||||
</label>
|
||||
</div>
|
||||
<div class="box">
|
||||
<div class="box-header">
|
||||
{{'options' | i18n}}
|
||||
</div>
|
||||
<div class="box-content box-content-padded">
|
||||
<div class="form-group">
|
||||
<label for="clearClipboard">{{'clearClipboard' | i18n}}</label>
|
||||
<select id="clearClipboard" name="ClearClipboard" [(ngModel)]="clearClipboard"
|
||||
(change)="saveClearClipboard()">
|
||||
<option *ngFor="let o of clearClipboardOptions" [ngValue]="o.value">{{o.name}}</option>
|
||||
</select>
|
||||
<small class="help-block">{{'clearClipboardDesc' | i18n}}</small>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="checkbox">
|
||||
<label for="minimizeOnCopyToClipboard">
|
||||
<input id="minimizeOnCopyToClipboard" type="checkbox"
|
||||
name="MinimizeOnCopyToClipboard" [(ngModel)]="minimizeOnCopyToClipboard"
|
||||
(change)="saveMinOnCopyToClipboard()">
|
||||
{{'minimizeOnCopyToClipboard' | i18n}}
|
||||
</label>
|
||||
</div>
|
||||
<small class="help-block">{{'minimizeOnCopyToClipboardDesc' | i18n}}</small>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="checkbox">
|
||||
<label for="disableFavicons">
|
||||
<input id="disableFavicons" type="checkbox" name="DisableFavicons"
|
||||
[(ngModel)]="disableFavicons" (change)="saveFavicons()">
|
||||
{{'disableFavicon' | i18n}}
|
||||
</label>
|
||||
</div>
|
||||
<small class="help-block">{{'disableFaviconDesc' | i18n}}</small>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="checkbox">
|
||||
<label for="enableBrowserIntegration">
|
||||
<input id="enableBrowserIntegration" type="checkbox" name="EnableBrowserIntegration"
|
||||
[(ngModel)]="enableBrowserIntegration" (change)="saveBrowserIntegration()">
|
||||
{{'enableBrowserIntegration' | i18n}}
|
||||
</label>
|
||||
</div>
|
||||
<small class="help-block">{{'enableBrowserIntegrationDesc' | i18n}}</small>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="checkbox">
|
||||
<label for="enableBrowserIntegrationFingerprint">
|
||||
<input id="enableBrowserIntegrationFingerprint" type="checkbox" name="EnableBrowserIntegrationFingerprint"
|
||||
[(ngModel)]="enableBrowserIntegrationFingerprint" (change)="saveBrowserIntegrationFingerprint()" [disabled]="!enableBrowserIntegration">
|
||||
{{'enableBrowserIntegrationFingerprint' | i18n}}
|
||||
</label>
|
||||
</div>
|
||||
<small class="help-block">{{'enableBrowserIntegrationFingerprintDesc' | i18n}}</small>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="checkbox">
|
||||
<label for="enableTray">
|
||||
<input id="enableTray" type="checkbox" name="EnableTray" [(ngModel)]="enableTray"
|
||||
(change)="saveTray()">
|
||||
{{enableTrayText}}
|
||||
</label>
|
||||
</div>
|
||||
<small class="help-block">{{enableTrayDescText}}</small>
|
||||
</div>
|
||||
<div class="form-group" *ngIf="showMinToTray">
|
||||
<div class="checkbox">
|
||||
<label for="enableMinToTray">
|
||||
<input id="enableMinToTray" type="checkbox" name="EnableMinToTray"
|
||||
[(ngModel)]="enableMinToTray" (change)="saveMinToTray()">
|
||||
{{enableMinToTrayText}}
|
||||
</label>
|
||||
</div>
|
||||
<small class="help-block">{{enableMinToTrayDescText}}</small>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="checkbox">
|
||||
<label for="enableCloseToTray">
|
||||
<input id="enableCloseToTray" type="checkbox" name="EnableCloseToTray"
|
||||
[(ngModel)]="enableCloseToTray" (change)="saveCloseToTray()">
|
||||
{{enableCloseToTrayText}}
|
||||
</label>
|
||||
</div>
|
||||
<small class="help-block">{{enableCloseToTrayDescText}}</small>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="checkbox">
|
||||
<label for="startToTray">
|
||||
<input id="startToTray" type="checkbox" name="StartToTray" [(ngModel)]="startToTray"
|
||||
(change)="saveStartToTray()">
|
||||
{{startToTrayText}}
|
||||
</label>
|
||||
</div>
|
||||
<small class="help-block">{{startToTrayDescText}}</small>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="checkbox">
|
||||
<label for="openAtLogin">
|
||||
<input id="openAtLogin" type="checkbox" name="OpenAtLogin" [(ngModel)]="openAtLogin"
|
||||
(change)="saveOpenAtLogin()">
|
||||
{{'openAtLogin' | i18n}}
|
||||
</label>
|
||||
</div>
|
||||
<small class="help-block">{{'openAtLoginDesc' | i18n}}</small>
|
||||
</div>
|
||||
<div class="form-group" *ngIf="showAlwaysShowDock">
|
||||
<div class="checkbox">
|
||||
<label for="alwaysShowDock">
|
||||
<input id="alwaysShowDock" type="checkbox" name="AlwaysShowDock" [(ngModel)]="alwaysShowDock"
|
||||
(change)="saveAlwaysShowDock()">
|
||||
{{'alwaysShowDock' | i18n}}
|
||||
</label>
|
||||
</div>
|
||||
<small class="help-block">{{'alwaysShowDockDesc' | i18n}}</small>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="theme">{{'theme' | i18n}}</label>
|
||||
<select id="theme" name="Theme" [(ngModel)]="theme" (change)="saveTheme()">
|
||||
<option *ngFor="let o of themeOptions" [ngValue]="o.value">{{o.name}}</option>
|
||||
</select>
|
||||
<small class="help-block">{{'themeDesc' | i18n}}</small>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="locale">{{'language' | i18n}}</label>
|
||||
<select id="locale" name="Locale" [(ngModel)]="locale" (change)="saveLocale()">
|
||||
<option *ngFor="let o of localeOptions" [ngValue]="o.value">{{o.name}}</option>
|
||||
</select>
|
||||
<small class="help-block">{{'languageDesc' | i18n}}</small>
|
||||
</div>
|
||||
</div>
|
||||
<small class="help-block">{{ "vaultTimeoutActionLockDesc" | i18n }}</small>
|
||||
<div class="radio">
|
||||
<label for="vaultTimeoutActionLogOut">
|
||||
<input
|
||||
type="radio"
|
||||
name="VaultTimeoutAction"
|
||||
id="vaultTimeoutActionLogOut"
|
||||
value="logOut"
|
||||
[(ngModel)]="vaultTimeoutAction"
|
||||
(change)="saveVaultTimeoutOptions()"
|
||||
/>
|
||||
{{ "logOut" | i18n }}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" data-dismiss="modal">{{'close' | i18n}}</button>
|
||||
</div>
|
||||
<small class="help-block">{{ "vaultTimeoutActionLogOutDesc" | i18n }}</small>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="checkbox">
|
||||
<label for="pin">
|
||||
<input
|
||||
id="pin"
|
||||
type="checkbox"
|
||||
name="PIN"
|
||||
[(ngModel)]="pin"
|
||||
(change)="updatePin()"
|
||||
/>
|
||||
{{ "unlockWithPin" | i18n }}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group" *ngIf="supportsBiometric">
|
||||
<div class="checkbox">
|
||||
<label for="biometric">
|
||||
<input
|
||||
id="biometric"
|
||||
type="checkbox"
|
||||
name="biometric"
|
||||
[checked]="biometric"
|
||||
(change)="updateBiometric()"
|
||||
/>
|
||||
{{ biometricText | i18n }}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group" *ngIf="supportsBiometric">
|
||||
<div class="checkbox">
|
||||
<label for="noAutoPromptBiometrics">
|
||||
<input
|
||||
id="noAutoPromptBiometrics"
|
||||
type="checkbox"
|
||||
name="noAutoPromptBiometrics"
|
||||
[(ngModel)]="noAutoPromptBiometrics"
|
||||
[disabled]="!biometric"
|
||||
(change)="updateNoAutoPromptBiometrics()"
|
||||
/>
|
||||
{{ noAutoPromptBiometricsText | i18n }}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box">
|
||||
<div class="box-content box-content-padded">
|
||||
<h2>
|
||||
<button
|
||||
type="button"
|
||||
class="box-header-expandable"
|
||||
(click)="showAccountPreferences = !showAccountPreferences"
|
||||
[attr.aria-expanded]="showAccountPreferences"
|
||||
>
|
||||
{{ "accountPreferences" | i18n }}
|
||||
<i
|
||||
*ngIf="!showAccountPreferences"
|
||||
class="bwi bwi-angle-down bwi-sm icon"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
<i
|
||||
*ngIf="showAccountPreferences"
|
||||
class="bwi bwi-chevron-up bwi-sm icon"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
</button>
|
||||
</h2>
|
||||
<ng-container *ngIf="showAccountPreferences">
|
||||
<div class="form-group">
|
||||
<label for="clearClipboard">{{ "clearClipboard" | i18n }}</label>
|
||||
<select
|
||||
id="clearClipboard"
|
||||
name="ClearClipboard"
|
||||
[(ngModel)]="clearClipboard"
|
||||
(change)="saveClearClipboard()"
|
||||
>
|
||||
<option *ngFor="let o of clearClipboardOptions" [ngValue]="o.value">
|
||||
{{ o.name }}
|
||||
</option>
|
||||
</select>
|
||||
<small class="help-block">{{ "clearClipboardDesc" | i18n }}</small>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="checkbox">
|
||||
<label for="minimizeOnCopyToClipboard">
|
||||
<input
|
||||
id="minimizeOnCopyToClipboard"
|
||||
type="checkbox"
|
||||
name="MinimizeOnCopyToClipboard"
|
||||
[(ngModel)]="minimizeOnCopyToClipboard"
|
||||
(change)="saveMinOnCopyToClipboard()"
|
||||
/>
|
||||
{{ "minimizeOnCopyToClipboard" | i18n }}
|
||||
</label>
|
||||
</div>
|
||||
<small class="help-block">{{ "minimizeOnCopyToClipboardDesc" | i18n }}</small>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="checkbox">
|
||||
<label for="disableFavicons">
|
||||
<input
|
||||
id="disableFavicons"
|
||||
type="checkbox"
|
||||
name="DisableFavicons"
|
||||
[(ngModel)]="disableFavicons"
|
||||
(change)="saveFavicons()"
|
||||
/>
|
||||
{{ "disableFavicon" | i18n }}
|
||||
</label>
|
||||
</div>
|
||||
<small class="help-block">{{ "disableFaviconDesc" | i18n }}</small>
|
||||
</div>
|
||||
</ng-container>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box">
|
||||
<div class="box-content box-content-padded">
|
||||
<h2>
|
||||
<button
|
||||
type="button"
|
||||
class="box-header-expandable"
|
||||
(click)="showAppPreferences = !showAppPreferences"
|
||||
[attr.aria-expanded]="showAppPreferences"
|
||||
>
|
||||
{{ "appPreferences" | i18n }}
|
||||
<i
|
||||
*ngIf="!showAppPreferences"
|
||||
class="bwi bwi-angle-down bwi-sm icon"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
<i
|
||||
*ngIf="showAppPreferences"
|
||||
class="bwi bwi-chevron-up bwi-sm icon"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
</button>
|
||||
</h2>
|
||||
<ng-container *ngIf="showAppPreferences">
|
||||
<div class="form-group">
|
||||
<div class="checkbox">
|
||||
<label for="enableTray">
|
||||
<input
|
||||
id="enableTray"
|
||||
type="checkbox"
|
||||
name="EnableTray"
|
||||
[(ngModel)]="enableTray"
|
||||
(change)="saveTray()"
|
||||
/>
|
||||
{{ enableTrayText }}
|
||||
</label>
|
||||
</div>
|
||||
<small class="help-block">{{ enableTrayDescText }}</small>
|
||||
</div>
|
||||
<div class="form-group" *ngIf="showMinToTray">
|
||||
<div class="checkbox">
|
||||
<label for="enableMinToTray">
|
||||
<input
|
||||
id="enableMinToTray"
|
||||
type="checkbox"
|
||||
name="EnableMinToTray"
|
||||
[(ngModel)]="enableMinToTray"
|
||||
(change)="saveMinToTray()"
|
||||
/>
|
||||
{{ enableMinToTrayText }}
|
||||
</label>
|
||||
</div>
|
||||
<small class="help-block">{{ enableMinToTrayDescText }}</small>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="checkbox">
|
||||
<label for="enableCloseToTray">
|
||||
<input
|
||||
id="enableCloseToTray"
|
||||
type="checkbox"
|
||||
name="EnableCloseToTray"
|
||||
[(ngModel)]="enableCloseToTray"
|
||||
(change)="saveCloseToTray()"
|
||||
/>
|
||||
{{ enableCloseToTrayText }}
|
||||
</label>
|
||||
</div>
|
||||
<small class="help-block">{{ enableCloseToTrayDescText }}</small>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="checkbox">
|
||||
<label for="startToTray">
|
||||
<input
|
||||
id="startToTray"
|
||||
type="checkbox"
|
||||
name="StartToTray"
|
||||
[(ngModel)]="startToTray"
|
||||
(change)="saveStartToTray()"
|
||||
/>
|
||||
{{ startToTrayText }}
|
||||
</label>
|
||||
</div>
|
||||
<small class="help-block">{{ startToTrayDescText }}</small>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="checkbox">
|
||||
<label for="openAtLogin">
|
||||
<input
|
||||
id="openAtLogin"
|
||||
type="checkbox"
|
||||
name="OpenAtLogin"
|
||||
[(ngModel)]="openAtLogin"
|
||||
(change)="saveOpenAtLogin()"
|
||||
/>
|
||||
{{ "openAtLogin" | i18n }}
|
||||
</label>
|
||||
</div>
|
||||
<small class="help-block">{{ "openAtLoginDesc" | i18n }}</small>
|
||||
</div>
|
||||
<div class="form-group" *ngIf="showAlwaysShowDock">
|
||||
<div class="checkbox">
|
||||
<label for="alwaysShowDock">
|
||||
<input
|
||||
id="alwaysShowDock"
|
||||
type="checkbox"
|
||||
name="AlwaysShowDock"
|
||||
[(ngModel)]="alwaysShowDock"
|
||||
(change)="saveAlwaysShowDock()"
|
||||
/>
|
||||
{{ "alwaysShowDock" | i18n }}
|
||||
</label>
|
||||
</div>
|
||||
<small class="help-block">{{ "alwaysShowDockDesc" | i18n }}</small>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="checkbox">
|
||||
<label for="enableBrowserIntegration">
|
||||
<input
|
||||
id="enableBrowserIntegration"
|
||||
type="checkbox"
|
||||
name="EnableBrowserIntegration"
|
||||
[(ngModel)]="enableBrowserIntegration"
|
||||
(change)="saveBrowserIntegration()"
|
||||
/>
|
||||
{{ "enableBrowserIntegration" | i18n }}
|
||||
</label>
|
||||
</div>
|
||||
<small class="help-block">{{ "enableBrowserIntegrationDesc" | i18n }}</small>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="checkbox">
|
||||
<label for="enableBrowserIntegrationFingerprint">
|
||||
<input
|
||||
id="enableBrowserIntegrationFingerprint"
|
||||
type="checkbox"
|
||||
name="EnableBrowserIntegrationFingerprint"
|
||||
[(ngModel)]="enableBrowserIntegrationFingerprint"
|
||||
(change)="saveBrowserIntegrationFingerprint()"
|
||||
[disabled]="!enableBrowserIntegration"
|
||||
/>
|
||||
{{ "enableBrowserIntegrationFingerprint" | i18n }}
|
||||
</label>
|
||||
</div>
|
||||
<small class="help-block">{{
|
||||
"enableBrowserIntegrationFingerprintDesc" | i18n
|
||||
}}</small>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="theme">{{ "theme" | i18n }}</label>
|
||||
<select id="theme" name="Theme" [(ngModel)]="theme" (change)="saveTheme()">
|
||||
<option *ngFor="let o of themeOptions" [ngValue]="o.value">{{ o.name }}</option>
|
||||
</select>
|
||||
<small class="help-block">{{ "themeDesc" | i18n }}</small>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="locale">{{ "language" | i18n }}</label>
|
||||
<select id="locale" name="Locale" [(ngModel)]="locale" (change)="saveLocale()">
|
||||
<option *ngFor="let o of localeOptions" [ngValue]="o.value">{{ o.name }}</option>
|
||||
</select>
|
||||
<small class="help-block">{{ "languageDesc" | i18n }}</small>
|
||||
</div>
|
||||
</ng-container>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" data-dismiss="modal">{{ "close" | i18n }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,365 +1,407 @@
|
|||
import {
|
||||
Component,
|
||||
OnInit,
|
||||
} from '@angular/core';
|
||||
import { FormControl } from '@angular/forms';
|
||||
import { debounceTime } from 'rxjs/operators';
|
||||
import { Component, OnInit } from "@angular/core";
|
||||
import { FormControl } from "@angular/forms";
|
||||
import { debounceTime } from "rxjs/operators";
|
||||
|
||||
import { DeviceType } from 'jslib-common/enums/deviceType';
|
||||
import { ThemeType } from 'jslib-common/enums/themeType';
|
||||
import { ModalService } from "jslib-angular/services/modal.service";
|
||||
import { CryptoService } from "jslib-common/abstractions/crypto.service";
|
||||
import { I18nService } from "jslib-common/abstractions/i18n.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";
|
||||
import { VaultTimeoutService } from "jslib-common/abstractions/vaultTimeout.service";
|
||||
import { DeviceType } from "jslib-common/enums/deviceType";
|
||||
import { StorageLocation } from "jslib-common/enums/storageLocation";
|
||||
import { ThemeType } from "jslib-common/enums/themeType";
|
||||
import { Utils } from "jslib-common/misc/utils";
|
||||
import { isWindowsStore } from "jslib-electron/utils";
|
||||
|
||||
import { CryptoService } from 'jslib-common/abstractions/crypto.service';
|
||||
import { I18nService } from 'jslib-common/abstractions/i18n.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';
|
||||
import { StorageService } from 'jslib-common/abstractions/storage.service';
|
||||
import { VaultTimeoutService } from 'jslib-common/abstractions/vaultTimeout.service';
|
||||
|
||||
import { ConstantsService } from 'jslib-common/services/constants.service';
|
||||
|
||||
import { ModalService } from 'jslib-angular/services/modal.service';
|
||||
|
||||
import { ElectronConstants } from 'jslib-electron/electronConstants';
|
||||
|
||||
import { Utils } from 'jslib-common/misc/utils';
|
||||
import { isWindowsStore } from 'jslib-electron/utils';
|
||||
|
||||
import { SetPinComponent } from '../components/set-pin.component';
|
||||
import { SetPinComponent } from "../components/set-pin.component";
|
||||
|
||||
@Component({
|
||||
selector: 'app-settings',
|
||||
templateUrl: 'settings.component.html',
|
||||
selector: "app-settings",
|
||||
templateUrl: "settings.component.html",
|
||||
})
|
||||
export class SettingsComponent implements OnInit {
|
||||
vaultTimeoutAction: string;
|
||||
pin: boolean = null;
|
||||
disableFavicons: boolean = false;
|
||||
enableBrowserIntegration: boolean = false;
|
||||
enableBrowserIntegrationFingerprint: boolean = false;
|
||||
enableMinToTray: boolean = false;
|
||||
enableCloseToTray: boolean = false;
|
||||
enableTray: boolean = false;
|
||||
showMinToTray: boolean = false;
|
||||
startToTray: boolean = false;
|
||||
minimizeOnCopyToClipboard: boolean = false;
|
||||
locale: string;
|
||||
vaultTimeouts: any[];
|
||||
localeOptions: any[];
|
||||
theme: string;
|
||||
themeOptions: any[];
|
||||
clearClipboard: number;
|
||||
clearClipboardOptions: any[];
|
||||
supportsBiometric: boolean;
|
||||
biometric: boolean;
|
||||
biometricText: string;
|
||||
noAutoPromptBiometrics: boolean;
|
||||
noAutoPromptBiometricsText: string;
|
||||
alwaysShowDock: boolean;
|
||||
showAlwaysShowDock: boolean = false;
|
||||
openAtLogin: boolean;
|
||||
requireEnableTray: boolean = false;
|
||||
vaultTimeoutAction: string;
|
||||
pin: boolean = null;
|
||||
disableFavicons = false;
|
||||
enableBrowserIntegration = false;
|
||||
enableBrowserIntegrationFingerprint = false;
|
||||
enableMinToTray = false;
|
||||
enableCloseToTray = false;
|
||||
enableTray = false;
|
||||
showMinToTray = false;
|
||||
startToTray = false;
|
||||
minimizeOnCopyToClipboard = false;
|
||||
locale: string;
|
||||
vaultTimeouts: any[];
|
||||
localeOptions: any[];
|
||||
theme: ThemeType;
|
||||
themeOptions: any[];
|
||||
clearClipboard: number;
|
||||
clearClipboardOptions: any[];
|
||||
supportsBiometric: boolean;
|
||||
biometric: boolean;
|
||||
biometricText: string;
|
||||
noAutoPromptBiometrics: boolean;
|
||||
noAutoPromptBiometricsText: string;
|
||||
alwaysShowDock: boolean;
|
||||
showAlwaysShowDock = false;
|
||||
openAtLogin: boolean;
|
||||
requireEnableTray = false;
|
||||
|
||||
enableTrayText: string;
|
||||
enableTrayDescText: string;
|
||||
enableMinToTrayText: string;
|
||||
enableMinToTrayDescText: string;
|
||||
enableCloseToTrayText: string;
|
||||
enableCloseToTrayDescText: string;
|
||||
startToTrayText: string;
|
||||
startToTrayDescText: string;
|
||||
enableTrayText: string;
|
||||
enableTrayDescText: string;
|
||||
enableMinToTrayText: string;
|
||||
enableMinToTrayDescText: string;
|
||||
enableCloseToTrayText: string;
|
||||
enableCloseToTrayDescText: string;
|
||||
startToTrayText: string;
|
||||
startToTrayDescText: string;
|
||||
|
||||
vaultTimeout: FormControl = new FormControl(null);
|
||||
vaultTimeout: FormControl = new FormControl(null);
|
||||
|
||||
constructor(private i18nService: I18nService, private platformUtilsService: PlatformUtilsService,
|
||||
private storageService: StorageService, private vaultTimeoutService: VaultTimeoutService,
|
||||
private stateService: StateService, private messagingService: MessagingService,
|
||||
private cryptoService: CryptoService, private modalService: ModalService) {
|
||||
const isMac = this.platformUtilsService.getDevice() === DeviceType.MacOsDesktop;
|
||||
showSecurity = true;
|
||||
showAccountPreferences = true;
|
||||
showAppPreferences = true;
|
||||
|
||||
// Workaround to avoid ghosting trays https://github.com/electron/electron/issues/17622
|
||||
this.requireEnableTray = this.platformUtilsService.getDevice() === DeviceType.LinuxDesktop;
|
||||
currentUserEmail: string;
|
||||
|
||||
const trayKey = isMac ? 'enableMenuBar' : 'enableTray';
|
||||
this.enableTrayText = this.i18nService.t(trayKey);
|
||||
this.enableTrayDescText = this.i18nService.t(trayKey + 'Desc');
|
||||
constructor(
|
||||
private i18nService: I18nService,
|
||||
private platformUtilsService: PlatformUtilsService,
|
||||
private vaultTimeoutService: VaultTimeoutService,
|
||||
private stateService: StateService,
|
||||
private messagingService: MessagingService,
|
||||
private cryptoService: CryptoService,
|
||||
private modalService: ModalService
|
||||
) {
|
||||
const isMac = this.platformUtilsService.getDevice() === DeviceType.MacOsDesktop;
|
||||
|
||||
const minToTrayKey = isMac ? 'enableMinToMenuBar' : 'enableMinToTray';
|
||||
this.enableMinToTrayText = this.i18nService.t(minToTrayKey);
|
||||
this.enableMinToTrayDescText = this.i18nService.t(minToTrayKey + 'Desc');
|
||||
// Workaround to avoid ghosting trays https://github.com/electron/electron/issues/17622
|
||||
this.requireEnableTray = this.platformUtilsService.getDevice() === DeviceType.LinuxDesktop;
|
||||
|
||||
const closeToTrayKey = isMac ? 'enableCloseToMenuBar' : 'enableCloseToTray';
|
||||
this.enableCloseToTrayText = this.i18nService.t(closeToTrayKey);
|
||||
this.enableCloseToTrayDescText = this.i18nService.t(closeToTrayKey + 'Desc');
|
||||
const trayKey = isMac ? "enableMenuBar" : "enableTray";
|
||||
this.enableTrayText = this.i18nService.t(trayKey);
|
||||
this.enableTrayDescText = this.i18nService.t(trayKey + "Desc");
|
||||
|
||||
const startToTrayKey = isMac ? 'startToMenuBar' : 'startToTray';
|
||||
this.startToTrayText = this.i18nService.t(startToTrayKey);
|
||||
this.startToTrayDescText = this.i18nService.t(startToTrayKey + 'Desc');
|
||||
const minToTrayKey = isMac ? "enableMinToMenuBar" : "enableMinToTray";
|
||||
this.enableMinToTrayText = this.i18nService.t(minToTrayKey);
|
||||
this.enableMinToTrayDescText = this.i18nService.t(minToTrayKey + "Desc");
|
||||
|
||||
this.vaultTimeouts = [
|
||||
// { name: i18nService.t('immediately'), value: 0 },
|
||||
{ name: i18nService.t('oneMinute'), value: 1 },
|
||||
{ name: i18nService.t('fiveMinutes'), value: 5 },
|
||||
{ name: i18nService.t('fifteenMinutes'), value: 15 },
|
||||
{ name: i18nService.t('thirtyMinutes'), value: 30 },
|
||||
{ name: i18nService.t('oneHour'), value: 60 },
|
||||
{ name: i18nService.t('fourHours'), value: 240 },
|
||||
{ name: i18nService.t('onIdle'), value: -4 },
|
||||
{ name: i18nService.t('onSleep'), value: -3 },
|
||||
];
|
||||
const closeToTrayKey = isMac ? "enableCloseToMenuBar" : "enableCloseToTray";
|
||||
this.enableCloseToTrayText = this.i18nService.t(closeToTrayKey);
|
||||
this.enableCloseToTrayDescText = this.i18nService.t(closeToTrayKey + "Desc");
|
||||
|
||||
if (this.platformUtilsService.getDevice() !== DeviceType.LinuxDesktop) {
|
||||
this.vaultTimeouts.push({ name: i18nService.t('onLocked'), value: -2 });
|
||||
}
|
||||
const startToTrayKey = isMac ? "startToMenuBar" : "startToTray";
|
||||
this.startToTrayText = this.i18nService.t(startToTrayKey);
|
||||
this.startToTrayDescText = this.i18nService.t(startToTrayKey + "Desc");
|
||||
|
||||
this.vaultTimeouts = this.vaultTimeouts.concat([
|
||||
{ name: i18nService.t('onRestart'), value: -1 },
|
||||
{ name: i18nService.t('never'), value: null },
|
||||
]);
|
||||
this.vaultTimeouts = [
|
||||
// { name: i18nService.t('immediately'), value: 0 },
|
||||
{ name: i18nService.t("oneMinute"), value: 1 },
|
||||
{ name: i18nService.t("fiveMinutes"), value: 5 },
|
||||
{ name: i18nService.t("fifteenMinutes"), value: 15 },
|
||||
{ name: i18nService.t("thirtyMinutes"), value: 30 },
|
||||
{ name: i18nService.t("oneHour"), value: 60 },
|
||||
{ name: i18nService.t("fourHours"), value: 240 },
|
||||
{ name: i18nService.t("onIdle"), value: -4 },
|
||||
{ name: i18nService.t("onSleep"), value: -3 },
|
||||
];
|
||||
|
||||
this.vaultTimeout.valueChanges.pipe(debounceTime(500)).subscribe(() => {
|
||||
this.saveVaultTimeoutOptions();
|
||||
});
|
||||
|
||||
const localeOptions: any[] = [];
|
||||
i18nService.supportedTranslationLocales.forEach(locale => {
|
||||
let name = locale;
|
||||
if (i18nService.localeNames.has(locale)) {
|
||||
name += (' - ' + i18nService.localeNames.get(locale));
|
||||
}
|
||||
localeOptions.push({ name: name, value: locale });
|
||||
});
|
||||
localeOptions.sort(Utils.getSortFunction(i18nService, 'name'));
|
||||
localeOptions.splice(0, 0, { name: i18nService.t('default'), value: null });
|
||||
this.localeOptions = localeOptions;
|
||||
|
||||
this.themeOptions = [
|
||||
{ name: i18nService.t('default'), value: null },
|
||||
{ name: i18nService.t('light'), value: ThemeType.Light },
|
||||
{ name: i18nService.t('dark'), value: ThemeType.Dark },
|
||||
{ name: 'Nord', value: ThemeType.Nord },
|
||||
];
|
||||
|
||||
this.clearClipboardOptions = [
|
||||
{ name: i18nService.t('never'), value: null },
|
||||
{ name: i18nService.t('tenSeconds'), value: 10 },
|
||||
{ name: i18nService.t('twentySeconds'), value: 20 },
|
||||
{ name: i18nService.t('thirtySeconds'), value: 30 },
|
||||
{ name: i18nService.t('oneMinute'), value: 60 },
|
||||
{ name: i18nService.t('twoMinutes'), value: 120 },
|
||||
{ name: i18nService.t('fiveMinutes'), value: 300 },
|
||||
];
|
||||
if (this.platformUtilsService.getDevice() !== DeviceType.LinuxDesktop) {
|
||||
this.vaultTimeouts.push({ name: i18nService.t("onLocked"), value: -2 });
|
||||
}
|
||||
|
||||
async ngOnInit() {
|
||||
this.showMinToTray = this.platformUtilsService.getDevice() !== DeviceType.LinuxDesktop;
|
||||
this.vaultTimeout.setValue(await this.vaultTimeoutService.getVaultTimeout());
|
||||
this.vaultTimeoutAction = await this.storageService.get<string>(ConstantsService.vaultTimeoutActionKey);
|
||||
const pinSet = await this.vaultTimeoutService.isPinLockSet();
|
||||
this.pin = pinSet[0] || pinSet[1];
|
||||
this.disableFavicons = await this.storageService.get<boolean>(ConstantsService.disableFaviconKey);
|
||||
this.enableBrowserIntegration = await this.storageService.get<boolean>(
|
||||
ElectronConstants.enableBrowserIntegration);
|
||||
this.enableBrowserIntegrationFingerprint = await this.storageService.get<boolean>(ElectronConstants.enableBrowserIntegrationFingerprint);
|
||||
this.enableMinToTray = await this.storageService.get<boolean>(ElectronConstants.enableMinimizeToTrayKey);
|
||||
this.enableCloseToTray = await this.storageService.get<boolean>(ElectronConstants.enableCloseToTrayKey);
|
||||
this.enableTray = await this.storageService.get<boolean>(ElectronConstants.enableTrayKey);
|
||||
this.startToTray = await this.storageService.get<boolean>(ElectronConstants.enableStartToTrayKey);
|
||||
this.locale = await this.storageService.get<string>(ConstantsService.localeKey);
|
||||
this.theme = await this.storageService.get<string>(ConstantsService.themeKey);
|
||||
this.clearClipboard = await this.storageService.get<number>(ConstantsService.clearClipboardKey);
|
||||
this.minimizeOnCopyToClipboard = await this.storageService.get<boolean>(
|
||||
ElectronConstants.minimizeOnCopyToClipboardKey);
|
||||
this.supportsBiometric = await this.platformUtilsService.supportsBiometric();
|
||||
this.biometric = await this.vaultTimeoutService.isBiometricLockSet();
|
||||
this.biometricText = await this.storageService.get<string>(ConstantsService.biometricText);
|
||||
this.noAutoPromptBiometrics = await this.storageService.get<boolean>(ConstantsService.disableAutoBiometricsPromptKey);
|
||||
this.noAutoPromptBiometricsText = await this.storageService.get<string>(ElectronConstants.noAutoPromptBiometricsText);
|
||||
this.alwaysShowDock = await this.storageService.get<boolean>(ElectronConstants.alwaysShowDock);
|
||||
this.showAlwaysShowDock = this.platformUtilsService.getDevice() === DeviceType.MacOsDesktop;
|
||||
this.openAtLogin = await this.storageService.get<boolean>(ElectronConstants.openAtLogin);
|
||||
this.vaultTimeouts = this.vaultTimeouts.concat([
|
||||
{ name: i18nService.t("onRestart"), value: -1 },
|
||||
{ name: i18nService.t("never"), value: null },
|
||||
]);
|
||||
|
||||
const localeOptions: any[] = [];
|
||||
i18nService.supportedTranslationLocales.forEach((locale) => {
|
||||
let name = locale;
|
||||
if (i18nService.localeNames.has(locale)) {
|
||||
name += " - " + i18nService.localeNames.get(locale);
|
||||
}
|
||||
localeOptions.push({ name: name, value: locale });
|
||||
});
|
||||
localeOptions.sort(Utils.getSortFunction(i18nService, "name"));
|
||||
localeOptions.splice(0, 0, { name: i18nService.t("default"), value: null });
|
||||
this.localeOptions = localeOptions;
|
||||
|
||||
this.themeOptions = [
|
||||
{ name: i18nService.t("default"), value: ThemeType.System },
|
||||
{ name: i18nService.t("light"), value: ThemeType.Light },
|
||||
{ name: i18nService.t("dark"), value: ThemeType.Dark },
|
||||
{ name: "Nord", value: ThemeType.Nord },
|
||||
];
|
||||
|
||||
this.clearClipboardOptions = [
|
||||
{ name: i18nService.t("never"), value: null },
|
||||
{ name: i18nService.t("tenSeconds"), value: 10 },
|
||||
{ name: i18nService.t("twentySeconds"), value: 20 },
|
||||
{ name: i18nService.t("thirtySeconds"), value: 30 },
|
||||
{ name: i18nService.t("oneMinute"), value: 60 },
|
||||
{ name: i18nService.t("twoMinutes"), value: 120 },
|
||||
{ name: i18nService.t("fiveMinutes"), value: 300 },
|
||||
];
|
||||
}
|
||||
|
||||
async ngOnInit() {
|
||||
// App preferences
|
||||
this.showMinToTray = this.platformUtilsService.getDevice() !== DeviceType.LinuxDesktop;
|
||||
this.enableMinToTray = await this.stateService.getEnableMinimizeToTray();
|
||||
this.enableCloseToTray = await this.stateService.getEnableCloseToTray();
|
||||
this.enableTray = await this.stateService.getEnableTray();
|
||||
this.startToTray = await this.stateService.getEnableStartToTray();
|
||||
|
||||
this.alwaysShowDock = await this.stateService.getAlwaysShowDock();
|
||||
this.showAlwaysShowDock = this.platformUtilsService.getDevice() === DeviceType.MacOsDesktop;
|
||||
this.openAtLogin = await this.stateService.getOpenAtLogin();
|
||||
|
||||
this.locale = await this.stateService.getLocale();
|
||||
this.theme = await this.stateService.getTheme();
|
||||
|
||||
if ((await this.stateService.getUserId()) == null) {
|
||||
return;
|
||||
}
|
||||
this.currentUserEmail = await this.stateService.getEmail();
|
||||
|
||||
// Security
|
||||
this.vaultTimeout.setValue(await this.stateService.getVaultTimeout());
|
||||
this.vaultTimeoutAction = await this.stateService.getVaultTimeoutAction();
|
||||
this.vaultTimeout.valueChanges.pipe(debounceTime(500)).subscribe(() => {
|
||||
this.saveVaultTimeoutOptions();
|
||||
});
|
||||
|
||||
const pinSet = await this.vaultTimeoutService.isPinLockSet();
|
||||
this.pin = pinSet[0] || pinSet[1];
|
||||
|
||||
// Account preferences
|
||||
this.disableFavicons = await this.stateService.getDisableFavicon();
|
||||
this.enableBrowserIntegration = await this.stateService.getEnableBrowserIntegration();
|
||||
this.enableBrowserIntegrationFingerprint =
|
||||
await this.stateService.getEnableBrowserIntegrationFingerprint();
|
||||
this.clearClipboard = await this.stateService.getClearClipboard();
|
||||
this.minimizeOnCopyToClipboard = await this.stateService.getMinimizeOnCopyToClipboard();
|
||||
this.supportsBiometric = await this.platformUtilsService.supportsBiometric();
|
||||
this.biometric = await this.vaultTimeoutService.isBiometricLockSet();
|
||||
this.biometricText = await this.stateService.getBiometricText();
|
||||
this.noAutoPromptBiometrics = await this.stateService.getNoAutoPromptBiometrics();
|
||||
this.noAutoPromptBiometricsText = await this.stateService.getNoAutoPromptBiometricsText();
|
||||
}
|
||||
|
||||
async saveVaultTimeoutOptions() {
|
||||
if (this.vaultTimeoutAction === "logOut") {
|
||||
const confirmed = await this.platformUtilsService.showDialog(
|
||||
this.i18nService.t("vaultTimeoutLogOutConfirmation"),
|
||||
this.i18nService.t("vaultTimeoutLogOutConfirmationTitle"),
|
||||
this.i18nService.t("yes"),
|
||||
this.i18nService.t("cancel"),
|
||||
"warning"
|
||||
);
|
||||
if (!confirmed) {
|
||||
this.vaultTimeoutAction = "lock";
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
async saveVaultTimeoutOptions() {
|
||||
if (this.vaultTimeoutAction === 'logOut') {
|
||||
const confirmed = await this.platformUtilsService.showDialog(
|
||||
this.i18nService.t('vaultTimeoutLogOutConfirmation'),
|
||||
this.i18nService.t('vaultTimeoutLogOutConfirmationTitle'),
|
||||
this.i18nService.t('yes'), this.i18nService.t('cancel'), 'warning');
|
||||
if (!confirmed) {
|
||||
this.vaultTimeoutAction = 'lock';
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Avoid saving 0 since it's useless as a timeout value.
|
||||
if (this.vaultTimeout.value === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.vaultTimeout.valid) {
|
||||
this.platformUtilsService.showToast('error', null, this.i18nService.t('vaultTimeoutTooLarge'));
|
||||
return;
|
||||
}
|
||||
|
||||
await this.vaultTimeoutService.setVaultTimeoutOptions(this.vaultTimeout.value, this.vaultTimeoutAction);
|
||||
// Avoid saving 0 since it's useless as a timeout value.
|
||||
if (this.vaultTimeout.value === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
async updatePin() {
|
||||
if (this.pin) {
|
||||
const ref = this.modalService.open(SetPinComponent, { allowMultipleModals: true });
|
||||
|
||||
if (ref == null) {
|
||||
this.pin = false;
|
||||
return;
|
||||
}
|
||||
|
||||
this.pin = await ref.onClosedPromise();
|
||||
}
|
||||
if (!this.pin) {
|
||||
await this.cryptoService.clearPinProtectedKey();
|
||||
await this.vaultTimeoutService.clear();
|
||||
}
|
||||
if (!this.vaultTimeout.valid) {
|
||||
this.platformUtilsService.showToast(
|
||||
"error",
|
||||
null,
|
||||
this.i18nService.t("vaultTimeoutTooLarge")
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
async updateBiometric() {
|
||||
const current = this.biometric;
|
||||
if (this.biometric) {
|
||||
this.biometric = false;
|
||||
} else if (this.supportsBiometric) {
|
||||
this.biometric = await this.platformUtilsService.authenticateBiometric();
|
||||
}
|
||||
if (this.biometric === current) {
|
||||
return;
|
||||
}
|
||||
if (this.biometric) {
|
||||
await this.storageService.save(ConstantsService.biometricUnlockKey, true);
|
||||
} else {
|
||||
await this.storageService.remove(ConstantsService.biometricUnlockKey);
|
||||
await this.storageService.remove(ConstantsService.disableAutoBiometricsPromptKey);
|
||||
this.noAutoPromptBiometrics = false;
|
||||
}
|
||||
this.vaultTimeoutService.biometricLocked = false;
|
||||
await this.cryptoService.toggleKey();
|
||||
await this.vaultTimeoutService.setVaultTimeoutOptions(
|
||||
this.vaultTimeout.value,
|
||||
this.vaultTimeoutAction
|
||||
);
|
||||
}
|
||||
|
||||
async updatePin() {
|
||||
if (this.pin) {
|
||||
const ref = this.modalService.open(SetPinComponent, { allowMultipleModals: true });
|
||||
|
||||
if (ref == null) {
|
||||
this.pin = false;
|
||||
return;
|
||||
}
|
||||
|
||||
this.pin = await ref.onClosedPromise();
|
||||
}
|
||||
if (!this.pin) {
|
||||
await this.cryptoService.clearPinProtectedKey();
|
||||
await this.vaultTimeoutService.clear();
|
||||
}
|
||||
}
|
||||
|
||||
async updateBiometric() {
|
||||
const current = this.biometric;
|
||||
if (this.biometric) {
|
||||
this.biometric = false;
|
||||
} else if (this.supportsBiometric) {
|
||||
this.biometric = await this.platformUtilsService.authenticateBiometric();
|
||||
}
|
||||
if (this.biometric === current) {
|
||||
return;
|
||||
}
|
||||
if (this.biometric) {
|
||||
await this.stateService.setBiometricUnlock(true);
|
||||
} else {
|
||||
await this.stateService.setBiometricUnlock(null);
|
||||
await this.stateService.setNoAutoPromptBiometrics(null);
|
||||
this.noAutoPromptBiometrics = false;
|
||||
}
|
||||
await this.stateService.setBiometricLocked(false);
|
||||
await this.cryptoService.toggleKey();
|
||||
}
|
||||
|
||||
async updateNoAutoPromptBiometrics() {
|
||||
if (!this.biometric) {
|
||||
this.noAutoPromptBiometrics = false;
|
||||
}
|
||||
|
||||
async updateNoAutoPromptBiometrics() {
|
||||
if (!this.biometric) {
|
||||
this.noAutoPromptBiometrics = false;
|
||||
}
|
||||
if (this.noAutoPromptBiometrics) {
|
||||
await this.stateService.setNoAutoPromptBiometrics(true);
|
||||
} else {
|
||||
await this.stateService.setNoAutoPromptBiometrics(null);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.noAutoPromptBiometrics) {
|
||||
await this.storageService.save(ConstantsService.disableAutoBiometricsPromptKey, true);
|
||||
} else {
|
||||
await this.storageService.remove(ConstantsService.disableAutoBiometricsPromptKey);
|
||||
}
|
||||
async saveFavicons() {
|
||||
await this.stateService.setDisableFavicon(this.disableFavicons);
|
||||
await this.stateService.setDisableFavicon(this.disableFavicons, {
|
||||
storageLocation: StorageLocation.Disk,
|
||||
});
|
||||
this.messagingService.send("refreshCiphers");
|
||||
}
|
||||
|
||||
async saveMinToTray() {
|
||||
await this.stateService.setEnableMinimizeToTray(this.enableMinToTray);
|
||||
}
|
||||
|
||||
async saveCloseToTray() {
|
||||
if (this.requireEnableTray) {
|
||||
this.enableTray = true;
|
||||
await this.stateService.setEnableTray(this.enableTray);
|
||||
}
|
||||
|
||||
async saveFavicons() {
|
||||
await this.storageService.save(ConstantsService.disableFaviconKey, this.disableFavicons);
|
||||
await this.stateService.save(ConstantsService.disableFaviconKey, this.disableFavicons);
|
||||
this.messagingService.send('refreshCiphers');
|
||||
await this.stateService.setEnableCloseToTray(this.enableCloseToTray);
|
||||
}
|
||||
|
||||
async saveTray() {
|
||||
if (
|
||||
this.requireEnableTray &&
|
||||
!this.enableTray &&
|
||||
(this.startToTray || this.enableCloseToTray)
|
||||
) {
|
||||
const confirm = await this.platformUtilsService.showDialog(
|
||||
this.i18nService.t("confirmTrayDesc"),
|
||||
this.i18nService.t("confirmTrayTitle"),
|
||||
this.i18nService.t("yes"),
|
||||
this.i18nService.t("no"),
|
||||
"warning"
|
||||
);
|
||||
|
||||
if (confirm) {
|
||||
this.startToTray = false;
|
||||
await this.stateService.setEnableStartToTray(this.startToTray);
|
||||
this.enableCloseToTray = false;
|
||||
await this.stateService.setEnableCloseToTray(this.enableCloseToTray);
|
||||
} else {
|
||||
this.enableTray = true;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
async saveMinToTray() {
|
||||
await this.storageService.save(ElectronConstants.enableMinimizeToTrayKey, this.enableMinToTray);
|
||||
await this.stateService.setEnableTray(this.enableTray);
|
||||
this.messagingService.send(this.enableTray ? "showTray" : "removeTray");
|
||||
}
|
||||
|
||||
async saveStartToTray() {
|
||||
if (this.requireEnableTray) {
|
||||
this.enableTray = true;
|
||||
await this.stateService.setEnableTray(this.enableTray);
|
||||
}
|
||||
|
||||
async saveCloseToTray() {
|
||||
if (this.requireEnableTray) {
|
||||
this.enableTray = true;
|
||||
await this.storageService.save(ElectronConstants.enableTrayKey, this.enableTray);
|
||||
}
|
||||
await this.stateService.setEnableStartToTray(this.startToTray);
|
||||
}
|
||||
|
||||
await this.storageService.save(ElectronConstants.enableCloseToTrayKey, this.enableCloseToTray);
|
||||
async saveLocale() {
|
||||
await this.stateService.setLocale(this.locale);
|
||||
}
|
||||
|
||||
async saveTheme() {
|
||||
await this.stateService.setTheme(this.theme);
|
||||
window.setTimeout(() => window.location.reload(), 200);
|
||||
}
|
||||
|
||||
async saveMinOnCopyToClipboard() {
|
||||
await this.stateService.setMinimizeOnCopyToClipboard(this.minimizeOnCopyToClipboard);
|
||||
}
|
||||
|
||||
async saveClearClipboard() {
|
||||
await this.stateService.setClearClipboard(this.clearClipboard);
|
||||
}
|
||||
|
||||
async saveAlwaysShowDock() {
|
||||
await this.stateService.setAlwaysShowDock(this.alwaysShowDock);
|
||||
}
|
||||
|
||||
async saveOpenAtLogin() {
|
||||
this.stateService.setOpenAtLogin(this.openAtLogin);
|
||||
this.messagingService.send(this.openAtLogin ? "addOpenAtLogin" : "removeOpenAtLogin");
|
||||
}
|
||||
|
||||
async saveBrowserIntegration() {
|
||||
if (process.platform === "darwin" && !this.platformUtilsService.isMacAppStore()) {
|
||||
await this.platformUtilsService.showDialog(
|
||||
this.i18nService.t("browserIntegrationMasOnlyDesc"),
|
||||
this.i18nService.t("browserIntegrationMasOnlyTitle"),
|
||||
this.i18nService.t("ok"),
|
||||
null,
|
||||
"warning"
|
||||
);
|
||||
|
||||
this.enableBrowserIntegration = false;
|
||||
return;
|
||||
} else if (isWindowsStore()) {
|
||||
await this.platformUtilsService.showDialog(
|
||||
this.i18nService.t("browserIntegrationWindowsStoreDesc"),
|
||||
this.i18nService.t("browserIntegrationWindowsStoreTitle"),
|
||||
this.i18nService.t("ok"),
|
||||
null,
|
||||
"warning"
|
||||
);
|
||||
|
||||
this.enableBrowserIntegration = false;
|
||||
return;
|
||||
}
|
||||
|
||||
async saveTray() {
|
||||
if (this.requireEnableTray && !this.enableTray && (this.startToTray || this.enableCloseToTray)) {
|
||||
const confirm = await this.platformUtilsService.showDialog(
|
||||
this.i18nService.t('confirmTrayDesc'), this.i18nService.t('confirmTrayTitle'),
|
||||
this.i18nService.t('yes'), this.i18nService.t('no'), 'warning');
|
||||
await this.stateService.setEnableBrowserIntegration(this.enableBrowserIntegration);
|
||||
this.messagingService.send(
|
||||
this.enableBrowserIntegration ? "enableBrowserIntegration" : "disableBrowserIntegration"
|
||||
);
|
||||
|
||||
if (confirm) {
|
||||
this.startToTray = false;
|
||||
await this.storageService.save(ElectronConstants.enableStartToTrayKey, this.startToTray);
|
||||
this.enableCloseToTray = false;
|
||||
await this.storageService.save(ElectronConstants.enableCloseToTrayKey, this.enableCloseToTray);
|
||||
} else {
|
||||
this.enableTray = true;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
await this.storageService.save(ElectronConstants.enableTrayKey, this.enableTray);
|
||||
this.messagingService.send(this.enableTray ? 'showTray' : 'removeTray');
|
||||
if (!this.enableBrowserIntegration) {
|
||||
this.enableBrowserIntegrationFingerprint = false;
|
||||
this.saveBrowserIntegrationFingerprint();
|
||||
}
|
||||
}
|
||||
|
||||
async saveStartToTray() {
|
||||
if (this.requireEnableTray) {
|
||||
this.enableTray = true;
|
||||
await this.storageService.save(ElectronConstants.enableTrayKey, this.enableTray);
|
||||
}
|
||||
|
||||
await this.storageService.save(ElectronConstants.enableStartToTrayKey, this.startToTray);
|
||||
}
|
||||
|
||||
async saveLocale() {
|
||||
await this.storageService.save(ConstantsService.localeKey, this.locale);
|
||||
}
|
||||
|
||||
async saveTheme() {
|
||||
await this.storageService.save(ConstantsService.themeKey, this.theme);
|
||||
window.setTimeout(() => window.location.reload(), 200);
|
||||
}
|
||||
|
||||
async saveMinOnCopyToClipboard() {
|
||||
await this.storageService.save(ElectronConstants.minimizeOnCopyToClipboardKey, this.minimizeOnCopyToClipboard);
|
||||
}
|
||||
|
||||
async saveClearClipboard() {
|
||||
await this.storageService.save(ConstantsService.clearClipboardKey, this.clearClipboard);
|
||||
}
|
||||
|
||||
async saveAlwaysShowDock() {
|
||||
await this.storageService.save(ElectronConstants.alwaysShowDock, this.alwaysShowDock);
|
||||
}
|
||||
|
||||
async saveOpenAtLogin() {
|
||||
this.storageService.save(ElectronConstants.openAtLogin, this.openAtLogin);
|
||||
this.messagingService.send(this.openAtLogin ? 'addOpenAtLogin' : 'removeOpenAtLogin');
|
||||
}
|
||||
|
||||
async saveBrowserIntegration() {
|
||||
if (process.platform === 'darwin' && !this.platformUtilsService.isMacAppStore()) {
|
||||
await this.platformUtilsService.showDialog(
|
||||
this.i18nService.t('browserIntegrationMasOnlyDesc'),
|
||||
this.i18nService.t('browserIntegrationMasOnlyTitle'),
|
||||
this.i18nService.t('ok'), null, 'warning');
|
||||
|
||||
this.enableBrowserIntegration = false;
|
||||
return;
|
||||
} else if (isWindowsStore()) {
|
||||
await this.platformUtilsService.showDialog(
|
||||
this.i18nService.t('browserIntegrationWindowsStoreDesc'),
|
||||
this.i18nService.t('browserIntegrationWindowsStoreTitle'),
|
||||
this.i18nService.t('ok'), null, 'warning');
|
||||
|
||||
this.enableBrowserIntegration = false;
|
||||
return;
|
||||
}
|
||||
|
||||
await this.storageService.save(ElectronConstants.enableBrowserIntegration, this.enableBrowserIntegration);
|
||||
this.messagingService.send(this.enableBrowserIntegration ? 'enableBrowserIntegration' : 'disableBrowserIntegration');
|
||||
|
||||
if (!this.enableBrowserIntegration) {
|
||||
this.enableBrowserIntegrationFingerprint = false;
|
||||
this.saveBrowserIntegrationFingerprint();
|
||||
}
|
||||
}
|
||||
|
||||
async saveBrowserIntegrationFingerprint() {
|
||||
await this.storageService.save(ElectronConstants.enableBrowserIntegrationFingerprint, this.enableBrowserIntegrationFingerprint);
|
||||
}
|
||||
async saveBrowserIntegrationFingerprint() {
|
||||
await this.stateService.setEnableBrowserIntegrationFingerprint(
|
||||
this.enableBrowserIntegrationFingerprint
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
<form id="sso-page" (ngSubmit)="submit()">
|
||||
<div class="content">
|
||||
<img class="logo-image" alt="Bitwarden">
|
||||
<div class="box">
|
||||
<i class="fa fa-spinner fa-spin" title="{{'loading' | i18n}}" aria-hidden="true"></i>
|
||||
{{'loading' | i18n}}
|
||||
</div>
|
||||
<div class="content">
|
||||
<img class="logo-image" alt="Bitwarden" />
|
||||
<div class="box">
|
||||
<i class="bwi bwi-spinner bwi-spin" title="{{ 'loading' | i18n }}" aria-hidden="true"></i>
|
||||
{{ "loading" | i18n }}
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
|
|
@ -1,41 +1,54 @@
|
|||
import { Component } from '@angular/core';
|
||||
import { Component } from "@angular/core";
|
||||
import { ActivatedRoute, Router } from "@angular/router";
|
||||
|
||||
import {
|
||||
ActivatedRoute,
|
||||
Router,
|
||||
} from '@angular/router';
|
||||
|
||||
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';
|
||||
import { StorageService } from 'jslib-common/abstractions/storage.service';
|
||||
import { SyncService } from 'jslib-common/abstractions/sync.service';
|
||||
|
||||
import { SsoComponent as BaseSsoComponent } from 'jslib-angular/components/sso.component';
|
||||
import { SsoComponent as BaseSsoComponent } from "jslib-angular/components/sso.component";
|
||||
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";
|
||||
import { SyncService } from "jslib-common/abstractions/sync.service";
|
||||
|
||||
@Component({
|
||||
selector: 'app-sso',
|
||||
templateUrl: 'sso.component.html',
|
||||
selector: "app-sso",
|
||||
templateUrl: "sso.component.html",
|
||||
})
|
||||
export class SsoComponent extends BaseSsoComponent {
|
||||
constructor(authService: AuthService, router: Router,
|
||||
i18nService: I18nService, syncService: SyncService, route: ActivatedRoute,
|
||||
storageService: StorageService, stateService: StateService,
|
||||
platformUtilsService: PlatformUtilsService, apiService: ApiService,
|
||||
cryptoFunctionService: CryptoFunctionService, environmentService: EnvironmentService,
|
||||
passwordGenerationService: PasswordGenerationService, logService: LogService) {
|
||||
super(authService, router, i18nService, route, storageService, stateService, platformUtilsService,
|
||||
apiService, cryptoFunctionService, environmentService, passwordGenerationService, logService);
|
||||
super.onSuccessfulLogin = () => {
|
||||
return syncService.fullSync(true);
|
||||
};
|
||||
this.redirectUri = 'bitwarden://sso-callback';
|
||||
this.clientId = 'desktop';
|
||||
}
|
||||
constructor(
|
||||
authService: AuthService,
|
||||
router: Router,
|
||||
i18nService: I18nService,
|
||||
syncService: SyncService,
|
||||
route: ActivatedRoute,
|
||||
stateService: StateService,
|
||||
platformUtilsService: PlatformUtilsService,
|
||||
apiService: ApiService,
|
||||
cryptoFunctionService: CryptoFunctionService,
|
||||
environmentService: EnvironmentService,
|
||||
passwordGenerationService: PasswordGenerationService,
|
||||
logService: LogService
|
||||
) {
|
||||
super(
|
||||
authService,
|
||||
router,
|
||||
i18nService,
|
||||
route,
|
||||
stateService,
|
||||
platformUtilsService,
|
||||
apiService,
|
||||
cryptoFunctionService,
|
||||
environmentService,
|
||||
passwordGenerationService,
|
||||
logService
|
||||
);
|
||||
super.onSuccessfulLogin = () => {
|
||||
return syncService.fullSync(true);
|
||||
};
|
||||
this.redirectUri = "bitwarden://sso-callback";
|
||||
this.clientId = "desktop";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,28 +1,33 @@
|
|||
<div class="modal fade" role="dialog" aria-modal="true" aria-labelledby="twoStepTitle">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-body">
|
||||
<div class="box">
|
||||
<div class="box-header" id="twoStepTitle">
|
||||
{{'twoStepOptions' | i18n}}
|
||||
</div>
|
||||
<div class="box-content">
|
||||
<a href="#" appStopClick *ngFor="let p of providers" class="box-content-row"
|
||||
(click)="choose(p)">
|
||||
<img [src]="'images/two-factor/' + p.type + '.png'" alt="" class="img-right">
|
||||
<span class="text">{{p.name}}</span>
|
||||
<span class="detail">{{p.description}}</span>
|
||||
</a>
|
||||
<a href="#" appStopClick class="box-content-row" (click)="recover()">
|
||||
<span class="text">{{'recoveryCodeTitle' | i18n}}</span>
|
||||
<span class="detail">{{'recoveryCodeDesc' | i18n}}</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" data-dismiss="modal">{{'close' | i18n}}</button>
|
||||
</div>
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-body">
|
||||
<div class="box">
|
||||
<div class="box-header" id="twoStepTitle">
|
||||
{{ "twoStepOptions" | i18n }}
|
||||
</div>
|
||||
<div class="box-content">
|
||||
<button
|
||||
type="button"
|
||||
appStopClick
|
||||
*ngFor="let p of providers"
|
||||
class="box-content-row"
|
||||
(click)="choose(p)"
|
||||
>
|
||||
<img [src]="'images/two-factor/' + p.type + '.png'" alt="" class="img-right" />
|
||||
<span class="text">{{ p.name }}</span>
|
||||
<span class="detail">{{ p.description }}</span>
|
||||
</button>
|
||||
<button type="button" appStopClick class="box-content-row" (click)="recover()">
|
||||
<span class="text">{{ "recoveryCodeTitle" | i18n }}</span>
|
||||
<span class="detail">{{ "recoveryCodeDesc" | i18n }}</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" data-dismiss="modal">{{ "close" | i18n }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,21 +1,22 @@
|
|||
import { Component } from '@angular/core';
|
||||
import { Router } from '@angular/router';
|
||||
import { Component } from "@angular/core";
|
||||
import { Router } from "@angular/router";
|
||||
|
||||
import { AuthService } from 'jslib-common/abstractions/auth.service';
|
||||
import { I18nService } from 'jslib-common/abstractions/i18n.service';
|
||||
import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service';
|
||||
|
||||
import {
|
||||
TwoFactorOptionsComponent as BaseTwoFactorOptionsComponent,
|
||||
} from 'jslib-angular/components/two-factor-options.component';
|
||||
import { TwoFactorOptionsComponent as BaseTwoFactorOptionsComponent } from "jslib-angular/components/two-factor-options.component";
|
||||
import { I18nService } from "jslib-common/abstractions/i18n.service";
|
||||
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
|
||||
import { TwoFactorService } from "jslib-common/abstractions/twoFactor.service";
|
||||
|
||||
@Component({
|
||||
selector: 'app-two-factor-options',
|
||||
templateUrl: 'two-factor-options.component.html',
|
||||
selector: "app-two-factor-options",
|
||||
templateUrl: "two-factor-options.component.html",
|
||||
})
|
||||
export class TwoFactorOptionsComponent extends BaseTwoFactorOptionsComponent {
|
||||
constructor(authService: AuthService, router: Router,
|
||||
i18nService: I18nService, platformUtilsService: PlatformUtilsService) {
|
||||
super(authService, router, i18nService, platformUtilsService, window);
|
||||
}
|
||||
constructor(
|
||||
twoFactorService: TwoFactorService,
|
||||
router: Router,
|
||||
i18nService: I18nService,
|
||||
platformUtilsService: PlatformUtilsService
|
||||
) {
|
||||
super(twoFactorService, router, i18nService, platformUtilsService, window);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,88 +1,144 @@
|
|||
<form id="two-factor-page" #form (ngSubmit)="submit()" [appApiAction]="formPromise" attr.aria-hidden="{{showingModal}}">
|
||||
<div class="content">
|
||||
<h1>{{title}}</h1>
|
||||
<p *ngIf="selectedProviderType === providerType.Authenticator">{{'enterVerificationCodeApp' | i18n}}</p>
|
||||
<p *ngIf="selectedProviderType === providerType.Email">
|
||||
{{'enterVerificationCodeEmail' | i18n : twoFactorEmail}}
|
||||
</p>
|
||||
<div class="box last"
|
||||
*ngIf="selectedProviderType === providerType.Email || selectedProviderType === providerType.Authenticator">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="code">{{'verificationCode' | i18n}}</label>
|
||||
<input id="code" type="text" name="Code" [(ngModel)]="token" required appAutofocus>
|
||||
</div>
|
||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
||||
<label for="remember">{{'rememberMe' | i18n}}</label>
|
||||
<input id="remember" type="checkbox" name="Remember" [(ngModel)]="remember">
|
||||
</div>
|
||||
</div>
|
||||
<form
|
||||
id="two-factor-page"
|
||||
#form
|
||||
(ngSubmit)="submit()"
|
||||
[appApiAction]="formPromise"
|
||||
attr.aria-hidden="{{ showingModal }}"
|
||||
>
|
||||
<div id="content" class="content">
|
||||
<h1>{{ title }}</h1>
|
||||
<p *ngIf="selectedProviderType === providerType.Authenticator">
|
||||
{{ "enterVerificationCodeApp" | i18n }}
|
||||
</p>
|
||||
<p *ngIf="selectedProviderType === providerType.Email">
|
||||
{{ "enterVerificationCodeEmail" | i18n: twoFactorEmail }}
|
||||
</p>
|
||||
<div
|
||||
class="box last"
|
||||
*ngIf="
|
||||
selectedProviderType === providerType.Email ||
|
||||
selectedProviderType === providerType.Authenticator
|
||||
"
|
||||
>
|
||||
<div class="box-content">
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="code">{{ "verificationCode" | i18n }}</label>
|
||||
<input
|
||||
id="code"
|
||||
type="text"
|
||||
name="Code"
|
||||
[(ngModel)]="token"
|
||||
required
|
||||
appAutofocus
|
||||
appInputVerbatim
|
||||
/>
|
||||
</div>
|
||||
<ng-container *ngIf="selectedProviderType === providerType.Yubikey">
|
||||
<p>{{'insertYubiKey' | i18n}}</p>
|
||||
<img src="../../images/yubikey.jpg" alt="">
|
||||
<div class="box last">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="code" class="sr-only">{{'verificationCode' | i18n}}</label>
|
||||
<input id="code" type="password" name="Code" [(ngModel)]="token" required appAutofocus>
|
||||
</div>
|
||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
||||
<label for="remember">{{'rememberMe' | i18n}}</label>
|
||||
<input id="remember" type="checkbox" name="Remember" [(ngModel)]="remember">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="selectedProviderType === providerType.WebAuthn">
|
||||
<div id="web-authn-frame">
|
||||
<iframe id="webauthn_iframe" [allow]="webAuthnAllow"></iframe>
|
||||
</div>
|
||||
<div class="box first">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
||||
<label for="remember">{{'rememberMe' | i18n}}</label>
|
||||
<input id="remember" type="checkbox" name="Remember" [(ngModel)]="remember">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="selectedProviderType === providerType.Duo ||
|
||||
selectedProviderType === providerType.OrganizationDuo">
|
||||
<div id="duo-frame"><iframe id="duo_iframe"></iframe></div>
|
||||
<div class="box last">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
||||
<label for="remember">{{'rememberMe' | i18n}}</label>
|
||||
<input id="remember" type="checkbox" name="Remember" [(ngModel)]="remember">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
<div class="box last" *ngIf="selectedProviderType == null">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row">
|
||||
<p>{{'noTwoStepProviders' | i18n}}</p>
|
||||
<p>{{'noTwoStepProviders2' | i18n}}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="buttons">
|
||||
<button type="submit" class="btn primary block" [disabled]="form.loading" appBlurClick *ngIf="selectedProviderType != null && selectedProviderType !== providerType.Duo &&
|
||||
selectedProviderType !== providerType.OrganizationDuo">
|
||||
<span [hidden]="form.loading"><i class="fa fa-sign-in" aria-hidden="true"></i>
|
||||
{{'continue' | i18n}}</span>
|
||||
<i class="fa fa-spinner fa-spin" [hidden]="!form.loading" aria-hidden="true"></i>
|
||||
</button>
|
||||
<a routerLink="/login" class="btn block">{{'cancel' | i18n}}</a>
|
||||
</div>
|
||||
<div class="sub-options">
|
||||
<a href="#" appStopClick (click)="anotherMethod()" role="button">{{'useAnotherTwoStepMethod' | i18n}}</a>
|
||||
<a href="#" appStopClick (click)="sendEmail(true)" [appApiAction]="emailPromise" role="button"
|
||||
*ngIf="selectedProviderType === providerType.Email">
|
||||
{{'sendVerificationCodeEmailAgain' | i18n}}
|
||||
</a>
|
||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
||||
<label for="remember">{{ "rememberMe" | i18n }}</label>
|
||||
<input id="remember" type="checkbox" name="Remember" [(ngModel)]="remember" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<ng-container *ngIf="selectedProviderType === providerType.Yubikey">
|
||||
<p>{{ "insertYubiKey" | i18n }}</p>
|
||||
<img src="../../images/yubikey.jpg" alt="" />
|
||||
<div class="box last">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="code" class="sr-only">{{ "verificationCode" | i18n }}</label>
|
||||
<input
|
||||
id="code"
|
||||
type="password"
|
||||
name="Code"
|
||||
[(ngModel)]="token"
|
||||
required
|
||||
appAutofocus
|
||||
appInputVerbatim
|
||||
/>
|
||||
</div>
|
||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
||||
<label for="remember">{{ "rememberMe" | i18n }}</label>
|
||||
<input id="remember" type="checkbox" name="Remember" [(ngModel)]="remember" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="selectedProviderType === providerType.WebAuthn">
|
||||
<div id="web-authn-frame">
|
||||
<iframe id="webauthn_iframe" [allow]="webAuthnAllow"></iframe>
|
||||
</div>
|
||||
<div class="box first">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
||||
<label for="remember">{{ "rememberMe" | i18n }}</label>
|
||||
<input id="remember" type="checkbox" name="Remember" [(ngModel)]="remember" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
<ng-container
|
||||
*ngIf="
|
||||
selectedProviderType === providerType.Duo ||
|
||||
selectedProviderType === providerType.OrganizationDuo
|
||||
"
|
||||
>
|
||||
<div id="duo-frame"><iframe id="duo_iframe"></iframe></div>
|
||||
<div class="box last">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
||||
<label for="remember">{{ "rememberMe" | i18n }}</label>
|
||||
<input id="remember" type="checkbox" name="Remember" [(ngModel)]="remember" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
<div class="box last" *ngIf="selectedProviderType == null">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row">
|
||||
<p>{{ "noTwoStepProviders" | i18n }}</p>
|
||||
<p>{{ "noTwoStepProviders2" | i18n }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box last" [hidden]="!showCaptcha()">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row">
|
||||
<iframe id="hcaptcha_iframe" height="80"></iframe>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="buttons">
|
||||
<button
|
||||
type="submit"
|
||||
class="btn primary block"
|
||||
[disabled]="form.loading"
|
||||
*ngIf="
|
||||
selectedProviderType != null &&
|
||||
selectedProviderType !== providerType.Duo &&
|
||||
selectedProviderType !== providerType.OrganizationDuo
|
||||
"
|
||||
>
|
||||
<span [hidden]="form.loading"
|
||||
><i class="bwi bwi-sign-in" aria-hidden="true"></i> {{ "continue" | i18n }}</span
|
||||
>
|
||||
<i class="bwi bwi-spinner bwi-spin" [hidden]="!form.loading" aria-hidden="true"></i>
|
||||
</button>
|
||||
<button type="button" routerLink="/login" class="btn block">{{ "cancel" | i18n }}</button>
|
||||
</div>
|
||||
<div class="sub-options">
|
||||
<button type="button" appStopClick (click)="anotherMethod()">
|
||||
{{ "useAnotherTwoStepMethod" | i18n }}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
appStopClick
|
||||
(click)="sendEmail(true)"
|
||||
[appApiAction]="emailPromise"
|
||||
*ngIf="selectedProviderType === providerType.Email"
|
||||
>
|
||||
{{ "sendVerificationCodeEmailAgain" | i18n }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<ng-template #twoFactorOptions></ng-template>
|
||||
|
|
|
@ -1,71 +1,94 @@
|
|||
import {
|
||||
Component,
|
||||
ViewChild,
|
||||
ViewContainerRef,
|
||||
} from '@angular/core';
|
||||
import { Component, ViewChild, ViewContainerRef } from "@angular/core";
|
||||
import { ActivatedRoute, Router } from "@angular/router";
|
||||
|
||||
import {
|
||||
ActivatedRoute,
|
||||
Router,
|
||||
} from '@angular/router';
|
||||
import { TwoFactorComponent as BaseTwoFactorComponent } from "jslib-angular/components/two-factor.component";
|
||||
import { ModalService } from "jslib-angular/services/modal.service";
|
||||
import { ApiService } from "jslib-common/abstractions/api.service";
|
||||
import { AppIdService } from "jslib-common/abstractions/appId.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 { SyncService } from "jslib-common/abstractions/sync.service";
|
||||
import { TwoFactorService } from "jslib-common/abstractions/twoFactor.service";
|
||||
import { TwoFactorProviderType } from "jslib-common/enums/twoFactorProviderType";
|
||||
|
||||
import { TwoFactorOptionsComponent } from './two-factor-options.component';
|
||||
|
||||
import { TwoFactorProviderType } from 'jslib-common/enums/twoFactorProviderType';
|
||||
|
||||
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';
|
||||
import { SyncService } from 'jslib-common/abstractions/sync.service';
|
||||
|
||||
import { ModalService } from 'jslib-angular/services/modal.service';
|
||||
|
||||
import { TwoFactorComponent as BaseTwoFactorComponent } from 'jslib-angular/components/two-factor.component';
|
||||
import { TwoFactorOptionsComponent } from "./two-factor-options.component";
|
||||
|
||||
@Component({
|
||||
selector: 'app-two-factor',
|
||||
templateUrl: 'two-factor.component.html',
|
||||
selector: "app-two-factor",
|
||||
templateUrl: "two-factor.component.html",
|
||||
})
|
||||
export class TwoFactorComponent extends BaseTwoFactorComponent {
|
||||
@ViewChild('twoFactorOptions', { read: ViewContainerRef, static: true }) twoFactorOptionsModal: ViewContainerRef;
|
||||
@ViewChild("twoFactorOptions", { read: ViewContainerRef, static: true })
|
||||
twoFactorOptionsModal: ViewContainerRef;
|
||||
|
||||
showingModal = false;
|
||||
showingModal = false;
|
||||
|
||||
constructor(authService: AuthService, router: Router,
|
||||
i18nService: I18nService, apiService: ApiService,
|
||||
platformUtilsService: PlatformUtilsService, syncService: SyncService,
|
||||
environmentService: EnvironmentService, private modalService: ModalService,
|
||||
stateService: StateService, storageService: StorageService, route: ActivatedRoute,
|
||||
logService: LogService) {
|
||||
super(authService, router, i18nService, apiService, platformUtilsService, window, environmentService,
|
||||
stateService, storageService, route, logService);
|
||||
super.onSuccessfulLogin = () => {
|
||||
return syncService.fullSync(true);
|
||||
};
|
||||
}
|
||||
|
||||
async anotherMethod() {
|
||||
const [modal, childComponent] = await this.modalService.openViewRef(TwoFactorOptionsComponent, this.twoFactorOptionsModal);
|
||||
|
||||
modal.onShown.subscribe(() => {
|
||||
this.showingModal = true;
|
||||
});
|
||||
modal.onClosed.subscribe(() => {
|
||||
this.showingModal = false;
|
||||
});
|
||||
|
||||
childComponent.onProviderSelected.subscribe(async (provider: TwoFactorProviderType) => {
|
||||
modal.close();
|
||||
this.selectedProviderType = provider;
|
||||
await this.init();
|
||||
});
|
||||
childComponent.onRecoverSelected.subscribe(() => {
|
||||
modal.close();
|
||||
});
|
||||
constructor(
|
||||
authService: AuthService,
|
||||
router: Router,
|
||||
i18nService: I18nService,
|
||||
apiService: ApiService,
|
||||
platformUtilsService: PlatformUtilsService,
|
||||
syncService: SyncService,
|
||||
environmentService: EnvironmentService,
|
||||
private modalService: ModalService,
|
||||
stateService: StateService,
|
||||
route: ActivatedRoute,
|
||||
logService: LogService,
|
||||
twoFactorService: TwoFactorService,
|
||||
appIdService: AppIdService
|
||||
) {
|
||||
super(
|
||||
authService,
|
||||
router,
|
||||
i18nService,
|
||||
apiService,
|
||||
platformUtilsService,
|
||||
window,
|
||||
environmentService,
|
||||
stateService,
|
||||
route,
|
||||
logService,
|
||||
twoFactorService,
|
||||
appIdService
|
||||
);
|
||||
super.onSuccessfulLogin = () => {
|
||||
return syncService.fullSync(true);
|
||||
};
|
||||
}
|
||||
|
||||
async anotherMethod() {
|
||||
const [modal, childComponent] = await this.modalService.openViewRef(
|
||||
TwoFactorOptionsComponent,
|
||||
this.twoFactorOptionsModal
|
||||
);
|
||||
|
||||
modal.onShown.subscribe(() => {
|
||||
this.showingModal = true;
|
||||
});
|
||||
modal.onClosed.subscribe(() => {
|
||||
this.showingModal = false;
|
||||
});
|
||||
|
||||
childComponent.onProviderSelected.subscribe(async (provider: TwoFactorProviderType) => {
|
||||
modal.close();
|
||||
this.selectedProviderType = provider;
|
||||
await this.init();
|
||||
});
|
||||
childComponent.onRecoverSelected.subscribe(() => {
|
||||
modal.close();
|
||||
});
|
||||
}
|
||||
|
||||
async submit() {
|
||||
await super.submit();
|
||||
if (this.captchaSiteKey) {
|
||||
const content = document.getElementById("content") as HTMLDivElement;
|
||||
content.setAttribute("style", "width:335px");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,78 +1,122 @@
|
|||
<form id="update-temp-password-page" #form (ngSubmit)="submit()" [appApiAction]="formPromise">
|
||||
<div class="content">
|
||||
<app-callout type="warning" title="{{'updateMasterPassword' | i18n}}">
|
||||
{{'updateMasterPasswordWarning' | i18n}}
|
||||
</app-callout>
|
||||
<app-callout type="info" [enforcedPolicyOptions]="enforcedPolicyOptions" *ngIf="enforcedPolicyOptions">
|
||||
</app-callout>
|
||||
<div class="box">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<div class="box-content-row-flex">
|
||||
<div class="row-main">
|
||||
<label for="masterPassword">
|
||||
{{'masterPass' | i18n}}
|
||||
<strong class="sub-label text-{{masterPasswordScoreStyle.Color}}"
|
||||
*ngIf="masterPasswordScoreStyle.Text">
|
||||
{{masterPasswordScoreStyle.Text}}
|
||||
</strong>
|
||||
</label>
|
||||
<input id="masterPassword" type="{{showPassword ? 'text' : 'password'}}"
|
||||
name="MasterPassword" class="monospaced" [(ngModel)]="masterPassword" required
|
||||
[appAutofocus]="masterPassword === ''" (input)="updatePasswordStrength()">
|
||||
</div>
|
||||
<div class="action-buttons">
|
||||
<a class="row-btn" href="#" appStopClick appBlurClick role="button"
|
||||
appA11yTitle="{{'toggleVisibility' | i18n}}" (click)="togglePassword(false)">
|
||||
<i class="fa fa-lg" aria-hidden="true"
|
||||
[ngClass]="{'fa-eye': !showPassword, 'fa-eye-slash': showPassword}"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="progress">
|
||||
<div class="progress-bar bg-{{masterPasswordScoreStyle.Color}}" role="progressbar"
|
||||
aria-valuenow="0" aria-valuemin="0" aria-valuemax="100"
|
||||
[ngStyle]="{width: (masterPasswordScoreStyle.Width + '%')}"
|
||||
attr.aria-valuenow="{{masterPasswordScoreStyle.Width}}"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="content">
|
||||
<app-callout type="warning" title="{{ 'updateMasterPassword' | i18n }}">
|
||||
{{ "updateMasterPasswordWarning" | i18n }}
|
||||
</app-callout>
|
||||
<app-callout
|
||||
type="info"
|
||||
[enforcedPolicyOptions]="enforcedPolicyOptions"
|
||||
*ngIf="enforcedPolicyOptions"
|
||||
>
|
||||
</app-callout>
|
||||
<div class="box">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<div class="box-content-row-flex">
|
||||
<div class="row-main">
|
||||
<label for="masterPassword">
|
||||
{{ "masterPass" | i18n }}
|
||||
<strong
|
||||
class="sub-label text-{{ masterPasswordScoreStyle.Color }}"
|
||||
*ngIf="masterPasswordScoreStyle.Text"
|
||||
>
|
||||
{{ masterPasswordScoreStyle.Text }}
|
||||
</strong>
|
||||
</label>
|
||||
<input
|
||||
id="masterPassword"
|
||||
type="{{ showPassword ? 'text' : 'password' }}"
|
||||
name="MasterPassword"
|
||||
class="monospaced"
|
||||
[(ngModel)]="masterPassword"
|
||||
required
|
||||
[appAutofocus]="masterPassword === ''"
|
||||
(input)="updatePasswordStrength()"
|
||||
appInputVerbatim
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row box-content-row-flex" appBoxRow>
|
||||
<div class="row-main">
|
||||
<label for="masterPasswordRetype">{{'reTypeMasterPass' | i18n}}</label>
|
||||
<input id="masterPasswordRetype" type="{{showPassword ? 'text' : 'password'}}"
|
||||
name="MasterPasswordRetype" class="monospaced" [(ngModel)]="masterPasswordRetype" required>
|
||||
</div>
|
||||
<div class="action-buttons">
|
||||
<a class="row-btn" href="#" appStopClick appBlurClick role="button"
|
||||
appA11yTitle="{{'toggleVisibility' | i18n}}" (click)="togglePassword(true)">
|
||||
<i class="fa fa-lg" aria-hidden="true"
|
||||
[ngClass]="{'fa-eye': !showPassword, 'fa-eye-slash': showPassword}"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="action-buttons">
|
||||
<button
|
||||
type="button"
|
||||
class="row-btn"
|
||||
appStopClick
|
||||
appA11yTitle="{{ 'toggleVisibility' | i18n }}"
|
||||
[attr.aria-pressed]="showPassword"
|
||||
(click)="togglePassword(false)"
|
||||
>
|
||||
<i
|
||||
class="bwi bwi-lg"
|
||||
aria-hidden="true"
|
||||
[ngClass]="{ 'bwi-eye': !showPassword, 'bwi-eye-slash': showPassword }"
|
||||
></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="progress">
|
||||
<div
|
||||
class="progress-bar bg-{{ masterPasswordScoreStyle.Color }}"
|
||||
role="progressbar"
|
||||
aria-valuenow="0"
|
||||
aria-valuemin="0"
|
||||
aria-valuemax="100"
|
||||
[ngStyle]="{ width: masterPasswordScoreStyle.Width + '%' }"
|
||||
attr.aria-valuenow="{{ masterPasswordScoreStyle.Width }}"
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="hint">{{'masterPassHint' | i18n}}</label>
|
||||
<input id="hint" type="text" name="Hint" [(ngModel)]="hint">
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-footer">
|
||||
{{'masterPassHintDesc' | i18n}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="buttons">
|
||||
<button type="submit" class="btn primary block" [disabled]="form.loading" appBlurClick>
|
||||
<b [hidden]="form.loading">{{'submit' | i18n}}</b>
|
||||
<i class="fa fa-spinner fa-spin" [hidden]="!form.loading" aria-hidden="true"></i>
|
||||
</button>
|
||||
<a (click)="logOut()" class="btn block">{{'logOut' | i18n}}</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row box-content-row-flex" appBoxRow>
|
||||
<div class="row-main">
|
||||
<label for="masterPasswordRetype">{{ "reTypeMasterPass" | i18n }}</label>
|
||||
<input
|
||||
id="masterPasswordRetype"
|
||||
type="{{ showPassword ? 'text' : 'password' }}"
|
||||
name="MasterPasswordRetype"
|
||||
class="monospaced"
|
||||
[(ngModel)]="masterPasswordRetype"
|
||||
required
|
||||
appInputVerbatim
|
||||
/>
|
||||
</div>
|
||||
<div class="action-buttons">
|
||||
<button
|
||||
type="button"
|
||||
class="row-btn"
|
||||
appStopClick
|
||||
appA11yTitle="{{ 'toggleVisibility' | i18n }}"
|
||||
[attr.aria-pressed]="showPassword"
|
||||
(click)="togglePassword(true)"
|
||||
>
|
||||
<i
|
||||
class="bwi bwi-lg"
|
||||
aria-hidden="true"
|
||||
[ngClass]="{ 'bwi-eye': !showPassword, 'bwi-eye-slash': showPassword }"
|
||||
></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="hint">{{ "masterPassHint" | i18n }}</label>
|
||||
<input id="hint" type="text" name="Hint" [(ngModel)]="hint" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-footer">
|
||||
{{ "masterPassHintDesc" | i18n }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="buttons">
|
||||
<button type="submit" class="btn primary block" [disabled]="form.loading">
|
||||
<b [hidden]="form.loading">{{ "submit" | i18n }}</b>
|
||||
<i class="bwi bwi-spinner bwi-spin" [hidden]="!form.loading" aria-hidden="true"></i>
|
||||
</button>
|
||||
<button type="button" (click)="logOut()" class="btn block">{{ "logOut" | i18n }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
|
|
@ -1,65 +1,80 @@
|
|||
import { Component } from '@angular/core';
|
||||
import { Component } 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 { UpdateTempPasswordComponent as BaseUpdateTempPasswordComponent } from 'jslib-angular/components/update-temp-password.component';
|
||||
import { UpdateTempPasswordComponent as BaseUpdateTempPasswordComponent } from "jslib-angular/components/update-temp-password.component";
|
||||
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 { StateService } from "jslib-common/abstractions/state.service";
|
||||
import { SyncService } from "jslib-common/abstractions/sync.service";
|
||||
|
||||
interface MasterPasswordScore {
|
||||
Color: string;
|
||||
Text: string;
|
||||
Width: number;
|
||||
Color: string;
|
||||
Text: string;
|
||||
Width: number;
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'app-update-temp-password',
|
||||
templateUrl: 'update-temp-password.component.html',
|
||||
selector: "app-update-temp-password",
|
||||
templateUrl: "update-temp-password.component.html",
|
||||
})
|
||||
|
||||
export class UpdateTempPasswordComponent extends BaseUpdateTempPasswordComponent {
|
||||
get masterPasswordScoreStyle(): MasterPasswordScore {
|
||||
const scoreWidth = this.masterPasswordScore == null ? 0 : (this.masterPasswordScore + 1) * 20;
|
||||
switch (this.masterPasswordScore) {
|
||||
case 4:
|
||||
return {
|
||||
Color: 'bg-success',
|
||||
Text: 'strong',
|
||||
Width: scoreWidth,
|
||||
};
|
||||
case 3:
|
||||
return {
|
||||
Color: 'bg-primary',
|
||||
Text: 'good',
|
||||
Width: scoreWidth,
|
||||
};
|
||||
case 2:
|
||||
return {
|
||||
Color: 'bg-warning',
|
||||
Text: 'weak',
|
||||
Width: scoreWidth,
|
||||
};
|
||||
default:
|
||||
return {
|
||||
Color: 'bg-danger',
|
||||
Text: 'weak',
|
||||
Width: scoreWidth,
|
||||
};
|
||||
}
|
||||
}
|
||||
constructor(i18nService: I18nService, platformUtilsService: PlatformUtilsService,
|
||||
passwordGenerationService: PasswordGenerationService, policyService: PolicyService,
|
||||
cryptoService: CryptoService, userService: UserService,
|
||||
messagingService: MessagingService, apiService: ApiService,
|
||||
syncService: SyncService, logService: LogService) {
|
||||
super(i18nService, platformUtilsService, passwordGenerationService, policyService, cryptoService,
|
||||
userService, messagingService, apiService, syncService, logService);
|
||||
get masterPasswordScoreStyle(): MasterPasswordScore {
|
||||
const scoreWidth = this.masterPasswordScore == null ? 0 : (this.masterPasswordScore + 1) * 20;
|
||||
switch (this.masterPasswordScore) {
|
||||
case 4:
|
||||
return {
|
||||
Color: "bg-success",
|
||||
Text: "strong",
|
||||
Width: scoreWidth,
|
||||
};
|
||||
case 3:
|
||||
return {
|
||||
Color: "bg-primary",
|
||||
Text: "good",
|
||||
Width: scoreWidth,
|
||||
};
|
||||
case 2:
|
||||
return {
|
||||
Color: "bg-warning",
|
||||
Text: "weak",
|
||||
Width: scoreWidth,
|
||||
};
|
||||
default:
|
||||
return {
|
||||
Color: "bg-danger",
|
||||
Text: "weak",
|
||||
Width: scoreWidth,
|
||||
};
|
||||
}
|
||||
}
|
||||
constructor(
|
||||
i18nService: I18nService,
|
||||
platformUtilsService: PlatformUtilsService,
|
||||
passwordGenerationService: PasswordGenerationService,
|
||||
policyService: PolicyService,
|
||||
cryptoService: CryptoService,
|
||||
messagingService: MessagingService,
|
||||
apiService: ApiService,
|
||||
syncService: SyncService,
|
||||
logService: LogService,
|
||||
stateService: StateService
|
||||
) {
|
||||
super(
|
||||
i18nService,
|
||||
platformUtilsService,
|
||||
passwordGenerationService,
|
||||
policyService,
|
||||
cryptoService,
|
||||
messagingService,
|
||||
apiService,
|
||||
stateService,
|
||||
syncService,
|
||||
logService
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,24 +1,45 @@
|
|||
<app-callout type="info" *ngIf="vaultTimeoutPolicy">
|
||||
{{'vaultTimeoutPolicyInEffect' | i18n : vaultTimeoutPolicyHours : vaultTimeoutPolicyMinutes}}
|
||||
{{ "vaultTimeoutPolicyInEffect" | i18n: vaultTimeoutPolicyHours:vaultTimeoutPolicyMinutes }}
|
||||
</app-callout>
|
||||
|
||||
<div [formGroup]="form">
|
||||
<div class="form-group">
|
||||
<label for="vaultTimeout">{{'vaultTimeout' | i18n}}</label>
|
||||
<select id="vaultTimeout" name="VaultTimeout" formControlName="vaultTimeout" class="form-control" appAutofocus>
|
||||
<option *ngFor="let o of vaultTimeouts" [ngValue]="o.value">{{o.name}}</option>
|
||||
</select>
|
||||
<small class="form-text text-muted">{{'vaultTimeoutDesc' | i18n}}</small>
|
||||
<div class="form-group">
|
||||
<label for="vaultTimeout">{{ "vaultTimeout" | i18n }}</label>
|
||||
<select
|
||||
id="vaultTimeout"
|
||||
name="VaultTimeout"
|
||||
formControlName="vaultTimeout"
|
||||
class="form-control"
|
||||
>
|
||||
<option *ngFor="let o of vaultTimeouts" [ngValue]="o.value">{{ o.name }}</option>
|
||||
</select>
|
||||
<small class="form-text text-muted">{{ "vaultTimeoutDesc" | i18n }}</small>
|
||||
</div>
|
||||
<div class="form-group row" *ngIf="showCustom" formGroupName="custom">
|
||||
<div class="col">
|
||||
<label for="hours">{{ "hours" | i18n }}</label>
|
||||
<input
|
||||
id="hours"
|
||||
class="form-control"
|
||||
type="number"
|
||||
min="0"
|
||||
name="hours"
|
||||
formControlName="hours"
|
||||
/>
|
||||
</div>
|
||||
<div class="form-group row" *ngIf="showCustom" formGroupName="custom">
|
||||
<div class="col">
|
||||
<label for="hours">{{'hours' | i18n}}</label>
|
||||
<input id="hours" class="form-control" type="number" min="0" name="hours" formControlName="hours">
|
||||
</div>
|
||||
<div class="col">
|
||||
<label for="minutes">{{'minutes' | i18n}}</label>
|
||||
<input id="minutes" class="form-control" type="number" min="0" max="59" name="minutes" formControlName="minutes">
|
||||
</div>
|
||||
<div class="col">
|
||||
<label for="minutes">{{ "minutes" | i18n }}</label>
|
||||
<input
|
||||
id="minutes"
|
||||
class="form-control"
|
||||
type="number"
|
||||
min="0"
|
||||
max="59"
|
||||
name="minutes"
|
||||
formControlName="minutes"
|
||||
/>
|
||||
</div>
|
||||
<div class="form-group"></div> <!-- Styling fix -->
|
||||
</div>
|
||||
<div class="form-group"></div>
|
||||
<!-- Styling fix -->
|
||||
</div>
|
||||
|
|
|
@ -1,28 +1,22 @@
|
|||
import { Component } from '@angular/core';
|
||||
import {
|
||||
NG_VALIDATORS,
|
||||
NG_VALUE_ACCESSOR,
|
||||
} from '@angular/forms';
|
||||
import { Component } from "@angular/core";
|
||||
import { NG_VALIDATORS, NG_VALUE_ACCESSOR } from "@angular/forms";
|
||||
|
||||
import {
|
||||
VaultTimeoutInputComponent as VaultTimeoutInputComponentBase
|
||||
} from 'jslib-angular/components/settings/vault-timeout-input.component';
|
||||
import { VaultTimeoutInputComponent as VaultTimeoutInputComponentBase } from "jslib-angular/components/settings/vault-timeout-input.component";
|
||||
|
||||
@Component({
|
||||
selector: 'app-vault-timeout-input',
|
||||
templateUrl: 'vault-timeout-input.component.html',
|
||||
providers: [
|
||||
{
|
||||
provide: NG_VALUE_ACCESSOR,
|
||||
multi: true,
|
||||
useExisting: VaultTimeoutInputComponent,
|
||||
},
|
||||
{
|
||||
provide: NG_VALIDATORS,
|
||||
multi: true,
|
||||
useExisting: VaultTimeoutInputComponent,
|
||||
},
|
||||
],
|
||||
selector: "app-vault-timeout-input",
|
||||
templateUrl: "vault-timeout-input.component.html",
|
||||
providers: [
|
||||
{
|
||||
provide: NG_VALUE_ACCESSOR,
|
||||
multi: true,
|
||||
useExisting: VaultTimeoutInputComponent,
|
||||
},
|
||||
{
|
||||
provide: NG_VALIDATORS,
|
||||
multi: true,
|
||||
useExisting: VaultTimeoutInputComponent,
|
||||
},
|
||||
],
|
||||
})
|
||||
export class VaultTimeoutInputComponent extends VaultTimeoutInputComponentBase {
|
||||
}
|
||||
export class VaultTimeoutInputComponent extends VaultTimeoutInputComponentBase {}
|
||||
|
|
|
@ -1,73 +1,70 @@
|
|||
import { NgModule } from '@angular/core';
|
||||
import {
|
||||
RouterModule,
|
||||
Routes,
|
||||
} from '@angular/router';
|
||||
import { NgModule } from "@angular/core";
|
||||
import { RouterModule, Routes } from "@angular/router";
|
||||
|
||||
import { AuthGuardService } from 'jslib-angular/services/auth-guard.service';
|
||||
import { LockGuardService } from 'jslib-angular/services/lock-guard.service';
|
||||
import { UnauthGuardService } from 'jslib-angular/services/unauth-guard.service';
|
||||
import { AuthGuardService } from "jslib-angular/services/auth-guard.service";
|
||||
import { LockGuardService } from "jslib-angular/services/lock-guard.service";
|
||||
|
||||
import { HintComponent } from './accounts/hint.component';
|
||||
import { LockComponent } from './accounts/lock.component';
|
||||
import { LoginComponent } from './accounts/login.component';
|
||||
import { RegisterComponent } from './accounts/register.component';
|
||||
import { RemovePasswordComponent } from './accounts/remove-password.component';
|
||||
import { SetPasswordComponent } from './accounts/set-password.component';
|
||||
import { SsoComponent } from './accounts/sso.component';
|
||||
import { TwoFactorComponent } from './accounts/two-factor.component';
|
||||
import { UpdateTempPasswordComponent } from './accounts/update-temp-password.component';
|
||||
import { LoginGuardService } from "../services/loginGuard.service";
|
||||
|
||||
import { SendComponent } from './send/send.component';
|
||||
|
||||
import { VaultComponent } from './vault/vault.component';
|
||||
import { HintComponent } from "./accounts/hint.component";
|
||||
import { LockComponent } from "./accounts/lock.component";
|
||||
import { LoginComponent } from "./accounts/login.component";
|
||||
import { RegisterComponent } from "./accounts/register.component";
|
||||
import { RemovePasswordComponent } from "./accounts/remove-password.component";
|
||||
import { SetPasswordComponent } from "./accounts/set-password.component";
|
||||
import { SsoComponent } from "./accounts/sso.component";
|
||||
import { TwoFactorComponent } from "./accounts/two-factor.component";
|
||||
import { UpdateTempPasswordComponent } from "./accounts/update-temp-password.component";
|
||||
import { SendComponent } from "./send/send.component";
|
||||
import { VaultComponent } from "./vault/vault.component";
|
||||
|
||||
const routes: Routes = [
|
||||
{ path: '', redirectTo: '/vault', pathMatch: 'full' },
|
||||
{
|
||||
path: 'lock',
|
||||
component: LockComponent,
|
||||
canActivate: [LockGuardService],
|
||||
},
|
||||
{
|
||||
path: 'login',
|
||||
component: LoginComponent,
|
||||
canActivate: [UnauthGuardService],
|
||||
|
||||
},
|
||||
{ path: '2fa', component: TwoFactorComponent },
|
||||
{ path: 'register', component: RegisterComponent },
|
||||
{
|
||||
path: 'vault',
|
||||
component: VaultComponent,
|
||||
canActivate: [AuthGuardService],
|
||||
},
|
||||
{ path: 'hint', component: HintComponent },
|
||||
{ path: 'set-password', component: SetPasswordComponent },
|
||||
{ path: 'sso', component: SsoComponent },
|
||||
{
|
||||
path: 'send',
|
||||
component: SendComponent,
|
||||
canActivate: [AuthGuardService],
|
||||
},
|
||||
{
|
||||
path: 'update-temp-password',
|
||||
component: UpdateTempPasswordComponent,
|
||||
canActivate: [AuthGuardService],
|
||||
},
|
||||
{
|
||||
path: 'remove-password',
|
||||
component: RemovePasswordComponent,
|
||||
canActivate: [AuthGuardService],
|
||||
data: { titleId: 'removeMasterPassword' },
|
||||
},
|
||||
{ path: "", redirectTo: "/vault", pathMatch: "full" },
|
||||
{
|
||||
path: "lock",
|
||||
component: LockComponent,
|
||||
canActivate: [LockGuardService],
|
||||
},
|
||||
{
|
||||
path: "login",
|
||||
component: LoginComponent,
|
||||
canActivate: [LoginGuardService],
|
||||
},
|
||||
{ path: "2fa", component: TwoFactorComponent },
|
||||
{ path: "register", component: RegisterComponent },
|
||||
{
|
||||
path: "vault",
|
||||
component: VaultComponent,
|
||||
canActivate: [AuthGuardService],
|
||||
},
|
||||
{ path: "hint", component: HintComponent },
|
||||
{ path: "set-password", component: SetPasswordComponent },
|
||||
{ path: "sso", component: SsoComponent },
|
||||
{
|
||||
path: "send",
|
||||
component: SendComponent,
|
||||
canActivate: [AuthGuardService],
|
||||
},
|
||||
{
|
||||
path: "update-temp-password",
|
||||
component: UpdateTempPasswordComponent,
|
||||
canActivate: [AuthGuardService],
|
||||
},
|
||||
{
|
||||
path: "remove-password",
|
||||
component: RemovePasswordComponent,
|
||||
canActivate: [AuthGuardService],
|
||||
data: { titleId: "removeMasterPassword" },
|
||||
},
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule.forRoot(routes, {
|
||||
useHash: true,
|
||||
/*enableTracing: true,*/
|
||||
})],
|
||||
exports: [RouterModule],
|
||||
imports: [
|
||||
RouterModule.forRoot(routes, {
|
||||
useHash: true,
|
||||
/*enableTracing: true,*/
|
||||
}),
|
||||
],
|
||||
exports: [RouterModule],
|
||||
})
|
||||
export class AppRoutingModule { }
|
||||
export class AppRoutingModule {}
|
||||
|
|