Compare commits
37 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 |
|
@ -4,5 +4,6 @@ jslib
|
|||
webpack.main.js
|
||||
webpack.renderer.js
|
||||
src/scripts/duo.js
|
||||
desktop_native
|
||||
|
||||
**/node_modules
|
||||
|
|
|
@ -149,6 +149,20 @@ jobs:
|
|||
- name: Install Node dependencies
|
||||
run: npm ci
|
||||
|
||||
- name: Cache Native Module
|
||||
uses: actions/cache@48af2dc4a9e8278b89d7fa154b955c30c6aaab09 # v3.0.2
|
||||
id: cache
|
||||
with:
|
||||
path: desktop_native/*.node
|
||||
key: rust-${{ runner.os }}-${{ hashFiles('desktop_native/**/*') }}
|
||||
|
||||
- name: Build Native Module
|
||||
if: steps.cache.outputs.cache-hit != 'true'
|
||||
working-directory: './desktop_native'
|
||||
run: |
|
||||
npm ci
|
||||
npm run build:cross-platform
|
||||
|
||||
- name: Build application
|
||||
run: npm run dist:lin
|
||||
|
||||
|
@ -228,11 +242,18 @@ jobs:
|
|||
shell: pwsh
|
||||
run: choco install checksum --no-progress
|
||||
|
||||
- name: Rust
|
||||
shell: pwsh
|
||||
run: |
|
||||
rustup target install i686-pc-windows-msvc
|
||||
rustup target install aarch64-pc-windows-msvc
|
||||
|
||||
- name: Print environment
|
||||
run: |
|
||||
node --version
|
||||
npm --version
|
||||
choco --version
|
||||
rustup show
|
||||
|
||||
- name: Login to Azure
|
||||
uses: Azure/login@1f63701bf3e6892515f1b7ce2d2bf1708b46beaf
|
||||
|
@ -253,6 +274,20 @@ jobs:
|
|||
- name: Install Node dependencies
|
||||
run: npm ci
|
||||
|
||||
- name: Cache Native Module
|
||||
uses: actions/cache@48af2dc4a9e8278b89d7fa154b955c30c6aaab09 # v3.0.2
|
||||
id: cache
|
||||
with:
|
||||
path: desktop_native/*.node
|
||||
key: rust-${{ runner.os }}-${{ hashFiles('desktop_native/**/*') }}
|
||||
|
||||
- name: Build Native Module
|
||||
if: steps.cache.outputs.cache-hit != 'true'
|
||||
working-directory: './desktop_native'
|
||||
run: |
|
||||
npm ci
|
||||
npm run build:cross-platform
|
||||
|
||||
- name: Build & Sign (dev)
|
||||
env:
|
||||
ELECTRON_BUILDER_SIGN: 1
|
||||
|
@ -404,10 +439,15 @@ jobs:
|
|||
npm install -g node-gyp
|
||||
node-gyp install $(node -v)
|
||||
|
||||
- name: Rust
|
||||
shell: pwsh
|
||||
run: rustup target install aarch64-apple-darwin
|
||||
|
||||
- name: Print environment
|
||||
run: |
|
||||
node --version
|
||||
npm --version
|
||||
rustup show
|
||||
echo "GitHub ref: $GITHUB_REF"
|
||||
echo "GitHub event: $GITHUB_EVENT"
|
||||
|
||||
|
@ -492,9 +532,23 @@ jobs:
|
|||
env:
|
||||
BUILD_NUMBER: ${{ needs.setup.outputs.build_number }}
|
||||
run: |
|
||||
$package = Get-Content -Raw -Path $env:GITHUB_WORKSPACE\package.json | ConvertFrom-Json;
|
||||
$package.build | Add-Member -MemberType NoteProperty -Name buildVersion -Value "$env:BUILD_NUMBER";
|
||||
$package | ConvertTo-Json -Depth 32 | Set-Content $env:GITHUB_WORKSPACE\package.json;
|
||||
$package = Get-Content -Raw -Path $env:GITHUB_WORKSPACE\electron-builder.json | ConvertFrom-Json;
|
||||
$package | Add-Member -MemberType NoteProperty -Name buildVersion -Value "$env:BUILD_NUMBER";
|
||||
$package | ConvertTo-Json -Depth 32 | Set-Content $env:GITHUB_WORKSPACE\electron-builder.json;
|
||||
|
||||
- name: Cache Native Module
|
||||
uses: actions/cache@48af2dc4a9e8278b89d7fa154b955c30c6aaab09 # v3.0.2
|
||||
id: cache
|
||||
with:
|
||||
path: desktop_native/*.node
|
||||
key: rust-${{ runner.os }}-${{ hashFiles('desktop_native/**/*') }}
|
||||
|
||||
- name: Build Native Module
|
||||
if: steps.cache.outputs.cache-hit != 'true'
|
||||
working-directory: './desktop_native'
|
||||
run: |
|
||||
npm ci
|
||||
npm run build:cross-platform
|
||||
|
||||
- name: Install Node dependencies
|
||||
run: npm ci
|
||||
|
@ -510,14 +564,14 @@ jobs:
|
|||
uses: actions/checkout@a12a3943b4bdde767164f792f33f40b04645d846
|
||||
with:
|
||||
repository: 'bitwarden/browser'
|
||||
path: 'dist-safari/browser'
|
||||
path: 'dist-safari/bitwarden'
|
||||
ref: ${{ needs.setup.outputs.safari_ref }}
|
||||
|
||||
- name: Build Safari extension
|
||||
run: |
|
||||
npm install
|
||||
npm run dist:safari
|
||||
working-directory: dist-safari/browser
|
||||
working-directory: dist-safari/bitwarden/apps/browser
|
||||
|
||||
|
||||
macos-package-github:
|
||||
|
@ -548,10 +602,15 @@ jobs:
|
|||
npm install -g node-gyp
|
||||
node-gyp install $(node -v)
|
||||
|
||||
- name: Rust
|
||||
shell: pwsh
|
||||
run: rustup target install aarch64-apple-darwin
|
||||
|
||||
- name: Print environment
|
||||
run: |
|
||||
node --version
|
||||
npm --version
|
||||
rustup show
|
||||
echo "GitHub ref: $GITHUB_REF"
|
||||
echo "GitHub event: $GITHUB_EVENT"
|
||||
|
||||
|
@ -636,9 +695,23 @@ jobs:
|
|||
env:
|
||||
BUILD_NUMBER: ${{ needs.setup.outputs.build_number }}
|
||||
run: |
|
||||
$package = Get-Content -Raw -Path $env:GITHUB_WORKSPACE\package.json | ConvertFrom-Json;
|
||||
$package.build | Add-Member -MemberType NoteProperty -Name buildVersion -Value "$env:BUILD_NUMBER";
|
||||
$package | ConvertTo-Json -Depth 32 | Set-Content $env:GITHUB_WORKSPACE\package.json;
|
||||
$package = Get-Content -Raw -Path $env:GITHUB_WORKSPACE\electron-builder.json | ConvertFrom-Json;
|
||||
$package | Add-Member -MemberType NoteProperty -Name buildVersion -Value "$env:BUILD_NUMBER";
|
||||
$package | ConvertTo-Json -Depth 32 | Set-Content $env:GITHUB_WORKSPACE\electron-builder.json;
|
||||
|
||||
- name: Cache Native Module
|
||||
uses: actions/cache@48af2dc4a9e8278b89d7fa154b955c30c6aaab09 # v3.0.2
|
||||
id: cache
|
||||
with:
|
||||
path: desktop_native/*.node
|
||||
key: rust-${{ runner.os }}-${{ hashFiles('desktop_native/**/*') }}
|
||||
|
||||
- name: Build Native Module
|
||||
if: steps.cache.outputs.cache-hit != 'true'
|
||||
working-directory: './desktop_native'
|
||||
run: |
|
||||
npm ci
|
||||
npm run build:cross-platform
|
||||
|
||||
- name: NPM install
|
||||
run: npm ci
|
||||
|
@ -656,8 +729,8 @@ jobs:
|
|||
if: steps.safari-cache.outputs.cache-hit != 'true'
|
||||
uses: actions/checkout@a12a3943b4bdde767164f792f33f40b04645d846
|
||||
with:
|
||||
repository: 'bitwarden/browser'
|
||||
path: 'dist-safari/browser'
|
||||
repository: 'bitwarden/bitwarden'
|
||||
path: 'dist-safari/bitwarden'
|
||||
ref: ${{ needs.setup.outputs.safari_ref }}
|
||||
|
||||
- name: Build Safari extension
|
||||
|
@ -665,12 +738,13 @@ jobs:
|
|||
run: |
|
||||
npm install
|
||||
npm run dist:safari
|
||||
working-directory: dist-safari/browser
|
||||
working-directory: dist-safari/bitwarden/apps/browser
|
||||
|
||||
- name: Load Safari extension for .dmg
|
||||
working-directory: dist-safari/bitwarden/apps/browser
|
||||
run: |
|
||||
mkdir PlugIns
|
||||
cp -r dist-safari/browser/dist/Safari/dmg/build/Release/safari.appex PlugIns/safari.appex
|
||||
cp -r dist/Safari/dmg/build/Release/safari.appex PlugIns/safari.appex
|
||||
|
||||
- name: Build application (dist)
|
||||
env:
|
||||
|
@ -735,10 +809,15 @@ jobs:
|
|||
npm install -g node-gyp
|
||||
node-gyp install $(node -v)
|
||||
|
||||
- name: Rust
|
||||
shell: pwsh
|
||||
run: rustup target install aarch64-apple-darwin
|
||||
|
||||
- name: Print environment
|
||||
run: |
|
||||
node --version
|
||||
npm --version
|
||||
rustup show
|
||||
echo "GitHub ref: $GITHUB_REF"
|
||||
echo "GitHub event: $GITHUB_EVENT"
|
||||
|
||||
|
@ -823,9 +902,23 @@ jobs:
|
|||
env:
|
||||
BUILD_NUMBER: ${{ needs.setup.outputs.build_number }}
|
||||
run: |
|
||||
$package = Get-Content -Raw -Path $env:GITHUB_WORKSPACE\package.json | ConvertFrom-Json;
|
||||
$package.build | Add-Member -MemberType NoteProperty -Name buildVersion -Value "$env:BUILD_NUMBER";
|
||||
$package | ConvertTo-Json -Depth 32 | Set-Content $env:GITHUB_WORKSPACE\package.json;
|
||||
$package = Get-Content -Raw -Path $env:GITHUB_WORKSPACE\electron-builder.json | ConvertFrom-Json;
|
||||
$package | Add-Member -MemberType NoteProperty -Name buildVersion -Value "$env:BUILD_NUMBER";
|
||||
$package | ConvertTo-Json -Depth 32 | Set-Content $env:GITHUB_WORKSPACE\electron-builder.json;
|
||||
|
||||
- name: Cache Native Module
|
||||
uses: actions/cache@48af2dc4a9e8278b89d7fa154b955c30c6aaab09 # v3.0.2
|
||||
id: cache
|
||||
with:
|
||||
path: desktop_native/*.node
|
||||
key: rust-${{ runner.os }}-${{ hashFiles('desktop_native/**/*') }}
|
||||
|
||||
- name: Build Native Module
|
||||
if: steps.cache.outputs.cache-hit != 'true'
|
||||
working-directory: './desktop_native'
|
||||
run: |
|
||||
npm ci
|
||||
npm run build:cross-platform
|
||||
|
||||
- name: NPM install
|
||||
run: npm ci
|
||||
|
@ -844,7 +937,7 @@ jobs:
|
|||
uses: actions/checkout@a12a3943b4bdde767164f792f33f40b04645d846
|
||||
with:
|
||||
repository: 'bitwarden/browser'
|
||||
path: 'dist-safari/browser'
|
||||
path: 'dist-safari/bitwarden'
|
||||
ref: ${{ needs.setup.outputs.safari_ref }}
|
||||
|
||||
- name: Build Safari extension
|
||||
|
@ -852,12 +945,13 @@ jobs:
|
|||
run: |
|
||||
npm install
|
||||
npm run dist:safari
|
||||
working-directory: dist-safari/browser
|
||||
working-directory: dist-safari/bitwarden/apps/browser
|
||||
|
||||
- name: Load Safari extension for App Store
|
||||
working-directory: dist-safari/bitwarden/apps/browser
|
||||
run: |
|
||||
mkdir PlugIns
|
||||
cp -r dist-safari/browser/dist/Safari/mas/build/Release/safari.appex PlugIns/safari.appex
|
||||
cp -r dist/Safari/mas/build/Release/safari.appex PlugIns/safari.appex
|
||||
|
||||
- name: Build application for App Store
|
||||
run: npm run pack:mac:mas
|
||||
|
@ -1002,9 +1096,23 @@ jobs:
|
|||
env:
|
||||
BUILD_NUMBER: ${{ needs.setup.outputs.build_number }}
|
||||
run: |
|
||||
$package = Get-Content -Raw -Path $env:GITHUB_WORKSPACE\package.json | ConvertFrom-Json;
|
||||
$package.build | Add-Member -MemberType NoteProperty -Name buildVersion -Value "$env:BUILD_NUMBER";
|
||||
$package | ConvertTo-Json -Depth 32 | Set-Content $env:GITHUB_WORKSPACE\package.json;
|
||||
$package = Get-Content -Raw -Path $env:GITHUB_WORKSPACE\electron-builder.json | ConvertFrom-Json;
|
||||
$package | Add-Member -MemberType NoteProperty -Name buildVersion -Value "$env:BUILD_NUMBER";
|
||||
$package | ConvertTo-Json -Depth 32 | Set-Content $env:GITHUB_WORKSPACE\electron-builder.json;
|
||||
|
||||
- name: Cache Native Module
|
||||
uses: actions/cache@48af2dc4a9e8278b89d7fa154b955c30c6aaab09 # v3.0.2
|
||||
id: cache
|
||||
with:
|
||||
path: desktop_native/*.node
|
||||
key: rust-${{ runner.os }}-${{ hashFiles('desktop_native/**/*') }}
|
||||
|
||||
- name: Build Native Module
|
||||
if: steps.cache.outputs.cache-hit != 'true'
|
||||
working-directory: './desktop_native'
|
||||
run: |
|
||||
npm ci
|
||||
npm run build:cross-platform
|
||||
|
||||
- name: NPM install
|
||||
run: npm ci
|
||||
|
@ -1023,7 +1131,7 @@ jobs:
|
|||
uses: actions/checkout@a12a3943b4bdde767164f792f33f40b04645d846
|
||||
with:
|
||||
repository: 'bitwarden/browser'
|
||||
path: 'dist-safari/browser'
|
||||
path: 'dist-safari/bitwarden'
|
||||
ref: ${{ needs.setup.outputs.safari_ref }}
|
||||
|
||||
- name: Build Safari extension
|
||||
|
@ -1031,12 +1139,13 @@ jobs:
|
|||
run: |
|
||||
npm install
|
||||
npm run dist:safari
|
||||
working-directory: dist-safari/browser
|
||||
working-directory: dist-safari/bitwarden/apps/browser
|
||||
|
||||
- name: Load Safari extension for App Store
|
||||
working-directory: dist-safari/bitwarden/apps/browser
|
||||
run: |
|
||||
mkdir PlugIns
|
||||
cp -r dist-safari/browser/dist/Safari/masdev/build/Release/safari.appex PlugIns/safari.appex
|
||||
cp -r dist/Safari/masdev/build/Release/safari.appex PlugIns/safari.appex
|
||||
|
||||
- name: Build dev application for App Store
|
||||
run: npm run pack:mac:masdev
|
||||
|
|
|
@ -62,48 +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
|
||||
if: ${{ github.event.inputs.release_type != 'Dry Run' }}
|
||||
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.archive"
|
||||
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 }}
|
||||
|
@ -142,7 +163,7 @@ jobs:
|
|||
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
|
||||
|
@ -150,9 +171,6 @@ 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: |
|
||||
|
@ -179,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
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
build
|
||||
dist
|
||||
dist-safari
|
||||
desktop_native
|
||||
|
||||
jslib
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@ Here is how you can get involved:
|
|||
- **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
|
||||
- **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
|
||||
|
@ -31,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/
|
||||
|
|
17
README.md
17
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,20 +16,23 @@ The Bitwarden desktop app is written using Electron and Angular. The application
|
|||
|
||||
# Build/Run
|
||||
|
||||
**Requirements**
|
||||
## Requirements
|
||||
|
||||
- [Node.js](https://nodejs.org) v16.13.1 (LTS) or greater
|
||||
- NPM v8
|
||||
- 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.
|
||||
- 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
|
||||
|
|
|
@ -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
2
jslib
|
@ -1 +1 @@
|
|||
Subproject commit 4d58200ee90fb4fafa5d4f9f4c957654d4da306d
|
||||
Subproject commit 80c834b52a8b00f88250a47a9bbd40c269fc2cba
|
File diff suppressed because it is too large
Load Diff
199
package.json
199
package.json
|
@ -63,199 +63,6 @@
|
|||
"prettier": "prettier --write .",
|
||||
"prepare": "husky install"
|
||||
},
|
||||
"build": {
|
||||
"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"
|
||||
],
|
||||
"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"
|
||||
],
|
||||
"publish": [
|
||||
"github"
|
||||
]
|
||||
},
|
||||
"protocols": [
|
||||
{
|
||||
"name": "Bitwarden",
|
||||
"schemes": [
|
||||
"bitwarden"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular/compiler-cli": "^12.2.13",
|
||||
"@ngtools/webpack": "^12.2.13",
|
||||
|
@ -307,12 +114,16 @@
|
|||
"@bitwarden/jslib-angular": "file:jslib/angular",
|
||||
"@bitwarden/jslib-common": "file:jslib/common",
|
||||
"@bitwarden/jslib-electron": "file:jslib/electron",
|
||||
"@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",
|
||||
"rxjs": "^7.4.0",
|
||||
"sweetalert2": "^10.16.6"
|
||||
"sweetalert2": "^10.16.6",
|
||||
"zone.js": "0.11.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": "~16",
|
||||
|
|
|
@ -83,7 +83,7 @@
|
|||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button appBlurClick type="submit" class="primary" appA11yTitle="{{ 'save' | i18n }}">
|
||||
<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>
|
||||
|
|
|
@ -21,11 +21,11 @@
|
|||
</div>
|
||||
</div>
|
||||
<div class="buttons">
|
||||
<button type="submit" class="btn primary block" [disabled]="form.loading" appBlurClick>
|
||||
<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>
|
||||
<a routerLink="/login" class="btn block">{{ "cancel" | i18n }}</a>
|
||||
<button type="button" routerLink="/login" class="btn block">{{ "cancel" | i18n }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
|
|
@ -30,12 +30,10 @@
|
|||
/>
|
||||
</div>
|
||||
<div class="action-buttons">
|
||||
<a
|
||||
<button
|
||||
type="button"
|
||||
class="row-btn"
|
||||
href="#"
|
||||
appStopClick
|
||||
appBlurClick
|
||||
role="button"
|
||||
appA11yTitle="{{ 'toggleVisibility' | i18n }}"
|
||||
[attr.aria-pressed]="showPassword"
|
||||
(click)="togglePassword()"
|
||||
|
@ -45,7 +43,7 @@
|
|||
aria-hidden="true"
|
||||
[ngClass]="{ 'bwi-eye': !showPassword, 'bwi-eye-slash': showPassword }"
|
||||
></i>
|
||||
</a>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -59,17 +57,16 @@
|
|||
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">
|
||||
<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" appBlurClick (click)="logOut()">
|
||||
<button type="button" class="btn block" (click)="logOut()">
|
||||
{{ "logOut" | i18n }}
|
||||
</button>
|
||||
</div>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<div id="login-page">
|
||||
<div class="login-header">
|
||||
<a
|
||||
href="#"
|
||||
<button
|
||||
type="button"
|
||||
appStopClick
|
||||
(click)="settings()"
|
||||
class="environment-urls-settings-icon"
|
||||
|
@ -9,7 +9,7 @@
|
|||
>
|
||||
<i class="bwi bwi-cog bwi-lg" aria-hidden="true"></i>
|
||||
{{ "settings" | i18n }}
|
||||
</a>
|
||||
</button>
|
||||
</div>
|
||||
<form
|
||||
id="login-page"
|
||||
|
@ -48,12 +48,10 @@
|
|||
/>
|
||||
</div>
|
||||
<div class="action-buttons">
|
||||
<a
|
||||
<button
|
||||
type="button"
|
||||
class="row-btn"
|
||||
href="#"
|
||||
appStopClick
|
||||
appBlurClick
|
||||
role="button"
|
||||
appA11yTitle="{{ 'toggleVisibility' | i18n }}"
|
||||
[attr.aria-pressed]="showPassword"
|
||||
(click)="togglePassword()"
|
||||
|
@ -63,7 +61,7 @@
|
|||
aria-hidden="true"
|
||||
[ngClass]="{ 'bwi-eye': !showPassword, 'bwi-eye-slash': showPassword }"
|
||||
></i>
|
||||
</a>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -77,24 +75,28 @@
|
|||
</div>
|
||||
<div class="buttons with-rows">
|
||||
<div class="buttons-row">
|
||||
<button type="submit" class="btn primary block" [disabled]="form.loading" appBlurClick>
|
||||
<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>
|
||||
<a routerLink="/register" class="btn block">
|
||||
<button type="button" routerLink="/register" class="btn block">
|
||||
<i class="bwi bwi-pencil-square" aria-hidden="true"></i> {{ "createAccount" | i18n }}
|
||||
</a>
|
||||
</button>
|
||||
</div>
|
||||
<div class="buttons-row">
|
||||
<a (click)="launchSsoBrowser('desktop', 'bitwarden://sso-callback')" class="btn block">
|
||||
<button
|
||||
type="button"
|
||||
(click)="launchSsoBrowser('desktop', 'bitwarden://sso-callback')"
|
||||
class="btn block"
|
||||
>
|
||||
<i class="bwi bwi-bank" aria-hidden="true"></i> {{ "enterpriseSingleSignOn" | i18n }}
|
||||
</a>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sub-options">
|
||||
<a routerLink="/hint">{{ "getMasterPasswordHint" | i18n }}</a>
|
||||
<button type="button" routerLink="/hint">{{ "getMasterPasswordHint" | i18n }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
|
|
@ -48,14 +48,13 @@
|
|||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="primary" appBlurClick (click)="manage()" *ngIf="isPremium">
|
||||
<button type="button" class="primary" (click)="manage()" *ngIf="isPremium">
|
||||
<b>{{ "premiumManage" | i18n }}</b>
|
||||
</button>
|
||||
<button
|
||||
#purchaseBtn
|
||||
type="button"
|
||||
class="primary"
|
||||
appBlurClick
|
||||
(click)="purchase()"
|
||||
*ngIf="!isPremium"
|
||||
[disabled]="purchaseBtn.loading"
|
||||
|
@ -67,7 +66,6 @@
|
|||
<button
|
||||
#refreshBtn
|
||||
type="button"
|
||||
appBlurClick
|
||||
(click)="refresh()"
|
||||
[disabled]="refreshBtn.loading"
|
||||
appA11yTitle="{{ 'premiumRefresh' | i18n }}"
|
||||
|
|
|
@ -40,12 +40,10 @@
|
|||
/>
|
||||
</div>
|
||||
<div class="action-buttons">
|
||||
<a
|
||||
<button
|
||||
type="button"
|
||||
class="row-btn"
|
||||
href="#"
|
||||
appStopClick
|
||||
appBlurClick
|
||||
role="button"
|
||||
appA11yTitle="{{ 'toggleVisibility' | i18n }}"
|
||||
[attr.aria-pressed]="showPassword"
|
||||
(click)="togglePassword(false)"
|
||||
|
@ -55,7 +53,7 @@
|
|||
aria-hidden="true"
|
||||
[ngClass]="{ 'bwi-eye': !showPassword, 'bwi-eye-slash': showPassword }"
|
||||
></i>
|
||||
</a>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="progress">
|
||||
|
@ -91,12 +89,10 @@
|
|||
/>
|
||||
</div>
|
||||
<div class="action-buttons">
|
||||
<a
|
||||
<button
|
||||
type="button"
|
||||
class="row-btn"
|
||||
href="#"
|
||||
appStopClick
|
||||
appBlurClick
|
||||
role="button"
|
||||
appA11yTitle="{{ 'toggleVisibility' | i18n }}"
|
||||
[attr.aria-pressed]="showPassword"
|
||||
(click)="togglePassword(true)"
|
||||
|
@ -106,7 +102,7 @@
|
|||
aria-hidden="true"
|
||||
[ngClass]="{ 'bwi-eye': !showPassword, 'bwi-eye-slash': showPassword }"
|
||||
></i>
|
||||
</a>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-content-row" appBoxRow>
|
||||
|
@ -142,11 +138,11 @@
|
|||
</div>
|
||||
</div>
|
||||
<div class="buttons">
|
||||
<button type="submit" class="btn primary block" [disabled]="form.loading" appBlurClick>
|
||||
<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>
|
||||
<a routerLink="/login" class="btn block">{{ "cancel" | i18n }}</a>
|
||||
<button type="button" routerLink="/login" class="btn block">{{ "cancel" | i18n }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
type="submit"
|
||||
class="btn primary block"
|
||||
[disabled]="actionPromise"
|
||||
appBlurClick
|
||||
(click)="convert()"
|
||||
>
|
||||
<b [hidden]="continuing">{{ "removeMasterPassword" | i18n }}</b>
|
||||
|
@ -17,7 +16,6 @@
|
|||
type="button"
|
||||
class="btn secondary block"
|
||||
[disabled]="actionPromise"
|
||||
appBlurClick
|
||||
(click)="leave()"
|
||||
>
|
||||
<b [hidden]="leaving">{{ "leaveOrganization" | i18n }}</b>
|
||||
|
|
|
@ -56,12 +56,10 @@
|
|||
/>
|
||||
</div>
|
||||
<div class="action-buttons">
|
||||
<a
|
||||
<button
|
||||
type="button"
|
||||
class="row-btn"
|
||||
href="#"
|
||||
appStopClick
|
||||
appBlurClick
|
||||
role="button"
|
||||
appA11yTitle="{{ 'toggleVisibility' | i18n }}"
|
||||
[attr.aria-pressed]="showPassword"
|
||||
(click)="togglePassword(false)"
|
||||
|
@ -71,7 +69,7 @@
|
|||
aria-hidden="true"
|
||||
[ngClass]="{ 'bwi-eye': !showPassword, 'bwi-eye-slash': showPassword }"
|
||||
></i>
|
||||
</a>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="progress">
|
||||
|
@ -109,12 +107,10 @@
|
|||
/>
|
||||
</div>
|
||||
<div class="action-buttons">
|
||||
<a
|
||||
<button
|
||||
type="button"
|
||||
class="row-btn"
|
||||
href="#"
|
||||
appStopClick
|
||||
appBlurClick
|
||||
role="button"
|
||||
appA11yTitle="{{ 'toggleVisibility' | i18n }}"
|
||||
[attr.aria-pressed]="showPassword"
|
||||
(click)="togglePassword(true)"
|
||||
|
@ -124,7 +120,7 @@
|
|||
aria-hidden="true"
|
||||
[ngClass]="{ 'bwi-eye': !showPassword, 'bwi-eye-slash': showPassword }"
|
||||
></i>
|
||||
</a>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -7,8 +7,8 @@
|
|||
{{ "twoStepOptions" | i18n }}
|
||||
</div>
|
||||
<div class="box-content">
|
||||
<a
|
||||
href="#"
|
||||
<button
|
||||
type="button"
|
||||
appStopClick
|
||||
*ngFor="let p of providers"
|
||||
class="box-content-row"
|
||||
|
@ -17,11 +17,11 @@
|
|||
<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()">
|
||||
</button>
|
||||
<button type="button" appStopClick class="box-content-row" (click)="recover()">
|
||||
<span class="text">{{ "recoveryCodeTitle" | i18n }}</span>
|
||||
<span class="detail">{{ "recoveryCodeDesc" | i18n }}</span>
|
||||
</a>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -112,7 +112,6 @@
|
|||
type="submit"
|
||||
class="btn primary block"
|
||||
[disabled]="form.loading"
|
||||
appBlurClick
|
||||
*ngIf="
|
||||
selectedProviderType != null &&
|
||||
selectedProviderType !== providerType.Duo &&
|
||||
|
@ -124,22 +123,21 @@
|
|||
>
|
||||
<i class="bwi bwi-spinner bwi-spin" [hidden]="!form.loading" aria-hidden="true"></i>
|
||||
</button>
|
||||
<a routerLink="/login" class="btn block">{{ "cancel" | i18n }}</a>
|
||||
<button type="button" routerLink="/login" class="btn block">{{ "cancel" | i18n }}</button>
|
||||
</div>
|
||||
<div class="sub-options">
|
||||
<a href="#" appStopClick (click)="anotherMethod()" role="button">{{
|
||||
"useAnotherTwoStepMethod" | i18n
|
||||
}}</a>
|
||||
<a
|
||||
href="#"
|
||||
<button type="button" appStopClick (click)="anotherMethod()">
|
||||
{{ "useAnotherTwoStepMethod" | i18n }}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
appStopClick
|
||||
(click)="sendEmail(true)"
|
||||
[appApiAction]="emailPromise"
|
||||
role="button"
|
||||
*ngIf="selectedProviderType === providerType.Email"
|
||||
>
|
||||
{{ "sendVerificationCodeEmailAgain" | i18n }}
|
||||
</a>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
|
|
@ -4,6 +4,7 @@ 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";
|
||||
|
@ -38,7 +39,8 @@ export class TwoFactorComponent extends BaseTwoFactorComponent {
|
|||
stateService: StateService,
|
||||
route: ActivatedRoute,
|
||||
logService: LogService,
|
||||
twoFactorService: TwoFactorService
|
||||
twoFactorService: TwoFactorService,
|
||||
appIdService: AppIdService
|
||||
) {
|
||||
super(
|
||||
authService,
|
||||
|
@ -51,7 +53,8 @@ export class TwoFactorComponent extends BaseTwoFactorComponent {
|
|||
stateService,
|
||||
route,
|
||||
logService,
|
||||
twoFactorService
|
||||
twoFactorService,
|
||||
appIdService
|
||||
);
|
||||
super.onSuccessfulLogin = () => {
|
||||
return syncService.fullSync(true);
|
||||
|
|
|
@ -36,12 +36,10 @@
|
|||
/>
|
||||
</div>
|
||||
<div class="action-buttons">
|
||||
<a
|
||||
<button
|
||||
type="button"
|
||||
class="row-btn"
|
||||
href="#"
|
||||
appStopClick
|
||||
appBlurClick
|
||||
role="button"
|
||||
appA11yTitle="{{ 'toggleVisibility' | i18n }}"
|
||||
[attr.aria-pressed]="showPassword"
|
||||
(click)="togglePassword(false)"
|
||||
|
@ -51,7 +49,7 @@
|
|||
aria-hidden="true"
|
||||
[ngClass]="{ 'bwi-eye': !showPassword, 'bwi-eye-slash': showPassword }"
|
||||
></i>
|
||||
</a>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="progress">
|
||||
|
@ -84,12 +82,10 @@
|
|||
/>
|
||||
</div>
|
||||
<div class="action-buttons">
|
||||
<a
|
||||
<button
|
||||
type="button"
|
||||
class="row-btn"
|
||||
href="#"
|
||||
appStopClick
|
||||
appBlurClick
|
||||
role="button"
|
||||
appA11yTitle="{{ 'toggleVisibility' | i18n }}"
|
||||
[attr.aria-pressed]="showPassword"
|
||||
(click)="togglePassword(true)"
|
||||
|
@ -99,7 +95,7 @@
|
|||
aria-hidden="true"
|
||||
[ngClass]="{ 'bwi-eye': !showPassword, 'bwi-eye-slash': showPassword }"
|
||||
></i>
|
||||
</a>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -116,11 +112,11 @@
|
|||
</div>
|
||||
</div>
|
||||
<div class="buttons">
|
||||
<button type="submit" class="btn primary block" [disabled]="form.loading" appBlurClick>
|
||||
<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>
|
||||
<a (click)="logOut()" class="btn block">{{ "logOut" | i18n }}</a>
|
||||
<button type="button" (click)="logOut()" class="btn block">{{ "logOut" | i18n }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
|
|
@ -35,9 +35,10 @@ import { SyncService } from "jslib-common/abstractions/sync.service";
|
|||
import { SystemService } from "jslib-common/abstractions/system.service";
|
||||
import { TokenService } from "jslib-common/abstractions/token.service";
|
||||
import { VaultTimeoutService } from "jslib-common/abstractions/vaultTimeout.service";
|
||||
import { AuthenticationStatus } from "jslib-common/enums/authenticationStatus";
|
||||
import { CipherType } from "jslib-common/enums/cipherType";
|
||||
|
||||
import { MenuUpdateRequest } from "src/main/menu.updater";
|
||||
import { MenuUpdateRequest } from "../main/menu/menu.updater";
|
||||
|
||||
import { PremiumComponent } from "./accounts/premium.component";
|
||||
import { SettingsComponent } from "./accounts/settings.component";
|
||||
|
@ -330,7 +331,9 @@ export class AppComponent implements OnInit {
|
|||
if (message.userId != null) {
|
||||
await this.stateService.setActiveUser(message.userId);
|
||||
}
|
||||
const locked = await this.vaultTimeoutService.isLocked(message.userId);
|
||||
const locked =
|
||||
(await this.authService.getAuthStatus(message.userId)) ===
|
||||
AuthenticationStatus.Locked;
|
||||
if (locked) {
|
||||
this.messagingService.send("locked", { userId: message.userId });
|
||||
} else {
|
||||
|
@ -436,7 +439,8 @@ export class AppComponent implements OnInit {
|
|||
isAuthenticated: await this.stateService.getIsAuthenticated({
|
||||
userId: userId,
|
||||
}),
|
||||
isLocked: await this.vaultTimeoutService.isLocked(userId),
|
||||
isLocked:
|
||||
(await this.authService.getAuthStatus(userId)) === AuthenticationStatus.Locked,
|
||||
email: stateAccounts[i].profile.email,
|
||||
userId: stateAccounts[i].profile.userId,
|
||||
};
|
||||
|
@ -591,7 +595,7 @@ export class AppComponent implements OnInit {
|
|||
const keys = Object.keys(accounts);
|
||||
if (keys.length > 0) {
|
||||
for (const userId of keys) {
|
||||
if (!(await this.vaultTimeoutService.isLocked(userId))) {
|
||||
if ((await this.authService.getAuthStatus(userId)) === AuthenticationStatus.Unlocked) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -83,7 +83,7 @@ import { AppRoutingModule } from "./app-routing.module";
|
|||
import { AppComponent } from "./app.component";
|
||||
import { PasswordRepromptComponent } from "./components/password-reprompt.component";
|
||||
import { SetPinComponent } from "./components/set-pin.component";
|
||||
import { VerifyMasterPasswordComponent } from "./components/verify-master-password.component";
|
||||
import { UserVerificationComponent } from "./components/user-verification.component";
|
||||
import { AccountSwitcherComponent } from "./layout/account-switcher.component";
|
||||
import { HeaderComponent } from "./layout/header.component";
|
||||
import { NavComponent } from "./layout/nav.component";
|
||||
|
@ -91,7 +91,7 @@ import { SearchComponent } from "./layout/search/search.component";
|
|||
import { AddEditComponent as SendAddEditComponent } from "./send/add-edit.component";
|
||||
import { EffluxDatesComponent as SendEffluxDatesComponent } from "./send/efflux-dates.component";
|
||||
import { SendComponent } from "./send/send.component";
|
||||
import { ServicesModule } from "./services.module";
|
||||
import { ServicesModule } from "./services/services.module";
|
||||
import { AddEditCustomFieldsComponent } from "./vault/add-edit-custom-fields.component";
|
||||
import { AddEditComponent } from "./vault/add-edit.component";
|
||||
import { AttachmentsComponent } from "./vault/attachments.component";
|
||||
|
@ -212,9 +212,9 @@ registerLocaleData(localeZhTw, "zh-TW");
|
|||
TwoFactorComponent,
|
||||
TwoFactorOptionsComponent,
|
||||
UpdateTempPasswordComponent,
|
||||
UserVerificationComponent,
|
||||
VaultComponent,
|
||||
VaultTimeoutInputComponent,
|
||||
VerifyMasterPasswordComponent,
|
||||
ViewComponent,
|
||||
ViewCustomFieldsComponent,
|
||||
],
|
||||
|
|
|
@ -19,12 +19,10 @@
|
|||
/>
|
||||
</div>
|
||||
<div class="action-buttons">
|
||||
<a
|
||||
<button
|
||||
type="button"
|
||||
class="row-btn"
|
||||
href="#"
|
||||
appStopClick
|
||||
appBlurClick
|
||||
role="button"
|
||||
appA11yTitle="{{ 'toggleVisibility' | i18n }}"
|
||||
[attr.aria-pressed]="showPassword"
|
||||
(click)="togglePassword()"
|
||||
|
@ -34,7 +32,7 @@
|
|||
aria-hidden="true"
|
||||
[ngClass]="{ 'bwi-eye': !showPassword, 'bwi-eye-slash': showPassword }"
|
||||
></i>
|
||||
</a>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -44,7 +42,7 @@
|
|||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="submit" class="btn btn-primary btn-submit" appBlurClick>
|
||||
<button type="submit" class="btn btn-primary btn-submit">
|
||||
<span>{{ "ok" | i18n }}</span>
|
||||
</button>
|
||||
<button type="button" class="btn btn-outline-secondary" data-dismiss="modal">
|
||||
|
|
|
@ -22,11 +22,10 @@
|
|||
/>
|
||||
</div>
|
||||
<div class="action-buttons">
|
||||
<a
|
||||
<button
|
||||
type="button"
|
||||
class="row-btn"
|
||||
href="#"
|
||||
appStopClick
|
||||
appBlurClick
|
||||
appA11yTitle="{{ 'toggleVisibility' | i18n }}"
|
||||
[attr.aria-pressed]="showPin"
|
||||
(click)="toggleVisibility()"
|
||||
|
@ -36,7 +35,7 @@
|
|||
aria-hidden="true"
|
||||
[ngClass]="{ 'bwi-eye': !showPin, 'bwi-eye-slash': showPin }"
|
||||
></i>
|
||||
</a>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -2,16 +2,16 @@ import { animate, style, transition, trigger } from "@angular/animations";
|
|||
import { Component } from "@angular/core";
|
||||
import { NG_VALUE_ACCESSOR } from "@angular/forms";
|
||||
|
||||
import { VerifyMasterPasswordComponent as BaseComponent } from "jslib-angular/components/verify-master-password.component";
|
||||
import { UserVerificationComponent as BaseComponent } from "jslib-angular/components/user-verification.component";
|
||||
|
||||
@Component({
|
||||
selector: "app-verify-master-password",
|
||||
templateUrl: "verify-master-password.component.html",
|
||||
selector: "app-user-verification",
|
||||
templateUrl: "user-verification.component.html",
|
||||
providers: [
|
||||
{
|
||||
provide: NG_VALUE_ACCESSOR,
|
||||
multi: true,
|
||||
useExisting: VerifyMasterPasswordComponent,
|
||||
useExisting: UserVerificationComponent,
|
||||
},
|
||||
],
|
||||
animations: [
|
||||
|
@ -20,4 +20,4 @@ import { VerifyMasterPasswordComponent as BaseComponent } from "jslib-angular/co
|
|||
]),
|
||||
],
|
||||
})
|
||||
export class VerifyMasterPasswordComponent extends BaseComponent {}
|
||||
export class UserVerificationComponent extends BaseComponent {}
|
|
@ -53,7 +53,6 @@
|
|||
<button
|
||||
*ngFor="let a of accounts | keyvalue"
|
||||
class="account"
|
||||
[ngClass]="{ active: a.value.profile.authenticationStatus == 'active' }"
|
||||
(click)="switch(a.key)"
|
||||
appA11yTitle="{{ 'loggedInAsOn' | i18n: a.value.profile.email:a.value.serverUrl }}"
|
||||
attr.aria-label="{{ 'switchAccount' | i18n }}"
|
||||
|
@ -72,17 +71,17 @@
|
|||
<span class="server" aria-hidden="true" *ngIf="a.value.serverUrl != 'bitwarden.com'">{{
|
||||
a.value.serverUrl
|
||||
}}</span>
|
||||
<span class="status" aria-hidden="true">{{ a.value.profile.authenticationStatus }}</span>
|
||||
<span class="status" aria-hidden="true">{{
|
||||
(a.value.profile.authenticationStatus === authStatus.Unlocked ? "unlocked" : "locked")
|
||||
| i18n
|
||||
}}</span>
|
||||
</div>
|
||||
<i
|
||||
class="bwi bwi-unlock bwi-2x text-muted"
|
||||
class="bwi bwi-2x text-muted"
|
||||
[ngClass]="
|
||||
a.value.profile.authenticationStatus == authStatus.Unlocked ? 'bwi-unlock' : 'bwi-lock'
|
||||
"
|
||||
aria-hidden="true"
|
||||
*ngIf="a.value.profile.authenticationStatus == 'unlocked'"
|
||||
></i>
|
||||
<i
|
||||
class="bwi bwi-lock bwi-2x text-muted"
|
||||
aria-hidden="true"
|
||||
*ngIf="a.value.profile.authenticationStatus == 'locked'"
|
||||
></i>
|
||||
</button>
|
||||
</div>
|
||||
|
|
|
@ -2,9 +2,9 @@ import { animate, state, style, transition, trigger } from "@angular/animations"
|
|||
import { ConnectedPosition } from "@angular/cdk/overlay";
|
||||
import { Component, OnInit } from "@angular/core";
|
||||
|
||||
import { AuthService } from "jslib-common/abstractions/auth.service";
|
||||
import { MessagingService } from "jslib-common/abstractions/messaging.service";
|
||||
import { StateService } from "jslib-common/abstractions/state.service";
|
||||
import { VaultTimeoutService } from "jslib-common/abstractions/vaultTimeout.service";
|
||||
import { AuthenticationStatus } from "jslib-common/enums/authenticationStatus";
|
||||
import { Utils } from "jslib-common/misc/utils";
|
||||
import { Account } from "jslib-common/models/domain/account";
|
||||
|
@ -53,6 +53,7 @@ export class AccountSwitcherComponent implements OnInit {
|
|||
accounts: { [userId: string]: SwitcherAccount } = {};
|
||||
activeAccountEmail: string;
|
||||
serverUrl: string;
|
||||
authStatus = AuthenticationStatus;
|
||||
overlayPostition: ConnectedPosition[] = [
|
||||
{
|
||||
originX: "end",
|
||||
|
@ -78,22 +79,16 @@ export class AccountSwitcherComponent implements OnInit {
|
|||
|
||||
constructor(
|
||||
private stateService: StateService,
|
||||
private vaultTimeoutService: VaultTimeoutService,
|
||||
private authService: AuthService,
|
||||
private messagingService: MessagingService
|
||||
) {}
|
||||
|
||||
async ngOnInit(): Promise<void> {
|
||||
this.stateService.accounts.subscribe(async (accounts) => {
|
||||
this.stateService.accounts.subscribe(async (accounts: { [userId: string]: Account }) => {
|
||||
for (const userId in accounts) {
|
||||
if (userId === (await this.stateService.getUserId())) {
|
||||
accounts[userId].profile.authenticationStatus = AuthenticationStatus.Active;
|
||||
} else {
|
||||
accounts[userId].profile.authenticationStatus = (await this.vaultTimeoutService.isLocked(
|
||||
userId
|
||||
))
|
||||
? AuthenticationStatus.Locked
|
||||
: AuthenticationStatus.Unlocked;
|
||||
}
|
||||
accounts[userId].profile.authenticationStatus = await this.authService.getAuthStatus(
|
||||
userId
|
||||
);
|
||||
}
|
||||
|
||||
this.accounts = await this.createSwitcherAccounts(accounts);
|
||||
|
|
|
@ -1,5 +1,13 @@
|
|||
<ng-container *ngFor="let item of items">
|
||||
<a [routerLink]="item.link" class="btn primary" routerLinkActive="active" [title]="item.label">
|
||||
<button
|
||||
type="button"
|
||||
[routerLink]="item.link"
|
||||
class="btn primary"
|
||||
routerLinkActive="active"
|
||||
[title]="item.label"
|
||||
#rla="routerLinkActive"
|
||||
[attr.aria-pressed]="rla.isActive"
|
||||
>
|
||||
<i class="bwi" [ngClass]="item.icon"></i>{{ item.label }}
|
||||
</a>
|
||||
</button>
|
||||
</ng-container>
|
||||
|
|
|
@ -93,21 +93,20 @@
|
|||
</div>
|
||||
<div class="box">
|
||||
<div class="box-header">
|
||||
{{ "options" | i18n }}
|
||||
<a
|
||||
<button
|
||||
type="button"
|
||||
class="toggle"
|
||||
href="#"
|
||||
appStopClick
|
||||
appBlurClick
|
||||
role="button"
|
||||
(click)="toggleOptions()"
|
||||
[attr.aria-expanded]="showOptions"
|
||||
>
|
||||
{{ "options" | i18n }}
|
||||
<i
|
||||
class="bwi bwi-lg"
|
||||
aria-hidden="true"
|
||||
[ngClass]="{ 'bwi-angle-down': !showOptions, 'bwi-chevron-up': showOptions }"
|
||||
></i>
|
||||
</a>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div [hidden]="!showOptions">
|
||||
|
@ -157,12 +156,10 @@
|
|||
/>
|
||||
</div>
|
||||
<div class="action-buttons">
|
||||
<a
|
||||
<button
|
||||
type="button"
|
||||
class="row-btn"
|
||||
href="#"
|
||||
appStopClick
|
||||
appBlurClick
|
||||
role="button"
|
||||
appA11yTitle="{{ 'toggleVisibility' | i18n }}"
|
||||
[attr.aria-pressed]="showPassword"
|
||||
(click)="togglePasswordVisible()"
|
||||
|
@ -173,7 +170,7 @@
|
|||
aria-hidden="true"
|
||||
[ngClass]="{ 'bwi-eye': !showPassword, 'bwi-eye-slash': showPassword }"
|
||||
></i>
|
||||
</a>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -254,7 +251,6 @@
|
|||
</div>
|
||||
<div class="footer">
|
||||
<button
|
||||
appBlurClick
|
||||
type="submit"
|
||||
class="primary btn-submit"
|
||||
appA11yTitle="{{ 'save' | i18n }}"
|
||||
|
@ -264,12 +260,11 @@
|
|||
<i class="bwi bwi-spinner bwi-spin" title="{{ 'loading' | i18n }}" aria-hidden="true"></i>
|
||||
<span><i class="bwi bwi-save-changes bwi-lg bwi-fw" aria-hidden="true"></i></span>
|
||||
</button>
|
||||
<button appBlurClick type="button" (click)="cancel()" [disabled]="form.loading">
|
||||
<button type="button" (click)="cancel()" [disabled]="form.loading">
|
||||
{{ "cancel" | i18n }}
|
||||
</button>
|
||||
<div class="right">
|
||||
<button
|
||||
appBlurClick
|
||||
type="button"
|
||||
(click)="copyLinkToClipboard(link)"
|
||||
appA11yTitle="{{ 'copySendLinkToClipboard' | i18n }}"
|
||||
|
@ -279,7 +274,6 @@
|
|||
</button>
|
||||
<button
|
||||
#deleteBtn
|
||||
appBlurClick
|
||||
type="button"
|
||||
(click)="delete()"
|
||||
class="danger"
|
||||
|
|
|
@ -5,26 +5,41 @@
|
|||
<h2 class="sr-only">{{ "filters" | i18n }}</h2>
|
||||
<ul>
|
||||
<li [ngClass]="{ active: selectedAll }">
|
||||
<a href="#" appStopClick appBlurClick (click)="selectAll()">
|
||||
<button
|
||||
type="button"
|
||||
appStopClick
|
||||
(click)="selectAll()"
|
||||
[attr.aria-pressed]="selectedAll"
|
||||
>
|
||||
<i class="bwi bwi-fw bwi-filter" aria-hidden="true"></i> {{ "allSends" | i18n }}
|
||||
</a>
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
<h2>{{ "types" | i18n }}</h2>
|
||||
<ul>
|
||||
<li [ngClass]="{ active: selectedType === sendType.Text }">
|
||||
<a href="#" appStopClick appBlurClick (click)="selectType(sendType.Text)">
|
||||
<button
|
||||
type="button"
|
||||
appStopClick
|
||||
(click)="selectType(sendType.Text)"
|
||||
[attr.aria-pressed]="selectedType === sendType.Text"
|
||||
>
|
||||
<i class="bwi bwi-fw bwi-file-text" aria-hidden="true"></i> {{
|
||||
"sendTypeText" | i18n
|
||||
}}
|
||||
</a>
|
||||
</button>
|
||||
</li>
|
||||
<li [ngClass]="{ active: selectedType === sendType.File }">
|
||||
<a href="#" appStopClick appBlurClick (click)="selectType(sendType.File)">
|
||||
<button
|
||||
type="button"
|
||||
appStopClick
|
||||
(click)="selectType(sendType.File)"
|
||||
[attr.aria-pressed]="selectedType === sendType.File"
|
||||
>
|
||||
<i class="bwi bwi-fw bwi-file" aria-hidden="true"></i> {{
|
||||
"sendTypeFile" | i18n
|
||||
}}
|
||||
</a>
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
@ -36,20 +51,21 @@
|
|||
<div id="items" class="items">
|
||||
<div class="content">
|
||||
<div class="list" *ngIf="filteredSends.length">
|
||||
<a
|
||||
<button
|
||||
*ngFor="let s of filteredSends"
|
||||
appStopClick
|
||||
(click)="selectSend(s.id)"
|
||||
title="{{ 'viewItem' | i18n }}"
|
||||
(contextmenu)="viewSendMenu(s)"
|
||||
[ngClass]="{ active: s.id === sendId }"
|
||||
[attr.aria-pressed]="s.id === sendId"
|
||||
class="flex-list-item"
|
||||
>
|
||||
<div class="item-icon" aria-hidden="true">
|
||||
<span class="item-icon" aria-hidden="true">
|
||||
<i class="bwi bwi-fw bwi-lg" [ngClass]="s.type == 0 ? 'bwi-file-text' : 'bwi-file'"></i>
|
||||
</div>
|
||||
<div class="item-content">
|
||||
<div class="item-title">
|
||||
</span>
|
||||
<span class="item-content">
|
||||
<span class="item-title">
|
||||
{{ s.name }}
|
||||
<span class="title-badges">
|
||||
<ng-container *ngIf="s.disabled">
|
||||
|
@ -98,10 +114,10 @@
|
|||
<span class="sr-only">{{ "pendingDeletion" | i18n }}</span>
|
||||
</ng-container>
|
||||
</span>
|
||||
</div>
|
||||
</span>
|
||||
<span class="item-details">{{ s.deletionDate | date }}</span>
|
||||
</div>
|
||||
</a>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="no-items" *ngIf="!filteredSends.length">
|
||||
<i class="bwi bwi-spinner bwi-spin bwi-3x" *ngIf="!loaded" aria-hidden="true"></i>
|
||||
|
@ -112,12 +128,7 @@
|
|||
</div>
|
||||
</div>
|
||||
<div class="footer">
|
||||
<button
|
||||
appBlurClick
|
||||
(click)="addSend()"
|
||||
class="block primary"
|
||||
appA11yTitle="{{ 'addItem' | i18n }}"
|
||||
>
|
||||
<button (click)="addSend()" class="block primary" appA11yTitle="{{ 'addItem' | i18n }}">
|
||||
<i class="bwi bwi-plus bwi-lg" aria-hidden="true"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
|
|
@ -1,211 +0,0 @@
|
|||
import { APP_INITIALIZER, NgModule } from "@angular/core";
|
||||
|
||||
import { JslibServicesModule } from "jslib-angular/services/jslib-services.module";
|
||||
import { BroadcasterService as BroadcasterServiceAbstraction } from "jslib-common/abstractions/broadcaster.service";
|
||||
import { CryptoService as CryptoServiceAbstraction } from "jslib-common/abstractions/crypto.service";
|
||||
import { CryptoFunctionService as CryptoFunctionServiceAbstraction } from "jslib-common/abstractions/cryptoFunction.service";
|
||||
import { EnvironmentService as EnvironmentServiceAbstraction } from "jslib-common/abstractions/environment.service";
|
||||
import { EventService as EventServiceAbstraction } from "jslib-common/abstractions/event.service";
|
||||
import { I18nService as I18nServiceAbstraction } from "jslib-common/abstractions/i18n.service";
|
||||
import { LogService as LogServiceAbstraction } from "jslib-common/abstractions/log.service";
|
||||
import { MessagingService as MessagingServiceAbstraction } from "jslib-common/abstractions/messaging.service";
|
||||
import { NotificationsService as NotificationsServiceAbstraction } from "jslib-common/abstractions/notifications.service";
|
||||
import { PasswordRepromptService as PasswordRepromptServiceAbstraction } from "jslib-common/abstractions/passwordReprompt.service";
|
||||
import { PlatformUtilsService as PlatformUtilsServiceAbstraction } from "jslib-common/abstractions/platformUtils.service";
|
||||
import { StateService as StateServiceAbstraction } from "jslib-common/abstractions/state.service";
|
||||
import { StateMigrationService as StateMigrationServiceAbstraction } from "jslib-common/abstractions/stateMigration.service";
|
||||
import { StorageService as StorageServiceAbstraction } from "jslib-common/abstractions/storage.service";
|
||||
import { SyncService as SyncServiceAbstraction } from "jslib-common/abstractions/sync.service";
|
||||
import { SystemService as SystemServiceAbstraction } from "jslib-common/abstractions/system.service";
|
||||
import { TwoFactorService as TwoFactorServiceAbstraction } from "jslib-common/abstractions/twoFactor.service";
|
||||
import { VaultTimeoutService as VaultTimeoutServiceAbstraction } from "jslib-common/abstractions/vaultTimeout.service";
|
||||
import { ThemeType } from "jslib-common/enums/themeType";
|
||||
import { StateFactory } from "jslib-common/factories/stateFactory";
|
||||
import { GlobalState } from "jslib-common/models/domain/globalState";
|
||||
import { ContainerService } from "jslib-common/services/container.service";
|
||||
import { EventService } from "jslib-common/services/event.service";
|
||||
import { StateMigrationService } from "jslib-common/services/stateMigration.service";
|
||||
import { SystemService } from "jslib-common/services/system.service";
|
||||
import { VaultTimeoutService } from "jslib-common/services/vaultTimeout.service";
|
||||
import { ElectronCryptoService } from "jslib-electron/services/electronCrypto.service";
|
||||
import { ElectronLogService } from "jslib-electron/services/electronLog.service";
|
||||
import { ElectronPlatformUtilsService } from "jslib-electron/services/electronPlatformUtils.service";
|
||||
import { ElectronRendererMessagingService } from "jslib-electron/services/electronRendererMessaging.service";
|
||||
import { ElectronRendererSecureStorageService } from "jslib-electron/services/electronRendererSecureStorage.service";
|
||||
import { ElectronRendererStorageService } from "jslib-electron/services/electronRendererStorage.service";
|
||||
|
||||
import { Account } from "../models/account";
|
||||
import { I18nService } from "../services/i18n.service";
|
||||
import { LoginGuardService } from "../services/loginGuard.service";
|
||||
import { NativeMessagingService } from "../services/nativeMessaging.service";
|
||||
import { PasswordRepromptService } from "../services/passwordReprompt.service";
|
||||
import { StateService } from "../services/state.service";
|
||||
|
||||
import { SearchBarService } from "./layout/search/search-bar.service";
|
||||
|
||||
export function initFactory(
|
||||
window: Window,
|
||||
environmentService: EnvironmentServiceAbstraction,
|
||||
syncService: SyncServiceAbstraction,
|
||||
vaultTimeoutService: VaultTimeoutService,
|
||||
i18nService: I18nService,
|
||||
eventService: EventService,
|
||||
twoFactorService: TwoFactorServiceAbstraction,
|
||||
notificationsService: NotificationsServiceAbstraction,
|
||||
platformUtilsService: PlatformUtilsServiceAbstraction,
|
||||
stateService: StateServiceAbstraction,
|
||||
cryptoService: CryptoServiceAbstraction,
|
||||
nativeMessagingService: NativeMessagingService
|
||||
): () => Promise<void> {
|
||||
return async () => {
|
||||
nativeMessagingService.init();
|
||||
await stateService.init();
|
||||
await environmentService.setUrlsFromStorage();
|
||||
syncService.fullSync(true);
|
||||
await vaultTimeoutService.init(true);
|
||||
const locale = await stateService.getLocale();
|
||||
await i18nService.init(locale);
|
||||
eventService.init(true);
|
||||
twoFactorService.init();
|
||||
setTimeout(() => notificationsService.init(), 3000);
|
||||
const htmlEl = window.document.documentElement;
|
||||
htmlEl.classList.add("os_" + platformUtilsService.getDeviceString());
|
||||
htmlEl.classList.add("locale_" + i18nService.translationLocale);
|
||||
const theme = await platformUtilsService.getEffectiveTheme();
|
||||
htmlEl.classList.add("theme_" + theme);
|
||||
platformUtilsService.onDefaultSystemThemeChange(async (sysTheme) => {
|
||||
const bwTheme = await stateService.getTheme();
|
||||
if (bwTheme == null || bwTheme === ThemeType.System) {
|
||||
htmlEl.classList.remove("theme_" + ThemeType.Light, "theme_" + ThemeType.Dark);
|
||||
htmlEl.classList.add("theme_" + sysTheme);
|
||||
}
|
||||
});
|
||||
|
||||
let installAction = null;
|
||||
const installedVersion = await stateService.getInstalledVersion();
|
||||
const currentVersion = await platformUtilsService.getApplicationVersion();
|
||||
if (installedVersion == null) {
|
||||
installAction = "install";
|
||||
} else if (installedVersion !== currentVersion) {
|
||||
installAction = "update";
|
||||
}
|
||||
|
||||
if (installAction != null) {
|
||||
await stateService.setInstalledVersion(currentVersion);
|
||||
}
|
||||
|
||||
const containerService = new ContainerService(cryptoService);
|
||||
containerService.attachToGlobal(window);
|
||||
};
|
||||
}
|
||||
|
||||
@NgModule({
|
||||
imports: [JslibServicesModule],
|
||||
declarations: [],
|
||||
providers: [
|
||||
{
|
||||
provide: APP_INITIALIZER,
|
||||
useFactory: initFactory,
|
||||
deps: [
|
||||
"WINDOW",
|
||||
EnvironmentServiceAbstraction,
|
||||
SyncServiceAbstraction,
|
||||
VaultTimeoutServiceAbstraction,
|
||||
I18nServiceAbstraction,
|
||||
EventServiceAbstraction,
|
||||
TwoFactorServiceAbstraction,
|
||||
NotificationsServiceAbstraction,
|
||||
PlatformUtilsServiceAbstraction,
|
||||
StateServiceAbstraction,
|
||||
CryptoServiceAbstraction,
|
||||
NativeMessagingService,
|
||||
],
|
||||
multi: true,
|
||||
},
|
||||
{ provide: LogServiceAbstraction, useClass: ElectronLogService, deps: [] },
|
||||
{
|
||||
provide: PlatformUtilsServiceAbstraction,
|
||||
useFactory: (
|
||||
i18nService: I18nServiceAbstraction,
|
||||
messagingService: MessagingServiceAbstraction,
|
||||
stateService: StateServiceAbstraction
|
||||
) => new ElectronPlatformUtilsService(i18nService, messagingService, true, stateService),
|
||||
deps: [I18nServiceAbstraction, MessagingServiceAbstraction, StateServiceAbstraction],
|
||||
},
|
||||
{
|
||||
provide: I18nServiceAbstraction,
|
||||
useFactory: (window: Window) => new I18nService(window.navigator.language, "./locales"),
|
||||
deps: ["WINDOW"],
|
||||
},
|
||||
{
|
||||
provide: MessagingServiceAbstraction,
|
||||
useClass: ElectronRendererMessagingService,
|
||||
deps: [BroadcasterServiceAbstraction],
|
||||
},
|
||||
{ provide: StorageServiceAbstraction, useClass: ElectronRendererStorageService },
|
||||
{ provide: "SECURE_STORAGE", useClass: ElectronRendererSecureStorageService },
|
||||
{
|
||||
provide: CryptoServiceAbstraction,
|
||||
useClass: ElectronCryptoService,
|
||||
deps: [
|
||||
CryptoFunctionServiceAbstraction,
|
||||
PlatformUtilsServiceAbstraction,
|
||||
LogServiceAbstraction,
|
||||
StateServiceAbstraction,
|
||||
],
|
||||
},
|
||||
{
|
||||
provide: SystemServiceAbstraction,
|
||||
useFactory: (
|
||||
messagingService: MessagingServiceAbstraction,
|
||||
platformUtilsService: PlatformUtilsServiceAbstraction,
|
||||
stateService: StateServiceAbstraction
|
||||
) => new SystemService(messagingService, platformUtilsService, null, stateService),
|
||||
deps: [MessagingServiceAbstraction, PlatformUtilsServiceAbstraction, StateServiceAbstraction],
|
||||
},
|
||||
{ provide: PasswordRepromptServiceAbstraction, useClass: PasswordRepromptService },
|
||||
NativeMessagingService,
|
||||
SearchBarService,
|
||||
{
|
||||
provide: LoginGuardService,
|
||||
useClass: LoginGuardService,
|
||||
deps: [StateServiceAbstraction, PlatformUtilsServiceAbstraction, I18nServiceAbstraction],
|
||||
},
|
||||
{
|
||||
provide: StateServiceAbstraction,
|
||||
useFactory: (
|
||||
storageService: StorageServiceAbstraction,
|
||||
secureStorageService: StorageServiceAbstraction,
|
||||
logService: LogServiceAbstraction,
|
||||
stateMigrationService: StateMigrationServiceAbstraction
|
||||
) =>
|
||||
new StateService(
|
||||
storageService,
|
||||
secureStorageService,
|
||||
logService,
|
||||
stateMigrationService,
|
||||
new StateFactory(GlobalState, Account)
|
||||
),
|
||||
deps: [
|
||||
StorageServiceAbstraction,
|
||||
"SECURE_STORAGE",
|
||||
LogServiceAbstraction,
|
||||
StateMigrationServiceAbstraction,
|
||||
],
|
||||
},
|
||||
{
|
||||
provide: StateMigrationServiceAbstraction,
|
||||
useFactory: (
|
||||
storageService: StorageServiceAbstraction,
|
||||
secureStorageService: StorageServiceAbstraction
|
||||
) =>
|
||||
new StateMigrationService(
|
||||
storageService,
|
||||
secureStorageService,
|
||||
new StateFactory(GlobalState, Account)
|
||||
),
|
||||
deps: [StorageServiceAbstraction, "SECURE_STORAGE"],
|
||||
},
|
||||
],
|
||||
})
|
||||
export class ServicesModule {}
|
|
@ -0,0 +1,81 @@
|
|||
import { Inject, Injectable } from "@angular/core";
|
||||
|
||||
import { WINDOW } from "jslib-angular/services/jslib-services.module";
|
||||
import { CryptoService as CryptoServiceAbstraction } from "jslib-common/abstractions/crypto.service";
|
||||
import { EnvironmentService as EnvironmentServiceAbstraction } from "jslib-common/abstractions/environment.service";
|
||||
import { EventService as EventServiceAbstraction } from "jslib-common/abstractions/event.service";
|
||||
import { I18nService as I18nServiceAbstraction } from "jslib-common/abstractions/i18n.service";
|
||||
import { NotificationsService as NotificationsServiceAbstraction } from "jslib-common/abstractions/notifications.service";
|
||||
import { PlatformUtilsService as PlatformUtilsServiceAbstraction } from "jslib-common/abstractions/platformUtils.service";
|
||||
import { StateService as StateServiceAbstraction } from "jslib-common/abstractions/state.service";
|
||||
import { SyncService as SyncServiceAbstraction } from "jslib-common/abstractions/sync.service";
|
||||
import { TwoFactorService as TwoFactorServiceAbstraction } from "jslib-common/abstractions/twoFactor.service";
|
||||
import { VaultTimeoutService as VaultTimeoutServiceAbstraction } from "jslib-common/abstractions/vaultTimeout.service";
|
||||
import { ThemeType } from "jslib-common/enums/themeType";
|
||||
import { ContainerService } from "jslib-common/services/container.service";
|
||||
import { EventService } from "jslib-common/services/event.service";
|
||||
import { VaultTimeoutService } from "jslib-common/services/vaultTimeout.service";
|
||||
|
||||
import { I18nService } from "../../services/i18n.service";
|
||||
import { NativeMessagingService } from "../../services/nativeMessaging.service";
|
||||
|
||||
@Injectable()
|
||||
export class InitService {
|
||||
constructor(
|
||||
@Inject(WINDOW) private win: Window,
|
||||
private environmentService: EnvironmentServiceAbstraction,
|
||||
private syncService: SyncServiceAbstraction,
|
||||
private vaultTimeoutService: VaultTimeoutServiceAbstraction,
|
||||
private i18nService: I18nServiceAbstraction,
|
||||
private eventService: EventServiceAbstraction,
|
||||
private twoFactorService: TwoFactorServiceAbstraction,
|
||||
private notificationsService: NotificationsServiceAbstraction,
|
||||
private platformUtilsService: PlatformUtilsServiceAbstraction,
|
||||
private stateService: StateServiceAbstraction,
|
||||
private cryptoService: CryptoServiceAbstraction,
|
||||
private nativeMessagingService: NativeMessagingService
|
||||
) {}
|
||||
|
||||
init() {
|
||||
return async () => {
|
||||
this.nativeMessagingService.init();
|
||||
await this.stateService.init();
|
||||
await this.environmentService.setUrlsFromStorage();
|
||||
this.syncService.fullSync(true);
|
||||
(this.vaultTimeoutService as VaultTimeoutService).init(true);
|
||||
const locale = await this.stateService.getLocale();
|
||||
await (this.i18nService as I18nService).init(locale);
|
||||
(this.eventService as EventService).init(true);
|
||||
this.twoFactorService.init();
|
||||
setTimeout(() => this.notificationsService.init(), 3000);
|
||||
const htmlEl = this.win.document.documentElement;
|
||||
htmlEl.classList.add("os_" + this.platformUtilsService.getDeviceString());
|
||||
|
||||
const theme = await this.platformUtilsService.getEffectiveTheme();
|
||||
htmlEl.classList.add("theme_" + theme);
|
||||
this.platformUtilsService.onDefaultSystemThemeChange(async (sysTheme) => {
|
||||
const bwTheme = await this.stateService.getTheme();
|
||||
if (bwTheme == null || bwTheme === ThemeType.System) {
|
||||
htmlEl.classList.remove("theme_" + ThemeType.Light, "theme_" + ThemeType.Dark);
|
||||
htmlEl.classList.add("theme_" + sysTheme);
|
||||
}
|
||||
});
|
||||
|
||||
let installAction = null;
|
||||
const installedVersion = await this.stateService.getInstalledVersion();
|
||||
const currentVersion = await this.platformUtilsService.getApplicationVersion();
|
||||
if (installedVersion == null) {
|
||||
installAction = "install";
|
||||
} else if (installedVersion !== currentVersion) {
|
||||
installAction = "update";
|
||||
}
|
||||
|
||||
if (installAction != null) {
|
||||
await this.stateService.setInstalledVersion(currentVersion);
|
||||
}
|
||||
|
||||
const containerService = new ContainerService(this.cryptoService);
|
||||
containerService.attachToGlobal(this.win);
|
||||
};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,135 @@
|
|||
import { APP_INITIALIZER, InjectionToken, NgModule } from "@angular/core";
|
||||
|
||||
import {
|
||||
JslibServicesModule,
|
||||
SECURE_STORAGE,
|
||||
STATE_FACTORY,
|
||||
STATE_SERVICE_USE_CACHE,
|
||||
WINDOW,
|
||||
CLIENT_TYPE,
|
||||
LOCALES_DIRECTORY,
|
||||
SYSTEM_LANGUAGE,
|
||||
} from "jslib-angular/services/jslib-services.module";
|
||||
import { BroadcasterService as BroadcasterServiceAbstraction } from "jslib-common/abstractions/broadcaster.service";
|
||||
import { CryptoService as CryptoServiceAbstraction } from "jslib-common/abstractions/crypto.service";
|
||||
import { CryptoFunctionService as CryptoFunctionServiceAbstraction } from "jslib-common/abstractions/cryptoFunction.service";
|
||||
import { I18nService as I18nServiceAbstraction } from "jslib-common/abstractions/i18n.service";
|
||||
import {
|
||||
LogService,
|
||||
LogService as LogServiceAbstraction,
|
||||
} from "jslib-common/abstractions/log.service";
|
||||
import { MessagingService as MessagingServiceAbstraction } from "jslib-common/abstractions/messaging.service";
|
||||
import { PasswordRepromptService as PasswordRepromptServiceAbstraction } from "jslib-common/abstractions/passwordReprompt.service";
|
||||
import { PlatformUtilsService as PlatformUtilsServiceAbstraction } from "jslib-common/abstractions/platformUtils.service";
|
||||
import { StateService as StateServiceAbstraction } from "jslib-common/abstractions/state.service";
|
||||
import { StateMigrationService as StateMigrationServiceAbstraction } from "jslib-common/abstractions/stateMigration.service";
|
||||
import { StorageService as StorageServiceAbstraction } from "jslib-common/abstractions/storage.service";
|
||||
import { SystemService as SystemServiceAbstraction } from "jslib-common/abstractions/system.service";
|
||||
import { ClientType } from "jslib-common/enums/clientType";
|
||||
import { StateFactory } from "jslib-common/factories/stateFactory";
|
||||
import { GlobalState } from "jslib-common/models/domain/globalState";
|
||||
import { SystemService } from "jslib-common/services/system.service";
|
||||
import { ElectronCryptoService } from "jslib-electron/services/electronCrypto.service";
|
||||
import { ElectronLogService } from "jslib-electron/services/electronLog.service";
|
||||
import { ElectronPlatformUtilsService } from "jslib-electron/services/electronPlatformUtils.service";
|
||||
import { ElectronRendererMessagingService } from "jslib-electron/services/electronRendererMessaging.service";
|
||||
import { ElectronRendererSecureStorageService } from "jslib-electron/services/electronRendererSecureStorage.service";
|
||||
import { ElectronRendererStorageService } from "jslib-electron/services/electronRendererStorage.service";
|
||||
|
||||
import { Account } from "../../models/account";
|
||||
import { I18nService } from "../../services/i18n.service";
|
||||
import { LoginGuardService } from "../../services/loginGuard.service";
|
||||
import { NativeMessagingService } from "../../services/nativeMessaging.service";
|
||||
import { PasswordRepromptService } from "../../services/passwordReprompt.service";
|
||||
import { StateService } from "../../services/state.service";
|
||||
import { SearchBarService } from "../layout/search/search-bar.service";
|
||||
|
||||
import { InitService } from "./init.service";
|
||||
|
||||
const RELOAD_CALLBACK = new InjectionToken<() => any>("RELOAD_CALLBACK");
|
||||
|
||||
@NgModule({
|
||||
imports: [JslibServicesModule],
|
||||
declarations: [],
|
||||
providers: [
|
||||
InitService,
|
||||
NativeMessagingService,
|
||||
SearchBarService,
|
||||
LoginGuardService,
|
||||
{
|
||||
provide: APP_INITIALIZER,
|
||||
useFactory: (initService: InitService) => initService.init(),
|
||||
deps: [InitService],
|
||||
multi: true,
|
||||
},
|
||||
{
|
||||
provide: STATE_FACTORY,
|
||||
useValue: new StateFactory(GlobalState, Account),
|
||||
},
|
||||
{
|
||||
provide: CLIENT_TYPE,
|
||||
useValue: ClientType.Desktop,
|
||||
},
|
||||
{
|
||||
provide: RELOAD_CALLBACK,
|
||||
useValue: null,
|
||||
},
|
||||
{ provide: LogServiceAbstraction, useClass: ElectronLogService, deps: [] },
|
||||
{
|
||||
provide: PlatformUtilsServiceAbstraction,
|
||||
useClass: ElectronPlatformUtilsService,
|
||||
deps: [
|
||||
I18nServiceAbstraction,
|
||||
MessagingServiceAbstraction,
|
||||
CLIENT_TYPE,
|
||||
StateServiceAbstraction,
|
||||
],
|
||||
},
|
||||
{
|
||||
provide: I18nServiceAbstraction,
|
||||
useClass: I18nService,
|
||||
deps: [SYSTEM_LANGUAGE, LOCALES_DIRECTORY],
|
||||
},
|
||||
{
|
||||
provide: MessagingServiceAbstraction,
|
||||
useClass: ElectronRendererMessagingService,
|
||||
deps: [BroadcasterServiceAbstraction],
|
||||
},
|
||||
{ provide: StorageServiceAbstraction, useClass: ElectronRendererStorageService },
|
||||
{ provide: SECURE_STORAGE, useClass: ElectronRendererSecureStorageService },
|
||||
{
|
||||
provide: CryptoServiceAbstraction,
|
||||
useClass: ElectronCryptoService,
|
||||
deps: [
|
||||
CryptoFunctionServiceAbstraction,
|
||||
PlatformUtilsServiceAbstraction,
|
||||
LogServiceAbstraction,
|
||||
StateServiceAbstraction,
|
||||
],
|
||||
},
|
||||
{
|
||||
provide: SystemServiceAbstraction,
|
||||
useClass: SystemService,
|
||||
deps: [
|
||||
MessagingServiceAbstraction,
|
||||
PlatformUtilsServiceAbstraction,
|
||||
RELOAD_CALLBACK,
|
||||
StateServiceAbstraction,
|
||||
],
|
||||
},
|
||||
{ provide: PasswordRepromptServiceAbstraction, useClass: PasswordRepromptService },
|
||||
{
|
||||
provide: StateServiceAbstraction,
|
||||
useClass: StateService,
|
||||
deps: [
|
||||
StorageServiceAbstraction,
|
||||
SECURE_STORAGE,
|
||||
LogService,
|
||||
StateMigrationServiceAbstraction,
|
||||
STATE_FACTORY,
|
||||
STATE_SERVICE_USE_CACHE,
|
||||
],
|
||||
},
|
||||
],
|
||||
})
|
||||
export class ServicesModule {}
|
|
@ -10,15 +10,14 @@
|
|||
*ngFor="let f of cipher.fields; let i = index; trackBy: trackByFunction"
|
||||
[ngClass]="{ 'box-content-row-checkbox': f.type === fieldType.Boolean }"
|
||||
>
|
||||
<a
|
||||
href="#"
|
||||
<button
|
||||
type="button"
|
||||
appStopClick
|
||||
(click)="removeField(f)"
|
||||
appA11yTitle="{{ 'remove' | i18n }}"
|
||||
role="button"
|
||||
>
|
||||
<i class="bwi bwi-minus-circle bwi-lg" aria-hidden="true"></i>
|
||||
</a>
|
||||
</button>
|
||||
<label for="fieldName{{ i }}" class="sr-only">{{ "name" | i18n }}</label>
|
||||
<label for="fieldValue{{ i }}" class="sr-only">{{ "value" | i18n }}</label>
|
||||
<div class="row-main">
|
||||
|
@ -78,12 +77,10 @@
|
|||
class="action-buttons"
|
||||
*ngIf="f.type === fieldType.Hidden && (cipher.viewPassword || f.newField)"
|
||||
>
|
||||
<a
|
||||
<button
|
||||
type="button"
|
||||
class="row-btn"
|
||||
href="#"
|
||||
appStopClick
|
||||
appBlurClick
|
||||
role="button"
|
||||
appA11yTitle="{{ 'toggleVisibility' | i18n }}"
|
||||
[attr.aria-pressed]="f.showValue"
|
||||
(click)="toggleFieldValue(f)"
|
||||
|
@ -93,7 +90,7 @@
|
|||
aria-hidden="true"
|
||||
[ngClass]="{ 'bwi-eye': !f.showValue, 'bwi-eye-slash': f.showValue }"
|
||||
></i>
|
||||
</a>
|
||||
</button>
|
||||
</div>
|
||||
<div class="drag-handle" appA11yTitle="{{ 'dragToSort' | i18n }}" cdkDragHandle>
|
||||
<i class="bwi bwi-hamburger" aria-hidden="true"></i>
|
||||
|
@ -102,10 +99,10 @@
|
|||
</div>
|
||||
<!-- Add new custom field -->
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<a href="#" appStopClick (click)="addField()" role="button">
|
||||
<button type="button" appStopClick (click)="addField()">
|
||||
<i class="bwi bwi-plus-circle bwi-fw bwi-lg" aria-hidden="true"></i>
|
||||
{{ "newCustomField" | i18n }}
|
||||
</a>
|
||||
</button>
|
||||
<label for="addFieldType" class="sr-only">{{ "type" | i18n }}</label>
|
||||
<select id="addFieldType" name="AddFieldType" [(ngModel)]="addFieldType" class="field-type">
|
||||
<option *ngFor="let o of addFieldTypeOptions" [ngValue]="o.value">{{ o.name }}</option>
|
||||
|
|
|
@ -39,17 +39,15 @@
|
|||
/>
|
||||
</div>
|
||||
<div class="action-buttons">
|
||||
<a
|
||||
<button
|
||||
type="button"
|
||||
class="row-btn"
|
||||
href="#"
|
||||
appStopClick
|
||||
appBlurClick
|
||||
role="button"
|
||||
appA11yTitle="{{ 'generateUsername' | i18n }}"
|
||||
(click)="generateUsername()"
|
||||
>
|
||||
<i class="bwi bwi-lg bwi-generate" aria-hidden="true"></i>
|
||||
</a>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-content-row box-content-row-flex" appBoxRow>
|
||||
|
@ -70,7 +68,6 @@
|
|||
type="button"
|
||||
#checkPasswordBtn
|
||||
class="row-btn btn"
|
||||
appBlurClick
|
||||
appA11yTitle="{{ 'checkPassword' | i18n }}"
|
||||
(click)="checkPassword()"
|
||||
[appApiAction]="checkPasswordPromise"
|
||||
|
@ -87,12 +84,10 @@
|
|||
aria-hidden="true"
|
||||
></i>
|
||||
</button>
|
||||
<a
|
||||
<button
|
||||
type="button"
|
||||
class="row-btn"
|
||||
href="#"
|
||||
appStopClick
|
||||
appBlurClick
|
||||
role="button"
|
||||
appA11yTitle="{{ 'toggleVisibility' | i18n }}"
|
||||
[attr.aria-pressed]="showPassword"
|
||||
(click)="togglePassword()"
|
||||
|
@ -102,18 +97,16 @@
|
|||
aria-hidden="true"
|
||||
[ngClass]="{ 'bwi-eye': !showPassword, 'bwi-eye-slash': showPassword }"
|
||||
></i>
|
||||
</a>
|
||||
<a
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="row-btn"
|
||||
href="#"
|
||||
appStopClick
|
||||
appBlurClick
|
||||
role="button"
|
||||
appA11yTitle="{{ 'generatePassword' | i18n }}"
|
||||
(click)="generatePassword()"
|
||||
>
|
||||
<i class="bwi bwi-lg bwi-generate" aria-hidden="true"></i>
|
||||
</a>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-content-row" appBoxRow>
|
||||
|
@ -153,12 +146,10 @@
|
|||
/>
|
||||
</div>
|
||||
<div class="action-buttons">
|
||||
<a
|
||||
<button
|
||||
type="button"
|
||||
class="row-btn"
|
||||
href="#"
|
||||
appStopClick
|
||||
appBlurClick
|
||||
role="button"
|
||||
appA11yTitle="{{ 'toggleVisibility' | i18n }}"
|
||||
[attr.aria-pressed]="showCardNumber"
|
||||
(click)="toggleCardNumber()"
|
||||
|
@ -168,7 +159,7 @@
|
|||
aria-hidden="true"
|
||||
[ngClass]="{ 'bwi-eye': !showCardNumber, 'bwi-eye-slash': showCardNumber }"
|
||||
></i>
|
||||
</a>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-content-row" appBoxRow>
|
||||
|
@ -208,12 +199,10 @@
|
|||
/>
|
||||
</div>
|
||||
<div class="action-buttons">
|
||||
<a
|
||||
<button
|
||||
type="button"
|
||||
class="row-btn"
|
||||
href="#"
|
||||
appStopClick
|
||||
appBlurClick
|
||||
role="button"
|
||||
appA11yTitle="{{ 'toggleVisibility' | i18n }}"
|
||||
[attr.aria-pressed]="showCardCode"
|
||||
(click)="toggleCardCode()"
|
||||
|
@ -223,7 +212,7 @@
|
|||
aria-hidden="true"
|
||||
[ngClass]="{ 'bwi-eye': !showCardCode, 'bwi-eye-slash': showCardCode }"
|
||||
></i>
|
||||
</a>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -406,9 +395,14 @@
|
|||
appBoxRow
|
||||
*ngFor="let u of cipher.login.uris; let i = index; trackBy: trackByFunction"
|
||||
>
|
||||
<a href="#" appStopClick (click)="removeUri(u)" appA11yTitle="{{ 'remove' | i18n }}">
|
||||
<i class="bwi bwi-minus-circle bwi-lg" aria-hidden="true" role="button"></i>
|
||||
</a>
|
||||
<button
|
||||
type="button"
|
||||
appStopClick
|
||||
(click)="removeUri(u)"
|
||||
appA11yTitle="{{ 'remove' | i18n }}"
|
||||
>
|
||||
<i class="bwi bwi-minus-circle bwi-lg" aria-hidden="true"></i>
|
||||
</button>
|
||||
<div class="row-main">
|
||||
<label for="loginUri{{ i }}">{{ "uriPosition" | i18n: i + 1 }}</label>
|
||||
<input
|
||||
|
@ -435,31 +429,25 @@
|
|||
</select>
|
||||
</div>
|
||||
<div class="action-buttons">
|
||||
<a
|
||||
<button
|
||||
type="button"
|
||||
class="row-btn"
|
||||
href="#"
|
||||
appStopClick
|
||||
appBlurClick
|
||||
role="button"
|
||||
appA11yTitle="{{ 'toggleOptions' | i18n }}"
|
||||
(click)="toggleUriOptions(u)"
|
||||
[attr.aria-expanded]="
|
||||
!(u.showOptions === false || (u.showOptions == null && u.match == null))
|
||||
"
|
||||
>
|
||||
<i class="bwi bwi-lg bwi-cog" aria-hidden="true"></i>
|
||||
</a>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
<a
|
||||
href="#"
|
||||
appStopClick
|
||||
appBlurClick
|
||||
(click)="addUri()"
|
||||
class="box-content-row"
|
||||
role="button"
|
||||
>
|
||||
<button type="button" appStopClick (click)="addUri()" class="box-content-row">
|
||||
<i class="bwi bwi-plus-circle bwi-fw bwi-lg" aria-hidden="true"></i>
|
||||
{{ "newUri" | i18n }}
|
||||
</a>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box">
|
||||
|
@ -477,9 +465,13 @@
|
|||
<div class="box-content-row box-content-row-checkbox" appBoxRow *ngIf="canUseReprompt">
|
||||
<label for="passwordPrompt"
|
||||
>{{ "passwordPrompt" | i18n }}
|
||||
<a href="#" appA11yTitle="{{ 'learnMore' | i18n }}" (click)="openHelpReprompt()">
|
||||
<button
|
||||
type="button"
|
||||
appA11yTitle="{{ 'learnMore' | i18n }}"
|
||||
(click)="openHelpReprompt()"
|
||||
>
|
||||
<i class="bwi bwi-question-circle" aria-hidden="true"></i>
|
||||
</a>
|
||||
</button>
|
||||
</label>
|
||||
<input
|
||||
id="passwordPrompt"
|
||||
|
@ -489,30 +481,26 @@
|
|||
(change)="repromptChanged()"
|
||||
/>
|
||||
</div>
|
||||
<a
|
||||
<button
|
||||
type="button"
|
||||
class="box-content-row box-content-row-flex text-default"
|
||||
href="#"
|
||||
appStopClick
|
||||
appBlurClick
|
||||
(click)="attachments()"
|
||||
*ngIf="editMode && !cloneMode"
|
||||
role="button"
|
||||
>
|
||||
<div class="row-main">{{ "attachments" | i18n }}</div>
|
||||
<i class="bwi bwi-angle-right row-sub-icon" aria-hidden="true"></i>
|
||||
</a>
|
||||
<a
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="box-content-row box-content-row-flex text-default"
|
||||
href="#"
|
||||
appStopClick
|
||||
appBlurClick
|
||||
(click)="editCollections()"
|
||||
*ngIf="editMode && !cloneMode && cipher.organizationId"
|
||||
role="button"
|
||||
>
|
||||
<div class="row-main">{{ "collections" | i18n }}</div>
|
||||
<i class="bwi bwi-angle-right row-sub-icon" aria-hidden="true"></i>
|
||||
</a>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box">
|
||||
|
@ -577,7 +565,6 @@
|
|||
</div>
|
||||
<div class="footer">
|
||||
<button
|
||||
appBlurClick
|
||||
type="submit"
|
||||
class="primary"
|
||||
appA11yTitle="{{ 'save' | i18n }}"
|
||||
|
@ -590,12 +577,11 @@
|
|||
aria-hidden="true"
|
||||
></i>
|
||||
</button>
|
||||
<button appBlurClick type="button" (click)="cancel()">
|
||||
<button type="button" (click)="cancel()">
|
||||
{{ "cancel" | i18n }}
|
||||
</button>
|
||||
<div class="right">
|
||||
<button
|
||||
appBlurClick
|
||||
type="button"
|
||||
(click)="share()"
|
||||
appA11yTitle="{{ 'moveToOrganization' | i18n }}"
|
||||
|
@ -605,7 +591,6 @@
|
|||
</button>
|
||||
<button
|
||||
#deleteBtn
|
||||
appBlurClick
|
||||
type="button"
|
||||
(click)="delete()"
|
||||
class="danger"
|
||||
|
|
|
@ -17,7 +17,6 @@
|
|||
class="row-btn btn"
|
||||
type="button"
|
||||
appStopClick
|
||||
appBlurClick
|
||||
appA11yTitle="{{ 'delete' | i18n }}"
|
||||
(click)="delete(a)"
|
||||
#deleteBtn
|
||||
|
@ -56,7 +55,6 @@
|
|||
</div>
|
||||
<div class="modal-footer">
|
||||
<button
|
||||
appBlurClick
|
||||
type="submit"
|
||||
class="primary"
|
||||
appA11yTitle="{{ 'save' | i18n }}"
|
||||
|
|
|
@ -6,14 +6,15 @@
|
|||
*ngIf="ciphers.length"
|
||||
>
|
||||
<div class="list">
|
||||
<a
|
||||
<button
|
||||
type="button"
|
||||
*cdkVirtualFor="let c of ciphers; trackBy: trackByFn"
|
||||
appStopClick
|
||||
(click)="selectCipher(c)"
|
||||
(contextmenu)="rightClickCipher(c)"
|
||||
href="#"
|
||||
title="{{ 'viewItem' | i18n }}"
|
||||
[ngClass]="{ active: c.id === activeCipherId }"
|
||||
[attr.aria-pressed]="c.id === activeCipherId"
|
||||
class="flex-list-item virtual-scroll-item"
|
||||
>
|
||||
<app-vault-icon [cipher]="c"></app-vault-icon>
|
||||
|
@ -39,7 +40,7 @@
|
|||
</span>
|
||||
<span *ngIf="c.subTitle" class="detail">{{ c.subTitle }}</span>
|
||||
</div>
|
||||
</a>
|
||||
</button>
|
||||
</div>
|
||||
</cdk-virtual-scroll-viewport>
|
||||
<div class="no-items" *ngIf="!ciphers.length">
|
||||
|
@ -53,7 +54,6 @@
|
|||
</div>
|
||||
<div class="footer">
|
||||
<button
|
||||
appBlurClick
|
||||
(click)="addCipher()"
|
||||
(contextmenu)="addCipherOptions()"
|
||||
class="block primary"
|
||||
|
|
|
@ -28,7 +28,6 @@
|
|||
</div>
|
||||
<div class="modal-footer">
|
||||
<button
|
||||
appBlurClick
|
||||
type="submit"
|
||||
class="primary"
|
||||
appA11yTitle="{{ 'save' | i18n }}"
|
||||
|
|
|
@ -21,8 +21,8 @@
|
|||
<option *ngFor="let f of formatOptions" [value]="f.value">{{ f.name }}</option>
|
||||
</select>
|
||||
</div>
|
||||
<app-verify-master-password ngDefaultControl formControlName="secret" name="secret">
|
||||
</app-verify-master-password>
|
||||
<app-user-verification ngDefaultControl formControlName="secret" name="secret">
|
||||
</app-user-verification>
|
||||
</div>
|
||||
<div class="box-footer">
|
||||
<p>{{ "confirmIdentity" | i18n }}</p>
|
||||
|
@ -31,7 +31,6 @@
|
|||
</div>
|
||||
<div class="modal-footer">
|
||||
<button
|
||||
appBlurClick
|
||||
type="submit"
|
||||
class="primary"
|
||||
appA11yTitle="{{ 'submit' | i18n }}"
|
||||
|
|
|
@ -22,7 +22,6 @@
|
|||
</div>
|
||||
<div class="modal-footer">
|
||||
<button
|
||||
appBlurClick
|
||||
type="submit"
|
||||
class="primary"
|
||||
appA11yTitle="{{ 'save' | i18n }}"
|
||||
|
@ -43,7 +42,6 @@
|
|||
<div class="right">
|
||||
<button
|
||||
#deleteBtn
|
||||
appBlurClick
|
||||
type="button"
|
||||
(click)="delete()"
|
||||
class="danger"
|
||||
|
|
|
@ -41,7 +41,7 @@
|
|||
</div>
|
||||
<div class="generated-block" *ngIf="type === 'username'">
|
||||
<div class="generated-wrapper" [innerHTML]="username | colorPassword" appSelectCopy></div>
|
||||
<div class="action-buttons">
|
||||
<div class="action-buttons" #form [appApiAction]="usernameGeneratingPromise">
|
||||
<button
|
||||
type="button"
|
||||
class="icon-btn primary"
|
||||
|
@ -57,15 +57,26 @@
|
|||
appStopClick
|
||||
appA11yTitle="{{ 'regenerateUsername' | i18n }}"
|
||||
(click)="regenerate()"
|
||||
[disabled]="form.loading"
|
||||
>
|
||||
<i class="bwi bwi-lg bwi-generate" aria-hidden="true"></i>
|
||||
<i
|
||||
class="bwi bwi-lg bwi-generate"
|
||||
[ngClass]="form.loading ? 'bwi-spin' : ''"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box">
|
||||
<div class="box-content condensed">
|
||||
<div class="box-content-row box-content-row-radio">
|
||||
<label class="radio-header">{{ "whatWouldYouLikeToGenerate" | i18n }}</label>
|
||||
<div
|
||||
class="box-content-row box-content-row-radio"
|
||||
role="radiogroup"
|
||||
aria-labelledby="typeHeading"
|
||||
>
|
||||
<label id="typeHeading" class="radio-header">{{
|
||||
"whatWouldYouLikeToGenerate" | i18n
|
||||
}}</label>
|
||||
<div
|
||||
class="radio-group text-default"
|
||||
appBoxRow
|
||||
|
@ -76,12 +87,12 @@
|
|||
type="radio"
|
||||
class="radio"
|
||||
[(ngModel)]="type"
|
||||
name="Type_{{ o.value }}"
|
||||
name="Type"
|
||||
id="type_{{ o.value }}"
|
||||
[value]="o.value"
|
||||
(change)="typeChanged()"
|
||||
[checked]="type === o.value"
|
||||
[disabled]="comingFromAddEdit"
|
||||
[disabled]="comingFromAddEdit && type !== o.value"
|
||||
/>
|
||||
<label class="unstyled" for="type_{{ o.value }}">
|
||||
{{ o.name }}
|
||||
|
@ -105,8 +116,14 @@
|
|||
</button>
|
||||
</div>
|
||||
<div class="box-content condensed" [hidden]="!showOptions">
|
||||
<div class="box-content-row box-content-row-radio">
|
||||
<label class="radio-header">{{ "passwordType" | i18n }}</label>
|
||||
<div
|
||||
class="box-content-row box-content-row-radio"
|
||||
role="radiogroup"
|
||||
aria-labelledby="passwordTypeHeading"
|
||||
>
|
||||
<label id="passwordTypeHeading" class="radio-header">{{
|
||||
"passwordType" | i18n
|
||||
}}</label>
|
||||
<div
|
||||
class="radio-group text-default"
|
||||
appBoxRow
|
||||
|
@ -117,7 +134,7 @@
|
|||
type="radio"
|
||||
class="radio"
|
||||
[(ngModel)]="passwordOptions.type"
|
||||
name="PasswordType_{{ o.value }}"
|
||||
name="PasswordType"
|
||||
id="passwordType_{{ o.value }}"
|
||||
[value]="o.value"
|
||||
(change)="savePasswordOptions()"
|
||||
|
@ -209,6 +226,7 @@
|
|||
(change)="savePasswordOptions()"
|
||||
[disabled]="enforcedPasswordPolicyOptions?.useUppercase"
|
||||
[(ngModel)]="passwordOptions.uppercase"
|
||||
attr.aria-label="{{ 'uppercase' | i18n }}"
|
||||
/>
|
||||
</div>
|
||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
||||
|
@ -219,6 +237,7 @@
|
|||
(change)="savePasswordOptions()"
|
||||
[disabled]="enforcedPasswordPolicyOptions?.useLowercase"
|
||||
[(ngModel)]="passwordOptions.lowercase"
|
||||
attr.aria-label="{{ 'lowercase' | i18n }}"
|
||||
/>
|
||||
</div>
|
||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
||||
|
@ -229,6 +248,7 @@
|
|||
(change)="savePasswordOptions()"
|
||||
[disabled]="enforcedPasswordPolicyOptions?.useNumbers"
|
||||
[(ngModel)]="passwordOptions.number"
|
||||
attr.aria-label="{{ 'numbers' | i18n }}"
|
||||
/>
|
||||
</div>
|
||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
||||
|
@ -239,6 +259,7 @@
|
|||
(change)="savePasswordOptions()"
|
||||
[disabled]="enforcedPasswordPolicyOptions?.useSpecial"
|
||||
[(ngModel)]="passwordOptions.special"
|
||||
attr.aria-label="{{ 'specialCharacters' | i18n }}"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -295,13 +316,16 @@
|
|||
</button>
|
||||
</div>
|
||||
<div class="box-content condensed" [hidden]="!showOptions">
|
||||
<div class="box-content-row box-content-row-radio">
|
||||
<label class="radio-header">
|
||||
<div
|
||||
class="box-content-row box-content-row-radio"
|
||||
role="radiogroup"
|
||||
aria-labelledby="usernameTypeHeading"
|
||||
>
|
||||
<label id="usernameTypeHeading" class="radio-header">
|
||||
{{ "usernameType" | i18n }}
|
||||
<a
|
||||
href="#"
|
||||
appStopClick
|
||||
appBlurClick
|
||||
(click)="usernameTypesLearnMore()"
|
||||
appA11yTitle="{{ 'learnMore' | i18n }}"
|
||||
>
|
||||
|
@ -318,7 +342,7 @@
|
|||
type="radio"
|
||||
class="radio"
|
||||
[(ngModel)]="usernameOptions.type"
|
||||
name="UsernameType_{{ o.value }}"
|
||||
name="UsernameType"
|
||||
id="usernameType_{{ o.value }}"
|
||||
[value]="o.value"
|
||||
(change)="saveUsernameOptions()"
|
||||
|
@ -334,13 +358,13 @@
|
|||
</div>
|
||||
<div class="box" *ngIf="usernameOptions.type === 'forwarded'" [hidden]="!showOptions">
|
||||
<div class="box-content condensed">
|
||||
<div class="box-content-row">
|
||||
<label class="radio-header">{{ "service" | i18n }}</label>
|
||||
<div class="box-content-row" role="radiogroup" aria-labelledby="forwardTypeHeading">
|
||||
<label id="forwardTypeHeading" class="radio-header">{{ "service" | i18n }}</label>
|
||||
<div class="radio-group text-default" appBoxRow *ngFor="let o of forwardOptions">
|
||||
<input
|
||||
type="radio"
|
||||
[(ngModel)]="usernameOptions.forwardedService"
|
||||
name="ForwardType_{{ o.value }}"
|
||||
name="ForwardType"
|
||||
id="forwardtype_{{ o.value }}"
|
||||
[value]="o.value"
|
||||
(change)="saveUsernameOptions()"
|
||||
|
@ -351,6 +375,62 @@
|
|||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<ng-container *ngIf="usernameOptions.forwardedService === 'simplelogin'">
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="simplelogin-apikey">{{ "apiKey" | i18n }}</label>
|
||||
<input
|
||||
id="simplelogin-apikey"
|
||||
type="password"
|
||||
name="SimpleLoginApiKey"
|
||||
[(ngModel)]="usernameOptions.forwardedSimpleLoginApiKey"
|
||||
(blur)="saveUsernameOptions()"
|
||||
/>
|
||||
</div>
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="simplelogin-hostname">{{ "hostname" | i18n }}</label>
|
||||
<input
|
||||
id="simplelogin-hostname"
|
||||
type="text"
|
||||
name="SimpleLoginHostname"
|
||||
[(ngModel)]="usernameOptions.forwardedSimpleLoginHostname"
|
||||
(blur)="saveUsernameOptions()"
|
||||
/>
|
||||
</div>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="usernameOptions.forwardedService === 'anonaddy'">
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="anonaddy-accessToken">{{ "apiAccessToken" | i18n }}</label>
|
||||
<input
|
||||
id="anonaddy-accessToken"
|
||||
type="password"
|
||||
name="AnonAddyAccessToken"
|
||||
[(ngModel)]="usernameOptions.forwardedAnonAddyApiToken"
|
||||
(blur)="saveUsernameOptions()"
|
||||
/>
|
||||
</div>
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="anonaddy-domain">{{ "domainName" | i18n }}</label>
|
||||
<input
|
||||
id="anonaddy-domain"
|
||||
type="text"
|
||||
name="AnonAddyDomain"
|
||||
[(ngModel)]="usernameOptions.forwardedAnonAddyDomain"
|
||||
(blur)="saveUsernameOptions()"
|
||||
/>
|
||||
</div>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="usernameOptions.forwardedService === 'firefoxrelay'">
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="firefox-apikey">{{ "apiAccessToken" | i18n }}</label>
|
||||
<input
|
||||
id="firefox-apikey"
|
||||
type="password"
|
||||
name="FirefoxApiKey"
|
||||
[(ngModel)]="usernameOptions.forwardedFirefoxApiToken"
|
||||
(blur)="saveUsernameOptions()"
|
||||
/>
|
||||
</div>
|
||||
</ng-container>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box" *ngIf="usernameOptions.type === 'subaddress'" [hidden]="!showOptions">
|
||||
|
@ -365,13 +445,18 @@
|
|||
(blur)="saveUsernameOptions()"
|
||||
/>
|
||||
</div>
|
||||
<div class="box-content-row" *ngIf="subaddressOptions.length > 1">
|
||||
<label class="radio-header">{{ "type" | i18n }}</label>
|
||||
<div
|
||||
class="box-content-row"
|
||||
role="radiogroup"
|
||||
aria-labelledby="subaddressTypeHeading"
|
||||
*ngIf="subaddressOptions.length > 1"
|
||||
>
|
||||
<label id="subaddressTypeHeading" class="radio-header">{{ "type" | i18n }}</label>
|
||||
<div class="radio-group text-default" appBoxRow *ngFor="let o of subaddressOptions">
|
||||
<input
|
||||
type="radio"
|
||||
[(ngModel)]="usernameOptions.subaddressType"
|
||||
name="SubaddressType_{{ o.value }}"
|
||||
name="SubaddressType"
|
||||
id="subaddresstype_{{ o.value }}"
|
||||
[value]="o.value"
|
||||
(change)="saveUsernameOptions()"
|
||||
|
@ -407,13 +492,18 @@
|
|||
(blur)="saveUsernameOptions()"
|
||||
/>
|
||||
</div>
|
||||
<div class="box-content-row" *ngIf="catchallOptions.length > 1">
|
||||
<label class="radio-header">{{ "type" | i18n }}</label>
|
||||
<div
|
||||
class="box-content-row"
|
||||
role="radiogroup"
|
||||
aria-labelledby="catchallTypeHeading"
|
||||
*ngIf="catchallOptions.length > 1"
|
||||
>
|
||||
<label id="catchallTypeHeading" class="radio-header">{{ "type" | i18n }}</label>
|
||||
<div class="radio-group text-default" appBoxRow *ngFor="let o of catchallOptions">
|
||||
<input
|
||||
type="radio"
|
||||
[(ngModel)]="usernameOptions.catchallType"
|
||||
name="CatchallType_{{ o.value }}"
|
||||
name="CatchallType"
|
||||
id="catchalltype_{{ o.value }}"
|
||||
[value]="o.value"
|
||||
(change)="saveUsernameOptions()"
|
||||
|
@ -465,7 +555,6 @@
|
|||
<button
|
||||
type="button"
|
||||
class="primary"
|
||||
appBlurClick
|
||||
*ngIf="comingFromAddEdit"
|
||||
(click)="select()"
|
||||
appA11yTitle="{{ 'select' | i18n }}"
|
||||
|
|
|
@ -3,6 +3,7 @@ import { ActivatedRoute } from "@angular/router";
|
|||
|
||||
import { GeneratorComponent as BaseGeneratorComponent } from "jslib-angular/components/generator.component";
|
||||
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";
|
||||
|
@ -19,7 +20,8 @@ export class GeneratorComponent extends BaseGeneratorComponent {
|
|||
stateService: StateService,
|
||||
platformUtilsService: PlatformUtilsService,
|
||||
i18nService: I18nService,
|
||||
route: ActivatedRoute
|
||||
route: ActivatedRoute,
|
||||
logService: LogService
|
||||
) {
|
||||
super(
|
||||
passwordGenerationService,
|
||||
|
@ -27,6 +29,7 @@ export class GeneratorComponent extends BaseGeneratorComponent {
|
|||
platformUtilsService,
|
||||
stateService,
|
||||
i18nService,
|
||||
logService,
|
||||
route,
|
||||
window
|
||||
);
|
||||
|
|
|
@ -3,51 +3,81 @@
|
|||
<h2 class="sr-only">{{ "filters" | i18n }}</h2>
|
||||
<ul>
|
||||
<li [ngClass]="{ active: selectedAll }">
|
||||
<a href="#" appStopClick appBlurClick (click)="selectAll()">
|
||||
<button type="button" [attr.aria-pressed]="selectedAll" appStopClick (click)="selectAll()">
|
||||
<i class="bwi bwi-fw bwi-filter" aria-hidden="true"></i> {{ "allItems" | i18n }}
|
||||
</a>
|
||||
</button>
|
||||
</li>
|
||||
<li [ngClass]="{ active: selectedFavorites }">
|
||||
<a href="#" appStopClick appBlurClick (click)="selectFavorites()">
|
||||
<button
|
||||
type="button"
|
||||
[attr.aria-pressed]="selectedFavorites"
|
||||
appStopClick
|
||||
(click)="selectFavorites()"
|
||||
>
|
||||
<i class="bwi bwi-fw bwi-star" aria-hidden="true"></i> {{ "favorites" | i18n }}
|
||||
</a>
|
||||
</button>
|
||||
</li>
|
||||
<li [ngClass]="{ active: selectedTrash }">
|
||||
<a href="#" appStopClick appBlurClick (click)="selectTrash()">
|
||||
<button
|
||||
type="button"
|
||||
[attr.aria-pressed]="selectedTrash"
|
||||
appStopClick
|
||||
(click)="selectTrash()"
|
||||
>
|
||||
<i class="bwi bwi-fw bwi-trash" aria-hidden="true"></i> {{ "trash" | i18n }}
|
||||
</a>
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
<h2>{{ "types" | i18n }}</h2>
|
||||
<ul>
|
||||
<li [ngClass]="{ active: selectedType === cipherType.Login }">
|
||||
<a href="#" appStopClick appBlurClick (click)="selectType(cipherType.Login)">
|
||||
<button
|
||||
type="button"
|
||||
[attr.aria-pressed]="selectedType === cipherType.Login"
|
||||
appStopClick
|
||||
(click)="selectType(cipherType.Login)"
|
||||
>
|
||||
<i class="bwi bwi-fw bwi-globe" aria-hidden="true"></i> {{ "typeLogin" | i18n }}
|
||||
</a>
|
||||
</button>
|
||||
</li>
|
||||
<li [ngClass]="{ active: selectedType === cipherType.Card }">
|
||||
<a href="#" appStopClick appBlurClick (click)="selectType(cipherType.Card)">
|
||||
<button
|
||||
type="button"
|
||||
[attr.aria-pressed]="selectedType === cipherType.Card"
|
||||
appStopClick
|
||||
(click)="selectType(cipherType.Card)"
|
||||
>
|
||||
<i class="bwi bwi-fw bwi-credit-card" aria-hidden="true"></i> {{ "typeCard" | i18n }}
|
||||
</a>
|
||||
</button>
|
||||
</li>
|
||||
<li [ngClass]="{ active: selectedType === cipherType.Identity }">
|
||||
<a href="#" appStopClick appBlurClick (click)="selectType(cipherType.Identity)">
|
||||
<button
|
||||
type="button"
|
||||
[attr.aria-pressed]="selectedType === cipherType.Identity"
|
||||
appStopClick
|
||||
(click)="selectType(cipherType.Identity)"
|
||||
>
|
||||
<i class="bwi bwi-fw bwi-id-card" aria-hidden="true"></i> {{ "typeIdentity" | i18n }}
|
||||
</a>
|
||||
</button>
|
||||
</li>
|
||||
<li [ngClass]="{ active: selectedType === cipherType.SecureNote }">
|
||||
<a href="#" appStopClick appBlurClick (click)="selectType(cipherType.SecureNote)">
|
||||
<button
|
||||
type="button"
|
||||
[attr.aria-pressed]="selectedType === cipherType.SecureNote"
|
||||
appStopClick
|
||||
(click)="selectType(cipherType.SecureNote)"
|
||||
>
|
||||
<i class="bwi bwi-fw bwi-sticky-note" aria-hidden="true"></i> {{
|
||||
"typeSecureNote" | i18n
|
||||
}}
|
||||
</a>
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
<p *ngIf="!loaded" class="text-muted">{{ "loading" | i18n }}</p>
|
||||
<ng-container *ngIf="loaded">
|
||||
<div class="heading">
|
||||
<h2>{{ "folders" | i18n }}</h2>
|
||||
<button appBlurClick (click)="addFolder()" appA11yTitle="{{ 'addFolder' | i18n }}">
|
||||
<button (click)="addFolder()" appA11yTitle="{{ 'addFolder' | i18n }}">
|
||||
<i class="bwi bwi-plus bwi-fw" aria-hidden="true"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
@ -57,7 +87,12 @@
|
|||
*ngFor="let f of folders"
|
||||
[ngClass]="{ active: selectedFolder && f.node.id === selectedFolderId }"
|
||||
>
|
||||
<a href="#" appStopClick appBlurClick (click)="selectFolder(f.node)">
|
||||
<button
|
||||
type="button"
|
||||
[attr.aria-pressed]="selectedFolder && f.node.id === selectedFolderId"
|
||||
appStopClick
|
||||
(click)="selectFolder(f.node)"
|
||||
>
|
||||
<i
|
||||
*ngIf="f.children.length"
|
||||
class="bwi bwi-fw"
|
||||
|
@ -86,7 +121,7 @@
|
|||
>
|
||||
<i class="bwi bwi-pencil bwi-fw" aria-hidden="true"></i>
|
||||
</span>
|
||||
</a>
|
||||
</button>
|
||||
<ul class="bwi-ul" *ngIf="f.children.length && !isCollapsed(f.node)">
|
||||
<ng-container
|
||||
*ngTemplateOutlet="recursiveFolders; context: { $implicit: f.children }"
|
||||
|
@ -107,7 +142,13 @@
|
|||
*ngFor="let c of collections"
|
||||
[ngClass]="{ active: c.node.id === selectedCollectionId }"
|
||||
>
|
||||
<a href="#" appStopClick appBlurClick (click)="selectCollection(c.node)">
|
||||
<button
|
||||
*ngIf="c.children.length == 0"
|
||||
type="button"
|
||||
[attr.aria-pressed]="c.node.id === selectedCollectionId"
|
||||
appStopClick
|
||||
(click)="selectCollection(c.node)"
|
||||
>
|
||||
<i
|
||||
*ngIf="c.children.length"
|
||||
class="bwi bwi-fw"
|
||||
|
@ -126,7 +167,7 @@
|
|||
aria-hidden="true"
|
||||
></i>
|
||||
{{ c.node.name }}
|
||||
</a>
|
||||
</button>
|
||||
<ul class="bwi-ul" *ngIf="c.children.length && !isCollapsed(c.node)">
|
||||
<ng-container
|
||||
*ngTemplateOutlet="recursiveCollections; context: { $implicit: c.children }"
|
||||
|
|
|
@ -17,16 +17,15 @@
|
|||
<span class="detail">{{ h.date | date: "medium" }}</span>
|
||||
</div>
|
||||
<div class="action-buttons">
|
||||
<a
|
||||
<button
|
||||
type="button"
|
||||
class="row-btn"
|
||||
href="#"
|
||||
appStopClick
|
||||
appA11yTitle="{{ 'copyPassword' | i18n }}"
|
||||
(click)="copy(h.password)"
|
||||
role="button"
|
||||
>
|
||||
<i class="bwi bwi-lg bwi-clone" aria-hidden="true"></i>
|
||||
</a>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-content-row" *ngIf="!history.length">
|
||||
|
@ -39,7 +38,6 @@
|
|||
<button type="button" data-dismiss="modal">{{ "close" | i18n }}</button>
|
||||
<div class="right">
|
||||
<button
|
||||
appBlurClick
|
||||
type="button"
|
||||
(click)="clear()"
|
||||
class="danger"
|
||||
|
|
|
@ -15,16 +15,15 @@
|
|||
<span class="detail">{{ h.lastUsedDate | date: "medium" }}</span>
|
||||
</div>
|
||||
<div class="action-buttons">
|
||||
<a
|
||||
<button
|
||||
type="button"
|
||||
class="row-btn"
|
||||
href="#"
|
||||
appStopClick
|
||||
appA11yTitle="{{ 'copyPassword' | i18n }}"
|
||||
(click)="copy(h.password)"
|
||||
role="button"
|
||||
>
|
||||
<i class="bwi bwi-lg bwi-clone" aria-hidden="true"></i>
|
||||
</a>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-content-row" *ngIf="!history.length">
|
||||
|
|
|
@ -54,7 +54,6 @@
|
|||
</div>
|
||||
<div class="modal-footer">
|
||||
<button
|
||||
appBlurClick
|
||||
type="submit"
|
||||
class="primary"
|
||||
appA11yTitle="{{ 'save' | i18n }}"
|
||||
|
|
|
@ -35,24 +35,23 @@
|
|||
</div>
|
||||
</div>
|
||||
<div class="action-buttons">
|
||||
<a
|
||||
<button
|
||||
type="button"
|
||||
class="row-btn"
|
||||
href="#"
|
||||
appStopClick
|
||||
appA11yTitle="{{ 'toggleVisibility' | i18n }}"
|
||||
*ngIf="field.type === fieldType.Hidden && cipher.viewPassword"
|
||||
(click)="toggleFieldValue(field)"
|
||||
role="button"
|
||||
>
|
||||
<i
|
||||
class="bwi bwi-lg"
|
||||
aria-hidden="true"
|
||||
[ngClass]="{ 'bwi-eye': !field.showValue, 'bwi-eye-slash': field.showValue }"
|
||||
></i>
|
||||
</a>
|
||||
<a
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="row-btn"
|
||||
href="#"
|
||||
appStopClick
|
||||
appA11yTitle="{{ 'copyValue' | i18n }}"
|
||||
*ngIf="
|
||||
|
@ -64,10 +63,9 @@
|
|||
(click)="
|
||||
copy(field.value, 'value', field.type === fieldType.Hidden ? 'H_Field' : 'Field')
|
||||
"
|
||||
role="button"
|
||||
>
|
||||
<i class="bwi bwi-lg bwi-clone" aria-hidden="true"></i>
|
||||
</a>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -22,16 +22,15 @@
|
|||
{{ cipher.login.username }}
|
||||
</div>
|
||||
<div class="action-buttons">
|
||||
<a
|
||||
<button
|
||||
type="button"
|
||||
class="row-btn"
|
||||
href="#"
|
||||
appStopClick
|
||||
appA11yTitle="{{ 'copyUsername' | i18n }}"
|
||||
(click)="copy(cipher.login.username, 'username', 'Username')"
|
||||
role="button"
|
||||
>
|
||||
<i class="bwi bwi-lg bwi-clone" aria-hidden="true"></i>
|
||||
</a>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-content-row box-content-row-flex" *ngIf="cipher.login.password">
|
||||
|
@ -57,7 +56,6 @@
|
|||
type="button"
|
||||
#checkPasswordBtn
|
||||
class="row-btn btn"
|
||||
appBlurClick
|
||||
appA11yTitle="{{ 'checkPassword' | i18n }}"
|
||||
(click)="checkPassword()"
|
||||
[appApiAction]="checkPasswordPromise"
|
||||
|
@ -74,31 +72,29 @@
|
|||
aria-hidden="true"
|
||||
></i>
|
||||
</button>
|
||||
<a
|
||||
<button
|
||||
type="button"
|
||||
class="row-btn"
|
||||
href="#"
|
||||
appStopClick
|
||||
appA11yTitle="{{ 'toggleVisibility' | i18n }}"
|
||||
[attr.aria-pressed]="showPassword"
|
||||
(click)="togglePassword()"
|
||||
role="button"
|
||||
>
|
||||
<i
|
||||
class="bwi bwi-lg"
|
||||
aria-hidden="true"
|
||||
[ngClass]="{ 'bwi-eye': !showPassword, 'bwi-eye-slash': showPassword }"
|
||||
></i>
|
||||
</a>
|
||||
<a
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="row-btn"
|
||||
href="#"
|
||||
appStopClick
|
||||
appA11yTitle="{{ 'copyPassword' | i18n }}"
|
||||
(click)="copy(cipher.login.password, 'password', 'Password')"
|
||||
role="button"
|
||||
>
|
||||
<i class="bwi bwi-lg bwi-clone" aria-hidden="true"></i>
|
||||
</a>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
|
@ -131,16 +127,15 @@
|
|||
</svg>
|
||||
</span>
|
||||
<div class="action-buttons">
|
||||
<a
|
||||
<button
|
||||
type="button"
|
||||
class="row-btn"
|
||||
href="#"
|
||||
appStopClick
|
||||
appA11yTitle="{{ 'copyValue' | i18n }}"
|
||||
(click)="copy(totpCode, 'verificationCodeTotp', 'TOTP')"
|
||||
role="button"
|
||||
>
|
||||
<i class="bwi bwi-lg bwi-clone" aria-hidden="true"></i>
|
||||
</a>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -153,35 +148,37 @@
|
|||
<div class="box-content-row box-content-row-flex" *ngIf="cipher.card.number">
|
||||
<div class="row-main">
|
||||
<span class="row-label">{{ "number" | i18n }}</span>
|
||||
<span *ngIf="!showCardNumber" class="monospaced">{{ cipher.card.maskedNumber }}</span>
|
||||
<span *ngIf="showCardNumber" class="monospaced">{{ cipher.card.number }}</span>
|
||||
<span *ngIf="!showCardNumber" class="monospaced">{{
|
||||
cipher.card.maskedNumber | creditCardNumber: cipher.card.brand
|
||||
}}</span>
|
||||
<span *ngIf="showCardNumber" class="monospaced">{{
|
||||
cipher.card.number | creditCardNumber: cipher.card.brand
|
||||
}}</span>
|
||||
</div>
|
||||
<div class="action-buttons">
|
||||
<a
|
||||
<button
|
||||
type="button"
|
||||
class="row-btn"
|
||||
href="#"
|
||||
appStopClick
|
||||
appA11yTitle="{{ 'toggleVisibility' | i18n }}"
|
||||
[attr.aria-pressed]="showCardNumber"
|
||||
(click)="toggleCardNumber()"
|
||||
role="button"
|
||||
>
|
||||
<i
|
||||
class="bwi bwi-lg"
|
||||
aria-hidden="true"
|
||||
[ngClass]="{ 'bwi-eye': !showCardNumber, 'bwi-eye-slash': showCardNumber }"
|
||||
></i>
|
||||
</a>
|
||||
<a
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="row-btn"
|
||||
href="#"
|
||||
appStopClick
|
||||
appA11yTitle="{{ 'copyNumber' | i18n }}"
|
||||
(click)="copy(cipher.card.number, 'number', 'Card Number')"
|
||||
role="button"
|
||||
>
|
||||
<i class="bwi bwi-lg bwi-clone" aria-hidden="true"></i>
|
||||
</a>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-content-row" *ngIf="cipher.card.brand">
|
||||
|
@ -199,31 +196,29 @@
|
|||
<span *ngIf="showCardCode" class="monospaced">{{ cipher.card.code }}</span>
|
||||
</div>
|
||||
<div class="action-buttons">
|
||||
<a
|
||||
<button
|
||||
type="button"
|
||||
class="row-btn"
|
||||
href="#"
|
||||
appStopClick
|
||||
appA11yTitle="{{ 'toggleVisibility' | i18n }}"
|
||||
[attr.aria-pressed]="showCardCode"
|
||||
(click)="toggleCardCode()"
|
||||
role="button"
|
||||
>
|
||||
<i
|
||||
class="bwi bwi-lg"
|
||||
aria-hidden="true"
|
||||
[ngClass]="{ 'bwi-eye': !showCardCode, 'bwi-eye-slash': showCardCode }"
|
||||
></i>
|
||||
</a>
|
||||
<a
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="row-btn"
|
||||
href="#"
|
||||
appStopClick
|
||||
appA11yTitle="{{ 'copySecurityCode' | i18n }}"
|
||||
(click)="copy(cipher.card.code, 'securityCode', 'Security Code')"
|
||||
role="button"
|
||||
>
|
||||
<i class="bwi bwi-lg bwi-clone" aria-hidden="true"></i>
|
||||
</a>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -289,27 +284,25 @@
|
|||
<span title="{{ u.uri }}">{{ u.hostOrUri }}</span>
|
||||
</div>
|
||||
<div class="action-buttons">
|
||||
<a
|
||||
<button
|
||||
type="button"
|
||||
class="row-btn"
|
||||
href="#"
|
||||
appStopClick
|
||||
appA11yTitle="{{ 'launch' | i18n }}"
|
||||
*ngIf="u.canLaunch"
|
||||
(click)="launch(u)"
|
||||
role="button"
|
||||
>
|
||||
<i class="bwi bwi-lg bwi-share-square" aria-hidden="true"></i>
|
||||
</a>
|
||||
<a
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="row-btn"
|
||||
href="#"
|
||||
appStopClick
|
||||
appA11yTitle="{{ 'copyUri' | i18n }}"
|
||||
(click)="copy(u.uri, u.isWebsite ? 'website' : 'uri', 'URI')"
|
||||
role="button"
|
||||
>
|
||||
<i class="bwi bwi-lg bwi-clone" aria-hidden="true"></i>
|
||||
</a>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -334,12 +327,11 @@
|
|||
{{ "attachments" | i18n }}
|
||||
</div>
|
||||
<div class="box-content">
|
||||
<a
|
||||
<button
|
||||
type="button"
|
||||
class="box-content-row box-content-row-flex text-default"
|
||||
*ngFor="let attachment of cipher.attachments"
|
||||
href="#"
|
||||
appStopClick
|
||||
appBlurCLick
|
||||
(click)="downloadAttachment(attachment)"
|
||||
>
|
||||
<span class="row-main">{{ attachment.fileName }}</span>
|
||||
|
@ -354,7 +346,7 @@
|
|||
*ngIf="attachment.downloading"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
</a>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box">
|
||||
|
@ -369,15 +361,14 @@
|
|||
</div>
|
||||
<div *ngIf="cipher.hasPasswordHistory">
|
||||
<b class="font-weight-semibold">{{ "passwordHistory" | i18n }}:</b>
|
||||
<a
|
||||
href="#"
|
||||
<button
|
||||
type="button"
|
||||
(click)="viewHistory()"
|
||||
appStopClick
|
||||
role="button"
|
||||
appA11yTitle="{{ 'passwordHistory' | i18n }}, {{ cipher.passwordHistory.length }}"
|
||||
>
|
||||
<span aria-hidden="true">{{ cipher.passwordHistory.length }}</span>
|
||||
</a>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -385,7 +376,6 @@
|
|||
</div>
|
||||
<div class="footer" *ngIf="cipher">
|
||||
<button
|
||||
appBlurClick
|
||||
class="primary"
|
||||
(click)="edit()"
|
||||
appA11yTitle="{{ 'edit' | i18n }}"
|
||||
|
@ -394,7 +384,6 @@
|
|||
<i class="bwi bwi-pencil bwi-fw bwi-lg" aria-hidden="true"></i>
|
||||
</button>
|
||||
<button
|
||||
appBlurClick
|
||||
class="primary"
|
||||
(click)="restore()"
|
||||
appA11yTitle="{{ 'restore' | i18n }}"
|
||||
|
@ -403,7 +392,6 @@
|
|||
<i class="bwi bwi-undo bwi-fw bwi-lg" aria-hidden="true"></i>
|
||||
</button>
|
||||
<button
|
||||
appBlurClick
|
||||
class="primary"
|
||||
*ngIf="!cipher?.organizationId && !cipher.isDeleted"
|
||||
(click)="clone()"
|
||||
|
@ -413,7 +401,6 @@
|
|||
</button>
|
||||
<div class="right">
|
||||
<button
|
||||
appBlurClick
|
||||
type="button"
|
||||
(click)="delete()"
|
||||
class="danger"
|
||||
|
|
|
@ -370,10 +370,10 @@
|
|||
"message": "Hazırkı parolun üzərinə yazmaq istədiyinizə əminsiniz?"
|
||||
},
|
||||
"overwriteUsername": {
|
||||
"message": "Overwrite Username"
|
||||
"message": "İstifadəçi adının üzərinə yaz"
|
||||
},
|
||||
"overwriteUsernameConfirmation": {
|
||||
"message": "Are you sure you want to overwrite the current username?"
|
||||
"message": "Hazırkı istifadəçi adının üzərinə yazmaq istədiyinizə əminsiniz?"
|
||||
},
|
||||
"noneFolder": {
|
||||
"message": "Qovluq yoxdur",
|
||||
|
@ -1198,7 +1198,7 @@
|
|||
"description": "Domain name. Ex. website.com"
|
||||
},
|
||||
"domainName": {
|
||||
"message": "Domain Name",
|
||||
"message": "Domen adı",
|
||||
"description": "Domain name. Ex. website.com"
|
||||
},
|
||||
"host": {
|
||||
|
@ -1841,45 +1841,45 @@
|
|||
}
|
||||
},
|
||||
"generator": {
|
||||
"message": "Generator"
|
||||
"message": "Yaradıcı"
|
||||
},
|
||||
"whatWouldYouLikeToGenerate": {
|
||||
"message": "What would you like to generate?"
|
||||
"message": "Nə yaratmaq istəyirsiniz?"
|
||||
},
|
||||
"passwordType": {
|
||||
"message": "Password Type"
|
||||
"message": "Parol növü"
|
||||
},
|
||||
"regenerateUsername": {
|
||||
"message": "Regenerate Username"
|
||||
"message": "İstifadəçi adını yenidən yarat"
|
||||
},
|
||||
"generateUsername": {
|
||||
"message": "Generate Username"
|
||||
"message": "İstifadəçi adı yarat"
|
||||
},
|
||||
"usernameType": {
|
||||
"message": "Username Type"
|
||||
"message": "İstifadəçi adı növü"
|
||||
},
|
||||
"plusAddressedEmail": {
|
||||
"message": "Plus Addressed Email"
|
||||
"message": "Plyus ünvanlı e-poçt"
|
||||
},
|
||||
"plusAddressedEmailDesc": {
|
||||
"message": "Use your email provider's sub-addressing capabilities."
|
||||
"message": "E-poçt təchizatçınızın alt ünvan özəlliklərini istifadə et."
|
||||
},
|
||||
"catchallEmail": {
|
||||
"message": "Catch-all Email"
|
||||
"message": "Catch-all E-poçt"
|
||||
},
|
||||
"catchallEmailDesc": {
|
||||
"message": "Use your domain's configured catch-all inbox."
|
||||
"message": "Domeninizin konfiqurasiya edilmiş hamısını yaxalama gələn qutusunu istifadə edin."
|
||||
},
|
||||
"random": {
|
||||
"message": "Random"
|
||||
"message": "Təsadüfi"
|
||||
},
|
||||
"randomWord": {
|
||||
"message": "Random Word"
|
||||
"message": "Təsadüfi söz"
|
||||
},
|
||||
"websiteName": {
|
||||
"message": "Website Name"
|
||||
"message": "Veb sayt adı"
|
||||
},
|
||||
"service": {
|
||||
"message": "Service"
|
||||
"message": "Xidmət"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -370,10 +370,10 @@
|
|||
"message": "Είστε βέβαιοι ότι θέλετε να αντικαταστήσετε τον τρέχον κωδικό πρόσβασης;"
|
||||
},
|
||||
"overwriteUsername": {
|
||||
"message": "Overwrite Username"
|
||||
"message": "Αντικατάσταση Username"
|
||||
},
|
||||
"overwriteUsernameConfirmation": {
|
||||
"message": "Are you sure you want to overwrite the current username?"
|
||||
"message": "Είστε βέβαιοι ότι θέλετε να αντικαταστήσετε το τρέχον username;"
|
||||
},
|
||||
"noneFolder": {
|
||||
"message": "Χωρίς Φάκελο",
|
||||
|
@ -743,10 +743,10 @@
|
|||
"message": "Γεννήτρια Κωδικού"
|
||||
},
|
||||
"contactUs": {
|
||||
"message": "Contact Us"
|
||||
"message": "Επικοινωνία"
|
||||
},
|
||||
"getHelp": {
|
||||
"message": "Get Help"
|
||||
"message": "Ζητήστε Βοήθεια"
|
||||
},
|
||||
"fileBugReport": {
|
||||
"message": "Υποβολή Αναφοράς Σφάλματος"
|
||||
|
@ -1198,7 +1198,7 @@
|
|||
"description": "Domain name. Ex. website.com"
|
||||
},
|
||||
"domainName": {
|
||||
"message": "Domain Name",
|
||||
"message": "Όνομα τομέα",
|
||||
"description": "Domain name. Ex. website.com"
|
||||
},
|
||||
"host": {
|
||||
|
@ -1829,10 +1829,10 @@
|
|||
"message": "Έχει λήξει το χρονικό όριο. Παρακαλώ επιστρέψτε και προσπαθήστε να συνδεθείτε ξανά."
|
||||
},
|
||||
"exportingPersonalVaultTitle": {
|
||||
"message": "Exporting Personal Vault"
|
||||
"message": "Εξαγωγή Προσωπικού Vault"
|
||||
},
|
||||
"exportingPersonalVaultDescription": {
|
||||
"message": "Only the personal vault items associated with $EMAIL$ will be exported. Organization vault items will not be included.",
|
||||
"message": "Θα εξαχθούν μόνο τα προσωπικά αντικείμενα Vault που σχετίζονται με το $EMAIL$. Τα αντικείμενα Vault οργανισμού δεν θα συμπεριληφθούν.",
|
||||
"placeholders": {
|
||||
"email": {
|
||||
"content": "$1",
|
||||
|
@ -1841,45 +1841,45 @@
|
|||
}
|
||||
},
|
||||
"generator": {
|
||||
"message": "Generator"
|
||||
"message": "Γεννήτρια"
|
||||
},
|
||||
"whatWouldYouLikeToGenerate": {
|
||||
"message": "What would you like to generate?"
|
||||
"message": "Τι θα θέλατε να δημιουργήσετε;"
|
||||
},
|
||||
"passwordType": {
|
||||
"message": "Password Type"
|
||||
"message": "Τύπος Κωδικού"
|
||||
},
|
||||
"regenerateUsername": {
|
||||
"message": "Regenerate Username"
|
||||
"message": "Επαναδημιουργία Ονόματος Χρήστη"
|
||||
},
|
||||
"generateUsername": {
|
||||
"message": "Generate Username"
|
||||
"message": "Δημιουργία Ονόματος Χρήστη"
|
||||
},
|
||||
"usernameType": {
|
||||
"message": "Username Type"
|
||||
"message": "Τύπος Ονόματος Χρήστη"
|
||||
},
|
||||
"plusAddressedEmail": {
|
||||
"message": "Plus Addressed Email"
|
||||
"message": "Συν Διεύθυνση Email"
|
||||
},
|
||||
"plusAddressedEmailDesc": {
|
||||
"message": "Use your email provider's sub-addressing capabilities."
|
||||
"message": "Χρησιμοποιήστε τις δυνατότητες δευτερεύουσας διεύθυνσης του παρόχου email σας."
|
||||
},
|
||||
"catchallEmail": {
|
||||
"message": "Catch-all Email"
|
||||
},
|
||||
"catchallEmailDesc": {
|
||||
"message": "Use your domain's configured catch-all inbox."
|
||||
"message": "Χρησιμοποιήστε τα διαμορφωμένα εισερχόμενα catch-all του domain σας."
|
||||
},
|
||||
"random": {
|
||||
"message": "Random"
|
||||
"message": "Τυχαίο"
|
||||
},
|
||||
"randomWord": {
|
||||
"message": "Random Word"
|
||||
"message": "Τυχαία Λέξη"
|
||||
},
|
||||
"websiteName": {
|
||||
"message": "Website Name"
|
||||
"message": "Όνομα Ιστοσελίδας"
|
||||
},
|
||||
"service": {
|
||||
"message": "Service"
|
||||
"message": "Υπηρεσία"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -400,6 +400,18 @@
|
|||
"length": {
|
||||
"message": "Length"
|
||||
},
|
||||
"uppercase": {
|
||||
"message": "Uppercase (A-Z)"
|
||||
},
|
||||
"lowercase": {
|
||||
"message": "Lowercase (a-z)"
|
||||
},
|
||||
"numbers": {
|
||||
"message": "Numbers (0-9)"
|
||||
},
|
||||
"specialCharacters": {
|
||||
"message": "Special Characters (!@#$%^&*)"
|
||||
},
|
||||
"numWords": {
|
||||
"message": "Number of Words"
|
||||
},
|
||||
|
@ -775,7 +787,7 @@
|
|||
"description": "A 'fingerprint phrase' is a unique word phrase (similar to a passphrase) that a user can use to authenticate their public key with another user, for the purposes of sharing."
|
||||
},
|
||||
"goToWebVault": {
|
||||
"message": "Go To Web Vault"
|
||||
"message": "Go to Web Vault"
|
||||
},
|
||||
"getMobileApp": {
|
||||
"message": "Get Mobile App"
|
||||
|
@ -973,7 +985,7 @@
|
|||
"description": "Copy to clipboard"
|
||||
},
|
||||
"checkForUpdates": {
|
||||
"message": "Check For Updates"
|
||||
"message": "Check for Updates…"
|
||||
},
|
||||
"version": {
|
||||
"message": "Version $VERSION_NUM$",
|
||||
|
@ -985,7 +997,7 @@
|
|||
}
|
||||
},
|
||||
"restartToUpdate": {
|
||||
"message": "Restart To Update"
|
||||
"message": "Restart to Update"
|
||||
},
|
||||
"restartToUpdateDesc": {
|
||||
"message": "Version $VERSION_NUM$ is ready to install. You must restart the application to complete the installation. Do you want to restart and update now?",
|
||||
|
@ -1587,7 +1599,8 @@
|
|||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"maxAccessCount": {
|
||||
"message": "Maximum Access Count"
|
||||
"message": "Maximum Access Count",
|
||||
"description": "This text will be displayed after a Send has been accessed the maximum amount of times."
|
||||
},
|
||||
"maxAccessCountDesc": {
|
||||
"message": "If set, users will no longer be able to access this Send once the maximum access count is reached.",
|
||||
|
@ -1840,6 +1853,12 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"locked": {
|
||||
"message": "Locked"
|
||||
},
|
||||
"unlocked": {
|
||||
"message": "Unlocked"
|
||||
},
|
||||
"generator": {
|
||||
"message": "Generator"
|
||||
},
|
||||
|
@ -1859,7 +1878,8 @@
|
|||
"message": "Username Type"
|
||||
},
|
||||
"plusAddressedEmail": {
|
||||
"message": "Plus Addressed Email"
|
||||
"message": "Plus Addressed Email",
|
||||
"description": "Username generator option that appends a random sub-address to the username. For example: address+subaddress@email.com"
|
||||
},
|
||||
"plusAddressedEmailDesc": {
|
||||
"message": "Use your email provider's sub-addressing capabilities."
|
||||
|
@ -1881,5 +1901,21 @@
|
|||
},
|
||||
"service": {
|
||||
"message": "Service"
|
||||
},
|
||||
"forwardedEmail": {
|
||||
"message": "Forwarded Email Alias"
|
||||
},
|
||||
"forwardedEmailDesc": {
|
||||
"message": "Generate an email alias with an external forwarding service."
|
||||
},
|
||||
"hostname": {
|
||||
"message": "Hostname",
|
||||
"description": "Part of a URL."
|
||||
},
|
||||
"apiAccessToken": {
|
||||
"message": "API Access Token"
|
||||
},
|
||||
"apiKey": {
|
||||
"message": "API Key"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -136,7 +136,7 @@
|
|||
"description": "Toggling an expand/collapse state."
|
||||
},
|
||||
"cardholderName": {
|
||||
"message": "Nombre en la tarjeta"
|
||||
"message": "Nombre del propietario de la tarjeta"
|
||||
},
|
||||
"number": {
|
||||
"message": "Número"
|
||||
|
@ -370,10 +370,10 @@
|
|||
"message": "¿Estás seguro de que quieres sobreescribir la contraseña actual?"
|
||||
},
|
||||
"overwriteUsername": {
|
||||
"message": "Overwrite Username"
|
||||
"message": "Reemplazar nombre de usuario"
|
||||
},
|
||||
"overwriteUsernameConfirmation": {
|
||||
"message": "Are you sure you want to overwrite the current username?"
|
||||
"message": "¿Esta seguro que desea reemplazar el nombre de usuario?"
|
||||
},
|
||||
"noneFolder": {
|
||||
"message": "Sin carpeta",
|
||||
|
@ -1198,7 +1198,7 @@
|
|||
"description": "Domain name. Ex. website.com"
|
||||
},
|
||||
"domainName": {
|
||||
"message": "Domain Name",
|
||||
"message": "Nombre de dominio",
|
||||
"description": "Domain name. Ex. website.com"
|
||||
},
|
||||
"host": {
|
||||
|
@ -1841,25 +1841,25 @@
|
|||
}
|
||||
},
|
||||
"generator": {
|
||||
"message": "Generator"
|
||||
"message": "Generador"
|
||||
},
|
||||
"whatWouldYouLikeToGenerate": {
|
||||
"message": "What would you like to generate?"
|
||||
"message": "¿Qué deseas generar?"
|
||||
},
|
||||
"passwordType": {
|
||||
"message": "Password Type"
|
||||
"message": "Tipo de contraseña"
|
||||
},
|
||||
"regenerateUsername": {
|
||||
"message": "Regenerate Username"
|
||||
"message": "Regenerar nombre de usuario"
|
||||
},
|
||||
"generateUsername": {
|
||||
"message": "Generate Username"
|
||||
"message": "Generar nombre de usuario"
|
||||
},
|
||||
"usernameType": {
|
||||
"message": "Username Type"
|
||||
"message": "Tipo de nombre de usuario"
|
||||
},
|
||||
"plusAddressedEmail": {
|
||||
"message": "Plus Addressed Email"
|
||||
"message": "Dirección adicional de correo electrónico"
|
||||
},
|
||||
"plusAddressedEmailDesc": {
|
||||
"message": "Use your email provider's sub-addressing capabilities."
|
||||
|
@ -1871,15 +1871,15 @@
|
|||
"message": "Use your domain's configured catch-all inbox."
|
||||
},
|
||||
"random": {
|
||||
"message": "Random"
|
||||
"message": "Aleatorio"
|
||||
},
|
||||
"randomWord": {
|
||||
"message": "Random Word"
|
||||
"message": "Palabra aleatoria"
|
||||
},
|
||||
"websiteName": {
|
||||
"message": "Website Name"
|
||||
"message": "Nombre del sitio web"
|
||||
},
|
||||
"service": {
|
||||
"message": "Service"
|
||||
"message": "Servicio"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -370,10 +370,10 @@
|
|||
"message": "Oled kindel, et soovid olemasolevat parooli üle kirjutada?"
|
||||
},
|
||||
"overwriteUsername": {
|
||||
"message": "Overwrite Username"
|
||||
"message": "Kasutajanime ülekirjutamine"
|
||||
},
|
||||
"overwriteUsernameConfirmation": {
|
||||
"message": "Are you sure you want to overwrite the current username?"
|
||||
"message": "Oled kindel, et soovid praegust kasutajanime üle kirjutada?"
|
||||
},
|
||||
"noneFolder": {
|
||||
"message": "Kaust puudub",
|
||||
|
@ -1198,7 +1198,7 @@
|
|||
"description": "Domain name. Ex. website.com"
|
||||
},
|
||||
"domainName": {
|
||||
"message": "Domain Name",
|
||||
"message": "Domeeni nimi",
|
||||
"description": "Domain name. Ex. website.com"
|
||||
},
|
||||
"host": {
|
||||
|
@ -1841,45 +1841,45 @@
|
|||
}
|
||||
},
|
||||
"generator": {
|
||||
"message": "Generator"
|
||||
"message": "Genereerija"
|
||||
},
|
||||
"whatWouldYouLikeToGenerate": {
|
||||
"message": "What would you like to generate?"
|
||||
"message": "Mida sa soovid genereerida?"
|
||||
},
|
||||
"passwordType": {
|
||||
"message": "Password Type"
|
||||
"message": "Parooli tüüp"
|
||||
},
|
||||
"regenerateUsername": {
|
||||
"message": "Regenerate Username"
|
||||
"message": "Genereeri kasutajanimi uuesti"
|
||||
},
|
||||
"generateUsername": {
|
||||
"message": "Generate Username"
|
||||
"message": "Genereeri kasutajanimi"
|
||||
},
|
||||
"usernameType": {
|
||||
"message": "Username Type"
|
||||
"message": "Kasutajanime tüüp"
|
||||
},
|
||||
"plusAddressedEmail": {
|
||||
"message": "Plus Addressed Email"
|
||||
"message": "Lisaks e-post"
|
||||
},
|
||||
"plusAddressedEmailDesc": {
|
||||
"message": "Use your email provider's sub-addressing capabilities."
|
||||
"message": "Kasuta e-posti teenuspakkuja alamadressimise võimalusi."
|
||||
},
|
||||
"catchallEmail": {
|
||||
"message": "Catch-all Email"
|
||||
"message": "Kogumisaadress"
|
||||
},
|
||||
"catchallEmailDesc": {
|
||||
"message": "Use your domain's configured catch-all inbox."
|
||||
"message": "Kasuta domeenipõhist kogumisaadressi."
|
||||
},
|
||||
"random": {
|
||||
"message": "Random"
|
||||
"message": "Juhuslik"
|
||||
},
|
||||
"randomWord": {
|
||||
"message": "Random Word"
|
||||
"message": "Juhuslik sõna"
|
||||
},
|
||||
"websiteName": {
|
||||
"message": "Website Name"
|
||||
"message": "Veebilehe nimi"
|
||||
},
|
||||
"service": {
|
||||
"message": "Service"
|
||||
"message": "Teenus"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -370,10 +370,10 @@
|
|||
"message": "آیا از بازنویسی بر روی پسورد فعلی مطمئن هستید؟"
|
||||
},
|
||||
"overwriteUsername": {
|
||||
"message": "Overwrite Username"
|
||||
"message": "بازنویسی نام کاربری"
|
||||
},
|
||||
"overwriteUsernameConfirmation": {
|
||||
"message": "Are you sure you want to overwrite the current username?"
|
||||
"message": "آیا از بازنویسی نام کاربری فعلی مطمئن هستید؟"
|
||||
},
|
||||
"noneFolder": {
|
||||
"message": "بدون پوشه",
|
||||
|
@ -1198,7 +1198,7 @@
|
|||
"description": "Domain name. Ex. website.com"
|
||||
},
|
||||
"domainName": {
|
||||
"message": "Domain Name",
|
||||
"message": "نام دامنه",
|
||||
"description": "Domain name. Ex. website.com"
|
||||
},
|
||||
"host": {
|
||||
|
@ -1841,45 +1841,45 @@
|
|||
}
|
||||
},
|
||||
"generator": {
|
||||
"message": "Generator"
|
||||
"message": "تولید کننده"
|
||||
},
|
||||
"whatWouldYouLikeToGenerate": {
|
||||
"message": "What would you like to generate?"
|
||||
"message": "چه چیزی دوست دارید تولید کنید؟"
|
||||
},
|
||||
"passwordType": {
|
||||
"message": "Password Type"
|
||||
"message": "نوع گذرواژه"
|
||||
},
|
||||
"regenerateUsername": {
|
||||
"message": "Regenerate Username"
|
||||
"message": "ایجاد مجدد نام کاربری"
|
||||
},
|
||||
"generateUsername": {
|
||||
"message": "Generate Username"
|
||||
"message": "ایجاد نام کاربری"
|
||||
},
|
||||
"usernameType": {
|
||||
"message": "Username Type"
|
||||
"message": "نوع نام کاربری"
|
||||
},
|
||||
"plusAddressedEmail": {
|
||||
"message": "Plus Addressed Email"
|
||||
"message": "به علاوه ایمیل آدرس داده شده"
|
||||
},
|
||||
"plusAddressedEmailDesc": {
|
||||
"message": "Use your email provider's sub-addressing capabilities."
|
||||
"message": "از قابلیت های آدرس دهی فرعی ارائه دهنده ایمیل خود استفاده کنید."
|
||||
},
|
||||
"catchallEmail": {
|
||||
"message": "Catch-all Email"
|
||||
"message": "رایانامه همهگیر"
|
||||
},
|
||||
"catchallEmailDesc": {
|
||||
"message": "Use your domain's configured catch-all inbox."
|
||||
"message": "از صندوق ورودی پیکربندی شده دامنه خود استفاده کنید."
|
||||
},
|
||||
"random": {
|
||||
"message": "Random"
|
||||
"message": "تصادفی"
|
||||
},
|
||||
"randomWord": {
|
||||
"message": "Random Word"
|
||||
"message": "کلمه تصادفی"
|
||||
},
|
||||
"websiteName": {
|
||||
"message": "Website Name"
|
||||
"message": "نام وب سایت"
|
||||
},
|
||||
"service": {
|
||||
"message": "Service"
|
||||
"message": "خدمت"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -370,10 +370,10 @@
|
|||
"message": "האם אתה בטוח שברצונך לדרוס את הסיסמה הנוכחית?"
|
||||
},
|
||||
"overwriteUsername": {
|
||||
"message": "Overwrite Username"
|
||||
"message": "כתוב מחדש את שם המשתמש"
|
||||
},
|
||||
"overwriteUsernameConfirmation": {
|
||||
"message": "Are you sure you want to overwrite the current username?"
|
||||
"message": "האם אתה בטוח שברצונך לדרוס את שם המשתמש הנוכחי?"
|
||||
},
|
||||
"noneFolder": {
|
||||
"message": "ללא תיקיה",
|
||||
|
@ -1198,7 +1198,7 @@
|
|||
"description": "Domain name. Ex. website.com"
|
||||
},
|
||||
"domainName": {
|
||||
"message": "Domain Name",
|
||||
"message": "שם דומיין",
|
||||
"description": "Domain name. Ex. website.com"
|
||||
},
|
||||
"host": {
|
||||
|
@ -1829,10 +1829,10 @@
|
|||
"message": "זמן ההפעלה שלך תם. נא לחזור ולנסות להיכנס שוב."
|
||||
},
|
||||
"exportingPersonalVaultTitle": {
|
||||
"message": "Exporting Personal Vault"
|
||||
"message": "הכספת האישית מיוצאת"
|
||||
},
|
||||
"exportingPersonalVaultDescription": {
|
||||
"message": "Only the personal vault items associated with $EMAIL$ will be exported. Organization vault items will not be included.",
|
||||
"message": "רק פריטי הכספת האישית שמשויכת עם $EMAIL$ ייוצאו. פריטי הכספת הארגוניים לא יהיו חלק מהייצוא.",
|
||||
"placeholders": {
|
||||
"email": {
|
||||
"content": "$1",
|
||||
|
@ -1841,22 +1841,22 @@
|
|||
}
|
||||
},
|
||||
"generator": {
|
||||
"message": "Generator"
|
||||
"message": "מחולל"
|
||||
},
|
||||
"whatWouldYouLikeToGenerate": {
|
||||
"message": "What would you like to generate?"
|
||||
"message": "מה ברצונך לחולל?"
|
||||
},
|
||||
"passwordType": {
|
||||
"message": "Password Type"
|
||||
"message": "סוג סיסמה"
|
||||
},
|
||||
"regenerateUsername": {
|
||||
"message": "Regenerate Username"
|
||||
"message": "חולל מחדש שם משתמש"
|
||||
},
|
||||
"generateUsername": {
|
||||
"message": "Generate Username"
|
||||
"message": "חולל שם משתמש"
|
||||
},
|
||||
"usernameType": {
|
||||
"message": "Username Type"
|
||||
"message": "סוג שם משתמש"
|
||||
},
|
||||
"plusAddressedEmail": {
|
||||
"message": "Plus Addressed Email"
|
||||
|
@ -1871,15 +1871,15 @@
|
|||
"message": "Use your domain's configured catch-all inbox."
|
||||
},
|
||||
"random": {
|
||||
"message": "Random"
|
||||
"message": "אקראי"
|
||||
},
|
||||
"randomWord": {
|
||||
"message": "Random Word"
|
||||
"message": "מילה אקראית"
|
||||
},
|
||||
"websiteName": {
|
||||
"message": "Website Name"
|
||||
"message": "שם אתר"
|
||||
},
|
||||
"service": {
|
||||
"message": "Service"
|
||||
"message": "שירות"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -370,10 +370,10 @@
|
|||
"message": "정말 현재 비밀번호를 덮어쓰시겠습니까?"
|
||||
},
|
||||
"overwriteUsername": {
|
||||
"message": "Overwrite Username"
|
||||
"message": "아이디 덮어쓰기"
|
||||
},
|
||||
"overwriteUsernameConfirmation": {
|
||||
"message": "Are you sure you want to overwrite the current username?"
|
||||
"message": "정말 현재 아이디를 덮어쓰시겠습니까?"
|
||||
},
|
||||
"noneFolder": {
|
||||
"message": "폴더 없음",
|
||||
|
@ -743,10 +743,10 @@
|
|||
"message": "비밀번호 생성기"
|
||||
},
|
||||
"contactUs": {
|
||||
"message": "Contact Us"
|
||||
"message": "문의하기"
|
||||
},
|
||||
"getHelp": {
|
||||
"message": "Get Help"
|
||||
"message": "도움말"
|
||||
},
|
||||
"fileBugReport": {
|
||||
"message": "버그 보고서 첨부"
|
||||
|
@ -1198,7 +1198,7 @@
|
|||
"description": "Domain name. Ex. website.com"
|
||||
},
|
||||
"domainName": {
|
||||
"message": "Domain Name",
|
||||
"message": "도메인 이름",
|
||||
"description": "Domain name. Ex. website.com"
|
||||
},
|
||||
"host": {
|
||||
|
@ -1829,7 +1829,7 @@
|
|||
"message": "세션 시간이 초과되었습니다. 다시 로그인해주세요."
|
||||
},
|
||||
"exportingPersonalVaultTitle": {
|
||||
"message": "Exporting Personal Vault"
|
||||
"message": "개인 보관함을 내보내는 중"
|
||||
},
|
||||
"exportingPersonalVaultDescription": {
|
||||
"message": "Only the personal vault items associated with $EMAIL$ will be exported. Organization vault items will not be included.",
|
||||
|
@ -1841,22 +1841,22 @@
|
|||
}
|
||||
},
|
||||
"generator": {
|
||||
"message": "Generator"
|
||||
"message": "생성기"
|
||||
},
|
||||
"whatWouldYouLikeToGenerate": {
|
||||
"message": "What would you like to generate?"
|
||||
"message": "무엇을 생성하실건가요?"
|
||||
},
|
||||
"passwordType": {
|
||||
"message": "Password Type"
|
||||
"message": "비밀번호 유형"
|
||||
},
|
||||
"regenerateUsername": {
|
||||
"message": "Regenerate Username"
|
||||
"message": "아이디 재생성"
|
||||
},
|
||||
"generateUsername": {
|
||||
"message": "Generate Username"
|
||||
"message": "아이디 생성"
|
||||
},
|
||||
"usernameType": {
|
||||
"message": "Username Type"
|
||||
"message": "아이디 유형"
|
||||
},
|
||||
"plusAddressedEmail": {
|
||||
"message": "Plus Addressed Email"
|
||||
|
@ -1871,15 +1871,15 @@
|
|||
"message": "Use your domain's configured catch-all inbox."
|
||||
},
|
||||
"random": {
|
||||
"message": "Random"
|
||||
"message": "무작위"
|
||||
},
|
||||
"randomWord": {
|
||||
"message": "Random Word"
|
||||
"message": "무작위 단어"
|
||||
},
|
||||
"websiteName": {
|
||||
"message": "Website Name"
|
||||
"message": "웹사이트 이름"
|
||||
},
|
||||
"service": {
|
||||
"message": "Service"
|
||||
"message": "서비스"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -370,10 +370,10 @@
|
|||
"message": "Vai tiešām pārrakstīt esošo paroli?"
|
||||
},
|
||||
"overwriteUsername": {
|
||||
"message": "Overwrite Username"
|
||||
"message": "Pārrakstīt lietotājvārdu"
|
||||
},
|
||||
"overwriteUsernameConfirmation": {
|
||||
"message": "Are you sure you want to overwrite the current username?"
|
||||
"message": "Vai tiešām pārrakstīt pašreizējo lietotājvārdu?"
|
||||
},
|
||||
"noneFolder": {
|
||||
"message": "Nav mapes",
|
||||
|
@ -1198,7 +1198,7 @@
|
|||
"description": "Domain name. Ex. website.com"
|
||||
},
|
||||
"domainName": {
|
||||
"message": "Domain Name",
|
||||
"message": "Domēna vārds",
|
||||
"description": "Domain name. Ex. website.com"
|
||||
},
|
||||
"host": {
|
||||
|
@ -1841,45 +1841,45 @@
|
|||
}
|
||||
},
|
||||
"generator": {
|
||||
"message": "Generator"
|
||||
"message": "Veidotājs"
|
||||
},
|
||||
"whatWouldYouLikeToGenerate": {
|
||||
"message": "What would you like to generate?"
|
||||
"message": "Ko ir nepieciešams izveidot?"
|
||||
},
|
||||
"passwordType": {
|
||||
"message": "Password Type"
|
||||
"message": "Paroles veids"
|
||||
},
|
||||
"regenerateUsername": {
|
||||
"message": "Regenerate Username"
|
||||
"message": "Pārizveidot lietotājvārdu"
|
||||
},
|
||||
"generateUsername": {
|
||||
"message": "Generate Username"
|
||||
"message": "Izveidot lietotājvārdu"
|
||||
},
|
||||
"usernameType": {
|
||||
"message": "Username Type"
|
||||
"message": "Lietotājvārda veids"
|
||||
},
|
||||
"plusAddressedEmail": {
|
||||
"message": "Plus Addressed Email"
|
||||
"message": "E-pasta adrese ar plusu"
|
||||
},
|
||||
"plusAddressedEmailDesc": {
|
||||
"message": "Use your email provider's sub-addressing capabilities."
|
||||
"message": "Izmantot e-pasta pakalpojuma nodrošinātāja apakšadresēšanas spējas."
|
||||
},
|
||||
"catchallEmail": {
|
||||
"message": "Catch-all Email"
|
||||
"message": "Visu tverošā e-pasta adrese"
|
||||
},
|
||||
"catchallEmailDesc": {
|
||||
"message": "Use your domain's configured catch-all inbox."
|
||||
"message": "Izmantot uzstādīto domēna visu tverošo iesūtni."
|
||||
},
|
||||
"random": {
|
||||
"message": "Random"
|
||||
"message": "Nejauši"
|
||||
},
|
||||
"randomWord": {
|
||||
"message": "Random Word"
|
||||
"message": "Nejaušs vārds"
|
||||
},
|
||||
"websiteName": {
|
||||
"message": "Website Name"
|
||||
"message": "Tīmekļa vietnes nosaukums"
|
||||
},
|
||||
"service": {
|
||||
"message": "Service"
|
||||
"message": "Pakalpojums"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -370,7 +370,7 @@
|
|||
"message": "Czy na pewno chcesz zastąpić obecne hasło?"
|
||||
},
|
||||
"overwriteUsername": {
|
||||
"message": "Overwrite Username"
|
||||
"message": "Nadpisz nazwę użytkownika"
|
||||
},
|
||||
"overwriteUsernameConfirmation": {
|
||||
"message": "Are you sure you want to overwrite the current username?"
|
||||
|
@ -1198,7 +1198,7 @@
|
|||
"description": "Domain name. Ex. website.com"
|
||||
},
|
||||
"domainName": {
|
||||
"message": "Domain Name",
|
||||
"message": "Nazwa domeny",
|
||||
"description": "Domain name. Ex. website.com"
|
||||
},
|
||||
"host": {
|
||||
|
@ -1844,42 +1844,42 @@
|
|||
"message": "Generator"
|
||||
},
|
||||
"whatWouldYouLikeToGenerate": {
|
||||
"message": "What would you like to generate?"
|
||||
"message": "Co chcesz wygenerować?"
|
||||
},
|
||||
"passwordType": {
|
||||
"message": "Password Type"
|
||||
"message": "Rodzaj hasła"
|
||||
},
|
||||
"regenerateUsername": {
|
||||
"message": "Regenerate Username"
|
||||
"message": "Wygeneruj ponownie nazwę użytkownika"
|
||||
},
|
||||
"generateUsername": {
|
||||
"message": "Generate Username"
|
||||
"message": "Wygeneruj nazwę użytkownika"
|
||||
},
|
||||
"usernameType": {
|
||||
"message": "Username Type"
|
||||
"message": "Rodzaj nazwy użytkownika"
|
||||
},
|
||||
"plusAddressedEmail": {
|
||||
"message": "Plus Addressed Email"
|
||||
"message": "Adres e-mail z plusem"
|
||||
},
|
||||
"plusAddressedEmailDesc": {
|
||||
"message": "Use your email provider's sub-addressing capabilities."
|
||||
"message": "Użyj możliwości dodawania aliasów u swojego dostawcy poczty e-mail."
|
||||
},
|
||||
"catchallEmail": {
|
||||
"message": "Catch-all Email"
|
||||
"message": "Adres catch-all"
|
||||
},
|
||||
"catchallEmailDesc": {
|
||||
"message": "Use your domain's configured catch-all inbox."
|
||||
"message": "Użyj skonfigurowanej skrzynki catch-all w swojej domenie."
|
||||
},
|
||||
"random": {
|
||||
"message": "Random"
|
||||
},
|
||||
"randomWord": {
|
||||
"message": "Random Word"
|
||||
"message": "Losowe słowo"
|
||||
},
|
||||
"websiteName": {
|
||||
"message": "Website Name"
|
||||
"message": "Nazwa witryny"
|
||||
},
|
||||
"service": {
|
||||
"message": "Service"
|
||||
"message": "Usługa"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -373,7 +373,7 @@
|
|||
"message": "Overwrite Username"
|
||||
},
|
||||
"overwriteUsernameConfirmation": {
|
||||
"message": "Are you sure you want to overwrite the current username?"
|
||||
"message": "Tem certeza que deseja substituir o usuário atual?"
|
||||
},
|
||||
"noneFolder": {
|
||||
"message": "Nenhuma Pasta",
|
||||
|
|
|
@ -373,7 +373,7 @@
|
|||
"message": "Перезаписать имя пользователя"
|
||||
},
|
||||
"overwriteUsernameConfirmation": {
|
||||
"message": "Вы уверены, что хотите перезаписать текущее имя пользователя?"
|
||||
"message": "Вы хотите перезаписать текущее имя пользователя?"
|
||||
},
|
||||
"noneFolder": {
|
||||
"message": "Без папки",
|
||||
|
@ -1850,7 +1850,7 @@
|
|||
"message": "Тип пароля"
|
||||
},
|
||||
"regenerateUsername": {
|
||||
"message": "Восстановить имя пользователя"
|
||||
"message": "Пересоздать имя пользователя"
|
||||
},
|
||||
"generateUsername": {
|
||||
"message": "Создать имя пользователя"
|
||||
|
@ -1859,16 +1859,16 @@
|
|||
"message": "Тип имени пользователя"
|
||||
},
|
||||
"plusAddressedEmail": {
|
||||
"message": "Plus Addressed Email"
|
||||
"message": "Плюс-адресованные email"
|
||||
},
|
||||
"plusAddressedEmailDesc": {
|
||||
"message": "Use your email provider's sub-addressing capabilities."
|
||||
"message": "Использовать возможности суб-адресации вашего провайдера электронной почты."
|
||||
},
|
||||
"catchallEmail": {
|
||||
"message": "Универсальная электронная почта"
|
||||
"message": "Catch-all-адрес электронной почты"
|
||||
},
|
||||
"catchallEmailDesc": {
|
||||
"message": "Use your domain's configured catch-all inbox."
|
||||
"message": "Использовать настроенную в вашем домене почту catch-all."
|
||||
},
|
||||
"random": {
|
||||
"message": "Случайно"
|
||||
|
@ -1880,6 +1880,6 @@
|
|||
"message": "Название сайта"
|
||||
},
|
||||
"service": {
|
||||
"message": "Service"
|
||||
"message": "Служба"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -370,10 +370,10 @@
|
|||
"message": "Сигурно променити тренутну лозинку?"
|
||||
},
|
||||
"overwriteUsername": {
|
||||
"message": "Overwrite Username"
|
||||
"message": "Препиши име"
|
||||
},
|
||||
"overwriteUsernameConfirmation": {
|
||||
"message": "Are you sure you want to overwrite the current username?"
|
||||
"message": "Сигурно преписати тренутно име?"
|
||||
},
|
||||
"noneFolder": {
|
||||
"message": "Без фасцикле",
|
||||
|
@ -1198,7 +1198,7 @@
|
|||
"description": "Domain name. Ex. website.com"
|
||||
},
|
||||
"domainName": {
|
||||
"message": "Domain Name",
|
||||
"message": "Име домена",
|
||||
"description": "Domain name. Ex. website.com"
|
||||
},
|
||||
"host": {
|
||||
|
@ -1841,45 +1841,45 @@
|
|||
}
|
||||
},
|
||||
"generator": {
|
||||
"message": "Generator"
|
||||
"message": "Генератор"
|
||||
},
|
||||
"whatWouldYouLikeToGenerate": {
|
||||
"message": "What would you like to generate?"
|
||||
"message": "Шта желите да генеришете?"
|
||||
},
|
||||
"passwordType": {
|
||||
"message": "Password Type"
|
||||
"message": "Тип лозинке"
|
||||
},
|
||||
"regenerateUsername": {
|
||||
"message": "Regenerate Username"
|
||||
"message": "Поново генериши име"
|
||||
},
|
||||
"generateUsername": {
|
||||
"message": "Generate Username"
|
||||
"message": "Генериши име"
|
||||
},
|
||||
"usernameType": {
|
||||
"message": "Username Type"
|
||||
"message": "Тип имена"
|
||||
},
|
||||
"plusAddressedEmail": {
|
||||
"message": "Plus Addressed Email"
|
||||
"message": "Плус имејл адресе"
|
||||
},
|
||||
"plusAddressedEmailDesc": {
|
||||
"message": "Use your email provider's sub-addressing capabilities."
|
||||
"message": "Користите могућности подадресирања вашег добављача е-поште."
|
||||
},
|
||||
"catchallEmail": {
|
||||
"message": "Catch-all Email"
|
||||
"message": "„Ухвати све“ е-порука"
|
||||
},
|
||||
"catchallEmailDesc": {
|
||||
"message": "Use your domain's configured catch-all inbox."
|
||||
},
|
||||
"random": {
|
||||
"message": "Random"
|
||||
"message": "Случајно"
|
||||
},
|
||||
"randomWord": {
|
||||
"message": "Random Word"
|
||||
"message": "Случајна реч"
|
||||
},
|
||||
"websiteName": {
|
||||
"message": "Website Name"
|
||||
"message": "Име Вашег веб-сајта"
|
||||
},
|
||||
"service": {
|
||||
"message": "Service"
|
||||
"message": "Сервис"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1198,7 +1198,7 @@
|
|||
"description": "Domain name. Ex. website.com"
|
||||
},
|
||||
"domainName": {
|
||||
"message": "Domain Name",
|
||||
"message": "Domännamn",
|
||||
"description": "Domain name. Ex. website.com"
|
||||
},
|
||||
"host": {
|
||||
|
|
|
@ -370,10 +370,10 @@
|
|||
"message": "Ви дійсно хочете перезаписати поточний пароль?"
|
||||
},
|
||||
"overwriteUsername": {
|
||||
"message": "Overwrite Username"
|
||||
"message": "Перезаписати ім'я користувача"
|
||||
},
|
||||
"overwriteUsernameConfirmation": {
|
||||
"message": "Are you sure you want to overwrite the current username?"
|
||||
"message": "Ви дійсно бажаєте перезаписати поточне ім'я користувача?"
|
||||
},
|
||||
"noneFolder": {
|
||||
"message": "Без теки",
|
||||
|
@ -1198,7 +1198,7 @@
|
|||
"description": "Domain name. Ex. website.com"
|
||||
},
|
||||
"domainName": {
|
||||
"message": "Domain Name",
|
||||
"message": "Ім'я домену",
|
||||
"description": "Domain name. Ex. website.com"
|
||||
},
|
||||
"host": {
|
||||
|
@ -1841,45 +1841,45 @@
|
|||
}
|
||||
},
|
||||
"generator": {
|
||||
"message": "Generator"
|
||||
"message": "Генератор"
|
||||
},
|
||||
"whatWouldYouLikeToGenerate": {
|
||||
"message": "What would you like to generate?"
|
||||
"message": "Що ви бажаєте згенерувати?"
|
||||
},
|
||||
"passwordType": {
|
||||
"message": "Password Type"
|
||||
"message": "Тип пароля"
|
||||
},
|
||||
"regenerateUsername": {
|
||||
"message": "Regenerate Username"
|
||||
"message": "Повторно генерувати ім'я користувача"
|
||||
},
|
||||
"generateUsername": {
|
||||
"message": "Generate Username"
|
||||
"message": "Генерувати ім'я користувача"
|
||||
},
|
||||
"usernameType": {
|
||||
"message": "Username Type"
|
||||
"message": "Тип імені користувача"
|
||||
},
|
||||
"plusAddressedEmail": {
|
||||
"message": "Plus Addressed Email"
|
||||
"message": "Плюс адреса електронної пошти"
|
||||
},
|
||||
"plusAddressedEmailDesc": {
|
||||
"message": "Use your email provider's sub-addressing capabilities."
|
||||
"message": "Використовуйте розширені можливості адрес вашого постачальника електронної пошти."
|
||||
},
|
||||
"catchallEmail": {
|
||||
"message": "Catch-all Email"
|
||||
"message": "Адреса е-пошти Catch-all"
|
||||
},
|
||||
"catchallEmailDesc": {
|
||||
"message": "Use your domain's configured catch-all inbox."
|
||||
"message": "Використовуйте свою скриньку вхідних Catch-All власного домену."
|
||||
},
|
||||
"random": {
|
||||
"message": "Random"
|
||||
"message": "Випадково"
|
||||
},
|
||||
"randomWord": {
|
||||
"message": "Random Word"
|
||||
"message": "Випадкове слово"
|
||||
},
|
||||
"websiteName": {
|
||||
"message": "Website Name"
|
||||
"message": "Назва вебсайту"
|
||||
},
|
||||
"service": {
|
||||
"message": "Service"
|
||||
"message": "Послуга"
|
||||
}
|
||||
}
|
||||
|
|
19
src/main.ts
19
src/main.ts
|
@ -2,11 +2,9 @@ import * as path from "path";
|
|||
|
||||
import { app } from "electron";
|
||||
|
||||
import { BiometricMain } from "jslib-common/abstractions/biometric.main";
|
||||
import { StateFactory } from "jslib-common/factories/stateFactory";
|
||||
import { GlobalState } from "jslib-common/models/domain/globalState";
|
||||
import { StateService } from "jslib-common/services/state.service";
|
||||
import { KeytarStorageListener } from "jslib-electron/keytarStorageListener";
|
||||
import { ElectronLogService } from "jslib-electron/services/electronLog.service";
|
||||
import { ElectronMainMessagingService } from "jslib-electron/services/electronMainMessaging.service";
|
||||
import { ElectronStorageService } from "jslib-electron/services/electronStorage.service";
|
||||
|
@ -14,7 +12,9 @@ import { TrayMain } from "jslib-electron/tray.main";
|
|||
import { UpdaterMain } from "jslib-electron/updater.main";
|
||||
import { WindowMain } from "jslib-electron/window.main";
|
||||
|
||||
import { MenuMain } from "./main/menu.main";
|
||||
import { BiometricMain } from "./main/biometric/biometric.main";
|
||||
import { DesktopCredentialStorageListener } from "./main/desktopCredentialStorageListener";
|
||||
import { MenuMain } from "./main/menu/menu.main";
|
||||
import { MessagingMain } from "./main/messaging.main";
|
||||
import { NativeMessagingMain } from "./main/nativeMessaging.main";
|
||||
import { PowerMonitorMain } from "./main/powerMonitor.main";
|
||||
|
@ -27,7 +27,7 @@ export class Main {
|
|||
storageService: ElectronStorageService;
|
||||
messagingService: ElectronMainMessagingService;
|
||||
stateService: StateService;
|
||||
keytarStorageListener: KeytarStorageListener;
|
||||
desktopCredentialStorageListener: DesktopCredentialStorageListener;
|
||||
|
||||
windowMain: WindowMain;
|
||||
messagingMain: MessagingMain;
|
||||
|
@ -116,7 +116,7 @@ export class Main {
|
|||
|
||||
if (process.platform === "win32") {
|
||||
// eslint-disable-next-line
|
||||
const BiometricWindowsMain = require("jslib-electron/biometric.windows.main").default;
|
||||
const BiometricWindowsMain = require("./main/biometric/biometric.windows.main").default;
|
||||
this.biometricMain = new BiometricWindowsMain(
|
||||
this.i18nService,
|
||||
this.windowMain,
|
||||
|
@ -125,11 +125,14 @@ export class Main {
|
|||
);
|
||||
} else if (process.platform === "darwin") {
|
||||
// eslint-disable-next-line
|
||||
const BiometricDarwinMain = require("jslib-electron/biometric.darwin.main").default;
|
||||
const BiometricDarwinMain = require("./main/biometric/biometric.darwin.main").default;
|
||||
this.biometricMain = new BiometricDarwinMain(this.i18nService, this.stateService);
|
||||
}
|
||||
|
||||
this.keytarStorageListener = new KeytarStorageListener("Bitwarden", this.biometricMain);
|
||||
this.desktopCredentialStorageListener = new DesktopCredentialStorageListener(
|
||||
"Bitwarden",
|
||||
this.biometricMain
|
||||
);
|
||||
|
||||
this.nativeMessagingMain = new NativeMessagingMain(
|
||||
this.logService,
|
||||
|
@ -140,7 +143,7 @@ export class Main {
|
|||
}
|
||||
|
||||
bootstrap() {
|
||||
this.keytarStorageListener.init();
|
||||
this.desktopCredentialStorageListener.init();
|
||||
this.windowMain.init().then(
|
||||
async () => {
|
||||
const locale = await this.stateService.getLocale();
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
import { ipcMain, systemPreferences } from "electron";
|
||||
|
||||
import { I18nService } from "jslib-common/abstractions/i18n.service";
|
||||
import { StateService } from "jslib-common/abstractions/state.service";
|
||||
|
||||
import { BiometricMain } from "../biometric/biometric.main";
|
||||
|
||||
export default class BiometricDarwinMain implements BiometricMain {
|
||||
isError = false;
|
||||
|
||||
constructor(private i18nservice: I18nService, private stateService: StateService) {}
|
||||
|
||||
async init() {
|
||||
await this.stateService.setEnableBiometric(await this.supportsBiometric());
|
||||
await this.stateService.setBiometricText("unlockWithTouchId");
|
||||
await this.stateService.setNoAutoPromptBiometricsText("noAutoPromptTouchId");
|
||||
|
||||
// eslint-disable-next-line
|
||||
ipcMain.on("biometric", async (event: any, message: any) => {
|
||||
event.returnValue = await this.authenticateBiometric();
|
||||
});
|
||||
}
|
||||
|
||||
supportsBiometric(): Promise<boolean> {
|
||||
return Promise.resolve(systemPreferences.canPromptTouchID());
|
||||
}
|
||||
|
||||
async authenticateBiometric(): Promise<boolean> {
|
||||
try {
|
||||
await systemPreferences.promptTouchID(this.i18nservice.t("touchIdConsentMessage"));
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
export abstract class BiometricMain {
|
||||
isError: boolean;
|
||||
init: () => Promise<void>;
|
||||
supportsBiometric: () => Promise<boolean>;
|
||||
authenticateBiometric: () => Promise<boolean>;
|
||||
}
|
|
@ -0,0 +1,146 @@
|
|||
import { ipcMain } from "electron";
|
||||
import forceFocus from "forcefocus";
|
||||
|
||||
import { I18nService } from "jslib-common/abstractions/i18n.service";
|
||||
import { LogService } from "jslib-common/abstractions/log.service";
|
||||
import { StateService } from "jslib-common/abstractions/state.service";
|
||||
import { WindowMain } from "jslib-electron/window.main";
|
||||
|
||||
import { BiometricMain } from "src/main/biometric/biometric.main";
|
||||
|
||||
export default class BiometricWindowsMain implements BiometricMain {
|
||||
isError = false;
|
||||
|
||||
private windowsSecurityCredentialsUiModule: any;
|
||||
|
||||
constructor(
|
||||
private i18nservice: I18nService,
|
||||
private windowMain: WindowMain,
|
||||
private stateService: StateService,
|
||||
private logService: LogService
|
||||
) {}
|
||||
|
||||
async init() {
|
||||
this.windowsSecurityCredentialsUiModule = this.getWindowsSecurityCredentialsUiModule();
|
||||
let supportsBiometric = false;
|
||||
try {
|
||||
supportsBiometric = await this.supportsBiometric();
|
||||
} catch {
|
||||
// store error state so we can let the user know on the settings page
|
||||
this.isError = true;
|
||||
}
|
||||
await this.stateService.setEnableBiometric(supportsBiometric);
|
||||
await this.stateService.setBiometricText("unlockWithWindowsHello");
|
||||
await this.stateService.setNoAutoPromptBiometricsText("noAutoPromptWindowsHello");
|
||||
|
||||
ipcMain.on("biometric", async (event: any, message: any) => {
|
||||
event.returnValue = await this.authenticateBiometric();
|
||||
});
|
||||
}
|
||||
|
||||
async supportsBiometric(): Promise<boolean> {
|
||||
const availability = await this.checkAvailabilityAsync();
|
||||
|
||||
return this.getAllowedAvailabilities().includes(availability);
|
||||
}
|
||||
|
||||
async authenticateBiometric(): Promise<boolean> {
|
||||
const module = this.getWindowsSecurityCredentialsUiModule();
|
||||
if (module == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const verification = await this.requestVerificationAsync(
|
||||
this.i18nservice.t("windowsHelloConsentMessage")
|
||||
);
|
||||
|
||||
return verification === module.UserConsentVerificationResult.verified;
|
||||
}
|
||||
|
||||
getWindowsSecurityCredentialsUiModule(): any {
|
||||
try {
|
||||
if (this.windowsSecurityCredentialsUiModule == null && this.getWindowsMajorVersion() >= 10) {
|
||||
this.windowsSecurityCredentialsUiModule = require("@nodert-win10-rs4/windows.security.credentials.ui");
|
||||
}
|
||||
return this.windowsSecurityCredentialsUiModule;
|
||||
} catch {
|
||||
this.isError = true;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
async checkAvailabilityAsync(): Promise<any> {
|
||||
const module = this.getWindowsSecurityCredentialsUiModule();
|
||||
if (module != null) {
|
||||
// eslint-disable-next-line
|
||||
return new Promise((resolve, reject) => {
|
||||
try {
|
||||
module.UserConsentVerifier.checkAvailabilityAsync((error: Error, result: any) => {
|
||||
if (error) {
|
||||
return resolve(null);
|
||||
}
|
||||
return resolve(result);
|
||||
});
|
||||
} catch {
|
||||
this.isError = true;
|
||||
return resolve(null);
|
||||
}
|
||||
});
|
||||
}
|
||||
return Promise.resolve(null);
|
||||
}
|
||||
|
||||
async requestVerificationAsync(message: string): Promise<any> {
|
||||
const module = this.getWindowsSecurityCredentialsUiModule();
|
||||
if (module != null) {
|
||||
return new Promise((resolve, reject) => {
|
||||
try {
|
||||
module.UserConsentVerifier.requestVerificationAsync(
|
||||
message,
|
||||
(error: Error, result: any) => {
|
||||
if (error) {
|
||||
return resolve(null);
|
||||
}
|
||||
return resolve(result);
|
||||
}
|
||||
);
|
||||
|
||||
forceFocus.focusWindow(this.windowMain.win);
|
||||
} catch (error) {
|
||||
this.isError = true;
|
||||
return reject(error);
|
||||
}
|
||||
});
|
||||
}
|
||||
return Promise.resolve(null);
|
||||
}
|
||||
|
||||
getAllowedAvailabilities(): any[] {
|
||||
try {
|
||||
const module = this.getWindowsSecurityCredentialsUiModule();
|
||||
if (module != null) {
|
||||
return [
|
||||
module.UserConsentVerifierAvailability.available,
|
||||
module.UserConsentVerifierAvailability.deviceBusy,
|
||||
];
|
||||
}
|
||||
} catch {
|
||||
/*Ignore error*/
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
getWindowsMajorVersion(): number {
|
||||
if (process.platform !== "win32") {
|
||||
return -1;
|
||||
}
|
||||
try {
|
||||
// eslint-disable-next-line
|
||||
const version = require("os").release();
|
||||
return Number.parseInt(version.split(".")[0], 10);
|
||||
} catch {
|
||||
this.logService.error("Unable to resolve windows major version number");
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
import { ipcMain } from "electron";
|
||||
import { deletePassword, getPassword, setPassword } from "keytar";
|
||||
|
||||
import { BiometricMain } from "./biometric/biometric.main";
|
||||
|
||||
const AuthRequiredSuffix = "_biometric";
|
||||
const AuthenticatedActions = ["getPassword"];
|
||||
|
||||
export class DesktopCredentialStorageListener {
|
||||
constructor(private serviceName: string, private biometricService: BiometricMain) {}
|
||||
|
||||
init() {
|
||||
ipcMain.on("keytar", async (event: any, message: any) => {
|
||||
try {
|
||||
let serviceName = this.serviceName;
|
||||
message.keySuffix = "_" + (message.keySuffix ?? "");
|
||||
if (message.keySuffix !== "_") {
|
||||
serviceName += message.keySuffix;
|
||||
}
|
||||
|
||||
const authenticationRequired =
|
||||
AuthenticatedActions.includes(message.action) && AuthRequiredSuffix === message.keySuffix;
|
||||
const authenticated = !authenticationRequired || (await this.authenticateBiometric());
|
||||
|
||||
let val: string | boolean = null;
|
||||
if (authenticated && message.action && message.key) {
|
||||
if (message.action === "getPassword") {
|
||||
val = await getPassword(serviceName, message.key);
|
||||
} else if (message.action === "hasPassword") {
|
||||
const result = await getPassword(serviceName, message.key);
|
||||
val = result != null;
|
||||
} else if (message.action === "setPassword" && message.value) {
|
||||
await setPassword(serviceName, message.key, message.value);
|
||||
} else if (message.action === "deletePassword") {
|
||||
await deletePassword(serviceName, message.key);
|
||||
}
|
||||
}
|
||||
event.returnValue = val;
|
||||
} catch {
|
||||
event.returnValue = null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private async authenticateBiometric(): Promise<boolean> {
|
||||
if (this.biometricService) {
|
||||
return await this.biometricService.authenticateBiometric();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -2,7 +2,7 @@ import { app, Menu } from "electron";
|
|||
|
||||
import { BaseMenu } from "jslib-electron/baseMenu";
|
||||
|
||||
import { Main } from "../main";
|
||||
import { Main } from "../../main";
|
||||
|
||||
import { MenuUpdateRequest } from "./menu.updater";
|
||||
import { Menubar } from "./menubar";
|
|
@ -7,7 +7,7 @@ import { StateService } from "jslib-common/abstractions/state.service";
|
|||
|
||||
import { Main } from "../main";
|
||||
|
||||
import { MenuUpdateRequest } from "./menu.updater";
|
||||
import { MenuUpdateRequest } from "./menu/menu.updater";
|
||||
|
||||
const SyncInterval = 5 * 60 * 1000; // 5 minutes
|
||||
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
{
|
||||
"name": "@bitwarden/desktop",
|
||||
"version": "1.30.1",
|
||||
"version": "1.33.1",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@bitwarden/desktop",
|
||||
"version": "1.30.1",
|
||||
"version": "1.33.1",
|
||||
"license": "GPL-3.0",
|
||||
"dependencies": {
|
||||
"@nodert-win10-rs4/windows.security.credentials.ui": "^0.4.4",
|
||||
"forcefocus": "^1.1.0",
|
||||
"keytar": "7.7.0"
|
||||
"keytar": "^7.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@nodert-win10-rs4/windows.security.credentials.ui": {
|
||||
|
@ -286,31 +286,75 @@
|
|||
"integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
|
||||
},
|
||||
"node_modules/keytar": {
|
||||
"version": "7.7.0",
|
||||
"resolved": "https://registry.npmjs.org/keytar/-/keytar-7.7.0.tgz",
|
||||
"integrity": "sha512-YEY9HWqThQc5q5xbXbRwsZTh2PJ36OSYRjSv3NN2xf5s5dpLTjEZnC2YikR29OaVybf9nQ0dJ/80i40RS97t/A==",
|
||||
"version": "7.9.0",
|
||||
"resolved": "https://registry.npmjs.org/keytar/-/keytar-7.9.0.tgz",
|
||||
"integrity": "sha512-VPD8mtVtm5JNtA2AErl6Chp06JBfy7diFQ7TQQhdpWOl6MrCRB+eRbvAZUsbGQS9kiMq0coJsy0W0vHpDCkWsQ==",
|
||||
"hasInstallScript": true,
|
||||
"dependencies": {
|
||||
"node-addon-api": "^3.0.0",
|
||||
"prebuild-install": "^6.0.0"
|
||||
"node-addon-api": "^4.3.0",
|
||||
"prebuild-install": "^7.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/keytar/node_modules/decompress-response": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz",
|
||||
"integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==",
|
||||
"dependencies": {
|
||||
"mimic-response": "^3.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/keytar/node_modules/detect-libc": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.1.tgz",
|
||||
"integrity": "sha512-463v3ZeIrcWtdgIg6vI6XUncguvr2TnGl4SzDXinkt9mSLpBJKXT3mW6xT3VQdDN11+WVs29pgvivTc4Lp8v+w==",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/keytar/node_modules/mimic-response": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz",
|
||||
"integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==",
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/keytar/node_modules/node-abi": {
|
||||
"version": "3.15.0",
|
||||
"resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.15.0.tgz",
|
||||
"integrity": "sha512-Ic6z/j6I9RLm4ov7npo1I48UQr2BEyFCqh6p7S1dhEx9jPO0GPGq/e2Rb7x7DroQrmiVMz/Bw1vJm9sPAl2nxA==",
|
||||
"dependencies": {
|
||||
"semver": "^7.3.5"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/keytar/node_modules/prebuild-install": {
|
||||
"version": "6.1.4",
|
||||
"resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-6.1.4.tgz",
|
||||
"integrity": "sha512-Z4vpywnK1lBg+zdPCVCsKq0xO66eEV9rWo2zrROGGiRS4JtueBOdlB1FnY8lcy7JsUud/Q3ijUxyWN26Ika0vQ==",
|
||||
"version": "7.1.0",
|
||||
"resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.0.tgz",
|
||||
"integrity": "sha512-CNcMgI1xBypOyGqjp3wOc8AAo1nMhZS3Cwd3iHIxOdAUbb+YxdNuM4Z5iIrZ8RLvOsf3F3bl7b7xGq6DjQoNYA==",
|
||||
"dependencies": {
|
||||
"detect-libc": "^1.0.3",
|
||||
"detect-libc": "^2.0.0",
|
||||
"expand-template": "^2.0.3",
|
||||
"github-from-package": "0.0.0",
|
||||
"minimist": "^1.2.3",
|
||||
"mkdirp-classic": "^0.5.3",
|
||||
"napi-build-utils": "^1.0.1",
|
||||
"node-abi": "^2.21.0",
|
||||
"node-abi": "^3.3.0",
|
||||
"npmlog": "^4.0.1",
|
||||
"pump": "^3.0.0",
|
||||
"rc": "^1.2.7",
|
||||
"simple-get": "^3.0.3",
|
||||
"simple-get": "^4.0.0",
|
||||
"tar-fs": "^2.0.0",
|
||||
"tunnel-agent": "^0.6.0"
|
||||
},
|
||||
|
@ -318,7 +362,56 @@
|
|||
"prebuild-install": "bin.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/keytar/node_modules/semver": {
|
||||
"version": "7.3.7",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz",
|
||||
"integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==",
|
||||
"dependencies": {
|
||||
"lru-cache": "^6.0.0"
|
||||
},
|
||||
"bin": {
|
||||
"semver": "bin/semver.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/keytar/node_modules/simple-get": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz",
|
||||
"integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/feross"
|
||||
},
|
||||
{
|
||||
"type": "patreon",
|
||||
"url": "https://www.patreon.com/feross"
|
||||
},
|
||||
{
|
||||
"type": "consulting",
|
||||
"url": "https://feross.org/support"
|
||||
}
|
||||
],
|
||||
"dependencies": {
|
||||
"decompress-response": "^6.0.0",
|
||||
"once": "^1.3.1",
|
||||
"simple-concat": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/lru-cache": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
|
||||
"integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
|
||||
"dependencies": {
|
||||
"yallist": "^4.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/mimic-response": {
|
||||
|
@ -333,9 +426,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/minimist": {
|
||||
"version": "1.2.5",
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
|
||||
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw=="
|
||||
"version": "1.2.6",
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz",
|
||||
"integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q=="
|
||||
},
|
||||
"node_modules/mkdirp-classic": {
|
||||
"version": "0.5.3",
|
||||
|
@ -361,9 +454,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/node-addon-api": {
|
||||
"version": "3.2.1",
|
||||
"resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.2.1.tgz",
|
||||
"integrity": "sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A=="
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-4.3.0.tgz",
|
||||
"integrity": "sha512-73sE9+3UaLYYFmDsFZnqCInzPyh3MqIwZO9cw58yIqAZhONrrabrYyYe3TuIqtIiOuTXVhsGau8hcrhhwSsDIQ=="
|
||||
},
|
||||
"node_modules/noop-logger": {
|
||||
"version": "0.1.1",
|
||||
|
@ -494,9 +587,9 @@
|
|||
"integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc="
|
||||
},
|
||||
"node_modules/signal-exit": {
|
||||
"version": "3.0.6",
|
||||
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.6.tgz",
|
||||
"integrity": "sha512-sDl4qMFpijcGw22U5w63KmD3cZJfBuFlVNbVMKje2keoKML7X2UzWbc4XrmEbDwg0NXJc3yv4/ox7b+JWb57kQ=="
|
||||
"version": "3.0.7",
|
||||
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
|
||||
"integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="
|
||||
},
|
||||
"node_modules/simple-concat": {
|
||||
"version": "1.0.1",
|
||||
|
@ -518,9 +611,9 @@
|
|||
]
|
||||
},
|
||||
"node_modules/simple-get": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/simple-get/-/simple-get-3.1.0.tgz",
|
||||
"integrity": "sha512-bCR6cP+aTdScaQCnQKbPKtJOKDp/hj9EDLJo3Nw4y1QksqaovlW/bnptB6/c1e+qmNIDHRK+oXFDdEqBT8WzUA==",
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/simple-get/-/simple-get-3.1.1.tgz",
|
||||
"integrity": "sha512-CQ5LTKGfCpvE1K0n2us+kuMPbk/q0EKl82s4aheV9oXjFEz6W/Y7oQFVJuU6QG77hRT4Ghb5RURteF5vnWjupA==",
|
||||
"dependencies": {
|
||||
"decompress-response": "^4.2.0",
|
||||
"once": "^1.3.1",
|
||||
|
@ -623,9 +716,12 @@
|
|||
"integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
|
||||
},
|
||||
"node_modules/which-pm-runs": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/which-pm-runs/-/which-pm-runs-1.0.0.tgz",
|
||||
"integrity": "sha1-Zws6+8VS4LVd9rd4DKdGFfI60cs="
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/which-pm-runs/-/which-pm-runs-1.1.0.tgz",
|
||||
"integrity": "sha512-n1brCuqClxfFfq/Rb0ICg9giSZqCS+pLtccdag6C2HyufBrh3fBOiy9nb6ggRMvWOVH5GrdJskj5iGTZNxd7SA==",
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/wide-align": {
|
||||
"version": "1.1.5",
|
||||
|
@ -639,6 +735,11 @@
|
|||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
|
||||
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
|
||||
},
|
||||
"node_modules/yallist": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
|
||||
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
|
@ -842,45 +943,97 @@
|
|||
"integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
|
||||
},
|
||||
"keytar": {
|
||||
"version": "7.7.0",
|
||||
"resolved": "https://registry.npmjs.org/keytar/-/keytar-7.7.0.tgz",
|
||||
"integrity": "sha512-YEY9HWqThQc5q5xbXbRwsZTh2PJ36OSYRjSv3NN2xf5s5dpLTjEZnC2YikR29OaVybf9nQ0dJ/80i40RS97t/A==",
|
||||
"version": "7.9.0",
|
||||
"resolved": "https://registry.npmjs.org/keytar/-/keytar-7.9.0.tgz",
|
||||
"integrity": "sha512-VPD8mtVtm5JNtA2AErl6Chp06JBfy7diFQ7TQQhdpWOl6MrCRB+eRbvAZUsbGQS9kiMq0coJsy0W0vHpDCkWsQ==",
|
||||
"requires": {
|
||||
"node-addon-api": "^3.0.0",
|
||||
"prebuild-install": "^6.0.0"
|
||||
"node-addon-api": "^4.3.0",
|
||||
"prebuild-install": "^7.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"prebuild-install": {
|
||||
"version": "6.1.4",
|
||||
"resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-6.1.4.tgz",
|
||||
"integrity": "sha512-Z4vpywnK1lBg+zdPCVCsKq0xO66eEV9rWo2zrROGGiRS4JtueBOdlB1FnY8lcy7JsUud/Q3ijUxyWN26Ika0vQ==",
|
||||
"decompress-response": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz",
|
||||
"integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==",
|
||||
"requires": {
|
||||
"detect-libc": "^1.0.3",
|
||||
"mimic-response": "^3.1.0"
|
||||
}
|
||||
},
|
||||
"detect-libc": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.1.tgz",
|
||||
"integrity": "sha512-463v3ZeIrcWtdgIg6vI6XUncguvr2TnGl4SzDXinkt9mSLpBJKXT3mW6xT3VQdDN11+WVs29pgvivTc4Lp8v+w=="
|
||||
},
|
||||
"mimic-response": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz",
|
||||
"integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ=="
|
||||
},
|
||||
"node-abi": {
|
||||
"version": "3.15.0",
|
||||
"resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.15.0.tgz",
|
||||
"integrity": "sha512-Ic6z/j6I9RLm4ov7npo1I48UQr2BEyFCqh6p7S1dhEx9jPO0GPGq/e2Rb7x7DroQrmiVMz/Bw1vJm9sPAl2nxA==",
|
||||
"requires": {
|
||||
"semver": "^7.3.5"
|
||||
}
|
||||
},
|
||||
"prebuild-install": {
|
||||
"version": "7.1.0",
|
||||
"resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.0.tgz",
|
||||
"integrity": "sha512-CNcMgI1xBypOyGqjp3wOc8AAo1nMhZS3Cwd3iHIxOdAUbb+YxdNuM4Z5iIrZ8RLvOsf3F3bl7b7xGq6DjQoNYA==",
|
||||
"requires": {
|
||||
"detect-libc": "^2.0.0",
|
||||
"expand-template": "^2.0.3",
|
||||
"github-from-package": "0.0.0",
|
||||
"minimist": "^1.2.3",
|
||||
"mkdirp-classic": "^0.5.3",
|
||||
"napi-build-utils": "^1.0.1",
|
||||
"node-abi": "^2.21.0",
|
||||
"node-abi": "^3.3.0",
|
||||
"npmlog": "^4.0.1",
|
||||
"pump": "^3.0.0",
|
||||
"rc": "^1.2.7",
|
||||
"simple-get": "^3.0.3",
|
||||
"simple-get": "^4.0.0",
|
||||
"tar-fs": "^2.0.0",
|
||||
"tunnel-agent": "^0.6.0"
|
||||
}
|
||||
},
|
||||
"semver": {
|
||||
"version": "7.3.7",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz",
|
||||
"integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==",
|
||||
"requires": {
|
||||
"lru-cache": "^6.0.0"
|
||||
}
|
||||
},
|
||||
"simple-get": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz",
|
||||
"integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==",
|
||||
"requires": {
|
||||
"decompress-response": "^6.0.0",
|
||||
"once": "^1.3.1",
|
||||
"simple-concat": "^1.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"lru-cache": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
|
||||
"integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
|
||||
"requires": {
|
||||
"yallist": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"mimic-response": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-2.1.0.tgz",
|
||||
"integrity": "sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA=="
|
||||
},
|
||||
"minimist": {
|
||||
"version": "1.2.5",
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
|
||||
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw=="
|
||||
"version": "1.2.6",
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz",
|
||||
"integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q=="
|
||||
},
|
||||
"mkdirp-classic": {
|
||||
"version": "0.5.3",
|
||||
|
@ -906,9 +1059,9 @@
|
|||
}
|
||||
},
|
||||
"node-addon-api": {
|
||||
"version": "3.2.1",
|
||||
"resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.2.1.tgz",
|
||||
"integrity": "sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A=="
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-4.3.0.tgz",
|
||||
"integrity": "sha512-73sE9+3UaLYYFmDsFZnqCInzPyh3MqIwZO9cw58yIqAZhONrrabrYyYe3TuIqtIiOuTXVhsGau8hcrhhwSsDIQ=="
|
||||
},
|
||||
"noop-logger": {
|
||||
"version": "0.1.1",
|
||||
|
@ -1021,9 +1174,9 @@
|
|||
"integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc="
|
||||
},
|
||||
"signal-exit": {
|
||||
"version": "3.0.6",
|
||||
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.6.tgz",
|
||||
"integrity": "sha512-sDl4qMFpijcGw22U5w63KmD3cZJfBuFlVNbVMKje2keoKML7X2UzWbc4XrmEbDwg0NXJc3yv4/ox7b+JWb57kQ=="
|
||||
"version": "3.0.7",
|
||||
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
|
||||
"integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="
|
||||
},
|
||||
"simple-concat": {
|
||||
"version": "1.0.1",
|
||||
|
@ -1031,9 +1184,9 @@
|
|||
"integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q=="
|
||||
},
|
||||
"simple-get": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/simple-get/-/simple-get-3.1.0.tgz",
|
||||
"integrity": "sha512-bCR6cP+aTdScaQCnQKbPKtJOKDp/hj9EDLJo3Nw4y1QksqaovlW/bnptB6/c1e+qmNIDHRK+oXFDdEqBT8WzUA==",
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/simple-get/-/simple-get-3.1.1.tgz",
|
||||
"integrity": "sha512-CQ5LTKGfCpvE1K0n2us+kuMPbk/q0EKl82s4aheV9oXjFEz6W/Y7oQFVJuU6QG77hRT4Ghb5RURteF5vnWjupA==",
|
||||
"requires": {
|
||||
"decompress-response": "^4.2.0",
|
||||
"once": "^1.3.1",
|
||||
|
@ -1120,9 +1273,9 @@
|
|||
"integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
|
||||
},
|
||||
"which-pm-runs": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/which-pm-runs/-/which-pm-runs-1.0.0.tgz",
|
||||
"integrity": "sha1-Zws6+8VS4LVd9rd4DKdGFfI60cs="
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/which-pm-runs/-/which-pm-runs-1.1.0.tgz",
|
||||
"integrity": "sha512-n1brCuqClxfFfq/Rb0ICg9giSZqCS+pLtccdag6C2HyufBrh3fBOiy9nb6ggRMvWOVH5GrdJskj5iGTZNxd7SA=="
|
||||
},
|
||||
"wide-align": {
|
||||
"version": "1.1.5",
|
||||
|
@ -1136,6 +1289,11 @@
|
|||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
|
||||
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
|
||||
},
|
||||
"yallist": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
|
||||
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
"name": "@bitwarden/desktop",
|
||||
"productName": "Bitwarden",
|
||||
"description": "A secure and free password manager for all of your devices.",
|
||||
"version": "1.32.2",
|
||||
"version": "1.33.1",
|
||||
"author": "Bitwarden Inc. <hello@bitwarden.com> (https://bitwarden.com)",
|
||||
"homepage": "https://bitwarden.com",
|
||||
"license": "GPL-3.0",
|
||||
|
@ -14,6 +14,6 @@
|
|||
"dependencies": {
|
||||
"@nodert-win10-rs4/windows.security.credentials.ui": "^0.4.4",
|
||||
"forcefocus": "^1.1.0",
|
||||
"keytar": "7.7.0"
|
||||
"keytar": "^7.9.0"
|
||||
}
|
||||
}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue