1
0
mirror of https://github.com/bitwarden/browser synced 2026-01-21 20:03:43 +00:00

Merge branch 'feature/org-admin-refresh' into EC-86-groups-table

This commit is contained in:
Shane Melton
2022-09-28 16:00:56 -07:00
573 changed files with 11184 additions and 4053 deletions

View File

@@ -4,7 +4,7 @@
"browser": true,
"webextensions": true
},
"plugins": ["@typescript-eslint", "rxjs", "rxjs-angular"],
"plugins": ["@typescript-eslint", "rxjs", "rxjs-angular", "import"],
"parser": "@typescript-eslint/parser",
"parserOptions": {
"project": ["./tsconfig.eslint.json"],
@@ -18,6 +18,16 @@
"prettier",
"plugin:rxjs/recommended"
],
"settings": {
"import/parsers": {
"@typescript-eslint/parser": [".ts"]
},
"import/resolver": {
"typescript": {
"alwaysTryTypes": true
}
}
},
"rules": {
"@typescript-eslint/no-explicit-any": "off", // TODO: This should be re-enabled
"@typescript-eslint/no-unused-vars": ["error", { "args": "none" }],
@@ -65,6 +75,27 @@
"selector": "CallExpression[callee.name='svgIcon']"
}
],
"curly": ["error", "all"]
"curly": ["error", "all"],
"import/namespace": ["off"], // This doesn't resolve namespace imports correctly, but TS will throw for this anyway
"import/no-restricted-paths": [
"error",
{
"zones": [
// Do not allow angular/node/electron code to be imported into common
{
"target": "./libs/common/**/*",
"from": "./libs/angular/**/*"
},
{
"target": "./libs/common/**/*",
"from": "./libs/node/**/*"
},
{
"target": "./libs/common/**/*",
"from": "./libs/electron/**/*"
}
]
}
]
}
}

16
.github/workflows/build-web-ee.yml vendored Normal file
View File

@@ -0,0 +1,16 @@
---
name: Build Web for EE
on:
workflow_dispatch:
jobs:
stub:
name: stub
runs-on: ubuntu-20.04
steps:
- name: Checkout repo
uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b # v3.0.2
- name: Stub
run: print 'This is only a stub'

View File

@@ -34,7 +34,7 @@ on:
jobs:
cloc:
name: CLOC
runs-on: ubuntu-20.04
runs-on: ubuntu-22.04
steps:
- name: Checkout repo
uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b # v3.0.2
@@ -51,7 +51,7 @@ jobs:
setup:
name: Setup
runs-on: ubuntu-20.04
runs-on: ubuntu-22.04
outputs:
version: ${{ steps.version.outputs.value }}
steps:
@@ -62,20 +62,31 @@ jobs:
id: version
run: echo "::set-output name=value::${GITHUB_SHA:0:7}"
build-oss-selfhost:
name: Build OSS zip
runs-on: ubuntu-20.04
build-artifacts:
name: Build artifacts
runs-on: ubuntu-22.04
needs:
- setup
env:
_VERSION: ${{ needs.setup.outputs.version }}
strategy:
matrix:
include:
- name: "selfhosted-open-source"
npm_command: "dist:oss:selfhost"
- name: "cloud-COMMERCIAL"
npm_command: "dist:bit:cloud"
- name: "selfhosted-COMMERCIAL"
npm_command: "dist:bit:selfhost"
- name: "cloud-QA"
npm_command: "build:bit:qa"
steps:
- name: Checkout repo
uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b # v3.0.2
- name: Set up Node
uses: actions/setup-node@56337c425554a6be30cdef71bf441f15be286854 # v3.1.1
uses: actions/setup-node@2fddd8803e2f5c9604345a0b591c3020ee971a93 # v3.4.1
with:
cache: 'npm'
cache-dependency-path: '**/package-lock.json'
@@ -94,127 +105,57 @@ jobs:
- name: Install dependencies
run: npm ci
- name: Build OSS selfhost
- name: Setup QA metadata
working-directory: apps/web
if: matrix.name == 'cloud-QA'
run: |
npm run dist:oss:selfhost
zip -r web-$_VERSION-selfhosted-open-source.zip build
VERSION=$( jq -r ".version" package.json)
jq --arg version "$VERSION - ${GITHUB_SHA:0:7}" '.version = $version' package.json > package.json.tmp
mv package.json.tmp package.json
- name: Upload build artifact
- name: Build ${{ matrix.name }}
working-directory: apps/web
run: npm run ${{ matrix.npm_command }}
- name: Upload ${{ matrix.name }} artifact
uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535 # v3.0.0
with:
name: web-${{ env._VERSION }}-selfhosted-open-source.zip
path: apps/web/web-${{ env._VERSION }}-selfhosted-open-source.zip
name: web-${{ env._VERSION }}-${{ matrix.name }}.zip
path: apps/web/build
if-no-files-found: error
build-cloud:
name: Build Cloud zip
runs-on: ubuntu-20.04
build-commercial-selfhost-image:
name: Build self-host docker image
runs-on: ubuntu-22.04
needs:
- setup
- build-artifacts
env:
_VERSION: ${{ needs.setup.outputs.version }}
steps:
- name: Checkout repo
uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b # v3.0.2
- name: Set up Node
uses: actions/setup-node@56337c425554a6be30cdef71bf441f15be286854 # v3.1.1
with:
cache: 'npm'
cache-dependency-path: '**/package-lock.json'
node-version: "16"
- name: Print environment
run: |
whoami
node --version
npm --version
gulp --version
docker --version
echo "GitHub ref: $GITHUB_REF"
echo "GitHub event: $GITHUB_EVENT"
- name: Install dependencies
run: npm ci
- name: Build Cloud
working-directory: apps/web
run: |
npm run dist:bit:cloud
zip -r web-$_VERSION-cloud-COMMERCIAL.zip build
- name: Upload build artifact
uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535 # v3.0.0
with:
name: web-${{ env._VERSION }}-cloud-COMMERCIAL.zip
path: apps/web/web-${{ env._VERSION }}-cloud-COMMERCIAL.zip
if-no-files-found: error
build-commercial-selfhost:
name: Build SelfHost Docker image
runs-on: ubuntu-20.04
needs:
- setup
env:
_VERSION: ${{ needs.setup.outputs.version }}
steps:
- name: Checkout repo
uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b # v3.0.2
- name: Set up Node
uses: actions/setup-node@56337c425554a6be30cdef71bf441f15be286854 # v3.1.1
with:
cache: 'npm'
cache-dependency-path: '**/package-lock.json'
node-version: "16"
- name: Print environment
run: |
whoami
node --version
npm --version
gulp --version
docker --version
echo "GitHub ref: $GITHUB_REF"
echo "GitHub event: $GITHUB_EVENT"
- name: Setup DCT
if: github.ref == 'refs/heads/master' || github.ref == 'refs/heads/rc' || github.ref == 'refs/heads/hotfix-rc-web'
if: github.ref == 'refs/heads/master' ||
github.ref == 'refs/heads/rc' ||
github.ref == 'refs/heads/hotfix-rc-web'
id: setup-dct
uses: bitwarden/gh-actions/setup-docker-trust@a8c384a05a974c05c48374c818b004be221d43ff
with:
azure-creds: ${{ secrets.AZURE_PROD_KV_CREDENTIALS }}
azure-keyvault-name: "bitwarden-prod-kv"
- name: Install dependencies
run: npm ci
- name: Build
working-directory: apps/web
run: |
echo -e "# Building Web\n"
echo "Building app"
echo "npm version $(npm --version)"
npm run dist:bit:selfhost
zip -r web-$_VERSION-selfhosted-COMMERCIAL.zip build
- name: Upload build artifact
uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535 # v3.0.0
- name: Download selfhosted-COMMERCIAL artifact
uses: actions/download-artifact@fb598a63ae348fa914e94cd0ff38f362e927b741
with:
name: web-${{ env._VERSION }}-selfhosted-COMMERCIAL.zip
path: apps/web/web-${{ env._VERSION }}-selfhosted-COMMERCIAL.zip
if-no-files-found: error
path: apps/web/build
- name: Build Docker image
working-directory: apps/web
run: |
echo -e "\nBuilding Docker image"
docker --version
docker build -t bitwarden/web .
run: docker build -t bitwarden/web .
- name: Tag rc branch
if: github.ref == 'refs/heads/rc'
@@ -229,7 +170,9 @@ jobs:
run: docker tag bitwarden/web bitwarden/web:hotfix-rc-web
- name: List Docker images
if: github.ref == 'refs/heads/master' || github.ref == 'refs/heads/rc' || github.ref == 'refs/heads/hotfix-rc-web'
if: github.ref == 'refs/heads/master' ||
github.ref == 'refs/heads/rc' ||
github.ref == 'refs/heads/hotfix-rc-web'
run: docker images
- name: Push rc image
@@ -254,7 +197,9 @@ jobs:
DOCKER_CONTENT_TRUST_REPOSITORY_PASSPHRASE: ${{ steps.setup-dct.outputs.dct-delegate-repo-passphrase }}
- name: Log out of Docker
if: github.ref == 'refs/heads/master' || github.ref == 'refs/heads/rc' || github.ref == 'refs/heads/hotfix-rc-web'
if: github.ref == 'refs/heads/master' ||
github.ref == 'refs/heads/rc' ||
github.ref == 'refs/heads/hotfix-rc-web'
run: |
docker logout
echo "DOCKER_CONTENT_TRUST=0" >> $GITHUB_ENV
@@ -289,33 +234,18 @@ jobs:
- name: Log out of Docker
run: docker logout
build-qa:
name: Build Docker images for QA environment
runs-on: ubuntu-20.04
runs-on: ubuntu-22.04
needs:
- setup
- build-artifacts
env:
_VERSION: ${{ needs.setup.outputs.version }}
steps:
- name: Checkout repo
uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b # v3.0.2
- name: Set up Node
uses: actions/setup-node@56337c425554a6be30cdef71bf441f15be286854 # v3.1.1
with:
cache: 'npm'
cache-dependency-path: '**/package-lock.json'
node-version: "16"
- name: Print environment
run: |
whoami
node --version
npm --version
gulp --version
docker --version
echo "GitHub ref: $GITHUB_REF"
echo "GitHub event: $GITHUB_EVENT"
- name: Login to Azure
uses: Azure/login@ec3c14589bd3e9312b3cc8c41e6860e258df9010 # v1.1
with:
@@ -324,24 +254,15 @@ jobs:
- name: Log into container registry
run: az acr login -n bitwardenqa
- name: Install dependencies
run: npm ci
- name: Download cloud-QA artifact
uses: actions/download-artifact@fb598a63ae348fa914e94cd0ff38f362e927b741
with:
name: web-${{ env._VERSION }}-cloud-QA.zip
path: apps/web/build
- name: Build
- name: Build Docker image
working-directory: apps/web
run: |
echo -e "# Building Web\n"
echo "Building app"
echo "npm version $(npm --version)"
VERSION=$( jq -r ".version" package.json)
jq --arg version "$VERSION - ${GITHUB_SHA:0:7}" '.version = $version' package.json > package.json.tmp
mv package.json.tmp package.json
npm run build:bit:qa
echo "{\"commit_hash\": \"$GITHUB_SHA\", \"ref\": \"$GITHUB_REF\"}" | jq . > build/info.json
echo -e "\nBuilding Docker image"
docker --version
docker build -t bitwardenqa.azurecr.io/web .
@@ -371,9 +292,6 @@ jobs:
if: github.ref == 'refs/heads/master'
run: docker tag bitwardenqa.azurecr.io/web bitwardenqa.azurecr.io/web:dev
- name: List Docker images
run: docker images
- name: Push image
env:
IMAGE_TAG: ${{ steps.image-tag.outputs.value }}
@@ -391,11 +309,8 @@ jobs:
name: Crowdin Push
if: github.ref == 'refs/heads/master'
needs:
- build-oss-selfhost
- build-cloud
- build-commercial-selfhost
- build-qa
runs-on: ubuntu-20.04
- build-artifacts
runs-on: ubuntu-22.04
steps:
- name: Checkout repo
uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b # v3.0.2 # v2.3.4
@@ -435,13 +350,12 @@ jobs:
check-failures:
name: Check for failures
if: always()
runs-on: ubuntu-20.04
runs-on: ubuntu-22.04
needs:
- cloc
- setup
- build-oss-selfhost
- build-cloud
- build-commercial-selfhost
- build-artifacts
- build-commercial-selfhost-image
- build-qa
- crowdin-push
steps:
@@ -450,9 +364,8 @@ jobs:
env:
CLOC_STATUS: ${{ needs.cloc.result }}
SETUP_STATUS: ${{ needs.setup.result }}
BUILD_OSS_SELFHOST_STATUS: ${{ needs.build-oss-selfhost.result }}
BUILD_CLOUD_STATUS: ${{ needs.build-cloud.result }}
BUILD_COMMERCIAL_SELFHOST_STATUS: ${{ needs.build-commercial-selfhost.result }}
ARTIFACT_STATUS: ${{ needs.build-artifacts.result }}
BUILD_SELFHOST_STATUS: ${{ needs.build-commercial-selfhost-image.result }}
BUILD_QA_STATUS: ${{ needs.build-qa.result }}
CROWDIN_PUSH_STATUS: ${{ needs.crowdin-push.result }}
run: |
@@ -460,11 +373,9 @@ jobs:
exit 1
elif [ "$SETUP_STATUS" = "failure" ]; then
exit 1
elif [ "$BUILD_OSS_SELFHOST_STATUS" = "failure" ]; then
elif [ "$ARTIFACT_STATUS" = "failure" ]; then
exit 1
elif [ "$BUILD_CLOUD_STATUS" = "failure" ]; then
exit 1
elif [ "$BUILD_COMMERCIAL_SELFHOST_STATUS" = "failure" ]; then
elif [ "$BUILD_SELFHOST_STATUS" = "failure" ]; then
exit 1
elif [ "$BUILD_QA_STATUS" = "failure" ]; then
exit 1

View File

