mirror of
https://github.com/bitwarden/mobile
synced 2025-12-05 23:53:33 +00:00
Compare commits
17 Commits
community/
...
v2022.11.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1b34f68794 | ||
|
|
c9eb3f7f2d | ||
|
|
eab32ef59e | ||
|
|
bb759c16ea | ||
|
|
b4c0fba5e9 | ||
|
|
5e4192b7db | ||
|
|
0187676b5e | ||
|
|
3b796c6599 | ||
|
|
eeb0f61986 | ||
|
|
13eff49372 | ||
|
|
47d3a8b345 | ||
|
|
76042a2ef7 | ||
|
|
0507fcd54a | ||
|
|
1a69cc0591 | ||
|
|
0866a78802 | ||
|
|
cfda5fd6ff | ||
|
|
526904d1d8 |
@@ -14,10 +14,6 @@
|
||||
<string>Dist: Extension 2021</string>
|
||||
<key>com.8bit.bitwarden.share-extension</key>
|
||||
<string>Dist: Share Extension 2021</string>
|
||||
<key>com.8bit.bitwarden.watchkitapp</key>
|
||||
<string>Dist: Bitwarden Watch App</string>
|
||||
<key>com.8bit.bitwarden.watchkitapp.watchkitextension</key>
|
||||
<string>Dist: Bitwarden Watch App Extension</string>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
BIN
.github/secrets/GoogleService-Info.plist.gpg
vendored
BIN
.github/secrets/GoogleService-Info.plist.gpg
vendored
Binary file not shown.
BIN
.github/secrets/dist_watch_app.mobileprovision.gpg
vendored
BIN
.github/secrets/dist_watch_app.mobileprovision.gpg
vendored
Binary file not shown.
Binary file not shown.
173
.github/workflows/build.yml
vendored
173
.github/workflows/build.yml
vendored
@@ -4,10 +4,10 @@ name: Build
|
||||
on:
|
||||
push:
|
||||
branches-ignore:
|
||||
- "l10n_master"
|
||||
- "gh-pages"
|
||||
- 'l10n_master'
|
||||
- 'gh-pages'
|
||||
paths-ignore:
|
||||
- ".github/workflows/**"
|
||||
- '.github/workflows/**'
|
||||
workflow_dispatch:
|
||||
inputs: {}
|
||||
|
||||
@@ -42,15 +42,15 @@ jobs:
|
||||
id: branch-check
|
||||
run: |
|
||||
if [[ $(git ls-remote --heads origin rc) ]]; then
|
||||
echo "rc_branch_exists=1" >> $GITHUB_OUTPUT
|
||||
echo "::set-output name=rc_branch_exists::1"
|
||||
else
|
||||
echo "rc_branch_exists=0" >> $GITHUB_OUTPUT
|
||||
echo "::set-output name=rc_branch_exists::0"
|
||||
fi
|
||||
|
||||
if [[ $(git ls-remote --heads origin hotfix-rc) ]]; then
|
||||
echo "hotfix_branch_exists=1" >> $GITHUB_OUTPUT
|
||||
echo "::set-output name=hotfix_branch_exists::1"
|
||||
else
|
||||
echo "hotfix_branch_exists=0" >> $GITHUB_OUTPUT
|
||||
echo "::set-output name=hotfix_branch_exists::0"
|
||||
fi
|
||||
shell: bash
|
||||
|
||||
@@ -62,19 +62,16 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
variant: ["prod", "qa"]
|
||||
variant: ['prod', 'qa']
|
||||
steps:
|
||||
- name: Setup NuGet
|
||||
uses: nuget/setup-nuget@b2bc17b761a1d88cab755a776c7922eb26eefbfa # v1.0.6
|
||||
uses: nuget/setup-nuget@b2bc17b761a1d88cab755a776c7922eb26eefbfa # v1.0.6
|
||||
with:
|
||||
nuget-version: 5.9.0
|
||||
|
||||
- name: Set up MSBuild
|
||||
uses: microsoft/setup-msbuild@ab534842b4bdf384b8aaf93765dc6f721d9f5fab
|
||||
|
||||
- name: Setup Windows builder
|
||||
run: choco install checksum --no-progress
|
||||
|
||||
|
||||
- name: Work Around for broken Windows 2022 Runner Image
|
||||
run: |
|
||||
Set-Location "C:\Program Files (x86)\Microsoft Visual Studio\Installer\"
|
||||
@@ -151,17 +148,7 @@ jobs:
|
||||
shell: pwsh
|
||||
|
||||
- name: Run Core tests
|
||||
run: dotnet test test/Core.Test/Core.Test.csproj --logger "trx;LogFileName=test-results.trx"
|
||||
shell: pwsh
|
||||
|
||||
- name: Report test results
|
||||
uses: dorny/test-reporter@c9b3d0e2bd2a4e96aaf424dbaa31c46b42318226
|
||||
if: always()
|
||||
with:
|
||||
name: Test Results
|
||||
path: "**/test-results.trx"
|
||||
reporter: dotnet-trx
|
||||
fail-on-error: true
|
||||
run: dotnet test test/Core.Test/Core.Test.csproj
|
||||
|
||||
- name: Build Play Store publisher
|
||||
if: ${{ matrix.variant == 'prod' }}
|
||||
@@ -188,7 +175,7 @@ jobs:
|
||||
run: |
|
||||
$androidPath = $($env:GITHUB_WORKSPACE + "/src/Android/Android.csproj");
|
||||
$packageName = "com.x8bit.bitwarden";
|
||||
|
||||
|
||||
if ("${{ matrix.variant }}" -ne "prod")
|
||||
{
|
||||
$packageName = "com.x8bit.bitwarden.${{ matrix.variant }}";
|
||||
@@ -252,34 +239,6 @@ jobs:
|
||||
path: ./com.x8bit.bitwarden.${{ matrix.variant }}.apk
|
||||
if-no-files-found: error
|
||||
|
||||
- name: Create checksum for Prod .apk artifact
|
||||
if: ${{ matrix.variant == 'prod' }}
|
||||
run: |
|
||||
checksum -f="./com.x8bit.bitwarden.apk" `
|
||||
-t sha256 | Out-File -Encoding ASCII ./bw-android-apk-sha256.txt
|
||||
|
||||
- name: Create checksum for Other .apk artifact
|
||||
if: ${{ matrix.variant != 'prod' }}
|
||||
run: |
|
||||
checksum -f="./com.x8bit.bitwarden.${{ matrix.variant }}.apk" `
|
||||
-t sha256 | Out-File -Encoding ASCII ./bw-android-${{ matrix.variant }}-apk-sha256.txt
|
||||
|
||||
- name: Upload .apk sha file for prod
|
||||
if: ${{ matrix.variant == 'prod' }}
|
||||
uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535
|
||||
with:
|
||||
name: bw-android-apk-sha256.txt
|
||||
path: ./bw-android-apk-sha256.txt
|
||||
if-no-files-found: error
|
||||
|
||||
- name: Upload .apk sha file for other
|
||||
if: ${{ matrix.variant != 'prod' }}
|
||||
uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535
|
||||
with:
|
||||
name: bw-android-${{ matrix.variant }}-apk-sha256.txt
|
||||
path: ./bw-android-${{ matrix.variant }}-apk-sha256.txt
|
||||
if-no-files-found: error
|
||||
|
||||
- name: Deploy to Play Store
|
||||
if: ${{ matrix.variant == 'prod' && (( github.ref == 'refs/heads/master'
|
||||
&& needs.setup.outputs.rc_branch_exists == 0
|
||||
@@ -301,16 +260,13 @@ jobs:
|
||||
runs-on: windows-2022
|
||||
steps:
|
||||
- name: Setup NuGet
|
||||
uses: nuget/setup-nuget@b2bc17b761a1d88cab755a776c7922eb26eefbfa # v1.0.6
|
||||
uses: nuget/setup-nuget@b2bc17b761a1d88cab755a776c7922eb26eefbfa # v1.0.6
|
||||
with:
|
||||
nuget-version: 5.9.0
|
||||
|
||||
- name: Set up MSBuild
|
||||
uses: microsoft/setup-msbuild@ab534842b4bdf384b8aaf93765dc6f721d9f5fab
|
||||
|
||||
- name: Setup Windows builder
|
||||
run: choco install checksum --no-progress
|
||||
|
||||
- name: Work Around for broken Windows 2022 Runner Image
|
||||
run: |
|
||||
Set-Location "C:\Program Files (x86)\Microsoft Visual Studio\Installer\"
|
||||
@@ -481,26 +437,14 @@ jobs:
|
||||
path: ./com.x8bit.bitwarden-fdroid.apk
|
||||
if-no-files-found: error
|
||||
|
||||
- name: Create checksum for F-Droid artifact
|
||||
run: |
|
||||
checksum -f="./com.x8bit.bitwarden-fdroid.apk" `
|
||||
-t sha256 | Out-File -Encoding ASCII ./bw-fdroid-apk-sha256.txt
|
||||
|
||||
- name: Upload F-Droid sha file
|
||||
uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535
|
||||
with:
|
||||
name: bw-fdroid-apk-sha256.txt
|
||||
path: ./bw-fdroid-apk-sha256.txt
|
||||
if-no-files-found: error
|
||||
|
||||
|
||||
ios:
|
||||
name: Apple iOS
|
||||
runs-on: macos-12
|
||||
runs-on: macos-11
|
||||
needs: setup
|
||||
steps:
|
||||
- name: Setup NuGet
|
||||
uses: nuget/setup-nuget@b2bc17b761a1d88cab755a776c7922eb26eefbfa # v1.0.6
|
||||
uses: nuget/setup-nuget@b2bc17b761a1d88cab755a776c7922eb26eefbfa # v1.0.6
|
||||
with:
|
||||
nuget-version: 5.9.0
|
||||
|
||||
@@ -531,7 +475,7 @@ jobs:
|
||||
do
|
||||
VALUE=$(az keyvault secret show --vault-name $KEYVAULT --name $i --query value --output tsv)
|
||||
echo "::add-mask::$VALUE"
|
||||
echo "$i=$VALUE" >> $GITHUB_OUTPUT
|
||||
echo "::set-output name=$i::$VALUE"
|
||||
done
|
||||
|
||||
- name: Decrypt secrets
|
||||
@@ -553,14 +497,6 @@ jobs:
|
||||
gpg --quiet --batch --yes --decrypt --passphrase="$DECRYPT_FILE_PASSWORD" \
|
||||
--output $HOME/secrets/dist_share_extension.mobileprovision \
|
||||
./.github/secrets/dist_share_extension.mobileprovision.gpg
|
||||
gpg --quiet --batch --yes --decrypt --passphrase="$DECRYPT_FILE_PASSWORD" \
|
||||
--output $HOME/secrets/dist_watch_app.mobileprovision \
|
||||
./.github/secrets/dist_watch_app.mobileprovision.gpg
|
||||
gpg --quiet --batch --yes --decrypt --passphrase="$DECRYPT_FILE_PASSWORD" \
|
||||
--output $HOME/secrets/dist_watch_app_extension.mobileprovision \
|
||||
./.github/secrets/dist_watch_app_extension.mobileprovision.gpg
|
||||
gpg --quiet --batch --yes --decrypt --passphrase="$DECRYPT_FILE_PASSWORD" \
|
||||
--output ./src/watchOS/bitwarden/GoogleService-Info.plist ./.github/secrets/GoogleService-Info.plist.gpg
|
||||
shell: bash
|
||||
|
||||
- name: Increment version
|
||||
@@ -575,9 +511,6 @@ jobs:
|
||||
perl -0777 -pi.bak -e 's/<key>CFBundleVersion<\/key>\s*<string>1<\/string>/<key>CFBundleVersion<\/key>\n\t<string>'"$BUILD_NUMBER"'<\/string>/' ./src/iOS.Extension/Info.plist
|
||||
perl -0777 -pi.bak -e 's/<key>CFBundleVersion<\/key>\s*<string>1<\/string>/<key>CFBundleVersion<\/key>\n\t<string>'"$BUILD_NUMBER"'<\/string>/' ./src/iOS.Autofill/Info.plist
|
||||
perl -0777 -pi.bak -e 's/<key>CFBundleVersion<\/key>\s*<string>1<\/string>/<key>CFBundleVersion<\/key>\n\t<string>'"$BUILD_NUMBER"'<\/string>/' ./src/iOS.ShareExtension/Info.plist
|
||||
cd src/watchOS/bitwarden
|
||||
agvtool new-version -all $BUILD_NUMBER
|
||||
cd ../../..
|
||||
shell: bash
|
||||
|
||||
- name: Update Entitlements
|
||||
@@ -612,8 +545,6 @@ jobs:
|
||||
BITWARDEN_PROFILE_PATH=$HOME/secrets/dist_bitwarden.mobileprovision
|
||||
EXTENSION_PROFILE_PATH=$HOME/secrets/dist_extension.mobileprovision
|
||||
SHARE_EXTENSION_PROFILE_PATH=$HOME/secrets/dist_share_extension.mobileprovision
|
||||
WATCH_APP_PROFILE_PATH=$HOME/secrets/dist_watch_app.mobileprovision
|
||||
WATCH_APP_EXTENSION_PROFILE_PATH=$HOME/secrets/dist_watch_app_extension.mobileprovision
|
||||
PROFILES_DIR_PATH=$HOME/Library/MobileDevice/Provisioning\ Profiles
|
||||
|
||||
mkdir -p "$PROFILES_DIR_PATH"
|
||||
@@ -629,25 +560,6 @@ jobs:
|
||||
|
||||
SHARE_EXTENSION_UUID=$(grep UUID -A1 -a $SHARE_EXTENSION_PROFILE_PATH | grep -io "[-A-F0-9]\{36\}")
|
||||
cp $SHARE_EXTENSION_PROFILE_PATH "$PROFILES_DIR_PATH/$SHARE_EXTENSION_UUID.mobileprovision"
|
||||
|
||||
WATCH_APP_UUID=$(grep UUID -A1 -a $WATCH_APP_PROFILE_PATH | grep -io "[-A-F0-9]\{36\}")
|
||||
cp $WATCH_APP_PROFILE_PATH "$PROFILES_DIR_PATH/$WATCH_APP_UUID.mobileprovision"
|
||||
|
||||
WATCH_APP_EXTENSION_UUID=$(grep UUID -A1 -a $WATCH_APP_EXTENSION_PROFILE_PATH | grep -io "[-A-F0-9]\{36\}")
|
||||
cp $WATCH_APP_EXTENSION_PROFILE_PATH "$PROFILES_DIR_PATH/$WATCH_APP_EXTENSION_UUID.mobileprovision"
|
||||
shell: bash
|
||||
|
||||
- name: Bulid WatchApp
|
||||
run: |
|
||||
echo "########################################"
|
||||
echo "##### Build WatchApp with Release Configuration"
|
||||
echo "########################################"
|
||||
|
||||
xcodebuild archive -workspace ./src/watchOS/bitwarden/bitwarden.xcodeproj/project.xcworkspace -configuration Release -scheme bitwarden\ WatchKit\ App -archivePath ./src/watchOS/bitwarden
|
||||
|
||||
echo "########################################"
|
||||
echo "##### Done"
|
||||
echo "########################################"
|
||||
shell: bash
|
||||
|
||||
- name: Restore packages
|
||||
@@ -685,12 +597,7 @@ jobs:
|
||||
ARCHIVE_DSYMS_PATH="$HOME/Library/Developer/Xcode/Archives/*/*.xcarchive/dSYMs"
|
||||
EXPORT_PATH="./bitwarden-export"
|
||||
|
||||
WATCH_ARCHIVE_DSYMS_PATH="./src/watchOS/bitwarden.xcarchive/dSYMs/"
|
||||
WATCH_DSYMS_EXPORT_PATH="$EXPORT_PATH/Watch_dSYMs"
|
||||
|
||||
cp -r -v $ARCHIVE_DSYMS_PATH $EXPORT_PATH
|
||||
mkdir $WATCH_DSYMS_EXPORT_PATH
|
||||
cp -r -v $WATCH_ARCHIVE_DSYMS_PATH $WATCH_DSYMS_EXPORT_PATH
|
||||
cp -r $ARCHIVE_DSYMS_PATH $EXPORT_PATH
|
||||
shell: bash
|
||||
|
||||
- name: Upload App Store .ipa & dSYMs artifacts
|
||||
@@ -713,39 +620,23 @@ jobs:
|
||||
|
||||
- name: Upload dSYMs to App Center
|
||||
if: |
|
||||
(github.ref == 'refs/heads/master'
|
||||
&& needs.setup.outputs.rc_branch_exists == 0
|
||||
&& needs.setup.outputs.hotfix_branch_exists == 0)
|
||||
|| (github.ref == 'refs/heads/rc' && needs.setup.outputs.hotfix_branch_exists == 0)
|
||||
|| github.ref == 'refs/heads/hotfix-rc'
|
||||
(github.ref == 'refs/heads/master'
|
||||
&& needs.setup.outputs.rc_branch_exists == 0
|
||||
&& needs.setup.outputs.hotfix_branch_exists == 0)
|
||||
|| (github.ref == 'refs/heads/rc' && needs.setup.outputs.hotfix_branch_exists == 0)
|
||||
|| github.ref == 'refs/heads/hotfix-rc'
|
||||
env:
|
||||
APPCENTER_IOS_TOKEN: ${{ steps.retrieve-secrets.outputs.appcenter-ios-token }}
|
||||
run: appcenter crashes upload-symbols -a bitwarden/bitwarden -s "./bitwarden-export/dSYMs" --token $APPCENTER_IOS_TOKEN
|
||||
shell: bash
|
||||
|
||||
- name: Upload Watch dSYMs to Firebase Crashlytics
|
||||
if: |
|
||||
(github.ref == 'refs/heads/master'
|
||||
&& needs.setup.outputs.rc_branch_exists == 0
|
||||
&& needs.setup.outputs.hotfix_branch_exists == 0)
|
||||
|| (github.ref == 'refs/heads/rc' && needs.setup.outputs.hotfix_branch_exists == 0)
|
||||
|| github.ref == 'refs/heads/hotfix-rc'
|
||||
run: |
|
||||
|
||||
echo "########################################"
|
||||
echo "##### Uploading Watch dSYMs to Firebase"
|
||||
echo "########################################"
|
||||
|
||||
find "$HOME/Library/Developer/XCode/DerivedData" -name "upload-symbols" -exec chmod +x {} \; -exec {} -gsp "./src/watchOS/bitwarden/GoogleService-Info.plist" -p ios "./bitwarden-export/Watch_dSYMs" \;
|
||||
shell: bash
|
||||
|
||||
- name: Deploy to App Store
|
||||
if: |
|
||||
(github.ref == 'refs/heads/master'
|
||||
&& needs.setup.outputs.rc_branch_exists == 0
|
||||
&& needs.setup.outputs.hotfix_branch_exists == 0)
|
||||
|| (github.ref == 'refs/heads/rc' && needs.setup.outputs.hotfix_branch_exists == 0)
|
||||
|| github.ref == 'refs/heads/hotfix-rc'
|
||||
(github.ref == 'refs/heads/master'
|
||||
&& needs.setup.outputs.rc_branch_exists == 0
|
||||
&& needs.setup.outputs.hotfix_branch_exists == 0)
|
||||
|| (github.ref == 'refs/heads/rc' && needs.setup.outputs.hotfix_branch_exists == 0)
|
||||
|| github.ref == 'refs/heads/hotfix-rc'
|
||||
env:
|
||||
APPLE_ID_USERNAME: ${{ secrets.APPLE_ID_USERNAME }}
|
||||
APPLE_ID_PASSWORD: ${{ secrets.APPLE_ID_PASSWORD }}
|
||||
@@ -785,7 +676,7 @@ jobs:
|
||||
do
|
||||
VALUE=$(az keyvault secret show --vault-name $KEYVAULT --name $i --query value --output tsv)
|
||||
echo "::add-mask::$VALUE"
|
||||
echo "$i=$VALUE" >> $GITHUB_OUTPUT
|
||||
echo "::set-output name=$i::$VALUE"
|
||||
done
|
||||
|
||||
- name: Upload Sources
|
||||
@@ -813,9 +704,9 @@ jobs:
|
||||
steps:
|
||||
- name: Check if any job failed
|
||||
if: |
|
||||
(github.ref == 'refs/heads/master')
|
||||
|| (github.ref == 'refs/heads/rc')
|
||||
|| (github.ref == 'refs/heads/hotfix-rc')
|
||||
(github.ref == 'refs/heads/master')
|
||||
|| (github.ref == 'refs/heads/rc')
|
||||
|| (github.ref == 'refs/heads/hotfix-rc')
|
||||
env:
|
||||
CLOC_STATUS: ${{ needs.cloc.result }}
|
||||
ANDROID_STATUS: ${{ needs.android.result }}
|
||||
@@ -853,7 +744,7 @@ jobs:
|
||||
do
|
||||
VALUE=$(az keyvault secret show --vault-name $KEYVAULT --name $i --query value --output tsv)
|
||||
echo "::add-mask::$VALUE"
|
||||
echo "$i=$VALUE" >> $GITHUB_OUTPUT
|
||||
echo "::set-output name=$i::$VALUE"
|
||||
done
|
||||
|
||||
- name: Notify Slack on failure
|
||||
|
||||
4
.github/workflows/enforce-labels.yml
vendored
4
.github/workflows/enforce-labels.yml
vendored
@@ -12,5 +12,5 @@ jobs:
|
||||
- name: Enforce Label
|
||||
uses: yogevbd/enforce-label-action@8d1e1709b1011e6d90400a0e6cf7c0b77aa5efeb
|
||||
with:
|
||||
BANNED_LABELS: "hold,needs-qa"
|
||||
BANNED_LABELS_DESCRIPTION: "PRs with the hold or needs-qa labels cannot be merged"
|
||||
BANNED_LABELS: "hold"
|
||||
BANNED_LABELS_DESCRIPTION: "PRs on hold cannot be merged"
|
||||
|
||||
18
.github/workflows/release.yml
vendored
18
.github/workflows/release.yml
vendored
@@ -1,6 +1,5 @@
|
||||
---
|
||||
name: Release
|
||||
run-name: Release ${{ inputs.release_type }}
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
@@ -52,10 +51,9 @@ jobs:
|
||||
id: branch
|
||||
run: |
|
||||
BRANCH_NAME=$(basename ${{ github.ref }})
|
||||
echo "branch-name=$BRANCH_NAME" >> $GITHUB_OUTPUT
|
||||
echo "::set-output name=branch-name::$BRANCH_NAME"
|
||||
|
||||
- name: Create GitHub deployment
|
||||
if: ${{ github.event.inputs.release_type != 'Dry Run' }}
|
||||
uses: chrnorm/deployment-action@1b599fe41a0ef1f95191e7f2eec4743f2d7dfc48
|
||||
id: deployment
|
||||
with:
|
||||
@@ -64,7 +62,7 @@ jobs:
|
||||
environment: 'production'
|
||||
description: 'Deployment ${{ steps.version.outputs.version }} from branch ${{ steps.branch.outputs.branch-name }}'
|
||||
task: release
|
||||
|
||||
|
||||
|
||||
- name: Download all artifacts
|
||||
if: ${{ github.event.inputs.release_type != 'Dry Run' }}
|
||||
@@ -74,7 +72,7 @@ jobs:
|
||||
workflow_conclusion: success
|
||||
branch: ${{ steps.branch.outputs.branch-name }}
|
||||
|
||||
- name: Dry Run - Download all artifacts
|
||||
- name: Download all artifacts
|
||||
if: ${{ github.event.inputs.release_type == 'Dry Run' }}
|
||||
uses: dawidd6/action-download-artifact@575b1e4167df67acf7e692af784566618b23c71e # v2.17.10
|
||||
with:
|
||||
@@ -92,9 +90,7 @@ jobs:
|
||||
artifacts: "./com.x8bit.bitwarden.aab/com.x8bit.bitwarden.aab,
|
||||
./com.x8bit.bitwarden.apk/com.x8bit.bitwarden.apk,
|
||||
./com.x8bit.bitwarden-fdroid.apk/com.x8bit.bitwarden-fdroid.apk,
|
||||
./Bitwarden iOS.zip,
|
||||
./bw-android-apk-sha256.txt,
|
||||
./bw-fdroid-apk-sha256.txt"
|
||||
./Bitwarden iOS.zip"
|
||||
commit: ${{ github.sha }}
|
||||
tag: v${{ steps.version.outputs.version }}
|
||||
name: Version ${{ steps.version.outputs.version }}
|
||||
@@ -103,7 +99,7 @@ jobs:
|
||||
draft: true
|
||||
|
||||
- name: Update deployment status to Success
|
||||
if: ${{ github.event.inputs.release_type != 'Dry Run' && success() }}
|
||||
if: ${{ success() }}
|
||||
uses: chrnorm/deployment-status@07b3930847f65e71c9c6802ff5a402f6dfb46b86
|
||||
with:
|
||||
token: '${{ secrets.GITHUB_TOKEN }}'
|
||||
@@ -111,7 +107,7 @@ jobs:
|
||||
deployment-id: ${{ steps.deployment.outputs.deployment_id }}
|
||||
|
||||
- name: Update deployment status to Failure
|
||||
if: ${{ github.event.inputs.release_type != 'Dry Run' && failure() }}
|
||||
if: ${{ failure() }}
|
||||
uses: chrnorm/deployment-status@07b3930847f65e71c9c6802ff5a402f6dfb46b86
|
||||
with:
|
||||
token: '${{ secrets.GITHUB_TOKEN }}'
|
||||
@@ -137,7 +133,7 @@ jobs:
|
||||
branch: ${{ needs.release.outputs.branch-name }}
|
||||
name: com.x8bit.bitwarden-fdroid.apk
|
||||
|
||||
- name: Dry Run - Download F-Droid .apk artifact
|
||||
- name: Download F-Droid .apk artifact
|
||||
if: ${{ github.event.inputs.release_type == 'Dry Run' }}
|
||||
uses: dawidd6/action-download-artifact@575b1e4167df67acf7e692af784566618b23c71e # v2.17.10
|
||||
with:
|
||||
|
||||
6
.github/workflows/version-auto-bump.yml
vendored
6
.github/workflows/version-auto-bump.yml
vendored
@@ -21,15 +21,15 @@ jobs:
|
||||
env:
|
||||
RELEASE_TAG: ${{ github.ref }}
|
||||
run: |
|
||||
CURR_MAJOR=$(echo $RELEASE_TAG | sed -r 's/refs\/tags\/v([0-9]{4}\.[0-9]{1,2})\.([0-9]{1,2})/\1/')
|
||||
CURR_PATCH=$(echo $RELEASE_TAG | sed -r 's/refs\/tags\/v([0-9]{4}\.[0-9]{1,2})\.([0-9]{1,2})/\2/')
|
||||
CURR_MAJOR=$(echo $RELEASE_TAG | sed -r 's/v([0-9]{4}\.[0-9]{1,2})\.([0-9]{1,2})/\1/')
|
||||
CURR_PATCH=$(echo $RELEASE_TAG | sed -r 's/v([0-9]{4}\.[0-9]{1,2})\.([0-9]{1,2})/\2/')
|
||||
echo "Current Major: $CURR_MAJOR"
|
||||
echo "Current Patch: $CURR_PATCH"
|
||||
|
||||
NEW_PATCH=$((CURR_PATCH+1))
|
||||
NEW_VER=$CURR_MAJOR.$NEW_PATCH
|
||||
echo "New Version: $NEW_VER"
|
||||
echo "new-version=$NEW_VER" >> $GITHUB_OUTPUT
|
||||
echo "::set-output name=new-version::$NEW_VER"
|
||||
|
||||
trigger_version_bump:
|
||||
name: "Trigger version bump workflow"
|
||||
|
||||
10
.github/workflows/version-bump.yml
vendored
10
.github/workflows/version-bump.yml
vendored
@@ -37,7 +37,8 @@ jobs:
|
||||
git_commit_gpgsign: true
|
||||
|
||||
- name: Create Version Branch
|
||||
run: git switch -c version_bump_${{ github.event.inputs.version_number }}
|
||||
run: |
|
||||
git switch -c version_bump_${{ github.event.inputs.version_number }}
|
||||
|
||||
- name: Bump Version - Android XML
|
||||
uses: bitwarden/gh-actions/version-bump@03ad9a873c39cdc95dd8d77dbbda67f84db43945
|
||||
@@ -78,15 +79,16 @@ jobs:
|
||||
id: version-changed
|
||||
run: |
|
||||
if [ -n "$(git status --porcelain)" ]; then
|
||||
echo "changes_to_commit=TRUE" >> $GITHUB_OUTPUT
|
||||
echo "::set-output name=changes_to_commit::TRUE"
|
||||
else
|
||||
echo "changes_to_commit=FALSE" >> $GITHUB_OUTPUT
|
||||
echo "::set-output name=changes_to_commit::FALSE"
|
||||
echo "No changes to commit!";
|
||||
fi
|
||||
|
||||
- name: Commit files
|
||||
if: ${{ steps.version-changed.outputs.changes_to_commit == 'TRUE' }}
|
||||
run: git commit -m "Bumped version to ${{ github.event.inputs.version_number }}" -a
|
||||
run: |
|
||||
git commit -m "Bumped version to ${{ github.event.inputs.version_number }}" -a
|
||||
|
||||
- name: Push changes
|
||||
if: ${{ steps.version-changed.outputs.changes_to_commit == 'TRUE' }}
|
||||
|
||||
126
.gitignore
vendored
126
.gitignore
vendored
@@ -30,7 +30,6 @@ Components/
|
||||
[Rr]eleases/
|
||||
x64/
|
||||
x86/
|
||||
!src/lib/x86/
|
||||
build/
|
||||
bld/
|
||||
[Bb]in/
|
||||
@@ -211,128 +210,3 @@ project.lock.json
|
||||
.DS_Store
|
||||
src/App/Css
|
||||
tools
|
||||
|
||||
# Created by https://www.toptal.com/developers/gitignore/api/swift,objective-c
|
||||
# Edit at https://www.toptal.com/developers/gitignore?templates=swift,objective-c
|
||||
|
||||
### Objective-C ###
|
||||
# Xcode
|
||||
#
|
||||
# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
|
||||
|
||||
## User settings
|
||||
xcuserdata/
|
||||
|
||||
## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9)
|
||||
*.xcscmblueprint
|
||||
*.xccheckout
|
||||
|
||||
## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4)
|
||||
build/
|
||||
DerivedData/
|
||||
*.moved-aside
|
||||
*.pbxuser
|
||||
!default.pbxuser
|
||||
*.mode1v3
|
||||
!default.mode1v3
|
||||
*.mode2v3
|
||||
!default.mode2v3
|
||||
*.perspectivev3
|
||||
!default.perspectivev3
|
||||
|
||||
## Obj-C/Swift specific
|
||||
*.hmap
|
||||
|
||||
## App packaging
|
||||
*.ipa
|
||||
*.dSYM.zip
|
||||
*.dSYM
|
||||
|
||||
# CocoaPods
|
||||
# We recommend against adding the Pods directory to your .gitignore. However
|
||||
# you should judge for yourself, the pros and cons are mentioned at:
|
||||
# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
|
||||
# Pods/
|
||||
# Add this line if you want to avoid checking in source code from the Xcode workspace
|
||||
# *.xcworkspace
|
||||
|
||||
# Carthage
|
||||
# Add this line if you want to avoid checking in source code from Carthage dependencies.
|
||||
# Carthage/Checkouts
|
||||
|
||||
Carthage/Build/
|
||||
|
||||
# fastlane
|
||||
# It is recommended to not store the screenshots in the git repo.
|
||||
# Instead, use fastlane to re-generate the screenshots whenever they are needed.
|
||||
# For more information about the recommended setup visit:
|
||||
# https://docs.fastlane.tools/best-practices/source-control/#source-control
|
||||
|
||||
fastlane/report.xml
|
||||
fastlane/Preview.html
|
||||
fastlane/screenshots/**/*.png
|
||||
fastlane/test_output
|
||||
|
||||
# Code Injection
|
||||
# After new code Injection tools there's a generated folder /iOSInjectionProject
|
||||
# https://github.com/johnno1962/injectionforxcode
|
||||
|
||||
iOSInjectionProject/
|
||||
|
||||
### Objective-C Patch ###
|
||||
|
||||
### Swift ###
|
||||
# Xcode
|
||||
# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Playgrounds
|
||||
timeline.xctimeline
|
||||
playground.xcworkspace
|
||||
|
||||
# Swift Package Manager
|
||||
# Add this line if you want to avoid checking in source code from Swift Package Manager dependencies.
|
||||
# Packages/
|
||||
# Package.pins
|
||||
# Package.resolved
|
||||
# *.xcodeproj
|
||||
# Xcode automatically generates this directory with a .xcworkspacedata file and xcuserdata
|
||||
# hence it is not needed unless you have added a package configuration file to your project
|
||||
# .swiftpm
|
||||
|
||||
.build/
|
||||
|
||||
# CocoaPods
|
||||
# We recommend against adding the Pods directory to your .gitignore. However
|
||||
# you should judge for yourself, the pros and cons are mentioned at:
|
||||
# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
|
||||
# Pods/
|
||||
# Add this line if you want to avoid checking in source code from the Xcode workspace
|
||||
# *.xcworkspace
|
||||
|
||||
# Carthage
|
||||
# Add this line if you want to avoid checking in source code from Carthage dependencies.
|
||||
# Carthage/Checkouts
|
||||
|
||||
|
||||
# Accio dependency management
|
||||
Dependencies/
|
||||
.accio/
|
||||
|
||||
# fastlane
|
||||
# It is recommended to not store the screenshots in the git repo.
|
||||
# Instead, use fastlane to re-generate the screenshots whenever they are needed.
|
||||
# For more information about the recommended setup visit:
|
||||
# https://docs.fastlane.tools/best-practices/source-control/#source-control
|
||||
|
||||
|
||||
# Code Injection
|
||||
# After new code Injection tools there's a generated folder /iOSInjectionProject
|
||||
# https://github.com/johnno1962/injectionforxcode
|
||||
|
||||
|
||||
# End of https://www.toptal.com/developers/gitignore/api/swift,objective-c
|
||||
|
||||
@@ -12,7 +12,7 @@ The Bitwarden mobile application is written in C# with Xamarin Android, Xamarin
|
||||
|
||||
# Build/Run
|
||||
|
||||
Please refer to the [Mobile section](https://contributing.bitwarden.com/getting-started/clients/mobile/) of the [Contributing Documentation](https://contributing.bitwarden.com/) for build instructions, recommended tooling, code style tips, and lots of other great information to get you started.
|
||||
Please refer to the [Mobile section](https://contributing.bitwarden.com/mobile/) of the [Contributing Documentation](https://contributing.bitwarden.com/) for build instructions, recommended tooling, code style tips, and lots of other great information to get you started.
|
||||
|
||||
# We're Hiring!
|
||||
|
||||
|
||||
@@ -132,7 +132,6 @@ Task("UpdateAndroidCodeFiles")
|
||||
Path.Combine(_slnPath, "src", "Android", "Receivers", "PackageReplacedReceiver.cs"),
|
||||
Path.Combine(_slnPath, "src", "Android", "Receivers", "RestrictionsChangedReceiver.cs"),
|
||||
Path.Combine(_slnPath, "src", "Android", "Services", "DeviceActionService.cs"),
|
||||
Path.Combine(_slnPath, "src", "Android", "Services", "FileService.cs"),
|
||||
Path.Combine(_slnPath, "src", "Android", "Tiles", "AutofillTileService.cs"),
|
||||
Path.Combine(_slnPath, "src", "Android", "Tiles", "GeneratorTileService.cs"),
|
||||
Path.Combine(_slnPath, "src", "Android", "Tiles", "MyVaultTileService.cs"),
|
||||
|
||||
Binary file not shown.
@@ -33,7 +33,6 @@ namespace Bit.Droid.Accessibility
|
||||
// - Resources/xml/autofillservice.xml
|
||||
new Browser("alook.browser", "search_fragment_input_view"),
|
||||
new Browser("alook.browser.google", "search_fragment_input_view"),
|
||||
new Browser("app.vanadium.browser", "url_bar"),
|
||||
new Browser("com.amazon.cloud9", "url"),
|
||||
new Browser("com.android.browser", "url"),
|
||||
new Browser("com.android.chrome", "url_bar"),
|
||||
@@ -67,7 +66,6 @@ namespace Bit.Droid.Accessibility
|
||||
new Browser("com.mmbox.xbrowser", "search_box"),
|
||||
new Browser("com.mycompany.app.soulbrowser", "edit_text"),
|
||||
new Browser("com.naver.whale", "url_bar"),
|
||||
new Browser("com.neeva.app", "full_url_text_view"),
|
||||
new Browser("com.opera.browser", "url_field"),
|
||||
new Browser("com.opera.browser.beta", "url_field"),
|
||||
new Browser("com.opera.gx", "addressbarEdit"),
|
||||
@@ -76,7 +74,6 @@ namespace Bit.Droid.Accessibility
|
||||
new Browser("com.opera.touch", "addressbarEdit"),
|
||||
new Browser("com.qflair.browserq", "url"),
|
||||
new Browser("com.qwant.liberty", "mozac_browser_toolbar_url_view,url_bar_title"), // 2nd = Legacy (before v4)
|
||||
new Browser("com.rainsee.create", "search_box"),
|
||||
new Browser("com.sec.android.app.sbrowser", "location_bar_edit_text"),
|
||||
new Browser("com.sec.android.app.sbrowser.beta", "location_bar_edit_text"),
|
||||
new Browser("com.stoutner.privacybrowser.free", "url_edittext"),
|
||||
@@ -86,9 +83,6 @@ namespace Bit.Droid.Accessibility
|
||||
new Browser("com.vivaldi.browser.sopranos", "url_bar"),
|
||||
new Browser("com.yandex.browser", "bro_omnibar_address_title_text,bro_omnibox_collapsed_title",
|
||||
(s) => s.Split(new char[]{' ', ' '}).FirstOrDefault()), // 0 = Regular Space, 1 = No-break space (00A0)
|
||||
new Browser("com.yjllq.internet", "search_box"),
|
||||
new Browser("com.yjllq.kito", "search_box"),
|
||||
new Browser("com.yujian.ResideMenuDemo", "search_box"),
|
||||
new Browser("com.z28j.feel", "g2"),
|
||||
new Browser("idm.internet.download.manager", "search"),
|
||||
new Browser("idm.internet.download.manager.adm.lite", "search"),
|
||||
@@ -96,7 +90,6 @@ namespace Bit.Droid.Accessibility
|
||||
new Browser("io.github.forkmaintainers.iceraven", "mozac_browser_toolbar_url_view"),
|
||||
new Browser("mark.via", "am,an"),
|
||||
new Browser("mark.via.gp", "as"),
|
||||
new Browser("net.dezor.browser", "url_bar"),
|
||||
new Browser("net.slions.fulguris.full.download", "search"),
|
||||
new Browser("net.slions.fulguris.full.download.debug", "search"),
|
||||
new Browser("net.slions.fulguris.full.playstore", "search"),
|
||||
@@ -136,7 +129,6 @@ namespace Bit.Droid.Accessibility
|
||||
new Browser("com.htc.sense.browser", "title"),
|
||||
new Browser("com.jerky.browser2", "enterUrl"),
|
||||
new Browser("com.ksmobile.cb", "address_bar_edit_text"),
|
||||
new Browser("com.lemurbrowser.exts","url_bar"),
|
||||
new Browser("com.linkbubble.playstore", "url_text"),
|
||||
new Browser("com.mx.browser", "address_editor_with_progress"),
|
||||
new Browser("com.mx.browser.tablet", "address_editor_with_progress"),
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
<AndroidManifest>Properties\AndroidManifest.xml</AndroidManifest>
|
||||
<MonoAndroidResourcePrefix>Resources</MonoAndroidResourcePrefix>
|
||||
<MonoAndroidAssetsPrefix>Assets</MonoAndroidAssetsPrefix>
|
||||
<TargetFrameworkVersion>v13.0</TargetFrameworkVersion>
|
||||
<TargetFrameworkVersion>v12.1</TargetFrameworkVersion>
|
||||
<AndroidHttpClientHandlerType>Xamarin.Android.Net.AndroidClientHandler</AndroidHttpClientHandlerType>
|
||||
<NuGetPackageImportStamp>
|
||||
</NuGetPackageImportStamp>
|
||||
@@ -77,12 +77,12 @@
|
||||
<PackageReference Include="Portable.BouncyCastle">
|
||||
<Version>1.9.0</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Xamarin.AndroidX.AppCompat" Version="1.5.1.1" />
|
||||
<PackageReference Include="Xamarin.AndroidX.AutoFill" Version="1.1.0.14" />
|
||||
<PackageReference Include="Xamarin.AndroidX.CardView" Version="1.0.0.17" />
|
||||
<PackageReference Include="Xamarin.AndroidX.Core" Version="1.9.0.1" />
|
||||
<PackageReference Include="Xamarin.AndroidX.Legacy.Support.V4" Version="1.0.0.15" />
|
||||
<PackageReference Include="Xamarin.AndroidX.MediaRouter" Version="1.3.1.1" />
|
||||
<PackageReference Include="Xamarin.AndroidX.AppCompat" Version="1.5.1" />
|
||||
<PackageReference Include="Xamarin.AndroidX.AutoFill" Version="1.1.0.13" />
|
||||
<PackageReference Include="Xamarin.AndroidX.CardView" Version="1.0.0.16" />
|
||||
<PackageReference Include="Xamarin.AndroidX.Core" Version="1.9.0" />
|
||||
<PackageReference Include="Xamarin.AndroidX.Legacy.Support.V4" Version="1.0.0.14" />
|
||||
<PackageReference Include="Xamarin.AndroidX.MediaRouter" Version="1.3.1" />
|
||||
<PackageReference Include="Xamarin.Essentials">
|
||||
<Version>1.7.3</Version>
|
||||
</PackageReference>
|
||||
@@ -103,10 +103,8 @@
|
||||
<Compile Include="Accessibility\Browser.cs" />
|
||||
<Compile Include="Accessibility\NodeList.cs" />
|
||||
<Compile Include="Accessibility\KnownUsernameField.cs" />
|
||||
<Compile Include="Autofill\AutofillConstants.cs" />
|
||||
<Compile Include="Autofill\AutofillHelpers.cs" />
|
||||
<Compile Include="Autofill\AutofillService.cs" />
|
||||
<Compile Include="Autofill\AutofillExternalSelectionActivity.cs" />
|
||||
<Compile Include="Autofill\Field.cs" />
|
||||
<Compile Include="Autofill\FieldCollection.cs" />
|
||||
<Compile Include="Autofill\FilledItem.cs" />
|
||||
@@ -159,7 +157,6 @@
|
||||
<Compile Include="Services\AutofillHandler.cs" />
|
||||
<Compile Include="Constants.cs" />
|
||||
<Compile Include="Effects\RemoveFontPaddingEffect.cs" />
|
||||
<Compile Include="Services\WatchDeviceService.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidAsset Include="Assets\bwi-font.ttf" />
|
||||
@@ -169,10 +166,6 @@
|
||||
<GoogleServicesJson Include="google-services.json" />
|
||||
<GoogleServicesJson Include="google-services.json.enc" />
|
||||
<None Include="fdroid-keystore.jks.enc" />
|
||||
<AndroidNativeLibrary Include="lib\arm64-v8a\libargon2.so" />
|
||||
<AndroidNativeLibrary Include="lib\armeabi-v7a\libargon2.so" />
|
||||
<AndroidNativeLibrary Include="lib\x86\libargon2.so" />
|
||||
<AndroidNativeLibrary Include="lib\x86_64\libargon2.so" />
|
||||
<None Include="Properties\AndroidManifest.xml" />
|
||||
<None Include="upload-keystore.jks.enc" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
namespace Bit.Droid.Autofill
|
||||
{
|
||||
public class AutofillConstants
|
||||
{
|
||||
public const string AutofillFramework = "autofillFramework";
|
||||
public const string AutofillFrameworkFillType = "autofillFrameworkFillType";
|
||||
public const string AutofillFrameworkUri = "autofillFrameworkUri";
|
||||
public const string AutofillFrameworkCipherId = "autofillFrameworkCipherId";
|
||||
}
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
using System.Threading.Tasks;
|
||||
using Android.App;
|
||||
using Android.Content.PM;
|
||||
using Android.OS;
|
||||
using Bit.Core.Abstractions;
|
||||
using Bit.Core.Utilities;
|
||||
using Bit.Droid.Utilities;
|
||||
|
||||
namespace Bit.Droid.Autofill
|
||||
{
|
||||
[Activity(
|
||||
NoHistory = true,
|
||||
LaunchMode = LaunchMode.SingleTop)]
|
||||
public class AutofillExternalSelectionActivity : Xamarin.Forms.Platform.Android.FormsAppCompatActivity
|
||||
{
|
||||
protected override void OnCreate(Bundle bundle)
|
||||
{
|
||||
Intent?.Validate();
|
||||
base.OnCreate(bundle);
|
||||
|
||||
var cipherId = Intent?.GetStringExtra(AutofillConstants.AutofillFrameworkCipherId);
|
||||
if (string.IsNullOrEmpty(cipherId))
|
||||
{
|
||||
SetResult(Result.Canceled);
|
||||
Finish();
|
||||
return;
|
||||
}
|
||||
|
||||
GetCipherAndPerformAutofillAsync(cipherId).FireAndForget();
|
||||
}
|
||||
|
||||
private async Task GetCipherAndPerformAutofillAsync(string cipherId)
|
||||
{
|
||||
var cipherService = ServiceContainer.Resolve<ICipherService>();
|
||||
var cipher = await cipherService.GetAsync(cipherId);
|
||||
var decCipher = await cipher.DecryptAsync();
|
||||
|
||||
var autofillHandler = ServiceContainer.Resolve<IAutofillHandler>();
|
||||
autofillHandler.Autofill(decCipher);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -54,7 +54,6 @@ namespace Bit.Droid.Autofill
|
||||
{
|
||||
"alook.browser",
|
||||
"alook.browser.google",
|
||||
"app.vanadium.browser",
|
||||
"com.amazon.cloud9",
|
||||
"com.android.browser",
|
||||
"com.android.chrome",
|
||||
@@ -79,7 +78,6 @@ namespace Bit.Droid.Autofill
|
||||
"com.jamal2367.styx",
|
||||
"com.kiwibrowser.browser",
|
||||
"com.kiwibrowser.browser.dev",
|
||||
"com.lemurbrowser.exts",
|
||||
"com.microsoft.emmx",
|
||||
"com.microsoft.emmx.beta",
|
||||
"com.microsoft.emmx.canary",
|
||||
@@ -88,7 +86,6 @@ namespace Bit.Droid.Autofill
|
||||
"com.mmbox.xbrowser",
|
||||
"com.mycompany.app.soulbrowser",
|
||||
"com.naver.whale",
|
||||
"com.neeva.app",
|
||||
"com.opera.browser",
|
||||
"com.opera.browser.beta",
|
||||
"com.opera.gx",
|
||||
@@ -97,7 +94,6 @@ namespace Bit.Droid.Autofill
|
||||
"com.opera.touch",
|
||||
"com.qflair.browserq",
|
||||
"com.qwant.liberty",
|
||||
"com.rainsee.create",
|
||||
"com.sec.android.app.sbrowser",
|
||||
"com.sec.android.app.sbrowser.beta",
|
||||
"com.stoutner.privacybrowser.free",
|
||||
@@ -106,9 +102,6 @@ namespace Bit.Droid.Autofill
|
||||
"com.vivaldi.browser.snapshot",
|
||||
"com.vivaldi.browser.sopranos",
|
||||
"com.yandex.browser",
|
||||
"com.yjllq.internet",
|
||||
"com.yjllq.kito",
|
||||
"com.yujian.ResideMenuDemo",
|
||||
"com.z28j.feel",
|
||||
"idm.internet.download.manager",
|
||||
"idm.internet.download.manager.adm.lite",
|
||||
@@ -116,7 +109,6 @@ namespace Bit.Droid.Autofill
|
||||
"io.github.forkmaintainers.iceraven",
|
||||
"mark.via",
|
||||
"mark.via.gp",
|
||||
"net.dezor.browser",
|
||||
"net.slions.fulguris.full.download",
|
||||
"net.slions.fulguris.full.download.debug",
|
||||
"net.slions.fulguris.full.playstore",
|
||||
@@ -215,7 +207,7 @@ namespace Bit.Droid.Autofill
|
||||
}
|
||||
}
|
||||
var dataset = BuildDataset(parser.ApplicationContext, parser.FieldCollection, items[i],
|
||||
true, inlinePresentationSpec);
|
||||
inlinePresentationSpec);
|
||||
if (dataset != null)
|
||||
{
|
||||
responseBuilder.AddDataset(dataset);
|
||||
@@ -229,7 +221,7 @@ namespace Bit.Droid.Autofill
|
||||
}
|
||||
|
||||
public static Dataset BuildDataset(Context context, FieldCollection fields, FilledItem filledItem,
|
||||
bool includeAuthIntent, InlinePresentationSpec inlinePresentationSpec = null)
|
||||
InlinePresentationSpec inlinePresentationSpec = null)
|
||||
{
|
||||
var overlayPresentation = BuildOverlayPresentation(
|
||||
filledItem.Name,
|
||||
@@ -250,15 +242,6 @@ namespace Bit.Droid.Autofill
|
||||
{
|
||||
datasetBuilder.SetInlinePresentation(inlinePresentation);
|
||||
}
|
||||
if (includeAuthIntent)
|
||||
{
|
||||
var intent = new Intent(context, typeof(AutofillExternalSelectionActivity));
|
||||
intent.PutExtra(AutofillConstants.AutofillFramework, true);
|
||||
intent.PutExtra(AutofillConstants.AutofillFrameworkCipherId, filledItem.Id);
|
||||
var pendingIntent = PendingIntent.GetActivity(context, ++_pendingIntentId, intent,
|
||||
AndroidHelpers.AddPendingIntentMutabilityFlag(PendingIntentFlags.CancelCurrent, true));
|
||||
datasetBuilder.SetAuthentication(pendingIntent?.IntentSender);
|
||||
}
|
||||
if (filledItem.ApplyToFields(fields, datasetBuilder))
|
||||
{
|
||||
return datasetBuilder.Build();
|
||||
@@ -270,26 +253,25 @@ namespace Bit.Droid.Autofill
|
||||
IList<InlinePresentationSpec> inlinePresentationSpecs = null)
|
||||
{
|
||||
var intent = new Intent(context, typeof(MainActivity));
|
||||
intent.PutExtra(AutofillConstants.AutofillFramework, true);
|
||||
intent.PutExtra("autofillFramework", true);
|
||||
if (fields.FillableForLogin)
|
||||
{
|
||||
intent.PutExtra(AutofillConstants.AutofillFrameworkFillType, (int)CipherType.Login);
|
||||
intent.PutExtra("autofillFrameworkFillType", (int)CipherType.Login);
|
||||
}
|
||||
else if (fields.FillableForCard)
|
||||
{
|
||||
intent.PutExtra(AutofillConstants.AutofillFrameworkFillType, (int)CipherType.Card);
|
||||
intent.PutExtra("autofillFrameworkFillType", (int)CipherType.Card);
|
||||
}
|
||||
else if (fields.FillableForIdentity)
|
||||
{
|
||||
intent.PutExtra(AutofillConstants.AutofillFrameworkFillType, (int)CipherType.Identity);
|
||||
intent.PutExtra("autofillFrameworkFillType", (int)CipherType.Identity);
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
intent.PutExtra(AutofillConstants.AutofillFrameworkUri, uri);
|
||||
var pendingIntent = PendingIntent.GetActivity(context, ++_pendingIntentId, intent,
|
||||
AndroidHelpers.AddPendingIntentMutabilityFlag(PendingIntentFlags.CancelCurrent, true));
|
||||
intent.PutExtra("autofillFrameworkUri", uri);
|
||||
var pendingIntent = PendingIntent.GetActivity(context, ++_pendingIntentId, intent, AndroidHelpers.AddPendingIntentMutabilityFlag(PendingIntentFlags.CancelCurrent, true));
|
||||
|
||||
var overlayPresentation = BuildOverlayPresentation(
|
||||
AppResources.AutofillWithBitwarden,
|
||||
|
||||
@@ -12,7 +12,6 @@ namespace Bit.Droid.Autofill
|
||||
private List<Field> _passwordFields = null;
|
||||
private List<Field> _usernameFields = null;
|
||||
private HashSet<string> _ignoreSearchTerms = new HashSet<string> { "search", "find", "recipient", "edit" };
|
||||
private HashSet<string> _usernameTerms = new HashSet<string> { "email", "phone", "username"};
|
||||
private HashSet<string> _passwordTerms = new HashSet<string> { "password", "pswd" };
|
||||
|
||||
public List<AutofillId> AutofillIds { get; private set; } = new List<AutofillId>();
|
||||
@@ -99,11 +98,6 @@ namespace Bit.Droid.Autofill
|
||||
_usernameFields.Add(usernameField);
|
||||
}
|
||||
}
|
||||
|
||||
if (!_usernameFields.Any())
|
||||
{
|
||||
_usernameFields = Fields.Where(f => FieldIsUsername(f)).ToList();
|
||||
}
|
||||
}
|
||||
return _usernameFields;
|
||||
}
|
||||
@@ -327,23 +321,13 @@ namespace Bit.Droid.Autofill
|
||||
}
|
||||
|
||||
return inputTypePassword && !ValueContainsAnyTerms(f.IdEntry, _ignoreSearchTerms) &&
|
||||
!ValueContainsAnyTerms(f.Hint, _ignoreSearchTerms) && !FieldIsUsername(f);
|
||||
!ValueContainsAnyTerms(f.Hint, _ignoreSearchTerms);
|
||||
}
|
||||
|
||||
private bool FieldHasPasswordTerms(Field f)
|
||||
{
|
||||
return ValueContainsAnyTerms(f.IdEntry, _passwordTerms) || ValueContainsAnyTerms(f.Hint, _passwordTerms);
|
||||
}
|
||||
|
||||
private bool FieldIsUsername(Field f)
|
||||
{
|
||||
return f.InputType.HasFlag(InputTypes.TextVariationWebEmailAddress) || FieldHasUsernameTerms(f);
|
||||
}
|
||||
|
||||
private bool FieldHasUsernameTerms(Field f)
|
||||
{
|
||||
return ValueContainsAnyTerms(f.IdEntry, _usernameTerms) || ValueContainsAnyTerms(f.Hint, _usernameTerms);
|
||||
}
|
||||
|
||||
private bool ValueContainsAnyTerms(string value, HashSet<string> terms)
|
||||
{
|
||||
@@ -355,4 +339,4 @@ namespace Bit.Droid.Autofill
|
||||
return terms.Any(t => lowerValue.Contains(t));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -23,7 +23,6 @@ namespace Bit.Droid.Autofill
|
||||
|
||||
public FilledItem(CipherView cipher)
|
||||
{
|
||||
Id = cipher.Id;
|
||||
Name = cipher.Name;
|
||||
Type = cipher.Type;
|
||||
Subtitle = cipher.SubTitle;
|
||||
@@ -56,7 +55,6 @@ namespace Bit.Droid.Autofill
|
||||
}
|
||||
}
|
||||
|
||||
public string Id { get; set; }
|
||||
public string Name { get; set; }
|
||||
public string Subtitle { get; set; } = string.Empty;
|
||||
public int Icon { get; set; } = Resource.Drawable.login;
|
||||
|
||||
@@ -18,7 +18,6 @@ using Bit.Core;
|
||||
using Bit.Core.Abstractions;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Utilities;
|
||||
using Bit.Droid.Autofill;
|
||||
using Bit.Droid.Receivers;
|
||||
using Bit.Droid.Utilities;
|
||||
using Newtonsoft.Json;
|
||||
@@ -323,13 +322,13 @@ namespace Bit.Droid
|
||||
{
|
||||
var options = new AppOptions
|
||||
{
|
||||
Uri = Intent.GetStringExtra("uri") ?? Intent.GetStringExtra(AutofillConstants.AutofillFrameworkUri),
|
||||
Uri = Intent.GetStringExtra("uri") ?? Intent.GetStringExtra("autofillFrameworkUri"),
|
||||
MyVaultTile = Intent.GetBooleanExtra("myVaultTile", false),
|
||||
GeneratorTile = Intent.GetBooleanExtra("generatorTile", false),
|
||||
FromAutofillFramework = Intent.GetBooleanExtra(AutofillConstants.AutofillFramework, false),
|
||||
FromAutofillFramework = Intent.GetBooleanExtra("autofillFramework", false),
|
||||
CreateSend = GetCreateSendRequest(Intent)
|
||||
};
|
||||
var fillType = Intent.GetIntExtra(AutofillConstants.AutofillFrameworkFillType, 0);
|
||||
var fillType = Intent.GetIntExtra("autofillFrameworkFillType", 0);
|
||||
if (fillType > 0)
|
||||
{
|
||||
options.FillType = (CipherType)fillType;
|
||||
|
||||
@@ -45,16 +45,9 @@ namespace Bit.Droid
|
||||
if (ServiceContainer.RegisteredServices.Count == 0)
|
||||
{
|
||||
RegisterLocalServices();
|
||||
|
||||
var deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
||||
ServiceContainer.Init(deviceActionService.DeviceUserAgent, Core.Constants.ClearCiphersCacheKey,
|
||||
Core.Constants.AndroidAllClearCipherCacheKeys);
|
||||
|
||||
ServiceContainer.Register<IWatchDeviceService>(new WatchDeviceService(ServiceContainer.Resolve<ICipherService>(),
|
||||
ServiceContainer.Resolve<IEnvironmentService>(),
|
||||
ServiceContainer.Resolve<IStateService>(),
|
||||
ServiceContainer.Resolve<IVaultTimeoutService>()));
|
||||
|
||||
InitializeAppSetup();
|
||||
|
||||
// TODO: Update when https://github.com/bitwarden/mobile/pull/1662 gets merged
|
||||
@@ -80,9 +73,8 @@ namespace Bit.Droid
|
||||
ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService"),
|
||||
ServiceContainer.Resolve<IAuthService>("authService"),
|
||||
ServiceContainer.Resolve<ILogger>("logger"),
|
||||
ServiceContainer.Resolve<IMessagingService>("messagingService"),
|
||||
ServiceContainer.Resolve<IWatchDeviceService>());
|
||||
ServiceContainer.Register<IAccountsManager>("accountsManager", accountsManager);
|
||||
ServiceContainer.Resolve<IMessagingService>("messagingService"));
|
||||
ServiceContainer.Register<IAccountsManager>("accountsManager", accountsManager);
|
||||
}
|
||||
#if !FDROID
|
||||
if (Build.VERSION.SdkInt <= BuildVersionCodes.Kitkat)
|
||||
@@ -149,10 +141,9 @@ namespace Bit.Droid
|
||||
var clipboardService = new ClipboardService(stateService);
|
||||
var deviceActionService = new DeviceActionService(stateService, messagingService);
|
||||
var fileService = new FileService(stateService, broadcasterService);
|
||||
var autofillHandler = new AutofillHandler(stateService, messagingService, clipboardService, new LazyResolve<IEventService>());
|
||||
var platformUtilsService = new MobilePlatformUtilsService(deviceActionService, clipboardService,
|
||||
messagingService, broadcasterService);
|
||||
var autofillHandler = new AutofillHandler(stateService, messagingService, clipboardService,
|
||||
platformUtilsService, new LazyResolve<IEventService>());
|
||||
var biometricService = new BiometricService();
|
||||
var cryptoFunctionService = new PclCryptoFunctionService(cryptoPrimitiveService);
|
||||
var cryptoService = new CryptoService(stateService, cryptoFunctionService);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:versionCode="1" android:versionName="2023.1.1" android:installLocation="internalOnly" package="com.x8bit.bitwarden">
|
||||
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="33" />
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:versionCode="1" android:versionName="2022.11.0" android:installLocation="internalOnly" package="com.x8bit.bitwarden">
|
||||
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="32" />
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.NFC" />
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
@@ -40,16 +40,10 @@
|
||||
</intent-filter>
|
||||
</activity>
|
||||
</application>
|
||||
<!-- Support for Xamarin.Essentials.Browser.OpenAsync (for Android > 11) -->
|
||||
<!-- Related docs: https://learn.microsoft.com/en-us/xamarin/essentials/open-browser?tabs=android -->
|
||||
<!-- Package visibility (for Android 11+) -->
|
||||
<queries>
|
||||
<intent>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<data android:scheme="http"/>
|
||||
</intent>
|
||||
<intent>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<data android:scheme="https"/>
|
||||
<action android:name="*" />
|
||||
</intent>
|
||||
</queries>
|
||||
</manifest>
|
||||
@@ -1,5 +1,4 @@
|
||||
using Android.Content;
|
||||
using Android.OS;
|
||||
|
||||
namespace Bit.Droid.Receivers
|
||||
{
|
||||
@@ -9,17 +8,7 @@ namespace Bit.Droid.Receivers
|
||||
public override void OnReceive(Context context, Intent intent)
|
||||
{
|
||||
var clipboardManager = context.GetSystemService(Context.ClipboardService) as ClipboardManager;
|
||||
if (clipboardManager == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
// ClearPrimaryClip is supported down to API 28 with mixed results, so we're requiring 33+ instead
|
||||
if ((int)Build.VERSION.SdkInt < 33)
|
||||
{
|
||||
clipboardManager.PrimaryClip = ClipData.NewPlainText("bitwarden", " ");
|
||||
return;
|
||||
}
|
||||
clipboardManager.ClearPrimaryClip();
|
||||
clipboardManager.PrimaryClip = ClipData.NewPlainText("bitwarden", " ");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,7 +44,6 @@ namespace Bit.Droid.Renderers
|
||||
}
|
||||
if (Control != null)
|
||||
{
|
||||
Control.SetHintTextColor(ThemeHelpers.MutedColor);
|
||||
var t = ResourcesCompat.GetDrawable(Resources, Resource.Drawable.switch_thumb, null);
|
||||
if (t is GradientDrawable thumb)
|
||||
{
|
||||
|
||||
@@ -17,9 +17,6 @@
|
||||
<compatibility-package
|
||||
android:name="alook.browser.google"
|
||||
android:maxLongVersionCode="10000000000"/>
|
||||
<compatibility-package
|
||||
android:name="app.vanadium.browser"
|
||||
android:maxLongVersionCode="10000000000"/>
|
||||
<compatibility-package
|
||||
android:name="com.amazon.cloud9"
|
||||
android:maxLongVersionCode="10000000000"/>
|
||||
@@ -92,9 +89,6 @@
|
||||
<compatibility-package
|
||||
android:name="com.kiwibrowser.browser.dev"
|
||||
android:maxLongVersionCode="10000000000"/>
|
||||
<compatibility-package
|
||||
android:name="com.lemurbrowser.exts"
|
||||
android:maxLongVersionCode="10000000000"/>
|
||||
<compatibility-package
|
||||
android:name="com.microsoft.emmx"
|
||||
android:maxLongVersionCode="10000000000"/>
|
||||
@@ -119,9 +113,6 @@
|
||||
<compatibility-package
|
||||
android:name="com.naver.whale"
|
||||
android:maxLongVersionCode="10000000000"/>
|
||||
<compatibility-package
|
||||
android:name="com.neeva.app"
|
||||
android:maxLongVersionCode="10000000000"/>
|
||||
<compatibility-package
|
||||
android:name="com.opera.browser"
|
||||
android:maxLongVersionCode="10000000000"/>
|
||||
@@ -146,9 +137,6 @@
|
||||
<compatibility-package
|
||||
android:name="com.qwant.liberty"
|
||||
android:maxLongVersionCode="10000000000"/>
|
||||
<compatibility-package
|
||||
android:name="com.rainsee.create"
|
||||
android:maxLongVersionCode="10000000000"/>
|
||||
<compatibility-package
|
||||
android:name="com.sec.android.app.sbrowser"
|
||||
android:maxLongVersionCode="10000000000"/>
|
||||
@@ -173,15 +161,6 @@
|
||||
<compatibility-package
|
||||
android:name="com.yandex.browser"
|
||||
android:maxLongVersionCode="10000000000"/>
|
||||
<compatibility-package
|
||||
android:name="com.yjllq.internet"
|
||||
android:maxLongVersionCode="10000000000"/>
|
||||
<compatibility-package
|
||||
android:name="com.yjllq.kito"
|
||||
android:maxLongVersionCode="10000000000"/>
|
||||
<compatibility-package
|
||||
android:name="com.yujian.ResideMenuDemo"
|
||||
android:maxLongVersionCode="10000000000"/>
|
||||
<compatibility-package
|
||||
android:name="com.z28j.feel"
|
||||
android:maxLongVersionCode="10000000000"/>
|
||||
@@ -203,9 +182,6 @@
|
||||
<compatibility-package
|
||||
android:name="mark.via.gp"
|
||||
android:maxLongVersionCode="10000000000"/>
|
||||
<compatibility-package
|
||||
android:name="net.dezor.browser"
|
||||
android:maxLongVersionCode="10000000000"/>
|
||||
<compatibility-package
|
||||
android:name="net.slions.fulguris.full.download"
|
||||
android:maxLongVersionCode="10000000000"/>
|
||||
|
||||
@@ -6,7 +6,6 @@ using Android.Content;
|
||||
using Android.OS;
|
||||
using Android.Provider;
|
||||
using Android.Views.Autofill;
|
||||
using Bit.App.Resources;
|
||||
using Bit.Core.Abstractions;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Models.View;
|
||||
@@ -21,19 +20,16 @@ namespace Bit.Droid.Services
|
||||
private readonly IStateService _stateService;
|
||||
private readonly IMessagingService _messagingService;
|
||||
private readonly IClipboardService _clipboardService;
|
||||
private readonly IPlatformUtilsService _platformUtilsService;
|
||||
private readonly LazyResolve<IEventService> _eventService;
|
||||
|
||||
public AutofillHandler(IStateService stateService,
|
||||
IMessagingService messagingService,
|
||||
IClipboardService clipboardService,
|
||||
IPlatformUtilsService platformUtilsService,
|
||||
LazyResolve<IEventService> eventService)
|
||||
{
|
||||
_stateService = stateService;
|
||||
_messagingService = messagingService;
|
||||
_clipboardService = clipboardService;
|
||||
_platformUtilsService = platformUtilsService;
|
||||
_eventService = eventService;
|
||||
}
|
||||
|
||||
@@ -77,12 +73,12 @@ namespace Bit.Droid.Services
|
||||
|
||||
public void Autofill(CipherView cipher)
|
||||
{
|
||||
var activity = CrossCurrentActivity.Current.Activity as Xamarin.Forms.Platform.Android.FormsAppCompatActivity;
|
||||
var activity = (MainActivity)CrossCurrentActivity.Current.Activity;
|
||||
if (activity == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (activity.Intent?.GetBooleanExtra(AutofillConstants.AutofillFramework, false) ?? false)
|
||||
if (activity.Intent?.GetBooleanExtra("autofillFramework", false) ?? false)
|
||||
{
|
||||
if (cipher == null)
|
||||
{
|
||||
@@ -107,7 +103,7 @@ namespace Bit.Droid.Services
|
||||
return;
|
||||
}
|
||||
var task = CopyTotpAsync(cipher);
|
||||
var dataset = AutofillHelpers.BuildDataset(activity, parser.FieldCollection, new FilledItem(cipher), false);
|
||||
var dataset = AutofillHelpers.BuildDataset(activity, parser.FieldCollection, new FilledItem(cipher));
|
||||
var replyIntent = new Intent();
|
||||
replyIntent.PutExtra(AutofillManager.ExtraAuthenticationResult, dataset);
|
||||
activity.SetResult(Result.Ok, replyIntent);
|
||||
@@ -206,7 +202,6 @@ namespace Bit.Droid.Services
|
||||
if (totp != null)
|
||||
{
|
||||
await _clipboardService.CopyTextAsync(totp);
|
||||
_platformUtilsService.ShowToastForCopiedValue(AppResources.VerificationCodeTotp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ using Android.OS;
|
||||
using Bit.Core.Abstractions;
|
||||
using Bit.Droid.Receivers;
|
||||
using Bit.Droid.Utilities;
|
||||
using Plugin.CurrentActivity;
|
||||
using Xamarin.Essentials;
|
||||
|
||||
namespace Bit.Droid.Services
|
||||
@@ -20,9 +21,9 @@ namespace Bit.Droid.Services
|
||||
_stateService = stateService;
|
||||
|
||||
_clearClipboardPendingIntent = new Lazy<PendingIntent>(() =>
|
||||
PendingIntent.GetBroadcast(Application.Context,
|
||||
PendingIntent.GetBroadcast(CrossCurrentActivity.Current.Activity,
|
||||
0,
|
||||
new Intent(Application.Context, typeof(ClearClipboardAlarmReceiver)),
|
||||
new Intent(CrossCurrentActivity.Current.Activity, typeof(ClearClipboardAlarmReceiver)),
|
||||
AndroidHelpers.AddPendingIntentMutabilityFlag(PendingIntentFlags.UpdateCurrent, false)));
|
||||
}
|
||||
|
||||
@@ -44,7 +45,7 @@ namespace Bit.Droid.Services
|
||||
}
|
||||
catch (Java.Lang.SecurityException ex) when (ex.Message.Contains("does not belong to"))
|
||||
{
|
||||
// #1962 Just ignore, the content is copied either way but there is some app interfering in the process
|
||||
// #1962 Just ignore, the content is copied either way but there is some app interfiering in the process
|
||||
// that the OS catches and just throws this exception.
|
||||
}
|
||||
}
|
||||
@@ -57,7 +58,9 @@ namespace Bit.Droid.Services
|
||||
|
||||
private void CopyToClipboard(string text, bool isSensitive = true)
|
||||
{
|
||||
var clipboardManager = Application.Context.GetSystemService(Context.ClipboardService) as ClipboardManager;
|
||||
var activity = (MainActivity)CrossCurrentActivity.Current.Activity;
|
||||
var clipboardManager = activity.GetSystemService(
|
||||
Context.ClipboardService) as Android.Content.ClipboardManager;
|
||||
var clipData = ClipData.NewPlainText("bitwarden", text);
|
||||
if (isSensitive)
|
||||
{
|
||||
@@ -84,7 +87,7 @@ namespace Bit.Droid.Services
|
||||
return;
|
||||
}
|
||||
var triggerMs = Java.Lang.JavaSystem.CurrentTimeMillis() + clearMs;
|
||||
var alarmManager = Application.Context.GetSystemService(Context.AlarmService) as AlarmManager;
|
||||
var alarmManager = CrossCurrentActivity.Current.Activity.GetSystemService(Context.AlarmService) as AlarmManager;
|
||||
alarmManager.Set(AlarmType.Rtc, triggerMs, _clearClipboardPendingIntent.Value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,8 +5,6 @@ using Org.BouncyCastle.Crypto.Digests;
|
||||
using Org.BouncyCastle.Crypto.Generators;
|
||||
using Org.BouncyCastle.Crypto.Parameters;
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using Java.Lang;
|
||||
|
||||
namespace Bit.Droid.Services
|
||||
{
|
||||
@@ -35,19 +33,5 @@ namespace Bit.Droid.Services
|
||||
generator.Init(password, salt, iterations);
|
||||
return ((KeyParameter)generator.GenerateDerivedMacParameters(keySize)).GetKey();
|
||||
}
|
||||
|
||||
public byte[] Argon2id(byte[] password, byte[] salt, int iterations, int memory, int parallelism)
|
||||
{
|
||||
JavaSystem.LoadLibrary("argon2");
|
||||
int keySize = 32;
|
||||
var key = new byte[keySize];
|
||||
argon2id_hash_raw(iterations, memory, parallelism,
|
||||
password, password.Length, salt, salt.Length, key, key.Length);
|
||||
return key;
|
||||
}
|
||||
|
||||
[DllImport("argon2", EntryPoint = "argon2id_hash_raw")]
|
||||
private static extern int argon2id_hash_raw(int timeCost, int memoryCost, int parallelism,
|
||||
byte[] pwd, int pwdlen, byte[] salt, int saltlen, byte[] hash, int hashlen);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,6 @@ using Bit.Core.Enums;
|
||||
using Bit.Core.Utilities;
|
||||
using Bit.Droid.Utilities;
|
||||
using Plugin.CurrentActivity;
|
||||
using static Bit.App.Pages.SettingsPageViewModel;
|
||||
|
||||
namespace Bit.Droid.Services
|
||||
{
|
||||
@@ -70,17 +69,14 @@ namespace Bit.Droid.Services
|
||||
|
||||
public bool LaunchApp(string appName)
|
||||
{
|
||||
if ((int)Build.VERSION.SdkInt < 33)
|
||||
{
|
||||
// API 33 required to avoid using wildcard app visibility or dangerous permissions
|
||||
// https://developer.android.com/reference/android/content/pm/PackageManager#getLaunchIntentSenderForPackage(java.lang.String)
|
||||
return false;
|
||||
}
|
||||
var activity = CrossCurrentActivity.Current.Activity;
|
||||
appName = appName.Replace("androidapp://", string.Empty);
|
||||
var launchIntentSender = activity?.PackageManager?.GetLaunchIntentSenderForPackage(appName);
|
||||
launchIntentSender?.SendIntent(activity, Result.Ok, null, null, null);
|
||||
return launchIntentSender != null;
|
||||
var launchIntent = activity.PackageManager.GetLaunchIntentForPackage(appName);
|
||||
if (launchIntent != null)
|
||||
{
|
||||
activity.StartActivity(launchIntent);
|
||||
}
|
||||
return launchIntent != null;
|
||||
}
|
||||
|
||||
public async Task ShowLoadingAsync(string text)
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using Bit.App.Abstractions;
|
||||
using Bit.App.Models;
|
||||
@@ -26,13 +25,13 @@ namespace Bit.Droid.Services
|
||||
try
|
||||
{
|
||||
var fallback = ToDotnetFallbackLanguage(new PlatformCulture(netLanguage));
|
||||
Debug.WriteLine(netLanguage + " failed, trying " + fallback + " (" + e1.Message + ")");
|
||||
Console.WriteLine(netLanguage + " failed, trying " + fallback + " (" + e1.Message + ")");
|
||||
ci = new CultureInfo(fallback);
|
||||
}
|
||||
catch (CultureNotFoundException e2)
|
||||
{
|
||||
// iOS language not valid .NET culture, falling back to English
|
||||
Debug.WriteLine(netLanguage + " couldn't be set, using 'en' (" + e2.Message + ")");
|
||||
Console.WriteLine(netLanguage + " couldn't be set, using 'en' (" + e2.Message + ")");
|
||||
ci = new CultureInfo("en");
|
||||
}
|
||||
}
|
||||
@@ -41,7 +40,7 @@ namespace Bit.Droid.Services
|
||||
|
||||
private string AndroidToDotnetLanguage(string androidLanguage)
|
||||
{
|
||||
Debug.WriteLine("Android Language:" + androidLanguage);
|
||||
Console.WriteLine("Android Language:" + androidLanguage);
|
||||
var netLanguage = androidLanguage;
|
||||
if (androidLanguage.StartsWith("zh"))
|
||||
{
|
||||
@@ -80,13 +79,13 @@ namespace Bit.Droid.Services
|
||||
// ONLY use cultures that have been tested and known to work
|
||||
}
|
||||
}
|
||||
Debug.WriteLine(".NET Language/Locale:" + netLanguage);
|
||||
Console.WriteLine(".NET Language/Locale:" + netLanguage);
|
||||
return netLanguage;
|
||||
}
|
||||
|
||||
private string ToDotnetFallbackLanguage(PlatformCulture platCulture)
|
||||
{
|
||||
Debug.WriteLine(".NET Fallback Language:" + platCulture.LanguageCode);
|
||||
Console.WriteLine(".NET Fallback Language:" + platCulture.LanguageCode);
|
||||
var netLanguage = platCulture.LanguageCode; // use the first part of the identifier (two chars, usually);
|
||||
switch (platCulture.LanguageCode)
|
||||
{
|
||||
@@ -96,7 +95,7 @@ namespace Bit.Droid.Services
|
||||
// add more application-specific cases here (if required)
|
||||
// ONLY use cultures that have been tested and known to work
|
||||
}
|
||||
Debug.WriteLine(".NET Fallback Language/Locale:" + netLanguage + " (application-specific)");
|
||||
Console.WriteLine(".NET Fallback Language/Locale:" + netLanguage + " (application-specific)");
|
||||
return netLanguage;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Bit.App.Services;
|
||||
using Bit.Core.Abstractions;
|
||||
using Bit.Core.Models;
|
||||
|
||||
namespace Bit.Droid.Services
|
||||
{
|
||||
public class WatchDeviceService : BaseWatchDeviceService
|
||||
{
|
||||
public WatchDeviceService(ICipherService cipherService,
|
||||
IEnvironmentService environmentService,
|
||||
IStateService stateService,
|
||||
IVaultTimeoutService vaultTimeoutService)
|
||||
: base(cipherService, environmentService, stateService, vaultTimeoutService)
|
||||
{
|
||||
}
|
||||
|
||||
protected override bool IsSupported => false;
|
||||
|
||||
public override bool IsConnected => false;
|
||||
|
||||
protected override bool CanSendData => false;
|
||||
|
||||
protected override Task SendDataToWatchAsync(WatchDTO watchDto) => throw new NotImplementedException();
|
||||
|
||||
protected override void ConnectToWatch() => throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
@@ -62,7 +62,7 @@ namespace Bit.Droid.Utilities
|
||||
theme = ThemeManager.Dark;
|
||||
}
|
||||
|
||||
if (theme == ThemeManager.Dark || theme == ThemeManager.Black || theme == ThemeManager.Nord || theme == ThemeManager.SolarizedDark)
|
||||
if (theme == ThemeManager.Dark || theme == ThemeManager.Black || theme == ThemeManager.Nord)
|
||||
{
|
||||
LightTheme = false;
|
||||
}
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,6 +1,5 @@
|
||||
using System.Threading.Tasks;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Models;
|
||||
|
||||
namespace Bit.App.Abstractions
|
||||
{
|
||||
|
||||
@@ -125,9 +125,6 @@
|
||||
<Compile Update="Pages\Accounts\LoginPasswordlessPage.xaml.cs">
|
||||
<DependentUpon>LoginPasswordlessPage.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Update="Pages\Accounts\LoginPasswordlessRequestPage.xaml.cs">
|
||||
<DependentUpon>LoginPasswordlessRequestPage.xaml</DependentUpon>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
@@ -143,7 +140,6 @@
|
||||
<Folder Include="Utilities\AccountManagement\" />
|
||||
<Folder Include="Controls\DateTime\" />
|
||||
<Folder Include="Controls\IconLabelButton\" />
|
||||
<Folder Include="Controls\PasswordStrengthProgressBar\" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
@@ -436,6 +432,5 @@
|
||||
<None Remove="Utilities\AccountManagement\" />
|
||||
<None Remove="Controls\DateTime\" />
|
||||
<None Remove="Controls\IconLabelButton\" />
|
||||
<None Remove="Controls\PasswordStrengthProgressBar\" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
||||
@@ -204,7 +204,7 @@ namespace Bit.App
|
||||
FingerprintPhrase = loginRequestData.RequestFingerprint,
|
||||
RequestDate = loginRequestData.CreationDate,
|
||||
DeviceType = loginRequestData.RequestDeviceType,
|
||||
Origin = loginRequestData.Origin
|
||||
Origin = loginRequestData.Origin,
|
||||
});
|
||||
await _stateService.SetPasswordlessLoginNotificationAsync(null);
|
||||
_pushNotificationService.DismissLocalNotification(Constants.PasswordlessNotificationId);
|
||||
@@ -459,7 +459,14 @@ namespace Bit.App
|
||||
switch (navTarget)
|
||||
{
|
||||
case NavigationTarget.HomeLogin:
|
||||
Current.MainPage = new NavigationPage(new HomePage(Options));
|
||||
if (navParams is HomeNavigationParams homeParams)
|
||||
{
|
||||
Current.MainPage = new NavigationPage(new HomePage(Options, homeParams.ShouldCheckRememberEmail));
|
||||
}
|
||||
else
|
||||
{
|
||||
Current.MainPage = new NavigationPage(new HomePage(Options));
|
||||
}
|
||||
break;
|
||||
case NavigationTarget.Login:
|
||||
if (navParams is LoginNavigationParams loginParams)
|
||||
|
||||
@@ -14,7 +14,7 @@ namespace Bit.App.Controls
|
||||
{
|
||||
AccountView = accountView;
|
||||
AvatarImageSource = ServiceContainer.Resolve<IAvatarImageSourcePool>("avatarImageSourcePool")
|
||||
?.GetOrCreateAvatar(AccountView.UserId, AccountView.Name, AccountView.Email, AccountView.AvatarColor);
|
||||
?.GetOrCreateAvatar(AccountView.UserId, AccountView.Name, AccountView.Email);
|
||||
}
|
||||
|
||||
public AccountView AccountView
|
||||
|
||||
@@ -13,7 +13,6 @@ namespace Bit.App.Controls
|
||||
{
|
||||
private readonly string _text;
|
||||
private readonly string _id;
|
||||
private readonly string _color;
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
@@ -24,7 +23,7 @@ namespace Bit.App.Controls
|
||||
|
||||
if (obj is AvatarImageSource avatar)
|
||||
{
|
||||
return avatar._id == _id && avatar._text == _text && avatar._color == _color;
|
||||
return avatar._id == _id && avatar._text == _text;
|
||||
}
|
||||
|
||||
return base.Equals(obj);
|
||||
@@ -32,7 +31,7 @@ namespace Bit.App.Controls
|
||||
|
||||
public override int GetHashCode() => _id?.GetHashCode() ?? _text?.GetHashCode() ?? -1;
|
||||
|
||||
public AvatarImageSource(string userId = null, string name = null, string email = null, string color = null)
|
||||
public AvatarImageSource(string userId = null, string name = null, string email = null)
|
||||
{
|
||||
_id = userId;
|
||||
_text = name;
|
||||
@@ -40,7 +39,6 @@ namespace Bit.App.Controls
|
||||
{
|
||||
_text = email;
|
||||
}
|
||||
_color = color;
|
||||
}
|
||||
|
||||
public override Func<CancellationToken, Task<Stream>> Stream => GetStreamAsync;
|
||||
@@ -73,7 +71,7 @@ namespace Bit.App.Controls
|
||||
chars = upperCaseText = _text.ToUpper();
|
||||
}
|
||||
|
||||
var bgColor = _color ?? CoreHelpers.StringToColor(_id ?? upperCaseText, "#33ffffff");
|
||||
var bgColor = CoreHelpers.StringToColor(_id ?? upperCaseText, "#33ffffff");
|
||||
var textColor = CoreHelpers.TextColorFromBgColor(bgColor);
|
||||
var size = 50;
|
||||
|
||||
|
||||
@@ -5,19 +5,19 @@ namespace Bit.App.Controls
|
||||
{
|
||||
public interface IAvatarImageSourcePool
|
||||
{
|
||||
AvatarImageSource GetOrCreateAvatar(string userId, string name, string email, string color);
|
||||
AvatarImageSource GetOrCreateAvatar(string userId, string name, string email);
|
||||
}
|
||||
|
||||
public class AvatarImageSourcePool : IAvatarImageSourcePool
|
||||
{
|
||||
private readonly ConcurrentDictionary<string, AvatarImageSource> _cache = new ConcurrentDictionary<string, AvatarImageSource>();
|
||||
|
||||
public AvatarImageSource GetOrCreateAvatar(string userId, string name, string email, string color)
|
||||
public AvatarImageSource GetOrCreateAvatar(string userId, string name, string email)
|
||||
{
|
||||
var key = $"{userId}{name}{email}{color}";
|
||||
var key = $"{userId}{name}{email}";
|
||||
if (!_cache.TryGetValue(key, out var avatar))
|
||||
{
|
||||
avatar = new AvatarImageSource(userId, name, email, color);
|
||||
avatar = new AvatarImageSource(userId, name, email);
|
||||
if (!_cache.TryAdd(key, avatar)
|
||||
&&
|
||||
!_cache.TryGetValue(key, out avatar)) // If add fails another thread created the avatar in between the first try get and the try add.
|
||||
|
||||
@@ -21,13 +21,13 @@ namespace Bit.App.Controls
|
||||
nameof(StrokeWidth), typeof(float), typeof(CircularProgressbarView), 3f);
|
||||
|
||||
public static readonly BindableProperty ProgressColorProperty = BindableProperty.Create(
|
||||
nameof(ProgressColor), typeof(Color), typeof(CircularProgressbarView), Color.FromHex("175DDC"));
|
||||
nameof(ProgressColor), typeof(Color), typeof(CircularProgressbarView), Color.Default);
|
||||
|
||||
public static readonly BindableProperty EndingProgressColorProperty = BindableProperty.Create(
|
||||
nameof(EndingProgressColor), typeof(Color), typeof(CircularProgressbarView), Color.FromHex("dd4b39"));
|
||||
nameof(EndingProgressColor), typeof(Color), typeof(CircularProgressbarView), Color.Default);
|
||||
|
||||
public static readonly BindableProperty BackgroundProgressColorProperty = BindableProperty.Create(
|
||||
nameof(BackgroundProgressColor), typeof(Color), typeof(CircularProgressbarView), Color.White);
|
||||
nameof(BackgroundProgressColor), typeof(Color), typeof(CircularProgressbarView), Color.Default);
|
||||
|
||||
public double Progress
|
||||
{
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
using System.Linq;
|
||||
using Xamarin.CommunityToolkit.Converters;
|
||||
using Xamarin.Forms;
|
||||
using Xamarin.Forms;
|
||||
|
||||
namespace Bit.App.Controls
|
||||
{
|
||||
@@ -8,13 +6,4 @@ namespace Bit.App.Controls
|
||||
{
|
||||
public string ExtraDataForLogging { get; set; }
|
||||
}
|
||||
|
||||
public class SelectionChangedEventArgsConverter : BaseNullableConverterOneWay<SelectionChangedEventArgs, object>
|
||||
{
|
||||
public override object? ConvertFrom(SelectionChangedEventArgs? value)
|
||||
{
|
||||
return value?.CurrentSelection.FirstOrDefault();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ namespace Bit.App.Controls
|
||||
public class ExtendedSlider : Slider
|
||||
{
|
||||
public static readonly BindableProperty ThumbBorderColorProperty = BindableProperty.Create(
|
||||
nameof(ThumbBorderColor), typeof(Color), typeof(ExtendedSlider), Color.FromHex("b5b5b5"));
|
||||
nameof(ThumbBorderColor), typeof(Color), typeof(ExtendedSlider), Color.Default);
|
||||
|
||||
public Color ThumbBorderColor
|
||||
{
|
||||
|
||||
@@ -5,10 +5,10 @@ namespace Bit.App.Controls
|
||||
public class ExtendedStepper : Stepper
|
||||
{
|
||||
public static readonly BindableProperty StepperBackgroundColorProperty = BindableProperty.Create(
|
||||
nameof(StepperBackgroundColor), typeof(Color), typeof(ExtendedStepper), Color.White);
|
||||
nameof(StepperBackgroundColor), typeof(Color), typeof(ExtendedStepper), Color.Default);
|
||||
|
||||
public static readonly BindableProperty StepperForegroundColorProperty = BindableProperty.Create(
|
||||
nameof(StepperForegroundColor), typeof(Color), typeof(ExtendedStepper), Color.Black);
|
||||
nameof(StepperForegroundColor), typeof(Color), typeof(ExtendedStepper), Color.Default);
|
||||
|
||||
public Color StepperBackgroundColor
|
||||
{
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
Padding="1"
|
||||
StyleClass="btn-icon-secondary"
|
||||
BackgroundColor="{Binding IconLabelBorderColor, Source={x:Reference _iconLabelButton}}"
|
||||
BorderColor="Transparent"
|
||||
HasShadow="False">
|
||||
<Frame.GestureRecognizers>
|
||||
<TapGestureRecognizer Command="{Binding ButtonCommand, Source={x:Reference _iconLabelButton}}" />
|
||||
@@ -18,7 +17,6 @@
|
||||
Padding="0"
|
||||
CornerRadius="{Binding CornerRadius, Source={x:Reference _iconLabelButton}}"
|
||||
BackgroundColor="{Binding IconLabelBackgroundColor, Source={x:Reference _iconLabelButton}}"
|
||||
BorderColor="Transparent"
|
||||
IsClippedToBounds="True"
|
||||
HasShadow="False">
|
||||
<StackLayout
|
||||
|
||||
@@ -19,7 +19,7 @@ namespace Bit.App.Controls
|
||||
nameof(Label), typeof(string), typeof(IconLabelButton));
|
||||
|
||||
public static readonly BindableProperty ButtonCommandProperty = BindableProperty.Create(
|
||||
nameof(ButtonCommand), typeof(ICommand), typeof(IconLabelButton));
|
||||
nameof(ButtonCommand), typeof(Command), typeof(IconLabelButton));
|
||||
|
||||
public static readonly BindableProperty IconLabelColorProperty = BindableProperty.Create(
|
||||
nameof(IconLabelColor), typeof(Color), typeof(IconLabelButton), Color.White);
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Bit.App.Controls
|
||||
{
|
||||
public interface IPasswordStrengthable
|
||||
{
|
||||
string Password { get; }
|
||||
List<string> UserInputs { get; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
using Bit.Core.Attributes;
|
||||
|
||||
namespace Bit.App.Controls
|
||||
{
|
||||
public enum PasswordStrengthLevel
|
||||
{
|
||||
[LocalizableEnum("Weak")]
|
||||
VeryWeak,
|
||||
[LocalizableEnum("Weak")]
|
||||
Weak,
|
||||
[LocalizableEnum("Good")]
|
||||
Good,
|
||||
[LocalizableEnum("Strong")]
|
||||
Strong
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<StackLayout
|
||||
xmlns="http://xamarin.com/schemas/2014/forms"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
|
||||
xmlns:controls="clr-namespace:Bit.App.Controls"
|
||||
xmlns:u="clr-namespace:Bit.App.Utilities"
|
||||
x:DataType="controls:PasswordStrengthViewModel"
|
||||
x:Class="Bit.App.Controls.PasswordStrengthProgressBar"
|
||||
StyleClass="box">
|
||||
|
||||
<StackLayout.Resources>
|
||||
<ResourceDictionary>
|
||||
<u:LocalizableEnumConverter x:Key="localizableEnum" />
|
||||
</ResourceDictionary>
|
||||
</StackLayout.Resources>
|
||||
|
||||
<ProgressBar
|
||||
x:Name="_progressBar"
|
||||
u:ProgressBarExtensions.AnimatedProgress="{Binding PasswordStrength}"
|
||||
ScaleY="2" />
|
||||
|
||||
<Label
|
||||
x:Name="_progressLabel"
|
||||
Text="{Binding PasswordStrengthLevel, Converter={StaticResource localizableEnum}, TargetNullValue=' ' }"
|
||||
StyleClass="box-footer-label" />
|
||||
|
||||
</StackLayout>
|
||||
|
||||
@@ -1,108 +0,0 @@
|
||||
using System;
|
||||
using Xamarin.Forms;
|
||||
|
||||
namespace Bit.App.Controls
|
||||
{
|
||||
public partial class PasswordStrengthProgressBar : StackLayout
|
||||
{
|
||||
public static readonly BindableProperty PasswordStrengthLevelProperty = BindableProperty.Create(
|
||||
nameof(PasswordStrengthLevel),
|
||||
typeof(PasswordStrengthLevel),
|
||||
typeof(PasswordStrengthProgressBar),
|
||||
propertyChanged: OnControlPropertyChanged);
|
||||
|
||||
public static readonly BindableProperty VeryWeakColorProperty = BindableProperty.Create(
|
||||
nameof(VeryWeakColor),
|
||||
typeof(Color),
|
||||
typeof(PasswordStrengthProgressBar),
|
||||
propertyChanged: OnControlPropertyChanged);
|
||||
|
||||
public static readonly BindableProperty WeakColorProperty = BindableProperty.Create(
|
||||
nameof(WeakColor),
|
||||
typeof(Color),
|
||||
typeof(PasswordStrengthProgressBar),
|
||||
propertyChanged: OnControlPropertyChanged);
|
||||
|
||||
public static readonly BindableProperty GoodColorProperty = BindableProperty.Create(
|
||||
nameof(GoodColor),
|
||||
typeof(Color),
|
||||
typeof(PasswordStrengthProgressBar),
|
||||
propertyChanged: OnControlPropertyChanged);
|
||||
|
||||
public static readonly BindableProperty StrongColorProperty = BindableProperty.Create(
|
||||
nameof(StrongColor),
|
||||
typeof(Color),
|
||||
typeof(PasswordStrengthProgressBar),
|
||||
propertyChanged: OnControlPropertyChanged);
|
||||
|
||||
public PasswordStrengthLevel? PasswordStrengthLevel
|
||||
{
|
||||
get { return (PasswordStrengthLevel?)GetValue(PasswordStrengthLevelProperty); }
|
||||
set { SetValue(PasswordStrengthLevelProperty, value); }
|
||||
}
|
||||
|
||||
public Color VeryWeakColor
|
||||
{
|
||||
get { return (Color)GetValue(VeryWeakColorProperty); }
|
||||
set { SetValue(VeryWeakColorProperty, value); }
|
||||
}
|
||||
|
||||
public Color WeakColor
|
||||
{
|
||||
get { return (Color)GetValue(WeakColorProperty); }
|
||||
set { SetValue(WeakColorProperty, value); }
|
||||
}
|
||||
|
||||
public Color GoodColor
|
||||
{
|
||||
get { return (Color)GetValue(GoodColorProperty); }
|
||||
set { SetValue(GoodColorProperty, value); }
|
||||
}
|
||||
|
||||
public Color StrongColor
|
||||
{
|
||||
get { return (Color)GetValue(StrongColorProperty); }
|
||||
set { SetValue(StrongColorProperty, value); }
|
||||
}
|
||||
|
||||
public PasswordStrengthProgressBar()
|
||||
{
|
||||
InitializeComponent();
|
||||
SetBinding(PasswordStrengthProgressBar.PasswordStrengthLevelProperty, new Binding() { Path = nameof(PasswordStrengthViewModel.PasswordStrengthLevel) });
|
||||
UpdateColors();
|
||||
}
|
||||
|
||||
private static void OnControlPropertyChanged(BindableObject bindable, object oldValue, object newValue)
|
||||
{
|
||||
(bindable as PasswordStrengthProgressBar)?.UpdateColors();
|
||||
}
|
||||
|
||||
public void UpdateColors()
|
||||
{
|
||||
if (_progressBar == null || _progressLabel == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
_progressBar.ProgressColor = GetColorForStrength();
|
||||
_progressLabel.TextColor = _progressBar.ProgressColor;
|
||||
}
|
||||
|
||||
private Color GetColorForStrength()
|
||||
{
|
||||
switch (PasswordStrengthLevel)
|
||||
{
|
||||
case Controls.PasswordStrengthLevel.VeryWeak:
|
||||
return VeryWeakColor;
|
||||
case Controls.PasswordStrengthLevel.Weak:
|
||||
return WeakColor;
|
||||
case Controls.PasswordStrengthLevel.Good:
|
||||
return GoodColor;
|
||||
case Controls.PasswordStrengthLevel.Strong:
|
||||
return StrongColor;
|
||||
default:
|
||||
return Color.Transparent;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,67 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using Bit.Core.Abstractions;
|
||||
using Bit.Core.Utilities;
|
||||
using Xamarin.Forms;
|
||||
|
||||
namespace Bit.App.Controls
|
||||
{
|
||||
public class PasswordStrengthViewModel : ExtendedViewModel
|
||||
{
|
||||
private readonly IPasswordGenerationService _passwordGenerationService;
|
||||
private readonly IPasswordStrengthable _passwordStrengthable;
|
||||
private double _passwordStrength;
|
||||
private Color _passwordColor;
|
||||
private PasswordStrengthLevel? _passwordStrengthLevel;
|
||||
|
||||
public PasswordStrengthViewModel(IPasswordStrengthable passwordStrengthable)
|
||||
{
|
||||
_passwordGenerationService = ServiceContainer.Resolve<IPasswordGenerationService>();
|
||||
_passwordStrengthable = passwordStrengthable;
|
||||
}
|
||||
|
||||
public double PasswordStrength
|
||||
{
|
||||
get => _passwordStrength;
|
||||
set => SetProperty(ref _passwordStrength, value);
|
||||
}
|
||||
|
||||
public PasswordStrengthLevel? PasswordStrengthLevel
|
||||
{
|
||||
get => _passwordStrengthLevel;
|
||||
set => SetProperty(ref _passwordStrengthLevel, value);
|
||||
}
|
||||
|
||||
public List<string> GetPasswordStrengthUserInput(string email) => _passwordGenerationService.GetPasswordStrengthUserInput(email);
|
||||
|
||||
public void CalculatePasswordStrength()
|
||||
{
|
||||
if (string.IsNullOrEmpty(_passwordStrengthable.Password))
|
||||
{
|
||||
PasswordStrength = 0;
|
||||
PasswordStrengthLevel = null;
|
||||
return;
|
||||
}
|
||||
|
||||
var passwordStrength = _passwordGenerationService.PasswordStrength(_passwordStrengthable.Password, _passwordStrengthable.UserInputs);
|
||||
// The passwordStrength.Score is 0..4, convertion was made to be used as a progress directly by the control 0..1
|
||||
PasswordStrength = (passwordStrength.Score + 1f) / 5f;
|
||||
if (PasswordStrength <= 0.4f)
|
||||
{
|
||||
PasswordStrengthLevel = Controls.PasswordStrengthLevel.VeryWeak;
|
||||
}
|
||||
else if (PasswordStrength <= 0.6f)
|
||||
{
|
||||
PasswordStrengthLevel = Controls.PasswordStrengthLevel.Weak;
|
||||
}
|
||||
else if (PasswordStrength <= 0.8f)
|
||||
{
|
||||
PasswordStrengthLevel = Controls.PasswordStrengthLevel.Good;
|
||||
}
|
||||
else
|
||||
{
|
||||
PasswordStrengthLevel = Controls.PasswordStrengthLevel.Strong;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,8 +4,6 @@ using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using Bit.App.Abstractions;
|
||||
using Bit.App.Resources;
|
||||
using Bit.App.Utilities;
|
||||
using Bit.Core;
|
||||
using Bit.Core.Abstractions;
|
||||
using Bit.Core.Models.Domain;
|
||||
using Bit.Core.Utilities;
|
||||
@@ -149,8 +147,8 @@ namespace Bit.App.Pages
|
||||
}
|
||||
if (IsPolicyInEffect)
|
||||
{
|
||||
var userInputs = _passwordGenerationService.GetPasswordStrengthUserInput(await _stateService.GetEmailAsync());
|
||||
var passwordStrength = _passwordGenerationService.PasswordStrength(MasterPassword, userInputs);
|
||||
var userInput = await GetPasswordStrengthUserInput();
|
||||
var passwordStrength = _passwordGenerationService.PasswordStrength(MasterPassword, userInput);
|
||||
if (!await _policyService.EvaluateMasterPassword(passwordStrength.Score, MasterPassword, Policy))
|
||||
{
|
||||
await _platformUtilsService.ShowDialogAsync(AppResources.MasterPasswordPolicyValidationMessage,
|
||||
@@ -160,7 +158,7 @@ namespace Bit.App.Pages
|
||||
}
|
||||
else
|
||||
{
|
||||
if (MasterPassword.Length < Constants.MasterPasswordMinimumChars)
|
||||
if (MasterPassword.Length < 8)
|
||||
{
|
||||
await _platformUtilsService.ShowDialogAsync(AppResources.MasterPasswordLengthValMessage,
|
||||
AppResources.MasterPasswordPolicyValidationTitle, AppResources.Ok);
|
||||
@@ -176,5 +174,19 @@ namespace Bit.App.Pages
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private async Task<List<string>> GetPasswordStrengthUserInput()
|
||||
{
|
||||
var email = await _stateService.GetEmailAsync();
|
||||
List<string> userInput = null;
|
||||
var atPosition = email.IndexOf('@');
|
||||
if (atPosition > -1)
|
||||
{
|
||||
var rx = new Regex("/[^A-Za-z0-9]/", RegexOptions.Compiled);
|
||||
var data = rx.Split(email.Substring(0, atPosition).Trim().ToLower());
|
||||
userInput = new List<string>(data);
|
||||
}
|
||||
return userInput;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,9 +51,7 @@
|
||||
x:Name="_email"
|
||||
Text="{Binding Email}"
|
||||
Keyboard="Email"
|
||||
StyleClass="box-value"
|
||||
ReturnType="Go"
|
||||
ReturnCommand="{Binding ContinueCommand}">
|
||||
StyleClass="box-value">
|
||||
<VisualStateManager.VisualStateGroups>
|
||||
<VisualStateGroup x:Name="CommonStates">
|
||||
<VisualState x:Name="Disabled">
|
||||
|
||||
@@ -15,13 +15,14 @@ namespace Bit.App.Pages
|
||||
private readonly AppOptions _appOptions;
|
||||
private IBroadcasterService _broadcasterService;
|
||||
|
||||
public HomePage(AppOptions appOptions = null)
|
||||
public HomePage(AppOptions appOptions = null, bool shouldCheckRememberEmail = true)
|
||||
{
|
||||
_broadcasterService = ServiceContainer.Resolve<IBroadcasterService>("broadcasterService");
|
||||
_appOptions = appOptions;
|
||||
InitializeComponent();
|
||||
_vm = BindingContext as HomeViewModel;
|
||||
_vm.Page = this;
|
||||
_vm.ShouldCheckRememberEmail = shouldCheckRememberEmail;
|
||||
_vm.ShowCancelButton = _appOptions?.IosExtension ?? false;
|
||||
_vm.StartLoginAction = async () => await StartLoginAsync();
|
||||
_vm.StartRegisterAction = () => Device.BeginInvokeOnMainThread(async () => await StartRegisterAsync());
|
||||
@@ -70,6 +71,8 @@ namespace Bit.App.Pages
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
_vm.CheckNavigateLoginStep();
|
||||
}
|
||||
|
||||
protected override bool OnBackButtonPressed()
|
||||
|
||||
@@ -73,6 +73,8 @@ namespace Bit.App.Pages
|
||||
|
||||
public bool CanContinue => !string.IsNullOrEmpty(Email);
|
||||
|
||||
public bool ShouldCheckRememberEmail { get; set; }
|
||||
|
||||
public FormattedString CreateAccountText
|
||||
{
|
||||
get
|
||||
@@ -108,6 +110,15 @@ namespace Bit.App.Pages
|
||||
RememberEmail = !string.IsNullOrEmpty(Email);
|
||||
}
|
||||
|
||||
public void CheckNavigateLoginStep()
|
||||
{
|
||||
if (ShouldCheckRememberEmail && RememberEmail)
|
||||
{
|
||||
StartLoginAction();
|
||||
}
|
||||
ShouldCheckRememberEmail = false;
|
||||
}
|
||||
|
||||
public async Task ContinueToLoginStepAsync()
|
||||
{
|
||||
try
|
||||
@@ -125,16 +136,16 @@ namespace Bit.App.Pages
|
||||
AppResources.Ok);
|
||||
return;
|
||||
}
|
||||
|
||||
await _stateService.SetRememberedEmailAsync(RememberEmail ? Email : null);
|
||||
var userId = await _stateService.GetUserIdAsync(Email);
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(userId) &&
|
||||
(await _stateService.GetEnvironmentUrlsAsync(userId))?.Base == _environmentService.BaseUrl &&
|
||||
await _stateService.IsAuthenticatedAsync(userId))
|
||||
if (!string.IsNullOrWhiteSpace(userId))
|
||||
{
|
||||
await _accountManager.PromptToSwitchToExistingAccountAsync(userId);
|
||||
return;
|
||||
var userEnvUrls = await _stateService.GetEnvironmentUrlsAsync(userId);
|
||||
if (userEnvUrls?.Base == _environmentService.BaseUrl)
|
||||
{
|
||||
await _accountManager.PromptToSwitchToExistingAccountAsync(userId);
|
||||
return;
|
||||
}
|
||||
}
|
||||
StartLoginAction();
|
||||
}
|
||||
|
||||
@@ -28,7 +28,6 @@ namespace Bit.App.Pages
|
||||
private readonly IBiometricService _biometricService;
|
||||
private readonly IKeyConnectorService _keyConnectorService;
|
||||
private readonly ILogger _logger;
|
||||
private readonly IWatchDeviceService _watchDeviceService;
|
||||
private readonly WeakEventManager<int?> _secretEntryFocusWeakEventManager = new WeakEventManager<int?>();
|
||||
|
||||
private string _email;
|
||||
@@ -57,7 +56,6 @@ namespace Bit.App.Pages
|
||||
_biometricService = ServiceContainer.Resolve<IBiometricService>("biometricService");
|
||||
_keyConnectorService = ServiceContainer.Resolve<IKeyConnectorService>("keyConnectorService");
|
||||
_logger = ServiceContainer.Resolve<ILogger>("logger");
|
||||
_watchDeviceService = ServiceContainer.Resolve<IWatchDeviceService>();
|
||||
|
||||
PageTitle = AppResources.VerifyMasterPassword;
|
||||
TogglePasswordCommand = new Command(TogglePassword);
|
||||
@@ -228,7 +226,8 @@ namespace Bit.App.Pages
|
||||
}
|
||||
|
||||
ShowPassword = false;
|
||||
var kdfConfig = await _stateService.GetActiveUserCustomDataAsync(a => new KdfConfig(a?.Profile));
|
||||
var kdf = await _stateService.GetKdfTypeAsync();
|
||||
var kdfIterations = await _stateService.GetKdfIterationsAsync();
|
||||
|
||||
if (PinLock)
|
||||
{
|
||||
@@ -238,7 +237,7 @@ namespace Bit.App.Pages
|
||||
if (_isPinProtected)
|
||||
{
|
||||
var key = await _cryptoService.MakeKeyFromPinAsync(Pin, _email,
|
||||
kdfConfig,
|
||||
kdf.GetValueOrDefault(KdfType.PBKDF2_SHA256), kdfIterations.GetValueOrDefault(5000),
|
||||
await _stateService.GetPinProtectedKeyAsync());
|
||||
var encKey = await _cryptoService.GetEncKeyAsync(key);
|
||||
var protectedPin = await _stateService.GetProtectedPinAsync();
|
||||
@@ -253,7 +252,8 @@ namespace Bit.App.Pages
|
||||
}
|
||||
else
|
||||
{
|
||||
var key = await _cryptoService.MakeKeyFromPinAsync(Pin, _email, kdfConfig);
|
||||
var key = await _cryptoService.MakeKeyFromPinAsync(Pin, _email,
|
||||
kdf.GetValueOrDefault(KdfType.PBKDF2_SHA256), kdfIterations.GetValueOrDefault(5000));
|
||||
failed = false;
|
||||
Pin = string.Empty;
|
||||
await AppHelpers.ResetInvalidUnlockAttemptsAsync();
|
||||
@@ -278,7 +278,7 @@ namespace Bit.App.Pages
|
||||
}
|
||||
else
|
||||
{
|
||||
var key = await _cryptoService.MakeKeyAsync(MasterPassword, _email, kdfConfig);
|
||||
var key = await _cryptoService.MakeKeyAsync(MasterPassword, _email, kdf, kdfIterations);
|
||||
var storedKeyHash = await _cryptoService.GetKeyHashAsync();
|
||||
var passwordValid = false;
|
||||
|
||||
@@ -312,7 +312,8 @@ namespace Bit.App.Pages
|
||||
var protectedPin = await _stateService.GetProtectedPinAsync();
|
||||
var encKey = await _cryptoService.GetEncKeyAsync(key);
|
||||
var decPin = await _cryptoService.DecryptToUtf8Async(new EncString(protectedPin), encKey);
|
||||
var pinKey = await _cryptoService.MakePinKeyAysnc(decPin, _email, kdfConfig);
|
||||
var pinKey = await _cryptoService.MakePinKeyAysnc(decPin, _email,
|
||||
kdf.GetValueOrDefault(KdfType.PBKDF2_SHA256), kdfIterations.GetValueOrDefault(5000));
|
||||
await _stateService.SetPinProtectedKeyAsync(await _cryptoService.EncryptAsync(key.Key, pinKey));
|
||||
}
|
||||
MasterPassword = string.Empty;
|
||||
@@ -386,7 +387,6 @@ namespace Bit.App.Pages
|
||||
private async Task DoContinueAsync()
|
||||
{
|
||||
await _stateService.SetBiometricLockedAsync(false);
|
||||
_watchDeviceService.SyncDataToWatchAsync().FireAndForget();
|
||||
_messagingService.Send("unlocked");
|
||||
UnlockedAction?.Invoke();
|
||||
}
|
||||
|
||||
@@ -110,8 +110,8 @@
|
||||
VerticalOptions="CenterAndExpand"
|
||||
Icon="{Binding Source={x:Static core:BitwardenIcons.Device}}"
|
||||
Label="{u:I18n LogInWithAnotherDevice}"
|
||||
ButtonCommand="{Binding LogInWithDeviceCommand}"
|
||||
IsVisible="{Binding IsKnownDevice}"/>
|
||||
ButtonCommand="{Binding LogInCommand}"
|
||||
IsVisible="False"/>
|
||||
<controls:IconLabelButton
|
||||
HorizontalOptions="Fill"
|
||||
VerticalOptions="CenterAndExpand"
|
||||
|
||||
@@ -27,9 +27,9 @@ namespace Bit.App.Pages
|
||||
_vm.Page = this;
|
||||
_vm.StartTwoFactorAction = () => Device.BeginInvokeOnMainThread(async () => await StartTwoFactorAsync());
|
||||
_vm.LogInSuccessAction = () => Device.BeginInvokeOnMainThread(async () => await LogInSuccessAsync());
|
||||
_vm.LogInWithDeviceAction = () => StartLoginWithDeviceAsync().FireAndForget();
|
||||
_vm.StartSsoLoginAction = () => Device.BeginInvokeOnMainThread(async () => await StartSsoLoginAsync());
|
||||
_vm.UpdateTempPasswordAction = () => Device.BeginInvokeOnMainThread(async () => await UpdateTempPasswordAsync());
|
||||
_vm.UpdateTempPasswordAction =
|
||||
() => Device.BeginInvokeOnMainThread(async () => await UpdateTempPasswordAsync());
|
||||
_vm.CloseAction = async () =>
|
||||
{
|
||||
await _accountListOverlay.HideAsync();
|
||||
@@ -122,12 +122,6 @@ namespace Bit.App.Pages
|
||||
}
|
||||
}
|
||||
|
||||
private async Task StartLoginWithDeviceAsync()
|
||||
{
|
||||
var page = new LoginPasswordlessRequestPage(_vm.Email, _appOptions);
|
||||
await Navigation.PushModalAsync(new NavigationPage(page));
|
||||
}
|
||||
|
||||
private async Task StartSsoLoginAsync()
|
||||
{
|
||||
var page = new LoginSsoPage(_appOptions);
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Input;
|
||||
@@ -60,7 +59,6 @@ namespace Bit.App.Pages
|
||||
TogglePasswordCommand = new Command(TogglePassword);
|
||||
LogInCommand = new Command(async () => await LogInAsync());
|
||||
MoreCommand = new AsyncCommand(MoreAsync, onException: _logger.Exception, allowsMultipleExecutions: false);
|
||||
LogInWithDeviceCommand = new AsyncCommand(() => Device.InvokeOnMainThreadAsync(LogInWithDeviceAction), onException: _logger.Exception, allowsMultipleExecutions: false);
|
||||
|
||||
AccountSwitchingOverlayViewModel = new AccountSwitchingOverlayViewModel(_stateService, _messagingService, _logger)
|
||||
{
|
||||
@@ -118,7 +116,6 @@ namespace Bit.App.Pages
|
||||
public Command LogInCommand { get; }
|
||||
public Command TogglePasswordCommand { get; }
|
||||
public ICommand MoreCommand { get; internal set; }
|
||||
public ICommand LogInWithDeviceCommand { get; }
|
||||
public string ShowPasswordIcon => ShowPassword ? BitwardenIcons.EyeSlash : BitwardenIcons.Eye;
|
||||
public string PasswordVisibilityAccessibilityText => ShowPassword ? AppResources.PasswordIsVisibleTapToHide : AppResources.PasswordIsNotVisibleTapToShow;
|
||||
public string LoggingInAsText => string.Format(AppResources.LoggingInAsX, Email);
|
||||
@@ -126,7 +123,6 @@ namespace Bit.App.Pages
|
||||
public bool CanRemoveAccount { get; set; }
|
||||
public Action StartTwoFactorAction { get; set; }
|
||||
public Action LogInSuccessAction { get; set; }
|
||||
public Action LogInWithDeviceAction { get; set; }
|
||||
public Action UpdateTempPasswordAction { get; set; }
|
||||
public Action StartSsoLoginAction { get; set; }
|
||||
public Action CloseAction { get; set; }
|
||||
@@ -305,5 +301,15 @@ namespace Bit.App.Pages
|
||||
_logger.Exception(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleException(Exception ex)
|
||||
{
|
||||
Xamarin.Essentials.MainThread.InvokeOnMainThreadAsync(async () =>
|
||||
{
|
||||
await _deviceActionService.HideLoadingAsync();
|
||||
await _platformUtilsService.ShowDialogAsync(AppResources.GenericErrorMessage);
|
||||
}).FireAndForget();
|
||||
_logger.Exception(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,76 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<pages:BaseContentPage
|
||||
xmlns="http://xamarin.com/schemas/2014/forms"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
|
||||
x:Class="Bit.App.Pages.LoginPasswordlessRequestPage"
|
||||
xmlns:pages="clr-namespace:Bit.App.Pages"
|
||||
xmlns:controls="clr-namespace:Bit.App.Controls"
|
||||
xmlns:u="clr-namespace:Bit.App.Utilities"
|
||||
x:DataType="pages:LoginPasswordlessRequestViewModel"
|
||||
Title="{Binding PageTitle}">
|
||||
|
||||
<ContentPage.BindingContext>
|
||||
<pages:LoginPasswordlessRequestViewModel />
|
||||
</ContentPage.BindingContext>
|
||||
|
||||
<ContentPage.ToolbarItems>
|
||||
<ToolbarItem Text="{u:I18n Close}" Command="{Binding CloseCommand}" Order="Primary" Priority="-1" x:Name="_closeItem"/>
|
||||
</ContentPage.ToolbarItems>
|
||||
|
||||
<ScrollView>
|
||||
<StackLayout
|
||||
Padding="7, 0, 7, 20">
|
||||
<Label
|
||||
Text="{u:I18n LogInInitiated}"
|
||||
FontSize="Title"
|
||||
FontAttributes="Bold"
|
||||
Margin="0,14,0,21"/>
|
||||
<Label
|
||||
Text="{u:I18n ANotificationHasBeenSentToYourDevice}"
|
||||
FontSize="Small"
|
||||
Margin="0,0,0,10"/>
|
||||
<Label
|
||||
Text="{u:I18n PleaseMakeSureYourVaultIsUnlockedAndTheFingerprintPhraseMatchesOnTheOtherDevice}"
|
||||
FontSize="Small"
|
||||
Margin="0,0,0,24"/>
|
||||
<Label
|
||||
Text="{u:I18n FingerprintPhrase}"
|
||||
FontSize="Small"
|
||||
FontAttributes="Bold"/>
|
||||
<controls:MonoLabel
|
||||
FormattedText="{Binding FingerprintPhrase}"
|
||||
FontSize="Medium"
|
||||
TextColor="{DynamicResource FingerprintPhrase}"/>
|
||||
<Label
|
||||
Text="{u:I18n ResendNotification}"
|
||||
StyleClass="text-md"
|
||||
HorizontalOptions="Start"
|
||||
Margin="0,40,0,0"
|
||||
TextColor="{DynamicResource HyperlinkColor}">
|
||||
<Label.GestureRecognizers>
|
||||
<TapGestureRecognizer Command="{Binding CreatePasswordlessLoginCommand}" />
|
||||
</Label.GestureRecognizers>
|
||||
</Label>
|
||||
<StackLayout
|
||||
Orientation="Horizontal"
|
||||
Margin="0,30,0,0">
|
||||
<Label
|
||||
Text="{u:I18n NeedAnotherOption}"
|
||||
FontSize="Small"
|
||||
VerticalTextAlignment="End"/>
|
||||
<Label
|
||||
Text="{u:I18n ViewAllLoginOptions}"
|
||||
StyleClass="text-md"
|
||||
VerticalTextAlignment="End"
|
||||
VerticalOptions="CenterAndExpand"
|
||||
Margin="5, 0"
|
||||
TextColor="{DynamicResource HyperlinkColor}">
|
||||
<Label.GestureRecognizers>
|
||||
<TapGestureRecognizer Command="{Binding CloseCommand}" />
|
||||
</Label.GestureRecognizers>
|
||||
</Label>
|
||||
</StackLayout>
|
||||
|
||||
</StackLayout>
|
||||
</ScrollView>
|
||||
</pages:BaseContentPage>
|
||||
@@ -1,65 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Bit.App.Models;
|
||||
using Bit.App.Utilities;
|
||||
using Xamarin.Forms;
|
||||
|
||||
namespace Bit.App.Pages
|
||||
{
|
||||
public partial class LoginPasswordlessRequestPage : BaseContentPage
|
||||
{
|
||||
private LoginPasswordlessRequestViewModel _vm;
|
||||
private readonly AppOptions _appOptions;
|
||||
|
||||
public LoginPasswordlessRequestPage(string email, AppOptions appOptions = null)
|
||||
{
|
||||
InitializeComponent();
|
||||
_appOptions = appOptions;
|
||||
_vm = BindingContext as LoginPasswordlessRequestViewModel;
|
||||
_vm.Page = this;
|
||||
_vm.Email = email;
|
||||
_vm.StartTwoFactorAction = () => Device.BeginInvokeOnMainThread(async () => await StartTwoFactorAsync());
|
||||
_vm.LogInSuccessAction = () => Device.BeginInvokeOnMainThread(async () => await LogInSuccessAsync());
|
||||
_vm.UpdateTempPasswordAction = () => Device.BeginInvokeOnMainThread(async () => await UpdateTempPasswordAsync());
|
||||
_vm.CloseAction = () => { Navigation.PopModalAsync(); };
|
||||
|
||||
_vm.CreatePasswordlessLoginCommand.Execute(null);
|
||||
}
|
||||
|
||||
protected override void OnAppearing()
|
||||
{
|
||||
base.OnAppearing();
|
||||
_vm.StartCheckLoginRequestStatus();
|
||||
}
|
||||
|
||||
protected override void OnDisappearing()
|
||||
{
|
||||
base.OnDisappearing();
|
||||
_vm.StopCheckLoginRequestStatus();
|
||||
}
|
||||
|
||||
private async Task StartTwoFactorAsync()
|
||||
{
|
||||
var page = new TwoFactorPage(false, _appOptions);
|
||||
await Navigation.PushModalAsync(new NavigationPage(page));
|
||||
}
|
||||
|
||||
private async Task LogInSuccessAsync()
|
||||
{
|
||||
if (AppHelpers.SetAlternateMainPage(_appOptions))
|
||||
{
|
||||
return;
|
||||
}
|
||||
var previousPage = await AppHelpers.ClearPreviousPage();
|
||||
Application.Current.MainPage = new TabsPage(_appOptions, previousPage);
|
||||
}
|
||||
|
||||
private async Task UpdateTempPasswordAsync()
|
||||
{
|
||||
var page = new UpdateTempPasswordPage();
|
||||
await Navigation.PushModalAsync(new NavigationPage(page));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,194 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Input;
|
||||
using Bit.App.Abstractions;
|
||||
using Bit.App.Resources;
|
||||
using Bit.App.Utilities;
|
||||
using Bit.Core;
|
||||
using Bit.Core.Abstractions;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Models.Domain;
|
||||
using Bit.Core.Services;
|
||||
using Bit.Core.Utilities;
|
||||
using Xamarin.CommunityToolkit.ObjectModel;
|
||||
using Xamarin.Essentials;
|
||||
using Xamarin.Forms;
|
||||
|
||||
namespace Bit.App.Pages
|
||||
{
|
||||
public class LoginPasswordlessRequestViewModel : CaptchaProtectedViewModel
|
||||
{
|
||||
private const int REQUEST_TIME_UPDATE_PERIOD_IN_SECONDS = 4;
|
||||
|
||||
private IDeviceActionService _deviceActionService;
|
||||
private IAuthService _authService;
|
||||
private ISyncService _syncService;
|
||||
private II18nService _i18nService;
|
||||
private IStateService _stateService;
|
||||
private IPlatformUtilsService _platformUtilsService;
|
||||
private IEnvironmentService _environmentService;
|
||||
private ILogger _logger;
|
||||
|
||||
protected override II18nService i18nService => _i18nService;
|
||||
protected override IEnvironmentService environmentService => _environmentService;
|
||||
protected override IDeviceActionService deviceActionService => _deviceActionService;
|
||||
protected override IPlatformUtilsService platformUtilsService => _platformUtilsService;
|
||||
|
||||
private CancellationTokenSource _checkLoginRequestStatusCts;
|
||||
private Task _checkLoginRequestStatusTask;
|
||||
private string _fingerprintPhrase;
|
||||
private string _email;
|
||||
private string _requestId;
|
||||
private string _requestAccessCode;
|
||||
// Item1 publicKey, Item2 privateKey
|
||||
private Tuple<byte[], byte[]> _requestKeyPair;
|
||||
|
||||
public LoginPasswordlessRequestViewModel()
|
||||
{
|
||||
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>();
|
||||
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>();
|
||||
_environmentService = ServiceContainer.Resolve<IEnvironmentService>();
|
||||
_authService = ServiceContainer.Resolve<IAuthService>();
|
||||
_syncService = ServiceContainer.Resolve<ISyncService>();
|
||||
_i18nService = ServiceContainer.Resolve<II18nService>();
|
||||
_stateService = ServiceContainer.Resolve<IStateService>();
|
||||
_logger = ServiceContainer.Resolve<ILogger>();
|
||||
|
||||
PageTitle = AppResources.LogInWithAnotherDevice;
|
||||
|
||||
CreatePasswordlessLoginCommand = new AsyncCommand(CreatePasswordlessLoginAsync,
|
||||
onException: ex => HandleException(ex),
|
||||
allowsMultipleExecutions: false);
|
||||
|
||||
CloseCommand = new AsyncCommand(() => Device.InvokeOnMainThreadAsync(CloseAction),
|
||||
onException: _logger.Exception,
|
||||
allowsMultipleExecutions: false);
|
||||
}
|
||||
|
||||
public Action StartTwoFactorAction { get; set; }
|
||||
public Action LogInSuccessAction { get; set; }
|
||||
public Action UpdateTempPasswordAction { get; set; }
|
||||
public Action CloseAction { get; set; }
|
||||
|
||||
public ICommand CreatePasswordlessLoginCommand { get; }
|
||||
public ICommand CloseCommand { get; }
|
||||
|
||||
public string FingerprintPhrase
|
||||
{
|
||||
get => _fingerprintPhrase;
|
||||
set => SetProperty(ref _fingerprintPhrase, value);
|
||||
}
|
||||
|
||||
public string Email
|
||||
{
|
||||
get => _email;
|
||||
set => SetProperty(ref _email, value);
|
||||
}
|
||||
|
||||
public void StartCheckLoginRequestStatus()
|
||||
{
|
||||
try
|
||||
{
|
||||
_checkLoginRequestStatusCts?.Cancel();
|
||||
_checkLoginRequestStatusCts = new CancellationTokenSource();
|
||||
_checkLoginRequestStatusTask = new TimerTask(_logger, CheckLoginRequestStatus, _checkLoginRequestStatusCts).RunPeriodic(TimeSpan.FromSeconds(REQUEST_TIME_UPDATE_PERIOD_IN_SECONDS));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Exception(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public void StopCheckLoginRequestStatus()
|
||||
{
|
||||
try
|
||||
{
|
||||
_checkLoginRequestStatusCts?.Cancel();
|
||||
_checkLoginRequestStatusCts?.Dispose();
|
||||
_checkLoginRequestStatusCts = null;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Exception(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task CheckLoginRequestStatus()
|
||||
{
|
||||
if (string.IsNullOrEmpty(_requestId) || string.IsNullOrEmpty(_requestAccessCode))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var response = await _authService.GetPasswordlessLoginResponseAsync(_requestId, _requestAccessCode);
|
||||
|
||||
if (response.RequestApproved == null || !response.RequestApproved.Value)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
StopCheckLoginRequestStatus();
|
||||
|
||||
var authResult = await _authService.LogInPasswordlessAsync(Email, _requestAccessCode, _requestId, _requestKeyPair.Item2, response.Key, response.MasterPasswordHash);
|
||||
await AppHelpers.ResetInvalidUnlockAttemptsAsync();
|
||||
|
||||
if (await HandleCaptchaAsync(authResult.CaptchaSiteKey, authResult.CaptchaNeeded, CheckLoginRequestStatus))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (authResult.TwoFactor)
|
||||
{
|
||||
StartTwoFactorAction?.Invoke();
|
||||
}
|
||||
else if (authResult.ForcePasswordReset)
|
||||
{
|
||||
UpdateTempPasswordAction?.Invoke();
|
||||
}
|
||||
else
|
||||
{
|
||||
_syncService.FullSyncAsync(true).FireAndForget();
|
||||
LogInSuccessAction?.Invoke();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
StartCheckLoginRequestStatus();
|
||||
HandleException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task CreatePasswordlessLoginAsync()
|
||||
{
|
||||
await Device.InvokeOnMainThreadAsync(() => _deviceActionService.ShowLoadingAsync(AppResources.Loading));
|
||||
|
||||
var response = await _authService.PasswordlessCreateLoginRequestAsync(_email);
|
||||
if (response != null)
|
||||
{
|
||||
FingerprintPhrase = response.RequestFingerprint;
|
||||
_requestId = response.Id;
|
||||
_requestAccessCode = response.RequestAccessCode;
|
||||
_requestKeyPair = response.RequestKeyPair;
|
||||
}
|
||||
|
||||
await _deviceActionService.HideLoadingAsync();
|
||||
}
|
||||
|
||||
private void HandleException(Exception ex)
|
||||
{
|
||||
Xamarin.Essentials.MainThread.InvokeOnMainThreadAsync(async () =>
|
||||
{
|
||||
await _deviceActionService.HideLoadingAsync();
|
||||
await _platformUtilsService.ShowDialogAsync(AppResources.GenericErrorMessage);
|
||||
}).FireAndForget();
|
||||
_logger.Exception(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -117,14 +117,6 @@ namespace Bit.App.Pages
|
||||
return;
|
||||
}
|
||||
|
||||
var loginRequestData = await _authService.GetPasswordlessLoginRequestByIdAsync(LoginRequest.Id);
|
||||
if (loginRequestData.IsAnswered)
|
||||
{
|
||||
await _platformUtilsService.ShowDialogAsync(AppResources.ThisRequestIsNoLongerValid);
|
||||
await Page.Navigation.PopModalAsync();
|
||||
return;
|
||||
}
|
||||
|
||||
await _deviceActionService.ShowLoadingAsync(AppResources.Loading);
|
||||
await _authService.PasswordlessLoginAsync(LoginRequest.Id, LoginRequest.PubKey, approveRequest);
|
||||
await _deviceActionService.HideLoadingAsync();
|
||||
@@ -148,6 +140,16 @@ namespace Bit.App.Pages
|
||||
|
||||
return string.Format(AppResources.XMinutesAgo, DateTime.UtcNow.Minute - requestDate.Value.ToUniversalTime().Minute);
|
||||
}
|
||||
|
||||
private void HandleException(Exception ex)
|
||||
{
|
||||
Xamarin.Essentials.MainThread.InvokeOnMainThreadAsync(async () =>
|
||||
{
|
||||
await _deviceActionService.HideLoadingAsync();
|
||||
await _platformUtilsService.ShowDialogAsync(AppResources.GenericErrorMessage);
|
||||
}).FireAndForget();
|
||||
_logger.Exception(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public class LoginPasswordlessDetails
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
</ContentPage.ToolbarItems>
|
||||
|
||||
<ScrollView>
|
||||
<StackLayout Spacing="10">
|
||||
<StackLayout Spacing="20">
|
||||
<StackLayout StyleClass="box">
|
||||
<StackLayout StyleClass="box-row">
|
||||
<Label
|
||||
@@ -72,19 +72,8 @@
|
||||
AutomationProperties.HelpText="{Binding PasswordVisibilityAccessibilityText}"/>
|
||||
</Grid>
|
||||
<Label
|
||||
StyleClass="box-sub-label"
|
||||
Margin="0,0,0,10">
|
||||
<Label.FormattedText>
|
||||
<FormattedString>
|
||||
<Span Text="{u:I18n Important}" TextColor="{DynamicResource InfoColor}"/>
|
||||
<Span Text=": " TextColor="{DynamicResource InfoColor}"/>
|
||||
<Span Text="{Binding MasterPasswordMininumCharactersDescription}" TextColor="{DynamicResource MutedColor}"/>
|
||||
</FormattedString>
|
||||
</Label.FormattedText>
|
||||
</Label>
|
||||
<controls:PasswordStrengthProgressBar
|
||||
BindingContext="{Binding PasswordStrengthViewModel}"
|
||||
Margin="0,0"/>
|
||||
Text="{u:I18n MasterPasswordDescription}"
|
||||
StyleClass="box-footer-label" />
|
||||
</StackLayout>
|
||||
<StackLayout StyleClass="box">
|
||||
<Grid StyleClass="box-row">
|
||||
@@ -137,17 +126,6 @@
|
||||
StyleClass="box-footer-label" />
|
||||
</StackLayout>
|
||||
<StackLayout StyleClass="box">
|
||||
<StackLayout StyleClass="box-row, box-row-switch">
|
||||
<Switch
|
||||
IsToggled="{Binding CheckExposedMasterPassword}"
|
||||
StyleClass="box-value"
|
||||
HorizontalOptions="Start"
|
||||
Margin="0, 0, 10, 0"/>
|
||||
<Label
|
||||
Text="{u:I18n CheckKnownDataBreachesForThisPassword}"
|
||||
StyleClass="box-footer-label"
|
||||
VerticalOptions="Center"/>
|
||||
</StackLayout>
|
||||
<StackLayout StyleClass="box-row, box-row-switch"
|
||||
IsVisible="{Binding ShowTerms}">
|
||||
<Switch
|
||||
|
||||
@@ -1,36 +1,28 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Input;
|
||||
using Bit.App.Abstractions;
|
||||
using Bit.App.Controls;
|
||||
using Bit.App.Resources;
|
||||
using Bit.App.Utilities;
|
||||
using Bit.Core;
|
||||
using Bit.Core.Abstractions;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Exceptions;
|
||||
using Bit.Core.Models.Request;
|
||||
using Bit.Core.Services;
|
||||
using Bit.Core.Utilities;
|
||||
using Xamarin.Forms;
|
||||
|
||||
namespace Bit.App.Pages
|
||||
{
|
||||
public class RegisterPageViewModel : CaptchaProtectedViewModel, IPasswordStrengthable
|
||||
public class RegisterPageViewModel : CaptchaProtectedViewModel
|
||||
{
|
||||
private readonly IDeviceActionService _deviceActionService;
|
||||
private readonly II18nService _i18nService;
|
||||
private readonly IEnvironmentService _environmentService;
|
||||
private readonly IAuditService _auditService;
|
||||
private readonly IApiService _apiService;
|
||||
private readonly ICryptoService _cryptoService;
|
||||
private readonly IPlatformUtilsService _platformUtilsService;
|
||||
private string _email;
|
||||
private string _masterPassword;
|
||||
private bool _showPassword;
|
||||
private bool _acceptPolicies;
|
||||
private bool _checkExposedMasterPassword;
|
||||
|
||||
public RegisterPageViewModel()
|
||||
{
|
||||
@@ -40,15 +32,12 @@ namespace Bit.App.Pages
|
||||
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
||||
_i18nService = ServiceContainer.Resolve<II18nService>("i18nService");
|
||||
_environmentService = ServiceContainer.Resolve<IEnvironmentService>("environmentService");
|
||||
_auditService = ServiceContainer.Resolve<IAuditService>();
|
||||
|
||||
PageTitle = AppResources.CreateAccount;
|
||||
TogglePasswordCommand = new Command(TogglePassword);
|
||||
ToggleConfirmPasswordCommand = new Command(ToggleConfirmPassword);
|
||||
SubmitCommand = new Command(async () => await SubmitAsync());
|
||||
ShowTerms = !_platformUtilsService.IsSelfHost();
|
||||
PasswordStrengthViewModel = new PasswordStrengthViewModel(this);
|
||||
CheckExposedMasterPassword = true;
|
||||
}
|
||||
|
||||
public ICommand PoliciesClickCommand => new Command<string>((url) =>
|
||||
@@ -72,34 +61,6 @@ namespace Bit.App.Pages
|
||||
get => _acceptPolicies;
|
||||
set => SetProperty(ref _acceptPolicies, value);
|
||||
}
|
||||
|
||||
public bool CheckExposedMasterPassword
|
||||
{
|
||||
get => _checkExposedMasterPassword;
|
||||
set => SetProperty(ref _checkExposedMasterPassword, value);
|
||||
}
|
||||
|
||||
public string MasterPassword
|
||||
{
|
||||
get => _masterPassword;
|
||||
set
|
||||
{
|
||||
SetProperty(ref _masterPassword, value);
|
||||
PasswordStrengthViewModel.CalculatePasswordStrength();
|
||||
}
|
||||
}
|
||||
|
||||
public string Email
|
||||
{
|
||||
get => _email;
|
||||
set => SetProperty(ref _email, value);
|
||||
}
|
||||
|
||||
public string Password => MasterPassword;
|
||||
public List<string> UserInputs => PasswordStrengthViewModel.GetPasswordStrengthUserInput(Email);
|
||||
public string MasterPasswordMininumCharactersDescription => string.Format(AppResources.YourMasterPasswordCannotBeRecoveredIfYouForgetItXCharactersMinimum,
|
||||
Constants.MasterPasswordMinimumChars);
|
||||
public PasswordStrengthViewModel PasswordStrengthViewModel { get; }
|
||||
public bool ShowTerms { get; set; }
|
||||
public Command SubmitCommand { get; }
|
||||
public Command TogglePasswordCommand { get; }
|
||||
@@ -107,10 +68,13 @@ namespace Bit.App.Pages
|
||||
public string ShowPasswordIcon => ShowPassword ? BitwardenIcons.EyeSlash : BitwardenIcons.Eye;
|
||||
public string PasswordVisibilityAccessibilityText => ShowPassword ? AppResources.PasswordIsVisibleTapToHide : AppResources.PasswordIsNotVisibleTapToShow;
|
||||
public string Name { get; set; }
|
||||
public string Email { get; set; }
|
||||
public string MasterPassword { get; set; }
|
||||
public string ConfirmMasterPassword { get; set; }
|
||||
public string Hint { get; set; }
|
||||
public Action RegistrationSuccess { get; set; }
|
||||
public Action CloseAction { get; set; }
|
||||
|
||||
protected override II18nService i18nService => _i18nService;
|
||||
protected override IEnvironmentService environmentService => _environmentService;
|
||||
protected override IDeviceActionService deviceActionService => _deviceActionService;
|
||||
@@ -146,7 +110,7 @@ namespace Bit.App.Pages
|
||||
AppResources.Ok);
|
||||
return;
|
||||
}
|
||||
if (MasterPassword.Length < Constants.MasterPasswordMinimumChars)
|
||||
if (MasterPassword.Length < 8)
|
||||
{
|
||||
await _platformUtilsService.ShowDialogAsync(AppResources.MasterPasswordLengthValMessage,
|
||||
AppResources.AnErrorHasOccurred, AppResources.Ok);
|
||||
@@ -164,10 +128,8 @@ namespace Bit.App.Pages
|
||||
AppResources.AnErrorHasOccurred, AppResources.Ok);
|
||||
return;
|
||||
}
|
||||
if (await IsPasswordWeakOrExposed())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: Password strength check?
|
||||
|
||||
if (showLoading)
|
||||
{
|
||||
@@ -176,8 +138,9 @@ namespace Bit.App.Pages
|
||||
|
||||
Name = string.IsNullOrWhiteSpace(Name) ? null : Name;
|
||||
Email = Email.Trim().ToLower();
|
||||
var kdfConfig = new KdfConfig(KdfType.PBKDF2_SHA256, Constants.Pbkdf2Iterations, null, null);
|
||||
var key = await _cryptoService.MakeKeyAsync(MasterPassword, Email, kdfConfig);
|
||||
var kdf = KdfType.PBKDF2_SHA256;
|
||||
var kdfIterations = 100_000;
|
||||
var key = await _cryptoService.MakeKeyAsync(MasterPassword, Email, kdf, kdfIterations);
|
||||
var encKey = await _cryptoService.MakeEncKeyAsync(key);
|
||||
var hashedPassword = await _cryptoService.HashPasswordAsync(MasterPassword, key);
|
||||
var keys = await _cryptoService.MakeKeyPairAsync(encKey.Item1);
|
||||
@@ -188,10 +151,8 @@ namespace Bit.App.Pages
|
||||
MasterPasswordHash = hashedPassword,
|
||||
MasterPasswordHint = Hint,
|
||||
Key = encKey.Item2.EncryptedString,
|
||||
Kdf = kdfConfig.Type,
|
||||
KdfIterations = kdfConfig.Iterations,
|
||||
KdfMemory = kdfConfig.Memory,
|
||||
KdfParallelism = kdfConfig.Parallelism,
|
||||
Kdf = kdf,
|
||||
KdfIterations = kdfIterations,
|
||||
Keys = new KeysRequest
|
||||
{
|
||||
PublicKey = keys.Item1,
|
||||
@@ -199,7 +160,6 @@ namespace Bit.App.Pages
|
||||
},
|
||||
CaptchaResponse = _captchaToken,
|
||||
};
|
||||
|
||||
// TODO: org invite?
|
||||
|
||||
try
|
||||
@@ -248,43 +208,5 @@ namespace Bit.App.Pages
|
||||
entry.Focus();
|
||||
entry.CursorPosition = String.IsNullOrEmpty(ConfirmMasterPassword) ? 0 : ConfirmMasterPassword.Length;
|
||||
}
|
||||
|
||||
private async Task<bool> IsPasswordWeakOrExposed()
|
||||
{
|
||||
try
|
||||
{
|
||||
var title = string.Empty;
|
||||
var message = string.Empty;
|
||||
var exposedPassword = CheckExposedMasterPassword ? await _auditService.PasswordLeakedAsync(MasterPassword) > 0 : false;
|
||||
var weakPassword = PasswordStrengthViewModel.PasswordStrengthLevel <= PasswordStrengthLevel.Weak;
|
||||
|
||||
if (exposedPassword && weakPassword)
|
||||
{
|
||||
title = AppResources.WeakAndExposedMasterPassword;
|
||||
message = AppResources.WeakPasswordIdentifiedAndFoundInADataBreachAlertDescription;
|
||||
}
|
||||
else if (exposedPassword)
|
||||
{
|
||||
title = AppResources.ExposedMasterPassword;
|
||||
message = AppResources.PasswordFoundInADataBreachAlertDescription;
|
||||
}
|
||||
else if (weakPassword)
|
||||
{
|
||||
title = AppResources.WeakMasterPassword;
|
||||
message = AppResources.WeakPasswordIdentifiedUseAStrongPasswordToProtectYourAccount;
|
||||
}
|
||||
|
||||
if (exposedPassword || weakPassword)
|
||||
{
|
||||
return !await _platformUtilsService.ShowDialogAsync(message, title, AppResources.Yes, AppResources.No);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
HandleException(ex);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@
|
||||
Margin="0, 12, 0, 0"
|
||||
HasShadow="False"
|
||||
BackgroundColor="Transparent"
|
||||
BorderColor="{DynamicResource PrimaryColor}">
|
||||
BorderColor="Accent">
|
||||
<Label
|
||||
Text="{u:I18n ResetPasswordAutoEnrollInviteWarning}"
|
||||
StyleClass="text-muted, text-sm, text-bold"
|
||||
@@ -69,7 +69,7 @@
|
||||
Margin="0, 12, 0, 0"
|
||||
HasShadow="False"
|
||||
BackgroundColor="Transparent"
|
||||
BorderColor="{DynamicResource PrimaryColor}">
|
||||
BorderColor="Accent">
|
||||
<Label
|
||||
Text="{Binding PolicySummary}"
|
||||
StyleClass="text-muted, text-sm, text-bold"
|
||||
|
||||
@@ -5,7 +5,6 @@ using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using Bit.App.Abstractions;
|
||||
using Bit.App.Resources;
|
||||
using Bit.App.Utilities;
|
||||
using Bit.Core;
|
||||
using Bit.Core.Abstractions;
|
||||
using Bit.Core.Enums;
|
||||
@@ -138,8 +137,8 @@ namespace Bit.App.Pages
|
||||
}
|
||||
if (IsPolicyInEffect)
|
||||
{
|
||||
var userInputs = _passwordGenerationService.GetPasswordStrengthUserInput(await _stateService.GetEmailAsync());
|
||||
var passwordStrength = _passwordGenerationService.PasswordStrength(MasterPassword, userInputs);
|
||||
var userInput = await GetPasswordStrengthUserInput();
|
||||
var passwordStrength = _passwordGenerationService.PasswordStrength(MasterPassword, userInput);
|
||||
if (!await _policyService.EvaluateMasterPassword(passwordStrength.Score, MasterPassword, Policy))
|
||||
{
|
||||
await Page.DisplayAlert(AppResources.MasterPasswordPolicyValidationTitle,
|
||||
@@ -149,7 +148,7 @@ namespace Bit.App.Pages
|
||||
}
|
||||
else
|
||||
{
|
||||
if (MasterPassword.Length < Constants.MasterPasswordMinimumChars)
|
||||
if (MasterPassword.Length < 8)
|
||||
{
|
||||
await Page.DisplayAlert(AppResources.MasterPasswordPolicyValidationTitle,
|
||||
AppResources.MasterPasswordLengthValMessage, AppResources.Ok);
|
||||
@@ -163,9 +162,10 @@ namespace Bit.App.Pages
|
||||
return;
|
||||
}
|
||||
|
||||
var kdfConfig = new KdfConfig(KdfType.PBKDF2_SHA256, Constants.Pbkdf2Iterations, null, null);
|
||||
var kdf = KdfType.PBKDF2_SHA256;
|
||||
var kdfIterations = 100000;
|
||||
var email = await _stateService.GetEmailAsync();
|
||||
var key = await _cryptoService.MakeKeyAsync(MasterPassword, email, kdfConfig);
|
||||
var key = await _cryptoService.MakeKeyAsync(MasterPassword, email, kdf, kdfIterations);
|
||||
var masterPasswordHash = await _cryptoService.HashPasswordAsync(MasterPassword, key, HashPurpose.ServerAuthorization);
|
||||
var localMasterPasswordHash = await _cryptoService.HashPasswordAsync(MasterPassword, key, HashPurpose.LocalAuthorization);
|
||||
|
||||
@@ -186,10 +186,8 @@ namespace Bit.App.Pages
|
||||
MasterPasswordHash = masterPasswordHash,
|
||||
Key = encKey.Item2.EncryptedString,
|
||||
MasterPasswordHint = Hint,
|
||||
Kdf = kdfConfig.Type.GetValueOrDefault(KdfType.PBKDF2_SHA256),
|
||||
KdfIterations = kdfConfig.Iterations.GetValueOrDefault(Constants.Pbkdf2Iterations),
|
||||
KdfMemory = kdfConfig.Memory,
|
||||
KdfParallelism = kdfConfig.Parallelism,
|
||||
Kdf = kdf,
|
||||
KdfIterations = kdfIterations,
|
||||
OrgIdentifier = OrgIdentifier,
|
||||
Keys = new KeysRequest
|
||||
{
|
||||
@@ -203,7 +201,8 @@ namespace Bit.App.Pages
|
||||
await _deviceActionService.ShowLoadingAsync(AppResources.CreatingAccount);
|
||||
// Set Password and relevant information
|
||||
await _apiService.SetPasswordAsync(request);
|
||||
await _stateService.SetKdfConfigurationAsync(kdfConfig);
|
||||
await _stateService.SetKdfTypeAsync(kdf);
|
||||
await _stateService.SetKdfIterationsAsync(kdfIterations);
|
||||
await _cryptoService.SetKeyAsync(key);
|
||||
await _cryptoService.SetKeyHashAsync(localMasterPasswordHash);
|
||||
await _cryptoService.SetEncKeyAsync(encKey.Item2.EncryptedString);
|
||||
|
||||
@@ -44,7 +44,7 @@
|
||||
Margin="0"
|
||||
HasShadow="False"
|
||||
BackgroundColor="Transparent"
|
||||
BorderColor="{DynamicResource PrimaryColor}">
|
||||
BorderColor="Accent">
|
||||
<Label
|
||||
Text="{u:I18n UpdateMasterPasswordWarning}"
|
||||
StyleClass="text-muted, text-sm, text-bold"
|
||||
@@ -67,7 +67,7 @@
|
||||
Margin="0"
|
||||
HasShadow="False"
|
||||
BackgroundColor="Transparent"
|
||||
BorderColor="{DynamicResource PrimaryColor}">
|
||||
BorderColor="Accent">
|
||||
<Label
|
||||
Text="{Binding PolicySummary}"
|
||||
StyleClass="text-muted, text-sm, text-bold"
|
||||
|
||||
@@ -43,11 +43,12 @@ namespace Bit.App.Pages
|
||||
}
|
||||
|
||||
// Retrieve details for key generation
|
||||
var kdfConfig = await _stateService.GetActiveUserCustomDataAsync(a => new KdfConfig(a?.Profile));
|
||||
var kdf = await _stateService.GetKdfTypeAsync();
|
||||
var kdfIterations = await _stateService.GetKdfIterationsAsync();
|
||||
var email = await _stateService.GetEmailAsync();
|
||||
|
||||
// Create new key and hash new password
|
||||
var key = await _cryptoService.MakeKeyAsync(MasterPassword, email, kdfConfig);
|
||||
var key = await _cryptoService.MakeKeyAsync(MasterPassword, email, kdf, kdfIterations);
|
||||
var masterPasswordHash = await _cryptoService.HashPasswordAsync(MasterPassword, key);
|
||||
|
||||
// Create new encKey for the User
|
||||
|
||||
@@ -129,8 +129,8 @@ namespace Bit.App.Pages
|
||||
{
|
||||
if (useCurrentActiveAccount)
|
||||
{
|
||||
var user = await _stateService.GetActiveUserCustomDataAsync(a => (a?.Profile?.UserId, a?.Profile?.Name, a?.Profile?.Email, a?.Profile?.AvatarColor));
|
||||
return new AvatarImageSource(user.UserId, user.Name, user.Email, user.AvatarColor);
|
||||
return new AvatarImageSource(await _stateService.GetActiveUserIdAsync(),
|
||||
await _stateService.GetNameAsync(), await _stateService.GetEmailAsync());
|
||||
}
|
||||
return new AvatarImageSource();
|
||||
}
|
||||
|
||||
@@ -1,10 +1,4 @@
|
||||
using System;
|
||||
using Bit.App.Abstractions;
|
||||
using Bit.App.Controls;
|
||||
using Bit.App.Resources;
|
||||
using Bit.Core.Abstractions;
|
||||
using Bit.Core.Exceptions;
|
||||
using Bit.Core.Services;
|
||||
using Bit.App.Controls;
|
||||
using Bit.Core.Utilities;
|
||||
using Xamarin.Forms;
|
||||
|
||||
@@ -14,9 +8,6 @@ namespace Bit.App.Pages
|
||||
{
|
||||
private string _pageTitle = string.Empty;
|
||||
private AvatarImageSource _avatar;
|
||||
private LazyResolve<IDeviceActionService> _deviceActionService = new LazyResolve<IDeviceActionService>();
|
||||
private LazyResolve<IPlatformUtilsService> _platformUtilsService = new LazyResolve<IPlatformUtilsService>();
|
||||
private LazyResolve<ILogger> _logger = new LazyResolve<ILogger>();
|
||||
|
||||
public string PageTitle
|
||||
{
|
||||
@@ -31,21 +22,5 @@ namespace Bit.App.Pages
|
||||
}
|
||||
|
||||
public ContentPage Page { get; set; }
|
||||
|
||||
protected void HandleException(Exception ex, string message = null)
|
||||
{
|
||||
if (ex is ApiException apiException && apiException.Error != null)
|
||||
{
|
||||
message = apiException.Error.GetSingleMessage();
|
||||
}
|
||||
|
||||
Xamarin.Essentials.MainThread.InvokeOnMainThreadAsync(async () =>
|
||||
{
|
||||
await _deviceActionService.Value.HideLoadingAsync();
|
||||
await _platformUtilsService.Value.ShowDialogAsync(message ?? AppResources.GenericErrorMessage);
|
||||
}).FireAndForget();
|
||||
_logger.Value.Exception(ex);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,22 +16,6 @@ namespace Bit.App.Pages
|
||||
protected abstract IPlatformUtilsService platformUtilsService { get; }
|
||||
protected string _captchaToken = null;
|
||||
|
||||
protected async Task<bool> HandleCaptchaAsync(string captchaSiteKey, bool needsCaptcha, Func<Task> onSuccess)
|
||||
{
|
||||
if (!needsCaptcha)
|
||||
{
|
||||
_captchaToken = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (await HandleCaptchaAsync(captchaSiteKey))
|
||||
{
|
||||
await onSuccess();
|
||||
_captchaToken = null;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
protected async Task<bool> HandleCaptchaAsync(string CaptchaSiteKey)
|
||||
{
|
||||
var callbackUri = "bitwarden://captcha-callback";
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<pages:BaseContentPage
|
||||
xmlns="http://xamarin.com/schemas/2014/forms"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
|
||||
@@ -20,7 +20,6 @@
|
||||
<ResourceDictionary>
|
||||
<u:InverseBoolConverter x:Key="inverseBool" />
|
||||
<u:LocalizableEnumConverter x:Key="localizableEnum" />
|
||||
<u:IconGlyphConverter x:Key="iconGlyphConverter" />
|
||||
<xct:EnumToBoolConverter x:Key="enumToBool"/>
|
||||
<ToolbarItem Text="{u:I18n Cancel}" Command="{Binding CloseCommand}" Order="Primary" Priority="-1"
|
||||
x:Name="_closeItem" x:Key="closeItem" />
|
||||
@@ -67,7 +66,7 @@
|
||||
Margin="0"
|
||||
HasShadow="False"
|
||||
BackgroundColor="Transparent"
|
||||
BorderColor="{DynamicResource PrimaryColor}">
|
||||
BorderColor="Accent">
|
||||
<Label
|
||||
Text="{u:I18n PasswordGeneratorPolicyInEffect}"
|
||||
StyleClass="text-muted, text-sm, text-bold"
|
||||
@@ -252,7 +251,7 @@
|
||||
Grid.Row="1"/>
|
||||
<controls:IconButton
|
||||
StyleClass="box-row-button, box-row-button-platform"
|
||||
Text="{Binding ShowAnonAddyApiAccessToken, Converter={StaticResource inverseBool, iconGlyphConverter}, ConverterParameter={x:Static u:BooleanGlyphType.Eye}}"
|
||||
Text="{Binding ShowAnonAddyHiddenValueIcon}"
|
||||
Command="{Binding ToggleForwardedEmailHiddenValueCommand}"
|
||||
Grid.Row="1"
|
||||
Grid.Column="1"/>
|
||||
@@ -281,7 +280,7 @@
|
||||
IsPassword="{Binding ShowFirefoxRelayApiAccessToken, Converter={StaticResource inverseBool}}"/>
|
||||
<controls:IconButton
|
||||
StyleClass="box-row-button, box-row-button-platform"
|
||||
Text="{Binding ShowFirefoxRelayApiAccessToken, Converter={StaticResource inverseBool, iconGlyphConverter}, ConverterParameter={x:Static u:BooleanGlyphType.Eye}}"
|
||||
Text="{Binding ShowFirefoxRelayHiddenValueIcon}"
|
||||
Command="{Binding ToggleForwardedEmailHiddenValueCommand}"
|
||||
Grid.Row="1"
|
||||
Grid.Column="1"/>
|
||||
@@ -302,49 +301,7 @@
|
||||
IsPassword="{Binding ShowSimpleLoginApiKey, Converter={StaticResource inverseBool}}"/>
|
||||
<controls:IconButton
|
||||
StyleClass="box-row-button, box-row-button-platform"
|
||||
Text="{Binding ShowSimpleLoginApiKey, Converter={StaticResource inverseBool, iconGlyphConverter}, ConverterParameter={x:Static u:BooleanGlyphType.Eye}}"
|
||||
Command="{Binding ToggleForwardedEmailHiddenValueCommand}"
|
||||
Grid.Row="1"
|
||||
Grid.Column="1"/>
|
||||
</Grid>
|
||||
<!--DUCKDUCKGO OPTIONS-->
|
||||
<Grid StyleClass="box-row, box-row-input"
|
||||
IsVisible="{Binding ForwardedEmailServiceSelected, Converter={StaticResource enumToBool}, ConverterParameter={x:Static enums:ForwardedEmailServiceType.DuckDuckGo}}"
|
||||
Grid.RowDefinitions="Auto,*"
|
||||
Grid.ColumnDefinitions="*,Auto">
|
||||
<Label
|
||||
Text="{u:I18n APIKeyRequiredParenthesis}"
|
||||
StyleClass="box-label"/>
|
||||
<Entry
|
||||
x:Name="_duckDuckGoApiAccessTokenEntry"
|
||||
Text="{Binding DuckDuckGoApiKey}"
|
||||
StyleClass="box-value"
|
||||
Grid.Row="1"
|
||||
IsPassword="{Binding ShowDuckDuckGoApiKey, Converter={StaticResource inverseBool}}"/>
|
||||
<controls:IconButton
|
||||
StyleClass="box-row-button, box-row-button-platform"
|
||||
Text="{Binding ShowDuckDuckGoApiKey, Converter={StaticResource inverseBool, iconGlyphConverter}, ConverterParameter={x:Static u:BooleanGlyphType.Eye}}"
|
||||
Command="{Binding ToggleForwardedEmailHiddenValueCommand}"
|
||||
Grid.Row="1"
|
||||
Grid.Column="1"/>
|
||||
</Grid>
|
||||
<!--FASTMAIL OPTIONS-->
|
||||
<Grid StyleClass="box-row, box-row-input"
|
||||
IsVisible="{Binding ForwardedEmailServiceSelected, Converter={StaticResource enumToBool}, ConverterParameter={x:Static enums:ForwardedEmailServiceType.Fastmail}}"
|
||||
Grid.RowDefinitions="Auto,*"
|
||||
Grid.ColumnDefinitions="*,Auto">
|
||||
<Label
|
||||
Text="{u:I18n APIKeyRequiredParenthesis}"
|
||||
StyleClass="box-label"/>
|
||||
<Entry
|
||||
x:Name="_fastmailApiAccessTokenEntry"
|
||||
Text="{Binding FastmailApiKey}"
|
||||
StyleClass="box-value"
|
||||
Grid.Row="1"
|
||||
IsPassword="{Binding ShowFastmailApiKey, Converter={StaticResource inverseBool}}"/>
|
||||
<controls:IconButton
|
||||
StyleClass="box-row-button, box-row-button-platform"
|
||||
Text="{Binding ShowFastmailApiKey, Converter={StaticResource iconGlyphConverter}, ConverterParameter={x:Static u:BooleanGlyphType.Eye}}"
|
||||
Text="{Binding ShowSimpleLoginHiddenValueIcon}"
|
||||
Command="{Binding ToggleForwardedEmailHiddenValueCommand}"
|
||||
Grid.Row="1"
|
||||
Grid.Column="1"/>
|
||||
@@ -505,7 +462,7 @@
|
||||
StyleClass="box-value"
|
||||
HorizontalOptions="End"
|
||||
AutomationProperties.IsInAccessibleTree="True"
|
||||
AutomationProperties.Name="{u:I18n LowercaseAtoZ}"/>
|
||||
AutomationProperties.Name="{u:I18n LowercaseAtoZ}" />
|
||||
</StackLayout>
|
||||
<BoxView StyleClass="box-row-separator" />
|
||||
<StackLayout StyleClass="box-row, box-row-switch">
|
||||
|
||||
@@ -52,8 +52,6 @@ namespace Bit.App.Pages
|
||||
private bool _showFirefoxRelayApiAccessToken;
|
||||
private bool _showAnonAddyApiAccessToken;
|
||||
private bool _showSimpleLoginApiKey;
|
||||
private bool _showDuckDuckGoApiKey;
|
||||
private bool _showFastmailApiKey;
|
||||
private bool _editMode;
|
||||
|
||||
public GeneratorPageViewModel()
|
||||
@@ -81,8 +79,6 @@ namespace Bit.App.Pages
|
||||
|
||||
ForwardedEmailServiceTypeOptions = new List<ForwardedEmailServiceType> {
|
||||
ForwardedEmailServiceType.AnonAddy,
|
||||
ForwardedEmailServiceType.DuckDuckGo,
|
||||
ForwardedEmailServiceType.Fastmail,
|
||||
ForwardedEmailServiceType.FirefoxRelay,
|
||||
ForwardedEmailServiceType.SimpleLogin
|
||||
};
|
||||
@@ -465,9 +461,15 @@ namespace Bit.App.Pages
|
||||
{
|
||||
return _showAnonAddyApiAccessToken;
|
||||
}
|
||||
set => SetProperty(ref _showAnonAddyApiAccessToken, value);
|
||||
set => SetProperty(ref _showAnonAddyApiAccessToken, value,
|
||||
additionalPropertyNames: new string[]
|
||||
{
|
||||
nameof(ShowAnonAddyHiddenValueIcon)
|
||||
});
|
||||
}
|
||||
|
||||
public string ShowAnonAddyHiddenValueIcon => _showAnonAddyApiAccessToken ? BitwardenIcons.EyeSlash : BitwardenIcons.Eye;
|
||||
|
||||
public string AnonAddyDomainName
|
||||
{
|
||||
get => _usernameOptions.AnonAddyDomainName;
|
||||
@@ -502,9 +504,15 @@ namespace Bit.App.Pages
|
||||
{
|
||||
return _showFirefoxRelayApiAccessToken;
|
||||
}
|
||||
set => SetProperty(ref _showFirefoxRelayApiAccessToken, value);
|
||||
set => SetProperty(ref _showFirefoxRelayApiAccessToken, value,
|
||||
additionalPropertyNames: new string[]
|
||||
{
|
||||
nameof(ShowFirefoxRelayHiddenValueIcon)
|
||||
});
|
||||
}
|
||||
|
||||
public string ShowFirefoxRelayHiddenValueIcon => _showFirefoxRelayApiAccessToken ? BitwardenIcons.EyeSlash : BitwardenIcons.Eye;
|
||||
|
||||
public string SimpleLoginApiKey
|
||||
{
|
||||
get => _usernameOptions.SimpleLoginApiKey;
|
||||
@@ -525,55 +533,14 @@ namespace Bit.App.Pages
|
||||
{
|
||||
return _showSimpleLoginApiKey;
|
||||
}
|
||||
set => SetProperty(ref _showSimpleLoginApiKey, value);
|
||||
}
|
||||
|
||||
public string DuckDuckGoApiKey
|
||||
{
|
||||
get => _usernameOptions.DuckDuckGoApiKey;
|
||||
set
|
||||
{
|
||||
if (_usernameOptions.DuckDuckGoApiKey != value)
|
||||
set => SetProperty(ref _showSimpleLoginApiKey, value,
|
||||
additionalPropertyNames: new string[]
|
||||
{
|
||||
_usernameOptions.DuckDuckGoApiKey = value;
|
||||
TriggerPropertyChanged(nameof(DuckDuckGoApiKey));
|
||||
SaveUsernameOptionsAsync(false).FireAndForget();
|
||||
}
|
||||
}
|
||||
nameof(ShowSimpleLoginHiddenValueIcon)
|
||||
});
|
||||
}
|
||||
|
||||
public bool ShowDuckDuckGoApiKey
|
||||
{
|
||||
get
|
||||
{
|
||||
return _showDuckDuckGoApiKey;
|
||||
}
|
||||
set => SetProperty(ref _showDuckDuckGoApiKey, value);
|
||||
}
|
||||
|
||||
|
||||
public string FastmailApiKey
|
||||
{
|
||||
get => _usernameOptions.FastMailApiKey;
|
||||
set
|
||||
{
|
||||
if (_usernameOptions.FastMailApiKey != value)
|
||||
{
|
||||
_usernameOptions.FastMailApiKey = value;
|
||||
TriggerPropertyChanged(nameof(FastmailApiKey));
|
||||
SaveUsernameOptionsAsync(false).FireAndForget();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool ShowFastmailApiKey
|
||||
{
|
||||
get
|
||||
{
|
||||
return _showFastmailApiKey;
|
||||
}
|
||||
set => SetProperty(ref _showFastmailApiKey, value);
|
||||
}
|
||||
public string ShowSimpleLoginHiddenValueIcon => _showSimpleLoginApiKey ? BitwardenIcons.EyeSlash : BitwardenIcons.Eye;
|
||||
|
||||
public bool CapitalizeRandomWordUsername
|
||||
{
|
||||
@@ -811,8 +778,6 @@ namespace Bit.App.Pages
|
||||
TriggerPropertyChanged(nameof(FirefoxRelayApiAccessToken));
|
||||
TriggerPropertyChanged(nameof(AnonAddyDomainName));
|
||||
TriggerPropertyChanged(nameof(AnonAddyApiAccessToken));
|
||||
TriggerPropertyChanged(nameof(DuckDuckGoApiKey));
|
||||
TriggerPropertyChanged(nameof(FastmailApiKey));
|
||||
TriggerPropertyChanged(nameof(CatchAllEmailDomain));
|
||||
TriggerPropertyChanged(nameof(ForwardedEmailServiceSelected));
|
||||
TriggerPropertyChanged(nameof(UsernameTypeSelected));
|
||||
@@ -884,12 +849,6 @@ namespace Bit.App.Pages
|
||||
case ForwardedEmailServiceType.SimpleLogin:
|
||||
ShowSimpleLoginApiKey = !ShowSimpleLoginApiKey;
|
||||
break;
|
||||
case ForwardedEmailServiceType.DuckDuckGo:
|
||||
ShowDuckDuckGoApiKey = !ShowDuckDuckGoApiKey;
|
||||
break;
|
||||
case ForwardedEmailServiceType.Fastmail:
|
||||
ShowFastmailApiKey = !ShowFastmailApiKey;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,7 +67,7 @@
|
||||
Margin="0, 12, 0, 0"
|
||||
HasShadow="False"
|
||||
BackgroundColor="Transparent"
|
||||
BorderColor="{DynamicResource PrimaryColor}">
|
||||
BorderColor="Accent">
|
||||
<Label
|
||||
Text="{u:I18n SendDisabledWarning}"
|
||||
StyleClass="text-muted, text-sm, text-bold"
|
||||
@@ -79,7 +79,7 @@
|
||||
Margin="0, 12, 0, 0"
|
||||
HasShadow="False"
|
||||
BackgroundColor="Transparent"
|
||||
BorderColor="{DynamicResource PrimaryColor}">
|
||||
BorderColor="Accent">
|
||||
<Label
|
||||
Text="{u:I18n SendOptionsPolicyInEffect}"
|
||||
StyleClass="text-muted, text-sm, text-bold"
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
Margin="0, 12, 0, 0"
|
||||
HasShadow="False"
|
||||
BackgroundColor="Transparent"
|
||||
BorderColor="{DynamicResource PrimaryColor}">
|
||||
BorderColor="Accent">
|
||||
<Label
|
||||
Text="{u:I18n SendDisabledWarning}"
|
||||
StyleClass="text-muted, text-sm, text-bold"
|
||||
@@ -60,7 +60,7 @@
|
||||
Margin="0, 12, 0, 0"
|
||||
HasShadow="False"
|
||||
BackgroundColor="Transparent"
|
||||
BorderColor="{DynamicResource PrimaryColor}">
|
||||
BorderColor="Accent">
|
||||
<Label
|
||||
Text="{u:I18n SendOptionsPolicyInEffect}"
|
||||
StyleClass="text-muted, text-sm, text-bold"
|
||||
|
||||
@@ -108,7 +108,7 @@
|
||||
Margin="0, 12, 0, 6"
|
||||
HasShadow="False"
|
||||
BackgroundColor="Transparent"
|
||||
BorderColor="{DynamicResource PrimaryColor}">
|
||||
BorderColor="Accent">
|
||||
<Label
|
||||
Text="{u:I18n SendDisabledWarning}"
|
||||
StyleClass="text-muted, text-sm, text-bold"
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
Margin="0, 12, 0, 0"
|
||||
HasShadow="False"
|
||||
BackgroundColor="Transparent"
|
||||
BorderColor="{DynamicResource PrimaryColor}">
|
||||
BorderColor="Accent">
|
||||
<Label
|
||||
Text="{u:I18n DisablePersonalVaultExportPolicyInEffect}"
|
||||
StyleClass="text-muted, text-sm, text-bold"
|
||||
|
||||
@@ -1,107 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<pages:BaseContentPage
|
||||
xmlns="http://xamarin.com/schemas/2014/forms"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
|
||||
xmlns:xct="http://xamarin.com/schemas/2020/toolkit"
|
||||
x:Class="Bit.App.Pages.LoginPasswordlessRequestsListPage"
|
||||
xmlns:pages="clr-namespace:Bit.App.Pages"
|
||||
x:DataType="pages:LoginPasswordlessRequestsListViewModel"
|
||||
xmlns:models="clr-namespace:Bit.Core.Models.Response;assembly=BitwardenCore"
|
||||
xmlns:core="clr-namespace:Bit.Core;assembly=BitwardenCore"
|
||||
xmlns:controls="clr-namespace:Bit.App.Controls"
|
||||
xmlns:u="clr-namespace:Bit.App.Utilities"
|
||||
Title="{Binding PageTitle}">
|
||||
|
||||
<ContentPage.BindingContext>
|
||||
<pages:LoginPasswordlessRequestsListViewModel />
|
||||
</ContentPage.BindingContext>
|
||||
|
||||
<ContentPage.ToolbarItems>
|
||||
<ToolbarItem Text="{u:I18n Close}" Clicked="Close_Clicked" Order="Primary" Priority="-1" />
|
||||
</ContentPage.ToolbarItems>
|
||||
|
||||
<ContentPage.Resources>
|
||||
<ResourceDictionary>
|
||||
<u:DateTimeConverter x:Key="dateTime" />
|
||||
<xct:ItemSelectedEventArgsConverter x:Key="ItemSelectedEventArgsConverter" />
|
||||
<controls:SelectionChangedEventArgsConverter x:Key="SelectionChangedEventArgsConverter" />
|
||||
<DataTemplate
|
||||
x:Key="loginRequestTemplate"
|
||||
x:DataType="models:PasswordlessLoginResponse">
|
||||
<Grid
|
||||
Padding="10, 0"
|
||||
RowSpacing="0"
|
||||
RowDefinitions="*, Auto, *, 10"
|
||||
ColumnDefinitions="*, *">
|
||||
<Label
|
||||
Text="{u:I18n FingerprintPhrase}"
|
||||
FontSize="Small"
|
||||
Padding="0, 10, 0 ,0"
|
||||
FontAttributes="Bold"/>
|
||||
<controls:MonoLabel
|
||||
FormattedText="{Binding RequestFingerprint}"
|
||||
Grid.Row="1"
|
||||
Grid.ColumnSpan="2"
|
||||
FontSize="Small"
|
||||
Padding="0, 5, 0, 10"
|
||||
VerticalTextAlignment="Center"
|
||||
TextColor="{DynamicResource FingerprintPhrase}"/>
|
||||
<Label
|
||||
Grid.Row="2"
|
||||
HorizontalOptions="Start"
|
||||
HorizontalTextAlignment="Start"
|
||||
Text="{Binding RequestDeviceType}"
|
||||
StyleClass="list-header-sub" />
|
||||
<Label
|
||||
Grid.Row="2"
|
||||
Grid.Column="1"
|
||||
HorizontalOptions="End"
|
||||
HorizontalTextAlignment="End"
|
||||
Text="{Binding CreationDate, Converter={StaticResource dateTime}}"
|
||||
StyleClass="list-header-sub" />
|
||||
<BoxView
|
||||
StyleClass="list-section-separator-top, list-section-separator-top-platform"
|
||||
VerticalOptions="End"
|
||||
Grid.Row="3"
|
||||
Grid.ColumnSpan="2"/>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
|
||||
<StackLayout
|
||||
x:Key="mainLayout"
|
||||
x:Name="_mainLayout"
|
||||
Padding="0, 10">
|
||||
<RefreshView
|
||||
IsRefreshing="{Binding IsRefreshing}"
|
||||
Command="{Binding RefreshCommand}"
|
||||
VerticalOptions="FillAndExpand"
|
||||
BackgroundColor="{DynamicResource BackgroundColor}">
|
||||
<controls:ExtendedCollectionView
|
||||
ItemsSource="{Binding LoginRequests}"
|
||||
ItemTemplate="{StaticResource loginRequestTemplate}"
|
||||
SelectionMode="Single"
|
||||
ExtraDataForLogging="Login requests page" >
|
||||
<controls:ExtendedCollectionView.Behaviors>
|
||||
<xct:EventToCommandBehavior
|
||||
EventName="SelectionChanged"
|
||||
Command="{Binding AnswerRequestCommand}"
|
||||
EventArgsConverter="{StaticResource SelectionChangedEventArgsConverter}" />
|
||||
</controls:ExtendedCollectionView.Behaviors>
|
||||
</controls:ExtendedCollectionView>
|
||||
</RefreshView>
|
||||
<controls:IconLabelButton
|
||||
VerticalOptions="End"
|
||||
Margin="10,0"
|
||||
Icon="{Binding Source={x:Static core:BitwardenIcons.Trash}}"
|
||||
Label="{u:I18n DeclineAllRequests}"
|
||||
ButtonCommand="{Binding DeclineAllRequestsCommand}"/>
|
||||
</StackLayout>
|
||||
</ResourceDictionary>
|
||||
</ContentPage.Resources>
|
||||
|
||||
<ContentView
|
||||
x:Name="_mainContent">
|
||||
</ContentView>
|
||||
|
||||
</pages:BaseContentPage>
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Bit.Core.Abstractions;
|
||||
using Bit.Core.Models.Response;
|
||||
using Bit.Core.Utilities;
|
||||
using Xamarin.Forms;
|
||||
|
||||
namespace Bit.App.Pages
|
||||
{
|
||||
public partial class LoginPasswordlessRequestsListPage : BaseContentPage
|
||||
{
|
||||
private LoginPasswordlessRequestsListViewModel _vm;
|
||||
|
||||
public LoginPasswordlessRequestsListPage()
|
||||
{
|
||||
InitializeComponent();
|
||||
SetActivityIndicator(_mainContent);
|
||||
_vm = BindingContext as LoginPasswordlessRequestsListViewModel;
|
||||
_vm.Page = this;
|
||||
}
|
||||
|
||||
protected override async void OnAppearing()
|
||||
{
|
||||
base.OnAppearing();
|
||||
await LoadOnAppearedAsync(_mainLayout, false, _vm.RefreshAsync, _mainContent);
|
||||
}
|
||||
|
||||
private async void Close_Clicked(object sender, System.EventArgs e)
|
||||
{
|
||||
if (DoOnce())
|
||||
{
|
||||
await Navigation.PopModalAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,139 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Input;
|
||||
using Bit.App.Abstractions;
|
||||
using Bit.App.Resources;
|
||||
using Bit.Core.Abstractions;
|
||||
using Bit.Core.Models.Response;
|
||||
using Bit.Core.Utilities;
|
||||
using Xamarin.CommunityToolkit.ObjectModel;
|
||||
using Xamarin.Forms;
|
||||
|
||||
namespace Bit.App.Pages
|
||||
{
|
||||
public class LoginPasswordlessRequestsListViewModel : BaseViewModel
|
||||
{
|
||||
private readonly IAuthService _authService;
|
||||
private readonly IStateService _stateService;
|
||||
private readonly IDeviceActionService _deviceActionService;
|
||||
private readonly IPlatformUtilsService _platformUtilsService;
|
||||
private bool _isRefreshing;
|
||||
|
||||
public LoginPasswordlessRequestsListViewModel()
|
||||
{
|
||||
_authService = ServiceContainer.Resolve<IAuthService>();
|
||||
_stateService = ServiceContainer.Resolve<IStateService>();
|
||||
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>();
|
||||
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>();
|
||||
|
||||
PageTitle = AppResources.PendingLogInRequests;
|
||||
LoginRequests = new ObservableRangeCollection<PasswordlessLoginResponse>();
|
||||
|
||||
AnswerRequestCommand = new AsyncCommand<PasswordlessLoginResponse>(PasswordlessLoginAsync,
|
||||
onException: ex => HandleException(ex),
|
||||
allowsMultipleExecutions: false);
|
||||
|
||||
DeclineAllRequestsCommand = new AsyncCommand(DeclineAllRequestsAsync,
|
||||
onException: ex => HandleException(ex),
|
||||
allowsMultipleExecutions: false);
|
||||
|
||||
RefreshCommand = new Command(async () => await RefreshAsync());
|
||||
}
|
||||
|
||||
public ICommand RefreshCommand { get; }
|
||||
|
||||
public AsyncCommand<PasswordlessLoginResponse> AnswerRequestCommand { get; }
|
||||
|
||||
public AsyncCommand DeclineAllRequestsCommand { get; }
|
||||
|
||||
public ObservableRangeCollection<PasswordlessLoginResponse> LoginRequests { get; }
|
||||
|
||||
public bool IsRefreshing
|
||||
{
|
||||
get => _isRefreshing;
|
||||
set => SetProperty(ref _isRefreshing, value);
|
||||
}
|
||||
|
||||
public async Task RefreshAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
IsRefreshing = true;
|
||||
LoginRequests.ReplaceRange(await _authService.GetActivePasswordlessLoginRequestsAsync());
|
||||
if (!LoginRequests.Any())
|
||||
{
|
||||
Page.Navigation.PopModalAsync().FireAndForget();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
HandleException(ex);
|
||||
}
|
||||
finally
|
||||
{
|
||||
IsRefreshing = false;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task PasswordlessLoginAsync(PasswordlessLoginResponse request)
|
||||
{
|
||||
if (request.IsExpired)
|
||||
{
|
||||
await _platformUtilsService.ShowDialogAsync(AppResources.LoginRequestHasAlreadyExpired);
|
||||
await Page.Navigation.PopModalAsync();
|
||||
return;
|
||||
}
|
||||
|
||||
var loginRequestData = await _authService.GetPasswordlessLoginRequestByIdAsync(request.Id);
|
||||
if (loginRequestData.IsAnswered)
|
||||
{
|
||||
await _platformUtilsService.ShowDialogAsync(AppResources.ThisRequestIsNoLongerValid);
|
||||
return;
|
||||
}
|
||||
|
||||
var page = new LoginPasswordlessPage(new LoginPasswordlessDetails()
|
||||
{
|
||||
PubKey = loginRequestData.PublicKey,
|
||||
Id = loginRequestData.Id,
|
||||
IpAddress = loginRequestData.RequestIpAddress,
|
||||
Email = await _stateService.GetEmailAsync(),
|
||||
FingerprintPhrase = loginRequestData.RequestFingerprint,
|
||||
RequestDate = loginRequestData.CreationDate,
|
||||
DeviceType = loginRequestData.RequestDeviceType,
|
||||
Origin = loginRequestData.Origin
|
||||
});
|
||||
|
||||
await Device.InvokeOnMainThreadAsync(() => Application.Current.MainPage.Navigation.PushModalAsync(new NavigationPage(page)));
|
||||
}
|
||||
|
||||
private async Task DeclineAllRequestsAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!await _platformUtilsService.ShowDialogAsync(AppResources.AreYouSureYouWantToDeclineAllPendingLogInRequests, null, AppResources.Yes, AppResources.No))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
await _deviceActionService.ShowLoadingAsync(AppResources.Loading);
|
||||
var taskList = new List<Task>();
|
||||
foreach (var request in LoginRequests)
|
||||
{
|
||||
taskList.Add(_authService.PasswordlessLoginAsync(request.Id, request.PublicKey, false));
|
||||
}
|
||||
await Task.WhenAll(taskList);
|
||||
await _deviceActionService.HideLoadingAsync();
|
||||
await RefreshAsync();
|
||||
_platformUtilsService.ShowToast("info", null, AppResources.RequestsDeclined);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
HandleException(ex);
|
||||
RefreshAsync().FireAndForget();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
Padding="10"
|
||||
HasShadow="False"
|
||||
BackgroundColor="Transparent"
|
||||
BorderColor="{DynamicResource PrimaryColor}">
|
||||
BorderColor="Accent">
|
||||
<Label
|
||||
Text="{Binding Name, Mode=OneWay}"
|
||||
StyleClass="text-muted, text-sm, text-bold"
|
||||
@@ -57,7 +57,7 @@
|
||||
Padding="10"
|
||||
HasShadow="False"
|
||||
BackgroundColor="Transparent"
|
||||
BorderColor="{DynamicResource PrimaryColor}">
|
||||
BorderColor="Accent">
|
||||
<Label
|
||||
Text="{Binding Name, Mode=OneWay}"
|
||||
StyleClass="text-muted, text-sm, text-bold"
|
||||
|
||||
@@ -7,11 +7,7 @@ using Bit.App.Pages.Accounts;
|
||||
using Bit.App.Resources;
|
||||
using Bit.Core.Abstractions;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Models;
|
||||
using Bit.Core.Models.Domain;
|
||||
using Bit.Core.Models.Response;
|
||||
using Bit.Core.Models.View;
|
||||
using Bit.Core.Services;
|
||||
using Bit.Core.Utilities;
|
||||
using Xamarin.CommunityToolkit.ObjectModel;
|
||||
using Xamarin.Forms;
|
||||
@@ -36,8 +32,6 @@ namespace Bit.App.Pages
|
||||
private readonly IClipboardService _clipboardService;
|
||||
private readonly ILogger _loggerService;
|
||||
private readonly IPushNotificationService _pushNotificationService;
|
||||
private readonly IAuthService _authService;
|
||||
private readonly IWatchDeviceService _watchDeviceService;
|
||||
private const int CustomVaultTimeoutValue = -100;
|
||||
|
||||
private bool _supportsBiometric;
|
||||
@@ -50,7 +44,7 @@ namespace Bit.App.Pages
|
||||
private bool _showChangeMasterPassword;
|
||||
private bool _reportLoggingEnabled;
|
||||
private bool _approvePasswordlessLoginRequests;
|
||||
private bool _shouldConnectToWatch;
|
||||
|
||||
private List<KeyValuePair<string, int?>> _vaultTimeouts =
|
||||
new List<KeyValuePair<string, int?>>
|
||||
{
|
||||
@@ -93,8 +87,7 @@ namespace Bit.App.Pages
|
||||
_clipboardService = ServiceContainer.Resolve<IClipboardService>("clipboardService");
|
||||
_loggerService = ServiceContainer.Resolve<ILogger>("logger");
|
||||
_pushNotificationService = ServiceContainer.Resolve<IPushNotificationService>();
|
||||
_authService = ServiceContainer.Resolve<IAuthService>();
|
||||
_watchDeviceService = ServiceContainer.Resolve<IWatchDeviceService>();
|
||||
|
||||
GroupedItems = new ObservableRangeCollection<ISettingsPageListItem>();
|
||||
PageTitle = AppResources.Settings;
|
||||
|
||||
@@ -145,8 +138,6 @@ namespace Bit.App.Pages
|
||||
!await _keyConnectorService.GetUsesKeyConnector();
|
||||
_reportLoggingEnabled = await _loggerService.IsEnabled();
|
||||
_approvePasswordlessLoginRequests = await _stateService.GetApprovePasswordlessLoginsAsync();
|
||||
_shouldConnectToWatch = await _stateService.GetShouldConnectToWatchAsync();
|
||||
|
||||
BuildList();
|
||||
}
|
||||
|
||||
@@ -422,9 +413,12 @@ namespace Bit.App.Pages
|
||||
AppResources.Yes, AppResources.No);
|
||||
}
|
||||
|
||||
var kdfConfig = await _stateService.GetActiveUserCustomDataAsync(a => new KdfConfig(a?.Profile));
|
||||
var kdf = await _stateService.GetKdfTypeAsync();
|
||||
var kdfIterations = await _stateService.GetKdfIterationsAsync();
|
||||
var email = await _stateService.GetEmailAsync();
|
||||
var pinKey = await _cryptoService.MakePinKeyAysnc(pin, email, kdfConfig);
|
||||
var pinKey = await _cryptoService.MakePinKeyAysnc(pin, email,
|
||||
kdf.GetValueOrDefault(Core.Enums.KdfType.PBKDF2_SHA256),
|
||||
kdfIterations.GetValueOrDefault(5000));
|
||||
var key = await _cryptoService.GetKeyAsync();
|
||||
var pinProtectedKey = await _cryptoService.EncryptAsync(key.Key, pinKey);
|
||||
|
||||
@@ -563,14 +557,6 @@ namespace Bit.App.Pages
|
||||
ExecuteAsync = () => TwoStepAsync()
|
||||
}
|
||||
};
|
||||
if (_approvePasswordlessLoginRequests)
|
||||
{
|
||||
manageItems.Add(new SettingsPageListItem
|
||||
{
|
||||
Name = AppResources.PendingLogInRequests,
|
||||
ExecuteAsync = () => PendingLoginRequestsAsync()
|
||||
});
|
||||
}
|
||||
if (_supportsBiometric || _biometric)
|
||||
{
|
||||
var biometricName = AppResources.Biometrics;
|
||||
@@ -615,26 +601,19 @@ namespace Bit.App.Pages
|
||||
ExecuteAsync = () => SetScreenCaptureAllowedAsync()
|
||||
});
|
||||
}
|
||||
var accountItems = new List<SettingsPageListItem>();
|
||||
if (Device.RuntimePlatform == Device.iOS)
|
||||
var accountItems = new List<SettingsPageListItem>
|
||||
{
|
||||
accountItems.Add(new SettingsPageListItem
|
||||
new SettingsPageListItem
|
||||
{
|
||||
Name = AppResources.ConnectToWatch,
|
||||
SubLabel = _shouldConnectToWatch ? AppResources.On : AppResources.Off,
|
||||
ExecuteAsync = () => ToggleWatchConnectionAsync()
|
||||
});
|
||||
}
|
||||
accountItems.Add(new SettingsPageListItem
|
||||
{
|
||||
Name = AppResources.FingerprintPhrase,
|
||||
ExecuteAsync = () => FingerprintAsync()
|
||||
});
|
||||
accountItems.Add(new SettingsPageListItem
|
||||
{
|
||||
Name = AppResources.LogOut,
|
||||
ExecuteAsync = () => LogOutAsync()
|
||||
});
|
||||
Name = AppResources.FingerprintPhrase,
|
||||
ExecuteAsync = () => FingerprintAsync()
|
||||
},
|
||||
new SettingsPageListItem
|
||||
{
|
||||
Name = AppResources.LogOut,
|
||||
ExecuteAsync = () => LogOutAsync()
|
||||
}
|
||||
};
|
||||
if (_showChangeMasterPassword)
|
||||
{
|
||||
accountItems.Insert(0, new SettingsPageListItem
|
||||
@@ -762,25 +741,6 @@ namespace Bit.App.Pages
|
||||
}
|
||||
}
|
||||
|
||||
private async Task PendingLoginRequestsAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
var requests = await _authService.GetActivePasswordlessLoginRequestsAsync();
|
||||
if (requests == null || !requests.Any())
|
||||
{
|
||||
_platformUtilsService.ShowToast("info", null, AppResources.NoPendingRequests);
|
||||
return;
|
||||
}
|
||||
|
||||
Page.Navigation.PushModalAsync(new NavigationPage(new LoginPasswordlessRequestsListPage())).FireAndForget();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
HandleException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private bool IncludeLinksWithSubscriptionInfo()
|
||||
{
|
||||
if (Device.RuntimePlatform == Device.iOS)
|
||||
@@ -831,13 +791,5 @@ namespace Bit.App.Pages
|
||||
await Page.DisplayAlert(AppResources.AnErrorHasOccurred, AppResources.GenericErrorMessage, AppResources.Ok);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task ToggleWatchConnectionAsync()
|
||||
{
|
||||
_shouldConnectToWatch = !_shouldConnectToWatch;
|
||||
|
||||
await _watchDeviceService.SetShouldConnectToWatchAsync(_shouldConnectToWatch);
|
||||
BuildList();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,7 +96,7 @@
|
||||
Margin="0"
|
||||
HasShadow="False"
|
||||
BackgroundColor="Transparent"
|
||||
BorderColor="{DynamicResource PrimaryColor}">
|
||||
BorderColor="Accent">
|
||||
<Label
|
||||
Text="{u:I18n PersonalOwnershipPolicyInEffect}"
|
||||
StyleClass="text-muted, text-sm, text-bold"
|
||||
|
||||
@@ -263,6 +263,12 @@ namespace Bit.App.Pages
|
||||
{
|
||||
if (DoOnce())
|
||||
{
|
||||
var cameraPermission = await PermissionManager.CheckAndRequestPermissionAsync(new Permissions.Camera());
|
||||
if (cameraPermission != PermissionStatus.Granted)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var page = new ScanPage(key =>
|
||||
{
|
||||
Device.BeginInvokeOnMainThread(async () =>
|
||||
@@ -271,7 +277,6 @@ namespace Bit.App.Pages
|
||||
await _vm.UpdateTotpKeyAsync(key);
|
||||
});
|
||||
});
|
||||
|
||||
await Navigation.PushModalAsync(new Xamarin.Forms.NavigationPage(page));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,7 +29,6 @@ namespace Bit.App.Pages
|
||||
private readonly ICustomFieldItemFactory _customFieldItemFactory;
|
||||
private readonly IClipboardService _clipboardService;
|
||||
private readonly IAutofillHandler _autofillHandler;
|
||||
private readonly IWatchDeviceService _watchDeviceService;
|
||||
|
||||
private bool _showNotesSeparator;
|
||||
private bool _showPassword;
|
||||
@@ -81,7 +80,6 @@ namespace Bit.App.Pages
|
||||
_customFieldItemFactory = ServiceContainer.Resolve<ICustomFieldItemFactory>("customFieldItemFactory");
|
||||
_clipboardService = ServiceContainer.Resolve<IClipboardService>("clipboardService");
|
||||
_autofillHandler = ServiceContainer.Resolve<IAutofillHandler>();
|
||||
_watchDeviceService = ServiceContainer.Resolve<IWatchDeviceService>();
|
||||
|
||||
GeneratePasswordCommand = new Command(GeneratePassword);
|
||||
TogglePasswordCommand = new Command(TogglePassword);
|
||||
@@ -139,7 +137,6 @@ namespace Bit.App.Pages
|
||||
new KeyValuePair<string, string>(AppResources.Mr, AppResources.Mr),
|
||||
new KeyValuePair<string, string>(AppResources.Mrs, AppResources.Mrs),
|
||||
new KeyValuePair<string, string>(AppResources.Ms, AppResources.Ms),
|
||||
new KeyValuePair<string, string>(AppResources.Mx, AppResources.Mx),
|
||||
new KeyValuePair<string, string>(AppResources.Dr, AppResources.Dr),
|
||||
};
|
||||
FolderOptions = new List<KeyValuePair<string, string>>();
|
||||
@@ -510,8 +507,6 @@ namespace Bit.App.Pages
|
||||
EditMode && !CloneMode ? AppResources.ItemUpdated : AppResources.NewItemCreated);
|
||||
_messagingService.Send(EditMode && !CloneMode ? "editedCipher" : "addedCipher", Cipher.Id);
|
||||
|
||||
_watchDeviceService.SyncDataToWatchAsync().FireAndForget();
|
||||
|
||||
if (Page is CipherAddEditPage page && page.FromAutofillFramework)
|
||||
{
|
||||
// Close and go back to app
|
||||
|
||||
@@ -31,7 +31,6 @@ namespace Bit.App.Pages
|
||||
private readonly ILocalizeService _localizeService;
|
||||
private readonly ICustomFieldItemFactory _customFieldItemFactory;
|
||||
private readonly IClipboardService _clipboardService;
|
||||
private readonly IWatchDeviceService _watchDeviceService;
|
||||
|
||||
private List<ICustomFieldItemViewModel> _fields;
|
||||
private bool _canAccessPremium;
|
||||
@@ -63,7 +62,6 @@ namespace Bit.App.Pages
|
||||
_localizeService = ServiceContainer.Resolve<ILocalizeService>("localizeService");
|
||||
_customFieldItemFactory = ServiceContainer.Resolve<ICustomFieldItemFactory>("customFieldItemFactory");
|
||||
_clipboardService = ServiceContainer.Resolve<IClipboardService>("clipboardService");
|
||||
_watchDeviceService = ServiceContainer.Resolve<IWatchDeviceService>();
|
||||
|
||||
CopyCommand = new AsyncCommand<string>((id) => CopyAsync(id, null), onException: ex => _logger.Exception(ex), allowsMultipleExecutions: false);
|
||||
CopyUriCommand = new AsyncCommand<LoginUriView>(uriView => CopyAsync("LoginUri", uriView.Uri), onException: ex => _logger.Exception(ex), allowsMultipleExecutions: false);
|
||||
@@ -373,9 +371,6 @@ namespace Bit.App.Pages
|
||||
await _cipherService.SoftDeleteWithServerAsync(Cipher.Id);
|
||||
}
|
||||
await _deviceActionService.HideLoadingAsync();
|
||||
|
||||
_watchDeviceService.SyncDataToWatchAsync().FireAndForget();
|
||||
|
||||
_platformUtilsService.ShowToast("success", null,
|
||||
Cipher.IsDeleted ? AppResources.ItemDeleted : AppResources.ItemSoftDeleted);
|
||||
_messagingService.Send(Cipher.IsDeleted ? "deletedCipher" : "softDeletedCipher", Cipher);
|
||||
|
||||
@@ -108,10 +108,6 @@ namespace Bit.App.Pages
|
||||
else if (message.Command == "syncCompleted")
|
||||
{
|
||||
await Task.Delay(500);
|
||||
if (_vm.MainPage)
|
||||
{
|
||||
_vm.AvatarImageSource = await GetAvatarImageSourceAsync();
|
||||
}
|
||||
Device.BeginInvokeOnMainThread(() =>
|
||||
{
|
||||
IsBusy = false;
|
||||
|
||||
@@ -34,13 +34,16 @@
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
<ContentView
|
||||
x:Name="_scannerContainer"
|
||||
<zxing:ZXingScannerView
|
||||
x:Name="_zxing"
|
||||
HorizontalOptions="FillAndExpand"
|
||||
VerticalOptions="FillAndExpand"
|
||||
AutomationId="zxingScannerView"
|
||||
IsVisible="{Binding ShowScanner}"
|
||||
Grid.Column="0"
|
||||
Grid.Row="0"
|
||||
Grid.RowSpan="3"/>
|
||||
Grid.RowSpan="3"
|
||||
OnScanResult="OnScanResult"/>
|
||||
<StackLayout
|
||||
VerticalOptions="Center"
|
||||
HorizontalOptions="FillAndExpand"
|
||||
|
||||
@@ -8,10 +8,8 @@ using Bit.Core.Abstractions;
|
||||
using Bit.Core.Utilities;
|
||||
using SkiaSharp;
|
||||
using SkiaSharp.Views.Forms;
|
||||
using Xamarin.CommunityToolkit.ObjectModel;
|
||||
using Xamarin.Essentials;
|
||||
using Xamarin.Forms;
|
||||
using ZXing.Net.Mobile.Forms;
|
||||
|
||||
namespace Bit.App.Pages
|
||||
{
|
||||
@@ -28,15 +26,20 @@ namespace Bit.App.Pages
|
||||
private bool _pageIsActive;
|
||||
private bool _qrcodeFound;
|
||||
private float _scale;
|
||||
private ZXingScannerView _zxing;
|
||||
|
||||
private readonly LazyResolve<ILogger> _logger = new LazyResolve<ILogger>("logger");
|
||||
|
||||
public ScanPage(Action<string> callback)
|
||||
{
|
||||
InitializeComponent();
|
||||
_callback = callback;
|
||||
ViewModel.InitScannerCommand = new Command(() => InitScanner());
|
||||
|
||||
InitializeComponent();
|
||||
_zxing.Options = new ZXing.Mobile.MobileBarcodeScanningOptions
|
||||
{
|
||||
UseNativeScanning = true,
|
||||
PossibleFormats = new List<ZXing.BarcodeFormat> { ZXing.BarcodeFormat.QR_CODE },
|
||||
AutoRotate = false,
|
||||
TryInverted = true
|
||||
};
|
||||
if (Device.RuntimePlatform == Device.Android)
|
||||
{
|
||||
ToolbarItems.RemoveAt(0);
|
||||
@@ -52,53 +55,6 @@ namespace Bit.App.Pages
|
||||
protected override void OnAppearing()
|
||||
{
|
||||
base.OnAppearing();
|
||||
StartScanner();
|
||||
}
|
||||
|
||||
protected override void OnDisappearing()
|
||||
{
|
||||
StopScanner().FireAndForget();
|
||||
base.OnDisappearing();
|
||||
}
|
||||
|
||||
// Fix known bug with DelayBetweenAnalyzingFrames & DelayBetweenContinuousScans: https://github.com/Redth/ZXing.Net.Mobile/issues/721
|
||||
private void InitScanner()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!ViewModel.HasCameraPermission || !ViewModel.ShowScanner || _zxing != null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_zxing = new ZXingScannerView();
|
||||
_zxing.Options = new ZXing.Mobile.MobileBarcodeScanningOptions
|
||||
{
|
||||
UseNativeScanning = true,
|
||||
PossibleFormats = new List<ZXing.BarcodeFormat> { ZXing.BarcodeFormat.QR_CODE },
|
||||
AutoRotate = false,
|
||||
TryInverted = true,
|
||||
DelayBetweenAnalyzingFrames = 5,
|
||||
DelayBetweenContinuousScans = 5
|
||||
};
|
||||
_scannerContainer.Content = _zxing;
|
||||
StartScanner();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Value.Exception(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private void StartScanner()
|
||||
{
|
||||
if (_zxing == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_zxing.OnScanResult -= OnScanResult;
|
||||
_zxing.OnScanResult += OnScanResult;
|
||||
_zxing.IsScanning = true;
|
||||
|
||||
// Fix for Autofocus, now it's done every 2 seconds so that the user does't have to do it
|
||||
@@ -142,21 +98,16 @@ namespace Bit.App.Pages
|
||||
AnimationLoopAsync();
|
||||
}
|
||||
|
||||
private async Task StopScanner()
|
||||
protected override async void OnDisappearing()
|
||||
{
|
||||
if (_zxing == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_autofocusCts?.Cancel();
|
||||
if (_continuousAutofocusTask != null)
|
||||
{
|
||||
await _continuousAutofocusTask;
|
||||
}
|
||||
_zxing.IsScanning = false;
|
||||
_zxing.OnScanResult -= OnScanResult;
|
||||
_pageIsActive = false;
|
||||
base.OnDisappearing();
|
||||
}
|
||||
|
||||
private async void OnScanResult(ZXing.Result result)
|
||||
|
||||
@@ -1,14 +1,6 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Input;
|
||||
using Bit.App.Abstractions;
|
||||
using Bit.App.Resources;
|
||||
using Bit.App.Resources;
|
||||
using Bit.App.Utilities;
|
||||
using Bit.Core.Abstractions;
|
||||
using Bit.Core.Services;
|
||||
using Bit.Core.Utilities;
|
||||
using Xamarin.CommunityToolkit.ObjectModel;
|
||||
using Xamarin.Essentials;
|
||||
using Xamarin.Forms;
|
||||
|
||||
namespace Bit.App.Pages
|
||||
@@ -17,46 +9,13 @@ namespace Bit.App.Pages
|
||||
{
|
||||
private bool _showScanner = true;
|
||||
private string _totpAuthenticationKey;
|
||||
private IPlatformUtilsService _platformUtilsService;
|
||||
private IDeviceActionService _deviceActionService;
|
||||
private ILogger _logger;
|
||||
|
||||
public ScanPageViewModel()
|
||||
{
|
||||
ToggleScanModeCommand = new AsyncCommand(ToggleScanMode, onException: HandleException);
|
||||
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
||||
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
||||
_logger = ServiceContainer.Resolve<ILogger>();
|
||||
InitAsync().FireAndForget();
|
||||
ToggleScanModeCommand = new Command(() => ShowScanner = !ShowScanner);
|
||||
}
|
||||
|
||||
public async Task InitAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
await Device.InvokeOnMainThreadAsync(async () =>
|
||||
{
|
||||
var hasCameraPermission = await PermissionManager.CheckAndRequestPermissionAsync(new Permissions.Camera());
|
||||
HasCameraPermission = hasCameraPermission == PermissionStatus.Granted;
|
||||
ShowScanner = hasCameraPermission == PermissionStatus.Granted;
|
||||
});
|
||||
|
||||
if (!HasCameraPermission)
|
||||
{
|
||||
return;
|
||||
}
|
||||
InitScannerCommand.Execute(null);
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
HandleException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public ICommand ToggleScanModeCommand { get; set; }
|
||||
public ICommand InitScannerCommand { get; set; }
|
||||
|
||||
public bool HasCameraPermission { get; set; }
|
||||
public Command ToggleScanModeCommand { get; set; }
|
||||
public string ScanQrPageTitle => ShowScanner ? AppResources.ScanQrTitle : AppResources.AuthenticatorKeyScanner;
|
||||
public string CameraInstructionTop => ShowScanner ? AppResources.PointYourCameraAtTheQRCode : AppResources.OnceTheKeyIsSuccessfullyEntered;
|
||||
public string TotpAuthenticationKey
|
||||
@@ -80,23 +39,6 @@ namespace Bit.App.Pages
|
||||
});
|
||||
}
|
||||
|
||||
private async Task ToggleScanMode()
|
||||
{
|
||||
var cameraPermission = await PermissionManager.CheckAndRequestPermissionAsync(new Permissions.Camera());
|
||||
HasCameraPermission = cameraPermission == PermissionStatus.Granted;
|
||||
if (!HasCameraPermission)
|
||||
{
|
||||
var openAppSettingsResult = await _platformUtilsService.ShowDialogAsync(AppResources.EnableCamerPermissionToUseTheScanner, title: string.Empty, confirmText: AppResources.Settings, cancelText: AppResources.NoThanks);
|
||||
if (openAppSettingsResult)
|
||||
{
|
||||
_deviceActionService.OpenAppSettings();
|
||||
}
|
||||
return;
|
||||
}
|
||||
ShowScanner = !ShowScanner;
|
||||
InitScannerCommand.Execute(null);
|
||||
}
|
||||
|
||||
public FormattedString ToggleScanModeLabel
|
||||
{
|
||||
get
|
||||
@@ -115,15 +57,5 @@ namespace Bit.App.Pages
|
||||
return fs;
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleException(Exception ex)
|
||||
{
|
||||
Xamarin.Essentials.MainThread.InvokeOnMainThreadAsync(async () =>
|
||||
{
|
||||
await _deviceActionService.HideLoadingAsync();
|
||||
await _platformUtilsService.ShowDialogAsync(AppResources.GenericErrorMessage);
|
||||
}).FireAndForget();
|
||||
_logger.Exception(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
278
src/App/Resources/AppResources.Designer.cs
generated
278
src/App/Resources/AppResources.Designer.cs
generated
@@ -481,15 +481,6 @@ namespace Bit.App.Resources {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to A notification has been sent to your device..
|
||||
/// </summary>
|
||||
public static string ANotificationHasBeenSentToYourDevice {
|
||||
get {
|
||||
return ResourceManager.GetString("ANotificationHasBeenSentToYourDevice", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to API access token.
|
||||
/// </summary>
|
||||
@@ -562,15 +553,6 @@ namespace Bit.App.Resources {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Are you sure you want to decline all pending login requests?.
|
||||
/// </summary>
|
||||
public static string AreYouSureYouWantToDeclineAllPendingLogInRequests {
|
||||
get {
|
||||
return ResourceManager.GetString("AreYouSureYouWantToDeclineAllPendingLogInRequests", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Are you sure you want to turn on screen capture?.
|
||||
/// </summary>
|
||||
@@ -1381,15 +1363,6 @@ namespace Bit.App.Resources {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Check known data breaches for this password.
|
||||
/// </summary>
|
||||
public static string CheckKnownDataBreachesForThisPassword {
|
||||
get {
|
||||
return ResourceManager.GetString("CheckKnownDataBreachesForThisPassword", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Check if password has been exposed..
|
||||
/// </summary>
|
||||
@@ -1516,15 +1489,6 @@ namespace Bit.App.Resources {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Connect to Watch.
|
||||
/// </summary>
|
||||
public static string ConnectToWatch {
|
||||
get {
|
||||
return ResourceManager.GetString("ConnectToWatch", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Continue.
|
||||
/// </summary>
|
||||
@@ -1768,15 +1732,6 @@ namespace Bit.App.Resources {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Decline all requests.
|
||||
/// </summary>
|
||||
public static string DeclineAllRequests {
|
||||
get {
|
||||
return ResourceManager.GetString("DeclineAllRequests", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Default.
|
||||
/// </summary>
|
||||
@@ -2065,15 +2020,6 @@ namespace Bit.App.Resources {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to DuckDuckGo.
|
||||
/// </summary>
|
||||
public static string DuckDuckGo {
|
||||
get {
|
||||
return ResourceManager.GetString("DuckDuckGo", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Edit.
|
||||
/// </summary>
|
||||
@@ -2173,15 +2119,6 @@ namespace Bit.App.Resources {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Enable camera permission to use the scanner.
|
||||
/// </summary>
|
||||
public static string EnableCamerPermissionToUseTheScanner {
|
||||
get {
|
||||
return ResourceManager.GetString("EnableCamerPermissionToUseTheScanner", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Enabled.
|
||||
/// </summary>
|
||||
@@ -2452,15 +2389,6 @@ namespace Bit.App.Resources {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Exposed Master Password.
|
||||
/// </summary>
|
||||
public static string ExposedMasterPassword {
|
||||
get {
|
||||
return ResourceManager.GetString("ExposedMasterPassword", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Extension activated!.
|
||||
/// </summary>
|
||||
@@ -2569,15 +2497,6 @@ namespace Bit.App.Resources {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Fastmail.
|
||||
/// </summary>
|
||||
public static string Fastmail {
|
||||
get {
|
||||
return ResourceManager.GetString("Fastmail", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Favorite.
|
||||
/// </summary>
|
||||
@@ -2992,15 +2911,6 @@ namespace Bit.App.Resources {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Good.
|
||||
/// </summary>
|
||||
public static string Good {
|
||||
get {
|
||||
return ResourceManager.GetString("Good", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Go to my vault.
|
||||
/// </summary>
|
||||
@@ -3136,15 +3046,6 @@ namespace Bit.App.Resources {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Important.
|
||||
/// </summary>
|
||||
public static string Important {
|
||||
get {
|
||||
return ResourceManager.GetString("Important", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Import items.
|
||||
/// </summary>
|
||||
@@ -3579,15 +3480,6 @@ namespace Bit.App.Resources {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Log in initiated.
|
||||
/// </summary>
|
||||
public static string LogInInitiated {
|
||||
get {
|
||||
return ResourceManager.GetString("LogInInitiated", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Login.
|
||||
/// </summary>
|
||||
@@ -3634,7 +3526,7 @@ namespace Bit.App.Resources {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Enterprise single sign-on.
|
||||
/// Looks up a localized string similar to Enterprise Single Sign-On.
|
||||
/// </summary>
|
||||
public static string LogInSso {
|
||||
get {
|
||||
@@ -3679,7 +3571,7 @@ namespace Bit.App.Resources {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Log in with master password.
|
||||
/// Looks up a localized string similar to Log In with master password.
|
||||
/// </summary>
|
||||
public static string LogInWithMasterPassword {
|
||||
get {
|
||||
@@ -4019,18 +3911,7 @@ namespace Bit.App.Resources {
|
||||
return ResourceManager.GetString("Ms", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Mx.
|
||||
/// </summary>
|
||||
public static string Mx
|
||||
{
|
||||
get
|
||||
{
|
||||
return ResourceManager.GetString("Mx", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to You must log into the main Bitwarden app before you can use the extension..
|
||||
/// </summary>
|
||||
@@ -4085,15 +3966,6 @@ namespace Bit.App.Resources {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Need another option?.
|
||||
/// </summary>
|
||||
public static string NeedAnotherOption {
|
||||
get {
|
||||
return ResourceManager.GetString("NeedAnotherOption", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Never.
|
||||
/// </summary>
|
||||
@@ -4301,15 +4173,6 @@ namespace Bit.App.Resources {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to No pending requests.
|
||||
/// </summary>
|
||||
public static string NoPendingRequests {
|
||||
get {
|
||||
return ResourceManager.GetString("NoPendingRequests", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Nord.
|
||||
/// </summary>
|
||||
@@ -4662,15 +4525,6 @@ namespace Bit.App.Resources {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Password found in a data breach. Use a unique password to protect your account. Are you sure you want to use an exposed password?.
|
||||
/// </summary>
|
||||
public static string PasswordFoundInADataBreachAlertDescription {
|
||||
get {
|
||||
return ResourceManager.GetString("PasswordFoundInADataBreachAlertDescription", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Password generated.
|
||||
/// </summary>
|
||||
@@ -4797,15 +4651,6 @@ namespace Bit.App.Resources {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Pending login requests.
|
||||
/// </summary>
|
||||
public static string PendingLogInRequests {
|
||||
get {
|
||||
return ResourceManager.GetString("PendingLogInRequests", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to An organization policy is affecting your ownership options..
|
||||
/// </summary>
|
||||
@@ -4860,15 +4705,6 @@ namespace Bit.App.Resources {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Please make sure your vault is unlocked and the Fingerprint phrase matches on the other device..
|
||||
/// </summary>
|
||||
public static string PleaseMakeSureYourVaultIsUnlockedAndTheFingerprintPhraseMatchesOnTheOtherDevice {
|
||||
get {
|
||||
return ResourceManager.GetString("PleaseMakeSureYourVaultIsUnlockedAndTheFingerprintPhraseMatchesOnTheOtherDevice", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Plus addressed email.
|
||||
/// </summary>
|
||||
@@ -5158,15 +4994,6 @@ namespace Bit.App.Resources {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Requests declined.
|
||||
/// </summary>
|
||||
public static string RequestsDeclined {
|
||||
get {
|
||||
return ResourceManager.GetString("RequestsDeclined", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Resend code.
|
||||
/// </summary>
|
||||
@@ -5176,15 +5003,6 @@ namespace Bit.App.Resources {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Resend notification.
|
||||
/// </summary>
|
||||
public static string ResendNotification {
|
||||
get {
|
||||
return ResourceManager.GetString("ResendNotification", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to This organization has an enterprise policy that will automatically enroll you in password reset. Enrollment will allow organization administrators to change your master password..
|
||||
/// </summary>
|
||||
@@ -5374,6 +5192,15 @@ namespace Bit.App.Resources {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to .
|
||||
/// </summary>
|
||||
public static string SelectAddTotpToStoreTheKeySafely {
|
||||
get {
|
||||
return ResourceManager.GetString("SelectAddTotpToStoreTheKeySafely", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to You must select at least one collection..
|
||||
/// </summary>
|
||||
@@ -5788,15 +5615,6 @@ namespace Bit.App.Resources {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Strong.
|
||||
/// </summary>
|
||||
public static string Strong {
|
||||
get {
|
||||
return ResourceManager.GetString("Strong", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Submit.
|
||||
/// </summary>
|
||||
@@ -6013,15 +5831,6 @@ namespace Bit.App.Resources {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to This request is no longer valid.
|
||||
/// </summary>
|
||||
public static string ThisRequestIsNoLongerValid {
|
||||
get {
|
||||
return ResourceManager.GetString("ThisRequestIsNoLongerValid", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to 3 days.
|
||||
/// </summary>
|
||||
@@ -6733,15 +6542,6 @@ namespace Bit.App.Resources {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to View all log in options.
|
||||
/// </summary>
|
||||
public static string ViewAllLoginOptions {
|
||||
get {
|
||||
return ResourceManager.GetString("ViewAllLoginOptions", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to View item.
|
||||
/// </summary>
|
||||
@@ -6778,51 +6578,6 @@ namespace Bit.App.Resources {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Weak.
|
||||
/// </summary>
|
||||
public static string Weak {
|
||||
get {
|
||||
return ResourceManager.GetString("Weak", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Weak and Exposed Master Password.
|
||||
/// </summary>
|
||||
public static string WeakAndExposedMasterPassword {
|
||||
get {
|
||||
return ResourceManager.GetString("WeakAndExposedMasterPassword", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Weak Master Password.
|
||||
/// </summary>
|
||||
public static string WeakMasterPassword {
|
||||
get {
|
||||
return ResourceManager.GetString("WeakMasterPassword", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Weak password identified and found in a data breach. Use a strong and unique password to protect your account. Are you sure you want to use this password?.
|
||||
/// </summary>
|
||||
public static string WeakPasswordIdentifiedAndFoundInADataBreachAlertDescription {
|
||||
get {
|
||||
return ResourceManager.GetString("WeakPasswordIdentifiedAndFoundInADataBreachAlertDescription", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Weak password identified. Use a strong password to protect your account. Are you sure you want to use a weak password?.
|
||||
/// </summary>
|
||||
public static string WeakPasswordIdentifiedUseAStrongPasswordToProtectYourAccount {
|
||||
get {
|
||||
return ResourceManager.GetString("WeakPasswordIdentifiedUseAStrongPasswordToProtectYourAccount", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Website.
|
||||
/// </summary>
|
||||
@@ -6940,15 +6695,6 @@ namespace Bit.App.Resources {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Your master password cannot be recovered if you forget it! {0} characters minimum..
|
||||
/// </summary>
|
||||
public static string YourMasterPasswordCannotBeRecoveredIfYouForgetItXCharactersMinimum {
|
||||
get {
|
||||
return ResourceManager.GetString("YourMasterPasswordCannotBeRecoveredIfYouForgetItXCharactersMinimum", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to To continue, hold your YubiKey NEO against the back of the device or insert your YubiKey into your device's USB port, then touch its button..
|
||||
/// </summary>
|
||||
|
||||
@@ -376,7 +376,7 @@
|
||||
</data>
|
||||
<data name="ValueHasBeenCopied" xml:space="preserve">
|
||||
<value>{0} is gekopieer.</value>
|
||||
<comment>Confirmation message after successfully copying a value to the clipboard.</comment>
|
||||
<comment>Confirmation message after suceessfully copying a value to the clipboard.</comment>
|
||||
</data>
|
||||
<data name="VerifyFingerprint" xml:space="preserve">
|
||||
<value>Bevestig vingerafdruk</value>
|
||||
@@ -1103,9 +1103,6 @@ Skandering gebeur outomaties.</value>
|
||||
<data name="Ms" xml:space="preserve">
|
||||
<value>Mej.</value>
|
||||
</data>
|
||||
<data name="Mx" xml:space="preserve">
|
||||
<value>Mx</value>
|
||||
</data>
|
||||
<data name="November" xml:space="preserve">
|
||||
<value>November</value>
|
||||
</data>
|
||||
@@ -1579,7 +1576,7 @@ Skandering gebeur outomaties.</value>
|
||||
<comment>'Nord' is the name of a specific color scheme. It should not be translated.</comment>
|
||||
</data>
|
||||
<data name="SolarizedDark" xml:space="preserve">
|
||||
<value>'Solarized' Donker</value>
|
||||
<value>Solarized Dark</value>
|
||||
<comment>'Solarized Dark' is the name of a specific color scheme. It should not be translated.</comment>
|
||||
</data>
|
||||
<data name="AutofillBlockedUris" xml:space="preserve">
|
||||
@@ -2302,6 +2299,9 @@ Skandering gebeur outomaties.</value>
|
||||
<value>Sodra u die sleutel reg ingevoer het,
|
||||
kies u Voeg TOTP toe om die sleutel veilig te bewaar</value>
|
||||
</data>
|
||||
<data name="SelectAddTotpToStoreTheKeySafely" xml:space="preserve">
|
||||
<value></value>
|
||||
</data>
|
||||
<data name="NeverLockWarning" xml:space="preserve">
|
||||
<value>Deur u vergrendelopsies na “Nooit” te stel, is u kluis beskikbaar aan enigeen met toegang tot u toestel. Indien u hierdie opsie gebruik moet u seker maak dat u u toestel voldoende beskerm.</value>
|
||||
</data>
|
||||
@@ -2422,14 +2422,6 @@ kies u Voeg TOTP toe om die sleutel veilig te bewaar</value>
|
||||
<value>SimpleLogin</value>
|
||||
<comment>"SimpleLogin" is the product name and should not be translated.</comment>
|
||||
</data>
|
||||
<data name="DuckDuckGo" xml:space="preserve">
|
||||
<value>DuckDuckGo</value>
|
||||
<comment>"DuckDuckGo" is the product name and should not be translated.</comment>
|
||||
</data>
|
||||
<data name="Fastmail" xml:space="preserve">
|
||||
<value>Fastmail</value>
|
||||
<comment>"Fastmail" is the product name and should not be translated.</comment>
|
||||
</data>
|
||||
<data name="APIAccessToken" xml:space="preserve">
|
||||
<value>API-toegangsteken</value>
|
||||
</data>
|
||||
@@ -2460,9 +2452,6 @@ kies u Voeg TOTP toe om die sleutel veilig te bewaar</value>
|
||||
<data name="Random" xml:space="preserve">
|
||||
<value>Lukraak</value>
|
||||
</data>
|
||||
<data name="ConnectToWatch" xml:space="preserve">
|
||||
<value>Koppel aan horlosie</value>
|
||||
</data>
|
||||
<data name="AccessibilityServiceDisclosure" xml:space="preserve">
|
||||
<value>Toeganklikheidsdiensopenbaarmaking</value>
|
||||
</data>
|
||||
@@ -2479,101 +2468,8 @@ kies u Voeg TOTP toe om die sleutel veilig te bewaar</value>
|
||||
<value>Aantekenversoek het reeds verstryk.</value>
|
||||
</data>
|
||||
<data name="LoginAttemptFromXDoYouWantToSwitchToThisAccount" xml:space="preserve">
|
||||
<value>Teken Aan probeerslag van:
|
||||
<value>Login attempt from:
|
||||
{0}
|
||||
Wil u na die rekening omskakel?</value>
|
||||
</data>
|
||||
<data name="NewAroundHere" xml:space="preserve">
|
||||
<value>Nuut hier?</value>
|
||||
</data>
|
||||
<data name="GetMasterPasswordwordHint" xml:space="preserve">
|
||||
<value>Kry hoofwagwoord wenk</value>
|
||||
</data>
|
||||
<data name="LoggingInAsX" xml:space="preserve">
|
||||
<value>Teken in as {0}</value>
|
||||
</data>
|
||||
<data name="NotYou" xml:space="preserve">
|
||||
<value>Nie jy nie?</value>
|
||||
</data>
|
||||
<data name="LogInWithMasterPassword" xml:space="preserve">
|
||||
<value>Teken aan met hoofwagwoord</value>
|
||||
</data>
|
||||
<data name="LogInWithAnotherDevice" xml:space="preserve">
|
||||
<value>Teken Aan met 'n ander toestel</value>
|
||||
</data>
|
||||
<data name="LogInInitiated" xml:space="preserve">
|
||||
<value>Aanmelding begin</value>
|
||||
</data>
|
||||
<data name="ANotificationHasBeenSentToYourDevice" xml:space="preserve">
|
||||
<value>'n Kennisgewing was gestuur na u toestel.</value>
|
||||
</data>
|
||||
<data name="PleaseMakeSureYourVaultIsUnlockedAndTheFingerprintPhraseMatchesOnTheOtherDevice" xml:space="preserve">
|
||||
<value>Asseblief maak seker jou kluis is oopgesluit en die vingerafdruk frase stem ooreen op die ander toestel.</value>
|
||||
</data>
|
||||
<data name="ResendNotification" xml:space="preserve">
|
||||
<value>Stuur kennisgewing weer</value>
|
||||
</data>
|
||||
<data name="NeedAnotherOption" xml:space="preserve">
|
||||
<value>Is daar nog 'n opsie nodig?</value>
|
||||
</data>
|
||||
<data name="ViewAllLoginOptions" xml:space="preserve">
|
||||
<value>Wys alle aanmeldings opsies</value>
|
||||
</data>
|
||||
<data name="ThisRequestIsNoLongerValid" xml:space="preserve">
|
||||
<value>Hierdie versoek is nie langer gelding nie</value>
|
||||
</data>
|
||||
<data name="PendingLogInRequests" xml:space="preserve">
|
||||
<value>Pending login requests</value>
|
||||
</data>
|
||||
<data name="DeclineAllRequests" xml:space="preserve">
|
||||
<value>Decline all requests</value>
|
||||
</data>
|
||||
<data name="AreYouSureYouWantToDeclineAllPendingLogInRequests" xml:space="preserve">
|
||||
<value>Are you sure you want to decline all pending login requests?</value>
|
||||
</data>
|
||||
<data name="RequestsDeclined" xml:space="preserve">
|
||||
<value>Requests declined</value>
|
||||
</data>
|
||||
<data name="NoPendingRequests" xml:space="preserve">
|
||||
<value>No pending requests</value>
|
||||
</data>
|
||||
<data name="EnableCamerPermissionToUseTheScanner" xml:space="preserve">
|
||||
<value>Laat die kamera versoek toe om die skandeerder te gebruik</value>
|
||||
</data>
|
||||
<data name="Important" xml:space="preserve">
|
||||
<value>Important</value>
|
||||
</data>
|
||||
<data name="YourMasterPasswordCannotBeRecoveredIfYouForgetItXCharactersMinimum" xml:space="preserve">
|
||||
<value>Your master password cannot be recovered if you forget it! {0} characters minimum.</value>
|
||||
</data>
|
||||
<data name="WeakMasterPassword" xml:space="preserve">
|
||||
<value>Swak Hoofwagwoord</value>
|
||||
</data>
|
||||
<data name="WeakPasswordIdentifiedUseAStrongPasswordToProtectYourAccount" xml:space="preserve">
|
||||
<value>Weak password identified. Use a strong password to protect your account. Are you sure you want to use a weak password?</value>
|
||||
</data>
|
||||
<data name="Weak" xml:space="preserve">
|
||||
<value>Swak</value>
|
||||
</data>
|
||||
<data name="Good" xml:space="preserve">
|
||||
<value>Good</value>
|
||||
</data>
|
||||
<data name="Strong" xml:space="preserve">
|
||||
<value>Strong</value>
|
||||
</data>
|
||||
<data name="CheckKnownDataBreachesForThisPassword" xml:space="preserve">
|
||||
<value>Check known data breaches for this password</value>
|
||||
</data>
|
||||
<data name="ExposedMasterPassword" xml:space="preserve">
|
||||
<value>Exposed Master Password</value>
|
||||
</data>
|
||||
<data name="PasswordFoundInADataBreachAlertDescription" xml:space="preserve">
|
||||
<value>Password found in a data breach. Use a unique password to protect your account. Are you sure you want to use an exposed password?</value>
|
||||
</data>
|
||||
<data name="WeakAndExposedMasterPassword" xml:space="preserve">
|
||||
<value>Weak and Exposed Master Password</value>
|
||||
</data>
|
||||
<data name="WeakPasswordIdentifiedAndFoundInADataBreachAlertDescription" xml:space="preserve">
|
||||
<value>Weak password identified and found in a data breach. Use a strong and unique password to protect your account. Are you sure you want to use this password?</value>
|
||||
Do you want to switch to this account?</value>
|
||||
</data>
|
||||
</root>
|
||||
|
||||
@@ -118,17 +118,17 @@
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<data name="About" xml:space="preserve">
|
||||
<value>عن التطبيق</value>
|
||||
<value>حول</value>
|
||||
</data>
|
||||
<data name="Add" xml:space="preserve">
|
||||
<value>أضِف</value>
|
||||
<value>إضافة</value>
|
||||
<comment>Add/create a new entity (verb).</comment>
|
||||
</data>
|
||||
<data name="AddFolder" xml:space="preserve">
|
||||
<value>مجلد مضاف</value>
|
||||
<value>إضافة مجلد</value>
|
||||
</data>
|
||||
<data name="AddItem" xml:space="preserve">
|
||||
<value>تمت إضافة العنصر</value>
|
||||
<value>إضافة عنصر</value>
|
||||
<comment>The title for the add item page.</comment>
|
||||
</data>
|
||||
<data name="AnErrorHasOccurred" xml:space="preserve">
|
||||
@@ -323,14 +323,14 @@
|
||||
<comment>Label for a password.</comment>
|
||||
</data>
|
||||
<data name="Save" xml:space="preserve">
|
||||
<value>حفظ</value>
|
||||
<value>تسجيل</value>
|
||||
<comment>Button text for a save operation (verb).</comment>
|
||||
</data>
|
||||
<data name="Move" xml:space="preserve">
|
||||
<value>نقل</value>
|
||||
</data>
|
||||
<data name="Saving" xml:space="preserve">
|
||||
<value>حفظ...</value>
|
||||
<value>تسجيل ...</value>
|
||||
<comment>Message shown when interacting with the server</comment>
|
||||
</data>
|
||||
<data name="Settings" xml:space="preserve">
|
||||
@@ -376,7 +376,7 @@
|
||||
</data>
|
||||
<data name="ValueHasBeenCopied" xml:space="preserve">
|
||||
<value>{0} تم نسخه</value>
|
||||
<comment>Confirmation message after successfully copying a value to the clipboard.</comment>
|
||||
<comment>Confirmation message after suceessfully copying a value to the clipboard.</comment>
|
||||
</data>
|
||||
<data name="VerifyFingerprint" xml:space="preserve">
|
||||
<value>التحقق من بصمة الإصبع</value>
|
||||
@@ -1103,9 +1103,6 @@
|
||||
<data name="Ms" xml:space="preserve">
|
||||
<value>آنسة</value>
|
||||
</data>
|
||||
<data name="Mx" xml:space="preserve">
|
||||
<value>Mx</value>
|
||||
</data>
|
||||
<data name="November" xml:space="preserve">
|
||||
<value>نوفمبر</value>
|
||||
</data>
|
||||
@@ -2303,6 +2300,9 @@
|
||||
<value>بمجرد إدخال المفتاح بنجاح،
|
||||
حدد إضافة TOTP لتخزين المفتاح بأمان</value>
|
||||
</data>
|
||||
<data name="SelectAddTotpToStoreTheKeySafely" xml:space="preserve">
|
||||
<value></value>
|
||||
</data>
|
||||
<data name="NeverLockWarning" xml:space="preserve">
|
||||
<value>تعيين خيارات قفل الخاص بك إلى "مطلقا" يبقي خزنتك متاحةً لأي شخص لديه حق الوصول إلى جهازك. إذا كنت تستخدم هذا الخيار، يجب أن تتأكد من الحفاظ على حماية جهازك بشكل صحيح.</value>
|
||||
</data>
|
||||
@@ -2423,14 +2423,6 @@
|
||||
<value>SimpleLogin</value>
|
||||
<comment>"SimpleLogin" is the product name and should not be translated.</comment>
|
||||
</data>
|
||||
<data name="DuckDuckGo" xml:space="preserve">
|
||||
<value>DuckDuckGo</value>
|
||||
<comment>"DuckDuckGo" is the product name and should not be translated.</comment>
|
||||
</data>
|
||||
<data name="Fastmail" xml:space="preserve">
|
||||
<value>Fastmail</value>
|
||||
<comment>"Fastmail" is the product name and should not be translated.</comment>
|
||||
</data>
|
||||
<data name="APIAccessToken" xml:space="preserve">
|
||||
<value>رمز الوصول API</value>
|
||||
</data>
|
||||
@@ -2461,9 +2453,6 @@
|
||||
<data name="Random" xml:space="preserve">
|
||||
<value>عشوائي</value>
|
||||
</data>
|
||||
<data name="ConnectToWatch" xml:space="preserve">
|
||||
<value>متّصل بالسّاعة</value>
|
||||
</data>
|
||||
<data name="AccessibilityServiceDisclosure" xml:space="preserve">
|
||||
<value>كشف خدمة إمكانية الوصول</value>
|
||||
</data>
|
||||
@@ -2484,97 +2473,4 @@
|
||||
{0}
|
||||
هل تريد التبديل إلى هذا الحساب؟</value>
|
||||
</data>
|
||||
<data name="NewAroundHere" xml:space="preserve">
|
||||
<value>جديد هنا؟</value>
|
||||
</data>
|
||||
<data name="GetMasterPasswordwordHint" xml:space="preserve">
|
||||
<value>احصل على تلميح كلمة المرور الرئيسية</value>
|
||||
</data>
|
||||
<data name="LoggingInAsX" xml:space="preserve">
|
||||
<value>تسجيل الدخول كـ {0}</value>
|
||||
</data>
|
||||
<data name="NotYou" xml:space="preserve">
|
||||
<value>ليس أنت؟</value>
|
||||
</data>
|
||||
<data name="LogInWithMasterPassword" xml:space="preserve">
|
||||
<value>تسجيل الدخول باستخدام كلمة المرور الرئيسية</value>
|
||||
</data>
|
||||
<data name="LogInWithAnotherDevice" xml:space="preserve">
|
||||
<value>تسجيل الدخول باستخدام جهاز آخر</value>
|
||||
</data>
|
||||
<data name="LogInInitiated" xml:space="preserve">
|
||||
<value>بدء تسجيل الدخول</value>
|
||||
</data>
|
||||
<data name="ANotificationHasBeenSentToYourDevice" xml:space="preserve">
|
||||
<value>تم إرسال إشعار إلى جهازك.</value>
|
||||
</data>
|
||||
<data name="PleaseMakeSureYourVaultIsUnlockedAndTheFingerprintPhraseMatchesOnTheOtherDevice" xml:space="preserve">
|
||||
<value>الرجاء التأكد من أن الخزنة الخاصة بك غير مقفلة وأن عبارة بصمة الإصبع تتطابق على الجهاز الآخر.</value>
|
||||
</data>
|
||||
<data name="ResendNotification" xml:space="preserve">
|
||||
<value>إعادة إرسال الإشعار</value>
|
||||
</data>
|
||||
<data name="NeedAnotherOption" xml:space="preserve">
|
||||
<value>هل تحتاج إلى خيار آخر؟</value>
|
||||
</data>
|
||||
<data name="ViewAllLoginOptions" xml:space="preserve">
|
||||
<value>عرض جميع خيارات تسجيل الدخول</value>
|
||||
</data>
|
||||
<data name="ThisRequestIsNoLongerValid" xml:space="preserve">
|
||||
<value>هذا الطلب لم يعد صالحًا</value>
|
||||
</data>
|
||||
<data name="PendingLogInRequests" xml:space="preserve">
|
||||
<value>Pending login requests</value>
|
||||
</data>
|
||||
<data name="DeclineAllRequests" xml:space="preserve">
|
||||
<value>Decline all requests</value>
|
||||
</data>
|
||||
<data name="AreYouSureYouWantToDeclineAllPendingLogInRequests" xml:space="preserve">
|
||||
<value>Are you sure you want to decline all pending login requests?</value>
|
||||
</data>
|
||||
<data name="RequestsDeclined" xml:space="preserve">
|
||||
<value>Requests declined</value>
|
||||
</data>
|
||||
<data name="NoPendingRequests" xml:space="preserve">
|
||||
<value>No pending requests</value>
|
||||
</data>
|
||||
<data name="EnableCamerPermissionToUseTheScanner" xml:space="preserve">
|
||||
<value>تمكين إذن الكاميرا لاستخدام الماسح الضوئي</value>
|
||||
</data>
|
||||
<data name="Important" xml:space="preserve">
|
||||
<value>Important</value>
|
||||
</data>
|
||||
<data name="YourMasterPasswordCannotBeRecoveredIfYouForgetItXCharactersMinimum" xml:space="preserve">
|
||||
<value>Your master password cannot be recovered if you forget it! {0} characters minimum.</value>
|
||||
</data>
|
||||
<data name="WeakMasterPassword" xml:space="preserve">
|
||||
<value>Weak Master Password</value>
|
||||
</data>
|
||||
<data name="WeakPasswordIdentifiedUseAStrongPasswordToProtectYourAccount" xml:space="preserve">
|
||||
<value>Weak password identified. Use a strong password to protect your account. Are you sure you want to use a weak password?</value>
|
||||
</data>
|
||||
<data name="Weak" xml:space="preserve">
|
||||
<value>Weak</value>
|
||||
</data>
|
||||
<data name="Good" xml:space="preserve">
|
||||
<value>Good</value>
|
||||
</data>
|
||||
<data name="Strong" xml:space="preserve">
|
||||
<value>Strong</value>
|
||||
</data>
|
||||
<data name="CheckKnownDataBreachesForThisPassword" xml:space="preserve">
|
||||
<value>Check known data breaches for this password</value>
|
||||
</data>
|
||||
<data name="ExposedMasterPassword" xml:space="preserve">
|
||||
<value>Exposed Master Password</value>
|
||||
</data>
|
||||
<data name="PasswordFoundInADataBreachAlertDescription" xml:space="preserve">
|
||||
<value>Password found in a data breach. Use a unique password to protect your account. Are you sure you want to use an exposed password?</value>
|
||||
</data>
|
||||
<data name="WeakAndExposedMasterPassword" xml:space="preserve">
|
||||
<value>Weak and Exposed Master Password</value>
|
||||
</data>
|
||||
<data name="WeakPasswordIdentifiedAndFoundInADataBreachAlertDescription" xml:space="preserve">
|
||||
<value>Weak password identified and found in a data breach. Use a strong and unique password to protect your account. Are you sure you want to use this password?</value>
|
||||
</data>
|
||||
</root>
|
||||
|
||||
@@ -376,7 +376,7 @@
|
||||
</data>
|
||||
<data name="ValueHasBeenCopied" xml:space="preserve">
|
||||
<value>{0} kopyalandı.</value>
|
||||
<comment>Confirmation message after successfully copying a value to the clipboard.</comment>
|
||||
<comment>Confirmation message after suceessfully copying a value to the clipboard.</comment>
|
||||
</data>
|
||||
<data name="VerifyFingerprint" xml:space="preserve">
|
||||
<value>Barmaq izini təsdiqlə</value>
|
||||
@@ -1103,9 +1103,6 @@ Skan prosesi avtomatik baş tutacaq.</value>
|
||||
<data name="Ms" xml:space="preserve">
|
||||
<value>Hörmətli</value>
|
||||
</data>
|
||||
<data name="Mx" xml:space="preserve">
|
||||
<value>Hörmətli</value>
|
||||
</data>
|
||||
<data name="November" xml:space="preserve">
|
||||
<value>Noyabr</value>
|
||||
</data>
|
||||
@@ -2140,7 +2137,7 @@ Skan prosesi avtomatik baş tutacaq.</value>
|
||||
<value>Bu təşkilat, sizi "parol sıfırlama"da avtomatik olaraq qeydiyyata alan müəssisə siyasətinə sahibdir. Qeydiyyat, təşkilat administratorlarına ana parolunuzu dəyişdirmə icazəsi verəcək.</value>
|
||||
</data>
|
||||
<data name="VaultTimeoutPolicyInEffect" xml:space="preserve">
|
||||
<value>Təşkilatınızın siyasətləri, anbarınızın vaxt bitişinə təsir edir. Anbar vaxt bitişi üçün icazə verilən maksimum vaxt {0} saat {1} dəqiqədir</value>
|
||||
<value>Təşkilatınızın siyasətləri, anbarınızın vaxt bitişinə təsir edir. Anbar vaxt bitişi üçün icazə verilən maksimum vaxt $HOURS$ saat $MINUTES$ dəqiqədir</value>
|
||||
</data>
|
||||
<data name="VaultTimeoutToLarge" xml:space="preserve">
|
||||
<value>Anbar vaxt bitişi, təşkilatınız tərəfindən tənzimlənən məhdudiyyətləri aşır.</value>
|
||||
@@ -2301,6 +2298,9 @@ Skan prosesi avtomatik baş tutacaq.</value>
|
||||
<data name="OnceTheKeyIsSuccessfullyEntered" xml:space="preserve">
|
||||
<value>Açar uğurla daxil edildikdən sonra, açarı güvənli şəkildə saxlamaq üçün "TOTP əlavə et"i seçin</value>
|
||||
</data>
|
||||
<data name="SelectAddTotpToStoreTheKeySafely" xml:space="preserve">
|
||||
<value></value>
|
||||
</data>
|
||||
<data name="NeverLockWarning" xml:space="preserve">
|
||||
<value>Kilid seçimlərini "Heç vaxt" olaraq tənzimləmək, anbarınızı cihazınıza müraciəti olan hər kəsə əlçatan edir. Bu seçimi istifadə etsəniz, cihazınızı düzgün qoruduğunuza əmin olmalısınız.</value>
|
||||
</data>
|
||||
@@ -2421,14 +2421,6 @@ Skan prosesi avtomatik baş tutacaq.</value>
|
||||
<value>SimpleLogin</value>
|
||||
<comment>"SimpleLogin" is the product name and should not be translated.</comment>
|
||||
</data>
|
||||
<data name="DuckDuckGo" xml:space="preserve">
|
||||
<value>DuckDuckGo</value>
|
||||
<comment>"DuckDuckGo" is the product name and should not be translated.</comment>
|
||||
</data>
|
||||
<data name="Fastmail" xml:space="preserve">
|
||||
<value>Fastmail</value>
|
||||
<comment>"Fastmail" is the product name and should not be translated.</comment>
|
||||
</data>
|
||||
<data name="APIAccessToken" xml:space="preserve">
|
||||
<value>API müraciət tokeni</value>
|
||||
</data>
|
||||
@@ -2459,9 +2451,6 @@ Skan prosesi avtomatik baş tutacaq.</value>
|
||||
<data name="Random" xml:space="preserve">
|
||||
<value>Təsadüfi</value>
|
||||
</data>
|
||||
<data name="ConnectToWatch" xml:space="preserve">
|
||||
<value>"Watch"a bağlan</value>
|
||||
</data>
|
||||
<data name="AccessibilityServiceDisclosure" xml:space="preserve">
|
||||
<value>Əlçatımlılıq Xidməti açıqlaması</value>
|
||||
</data>
|
||||
@@ -2482,97 +2471,4 @@ Skan prosesi avtomatik baş tutacaq.</value>
|
||||
{0}
|
||||
Bu hesaba keçmək istəyirsiniz?</value>
|
||||
</data>
|
||||
<data name="NewAroundHere" xml:space="preserve">
|
||||
<value>Burada yenisiniz?</value>
|
||||
</data>
|
||||
<data name="GetMasterPasswordwordHint" xml:space="preserve">
|
||||
<value>Ana parol üçün məsləhət alın</value>
|
||||
</data>
|
||||
<data name="LoggingInAsX" xml:space="preserve">
|
||||
<value>{0} olaraq giriş edilir</value>
|
||||
</data>
|
||||
<data name="NotYou" xml:space="preserve">
|
||||
<value>Siz deyilsiniz?</value>
|
||||
</data>
|
||||
<data name="LogInWithMasterPassword" xml:space="preserve">
|
||||
<value>Ana parolla giriş et</value>
|
||||
</data>
|
||||
<data name="LogInWithAnotherDevice" xml:space="preserve">
|
||||
<value>Başqa cihazla giriş et</value>
|
||||
</data>
|
||||
<data name="LogInInitiated" xml:space="preserve">
|
||||
<value>Giriş etmə başladıldı</value>
|
||||
</data>
|
||||
<data name="ANotificationHasBeenSentToYourDevice" xml:space="preserve">
|
||||
<value>Cihazınıza bir bildiriş göndərildi.</value>
|
||||
</data>
|
||||
<data name="PleaseMakeSureYourVaultIsUnlockedAndTheFingerprintPhraseMatchesOnTheOtherDevice" xml:space="preserve">
|
||||
<value>Zəhmət olmasa anbarınızın kilidinin açıq olduğuna və Barmaq izi ifadəsinin digər cihazda uyğun gəldiyinə əmin olun.</value>
|
||||
</data>
|
||||
<data name="ResendNotification" xml:space="preserve">
|
||||
<value>Bildirişi təkrar göndər</value>
|
||||
</data>
|
||||
<data name="NeedAnotherOption" xml:space="preserve">
|
||||
<value>Başqa bir seçimə ehtiyacınız var?</value>
|
||||
</data>
|
||||
<data name="ViewAllLoginOptions" xml:space="preserve">
|
||||
<value>Bütün giriş etmə seçimlərinə bax</value>
|
||||
</data>
|
||||
<data name="ThisRequestIsNoLongerValid" xml:space="preserve">
|
||||
<value>Bu tələb artıq yararsızdır</value>
|
||||
</data>
|
||||
<data name="PendingLogInRequests" xml:space="preserve">
|
||||
<value>Giriş tələbləri gözlənilir</value>
|
||||
</data>
|
||||
<data name="DeclineAllRequests" xml:space="preserve">
|
||||
<value>Bütün tələbləri rədd et</value>
|
||||
</data>
|
||||
<data name="AreYouSureYouWantToDeclineAllPendingLogInRequests" xml:space="preserve">
|
||||
<value>Gözləyən bütün giriş tələblərini rədd etmək istədiyinizə əminsiniz?</value>
|
||||
</data>
|
||||
<data name="RequestsDeclined" xml:space="preserve">
|
||||
<value>Tələblər rədd edildi</value>
|
||||
</data>
|
||||
<data name="NoPendingRequests" xml:space="preserve">
|
||||
<value>Gözləyən heç bir tələb yoxdur</value>
|
||||
</data>
|
||||
<data name="EnableCamerPermissionToUseTheScanner" xml:space="preserve">
|
||||
<value>Skaneri istifadə etmək üçün kamera icazəsini fəallaşdırın</value>
|
||||
</data>
|
||||
<data name="Important" xml:space="preserve">
|
||||
<value>Vacib</value>
|
||||
</data>
|
||||
<data name="YourMasterPasswordCannotBeRecoveredIfYouForgetItXCharactersMinimum" xml:space="preserve">
|
||||
<value>Ana parolunuzu unutsanız bərpa edə bilməzsiniz! Ən az {0} simvol.</value>
|
||||
</data>
|
||||
<data name="WeakMasterPassword" xml:space="preserve">
|
||||
<value>Zəif ana parol</value>
|
||||
</data>
|
||||
<data name="WeakPasswordIdentifiedUseAStrongPasswordToProtectYourAccount" xml:space="preserve">
|
||||
<value>Zəif parol aşkarlandı. Hesabınızı qorumaq üçün güclü bir parol istifadə edin. Zəif bir parol istifadə etmək istədiyinizə əminsiniz?</value>
|
||||
</data>
|
||||
<data name="Weak" xml:space="preserve">
|
||||
<value>Zəif</value>
|
||||
</data>
|
||||
<data name="Good" xml:space="preserve">
|
||||
<value>Yaxşı</value>
|
||||
</data>
|
||||
<data name="Strong" xml:space="preserve">
|
||||
<value>Güclü</value>
|
||||
</data>
|
||||
<data name="CheckKnownDataBreachesForThisPassword" xml:space="preserve">
|
||||
<value>Bu parol üçün bilinən məlumat pozuntularını yoxlayın</value>
|
||||
</data>
|
||||
<data name="ExposedMasterPassword" xml:space="preserve">
|
||||
<value>İfşa olunmuş ana parol</value>
|
||||
</data>
|
||||
<data name="PasswordFoundInADataBreachAlertDescription" xml:space="preserve">
|
||||
<value>Parol, məlumat pozuntusunda tapıldı. Hesabınızı qorumaq üçün unikal bir parol istifadə edin. İfşa olunmuş bir parol istifadə etmək istədiyinizə əminsiniz?</value>
|
||||
</data>
|
||||
<data name="WeakAndExposedMasterPassword" xml:space="preserve">
|
||||
<value>Zəif və ifşa olunmuş ana parol</value>
|
||||
</data>
|
||||
<data name="WeakPasswordIdentifiedAndFoundInADataBreachAlertDescription" xml:space="preserve">
|
||||
<value>Zəif parol məlumat pozuntusunda aşkarlandı və tapıldı. Hesabınızı qorumaq üçün güclü və unikal bir parol istifadə edin. Bu parolu istifadə etmək istədiyinizə əminsiniz?</value>
|
||||
</data>
|
||||
</root>
|
||||
|
||||
@@ -349,7 +349,7 @@
|
||||
<value>Адправіць</value>
|
||||
</data>
|
||||
<data name="Sync" xml:space="preserve">
|
||||
<value>Сінхранізаваць</value>
|
||||
<value>Сінхранізавана</value>
|
||||
<comment>The title for the sync page.</comment>
|
||||
</data>
|
||||
<data name="ThankYou" xml:space="preserve">
|
||||
@@ -376,7 +376,7 @@
|
||||
</data>
|
||||
<data name="ValueHasBeenCopied" xml:space="preserve">
|
||||
<value>{0} скапіяваны</value>
|
||||
<comment>Confirmation message after successfully copying a value to the clipboard.</comment>
|
||||
<comment>Confirmation message after suceessfully copying a value to the clipboard.</comment>
|
||||
</data>
|
||||
<data name="VerifyFingerprint" xml:space="preserve">
|
||||
<value>Праверка адбітка пальца</value>
|
||||
@@ -1102,9 +1102,6 @@
|
||||
<data name="Ms" xml:space="preserve">
|
||||
<value>Пані</value>
|
||||
</data>
|
||||
<data name="Mx" xml:space="preserve">
|
||||
<value>Mx</value>
|
||||
</data>
|
||||
<data name="November" xml:space="preserve">
|
||||
<value>Лістапад</value>
|
||||
</data>
|
||||
@@ -1578,7 +1575,7 @@
|
||||
<comment>'Nord' is the name of a specific color scheme. It should not be translated.</comment>
|
||||
</data>
|
||||
<data name="SolarizedDark" xml:space="preserve">
|
||||
<value>Solarized Dark</value>
|
||||
<value>Цёмная Solarized</value>
|
||||
<comment>'Solarized Dark' is the name of a specific color scheme. It should not be translated.</comment>
|
||||
</data>
|
||||
<data name="AutofillBlockedUris" xml:space="preserve">
|
||||
@@ -1625,7 +1622,7 @@
|
||||
<value>Біяметрычныя праверка</value>
|
||||
</data>
|
||||
<data name="Biometrics" xml:space="preserve">
|
||||
<value>біяметрыяй</value>
|
||||
<value>Біяметрыяй</value>
|
||||
</data>
|
||||
<data name="UseBiometricsToUnlock" xml:space="preserve">
|
||||
<value>Выкарыстоўваць біяметрычныя даныя для разблакіроўкі</value>
|
||||
@@ -2302,6 +2299,9 @@
|
||||
<value>Пасля таго, як ваш ключ паспяхова ўведзены,
|
||||
выберыце "Дадаць TOTP" для надзейнага захавання ключа</value>
|
||||
</data>
|
||||
<data name="SelectAddTotpToStoreTheKeySafely" xml:space="preserve">
|
||||
<value></value>
|
||||
</data>
|
||||
<data name="NeverLockWarning" xml:space="preserve">
|
||||
<value>Прызначыўшы параметр блакіравання "Ніколі", ваша сховішча будзе даступна кожнаму, хто мае доступ да вашай прылады. Калі вы выкарыстоўваеце гэты параметр, вы павінны быць упэўнены ў тым, што ваша прылада надзейна абаронена.</value>
|
||||
</data>
|
||||
@@ -2422,14 +2422,6 @@
|
||||
<value>SimpleLogin</value>
|
||||
<comment>"SimpleLogin" is the product name and should not be translated.</comment>
|
||||
</data>
|
||||
<data name="DuckDuckGo" xml:space="preserve">
|
||||
<value>DuckDuckGo</value>
|
||||
<comment>"DuckDuckGo" is the product name and should not be translated.</comment>
|
||||
</data>
|
||||
<data name="Fastmail" xml:space="preserve">
|
||||
<value>Fastmail</value>
|
||||
<comment>"Fastmail" is the product name and should not be translated.</comment>
|
||||
</data>
|
||||
<data name="APIAccessToken" xml:space="preserve">
|
||||
<value>Токен доступу да API</value>
|
||||
</data>
|
||||
@@ -2460,9 +2452,6 @@
|
||||
<data name="Random" xml:space="preserve">
|
||||
<value>Выпадкова</value>
|
||||
</data>
|
||||
<data name="ConnectToWatch" xml:space="preserve">
|
||||
<value>Падлучыцца да гадзінніка</value>
|
||||
</data>
|
||||
<data name="AccessibilityServiceDisclosure" xml:space="preserve">
|
||||
<value>Апісанне службы спецыяльных магчымасцей</value>
|
||||
</data>
|
||||
@@ -2483,97 +2472,4 @@
|
||||
{0}
|
||||
Вы сапраўды хочаце перайсці на гэты ўліковы запіс?</value>
|
||||
</data>
|
||||
<data name="NewAroundHere" xml:space="preserve">
|
||||
<value>Упершыню тут?</value>
|
||||
</data>
|
||||
<data name="GetMasterPasswordwordHint" xml:space="preserve">
|
||||
<value>Атрымаць падказку да асноўнага пароля</value>
|
||||
</data>
|
||||
<data name="LoggingInAsX" xml:space="preserve">
|
||||
<value>Увайсці як {0}</value>
|
||||
</data>
|
||||
<data name="NotYou" xml:space="preserve">
|
||||
<value>Не вы?</value>
|
||||
</data>
|
||||
<data name="LogInWithMasterPassword" xml:space="preserve">
|
||||
<value>Увайсці з асноўным паролем</value>
|
||||
</data>
|
||||
<data name="LogInWithAnotherDevice" xml:space="preserve">
|
||||
<value>Увайсці з іншай прылады</value>
|
||||
</data>
|
||||
<data name="LogInInitiated" xml:space="preserve">
|
||||
<value>Ініцыяваны ўваход</value>
|
||||
</data>
|
||||
<data name="ANotificationHasBeenSentToYourDevice" xml:space="preserve">
|
||||
<value>Апавяшчэнне было адпраўлена на вашу прыладу.</value>
|
||||
</data>
|
||||
<data name="PleaseMakeSureYourVaultIsUnlockedAndTheFingerprintPhraseMatchesOnTheOtherDevice" xml:space="preserve">
|
||||
<value>Пераканайцеся, што ваша сховішча разблакіравана, а фраза адбітка пальца супадае з іншай прыладай.</value>
|
||||
</data>
|
||||
<data name="ResendNotification" xml:space="preserve">
|
||||
<value>Адправіць апавяшчэнне паўторна</value>
|
||||
</data>
|
||||
<data name="NeedAnotherOption" xml:space="preserve">
|
||||
<value>Неабходны іншы варыянт?</value>
|
||||
</data>
|
||||
<data name="ViewAllLoginOptions" xml:space="preserve">
|
||||
<value>Паглядзець усе варыянты ўваходу</value>
|
||||
</data>
|
||||
<data name="ThisRequestIsNoLongerValid" xml:space="preserve">
|
||||
<value>Гэты запыт больш не дзейнічае</value>
|
||||
</data>
|
||||
<data name="PendingLogInRequests" xml:space="preserve">
|
||||
<value>Чаканне запыту на ўваход</value>
|
||||
</data>
|
||||
<data name="DeclineAllRequests" xml:space="preserve">
|
||||
<value>Адхіліць усе запыты</value>
|
||||
</data>
|
||||
<data name="AreYouSureYouWantToDeclineAllPendingLogInRequests" xml:space="preserve">
|
||||
<value>Вы сапраўды хочаце адхіліць усе запыты, якія чакаюць уваходу?</value>
|
||||
</data>
|
||||
<data name="RequestsDeclined" xml:space="preserve">
|
||||
<value>Запыты адхілены</value>
|
||||
</data>
|
||||
<data name="NoPendingRequests" xml:space="preserve">
|
||||
<value>Няма запытаў, якія чакаюць уваходу</value>
|
||||
</data>
|
||||
<data name="EnableCamerPermissionToUseTheScanner" xml:space="preserve">
|
||||
<value>Каб выкарыстоўваць сканер дайце дазвол на выкарыстанне камеры</value>
|
||||
</data>
|
||||
<data name="Important" xml:space="preserve">
|
||||
<value>Важна</value>
|
||||
</data>
|
||||
<data name="YourMasterPasswordCannotBeRecoveredIfYouForgetItXCharactersMinimum" xml:space="preserve">
|
||||
<value>Ваш асноўны пароль нельга будзе аднавіць, калі вы забудзеце яго! Мінімальная колькасць сімвалаў: {0}.</value>
|
||||
</data>
|
||||
<data name="WeakMasterPassword" xml:space="preserve">
|
||||
<value>Ненадзейны асноўны пароль</value>
|
||||
</data>
|
||||
<data name="WeakPasswordIdentifiedUseAStrongPasswordToProtectYourAccount" xml:space="preserve">
|
||||
<value>Вызначаны ненадзейны пароль. Выкарыстоўвайце надзейны пароль для абароны вашага ўліковага запісу. Вы сапраўды хочаце выкарыстоўваць ненадзейны пароль?</value>
|
||||
</data>
|
||||
<data name="Weak" xml:space="preserve">
|
||||
<value>Ненадзейны</value>
|
||||
</data>
|
||||
<data name="Good" xml:space="preserve">
|
||||
<value>Добры</value>
|
||||
</data>
|
||||
<data name="Strong" xml:space="preserve">
|
||||
<value>Надзейны</value>
|
||||
</data>
|
||||
<data name="CheckKnownDataBreachesForThisPassword" xml:space="preserve">
|
||||
<value>Праверыць у вядомых уцечках даных для гэтага пароля</value>
|
||||
</data>
|
||||
<data name="ExposedMasterPassword" xml:space="preserve">
|
||||
<value>Скампраметаваны асноўны пароль</value>
|
||||
</data>
|
||||
<data name="PasswordFoundInADataBreachAlertDescription" xml:space="preserve">
|
||||
<value>Пароль знойдзены ва ўцечках даных. Выкарыстоўвайце ўнікальныя паролі для абароны свайго ўліковага запісу. Вы сапраўды хочаце выкарыстоўваць скампраметаваны пароль?</value>
|
||||
</data>
|
||||
<data name="WeakAndExposedMasterPassword" xml:space="preserve">
|
||||
<value>Ненадзейны і скампраметаваны асноўны пароль</value>
|
||||
</data>
|
||||
<data name="WeakPasswordIdentifiedAndFoundInADataBreachAlertDescription" xml:space="preserve">
|
||||
<value>Вызначаны ненадзейны пароль, які знойдзены ва ўцечках даных. Выкарыстоўвайце надзейныя і ўнікальныя паролі для абароны свайго ўліковага запісу. Вы сапраўды хочаце выкарыстоўваць гэты пароль?</value>
|
||||
</data>
|
||||
</root>
|
||||
|
||||
@@ -300,7 +300,7 @@
|
||||
<comment>The title for the vault page.</comment>
|
||||
</data>
|
||||
<data name="Authenticator" xml:space="preserve">
|
||||
<value>Удостоверител</value>
|
||||
<value>Authenticator</value>
|
||||
<comment>Authenticator TOTP feature</comment>
|
||||
</data>
|
||||
<data name="Name" xml:space="preserve">
|
||||
@@ -376,7 +376,7 @@
|
||||
</data>
|
||||
<data name="ValueHasBeenCopied" xml:space="preserve">
|
||||
<value>{0} беше копирано.</value>
|
||||
<comment>Confirmation message after successfully copying a value to the clipboard.</comment>
|
||||
<comment>Confirmation message after suceessfully copying a value to the clipboard.</comment>
|
||||
</data>
|
||||
<data name="VerifyFingerprint" xml:space="preserve">
|
||||
<value>Потвърждаване на пръстовия отпечатък</value>
|
||||
@@ -1103,9 +1103,6 @@
|
||||
<data name="Ms" xml:space="preserve">
|
||||
<value>Г-ца</value>
|
||||
</data>
|
||||
<data name="Mx" xml:space="preserve">
|
||||
<value>Mx</value>
|
||||
</data>
|
||||
<data name="November" xml:space="preserve">
|
||||
<value>ноември</value>
|
||||
</data>
|
||||
@@ -2083,7 +2080,7 @@
|
||||
<value>Това действие е защитено. За да продължите, въведете отново главната си парола, за да потвърдите самоличността си.</value>
|
||||
</data>
|
||||
<data name="CaptchaRequired" xml:space="preserve">
|
||||
<value>Адресът за hCaptcha е задължителен</value>
|
||||
<value>Captcha required</value>
|
||||
</data>
|
||||
<data name="CaptchaFailed" xml:space="preserve">
|
||||
<value>
|
||||
@@ -2270,7 +2267,7 @@
|
||||
<value>Всички</value>
|
||||
</data>
|
||||
<data name="Totp" xml:space="preserve">
|
||||
<value>Кода за потвърждаване</value>
|
||||
<value>TOTP</value>
|
||||
</data>
|
||||
<data name="VerificationCodes" xml:space="preserve">
|
||||
<value>Кодове за потвърждаване</value>
|
||||
@@ -2294,15 +2291,18 @@
|
||||
<value>Ръчно въвеждане на кода</value>
|
||||
</data>
|
||||
<data name="AddTotp" xml:space="preserve">
|
||||
<value>Копиране на кода за потвърждаване</value>
|
||||
<value>Add TOTP</value>
|
||||
</data>
|
||||
<data name="SetupTotp" xml:space="preserve">
|
||||
<value>Копиране на кода за потвърждаване</value>
|
||||
<value>Set up TOTP</value>
|
||||
</data>
|
||||
<data name="OnceTheKeyIsSuccessfullyEntered" xml:space="preserve">
|
||||
<value>Once the key is successfully entered,
|
||||
select Add TOTP to store the key safely</value>
|
||||
</data>
|
||||
<data name="SelectAddTotpToStoreTheKeySafely" xml:space="preserve">
|
||||
<value></value>
|
||||
</data>
|
||||
<data name="NeverLockWarning" xml:space="preserve">
|
||||
<value>Ако изберете „Никога“ като настройка за заключването, трезорът Ви ще бъде достъпен за всеки, който има досег с устройството. Ако използвате тази настройка, трябва да се уверите, че устройството Ви е удачно защитено.</value>
|
||||
</data>
|
||||
@@ -2337,7 +2337,7 @@ select Add TOTP to store the key safely</value>
|
||||
<value>Време</value>
|
||||
</data>
|
||||
<data name="Near" xml:space="preserve">
|
||||
<value>Близо</value>
|
||||
<value>Near</value>
|
||||
</data>
|
||||
<data name="ConfirmLogIn" xml:space="preserve">
|
||||
<value>Потвърждаване на вписването</value>
|
||||
@@ -2388,13 +2388,13 @@ select Add TOTP to store the key safely</value>
|
||||
<value>Тип потребителско име</value>
|
||||
</data>
|
||||
<data name="PlusAddressedEmail" xml:space="preserve">
|
||||
<value>Адрес на е-поща с плюс</value>
|
||||
<value>Plus addressed email</value>
|
||||
</data>
|
||||
<data name="CatchAllEmail" xml:space="preserve">
|
||||
<value>Хващаща всичко е-поща</value>
|
||||
<value>Catch-all email</value>
|
||||
</data>
|
||||
<data name="ForwardedEmailAlias" xml:space="preserve">
|
||||
<value>Псевдоним на препратена е-поща</value>
|
||||
<value>Forwarded email alias</value>
|
||||
</data>
|
||||
<data name="RandomWord" xml:space="preserve">
|
||||
<value>Произволна дума</value>
|
||||
@@ -2423,14 +2423,6 @@ select Add TOTP to store the key safely</value>
|
||||
<value>SimpleLogin</value>
|
||||
<comment>"SimpleLogin" is the product name and should not be translated.</comment>
|
||||
</data>
|
||||
<data name="DuckDuckGo" xml:space="preserve">
|
||||
<value>DuckDuckGo</value>
|
||||
<comment>"DuckDuckGo" is the product name and should not be translated.</comment>
|
||||
</data>
|
||||
<data name="Fastmail" xml:space="preserve">
|
||||
<value>Fastmail</value>
|
||||
<comment>"Fastmail" is the product name and should not be translated.</comment>
|
||||
</data>
|
||||
<data name="APIAccessToken" xml:space="preserve">
|
||||
<value>Идентификатор за достъп до API</value>
|
||||
</data>
|
||||
@@ -2441,7 +2433,7 @@ select Add TOTP to store the key safely</value>
|
||||
<value>Генериране на потр. име</value>
|
||||
</data>
|
||||
<data name="EmailType" xml:space="preserve">
|
||||
<value>Вид е-поща</value>
|
||||
<value>Email Type</value>
|
||||
</data>
|
||||
<data name="WebsiteRequired" xml:space="preserve">
|
||||
<value>Уеб сайт (задължително)</value>
|
||||
@@ -2453,17 +2445,14 @@ select Add TOTP to store the key safely</value>
|
||||
<value>Използвайте възможностите за под-адресиране на е-поща на своя доставчик</value>
|
||||
</data>
|
||||
<data name="CatchAllEmailDescription" xml:space="preserve">
|
||||
<value>Използвайте конфигурираната входяща кутия за събиране на всичко.</value>
|
||||
<value>Use your domain's configured catch-all inbox.</value>
|
||||
</data>
|
||||
<data name="ForwardedEmailDescription" xml:space="preserve">
|
||||
<value>Създайте псевдоним на е-поща с външна услуга за препращане.</value>
|
||||
<value>Generate an email alias with an external forwarding service.</value>
|
||||
</data>
|
||||
<data name="Random" xml:space="preserve">
|
||||
<value>Произволно</value>
|
||||
</data>
|
||||
<data name="ConnectToWatch" xml:space="preserve">
|
||||
<value>Свързване към часовник</value>
|
||||
</data>
|
||||
<data name="AccessibilityServiceDisclosure" xml:space="preserve">
|
||||
<value>Използване на услугата за достъпност</value>
|
||||
</data>
|
||||
@@ -2484,97 +2473,4 @@ select Add TOTP to store the key safely</value>
|
||||
{0}
|
||||
Искате ли да превключите към тази регистрация?</value>
|
||||
</data>
|
||||
<data name="NewAroundHere" xml:space="preserve">
|
||||
<value>За пръв път ли сте тук?</value>
|
||||
</data>
|
||||
<data name="GetMasterPasswordwordHint" xml:space="preserve">
|
||||
<value>Получете подсказване за главната парола</value>
|
||||
</data>
|
||||
<data name="LoggingInAsX" xml:space="preserve">
|
||||
<value>Вписване като {0}</value>
|
||||
</data>
|
||||
<data name="NotYou" xml:space="preserve">
|
||||
<value>Това не сте Вие?</value>
|
||||
</data>
|
||||
<data name="LogInWithMasterPassword" xml:space="preserve">
|
||||
<value>Вписване с главната парола</value>
|
||||
</data>
|
||||
<data name="LogInWithAnotherDevice" xml:space="preserve">
|
||||
<value>Вписване с друго устройство</value>
|
||||
</data>
|
||||
<data name="LogInInitiated" xml:space="preserve">
|
||||
<value>Вписването е стартирано</value>
|
||||
</data>
|
||||
<data name="ANotificationHasBeenSentToYourDevice" xml:space="preserve">
|
||||
<value>Към устройството Ви е изпратено известие.</value>
|
||||
</data>
|
||||
<data name="PleaseMakeSureYourVaultIsUnlockedAndTheFingerprintPhraseMatchesOnTheOtherDevice" xml:space="preserve">
|
||||
<value>Уверете се, че трезорът Ви е отключен и че Уникалната фраза съвпада с другото устройство.</value>
|
||||
</data>
|
||||
<data name="ResendNotification" xml:space="preserve">
|
||||
<value>Повторно изпращане на известието</value>
|
||||
</data>
|
||||
<data name="NeedAnotherOption" xml:space="preserve">
|
||||
<value>Предпочитате друг вариант?</value>
|
||||
</data>
|
||||
<data name="ViewAllLoginOptions" xml:space="preserve">
|
||||
<value>Вижте всички възможности за вписване</value>
|
||||
</data>
|
||||
<data name="ThisRequestIsNoLongerValid" xml:space="preserve">
|
||||
<value>Тази зявка вече не е приложима</value>
|
||||
</data>
|
||||
<data name="PendingLogInRequests" xml:space="preserve">
|
||||
<value>Чакащи заявки за вписване</value>
|
||||
</data>
|
||||
<data name="DeclineAllRequests" xml:space="preserve">
|
||||
<value>Отказване на всички заявки</value>
|
||||
</data>
|
||||
<data name="AreYouSureYouWantToDeclineAllPendingLogInRequests" xml:space="preserve">
|
||||
<value>Наистина ли искате да откажете всички чакащи заявки за вписване?</value>
|
||||
</data>
|
||||
<data name="RequestsDeclined" xml:space="preserve">
|
||||
<value>Заявките са отказани</value>
|
||||
</data>
|
||||
<data name="NoPendingRequests" xml:space="preserve">
|
||||
<value>Няма чакащи заявки</value>
|
||||
</data>
|
||||
<data name="EnableCamerPermissionToUseTheScanner" xml:space="preserve">
|
||||
<value>Разрешете достъпа до камерата, за да използвате скенера</value>
|
||||
</data>
|
||||
<data name="Important" xml:space="preserve">
|
||||
<value>Важно</value>
|
||||
</data>
|
||||
<data name="YourMasterPasswordCannotBeRecoveredIfYouForgetItXCharactersMinimum" xml:space="preserve">
|
||||
<value>Главната парола не може да бъде възстановена, ако я забравите! Дължината трябва да е поне {0} знака.</value>
|
||||
</data>
|
||||
<data name="WeakMasterPassword" xml:space="preserve">
|
||||
<value>Слаба главна парола</value>
|
||||
</data>
|
||||
<data name="WeakPasswordIdentifiedUseAStrongPasswordToProtectYourAccount" xml:space="preserve">
|
||||
<value>Разпозната е слаба парола. Използвайте силна парола, за да защитете данните си. Наистина ли искате да използвате слаба парола?</value>
|
||||
</data>
|
||||
<data name="Weak" xml:space="preserve">
|
||||
<value>Слаба</value>
|
||||
</data>
|
||||
<data name="Good" xml:space="preserve">
|
||||
<value>Добра</value>
|
||||
</data>
|
||||
<data name="Strong" xml:space="preserve">
|
||||
<value>Силна</value>
|
||||
</data>
|
||||
<data name="CheckKnownDataBreachesForThisPassword" xml:space="preserve">
|
||||
<value>Проверяване в известните случаи на изтекли данни за тази парола</value>
|
||||
</data>
|
||||
<data name="ExposedMasterPassword" xml:space="preserve">
|
||||
<value>Exposed Master Password</value>
|
||||
</data>
|
||||
<data name="PasswordFoundInADataBreachAlertDescription" xml:space="preserve">
|
||||
<value>Password found in a data breach. Use a unique password to protect your account. Are you sure you want to use an exposed password?</value>
|
||||
</data>
|
||||
<data name="WeakAndExposedMasterPassword" xml:space="preserve">
|
||||
<value>Weak and Exposed Master Password</value>
|
||||
</data>
|
||||
<data name="WeakPasswordIdentifiedAndFoundInADataBreachAlertDescription" xml:space="preserve">
|
||||
<value>Weak password identified and found in a data breach. Use a strong and unique password to protect your account. Are you sure you want to use this password?</value>
|
||||
</data>
|
||||
</root>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user