@@ -2,79 +2,9 @@
name: QA - Web Release
on:
workflow_dispatch:
inputs:
image_extension:
description: "Image tag extension"
required: false
env:
_QA_CLUSTER_RESOURCE_GROUP: "bw-env-qa"
_QA_CLUSTER_NAME: "bw-aks-qa"
_QA_K8S_NAMESPACE: "bw-qa"
_QA_K8S_APP_NAME: "bw-web"
workflow_dispatch: {}
jobs:
deploy:
name: Deploy QA Web
runs-on: ubuntu-20.04
steps:
- name: Checkout Repo
uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b # v3.0.2
- name: Setup
run: export PATH=$PATH:~/work/web/web
- name: Login to Azure
uses: Azure/login@ec3c14589bd3e9312b3cc8c41e6860e258df9010 # v1.1
with:
creds: ${{ secrets.AZURE_QA_KV_CREDENTIALS }}
- name: Retrieve secrets
id: retrieve-secrets
env:
KEYVAULT: bitwarden-qa-kv
SECRETS: |
qa-aks-kubectl-credentials
run: |
for i in ${SECRETS//,/ }
do
VALUE=$(az keyvault secret show --vault-name $KEYVAULT --name $i --query value --output tsv)
echo "::add-mask::$VALUE"
echo "::set-output name=$i::$VALUE"
done
- name: Login with qa-aks-kubectl-credentials SP
uses: Azure/login@ec3c14589bd3e9312b3cc8c41e6860e258df9010 # v1.1
with:
creds: ${{ env.qa-aks-kubectl-credentials }}
- name: Setup AKS access
run: |
echo "---az install---"
az aks install-cli --install-location ./kubectl --kubelogin-install-location ./kubelogin
echo "---az get-creds---"
az aks get-credentials -n $_QA_CLUSTER_NAME -g $_QA_CLUSTER_RESOURCE_GROUP
- name: Get image tag
id: image_tag
run: |
IMAGE_TAG=$(echo "${GITHUB_REF:11}" | sed "s#/#-#g")
TAG_EXTENSION=${{ github.event.inputs.image_extension }}
if [[ $TAG_EXTENSION ]]; then
IMAGE_TAG=$IMAGE_TAG-$TAG_EXTENSION
fi
echo "::set-output name=value::$IMAGE_TAG"
- name: Deploy Web image
env:
IMAGE_TAG: ${{ steps.image_tag.outputs.value }}
run: |
kubectl set image -n $_QA_K8S_NAMESPACE deployment/web web=bitwardenqa.azurecr.io/web:$IMAGE_TAG --record
kubectl rollout restart -n $_QA_K8S_NAMESPACE deployment/web
kubectl rollout status deployment/web -n $_QA_K8S_NAMESPACE
cfpages-deploy:
name: Deploy Web Vault to QA CloudFlare Pages branch
runs-on: ubuntu-20.04
@@ -104,15 +34,11 @@ jobs:
uses: bitwarden/gh-actions/download-artifacts@850faad0cf6c02a8c0dc46eddde2363fbd6c373a
with:
workflow: build-web.yml
path: apps/web
path: apps/web/build
workflow_conclusion: success
branch: ${{ github.ref_name }}
artifacts: web-*-cloud-COMMERCIAL.zip
artifacts: web-*-cloud-QA.zip
# This should result in a build directory in the current working directory
- name: Unzip build asset
working-directory: apps/web
run: unzip web-*-cloud-COMMERCIAL.zip
- name: Checkout Repo
uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b # v3.0.2

View File

@@ -27,3 +27,6 @@ libs/.github
# Github Workflows
.github/workflows
# Forked library files
libs/common/src/types/deep-jsonify.ts

View File

@@ -16,7 +16,7 @@ entity on behalf of whom you are acting (as applicable, "You" or "Your").
1. DEFINITIONS
"Bitwarden Software" means the Bitwarden server software, libraries, and
"Bitwarden Software" means the Bitwarden client software, libraries, and
Commercial Modules.
"Commercial Modules" means the modules designed to work with and enhance the

View File

@@ -1,6 +1,6 @@
{
"name": "@bitwarden/browser",
"version": "2022.9.0",
"version": "2022.9.1",
"scripts": {
"build": "webpack",
"build:mv3": "cross-env MANIFEST_VERSION=3 webpack",

View File

@@ -157,7 +157,7 @@
"description": "A 'fingerprint phrase' is a unique word phrase (similar to a passphrase) that a user can use to authenticate their public key with another user, for the purposes of sharing."
},
"twoStepLogin": {
"message": "Two-step login"
"message": "تسجيل الدخول بخطوتين"
},
"logOut": {
"message": "تسجيل الخروج"
@@ -258,7 +258,7 @@
"description": "Make the first letter of a work uppercase."
},
"includeNumber": {
"message": "Include Number"
"message": "تضمين الرقم"
},
"minNumbers": {
"message": "الحد الأدنى من الأرقام"
@@ -315,7 +315,7 @@
"message": "عرض العنصر"
},
"launch": {
"message": "Launch"
"message": "بدء"
},
"website": {
"message": "الموقع الإلكتروني"
@@ -327,7 +327,7 @@
"message": "إدارة"
},
"other": {
"message": "Other"
"message": "الأخرى"
},
"rateExtension": {
"message": "قيِّم هذه الإضافة"
@@ -412,7 +412,7 @@
"message": "مطلقاً"
},
"security": {
"message": "Security"
"message": "الأمان"
},
"errorOccurred": {
"message": "لقد حدث خطأ ما"
@@ -488,10 +488,10 @@
"message": "تغيير كلمة المرور الرئيسية"
},
"changeMasterPasswordConfirmation": {
"message": "You can change your master password on the bitwarden.com web vault. Do you want to visit the website now?"
"message": "يمكنك تغيير كلمة المرور الرئيسية من خزنة الويب في bitwarden.com. هل تريد زيارة الموقع الآن؟"
},
"twoStepLoginConfirmation": {
"message": "Two-step login makes your account more secure by requiring you to verify your login with another device such as a security key, authenticator app, SMS, phone call, or email. Two-step login can be enabled on the bitwarden.com web vault. Do you want to visit the website now?"
"message": "تسجيل الدخول بخطوتين يجعل حسابك أكثر أمنا من خلال مطالبتك بالتحقق من تسجيل الدخول باستخدام جهاز آخر مثل مفتاح الأمان، تطبيق المصادقة، الرسائل القصيرة، المكالمة الهاتفية، أو البريد الإلكتروني. يمكن تمكين تسجيل الدخول بخطوتين على خزنة الويب bitwarden.com. هل تريد زيارة الموقع الآن؟"
},
"editedFolder": {
"message": "Edited folder"
@@ -500,13 +500,13 @@
"message": "هل أنت متأكد من حذف هذا المجلّد؟"
},
"deletedFolder": {
"message": "Deleted folder"
"message": "تم حذف المجلد"
},
"gettingStartedTutorial": {
"message": "Getting Started Tutorial"
"message": "لنبدأ نتعلم معاً"
},
"gettingStartedTutorialVideo": {
"message": "Watch our getting started tutorial to learn how to get the most out of the browser extension."
"message": "مشاهدة دروس البَدْء تمكنك من الحصول على أقصى أستفادة من ملحق المتصفح."
},
"syncingComplete": {
"message": "تم إكمال المزامنة"
@@ -531,31 +531,31 @@
}
},
"newUri": {
"message": "New URI"
"message": "رابط جديد"
},
"addedItem": {
"message": "Added item"
"message": "تمت إضافة العنصر"
},
"editedItem": {
"message": "Edited item"
"message": "تم تعديل العنصر"
},
"deleteItemConfirmation": {
"message": "Do you really want to send to the trash?"
"message": "هل تريد حقاً أن ترسل إلى سلة المهملات؟"
},
"deletedItem": {
"message": "تم إرسال العنصر إلى سلة المهملات"
},
"overwritePassword": {
"message": "Overwrite Password"
"message": "الكتابة فوق كلمة المرور"
},
"overwritePasswordConfirmation": {
"message": "Are you sure you want to overwrite the current password?"
"message": "هل أنت متأكد من أنك تريد الكتابة فوق كلمة المرور الموجودة؟"
},
"overwriteUsername": {
"message": "Overwrite Username"
"message": "الكتابة فوق اسم المستخدم"
},
"overwriteUsernameConfirmation": {
"message": "Are you sure you want to overwrite the current username?"
"message": "هل أنت متأكد من أنك تريد الكتابة فوق اسم المستخدم الموجود؟"
},
"searchFolder": {
"message": "إبحث في المجلّد"
@@ -589,15 +589,15 @@
"message": "List identity items on the Tab page for easy auto-fill."
},
"clearClipboard": {
"message": "Clear clipboard",
"message": "مسح الحافظة",
"description": "Clipboard is the operating system thing where you copy/paste data to on your device."
},
"clearClipboardDesc": {
"message": "Automatically clear copied values from your clipboard.",
"message": "مسح القيم المنسوخة تلقائيًا من حافظتك.",
"description": "Clipboard is the operating system thing where you copy/paste data to on your device."
},
"notificationAddDesc": {
"message": "Should Bitwarden remember this password for you?"
"message": "هل يجب على Bitwarden تذكر كلمة المرور هذه لك؟"
},
"notificationAddSave": {
"message": "حفظ"
@@ -683,10 +683,10 @@
"message": "Move to Organization"
},
"share": {
"message": "Share"
"message": "مشاركة"
},
"movedItemToOrg": {
"message": "$ITEMNAME$ moved to $ORGNAME$",
"message": "$ITEMNAME$ انتقل إلى $ORGNAME$",
"placeholders": {
"itemname": {
"content": "$1",
@@ -699,16 +699,16 @@
}
},
"moveToOrgDesc": {
"message": "Choose an organization that you wish to move this item to. Moving to an organization transfers ownership of the item to that organization. You will no longer be the direct owner of this item once it has been moved."
"message": "اختر المؤسسة التي ترغب في نقل هذا العنصر إليها. يؤدي النقل إلى مؤسسة إلى نقل ملكية العنصر إلى تلك المؤسسة. لن تكون المالك المباشر لهذا العنصر بعد نقله."
},
"learnMore": {
"message": "Learn more"
"message": "معرفة المزيد"
},
"authenticatorKeyTotp": {
"message": "Authenticator Key (TOTP)"
"message": "مفتاح المصادقة (TOTP)"
},
"verificationCodeTotp": {
"message": "Verification Code (TOTP)"
"message": "رمز التحقق (TOTP)"
},
"copyVerificationCode": {
"message": "Copy Verification Code"
@@ -1124,16 +1124,16 @@
"message": "Dr"
},
"firstName": {
"message": "First Name"
"message": "الاسم الأول"
},
"middleName": {
"message": "Middle Name"
"message": "الاسم الأوسط"
},
"lastName": {
"message": "Last Name"
"message": "الاسم الأخير"
},
"fullName": {
"message": "Full Name"
"message": "الاسم الكامل"
},
"identityName": {
"message": "Identity Name"
@@ -1289,7 +1289,7 @@
"description": "The URI of one of the current open tabs in the browser."
},
"organization": {
"message": "Organization",
"message": "المؤسسة",
"description": "An entity of multiple related people (ex. a team or business organization)."
},
"types": {
@@ -1672,17 +1672,17 @@
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
},
"expirationDate": {
"message": "Expiration Date"
"message": "تاريخ انتهاء الصلاحية"
},
"expirationDateDesc": {
"message": "If set, access to this Send will expire on the specified date and time.",
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
},
"oneDay": {
"message": "1 day"
"message": "يوم واحد"
},
"days": {
"message": "$DAYS$ days",
"message": "$DAYS$ أيام",
"placeholders": {
"days": {
"content": "$1",
@@ -1731,7 +1731,7 @@
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
},
"newPassword": {
"message": "New Password"
"message": "كلمة المرور الجديدة"
},
"sendDisabled": {
"message": "Send Disabled",
@@ -1766,7 +1766,7 @@
"description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read '**To use a calendar style date picker ** click here to pop out your window.'"
},
"sendFirefoxCustomDatePopoutMessage2": {
"message": "click here",
"message": "انقر هنا",
"description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'To use a calendar style date picker **click here** to pop out your window.'"
},
"sendFirefoxCustomDatePopoutMessage3": {
@@ -1798,37 +1798,37 @@
"message": "Master password re-prompt"
},
"passwordConfirmation": {
"message": "Master password confirmation"
"message": "تأكيد كلمة المرور الرئيسية"
},
"passwordConfirmationDesc": {
"message": "This action is protected. To continue, please re-enter your master password to verify your identity."
"message": "هذا الإجراء محمي. للاستمرار، يرجى إعادة إدخال كلمة المرور الرئيسية للتحقق من هويتك."
},
"emailVerificationRequired": {
"message": "Email Verification Required"
"message": "تأكيد البريد الإلكتروني مطلوب"
},
"emailVerificationRequiredDesc": {
"message": "You must verify your email to use this feature. You can verify your email in the web vault."
"message": "يجب عليك تأكيد بريدك الإلكتروني لاستخدام هذه الميزة. يمكنك تأكيد بريدك الإلكتروني في خزنة الويب."
},
"updatedMasterPassword": {
"message": "Updated Master Password"
},
"updateMasterPassword": {
"message": "Update Master Password"
"message": "تحديث كلمة المرور الرئيسية"
},
"updateMasterPasswordWarning": {
"message": "Your Master Password was recently changed by an administrator in your organization. In order to access the vault, you must update it now. Proceeding will log you out of your current session, requiring you to log back in. Active sessions on other devices may continue to remain active for up to one hour."
"message": "تم تغيير كلمة المرور الرئيسية الخاصة بك مؤخرًا من قبل مسؤول في مؤسستك. من أجل الوصول إلى الخزنة، يجب عليك تحديثها الآن. سيتم تسجيل خروجك من الجلسة الحالية، مما يتطلب منك تسجيل الدخول مرة أخرى. قد تظل الجلسات النشطة على أجهزة أخرى نشطة لمدة تصل إلى ساعة واحدة."
},
"resetPasswordPolicyAutoEnroll": {
"message": "Automatic Enrollment"
"message": "التسجيل التلقائي"
},
"resetPasswordAutoEnrollInviteWarning": {
"message": "This organization has an enterprise policy that will automatically enroll you in password reset. Enrollment will allow organization administrators to change your master password."
"message": "هذه المؤسسة لديها سياسة تقوم تلقائياً بتسجيلك في إعادة تعيين كلمة المرور. هذا التسجيل سيسمح لمسؤولي المؤسسة بتغيير كلمة المرور الرئيسية الخاصة بك."
},
"selectFolder": {
"message": "Select folder..."
},
"ssoCompleteRegistration": {
"message": "In order to complete logging in with SSO, please set a master password to access and protect your vault."
"message": "من أجل إكمال تسجيل الدخول باستخدام SSO، يرجى تعيين كلمة المرور الرئيسية للوصول لخزنتك وحمايتها."
},
"hours": {
"message": "ساعات"
@@ -1837,7 +1837,7 @@
"message": "دقائق"
},
"vaultTimeoutPolicyInEffect": {
"message": "Your organization policies are affecting your vault timeout. Maximum allowed Vault Timeout is $HOURS$ hour(s) and $MINUTES$ minute(s)",
"message": "سياسات مؤسستك تؤثر على مهلة الخزنة الخاص بك. الحد الأقصى المسموح به لمهلة الخزنة هو $HOURS$ ساعة/ساعات و $MINUTES$ دقيقة/دقائق",
"placeholders": {
"hours": {
"content": "$1",
@@ -1850,19 +1850,19 @@
}
},
"vaultTimeoutTooLarge": {
"message": "Your vault timeout exceeds the restrictions set by your organization."
"message": "مهلة خزنتك تتجاوز القيود التي تضعها مؤسستك."
},
"vaultExportDisabled": {
"message": "Vault Export Disabled"
"message": "تصدير الخزنة مُعطّل"
},
"personalVaultExportPolicyInEffect": {
"message": "One or more organization policies prevents you from exporting your personal vault."
"message": "واحدة أو أكثر من سياسات المؤسسة تمنعك من تصدير خزانتك الشخصية."
},
"copyCustomFieldNameInvalidElement": {
"message": "Unable to identify a valid form element. Try inspecting the HTML instead."
"message": "غير قادر على التعرف على نموذج صالح. حاول فحص HTML بدلا من ذلك."
},
"copyCustomFieldNameNotUnique": {
"message": "No unique identifier found."
"message": "لم يتم العثور على معرف فريد."
},
"convertOrganizationEncryptionDesc": {
"message": "$ORGANIZATION$ is using SSO with a self-hosted key server. A master password is no longer required to log in for members of this organization.",
@@ -1923,10 +1923,10 @@
"description": "Username generator option that appends a random sub-address to the username. For example: address+subaddress@email.com"
},
"plusAddressedEmailDesc": {
"message": "Use your email provider's sub-addressing capabilities."
"message": "استخدم قدرات العنوان الفرعي لمزود البريد الإلكتروني الخاص بك."
},
"catchallEmail": {
"message": "Catch-all Email"
"message": "تجميع كل البريد الإلكتروني"
},
"catchallEmailDesc": {
"message": "Use your domain's configured catch-all inbox."
@@ -1950,10 +1950,10 @@
"message": "الخدمة"
},
"forwardedEmail": {
"message": "Forwarded Email Alias"
"message": "إعادة توجيه الاسم المستعار للبريد الإلكتروني"
},
"forwardedEmailDesc": {
"message": "Generate an email alias with an external forwarding service."
"message": "إنشاء بريد إلكتروني مستعار مع خدمة إعادة توجيه خارجية."
},
"hostname": {
"message": "اسم المضيف",
@@ -1993,7 +1993,7 @@
"message": "تم تعديل الإعدادات"
},
"environmentEditedClick": {
"message": "Click here"
"message": "انقر هنا"
},
"environmentEditedReset": {
"message": "لإعادة تعيين الإعدادات المُعدة مسبقاً"
@@ -2017,7 +2017,7 @@
}
},
"lastSeenOn": {
"message": "last seen on $DATE$",
"message": "آخر ظهور في $DATE$",
"placeholders": {
"date": {
"content": "$1",

View File

@@ -20,7 +20,7 @@
"message": "Увайсці"
},
"enterpriseSingleSignOn": {
"message": "Адзіны ўваход у карпаратыўную сістэму (SSO)."
"message": "Адзіны ўваход прадпрыемства (SSO)"
},
"cancel": {
"message": "Скасаваць"
@@ -32,7 +32,7 @@
"message": "Адправіць"
},
"emailAddress": {
"message": "Адрас эл. пошты"
"message": "Адрас электроннай пошты"
},
"masterPass": {
"message": "Асноўны пароль"
@@ -41,7 +41,7 @@
"message": "Асноўны пароль — ключ да вашага бяспечнага сховішча. Ён вельмі важны, таму не забывайце яго. Аднавіць асноўны пароль немагчыма."
},
"masterPassHintDesc": {
"message": "Падказка да асноўнага пароля можа дапамагчы вам яго ўспомніць."
"message": "Падказка да асноўнага пароля можа дапамагчы вам успомніць яго, калі вы яго забылі."
},
"reTypeMasterPass": {
"message": "Увядзіце асноўны пароль паўторна"
@@ -71,34 +71,34 @@
"message": "Бягучая ўкладка"
},
"copyPassword": {
"message": "Капіяваць пароль"
"message": "Скапіяваць пароль"
},
"copyNote": {
"message": "Капіяваць нататку"
"message": "Скапіяваць нататку"
},
"copyUri": {
"message": "Капіяваць URI"
"message": "Скапіяваць URI"
},
"copyUsername": {
"message": "Капіяваць імя карыстальніка"
"message": "Скапіяваць імя карыстальніка"
},
"copyNumber": {
"message": "Капіяваць нумар"
"message": "Скапіяваць нумар"
},
"copySecurityCode": {
"message": "Капіяваць код бяспекі"
"message": "Скапіяваць код бяспекі"
},
"autoFill": {
"message": "Аўтазапаўненне"
},
"generatePasswordCopied": {
"message": "Стварыць пароль (з капіяваннем)"
"message": "Генерыраваць пароль (з капіяваннем)"
},
"copyElementIdentifier": {
"message": "Скапіяваць назву карыстальніцкага пароля"
},
"noMatchingLogins": {
"message": "Няма падыходных уліковых даных."
"message": "Няма адпаведных лагінаў."
},
"unlockVaultMenu": {
"message": "Разблакіраваць сховішча"
@@ -110,7 +110,7 @@
"message": "Няма ўліковых даных, даступных для аўтазапаўнення ў бягучую ўкладку браўзера."
},
"addLogin": {
"message": "Дадаць ўліковыя даныя"
"message": "Дадаць лагін"
},
"addItem": {
"message": "Дадаць элемент"
@@ -119,10 +119,10 @@
"message": "Падказка да пароля"
},
"enterEmailToGetHint": {
"message": "Увядзіце адрас электроннай пошты ўліковага запісу для атрымання падказкі для асноўнага пароля."
"message": "Увядзіце адрас электроннай пошты ўліковага запісу для атрымання падказкі да асноўнага пароля."
},
"getMasterPasswordHint": {
"message": "Атрымаць падказку для асноўнага пароля"
"message": "Атрымаць падказку да асноўнага пароля"
},
"continue": {
"message": "Працягнуць"
@@ -208,23 +208,23 @@
"message": "Генератар пароляў"
},
"generator": {
"message": "Згенерыраваць",
"message": "Генератар",
"description": "Short for 'Password Generator'."
},
"passGenInfo": {
"message": "Аўтаматычна ствараць моцныя, унікальныя паролі для вашых уліковых даных."
"message": "Аўтаматычна генерыруйце надзейныя і ўнікальныя паролі для вашых лагінаў."
},
"bitWebVault": {
"message": "Вэб-сховішча Bitwarden"
},
"importItems": {
"message": "Імпарт элементаў"
"message": "Імпартаванне элементаў"
},
"select": {
"message": "Выбраць"
},
"generatePassword": {
"message": "Стварыць пароль"
"message": "Генерыраваць пароль"
},
"regeneratePassword": {
"message": "Стварыць новы пароль"
@@ -254,17 +254,17 @@
"message": "Раздзяляльнік слоў"
},
"capitalize": {
"message": "З вялікай літары",
"message": "Вялікія літары",
"description": "Make the first letter of a work uppercase."
},
"includeNumber": {
"message": "Уключыць лічбу"
},
"minNumbers": {
"message": "Мін. колькасць лічбаў"
"message": "Мінімум лічбаў"
},
"minSpecial": {
"message": "Мін. колькасць сімвалаў"
"message": "Мінімум спецыяльных сімвалаў"
},
"avoidAmbChar": {
"message": "Пазбягаць неадназначных сімвалаў"
@@ -300,10 +300,10 @@
"message": "Нататкі"
},
"note": {
"message": "Нататкі"
"message": "Нататка"
},
"editItem": {
"message": "Рэдагаванне элемента"
"message": "Рэдагаваць элемент"
},
"folder": {
"message": "Папка"
@@ -324,7 +324,7 @@
"message": "Пераключыць бачнасць"
},
"manage": {
"message": "Кіраваць"
"message": "Кіраванне"
},
"other": {
"message": "Iншае"
@@ -348,7 +348,7 @@
"message": "Разблакіраваць"
},
"loggedInAsOn": {
"message": "Выкананы ўваход на $HOSTNAME$ як $EMAIL$.",
"message": "Вы ўвайшлі як $HOSTNAME$ у $EMAIL$.",
"placeholders": {
"email": {
"content": "$1",
@@ -403,7 +403,7 @@
"message": "4 гадзіны"
},
"onLocked": {
"message": "Разам з камп'ютарам"
"message": "Пры блакіраванні сістэмы"
},
"onRestart": {
"message": "Пры перазапуску браўзера"
@@ -433,7 +433,7 @@
"message": "Асноўны пароль павінен быць даўжынёй не менш за 8 сімвалаў."
},
"masterPassDoesntMatch": {
"message": "Асноўныя паролі не супадаюць."
"message": "Пацвярджэнне асноўнага пароля не супадае."
},
"newAccountCreated": {
"message": "Ваш уліковы запіс створаны! Вы можаце ўвайсці."
@@ -448,7 +448,7 @@
"message": "Памылковы праверачны код"
},
"valueCopied": {
"message": "$VALUE$ скапіяваны(-а)",
"message": "$VALUE$ скапіяваны",
"description": "Value has been copied to the clipboard.",
"placeholders": {
"value": {
@@ -458,10 +458,10 @@
}
},
"autofillError": {
"message": "Не ўдаецца аўтаматычна запоўніць выбраны элемент на гэтай старонцы. Скапіюйце і ўстаўце інфармацыю ўручную."
"message": "Немагчыма аўтазапоўніць выбраны элемент на гэтай старонцы. Скапіюйце і ўстаўце інфармацыю ўручную."
},
"loggedOut": {
"message": "Вы выйшлі са сховішча"
"message": "Вы выйшлі"
},
"loginExpired": {
"message": "Скончыўся тэрмін дзеяння вашага сеансу."
@@ -479,7 +479,7 @@
"message": "Адбылася нечаканая памылка."
},
"nameRequired": {
"message": "Патрэбна назва."
"message": "Патрабуецца назва."
},
"addedFolder": {
"message": "Папка дададзена"
@@ -503,7 +503,7 @@
"message": "Папка выдалена"
},
"gettingStartedTutorial": {
"message": "Дапаможнік па пачатку працы"
"message": "Уводзіны ў карыстанне праграмай"
},
"gettingStartedTutorialVideo": {
"message": "Праглядзіце невялікі навучальны матэрыял, каб даведацца. як атрымаць максімальную аддачу ад пашырэння браўзера."
@@ -540,7 +540,7 @@
"message": "Элемент адрэдагаваны"
},
"deleteItemConfirmation": {
"message": "Вы ўпэўнены, што хочаце выдаліць гэты элемент?"
"message": "Вы ўпэўнены, што хочаце адправіць гэты элемент у сметніцу?"
},
"deletedItem": {
"message": "Выдалены элемент"
@@ -574,19 +574,19 @@
"message": "Пытацца пры дадаванні лагіна"
},
"addLoginNotificationDesc": {
"message": "Апавяшчэнне аб даданні ўліковых даных аўтаматычна прапануе вам захаваць новыя ўліковыя даныя ў сховішчы."
"message": "Пытацца пра дадаванне элемента, калі ён адсутнічае ў вашым сховішчы."
},
"showCardsCurrentTab": {
"message": "Паказваць карткі на старонцы з укладкамі"
},
"showCardsCurrentTabDesc": {
"message": "Паказваць спіс элементаў на старонцы з укладкамі для лёгкага аўтазапаўнення."
"message": "Спіс элементаў картак на старонцы з укладкамі для лёгкага аўтазапаўнення."
},
"showIdentitiesCurrentTab": {
"message": "Паказваць пасведчанні на старонцы з укладкамі"
},
"showIdentitiesCurrentTabDesc": {
"message": "Паказваць пасведчанні элементаў на старонцы з укладкамі для лёгкага аўтазапаўнення."
"message": "Спіс элементаў пасведчання на старонцы з укладкамі для лёгкага аўтазапаўнення."
},
"clearClipboard": {
"message": "Ачыстка буфера абмену",
@@ -603,13 +603,13 @@
"message": "Так, захаваць зараз"
},
"enableChangedPasswordNotification": {
"message": "Пытацца пра абнаўленні існуючых даных уваходу"
"message": "Пытацца пра абнаўленні існуючага лагіна"
},
"changedPasswordNotificationDesc": {
"message": "Пытаць пра абнаўленне пароля ўваходу пры выяўленні змяненняў на вэб-сайце."
"message": "Пытацца пра абнаўленне пароля ад лагіна пры выяўленні змяненняў на вэб-сайце."
},
"notificationChangeDesc": {
"message": "Вы хочаце абнавіць гэты пароль у Bitwarden?"
"message": "Хочаце абнавіць гэты пароль у Bitwarden?"
},
"notificationChangeSave": {
"message": "Так, абнавіць зараз"
@@ -621,17 +621,17 @@
"message": "Выкарыстоўваць падвоены націск для доступу да генератара пароля і супастаўлення лагінаў для вэб-сайтаў. "
},
"defaultUriMatchDetection": {
"message": "Выяўленне супадзення URI па змаўчанні",
"message": "Прадвызначанае выяўленне супадзення URI",
"description": "Default URI match detection for auto-fill."
},
"defaultUriMatchDetectionDesc": {
"message": "Выберыце спосаб па змаўчанні, які выкарыстоўваецца пры вызначэнні адпаведнасці URI для ўліковых даных пры выкананні такіх дзеянняў, як аўтаматычнае запаўненне."
"message": "Выберыце прадвызначаны спосаб вызначэння адпаведнасці URI для лагінаў пры выкананні такіх дзеянняў, як аўтаматычнае запаўненне."
},
"theme": {
"message": "Тэма"
},
"themeDesc": {
"message": "Змена колеравай тэмы праграмы."
"message": "Змена каляровай тэмы праграмы."
},
"dark": {
"message": "Цёмная",
@@ -646,7 +646,7 @@
"description": "'Solarized' is a noun and the name of a color scheme. It should not be translated."
},
"exportVault": {
"message": "Экспарт сховішча"
"message": "Экспартаваць сховішча"
},
"fileFormat": {
"message": "Фармат файла"
@@ -656,7 +656,7 @@
"description": "WARNING (should stay in capitalized letters if the language permits)"
},
"confirmVaultExport": {
"message": "Пацвердзіць экспарт сховішча"
"message": "Пацвердзіць экспартаванне сховішча"
},
"exportWarningDesc": {
"message": "Экспартуемы файл утрымлівае даныя вашага сховішча ў незашыфраваным фармаце. Яго не варта захоўваць ці адпраўляць па небяспечным каналам (напрыклад, па электроннай пошце). Выдаліце яго адразу пасля выкарыстання."
@@ -668,7 +668,7 @@
"message": "Ключы шыфравання з'яўляюцца ўнікальнымі для кожнага ўліковага запісу Bitwarden, таму нельга імпартаваць зашыфраванае сховішча ў іншы ўліковы запіс."
},
"exportMasterPassword": {
"message": "Увядзіце ваш асноўны пароль для экспарту даных са сховішча."
"message": "Увядзіце ваш асноўны пароль для экспартавання даных сховішча."
},
"shared": {
"message": "Абагуленыя"
@@ -705,13 +705,13 @@
"message": "Даведацца больш"
},
"authenticatorKeyTotp": {
"message": "Ключ праверкі сапраўднасці (TOTP)"
"message": "Ключ аўтэнтыфікацыі (TOTP)"
},
"verificationCodeTotp": {
"message": "Код праверкі (TOTP)"
},
"copyVerificationCode": {
"message": "Капіяваць код праверкі"
"message": "Скапіяваць праверачны код"
},
"attachments": {
"message": "Далучэнні"
@@ -753,7 +753,7 @@
"message": "Прэміяльны статус"
},
"premiumManage": {
"message": "Кіраванне статусам"
"message": "Кіраваць статусам"
},
"premiumManageAlert": {
"message": "Вы можаце кіраваць сваім статусам на bitwarden.com. Перайсці на сайт зараз?"
@@ -798,7 +798,7 @@
"message": "Дзякуем вам за падтрымку Bitwarden."
},
"premiumPrice": {
"message": "Усяго толькі за $PRICE$ на год!",
"message": "Усяго за $PRICE$ у год!",
"placeholders": {
"price": {
"content": "$1",
@@ -810,13 +810,13 @@
"message": "Абнаўленне завершана"
},
"enableAutoTotpCopy": {
"message": "Капіяваць TOTP аўтаматычна"
"message": "Скапіяваць TOTP аўтаматычна"
},
"disableAutoTotpCopyDesc": {
"message": "Калі да вашых уліковых даных прымацаваны ключ праверкі сапраўднасці, то код пацвярджэння TOTP будзе скапіяваны пры аўтазапаўненні ўліковых даных."
"message": "Калі ў лагіна ёсць ключ аўтэнтыфікацыі, то праверачны код TOTP будзе скапіяваны ў буфер абмену пры аўтазапаўненні ўваходу."
},
"enableAutoBiometricsPrompt": {
"message": "Запытваць біяметрыю пры запуску"
"message": "Пытацца пра біяметрыю пры запуску"
},
"premiumRequired": {
"message": "Патрабуецца прэміяльны статус"
@@ -825,10 +825,10 @@
"message": "Для выкарыстання гэтай функцыі патрабуецца прэміяльны статус."
},
"enterVerificationCodeApp": {
"message": "Увядзіце 6 лічбаў кода праверкі з вашай праграмы праверкі сапраўднасці."
"message": "Увядзіце 6 лічбаў праверачнага кода з вашай праграмы аўтэнтыфікацыі."
},
"enterVerificationCodeEmail": {
"message": "Увядзіце 6 лічбаў кода праверкі, які быў адпраўлены на $EMAIL$.",
"message": "Увядзіце 6 лічбаў праверачнага кода, які быў адпраўлены на $EMAIL$.",
"placeholders": {
"email": {
"content": "$1",
@@ -855,10 +855,10 @@
"message": "Выкарыстоўваць іншы метад двухэтапнага ўваходу"
},
"insertYubiKey": {
"message": "Устаўце ваш YubiKey ў порт USB вашага камп'ютара, затым націсніце на кнопку."
"message": "Устаўце свой YubiKey у порт USB камп'ютара, а потым націсніце на кнопку."
},
"insertU2f": {
"message": "Устаўце ваш ключ бяспекі ў порт USB вашага камп'ютара. Калі на ім ёсць кнопка, націсніце на яе."
"message": "Устаўце ваш ключ бяспекі ў порт USB камп'ютара. Калі на ім ёсць кнопка, націсніце на яе."
},
"webAuthnNewTab": {
"message": "Каб пачаць праверку WebAuthn 2FA, націсніце кнопку знізу для адкрыцця новай укладкі і прытрымлівайцеся інструкцый, якія паказаны ў новай укладцы."
@@ -882,13 +882,13 @@
"message": "Параметры двухэтапнага ўваходу"
},
"recoveryCodeDesc": {
"message": "Згубілі доступ да ўсіх варыянтаў двухэтапнага ўваходу? Скарыстайцеся кодам аднаўлення, каб адключыць двухэтапны ўваход для вашага ўліковага запісу."
"message": "Згубілі доступ да ўсіх варыянтаў доступу пастаўшчыкоў двухэтапнай аўтэнтыфікацыі? Скарыстайцеся кодам аднаўлення, каб адключыць праверку пастаўшчыкоў двухэтапнай аўтэнтыфікацыі для вашага ўліковага запісу."
},
"recoveryCodeTitle": {
"message": "Код аднаўлення"
},
"authenticatorAppTitle": {
"message": "Праграма праверкі сапраўднасці"
"message": "Праграма аўтэнтыфікацыі"
},
"authenticatorAppDesc": {
"message": "Выкарыстоўвайце праграму для праверкі сапраўднасці (напрыклад, Authy або Google Authenticator) для стварэння кодаў праверкі на аснове часу.",
@@ -927,22 +927,22 @@
"message": "Увядзіце асноўны URL-адрас на вашым серверы."
},
"customEnvironment": {
"message": "Налады асяроддзя"
"message": "Карыстальніцкае асяроддзе"
},
"customEnvironmentFooter": {
"message": "Для вопытных карыстальнікаў. Можна ўвесці URL-адрасы асобна для кжонай службы."
"message": "Для дасведчаных карыстальнікаў. Можна ўвесці URL-адрасы асобна для кожнай службы."
},
"baseUrl": {
"message": "URL-адрас сервера"
},
"apiUrl": {
"message": "API URL-адраса сервера"
"message": "Сервер URL-адраса API"
},
"webVaultUrl": {
"message": "URL-адрас сервера вэб-сховішча"
},
"identityUrl": {
"message": "URL-адрас сервера ідэнтыфікацыі"
"message": "URL-адрас сервера пасведчання"
},
"notificationsUrl": {
"message": "URL-адрас сервера апавяшчэнняў"
@@ -954,22 +954,22 @@
"message": "URL-адрас сервера асяроддзя захаваны."
},
"enableAutoFillOnPageLoad": {
"message": "Уключыць аўтазапаўненне пры загрузцы старонкі"
"message": "Аўтазапаўненне пры загрузцы старонкі"
},
"enableAutoFillOnPageLoadDesc": {
"message": "Пры выяўленні формы ўваходу выконваецца аўтазапаўненне падчас загрузкі вэб-старонкі."
"message": "Калі выяўлена форма ўваходу, то будзе выканана яе аўтазапаўненне падчас загрузкі вэб-старонкі."
},
"experimentalFeature": {
"message": "Гэта эксперыментальная функцыя. Выкарыстоўвайце на свой страх і рызыку."
},
"defaultAutoFillOnPageLoad": {
"message": "Прадвызначана налада аўтазапаўнення для элементаў уваходу"
"message": "Прадвызначаная налада аўтазапаўнення для элементаў уваходу"
},
"defaultAutoFillOnPageLoadDesc": {
"message": "Вы можаце выключыць аўтазапаўненне на старонцы загрузцы для асобных элементаў уваходу ў меню \"Рэдагаваць\"."
},
"itemAutoFillOnPageLoad": {
"message": "Аўтазапаўненне пры загрузцы (калі ўключана ў параметрах праграмы)"
"message": "Аўтазапаўненне пры загрузцы старонкі (калі ўключана ў параметрах праграмы)"
},
"autoFillOnPageLoadUseDefault": {
"message": "Выкарыстоўваць прадвызначаныя налады"
@@ -987,10 +987,10 @@
"message": "Адкрыць сховішча ў бакавой панэлі"
},
"commandAutofillDesc": {
"message": "Аўтазапаўненне апошніх скарыстаных уліковых даных для бягучага вэб-сайта."
"message": "Аўтазапаўненне апошняга скарыстанага лагіна для бягучага вэб-сайта"
},
"commandGeneratePasswordDesc": {
"message": "Стварыць і капіяваць новы выпадковы парольу буфер абмену."
"message": "Генерыраваць і скапіяваць новы выпадковы пароль у буфер абмену"
},
"commandLockVaultDesc": {
"message": "Заблакіраваць сховішча"
@@ -1002,7 +1002,7 @@
"message": "Карыстальніцкія палі"
},
"copyValue": {
"message": "Капіяваць значэнне"
"message": "Скапіяваць значэнне"
},
"value": {
"message": "Значэнне"
@@ -1020,7 +1020,7 @@
"message": "Схавана"
},
"cfTypeBoolean": {
"message": "Лагічнае"
"message": "Булева"
},
"cfTypeLinked": {
"message": "Звязана",
@@ -1031,7 +1031,7 @@
"description": "This describes a value that is 'linked' (tied) to another value."
},
"popup2faCloseMessage": {
"message": "Націск за межамі гэтага акна для прагляду кода праверкі з электроннай пошты прывядзе да яго закрыцця. Адкрыць bitwarden у новым акне?"
"message": "Націск за межамі ўсплывальнага акна для прагляду праверачнага кода прывядзе да яго закрыцця. Адкрыць гэта ўсплывальнае акно ў новым акне, якое не закрыецца?"
},
"popupU2fCloseMessage": {
"message": "Гэты браўзар не можа апрацоўваць запыты U2F у гэтым усплывальным акне. Вы хочаце адкрыць гэта ўсплывальнае акно ў новым акне, каб мець магчымасць увайсці ў сістэму, выкарыстоўваючы U2F?"
@@ -1058,10 +1058,10 @@
"message": "Тып карткі"
},
"expirationMonth": {
"message": "Месяц заканчэння"
"message": "Месяц завяршэння"
},
"expirationYear": {
"message": "Год заканчэння"
"message": "Год завяршэння"
},
"expiration": {
"message": "Тэрмін дзеяння"
@@ -1079,7 +1079,7 @@
"message": "Красавік"
},
"may": {
"message": "Май"
"message": "Травень"
},
"june": {
"message": "Чэрвень"
@@ -1121,7 +1121,7 @@
"message": "Пані"
},
"dr": {
"message": "Док."
"message": "Доктар"
},
"firstName": {
"message": "Імя"
@@ -1136,7 +1136,7 @@
"message": "Поўнае імя"
},
"identityName": {
"message": "Імя"
"message": "Імя пасведчання"
},
"company": {
"message": "Кампанія"
@@ -1160,13 +1160,13 @@
"message": "Адрас"
},
"address1": {
"message": "Радок адрасу 1"
"message": "Адрас 1"
},
"address2": {
"message": "Радок адрасу 2"
"message": "Адрас 2"
},
"address3": {
"message": "Радок адрасу 3"
"message": "Адрас 3"
},
"cityTown": {
"message": "Горад / Пасёлак"
@@ -1184,7 +1184,7 @@
"message": "Тып"
},
"typeLogin": {
"message": "Уліковыя даныя"
"message": "Лагін"
},
"typeLogins": {
"message": "Уліковыя даныя"
@@ -1223,7 +1223,7 @@
"message": "Пасведчанні"
},
"logins": {
"message": "Уліковыя даныя"
"message": "Лагіны"
},
"secureNotes": {
"message": "Бяспечныя нататкі"
@@ -1256,7 +1256,7 @@
"description": "Domain name. Ex. website.com"
},
"host": {
"message": "Хост",
"message": "Вузел",
"description": "A URL's host value. For example, the host of https://sub.domain.com:443 is 'sub.domain.com:443'."
},
"exact": {
@@ -1274,7 +1274,7 @@
"description": "URI match detection for auto-fill."
},
"defaultMatchDetection": {
"message": "Метад выяўлення па змаўчанні",
"message": "Прадвызначаны метад выяўлення",
"description": "Default URI match detection for auto-fill."
},
"toggleOptions": {
@@ -1285,7 +1285,7 @@
"description": "Toggle the display of the URIs of the currently open tabs in the browser."
},
"currentUri": {
"message": "Бягучы URI укладкі",
"message": "Бягучы URI",
"description": "The URI of one of the current open tabs in the browser."
},
"organization": {
@@ -1305,7 +1305,7 @@
"message": "Выдаліць"
},
"default": {
"message": а змаўчанні"
"message": радвызначана"
},
"dateUpdated": {
"message": "Абноўлена",
@@ -1316,7 +1316,7 @@
"description": "ex. Date this password was updated"
},
"neverLockWarning": {
"message": "Вы ўпэўнены, што хочаце адключыць блакіроўку сховішча? У гэтым выпадку ключ шыфравання вашага сховішча будзе захаваны на вашай прыладзе. Адключаючы блакіроўку, вы павінны пераканацца, што ваша прылада надзейна абаронена."
"message": "Вы ўпэўнены, што хочаце адключыць блакіроўку сховішча? Прызначыўшы параметр блакіравання \"Ніколі\", ключ шыфравання будзе захоўвацца на вашай прыладзе. Калі вы выкарыстоўваеце гэты параметр, вы павінны быць упэўнены ў тым, што ваша прылада надзейна абаронена."
},
"noOrganizationsList": {
"message": "Вы не з'яўляецеся членам якой-небудзь арганізацыі. Арганізацыі дазваляюць бяспечна абменьвацца элементамі з іншымі карыстальнікамі."
@@ -1331,7 +1331,7 @@
"message": "Каму належыць гэты элемент?"
},
"strong": {
"message": "Моцны",
"message": "Надзейны",
"description": "ex. A strong password. Scale: Weak -> Good -> Strong"
},
"good": {
@@ -1339,14 +1339,14 @@
"description": "ex. A good password. Scale: Weak -> Good -> Strong"
},
"weak": {
"message": "Слабы",
"message": "Ненадзейны",
"description": "ex. A weak password. Scale: Weak -> Good -> Strong"
},
"weakMasterPassword": {
"message": "Слабы асноўны пароль"
},
"weakMasterPasswordDesc": {
"message": "Асноўны пароль, выбраны вамі, з'яўляецца слабым. Для належнай абароны ўліковага запісу Bitwarden, вы павінны выкарыстоўваць моцны асноўны пароль (або парольную фразу). Вы ўпэўнены, што хочаце выкарыстоўваць гэты асноўны пароль?"
"message": "Асноўны пароль, які вы выбралі з'яўляецца ненадзейным. Для належнай абароны ўліковага запісу Bitwarden, вы павінны выкарыстоўваць надзейны асноўны пароль (або парольную фразу). Вы ўпэўнены, што хочаце выкарыстоўваць гэты асноўны пароль?"
},
"pin": {
"message": "PIN-код",
@@ -1374,7 +1374,7 @@
"message": "Для ўключэння біяметрыі ў браўзеры, пацвердзіце гэта ў праграме Bitwarden на сваім камп'ютары."
},
"lockWithMasterPassOnRestart": {
"message": "Блакіраваць асноўным паролем пры перазапуску браўзера"
"message": "Заблакіраваць асноўным паролем пры перазапуску браўзера"
},
"selectOneCollection": {
"message": "Вы павінны выбраць прынамсі адну калекцыю."
@@ -1386,7 +1386,7 @@
"message": "Кланіраваць"
},
"passwordGeneratorPolicyInEffect": {
"message": "На налады генератара ўплываюць адна або некалькі палітык арганізацый."
"message": "Адна або больш палітык арганізацыі ўплывае на налады генератара."
},
"vaultTimeoutAction": {
"message": "Дзеянне пры тайм-аўце"
@@ -1415,19 +1415,19 @@
"message": "Аднавіць элемент"
},
"restoreItemConfirmation": {
"message": "Вы сапраўды жадаеце аднавіць гэты элемент?"
"message": "Вы ўпэўнены, што хочаце аднавіць гэты элемент?"
},
"restoredItem": {
"message": "Элемент адноўлены"
},
"vaultTimeoutLogOutConfirmation": {
"message": "Выхад з сістэмы выдаліць доступ да сховішча і спатрабуе праверку сапраўднасці анлайн па заканчэнні перыяду чакання. Вы сапраўды жадаеце ўключыць гэтую наладу?"
"message": "Выхад з сістэмы скасуе ўсе магчымасці доступу да сховішча і запатрабуе аўтэнтыфікацыю праз інтэрнэт пасля завяршэння часу чакання. Вы ўпэўнены, што хочаце выкарыстоўваць гэты параметр?"
},
"vaultTimeoutLogOutConfirmationTitle": {
"message": "Пацвярджэнне дзеяння для тайм-аута"
},
"autoFillAndSave": {
"message": "Запоўніць і захаваць"
"message": "Аўтазапоўніць і захаваць"
},
"autoFillSuccessAndSavedUri": {
"message": "Аўтазапоўнены элемент і захаваны URI"
@@ -1439,7 +1439,7 @@
"message": "Задаць асноўны пароль"
},
"masterPasswordPolicyInEffect": {
"message": "Згодна з адной або некалькімі палітыкамі арганізацыі неабходна, каб ваш асноўны пароль адказваў наступным патрабаванням:"
"message": "Адна або больш палітык арганізацыі патрабуе, каб ваш асноўны пароль адпавядаў наступным патрабаванням:"
},
"policyInEffectMinComplexity": {
"message": "Мінімальны ўзровень складанасці $SCORE$",
@@ -1460,13 +1460,13 @@
}
},
"policyInEffectUppercase": {
"message": "Уключыць адну ці больш прапісных літар"
"message": "Уключыць адну або некалькі вялікіх літар"
},
"policyInEffectLowercase": {
"message": "Уключыць адну ці больш малых літар"
"message": "Уключыць адну або некалькі малых літар"
},
"policyInEffectNumbers": {
"message": "Уключыць адну ці больш лічбаў"
"message": "Уключыць адну або некалькі лічбаў"
},
"policyInEffectSpecial": {
"message": "Уключаць хаця б адзін з наступных спецыяльных сімвалаў $CHARS$",
@@ -1481,7 +1481,7 @@
"message": "Ваш новы асноўны пароль не адпавядае патрабаванням палітыкі арганізацыі."
},
"acceptPolicies": {
"message": "Ставіўшы гэты сцяжок вы пагаджаецеся з наступным:"
"message": "Ставячы гэты сцяжок, вы пагаджаецеся з наступным:"
},
"acceptPoliciesRequired": {
"message": "Умовы выкарыстання і Палітыка прыватнасці не былі пацверджаны."
@@ -1496,7 +1496,7 @@
"message": "Падказка для пароля не можа супадаць з паролем."
},
"ok": {
"message": "ОК"
"message": "Добра"
},
"desktopSyncVerificationTitle": {
"message": "Праверка сінхранізацыі на камп'ютары"
@@ -1538,7 +1538,7 @@
"message": "Біяметрыя не ўключана"
},
"biometricsNotEnabledDesc": {
"message": "Для актывацыі біяметрыі ў браўзеры спачатку неабходна ўключыць біяметрыю ў праграме на камп'ютары."
"message": "Для актывацыі біяметрыі ў браўзеры неабходна спачатку ўключыць яе ў наладах праграмы для камп'ютара."
},
"biometricsNotSupportedTitle": {
"message": "Біяметрыя не падтрымліваецца"
@@ -1559,7 +1559,7 @@
"message": "Гэта дзеянне немагчыма выканаць у бакавой панэлі. Паспрабуйце паўтарыць гэта дзеянне ва ўсплывальным або асобным акне."
},
"personalOwnershipSubmitError": {
"message": "У адпаведнасці з карпаратыўнай палітыкай вам забаронена захоўваць элементы ў асабістым сховішчы. Змяніце параметры ўласнасці на арганізацыю і выберыце з даступных калекцый."
"message": "У адпаведнасці з палітыкай прадпрыемства вам забаронена захоўваць элементы ў асабістым сховішчы. Змяніце параметры ўласнасці на арганізацыю і выберыце з даступных калекцый."
},
"personalOwnershipPolicyInEffect": {
"message": "Палітыка арганізацыі ўплывае на вашы параметры ўласнасці."
@@ -1615,7 +1615,7 @@
"message": "Абаронена паролем"
},
"copySendLink": {
"message": "Капіяваць спасылку Send",
"message": "Скапіяваць спасылку на Send",
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
},
"removePassword": {
@@ -1628,7 +1628,7 @@
"message": "Пароль выдалены"
},
"deletedSend": {
"message": "Выдалены Send",
"message": "Send выдалены",
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
},
"sendLink": {
@@ -1682,7 +1682,7 @@
"message": "1 дзень"
},
"days": {
"message": "$DAYS$ дзён",
"message": "Дзён: $DAYS$",
"placeholders": {
"days": {
"content": "$1",
@@ -1697,7 +1697,7 @@
"message": "Максімальная колькасць доступаў"
},
"maximumAccessCountDesc": {
"message": "Калі зададзена, то карыстальнікі больш не змогуць атрымаць доступ да гэтага Send пасля таго, як будзе дасягнута максімальная колькасць зваротаў.",
"message": "Калі прызначана, то карыстальнікі больш не змогуць атрымаць доступ да гэтага Send пасля таго, як будзе дасягнута максімальная колькасць зваротаў.",
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
},
"sendPasswordDesc": {
@@ -1738,7 +1738,7 @@
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
},
"sendDisabledWarning": {
"message": "У адпаведнасці з карпаратыўнай палітыкай, вы можаце выдаліць толькі бягучы Send.",
"message": "У адпаведнасці з палітыкай прадпрыемства, вы можаце выдаліць толькі бягучы Send.",
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
},
"createdSend": {
@@ -1746,7 +1746,7 @@
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
},
"editedSend": {
"message": "Адрэдагаваны Send",
"message": "Send адрэдагаваны",
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
},
"sendLinuxChromiumFileWarning": {
@@ -1756,7 +1756,7 @@
"message": "Для выбару файла з выкарыстаннем Firefox неабходна адкрыць пашырэнне на бакавой панэлі або перайсці ў новае акно, націснуўшы на гэты банэр."
},
"sendSafariFileWarning": {
"message": "Для выбару файла з выкарыстаннем Safari неабходна перайсці ў асобнае акно, націснуўшы на гэты банэр."
"message": "Для выбару файла з выкарыстаннем Safari неабходна перайсці ў новае акно, націснуўшы на гэты банэр."
},
"sendFileCalloutHeader": {
"message": "Перад тым, як пачаць"
@@ -1795,7 +1795,7 @@
"message": "Адна або больш палітык арганізацыі ўплываюць на параметры Send."
},
"passwordPrompt": {
"message": "Паўторны запыт галоўнага пароля"
"message": "Паўторны запыт асноўнага пароля"
},
"passwordConfirmation": {
"message": "Пацвярджэнне асноўнага пароля"
@@ -1828,7 +1828,7 @@
"message": "Выбраць папку..."
},
"ssoCompleteRegistration": {
"message": "Для завяршэння ўваходу праз SSO, задайце асноўны пароль для доступу і абароны вашаго сховішча."
"message": "Для завяршэння працэсу ўваходу з дапамогай SSO, прызначце асноўны пароль для доступу да вашага сховішча і яго абароны."
},
"hours": {
"message": "Гадзіны"
@@ -1859,13 +1859,13 @@
"message": "Адна або больш палітык арганізацыі не дазваляюць вам экспартаваць асабістае сховішча."
},
"copyCustomFieldNameInvalidElement": {
"message": "Не атрымалася ідэнтыфікаваць дзеючы элемент формы. Паспрабуйце замест гэтага праверыць HTML."
"message": "Немагчыма ідэнтыфікаваць дзеючы элемент формы. Паспрабуйце замест гэтага праверыць HTML."
},
"copyCustomFieldNameNotUnique": {
"message": "Не знойдзены ўнікальны ідэнтыфікатар."
},
"convertOrganizationEncryptionDesc": {
"message": "$ORGANIZATION$ выкарыстоўвае SSO з уласным серверам ключоў. Для аўтарызацыі ўдзельнікам гэтай арганізацыі больш няма неабходнасці выкарыстоўваць асноўны пароль.",
"message": "$ORGANIZATION$ выкарыстоўвае SSO з уласным серверам ключоў. Асноўны пароль для ўдзельнікаў гэтай арганізацыі больш не патрабуецца.",
"placeholders": {
"organization": {
"content": "$1",
@@ -1874,7 +1874,7 @@
}
},
"leaveOrganization": {
"message": "Пакінуць арганізацыю"
"message": "Выйсці з арганізацыі"
},
"removeMasterPassword": {
"message": "Выдаліць асноўны пароль"
@@ -1883,7 +1883,7 @@
"message": "Асноўны пароль выдалены."
},
"leaveOrganizationConfirmation": {
"message": "Вы ўпэўнены, што хочаце пакінуць гэту арганізацыю?"
"message": "Вы ўпэўнены, што хочаце выйсці з гэтай арганізацыі?"
},
"leftOrganization": {
"message": "Вы пакінулі арганізацыю."
@@ -1898,7 +1898,7 @@
"message": "Экспартаванне асабістага сховішча"
},
"exportingPersonalVaultDescription": {
"message": "Толькі элементы асабістага сховішча, якія звязаны з $EMAIL$ будуць экспартаваныя. Элементы сховішча арганізацыі не будуць уключаны.",
"message": "Будуць экспартаваны толькі асабістыя элементы сховішча, якія звязаны з $EMAIL$. Элементы сховішча арганізацыі не будуць уключаны.",
"placeholders": {
"email": {
"content": "$1",
@@ -1975,7 +1975,7 @@
"message": "Арганізацыя адключана."
},
"disabledOrganizationFilterError": {
"message": "Элементы ў адключаных арганізацыя недаступны. Звяжыцеся з уладальнікам вашай арганізацыі для атрымання дапамогі."
"message": "Доступ да элементаў у адключаных арганізацыях немагчымы. Звяжыце з уладальнікам арганізацыі для атрымання дапамогі."
},
"cardBrandMir": {
"message": "Mir"
@@ -1999,16 +1999,16 @@
"message": "для скіду да прадвызначаных наладаў"
},
"serverVersion": {
"message": "Server Version"
"message": "Версія сервера"
},
"selfHosted": {
"message": "Self-Hosted"
"message": "Уласнае размяшчэнне"
},
"thirdParty": {
"message": "Third-Party"
"message": "Іншы пастаўшчык"
},
"thirdPartyServerMessage": {
"message": "Connected to third-party server implementation, $SERVERNAME$. Please verify bugs using the official server, or report them to the third-party server.",
"message": "Падлучэнне да сервера іншага пастаўшчыка $SERVERNAME$. Калі ласка, праверце памылкі з дапамогай афіцыйнага сервера або паведаміце пра іх пастаўшчыку сервера.",
"placeholders": {
"servername": {
"content": "$1",
@@ -2017,7 +2017,7 @@
}
},
"lastSeenOn": {
"message": "last seen on $DATE$",
"message": "апошні раз быў(-ла) $DATE$",
"placeholders": {
"date": {
"content": "$1",

View File

@@ -2002,7 +2002,7 @@
"message": "Версия на сървъра"
},
"selfHosted": {
"message": "Self-Hosted"
"message": "Собствен хостинг"
},
"thirdParty": {
"message": "Third-Party"
@@ -2017,7 +2017,7 @@
}
},
"lastSeenOn": {
"message": "last seen on $DATE$",
"message": "последно видян на $DATE$",
"placeholders": {
"date": {
"content": "$1",

View File

@@ -1999,16 +1999,16 @@
"message": "per restablir els paràmetres preconfigurats"
},
"serverVersion": {
"message": "Server Version"
"message": "Versió del servidor"
},
"selfHosted": {
"message": "Self-Hosted"
"message": "Autoallotjat"
},
"thirdParty": {
"message": "Third-Party"
"message": "Tercers"
},
"thirdPartyServerMessage": {
"message": "Connected to third-party server implementation, $SERVERNAME$. Please verify bugs using the official server, or report them to the third-party server.",
"message": "Connectat a la implementació del servidor de tercers, $SERVERNAME$. Verifiqueu els errors utilitzant el servidor oficial o comuniqueu-los al servidor de tercers.",
"placeholders": {
"servername": {
"content": "$1",
@@ -2017,7 +2017,7 @@
}
},
"lastSeenOn": {
"message": "last seen on $DATE$",
"message": "vist per última vegada el $DATE$",
"placeholders": {
"date": {
"content": "$1",

View File

@@ -1999,16 +1999,16 @@
"message": "for at nulstille til forudkonfigurerede indstillinger"
},
"serverVersion": {
"message": "Server Version"
"message": "Server version"
},
"selfHosted": {
"message": "Self-Hosted"
"message": "Selv-hostet"
},
"thirdParty": {
"message": "Third-Party"
"message": "Tredjepart"
},
"thirdPartyServerMessage": {
"message": "Connected to third-party server implementation, $SERVERNAME$. Please verify bugs using the official server, or report them to the third-party server.",
"message": "Forbundet til tredjepartsserverimplementering, $SERVERNAME$. Kontrollér venligst fejl ved hjælp af den officielle server, eller rapportér dem til tredjepartsserveren.",
"placeholders": {
"servername": {
"content": "$1",
@@ -2017,7 +2017,7 @@
}
},
"lastSeenOn": {
"message": "last seen on $DATE$",
"message": "sidst set den $DATE$",
"placeholders": {
"date": {
"content": "$1",

View File

@@ -149,7 +149,7 @@
"message": "Master-Passwort ändern"
},
"fingerprintPhrase": {
"message": "Prüfschlüssel",
"message": "Fingerabdruck-Phrase",
"description": "A 'fingerprint phrase' is a unique word phrase (similar to a passphrase) that a user can use to authenticate their public key with another user, for the purposes of sharing."
},
"yourAccountsFingerprint": {
@@ -193,7 +193,7 @@
"message": "Es gibt keine Ordner zum Anzeigen."
},
"helpFeedback": {
"message": "Hilfe & Rückmeldung"
"message": "Hilfe & Feedback"
},
"sync": {
"message": "Synchronisierung"
@@ -215,7 +215,7 @@
"message": "Generiert automatisch ein starkes und einzigartiges Passwort."
},
"bitWebVault": {
"message": "Bitwarden Web-Datenspeicher"
"message": "Bitwarden Web-Tresor"
},
"importItems": {
"message": "Einträge importieren"
@@ -1436,7 +1436,7 @@
"message": "Automatisch ausgefüllter Eintrag"
},
"setMasterPassword": {
"message": "Masterpasswort festlegen"
"message": "Master-Passwort festlegen"
},
"masterPasswordPolicyInEffect": {
"message": "Eine oder mehrere Organisationsrichtlinien erfordern, dass dein Masterpasswort die folgenden Anforderungen erfüllt:"
@@ -2002,7 +2002,7 @@
"message": "Server-Version"
},
"selfHosted": {
"message": "Selbstverwaltet"
"message": "Selbst-gehostet"
},
"thirdParty": {
"message": "Drittanbieter"

View File

@@ -1999,7 +1999,7 @@
"message": "επαναφορά στις προ-ρυθμισμένες ρυθμίσεις"
},
"serverVersion": {
"message": "Server Version"
"message": "Έκδοση διακομιστή"
},
"selfHosted": {
"message": "Self-Hosted"

View File

@@ -2017,7 +2017,7 @@
}
},
"lastSeenOn": {
"message": "last seen on $DATE$",
"message": "last seen on: $DATE$",
"placeholders": {
"date": {
"content": "$1",

View File

@@ -56,7 +56,7 @@
"message": "Kutxa gotorra"
},
"myVault": {
"message": "Nire kutxa gotorra"
"message": "Kutxa gotorra"
},
"allVaults": {
"message": "Kutxa gotor guztiak"
@@ -1999,16 +1999,16 @@
"message": "ezarpen lehenetsiak ezartzeko"
},
"serverVersion": {
"message": "Server Version"
"message": "Zerbitzariaren bertsioa"
},
"selfHosted": {
"message": "Self-Hosted"
"message": "Ostatatze propioduna"
},
"thirdParty": {
"message": "Third-Party"
"message": "Hirugarrenen aplikazioak"
},
"thirdPartyServerMessage": {
"message": "Connected to third-party server implementation, $SERVERNAME$. Please verify bugs using the official server, or report them to the third-party server.",
"message": "Hirugarrenen zerbitzariaren inplementaziora konektatuta, $SERVERNAME$. Mesedez, egiaztatu akatsak zerbitzari ofiziala erabiliz, edo galdetu hirugarren zerbitzariari.",
"placeholders": {
"servername": {
"content": "$1",
@@ -2017,7 +2017,7 @@
}
},
"lastSeenOn": {
"message": "last seen on $DATE$",
"message": "Azkenekoz ikusia: $DATE$",
"placeholders": {
"date": {
"content": "$1",

View File

@@ -1999,16 +1999,16 @@
"message": "per ritornare alle impostazioni preconfigurate"
},
"serverVersion": {
"message": "Server Version"
"message": "Versione Server"
},
"selfHosted": {
"message": "Self-Hosted"
},
"thirdParty": {
"message": "Third-Party"
"message": "Terze parti"
},
"thirdPartyServerMessage": {
"message": "Connected to third-party server implementation, $SERVERNAME$. Please verify bugs using the official server, or report them to the third-party server.",
"message": "Connesso a una implementazione server di terze parti, $SERVERNAME$. Controlla i bug utilizzando il server ufficiale o segnalali al server di terze parti.",
"placeholders": {
"servername": {
"content": "$1",
@@ -2017,7 +2017,7 @@
}
},
"lastSeenOn": {
"message": "last seen on $DATE$",
"message": "visto l'ultima volta il $DATE$",
"placeholders": {
"date": {
"content": "$1",

View File

@@ -430,7 +430,7 @@
"message": "마스터 비밀번호를 재입력해야 합니다."
},
"masterPasswordMinlength": {
"message": "Master password must be at least 8 characters long."
"message": "마스터 비밀번호는 최소 8자 이상이어야 합니다."
},
"masterPassDoesntMatch": {
"message": "마스터 비밀번호 확인과 마스터 비밀번호가 일치하지 않습니다."

View File

@@ -1999,16 +1999,16 @@
"message": "lai atiestatītu pirmsuzstādītos iestatījumus"
},
"serverVersion": {
"message": "Server Version"
"message": "Servera versija"
},
"selfHosted": {
"message": "Self-Hosted"
"message": "Pašizvietots"
},
"thirdParty": {
"message": "Third-Party"
"message": "Trešās puses"
},
"thirdPartyServerMessage": {
"message": "Connected to third-party server implementation, $SERVERNAME$. Please verify bugs using the official server, or report them to the third-party server.",
"message": "Savienots ar trešās puses izvietotu serveri $SERVERNAME$. Lūgums pārbaudīt nepilnību esamību oficiālajā serverī vai ziņot par tām trešās puses servera uzturētājiem.",
"placeholders": {
"servername": {
"content": "$1",
@@ -2017,7 +2017,7 @@
}
},
"lastSeenOn": {
"message": "last seen on $DATE$",
"message": "pēdējoreiz manīts $DATE$",
"placeholders": {
"date": {
"content": "$1",

View File

@@ -406,7 +406,7 @@
"message": "Po zablokowaniu komputera"
},
"onRestart": {
"message": "Po uruchomieniu przeglądarki"
"message": "Po restarcie przeglądarki"
},
"never": {
"message": "Nigdy"
@@ -577,16 +577,16 @@
"message": "\"Dodaj powiadomienia logowania\" automatycznie wyświetla monit o zapisanie nowych danych logowania do sejfu przy każdym pierwszym logowaniu."
},
"showCardsCurrentTab": {
"message": "Pokaż karty na stronie Karta"
"message": "Pokaż karty na stronie głównej"
},
"showCardsCurrentTabDesc": {
"message": "Wyświetlaj elementy karty na stronie Karta, aby ułatwić autouzupełnianie."
"message": "Pokaż elementy karty na stronie głównej, aby ułatwić autouzupełnianie."
},
"showIdentitiesCurrentTab": {
"message": "Pokaż tożsamości na stronie Karta"
"message": "Pokaż tożsamości na stronie głównej"
},
"showIdentitiesCurrentTabDesc": {
"message": "Wyświetlaj elementy tożsamości na stronie Karta, aby ułatwić autouzupełnianie."
"message": "Pokaż elementy tożsamości na stronie głównej, aby ułatwić autouzupełnianie."
},
"clearClipboard": {
"message": "Wyczyść schowek",
@@ -621,7 +621,7 @@
"message": "Użyj drugiego kliknięcia, aby uzyskać dostęp do generowania haseł i pasujących danych logowania do witryny. "
},
"defaultUriMatchDetection": {
"message": "Domyślna metoda dopasowania adresu",
"message": "Domyślne wykrywanie dopasowania",
"description": "Default URI match detection for auto-fill."
},
"defaultUriMatchDetectionDesc": {
@@ -1091,7 +1091,7 @@
"message": "Sierpień"
},
"september": {
"message": "September"
"message": "Wrzesień"
},
"october": {
"message": "Październik"

View File

@@ -1969,19 +1969,19 @@
"message": "Erro de Key Connector: certifique-se de que a Key Connector está disponível e funcionando corretamente."
},
"premiumSubcriptionRequired": {
"message": "Premium subscription required"
"message": "Assinatura Premium necessária"
},
"organizationIsDisabled": {
"message": "Organization is disabled."
"message": "Organização está desabilitada."
},
"disabledOrganizationFilterError": {
"message": "Items in disabled Organizations cannot be accessed. Contact your Organization owner for assistance."
"message": "Itens em Organizações Desativadas não podem ser acessados. Entre em contato com o proprietário da sua Organização para obter assistência."
},
"cardBrandMir": {
"message": "Mir"
},
"loggingInTo": {
"message": "Logging in to $DOMAIN$",
"message": "Fazendo login em $DOMAIN$",
"placeholders": {
"domain": {
"content": "$1",
@@ -1990,22 +1990,22 @@
}
},
"settingsEdited": {
"message": "Settings have been edited"
"message": "As configurações foram editadas"
},
"environmentEditedClick": {
"message": "Click here"
"message": "Clique aqui"
},
"environmentEditedReset": {
"message": "to reset to pre-configured settings"
"message": "para redefinir para as configurações pré-configuradas"
},
"serverVersion": {
"message": "Server Version"
"message": "Versão do servidor"
},
"selfHosted": {
"message": "Self-Hosted"
"message": "Auto-hospedado"
},
"thirdParty": {
"message": "Third-Party"
"message": "Terceiros"
},
"thirdPartyServerMessage": {
"message": "Connected to third-party server implementation, $SERVERNAME$. Please verify bugs using the official server, or report them to the third-party server.",

View File

@@ -53,7 +53,7 @@
"message": "Zavihek"
},
"vault": {
"message": "Vault"
"message": "Sef"
},
"myVault": {
"message": "Moj trezor"

View File

@@ -1999,13 +1999,13 @@
"message": "за рисетовање на подразумевана подешавања"
},
"serverVersion": {
"message": "Server Version"
"message": "Верзија сервера"
},
"selfHosted": {
"message": "Self-Hosted"
"message": "Личан хостинг"
},
"thirdParty": {
"message": "Third-Party"
"message": "Трећа страна"
},
"thirdPartyServerMessage": {
"message": "Connected to third-party server implementation, $SERVERNAME$. Please verify bugs using the official server, or report them to the third-party server.",
@@ -2017,7 +2017,7 @@
}
},
"lastSeenOn": {
"message": "last seen on $DATE$",
"message": "последње виђено у $DATE$",
"placeholders": {
"date": {
"content": "$1",

View File

@@ -146,10 +146,10 @@
"message": "Hesap"
},
"changeMasterPassword": {
"message": "Ana Parolayı Değiştir"
"message": "Ana parolayı değiştir"
},
"fingerprintPhrase": {
"message": "Özgün Cümle",
"message": "Parmak izi ifadesi",
"description": "A 'fingerprint phrase' is a unique word phrase (similar to a passphrase) that a user can use to authenticate their public key with another user, for the purposes of sharing."
},
"yourAccountsFingerprint": {
@@ -157,10 +157,10 @@
"description": "A 'fingerprint phrase' is a unique word phrase (similar to a passphrase) that a user can use to authenticate their public key with another user, for the purposes of sharing."
},
"twoStepLogin": {
"message": "İki Aşamalı Giriş"
"message": "İki aşamalı giriş"
},
"logOut": {
"message": ıkış Yap"
"message": ıkış yap"
},
"about": {
"message": "Hakkında"
@@ -193,7 +193,7 @@
"message": "Listelenecek klasör yok."
},
"helpFeedback": {
"message": "Yardım & Geribildirim"
"message": "Yardım ve geribildirim"
},
"sync": {
"message": "Eşitle"
@@ -215,10 +215,10 @@
"message": "Hesaplarınız için otomatik olarak güçlü, özgün parolalar oluşturun."
},
"bitWebVault": {
"message": "Bitwarden Web Kasası"
"message": "Bitwarden web kasası"
},
"importItems": {
"message": "Hesapları İçe Aktar"
"message": "Hesapları içe aktar"
},
"select": {
"message": "Seç"
@@ -367,7 +367,7 @@
"message": "Kasa zaman aşımı"
},
"lockNow": {
"message": "Şimdi Kilitle"
"message": "Şimdi kilitle"
},
"immediately": {
"message": "Hemen"
@@ -403,10 +403,10 @@
"message": "4 saat"
},
"onLocked": {
"message": "Sistem Kilitliyken"
"message": "Sistem kilitlenince"
},
"onRestart": {
"message": "Tarayıcı Yeniden Başlatıldığında"
"message": "Tarayıcı yeniden başlatılınca"
},
"never": {
"message": "Asla"
@@ -574,7 +574,7 @@
"message": "Hesap eklemeyi öner"
},
"addLoginNotificationDesc": {
"message": "\"Hesap Ekle Bildirimi\" otomatik olarak, ilk kez oturum açtığınız hesabınızı kasanıza kaydetmeniz için uyarı verir."
"message": "\"Hesap ekle\" bildirimi, ilk kez kullandığınız hesap bilgilerini kasanıza kaydetmek isteyip istemediğinizi otomatik olarak sorar."
},
"showCardsCurrentTab": {
"message": "Sekme sayfasında kartları göster"
@@ -589,7 +589,7 @@
"message": "Kolay otomatik doldurma için sekme sayfasında kimlik öğelerini listele"
},
"clearClipboard": {
"message": "Panoyu Temizle",
"message": "Panoyu temizle",
"description": "Clipboard is the operating system thing where you copy/paste data to on your device."
},
"clearClipboardDesc": {
@@ -646,7 +646,7 @@
"description": "'Solarized' is a noun and the name of a color scheme. It should not be translated."
},
"exportVault": {
"message": "Kasayı Dışa Aktar"
"message": "Kasayı dışa aktar"
},
"fileFormat": {
"message": "Dosya biçimi"
@@ -750,7 +750,7 @@
"message": "Şifreleme anahtarınızı güncellemeden bu özelliği kullanamazsınız."
},
"premiumMembership": {
"message": "Premium Üyelik"
"message": "Premium üyelik"
},
"premiumManage": {
"message": "Üyeliğimi yönet"
@@ -966,7 +966,7 @@
"message": "Hesaplar için varsayılan otomatik doldurma ayarı"
},
"defaultAutoFillOnPageLoadDesc": {
"message": "\"Sayfa yüklendiğinde otomatik doldur\"u açtıktan sonra her hesap için bu özelliği ayrı ayrııp kapatabilirsiniz. Bu ayar, özellikle ayarlama yapmadığınız hesaplarda kullanılacak varsayılan ayardır."
"message": "\"Sayfa yüklendiğinde otomatik doldur\"u her hesabın \"Düzenle\" görünümünden ayrı ayrı kapatabilirsiniz."
},
"itemAutoFillOnPageLoad": {
"message": "Sayfa yüklendiğinde otomatik doldur (seçeneklerde etkinleştirilmişse)"
@@ -1898,7 +1898,7 @@
"message": "Kişisel Kasayı Dışa Aktar"
},
"exportingPersonalVaultDescription": {
"message": "Yalnızca $EMAIL$ ile ilişkili kişisel kasa öğeleri dışa aktarılacaktır. Kuruluş kasası öğeleri dahil edilmeyecektir.",
"message": "Yalnızca $EMAIL$ ile ilişkili kişisel kasadaki kayıtlar dışa aktarılacaktır. Kuruluş kasasındaki kayıtlar dahil edilmeyecektir.",
"placeholders": {
"email": {
"content": "$1",
@@ -2008,7 +2008,7 @@
"message": "Üçüncü Taraf"
},
"thirdPartyServerMessage": {
"message": "Connected to third-party server implementation, $SERVERNAME$. Please verify bugs using the official server, or report them to the third-party server.",
"message": "$SERVERNAME$ adresindeki üçüncü taraf sunucuya bağlandınız. Lütfen resmi sunucuyu kullanarak hataları doğrulayın veya üçüncü taraf sunucuya bildirin.",
"placeholders": {
"servername": {
"content": "$1",

View File

@@ -567,7 +567,7 @@
"message": "搜索类型"
},
"noneFolder": {
"message": "默认文件夹",
"message": "文件夹",
"description": "This is the folder for uncategorized items"
},
"enableAddLoginNotification": {
@@ -2008,7 +2008,7 @@
"message": "第三方"
},
"thirdPartyServerMessage": {
"message": "已连接到第三方服务器,$SERVERNAME$。请使用官方服务器验证错误,或将其报告给第三方服务器。",
"message": "已连接到第三方服务器实现$SERVERNAME$。请使用官方服务器验证错误,或将其报告给第三方服务器。",
"placeholders": {
"servername": {
"content": "$1",

View File

@@ -16,7 +16,7 @@ import { KeyConnectorService as KeyConnectorServiceAbstraction } from "@bitwarde
import { LogService as LogServiceAbstraction } from "@bitwarden/common/abstractions/log.service";
import { MessagingService as MessagingServiceAbstraction } from "@bitwarden/common/abstractions/messaging.service";
import { NotificationsService as NotificationsServiceAbstraction } from "@bitwarden/common/abstractions/notifications.service";
import { OrganizationService as OrganizationServiceAbstraction } from "@bitwarden/common/abstractions/organization.service";
import { OrganizationService as OrganizationServiceAbstraction } from "@bitwarden/common/abstractions/organization/organization.service.abstraction";
import { PasswordGenerationService as PasswordGenerationServiceAbstraction } from "@bitwarden/common/abstractions/passwordGeneration.service";
import { PlatformUtilsService as PlatformUtilsServiceAbstraction } from "@bitwarden/common/abstractions/platformUtils.service";
import { PolicyApiServiceAbstraction } from "@bitwarden/common/abstractions/policy/policy-api.service.abstraction";
@@ -27,6 +27,7 @@ import { SendService as SendServiceAbstraction } from "@bitwarden/common/abstrac
import { SettingsService as SettingsServiceAbstraction } from "@bitwarden/common/abstractions/settings.service";
import { AbstractStorageService } from "@bitwarden/common/abstractions/storage.service";
import { SyncService as SyncServiceAbstraction } from "@bitwarden/common/abstractions/sync/sync.service.abstraction";
import { SyncNotifierService as SyncNotifierServiceAbstraction } from "@bitwarden/common/abstractions/sync/syncNotifier.service.abstraction";
import { SystemService as SystemServiceAbstraction } from "@bitwarden/common/abstractions/system.service";
import { TokenService as TokenServiceAbstraction } from "@bitwarden/common/abstractions/token.service";
import { TotpService as TotpServiceAbstraction } from "@bitwarden/common/abstractions/totp.service";
@@ -58,7 +59,7 @@ import { FolderApiService } from "@bitwarden/common/services/folder/folder-api.s
import { KeyConnectorService } from "@bitwarden/common/services/keyConnector.service";
import { MemoryStorageService } from "@bitwarden/common/services/memoryStorage.service";
import { NotificationsService } from "@bitwarden/common/services/notifications.service";
import { OrganizationService } from "@bitwarden/common/services/organization.service";
import { OrganizationService } from "@bitwarden/common/services/organization/organization.service";
import { PasswordGenerationService } from "@bitwarden/common/services/passwordGeneration.service";
import { PolicyApiService } from "@bitwarden/common/services/policy/policy-api.service";
import { PolicyService } from "@bitwarden/common/services/policy/policy.service";
@@ -68,6 +69,7 @@ import { SendService } from "@bitwarden/common/services/send.service";
import { SettingsService } from "@bitwarden/common/services/settings.service";
import { StateMigrationService } from "@bitwarden/common/services/stateMigration.service";
import { SyncService } from "@bitwarden/common/services/sync/sync.service";
import { SyncNotifierService } from "@bitwarden/common/services/sync/syncNotifier.service";
import { SystemService } from "@bitwarden/common/services/system.service";
import { TokenService } from "@bitwarden/common/services/token.service";
import { TotpService } from "@bitwarden/common/services/totp.service";
@@ -158,6 +160,7 @@ export default class MainBackground {
folderApiService: FolderApiServiceAbstraction;
policyApiService: PolicyApiServiceAbstraction;
userVerificationApiService: UserVerificationApiServiceAbstraction;
syncNotifierService: SyncNotifierServiceAbstraction;
// Passed to the popup for Safari to workaround issues with theming, downloading, etc.
backgroundWindow = window;
@@ -298,7 +301,8 @@ export default class MainBackground {
this.cryptoFunctionService,
this.stateService
);
this.organizationService = new OrganizationService(this.stateService);
this.syncNotifierService = new SyncNotifierService();
this.organizationService = new OrganizationService(this.stateService, this.syncNotifierService);
this.policyService = new PolicyService(this.stateService, this.organizationService);
this.policyApiService = new PolicyApiService(
this.policyService,
@@ -388,9 +392,9 @@ export default class MainBackground {
this.logService,
this.keyConnectorService,
this.stateService,
this.organizationService,
this.providerService,
this.folderApiService,
this.syncNotifierService,
logoutCallback
);
this.eventService = new EventService(

View File

@@ -1,12 +1,17 @@
import { OrganizationService as AbstractOrganizationService } from "@bitwarden/common/abstractions/organization.service";
import { OrganizationService } from "@bitwarden/common/services/organization.service";
import { OrganizationService as AbstractOrganizationService } from "@bitwarden/common/abstractions/organization/organization.service.abstraction";
import { OrganizationService } from "@bitwarden/common/services/organization/organization.service";
import { FactoryOptions, CachedServices, factory } from "./factory-options";
import { stateServiceFactory, StateServiceInitOptions } from "./state-service.factory";
import {
syncNotifierServiceFactory,
SyncNotifierServiceInitOptions,
} from "./sync-notifier-service.factory";
type OrganizationServiceFactoryOptions = FactoryOptions;
export type OrganizationServiceInitOptions = OrganizationServiceFactoryOptions &
SyncNotifierServiceInitOptions &
StateServiceInitOptions;
export function organizationServiceFactory(
@@ -17,6 +22,10 @@ export function organizationServiceFactory(
cache,
"organizationService",
opts,
async () => new OrganizationService(await stateServiceFactory(cache, opts))
async () =>
new OrganizationService(
await stateServiceFactory(cache, opts),
await syncNotifierServiceFactory(cache, opts)
)
);
}

View File

@@ -0,0 +1,17 @@
import { SyncNotifierService as AbstractSyncNotifierService } from "@bitwarden/common/abstractions/sync/syncNotifier.service.abstraction";
import { SyncNotifierService } from "@bitwarden/common/services/sync/syncNotifier.service";
import { FactoryOptions, CachedServices, factory } from "./factory-options";
type SyncNotifierServiceFactoryOptions = FactoryOptions;
export type SyncNotifierServiceInitOptions = SyncNotifierServiceFactoryOptions;
export function syncNotifierServiceFactory(
cache: { syncNotifierService?: AbstractSyncNotifierService } & CachedServices,
opts: SyncNotifierServiceInitOptions
): Promise<AbstractSyncNotifierService> {
return factory(cache, "syncNotifierService", opts, () =>
Promise.resolve(new SyncNotifierService())
);
}

View File

@@ -19,6 +19,7 @@ describe("sessionSync decorator", () => {
ctor: ctor,
initializer: initializer,
}),
testClass.testProperty.complete(),
]);
});
});

View File

@@ -5,6 +5,7 @@ import { BrowserApi } from "../../browser/browserApi";
import { StateService } from "../../services/abstractions/state.service";
import { SessionSyncer } from "./session-syncer";
import { SyncedItemMetadata } from "./sync-item-metadata";
describe("session syncer", () => {
const propertyKey = "behaviorSubject";
@@ -140,12 +141,14 @@ describe("session syncer", () => {
});
it("should update from message on emit from another instance", async () => {
const builder = jest.fn();
jest.spyOn(SyncedItemMetadata, "builder").mockReturnValue(builder);
stateService.getFromSessionMemory.mockResolvedValue("test");
await sut.updateFromMessage({ command: `${sessionKey}_update`, id: "different_id" });
expect(stateService.getFromSessionMemory).toHaveBeenCalledTimes(1);
expect(stateService.getFromSessionMemory).toHaveBeenCalledWith(sessionKey);
expect(stateService.getFromSessionMemory).toHaveBeenCalledWith(sessionKey, builder);
expect(nextSpy).toHaveBeenCalledTimes(1);
expect(nextSpy).toHaveBeenCalledWith("test");

View File

@@ -66,8 +66,8 @@ export class SessionSyncer {
if (message.command != this.updateMessageCommand || message.id === this.id) {
return;
}
const keyValuePair = await this.stateService.getFromSessionMemory(this.metaData.sessionKey);
const value = SyncedItemMetadata.buildFromKeyValuePair(keyValuePair, this.metaData);
const builder = SyncedItemMetadata.builder(this.metaData);
const value = await this.stateService.getFromSessionMemory(this.metaData.sessionKey, builder);
this.ignoreNextUpdate = true;
this.behaviorSubject.next(value);
}

View File

@@ -5,19 +5,15 @@ export class SyncedItemMetadata {
initializer?: (keyValuePair: any) => any;
initializeAsArray?: boolean;
static buildFromKeyValuePair(keyValuePair: any, metadata: SyncedItemMetadata): any {
const builder = SyncedItemMetadata.getBuilder(metadata);
static builder(metadata: SyncedItemMetadata): (o: any) => any {
const itemBuilder =
metadata.initializer != null
? metadata.initializer
: (o: any) => Object.assign(new metadata.ctor(), o);
if (metadata.initializeAsArray) {
return keyValuePair.map((o: any) => builder(o));
return (keyValuePair: any) => keyValuePair.map((o: any) => itemBuilder(o));
} else {
return builder(keyValuePair);
return (keyValuePair: any) => itemBuilder(keyValuePair);
}
}
private static getBuilder(metadata: SyncedItemMetadata): (o: any) => any {
return metadata.initializer != null
? metadata.initializer
: (o: any) => Object.assign(new metadata.ctor(), o);
}
}

View File

@@ -1,59 +1,39 @@
import { SyncedItemMetadata } from "./sync-item-metadata";
describe("build from key value pair", () => {
describe("builder", () => {
const propertyKey = "propertyKey";
const key = "key";
const initializer = (s: any) => "used initializer";
class TestClass {}
const ctor = TestClass;
it("should call initializer if provided", () => {
const actual = SyncedItemMetadata.buildFromKeyValuePair(
{},
{
propertyKey,
sessionKey: "key",
initializer: initializer,
}
);
expect(actual).toEqual("used initializer");
it("should use initializer if provided", () => {
const metadata = { propertyKey, sessionKey: key, initializer };
const builder = SyncedItemMetadata.builder(metadata);
expect(builder({})).toBe("used initializer");
});
it("should call ctor if provided", () => {
const expected = { provided: "value" };
const actual = SyncedItemMetadata.buildFromKeyValuePair(expected, {
propertyKey,
sessionKey: key,
ctor: ctor,
});
expect(actual).toBeInstanceOf(ctor);
expect(actual).toEqual(expect.objectContaining(expected));
it("should use ctor if initializer is not provided", () => {
const metadata = { propertyKey, sessionKey: key, ctor };
const builder = SyncedItemMetadata.builder(metadata);
expect(builder({})).toBeInstanceOf(TestClass);
});
it("should prefer using initializer if both are provided", () => {
const actual = SyncedItemMetadata.buildFromKeyValuePair(
{},
{
propertyKey,
sessionKey: key,
initializer: initializer,
ctor: ctor,
}
);
expect(actual).toEqual("used initializer");
it("should prefer initializer over ctor", () => {
const metadata = { propertyKey, sessionKey: key, ctor, initializer };
const builder = SyncedItemMetadata.builder(metadata);
expect(builder({})).toBe("used initializer");
});
it("should honor initialize as array", () => {
const actual = SyncedItemMetadata.buildFromKeyValuePair([1, 2], {
const metadata = {
propertyKey,
sessionKey: key,
initializer: initializer,
initializeAsArray: true,
});
expect(actual).toEqual(["used initializer", "used initializer"]);
};
const builder = SyncedItemMetadata.builder(metadata);
expect(builder([{}])).toBeInstanceOf(Array);
expect(builder([{}])[0]).toBe("used initializer");
});
});

View File

@@ -2,7 +2,7 @@
"manifest_version": 2,
"name": "__MSG_extName__",
"short_name": "__MSG_appName__",
"version": "2022.9.0",
"version": "2022.9.1",
"description": "__MSG_extDesc__",
"default_locale": "en",
"author": "Bitwarden Inc.",

View File

@@ -3,7 +3,7 @@
"minimum_chrome_version": "102.0",
"name": "__MSG_extName__",
"short_name": "__MSG_appName__",
"version": "2022.9.0",
"version": "2022.9.1",
"description": "__MSG_extDesc__",
"default_locale": "en",
"author": "Bitwarden Inc.",

View File

@@ -1,4 +1,4 @@
<form #form (ngSubmit)="submit()" [appApiAction]="formPromise">
<form #form (ngSubmit)="submit()" [appApiAction]="formPromise" [formGroup]="formGroup">
<header>
<div class="left">
<button type="button" routerLink="/home">{{ "cancel" | i18n }}</button>
@@ -18,15 +18,7 @@
<div class="box-content">
<div class="box-content-row" appBoxRow>
<label for="email">{{ "emailAddress" | i18n }}</label>
<input
id="email"
type="text"
name="Email"
[(ngModel)]="email"
required
inputmode="email"
appInputVerbatim="false"
/>
<input id="email" type="email" formControlName="email" appInputVerbatim="false" />
</div>
<div class="box-content-row box-content-row-flex" appBoxRow>
<div class="row-main">
@@ -34,10 +26,8 @@
<input
id="masterPassword"
type="{{ showPassword ? 'text' : 'password' }}"
name="MasterPassword"
class="monospaced"
[(ngModel)]="masterPassword"
required
formControlName="masterPassword"
appInputVerbatim
/>
</div>

View File

@@ -1,10 +1,12 @@
import { Component, NgZone } from "@angular/core";
import { FormBuilder } from "@angular/forms";
import { Router } from "@angular/router";
import { LoginComponent as BaseLoginComponent } from "@bitwarden/angular/components/login.component";
import { AuthService } from "@bitwarden/common/abstractions/auth.service";
import { CryptoFunctionService } from "@bitwarden/common/abstractions/cryptoFunction.service";
import { EnvironmentService } from "@bitwarden/common/abstractions/environment.service";
import { FormValidationErrorsService } from "@bitwarden/common/abstractions/formValidationErrors.service";
import { I18nService } from "@bitwarden/common/abstractions/i18n.service";
import { LogService } from "@bitwarden/common/abstractions/log.service";
import { PasswordGenerationService } from "@bitwarden/common/abstractions/passwordGeneration.service";
@@ -30,7 +32,9 @@ export class LoginComponent extends BaseLoginComponent {
protected cryptoFunctionService: CryptoFunctionService,
syncService: SyncService,
logService: LogService,
ngZone: NgZone
ngZone: NgZone,
formBuilder: FormBuilder,
formValidationErrorService: FormValidationErrorsService
) {
super(
authService,
@@ -42,7 +46,9 @@ export class LoginComponent extends BaseLoginComponent {
passwordGenerationService,
cryptoFunctionService,
logService,
ngZone
ngZone,
formBuilder,
formValidationErrorService
);
super.onSuccessfulLogin = async () => {
await syncService.fullSync(true);

View File

@@ -15,7 +15,7 @@
<app-vault-icon [cipher]="cipher"></app-vault-icon>
<div class="row-main-content">
<span class="text">
{{ cipher.name }}
{{ cipher.name | ellipsis: 20 }}
<ng-container *ngIf="cipher.organizationId">
<i
class="bwi bwi-collection text-muted"

View File

@@ -26,7 +26,7 @@ import { KeyConnectorService } from "@bitwarden/common/abstractions/keyConnector
import { LogService as LogServiceAbstraction } from "@bitwarden/common/abstractions/log.service";
import { MessagingService } from "@bitwarden/common/abstractions/messaging.service";
import { NotificationsService } from "@bitwarden/common/abstractions/notifications.service";
import { OrganizationService } from "@bitwarden/common/abstractions/organization.service";
import { OrganizationService } from "@bitwarden/common/abstractions/organization/organization.service.abstraction";
import { PasswordGenerationService } from "@bitwarden/common/abstractions/passwordGeneration.service";
import { PasswordRepromptService as PasswordRepromptServiceAbstraction } from "@bitwarden/common/abstractions/passwordReprompt.service";
import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service";

View File

@@ -14,7 +14,7 @@
<p class="text-center" *ngIf="isCloud">
{{ "serverVersion" | i18n }}: {{ this.serverConfig?.version }}
<span *ngIf="!serverConfig.isValid()">
({{ "lastSeenOn" | i18n }}: {{ serverConfig.utcDate | date: "mediumDate" }})
({{ "lastSeenOn" | i18n: (serverConfig.utcDate | date: "mediumDate") }})
</span>
</p>
@@ -24,7 +24,7 @@
{{ "serverVersion" | i18n }} <small>({{ "thirdParty" | i18n }})</small>:
{{ this.serverConfig?.version }}
<span *ngIf="!serverConfig.isValid()">
({{ "lastSeenOn" | i18n }}: {{ serverConfig.utcDate | date: "mediumDate" }})
({{ "lastSeenOn" | i18n: (serverConfig.utcDate | date: "mediumDate") }})
</span>
</p>
<div class="text-center">
@@ -36,7 +36,7 @@
{{ "serverVersion" | i18n }} <small>({{ "selfHosted" | i18n }})</small>:
{{ this.serverConfig?.version }}
<span *ngIf="!serverConfig.isValid()">
({{ "lastSeenOn" | i18n }}: {{ serverConfig.utcDate | date: "mediumDate" }})
({{ "lastSeenOn" | i18n: (serverConfig.utcDate | date: "mediumDate") }})
</span>
</p>
</ng-container>

View File

@@ -12,7 +12,7 @@ import { FolderService } from "@bitwarden/common/abstractions/folder/folder.serv
import { I18nService } from "@bitwarden/common/abstractions/i18n.service";
import { LogService } from "@bitwarden/common/abstractions/log.service";
import { MessagingService } from "@bitwarden/common/abstractions/messaging.service";
import { OrganizationService } from "@bitwarden/common/abstractions/organization.service";
import { OrganizationService } from "@bitwarden/common/abstractions/organization/organization.service.abstraction";
import { PasswordRepromptService } from "@bitwarden/common/abstractions/passwordReprompt.service";
import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service";
import { PolicyService } from "@bitwarden/common/abstractions/policy/policy.service.abstraction";

View File

@@ -10,7 +10,7 @@ import { CipherService } from "@bitwarden/common/abstractions/cipher.service";
import { CollectionService } from "@bitwarden/common/abstractions/collection.service";
import { FolderService } from "@bitwarden/common/abstractions/folder/folder.service.abstraction";
import { I18nService } from "@bitwarden/common/abstractions/i18n.service";
import { OrganizationService } from "@bitwarden/common/abstractions/organization.service";
import { OrganizationService } from "@bitwarden/common/abstractions/organization/organization.service.abstraction";
import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service";
import { SearchService } from "@bitwarden/common/abstractions/search.service";
import { CipherType } from "@bitwarden/common/enums/cipherType";
@@ -78,7 +78,7 @@ export class CiphersComponent extends BaseCiphersComponent implements OnInit, On
async ngOnInit() {
this.searchTypeSearch = !this.platformUtilsService.isSafari();
this.showOrganizations = await this.organizationService.hasOrganizations();
this.showOrganizations = this.organizationService.hasOrganizations();
this.vaultFilter = this.vaultFilterService.getVaultFilter();
// eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe
this.route.queryParams.pipe(first()).subscribe(async (params) => {

View File

@@ -4,7 +4,7 @@ import { Router } from "@angular/router";
import { BroadcasterService } from "@bitwarden/common/abstractions/broadcaster.service";
import { CipherService } from "@bitwarden/common/abstractions/cipher.service";
import { I18nService } from "@bitwarden/common/abstractions/i18n.service";
import { OrganizationService } from "@bitwarden/common/abstractions/organization.service";
import { OrganizationService } from "@bitwarden/common/abstractions/organization/organization.service.abstraction";
import { PasswordRepromptService } from "@bitwarden/common/abstractions/passwordReprompt.service";
import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service";
import { SearchService } from "@bitwarden/common/abstractions/search.service";
@@ -219,7 +219,7 @@ export class CurrentTabComponent implements OnInit, OnDestroy {
const otherTypes: CipherType[] = [];
const dontShowCards = await this.stateService.getDontShowCardsCurrentTab();
const dontShowIdentities = await this.stateService.getDontShowIdentitiesCurrentTab();
this.showOrganizations = await this.organizationService.hasOrganizations();
this.showOrganizations = this.organizationService.hasOrganizations();
if (!dontShowCards) {
otherTypes.push(CipherType.Card);
}

View File

@@ -1,70 +1,76 @@
<form #form (ngSubmit)="submit()" [appApiAction]="formPromise">
<header>
<div class="left">
<button type="button" (click)="cancel()">{{ "cancel" | i18n }}</button>
</div>
<h1 class="center">
<span class="title">{{ "moveToOrganization" | i18n }}</span>
</h1>
<div class="right">
<button
type="submit"
[disabled]="form.loading || !canSave"
*ngIf="organizations && organizations.length"
>
<span [hidden]="form.loading">{{ "move" | i18n }}</span>
<i class="bwi bwi-spinner bwi-lg bwi-spin" [hidden]="!form.loading" aria-hidden="true"></i>
</button>
</div>
</header>
<main tabindex="-1">
<div class="box">
<div class="box-content" *ngIf="!organizations || !organizations.length">
<div class="box-content-row padded no-hover">
{{ "noOrganizationsList" | i18n }}
</div>
<ng-container *ngIf="organizations$ | async as organizations">
<header>
<div class="left">
<button type="button" (click)="cancel()">{{ "cancel" | i18n }}</button>
</div>
<div class="box-content" *ngIf="organizations && organizations.length">
<div class="box-content-row" appBoxRow>
<label for="organization">{{ "organization" | i18n }}</label>
<select
id="organization"
name="OrganizationId"
[(ngModel)]="organizationId"
(change)="filterCollections()"
>
<option *ngFor="let o of organizations" [ngValue]="o.id">{{ o.name }}</option>
</select>
</div>
</div>
<div class="box-footer">
{{ "moveToOrgDesc" | i18n }}
</div>
</div>
<div class="box" *ngIf="organizations && organizations.length">
<h2 class="box-header">
{{ "collections" | i18n }}
</h2>
<div class="box-content" *ngIf="!collections || !collections.length">
<div class="box-content-row padded no-hover">
{{ "noCollectionsInList" | i18n }}
</div>
</div>
<div class="box-content" *ngIf="collections && collections.length">
<div
class="box-content-row box-content-row-checkbox"
*ngFor="let c of collections; let i = index"
appBoxRow
<h1 class="center">
<span class="title">{{ "moveToOrganization" | i18n }}</span>
</h1>
<div class="right">
<button
type="submit"
[disabled]="form.loading || !canSave"
*ngIf="organizations && organizations.length"
>
<label for="collection_{{ i }}">{{ c.name }}</label>
<input
id="collection_{{ i }}"
type="checkbox"
[(ngModel)]="c.checked"
name="Collection[{{ i }}].Checked"
/>
<span [hidden]="form.loading">{{ "move" | i18n }}</span>
<i
class="bwi bwi-spinner bwi-lg bwi-spin"
[hidden]="!form.loading"
aria-hidden="true"
></i>
</button>
</div>
</header>
<main tabindex="-1">
<div class="box">
<div class="box-content" *ngIf="!organizations || !organizations.length">
<div class="box-content-row padded no-hover">
{{ "noOrganizationsList" | i18n }}
</div>
</div>
<div class="box-content" *ngIf="organizations && organizations.length">
<div class="box-content-row" appBoxRow>
<label for="organization">{{ "organization" | i18n }}</label>
<select
id="organization"
name="OrganizationId"
[(ngModel)]="organizationId"
(change)="filterCollections()"
>
<option *ngFor="let o of organizations" [ngValue]="o.id">{{ o.name }}</option>
</select>
</div>
</div>
<div class="box-footer">
{{ "moveToOrgDesc" | i18n }}
</div>
</div>
</div>
</main>
<div class="box" *ngIf="organizations && organizations.length">
<h2 class="box-header">
{{ "collections" | i18n }}
</h2>
<div class="box-content" *ngIf="!collections || !collections.length">
<div class="box-content-row padded no-hover">
{{ "noCollectionsInList" | i18n }}
</div>
</div>
<div class="box-content" *ngIf="collections && collections.length">
<div
class="box-content-row box-content-row-checkbox"
*ngFor="let c of collections; let i = index"
appBoxRow
>
<label for="collection_{{ i }}">{{ c.name }}</label>
<input
id="collection_{{ i }}"
type="checkbox"
[(ngModel)]="c.checked"
name="Collection[{{ i }}].Checked"
/>
</div>
</div>
</div>
</main>
</ng-container>
</form>

View File

@@ -7,7 +7,7 @@ import { CipherService } from "@bitwarden/common/abstractions/cipher.service";
import { CollectionService } from "@bitwarden/common/abstractions/collection.service";
import { I18nService } from "@bitwarden/common/abstractions/i18n.service";
import { LogService } from "@bitwarden/common/abstractions/log.service";
import { OrganizationService } from "@bitwarden/common/abstractions/organization.service";
import { OrganizationService } from "@bitwarden/common/abstractions/organization/organization.service.abstraction";
import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service";
@Component({

View File

@@ -1,64 +1,70 @@
<div class="content org-filter-content" *ngIf="loaded && show">
<button
#toggleVaults
class="org-filter"
(click)="openOverlay()"
aria-haspopup="menu"
aria-controls="cdk-overlay-container"
[attr.aria-expanded]="isOpen"
[attr.aria-label]="vaultFilterDisplay"
>
{{ vaultFilterDisplay | ellipsis: 45 }}&nbsp;
<i
class="bwi bwi-sm"
aria-hidden="true"
[ngClass]="{ 'bwi-angle-down': !isOpen, 'bwi-chevron-up': isOpen }"
></i>
</button>
<ng-template class="vault-select-container" #vaultSelectorTemplate>
<div
class="vault-select"
[@transformPanel]="'open'"
cdkTrapFocus
cdkTrapFocusAutoCapture
role="dialog"
aria-modal="true"
>
<ng-container *ngIf="loaded && organizations$ | async as organizations">
<div class="content org-filter-content" *ngIf="loaded && shouldShow(organizations)">
<ng-container *ngIf="selectedVault$ | async as vaultFilterDisplay">
<button
appStopClick
(click)="selectAllVaults()"
[ngClass]="{ active: !myVaultOnly && !selectOrganizationId }"
#toggleVaults
class="org-filter"
(click)="openOverlay()"
aria-haspopup="menu"
aria-controls="cdk-overlay-container"
[attr.aria-expanded]="isOpen"
[attr.aria-label]="vaultFilterDisplay"
>
<i class="bwi bwi-fw bwi-filter" aria-hidden="true"></i>
&nbsp;{{ "allVaults" | i18n }}
{{ vaultFilterDisplay | ellipsis: 45 }}&nbsp;
<i
class="bwi bwi-sm"
aria-hidden="true"
[ngClass]="{ 'bwi-angle-down': !isOpen, 'bwi-chevron-up': isOpen }"
></i>
</button>
<button *ngIf="!enforcePersonalOwnwership" appStopClick (click)="selectMyVault()">
<i class="bwi bwi-fw bwi-user" aria-hidden="true"></i>
&nbsp;{{ "myVault" | i18n }}
</button>
<button
*ngFor="let organization of organizations"
appStopClick
(click)="selectOrganization(organization)"
</ng-container>
<ng-template class="vault-select-container" #vaultSelectorTemplate>
<div
class="vault-select"
[@transformPanel]="'open'"
cdkTrapFocus
cdkTrapFocusAutoCapture
role="dialog"
aria-modal="true"
>
<i
*ngIf="organization.planProductType !== 1"
class="bwi bwi-fw bwi-business"
aria-hidden="true"
></i>
<i
*ngIf="organization.planProductType === 1"
class="bwi bwi-fw bwi-family"
aria-hidden="true"
></i>
<span>&nbsp;{{ organization.name | ellipsis: (organization.enabled ? 21 : 18):true }}</span>
<i
*ngIf="!organization.enabled"
class="bwi bwi-fw bwi-exclamation-triangle text-danger"
aria-label="{{ 'organizationIsDisabled' | i18n }}"
appA11yTitle="{{ 'organizationIsDisabled' | i18n }}"
></i>
</button>
</div>
</ng-template>
</div>
<button
appStopClick
(click)="selectAllVaults()"
[ngClass]="{ active: !myVaultOnly && !selectOrganizationId }"
>
<i class="bwi bwi-fw bwi-filter" aria-hidden="true"></i>
&nbsp;{{ "allVaults" | i18n }}
</button>
<button *ngIf="!enforcePersonalOwnwership" appStopClick (click)="selectMyVault()">
<i class="bwi bwi-fw bwi-user" aria-hidden="true"></i>
&nbsp;{{ "myVault" | i18n }}
</button>
<button
*ngFor="let organization of organizations"
appStopClick
(click)="selectOrganization(organization)"
>
<i
*ngIf="organization.planProductType !== 1"
class="bwi bwi-fw bwi-business"
aria-hidden="true"
></i>
<i
*ngIf="organization.planProductType === 1"
class="bwi bwi-fw bwi-family"
aria-hidden="true"
></i>
<span
>&nbsp;{{ organization.name | ellipsis: (organization.enabled ? 21 : 18):true }}</span
>
<i
*ngIf="!organization.enabled"
class="bwi bwi-fw bwi-exclamation-triangle text-danger"
aria-label="{{ 'organizationIsDisabled' | i18n }}"
appA11yTitle="{{ 'organizationIsDisabled' | i18n }}"
></i>
</button>
</div>
</ng-template>
</div>
</ng-container>

View File

@@ -5,19 +5,19 @@ import {
Component,
ElementRef,
EventEmitter,
NgZone,
OnInit,
Output,
TemplateRef,
ViewChild,
ViewContainerRef,
HostListener,
OnDestroy,
} from "@angular/core";
import { merge } from "rxjs";
import { BehaviorSubject, concatMap, map, merge, Observable, Subject, takeUntil } from "rxjs";
import { VaultFilter } from "@bitwarden/angular/vault/vault-filter/models/vault-filter.model";
import { BroadcasterService } from "@bitwarden/common/abstractions/broadcaster.service";
import { I18nService } from "@bitwarden/common/abstractions/i18n.service";
import { OrganizationService } from "@bitwarden/common/abstractions/organization/organization.service.abstraction";
import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service";
import { Organization } from "@bitwarden/common/models/domain/organization";
@@ -47,20 +47,22 @@ import { VaultFilterService } from "../../services/vaultFilter.service";
]),
],
})
// eslint-disable-next-line rxjs-angular/prefer-takeuntil
export class VaultSelectComponent implements OnInit {
export class VaultSelectComponent implements OnInit, OnDestroy {
@Output() onVaultSelectionChanged = new EventEmitter();
@ViewChild("toggleVaults", { read: ElementRef })
buttonRef: ElementRef<HTMLButtonElement>;
@ViewChild("vaultSelectorTemplate", { read: TemplateRef }) templateRef: TemplateRef<HTMLElement>;
private _selectedVault = new BehaviorSubject<string>(null);
isOpen = false;
loaded = false;
organizations: Organization[];
organizations$: Observable<Organization[]>;
selectedVault$: Observable<string> = this._selectedVault.asObservable();
vaultFilter: VaultFilter = new VaultFilter();
vaultFilterDisplay = "";
enforcePersonalOwnwership = false;
enforcePersonalOwnership = false;
overlayPostition: ConnectedPosition[] = [
{
originX: "start",
@@ -71,22 +73,22 @@ export class VaultSelectComponent implements OnInit {
];
private overlayRef: OverlayRef;
private _destroy = new Subject<void>();
get show() {
shouldShow(organizations: Organization[]): boolean {
return (
(this.organizations.length > 0 && !this.enforcePersonalOwnwership) ||
(this.organizations.length > 1 && this.enforcePersonalOwnwership)
(organizations.length > 0 && !this.enforcePersonalOwnership) ||
(organizations.length > 1 && this.enforcePersonalOwnership)
);
}
constructor(
private vaultFilterService: VaultFilterService,
private i18nService: I18nService,
private ngZone: NgZone,
private broadcasterService: BroadcasterService,
private overlay: Overlay,
private viewContainerRef: ViewContainerRef,
private platformUtilsService: PlatformUtilsService
private platformUtilsService: PlatformUtilsService,
private organizationService: OrganizationService
) {}
@HostListener("document:keydown.escape", ["$event"])
@@ -98,46 +100,45 @@ export class VaultSelectComponent implements OnInit {
}
async ngOnInit() {
await this.load();
this.broadcasterService.subscribe(this.constructor.name, (message: any) => {
this.ngZone.run(async () => {
switch (message.command) {
case "syncCompleted":
await this.load();
break;
default:
break;
}
});
});
this.organizations$ = this.organizationService.organizations$
.pipe(takeUntil(this._destroy))
.pipe(map((orgs) => orgs.sort((a, b) => a.name.localeCompare(b.name))));
this.organizations$
.pipe(
concatMap(async (organizations) => {
this.enforcePersonalOwnership =
await this.vaultFilterService.checkForPersonalOwnershipPolicy();
if (this.shouldShow(organizations)) {
if (this.enforcePersonalOwnership && !this.vaultFilter.myVaultOnly) {
const firstOrganization = organizations[0];
this._selectedVault.next(firstOrganization.name);
this.vaultFilterService.setVaultFilter(firstOrganization.id);
this.vaultFilter.selectedOrganizationId = firstOrganization.id;
} else if (this.vaultFilter.myVaultOnly) {
this._selectedVault.next(this.i18nService.t(this.vaultFilterService.myVault));
} else if (this.vaultFilter.selectedOrganizationId != null) {
const selectedOrganization = organizations.find(
(o) => o.id === this.vaultFilter.selectedOrganizationId
);
this._selectedVault.next(selectedOrganization.name);
} else {
this._selectedVault.next(this.i18nService.t(this.vaultFilterService.allVaults));
}
}
})
)
.pipe(takeUntil(this._destroy))
.subscribe();
this.loaded = true;
}
async load() {
this.vaultFilter = this.vaultFilterService.getVaultFilter();
this.organizations = (await this.vaultFilterService.buildOrganizations()).sort((a, b) =>
a.name.localeCompare(b.name)
);
this.enforcePersonalOwnwership =
await this.vaultFilterService.checkForPersonalOwnershipPolicy();
if (this.show) {
if (this.enforcePersonalOwnwership && !this.vaultFilter.myVaultOnly) {
this.vaultFilterService.setVaultFilter(this.organizations[0].id);
this.vaultFilter.selectedOrganizationId = this.organizations[0].id;
this.vaultFilterDisplay = this.organizations.find(
(o) => o.id === this.vaultFilter.selectedOrganizationId
).name;
} else if (this.vaultFilter.myVaultOnly) {
this.vaultFilterDisplay = this.i18nService.t(this.vaultFilterService.myVault);
} else if (this.vaultFilter.selectedOrganizationId != null) {
this.vaultFilterDisplay = this.organizations.find(
(o) => o.id === this.vaultFilter.selectedOrganizationId
).name;
} else {
this.vaultFilterDisplay = this.i18nService.t(this.vaultFilterService.allVaults);
}
}
this.loaded = true;
ngOnDestroy(): void {
this._destroy.next();
this._destroy.complete();
this._selectedVault.complete();
}
openOverlay() {
@@ -191,20 +192,20 @@ export class VaultSelectComponent implements OnInit {
this.i18nService.t("disabledOrganizationFilterError")
);
} else {
this.vaultFilterDisplay = organization.name;
this._selectedVault.next(organization.name);
this.vaultFilterService.setVaultFilter(organization.id);
this.onVaultSelectionChanged.emit();
this.close();
}
}
selectAllVaults() {
this.vaultFilterDisplay = this.i18nService.t(this.vaultFilterService.allVaults);
this._selectedVault.next(this.i18nService.t(this.vaultFilterService.allVaults));
this.vaultFilterService.setVaultFilter(this.vaultFilterService.allVaults);
this.onVaultSelectionChanged.emit();
this.close();
}
selectMyVault() {
this.vaultFilterDisplay = this.i18nService.t(this.vaultFilterService.myVault);
this._selectedVault.next(this.i18nService.t(this.vaultFilterService.myVault));
this.vaultFilterService.setVaultFilter(this.vaultFilterService.myVault);
this.onVaultSelectionChanged.emit();
this.close();

View File

@@ -5,7 +5,14 @@
<div class="box-content">
<div class="box-content-row box-content-row-flex" *ngFor="let field of cipher.fields">
<div class="row-main">
<span class="row-label">{{ field.name }}</span>
<span
*ngIf="field.type != fieldType.Linked"
class="row-label draggable"
draggable="true"
(dragstart)="setTextDataOnDrag($event, field.value)"
>{{ field.name }}</span
>
<span *ngIf="field.type === fieldType.Linked" class="row-label">{{ field.name }}</span>
<div *ngIf="field.type === fieldType.Text">
{{ field.value || "&nbsp;" }}
</div>

View File

@@ -18,7 +18,13 @@
</h2>
<div class="box-content">
<div class="box-content-row">
<label for="name">{{ "name" | i18n }}</label>
<label
for="name"
class="draggable"
draggable="true"
(dragstart)="setTextDataOnDrag($event, cipher.name)"
>{{ "name" | i18n }}</label
>
<input id="name" type="text" [value]="cipher.name" readonly aria-readonly="true" />
</div>
<!-- Login -->
@@ -139,7 +145,7 @@
<div
class="box-content-row box-content-row-flex totp"
[ngClass]="{ low: totpLow }"
*ngIf="cipher.login.totp && totpCode && canAccessPremium"
*ngIf="cipher.login.totp && totpCode"
>
<div class="row-main">
<span
@@ -178,10 +184,7 @@
</div>
</div>
<div
class="box-content-row box-content-row-flex totp"
*ngIf="cipher.login.totp && !canAccessPremium"
>
<div class="box-content-row box-content-row-flex totp" *ngIf="showPremiumRequiredTotp">
<div class="row-main">
<span class="row-label">{{ "verificationCodeTotp" | i18n }}</span>
<span class="row-label">
@@ -195,12 +198,22 @@
<!-- Card -->
<div *ngIf="cipher.card">
<div class="box-content-row" *ngIf="cipher.card.cardholderName">
<span class="row-label">{{ "cardholderName" | i18n }}</span>
<span
class="row-label draggable"
draggable="true"
(dragstart)="setTextDataOnDrag($event, cipher.card.cardholderName)"
>{{ "cardholderName" | i18n }}</span
>
{{ cipher.card.cardholderName }}
</div>
<div class="box-content-row box-content-row-flex" *ngIf="cipher.card.number">
<div class="row-main">
<span class="row-label">{{ "number" | i18n }}</span>
<span
class="row-label draggable"
draggable="true"
(dragstart)="setTextDataOnDrag($event, cipher.card.number)"
>{{ "number" | i18n }}</span
>
<span [hidden]="showCardNumber" class="monospaced">{{
cipher.card.maskedNumber | creditCardNumber: cipher.card.brand
}}</span>
@@ -236,16 +249,31 @@
</div>
</div>
<div class="box-content-row" *ngIf="cipher.card.brand">
<span class="row-label">{{ "brand" | i18n }}</span>
<span
class="row-label draggable"
draggable="true"
(dragstart)="setTextDataOnDrag($event, cipher.card.brand)"
>{{ "brand" | i18n }}</span
>
{{ cipher.card.brand }}
</div>
<div class="box-content-row" *ngIf="cipher.card.expiration">
<span class="row-label">{{ "expiration" | i18n }}</span>
<span
class="row-label draggable"
draggable="true"
(dragstart)="setTextDataOnDrag($event, cipher.card.expiration)"
>{{ "expiration" | i18n }}</span
>
{{ cipher.card.expiration }}
</div>
<div class="box-content-row box-content-row-flex" *ngIf="cipher.card.code">
<div class="row-main">
<span class="row-label">{{ "securityCode" | i18n }}</span>
<span
class="row-label draggable"
draggable="true"
(dragstart)="setTextDataOnDrag($event, cipher.card.code)"
>{{ "securityCode" | i18n }}</span
>
<span [hidden]="showCardCode" class="monospaced">{{ cipher.card.maskedCode }}</span>
<span [hidden]="!showCardCode" class="monospaced">{{ cipher.card.code }}</span>
</div>
@@ -280,42 +308,98 @@
<!-- Identity -->
<div *ngIf="cipher.identity">
<div class="box-content-row" *ngIf="cipher.identity.fullName">
<span class="row-label">{{ "identityName" | i18n }}</span>
<span
class="row-label draggable"
draggable="true"
(dragstart)="setTextDataOnDrag($event, cipher.identity.fullName)"
>{{ "identityName" | i18n }}</span
>
{{ cipher.identity.fullName }}
</div>
<div class="box-content-row" *ngIf="cipher.identity.username">
<span class="row-label">{{ "username" | i18n }}</span>
<span
class="row-label draggable"
draggable="true"
(dragstart)="setTextDataOnDrag($event, cipher.identity.username)"
>{{ "username" | i18n }}</span
>
{{ cipher.identity.username }}
</div>
<div class="box-content-row" *ngIf="cipher.identity.company">
<span class="row-label">{{ "company" | i18n }}</span>
<span
class="row-label draggable"
draggable="true"
(dragstart)="setTextDataOnDrag($event, cipher.identity.company)"
>{{ "company" | i18n }}</span
>
{{ cipher.identity.company }}
</div>
<div class="box-content-row" *ngIf="cipher.identity.ssn">
<span class="row-label">{{ "ssn" | i18n }}</span>
<span
class="row-label draggable"
draggable="true"
(dragstart)="setTextDataOnDrag($event, cipher.identity.ssn)"
>{{ "ssn" | i18n }}</span
>
{{ cipher.identity.ssn }}
</div>
<div class="box-content-row" *ngIf="cipher.identity.passportNumber">
<span class="row-label">{{ "passportNumber" | i18n }}</span>
<span
class="row-label draggable"
draggable="true"
(dragstart)="setTextDataOnDrag($event, cipher.identity.passportNumber)"
>{{ "passportNumber" | i18n }}</span
>
{{ cipher.identity.passportNumber }}
</div>
<div class="box-content-row" *ngIf="cipher.identity.licenseNumber">
<span class="row-label">{{ "licenseNumber" | i18n }}</span>
<span
class="row-label draggable"
draggable="true"
(dragstart)="setTextDataOnDrag($event, cipher.identity.licenseNumber)"
>{{ "licenseNumber" | i18n }}</span
>
{{ cipher.identity.licenseNumber }}
</div>
<div class="box-content-row" *ngIf="cipher.identity.email">
<span class="row-label">{{ "email" | i18n }}</span>
<span
class="row-label draggable"
draggable="true"
(dragstart)="setTextDataOnDrag($event, cipher.identity.email)"
>{{ "email" | i18n }}</span
>
{{ cipher.identity.email }}
</div>
<div class="box-content-row" *ngIf="cipher.identity.phone">
<span class="row-label">{{ "phone" | i18n }}</span>
<span
class="row-label draggable"
draggable="true"
(dragstart)="setTextDataOnDrag($event, cipher.identity.phone)"
>{{ "phone" | i18n }}</span
>
{{ cipher.identity.phone }}
</div>
<div
class="box-content-row"
*ngIf="cipher.identity.address1 || cipher.identity.city || cipher.identity.country"
>
<span class="row-label">{{ "address" | i18n }}</span>
<span
class="row-label draggable"
draggable="true"
(dragstart)="
setTextDataOnDrag(
$event,
(cipher.identity.address1 ? cipher.identity.address1 + '\n' : '') +
(cipher.identity.address2 ? cipher.identity.address2 + '\n' : '') +
(cipher.identity.address3 ? cipher.identity.address3 + '\n' : '') +
(cipher.identity.fullAddressPart2
? cipher.identity.fullAddressPart2 + '\n'
: '') +
(cipher.identity.country ? cipher.identity.country : '')
)
"
>{{ "address" | i18n }}</span
>
<div *ngIf="cipher.identity.address1">{{ cipher.identity.address1 }}</div>
<div *ngIf="cipher.identity.address2">{{ cipher.identity.address2 }}</div>
<div *ngIf="cipher.identity.address3">{{ cipher.identity.address3 }}</div>
@@ -332,12 +416,22 @@
*ngFor="let u of cipher.login.uris; let i = index"
>
<div class="row-main">
<label for="hostOrUri{{ i }}" class="row-label" *ngIf="!u.isWebsite">{{
"uri" | i18n
}}</label>
<label for="hostOrUri{{ i }}" class="row-label" *ngIf="u.isWebsite">{{
"website" | i18n
}}</label>
<label
for="hostOrUri{{ i }}"
class="row-label draggable"
draggable="true"
(dragstart)="setTextDataOnDrag($event, u.uri)"
*ngIf="!u.isWebsite"
>{{ "uri" | i18n }}</label
>
<label
for="hostOrUri{{ i }}"
class="row-label draggable"
draggable="true"
(dragstart)="setTextDataOnDrag($event, u.uri)"
*ngIf="u.isWebsite"
>{{ "website" | i18n }}</label
>
<span title="{{ u.uri }}">
<input
id="hostOrUri{{ i }}"
@@ -376,7 +470,13 @@
</div>
<div class="box" *ngIf="cipher.notes">
<h2 class="box-header">
<label for="notes">{{ "notes" | i18n }}</label>
<label
for="notes"
class="draggable"
draggable="true"
(dragstart)="setTextDataOnDrag($event, cipher.notes)"
>{{ "notes" | i18n }}</label
>
</h2>
<div class="box-content">
<div class="box-content-row">

View File

@@ -1,3 +1,5 @@
import { Jsonify } from "type-fest";
import { StateService as BaseStateServiceAbstraction } from "@bitwarden/common/abstractions/state.service";
import { StorageOptions } from "@bitwarden/common/models/domain/storageOptions";
@@ -7,7 +9,7 @@ import { BrowserGroupingsComponentState } from "src/models/browserGroupingsCompo
import { BrowserSendComponentState } from "src/models/browserSendComponentState";
export abstract class StateService extends BaseStateServiceAbstraction<Account> {
abstract getFromSessionMemory<T>(key: string): Promise<T>;
abstract getFromSessionMemory<T>(key: string, deserializer?: (obj: Jsonify<T>) => T): Promise<T>;
abstract setInSessionMemory(key: string, value: any): Promise<void>;
getBrowserGroupingComponentState: (
options?: StorageOptions

View File

@@ -96,6 +96,13 @@ describe("Browser Session Storage Service", () => {
expect(cache.has("test")).toBe(true);
expect(cache.get("test")).toEqual(session.test);
});
it("should use a deserializer if provided", async () => {
const deserializer = jest.fn().mockReturnValue(testObj);
const result = await sut.get("test", { deserializer: deserializer });
expect(deserializer).toHaveBeenCalledWith(session.test);
expect(result).toEqual(testObj);
});
});
});
});

View File

@@ -1,6 +1,12 @@
import { Jsonify } from "type-fest";
import { AbstractEncryptService } from "@bitwarden/common/abstractions/abstractEncrypt.service";
import { AbstractCachedStorageService } from "@bitwarden/common/abstractions/storage.service";
import {
AbstractCachedStorageService,
MemoryStorageServiceInterface,
} from "@bitwarden/common/abstractions/storage.service";
import { EncString } from "@bitwarden/common/models/domain/encString";
import { MemoryStorageOptions } from "@bitwarden/common/models/domain/storageOptions";
import { SymmetricCryptoKey } from "@bitwarden/common/models/domain/symmetricCryptoKey";
import { devFlag } from "../decorators/dev-flag.decorator";
@@ -15,7 +21,10 @@ const keys = {
sessionKey: "session",
};
export class LocalBackedSessionStorageService extends AbstractCachedStorageService {
export class LocalBackedSessionStorageService
extends AbstractCachedStorageService
implements MemoryStorageServiceInterface
{
private cache = new Map<string, unknown>();
private localStorage = new BrowserLocalStorageService();
private sessionStorage = new BrowserMemoryStorageService();
@@ -27,21 +36,26 @@ export class LocalBackedSessionStorageService extends AbstractCachedStorageServi
super();
}
async get<T>(key: string): Promise<T> {
async get<T>(key: string, options?: MemoryStorageOptions<T>): Promise<T> {
if (this.cache.has(key)) {
return this.cache.get(key) as T;
}
return await this.getBypassCache(key);
return await this.getBypassCache(key, options);
}
async getBypassCache<T>(key: string): Promise<T> {
async getBypassCache<T>(key: string, options?: MemoryStorageOptions<T>): Promise<T> {
const session = await this.getLocalSession(await this.getSessionEncKey());
if (session == null || !Object.keys(session).includes(key)) {
return null;
}
this.cache.set(key, session[key]);
let value = session[key];
if (options?.deserializer != null) {
value = options.deserializer(value as Jsonify<T>);
}
this.cache.set(key, value);
return this.cache.get(key) as T;
}

View File

@@ -1,8 +1,8 @@
import { Substitute, SubstituteOf } from "@fluffy-spoon/substitute";
import { Arg, Substitute, SubstituteOf } from "@fluffy-spoon/substitute";
import { LogService } from "@bitwarden/common/abstractions/log.service";
import {
AbstractCachedStorageService,
MemoryStorageServiceInterface,
AbstractStorageService,
} from "@bitwarden/common/abstractions/storage.service";
import { SendType } from "@bitwarden/common/enums/sendType";
@@ -49,7 +49,7 @@ describe("Browser State Service", () => {
});
describe("direct memory storage access", () => {
let memoryStorageService: AbstractCachedStorageService;
let memoryStorageService: LocalBackedSessionStorageService;
beforeEach(() => {
// We need `AbstractCachedStorageService` in the prototype chain to correctly test cache bypass.
@@ -79,12 +79,12 @@ describe("Browser State Service", () => {
});
describe("state methods", () => {
let memoryStorageService: SubstituteOf<AbstractStorageService>;
let memoryStorageService: SubstituteOf<AbstractStorageService & MemoryStorageServiceInterface>;
beforeEach(() => {
memoryStorageService = Substitute.for();
const stateGetter = (key: string) => Promise.resolve(JSON.parse(JSON.stringify(state)));
memoryStorageService.get("state").mimicks(stateGetter);
memoryStorageService.get("state", Arg.any()).mimicks(stateGetter);
sut = new StateService(
diskStorageService,

View File

@@ -1,3 +1,5 @@
import { Jsonify } from "type-fest";
import { AbstractCachedStorageService } from "@bitwarden/common/abstractions/storage.service";
import { GlobalState } from "@bitwarden/common/models/domain/globalState";
import { StorageOptions } from "@bitwarden/common/models/domain/storageOptions";
@@ -17,9 +19,9 @@ export class StateService
extends BaseStateService<GlobalState, Account>
implements StateServiceAbstraction
{
async getFromSessionMemory<T>(key: string): Promise<T> {
async getFromSessionMemory<T>(key: string, deserializer?: (obj: Jsonify<T>) => T): Promise<T> {
return this.memoryStorageService instanceof AbstractCachedStorageService
? await this.memoryStorageService.getBypassCache<T>(key)
? await this.memoryStorageService.getBypassCache<T>(key, { deserializer: deserializer })
: await this.memoryStorageService.get<T>(key);
}

View File

@@ -3,7 +3,7 @@ import { VaultFilterService as BaseVaultFilterService } from "@bitwarden/angular
import { CipherService } from "@bitwarden/common/abstractions/cipher.service";
import { CollectionService } from "@bitwarden/common/abstractions/collection.service";
import { FolderService } from "@bitwarden/common/abstractions/folder/folder.service.abstraction";
import { OrganizationService } from "@bitwarden/common/abstractions/organization.service";
import { OrganizationService } from "@bitwarden/common/abstractions/organization/organization.service.abstraction";
import { PolicyService } from "@bitwarden/common/abstractions/policy/policy.service.abstraction";
import { StateService } from "@bitwarden/common/abstractions/state.service";
import { CipherView } from "@bitwarden/common/models/view/cipherView";

View File

@@ -141,7 +141,7 @@ Bitwarden — гэта праграмнае забеспячэнне з адкр
<value>Сінхранізацыя і доступ да сховішча з некалькіх прылад</value>
</data>
<data name="ScreenshotVault" xml:space="preserve">
<value>Кіруйце ўсімі вашымі імёнамі карыстальніка і паролямі з бяспечнага сховішча</value>
<value>Кіруйце ўсімі вашымі лагінамі і паролямі з бяспечнага сховішча</value>
</data>
<data name="ScreenshotAutofill" xml:space="preserve">
<value>Хутка і аўтаматычна запаўняйце свае ўліковыя даныя на любым вэб-сайце</value>
@@ -150,7 +150,7 @@ Bitwarden — гэта праграмнае забеспячэнне з адкр
<value>У вас ёсць зручны доступ да сховішча з кантэкстнага меню</value>
</data>
<data name="ScreenshotPassword" xml:space="preserve">
<value>Аўтаматычна генерыруйце моцныя, выпадковыя і бяспечныя паролі</value>
<value>Аўтаматычна генерыруйце надзейныя, выпадковыя і бяспечныя паролі</value>
</data>
<data name="ScreenshotEdit" xml:space="preserve">
<value>Вашыя звесткі надзейна захоўваюцца, дзякуючы шыфраванню AES-256</value>

View File

@@ -124,11 +124,32 @@
<value>Сигурен и свободен управител на пароли за всички устройства</value>
</data>
<data name="Description" xml:space="preserve">
<value>Bitwarden е най-лесният и надежден начин да съхранявате регистрации и пароли като ги синхронизирате на всички свои устройства.
Кражбата на пароли е тежък проблем. Сайтовете в Интернет, програмите и мобилните приложения биват атакувани всеки ден. Пробивите в сигурността са факт и паролите биват откраднати. Ако използвате една и съща парола в много програми или сайтове, злонамерени лица могат лесно да достъпят вашата е-поща, електронно банкиране и други важни регистрации.
Експертите по сигурността препоръчват да ползвате различна, случайно генерирана парола за всяка отделна регистрация. Как да управлявате всичките тези пароли? Bitwarden ви позволява лесно да ги създавате, съхранявате и ползвате.
Bitwarden съхранява всички данни в шифриран трезор, който се синхронизира на всички устройства, които ползвате. Шифрирането се извършва преди данните да напуснат устройството ви — така само вие имате достъп до тях. Дори и екипът на Bitwarden не може да прочете данните, дори и да се опита. Данните са защитени чрез AES с 256-битов ключ, контролни суми с добавени случайни данни и удължаване на ключа с PBKDF2 SHA-256.
Bitwarden е със 100% отворен код! Изходният код е наличен в сайта GitHub и всеки може да го преглежда, извърши одит и даже да допринесе за Bitwarden.</value>
<value>Bitwarden, Inc. is the parent company of 8bit Solutions LLC.
NAMED BEST PASSWORD MANAGER BY THE VERGE, U.S. NEWS &amp; WORLD REPORT, CNET, AND MORE.
Manage, store, secure, and share unlimited passwords across unlimited devices from anywhere. Bitwarden delivers open source password management solutions to everyone, whether at home, at work, or on the go.
Generate strong, unique, and random passwords based on security requirements for every website you frequent.
Bitwarden Send quickly transmits encrypted information --- files and plaintext -- directly to anyone.
Bitwarden offers Teams and Enterprise plans for companies so you can securely share passwords with colleagues.
Why Choose Bitwarden:
World-Class Encryption
Passwords are protected with advanced end-to-end encryption (AES-256 bit, salted hashing, and PBKDF2 SHA-256) so your data stays secure and private.
Built-in Password Generator
Generate strong, unique, and random passwords based on security requirements for every website you frequent.
Global Translations
Bitwarden translations exist in 40 languages and are growing, thanks to our global community.
Cross-Platform Applications
Secure and share sensitive data within your Bitwarden Vault from any browser, mobile device, or desktop OS, and more.
</value>
</data>
<data name="AssetTitle" xml:space="preserve">
<value>Сигурен и свободен управител на пароли за всички устройства</value>

View File

@@ -124,11 +124,32 @@
<value>আপনার সমস্ত ডিভাইসের জন্য একটি সুরক্ষিত এবং বিনামূল্যের পাসওয়ার্ড ব্যবস্থাপক</value>
</data>
<data name="Description" xml:space="preserve">
<value>বিটওয়ার্ডেন আপনার সমস্ত লগইন এবং পাসওয়ার্ডগুলি সহজেই আপনার সমস্ত ডিভাইসের সাথে সিঙ্ক করার সময় সংরক্ষণ করার সবচেয়ে সহজ এবং নিরাপদ উপায়।
পাসওয়ার্ড চুরি একটি গুরুতর সমস্যা। আপনি যে ওয়েবসাইটগুলি এবং অ্যাপ্লিকেশনগুলি ব্যবহার করেন সেগুলি প্রতিদিন আক্রমণের শিকার হয়। সুরক্ষা লঙ্ঘন ঘটে এবং আপনার পাসওয়ার্ডগুলি চুরি হয়ে যায়। আপনি যখন অ্যাপ্লিকেশন এবং ওয়েবসাইট জুড়ে একই পাসওয়ার্ডগুলি পুনরায় ব্যবহার করেন হ্যাকাররা সহজেই আপনার ইমেল, ব্যাংক এবং অন্যান্য গুরুত্বপূর্ণ অ্যাকাউন্টগুলিতে ব্যাবহার করতে পারে।
সুরক্ষা বিশেষজ্ঞরা আপনার তৈরি প্রতিটি অ্যাকাউন্টের জন্য একটি পৃথক, এলোমেলোভাবে উৎপন্ন পাসওয়ার্ড ব্যবহার করার পরামর্শ দেয়। কিন্তু আপনি কীভাবে এই সমস্ত পাসওয়ার্ড পরিচালনা করবেন? Bitwarden আপনার পাসওয়ার্ডগুলি তৈরি, সঞ্চয় এবং ব্যাবহার করা আপনার পক্ষে সহজ করে তোলে।
Bitwarden আপনার সমস্ত লগইন একটি এনক্রিপ্ট করা ভল্টে সঞ্চয় করে যা আপনার সমস্ত ডিভাইস জুড়ে সিঙ্ক করে। যেহেতু আপনার ডিভাইসটি ছাড়ার আগে এটি সম্পূর্ণরূপে এনক্রিপ্ট করা হয়েছে, শুধুমাত্র আপনারই ডেটাতে ব্যাবহারাধিকার রয়েছে। এমনকি আমরা Bitwarden এর দল চাইলেও আপনার তথ্যগুলি পড়তে পারব না। আপনার ডেটা AES-256 বিট এনক্রিপশন, সল্টেড হ্যাশিং এবং PBKDF2 SHA-256 দিয়ে নামমুদ্রাম্কিত করা হয়েছে।
Bitwarden ১০০% মুক্ত সফ্টওয়্যার। Bitwarden এর উৎস কোডটি গিটহাবটিতে হোস্ট করা হয়েছে এবং Bitwarden কোডবেস সকলের পর্যালোচনা, নিরীক্ষণ এবং অবদানের জন্য মুক্ত।</value>
<value>Bitwarden, Inc. is the parent company of 8bit Solutions LLC.
NAMED BEST PASSWORD MANAGER BY THE VERGE, U.S. NEWS &amp; WORLD REPORT, CNET, AND MORE.
Manage, store, secure, and share unlimited passwords across unlimited devices from anywhere. Bitwarden delivers open source password management solutions to everyone, whether at home, at work, or on the go.
Generate strong, unique, and random passwords based on security requirements for every website you frequent.
Bitwarden Send quickly transmits encrypted information --- files and plaintext -- directly to anyone.
Bitwarden offers Teams and Enterprise plans for companies so you can securely share passwords with colleagues.
Why Choose Bitwarden:
World-Class Encryption
Passwords are protected with advanced end-to-end encryption (AES-256 bit, salted hashing, and PBKDF2 SHA-256) so your data stays secure and private.
Built-in Password Generator
Generate strong, unique, and random passwords based on security requirements for every website you frequent.
Global Translations
Bitwarden translations exist in 40 languages and are growing, thanks to our global community.
Cross-Platform Applications
Secure and share sensitive data within your Bitwarden Vault from any browser, mobile device, or desktop OS, and more.
</value>
</data>
<data name="AssetTitle" xml:space="preserve">
<value>আপনার সমস্ত ডিভাইসের জন্য একটি সুরক্ষিত এবং বিনামূল্যের পাসওয়ার্ড ব্যবস্থাপক</value>

View File

@@ -148,7 +148,8 @@ Traduccions globals
Les traduccions de Bitwarden existeixen en 40 idiomes i creixen, gràcies a la nostra comunitat global.
Aplicacions de plataforma creuada
Assegureu-vos i compartiu dades sensibles a la vostra caixa forta de Bitwarden des de qualsevol navegador, dispositiu mòbil o S.O. d'escriptori, i molt més.</value>
Assegureu-vos i compartiu dades sensibles a la vostra caixa forta de Bitwarden des de qualsevol navegador, dispositiu mòbil o S.O. d'escriptori, i molt més.
</value>
</data>
<data name="AssetTitle" xml:space="preserve">
<value>Administrador de contrasenyes segur i gratuït per a tots els vostres dispositius</value>

View File

@@ -124,13 +124,32 @@
<value>Bezpečný a bezplatný správce hesel pro všechna vaše zařízení</value>
</data>
<data name="Description" xml:space="preserve">
<value>bitwarden je nejjednodušší a nejbezpečnější způsob, jak uchovávat veškeré své přihlašovací údaje a zároveň je mít pohodlně synchronizované mezi všemi svými zařízeními.
<value>Bitwarden, Inc. is the parent company of 8bit Solutions LLC.
Krádež hesla je velice vážný problém. Stránky a aplikace, které každodenně používáte, jsou často pod útokem a vaše hesla mohou být odcizena. Pokud používáte stejné heslo mezi více aplikacemi nebo stránkami, hackeři se mohou snadno dostat k vaší emailové schránce, bankovnímu účtu či do jiných důležitých účtů.
NAMED BEST PASSWORD MANAGER BY THE VERGE, U.S. NEWS &amp; WORLD REPORT, CNET, AND MORE.
Bezpečnostní experti proto doporučují používat různá, silná a náhodně generovaná hesla pro každý účet zvlášť. Ale jak tato všechna hesla spravovat? bitwarden vám ulehčí jejich správu, ale také jejich generování nebo sdílení.
Manage, store, secure, and share unlimited passwords across unlimited devices from anywhere. Bitwarden delivers open source password management solutions to everyone, whether at home, at work, or on the go.
bitwarden uchovává veškeré vaše přihlašovací údaje v zašifrovaném trezoru, který se synchronizuje mezi všemi vašimi zařízeními. Jelikož jsou zašifrována ještě před opuštěním vašeho zařízení, máte přístup ke svým datům pouze vy. Dokonce ani tým v bitwardenu nemůže číst vaše data a to ani kdybychom chtěli. Vaše data jsou totiž zakódována pomocí šifrovávání AES-256, náhodným hashem a PBKDF2 SHA-256.</value>
Generate strong, unique, and random passwords based on security requirements for every website you frequent.
Bitwarden Send quickly transmits encrypted information --- files and plaintext -- directly to anyone.
Bitwarden offers Teams and Enterprise plans for companies so you can securely share passwords with colleagues.
Why Choose Bitwarden:
World-Class Encryption
Passwords are protected with advanced end-to-end encryption (AES-256 bit, salted hashing, and PBKDF2 SHA-256) so your data stays secure and private.
Built-in Password Generator
Generate strong, unique, and random passwords based on security requirements for every website you frequent.
Global Translations
Bitwarden translations exist in 40 languages and are growing, thanks to our global community.
Cross-Platform Applications
Secure and share sensitive data within your Bitwarden Vault from any browser, mobile device, or desktop OS, and more.
</value>
</data>
<data name="AssetTitle" xml:space="preserve">
<value>Bezpečný a bezplatný správce hesel pro všechna vaše zařízení</value>

View File

@@ -139,7 +139,7 @@ Bitwarden tilbyder Teams og Enterprise-planer for virksomheder, så du sikkert k
Hvorfor vælge Bitwarden:
Kryptering i verdensklasse
Adgangskoder er beskyttet med avanceret end-to-end-kryptering (AES-256 bit, saltet hashtag og PBKDF2 SHA-256), så dine data forbliver sikre og private.
Adgangskoder er beskyttet med avanceret end-to-end-kryptering (AES-256 bit, saltet hashing og PBKDF2 SHA-256), så dine data forbliver sikre og private.
Indbygget adgangskodegenerator
Generér stærke, unikke og tilfældige adgangskoder baseret på sikkerhedskrav til hvert websted, du besøger.
@@ -148,7 +148,8 @@ Globale oversættelser
Bitwarden findes på 40 sprog, og flere kommer til, takket være vores globale fællesskab.
Applikationer på tværs af platforme
Beskyt og del følsomme data i din Bitwarden boks fra enhver browser, mobilenhed eller desktop OS og mere.</value>
Beskyt og del følsomme data i din Bitwarden boks fra enhver browser, mobilenhed eller desktop OS og mere.
</value>
</data>
<data name="AssetTitle" xml:space="preserve">
<value>En sikker og gratis adgangskodemanager til alle dine enheder</value>

View File

@@ -124,15 +124,32 @@
<value>Ένας ασφαλής και δωρεάν διαχειριστής κωδικών για όλες τις συσκευές σας</value>
</data>
<data name="Description" xml:space="preserve">
<value>Το Bitwarden είναι ο ευκολότερος και ασφαλέστερος τρόπος για να αποθηκεύσετε όλες τις συνδέσεις και τους κωδικούς πρόσβασης σας, διατηρώντας παράλληλα το συγχρονισμό μεταξύ όλων των συσκευών σας. Η επέκταση της εφαρμογής Bitwarden σας επιτρέπει να συνδεθείτε γρήγορα σε οποιαδήποτε ιστοσελίδα μέσω του Safari ή του Chrome και υποστηρίζεται από εκατοντάδες άλλες δημοφιλείς εφαρμογές.
<value>Bitwarden, Inc. is the parent company of 8bit Solutions LLC.
Η κλοπή κωδικού πρόσβασης είναι ένα σοβαρό πρόβλημα. Οι ιστοσελίδες και οι εφαρμογές που χρησιμοποιείτε υποβάλλονται σε επίθεση κάθε μέρα. Παραβιάσεις ασφαλείας συμβαίνουν και οι κωδικοί ίσως κλαπούν. Όταν επαναχρησιμοποιείτε τους ίδιους κωδικούς πρόσβασης σε εφαρμογές και ιστοσελίδες, οι χάκερ μπορούν εύκολα να έχουν πρόσβαση στο ηλεκτρονικό σας ταχυδρομείο, στην τράπεζα σας και σε άλλους σημαντικούς λογαριασμούς.
NAMED BEST PASSWORD MANAGER BY THE VERGE, U.S. NEWS &amp; WORLD REPORT, CNET, AND MORE.
Οι ειδικοί ασφαλείας συστήνουν να χρησιμοποιείτε διαφορετικό, τυχαία δημιουργημένο κωδικό πρόσβασης για κάθε λογαριασμό που δημιουργείτε. Αλλά πώς διαχειρίζεστε όλους αυτούς τους κωδικούς πρόσβασης; Το Bitwarden σας διευκολύνει να δημιουργείτε, να αποθηκεύετε και να έχετε πρόσβαση στους κωδικούς πρόσβασης σας.
Manage, store, secure, and share unlimited passwords across unlimited devices from anywhere. Bitwarden delivers open source password management solutions to everyone, whether at home, at work, or on the go.
Το Bitwarden αποθηκεύει όλες τις συνδέσεις σας σε κρυπτογραφημένη μορφή που συγχρονίζεται σε όλες τις συσκευές σας. Δεδομένου ότι είναι πλήρως κρυπτογραφημένο, μόνο εσείς έχετε πρόσβαση στα δεδομένα σας. Ούτε η ομάδα του Bitwarden μπορεί να διαβάσει τα δεδομένα σας, ακόμα κι αν θέλουμε. Τα δεδομένα σας είναι σφραγισμένα με κρυπτογράφηση AES-256 bit, salted hashing και PBKDF2 SHA-256.
Generate strong, unique, and random passwords based on security requirements for every website you frequent.
Το Bitwarden είναι 100% λογισμικό ανοικτού κώδικα. Ο πηγαίος κώδικας για το Bitwarden φιλοξενείται στο GitHub και ο καθένας είναι ελεύθερος να επανεξετάσει, να ελέγξει και να συνεισφέρει στον κώδικα βάσης του Bitwarden.</value>
Bitwarden Send quickly transmits encrypted information --- files and plaintext -- directly to anyone.
Bitwarden offers Teams and Enterprise plans for companies so you can securely share passwords with colleagues.
Why Choose Bitwarden:
World-Class Encryption
Passwords are protected with advanced end-to-end encryption (AES-256 bit, salted hashing, and PBKDF2 SHA-256) so your data stays secure and private.
Built-in Password Generator
Generate strong, unique, and random passwords based on security requirements for every website you frequent.
Global Translations
Bitwarden translations exist in 40 languages and are growing, thanks to our global community.
Cross-Platform Applications
Secure and share sensitive data within your Bitwarden Vault from any browser, mobile device, or desktop OS, and more.
</value>
</data>
<data name="AssetTitle" xml:space="preserve">
<value>Ένας ασφαλής και δωρεάν διαχειριστής κωδικών για όλες τις συσκευές σας</value>

View File

@@ -124,15 +124,32 @@
<value>Tasuta ja turvaline paroolihaldur kõikidele sinu seadmetele</value>
</data>
<data name="Description" xml:space="preserve">
<value>Bitwarden muudab kontoandmete ja teiste isiklike andmete kasutamise erinevate seadmete vahel lihtsaks ja turvaliseks.
<value>Bitwarden, Inc. is the parent company of 8bit Solutions LLC.
Paroolivargustest on saamas järjest tõsisem probleem. Veebilehed ja rakendused, mida igapäevaselt kasutad, on pidevalt rünnakute all. Muuhulgas toimuvad alalõpmata andmelekked, millega koos saadakse ligipääs ka Sinu paroolidele. Kasutades erinevatel veebilehtedel ühesugust parooli, on häkkeritel lihtne ligi pääseda nii sinu e-postile, pangakontole või teistele tähtsatele kontodele.
NAMED BEST PASSWORD MANAGER BY THE VERGE, U.S. NEWS &amp; WORLD REPORT, CNET, AND MORE.
Turvaeksperdid soovitavad kasutada kõikides kasutajakontodes erinevaid, juhuslikult koostatud paroole. Kuidas aga kõiki neid paroole hallata? Bitwarden muudab paroolide loomise, talletamise ja nendele ligipääsu lihtsaks ja turvaliseks.
Manage, store, secure, and share unlimited passwords across unlimited devices from anywhere. Bitwarden delivers open source password management solutions to everyone, whether at home, at work, or on the go.
Bitwarden talletab kõik Sinu andmed krüpteeritud hoidlas, mis sünkroniseeritakse kõikide Sinu poolt kasutatavate seadmete vahel. Kuna hoidla sisu krüpteeritakse enne selle enne seadmest lahkumist, omad andmetele ligipääsu ainult sina. Ka bitwardeni meeskond ei saa Sinu andmeid vaadata, isegi kui neil selleks tahtmine oleks. Sinu andmed kaitsevad AES-256 bitine krüpteering, salted hashing ja PBKDF2 SHA-256.
Generate strong, unique, and random passwords based on security requirements for every website you frequent.
Bitwarden on 100% avatud koodiga tarkvara. Bitwarden lähtekood on saadaval GitHubis ja kõik saavad sellega tasuta tutvuda, seda kontrollida ja Bitwardeni koodibaasi panustada.</value>
Bitwarden Send quickly transmits encrypted information --- files and plaintext -- directly to anyone.
Bitwarden offers Teams and Enterprise plans for companies so you can securely share passwords with colleagues.
Why Choose Bitwarden:
World-Class Encryption
Passwords are protected with advanced end-to-end encryption (AES-256 bit, salted hashing, and PBKDF2 SHA-256) so your data stays secure and private.
Built-in Password Generator
Generate strong, unique, and random passwords based on security requirements for every website you frequent.
Global Translations
Bitwarden translations exist in 40 languages and are growing, thanks to our global community.
Cross-Platform Applications
Secure and share sensitive data within your Bitwarden Vault from any browser, mobile device, or desktop OS, and more.
</value>
</data>
<data name="AssetTitle" xml:space="preserve">
<value>Tasuta ja turvaline paroolihaldur kõikidele Sinu seadmetele</value>

View File

@@ -124,18 +124,32 @@
<value>یک مدیریت کننده کلمه عبور رایگان برای تمامی دستگاههایتان</value>
</data>
<data name="Description" xml:space="preserve">
<value>Bitwarden ساده ترین و امن ترین راه گردآوری تمام داده های ورودی و پسوردها است در حالی که به راحتی آنها را بین تمامی دستگاه ها همگام میکند.
<value>Bitwarden, Inc. is the parent company of 8bit Solutions LLC.
NAMED BEST PASSWORD MANAGER BY THE VERGE, U.S. NEWS &amp; WORLD REPORT, CNET, AND MORE.
سرقت پسورد یک مشکل جدی است. وبسایت ها و اپلیکیشن هایی که شما از آنها استفاده میکنید هر روز تحت حملات قرار دارند. نقص های امنیتی رخ میدهند و پسوردها به سرقت میروند. زمانی که شما مجدداً از همان پسورد برای تمام وبسایت ها و اپلیکیشن ها استفاده میکنید هکرها می‌توانند به راحتی به ایمیل، حساب بانکی، و سایر حسابهای کاربریتان دسترسی داشته باشند.
Manage, store, secure, and share unlimited passwords across unlimited devices from anywhere. Bitwarden delivers open source password management solutions to everyone, whether at home, at work, or on the go.
Generate strong, unique, and random passwords based on security requirements for every website you frequent.
متخصصان امنیتی توصیه میکنند که برای هر حساب کاربری که ایجاد میکنید از پسوردهای متفاوت و تصادفی تولید شده استفاده کنید. اما چطور تمامی این پسورها را مدیریت میکنید؟ بیت واردن ساختن، نگهداری، ودسترسی به پسوردهایتان را آسان میکند.
Bitwarden Send quickly transmits encrypted information --- files and plaintext -- directly to anyone.
Bitwarden offers Teams and Enterprise plans for companies so you can securely share passwords with colleagues.
Bitwarden تمامی داده های ورودی شما را در یک گاو صندوق رمزنگاری شده نگهداری میکند که قابل همگام سازی توسط تمامی دستگاه های شماست. از آنجا که این داده ها هر زمان قبل از ترک دستگاهتان کاملا رمزنگاری میشود، فقط شما به اطلاعاتتان دسترسی دارید. حتی اگر تیم ما در بیت واردن هم بخواهند نمیتوانند اطلاعات شما را مشاهده کنند. داده های شما توسط رمزگذاری AES-256 بیتی، هَش خرد شده، و PBKDF2SHA-256 رمزنگاری شده است.
Why Choose Bitwarden:
Bitwarden ۱۰۰٪ یک برنامه متن باز است. کد منبع بیت واردن در GitHub میزبانی میشود و هر کس آزاد است برای بررسی، تفتیش و کمک به کد دسترسی داشته باشد.</value>
World-Class Encryption
Passwords are protected with advanced end-to-end encryption (AES-256 bit, salted hashing, and PBKDF2 SHA-256) so your data stays secure and private.
Built-in Password Generator
Generate strong, unique, and random passwords based on security requirements for every website you frequent.
Global Translations
Bitwarden translations exist in 40 languages and are growing, thanks to our global community.
Cross-Platform Applications
Secure and share sensitive data within your Bitwarden Vault from any browser, mobile device, or desktop OS, and more.
</value>
</data>
<data name="AssetTitle" xml:space="preserve">
<value>یک مدیریت کننده کلمه عبور رایگان برای تمامی دستگاههایتان</value>

View File

@@ -134,7 +134,7 @@ Luo usein käyttämillesi sivustoille automaattisesti vahvoja, yksilöllisiä ja
Bitwarden Send -ominaisuudella lähetät tietoa nopeasti salattuna — tiedostoja ja tekstiä — suoraan kenelle tahansa.
Yrityksille Bitwarden tarjoaa Teams- ja Enterprise-tilaukset, jotka mahdollistavat turvallisen salasanojen jaon kollegoiden kesken.
Yrityksille Bitwarden tarjoaa Teams- ja Enterprise-tilaukset, jotka mahdollistavat turvallisen salasanojen jakamisen kollegoiden kesken.
Miksi Bitwarden?:

View File

@@ -124,15 +124,32 @@
<value>מנהל ססמאות חינמי ומאובטח עבור כל המכשירים שלך</value>
</data>
<data name="Description" xml:space="preserve">
<value>תוכנת Bitwarden היא הדרך הקלה והבטוחה לשמירת הסיסמאות שלך ועוזרת לסנכרן את הסיסמאות בין כל המכשירים שברשותך.
<value>Bitwarden, Inc. is the parent company of 8bit Solutions LLC.
גניבת סיסמאות היא בעיה רצינית. אתרים ואפליקציות מותקפים באופן יום יומי. פירצות אבטחה קורות מדי פעם ומאגרי סיסמאות נחשפים במלואם. כשאתה משתמש באותה סיסמה עבור כמה אתרים או אפליקציות, האקרים יכולים לנצל את אותה הסיסמה עבור אותם האתרים והאפליקציות ויכולים להשיג גישה למייל, לבנק, ולחשבונות חשובים אחרים.
NAMED BEST PASSWORD MANAGER BY THE VERGE, U.S. NEWS &amp; WORLD REPORT, CNET, AND MORE.
מומחי אבטחה ממליצים להשתמש בסיסמאות שונות ורנדומליות עבור כל חשבון שאתה יוצר. אבל איך אפשר לנהל את כל הסיסמאות הללו? תוכנת Bitwarden עוזרת לך ליצור, לשמור, ולגשת לכל הסיסמאות שלך.
Manage, store, secure, and share unlimited passwords across unlimited devices from anywhere. Bitwarden delivers open source password management solutions to everyone, whether at home, at work, or on the go.
תוכנת Bitwarden מאחסנת את כל הפרטים בכספת מוצפנת שמסתנכרנת בין כל המכשירים שלך. מאחר שהמידע מוצפן עוד לפני שהוא יוצא מהמכשיר שלך, רק לך יש גישה למידע. גם הצוות שלנו בBitwarden לא יכול לקרוא את המידע, אפילו אם ירצה. המידע שלך מוגן עם הצפנת AES-256 ביט, salted hashing, וPBKDF2 SHA-256.
Generate strong, unique, and random passwords based on security requirements for every website you frequent.
תוכנת Bitwarden היא 100% תוכנת קוד פתוח. קוד המקור של Bitwarden מאוחסן בGitHub וכל מי שרוצה יכול לתת ביקורת, להוסיף הערות, וכמובן לתרום מזמנו לפיתוח הקוד של Bitwarden.</value>
Bitwarden Send quickly transmits encrypted information --- files and plaintext -- directly to anyone.
Bitwarden offers Teams and Enterprise plans for companies so you can securely share passwords with colleagues.
Why Choose Bitwarden:
World-Class Encryption
Passwords are protected with advanced end-to-end encryption (AES-256 bit, salted hashing, and PBKDF2 SHA-256) so your data stays secure and private.
Built-in Password Generator
Generate strong, unique, and random passwords based on security requirements for every website you frequent.
Global Translations
Bitwarden translations exist in 40 languages and are growing, thanks to our global community.
Cross-Platform Applications
Secure and share sensitive data within your Bitwarden Vault from any browser, mobile device, or desktop OS, and more.
</value>
</data>
<data name="AssetTitle" xml:space="preserve">
<value>מנהל סיסמאות חינמי ומאובטח עבור כל המכשירים שלך</value>

View File

@@ -124,13 +124,32 @@
<value>आपके सभी उपकरणों के लिए एक सुरक्षित और नि: शुल्क कूटशब्द प्रबंधक</value>
</data>
<data name="Description" xml:space="preserve">
<value>bitwarden is the easiest and safest way to store all of your logins and passwords while conveniently keeping them synced between all of your devices.
<value>Bitwarden, Inc. is the parent company of 8bit Solutions LLC.
Password theft is a serious problem. The websites and apps that you use are under attack every day. Security breaches occur and your passwords are stolen. When you reuse the same passwords across apps and websites hackers can easily access your email, bank, and other important accounts.
NAMED BEST PASSWORD MANAGER BY THE VERGE, U.S. NEWS &amp; WORLD REPORT, CNET, AND MORE.
Security experts recommend that you use a different, randomly generated password for every account that you create. But how do you manage all those passwords? bitwarden makes it easy for you to create, store, and access your passwords.
Manage, store, secure, and share unlimited passwords across unlimited devices from anywhere. Bitwarden delivers open source password management solutions to everyone, whether at home, at work, or on the go.
bitwarden stores all of your logins in an encrypted vault that syncs across all of your devices. Since it's fully encrypted before it ever leaves your device, only you have access to your data. Not even the team at bitwarden can read your data, even if we wanted to. Your data is sealed with AES-256 bit encryption, salted hashing, and PBKDF2 SHA-256.</value>
Generate strong, unique, and random passwords based on security requirements for every website you frequent.
Bitwarden Send quickly transmits encrypted information --- files and plaintext -- directly to anyone.
Bitwarden offers Teams and Enterprise plans for companies so you can securely share passwords with colleagues.
Why Choose Bitwarden:
World-Class Encryption
Passwords are protected with advanced end-to-end encryption (AES-256 bit, salted hashing, and PBKDF2 SHA-256) so your data stays secure and private.
Built-in Password Generator
Generate strong, unique, and random passwords based on security requirements for every website you frequent.
Global Translations
Bitwarden translations exist in 40 languages and are growing, thanks to our global community.
Cross-Platform Applications
Secure and share sensitive data within your Bitwarden Vault from any browser, mobile device, or desktop OS, and more.
</value>
</data>
<data name="AssetTitle" xml:space="preserve">
<value>आपके सभी उपकरणों के लिए एक सुरक्षित और नि: शुल्क पासवर्ड प्रबंधक</value>

View File

@@ -124,15 +124,32 @@
<value>Pengelola sandi yang aman dan gratis untuk semua perangkat Anda</value>
</data>
<data name="Description" xml:space="preserve">
<value>Bitwarden adalah cara termudah dan teraman untuk menyimpan semua info masuk dan sandi Anda sambil tetap menjaga mereka disinkronkan di antara semua perangkat Anda.
<value>Bitwarden, Inc. is the parent company of 8bit Solutions LLC.
Pencurian sandi adalah masalah serius. Situs web dan aplikasi yang Anda gunakan diserang setiap hari. Pelanggaran keamanan terjadi dan sandi Anda dicuri. Ketika Anda menggunakan sandi yang sama di aplikasi dan situs web peretas dapat dengan mudah mengakses email, bank, dan akun penting Anda yang lain.
NAMED BEST PASSWORD MANAGER BY THE VERGE, U.S. NEWS &amp; WORLD REPORT, CNET, AND MORE.
Pakar keamanan merekomendasikan agar Anda menggunakan sandi yang berbeda dan dibuat secara acak untuk setiap akun yang Anda buat. Tapi bagaimana mengelola semua sandi tersebut? Bitwarden membuatnya menjadi mudah untuk Anda membuat, menyimpan dan mengakses sandi Anda.
Manage, store, secure, and share unlimited passwords across unlimited devices from anywhere. Bitwarden delivers open source password management solutions to everyone, whether at home, at work, or on the go.
Bitwarden menyimpan semua info masuk Anda di brankas terenkripsi yang disinkronkan di semua perangkat Anda. Karena sepenuhnya dienkripsi sebelum meninggalkan perangkat Anda, hanya Anda yang memiliki akses ke data Anda. Bahkan tim di Bitwarden tidak dapat membaca data Anda, bahkan jika kami mau. Data Anda disegel dengan ekripsi AES-256 bit, hash yang di-salt, dan PBKDF2 SHA-256.
Generate strong, unique, and random passwords based on security requirements for every website you frequent.
Bitwarden adalah 100% perangkat lunak sumber terbuka. Kode sumber untuk Bitwarden berada di GitHub dan setiap orang bebas untuk meninjau, mengaudit, dan berkontribusi ke basis kode Bitwarden.</value>
Bitwarden Send quickly transmits encrypted information --- files and plaintext -- directly to anyone.
Bitwarden offers Teams and Enterprise plans for companies so you can securely share passwords with colleagues.
Why Choose Bitwarden:
World-Class Encryption
Passwords are protected with advanced end-to-end encryption (AES-256 bit, salted hashing, and PBKDF2 SHA-256) so your data stays secure and private.
Built-in Password Generator
Generate strong, unique, and random passwords based on security requirements for every website you frequent.
Global Translations
Bitwarden translations exist in 40 languages and are growing, thanks to our global community.
Cross-Platform Applications
Secure and share sensitive data within your Bitwarden Vault from any browser, mobile device, or desktop OS, and more.
</value>
</data>
<data name="AssetTitle" xml:space="preserve">
<value>Pengelola sandi yang aman dan gratis untuk semua perangkat Anda</value>

View File

@@ -124,15 +124,32 @@
<value>あらゆる端末で使える、安全な無料パスワードマネージャー</value>
</data>
<data name="Description" xml:space="preserve">
<value>Bitwarden は、あらゆる端末間で同期しつつログイン情報やパスワードを保管しておける、最も簡単で安全なサービスです。
<value>Bitwarden, Inc. is the parent company of 8bit Solutions LLC.
パスワードの盗難は深刻な問題になっています。ウェブサイトやアプリは毎日攻撃を受けており、もしセキュリティに問題があればパスワードが盗難されてしまいます。 同じパスワードを他のアプリでも再利用していると、攻撃者はメールや銀行口座など大切なアカウントに簡単に侵入できてしまいます。
NAMED BEST PASSWORD MANAGER BY THE VERGE, U.S. NEWS &amp; WORLD REPORT, CNET, AND MORE.
セキュリティの専門家は、アカウント毎に別のランダムに生成したパスワードを使うことを推奨していますが、ランダムなパスワードをすべて覚えていられますか Bitwarden を使えば、わざわざパスワードを覚えなくても簡単にパスワードの生成、保管や利用ができます。
Manage, store, secure, and share unlimited passwords across unlimited devices from anywhere. Bitwarden delivers open source password management solutions to everyone, whether at home, at work, or on the go.
Bitwarden は端末間で同期できる、暗号化された保管庫にログイン情報を保管します。端末から送信される前に暗号化されるので、あなただけがそのデータにアクセスできるのです。Bitwarden の開発者チームですら、あなたに頼まれたとしてもデータを読み取ることはできません。データは AES-256 bit 暗号化、ソルト化ハッシュ、PBKDF2 SHA-256 で保護されます。
Generate strong, unique, and random passwords based on security requirements for every website you frequent.
Bitwarden は100%オープンソースソフトウェアです。Bitwarden のソースコードは GitHub にホストされており、誰でもBitwardenのコードを自由にレビュー、監査、貢献できます。</value>
Bitwarden Send quickly transmits encrypted information --- files and plaintext -- directly to anyone.
Bitwarden offers Teams and Enterprise plans for companies so you can securely share passwords with colleagues.
Why Choose Bitwarden:
World-Class Encryption
Passwords are protected with advanced end-to-end encryption (AES-256 bit, salted hashing, and PBKDF2 SHA-256) so your data stays secure and private.
Built-in Password Generator
Generate strong, unique, and random passwords based on security requirements for every website you frequent.
Global Translations
Bitwarden translations exist in 40 languages and are growing, thanks to our global community.
Cross-Platform Applications
Secure and share sensitive data within your Bitwarden Vault from any browser, mobile device, or desktop OS, and more.
</value>
</data>
<data name="AssetTitle" xml:space="preserve">
<value>あらゆる端末で使える、安全な無料パスワードマネージャー</value>

View File

@@ -124,15 +124,32 @@
<value>Saugi ir nemokama slaptažodžių tvarkyklė visiems įrenginiams</value>
</data>
<data name="Description" xml:space="preserve">
<value>Bitwarden“ yra paprasčiausias ir saugiausias būdas išsaugoti visus prisijungimus ir slaptažodžius, tuo pačiu patogu juos pasiekti iš visų jūsų įrenginių.
<value>Bitwarden, Inc. is the parent company of 8bit Solutions LLC.
Slaptažodžių vagystė yra rimta problema. Jūsų naudojamos svetainės ir programos yra atakuojamos kiekvieną dieną. Įvykus saugumo pažeidimų jūsų slaptažodžiai yra pavogiami. Kai pakartotinai naudojate tuos pačius slaptažodžius programose ir svetainėse, įsilaužėliai gali lengvai pasiekti jūsų el. pašto, banko ir kitas svarbias paskiras.
NAMED BEST PASSWORD MANAGER BY THE VERGE, U.S. NEWS &amp; WORLD REPORT, CNET, AND MORE.
Saugumo ekspertai rekomenduoja kiekvienai jūsų sukurtai paskyrai naudoti kitą, atsitiktinai sugeneruotą slaptažodį. Bet kaip valdyti visus tuos slaptažodžius? „Bitwarden“ leidžia jums lengvai kurti, saugoti ir pasiekti slaptažodžius.
Manage, store, secure, and share unlimited passwords across unlimited devices from anywhere. Bitwarden delivers open source password management solutions to everyone, whether at home, at work, or on the go.
„Bitwarden“ visus jūsų prisijungimus saugo šifruotoje saugykloje, kuri pasiekiama visuose jūsų įrenginiuose. Kadangi duomenys visiškai užšifruoti dar neišėjus iš jūsų įrenginio, prieigą prie savo duomenų turite tik jūs. Net „Bitwarden“ komanda negali perskaityti jūsų duomenų, net jei mes to ir norėtume. Jūsų duomenys užklijuojami naudojant AES-256 bitų šifravimą, druskinta maišos funkcija ir PBKDF2 SHA-256.
Generate strong, unique, and random passwords based on security requirements for every website you frequent.
Bitwarden“ yra 100% atvirojo kodo programinė įranga. „Bitwarden“ kodas yra „GitHub“ ir visi gali peržiūrėti, tikrinti ir prisidėti prie „Bitwarden“ kodo bazės.</value>
Bitwarden Send quickly transmits encrypted information --- files and plaintext -- directly to anyone.
Bitwarden offers Teams and Enterprise plans for companies so you can securely share passwords with colleagues.
Why Choose Bitwarden:
World-Class Encryption
Passwords are protected with advanced end-to-end encryption (AES-256 bit, salted hashing, and PBKDF2 SHA-256) so your data stays secure and private.
Built-in Password Generator
Generate strong, unique, and random passwords based on security requirements for every website you frequent.
Global Translations
Bitwarden translations exist in 40 languages and are growing, thanks to our global community.
Cross-Platform Applications
Secure and share sensitive data within your Bitwarden Vault from any browser, mobile device, or desktop OS, and more.
</value>
</data>
<data name="AssetTitle" xml:space="preserve">
<value>Saugi ir nemokama slaptažodžių tvarkyklė visiems įrenginiams</value>

View File

@@ -124,15 +124,31 @@
<value>Drošs bezmaksas paroļu pārvaldnieks visām Tavām ierīcēm</value>
</data>
<data name="Description" xml:space="preserve">
<value>Bitwarden ir vieglākais un drošākais veids, kā glabāt visus pierakstīšanās vienumus un paroles, vienlaicīgi ērti uzturot tās sinhronizētas visās ierīcēs.
<value>Bitwarden, Inc. is the parent company of 8bit Solutions LLC.
Paroļu zagšana ir nopietna nebūšana. Tīmekļa vietnēm un lietotnēm uzbrūk katru dienu. Datu drošības pārkāpumos tiek nozagtas paroles. Kad viena un tā pati parole tiek izmantota lietotnēs un tīmekļa vietnēs, urķi var viegli piekļūt e-pasta, banku un citiem būtiskiem kontiem.
NAMED BEST PASSWORD MANAGER BY THE VERGE, U.S. NEWS &amp; WORLD REPORT, CNET, AND MORE.
Drošības lietpratēji iesaka katram izveidotajam kontam izmantot dažādas, nejauši veidotas paroles. Kā tās visas pārvaldīt? Bitwarden atvieglo paroļu izveidošanu, glabāšanu un piekļūšanu tām.
Manage, store, secure, and share unlimited passwords across unlimited devices from anywhere. Bitwarden delivers open source password management solutions to everyone, whether at home, at work, or on the go.
Bitwarden uzglabā visus pierakstīšanās vienumus šifrētā glabātavā, kas tiek sinhronizēta visās izmantotajās ierīcēs. Tā kā tā dati tiek pilnībā šifrēti, pirms tie tiek izsūtīti no izmantotās ierīces, piekļuve tiem ir tikai glabātavas īpašniekam. Pat Bitwarden izstrādātāji nevar lasīt datus, pat ja to gribētu. Informācija tiek aizsargāta ar AES-256 bitu šifrēšanu, "sālīto" jaukšanu un PBKDF2 SHA-256.
Generate strong, unique, and random passwords based on security requirements for every website you frequent.
Bitwarden ir pilnībā atvērtā pirmkoda programmatūra. Bitwarden atvērtais pirmkods ir izvietots GitHub, un ikkatram ir iespēja pārskatīt, pārbaudīt un sniegt ieguldījumu Bitwarden pirmkodā.
Bitwarden Send quickly transmits encrypted information --- files and plaintext -- directly to anyone.
Bitwarden offers Teams and Enterprise plans for companies so you can securely share passwords with colleagues.
Why Choose Bitwarden:
World-Class Encryption
Passwords are protected with advanced end-to-end encryption (AES-256 bit, salted hashing, and PBKDF2 SHA-256) so your data stays secure and private.
Built-in Password Generator
Generate strong, unique, and random passwords based on security requirements for every website you frequent.
Global Translations
Bitwarden translations exist in 40 languages and are growing, thanks to our global community.
Cross-Platform Applications
Secure and share sensitive data within your Bitwarden Vault from any browser, mobile device, or desktop OS, and more.
</value>
</data>
<data name="AssetTitle" xml:space="preserve">

View File

@@ -124,15 +124,32 @@
<value>En sikker og fri passordbehandler for alle dine PCer og mobiler</value>
</data>
<data name="Description" xml:space="preserve">
<value>Bitwarden er den enkleste og tryggeste måten å lagre alle dine innlogginger og passord på, samtidig som de blir synkronisert mellom alle dine PCer og mobiler.
<value>Bitwarden, Inc. is the parent company of 8bit Solutions LLC.
Passordtyveri er et seriøst problem. Nettstedene og appene som du bruker er under angrep hver eneste dag. Sikkerhetsbrudd forekommer, og dine passord blir stjålet. Når du bruker de samme passordene over flere apper og nettsteder, kan hackere lett få tilgang til din e-post, bankkonto, og andre viktige kontoer.
NAMED BEST PASSWORD MANAGER BY THE VERGE, U.S. NEWS &amp; WORLD REPORT, CNET, AND MORE.
Sikkerhetseksperter anbefaler at du bruker forskjellig og tilfeldig genererte passord for hver eneste konto du lager. Men hvordan håndterer du alle de passordene? Bitwarden gjør det lett for deg å lage, lagre, og få tilgang til dine passord.
Manage, store, secure, and share unlimited passwords across unlimited devices from anywhere. Bitwarden delivers open source password management solutions to everyone, whether at home, at work, or on the go.
Bitwarden lagrer alle dine innlogginger i et kryptert hvelv som synkroniseres mellom alle dine PCer og mobiler. Ettersom hvelvet er fullstendig kryptert før det noensinne forlater enheten din, har bare du tilgang til dine dataer. Selv ikke de som jobber hos Bitwarden kan lese dine dataer, om vi så ville det. Dine dataer er forseglet med AES 256-bitkryptering, saltet hashing, og PBKDF2 SHA-256.
Generate strong, unique, and random passwords based on security requirements for every website you frequent.
Bitwardens programvare har 100% åpen kildekode. Kildekoden til Bitwarden betjenes på GitHub, og alle har all rett til å undersøke, gjennomgå og bidra til Bitwarden sin kodebase.</value>
Bitwarden Send quickly transmits encrypted information --- files and plaintext -- directly to anyone.
Bitwarden offers Teams and Enterprise plans for companies so you can securely share passwords with colleagues.
Why Choose Bitwarden:
World-Class Encryption
Passwords are protected with advanced end-to-end encryption (AES-256 bit, salted hashing, and PBKDF2 SHA-256) so your data stays secure and private.
Built-in Password Generator
Generate strong, unique, and random passwords based on security requirements for every website you frequent.
Global Translations
Bitwarden translations exist in 40 languages and are growing, thanks to our global community.
Cross-Platform Applications
Secure and share sensitive data within your Bitwarden Vault from any browser, mobile device, or desktop OS, and more.
</value>
</data>
<data name="AssetTitle" xml:space="preserve">
<value>En sikker og fri passordbehandler for alle dine PCer og mobiler</value>

View File

@@ -124,15 +124,32 @@
<value>Um gerenciador de senhas gratuito e seguro para todos os seus dispositivos</value>
</data>
<data name="Description" xml:space="preserve">
<value>O Bitwarden é a maneira mais fácil e segura de armazenar todos os seus usuários e senhas mantendo-os convenientemente sincronizados entre todos os seus dispositivos.
<value>Bitwarden, Inc. is the parent company of 8bit Solutions LLC.
O furto de senhas é um problema sério. Os sites e aplicativos que os utiliza estão sob ataque todos os dias. Brechas de segurança ocorrem e as suas senhas são furtadas. Quando você reutiliza as mesmas senhas entre aplicativos e sites, os hackers podem facilmente acessar o seu e-mail, banco, e outras contas importantes.
NAMED BEST PASSWORD MANAGER BY THE VERGE, U.S. NEWS &amp; WORLD REPORT, CNET, AND MORE.
Os especialistas de segurança recomendam que utilize uma senha diferente e aleatoriamente gerada para todas as contas que você cria. Mas como gerenciar todas essas senhas? O Bitwarden torna-lhe fácil criar, armazenar, e acessar as suas senhas.
Manage, store, secure, and share unlimited passwords across unlimited devices from anywhere. Bitwarden delivers open source password management solutions to everyone, whether at home, at work, or on the go.
O Bitwarden armazena todas as suas credenciais num cofre encriptado que sincroniza entre todos os seus dispositivos. Como são completamente encriptados antes de se quer sair do seu dispositivo, apenas você tem acesso aos seus dados. Nem se quer a equipe do Bitwarden pode ler os seus dados, mesmo se quiséssemos. Os seus dados são selados com encriptação AES-256 bits, salted hashing, e PBKDF2 SHA-256.
Generate strong, unique, and random passwords based on security requirements for every website you frequent.
Bitwarden é um software 100% de código aberto. O código-fonte do Bitwarden está hospedado no GitHub e todos são livres para revisar, auditar e contribuir com a base de códigos do Bitwarden.</value>
Bitwarden Send quickly transmits encrypted information --- files and plaintext -- directly to anyone.
Bitwarden offers Teams and Enterprise plans for companies so you can securely share passwords with colleagues.
Why Choose Bitwarden:
World-Class Encryption
Passwords are protected with advanced end-to-end encryption (AES-256 bit, salted hashing, and PBKDF2 SHA-256) so your data stays secure and private.
Built-in Password Generator
Generate strong, unique, and random passwords based on security requirements for every website you frequent.
Global Translations
Bitwarden translations exist in 40 languages and are growing, thanks to our global community.
Cross-Platform Applications
Secure and share sensitive data within your Bitwarden Vault from any browser, mobile device, or desktop OS, and more.
</value>
</data>
<data name="AssetTitle" xml:space="preserve">
<value>Um gerenciador de senhas gratuito e seguro para todos os seus dispositivos</value>

View File

@@ -124,15 +124,32 @@
<value>Um gestor de palavras-passe seguro e gratuito para todos os seus dispositivos</value>
</data>
<data name="Description" xml:space="preserve">
<value>O Bitwarden é a maneira mais fácil e segura de armazenar todas as suas credenciais e palavras-passe mantendo-as convenientemente sincronizadas entre todos os seus dispositivos.
<value>Bitwarden, Inc. is the parent company of 8bit Solutions LLC.
O furto de palavras-passe é um problema sério. Os websites e aplicações que utiliza estão sob ataque todos os dias. Quebras de segurança ocorrem e as suas palavras-passe são furtadas. Quando reutiliza as mesmas palavras-passe entre aplicações e websites, os hackers podem facilmente aceder ao seu email, banco, e outras contas importantes.
NAMED BEST PASSWORD MANAGER BY THE VERGE, U.S. NEWS &amp; WORLD REPORT, CNET, AND MORE.
Os especialistas de segurança recomendam que utilize uma palavra-passe diferente e aleatoriamente gerada para todas as contas que cria. Mas como é que gere todas essas palavras-passe? O Bitwarden torna-lhe fácil criar, armazenar, e aceder às suas palavras-passe.
Manage, store, secure, and share unlimited passwords across unlimited devices from anywhere. Bitwarden delivers open source password management solutions to everyone, whether at home, at work, or on the go.
O Bitwarden armazena todas as suas credenciais num cofre encriptado que sincroniza entre todos os seus dispositivos. Como são completamente encriptados antes de se quer sair do seu dispositivo, apenas você tem acesso aos seus dados. Nem se quer a equipa do Bitwarden pode ler os seus dados, mesmo se quiséssemos. Os seus dados são selados com encriptação AES-256 bits, salted hashing, e PBKDF2 SHA-256.
Generate strong, unique, and random passwords based on security requirements for every website you frequent.
O Bitwarden é 100% software de código aberto. O código fonte para o Bitwarden está hospedado no GitHub e todos podem revisar, auditar, e contribuir para a base de código do Bitwarden.</value>
Bitwarden Send quickly transmits encrypted information --- files and plaintext -- directly to anyone.
Bitwarden offers Teams and Enterprise plans for companies so you can securely share passwords with colleagues.
Why Choose Bitwarden:
World-Class Encryption
Passwords are protected with advanced end-to-end encryption (AES-256 bit, salted hashing, and PBKDF2 SHA-256) so your data stays secure and private.
Built-in Password Generator
Generate strong, unique, and random passwords based on security requirements for every website you frequent.
Global Translations
Bitwarden translations exist in 40 languages and are growing, thanks to our global community.
Cross-Platform Applications
Secure and share sensitive data within your Bitwarden Vault from any browser, mobile device, or desktop OS, and more.
</value>
</data>
<data name="AssetTitle" xml:space="preserve">
<value>Um gestor de palavras-passe seguro e gratuito para todos os seus dispositivos</value>

View File

@@ -124,15 +124,32 @@
<value>Сигурни и бесплатни менаџер лозинке за сва Ваша уређаја</value>
</data>
<data name="Description" xml:space="preserve">
<value>Bitwarden је најједноставнији и најсигурнији начин за складиштење свих ваших података за пријављивање и лозинки, док их истовремено усклађујете на свим својим уређајима.
<value>Bitwarden, Inc. is the parent company of 8bit Solutions LLC.
Крађа лозинке је озбиљан проблем. Веб локације и апликације које користите свакодневно су на удару. Дошло је до нарушавања безбедности и крађе лозинки. Када поново користите исте лозинке у апликацијама и на веб локацијама, хакери могу лако да приступе вашој е-пошти, банци и другим важним рачунима.
NAMED BEST PASSWORD MANAGER BY THE VERGE, U.S. NEWS &amp; WORLD REPORT, CNET, AND MORE.
Стручњаци за безбедност препоручују употребу различите, насумично генерисане лозинке за сваки налог који креирате. Али како управљате свим тим лозинкама? Bitwarden вам олакшава стварање, чување и приступ вашим лозинкама.
Manage, store, secure, and share unlimited passwords across unlimited devices from anywhere. Bitwarden delivers open source password management solutions to everyone, whether at home, at work, or on the go.
Bitwarden чува све ваше пријаве у шифрованом сефу који се синхронизује на свим вашим уређајима. Пошто је потпуно шифровано пре него што напусти уређај, само ви имате приступ подацима. Чак ни тим у Bitwarden не може да прочита ваше податке, чак и да то желимо. Ваши подаци су запечаћени са AES-256 битном енкрипцијом, усољеним хеширањем и PBKDF2 SHA-256.
Generate strong, unique, and random passwords based on security requirements for every website you frequent.
Bitwardenје софтвер са 100% отвореног кода. Изворни код Bitwarden-а се налази на GitHub и сви могу слободно да прегледају, провере и дају свој допринос за Bitwarden.</value>
Bitwarden Send quickly transmits encrypted information --- files and plaintext -- directly to anyone.
Bitwarden offers Teams and Enterprise plans for companies so you can securely share passwords with colleagues.
Why Choose Bitwarden:
World-Class Encryption
Passwords are protected with advanced end-to-end encryption (AES-256 bit, salted hashing, and PBKDF2 SHA-256) so your data stays secure and private.
Built-in Password Generator
Generate strong, unique, and random passwords based on security requirements for every website you frequent.
Global Translations
Bitwarden translations exist in 40 languages and are growing, thanks to our global community.
Cross-Platform Applications
Secure and share sensitive data within your Bitwarden Vault from any browser, mobile device, or desktop OS, and more.
</value>
</data>
<data name="AssetTitle" xml:space="preserve">
<value>Сигурни и бесплатни менаџер лозинке за сва Ваша уређаја</value>

View File

@@ -139,7 +139,7 @@ Bitwarden, parolaları iş arkadaşlarınızla güvenli bir şekilde paylaşabil
Neden Bitwarden?
Üst düzey şifreleme
Parolalarınız gelişmiş uçtan uca şifreleme (AES-256 bit, salted hashtag ve PBKDF2 SHA-256) ile korunuyor, böylece verileriniz güvende ve gizli kalıyor.
Parolalarınız gelişmiş uçtan uca şifreleme (AES-256 bit, salted hashing ve PBKDF2 SHA-256) ile korunuyor, böylece verileriniz güvende ve gizli kalıyor.
Dahili parola oluşturucu
Sık kullandığınız web siteleri için güvenlik gereksinimlerinize uygun, güçlü, benzersiz ve rastgele parolalar oluşturabilirsiniz.
@@ -148,7 +148,8 @@ Sık kullandığınız web siteleri için güvenlik gereksinimlerinize uygun, g
Bitwarden 40 dilde kullanılabiliyor ve gönüllü topluluğumuz sayesinde çeviri sayısı giderek artıyor.
Her platformla uyumlu uygulamalar
Bitwarden kasanızdaki hassas verilere her tarayıcıdan, mobil cihazdan veya masaüstü işletim sisteminden ulaşabilir ve onları paylaşabilirsiniz.</value>
Bitwarden kasanızdaki hassas verilere her tarayıcıdan, mobil cihazdan veya masaüstü işletim sisteminden ulaşabilir ve onları paylaşabilirsiniz.
</value>
</data>
<data name="AssetTitle" xml:space="preserve">
<value>Tüm cihazarınız için güvenli ve ücretsiz bir parola yöneticisi</value>

View File

@@ -13,7 +13,7 @@
"homepage": "https://bitwarden.com",
"repository": {
"type": "git",
"url": "https://github.com/bitwarden/cli"
"url": "https://github.com/bitwarden/clients"
},
"license": "GPL-3.0-only",
"scripts": {

View File

@@ -30,8 +30,8 @@ import { ImportService } from "@bitwarden/common/services/import.service";
import { KeyConnectorService } from "@bitwarden/common/services/keyConnector.service";
import { MemoryStorageService } from "@bitwarden/common/services/memoryStorage.service";
import { NoopMessagingService } from "@bitwarden/common/services/noopMessaging.service";
import { OrganizationService } from "@bitwarden/common/services/organization.service";
import { OrganizationApiService } from "@bitwarden/common/services/organization/organization-api.service";
import { OrganizationService } from "@bitwarden/common/services/organization/organization.service";
import { PasswordGenerationService } from "@bitwarden/common/services/passwordGeneration.service";
import { PolicyService } from "@bitwarden/common/services/policy/policy.service";
import { ProviderService } from "@bitwarden/common/services/provider.service";
@@ -41,6 +41,7 @@ import { SettingsService } from "@bitwarden/common/services/settings.service";
import { StateService } from "@bitwarden/common/services/state.service";
import { StateMigrationService } from "@bitwarden/common/services/stateMigration.service";
import { SyncService } from "@bitwarden/common/services/sync/sync.service";
import { SyncNotifierService } from "@bitwarden/common/services/sync/syncNotifier.service";
import { TokenService } from "@bitwarden/common/services/token.service";
import { TotpService } from "@bitwarden/common/services/totp.service";
import { TwoFactorService } from "@bitwarden/common/services/twoFactor.service";
@@ -113,6 +114,7 @@ export class Main {
folderApiService: FolderApiService;
userVerificationApiService: UserVerificationApiService;
organizationApiService: OrganizationApiServiceAbstraction;
syncNotifierService: SyncNotifierService;
constructor() {
let p = null;
@@ -191,7 +193,9 @@ export class Main {
customUserAgent
);
this.organizationApiService = new OrganizationApiService(this.apiService);
this.syncNotifierService = new SyncNotifierService();
this.organizationApiService = new OrganizationApiService(this.apiService, this.syncService);
this.containerService = new ContainerService(this.cryptoService, this.encryptService);
@@ -231,7 +235,7 @@ export class Main {
this.providerService = new ProviderService(this.stateService);
this.organizationService = new OrganizationService(this.stateService);
this.organizationService = new OrganizationService(this.stateService, this.syncNotifierService);
this.policyService = new PolicyService(this.stateService, this.organizationService);
@@ -311,9 +315,9 @@ export class Main {
this.logService,
this.keyConnectorService,
this.stateService,
this.organizationService,
this.providerService,
this.folderApiService,
this.syncNotifierService,
async (expired: boolean) => await this.logout()
);

View File

@@ -74,7 +74,6 @@ export class ConvertToKeyConnectorCommand {
} else if (answer.convert === "leave") {
await this.organizationApiService.leave(organization.id);
await this.keyConnectorService.removeConvertAccountRequired();
await this.syncService.fullSync(true);
return Response.success();
} else {
await this.logout();

View File

@@ -4,7 +4,7 @@ import { CipherService } from "@bitwarden/common/abstractions/cipher.service";
import { CollectionService } from "@bitwarden/common/abstractions/collection.service";
import { CryptoService } from "@bitwarden/common/abstractions/crypto.service";
import { FolderService } from "@bitwarden/common/abstractions/folder/folder.service.abstraction";
import { OrganizationService } from "@bitwarden/common/abstractions/organization.service";
import { OrganizationService } from "@bitwarden/common/abstractions/organization/organization.service.abstraction";
import { SearchService } from "@bitwarden/common/abstractions/search.service";
import { StateService } from "@bitwarden/common/abstractions/state.service";
import { TotpService } from "@bitwarden/common/abstractions/totp.service";

View File

@@ -2,7 +2,7 @@ import * as program from "commander";
import * as inquirer from "inquirer";
import { ImportService } from "@bitwarden/common/abstractions/import.service";
import { OrganizationService } from "@bitwarden/common/abstractions/organization.service";
import { OrganizationService } from "@bitwarden/common/abstractions/organization/organization.service.abstraction";
import { ImportType } from "@bitwarden/common/enums/importOptions";
import { Importer } from "@bitwarden/common/importers/importer";
import { Response } from "@bitwarden/node/cli/models/response";

View File

@@ -2,7 +2,7 @@ import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { CipherService } from "@bitwarden/common/abstractions/cipher.service";
import { CollectionService } from "@bitwarden/common/abstractions/collection.service";
import { FolderService } from "@bitwarden/common/abstractions/folder/folder.service.abstraction";
import { OrganizationService } from "@bitwarden/common/abstractions/organization.service";
import { OrganizationService } from "@bitwarden/common/abstractions/organization/organization.service.abstraction";
import { SearchService } from "@bitwarden/common/abstractions/search.service";
import { Utils } from "@bitwarden/common/misc/utils";
import { CollectionData } from "@bitwarden/common/models/data/collectionData";

View File

@@ -139,7 +139,7 @@ export class SendProgram extends Program {
return new program.Command("template")
.arguments("<object>")
.description("Get json templates for send objects", {
object: "Valid objects are: send, send.text, send.file",
object: "Valid objects are: send.text, send.file",
})
.action(async (object) => {
const cmd = new GetCommand(
@@ -176,12 +176,12 @@ export class SendProgram extends Program {
writeLn("");
writeLn(" Examples:");
writeLn("");
writeLn(" bw get send searchText");
writeLn(" bw get send id");
writeLn(" bw get send searchText --text");
writeLn(" bw get send searchText --file");
writeLn(" bw get send searchText --file --output ../Photos/photo.jpg");
writeLn(" bw get send searchText --file --raw");
writeLn(" bw send get searchText");
writeLn(" bw send get id");
writeLn(" bw send get searchText --text");
writeLn(" bw send get searchText --file");
writeLn(" bw send get searchText --file --output ../Photos/photo.jpg");
writeLn(" bw send get searchText --file --raw");
writeLn("", true);
})
.action(async (id: string, options: program.OptionValues) => {

View File

@@ -4,18 +4,18 @@
<metadata>
<id>bitwarden-cli</id>
<version>0.0.0</version>
<packageSourceUrl>https://github.com/bitwarden/cli/tree/master/stores/chocolatey</packageSourceUrl>
<packageSourceUrl>https://github.com/bitwarden/clients/tree/master/apps/cli/stores/chocolatey</packageSourceUrl>
<owners>kspearrin</owners>
<title>Bitwarden CLI</title>
<authors>Bitwarden Inc.</authors>
<projectUrl>https://bitwarden.com/</projectUrl>
<iconUrl>https://cdn.rawgit.com/bitwarden/desktop/51dd1341/resources/icon.png</iconUrl>
<iconUrl>https://raw.githubusercontent.com/bitwarden/brand/master/icons/256x256.png</iconUrl>
<copyright>Copyright © 2015-2022 Bitwarden Inc.</copyright>
<projectSourceUrl>https://github.com/bitwarden/cli/</projectSourceUrl>
<projectSourceUrl>https://github.com/bitwarden/clients/</projectSourceUrl>
<docsUrl>https://help.bitwarden.com/article/cli/</docsUrl>
<bugTrackerUrl>https://github.com/bitwarden/cli/issues</bugTrackerUrl>
<releaseNotes>https://github.com/bitwarden/cli/releases</releaseNotes>
<licenseUrl>https://github.com/bitwarden/cli/blob/master/LICENSE.txt</licenseUrl>
<bugTrackerUrl>https://github.com/bitwarden/clients/issues</bugTrackerUrl>
<releaseNotes>https://github.com/bitwarden/clients/releases</releaseNotes>
<licenseUrl>https://github.com/bitwarden/clients/blob/master/LICENSE.txt</licenseUrl>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<tags>bitwarden password manager cli</tags>
<summary>A secure and free password manager for all of your devices.</summary>
@@ -24,15 +24,31 @@
-----------------
Bitwarden is the easiest and safest way to store all of your logins and passwords while conveniently keeping them synced between all of your devices.
Password theft is a serious problem. The websites and apps that you use are under attack every day. Security breaches occur and your passwords are stolen. When you reuse the same passwords across apps and websites hackers can easily access your email, bank, and other important accounts.
Security experts recommend that you use a different, randomly generated password for every account that you create. But how do you manage all those passwords? Bitwarden makes it easy for you to create, store, and access your passwords.
Bitwarden stores all of your logins in an encrypted vault that syncs across all of your devices. Since it's fully encrypted before it ever leaves your device, only you have access to your data. Not even the team at Bitwarden can read your data, even if we wanted to. Your data is sealed with AES-256 bit encryption, salted hashing, and PBKDF2 SHA-256.
Bitwarden is 100% open source software. The source code for Bitwarden is hosted on GitHub and everyone is free to review, audit, and contribute to the Bitwarden codebase.
Bitwarden, Inc. is the parent company of 8bit Solutions LLC.
NAMED BEST PASSWORD MANAGER BY THE VERGE, U.S. NEWS &amp; WORLD REPORT, CNET, AND MORE.
Manage, store, secure, and share unlimited passwords across unlimited devices from anywhere. Bitwarden delivers open source password management solutions to everyone, whether at home, at work, or on the go.
Generate strong, unique, and random passwords based on security requirements for every website you frequent.
Bitwarden Send quickly transmits encrypted information --- files and plaintext -- directly to anyone.
Bitwarden offers Teams and Enterprise plans for companies so you can securely share passwords with colleagues.
Why Choose Bitwarden:
World-Class Encryption
Passwords are protected with advanced end-to-end encryption (AES-256 bit, salted hashing, and PBKDF2 SHA-256) so your data stays secure and private.
Built-in Password Generator
Generate strong, unique, and random passwords based on security requirements for every website you frequent.
Global Translations
Bitwarden translations exist in 40 languages and are growing, thanks to our global community.
Cross-Platform Applications
Secure and share sensitive data within your Bitwarden Vault from any browser, mobile device, or desktop OS, and more.
</description>
</metadata>
<files>

View File

@@ -1,13 +1,40 @@
name: bw
version: __version__
summary: Bitwarden CLI
description: A secure and free password manager for all of your devices.
summary: Bitwarden CLI - A secure and free password manager for all of your devices.
description: |
Bitwarden, Inc. is the parent company of 8bit Solutions LLC.
NAMED BEST PASSWORD MANAGER BY THE VERGE, U.S. NEWS &amp; WORLD REPORT, CNET, AND MORE.
Manage, store, secure, and share unlimited passwords across unlimited devices from anywhere. Bitwarden delivers open source password management solutions to everyone, whether at home, at work, or on the go.
Generate strong, unique, and random passwords based on security requirements for every website you frequent.
Bitwarden Send quickly transmits encrypted information --- files and plaintext -- directly to anyone.
Bitwarden offers Teams and Enterprise plans for companies so you can securely share passwords with colleagues.
Why Choose Bitwarden:
World-Class Encryption
Passwords are protected with advanced end-to-end encryption (AES-256 bit, salted hashing, and PBKDF2 SHA-256) so your data stays secure and private.
Built-in Password Generator
Generate strong, unique, and random passwords based on security requirements for every website you frequent.
Global Translations
Bitwarden translations exist in 40 languages and are growing, thanks to our global community.
Cross-Platform Applications
Secure and share sensitive data within your Bitwarden Vault from any browser, mobile device, or desktop OS, and more.
confinement: strict
base: core18
apps:
bw:
command: bw
plugs: [network, home, network-bind]
environment:
XDG_CONFIG_HOME: $SNAP_USER_DATA
plugs: [network, network-bind]
parts:
bw:
plugin: dump

View File

@@ -1,4 +1,6 @@
{
"devFlags": {},
"flags": {}
"flags": {
"showDDGSetting": true
}
}

View File

@@ -1,3 +1,5 @@
{
"flags": {}
"flags": {
"showDDGSetting": true
}
}

View File

@@ -153,7 +153,7 @@ dependencies = [
"security-framework-sys",
"tokio",
"widestring",
"windows 0.32.0",
"windows",
]
[[package]]
@@ -329,18 +329,22 @@ dependencies = [
"pkg-config",
]
[[package]]
name = "lazy_static"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
version = "0.2.119"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1bf2e165bb3457c8e098ea76f3e3bc9db55f87aa90d52d0e6be741470916aaa4"
[[package]]
name = "libloading"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "efbc0f03f9a775e9f6aed295c6a1ba2253c5757a9e03d55c6caa46a681abcddd"
dependencies = [
"cfg-if",
"winapi",
]
[[package]]
name = "libsecret"
version = "0.1.4"
@@ -426,28 +430,29 @@ dependencies = [
[[package]]
name = "napi"
version = "2.1.0"
version = "2.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "17ec66e60f000c78dd7c6215b6fa260e0591e09805024332bc5b3f55acc12244"
checksum = "743fece4c26c5132f8559080145fde9ba88700c0f1aa30a1ab3e057ab105814d"
dependencies = [
"bitflags",
"ctor",
"lazy_static",
"napi-sys",
"once_cell",
"thread_local",
"tokio",
"windows 0.30.0",
]
[[package]]
name = "napi-build"
version = "1.2.1"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ebd4419172727423cf30351406c54f6cc1b354a2cfb4f1dba3e6cd07f6d5522b"
checksum = "882a73d9ef23e8dc2ebbffb6a6ae2ef467c0f18ac10711e4cc59c5485d41df0e"
[[package]]
name = "napi-derive"
version = "2.1.0"
version = "2.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "74ac5287a5e94a8728fc82d16c5127acc5eb5b8ad6404ef5f82d6a4ce8d5bdd2"
checksum = "39f3d8b02ef355898ea98f69082d9a183c8701c836942c2daf3e92364e88a0fa"
dependencies = [
"convert_case",
"napi-derive-backend",
@@ -458,9 +463,9 @@ dependencies = [
[[package]]
name = "napi-derive-backend"
version = "1.0.25"
version = "1.0.38"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "427f4f04525635cdf22005d1be62d6d671bcb5550d694a1efb480a315422b4af"
checksum = "6c35640513eb442fcbd1653a1c112fb6b2cc12b54d82f9c141f5859c721cab36"
dependencies = [
"convert_case",
"once_cell",
@@ -472,9 +477,12 @@ dependencies = [
[[package]]
name = "napi-sys"
version = "2.1.0"
version = "2.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a385494dac3c52cbcacb393bb3b42669e7db8ab240c7ad5115f549eb061f2cc"
checksum = "529671ebfae679f2ce9630b62dd53c72c56b3eb8b2c852e7e2fa91704ff93d67"
dependencies = [
"libloading",
]
[[package]]
name = "ntapi"
@@ -513,9 +521,9 @@ dependencies = [
[[package]]
name = "parking_lot_core"
version = "0.9.1"
version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28141e0cc4143da2443301914478dc976a61ffdb3f043058310c70df2fed8954"
checksum = "09a279cbf25cb0757810394fbc1e359949b59e348145c643a939a525692e6929"
dependencies = [
"cfg-if",
"libc",
@@ -745,6 +753,15 @@ dependencies = [
"syn",
]
[[package]]
name = "thread_local"
version = "1.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5516c27b78311c50bf42c071425c560ac799b11c30b31f87e3081965fe5e0180"
dependencies = [
"once_cell",
]
[[package]]
name = "tokio"
version = "1.17.0"
@@ -846,19 +863,6 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows"
version = "0.30.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b749ebd2304aa012c5992d11a25d07b406bdbe5f79d371cb7a918ce501a19eb0"
dependencies = [
"windows_aarch64_msvc 0.30.0",
"windows_i686_gnu 0.30.0",
"windows_i686_msvc 0.30.0",
"windows_x86_64_gnu 0.30.0",
"windows_x86_64_msvc 0.30.0",
]
[[package]]
name = "windows"
version = "0.32.0"
@@ -874,23 +878,17 @@ dependencies = [
[[package]]
name = "windows-sys"
version = "0.32.0"
version = "0.36.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3df6e476185f92a12c072be4a189a0210dcdcf512a1891d6dff9edb874deadc6"
checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2"
dependencies = [
"windows_aarch64_msvc 0.32.0",
"windows_i686_gnu 0.32.0",
"windows_i686_msvc 0.32.0",
"windows_x86_64_gnu 0.32.0",
"windows_x86_64_msvc 0.32.0",
"windows_aarch64_msvc 0.36.1",
"windows_i686_gnu 0.36.1",
"windows_i686_msvc 0.36.1",
"windows_x86_64_gnu 0.36.1",
"windows_x86_64_msvc 0.36.1",
]
[[package]]
name = "windows_aarch64_msvc"
version = "0.30.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "29277a4435d642f775f63c7d1faeb927adba532886ce0287bd985bffb16b6bca"
[[package]]
name = "windows_aarch64_msvc"
version = "0.32.0"
@@ -898,10 +896,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d8e92753b1c443191654ec532f14c199742964a061be25d77d7a96f09db20bf5"
[[package]]
name = "windows_i686_gnu"
version = "0.30.0"
name = "windows_aarch64_msvc"
version = "0.36.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1145e1989da93956c68d1864f32fb97c8f561a8f89a5125f6a2b7ea75524e4b8"
checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47"
[[package]]
name = "windows_i686_gnu"
@@ -910,10 +908,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a711c68811799e017b6038e0922cb27a5e2f43a2ddb609fe0b6f3eeda9de615"
[[package]]
name = "windows_i686_msvc"
version = "0.30.0"
name = "windows_i686_gnu"
version = "0.36.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d4a09e3a0d4753b73019db171c1339cd4362c8c44baf1bcea336235e955954a6"
checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6"
[[package]]
name = "windows_i686_msvc"
@@ -922,10 +920,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "146c11bb1a02615db74680b32a68e2d61f553cc24c4eb5b4ca10311740e44172"
[[package]]
name = "windows_x86_64_gnu"
version = "0.30.0"
name = "windows_i686_msvc"
version = "0.36.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ca64fcb0220d58db4c119e050e7af03c69e6f4f415ef69ec1773d9aab422d5a"
checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024"
[[package]]
name = "windows_x86_64_gnu"
@@ -934,13 +932,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c912b12f7454c6620635bbff3450962753834be2a594819bd5e945af18ec64bc"
[[package]]
name = "windows_x86_64_msvc"
version = "0.30.0"
name = "windows_x86_64_gnu"
version = "0.36.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08cabc9f0066848fef4bc6a1c1668e6efce38b661d2aeec75d18d8617eebb5f1"
checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1"
[[package]]
name = "windows_x86_64_msvc"
version = "0.32.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "504a2476202769977a040c6364301a3f65d0cc9e3fb08600b2bda150a0488316"
[[package]]
name = "windows_x86_64_msvc"
version = "0.36.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680"

View File

@@ -12,13 +12,13 @@ crate-type = ["cdylib"]
[dependencies]
anyhow = "1.0"
napi = {version = "2", features = ["async"]}
napi-derive = "2"
napi = {version = "2.9.1", features = ["async"]}
napi-derive = "2.9.1"
scopeguard = "1.1.0"
tokio = {version = "1.17.0", features = ["full"]}
[build-dependencies]
napi-build = "1"
napi-build = "2.0.1"
[target.'cfg(windows)'.dependencies]
widestring = "0.5.1"
@@ -30,6 +30,8 @@ windows = {version = "0.32.0", features = [
"Win32_Foundation",
"Win32_Security_Credentials",
"Win32_System_WinRT",
"Win32_UI_Input_KeyboardAndMouse",
"Win32_UI_WindowsAndMessaging",
]}
[target.'cfg(windows)'.dev-dependencies]

View File

@@ -1,19 +1,33 @@
use anyhow::Result;
use windows::{
core::factory, Foundation::IAsyncOperation, Security::Credentials::UI::*,
Win32::Foundation::HWND, Win32::System::WinRT::IUserConsentVerifierInterop,
core::{factory, HSTRING},
Foundation::IAsyncOperation,
Security::Credentials::UI::*,
Win32::{
Foundation::HWND,
System::WinRT::IUserConsentVerifierInterop,
UI::{
Input::KeyboardAndMouse::{
self, keybd_event, GetAsyncKeyState, SetFocus, KEYEVENTF_EXTENDEDKEY,
KEYEVENTF_KEYUP, VK_MENU,
},
WindowsAndMessaging::{self, SetForegroundWindow},
},
},
};
pub fn prompt(hwnd: Vec<u8>, message: String) -> Result<bool> {
let interop = factory::<UserConsentVerifier, IUserConsentVerifierInterop>()?;
let h = isize::from_le_bytes(hwnd.try_into().unwrap());
let h = isize::from_le_bytes(hwnd.clone().try_into().unwrap());
let window = HWND(h);
let operation: IAsyncOperation<UserConsentVerificationResult> =
unsafe { interop.RequestVerificationForWindowAsync(window, message)? };
// The Windows Hello prompt is displayed inside the application window. For best result we
// should set the window to the foreground and focus it.
set_focus(window);
let result: UserConsentVerificationResult = operation.get()?;
let interop = factory::<UserConsentVerifier, IUserConsentVerifierInterop>()?;
let operation: IAsyncOperation<UserConsentVerificationResult> =
unsafe { interop.RequestVerificationForWindowAsync(window, &HSTRING::from(message))? };
let result = operation.get()?;
match result {
UserConsentVerificationResult::Verified => Ok(true),
@@ -31,6 +45,31 @@ pub fn available() -> Result<bool> {
}
}
fn set_focus(window: HWND) {
let mut pressed = false;
unsafe {
// Simulate holding down Alt key to bypass windows limitations
// https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getasynckeystate#return-value
// The most significant bit indicates if the key is currently being pressed. This means the
// value will be negative if the key is pressed.
if GetAsyncKeyState(VK_MENU.0 as i32) >= 0 {
pressed = true;
keybd_event(VK_MENU.0 as u8, 0, KEYEVENTF_EXTENDEDKEY, 0);
}
SetForegroundWindow(window);
SetFocus(window);
if pressed {
keybd_event(
VK_MENU.0 as u8,
0,
KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP,
0,
);
}
}
}
#[cfg(test)]
mod tests {
use super::*;

View File

@@ -0,0 +1,14 @@
const { pathsToModuleNameMapper } = require("ts-jest");
const { compilerOptions } = require("./tsconfig");
const sharedConfig = require("../../libs/shared/jest.config.base");
module.exports = {
...sharedConfig,
preset: "jest-preset-angular",
setupFilesAfterEnv: ["<rootDir>/test.setup.ts"],
moduleNameMapper: pathsToModuleNameMapper(compilerOptions?.paths || {}, {
prefix: "<rootDir>/",
}),
};

View File

@@ -0,0 +1,5 @@
{
"rules": {
"no-console": "off"
}
}

View File

@@ -0,0 +1,681 @@
{
"name": "native-messaging-test-runner",
"version": "1.0.0",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "native-messaging-test-runner",
"version": "1.0.0",
"license": "GPL-3.0",
"dependencies": {
"@bitwarden/common": "file:../../../libs/common",
"@bitwarden/node": "file:../../../libs/node",
"module-alias": "^2.2.2",
"node-ipc": "9.2.1",
"ts-node": "^10.9.1",
"uuid": "^8.3.2",
"yargs": "^17.5.1"
},
"devDependencies": {
"@tsconfig/node16": "^1.0.3",
"@types/node": "^18.6.5",
"@types/node-ipc": "9.2.0",
"typescript": "^4.7.4"
}
},
"../../../libs/common": {
"name": "@bitwarden/common",
"version": "0.0.0",
"license": "GPL-3.0"
},
"../../../libs/node": {
"name": "@bitwarden/node",
"version": "0.0.0",
"license": "GPL-3.0",
"dependencies": {
"@bitwarden/common": "file:../common"
}
},
"node_modules/@bitwarden/common": {
"resolved": "../../../libs/common",
"link": true
},
"node_modules/@bitwarden/node": {
"resolved": "../../../libs/node",
"link": true
},
"node_modules/@cspotcode/source-map-support": {
"version": "0.8.1",
"license": "MIT",
"dependencies": {
"@jridgewell/trace-mapping": "0.3.9"
},
"engines": {
"node": ">=12"
}
},
"node_modules/@jridgewell/resolve-uri": {
"version": "3.1.0",
"license": "MIT",
"engines": {
"node": ">=6.0.0"
}
},
"node_modules/@jridgewell/sourcemap-codec": {
"version": "1.4.14",
"license": "MIT"
},
"node_modules/@jridgewell/trace-mapping": {
"version": "0.3.9",
"license": "MIT",
"dependencies": {
"@jridgewell/resolve-uri": "^3.0.3",
"@jridgewell/sourcemap-codec": "^1.4.10"
}
},
"node_modules/@tsconfig/node10": {
"version": "1.0.9",
"license": "MIT"
},
"node_modules/@tsconfig/node12": {
"version": "1.0.11",
"license": "MIT"
},
"node_modules/@tsconfig/node14": {
"version": "1.0.3",
"license": "MIT"
},
"node_modules/@tsconfig/node16": {
"version": "1.0.3",
"license": "MIT"
},
"node_modules/@types/node": {
"version": "18.6.5",
"license": "MIT"
},
"node_modules/@types/node-ipc": {
"version": "9.2.0",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/node": "*"
}
},
"node_modules/acorn": {
"version": "8.8.0",
"license": "MIT",
"bin": {
"acorn": "bin/acorn"
},
"engines": {
"node": ">=0.4.0"
}
},
"node_modules/acorn-walk": {
"version": "8.2.0",
"license": "MIT",
"engines": {
"node": ">=0.4.0"
}
},
"node_modules/ansi-regex": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
"engines": {
"node": ">=8"
}
},
"node_modules/ansi-styles": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
"dependencies": {
"color-convert": "^2.0.1"
},
"engines": {
"node": ">=8"
},
"funding": {
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
}
},
"node_modules/arg": {
"version": "4.1.3",
"license": "MIT"
},
"node_modules/cliui": {
"version": "7.0.4",
"resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz",
"integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==",
"dependencies": {
"string-width": "^4.2.0",
"strip-ansi": "^6.0.0",
"wrap-ansi": "^7.0.0"
}
},
"node_modules/color-convert": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
"dependencies": {
"color-name": "~1.1.4"
},
"engines": {
"node": ">=7.0.0"
}
},
"node_modules/color-name": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
},
"node_modules/create-require": {
"version": "1.1.1",
"license": "MIT"
},
"node_modules/diff": {
"version": "4.0.2",
"license": "BSD-3-Clause",
"engines": {
"node": ">=0.3.1"
}
},
"node_modules/easy-stack": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/easy-stack/-/easy-stack-1.0.1.tgz",
"integrity": "sha512-wK2sCs4feiiJeFXn3zvY0p41mdU5VUgbgs1rNsc/y5ngFUijdWd+iIN8eoyuZHKB8xN6BL4PdWmzqFmxNg6V2w==",
"engines": {
"node": ">=6.0.0"
}
},
"node_modules/emoji-regex": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
},
"node_modules/escalade": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
"integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==",
"engines": {
"node": ">=6"
}
},
"node_modules/event-pubsub": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/event-pubsub/-/event-pubsub-4.3.0.tgz",
"integrity": "sha512-z7IyloorXvKbFx9Bpie2+vMJKKx1fH1EN5yiTfp8CiLOTptSYy1g8H4yDpGlEdshL1PBiFtBHepF2cNsqeEeFQ==",
"engines": {
"node": ">=4.0.0"
}
},
"node_modules/get-caller-file": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
"integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
"engines": {
"node": "6.* || 8.* || >= 10.*"
}
},
"node_modules/is-fullwidth-code-point": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
"engines": {
"node": ">=8"
}
},
"node_modules/js-message": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/js-message/-/js-message-1.0.7.tgz",
"integrity": "sha512-efJLHhLjIyKRewNS9EGZ4UpI8NguuL6fKkhRxVuMmrGV2xN/0APGdQYwLFky5w9naebSZ0OwAGp0G6/2Cg90rA==",
"engines": {
"node": ">=0.6.0"
}
},
"node_modules/js-queue": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/js-queue/-/js-queue-2.0.2.tgz",
"integrity": "sha512-pbKLsbCfi7kriM3s1J4DDCo7jQkI58zPLHi0heXPzPlj0hjUsm+FesPUbE0DSbIVIK503A36aUBoCN7eMFedkA==",
"dependencies": {
"easy-stack": "^1.0.1"
},
"engines": {
"node": ">=1.0.0"
}
},
"node_modules/make-error": {
"version": "1.3.6",
"license": "ISC"
},
"node_modules/module-alias": {
"version": "2.2.2",
"resolved": "https://registry.npmjs.org/module-alias/-/module-alias-2.2.2.tgz",
"integrity": "sha512-A/78XjoX2EmNvppVWEhM2oGk3x4lLxnkEA4jTbaK97QKSDjkIoOsKQlfylt/d3kKKi596Qy3NP5XrXJ6fZIC9Q=="
},
"node_modules/node-ipc": {
"version": "9.2.1",
"resolved": "https://registry.npmjs.org/node-ipc/-/node-ipc-9.2.1.tgz",
"integrity": "sha512-mJzaM6O3xHf9VT8BULvJSbdVbmHUKRNOH7zDDkCrA1/T+CVjq2WVIDfLt0azZRXpgArJtl3rtmEozrbXPZ9GaQ==",
"dependencies": {
"event-pubsub": "4.3.0",
"js-message": "1.0.7",
"js-queue": "2.0.2"
},
"engines": {
"node": ">=8.0.0"
}
},
"node_modules/require-directory": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
"integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/string-width": {
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
"dependencies": {
"emoji-regex": "^8.0.0",
"is-fullwidth-code-point": "^3.0.0",
"strip-ansi": "^6.0.1"
},
"engines": {
"node": ">=8"
}
},
"node_modules/strip-ansi": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
"dependencies": {
"ansi-regex": "^5.0.1"
},
"engines": {
"node": ">=8"
}
},
"node_modules/ts-node": {
"version": "10.9.1",
"license": "MIT",
"dependencies": {
"@cspotcode/source-map-support": "^0.8.0",
"@tsconfig/node10": "^1.0.7",
"@tsconfig/node12": "^1.0.7",
"@tsconfig/node14": "^1.0.0",
"@tsconfig/node16": "^1.0.2",
"acorn": "^8.4.1",
"acorn-walk": "^8.1.1",
"arg": "^4.1.0",
"create-require": "^1.1.0",
"diff": "^4.0.1",
"make-error": "^1.1.1",
"v8-compile-cache-lib": "^3.0.1",
"yn": "3.1.1"
},
"bin": {
"ts-node": "dist/bin.js",
"ts-node-cwd": "dist/bin-cwd.js",
"ts-node-esm": "dist/bin-esm.js",
"ts-node-script": "dist/bin-script.js",
"ts-node-transpile-only": "dist/bin-transpile.js",
"ts-script": "dist/bin-script-deprecated.js"
},
"peerDependencies": {
"@swc/core": ">=1.2.50",
"@swc/wasm": ">=1.2.50",
"@types/node": "*",
"typescript": ">=2.7"
},
"peerDependenciesMeta": {
"@swc/core": {
"optional": true
},
"@swc/wasm": {
"optional": true
}
}
},
"node_modules/typescript": {
"version": "4.7.4",
"license": "Apache-2.0",
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
},
"engines": {
"node": ">=4.2.0"
}
},
"node_modules/uuid": {
"version": "8.3.2",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
"integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
"bin": {
"uuid": "dist/bin/uuid"
}
},
"node_modules/v8-compile-cache-lib": {
"version": "3.0.1",
"license": "MIT"
},
"node_modules/wrap-ansi": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
"integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
"dependencies": {
"ansi-styles": "^4.0.0",
"string-width": "^4.1.0",
"strip-ansi": "^6.0.0"
},
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/chalk/wrap-ansi?sponsor=1"
}
},
"node_modules/y18n": {
"version": "5.0.8",
"resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
"integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
"engines": {
"node": ">=10"
}
},
"node_modules/yargs": {
"version": "17.5.1",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-17.5.1.tgz",
"integrity": "sha512-t6YAJcxDkNX7NFYiVtKvWUz8l+PaKTLiL63mJYWR2GnHq2gjEWISzsLp9wg3aY36dY1j+gfIEL3pIF+XlJJfbA==",
"dependencies": {
"cliui": "^7.0.2",
"escalade": "^3.1.1",
"get-caller-file": "^2.0.5",
"require-directory": "^2.1.1",
"string-width": "^4.2.3",
"y18n": "^5.0.5",
"yargs-parser": "^21.0.0"
},
"engines": {
"node": ">=12"
}
},
"node_modules/yargs-parser": {
"version": "21.1.1",
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",
"integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==",
"engines": {
"node": ">=12"
}
},
"node_modules/yn": {
"version": "3.1.1",
"license": "MIT",
"engines": {
"node": ">=6"
}
}
},
"dependencies": {
"@bitwarden/common": {
"version": "file:../../../libs/common"
},
"@bitwarden/node": {
"version": "file:../../../libs/node",
"requires": {
"@bitwarden/common": "file:../common"
}
},
"@cspotcode/source-map-support": {
"version": "0.8.1",
"requires": {
"@jridgewell/trace-mapping": "0.3.9"
}
},
"@jridgewell/resolve-uri": {
"version": "3.1.0"
},
"@jridgewell/sourcemap-codec": {
"version": "1.4.14"
},
"@jridgewell/trace-mapping": {
"version": "0.3.9",
"requires": {
"@jridgewell/resolve-uri": "^3.0.3",
"@jridgewell/sourcemap-codec": "^1.4.10"
}
},
"@tsconfig/node10": {
"version": "1.0.9"
},
"@tsconfig/node12": {
"version": "1.0.11"
},
"@tsconfig/node14": {
"version": "1.0.3"
},
"@tsconfig/node16": {
"version": "1.0.3"
},
"@types/node": {
"version": "18.6.5"
},
"@types/node-ipc": {
"version": "9.2.0",
"dev": true,
"requires": {
"@types/node": "*"
}
},
"acorn": {
"version": "8.8.0"
},
"acorn-walk": {
"version": "8.2.0"
},
"ansi-regex": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="
},
"ansi-styles": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
"requires": {
"color-convert": "^2.0.1"
}
},
"arg": {
"version": "4.1.3"
},
"cliui": {
"version": "7.0.4",
"resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz",
"integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==",
"requires": {
"string-width": "^4.2.0",
"strip-ansi": "^6.0.0",
"wrap-ansi": "^7.0.0"
}
},
"color-convert": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
"requires": {
"color-name": "~1.1.4"
}
},
"color-name": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
},
"create-require": {
"version": "1.1.1"
},
"diff": {
"version": "4.0.2"
},
"easy-stack": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/easy-stack/-/easy-stack-1.0.1.tgz",
"integrity": "sha512-wK2sCs4feiiJeFXn3zvY0p41mdU5VUgbgs1rNsc/y5ngFUijdWd+iIN8eoyuZHKB8xN6BL4PdWmzqFmxNg6V2w=="
},
"emoji-regex": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
},
"escalade": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
"integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw=="
},
"event-pubsub": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/event-pubsub/-/event-pubsub-4.3.0.tgz",
"integrity": "sha512-z7IyloorXvKbFx9Bpie2+vMJKKx1fH1EN5yiTfp8CiLOTptSYy1g8H4yDpGlEdshL1PBiFtBHepF2cNsqeEeFQ=="
},
"get-caller-file": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
"integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg=="
},
"is-fullwidth-code-point": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="
},
"js-message": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/js-message/-/js-message-1.0.7.tgz",
"integrity": "sha512-efJLHhLjIyKRewNS9EGZ4UpI8NguuL6fKkhRxVuMmrGV2xN/0APGdQYwLFky5w9naebSZ0OwAGp0G6/2Cg90rA==",
"resolved": "https://registry.npmjs.org/js-message/-/js-message-1.0.7.tgz",
"integrity": "sha512-efJLHhLjIyKRewNS9EGZ4UpI8NguuL6fKkhRxVuMmrGV2xN/0APGdQYwLFky5w9naebSZ0OwAGp0G6/2Cg90rA=="
},
"js-queue": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/js-queue/-/js-queue-2.0.2.tgz",
"integrity": "sha512-pbKLsbCfi7kriM3s1J4DDCo7jQkI58zPLHi0heXPzPlj0hjUsm+FesPUbE0DSbIVIK503A36aUBoCN7eMFedkA==",
"resolved": "https://registry.npmjs.org/js-queue/-/js-queue-2.0.2.tgz",
"integrity": "sha512-pbKLsbCfi7kriM3s1J4DDCo7jQkI58zPLHi0heXPzPlj0hjUsm+FesPUbE0DSbIVIK503A36aUBoCN7eMFedkA==",
"requires": {
"easy-stack": "^1.0.1"
}
},
"make-error": {
"version": "1.3.6"
},
"module-alias": {
"version": "2.2.2",
"resolved": "https://registry.npmjs.org/module-alias/-/module-alias-2.2.2.tgz",
"integrity": "sha512-A/78XjoX2EmNvppVWEhM2oGk3x4lLxnkEA4jTbaK97QKSDjkIoOsKQlfylt/d3kKKi596Qy3NP5XrXJ6fZIC9Q=="
},
"node-ipc": {
"version": "9.2.1",
"resolved": "https://registry.npmjs.org/node-ipc/-/node-ipc-9.2.1.tgz",
"integrity": "sha512-mJzaM6O3xHf9VT8BULvJSbdVbmHUKRNOH7zDDkCrA1/T+CVjq2WVIDfLt0azZRXpgArJtl3rtmEozrbXPZ9GaQ==",
"requires": {
"event-pubsub": "4.3.0",
"js-message": "1.0.7",
"js-queue": "2.0.2"
}
},
"require-directory": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
"integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q=="
},
"string-width": {
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
"requires": {
"emoji-regex": "^8.0.0",
"is-fullwidth-code-point": "^3.0.0",
"strip-ansi": "^6.0.1"
}
},
"strip-ansi": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
"requires": {
"ansi-regex": "^5.0.1"
}
},
"ts-node": {
"version": "10.9.1",
"requires": {
"@cspotcode/source-map-support": "^0.8.0",
"@tsconfig/node10": "^1.0.7",
"@tsconfig/node12": "^1.0.7",
"@tsconfig/node14": "^1.0.0",
"@tsconfig/node16": "^1.0.2",
"acorn": "^8.4.1",
"acorn-walk": "^8.1.1",
"arg": "^4.1.0",
"create-require": "^1.1.0",
"diff": "^4.0.1",
"make-error": "^1.1.1",
"v8-compile-cache-lib": "^3.0.1",
"yn": "3.1.1"
}
},
"typescript": {
"version": "4.7.4"
},
"uuid": {
"version": "8.3.2",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
"integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg=="
},
"v8-compile-cache-lib": {
"version": "3.0.1"
},
"wrap-ansi": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
"integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
"requires": {
"ansi-styles": "^4.0.0",
"string-width": "^4.1.0",
"strip-ansi": "^6.0.0"
}
},
"y18n": {
"version": "5.0.8",
"resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
"integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA=="
},
"yargs": {
"version": "17.5.1",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-17.5.1.tgz",
"integrity": "sha512-t6YAJcxDkNX7NFYiVtKvWUz8l+PaKTLiL63mJYWR2GnHq2gjEWISzsLp9wg3aY36dY1j+gfIEL3pIF+XlJJfbA==",
"requires": {
"cliui": "^7.0.2",
"escalade": "^3.1.1",
"get-caller-file": "^2.0.5",
"require-directory": "^2.1.1",
"string-width": "^4.2.3",
"y18n": "^5.0.5",
"yargs-parser": "^21.0.0"
}
},
"yargs-parser": {
"version": "21.1.1",
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",
"integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw=="
},
"yn": {
"version": "3.1.1"
}
}
}

View File

@@ -0,0 +1,35 @@
{
"name": "native-messaging-test-runner",
"version": "1.0.0",
"description": "Test runner for Desktop native messaging",
"main": "dist/bw-handshake.ts",
"scripts": {
"handshake": "tsc && node dist/apps/desktop/native-messaging-test-runner/src/commands/bw-handshake.js",
"status": "tsc && node dist/apps/desktop/native-messaging-test-runner/src/commands/bw-status.js",
"retrieve": "tsc && node dist/apps/desktop/native-messaging-test-runner/src/commands/bw-credential-retrieval.js",
"create": "tsc && node dist/apps/desktop/native-messaging-test-runner/src/commands/bw-credential-create.js",
"update": "tsc && node dist/apps/desktop/native-messaging-test-runner/src/commands/bw-credential-update.js",
"generate": "tsc && node dist/apps/desktop/native-messaging-test-runner/src/commands/bw-generate-password.js"
},
"author": "Bitwarden Inc. <hello@bitwarden.com> (https://bitwarden.com)",
"license": "GPL-3.0",
"dependencies": {
"@bitwarden/common": "file:../../../libs/common",
"@bitwarden/node": "file:../../../libs/node",
"module-alias": "^2.2.2",
"node-ipc": "9.2.1",
"ts-node": "^10.9.1",
"uuid": "^8.3.2",
"yargs": "^17.5.1"
},
"devDependencies": {
"@tsconfig/node16": "^1.0.3",
"@types/node": "^18.6.5",
"@types/node-ipc": "9.2.0",
"typescript": "^4.7.4"
},
"_moduleAliases": {
"@bitwarden/common": "dist/libs/common/src",
"@bitwarden/node/services/nodeCryptoFunction.service": "dist/libs/node/src/services/nodeCryptoFunction.service"
}
}

View File

@@ -0,0 +1,66 @@
import "module-alias/register";
import yargs from "yargs";
import { hideBin } from "yargs/helpers";
import { NativeMessagingVersion } from "@bitwarden/common/enums/nativeMessagingVersion";
import { CredentialCreatePayload } from "../../../src/models/nativeMessaging/encryptedMessagePayloads/credentialCreatePayload";
import { LogUtils } from "../logUtils";
import NativeMessageService from "../nativeMessageService";
import * as config from "../variables";
const argv: any = yargs(hideBin(process.argv)).option("name", {
alias: "n",
demand: true,
describe: "Name that the created login will be given",
type: "string",
}).argv;
const { name } = argv;
(async () => {
const nativeMessageService = new NativeMessageService(NativeMessagingVersion.One);
// Handshake
LogUtils.logInfo("Sending Handshake");
const handshakeResponse = await nativeMessageService.sendHandshake(
config.testRsaPublicKey,
config.applicationName
);
if (!handshakeResponse.status) {
LogUtils.logError(" Handshake failed. Error was: " + handshakeResponse.error);
nativeMessageService.disconnect();
return;
}
// Get active account userId
const status = await nativeMessageService.checkStatus(handshakeResponse.sharedKey);
const activeUser = status.payload.filter((a) => a.active === true && a.status === "unlocked")[0];
if (activeUser === undefined) {
LogUtils.logError("No active or unlocked user");
}
LogUtils.logInfo("Active userId: " + activeUser.id);
LogUtils.logSuccess("Handshake success response");
const response = await nativeMessageService.credentialCreation(handshakeResponse.sharedKey, {
name: name,
userName: "SuperAwesomeUser",
password: "dolhpin",
uri: "google.com",
userId: activeUser.id,
} as CredentialCreatePayload);
if (response.payload.status === "failure") {
LogUtils.logError("Failure response returned ");
} else if (response.payload.status === "success") {
LogUtils.logSuccess("Success response returned ");
} else if (response.payload.error === "locked") {
LogUtils.logError("Error: vault is locked");
} else {
LogUtils.logWarning("Other response: ", response);
}
nativeMessageService.disconnect();
})();

View File

@@ -0,0 +1,46 @@
import "module-alias/register";
import yargs from "yargs";
import { hideBin } from "yargs/helpers";
import { NativeMessagingVersion } from "@bitwarden/common/enums/nativeMessagingVersion";
import { LogUtils } from "../logUtils";
import NativeMessageService from "../nativeMessageService";
import * as config from "../variables";
const argv: any = yargs(hideBin(process.argv)).option("uri", {
alias: "u",
demand: true,
describe: "The uri to retrieve logins for",
type: "string",
}).argv;
const { uri } = argv;
(async () => {
const nativeMessageService = new NativeMessageService(NativeMessagingVersion.One);
// Handshake
LogUtils.logInfo("Sending Handshake");
const handshakeResponse = await nativeMessageService.sendHandshake(
config.testRsaPublicKey,
config.applicationName
);
if (!handshakeResponse.status) {
LogUtils.logError(" Handshake failed. Error was: " + handshakeResponse.error);
nativeMessageService.disconnect();
return;
}
LogUtils.logSuccess("Handshake success response");
const response = await nativeMessageService.credentialRetrieval(handshakeResponse.sharedKey, uri);
if (response.payload.error != null) {
LogUtils.logError("Error response returned: ", response.payload.error);
} else {
LogUtils.logSuccess("Credentials returned ", response);
}
nativeMessageService.disconnect();
})();

View File

@@ -0,0 +1,89 @@
import "module-alias/register";
import yargs from "yargs";
import { hideBin } from "yargs/helpers";
import { NativeMessagingVersion } from "@bitwarden/common/enums/nativeMessagingVersion";
import { CredentialUpdatePayload } from "../../../src/models/nativeMessaging/encryptedMessagePayloads/credentialUpdatePayload";
import { LogUtils } from "../logUtils";
import NativeMessageService from "../nativeMessageService";
import * as config from "../variables";
// Command line arguments
const argv: any = yargs(hideBin(process.argv))
.option("name", {
alias: "n",
demand: true,
describe: "Name that the updated login will be given",
type: "string",
})
.option("username", {
alias: "u",
demand: true,
describe: "Username that the login will be given",
type: "string",
})
.option("password", {
alias: "p",
demand: true,
describe: "Password that the login will be given",
type: "string",
})
.option("uri", {
demand: true,
describe: "Uri that the login will be given",
type: "string",
})
.option("credentialId", {
demand: true,
describe: "GUID of the credential to update",
type: "string",
}).argv;
const { name, username, password, uri, credentialId } = argv;
(async () => {
const nativeMessageService = new NativeMessageService(NativeMessagingVersion.One);
// Handshake
LogUtils.logInfo("Sending Handshake");
const handshakeResponse = await nativeMessageService.sendHandshake(
config.testRsaPublicKey,
config.applicationName
);
if (!handshakeResponse.status) {
LogUtils.logError(" Handshake failed. Error was: " + handshakeResponse.error);
nativeMessageService.disconnect();
return;
}
LogUtils.logSuccess("Handshake success response");
// Get active account userId
const status = await nativeMessageService.checkStatus(handshakeResponse.sharedKey);
const activeUser = status.payload.filter((a) => a.active === true && a.status === "unlocked")[0];
if (activeUser === undefined) {
LogUtils.logError("No active or unlocked user");
}
LogUtils.logInfo("Active userId: " + activeUser.id);
const response = await nativeMessageService.credentialUpdate(handshakeResponse.sharedKey, {
name: name,
password: password,
userName: username,
uri: uri,
userId: activeUser.id,
credentialId: credentialId,
} as CredentialUpdatePayload);
if (response.payload.status === "failure") {
LogUtils.logError("Failure response returned ");
} else if (response.payload.status === "success") {
LogUtils.logSuccess("Success response returned ");
} else {
LogUtils.logWarning("Other response: ", response);
}
nativeMessageService.disconnect();
})();

View File

@@ -0,0 +1,46 @@
import "module-alias/register";
import yargs from "yargs";
import { hideBin } from "yargs/helpers";
import { NativeMessagingVersion } from "@bitwarden/common/enums/nativeMessagingVersion";
import { LogUtils } from "../logUtils";
import NativeMessageService from "../nativeMessageService";
import * as config from "../variables";
const argv: any = yargs(hideBin(process.argv)).option("userId", {
alias: "u",
demand: true,
describe: "UserId to generate password for",
type: "string",
}).argv;
const { userId } = argv;
(async () => {
const nativeMessageService = new NativeMessageService(NativeMessagingVersion.One);
// Handshake
LogUtils.logInfo("Sending Handshake");
const handshakeResponse = await nativeMessageService.sendHandshake(
config.testRsaPublicKey,
config.applicationName
);
if (!handshakeResponse.status) {
LogUtils.logError(" Handshake failed. Error was: " + handshakeResponse.error);
nativeMessageService.disconnect();
return;
}
LogUtils.logSuccess("Handshake success response");
const response = await nativeMessageService.generatePassword(handshakeResponse.sharedKey, userId);
if (response.payload.error != null) {
LogUtils.logError("Error response returned: ", response.payload.error);
} else {
LogUtils.logSuccess("Response: ", response);
}
nativeMessageService.disconnect();
})();

View File

@@ -0,0 +1,25 @@
import "module-alias/register";
import { NativeMessagingVersion } from "@bitwarden/common/enums/nativeMessagingVersion";
import { LogUtils } from "../logUtils";
import NativeMessageService from "../nativeMessageService";
import * as config from "../variables";
(async () => {
const nativeMessageService = new NativeMessageService(NativeMessagingVersion.One);
const response = await nativeMessageService.sendHandshake(
config.testRsaPublicKey,
config.applicationName
);
LogUtils.logSuccess("Received response to handshake request");
if (response.status) {
LogUtils.logSuccess("Handshake success response");
} else if (response.error === "canceled") {
LogUtils.logWarning("Handshake canceled by user");
} else {
LogUtils.logError("Handshake failure response");
}
nativeMessageService.disconnect();
})();

View File

@@ -0,0 +1,29 @@
import "module-alias/register";
import { NativeMessagingVersion } from "@bitwarden/common/enums/nativeMessagingVersion";
import { LogUtils } from "../logUtils";
import NativeMessageService from "../nativeMessageService";
import * as config from "../variables";
(async () => {
const nativeMessageService = new NativeMessageService(NativeMessagingVersion.One);
LogUtils.logInfo("Sending Handshake");
const handshakeResponse = await nativeMessageService.sendHandshake(
config.testRsaPublicKey,
config.applicationName
);
LogUtils.logSuccess("Received response to handshake request");
if (!handshakeResponse.status) {
LogUtils.logError(" Handshake failed. Error was: " + handshakeResponse.error);
nativeMessageService.disconnect();
return;
}
LogUtils.logSuccess("Handshake success response");
const status = await nativeMessageService.checkStatus(handshakeResponse.sharedKey);
LogUtils.logSuccess("Status output is: ", status);
nativeMessageService.disconnect();
})();

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