diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
index 203c7ae7607..2665f345568 100644
--- a/.github/CODEOWNERS
+++ b/.github/CODEOWNERS
@@ -8,7 +8,7 @@
apps/desktop/desktop_native @bitwarden/team-platform-dev
apps/desktop/desktop_native/objc/src/native/autofill @bitwarden/team-autofill-dev
apps/desktop/desktop_native/core/src/autofill @bitwarden/team-autofill-dev
-## No ownership fo Cargo.lock and Cargo.toml to allow dependency updates
+## No ownership for Cargo.lock and Cargo.toml to allow dependency updates
apps/desktop/desktop_native/Cargo.lock
apps/desktop/desktop_native/Cargo.toml
@@ -96,6 +96,12 @@ libs/logging @bitwarden/team-platform-dev
libs/storage-test-utils @bitwarden/team-platform-dev
libs/messaging @bitwarden/team-platform-dev
libs/messaging-internal @bitwarden/team-platform-dev
+libs/serialization @bitwarden/team-platform-dev
+libs/guid @bitwarden/team-platform-dev
+libs/client-type @bitwarden/team-platform-dev
+libs/core-test-utils @bitwarden/team-platform-dev
+libs/state @bitwarden/team-platform-dev
+libs/state-test-utils @bitwarden/team-platform-dev
# Web utils used across app and connectors
apps/web/src/utils/ @bitwarden/team-platform-dev
# Web core and shared files
diff --git a/.github/workflows/build-browser.yml b/.github/workflows/build-browser.yml
index bd7d70e8543..be140b9a20e 100644
--- a/.github/workflows/build-browser.yml
+++ b/.github/workflows/build-browser.yml
@@ -269,19 +269,19 @@ jobs:
# Declare variable as indexed array
declare -a FILES
- # Search for source files that are greater than 4M
+ # Search for source files that are greater than 5M
TARGET_DIR='./browser-source/apps/browser'
while IFS=' ' read -r RESULT; do
FILES+=("$RESULT")
- done < <(find $TARGET_DIR -size +4M)
+ done < <(find $TARGET_DIR -size +5M)
# Validate results and provide messaging
if [[ ${#FILES[@]} -ne 0 ]]; then
- echo "File(s) exceeds size limit: 4MB"
+ echo "File(s) exceeds size limit: 5MB"
for FILE in ${FILES[@]}; do
echo "- $(du --si $FILE)"
done
- echo "ERROR Firefox rejects extension uploads that contain files larger than 4MB"
+ echo "ERROR Firefox rejects extension uploads that contain files larger than 5MB"
# Invoke failure
exit 1
fi
diff --git a/.github/workflows/build-web.yml b/.github/workflows/build-web.yml
index b4163d161cf..f00ae07fba3 100644
--- a/.github/workflows/build-web.yml
+++ b/.github/workflows/build-web.yml
@@ -280,7 +280,7 @@ jobs:
IMAGE_NAME: ${{ steps.image-name.outputs.name }}
run: |
mkdir build
- docker run --rm --volume $(pwd)/build:/temp --entrypoint bash \
+ docker run --rm --volume $(pwd)/build:/temp --entrypoint sh \
$IMAGE_NAME -c "cp -r ./ /temp"
zip -r web-${{ env._VERSION }}-${{ matrix.artifact_name }}.zip build
diff --git a/.github/workflows/deploy-web.yml b/.github/workflows/deploy-web.yml
index e21f7ae1e79..d3788dc77b9 100644
--- a/.github/workflows/deploy-web.yml
+++ b/.github/workflows/deploy-web.yml
@@ -69,7 +69,6 @@ jobs:
azure_login_client_key_name: ${{ steps.config.outputs.azure_login_client_key_name }}
azure_login_subscription_id_key_name: ${{ steps.config.outputs.azure_login_subscription_id_key_name }}
retrieve_secrets_keyvault: ${{ steps.config.outputs.retrieve_secrets_keyvault }}
- sync_utility: ${{ steps.config.outputs.sync_utility }}
sync_delete_destination_files: ${{ steps.config.outputs.sync_delete_destination_files }}
slack_channel_name: ${{ steps.config.outputs.slack_channel_name }}
steps:
@@ -127,8 +126,6 @@ jobs:
echo "slack_channel_name=alerts-deploy-dev" >> $GITHUB_OUTPUT
;;
esac
- # Set the sync utility to use for deployment to the environment (az-sync or azcopy)
- echo "sync_utility=azcopy" >> $GITHUB_OUTPUT
- name: Environment Protection
env:
@@ -337,32 +334,6 @@ jobs:
description: 'Deployment from branch/tag: ${{ inputs.branch-or-tag }}'
ref: ${{ needs.artifact-check.outputs.artifact_build_commit }}
- - name: Login to Azure
- uses: bitwarden/gh-actions/azure-login@main
- with:
- subscription_id: ${{ secrets[needs.setup.outputs.azure_login_subscription_id_key_name] }}
- tenant_id: ${{ secrets.AZURE_TENANT_ID }}
- client_id: ${{ secrets[needs.setup.outputs.azure_login_client_key_name] }}
-
- - name: Retrieve Storage Account connection string for az sync
- if: ${{ needs.setup.outputs.sync_utility == 'az-sync' }}
- id: retrieve-secrets-az-sync
- uses: bitwarden/gh-actions/get-keyvault-secrets@main
- with:
- keyvault: ${{ needs.setup.outputs.retrieve_secrets_keyvault }}
- secrets: "sa-bitwarden-web-vault-dev-key-temp"
-
- - name: Retrieve Storage Account name and SPN credentials for azcopy
- if: ${{ needs.setup.outputs.sync_utility == 'azcopy' }}
- id: retrieve-secrets-azcopy
- uses: bitwarden/gh-actions/get-keyvault-secrets@main
- with:
- keyvault: ${{ needs.setup.outputs.retrieve_secrets_keyvault }}
- secrets: "sa-bitwarden-web-vault-name,sp-bitwarden-web-vault-password,sp-bitwarden-web-vault-appid,sp-bitwarden-web-vault-tenant"
-
- - name: Log out from Azure
- uses: bitwarden/gh-actions/azure-logout@main
-
- name: 'Download latest cloud asset using GitHub Run ID: ${{ inputs.build-web-run-id }}'
if: ${{ inputs.build-web-run-id }}
uses: bitwarden/gh-actions/download-artifacts@main
@@ -389,28 +360,32 @@ jobs:
working-directory: apps/web
run: unzip ${{ env._ENVIRONMENT_ARTIFACT }}
- - name: Sync to Azure Storage Account using az storage blob sync
- if: ${{ needs.setup.outputs.sync_utility == 'az-sync' }}
- working-directory: apps/web
- run: |
- az storage blob sync \
- --source "./build" \
- --container '$web' \
- --connection-string "${{ steps.retrieve-secrets-az-sync.outputs.sa-bitwarden-web-vault-dev-key-temp }}" \
- --delete-destination=${{ inputs.force-delete-destination }}
+ - name: Login to Azure
+ uses: bitwarden/gh-actions/azure-login@main
+ with:
+ subscription_id: ${{ secrets[needs.setup.outputs.azure_login_subscription_id_key_name] }}
+ tenant_id: ${{ secrets.AZURE_TENANT_ID }}
+ client_id: ${{ secrets[needs.setup.outputs.azure_login_client_key_name] }}
+
+ - name: Retrieve Storage Account name
+ id: retrieve-secrets-azcopy
+ uses: bitwarden/gh-actions/get-keyvault-secrets@main
+ with:
+ keyvault: ${{ needs.setup.outputs.retrieve_secrets_keyvault }}
+ secrets: "sa-bitwarden-web-vault-name"
- name: Sync to Azure Storage Account using azcopy
- if: ${{ needs.setup.outputs.sync_utility == 'azcopy' }}
working-directory: apps/web
env:
- AZCOPY_AUTO_LOGIN_TYPE: SPN
- AZCOPY_SPA_APPLICATION_ID: ${{ steps.retrieve-secrets-azcopy.outputs.sp-bitwarden-web-vault-appid }}
- AZCOPY_SPA_CLIENT_SECRET: ${{ steps.retrieve-secrets-azcopy.outputs.sp-bitwarden-web-vault-password }}
- AZCOPY_TENANT_ID: ${{ steps.retrieve-secrets-azcopy.outputs.sp-bitwarden-web-vault-tenant }}
+ AZCOPY_AUTO_LOGIN_TYPE: AZCLI
+ AZCOPY_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }}
run: |
azcopy sync ./build 'https://${{ steps.retrieve-secrets-azcopy.outputs.sa-bitwarden-web-vault-name }}.blob.core.windows.net/$web/' \
--delete-destination=${{ inputs.force-delete-destination }} --compare-hash="MD5"
+ - name: Log out from Azure
+ uses: bitwarden/gh-actions/azure-logout@main
+
- name: Debug sync logs
if: ${{ inputs.debug }}
run: cat /home/runner/.azcopy/*.log
diff --git a/.github/workflows/scan.yml b/.github/workflows/scan.yml
index c96dae51c0e..57774fa9991 100644
--- a/.github/workflows/scan.yml
+++ b/.github/workflows/scan.yml
@@ -50,4 +50,7 @@ jobs:
permissions:
contents: read
pull-requests: write
- id-token: write
\ No newline at end of file
+ id-token: write
+ with:
+ sonar-test-inclusions: "**/*.spec.ts"
+ sonar-exclusions: "**/*.spec.ts"
diff --git a/apps/browser/package.json b/apps/browser/package.json
index c0e8058a1bc..8cf643135a2 100644
--- a/apps/browser/package.json
+++ b/apps/browser/package.json
@@ -14,16 +14,13 @@
"build:watch:firefox": "npm run build:firefox -- --watch",
"build:watch:opera": "npm run build:opera -- --watch",
"build:watch:safari": "npm run build:safari -- --watch",
+ "build:watch:firefox:mv3": "cross-env MANIFEST_VERSION=3 npm run build:firefox -- --watch",
+ "build:watch:safari:mv3": "cross-env MANIFEST_VERSION=3 npm run build:safari -- --watch",
"build:prod:chrome": "cross-env NODE_ENV=production npm run build:chrome",
"build:prod:edge": "cross-env NODE_ENV=production npm run build:edge",
"build:prod:firefox": "cross-env NODE_ENV=production npm run build:firefox",
"build:prod:opera": "cross-env NODE_ENV=production npm run build:opera",
"build:prod:safari": "cross-env NODE_ENV=production npm run build:safari",
- "build:nonprod:chrome": "cross-env npm run build:chrome",
- "build:nonprod:edge": "cross-env npm run build:edge",
- "build:nonprod:firefox": "cross-env npm run build:firefox",
- "build:nonprod:opera": "cross-env npm run build:opera",
- "build:nonprod:safari": "cross-env npm run build:safari",
"dist:chrome": "npm run build:prod:chrome && mkdir -p dist && ./scripts/compress.sh dist-chrome.zip",
"dist:edge": "npm run build:prod:edge && mkdir -p dist && ./scripts/compress.sh dist-edge.zip",
"dist:firefox": "npm run build:prod:firefox && mkdir -p dist && ./scripts/compress.sh dist-firefox.zip",
@@ -32,15 +29,6 @@
"dist:firefox:mv3": "cross-env MANIFEST_VERSION=3 npm run dist:firefox",
"dist:opera:mv3": "cross-env MANIFEST_VERSION=3 npm run dist:opera",
"dist:safari:mv3": "cross-env MANIFEST_VERSION=3 npm run dist:safari",
- "dist:chrome:nonprod": "npm run build:nonprod:chrome && mkdir -p dist && ./scripts/compress.sh dist-chrome.zip",
- "dist:edge:nonprod": "npm run build:nonprod:edge && mkdir -p dist && ./scripts/compress.sh dist-edge.zip",
- "dist:firefox:nonprod": "npm run build:nonprod:firefox && mkdir -p dist && ./scripts/compress.sh dist-firefox.zip",
- "dist:opera:nonprod": "npm run build:nonprod:opera && mkdir -p dist && ./scripts/compress.sh dist-opera.zip",
- "dist:safari:nonprod": "npm run build:nonprod:safari && ./scripts/package-safari.ps1",
- "dist:edge:mv3:nonprod": "cross-env MANIFEST_VERSION=3 npm run dist:edge:nonprod",
- "dist:firefox:mv3:nonprod": "cross-env MANIFEST_VERSION=3 npm run dist:firefox:nonprod",
- "dist:opera:mv3:nonprod": "cross-env MANIFEST_VERSION=3 npm run dist:opera:nonprod",
- "dist:safari:mv3:nonprod": "cross-env MANIFEST_VERSION=3 npm run dist:safari:nonprod",
"test": "jest",
"test:watch": "jest --watch",
"test:watch:all": "jest --watchAll",
diff --git a/apps/browser/src/_locales/ar/messages.json b/apps/browser/src/_locales/ar/messages.json
index d7aef05ab92..65ee9cab458 100644
--- a/apps/browser/src/_locales/ar/messages.json
+++ b/apps/browser/src/_locales/ar/messages.json
@@ -547,6 +547,9 @@
"searchVault": {
"message": "البحث في الخزانة"
},
+ "resetSearch": {
+ "message": "Reset search"
+ },
"edit": {
"message": "تعديل"
},
diff --git a/apps/browser/src/_locales/az/messages.json b/apps/browser/src/_locales/az/messages.json
index 5e7bf056980..16b74ffe175 100644
--- a/apps/browser/src/_locales/az/messages.json
+++ b/apps/browser/src/_locales/az/messages.json
@@ -547,6 +547,9 @@
"searchVault": {
"message": "Seyfdə axtar"
},
+ "resetSearch": {
+ "message": "Reset search"
+ },
"edit": {
"message": "Düzəliş et"
},
diff --git a/apps/browser/src/_locales/be/messages.json b/apps/browser/src/_locales/be/messages.json
index a49899eaee0..d7a1db3adc8 100644
--- a/apps/browser/src/_locales/be/messages.json
+++ b/apps/browser/src/_locales/be/messages.json
@@ -547,6 +547,9 @@
"searchVault": {
"message": "Пошук у сховішчы"
},
+ "resetSearch": {
+ "message": "Reset search"
+ },
"edit": {
"message": "Рэдагаваць"
},
diff --git a/apps/browser/src/_locales/bg/messages.json b/apps/browser/src/_locales/bg/messages.json
index 672e029a662..79e13cdb677 100644
--- a/apps/browser/src/_locales/bg/messages.json
+++ b/apps/browser/src/_locales/bg/messages.json
@@ -547,6 +547,9 @@
"searchVault": {
"message": "Търсене в трезора"
},
+ "resetSearch": {
+ "message": "Нулиране на търсенето"
+ },
"edit": {
"message": "Редактиране"
},
diff --git a/apps/browser/src/_locales/bn/messages.json b/apps/browser/src/_locales/bn/messages.json
index 4e30612b9a6..a3c029fb963 100644
--- a/apps/browser/src/_locales/bn/messages.json
+++ b/apps/browser/src/_locales/bn/messages.json
@@ -547,6 +547,9 @@
"searchVault": {
"message": "ভল্ট খুঁজুন"
},
+ "resetSearch": {
+ "message": "Reset search"
+ },
"edit": {
"message": "সম্পাদনা"
},
diff --git a/apps/browser/src/_locales/bs/messages.json b/apps/browser/src/_locales/bs/messages.json
index be64d0bade5..8a94ba3e9e9 100644
--- a/apps/browser/src/_locales/bs/messages.json
+++ b/apps/browser/src/_locales/bs/messages.json
@@ -547,6 +547,9 @@
"searchVault": {
"message": "Search vault"
},
+ "resetSearch": {
+ "message": "Reset search"
+ },
"edit": {
"message": "Edit"
},
diff --git a/apps/browser/src/_locales/ca/messages.json b/apps/browser/src/_locales/ca/messages.json
index f6c40da1096..42fb9c24003 100644
--- a/apps/browser/src/_locales/ca/messages.json
+++ b/apps/browser/src/_locales/ca/messages.json
@@ -547,6 +547,9 @@
"searchVault": {
"message": "Cerca en la caixa forta"
},
+ "resetSearch": {
+ "message": "Reset search"
+ },
"edit": {
"message": "Edita"
},
diff --git a/apps/browser/src/_locales/cs/messages.json b/apps/browser/src/_locales/cs/messages.json
index 4e0096a1520..3f8dd2e2b48 100644
--- a/apps/browser/src/_locales/cs/messages.json
+++ b/apps/browser/src/_locales/cs/messages.json
@@ -547,6 +547,9 @@
"searchVault": {
"message": "Vyhledat v trezoru"
},
+ "resetSearch": {
+ "message": "Resetovat hledání"
+ },
"edit": {
"message": "Upravit"
},
diff --git a/apps/browser/src/_locales/cy/messages.json b/apps/browser/src/_locales/cy/messages.json
index 1235b49dd2c..307373da9aa 100644
--- a/apps/browser/src/_locales/cy/messages.json
+++ b/apps/browser/src/_locales/cy/messages.json
@@ -547,6 +547,9 @@
"searchVault": {
"message": "Chwilio'r gell"
},
+ "resetSearch": {
+ "message": "Reset search"
+ },
"edit": {
"message": "Golygu"
},
diff --git a/apps/browser/src/_locales/da/messages.json b/apps/browser/src/_locales/da/messages.json
index bc34810f97f..4b6da81a994 100644
--- a/apps/browser/src/_locales/da/messages.json
+++ b/apps/browser/src/_locales/da/messages.json
@@ -547,6 +547,9 @@
"searchVault": {
"message": "Søg i boks"
},
+ "resetSearch": {
+ "message": "Reset search"
+ },
"edit": {
"message": "Redigér"
},
diff --git a/apps/browser/src/_locales/de/messages.json b/apps/browser/src/_locales/de/messages.json
index 2e3d9369c41..9ef82a6d5ae 100644
--- a/apps/browser/src/_locales/de/messages.json
+++ b/apps/browser/src/_locales/de/messages.json
@@ -547,6 +547,9 @@
"searchVault": {
"message": "Tresor durchsuchen"
},
+ "resetSearch": {
+ "message": "Suche zurücksetzen"
+ },
"edit": {
"message": "Bearbeiten"
},
diff --git a/apps/browser/src/_locales/el/messages.json b/apps/browser/src/_locales/el/messages.json
index 014d17b74c8..fa4f3ac0f3c 100644
--- a/apps/browser/src/_locales/el/messages.json
+++ b/apps/browser/src/_locales/el/messages.json
@@ -547,6 +547,9 @@
"searchVault": {
"message": "Αναζήτηση στο vault"
},
+ "resetSearch": {
+ "message": "Reset search"
+ },
"edit": {
"message": "Επεξεργασία"
},
diff --git a/apps/browser/src/_locales/en/messages.json b/apps/browser/src/_locales/en/messages.json
index a1b41b44bfd..ad933c24875 100644
--- a/apps/browser/src/_locales/en/messages.json
+++ b/apps/browser/src/_locales/en/messages.json
@@ -3657,25 +3657,6 @@
"thisRequestIsNoLongerValid": {
"message": "This request is no longer valid."
},
- "areYouTryingToAccessYourAccount": {
- "message": "Are you trying to access your account?"
- },
- "logInConfirmedForEmailOnDevice": {
- "message": "Login confirmed for $EMAIL$ on $DEVICE$",
- "placeholders": {
- "email": {
- "content": "$1",
- "example": "name@example.com"
- },
- "device": {
- "content": "$2",
- "example": "iOS"
- }
- }
- },
- "youDeniedALogInAttemptFromAnotherDevice": {
- "message": "You denied a login attempt from another device. If this really was you, try to log in with the device again."
- },
"loginRequestHasAlreadyExpired": {
"message": "Login request has already expired."
},
diff --git a/apps/browser/src/_locales/en_GB/messages.json b/apps/browser/src/_locales/en_GB/messages.json
index a17a48e95b8..a70fbd85123 100644
--- a/apps/browser/src/_locales/en_GB/messages.json
+++ b/apps/browser/src/_locales/en_GB/messages.json
@@ -547,6 +547,9 @@
"searchVault": {
"message": "Search vault"
},
+ "resetSearch": {
+ "message": "Reset search"
+ },
"edit": {
"message": "Edit"
},
diff --git a/apps/browser/src/_locales/en_IN/messages.json b/apps/browser/src/_locales/en_IN/messages.json
index 9f383c2f0e3..39de06249fc 100644
--- a/apps/browser/src/_locales/en_IN/messages.json
+++ b/apps/browser/src/_locales/en_IN/messages.json
@@ -547,6 +547,9 @@
"searchVault": {
"message": "Search vault"
},
+ "resetSearch": {
+ "message": "Reset search"
+ },
"edit": {
"message": "Edit"
},
diff --git a/apps/browser/src/_locales/es/messages.json b/apps/browser/src/_locales/es/messages.json
index 35a28528f49..3b681054abc 100644
--- a/apps/browser/src/_locales/es/messages.json
+++ b/apps/browser/src/_locales/es/messages.json
@@ -547,6 +547,9 @@
"searchVault": {
"message": "Buscar en caja fuerte"
},
+ "resetSearch": {
+ "message": "Reset search"
+ },
"edit": {
"message": "Editar"
},
diff --git a/apps/browser/src/_locales/et/messages.json b/apps/browser/src/_locales/et/messages.json
index daadcbf00e9..8b61aa70a60 100644
--- a/apps/browser/src/_locales/et/messages.json
+++ b/apps/browser/src/_locales/et/messages.json
@@ -547,6 +547,9 @@
"searchVault": {
"message": "Otsi hoidlast"
},
+ "resetSearch": {
+ "message": "Reset search"
+ },
"edit": {
"message": "Muuda"
},
diff --git a/apps/browser/src/_locales/eu/messages.json b/apps/browser/src/_locales/eu/messages.json
index e5f836fcaae..73bd992dacb 100644
--- a/apps/browser/src/_locales/eu/messages.json
+++ b/apps/browser/src/_locales/eu/messages.json
@@ -547,6 +547,9 @@
"searchVault": {
"message": "Bilatu kutxa gotorrean"
},
+ "resetSearch": {
+ "message": "Reset search"
+ },
"edit": {
"message": "Editatu"
},
diff --git a/apps/browser/src/_locales/fa/messages.json b/apps/browser/src/_locales/fa/messages.json
index e551e96f74a..18afcb775f9 100644
--- a/apps/browser/src/_locales/fa/messages.json
+++ b/apps/browser/src/_locales/fa/messages.json
@@ -547,6 +547,9 @@
"searchVault": {
"message": "جستجوی گاوصندوق"
},
+ "resetSearch": {
+ "message": "Reset search"
+ },
"edit": {
"message": "ویرایش"
},
diff --git a/apps/browser/src/_locales/fi/messages.json b/apps/browser/src/_locales/fi/messages.json
index 22f2046bae3..894b50b5273 100644
--- a/apps/browser/src/_locales/fi/messages.json
+++ b/apps/browser/src/_locales/fi/messages.json
@@ -547,6 +547,9 @@
"searchVault": {
"message": "Etsi holvista"
},
+ "resetSearch": {
+ "message": "Nollaa haku"
+ },
"edit": {
"message": "Muokkaa"
},
@@ -887,7 +890,7 @@
"message": "Viimeistele kirjautuminen seuraamalla seuraavia vaiheita."
},
"followTheStepsBelowToFinishLoggingInWithSecurityKey": {
- "message": "Follow the steps below to finish logging in with your security key."
+ "message": "Seuraa alla olevia ohjeita, jotta pääset kirjautumaan suojausavaimellasi."
},
"restartRegistration": {
"message": "Aloita rekisteröityminen alusta"
@@ -1063,7 +1066,7 @@
"message": "Tallenna"
},
"notificationViewAria": {
- "message": "View $ITEMNAME$, opens in new window",
+ "message": "Näytä $ITEMNAME$. Avautuu uudessa ikkunassa",
"placeholders": {
"itemName": {
"content": "$1"
@@ -1093,15 +1096,15 @@
}
},
"notificationLoginSaveConfirmation": {
- "message": "saved to Bitwarden.",
+ "message": "tallennettu Bitwardeniin.",
"description": "Shown to user after item is saved."
},
"notificationLoginUpdatedConfirmation": {
- "message": "updated in Bitwarden.",
+ "message": "päivitetty Bitwardeniin.",
"description": "Shown to user after item is updated."
},
"selectItemAriaLabel": {
- "message": "Select $ITEMTYPE$, $ITEMNAME$",
+ "message": "Valitse $ITEMTYPE$, $ITEMNAME$",
"description": "Used by screen readers. $1 is the item type (like vault or folder), $2 is the selected item name.",
"placeholders": {
"itemType": {
@@ -1121,7 +1124,7 @@
"description": "Button text for updating an existing login entry."
},
"unlockToSave": {
- "message": "Unlock to save this login",
+ "message": "Avaa tallentaaksesi tämä kirjautumistieto",
"description": "User prompt to take action in order to save the login they just entered."
},
"saveLogin": {
@@ -1174,10 +1177,10 @@
"description": "Detailed error message shown when saving login details fails."
},
"changePasswordWarning": {
- "message": "After changing your password, you will need to log in with your new password. Active sessions on other devices will be logged out within one hour."
+ "message": "Kun olet vaihtanut salasanaasi, sinun täytyy kirjautua sisään uudella salasanalla. Aktiiviset istunnot muilla laitteilla kirjataan ulos tunnin kuluessa."
},
"accountRecoveryUpdateMasterPasswordSubtitle": {
- "message": "Change your master password to complete account recovery."
+ "message": "Vaihda pääsalasanasi, jotta voit jatkaa tilin palautusta."
},
"enableChangedPasswordNotification": {
"message": "Kysy päivitetäänkö kirjautumistieto"
@@ -1372,7 +1375,7 @@
"message": "Ominaisuus ei ole käytettävissä"
},
"legacyEncryptionUnsupported": {
- "message": "Legacy encryption is no longer supported. Please contact support to recover your account."
+ "message": "Vanhaa salausta ei enää tueta. Ota yhteyttä tukeen palauttaaksesi tilisi."
},
"premiumMembership": {
"message": "Premium-jäsenyys"
@@ -1606,10 +1609,10 @@
"message": "Automaattitäytön ehdotukset"
},
"autofillSpotlightTitle": {
- "message": "Easily find autofill suggestions"
+ "message": "Löydä helposti automaattisen täytön ehdotukset"
},
"autofillSpotlightDesc": {
- "message": "Turn off your browser's autofill settings, so they don't conflict with Bitwarden."
+ "message": "Poista käytöstä selaimesi oletuksena asetetut automaattisen täytön asetukset, joten ne eivät aiheuta ongelmia Bitwardenin kanssa."
},
"turnOffBrowserAutofill": {
"message": "Poista automaattitäyttö käytöstä selaimessa $BROWSER$",
@@ -1830,7 +1833,7 @@
"message": "Turvakoodi (CVC/CVV)"
},
"cardNumber": {
- "message": "card number"
+ "message": "kortin numero"
},
"ex": {
"message": "esim."
@@ -1932,7 +1935,7 @@
"message": "SSH-avain"
},
"typeNote": {
- "message": "Note"
+ "message": "Muistiinpano"
},
"newItemHeader": {
"message": "Uusi $TYPE$",
@@ -2166,7 +2169,7 @@
"message": "Aseta PIN-koodi Bitwardenin avaukselle. PIN-asetukset tyhjentyvät, jos kirjaudut laajennuksesta kokonaan ulos."
},
"setPinCode": {
- "message": "You can use this PIN to unlock Bitwarden. Your PIN will be reset if you ever fully log out of the application."
+ "message": "Voit käyttää PIN-koodia avataksesi Bitwardenin. PIN-koodisi nollataan, mikäli kirjaudut täysin ulos sovelluksesta."
},
"pinRequired": {
"message": "PIN-koodi vaaditaan."
@@ -2497,10 +2500,10 @@
"message": "Organisaatiokäytäntö estää kohteiden tuonnin yksityiseen holviisi."
},
"restrictCardTypeImport": {
- "message": "Cannot import card item types"
+ "message": "Ei voitu tuoda kortteja"
},
"restrictCardTypeImportDesc": {
- "message": "A policy set by 1 or more organizations prevents you from importing cards to your vaults."
+ "message": "Käytäntö, jonka on asettanut 1 tai useampi organisaatiosi estää sinua tuomasta korttitietoja holviisi."
},
"domainsTitle": {
"message": "Verkkotunnukset",
@@ -2547,7 +2550,7 @@
}
},
"atRiskPassword": {
- "message": "At-risk password"
+ "message": "Riskialttiit salasanat"
},
"atRiskPasswords": {
"message": "Vaarantuneet salasanat"
@@ -2584,7 +2587,7 @@
}
},
"atRiskChangePrompt": {
- "message": "Your password for this site is at-risk. $ORGANIZATION$ has requested that you change it.",
+ "message": "Salasanasi tälle sivustolle ei ole turvallinen. $ORGANIZATION$ on ilmoittanut, että se tulisi vaihtaa.",
"placeholders": {
"organization": {
"content": "$1",
@@ -2594,7 +2597,7 @@
"description": "Notification body when a login triggers an at-risk password change request and the change password domain is known."
},
"atRiskNavigatePrompt": {
- "message": "$ORGANIZATION$ wants you to change this password because it is at-risk. Navigate to your account settings to change the password.",
+ "message": "$ORGANIZATION$ ovat pyytäneet, että vaihdat tämän salasnaan, sillä se ei ole turvallinen. Mene tilin asetuksiin ja vaihda salasana.",
"placeholders": {
"organization": {
"content": "$1",
@@ -2723,7 +2726,7 @@
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
},
"maxAccessCountReached": {
- "message": "Max access count reached",
+ "message": "Käyttökertojen enimmäismäärä on saavutettu",
"description": "This text will be displayed after a Send has been accessed the maximum amount of times."
},
"hideTextByDefault": {
@@ -2929,7 +2932,7 @@
"message": "Sinun on vahvistettava sähköpostiosoitteesi käyttääksesi ominaisuutta. Voit vahvistaa osoitteesi verkkoholvissa."
},
"masterPasswordSuccessfullySet": {
- "message": "Master password successfully set"
+ "message": "Pääsalasana asetettu"
},
"updatedMasterPassword": {
"message": "Pääsalasanasi on vaihdettu"
@@ -3070,13 +3073,13 @@
"message": "Yksilöllistä tunnistetta ei löytynyt."
},
"removeMasterPasswordForOrganizationUserKeyConnector": {
- "message": "A master password is no longer required for members of the following organization. Please confirm the domain below with your organization administrator."
+ "message": "Pääsalasanaa ei enää tarvita tämän organisaation jäsenille. Ole hyvä ja vahvista alla oleva verkkotunnus organisaation ylläpitäjän kanssa."
},
"organizationName": {
"message": "Organisaation nimi"
},
"keyConnectorDomain": {
- "message": "Key Connector domain"
+ "message": "Key Connector URL"
},
"leaveOrganization": {
"message": "Poistu organisaatiosta"
@@ -3464,7 +3467,7 @@
"message": "Pyyntö lähetetty"
},
"loginRequestApprovedForEmailOnDevice": {
- "message": "Login request approved for $EMAIL$ on $DEVICE$",
+ "message": "Kirjautuminen hyväksytty tunnuksella $EMAIL$ laitteella $DEVICE$",
"placeholders": {
"email": {
"content": "$1",
@@ -3477,13 +3480,13 @@
}
},
"youDeniedLoginAttemptFromAnotherDevice": {
- "message": "You denied a login attempt from another device. If this was you, try to log in with the device again."
+ "message": "Kirjautumispyyntö on estetty toiselta laitteelta. Jos se olit sinä, yritä kirjautua uudelleen samalla laitteella."
},
"device": {
- "message": "Device"
+ "message": "Laite"
},
"loginStatus": {
- "message": "Login status"
+ "message": "Kirjautumisen tila"
},
"masterPasswordChanged": {
"message": "Pääsalasana tallennettiin"
@@ -3582,53 +3585,53 @@
"message": "Muista tämä laite tehdäksesi tulevista kirjautumisista saumattomia"
},
"manageDevices": {
- "message": "Manage devices"
+ "message": "Hallinnoi laitteita"
},
"currentSession": {
- "message": "Current session"
+ "message": "Nykyinen istunto"
},
"mobile": {
- "message": "Mobile",
+ "message": "Mobiili",
"description": "Mobile app"
},
"extension": {
- "message": "Extension",
+ "message": "Laajennus",
"description": "Browser extension/addon"
},
"desktop": {
- "message": "Desktop",
+ "message": "Työpöytä",
"description": "Desktop app"
},
"webVault": {
- "message": "Web vault"
+ "message": "Verkkoholvi"
},
"webApp": {
- "message": "Web app"
+ "message": "Verkkosovellus"
},
"cli": {
- "message": "CLI"
+ "message": "Komentorivi"
},
"sdk": {
"message": "SDK",
"description": "Software Development Kit"
},
"requestPending": {
- "message": "Request pending"
+ "message": "Pyyntö odottaa"
},
"firstLogin": {
- "message": "First login"
+ "message": "Ensimmäinen kirjautuminen"
},
"trusted": {
- "message": "Trusted"
+ "message": "Luotettu"
},
"needsApproval": {
- "message": "Needs approval"
+ "message": "Vaatii hyväksynnän"
},
"devices": {
- "message": "Devices"
+ "message": "Laitteet"
},
"accessAttemptBy": {
- "message": "Access attempt by $EMAIL$",
+ "message": "Kirjautumisyritys sähköpostilla $EMAIL$ ",
"placeholders": {
"email": {
"content": "$1",
@@ -3637,28 +3640,28 @@
}
},
"confirmAccess": {
- "message": "Confirm access"
+ "message": "Hyväksy pääsy"
},
"denyAccess": {
- "message": "Deny access"
+ "message": "Estä pääsy"
},
"time": {
- "message": "Time"
+ "message": "Aika"
},
"deviceType": {
- "message": "Device Type"
+ "message": "Laitteen tyyppi"
},
"loginRequest": {
- "message": "Login request"
+ "message": "Kirjautumispyyntö"
},
"thisRequestIsNoLongerValid": {
- "message": "This request is no longer valid."
+ "message": "Tämä pyyntö ei ole enää voimassa."
},
"areYouTryingToAccessYourAccount": {
- "message": "Are you trying to access your account?"
+ "message": "Yritätkö kirjautua tilillesi?"
},
"logInConfirmedForEmailOnDevice": {
- "message": "Login confirmed for $EMAIL$ on $DEVICE$",
+ "message": "Kirjautuminen vahvistettu tunnuksella $EMAIL$ laitteella $DEVICE$",
"placeholders": {
"email": {
"content": "$1",
@@ -3671,16 +3674,16 @@
}
},
"youDeniedALogInAttemptFromAnotherDevice": {
- "message": "You denied a login attempt from another device. If this really was you, try to log in with the device again."
+ "message": "Estit toisen laitteen lähettämän kirjautumispyynnön. Jos kuitenkin tunnistit kirjautumisyrityksen, suorita kirjautuminen uudelleen."
},
"loginRequestHasAlreadyExpired": {
- "message": "Login request has already expired."
+ "message": "Kirjautumispyyntö on jo vanhentunut."
},
"justNow": {
- "message": "Just now"
+ "message": "juuri nyt"
},
"requestedXMinutesAgo": {
- "message": "Requested $MINUTES$ minutes ago",
+ "message": "pyydetty $MINUTES$ minuuttia sitten",
"placeholders": {
"minutes": {
"content": "$1",
@@ -3710,10 +3713,10 @@
"message": "Pyydä hyväksyntää ylläpidolta"
},
"unableToCompleteLogin": {
- "message": "Unable to complete login"
+ "message": "Kirjautuminen epäonnistui"
},
"loginOnTrustedDeviceOrAskAdminToAssignPassword": {
- "message": "You need to log in on a trusted device or ask your administrator to assign you a password."
+ "message": "Sinun on kirjauduttava luotettuun laitteeseen tai pyydettävä järjestelmänvalvojaasi antamaan sinulle salasana."
},
"ssoIdentifierRequired": {
"message": "Organisaation kertakirjautumistunniste tarvitaan."
@@ -3789,23 +3792,23 @@
"message": "Organisaatio ei ole luotettu"
},
"emergencyAccessTrustWarning": {
- "message": "For the security of your account, only confirm if you have granted emergency access to this user and their fingerprint matches what is displayed in their account"
+ "message": "Tilisi turvallisuuden varmistamiseksi, vahvista vain, jos olet antanut hätäpääsyn tälle käyttäjälle ja hänen sormenjälkensä vastaa sitä, mitä hänen tilillään näkyy"
},
"orgTrustWarning": {
- "message": "For the security of your account, only proceed if you are a member of this organization, have account recovery enabled, and the fingerprint displayed below matches the organization's fingerprint."
+ "message": "Tilisi turvallisuuden takaamiseksi jatka vain, jos olet tämän organisaation jäsen, tilin palautus on käytössä ja alla näkyvä sormenjälki vastaa organisaatiosi sormenjälkeä."
},
"orgTrustWarning1": {
- "message": "This organization has an Enterprise policy that will enroll you in account recovery. Enrollment will allow organization administrators to change your password. Only proceed if you recognize this organization and the fingerprint phrase displayed below matches the organization's fingerprint."
+ "message": "Tällä organisaatiolla on yrityskäytäntö, joka tulee ilmi, kun yrität palauttaa tiliäsi. Ilmoittautuminen sallii organisaation ylläpitäjien vaihtaa salasanasi. Jatka vain, jos tunnistat tämän organisaation ja alla näkyvän sormenjäljen, joka vastaa organisaation sormenjälkeä."
},
"trustUser": {
"message": "Luota käyttäjään"
},
"sendsTitleNoItems": {
- "message": "Send sensitive information safely",
+ "message": "Lähetä arkaluonteisia tietoja turvallisesti",
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
},
"sendsBodyNoItems": {
- "message": "Share files and data securely with anyone, on any platform. Your information will remain end-to-end encrypted while limiting exposure.",
+ "message": "Jaa tiedostoja ja dataa turvallisesti kenen tahansa kanssa millä tahansa alustalla. Tiedot pysyvät päästä päähän salattuina.",
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
},
"inputRequired": {
@@ -4395,23 +4398,23 @@
"description": "Label indicating the most common import formats"
},
"uriMatchDefaultStrategyHint": {
- "message": "URI match detection is how Bitwarden identifies autofill suggestions.",
+ "message": "URI:n Säännöllinen lauseke -asetus on se tapa, jolla Bitwarden tekee automaattisen täytön.",
"description": "Explains to the user that URI match detection determines how Bitwarden suggests autofill options, and clarifies that this default strategy applies when no specific match detection is set for a login item."
},
"regExAdvancedOptionWarning": {
- "message": "\"Regular expression\" is an advanced option with increased risk of exposing credentials.",
+ "message": "Säänöllinen lauseke -asetus on kehittynyt asetus, joka lisää kirjautumistietoja kaappausriskiä.",
"description": "Content for dialog which warns a user when selecting 'regular expression' matching strategy as a cipher match strategy"
},
"startsWithAdvancedOptionWarning": {
- "message": "\"Starts with\" is an advanced option with increased risk of exposing credentials.",
+ "message": "Alkaa sanoilla -asetus on kehittynyt asetus, joka lisää kirjautumistietoja kaappausriskiä.",
"description": "Content for dialog which warns a user when selecting 'starts with' matching strategy as a cipher match strategy"
},
"uriMatchWarningDialogLink": {
- "message": "More about match detection",
+ "message": "Lisätietoa vastaavuustunnistuksesta",
"description": "Link to match detection docs on warning dialog for advance match strategy"
},
"uriAdvancedOption": {
- "message": "Advanced options",
+ "message": "Lisäasetukset",
"description": "Advanced option placeholder for uri option component"
},
"confirmContinueToBrowserSettingsTitle": {
@@ -4598,7 +4601,7 @@
}
},
"copyFieldCipherName": {
- "message": "Copy $FIELD$, $CIPHERNAME$",
+ "message": "Kopioi $FIELD$, $CIPHERNAME$",
"description": "Title for a button that copies a field value to the clipboard.",
"placeholders": {
"field": {
@@ -4754,16 +4757,16 @@
"message": "Hanki mobiilisovellus"
},
"getTheMobileAppDesc": {
- "message": "Access your passwords on the go with the Bitwarden mobile app."
+ "message": "Pääse käsiksi salasanoihisi, jos et pääse tietokoneen ääreen Bitwarden-mobiilisovelluksella."
},
"getTheDesktopApp": {
"message": "Hanki työpöytäsovellus"
},
"getTheDesktopAppDesc": {
- "message": "Access your vault without a browser, then set up unlock with biometrics to expedite unlocking in both the desktop app and browser extension."
+ "message": "Käytä holviasi ilman selainta ja aseta sitten lukitus biometriikan avulla nopeuttaaksesi lukituksen avaamista sekä työpöytäsovelluksessa että selaimessa."
},
"downloadFromBitwardenNow": {
- "message": "Download from bitwarden.com now"
+ "message": "Lataa bitwarden.comista nyt"
},
"getItOnGooglePlay": {
"message": "Hanki se Google Playstä"
@@ -5233,16 +5236,16 @@
"message": "Biometrinen avaus ei ole tällä hetkellä käytettävissä tuntemattomasta syystä."
},
"unlockVault": {
- "message": "Unlock your vault in seconds"
+ "message": "Avaa holvisi lukitus sekunneissa"
},
"unlockVaultDesc": {
- "message": "You can customize your unlock and timeout settings to more quickly access your vault."
+ "message": "Voit muokata avaus- ja aikakatkaisuasetuksiasi päästäksesi holvisi nopeammin käsiksi."
},
"unlockPinSet": {
- "message": "Unlock PIN set"
+ "message": "PIN asetettu"
},
"unlockWithBiometricSet": {
- "message": "Unlock with biometrics set"
+ "message": "Biometrinen kirjautuminen otettu käyttöön"
},
"authenticating": {
"message": "Todennetaan"
@@ -5464,7 +5467,7 @@
"message": "Holvin asetukset"
},
"emptyVaultDescription": {
- "message": "The vault protects more than just your passwords. Store secure logins, IDs, cards and notes securely here."
+ "message": "Holvi suojaa muutakin kuin salasanojasi. Säilytä kirjautumsitietojen lisäksi kortteja, muistiinpanoja ja henkilötietoja turvallisesti."
},
"introCarouselLabel": {
"message": "Tervetuloa Bitwardeniin"
@@ -5500,7 +5503,7 @@
"message": "Tuo olemassa olevat salasanat"
},
"emptyVaultNudgeBody": {
- "message": "Use the importer to quickly transfer logins to Bitwarden without manually adding them."
+ "message": "Käytä tuojaa siirtääksesi kirjautumisia nopeasti Bitwardeniin lisäämättä niitä manuaalisesti."
},
"emptyVaultNudgeButton": {
"message": "Tuo nyt"
@@ -5509,19 +5512,19 @@
"message": "Tervetuloa holviisi!"
},
"hasItemsVaultNudgeBodyOne": {
- "message": "Autofill items for the current page"
+ "message": "Täytä nykyisen sivun kohteet automaattisesti"
},
"hasItemsVaultNudgeBodyTwo": {
- "message": "Favorite items for easy access"
+ "message": "Suosikkikohteita helppoon käyttöön"
},
"hasItemsVaultNudgeBodyThree": {
- "message": "Search your vault for something else"
+ "message": "Etsi holvistasi jotain muuta"
},
"newLoginNudgeTitle": {
"message": "Säästä aikaa automaattitäytöllä"
},
"newLoginNudgeBodyOne": {
- "message": "Include a",
+ "message": "Sisällytä a",
"description": "This is in multiple parts to allow for bold text in the middle of the sentence.",
"example": "Include a Website so this login appears as an autofill suggestion."
},
@@ -5531,63 +5534,63 @@
"example": "Include a Website so this login appears as an autofill suggestion."
},
"newLoginNudgeBodyTwo": {
- "message": "so this login appears as an autofill suggestion.",
+ "message": ", joten tämä kirjautuminen näkyy automaattisen täytön ehdotuksena.",
"description": "This is in multiple parts to allow for bold text in the middle of the sentence.",
"example": "Include a Website so this login appears as an autofill suggestion."
},
"newCardNudgeTitle": {
- "message": "Seamless online checkout"
+ "message": "Saumaton verkkomaksaminen"
},
"newCardNudgeBody": {
- "message": "With cards, easily autofill payment forms securely and accurately."
+ "message": "Korteilla voit helposti täyttää automaattisesti maksulomakkeet turvallisesti ja oikein."
},
"newIdentityNudgeTitle": {
- "message": "Simplify creating accounts"
+ "message": "Yksinkertaista tilien luomista"
},
"newIdentityNudgeBody": {
- "message": "With identities, quickly autofill long registration or contact forms."
+ "message": "Identiteettien avulla täytä pitkät rekisteröinti- tai yhteydenottolomakkeet nopeasti automaattisesti."
},
"newNoteNudgeTitle": {
- "message": "Keep your sensitive data safe"
+ "message": "Pidä arkaluonteiset tietosi turvassa"
},
"newNoteNudgeBody": {
- "message": "With notes, securely store sensitive data like banking or insurance details."
+ "message": "Muistiinpanojen avulla tallennetaan turvallisesti arkaluonteiset tiedot, kuten pankkitiedot."
},
"newSshNudgeTitle": {
- "message": "Developer-friendly SSH access"
+ "message": "Kehittäjäystävällinen SSH-käyttö"
},
"newSshNudgeBodyOne": {
- "message": "Store your keys and connect with the SSH agent for fast, encrypted authentication.",
+ "message": "Säilytä avaimet ja yhdistä SSH-agenttiin, niin saat nopean ja salatun todennuksen.",
"description": "Two part message",
"example": "Store your keys and connect with the SSH agent for fast, encrypted authentication. Learn more about SSH agent"
},
"newSshNudgeBodyTwo": {
- "message": "Learn more about SSH agent",
+ "message": "Lue lisää SSH-agentista",
"description": "Two part message",
"example": "Store your keys and connect with the SSH agent for fast, encrypted authentication. Learn more about SSH agent"
},
"generatorNudgeTitle": {
- "message": "Quickly create passwords"
+ "message": "Luo salasanat nopeasti"
},
"generatorNudgeBodyOne": {
- "message": "Easily create strong and unique passwords by clicking on",
+ "message": "Luo helposti vahvoja ja uniikkeja salasanoja klikkaamalla",
"description": "Two part message",
"example": "Easily create strong and unique passwords by clicking on {icon} to help you keep your logins secure."
},
"generatorNudgeBodyTwo": {
- "message": "to help you keep your logins secure.",
+ "message": "auttaakssi sinua pitämään kirjautumisesi turvassa.",
"description": "Two part message",
"example": "Easily create strong and unique passwords by clicking on {icon} to help you keep your logins secure."
},
"generatorNudgeBodyAria": {
- "message": "Easily create strong and unique passwords by clicking on the Generate password button to help you keep your logins secure.",
+ "message": "Luo helposti vahvoja ja uniikkeja salasanoja klikkaamalla Luo salasana -painiketta. Sen avuilla voit pitää kirjautumisesi turvallisina.",
"description": "Aria label for the body content of the generator nudge"
},
"noPermissionsViewPage": {
- "message": "You do not have permissions to view this page. Try logging in with a different account."
+ "message": "Sinulla ei ole oikeuksia tähän sivuun. Yritä kirjautua sisään toisella tilillä."
},
"wasmNotSupported": {
- "message": "WebAssembly is not supported on your browser or is not enabled. WebAssembly is required to use the Bitwarden app.",
+ "message": "WebAssembly ei ole tuettu selaimessasi tai se ei ole käytössä. WebAssembly vaaditaan, jotta voi käyttää Bitwardenia.",
"description": "'WebAssembly' is a technical term and should not be translated."
}
}
diff --git a/apps/browser/src/_locales/fil/messages.json b/apps/browser/src/_locales/fil/messages.json
index 88610d6874c..dfc8d65cfd9 100644
--- a/apps/browser/src/_locales/fil/messages.json
+++ b/apps/browser/src/_locales/fil/messages.json
@@ -547,6 +547,9 @@
"searchVault": {
"message": "Hanapin ang vault"
},
+ "resetSearch": {
+ "message": "Reset search"
+ },
"edit": {
"message": "I-edit"
},
diff --git a/apps/browser/src/_locales/fr/messages.json b/apps/browser/src/_locales/fr/messages.json
index 680a19f33cc..c35f4ca4bb9 100644
--- a/apps/browser/src/_locales/fr/messages.json
+++ b/apps/browser/src/_locales/fr/messages.json
@@ -547,6 +547,9 @@
"searchVault": {
"message": "Rechercher dans le coffre"
},
+ "resetSearch": {
+ "message": "Réinitialiser la recherche"
+ },
"edit": {
"message": "Modifier"
},
@@ -1063,7 +1066,7 @@
"message": "Enregistrer"
},
"notificationViewAria": {
- "message": "View $ITEMNAME$, opens in new window",
+ "message": "Afficher $ITEMNAME$, s'ouvre dans une nouvelle fenêtre",
"placeholders": {
"itemName": {
"content": "$1"
@@ -1076,14 +1079,14 @@
"description": "Aria label for the new item button in notification bar confirmation message when error is prompted"
},
"notificationEditTooltip": {
- "message": "Edit before saving",
+ "message": "Modifier avant d'enregistrer",
"description": "Tooltip and Aria label for edit button on cipher item"
},
"newNotification": {
"message": "Nouvelle notification"
},
"labelWithNotification": {
- "message": "$LABEL$: New notification",
+ "message": "$LABEL$: Nouvelle notification",
"description": "Label for the notification with a new login suggestion.",
"placeholders": {
"label": {
@@ -1101,7 +1104,7 @@
"description": "Shown to user after item is updated."
},
"selectItemAriaLabel": {
- "message": "Select $ITEMTYPE$, $ITEMNAME$",
+ "message": "Sélectionner $ITEMTYPE$, $ITEMNAME$",
"description": "Used by screen readers. $1 is the item type (like vault or folder), $2 is the selected item name.",
"placeholders": {
"itemType": {
@@ -1141,7 +1144,7 @@
"description": "Message displayed when login details are successfully updated."
},
"loginUpdateTaskSuccess": {
- "message": "Great job! You took the steps to make you and $ORGANIZATION$ more secure.",
+ "message": "Bon travail ! Vous avez pris les mesures pour rendre vous et $ORGANIZATION$ plus sécurisés.",
"placeholders": {
"organization": {
"content": "$1"
@@ -1150,7 +1153,7 @@
"description": "Shown to user after login is updated."
},
"loginUpdateTaskSuccessAdditional": {
- "message": "Thank you for making $ORGANIZATION$ more secure. You have $TASK_COUNT$ more passwords to update.",
+ "message": "Merci d'avoir rendu $ORGANIZATION$ plus sécurisé. Il vous reste $TASK_COUNT$ mots de passe à mettre à jour.",
"placeholders": {
"organization": {
"content": "$1"
@@ -1174,10 +1177,10 @@
"description": "Detailed error message shown when saving login details fails."
},
"changePasswordWarning": {
- "message": "After changing your password, you will need to log in with your new password. Active sessions on other devices will be logged out within one hour."
+ "message": "Après avoir changé votre mot de passe, vous devrez vous connecter avec votre nouveau mot de passe. Les sessions actives sur d'autres appareils seront déconnectées dans l'heure qui suit."
},
"accountRecoveryUpdateMasterPasswordSubtitle": {
- "message": "Change your master password to complete account recovery."
+ "message": "Changez votre mot de passe principal pour finaliser la récupération de compte."
},
"enableChangedPasswordNotification": {
"message": "Demander de mettre à jour un identifiant existant"
@@ -1372,7 +1375,7 @@
"message": "Fonctionnalité indisponible"
},
"legacyEncryptionUnsupported": {
- "message": "Legacy encryption is no longer supported. Please contact support to recover your account."
+ "message": "Le chiffrement hérité n'est plus pris en charge. Veuillez contacter le support pour récupérer votre compte."
},
"premiumMembership": {
"message": "Adhésion Premium"
@@ -1606,10 +1609,10 @@
"message": "Suggestions de saisie automatique"
},
"autofillSpotlightTitle": {
- "message": "Easily find autofill suggestions"
+ "message": "Trouver facilement des suggestions de remplissage automatique"
},
"autofillSpotlightDesc": {
- "message": "Turn off your browser's autofill settings, so they don't conflict with Bitwarden."
+ "message": "Désactivez les paramètres de remplissage automatique de votre navigateur pour qu'ils n'entrent pas en conflit avec Bitwarden."
},
"turnOffBrowserAutofill": {
"message": "Désactiver le remplissage automatique de $BROWSER$",
@@ -1830,7 +1833,7 @@
"message": "Code de sécurité"
},
"cardNumber": {
- "message": "card number"
+ "message": "numéro de carte"
},
"ex": {
"message": "ex."
@@ -2166,7 +2169,7 @@
"message": "Définissez votre code PIN pour déverrouiller Bitwarden. Les paramètres relatifs à votre code PIN seront réinitialisés si vous vous déconnectez complètement de l'application."
},
"setPinCode": {
- "message": "You can use this PIN to unlock Bitwarden. Your PIN will be reset if you ever fully log out of the application."
+ "message": "Vous pouvez utiliser ce code PIN pour déverrouiller Bitwarden. Votre code PIN sera réinitialisé si vous vous déconnectez complètement de l'application."
},
"pinRequired": {
"message": "Le code PIN est requis."
@@ -2217,7 +2220,7 @@
"message": "Utiliser ce mot de passe"
},
"useThisPassphrase": {
- "message": "Use this passphrase"
+ "message": "Utilisez cette phrase secrète"
},
"useThisUsername": {
"message": "Utiliser ce nom d'utilisateur"
@@ -2497,10 +2500,10 @@
"message": "Une politique d'organisation a bloqué l'import d'éléments dans votre coffre personel."
},
"restrictCardTypeImport": {
- "message": "Cannot import card item types"
+ "message": "Impossible d'importer des types d'éléments de carte"
},
"restrictCardTypeImportDesc": {
- "message": "A policy set by 1 or more organizations prevents you from importing cards to your vaults."
+ "message": "Une politique définie par 1 ou plusieurs organisations vous empêche d'importer des cartes dans vos coffres."
},
"domainsTitle": {
"message": "Domaines",
@@ -2584,7 +2587,7 @@
}
},
"atRiskChangePrompt": {
- "message": "Your password for this site is at-risk. $ORGANIZATION$ has requested that you change it.",
+ "message": "Votre mot de passe pour ce site est à risque. $ORGANIZATION$ vous a demandé de le modifier.",
"placeholders": {
"organization": {
"content": "$1",
@@ -2594,7 +2597,7 @@
"description": "Notification body when a login triggers an at-risk password change request and the change password domain is known."
},
"atRiskNavigatePrompt": {
- "message": "$ORGANIZATION$ wants you to change this password because it is at-risk. Navigate to your account settings to change the password.",
+ "message": "$ORGANIZATION$ souhaite que vous changiez ce mot de passe car il est à risque. Accédez aux paramètres de votre compte pour modifier le mot de passe.",
"placeholders": {
"organization": {
"content": "$1",
@@ -2632,14 +2635,14 @@
"description": "Description of the review at-risk login slide on the at-risk password page carousel"
},
"reviewAtRiskLoginSlideImgAltPeriod": {
- "message": "Illustration of a list of logins that are at-risk."
+ "message": "Illustration d'une liste de connexions à risque."
},
"generatePasswordSlideDesc": {
"message": "Générez rapidement un mot de passe fort et unique grâce au menu de saisie automatique de Bitwarden sur le site à risque.",
"description": "Description of the generate password slide on the at-risk password page carousel"
},
"generatePasswordSlideImgAltPeriod": {
- "message": "Illustration of the Bitwarden autofill menu displaying a generated password."
+ "message": "Illustration du menu de remplissage automatique de Bitwarden affichant un mot de passe généré."
},
"updateInBitwarden": {
"message": "Mettre à jour dans Bitwarden"
@@ -2649,7 +2652,7 @@
"description": "Description of the update in Bitwarden slide on the at-risk password page carousel"
},
"updateInBitwardenSlideImgAltPeriod": {
- "message": "Illustration of a Bitwarden’s notification prompting the user to update the login."
+ "message": "Illustration d'une notification de Bitwarden invitant l'utilisateur à mettre à jour la connexion."
},
"turnOnAutofill": {
"message": "Activer la saisie automatique"
@@ -2723,7 +2726,7 @@
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
},
"maxAccessCountReached": {
- "message": "Max access count reached",
+ "message": "Nombre maximal d'accès atteint",
"description": "This text will be displayed after a Send has been accessed the maximum amount of times."
},
"hideTextByDefault": {
@@ -2929,7 +2932,7 @@
"message": "Vous devez vérifier votre courriel pour utiliser cette fonctionnalité. Vous pouvez vérifier votre courriel dans le coffre web."
},
"masterPasswordSuccessfullySet": {
- "message": "Master password successfully set"
+ "message": "Mot de passe principal défini avec succès"
},
"updatedMasterPassword": {
"message": "Mot de passe principal mis à jour"
@@ -3070,13 +3073,13 @@
"message": "Aucun identifiant unique trouvé."
},
"removeMasterPasswordForOrganizationUserKeyConnector": {
- "message": "A master password is no longer required for members of the following organization. Please confirm the domain below with your organization administrator."
+ "message": "Un mot de passe principal n’est plus requis pour les membres de l’organisation suivante. Veuillez confirmer le domaine ci-dessous auprès de l'administrateur de votre organisation."
},
"organizationName": {
"message": "Nom de l'organisation"
},
"keyConnectorDomain": {
- "message": "Key Connector domain"
+ "message": "Domaine du connecteur clé"
},
"leaveOrganization": {
"message": "Quitter l'organisation"
@@ -3112,7 +3115,7 @@
}
},
"exportingIndividualVaultWithAttachmentsDescription": {
- "message": "Only the individual vault items including attachments associated with $EMAIL$ will be exported. Organization vault items will not be included",
+ "message": "Seuls les éléments individuels du coffre-fort, y compris les pièces jointes associées à $EMAIL$, seront exportés. Les éléments du coffre-fort de l'organisation ne seront pas inclus",
"placeholders": {
"email": {
"content": "$1",
@@ -3464,7 +3467,7 @@
"message": "Demande envoyée"
},
"loginRequestApprovedForEmailOnDevice": {
- "message": "Login request approved for $EMAIL$ on $DEVICE$",
+ "message": "Demande de connexion approuvée pour $EMAIL$ sur $DEVICE$",
"placeholders": {
"email": {
"content": "$1",
@@ -3477,16 +3480,16 @@
}
},
"youDeniedLoginAttemptFromAnotherDevice": {
- "message": "You denied a login attempt from another device. If this was you, try to log in with the device again."
+ "message": "Vous avez refusé une tentative de connexion depuis un autre appareil. Si c'était vous, essayez de vous reconnecter avec l'appareil."
},
"device": {
- "message": "Device"
+ "message": "Appareil"
},
"loginStatus": {
- "message": "Login status"
+ "message": "Statut de connexion"
},
"masterPasswordChanged": {
- "message": "Master password saved"
+ "message": "Mot de passe principal enregistré"
},
"exposedMasterPassword": {
"message": "Mot de passe principal exposé"
@@ -3582,10 +3585,10 @@
"message": "Mémorisez cet appareil pour faciliter les futures connexions"
},
"manageDevices": {
- "message": "Manage devices"
+ "message": "Gérer les appareils"
},
"currentSession": {
- "message": "Current session"
+ "message": "Session en cours"
},
"mobile": {
"message": "Mobile",
@@ -3596,14 +3599,14 @@
"description": "Browser extension/addon"
},
"desktop": {
- "message": "Desktop",
+ "message": "Bureau",
"description": "Desktop app"
},
"webVault": {
- "message": "Web vault"
+ "message": "Coffre-fort Web"
},
"webApp": {
- "message": "Web app"
+ "message": "Application web"
},
"cli": {
"message": "CLI"
@@ -3613,22 +3616,22 @@
"description": "Software Development Kit"
},
"requestPending": {
- "message": "Request pending"
+ "message": "Demande en attente"
},
"firstLogin": {
- "message": "First login"
+ "message": "Première connexion"
},
"trusted": {
- "message": "Trusted"
+ "message": "Approuvé"
},
"needsApproval": {
- "message": "Needs approval"
+ "message": "Requiert une approbation"
},
"devices": {
- "message": "Devices"
+ "message": "Appareils"
},
"accessAttemptBy": {
- "message": "Access attempt by $EMAIL$",
+ "message": "Tentative d'accès par $EMAIL$",
"placeholders": {
"email": {
"content": "$1",
@@ -3637,28 +3640,28 @@
}
},
"confirmAccess": {
- "message": "Confirm access"
+ "message": "Confirmer l'accès"
},
"denyAccess": {
- "message": "Deny access"
+ "message": "Refuser l'accès"
},
"time": {
- "message": "Time"
+ "message": "Temps"
},
"deviceType": {
- "message": "Device Type"
+ "message": "Type d'appareil"
},
"loginRequest": {
- "message": "Login request"
+ "message": "Demande de connexion"
},
"thisRequestIsNoLongerValid": {
- "message": "This request is no longer valid."
+ "message": "Cette demande n'est plus valide."
},
"areYouTryingToAccessYourAccount": {
- "message": "Are you trying to access your account?"
+ "message": "Essayez-vous d'accéder à votre compte ?"
},
"logInConfirmedForEmailOnDevice": {
- "message": "Login confirmed for $EMAIL$ on $DEVICE$",
+ "message": "Connexion confirmée pour $EMAIL$ sur $DEVICE$",
"placeholders": {
"email": {
"content": "$1",
@@ -3671,16 +3674,16 @@
}
},
"youDeniedALogInAttemptFromAnotherDevice": {
- "message": "You denied a login attempt from another device. If this really was you, try to log in with the device again."
+ "message": "Vous avez refusé une tentative de connexion depuis un autre appareil. Si c'était vraiment vous, essayez de vous reconnecter avec l'appareil."
},
"loginRequestHasAlreadyExpired": {
- "message": "Login request has already expired."
+ "message": "La demande de connexion a déjà expiré."
},
"justNow": {
- "message": "Just now"
+ "message": "À l’instant"
},
"requestedXMinutesAgo": {
- "message": "Requested $MINUTES$ minutes ago",
+ "message": "Demandé $MINUTES$ il y a quelques minutes",
"placeholders": {
"minutes": {
"content": "$1",
@@ -3710,10 +3713,10 @@
"message": "Demander l'approbation de l'administrateur"
},
"unableToCompleteLogin": {
- "message": "Unable to complete login"
+ "message": "Impossible de terminer la connexion"
},
"loginOnTrustedDeviceOrAskAdminToAssignPassword": {
- "message": "You need to log in on a trusted device or ask your administrator to assign you a password."
+ "message": "Vous devez vous connecter sur un appareil de confiance ou demander à votre administrateur de vous attribuer un mot de passe."
},
"ssoIdentifierRequired": {
"message": "Identifiant SSO de l'organisation requis."
@@ -3786,26 +3789,26 @@
"message": "Ne pas faire confiance"
},
"organizationNotTrusted": {
- "message": "Organization is not trusted"
+ "message": "L'organisation n'est pas fiable"
},
"emergencyAccessTrustWarning": {
- "message": "For the security of your account, only confirm if you have granted emergency access to this user and their fingerprint matches what is displayed in their account"
+ "message": "Pour la sécurité de votre compte, ne confirmez que si vous avez accordé un accès d'urgence à cet utilisateur et que son empreinte digitale correspond à ce qui est affiché dans son compte"
},
"orgTrustWarning": {
- "message": "For the security of your account, only proceed if you are a member of this organization, have account recovery enabled, and the fingerprint displayed below matches the organization's fingerprint."
+ "message": "Pour la sécurité de votre compte, ne procédez que si vous êtes membre de cette organisation, que la récupération de compte est activée et que l'empreinte digitale affichée ci-dessous correspond à l'empreinte digitale de l'organisation."
},
"orgTrustWarning1": {
- "message": "This organization has an Enterprise policy that will enroll you in account recovery. Enrollment will allow organization administrators to change your password. Only proceed if you recognize this organization and the fingerprint phrase displayed below matches the organization's fingerprint."
+ "message": "Cette organisation dispose d’une politique d’entreprise qui vous inscrira au recouvrement de compte. L'inscription permettra aux administrateurs de l'organisation de modifier votre mot de passe. Ne continuez que si vous reconnaissez cette organisation et que la phrase d'empreinte digitale affichée ci-dessous correspond à l'empreinte digitale de l'organisation."
},
"trustUser": {
"message": "Faire confiance à l'utilisateur"
},
"sendsTitleNoItems": {
- "message": "Send sensitive information safely",
+ "message": "Envoyez des informations sensibles en toute sécurité",
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
},
"sendsBodyNoItems": {
- "message": "Share files and data securely with anyone, on any platform. Your information will remain end-to-end encrypted while limiting exposure.",
+ "message": "Partagez des fichiers et des données en toute sécurité avec n'importe qui, sur n'importe quelle plateforme. Vos informations resteront cryptées de bout en bout tout en limitant l'exposition.",
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
},
"inputRequired": {
@@ -4395,19 +4398,19 @@
"description": "Label indicating the most common import formats"
},
"uriMatchDefaultStrategyHint": {
- "message": "URI match detection is how Bitwarden identifies autofill suggestions.",
+ "message": "La détection de correspondance d'URI est la manière dont Bitwarden identifie les suggestions de remplissage automatique.",
"description": "Explains to the user that URI match detection determines how Bitwarden suggests autofill options, and clarifies that this default strategy applies when no specific match detection is set for a login item."
},
"regExAdvancedOptionWarning": {
- "message": "\"Regular expression\" is an advanced option with increased risk of exposing credentials.",
+ "message": "\"Expression régulière\" est une option avancée présentant un risque accru d’exposition des informations d’identification.",
"description": "Content for dialog which warns a user when selecting 'regular expression' matching strategy as a cipher match strategy"
},
"startsWithAdvancedOptionWarning": {
- "message": "\"Starts with\" is an advanced option with increased risk of exposing credentials.",
+ "message": "\"Commence par\" est une option avancée présentant un risque accru d’exposition des informations d’identification.",
"description": "Content for dialog which warns a user when selecting 'starts with' matching strategy as a cipher match strategy"
},
"uriMatchWarningDialogLink": {
- "message": "More about match detection",
+ "message": "En savoir plus sur la détection des correspondances",
"description": "Link to match detection docs on warning dialog for advance match strategy"
},
"uriAdvancedOption": {
@@ -4560,7 +4563,7 @@
}
},
"viewItemTitleWithField": {
- "message": "View item - $ITEMNAME$ - $FIELD$",
+ "message": "Afficher l'élément - $ITEMNAME$ - $FIELD$",
"description": "Title for a link that opens a view for an item.",
"placeholders": {
"itemname": {
@@ -4584,7 +4587,7 @@
}
},
"autofillTitleWithField": {
- "message": "Autofill - $ITEMNAME$ - $FIELD$",
+ "message": "Remplissage automatique - $ITEMNAME$ - $FIELD$",
"description": "Title for a button that autofills a login item.",
"placeholders": {
"itemname": {
@@ -4598,7 +4601,7 @@
}
},
"copyFieldCipherName": {
- "message": "Copy $FIELD$, $CIPHERNAME$",
+ "message": "Copiez $FIELD$, $CIPHERNAME$",
"description": "Title for a button that copies a field value to the clipboard.",
"placeholders": {
"field": {
@@ -4754,19 +4757,19 @@
"message": "Télécharger l'application mobile"
},
"getTheMobileAppDesc": {
- "message": "Access your passwords on the go with the Bitwarden mobile app."
+ "message": "Accédez à vos mots de passe en déplacement avec l'application mobile Bitwarden."
},
"getTheDesktopApp": {
"message": "Télécharger l'application de bureau"
},
"getTheDesktopAppDesc": {
- "message": "Access your vault without a browser, then set up unlock with biometrics to expedite unlocking in both the desktop app and browser extension."
+ "message": "Accédez à votre coffre-fort sans navigateur, puis configurez le déverrouillage avec la biométrie pour accélérer le déverrouillage à la fois dans l'application de bureau et dans l'extension du navigateur."
},
"downloadFromBitwardenNow": {
- "message": "Download from bitwarden.com now"
+ "message": "Téléchargez maintenant depuis bitwarden.com"
},
"getItOnGooglePlay": {
- "message": "Get it on Google Play"
+ "message": "Obtenez-le sur Google Play"
},
"downloadOnTheAppStore": {
"message": "Télécharger depuis l’App Store"
@@ -5236,13 +5239,13 @@
"message": "Déverouillez votre coffre en quelques secondes"
},
"unlockVaultDesc": {
- "message": "You can customize your unlock and timeout settings to more quickly access your vault."
+ "message": "Vous pouvez personnaliser vos paramètres de déverrouillage et de délai d'attente pour accéder plus rapidement à votre coffre-fort."
},
"unlockPinSet": {
- "message": "Unlock PIN set"
+ "message": "Déverrouiller l'ensemble de codes PIN"
},
"unlockWithBiometricSet": {
- "message": "Unlock with biometrics set"
+ "message": "Déverrouiller avec l'ensemble biométrique"
},
"authenticating": {
"message": "Authentification"
@@ -5256,7 +5259,7 @@
"description": "Notification message for when a password has been regenerated"
},
"saveToBitwarden": {
- "message": "Save to Bitwarden",
+ "message": "Enregistrer dans Bitwarden",
"description": "Confirmation message for saving a login to Bitwarden"
},
"spaceCharacterDescriptor": {
@@ -5464,7 +5467,7 @@
"message": "Options du coffre"
},
"emptyVaultDescription": {
- "message": "The vault protects more than just your passwords. Store secure logins, IDs, cards and notes securely here."
+ "message": "Le coffre-fort protège bien plus que vos mots de passe. Stockez ici en toute sécurité des identifiants, des cartes d'identité, des cartes et des notes sécurisés."
},
"introCarouselLabel": {
"message": "Bienvenue sur Bitwarden"
@@ -5473,34 +5476,34 @@
"message": "Priorité à la sécurité"
},
"securityPrioritizedBody": {
- "message": "Save logins, cards, and identities to your secure vault. Bitwarden uses zero-knowledge, end-to-end encryption to protect what’s important to you."
+ "message": "Enregistrez les identifiants, les cartes et les identités dans votre coffre-fort sécurisé. Bitwarden utilise un cryptage de bout en bout à connaissance nulle pour protéger ce qui est important pour vous."
},
"quickLogin": {
"message": "Connexion rapide et facile"
},
"quickLoginBody": {
- "message": "Set up biometric unlock and autofill to log into your accounts without typing a single letter."
+ "message": "Configurez le déverrouillage biométrique et le remplissage automatique pour vous connecter à vos comptes sans taper une seule lettre."
},
"secureUser": {
- "message": "Level up your logins"
+ "message": "Améliorez vos connexions"
},
"secureUserBody": {
- "message": "Use the generator to create and save strong, unique passwords for all your accounts."
+ "message": "Utilisez le générateur pour créer et enregistrer des mots de passe forts et uniques pour tous vos comptes."
},
"secureDevices": {
- "message": "Your data, when and where you need it"
+ "message": "Vos données, quand et où vous en avez besoin"
},
"secureDevicesBody": {
- "message": "Save unlimited passwords across unlimited devices with Bitwarden mobile, browser, and desktop apps."
+ "message": "Enregistrez des mots de passe illimités sur un nombre illimité d'appareils avec les applications mobiles, de navigateur et de bureau Bitwarden."
},
"nudgeBadgeAria": {
"message": "1 notification"
},
"emptyVaultNudgeTitle": {
- "message": "Import existing passwords"
+ "message": "Importer des mots de passe existants"
},
"emptyVaultNudgeBody": {
- "message": "Use the importer to quickly transfer logins to Bitwarden without manually adding them."
+ "message": "Utilisez l'importateur pour transférer rapidement les connexions vers Bitwarden sans les ajouter manuellement."
},
"emptyVaultNudgeButton": {
"message": "Importer maintenant"
@@ -5509,19 +5512,19 @@
"message": "Bienvenue dans votre coffre !"
},
"hasItemsVaultNudgeBodyOne": {
- "message": "Autofill items for the current page"
+ "message": "Remplissage automatique des éléments de la page actuelle"
},
"hasItemsVaultNudgeBodyTwo": {
- "message": "Favorite items for easy access"
+ "message": "Articles préférés pour un accès facile"
},
"hasItemsVaultNudgeBodyThree": {
- "message": "Search your vault for something else"
+ "message": "Recherchez autre chose dans votre coffre-fort"
},
"newLoginNudgeTitle": {
"message": "Gagnez du temps avec le remplissage automatique"
},
"newLoginNudgeBodyOne": {
- "message": "Include a",
+ "message": "Inclure un",
"description": "This is in multiple parts to allow for bold text in the middle of the sentence.",
"example": "Include a Website so this login appears as an autofill suggestion."
},
@@ -5531,33 +5534,33 @@
"example": "Include a Website so this login appears as an autofill suggestion."
},
"newLoginNudgeBodyTwo": {
- "message": "so this login appears as an autofill suggestion.",
+ "message": "cette connexion apparaît donc comme une suggestion de remplissage automatique.",
"description": "This is in multiple parts to allow for bold text in the middle of the sentence.",
"example": "Include a Website so this login appears as an autofill suggestion."
},
"newCardNudgeTitle": {
- "message": "Seamless online checkout"
+ "message": "Paiement en ligne transparent"
},
"newCardNudgeBody": {
- "message": "With cards, easily autofill payment forms securely and accurately."
+ "message": "Avec les cartes, remplissez facilement et automatiquement les formulaires de paiement de manière sécurisée et précise."
},
"newIdentityNudgeTitle": {
- "message": "Simplify creating accounts"
+ "message": "Simplifiez la création de comptes"
},
"newIdentityNudgeBody": {
- "message": "With identities, quickly autofill long registration or contact forms."
+ "message": "Avec les identités, remplissez rapidement automatiquement les longs formulaires d'inscription ou de contact."
},
"newNoteNudgeTitle": {
- "message": "Keep your sensitive data safe"
+ "message": "Gardez vos données sensibles en sécurité"
},
"newNoteNudgeBody": {
- "message": "With notes, securely store sensitive data like banking or insurance details."
+ "message": "Avec des notes, stockez en toute sécurité des données sensibles telles que des informations bancaires ou d’assurance."
},
"newSshNudgeTitle": {
- "message": "Developer-friendly SSH access"
+ "message": "Accès SSH convivial pour les développeurs"
},
"newSshNudgeBodyOne": {
- "message": "Store your keys and connect with the SSH agent for fast, encrypted authentication.",
+ "message": "Stockez vos clés et connectez-vous à l'agent SSH pour une authentification rapide et cryptée.",
"description": "Two part message",
"example": "Store your keys and connect with the SSH agent for fast, encrypted authentication. Learn more about SSH agent"
},
@@ -5567,27 +5570,27 @@
"example": "Store your keys and connect with the SSH agent for fast, encrypted authentication. Learn more about SSH agent"
},
"generatorNudgeTitle": {
- "message": "Quickly create passwords"
+ "message": "Créez rapidement des mots de passe"
},
"generatorNudgeBodyOne": {
- "message": "Easily create strong and unique passwords by clicking on",
+ "message": "Créez facilement des mots de passe forts et uniques en cliquant sur",
"description": "Two part message",
"example": "Easily create strong and unique passwords by clicking on {icon} to help you keep your logins secure."
},
"generatorNudgeBodyTwo": {
- "message": "to help you keep your logins secure.",
+ "message": "pour vous aider à garder vos connexions sécurisées.",
"description": "Two part message",
"example": "Easily create strong and unique passwords by clicking on {icon} to help you keep your logins secure."
},
"generatorNudgeBodyAria": {
- "message": "Easily create strong and unique passwords by clicking on the Generate password button to help you keep your logins secure.",
+ "message": "Créez facilement des mots de passe forts et uniques en cliquant sur le bouton Générer un mot de passe pour vous aider à sécuriser vos connexions.",
"description": "Aria label for the body content of the generator nudge"
},
"noPermissionsViewPage": {
- "message": "You do not have permissions to view this page. Try logging in with a different account."
+ "message": "Vous n'avez pas les autorisations pour consulter cette page. Essayez de vous connecter avec un autre compte."
},
"wasmNotSupported": {
- "message": "WebAssembly is not supported on your browser or is not enabled. WebAssembly is required to use the Bitwarden app.",
+ "message": "WebAssembly n'est pas pris en charge sur votre navigateur ou n'est pas activé. WebAssembly est requis pour utiliser l'application Bitwarden.",
"description": "'WebAssembly' is a technical term and should not be translated."
}
}
diff --git a/apps/browser/src/_locales/gl/messages.json b/apps/browser/src/_locales/gl/messages.json
index 559d0ca82b3..c7d47b61209 100644
--- a/apps/browser/src/_locales/gl/messages.json
+++ b/apps/browser/src/_locales/gl/messages.json
@@ -547,6 +547,9 @@
"searchVault": {
"message": "Buscar na caixa forte"
},
+ "resetSearch": {
+ "message": "Reset search"
+ },
"edit": {
"message": "Editar"
},
diff --git a/apps/browser/src/_locales/he/messages.json b/apps/browser/src/_locales/he/messages.json
index e4d959785bf..c13bcad10c6 100644
--- a/apps/browser/src/_locales/he/messages.json
+++ b/apps/browser/src/_locales/he/messages.json
@@ -547,6 +547,9 @@
"searchVault": {
"message": "חיפוש בכספת"
},
+ "resetSearch": {
+ "message": "Reset search"
+ },
"edit": {
"message": "ערוך"
},
diff --git a/apps/browser/src/_locales/hi/messages.json b/apps/browser/src/_locales/hi/messages.json
index 4fd2652d786..cc89f9cc2dd 100644
--- a/apps/browser/src/_locales/hi/messages.json
+++ b/apps/browser/src/_locales/hi/messages.json
@@ -547,6 +547,9 @@
"searchVault": {
"message": "वॉल्ट खोजे"
},
+ "resetSearch": {
+ "message": "खोज रीसेट करें"
+ },
"edit": {
"message": "संपादन करें"
},
diff --git a/apps/browser/src/_locales/hr/messages.json b/apps/browser/src/_locales/hr/messages.json
index 07c3e20cd18..97bd79f2950 100644
--- a/apps/browser/src/_locales/hr/messages.json
+++ b/apps/browser/src/_locales/hr/messages.json
@@ -547,6 +547,9 @@
"searchVault": {
"message": "Pretraži trezor"
},
+ "resetSearch": {
+ "message": "Ponovno postavljanje pretraživanja"
+ },
"edit": {
"message": "Uredi"
},
@@ -1830,7 +1833,7 @@
"message": "Sigurnosni kôd"
},
"cardNumber": {
- "message": "card number"
+ "message": "broj kartice"
},
"ex": {
"message": "npr."
@@ -3464,7 +3467,7 @@
"message": "Zahtjev poslan"
},
"loginRequestApprovedForEmailOnDevice": {
- "message": "Login request approved for $EMAIL$ on $DEVICE$",
+ "message": "Prijava za $EMAIL$ potvrđena na uređaju $DEVICE$",
"placeholders": {
"email": {
"content": "$1",
@@ -3477,13 +3480,13 @@
}
},
"youDeniedLoginAttemptFromAnotherDevice": {
- "message": "You denied a login attempt from another device. If this was you, try to log in with the device again."
+ "message": "Odbijena je prijava na drugom uređaju. Ako si ovo stvarno ti, pokušaj se ponovno prijaviti uređajem."
},
"device": {
- "message": "Device"
+ "message": "Uređaj"
},
"loginStatus": {
- "message": "Login status"
+ "message": "Status prijave"
},
"masterPasswordChanged": {
"message": "Glavna lozinka promijenjena"
@@ -3582,17 +3585,17 @@
"message": "Zapamti ovaj uređaj kako bi buduće prijave bile brže"
},
"manageDevices": {
- "message": "Manage devices"
+ "message": "Upravljaj uređajima"
},
"currentSession": {
- "message": "Current session"
+ "message": "Trenutna sesija"
},
"mobile": {
- "message": "Mobile",
+ "message": "Mobitel",
"description": "Mobile app"
},
"extension": {
- "message": "Extension",
+ "message": "Proširenje",
"description": "Browser extension/addon"
},
"desktop": {
@@ -3600,10 +3603,10 @@
"description": "Desktop app"
},
"webVault": {
- "message": "Web vault"
+ "message": "Web trezor"
},
"webApp": {
- "message": "Web app"
+ "message": "Web aplikacija"
},
"cli": {
"message": "CLI"
@@ -3613,22 +3616,22 @@
"description": "Software Development Kit"
},
"requestPending": {
- "message": "Request pending"
+ "message": "Zahtjev u tijeku"
},
"firstLogin": {
- "message": "First login"
+ "message": "Prva prijava"
},
"trusted": {
- "message": "Trusted"
+ "message": "Pouzdan"
},
"needsApproval": {
- "message": "Needs approval"
+ "message": "Zahtijeva odobrenje"
},
"devices": {
- "message": "Devices"
+ "message": "Uređaji"
},
"accessAttemptBy": {
- "message": "Access attempt by $EMAIL$",
+ "message": "$EMAIL$ pokušava pristupiti",
"placeholders": {
"email": {
"content": "$1",
@@ -3637,28 +3640,28 @@
}
},
"confirmAccess": {
- "message": "Confirm access"
+ "message": "Potvrdi pristup"
},
"denyAccess": {
- "message": "Deny access"
+ "message": "Odbij pristup"
},
"time": {
- "message": "Time"
+ "message": "Vrijeme"
},
"deviceType": {
- "message": "Device Type"
+ "message": "Vrsta uređaja"
},
"loginRequest": {
- "message": "Login request"
+ "message": "Zahtjev za prijavu"
},
"thisRequestIsNoLongerValid": {
- "message": "This request is no longer valid."
+ "message": "Ovaj zahtjev više nije valjan."
},
"areYouTryingToAccessYourAccount": {
- "message": "Are you trying to access your account?"
+ "message": "Pokušavaš li pristupiti svom računu?"
},
"logInConfirmedForEmailOnDevice": {
- "message": "Login confirmed for $EMAIL$ on $DEVICE$",
+ "message": "Prijava za $EMAIL$ potvrđena na uređaju $DEVICE$",
"placeholders": {
"email": {
"content": "$1",
@@ -3671,16 +3674,16 @@
}
},
"youDeniedALogInAttemptFromAnotherDevice": {
- "message": "You denied a login attempt from another device. If this really was you, try to log in with the device again."
+ "message": "Odbijena je prijava na drugom uređaju. Ako si ovo stvarno ti, pokušaj se ponovno prijaviti uređajem."
},
"loginRequestHasAlreadyExpired": {
- "message": "Login request has already expired."
+ "message": "Zahtjev za prijavu je već istekao."
},
"justNow": {
- "message": "Just now"
+ "message": "Upravo"
},
"requestedXMinutesAgo": {
- "message": "Requested $MINUTES$ minutes ago",
+ "message": "Zatraženo prije $MINUTES$ minute/a",
"placeholders": {
"minutes": {
"content": "$1",
@@ -4598,7 +4601,7 @@
}
},
"copyFieldCipherName": {
- "message": "Copy $FIELD$, $CIPHERNAME$",
+ "message": "Kopiraj $FIELD$, $CIPHERNAME$",
"description": "Title for a button that copies a field value to the clipboard.",
"placeholders": {
"field": {
diff --git a/apps/browser/src/_locales/hu/messages.json b/apps/browser/src/_locales/hu/messages.json
index b77d613da51..cfe79ea4b20 100644
--- a/apps/browser/src/_locales/hu/messages.json
+++ b/apps/browser/src/_locales/hu/messages.json
@@ -547,6 +547,9 @@
"searchVault": {
"message": "Keresés a széfben"
},
+ "resetSearch": {
+ "message": "Keresés visszaállítása"
+ },
"edit": {
"message": "Szerkesztés"
},
diff --git a/apps/browser/src/_locales/id/messages.json b/apps/browser/src/_locales/id/messages.json
index 07e094e10bd..4ce58ee7618 100644
--- a/apps/browser/src/_locales/id/messages.json
+++ b/apps/browser/src/_locales/id/messages.json
@@ -547,6 +547,9 @@
"searchVault": {
"message": "Cari brankas"
},
+ "resetSearch": {
+ "message": "Reset search"
+ },
"edit": {
"message": "Edit"
},
diff --git a/apps/browser/src/_locales/it/messages.json b/apps/browser/src/_locales/it/messages.json
index 167cc51c0b1..164d46bcec5 100644
--- a/apps/browser/src/_locales/it/messages.json
+++ b/apps/browser/src/_locales/it/messages.json
@@ -547,6 +547,9 @@
"searchVault": {
"message": "Cerca nella cassaforte"
},
+ "resetSearch": {
+ "message": "Reset search"
+ },
"edit": {
"message": "Modifica"
},
diff --git a/apps/browser/src/_locales/ja/messages.json b/apps/browser/src/_locales/ja/messages.json
index 49d22bd065f..977d04ca28a 100644
--- a/apps/browser/src/_locales/ja/messages.json
+++ b/apps/browser/src/_locales/ja/messages.json
@@ -547,6 +547,9 @@
"searchVault": {
"message": "保管庫を検索"
},
+ "resetSearch": {
+ "message": "Reset search"
+ },
"edit": {
"message": "編集"
},
diff --git a/apps/browser/src/_locales/ka/messages.json b/apps/browser/src/_locales/ka/messages.json
index 1e75805638c..da70c255803 100644
--- a/apps/browser/src/_locales/ka/messages.json
+++ b/apps/browser/src/_locales/ka/messages.json
@@ -547,6 +547,9 @@
"searchVault": {
"message": "Search vault"
},
+ "resetSearch": {
+ "message": "Reset search"
+ },
"edit": {
"message": "ჩასწორება"
},
diff --git a/apps/browser/src/_locales/km/messages.json b/apps/browser/src/_locales/km/messages.json
index 9a6d9a4d316..f4c58318bc1 100644
--- a/apps/browser/src/_locales/km/messages.json
+++ b/apps/browser/src/_locales/km/messages.json
@@ -547,6 +547,9 @@
"searchVault": {
"message": "Search vault"
},
+ "resetSearch": {
+ "message": "Reset search"
+ },
"edit": {
"message": "Edit"
},
diff --git a/apps/browser/src/_locales/kn/messages.json b/apps/browser/src/_locales/kn/messages.json
index c7a821de19b..31b285f6780 100644
--- a/apps/browser/src/_locales/kn/messages.json
+++ b/apps/browser/src/_locales/kn/messages.json
@@ -547,6 +547,9 @@
"searchVault": {
"message": "ವಾಲ್ಟ್ ಹುಡುಕಿ"
},
+ "resetSearch": {
+ "message": "Reset search"
+ },
"edit": {
"message": "ಎಡಿಟ್"
},
diff --git a/apps/browser/src/_locales/ko/messages.json b/apps/browser/src/_locales/ko/messages.json
index 730d3eeda61..89d59488aaf 100644
--- a/apps/browser/src/_locales/ko/messages.json
+++ b/apps/browser/src/_locales/ko/messages.json
@@ -3,7 +3,7 @@
"message": "Bitwarden"
},
"appLogoLabel": {
- "message": "Bitwarden logo"
+ "message": "Bitwarden 로고"
},
"extName": {
"message": "Bitwarden 비밀번호 관리자",
@@ -465,10 +465,10 @@
"message": "암호 생성"
},
"passwordGenerated": {
- "message": "Password generated"
+ "message": "비밀번호 생성됨"
},
"passphraseGenerated": {
- "message": "Passphrase generated"
+ "message": "패스프레이즈 생성됨"
},
"usernameGenerated": {
"message": "Username generated"
@@ -547,6 +547,9 @@
"searchVault": {
"message": "보관함 검색"
},
+ "resetSearch": {
+ "message": "Reset search"
+ },
"edit": {
"message": "편집"
},
@@ -1483,17 +1486,17 @@
"message": "Don't ask again on this device for 30 days"
},
"selectAnotherMethod": {
- "message": "Select another method",
+ "message": "다른 방법 시도",
"description": "Select another two-step login method"
},
"useYourRecoveryCode": {
- "message": "Use your recovery code"
+ "message": "복구 코드 사용"
},
"insertU2f": {
"message": "보안 키를 컴퓨터의 USB 포트에 삽입하고 버튼이 있는 경우 누르세요."
},
"openInNewTab": {
- "message": "Open in new tab"
+ "message": "새 탭에서 열기"
},
"webAuthnAuthenticate": {
"message": "WebAuthn 인증"
diff --git a/apps/browser/src/_locales/lt/messages.json b/apps/browser/src/_locales/lt/messages.json
index 29815c9de82..5a4f0d8c419 100644
--- a/apps/browser/src/_locales/lt/messages.json
+++ b/apps/browser/src/_locales/lt/messages.json
@@ -547,6 +547,9 @@
"searchVault": {
"message": "Ieškoti saugykloje"
},
+ "resetSearch": {
+ "message": "Reset search"
+ },
"edit": {
"message": "Keisti"
},
diff --git a/apps/browser/src/_locales/lv/messages.json b/apps/browser/src/_locales/lv/messages.json
index ba43b1e5f44..81447182f26 100644
--- a/apps/browser/src/_locales/lv/messages.json
+++ b/apps/browser/src/_locales/lv/messages.json
@@ -547,6 +547,9 @@
"searchVault": {
"message": "Meklēt glabātavā"
},
+ "resetSearch": {
+ "message": "Atiestatīt meklēšanu"
+ },
"edit": {
"message": "Labot"
},
diff --git a/apps/browser/src/_locales/ml/messages.json b/apps/browser/src/_locales/ml/messages.json
index a39fe07b2c6..11709a4611b 100644
--- a/apps/browser/src/_locales/ml/messages.json
+++ b/apps/browser/src/_locales/ml/messages.json
@@ -547,6 +547,9 @@
"searchVault": {
"message": "വാൾട് തിരയുക"
},
+ "resetSearch": {
+ "message": "Reset search"
+ },
"edit": {
"message": "തിരുത്തുക"
},
diff --git a/apps/browser/src/_locales/mr/messages.json b/apps/browser/src/_locales/mr/messages.json
index c79b8b322a7..e7d06e4d5f9 100644
--- a/apps/browser/src/_locales/mr/messages.json
+++ b/apps/browser/src/_locales/mr/messages.json
@@ -547,6 +547,9 @@
"searchVault": {
"message": "तिजोरीत शोधा"
},
+ "resetSearch": {
+ "message": "Reset search"
+ },
"edit": {
"message": "Edit"
},
diff --git a/apps/browser/src/_locales/my/messages.json b/apps/browser/src/_locales/my/messages.json
index 9a6d9a4d316..f4c58318bc1 100644
--- a/apps/browser/src/_locales/my/messages.json
+++ b/apps/browser/src/_locales/my/messages.json
@@ -547,6 +547,9 @@
"searchVault": {
"message": "Search vault"
},
+ "resetSearch": {
+ "message": "Reset search"
+ },
"edit": {
"message": "Edit"
},
diff --git a/apps/browser/src/_locales/nb/messages.json b/apps/browser/src/_locales/nb/messages.json
index 01353cac5e0..7cbe8747d3b 100644
--- a/apps/browser/src/_locales/nb/messages.json
+++ b/apps/browser/src/_locales/nb/messages.json
@@ -547,6 +547,9 @@
"searchVault": {
"message": "Søk i hvelvet"
},
+ "resetSearch": {
+ "message": "Reset search"
+ },
"edit": {
"message": "Rediger"
},
@@ -759,7 +762,7 @@
"message": "Hovedpassord"
},
"masterPassImportant": {
- "message": "Your master password cannot be recovered if you forget it!"
+ "message": "Hovedpassordet kan ikke gjenopprettes hvis du glemmer det!"
},
"masterPassHintLabel": {
"message": "Få et hint om hovedpassordet"
@@ -814,7 +817,7 @@
"message": "En verifiseringskode er påkrevd."
},
"webauthnCancelOrTimeout": {
- "message": "The authentication was cancelled or took too long. Please try again."
+ "message": "Autentiseringen ble avbrutt eller tok for lang tid. Prøv igjen."
},
"invalidVerificationCode": {
"message": "Ugyldig bekreftelseskode"
@@ -1097,7 +1100,7 @@
"description": "Shown to user after item is saved."
},
"notificationLoginUpdatedConfirmation": {
- "message": "updated in Bitwarden.",
+ "message": "oppdatert i Bitwarden.",
"description": "Shown to user after item is updated."
},
"selectItemAriaLabel": {
@@ -1252,25 +1255,25 @@
"message": "Filformat"
},
"fileEncryptedExportWarningDesc": {
- "message": "This file export will be password protected and require the file password to decrypt."
+ "message": "Denne fileksporten vil bli passordbeskyttet og vil kreve filpassordet ved dekryptering."
},
"filePassword": {
"message": "Filpassord"
},
"exportPasswordDescription": {
- "message": "This password will be used to export and import this file"
+ "message": "Dette passordet brukes for eksport og import av denne filen"
},
"accountRestrictedOptionDescription": {
- "message": "Use your account encryption key, derived from your account's username and Master Password, to encrypt the export and restrict import to only the current Bitwarden account."
+ "message": "Bruk kontokrypteringsnøkkelen, avledet fra ditt kontobrukernavn og hovedpassord, for å kryptere eksporten og hindre import til andre kontoer enn den aktuelle Bitwarden-kontoen."
},
"passwordProtectedOptionDescription": {
- "message": "Set a file password to encrypt the export and import it to any Bitwarden account using the password for decryption."
+ "message": "Sett et filpassord for kryptering av eksporten. Bruk passordet for å dekryptere og importere til en hvilken som helst Bitwarden-konto."
},
"exportTypeHeading": {
"message": "Eksporttype"
},
"accountRestricted": {
- "message": "Account restricted"
+ "message": "Kontoen er begrenset"
},
"filePasswordAndConfirmFilePasswordDoNotMatch": {
"message": "«Filpassord» og «Bekreft filpassord» stemmer ikke overens."
@@ -1621,7 +1624,7 @@
}
},
"turnOffAutofill": {
- "message": "Turn off autofill"
+ "message": "Skru av autoutfylling"
},
"showInlineMenuLabel": {
"message": "Vis autoutfyll-forslag i tekstbokser"
@@ -1830,7 +1833,7 @@
"message": "Sikkerhetskode"
},
"cardNumber": {
- "message": "card number"
+ "message": "kortnummer"
},
"ex": {
"message": "f.eks."
@@ -2534,7 +2537,7 @@
"message": "Endre"
},
"changePassword": {
- "message": "Change password",
+ "message": "Endre passord",
"description": "Change password button for browser at risk notification on login."
},
"changeButtonTitle": {
@@ -2723,7 +2726,7 @@
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
},
"maxAccessCountReached": {
- "message": "Max access count reached",
+ "message": "Maks antall tilganger er nådd",
"description": "This text will be displayed after a Send has been accessed the maximum amount of times."
},
"hideTextByDefault": {
@@ -3073,7 +3076,7 @@
"message": "A master password is no longer required for members of the following organization. Please confirm the domain below with your organization administrator."
},
"organizationName": {
- "message": "Organization name"
+ "message": "Organisasjonens navn"
},
"keyConnectorDomain": {
"message": "Key Connector domain"
@@ -3103,7 +3106,7 @@
"message": "Eksporterer personlig hvelv"
},
"exportingIndividualVaultDescription": {
- "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included. Only vault item information will be exported and will not include associated attachments.",
+ "message": "Kun de individuelle hvelvgjenstandene som er assosiert med $EMAIL$ vil bli eksportert. Organisasjonshvelv-gjenstander vil ikke bli inkludert. Kun hvelvgjenstandsinfo vil bli eksportert og vil ikke inkludere assosierte vedlegg.",
"placeholders": {
"email": {
"content": "$1",
@@ -3121,7 +3124,7 @@
}
},
"exportingOrganizationVaultTitle": {
- "message": "Exporting organization vault"
+ "message": "Eksporterer organisasjonshvelv"
},
"exportingOrganizationVaultDesc": {
"message": "Only the organization vault associated with $ORGANIZATION$ will be exported. Items in individual vaults or other organizations will not be included.",
@@ -3480,13 +3483,13 @@
"message": "You denied a login attempt from another device. If this was you, try to log in with the device again."
},
"device": {
- "message": "Device"
+ "message": "Enhet"
},
"loginStatus": {
- "message": "Login status"
+ "message": "Innloggingsstatus"
},
"masterPasswordChanged": {
- "message": "Master password saved"
+ "message": "Hovedpassordet er endret"
},
"exposedMasterPassword": {
"message": "Eksponert hovedpassord"
@@ -3585,28 +3588,28 @@
"message": "Manage devices"
},
"currentSession": {
- "message": "Current session"
+ "message": "Gjeldende økt"
},
"mobile": {
- "message": "Mobile",
+ "message": "Mobil",
"description": "Mobile app"
},
"extension": {
- "message": "Extension",
+ "message": "Utvidelse",
"description": "Browser extension/addon"
},
"desktop": {
- "message": "Desktop",
+ "message": "Skrivebord",
"description": "Desktop app"
},
"webVault": {
- "message": "Web vault"
+ "message": "Netthvelv"
},
"webApp": {
- "message": "Web app"
+ "message": "Nettapp"
},
"cli": {
- "message": "CLI"
+ "message": "Ledetekst"
},
"sdk": {
"message": "SDK",
@@ -3619,13 +3622,13 @@
"message": "First login"
},
"trusted": {
- "message": "Trusted"
+ "message": "Betrodd"
},
"needsApproval": {
- "message": "Needs approval"
+ "message": "Trenger godkjenning"
},
"devices": {
- "message": "Devices"
+ "message": "Enheter"
},
"accessAttemptBy": {
"message": "Access attempt by $EMAIL$",
@@ -3637,22 +3640,22 @@
}
},
"confirmAccess": {
- "message": "Confirm access"
+ "message": "Bekreft tilgang"
},
"denyAccess": {
- "message": "Deny access"
+ "message": "Nekt tilgang"
},
"time": {
- "message": "Time"
+ "message": "Tid"
},
"deviceType": {
- "message": "Device Type"
+ "message": "Enhetstype"
},
"loginRequest": {
- "message": "Login request"
+ "message": "Innloggingsforespørsel"
},
"thisRequestIsNoLongerValid": {
- "message": "This request is no longer valid."
+ "message": "Denne forespørselen er ikke lenger gyldig."
},
"areYouTryingToAccessYourAccount": {
"message": "Are you trying to access your account?"
@@ -3674,13 +3677,13 @@
"message": "You denied a login attempt from another device. If this really was you, try to log in with the device again."
},
"loginRequestHasAlreadyExpired": {
- "message": "Login request has already expired."
+ "message": "Innloggingsforespørselen har allerede utløpt."
},
"justNow": {
- "message": "Just now"
+ "message": "Akkurat nå"
},
"requestedXMinutesAgo": {
- "message": "Requested $MINUTES$ minutes ago",
+ "message": "Forespurt for $MINUTES$ minutter siden",
"placeholders": {
"minutes": {
"content": "$1",
@@ -3716,7 +3719,7 @@
"message": "You need to log in on a trusted device or ask your administrator to assign you a password."
},
"ssoIdentifierRequired": {
- "message": "Organization SSO identifier is required."
+ "message": "Organisasjonsidentifikator er påkrevd."
},
"creatingAccountOn": {
"message": "Oppretter en konto på"
@@ -3744,7 +3747,7 @@
"description": "European Union"
},
"accessDenied": {
- "message": "Access denied. You do not have permission to view this page."
+ "message": "Ingen tilgang. Du har ikke tillatelse til å se denne siden."
},
"general": {
"message": "Generelt"
@@ -3753,10 +3756,10 @@
"message": "Vis"
},
"accountSuccessfullyCreated": {
- "message": "Account successfully created!"
+ "message": "Kontoen ble vellykket opprettet!"
},
"adminApprovalRequested": {
- "message": "Admin approval requested"
+ "message": "Admin-godkjenning forespurt"
},
"adminApprovalRequestSentToAdmins": {
"message": "Forespørselen din har blitt sendt til administratoren din."
@@ -3836,7 +3839,7 @@
}
},
"inputForbiddenCharacters": {
- "message": "The following characters are not allowed: $CHARACTERS$",
+ "message": "Følgende tegn er ikke tillatt: $CHARACTERS$",
"placeholders": {
"characters": {
"content": "$1",
@@ -4043,7 +4046,7 @@
"message": "Importeringsfeil"
},
"importErrorDesc": {
- "message": "There was a problem with the data you tried to import. Please resolve the errors listed below in your source file and try again."
+ "message": "Det oppstod et problem med dataene du prøvde å importere. Vennligst løs feilene listet nedenfor i kildefilen og prøv på nytt."
},
"resolveTheErrorsBelowAndTryAgain": {
"message": "Fiks feilene nedenfor og prøv igjen."
@@ -4103,7 +4106,7 @@
"message": "Total"
},
"importWarning": {
- "message": "You are importing data to $ORGANIZATION$. Your data may be shared with members of this organization. Do you want to proceed?",
+ "message": "Du importerer data til $ORGANIZATION$. Dataene kan deles med medlemmer av denne organisasjonen. Vil du fortsette?",
"placeholders": {
"organization": {
"content": "$1",
@@ -4130,7 +4133,7 @@
"message": "Ingenting ble importert."
},
"importEncKeyError": {
- "message": "Error decrypting the exported file. Your encryption key does not match the encryption key used export the data."
+ "message": "Feil under dekryptering av den eksporterte filen. Krypteringsnøkkelen samsvarte ikke med krypteringsnøkkelen som ble brukt eksport av data."
},
"invalidFilePassword": {
"message": "Ugyldig filpassord, vennligst bruk passordet du skrev inn da du opprettet eksportfilen."
@@ -4148,7 +4151,7 @@
"message": "Velg en samling"
},
"importTargetHint": {
- "message": "Select this option if you want the imported file contents moved to a $DESTINATION$",
+ "message": "Velg dette alternativet hvis du vil flytte den importerte filens innhold til en $DESTINATION$",
"description": "Located as a hint under the import target. Will be appended by either folder or collection, depending if the user is importing into an individual or an organizational vault.",
"placeholders": {
"destination": {
@@ -4210,7 +4213,7 @@
"message": "Passkoden vil ikke bli kopiert"
},
"passkeyNotCopiedAlert": {
- "message": "The passkey will not be copied to the cloned item. Do you want to continue cloning this item?"
+ "message": "Passnøkkelen kopieres ikke til det klonede elementet. Vil du fortsette kloningen av denne gjenstanden?"
},
"passkeyFeatureIsNotImplementedForAccountsWithoutMasterPassword": {
"message": "Verification required by the initiating site. This feature is not yet implemented for accounts without master password."
@@ -4411,7 +4414,7 @@
"description": "Link to match detection docs on warning dialog for advance match strategy"
},
"uriAdvancedOption": {
- "message": "Advanced options",
+ "message": "Avanserte alternativer",
"description": "Advanced option placeholder for uri option component"
},
"confirmContinueToBrowserSettingsTitle": {
@@ -4745,31 +4748,31 @@
}
},
"downloadBitwarden": {
- "message": "Download Bitwarden"
+ "message": "Last ned Bitwarden"
},
"downloadBitwardenOnAllDevices": {
"message": "Download Bitwarden on all devices"
},
"getTheMobileApp": {
- "message": "Get the mobile app"
+ "message": "Skaff deg mobilappen"
},
"getTheMobileAppDesc": {
"message": "Access your passwords on the go with the Bitwarden mobile app."
},
"getTheDesktopApp": {
- "message": "Get the desktop app"
+ "message": "Skaff deg skrivebordsappen"
},
"getTheDesktopAppDesc": {
"message": "Access your vault without a browser, then set up unlock with biometrics to expedite unlocking in both the desktop app and browser extension."
},
"downloadFromBitwardenNow": {
- "message": "Download from bitwarden.com now"
+ "message": "Last ned fra bitwarden.com nå"
},
"getItOnGooglePlay": {
- "message": "Get it on Google Play"
+ "message": "Skaff den på Google Play"
},
"downloadOnTheAppStore": {
- "message": "Download on the App Store"
+ "message": "Last ned fra App Store"
},
"permanentlyDeleteAttachmentConfirmation": {
"message": "Are you sure you want to permanently delete this attachment?"
@@ -5494,7 +5497,7 @@
"message": "Save unlimited passwords across unlimited devices with Bitwarden mobile, browser, and desktop apps."
},
"nudgeBadgeAria": {
- "message": "1 notification"
+ "message": "1 varsel"
},
"emptyVaultNudgeTitle": {
"message": "Importer eksisterende passord"
@@ -5521,12 +5524,12 @@
"message": "Spar tid med auto-utfylling"
},
"newLoginNudgeBodyOne": {
- "message": "Include a",
+ "message": "Inkluder en",
"description": "This is in multiple parts to allow for bold text in the middle of the sentence.",
"example": "Include a Website so this login appears as an autofill suggestion."
},
"newLoginNudgeBodyBold": {
- "message": "Website",
+ "message": "Nettsted",
"description": "This is in multiple parts to allow for bold text in the middle of the sentence.",
"example": "Include a Website so this login appears as an autofill suggestion."
},
diff --git a/apps/browser/src/_locales/ne/messages.json b/apps/browser/src/_locales/ne/messages.json
index 9a6d9a4d316..f4c58318bc1 100644
--- a/apps/browser/src/_locales/ne/messages.json
+++ b/apps/browser/src/_locales/ne/messages.json
@@ -547,6 +547,9 @@
"searchVault": {
"message": "Search vault"
},
+ "resetSearch": {
+ "message": "Reset search"
+ },
"edit": {
"message": "Edit"
},
diff --git a/apps/browser/src/_locales/nl/messages.json b/apps/browser/src/_locales/nl/messages.json
index b5220861652..0c2bd31935f 100644
--- a/apps/browser/src/_locales/nl/messages.json
+++ b/apps/browser/src/_locales/nl/messages.json
@@ -547,6 +547,9 @@
"searchVault": {
"message": "Kluis doorzoeken"
},
+ "resetSearch": {
+ "message": "Zoekopdracht resetten"
+ },
"edit": {
"message": "Bewerken"
},
diff --git a/apps/browser/src/_locales/nn/messages.json b/apps/browser/src/_locales/nn/messages.json
index 9a6d9a4d316..f4c58318bc1 100644
--- a/apps/browser/src/_locales/nn/messages.json
+++ b/apps/browser/src/_locales/nn/messages.json
@@ -547,6 +547,9 @@
"searchVault": {
"message": "Search vault"
},
+ "resetSearch": {
+ "message": "Reset search"
+ },
"edit": {
"message": "Edit"
},
diff --git a/apps/browser/src/_locales/or/messages.json b/apps/browser/src/_locales/or/messages.json
index 9a6d9a4d316..f4c58318bc1 100644
--- a/apps/browser/src/_locales/or/messages.json
+++ b/apps/browser/src/_locales/or/messages.json
@@ -547,6 +547,9 @@
"searchVault": {
"message": "Search vault"
},
+ "resetSearch": {
+ "message": "Reset search"
+ },
"edit": {
"message": "Edit"
},
diff --git a/apps/browser/src/_locales/pl/messages.json b/apps/browser/src/_locales/pl/messages.json
index aa8ab76d543..04112b08219 100644
--- a/apps/browser/src/_locales/pl/messages.json
+++ b/apps/browser/src/_locales/pl/messages.json
@@ -26,7 +26,7 @@
"message": "Nowy w Bitwarden?"
},
"logInWithPasskey": {
- "message": "Logowaniem kluczem dostępu"
+ "message": "Logowanie kluczem dostępu"
},
"useSingleSignOn": {
"message": "Użyj logowania jednokrotnego"
@@ -96,7 +96,7 @@
"message": "Dołącz do organizacji"
},
"joinOrganizationName": {
- "message": "Dołącz do $ORGANIZATIONNAME$",
+ "message": "Dołącz do organizacji $ORGANIZATIONNAME$",
"placeholders": {
"organizationName": {
"content": "$1",
@@ -338,7 +338,7 @@
"message": "Przejść do bitwarden.com?"
},
"bitwardenForBusiness": {
- "message": "Bitwarden dla biznesu"
+ "message": "Bitwarden dla firm"
},
"bitwardenAuthenticator": {
"message": "Bitwarden Authenticator"
@@ -347,16 +347,16 @@
"message": "Bitwarden Authenticator umożliwia przechowywanie kluczy uwierzytelniających i generowanie kodów TOTP dla weryfikacji dwustopniowej. Dowiedz się wiecej na bitwarden.com"
},
"bitwardenSecretsManager": {
- "message": "Menedżer sekretów Bitwarden"
+ "message": "Bitwarden Secrets Manager"
},
"continueToSecretsManagerPageDesc": {
- "message": "Bezpiecznie przechowuj, zarządzaj i udostępniaj sekrety programistów z Menedżerem sekretów Bitwarden. Dowiedz się więcej na stronie bitwarden.com."
+ "message": "Bezpiecznie przechowuj, zarządzaj i udostępniaj sekrety deweloperów za pomocą usługi Bitwarden Secrets Manager. Dowiedz się więcej na stronie bitwarden.com."
},
"passwordlessDotDev": {
"message": "Passwordless.dev"
},
"continueToPasswordlessDotDevPageDesc": {
- "message": "Twórz przyjemne i bezpieczne doświadczenia z logowaniem wolne od tradycyjnych haseł za pomocą Passwordless.dev. Dowiedz się więcej na stronie bitwarden.com."
+ "message": "Loguj się szybko i bezpiecznie bez tradycyjnych haseł za pomocą usługi Passwordless.dev. Dowiedz się więcej na stronie bitwarden.com."
},
"freeBitwardenFamilies": {
"message": "Darmowy plan rodzinny"
@@ -541,12 +541,15 @@
"description": "Label for the avoid ambiguous characters checkbox."
},
"generatorPolicyInEffect": {
- "message": "Wymagania polityki przedsiębiorstwa zostały użyte do ustawienia opcji generatora.",
+ "message": "Zasady organizacji zostały zastosowane do opcji generatora.",
"description": "Indicates that a policy limits the credential generator screen."
},
"searchVault": {
"message": "Szukaj w sejfie"
},
+ "resetSearch": {
+ "message": "Zresetuj wyszukiwanie"
+ },
"edit": {
"message": "Edytuj"
},
@@ -647,7 +650,7 @@
"message": "Blokowanie sejfu"
},
"otherOptions": {
- "message": "Pozostałe opcje"
+ "message": "Inne opcje"
},
"rateExtension": {
"message": "Oceń rozszerzenie"
@@ -741,10 +744,10 @@
"message": "4 godziny"
},
"onLocked": {
- "message": "Po zablokowaniu komputera"
+ "message": "Po zablokowaniu urządzenia"
},
"onRestart": {
- "message": "Po restarcie przeglądarki"
+ "message": "Po uruchomieniu przeglądarki"
},
"never": {
"message": "Nigdy"
@@ -802,7 +805,7 @@
"message": "Zalogowano!"
},
"youSuccessfullyLoggedIn": {
- "message": "Zalogowałeś się pomyślnie"
+ "message": "Zalogowano"
},
"youMayCloseThisWindow": {
"message": "Możesz zamknąć to okno"
@@ -830,7 +833,7 @@
}
},
"autofillError": {
- "message": "Nie można zastosować autouzupełnienia na tej stronie. Skopiuj i wklej informacje ręcznie."
+ "message": "Nie można uzupełnić elementu na tej stronie internetowej. Skopiuj i wklej informacje ręcznie."
},
"totpCaptureError": {
"message": "Nie można zeskanować kodu QR z obecnej strony"
@@ -860,7 +863,7 @@
"message": "Wylogowano"
},
"loggedOutDesc": {
- "message": "Zostałeś wylogowany z konta."
+ "message": "Wylogowano z konta."
},
"loginExpired": {
"message": "Twoja sesja wygasła."
@@ -890,13 +893,13 @@
"message": "Wykonaj poniższe kroki, aby zakończyć logowanie za pomocą klucza bezpieczeństwa."
},
"restartRegistration": {
- "message": "Zacznij rejestrację od początku"
+ "message": "Rozpocznij rejestrację od początku"
},
"expiredLink": {
"message": "Link wygasł"
},
"pleaseRestartRegistrationOrTryLoggingIn": {
- "message": "Zrestartuj rejestrację lub spróbuj się zalogować."
+ "message": "Rozpocznij rejestrację od początku lub spróbuj się zalogować."
},
"youMayAlreadyHaveAnAccount": {
"message": "Możesz mieć już konto"
@@ -926,7 +929,7 @@
"message": "Logowanie dwustopniowe zwiększa bezpieczeństwo konta, wymagając weryfikacji logowania za pomocą innego urządzenia, takiego jak klucz bezpieczeństwa, aplikacja uwierzytelniająca, wiadomość SMS, połączenie telefoniczne lub wiadomość e-mail. Logowanie dwustopniowe możesz skonfigurować w sejfie internetowym bitwarden.com. Czy chcesz przejść do strony?"
},
"twoStepLoginConfirmationContent": {
- "message": "Spraw, aby Twoje konto było bezpieczniejsze poprzez skonfigurowanie dwustopniowego logowania w aplikacji internetowej Bitwarden."
+ "message": "Zwiększ bezpieczeństwo konta, konfigurując logowanie dwustopniowe w aplikacji internetowej Bitwarden."
},
"twoStepLoginConfirmationTitle": {
"message": "Przejść do aplikacji internetowej?"
@@ -1025,22 +1028,22 @@
"message": "Proponuj dodanie elementu, jeśli nie ma go w sejfie. Dotyczy wszystkich zalogowanych kont."
},
"showCardsInVaultViewV2": {
- "message": "Pokazuj zawsze karty w sugestiach autouzupełniania"
+ "message": "Zawsze pokazuj karty w sugestiach autouzupełniania"
},
"showCardsCurrentTab": {
"message": "Pokaż karty na stronie głównej"
},
"showCardsCurrentTabDesc": {
- "message": "Pokaż elementy karty na stronie głównej, aby ułatwić autouzupełnianie."
+ "message": "Wyświetla karty na głównej karcie sejfu."
},
"showIdentitiesInVaultViewV2": {
- "message": "Pokazuj zawsze tożsamości w sugestiach autouzupełniania"
+ "message": "Zawsze pokazuj tożsamości w sugestiach autouzupełniania"
},
"showIdentitiesCurrentTab": {
"message": "Pokaż tożsamości na stronie głównej"
},
"showIdentitiesCurrentTabDesc": {
- "message": "Pokaż elementy tożsamości na stronie głównej, aby ułatwić autouzupełnianie."
+ "message": "Wyświetla tożsamości na głównej karcie sejfu."
},
"clickToAutofillOnVault": {
"message": "Kliknij na elementy, aby je uzupełnić"
@@ -1063,7 +1066,7 @@
"message": "Zapisz"
},
"notificationViewAria": {
- "message": "Wyświetl $ITEMNAME$, otworzy się w nowym oknie",
+ "message": "Pokaż $ITEMNAME$, otwiera się w nowym oknie",
"placeholders": {
"itemName": {
"content": "$1"
@@ -1129,7 +1132,7 @@
"description": "Prompt asking the user if they want to save their login details."
},
"updateLogin": {
- "message": "Zaktualizuj obecne dane logowania",
+ "message": "Zaktualizuj dane logowania",
"description": "Prompt asking the user if they want to update an existing login entry."
},
"loginSaveSuccess": {
@@ -1174,7 +1177,7 @@
"description": "Detailed error message shown when saving login details fails."
},
"changePasswordWarning": {
- "message": "Po zmianie hasła musisz się zalogować przy użyciu nowego hasła. Aktywne sesje na innych urządzeniach zostaną wylogowane w ciągu jednej godziny."
+ "message": "Po zmianie hasła zaloguj się za pomocą nowego hasła. Aktywne sesje na innych urządzeniach zostaną wylogowane w ciągu jednej godziny."
},
"accountRecoveryUpdateMasterPasswordSubtitle": {
"message": "Zmień hasło główne, aby zakończyć odzyskiwanie konta."
@@ -1201,7 +1204,7 @@
"message": "Zaktualizuj"
},
"notificationUnlockDesc": {
- "message": "Odblokuj swój sejf Bitwarden, aby ukończyć żądanie autouzupełniania."
+ "message": "Odblokuj sejf Bitwarden, aby uzupełnić dane."
},
"notificationUnlock": {
"message": "Odblokuj"
@@ -1270,7 +1273,7 @@
"message": "Rodzaj eksportu"
},
"accountRestricted": {
- "message": "Konto ograniczone"
+ "message": "Konto zostało ograniczone"
},
"filePasswordAndConfirmFilePasswordDoNotMatch": {
"message": "Hasła pliku nie pasują do siebie."
@@ -1287,7 +1290,7 @@
"message": "Potwierdź eksportowanie sejfu"
},
"exportWarningDesc": {
- "message": "Plik zawiera dane sejfu w niezaszyfrowanym formacie. Nie powinieneś go przechowywać, ani przesyłać poprzez niezabezpieczone kanały (takie jak poczta e-mail). Skasuj go natychmiast po użyciu."
+ "message": "Plik zawiera dane sejfu w niezaszyfrowanym formacie. Nie należy go przechowywać ani przesyłać poprzez niezabezpieczone kanały (takie jak poczta e-mail). Usuń go natychmiast po użyciu."
},
"encExportKeyWarningDesc": {
"message": "Dane eksportu zostaną zaszyfrowane za pomocą klucza szyfrowania konta. Jeśli kiedykolwiek zmienisz ten klucz, wyeksportuj dane ponownie, ponieważ nie będziesz w stanie odszyfrować tego pliku."
@@ -1302,7 +1305,7 @@
"message": "Udostępnione"
},
"bitwardenForBusinessPageDesc": {
- "message": "Bitwarden dla biznesu pozwala na udostępnianie zawartości sejfu innym użytkownikom za pośrednictwem organizacji. Dowiedz się więcej na stronie bitwarden.com."
+ "message": "Bitwarden dla firm pozwala na udostępnianie zawartości sejfu innym użytkownikom za pośrednictwem organizacji. Dowiedz się więcej na stronie bitwarden.com."
},
"moveToOrganization": {
"message": "Przenieś do organizacji"
@@ -1456,7 +1459,7 @@
"message": "Automatycznie kopiuje kod TOTP do schowka podczas autouzupełniania."
},
"enableAutoBiometricsPrompt": {
- "message": "Poproś o dane biometryczne przy uruchomieniu"
+ "message": "Wymagaj odblokowania biometrią po uruchomieniu przeglądarki"
},
"premiumRequired": {
"message": "Konto premium jest wymagane"
@@ -1468,7 +1471,7 @@
"message": "Limit czasu uwierzytelniania"
},
"authenticationSessionTimedOut": {
- "message": "Upłynął limit czasu uwierzytelniania. Uruchom ponownie proces logowania."
+ "message": "Upłynął limit czasu uwierzytelniania. Zaloguj się ponownie."
},
"verificationCodeEmailSent": {
"message": "Wiadomość weryfikacyjna została wysłana na adres $EMAIL$.",
@@ -1508,10 +1511,10 @@
"message": "Logowanie jest niedostępne"
},
"noTwoStepProviders": {
- "message": "Konto posiada włączoną opcję logowania dwustopniowego, jednak ta przeglądarka nie wspiera żadnego ze skonfigurowanych mechanizmów autoryzacji dwustopniowej."
+ "message": "Konto jest zabezpieczone logowaniem dwustopniowym, ale żadna ze skonfigurowanych metod nie jest obsługiwana w tej przeglądarce."
},
"noTwoStepProviders2": {
- "message": "Proszę użyć obsługiwanej przeglądarki (takiej jak Chrome) i/lub dodać dodatkowych dostawców, którzy są lepiej wspierani przez przeglądarki internetowe (np. aplikacja uwierzytelniająca)."
+ "message": "Użyj obsługiwanej przeglądarki (np. Chrome) lub dodaj dodatkowe opcje logowania dwustopniowego, które są obsługiwane na różnych przeglądarkach (takie jak aplikacja uwierzytelniająca)."
},
"twoStepOptions": {
"message": "Opcje logowania dwustopniowego"
@@ -1609,7 +1612,7 @@
"message": "Łatwe wyszukiwanie sugestii autouzupełniania"
},
"autofillSpotlightDesc": {
- "message": "Wyłącz ustawienia autouzupełniania swojej przeglądarki, aby nie kolidowały z Bitwarden."
+ "message": "Wyłącz autouzupełnianie przeglądarki, aby uniknąć konfliktów z Bitwarden."
},
"turnOffBrowserAutofill": {
"message": "Wyłącz autouzupełnianie $BROWSER$",
@@ -1645,15 +1648,15 @@
"message": "Edytuj ustawienia przeglądarki."
},
"autofillOverlayVisibilityOff": {
- "message": "Wył.",
+ "message": "Wyłączone",
"description": "Overlay setting select option for disabling autofill overlay"
},
"autofillOverlayVisibilityOnFieldFocus": {
- "message": "Gdy pole jest zaznaczone",
+ "message": "Po kliknięciu pola",
"description": "Overlay appearance select option for showing the field on focus of the input element"
},
"autofillOverlayVisibilityOnButtonClick": {
- "message": "Gdy wybrano ikonę autouzupełniania",
+ "message": "Po kliknięciu ikony autouzupełniania",
"description": "Overlay appearance select option for showing the field on click of the overlay icon"
},
"enableAutoFillOnPageLoadSectionTitle": {
@@ -1971,7 +1974,7 @@
"message": "Wyczyść historię generatora"
},
"cleargGeneratorHistoryDescription": {
- "message": "Jeśli zatwierdzisz, wszystkie wygenerowane hasła zostaną usunięte z historii generatora. Czy chcesz kontynuować mimo to?"
+ "message": "Wszystkie wpisy zostaną trwale usunięte z historii generatora. Czy na pewno chcesz kontynuować?"
},
"back": {
"message": "Wstecz"
@@ -1980,7 +1983,7 @@
"message": "Kolekcje"
},
"nCollections": {
- "message": "Kolekcje ($COUNT$)",
+ "message": "W $COUNT$ kolekcjach",
"placeholders": {
"count": {
"content": "$1",
@@ -2066,7 +2069,7 @@
"description": "Default URI match detection for autofill."
},
"toggleOptions": {
- "message": "Zmień opcje"
+ "message": "Przełącz opcje"
},
"toggleCurrentUris": {
"message": "Przełącz obecny URI",
@@ -2096,13 +2099,13 @@
"message": "Brak zawartości do pokazania"
},
"nothingGeneratedRecently": {
- "message": "Nic nie zostało wygenerowane przez ciebie w ostatnim czasie"
+ "message": "Nic nie zostało wygenerowane w ostatnim czasie"
},
"remove": {
"message": "Usuń"
},
"default": {
- "message": "Domyślne"
+ "message": "Domyślna"
},
"dateUpdated": {
"message": "Zaktualizowano",
@@ -2147,7 +2150,7 @@
"message": "Hasło główne jest słabe"
},
"weakMasterPasswordDesc": {
- "message": "Wybrane przez Ciebie hasło główne jest słabe. Powinieneś użyć silniejszego hasła (lub frazy), aby właściwie chronić swoje konto Bitwarden. Czy na pewno chcesz użyć tego hasła głównego?"
+ "message": "Użyj silniejszego hasła, aby odpowiednio chronić konto Bitwarden. Czy na pewno chcesz użyć tego hasła głównego?"
},
"pin": {
"message": "Kod PIN",
@@ -2163,10 +2166,10 @@
"message": "Ustaw kod PIN"
},
"setYourPinCode": {
- "message": "Ustaw kod PIN do odblokowywania aplikacji Bitwarden. Ustawienia odblokowywania kodem PIN zostaną zresetowane po wylogowaniu."
+ "message": "Ustaw kod PIN do odblokowania aplikacji Bitwarden. Ustawienia kodu PIN zostaną zresetowane po wylogowaniu."
},
"setPinCode": {
- "message": "Możesz użyć tego kodu PIN, aby odblokować Bitwarden. Twój kod PIN zostanie zresetowany, jeśli kiedykolwiek wylogujesz się z aplikacji."
+ "message": "Możesz użyć tego kodu PIN do odblokowania aplikacji Bitwarden. Kod PIN zostanie zresetowany po wylogowaniu."
},
"pinRequired": {
"message": "Kod PIN jest wymagany."
@@ -2187,13 +2190,13 @@
"message": "Oczekiwanie na potwierdzenie z aplikacji desktopowej"
},
"awaitDesktopDesc": {
- "message": "Włącz dane biometryczne w aplikacji desktopowej Bitwarden, aby włączyć tę samą funkcję w przeglądarce."
+ "message": "Włącz najpierw biometrię w aplikacji desktopowej Bitwarden, aby skonfigurować dane biometryczne w przeglądarce."
},
"lockWithMasterPassOnRestart": {
"message": "Zablokuj hasłem głównym po uruchomieniu przeglądarki"
},
"lockWithMasterPassOnRestart1": {
- "message": "Wymagaj hasła głównego przy ponownym uruchomieniu przeglądarki"
+ "message": "Wymagaj hasła głównego po uruchomieniu przeglądarki"
},
"selectOneCollection": {
"message": "Musisz wybrać co najmniej jedną kolekcję."
@@ -2223,14 +2226,14 @@
"message": "Użyj tej nazwy użytkownika"
},
"securePasswordGenerated": {
- "message": "Wygenerowane bezpieczne hasło! Nie zapomnij również zaktualizować hasła na stronie."
+ "message": "Bezpieczne hasło zostało wygenerowane! Nie zapomnij zaktualizować hasła na stronie internetowej."
},
"useGeneratorHelpTextPartOne": {
"message": "Użyj generatora",
"description": "This will be used as part of a larger sentence, broken up to include the generator icon. The full sentence will read 'Use the generator [GENERATOR_ICON] to create a strong unique password'"
},
"useGeneratorHelpTextPartTwo": {
- "message": ", aby utworzyć mocne unikalne hasło",
+ "message": ", aby utworzyć silne i unikalne hasło",
"description": "This will be used as part of a larger sentence, broken up to include the generator icon. The full sentence will read 'Use the generator [GENERATOR_ICON] to create a strong unique password'"
},
"vaultCustomization": {
@@ -2296,10 +2299,10 @@
"message": "Czy chcesz uzupełnić dane logowania?"
},
"autofillIframeWarning": {
- "message": "Formularz jest hostowany przez inną domenę niż zapisany adres URI dla tego loginu. Wybierz OK, aby i tak automatycznie wypełnić lub anuluj, aby zatrzymać."
+ "message": "Formularz jest hostowany przez inną domenę niż URI zapisanych danych logowania. Kliknij OK, aby uzupełnić dane logowania lub Anuluj, aby zatrzymać."
},
"autofillIframeWarningTip": {
- "message": "Aby zapobiec temu ostrzeżeniu w przyszłości, zapisz ten URI, $HOSTNAME$, dla tej witryny.",
+ "message": "Aby uniknąć ostrzeżenia w przyszłości, zapisz URI $HOSTNAME$ w danych logowania.",
"placeholders": {
"hostname": {
"content": "$1",
@@ -2368,7 +2371,7 @@
"message": "Anuluj subskrypcję"
},
"atAnyTime": {
- "message": "w każdej chwili."
+ "message": "w dowolnym momencie."
},
"byContinuingYouAgreeToThe": {
"message": "Kontynuując, akceptujesz"
@@ -2407,7 +2410,7 @@
"message": "Weryfikacja synchronizacji z aplikacją desktopową"
},
"desktopIntegrationVerificationText": {
- "message": "Zweryfikuj aplikację desktopową z odciskiem klucza: "
+ "message": "Zweryfikuj aplikację desktopową z identyfikatorem: "
},
"desktopIntegrationDisabledTitle": {
"message": "Połączenie z przeglądarką jest wyłączone"
@@ -2419,7 +2422,7 @@
"message": "Uruchom aplikację desktopową Bitwarden"
},
"startDesktopDesc": {
- "message": "Aplikacja desktopowa Bitwarden, przed odblokowaniem danymi biometrycznymi, musi zostać ponownie uruchomiona."
+ "message": "Aplikacja desktopowa Bitwarden musi zostać uruchomiona przed odblokowaniem za pomocą biometrii."
},
"errorEnableBiometricTitle": {
"message": "Nie można włączyć biometrii"
@@ -2434,7 +2437,7 @@
"message": "Komunikacja z aplikacją desktopową została przerwana"
},
"nativeMessagingWrongUserDesc": {
- "message": "W aplikacji desktopowej jesteś zalogowany na inne konto. Upewnij się, że w obu aplikacjach jesteś zalogowany na to same konto."
+ "message": "Aplikacja desktopowa jest zalogowana na inne konto. Upewnij się, że obie aplikacje są zalogowane na to samo konto."
},
"nativeMessagingWrongUserTitle": {
"message": "Konto jest niezgodne"
@@ -2443,13 +2446,13 @@
"message": "Klucz biometrii jest nieprawidłowy"
},
"nativeMessagingWrongUserKeyDesc": {
- "message": "Odblokowanie biometryczne się nie powiodło. Sekretny klucz biometryczny nie odblokował sejfu. Spróbuj skonfigurować biometrię ponownie."
+ "message": "Odblokowanie biometrią nie powiodło się. Klucz biometrii nie odblokował sejfu. Spróbuj ponownie skonfigurować biometrię."
},
"biometricsNotEnabledTitle": {
"message": "Biometria jest wyłączona"
},
"biometricsNotEnabledDesc": {
- "message": "Aby włączyć dane biometryczne w przeglądarce, musisz włączyć tę samą funkcję w ustawianiach aplikacji desktopowej."
+ "message": "Aby skonfigurować dane biometryczne w przeglądarce, włącz najpierw biometrię w aplikacji desktopowej."
},
"biometricsNotSupportedTitle": {
"message": "Biometria nie jest obsługiwana"
@@ -2485,7 +2488,7 @@
"message": "Wystąpił błąd żądania uprawnienia"
},
"nativeMessaginPermissionSidebarDesc": {
- "message": "Ta operacja nie może zostać wykonana na pasku bocznym. Spróbuj ponownie w nowym oknie."
+ "message": "Akcji nie można wykonać na pasku bocznym. Otwórz rozszerzenie w oknie."
},
"personalOwnershipSubmitError": {
"message": "Ze względu na zasadę organizacji, nie możesz zapisywać elementów w osobistym sejfie. Zmień właściciela elementu na organizację i wybierz jedną z dostępnych kolekcji."
@@ -2494,7 +2497,7 @@
"message": "Zasada organizacji ma wpływ na opcję własności elementów."
},
"personalOwnershipPolicyInEffectImports": {
- "message": "Polityka organizacji zablokowała importowanie elementów do Twojego sejfu."
+ "message": "Zasada organizacji zablokowała importowanie elementów do osobistego sejfu."
},
"restrictCardTypeImport": {
"message": "Nie można zaimportować karty"
@@ -2619,7 +2622,7 @@
"message": "Zmień zagrożone hasła szybciej"
},
"changeAtRiskPasswordsFasterDesc": {
- "message": "Zaktualizuj swoje ustawienia, aby szybko autouzupełniać hasła i generować nowe"
+ "message": "Zaktualizuj ustawienia, aby szybko uzupełniać hasła i generować nowe."
},
"reviewAtRiskLogins": {
"message": "Sprawdź zagrożone dane logowania"
@@ -2645,7 +2648,7 @@
"message": "Zaktualizuj w Bitwarden"
},
"updateInBitwardenSlideDesc": {
- "message": "Bitwarden poprosi Cię o aktualizację hasła w menedżerze haseł.",
+ "message": "Bitwarden zaproponuje aktualizację hasła w menedżerze haseł.",
"description": "Description of the update in Bitwarden slide on the at-risk password page carousel"
},
"updateInBitwardenSlideImgAltPeriod": {
@@ -2887,7 +2890,7 @@
"message": "Aby wybrać plik, otwórz rozszerzenie w oknie."
},
"popOut": {
- "message": "Odepnij"
+ "message": "Otwórz w nowym oknie"
},
"sendFileCalloutHeader": {
"message": "Zanim zaczniesz"
@@ -2960,7 +2963,7 @@
"description": "Used as a message within the notification bar when no folders are found"
},
"orgPermissionsUpdatedMustSetPassword": {
- "message": "Uprawnienia w Twojej organizacji zostały zaktualizowane, musisz teraz ustawić hasło główne.",
+ "message": "Uprawnienia organizacji zostały zaktualizowane. Ustaw hasło główne.",
"description": "Used as a card title description on the set password page to explain why the user is there"
},
"orgRequiresYouToSetPassword": {
@@ -3146,7 +3149,7 @@
"description": "This is part of a larger sentence. The full sentence will read 'Contact customer success to avoid additional data loss.'"
},
"contactCSToAvoidDataLossPart2": {
- "message": "aby uniknąć dalszej utraty danych.",
+ "message": "aby uniknąć dodatkowej utraty danych.",
"description": "This is part of a larger sentence. The full sentence will read 'Contact customer success to avoid additional data loss.'"
},
"generateUsername": {
@@ -3243,7 +3246,7 @@
}
},
"forwarderGeneratedBy": {
- "message": "Wygenerowane przez Bitwarden.",
+ "message": "Wygenerowano przez Bitwarden.",
"description": "Displayed with the address on the forwarding service's configuration screen."
},
"forwarderGeneratedByWithWebsite": {
@@ -3365,7 +3368,7 @@
"message": "Klucz API"
},
"ssoKeyConnectorError": {
- "message": "Błąd serwera Key Connector: upewnij się, że serwer Key Connector jest dostępny i działa poprawnie."
+ "message": "Wystąpił błąd serwera Key Connector. Upewnij się, że serwer jest dostępny i działa poprawnie."
},
"premiumSubcriptionRequired": {
"message": "Wymagana jest subskrypcja premium"
@@ -3395,7 +3398,7 @@
"message": "Inny dostawca"
},
"thirdPartyServerMessage": {
- "message": "Połączono z implementacją serwera innego dostawcy, $SERVERNAME$. Zweryfikuj błędy za pomocą oficjalnego serwera lub zgłoś je serwerowi innego dostawcy.",
+ "message": "Połączono z implementacją serwera innego dostawcy $SERVERNAME$. Zweryfikuj błędy za pomocą oficjalnego serwera lub zgłoś je serwerowi.",
"placeholders": {
"servername": {
"content": "$1",
@@ -3428,7 +3431,7 @@
"message": "Unikalny identyfikator konta"
},
"fingerprintMatchInfo": {
- "message": "Upewnij się, że sejf jest odblokowany, a unikalny identyfikator konta pasuje do drugiego urządzenia."
+ "message": "Upewnij się, że sejf jest odblokowany, a identyfikator pasuje do drugiego urządzenia."
},
"resendNotification": {
"message": "Wyślij ponownie powiadomienie"
@@ -3446,7 +3449,7 @@
"message": "aplikacji internetowej"
},
"notificationSentDevicePart2": {
- "message": "Upewnij się, że fraza odcisku palca zgadza się z tą poniżej, zanim zatwierdzisz."
+ "message": "Upewnij się, że identyfikator jest zgodny."
},
"aNotificationWasSentToYourDevice": {
"message": "Powiadomienie zostało wysłane na urządzenie"
@@ -3483,7 +3486,7 @@
"message": "Urządzenie"
},
"loginStatus": {
- "message": "Status zalogowania"
+ "message": "Status logowania"
},
"masterPasswordChanged": {
"message": "Hasło główne zostało zapisane"
@@ -3492,7 +3495,7 @@
"message": "Hasło główne zostało ujawnione"
},
"exposedMasterPasswordDesc": {
- "message": "Hasło ujawnione w wyniku naruszenia ochrony danych. Użyj unikalnego hasła, aby chronić swoje konto. Czy na pewno chcesz użyć ujawnionego hasła?"
+ "message": "Hasło zostało ujawnione w wycieku danych. Użyj unikalnego hasła, aby chronić konto. Czy na pewno chcesz użyć ujawnionego hasła?"
},
"weakAndExposedMasterPassword": {
"message": "Hasło główne jest słabe i ujawnione"
@@ -3501,7 +3504,7 @@
"message": "Hasło jest słabe i zostało ujawnione w wycieku danych. Użyj mocnego i unikalnego hasła, aby chronić konto. Czy na pewno chcesz użyć tego hasła?"
},
"checkForBreaches": {
- "message": "Sprawdź znane naruszenia ochrony danych tego hasła"
+ "message": "Sprawdź hasło w znanych wyciekach danych"
},
"important": {
"message": "Ważne:"
@@ -3534,7 +3537,7 @@
}
},
"autofillSelectInfoWithoutCommand": {
- "message": "Wybierz element z tego ekranu lub zobacz inne opcje w ustawieniach."
+ "message": "Wybierz element lub zobacz inne opcje w ustawieniach."
},
"gotIt": {
"message": "Ok"
@@ -3579,7 +3582,7 @@
"message": "Otwiera w nowym oknie"
},
"rememberThisDeviceToMakeFutureLoginsSeamless": {
- "message": "Zapamiętaj to urządzenie, aby przyszłe logowania były bezproblemowe"
+ "message": "Zapamiętaj urządzenie, aby przyszłe logowania były bezproblemowe"
},
"manageDevices": {
"message": "Zarządzaj urządzeniami"
@@ -3622,7 +3625,7 @@
"message": "Zaufane"
},
"needsApproval": {
- "message": "Wymagane potwierdzenie"
+ "message": "Potwierdzenie jest wymagane"
},
"devices": {
"message": "Urządzenia"
@@ -3680,7 +3683,7 @@
"message": "Teraz"
},
"requestedXMinutesAgo": {
- "message": "Poproszono $MINUTES$ min temu",
+ "message": "$MINUTES$ min temu",
"placeholders": {
"minutes": {
"content": "$1",
@@ -3689,10 +3692,10 @@
}
},
"deviceApprovalRequired": {
- "message": "Wymagane zatwierdzenie urządzenia. Wybierz opcję zatwierdzenia poniżej:"
+ "message": "Potwierdzenie urządzenia jest wymagane. Wybierz opcję:"
},
"deviceApprovalRequiredV2": {
- "message": "Wymagane zatwierdzenie urządzenia"
+ "message": "Potwierdzenie urządzenia jest wymagane"
},
"selectAnApprovalOptionBelow": {
"message": "Wybierz opcję potwierdzenia"
@@ -3725,7 +3728,7 @@
"message": "Sprawdź swoją pocztę e-mail"
},
"followTheLinkInTheEmailSentTo": {
- "message": "Kliknij łącze w wiadomości e-mail wysłanej do"
+ "message": "Kliknij link w wiadomości wysłanej na adres"
},
"andContinueCreatingYourAccount": {
"message": "i kontynuuj tworzenie konta."
@@ -3792,7 +3795,7 @@
"message": "Dla bezpieczeństwa Twojego konta potwierdź tylko, jeśli przyznano temu użytkownikowi dostęp awaryjny i jego odcisk palca pasuje do tego, co widnieje na jego koncie"
},
"orgTrustWarning": {
- "message": "Dla zapewnienia bezpieczeństwa konta kontynuuj tylko wtedy, gdy jesteś członkiem tej organizacji, włączono odzyskiwanie konta, a odcisk palca wyświetlany poniżej pasuje do odcisku palca organizacji."
+ "message": "Kontynuuj tylko wtedy, gdy jesteś członkiem organizacji, masz włączone odzyskiwanie konta, a unikalny identyfikator pasuje do organizacji."
},
"orgTrustWarning1": {
"message": "Zasada organizacji pozwala administratorom organizacji na zmianę Twojego hasła. Kontynuuj tylko wtedy, gdy rozpoznajesz organizację, a unikalny identyfikator pasuje do organizacji."
@@ -3805,7 +3808,7 @@
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
},
"sendsBodyNoItems": {
- "message": "Udostępniaj pliki i teksty każdemu, na dowolnej platformie. Informacje będę szyfrowane end-to-end, zapewniając poufność.",
+ "message": "Udostępniaj pliki i teksty każdemu na dowolnej platformie. Informacje będę szyfrowane end-to-end, zapewniając poufność.",
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
},
"inputRequired": {
@@ -3943,7 +3946,7 @@
"message": "Przełącz nawigację boczną"
},
"skipToContent": {
- "message": "Przejdź do treści"
+ "message": "Przejdź do zawartości"
},
"bitwardenOverlayButton": {
"message": "Przycisk menu autouzupełniania Bitwarden",
@@ -3958,7 +3961,7 @@
"description": "Page title in overlay"
},
"unlockYourAccountToViewMatchingLogins": {
- "message": "Odblokuj swoje konto, aby wyświetlić pasujące elementy",
+ "message": "Odblokuj konto, aby zobaczy pasujące dane logowania",
"description": "Text to display in overlay when the account is locked."
},
"unlockYourAccountToViewAutofillSuggestions": {
@@ -3970,7 +3973,7 @@
"description": "Button text to display in overlay when the account is locked."
},
"unlockAccountAria": {
- "message": "Odblokuj swoje konto, otwiera się w nowym oknie",
+ "message": "Odblokuj konto, otwiera się w nowym oknie",
"description": "Screen reader text (aria-label) for unlock account button in overlay"
},
"totpCodeAria": {
@@ -3978,7 +3981,7 @@
"description": "Aria label for the totp code displayed in the inline menu for autofill"
},
"totpSecondsSpanAria": {
- "message": "Pozostały czas do wygaśnięcia bieżącego TOTP",
+ "message": "Pozostały czas do wygaśnięcia kodu TOTP",
"description": "Aria label for the totp seconds displayed in the inline menu for autofill"
},
"fillCredentialsFor": {
@@ -4006,7 +4009,7 @@
"description": "Button text to display within inline menu when there are no matching items on a login field"
},
"addNewLoginItemAria": {
- "message": "Dodaj nowe dane logowania do sejfu, otwiera się w nowym oknie",
+ "message": "Dodaj nowe dane logowania, otwiera się w nowym oknie",
"description": "Screen reader text (aria-label) for new login button within inline menu"
},
"newCard": {
@@ -4014,7 +4017,7 @@
"description": "Button text to display within inline menu when there are no matching items on a credit card field"
},
"addNewCardItemAria": {
- "message": "Dodaj nową kartę do sejfu, otwiera się w nowym oknie",
+ "message": "Dodaj nową kartę, otwiera się w nowym oknie",
"description": "Screen reader text (aria-label) for new card button within inline menu"
},
"newIdentity": {
@@ -4022,7 +4025,7 @@
"description": "Button text to display within inline menu when there are no matching items on an identity field"
},
"addNewIdentityItemAria": {
- "message": "Dodaj nową tożsamość do sejfu, otwiera się w nowym oknie",
+ "message": "Dodaj nową tożsamość, otwiera się w nowym oknie",
"description": "Screen reader text (aria-label) for new identity button within inline menu"
},
"bitwardenOverlayMenuAvailable": {
@@ -4118,13 +4121,13 @@
"message": "Konto wymaga logowania dwustopniowego Duo."
},
"popoutExtension": {
- "message": "Otwórz rozszerzenie w nowym oknie"
+ "message": "Otwórz rozszerzenie w oknie"
},
"launchDuo": {
"message": "Uruchom Duo"
},
"importFormatError": {
- "message": "Dane nie są poprawnie sformatowane. Sprawdź importowany plik i spróbuj ponownie."
+ "message": "Dane nie są prawidłowo sformatowane. Sprawdź plik i spróbuj ponownie."
},
"importNothingError": {
"message": "Nic nie zostało zaimportowane."
@@ -4133,7 +4136,7 @@
"message": "Wystąpił błąd podczas odszyfrowywania pliku. Klucz szyfrowania nie pasuje do klucza użytego podczas eksportowania danych."
},
"invalidFilePassword": {
- "message": "Hasło do pliku jest nieprawidłowe. Użyj hasła które podano przy tworzeniu pliku eksportu."
+ "message": "Hasło pliku jest nieprawidłowe. Użyj prawidłowego hasła."
},
"destination": {
"message": "Miejsce docelowe"
@@ -4148,7 +4151,7 @@
"message": "Wybierz kolekcję"
},
"importTargetHint": {
- "message": "Wybierz tę opcję, jeśli chcesz, aby zawartość zaimportowanego pliku została przeniesiona do $DESTINATION$",
+ "message": "Wybierz tę opcję, jeśli chcesz przenieść dane do $DESTINATION$",
"description": "Located as a hint under the import target. Will be appended by either folder or collection, depending if the user is importing into an individual or an organizational vault.",
"placeholders": {
"destination": {
@@ -4189,7 +4192,7 @@
"message": "Potwierdź importowanie sejfu"
},
"confirmVaultImportDesc": {
- "message": "Plik jest chroniony hasłem. Wprowadź hasło pliku, aby zaimportować dane."
+ "message": "Plik jest chroniony hasłem. Wpisz hasło pliku, aby zaimportować dane."
},
"confirmFilePassword": {
"message": "Potwierdź hasło pliku"
@@ -4246,7 +4249,7 @@
"message": "Wybierz dane logowania, do których przypisać klucz dostępu"
},
"chooseCipherForPasskeyAuth": {
- "message": "Wybierz klucz dostępu, żeby się zalogować"
+ "message": "Wybierz klucz dostępu"
},
"passkeyItem": {
"message": "Element klucza dostępu"
@@ -4261,16 +4264,16 @@
"message": "Funkcja nie jest jeszcze obsługiwana"
},
"yourPasskeyIsLocked": {
- "message": "Wymagane uwierzytelnienie, aby używać klucza dostępu. Sprawdź swoją tożsamość, aby kontynuować."
+ "message": "Aby użyć klucza dostępu, wymagane jest uwierzytelnienie. Zweryfikuj swoją tożsamość."
},
"multifactorAuthenticationCancelled": {
- "message": "Uwierzytelnianie wieloskładnikowe zostało anulowane"
+ "message": "Logowanie dwustopniowe zostało anulowane"
},
"noLastPassDataFound": {
"message": "Nie znaleziono danych LastPass"
},
"incorrectUsernameOrPassword": {
- "message": "Nieprawidłowa nazwa użytkownika lub hasło"
+ "message": "Nazwa użytkownika lub hasło są nieprawidłowe"
},
"incorrectPassword": {
"message": "Hasło jest nieprawidłowe"
@@ -4282,7 +4285,7 @@
"message": "Kod PIN jest nieprawidłowy"
},
"multifactorAuthenticationFailed": {
- "message": "Uwierzytelnianie wieloskładnikowe nie powiodło się"
+ "message": "Logowanie dwustopniowe nie powiodło się"
},
"includeSharedFolders": {
"message": "Dołącz udostępnione foldery"
@@ -4294,10 +4297,10 @@
"message": "Importowanie konta..."
},
"lastPassMFARequired": {
- "message": "Wymagane jest uwierzytelnianie wieloskładnikowe LastPass"
+ "message": "Logowanie dwustopniowe LastPass jest wymagane"
},
"lastPassMFADesc": {
- "message": "Wprowadź jednorazowy kod z aplikacji uwierzytelniającej"
+ "message": "Wpisz jednorazowy kod z aplikacji uwierzytelniającej"
},
"lastPassOOBDesc": {
"message": "Zatwierdź żądanie logowania w aplikacji uwierzytelniającej lub wprowadź jednorazowe hasło."
@@ -4309,7 +4312,7 @@
"message": "Hasło główne LastPass"
},
"lastPassAuthRequired": {
- "message": "Wymagane uwierzytelnianie LastPass"
+ "message": "Uwierzytelnianie LastPass jest wymagane"
},
"awaitingSSO": {
"message": "Oczekiwanie na logowanie jednokrotne"
@@ -4328,7 +4331,7 @@
"message": "Importuj z CSV"
},
"lastPassTryAgainCheckEmail": {
- "message": "Spróbuj ponownie lub poszukaj wiadomości e-mail od LastPass, aby zweryfikować, że to Ty."
+ "message": "Spróbuj ponownie lub poszukaj wiadomości od LastPass, aby zweryfikować logowanie."
},
"collection": {
"message": "Kolekcja"
@@ -4373,13 +4376,13 @@
"message": "hostowany w"
},
"useDeviceOrHardwareKey": {
- "message": "Użyj swojego urządzenia lub klucza sprzętowego"
+ "message": "Użyj urządzenia lub klucza sprzętowego"
},
"justOnce": {
"message": "Tylko raz"
},
"alwaysForThisSite": {
- "message": "Zawsze dla tej witryny"
+ "message": "Zawsze dla tej strony"
},
"domainAddedToExcludedDomains": {
"message": "Domena $DOMAIN$ została dodana do wykluczonych domen.",
@@ -4411,7 +4414,7 @@
"description": "Link to match detection docs on warning dialog for advance match strategy"
},
"uriAdvancedOption": {
- "message": "Ustawienia zaawansowane",
+ "message": "Opcje zaawansowane",
"description": "Advanced option placeholder for uri option component"
},
"confirmContinueToBrowserSettingsTitle": {
@@ -4443,7 +4446,7 @@
"description": "Dialog title facilitating the ability to override a chrome browser's default autofill behavior"
},
"overrideDefaultBrowserAutofillDescription": {
- "message": "Ignorowanie tej opcji może spowodować konflikty pomiędzy menu autouzupełniania Bitwarden a przeglądarką.",
+ "message": "Zignorowanie tej opcji może spowodować konflikty pomiędzy autouzupełnianiem Bitwarden a przeglądarką.",
"description": "Dialog message facilitating the ability to override a chrome browser's default autofill behavior"
},
"overrideDefaultBrowserAutoFillSettings": {
@@ -4748,19 +4751,19 @@
"message": "Pobierz Bitwarden"
},
"downloadBitwardenOnAllDevices": {
- "message": "Pobierz Bitwarden na wszystkich urządzeniach"
+ "message": "Bitwarden na inne urządzenia"
},
"getTheMobileApp": {
"message": "Pobierz aplikację mobilną"
},
"getTheMobileAppDesc": {
- "message": "Uzyskaj dostęp do haseł przy pomocy aplikacji mobilnej Bitwarden."
+ "message": "Uzyskaj dostęp do haseł za pomocą aplikacji mobilnej Bitwarden."
},
"getTheDesktopApp": {
"message": "Pobierz aplikację desktopową"
},
"getTheDesktopAppDesc": {
- "message": "Uzyskaj dostęp do sejfu bez przeglądarki, a następnie ustaw odblokowanie biometryczne, aby przyspieszyć odblokowanie zarówno w aplikacji desktopowej, jak i w rozszerzeniu przeglądarki."
+ "message": "Uzyskaj dostęp do sejfu bez przeglądarki. Skonfiguruj biometrię, aby przyśpieszyć odblokowywanie aplikacji."
},
"downloadFromBitwardenNow": {
"message": "Pobierz z bitwarden.com"
@@ -4787,7 +4790,7 @@
"message": "Filtruj sejf"
},
"filterApplied": {
- "message": "Zastosowano jeden filtr"
+ "message": "Zastosowano 1 filtr"
},
"filterAppliedPlural": {
"message": "$COUNT$ filtrów zastosowanych",
@@ -4817,7 +4820,7 @@
}
},
"cardNumberEndsWith": {
- "message": "numer karty kończy się",
+ "message": "numer karty kończący się",
"description": "Used within the inline menu to provide an aria description when users are attempting to fill a card cipher."
},
"loginCredentials": {
@@ -4886,7 +4889,7 @@
"message": "Karta wygasła"
},
"cardExpiredMessage": {
- "message": "Jeśli ją wznowiłeś, zaktualizuj informacje o karcie"
+ "message": "Jeśli karta została odnowiona, zaktualizuj informacje o niej"
},
"cardDetails": {
"message": "Szczegóły karty"
@@ -4924,7 +4927,7 @@
"description": "A section header for a list of passwords."
},
"logInWithPasskeyAriaLabel": {
- "message": "Logowaniem kluczem dostępu",
+ "message": "Logowanie kluczem dostępu",
"description": "ARIA label for the inline menu button that logs in with a passkey."
},
"assign": {
@@ -4964,16 +4967,16 @@
"message": "Użyj pól tekstowych dla danych takich jak pytania bezpieczeństwa"
},
"hiddenHelpText": {
- "message": "Użyj ukrytych pól dla danych poufnych, takich jak hasło"
+ "message": "Użyj ukrytych pól dla danych poufnych takich jak hasło"
},
"checkBoxHelpText": {
- "message": "Użyj pól wyboru, jeśli chcesz automatycznie wypełnić pole wyboru formularza, np. zapamiętaj e-mail"
+ "message": "Użyj pól wyboru, gdy chcesz uzupełnić pole wyboru formularza, np. zapamiętaj adres e-mail"
},
"linkedHelpText": {
"message": "Użyj powiązanego pola, gdy masz problemy z autouzupełnianiem na konkretnej stronie internetowej."
},
"linkedLabelHelpText": {
- "message": "Wprowadź atrybut z HTML'a: id, name, aria-label lub placeholder."
+ "message": "Wpisz identyfikator, nazwę, etykietę lub tekst zastępczy pola HTML."
},
"editField": {
"message": "Edytuj pole"
@@ -5006,7 +5009,7 @@
}
},
"reorderToggleButton": {
- "message": "Zmień kolejność $LABEL$. Użyj klawiszy ze strzałkami, aby przenieść element w górę lub w dół.",
+ "message": "Zmień kolejność pola $LABEL$. Użyj klawiszy ze strzałkami, aby przenieść element w górę lub w dół.",
"placeholders": {
"label": {
"content": "$1",
@@ -5015,10 +5018,10 @@
}
},
"reorderWebsiteUriButton": {
- "message": "Zmień kolejność URI strony. Użyj klawiszy ze strzałkami, aby przenieść element w górę lub w dół."
+ "message": "Zmień kolejność URI stron internetowych. Użyj klawiszy ze strzałkami, aby przenieść element w górę lub w dół."
},
"reorderFieldUp": {
- "message": "$LABEL$ przeniósł się w górę, pozycja $INDEX$ z $LENGTH$",
+ "message": "Pole $LABEL$ zostało przeniesione w górę. Pozycja $INDEX$ z $LENGTH$",
"placeholders": {
"label": {
"content": "$1",
@@ -5075,7 +5078,7 @@
"message": "Przypisano kolekcje"
},
"nothingSelected": {
- "message": "Nie zaznaczyłeś żadnych elementów."
+ "message": "Nie zaznaczono żadnych elementów."
},
"itemsMovedToOrg": {
"message": "Elementy zostały przeniesione do organizacji $ORGNAME$",
@@ -5096,7 +5099,7 @@
}
},
"reorderFieldDown": {
- "message": "$LABEL$ przeniósł się w dół, pozycja $INDEX$ z $LENGTH$",
+ "message": "Pole $LABEL$ zostało przeniesione w dół. Pozycja $INDEX$ z $LENGTH$",
"placeholders": {
"label": {
"content": "$1",
@@ -5140,7 +5143,7 @@
"message": "Domyślny systemu"
},
"enterprisePolicyRequirementsApplied": {
- "message": "Zastosowano wymagania zasady organizacji"
+ "message": "Wymagania zasady organizacji zostały zastosowane"
},
"sshPrivateKey": {
"message": "Klucz prywatny"
@@ -5167,7 +5170,7 @@
"message": "RSA 4096-bit"
},
"retry": {
- "message": "Powtórz"
+ "message": "Spróbuj ponownie"
},
"vaultCustomTimeoutMinimum": {
"message": "Minimalny niestandardowy czas to 1 minuta."
@@ -5176,7 +5179,7 @@
"message": "Dostępna jest dodatkowa zawartość"
},
"fileSavedToDevice": {
- "message": "Plik zapisany na urządzeniu. Zarządzaj plikiem na swoim urządzeniu."
+ "message": "Plik został zapisany na urządzeniu."
},
"showCharacterCount": {
"message": "Pokaż liczbę znaków"
@@ -5200,10 +5203,10 @@
"message": "Przywróć"
},
"deleteForever": {
- "message": "Usuń na zawsze"
+ "message": "Usuń trwale"
},
"noEditPermissions": {
- "message": "Nie masz uprawnień do edycji tego elementu"
+ "message": "Nie masz uprawnień do edycji elementu"
},
"biometricsStatusHelptextUnlockNeeded": {
"message": "Odblokowanie biometrią jest niedostępne, ponieważ najpierw wymagane jest odblokowanie kodem PIN lub hasłem."
@@ -5410,10 +5413,10 @@
"message": "Szerokość rozszerzenia"
},
"wide": {
- "message": "Szerokie"
+ "message": "Szeroka"
},
"extraWide": {
- "message": "Bardzo szerokie"
+ "message": "Bardzo szeroka"
},
"sshKeyWrongPassword": {
"message": "Hasło jest nieprawidłowe."
@@ -5455,16 +5458,16 @@
"message": "Zaktualizuj aplikację desktopową"
},
"updateDesktopAppOrDisableFingerprintDialogMessage": {
- "message": "Aby używać odblokowywania biometrycznego, zaktualizuj aplikację na komputerze lub wyłącz odblokowywanie odciskiem palca w ustawieniach aplikacji na komputerze."
+ "message": "Aby używać biometrii, zaktualizuj aplikację desktopową."
},
"changeAtRiskPassword": {
"message": "Zmień zagrożone hasło"
},
"settingsVaultOptions": {
- "message": "Ustawienia Sejfu"
+ "message": "Opcje sejfu"
},
"emptyVaultDescription": {
- "message": "Sejf chroni nie tylko Twoje hasła. Przechowuj tutaj bezpiecznie loginy, identyfikatory, karty i notatki."
+ "message": "Sejf chroni nie tylko hasła. Przechowuj bezpiecznie dane logowania, identyfikatory, karty i notatki."
},
"introCarouselLabel": {
"message": "Witaj w Bitwarden"
@@ -5509,13 +5512,13 @@
"message": "Witaj w sejfie!"
},
"hasItemsVaultNudgeBodyOne": {
- "message": "Autouzupełnianie elementów dla bieżącej strony"
+ "message": "Uzupełniaj elementy na stronie internetowej"
},
"hasItemsVaultNudgeBodyTwo": {
- "message": "Ulubione elementy dla szybkiego dostępu"
+ "message": "Dodawaj do ulubionych wybrane elementy"
},
"hasItemsVaultNudgeBodyThree": {
- "message": "Przeszukaj sejf w poszukiwaniu czegoś innego"
+ "message": "Przeszukuj sejf w poszukiwaniu czegoś innego"
},
"newLoginNudgeTitle": {
"message": "Oszczędzaj czas dzięki autouzupełnianiu"
@@ -5584,7 +5587,7 @@
"description": "Aria label for the body content of the generator nudge"
},
"noPermissionsViewPage": {
- "message": "Nie masz uprawnień do przeglądania tej strony. Spróbuj zalogować się na inne konto."
+ "message": "Nie masz uprawnień do przeglądania tej strony. Zaloguj się na inne konto."
},
"wasmNotSupported": {
"message": "WebAssembly nie jest obsługiwany w przeglądarce lub jest wyłączony. WebAssembly jest wymagany do korzystania z aplikacji Bitwarden.",
diff --git a/apps/browser/src/_locales/pt_BR/messages.json b/apps/browser/src/_locales/pt_BR/messages.json
index 7d5509a628b..46fbb9beca3 100644
--- a/apps/browser/src/_locales/pt_BR/messages.json
+++ b/apps/browser/src/_locales/pt_BR/messages.json
@@ -547,6 +547,9 @@
"searchVault": {
"message": "Pesquisar no Cofre"
},
+ "resetSearch": {
+ "message": "Reset search"
+ },
"edit": {
"message": "Editar"
},
diff --git a/apps/browser/src/_locales/pt_PT/messages.json b/apps/browser/src/_locales/pt_PT/messages.json
index 1e77e1c3035..3cd813847d1 100644
--- a/apps/browser/src/_locales/pt_PT/messages.json
+++ b/apps/browser/src/_locales/pt_PT/messages.json
@@ -547,6 +547,9 @@
"searchVault": {
"message": "Procurar no cofre"
},
+ "resetSearch": {
+ "message": "Repor pesquisa"
+ },
"edit": {
"message": "Editar"
},
diff --git a/apps/browser/src/_locales/ro/messages.json b/apps/browser/src/_locales/ro/messages.json
index 7f54640af25..13fe8aa9482 100644
--- a/apps/browser/src/_locales/ro/messages.json
+++ b/apps/browser/src/_locales/ro/messages.json
@@ -398,7 +398,7 @@
"message": "Numele folderului"
},
"folderHintText": {
- "message": "Nest a folder by adding the parent folder's name followed by a “/”. Example: Social/Forums"
+ "message": "Grupează un folder prin adăugarea numelui folderului părinte urmat de \"/\" Exemplu: Social/Forums"
},
"noFoldersAdded": {
"message": "No folders added"
@@ -547,6 +547,9 @@
"searchVault": {
"message": "Căutare în seif"
},
+ "resetSearch": {
+ "message": "Reset search"
+ },
"edit": {
"message": "Editare"
},
@@ -887,7 +890,7 @@
"message": "Follow the steps below to finish logging in."
},
"followTheStepsBelowToFinishLoggingInWithSecurityKey": {
- "message": "Follow the steps below to finish logging in with your security key."
+ "message": "Urmează pașii de mai jos pentru a finaliza autentificarea cu cheia de securitate."
},
"restartRegistration": {
"message": "Reporniți înregistrarea"
@@ -929,7 +932,7 @@
"message": "Make your account more secure by setting up two-step login in the Bitwarden web app."
},
"twoStepLoginConfirmationTitle": {
- "message": "Continue to web app?"
+ "message": "Continuați în aplicația web?"
},
"editedFolder": {
"message": "Dosar salvat"
@@ -1046,7 +1049,7 @@
"message": "Click items to autofill on Vault view"
},
"clickToAutofill": {
- "message": "Click items in autofill suggestion to fill"
+ "message": "Faceți clic pe elementele din sugestia de completare automată pentru a completa"
},
"clearClipboard": {
"message": "Golire clipboard",
diff --git a/apps/browser/src/_locales/ru/messages.json b/apps/browser/src/_locales/ru/messages.json
index db236cbfcc8..a5a6809a059 100644
--- a/apps/browser/src/_locales/ru/messages.json
+++ b/apps/browser/src/_locales/ru/messages.json
@@ -547,6 +547,9 @@
"searchVault": {
"message": "Поиск в хранилище"
},
+ "resetSearch": {
+ "message": "Сбросить поиск"
+ },
"edit": {
"message": "Изменить"
},
@@ -3795,7 +3798,7 @@
"message": "В целях обеспечения безопасности вашего аккаунта продолжайте только в том случае, если вы являетесь членом этой организации, у вас включено восстановление аккаунта, а отображаемый ниже отпечаток совпадает с отпечатком организации."
},
"orgTrustWarning1": {
- "message": "В этой организации действует политика, которая позволит вам участвовать в восстановлении аккаунта. Регистрация позволит администраторам организации изменить ваш пароль. Продолжайте, только если вы знаете эту организацию и фраза отпечатков, показанная ниже, совпадает с отпечатками организации."
+ "message": "Эта организация имеет корпоративную политику, которая зарегистрирует вас в системе восстановления аккаунта. Регистрация позволит администраторам организации изменить ваш пароль. Продолжайте только в том случае, если вы узнаете эту организацию и фраза отпечатка, отображаемая ниже, соответствует отпечатку организации."
},
"trustUser": {
"message": "Доверенный пользователь"
@@ -5242,7 +5245,7 @@
"message": "Установить PIN--код разблокировки"
},
"unlockWithBiometricSet": {
- "message": "Разблокировать с помощью биометрии"
+ "message": "Разблокировка с помощью биометрии настроена"
},
"authenticating": {
"message": "Аутентификация"
diff --git a/apps/browser/src/_locales/si/messages.json b/apps/browser/src/_locales/si/messages.json
index 13e6c2522bf..7bc0ba2694a 100644
--- a/apps/browser/src/_locales/si/messages.json
+++ b/apps/browser/src/_locales/si/messages.json
@@ -547,6 +547,9 @@
"searchVault": {
"message": "සුරක්ෂිතාගාරය සොයන්න"
},
+ "resetSearch": {
+ "message": "Reset search"
+ },
"edit": {
"message": "සංස්කරණය"
},
diff --git a/apps/browser/src/_locales/sk/messages.json b/apps/browser/src/_locales/sk/messages.json
index e98c643edb9..28a687be339 100644
--- a/apps/browser/src/_locales/sk/messages.json
+++ b/apps/browser/src/_locales/sk/messages.json
@@ -547,6 +547,9 @@
"searchVault": {
"message": "Prehľadávať trezor"
},
+ "resetSearch": {
+ "message": "Resetovať vyhľadávanie"
+ },
"edit": {
"message": "Upraviť"
},
diff --git a/apps/browser/src/_locales/sl/messages.json b/apps/browser/src/_locales/sl/messages.json
index 397b7be54e8..2db40266cd7 100644
--- a/apps/browser/src/_locales/sl/messages.json
+++ b/apps/browser/src/_locales/sl/messages.json
@@ -547,6 +547,9 @@
"searchVault": {
"message": "Išči v trezorju"
},
+ "resetSearch": {
+ "message": "Reset search"
+ },
"edit": {
"message": "Uredi"
},
diff --git a/apps/browser/src/_locales/sr/messages.json b/apps/browser/src/_locales/sr/messages.json
index f68d0b97447..3ee18612553 100644
--- a/apps/browser/src/_locales/sr/messages.json
+++ b/apps/browser/src/_locales/sr/messages.json
@@ -547,6 +547,9 @@
"searchVault": {
"message": "Претражи сеф"
},
+ "resetSearch": {
+ "message": "Ресетовати претрагу"
+ },
"edit": {
"message": "Уреди"
},
@@ -1830,7 +1833,7 @@
"message": "Сигурносни код"
},
"cardNumber": {
- "message": "card number"
+ "message": "број картице"
},
"ex": {
"message": "нпр."
@@ -3464,7 +3467,7 @@
"message": "Захтев је послат"
},
"loginRequestApprovedForEmailOnDevice": {
- "message": "Login request approved for $EMAIL$ on $DEVICE$",
+ "message": "Захтев за пријаву одобрен за $EMAIL$ на $DEVICE$",
"placeholders": {
"email": {
"content": "$1",
@@ -3477,13 +3480,13 @@
}
},
"youDeniedLoginAttemptFromAnotherDevice": {
- "message": "You denied a login attempt from another device. If this was you, try to log in with the device again."
+ "message": "Одбили сте покушај пријаве са другог уређаја. Ако сте то били ви, покушајте поново да се пријавите помоћу уређаја."
},
"device": {
- "message": "Device"
+ "message": "Уређај"
},
"loginStatus": {
- "message": "Login status"
+ "message": "Статус пријаве"
},
"masterPasswordChanged": {
"message": "Главна лозинка сачувана"
@@ -3582,28 +3585,28 @@
"message": "Запамтити овај уређај да би будуће пријаве биле беспрекорне"
},
"manageDevices": {
- "message": "Manage devices"
+ "message": "Управљање уређајима"
},
"currentSession": {
- "message": "Current session"
+ "message": "Тренутна сесија"
},
"mobile": {
- "message": "Mobile",
+ "message": "Мобилни",
"description": "Mobile app"
},
"extension": {
- "message": "Extension",
+ "message": "Додатак",
"description": "Browser extension/addon"
},
"desktop": {
- "message": "Desktop",
+ "message": "Десктоп",
"description": "Desktop app"
},
"webVault": {
- "message": "Web vault"
+ "message": "Интернет Сеф"
},
"webApp": {
- "message": "Web app"
+ "message": "Веб апликација"
},
"cli": {
"message": "CLI"
@@ -3613,22 +3616,22 @@
"description": "Software Development Kit"
},
"requestPending": {
- "message": "Request pending"
+ "message": "Захтев је на чекању"
},
"firstLogin": {
- "message": "First login"
+ "message": "Прва пријава"
},
"trusted": {
- "message": "Trusted"
+ "message": "Поуздан"
},
"needsApproval": {
- "message": "Needs approval"
+ "message": "Потребно је одобрење"
},
"devices": {
- "message": "Devices"
+ "message": "Уређаји"
},
"accessAttemptBy": {
- "message": "Access attempt by $EMAIL$",
+ "message": "Покушај приступа са $EMAIL$",
"placeholders": {
"email": {
"content": "$1",
@@ -3637,28 +3640,28 @@
}
},
"confirmAccess": {
- "message": "Confirm access"
+ "message": "Потврди приступ"
},
"denyAccess": {
- "message": "Deny access"
+ "message": "Одбиј приступ"
},
"time": {
- "message": "Time"
+ "message": "Време"
},
"deviceType": {
- "message": "Device Type"
+ "message": "Тип уређаја"
},
"loginRequest": {
- "message": "Login request"
+ "message": "Захтев за пријаву"
},
"thisRequestIsNoLongerValid": {
- "message": "This request is no longer valid."
+ "message": "Овај захтев више није важећи."
},
"areYouTryingToAccessYourAccount": {
- "message": "Are you trying to access your account?"
+ "message": "Да ли покушавате да приступите вашем налогу?"
},
"logInConfirmedForEmailOnDevice": {
- "message": "Login confirmed for $EMAIL$ on $DEVICE$",
+ "message": "Пријава потврђена за $EMAIL$ на $DEVICE$",
"placeholders": {
"email": {
"content": "$1",
@@ -3671,16 +3674,16 @@
}
},
"youDeniedALogInAttemptFromAnotherDevice": {
- "message": "You denied a login attempt from another device. If this really was you, try to log in with the device again."
+ "message": "Одбили сте покушај пријаве са другог уређаја. Ако сте то заиста били ви, покушајте поново да се пријавите помоћу уређаја."
},
"loginRequestHasAlreadyExpired": {
- "message": "Login request has already expired."
+ "message": "Захтев за пријаву је већ истекао."
},
"justNow": {
- "message": "Just now"
+ "message": "Управо сада"
},
"requestedXMinutesAgo": {
- "message": "Requested $MINUTES$ minutes ago",
+ "message": "Затражено пре $MINUTES$ минута",
"placeholders": {
"minutes": {
"content": "$1",
@@ -4598,7 +4601,7 @@
}
},
"copyFieldCipherName": {
- "message": "Copy $FIELD$, $CIPHERNAME$",
+ "message": "Копирај $FIELD$, $CIPHERNAME$",
"description": "Title for a button that copies a field value to the clipboard.",
"placeholders": {
"field": {
diff --git a/apps/browser/src/_locales/sv/messages.json b/apps/browser/src/_locales/sv/messages.json
index cbfc3e478f5..49d2765bf6e 100644
--- a/apps/browser/src/_locales/sv/messages.json
+++ b/apps/browser/src/_locales/sv/messages.json
@@ -547,6 +547,9 @@
"searchVault": {
"message": "Sök i valvet"
},
+ "resetSearch": {
+ "message": "Nollställ sökning"
+ },
"edit": {
"message": "Redigera"
},
diff --git a/apps/browser/src/_locales/te/messages.json b/apps/browser/src/_locales/te/messages.json
index 9a6d9a4d316..f4c58318bc1 100644
--- a/apps/browser/src/_locales/te/messages.json
+++ b/apps/browser/src/_locales/te/messages.json
@@ -547,6 +547,9 @@
"searchVault": {
"message": "Search vault"
},
+ "resetSearch": {
+ "message": "Reset search"
+ },
"edit": {
"message": "Edit"
},
diff --git a/apps/browser/src/_locales/th/messages.json b/apps/browser/src/_locales/th/messages.json
index 49515eb1c64..fd92c71c200 100644
--- a/apps/browser/src/_locales/th/messages.json
+++ b/apps/browser/src/_locales/th/messages.json
@@ -547,6 +547,9 @@
"searchVault": {
"message": "ค้นหาในตู้นิรภัย"
},
+ "resetSearch": {
+ "message": "Reset search"
+ },
"edit": {
"message": "แก้ไข"
},
diff --git a/apps/browser/src/_locales/tr/messages.json b/apps/browser/src/_locales/tr/messages.json
index cd7c8d3f0b6..83265497ddf 100644
--- a/apps/browser/src/_locales/tr/messages.json
+++ b/apps/browser/src/_locales/tr/messages.json
@@ -462,13 +462,13 @@
"message": "Parola oluştur"
},
"generatePassphrase": {
- "message": "Parola üret"
+ "message": "Parola cümlesi üret"
},
"passwordGenerated": {
"message": "Parola üretildi"
},
"passphraseGenerated": {
- "message": "Parola ifadesi oluşturuldu"
+ "message": "Parola cümlesi üretildi"
},
"usernameGenerated": {
"message": "Kullanıcı adı üretildi"
@@ -547,6 +547,9 @@
"searchVault": {
"message": "Kasada ara"
},
+ "resetSearch": {
+ "message": "Aramayı sıfırla"
+ },
"edit": {
"message": "Düzenle"
},
@@ -569,7 +572,7 @@
"message": "Kimlik doğrulama sırrı"
},
"passphrase": {
- "message": "Uzun söz"
+ "message": "Parola cümlesi"
},
"favorite": {
"message": "Favori"
@@ -2217,7 +2220,7 @@
"message": "Bu parolayı kullan"
},
"useThisPassphrase": {
- "message": "Bu parola ifadesini kullanın"
+ "message": "Bu parola cümlesini kullan"
},
"useThisUsername": {
"message": "Bu kullanıcı adını kullan"
@@ -3180,7 +3183,7 @@
}
},
"passphraseNumWordsRecommendationHint": {
- "message": "Güçlü bir parola ifadesi oluşturmak için $RECOMMENDED$ veya daha fazla kelime kullanın.",
+ "message": " Güçlü bir parola cümlesi üretmek için en az $RECOMMENDED$ kelime kullanın.",
"description": "Appended to `spinboxBoundariesHint` to recommend a number of words to the user. This must include any language-specific 'sentence' separator characters (e.g. a space in english).",
"placeholders": {
"recommended": {
diff --git a/apps/browser/src/_locales/uk/messages.json b/apps/browser/src/_locales/uk/messages.json
index 083d89fbd12..a03440efe02 100644
--- a/apps/browser/src/_locales/uk/messages.json
+++ b/apps/browser/src/_locales/uk/messages.json
@@ -547,6 +547,9 @@
"searchVault": {
"message": "Пошук"
},
+ "resetSearch": {
+ "message": "Скинути пошук"
+ },
"edit": {
"message": "Змінити"
},
@@ -1830,7 +1833,7 @@
"message": "Код безпеки"
},
"cardNumber": {
- "message": "card number"
+ "message": "номер картки"
},
"ex": {
"message": "зразок"
@@ -3464,7 +3467,7 @@
"message": "Запит надіслано"
},
"loginRequestApprovedForEmailOnDevice": {
- "message": "Login request approved for $EMAIL$ on $DEVICE$",
+ "message": "Запит входу підтверджено для $EMAIL$ на $DEVICE$",
"placeholders": {
"email": {
"content": "$1",
@@ -3477,13 +3480,13 @@
}
},
"youDeniedLoginAttemptFromAnotherDevice": {
- "message": "You denied a login attempt from another device. If this was you, try to log in with the device again."
+ "message": "Ви відхилили спробу входу з іншого пристрою. Якщо це були ви, спробуйте ввійти з пристроєм знову."
},
"device": {
- "message": "Device"
+ "message": "Пристрій"
},
"loginStatus": {
- "message": "Login status"
+ "message": "Стан входу в систему"
},
"masterPasswordChanged": {
"message": "Головний пароль збережено"
@@ -3582,28 +3585,28 @@
"message": "Запам'ятайте цей пристрій, щоб спростити майбутні входи в систему"
},
"manageDevices": {
- "message": "Manage devices"
+ "message": "Керувати пристроями"
},
"currentSession": {
- "message": "Current session"
+ "message": "Поточний сеанс"
},
"mobile": {
- "message": "Mobile",
+ "message": "Мобільний",
"description": "Mobile app"
},
"extension": {
- "message": "Extension",
+ "message": "Розширення",
"description": "Browser extension/addon"
},
"desktop": {
- "message": "Desktop",
+ "message": "Комп'ютер",
"description": "Desktop app"
},
"webVault": {
- "message": "Web vault"
+ "message": "Вебсховище"
},
"webApp": {
- "message": "Web app"
+ "message": "Вебпрограма"
},
"cli": {
"message": "CLI"
@@ -3613,22 +3616,22 @@
"description": "Software Development Kit"
},
"requestPending": {
- "message": "Request pending"
+ "message": "Запит в очікуванні"
},
"firstLogin": {
- "message": "First login"
+ "message": "Перший вхід"
},
"trusted": {
- "message": "Trusted"
+ "message": "Надійний"
},
"needsApproval": {
- "message": "Needs approval"
+ "message": "Потребує підтвердження"
},
"devices": {
- "message": "Devices"
+ "message": "Пристрої"
},
"accessAttemptBy": {
- "message": "Access attempt by $EMAIL$",
+ "message": "Спроба доступу з $EMAIL$",
"placeholders": {
"email": {
"content": "$1",
@@ -3637,28 +3640,28 @@
}
},
"confirmAccess": {
- "message": "Confirm access"
+ "message": "Підтвердити доступ"
},
"denyAccess": {
- "message": "Deny access"
+ "message": "Заборонити доступ"
},
"time": {
- "message": "Time"
+ "message": "Час"
},
"deviceType": {
- "message": "Device Type"
+ "message": "Тип пристрою"
},
"loginRequest": {
- "message": "Login request"
+ "message": "Запит входу"
},
"thisRequestIsNoLongerValid": {
- "message": "This request is no longer valid."
+ "message": "Цей запит більше недійсний."
},
"areYouTryingToAccessYourAccount": {
- "message": "Are you trying to access your account?"
+ "message": "Ви намагаєтесь отримати доступ до свого облікового запису?"
},
"logInConfirmedForEmailOnDevice": {
- "message": "Login confirmed for $EMAIL$ on $DEVICE$",
+ "message": "Підтверджено вхід для $EMAIL$ на $DEVICE$",
"placeholders": {
"email": {
"content": "$1",
@@ -3671,16 +3674,16 @@
}
},
"youDeniedALogInAttemptFromAnotherDevice": {
- "message": "You denied a login attempt from another device. If this really was you, try to log in with the device again."
+ "message": "Ви відхилили спробу входу з іншого пристрою. Якщо це були дійсно ви, спробуйте ввійти з пристроєм знову."
},
"loginRequestHasAlreadyExpired": {
- "message": "Login request has already expired."
+ "message": "Термін дії запиту на вхід завершився."
},
"justNow": {
- "message": "Just now"
+ "message": "Щойно"
},
"requestedXMinutesAgo": {
- "message": "Requested $MINUTES$ minutes ago",
+ "message": "Запитано $MINUTES$ хвилин тому",
"placeholders": {
"minutes": {
"content": "$1",
@@ -4598,7 +4601,7 @@
}
},
"copyFieldCipherName": {
- "message": "Copy $FIELD$, $CIPHERNAME$",
+ "message": "Копіювати $FIELD$, $CIPHERNAME$",
"description": "Title for a button that copies a field value to the clipboard.",
"placeholders": {
"field": {
diff --git a/apps/browser/src/_locales/vi/messages.json b/apps/browser/src/_locales/vi/messages.json
index b5de1c2981c..f25cc9c51dc 100644
--- a/apps/browser/src/_locales/vi/messages.json
+++ b/apps/browser/src/_locales/vi/messages.json
@@ -453,7 +453,7 @@
"message": "Ứng dụng web Bitwarden"
},
"importItems": {
- "message": "Nhập mục"
+ "message": "Nhập vào kho"
},
"select": {
"message": "Chọn"
@@ -547,6 +547,9 @@
"searchVault": {
"message": "Tìm kiếm trong kho lưu trữ"
},
+ "resetSearch": {
+ "message": "Đặt lại tìm kiếm"
+ },
"edit": {
"message": "Sửa"
},
@@ -1013,16 +1016,16 @@
"description": "This is the folder for uncategorized items"
},
"enableAddLoginNotification": {
- "message": "Hỏi để thêm đăng nhập"
+ "message": "Gợi ý thêm mục đăng nhập"
},
"vaultSaveOptionsTitle": {
"message": "Tùy chọn lưu vào kho"
},
"addLoginNotificationDesc": {
- "message": "Nếu không tìm thấy mục nào trong kho của bạn, hãy yêu cầu thêm mục đó."
+ "message": "Gợi ý thêm mục nếu không tìm thấy mục nào trong kho trùng khớp."
},
"addLoginNotificationDescAlt": {
- "message": "Nếu không tìm thấy mục nào trong kho của bạn, hãy yêu cầu thêm mục đó. Áp dụng cho tất cả tài khoản đã đăng nhập."
+ "message": "Gợi ý thêm mục nếu không tìm thấy mục nào trong kho trùng khớp. Áp dụng cho tất cả tài khoản đã đăng nhập."
},
"showCardsInVaultViewV2": {
"message": "Luôn hiển thị thẻ như đề xuất tự động điền trên giao diện kho"
@@ -1223,7 +1226,7 @@
"description": "Default URI match detection for autofill."
},
"defaultUriMatchDetectionDesc": {
- "message": "Chọn cách thức mặc định hệ thống so khớp đường dẫn (URI) để xử lý đăng nhập khi thực hiện các thao tác như tự động điền."
+ "message": "Chọn cách hệ thống so khớp đường dẫn để xử lý đăng nhập khi thực hiện các thao tác như tự động điền."
},
"theme": {
"message": "Chủ đề"
@@ -1735,7 +1738,7 @@
"message": "Văn bản"
},
"cfTypeHidden": {
- "message": "Đã ẩn đi"
+ "message": "Bí ẩn"
},
"cfTypeBoolean": {
"message": "Đúng/Sai"
@@ -1845,10 +1848,10 @@
"message": "Bà"
},
"ms": {
- "message": "Chị"
+ "message": "Cô"
},
"dr": {
- "message": "Bác sĩ"
+ "message": "Tiến sĩ/Bác sĩ"
},
"mx": {
"message": "Mx"
@@ -1902,7 +1905,7 @@
"message": "Xã / Phường"
},
"stateProvince": {
- "message": "Tỉnh/Thành Phố"
+ "message": "Tỉnh / Thành Phố"
},
"zipPostalCode": {
"message": "Mã bưu chính"
@@ -2109,7 +2112,7 @@
"description": "ex. Date this item was updated"
},
"dateCreated": {
- "message": "Ngày tạo",
+ "message": "Tạo lúc",
"description": "ex. Date this item was created"
},
"datePasswordUpdated": {
@@ -2688,7 +2691,7 @@
"message": "Giới hạn số lượt xem"
},
"limitSendViewsHint": {
- "message": "Không ai có thể xem mục Gửi này sau khi đạt đến giới hạn.",
+ "message": "Không ai có thể xem Send này sau khi đạt đến giới hạn.",
"description": "Displayed under the limit views field on Send"
},
"limitSendViewsCount": {
@@ -2727,7 +2730,7 @@
"description": "This text will be displayed after a Send has been accessed the maximum amount of times."
},
"hideTextByDefault": {
- "message": "Mặc định ẩn văn bản"
+ "message": "Ẩn văn bản theo mặc định"
},
"expired": {
"message": "Đã hết hạn"
@@ -2782,7 +2785,7 @@
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
},
"deletionDate": {
- "message": "Ngày xóa"
+ "message": "Xóa sau"
},
"deletionDateDescV2": {
"message": "Send sẽ được xóa vĩnh viễn vào ngày này.",
@@ -4312,7 +4315,7 @@
"message": "Yêu cầu xác thực LastPass"
},
"awaitingSSO": {
- "message": "Đang chờ xác thực Đăng nhập một lần"
+ "message": "Đang chờ xác thực SSO"
},
"awaitingSSODesc": {
"message": "Vui lòng tiếp tục đăng nhập bằng thông tin đăng nhập của công ty bạn."
@@ -4757,7 +4760,7 @@
"message": "Truy cập mật khẩu của bạn mọi lúc mọi nơi với ứng dụng di động Bitwarden."
},
"getTheDesktopApp": {
- "message": "Tải ứng dụng cho máy tính"
+ "message": "Tải ứng dụng máy tính"
},
"getTheDesktopAppDesc": {
"message": "Truy cập kho lưu trữ của bạn mà không cần trình duyệt, sau đó thiết lập mở khóa bằng sinh trắc học để mở khóa dễ dàng trong cả ứng dụng trên máy tính và tiện ích mở rộng trình duyệt."
@@ -4892,7 +4895,7 @@
"message": "Thông tin thẻ"
},
"cardBrandDetails": {
- "message": "Chi tiết $BRAND$",
+ "message": "Chi tiết thẻ $BRAND$",
"placeholders": {
"brand": {
"content": "$1",
diff --git a/apps/browser/src/_locales/zh_CN/messages.json b/apps/browser/src/_locales/zh_CN/messages.json
index f44265425e9..2382e6fc971 100644
--- a/apps/browser/src/_locales/zh_CN/messages.json
+++ b/apps/browser/src/_locales/zh_CN/messages.json
@@ -547,6 +547,9 @@
"searchVault": {
"message": "搜索密码库"
},
+ "resetSearch": {
+ "message": "重置搜索"
+ },
"edit": {
"message": "编辑"
},
@@ -3477,7 +3480,7 @@
}
},
"youDeniedLoginAttemptFromAnotherDevice": {
- "message": "You denied a login attempt from another device. If this was you, try to log in with the device again."
+ "message": "您拒绝了一个来自其他设备的登录尝试。若确实是您本人,请尝试再次发起设备登录。"
},
"device": {
"message": "设备"
@@ -3585,25 +3588,25 @@
"message": "Manage devices"
},
"currentSession": {
- "message": "Current session"
+ "message": "当前会话"
},
"mobile": {
- "message": "Mobile",
+ "message": "移动端",
"description": "Mobile app"
},
"extension": {
- "message": "Extension",
+ "message": "扩展",
"description": "Browser extension/addon"
},
"desktop": {
- "message": "Desktop",
+ "message": "桌面端",
"description": "Desktop app"
},
"webVault": {
- "message": "Web vault"
+ "message": "网页密码库"
},
"webApp": {
- "message": "Web app"
+ "message": "网页 App"
},
"cli": {
"message": "CLI"
@@ -3613,7 +3616,7 @@
"description": "Software Development Kit"
},
"requestPending": {
- "message": "Request pending"
+ "message": "请求待处理"
},
"firstLogin": {
"message": "First login"
@@ -3671,7 +3674,7 @@
}
},
"youDeniedALogInAttemptFromAnotherDevice": {
- "message": "您拒绝了另一台设备的登录尝试。如果真的是您,请尝试再次使用该设备登录。"
+ "message": "您拒绝了一个来自其他设备的登录尝试。若确实是您本人,请尝试再次发起设备登录。"
},
"loginRequestHasAlreadyExpired": {
"message": "登录请求已过期。"
@@ -3680,7 +3683,7 @@
"message": "Just now"
},
"requestedXMinutesAgo": {
- "message": "Requested $MINUTES$ minutes ago",
+ "message": "请求于 $MINUTES$ 分钟前",
"placeholders": {
"minutes": {
"content": "$1",
@@ -4407,7 +4410,7 @@
"description": "Content for dialog which warns a user when selecting 'starts with' matching strategy as a cipher match strategy"
},
"uriMatchWarningDialogLink": {
- "message": "More about match detection",
+ "message": "更多关于匹配检测",
"description": "Link to match detection docs on warning dialog for advance match strategy"
},
"uriAdvancedOption": {
diff --git a/apps/browser/src/_locales/zh_TW/messages.json b/apps/browser/src/_locales/zh_TW/messages.json
index b41b9271c75..d2776cb227d 100644
--- a/apps/browser/src/_locales/zh_TW/messages.json
+++ b/apps/browser/src/_locales/zh_TW/messages.json
@@ -547,6 +547,9 @@
"searchVault": {
"message": "搜尋密碼庫"
},
+ "resetSearch": {
+ "message": "Reset search"
+ },
"edit": {
"message": "編輯"
},
diff --git a/apps/browser/src/auth/popup/account-switching/current-account.component.html b/apps/browser/src/auth/popup/account-switching/current-account.component.html
index f59a2b08fdd..09342c58756 100644
--- a/apps/browser/src/auth/popup/account-switching/current-account.component.html
+++ b/apps/browser/src/auth/popup/account-switching/current-account.component.html
@@ -1,4 +1,4 @@
-
+
-
-
-
-
-
-
-
-
- {{ "orgPermissionsUpdatedMustSetPassword" | i18n }}
-
-
-
- {{ "orgRequiresYouToSetPassword" | i18n }}
-
-
-
- {{ "resetPasswordAutoEnrollInviteWarning" | i18n }}
-
-
-
-
-
-
-
-
-
-
- {{ "masterPass" | i18n }}
-
- {{ text }}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {{ "reTypeMasterPass" | i18n }}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {{ "masterPassHint" | i18n }}
-
-
-
-
-
-
-
-
diff --git a/apps/browser/src/auth/popup/set-password.component.ts b/apps/browser/src/auth/popup/set-password.component.ts
deleted file mode 100644
index 2a796854531..00000000000
--- a/apps/browser/src/auth/popup/set-password.component.ts
+++ /dev/null
@@ -1,10 +0,0 @@
-import { Component } from "@angular/core";
-
-import { SetPasswordComponent as BaseSetPasswordComponent } from "@bitwarden/angular/auth/components/set-password.component";
-
-@Component({
- selector: "app-set-password",
- templateUrl: "set-password.component.html",
- standalone: false,
-})
-export class SetPasswordComponent extends BaseSetPasswordComponent {}
diff --git a/apps/browser/src/auth/popup/settings/account-security.component.spec.ts b/apps/browser/src/auth/popup/settings/account-security.component.spec.ts
index b50e1f55032..014f2a7638b 100644
--- a/apps/browser/src/auth/popup/settings/account-security.component.spec.ts
+++ b/apps/browser/src/auth/popup/settings/account-security.component.spec.ts
@@ -5,7 +5,6 @@ import { mock } from "jest-mock-extended";
import { firstValueFrom, of } from "rxjs";
import { CollectionService } from "@bitwarden/admin-console/common";
-import { PinServiceAbstraction } from "@bitwarden/auth/common";
import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
@@ -13,6 +12,7 @@ import { PolicyType } from "@bitwarden/common/admin-console/enums";
import { Policy } from "@bitwarden/common/admin-console/models/domain/policy";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction";
+import { PinServiceAbstraction } from "@bitwarden/common/key-management/pin/pin.service.abstraction";
import {
VaultTimeoutSettingsService,
VaultTimeoutService,
diff --git a/apps/browser/src/auth/popup/settings/account-security.component.ts b/apps/browser/src/auth/popup/settings/account-security.component.ts
index 6c072532a5d..b41cfe14c4f 100644
--- a/apps/browser/src/auth/popup/settings/account-security.component.ts
+++ b/apps/browser/src/auth/popup/settings/account-security.component.ts
@@ -25,7 +25,6 @@ import { JslibModule } from "@bitwarden/angular/jslib.module";
import { NudgesService, NudgeType } from "@bitwarden/angular/vault";
import { SpotlightComponent } from "@bitwarden/angular/vault/components/spotlight/spotlight.component";
import { FingerprintDialogComponent, VaultTimeoutInputComponent } from "@bitwarden/auth/angular";
-import { PinServiceAbstraction } from "@bitwarden/auth/common";
import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
import { PolicyType } from "@bitwarden/common/admin-console/enums";
import { getFirstPolicy } from "@bitwarden/common/admin-console/services/policy/default-policy.service";
@@ -33,6 +32,7 @@ import { AccountService } from "@bitwarden/common/auth/abstractions/account.serv
import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction";
import { getUserId } from "@bitwarden/common/auth/services/account.service";
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
+import { PinServiceAbstraction } from "@bitwarden/common/key-management/pin/pin.service.abstraction";
import {
VaultTimeout,
VaultTimeoutAction,
diff --git a/apps/browser/src/auth/popup/settings/vault-timeout-input.component.html b/apps/browser/src/auth/popup/settings/vault-timeout-input.component.html
deleted file mode 100644
index c62f29130bf..00000000000
--- a/apps/browser/src/auth/popup/settings/vault-timeout-input.component.html
+++ /dev/null
@@ -1,44 +0,0 @@
-
-
- {{ "vaultTimeout" | i18n }}
-
- {{ o.name }}
-
-
-
-
-
-
- {{ "hours" | i18n }}
-
-
-
-
-
- {{ "minutes" | i18n }}
-
-
-
-
-
-
diff --git a/apps/browser/src/auth/popup/settings/vault-timeout-input.component.ts b/apps/browser/src/auth/popup/settings/vault-timeout-input.component.ts
deleted file mode 100644
index 25a4d01333d..00000000000
--- a/apps/browser/src/auth/popup/settings/vault-timeout-input.component.ts
+++ /dev/null
@@ -1,23 +0,0 @@
-import { Component } from "@angular/core";
-import { NG_VALIDATORS, NG_VALUE_ACCESSOR } from "@angular/forms";
-
-import { VaultTimeoutInputComponent as VaultTimeoutInputComponentBase } from "@bitwarden/auth/angular";
-
-@Component({
- selector: "app-vault-timeout-input",
- templateUrl: "vault-timeout-input.component.html",
- providers: [
- {
- provide: NG_VALUE_ACCESSOR,
- multi: true,
- useExisting: VaultTimeoutInputComponent,
- },
- {
- provide: NG_VALIDATORS,
- multi: true,
- useExisting: VaultTimeoutInputComponent,
- },
- ],
- standalone: false,
-})
-export class VaultTimeoutInputComponent extends VaultTimeoutInputComponentBase {}
diff --git a/apps/browser/src/auth/popup/update-temp-password.component.html b/apps/browser/src/auth/popup/update-temp-password.component.html
deleted file mode 100644
index 0ce82aa20cf..00000000000
--- a/apps/browser/src/auth/popup/update-temp-password.component.html
+++ /dev/null
@@ -1,142 +0,0 @@
-
diff --git a/apps/browser/src/auth/popup/update-temp-password.component.ts b/apps/browser/src/auth/popup/update-temp-password.component.ts
deleted file mode 100644
index e8cf64b7548..00000000000
--- a/apps/browser/src/auth/popup/update-temp-password.component.ts
+++ /dev/null
@@ -1,30 +0,0 @@
-import { Component } from "@angular/core";
-import { firstValueFrom } from "rxjs";
-
-import { UpdateTempPasswordComponent as BaseUpdateTempPasswordComponent } from "@bitwarden/angular/auth/components/update-temp-password.component";
-
-import { postLogoutMessageListener$ } from "./utils/post-logout-message-listener";
-
-@Component({
- selector: "app-update-temp-password",
- templateUrl: "update-temp-password.component.html",
- standalone: false,
-})
-export class UpdateTempPasswordComponent extends BaseUpdateTempPasswordComponent {
- onSuccessfulChangePassword: () => Promise = this.doOnSuccessfulChangePassword.bind(this);
-
- private async doOnSuccessfulChangePassword() {
- // start listening for "switchAccountFinish" or "doneLoggingOut"
- const messagePromise = firstValueFrom(postLogoutMessageListener$);
- this.messagingService.send("logout");
- // wait for messages
- const command = await messagePromise;
-
- // doneLoggingOut already has a message handler that will navigate us
- if (command === "switchAccountFinish") {
- // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
- // eslint-disable-next-line @typescript-eslint/no-floating-promises
- this.router.navigate(["/"]);
- }
- }
-}
diff --git a/apps/browser/src/autofill/background/abstractions/notification.background.ts b/apps/browser/src/autofill/background/abstractions/notification.background.ts
index 9c9c5c0e243..f2152b44862 100644
--- a/apps/browser/src/autofill/background/abstractions/notification.background.ts
+++ b/apps/browser/src/autofill/background/abstractions/notification.background.ts
@@ -1,9 +1,6 @@
import { NeverDomains } from "@bitwarden/common/models/domain/domain-service";
import { ServerConfig } from "@bitwarden/common/platform/abstractions/config/server-config";
-import { UserId } from "@bitwarden/common/types/guid";
-import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
import { FolderView } from "@bitwarden/common/vault/models/view/folder.view";
-import { SecurityTask } from "@bitwarden/common/vault/tasks";
import { CollectionView } from "../../content/components/common-types";
import { NotificationQueueMessageTypes } from "../../enums/notification-queue-message-type.enum";
@@ -60,23 +57,10 @@ type LockedVaultPendingNotificationsData = {
target: string;
};
-type AtRiskPasswordNotificationsData = {
- activeUserId: UserId;
- cipher: CipherView;
- securityTask: SecurityTask;
- uri: string;
-};
-
type AdjustNotificationBarMessageData = {
height: number;
};
-type ChangePasswordMessageData = {
- currentPassword: string;
- newPassword: string;
- url: string;
-};
-
type AddLoginMessageData = {
username: string;
password: string;
@@ -92,10 +76,7 @@ type NotificationBackgroundExtensionMessage = {
command: string;
data?: Partial &
Partial &
- Partial &
- Partial &
- Partial;
- login?: AddLoginMessageData;
+ Partial;
folder?: string;
edit?: boolean;
details?: AutofillPageDetails;
@@ -121,18 +102,6 @@ type NotificationBackgroundExtensionMessageHandlers = {
bgCloseNotificationBar: ({ message, sender }: BackgroundOnMessageHandlerParams) => Promise;
bgOpenAtRiskPasswords: ({ message, sender }: BackgroundOnMessageHandlerParams) => Promise;
bgAdjustNotificationBar: ({ message, sender }: BackgroundOnMessageHandlerParams) => Promise;
- bgTriggerAddLoginNotification: ({
- message,
- sender,
- }: BackgroundOnMessageHandlerParams) => Promise;
- bgTriggerChangedPasswordNotification: ({
- message,
- sender,
- }: BackgroundOnMessageHandlerParams) => Promise;
- bgTriggerAtRiskPasswordNotification: ({
- message,
- sender,
- }: BackgroundOnMessageHandlerParams) => Promise;
bgRemoveTabFromNotificationQueue: ({ sender }: BackgroundSenderParam) => void;
bgSaveCipher: ({ message, sender }: BackgroundOnMessageHandlerParams) => void;
bgOpenAddEditVaultItemPopout: ({
@@ -162,7 +131,6 @@ export {
NotificationQueueMessageItem,
LockedVaultPendingNotificationsData,
AdjustNotificationBarMessageData,
- ChangePasswordMessageData,
UnlockVaultMessageData,
AddLoginMessageData,
NotificationBackgroundExtensionMessage,
diff --git a/apps/browser/src/autofill/background/abstractions/overlay-notifications.background.ts b/apps/browser/src/autofill/background/abstractions/overlay-notifications.background.ts
index 0ec6a9ae04a..d446e18b480 100644
--- a/apps/browser/src/autofill/background/abstractions/overlay-notifications.background.ts
+++ b/apps/browser/src/autofill/background/abstractions/overlay-notifications.background.ts
@@ -1,3 +1,6 @@
+import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
+import { SecurityTask } from "@bitwarden/common/vault/tasks";
+
import AutofillPageDetails from "../../models/autofill-page-details";
export type NotificationTypeData = {
@@ -8,6 +11,12 @@ export type NotificationTypeData = {
launchTimestamp?: number;
};
+export type LoginSecurityTaskInfo = {
+ securityTask: SecurityTask;
+ cipher: CipherView;
+ uri: ModifyLoginCipherFormData["uri"];
+};
+
export type WebsiteOriginsWithFields = Map>;
export type ActiveFormSubmissionRequests = Set;
diff --git a/apps/browser/src/autofill/background/abstractions/overlay.background.ts b/apps/browser/src/autofill/background/abstractions/overlay.background.ts
index 5e2b755ad4a..75f2659c9df 100644
--- a/apps/browser/src/autofill/background/abstractions/overlay.background.ts
+++ b/apps/browser/src/autofill/background/abstractions/overlay.background.ts
@@ -245,6 +245,7 @@ export type OverlayBackgroundExtensionMessageHandlers = {
editedCipher: () => void;
deletedCipher: () => void;
bgSaveCipher: () => void;
+ updateOverlayCiphers: () => void;
fido2AbortRequest: ({ message, sender }: BackgroundOnMessageHandlerParams) => void;
};
diff --git a/apps/browser/src/autofill/background/notification.background.spec.ts b/apps/browser/src/autofill/background/notification.background.spec.ts
index 5e7e3ed30f5..7302ae7d705 100644
--- a/apps/browser/src/autofill/background/notification.background.spec.ts
+++ b/apps/browser/src/autofill/background/notification.background.spec.ts
@@ -3,17 +3,18 @@ import { BehaviorSubject, firstValueFrom, of } from "rxjs";
import { CollectionService } from "@bitwarden/admin-console/common";
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
-import { DefaultPolicyService } from "@bitwarden/common/admin-console/services/policy/default-policy.service";
+import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
import { AccountInfo, AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
import { AuthService } from "@bitwarden/common/auth/services/auth.service";
import { ExtensionCommand } from "@bitwarden/common/autofill/constants";
import { DomainSettingsService } from "@bitwarden/common/autofill/services/domain-settings.service";
-import { UserNotificationSettingsService } from "@bitwarden/common/autofill/services/user-notification-settings.service";
+import { UserNotificationSettingsServiceAbstraction } from "@bitwarden/common/autofill/services/user-notification-settings.service";
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
+import { ThemeTypes } from "@bitwarden/common/platform/enums";
import { SelfHostedEnvironment } from "@bitwarden/common/platform/services/default-environment.service";
import { ThemeStateService } from "@bitwarden/common/platform/theming/theme-state.service";
import { UserId } from "@bitwarden/common/types/guid";
@@ -38,6 +39,7 @@ import {
LockedVaultPendingNotificationsData,
NotificationBackgroundExtensionMessage,
} from "./abstractions/notification.background";
+import { ModifyLoginCipherFormData } from "./abstractions/overlay-notifications.background";
import NotificationBackground from "./notification.background";
jest.mock("rxjs", () => {
@@ -58,13 +60,21 @@ describe("NotificationBackground", () => {
const collectionService = mock();
let activeAccountStatusMock$: BehaviorSubject;
let authService: MockProxy;
- const policyService = mock();
+ const policyAppliesToUser$ = new BehaviorSubject(true);
+ const policyService = mock({
+ policyAppliesToUser$: jest.fn().mockReturnValue(policyAppliesToUser$),
+ });
const folderService = mock();
- const userNotificationSettingsService = mock();
+ const enableChangedPasswordPromptMock$ = new BehaviorSubject(true);
+ const userNotificationSettingsService = mock();
+ userNotificationSettingsService.enableChangedPasswordPrompt$ = enableChangedPasswordPromptMock$;
+
const domainSettingsService = mock();
const environmentService = mock();
const logService = mock();
+ const selectedThemeMock$ = new BehaviorSubject(ThemeTypes.Light);
const themeStateService = mock();
+ themeStateService.selectedTheme$ = selectedThemeMock$;
const configService = mock();
const accountService = mock();
const organizationService = mock();
@@ -164,7 +174,7 @@ describe("NotificationBackground", () => {
});
});
- describe("notification bar extension message handlers", () => {
+ describe("notification bar extension message handlers and triggers", () => {
beforeEach(() => {
notificationBackground.init();
});
@@ -283,7 +293,12 @@ describe("NotificationBackground", () => {
let pushAddLoginToQueueSpy: jest.SpyInstance;
let pushChangePasswordToQueueSpy: jest.SpyInstance;
let getAllDecryptedForUrlSpy: jest.SpyInstance;
-
+ const mockModifyLoginCipherFormData: ModifyLoginCipherFormData = {
+ username: "test",
+ password: "password",
+ uri: "https://example.com",
+ newPassword: null,
+ };
beforeEach(() => {
tab = createChromeTabMock();
sender = mock({ tab });
@@ -304,43 +319,34 @@ describe("NotificationBackground", () => {
});
it("skips attempting to add the login if the user is logged out", async () => {
- const message: NotificationBackgroundExtensionMessage = {
- command: "bgTriggerAddLoginNotification",
- login: { username: "test", password: "password", url: "https://example.com" },
- };
+ const data: ModifyLoginCipherFormData = mockModifyLoginCipherFormData;
activeAccountStatusMock$.next(AuthenticationStatus.LoggedOut);
- sendMockExtensionMessage(message, sender);
- await flushPromises();
+ await notificationBackground.triggerAddLoginNotification(data, tab);
expect(getEnableAddedLoginPromptSpy).not.toHaveBeenCalled();
expect(pushAddLoginToQueueSpy).not.toHaveBeenCalled();
});
it("skips attempting to add the login if the login data does not contain a valid url", async () => {
- const message: NotificationBackgroundExtensionMessage = {
- command: "bgTriggerAddLoginNotification",
- login: { username: "test", password: "password", url: "" },
+ const data: ModifyLoginCipherFormData = {
+ ...mockModifyLoginCipherFormData,
+ uri: "",
};
activeAccountStatusMock$.next(AuthenticationStatus.Locked);
- sendMockExtensionMessage(message, sender);
- await flushPromises();
+ await notificationBackground.triggerAddLoginNotification(data, tab);
expect(getEnableAddedLoginPromptSpy).not.toHaveBeenCalled();
expect(pushAddLoginToQueueSpy).not.toHaveBeenCalled();
});
it("skips attempting to add the login if the user with a locked vault has disabled the login notification", async () => {
- const message: NotificationBackgroundExtensionMessage = {
- command: "bgTriggerAddLoginNotification",
- login: { username: "test", password: "password", url: "https://example.com" },
- };
+ const data: ModifyLoginCipherFormData = mockModifyLoginCipherFormData;
activeAccountStatusMock$.next(AuthenticationStatus.Locked);
getEnableAddedLoginPromptSpy.mockReturnValueOnce(false);
- sendMockExtensionMessage(message, sender);
- await flushPromises();
+ await notificationBackground.triggerAddLoginNotification(data, tab);
expect(getEnableAddedLoginPromptSpy).toHaveBeenCalled();
expect(getAllDecryptedForUrlSpy).not.toHaveBeenCalled();
@@ -349,16 +355,12 @@ describe("NotificationBackground", () => {
});
it("skips attempting to add the login if the user with an unlocked vault has disabled the login notification", async () => {
- const message: NotificationBackgroundExtensionMessage = {
- command: "bgTriggerAddLoginNotification",
- login: { username: "test", password: "password", url: "https://example.com" },
- };
+ const data: ModifyLoginCipherFormData = mockModifyLoginCipherFormData;
activeAccountStatusMock$.next(AuthenticationStatus.Unlocked);
getEnableAddedLoginPromptSpy.mockReturnValueOnce(false);
getAllDecryptedForUrlSpy.mockResolvedValueOnce([]);
- sendMockExtensionMessage(message, sender);
- await flushPromises();
+ await notificationBackground.triggerAddLoginNotification(data, tab);
expect(getEnableAddedLoginPromptSpy).toHaveBeenCalled();
expect(getAllDecryptedForUrlSpy).toHaveBeenCalled();
@@ -367,10 +369,7 @@ describe("NotificationBackground", () => {
});
it("skips attempting to change the password for an existing login if the user has disabled changing the password notification", async () => {
- const message: NotificationBackgroundExtensionMessage = {
- command: "bgTriggerAddLoginNotification",
- login: { username: "test", password: "password", url: "https://example.com" },
- };
+ const data: ModifyLoginCipherFormData = mockModifyLoginCipherFormData;
activeAccountStatusMock$.next(AuthenticationStatus.Unlocked);
getEnableAddedLoginPromptSpy.mockReturnValueOnce(true);
getEnableChangedPasswordPromptSpy.mockReturnValueOnce(false);
@@ -378,8 +377,7 @@ describe("NotificationBackground", () => {
mock({ login: { username: "test", password: "oldPassword" } }),
]);
- sendMockExtensionMessage(message, sender);
- await flushPromises();
+ await notificationBackground.triggerAddLoginNotification(data, tab);
expect(getEnableAddedLoginPromptSpy).toHaveBeenCalled();
expect(getAllDecryptedForUrlSpy).toHaveBeenCalled();
@@ -389,18 +387,14 @@ describe("NotificationBackground", () => {
});
it("skips attempting to change the password for an existing login if the password has not changed", async () => {
- const message: NotificationBackgroundExtensionMessage = {
- command: "bgTriggerAddLoginNotification",
- login: { username: "test", password: "password", url: "https://example.com" },
- };
+ const data: ModifyLoginCipherFormData = mockModifyLoginCipherFormData;
activeAccountStatusMock$.next(AuthenticationStatus.Unlocked);
getEnableAddedLoginPromptSpy.mockReturnValueOnce(true);
getAllDecryptedForUrlSpy.mockResolvedValueOnce([
mock({ login: { username: "test", password: "password" } }),
]);
- sendMockExtensionMessage(message, sender);
- await flushPromises();
+ await notificationBackground.triggerAddLoginNotification(data, tab);
expect(getEnableAddedLoginPromptSpy).toHaveBeenCalled();
expect(getAllDecryptedForUrlSpy).toHaveBeenCalled();
@@ -409,48 +403,55 @@ describe("NotificationBackground", () => {
});
it("adds the login to the queue if the user has a locked account", async () => {
- const login = { username: "test", password: "password", url: "https://example.com" };
- const message: NotificationBackgroundExtensionMessage = {
- command: "bgTriggerAddLoginNotification",
- login,
- };
+ const data: ModifyLoginCipherFormData = mockModifyLoginCipherFormData;
activeAccountStatusMock$.next(AuthenticationStatus.Locked);
getEnableAddedLoginPromptSpy.mockReturnValueOnce(true);
- sendMockExtensionMessage(message, sender);
- await flushPromises();
+ await notificationBackground.triggerAddLoginNotification(data, tab);
- expect(pushAddLoginToQueueSpy).toHaveBeenCalledWith("example.com", login, sender.tab, true);
+ expect(pushAddLoginToQueueSpy).toHaveBeenCalledWith(
+ "example.com",
+ {
+ url: data.uri,
+ username: data.username,
+ password: data.password,
+ },
+ sender.tab,
+ true,
+ );
});
it("adds the login to the queue if the user has an unlocked account and the login is new", async () => {
- const login = {
- username: undefined,
- password: "password",
- url: "https://example.com",
- } as any;
- const message: NotificationBackgroundExtensionMessage = {
- command: "bgTriggerAddLoginNotification",
- login,
+ const data: ModifyLoginCipherFormData = {
+ ...mockModifyLoginCipherFormData,
+ username: null,
};
+
activeAccountStatusMock$.next(AuthenticationStatus.Unlocked);
getEnableAddedLoginPromptSpy.mockReturnValueOnce(true);
getAllDecryptedForUrlSpy.mockResolvedValueOnce([
mock({ login: { username: "anotherTestUsername", password: "password" } }),
]);
- sendMockExtensionMessage(message, sender);
- await flushPromises();
+ await notificationBackground.triggerAddLoginNotification(data, tab);
- expect(pushAddLoginToQueueSpy).toHaveBeenCalledWith("example.com", login, sender.tab);
+ expect(pushAddLoginToQueueSpy).toHaveBeenCalledWith(
+ "example.com",
+ {
+ url: data.uri,
+ username: data.username,
+ password: data.password,
+ },
+ sender.tab,
+ );
});
it("adds a change password message to the queue if the user has changed an existing cipher's password", async () => {
- const login = { username: "tEsT", password: "password", url: "https://example.com" };
- const message: NotificationBackgroundExtensionMessage = {
- command: "bgTriggerAddLoginNotification",
- login,
+ const data: ModifyLoginCipherFormData = {
+ ...mockModifyLoginCipherFormData,
+ username: "tEsT",
};
+
activeAccountStatusMock$.next(AuthenticationStatus.Unlocked);
getEnableAddedLoginPromptSpy.mockResolvedValueOnce(true);
getEnableChangedPasswordPromptSpy.mockResolvedValueOnce(true);
@@ -461,13 +462,12 @@ describe("NotificationBackground", () => {
}),
]);
- sendMockExtensionMessage(message, sender);
- await flushPromises();
+ await notificationBackground.triggerAddLoginNotification(data, tab);
expect(pushChangePasswordToQueueSpy).toHaveBeenCalledWith(
"cipher-id",
"example.com",
- login.password,
+ data.password,
sender.tab,
);
});
@@ -478,6 +478,12 @@ describe("NotificationBackground", () => {
let sender: chrome.runtime.MessageSender;
let pushChangePasswordToQueueSpy: jest.SpyInstance;
let getAllDecryptedForUrlSpy: jest.SpyInstance;
+ const mockModifyLoginCipherFormData: ModifyLoginCipherFormData = {
+ username: null,
+ uri: null,
+ password: "currentPassword",
+ newPassword: "newPassword",
+ };
beforeEach(() => {
tab = createChromeTabMock();
@@ -490,69 +496,51 @@ describe("NotificationBackground", () => {
});
it("skips attempting to add the change password message to the queue if the passed url is not valid", async () => {
- const message: NotificationBackgroundExtensionMessage = {
- command: "bgTriggerChangedPasswordNotification",
- data: { newPassword: "newPassword", currentPassword: "currentPassword", url: "" },
- };
+ const data: ModifyLoginCipherFormData = mockModifyLoginCipherFormData;
- sendMockExtensionMessage(message);
- await flushPromises();
+ await notificationBackground.triggerChangedPasswordNotification(data, tab);
expect(pushChangePasswordToQueueSpy).not.toHaveBeenCalled();
});
it("adds a change password message to the queue if the user does not have an unlocked account", async () => {
- const message: NotificationBackgroundExtensionMessage = {
- command: "bgTriggerChangedPasswordNotification",
- data: {
- newPassword: "newPassword",
- currentPassword: "currentPassword",
- url: "https://example.com",
- },
+ const data: ModifyLoginCipherFormData = {
+ ...mockModifyLoginCipherFormData,
+ uri: "https://example.com",
};
+
activeAccountStatusMock$.next(AuthenticationStatus.Locked);
- sendMockExtensionMessage(message, sender);
- await flushPromises();
+ await notificationBackground.triggerChangedPasswordNotification(data, tab);
expect(pushChangePasswordToQueueSpy).toHaveBeenCalledWith(
null,
"example.com",
- message.data?.newPassword,
+ data?.newPassword,
sender.tab,
true,
);
});
it("skips adding a change password message to the queue if the multiple ciphers exist for the passed URL and the current password is not found within the list of ciphers", async () => {
- const message: NotificationBackgroundExtensionMessage = {
- command: "bgTriggerChangedPasswordNotification",
- data: {
- newPassword: "newPassword",
- currentPassword: "currentPassword",
- url: "https://example.com",
- },
+ const data: ModifyLoginCipherFormData = {
+ ...mockModifyLoginCipherFormData,
+ uri: "https://example.com",
};
activeAccountStatusMock$.next(AuthenticationStatus.Unlocked);
getAllDecryptedForUrlSpy.mockResolvedValueOnce([
mock({ login: { username: "test", password: "password" } }),
]);
-
- sendMockExtensionMessage(message, sender);
- await flushPromises();
+ await notificationBackground.triggerChangedPasswordNotification(data, tab);
expect(getAllDecryptedForUrlSpy).toHaveBeenCalled();
expect(pushChangePasswordToQueueSpy).not.toHaveBeenCalled();
});
it("skips adding a change password message if more than one existing cipher is found with a matching password ", async () => {
- const message: NotificationBackgroundExtensionMessage = {
- command: "bgTriggerChangedPasswordNotification",
- data: {
- newPassword: "newPassword",
- currentPassword: "currentPassword",
- url: "https://example.com",
- },
+ const data: ModifyLoginCipherFormData = {
+ ...mockModifyLoginCipherFormData,
+ uri: "https://example.com",
};
activeAccountStatusMock$.next(AuthenticationStatus.Unlocked);
getAllDecryptedForUrlSpy.mockResolvedValueOnce([
@@ -560,21 +548,16 @@ describe("NotificationBackground", () => {
mock({ login: { username: "test2", password: "password" } }),
]);
- sendMockExtensionMessage(message, sender);
- await flushPromises();
+ await notificationBackground.triggerChangedPasswordNotification(data, tab);
expect(getAllDecryptedForUrlSpy).toHaveBeenCalled();
expect(pushChangePasswordToQueueSpy).not.toHaveBeenCalled();
});
it("adds a change password message to the queue if a single cipher matches the passed current password", async () => {
- const message: NotificationBackgroundExtensionMessage = {
- command: "bgTriggerChangedPasswordNotification",
- data: {
- newPassword: "newPassword",
- currentPassword: "currentPassword",
- url: "https://example.com",
- },
+ const data: ModifyLoginCipherFormData = {
+ ...mockModifyLoginCipherFormData,
+ uri: "https://example.com",
};
activeAccountStatusMock$.next(AuthenticationStatus.Unlocked);
getAllDecryptedForUrlSpy.mockResolvedValueOnce([
@@ -584,24 +567,20 @@ describe("NotificationBackground", () => {
}),
]);
- sendMockExtensionMessage(message, sender);
- await flushPromises();
+ await notificationBackground.triggerChangedPasswordNotification(data, tab);
expect(pushChangePasswordToQueueSpy).toHaveBeenCalledWith(
"cipher-id",
"example.com",
- message.data?.newPassword,
+ data?.newPassword,
sender.tab,
);
});
it("skips adding a change password message if no current password is passed in the message and more than one cipher is found for a url", async () => {
- const message: NotificationBackgroundExtensionMessage = {
- command: "bgTriggerChangedPasswordNotification",
- data: {
- newPassword: "newPassword",
- url: "https://example.com",
- },
+ const data: ModifyLoginCipherFormData = {
+ ...mockModifyLoginCipherFormData,
+ uri: "https://example.com",
};
activeAccountStatusMock$.next(AuthenticationStatus.Unlocked);
getAllDecryptedForUrlSpy.mockResolvedValueOnce([
@@ -609,20 +588,17 @@ describe("NotificationBackground", () => {
mock({ login: { username: "test2", password: "password" } }),
]);
- sendMockExtensionMessage(message, sender);
- await flushPromises();
+ await notificationBackground.triggerChangedPasswordNotification(data, tab);
expect(getAllDecryptedForUrlSpy).toHaveBeenCalled();
expect(pushChangePasswordToQueueSpy).not.toHaveBeenCalled();
});
it("adds a change password message to the queue if no current password is passed with the message, but a single cipher is matched for the uri", async () => {
- const message: NotificationBackgroundExtensionMessage = {
- command: "bgTriggerChangedPasswordNotification",
- data: {
- newPassword: "newPassword",
- url: "https://example.com",
- },
+ const data: ModifyLoginCipherFormData = {
+ ...mockModifyLoginCipherFormData,
+ uri: "https://example.com",
+ password: null,
};
activeAccountStatusMock$.next(AuthenticationStatus.Unlocked);
getAllDecryptedForUrlSpy.mockResolvedValueOnce([
@@ -632,13 +608,12 @@ describe("NotificationBackground", () => {
}),
]);
- sendMockExtensionMessage(message, sender);
- await flushPromises();
+ await notificationBackground.triggerChangedPasswordNotification(data, tab);
expect(pushChangePasswordToQueueSpy).toHaveBeenCalledWith(
"cipher-id",
"example.com",
- message.data?.newPassword,
+ data?.newPassword,
sender.tab,
);
});
diff --git a/apps/browser/src/autofill/background/notification.background.ts b/apps/browser/src/autofill/background/notification.background.ts
index 65c1ca0277f..3f6e93d8454 100644
--- a/apps/browser/src/autofill/background/notification.background.ts
+++ b/apps/browser/src/autofill/background/notification.background.ts
@@ -41,7 +41,7 @@ import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
import { LoginUriView } from "@bitwarden/common/vault/models/view/login-uri.view";
import { LoginView } from "@bitwarden/common/vault/models/view/login.view";
import { TaskService } from "@bitwarden/common/vault/tasks";
-import { SecurityTaskType } from "@bitwarden/common/vault/tasks/enums";
+import { SecurityTaskStatus, SecurityTaskType } from "@bitwarden/common/vault/tasks/enums";
import { SecurityTask } from "@bitwarden/common/vault/tasks/models/security-task";
// FIXME (PM-22628): Popup imports are forbidden in background
@@ -68,14 +68,17 @@ import {
AddChangePasswordQueueMessage,
AddLoginQueueMessage,
AddUnlockVaultQueueMessage,
- ChangePasswordMessageData,
AddLoginMessageData,
NotificationQueueMessageItem,
LockedVaultPendingNotificationsData,
NotificationBackgroundExtensionMessage,
NotificationBackgroundExtensionMessageHandlers,
} from "./abstractions/notification.background";
-import { NotificationTypeData } from "./abstractions/overlay-notifications.background";
+import {
+ LoginSecurityTaskInfo,
+ ModifyLoginCipherFormData,
+ NotificationTypeData,
+} from "./abstractions/overlay-notifications.background";
import { OverlayBackgroundExtensionMessage } from "./abstractions/overlay.background";
export default class NotificationBackground {
@@ -91,12 +94,6 @@ export default class NotificationBackground {
private readonly extensionMessageHandlers: NotificationBackgroundExtensionMessageHandlers = {
bgAdjustNotificationBar: ({ message, sender }) =>
this.handleAdjustNotificationBarMessage(message, sender),
- bgTriggerAddLoginNotification: ({ message, sender }) =>
- this.triggerAddLoginNotification(message, sender),
- bgTriggerChangedPasswordNotification: ({ message, sender }) =>
- this.triggerChangedPasswordNotification(message, sender),
- bgTriggerAtRiskPasswordNotification: ({ message, sender }) =>
- this.triggerAtRiskPasswordNotification(message, sender),
bgCloseNotificationBar: ({ message, sender }) =>
this.handleCloseNotificationBarMessage(message, sender),
bgOpenAtRiskPasswords: ({ message, sender }) =>
@@ -286,6 +283,62 @@ export default class NotificationBackground {
};
}
+ /**
+ * If there is a security task for this cipher at login, return the task, cipher view, and uri.
+ *
+ * @param modifyLoginData - The modified login form data
+ * @param activeUserId - The currently logged in user ID
+ */
+ private async getSecurityTaskAndCipherForLoginData(
+ modifyLoginData: ModifyLoginCipherFormData,
+ activeUserId: UserId,
+ ): Promise {
+ const tasks: SecurityTask[] = await this.getSecurityTasks(activeUserId);
+ if (!tasks?.length) {
+ return null;
+ }
+
+ const urlCiphers: CipherView[] = await this.cipherService.getAllDecryptedForUrl(
+ modifyLoginData.uri,
+ activeUserId,
+ );
+ if (!urlCiphers?.length) {
+ return null;
+ }
+
+ const securityTaskForLogin = urlCiphers.reduce(
+ (taskInfo: LoginSecurityTaskInfo | null, cipher: CipherView) => {
+ if (
+ // exit early if info was found already
+ taskInfo ||
+ // exit early if the cipher was deleted
+ cipher.deletedDate ||
+ // exit early if the entered login info doesn't match an existing cipher
+ modifyLoginData.username !== cipher.login.username ||
+ modifyLoginData.password !== cipher.login.password
+ ) {
+ return taskInfo;
+ }
+
+ // Find the first security task for the cipherId belonging to the entered login
+ const cipherSecurityTask = tasks.find(
+ ({ cipherId, status }) =>
+ cipher.id === cipherId && // match security task cipher id to url cipher id
+ status === SecurityTaskStatus.Pending, // security task has not been completed
+ );
+
+ if (cipherSecurityTask) {
+ return { securityTask: cipherSecurityTask, cipher, uri: modifyLoginData.uri };
+ }
+
+ return taskInfo;
+ },
+ null,
+ );
+
+ return securityTaskForLogin;
+ }
+
/**
* Gets the active user server config from the config service.
*/
@@ -302,6 +355,10 @@ export default class NotificationBackground {
return flagValue;
}
+ /**
+ * Gets the current authentication status of the user.
+ * @returns Promise - The current authentication status of the user.
+ */
private async getAuthStatus() {
return await firstValueFrom(this.authService.activeAccountStatus$);
}
@@ -400,11 +457,32 @@ export default class NotificationBackground {
* @param sender - The contextual sender of the message
*/
async triggerAtRiskPasswordNotification(
- message: NotificationBackgroundExtensionMessage,
- sender: chrome.runtime.MessageSender,
+ data: ModifyLoginCipherFormData,
+ tab: chrome.tabs.Tab,
): Promise {
- const { activeUserId, securityTask, cipher } = message.data;
- const domain = Utils.getDomain(sender.tab.url);
+ const flag = await this.getNotificationFlag();
+ if (!flag) {
+ return false;
+ }
+
+ const activeUserId = await firstValueFrom(
+ this.accountService.activeAccount$.pipe(getOptionalUserId),
+ );
+
+ if (!activeUserId) {
+ return false;
+ }
+ const loginSecurityTaskInfo = await this.getSecurityTaskAndCipherForLoginData(
+ data,
+ activeUserId,
+ );
+
+ if (!loginSecurityTaskInfo) {
+ return false;
+ }
+
+ const { securityTask, cipher } = loginSecurityTaskInfo;
+ const domain = Utils.getDomain(tab.url);
const passwordChangeUri =
await new TemporaryNotificationChangeLoginService().getChangePasswordUrl(cipher);
@@ -418,7 +496,7 @@ export default class NotificationBackground {
.pipe(getOrganizationById(securityTask.organizationId)),
);
- this.removeTabFromNotificationQueue(sender.tab);
+ this.removeTabFromNotificationQueue(tab);
const launchTimestamp = new Date().getTime();
const queueMessage: NotificationQueueMessageItem = {
domain,
@@ -426,12 +504,12 @@ export default class NotificationBackground {
type: NotificationQueueMessageType.AtRiskPassword,
passwordChangeUri,
organizationName: organization.name,
- tab: sender.tab,
+ tab: tab,
launchTimestamp,
expires: new Date(launchTimestamp + NOTIFICATION_BAR_LIFESPAN_MS),
};
this.notificationQueue.push(queueMessage);
- await this.checkNotificationQueue(sender.tab);
+ await this.checkNotificationQueue(tab);
return true;
}
@@ -444,17 +522,22 @@ export default class NotificationBackground {
* @param sender - The contextual sender of the message
*/
async triggerAddLoginNotification(
- message: NotificationBackgroundExtensionMessage,
- sender: chrome.runtime.MessageSender,
+ data: ModifyLoginCipherFormData,
+ tab: chrome.tabs.Tab,
): Promise {
+ const login = {
+ url: data.uri,
+ username: data.username,
+ password: data.password || data.newPassword,
+ };
+
const authStatus = await this.getAuthStatus();
if (authStatus === AuthenticationStatus.LoggedOut) {
return false;
}
- const loginInfo = message.login;
- const normalizedUsername = loginInfo.username ? loginInfo.username.toLowerCase() : "";
- const loginDomain = Utils.getDomain(loginInfo.url);
+ const normalizedUsername = login.username ? login.username.toLowerCase() : "";
+ const loginDomain = Utils.getDomain(login.url);
if (loginDomain == null) {
return false;
}
@@ -463,7 +546,7 @@ export default class NotificationBackground {
if (authStatus === AuthenticationStatus.Locked) {
if (addLoginIsEnabled) {
- await this.pushAddLoginToQueue(loginDomain, loginInfo, sender.tab, true);
+ await this.pushAddLoginToQueue(loginDomain, login, tab, true);
}
return false;
@@ -476,12 +559,12 @@ export default class NotificationBackground {
return false;
}
- const ciphers = await this.cipherService.getAllDecryptedForUrl(loginInfo.url, activeUserId);
+ const ciphers = await this.cipherService.getAllDecryptedForUrl(login.url, activeUserId);
const usernameMatches = ciphers.filter(
(c) => c.login.username != null && c.login.username.toLowerCase() === normalizedUsername,
);
if (addLoginIsEnabled && usernameMatches.length === 0) {
- await this.pushAddLoginToQueue(loginDomain, loginInfo, sender.tab);
+ await this.pushAddLoginToQueue(loginDomain, login, tab);
return true;
}
@@ -490,14 +573,9 @@ export default class NotificationBackground {
if (
changePasswordIsEnabled &&
usernameMatches.length === 1 &&
- usernameMatches[0].login.password !== loginInfo.password
+ usernameMatches[0].login.password !== login.password
) {
- await this.pushChangePasswordToQueue(
- usernameMatches[0].id,
- loginDomain,
- loginInfo.password,
- sender.tab,
- );
+ await this.pushChangePasswordToQueue(usernameMatches[0].id, loginDomain, login.password, tab);
return true;
}
return false;
@@ -535,23 +613,22 @@ export default class NotificationBackground {
* @param sender - The contextual sender of the message
*/
async triggerChangedPasswordNotification(
- message: NotificationBackgroundExtensionMessage,
- sender: chrome.runtime.MessageSender,
- ) {
- const changeData = message.data as ChangePasswordMessageData;
+ data: ModifyLoginCipherFormData,
+ tab: chrome.tabs.Tab,
+ ): Promise {
+ const changeData = {
+ url: data.uri,
+ currentPassword: data.password,
+ newPassword: data.newPassword,
+ };
+
const loginDomain = Utils.getDomain(changeData.url);
if (loginDomain == null) {
return false;
}
if ((await this.getAuthStatus()) < AuthenticationStatus.Unlocked) {
- await this.pushChangePasswordToQueue(
- null,
- loginDomain,
- changeData.newPassword,
- sender.tab,
- true,
- );
+ await this.pushChangePasswordToQueue(null, loginDomain, changeData.newPassword, tab, true);
return true;
}
@@ -575,7 +652,7 @@ export default class NotificationBackground {
id = ciphers[0].id;
}
if (id != null) {
- await this.pushChangePasswordToQueue(id, loginDomain, changeData.newPassword, sender.tab);
+ await this.pushChangePasswordToQueue(id, loginDomain, changeData.newPassword, tab);
return true;
}
return false;
@@ -1030,18 +1107,23 @@ export default class NotificationBackground {
private async getCollectionData(
message: NotificationBackgroundExtensionMessage,
): Promise {
- const collections = (await this.collectionService.getAllDecrypted()).reduce(
- (acc, collection) => {
- if (collection.organizationId === message?.orgId) {
- acc.push({
- id: collection.id,
- name: collection.name,
- organizationId: collection.organizationId,
- });
- }
- return acc;
- },
- [],
+ const collections = await firstValueFrom(
+ this.accountService.activeAccount$.pipe(
+ getUserId,
+ switchMap((userId) => this.collectionService.decryptedCollections$(userId)),
+ map((collections) =>
+ collections.reduce((acc, collection) => {
+ if (collection.organizationId === message?.orgId) {
+ acc.push({
+ id: collection.id,
+ name: collection.name,
+ organizationId: collection.organizationId,
+ });
+ }
+ return acc;
+ }, []),
+ ),
+ ),
);
return collections;
}
diff --git a/apps/browser/src/autofill/background/overlay-notifications.background.ts b/apps/browser/src/autofill/background/overlay-notifications.background.ts
index 93357113fc4..1d7f2b1f9d8 100644
--- a/apps/browser/src/autofill/background/overlay-notifications.background.ts
+++ b/apps/browser/src/autofill/background/overlay-notifications.background.ts
@@ -1,17 +1,15 @@
// FIXME: Update this file to be type safe and remove this and next line
// @ts-strict-ignore
-import { firstValueFrom, Subject, switchMap, timer } from "rxjs";
+import { Subject, switchMap, timer } from "rxjs";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
-import { getOptionalUserId } from "@bitwarden/common/auth/services/account.service";
import { CLEAR_NOTIFICATION_LOGIN_DATA_DURATION } from "@bitwarden/common/autofill/constants";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
-import { UserId } from "@bitwarden/common/types/guid";
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
-import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
-import { SecurityTask, SecurityTaskStatus, TaskService } from "@bitwarden/common/vault/tasks";
+import { TaskService } from "@bitwarden/common/vault/tasks";
import { BrowserApi } from "../../platform/browser/browser-api";
+import { NotificationType, NotificationTypes } from "../notification/abstractions/notification-bar";
import { generateDomainMatchPatterns, isInvalidResponseStatusCode } from "../utils";
import {
@@ -25,12 +23,6 @@ import {
} from "./abstractions/overlay-notifications.background";
import NotificationBackground from "./notification.background";
-type LoginSecurityTaskInfo = {
- securityTask: SecurityTask;
- cipher: CipherView;
- uri: ModifyLoginCipherFormData["uri"];
-};
-
export class OverlayNotificationsBackground implements OverlayNotificationsBackgroundInterface {
private websiteOriginsWithFields: WebsiteOriginsWithFields = new Map();
private activeFormSubmissionRequests: ActiveFormSubmissionRequests = new Set();
@@ -274,8 +266,8 @@ export class OverlayNotificationsBackground implements OverlayNotificationsBackg
const modifyLoginData = this.modifyLoginCipherFormData.get(tabId);
return (
!modifyLoginData ||
- !this.shouldAttemptAddLoginNotification(modifyLoginData) ||
- !this.shouldAttemptChangedPasswordNotification(modifyLoginData)
+ !this.shouldAttemptNotification(modifyLoginData, NotificationTypes.Add) ||
+ !this.shouldAttemptNotification(modifyLoginData, NotificationTypes.Change)
);
};
@@ -381,7 +373,7 @@ export class OverlayNotificationsBackground implements OverlayNotificationsBackg
return;
}
- await this.triggerNotificationInit(requestId, modifyLoginData, tab);
+ await this.processNotifications(requestId, modifyLoginData, tab);
};
/**
@@ -401,171 +393,86 @@ export class OverlayNotificationsBackground implements OverlayNotificationsBackg
const handleWebNavigationOnCompleted = async () => {
chrome.webNavigation.onCompleted.removeListener(handleWebNavigationOnCompleted);
const tab = await BrowserApi.getTab(tabId);
- await this.triggerNotificationInit(requestId, modifyLoginData, tab);
+ await this.processNotifications(requestId, modifyLoginData, tab);
};
chrome.webNavigation.onCompleted.addListener(handleWebNavigationOnCompleted);
};
/**
- * Initializes the add login or change password notification based on the modified login form data
- * and the tab details. This will trigger the notification to be displayed to the user.
+ * This method attempts to trigger the add login, change password, or at-risk password notifications
+ * based on the modified login data and the tab details.
*
* @param requestId - The details of the web response
* @param modifyLoginData - The modified login form data
* @param tab - The tab details
*/
- private triggerNotificationInit = async (
+ private processNotifications = async (
requestId: chrome.webRequest.ResourceRequest["requestId"],
modifyLoginData: ModifyLoginCipherFormData,
tab: chrome.tabs.Tab,
+ config: { skippable: NotificationType[] } = { skippable: [] },
) => {
- let result: string;
- if (this.shouldAttemptChangedPasswordNotification(modifyLoginData)) {
- // These notifications are temporarily setup as "messages" to the notification background.
- // This will be structured differently in a future refactor.
- const success = await this.notificationBackground.triggerChangedPasswordNotification(
- {
- command: "bgChangedPassword",
- data: {
- url: modifyLoginData.uri,
- currentPassword: modifyLoginData.password,
- newPassword: modifyLoginData.newPassword,
- },
- },
- { tab },
- );
- if (!success) {
- result = "Unqualified changedPassword notification attempt.";
- }
- }
-
- if (this.shouldAttemptAddLoginNotification(modifyLoginData)) {
- const success = await this.notificationBackground.triggerAddLoginNotification(
- {
- command: "bgTriggerAddLoginNotification",
- login: {
- url: modifyLoginData.uri,
- username: modifyLoginData.username,
- password: modifyLoginData.password || modifyLoginData.newPassword,
- },
- },
- { tab },
- );
- if (!success) {
- result = "Unqualified addLogin notification attempt.";
- }
- }
-
- const shouldGetTasks =
- (await this.notificationBackground.getNotificationFlag()) && !modifyLoginData.newPassword;
-
- if (shouldGetTasks) {
- const activeUserId = await firstValueFrom(
- this.accountService.activeAccount$.pipe(getOptionalUserId),
- );
-
- if (activeUserId) {
- const loginSecurityTaskInfo = await this.getSecurityTaskAndCipherForLoginData(
- modifyLoginData,
- activeUserId,
- );
-
- if (loginSecurityTaskInfo) {
- await this.notificationBackground.triggerAtRiskPasswordNotification(
- {
- command: "bgTriggerAtRiskPasswordNotification",
- data: {
- activeUserId,
- cipher: loginSecurityTaskInfo.cipher,
- securityTask: loginSecurityTaskInfo.securityTask,
- },
- },
- { tab },
- );
- } else {
- result = "Unqualified atRiskPassword notification attempt.";
- }
- }
- }
- this.clearCompletedWebRequest(requestId, tab);
- return result;
- };
-
- /**
- * Determines if the change password notification should be triggered.
- *
- * @param modifyLoginData - The modified login form data
- */
- private shouldAttemptChangedPasswordNotification = (
- modifyLoginData: ModifyLoginCipherFormData,
- ) => {
- return modifyLoginData?.newPassword && !modifyLoginData.username;
- };
-
- /**
- * Determines if the add login notification should be triggered.
- *
- * @param modifyLoginData - The modified login form data
- */
- private shouldAttemptAddLoginNotification = (modifyLoginData: ModifyLoginCipherFormData) => {
- return modifyLoginData?.username && (modifyLoginData.password || modifyLoginData.newPassword);
- };
-
- /**
- * If there is a security task for this cipher at login, return the task, cipher view, and uri.
- *
- * @param modifyLoginData - The modified login form data
- * @param activeUserId - The currently logged in user ID
- */
- private async getSecurityTaskAndCipherForLoginData(
- modifyLoginData: ModifyLoginCipherFormData,
- activeUserId: UserId,
- ): Promise {
- const tasks: SecurityTask[] = await this.notificationBackground.getSecurityTasks(activeUserId);
- if (!tasks?.length) {
- return null;
- }
-
- const urlCiphers: CipherView[] = await this.cipherService.getAllDecryptedForUrl(
- modifyLoginData.uri,
- activeUserId,
- );
- if (!urlCiphers?.length) {
- return null;
- }
-
- const securityTaskForLogin = urlCiphers.reduce(
- (taskInfo: LoginSecurityTaskInfo | null, cipher: CipherView) => {
- if (
- // exit early if info was found already
- taskInfo ||
- // exit early if the cipher was deleted
- cipher.deletedDate ||
- // exit early if the entered login info doesn't match an existing cipher
- modifyLoginData.username !== cipher.login.username ||
- modifyLoginData.password !== cipher.login.password
- ) {
- return taskInfo;
- }
-
- // Find the first security task for the cipherId belonging to the entered login
- const cipherSecurityTask = tasks.find(
- ({ cipherId, status }) =>
- cipher.id === cipherId && // match security task cipher id to url cipher id
- status === SecurityTaskStatus.Pending, // security task has not been completed
- );
-
- if (cipherSecurityTask) {
- return { securityTask: cipherSecurityTask, cipher, uri: modifyLoginData.uri };
- }
-
- return taskInfo;
+ const notificationCandidates = [
+ {
+ type: NotificationTypes.Change,
+ trigger: this.notificationBackground.triggerChangedPasswordNotification,
},
- null,
+ {
+ type: NotificationTypes.Add,
+ trigger: this.notificationBackground.triggerAddLoginNotification,
+ },
+ {
+ type: NotificationTypes.AtRiskPassword,
+ trigger: this.notificationBackground.triggerAtRiskPasswordNotification,
+ },
+ ].filter(
+ (candidate) =>
+ this.shouldAttemptNotification(modifyLoginData, candidate.type) ||
+ config.skippable.includes(candidate.type),
);
- return securityTaskForLogin;
- }
+ const results: string[] = [];
+ for (const { trigger, type } of notificationCandidates) {
+ const success = await trigger.bind(this.notificationBackground)(modifyLoginData, tab);
+ if (success) {
+ results.push(`Success: ${type}`);
+ break;
+ } else {
+ results.push(`Unqualified ${type} notification attempt.`);
+ }
+ }
+
+ this.clearCompletedWebRequest(requestId, tab);
+ return results.join(" ");
+ };
+
+ /**
+ * Determines if the add login notification should be attempted based on the modified login form data.
+ * @param modifyLoginData modified login form data
+ * @param notificationType The type of notification to be triggered
+ * @returns true if the notification should be attempted, false otherwise
+ */
+ private shouldAttemptNotification = (
+ modifyLoginData: ModifyLoginCipherFormData,
+ notificationType: NotificationType,
+ ): boolean => {
+ switch (notificationType) {
+ case NotificationTypes.Change:
+ return modifyLoginData?.newPassword && !modifyLoginData.username;
+ case NotificationTypes.Add:
+ return (
+ modifyLoginData?.username && !!(modifyLoginData.password || modifyLoginData.newPassword)
+ );
+ case NotificationTypes.AtRiskPassword:
+ return !modifyLoginData.newPassword;
+ case NotificationTypes.Unlock:
+ // Unlock notifications are handled separately and do not require form data
+ return false;
+ default:
+ this.logService.error(`Unknown notification type: ${notificationType}`);
+ return false;
+ }
+ };
/**
* Clears the completed web request and removes the modified login form data for the tab.
diff --git a/apps/browser/src/autofill/background/overlay.background.ts b/apps/browser/src/autofill/background/overlay.background.ts
index 1f249454393..f55b5c8cc3d 100644
--- a/apps/browser/src/autofill/background/overlay.background.ts
+++ b/apps/browser/src/autofill/background/overlay.background.ts
@@ -191,6 +191,7 @@ export class OverlayBackground implements OverlayBackgroundInterface {
editedCipher: () => this.updateOverlayCiphers(),
deletedCipher: () => this.updateOverlayCiphers(),
bgSaveCipher: () => this.updateOverlayCiphers(),
+ updateOverlayCiphers: () => this.updateOverlayCiphers(),
fido2AbortRequest: ({ sender }) => this.abortFido2ActiveRequest(sender.tab.id),
};
private readonly inlineMenuButtonPortMessageHandlers: InlineMenuButtonPortMessageHandlers = {
diff --git a/apps/browser/src/autofill/background/tabs.background.ts b/apps/browser/src/autofill/background/tabs.background.ts
index 4d520680980..cd2c1595d69 100644
--- a/apps/browser/src/autofill/background/tabs.background.ts
+++ b/apps/browser/src/autofill/background/tabs.background.ts
@@ -20,10 +20,8 @@ export default class TabsBackground {
return;
}
- // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
- // eslint-disable-next-line @typescript-eslint/no-floating-promises
- this.updateCurrentTabData();
- this.setupTabEventListeners();
+ void this.updateCurrentTabData();
+ void this.setupTabEventListeners();
}
/**
diff --git a/apps/browser/src/autofill/content/autofill-init.ts b/apps/browser/src/autofill/content/autofill-init.ts
index 8f69937ac60..b6fc6c3392e 100644
--- a/apps/browser/src/autofill/content/autofill-init.ts
+++ b/apps/browser/src/autofill/content/autofill-init.ts
@@ -1,5 +1,3 @@
-// FIXME: Update this file to be type safe and remove this and next line
-// @ts-strict-ignore
import { EVENTS } from "@bitwarden/common/autofill/constants";
import AutofillPageDetails from "../models/autofill-page-details";
@@ -122,7 +120,7 @@ class AutofillInit implements AutofillInitInterface {
* @param {AutofillExtensionMessage} message
*/
private async fillForm({ fillScript, pageDetailsUrl }: AutofillExtensionMessage) {
- if ((document.defaultView || window).location.href !== pageDetailsUrl) {
+ if ((document.defaultView || window).location.href !== pageDetailsUrl || !fillScript) {
return;
}
@@ -177,7 +175,7 @@ class AutofillInit implements AutofillInitInterface {
message: AutofillExtensionMessage,
sender: chrome.runtime.MessageSender,
sendResponse: (response?: any) => void,
- ): boolean => {
+ ): boolean | null => {
const command: string = message.command;
const handler: CallableFunction | undefined = this.getExtensionMessageHandler(command);
if (!handler) {
diff --git a/apps/browser/src/autofill/content/autofiller.ts b/apps/browser/src/autofill/content/autofiller.ts
index c7a742f1fe1..bc9fd0bb05f 100644
--- a/apps/browser/src/autofill/content/autofiller.ts
+++ b/apps/browser/src/autofill/content/autofiller.ts
@@ -1,5 +1,3 @@
-// FIXME: Update this file to be type safe and remove this and next line
-// @ts-strict-ignore
import { setupExtensionDisconnectAction } from "../utils";
if (document.readyState === "loading") {
@@ -9,7 +7,7 @@ if (document.readyState === "loading") {
}
function loadAutofiller() {
- let pageHref: string = null;
+ let pageHref: null | string = null;
let filledThisHref = false;
let delayFillTimeout: number;
let doFillInterval: number | NodeJS.Timeout;
@@ -51,9 +49,7 @@ function loadAutofiller() {
sender: "autofiller",
};
- // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
- // eslint-disable-next-line @typescript-eslint/no-floating-promises
- chrome.runtime.sendMessage(msg);
+ void chrome.runtime.sendMessage(msg);
}
}
diff --git a/apps/browser/src/autofill/content/bootstrap-autofill-overlay-menu.ts b/apps/browser/src/autofill/content/bootstrap-autofill-overlay-menu.ts
index 605ffff0fec..40e32843fd4 100644
--- a/apps/browser/src/autofill/content/bootstrap-autofill-overlay-menu.ts
+++ b/apps/browser/src/autofill/content/bootstrap-autofill-overlay-menu.ts
@@ -1,5 +1,3 @@
-// FIXME: Update this file to be type safe and remove this and next line
-// @ts-strict-ignore
import { AutofillInlineMenuContentService } from "../overlay/inline-menu/content/autofill-inline-menu-content.service";
import { AutofillOverlayContentService } from "../services/autofill-overlay-content.service";
import DomElementVisibilityService from "../services/dom-element-visibility.service";
@@ -11,7 +9,7 @@ import AutofillInit from "./autofill-init";
(function (windowContext) {
if (!windowContext.bitwardenAutofillInit) {
- let inlineMenuContentService: AutofillInlineMenuContentService;
+ let inlineMenuContentService: undefined | AutofillInlineMenuContentService;
if (globalThis.self === globalThis.top) {
inlineMenuContentService = new AutofillInlineMenuContentService();
}
diff --git a/apps/browser/src/autofill/content/bootstrap-autofill-overlay-notifications.ts b/apps/browser/src/autofill/content/bootstrap-autofill-overlay-notifications.ts
index 495ae0e22db..8a079fa26c8 100644
--- a/apps/browser/src/autofill/content/bootstrap-autofill-overlay-notifications.ts
+++ b/apps/browser/src/autofill/content/bootstrap-autofill-overlay-notifications.ts
@@ -1,5 +1,3 @@
-// FIXME: Update this file to be type safe and remove this and next line
-// @ts-strict-ignore
import { OverlayNotificationsContentService } from "../overlay/notifications/content/overlay-notifications-content.service";
import { AutofillOverlayContentService } from "../services/autofill-overlay-content.service";
import DomElementVisibilityService from "../services/dom-element-visibility.service";
@@ -20,7 +18,7 @@ import AutofillInit from "./autofill-init";
inlineMenuFieldQualificationService,
);
- let overlayNotificationsContentService: OverlayNotificationsContentService;
+ let overlayNotificationsContentService: undefined | OverlayNotificationsContentService;
if (globalThis.self === globalThis.top) {
overlayNotificationsContentService = new OverlayNotificationsContentService();
}
@@ -29,7 +27,7 @@ import AutofillInit from "./autofill-init";
domQueryService,
domElementVisibilityService,
autofillOverlayContentService,
- null,
+ undefined,
overlayNotificationsContentService,
);
setupAutofillInitDisconnectAction(windowContext);
diff --git a/apps/browser/src/autofill/content/bootstrap-autofill-overlay.ts b/apps/browser/src/autofill/content/bootstrap-autofill-overlay.ts
index 1777b135fe9..d204362ee25 100644
--- a/apps/browser/src/autofill/content/bootstrap-autofill-overlay.ts
+++ b/apps/browser/src/autofill/content/bootstrap-autofill-overlay.ts
@@ -1,5 +1,3 @@
-// FIXME: Update this file to be type safe and remove this and next line
-// @ts-strict-ignore
import { AutofillInlineMenuContentService } from "../overlay/inline-menu/content/autofill-inline-menu-content.service";
import { OverlayNotificationsContentService } from "../overlay/notifications/content/overlay-notifications-content.service";
import { AutofillOverlayContentService } from "../services/autofill-overlay-content.service";
@@ -12,8 +10,8 @@ import AutofillInit from "./autofill-init";
(function (windowContext) {
if (!windowContext.bitwardenAutofillInit) {
- let inlineMenuContentService: AutofillInlineMenuContentService;
- let overlayNotificationsContentService: OverlayNotificationsContentService;
+ let inlineMenuContentService: undefined | AutofillInlineMenuContentService;
+ let overlayNotificationsContentService: undefined | OverlayNotificationsContentService;
if (globalThis.self === globalThis.top) {
inlineMenuContentService = new AutofillInlineMenuContentService();
overlayNotificationsContentService = new OverlayNotificationsContentService();
diff --git a/apps/browser/src/autofill/content/components/.lit-storybook/main.ts b/apps/browser/src/autofill/content/components/.lit-storybook/main.ts
index a316d8f5baa..53c06264672 100644
--- a/apps/browser/src/autofill/content/components/.lit-storybook/main.ts
+++ b/apps/browser/src/autofill/content/components/.lit-storybook/main.ts
@@ -1,9 +1,16 @@
-import path, { dirname, join } from "path";
+import { createRequire } from "module";
+import { dirname, join, resolve } from "path";
+import { fileURLToPath } from "url";
import type { StorybookConfig } from "@storybook/web-components-webpack5";
import remarkGfm from "remark-gfm";
import TsconfigPathsPlugin from "tsconfig-paths-webpack-plugin";
+const currentFile = fileURLToPath(import.meta.url);
+const currentDirectory = dirname(currentFile);
+
+const require = createRequire(import.meta.url);
+
const getAbsolutePath = (value: string): string =>
dirname(require.resolve(join(value, "package.json")));
@@ -43,7 +50,7 @@ const config: StorybookConfig = {
if (config.resolve) {
config.resolve.plugins = [
new TsconfigPathsPlugin({
- configFile: path.resolve(__dirname, "../../../../../tsconfig.json"),
+ configFile: resolve(currentDirectory, "../../../../../tsconfig.json"),
}),
] as any;
}
diff --git a/apps/browser/src/autofill/content/components/buttons/action-button.ts b/apps/browser/src/autofill/content/components/buttons/action-button.ts
index 339b628875c..b43bed7f96b 100644
--- a/apps/browser/src/autofill/content/components/buttons/action-button.ts
+++ b/apps/browser/src/autofill/content/components/buttons/action-button.ts
@@ -8,6 +8,7 @@ import { Spinner } from "../icons";
export type ActionButtonProps = {
buttonText: string | TemplateResult;
+ dataTestId?: string;
disabled?: boolean;
isLoading?: boolean;
theme: Theme;
@@ -17,6 +18,7 @@ export type ActionButtonProps = {
export function ActionButton({
buttonText,
+ dataTestId,
disabled = false,
isLoading = false,
theme,
@@ -32,6 +34,7 @@ export function ActionButton({
return html`
;
-const Template = (args: AtRiskNotificationProps) => AtRiskNotification({ ...args });
-
+const Template = (args: AtRiskNotificationProps) => {
+ const notificationTestId = getNotificationTestId(NotificationTypes.AtRiskPassword);
+ return AtRiskNotification({ ...args, notificationTestId });
+};
export const Default: StoryObj = {
render: Template,
};
diff --git a/apps/browser/src/autofill/content/components/notification/at-risk-password/container.ts b/apps/browser/src/autofill/content/components/notification/at-risk-password/container.ts
index 90da0833fd9..001e9f24001 100644
--- a/apps/browser/src/autofill/content/components/notification/at-risk-password/container.ts
+++ b/apps/browser/src/autofill/content/components/notification/at-risk-password/container.ts
@@ -18,11 +18,13 @@ export type AtRiskNotificationProps = NotificationBarIframeInitData & {
handleCloseNotification: (e: Event) => void;
} & {
i18n: I18n;
+ notificationTestId: string;
};
export function AtRiskNotification({
handleCloseNotification,
i18n,
+ notificationTestId,
theme = ThemeTypes.Light,
params,
}: AtRiskNotificationProps) {
@@ -33,7 +35,7 @@ export function AtRiskNotification({
);
return html`
-
+
${NotificationHeader({
handleCloseNotification,
i18n,
diff --git a/apps/browser/src/autofill/content/components/notification/at-risk-password/footer.ts b/apps/browser/src/autofill/content/components/notification/at-risk-password/footer.ts
index d7805492fa6..e89edaf8b69 100644
--- a/apps/browser/src/autofill/content/components/notification/at-risk-password/footer.ts
+++ b/apps/browser/src/autofill/content/components/notification/at-risk-password/footer.ts
@@ -26,6 +26,7 @@ export function AtRiskNotificationFooter({
open(passwordChangeUri, "_blank");
},
buttonText: AdditionalTasksButtonContent({ buttonText: i18n.changePassword, theme }),
+ dataTestId: "change-password-button",
theme,
fullWidth: false,
})}
diff --git a/apps/browser/src/autofill/content/trigger-autofill-script-injection.ts b/apps/browser/src/autofill/content/trigger-autofill-script-injection.ts
index 95a2391991b..9db691c1359 100644
--- a/apps/browser/src/autofill/content/trigger-autofill-script-injection.ts
+++ b/apps/browser/src/autofill/content/trigger-autofill-script-injection.ts
@@ -1,5 +1,3 @@
(function () {
- // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
- // eslint-disable-next-line @typescript-eslint/no-floating-promises
- chrome.runtime.sendMessage({ command: "triggerAutofillScriptInjection" });
+ void chrome.runtime.sendMessage({ command: "triggerAutofillScriptInjection" });
})();
diff --git a/apps/browser/src/autofill/notification/bar.ts b/apps/browser/src/autofill/notification/bar.ts
index 285ae4aa257..6e20a07f81f 100644
--- a/apps/browser/src/autofill/notification/bar.ts
+++ b/apps/browser/src/autofill/notification/bar.ts
@@ -200,7 +200,7 @@ export function getNotificationTestId(
[NotificationTypes.Unlock]: "unlock-notification-bar",
[NotificationTypes.Add]: "save-notification-bar",
[NotificationTypes.Change]: "update-notification-bar",
- [NotificationTypes.AtRiskPassword]: "at-risk-password-notification-bar",
+ [NotificationTypes.AtRiskPassword]: "at-risk-notification-bar",
}[notificationType];
}
@@ -287,6 +287,7 @@ async function initNotificationBar(message: NotificationBarWindowMessage) {
type: notificationBarIframeInitData.type as NotificationType,
theme: resolvedTheme,
i18n,
+ notificationTestId,
params: initData.params,
handleCloseNotification,
}),
diff --git a/apps/browser/src/autofill/services/autofill-constants.ts b/apps/browser/src/autofill/services/autofill-constants.ts
index 55c3cced726..7467d5d4ba7 100644
--- a/apps/browser/src/autofill/services/autofill-constants.ts
+++ b/apps/browser/src/autofill/services/autofill-constants.ts
@@ -50,6 +50,15 @@ export class AutoFillConstants {
static readonly SearchFieldNames: string[] = ["search", "query", "find", "go"];
+ static readonly NewEmailFieldKeywords: string[] = [
+ "new-email",
+ "newemail",
+ "new email",
+ "neue e-mail",
+ ];
+
+ static readonly NewsletterFormNames: string[] = ["newsletter"];
+
static readonly FieldIgnoreList: string[] = ["captcha", "findanything", "forgot"];
static readonly PasswordFieldExcludeList: string[] = [
diff --git a/apps/browser/src/autofill/services/autofill.service.ts b/apps/browser/src/autofill/services/autofill.service.ts
index 6aa99bbda41..099f345cb75 100644
--- a/apps/browser/src/autofill/services/autofill.service.ts
+++ b/apps/browser/src/autofill/services/autofill.service.ts
@@ -213,9 +213,7 @@ export default class AutofillService implements AutofillServiceInterface {
this.autofillScriptPortsSet.delete(port);
});
- // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
- // eslint-disable-next-line @typescript-eslint/no-floating-promises
- this.injectAutofillScriptsInAllTabs();
+ void this.injectAutofillScriptsInAllTabs();
}
/**
@@ -470,9 +468,7 @@ export default class AutofillService implements AutofillServiceInterface {
await this.cipherService.updateLastUsedDate(options.cipher.id, activeAccount.id);
}
- // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
- // eslint-disable-next-line @typescript-eslint/no-floating-promises
- BrowserApi.tabSendMessage(
+ void BrowserApi.tabSendMessage(
tab,
{
command: options.autoSubmitLogin ? "triggerAutoSubmitLogin" : "fillForm",
@@ -502,9 +498,10 @@ export default class AutofillService implements AutofillServiceInterface {
);
if (didAutofill) {
- // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
- // eslint-disable-next-line @typescript-eslint/no-floating-promises
- this.eventCollectionService.collect(EventType.Cipher_ClientAutofilled, options.cipher.id);
+ await this.eventCollectionService.collect(
+ EventType.Cipher_ClientAutofilled,
+ options.cipher.id,
+ );
if (totp !== null) {
return totp;
} else {
diff --git a/apps/browser/src/autofill/services/inline-menu-field-qualification.service.ts b/apps/browser/src/autofill/services/inline-menu-field-qualification.service.ts
index 9b16a0cfbdd..b12017484eb 100644
--- a/apps/browser/src/autofill/services/inline-menu-field-qualification.service.ts
+++ b/apps/browser/src/autofill/services/inline-menu-field-qualification.service.ts
@@ -58,6 +58,8 @@ export class InlineMenuFieldQualificationService
"neue e-mail",
"pwdcheck",
];
+ private newEmailFieldKeywords = new Set(AutoFillConstants.NewEmailFieldKeywords);
+ private newsletterFormKeywords = new Set(AutoFillConstants.NewsletterFormNames);
private updatePasswordFieldKeywords = [
"update password",
"change password",
@@ -152,6 +154,61 @@ export class InlineMenuFieldQualificationService
private totpFieldAutocompleteValue = "one-time-code";
private premiumEnabled = false;
+ /**
+ * Validates the provided field to indicate if the field is a new email field used for account creation/registration.
+ *
+ * @param field - The field to validate
+ */
+ private isExplicitIdentityEmailField(field: AutofillField): boolean {
+ const matchFieldAttributeValues = [field.type, field.htmlName, field.htmlID, field.placeholder];
+ for (let attrIndex = 0; attrIndex < matchFieldAttributeValues.length; attrIndex++) {
+ if (!matchFieldAttributeValues[attrIndex]) {
+ continue;
+ }
+
+ for (let keywordIndex = 0; keywordIndex < matchFieldAttributeValues.length; keywordIndex++) {
+ if (this.newEmailFieldKeywords.has(matchFieldAttributeValues[attrIndex])) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Validates the provided form to indicate if the form is related to newsletter registration.
+ *
+ * @param parentForm - The form to validate
+ */
+ private isNewsletterForm(parentForm: any): boolean {
+ if (!parentForm) {
+ return false;
+ }
+
+ const matchFieldAttributeValues = [
+ parentForm.type,
+ parentForm.htmlName,
+ parentForm.htmlID,
+ parentForm.placeholder,
+ ];
+
+ for (let attrIndex = 0; attrIndex < matchFieldAttributeValues.length; attrIndex++) {
+ const attrValue = matchFieldAttributeValues[attrIndex];
+ if (!attrValue || typeof attrValue !== "string") {
+ continue;
+ }
+ const attrValueLower = attrValue.toLowerCase();
+ for (const keyword of this.newsletterFormKeywords) {
+ if (attrValueLower.includes(keyword.toLowerCase())) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
constructor() {
void Promise.all([
sendExtensionMessage("getInlineMenuFieldQualificationFeatureFlag"),
@@ -300,7 +357,11 @@ export class InlineMenuFieldQualificationService
return false;
}
- return this.fieldContainsAutocompleteValues(field, this.identityAutocompleteValues);
+ return (
+ // Recognize explicit identity email fields (like id="new-email")
+ this.isFieldForIdentityEmail(field) ||
+ this.fieldContainsAutocompleteValues(field, this.identityAutocompleteValues)
+ );
}
/**
@@ -397,6 +458,12 @@ export class InlineMenuFieldQualificationService
): boolean {
// If the provided field is set with an autocomplete of "username", we should assume that
// the page developer intends for this field to be interpreted as a username field.
+
+ // Exclude non-login email field from being treated as a login username field
+ if (this.isExplicitIdentityEmailField(field)) {
+ return false;
+ }
+
if (this.fieldContainsAutocompleteValues(field, this.loginUsernameAutocompleteValues)) {
const newPasswordFieldsInPageDetails = pageDetails.fields.filter(
(field) => field.viewable && this.isNewPasswordField(field),
@@ -415,6 +482,10 @@ export class InlineMenuFieldQualificationService
const parentForm = pageDetails.forms[field.form];
const passwordFieldsInPageDetails = pageDetails.fields.filter(this.isCurrentPasswordField);
+ if (this.isNewsletterForm(parentForm)) {
+ return false;
+ }
+
// If the field is not structured within a form, we need to identify if the field is used in conjunction
// with a password field. If that's the case, then we should assume that it is a form field element.
if (!parentForm) {
@@ -822,9 +893,14 @@ export class InlineMenuFieldQualificationService
* @param field - The field to validate
*/
isFieldForIdentityEmail = (field: AutofillField): boolean => {
+ if (this.isExplicitIdentityEmailField(field)) {
+ return true;
+ }
+
if (
this.fieldContainsAutocompleteValues(field, this.emailAutocompleteValue) ||
- field.type === "email"
+ field.type === "email" ||
+ field.htmlName === "email"
) {
return true;
}
diff --git a/apps/browser/src/background/idle.background.ts b/apps/browser/src/background/idle.background.ts
index 6fd555f4287..aaa23a140db 100644
--- a/apps/browser/src/background/idle.background.ts
+++ b/apps/browser/src/background/idle.background.ts
@@ -1,5 +1,3 @@
-// FIXME: Update this file to be type safe and remove this and next line
-// @ts-strict-ignore
import { firstValueFrom } from "rxjs";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
@@ -15,7 +13,7 @@ const IdleInterval = 60 * 5; // 5 minutes
export default class IdleBackground {
private idle: typeof chrome.idle | typeof browser.idle | null;
- private idleTimer: number | NodeJS.Timeout = null;
+ private idleTimer: null | number | NodeJS.Timeout = null;
private idleState = "active";
constructor(
@@ -80,9 +78,8 @@ export default class IdleBackground {
globalThis.clearTimeout(this.idleTimer);
this.idleTimer = null;
}
- // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
- // eslint-disable-next-line @typescript-eslint/no-floating-promises
- this.idle.queryState(IdleInterval, (state: string) => {
+
+ void this.idle?.queryState(IdleInterval, (state: string) => {
if (state !== this.idleState) {
this.idleState = state;
handler(state);
diff --git a/apps/browser/src/background/main.background.ts b/apps/browser/src/background/main.background.ts
index 28f179cf9d4..3a1e8e6119a 100644
--- a/apps/browser/src/background/main.background.ts
+++ b/apps/browser/src/background/main.background.ts
@@ -14,8 +14,6 @@ import {
InternalUserDecryptionOptionsServiceAbstraction,
LoginEmailServiceAbstraction,
LogoutReason,
- PinService,
- PinServiceAbstraction,
UserDecryptionOptionsService,
} from "@bitwarden/auth/common";
import { ApiService as ApiServiceAbstraction } from "@bitwarden/common/abstractions/api.service";
@@ -45,6 +43,7 @@ import { AccountServiceImplementation } from "@bitwarden/common/auth/services/ac
import { AuthRequestAnsweringService } from "@bitwarden/common/auth/services/auth-request-answering/auth-request-answering.service";
import { AuthService } from "@bitwarden/common/auth/services/auth.service";
import { AvatarService } from "@bitwarden/common/auth/services/avatar.service";
+import { DefaultActiveUserAccessor } from "@bitwarden/common/auth/services/default-active-user.accessor";
import { DevicesServiceImplementation } from "@bitwarden/common/auth/services/devices/devices.service.implementation";
import { DevicesApiServiceImplementation } from "@bitwarden/common/auth/services/devices-api.service.implementation";
import { SsoLoginService } from "@bitwarden/common/auth/services/sso-login.service";
@@ -75,10 +74,7 @@ import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
import { ProcessReloadServiceAbstraction } from "@bitwarden/common/key-management/abstractions/process-reload.service";
import { CryptoFunctionService as CryptoFunctionServiceAbstraction } from "@bitwarden/common/key-management/crypto/abstractions/crypto-function.service";
import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service";
-import { BulkEncryptServiceImplementation } from "@bitwarden/common/key-management/crypto/services/bulk-encrypt.service.implementation";
import { EncryptServiceImplementation } from "@bitwarden/common/key-management/crypto/services/encrypt.service.implementation";
-import { FallbackBulkEncryptService } from "@bitwarden/common/key-management/crypto/services/fallback-bulk-encrypt.service";
-import { MultithreadEncryptServiceImplementation } from "@bitwarden/common/key-management/crypto/services/multithread-encrypt.service.implementation";
import { WebCryptoFunctionService } from "@bitwarden/common/key-management/crypto/services/web-crypto-function.service";
import { DeviceTrustServiceAbstraction } from "@bitwarden/common/key-management/device-trust/abstractions/device-trust.service.abstraction";
import { DeviceTrustService } from "@bitwarden/common/key-management/device-trust/services/device-trust.service.implementation";
@@ -86,6 +82,8 @@ import { KeyConnectorService as KeyConnectorServiceAbstraction } from "@bitwarde
import { KeyConnectorService } from "@bitwarden/common/key-management/key-connector/services/key-connector.service";
import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/key-management/master-password/abstractions/master-password.service.abstraction";
import { MasterPasswordService } from "@bitwarden/common/key-management/master-password/services/master-password.service";
+import { PinServiceAbstraction } from "@bitwarden/common/key-management/pin/pin.service.abstraction";
+import { PinService } from "@bitwarden/common/key-management/pin/pin.service.implementation";
import { DefaultProcessReloadService } from "@bitwarden/common/key-management/services/default-process-reload.service";
import {
DefaultVaultTimeoutSettingsService,
@@ -132,7 +130,7 @@ import {
WebPushNotificationsApiService,
WorkerWebPushConnectionService,
} from "@bitwarden/common/platform/notifications/internal";
-import { SystemNotificationsService } from "@bitwarden/common/platform/notifications/system-notifications-service";
+import { SystemNotificationsService } from "@bitwarden/common/platform/notifications/system-notifications.service";
import { UnsupportedSystemNotificationsService } from "@bitwarden/common/platform/notifications/unsupported-system-notifications.service";
import { AppIdService } from "@bitwarden/common/platform/services/app-id.service";
import { ConfigApiService } from "@bitwarden/common/platform/services/config/config-api.service";
@@ -373,7 +371,6 @@ export default class MainBackground {
vaultFilterService: VaultFilterService;
usernameGenerationService: UsernameGenerationServiceAbstraction;
encryptService: EncryptService;
- bulkEncryptService: FallbackBulkEncryptService;
folderApiService: FolderApiServiceAbstraction;
policyApiService: PolicyApiServiceAbstraction;
sendApiService: SendApiServiceAbstraction;
@@ -586,13 +583,11 @@ export default class MainBackground {
storageServiceProvider,
);
- this.encryptService = BrowserApi.isManifestVersion(2)
- ? new MultithreadEncryptServiceImplementation(
- this.cryptoFunctionService,
- this.logService,
- true,
- )
- : new EncryptServiceImplementation(this.cryptoFunctionService, this.logService, true);
+ this.encryptService = new EncryptServiceImplementation(
+ this.cryptoFunctionService,
+ this.logService,
+ true,
+ );
this.singleUserStateProvider = new DefaultSingleUserStateProvider(
storageServiceProvider,
@@ -606,7 +601,7 @@ export default class MainBackground {
this.singleUserStateProvider,
);
this.activeUserStateProvider = new DefaultActiveUserStateProvider(
- this.accountService,
+ new DefaultActiveUserAccessor(this.accountService),
this.singleUserStateProvider,
);
this.derivedStateProvider = new InlineDerivedStateProvider();
@@ -677,6 +672,8 @@ export default class MainBackground {
this.keyGenerationService,
this.encryptService,
this.logService,
+ this.cryptoFunctionService,
+ this.accountService,
);
this.i18nService = new I18nService(BrowserApi.getUILanguage(), this.globalStateProvider);
@@ -890,8 +887,6 @@ export default class MainBackground {
this.themeStateService = new DefaultThemeStateService(this.globalStateProvider);
- this.bulkEncryptService = new FallbackBulkEncryptService(this.encryptService);
-
this.cipherEncryptionService = new DefaultCipherEncryptionService(
this.sdkService,
this.logService,
@@ -906,13 +901,13 @@ export default class MainBackground {
this.stateService,
this.autofillSettingsService,
this.encryptService,
- this.bulkEncryptService,
this.cipherFileUploadService,
this.configService,
this.stateProvider,
this.accountService,
this.logService,
this.cipherEncryptionService,
+ this.messagingService,
);
this.folderService = new FolderService(
this.keyService,
@@ -1135,11 +1130,7 @@ export default class MainBackground {
this.actionsService = new BrowserActionsService(this.logService, this.platformUtilsService);
- const isChrome = this.platformUtilsService.isChrome();
- const isSafari = this.platformUtilsService.isSafari();
- const isFirefox = this.platformUtilsService.isFirefox();
-
- if ((isChrome || isFirefox) && !isSafari) {
+ if ("notifications" in chrome) {
this.systemNotificationService = new BrowserSystemNotificationService(
this.logService,
this.platformUtilsService,
@@ -1148,18 +1139,6 @@ export default class MainBackground {
this.systemNotificationService = new UnsupportedSystemNotificationsService();
}
- // void Promise.all([this.configService.getFeatureFlag(FeatureFlag.PM14938_BrowserExtensionLoginApproval)])
- // .then((isBrowserExtensionLoginApprovalFFOn) => {
- // if (isBrowserExtensionLoginApprovalFFOn) {
- // this.authRequestAnsweringService = new AuthRequestAnsweringService(
- // this.systemNotificationService,
- // this.actionsService,
- // );
- // } else {
- // this.authRequestAnsweringService = new UnsupportedAuthRequestAnsweringService();
- // }
- // });
-
this.authRequestAnsweringService = new AuthRequestAnsweringService(
this.accountService,
this.actionsService,
@@ -1440,13 +1419,6 @@ export default class MainBackground {
// Only the "true" background should run migrations
await this.stateService.init({ runMigrations: true });
- this.configService.serverConfig$.subscribe((newConfig) => {
- if (newConfig != null) {
- this.encryptService.onServerConfigChange(newConfig);
- this.bulkEncryptService.onServerConfigChange(newConfig);
- }
- });
-
// This is here instead of in in the InitService b/c we don't plan for
// side effects to run in the Browser InitService.
const accounts = await firstValueFrom(this.accountService.accounts$);
@@ -1479,15 +1451,6 @@ export default class MainBackground {
this.syncServiceListener?.listener$().subscribe();
await this.autoSubmitLoginBackground.init();
- if (
- BrowserApi.isManifestVersion(2) &&
- (await this.configService.getFeatureFlag(FeatureFlag.PM4154_BulkEncryptionService))
- ) {
- await this.bulkEncryptService.setFeatureFlagEncryptService(
- new BulkEncryptServiceImplementation(this.cryptoFunctionService, this.logService),
- );
- }
-
// If the user is logged out, switch to the next account
const active = await firstValueFrom(this.accountService.activeAccount$);
if (active != null) {
@@ -1662,7 +1625,6 @@ export default class MainBackground {
this.keyService.clearKeys(userBeingLoggedOut),
this.cipherService.clear(userBeingLoggedOut),
this.folderService.clear(userBeingLoggedOut),
- this.collectionService.clear(userBeingLoggedOut),
this.vaultTimeoutSettingsService.clear(userBeingLoggedOut),
this.vaultFilterService.clear(),
this.biometricStateService.logout(userBeingLoggedOut),
@@ -1774,6 +1736,7 @@ export default class MainBackground {
/**
* Opens the popup to the given page
+ *
* @default ExtensionPageUrls.Index
* @deprecated Migrating to the browser actions service.
*/
diff --git a/apps/browser/src/key-management/lock/services/extension-lock-component.service.spec.ts b/apps/browser/src/key-management/lock/services/extension-lock-component.service.spec.ts
index 0ae0997fe4b..7678b65d29e 100644
--- a/apps/browser/src/key-management/lock/services/extension-lock-component.service.spec.ts
+++ b/apps/browser/src/key-management/lock/services/extension-lock-component.service.spec.ts
@@ -4,10 +4,8 @@ import { TestBed } from "@angular/core/testing";
import { mock, MockProxy } from "jest-mock-extended";
import { firstValueFrom, of } from "rxjs";
-import {
- PinServiceAbstraction,
- UserDecryptionOptionsServiceAbstraction,
-} from "@bitwarden/auth/common";
+import { UserDecryptionOptionsServiceAbstraction } from "@bitwarden/auth/common";
+import { PinServiceAbstraction } from "@bitwarden/common/key-management/pin/pin.service.abstraction";
import { VaultTimeoutSettingsService } from "@bitwarden/common/key-management/vault-timeout";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { UserId } from "@bitwarden/common/types/guid";
diff --git a/apps/browser/src/key-management/lock/services/extension-lock-component.service.ts b/apps/browser/src/key-management/lock/services/extension-lock-component.service.ts
index 52ad5a56c89..9f137d694a9 100644
--- a/apps/browser/src/key-management/lock/services/extension-lock-component.service.ts
+++ b/apps/browser/src/key-management/lock/services/extension-lock-component.service.ts
@@ -3,10 +3,8 @@
import { inject } from "@angular/core";
import { combineLatest, defer, firstValueFrom, map, Observable } from "rxjs";
-import {
- PinServiceAbstraction,
- UserDecryptionOptionsServiceAbstraction,
-} from "@bitwarden/auth/common";
+import { UserDecryptionOptionsServiceAbstraction } from "@bitwarden/auth/common";
+import { PinServiceAbstraction } from "@bitwarden/common/key-management/pin/pin.service.abstraction";
import { UserId } from "@bitwarden/common/types/guid";
import {
BiometricsService,
diff --git a/apps/browser/src/platform/browser/from-chrome-event.ts b/apps/browser/src/platform/browser/from-chrome-event.ts
index 28e57f58132..e0cd7a10b83 100644
--- a/apps/browser/src/platform/browser/from-chrome-event.ts
+++ b/apps/browser/src/platform/browser/from-chrome-event.ts
@@ -1,5 +1,3 @@
-// FIXME: Update this file to be type safe and remove this and next line
-// @ts-strict-ignore
import { Observable } from "rxjs";
import { BrowserApi } from "./browser-api";
@@ -26,13 +24,13 @@ export function fromChromeEvent
(
event: chrome.events.Event<(...args: T) => void>,
): Observable {
return new Observable((subscriber) => {
- const handler = (...args: T) => {
+ const handler = (...args: readonly unknown[]) => {
if (chrome.runtime.lastError) {
subscriber.error(chrome.runtime.lastError);
return;
}
- subscriber.next(args);
+ subscriber.next(args as T);
};
BrowserApi.addListener(event, handler);
diff --git a/apps/browser/src/platform/popup/layout/popup-header.component.html b/apps/browser/src/platform/popup/layout/popup-header.component.html
index c58bc258bf6..014ebc86411 100644
--- a/apps/browser/src/platform/popup/layout/popup-header.component.html
+++ b/apps/browser/src/platform/popup/layout/popup-header.component.html
@@ -1,16 +1,21 @@
+
+
`,
@@ -557,7 +557,10 @@ export const CenteredContent: Story = {
-
+
+
Page with no content
Before centering a div
One must first center oneself
@@ -651,7 +654,7 @@ export const WithVirtualScrollChild: Story = {
@defer (on immediate) {
-
+
{
const result = await sut.get("test");
// FIXME: Remove when updating file. Eslint update
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
- expect(encryptService.decryptString).toHaveBeenCalledWith(encrypted, sessionKey),
- expect(result).toEqual("decrypted");
+ (expect(encryptService.decryptString).toHaveBeenCalledWith(encrypted, sessionKey),
+ expect(result).toEqual("decrypted"));
});
it("caches the decrypted value when one is stored in local storage", async () => {
@@ -69,8 +69,8 @@ describe("LocalBackedSessionStorage", () => {
const result = await sut.get("test");
// FIXME: Remove when updating file. Eslint update
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
- expect(encryptService.decryptString).toHaveBeenCalledWith(encrypted, sessionKey),
- expect(result).toEqual("decrypted");
+ (expect(encryptService.decryptString).toHaveBeenCalledWith(encrypted, sessionKey),
+ expect(result).toEqual("decrypted"));
});
it("caches the decrypted value when one is stored in local storage", async () => {
diff --git a/apps/browser/src/platform/system-notifications/browser-system-notification.service.ts b/apps/browser/src/platform/system-notifications/browser-system-notification.service.ts
index ec919ceebce..168ceb05969 100644
--- a/apps/browser/src/platform/system-notifications/browser-system-notification.service.ts
+++ b/apps/browser/src/platform/system-notifications/browser-system-notification.service.ts
@@ -1,4 +1,5 @@
-import { Observable, Subject } from "rxjs";
+import { map, merge, Observable } from "rxjs";
+import { v4 as uuidv4 } from "uuid";
import { DeviceType } from "@bitwarden/common/enums";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
@@ -9,80 +10,84 @@ import {
SystemNotificationCreateInfo,
SystemNotificationEvent,
SystemNotificationsService,
-} from "@bitwarden/common/platform/notifications/system-notifications-service";
+} from "@bitwarden/common/platform/notifications/system-notifications.service";
+
+import { fromChromeEvent } from "../browser/from-chrome-event";
export class BrowserSystemNotificationService implements SystemNotificationsService {
- private systemNotificationClickedSubject = new Subject();
notificationClicked$: Observable;
constructor(
private logService: LogService,
private platformUtilsService: PlatformUtilsService,
) {
- this.notificationClicked$ = this.systemNotificationClickedSubject.asObservable();
+ this.notificationClicked$ = merge(
+ fromChromeEvent(chrome.notifications.onButtonClicked).pipe(
+ map(([notificationId, buttonIndex]) => ({
+ id: notificationId,
+ buttonIdentifier: buttonIndex,
+ })),
+ ),
+ fromChromeEvent(chrome.notifications.onClicked).pipe(
+ map(([notificationId]: [string]) => ({
+ id: notificationId,
+ buttonIdentifier: ButtonLocation.NotificationButton,
+ })),
+ ),
+ );
}
- async create(createInfo: SystemNotificationCreateInfo): Promise {
+ async create(createInfo: SystemNotificationCreateInfo): Promise {
try {
- switch (this.platformUtilsService.getDevice()) {
- case DeviceType.ChromeExtension:
- chrome.notifications.create(createInfo.id, {
- iconUrl: "https://avatars.githubusercontent.com/u/15990069?s=200",
- message: createInfo.body,
- type: "basic",
- title: createInfo.title,
- buttons: createInfo.buttons.map((value) => {
- return { title: value.title };
+ const notificationId = createInfo.id || uuidv4();
+ const deviceType = this.platformUtilsService.getDevice();
+
+ this.logService.error(DeviceType[deviceType]);
+
+ const notificationOptions: chrome.notifications.NotificationOptions = {
+ iconUrl: "https://avatars.githubusercontent.com/u/15990069?s=200",
+ message: createInfo.body,
+ type: "basic",
+ title: createInfo.title,
+ buttons: createInfo.buttons.map((value) => {
+ return { title: value.title };
+ }),
+ };
+
+ switch (deviceType) {
+ case DeviceType.FirefoxExtension:
+ // Firefox does not support buttons in notifications
+ delete notificationOptions.buttons;
+ break;
+ default:
+ break;
+ }
+
+ chrome.notifications.create(notificationId, notificationOptions);
+
+ // eslint-disable-next-line no-restricted-syntax
+ chrome.notifications.onButtonClicked.addListener(
+ (notificationId: string, buttonIndex: number) => {
+ this.notificationClicked$.subscribe({
+ next: () => ({
+ id: notificationId,
+ buttonIdentifier: buttonIndex,
}),
});
+ },
+ );
- // eslint-disable-next-line no-restricted-syntax
- chrome.notifications.onButtonClicked.addListener(
- (notificationId: string, buttonIndex: number) => {
- this.systemNotificationClickedSubject.next({
- id: notificationId,
- type: createInfo.type,
- buttonIdentifier: buttonIndex,
- });
- },
- );
+ // eslint-disable-next-line no-restricted-syntax
+ chrome.notifications.onClicked.addListener((notificationId: string) => {
+ this.notificationClicked$.subscribe({
+ next: () => ({
+ id: notificationId,
+ buttonIdentifier: ButtonLocation.NotificationButton,
+ }),
+ });
+ });
- // eslint-disable-next-line no-restricted-syntax
- chrome.notifications.onClicked.addListener((notificationId: string) => {
- this.systemNotificationClickedSubject.next({
- id: notificationId,
- type: createInfo.type,
- buttonIdentifier: ButtonLocation.NotificationButton,
- });
- });
-
- break;
- case DeviceType.FirefoxExtension:
- await browser.notifications.create(createInfo.id, {
- iconUrl: "https://avatars.githubusercontent.com/u/15990069?s=200",
- message: createInfo.title,
- type: "basic",
- title: createInfo.title,
- });
-
- browser.notifications.onButtonClicked.addListener(
- (notificationId: string, buttonIndex: number) => {
- this.systemNotificationClickedSubject.next({
- id: notificationId,
- type: createInfo.type,
- buttonIdentifier: buttonIndex,
- });
- },
- );
-
- browser.notifications.onClicked.addListener((notificationId: string) => {
- this.systemNotificationClickedSubject.next({
- id: notificationId,
- type: createInfo.type,
- buttonIdentifier: ButtonLocation.NotificationButton,
- });
- });
- }
+ return notificationId;
} catch (e) {
this.logService.error(
`Failed to create notification on ${this.platformUtilsService.getDevice()} with error: ${e}`,
@@ -95,16 +100,6 @@ export class BrowserSystemNotificationService implements SystemNotificationsServ
}
isSupported(): boolean {
- switch (this.platformUtilsService.getDevice()) {
- case DeviceType.EdgeExtension:
- case DeviceType.VivaldiExtension:
- case DeviceType.OperaExtension:
- case DeviceType.ChromeExtension:
- return true;
- case DeviceType.FirefoxExtension:
- return false;
- default:
- return false;
- }
+ return "notifications" in chrome;
}
}
diff --git a/apps/browser/src/popup/app-routing.module.ts b/apps/browser/src/popup/app-routing.module.ts
index 52a60d9c23d..f01809433e3 100644
--- a/apps/browser/src/popup/app-routing.module.ts
+++ b/apps/browser/src/popup/app-routing.module.ts
@@ -32,7 +32,6 @@ import {
RegistrationStartSecondaryComponent,
RegistrationStartSecondaryComponentData,
RegistrationUserAddIcon,
- SetPasswordJitComponent,
SsoComponent,
TwoFactorTimeoutIcon,
TwoFactorAuthComponent,
@@ -43,15 +42,13 @@ import {
VaultIcon,
} from "@bitwarden/auth/angular";
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
-import { AnonLayoutWrapperComponent, AnonLayoutWrapperData, Icons } from "@bitwarden/components";
+import { AnonLayoutWrapperData, Icons } from "@bitwarden/components";
import { LockComponent } from "@bitwarden/key-management-ui";
import { AccountSwitcherComponent } from "../auth/popup/account-switching/account-switcher.component";
import { fido2AuthGuard } from "../auth/popup/guards/fido2-auth.guard";
-import { SetPasswordComponent } from "../auth/popup/set-password.component";
import { AccountSecurityComponent } from "../auth/popup/settings/account-security.component";
import { ExtensionDeviceManagementComponent } from "../auth/popup/settings/extension-device-management.component";
-import { UpdateTempPasswordComponent } from "../auth/popup/update-temp-password.component";
import { Fido2Component } from "../autofill/popup/fido2/fido2.component";
import { AutofillComponent } from "../autofill/popup/settings/autofill.component";
import { BlockedDomainsComponent } from "../autofill/popup/settings/blocked-domains.component";
@@ -180,11 +177,6 @@ const routes: Routes = [
elevation: 1,
} satisfies RouteDataProperties & ExtensionAnonLayoutWrapperData,
},
- {
- path: "set-password",
- component: SetPasswordComponent,
- data: { elevation: 1 } satisfies RouteDataProperties,
- },
{
path: "remove-password",
component: RemovePasswordComponent,
@@ -337,20 +329,6 @@ const routes: Routes = [
canActivate: [authGuard],
data: { elevation: 1 } satisfies RouteDataProperties,
},
- {
- path: "update-temp-password",
- component: UpdateTempPasswordComponent,
- canActivate: [
- canAccessFeature(
- FeatureFlag.PM16117_ChangeExistingPasswordRefactor,
- false,
- `/change-password`,
- false,
- ),
- authGuard,
- ],
- data: { elevation: 1 } satisfies RouteDataProperties,
- },
{
path: "",
component: ExtensionAnonLayoutWrapperComponent,
@@ -398,7 +376,7 @@ const routes: Routes = [
},
{
path: "set-initial-password",
- canActivate: [canAccessFeature(FeatureFlag.PM16117_SetInitialPasswordRefactor), authGuard],
+ canActivate: [authGuard],
component: SetInitialPasswordComponent,
data: {
elevation: 1,
@@ -586,29 +564,7 @@ const routes: Routes = [
component: ChangePasswordComponent,
},
],
- canActivate: [
- canAccessFeature(FeatureFlag.PM16117_ChangeExistingPasswordRefactor),
- authGuard,
- ],
- },
- ],
- },
- {
- path: "",
- component: AnonLayoutWrapperComponent,
- children: [
- {
- path: "set-password-jit",
- component: SetPasswordJitComponent,
- data: {
- pageTitle: {
- key: "joinOrganization",
- },
- pageSubtitle: {
- key: "finishJoiningThisOrganizationBySettingAMasterPassword",
- },
- elevation: 1,
- } satisfies RouteDataProperties & AnonLayoutWrapperData,
+ canActivate: [authGuard],
},
],
},
diff --git a/apps/browser/src/popup/app.module.ts b/apps/browser/src/popup/app.module.ts
index 77c87838ff7..5150c51d765 100644
--- a/apps/browser/src/popup/app.module.ts
+++ b/apps/browser/src/popup/app.module.ts
@@ -26,10 +26,7 @@ import {
import { AccountComponent } from "../auth/popup/account-switching/account.component";
import { CurrentAccountComponent } from "../auth/popup/account-switching/current-account.component";
-import { SetPasswordComponent } from "../auth/popup/set-password.component";
import { AccountSecurityComponent } from "../auth/popup/settings/account-security.component";
-import { VaultTimeoutInputComponent } from "../auth/popup/settings/vault-timeout-input.component";
-import { UpdateTempPasswordComponent } from "../auth/popup/update-temp-password.component";
import { AutofillComponent } from "../autofill/popup/settings/autofill.component";
import { NotificationsSettingsComponent } from "../autofill/popup/settings/notifications.component";
import { RemovePasswordComponent } from "../key-management/key-connector/remove-password.component";
@@ -96,11 +93,8 @@ import "../platform/popup/locales";
AppComponent,
ColorPasswordPipe,
ColorPasswordCountPipe,
- SetPasswordComponent,
TabsV2Component,
- UpdateTempPasswordComponent,
UserVerificationComponent,
- VaultTimeoutInputComponent,
RemovePasswordComponent,
EnvironmentSelectorComponent,
],
diff --git a/apps/browser/src/popup/services/init.service.ts b/apps/browser/src/popup/services/init.service.ts
index f29745e6f59..9e750ae7341 100644
--- a/apps/browser/src/popup/services/init.service.ts
+++ b/apps/browser/src/popup/services/init.service.ts
@@ -3,9 +3,6 @@ import { inject, Inject, Injectable } from "@angular/core";
import { AbstractThemingService } from "@bitwarden/angular/platform/services/theming/theming.service.abstraction";
import { TwoFactorService } from "@bitwarden/common/auth/abstractions/two-factor.service";
-import { BulkEncryptService } from "@bitwarden/common/key-management/crypto/abstractions/bulk-encrypt.service";
-import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service";
-import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { LogService as LogServiceAbstraction } from "@bitwarden/common/platform/abstractions/log.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
@@ -30,9 +27,6 @@ export class InitService {
private themingService: AbstractThemingService,
private sdkLoadService: SdkLoadService,
private viewCacheService: PopupViewCacheService,
- private configService: ConfigService,
- private encryptService: EncryptService,
- private bulkEncryptService: BulkEncryptService,
@Inject(DOCUMENT) private document: Document,
) {}
@@ -40,12 +34,6 @@ export class InitService {
return async () => {
await this.sdkLoadService.loadAndInit();
await this.stateService.init({ runMigrations: false }); // Browser background is responsible for migrations
- this.configService.serverConfig$.subscribe((newConfig) => {
- if (newConfig != null) {
- this.encryptService.onServerConfigChange(newConfig);
- this.bulkEncryptService.onServerConfigChange(newConfig);
- }
- });
await this.i18nService.init();
this.twoFactorService.init();
await this.viewCacheService.init();
diff --git a/apps/browser/src/popup/services/services.module.ts b/apps/browser/src/popup/services/services.module.ts
index 53739f65f07..a5979bdd233 100644
--- a/apps/browser/src/popup/services/services.module.ts
+++ b/apps/browser/src/popup/services/services.module.ts
@@ -33,7 +33,6 @@ import {
import {
LockService,
LoginEmailService,
- PinServiceAbstraction,
SsoUrlService,
LogoutService,
} from "@bitwarden/auth/common";
@@ -67,6 +66,7 @@ import { CryptoFunctionService } from "@bitwarden/common/key-management/crypto/a
import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service";
import { WebCryptoFunctionService } from "@bitwarden/common/key-management/crypto/services/web-crypto-function.service";
import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/key-management/master-password/abstractions/master-password.service.abstraction";
+import { PinServiceAbstraction } from "@bitwarden/common/key-management/pin/pin.service.abstraction";
import {
VaultTimeoutService,
VaultTimeoutStringType,
@@ -97,7 +97,7 @@ import { Message, MessageListener, MessageSender } from "@bitwarden/common/platf
import { SubjectMessageSender } from "@bitwarden/common/platform/messaging/internal";
import { flagEnabled } from "@bitwarden/common/platform/misc/flags";
import { ServerNotificationsService } from "@bitwarden/common/platform/notifications";
-import { SystemNotificationsService } from "@bitwarden/common/platform/notifications/system-notifications-service";
+import { SystemNotificationsService } from "@bitwarden/common/platform/notifications/system-notifications.service";
import { TaskSchedulerService } from "@bitwarden/common/platform/scheduling";
import { ConsoleLogService } from "@bitwarden/common/platform/services/console-log.service";
import { ContainerService } from "@bitwarden/common/platform/services/container.service";
diff --git a/apps/browser/src/vault/popup/components/vault-v2/add-edit/add-edit-v2.component.spec.ts b/apps/browser/src/vault/popup/components/vault-v2/add-edit/add-edit-v2.component.spec.ts
index 216ec1c3f1b..1bffcd9ad51 100644
--- a/apps/browser/src/vault/popup/components/vault-v2/add-edit/add-edit-v2.component.spec.ts
+++ b/apps/browser/src/vault/popup/components/vault-v2/add-edit/add-edit-v2.component.spec.ts
@@ -26,6 +26,7 @@ import {
} from "@bitwarden/vault";
import { BrowserFido2UserInterfaceSession } from "../../../../../autofill/fido2/services/browser-fido2-user-interface.service";
+import { BrowserApi } from "../../../../../platform/browser/browser-api";
import BrowserPopupUtils from "../../../../../platform/browser/browser-popup-utils";
import { PopupRouterCacheService } from "../../../../../platform/popup/view-cache/popup-router-cache.service";
import { PopupCloseWarningService } from "../../../../../popup/services/popup-close-warning.service";
@@ -309,6 +310,19 @@ describe("AddEditV2Component", () => {
expect(navigate).not.toHaveBeenCalled();
expect(back).toHaveBeenCalled();
});
+
+ it.each(["add", "edit", "partial-edit"])(
+ "sends the addEditCipherSubmitted message when a cipher is edited, added or partially edited",
+ async (mode) => {
+ const sendMessageSpy = jest.spyOn(BrowserApi, "sendMessage");
+ component.config.mode = mode;
+
+ await component.onCipherSaved({ id: "123-456-789" } as CipherView);
+
+ expect(sendMessageSpy).toHaveBeenCalled();
+ expect(sendMessageSpy).toHaveBeenCalledWith("addEditCipherSubmitted");
+ },
+ );
});
describe("handleBackButton", () => {
diff --git a/apps/browser/src/vault/popup/components/vault-v2/add-edit/add-edit-v2.component.ts b/apps/browser/src/vault/popup/components/vault-v2/add-edit/add-edit-v2.component.ts
index f019636e690..3985fc85a54 100644
--- a/apps/browser/src/vault/popup/components/vault-v2/add-edit/add-edit-v2.component.ts
+++ b/apps/browser/src/vault/popup/components/vault-v2/add-edit/add-edit-v2.component.ts
@@ -268,6 +268,7 @@ export class AddEditV2Component implements OnInit {
// Clear popup history so after closing/reopening, Back won’t return to the add-edit form
await this.popupRouterCacheService.setHistory([]);
}
+ await BrowserApi.sendMessage("addEditCipherSubmitted");
}
subscribeToParams(): void {
diff --git a/apps/browser/src/vault/popup/components/vault-v2/assign-collections/assign-collections.component.ts b/apps/browser/src/vault/popup/components/vault-v2/assign-collections/assign-collections.component.ts
index 8374cc254a9..0b7346c8613 100644
--- a/apps/browser/src/vault/popup/components/vault-v2/assign-collections/assign-collections.component.ts
+++ b/apps/browser/src/vault/popup/components/vault-v2/assign-collections/assign-collections.component.ts
@@ -10,6 +10,7 @@ import { Observable, combineLatest, filter, first, map, switchMap } from "rxjs";
import { CollectionService } from "@bitwarden/admin-console/common";
import { JslibModule } from "@bitwarden/angular/jslib.module";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
+import { getUserId } from "@bitwarden/common/auth/services/account.service";
import { OrganizationId } from "@bitwarden/common/types/guid";
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
@@ -70,7 +71,12 @@ export class AssignCollections {
),
);
- combineLatest([cipher$, this.collectionService.decryptedCollections$])
+ const decryptedCollection$ = this.accountService.activeAccount$.pipe(
+ getUserId,
+ switchMap((userId) => this.collectionService.decryptedCollections$(userId)),
+ );
+
+ combineLatest([cipher$, decryptedCollection$])
.pipe(takeUntilDestroyed(), first())
.subscribe(([cipherView, collections]) => {
let availableCollections = collections;
diff --git a/apps/browser/src/vault/popup/components/vault-v2/attachments/open-attachments/open-attachments.component.spec.ts b/apps/browser/src/vault/popup/components/vault-v2/attachments/open-attachments/open-attachments.component.spec.ts
index 19779d73a11..c669ba167df 100644
--- a/apps/browser/src/vault/popup/components/vault-v2/attachments/open-attachments/open-attachments.component.spec.ts
+++ b/apps/browser/src/vault/popup/components/vault-v2/attachments/open-attachments/open-attachments.component.spec.ts
@@ -10,7 +10,7 @@ import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abs
import { ProductTierType } from "@bitwarden/common/billing/enums";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { Utils } from "@bitwarden/common/platform/misc/utils";
-import { CipherId, UserId } from "@bitwarden/common/types/guid";
+import { CipherId, OrganizationId, UserId } from "@bitwarden/common/types/guid";
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
import { CipherType } from "@bitwarden/common/vault/enums";
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
@@ -163,7 +163,7 @@ describe("OpenAttachmentsComponent", () => {
it("sets `cipherIsAPartOfFreeOrg` to true when the cipher is a part of a free organization", async () => {
cipherView.organizationId = "888-333-333";
org.productTierType = ProductTierType.Free;
- org.id = cipherView.organizationId;
+ org.id = cipherView.organizationId as OrganizationId;
await component.ngOnInit();
@@ -173,7 +173,7 @@ describe("OpenAttachmentsComponent", () => {
it("sets `cipherIsAPartOfFreeOrg` to false when the organization is not free", async () => {
cipherView.organizationId = "888-333-333";
org.productTierType = ProductTierType.Families;
- org.id = cipherView.organizationId;
+ org.id = cipherView.organizationId as OrganizationId;
await component.ngOnInit();
diff --git a/apps/browser/src/vault/popup/components/vault-v2/item-more-options/item-more-options.component.ts b/apps/browser/src/vault/popup/components/vault-v2/item-more-options/item-more-options.component.ts
index ce16ec2f3e0..9ee3c3a6e41 100644
--- a/apps/browser/src/vault/popup/components/vault-v2/item-more-options/item-more-options.component.ts
+++ b/apps/browser/src/vault/popup/components/vault-v2/item-more-options/item-more-options.component.ts
@@ -78,7 +78,7 @@ export class ItemMoreOptionsComponent {
switchMap(([c, restrictedTypes]) => {
// This will check for restrictions from org policies before allowing cloning.
const isItemRestricted = restrictedTypes.some(
- (restrictType) => restrictType.cipherType === c.type,
+ (restrictType) => restrictType.cipherType === CipherViewLikeUtils.getType(c),
);
if (!isItemRestricted) {
return this.cipherAuthorizationService.canCloneCipher$(c);
@@ -93,7 +93,7 @@ export class ItemMoreOptionsComponent {
switchMap((userId) => {
return combineLatest([
this.organizationService.hasOrganizations(userId),
- this.collectionService.decryptedCollections$,
+ this.collectionService.decryptedCollections$(userId),
]).pipe(
map(([hasOrgs, collections]) => {
const canEditCollections = collections.some((c) => !c.readOnly);
diff --git a/apps/browser/src/vault/popup/services/vault-popup-items.service.spec.ts b/apps/browser/src/vault/popup/services/vault-popup-items.service.spec.ts
index 48788ea5ae9..32974da162d 100644
--- a/apps/browser/src/vault/popup/services/vault-popup-items.service.spec.ts
+++ b/apps/browser/src/vault/popup/services/vault-popup-items.service.spec.ts
@@ -139,7 +139,7 @@ describe("VaultPopupItemsService", () => {
];
organizationServiceMock.organizations$.mockReturnValue(new BehaviorSubject([mockOrg]));
- collectionService.decryptedCollections$ = new BehaviorSubject(mockCollections);
+ collectionService.decryptedCollections$.mockReturnValue(new BehaviorSubject(mockCollections));
activeUserLastSync$ = new BehaviorSubject(new Date());
syncServiceMock.activeUserLastSync$.mockReturnValue(activeUserLastSync$);
diff --git a/apps/browser/src/vault/popup/services/vault-popup-items.service.ts b/apps/browser/src/vault/popup/services/vault-popup-items.service.ts
index b2d4fd1b262..9d44eef2e47 100644
--- a/apps/browser/src/vault/popup/services/vault-popup-items.service.ts
+++ b/apps/browser/src/vault/popup/services/vault-popup-items.service.ts
@@ -72,6 +72,11 @@ export class VaultPopupItemsService {
private organizations$ = this.activeUserId$.pipe(
switchMap((userId) => this.organizationService.organizations$(userId)),
);
+
+ private decryptedCollections$ = this.activeUserId$.pipe(
+ switchMap((userId) => this.collectionService.decryptedCollections$(userId)),
+ );
+
/**
* Observable that contains the list of other cipher types that should be shown
* in the autofill section of the Vault tab. Depends on vault settings.
@@ -130,7 +135,7 @@ export class VaultPopupItemsService {
private _activeCipherList$: Observable = this._allDecryptedCiphers$.pipe(
switchMap((ciphers) =>
- combineLatest([this.organizations$, this.collectionService.decryptedCollections$]).pipe(
+ combineLatest([this.organizations$, this.decryptedCollections$]).pipe(
map(([organizations, collections]) => {
const orgMap = Object.fromEntries(organizations.map((org) => [org.id, org]));
const collectionMap = Object.fromEntries(collections.map((col) => [col.id, col]));
@@ -291,7 +296,7 @@ export class VaultPopupItemsService {
*/
deletedCiphers$: Observable = this._allDecryptedCiphers$.pipe(
switchMap((ciphers) =>
- combineLatest([this.organizations$, this.collectionService.decryptedCollections$]).pipe(
+ combineLatest([this.organizations$, this.decryptedCollections$]).pipe(
map(([organizations, collections]) => {
const orgMap = Object.fromEntries(organizations.map((org) => [org.id, org]));
const collectionMap = Object.fromEntries(collections.map((col) => [col.id, col]));
diff --git a/apps/browser/src/vault/popup/services/vault-popup-list-filters.service.spec.ts b/apps/browser/src/vault/popup/services/vault-popup-list-filters.service.spec.ts
index 9f1bd6e6e55..ebaeaeb6076 100644
--- a/apps/browser/src/vault/popup/services/vault-popup-list-filters.service.spec.ts
+++ b/apps/browser/src/vault/popup/services/vault-popup-list-filters.service.spec.ts
@@ -20,6 +20,7 @@ import { UserId } from "@bitwarden/common/types/guid";
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
import { FolderService } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction";
import { CipherType } from "@bitwarden/common/vault/enums";
+import { TreeNode } from "@bitwarden/common/vault/models/domain/tree-node";
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
import { FolderView } from "@bitwarden/common/vault/models/view/folder.view";
import {
@@ -58,7 +59,7 @@ describe("VaultPopupListFiltersService", () => {
};
const collectionService = {
- decryptedCollections$,
+ decryptedCollections$: () => decryptedCollections$,
getAllNested: () => Promise.resolve([]),
} as unknown as CollectionService;
@@ -106,7 +107,7 @@ describe("VaultPopupListFiltersService", () => {
signal: jest.fn(() => mockCachedSignal),
};
- collectionService.getAllNested = () => Promise.resolve([]);
+ collectionService.getAllNested = () => [];
TestBed.configureTestingModule({
providers: [
{
@@ -382,14 +383,7 @@ describe("VaultPopupListFiltersService", () => {
beforeEach(() => {
decryptedCollections$.next(testCollections);
- collectionService.getAllNested = () =>
- Promise.resolve(
- testCollections.map((c) => ({
- children: [],
- node: c,
- parent: null,
- })),
- );
+ collectionService.getAllNested = () => testCollections.map((c) => new TreeNode(c, null));
});
it("returns all collections", (done) => {
@@ -755,15 +749,13 @@ function createSeededVaultPopupListFiltersService(
} as any;
const collectionServiceMock = {
- decryptedCollections$: seededCollections$,
+ decryptedCollections$: () => seededCollections$,
getAllNested: () =>
- Promise.resolve(
- seededCollections$.value.map((c) => ({
- children: [],
- node: c,
- parent: null,
- })),
- ),
+ seededCollections$.value.map((c) => ({
+ children: [],
+ node: c,
+ parent: null,
+ })),
} as any;
const folderServiceMock = {
diff --git a/apps/browser/src/vault/popup/services/vault-popup-list-filters.service.ts b/apps/browser/src/vault/popup/services/vault-popup-list-filters.service.ts
index 7af6fb5f212..adc0589e7e8 100644
--- a/apps/browser/src/vault/popup/services/vault-popup-list-filters.service.ts
+++ b/apps/browser/src/vault/popup/services/vault-popup-list-filters.service.ts
@@ -6,7 +6,6 @@ import {
debounceTime,
distinctUntilChanged,
filter,
- from,
map,
Observable,
shareReplay,
@@ -446,7 +445,7 @@ export class VaultPopupListFiltersService {
this.filters$.pipe(
distinctUntilChanged((prev, curr) => prev.organization?.id === curr.organization?.id),
),
- this.collectionService.decryptedCollections$,
+ this.collectionService.decryptedCollections$(userId),
this.organizationService.memberOrganizations$(userId),
this.configService.getFeatureFlag$(FeatureFlag.CreateDefaultLocation),
]),
@@ -463,16 +462,11 @@ export class VaultPopupListFiltersService {
}
return sortDefaultCollections(filtered, orgs, this.i18nService.collator);
}),
- switchMap((collections) => {
- return from(this.collectionService.getAllNested(collections)).pipe(
- map(
- (nested) =>
- new DynamicTreeNode({
- fullList: collections,
- nestedList: nested,
- }),
- ),
- );
+ map((fullList) => {
+ return new DynamicTreeNode({
+ fullList,
+ nestedList: this.collectionService.getAllNested(fullList),
+ });
}),
map((tree) =>
tree.nestedList.map((c) => this.convertToChipSelectOption(c, "bwi-collection-shared")),
diff --git a/apps/browser/store/locales/sv/copy.resx b/apps/browser/store/locales/sv/copy.resx
index c37095ec167..8f3564f30c3 100644
--- a/apps/browser/store/locales/sv/copy.resx
+++ b/apps/browser/store/locales/sv/copy.resx
@@ -121,7 +121,7 @@
Bitwarden Lösenordshanterare
- Hemma, på jobbet eller på resande fot säkrar Bitwarden enkelt alla dina lösenord, passkeys, och känslig information.
+ Hemma, på jobbet eller på resande fot säkrar Bitwarden enkelt alla dina lösenord, inloggningsnycklar och känslig information.
Erkänd som den bästa lösenordshanteraren av PCMag, WIRED, The Verge, CNET, G2 och många fler!
@@ -169,7 +169,7 @@ End-to-end krypterade lösningar för hantering av referenser från Bitwarden g
- Hemma, på jobbet eller på resande fot säkrar Bitwarden enkelt alla dina lösenord, passkeys, och känslig information.
+ Hemma, på jobbet eller på resande fot säkrar Bitwarden enkelt alla dina lösenord, inloggningsnycklar och känslig information.
Synkronisera och kom åt ditt valv från flera enheter
diff --git a/apps/browser/tsconfig.json b/apps/browser/tsconfig.json
index a554120bd1e..0fd6cac4230 100644
--- a/apps/browser/tsconfig.json
+++ b/apps/browser/tsconfig.json
@@ -3,7 +3,6 @@
"include": [
"src",
"../../libs/common/src/autofill/constants",
- "../../libs/common/custom-matchers.d.ts",
- "../../libs/common/src/key-management/crypto/services/encrypt.worker.ts"
+ "../../libs/common/custom-matchers.d.ts"
]
}
diff --git a/apps/browser/webpack.config.js b/apps/browser/webpack.config.js
index 90638f4e334..551225231f7 100644
--- a/apps/browser/webpack.config.js
+++ b/apps/browser/webpack.config.js
@@ -199,7 +199,6 @@ const mainConfig = {
"./src/autofill/overlay/inline-menu/pages/list/bootstrap-autofill-inline-menu-list.ts",
"overlay/menu":
"./src/autofill/overlay/inline-menu/pages/menu-container/bootstrap-autofill-inline-menu-container.ts",
- "encrypt-worker": "../../libs/common/src/key-management/crypto/services/encrypt.worker.ts",
"content/send-on-installed-message": "./src/vault/content/send-on-installed-message.ts",
"content/send-popup-open-message": "./src/vault/content/send-popup-open-message.ts",
},
diff --git a/apps/cli/src/admin-console/models/request/organization-collection.request.ts b/apps/cli/src/admin-console/models/request/organization-collection.request.ts
index b5f796afe2d..14714e4758b 100644
--- a/apps/cli/src/admin-console/models/request/organization-collection.request.ts
+++ b/apps/cli/src/admin-console/models/request/organization-collection.request.ts
@@ -1,13 +1,14 @@
// FIXME: Update this file to be type safe and remove this and next line
// @ts-strict-ignore
import { CollectionExport } from "@bitwarden/common/models/export/collection.export";
+import { OrganizationId } from "@bitwarden/common/types/guid";
import { SelectionReadOnly } from "../selection-read-only";
export class OrganizationCollectionRequest extends CollectionExport {
static template(): OrganizationCollectionRequest {
const req = new OrganizationCollectionRequest();
- req.organizationId = "00000000-0000-0000-0000-000000000000";
+ req.organizationId = "00000000-0000-0000-0000-000000000000" as OrganizationId;
req.name = "Collection name";
req.externalId = null;
req.groups = [SelectionReadOnly.template(), SelectionReadOnly.template()];
diff --git a/apps/cli/src/auth/commands/login.command.ts b/apps/cli/src/auth/commands/login.command.ts
index 3ceac859c43..d25e9a70d88 100644
--- a/apps/cli/src/auth/commands/login.command.ts
+++ b/apps/cli/src/auth/commands/login.command.ts
@@ -428,7 +428,8 @@ export class LoginCommand {
);
const request = new PasswordRequest();
- request.masterPasswordHash = await this.keyService.hashMasterKey(currentPassword, null);
+ const masterKey = await this.keyService.getOrDeriveMasterKey(currentPassword, userId);
+ request.masterPasswordHash = await this.keyService.hashMasterKey(currentPassword, masterKey);
request.masterPasswordHint = hint;
request.newMasterPasswordHash = newPasswordHash;
request.key = newUserKey[1].encryptedString;
diff --git a/apps/cli/src/commands/edit.command.ts b/apps/cli/src/commands/edit.command.ts
index ebf877011b7..c2881568656 100644
--- a/apps/cli/src/commands/edit.command.ts
+++ b/apps/cli/src/commands/edit.command.ts
@@ -4,6 +4,8 @@ import { firstValueFrom } from "rxjs";
import { CollectionRequest } from "@bitwarden/admin-console/common";
import { ApiService } from "@bitwarden/common/abstractions/api.service";
+import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
+import { PolicyType } from "@bitwarden/common/admin-console/enums";
import { SelectionReadOnlyRequest } from "@bitwarden/common/admin-console/models/request/selection-read-only.request";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { getUserId } from "@bitwarden/common/auth/services/account.service";
@@ -36,6 +38,7 @@ export class EditCommand {
private folderApiService: FolderApiServiceAbstraction,
private accountService: AccountService,
private cliRestrictedItemTypesService: CliRestrictedItemTypesService,
+ private policyService: PolicyService,
) {}
async run(
@@ -104,6 +107,18 @@ export class EditCommand {
return Response.error("Editing this item type is restricted by organizational policy.");
}
+ const isPersonalVaultItem = cipherView.organizationId == null;
+
+ const organizationOwnershipPolicyApplies = await firstValueFrom(
+ this.policyService.policyAppliesToUser$(PolicyType.OrganizationDataOwnership, activeUserId),
+ );
+
+ if (isPersonalVaultItem && organizationOwnershipPolicyApplies) {
+ return Response.error(
+ "An organization policy restricts editing this cipher. Please use the share command first before modifying it.",
+ );
+ }
+
const encCipher = await this.cipherService.encrypt(cipherView, activeUserId);
try {
const updatedCipher = await this.cipherService.updateWithServer(encCipher);
@@ -155,7 +170,7 @@ export class EditCommand {
let folderView = await folder.decrypt();
folderView = FolderExport.toView(req, folderView);
- const userKey = await this.keyService.getUserKeyWithLegacySupport(activeUserId);
+ const userKey = await this.keyService.getUserKey(activeUserId);
const encFolder = await this.folderService.encrypt(folderView, userKey);
try {
const folder = await this.folderApiService.save(encFolder, activeUserId);
diff --git a/apps/cli/src/commands/get.command.ts b/apps/cli/src/commands/get.command.ts
index b20052fbb53..756316cba43 100644
--- a/apps/cli/src/commands/get.command.ts
+++ b/apps/cli/src/commands/get.command.ts
@@ -24,6 +24,7 @@ import { LoginUriExport } from "@bitwarden/common/models/export/login-uri.export
import { LoginExport } from "@bitwarden/common/models/export/login.export";
import { SecureNoteExport } from "@bitwarden/common/models/export/secure-note.export";
import { ErrorResponse } from "@bitwarden/common/models/response/error.response";
+import { getById } from "@bitwarden/common/platform/misc";
import { Utils } from "@bitwarden/common/platform/misc/utils";
import { CipherId, OrganizationId, UserId } from "@bitwarden/common/types/guid";
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
@@ -442,8 +443,11 @@ export class GetCommand extends DownloadCommand {
private async getCollection(id: string) {
let decCollection: CollectionView = null;
+ const activeUserId = await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId));
if (Utils.isGuid(id)) {
- const collection = await this.collectionService.get(id);
+ const collection = await firstValueFrom(
+ this.collectionService.encryptedCollections$(activeUserId).pipe(getById(id)),
+ );
if (collection != null) {
const orgKeys = await firstValueFrom(this.keyService.activeUserOrgKeys$);
decCollection = await collection.decrypt(
@@ -451,7 +455,9 @@ export class GetCommand extends DownloadCommand {
);
}
} else if (id.trim() !== "") {
- let collections = await this.collectionService.getAllDecrypted();
+ let collections = await firstValueFrom(
+ this.collectionService.decryptedCollections$(activeUserId),
+ );
collections = CliUtils.searchCollections(collections, id);
if (collections.length > 1) {
return Response.multipleResults(collections.map((c) => c.id));
diff --git a/apps/cli/src/commands/list.command.ts b/apps/cli/src/commands/list.command.ts
index 517050728c0..94abd97d6eb 100644
--- a/apps/cli/src/commands/list.command.ts
+++ b/apps/cli/src/commands/list.command.ts
@@ -20,6 +20,7 @@ import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.servi
import { FolderService } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction";
import { SearchService } from "@bitwarden/common/vault/abstractions/search.service";
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
+import { KeyService } from "@bitwarden/key-management";
import { CollectionResponse } from "../admin-console/models/response/collection.response";
import { OrganizationUserResponse } from "../admin-console/models/response/organization-user.response";
@@ -42,6 +43,7 @@ export class ListCommand {
private apiService: ApiService,
private eventCollectionService: EventCollectionService,
private accountService: AccountService,
+ private keyService: KeyService,
private cliRestrictedItemTypesService: CliRestrictedItemTypesService,
) {}
@@ -158,7 +160,10 @@ export class ListCommand {
}
private async listCollections(options: Options) {
- let collections = await this.collectionService.getAllDecrypted();
+ const activeUserId = await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId));
+ let collections = await firstValueFrom(
+ this.collectionService.decryptedCollections$(activeUserId),
+ );
if (options.organizationId != null) {
collections = collections.filter((c) => {
@@ -178,13 +183,13 @@ export class ListCommand {
}
private async listOrganizationCollections(options: Options) {
+ const userId = await firstValueFrom(getUserId(this.accountService.activeAccount$));
if (options.organizationId == null || options.organizationId === "") {
return Response.badRequest("`organizationid` option is required.");
}
if (!Utils.isGuid(options.organizationId)) {
return Response.badRequest("`" + options.organizationId + "` is not a GUID.");
}
- const userId = await firstValueFrom(getUserId(this.accountService.activeAccount$));
if (!userId) {
return Response.badRequest("No user found.");
}
@@ -207,7 +212,13 @@ export class ListCommand {
const collections = response.data
.filter((c) => c.organizationId === options.organizationId)
.map((r) => new Collection(new CollectionData(r as ApiCollectionDetailsResponse)));
- let decCollections = await this.collectionService.decryptMany(collections);
+ const orgKeys = await firstValueFrom(this.keyService.orgKeys$(userId));
+ if (orgKeys == null) {
+ throw new Error("Organization keys not found.");
+ }
+ let decCollections = await firstValueFrom(
+ this.collectionService.decryptMany$(collections, orgKeys),
+ );
if (options.search != null && options.search.trim() !== "") {
decCollections = CliUtils.searchCollections(decCollections, options.search);
}
diff --git a/apps/cli/src/oss-serve-configurator.ts b/apps/cli/src/oss-serve-configurator.ts
index 14e6ace3b34..a460fa270a8 100644
--- a/apps/cli/src/oss-serve-configurator.ts
+++ b/apps/cli/src/oss-serve-configurator.ts
@@ -79,6 +79,7 @@ export class OssServeConfigurator {
this.serviceContainer.apiService,
this.serviceContainer.eventCollectionService,
this.serviceContainer.accountService,
+ this.serviceContainer.keyService,
this.serviceContainer.cliRestrictedItemTypesService,
);
this.createCommand = new CreateCommand(
@@ -102,6 +103,7 @@ export class OssServeConfigurator {
this.serviceContainer.folderApiService,
this.serviceContainer.accountService,
this.serviceContainer.cliRestrictedItemTypesService,
+ this.serviceContainer.policyService,
);
this.generateCommand = new GenerateCommand(
this.serviceContainer.passwordGenerationService,
diff --git a/apps/cli/src/service-container/service-container.ts b/apps/cli/src/service-container/service-container.ts
index 78f961973d9..0ec24768b79 100644
--- a/apps/cli/src/service-container/service-container.ts
+++ b/apps/cli/src/service-container/service-container.ts
@@ -16,8 +16,6 @@ import {
AuthRequestService,
LoginStrategyService,
LoginStrategyServiceAbstraction,
- PinService,
- PinServiceAbstraction,
UserDecryptionOptionsService,
SsoUrlService,
AuthRequestApiServiceAbstraction,
@@ -44,6 +42,7 @@ import {
} from "@bitwarden/common/auth/services/account.service";
import { AuthService } from "@bitwarden/common/auth/services/auth.service";
import { AvatarService } from "@bitwarden/common/auth/services/avatar.service";
+import { DefaultActiveUserAccessor } from "@bitwarden/common/auth/services/default-active-user.accessor";
import { DevicesApiServiceImplementation } from "@bitwarden/common/auth/services/devices-api.service.implementation";
import { MasterPasswordApiService } from "@bitwarden/common/auth/services/master-password/master-password-api.service.implementation";
import { TokenService } from "@bitwarden/common/auth/services/token.service";
@@ -62,12 +61,13 @@ import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abs
import { DefaultBillingAccountProfileStateService } from "@bitwarden/common/billing/services/account/billing-account-profile-state.service";
import { ClientType } from "@bitwarden/common/enums";
import { EncryptServiceImplementation } from "@bitwarden/common/key-management/crypto/services/encrypt.service.implementation";
-import { FallbackBulkEncryptService } from "@bitwarden/common/key-management/crypto/services/fallback-bulk-encrypt.service";
import { DeviceTrustServiceAbstraction } from "@bitwarden/common/key-management/device-trust/abstractions/device-trust.service.abstraction";
import { DeviceTrustService } from "@bitwarden/common/key-management/device-trust/services/device-trust.service.implementation";
import { KeyConnectorService } from "@bitwarden/common/key-management/key-connector/services/key-connector.service";
import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/key-management/master-password/abstractions/master-password.service.abstraction";
import { MasterPasswordService } from "@bitwarden/common/key-management/master-password/services/master-password.service";
+import { PinServiceAbstraction } from "@bitwarden/common/key-management/pin/pin.service.abstraction";
+import { PinService } from "@bitwarden/common/key-management/pin/pin.service.implementation";
import {
DefaultVaultTimeoutService,
DefaultVaultTimeoutSettingsService,
@@ -290,7 +290,6 @@ export class ServiceContainer {
cipherAuthorizationService: CipherAuthorizationService;
ssoUrlService: SsoUrlService;
masterPasswordApiService: MasterPasswordApiServiceAbstraction;
- bulkEncryptService: FallbackBulkEncryptService;
cipherEncryptionService: CipherEncryptionService;
restrictedItemTypesService: RestrictedItemTypesService;
cliRestrictedItemTypesService: CliRestrictedItemTypesService;
@@ -325,7 +324,6 @@ export class ServiceContainer {
this.logService,
true,
);
- this.bulkEncryptService = new FallbackBulkEncryptService(this.encryptService);
this.storageService = new LowdbStorageService(this.logService, null, p, false, true);
this.secureStorageService = new NodeEnvSecureStorageService(
this.storageService,
@@ -380,7 +378,7 @@ export class ServiceContainer {
);
this.activeUserStateProvider = new DefaultActiveUserStateProvider(
- this.accountService,
+ new DefaultActiveUserAccessor(this.accountService),
this.singleUserStateProvider,
);
@@ -431,16 +429,17 @@ export class ServiceContainer {
migrationRunner,
);
+ this.kdfConfigService = new DefaultKdfConfigService(this.stateProvider);
this.masterPasswordService = new MasterPasswordService(
this.stateProvider,
this.stateService,
this.keyGenerationService,
this.encryptService,
this.logService,
+ this.cryptoFunctionService,
+ this.accountService,
);
- this.kdfConfigService = new DefaultKdfConfigService(this.stateProvider);
-
this.pinService = new PinService(
this.accountService,
this.cryptoFunctionService,
@@ -718,13 +717,13 @@ export class ServiceContainer {
this.stateService,
this.autofillSettingsService,
this.encryptService,
- this.bulkEncryptService,
this.cipherFileUploadService,
this.configService,
this.stateProvider,
this.accountService,
this.logService,
this.cipherEncryptionService,
+ this.messagingService,
);
this.folderService = new FolderService(
@@ -901,7 +900,6 @@ export class ServiceContainer {
this.keyService.clearKeys(userId),
this.cipherService.clear(userId),
this.folderService.clear(userId),
- this.collectionService.clear(userId),
]);
await this.stateEventRunnerService.handleEvent("logout", userId as UserId);
@@ -921,12 +919,6 @@ export class ServiceContainer {
await this.sdkLoadService.loadAndInit();
await this.storageService.init();
await this.stateService.init();
- this.configService.serverConfig$.subscribe((newConfig) => {
- if (newConfig != null) {
- this.encryptService.onServerConfigChange(newConfig);
- this.bulkEncryptService.onServerConfigChange(newConfig);
- }
- });
this.containerService.attachToGlobal(global);
await this.i18nService.init();
this.twoFactorService.init();
diff --git a/apps/cli/src/tools/send/send.program.ts b/apps/cli/src/tools/send/send.program.ts
index 650f448e558..82699e273c3 100644
--- a/apps/cli/src/tools/send/send.program.ts
+++ b/apps/cli/src/tools/send/send.program.ts
@@ -149,11 +149,11 @@ export class SendProgram extends BaseProgram {
private templateCommand(): Command {
return new Command("template")
- .argument("", "Valid objects are: send.text, send.file")
+ .argument("", "Valid objects are: send.text, text, send.file, file")
.description("Get json templates for send objects")
- .action((options: OptionValues) =>
- this.processResponse(new SendTemplateCommand().run(options.object)),
- );
+ .action((object: string) => {
+ this.processResponse(new SendTemplateCommand().run(object));
+ });
}
private getCommand(): Command {
diff --git a/apps/cli/src/vault.program.ts b/apps/cli/src/vault.program.ts
index d5615d0bb1c..bdcc52393ca 100644
--- a/apps/cli/src/vault.program.ts
+++ b/apps/cli/src/vault.program.ts
@@ -114,6 +114,7 @@ export class VaultProgram extends BaseProgram {
this.serviceContainer.apiService,
this.serviceContainer.eventCollectionService,
this.serviceContainer.accountService,
+ this.serviceContainer.keyService,
this.serviceContainer.cliRestrictedItemTypesService,
);
const response = await command.run(object, cmd);
@@ -284,6 +285,7 @@ export class VaultProgram extends BaseProgram {
this.serviceContainer.folderApiService,
this.serviceContainer.accountService,
this.serviceContainer.cliRestrictedItemTypesService,
+ this.serviceContainer.policyService,
);
const response = await command.run(object, id, encodedJson, cmd);
this.processResponse(response);
diff --git a/apps/cli/src/vault/create.command.ts b/apps/cli/src/vault/create.command.ts
index 39a0b8d464d..33ec52eeca8 100644
--- a/apps/cli/src/vault/create.command.ts
+++ b/apps/cli/src/vault/create.command.ts
@@ -180,7 +180,7 @@ export class CreateCommand {
private async createFolder(req: FolderExport) {
const activeUserId = await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId));
- const userKey = await this.keyService.getUserKeyWithLegacySupport(activeUserId);
+ const userKey = await this.keyService.getUserKey(activeUserId);
const folder = await this.folderService.encrypt(FolderExport.toView(req), userKey);
try {
await this.folderApiService.save(folder, activeUserId);
diff --git a/apps/desktop/desktop_native/autotype/src/lib.rs b/apps/desktop/desktop_native/autotype/src/lib.rs
index e3083422eb2..6d7b9f9db85 100644
--- a/apps/desktop/desktop_native/autotype/src/lib.rs
+++ b/apps/desktop/desktop_native/autotype/src/lib.rs
@@ -10,3 +10,13 @@ mod windowing;
pub fn get_foreground_window_title() -> std::result::Result {
windowing::get_foreground_window_title()
}
+
+/// Attempts to type the input text wherever the user's cursor is.
+///
+/// `input` must be an array of utf-16 encoded characters to insert.
+///
+/// TODO: The error handling will be improved in a future PR: PM-23615
+#[allow(clippy::result_unit_err)]
+pub fn type_input(input: Vec) -> std::result::Result<(), ()> {
+ windowing::type_input(input)
+}
diff --git a/apps/desktop/desktop_native/autotype/src/linux.rs b/apps/desktop/desktop_native/autotype/src/linux.rs
index aa06da21a49..d53d7af0bd9 100644
--- a/apps/desktop/desktop_native/autotype/src/linux.rs
+++ b/apps/desktop/desktop_native/autotype/src/linux.rs
@@ -1,3 +1,7 @@
pub fn get_foreground_window_title() -> std::result::Result {
todo!("Bitwarden does not yet support Linux autotype");
}
+
+pub fn type_input(_input: Vec) -> std::result::Result<(), ()> {
+ todo!("Bitwarden does not yet support Linux autotype");
+}
diff --git a/apps/desktop/desktop_native/autotype/src/macos.rs b/apps/desktop/desktop_native/autotype/src/macos.rs
index 12a4ca08d3e..7ab9f5441b7 100644
--- a/apps/desktop/desktop_native/autotype/src/macos.rs
+++ b/apps/desktop/desktop_native/autotype/src/macos.rs
@@ -1,3 +1,7 @@
pub fn get_foreground_window_title() -> std::result::Result {
todo!("Bitwarden does not yet support Mac OS autotype");
}
+
+pub fn type_input(_input: Vec) -> std::result::Result<(), ()> {
+ todo!("Bitwarden does not yet support Mac OS autotype");
+}
diff --git a/apps/desktop/desktop_native/autotype/src/windows.rs b/apps/desktop/desktop_native/autotype/src/windows.rs
index d86d5dd35ae..dba0251ed82 100644
--- a/apps/desktop/desktop_native/autotype/src/windows.rs
+++ b/apps/desktop/desktop_native/autotype/src/windows.rs
@@ -1,7 +1,11 @@
use std::ffi::OsString;
use std::os::windows::ffi::OsStringExt;
-use windows::Win32::Foundation::HWND;
+use windows::Win32::Foundation::{GetLastError, HWND};
+use windows::Win32::UI::Input::KeyboardAndMouse::{
+ SendInput, INPUT, INPUT_0, INPUT_KEYBOARD, KEYBDINPUT, KEYEVENTF_KEYUP, KEYEVENTF_UNICODE,
+ VIRTUAL_KEY,
+};
use windows::Win32::UI::WindowsAndMessaging::{
GetForegroundWindow, GetWindowTextLengthW, GetWindowTextW,
};
@@ -18,6 +22,39 @@ pub fn get_foreground_window_title() -> std::result::Result {
Ok(window_title)
}
+/// Attempts to type the input text wherever the user's cursor is.
+///
+/// `input` must be an array of utf-16 encoded characters to insert.
+///
+/// https://learn.microsoft.com/en-in/windows/win32/api/winuser/nf-winuser-sendinput
+pub fn type_input(input: Vec) -> Result<(), ()> {
+ const TAB_KEY: u16 = 9;
+ let mut keyboard_inputs: Vec = Vec::new();
+
+ // Release hotkeys
+ keyboard_inputs.push(build_virtual_key_input(InputKeyPress::Up, 0x12)); // alt
+ keyboard_inputs.push(build_virtual_key_input(InputKeyPress::Up, 0x11)); // ctrl
+ keyboard_inputs.push(build_unicode_input(InputKeyPress::Up, 105)); // i
+
+ for i in input {
+ let next_down_input = if i == TAB_KEY {
+ build_virtual_key_input(InputKeyPress::Down, i as u8)
+ } else {
+ build_unicode_input(InputKeyPress::Down, i)
+ };
+ let next_up_input = if i == TAB_KEY {
+ build_virtual_key_input(InputKeyPress::Up, i as u8)
+ } else {
+ build_unicode_input(InputKeyPress::Up, i)
+ };
+
+ keyboard_inputs.push(next_down_input);
+ keyboard_inputs.push(next_up_input);
+ }
+
+ send_input(keyboard_inputs)
+}
+
/// Gets the foreground window handle.
///
/// https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getforegroundwindow
@@ -33,8 +70,6 @@ fn get_foreground_window() -> Result {
/// Gets the length of the window title bar text.
///
-/// TODO: Future improvement is to use GetLastError for better error handling
-///
/// https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getwindowtextlengthw
fn get_window_title_length(window_handle: HWND) -> Result {
if window_handle.is_invalid() {
@@ -49,8 +84,6 @@ fn get_window_title_length(window_handle: HWND) -> Result {
/// Gets the window title bar title.
///
-/// TODO: Future improvement is to use GetLastError for better error handling
-///
/// https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getwindowtextw
fn get_window_title(window_handle: HWND) -> Result, ()> {
if window_handle.is_invalid() {
@@ -73,3 +106,94 @@ fn get_window_title(window_handle: HWND) -> Result , ()> {
Ok(Some(window_title.to_string_lossy().into_owned()))
}
+
+/// Used in build_input() to specify if an input key is being pressed (down) or released (up).
+enum InputKeyPress {
+ Down,
+ Up,
+}
+
+/// A function for easily building keyboard unicode INPUT structs used in SendInput().
+///
+/// Before modifying this function, make sure you read the SendInput() documentation:
+/// https://learn.microsoft.com/en-in/windows/win32/api/winuser/nf-winuser-sendinput
+fn build_unicode_input(key_press: InputKeyPress, character: u16) -> INPUT {
+ match key_press {
+ InputKeyPress::Down => INPUT {
+ r#type: INPUT_KEYBOARD,
+ Anonymous: INPUT_0 {
+ ki: KEYBDINPUT {
+ wVk: Default::default(),
+ wScan: character,
+ dwFlags: KEYEVENTF_UNICODE,
+ time: 0,
+ dwExtraInfo: 0,
+ },
+ },
+ },
+ InputKeyPress::Up => INPUT {
+ r#type: INPUT_KEYBOARD,
+ Anonymous: INPUT_0 {
+ ki: KEYBDINPUT {
+ wVk: Default::default(),
+ wScan: character,
+ dwFlags: KEYEVENTF_KEYUP | KEYEVENTF_UNICODE,
+ time: 0,
+ dwExtraInfo: 0,
+ },
+ },
+ },
+ }
+}
+
+/// A function for easily building keyboard virtual-key INPUT structs used in SendInput().
+///
+/// Before modifying this function, make sure you read the SendInput() documentation:
+/// https://learn.microsoft.com/en-in/windows/win32/api/winuser/nf-winuser-sendinput
+/// https://learn.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes
+fn build_virtual_key_input(key_press: InputKeyPress, virtual_key: u8) -> INPUT {
+ match key_press {
+ InputKeyPress::Down => INPUT {
+ r#type: INPUT_KEYBOARD,
+ Anonymous: INPUT_0 {
+ ki: KEYBDINPUT {
+ wVk: VIRTUAL_KEY(virtual_key as u16),
+ wScan: Default::default(),
+ dwFlags: Default::default(),
+ time: 0,
+ dwExtraInfo: 0,
+ },
+ },
+ },
+ InputKeyPress::Up => INPUT {
+ r#type: INPUT_KEYBOARD,
+ Anonymous: INPUT_0 {
+ ki: KEYBDINPUT {
+ wVk: VIRTUAL_KEY(virtual_key as u16),
+ wScan: Default::default(),
+ dwFlags: KEYEVENTF_KEYUP,
+ time: 0,
+ dwExtraInfo: 0,
+ },
+ },
+ },
+ }
+}
+
+/// Attempts to type the provided input wherever the user's cursor is.
+///
+/// https://learn.microsoft.com/en-in/windows/win32/api/winuser/nf-winuser-sendinput
+fn send_input(inputs: Vec ) -> Result<(), ()> {
+ let insert_count = unsafe { SendInput(&inputs, std::mem::size_of:: () as i32) };
+
+ let e = unsafe { GetLastError().to_hresult().message() };
+ println!("type_input() called, GetLastError() is: {:?}", e);
+
+ if insert_count == 0 {
+ return Err(()); // input was blocked by another thread
+ } else if insert_count != inputs.len() as u32 {
+ return Err(()); // input insertion not completed
+ }
+
+ Ok(())
+}
diff --git a/apps/desktop/desktop_native/napi/index.d.ts b/apps/desktop/desktop_native/napi/index.d.ts
index f554fdb12e8..5ea75bd6120 100644
--- a/apps/desktop/desktop_native/napi/index.d.ts
+++ b/apps/desktop/desktop_native/napi/index.d.ts
@@ -210,4 +210,5 @@ export declare namespace logging {
}
export declare namespace autotype {
export function getForegroundWindowTitle(): string
+ export function typeInput(input: Array): void
}
diff --git a/apps/desktop/desktop_native/napi/src/lib.rs b/apps/desktop/desktop_native/napi/src/lib.rs
index aa271b335ad..d0a57b5632a 100644
--- a/apps/desktop/desktop_native/napi/src/lib.rs
+++ b/apps/desktop/desktop_native/napi/src/lib.rs
@@ -872,8 +872,15 @@ pub mod autotype {
pub fn get_foreground_window_title() -> napi::Result {
autotype::get_foreground_window_title().map_err(|_| {
napi::Error::from_reason(
- "Autotype Error: faild to get foreground window title".to_string(),
+ "Autotype Error: failed to get foreground window title".to_string(),
)
})
}
+
+ #[napi]
+ pub fn type_input(input: Vec) -> napi::Result<(), napi::Status> {
+ autotype::type_input(input).map_err(|_| {
+ napi::Error::from_reason("Autotype Error: failed to type input".to_string())
+ })
+ }
}
diff --git a/apps/desktop/native-messaging-test-runner/package-lock.json b/apps/desktop/native-messaging-test-runner/package-lock.json
index 043393df58b..718bf7efb39 100644
--- a/apps/desktop/native-messaging-test-runner/package-lock.json
+++ b/apps/desktop/native-messaging-test-runner/package-lock.json
@@ -16,7 +16,7 @@
"module-alias": "2.2.3",
"ts-node": "10.9.2",
"uuid": "11.1.0",
- "yargs": "17.7.2"
+ "yargs": "18.0.0"
},
"devDependencies": {
"@types/node": "22.15.3",
@@ -150,24 +150,24 @@
}
},
"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==",
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz",
+ "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==",
"license": "MIT",
"engines": {
- "node": ">=8"
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-regex?sponsor=1"
}
},
"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==",
+ "version": "6.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz",
+ "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==",
"license": "MIT",
- "dependencies": {
- "color-convert": "^2.0.1"
- },
"engines": {
- "node": ">=8"
+ "node": ">=12"
},
"funding": {
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
@@ -180,37 +180,19 @@
"license": "MIT"
},
"node_modules/cliui": {
- "version": "8.0.1",
- "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
- "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
+ "version": "9.0.1",
+ "resolved": "https://registry.npmjs.org/cliui/-/cliui-9.0.1.tgz",
+ "integrity": "sha512-k7ndgKhwoQveBL+/1tqGJYNz097I7WOvwbmmU2AR5+magtbjPWQTS1C5vzGkBC8Ym8UWRzfKUzUUqFLypY4Q+w==",
"license": "ISC",
"dependencies": {
- "string-width": "^4.2.0",
- "strip-ansi": "^6.0.1",
- "wrap-ansi": "^7.0.0"
+ "string-width": "^7.2.0",
+ "strip-ansi": "^7.1.0",
+ "wrap-ansi": "^9.0.0"
},
"engines": {
- "node": ">=12"
+ "node": ">=20"
}
},
- "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==",
- "license": "MIT",
- "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==",
- "license": "MIT"
- },
"node_modules/create-require": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz",
@@ -227,9 +209,9 @@
}
},
"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==",
+ "version": "10.4.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.4.0.tgz",
+ "integrity": "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==",
"license": "MIT"
},
"node_modules/escalade": {
@@ -250,13 +232,16 @@
"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==",
+ "node_modules/get-east-asian-width": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.3.0.tgz",
+ "integrity": "sha512-vpeMIQKxczTD/0s2CdEWHcb0eeJe6TFjxb+J5xgX7hScxqrGuyjmv4c1D4A/gelKfyox0gJJwIHF+fLjeaM8kQ==",
"license": "MIT",
"engines": {
- "node": ">=8"
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/make-error": {
@@ -271,39 +256,36 @@
"integrity": "sha512-23g5BFj4zdQL/b6tor7Ji+QY4pEfNH784BMslY9Qb0UnJWRAt+lQGLYmRaM0KDBwIG23ffEBELhZDP2rhi9f/Q==",
"license": "MIT"
},
- "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==",
- "license": "MIT",
- "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==",
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz",
+ "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==",
"license": "MIT",
"dependencies": {
- "emoji-regex": "^8.0.0",
- "is-fullwidth-code-point": "^3.0.0",
- "strip-ansi": "^6.0.1"
+ "emoji-regex": "^10.3.0",
+ "get-east-asian-width": "^1.0.0",
+ "strip-ansi": "^7.1.0"
},
"engines": {
- "node": ">=8"
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
}
},
"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==",
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
+ "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
"license": "MIT",
"dependencies": {
- "ansi-regex": "^5.0.1"
+ "ansi-regex": "^6.0.1"
},
"engines": {
- "node": ">=8"
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/strip-ansi?sponsor=1"
}
},
"node_modules/ts-node": {
@@ -388,17 +370,17 @@
"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==",
+ "version": "9.0.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.0.tgz",
+ "integrity": "sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==",
"license": "MIT",
"dependencies": {
- "ansi-styles": "^4.0.0",
- "string-width": "^4.1.0",
- "strip-ansi": "^6.0.0"
+ "ansi-styles": "^6.2.1",
+ "string-width": "^7.0.0",
+ "strip-ansi": "^7.1.0"
},
"engines": {
- "node": ">=10"
+ "node": ">=18"
},
"funding": {
"url": "https://github.com/chalk/wrap-ansi?sponsor=1"
@@ -414,30 +396,29 @@
}
},
"node_modules/yargs": {
- "version": "17.7.2",
- "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz",
- "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==",
+ "version": "18.0.0",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-18.0.0.tgz",
+ "integrity": "sha512-4UEqdc2RYGHZc7Doyqkrqiln3p9X2DZVxaGbwhn2pi7MrRagKaOcIKe8L3OxYcbhXLgLFUS3zAYuQjKBQgmuNg==",
"license": "MIT",
"dependencies": {
- "cliui": "^8.0.1",
+ "cliui": "^9.0.1",
"escalade": "^3.1.1",
"get-caller-file": "^2.0.5",
- "require-directory": "^2.1.1",
- "string-width": "^4.2.3",
+ "string-width": "^7.2.0",
"y18n": "^5.0.5",
- "yargs-parser": "^21.1.1"
+ "yargs-parser": "^22.0.0"
},
"engines": {
- "node": ">=12"
+ "node": "^20.19.0 || ^22.12.0 || >=23"
}
},
"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==",
+ "version": "22.0.0",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-22.0.0.tgz",
+ "integrity": "sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw==",
"license": "ISC",
"engines": {
- "node": ">=12"
+ "node": "^20.19.0 || ^22.12.0 || >=23"
}
},
"node_modules/yn": {
diff --git a/apps/desktop/native-messaging-test-runner/package.json b/apps/desktop/native-messaging-test-runner/package.json
index 56e3e4edcf8..35a110c3958 100644
--- a/apps/desktop/native-messaging-test-runner/package.json
+++ b/apps/desktop/native-messaging-test-runner/package.json
@@ -21,7 +21,7 @@
"module-alias": "2.2.3",
"ts-node": "10.9.2",
"uuid": "11.1.0",
- "yargs": "17.7.2"
+ "yargs": "18.0.0"
},
"devDependencies": {
"@types/node": "22.15.3",
diff --git a/apps/desktop/package.json b/apps/desktop/package.json
index 2ab88fed621..37650c08b95 100644
--- a/apps/desktop/package.json
+++ b/apps/desktop/package.json
@@ -1,7 +1,7 @@
{
"name": "@bitwarden/desktop",
"description": "A secure and free password manager for all of your devices.",
- "version": "2025.7.0",
+ "version": "2025.8.0",
"keywords": [
"bitwarden",
"password",
diff --git a/apps/desktop/src/app/accounts/settings.component.html b/apps/desktop/src/app/accounts/settings.component.html
index 473cfa73f1d..e3ebfb467cf 100644
--- a/apps/desktop/src/app/accounts/settings.component.html
+++ b/apps/desktop/src/app/accounts/settings.component.html
@@ -30,88 +30,39 @@
-
-
- {{
- "vaultTimeoutPolicyWithActionInEffect"
- | i18n: policy.timeout.hours : policy.timeout.minutes : (policy.action | i18n)
- }}
-
-
- {{
- "vaultTimeoutPolicyInEffect"
- | i18n: policy.timeout.hours : policy.timeout.minutes
- }}
-
-
- {{ "vaultTimeoutActionPolicyInEffect" | i18n: (policy.action | i18n) }}
-
-
-
-
-
-
-
-
-
{{ name }}
-
-
-
- {{ "new" | i18n }}
-
+
+ {{ name }}
+ @if (showConnectedBadge()) {
+
+ @if (isConnected) {
+ {{ "on" | i18n }}
+ }
+ @if (!isConnected) {
+ {{ "off" | i18n }}
+ }
+
+ }
+
+
{{ description }}
+
+ @if (canSetupConnection) {
+
+ {{ "connectIntegrationButtonDesc" | i18n: name }}
+
+ }
+
+ @if (linkURL) {
+
+
+ }
+ @if (showNewBadge()) {
+
+ {{ "new" | i18n }}
+
+ }
diff --git a/apps/web/src/app/admin-console/organizations/shared/components/integrations/integration-card/integration-card.component.spec.ts b/apps/web/src/app/admin-console/organizations/shared/components/integrations/integration-card/integration-card.component.spec.ts
index ec057f25176..382d245b235 100644
--- a/apps/web/src/app/admin-console/organizations/shared/components/integrations/integration-card/integration-card.component.spec.ts
+++ b/apps/web/src/app/admin-console/organizations/shared/components/integrations/integration-card/integration-card.component.spec.ts
@@ -1,12 +1,15 @@
import { ComponentFixture, TestBed } from "@angular/core/testing";
+import { ActivatedRoute } from "@angular/router";
import { mock } from "jest-mock-extended";
import { BehaviorSubject } from "rxjs";
import { SYSTEM_THEME_OBSERVABLE } from "@bitwarden/angular/services/injection-tokens";
+// eslint-disable-next-line no-restricted-imports
+import { OrganizationIntegrationApiService } from "@bitwarden/bit-common/dirt/integrations/services";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { ThemeType } from "@bitwarden/common/platform/enums";
import { ThemeStateService } from "@bitwarden/common/platform/theming/theme-state.service";
-// FIXME: remove `src` and fix import
+import { ToastService } from "@bitwarden/components";
// eslint-disable-next-line no-restricted-imports
import { SharedModule } from "@bitwarden/components/src/shared";
import { I18nPipe } from "@bitwarden/ui-common";
@@ -16,6 +19,9 @@ import { IntegrationCardComponent } from "./integration-card.component";
describe("IntegrationCardComponent", () => {
let component: IntegrationCardComponent;
let fixture: ComponentFixture
;
+ const mockI18nService = mock();
+ const activatedRoute = mock();
+ const mockOrgIntegrationApiService = mock();
const systemTheme$ = new BehaviorSubject(ThemeType.Light);
const usersPreferenceTheme$ = new BehaviorSubject(ThemeType.Light);
@@ -23,26 +29,22 @@ describe("IntegrationCardComponent", () => {
beforeEach(async () => {
// reset system theme
systemTheme$.next(ThemeType.Light);
+ activatedRoute.snapshot = {
+ paramMap: {
+ get: jest.fn().mockReturnValue("test-organization-id"),
+ },
+ } as any;
await TestBed.configureTestingModule({
imports: [IntegrationCardComponent, SharedModule],
providers: [
- {
- provide: ThemeStateService,
- useValue: { selectedTheme$: usersPreferenceTheme$ },
- },
- {
- provide: SYSTEM_THEME_OBSERVABLE,
- useValue: systemTheme$,
- },
- {
- provide: I18nPipe,
- useValue: mock(),
- },
- {
- provide: I18nService,
- useValue: mock(),
- },
+ { provide: ThemeStateService, useValue: { selectedTheme$: usersPreferenceTheme$ } },
+ { provide: SYSTEM_THEME_OBSERVABLE, useValue: systemTheme$ },
+ { provide: I18nPipe, useValue: mock() },
+ { provide: I18nService, useValue: mockI18nService },
+ { provide: ActivatedRoute, useValue: activatedRoute },
+ { provide: OrganizationIntegrationApiService, useValue: mockOrgIntegrationApiService },
+ { provide: ToastService, useValue: mock() },
],
}).compileComponents();
});
@@ -55,6 +57,7 @@ describe("IntegrationCardComponent", () => {
component.image = "test-image.png";
component.linkURL = "https://example.com/";
+ mockI18nService.t.mockImplementation((key) => key);
fixture.detectChanges();
});
@@ -67,7 +70,7 @@ describe("IntegrationCardComponent", () => {
it("renders card body", () => {
const name = fixture.nativeElement.querySelector("h3");
- expect(name.textContent).toBe("Integration Name");
+ expect(name.textContent).toContain("Integration Name");
});
it("assigns external rel attribute", () => {
@@ -182,4 +185,28 @@ describe("IntegrationCardComponent", () => {
});
});
});
+
+ describe("connected badge", () => {
+ it("shows connected badge when isConnected is true", () => {
+ component.isConnected = true;
+
+ expect(component.showConnectedBadge()).toBe(true);
+ });
+
+ it("does not show connected badge when isConnected is false", () => {
+ component.isConnected = false;
+ fixture.detectChanges();
+ const name = fixture.nativeElement.querySelector("h3 > span > span > span");
+
+ expect(name.textContent).toContain("off");
+ // when isConnected is true/false, the badge should be shown as on/off
+ // when isConnected is undefined, the badge should not be shown
+ expect(component.showConnectedBadge()).toBe(true);
+ });
+
+ it("does not show connected badge when isConnected is undefined", () => {
+ component.isConnected = undefined;
+ expect(component.showConnectedBadge()).toBe(false);
+ });
+ });
});
diff --git a/apps/web/src/app/admin-console/organizations/shared/components/integrations/integration-card/integration-card.component.ts b/apps/web/src/app/admin-console/organizations/shared/components/integrations/integration-card/integration-card.component.ts
index 20e4028e9df..1d95d3182b2 100644
--- a/apps/web/src/app/admin-console/organizations/shared/components/integrations/integration-card/integration-card.component.ts
+++ b/apps/web/src/app/admin-console/organizations/shared/components/integrations/integration-card/integration-card.component.ts
@@ -9,13 +9,26 @@ import {
OnDestroy,
ViewChild,
} from "@angular/core";
-import { Observable, Subject, combineLatest, takeUntil } from "rxjs";
+import { ActivatedRoute } from "@angular/router";
+import { Observable, Subject, combineLatest, lastValueFrom, takeUntil } from "rxjs";
import { SYSTEM_THEME_OBSERVABLE } from "@bitwarden/angular/services/injection-tokens";
+// eslint-disable-next-line no-restricted-imports
+import {
+ OrganizationIntegrationType,
+ OrganizationIntegrationRequest,
+ OrganizationIntegrationResponse,
+ OrganizationIntegrationApiService,
+} from "@bitwarden/bit-common/dirt/integrations/index";
+import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { ThemeType } from "@bitwarden/common/platform/enums";
import { ThemeStateService } from "@bitwarden/common/platform/theming/theme-state.service";
+import { OrganizationId } from "@bitwarden/common/types/guid";
+import { DialogService, ToastService } from "@bitwarden/components";
import { SharedModule } from "../../../../../../shared/shared.module";
+import { openHecConnectDialog } from "../integration-dialog/index";
+import { Integration } from "../models";
@Component({
selector: "app-integration-card",
@@ -30,6 +43,7 @@ export class IntegrationCardComponent implements AfterViewInit, OnDestroy {
@Input() image: string;
@Input() imageDarkMode?: string;
@Input() linkURL: string;
+ @Input() integrationSettings: Integration;
/** Adds relevant `rel` attribute to external links */
@Input() externalURL?: boolean;
@@ -41,11 +55,19 @@ export class IntegrationCardComponent implements AfterViewInit, OnDestroy {
* @example "2024-12-31"
*/
@Input() newBadgeExpiration?: string;
+ @Input() description?: string;
+ @Input() isConnected?: boolean;
+ @Input() canSetupConnection?: boolean;
constructor(
private themeStateService: ThemeStateService,
@Inject(SYSTEM_THEME_OBSERVABLE)
private systemTheme$: Observable,
+ private dialogService: DialogService,
+ private activatedRoute: ActivatedRoute,
+ private apiService: OrganizationIntegrationApiService,
+ private toastService: ToastService,
+ private i18nService: I18nService,
) {}
ngAfterViewInit() {
@@ -93,4 +115,63 @@ export class IntegrationCardComponent implements AfterViewInit, OnDestroy {
return expirationDate > new Date();
}
+
+ showConnectedBadge(): boolean {
+ return this.isConnected !== undefined;
+ }
+
+ async setupConnection() {
+ // invoke the dialog to connect the integration
+ const dialog = openHecConnectDialog(this.dialogService, {
+ data: {
+ settings: this.integrationSettings,
+ },
+ });
+
+ const result = await lastValueFrom(dialog.closed);
+
+ // the dialog was cancelled
+ if (!result || !result.success) {
+ return;
+ }
+
+ // save the integration
+ try {
+ const dbResponse = await this.saveHecIntegration(result.configuration);
+ this.isConnected = !!dbResponse.id;
+ } catch {
+ this.toastService.showToast({
+ variant: "error",
+ title: null,
+ message: this.i18nService.t("failedToSaveIntegration"),
+ });
+ return;
+ }
+ }
+
+ async saveHecIntegration(configuration: string): Promise {
+ const organizationId = this.activatedRoute.snapshot.paramMap.get(
+ "organizationId",
+ ) as OrganizationId;
+
+ const request = new OrganizationIntegrationRequest(
+ OrganizationIntegrationType.Hec,
+ configuration,
+ );
+
+ const integrations = await this.apiService.getOrganizationIntegrations(organizationId);
+ const existingIntegration = integrations.find(
+ (i) => i.type === OrganizationIntegrationType.Hec,
+ );
+
+ if (existingIntegration) {
+ return await this.apiService.updateOrganizationIntegration(
+ organizationId,
+ existingIntegration.id,
+ request,
+ );
+ } else {
+ return await this.apiService.createOrganizationIntegration(organizationId, request);
+ }
+ }
}
diff --git a/apps/web/src/app/admin-console/organizations/shared/components/integrations/integration-card/integration-card.stories.ts b/apps/web/src/app/admin-console/organizations/shared/components/integrations/integration-card/integration-card.stories.ts
deleted file mode 100644
index 256bfd3d827..00000000000
--- a/apps/web/src/app/admin-console/organizations/shared/components/integrations/integration-card/integration-card.stories.ts
+++ /dev/null
@@ -1,58 +0,0 @@
-import { importProvidersFrom } from "@angular/core";
-import { Meta, StoryObj, applicationConfig, moduleMetadata } from "@storybook/angular";
-import { of } from "rxjs";
-
-import { SYSTEM_THEME_OBSERVABLE } from "@bitwarden/angular/services/injection-tokens";
-import { ThemeTypes } from "@bitwarden/common/platform/enums";
-import { ThemeStateService } from "@bitwarden/common/platform/theming/theme-state.service";
-
-import { PreloadedEnglishI18nModule } from "../../../../../../core/tests";
-
-import { IntegrationCardComponent } from "./integration-card.component";
-
-class MockThemeService implements Partial {}
-
-export default {
- title: "Web/Integration Layout/Integration Card",
- component: IntegrationCardComponent,
- decorators: [
- applicationConfig({
- providers: [importProvidersFrom(PreloadedEnglishI18nModule)],
- }),
- moduleMetadata({
- providers: [
- {
- provide: ThemeStateService,
- useClass: MockThemeService,
- },
- {
- provide: SYSTEM_THEME_OBSERVABLE,
- useValue: of(ThemeTypes.Light),
- },
- ],
- }),
- ],
- args: {
- integrations: [],
- },
-} as Meta;
-
-type Story = StoryObj;
-
-export const Default: Story = {
- render: (args) => ({
- props: args,
- template: /*html*/ `
-
- `,
- }),
- args: {
- name: "Bitwarden",
- image: "/integrations/bitwarden-vertical-blue.svg",
- linkURL: "https://bitwarden.com",
- },
-};
diff --git a/apps/web/src/app/admin-console/organizations/shared/components/integrations/integration-dialog/connect-dialog/connect-dialog-hec.component.html b/apps/web/src/app/admin-console/organizations/shared/components/integrations/integration-dialog/connect-dialog/connect-dialog-hec.component.html
new file mode 100644
index 00000000000..7f28317dd67
--- /dev/null
+++ b/apps/web/src/app/admin-console/organizations/shared/components/integrations/integration-dialog/connect-dialog/connect-dialog-hec.component.html
@@ -0,0 +1,38 @@
+
+
+
+ {{ "connectIntegrationButtonDesc" | i18n: connectInfo.settings.name }}
+
+
+ @if (loading) {
+
+
+
+ }
+ @if (!loading) {
+
+
+ {{ "url" | i18n }}
+
+
+
+ {{ "bearerToken" | i18n }}
+
+
+
+ {{ "index" | i18n }}
+
+
+
+ }
+
+
+
+ {{ "save" | i18n }}
+
+
+ {{ "cancel" | i18n }}
+
+
+
+
diff --git a/apps/web/src/app/admin-console/organizations/shared/components/integrations/integration-dialog/connect-dialog/connect-dialog-hec.component.spec.ts b/apps/web/src/app/admin-console/organizations/shared/components/integrations/integration-dialog/connect-dialog/connect-dialog-hec.component.spec.ts
new file mode 100644
index 00000000000..9be854545aa
--- /dev/null
+++ b/apps/web/src/app/admin-console/organizations/shared/components/integrations/integration-dialog/connect-dialog/connect-dialog-hec.component.spec.ts
@@ -0,0 +1,176 @@
+import { ComponentFixture, TestBed } from "@angular/core/testing";
+import { FormBuilder, ReactiveFormsModule } from "@angular/forms";
+import { BrowserAnimationsModule } from "@angular/platform-browser/animations";
+import { mock } from "jest-mock-extended";
+
+import { IntegrationType } from "@bitwarden/common/enums";
+import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
+import { DIALOG_DATA, DialogConfig, DialogRef, DialogService } from "@bitwarden/components";
+import { I18nPipe } from "@bitwarden/ui-common";
+import { SharedModule } from "@bitwarden/web-vault/app/shared";
+
+import { Integration } from "../../models";
+
+import {
+ ConnectHecDialogComponent,
+ HecConnectDialogParams,
+ HecConnectDialogResult,
+ openHecConnectDialog,
+} from "./connect-dialog-hec.component";
+
+beforeAll(() => {
+ // Mock element.animate for jsdom
+ // the animate function is not available in jsdom, so we provide a mock implementation
+ // This is necessary for tests that rely on animations
+ // This mock does not perform any actual animations, it just provides a structure that allows tests
+ // to run without throwing errors related to missing animate function
+ if (!HTMLElement.prototype.animate) {
+ HTMLElement.prototype.animate = function () {
+ return {
+ play: () => {},
+ pause: () => {},
+ finish: () => {},
+ cancel: () => {},
+ reverse: () => {},
+ addEventListener: () => {},
+ removeEventListener: () => {},
+ dispatchEvent: () => false,
+ onfinish: null,
+ oncancel: null,
+ startTime: 0,
+ currentTime: 0,
+ playbackRate: 1,
+ playState: "idle",
+ replaceState: "active",
+ effect: null,
+ finished: Promise.resolve(),
+ id: "",
+ remove: () => {},
+ timeline: null,
+ ready: Promise.resolve(),
+ } as unknown as Animation;
+ };
+ }
+});
+
+describe("ConnectDialogHecComponent", () => {
+ let component: ConnectHecDialogComponent;
+ let fixture: ComponentFixture;
+ let dialogRefMock = mock>();
+ const mockI18nService = mock();
+
+ const integrationMock: Integration = {
+ name: "Test Integration",
+ image: "test-image.png",
+ linkURL: "https://example.com",
+ imageDarkMode: "test-image-dark.png",
+ newBadgeExpiration: "2024-12-31",
+ description: "Test Description",
+ isConnected: false,
+ canSetupConnection: true,
+ type: IntegrationType.EVENT,
+ } as Integration;
+ const connectInfo: HecConnectDialogParams = { settings: integrationMock };
+
+ beforeEach(async () => {
+ dialogRefMock = mock>();
+
+ await TestBed.configureTestingModule({
+ imports: [ReactiveFormsModule, SharedModule, BrowserAnimationsModule],
+ providers: [
+ FormBuilder,
+ { provide: DIALOG_DATA, useValue: connectInfo },
+ { provide: DialogRef, useValue: dialogRefMock },
+ { provide: I18nPipe, useValue: mock() },
+ { provide: I18nService, useValue: mockI18nService },
+ ],
+ }).compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(ConnectHecDialogComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ mockI18nService.t.mockImplementation((key) => key);
+ });
+
+ it("should create the component", () => {
+ expect(component).toBeTruthy();
+ });
+
+ it("should initialize form with empty values", () => {
+ expect(component.formGroup.value).toEqual({
+ url: "",
+ bearerToken: "",
+ index: "",
+ service: "Test Integration",
+ });
+ });
+
+ it("should have required validators for all fields", () => {
+ component.formGroup.setValue({ url: "", bearerToken: "", index: "", service: "" });
+ expect(component.formGroup.valid).toBeFalsy();
+
+ component.formGroup.setValue({
+ url: "https://test.com",
+ bearerToken: "token",
+ index: "1",
+ service: "Test Service",
+ });
+ expect(component.formGroup.valid).toBeTruthy();
+ });
+
+ it("should invalidate url if not matching pattern", () => {
+ component.formGroup.setValue({
+ url: "ftp://test.com",
+ bearerToken: "token",
+ index: "1",
+ service: "Test Service",
+ });
+ expect(component.formGroup.valid).toBeFalsy();
+
+ component.formGroup.setValue({
+ url: "https://test.com",
+ bearerToken: "token",
+ index: "1",
+ service: "Test Service",
+ });
+ expect(component.formGroup.valid).toBeTruthy();
+ });
+
+ it("should call dialogRef.close with correct result on submit", async () => {
+ component.formGroup.setValue({
+ url: "https://test.com",
+ bearerToken: "token",
+ index: "1",
+ service: "Test Service",
+ });
+
+ await component.submit();
+
+ expect(dialogRefMock.close).toHaveBeenCalledWith({
+ integrationSettings: integrationMock,
+ configuration: JSON.stringify({
+ url: "https://test.com",
+ bearerToken: "token",
+ index: "1",
+ service: "Test Service",
+ }),
+ success: true,
+ error: null,
+ });
+ });
+});
+
+describe("openCrowdstrikeConnectDialog", () => {
+ it("should call dialogService.open with correct params", () => {
+ const dialogServiceMock = mock();
+ const config: DialogConfig> = {
+ data: { settings: { name: "Test" } as Integration },
+ } as any;
+
+ openHecConnectDialog(dialogServiceMock, config);
+
+ expect(dialogServiceMock.open).toHaveBeenCalledWith(ConnectHecDialogComponent, config);
+ });
+});
diff --git a/apps/web/src/app/admin-console/organizations/shared/components/integrations/integration-dialog/connect-dialog/connect-dialog-hec.component.ts b/apps/web/src/app/admin-console/organizations/shared/components/integrations/integration-dialog/connect-dialog/connect-dialog-hec.component.ts
new file mode 100644
index 00000000000..c0af17db8d7
--- /dev/null
+++ b/apps/web/src/app/admin-console/organizations/shared/components/integrations/integration-dialog/connect-dialog/connect-dialog-hec.component.ts
@@ -0,0 +1,81 @@
+import { Component, Inject, OnInit } from "@angular/core";
+import { FormBuilder, Validators } from "@angular/forms";
+
+import { DIALOG_DATA, DialogConfig, DialogRef, DialogService } from "@bitwarden/components";
+import { SharedModule } from "@bitwarden/web-vault/app/shared";
+
+import { Integration } from "../../models";
+
+export type HecConnectDialogParams = {
+ settings: Integration;
+};
+
+export interface HecConnectDialogResult {
+ integrationSettings: Integration;
+ configuration: string;
+ success: boolean;
+ error: string | null;
+}
+
+@Component({
+ templateUrl: "./connect-dialog-hec.component.html",
+ imports: [SharedModule],
+})
+export class ConnectHecDialogComponent implements OnInit {
+ loading = false;
+ formGroup = this.formBuilder.group({
+ url: ["", [Validators.required, Validators.pattern("https?://.+")]],
+ bearerToken: ["", Validators.required],
+ index: ["", Validators.required],
+ service: ["", Validators.required],
+ });
+
+ constructor(
+ @Inject(DIALOG_DATA) protected connectInfo: HecConnectDialogParams,
+ protected formBuilder: FormBuilder,
+ private dialogRef: DialogRef,
+ ) {}
+
+ ngOnInit(): void {
+ const settings = this.getSettingsAsJson(this.connectInfo.settings.configuration ?? "");
+
+ if (settings) {
+ this.formGroup.patchValue({
+ url: settings?.url || "",
+ bearerToken: settings?.bearerToken || "",
+ index: settings?.index || "",
+ service: this.connectInfo.settings.name,
+ });
+ }
+ }
+
+ getSettingsAsJson(configuration: string) {
+ try {
+ return JSON.parse(configuration);
+ } catch {
+ return {};
+ }
+ }
+
+ submit = async (): Promise => {
+ const formJson = this.formGroup.getRawValue();
+
+ const result: HecConnectDialogResult = {
+ integrationSettings: this.connectInfo.settings,
+ configuration: JSON.stringify(formJson),
+ success: true,
+ error: null,
+ };
+
+ this.dialogRef.close(result);
+
+ return;
+ };
+}
+
+export function openHecConnectDialog(
+ dialogService: DialogService,
+ config: DialogConfig>,
+) {
+ return dialogService.open(ConnectHecDialogComponent, config);
+}
diff --git a/apps/web/src/app/admin-console/organizations/shared/components/integrations/integration-dialog/index.ts b/apps/web/src/app/admin-console/organizations/shared/components/integrations/integration-dialog/index.ts
new file mode 100644
index 00000000000..8c4891b9aa8
--- /dev/null
+++ b/apps/web/src/app/admin-console/organizations/shared/components/integrations/integration-dialog/index.ts
@@ -0,0 +1 @@
+export * from "./connect-dialog/connect-dialog-hec.component";
diff --git a/apps/web/src/app/admin-console/organizations/shared/components/integrations/integration-grid/integration-grid.component.html b/apps/web/src/app/admin-console/organizations/shared/components/integrations/integration-grid/integration-grid.component.html
index 4b4b3ac972b..661c57b47fc 100644
--- a/apps/web/src/app/admin-console/organizations/shared/components/integrations/integration-grid/integration-grid.component.html
+++ b/apps/web/src/app/admin-console/organizations/shared/components/integrations/integration-grid/integration-grid.component.html
@@ -13,6 +13,10 @@
[imageDarkMode]="integration.imageDarkMode"
[externalURL]="integration.type === IntegrationType.SDK"
[newBadgeExpiration]="integration.newBadgeExpiration"
+ [description]="integration.description | i18n"
+ [isConnected]="integration.isConnected"
+ [canSetupConnection]="integration.canSetupConnection"
+ [integrationSettings]="integration"
>
diff --git a/apps/web/src/app/admin-console/organizations/shared/components/integrations/integration-grid/integration-grid.component.spec.ts b/apps/web/src/app/admin-console/organizations/shared/components/integrations/integration-grid/integration-grid.component.spec.ts
index 04866f4627b..01a512ac38c 100644
--- a/apps/web/src/app/admin-console/organizations/shared/components/integrations/integration-grid/integration-grid.component.spec.ts
+++ b/apps/web/src/app/admin-console/organizations/shared/components/integrations/integration-grid/integration-grid.component.spec.ts
@@ -1,14 +1,20 @@
import { ComponentFixture, TestBed } from "@angular/core/testing";
import { By } from "@angular/platform-browser";
+import { ActivatedRoute } from "@angular/router";
import { mock } from "jest-mock-extended";
import { of } from "rxjs";
import { SYSTEM_THEME_OBSERVABLE } from "@bitwarden/angular/services/injection-tokens";
+// eslint-disable-next-line no-restricted-imports
+import { OrganizationIntegrationApiService } from "@bitwarden/bit-common/dirt/integrations/services";
import { IntegrationType } from "@bitwarden/common/enums";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { ThemeTypes } from "@bitwarden/common/platform/enums";
+// eslint-disable-next-line import/order
import { ThemeStateService } from "@bitwarden/common/platform/theming/theme-state.service";
// FIXME: remove `src` and fix import
+
+import { ToastService } from "@bitwarden/components";
// eslint-disable-next-line no-restricted-imports
import { SharedModule } from "@bitwarden/components/src/shared";
import { I18nPipe } from "@bitwarden/ui-common";
@@ -21,6 +27,8 @@ import { IntegrationGridComponent } from "./integration-grid.component";
describe("IntegrationGridComponent", () => {
let component: IntegrationGridComponent;
let fixture: ComponentFixture;
+ const mockActivatedRoute = mock();
+ const mockOrgIntegrationApiService = mock();
const integrations: Integration[] = [
{
name: "Integration 1",
@@ -37,6 +45,12 @@ describe("IntegrationGridComponent", () => {
];
beforeEach(() => {
+ mockActivatedRoute.snapshot = {
+ paramMap: {
+ get: jest.fn().mockReturnValue("test-organization-id"),
+ },
+ } as any;
+
TestBed.configureTestingModule({
imports: [IntegrationGridComponent, IntegrationCardComponent, SharedModule],
providers: [
@@ -56,6 +70,18 @@ describe("IntegrationGridComponent", () => {
provide: I18nService,
useValue: mock({ t: (key, p1) => key + " " + p1 }),
},
+ {
+ provide: ActivatedRoute,
+ useValue: mockActivatedRoute,
+ },
+ {
+ provide: OrganizationIntegrationApiService,
+ useValue: mockOrgIntegrationApiService,
+ },
+ {
+ provide: ToastService,
+ useValue: mock(),
+ },
],
});
diff --git a/apps/web/src/app/admin-console/organizations/shared/components/integrations/integration-grid/integration-grid.stories.ts b/apps/web/src/app/admin-console/organizations/shared/components/integrations/integration-grid/integration-grid.stories.ts
deleted file mode 100644
index b6580af2881..00000000000
--- a/apps/web/src/app/admin-console/organizations/shared/components/integrations/integration-grid/integration-grid.stories.ts
+++ /dev/null
@@ -1,70 +0,0 @@
-import { importProvidersFrom } from "@angular/core";
-import { Meta, StoryObj, applicationConfig, moduleMetadata } from "@storybook/angular";
-import { of } from "rxjs";
-
-import { SYSTEM_THEME_OBSERVABLE } from "@bitwarden/angular/services/injection-tokens";
-import { IntegrationType } from "@bitwarden/common/enums";
-import { ThemeTypes } from "@bitwarden/common/platform/enums";
-import { ThemeStateService } from "@bitwarden/common/platform/theming/theme-state.service";
-
-import { PreloadedEnglishI18nModule } from "../../../../../../core/tests";
-import { IntegrationCardComponent } from "../integration-card/integration-card.component";
-import { IntegrationGridComponent } from "../integration-grid/integration-grid.component";
-
-class MockThemeService implements Partial {}
-
-export default {
- title: "Web/Integration Layout/Integration Grid",
- component: IntegrationGridComponent,
- decorators: [
- applicationConfig({
- providers: [importProvidersFrom(PreloadedEnglishI18nModule)],
- }),
- moduleMetadata({
- imports: [IntegrationCardComponent],
- providers: [
- {
- provide: ThemeStateService,
- useClass: MockThemeService,
- },
- {
- provide: SYSTEM_THEME_OBSERVABLE,
- useValue: of(ThemeTypes.Dark),
- },
- ],
- }),
- ],
-} as Meta;
-
-type Story = StoryObj;
-
-export const Default: Story = {
- render: (args) => ({
- props: args,
- template: /*html*/ `
-
- `,
- }),
- args: {
- integrations: [
- {
- name: "Card 1",
- linkURL: "https://bitwarden.com",
- image: "/integrations/bitwarden-vertical-blue.svg",
- type: IntegrationType.SSO,
- },
- {
- name: "Card 2",
- linkURL: "https://bitwarden.com",
- image: "/integrations/bitwarden-vertical-blue.svg",
- type: IntegrationType.SDK,
- },
- {
- name: "Card 3",
- linkURL: "https://bitwarden.com",
- image: "/integrations/bitwarden-vertical-blue.svg",
- type: IntegrationType.SCIM,
- },
- ],
- },
-};
diff --git a/apps/web/src/app/admin-console/organizations/shared/components/integrations/models.ts b/apps/web/src/app/admin-console/organizations/shared/components/integrations/models.ts
index 765b1d44a2e..b3d24ffb3b0 100644
--- a/apps/web/src/app/admin-console/organizations/shared/components/integrations/models.ts
+++ b/apps/web/src/app/admin-console/organizations/shared/components/integrations/models.ts
@@ -17,4 +17,8 @@ export type Integration = {
* @example "2024-12-31"
*/
newBadgeExpiration?: string;
+ description?: string;
+ isConnected?: boolean;
+ canSetupConnection?: boolean;
+ configuration?: string;
};
diff --git a/apps/web/src/app/admin-console/organizations/shared/validators/free-org-collection-limit.validator.spec.ts b/apps/web/src/app/admin-console/organizations/shared/validators/free-org-collection-limit.validator.spec.ts
index 120a58d6b1a..f9d389c979f 100644
--- a/apps/web/src/app/admin-console/organizations/shared/validators/free-org-collection-limit.validator.spec.ts
+++ b/apps/web/src/app/admin-console/organizations/shared/validators/free-org-collection-limit.validator.spec.ts
@@ -18,7 +18,7 @@ describe("freeOrgCollectionLimitValidator", () => {
it("returns null if organization is not found", async () => {
const orgs: Organization[] = [];
- const validator = freeOrgCollectionLimitValidator(of(orgs), [], i18nService);
+ const validator = freeOrgCollectionLimitValidator(of(orgs), of([]), i18nService);
const control = new FormControl("org-id");
const result: Observable = validator(control) as Observable;
@@ -28,7 +28,7 @@ describe("freeOrgCollectionLimitValidator", () => {
});
it("returns null if control is not an instance of FormControl", async () => {
- const validator = freeOrgCollectionLimitValidator(of([]), [], i18nService);
+ const validator = freeOrgCollectionLimitValidator(of([]), of([]), i18nService);
const control = {} as AbstractControl;
const result: Observable = validator(
@@ -40,7 +40,7 @@ describe("freeOrgCollectionLimitValidator", () => {
});
it("returns null if control is not provided", async () => {
- const validator = freeOrgCollectionLimitValidator(of([]), [], i18nService);
+ const validator = freeOrgCollectionLimitValidator(of([]), of([]), i18nService);
const result: Observable = validator(
undefined as any,
@@ -53,7 +53,7 @@ describe("freeOrgCollectionLimitValidator", () => {
it("returns null if organization has not reached collection limit (Observable)", async () => {
const org = { id: "org-id", maxCollections: 2 } as Organization;
const collections = [{ organizationId: "org-id" } as Collection];
- const validator = freeOrgCollectionLimitValidator(of([org]), collections, i18nService);
+ const validator = freeOrgCollectionLimitValidator(of([org]), of(collections), i18nService);
const control = new FormControl("org-id");
const result$ = validator(control) as Observable;
@@ -65,7 +65,7 @@ describe("freeOrgCollectionLimitValidator", () => {
it("returns error if organization has reached collection limit (Observable)", async () => {
const org = { id: "org-id", maxCollections: 1 } as Organization;
const collections = [{ organizationId: "org-id" } as Collection];
- const validator = freeOrgCollectionLimitValidator(of([org]), collections, i18nService);
+ const validator = freeOrgCollectionLimitValidator(of([org]), of(collections), i18nService);
const control = new FormControl("org-id");
const result$ = validator(control) as Observable;
diff --git a/apps/web/src/app/admin-console/organizations/shared/validators/free-org-collection-limit.validator.ts b/apps/web/src/app/admin-console/organizations/shared/validators/free-org-collection-limit.validator.ts
index 75919d31c1a..7132428c375 100644
--- a/apps/web/src/app/admin-console/organizations/shared/validators/free-org-collection-limit.validator.ts
+++ b/apps/web/src/app/admin-console/organizations/shared/validators/free-org-collection-limit.validator.ts
@@ -1,13 +1,14 @@
import { AbstractControl, AsyncValidatorFn, FormControl, ValidationErrors } from "@angular/forms";
-import { map, Observable, of } from "rxjs";
+import { combineLatest, map, Observable, of } from "rxjs";
import { Collection } from "@bitwarden/admin-console/common";
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
+import { getById } from "@bitwarden/common/platform/misc";
export function freeOrgCollectionLimitValidator(
- orgs: Observable,
- collections: Collection[],
+ organizations$: Observable,
+ collections$: Observable,
i18nService: I18nService,
): AsyncValidatorFn {
return (control: AbstractControl): Observable => {
@@ -21,15 +22,16 @@ export function freeOrgCollectionLimitValidator(
return of(null);
}
- return orgs.pipe(
- map((organizations) => organizations.find((org) => org.id === orgId)),
- map((org) => {
- if (!org) {
+ return combineLatest([organizations$.pipe(getById(orgId)), collections$]).pipe(
+ map(([organization, collections]) => {
+ if (!organization) {
return null;
}
- const orgCollections = collections.filter((c) => c.organizationId === org.id);
- const hasReachedLimit = org.maxCollections === orgCollections.length;
+ const orgCollections = collections.filter(
+ (collection: Collection) => collection.organizationId === organization.id,
+ );
+ const hasReachedLimit = organization.maxCollections === orgCollections.length;
if (hasReachedLimit) {
return {
diff --git a/apps/web/src/app/app.component.ts b/apps/web/src/app/app.component.ts
index 1bdb0c37728..0729d42f053 100644
--- a/apps/web/src/app/app.component.ts
+++ b/apps/web/src/app/app.component.ts
@@ -35,12 +35,14 @@ import {
MasterPasswordPolicy,
PasswordGeneratorPolicy,
OrganizationDataOwnershipPolicy,
+ vNextOrganizationDataOwnershipPolicy,
RequireSsoPolicy,
ResetPasswordPolicy,
SendOptionsPolicy,
SingleOrgPolicy,
TwoFactorAuthenticationPolicy,
RemoveUnlockWithPinPolicy,
+ RestrictedItemTypesPolicy,
} from "./admin-console/organizations/policies";
const BroadcasterSubscriptionId = "AppComponent";
@@ -244,8 +246,10 @@ export class AppComponent implements OnDestroy, OnInit {
new SingleOrgPolicy(),
new RequireSsoPolicy(),
new OrganizationDataOwnershipPolicy(),
+ new vNextOrganizationDataOwnershipPolicy(),
new DisableSendPolicy(),
new SendOptionsPolicy(),
+ new RestrictedItemTypesPolicy(),
]);
}
@@ -285,7 +289,6 @@ export class AppComponent implements OnDestroy, OnInit {
this.keyService.clearKeys(userId),
this.cipherService.clear(userId),
this.folderService.clear(userId),
- this.collectionService.clear(userId),
this.biometricStateService.logout(userId),
]);
diff --git a/apps/web/src/app/auth/core/services/index.ts b/apps/web/src/app/auth/core/services/index.ts
index 8c556986225..02f13cd436b 100644
--- a/apps/web/src/app/auth/core/services/index.ts
+++ b/apps/web/src/app/auth/core/services/index.ts
@@ -3,7 +3,6 @@ export * from "./login";
export * from "./login-decryption-options";
export * from "./webauthn-login";
export * from "./password-management";
-export * from "./set-password-jit";
export * from "./registration";
export * from "./two-factor-auth";
export * from "./link-sso.service";
diff --git a/apps/web/src/app/auth/core/services/login/web-login-component.service.spec.ts b/apps/web/src/app/auth/core/services/login/web-login-component.service.spec.ts
index 4cc06baf32b..799e10bc15c 100644
--- a/apps/web/src/app/auth/core/services/login/web-login-component.service.spec.ts
+++ b/apps/web/src/app/auth/core/services/login/web-login-component.service.spec.ts
@@ -1,6 +1,5 @@
import { TestBed } from "@angular/core/testing";
import { MockProxy, mock } from "jest-mock-extended";
-import { of } from "rxjs";
import { DefaultLoginComponentService } from "@bitwarden/auth/angular";
import { PolicyApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/policy/policy-api.service.abstraction";
@@ -138,8 +137,8 @@ describe("WebLoginComponentService", () => {
resetPasswordPolicyEnabled,
]);
- internalPolicyService.masterPasswordPolicyOptions$.mockReturnValue(
- of(masterPasswordPolicyOptions),
+ internalPolicyService.combinePoliciesIntoMasterPasswordPolicyOptions.mockReturnValue(
+ masterPasswordPolicyOptions,
);
const result = await service.getOrgPoliciesFromOrgInvite();
diff --git a/apps/web/src/app/auth/core/services/login/web-login-component.service.ts b/apps/web/src/app/auth/core/services/login/web-login-component.service.ts
index cf0adb91144..4ee84ecfde2 100644
--- a/apps/web/src/app/auth/core/services/login/web-login-component.service.ts
+++ b/apps/web/src/app/auth/core/services/login/web-login-component.service.ts
@@ -2,7 +2,6 @@
// @ts-strict-ignore
import { Injectable } from "@angular/core";
import { Router } from "@angular/router";
-import { firstValueFrom, switchMap } from "rxjs";
import {
DefaultLoginComponentService,
@@ -11,13 +10,10 @@ import {
} from "@bitwarden/auth/angular";
import { PolicyApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/policy/policy-api.service.abstraction";
import { InternalPolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
-import { MasterPasswordPolicyOptions } from "@bitwarden/common/admin-console/models/domain/master-password-policy-options";
import { Policy } from "@bitwarden/common/admin-console/models/domain/policy";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { SsoLoginServiceAbstraction } from "@bitwarden/common/auth/abstractions/sso-login.service.abstraction";
-import { getUserId } from "@bitwarden/common/auth/services/account.service";
import { OrganizationInviteService } from "@bitwarden/common/auth/services/organization-invite/organization-invite.service";
-import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
import { CryptoFunctionService } from "@bitwarden/common/key-management/crypto/abstractions/crypto-function.service";
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service";
@@ -99,23 +95,8 @@ export class WebLoginComponentService
const isPolicyAndAutoEnrollEnabled =
resetPasswordPolicy[1] && resetPasswordPolicy[0].autoEnrollEnabled;
- let enforcedPasswordPolicyOptions: MasterPasswordPolicyOptions;
-
- if (
- await this.configService.getFeatureFlag(FeatureFlag.PM16117_ChangeExistingPasswordRefactor)
- ) {
- enforcedPasswordPolicyOptions =
- this.policyService.combinePoliciesIntoMasterPasswordPolicyOptions(policies);
- } else {
- enforcedPasswordPolicyOptions = await firstValueFrom(
- this.accountService.activeAccount$.pipe(
- getUserId,
- switchMap((userId) =>
- this.policyService.masterPasswordPolicyOptions$(userId, policies),
- ),
- ),
- );
- }
+ const enforcedPasswordPolicyOptions =
+ this.policyService.combinePoliciesIntoMasterPasswordPolicyOptions(policies);
return {
policies,
diff --git a/apps/web/src/app/auth/core/services/set-password-jit/index.ts b/apps/web/src/app/auth/core/services/set-password-jit/index.ts
deleted file mode 100644
index fc119fd964f..00000000000
--- a/apps/web/src/app/auth/core/services/set-password-jit/index.ts
+++ /dev/null
@@ -1 +0,0 @@
-export * from "./web-set-password-jit.service";
diff --git a/apps/web/src/app/auth/core/services/set-password-jit/web-set-password-jit.service.ts b/apps/web/src/app/auth/core/services/set-password-jit/web-set-password-jit.service.ts
deleted file mode 100644
index 3078b8e3b83..00000000000
--- a/apps/web/src/app/auth/core/services/set-password-jit/web-set-password-jit.service.ts
+++ /dev/null
@@ -1,27 +0,0 @@
-import { inject } from "@angular/core";
-
-import {
- DefaultSetPasswordJitService,
- SetPasswordCredentials,
- SetPasswordJitService,
-} from "@bitwarden/auth/angular";
-import { OrganizationInviteService } from "@bitwarden/common/auth/services/organization-invite/organization-invite.service";
-
-import { RouterService } from "../../../../core/router.service";
-
-export class WebSetPasswordJitService
- extends DefaultSetPasswordJitService
- implements SetPasswordJitService
-{
- routerService = inject(RouterService);
- organizationInviteService = inject(OrganizationInviteService);
-
- override async setPassword(credentials: SetPasswordCredentials) {
- await super.setPassword(credentials);
-
- // SSO JIT accepts org invites when setting their MP, meaning
- // we can clear the deep linked url for accepting it.
- await this.routerService.getAndClearLoginRedirectUrl();
- await this.organizationInviteService.clearOrganizationInvitation();
- }
-}
diff --git a/apps/web/src/app/auth/emergency-access/services/emergency-access.service.spec.ts b/apps/web/src/app/auth/emergency-access/services/emergency-access.service.spec.ts
index 05373534ce7..2ff38f6eab0 100644
--- a/apps/web/src/app/auth/emergency-access/services/emergency-access.service.spec.ts
+++ b/apps/web/src/app/auth/emergency-access/services/emergency-access.service.spec.ts
@@ -2,23 +2,22 @@
// @ts-strict-ignore
import { MockProxy } from "jest-mock-extended";
import mock from "jest-mock-extended/lib/Mock";
+import { of } from "rxjs";
import { ApiService } from "@bitwarden/common/abstractions/api.service";
-import { BulkEncryptService } from "@bitwarden/common/key-management/crypto/abstractions/bulk-encrypt.service";
import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service";
import { EncString } from "@bitwarden/common/key-management/crypto/models/enc-string";
import { ListResponse } from "@bitwarden/common/models/response/list.response";
import { UserKeyResponse } from "@bitwarden/common/models/response/user-key.response";
-import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { EncryptionType } from "@bitwarden/common/platform/enums";
import { Utils } from "@bitwarden/common/platform/misc/utils";
import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key";
import { CsprngArray } from "@bitwarden/common/types/csprng";
import { UserId } from "@bitwarden/common/types/guid";
-import { UserKey, MasterKey } from "@bitwarden/common/types/key";
+import { UserKey, MasterKey, UserPrivateKey } from "@bitwarden/common/types/key";
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
-import { KdfType, KeyService } from "@bitwarden/key-management";
+import { Argon2KdfConfig, KdfType, KeyService, PBKDF2KdfConfig } from "@bitwarden/key-management";
import { EmergencyAccessStatusType } from "../enums/emergency-access-status-type";
import { EmergencyAccessType } from "../enums/emergency-access-type";
@@ -28,6 +27,7 @@ import {
EmergencyAccessGranteeDetailsResponse,
EmergencyAccessGrantorDetailsResponse,
EmergencyAccessTakeoverResponse,
+ EmergencyAccessViewResponse,
} from "../response/emergency-access.response";
import { EmergencyAccessApiService } from "./emergency-access-api.service";
@@ -38,11 +38,9 @@ describe("EmergencyAccessService", () => {
let apiService: MockProxy;
let keyService: MockProxy;
let encryptService: MockProxy;
- let bulkEncryptService: MockProxy;
let cipherService: MockProxy;
let logService: MockProxy;
let emergencyAccessService: EmergencyAccessService;
- let configService: ConfigService;
const mockNewUserKey = new SymmetricCryptoKey(new Uint8Array(64)) as UserKey;
const mockTrustedPublicKeys = [Utils.fromUtf8ToArray("trustedPublicKey")];
@@ -52,7 +50,6 @@ describe("EmergencyAccessService", () => {
apiService = mock();
keyService = mock();
encryptService = mock();
- bulkEncryptService = mock();
cipherService = mock();
logService = mock();
@@ -61,10 +58,8 @@ describe("EmergencyAccessService", () => {
apiService,
keyService,
encryptService,
- bulkEncryptService,
cipherService,
logService,
- configService,
);
});
@@ -149,88 +144,306 @@ describe("EmergencyAccessService", () => {
});
});
+ describe("getViewOnlyCiphers", () => {
+ const params = {
+ id: "emergency-access-id",
+ activeUserId: Utils.newGuid() as UserId,
+ };
+
+ it("throws an error is the active user's private key isn't available", async () => {
+ keyService.userPrivateKey$.mockReturnValue(of(null));
+
+ await expect(
+ emergencyAccessService.getViewOnlyCiphers(params.id, params.activeUserId),
+ ).rejects.toThrow("Active user does not have a private key, cannot get view only ciphers.");
+ });
+
+ it("should return decrypted and sorted ciphers", async () => {
+ const emergencyAccessViewResponse = {
+ keyEncrypted: "mockKeyEncrypted",
+ ciphers: [
+ { id: "cipher1", name: "encryptedName1" },
+ { id: "cipher2", name: "encryptedName2" },
+ ],
+ } as EmergencyAccessViewResponse;
+
+ const mockEncryptedCipher1 = {
+ id: "cipher1",
+ decrypt: jest.fn().mockResolvedValue({ id: "cipher1", decrypted: true }),
+ };
+ const mockEncryptedCipher2 = {
+ id: "cipher2",
+ decrypt: jest.fn().mockResolvedValue({ id: "cipher2", decrypted: true }),
+ };
+ emergencyAccessViewResponse.ciphers.map = jest.fn().mockImplementation(() => {
+ return [mockEncryptedCipher1, mockEncryptedCipher2];
+ });
+ cipherService.getLocaleSortingFunction.mockReturnValue((a: any, b: any) =>
+ a.id.localeCompare(b.id),
+ );
+ emergencyAccessApiService.postEmergencyAccessView.mockResolvedValue(
+ emergencyAccessViewResponse,
+ );
+
+ const mockPrivateKey = new Uint8Array(64) as UserPrivateKey;
+ keyService.userPrivateKey$.mockReturnValue(of(mockPrivateKey));
+
+ const mockDecryptedGrantorUserKey = new SymmetricCryptoKey(new Uint8Array(64));
+ encryptService.decapsulateKeyUnsigned.mockResolvedValueOnce(mockDecryptedGrantorUserKey);
+ const mockGrantorUserKey = mockDecryptedGrantorUserKey as UserKey;
+
+ const result = await emergencyAccessService.getViewOnlyCiphers(
+ params.id,
+ params.activeUserId,
+ );
+
+ expect(result).toEqual([
+ { id: "cipher1", decrypted: true },
+ { id: "cipher2", decrypted: true },
+ ]);
+ expect(mockEncryptedCipher1.decrypt).toHaveBeenCalledWith(mockGrantorUserKey);
+ expect(mockEncryptedCipher2.decrypt).toHaveBeenCalledWith(mockGrantorUserKey);
+ expect(emergencyAccessApiService.postEmergencyAccessView).toHaveBeenCalledWith(params.id);
+ expect(keyService.userPrivateKey$).toHaveBeenCalledWith(params.activeUserId);
+ expect(encryptService.decapsulateKeyUnsigned).toHaveBeenCalledWith(
+ new EncString(emergencyAccessViewResponse.keyEncrypted),
+ mockPrivateKey,
+ );
+ expect(cipherService.getLocaleSortingFunction).toHaveBeenCalled();
+ });
+ });
+
describe("takeover", () => {
- const mockId = "emergencyAccessId";
- const mockEmail = "emergencyAccessEmail";
- const mockName = "emergencyAccessName";
+ const params = {
+ id: "emergencyAccessId",
+ masterPassword: "mockPassword",
+ email: "emergencyAccessEmail",
+ activeUserId: Utils.newGuid() as UserId,
+ };
+
+ const takeoverResponse = {
+ keyEncrypted: "EncryptedKey",
+ kdf: KdfType.PBKDF2_SHA256,
+ kdfIterations: 500,
+ } as EmergencyAccessTakeoverResponse;
+
+ const userPrivateKey = new Uint8Array(64) as UserPrivateKey;
+ const mockMasterKey = new SymmetricCryptoKey(new Uint8Array(64) as CsprngArray) as MasterKey;
+ const mockMasterKeyHash = "mockMasterKeyHash";
+ let mockGrantorUserKey: UserKey;
+
+ // must mock [UserKey, EncString] return from keyService.encryptUserKeyWithMasterKey
+ // where UserKey is the decrypted grantor user key
+ const mockMasterKeyEncryptedUserKey = new EncString(
+ EncryptionType.AesCbc256_HmacSha256_B64,
+ "mockMasterKeyEncryptedUserKey",
+ );
+
+ beforeEach(() => {
+ emergencyAccessApiService.postEmergencyAccessTakeover.mockResolvedValueOnce(takeoverResponse);
+ keyService.userPrivateKey$.mockReturnValue(of(userPrivateKey));
+
+ const mockDecryptedGrantorUserKey = new SymmetricCryptoKey(new Uint8Array(64));
+ encryptService.decapsulateKeyUnsigned.mockResolvedValueOnce(mockDecryptedGrantorUserKey);
+ mockGrantorUserKey = mockDecryptedGrantorUserKey as UserKey;
+
+ keyService.makeMasterKey.mockResolvedValueOnce(mockMasterKey);
+ keyService.hashMasterKey.mockResolvedValueOnce(mockMasterKeyHash);
+ keyService.encryptUserKeyWithMasterKey.mockResolvedValueOnce([
+ mockGrantorUserKey,
+ mockMasterKeyEncryptedUserKey,
+ ]);
+ });
it("posts a new password when decryption succeeds", async () => {
// Arrange
- emergencyAccessApiService.postEmergencyAccessTakeover.mockResolvedValueOnce({
- keyEncrypted: "EncryptedKey",
- kdf: KdfType.PBKDF2_SHA256,
- kdfIterations: 500,
- } as EmergencyAccessTakeoverResponse);
-
- const mockDecryptedGrantorUserKey = new Uint8Array(64);
- keyService.getPrivateKey.mockResolvedValue(new Uint8Array(64));
- encryptService.decapsulateKeyUnsigned.mockResolvedValueOnce(
- new SymmetricCryptoKey(mockDecryptedGrantorUserKey),
- );
-
- const mockMasterKey = new SymmetricCryptoKey(new Uint8Array(64) as CsprngArray) as MasterKey;
-
- keyService.makeMasterKey.mockResolvedValueOnce(mockMasterKey);
-
- const mockMasterKeyHash = "mockMasterKeyHash";
- keyService.hashMasterKey.mockResolvedValueOnce(mockMasterKeyHash);
-
- // must mock [UserKey, EncString] return from keyService.encryptUserKeyWithMasterKey
- // where UserKey is the decrypted grantor user key
- const mockMasterKeyEncryptedUserKey = new EncString(
- EncryptionType.AesCbc256_HmacSha256_B64,
- "mockMasterKeyEncryptedUserKey",
- );
-
- const mockUserKey = new SymmetricCryptoKey(mockDecryptedGrantorUserKey) as UserKey;
-
- keyService.encryptUserKeyWithMasterKey.mockResolvedValueOnce([
- mockUserKey,
- mockMasterKeyEncryptedUserKey,
- ]);
+ const expectedKdfConfig = new PBKDF2KdfConfig(takeoverResponse.kdfIterations);
const expectedEmergencyAccessPasswordRequest = new EmergencyAccessPasswordRequest();
expectedEmergencyAccessPasswordRequest.newMasterPasswordHash = mockMasterKeyHash;
expectedEmergencyAccessPasswordRequest.key = mockMasterKeyEncryptedUserKey.encryptedString;
// Act
- await emergencyAccessService.takeover(mockId, mockEmail, mockName);
+ await emergencyAccessService.takeover(
+ params.id,
+ params.masterPassword,
+ params.email,
+ params.activeUserId,
+ );
// Assert
+ expect(keyService.userPrivateKey$).toHaveBeenCalledWith(params.activeUserId);
+ expect(encryptService.decapsulateKeyUnsigned).toHaveBeenCalledWith(
+ new EncString(takeoverResponse.keyEncrypted),
+ userPrivateKey,
+ );
+ expect(keyService.makeMasterKey).toHaveBeenCalledWith(
+ params.masterPassword,
+ params.email,
+ expectedKdfConfig,
+ );
+ expect(keyService.hashMasterKey).toHaveBeenCalledWith(params.masterPassword, mockMasterKey);
+ expect(keyService.encryptUserKeyWithMasterKey).toHaveBeenCalledWith(
+ mockMasterKey,
+ mockGrantorUserKey,
+ );
expect(emergencyAccessApiService.postEmergencyAccessPassword).toHaveBeenCalledWith(
- mockId,
+ params.id,
expectedEmergencyAccessPasswordRequest,
);
});
- it("should not post a new password if decryption fails", async () => {
- encryptService.rsaDecrypt.mockResolvedValueOnce(null);
- emergencyAccessApiService.postEmergencyAccessTakeover.mockResolvedValueOnce({
+ it("uses argon2 KDF if takeover response is argon2", async () => {
+ const argon2TakeoverResponse = {
keyEncrypted: "EncryptedKey",
- kdf: KdfType.PBKDF2_SHA256,
- kdfIterations: 500,
- } as EmergencyAccessTakeoverResponse);
- keyService.getPrivateKey.mockResolvedValue(new Uint8Array(64));
+ kdf: KdfType.Argon2id,
+ kdfIterations: 3,
+ kdfMemory: 64,
+ kdfParallelism: 4,
+ } as EmergencyAccessTakeoverResponse;
+ emergencyAccessApiService.postEmergencyAccessTakeover.mockReset();
+ emergencyAccessApiService.postEmergencyAccessTakeover.mockResolvedValueOnce(
+ argon2TakeoverResponse,
+ );
+
+ const expectedKdfConfig = new Argon2KdfConfig(
+ argon2TakeoverResponse.kdfIterations,
+ argon2TakeoverResponse.kdfMemory,
+ argon2TakeoverResponse.kdfParallelism,
+ );
+
+ const expectedEmergencyAccessPasswordRequest = new EmergencyAccessPasswordRequest();
+ expectedEmergencyAccessPasswordRequest.newMasterPasswordHash = mockMasterKeyHash;
+ expectedEmergencyAccessPasswordRequest.key = mockMasterKeyEncryptedUserKey.encryptedString;
+
+ await emergencyAccessService.takeover(
+ params.id,
+ params.masterPassword,
+ params.email,
+ params.activeUserId,
+ );
+
+ expect(keyService.userPrivateKey$).toHaveBeenCalledWith(params.activeUserId);
+ expect(encryptService.decapsulateKeyUnsigned).toHaveBeenCalledWith(
+ new EncString(argon2TakeoverResponse.keyEncrypted),
+ userPrivateKey,
+ );
+ expect(keyService.makeMasterKey).toHaveBeenCalledWith(
+ params.masterPassword,
+ params.email,
+ expectedKdfConfig,
+ );
+ expect(keyService.hashMasterKey).toHaveBeenCalledWith(params.masterPassword, mockMasterKey);
+ expect(keyService.encryptUserKeyWithMasterKey).toHaveBeenCalledWith(
+ mockMasterKey,
+ mockGrantorUserKey,
+ );
+ expect(emergencyAccessApiService.postEmergencyAccessPassword).toHaveBeenCalledWith(
+ params.id,
+ expectedEmergencyAccessPasswordRequest,
+ );
+ });
+
+ it("throws an error if masterKeyEncryptedUserKey is not found", async () => {
+ keyService.encryptUserKeyWithMasterKey.mockReset();
+ keyService.encryptUserKeyWithMasterKey.mockResolvedValueOnce(null);
+ const expectedKdfConfig = new PBKDF2KdfConfig(takeoverResponse.kdfIterations);
await expect(
- emergencyAccessService.takeover(mockId, mockEmail, mockName),
- ).rejects.toThrowError("Failed to decrypt grantor key");
+ emergencyAccessService.takeover(
+ params.id,
+ params.masterPassword,
+ params.email,
+ params.activeUserId,
+ ),
+ ).rejects.toThrow("masterKeyEncryptedUserKey not found");
+ expect(keyService.userPrivateKey$).toHaveBeenCalledWith(params.activeUserId);
+ expect(encryptService.decapsulateKeyUnsigned).toHaveBeenCalledWith(
+ new EncString(takeoverResponse.keyEncrypted),
+ userPrivateKey,
+ );
+ expect(keyService.makeMasterKey).toHaveBeenCalledWith(
+ params.masterPassword,
+ params.email,
+ expectedKdfConfig,
+ );
+ expect(keyService.hashMasterKey).toHaveBeenCalledWith(params.masterPassword, mockMasterKey);
+ expect(keyService.encryptUserKeyWithMasterKey).toHaveBeenCalledWith(
+ mockMasterKey,
+ mockGrantorUserKey,
+ );
+ expect(emergencyAccessApiService.postEmergencyAccessPassword).not.toHaveBeenCalled();
+ });
+
+ it("should not post a new password if decryption fails", async () => {
+ emergencyAccessApiService.postEmergencyAccessTakeover.mockResolvedValueOnce(takeoverResponse);
+ encryptService.decapsulateKeyUnsigned.mockReset();
+ encryptService.decapsulateKeyUnsigned.mockResolvedValueOnce(null);
+
+ await expect(
+ emergencyAccessService.takeover(
+ params.id,
+ params.masterPassword,
+ params.email,
+ params.activeUserId,
+ ),
+ ).rejects.toThrow("Failed to decrypt grantor key");
+
+ expect(keyService.userPrivateKey$).toHaveBeenCalledWith(params.activeUserId);
+ expect(encryptService.decapsulateKeyUnsigned).toHaveBeenCalledWith(
+ new EncString(takeoverResponse.keyEncrypted),
+ userPrivateKey,
+ );
+ expect(keyService.makeMasterKey).not.toHaveBeenCalled();
+ expect(keyService.hashMasterKey).not.toHaveBeenCalled();
+ expect(keyService.encryptUserKeyWithMasterKey).not.toHaveBeenCalled();
+ expect(emergencyAccessApiService.postEmergencyAccessPassword).not.toHaveBeenCalled();
+ });
+
+ it("should not post a new password if decryption throws", async () => {
+ encryptService.decapsulateKeyUnsigned.mockReset();
+ encryptService.decapsulateKeyUnsigned.mockImplementationOnce(() => {
+ throw new Error("Failed to unwrap grantor key");
+ });
+
+ await expect(
+ emergencyAccessService.takeover(
+ params.id,
+ params.masterPassword,
+ params.email,
+ params.activeUserId,
+ ),
+ ).rejects.toThrowError("Failed to unwrap grantor key");
+
+ expect(keyService.userPrivateKey$).toHaveBeenCalledWith(params.activeUserId);
+ expect(encryptService.decapsulateKeyUnsigned).toHaveBeenCalledWith(
+ new EncString(takeoverResponse.keyEncrypted),
+ userPrivateKey,
+ );
+ expect(keyService.makeMasterKey).not.toHaveBeenCalled();
+ expect(keyService.hashMasterKey).not.toHaveBeenCalled();
+ expect(keyService.encryptUserKeyWithMasterKey).not.toHaveBeenCalled();
expect(emergencyAccessApiService.postEmergencyAccessPassword).not.toHaveBeenCalled();
});
it("should throw an error if the users private key cannot be retrieved", async () => {
- emergencyAccessApiService.postEmergencyAccessTakeover.mockResolvedValueOnce({
- keyEncrypted: "EncryptedKey",
- kdf: KdfType.PBKDF2_SHA256,
- kdfIterations: 500,
- } as EmergencyAccessTakeoverResponse);
- keyService.getPrivateKey.mockResolvedValue(null);
+ keyService.userPrivateKey$.mockReturnValue(of(null));
- await expect(emergencyAccessService.takeover(mockId, mockEmail, mockName)).rejects.toThrow(
- "user does not have a private key",
- );
+ await expect(
+ emergencyAccessService.takeover(
+ params.id,
+ params.masterPassword,
+ params.email,
+ params.activeUserId,
+ ),
+ ).rejects.toThrow("user does not have a private key");
+ expect(keyService.userPrivateKey$).toHaveBeenCalledWith(params.activeUserId);
+ expect(encryptService.decapsulateKeyUnsigned).not.toHaveBeenCalled();
+ expect(keyService.makeMasterKey).not.toHaveBeenCalled();
+ expect(keyService.hashMasterKey).not.toHaveBeenCalled();
+ expect(keyService.encryptUserKeyWithMasterKey).not.toHaveBeenCalled();
expect(emergencyAccessApiService.postEmergencyAccessPassword).not.toHaveBeenCalled();
});
});
diff --git a/apps/web/src/app/auth/emergency-access/services/emergency-access.service.ts b/apps/web/src/app/auth/emergency-access/services/emergency-access.service.ts
index a814af32505..cce8d9345b2 100644
--- a/apps/web/src/app/auth/emergency-access/services/emergency-access.service.ts
+++ b/apps/web/src/app/auth/emergency-access/services/emergency-access.service.ts
@@ -1,16 +1,14 @@
import { Injectable } from "@angular/core";
+import { firstValueFrom } from "rxjs";
import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { PolicyData } from "@bitwarden/common/admin-console/models/data/policy.data";
import { Policy } from "@bitwarden/common/admin-console/models/domain/policy";
-import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
-import { BulkEncryptService } from "@bitwarden/common/key-management/crypto/abstractions/bulk-encrypt.service";
import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service";
import {
EncryptedString,
EncString,
} from "@bitwarden/common/key-management/crypto/models/enc-string";
-import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { Utils } from "@bitwarden/common/platform/misc/utils";
import { UserId } from "@bitwarden/common/types/guid";
@@ -59,10 +57,8 @@ export class EmergencyAccessService
private apiService: ApiService,
private keyService: KeyService,
private encryptService: EncryptService,
- private bulkEncryptService: BulkEncryptService,
private cipherService: CipherService,
private logService: LogService,
- private configService: ConfigService,
) {}
/**
@@ -242,11 +238,14 @@ export class EmergencyAccessService
* Gets the grantor ciphers for an emergency access in view mode.
* Intended for grantee.
* @param id emergency access id
+ * @param activeUserId the user id of the active user
*/
- async getViewOnlyCiphers(id: string): Promise {
+ async getViewOnlyCiphers(id: string, activeUserId: UserId): Promise {
const response = await this.emergencyAccessApiService.postEmergencyAccessView(id);
- const activeUserPrivateKey = await this.keyService.getPrivateKey();
+ const activeUserPrivateKey = await firstValueFrom(
+ this.keyService.userPrivateKey$(activeUserId),
+ );
if (activeUserPrivateKey == null) {
throw new Error("Active user does not have a private key, cannot get view only ciphers.");
@@ -258,17 +257,8 @@ export class EmergencyAccessService
)) as UserKey;
let ciphers: CipherView[] = [];
- if (await this.configService.getFeatureFlag(FeatureFlag.PM4154_BulkEncryptionService)) {
- ciphers = await this.bulkEncryptService.decryptItems(
- response.ciphers.map((c) => new Cipher(c)),
- grantorUserKey,
- );
- } else {
- ciphers = await this.encryptService.decryptItems(
- response.ciphers.map((c) => new Cipher(c)),
- grantorUserKey,
- );
- }
+ const ciphersEncrypted = response.ciphers.map((c) => new Cipher(c));
+ ciphers = await Promise.all(ciphersEncrypted.map(async (c) => c.decrypt(grantorUserKey)));
return ciphers.sort(this.cipherService.getLocaleSortingFunction());
}
@@ -278,11 +268,14 @@ export class EmergencyAccessService
* @param id emergency access id
* @param masterPassword new master password
* @param email email address of grantee (must be consistent or login will fail)
+ * @param activeUserId the user id of the active user
*/
- async takeover(id: string, masterPassword: string, email: string) {
+ async takeover(id: string, masterPassword: string, email: string, activeUserId: UserId) {
const takeoverResponse = await this.emergencyAccessApiService.postEmergencyAccessTakeover(id);
- const activeUserPrivateKey = await this.keyService.getPrivateKey();
+ const activeUserPrivateKey = await firstValueFrom(
+ this.keyService.userPrivateKey$(activeUserId),
+ );
if (activeUserPrivateKey == null) {
throw new Error("Active user does not have a private key, cannot complete a takeover.");
@@ -326,9 +319,7 @@ export class EmergencyAccessService
request.newMasterPasswordHash = masterKeyHash;
request.key = encKey[1].encryptedString;
- // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
- // eslint-disable-next-line @typescript-eslint/no-floating-promises
- this.emergencyAccessApiService.postEmergencyAccessPassword(id, request);
+ await this.emergencyAccessApiService.postEmergencyAccessPassword(id, request);
}
private async getEmergencyAccessData(): Promise {
diff --git a/apps/web/src/app/auth/set-password.component.html b/apps/web/src/app/auth/set-password.component.html
deleted file mode 100644
index 252893d22cb..00000000000
--- a/apps/web/src/app/auth/set-password.component.html
+++ /dev/null
@@ -1,130 +0,0 @@
-
-
-
-
{{ "setMasterPassword" | i18n }}
-
-
-
- {{ "loading" | i18n }}
-
-
-
- {{ "orgPermissionsUpdatedMustSetPassword" | i18n }}
-
-
-
- {{ "orgRequiresYouToSetPassword" | i18n }}
-
-
-
- {{ "resetPasswordAutoEnrollInviteWarning" | i18n }}
-
-
-
-
- {{ "masterPassHint" | i18n }}
-
- {{ "masterPassHintDesc" | i18n }}
-
-
-
-
-
- {{ "submit" | i18n }}
-
-
- {{ "logOut" | i18n }}
-
-
-
-
-
-
-
diff --git a/apps/web/src/app/auth/set-password.component.ts b/apps/web/src/app/auth/set-password.component.ts
deleted file mode 100644
index a2044e298a5..00000000000
--- a/apps/web/src/app/auth/set-password.component.ts
+++ /dev/null
@@ -1,30 +0,0 @@
-import { Component, inject } from "@angular/core";
-
-import { SetPasswordComponent as BaseSetPasswordComponent } from "@bitwarden/angular/auth/components/set-password.component";
-import { OrganizationInviteService } from "@bitwarden/common/auth/services/organization-invite/organization-invite.service";
-import { EncString } from "@bitwarden/common/key-management/crypto/models/enc-string";
-import { MasterKey, UserKey } from "@bitwarden/common/types/key";
-
-import { RouterService } from "../core";
-
-@Component({
- selector: "app-set-password",
- templateUrl: "set-password.component.html",
- standalone: false,
-})
-export class SetPasswordComponent extends BaseSetPasswordComponent {
- routerService = inject(RouterService);
- organizationInviteService = inject(OrganizationInviteService);
-
- protected override async onSetPasswordSuccess(
- masterKey: MasterKey,
- userKey: [UserKey, EncString],
- keyPair: [string, EncString],
- ): Promise {
- await super.onSetPasswordSuccess(masterKey, userKey, keyPair);
- // SSO JIT accepts org invites when setting their MP, meaning
- // we can clear the deep linked url for accepting it.
- await this.routerService.getAndClearLoginRedirectUrl();
- await this.organizationInviteService.clearOrganizationInvitation();
- }
-}
diff --git a/apps/web/src/app/auth/settings/account/change-email.component.spec.ts b/apps/web/src/app/auth/settings/account/change-email.component.spec.ts
index f5c0733e5b0..bd0d9df9f06 100644
--- a/apps/web/src/app/auth/settings/account/change-email.component.spec.ts
+++ b/apps/web/src/app/auth/settings/account/change-email.component.spec.ts
@@ -89,7 +89,7 @@ describe("ChangeEmailComponent", () => {
});
keyService.getOrDeriveMasterKey
- .calledWith("password", "UserId")
+ .calledWith("password", "UserId" as UserId)
.mockResolvedValue("getOrDeriveMasterKey" as any);
keyService.hashMasterKey
.calledWith("password", "getOrDeriveMasterKey" as any)
diff --git a/apps/web/src/app/auth/settings/change-password.component.html b/apps/web/src/app/auth/settings/change-password.component.html
deleted file mode 100644
index 34bb74ee473..00000000000
--- a/apps/web/src/app/auth/settings/change-password.component.html
+++ /dev/null
@@ -1,129 +0,0 @@
-
-
-{{ "loggedOutWarning" | i18n }}
-
-
-
-
-
-
-
- {{ "currentMasterPass" | i18n }}
-
-
-
-
-
-
-
-
- {{ "confirmNewMasterPass" | i18n }}
-
-
-
-
-
-
-
- {{ "newMasterPassHint" | i18n }}
-
-
-
- {{ "changeMasterPassword" | i18n }}
-
-
-
-
diff --git a/apps/web/src/app/auth/settings/change-password.component.ts b/apps/web/src/app/auth/settings/change-password.component.ts
deleted file mode 100644
index ce10a0e5a34..00000000000
--- a/apps/web/src/app/auth/settings/change-password.component.ts
+++ /dev/null
@@ -1,258 +0,0 @@
-// FIXME: Update this file to be type safe and remove this and next line
-// @ts-strict-ignore
-import { Component, OnDestroy, OnInit } from "@angular/core";
-import { Router } from "@angular/router";
-import { firstValueFrom, map } from "rxjs";
-
-import { ChangePasswordComponent as BaseChangePasswordComponent } from "@bitwarden/angular/auth/components/change-password.component";
-import { AuditService } from "@bitwarden/common/abstractions/audit.service";
-import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
-import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
-import { MasterPasswordApiService } from "@bitwarden/common/auth/abstractions/master-password-api.service.abstraction";
-import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction";
-import { PasswordRequest } from "@bitwarden/common/auth/models/request/password.request";
-import { getUserId } from "@bitwarden/common/auth/services/account.service";
-import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/key-management/master-password/abstractions/master-password.service.abstraction";
-import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
-import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
-import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
-import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
-import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction";
-import { DialogService, ToastService } from "@bitwarden/components";
-import { KdfConfigService, KeyService } from "@bitwarden/key-management";
-
-import { UserKeyRotationService } from "../../key-management/key-rotation/user-key-rotation.service";
-
-/**
- * @deprecated use the auth `PasswordSettingsComponent` instead
- */
-@Component({
- selector: "app-change-password",
- templateUrl: "change-password.component.html",
- standalone: false,
-})
-export class ChangePasswordComponent
- extends BaseChangePasswordComponent
- implements OnInit, OnDestroy
-{
- loading = false;
- rotateUserKey = false;
- currentMasterPassword: string;
- masterPasswordHint: string;
- checkForBreaches = true;
- characterMinimumMessage = "";
-
- constructor(
- private auditService: AuditService,
- private cipherService: CipherService,
- private keyRotationService: UserKeyRotationService,
- private masterPasswordApiService: MasterPasswordApiService,
- private router: Router,
- private syncService: SyncService,
- private userVerificationService: UserVerificationService,
- protected accountService: AccountService,
- protected dialogService: DialogService,
- protected i18nService: I18nService,
- protected kdfConfigService: KdfConfigService,
- protected keyService: KeyService,
- protected masterPasswordService: InternalMasterPasswordServiceAbstraction,
- protected messagingService: MessagingService,
- protected platformUtilsService: PlatformUtilsService,
- protected policyService: PolicyService,
- protected toastService: ToastService,
- ) {
- super(
- accountService,
- dialogService,
- i18nService,
- kdfConfigService,
- keyService,
- masterPasswordService,
- messagingService,
- platformUtilsService,
- policyService,
- toastService,
- );
- }
-
- async ngOnInit() {
- if (!(await this.userVerificationService.hasMasterPassword())) {
- // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
- // eslint-disable-next-line @typescript-eslint/no-floating-promises
- this.router.navigate(["/settings/security/two-factor"]);
- }
-
- await super.ngOnInit();
-
- this.characterMinimumMessage = this.i18nService.t("characterMinimum", this.minimumLength);
- }
-
- async rotateUserKeyClicked() {
- if (this.rotateUserKey) {
- const activeUserId = await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId));
-
- const ciphers = await this.cipherService.getAllDecrypted(activeUserId);
- let hasOldAttachments = false;
- if (ciphers != null) {
- for (let i = 0; i < ciphers.length; i++) {
- if (ciphers[i].organizationId == null && ciphers[i].hasOldAttachments) {
- hasOldAttachments = true;
- break;
- }
- }
- }
-
- if (hasOldAttachments) {
- const learnMore = await this.dialogService.openSimpleDialog({
- title: { key: "warning" },
- content: { key: "oldAttachmentsNeedFixDesc" },
- acceptButtonText: { key: "learnMore" },
- cancelButtonText: { key: "close" },
- type: "warning",
- });
-
- if (learnMore) {
- this.platformUtilsService.launchUri(
- "https://bitwarden.com/help/attachments/#add-storage-space",
- );
- }
- this.rotateUserKey = false;
- return;
- }
-
- const result = await this.dialogService.openSimpleDialog({
- title: { key: "rotateEncKeyTitle" },
- content:
- this.i18nService.t("updateEncryptionKeyWarning") +
- " " +
- this.i18nService.t("updateEncryptionKeyAccountExportWarning") +
- " " +
- this.i18nService.t("rotateEncKeyConfirmation"),
- type: "warning",
- });
-
- if (!result) {
- this.rotateUserKey = false;
- }
- }
- }
-
- async submit() {
- this.loading = true;
- if (this.currentMasterPassword == null || this.currentMasterPassword === "") {
- this.toastService.showToast({
- variant: "error",
- title: this.i18nService.t("errorOccurred"),
- message: this.i18nService.t("masterPasswordRequired"),
- });
- this.loading = false;
- return;
- }
-
- if (
- this.masterPasswordHint != null &&
- this.masterPasswordHint.toLowerCase() === this.masterPassword.toLowerCase()
- ) {
- this.toastService.showToast({
- variant: "error",
- title: this.i18nService.t("errorOccurred"),
- message: this.i18nService.t("hintEqualsPassword"),
- });
- this.loading = false;
- return;
- }
-
- this.leakedPassword = false;
- if (this.checkForBreaches) {
- this.leakedPassword = (await this.auditService.passwordLeaked(this.masterPassword)) > 0;
- }
-
- if (!(await this.strongPassword())) {
- this.loading = false;
- return;
- }
-
- try {
- if (this.rotateUserKey) {
- await this.syncService.fullSync(true);
- const user = await firstValueFrom(this.accountService.activeAccount$);
- await this.keyRotationService.rotateUserKeyMasterPasswordAndEncryptedData(
- this.currentMasterPassword,
- this.masterPassword,
- user,
- this.masterPasswordHint,
- );
- } else {
- await this.updatePassword(this.masterPassword);
- }
- } catch (e) {
- this.toastService.showToast({
- variant: "error",
- title: this.i18nService.t("errorOccurred"),
- message: e.message,
- });
- } finally {
- this.loading = false;
- }
- }
-
- // todo: move this to a service
- // https://bitwarden.atlassian.net/browse/PM-17108
- private async updatePassword(newMasterPassword: string) {
- const currentMasterPassword = this.currentMasterPassword;
- const { userId, email } = await firstValueFrom(
- this.accountService.activeAccount$.pipe(map((a) => ({ userId: a?.id, email: a?.email }))),
- );
- const kdfConfig = await firstValueFrom(this.kdfConfigService.getKdfConfig$(userId));
-
- const currentMasterKey = await this.keyService.makeMasterKey(
- currentMasterPassword,
- email,
- kdfConfig,
- );
- const decryptedUserKey = await this.masterPasswordService.decryptUserKeyWithMasterKey(
- currentMasterKey,
- userId,
- );
- if (decryptedUserKey == null) {
- this.toastService.showToast({
- variant: "error",
- title: null,
- message: this.i18nService.t("invalidMasterPassword"),
- });
- return;
- }
-
- const newMasterKey = await this.keyService.makeMasterKey(newMasterPassword, email, kdfConfig);
- const newMasterKeyEncryptedUserKey = await this.keyService.encryptUserKeyWithMasterKey(
- newMasterKey,
- decryptedUserKey,
- );
-
- const request = new PasswordRequest();
- request.masterPasswordHash = await this.keyService.hashMasterKey(
- this.currentMasterPassword,
- currentMasterKey,
- );
- request.masterPasswordHint = this.masterPasswordHint;
- request.newMasterPasswordHash = await this.keyService.hashMasterKey(
- newMasterPassword,
- newMasterKey,
- );
- request.key = newMasterKeyEncryptedUserKey[1].encryptedString;
- try {
- await this.masterPasswordApiService.postPassword(request);
- this.toastService.showToast({
- variant: "success",
- message: this.i18nService.t("masterPasswordChanged"),
- });
- this.messagingService.send("logout");
- } catch {
- this.toastService.showToast({
- variant: "error",
- title: null,
- message: this.i18nService.t("errorOccurred"),
- });
- }
- }
-}
diff --git a/apps/web/src/app/auth/settings/emergency-access/confirm/emergency-access-confirm.component.ts b/apps/web/src/app/auth/settings/emergency-access/confirm/emergency-access-confirm.component.ts
index cd7a585f3b1..641dde66cc4 100644
--- a/apps/web/src/app/auth/settings/emergency-access/confirm/emergency-access-confirm.component.ts
+++ b/apps/web/src/app/auth/settings/emergency-access/confirm/emergency-access-confirm.component.ts
@@ -8,6 +8,8 @@ import { LogService } from "@bitwarden/common/platform/abstractions/log.service"
import { DialogConfig, DialogRef, DIALOG_DATA, DialogService } from "@bitwarden/components";
import { KeyService } from "@bitwarden/key-management";
+import { SharedModule } from "../../../../shared";
+
// FIXME: update to use a const object instead of a typescript enum
// eslint-disable-next-line @bitwarden/platform/no-enums
export enum EmergencyAccessConfirmDialogResult {
@@ -24,9 +26,8 @@ type EmergencyAccessConfirmDialogData = {
publicKey: Uint8Array;
};
@Component({
- selector: "emergency-access-confirm",
templateUrl: "emergency-access-confirm.component.html",
- standalone: false,
+ imports: [SharedModule],
})
export class EmergencyAccessConfirmComponent implements OnInit {
loading = true;
diff --git a/apps/web/src/app/auth/settings/emergency-access/emergency-access-add-edit.component.ts b/apps/web/src/app/auth/settings/emergency-access/emergency-access-add-edit.component.ts
index 2f3f3a20b04..baa0f396fc5 100644
--- a/apps/web/src/app/auth/settings/emergency-access/emergency-access-add-edit.component.ts
+++ b/apps/web/src/app/auth/settings/emergency-access/emergency-access-add-edit.component.ts
@@ -14,6 +14,8 @@ import {
ToastService,
} from "@bitwarden/components";
+import { SharedModule } from "../../../shared/shared.module";
+import { PremiumBadgeComponent } from "../../../vault/components/premium-badge.component";
import { EmergencyAccessService } from "../../emergency-access";
import { EmergencyAccessType } from "../../emergency-access/enums/emergency-access-type";
@@ -34,9 +36,8 @@ export enum EmergencyAccessAddEditDialogResult {
Deleted = "deleted",
}
@Component({
- selector: "emergency-access-add-edit",
templateUrl: "emergency-access-add-edit.component.html",
- standalone: false,
+ imports: [SharedModule, PremiumBadgeComponent],
})
export class EmergencyAccessAddEditComponent implements OnInit {
loading = true;
diff --git a/apps/web/src/app/auth/settings/emergency-access/emergency-access.component.ts b/apps/web/src/app/auth/settings/emergency-access/emergency-access.component.ts
index 1d78bb7dd17..6de647dc5ce 100644
--- a/apps/web/src/app/auth/settings/emergency-access/emergency-access.component.ts
+++ b/apps/web/src/app/auth/settings/emergency-access/emergency-access.component.ts
@@ -10,8 +10,6 @@ import { OrganizationManagementPreferencesService } from "@bitwarden/common/admi
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { getUserId } from "@bitwarden/common/auth/services/account.service";
import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service";
-import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
-import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
@@ -20,6 +18,9 @@ import { StateService } from "@bitwarden/common/platform/abstractions/state.serv
import { Utils } from "@bitwarden/common/platform/misc/utils";
import { DialogService, ToastService } from "@bitwarden/components";
+import { HeaderModule } from "../../../layouts/header/header.module";
+import { SharedModule } from "../../../shared/shared.module";
+import { PremiumBadgeComponent } from "../../../vault/components/premium-badge.component";
import { EmergencyAccessService } from "../../emergency-access";
import { EmergencyAccessStatusType } from "../../emergency-access/enums/emergency-access-status-type";
import { EmergencyAccessType } from "../../emergency-access/enums/emergency-access-type";
@@ -40,15 +41,10 @@ import {
EmergencyAccessTakeoverDialogComponent,
EmergencyAccessTakeoverDialogResultType,
} from "./takeover/emergency-access-takeover-dialog.component";
-import {
- EmergencyAccessTakeoverComponent,
- EmergencyAccessTakeoverResultType,
-} from "./takeover/emergency-access-takeover.component";
@Component({
- selector: "emergency-access",
templateUrl: "emergency-access.component.html",
- standalone: false,
+ imports: [SharedModule, HeaderModule, PremiumBadgeComponent],
})
export class EmergencyAccessComponent implements OnInit {
loaded = false;
@@ -75,7 +71,6 @@ export class EmergencyAccessComponent implements OnInit {
private toastService: ToastService,
private apiService: ApiService,
private accountService: AccountService,
- private configService: ConfigService,
) {
this.canAccessPremium$ = this.accountService.activeAccount$.pipe(
switchMap((account) =>
@@ -292,60 +287,36 @@ export class EmergencyAccessComponent implements OnInit {
}
takeover = async (details: GrantorEmergencyAccess) => {
- const changePasswordRefactorFlag = await this.configService.getFeatureFlag(
- FeatureFlag.PM16117_ChangeExistingPasswordRefactor,
- );
-
- if (changePasswordRefactorFlag) {
- if (!details || !details.email || !details.id) {
- this.toastService.showToast({
- variant: "error",
- title: this.i18nService.t("errorOccurred"),
- message: this.i18nService.t("grantorDetailsNotFound"),
- });
- this.logService.error(
- "Grantor details not found when attempting emergency access takeover",
- );
-
- return;
- }
-
- const grantorName = this.userNamePipe.transform(details);
-
- const dialogRef = EmergencyAccessTakeoverDialogComponent.open(this.dialogService, {
- data: {
- grantorName,
- grantorEmail: details.email,
- emergencyAccessId: details.id,
- },
+ if (!details || !details.email || !details.id) {
+ this.toastService.showToast({
+ variant: "error",
+ title: this.i18nService.t("errorOccurred"),
+ message: this.i18nService.t("grantorDetailsNotFound"),
});
- const result = await lastValueFrom(dialogRef.closed);
- if (result === EmergencyAccessTakeoverDialogResultType.Done) {
- this.toastService.showToast({
- variant: "success",
- title: "",
- message: this.i18nService.t("passwordResetFor", grantorName),
- });
- }
+ this.logService.error("Grantor details not found when attempting emergency access takeover");
return;
}
- const dialogRef = EmergencyAccessTakeoverComponent.open(this.dialogService, {
+ const grantorName = this.userNamePipe.transform(details);
+
+ const dialogRef = EmergencyAccessTakeoverDialogComponent.open(this.dialogService, {
data: {
- name: this.userNamePipe.transform(details),
- email: details.email,
- emergencyAccessId: details.id ?? null,
+ grantorName,
+ grantorEmail: details.email,
+ emergencyAccessId: details.id,
},
});
const result = await lastValueFrom(dialogRef.closed);
- if (result === EmergencyAccessTakeoverResultType.Done) {
+ if (result === EmergencyAccessTakeoverDialogResultType.Done) {
this.toastService.showToast({
variant: "success",
- title: null,
- message: this.i18nService.t("passwordResetFor", this.userNamePipe.transform(details)),
+ title: "",
+ message: this.i18nService.t("passwordResetFor", grantorName),
});
}
+
+ return;
};
private removeGrantee(details: GranteeEmergencyAccess) {
diff --git a/apps/web/src/app/auth/settings/emergency-access/takeover/emergency-access-takeover-dialog.component.ts b/apps/web/src/app/auth/settings/emergency-access/takeover/emergency-access-takeover-dialog.component.ts
index 3ad9ce6b1fb..e5c21fb82b9 100644
--- a/apps/web/src/app/auth/settings/emergency-access/takeover/emergency-access-takeover-dialog.component.ts
+++ b/apps/web/src/app/auth/settings/emergency-access/takeover/emergency-access-takeover-dialog.component.ts
@@ -49,7 +49,6 @@ export type EmergencyAccessTakeoverDialogResultType =
* @link https://bitwarden.com/help/emergency-access/
*/
@Component({
- standalone: true,
selector: "auth-emergency-access-takeover-dialog",
templateUrl: "./emergency-access-takeover-dialog.component.html",
imports: [
@@ -116,10 +115,12 @@ export class EmergencyAccessTakeoverDialogComponent implements OnInit {
this.parentSubmittingBehaviorSubject.next(true);
try {
+ const activeUserId = await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId));
await this.emergencyAccessService.takeover(
this.dialogData.emergencyAccessId,
passwordInputResult.newPassword,
this.dialogData.grantorEmail,
+ activeUserId,
);
} catch (e) {
this.logService.error(e);
diff --git a/apps/web/src/app/auth/settings/emergency-access/takeover/emergency-access-takeover.component.html b/apps/web/src/app/auth/settings/emergency-access/takeover/emergency-access-takeover.component.html
deleted file mode 100644
index 64b35344455..00000000000
--- a/apps/web/src/app/auth/settings/emergency-access/takeover/emergency-access-takeover.component.html
+++ /dev/null
@@ -1,54 +0,0 @@
-
-
-
- {{ "takeover" | i18n }}
- {{ params.name }}
-
-
-
{{ "loggedOutWarning" | i18n }}
-
-
-
-
-
- {{ "newMasterPass" | i18n }}
-
-
-
-
-
-
-
-
- {{ "confirmNewMasterPass" | i18n }}
-
-
-
-
-
-
-
-
- {{ "save" | i18n }}
-
-
- {{ "cancel" | i18n }}
-
-
-
-
diff --git a/apps/web/src/app/auth/settings/emergency-access/takeover/emergency-access-takeover.component.ts b/apps/web/src/app/auth/settings/emergency-access/takeover/emergency-access-takeover.component.ts
deleted file mode 100644
index ede60887725..00000000000
--- a/apps/web/src/app/auth/settings/emergency-access/takeover/emergency-access-takeover.component.ts
+++ /dev/null
@@ -1,145 +0,0 @@
-// FIXME: Update this file to be type safe and remove this and next line
-// @ts-strict-ignore
-import { Component, OnDestroy, OnInit, Inject, Input } from "@angular/core";
-import { FormBuilder, Validators } from "@angular/forms";
-import { switchMap, takeUntil } from "rxjs";
-
-import { ChangePasswordComponent } from "@bitwarden/angular/auth/components/change-password.component";
-import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
-import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
-import { getUserId } from "@bitwarden/common/auth/services/account.service";
-import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/key-management/master-password/abstractions/master-password.service.abstraction";
-import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
-import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
-import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
-import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
-import {
- DialogConfig,
- DialogRef,
- DIALOG_DATA,
- DialogService,
- ToastService,
-} from "@bitwarden/components";
-import { KdfType, KdfConfigService, KeyService } from "@bitwarden/key-management";
-
-import { EmergencyAccessService } from "../../../emergency-access";
-
-// FIXME: update to use a const object instead of a typescript enum
-// eslint-disable-next-line @bitwarden/platform/no-enums
-export enum EmergencyAccessTakeoverResultType {
- Done = "done",
-}
-type EmergencyAccessTakeoverDialogData = {
- /** display name of the account requesting emergency access takeover */
- name: string;
- /** email of the account requesting emergency access takeover */
- email: string;
- /** traces a unique emergency request */
- emergencyAccessId: string;
-};
-@Component({
- selector: "emergency-access-takeover",
- templateUrl: "emergency-access-takeover.component.html",
- standalone: false,
-})
-export class EmergencyAccessTakeoverComponent
- extends ChangePasswordComponent
- implements OnInit, OnDestroy
-{
- @Input() kdf: KdfType;
- @Input() kdfIterations: number;
- takeoverForm = this.formBuilder.group({
- masterPassword: ["", [Validators.required]],
- masterPasswordRetype: ["", [Validators.required]],
- });
-
- constructor(
- @Inject(DIALOG_DATA) protected params: EmergencyAccessTakeoverDialogData,
- private formBuilder: FormBuilder,
- i18nService: I18nService,
- keyService: KeyService,
- messagingService: MessagingService,
- platformUtilsService: PlatformUtilsService,
- policyService: PolicyService,
- private emergencyAccessService: EmergencyAccessService,
- private logService: LogService,
- dialogService: DialogService,
- private dialogRef: DialogRef,
- kdfConfigService: KdfConfigService,
- masterPasswordService: InternalMasterPasswordServiceAbstraction,
- accountService: AccountService,
- protected toastService: ToastService,
- ) {
- super(
- accountService,
- dialogService,
- i18nService,
- kdfConfigService,
- keyService,
- masterPasswordService,
- messagingService,
- platformUtilsService,
- policyService,
- toastService,
- );
- }
-
- async ngOnInit() {
- const policies = await this.emergencyAccessService.getGrantorPolicies(
- this.params.emergencyAccessId,
- );
- this.accountService.activeAccount$
- .pipe(
- getUserId,
- switchMap((userId) => this.policyService.masterPasswordPolicyOptions$(userId, policies)),
- takeUntil(this.destroy$),
- )
- .subscribe((enforcedPolicyOptions) => (this.enforcedPolicyOptions = enforcedPolicyOptions));
- }
-
- ngOnDestroy(): void {
- super.ngOnDestroy();
- }
-
- submit = async () => {
- if (this.takeoverForm.invalid) {
- this.takeoverForm.markAllAsTouched();
- return;
- }
- this.masterPassword = this.takeoverForm.get("masterPassword").value;
- this.masterPasswordRetype = this.takeoverForm.get("masterPasswordRetype").value;
- if (!(await this.strongPassword())) {
- return;
- }
-
- try {
- await this.emergencyAccessService.takeover(
- this.params.emergencyAccessId,
- this.masterPassword,
- this.params.email,
- );
- } catch (e) {
- this.logService.error(e);
- this.toastService.showToast({
- variant: "error",
- title: this.i18nService.t("errorOccurred"),
- message: this.i18nService.t("unexpectedError"),
- });
- }
- this.dialogRef.close(EmergencyAccessTakeoverResultType.Done);
- };
- /**
- * Strongly typed helper to open a EmergencyAccessTakeoverComponent
- * @param dialogService Instance of the dialog service that will be used to open the dialog
- * @param config Configuration for the dialog
- */
- static open = (
- dialogService: DialogService,
- config: DialogConfig,
- ) => {
- return dialogService.open(
- EmergencyAccessTakeoverComponent,
- config,
- );
- };
-}
diff --git a/apps/web/src/app/auth/settings/emergency-access/view/emergency-access-view.component.ts b/apps/web/src/app/auth/settings/emergency-access/view/emergency-access-view.component.ts
index 607e6e6a2c7..250261fb0e7 100644
--- a/apps/web/src/app/auth/settings/emergency-access/view/emergency-access-view.component.ts
+++ b/apps/web/src/app/auth/settings/emergency-access/view/emergency-access-view.component.ts
@@ -2,20 +2,22 @@ import { Component, OnInit } from "@angular/core";
import { ActivatedRoute, Router } from "@angular/router";
import { firstValueFrom } from "rxjs";
+import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
+import { getUserId } from "@bitwarden/common/auth/services/account.service";
import { EmergencyAccessId } from "@bitwarden/common/types/guid";
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
import { DialogService } from "@bitwarden/components";
import { CipherFormConfigService, DefaultCipherFormConfigService } from "@bitwarden/vault";
+import { SharedModule } from "../../../../shared/shared.module";
import { EmergencyAccessService } from "../../../emergency-access";
import { EmergencyViewDialogComponent } from "./emergency-view-dialog.component";
@Component({
- selector: "emergency-access-view",
templateUrl: "emergency-access-view.component.html",
providers: [{ provide: CipherFormConfigService, useClass: DefaultCipherFormConfigService }],
- standalone: false,
+ imports: [SharedModule],
})
export class EmergencyAccessViewComponent implements OnInit {
id: EmergencyAccessId | null = null;
@@ -27,6 +29,7 @@ export class EmergencyAccessViewComponent implements OnInit {
private route: ActivatedRoute,
private emergencyAccessService: EmergencyAccessService,
private dialogService: DialogService,
+ private accountService: AccountService,
) {}
async ngOnInit() {
@@ -37,7 +40,8 @@ export class EmergencyAccessViewComponent implements OnInit {
}
this.id = qParams.id;
- this.ciphers = await this.emergencyAccessService.getViewOnlyCiphers(qParams.id);
+ const userId = await firstValueFrom(getUserId(this.accountService.activeAccount$));
+ this.ciphers = await this.emergencyAccessService.getViewOnlyCiphers(qParams.id, userId);
this.loaded = true;
}
diff --git a/apps/web/src/app/auth/settings/security/change-kdf/change-kdf-confirmation.component.ts b/apps/web/src/app/auth/settings/security/change-kdf/change-kdf-confirmation.component.ts
index 0bfc46eea96..5aa8eeb907c 100644
--- a/apps/web/src/app/auth/settings/security/change-kdf/change-kdf-confirmation.component.ts
+++ b/apps/web/src/app/auth/settings/security/change-kdf/change-kdf-confirmation.component.ts
@@ -2,14 +2,13 @@
// @ts-strict-ignore
import { Component, Inject } from "@angular/core";
import { FormGroup, FormControl, Validators } from "@angular/forms";
-import { firstValueFrom, map } from "rxjs";
+import { firstValueFrom } from "rxjs";
import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { KdfRequest } from "@bitwarden/common/models/request/kdf.request";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
-import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { DIALOG_DATA, ToastService } from "@bitwarden/components";
import { KdfConfig, KdfType, KeyService } from "@bitwarden/key-management";
@@ -31,7 +30,6 @@ export class ChangeKdfConfirmationComponent {
constructor(
private apiService: ApiService,
private i18nService: I18nService,
- private platformUtilsService: PlatformUtilsService,
private keyService: KeyService,
private messagingService: MessagingService,
@Inject(DIALOG_DATA) params: { kdf: KdfType; kdfConfig: KdfConfig },
@@ -58,6 +56,10 @@ export class ChangeKdfConfirmationComponent {
};
private async makeKeyAndSaveAsync() {
+ const activeAccount = await firstValueFrom(this.accountService.activeAccount$);
+ if (activeAccount == null) {
+ throw new Error("No active account found.");
+ }
const masterPassword = this.form.value.masterPassword;
// Ensure the KDF config is valid.
@@ -70,13 +72,14 @@ export class ChangeKdfConfirmationComponent {
request.kdfMemory = this.kdfConfig.memory;
request.kdfParallelism = this.kdfConfig.parallelism;
}
- const masterKey = await this.keyService.getOrDeriveMasterKey(masterPassword);
+ const masterKey = await this.keyService.getOrDeriveMasterKey(masterPassword, activeAccount.id);
request.masterPasswordHash = await this.keyService.hashMasterKey(masterPassword, masterKey);
- const email = await firstValueFrom(
- this.accountService.activeAccount$.pipe(map((a) => a?.email)),
- );
- const newMasterKey = await this.keyService.makeMasterKey(masterPassword, email, this.kdfConfig);
+ const newMasterKey = await this.keyService.makeMasterKey(
+ masterPassword,
+ activeAccount.email,
+ this.kdfConfig,
+ );
request.newMasterPasswordHash = await this.keyService.hashMasterKey(
masterPassword,
newMasterKey,
diff --git a/apps/web/src/app/auth/settings/security/device-management-old.component.html b/apps/web/src/app/auth/settings/security/device-management-old.component.html
index 587703c7389..da01d0fe8f4 100644
--- a/apps/web/src/app/auth/settings/security/device-management-old.component.html
+++ b/apps/web/src/app/auth/settings/security/device-management-old.component.html
@@ -11,7 +11,7 @@
- {{ "aDeviceIs" | i18n }}
+ {{ "aDeviceIs" | i18n }}
-
-
-
{{ "updateMasterPassword" | i18n }}
-
-
-
{{ "masterPasswordInvalidWarning" | i18n }}
-
-
-
-
-
-
- {{ "currentMasterPass" | i18n }}
-
-
-
-
-
-
-
-
- {{ "confirmNewMasterPass" | i18n }}
-
-
-
-
-
-
- {{ "changeMasterPassword" | i18n }}
-
-
- {{ "cancel" | i18n }}
-
-
-
-
-
-
-
diff --git a/apps/web/src/app/auth/update-password.component.ts b/apps/web/src/app/auth/update-password.component.ts
deleted file mode 100644
index bc53f824228..00000000000
--- a/apps/web/src/app/auth/update-password.component.ts
+++ /dev/null
@@ -1,24 +0,0 @@
-import { Component, inject } from "@angular/core";
-
-import { UpdatePasswordComponent as BaseUpdatePasswordComponent } from "@bitwarden/angular/auth/components/update-password.component";
-import { OrganizationInviteService } from "@bitwarden/common/auth/services/organization-invite/organization-invite.service";
-
-import { RouterService } from "../core";
-
-@Component({
- selector: "app-update-password",
- templateUrl: "update-password.component.html",
- standalone: false,
-})
-export class UpdatePasswordComponent extends BaseUpdatePasswordComponent {
- private routerService = inject(RouterService);
- private organizationInviteService = inject(OrganizationInviteService);
-
- override async cancel() {
- // clearing the login redirect url so that the user
- // does not join the organization if they cancel
- await this.routerService.getAndClearLoginRedirectUrl();
- await this.organizationInviteService.clearOrganizationInvitation();
- await super.cancel();
- }
-}
diff --git a/apps/web/src/app/auth/update-temp-password.component.html b/apps/web/src/app/auth/update-temp-password.component.html
deleted file mode 100644
index 4fd0ea72b5f..00000000000
--- a/apps/web/src/app/auth/update-temp-password.component.html
+++ /dev/null
@@ -1,96 +0,0 @@
-
-
-
-
{{ "updateMasterPassword" | i18n }}
-
-
{{ masterPasswordWarningText }}
-
-
-
- {{ "currentMasterPass" | i18n }}
-
-
-
-
-
- {{ "newMasterPass" | i18n }}
-
-
-
-
-
-
-
- {{ "confirmNewMasterPass" | i18n }}
-
-
-
-
- {{ "masterPassHint" | i18n }}
-
- {{ "masterPassHintDesc" | i18n }}
-
-
-
-
- {{ "submit" | i18n }}
-
-
- {{ "logOut" | i18n }}
-
-
-
-
-
-
diff --git a/apps/web/src/app/auth/update-temp-password.component.ts b/apps/web/src/app/auth/update-temp-password.component.ts
deleted file mode 100644
index ead10660b92..00000000000
--- a/apps/web/src/app/auth/update-temp-password.component.ts
+++ /dev/null
@@ -1,10 +0,0 @@
-import { Component } from "@angular/core";
-
-import { UpdateTempPasswordComponent as BaseUpdateTempPasswordComponent } from "@bitwarden/angular/auth/components/update-temp-password.component";
-
-@Component({
- selector: "app-update-temp-password",
- templateUrl: "update-temp-password.component.html",
- standalone: false,
-})
-export class UpdateTempPasswordComponent extends BaseUpdateTempPasswordComponent {}
diff --git a/apps/web/src/app/billing/accounts/trial-initiation/trial-billing-step.component.ts b/apps/web/src/app/billing/accounts/trial-initiation/trial-billing-step.component.ts
index fda7faeeb25..e13fac41f75 100644
--- a/apps/web/src/app/billing/accounts/trial-initiation/trial-billing-step.component.ts
+++ b/apps/web/src/app/billing/accounts/trial-initiation/trial-billing-step.component.ts
@@ -304,6 +304,7 @@ export class TrialBillingStepComponent implements OnInit, OnDestroy {
this.fetchingTaxAmount = true;
if (!this.taxInfoComponent.validate()) {
+ this.fetchingTaxAmount = false;
return 0;
}
@@ -326,7 +327,7 @@ export class TrialBillingStepComponent implements OnInit, OnDestroy {
const response = await this.taxService.previewTaxAmountForOrganizationTrial(request);
this.fetchingTaxAmount = false;
- return response.taxAmount;
+ return response;
};
get price() {
diff --git a/apps/web/src/app/billing/organizations/change-plan-dialog.component.html b/apps/web/src/app/billing/organizations/change-plan-dialog.component.html
index 91e2e83a92c..ace3d749a3f 100644
--- a/apps/web/src/app/billing/organizations/change-plan-dialog.component.html
+++ b/apps/web/src/app/billing/organizations/change-plan-dialog.component.html
@@ -11,24 +11,6 @@
}}
-
0
- ? discountPercentageFromSub
- : this.discountPercentage && selectedInterval === planIntervals.Annually
- "
- bitBadge
- variant="success"
- >{{
- "upgradeDiscount"
- | i18n
- : (selectedInterval === planIntervals.Annually && discountPercentageFromSub == 0
- ? this.discountPercentage
- : this.discountPercentageFromSub)
- }}
diff --git a/apps/web/src/app/billing/organizations/change-plan-dialog.component.ts b/apps/web/src/app/billing/organizations/change-plan-dialog.component.ts
index dc8474d24d6..129d8e1de48 100644
--- a/apps/web/src/app/billing/organizations/change-plan-dialog.component.ts
+++ b/apps/web/src/app/billing/organizations/change-plan-dialog.component.ts
@@ -54,6 +54,7 @@ import { PlanResponse } from "@bitwarden/common/billing/models/response/plan.res
import { ListResponse } from "@bitwarden/common/models/response/list.response";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
+import { OrganizationId } from "@bitwarden/common/types/guid";
import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction";
import {
DIALOG_DATA,
@@ -149,7 +150,6 @@ export class ChangePlanDialogComponent implements OnInit, OnDestroy {
@Output() onCanceled = new EventEmitter();
@Output() onTrialBillingSuccess = new EventEmitter();
- protected discountPercentage: number = 20;
protected discountPercentageFromSub: number;
protected loading = true;
protected planCards: PlanCard[];
@@ -892,7 +892,14 @@ export class ChangePlanDialogComponent implements OnInit, OnDestroy {
// Backfill pub/priv key if necessary
if (!this.organization.hasPublicAndPrivateKeys) {
- const orgShareKey = await this.keyService.getOrgKey(this.organizationId);
+ const userId = await firstValueFrom(
+ this.accountService.activeAccount$.pipe(map((a) => a?.id)),
+ );
+ const orgShareKey = await firstValueFrom(
+ this.keyService
+ .orgKeys$(userId)
+ .pipe(map((orgKeys) => orgKeys?.[this.organizationId as OrganizationId] ?? null)),
+ );
const orgKeys = await this.keyService.makeKeyPair(orgShareKey);
request.keys = new OrganizationKeysRequest(orgKeys[0], orgKeys[1].encryptedString);
}
diff --git a/apps/web/src/app/billing/organizations/organization-plans.component.ts b/apps/web/src/app/billing/organizations/organization-plans.component.ts
index 570e243933e..0d2c3a1d03f 100644
--- a/apps/web/src/app/billing/organizations/organization-plans.component.ts
+++ b/apps/web/src/app/billing/organizations/organization-plans.component.ts
@@ -54,6 +54,7 @@ import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.servic
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key";
+import { OrganizationId } from "@bitwarden/common/types/guid";
import { OrgKey } from "@bitwarden/common/types/key";
import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction";
import { ToastService } from "@bitwarden/components";
@@ -756,7 +757,14 @@ export class OrganizationPlansComponent implements OnInit, OnDestroy {
// Backfill pub/priv key if necessary
if (!this.organization.hasPublicAndPrivateKeys) {
- const orgShareKey = await this.keyService.getOrgKey(this.organizationId);
+ const userId = await firstValueFrom(
+ this.accountService.activeAccount$.pipe(map((a) => a?.id)),
+ );
+ const orgShareKey = await firstValueFrom(
+ this.keyService
+ .orgKeys$(userId)
+ .pipe(map((orgKeys) => orgKeys?.[this.organizationId as OrganizationId] ?? null)),
+ );
const orgKeys = await this.keyService.makeKeyPair(orgShareKey);
request.keys = new OrganizationKeysRequest(orgKeys[0], orgKeys[1].encryptedString);
}
diff --git a/apps/web/src/app/billing/payment/components/change-payment-method-dialog.component.ts b/apps/web/src/app/billing/payment/components/change-payment-method-dialog.component.ts
index ff5156ba636..15c63d8f99f 100644
--- a/apps/web/src/app/billing/payment/components/change-payment-method-dialog.component.ts
+++ b/apps/web/src/app/billing/payment/components/change-payment-method-dialog.component.ts
@@ -1,5 +1,5 @@
import { DIALOG_DATA } from "@angular/cdk/dialog";
-import { Component, Inject, ViewChild } from "@angular/core";
+import { Component, Inject } from "@angular/core";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { DialogConfig, DialogRef, DialogService, ToastService } from "@bitwarden/components";
@@ -7,19 +7,17 @@ import { DialogConfig, DialogRef, DialogService, ToastService } from "@bitwarden
import { SharedModule } from "../../../shared";
import { BillingClient } from "../../services";
import { BillableEntity } from "../../types";
-import { MaskedPaymentMethod } from "../types";
import { EnterPaymentMethodComponent } from "./enter-payment-method.component";
+import {
+ SubmitPaymentMethodDialogComponent,
+ SubmitPaymentMethodDialogResult,
+} from "./submit-payment-method-dialog.component";
type DialogParams = {
owner: BillableEntity;
};
-type DialogResult =
- | { type: "cancelled" }
- | { type: "error" }
- | { type: "success"; paymentMethod: MaskedPaymentMethod };
-
@Component({
template: `
@@ -55,63 +53,23 @@ type DialogResult =
imports: [EnterPaymentMethodComponent, SharedModule],
providers: [BillingClient],
})
-export class ChangePaymentMethodDialogComponent {
- @ViewChild(EnterPaymentMethodComponent)
- private enterPaymentMethodComponent!: EnterPaymentMethodComponent;
- protected formGroup = EnterPaymentMethodComponent.getFormGroup();
+export class ChangePaymentMethodDialogComponent extends SubmitPaymentMethodDialogComponent {
+ protected override owner: BillableEntity;
constructor(
- private billingClient: BillingClient,
+ billingClient: BillingClient,
@Inject(DIALOG_DATA) protected dialogParams: DialogParams,
- private dialogRef: DialogRef,
- private i18nService: I18nService,
- private toastService: ToastService,
- ) {}
-
- submit = async () => {
- this.formGroup.markAllAsTouched();
-
- if (!this.formGroup.valid) {
- return;
- }
-
- const paymentMethod = await this.enterPaymentMethodComponent.tokenize();
- const billingAddress =
- this.formGroup.value.type !== "payPal"
- ? this.formGroup.controls.billingAddress.getRawValue()
- : null;
-
- const result = await this.billingClient.updatePaymentMethod(
- this.dialogParams.owner,
- paymentMethod,
- billingAddress,
- );
-
- switch (result.type) {
- case "success": {
- this.toastService.showToast({
- variant: "success",
- title: "",
- message: this.i18nService.t("paymentMethodUpdated"),
- });
- this.dialogRef.close({
- type: "success",
- paymentMethod: result.value,
- });
- break;
- }
- case "error": {
- this.toastService.showToast({
- variant: "error",
- title: "",
- message: result.message,
- });
- this.dialogRef.close({ type: "error" });
- break;
- }
- }
- };
+ dialogRef: DialogRef,
+ i18nService: I18nService,
+ toastService: ToastService,
+ ) {
+ super(billingClient, dialogRef, i18nService, toastService);
+ this.owner = this.dialogParams.owner;
+ }
static open = (dialogService: DialogService, dialogConfig: DialogConfig) =>
- dialogService.open(ChangePaymentMethodDialogComponent, dialogConfig);
+ dialogService.open(
+ ChangePaymentMethodDialogComponent,
+ dialogConfig,
+ );
}
diff --git a/apps/web/src/app/billing/payment/components/enter-payment-method.component.ts b/apps/web/src/app/billing/payment/components/enter-payment-method.component.ts
index b73c3297e9e..b5d732031c0 100644
--- a/apps/web/src/app/billing/payment/components/enter-payment-method.component.ts
+++ b/apps/web/src/app/billing/payment/components/enter-payment-method.component.ts
@@ -108,7 +108,7 @@ type PaymentMethodFormGroup = FormGroup<{
- {{ "cardSecurityCodeDescription" | i18n }}
+ {{ "cardSecurityCodeDescription" | i18n }}
diff --git a/apps/web/src/app/billing/payment/components/index.ts b/apps/web/src/app/billing/payment/components/index.ts
index 3bf7f5ecd36..7e500d2119e 100644
--- a/apps/web/src/app/billing/payment/components/index.ts
+++ b/apps/web/src/app/billing/payment/components/index.ts
@@ -6,4 +6,6 @@ export * from "./display-payment-method.component";
export * from "./edit-billing-address-dialog.component";
export * from "./enter-billing-address.component";
export * from "./enter-payment-method.component";
+export * from "./require-payment-method-dialog.component";
+export * from "./submit-payment-method-dialog.component";
export * from "./verify-bank-account.component";
diff --git a/apps/web/src/app/billing/payment/components/require-payment-method-dialog.component.ts b/apps/web/src/app/billing/payment/components/require-payment-method-dialog.component.ts
new file mode 100644
index 00000000000..72585badca0
--- /dev/null
+++ b/apps/web/src/app/billing/payment/components/require-payment-method-dialog.component.ts
@@ -0,0 +1,77 @@
+import { DIALOG_DATA } from "@angular/cdk/dialog";
+import { Component, Inject } from "@angular/core";
+
+import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
+import {
+ CalloutTypes,
+ DialogConfig,
+ DialogRef,
+ DialogService,
+ ToastService,
+} from "@bitwarden/components";
+
+import { SharedModule } from "../../../shared";
+import { BillingClient } from "../../services";
+import { BillableEntity } from "../../types";
+
+import { EnterPaymentMethodComponent } from "./enter-payment-method.component";
+import {
+ SubmitPaymentMethodDialogComponent,
+ SubmitPaymentMethodDialogResult,
+} from "./submit-payment-method-dialog.component";
+
+type DialogParams = {
+ owner: BillableEntity;
+ callout: {
+ type: CalloutTypes;
+ title: string;
+ message: string;
+ };
+};
+
+@Component({
+ template: `
+
+
+
+ {{ "addPaymentMethod" | i18n }}
+
+
+
+ {{ dialogParams.callout.message }}
+
+
+
+
+
+
+ {{ "save" | i18n }}
+
+
+
+
+ `,
+ standalone: true,
+ imports: [EnterPaymentMethodComponent, SharedModule],
+ providers: [BillingClient],
+})
+export class RequirePaymentMethodDialogComponent extends SubmitPaymentMethodDialogComponent {
+ protected override owner: BillableEntity;
+
+ constructor(
+ billingClient: BillingClient,
+ @Inject(DIALOG_DATA) protected dialogParams: DialogParams,
+ dialogRef: DialogRef,
+ i18nService: I18nService,
+ toastService: ToastService,
+ ) {
+ super(billingClient, dialogRef, i18nService, toastService);
+ this.owner = this.dialogParams.owner;
+ }
+
+ static open = (dialogService: DialogService, dialogConfig: DialogConfig) =>
+ dialogService.open(RequirePaymentMethodDialogComponent, {
+ ...dialogConfig,
+ disableClose: true,
+ });
+}
diff --git a/apps/web/src/app/billing/payment/components/submit-payment-method-dialog.component.ts b/apps/web/src/app/billing/payment/components/submit-payment-method-dialog.component.ts
new file mode 100644
index 00000000000..0a0a5bf26d9
--- /dev/null
+++ b/apps/web/src/app/billing/payment/components/submit-payment-method-dialog.component.ts
@@ -0,0 +1,75 @@
+import { Component, ViewChild } from "@angular/core";
+
+import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
+import { DialogRef, ToastService } from "@bitwarden/components";
+
+import { BillingClient } from "../../services";
+import { BillableEntity } from "../../types";
+import { MaskedPaymentMethod } from "../types";
+
+import { EnterPaymentMethodComponent } from "./enter-payment-method.component";
+
+export type SubmitPaymentMethodDialogResult =
+ | { type: "cancelled" }
+ | { type: "error" }
+ | { type: "success"; paymentMethod: MaskedPaymentMethod };
+
+@Component({ template: "" })
+export abstract class SubmitPaymentMethodDialogComponent {
+ @ViewChild(EnterPaymentMethodComponent)
+ private enterPaymentMethodComponent!: EnterPaymentMethodComponent;
+ protected formGroup = EnterPaymentMethodComponent.getFormGroup();
+
+ protected abstract owner: BillableEntity;
+
+ protected constructor(
+ protected billingClient: BillingClient,
+ protected dialogRef: DialogRef,
+ protected i18nService: I18nService,
+ protected toastService: ToastService,
+ ) {}
+
+ submit = async () => {
+ this.formGroup.markAllAsTouched();
+
+ if (!this.formGroup.valid) {
+ return;
+ }
+
+ const paymentMethod = await this.enterPaymentMethodComponent.tokenize();
+ const billingAddress =
+ this.formGroup.value.type !== "payPal"
+ ? this.formGroup.controls.billingAddress.getRawValue()
+ : null;
+
+ const result = await this.billingClient.updatePaymentMethod(
+ this.owner,
+ paymentMethod,
+ billingAddress,
+ );
+
+ switch (result.type) {
+ case "success": {
+ this.toastService.showToast({
+ variant: "success",
+ title: "",
+ message: this.i18nService.t("paymentMethodUpdated"),
+ });
+ this.dialogRef.close({
+ type: "success",
+ paymentMethod: result.value,
+ });
+ break;
+ }
+ case "error": {
+ this.toastService.showToast({
+ variant: "error",
+ title: "",
+ message: result.message,
+ });
+ this.dialogRef.close({ type: "error" });
+ break;
+ }
+ }
+ };
+}
diff --git a/apps/web/src/app/billing/shared/plan-card/plan-card.component.html b/apps/web/src/app/billing/shared/plan-card/plan-card.component.html
index 08fd3b435f6..af228842720 100644
--- a/apps/web/src/app/billing/shared/plan-card/plan-card.component.html
+++ b/apps/web/src/app/billing/shared/plan-card/plan-card.component.html
@@ -31,10 +31,6 @@
class="tw-text-[1.25rem] tw-mt-[6px] tw-font-bold tw-mb-0 tw-leading-[2rem] tw-flex tw-items-center"
>
{{ plan().title }}
-
-
- {{ "upgradeDiscount" | i18n: plan().discount }}
{{ plan().costPerMember | currency: "$" }}
diff --git a/apps/web/src/app/billing/shared/plan-card/plan-card.component.ts b/apps/web/src/app/billing/shared/plan-card/plan-card.component.ts
index 9e3f03a5e7d..4150ddc25ba 100644
--- a/apps/web/src/app/billing/shared/plan-card/plan-card.component.ts
+++ b/apps/web/src/app/billing/shared/plan-card/plan-card.component.ts
@@ -5,7 +5,6 @@ import { ProductTierType } from "@bitwarden/common/billing/enums";
export interface PlanCard {
title: string;
costPerMember: number;
- discount?: number;
isDisabled: boolean;
isAnnual: boolean;
isSelected: boolean;
diff --git a/apps/web/src/app/billing/trial-initiation/complete-trial-initiation/complete-trial-initiation.component.html b/apps/web/src/app/billing/trial-initiation/complete-trial-initiation/complete-trial-initiation.component.html
index 98fe4032b55..e74997cb9f5 100644
--- a/apps/web/src/app/billing/trial-initiation/complete-trial-initiation/complete-trial-initiation.component.html
+++ b/apps/web/src/app/billing/trial-initiation/complete-trial-initiation/complete-trial-initiation.component.html
@@ -12,6 +12,7 @@
- // Web chooses to have Light as the default theme
- new DefaultThemeStateService(globalStateProvider, ThemeTypes.Light),
+ useClass: DefaultThemeStateService,
deps: [GlobalStateProvider],
}),
safeProvider({
@@ -277,21 +274,6 @@ const safeProviders: SafeProvider[] = [
useClass: WebLockComponentService,
deps: [],
}),
- safeProvider({
- provide: SetPasswordJitService,
- useClass: WebSetPasswordJitService,
- deps: [
- EncryptService,
- I18nServiceAbstraction,
- KdfConfigService,
- KeyServiceAbstraction,
- MasterPasswordApiService,
- InternalMasterPasswordServiceAbstraction,
- OrganizationApiServiceAbstraction,
- OrganizationUserApiService,
- InternalUserDecryptionOptionsServiceAbstraction,
- ],
- }),
safeProvider({
provide: SetInitialPasswordService,
useClass: WebSetInitialPasswordService,
@@ -412,6 +394,11 @@ const safeProviders: SafeProvider[] = [
useClass: DefaultDeviceManagementComponentService,
deps: [],
}),
+ safeProvider({
+ provide: OrganizationIntegrationApiService,
+ useClass: OrganizationIntegrationApiService,
+ deps: [ApiService],
+ }),
];
@NgModule({
diff --git a/apps/web/src/app/core/init.service.ts b/apps/web/src/app/core/init.service.ts
index 72951658b23..ecf10bfa723 100644
--- a/apps/web/src/app/core/init.service.ts
+++ b/apps/web/src/app/core/init.service.ts
@@ -7,10 +7,8 @@ import { WINDOW } from "@bitwarden/angular/services/injection-tokens";
import { EventUploadService as EventUploadServiceAbstraction } from "@bitwarden/common/abstractions/event/event-upload.service";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { TwoFactorService as TwoFactorServiceAbstraction } from "@bitwarden/common/auth/abstractions/two-factor.service";
-import { BulkEncryptService } from "@bitwarden/common/key-management/crypto/abstractions/bulk-encrypt.service";
import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service";
import { DefaultVaultTimeoutService } from "@bitwarden/common/key-management/vault-timeout";
-import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
import { I18nService as I18nServiceAbstraction } from "@bitwarden/common/platform/abstractions/i18n.service";
import { SdkLoadService } from "@bitwarden/common/platform/abstractions/sdk/sdk-load.service";
import { StateService as StateServiceAbstraction } from "@bitwarden/common/platform/abstractions/state.service";
@@ -42,8 +40,6 @@ export class InitService {
private versionService: VersionService,
private ipcService: IpcService,
private sdkLoadService: SdkLoadService,
- private configService: ConfigService,
- private bulkEncryptService: BulkEncryptService,
private taskService: TaskService,
@Inject(DOCUMENT) private document: Document,
) {}
@@ -53,13 +49,6 @@ export class InitService {
await this.sdkLoadService.loadAndInit();
await this.stateService.init();
- this.configService.serverConfig$.subscribe((newConfig) => {
- if (newConfig != null) {
- this.encryptService.onServerConfigChange(newConfig);
- this.bulkEncryptService.onServerConfigChange(newConfig);
- }
- });
-
const activeAccount = await firstValueFrom(this.accountService.activeAccount$);
if (activeAccount) {
// If there is an active account, we must await the process of setting the user key in memory
diff --git a/apps/web/src/app/oss-routing.module.ts b/apps/web/src/app/oss-routing.module.ts
index 1fb19757d60..4c1ed1a7472 100644
--- a/apps/web/src/app/oss-routing.module.ts
+++ b/apps/web/src/app/oss-routing.module.ts
@@ -12,14 +12,12 @@ import {
} from "@bitwarden/angular/auth/guards";
import { ChangePasswordComponent } from "@bitwarden/angular/auth/password-management/change-password";
import { SetInitialPasswordComponent } from "@bitwarden/angular/auth/password-management/set-initial-password/set-initial-password.component";
-import { canAccessFeature } from "@bitwarden/angular/platform/guard/feature-flag.guard";
import {
PasswordHintComponent,
RegistrationFinishComponent,
RegistrationStartComponent,
RegistrationStartSecondaryComponent,
RegistrationStartSecondaryComponentData,
- SetPasswordJitComponent,
RegistrationLinkExpiredComponent,
LoginComponent,
LoginSecondaryContentComponent,
@@ -39,7 +37,6 @@ import {
NewDeviceVerificationComponent,
DeviceVerificationIcon,
} from "@bitwarden/auth/angular";
-import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
import { AnonLayoutWrapperComponent, AnonLayoutWrapperData, Icons } from "@bitwarden/components";
import { LockComponent } from "@bitwarden/key-management-ui";
import { VaultIcons } from "@bitwarden/vault";
@@ -55,13 +52,10 @@ import { LoginViaWebAuthnComponent } from "./auth/login/login-via-webauthn/login
import { AcceptOrganizationComponent } from "./auth/organization-invite/accept-organization.component";
import { RecoverDeleteComponent } from "./auth/recover-delete.component";
import { RecoverTwoFactorComponent } from "./auth/recover-two-factor.component";
-import { SetPasswordComponent } from "./auth/set-password.component";
import { AccountComponent } from "./auth/settings/account/account.component";
import { EmergencyAccessComponent } from "./auth/settings/emergency-access/emergency-access.component";
import { EmergencyAccessViewComponent } from "./auth/settings/emergency-access/view/emergency-access-view.component";
import { SecurityRoutingModule } from "./auth/settings/security/security-routing.module";
-import { UpdatePasswordComponent } from "./auth/update-password.component";
-import { UpdateTempPasswordComponent } from "./auth/update-temp-password.component";
import { VerifyEmailTokenComponent } from "./auth/verify-email-token.component";
import { VerifyRecoverDeleteComponent } from "./auth/verify-recover-delete.component";
import { SponsoredFamiliesComponent } from "./billing/settings/sponsored-families.component";
@@ -115,11 +109,6 @@ const routes: Routes = [
component: LoginViaWebAuthnComponent,
data: { titleId: "logInWithPasskey" } satisfies RouteDataProperties,
},
- {
- path: "set-password",
- component: SetPasswordComponent,
- data: { titleId: "setMasterPassword" } satisfies RouteDataProperties,
- },
{ path: "verify-email", component: VerifyEmailTokenComponent },
{
path: "accept-organization",
@@ -143,34 +132,6 @@ const routes: Routes = [
canActivate: [unauthGuardFn()],
data: { titleId: "deleteOrganization" },
},
- {
- path: "update-temp-password",
- component: UpdateTempPasswordComponent,
- canActivate: [
- canAccessFeature(
- FeatureFlag.PM16117_ChangeExistingPasswordRefactor,
- false,
- "change-password",
- false,
- ),
- authGuard,
- ],
- data: { titleId: "updateTempPassword" } satisfies RouteDataProperties,
- },
- {
- path: "update-password",
- component: UpdatePasswordComponent,
- canActivate: [
- canAccessFeature(
- FeatureFlag.PM16117_ChangeExistingPasswordRefactor,
- false,
- "change-password",
- false,
- ),
- authGuard,
- ],
- data: { titleId: "updatePassword" } satisfies RouteDataProperties,
- },
],
},
{
@@ -329,24 +290,12 @@ const routes: Routes = [
},
{
path: "set-initial-password",
- canActivate: [canAccessFeature(FeatureFlag.PM16117_SetInitialPasswordRefactor), authGuard],
+ canActivate: [authGuard],
component: SetInitialPasswordComponent,
data: {
maxWidth: "lg",
} satisfies AnonLayoutWrapperData,
},
- {
- path: "set-password-jit",
- component: SetPasswordJitComponent,
- data: {
- pageTitle: {
- key: "joinOrganization",
- },
- pageSubtitle: {
- key: "finishJoiningThisOrganizationBySettingAMasterPassword",
- },
- } satisfies AnonLayoutWrapperData,
- },
{
path: "signup-link-expired",
canActivate: [unauthGuardFn()],
@@ -601,10 +550,7 @@ const routes: Routes = [
{
path: "change-password",
component: ChangePasswordComponent,
- canActivate: [
- canAccessFeature(FeatureFlag.PM16117_ChangeExistingPasswordRefactor),
- authGuard,
- ],
+ canActivate: [authGuard],
},
{
path: "setup-extension",
diff --git a/apps/web/src/app/secrets-manager/models/requests/request-sm-access.request.ts b/apps/web/src/app/secrets-manager/models/requests/request-sm-access.request.ts
index 5edd8bc046e..7292e13a6a5 100644
--- a/apps/web/src/app/secrets-manager/models/requests/request-sm-access.request.ts
+++ b/apps/web/src/app/secrets-manager/models/requests/request-sm-access.request.ts
@@ -1,8 +1,8 @@
// FIXME: Update this file to be type safe and remove this and next line
// @ts-strict-ignore
-import { Guid } from "@bitwarden/common/types/guid";
+import { OrganizationId } from "@bitwarden/common/types/guid";
export class RequestSMAccessRequest {
- OrganizationId: Guid;
+ OrganizationId: OrganizationId;
EmailContent: string;
}
diff --git a/apps/web/src/app/secrets-manager/secrets-manager-landing/request-sm-access.component.ts b/apps/web/src/app/secrets-manager/secrets-manager-landing/request-sm-access.component.ts
index 443b3e03e5f..0e32321a0b3 100644
--- a/apps/web/src/app/secrets-manager/secrets-manager-landing/request-sm-access.component.ts
+++ b/apps/web/src/app/secrets-manager/secrets-manager-landing/request-sm-access.component.ts
@@ -10,7 +10,6 @@ import { Organization } from "@bitwarden/common/admin-console/models/domain/orga
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { getUserId } from "@bitwarden/common/auth/services/account.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
-import { Guid } from "@bitwarden/common/types/guid";
import { NoItemsModule, SearchModule, ToastService } from "@bitwarden/components";
import { HeaderModule } from "../../layouts/header/header.module";
@@ -63,7 +62,7 @@ export class RequestSMAccessComponent implements OnInit {
const formValue = this.requestAccessForm.value;
const request = new RequestSMAccessRequest();
- request.OrganizationId = formValue.selectedOrganization.id as Guid;
+ request.OrganizationId = formValue.selectedOrganization.id;
request.EmailContent = formValue.requestAccessEmailContents;
await this.smLandingApiService.requestSMAccessFromAdmins(request);
diff --git a/apps/web/src/app/settings/domain-rules.component.html b/apps/web/src/app/settings/domain-rules.component.html
index 7474b63372d..8ebeecb429f 100644
--- a/apps/web/src/app/settings/domain-rules.component.html
+++ b/apps/web/src/app/settings/domain-rules.component.html
@@ -60,7 +60,6 @@
diff --git a/apps/web/src/app/shared/loose-components.module.ts b/apps/web/src/app/shared/loose-components.module.ts
index 97c3fa0375c..637e1b77ce0 100644
--- a/apps/web/src/app/shared/loose-components.module.ts
+++ b/apps/web/src/app/shared/loose-components.module.ts
@@ -14,16 +14,8 @@ import { VerifyRecoverDeleteOrgComponent } from "../admin-console/organizations/
import { AcceptFamilySponsorshipComponent } from "../admin-console/organizations/sponsorships/accept-family-sponsorship.component";
import { RecoverDeleteComponent } from "../auth/recover-delete.component";
import { RecoverTwoFactorComponent } from "../auth/recover-two-factor.component";
-import { SetPasswordComponent } from "../auth/set-password.component";
import { DangerZoneComponent } from "../auth/settings/account/danger-zone.component";
-import { EmergencyAccessConfirmComponent } from "../auth/settings/emergency-access/confirm/emergency-access-confirm.component";
-import { EmergencyAccessAddEditComponent } from "../auth/settings/emergency-access/emergency-access-add-edit.component";
-import { EmergencyAccessComponent } from "../auth/settings/emergency-access/emergency-access.component";
-import { EmergencyAccessTakeoverComponent } from "../auth/settings/emergency-access/takeover/emergency-access-takeover.component";
-import { EmergencyAccessViewComponent } from "../auth/settings/emergency-access/view/emergency-access-view.component";
import { UserVerificationModule } from "../auth/shared/components/user-verification";
-import { UpdatePasswordComponent } from "../auth/update-password.component";
-import { UpdateTempPasswordComponent } from "../auth/update-temp-password.component";
import { VerifyEmailTokenComponent } from "../auth/verify-email-token.component";
import { VerifyRecoverDeleteComponent } from "../auth/verify-recover-delete.component";
import { FreeBitwardenFamiliesComponent } from "../billing/members/free-bitwarden-families.component";
@@ -70,11 +62,6 @@ import { SharedModule } from "./shared.module";
],
declarations: [
AcceptFamilySponsorshipComponent,
- EmergencyAccessAddEditComponent,
- EmergencyAccessComponent,
- EmergencyAccessConfirmComponent,
- EmergencyAccessTakeoverComponent,
- EmergencyAccessViewComponent,
OrgEventsComponent,
OrgExposedPasswordsReportComponent,
OrgInactiveTwoFactorReportComponent,
@@ -85,23 +72,15 @@ import { SharedModule } from "./shared.module";
RecoverDeleteComponent,
RecoverTwoFactorComponent,
RemovePasswordComponent,
- SetPasswordComponent,
SponsoredFamiliesComponent,
FreeBitwardenFamiliesComponent,
SponsoringOrgRowComponent,
- UpdatePasswordComponent,
- UpdateTempPasswordComponent,
VerifyEmailTokenComponent,
VerifyRecoverDeleteComponent,
],
exports: [
UserVerificationModule,
PremiumBadgeComponent,
- EmergencyAccessAddEditComponent,
- EmergencyAccessComponent,
- EmergencyAccessConfirmComponent,
- EmergencyAccessTakeoverComponent,
- EmergencyAccessViewComponent,
OrganizationLayoutComponent,
OrgEventsComponent,
OrgExposedPasswordsReportComponent,
@@ -114,12 +93,9 @@ import { SharedModule } from "./shared.module";
RecoverDeleteComponent,
RecoverTwoFactorComponent,
RemovePasswordComponent,
- SetPasswordComponent,
SponsoredFamiliesComponent,
FreeBitwardenFamiliesComponent,
SponsoringOrgRowComponent,
- UpdateTempPasswordComponent,
- UpdatePasswordComponent,
VerifyEmailTokenComponent,
VerifyRecoverDeleteComponent,
HeaderModule,
diff --git a/apps/web/src/app/tools/send/send-access/send-access-file.component.ts b/apps/web/src/app/tools/send/send-access/send-access-file.component.ts
index 3b1bf427a0b..239861dd244 100644
--- a/apps/web/src/app/tools/send/send-access/send-access-file.component.ts
+++ b/apps/web/src/app/tools/send/send-access/send-access-file.component.ts
@@ -63,7 +63,7 @@ export class SendAccessFileComponent {
try {
const encBuf = await EncArrayBuffer.fromResponse(response);
- const decBuf = await this.encryptService.decryptToBytes(encBuf, this.decKey);
+ const decBuf = await this.encryptService.decryptFileData(encBuf, this.decKey);
this.fileDownloadService.download({
fileName: this.send.file.fileName,
blobData: decBuf,
diff --git a/apps/web/src/app/vault/components/browser-extension-prompt/browser-extension-prompt.component.html b/apps/web/src/app/vault/components/browser-extension-prompt/browser-extension-prompt.component.html
index 1c643fcc3e4..56332cc424b 100644
--- a/apps/web/src/app/vault/components/browser-extension-prompt/browser-extension-prompt.component.html
+++ b/apps/web/src/app/vault/components/browser-extension-prompt/browser-extension-prompt.component.html
@@ -26,14 +26,7 @@
-
- {{ "openExtensionManuallyPart1" | i18n }}
-
- {{ "openExtensionManuallyPart2" | i18n }}
-
+
diff --git a/apps/web/src/app/vault/components/browser-extension-prompt/browser-extension-prompt.component.ts b/apps/web/src/app/vault/components/browser-extension-prompt/browser-extension-prompt.component.ts
index 624275a8297..177311cbfde 100644
--- a/apps/web/src/app/vault/components/browser-extension-prompt/browser-extension-prompt.component.ts
+++ b/apps/web/src/app/vault/components/browser-extension-prompt/browser-extension-prompt.component.ts
@@ -3,17 +3,17 @@ import { Component, Inject, OnDestroy, OnInit } from "@angular/core";
import { ButtonComponent, IconModule } from "@bitwarden/components";
import { I18nPipe } from "@bitwarden/ui-common";
-import { VaultIcons } from "@bitwarden/vault";
import {
BrowserExtensionPromptService,
BrowserPromptState,
} from "../../services/browser-extension-prompt.service";
+import { ManuallyOpenExtensionComponent } from "../manually-open-extension/manually-open-extension.component";
@Component({
selector: "vault-browser-extension-prompt",
templateUrl: "./browser-extension-prompt.component.html",
- imports: [CommonModule, I18nPipe, ButtonComponent, IconModule],
+ imports: [CommonModule, I18nPipe, ButtonComponent, IconModule, ManuallyOpenExtensionComponent],
})
export class BrowserExtensionPromptComponent implements OnInit, OnDestroy {
/** Current state of the prompt page */
@@ -22,8 +22,6 @@ export class BrowserExtensionPromptComponent implements OnInit, OnDestroy {
/** All available page states */
protected BrowserPromptState = BrowserPromptState;
- protected BitwardenIcon = VaultIcons.BitwardenIcon;
-
/** Content of the meta[name="viewport"] element */
private viewportContent: string | null = null;
diff --git a/apps/web/src/app/vault/components/manually-open-extension/manually-open-extension.component.html b/apps/web/src/app/vault/components/manually-open-extension/manually-open-extension.component.html
new file mode 100644
index 00000000000..22c36e51177
--- /dev/null
+++ b/apps/web/src/app/vault/components/manually-open-extension/manually-open-extension.component.html
@@ -0,0 +1,8 @@
+
+ {{ "openExtensionManuallyPart1" | i18n }}
+
+ {{ "openExtensionManuallyPart2" | i18n }}
+
diff --git a/apps/web/src/app/vault/components/manually-open-extension/manually-open-extension.component.ts b/apps/web/src/app/vault/components/manually-open-extension/manually-open-extension.component.ts
new file mode 100644
index 00000000000..22041b61198
--- /dev/null
+++ b/apps/web/src/app/vault/components/manually-open-extension/manually-open-extension.component.ts
@@ -0,0 +1,14 @@
+import { Component } from "@angular/core";
+
+import { IconModule } from "@bitwarden/components";
+import { I18nPipe } from "@bitwarden/ui-common";
+import { VaultIcons } from "@bitwarden/vault";
+
+@Component({
+ selector: "vault-manually-open-extension",
+ templateUrl: "./manually-open-extension.component.html",
+ imports: [I18nPipe, IconModule],
+})
+export class ManuallyOpenExtensionComponent {
+ protected BitwardenIcon = VaultIcons.BitwardenIcon;
+}
diff --git a/apps/web/src/app/vault/components/setup-extension/add-extension-videos.component.ts b/apps/web/src/app/vault/components/setup-extension/add-extension-videos.component.ts
index d053e05c36b..6bde812065b 100644
--- a/apps/web/src/app/vault/components/setup-extension/add-extension-videos.component.ts
+++ b/apps/web/src/app/vault/components/setup-extension/add-extension-videos.component.ts
@@ -34,8 +34,8 @@ export class AddExtensionVideosComponent {
/** CSS classes for the video container, pulled into the class only for readability. */
protected videoContainerClass = [
"tw-absolute tw-left-0 tw-top-0 tw-w-[15rem] tw-opacity-0 md:tw-opacity-100 md:tw-relative lg:tw-w-[17rem] tw-max-w-full tw-aspect-[0.807]",
- `[${this.cssOverlayVariable}:0.7] after:tw-absolute after:tw-top-0 after:tw-left-0 after:tw-size-full after:tw-bg-primary-100 after:tw-content-[''] after:tw-rounded-lg after:tw-opacity-[--overlay-opacity]`,
- `[${this.cssBorderVariable}:0] before:tw-absolute before:tw-top-0 before:tw-left-0 before:tw-w-full before:tw-h-2 before:tw-bg-primary-600 before:tw-content-[''] before:tw-rounded-t-lg before:tw-opacity-[--border-opacity]`,
+ `[--overlay-opacity:0.7] after:tw-absolute after:tw-top-0 after:tw-left-0 after:tw-size-full after:tw-bg-primary-100 after:tw-content-[''] after:tw-rounded-lg after:tw-opacity-[--overlay-opacity]`,
+ `[--border-opacity:0] before:tw-absolute before:tw-top-0 before:tw-left-0 before:tw-w-full before:tw-h-2 before:tw-bg-primary-600 before:tw-content-[''] before:tw-rounded-t-lg before:tw-opacity-[--border-opacity]`,
"after:tw-transition-opacity after:tw-duration-400 after:tw-ease-linear",
"before:tw-transition-opacity before:tw-duration-400 before:tw-ease-linear",
].join(" ");
diff --git a/apps/web/src/app/vault/components/setup-extension/setup-extension.component.html b/apps/web/src/app/vault/components/setup-extension/setup-extension.component.html
index 3b9ec19fd34..ac24383a4d3 100644
--- a/apps/web/src/app/vault/components/setup-extension/setup-extension.component.html
+++ b/apps/web/src/app/vault/components/setup-extension/setup-extension.component.html
@@ -45,12 +45,16 @@
{{ "gettingStartedWithBitwardenPart1" | i18n }}
-
+
{{ "gettingStartedWithBitwardenPart2" | i18n }}
{{ "and" | i18n }}
-
+
{{ "gettingStartedWithBitwardenPart3" | i18n }}
+
+
diff --git a/apps/web/src/app/vault/components/setup-extension/setup-extension.component.spec.ts b/apps/web/src/app/vault/components/setup-extension/setup-extension.component.spec.ts
index e824cd92f37..8bb80e6fb44 100644
--- a/apps/web/src/app/vault/components/setup-extension/setup-extension.component.spec.ts
+++ b/apps/web/src/app/vault/components/setup-extension/setup-extension.component.spec.ts
@@ -1,4 +1,4 @@
-import { ComponentFixture, TestBed } from "@angular/core/testing";
+import { ComponentFixture, fakeAsync, TestBed, tick } from "@angular/core/testing";
import { By } from "@angular/platform-browser";
import { Router, RouterModule } from "@angular/router";
import { BehaviorSubject } from "rxjs";
@@ -11,10 +11,12 @@ import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.servic
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { Utils } from "@bitwarden/common/platform/misc/utils";
import { StateProvider } from "@bitwarden/common/platform/state";
+import { AnonLayoutWrapperDataService } from "@bitwarden/components";
+import { VaultIcons } from "@bitwarden/vault";
import { WebBrowserInteractionService } from "../../services/web-browser-interaction.service";
-import { SetupExtensionComponent } from "./setup-extension.component";
+import { SetupExtensionComponent, SetupExtensionState } from "./setup-extension.component";
describe("SetupExtensionComponent", () => {
let fixture: ComponentFixture;
@@ -24,12 +26,14 @@ describe("SetupExtensionComponent", () => {
const navigate = jest.fn().mockResolvedValue(true);
const openExtension = jest.fn().mockResolvedValue(true);
const update = jest.fn().mockResolvedValue(true);
+ const setAnonLayoutWrapperData = jest.fn();
const extensionInstalled$ = new BehaviorSubject(null);
beforeEach(async () => {
navigate.mockClear();
openExtension.mockClear();
update.mockClear();
+ setAnonLayoutWrapperData.mockClear();
getFeatureFlag.mockClear().mockResolvedValue(true);
window.matchMedia = jest.fn().mockReturnValue(false);
@@ -40,6 +44,7 @@ describe("SetupExtensionComponent", () => {
{ provide: ConfigService, useValue: { getFeatureFlag } },
{ provide: WebBrowserInteractionService, useValue: { extensionInstalled$, openExtension } },
{ provide: PlatformUtilsService, useValue: { getDevice: () => DeviceType.UnknownBrowser } },
+ { provide: AnonLayoutWrapperDataService, useValue: { setAnonLayoutWrapperData } },
{
provide: AccountService,
useValue: { activeAccount$: new BehaviorSubject({ account: { id: "account-id" } }) },
@@ -136,6 +141,27 @@ describe("SetupExtensionComponent", () => {
it("dismisses the extension page", () => {
expect(update).toHaveBeenCalledTimes(1);
});
+
+ it("shows error state when extension fails to open", fakeAsync(() => {
+ openExtension.mockRejectedValueOnce(new Error("Failed to open extension"));
+
+ const openExtensionButton = fixture.debugElement.query(By.css("button"));
+
+ openExtensionButton.triggerEventHandler("click");
+
+ tick();
+
+ expect(component["state"]).toBe(SetupExtensionState.ManualOpen);
+ expect(setAnonLayoutWrapperData).toHaveBeenCalledWith({
+ pageTitle: {
+ key: "somethingWentWrong",
+ },
+ pageIcon: VaultIcons.BrowserExtensionIcon,
+ hideIcon: false,
+ hideCardWrapper: false,
+ maxWidth: "md",
+ });
+ }));
});
});
});
diff --git a/apps/web/src/app/vault/components/setup-extension/setup-extension.component.ts b/apps/web/src/app/vault/components/setup-extension/setup-extension.component.ts
index 14770ca5d6c..67d13ef1e4f 100644
--- a/apps/web/src/app/vault/components/setup-extension/setup-extension.component.ts
+++ b/apps/web/src/app/vault/components/setup-extension/setup-extension.component.ts
@@ -15,6 +15,7 @@ import { StateProvider } from "@bitwarden/common/platform/state";
import { UnionOfValues } from "@bitwarden/common/vault/types/union-of-values";
import { getWebStoreUrl } from "@bitwarden/common/vault/utils/get-web-store-url";
import {
+ AnonLayoutWrapperDataService,
ButtonComponent,
DialogRef,
DialogService,
@@ -25,6 +26,7 @@ import { VaultIcons } from "@bitwarden/vault";
import { SETUP_EXTENSION_DISMISSED } from "../../guards/setup-extension-redirect.guard";
import { WebBrowserInteractionService } from "../../services/web-browser-interaction.service";
+import { ManuallyOpenExtensionComponent } from "../manually-open-extension/manually-open-extension.component";
import {
AddExtensionLaterDialogComponent,
@@ -32,10 +34,11 @@ import {
} from "./add-extension-later-dialog.component";
import { AddExtensionVideosComponent } from "./add-extension-videos.component";
-const SetupExtensionState = {
+export const SetupExtensionState = {
Loading: "loading",
NeedsExtension: "needs-extension",
Success: "success",
+ ManualOpen: "manual-open",
} as const;
type SetupExtensionState = UnionOfValues;
@@ -51,6 +54,7 @@ type SetupExtensionState = UnionOfValues;
IconModule,
RouterModule,
AddExtensionVideosComponent,
+ ManuallyOpenExtensionComponent,
],
})
export class SetupExtensionComponent implements OnInit, OnDestroy {
@@ -63,6 +67,7 @@ export class SetupExtensionComponent implements OnInit, OnDestroy {
private stateProvider = inject(StateProvider);
private accountService = inject(AccountService);
private document = inject(DOCUMENT);
+ private anonLayoutWrapperDataService = inject(AnonLayoutWrapperDataService);
protected SetupExtensionState = SetupExtensionState;
protected PartyIcon = VaultIcons.Party;
@@ -153,8 +158,21 @@ export class SetupExtensionComponent implements OnInit, OnDestroy {
}
/** Opens the browser extension */
- openExtension() {
- void this.webBrowserExtensionInteractionService.openExtension();
+ async openExtension() {
+ await this.webBrowserExtensionInteractionService.openExtension().catch(() => {
+ this.state = SetupExtensionState.ManualOpen;
+
+ // Update the anon layout data to show the proper error design
+ this.anonLayoutWrapperDataService.setAnonLayoutWrapperData({
+ pageTitle: {
+ key: "somethingWentWrong",
+ },
+ pageIcon: VaultIcons.BrowserExtensionIcon,
+ hideIcon: false,
+ hideCardWrapper: false,
+ maxWidth: "md",
+ });
+ });
}
/** Update local state to never show this page again. */
diff --git a/apps/web/src/app/vault/components/vault-items/vault-cipher-row.component.html b/apps/web/src/app/vault/components/vault-items/vault-cipher-row.component.html
index 20b87bfc036..006bf9ff197 100644
--- a/apps/web/src/app/vault/components/vault-items/vault-cipher-row.component.html
+++ b/apps/web/src/app/vault/components/vault-items/vault-cipher-row.component.html
@@ -93,73 +93,74 @@
-
-
-
-
-
- {{ "copyUsername" | i18n }}
-
-
-
- {{ "copyPassword" | i18n }}
-
-
-
- {{ "copyVerificationCode" | i18n }}
-
-
-
- {{ "launch" | i18n }}
-
-
-
-
-
- {{ "attachments" | i18n }}
-
-
-
- {{ "clone" | i18n }}
-
+ @if (!decryptionFailure && !hideMenu) {
-
- {{ "assignToCollections" | i18n }}
-
-
-
- {{ "eventLogs" | i18n }}
-
-
-
- {{ "restore" | i18n }}
-
-
-
-
- {{ (isDeleted ? "permanentlyDelete" : "delete") | i18n }}
-
-
-
+ appStopProp
+ appA11yTitle="{{ 'options' | i18n }}"
+ >
+
+
+
+
+ {{ "copyUsername" | i18n }}
+
+
+
+ {{ "copyPassword" | i18n }}
+
+
+
+ {{ "copyVerificationCode" | i18n }}
+
+
+
+ {{ "launch" | i18n }}
+
+
+
+
+
+ {{ "attachments" | i18n }}
+
+
+
+ {{ "clone" | i18n }}
+
+
+
+ {{ "assignToCollections" | i18n }}
+
+
+
+ {{ "eventLogs" | i18n }}
+
+
+
+ {{ "restore" | i18n }}
+
+
+
+
+ {{ (isDeleted ? "permanentlyDelete" : "delete") | i18n }}
+
+
+
+ }
diff --git a/apps/web/src/app/vault/components/vault-items/vault-cipher-row.component.ts b/apps/web/src/app/vault/components/vault-items/vault-cipher-row.component.ts
index cb4d8ad70b1..32037493e36 100644
--- a/apps/web/src/app/vault/components/vault-items/vault-cipher-row.component.ts
+++ b/apps/web/src/app/vault/components/vault-items/vault-cipher-row.component.ts
@@ -189,8 +189,14 @@ export class VaultCipherRowComponent implements OnInit
return this.i18nService.t("noAccess");
}
+ protected get showCopyUsername(): boolean {
+ const usernameCopy = CipherViewLikeUtils.hasCopyableValue(this.cipher, "username");
+ return this.isNotDeletedLoginCipher && usernameCopy;
+ }
+
protected get showCopyPassword(): boolean {
- return this.isNotDeletedLoginCipher && this.cipher.viewPassword;
+ const passwordCopy = CipherViewLikeUtils.hasCopyableValue(this.cipher, "password");
+ return this.isNotDeletedLoginCipher && this.cipher.viewPassword && passwordCopy;
}
protected get showCopyTotp(): boolean {
@@ -201,16 +207,20 @@ export class VaultCipherRowComponent implements OnInit
return this.isNotDeletedLoginCipher && this.canLaunch;
}
- protected get disableMenu() {
+ protected get isDeletedCanRestore(): boolean {
+ return CipherViewLikeUtils.isDeleted(this.cipher) && this.canRestoreCipher;
+ }
+
+ protected get hideMenu() {
return !(
- this.isNotDeletedLoginCipher ||
+ this.isDeletedCanRestore ||
+ this.showCopyUsername ||
this.showCopyPassword ||
this.showCopyTotp ||
this.showLaunchUri ||
this.showAttachments ||
this.showClone ||
- this.canEditCipher ||
- (CipherViewLikeUtils.isDeleted(this.cipher) && this.canRestoreCipher)
+ this.canEditCipher
);
}
diff --git a/apps/web/src/app/vault/components/vault-items/vault-items.component.ts b/apps/web/src/app/vault/components/vault-items/vault-items.component.ts
index ebee57878db..a8dd0056806 100644
--- a/apps/web/src/app/vault/components/vault-items/vault-items.component.ts
+++ b/apps/web/src/app/vault/components/vault-items/vault-items.component.ts
@@ -28,8 +28,8 @@ import { VaultItem } from "./vault-item";
import { VaultItemEvent } from "./vault-item-event";
// Fixed manual row height required due to how cdk-virtual-scroll works
-export const RowHeight = 75.5;
-export const RowHeightClass = `tw-h-[75.5px]`;
+export const RowHeight = 75;
+export const RowHeightClass = `tw-h-[75px]`;
const MaxSelectionCount = 500;
@@ -166,6 +166,10 @@ export class VaultItemsComponent {
);
}
+ clearSelection() {
+ this.selection.clear();
+ }
+
get showExtraColumn() {
return this.showCollections || this.showGroups || this.showOwner;
}
@@ -285,7 +289,7 @@ export class VaultItemsComponent {
protected canClone(vaultItem: VaultItem) {
// This will check for restrictions from org policies before allowing cloning.
const isItemRestricted = this.restrictedPolicies().some(
- (rt) => rt.cipherType === vaultItem.cipher.type,
+ (rt) => rt.cipherType === CipherViewLikeUtils.getType(vaultItem.cipher),
);
if (isItemRestricted) {
return false;
@@ -566,7 +570,7 @@ export class VaultItemsComponent {
}
private hasPersonalItems(): boolean {
- return this.selection.selected.some(({ cipher }) => cipher?.organizationId === null);
+ return this.selection.selected.some(({ cipher }) => !cipher?.organizationId);
}
private allCiphersHaveEditAccess(): boolean {
diff --git a/apps/web/src/app/vault/components/vault-items/vault-items.stories.ts b/apps/web/src/app/vault/components/vault-items/vault-items.stories.ts
index 78c4d21dede..c114cb6d7c2 100644
--- a/apps/web/src/app/vault/components/vault-items/vault-items.stories.ts
+++ b/apps/web/src/app/vault/components/vault-items/vault-items.stories.ts
@@ -29,6 +29,7 @@ import {
} from "@bitwarden/common/platform/abstractions/environment.service";
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key";
+import { CollectionId, OrganizationId } from "@bitwarden/common/types/guid";
import { CipherType } from "@bitwarden/common/vault/enums";
import { AttachmentView } from "@bitwarden/common/vault/models/view/attachment.view";
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
@@ -262,7 +263,7 @@ export const OrganizationTrash: Story = {
};
const unassignedCollection = new CollectionAdminView();
-unassignedCollection.id = Unassigned;
+unassignedCollection.id = Unassigned as CollectionId;
unassignedCollection.name = "Unassigned";
export const OrganizationTopLevelCollection: Story = {
args: {
@@ -327,7 +328,7 @@ function createCollectionView(i: number): CollectionAdminView {
const organization = organizations[i % (organizations.length + 1)];
const group = groups[i % (groups.length + 1)];
const view = new CollectionAdminView();
- view.id = `collection-${i}`;
+ view.id = `collection-${i}` as CollectionId;
view.name = `Collection ${i}`;
view.organizationId = organization?.id;
view.manage = true;
@@ -357,7 +358,7 @@ function createGroupView(i: number): GroupView {
function createOrganization(i: number): Organization {
const organization = new Organization();
- organization.id = `organization-${i}`;
+ organization.id = `organization-${i}` as OrganizationId;
organization.name = `Organization ${i}`;
organization.type = OrganizationUserType.Owner;
organization.permissions = new PermissionsApi();
diff --git a/apps/web/src/app/vault/individual-vault/bulk-action-dialogs/bulk-delete-dialog/bulk-delete-dialog.component.ts b/apps/web/src/app/vault/individual-vault/bulk-action-dialogs/bulk-delete-dialog/bulk-delete-dialog.component.ts
index 128afdcccfc..78abad1ebf8 100644
--- a/apps/web/src/app/vault/individual-vault/bulk-action-dialogs/bulk-delete-dialog/bulk-delete-dialog.component.ts
+++ b/apps/web/src/app/vault/individual-vault/bulk-action-dialogs/bulk-delete-dialog/bulk-delete-dialog.component.ts
@@ -9,7 +9,7 @@ import { Organization } from "@bitwarden/common/admin-console/models/domain/orga
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { getUserId } from "@bitwarden/common/auth/services/account.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
-import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
+import { CollectionId } from "@bitwarden/common/types/guid";
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
import { CipherBulkDeleteRequest } from "@bitwarden/common/vault/models/request/cipher-bulk-delete.request";
import { UnionOfValues } from "@bitwarden/common/vault/types/union-of-values";
@@ -68,7 +68,6 @@ export class BulkDeleteDialogComponent {
@Inject(DIALOG_DATA) params: BulkDeleteDialogParams,
private dialogRef: DialogRef,
private cipherService: CipherService,
- private platformUtilsService: PlatformUtilsService,
private i18nService: I18nService,
private apiService: ApiService,
private collectionService: CollectionService,
@@ -116,7 +115,11 @@ export class BulkDeleteDialogComponent {
});
}
if (this.collections.length) {
- await this.collectionService.delete(this.collections.map((c) => c.id));
+ const userId = await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId));
+ await this.collectionService.delete(
+ this.collections.map((c) => c.id as CollectionId),
+ userId,
+ );
this.toastService.showToast({
variant: "success",
title: null,
diff --git a/apps/web/src/app/vault/individual-vault/vault-filter/services/routed-vault-filter.service.ts b/apps/web/src/app/vault/individual-vault/vault-filter/services/routed-vault-filter.service.ts
index a42b5228272..a5a99428b2d 100644
--- a/apps/web/src/app/vault/individual-vault/vault-filter/services/routed-vault-filter.service.ts
+++ b/apps/web/src/app/vault/individual-vault/vault-filter/services/routed-vault-filter.service.ts
@@ -2,6 +2,8 @@ import { Injectable, OnDestroy } from "@angular/core";
import { ActivatedRoute, NavigationExtras } from "@angular/router";
import { combineLatest, map, Observable, Subject, takeUntil } from "rxjs";
+import { CollectionId, OrganizationId } from "@bitwarden/common/types/guid";
+
import {
isRoutedVaultFilterItemType,
RoutedVaultFilterModel,
@@ -31,10 +33,12 @@ export class RoutedVaultFilterService implements OnDestroy {
const type = isRoutedVaultFilterItemType(unsafeType) ? unsafeType : undefined;
return {
- collectionId: queryParams.get("collectionId") ?? undefined,
+ collectionId: (queryParams.get("collectionId") as CollectionId) ?? undefined,
folderId: queryParams.get("folderId") ?? undefined,
organizationId:
- params.get("organizationId") ?? queryParams.get("organizationId") ?? undefined,
+ (params.get("organizationId") as OrganizationId) ??
+ (queryParams.get("organizationId") as OrganizationId) ??
+ undefined,
organizationIdParamType:
params.get("organizationId") != undefined ? ("path" as const) : ("query" as const),
type,
diff --git a/apps/web/src/app/vault/individual-vault/vault-filter/services/vault-filter.service.spec.ts b/apps/web/src/app/vault/individual-vault/vault-filter/services/vault-filter.service.spec.ts
index 93189f2bf1c..b7a19bf2e76 100644
--- a/apps/web/src/app/vault/individual-vault/vault-filter/services/vault-filter.service.spec.ts
+++ b/apps/web/src/app/vault/individual-vault/vault-filter/services/vault-filter.service.spec.ts
@@ -78,7 +78,7 @@ describe("vault filter service", () => {
configService.getFeatureFlag$.mockReturnValue(of(true));
organizationService.memberOrganizations$.mockReturnValue(organizations);
folderService.folderViews$.mockReturnValue(folderViews);
- collectionService.decryptedCollections$ = collectionViews;
+ collectionService.decryptedCollections$.mockReturnValue(collectionViews);
policyService.policyAppliesToUser$
.calledWith(PolicyType.OrganizationDataOwnership, mockUserId)
.mockReturnValue(organizationDataOwnershipPolicy);
diff --git a/apps/web/src/app/vault/individual-vault/vault-filter/services/vault-filter.service.ts b/apps/web/src/app/vault/individual-vault/vault-filter/services/vault-filter.service.ts
index 1fe618c6c4e..11e074db985 100644
--- a/apps/web/src/app/vault/individual-vault/vault-filter/services/vault-filter.service.ts
+++ b/apps/web/src/app/vault/individual-vault/vault-filter/services/vault-filter.service.ts
@@ -4,7 +4,6 @@ import { Injectable } from "@angular/core";
import {
BehaviorSubject,
combineLatest,
- combineLatestWith,
filter,
firstValueFrom,
map,
@@ -29,7 +28,7 @@ import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { SingleUserState, StateProvider } from "@bitwarden/common/platform/state";
-import { UserId } from "@bitwarden/common/types/guid";
+import { OrganizationId, UserId } from "@bitwarden/common/types/guid";
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
import { FolderService } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction";
import { CipherType } from "@bitwarden/common/vault/enums";
@@ -100,13 +99,13 @@ export class VaultFilterService implements VaultFilterServiceAbstraction {
map((folders) => this.buildFolderTree(folders)),
);
- filteredCollections$: Observable =
- this.collectionService.decryptedCollections$.pipe(
- combineLatestWith(this._organizationFilter),
- switchMap(([collections, org]) => {
- return this.filterCollections(collections, org);
- }),
- );
+ filteredCollections$: Observable = combineLatest([
+ this.accountService.activeAccount$.pipe(
+ getUserId,
+ switchMap((userId) => this.collectionService.decryptedCollections$(userId)),
+ ),
+ this._organizationFilter,
+ ]).pipe(switchMap(([collections, org]) => this.filterCollections(collections, org)));
collectionTree$: Observable> = combineLatest([
this.filteredCollections$,
@@ -210,7 +209,7 @@ export class VaultFilterService implements VaultFilterServiceAbstraction {
protected getOrganizationFilterMyVault(): TreeNode {
const myVault = new Organization() as OrganizationFilter;
- myVault.id = "MyVault";
+ myVault.id = "MyVault" as OrganizationId;
myVault.icon = "bwi-user";
myVault.enabled = true;
myVault.hideOptions = true;
diff --git a/apps/web/src/app/vault/individual-vault/vault-filter/shared/components/vault-filter-section.component.html b/apps/web/src/app/vault/individual-vault/vault-filter/shared/components/vault-filter-section.component.html
index 1485c1f5343..01a38a02d51 100644
--- a/apps/web/src/app/vault/individual-vault/vault-filter/shared/components/vault-filter-section.component.html
+++ b/apps/web/src/app/vault/individual-vault/vault-filter/shared/components/vault-filter-section.component.html
@@ -1,3 +1,4 @@
+
) {
- let collectionId: string | undefined;
+ let collectionId: CollectionId | All | Unassigned | undefined;
if (value != null && value.node.id === null) {
collectionId = Unassigned;
diff --git a/apps/web/src/app/vault/individual-vault/vault-filter/shared/models/routed-vault-filter.model.ts b/apps/web/src/app/vault/individual-vault/vault-filter/shared/models/routed-vault-filter.model.ts
index 866ba1d9848..280ffd15732 100644
--- a/apps/web/src/app/vault/individual-vault/vault-filter/shared/models/routed-vault-filter.model.ts
+++ b/apps/web/src/app/vault/individual-vault/vault-filter/shared/models/routed-vault-filter.model.ts
@@ -1,4 +1,11 @@
+import { Unassigned } from "@bitwarden/admin-console/common";
+import { CollectionId, OrganizationId } from "@bitwarden/common/types/guid";
+
+/**
+ * A constant used to represent viewing "all" of a particular filter.
+ */
export const All = "all";
+export type All = typeof All;
// TODO: Remove `All` when moving to vertical navigation.
const itemTypes = [
@@ -19,9 +26,9 @@ export function isRoutedVaultFilterItemType(value: unknown): value is RoutedVaul
}
export interface RoutedVaultFilterModel {
- collectionId?: string;
+ collectionId?: CollectionId | All | Unassigned;
folderId?: string;
- organizationId?: string;
+ organizationId?: OrganizationId | Unassigned;
type?: RoutedVaultFilterItemType;
organizationIdParamType?: "path" | "query";
diff --git a/apps/web/src/app/vault/individual-vault/vault-header/vault-header.component.html b/apps/web/src/app/vault/individual-vault/vault-header/vault-header.component.html
index 413b4792f4e..02a7a2b12ff 100644
--- a/apps/web/src/app/vault/individual-vault/vault-header/vault-header.component.html
+++ b/apps/web/src/app/vault/individual-vault/vault-header/vault-header.component.html
@@ -78,6 +78,7 @@
implements OnInit, OnDestroy {
@ViewChild("vaultFilter", { static: true }) filterComponent: VaultFilterComponent;
+ @ViewChild("vaultItems", { static: false }) vaultItemsComponent: VaultItemsComponent;
trashCleanupWarning: string = null;
kdfIterations: number;
@@ -334,7 +336,8 @@ export class VaultComponent implements OnInit, OnDestr
});
const filter$ = this.routedVaultFilterService.filter$;
- const allCollections$ = this.collectionService.decryptedCollections$;
+
+ const allCollections$ = this.collectionService.decryptedCollections$(activeUserId);
const nestedCollections$ = allCollections$.pipe(
map((collections) => getNestedCollectionTree(collections)),
);
@@ -861,7 +864,10 @@ export class VaultComponent implements OnInit, OnDestr
if (result.collection) {
// Update CollectionService with the new collection
const c = new CollectionData(result.collection as CollectionDetailsResponse);
- await this.collectionService.upsert(c);
+ const activeUserId = await firstValueFrom(
+ this.accountService.activeAccount$.pipe(getUserId),
+ );
+ await this.collectionService.upsert(c, activeUserId);
}
this.refresh();
}
@@ -878,20 +884,23 @@ export class VaultComponent implements OnInit, OnDestr
});
const result = await lastValueFrom(dialog.closed);
+ const activeUserId = await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId));
if (result.action === CollectionDialogAction.Saved) {
if (result.collection) {
// Update CollectionService with the new collection
const c = new CollectionData(result.collection as CollectionDetailsResponse);
- await this.collectionService.upsert(c);
+ await this.collectionService.upsert(c, activeUserId);
}
this.refresh();
} else if (result.action === CollectionDialogAction.Deleted) {
- await this.collectionService.delete(result.collection?.id);
- this.refresh();
+ const parent = this.selectedCollection?.parent;
// Navigate away if we deleted the collection we were viewing
- if (this.selectedCollection?.node.id === c?.id) {
+ const navigateAway = this.selectedCollection && this.selectedCollection.node.id === c.id;
+ await this.collectionService.delete([result.collection?.id as CollectionId], activeUserId);
+ this.refresh();
+ if (navigateAway) {
await this.router.navigate([], {
- queryParams: { collectionId: this.selectedCollection.parent?.node.id ?? null },
+ queryParams: { collectionId: parent?.node.id ?? null },
queryParamsHandling: "merge",
replaceUrl: true,
});
@@ -916,18 +925,22 @@ export class VaultComponent implements OnInit, OnDestr
return;
}
try {
+ const parent = this.selectedCollection?.parent;
+ // Navigate away if we deleted the collection we were viewing
+ const navigateAway =
+ this.selectedCollection && this.selectedCollection.node.id === collection.id;
await this.apiService.deleteCollection(collection.organizationId, collection.id);
- await this.collectionService.delete(collection.id);
+ const activeUserId = await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId));
+ await this.collectionService.delete([collection.id as CollectionId], activeUserId);
this.toastService.showToast({
variant: "success",
title: null,
message: this.i18nService.t("deletedCollectionId", collection.name),
});
- // Navigate away if we deleted the collection we were viewing
- if (this.selectedCollection?.node.id === collection.id) {
+ if (navigateAway) {
await this.router.navigate([], {
- queryParams: { collectionId: this.selectedCollection.parent?.node.id ?? null },
+ queryParams: { collectionId: parent?.node.id ?? null },
queryParamsHandling: "merge",
replaceUrl: true,
});
@@ -1270,6 +1283,7 @@ export class VaultComponent implements OnInit, OnDestr
private refresh() {
this.refresh$.next();
+ this.vaultItemsComponent?.clearSelection();
}
private async go(queryParams: any = null) {
diff --git a/apps/web/src/app/vault/services/web-browser-interaction.service.ts b/apps/web/src/app/vault/services/web-browser-interaction.service.ts
index 1f91942591b..ed5e2ef9948 100644
--- a/apps/web/src/app/vault/services/web-browser-interaction.service.ts
+++ b/apps/web/src/app/vault/services/web-browser-interaction.service.ts
@@ -25,7 +25,7 @@ import { VaultMessages } from "@bitwarden/common/vault/enums/vault-messages.enum
* used to allow for the extension to open and then emit to the message.
* NOTE: This value isn't computed by any means, it is just a reasonable timeout for the extension to respond.
*/
-const OPEN_RESPONSE_TIMEOUT_MS = 1500;
+const OPEN_RESPONSE_TIMEOUT_MS = 2000;
/**
* Timeout for checking if the extension is installed.
diff --git a/apps/web/src/images/integrations/logo-crowdstrike-black.svg b/apps/web/src/images/integrations/logo-crowdstrike-black.svg
new file mode 100644
index 00000000000..25875d705cb
--- /dev/null
+++ b/apps/web/src/images/integrations/logo-crowdstrike-black.svg
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/apps/web/src/locales/af/messages.json b/apps/web/src/locales/af/messages.json
index 5cd8d087d15..a27cff6212f 100644
--- a/apps/web/src/locales/af/messages.json
+++ b/apps/web/src/locales/af/messages.json
@@ -568,6 +568,9 @@
"cancel": {
"message": "Kanselleer"
},
+ "later": {
+ "message": "Later"
+ },
"canceled": {
"message": "Gekanselleer"
},
@@ -626,6 +629,9 @@
"searchGroups": {
"message": "Deursoek groepe"
},
+ "resetSearch": {
+ "message": "Reset search"
+ },
"allItems": {
"message": "Alle items"
},
@@ -4626,6 +4632,9 @@
"receiveMarketingEmailsV2": {
"message": "Get advice, announcements, and research opportunities from Bitwarden in your inbox."
},
+ "subscribe": {
+ "message": "Subscribe"
+ },
"unsubscribe": {
"message": "Unsubscribe"
},
@@ -9465,6 +9474,12 @@
"deviceManagementDesc": {
"message": "Configure device management for Bitwarden using the implementation guide for your platform."
},
+ "crowdstrikeEventIntegrationDesc": {
+ "message": "Send event data to your Logscale instance"
+ },
+ "failedToSaveIntegration": {
+ "message": "Failed to save integration. Please try again later."
+ },
"deviceIdMissing": {
"message": "Device ID is missing"
},
@@ -9480,6 +9495,15 @@
"reopenLinkOnDesktop": {
"message": "Reopen this link from your email on a desktop."
},
+ "connectIntegrationButtonDesc": {
+ "message": "Connect $INTEGRATION$",
+ "placeholders": {
+ "integration": {
+ "content": "$1",
+ "example": "Crowdstrike"
+ }
+ }
+ },
"integrationCardTooltip": {
"message": "Launch $INTEGRATION$ implementation guide.",
"placeholders": {
@@ -9537,6 +9561,15 @@
"createNewClientToManageAsProvider": {
"message": "Create a new client organization to manage as a Provider. Additional seats will be reflected in the next billing cycle."
},
+ "url": {
+ "message": "URL"
+ },
+ "bearerToken": {
+ "message": "Bearer Token"
+ },
+ "index": {
+ "message": "Index"
+ },
"selectAPlan": {
"message": "Kies ’n plan"
},
@@ -10285,8 +10318,8 @@
"organizationUserDeletedDesc": {
"message": "The user was removed from the organization and all associated user data has been deleted."
},
- "deletedUserId": {
- "message": "Deleted user $ID$ - an owner / admin deleted the user account",
+ "deletedUserIdEventMessage": {
+ "message": "Deleted user $ID$",
"placeholders": {
"id": {
"content": "$1",
@@ -10396,8 +10429,8 @@
"domainStatusUnderVerification": {
"message": "Under verification"
},
- "claimedDomainsDesc": {
- "message": "Claim a domain to own all member accounts whose email address matches the domain. Members will be able to skip the SSO identifier when logging in. Administrators will also be able to delete member accounts."
+ "claimedDomainsDescription": {
+ "message": "Claim a domain to own member accounts. The SSO identifier page will be skipped during login for members with claimed domains and administrators will be able to delete claimed accounts."
},
"invalidDomainNameClaimMessage": {
"message": "Input is not a valid format. Format: mydomain.com. Subdomains require separate entries to be claimed."
@@ -10896,5 +10929,56 @@
"example": "12-3456789"
}
}
+ },
+ "unpaidInvoices": {
+ "message": "Unpaid invoices"
+ },
+ "unpaidInvoicesForServiceUser": {
+ "message": "Your subscription has not been paid. Contact your provider administrator to restore service to you and your clients.",
+ "description": "A message shown in a non-dismissible dialog to service users of unpaid providers."
+ },
+ "providerSuspended": {
+ "message": "$PROVIDER$ is suspended",
+ "placeholders": {
+ "provider": {
+ "content": "$1",
+ "example": "Acme Industries"
+ }
+ }
+ },
+ "restoreProviderPortalAccessViaCustomerSupport": {
+ "message": "To restore access to your provider portal, contact Bitwarden Customer Support to renew your subscription.",
+ "description": "A message shown in a non-dismissible dialog to any user of a suspended providers."
+ },
+ "restoreProviderPortalAccessViaPaymentMethod": {
+ "message": "Your subscription has not been paid. To restore service to you and your clients, add a payment method by $CANCELLATION_DATE$.",
+ "placeholders": {
+ "cancellation_date": {
+ "content": "$1",
+ "example": "07/10/2025"
+ }
+ },
+ "description": "A message shown in a non-dismissible dialog to admins of unpaid providers."
+ },
+ "subscribetoEnterprise": {
+ "message": "Subscribe to $PLAN$",
+ "placeholders": {
+ "plan": {
+ "content": "$1",
+ "example": "Teams"
+ }
+ }
+ },
+ "subscribeEnterpriseSubtitle": {
+ "message": "Your 7-day $PLAN$ trial starts today. Add a payment method now to continue using these features after your trial ends: ",
+ "placeholders": {
+ "plan": {
+ "content": "$1",
+ "example": "Teams"
+ }
+ }
+ },
+ "unlimitedSecretsAndProjects": {
+ "message": "Unlimited secrets and projects"
}
-}
+}
\ No newline at end of file
diff --git a/apps/web/src/locales/ar/messages.json b/apps/web/src/locales/ar/messages.json
index 7642fa69d34..8b544671386 100644
--- a/apps/web/src/locales/ar/messages.json
+++ b/apps/web/src/locales/ar/messages.json
@@ -568,6 +568,9 @@
"cancel": {
"message": "إلغاء"
},
+ "later": {
+ "message": "Later"
+ },
"canceled": {
"message": "ملغى"
},
@@ -626,6 +629,9 @@
"searchGroups": {
"message": "البحث عن فِرَق"
},
+ "resetSearch": {
+ "message": "Reset search"
+ },
"allItems": {
"message": "جميع العناصر"
},
@@ -4626,6 +4632,9 @@
"receiveMarketingEmailsV2": {
"message": "Get advice, announcements, and research opportunities from Bitwarden in your inbox."
},
+ "subscribe": {
+ "message": "Subscribe"
+ },
"unsubscribe": {
"message": "Unsubscribe"
},
@@ -9465,6 +9474,12 @@
"deviceManagementDesc": {
"message": "Configure device management for Bitwarden using the implementation guide for your platform."
},
+ "crowdstrikeEventIntegrationDesc": {
+ "message": "Send event data to your Logscale instance"
+ },
+ "failedToSaveIntegration": {
+ "message": "Failed to save integration. Please try again later."
+ },
"deviceIdMissing": {
"message": "Device ID is missing"
},
@@ -9480,6 +9495,15 @@
"reopenLinkOnDesktop": {
"message": "Reopen this link from your email on a desktop."
},
+ "connectIntegrationButtonDesc": {
+ "message": "Connect $INTEGRATION$",
+ "placeholders": {
+ "integration": {
+ "content": "$1",
+ "example": "Crowdstrike"
+ }
+ }
+ },
"integrationCardTooltip": {
"message": "Launch $INTEGRATION$ implementation guide.",
"placeholders": {
@@ -9537,6 +9561,15 @@
"createNewClientToManageAsProvider": {
"message": "Create a new client organization to manage as a Provider. Additional seats will be reflected in the next billing cycle."
},
+ "url": {
+ "message": "URL"
+ },
+ "bearerToken": {
+ "message": "Bearer Token"
+ },
+ "index": {
+ "message": "Index"
+ },
"selectAPlan": {
"message": "Select a plan"
},
@@ -10285,8 +10318,8 @@
"organizationUserDeletedDesc": {
"message": "The user was removed from the organization and all associated user data has been deleted."
},
- "deletedUserId": {
- "message": "Deleted user $ID$ - an owner / admin deleted the user account",
+ "deletedUserIdEventMessage": {
+ "message": "Deleted user $ID$",
"placeholders": {
"id": {
"content": "$1",
@@ -10396,8 +10429,8 @@
"domainStatusUnderVerification": {
"message": "Under verification"
},
- "claimedDomainsDesc": {
- "message": "Claim a domain to own all member accounts whose email address matches the domain. Members will be able to skip the SSO identifier when logging in. Administrators will also be able to delete member accounts."
+ "claimedDomainsDescription": {
+ "message": "Claim a domain to own member accounts. The SSO identifier page will be skipped during login for members with claimed domains and administrators will be able to delete claimed accounts."
},
"invalidDomainNameClaimMessage": {
"message": "Input is not a valid format. Format: mydomain.com. Subdomains require separate entries to be claimed."
@@ -10896,5 +10929,56 @@
"example": "12-3456789"
}
}
+ },
+ "unpaidInvoices": {
+ "message": "Unpaid invoices"
+ },
+ "unpaidInvoicesForServiceUser": {
+ "message": "Your subscription has not been paid. Contact your provider administrator to restore service to you and your clients.",
+ "description": "A message shown in a non-dismissible dialog to service users of unpaid providers."
+ },
+ "providerSuspended": {
+ "message": "$PROVIDER$ is suspended",
+ "placeholders": {
+ "provider": {
+ "content": "$1",
+ "example": "Acme Industries"
+ }
+ }
+ },
+ "restoreProviderPortalAccessViaCustomerSupport": {
+ "message": "To restore access to your provider portal, contact Bitwarden Customer Support to renew your subscription.",
+ "description": "A message shown in a non-dismissible dialog to any user of a suspended providers."
+ },
+ "restoreProviderPortalAccessViaPaymentMethod": {
+ "message": "Your subscription has not been paid. To restore service to you and your clients, add a payment method by $CANCELLATION_DATE$.",
+ "placeholders": {
+ "cancellation_date": {
+ "content": "$1",
+ "example": "07/10/2025"
+ }
+ },
+ "description": "A message shown in a non-dismissible dialog to admins of unpaid providers."
+ },
+ "subscribetoEnterprise": {
+ "message": "Subscribe to $PLAN$",
+ "placeholders": {
+ "plan": {
+ "content": "$1",
+ "example": "Teams"
+ }
+ }
+ },
+ "subscribeEnterpriseSubtitle": {
+ "message": "Your 7-day $PLAN$ trial starts today. Add a payment method now to continue using these features after your trial ends: ",
+ "placeholders": {
+ "plan": {
+ "content": "$1",
+ "example": "Teams"
+ }
+ }
+ },
+ "unlimitedSecretsAndProjects": {
+ "message": "Unlimited secrets and projects"
}
-}
+}
\ No newline at end of file
diff --git a/apps/web/src/locales/az/messages.json b/apps/web/src/locales/az/messages.json
index 2c51163b62c..930e46f410b 100644
--- a/apps/web/src/locales/az/messages.json
+++ b/apps/web/src/locales/az/messages.json
@@ -568,6 +568,9 @@
"cancel": {
"message": "İmtina"
},
+ "later": {
+ "message": "Later"
+ },
"canceled": {
"message": "Ləğv edildi"
},
@@ -626,6 +629,9 @@
"searchGroups": {
"message": "Qrupları axtar"
},
+ "resetSearch": {
+ "message": "Reset search"
+ },
"allItems": {
"message": "Bütün elementlər"
},
@@ -4626,6 +4632,9 @@
"receiveMarketingEmailsV2": {
"message": "Bitwarden-in tövsiyə, elan və araşdırma imkanlarını gələn qutunuzda əldə edin."
},
+ "subscribe": {
+ "message": "Subscribe"
+ },
"unsubscribe": {
"message": "Abunəlikdən çıx"
},
@@ -9465,6 +9474,12 @@
"deviceManagementDesc": {
"message": "Platformanız üçün icra bələdçisini istifadə edərək Bitwarden üçün cihaz idarəetməsini konfiqurasiya edin."
},
+ "crowdstrikeEventIntegrationDesc": {
+ "message": "Send event data to your Logscale instance"
+ },
+ "failedToSaveIntegration": {
+ "message": "Failed to save integration. Please try again later."
+ },
"deviceIdMissing": {
"message": "Cihaz kimliyi əskikdir"
},
@@ -9480,6 +9495,15 @@
"reopenLinkOnDesktop": {
"message": "Bu keçidi e-poçtunuzdan masaüstündə yenidən açın."
},
+ "connectIntegrationButtonDesc": {
+ "message": "Connect $INTEGRATION$",
+ "placeholders": {
+ "integration": {
+ "content": "$1",
+ "example": "Crowdstrike"
+ }
+ }
+ },
"integrationCardTooltip": {
"message": "$INTEGRATION$ icra bələdçisini başlat.",
"placeholders": {
@@ -9537,6 +9561,15 @@
"createNewClientToManageAsProvider": {
"message": "Provayder kimi idarə etmək üçün yeni bir client təşkilatı yaradın. Əlavə yerlər növbəti faktura dövründə əks olunacaq."
},
+ "url": {
+ "message": "URL"
+ },
+ "bearerToken": {
+ "message": "Bearer Token"
+ },
+ "index": {
+ "message": "Index"
+ },
"selectAPlan": {
"message": "Bir plan seçin"
},
@@ -10285,8 +10318,8 @@
"organizationUserDeletedDesc": {
"message": "İstifadəçi təşkilatdan çıxarıldı və əlaqələndirilmiş bütün istifadəçi dataları silindi."
},
- "deletedUserId": {
- "message": "$ID$ istifadəçisi silindi - bir sahib/admin, istifadəçi hesabını sildi",
+ "deletedUserIdEventMessage": {
+ "message": "Deleted user $ID$",
"placeholders": {
"id": {
"content": "$1",
@@ -10396,8 +10429,8 @@
"domainStatusUnderVerification": {
"message": "Doğrulama altında"
},
- "claimedDomainsDesc": {
- "message": "Domenlə uyuşan e-poçt ünvanına sahib bütün üzvlərin hesablarına sahib olmaq üçün bir domen götürün. Üzvlər giriş edərkən SSO identifikatorunu ötürə biləcək. Həmçinin inzibatçılar, üzv hesablarını silə biləcək."
+ "claimedDomainsDescription": {
+ "message": "Üzv hesablarına sahiblik etmək üçün bir domen götürün. Domen götürmüş üzvlər üçün giriş zamanı SSO identifikatoru səhifəsi ötürüləcək və inzibatçılar bu domenə aid hesabları silə biləcək."
},
"invalidDomainNameClaimMessage": {
"message": "Giriş, yararlı bir format deyil. Format: mydomain.com. Alt domenlərin götürülməsi üçün ayrıca girişlər tələb olunur."
@@ -10896,5 +10929,56 @@
"example": "12-3456789"
}
}
+ },
+ "unpaidInvoices": {
+ "message": "Unpaid invoices"
+ },
+ "unpaidInvoicesForServiceUser": {
+ "message": "Your subscription has not been paid. Contact your provider administrator to restore service to you and your clients.",
+ "description": "A message shown in a non-dismissible dialog to service users of unpaid providers."
+ },
+ "providerSuspended": {
+ "message": "$PROVIDER$ is suspended",
+ "placeholders": {
+ "provider": {
+ "content": "$1",
+ "example": "Acme Industries"
+ }
+ }
+ },
+ "restoreProviderPortalAccessViaCustomerSupport": {
+ "message": "To restore access to your provider portal, contact Bitwarden Customer Support to renew your subscription.",
+ "description": "A message shown in a non-dismissible dialog to any user of a suspended providers."
+ },
+ "restoreProviderPortalAccessViaPaymentMethod": {
+ "message": "Your subscription has not been paid. To restore service to you and your clients, add a payment method by $CANCELLATION_DATE$.",
+ "placeholders": {
+ "cancellation_date": {
+ "content": "$1",
+ "example": "07/10/2025"
+ }
+ },
+ "description": "A message shown in a non-dismissible dialog to admins of unpaid providers."
+ },
+ "subscribetoEnterprise": {
+ "message": "Subscribe to $PLAN$",
+ "placeholders": {
+ "plan": {
+ "content": "$1",
+ "example": "Teams"
+ }
+ }
+ },
+ "subscribeEnterpriseSubtitle": {
+ "message": "Your 7-day $PLAN$ trial starts today. Add a payment method now to continue using these features after your trial ends: ",
+ "placeholders": {
+ "plan": {
+ "content": "$1",
+ "example": "Teams"
+ }
+ }
+ },
+ "unlimitedSecretsAndProjects": {
+ "message": "Unlimited secrets and projects"
}
-}
+}
\ No newline at end of file
diff --git a/apps/web/src/locales/be/messages.json b/apps/web/src/locales/be/messages.json
index d07df39790c..3921940657b 100644
--- a/apps/web/src/locales/be/messages.json
+++ b/apps/web/src/locales/be/messages.json
@@ -568,6 +568,9 @@
"cancel": {
"message": "Скасаваць"
},
+ "later": {
+ "message": "Later"
+ },
"canceled": {
"message": "Скасавана"
},
@@ -626,6 +629,9 @@
"searchGroups": {
"message": "Пошук груп"
},
+ "resetSearch": {
+ "message": "Reset search"
+ },
"allItems": {
"message": "Усе элементы"
},
@@ -4626,6 +4632,9 @@
"receiveMarketingEmailsV2": {
"message": "Get advice, announcements, and research opportunities from Bitwarden in your inbox."
},
+ "subscribe": {
+ "message": "Subscribe"
+ },
"unsubscribe": {
"message": "Unsubscribe"
},
@@ -9465,6 +9474,12 @@
"deviceManagementDesc": {
"message": "Configure device management for Bitwarden using the implementation guide for your platform."
},
+ "crowdstrikeEventIntegrationDesc": {
+ "message": "Send event data to your Logscale instance"
+ },
+ "failedToSaveIntegration": {
+ "message": "Failed to save integration. Please try again later."
+ },
"deviceIdMissing": {
"message": "Device ID is missing"
},
@@ -9480,6 +9495,15 @@
"reopenLinkOnDesktop": {
"message": "Reopen this link from your email on a desktop."
},
+ "connectIntegrationButtonDesc": {
+ "message": "Connect $INTEGRATION$",
+ "placeholders": {
+ "integration": {
+ "content": "$1",
+ "example": "Crowdstrike"
+ }
+ }
+ },
"integrationCardTooltip": {
"message": "Launch $INTEGRATION$ implementation guide.",
"placeholders": {
@@ -9537,6 +9561,15 @@
"createNewClientToManageAsProvider": {
"message": "Create a new client organization to manage as a Provider. Additional seats will be reflected in the next billing cycle."
},
+ "url": {
+ "message": "URL"
+ },
+ "bearerToken": {
+ "message": "Bearer Token"
+ },
+ "index": {
+ "message": "Index"
+ },
"selectAPlan": {
"message": "Select a plan"
},
@@ -10285,8 +10318,8 @@
"organizationUserDeletedDesc": {
"message": "The user was removed from the organization and all associated user data has been deleted."
},
- "deletedUserId": {
- "message": "Deleted user $ID$ - an owner / admin deleted the user account",
+ "deletedUserIdEventMessage": {
+ "message": "Deleted user $ID$",
"placeholders": {
"id": {
"content": "$1",
@@ -10396,8 +10429,8 @@
"domainStatusUnderVerification": {
"message": "Under verification"
},
- "claimedDomainsDesc": {
- "message": "Claim a domain to own all member accounts whose email address matches the domain. Members will be able to skip the SSO identifier when logging in. Administrators will also be able to delete member accounts."
+ "claimedDomainsDescription": {
+ "message": "Claim a domain to own member accounts. The SSO identifier page will be skipped during login for members with claimed domains and administrators will be able to delete claimed accounts."
},
"invalidDomainNameClaimMessage": {
"message": "Input is not a valid format. Format: mydomain.com. Subdomains require separate entries to be claimed."
@@ -10896,5 +10929,56 @@
"example": "12-3456789"
}
}
+ },
+ "unpaidInvoices": {
+ "message": "Unpaid invoices"
+ },
+ "unpaidInvoicesForServiceUser": {
+ "message": "Your subscription has not been paid. Contact your provider administrator to restore service to you and your clients.",
+ "description": "A message shown in a non-dismissible dialog to service users of unpaid providers."
+ },
+ "providerSuspended": {
+ "message": "$PROVIDER$ is suspended",
+ "placeholders": {
+ "provider": {
+ "content": "$1",
+ "example": "Acme Industries"
+ }
+ }
+ },
+ "restoreProviderPortalAccessViaCustomerSupport": {
+ "message": "To restore access to your provider portal, contact Bitwarden Customer Support to renew your subscription.",
+ "description": "A message shown in a non-dismissible dialog to any user of a suspended providers."
+ },
+ "restoreProviderPortalAccessViaPaymentMethod": {
+ "message": "Your subscription has not been paid. To restore service to you and your clients, add a payment method by $CANCELLATION_DATE$.",
+ "placeholders": {
+ "cancellation_date": {
+ "content": "$1",
+ "example": "07/10/2025"
+ }
+ },
+ "description": "A message shown in a non-dismissible dialog to admins of unpaid providers."
+ },
+ "subscribetoEnterprise": {
+ "message": "Subscribe to $PLAN$",
+ "placeholders": {
+ "plan": {
+ "content": "$1",
+ "example": "Teams"
+ }
+ }
+ },
+ "subscribeEnterpriseSubtitle": {
+ "message": "Your 7-day $PLAN$ trial starts today. Add a payment method now to continue using these features after your trial ends: ",
+ "placeholders": {
+ "plan": {
+ "content": "$1",
+ "example": "Teams"
+ }
+ }
+ },
+ "unlimitedSecretsAndProjects": {
+ "message": "Unlimited secrets and projects"
}
-}
+}
\ No newline at end of file
diff --git a/apps/web/src/locales/bg/messages.json b/apps/web/src/locales/bg/messages.json
index 315ce4ee30b..5ea290d81f0 100644
--- a/apps/web/src/locales/bg/messages.json
+++ b/apps/web/src/locales/bg/messages.json
@@ -568,6 +568,9 @@
"cancel": {
"message": "Отказ"
},
+ "later": {
+ "message": "По-късно"
+ },
"canceled": {
"message": "Отказано"
},
@@ -626,6 +629,9 @@
"searchGroups": {
"message": "Търсене в групите"
},
+ "resetSearch": {
+ "message": "Нулиране на търсенето"
+ },
"allItems": {
"message": "Всички елементи"
},
@@ -4626,6 +4632,9 @@
"receiveMarketingEmailsV2": {
"message": "Получавайте съвети, обявления и предложения за участие в проучвания от Битуорден в пощенската си кутия."
},
+ "subscribe": {
+ "message": "Абониране"
+ },
"unsubscribe": {
"message": "Отписване"
},
@@ -6288,25 +6297,25 @@
"message": "OIDC redirect behavior"
},
"getClaimsFromUserInfoEndpoint": {
- "message": "Get claims from user info endpoint"
+ "message": "Получаване на свойствата от адреса за информация за потребителя"
},
"additionalScopes": {
"message": "Custom scopes"
},
"additionalUserIdClaimTypes": {
- "message": "Custom user ID claim types"
+ "message": "Персонализирани типове свойства за потребителски идентификатор"
},
"additionalEmailClaimTypes": {
- "message": "Email claim types"
+ "message": "Типове свойства за е-поща"
},
"additionalNameClaimTypes": {
- "message": "Custom name claim types"
+ "message": "Персонализирани типове свойства за име"
},
"acrValues": {
"message": "Requested authentication context class reference values"
},
"expectedReturnAcrValue": {
- "message": "Expected \"acr\" claim value in response"
+ "message": "В отговора липсва стойност за свойството „acr“"
},
"spEntityId": {
"message": "SP entity ID"
@@ -6429,7 +6438,7 @@
"message": "Връзката е с изтекла давност. Помолете спонсора да изпрати отново предложението."
},
"reclaimedFreePlan": {
- "message": "Reclaimed free plan"
+ "message": "Възвърнат безплатен план"
},
"redeem": {
"message": "Redeem"
@@ -7951,7 +7960,7 @@
"message": "Домейнът е запазен"
},
"duplicateDomainError": {
- "message": "Не може да заявите един и същ домейн повече от веднъж."
+ "message": "Не може да присвоите един и същ домейн повече от веднъж."
},
"domainNotAvailable": {
"message": "Някой вече ползва $DOMAIN$. Изберете различен домейн, за да продължите.",
@@ -9465,6 +9474,12 @@
"deviceManagementDesc": {
"message": "Настройте управлението на устройства в Битуорден, като използвате ръководството за внедряване за платформата си."
},
+ "crowdstrikeEventIntegrationDesc": {
+ "message": "Изпращане на данни за събитията до Вашата инстанция на Logscale"
+ },
+ "failedToSaveIntegration": {
+ "message": "Интеграцията не беше запазена. Опитайте отново по-късно."
+ },
"deviceIdMissing": {
"message": "Липсва идентификатор на устройството"
},
@@ -9480,6 +9495,15 @@
"reopenLinkOnDesktop": {
"message": "Отворете тази връзка от е-пощата си на компютър."
},
+ "connectIntegrationButtonDesc": {
+ "message": "Свързване на $INTEGRATION$",
+ "placeholders": {
+ "integration": {
+ "content": "$1",
+ "example": "Crowdstrike"
+ }
+ }
+ },
"integrationCardTooltip": {
"message": "Стартиране на ръководството за внедряване за $INTEGRATION$.",
"placeholders": {
@@ -9537,6 +9561,15 @@
"createNewClientToManageAsProvider": {
"message": "Създайте нова организация, която да управлявате като доставчик. Допълнителните места ще бъдат отразени в следващия платежен период."
},
+ "url": {
+ "message": "Адрес"
+ },
+ "bearerToken": {
+ "message": "Идентификатор „Bearer“"
+ },
+ "index": {
+ "message": "Индекс"
+ },
"selectAPlan": {
"message": "Изберете план"
},
@@ -10285,8 +10318,8 @@
"organizationUserDeletedDesc": {
"message": "Потребителят беше премахнат от организацията и всичките му данни бяха изтрити."
},
- "deletedUserId": {
- "message": "Изтрит потребител $ID$ – собственик / администратор е изтрил регистрацията на потребителя",
+ "deletedUserIdEventMessage": {
+ "message": "Потребител № $ID$ е изтрит",
"placeholders": {
"id": {
"content": "$1",
@@ -10396,8 +10429,8 @@
"domainStatusUnderVerification": {
"message": "В процес на проверка"
},
- "claimedDomainsDesc": {
- "message": "Присвоете домейн, за да получите притежание върху всички членски акаунти, чиито е-пощи са с адрес от този домейн. Членовете ще могат да пропускат еднократното удостоверяване при вписване. Администраторите ще могат също така да изтриват членските акаунти."
+ "claimedDomainsDescription": {
+ "message": "Присвойте домейн, за да притежавате акаунтите на членовете. Страницата за еднократно удостоверяване ще бъде пропускана при вписването на членове с присвоени домейни, а администраторите ще могат да изтриват присвоените акаунти."
},
"invalidDomainNameClaimMessage": {
"message": "Неправилен формат. Пример: mydomain.com. Под-домейните изискват всеки отделен запис да бъде присвоен."
@@ -10896,5 +10929,56 @@
"example": "12-3456789"
}
}
+ },
+ "unpaidInvoices": {
+ "message": "Неплатени фактури"
+ },
+ "unpaidInvoicesForServiceUser": {
+ "message": "Абонаментът Ви не е платен. Свържете се с администратора на доставчика си, за да възстанови услугата за Вас и Вашите клиенти.",
+ "description": "A message shown in a non-dismissible dialog to service users of unpaid providers."
+ },
+ "providerSuspended": {
+ "message": "Достъпът на $PROVIDER$ е преустановен",
+ "placeholders": {
+ "provider": {
+ "content": "$1",
+ "example": "Acme Industries"
+ }
+ }
+ },
+ "restoreProviderPortalAccessViaCustomerSupport": {
+ "message": "За да възстановите достъпа до портала на доставчика си, свържете се с поддръжката на Битуорден, за да подновите абонамента си.",
+ "description": "A message shown in a non-dismissible dialog to any user of a suspended providers."
+ },
+ "restoreProviderPortalAccessViaPaymentMethod": {
+ "message": "Вашият абонамент не е платен. За да възстановите услугата за Вас и Вашите клиенти, добавете разплащателен метод до $CANCELLATION_DATE$.",
+ "placeholders": {
+ "cancellation_date": {
+ "content": "$1",
+ "example": "07/10/2025"
+ }
+ },
+ "description": "A message shown in a non-dismissible dialog to admins of unpaid providers."
+ },
+ "subscribetoEnterprise": {
+ "message": "Стартиране на абонамент за $PLAN$",
+ "placeholders": {
+ "plan": {
+ "content": "$1",
+ "example": "Teams"
+ }
+ }
+ },
+ "subscribeEnterpriseSubtitle": {
+ "message": "Вашият 7-дневен пробен период на $PLAN$ започва днес. Добавете разплащателен метод, за да продължите да използвате тези функционалности и след като приключи пробният период: ",
+ "placeholders": {
+ "plan": {
+ "content": "$1",
+ "example": "Teams"
+ }
+ }
+ },
+ "unlimitedSecretsAndProjects": {
+ "message": "Неограничен брой тайни и проекти"
}
-}
+}
\ No newline at end of file
diff --git a/apps/web/src/locales/bn/messages.json b/apps/web/src/locales/bn/messages.json
index 0b0573c244d..6f522d4db62 100644
--- a/apps/web/src/locales/bn/messages.json
+++ b/apps/web/src/locales/bn/messages.json
@@ -568,6 +568,9 @@
"cancel": {
"message": "বাতিল"
},
+ "later": {
+ "message": "Later"
+ },
"canceled": {
"message": "বাতিলকৃত"
},
@@ -626,6 +629,9 @@
"searchGroups": {
"message": "Search groups"
},
+ "resetSearch": {
+ "message": "Reset search"
+ },
"allItems": {
"message": "সকল বস্তু"
},
@@ -4626,6 +4632,9 @@
"receiveMarketingEmailsV2": {
"message": "Get advice, announcements, and research opportunities from Bitwarden in your inbox."
},
+ "subscribe": {
+ "message": "Subscribe"
+ },
"unsubscribe": {
"message": "Unsubscribe"
},
@@ -9465,6 +9474,12 @@
"deviceManagementDesc": {
"message": "Configure device management for Bitwarden using the implementation guide for your platform."
},
+ "crowdstrikeEventIntegrationDesc": {
+ "message": "Send event data to your Logscale instance"
+ },
+ "failedToSaveIntegration": {
+ "message": "Failed to save integration. Please try again later."
+ },
"deviceIdMissing": {
"message": "Device ID is missing"
},
@@ -9480,6 +9495,15 @@
"reopenLinkOnDesktop": {
"message": "Reopen this link from your email on a desktop."
},
+ "connectIntegrationButtonDesc": {
+ "message": "Connect $INTEGRATION$",
+ "placeholders": {
+ "integration": {
+ "content": "$1",
+ "example": "Crowdstrike"
+ }
+ }
+ },
"integrationCardTooltip": {
"message": "Launch $INTEGRATION$ implementation guide.",
"placeholders": {
@@ -9537,6 +9561,15 @@
"createNewClientToManageAsProvider": {
"message": "Create a new client organization to manage as a Provider. Additional seats will be reflected in the next billing cycle."
},
+ "url": {
+ "message": "URL"
+ },
+ "bearerToken": {
+ "message": "Bearer Token"
+ },
+ "index": {
+ "message": "Index"
+ },
"selectAPlan": {
"message": "Select a plan"
},
@@ -10285,8 +10318,8 @@
"organizationUserDeletedDesc": {
"message": "The user was removed from the organization and all associated user data has been deleted."
},
- "deletedUserId": {
- "message": "Deleted user $ID$ - an owner / admin deleted the user account",
+ "deletedUserIdEventMessage": {
+ "message": "Deleted user $ID$",
"placeholders": {
"id": {
"content": "$1",
@@ -10396,8 +10429,8 @@
"domainStatusUnderVerification": {
"message": "Under verification"
},
- "claimedDomainsDesc": {
- "message": "Claim a domain to own all member accounts whose email address matches the domain. Members will be able to skip the SSO identifier when logging in. Administrators will also be able to delete member accounts."
+ "claimedDomainsDescription": {
+ "message": "Claim a domain to own member accounts. The SSO identifier page will be skipped during login for members with claimed domains and administrators will be able to delete claimed accounts."
},
"invalidDomainNameClaimMessage": {
"message": "Input is not a valid format. Format: mydomain.com. Subdomains require separate entries to be claimed."
@@ -10896,5 +10929,56 @@
"example": "12-3456789"
}
}
+ },
+ "unpaidInvoices": {
+ "message": "Unpaid invoices"
+ },
+ "unpaidInvoicesForServiceUser": {
+ "message": "Your subscription has not been paid. Contact your provider administrator to restore service to you and your clients.",
+ "description": "A message shown in a non-dismissible dialog to service users of unpaid providers."
+ },
+ "providerSuspended": {
+ "message": "$PROVIDER$ is suspended",
+ "placeholders": {
+ "provider": {
+ "content": "$1",
+ "example": "Acme Industries"
+ }
+ }
+ },
+ "restoreProviderPortalAccessViaCustomerSupport": {
+ "message": "To restore access to your provider portal, contact Bitwarden Customer Support to renew your subscription.",
+ "description": "A message shown in a non-dismissible dialog to any user of a suspended providers."
+ },
+ "restoreProviderPortalAccessViaPaymentMethod": {
+ "message": "Your subscription has not been paid. To restore service to you and your clients, add a payment method by $CANCELLATION_DATE$.",
+ "placeholders": {
+ "cancellation_date": {
+ "content": "$1",
+ "example": "07/10/2025"
+ }
+ },
+ "description": "A message shown in a non-dismissible dialog to admins of unpaid providers."
+ },
+ "subscribetoEnterprise": {
+ "message": "Subscribe to $PLAN$",
+ "placeholders": {
+ "plan": {
+ "content": "$1",
+ "example": "Teams"
+ }
+ }
+ },
+ "subscribeEnterpriseSubtitle": {
+ "message": "Your 7-day $PLAN$ trial starts today. Add a payment method now to continue using these features after your trial ends: ",
+ "placeholders": {
+ "plan": {
+ "content": "$1",
+ "example": "Teams"
+ }
+ }
+ },
+ "unlimitedSecretsAndProjects": {
+ "message": "Unlimited secrets and projects"
}
-}
+}
\ No newline at end of file
diff --git a/apps/web/src/locales/bs/messages.json b/apps/web/src/locales/bs/messages.json
index 02ed711cb5f..7896b4ef1ce 100644
--- a/apps/web/src/locales/bs/messages.json
+++ b/apps/web/src/locales/bs/messages.json
@@ -568,6 +568,9 @@
"cancel": {
"message": "Otkaži"
},
+ "later": {
+ "message": "Later"
+ },
"canceled": {
"message": "Otkazano"
},
@@ -626,6 +629,9 @@
"searchGroups": {
"message": "Search groups"
},
+ "resetSearch": {
+ "message": "Reset search"
+ },
"allItems": {
"message": "Sve stavke"
},
@@ -4626,6 +4632,9 @@
"receiveMarketingEmailsV2": {
"message": "Get advice, announcements, and research opportunities from Bitwarden in your inbox."
},
+ "subscribe": {
+ "message": "Subscribe"
+ },
"unsubscribe": {
"message": "Unsubscribe"
},
@@ -9465,6 +9474,12 @@
"deviceManagementDesc": {
"message": "Configure device management for Bitwarden using the implementation guide for your platform."
},
+ "crowdstrikeEventIntegrationDesc": {
+ "message": "Send event data to your Logscale instance"
+ },
+ "failedToSaveIntegration": {
+ "message": "Failed to save integration. Please try again later."
+ },
"deviceIdMissing": {
"message": "Device ID is missing"
},
@@ -9480,6 +9495,15 @@
"reopenLinkOnDesktop": {
"message": "Reopen this link from your email on a desktop."
},
+ "connectIntegrationButtonDesc": {
+ "message": "Connect $INTEGRATION$",
+ "placeholders": {
+ "integration": {
+ "content": "$1",
+ "example": "Crowdstrike"
+ }
+ }
+ },
"integrationCardTooltip": {
"message": "Launch $INTEGRATION$ implementation guide.",
"placeholders": {
@@ -9537,6 +9561,15 @@
"createNewClientToManageAsProvider": {
"message": "Create a new client organization to manage as a Provider. Additional seats will be reflected in the next billing cycle."
},
+ "url": {
+ "message": "URL"
+ },
+ "bearerToken": {
+ "message": "Bearer Token"
+ },
+ "index": {
+ "message": "Index"
+ },
"selectAPlan": {
"message": "Select a plan"
},
@@ -10285,8 +10318,8 @@
"organizationUserDeletedDesc": {
"message": "The user was removed from the organization and all associated user data has been deleted."
},
- "deletedUserId": {
- "message": "Deleted user $ID$ - an owner / admin deleted the user account",
+ "deletedUserIdEventMessage": {
+ "message": "Deleted user $ID$",
"placeholders": {
"id": {
"content": "$1",
@@ -10396,8 +10429,8 @@
"domainStatusUnderVerification": {
"message": "Under verification"
},
- "claimedDomainsDesc": {
- "message": "Claim a domain to own all member accounts whose email address matches the domain. Members will be able to skip the SSO identifier when logging in. Administrators will also be able to delete member accounts."
+ "claimedDomainsDescription": {
+ "message": "Claim a domain to own member accounts. The SSO identifier page will be skipped during login for members with claimed domains and administrators will be able to delete claimed accounts."
},
"invalidDomainNameClaimMessage": {
"message": "Input is not a valid format. Format: mydomain.com. Subdomains require separate entries to be claimed."
@@ -10896,5 +10929,56 @@
"example": "12-3456789"
}
}
+ },
+ "unpaidInvoices": {
+ "message": "Unpaid invoices"
+ },
+ "unpaidInvoicesForServiceUser": {
+ "message": "Your subscription has not been paid. Contact your provider administrator to restore service to you and your clients.",
+ "description": "A message shown in a non-dismissible dialog to service users of unpaid providers."
+ },
+ "providerSuspended": {
+ "message": "$PROVIDER$ is suspended",
+ "placeholders": {
+ "provider": {
+ "content": "$1",
+ "example": "Acme Industries"
+ }
+ }
+ },
+ "restoreProviderPortalAccessViaCustomerSupport": {
+ "message": "To restore access to your provider portal, contact Bitwarden Customer Support to renew your subscription.",
+ "description": "A message shown in a non-dismissible dialog to any user of a suspended providers."
+ },
+ "restoreProviderPortalAccessViaPaymentMethod": {
+ "message": "Your subscription has not been paid. To restore service to you and your clients, add a payment method by $CANCELLATION_DATE$.",
+ "placeholders": {
+ "cancellation_date": {
+ "content": "$1",
+ "example": "07/10/2025"
+ }
+ },
+ "description": "A message shown in a non-dismissible dialog to admins of unpaid providers."
+ },
+ "subscribetoEnterprise": {
+ "message": "Subscribe to $PLAN$",
+ "placeholders": {
+ "plan": {
+ "content": "$1",
+ "example": "Teams"
+ }
+ }
+ },
+ "subscribeEnterpriseSubtitle": {
+ "message": "Your 7-day $PLAN$ trial starts today. Add a payment method now to continue using these features after your trial ends: ",
+ "placeholders": {
+ "plan": {
+ "content": "$1",
+ "example": "Teams"
+ }
+ }
+ },
+ "unlimitedSecretsAndProjects": {
+ "message": "Unlimited secrets and projects"
}
-}
+}
\ No newline at end of file
diff --git a/apps/web/src/locales/ca/messages.json b/apps/web/src/locales/ca/messages.json
index 782ecc69b67..7a9726fb57d 100644
--- a/apps/web/src/locales/ca/messages.json
+++ b/apps/web/src/locales/ca/messages.json
@@ -568,6 +568,9 @@
"cancel": {
"message": "Cancel·la"
},
+ "later": {
+ "message": "Later"
+ },
"canceled": {
"message": "Cancel·lat"
},
@@ -626,6 +629,9 @@
"searchGroups": {
"message": "Cerca grups"
},
+ "resetSearch": {
+ "message": "Reset search"
+ },
"allItems": {
"message": "Tots els elements"
},
@@ -1090,7 +1096,7 @@
"message": "Utilitzeu un mètode d'inici de sessió diferent"
},
"logInWithPasskey": {
- "message": "Inicieu sessió amb la clau de pas"
+ "message": "Inici de sessió amb la clau de pas"
},
"useSingleSignOn": {
"message": "Usa inici de sessió únic"
@@ -1195,10 +1201,10 @@
"message": "Comença la prova"
},
"logIn": {
- "message": "Inicia sessió"
+ "message": "Inici de sessió"
},
"logInToBitwarden": {
- "message": "Inicia sessió a Bitwarden"
+ "message": "Inici de sessió a Bitwarden"
},
"enterTheCodeSentToYourEmail": {
"message": "Enter the code sent to your email"
@@ -1805,7 +1811,7 @@
"message": "Si es procedeix, es tancarà la vostra sessió actual i l'haureu de tornar a iniciar. Les sessions d'altres dispositius poden mantenir-se actives fins a una hora."
},
"changePasswordWarning": {
- "message": "After changing your password, you will need to log in with your new password. Active sessions on other devices will be logged out within one hour."
+ "message": "En canviar la teva contrasenya, cal iniciar la sessió amb la nova contrasenya. Les sessions actives en altres dispositius es tancaran en una hora."
},
"emailChanged": {
"message": "El correu electrònic s'ha guardat"
@@ -1899,7 +1905,7 @@
"message": "New device login"
},
"turnOffNewDeviceLoginProtection": {
- "message": "Turn off new device login protection"
+ "message": "Desactiva la protecció d'inici de sessió del dispositiu nou"
},
"turnOnNewDeviceLoginProtection": {
"message": "Turn on new device login protection"
@@ -2192,7 +2198,7 @@
"message": "A policy set by 1 or more organizations prevents you from importing cards to your vaults."
},
"yourSingleUseRecoveryCode": {
- "message": "Your single-use recovery code can be used to turn off two-step login in the event that you lose access to your two-step login provider. Bitwarden recommends you write down the recovery code and keep it in a safe place."
+ "message": "El vostre codi de recuperació d'un sol ús es pot utilitzar per desactivar l'inici de sessió en dos passos en cas que perdeu l'accés al vostre proveïdor d'inici de sessió en dos passos. Bitwarden us recomana que anoteu el codi de recuperació i el guardeu en un lloc segur."
},
"viewRecoveryCode": {
"message": "Mostra el codi de recuperació"
@@ -2764,7 +2770,7 @@
}
},
"premiumPriceWithFamilyPlan": {
- "message": "Hazte Premium por sólo $PRICE$ /año, u obtén cuentas Premium para usuarios $FAMILYPLANUSERCOUNT$ y uso compartido familiar ilimitado con un ",
+ "message": "Fes-te premium per només $PRICE$/any o aconsegueix comptes premium per a usuaris de $FAMILYPLANUSERCOUNT$ i compartició familiar il·limitada amb un ",
"placeholders": {
"price": {
"content": "$1",
@@ -2777,7 +2783,7 @@
}
},
"bitwardenFamiliesPlan": {
- "message": "Plan Familias Bitwarden."
+ "message": "Pla Famílies de Bitwarden."
},
"addons": {
"message": "Complements"
@@ -2968,10 +2974,10 @@
"message": "Factures"
},
"noUnpaidInvoices": {
- "message": "No unpaid invoices."
+ "message": "Sense factures impagades."
},
"noPaidInvoices": {
- "message": "No paid invoices."
+ "message": "Sense factures pagades."
},
"paid": {
"message": "Pagat",
@@ -3933,10 +3939,10 @@
"message": "Dispositiu"
},
"loginStatus": {
- "message": "Login status"
+ "message": "Estat d'accés"
},
"firstLogin": {
- "message": "First login"
+ "message": "Primer inici de sessió"
},
"trusted": {
"message": "Trusted"
@@ -4626,6 +4632,9 @@
"receiveMarketingEmailsV2": {
"message": "Get advice, announcements, and research opportunities from Bitwarden in your inbox."
},
+ "subscribe": {
+ "message": "Subscribe"
+ },
"unsubscribe": {
"message": "Anul·la subscripció"
},
@@ -6915,7 +6924,7 @@
"message": "Email generated"
},
"spinboxBoundariesHint": {
- "message": "Value must be between $MIN$ and $MAX$.",
+ "message": "El valor ha d'estar entre $MIN$ i $MAX$.",
"description": "Explains spin box minimum and maximum values to the user",
"placeholders": {
"min": {
@@ -6929,7 +6938,7 @@
}
},
"passwordLengthRecommendationHint": {
- "message": " Use $RECOMMENDED$ characters or more to generate a strong password.",
+ "message": " Utilitzeu $RECOMMENDED$ caràcters o més per generar una contrasenya segura.",
"description": "Appended to `spinboxBoundariesHint` to recommend a length to the user. This must include any language-specific 'sentence' separator characters (e.g. a space in english).",
"placeholders": {
"recommended": {
@@ -6939,7 +6948,7 @@
}
},
"passphraseNumWordsRecommendationHint": {
- "message": " Use $RECOMMENDED$ words or more to generate a strong passphrase.",
+ "message": " Utilitzeu $RECOMMENDED$ paraules o més per generar una frase de contrasenya segura.",
"description": "Appended to `spinboxBoundariesHint` to recommend a number of words to the user. This must include any language-specific 'sentence' separator characters (e.g. a space in english).",
"placeholders": {
"recommended": {
@@ -8248,7 +8257,7 @@
"message": "Actualitzeu la configuració d'encriptació per complir les noves recomanacions de seguretat i millorar la protecció del compte."
},
"kdfSettingsChangeLogoutWarning": {
- "message": "Proceeding will log you out of all active sessions. You will need to log back in and complete two-step login, if any. We recommend exporting your vault before changing your encryption settings to prevent data loss."
+ "message": "Si continueu, tancareu totes les sessions actives. Haureu de tornar a iniciar la sessió i completar l'inici de sessió en dos passos. Recomanem que exporteu la caixa forta abans de canviar la configuració de xifratge per evitar la pèrdua de dades."
},
"secretsManager": {
"message": "Administrador de secrets"
@@ -9465,6 +9474,12 @@
"deviceManagementDesc": {
"message": "Configure device management for Bitwarden using the implementation guide for your platform."
},
+ "crowdstrikeEventIntegrationDesc": {
+ "message": "Send event data to your Logscale instance"
+ },
+ "failedToSaveIntegration": {
+ "message": "Failed to save integration. Please try again later."
+ },
"deviceIdMissing": {
"message": "Device ID is missing"
},
@@ -9480,6 +9495,15 @@
"reopenLinkOnDesktop": {
"message": "Reopen this link from your email on a desktop."
},
+ "connectIntegrationButtonDesc": {
+ "message": "Connect $INTEGRATION$",
+ "placeholders": {
+ "integration": {
+ "content": "$1",
+ "example": "Crowdstrike"
+ }
+ }
+ },
"integrationCardTooltip": {
"message": "Launch $INTEGRATION$ implementation guide.",
"placeholders": {
@@ -9537,6 +9561,15 @@
"createNewClientToManageAsProvider": {
"message": "Creeu una nova organització client per gestionar com a proveïdor. Els seients addicionals es reflectiran en el proper cicle de facturació."
},
+ "url": {
+ "message": "URL"
+ },
+ "bearerToken": {
+ "message": "Bearer Token"
+ },
+ "index": {
+ "message": "Index"
+ },
"selectAPlan": {
"message": "Seleccioneu un pla"
},
@@ -9583,10 +9616,10 @@
"message": "Continue setting up your free trial of Bitwarden"
},
"continueSettingUpPasswordManager": {
- "message": "Continue setting up Bitwarden Password Manager"
+ "message": "Continua configurant el gestor de contrasenyes de Bitwarden"
},
"continueSettingUpFreeTrialPasswordManager": {
- "message": "Continue setting up your free trial of Bitwarden Password Manager"
+ "message": "Continueu configurant la prova gratuïta del Gestor de contrasenyes de Bitwarden"
},
"continueSettingUpSecretsManager": {
"message": "Continue setting up Bitwarden Secrets Manager"
@@ -9614,7 +9647,7 @@
}
},
"backTo": {
- "message": "Back to $NAME$",
+ "message": "Torna a $NAME$",
"description": "Navigate back to a previous folder or collection",
"placeholders": {
"name": {
@@ -9624,7 +9657,7 @@
}
},
"back": {
- "message": "Back",
+ "message": "Arrere",
"description": "Button text to navigate back"
},
"removeItem": {
@@ -9674,16 +9707,16 @@
}
},
"lowKDFIterationsBanner": {
- "message": "Low KDF iterations. Increase your iterations to improve the security of your account."
+ "message": "Iteracions baixes de KDF. Augmenta les iteracions per millorar la seguretat del teu compte."
},
"changeKDFSettings": {
- "message": "Change KDF settings"
+ "message": "Canvia la configuració de KDF"
},
"secureYourInfrastructure": {
- "message": "Secure your infrastructure"
+ "message": "Assegureu la vostra infraestructura"
},
"protectYourFamilyOrBusiness": {
- "message": "Protect your family or business"
+ "message": "Protegeix la família o el negoci"
},
"upgradeOrganizationCloseSecurityGaps": {
"message": "Close security gaps with monitoring reports"
@@ -9777,7 +9810,7 @@
"message": "Off"
},
"higherKDFIterations": {
- "message": "Higher KDF iterations can help protect your master password from being brute forced by an attacker."
+ "message": "Les iteracions mes altes de KDF poden ajudar a protegir la contrasenya mestra de ser forçada per un atacant."
},
"incrementsOf100,000": {
"message": "increments of 100,000"
@@ -9786,10 +9819,10 @@
"message": "small increments"
},
"kdfIterationRecommends": {
- "message": "We recommend 600,000 or more"
+ "message": "Recomanem 600.000 o més"
},
"kdfToHighWarningIncreaseInIncrements": {
- "message": "For older devices, setting your KDF too high may lead to performance issues. Increase the value in $VALUE$ and test your devices.",
+ "message": "Per a dispositius més antics, configurar el KDF a un valor massa alt pot provocar problemes de rendiment. Augmenteu el valor de $VALUE$ i proveu els dispositius.",
"placeholders": {
"value": {
"content": "$1",
@@ -9941,7 +9974,7 @@
"message": "Config"
},
"learnMoreAboutEmergencyAccess": {
- "message": "Learn more about emergency access"
+ "message": "Més informació sobre l'accés d'emergència"
},
"learnMoreAboutMatchDetection": {
"message": "Learn more about match detection"
@@ -10160,7 +10193,7 @@
"message": "Your Secrets Manager subscription will upgrade based on the plan selected"
},
"bitwardenPasswordManager": {
- "message": "Bitwarden Password Manager"
+ "message": "Gestor de contrasenyes Bitwarden"
},
"secretsManagerComplimentaryPasswordManager": {
"message": "Your complimentary one year Password Manager subscription will upgrade to the selected plan. You will not be charged until the complimentary period is over."
@@ -10285,8 +10318,8 @@
"organizationUserDeletedDesc": {
"message": "The user was removed from the organization and all associated user data has been deleted."
},
- "deletedUserId": {
- "message": "Deleted user $ID$ - an owner / admin deleted the user account",
+ "deletedUserIdEventMessage": {
+ "message": "Deleted user $ID$",
"placeholders": {
"id": {
"content": "$1",
@@ -10355,13 +10388,13 @@
"message": "Remove members"
},
"devices": {
- "message": "Devices"
+ "message": "Dispositius"
},
"deviceListDescription": {
- "message": "Your account was logged in to each of the devices below. If you do not recognize a device, remove it now."
+ "message": "Heu iniciat la sessió al vostre compte en cadascun dels dispositius següents. Si no en reconeixeu algun, suprimiu-lo ara."
},
"deviceListDescriptionTemp": {
- "message": "Your account was logged in to each of the devices below."
+ "message": "Heu iniciat la sessió al vostre compte en cadascun dels dispositius següents."
},
"claimedDomains": {
"message": "Claimed domains"
@@ -10396,8 +10429,8 @@
"domainStatusUnderVerification": {
"message": "Under verification"
},
- "claimedDomainsDesc": {
- "message": "Claim a domain to own all member accounts whose email address matches the domain. Members will be able to skip the SSO identifier when logging in. Administrators will also be able to delete member accounts."
+ "claimedDomainsDescription": {
+ "message": "Claim a domain to own member accounts. The SSO identifier page will be skipped during login for members with claimed domains and administrators will be able to delete claimed accounts."
},
"invalidDomainNameClaimMessage": {
"message": "Input is not a valid format. Format: mydomain.com. Subdomains require separate entries to be claimed."
@@ -10709,7 +10742,7 @@
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
},
"sendsBodyNoItems": {
- "message": "Share files and data securely with anyone, on any platform. Your information will remain end-to-end encrypted while limiting exposure.",
+ "message": "Comparteix fitxers i dades de manera segura amb qualsevol persona, en qualsevol plataforma. La teua informació romandrà xifrada de principi a fi i alhora limitarà l'exposició.",
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
},
"generatorNudgeTitle": {
@@ -10896,5 +10929,56 @@
"example": "12-3456789"
}
}
+ },
+ "unpaidInvoices": {
+ "message": "Unpaid invoices"
+ },
+ "unpaidInvoicesForServiceUser": {
+ "message": "Your subscription has not been paid. Contact your provider administrator to restore service to you and your clients.",
+ "description": "A message shown in a non-dismissible dialog to service users of unpaid providers."
+ },
+ "providerSuspended": {
+ "message": "$PROVIDER$ is suspended",
+ "placeholders": {
+ "provider": {
+ "content": "$1",
+ "example": "Acme Industries"
+ }
+ }
+ },
+ "restoreProviderPortalAccessViaCustomerSupport": {
+ "message": "To restore access to your provider portal, contact Bitwarden Customer Support to renew your subscription.",
+ "description": "A message shown in a non-dismissible dialog to any user of a suspended providers."
+ },
+ "restoreProviderPortalAccessViaPaymentMethod": {
+ "message": "Your subscription has not been paid. To restore service to you and your clients, add a payment method by $CANCELLATION_DATE$.",
+ "placeholders": {
+ "cancellation_date": {
+ "content": "$1",
+ "example": "07/10/2025"
+ }
+ },
+ "description": "A message shown in a non-dismissible dialog to admins of unpaid providers."
+ },
+ "subscribetoEnterprise": {
+ "message": "Subscribe to $PLAN$",
+ "placeholders": {
+ "plan": {
+ "content": "$1",
+ "example": "Teams"
+ }
+ }
+ },
+ "subscribeEnterpriseSubtitle": {
+ "message": "Your 7-day $PLAN$ trial starts today. Add a payment method now to continue using these features after your trial ends: ",
+ "placeholders": {
+ "plan": {
+ "content": "$1",
+ "example": "Teams"
+ }
+ }
+ },
+ "unlimitedSecretsAndProjects": {
+ "message": "Unlimited secrets and projects"
}
-}
+}
\ No newline at end of file
diff --git a/apps/web/src/locales/cs/messages.json b/apps/web/src/locales/cs/messages.json
index 8569b953fb7..0431703599d 100644
--- a/apps/web/src/locales/cs/messages.json
+++ b/apps/web/src/locales/cs/messages.json
@@ -568,6 +568,9 @@
"cancel": {
"message": "Zrušit"
},
+ "later": {
+ "message": "Později"
+ },
"canceled": {
"message": "Zrušeno"
},
@@ -626,6 +629,9 @@
"searchGroups": {
"message": "Prohledat skupiny"
},
+ "resetSearch": {
+ "message": "Resetovat hledání"
+ },
"allItems": {
"message": "Všechny položky"
},
@@ -4626,6 +4632,9 @@
"receiveMarketingEmailsV2": {
"message": "Dostávejte do své e-mailové schránky rady, oznámení a příležitosti k výzkumu od společnosti Bitwarden."
},
+ "subscribe": {
+ "message": "Předplatné"
+ },
"unsubscribe": {
"message": "Odhlásit odběr"
},
@@ -9465,6 +9474,12 @@
"deviceManagementDesc": {
"message": "Nastaví správu zařízení pro Bitwarden pomocí implementačního průvodce pro Vaši platformu."
},
+ "crowdstrikeEventIntegrationDesc": {
+ "message": "Odeslat data události do Vaší instance Logscale"
+ },
+ "failedToSaveIntegration": {
+ "message": "Nepodařilo se uložit integraci. Opakujte akci později."
+ },
"deviceIdMissing": {
"message": "Chybí ID zařízení"
},
@@ -9480,6 +9495,15 @@
"reopenLinkOnDesktop": {
"message": "Znovu otevřete tento odkaz z Vašeho e-mailu na počítači."
},
+ "connectIntegrationButtonDesc": {
+ "message": "Připojit $INTEGRATION$",
+ "placeholders": {
+ "integration": {
+ "content": "$1",
+ "example": "Crowdstrike"
+ }
+ }
+ },
"integrationCardTooltip": {
"message": "Spustí průvodce implementací $INTEGRATION$.",
"placeholders": {
@@ -9537,6 +9561,15 @@
"createNewClientToManageAsProvider": {
"message": "Vytvořte novou klientskou organizaci pro správu jako poskytovatele. Další uživatelé budou reflektováni v dalším platebním cyklu."
},
+ "url": {
+ "message": "URL"
+ },
+ "bearerToken": {
+ "message": "Token na doručitele"
+ },
+ "index": {
+ "message": "Index"
+ },
"selectAPlan": {
"message": "Vyberte plán"
},
@@ -10285,8 +10318,8 @@
"organizationUserDeletedDesc": {
"message": "Uživatel byl odebrán z organizace a všechna přidružená uživatelská data byla smazána."
},
- "deletedUserId": {
- "message": "Smazaný uživatel $ID$ - vlastník / správce smazal uživatelský účet",
+ "deletedUserIdEventMessage": {
+ "message": "Smazaný uživatel $ID$",
"placeholders": {
"id": {
"content": "$1",
@@ -10396,8 +10429,8 @@
"domainStatusUnderVerification": {
"message": "V ověřování"
},
- "claimedDomainsDesc": {
- "message": "Uplatněním domény získáte všechny členské účty, jejichž e-mailová adresa se shoduje s doménou. Členové budou moci při přihlašování přeskočit identifikátor SSO. Správci budou moci členské účty také mazat."
+ "claimedDomainsDescription": {
+ "message": "Požádejte o doménu, abyste mohli vlastnit členské účty. Stránka s identifikátorem SSO bude při přihlašování členů s deklarovanými doménami přeskočena a správci budou moci deklarované účty smazat."
},
"invalidDomainNameClaimMessage": {
"message": "Vstup není platný formát. Příklad: mojedoména.cz.Poddomény vyžadují samostatné záznamy, které je třeba uplatnit."
@@ -10896,5 +10929,56 @@
"example": "12-3456789"
}
}
+ },
+ "unpaidInvoices": {
+ "message": "Nezaplacené faktury"
+ },
+ "unpaidInvoicesForServiceUser": {
+ "message": "Vaše předplatné nebylo zaplaceno. Obraťte se na správce poskytovatele, abyste obnovili službu pro Vás a Vaše klienty.",
+ "description": "A message shown in a non-dismissible dialog to service users of unpaid providers."
+ },
+ "providerSuspended": {
+ "message": "$PROVIDER$ je pozastaven",
+ "placeholders": {
+ "provider": {
+ "content": "$1",
+ "example": "Acme Industries"
+ }
+ }
+ },
+ "restoreProviderPortalAccessViaCustomerSupport": {
+ "message": "Chcete-li obnovit přístup k portálu poskytovatele, obraťte se na zákaznickou podporu Bitwarden pro obnovení předplatného.",
+ "description": "A message shown in a non-dismissible dialog to any user of a suspended providers."
+ },
+ "restoreProviderPortalAccessViaPaymentMethod": {
+ "message": "Vaše předplatné nebylo zaplaceno. Chcete-li obnovit službu Vám a Vašim klientům, přidejte platební metodu do $CANCELLATION_DATE$.",
+ "placeholders": {
+ "cancellation_date": {
+ "content": "$1",
+ "example": "07/10/2025"
+ }
+ },
+ "description": "A message shown in a non-dismissible dialog to admins of unpaid providers."
+ },
+ "subscribetoEnterprise": {
+ "message": "Předplatné $PLAN$",
+ "placeholders": {
+ "plan": {
+ "content": "$1",
+ "example": "Teams"
+ }
+ }
+ },
+ "subscribeEnterpriseSubtitle": {
+ "message": "Vaše 7denní zkušební verze $PLAN$ začíná dnes. Přidejte platební metodu nyní, abyste mohli pokračovat v používání těchto funkcí po skončení zkušební verze: ",
+ "placeholders": {
+ "plan": {
+ "content": "$1",
+ "example": "Teams"
+ }
+ }
+ },
+ "unlimitedSecretsAndProjects": {
+ "message": "Neomezené tajné klíče a projekty"
}
-}
+}
\ No newline at end of file
diff --git a/apps/web/src/locales/cy/messages.json b/apps/web/src/locales/cy/messages.json
index 4089f11392c..0dc3f6db8c6 100644
--- a/apps/web/src/locales/cy/messages.json
+++ b/apps/web/src/locales/cy/messages.json
@@ -568,6 +568,9 @@
"cancel": {
"message": "Cancel"
},
+ "later": {
+ "message": "Later"
+ },
"canceled": {
"message": "Canceled"
},
@@ -626,6 +629,9 @@
"searchGroups": {
"message": "Search groups"
},
+ "resetSearch": {
+ "message": "Reset search"
+ },
"allItems": {
"message": "All items"
},
@@ -4626,6 +4632,9 @@
"receiveMarketingEmailsV2": {
"message": "Get advice, announcements, and research opportunities from Bitwarden in your inbox."
},
+ "subscribe": {
+ "message": "Subscribe"
+ },
"unsubscribe": {
"message": "Unsubscribe"
},
@@ -9465,6 +9474,12 @@
"deviceManagementDesc": {
"message": "Configure device management for Bitwarden using the implementation guide for your platform."
},
+ "crowdstrikeEventIntegrationDesc": {
+ "message": "Send event data to your Logscale instance"
+ },
+ "failedToSaveIntegration": {
+ "message": "Failed to save integration. Please try again later."
+ },
"deviceIdMissing": {
"message": "Device ID is missing"
},
@@ -9480,6 +9495,15 @@
"reopenLinkOnDesktop": {
"message": "Reopen this link from your email on a desktop."
},
+ "connectIntegrationButtonDesc": {
+ "message": "Connect $INTEGRATION$",
+ "placeholders": {
+ "integration": {
+ "content": "$1",
+ "example": "Crowdstrike"
+ }
+ }
+ },
"integrationCardTooltip": {
"message": "Launch $INTEGRATION$ implementation guide.",
"placeholders": {
@@ -9537,6 +9561,15 @@
"createNewClientToManageAsProvider": {
"message": "Create a new client organization to manage as a Provider. Additional seats will be reflected in the next billing cycle."
},
+ "url": {
+ "message": "URL"
+ },
+ "bearerToken": {
+ "message": "Bearer Token"
+ },
+ "index": {
+ "message": "Index"
+ },
"selectAPlan": {
"message": "Select a plan"
},
@@ -10285,8 +10318,8 @@
"organizationUserDeletedDesc": {
"message": "The user was removed from the organization and all associated user data has been deleted."
},
- "deletedUserId": {
- "message": "Deleted user $ID$ - an owner / admin deleted the user account",
+ "deletedUserIdEventMessage": {
+ "message": "Deleted user $ID$",
"placeholders": {
"id": {
"content": "$1",
@@ -10396,8 +10429,8 @@
"domainStatusUnderVerification": {
"message": "Under verification"
},
- "claimedDomainsDesc": {
- "message": "Claim a domain to own all member accounts whose email address matches the domain. Members will be able to skip the SSO identifier when logging in. Administrators will also be able to delete member accounts."
+ "claimedDomainsDescription": {
+ "message": "Claim a domain to own member accounts. The SSO identifier page will be skipped during login for members with claimed domains and administrators will be able to delete claimed accounts."
},
"invalidDomainNameClaimMessage": {
"message": "Input is not a valid format. Format: mydomain.com. Subdomains require separate entries to be claimed."
@@ -10896,5 +10929,56 @@
"example": "12-3456789"
}
}
+ },
+ "unpaidInvoices": {
+ "message": "Unpaid invoices"
+ },
+ "unpaidInvoicesForServiceUser": {
+ "message": "Your subscription has not been paid. Contact your provider administrator to restore service to you and your clients.",
+ "description": "A message shown in a non-dismissible dialog to service users of unpaid providers."
+ },
+ "providerSuspended": {
+ "message": "$PROVIDER$ is suspended",
+ "placeholders": {
+ "provider": {
+ "content": "$1",
+ "example": "Acme Industries"
+ }
+ }
+ },
+ "restoreProviderPortalAccessViaCustomerSupport": {
+ "message": "To restore access to your provider portal, contact Bitwarden Customer Support to renew your subscription.",
+ "description": "A message shown in a non-dismissible dialog to any user of a suspended providers."
+ },
+ "restoreProviderPortalAccessViaPaymentMethod": {
+ "message": "Your subscription has not been paid. To restore service to you and your clients, add a payment method by $CANCELLATION_DATE$.",
+ "placeholders": {
+ "cancellation_date": {
+ "content": "$1",
+ "example": "07/10/2025"
+ }
+ },
+ "description": "A message shown in a non-dismissible dialog to admins of unpaid providers."
+ },
+ "subscribetoEnterprise": {
+ "message": "Subscribe to $PLAN$",
+ "placeholders": {
+ "plan": {
+ "content": "$1",
+ "example": "Teams"
+ }
+ }
+ },
+ "subscribeEnterpriseSubtitle": {
+ "message": "Your 7-day $PLAN$ trial starts today. Add a payment method now to continue using these features after your trial ends: ",
+ "placeholders": {
+ "plan": {
+ "content": "$1",
+ "example": "Teams"
+ }
+ }
+ },
+ "unlimitedSecretsAndProjects": {
+ "message": "Unlimited secrets and projects"
}
-}
+}
\ No newline at end of file
diff --git a/apps/web/src/locales/da/messages.json b/apps/web/src/locales/da/messages.json
index 1d2ebcaca0f..b5ee42479af 100644
--- a/apps/web/src/locales/da/messages.json
+++ b/apps/web/src/locales/da/messages.json
@@ -568,6 +568,9 @@
"cancel": {
"message": "Annullér"
},
+ "later": {
+ "message": "Later"
+ },
"canceled": {
"message": "Annulleret"
},
@@ -626,6 +629,9 @@
"searchGroups": {
"message": "Søg grupper"
},
+ "resetSearch": {
+ "message": "Reset search"
+ },
"allItems": {
"message": "Alle emner"
},
@@ -4626,6 +4632,9 @@
"receiveMarketingEmailsV2": {
"message": "Få råd, bekendtgørelser og forskningsmuligheder fra Bitwarden i indbakken."
},
+ "subscribe": {
+ "message": "Subscribe"
+ },
"unsubscribe": {
"message": "Afmeld"
},
@@ -9465,6 +9474,12 @@
"deviceManagementDesc": {
"message": "Opsæt enhedshåndtering for Bitwarden vha. implementeringsvejledningen for den aktuelle platform."
},
+ "crowdstrikeEventIntegrationDesc": {
+ "message": "Send event data to your Logscale instance"
+ },
+ "failedToSaveIntegration": {
+ "message": "Failed to save integration. Please try again later."
+ },
"deviceIdMissing": {
"message": "Device ID is missing"
},
@@ -9480,6 +9495,15 @@
"reopenLinkOnDesktop": {
"message": "Reopen this link from your email on a desktop."
},
+ "connectIntegrationButtonDesc": {
+ "message": "Connect $INTEGRATION$",
+ "placeholders": {
+ "integration": {
+ "content": "$1",
+ "example": "Crowdstrike"
+ }
+ }
+ },
"integrationCardTooltip": {
"message": "Start $INTEGRATION$-implementeringsguiden.",
"placeholders": {
@@ -9537,6 +9561,15 @@
"createNewClientToManageAsProvider": {
"message": "Opret en ny kundeorganisation til at håndtere som udbyder. Yderligere pladser afspejles i næste faktureringscyklus."
},
+ "url": {
+ "message": "URL"
+ },
+ "bearerToken": {
+ "message": "Bearer Token"
+ },
+ "index": {
+ "message": "Index"
+ },
"selectAPlan": {
"message": "Vælg en abonnementstype"
},
@@ -10285,8 +10318,8 @@
"organizationUserDeletedDesc": {
"message": "Brugeren er fjernet fra organisationen, og alle tilknyttede brugerdata er slettet."
},
- "deletedUserId": {
- "message": "Slettet bruger $ID$ - en ejer/admin har slettet brugerkontoen",
+ "deletedUserIdEventMessage": {
+ "message": "Deleted user $ID$",
"placeholders": {
"id": {
"content": "$1",
@@ -10396,8 +10429,8 @@
"domainStatusUnderVerification": {
"message": "Under verifikation"
},
- "claimedDomainsDesc": {
- "message": "Registrér et domæne for at eje alle medlemskonti, hvis e-mailadresse matcher domænet. Medlemmer vil kunne overspringe SSO-identifikatoren under indlogning. Administratorer vil også kunne slette medlemskonti."
+ "claimedDomainsDescription": {
+ "message": "Claim a domain to own member accounts. The SSO identifier page will be skipped during login for members with claimed domains and administrators will be able to delete claimed accounts."
},
"invalidDomainNameClaimMessage": {
"message": "Input er ikke i et gyldigt format. Format: mitdomaene.dk. Underdomæner kræver, at separate poster registreres."
@@ -10896,5 +10929,56 @@
"example": "12-3456789"
}
}
+ },
+ "unpaidInvoices": {
+ "message": "Unpaid invoices"
+ },
+ "unpaidInvoicesForServiceUser": {
+ "message": "Your subscription has not been paid. Contact your provider administrator to restore service to you and your clients.",
+ "description": "A message shown in a non-dismissible dialog to service users of unpaid providers."
+ },
+ "providerSuspended": {
+ "message": "$PROVIDER$ is suspended",
+ "placeholders": {
+ "provider": {
+ "content": "$1",
+ "example": "Acme Industries"
+ }
+ }
+ },
+ "restoreProviderPortalAccessViaCustomerSupport": {
+ "message": "To restore access to your provider portal, contact Bitwarden Customer Support to renew your subscription.",
+ "description": "A message shown in a non-dismissible dialog to any user of a suspended providers."
+ },
+ "restoreProviderPortalAccessViaPaymentMethod": {
+ "message": "Your subscription has not been paid. To restore service to you and your clients, add a payment method by $CANCELLATION_DATE$.",
+ "placeholders": {
+ "cancellation_date": {
+ "content": "$1",
+ "example": "07/10/2025"
+ }
+ },
+ "description": "A message shown in a non-dismissible dialog to admins of unpaid providers."
+ },
+ "subscribetoEnterprise": {
+ "message": "Subscribe to $PLAN$",
+ "placeholders": {
+ "plan": {
+ "content": "$1",
+ "example": "Teams"
+ }
+ }
+ },
+ "subscribeEnterpriseSubtitle": {
+ "message": "Your 7-day $PLAN$ trial starts today. Add a payment method now to continue using these features after your trial ends: ",
+ "placeholders": {
+ "plan": {
+ "content": "$1",
+ "example": "Teams"
+ }
+ }
+ },
+ "unlimitedSecretsAndProjects": {
+ "message": "Unlimited secrets and projects"
}
-}
+}
\ No newline at end of file
diff --git a/apps/web/src/locales/de/messages.json b/apps/web/src/locales/de/messages.json
index 6c94a17f36c..004dad19ab3 100644
--- a/apps/web/src/locales/de/messages.json
+++ b/apps/web/src/locales/de/messages.json
@@ -568,6 +568,9 @@
"cancel": {
"message": "Abbrechen"
},
+ "later": {
+ "message": "Später"
+ },
"canceled": {
"message": "Abgebrochen"
},
@@ -626,6 +629,9 @@
"searchGroups": {
"message": "Gruppen durchsuchen"
},
+ "resetSearch": {
+ "message": "Suche zurücksetzen"
+ },
"allItems": {
"message": "Alle Einträge"
},
@@ -4626,6 +4632,9 @@
"receiveMarketingEmailsV2": {
"message": "Erhalte Ratschläge, Ankündigungen und Marktforschungsumfragen von Bitwarden in deinem Posteingang."
},
+ "subscribe": {
+ "message": "Subscribe"
+ },
"unsubscribe": {
"message": "Deabonnieren"
},
@@ -9465,6 +9474,12 @@
"deviceManagementDesc": {
"message": "Konfiguriere die Geräteverwaltung für Bitwarden mithilfe der Implementierungsanleitung für deine Plattform."
},
+ "crowdstrikeEventIntegrationDesc": {
+ "message": "Send event data to your Logscale instance"
+ },
+ "failedToSaveIntegration": {
+ "message": "Failed to save integration. Please try again later."
+ },
"deviceIdMissing": {
"message": "Geräte-ID fehlt"
},
@@ -9480,6 +9495,15 @@
"reopenLinkOnDesktop": {
"message": "Öffne diesen Link aus deiner E-Mail auf einem Desktop-Rechner."
},
+ "connectIntegrationButtonDesc": {
+ "message": "Connect $INTEGRATION$",
+ "placeholders": {
+ "integration": {
+ "content": "$1",
+ "example": "Crowdstrike"
+ }
+ }
+ },
"integrationCardTooltip": {
"message": "$INTEGRATION$-Implementierungsanleitung starten.",
"placeholders": {
@@ -9537,6 +9561,15 @@
"createNewClientToManageAsProvider": {
"message": "Erstelle eine neue Kunden-Organisation, um sie als Anbieter zu verwalten. Zusätzliche Benutzerplätze werden im nächsten Abrechnungszeitraum berücksichtigt."
},
+ "url": {
+ "message": "URL"
+ },
+ "bearerToken": {
+ "message": "Bearer Token"
+ },
+ "index": {
+ "message": "Index"
+ },
"selectAPlan": {
"message": "Ein Abo auswählen"
},
@@ -10285,8 +10318,8 @@
"organizationUserDeletedDesc": {
"message": "Der Benutzer wurde aus der Organisation entfernt und alle zugehörigen Benutzerdaten wurden gelöscht."
},
- "deletedUserId": {
- "message": "Gelöschter Benutzer $ID$ - ein Eigentümer / Administrator hat das Benutzerkonto gelöscht",
+ "deletedUserIdEventMessage": {
+ "message": "Deleted user $ID$",
"placeholders": {
"id": {
"content": "$1",
@@ -10396,8 +10429,8 @@
"domainStatusUnderVerification": {
"message": "In Verifizierung"
},
- "claimedDomainsDesc": {
- "message": "Beanspruche eine Domain um alle Mitgliedskonten zu besitzen, deren E-Mail-Adresse mit der Domain übereinstimmt. Mitglieder können beim Anmelden die SSO-Kennung überspringen. Administratoren können auch Mitgliedskonten löschen."
+ "claimedDomainsDescription": {
+ "message": "Claim a domain to own member accounts. The SSO identifier page will be skipped during login for members with claimed domains and administrators will be able to delete claimed accounts."
},
"invalidDomainNameClaimMessage": {
"message": "Die Eingabe ist kein gültiges Format. Format: mydomain.com. Subdomains erfordern separate Einträge, um beansprucht zu werden."
@@ -10896,5 +10929,56 @@
"example": "12-3456789"
}
}
+ },
+ "unpaidInvoices": {
+ "message": "Unpaid invoices"
+ },
+ "unpaidInvoicesForServiceUser": {
+ "message": "Your subscription has not been paid. Contact your provider administrator to restore service to you and your clients.",
+ "description": "A message shown in a non-dismissible dialog to service users of unpaid providers."
+ },
+ "providerSuspended": {
+ "message": "$PROVIDER$ is suspended",
+ "placeholders": {
+ "provider": {
+ "content": "$1",
+ "example": "Acme Industries"
+ }
+ }
+ },
+ "restoreProviderPortalAccessViaCustomerSupport": {
+ "message": "To restore access to your provider portal, contact Bitwarden Customer Support to renew your subscription.",
+ "description": "A message shown in a non-dismissible dialog to any user of a suspended providers."
+ },
+ "restoreProviderPortalAccessViaPaymentMethod": {
+ "message": "Your subscription has not been paid. To restore service to you and your clients, add a payment method by $CANCELLATION_DATE$.",
+ "placeholders": {
+ "cancellation_date": {
+ "content": "$1",
+ "example": "07/10/2025"
+ }
+ },
+ "description": "A message shown in a non-dismissible dialog to admins of unpaid providers."
+ },
+ "subscribetoEnterprise": {
+ "message": "Subscribe to $PLAN$",
+ "placeholders": {
+ "plan": {
+ "content": "$1",
+ "example": "Teams"
+ }
+ }
+ },
+ "subscribeEnterpriseSubtitle": {
+ "message": "Your 7-day $PLAN$ trial starts today. Add a payment method now to continue using these features after your trial ends: ",
+ "placeholders": {
+ "plan": {
+ "content": "$1",
+ "example": "Teams"
+ }
+ }
+ },
+ "unlimitedSecretsAndProjects": {
+ "message": "Unlimited secrets and projects"
}
-}
+}
\ No newline at end of file
diff --git a/apps/web/src/locales/el/messages.json b/apps/web/src/locales/el/messages.json
index 21f13a3440f..7cee4a5e65b 100644
--- a/apps/web/src/locales/el/messages.json
+++ b/apps/web/src/locales/el/messages.json
@@ -568,6 +568,9 @@
"cancel": {
"message": "Ακύρωση"
},
+ "later": {
+ "message": "Later"
+ },
"canceled": {
"message": "Ακυρώθηκε"
},
@@ -626,6 +629,9 @@
"searchGroups": {
"message": "Αναζήτηση ομάδων"
},
+ "resetSearch": {
+ "message": "Reset search"
+ },
"allItems": {
"message": "Όλα τα στοιχεία"
},
@@ -4626,6 +4632,9 @@
"receiveMarketingEmailsV2": {
"message": "Get advice, announcements, and research opportunities from Bitwarden in your inbox."
},
+ "subscribe": {
+ "message": "Subscribe"
+ },
"unsubscribe": {
"message": "Ακύρωση συνδρομής"
},
@@ -9465,6 +9474,12 @@
"deviceManagementDesc": {
"message": "Configure device management for Bitwarden using the implementation guide for your platform."
},
+ "crowdstrikeEventIntegrationDesc": {
+ "message": "Send event data to your Logscale instance"
+ },
+ "failedToSaveIntegration": {
+ "message": "Failed to save integration. Please try again later."
+ },
"deviceIdMissing": {
"message": "Device ID is missing"
},
@@ -9480,6 +9495,15 @@
"reopenLinkOnDesktop": {
"message": "Reopen this link from your email on a desktop."
},
+ "connectIntegrationButtonDesc": {
+ "message": "Connect $INTEGRATION$",
+ "placeholders": {
+ "integration": {
+ "content": "$1",
+ "example": "Crowdstrike"
+ }
+ }
+ },
"integrationCardTooltip": {
"message": "Launch $INTEGRATION$ implementation guide.",
"placeholders": {
@@ -9537,6 +9561,15 @@
"createNewClientToManageAsProvider": {
"message": "Create a new client organization to manage as a Provider. Additional seats will be reflected in the next billing cycle."
},
+ "url": {
+ "message": "URL"
+ },
+ "bearerToken": {
+ "message": "Bearer Token"
+ },
+ "index": {
+ "message": "Index"
+ },
"selectAPlan": {
"message": "Επιλογή προγράμματος"
},
@@ -10285,8 +10318,8 @@
"organizationUserDeletedDesc": {
"message": "The user was removed from the organization and all associated user data has been deleted."
},
- "deletedUserId": {
- "message": "Deleted user $ID$ - an owner / admin deleted the user account",
+ "deletedUserIdEventMessage": {
+ "message": "Deleted user $ID$",
"placeholders": {
"id": {
"content": "$1",
@@ -10396,8 +10429,8 @@
"domainStatusUnderVerification": {
"message": "Under verification"
},
- "claimedDomainsDesc": {
- "message": "Claim a domain to own all member accounts whose email address matches the domain. Members will be able to skip the SSO identifier when logging in. Administrators will also be able to delete member accounts."
+ "claimedDomainsDescription": {
+ "message": "Claim a domain to own member accounts. The SSO identifier page will be skipped during login for members with claimed domains and administrators will be able to delete claimed accounts."
},
"invalidDomainNameClaimMessage": {
"message": "Input is not a valid format. Format: mydomain.com. Subdomains require separate entries to be claimed."
@@ -10896,5 +10929,56 @@
"example": "12-3456789"
}
}
+ },
+ "unpaidInvoices": {
+ "message": "Unpaid invoices"
+ },
+ "unpaidInvoicesForServiceUser": {
+ "message": "Your subscription has not been paid. Contact your provider administrator to restore service to you and your clients.",
+ "description": "A message shown in a non-dismissible dialog to service users of unpaid providers."
+ },
+ "providerSuspended": {
+ "message": "$PROVIDER$ is suspended",
+ "placeholders": {
+ "provider": {
+ "content": "$1",
+ "example": "Acme Industries"
+ }
+ }
+ },
+ "restoreProviderPortalAccessViaCustomerSupport": {
+ "message": "To restore access to your provider portal, contact Bitwarden Customer Support to renew your subscription.",
+ "description": "A message shown in a non-dismissible dialog to any user of a suspended providers."
+ },
+ "restoreProviderPortalAccessViaPaymentMethod": {
+ "message": "Your subscription has not been paid. To restore service to you and your clients, add a payment method by $CANCELLATION_DATE$.",
+ "placeholders": {
+ "cancellation_date": {
+ "content": "$1",
+ "example": "07/10/2025"
+ }
+ },
+ "description": "A message shown in a non-dismissible dialog to admins of unpaid providers."
+ },
+ "subscribetoEnterprise": {
+ "message": "Subscribe to $PLAN$",
+ "placeholders": {
+ "plan": {
+ "content": "$1",
+ "example": "Teams"
+ }
+ }
+ },
+ "subscribeEnterpriseSubtitle": {
+ "message": "Your 7-day $PLAN$ trial starts today. Add a payment method now to continue using these features after your trial ends: ",
+ "placeholders": {
+ "plan": {
+ "content": "$1",
+ "example": "Teams"
+ }
+ }
+ },
+ "unlimitedSecretsAndProjects": {
+ "message": "Unlimited secrets and projects"
}
-}
+}
\ No newline at end of file
diff --git a/apps/web/src/locales/en/messages.json b/apps/web/src/locales/en/messages.json
index b143d4da56a..587dcd84e0c 100644
--- a/apps/web/src/locales/en/messages.json
+++ b/apps/web/src/locales/en/messages.json
@@ -1428,9 +1428,6 @@
"notificationSentDevicePart1": {
"message": "Unlock Bitwarden on your device or on the "
},
- "areYouTryingToAccessYourAccount": {
- "message": "Are you trying to access your account?"
- },
"accessAttemptBy": {
"message": "Access attempt by $EMAIL$",
"placeholders": {
@@ -3981,22 +3978,6 @@
"thisRequestIsNoLongerValid": {
"message": "This request is no longer valid."
},
- "logInConfirmedForEmailOnDevice": {
- "message": "Login confirmed for $EMAIL$ on $DEVICE$",
- "placeholders": {
- "email": {
- "content": "$1",
- "example": "name@example.com"
- },
- "device": {
- "content": "$2",
- "example": "iOS"
- }
- }
- },
- "youDeniedALogInAttemptFromAnotherDevice": {
- "message": "You denied a login attempt from another device. If this really was you, try to log in with the device again."
- },
"loginRequestApprovedForEmailOnDevice": {
"message": "Login request approved for $EMAIL$ on $DEVICE$",
"placeholders": {
@@ -5448,6 +5429,37 @@
"organizationDataOwnership": {
"message": "Enforce organization data ownership"
},
+ "organizationDataOwnershipDesc": {
+ "message": "Require all items to be owned by an organization, removing the option to store items at the account level.",
+ "description": "This is the policy description shown in the policy list."
+ },
+ "organizationDataOwnershipContent": {
+ "message": "All items will be owned and saved to the organization, enabling organization-wide controls, visibility, and reporting. When turned on, a default collection be available for each member to store items. Learn more about managing the ",
+ "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'All items will be owned and saved to the organization, enabling organization-wide controls, visibility, and reporting. When turned on, a default collection be available for each member to store items. Learn more about managing the credential lifecycle.'"
+ },
+ "organizationDataOwnershipContentAnchor":{
+ "message": "credential lifecycle",
+ "description": "This will be used as a hyperlink"
+ },
+ "organizationDataOwnershipWarningTitle":{
+ "message": "Are you sure you want to proceed?"
+ },
+ "organizationDataOwnershipWarning1":{
+ "message": "will remain accessible to members"
+ },
+ "organizationDataOwnershipWarning2":{
+ "message": "will not be automatically selected when creating new items"
+ },
+ "organizationDataOwnershipWarning3":{
+ "message": "cannot be managed from the Admin Console until the user is offboarded"
+ },
+ "organizationDataOwnershipWarningContentTop":{
+ "message": "By turning this policy off, the default collection: "
+ },
+ "organizationDataOwnershipWarningContentBottom":{
+ "message": "Learn more about the ",
+ "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Learn more about the credential lifecycle.'"
+ },
"personalOwnership": {
"message": "Remove individual vault"
},
@@ -9478,6 +9490,12 @@
"deviceManagementDesc": {
"message": "Configure device management for Bitwarden using the implementation guide for your platform."
},
+ "crowdstrikeEventIntegrationDesc": {
+ "message": "Send event data to your Logscale instance"
+ },
+ "failedToSaveIntegration": {
+ "message": "Failed to save integration. Please try again later."
+ },
"deviceIdMissing": {
"message": "Device ID is missing"
},
@@ -9493,6 +9511,15 @@
"reopenLinkOnDesktop": {
"message": "Reopen this link from your email on a desktop."
},
+ "connectIntegrationButtonDesc": {
+ "message": "Connect $INTEGRATION$",
+ "placeholders": {
+ "integration": {
+ "content": "$1",
+ "example": "Crowdstrike"
+ }
+ }
+ },
"integrationCardTooltip": {
"message": "Launch $INTEGRATION$ implementation guide.",
"placeholders": {
@@ -9550,6 +9577,15 @@
"createNewClientToManageAsProvider": {
"message": "Create a new client organization to manage as a Provider. Additional seats will be reflected in the next billing cycle."
},
+ "url": {
+ "message": "URL"
+ },
+ "bearerToken": {
+ "message": "Bearer Token"
+ },
+ "index": {
+ "message": "Index"
+ },
"selectAPlan": {
"message": "Select a plan"
},
@@ -10910,6 +10946,36 @@
}
}
},
+ "unpaidInvoices": {
+ "message": "Unpaid invoices"
+ },
+ "unpaidInvoicesForServiceUser": {
+ "message": "Your subscription has not been paid. Contact your provider administrator to restore service to you and your clients.",
+ "description": "A message shown in a non-dismissible dialog to service users of unpaid providers."
+ },
+ "providerSuspended": {
+ "message": "$PROVIDER$ is suspended",
+ "placeholders": {
+ "provider": {
+ "content": "$1",
+ "example": "Acme Industries"
+ }
+ }
+ },
+ "restoreProviderPortalAccessViaCustomerSupport": {
+ "message": "To restore access to your provider portal, contact Bitwarden Customer Support to renew your subscription.",
+ "description": "A message shown in a non-dismissible dialog to any user of a suspended providers."
+ },
+ "restoreProviderPortalAccessViaPaymentMethod": {
+ "message": "Your subscription has not been paid. To restore service to you and your clients, add a payment method by $CANCELLATION_DATE$.",
+ "placeholders": {
+ "cancellation_date": {
+ "content": "$1",
+ "example": "07/10/2025"
+ }
+ },
+ "description": "A message shown in a non-dismissible dialog to admins of unpaid providers."
+ },
"subscribetoEnterprise": {
"message": "Subscribe to $PLAN$",
"placeholders": {
@@ -10930,5 +10996,11 @@
},
"unlimitedSecretsAndProjects": {
"message": "Unlimited secrets and projects"
+ },
+ "providersubscriptionCanceled": {
+ "message": "Subscription canceled"
+ },
+ "providersubCanceledmessage": {
+ "message" : "To resubscribe, contact Bitwarden Customer Support."
}
}
\ No newline at end of file
diff --git a/apps/web/src/locales/en_GB/messages.json b/apps/web/src/locales/en_GB/messages.json
index 2b3edba1ee9..751041c1070 100644
--- a/apps/web/src/locales/en_GB/messages.json
+++ b/apps/web/src/locales/en_GB/messages.json
@@ -568,6 +568,9 @@
"cancel": {
"message": "Cancel"
},
+ "later": {
+ "message": "Later"
+ },
"canceled": {
"message": "Cancelled"
},
@@ -626,6 +629,9 @@
"searchGroups": {
"message": "Search groups"
},
+ "resetSearch": {
+ "message": "Reset search"
+ },
"allItems": {
"message": "All items"
},
@@ -4626,6 +4632,9 @@
"receiveMarketingEmailsV2": {
"message": "Get advice, announcements, and research opportunities from Bitwarden in your inbox."
},
+ "subscribe": {
+ "message": "Subscribe"
+ },
"unsubscribe": {
"message": "Unsubscribe"
},
@@ -9465,6 +9474,12 @@
"deviceManagementDesc": {
"message": "Configure device management for Bitwarden using the implementation guide for your platform."
},
+ "crowdstrikeEventIntegrationDesc": {
+ "message": "Send event data to your Logscale instance"
+ },
+ "failedToSaveIntegration": {
+ "message": "Failed to save integration. Please try again later."
+ },
"deviceIdMissing": {
"message": "Device ID is missing"
},
@@ -9480,6 +9495,15 @@
"reopenLinkOnDesktop": {
"message": "Reopen this link from your email on a desktop."
},
+ "connectIntegrationButtonDesc": {
+ "message": "Connect $INTEGRATION$",
+ "placeholders": {
+ "integration": {
+ "content": "$1",
+ "example": "Crowdstrike"
+ }
+ }
+ },
"integrationCardTooltip": {
"message": "Launch $INTEGRATION$ implementation guide.",
"placeholders": {
@@ -9537,6 +9561,15 @@
"createNewClientToManageAsProvider": {
"message": "Create a new client organisation to manage as a Provider. Additional seats will be reflected in the next billing cycle."
},
+ "url": {
+ "message": "URL"
+ },
+ "bearerToken": {
+ "message": "Bearer Token"
+ },
+ "index": {
+ "message": "Index"
+ },
"selectAPlan": {
"message": "Select a plan"
},
@@ -10285,8 +10318,8 @@
"organizationUserDeletedDesc": {
"message": "The user was removed from the organisation and all associated user data has been deleted."
},
- "deletedUserId": {
- "message": "Deleted user $ID$ - an owner / admin deleted the user account",
+ "deletedUserIdEventMessage": {
+ "message": "Deleted user $ID$",
"placeholders": {
"id": {
"content": "$1",
@@ -10396,8 +10429,8 @@
"domainStatusUnderVerification": {
"message": "Under verification"
},
- "claimedDomainsDesc": {
- "message": "Claim a domain to own all member accounts whose email address matches the domain. Members will be able to skip the SSO identifier when logging in. Administrators will also be able to delete member accounts."
+ "claimedDomainsDescription": {
+ "message": "Claim a domain to own member accounts. The SSO identifier page will be skipped during login for members with claimed domains and administrators will be able to delete claimed accounts."
},
"invalidDomainNameClaimMessage": {
"message": "Input is not a valid format. Format: mydomain.com. Subdomains require separate entries to be claimed."
@@ -10896,5 +10929,56 @@
"example": "12-3456789"
}
}
+ },
+ "unpaidInvoices": {
+ "message": "Unpaid invoices"
+ },
+ "unpaidInvoicesForServiceUser": {
+ "message": "Your subscription has not been paid. Contact your provider administrator to restore service to you and your clients.",
+ "description": "A message shown in a non-dismissible dialog to service users of unpaid providers."
+ },
+ "providerSuspended": {
+ "message": "$PROVIDER$ is suspended",
+ "placeholders": {
+ "provider": {
+ "content": "$1",
+ "example": "Acme Industries"
+ }
+ }
+ },
+ "restoreProviderPortalAccessViaCustomerSupport": {
+ "message": "To restore access to your provider portal, contact Bitwarden Customer Support to renew your subscription.",
+ "description": "A message shown in a non-dismissible dialog to any user of a suspended providers."
+ },
+ "restoreProviderPortalAccessViaPaymentMethod": {
+ "message": "Your subscription has not been paid. To restore service to you and your clients, add a payment method by $CANCELLATION_DATE$.",
+ "placeholders": {
+ "cancellation_date": {
+ "content": "$1",
+ "example": "07/10/2025"
+ }
+ },
+ "description": "A message shown in a non-dismissible dialog to admins of unpaid providers."
+ },
+ "subscribetoEnterprise": {
+ "message": "Subscribe to $PLAN$",
+ "placeholders": {
+ "plan": {
+ "content": "$1",
+ "example": "Teams"
+ }
+ }
+ },
+ "subscribeEnterpriseSubtitle": {
+ "message": "Your 7-day $PLAN$ trial starts today. Add a payment method now to continue using these features after your trial ends: ",
+ "placeholders": {
+ "plan": {
+ "content": "$1",
+ "example": "Teams"
+ }
+ }
+ },
+ "unlimitedSecretsAndProjects": {
+ "message": "Unlimited secrets and projects"
}
-}
+}
\ No newline at end of file
diff --git a/apps/web/src/locales/en_IN/messages.json b/apps/web/src/locales/en_IN/messages.json
index f2fb53f9b7d..0e0a527902d 100644
--- a/apps/web/src/locales/en_IN/messages.json
+++ b/apps/web/src/locales/en_IN/messages.json
@@ -568,6 +568,9 @@
"cancel": {
"message": "Cancel"
},
+ "later": {
+ "message": "Later"
+ },
"canceled": {
"message": "Cancelled"
},
@@ -626,6 +629,9 @@
"searchGroups": {
"message": "Search groups"
},
+ "resetSearch": {
+ "message": "Reset search"
+ },
"allItems": {
"message": "All items"
},
@@ -4626,6 +4632,9 @@
"receiveMarketingEmailsV2": {
"message": "Get advice, announcements, and research opportunities from Bitwarden in your inbox."
},
+ "subscribe": {
+ "message": "Subscribe"
+ },
"unsubscribe": {
"message": "Unsubscribe"
},
@@ -9465,6 +9474,12 @@
"deviceManagementDesc": {
"message": "Configure device management for Bitwarden using the implementation guide for your platform."
},
+ "crowdstrikeEventIntegrationDesc": {
+ "message": "Send event data to your Logscale instance"
+ },
+ "failedToSaveIntegration": {
+ "message": "Failed to save integration. Please try again later."
+ },
"deviceIdMissing": {
"message": "Device ID is missing"
},
@@ -9480,6 +9495,15 @@
"reopenLinkOnDesktop": {
"message": "Reopen this link from your email on a desktop."
},
+ "connectIntegrationButtonDesc": {
+ "message": "Connect $INTEGRATION$",
+ "placeholders": {
+ "integration": {
+ "content": "$1",
+ "example": "Crowdstrike"
+ }
+ }
+ },
"integrationCardTooltip": {
"message": "Launch $INTEGRATION$ implementation guide.",
"placeholders": {
@@ -9537,6 +9561,15 @@
"createNewClientToManageAsProvider": {
"message": "Create a new client organisation to manage as a Provider. Additional seats will be reflected in the next billing cycle."
},
+ "url": {
+ "message": "URL"
+ },
+ "bearerToken": {
+ "message": "Bearer Token"
+ },
+ "index": {
+ "message": "Index"
+ },
"selectAPlan": {
"message": "Select a plan"
},
@@ -10285,8 +10318,8 @@
"organizationUserDeletedDesc": {
"message": "The user was removed from the organisation and all associated user data has been deleted."
},
- "deletedUserId": {
- "message": "Deleted user $ID$ - an owner / admin deleted the user account",
+ "deletedUserIdEventMessage": {
+ "message": "Deleted user $ID$",
"placeholders": {
"id": {
"content": "$1",
@@ -10396,8 +10429,8 @@
"domainStatusUnderVerification": {
"message": "Under verification"
},
- "claimedDomainsDesc": {
- "message": "Claim a domain to own all member accounts whose email address matches the domain. Members will be able to skip the SSO identifier when logging in. Administrators will also be able to delete member accounts."
+ "claimedDomainsDescription": {
+ "message": "Claim a domain to own member accounts. The SSO identifier page will be skipped during login for members with claimed domains and administrators will be able to delete claimed accounts."
},
"invalidDomainNameClaimMessage": {
"message": "Input is not a valid format. Format: mydomain.com. Subdomains require separate entries to be claimed."
@@ -10896,5 +10929,56 @@
"example": "12-3456789"
}
}
+ },
+ "unpaidInvoices": {
+ "message": "Unpaid invoices"
+ },
+ "unpaidInvoicesForServiceUser": {
+ "message": "Your subscription has not been paid. Contact your provider administrator to restore service to you and your clients.",
+ "description": "A message shown in a non-dismissible dialog to service users of unpaid providers."
+ },
+ "providerSuspended": {
+ "message": "$PROVIDER$ is suspended",
+ "placeholders": {
+ "provider": {
+ "content": "$1",
+ "example": "Acme Industries"
+ }
+ }
+ },
+ "restoreProviderPortalAccessViaCustomerSupport": {
+ "message": "To restore access to your provider portal, contact Bitwarden Customer Support to renew your subscription.",
+ "description": "A message shown in a non-dismissible dialog to any user of a suspended providers."
+ },
+ "restoreProviderPortalAccessViaPaymentMethod": {
+ "message": "Your subscription has not been paid. To restore service to you and your clients, add a payment method by $CANCELLATION_DATE$.",
+ "placeholders": {
+ "cancellation_date": {
+ "content": "$1",
+ "example": "07/10/2025"
+ }
+ },
+ "description": "A message shown in a non-dismissible dialog to admins of unpaid providers."
+ },
+ "subscribetoEnterprise": {
+ "message": "Subscribe to $PLAN$",
+ "placeholders": {
+ "plan": {
+ "content": "$1",
+ "example": "Teams"
+ }
+ }
+ },
+ "subscribeEnterpriseSubtitle": {
+ "message": "Your 7-day $PLAN$ trial starts today. Add a payment method now to continue using these features after your trial ends: ",
+ "placeholders": {
+ "plan": {
+ "content": "$1",
+ "example": "Teams"
+ }
+ }
+ },
+ "unlimitedSecretsAndProjects": {
+ "message": "Unlimited secrets and projects"
}
-}
+}
\ No newline at end of file
diff --git a/apps/web/src/locales/eo/messages.json b/apps/web/src/locales/eo/messages.json
index ebc17d7e9b2..b01524e54cb 100644
--- a/apps/web/src/locales/eo/messages.json
+++ b/apps/web/src/locales/eo/messages.json
@@ -568,6 +568,9 @@
"cancel": {
"message": "Nuligi"
},
+ "later": {
+ "message": "Later"
+ },
"canceled": {
"message": "Nuligita"
},
@@ -626,6 +629,9 @@
"searchGroups": {
"message": "Serĉi grupojn"
},
+ "resetSearch": {
+ "message": "Reset search"
+ },
"allItems": {
"message": "Ĉiuj eroj"
},
@@ -4626,6 +4632,9 @@
"receiveMarketingEmailsV2": {
"message": "Get advice, announcements, and research opportunities from Bitwarden in your inbox."
},
+ "subscribe": {
+ "message": "Subscribe"
+ },
"unsubscribe": {
"message": "Unsubscribe"
},
@@ -9465,6 +9474,12 @@
"deviceManagementDesc": {
"message": "Configure device management for Bitwarden using the implementation guide for your platform."
},
+ "crowdstrikeEventIntegrationDesc": {
+ "message": "Send event data to your Logscale instance"
+ },
+ "failedToSaveIntegration": {
+ "message": "Failed to save integration. Please try again later."
+ },
"deviceIdMissing": {
"message": "Device ID is missing"
},
@@ -9480,6 +9495,15 @@
"reopenLinkOnDesktop": {
"message": "Reopen this link from your email on a desktop."
},
+ "connectIntegrationButtonDesc": {
+ "message": "Connect $INTEGRATION$",
+ "placeholders": {
+ "integration": {
+ "content": "$1",
+ "example": "Crowdstrike"
+ }
+ }
+ },
"integrationCardTooltip": {
"message": "Launch $INTEGRATION$ implementation guide.",
"placeholders": {
@@ -9537,6 +9561,15 @@
"createNewClientToManageAsProvider": {
"message": "Create a new client organization to manage as a Provider. Additional seats will be reflected in the next billing cycle."
},
+ "url": {
+ "message": "URL"
+ },
+ "bearerToken": {
+ "message": "Bearer Token"
+ },
+ "index": {
+ "message": "Index"
+ },
"selectAPlan": {
"message": "Select a plan"
},
@@ -10285,8 +10318,8 @@
"organizationUserDeletedDesc": {
"message": "The user was removed from the organization and all associated user data has been deleted."
},
- "deletedUserId": {
- "message": "Deleted user $ID$ - an owner / admin deleted the user account",
+ "deletedUserIdEventMessage": {
+ "message": "Deleted user $ID$",
"placeholders": {
"id": {
"content": "$1",
@@ -10396,8 +10429,8 @@
"domainStatusUnderVerification": {
"message": "En konfirmado"
},
- "claimedDomainsDesc": {
- "message": "Claim a domain to own all member accounts whose email address matches the domain. Members will be able to skip the SSO identifier when logging in. Administrators will also be able to delete member accounts."
+ "claimedDomainsDescription": {
+ "message": "Claim a domain to own member accounts. The SSO identifier page will be skipped during login for members with claimed domains and administrators will be able to delete claimed accounts."
},
"invalidDomainNameClaimMessage": {
"message": "Input is not a valid format. Format: mydomain.com. Subdomains require separate entries to be claimed."
@@ -10896,5 +10929,56 @@
"example": "12-3456789"
}
}
+ },
+ "unpaidInvoices": {
+ "message": "Unpaid invoices"
+ },
+ "unpaidInvoicesForServiceUser": {
+ "message": "Your subscription has not been paid. Contact your provider administrator to restore service to you and your clients.",
+ "description": "A message shown in a non-dismissible dialog to service users of unpaid providers."
+ },
+ "providerSuspended": {
+ "message": "$PROVIDER$ is suspended",
+ "placeholders": {
+ "provider": {
+ "content": "$1",
+ "example": "Acme Industries"
+ }
+ }
+ },
+ "restoreProviderPortalAccessViaCustomerSupport": {
+ "message": "To restore access to your provider portal, contact Bitwarden Customer Support to renew your subscription.",
+ "description": "A message shown in a non-dismissible dialog to any user of a suspended providers."
+ },
+ "restoreProviderPortalAccessViaPaymentMethod": {
+ "message": "Your subscription has not been paid. To restore service to you and your clients, add a payment method by $CANCELLATION_DATE$.",
+ "placeholders": {
+ "cancellation_date": {
+ "content": "$1",
+ "example": "07/10/2025"
+ }
+ },
+ "description": "A message shown in a non-dismissible dialog to admins of unpaid providers."
+ },
+ "subscribetoEnterprise": {
+ "message": "Subscribe to $PLAN$",
+ "placeholders": {
+ "plan": {
+ "content": "$1",
+ "example": "Teams"
+ }
+ }
+ },
+ "subscribeEnterpriseSubtitle": {
+ "message": "Your 7-day $PLAN$ trial starts today. Add a payment method now to continue using these features after your trial ends: ",
+ "placeholders": {
+ "plan": {
+ "content": "$1",
+ "example": "Teams"
+ }
+ }
+ },
+ "unlimitedSecretsAndProjects": {
+ "message": "Unlimited secrets and projects"
}
-}
+}
\ No newline at end of file
diff --git a/apps/web/src/locales/es/messages.json b/apps/web/src/locales/es/messages.json
index 5c70de44650..189d0d8c582 100644
--- a/apps/web/src/locales/es/messages.json
+++ b/apps/web/src/locales/es/messages.json
@@ -568,6 +568,9 @@
"cancel": {
"message": "Cancelar"
},
+ "later": {
+ "message": "Later"
+ },
"canceled": {
"message": "Cancelado"
},
@@ -626,6 +629,9 @@
"searchGroups": {
"message": "Buscar grupos"
},
+ "resetSearch": {
+ "message": "Reset search"
+ },
"allItems": {
"message": "Todos los elementos"
},
@@ -4626,6 +4632,9 @@
"receiveMarketingEmailsV2": {
"message": "Get advice, announcements, and research opportunities from Bitwarden in your inbox."
},
+ "subscribe": {
+ "message": "Subscribe"
+ },
"unsubscribe": {
"message": "Darse de baja"
},
@@ -9465,6 +9474,12 @@
"deviceManagementDesc": {
"message": "Configure device management for Bitwarden using the implementation guide for your platform."
},
+ "crowdstrikeEventIntegrationDesc": {
+ "message": "Send event data to your Logscale instance"
+ },
+ "failedToSaveIntegration": {
+ "message": "Failed to save integration. Please try again later."
+ },
"deviceIdMissing": {
"message": "Falta el ID del dispositivo"
},
@@ -9480,6 +9495,15 @@
"reopenLinkOnDesktop": {
"message": "Reopen this link from your email on a desktop."
},
+ "connectIntegrationButtonDesc": {
+ "message": "Connect $INTEGRATION$",
+ "placeholders": {
+ "integration": {
+ "content": "$1",
+ "example": "Crowdstrike"
+ }
+ }
+ },
"integrationCardTooltip": {
"message": "Launch $INTEGRATION$ implementation guide.",
"placeholders": {
@@ -9537,6 +9561,15 @@
"createNewClientToManageAsProvider": {
"message": "Create a new client organization to manage as a Provider. Additional seats will be reflected in the next billing cycle."
},
+ "url": {
+ "message": "URL"
+ },
+ "bearerToken": {
+ "message": "Bearer Token"
+ },
+ "index": {
+ "message": "Index"
+ },
"selectAPlan": {
"message": "Selecciona un plan"
},
@@ -10285,8 +10318,8 @@
"organizationUserDeletedDesc": {
"message": "The user was removed from the organization and all associated user data has been deleted."
},
- "deletedUserId": {
- "message": "Deleted user $ID$ - an owner / admin deleted the user account",
+ "deletedUserIdEventMessage": {
+ "message": "Deleted user $ID$",
"placeholders": {
"id": {
"content": "$1",
@@ -10396,8 +10429,8 @@
"domainStatusUnderVerification": {
"message": "Under verification"
},
- "claimedDomainsDesc": {
- "message": "Claim a domain to own all member accounts whose email address matches the domain. Members will be able to skip the SSO identifier when logging in. Administrators will also be able to delete member accounts."
+ "claimedDomainsDescription": {
+ "message": "Claim a domain to own member accounts. The SSO identifier page will be skipped during login for members with claimed domains and administrators will be able to delete claimed accounts."
},
"invalidDomainNameClaimMessage": {
"message": "Input is not a valid format. Format: mydomain.com. Subdomains require separate entries to be claimed."
@@ -10896,5 +10929,56 @@
"example": "12-3456789"
}
}
+ },
+ "unpaidInvoices": {
+ "message": "Unpaid invoices"
+ },
+ "unpaidInvoicesForServiceUser": {
+ "message": "Your subscription has not been paid. Contact your provider administrator to restore service to you and your clients.",
+ "description": "A message shown in a non-dismissible dialog to service users of unpaid providers."
+ },
+ "providerSuspended": {
+ "message": "$PROVIDER$ is suspended",
+ "placeholders": {
+ "provider": {
+ "content": "$1",
+ "example": "Acme Industries"
+ }
+ }
+ },
+ "restoreProviderPortalAccessViaCustomerSupport": {
+ "message": "To restore access to your provider portal, contact Bitwarden Customer Support to renew your subscription.",
+ "description": "A message shown in a non-dismissible dialog to any user of a suspended providers."
+ },
+ "restoreProviderPortalAccessViaPaymentMethod": {
+ "message": "Your subscription has not been paid. To restore service to you and your clients, add a payment method by $CANCELLATION_DATE$.",
+ "placeholders": {
+ "cancellation_date": {
+ "content": "$1",
+ "example": "07/10/2025"
+ }
+ },
+ "description": "A message shown in a non-dismissible dialog to admins of unpaid providers."
+ },
+ "subscribetoEnterprise": {
+ "message": "Subscribe to $PLAN$",
+ "placeholders": {
+ "plan": {
+ "content": "$1",
+ "example": "Teams"
+ }
+ }
+ },
+ "subscribeEnterpriseSubtitle": {
+ "message": "Your 7-day $PLAN$ trial starts today. Add a payment method now to continue using these features after your trial ends: ",
+ "placeholders": {
+ "plan": {
+ "content": "$1",
+ "example": "Teams"
+ }
+ }
+ },
+ "unlimitedSecretsAndProjects": {
+ "message": "Unlimited secrets and projects"
}
-}
+}
\ No newline at end of file
diff --git a/apps/web/src/locales/et/messages.json b/apps/web/src/locales/et/messages.json
index 6e865f9b5cb..36fca68a02a 100644
--- a/apps/web/src/locales/et/messages.json
+++ b/apps/web/src/locales/et/messages.json
@@ -568,6 +568,9 @@
"cancel": {
"message": "Tühista"
},
+ "later": {
+ "message": "Later"
+ },
"canceled": {
"message": "Tühistatud"
},
@@ -626,6 +629,9 @@
"searchGroups": {
"message": "Otsi gruppidest"
},
+ "resetSearch": {
+ "message": "Reset search"
+ },
"allItems": {
"message": "Kõik kirjed"
},
@@ -4626,6 +4632,9 @@
"receiveMarketingEmailsV2": {
"message": "Soovin saada nõuandeid, uudiseid ja pakkumisi Bitwardenilt oma postkasti."
},
+ "subscribe": {
+ "message": "Subscribe"
+ },
"unsubscribe": {
"message": "Lõpeta tellimus"
},
@@ -9465,6 +9474,12 @@
"deviceManagementDesc": {
"message": "Configure device management for Bitwarden using the implementation guide for your platform."
},
+ "crowdstrikeEventIntegrationDesc": {
+ "message": "Send event data to your Logscale instance"
+ },
+ "failedToSaveIntegration": {
+ "message": "Failed to save integration. Please try again later."
+ },
"deviceIdMissing": {
"message": "Device ID is missing"
},
@@ -9480,6 +9495,15 @@
"reopenLinkOnDesktop": {
"message": "Reopen this link from your email on a desktop."
},
+ "connectIntegrationButtonDesc": {
+ "message": "Connect $INTEGRATION$",
+ "placeholders": {
+ "integration": {
+ "content": "$1",
+ "example": "Crowdstrike"
+ }
+ }
+ },
"integrationCardTooltip": {
"message": "Launch $INTEGRATION$ implementation guide.",
"placeholders": {
@@ -9537,6 +9561,15 @@
"createNewClientToManageAsProvider": {
"message": "Create a new client organization to manage as a Provider. Additional seats will be reflected in the next billing cycle."
},
+ "url": {
+ "message": "URL"
+ },
+ "bearerToken": {
+ "message": "Bearer Token"
+ },
+ "index": {
+ "message": "Index"
+ },
"selectAPlan": {
"message": "Select a plan"
},
@@ -10285,8 +10318,8 @@
"organizationUserDeletedDesc": {
"message": "The user was removed from the organization and all associated user data has been deleted."
},
- "deletedUserId": {
- "message": "Deleted user $ID$ - an owner / admin deleted the user account",
+ "deletedUserIdEventMessage": {
+ "message": "Deleted user $ID$",
"placeholders": {
"id": {
"content": "$1",
@@ -10396,8 +10429,8 @@
"domainStatusUnderVerification": {
"message": "Under verification"
},
- "claimedDomainsDesc": {
- "message": "Claim a domain to own all member accounts whose email address matches the domain. Members will be able to skip the SSO identifier when logging in. Administrators will also be able to delete member accounts."
+ "claimedDomainsDescription": {
+ "message": "Claim a domain to own member accounts. The SSO identifier page will be skipped during login for members with claimed domains and administrators will be able to delete claimed accounts."
},
"invalidDomainNameClaimMessage": {
"message": "Input is not a valid format. Format: mydomain.com. Subdomains require separate entries to be claimed."
@@ -10896,5 +10929,56 @@
"example": "12-3456789"
}
}
+ },
+ "unpaidInvoices": {
+ "message": "Unpaid invoices"
+ },
+ "unpaidInvoicesForServiceUser": {
+ "message": "Your subscription has not been paid. Contact your provider administrator to restore service to you and your clients.",
+ "description": "A message shown in a non-dismissible dialog to service users of unpaid providers."
+ },
+ "providerSuspended": {
+ "message": "$PROVIDER$ is suspended",
+ "placeholders": {
+ "provider": {
+ "content": "$1",
+ "example": "Acme Industries"
+ }
+ }
+ },
+ "restoreProviderPortalAccessViaCustomerSupport": {
+ "message": "To restore access to your provider portal, contact Bitwarden Customer Support to renew your subscription.",
+ "description": "A message shown in a non-dismissible dialog to any user of a suspended providers."
+ },
+ "restoreProviderPortalAccessViaPaymentMethod": {
+ "message": "Your subscription has not been paid. To restore service to you and your clients, add a payment method by $CANCELLATION_DATE$.",
+ "placeholders": {
+ "cancellation_date": {
+ "content": "$1",
+ "example": "07/10/2025"
+ }
+ },
+ "description": "A message shown in a non-dismissible dialog to admins of unpaid providers."
+ },
+ "subscribetoEnterprise": {
+ "message": "Subscribe to $PLAN$",
+ "placeholders": {
+ "plan": {
+ "content": "$1",
+ "example": "Teams"
+ }
+ }
+ },
+ "subscribeEnterpriseSubtitle": {
+ "message": "Your 7-day $PLAN$ trial starts today. Add a payment method now to continue using these features after your trial ends: ",
+ "placeholders": {
+ "plan": {
+ "content": "$1",
+ "example": "Teams"
+ }
+ }
+ },
+ "unlimitedSecretsAndProjects": {
+ "message": "Unlimited secrets and projects"
}
-}
+}
\ No newline at end of file
diff --git a/apps/web/src/locales/eu/messages.json b/apps/web/src/locales/eu/messages.json
index fe066e152c0..f666d9146af 100644
--- a/apps/web/src/locales/eu/messages.json
+++ b/apps/web/src/locales/eu/messages.json
@@ -568,6 +568,9 @@
"cancel": {
"message": "Ezeztatu"
},
+ "later": {
+ "message": "Later"
+ },
"canceled": {
"message": "Ezeztatuta"
},
@@ -626,6 +629,9 @@
"searchGroups": {
"message": "Search groups"
},
+ "resetSearch": {
+ "message": "Reset search"
+ },
"allItems": {
"message": "Elementu guztiak"
},
@@ -4626,6 +4632,9 @@
"receiveMarketingEmailsV2": {
"message": "Get advice, announcements, and research opportunities from Bitwarden in your inbox."
},
+ "subscribe": {
+ "message": "Subscribe"
+ },
"unsubscribe": {
"message": "Unsubscribe"
},
@@ -9465,6 +9474,12 @@
"deviceManagementDesc": {
"message": "Configure device management for Bitwarden using the implementation guide for your platform."
},
+ "crowdstrikeEventIntegrationDesc": {
+ "message": "Send event data to your Logscale instance"
+ },
+ "failedToSaveIntegration": {
+ "message": "Failed to save integration. Please try again later."
+ },
"deviceIdMissing": {
"message": "Device ID is missing"
},
@@ -9480,6 +9495,15 @@
"reopenLinkOnDesktop": {
"message": "Reopen this link from your email on a desktop."
},
+ "connectIntegrationButtonDesc": {
+ "message": "Connect $INTEGRATION$",
+ "placeholders": {
+ "integration": {
+ "content": "$1",
+ "example": "Crowdstrike"
+ }
+ }
+ },
"integrationCardTooltip": {
"message": "Launch $INTEGRATION$ implementation guide.",
"placeholders": {
@@ -9537,6 +9561,15 @@
"createNewClientToManageAsProvider": {
"message": "Create a new client organization to manage as a Provider. Additional seats will be reflected in the next billing cycle."
},
+ "url": {
+ "message": "URL"
+ },
+ "bearerToken": {
+ "message": "Bearer Token"
+ },
+ "index": {
+ "message": "Index"
+ },
"selectAPlan": {
"message": "Select a plan"
},
@@ -10285,8 +10318,8 @@
"organizationUserDeletedDesc": {
"message": "The user was removed from the organization and all associated user data has been deleted."
},
- "deletedUserId": {
- "message": "Deleted user $ID$ - an owner / admin deleted the user account",
+ "deletedUserIdEventMessage": {
+ "message": "Deleted user $ID$",
"placeholders": {
"id": {
"content": "$1",
@@ -10396,8 +10429,8 @@
"domainStatusUnderVerification": {
"message": "Under verification"
},
- "claimedDomainsDesc": {
- "message": "Claim a domain to own all member accounts whose email address matches the domain. Members will be able to skip the SSO identifier when logging in. Administrators will also be able to delete member accounts."
+ "claimedDomainsDescription": {
+ "message": "Claim a domain to own member accounts. The SSO identifier page will be skipped during login for members with claimed domains and administrators will be able to delete claimed accounts."
},
"invalidDomainNameClaimMessage": {
"message": "Input is not a valid format. Format: mydomain.com. Subdomains require separate entries to be claimed."
@@ -10896,5 +10929,56 @@
"example": "12-3456789"
}
}
+ },
+ "unpaidInvoices": {
+ "message": "Unpaid invoices"
+ },
+ "unpaidInvoicesForServiceUser": {
+ "message": "Your subscription has not been paid. Contact your provider administrator to restore service to you and your clients.",
+ "description": "A message shown in a non-dismissible dialog to service users of unpaid providers."
+ },
+ "providerSuspended": {
+ "message": "$PROVIDER$ is suspended",
+ "placeholders": {
+ "provider": {
+ "content": "$1",
+ "example": "Acme Industries"
+ }
+ }
+ },
+ "restoreProviderPortalAccessViaCustomerSupport": {
+ "message": "To restore access to your provider portal, contact Bitwarden Customer Support to renew your subscription.",
+ "description": "A message shown in a non-dismissible dialog to any user of a suspended providers."
+ },
+ "restoreProviderPortalAccessViaPaymentMethod": {
+ "message": "Your subscription has not been paid. To restore service to you and your clients, add a payment method by $CANCELLATION_DATE$.",
+ "placeholders": {
+ "cancellation_date": {
+ "content": "$1",
+ "example": "07/10/2025"
+ }
+ },
+ "description": "A message shown in a non-dismissible dialog to admins of unpaid providers."
+ },
+ "subscribetoEnterprise": {
+ "message": "Subscribe to $PLAN$",
+ "placeholders": {
+ "plan": {
+ "content": "$1",
+ "example": "Teams"
+ }
+ }
+ },
+ "subscribeEnterpriseSubtitle": {
+ "message": "Your 7-day $PLAN$ trial starts today. Add a payment method now to continue using these features after your trial ends: ",
+ "placeholders": {
+ "plan": {
+ "content": "$1",
+ "example": "Teams"
+ }
+ }
+ },
+ "unlimitedSecretsAndProjects": {
+ "message": "Unlimited secrets and projects"
}
-}
+}
\ No newline at end of file
diff --git a/apps/web/src/locales/fa/messages.json b/apps/web/src/locales/fa/messages.json
index 8e8fd9663bf..14742332573 100644
--- a/apps/web/src/locales/fa/messages.json
+++ b/apps/web/src/locales/fa/messages.json
@@ -568,6 +568,9 @@
"cancel": {
"message": "انصراف"
},
+ "later": {
+ "message": "Later"
+ },
"canceled": {
"message": "لغو شده"
},
@@ -626,6 +629,9 @@
"searchGroups": {
"message": "جستجوی گروهها"
},
+ "resetSearch": {
+ "message": "Reset search"
+ },
"allItems": {
"message": "تمام موارد"
},
@@ -4626,6 +4632,9 @@
"receiveMarketingEmailsV2": {
"message": "پیشنهادها، اعلانها و فرصتهای تحقیقاتی Bitwarden را در صندوق ورودی خود دریافت کنید."
},
+ "subscribe": {
+ "message": "Subscribe"
+ },
"unsubscribe": {
"message": "لغو اشتراک"
},
@@ -9465,6 +9474,12 @@
"deviceManagementDesc": {
"message": "مدیریت دستگاهها را برای Bitwarden با استفاده از راهنمای پیادهسازی مربوط به پلتفرم خود پیکربندی کنید."
},
+ "crowdstrikeEventIntegrationDesc": {
+ "message": "Send event data to your Logscale instance"
+ },
+ "failedToSaveIntegration": {
+ "message": "Failed to save integration. Please try again later."
+ },
"deviceIdMissing": {
"message": "شناسه دستگاه موجود نیست"
},
@@ -9480,6 +9495,15 @@
"reopenLinkOnDesktop": {
"message": "این لینک را از ایمیل خود روی دسکتاپ باز کنید."
},
+ "connectIntegrationButtonDesc": {
+ "message": "Connect $INTEGRATION$",
+ "placeholders": {
+ "integration": {
+ "content": "$1",
+ "example": "Crowdstrike"
+ }
+ }
+ },
"integrationCardTooltip": {
"message": "راهنمای پیادهسازی $INTEGRATION$ را اجرا کنید.",
"placeholders": {
@@ -9537,6 +9561,15 @@
"createNewClientToManageAsProvider": {
"message": "یک سازمان مشتری جدید بهعنوان ارائهدهنده ایجاد کنید. صندلیهای اضافی در دوره صورتحساب بعدی اعمال خواهند شد."
},
+ "url": {
+ "message": "URL"
+ },
+ "bearerToken": {
+ "message": "Bearer Token"
+ },
+ "index": {
+ "message": "Index"
+ },
"selectAPlan": {
"message": "یک طرح انتخاب کنید"
},
@@ -10285,8 +10318,8 @@
"organizationUserDeletedDesc": {
"message": "کاربر از سازمان حذف شده و تمامی دادههای مربوط به کاربر نیز پاک شدهاند."
},
- "deletedUserId": {
- "message": "کاربر $ID$ حذف شد - یک مالک یا مدیر حساب کاربری را حذف کرده است",
+ "deletedUserIdEventMessage": {
+ "message": "Deleted user $ID$",
"placeholders": {
"id": {
"content": "$1",
@@ -10396,8 +10429,8 @@
"domainStatusUnderVerification": {
"message": "در حال تأیید"
},
- "claimedDomainsDesc": {
- "message": "یک دامنه را ثبت کنید تا مالک تمام حسابهای کاربری اعضایی باشید که آدرس ایمیل آنها با آن دامنه مطابقت دارد. اعضا میتوانند هنگام ورود، مرحله شناسایی SSO را رد کنند. همچنین مدیران قادر خواهند بود حسابهای کاربری اعضا را حذف کنند."
+ "claimedDomainsDescription": {
+ "message": "Claim a domain to own member accounts. The SSO identifier page will be skipped during login for members with claimed domains and administrators will be able to delete claimed accounts."
},
"invalidDomainNameClaimMessage": {
"message": "ورودی یک قالب معتبر نیست. فرمت: .mydomain.com زیر دامنه ها برای ثبت نیاز به ورودیهای جداگانه دارند."
@@ -10896,5 +10929,56 @@
"example": "12-3456789"
}
}
+ },
+ "unpaidInvoices": {
+ "message": "Unpaid invoices"
+ },
+ "unpaidInvoicesForServiceUser": {
+ "message": "Your subscription has not been paid. Contact your provider administrator to restore service to you and your clients.",
+ "description": "A message shown in a non-dismissible dialog to service users of unpaid providers."
+ },
+ "providerSuspended": {
+ "message": "$PROVIDER$ is suspended",
+ "placeholders": {
+ "provider": {
+ "content": "$1",
+ "example": "Acme Industries"
+ }
+ }
+ },
+ "restoreProviderPortalAccessViaCustomerSupport": {
+ "message": "To restore access to your provider portal, contact Bitwarden Customer Support to renew your subscription.",
+ "description": "A message shown in a non-dismissible dialog to any user of a suspended providers."
+ },
+ "restoreProviderPortalAccessViaPaymentMethod": {
+ "message": "Your subscription has not been paid. To restore service to you and your clients, add a payment method by $CANCELLATION_DATE$.",
+ "placeholders": {
+ "cancellation_date": {
+ "content": "$1",
+ "example": "07/10/2025"
+ }
+ },
+ "description": "A message shown in a non-dismissible dialog to admins of unpaid providers."
+ },
+ "subscribetoEnterprise": {
+ "message": "Subscribe to $PLAN$",
+ "placeholders": {
+ "plan": {
+ "content": "$1",
+ "example": "Teams"
+ }
+ }
+ },
+ "subscribeEnterpriseSubtitle": {
+ "message": "Your 7-day $PLAN$ trial starts today. Add a payment method now to continue using these features after your trial ends: ",
+ "placeholders": {
+ "plan": {
+ "content": "$1",
+ "example": "Teams"
+ }
+ }
+ },
+ "unlimitedSecretsAndProjects": {
+ "message": "Unlimited secrets and projects"
}
-}
+}
\ No newline at end of file
diff --git a/apps/web/src/locales/fi/messages.json b/apps/web/src/locales/fi/messages.json
index b2387149755..0054cd5099d 100644
--- a/apps/web/src/locales/fi/messages.json
+++ b/apps/web/src/locales/fi/messages.json
@@ -568,6 +568,9 @@
"cancel": {
"message": "Peruuta"
},
+ "later": {
+ "message": "Later"
+ },
"canceled": {
"message": "Peruttu"
},
@@ -626,6 +629,9 @@
"searchGroups": {
"message": "Etsi ryhmistä"
},
+ "resetSearch": {
+ "message": "Reset search"
+ },
"allItems": {
"message": "Kaikki kohteet"
},
@@ -4626,6 +4632,9 @@
"receiveMarketingEmailsV2": {
"message": "Vastaanota Bitwardenilta postilaatikkoosi vinkkejä, uutisia ja tutkimusmahdollisuuksia."
},
+ "subscribe": {
+ "message": "Subscribe"
+ },
"unsubscribe": {
"message": "Lopeta tilaus"
},
@@ -9465,6 +9474,12 @@
"deviceManagementDesc": {
"message": "Configure device management for Bitwarden using the implementation guide for your platform."
},
+ "crowdstrikeEventIntegrationDesc": {
+ "message": "Send event data to your Logscale instance"
+ },
+ "failedToSaveIntegration": {
+ "message": "Failed to save integration. Please try again later."
+ },
"deviceIdMissing": {
"message": "Laitteen ID puuttuu"
},
@@ -9480,6 +9495,15 @@
"reopenLinkOnDesktop": {
"message": "Reopen this link from your email on a desktop."
},
+ "connectIntegrationButtonDesc": {
+ "message": "Connect $INTEGRATION$",
+ "placeholders": {
+ "integration": {
+ "content": "$1",
+ "example": "Crowdstrike"
+ }
+ }
+ },
"integrationCardTooltip": {
"message": "Launch $INTEGRATION$ implementation guide.",
"placeholders": {
@@ -9537,6 +9561,15 @@
"createNewClientToManageAsProvider": {
"message": "Luo uusi asiakasorganisaatio, jota hallitset toimittajana. Uudet käyttäjäpaikat näkyvät seuraavalla laskutusjaksolla."
},
+ "url": {
+ "message": "URL"
+ },
+ "bearerToken": {
+ "message": "Bearer Token"
+ },
+ "index": {
+ "message": "Index"
+ },
"selectAPlan": {
"message": "Valitse tilaus"
},
@@ -10285,8 +10318,8 @@
"organizationUserDeletedDesc": {
"message": "The user was removed from the organization and all associated user data has been deleted."
},
- "deletedUserId": {
- "message": "Deleted user $ID$ - an owner / admin deleted the user account",
+ "deletedUserIdEventMessage": {
+ "message": "Deleted user $ID$",
"placeholders": {
"id": {
"content": "$1",
@@ -10396,8 +10429,8 @@
"domainStatusUnderVerification": {
"message": "Vahvistettavana"
},
- "claimedDomainsDesc": {
- "message": "Claim a domain to own all member accounts whose email address matches the domain. Members will be able to skip the SSO identifier when logging in. Administrators will also be able to delete member accounts."
+ "claimedDomainsDescription": {
+ "message": "Claim a domain to own member accounts. The SSO identifier page will be skipped during login for members with claimed domains and administrators will be able to delete claimed accounts."
},
"invalidDomainNameClaimMessage": {
"message": "Input is not a valid format. Format: mydomain.com. Subdomains require separate entries to be claimed."
@@ -10896,5 +10929,56 @@
"example": "12-3456789"
}
}
+ },
+ "unpaidInvoices": {
+ "message": "Unpaid invoices"
+ },
+ "unpaidInvoicesForServiceUser": {
+ "message": "Your subscription has not been paid. Contact your provider administrator to restore service to you and your clients.",
+ "description": "A message shown in a non-dismissible dialog to service users of unpaid providers."
+ },
+ "providerSuspended": {
+ "message": "$PROVIDER$ is suspended",
+ "placeholders": {
+ "provider": {
+ "content": "$1",
+ "example": "Acme Industries"
+ }
+ }
+ },
+ "restoreProviderPortalAccessViaCustomerSupport": {
+ "message": "To restore access to your provider portal, contact Bitwarden Customer Support to renew your subscription.",
+ "description": "A message shown in a non-dismissible dialog to any user of a suspended providers."
+ },
+ "restoreProviderPortalAccessViaPaymentMethod": {
+ "message": "Your subscription has not been paid. To restore service to you and your clients, add a payment method by $CANCELLATION_DATE$.",
+ "placeholders": {
+ "cancellation_date": {
+ "content": "$1",
+ "example": "07/10/2025"
+ }
+ },
+ "description": "A message shown in a non-dismissible dialog to admins of unpaid providers."
+ },
+ "subscribetoEnterprise": {
+ "message": "Subscribe to $PLAN$",
+ "placeholders": {
+ "plan": {
+ "content": "$1",
+ "example": "Teams"
+ }
+ }
+ },
+ "subscribeEnterpriseSubtitle": {
+ "message": "Your 7-day $PLAN$ trial starts today. Add a payment method now to continue using these features after your trial ends: ",
+ "placeholders": {
+ "plan": {
+ "content": "$1",
+ "example": "Teams"
+ }
+ }
+ },
+ "unlimitedSecretsAndProjects": {
+ "message": "Unlimited secrets and projects"
}
-}
+}
\ No newline at end of file
diff --git a/apps/web/src/locales/fil/messages.json b/apps/web/src/locales/fil/messages.json
index b56828be0e4..eea5d54c5a2 100644
--- a/apps/web/src/locales/fil/messages.json
+++ b/apps/web/src/locales/fil/messages.json
@@ -568,6 +568,9 @@
"cancel": {
"message": "Kanselahin"
},
+ "later": {
+ "message": "Later"
+ },
"canceled": {
"message": "Kinansela"
},
@@ -626,6 +629,9 @@
"searchGroups": {
"message": "Maghanap sa mga grupo"
},
+ "resetSearch": {
+ "message": "Reset search"
+ },
"allItems": {
"message": "Lahat ng mga item"
},
@@ -4626,6 +4632,9 @@
"receiveMarketingEmailsV2": {
"message": "Get advice, announcements, and research opportunities from Bitwarden in your inbox."
},
+ "subscribe": {
+ "message": "Subscribe"
+ },
"unsubscribe": {
"message": "Unsubscribe"
},
@@ -9465,6 +9474,12 @@
"deviceManagementDesc": {
"message": "Configure device management for Bitwarden using the implementation guide for your platform."
},
+ "crowdstrikeEventIntegrationDesc": {
+ "message": "Send event data to your Logscale instance"
+ },
+ "failedToSaveIntegration": {
+ "message": "Failed to save integration. Please try again later."
+ },
"deviceIdMissing": {
"message": "Device ID is missing"
},
@@ -9480,6 +9495,15 @@
"reopenLinkOnDesktop": {
"message": "Reopen this link from your email on a desktop."
},
+ "connectIntegrationButtonDesc": {
+ "message": "Connect $INTEGRATION$",
+ "placeholders": {
+ "integration": {
+ "content": "$1",
+ "example": "Crowdstrike"
+ }
+ }
+ },
"integrationCardTooltip": {
"message": "Launch $INTEGRATION$ implementation guide.",
"placeholders": {
@@ -9537,6 +9561,15 @@
"createNewClientToManageAsProvider": {
"message": "Create a new client organization to manage as a Provider. Additional seats will be reflected in the next billing cycle."
},
+ "url": {
+ "message": "URL"
+ },
+ "bearerToken": {
+ "message": "Bearer Token"
+ },
+ "index": {
+ "message": "Index"
+ },
"selectAPlan": {
"message": "Select a plan"
},
@@ -10285,8 +10318,8 @@
"organizationUserDeletedDesc": {
"message": "The user was removed from the organization and all associated user data has been deleted."
},
- "deletedUserId": {
- "message": "Deleted user $ID$ - an owner / admin deleted the user account",
+ "deletedUserIdEventMessage": {
+ "message": "Deleted user $ID$",
"placeholders": {
"id": {
"content": "$1",
@@ -10396,8 +10429,8 @@
"domainStatusUnderVerification": {
"message": "Under verification"
},
- "claimedDomainsDesc": {
- "message": "Claim a domain to own all member accounts whose email address matches the domain. Members will be able to skip the SSO identifier when logging in. Administrators will also be able to delete member accounts."
+ "claimedDomainsDescription": {
+ "message": "Claim a domain to own member accounts. The SSO identifier page will be skipped during login for members with claimed domains and administrators will be able to delete claimed accounts."
},
"invalidDomainNameClaimMessage": {
"message": "Input is not a valid format. Format: mydomain.com. Subdomains require separate entries to be claimed."
@@ -10896,5 +10929,56 @@
"example": "12-3456789"
}
}
+ },
+ "unpaidInvoices": {
+ "message": "Unpaid invoices"
+ },
+ "unpaidInvoicesForServiceUser": {
+ "message": "Your subscription has not been paid. Contact your provider administrator to restore service to you and your clients.",
+ "description": "A message shown in a non-dismissible dialog to service users of unpaid providers."
+ },
+ "providerSuspended": {
+ "message": "$PROVIDER$ is suspended",
+ "placeholders": {
+ "provider": {
+ "content": "$1",
+ "example": "Acme Industries"
+ }
+ }
+ },
+ "restoreProviderPortalAccessViaCustomerSupport": {
+ "message": "To restore access to your provider portal, contact Bitwarden Customer Support to renew your subscription.",
+ "description": "A message shown in a non-dismissible dialog to any user of a suspended providers."
+ },
+ "restoreProviderPortalAccessViaPaymentMethod": {
+ "message": "Your subscription has not been paid. To restore service to you and your clients, add a payment method by $CANCELLATION_DATE$.",
+ "placeholders": {
+ "cancellation_date": {
+ "content": "$1",
+ "example": "07/10/2025"
+ }
+ },
+ "description": "A message shown in a non-dismissible dialog to admins of unpaid providers."
+ },
+ "subscribetoEnterprise": {
+ "message": "Subscribe to $PLAN$",
+ "placeholders": {
+ "plan": {
+ "content": "$1",
+ "example": "Teams"
+ }
+ }
+ },
+ "subscribeEnterpriseSubtitle": {
+ "message": "Your 7-day $PLAN$ trial starts today. Add a payment method now to continue using these features after your trial ends: ",
+ "placeholders": {
+ "plan": {
+ "content": "$1",
+ "example": "Teams"
+ }
+ }
+ },
+ "unlimitedSecretsAndProjects": {
+ "message": "Unlimited secrets and projects"
}
-}
+}
\ No newline at end of file
diff --git a/apps/web/src/locales/fr/messages.json b/apps/web/src/locales/fr/messages.json
index 9a86b4aceb5..b235c16fe49 100644
--- a/apps/web/src/locales/fr/messages.json
+++ b/apps/web/src/locales/fr/messages.json
@@ -568,6 +568,9 @@
"cancel": {
"message": "Annuler"
},
+ "later": {
+ "message": "Plus tard"
+ },
"canceled": {
"message": "Annulé"
},
@@ -626,6 +629,9 @@
"searchGroups": {
"message": "Rechercher des groupes"
},
+ "resetSearch": {
+ "message": "Réinitialiser la recherche"
+ },
"allItems": {
"message": "Tous les éléments"
},
@@ -4626,6 +4632,9 @@
"receiveMarketingEmailsV2": {
"message": "Obtenez des conseils, des annonces et des opportunités de recherche de la part de Bitwarden dans votre boîte de réception."
},
+ "subscribe": {
+ "message": "Abonnez-vous"
+ },
"unsubscribe": {
"message": "Désabonnez-vous"
},
@@ -9465,6 +9474,12 @@
"deviceManagementDesc": {
"message": "Configurez la gestion des appareils pour Bitwarden en utilisant le guide d'implémentation pour votre plateforme."
},
+ "crowdstrikeEventIntegrationDesc": {
+ "message": "Envoyer les données de l'événement à votre instance Logscale"
+ },
+ "failedToSaveIntegration": {
+ "message": "Impossible d'enregistrer l'intégration. Veuillez réessayer plus tard."
+ },
"deviceIdMissing": {
"message": "L'identification de l'appareil est manquante"
},
@@ -9480,6 +9495,15 @@
"reopenLinkOnDesktop": {
"message": "Réouvrez ce lien à partir de votre courriel sur un bureau."
},
+ "connectIntegrationButtonDesc": {
+ "message": "Connecter $INTEGRATION$",
+ "placeholders": {
+ "integration": {
+ "content": "$1",
+ "example": "Crowdstrike"
+ }
+ }
+ },
"integrationCardTooltip": {
"message": "Lancez le guide d'implémentation $INTEGRATION$.",
"placeholders": {
@@ -9537,6 +9561,15 @@
"createNewClientToManageAsProvider": {
"message": "Créez une nouvelle organisation de clients à gérer en tant que Fournisseur. Des sièges supplémentaires seront reflétés lors du prochain cycle de facturation."
},
+ "url": {
+ "message": "URL"
+ },
+ "bearerToken": {
+ "message": "Jeton du porteur"
+ },
+ "index": {
+ "message": "Index"
+ },
"selectAPlan": {
"message": "Sélectionnez un plan"
},
@@ -10285,8 +10318,8 @@
"organizationUserDeletedDesc": {
"message": "L'utilisateur a été retiré de l'organisation et toutes ses données utilisateur associées ont été supprimées."
},
- "deletedUserId": {
- "message": "Utilisateur supprimé $ID$ - un propriétaire / administrateur a supprimé le compte utilisateur",
+ "deletedUserIdEventMessage": {
+ "message": "Utilisateur $ID$ supprimé",
"placeholders": {
"id": {
"content": "$1",
@@ -10396,8 +10429,8 @@
"domainStatusUnderVerification": {
"message": "En cours de vérification"
},
- "claimedDomainsDesc": {
- "message": "Réclamez un domaine pour posséder tous les comptes membres dont l'adresse courriel correspond au domaine. Les membres pourront éviter l'identifiant SSO lors de la connexion. Les administrateurs seront également en mesure de supprimer les comptes de membre."
+ "claimedDomainsDescription": {
+ "message": "Réclamer un domaine pour réclamer les comptes des membres. La page d'identification SSO sera ignorée lors de la connexion pour les membres ayant des domaines réclamés et les administrateurs pourront supprimer les comptes réclamés."
},
"invalidDomainNameClaimMessage": {
"message": "L'entrée n'est pas un format valide. Format: mondomaine.com. Les sous-domaines nécessitent des entrées séparées pour être réclamés."
@@ -10896,5 +10929,56 @@
"example": "12-3456789"
}
}
+ },
+ "unpaidInvoices": {
+ "message": "Facture impayée"
+ },
+ "unpaidInvoicesForServiceUser": {
+ "message": "Votre abonnement n'a pas été payé. Contactez l'administrateur de votre fournisseur pour restaurer le service à vous et à vos clients.",
+ "description": "A message shown in a non-dismissible dialog to service users of unpaid providers."
+ },
+ "providerSuspended": {
+ "message": "$PROVIDER$ est suspendu",
+ "placeholders": {
+ "provider": {
+ "content": "$1",
+ "example": "Acme Industries"
+ }
+ }
+ },
+ "restoreProviderPortalAccessViaCustomerSupport": {
+ "message": "Pour restaurer l'accès à votre portail de fournisseur, contactez l'assistance clientèle de Bitwarden pour renouveler votre abonnement.",
+ "description": "A message shown in a non-dismissible dialog to any user of a suspended providers."
+ },
+ "restoreProviderPortalAccessViaPaymentMethod": {
+ "message": "Votre abonnement n'a pas été payé. Pour restaurer le service à vous et à vos clients, ajoutez une méthode de paiement d'ici le $CANCELLATION_DATE$.",
+ "placeholders": {
+ "cancellation_date": {
+ "content": "$1",
+ "example": "07/10/2025"
+ }
+ },
+ "description": "A message shown in a non-dismissible dialog to admins of unpaid providers."
+ },
+ "subscribetoEnterprise": {
+ "message": "Abonnez-vous au plan $PLAN$",
+ "placeholders": {
+ "plan": {
+ "content": "$1",
+ "example": "Teams"
+ }
+ }
+ },
+ "subscribeEnterpriseSubtitle": {
+ "message": "Votre période d'essai $PLAN$ de 7 jours commence aujourd'hui. Ajoutez un moyen de paiement maintenant pour continuer à utiliser ces fonctionnalités après la fin de votre période d'essai: ",
+ "placeholders": {
+ "plan": {
+ "content": "$1",
+ "example": "Teams"
+ }
+ }
+ },
+ "unlimitedSecretsAndProjects": {
+ "message": "Projets et Secrets illimités"
}
-}
+}
\ No newline at end of file
diff --git a/apps/web/src/locales/gl/messages.json b/apps/web/src/locales/gl/messages.json
index a456d62e8c6..95b34ad308d 100644
--- a/apps/web/src/locales/gl/messages.json
+++ b/apps/web/src/locales/gl/messages.json
@@ -568,6 +568,9 @@
"cancel": {
"message": "Cancel"
},
+ "later": {
+ "message": "Later"
+ },
"canceled": {
"message": "Canceled"
},
@@ -626,6 +629,9 @@
"searchGroups": {
"message": "Procurar grupos"
},
+ "resetSearch": {
+ "message": "Reset search"
+ },
"allItems": {
"message": "Todos os elementos"
},
@@ -4626,6 +4632,9 @@
"receiveMarketingEmailsV2": {
"message": "Get advice, announcements, and research opportunities from Bitwarden in your inbox."
},
+ "subscribe": {
+ "message": "Subscribe"
+ },
"unsubscribe": {
"message": "Unsubscribe"
},
@@ -9465,6 +9474,12 @@
"deviceManagementDesc": {
"message": "Configure device management for Bitwarden using the implementation guide for your platform."
},
+ "crowdstrikeEventIntegrationDesc": {
+ "message": "Send event data to your Logscale instance"
+ },
+ "failedToSaveIntegration": {
+ "message": "Failed to save integration. Please try again later."
+ },
"deviceIdMissing": {
"message": "Device ID is missing"
},
@@ -9480,6 +9495,15 @@
"reopenLinkOnDesktop": {
"message": "Reopen this link from your email on a desktop."
},
+ "connectIntegrationButtonDesc": {
+ "message": "Connect $INTEGRATION$",
+ "placeholders": {
+ "integration": {
+ "content": "$1",
+ "example": "Crowdstrike"
+ }
+ }
+ },
"integrationCardTooltip": {
"message": "Launch $INTEGRATION$ implementation guide.",
"placeholders": {
@@ -9537,6 +9561,15 @@
"createNewClientToManageAsProvider": {
"message": "Create a new client organization to manage as a Provider. Additional seats will be reflected in the next billing cycle."
},
+ "url": {
+ "message": "URL"
+ },
+ "bearerToken": {
+ "message": "Bearer Token"
+ },
+ "index": {
+ "message": "Index"
+ },
"selectAPlan": {
"message": "Select a plan"
},
@@ -10285,8 +10318,8 @@
"organizationUserDeletedDesc": {
"message": "The user was removed from the organization and all associated user data has been deleted."
},
- "deletedUserId": {
- "message": "Deleted user $ID$ - an owner / admin deleted the user account",
+ "deletedUserIdEventMessage": {
+ "message": "Deleted user $ID$",
"placeholders": {
"id": {
"content": "$1",
@@ -10396,8 +10429,8 @@
"domainStatusUnderVerification": {
"message": "Under verification"
},
- "claimedDomainsDesc": {
- "message": "Claim a domain to own all member accounts whose email address matches the domain. Members will be able to skip the SSO identifier when logging in. Administrators will also be able to delete member accounts."
+ "claimedDomainsDescription": {
+ "message": "Claim a domain to own member accounts. The SSO identifier page will be skipped during login for members with claimed domains and administrators will be able to delete claimed accounts."
},
"invalidDomainNameClaimMessage": {
"message": "Input is not a valid format. Format: mydomain.com. Subdomains require separate entries to be claimed."
@@ -10896,5 +10929,56 @@
"example": "12-3456789"
}
}
+ },
+ "unpaidInvoices": {
+ "message": "Unpaid invoices"
+ },
+ "unpaidInvoicesForServiceUser": {
+ "message": "Your subscription has not been paid. Contact your provider administrator to restore service to you and your clients.",
+ "description": "A message shown in a non-dismissible dialog to service users of unpaid providers."
+ },
+ "providerSuspended": {
+ "message": "$PROVIDER$ is suspended",
+ "placeholders": {
+ "provider": {
+ "content": "$1",
+ "example": "Acme Industries"
+ }
+ }
+ },
+ "restoreProviderPortalAccessViaCustomerSupport": {
+ "message": "To restore access to your provider portal, contact Bitwarden Customer Support to renew your subscription.",
+ "description": "A message shown in a non-dismissible dialog to any user of a suspended providers."
+ },
+ "restoreProviderPortalAccessViaPaymentMethod": {
+ "message": "Your subscription has not been paid. To restore service to you and your clients, add a payment method by $CANCELLATION_DATE$.",
+ "placeholders": {
+ "cancellation_date": {
+ "content": "$1",
+ "example": "07/10/2025"
+ }
+ },
+ "description": "A message shown in a non-dismissible dialog to admins of unpaid providers."
+ },
+ "subscribetoEnterprise": {
+ "message": "Subscribe to $PLAN$",
+ "placeholders": {
+ "plan": {
+ "content": "$1",
+ "example": "Teams"
+ }
+ }
+ },
+ "subscribeEnterpriseSubtitle": {
+ "message": "Your 7-day $PLAN$ trial starts today. Add a payment method now to continue using these features after your trial ends: ",
+ "placeholders": {
+ "plan": {
+ "content": "$1",
+ "example": "Teams"
+ }
+ }
+ },
+ "unlimitedSecretsAndProjects": {
+ "message": "Unlimited secrets and projects"
}
-}
+}
\ No newline at end of file
diff --git a/apps/web/src/locales/he/messages.json b/apps/web/src/locales/he/messages.json
index 6d0d2e38166..0afcbd44c1b 100644
--- a/apps/web/src/locales/he/messages.json
+++ b/apps/web/src/locales/he/messages.json
@@ -568,6 +568,9 @@
"cancel": {
"message": "בטל"
},
+ "later": {
+ "message": "Later"
+ },
"canceled": {
"message": "בוטל"
},
@@ -626,6 +629,9 @@
"searchGroups": {
"message": "חפש קבוצות"
},
+ "resetSearch": {
+ "message": "Reset search"
+ },
"allItems": {
"message": "כל הפריטים"
},
@@ -4626,6 +4632,9 @@
"receiveMarketingEmailsV2": {
"message": "קבל עצות, הכרזות, והזדמנויות מחקר מאת Bitwarden בדואר הנכנס שלך."
},
+ "subscribe": {
+ "message": "Subscribe"
+ },
"unsubscribe": {
"message": "בטל הרשמה"
},
@@ -9465,6 +9474,12 @@
"deviceManagementDesc": {
"message": "קבע תצורת ניהול מכשירים עבור Bitwarden באמצעות מדריך היישום עבור הפלטפורמה שלך."
},
+ "crowdstrikeEventIntegrationDesc": {
+ "message": "Send event data to your Logscale instance"
+ },
+ "failedToSaveIntegration": {
+ "message": "Failed to save integration. Please try again later."
+ },
"deviceIdMissing": {
"message": "מזהה המכשיר חסר"
},
@@ -9480,6 +9495,15 @@
"reopenLinkOnDesktop": {
"message": "פתח מחדש קישור זה מהדוא\"ל שלך בשולחן עבודה."
},
+ "connectIntegrationButtonDesc": {
+ "message": "Connect $INTEGRATION$",
+ "placeholders": {
+ "integration": {
+ "content": "$1",
+ "example": "Crowdstrike"
+ }
+ }
+ },
"integrationCardTooltip": {
"message": "פתח מדריך יישום $INTEGRATION$.",
"placeholders": {
@@ -9537,6 +9561,15 @@
"createNewClientToManageAsProvider": {
"message": "צור ארגון לקוחות חדש כדי לנהל כספק. מקומות נוספים ישתקפו במחזור החיוב הבא."
},
+ "url": {
+ "message": "URL"
+ },
+ "bearerToken": {
+ "message": "Bearer Token"
+ },
+ "index": {
+ "message": "Index"
+ },
"selectAPlan": {
"message": "בחר תוכנית"
},
@@ -10285,8 +10318,8 @@
"organizationUserDeletedDesc": {
"message": "המשתמש הוסר מהארגון וכל נתוני המשתמש המשויכים נמחקו."
},
- "deletedUserId": {
- "message": "משתמש $ID$ נמחק - בעלים / מנהל מחק את חשבון המשתמש",
+ "deletedUserIdEventMessage": {
+ "message": "Deleted user $ID$",
"placeholders": {
"id": {
"content": "$1",
@@ -10396,8 +10429,8 @@
"domainStatusUnderVerification": {
"message": "תחת אימות"
},
- "claimedDomainsDesc": {
- "message": "דרוש דומיין כדי להחזיק בכל חשבונות החברים אשר כתובת הדוא\"ל שלהם תואמת את הדומיין. חברים יוכלו לדלג על מזהה ה־SSO בעת כניסה. מנהלים גם יוכלו למחוק חשבונות חברים."
+ "claimedDomainsDescription": {
+ "message": "Claim a domain to own member accounts. The SSO identifier page will be skipped during login for members with claimed domains and administrators will be able to delete claimed accounts."
},
"invalidDomainNameClaimMessage": {
"message": "הקלט אינו בפורמט תקין. פורמט: mydomain.com. תת-דומיינים דורשים שרשומות נפרדות ידרשו."
@@ -10896,5 +10929,56 @@
"example": "12-3456789"
}
}
+ },
+ "unpaidInvoices": {
+ "message": "Unpaid invoices"
+ },
+ "unpaidInvoicesForServiceUser": {
+ "message": "Your subscription has not been paid. Contact your provider administrator to restore service to you and your clients.",
+ "description": "A message shown in a non-dismissible dialog to service users of unpaid providers."
+ },
+ "providerSuspended": {
+ "message": "$PROVIDER$ is suspended",
+ "placeholders": {
+ "provider": {
+ "content": "$1",
+ "example": "Acme Industries"
+ }
+ }
+ },
+ "restoreProviderPortalAccessViaCustomerSupport": {
+ "message": "To restore access to your provider portal, contact Bitwarden Customer Support to renew your subscription.",
+ "description": "A message shown in a non-dismissible dialog to any user of a suspended providers."
+ },
+ "restoreProviderPortalAccessViaPaymentMethod": {
+ "message": "Your subscription has not been paid. To restore service to you and your clients, add a payment method by $CANCELLATION_DATE$.",
+ "placeholders": {
+ "cancellation_date": {
+ "content": "$1",
+ "example": "07/10/2025"
+ }
+ },
+ "description": "A message shown in a non-dismissible dialog to admins of unpaid providers."
+ },
+ "subscribetoEnterprise": {
+ "message": "Subscribe to $PLAN$",
+ "placeholders": {
+ "plan": {
+ "content": "$1",
+ "example": "Teams"
+ }
+ }
+ },
+ "subscribeEnterpriseSubtitle": {
+ "message": "Your 7-day $PLAN$ trial starts today. Add a payment method now to continue using these features after your trial ends: ",
+ "placeholders": {
+ "plan": {
+ "content": "$1",
+ "example": "Teams"
+ }
+ }
+ },
+ "unlimitedSecretsAndProjects": {
+ "message": "Unlimited secrets and projects"
}
-}
+}
\ No newline at end of file
diff --git a/apps/web/src/locales/hi/messages.json b/apps/web/src/locales/hi/messages.json
index 301e8a7abb3..e8d1ffb706f 100644
--- a/apps/web/src/locales/hi/messages.json
+++ b/apps/web/src/locales/hi/messages.json
@@ -568,6 +568,9 @@
"cancel": {
"message": "रद्द करें"
},
+ "later": {
+ "message": "Later"
+ },
"canceled": {
"message": "रद्द की गई"
},
@@ -626,6 +629,9 @@
"searchGroups": {
"message": "Search groups"
},
+ "resetSearch": {
+ "message": "Reset search"
+ },
"allItems": {
"message": "सभी सामग्री"
},
@@ -4626,6 +4632,9 @@
"receiveMarketingEmailsV2": {
"message": "Get advice, announcements, and research opportunities from Bitwarden in your inbox."
},
+ "subscribe": {
+ "message": "Subscribe"
+ },
"unsubscribe": {
"message": "Unsubscribe"
},
@@ -9465,6 +9474,12 @@
"deviceManagementDesc": {
"message": "Configure device management for Bitwarden using the implementation guide for your platform."
},
+ "crowdstrikeEventIntegrationDesc": {
+ "message": "Send event data to your Logscale instance"
+ },
+ "failedToSaveIntegration": {
+ "message": "Failed to save integration. Please try again later."
+ },
"deviceIdMissing": {
"message": "Device ID is missing"
},
@@ -9480,6 +9495,15 @@
"reopenLinkOnDesktop": {
"message": "Reopen this link from your email on a desktop."
},
+ "connectIntegrationButtonDesc": {
+ "message": "Connect $INTEGRATION$",
+ "placeholders": {
+ "integration": {
+ "content": "$1",
+ "example": "Crowdstrike"
+ }
+ }
+ },
"integrationCardTooltip": {
"message": "Launch $INTEGRATION$ implementation guide.",
"placeholders": {
@@ -9537,6 +9561,15 @@
"createNewClientToManageAsProvider": {
"message": "Create a new client organization to manage as a Provider. Additional seats will be reflected in the next billing cycle."
},
+ "url": {
+ "message": "URL"
+ },
+ "bearerToken": {
+ "message": "Bearer Token"
+ },
+ "index": {
+ "message": "Index"
+ },
"selectAPlan": {
"message": "Select a plan"
},
@@ -10285,8 +10318,8 @@
"organizationUserDeletedDesc": {
"message": "The user was removed from the organization and all associated user data has been deleted."
},
- "deletedUserId": {
- "message": "Deleted user $ID$ - an owner / admin deleted the user account",
+ "deletedUserIdEventMessage": {
+ "message": "Deleted user $ID$",
"placeholders": {
"id": {
"content": "$1",
@@ -10396,8 +10429,8 @@
"domainStatusUnderVerification": {
"message": "Under verification"
},
- "claimedDomainsDesc": {
- "message": "Claim a domain to own all member accounts whose email address matches the domain. Members will be able to skip the SSO identifier when logging in. Administrators will also be able to delete member accounts."
+ "claimedDomainsDescription": {
+ "message": "Claim a domain to own member accounts. The SSO identifier page will be skipped during login for members with claimed domains and administrators will be able to delete claimed accounts."
},
"invalidDomainNameClaimMessage": {
"message": "Input is not a valid format. Format: mydomain.com. Subdomains require separate entries to be claimed."
@@ -10896,5 +10929,56 @@
"example": "12-3456789"
}
}
+ },
+ "unpaidInvoices": {
+ "message": "Unpaid invoices"
+ },
+ "unpaidInvoicesForServiceUser": {
+ "message": "Your subscription has not been paid. Contact your provider administrator to restore service to you and your clients.",
+ "description": "A message shown in a non-dismissible dialog to service users of unpaid providers."
+ },
+ "providerSuspended": {
+ "message": "$PROVIDER$ is suspended",
+ "placeholders": {
+ "provider": {
+ "content": "$1",
+ "example": "Acme Industries"
+ }
+ }
+ },
+ "restoreProviderPortalAccessViaCustomerSupport": {
+ "message": "To restore access to your provider portal, contact Bitwarden Customer Support to renew your subscription.",
+ "description": "A message shown in a non-dismissible dialog to any user of a suspended providers."
+ },
+ "restoreProviderPortalAccessViaPaymentMethod": {
+ "message": "Your subscription has not been paid. To restore service to you and your clients, add a payment method by $CANCELLATION_DATE$.",
+ "placeholders": {
+ "cancellation_date": {
+ "content": "$1",
+ "example": "07/10/2025"
+ }
+ },
+ "description": "A message shown in a non-dismissible dialog to admins of unpaid providers."
+ },
+ "subscribetoEnterprise": {
+ "message": "Subscribe to $PLAN$",
+ "placeholders": {
+ "plan": {
+ "content": "$1",
+ "example": "Teams"
+ }
+ }
+ },
+ "subscribeEnterpriseSubtitle": {
+ "message": "Your 7-day $PLAN$ trial starts today. Add a payment method now to continue using these features after your trial ends: ",
+ "placeholders": {
+ "plan": {
+ "content": "$1",
+ "example": "Teams"
+ }
+ }
+ },
+ "unlimitedSecretsAndProjects": {
+ "message": "Unlimited secrets and projects"
}
-}
+}
\ No newline at end of file
diff --git a/apps/web/src/locales/hr/messages.json b/apps/web/src/locales/hr/messages.json
index b3b2aec4eb6..ce81d99590e 100644
--- a/apps/web/src/locales/hr/messages.json
+++ b/apps/web/src/locales/hr/messages.json
@@ -568,6 +568,9 @@
"cancel": {
"message": "Odustani"
},
+ "later": {
+ "message": "Kasnije"
+ },
"canceled": {
"message": "Otkazano"
},
@@ -626,6 +629,9 @@
"searchGroups": {
"message": "Pretraži grupe"
},
+ "resetSearch": {
+ "message": "Ponovno postavljanje pretraživanja"
+ },
"allItems": {
"message": "Sve stavke"
},
@@ -648,7 +654,7 @@
"message": "Sigurna bilješka"
},
"typeNote": {
- "message": "Note"
+ "message": "Bilješka"
},
"typeSshKey": {
"message": "SSH ključ"
@@ -865,10 +871,10 @@
"message": "Kopiraj ime"
},
"cardNumber": {
- "message": "card number"
+ "message": "broj kartice"
},
"copyFieldCipherName": {
- "message": "Copy $FIELD$, $CIPHERNAME$",
+ "message": "Kopiraj $FIELD$, $CIPHERNAME$",
"description": "Title for a button that copies a field value to the clipboard.",
"placeholders": {
"field": {
@@ -3503,7 +3509,7 @@
"message": "Web trezor"
},
"webApp": {
- "message": "Web app"
+ "message": "Web aplikacija"
},
"cli": {
"message": "CLI"
@@ -3942,7 +3948,7 @@
"message": "Pouzdan"
},
"needsApproval": {
- "message": "Zahtjeva odobrenje"
+ "message": "Zahtijeva odobrenje"
},
"areYouTryingtoLogin": {
"message": "Pokušavaš li se prijaviti?"
@@ -3988,7 +3994,7 @@
"message": "Odbijena je prijava na drugom uređaju. Ako si ovo stvarno ti, pokušaj se ponovno prijaviti uređajem."
},
"loginRequestApprovedForEmailOnDevice": {
- "message": "Login request approved for $EMAIL$ on $DEVICE$",
+ "message": "Prijava za $EMAIL$ potvrđena na uređaju $DEVICE$",
"placeholders": {
"email": {
"content": "$1",
@@ -4001,7 +4007,7 @@
}
},
"youDeniedLoginAttemptFromAnotherDevice": {
- "message": "You denied a login attempt from another device. If this was you, try to log in with the device again."
+ "message": "Odbijena je prijava na drugom uređaju. Ako si ovo stvarno ti, pokušaj se ponovno prijaviti uređajem."
},
"loginRequestHasAlreadyExpired": {
"message": "Zahtjev za prijavu je već istekao."
@@ -4148,7 +4154,7 @@
"message": "Pregledaj zahtjev za prijavu"
},
"loginRequest": {
- "message": "Login request"
+ "message": "Zahtjev za prijavu"
},
"freeTrialEndPromptCount": {
"message": "Besplatno probno razdoblje završava za $COUNT$ dan/a.",
@@ -4626,6 +4632,9 @@
"receiveMarketingEmailsV2": {
"message": "Primaj e-poštom od Bitwardena savjete, najave i mogućnosti istraživanja."
},
+ "subscribe": {
+ "message": "Pretplati se"
+ },
"unsubscribe": {
"message": "Poništi pretplatu"
},
@@ -9465,6 +9474,12 @@
"deviceManagementDesc": {
"message": "Konfiguriraj upravljanje uređajima za Bitwarden pomoću vodiča za implementaciju za svoju platformu."
},
+ "crowdstrikeEventIntegrationDesc": {
+ "message": "Pošaljite podatke o događajima svojoj Logscale instanci"
+ },
+ "failedToSaveIntegration": {
+ "message": "Failed to save integration. Please try again later."
+ },
"deviceIdMissing": {
"message": "Nedostaje ID uređaja"
},
@@ -9480,6 +9495,15 @@
"reopenLinkOnDesktop": {
"message": "Otvori ovu poveznicu na stolnom računalu."
},
+ "connectIntegrationButtonDesc": {
+ "message": "Spoji $INTEGRATION$",
+ "placeholders": {
+ "integration": {
+ "content": "$1",
+ "example": "Crowdstrike"
+ }
+ }
+ },
"integrationCardTooltip": {
"message": "Pokreni vodič za implementaciju $INTEGRATION$.",
"placeholders": {
@@ -9537,6 +9561,15 @@
"createNewClientToManageAsProvider": {
"message": "Stvori novu klijentsku organizaciju kojom ćeš upravljati kao Pružatelj. Dodatna mjesta bit će vidljiva u sljedećem ciklusu naplate."
},
+ "url": {
+ "message": "URL"
+ },
+ "bearerToken": {
+ "message": "Bearer Token"
+ },
+ "index": {
+ "message": "Index"
+ },
"selectAPlan": {
"message": "Odaberi plan"
},
@@ -10285,8 +10318,8 @@
"organizationUserDeletedDesc": {
"message": "Korisnik je uklonjen iz organizacije i svi povezani korisnički podaci su izbrisani."
},
- "deletedUserId": {
- "message": "Izbrisan korisnik $ID$ - vlasnik/admin je izbrisao korisnički račun",
+ "deletedUserIdEventMessage": {
+ "message": "Izbrisan korisnik $ID$",
"placeholders": {
"id": {
"content": "$1",
@@ -10396,8 +10429,8 @@
"domainStatusUnderVerification": {
"message": "Provjera u tijeku"
},
- "claimedDomainsDesc": {
- "message": "Potvrdi domenu za vlasništvo nad svim računima članova čija adresa e-pošte odgovara domeni. Članovi će moći preskočiti SSO identifikator prilikom prijave. Administratori će također moći brisati članske račune."
+ "claimedDomainsDescription": {
+ "message": "Potvrdi domenu za vlasništvo nad članskim računima. Stranica za SSO identifikator bit će preskočena tijekom prijave za članove sa potvrđenim domenama, a administratori će moći izbrisati zatražene račune."
},
"invalidDomainNameClaimMessage": {
"message": "Unos nije važeći. Format: mojadomena.hr Poddomene zahtijevaju zasebne unose za provjeru."
@@ -10896,5 +10929,56 @@
"example": "12-3456789"
}
}
+ },
+ "unpaidInvoices": {
+ "message": "Neplaćeni računi"
+ },
+ "unpaidInvoicesForServiceUser": {
+ "message": "Tvoja pretplata nije plaćena. Obrati se administratoru svog davatelja usluga za obnovu usluge za sebe i svoje klijente.",
+ "description": "A message shown in a non-dismissible dialog to service users of unpaid providers."
+ },
+ "providerSuspended": {
+ "message": "$PROVIDER$ je suspendiran",
+ "placeholders": {
+ "provider": {
+ "content": "$1",
+ "example": "Acme Industries"
+ }
+ }
+ },
+ "restoreProviderPortalAccessViaCustomerSupport": {
+ "message": "Za vraćanje pristupa portalu tvojeg davatelja usluga, obrati se korisničkoj podršci Bitwardena za obnavljanje pretplate.",
+ "description": "A message shown in a non-dismissible dialog to any user of a suspended providers."
+ },
+ "restoreProviderPortalAccessViaPaymentMethod": {
+ "message": "Tvoja pretplata nije plaćena. Za vraćanje usluge sebi i svojim klijentima, dodaj način plaćanja do $CANCELLATION_DATE$.",
+ "placeholders": {
+ "cancellation_date": {
+ "content": "$1",
+ "example": "07/10/2025"
+ }
+ },
+ "description": "A message shown in a non-dismissible dialog to admins of unpaid providers."
+ },
+ "subscribetoEnterprise": {
+ "message": "Pretplati se na $PLAN$",
+ "placeholders": {
+ "plan": {
+ "content": "$1",
+ "example": "Teams"
+ }
+ }
+ },
+ "subscribeEnterpriseSubtitle": {
+ "message": "Tvoj 7-dnevni probni rok za plan $PLAN$ počinje danas. Dodaj način plaćanja za nastavak korištenja značajki nakon završetka probnog roka: ",
+ "placeholders": {
+ "plan": {
+ "content": "$1",
+ "example": "Teams"
+ }
+ }
+ },
+ "unlimitedSecretsAndProjects": {
+ "message": "Neograničen broj tajni i projekata"
}
-}
+}
\ No newline at end of file
diff --git a/apps/web/src/locales/hu/messages.json b/apps/web/src/locales/hu/messages.json
index a38ed7f0c03..b12e1131043 100644
--- a/apps/web/src/locales/hu/messages.json
+++ b/apps/web/src/locales/hu/messages.json
@@ -568,6 +568,9 @@
"cancel": {
"message": "Mégsem"
},
+ "later": {
+ "message": "Később"
+ },
"canceled": {
"message": "Megszakítva"
},
@@ -626,6 +629,9 @@
"searchGroups": {
"message": "Csoportok keresése"
},
+ "resetSearch": {
+ "message": "Keresés visszaállítása"
+ },
"allItems": {
"message": "Összes elem"
},
@@ -4626,6 +4632,9 @@
"receiveMarketingEmailsV2": {
"message": "Kapj tanácsokat, bejelentéseket és közvélemény-kutatásokat a Bitwardentől a postaládádba."
},
+ "subscribe": {
+ "message": "Feliratkozás"
+ },
"unsubscribe": {
"message": "Leiratkozás"
},
@@ -9465,6 +9474,12 @@
"deviceManagementDesc": {
"message": "Az eszközkezelés konfigurálása a Bitwarden számára a platform megvalósítási útmutatója segítségével."
},
+ "crowdstrikeEventIntegrationDesc": {
+ "message": "Eseményadatok küldése a Logscale példánynak"
+ },
+ "failedToSaveIntegration": {
+ "message": "Nem sikerült menteni az integrációt. Próbáljuk újra később."
+ },
"deviceIdMissing": {
"message": "Az eszköz AZ hiányzik."
},
@@ -9480,6 +9495,15 @@
"reopenLinkOnDesktop": {
"message": "Nyissuk meg újra ezt a hivatkozást az emailből az asztali gépen."
},
+ "connectIntegrationButtonDesc": {
+ "message": "$INTEGRATION$ csatlakozás",
+ "placeholders": {
+ "integration": {
+ "content": "$1",
+ "example": "Crowdstrike"
+ }
+ }
+ },
"integrationCardTooltip": {
"message": "$INTEGRATION$ megvalósítási útmutató elindítása.",
"placeholders": {
@@ -9537,6 +9561,15 @@
"createNewClientToManageAsProvider": {
"message": "Create a new client organization to manage as a Provider. Additional seats will be reflected in the next billing cycle."
},
+ "url": {
+ "message": "Webcím"
+ },
+ "bearerToken": {
+ "message": "Tulajdonosi vezérjel"
+ },
+ "index": {
+ "message": "Index"
+ },
"selectAPlan": {
"message": "Előfizetés kiválasztása"
},
@@ -10285,8 +10318,8 @@
"organizationUserDeletedDesc": {
"message": "A felhasználó eltávolításra került a szervezetből és az összes kapcsolódó felhasználói adat törlésre került."
},
- "deletedUserId": {
- "message": "$ID$ felhasználó törlésre került – egy tulajdonos/adminisztrátor törölte a felhasználói fiókot.",
+ "deletedUserIdEventMessage": {
+ "message": "$ID$ felhasználó törlésre került.",
"placeholders": {
"id": {
"content": "$1",
@@ -10396,8 +10429,8 @@
"domainStatusUnderVerification": {
"message": "Ellenőrzés alatt"
},
- "claimedDomainsDesc": {
- "message": "Igényeljünk egy tartományt az összes olyan tagfiók birtoklásához, amelynek email címe megegyezik a tartománnyal A tagok bejelentkezéskor kihagyhatják az egyszeri bejelentkezési azonosítót. Az adminisztrátorok törölhetik a tagfiókokat is."
+ "claimedDomainsDescription": {
+ "message": "Tartomány igénylése tagfiókok birtoklására. Az igényelt tartományokkal rendelkező tagok bejelentkezése során az SSO azonosító oldal kihagyásra kerül és az adminisztrátorok törölhetik az igényelt fiókokat."
},
"invalidDomainNameClaimMessage": {
"message": "A bemeneti formátum nem érvényes. Formátum: valami.hu Az altartományokhoz külön bejegyzések szükségesek az igényléshez."
@@ -10896,5 +10929,56 @@
"example": "12-3456789"
}
}
+ },
+ "unpaidInvoices": {
+ "message": "Kifizetetlen számlák"
+ },
+ "unpaidInvoicesForServiceUser": {
+ "message": "Az előfizetés nem lett kifizetve. Lépjünk kapcsolatba a szolgáltató adminisztrátorával, hogy visszaállítsa a szolgáltatást nekünk és az ügyfeleknek.",
+ "description": "A message shown in a non-dismissible dialog to service users of unpaid providers."
+ },
+ "providerSuspended": {
+ "message": "$PROVIDER$ felfüggesztésre került.",
+ "placeholders": {
+ "provider": {
+ "content": "$1",
+ "example": "Acme Industries"
+ }
+ }
+ },
+ "restoreProviderPortalAccessViaCustomerSupport": {
+ "message": "A szolgáltatói portálhoz hozzáférés visszaállításához lépjünk kapcsolatba a Bitwarden ügyfélszolgálatával az előfizetés megújításához.",
+ "description": "A message shown in a non-dismissible dialog to any user of a suspended providers."
+ },
+ "restoreProviderPortalAccessViaPaymentMethod": {
+ "message": "Az előfizetés nem lett kifizetve. A szolgáltatás visszaállításához nekünk és az ügyfeleknek adjunk hozzá egy fizetési módot $CANCELLATION_DATE$ segítségével.",
+ "placeholders": {
+ "cancellation_date": {
+ "content": "$1",
+ "example": "07/10/2025"
+ }
+ },
+ "description": "A message shown in a non-dismissible dialog to admins of unpaid providers."
+ },
+ "subscribetoEnterprise": {
+ "message": "Feliratkozás: $PLAN$",
+ "placeholders": {
+ "plan": {
+ "content": "$1",
+ "example": "Teams"
+ }
+ }
+ },
+ "subscribeEnterpriseSubtitle": {
+ "message": "A 7 napos $PLAN$ próbaverzió ma kezdődik. Adjunk hozzá most egy fizetési módot, hogy továbbra is használhassuk ezeket a funkciókat a próba befejezése után:",
+ "placeholders": {
+ "plan": {
+ "content": "$1",
+ "example": "Teams"
+ }
+ }
+ },
+ "unlimitedSecretsAndProjects": {
+ "message": "Korlátlan titkos elem és projekt"
}
-}
+}
\ No newline at end of file
diff --git a/apps/web/src/locales/id/messages.json b/apps/web/src/locales/id/messages.json
index d7d7e6699a3..f4ff6a3ce31 100644
--- a/apps/web/src/locales/id/messages.json
+++ b/apps/web/src/locales/id/messages.json
@@ -568,6 +568,9 @@
"cancel": {
"message": "Batalkan"
},
+ "later": {
+ "message": "Later"
+ },
"canceled": {
"message": "Dibatalkan"
},
@@ -626,6 +629,9 @@
"searchGroups": {
"message": "Search groups"
},
+ "resetSearch": {
+ "message": "Reset search"
+ },
"allItems": {
"message": "Semua Item"
},
@@ -4626,6 +4632,9 @@
"receiveMarketingEmailsV2": {
"message": "Get advice, announcements, and research opportunities from Bitwarden in your inbox."
},
+ "subscribe": {
+ "message": "Subscribe"
+ },
"unsubscribe": {
"message": "Unsubscribe"
},
@@ -9465,6 +9474,12 @@
"deviceManagementDesc": {
"message": "Configure device management for Bitwarden using the implementation guide for your platform."
},
+ "crowdstrikeEventIntegrationDesc": {
+ "message": "Send event data to your Logscale instance"
+ },
+ "failedToSaveIntegration": {
+ "message": "Failed to save integration. Please try again later."
+ },
"deviceIdMissing": {
"message": "Device ID is missing"
},
@@ -9480,6 +9495,15 @@
"reopenLinkOnDesktop": {
"message": "Reopen this link from your email on a desktop."
},
+ "connectIntegrationButtonDesc": {
+ "message": "Connect $INTEGRATION$",
+ "placeholders": {
+ "integration": {
+ "content": "$1",
+ "example": "Crowdstrike"
+ }
+ }
+ },
"integrationCardTooltip": {
"message": "Launch $INTEGRATION$ implementation guide.",
"placeholders": {
@@ -9537,6 +9561,15 @@
"createNewClientToManageAsProvider": {
"message": "Create a new client organization to manage as a Provider. Additional seats will be reflected in the next billing cycle."
},
+ "url": {
+ "message": "URL"
+ },
+ "bearerToken": {
+ "message": "Bearer Token"
+ },
+ "index": {
+ "message": "Index"
+ },
"selectAPlan": {
"message": "Select a plan"
},
@@ -10285,8 +10318,8 @@
"organizationUserDeletedDesc": {
"message": "The user was removed from the organization and all associated user data has been deleted."
},
- "deletedUserId": {
- "message": "Deleted user $ID$ - an owner / admin deleted the user account",
+ "deletedUserIdEventMessage": {
+ "message": "Deleted user $ID$",
"placeholders": {
"id": {
"content": "$1",
@@ -10396,8 +10429,8 @@
"domainStatusUnderVerification": {
"message": "Under verification"
},
- "claimedDomainsDesc": {
- "message": "Claim a domain to own all member accounts whose email address matches the domain. Members will be able to skip the SSO identifier when logging in. Administrators will also be able to delete member accounts."
+ "claimedDomainsDescription": {
+ "message": "Claim a domain to own member accounts. The SSO identifier page will be skipped during login for members with claimed domains and administrators will be able to delete claimed accounts."
},
"invalidDomainNameClaimMessage": {
"message": "Input is not a valid format. Format: mydomain.com. Subdomains require separate entries to be claimed."
@@ -10896,5 +10929,56 @@
"example": "12-3456789"
}
}
+ },
+ "unpaidInvoices": {
+ "message": "Unpaid invoices"
+ },
+ "unpaidInvoicesForServiceUser": {
+ "message": "Your subscription has not been paid. Contact your provider administrator to restore service to you and your clients.",
+ "description": "A message shown in a non-dismissible dialog to service users of unpaid providers."
+ },
+ "providerSuspended": {
+ "message": "$PROVIDER$ is suspended",
+ "placeholders": {
+ "provider": {
+ "content": "$1",
+ "example": "Acme Industries"
+ }
+ }
+ },
+ "restoreProviderPortalAccessViaCustomerSupport": {
+ "message": "To restore access to your provider portal, contact Bitwarden Customer Support to renew your subscription.",
+ "description": "A message shown in a non-dismissible dialog to any user of a suspended providers."
+ },
+ "restoreProviderPortalAccessViaPaymentMethod": {
+ "message": "Your subscription has not been paid. To restore service to you and your clients, add a payment method by $CANCELLATION_DATE$.",
+ "placeholders": {
+ "cancellation_date": {
+ "content": "$1",
+ "example": "07/10/2025"
+ }
+ },
+ "description": "A message shown in a non-dismissible dialog to admins of unpaid providers."
+ },
+ "subscribetoEnterprise": {
+ "message": "Subscribe to $PLAN$",
+ "placeholders": {
+ "plan": {
+ "content": "$1",
+ "example": "Teams"
+ }
+ }
+ },
+ "subscribeEnterpriseSubtitle": {
+ "message": "Your 7-day $PLAN$ trial starts today. Add a payment method now to continue using these features after your trial ends: ",
+ "placeholders": {
+ "plan": {
+ "content": "$1",
+ "example": "Teams"
+ }
+ }
+ },
+ "unlimitedSecretsAndProjects": {
+ "message": "Unlimited secrets and projects"
}
-}
+}
\ No newline at end of file
diff --git a/apps/web/src/locales/it/messages.json b/apps/web/src/locales/it/messages.json
index 0fdcc4952c1..c79a4258a0e 100644
--- a/apps/web/src/locales/it/messages.json
+++ b/apps/web/src/locales/it/messages.json
@@ -568,6 +568,9 @@
"cancel": {
"message": "Annulla"
},
+ "later": {
+ "message": "Later"
+ },
"canceled": {
"message": "Annullato"
},
@@ -626,6 +629,9 @@
"searchGroups": {
"message": "Cerca gruppi"
},
+ "resetSearch": {
+ "message": "Reset search"
+ },
"allItems": {
"message": "Tutti gli elementi"
},
@@ -4626,6 +4632,9 @@
"receiveMarketingEmailsV2": {
"message": "Ottieni consigli, annunci e opportunità di ricerca da Bitwarden nella tua casella di posta."
},
+ "subscribe": {
+ "message": "Subscribe"
+ },
"unsubscribe": {
"message": "Annulla iscrizione"
},
@@ -9465,6 +9474,12 @@
"deviceManagementDesc": {
"message": "Configura la gestione dispositivi consultando la guida per la tua piattaforma."
},
+ "crowdstrikeEventIntegrationDesc": {
+ "message": "Send event data to your Logscale instance"
+ },
+ "failedToSaveIntegration": {
+ "message": "Failed to save integration. Please try again later."
+ },
"deviceIdMissing": {
"message": "ID dispositivo mancante"
},
@@ -9480,6 +9495,15 @@
"reopenLinkOnDesktop": {
"message": "Riapri questo link dalla tua email su un computer portatile o fisso."
},
+ "connectIntegrationButtonDesc": {
+ "message": "Connect $INTEGRATION$",
+ "placeholders": {
+ "integration": {
+ "content": "$1",
+ "example": "Crowdstrike"
+ }
+ }
+ },
"integrationCardTooltip": {
"message": "Avvia la guida di $INTEGRATION$.",
"placeholders": {
@@ -9537,6 +9561,15 @@
"createNewClientToManageAsProvider": {
"message": "Crea una nuova organizzazione cliente da gestire come fornitore. Gli slot aggiuntivi saranno riflessi nel prossimo ciclo di fatturazione."
},
+ "url": {
+ "message": "URL"
+ },
+ "bearerToken": {
+ "message": "Bearer Token"
+ },
+ "index": {
+ "message": "Index"
+ },
"selectAPlan": {
"message": "Seleziona un piano"
},
@@ -10285,8 +10318,8 @@
"organizationUserDeletedDesc": {
"message": "L'utente è stato rimosso dall'organizzazione e tutti i dati utente associati sono stati eliminati."
},
- "deletedUserId": {
- "message": "Utente $ID$ eliminato da un proprietario o da un amministratore",
+ "deletedUserIdEventMessage": {
+ "message": "Deleted user $ID$",
"placeholders": {
"id": {
"content": "$1",
@@ -10396,8 +10429,8 @@
"domainStatusUnderVerification": {
"message": "In attesa di verifica"
},
- "claimedDomainsDesc": {
- "message": "Richiedi un dominio per acquisire la proprietà di tutti gli account membri il cui indirizzo email corrisponde al dominio. I membri saranno in grado di saltare l'identificatore SSO durante l'accesso. Gli amministratori potranno anche eliminare gli account membri."
+ "claimedDomainsDescription": {
+ "message": "Claim a domain to own member accounts. The SSO identifier page will be skipped during login for members with claimed domains and administrators will be able to delete claimed accounts."
},
"invalidDomainNameClaimMessage": {
"message": "L'input non è in un formato valido. Formato: ilmiodominio.com. I sotto-domini richiedono voci separate."
@@ -10896,5 +10929,56 @@
"example": "12-3456789"
}
}
+ },
+ "unpaidInvoices": {
+ "message": "Unpaid invoices"
+ },
+ "unpaidInvoicesForServiceUser": {
+ "message": "Your subscription has not been paid. Contact your provider administrator to restore service to you and your clients.",
+ "description": "A message shown in a non-dismissible dialog to service users of unpaid providers."
+ },
+ "providerSuspended": {
+ "message": "$PROVIDER$ is suspended",
+ "placeholders": {
+ "provider": {
+ "content": "$1",
+ "example": "Acme Industries"
+ }
+ }
+ },
+ "restoreProviderPortalAccessViaCustomerSupport": {
+ "message": "To restore access to your provider portal, contact Bitwarden Customer Support to renew your subscription.",
+ "description": "A message shown in a non-dismissible dialog to any user of a suspended providers."
+ },
+ "restoreProviderPortalAccessViaPaymentMethod": {
+ "message": "Your subscription has not been paid. To restore service to you and your clients, add a payment method by $CANCELLATION_DATE$.",
+ "placeholders": {
+ "cancellation_date": {
+ "content": "$1",
+ "example": "07/10/2025"
+ }
+ },
+ "description": "A message shown in a non-dismissible dialog to admins of unpaid providers."
+ },
+ "subscribetoEnterprise": {
+ "message": "Subscribe to $PLAN$",
+ "placeholders": {
+ "plan": {
+ "content": "$1",
+ "example": "Teams"
+ }
+ }
+ },
+ "subscribeEnterpriseSubtitle": {
+ "message": "Your 7-day $PLAN$ trial starts today. Add a payment method now to continue using these features after your trial ends: ",
+ "placeholders": {
+ "plan": {
+ "content": "$1",
+ "example": "Teams"
+ }
+ }
+ },
+ "unlimitedSecretsAndProjects": {
+ "message": "Unlimited secrets and projects"
}
-}
+}
\ No newline at end of file
diff --git a/apps/web/src/locales/ja/messages.json b/apps/web/src/locales/ja/messages.json
index b23e971a8ae..317dd8bca3a 100644
--- a/apps/web/src/locales/ja/messages.json
+++ b/apps/web/src/locales/ja/messages.json
@@ -568,6 +568,9 @@
"cancel": {
"message": "キャンセル"
},
+ "later": {
+ "message": "Later"
+ },
"canceled": {
"message": "キャンセルしました"
},
@@ -626,6 +629,9 @@
"searchGroups": {
"message": "グループを検索"
},
+ "resetSearch": {
+ "message": "Reset search"
+ },
"allItems": {
"message": "全てのアイテム"
},
@@ -4626,6 +4632,9 @@
"receiveMarketingEmailsV2": {
"message": "Bitwarden からメールでアドバイスやお知らせ、リサーチの機会を受け取りましょう。"
},
+ "subscribe": {
+ "message": "Subscribe"
+ },
"unsubscribe": {
"message": "配信停止"
},
@@ -9465,6 +9474,12 @@
"deviceManagementDesc": {
"message": "Configure device management for Bitwarden using the implementation guide for your platform."
},
+ "crowdstrikeEventIntegrationDesc": {
+ "message": "Send event data to your Logscale instance"
+ },
+ "failedToSaveIntegration": {
+ "message": "Failed to save integration. Please try again later."
+ },
"deviceIdMissing": {
"message": "Device ID is missing"
},
@@ -9480,6 +9495,15 @@
"reopenLinkOnDesktop": {
"message": "Reopen this link from your email on a desktop."
},
+ "connectIntegrationButtonDesc": {
+ "message": "Connect $INTEGRATION$",
+ "placeholders": {
+ "integration": {
+ "content": "$1",
+ "example": "Crowdstrike"
+ }
+ }
+ },
"integrationCardTooltip": {
"message": "Launch $INTEGRATION$ implementation guide.",
"placeholders": {
@@ -9537,6 +9561,15 @@
"createNewClientToManageAsProvider": {
"message": "プロバイダーとして管理するための新しいクライアント組織を作成します。次の請求サイクルに追加のシートが反映されます。"
},
+ "url": {
+ "message": "URL"
+ },
+ "bearerToken": {
+ "message": "Bearer Token"
+ },
+ "index": {
+ "message": "Index"
+ },
"selectAPlan": {
"message": "プランを選択"
},
@@ -10285,8 +10318,8 @@
"organizationUserDeletedDesc": {
"message": "The user was removed from the organization and all associated user data has been deleted."
},
- "deletedUserId": {
- "message": "Deleted user $ID$ - an owner / admin deleted the user account",
+ "deletedUserIdEventMessage": {
+ "message": "Deleted user $ID$",
"placeholders": {
"id": {
"content": "$1",
@@ -10396,8 +10429,8 @@
"domainStatusUnderVerification": {
"message": "Under verification"
},
- "claimedDomainsDesc": {
- "message": "Claim a domain to own all member accounts whose email address matches the domain. Members will be able to skip the SSO identifier when logging in. Administrators will also be able to delete member accounts."
+ "claimedDomainsDescription": {
+ "message": "Claim a domain to own member accounts. The SSO identifier page will be skipped during login for members with claimed domains and administrators will be able to delete claimed accounts."
},
"invalidDomainNameClaimMessage": {
"message": "Input is not a valid format. Format: mydomain.com. Subdomains require separate entries to be claimed."
@@ -10896,5 +10929,56 @@
"example": "12-3456789"
}
}
+ },
+ "unpaidInvoices": {
+ "message": "Unpaid invoices"
+ },
+ "unpaidInvoicesForServiceUser": {
+ "message": "Your subscription has not been paid. Contact your provider administrator to restore service to you and your clients.",
+ "description": "A message shown in a non-dismissible dialog to service users of unpaid providers."
+ },
+ "providerSuspended": {
+ "message": "$PROVIDER$ is suspended",
+ "placeholders": {
+ "provider": {
+ "content": "$1",
+ "example": "Acme Industries"
+ }
+ }
+ },
+ "restoreProviderPortalAccessViaCustomerSupport": {
+ "message": "To restore access to your provider portal, contact Bitwarden Customer Support to renew your subscription.",
+ "description": "A message shown in a non-dismissible dialog to any user of a suspended providers."
+ },
+ "restoreProviderPortalAccessViaPaymentMethod": {
+ "message": "Your subscription has not been paid. To restore service to you and your clients, add a payment method by $CANCELLATION_DATE$.",
+ "placeholders": {
+ "cancellation_date": {
+ "content": "$1",
+ "example": "07/10/2025"
+ }
+ },
+ "description": "A message shown in a non-dismissible dialog to admins of unpaid providers."
+ },
+ "subscribetoEnterprise": {
+ "message": "Subscribe to $PLAN$",
+ "placeholders": {
+ "plan": {
+ "content": "$1",
+ "example": "Teams"
+ }
+ }
+ },
+ "subscribeEnterpriseSubtitle": {
+ "message": "Your 7-day $PLAN$ trial starts today. Add a payment method now to continue using these features after your trial ends: ",
+ "placeholders": {
+ "plan": {
+ "content": "$1",
+ "example": "Teams"
+ }
+ }
+ },
+ "unlimitedSecretsAndProjects": {
+ "message": "Unlimited secrets and projects"
}
-}
+}
\ No newline at end of file
diff --git a/apps/web/src/locales/ka/messages.json b/apps/web/src/locales/ka/messages.json
index 30cd7a8b70f..1623582257a 100644
--- a/apps/web/src/locales/ka/messages.json
+++ b/apps/web/src/locales/ka/messages.json
@@ -568,6 +568,9 @@
"cancel": {
"message": "გაუქმება"
},
+ "later": {
+ "message": "Later"
+ },
"canceled": {
"message": "გაუქმებულია"
},
@@ -626,6 +629,9 @@
"searchGroups": {
"message": "ძებნა ჯგუფების"
},
+ "resetSearch": {
+ "message": "Reset search"
+ },
"allItems": {
"message": "ყველა საგნები"
},
@@ -4626,6 +4632,9 @@
"receiveMarketingEmailsV2": {
"message": "Get advice, announcements, and research opportunities from Bitwarden in your inbox."
},
+ "subscribe": {
+ "message": "Subscribe"
+ },
"unsubscribe": {
"message": "Unsubscribe"
},
@@ -9465,6 +9474,12 @@
"deviceManagementDesc": {
"message": "Configure device management for Bitwarden using the implementation guide for your platform."
},
+ "crowdstrikeEventIntegrationDesc": {
+ "message": "Send event data to your Logscale instance"
+ },
+ "failedToSaveIntegration": {
+ "message": "Failed to save integration. Please try again later."
+ },
"deviceIdMissing": {
"message": "Device ID is missing"
},
@@ -9480,6 +9495,15 @@
"reopenLinkOnDesktop": {
"message": "Reopen this link from your email on a desktop."
},
+ "connectIntegrationButtonDesc": {
+ "message": "Connect $INTEGRATION$",
+ "placeholders": {
+ "integration": {
+ "content": "$1",
+ "example": "Crowdstrike"
+ }
+ }
+ },
"integrationCardTooltip": {
"message": "Launch $INTEGRATION$ implementation guide.",
"placeholders": {
@@ -9537,6 +9561,15 @@
"createNewClientToManageAsProvider": {
"message": "Create a new client organization to manage as a Provider. Additional seats will be reflected in the next billing cycle."
},
+ "url": {
+ "message": "URL"
+ },
+ "bearerToken": {
+ "message": "Bearer Token"
+ },
+ "index": {
+ "message": "Index"
+ },
"selectAPlan": {
"message": "Select a plan"
},
@@ -10285,8 +10318,8 @@
"organizationUserDeletedDesc": {
"message": "The user was removed from the organization and all associated user data has been deleted."
},
- "deletedUserId": {
- "message": "Deleted user $ID$ - an owner / admin deleted the user account",
+ "deletedUserIdEventMessage": {
+ "message": "Deleted user $ID$",
"placeholders": {
"id": {
"content": "$1",
@@ -10396,8 +10429,8 @@
"domainStatusUnderVerification": {
"message": "Under verification"
},
- "claimedDomainsDesc": {
- "message": "Claim a domain to own all member accounts whose email address matches the domain. Members will be able to skip the SSO identifier when logging in. Administrators will also be able to delete member accounts."
+ "claimedDomainsDescription": {
+ "message": "Claim a domain to own member accounts. The SSO identifier page will be skipped during login for members with claimed domains and administrators will be able to delete claimed accounts."
},
"invalidDomainNameClaimMessage": {
"message": "Input is not a valid format. Format: mydomain.com. Subdomains require separate entries to be claimed."
@@ -10896,5 +10929,56 @@
"example": "12-3456789"
}
}
+ },
+ "unpaidInvoices": {
+ "message": "Unpaid invoices"
+ },
+ "unpaidInvoicesForServiceUser": {
+ "message": "Your subscription has not been paid. Contact your provider administrator to restore service to you and your clients.",
+ "description": "A message shown in a non-dismissible dialog to service users of unpaid providers."
+ },
+ "providerSuspended": {
+ "message": "$PROVIDER$ is suspended",
+ "placeholders": {
+ "provider": {
+ "content": "$1",
+ "example": "Acme Industries"
+ }
+ }
+ },
+ "restoreProviderPortalAccessViaCustomerSupport": {
+ "message": "To restore access to your provider portal, contact Bitwarden Customer Support to renew your subscription.",
+ "description": "A message shown in a non-dismissible dialog to any user of a suspended providers."
+ },
+ "restoreProviderPortalAccessViaPaymentMethod": {
+ "message": "Your subscription has not been paid. To restore service to you and your clients, add a payment method by $CANCELLATION_DATE$.",
+ "placeholders": {
+ "cancellation_date": {
+ "content": "$1",
+ "example": "07/10/2025"
+ }
+ },
+ "description": "A message shown in a non-dismissible dialog to admins of unpaid providers."
+ },
+ "subscribetoEnterprise": {
+ "message": "Subscribe to $PLAN$",
+ "placeholders": {
+ "plan": {
+ "content": "$1",
+ "example": "Teams"
+ }
+ }
+ },
+ "subscribeEnterpriseSubtitle": {
+ "message": "Your 7-day $PLAN$ trial starts today. Add a payment method now to continue using these features after your trial ends: ",
+ "placeholders": {
+ "plan": {
+ "content": "$1",
+ "example": "Teams"
+ }
+ }
+ },
+ "unlimitedSecretsAndProjects": {
+ "message": "Unlimited secrets and projects"
}
-}
+}
\ No newline at end of file
diff --git a/apps/web/src/locales/km/messages.json b/apps/web/src/locales/km/messages.json
index 79d58617943..24e3fc8075f 100644
--- a/apps/web/src/locales/km/messages.json
+++ b/apps/web/src/locales/km/messages.json
@@ -568,6 +568,9 @@
"cancel": {
"message": "Cancel"
},
+ "later": {
+ "message": "Later"
+ },
"canceled": {
"message": "Canceled"
},
@@ -626,6 +629,9 @@
"searchGroups": {
"message": "Search groups"
},
+ "resetSearch": {
+ "message": "Reset search"
+ },
"allItems": {
"message": "All items"
},
@@ -4626,6 +4632,9 @@
"receiveMarketingEmailsV2": {
"message": "Get advice, announcements, and research opportunities from Bitwarden in your inbox."
},
+ "subscribe": {
+ "message": "Subscribe"
+ },
"unsubscribe": {
"message": "Unsubscribe"
},
@@ -9465,6 +9474,12 @@
"deviceManagementDesc": {
"message": "Configure device management for Bitwarden using the implementation guide for your platform."
},
+ "crowdstrikeEventIntegrationDesc": {
+ "message": "Send event data to your Logscale instance"
+ },
+ "failedToSaveIntegration": {
+ "message": "Failed to save integration. Please try again later."
+ },
"deviceIdMissing": {
"message": "Device ID is missing"
},
@@ -9480,6 +9495,15 @@
"reopenLinkOnDesktop": {
"message": "Reopen this link from your email on a desktop."
},
+ "connectIntegrationButtonDesc": {
+ "message": "Connect $INTEGRATION$",
+ "placeholders": {
+ "integration": {
+ "content": "$1",
+ "example": "Crowdstrike"
+ }
+ }
+ },
"integrationCardTooltip": {
"message": "Launch $INTEGRATION$ implementation guide.",
"placeholders": {
@@ -9537,6 +9561,15 @@
"createNewClientToManageAsProvider": {
"message": "Create a new client organization to manage as a Provider. Additional seats will be reflected in the next billing cycle."
},
+ "url": {
+ "message": "URL"
+ },
+ "bearerToken": {
+ "message": "Bearer Token"
+ },
+ "index": {
+ "message": "Index"
+ },
"selectAPlan": {
"message": "Select a plan"
},
@@ -10285,8 +10318,8 @@
"organizationUserDeletedDesc": {
"message": "The user was removed from the organization and all associated user data has been deleted."
},
- "deletedUserId": {
- "message": "Deleted user $ID$ - an owner / admin deleted the user account",
+ "deletedUserIdEventMessage": {
+ "message": "Deleted user $ID$",
"placeholders": {
"id": {
"content": "$1",
@@ -10396,8 +10429,8 @@
"domainStatusUnderVerification": {
"message": "Under verification"
},
- "claimedDomainsDesc": {
- "message": "Claim a domain to own all member accounts whose email address matches the domain. Members will be able to skip the SSO identifier when logging in. Administrators will also be able to delete member accounts."
+ "claimedDomainsDescription": {
+ "message": "Claim a domain to own member accounts. The SSO identifier page will be skipped during login for members with claimed domains and administrators will be able to delete claimed accounts."
},
"invalidDomainNameClaimMessage": {
"message": "Input is not a valid format. Format: mydomain.com. Subdomains require separate entries to be claimed."
@@ -10896,5 +10929,56 @@
"example": "12-3456789"
}
}
+ },
+ "unpaidInvoices": {
+ "message": "Unpaid invoices"
+ },
+ "unpaidInvoicesForServiceUser": {
+ "message": "Your subscription has not been paid. Contact your provider administrator to restore service to you and your clients.",
+ "description": "A message shown in a non-dismissible dialog to service users of unpaid providers."
+ },
+ "providerSuspended": {
+ "message": "$PROVIDER$ is suspended",
+ "placeholders": {
+ "provider": {
+ "content": "$1",
+ "example": "Acme Industries"
+ }
+ }
+ },
+ "restoreProviderPortalAccessViaCustomerSupport": {
+ "message": "To restore access to your provider portal, contact Bitwarden Customer Support to renew your subscription.",
+ "description": "A message shown in a non-dismissible dialog to any user of a suspended providers."
+ },
+ "restoreProviderPortalAccessViaPaymentMethod": {
+ "message": "Your subscription has not been paid. To restore service to you and your clients, add a payment method by $CANCELLATION_DATE$.",
+ "placeholders": {
+ "cancellation_date": {
+ "content": "$1",
+ "example": "07/10/2025"
+ }
+ },
+ "description": "A message shown in a non-dismissible dialog to admins of unpaid providers."
+ },
+ "subscribetoEnterprise": {
+ "message": "Subscribe to $PLAN$",
+ "placeholders": {
+ "plan": {
+ "content": "$1",
+ "example": "Teams"
+ }
+ }
+ },
+ "subscribeEnterpriseSubtitle": {
+ "message": "Your 7-day $PLAN$ trial starts today. Add a payment method now to continue using these features after your trial ends: ",
+ "placeholders": {
+ "plan": {
+ "content": "$1",
+ "example": "Teams"
+ }
+ }
+ },
+ "unlimitedSecretsAndProjects": {
+ "message": "Unlimited secrets and projects"
}
-}
+}
\ No newline at end of file
diff --git a/apps/web/src/locales/kn/messages.json b/apps/web/src/locales/kn/messages.json
index 787da7b4dc9..00dee3d34b8 100644
--- a/apps/web/src/locales/kn/messages.json
+++ b/apps/web/src/locales/kn/messages.json
@@ -568,6 +568,9 @@
"cancel": {
"message": "ರದ್ದು"
},
+ "later": {
+ "message": "Later"
+ },
"canceled": {
"message": "ರದ್ದುಗೊಳಿಸಲಾಗಿದೆ"
},
@@ -626,6 +629,9 @@
"searchGroups": {
"message": "Search groups"
},
+ "resetSearch": {
+ "message": "Reset search"
+ },
"allItems": {
"message": "ಎಲ್ಲಾ ವಸ್ತುಗಳು"
},
@@ -4626,6 +4632,9 @@
"receiveMarketingEmailsV2": {
"message": "Get advice, announcements, and research opportunities from Bitwarden in your inbox."
},
+ "subscribe": {
+ "message": "Subscribe"
+ },
"unsubscribe": {
"message": "Unsubscribe"
},
@@ -9465,6 +9474,12 @@
"deviceManagementDesc": {
"message": "Configure device management for Bitwarden using the implementation guide for your platform."
},
+ "crowdstrikeEventIntegrationDesc": {
+ "message": "Send event data to your Logscale instance"
+ },
+ "failedToSaveIntegration": {
+ "message": "Failed to save integration. Please try again later."
+ },
"deviceIdMissing": {
"message": "Device ID is missing"
},
@@ -9480,6 +9495,15 @@
"reopenLinkOnDesktop": {
"message": "Reopen this link from your email on a desktop."
},
+ "connectIntegrationButtonDesc": {
+ "message": "Connect $INTEGRATION$",
+ "placeholders": {
+ "integration": {
+ "content": "$1",
+ "example": "Crowdstrike"
+ }
+ }
+ },
"integrationCardTooltip": {
"message": "Launch $INTEGRATION$ implementation guide.",
"placeholders": {
@@ -9537,6 +9561,15 @@
"createNewClientToManageAsProvider": {
"message": "Create a new client organization to manage as a Provider. Additional seats will be reflected in the next billing cycle."
},
+ "url": {
+ "message": "URL"
+ },
+ "bearerToken": {
+ "message": "Bearer Token"
+ },
+ "index": {
+ "message": "Index"
+ },
"selectAPlan": {
"message": "Select a plan"
},
@@ -10285,8 +10318,8 @@
"organizationUserDeletedDesc": {
"message": "The user was removed from the organization and all associated user data has been deleted."
},
- "deletedUserId": {
- "message": "Deleted user $ID$ - an owner / admin deleted the user account",
+ "deletedUserIdEventMessage": {
+ "message": "Deleted user $ID$",
"placeholders": {
"id": {
"content": "$1",
@@ -10396,8 +10429,8 @@
"domainStatusUnderVerification": {
"message": "Under verification"
},
- "claimedDomainsDesc": {
- "message": "Claim a domain to own all member accounts whose email address matches the domain. Members will be able to skip the SSO identifier when logging in. Administrators will also be able to delete member accounts."
+ "claimedDomainsDescription": {
+ "message": "Claim a domain to own member accounts. The SSO identifier page will be skipped during login for members with claimed domains and administrators will be able to delete claimed accounts."
},
"invalidDomainNameClaimMessage": {
"message": "Input is not a valid format. Format: mydomain.com. Subdomains require separate entries to be claimed."
@@ -10896,5 +10929,56 @@
"example": "12-3456789"
}
}
+ },
+ "unpaidInvoices": {
+ "message": "Unpaid invoices"
+ },
+ "unpaidInvoicesForServiceUser": {
+ "message": "Your subscription has not been paid. Contact your provider administrator to restore service to you and your clients.",
+ "description": "A message shown in a non-dismissible dialog to service users of unpaid providers."
+ },
+ "providerSuspended": {
+ "message": "$PROVIDER$ is suspended",
+ "placeholders": {
+ "provider": {
+ "content": "$1",
+ "example": "Acme Industries"
+ }
+ }
+ },
+ "restoreProviderPortalAccessViaCustomerSupport": {
+ "message": "To restore access to your provider portal, contact Bitwarden Customer Support to renew your subscription.",
+ "description": "A message shown in a non-dismissible dialog to any user of a suspended providers."
+ },
+ "restoreProviderPortalAccessViaPaymentMethod": {
+ "message": "Your subscription has not been paid. To restore service to you and your clients, add a payment method by $CANCELLATION_DATE$.",
+ "placeholders": {
+ "cancellation_date": {
+ "content": "$1",
+ "example": "07/10/2025"
+ }
+ },
+ "description": "A message shown in a non-dismissible dialog to admins of unpaid providers."
+ },
+ "subscribetoEnterprise": {
+ "message": "Subscribe to $PLAN$",
+ "placeholders": {
+ "plan": {
+ "content": "$1",
+ "example": "Teams"
+ }
+ }
+ },
+ "subscribeEnterpriseSubtitle": {
+ "message": "Your 7-day $PLAN$ trial starts today. Add a payment method now to continue using these features after your trial ends: ",
+ "placeholders": {
+ "plan": {
+ "content": "$1",
+ "example": "Teams"
+ }
+ }
+ },
+ "unlimitedSecretsAndProjects": {
+ "message": "Unlimited secrets and projects"
}
-}
+}
\ No newline at end of file
diff --git a/apps/web/src/locales/ko/messages.json b/apps/web/src/locales/ko/messages.json
index af7442ce153..8c79f02bd7d 100644
--- a/apps/web/src/locales/ko/messages.json
+++ b/apps/web/src/locales/ko/messages.json
@@ -568,6 +568,9 @@
"cancel": {
"message": "취소"
},
+ "later": {
+ "message": "Later"
+ },
"canceled": {
"message": "취소됨"
},
@@ -626,6 +629,9 @@
"searchGroups": {
"message": "그룹 검색"
},
+ "resetSearch": {
+ "message": "Reset search"
+ },
"allItems": {
"message": "모든 항목"
},
@@ -4626,6 +4632,9 @@
"receiveMarketingEmailsV2": {
"message": "Get advice, announcements, and research opportunities from Bitwarden in your inbox."
},
+ "subscribe": {
+ "message": "Subscribe"
+ },
"unsubscribe": {
"message": "Unsubscribe"
},
@@ -9465,6 +9474,12 @@
"deviceManagementDesc": {
"message": "Configure device management for Bitwarden using the implementation guide for your platform."
},
+ "crowdstrikeEventIntegrationDesc": {
+ "message": "Send event data to your Logscale instance"
+ },
+ "failedToSaveIntegration": {
+ "message": "Failed to save integration. Please try again later."
+ },
"deviceIdMissing": {
"message": "Device ID is missing"
},
@@ -9480,6 +9495,15 @@
"reopenLinkOnDesktop": {
"message": "Reopen this link from your email on a desktop."
},
+ "connectIntegrationButtonDesc": {
+ "message": "Connect $INTEGRATION$",
+ "placeholders": {
+ "integration": {
+ "content": "$1",
+ "example": "Crowdstrike"
+ }
+ }
+ },
"integrationCardTooltip": {
"message": "Launch $INTEGRATION$ implementation guide.",
"placeholders": {
@@ -9537,6 +9561,15 @@
"createNewClientToManageAsProvider": {
"message": "Create a new client organization to manage as a Provider. Additional seats will be reflected in the next billing cycle."
},
+ "url": {
+ "message": "URL"
+ },
+ "bearerToken": {
+ "message": "Bearer Token"
+ },
+ "index": {
+ "message": "Index"
+ },
"selectAPlan": {
"message": "Select a plan"
},
@@ -10285,8 +10318,8 @@
"organizationUserDeletedDesc": {
"message": "The user was removed from the organization and all associated user data has been deleted."
},
- "deletedUserId": {
- "message": "Deleted user $ID$ - an owner / admin deleted the user account",
+ "deletedUserIdEventMessage": {
+ "message": "Deleted user $ID$",
"placeholders": {
"id": {
"content": "$1",
@@ -10396,8 +10429,8 @@
"domainStatusUnderVerification": {
"message": "Under verification"
},
- "claimedDomainsDesc": {
- "message": "Claim a domain to own all member accounts whose email address matches the domain. Members will be able to skip the SSO identifier when logging in. Administrators will also be able to delete member accounts."
+ "claimedDomainsDescription": {
+ "message": "Claim a domain to own member accounts. The SSO identifier page will be skipped during login for members with claimed domains and administrators will be able to delete claimed accounts."
},
"invalidDomainNameClaimMessage": {
"message": "Input is not a valid format. Format: mydomain.com. Subdomains require separate entries to be claimed."
@@ -10896,5 +10929,56 @@
"example": "12-3456789"
}
}
+ },
+ "unpaidInvoices": {
+ "message": "Unpaid invoices"
+ },
+ "unpaidInvoicesForServiceUser": {
+ "message": "Your subscription has not been paid. Contact your provider administrator to restore service to you and your clients.",
+ "description": "A message shown in a non-dismissible dialog to service users of unpaid providers."
+ },
+ "providerSuspended": {
+ "message": "$PROVIDER$ is suspended",
+ "placeholders": {
+ "provider": {
+ "content": "$1",
+ "example": "Acme Industries"
+ }
+ }
+ },
+ "restoreProviderPortalAccessViaCustomerSupport": {
+ "message": "To restore access to your provider portal, contact Bitwarden Customer Support to renew your subscription.",
+ "description": "A message shown in a non-dismissible dialog to any user of a suspended providers."
+ },
+ "restoreProviderPortalAccessViaPaymentMethod": {
+ "message": "Your subscription has not been paid. To restore service to you and your clients, add a payment method by $CANCELLATION_DATE$.",
+ "placeholders": {
+ "cancellation_date": {
+ "content": "$1",
+ "example": "07/10/2025"
+ }
+ },
+ "description": "A message shown in a non-dismissible dialog to admins of unpaid providers."
+ },
+ "subscribetoEnterprise": {
+ "message": "Subscribe to $PLAN$",
+ "placeholders": {
+ "plan": {
+ "content": "$1",
+ "example": "Teams"
+ }
+ }
+ },
+ "subscribeEnterpriseSubtitle": {
+ "message": "Your 7-day $PLAN$ trial starts today. Add a payment method now to continue using these features after your trial ends: ",
+ "placeholders": {
+ "plan": {
+ "content": "$1",
+ "example": "Teams"
+ }
+ }
+ },
+ "unlimitedSecretsAndProjects": {
+ "message": "Unlimited secrets and projects"
}
-}
+}
\ No newline at end of file
diff --git a/apps/web/src/locales/lv/messages.json b/apps/web/src/locales/lv/messages.json
index ff80c724835..722eef1a68f 100644
--- a/apps/web/src/locales/lv/messages.json
+++ b/apps/web/src/locales/lv/messages.json
@@ -568,6 +568,9 @@
"cancel": {
"message": "Atcelt"
},
+ "later": {
+ "message": "Vēlāk"
+ },
"canceled": {
"message": "Atcelts"
},
@@ -626,6 +629,9 @@
"searchGroups": {
"message": "Meklēt kopas"
},
+ "resetSearch": {
+ "message": "Atiestatīt meklēšanu"
+ },
"allItems": {
"message": "Visi vienumi"
},
@@ -4626,6 +4632,9 @@
"receiveMarketingEmailsV2": {
"message": "Iegūt savā iesūtnē padomus, paziņojumus un izpētes iespējas no Bitwarden."
},
+ "subscribe": {
+ "message": "Abonēt"
+ },
"unsubscribe": {
"message": "Atteikt abonēšanu"
},
@@ -9465,6 +9474,12 @@
"deviceManagementDesc": {
"message": "Konfigurēt ierīču pārvaldību Bitwarden, izmantojot operētājsistēmai atbilstošas ieviešanas norādes."
},
+ "crowdstrikeEventIntegrationDesc": {
+ "message": "Nosūtīt notikum udatus uz savu Logscale instanci"
+ },
+ "failedToSaveIntegration": {
+ "message": "Failed to save integration. Please try again later."
+ },
"deviceIdMissing": {
"message": "Trūkst ierīces Id"
},
@@ -9480,6 +9495,15 @@
"reopenLinkOnDesktop": {
"message": "Šī saite e-pastā atkārtoti jāatver datorā."
},
+ "connectIntegrationButtonDesc": {
+ "message": "Pieslēgt $INTEGRATION$",
+ "placeholders": {
+ "integration": {
+ "content": "$1",
+ "example": "Crowdstrike"
+ }
+ }
+ },
"integrationCardTooltip": {
"message": "Palaist $INTEGRATION$ ieviešanas norādes.",
"placeholders": {
@@ -9537,6 +9561,15 @@
"createNewClientToManageAsProvider": {
"message": "Izveidot jaunu klienta apvienību, ko pārvaldīt kā nodrošinātājam. Papildu vietas tiks atspoguļotas nākamajā norēķinu posmā."
},
+ "url": {
+ "message": "URL"
+ },
+ "bearerToken": {
+ "message": "Bearer Token"
+ },
+ "index": {
+ "message": "Index"
+ },
"selectAPlan": {
"message": "Atlasīt plānu"
},
@@ -10285,8 +10318,8 @@
"organizationUserDeletedDesc": {
"message": "Lietotājs tika noņemts no apvienības, un visa saistītā lietotāja informācija tika izdzēsta."
},
- "deletedUserId": {
- "message": "Izdzēsts lietotājs $ID$ - īpašnieks/pārvaldītājs izdzēsa lietotāja kontu",
+ "deletedUserIdEventMessage": {
+ "message": "Izdzēsts lietotājs $ID$",
"placeholders": {
"id": {
"content": "$1",
@@ -10396,8 +10429,8 @@
"domainStatusUnderVerification": {
"message": "Apliecināšanā"
},
- "claimedDomainsDesc": {
- "message": "Pieteikt domēnu, lai iegūtu visu dalībnieku kontu, kuru e-pasta adrese atbilst domēnam, īpašumtiesības. Dalībnieki piesakoties varēs izlaist SSO identifikatoru. Pārvaldītāji varēs arī izdzēst dalībnieku kontus."
+ "claimedDomainsDescription": {
+ "message": "Iegūsti domēna piederību, lai iegūtu dalībnieku kontu īpašumtiesības. SSO identificētāja lapa tiks izlaista, kad pieteiksies dalībnieki, kuru kontu piederība ir atkarīga no domēna, un pārvaldītāji varēs izdzēst šāda veida kontus."
},
"invalidDomainNameClaimMessage": {
"message": "Ievadītā vērtība ir nederīga. Piemēram: mansdomens.lv. Apakšdomēnu pieteikšanai ir nepieciešami atsevišķi ieraksti."
@@ -10896,5 +10929,56 @@
"example": "12-3456789"
}
}
+ },
+ "unpaidInvoices": {
+ "message": "Neapmaksāti rēķini"
+ },
+ "unpaidInvoicesForServiceUser": {
+ "message": "Tavs abonements nav apmaksāts. Jāsazinās ar sava pakalpojuma sniedzēja pārvaldītāju, lai atjaunotu pakalpojumu sev un saviem klientiem.",
+ "description": "A message shown in a non-dismissible dialog to service users of unpaid providers."
+ },
+ "providerSuspended": {
+ "message": "$PROVIDER$ ir apturēts",
+ "placeholders": {
+ "provider": {
+ "content": "$1",
+ "example": "Acme Industries"
+ }
+ }
+ },
+ "restoreProviderPortalAccessViaCustomerSupport": {
+ "message": "Lai atjaunotu piekļuvi savam pakalpojuma sniedzēja portālam, jāsazinās ar Bitwarden klientu atbalstu, lai atjaunotu savu abonementu.",
+ "description": "A message shown in a non-dismissible dialog to any user of a suspended providers."
+ },
+ "restoreProviderPortalAccessViaPaymentMethod": {
+ "message": "Tavs abonements nav apmaksāts. Lai atjaunotu pakalpojumu sev un saviem klientiem, līdz $CANCELLATION_DATE$ jāpievieno maksājuma veids.",
+ "placeholders": {
+ "cancellation_date": {
+ "content": "$1",
+ "example": "07/10/2025"
+ }
+ },
+ "description": "A message shown in a non-dismissible dialog to admins of unpaid providers."
+ },
+ "subscribetoEnterprise": {
+ "message": "Abonēt $PLAN$",
+ "placeholders": {
+ "plan": {
+ "content": "$1",
+ "example": "Teams"
+ }
+ }
+ },
+ "subscribeEnterpriseSubtitle": {
+ "message": "Tavs $PLAN$ 7 dienu izmēģinājums sākas šodien. Tagad pievieno maksājumu veidu, lai turpinātu izmantot šīs iespējas pēc izmēģinājuma laika beigām: ",
+ "placeholders": {
+ "plan": {
+ "content": "$1",
+ "example": "Teams"
+ }
+ }
+ },
+ "unlimitedSecretsAndProjects": {
+ "message": "Neierobežoti noslēpumi un projekti"
}
-}
+}
\ No newline at end of file
diff --git a/apps/web/src/locales/ml/messages.json b/apps/web/src/locales/ml/messages.json
index 51dc8b6cdf9..49f7e3257ab 100644
--- a/apps/web/src/locales/ml/messages.json
+++ b/apps/web/src/locales/ml/messages.json
@@ -568,6 +568,9 @@
"cancel": {
"message": "റദ്ദാക്കുക"
},
+ "later": {
+ "message": "Later"
+ },
"canceled": {
"message": "റദ്ദാക്കി"
},
@@ -626,6 +629,9 @@
"searchGroups": {
"message": "Search groups"
},
+ "resetSearch": {
+ "message": "Reset search"
+ },
"allItems": {
"message": "എല്ലാ ഇനങ്ങൾ"
},
@@ -4626,6 +4632,9 @@
"receiveMarketingEmailsV2": {
"message": "Get advice, announcements, and research opportunities from Bitwarden in your inbox."
},
+ "subscribe": {
+ "message": "Subscribe"
+ },
"unsubscribe": {
"message": "Unsubscribe"
},
@@ -9465,6 +9474,12 @@
"deviceManagementDesc": {
"message": "Configure device management for Bitwarden using the implementation guide for your platform."
},
+ "crowdstrikeEventIntegrationDesc": {
+ "message": "Send event data to your Logscale instance"
+ },
+ "failedToSaveIntegration": {
+ "message": "Failed to save integration. Please try again later."
+ },
"deviceIdMissing": {
"message": "Device ID is missing"
},
@@ -9480,6 +9495,15 @@
"reopenLinkOnDesktop": {
"message": "Reopen this link from your email on a desktop."
},
+ "connectIntegrationButtonDesc": {
+ "message": "Connect $INTEGRATION$",
+ "placeholders": {
+ "integration": {
+ "content": "$1",
+ "example": "Crowdstrike"
+ }
+ }
+ },
"integrationCardTooltip": {
"message": "Launch $INTEGRATION$ implementation guide.",
"placeholders": {
@@ -9537,6 +9561,15 @@
"createNewClientToManageAsProvider": {
"message": "Create a new client organization to manage as a Provider. Additional seats will be reflected in the next billing cycle."
},
+ "url": {
+ "message": "URL"
+ },
+ "bearerToken": {
+ "message": "Bearer Token"
+ },
+ "index": {
+ "message": "Index"
+ },
"selectAPlan": {
"message": "Select a plan"
},
@@ -10285,8 +10318,8 @@
"organizationUserDeletedDesc": {
"message": "The user was removed from the organization and all associated user data has been deleted."
},
- "deletedUserId": {
- "message": "Deleted user $ID$ - an owner / admin deleted the user account",
+ "deletedUserIdEventMessage": {
+ "message": "Deleted user $ID$",
"placeholders": {
"id": {
"content": "$1",
@@ -10396,8 +10429,8 @@
"domainStatusUnderVerification": {
"message": "Under verification"
},
- "claimedDomainsDesc": {
- "message": "Claim a domain to own all member accounts whose email address matches the domain. Members will be able to skip the SSO identifier when logging in. Administrators will also be able to delete member accounts."
+ "claimedDomainsDescription": {
+ "message": "Claim a domain to own member accounts. The SSO identifier page will be skipped during login for members with claimed domains and administrators will be able to delete claimed accounts."
},
"invalidDomainNameClaimMessage": {
"message": "Input is not a valid format. Format: mydomain.com. Subdomains require separate entries to be claimed."
@@ -10896,5 +10929,56 @@
"example": "12-3456789"
}
}
+ },
+ "unpaidInvoices": {
+ "message": "Unpaid invoices"
+ },
+ "unpaidInvoicesForServiceUser": {
+ "message": "Your subscription has not been paid. Contact your provider administrator to restore service to you and your clients.",
+ "description": "A message shown in a non-dismissible dialog to service users of unpaid providers."
+ },
+ "providerSuspended": {
+ "message": "$PROVIDER$ is suspended",
+ "placeholders": {
+ "provider": {
+ "content": "$1",
+ "example": "Acme Industries"
+ }
+ }
+ },
+ "restoreProviderPortalAccessViaCustomerSupport": {
+ "message": "To restore access to your provider portal, contact Bitwarden Customer Support to renew your subscription.",
+ "description": "A message shown in a non-dismissible dialog to any user of a suspended providers."
+ },
+ "restoreProviderPortalAccessViaPaymentMethod": {
+ "message": "Your subscription has not been paid. To restore service to you and your clients, add a payment method by $CANCELLATION_DATE$.",
+ "placeholders": {
+ "cancellation_date": {
+ "content": "$1",
+ "example": "07/10/2025"
+ }
+ },
+ "description": "A message shown in a non-dismissible dialog to admins of unpaid providers."
+ },
+ "subscribetoEnterprise": {
+ "message": "Subscribe to $PLAN$",
+ "placeholders": {
+ "plan": {
+ "content": "$1",
+ "example": "Teams"
+ }
+ }
+ },
+ "subscribeEnterpriseSubtitle": {
+ "message": "Your 7-day $PLAN$ trial starts today. Add a payment method now to continue using these features after your trial ends: ",
+ "placeholders": {
+ "plan": {
+ "content": "$1",
+ "example": "Teams"
+ }
+ }
+ },
+ "unlimitedSecretsAndProjects": {
+ "message": "Unlimited secrets and projects"
}
-}
+}
\ No newline at end of file
diff --git a/apps/web/src/locales/mr/messages.json b/apps/web/src/locales/mr/messages.json
index 86c8a31943d..dec7fd66f1e 100644
--- a/apps/web/src/locales/mr/messages.json
+++ b/apps/web/src/locales/mr/messages.json
@@ -568,6 +568,9 @@
"cancel": {
"message": "रद्द करा"
},
+ "later": {
+ "message": "Later"
+ },
"canceled": {
"message": "Canceled"
},
@@ -626,6 +629,9 @@
"searchGroups": {
"message": "Search groups"
},
+ "resetSearch": {
+ "message": "Reset search"
+ },
"allItems": {
"message": "All items"
},
@@ -4626,6 +4632,9 @@
"receiveMarketingEmailsV2": {
"message": "Get advice, announcements, and research opportunities from Bitwarden in your inbox."
},
+ "subscribe": {
+ "message": "Subscribe"
+ },
"unsubscribe": {
"message": "Unsubscribe"
},
@@ -9465,6 +9474,12 @@
"deviceManagementDesc": {
"message": "Configure device management for Bitwarden using the implementation guide for your platform."
},
+ "crowdstrikeEventIntegrationDesc": {
+ "message": "Send event data to your Logscale instance"
+ },
+ "failedToSaveIntegration": {
+ "message": "Failed to save integration. Please try again later."
+ },
"deviceIdMissing": {
"message": "Device ID is missing"
},
@@ -9480,6 +9495,15 @@
"reopenLinkOnDesktop": {
"message": "Reopen this link from your email on a desktop."
},
+ "connectIntegrationButtonDesc": {
+ "message": "Connect $INTEGRATION$",
+ "placeholders": {
+ "integration": {
+ "content": "$1",
+ "example": "Crowdstrike"
+ }
+ }
+ },
"integrationCardTooltip": {
"message": "Launch $INTEGRATION$ implementation guide.",
"placeholders": {
@@ -9537,6 +9561,15 @@
"createNewClientToManageAsProvider": {
"message": "Create a new client organization to manage as a Provider. Additional seats will be reflected in the next billing cycle."
},
+ "url": {
+ "message": "URL"
+ },
+ "bearerToken": {
+ "message": "Bearer Token"
+ },
+ "index": {
+ "message": "Index"
+ },
"selectAPlan": {
"message": "Select a plan"
},
@@ -10285,8 +10318,8 @@
"organizationUserDeletedDesc": {
"message": "The user was removed from the organization and all associated user data has been deleted."
},
- "deletedUserId": {
- "message": "Deleted user $ID$ - an owner / admin deleted the user account",
+ "deletedUserIdEventMessage": {
+ "message": "Deleted user $ID$",
"placeholders": {
"id": {
"content": "$1",
@@ -10396,8 +10429,8 @@
"domainStatusUnderVerification": {
"message": "Under verification"
},
- "claimedDomainsDesc": {
- "message": "Claim a domain to own all member accounts whose email address matches the domain. Members will be able to skip the SSO identifier when logging in. Administrators will also be able to delete member accounts."
+ "claimedDomainsDescription": {
+ "message": "Claim a domain to own member accounts. The SSO identifier page will be skipped during login for members with claimed domains and administrators will be able to delete claimed accounts."
},
"invalidDomainNameClaimMessage": {
"message": "Input is not a valid format. Format: mydomain.com. Subdomains require separate entries to be claimed."
@@ -10896,5 +10929,56 @@
"example": "12-3456789"
}
}
+ },
+ "unpaidInvoices": {
+ "message": "Unpaid invoices"
+ },
+ "unpaidInvoicesForServiceUser": {
+ "message": "Your subscription has not been paid. Contact your provider administrator to restore service to you and your clients.",
+ "description": "A message shown in a non-dismissible dialog to service users of unpaid providers."
+ },
+ "providerSuspended": {
+ "message": "$PROVIDER$ is suspended",
+ "placeholders": {
+ "provider": {
+ "content": "$1",
+ "example": "Acme Industries"
+ }
+ }
+ },
+ "restoreProviderPortalAccessViaCustomerSupport": {
+ "message": "To restore access to your provider portal, contact Bitwarden Customer Support to renew your subscription.",
+ "description": "A message shown in a non-dismissible dialog to any user of a suspended providers."
+ },
+ "restoreProviderPortalAccessViaPaymentMethod": {
+ "message": "Your subscription has not been paid. To restore service to you and your clients, add a payment method by $CANCELLATION_DATE$.",
+ "placeholders": {
+ "cancellation_date": {
+ "content": "$1",
+ "example": "07/10/2025"
+ }
+ },
+ "description": "A message shown in a non-dismissible dialog to admins of unpaid providers."
+ },
+ "subscribetoEnterprise": {
+ "message": "Subscribe to $PLAN$",
+ "placeholders": {
+ "plan": {
+ "content": "$1",
+ "example": "Teams"
+ }
+ }
+ },
+ "subscribeEnterpriseSubtitle": {
+ "message": "Your 7-day $PLAN$ trial starts today. Add a payment method now to continue using these features after your trial ends: ",
+ "placeholders": {
+ "plan": {
+ "content": "$1",
+ "example": "Teams"
+ }
+ }
+ },
+ "unlimitedSecretsAndProjects": {
+ "message": "Unlimited secrets and projects"
}
-}
+}
\ No newline at end of file
diff --git a/apps/web/src/locales/my/messages.json b/apps/web/src/locales/my/messages.json
index 79d58617943..24e3fc8075f 100644
--- a/apps/web/src/locales/my/messages.json
+++ b/apps/web/src/locales/my/messages.json
@@ -568,6 +568,9 @@
"cancel": {
"message": "Cancel"
},
+ "later": {
+ "message": "Later"
+ },
"canceled": {
"message": "Canceled"
},
@@ -626,6 +629,9 @@
"searchGroups": {
"message": "Search groups"
},
+ "resetSearch": {
+ "message": "Reset search"
+ },
"allItems": {
"message": "All items"
},
@@ -4626,6 +4632,9 @@
"receiveMarketingEmailsV2": {
"message": "Get advice, announcements, and research opportunities from Bitwarden in your inbox."
},
+ "subscribe": {
+ "message": "Subscribe"
+ },
"unsubscribe": {
"message": "Unsubscribe"
},
@@ -9465,6 +9474,12 @@
"deviceManagementDesc": {
"message": "Configure device management for Bitwarden using the implementation guide for your platform."
},
+ "crowdstrikeEventIntegrationDesc": {
+ "message": "Send event data to your Logscale instance"
+ },
+ "failedToSaveIntegration": {
+ "message": "Failed to save integration. Please try again later."
+ },
"deviceIdMissing": {
"message": "Device ID is missing"
},
@@ -9480,6 +9495,15 @@
"reopenLinkOnDesktop": {
"message": "Reopen this link from your email on a desktop."
},
+ "connectIntegrationButtonDesc": {
+ "message": "Connect $INTEGRATION$",
+ "placeholders": {
+ "integration": {
+ "content": "$1",
+ "example": "Crowdstrike"
+ }
+ }
+ },
"integrationCardTooltip": {
"message": "Launch $INTEGRATION$ implementation guide.",
"placeholders": {
@@ -9537,6 +9561,15 @@
"createNewClientToManageAsProvider": {
"message": "Create a new client organization to manage as a Provider. Additional seats will be reflected in the next billing cycle."
},
+ "url": {
+ "message": "URL"
+ },
+ "bearerToken": {
+ "message": "Bearer Token"
+ },
+ "index": {
+ "message": "Index"
+ },
"selectAPlan": {
"message": "Select a plan"
},
@@ -10285,8 +10318,8 @@
"organizationUserDeletedDesc": {
"message": "The user was removed from the organization and all associated user data has been deleted."
},
- "deletedUserId": {
- "message": "Deleted user $ID$ - an owner / admin deleted the user account",
+ "deletedUserIdEventMessage": {
+ "message": "Deleted user $ID$",
"placeholders": {
"id": {
"content": "$1",
@@ -10396,8 +10429,8 @@
"domainStatusUnderVerification": {
"message": "Under verification"
},
- "claimedDomainsDesc": {
- "message": "Claim a domain to own all member accounts whose email address matches the domain. Members will be able to skip the SSO identifier when logging in. Administrators will also be able to delete member accounts."
+ "claimedDomainsDescription": {
+ "message": "Claim a domain to own member accounts. The SSO identifier page will be skipped during login for members with claimed domains and administrators will be able to delete claimed accounts."
},
"invalidDomainNameClaimMessage": {
"message": "Input is not a valid format. Format: mydomain.com. Subdomains require separate entries to be claimed."
@@ -10896,5 +10929,56 @@
"example": "12-3456789"
}
}
+ },
+ "unpaidInvoices": {
+ "message": "Unpaid invoices"
+ },
+ "unpaidInvoicesForServiceUser": {
+ "message": "Your subscription has not been paid. Contact your provider administrator to restore service to you and your clients.",
+ "description": "A message shown in a non-dismissible dialog to service users of unpaid providers."
+ },
+ "providerSuspended": {
+ "message": "$PROVIDER$ is suspended",
+ "placeholders": {
+ "provider": {
+ "content": "$1",
+ "example": "Acme Industries"
+ }
+ }
+ },
+ "restoreProviderPortalAccessViaCustomerSupport": {
+ "message": "To restore access to your provider portal, contact Bitwarden Customer Support to renew your subscription.",
+ "description": "A message shown in a non-dismissible dialog to any user of a suspended providers."
+ },
+ "restoreProviderPortalAccessViaPaymentMethod": {
+ "message": "Your subscription has not been paid. To restore service to you and your clients, add a payment method by $CANCELLATION_DATE$.",
+ "placeholders": {
+ "cancellation_date": {
+ "content": "$1",
+ "example": "07/10/2025"
+ }
+ },
+ "description": "A message shown in a non-dismissible dialog to admins of unpaid providers."
+ },
+ "subscribetoEnterprise": {
+ "message": "Subscribe to $PLAN$",
+ "placeholders": {
+ "plan": {
+ "content": "$1",
+ "example": "Teams"
+ }
+ }
+ },
+ "subscribeEnterpriseSubtitle": {
+ "message": "Your 7-day $PLAN$ trial starts today. Add a payment method now to continue using these features after your trial ends: ",
+ "placeholders": {
+ "plan": {
+ "content": "$1",
+ "example": "Teams"
+ }
+ }
+ },
+ "unlimitedSecretsAndProjects": {
+ "message": "Unlimited secrets and projects"
}
-}
+}
\ No newline at end of file
diff --git a/apps/web/src/locales/nb/messages.json b/apps/web/src/locales/nb/messages.json
index 6018d3664f1..65186458868 100644
--- a/apps/web/src/locales/nb/messages.json
+++ b/apps/web/src/locales/nb/messages.json
@@ -402,7 +402,7 @@
"message": "Frøken"
},
"mx": {
- "message": "Mx"
+ "message": "Edle"
},
"dr": {
"message": "Dr․"
@@ -568,6 +568,9 @@
"cancel": {
"message": "Avbryt"
},
+ "later": {
+ "message": "Senere"
+ },
"canceled": {
"message": "Avbrutt"
},
@@ -626,6 +629,9 @@
"searchGroups": {
"message": "Søk i grupper"
},
+ "resetSearch": {
+ "message": "Reset search"
+ },
"allItems": {
"message": "Alle elementer"
},
@@ -865,7 +871,7 @@
"message": "Kopiér navn"
},
"cardNumber": {
- "message": "card number"
+ "message": "kortnummer"
},
"copyFieldCipherName": {
"message": "Copy $FIELD$, $CIPHERNAME$",
@@ -1508,11 +1514,11 @@
"message": "Autentiseringsapp"
},
"authenticatorAppDescV2": {
- "message": "Enter a code generated by an authenticator app like Bitwarden Authenticator.",
+ "message": "Skriv inn en kode generert av en autentiseringsapp som Bitwarden Authenticator.",
"description": "'Bitwarden Authenticator' is a product name and should not be translated."
},
"yubiKeyTitleV2": {
- "message": "Yubico OTP security key"
+ "message": "YubiKey OTP-sikkerhetsnøkkel"
},
"yubiKeyDesc": {
"message": "Bruk en YubiKey for å få tilgang til kontoen din. Virker med enheter av typene YubiKey 4, 4 Nano, 4C, og NEO."
@@ -2048,7 +2054,7 @@
"message": "Velg en samling"
},
"importTargetHint": {
- "message": "Select this option if you want the imported file contents moved to a $DESTINATION$",
+ "message": "Velg dette alternativet hvis du vil flytte den importerte filens innhold til en $DESTINATION$",
"description": "Located as a hint under the import target. Will be appended by either folder or collection, depending if the user is importing into an individual or an organizational vault.",
"placeholders": {
"destination": {
@@ -3503,7 +3509,7 @@
"message": "Netthvelv"
},
"webApp": {
- "message": "Web app"
+ "message": "Nettapp"
},
"cli": {
"message": "Ledetekst"
@@ -3933,7 +3939,7 @@
"message": "Enhet"
},
"loginStatus": {
- "message": "Login status"
+ "message": "Innloggingsstatus"
},
"firstLogin": {
"message": "First login"
@@ -3942,7 +3948,7 @@
"message": "Betrodd"
},
"needsApproval": {
- "message": "Needs approval"
+ "message": "Trenger godkjenning"
},
"areYouTryingtoLogin": {
"message": "Prøver du å logge på?"
@@ -3957,19 +3963,19 @@
}
},
"deviceType": {
- "message": "Device Type"
+ "message": "Enhetstype"
},
"ipAddress": {
"message": "IP-adresse"
},
"confirmLogIn": {
- "message": "Confirm login"
+ "message": "Bekreft innlogging"
},
"denyLogIn": {
- "message": "Deny login"
+ "message": "Avslå innlogging"
},
"thisRequestIsNoLongerValid": {
- "message": "This request is no longer valid."
+ "message": "Denne forespørselen er ikke lenger gyldig."
},
"logInConfirmedForEmailOnDevice": {
"message": "Login confirmed for $EMAIL$ on $DEVICE$",
@@ -4004,13 +4010,13 @@
"message": "You denied a login attempt from another device. If this was you, try to log in with the device again."
},
"loginRequestHasAlreadyExpired": {
- "message": "Login request has already expired."
+ "message": "Innloggingsforespørselen har allerede utløpt."
},
"justNow": {
"message": "Akkurat nå"
},
"requestedXMinutesAgo": {
- "message": "Requested $MINUTES$ minutes ago",
+ "message": "Forespurt for $MINUTES$ minutter siden",
"placeholders": {
"minutes": {
"content": "$1",
@@ -4148,7 +4154,7 @@
"message": "Review login request"
},
"loginRequest": {
- "message": "Login request"
+ "message": "Innloggingsforespørsel"
},
"freeTrialEndPromptCount": {
"message": "Your free trial ends in $COUNT$ days.",
@@ -4203,7 +4209,7 @@
"message": "Bli med i organisasjon"
},
"joinOrganizationName": {
- "message": "Join $ORGANIZATIONNAME$",
+ "message": "Bli med i $ORGANIZATIONNAME$",
"placeholders": {
"organizationName": {
"content": "$1",
@@ -4479,7 +4485,7 @@
}
},
"subscriptionUpgrade": {
- "message": "You cannot invite more than $COUNT$ members without upgrading your plan.",
+ "message": "Du kan ikke invitere flere enn $COUNT$ brukere uten å oppgradere abonnementet ditt.",
"placeholders": {
"count": {
"content": "$1",
@@ -4626,6 +4632,9 @@
"receiveMarketingEmailsV2": {
"message": "Get advice, announcements, and research opportunities from Bitwarden in your inbox."
},
+ "subscribe": {
+ "message": "Abonnér"
+ },
"unsubscribe": {
"message": "Avabonner"
},
@@ -5827,7 +5836,7 @@
}
},
"emergencyAccessLoggedOutWarning": {
- "message": "Proceeding will log $NAME$ out of their current session, requiring them to log back in. Active sessions on other devices may continue to remain active for up to one hour.",
+ "message": "Å fortsette vil logge $NAME$ av deres nåværende økt, og krever at de logger på igjen. Aktive økter på andre enheter kan forbli aktive i opptil en time.",
"placeholders": {
"name": {
"content": "$1",
@@ -5842,7 +5851,7 @@
"message": "En eller flere av organisasjonens vilkår krever hovedpassordet ditt for å oppfylle følgende krav:"
},
"changePasswordDelegationMasterPasswordPolicyInEffect": {
- "message": "One or more organization policies require the master password to meet the following requirements:"
+ "message": "En eller flere av organisasjonens vilkår krever hovedpassordet ditt for å oppfylle følgende krav:"
},
"resetPasswordSuccess": {
"message": "Tilbakestilling av passord vellykket!"
@@ -5860,7 +5869,7 @@
"message": "Existing accounts with master passwords will require members to self-enroll before administrators can recover their accounts. Automatic enrollment will turn on account recovery for new members."
},
"accountRecoverySingleOrgRequirementDesc": {
- "message": "The single organization Enterprise policy must be turned on before activating this policy."
+ "message": "Virksomhetspolitikken for enkeltorganisasjoner må være i stand til å aktivere denne politikken."
},
"resetPasswordPolicyAutoEnroll": {
"message": "Automatisk registrering"
@@ -6543,7 +6552,7 @@
"message": "En verifiseringskode er påkrevd."
},
"webauthnCancelOrTimeout": {
- "message": "The authentication was cancelled or took too long. Please try again."
+ "message": "Autentiseringen ble avbrutt eller tok for lang tid. Prøv igjen."
},
"invalidVerificationCode": {
"message": "Ugyldig bekreftelseskode"
@@ -6687,7 +6696,7 @@
"message": "selvbetjent"
},
"customEnvironment": {
- "message": "Custom environment"
+ "message": "Tilpasset miljø"
},
"selfHostedBaseUrlHint": {
"message": "Specify the base URL of your on-premises hosted Bitwarden installation. Example: https://bitwarden.company.com"
@@ -6699,22 +6708,22 @@
"message": "You must add either the base Server URL or at least one custom environment."
},
"apiUrl": {
- "message": "API server URL"
+ "message": "API-tjenernettadresse"
},
"webVaultUrl": {
- "message": "Web vault server URL"
+ "message": "Netthvelvets tjenernettadresse"
},
"identityUrl": {
- "message": "Identity server URL"
+ "message": "Identitetstjenerens nettadresse"
},
"notificationsUrl": {
- "message": "Notifications server URL"
+ "message": "Varslingstjenerens URL"
},
"iconsUrl": {
- "message": "Icons server URL"
+ "message": "Ikonenes tjenernettadresse"
},
"environmentSaved": {
- "message": "Environment URLs saved"
+ "message": "Miljø-nettadressene har blitt lagret."
},
"selfHostingTitle": {
"message": "Selvhosting"
@@ -6823,7 +6832,7 @@
"message": "Eksporterer organisasjonshvelv"
},
"exportingIndividualVaultDescription": {
- "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included. Only vault item information will be exported and will not include associated attachments.",
+ "message": "Kun de individuelle hvelvgjenstandene som er assosiert med $EMAIL$ vil bli eksportert. Organisasjonshvelv-gjenstander vil ikke bli inkludert. Kun hvelvgjenstandsinfo vil bli eksportert og vil ikke inkludere assosierte vedlegg.",
"placeholders": {
"email": {
"content": "$1",
@@ -6903,16 +6912,16 @@
"message": "Generér passordfrase"
},
"passwordGenerated": {
- "message": "Password generated"
+ "message": "Passordet er generert."
},
"passphraseGenerated": {
- "message": "Passphrase generated"
+ "message": "Passordfrase generert"
},
"usernameGenerated": {
- "message": "Username generated"
+ "message": "Brukernavn generert"
},
"emailGenerated": {
- "message": "Email generated"
+ "message": "E-postadresse generert"
},
"spinboxBoundariesHint": {
"message": "Verdien må være mellom $MIN$ og $MAX$.",
@@ -7370,10 +7379,10 @@
"message": "Duo two-step login is required for your account. Follow the steps below to finish logging in."
},
"followTheStepsBelowToFinishLoggingIn": {
- "message": "Follow the steps below to finish logging in."
+ "message": "Følg trinnene nedenfor for å fullføre innloggingen."
},
"followTheStepsBelowToFinishLoggingInWithSecurityKey": {
- "message": "Follow the steps below to finish logging in with your security key."
+ "message": "Følg trinnene nedenfor for å fullføre innloggingen med sikkerhetsnøkkelen din."
},
"launchDuo": {
"message": "Start Duo"
@@ -8032,7 +8041,7 @@
"message": "Medlem"
},
"groupSlashMemberColumnHeader": {
- "message": "Group/Member"
+ "message": "Gruppe/Medlem"
},
"selectGroupsAndMembers": {
"message": "Velg grupper og medlemmer"
@@ -8239,7 +8248,7 @@
"message": "Can read, write"
},
"groupSlashUser": {
- "message": "Group/User"
+ "message": "Gruppe/Bruker"
},
"lowKdfIterations": {
"message": "Low KDF Iterations"
@@ -8343,13 +8352,13 @@
"message": "Create a secret"
},
"createProject": {
- "message": "Create a project"
+ "message": "Opprett et prosjekt"
},
"createServiceAccount": {
"message": "Create a service account"
},
"downloadThe": {
- "message": "Download the",
+ "message": "Last ned",
"description": "Link to a downloadable resource. This will be used as part of a larger phrase. Example: Download the Secrets Manager CLI"
},
"smCLI": {
@@ -8475,7 +8484,7 @@
"description": "The message shown to the user when bulk deleting projects and the user doesn't have access to some projects."
},
"updateKdfSettings": {
- "message": "Update KDF settings"
+ "message": "Oppdater KDF-innstillinger"
},
"loginInitiated": {
"message": "Innlogging igangsatt"
@@ -8605,7 +8614,7 @@
"message": "Approve login requests below to allow the requesting member to finish logging in. Unapproved requests expire after 1 week. Verify the member’s information before approving."
},
"deviceInfo": {
- "message": "Device info"
+ "message": "Enhetsinfo"
},
"time": {
"message": "Tid"
@@ -8626,7 +8635,7 @@
"message": "Device removed"
},
"removeDevice": {
- "message": "Remove device"
+ "message": "Fjern enhet"
},
"removeDeviceConfirmation": {
"message": "Are you sure you want to remove this device?"
@@ -8716,10 +8725,10 @@
"message": "Selected region flag"
},
"accountSuccessfullyCreated": {
- "message": "Account successfully created!"
+ "message": "Kontoen ble vellykket opprettet!"
},
"adminApprovalRequested": {
- "message": "Admin approval requested"
+ "message": "Admin-godkjenning forespurt"
},
"adminApprovalRequestSentToAdmins": {
"message": "Forespørselen din har blitt sendt til administratoren din."
@@ -8845,7 +8854,7 @@
"message": "Secrets Manager plan price"
},
"passwordManager": {
- "message": "Password Manager"
+ "message": "Passordbehandler"
},
"freeOrganization": {
"message": "Free Organization"
@@ -8900,7 +8909,7 @@
"message": "Passkoden vil ikke bli kopiert"
},
"passkeyNotCopiedAlert": {
- "message": "The passkey will not be copied to the cloned item. Do you want to continue cloning this item?"
+ "message": "Passnøkkelen kopieres ikke til det klonede elementet. Vil du fortsette kloningen av denne gjenstanden?"
},
"modifiedCollectionManagement": {
"message": "Modified collection management setting $ID$.",
@@ -8981,11 +8990,11 @@
"description": "Link to match detection docs on warning dialog for advance match strategy"
},
"uriAdvancedOption": {
- "message": "Advanced options",
+ "message": "Avanserte innstillinger",
"description": "Advanced option placeholder for uri option component"
},
"warningCapitalized": {
- "message": "Warning",
+ "message": "Advarsel",
"description": "Warning (should maintain locale-relevant capitalization)"
},
"maintainYourSubscription": {
@@ -8999,14 +9008,14 @@
}
},
"addAPaymentMethod": {
- "message": "add a payment method",
+ "message": "legg til en betalingsmetode",
"description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'To maintain your subscription for $ORG$, add a payment method'"
},
"organizationInformation": {
"message": "Organization information"
},
"confirmationDetails": {
- "message": "Confirmation details"
+ "message": "Bekreftelsesdetaljer"
},
"smFreeTrialThankYou": {
"message": "Thank you for signing up for Bitwarden Secrets Manager!"
@@ -9027,7 +9036,7 @@
"description": "Used as a form field label for a textarea input on the offboarding survey."
},
"missingFeatures": {
- "message": "Missing features",
+ "message": "Mangler funksjoner",
"description": "An option for the offboarding survey shown when a user cancels their subscription."
},
"movingToAnotherTool": {
@@ -9035,7 +9044,7 @@
"description": "An option for the offboarding survey shown when a user cancels their subscription."
},
"tooDifficultToUse": {
- "message": "Too difficult to use",
+ "message": "For vanskelig å bruke",
"description": "An option for the offboarding survey shown when a user cancels their subscription."
},
"notUsingEnough": {
@@ -9043,11 +9052,11 @@
"description": "An option for the offboarding survey shown when a user cancels their subscription."
},
"tooExpensive": {
- "message": "Too expensive",
+ "message": "For dyrt",
"description": "An option for the offboarding survey shown when a user cancels their subscription."
},
"freeForOneYear": {
- "message": "Free for 1 year"
+ "message": "Gratis i 1 år"
},
"newWebApp": {
"message": "Welcome to the new and improved web app. Learn more about what’s changed."
@@ -9221,7 +9230,7 @@
"description": "Message to encourage the user to start creating machine accounts."
},
"machineAccountsNoItemsTitle": {
- "message": "Nothing to show yet",
+ "message": "Intet å vise ennå",
"description": "Title to indicate that there are no machine accounts to display."
},
"deleteMachineAccounts": {
@@ -9420,7 +9429,7 @@
"message": "Automatically sync secrets from Bitwarden Secrets Manager to a third-party service."
},
"sdks": {
- "message": "SDKs"
+ "message": "SDK-er"
},
"sdksDesc": {
"message": "Use Bitwarden Secrets Manager SDK in the following programming languages to build your own applications."
@@ -9465,20 +9474,35 @@
"deviceManagementDesc": {
"message": "Configure device management for Bitwarden using the implementation guide for your platform."
},
+ "crowdstrikeEventIntegrationDesc": {
+ "message": "Send event data to your Logscale instance"
+ },
+ "failedToSaveIntegration": {
+ "message": "Failed to save integration. Please try again later."
+ },
"deviceIdMissing": {
- "message": "Device ID is missing"
+ "message": "Enhets-ID-en mangler"
},
"deviceTypeMissing": {
- "message": "Device type is missing"
+ "message": "Enhetstypen mangler"
},
"deviceCreationDateMissing": {
"message": "Device creation date is missing"
},
"desktopRequired": {
- "message": "Desktop required"
+ "message": "Skrivebord kreves"
},
"reopenLinkOnDesktop": {
- "message": "Reopen this link from your email on a desktop."
+ "message": "Åpne denne lenken på nytt fra e-posten din på et skrivebord."
+ },
+ "connectIntegrationButtonDesc": {
+ "message": "Connect $INTEGRATION$",
+ "placeholders": {
+ "integration": {
+ "content": "$1",
+ "example": "Crowdstrike"
+ }
+ }
},
"integrationCardTooltip": {
"message": "Launch $INTEGRATION$ implementation guide.",
@@ -9537,6 +9561,15 @@
"createNewClientToManageAsProvider": {
"message": "Create a new client organization to manage as a Provider. Additional seats will be reflected in the next billing cycle."
},
+ "url": {
+ "message": "URL"
+ },
+ "bearerToken": {
+ "message": "Bearer Token"
+ },
+ "index": {
+ "message": "Index"
+ },
"selectAPlan": {
"message": "Select a plan"
},
@@ -9559,7 +9592,7 @@
"message": "Successfully created new client"
},
"noAccess": {
- "message": "No access"
+ "message": "Ingen tilgang"
},
"collectionAdminConsoleManaged": {
"message": "This collection is only accessible from the admin console"
@@ -9568,10 +9601,10 @@
"message": "Toggle Organization Menu"
},
"vaultItemSelect": {
- "message": "Select vault item"
+ "message": "Velg hvelvobjekt"
},
"collectionItemSelect": {
- "message": "Select collection item"
+ "message": "Velg samlingsobjekt"
},
"manageBillingFromProviderPortalMessage": {
"message": "Manage billing from the Provider Portal"
@@ -9843,7 +9876,7 @@
"description": "The status of an invoice."
},
"clientDetails": {
- "message": "Client details"
+ "message": "Klientdetaljer"
},
"downloadCSV": {
"message": "Last ned CSV"
@@ -9877,7 +9910,7 @@
"message": "After making updates in the Bitwarden cloud server, upload your license file to apply the most recent changes."
},
"addToFolder": {
- "message": "Add to folder"
+ "message": "Legg til i mappe"
},
"selectFolder": {
"message": "Velg mappe"
@@ -9929,7 +9962,7 @@
"message": "Organization ID"
},
"projectIds": {
- "message": "Project IDs"
+ "message": "Prosjekt-ID-er"
},
"projectId": {
"message": "Prosjekt-ID"
@@ -10115,7 +10148,7 @@
"message": "RSA 4096-Bit"
},
"premiumAccounts": {
- "message": "6 premium accounts"
+ "message": "6 premiumkontoer"
},
"unlimitedSharing": {
"message": "Unlimited sharing"
@@ -10285,8 +10318,8 @@
"organizationUserDeletedDesc": {
"message": "The user was removed from the organization and all associated user data has been deleted."
},
- "deletedUserId": {
- "message": "Deleted user $ID$ - an owner / admin deleted the user account",
+ "deletedUserIdEventMessage": {
+ "message": "Deleted user $ID$",
"placeholders": {
"id": {
"content": "$1",
@@ -10325,7 +10358,7 @@
"message": "This action is not applicable to any of the selected members."
},
"deletedSuccessfully": {
- "message": "Deleted successfully"
+ "message": "Vellykket slettet"
},
"freeFamiliesSponsorship": {
"message": "Remove Free Bitwarden Families sponsorship"
@@ -10396,8 +10429,8 @@
"domainStatusUnderVerification": {
"message": "Under verification"
},
- "claimedDomainsDesc": {
- "message": "Claim a domain to own all member accounts whose email address matches the domain. Members will be able to skip the SSO identifier when logging in. Administrators will also be able to delete member accounts."
+ "claimedDomainsDescription": {
+ "message": "Claim a domain to own member accounts. The SSO identifier page will be skipped during login for members with claimed domains and administrators will be able to delete claimed accounts."
},
"invalidDomainNameClaimMessage": {
"message": "Input is not a valid format. Format: mydomain.com. Subdomains require separate entries to be claimed."
@@ -10521,13 +10554,13 @@
"message": "We had trouble opening the Bitwarden browser extension. Click the button to open it now."
},
"openExtension": {
- "message": "Open extension"
+ "message": "Åpne utvidelsen"
},
"doNotHaveExtension": {
"message": "Don't have the Bitwarden browser extension?"
},
"installExtension": {
- "message": "Install extension"
+ "message": "Installer utvidelsen"
},
"openedExtension": {
"message": "Opened the browser extension"
@@ -10733,12 +10766,12 @@
"message": "Spar tid med auto-utfylling"
},
"newLoginNudgeBodyOne": {
- "message": "Include a",
+ "message": "Inkluder en",
"description": "This is in multiple parts to allow for bold text in the middle of the sentence.",
"example": "Include a Website so this login appears as an autofill suggestion."
},
"newLoginNudgeBodyBold": {
- "message": "Website",
+ "message": "Nettsted",
"description": "This is in multiple parts to allow for bold text in the middle of the sentence.",
"example": "Include a Website so this login appears as an autofill suggestion."
},
@@ -10785,10 +10818,10 @@
"message": "Get the Bitwarden browser extension and start autofilling today"
},
"getTheExtension": {
- "message": "Get the extension"
+ "message": "Skaff deg utvidelsen"
},
"addItLater": {
- "message": "Add it later"
+ "message": "Legg det til senere"
},
"cannotAutofillPasswordsWithoutExtensionTitle": {
"message": "You can't autofill passwords without the browser extension"
@@ -10806,7 +10839,7 @@
"message": "Open the extension to log in and start autofilling."
},
"openBitwardenExtension": {
- "message": "Open Bitwarden extension"
+ "message": "Åpne Bitwarden-utvidelsen"
},
"gettingStartedWithBitwardenPart1": {
"message": "For tips on getting started with Bitwarden visit the",
@@ -10817,7 +10850,7 @@
"description": "This will be displayed as part of a larger sentence. The whole sentence reads: 'For tips on getting started with Bitwarden visit the Learning Center and Help Center'"
},
"gettingStartedWithBitwardenPart3": {
- "message": "Help Center",
+ "message": "Hjelpesenter",
"description": "This will be displayed as part of a larger sentence. The whole sentence reads: 'For tips on getting started with Bitwarden visit the Learning Center and Help Center'"
},
"setupExtensionContentAlt": {
@@ -10850,7 +10883,7 @@
"description": "Error message shown when trying to add credit to a trialing organization without a billing address."
},
"billingAddress": {
- "message": "Billing address"
+ "message": "Fakturaadresse"
},
"addBillingAddress": {
"message": "Add billing address"
@@ -10865,7 +10898,7 @@
"message": "Your billing address has been updated."
},
"paymentDetails": {
- "message": "Payment details"
+ "message": "Betalingsdetaljer"
},
"paymentMethodUpdated": {
"message": "Your payment method has been updated."
@@ -10880,7 +10913,7 @@
"message": "Must be a positive number"
},
"cardSecurityCode": {
- "message": "Card security code"
+ "message": "Kort-sikkerhetskode"
},
"cardSecurityCodeDescription": {
"message": "Card security code, also known as CVV or CVC, is typically a 3 digit number printed on the back of your credit card or 4 digit number printed on the front above your card number."
@@ -10896,5 +10929,56 @@
"example": "12-3456789"
}
}
+ },
+ "unpaidInvoices": {
+ "message": "Unpaid invoices"
+ },
+ "unpaidInvoicesForServiceUser": {
+ "message": "Your subscription has not been paid. Contact your provider administrator to restore service to you and your clients.",
+ "description": "A message shown in a non-dismissible dialog to service users of unpaid providers."
+ },
+ "providerSuspended": {
+ "message": "$PROVIDER$ is suspended",
+ "placeholders": {
+ "provider": {
+ "content": "$1",
+ "example": "Acme Industries"
+ }
+ }
+ },
+ "restoreProviderPortalAccessViaCustomerSupport": {
+ "message": "To restore access to your provider portal, contact Bitwarden Customer Support to renew your subscription.",
+ "description": "A message shown in a non-dismissible dialog to any user of a suspended providers."
+ },
+ "restoreProviderPortalAccessViaPaymentMethod": {
+ "message": "Your subscription has not been paid. To restore service to you and your clients, add a payment method by $CANCELLATION_DATE$.",
+ "placeholders": {
+ "cancellation_date": {
+ "content": "$1",
+ "example": "07/10/2025"
+ }
+ },
+ "description": "A message shown in a non-dismissible dialog to admins of unpaid providers."
+ },
+ "subscribetoEnterprise": {
+ "message": "Subscribe to $PLAN$",
+ "placeholders": {
+ "plan": {
+ "content": "$1",
+ "example": "Teams"
+ }
+ }
+ },
+ "subscribeEnterpriseSubtitle": {
+ "message": "Your 7-day $PLAN$ trial starts today. Add a payment method now to continue using these features after your trial ends: ",
+ "placeholders": {
+ "plan": {
+ "content": "$1",
+ "example": "Teams"
+ }
+ }
+ },
+ "unlimitedSecretsAndProjects": {
+ "message": "Unlimited secrets and projects"
}
-}
+}
\ No newline at end of file
diff --git a/apps/web/src/locales/ne/messages.json b/apps/web/src/locales/ne/messages.json
index ba910035310..0ee36ae9e06 100644
--- a/apps/web/src/locales/ne/messages.json
+++ b/apps/web/src/locales/ne/messages.json
@@ -568,6 +568,9 @@
"cancel": {
"message": "Cancel"
},
+ "later": {
+ "message": "Later"
+ },
"canceled": {
"message": "Canceled"
},
@@ -626,6 +629,9 @@
"searchGroups": {
"message": "Search groups"
},
+ "resetSearch": {
+ "message": "Reset search"
+ },
"allItems": {
"message": "All items"
},
@@ -4626,6 +4632,9 @@
"receiveMarketingEmailsV2": {
"message": "Get advice, announcements, and research opportunities from Bitwarden in your inbox."
},
+ "subscribe": {
+ "message": "Subscribe"
+ },
"unsubscribe": {
"message": "Unsubscribe"
},
@@ -9465,6 +9474,12 @@
"deviceManagementDesc": {
"message": "Configure device management for Bitwarden using the implementation guide for your platform."
},
+ "crowdstrikeEventIntegrationDesc": {
+ "message": "Send event data to your Logscale instance"
+ },
+ "failedToSaveIntegration": {
+ "message": "Failed to save integration. Please try again later."
+ },
"deviceIdMissing": {
"message": "Device ID is missing"
},
@@ -9480,6 +9495,15 @@
"reopenLinkOnDesktop": {
"message": "Reopen this link from your email on a desktop."
},
+ "connectIntegrationButtonDesc": {
+ "message": "Connect $INTEGRATION$",
+ "placeholders": {
+ "integration": {
+ "content": "$1",
+ "example": "Crowdstrike"
+ }
+ }
+ },
"integrationCardTooltip": {
"message": "Launch $INTEGRATION$ implementation guide.",
"placeholders": {
@@ -9537,6 +9561,15 @@
"createNewClientToManageAsProvider": {
"message": "Create a new client organization to manage as a Provider. Additional seats will be reflected in the next billing cycle."
},
+ "url": {
+ "message": "URL"
+ },
+ "bearerToken": {
+ "message": "Bearer Token"
+ },
+ "index": {
+ "message": "Index"
+ },
"selectAPlan": {
"message": "Select a plan"
},
@@ -10285,8 +10318,8 @@
"organizationUserDeletedDesc": {
"message": "The user was removed from the organization and all associated user data has been deleted."
},
- "deletedUserId": {
- "message": "Deleted user $ID$ - an owner / admin deleted the user account",
+ "deletedUserIdEventMessage": {
+ "message": "Deleted user $ID$",
"placeholders": {
"id": {
"content": "$1",
@@ -10396,8 +10429,8 @@
"domainStatusUnderVerification": {
"message": "Under verification"
},
- "claimedDomainsDesc": {
- "message": "Claim a domain to own all member accounts whose email address matches the domain. Members will be able to skip the SSO identifier when logging in. Administrators will also be able to delete member accounts."
+ "claimedDomainsDescription": {
+ "message": "Claim a domain to own member accounts. The SSO identifier page will be skipped during login for members with claimed domains and administrators will be able to delete claimed accounts."
},
"invalidDomainNameClaimMessage": {
"message": "Input is not a valid format. Format: mydomain.com. Subdomains require separate entries to be claimed."
@@ -10896,5 +10929,56 @@
"example": "12-3456789"
}
}
+ },
+ "unpaidInvoices": {
+ "message": "Unpaid invoices"
+ },
+ "unpaidInvoicesForServiceUser": {
+ "message": "Your subscription has not been paid. Contact your provider administrator to restore service to you and your clients.",
+ "description": "A message shown in a non-dismissible dialog to service users of unpaid providers."
+ },
+ "providerSuspended": {
+ "message": "$PROVIDER$ is suspended",
+ "placeholders": {
+ "provider": {
+ "content": "$1",
+ "example": "Acme Industries"
+ }
+ }
+ },
+ "restoreProviderPortalAccessViaCustomerSupport": {
+ "message": "To restore access to your provider portal, contact Bitwarden Customer Support to renew your subscription.",
+ "description": "A message shown in a non-dismissible dialog to any user of a suspended providers."
+ },
+ "restoreProviderPortalAccessViaPaymentMethod": {
+ "message": "Your subscription has not been paid. To restore service to you and your clients, add a payment method by $CANCELLATION_DATE$.",
+ "placeholders": {
+ "cancellation_date": {
+ "content": "$1",
+ "example": "07/10/2025"
+ }
+ },
+ "description": "A message shown in a non-dismissible dialog to admins of unpaid providers."
+ },
+ "subscribetoEnterprise": {
+ "message": "Subscribe to $PLAN$",
+ "placeholders": {
+ "plan": {
+ "content": "$1",
+ "example": "Teams"
+ }
+ }
+ },
+ "subscribeEnterpriseSubtitle": {
+ "message": "Your 7-day $PLAN$ trial starts today. Add a payment method now to continue using these features after your trial ends: ",
+ "placeholders": {
+ "plan": {
+ "content": "$1",
+ "example": "Teams"
+ }
+ }
+ },
+ "unlimitedSecretsAndProjects": {
+ "message": "Unlimited secrets and projects"
}
-}
+}
\ No newline at end of file
diff --git a/apps/web/src/locales/nl/messages.json b/apps/web/src/locales/nl/messages.json
index 45954a24a3f..9485b1bb573 100644
--- a/apps/web/src/locales/nl/messages.json
+++ b/apps/web/src/locales/nl/messages.json
@@ -568,6 +568,9 @@
"cancel": {
"message": "Annuleren"
},
+ "later": {
+ "message": "Later"
+ },
"canceled": {
"message": "Geannuleerd"
},
@@ -626,6 +629,9 @@
"searchGroups": {
"message": "Groepen zoeken"
},
+ "resetSearch": {
+ "message": "Zoekopdracht resetten"
+ },
"allItems": {
"message": "Alle Items"
},
@@ -4626,6 +4632,9 @@
"receiveMarketingEmailsV2": {
"message": "Krijg advies, aankondigingen en onderzoeksmogelijkheden van Bitwarden in je inbox."
},
+ "subscribe": {
+ "message": "Abonneren"
+ },
"unsubscribe": {
"message": "Afmelden"
},
@@ -9465,6 +9474,12 @@
"deviceManagementDesc": {
"message": "Apparaatbeheer voor Bitwarden configureren met behulp van de implementatiehandleiding voor jouw platform."
},
+ "crowdstrikeEventIntegrationDesc": {
+ "message": "Stuur eventgegevens naar je Logscale-instantie"
+ },
+ "failedToSaveIntegration": {
+ "message": "Opslaan van integratie mislukt. Probeer het later opnieuw."
+ },
"deviceIdMissing": {
"message": "Apparaat-ID ontbreekt"
},
@@ -9480,6 +9495,15 @@
"reopenLinkOnDesktop": {
"message": "Open deze link opnieuw vanaf je e-mail op een desktop."
},
+ "connectIntegrationButtonDesc": {
+ "message": "$INTEGRATION$ verbinden",
+ "placeholders": {
+ "integration": {
+ "content": "$1",
+ "example": "Crowdstrike"
+ }
+ }
+ },
"integrationCardTooltip": {
"message": "$INTEGRATION$ implementatiehandleiding openen.",
"placeholders": {
@@ -9537,6 +9561,15 @@
"createNewClientToManageAsProvider": {
"message": "Maak een nieuwe clientorganisatie aan om te beheren als Aanbieder. Extra plaatsen worden weergegeven in de volgende factureringscyclus."
},
+ "url": {
+ "message": "URL"
+ },
+ "bearerToken": {
+ "message": "Bearer-token"
+ },
+ "index": {
+ "message": "Index"
+ },
"selectAPlan": {
"message": "Selecteer een plan"
},
@@ -10285,8 +10318,8 @@
"organizationUserDeletedDesc": {
"message": "De gebruiker is verwijderd uit de organisatie en alle bijbehorende gebruikersgegevens zijn verwijderd."
},
- "deletedUserId": {
- "message": "Gebruiker $ID$ verwijderd - een eigenaar / beheerder heeft het gebruikersaccount verwijderd",
+ "deletedUserIdEventMessage": {
+ "message": "Gebruiker $ID$ verwijderd",
"placeholders": {
"id": {
"content": "$1",
@@ -10396,8 +10429,8 @@
"domainStatusUnderVerification": {
"message": "Gebruikersverificatie"
},
- "claimedDomainsDesc": {
- "message": "Verifieer een domein om eigenaar te worden van alle leden wiens e-mailadres overeenkomt met het domein. Leden kunnen de SSO-identificatie overslaan wanneer ze inloggen. Beheerders kunnen accounts van lelden verwijderen."
+ "claimedDomainsDescription": {
+ "message": "Claim een domein voor eigendom van ledenaccounts. De SSO-identificatiepagina wordt overgeslagen tijdens het inloggen voor leden met geclaimde domeinen en beheerders kunnen geclaimde accounts verwijderen."
},
"invalidDomainNameClaimMessage": {
"message": "Invoer is geen geldig formaat. Formaat: mijndomein.com. Subdomeinen moet je afzonderlijk verifiëren."
@@ -10896,5 +10929,56 @@
"example": "12-3456789"
}
}
+ },
+ "unpaidInvoices": {
+ "message": "Onbetaalde facturen"
+ },
+ "unpaidInvoicesForServiceUser": {
+ "message": "Je abonnement is niet betaald. Neem contact op met je providerbeheerder om de service naar jezelf en je klanten te herstellen.",
+ "description": "A message shown in a non-dismissible dialog to service users of unpaid providers."
+ },
+ "providerSuspended": {
+ "message": "$PROVIDER$ is opgeschort",
+ "placeholders": {
+ "provider": {
+ "content": "$1",
+ "example": "Acme Industries"
+ }
+ }
+ },
+ "restoreProviderPortalAccessViaCustomerSupport": {
+ "message": "Neem contact op met de Bitwarden Klantenservice om de toegang tot je providersportaal te herstellen om je abonnement te vernieuwen.",
+ "description": "A message shown in a non-dismissible dialog to any user of a suspended providers."
+ },
+ "restoreProviderPortalAccessViaPaymentMethod": {
+ "message": "Je abonnement is niet betaald. Voeg een betaalmethode toe voor $CANCELLATION_DATE$ om de service aan jezelf en klanten te herstellen.",
+ "placeholders": {
+ "cancellation_date": {
+ "content": "$1",
+ "example": "07/10/2025"
+ }
+ },
+ "description": "A message shown in a non-dismissible dialog to admins of unpaid providers."
+ },
+ "subscribetoEnterprise": {
+ "message": "Abonneren op $PLAN$",
+ "placeholders": {
+ "plan": {
+ "content": "$1",
+ "example": "Teams"
+ }
+ }
+ },
+ "subscribeEnterpriseSubtitle": {
+ "message": "Je proefperiode van 7 dagen $PLAN$ begint vandaag. Voeg nu een betaalmethode toe om deze functies na afloop van je proefperiode te blijven gebruiken ",
+ "placeholders": {
+ "plan": {
+ "content": "$1",
+ "example": "Teams"
+ }
+ }
+ },
+ "unlimitedSecretsAndProjects": {
+ "message": "Onbeperkte geheimen en projecten"
}
-}
+}
\ No newline at end of file
diff --git a/apps/web/src/locales/nn/messages.json b/apps/web/src/locales/nn/messages.json
index e04f1cb835f..c8b4abb158f 100644
--- a/apps/web/src/locales/nn/messages.json
+++ b/apps/web/src/locales/nn/messages.json
@@ -568,6 +568,9 @@
"cancel": {
"message": "Avbryt"
},
+ "later": {
+ "message": "Later"
+ },
"canceled": {
"message": "Brote av"
},
@@ -626,6 +629,9 @@
"searchGroups": {
"message": "Search groups"
},
+ "resetSearch": {
+ "message": "Reset search"
+ },
"allItems": {
"message": "Alle oppføringar"
},
@@ -4626,6 +4632,9 @@
"receiveMarketingEmailsV2": {
"message": "Get advice, announcements, and research opportunities from Bitwarden in your inbox."
},
+ "subscribe": {
+ "message": "Subscribe"
+ },
"unsubscribe": {
"message": "Unsubscribe"
},
@@ -9465,6 +9474,12 @@
"deviceManagementDesc": {
"message": "Configure device management for Bitwarden using the implementation guide for your platform."
},
+ "crowdstrikeEventIntegrationDesc": {
+ "message": "Send event data to your Logscale instance"
+ },
+ "failedToSaveIntegration": {
+ "message": "Failed to save integration. Please try again later."
+ },
"deviceIdMissing": {
"message": "Device ID is missing"
},
@@ -9480,6 +9495,15 @@
"reopenLinkOnDesktop": {
"message": "Reopen this link from your email on a desktop."
},
+ "connectIntegrationButtonDesc": {
+ "message": "Connect $INTEGRATION$",
+ "placeholders": {
+ "integration": {
+ "content": "$1",
+ "example": "Crowdstrike"
+ }
+ }
+ },
"integrationCardTooltip": {
"message": "Launch $INTEGRATION$ implementation guide.",
"placeholders": {
@@ -9537,6 +9561,15 @@
"createNewClientToManageAsProvider": {
"message": "Create a new client organization to manage as a Provider. Additional seats will be reflected in the next billing cycle."
},
+ "url": {
+ "message": "URL"
+ },
+ "bearerToken": {
+ "message": "Bearer Token"
+ },
+ "index": {
+ "message": "Index"
+ },
"selectAPlan": {
"message": "Select a plan"
},
@@ -10285,8 +10318,8 @@
"organizationUserDeletedDesc": {
"message": "The user was removed from the organization and all associated user data has been deleted."
},
- "deletedUserId": {
- "message": "Deleted user $ID$ - an owner / admin deleted the user account",
+ "deletedUserIdEventMessage": {
+ "message": "Deleted user $ID$",
"placeholders": {
"id": {
"content": "$1",
@@ -10396,8 +10429,8 @@
"domainStatusUnderVerification": {
"message": "Under verification"
},
- "claimedDomainsDesc": {
- "message": "Claim a domain to own all member accounts whose email address matches the domain. Members will be able to skip the SSO identifier when logging in. Administrators will also be able to delete member accounts."
+ "claimedDomainsDescription": {
+ "message": "Claim a domain to own member accounts. The SSO identifier page will be skipped during login for members with claimed domains and administrators will be able to delete claimed accounts."
},
"invalidDomainNameClaimMessage": {
"message": "Input is not a valid format. Format: mydomain.com. Subdomains require separate entries to be claimed."
@@ -10896,5 +10929,56 @@
"example": "12-3456789"
}
}
+ },
+ "unpaidInvoices": {
+ "message": "Unpaid invoices"
+ },
+ "unpaidInvoicesForServiceUser": {
+ "message": "Your subscription has not been paid. Contact your provider administrator to restore service to you and your clients.",
+ "description": "A message shown in a non-dismissible dialog to service users of unpaid providers."
+ },
+ "providerSuspended": {
+ "message": "$PROVIDER$ is suspended",
+ "placeholders": {
+ "provider": {
+ "content": "$1",
+ "example": "Acme Industries"
+ }
+ }
+ },
+ "restoreProviderPortalAccessViaCustomerSupport": {
+ "message": "To restore access to your provider portal, contact Bitwarden Customer Support to renew your subscription.",
+ "description": "A message shown in a non-dismissible dialog to any user of a suspended providers."
+ },
+ "restoreProviderPortalAccessViaPaymentMethod": {
+ "message": "Your subscription has not been paid. To restore service to you and your clients, add a payment method by $CANCELLATION_DATE$.",
+ "placeholders": {
+ "cancellation_date": {
+ "content": "$1",
+ "example": "07/10/2025"
+ }
+ },
+ "description": "A message shown in a non-dismissible dialog to admins of unpaid providers."
+ },
+ "subscribetoEnterprise": {
+ "message": "Subscribe to $PLAN$",
+ "placeholders": {
+ "plan": {
+ "content": "$1",
+ "example": "Teams"
+ }
+ }
+ },
+ "subscribeEnterpriseSubtitle": {
+ "message": "Your 7-day $PLAN$ trial starts today. Add a payment method now to continue using these features after your trial ends: ",
+ "placeholders": {
+ "plan": {
+ "content": "$1",
+ "example": "Teams"
+ }
+ }
+ },
+ "unlimitedSecretsAndProjects": {
+ "message": "Unlimited secrets and projects"
}
-}
+}
\ No newline at end of file
diff --git a/apps/web/src/locales/or/messages.json b/apps/web/src/locales/or/messages.json
index 79d58617943..24e3fc8075f 100644
--- a/apps/web/src/locales/or/messages.json
+++ b/apps/web/src/locales/or/messages.json
@@ -568,6 +568,9 @@
"cancel": {
"message": "Cancel"
},
+ "later": {
+ "message": "Later"
+ },
"canceled": {
"message": "Canceled"
},
@@ -626,6 +629,9 @@
"searchGroups": {
"message": "Search groups"
},
+ "resetSearch": {
+ "message": "Reset search"
+ },
"allItems": {
"message": "All items"
},
@@ -4626,6 +4632,9 @@
"receiveMarketingEmailsV2": {
"message": "Get advice, announcements, and research opportunities from Bitwarden in your inbox."
},
+ "subscribe": {
+ "message": "Subscribe"
+ },
"unsubscribe": {
"message": "Unsubscribe"
},
@@ -9465,6 +9474,12 @@
"deviceManagementDesc": {
"message": "Configure device management for Bitwarden using the implementation guide for your platform."
},
+ "crowdstrikeEventIntegrationDesc": {
+ "message": "Send event data to your Logscale instance"
+ },
+ "failedToSaveIntegration": {
+ "message": "Failed to save integration. Please try again later."
+ },
"deviceIdMissing": {
"message": "Device ID is missing"
},
@@ -9480,6 +9495,15 @@
"reopenLinkOnDesktop": {
"message": "Reopen this link from your email on a desktop."
},
+ "connectIntegrationButtonDesc": {
+ "message": "Connect $INTEGRATION$",
+ "placeholders": {
+ "integration": {
+ "content": "$1",
+ "example": "Crowdstrike"
+ }
+ }
+ },
"integrationCardTooltip": {
"message": "Launch $INTEGRATION$ implementation guide.",
"placeholders": {
@@ -9537,6 +9561,15 @@
"createNewClientToManageAsProvider": {
"message": "Create a new client organization to manage as a Provider. Additional seats will be reflected in the next billing cycle."
},
+ "url": {
+ "message": "URL"
+ },
+ "bearerToken": {
+ "message": "Bearer Token"
+ },
+ "index": {
+ "message": "Index"
+ },
"selectAPlan": {
"message": "Select a plan"
},
@@ -10285,8 +10318,8 @@
"organizationUserDeletedDesc": {
"message": "The user was removed from the organization and all associated user data has been deleted."
},
- "deletedUserId": {
- "message": "Deleted user $ID$ - an owner / admin deleted the user account",
+ "deletedUserIdEventMessage": {
+ "message": "Deleted user $ID$",
"placeholders": {
"id": {
"content": "$1",
@@ -10396,8 +10429,8 @@
"domainStatusUnderVerification": {
"message": "Under verification"
},
- "claimedDomainsDesc": {
- "message": "Claim a domain to own all member accounts whose email address matches the domain. Members will be able to skip the SSO identifier when logging in. Administrators will also be able to delete member accounts."
+ "claimedDomainsDescription": {
+ "message": "Claim a domain to own member accounts. The SSO identifier page will be skipped during login for members with claimed domains and administrators will be able to delete claimed accounts."
},
"invalidDomainNameClaimMessage": {
"message": "Input is not a valid format. Format: mydomain.com. Subdomains require separate entries to be claimed."
@@ -10896,5 +10929,56 @@
"example": "12-3456789"
}
}
+ },
+ "unpaidInvoices": {
+ "message": "Unpaid invoices"
+ },
+ "unpaidInvoicesForServiceUser": {
+ "message": "Your subscription has not been paid. Contact your provider administrator to restore service to you and your clients.",
+ "description": "A message shown in a non-dismissible dialog to service users of unpaid providers."
+ },
+ "providerSuspended": {
+ "message": "$PROVIDER$ is suspended",
+ "placeholders": {
+ "provider": {
+ "content": "$1",
+ "example": "Acme Industries"
+ }
+ }
+ },
+ "restoreProviderPortalAccessViaCustomerSupport": {
+ "message": "To restore access to your provider portal, contact Bitwarden Customer Support to renew your subscription.",
+ "description": "A message shown in a non-dismissible dialog to any user of a suspended providers."
+ },
+ "restoreProviderPortalAccessViaPaymentMethod": {
+ "message": "Your subscription has not been paid. To restore service to you and your clients, add a payment method by $CANCELLATION_DATE$.",
+ "placeholders": {
+ "cancellation_date": {
+ "content": "$1",
+ "example": "07/10/2025"
+ }
+ },
+ "description": "A message shown in a non-dismissible dialog to admins of unpaid providers."
+ },
+ "subscribetoEnterprise": {
+ "message": "Subscribe to $PLAN$",
+ "placeholders": {
+ "plan": {
+ "content": "$1",
+ "example": "Teams"
+ }
+ }
+ },
+ "subscribeEnterpriseSubtitle": {
+ "message": "Your 7-day $PLAN$ trial starts today. Add a payment method now to continue using these features after your trial ends: ",
+ "placeholders": {
+ "plan": {
+ "content": "$1",
+ "example": "Teams"
+ }
+ }
+ },
+ "unlimitedSecretsAndProjects": {
+ "message": "Unlimited secrets and projects"
}
-}
+}
\ No newline at end of file
diff --git a/apps/web/src/locales/pl/messages.json b/apps/web/src/locales/pl/messages.json
index e2d73ad47b3..bc4775d9545 100644
--- a/apps/web/src/locales/pl/messages.json
+++ b/apps/web/src/locales/pl/messages.json
@@ -568,6 +568,9 @@
"cancel": {
"message": "Anuluj"
},
+ "later": {
+ "message": "Później"
+ },
"canceled": {
"message": "Anulowane"
},
@@ -626,6 +629,9 @@
"searchGroups": {
"message": "Szukaj w grupach"
},
+ "resetSearch": {
+ "message": "Reset search"
+ },
"allItems": {
"message": "Wszystkie elementy"
},
@@ -4626,6 +4632,9 @@
"receiveMarketingEmailsV2": {
"message": "Uzyskaj poradę, ogłoszenia i możliwości badawcze od Bitwarden w swojej skrzynce odbiorczej."
},
+ "subscribe": {
+ "message": "Subskrybuj"
+ },
"unsubscribe": {
"message": "Anuluj subskrypcję"
},
@@ -9465,6 +9474,12 @@
"deviceManagementDesc": {
"message": "Skonfiguruj zarządzanie urządzeniem Bitwarden za pomocą instrukcji implementacyjnych dla Twojej platformy."
},
+ "crowdstrikeEventIntegrationDesc": {
+ "message": "Send event data to your Logscale instance"
+ },
+ "failedToSaveIntegration": {
+ "message": "Failed to save integration. Please try again later."
+ },
"deviceIdMissing": {
"message": "Brakuje identyfikatora urządzenia"
},
@@ -9480,6 +9495,15 @@
"reopenLinkOnDesktop": {
"message": "Otwórz ponownie ten link z adresu e-mail na komputerze."
},
+ "connectIntegrationButtonDesc": {
+ "message": "Connect $INTEGRATION$",
+ "placeholders": {
+ "integration": {
+ "content": "$1",
+ "example": "Crowdstrike"
+ }
+ }
+ },
"integrationCardTooltip": {
"message": "Uruchom przewodnik implementacyjny $INTEGRATION$.",
"placeholders": {
@@ -9537,6 +9561,15 @@
"createNewClientToManageAsProvider": {
"message": "Utwórz nową organizację klienta do zarządzania jako dostawca. Dodatkowe miejsca zostaną odzwierciedlone w następnym cyklu rozliczeniowym."
},
+ "url": {
+ "message": "URL"
+ },
+ "bearerToken": {
+ "message": "Bearer Token"
+ },
+ "index": {
+ "message": "Index"
+ },
"selectAPlan": {
"message": "Wybierz plan"
},
@@ -10285,8 +10318,8 @@
"organizationUserDeletedDesc": {
"message": "Użytkownik został usunięty z organizacji i wszystkie powiązane dane użytkownika zostały usunięte."
},
- "deletedUserId": {
- "message": "Usunięto użytkownika $ID$ - właściciel / administrator usunął konto użytkownika",
+ "deletedUserIdEventMessage": {
+ "message": "Usunięto użytkownika $ID$",
"placeholders": {
"id": {
"content": "$1",
@@ -10396,8 +10429,8 @@
"domainStatusUnderVerification": {
"message": "W trakcie weryfikacji"
},
- "claimedDomainsDesc": {
- "message": "Zgłoś domenę, aby przejąć własność wszystkich kont członków, których adres e-mail pasuje do domeny. Członkowie będą mogli pominąć identyfikator SSO podczas logowania. Administratorzy będą również mogli usuwać konta członków."
+ "claimedDomainsDescription": {
+ "message": "Zgłoś domenę, aby posiadać konta członków. Strona identyfikatora SSO zostanie pominięta podczas logowania dla członków z zadeklarowanymi domenami, a administratorzy będą mogli usuwać zadeklarowane konta."
},
"invalidDomainNameClaimMessage": {
"message": "Nieprawidłowy format. Format: mojadomena.com. Subdomeny wymagają osobnego zgłoszenia."
@@ -10896,5 +10929,56 @@
"example": "12-3456789"
}
}
+ },
+ "unpaidInvoices": {
+ "message": "Unpaid invoices"
+ },
+ "unpaidInvoicesForServiceUser": {
+ "message": "Your subscription has not been paid. Contact your provider administrator to restore service to you and your clients.",
+ "description": "A message shown in a non-dismissible dialog to service users of unpaid providers."
+ },
+ "providerSuspended": {
+ "message": "$PROVIDER$ is suspended",
+ "placeholders": {
+ "provider": {
+ "content": "$1",
+ "example": "Acme Industries"
+ }
+ }
+ },
+ "restoreProviderPortalAccessViaCustomerSupport": {
+ "message": "To restore access to your provider portal, contact Bitwarden Customer Support to renew your subscription.",
+ "description": "A message shown in a non-dismissible dialog to any user of a suspended providers."
+ },
+ "restoreProviderPortalAccessViaPaymentMethod": {
+ "message": "Your subscription has not been paid. To restore service to you and your clients, add a payment method by $CANCELLATION_DATE$.",
+ "placeholders": {
+ "cancellation_date": {
+ "content": "$1",
+ "example": "07/10/2025"
+ }
+ },
+ "description": "A message shown in a non-dismissible dialog to admins of unpaid providers."
+ },
+ "subscribetoEnterprise": {
+ "message": "Subskrybuj $PLAN$",
+ "placeholders": {
+ "plan": {
+ "content": "$1",
+ "example": "Teams"
+ }
+ }
+ },
+ "subscribeEnterpriseSubtitle": {
+ "message": "Twój 7-dniowy okres próbny $PLAN$ rozpoczyna się dzisiaj. Dodaj metodę płatności, aby kontynuować korzystanie z tych funkcji po zakończeniu okresu próbnego: ",
+ "placeholders": {
+ "plan": {
+ "content": "$1",
+ "example": "Teams"
+ }
+ }
+ },
+ "unlimitedSecretsAndProjects": {
+ "message": "Nieograniczone sekrety i projekty"
}
-}
+}
\ No newline at end of file
diff --git a/apps/web/src/locales/pt_BR/messages.json b/apps/web/src/locales/pt_BR/messages.json
index 09d4b078ee0..80fbce621eb 100644
--- a/apps/web/src/locales/pt_BR/messages.json
+++ b/apps/web/src/locales/pt_BR/messages.json
@@ -568,6 +568,9 @@
"cancel": {
"message": "Cancelar"
},
+ "later": {
+ "message": "Later"
+ },
"canceled": {
"message": "Cancelado"
},
@@ -626,6 +629,9 @@
"searchGroups": {
"message": "Buscar grupos"
},
+ "resetSearch": {
+ "message": "Reset search"
+ },
"allItems": {
"message": "Todos os Itens"
},
@@ -4626,6 +4632,9 @@
"receiveMarketingEmailsV2": {
"message": "Obtenha conselhos, novidades, e oportunidades de pesquisa do Bitwarden em sua caixa de entrada."
},
+ "subscribe": {
+ "message": "Subscribe"
+ },
"unsubscribe": {
"message": "Cancelar subscrição"
},
@@ -9465,6 +9474,12 @@
"deviceManagementDesc": {
"message": "Configure o gerenciamento de dispositivos para o Bitwarden usando o guia de implementação da sua plataforma."
},
+ "crowdstrikeEventIntegrationDesc": {
+ "message": "Send event data to your Logscale instance"
+ },
+ "failedToSaveIntegration": {
+ "message": "Failed to save integration. Please try again later."
+ },
"deviceIdMissing": {
"message": "ID do dispositivo está faltando"
},
@@ -9480,6 +9495,15 @@
"reopenLinkOnDesktop": {
"message": "Reabra este link a partir do seu e-mail em um desktop."
},
+ "connectIntegrationButtonDesc": {
+ "message": "Connect $INTEGRATION$",
+ "placeholders": {
+ "integration": {
+ "content": "$1",
+ "example": "Crowdstrike"
+ }
+ }
+ },
"integrationCardTooltip": {
"message": "Inicie o guia de implementação $INTEGRATION$.",
"placeholders": {
@@ -9537,6 +9561,15 @@
"createNewClientToManageAsProvider": {
"message": "Crie uma nova organização de cliente para gerenciar como um Provedor. Posições adicionais serão refletidas no próximo ciclo de faturamento."
},
+ "url": {
+ "message": "URL"
+ },
+ "bearerToken": {
+ "message": "Bearer Token"
+ },
+ "index": {
+ "message": "Index"
+ },
"selectAPlan": {
"message": "Selecione um plano"
},
@@ -10285,8 +10318,8 @@
"organizationUserDeletedDesc": {
"message": "O usuário foi removido da organização e todos os dados de usuários associados foram excluídos."
},
- "deletedUserId": {
- "message": "Usuário excluído $ID$ - um proprietário / administrador excluiu a conta do usuário",
+ "deletedUserIdEventMessage": {
+ "message": "Deleted user $ID$",
"placeholders": {
"id": {
"content": "$1",
@@ -10396,8 +10429,8 @@
"domainStatusUnderVerification": {
"message": "Em verificação"
},
- "claimedDomainsDesc": {
- "message": "Reivindique um domínio para se apropriar de todas as contas de membros cujo endereço de e-mail corresponde ao domínio. Os membros serão capazes de pular o identificador SSO ao fazer o login. Administradores também serão capazes de excluir contas de membros."
+ "claimedDomainsDescription": {
+ "message": "Claim a domain to own member accounts. The SSO identifier page will be skipped during login for members with claimed domains and administrators will be able to delete claimed accounts."
},
"invalidDomainNameClaimMessage": {
"message": "Entrada não é um formato válido. Formato: meudominio.com. Subdomínios requerem entradas separadas para serem reivindicados."
@@ -10896,5 +10929,56 @@
"example": "12-3456789"
}
}
+ },
+ "unpaidInvoices": {
+ "message": "Unpaid invoices"
+ },
+ "unpaidInvoicesForServiceUser": {
+ "message": "Your subscription has not been paid. Contact your provider administrator to restore service to you and your clients.",
+ "description": "A message shown in a non-dismissible dialog to service users of unpaid providers."
+ },
+ "providerSuspended": {
+ "message": "$PROVIDER$ is suspended",
+ "placeholders": {
+ "provider": {
+ "content": "$1",
+ "example": "Acme Industries"
+ }
+ }
+ },
+ "restoreProviderPortalAccessViaCustomerSupport": {
+ "message": "To restore access to your provider portal, contact Bitwarden Customer Support to renew your subscription.",
+ "description": "A message shown in a non-dismissible dialog to any user of a suspended providers."
+ },
+ "restoreProviderPortalAccessViaPaymentMethod": {
+ "message": "Your subscription has not been paid. To restore service to you and your clients, add a payment method by $CANCELLATION_DATE$.",
+ "placeholders": {
+ "cancellation_date": {
+ "content": "$1",
+ "example": "07/10/2025"
+ }
+ },
+ "description": "A message shown in a non-dismissible dialog to admins of unpaid providers."
+ },
+ "subscribetoEnterprise": {
+ "message": "Subscribe to $PLAN$",
+ "placeholders": {
+ "plan": {
+ "content": "$1",
+ "example": "Teams"
+ }
+ }
+ },
+ "subscribeEnterpriseSubtitle": {
+ "message": "Your 7-day $PLAN$ trial starts today. Add a payment method now to continue using these features after your trial ends: ",
+ "placeholders": {
+ "plan": {
+ "content": "$1",
+ "example": "Teams"
+ }
+ }
+ },
+ "unlimitedSecretsAndProjects": {
+ "message": "Unlimited secrets and projects"
}
-}
+}
\ No newline at end of file
diff --git a/apps/web/src/locales/pt_PT/messages.json b/apps/web/src/locales/pt_PT/messages.json
index cda5cec824d..7f9b240a74e 100644
--- a/apps/web/src/locales/pt_PT/messages.json
+++ b/apps/web/src/locales/pt_PT/messages.json
@@ -568,6 +568,9 @@
"cancel": {
"message": "Cancelar"
},
+ "later": {
+ "message": "Mais tarde"
+ },
"canceled": {
"message": "Cancelado"
},
@@ -626,6 +629,9 @@
"searchGroups": {
"message": "Procurar grupos"
},
+ "resetSearch": {
+ "message": "Repor pesquisa"
+ },
"allItems": {
"message": "Todos os itens"
},
@@ -4626,6 +4632,9 @@
"receiveMarketingEmailsV2": {
"message": "Receba conselhos, anúncios e oportunidades de investigação do Bitwarden na sua caixa de entrada."
},
+ "subscribe": {
+ "message": "Subscrever"
+ },
"unsubscribe": {
"message": "Anular subscrição"
},
@@ -9465,6 +9474,12 @@
"deviceManagementDesc": {
"message": "Configure a gestão de dispositivos do Bitwarden utilizando o guia de implementação da sua plataforma."
},
+ "crowdstrikeEventIntegrationDesc": {
+ "message": "Enviar dados de eventos para a sua instância Logscale"
+ },
+ "failedToSaveIntegration": {
+ "message": "Falha ao guardar a integração. Por favor, tente novamente mais tarde."
+ },
"deviceIdMissing": {
"message": "ID do dispositivo em falta"
},
@@ -9480,6 +9495,15 @@
"reopenLinkOnDesktop": {
"message": "Reabra este link a partir do seu e-mail num computador."
},
+ "connectIntegrationButtonDesc": {
+ "message": "Estabelecer ligação com o $INTEGRATION$",
+ "placeholders": {
+ "integration": {
+ "content": "$1",
+ "example": "Crowdstrike"
+ }
+ }
+ },
"integrationCardTooltip": {
"message": "Lançar o guia de implementação da $INTEGRATION$.",
"placeholders": {
@@ -9537,6 +9561,15 @@
"createNewClientToManageAsProvider": {
"message": "Crie uma nova organização de clientes para gerir como Fornecedor. Os lugares adicionais serão refletidos na próxima faturação."
},
+ "url": {
+ "message": "URL"
+ },
+ "bearerToken": {
+ "message": "Token de portador"
+ },
+ "index": {
+ "message": "Índice"
+ },
"selectAPlan": {
"message": "Selecionar um plano"
},
@@ -10285,8 +10318,8 @@
"organizationUserDeletedDesc": {
"message": "O utilizador foi removido da organização e todos os dados de utilizador associados foram eliminados."
},
- "deletedUserId": {
- "message": "Utilizador $ID$ eliminado - um proprietário/administrador eliminou a conta de utilizador",
+ "deletedUserIdEventMessage": {
+ "message": "Eliminar utilizador $ID$",
"placeholders": {
"id": {
"content": "$1",
@@ -10396,8 +10429,8 @@
"domainStatusUnderVerification": {
"message": "Sob verificação"
},
- "claimedDomainsDesc": {
- "message": "Reivindique um domínio para possuir todas as contas de membros cujo endereço de e-mail corresponda ao domínio. Os membros poderão ignorar o identificador SSO quando iniciarem sessão. Os administradores também poderão eliminar contas de membros."
+ "claimedDomainsDescription": {
+ "message": "Reivindique um domínio para possuir contas de membros. A página do identificador SSO será ignorada durante o início de sessão para membros com domínios reivindicados e os administradores poderão eliminar contas reivindicadas."
},
"invalidDomainNameClaimMessage": {
"message": "O campo não é um formato válido. Formato: omeudominio.com. Os subdomínios requerem campos separados para serem reivindicado."
@@ -10896,5 +10929,56 @@
"example": "12-3456789"
}
}
+ },
+ "unpaidInvoices": {
+ "message": "Faturas não pagas"
+ },
+ "unpaidInvoicesForServiceUser": {
+ "message": "A sua subscrição não foi paga. Contacte o administrador do seu fornecedor para restabelecer o serviço para si e para os seus clientes.",
+ "description": "A message shown in a non-dismissible dialog to service users of unpaid providers."
+ },
+ "providerSuspended": {
+ "message": "$PROVIDER$ está suspenso",
+ "placeholders": {
+ "provider": {
+ "content": "$1",
+ "example": "Acme Industries"
+ }
+ }
+ },
+ "restoreProviderPortalAccessViaCustomerSupport": {
+ "message": "Para restaurar o acesso ao portal do fornecedor, entre em contacto com o Apoio ao cliente do Bitwarden para renovar a sua subscrição.",
+ "description": "A message shown in a non-dismissible dialog to any user of a suspended providers."
+ },
+ "restoreProviderPortalAccessViaPaymentMethod": {
+ "message": "A sua subscrição não foi paga. Para restabelecer o serviço para si e para os seus clientes, adicione um método de pagamento até $CANCELLATION_DATE$.",
+ "placeholders": {
+ "cancellation_date": {
+ "content": "$1",
+ "example": "07/10/2025"
+ }
+ },
+ "description": "A message shown in a non-dismissible dialog to admins of unpaid providers."
+ },
+ "subscribetoEnterprise": {
+ "message": "Subscrever o plano $PLAN$",
+ "placeholders": {
+ "plan": {
+ "content": "$1",
+ "example": "Teams"
+ }
+ }
+ },
+ "subscribeEnterpriseSubtitle": {
+ "message": "O seu período experimental de 7 dias do plano $PLAN$ começa hoje. Adicione um método de pagamento agora para continuar a utilizar estas funcionalidades após o fim do período experimental: ",
+ "placeholders": {
+ "plan": {
+ "content": "$1",
+ "example": "Teams"
+ }
+ }
+ },
+ "unlimitedSecretsAndProjects": {
+ "message": "Segredos e projetos ilimitados"
}
-}
+}
\ No newline at end of file
diff --git a/apps/web/src/locales/ro/messages.json b/apps/web/src/locales/ro/messages.json
index 3d3e48f0893..44f4ac5a26b 100644
--- a/apps/web/src/locales/ro/messages.json
+++ b/apps/web/src/locales/ro/messages.json
@@ -568,6 +568,9 @@
"cancel": {
"message": "Anulare"
},
+ "later": {
+ "message": "Later"
+ },
"canceled": {
"message": "Anulat"
},
@@ -626,6 +629,9 @@
"searchGroups": {
"message": "Caută grupuri"
},
+ "resetSearch": {
+ "message": "Reset search"
+ },
"allItems": {
"message": "Toate articolele"
},
@@ -4626,6 +4632,9 @@
"receiveMarketingEmailsV2": {
"message": "Get advice, announcements, and research opportunities from Bitwarden in your inbox."
},
+ "subscribe": {
+ "message": "Subscribe"
+ },
"unsubscribe": {
"message": "Unsubscribe"
},
@@ -9465,6 +9474,12 @@
"deviceManagementDesc": {
"message": "Configure device management for Bitwarden using the implementation guide for your platform."
},
+ "crowdstrikeEventIntegrationDesc": {
+ "message": "Send event data to your Logscale instance"
+ },
+ "failedToSaveIntegration": {
+ "message": "Failed to save integration. Please try again later."
+ },
"deviceIdMissing": {
"message": "Device ID is missing"
},
@@ -9480,6 +9495,15 @@
"reopenLinkOnDesktop": {
"message": "Reopen this link from your email on a desktop."
},
+ "connectIntegrationButtonDesc": {
+ "message": "Connect $INTEGRATION$",
+ "placeholders": {
+ "integration": {
+ "content": "$1",
+ "example": "Crowdstrike"
+ }
+ }
+ },
"integrationCardTooltip": {
"message": "Launch $INTEGRATION$ implementation guide.",
"placeholders": {
@@ -9537,6 +9561,15 @@
"createNewClientToManageAsProvider": {
"message": "Create a new client organization to manage as a Provider. Additional seats will be reflected in the next billing cycle."
},
+ "url": {
+ "message": "URL"
+ },
+ "bearerToken": {
+ "message": "Bearer Token"
+ },
+ "index": {
+ "message": "Index"
+ },
"selectAPlan": {
"message": "Select a plan"
},
@@ -10285,8 +10318,8 @@
"organizationUserDeletedDesc": {
"message": "The user was removed from the organization and all associated user data has been deleted."
},
- "deletedUserId": {
- "message": "Deleted user $ID$ - an owner / admin deleted the user account",
+ "deletedUserIdEventMessage": {
+ "message": "Deleted user $ID$",
"placeholders": {
"id": {
"content": "$1",
@@ -10396,8 +10429,8 @@
"domainStatusUnderVerification": {
"message": "Under verification"
},
- "claimedDomainsDesc": {
- "message": "Claim a domain to own all member accounts whose email address matches the domain. Members will be able to skip the SSO identifier when logging in. Administrators will also be able to delete member accounts."
+ "claimedDomainsDescription": {
+ "message": "Claim a domain to own member accounts. The SSO identifier page will be skipped during login for members with claimed domains and administrators will be able to delete claimed accounts."
},
"invalidDomainNameClaimMessage": {
"message": "Input is not a valid format. Format: mydomain.com. Subdomains require separate entries to be claimed."
@@ -10896,5 +10929,56 @@
"example": "12-3456789"
}
}
+ },
+ "unpaidInvoices": {
+ "message": "Unpaid invoices"
+ },
+ "unpaidInvoicesForServiceUser": {
+ "message": "Your subscription has not been paid. Contact your provider administrator to restore service to you and your clients.",
+ "description": "A message shown in a non-dismissible dialog to service users of unpaid providers."
+ },
+ "providerSuspended": {
+ "message": "$PROVIDER$ is suspended",
+ "placeholders": {
+ "provider": {
+ "content": "$1",
+ "example": "Acme Industries"
+ }
+ }
+ },
+ "restoreProviderPortalAccessViaCustomerSupport": {
+ "message": "To restore access to your provider portal, contact Bitwarden Customer Support to renew your subscription.",
+ "description": "A message shown in a non-dismissible dialog to any user of a suspended providers."
+ },
+ "restoreProviderPortalAccessViaPaymentMethod": {
+ "message": "Your subscription has not been paid. To restore service to you and your clients, add a payment method by $CANCELLATION_DATE$.",
+ "placeholders": {
+ "cancellation_date": {
+ "content": "$1",
+ "example": "07/10/2025"
+ }
+ },
+ "description": "A message shown in a non-dismissible dialog to admins of unpaid providers."
+ },
+ "subscribetoEnterprise": {
+ "message": "Subscribe to $PLAN$",
+ "placeholders": {
+ "plan": {
+ "content": "$1",
+ "example": "Teams"
+ }
+ }
+ },
+ "subscribeEnterpriseSubtitle": {
+ "message": "Your 7-day $PLAN$ trial starts today. Add a payment method now to continue using these features after your trial ends: ",
+ "placeholders": {
+ "plan": {
+ "content": "$1",
+ "example": "Teams"
+ }
+ }
+ },
+ "unlimitedSecretsAndProjects": {
+ "message": "Unlimited secrets and projects"
}
-}
+}
\ No newline at end of file
diff --git a/apps/web/src/locales/ru/messages.json b/apps/web/src/locales/ru/messages.json
index 4cf70f03865..9b490c4a761 100644
--- a/apps/web/src/locales/ru/messages.json
+++ b/apps/web/src/locales/ru/messages.json
@@ -12,7 +12,7 @@
"message": "Никакие критически важные приложения не подвергаются риску"
},
"accessIntelligence": {
- "message": "Управление доступом"
+ "message": "Access Intelligence"
},
"riskInsights": {
"message": "Информация о рисках"
@@ -568,6 +568,9 @@
"cancel": {
"message": "Отмена"
},
+ "later": {
+ "message": "Позже"
+ },
"canceled": {
"message": "Отменено"
},
@@ -626,6 +629,9 @@
"searchGroups": {
"message": "Поиск групп"
},
+ "resetSearch": {
+ "message": "Сбросить поиск"
+ },
"allItems": {
"message": "Все элементы"
},
@@ -4626,6 +4632,9 @@
"receiveMarketingEmailsV2": {
"message": "Получайте советы, анонсы и возможности для исследований от Bitwarden на свой email."
},
+ "subscribe": {
+ "message": "Подписаться"
+ },
"unsubscribe": {
"message": "Отписаться"
},
@@ -5421,7 +5430,7 @@
"message": "В экстренном доступе отказано"
},
"grantorDetailsNotFound": {
- "message": "Grantor details not found"
+ "message": "Данные грантора не найдены"
},
"passwordResetFor": {
"message": "Сброшен пароль для $USER$. Теперь вы можете войти, используя новый пароль.",
@@ -5433,7 +5442,7 @@
}
},
"organizationDataOwnership": {
- "message": "Enforce organization data ownership"
+ "message": "Принудительное соблюдение прав собственности на данные организации"
},
"personalOwnership": {
"message": "Удалить личное хранилище"
@@ -6411,10 +6420,10 @@
"message": "Нет членов семей"
},
"noMemberFamiliesDescription": {
- "message": "Здесь отображаются участники, выкупившие семейные планы"
+ "message": "Здесь будут отображаться участники, которые воспользовались семейными тарифами"
},
"membersWithSponsoredFamilies": {
- "message": "Члены вашей организации имеют право на бесплатную программу Bitwarden Families. Здесь вы можете увидеть членов, которые спонсировали организацию Families."
+ "message": "Члены вашей организации имеют право на бесплатное использование Bitwarden Families. Здесь вы можете увидеть членов, которые спонсируются Families."
},
"organizationHasMemberMessage": {
"message": "Спонсорство не может быть отправлено $EMAIL$, поскольку он является членом вашей организации.",
@@ -9465,6 +9474,12 @@
"deviceManagementDesc": {
"message": "Настройте управление устройствами для Bitwarden, используя руководство по внедрению для вашей платформы."
},
+ "crowdstrikeEventIntegrationDesc": {
+ "message": "Отправлять данные о событиях в ваш инстанс Logscale"
+ },
+ "failedToSaveIntegration": {
+ "message": "Не удалось сохранить интеграцию. Пожалуйста, повторите попытку позже."
+ },
"deviceIdMissing": {
"message": "ID устройства отсутствует"
},
@@ -9480,6 +9495,15 @@
"reopenLinkOnDesktop": {
"message": "Откройте эту ссылку из email на вашем компьютере."
},
+ "connectIntegrationButtonDesc": {
+ "message": "Подключить $INTEGRATION$",
+ "placeholders": {
+ "integration": {
+ "content": "$1",
+ "example": "Crowdstrike"
+ }
+ }
+ },
"integrationCardTooltip": {
"message": "Запустить руководство по внедрению $INTEGRATION$.",
"placeholders": {
@@ -9537,6 +9561,15 @@
"createNewClientToManageAsProvider": {
"message": "Создайте новую клиентскую организацию для управления ею в качестве провайдера. Дополнительные места будут отражены в следующем биллинговом цикле."
},
+ "url": {
+ "message": "URL"
+ },
+ "bearerToken": {
+ "message": "Токен на предъявителя"
+ },
+ "index": {
+ "message": "Индекс"
+ },
"selectAPlan": {
"message": "Выберите план"
},
@@ -10285,8 +10318,8 @@
"organizationUserDeletedDesc": {
"message": "Пользователь был удален из организации, и все связанные с ним данные были удалены."
},
- "deletedUserId": {
- "message": "Владелец - удаленный пользователь $ID$ / администратор удалил аккаунт пользователя",
+ "deletedUserIdEventMessage": {
+ "message": "Удален пользователь $ID$",
"placeholders": {
"id": {
"content": "$1",
@@ -10396,8 +10429,8 @@
"domainStatusUnderVerification": {
"message": "Проверяется"
},
- "claimedDomainsDesc": {
- "message": "Укажите домен, чтобы все аккаунты пользователей, чей адрес email совпадает с доменом, принадлежали ему. Пользователи смогут пропускать идентификатор SSO при авторизации. Администраторы также смогут удалять аккаунты пользователей."
+ "claimedDomainsDescription": {
+ "message": "Заявите права на домен, чтобы владеть аккаунтами членов. Страница идентификатора SSO будет пропущена при пользователей с заявленными доменами, а администраторы смогут удалять заявленные аккаунты."
},
"invalidDomainNameClaimMessage": {
"message": "Введенные данные не соответствуют формату. Формат: mydomain.com. Для регистрации поддоменов необходимы отдельные записи."
@@ -10476,7 +10509,7 @@
"message": "В целях обеспечения безопасности вашего аккаунта продолжайте только в том случае, если вы являетесь членом этой организации, у вас включено восстановление аккаунта, а отображаемый ниже отпечаток совпадает с отпечатком организации."
},
"orgTrustWarning1": {
- "message": "В этой организации действует политика, которая позволит вам участвовать в восстановлении аккаунта. Регистрация позволит администраторам организации изменить ваш пароль. Продолжайте, только если вы знаете эту организацию и фраза отпечатков, показанная ниже, совпадает с отпечатками организации."
+ "message": "Эта организация имеет корпоративную политику, которая зарегистрирует вас в системе восстановления аккаунта. Регистрация позволит администраторам организации изменить ваш пароль. Продолжайте только в том случае, если вы узнаете эту организацию и фраза отпечатка, отображаемая ниже, соответствует отпечатку организации."
},
"trustUser": {
"message": "Доверенный пользователь"
@@ -10896,5 +10929,56 @@
"example": "12-3456789"
}
}
+ },
+ "unpaidInvoices": {
+ "message": "Неоплаченные счета"
+ },
+ "unpaidInvoicesForServiceUser": {
+ "message": "Ваша подписка не была оплачена. Обратитесь к администратору вашего провайдера, чтобы восстановить обслуживание для вас и ваших клиентов.",
+ "description": "A message shown in a non-dismissible dialog to service users of unpaid providers."
+ },
+ "providerSuspended": {
+ "message": "$PROVIDER$ приостановлен",
+ "placeholders": {
+ "provider": {
+ "content": "$1",
+ "example": "Acme Industries"
+ }
+ }
+ },
+ "restoreProviderPortalAccessViaCustomerSupport": {
+ "message": "Чтобы восстановить доступ к порталу вашего провайдера, обратитесь в службу поддержки Bitwarden для продления подписки.",
+ "description": "A message shown in a non-dismissible dialog to any user of a suspended providers."
+ },
+ "restoreProviderPortalAccessViaPaymentMethod": {
+ "message": "Ваша подписка не была оплачена. Чтобы восстановить обслуживание для вас и ваших клиентов, добавьте способ оплаты с помощью $CANCELLATION_DATE$.",
+ "placeholders": {
+ "cancellation_date": {
+ "content": "$1",
+ "example": "07/10/2025"
+ }
+ },
+ "description": "A message shown in a non-dismissible dialog to admins of unpaid providers."
+ },
+ "subscribetoEnterprise": {
+ "message": "Подписаться на $PLAN$",
+ "placeholders": {
+ "plan": {
+ "content": "$1",
+ "example": "Teams"
+ }
+ }
+ },
+ "subscribeEnterpriseSubtitle": {
+ "message": "Ваша 7-дневная пробная версия $PLAN$ начинается сегодня. Укажите способ оплаты прямо сейчас, чтобы продолжить пользоваться этими функциями после окончания пробной версии: ",
+ "placeholders": {
+ "plan": {
+ "content": "$1",
+ "example": "Teams"
+ }
+ }
+ },
+ "unlimitedSecretsAndProjects": {
+ "message": "Неограниченные секреты и проекты"
}
-}
+}
\ No newline at end of file
diff --git a/apps/web/src/locales/si/messages.json b/apps/web/src/locales/si/messages.json
index 03a569bc56f..be2aeaf3a65 100644
--- a/apps/web/src/locales/si/messages.json
+++ b/apps/web/src/locales/si/messages.json
@@ -568,6 +568,9 @@
"cancel": {
"message": "අවලංගු කරන්න"
},
+ "later": {
+ "message": "Later"
+ },
"canceled": {
"message": "Canceled"
},
@@ -626,6 +629,9 @@
"searchGroups": {
"message": "Search groups"
},
+ "resetSearch": {
+ "message": "Reset search"
+ },
"allItems": {
"message": "All items"
},
@@ -4626,6 +4632,9 @@
"receiveMarketingEmailsV2": {
"message": "Get advice, announcements, and research opportunities from Bitwarden in your inbox."
},
+ "subscribe": {
+ "message": "Subscribe"
+ },
"unsubscribe": {
"message": "Unsubscribe"
},
@@ -9465,6 +9474,12 @@
"deviceManagementDesc": {
"message": "Configure device management for Bitwarden using the implementation guide for your platform."
},
+ "crowdstrikeEventIntegrationDesc": {
+ "message": "Send event data to your Logscale instance"
+ },
+ "failedToSaveIntegration": {
+ "message": "Failed to save integration. Please try again later."
+ },
"deviceIdMissing": {
"message": "Device ID is missing"
},
@@ -9480,6 +9495,15 @@
"reopenLinkOnDesktop": {
"message": "Reopen this link from your email on a desktop."
},
+ "connectIntegrationButtonDesc": {
+ "message": "Connect $INTEGRATION$",
+ "placeholders": {
+ "integration": {
+ "content": "$1",
+ "example": "Crowdstrike"
+ }
+ }
+ },
"integrationCardTooltip": {
"message": "Launch $INTEGRATION$ implementation guide.",
"placeholders": {
@@ -9537,6 +9561,15 @@
"createNewClientToManageAsProvider": {
"message": "Create a new client organization to manage as a Provider. Additional seats will be reflected in the next billing cycle."
},
+ "url": {
+ "message": "URL"
+ },
+ "bearerToken": {
+ "message": "Bearer Token"
+ },
+ "index": {
+ "message": "Index"
+ },
"selectAPlan": {
"message": "Select a plan"
},
@@ -10285,8 +10318,8 @@
"organizationUserDeletedDesc": {
"message": "The user was removed from the organization and all associated user data has been deleted."
},
- "deletedUserId": {
- "message": "Deleted user $ID$ - an owner / admin deleted the user account",
+ "deletedUserIdEventMessage": {
+ "message": "Deleted user $ID$",
"placeholders": {
"id": {
"content": "$1",
@@ -10396,8 +10429,8 @@
"domainStatusUnderVerification": {
"message": "Under verification"
},
- "claimedDomainsDesc": {
- "message": "Claim a domain to own all member accounts whose email address matches the domain. Members will be able to skip the SSO identifier when logging in. Administrators will also be able to delete member accounts."
+ "claimedDomainsDescription": {
+ "message": "Claim a domain to own member accounts. The SSO identifier page will be skipped during login for members with claimed domains and administrators will be able to delete claimed accounts."
},
"invalidDomainNameClaimMessage": {
"message": "Input is not a valid format. Format: mydomain.com. Subdomains require separate entries to be claimed."
@@ -10896,5 +10929,56 @@
"example": "12-3456789"
}
}
+ },
+ "unpaidInvoices": {
+ "message": "Unpaid invoices"
+ },
+ "unpaidInvoicesForServiceUser": {
+ "message": "Your subscription has not been paid. Contact your provider administrator to restore service to you and your clients.",
+ "description": "A message shown in a non-dismissible dialog to service users of unpaid providers."
+ },
+ "providerSuspended": {
+ "message": "$PROVIDER$ is suspended",
+ "placeholders": {
+ "provider": {
+ "content": "$1",
+ "example": "Acme Industries"
+ }
+ }
+ },
+ "restoreProviderPortalAccessViaCustomerSupport": {
+ "message": "To restore access to your provider portal, contact Bitwarden Customer Support to renew your subscription.",
+ "description": "A message shown in a non-dismissible dialog to any user of a suspended providers."
+ },
+ "restoreProviderPortalAccessViaPaymentMethod": {
+ "message": "Your subscription has not been paid. To restore service to you and your clients, add a payment method by $CANCELLATION_DATE$.",
+ "placeholders": {
+ "cancellation_date": {
+ "content": "$1",
+ "example": "07/10/2025"
+ }
+ },
+ "description": "A message shown in a non-dismissible dialog to admins of unpaid providers."
+ },
+ "subscribetoEnterprise": {
+ "message": "Subscribe to $PLAN$",
+ "placeholders": {
+ "plan": {
+ "content": "$1",
+ "example": "Teams"
+ }
+ }
+ },
+ "subscribeEnterpriseSubtitle": {
+ "message": "Your 7-day $PLAN$ trial starts today. Add a payment method now to continue using these features after your trial ends: ",
+ "placeholders": {
+ "plan": {
+ "content": "$1",
+ "example": "Teams"
+ }
+ }
+ },
+ "unlimitedSecretsAndProjects": {
+ "message": "Unlimited secrets and projects"
}
-}
+}
\ No newline at end of file
diff --git a/apps/web/src/locales/sk/messages.json b/apps/web/src/locales/sk/messages.json
index 5543fc20820..e43d2c30be4 100644
--- a/apps/web/src/locales/sk/messages.json
+++ b/apps/web/src/locales/sk/messages.json
@@ -568,6 +568,9 @@
"cancel": {
"message": "Zrušiť"
},
+ "later": {
+ "message": "Neskôr"
+ },
"canceled": {
"message": "Zrušené"
},
@@ -626,6 +629,9 @@
"searchGroups": {
"message": "Search groups"
},
+ "resetSearch": {
+ "message": "Resetovať vyhľadávanie"
+ },
"allItems": {
"message": "Všetky položky"
},
@@ -4626,6 +4632,9 @@
"receiveMarketingEmailsV2": {
"message": "Dostávajte do schránky rady, oznámenia a príležitosti na výskum od spoločnosti Bitwarden."
},
+ "subscribe": {
+ "message": "Predplatiť si"
+ },
"unsubscribe": {
"message": "Odhlásiť sa z odberu"
},
@@ -9465,6 +9474,12 @@
"deviceManagementDesc": {
"message": "Configure device management for Bitwarden using the implementation guide for your platform."
},
+ "crowdstrikeEventIntegrationDesc": {
+ "message": "Pošlite dáta z denníka udalostí do vašej inštancie Logscale"
+ },
+ "failedToSaveIntegration": {
+ "message": "Failed to save integration. Please try again later."
+ },
"deviceIdMissing": {
"message": "Chýba ID zariadenia"
},
@@ -9480,6 +9495,15 @@
"reopenLinkOnDesktop": {
"message": "Otvorte tento odkaz z emailu na desktope."
},
+ "connectIntegrationButtonDesc": {
+ "message": "Prepojiť $INTEGRATION$",
+ "placeholders": {
+ "integration": {
+ "content": "$1",
+ "example": "Crowdstrike"
+ }
+ }
+ },
"integrationCardTooltip": {
"message": "Launch $INTEGRATION$ implementation guide.",
"placeholders": {
@@ -9537,6 +9561,15 @@
"createNewClientToManageAsProvider": {
"message": "Vytvoriť novú klientskú organizáciu ktorú môžete spravovať ako Poskytovateľ. Dodatočné sedenia sa prejavia v najbližšom fakturačnom období."
},
+ "url": {
+ "message": "URL"
+ },
+ "bearerToken": {
+ "message": "Bearer Token"
+ },
+ "index": {
+ "message": "Index"
+ },
"selectAPlan": {
"message": "Vyberte plán"
},
@@ -10285,8 +10318,8 @@
"organizationUserDeletedDesc": {
"message": "Používateľ bol odstránený z organizácie a všetky súvisiace dáta boli vymazané."
},
- "deletedUserId": {
- "message": "Odstránený používateľ $ID$ - vlastnik / správca vymazal používateľský účet",
+ "deletedUserIdEventMessage": {
+ "message": "Používateľ $ID$ vymazaný",
"placeholders": {
"id": {
"content": "$1",
@@ -10396,8 +10429,8 @@
"domainStatusUnderVerification": {
"message": "Overuje sa"
},
- "claimedDomainsDesc": {
- "message": "Privlastnite si doménu, aby ste vlastnili všetky členské účty, ktorých e-mailová adresa sa zhoduje s doménou. Členovia budú môcť pri prihlasovaní preskočiť identifikátor SSO. Správcovia budú môcť tiež vymazávať členské účty."
+ "claimedDomainsDescription": {
+ "message": "Privlastnite si doménu, aby ste mohli vlastniť členské účty. Stránka s identifikátorom SSO bude pri prihlásení členov s privlastnenými doménami preskočená a správcovia budú môcť členské účty odstrániť."
},
"invalidDomainNameClaimMessage": {
"message": "Zadaný neplatný formát. Formát: mydomain.com. Pre privlastnenie subdomén musia byť tieto každá zadaná individuálne."
@@ -10896,5 +10929,56 @@
"example": "12-3456789"
}
}
+ },
+ "unpaidInvoices": {
+ "message": "Neuhradené faktúry"
+ },
+ "unpaidInvoicesForServiceUser": {
+ "message": "Predplatné nebolo uhradené. Pre obnovu služby pre vás a vašich klientov, kontaktujte správcu vášho poskytovateľa.",
+ "description": "A message shown in a non-dismissible dialog to service users of unpaid providers."
+ },
+ "providerSuspended": {
+ "message": "$PROVIDER$ je pozastavený",
+ "placeholders": {
+ "provider": {
+ "content": "$1",
+ "example": "Acme Industries"
+ }
+ }
+ },
+ "restoreProviderPortalAccessViaCustomerSupport": {
+ "message": "Pre obnovu prístupu k portálu poskytovateľa, kontaktujte zákaznícku podporu Bitwarden a obnovte vaše predplatné.",
+ "description": "A message shown in a non-dismissible dialog to any user of a suspended providers."
+ },
+ "restoreProviderPortalAccessViaPaymentMethod": {
+ "message": "Predplatné nebolo uhradené. Pre obnovu služby pre vás a vašich klientov, pridajte platobnú metódu do $CANCELLATION_DATE$.",
+ "placeholders": {
+ "cancellation_date": {
+ "content": "$1",
+ "example": "07/10/2025"
+ }
+ },
+ "description": "A message shown in a non-dismissible dialog to admins of unpaid providers."
+ },
+ "subscribetoEnterprise": {
+ "message": "Predplatiť si $PLAN$",
+ "placeholders": {
+ "plan": {
+ "content": "$1",
+ "example": "Teams"
+ }
+ }
+ },
+ "subscribeEnterpriseSubtitle": {
+ "message": "Vaša 7-dňová skúšobná doba pre $PLAN$ začína dnes. Ak chcete používať nasledujúce funkcie po uplynutí skúšobnej doby, pridajte platobnú metódu: ",
+ "placeholders": {
+ "plan": {
+ "content": "$1",
+ "example": "Teams"
+ }
+ }
+ },
+ "unlimitedSecretsAndProjects": {
+ "message": "Neobmedzený počet položiek a projektov"
}
-}
+}
\ No newline at end of file
diff --git a/apps/web/src/locales/sl/messages.json b/apps/web/src/locales/sl/messages.json
index d210b85efb4..f4646e04244 100644
--- a/apps/web/src/locales/sl/messages.json
+++ b/apps/web/src/locales/sl/messages.json
@@ -568,6 +568,9 @@
"cancel": {
"message": "Prekliči"
},
+ "later": {
+ "message": "Later"
+ },
"canceled": {
"message": "Preklicano"
},
@@ -626,6 +629,9 @@
"searchGroups": {
"message": "Išči med skupinami"
},
+ "resetSearch": {
+ "message": "Reset search"
+ },
"allItems": {
"message": "Vsi elementi"
},
@@ -4626,6 +4632,9 @@
"receiveMarketingEmailsV2": {
"message": "Get advice, announcements, and research opportunities from Bitwarden in your inbox."
},
+ "subscribe": {
+ "message": "Subscribe"
+ },
"unsubscribe": {
"message": "Unsubscribe"
},
@@ -9465,6 +9474,12 @@
"deviceManagementDesc": {
"message": "Configure device management for Bitwarden using the implementation guide for your platform."
},
+ "crowdstrikeEventIntegrationDesc": {
+ "message": "Send event data to your Logscale instance"
+ },
+ "failedToSaveIntegration": {
+ "message": "Failed to save integration. Please try again later."
+ },
"deviceIdMissing": {
"message": "Device ID is missing"
},
@@ -9480,6 +9495,15 @@
"reopenLinkOnDesktop": {
"message": "Reopen this link from your email on a desktop."
},
+ "connectIntegrationButtonDesc": {
+ "message": "Connect $INTEGRATION$",
+ "placeholders": {
+ "integration": {
+ "content": "$1",
+ "example": "Crowdstrike"
+ }
+ }
+ },
"integrationCardTooltip": {
"message": "Launch $INTEGRATION$ implementation guide.",
"placeholders": {
@@ -9537,6 +9561,15 @@
"createNewClientToManageAsProvider": {
"message": "Create a new client organization to manage as a Provider. Additional seats will be reflected in the next billing cycle."
},
+ "url": {
+ "message": "URL"
+ },
+ "bearerToken": {
+ "message": "Bearer Token"
+ },
+ "index": {
+ "message": "Index"
+ },
"selectAPlan": {
"message": "Select a plan"
},
@@ -10285,8 +10318,8 @@
"organizationUserDeletedDesc": {
"message": "The user was removed from the organization and all associated user data has been deleted."
},
- "deletedUserId": {
- "message": "Deleted user $ID$ - an owner / admin deleted the user account",
+ "deletedUserIdEventMessage": {
+ "message": "Deleted user $ID$",
"placeholders": {
"id": {
"content": "$1",
@@ -10396,8 +10429,8 @@
"domainStatusUnderVerification": {
"message": "Under verification"
},
- "claimedDomainsDesc": {
- "message": "Claim a domain to own all member accounts whose email address matches the domain. Members will be able to skip the SSO identifier when logging in. Administrators will also be able to delete member accounts."
+ "claimedDomainsDescription": {
+ "message": "Claim a domain to own member accounts. The SSO identifier page will be skipped during login for members with claimed domains and administrators will be able to delete claimed accounts."
},
"invalidDomainNameClaimMessage": {
"message": "Input is not a valid format. Format: mydomain.com. Subdomains require separate entries to be claimed."
@@ -10896,5 +10929,56 @@
"example": "12-3456789"
}
}
+ },
+ "unpaidInvoices": {
+ "message": "Unpaid invoices"
+ },
+ "unpaidInvoicesForServiceUser": {
+ "message": "Your subscription has not been paid. Contact your provider administrator to restore service to you and your clients.",
+ "description": "A message shown in a non-dismissible dialog to service users of unpaid providers."
+ },
+ "providerSuspended": {
+ "message": "$PROVIDER$ is suspended",
+ "placeholders": {
+ "provider": {
+ "content": "$1",
+ "example": "Acme Industries"
+ }
+ }
+ },
+ "restoreProviderPortalAccessViaCustomerSupport": {
+ "message": "To restore access to your provider portal, contact Bitwarden Customer Support to renew your subscription.",
+ "description": "A message shown in a non-dismissible dialog to any user of a suspended providers."
+ },
+ "restoreProviderPortalAccessViaPaymentMethod": {
+ "message": "Your subscription has not been paid. To restore service to you and your clients, add a payment method by $CANCELLATION_DATE$.",
+ "placeholders": {
+ "cancellation_date": {
+ "content": "$1",
+ "example": "07/10/2025"
+ }
+ },
+ "description": "A message shown in a non-dismissible dialog to admins of unpaid providers."
+ },
+ "subscribetoEnterprise": {
+ "message": "Subscribe to $PLAN$",
+ "placeholders": {
+ "plan": {
+ "content": "$1",
+ "example": "Teams"
+ }
+ }
+ },
+ "subscribeEnterpriseSubtitle": {
+ "message": "Your 7-day $PLAN$ trial starts today. Add a payment method now to continue using these features after your trial ends: ",
+ "placeholders": {
+ "plan": {
+ "content": "$1",
+ "example": "Teams"
+ }
+ }
+ },
+ "unlimitedSecretsAndProjects": {
+ "message": "Unlimited secrets and projects"
}
-}
+}
\ No newline at end of file
diff --git a/apps/web/src/locales/sr_CS/messages.json b/apps/web/src/locales/sr_CS/messages.json
index eab8e098d81..f370c3e61ec 100644
--- a/apps/web/src/locales/sr_CS/messages.json
+++ b/apps/web/src/locales/sr_CS/messages.json
@@ -568,6 +568,9 @@
"cancel": {
"message": "Odustani"
},
+ "later": {
+ "message": "Later"
+ },
"canceled": {
"message": "Otkazano"
},
@@ -626,6 +629,9 @@
"searchGroups": {
"message": "Search groups"
},
+ "resetSearch": {
+ "message": "Reset search"
+ },
"allItems": {
"message": "Sve stavke"
},
@@ -4626,6 +4632,9 @@
"receiveMarketingEmailsV2": {
"message": "Get advice, announcements, and research opportunities from Bitwarden in your inbox."
},
+ "subscribe": {
+ "message": "Subscribe"
+ },
"unsubscribe": {
"message": "Unsubscribe"
},
@@ -9465,6 +9474,12 @@
"deviceManagementDesc": {
"message": "Configure device management for Bitwarden using the implementation guide for your platform."
},
+ "crowdstrikeEventIntegrationDesc": {
+ "message": "Send event data to your Logscale instance"
+ },
+ "failedToSaveIntegration": {
+ "message": "Failed to save integration. Please try again later."
+ },
"deviceIdMissing": {
"message": "Device ID is missing"
},
@@ -9480,6 +9495,15 @@
"reopenLinkOnDesktop": {
"message": "Reopen this link from your email on a desktop."
},
+ "connectIntegrationButtonDesc": {
+ "message": "Connect $INTEGRATION$",
+ "placeholders": {
+ "integration": {
+ "content": "$1",
+ "example": "Crowdstrike"
+ }
+ }
+ },
"integrationCardTooltip": {
"message": "Launch $INTEGRATION$ implementation guide.",
"placeholders": {
@@ -9537,6 +9561,15 @@
"createNewClientToManageAsProvider": {
"message": "Create a new client organization to manage as a Provider. Additional seats will be reflected in the next billing cycle."
},
+ "url": {
+ "message": "URL"
+ },
+ "bearerToken": {
+ "message": "Bearer Token"
+ },
+ "index": {
+ "message": "Index"
+ },
"selectAPlan": {
"message": "Select a plan"
},
@@ -10285,8 +10318,8 @@
"organizationUserDeletedDesc": {
"message": "The user was removed from the organization and all associated user data has been deleted."
},
- "deletedUserId": {
- "message": "Deleted user $ID$ - an owner / admin deleted the user account",
+ "deletedUserIdEventMessage": {
+ "message": "Deleted user $ID$",
"placeholders": {
"id": {
"content": "$1",
@@ -10396,8 +10429,8 @@
"domainStatusUnderVerification": {
"message": "Under verification"
},
- "claimedDomainsDesc": {
- "message": "Claim a domain to own all member accounts whose email address matches the domain. Members will be able to skip the SSO identifier when logging in. Administrators will also be able to delete member accounts."
+ "claimedDomainsDescription": {
+ "message": "Claim a domain to own member accounts. The SSO identifier page will be skipped during login for members with claimed domains and administrators will be able to delete claimed accounts."
},
"invalidDomainNameClaimMessage": {
"message": "Input is not a valid format. Format: mydomain.com. Subdomains require separate entries to be claimed."
@@ -10896,5 +10929,56 @@
"example": "12-3456789"
}
}
+ },
+ "unpaidInvoices": {
+ "message": "Unpaid invoices"
+ },
+ "unpaidInvoicesForServiceUser": {
+ "message": "Your subscription has not been paid. Contact your provider administrator to restore service to you and your clients.",
+ "description": "A message shown in a non-dismissible dialog to service users of unpaid providers."
+ },
+ "providerSuspended": {
+ "message": "$PROVIDER$ is suspended",
+ "placeholders": {
+ "provider": {
+ "content": "$1",
+ "example": "Acme Industries"
+ }
+ }
+ },
+ "restoreProviderPortalAccessViaCustomerSupport": {
+ "message": "To restore access to your provider portal, contact Bitwarden Customer Support to renew your subscription.",
+ "description": "A message shown in a non-dismissible dialog to any user of a suspended providers."
+ },
+ "restoreProviderPortalAccessViaPaymentMethod": {
+ "message": "Your subscription has not been paid. To restore service to you and your clients, add a payment method by $CANCELLATION_DATE$.",
+ "placeholders": {
+ "cancellation_date": {
+ "content": "$1",
+ "example": "07/10/2025"
+ }
+ },
+ "description": "A message shown in a non-dismissible dialog to admins of unpaid providers."
+ },
+ "subscribetoEnterprise": {
+ "message": "Subscribe to $PLAN$",
+ "placeholders": {
+ "plan": {
+ "content": "$1",
+ "example": "Teams"
+ }
+ }
+ },
+ "subscribeEnterpriseSubtitle": {
+ "message": "Your 7-day $PLAN$ trial starts today. Add a payment method now to continue using these features after your trial ends: ",
+ "placeholders": {
+ "plan": {
+ "content": "$1",
+ "example": "Teams"
+ }
+ }
+ },
+ "unlimitedSecretsAndProjects": {
+ "message": "Unlimited secrets and projects"
}
-}
+}
\ No newline at end of file
diff --git a/apps/web/src/locales/sr_CY/messages.json b/apps/web/src/locales/sr_CY/messages.json
index 2bdd42ff340..cab47f2de3b 100644
--- a/apps/web/src/locales/sr_CY/messages.json
+++ b/apps/web/src/locales/sr_CY/messages.json
@@ -568,6 +568,9 @@
"cancel": {
"message": "Откажи"
},
+ "later": {
+ "message": "Касније"
+ },
"canceled": {
"message": "Отказано"
},
@@ -626,6 +629,9 @@
"searchGroups": {
"message": "Претрага група"
},
+ "resetSearch": {
+ "message": "Ресетовати претрагу"
+ },
"allItems": {
"message": "Све ставке"
},
@@ -648,7 +654,7 @@
"message": "Сигурносна белешка"
},
"typeNote": {
- "message": "Note"
+ "message": "Белешка"
},
"typeSshKey": {
"message": "SSH кључ"
@@ -865,10 +871,10 @@
"message": "Копирати име"
},
"cardNumber": {
- "message": "card number"
+ "message": "број картице"
},
"copyFieldCipherName": {
- "message": "Copy $FIELD$, $CIPHERNAME$",
+ "message": "Копирај $FIELD$, $CIPHERNAME$",
"description": "Title for a button that copies a field value to the clipboard.",
"placeholders": {
"field": {
@@ -3503,7 +3509,7 @@
"message": "Интернет Сеф"
},
"webApp": {
- "message": "Web app"
+ "message": "Веб апликација"
},
"cli": {
"message": "CLI"
@@ -3988,7 +3994,7 @@
"message": "Одбили сте покушај пријаве са другог уређаја. Ако сте то заиста били ви, покушајте поново да се пријавите помоћу уређаја."
},
"loginRequestApprovedForEmailOnDevice": {
- "message": "Login request approved for $EMAIL$ on $DEVICE$",
+ "message": "Захтев за пријаву одобрен за $EMAIL$ на $DEVICE$",
"placeholders": {
"email": {
"content": "$1",
@@ -4001,7 +4007,7 @@
}
},
"youDeniedLoginAttemptFromAnotherDevice": {
- "message": "You denied a login attempt from another device. If this was you, try to log in with the device again."
+ "message": "Одбили сте покушај пријаве са другог уређаја. Ако сте то били ви, покушајте поново да се пријавите помоћу уређаја."
},
"loginRequestHasAlreadyExpired": {
"message": "Захтев за пријаву је већ истекао."
@@ -4148,7 +4154,7 @@
"message": "Прегледајте захтев за пријаву"
},
"loginRequest": {
- "message": "Login request"
+ "message": "Захтев за пријаву"
},
"freeTrialEndPromptCount": {
"message": "Ваша проба се завршава за $COUNT$ дана.",
@@ -4626,6 +4632,9 @@
"receiveMarketingEmailsV2": {
"message": "Добијајте савете, најаве и могућности истраживања од Bitwarden-а у пријемном сандучету."
},
+ "subscribe": {
+ "message": "Претплати се"
+ },
"unsubscribe": {
"message": "Одјави се"
},
@@ -9465,6 +9474,12 @@
"deviceManagementDesc": {
"message": "Конфигуришите управљање уређајима за Bitwarden помоћу водича за имплементацију за своју платформу."
},
+ "crowdstrikeEventIntegrationDesc": {
+ "message": "Пошаљите податке о догађају на вашу инстанцу Logscale-а"
+ },
+ "failedToSaveIntegration": {
+ "message": "Није успело сачувавање интеграције. Покушајте поново касније."
+ },
"deviceIdMissing": {
"message": "Недостаје ИД уређаја"
},
@@ -9480,6 +9495,15 @@
"reopenLinkOnDesktop": {
"message": "Reopen this link from your email on a desktop."
},
+ "connectIntegrationButtonDesc": {
+ "message": "Повезати $INTEGRATION$",
+ "placeholders": {
+ "integration": {
+ "content": "$1",
+ "example": "Crowdstrike"
+ }
+ }
+ },
"integrationCardTooltip": {
"message": "Покренути $INTEGRATION$ водич за имплементацију.",
"placeholders": {
@@ -9537,6 +9561,15 @@
"createNewClientToManageAsProvider": {
"message": "Креирајте нову клијентску организацију којом ћете управљати као добављач. Додатна места ће се одразити у следећем обрачунском циклусу."
},
+ "url": {
+ "message": "УРЛ"
+ },
+ "bearerToken": {
+ "message": "Токен носиоца"
+ },
+ "index": {
+ "message": "Индекс"
+ },
"selectAPlan": {
"message": "Изаберите пакет"
},
@@ -10285,8 +10318,8 @@
"organizationUserDeletedDesc": {
"message": "Корисник је уклоњен из организације и сви повезани кориснички подаци су избрисани."
},
- "deletedUserId": {
- "message": "Deleted user $ID$ - an owner / admin deleted the user account",
+ "deletedUserIdEventMessage": {
+ "message": "Корисник $ID$ је обрисан",
"placeholders": {
"id": {
"content": "$1",
@@ -10396,8 +10429,8 @@
"domainStatusUnderVerification": {
"message": "Под провером"
},
- "claimedDomainsDesc": {
- "message": "Claim a domain to own all member accounts whose email address matches the domain. Members will be able to skip the SSO identifier when logging in. Administrators will also be able to delete member accounts."
+ "claimedDomainsDescription": {
+ "message": "Захтевајте домен на сопствени рачуни чланова. Страница SSO идентификатора биће прескочена током пријаве за чланове са захтевним доменима и администратори ће моћи да избрише захтевене рачуне."
},
"invalidDomainNameClaimMessage": {
"message": "Input is not a valid format. Format: mydomain.com. Subdomains require separate entries to be claimed."
@@ -10896,5 +10929,56 @@
"example": "12-3456789"
}
}
+ },
+ "unpaidInvoices": {
+ "message": "Неплаћени рачуни"
+ },
+ "unpaidInvoicesForServiceUser": {
+ "message": "Ваша претплата није плаћена. Обратите се свом администратору провајдера да бисте вратили услугу вама и својим клијентима.",
+ "description": "A message shown in a non-dismissible dialog to service users of unpaid providers."
+ },
+ "providerSuspended": {
+ "message": "$PROVIDER$ је суспендован",
+ "placeholders": {
+ "provider": {
+ "content": "$1",
+ "example": "Acme Industries"
+ }
+ }
+ },
+ "restoreProviderPortalAccessViaCustomerSupport": {
+ "message": "Да бисте вратили приступ порталу провајдера, контактирајте Bitwarden корисничку подршку да бисте обновили претплату.",
+ "description": "A message shown in a non-dismissible dialog to any user of a suspended providers."
+ },
+ "restoreProviderPortalAccessViaPaymentMethod": {
+ "message": "Ваша претплата није плаћена. Да бисте вратили услугу вама и својим клијентима, додајте начин плаћања до $CANCELLATION_DATE$.",
+ "placeholders": {
+ "cancellation_date": {
+ "content": "$1",
+ "example": "07/10/2025"
+ }
+ },
+ "description": "A message shown in a non-dismissible dialog to admins of unpaid providers."
+ },
+ "subscribetoEnterprise": {
+ "message": "Претплати се на $PLAN$",
+ "placeholders": {
+ "plan": {
+ "content": "$1",
+ "example": "Teams"
+ }
+ }
+ },
+ "subscribeEnterpriseSubtitle": {
+ "message": "Ваш 7-дневни пробни $PLAN$ почиње данас. Додајте начин плаћања сада да бисте наставили да користите ове функције након завршетка вашег суђења: ",
+ "placeholders": {
+ "plan": {
+ "content": "$1",
+ "example": "Teams"
+ }
+ }
+ },
+ "unlimitedSecretsAndProjects": {
+ "message": "Неограничене тајне и пројекте"
}
-}
+}
\ No newline at end of file
diff --git a/apps/web/src/locales/sv/messages.json b/apps/web/src/locales/sv/messages.json
index 6fea9ba15d3..1a580776494 100644
--- a/apps/web/src/locales/sv/messages.json
+++ b/apps/web/src/locales/sv/messages.json
@@ -568,6 +568,9 @@
"cancel": {
"message": "Avbryt"
},
+ "later": {
+ "message": "Senare"
+ },
"canceled": {
"message": "Avbruten"
},
@@ -626,6 +629,9 @@
"searchGroups": {
"message": "Sök i grupper"
},
+ "resetSearch": {
+ "message": "Nollställ sökning"
+ },
"allItems": {
"message": "Alla objekt"
},
@@ -1102,7 +1108,7 @@
"message": "Ogiltig nyckel. Försök igen."
},
"twoFactorForPasskeysNotSupportedOnClientUpdateToLogIn": {
- "message": "Tvåstegsverifiering stöds inte för nycklar. Uppdatera appen för att logga in."
+ "message": "Tvåstegsverifiering stöds inte för inloggningsnycklar. Uppdatera appen för att logga in."
},
"loginWithPasskeyInfo": {
"message": "Använd en genererad inloggningsnyckel som automatiskt loggar in dig utan lösenord. Din identitet verifieras med biometri, såsom ansiktsigenkänning eller fingeravtryck, eller en annan FIDO2-säkerhetsmetod."
@@ -1168,7 +1174,7 @@
"message": "Ta bort inloggningsnyckel"
},
"removePasskeyInfo": {
- "message": "Om alla nycklar tas bort kommer du inte kunna logga in på nya enheter utan ditt huvudlösenord."
+ "message": "Om alla inloggningsnycklar tas bort kommer du inte kunna logga in på nya enheter utan ditt huvudlösenord."
},
"passkeyLimitReachedInfo": {
"message": "Gränsen för antal inloggningsnycklar har uppnåtts. Ta bort en inloggningsnyckel innan du lägger till en ny."
@@ -4626,6 +4632,9 @@
"receiveMarketingEmailsV2": {
"message": "Få råd, tillkännagivanden och forskningsmöjligheter från Bitwarden i din inkorg."
},
+ "subscribe": {
+ "message": "Abonnera"
+ },
"unsubscribe": {
"message": "Avsluta prenumeration"
},
@@ -9465,6 +9474,12 @@
"deviceManagementDesc": {
"message": "Konfigurera enhetshanteringen för Bitwarden med hjälp av implementeringsguiden för din plattform."
},
+ "crowdstrikeEventIntegrationDesc": {
+ "message": "Skicka händelsedata till din Logscale-instans"
+ },
+ "failedToSaveIntegration": {
+ "message": "Misslyckades med att spara integration. Försök igen senare."
+ },
"deviceIdMissing": {
"message": "Enhetens ID saknas"
},
@@ -9480,6 +9495,15 @@
"reopenLinkOnDesktop": {
"message": "Öppna den här länken igen från ditt e-postmeddelande på ett skrivbord."
},
+ "connectIntegrationButtonDesc": {
+ "message": "Anslut $INTEGRATION$",
+ "placeholders": {
+ "integration": {
+ "content": "$1",
+ "example": "Crowdstrike"
+ }
+ }
+ },
"integrationCardTooltip": {
"message": "Starta implementeringsguiden för $INTEGRATION$.",
"placeholders": {
@@ -9537,6 +9561,15 @@
"createNewClientToManageAsProvider": {
"message": "Skapa en ny kundorganisation som du kan hantera som Provider. Ytterligare platser kommer att återspeglas i nästa faktureringscykel."
},
+ "url": {
+ "message": "URL"
+ },
+ "bearerToken": {
+ "message": "Bärartoken"
+ },
+ "index": {
+ "message": "Index"
+ },
"selectAPlan": {
"message": "Välj en plan"
},
@@ -10285,8 +10318,8 @@
"organizationUserDeletedDesc": {
"message": "Användaren har tagits bort från organisationen och alla tillhörande användardata har raderats."
},
- "deletedUserId": {
- "message": "Borttagen användare $ID$ - en ägare/administratör har tagit bort användarkontot",
+ "deletedUserIdEventMessage": {
+ "message": "Tog bort användaren $ID$",
"placeholders": {
"id": {
"content": "$1",
@@ -10396,8 +10429,8 @@
"domainStatusUnderVerification": {
"message": "Under verifiering"
},
- "claimedDomainsDesc": {
- "message": "Gör anspråk på en domän för att äga alla medlemskonton vars e-postadress matchar domänen. Medlemmar kommer att kunna hoppa över SSO-identifieraren när de loggar in. Administratörer kommer också att kunna ta bort medlemskonton."
+ "claimedDomainsDescription": {
+ "message": "Begär en domän för att äga medlemskonton. SSO-identifierarsidan kommer att hoppas över under inloggningen för medlemmar med namngivna domäner och administratörer kommer att kunna ta bort begärda konton."
},
"invalidDomainNameClaimMessage": {
"message": "Inmatningen är inte ett giltigt format. Format: mydomain.com. Underdomäner kräver separata poster för att hävdas."
@@ -10896,5 +10929,56 @@
"example": "12-3456789"
}
}
+ },
+ "unpaidInvoices": {
+ "message": "Obetalda fakturor"
+ },
+ "unpaidInvoicesForServiceUser": {
+ "message": "Din prenumeration har inte betalats. Kontakta din leverantörsadministratör för att återställa tjänsten för dig och dina kunder.",
+ "description": "A message shown in a non-dismissible dialog to service users of unpaid providers."
+ },
+ "providerSuspended": {
+ "message": "$PROVIDER$ är avstängd",
+ "placeholders": {
+ "provider": {
+ "content": "$1",
+ "example": "Acme Industries"
+ }
+ }
+ },
+ "restoreProviderPortalAccessViaCustomerSupport": {
+ "message": "För att återställa åtkomsten till din leverantörsportal, kontakta Bitwardens kundsupport för att förnya ditt abonnemang.",
+ "description": "A message shown in a non-dismissible dialog to any user of a suspended providers."
+ },
+ "restoreProviderPortalAccessViaPaymentMethod": {
+ "message": "Din prenumeration har inte betalats. För att återställa tjänsten för dig och dina kunder, lägg till en betalningsmetod senast $CANCELLATION_DATE$.",
+ "placeholders": {
+ "cancellation_date": {
+ "content": "$1",
+ "example": "07/10/2025"
+ }
+ },
+ "description": "A message shown in a non-dismissible dialog to admins of unpaid providers."
+ },
+ "subscribetoEnterprise": {
+ "message": "Abonnera på $PLAN$",
+ "placeholders": {
+ "plan": {
+ "content": "$1",
+ "example": "Teams"
+ }
+ }
+ },
+ "subscribeEnterpriseSubtitle": {
+ "message": "Din testperiod på 7 dagar för $PLAN$ börjar idag. Lägg till en betalningsmetod nu för att fortsätta använda dessa funktioner efter din testperiod avslutas: ",
+ "placeholders": {
+ "plan": {
+ "content": "$1",
+ "example": "Teams"
+ }
+ }
+ },
+ "unlimitedSecretsAndProjects": {
+ "message": "Obegränsade hemligheter och projekt"
}
-}
+}
\ No newline at end of file
diff --git a/apps/web/src/locales/te/messages.json b/apps/web/src/locales/te/messages.json
index 79d58617943..24e3fc8075f 100644
--- a/apps/web/src/locales/te/messages.json
+++ b/apps/web/src/locales/te/messages.json
@@ -568,6 +568,9 @@
"cancel": {
"message": "Cancel"
},
+ "later": {
+ "message": "Later"
+ },
"canceled": {
"message": "Canceled"
},
@@ -626,6 +629,9 @@
"searchGroups": {
"message": "Search groups"
},
+ "resetSearch": {
+ "message": "Reset search"
+ },
"allItems": {
"message": "All items"
},
@@ -4626,6 +4632,9 @@
"receiveMarketingEmailsV2": {
"message": "Get advice, announcements, and research opportunities from Bitwarden in your inbox."
},
+ "subscribe": {
+ "message": "Subscribe"
+ },
"unsubscribe": {
"message": "Unsubscribe"
},
@@ -9465,6 +9474,12 @@
"deviceManagementDesc": {
"message": "Configure device management for Bitwarden using the implementation guide for your platform."
},
+ "crowdstrikeEventIntegrationDesc": {
+ "message": "Send event data to your Logscale instance"
+ },
+ "failedToSaveIntegration": {
+ "message": "Failed to save integration. Please try again later."
+ },
"deviceIdMissing": {
"message": "Device ID is missing"
},
@@ -9480,6 +9495,15 @@
"reopenLinkOnDesktop": {
"message": "Reopen this link from your email on a desktop."
},
+ "connectIntegrationButtonDesc": {
+ "message": "Connect $INTEGRATION$",
+ "placeholders": {
+ "integration": {
+ "content": "$1",
+ "example": "Crowdstrike"
+ }
+ }
+ },
"integrationCardTooltip": {
"message": "Launch $INTEGRATION$ implementation guide.",
"placeholders": {
@@ -9537,6 +9561,15 @@
"createNewClientToManageAsProvider": {
"message": "Create a new client organization to manage as a Provider. Additional seats will be reflected in the next billing cycle."
},
+ "url": {
+ "message": "URL"
+ },
+ "bearerToken": {
+ "message": "Bearer Token"
+ },
+ "index": {
+ "message": "Index"
+ },
"selectAPlan": {
"message": "Select a plan"
},
@@ -10285,8 +10318,8 @@
"organizationUserDeletedDesc": {
"message": "The user was removed from the organization and all associated user data has been deleted."
},
- "deletedUserId": {
- "message": "Deleted user $ID$ - an owner / admin deleted the user account",
+ "deletedUserIdEventMessage": {
+ "message": "Deleted user $ID$",
"placeholders": {
"id": {
"content": "$1",
@@ -10396,8 +10429,8 @@
"domainStatusUnderVerification": {
"message": "Under verification"
},
- "claimedDomainsDesc": {
- "message": "Claim a domain to own all member accounts whose email address matches the domain. Members will be able to skip the SSO identifier when logging in. Administrators will also be able to delete member accounts."
+ "claimedDomainsDescription": {
+ "message": "Claim a domain to own member accounts. The SSO identifier page will be skipped during login for members with claimed domains and administrators will be able to delete claimed accounts."
},
"invalidDomainNameClaimMessage": {
"message": "Input is not a valid format. Format: mydomain.com. Subdomains require separate entries to be claimed."
@@ -10896,5 +10929,56 @@
"example": "12-3456789"
}
}
+ },
+ "unpaidInvoices": {
+ "message": "Unpaid invoices"
+ },
+ "unpaidInvoicesForServiceUser": {
+ "message": "Your subscription has not been paid. Contact your provider administrator to restore service to you and your clients.",
+ "description": "A message shown in a non-dismissible dialog to service users of unpaid providers."
+ },
+ "providerSuspended": {
+ "message": "$PROVIDER$ is suspended",
+ "placeholders": {
+ "provider": {
+ "content": "$1",
+ "example": "Acme Industries"
+ }
+ }
+ },
+ "restoreProviderPortalAccessViaCustomerSupport": {
+ "message": "To restore access to your provider portal, contact Bitwarden Customer Support to renew your subscription.",
+ "description": "A message shown in a non-dismissible dialog to any user of a suspended providers."
+ },
+ "restoreProviderPortalAccessViaPaymentMethod": {
+ "message": "Your subscription has not been paid. To restore service to you and your clients, add a payment method by $CANCELLATION_DATE$.",
+ "placeholders": {
+ "cancellation_date": {
+ "content": "$1",
+ "example": "07/10/2025"
+ }
+ },
+ "description": "A message shown in a non-dismissible dialog to admins of unpaid providers."
+ },
+ "subscribetoEnterprise": {
+ "message": "Subscribe to $PLAN$",
+ "placeholders": {
+ "plan": {
+ "content": "$1",
+ "example": "Teams"
+ }
+ }
+ },
+ "subscribeEnterpriseSubtitle": {
+ "message": "Your 7-day $PLAN$ trial starts today. Add a payment method now to continue using these features after your trial ends: ",
+ "placeholders": {
+ "plan": {
+ "content": "$1",
+ "example": "Teams"
+ }
+ }
+ },
+ "unlimitedSecretsAndProjects": {
+ "message": "Unlimited secrets and projects"
}
-}
+}
\ No newline at end of file
diff --git a/apps/web/src/locales/th/messages.json b/apps/web/src/locales/th/messages.json
index 7adae0d45f7..a9a386b350d 100644
--- a/apps/web/src/locales/th/messages.json
+++ b/apps/web/src/locales/th/messages.json
@@ -568,6 +568,9 @@
"cancel": {
"message": "ยกเลิก"
},
+ "later": {
+ "message": "Later"
+ },
"canceled": {
"message": "ยกเลิกแล้ว"
},
@@ -626,6 +629,9 @@
"searchGroups": {
"message": "Search groups"
},
+ "resetSearch": {
+ "message": "Reset search"
+ },
"allItems": {
"message": "ไอเทมทั้งหมด"
},
@@ -4626,6 +4632,9 @@
"receiveMarketingEmailsV2": {
"message": "Get advice, announcements, and research opportunities from Bitwarden in your inbox."
},
+ "subscribe": {
+ "message": "Subscribe"
+ },
"unsubscribe": {
"message": "Unsubscribe"
},
@@ -9465,6 +9474,12 @@
"deviceManagementDesc": {
"message": "Configure device management for Bitwarden using the implementation guide for your platform."
},
+ "crowdstrikeEventIntegrationDesc": {
+ "message": "Send event data to your Logscale instance"
+ },
+ "failedToSaveIntegration": {
+ "message": "Failed to save integration. Please try again later."
+ },
"deviceIdMissing": {
"message": "Device ID is missing"
},
@@ -9480,6 +9495,15 @@
"reopenLinkOnDesktop": {
"message": "Reopen this link from your email on a desktop."
},
+ "connectIntegrationButtonDesc": {
+ "message": "Connect $INTEGRATION$",
+ "placeholders": {
+ "integration": {
+ "content": "$1",
+ "example": "Crowdstrike"
+ }
+ }
+ },
"integrationCardTooltip": {
"message": "Launch $INTEGRATION$ implementation guide.",
"placeholders": {
@@ -9537,6 +9561,15 @@
"createNewClientToManageAsProvider": {
"message": "Create a new client organization to manage as a Provider. Additional seats will be reflected in the next billing cycle."
},
+ "url": {
+ "message": "URL"
+ },
+ "bearerToken": {
+ "message": "Bearer Token"
+ },
+ "index": {
+ "message": "Index"
+ },
"selectAPlan": {
"message": "Select a plan"
},
@@ -10285,8 +10318,8 @@
"organizationUserDeletedDesc": {
"message": "The user was removed from the organization and all associated user data has been deleted."
},
- "deletedUserId": {
- "message": "Deleted user $ID$ - an owner / admin deleted the user account",
+ "deletedUserIdEventMessage": {
+ "message": "Deleted user $ID$",
"placeholders": {
"id": {
"content": "$1",
@@ -10396,8 +10429,8 @@
"domainStatusUnderVerification": {
"message": "Under verification"
},
- "claimedDomainsDesc": {
- "message": "Claim a domain to own all member accounts whose email address matches the domain. Members will be able to skip the SSO identifier when logging in. Administrators will also be able to delete member accounts."
+ "claimedDomainsDescription": {
+ "message": "Claim a domain to own member accounts. The SSO identifier page will be skipped during login for members with claimed domains and administrators will be able to delete claimed accounts."
},
"invalidDomainNameClaimMessage": {
"message": "Input is not a valid format. Format: mydomain.com. Subdomains require separate entries to be claimed."
@@ -10896,5 +10929,56 @@
"example": "12-3456789"
}
}
+ },
+ "unpaidInvoices": {
+ "message": "Unpaid invoices"
+ },
+ "unpaidInvoicesForServiceUser": {
+ "message": "Your subscription has not been paid. Contact your provider administrator to restore service to you and your clients.",
+ "description": "A message shown in a non-dismissible dialog to service users of unpaid providers."
+ },
+ "providerSuspended": {
+ "message": "$PROVIDER$ is suspended",
+ "placeholders": {
+ "provider": {
+ "content": "$1",
+ "example": "Acme Industries"
+ }
+ }
+ },
+ "restoreProviderPortalAccessViaCustomerSupport": {
+ "message": "To restore access to your provider portal, contact Bitwarden Customer Support to renew your subscription.",
+ "description": "A message shown in a non-dismissible dialog to any user of a suspended providers."
+ },
+ "restoreProviderPortalAccessViaPaymentMethod": {
+ "message": "Your subscription has not been paid. To restore service to you and your clients, add a payment method by $CANCELLATION_DATE$.",
+ "placeholders": {
+ "cancellation_date": {
+ "content": "$1",
+ "example": "07/10/2025"
+ }
+ },
+ "description": "A message shown in a non-dismissible dialog to admins of unpaid providers."
+ },
+ "subscribetoEnterprise": {
+ "message": "Subscribe to $PLAN$",
+ "placeholders": {
+ "plan": {
+ "content": "$1",
+ "example": "Teams"
+ }
+ }
+ },
+ "subscribeEnterpriseSubtitle": {
+ "message": "Your 7-day $PLAN$ trial starts today. Add a payment method now to continue using these features after your trial ends: ",
+ "placeholders": {
+ "plan": {
+ "content": "$1",
+ "example": "Teams"
+ }
+ }
+ },
+ "unlimitedSecretsAndProjects": {
+ "message": "Unlimited secrets and projects"
}
-}
+}
\ No newline at end of file
diff --git a/apps/web/src/locales/tr/messages.json b/apps/web/src/locales/tr/messages.json
index 34b549ba813..e0ca6a42826 100644
--- a/apps/web/src/locales/tr/messages.json
+++ b/apps/web/src/locales/tr/messages.json
@@ -568,6 +568,9 @@
"cancel": {
"message": "İptal"
},
+ "later": {
+ "message": "Daha sonra"
+ },
"canceled": {
"message": "İptal edildi"
},
@@ -626,6 +629,9 @@
"searchGroups": {
"message": "Gruplarda ara"
},
+ "resetSearch": {
+ "message": "Aramayı sıfırla"
+ },
"allItems": {
"message": "Tüm kayıtlar"
},
@@ -4626,6 +4632,9 @@
"receiveMarketingEmailsV2": {
"message": "Bitwarden'dan öneriler, duyurular ve araştırma fırsatları e-posta adresinize gelsin."
},
+ "subscribe": {
+ "message": "Abone ol"
+ },
"unsubscribe": {
"message": "İstediğiniz zaman"
},
@@ -9465,6 +9474,12 @@
"deviceManagementDesc": {
"message": "Configure device management for Bitwarden using the implementation guide for your platform."
},
+ "crowdstrikeEventIntegrationDesc": {
+ "message": "Send event data to your Logscale instance"
+ },
+ "failedToSaveIntegration": {
+ "message": "Failed to save integration. Please try again later."
+ },
"deviceIdMissing": {
"message": "Device ID is missing"
},
@@ -9480,6 +9495,15 @@
"reopenLinkOnDesktop": {
"message": "Reopen this link from your email on a desktop."
},
+ "connectIntegrationButtonDesc": {
+ "message": "Connect $INTEGRATION$",
+ "placeholders": {
+ "integration": {
+ "content": "$1",
+ "example": "Crowdstrike"
+ }
+ }
+ },
"integrationCardTooltip": {
"message": "Launch $INTEGRATION$ implementation guide.",
"placeholders": {
@@ -9537,6 +9561,15 @@
"createNewClientToManageAsProvider": {
"message": "Create a new client organization to manage as a Provider. Additional seats will be reflected in the next billing cycle."
},
+ "url": {
+ "message": "URL"
+ },
+ "bearerToken": {
+ "message": "Bearer Token"
+ },
+ "index": {
+ "message": "Index"
+ },
"selectAPlan": {
"message": "Bir plan seçin"
},
@@ -10285,8 +10318,8 @@
"organizationUserDeletedDesc": {
"message": "The user was removed from the organization and all associated user data has been deleted."
},
- "deletedUserId": {
- "message": "Deleted user $ID$ - an owner / admin deleted the user account",
+ "deletedUserIdEventMessage": {
+ "message": "Kullanıcı silindi: $ID$",
"placeholders": {
"id": {
"content": "$1",
@@ -10396,8 +10429,8 @@
"domainStatusUnderVerification": {
"message": "Under verification"
},
- "claimedDomainsDesc": {
- "message": "Claim a domain to own all member accounts whose email address matches the domain. Members will be able to skip the SSO identifier when logging in. Administrators will also be able to delete member accounts."
+ "claimedDomainsDescription": {
+ "message": "Claim a domain to own member accounts. The SSO identifier page will be skipped during login for members with claimed domains and administrators will be able to delete claimed accounts."
},
"invalidDomainNameClaimMessage": {
"message": "Input is not a valid format. Format: mydomain.com. Subdomains require separate entries to be claimed."
@@ -10896,5 +10929,56 @@
"example": "12-3456789"
}
}
+ },
+ "unpaidInvoices": {
+ "message": "Ödenmemiş faturalar"
+ },
+ "unpaidInvoicesForServiceUser": {
+ "message": "Your subscription has not been paid. Contact your provider administrator to restore service to you and your clients.",
+ "description": "A message shown in a non-dismissible dialog to service users of unpaid providers."
+ },
+ "providerSuspended": {
+ "message": "$PROVIDER$ is suspended",
+ "placeholders": {
+ "provider": {
+ "content": "$1",
+ "example": "Acme Industries"
+ }
+ }
+ },
+ "restoreProviderPortalAccessViaCustomerSupport": {
+ "message": "To restore access to your provider portal, contact Bitwarden Customer Support to renew your subscription.",
+ "description": "A message shown in a non-dismissible dialog to any user of a suspended providers."
+ },
+ "restoreProviderPortalAccessViaPaymentMethod": {
+ "message": "Your subscription has not been paid. To restore service to you and your clients, add a payment method by $CANCELLATION_DATE$.",
+ "placeholders": {
+ "cancellation_date": {
+ "content": "$1",
+ "example": "07/10/2025"
+ }
+ },
+ "description": "A message shown in a non-dismissible dialog to admins of unpaid providers."
+ },
+ "subscribetoEnterprise": {
+ "message": "Subscribe to $PLAN$",
+ "placeholders": {
+ "plan": {
+ "content": "$1",
+ "example": "Teams"
+ }
+ }
+ },
+ "subscribeEnterpriseSubtitle": {
+ "message": "Your 7-day $PLAN$ trial starts today. Add a payment method now to continue using these features after your trial ends: ",
+ "placeholders": {
+ "plan": {
+ "content": "$1",
+ "example": "Teams"
+ }
+ }
+ },
+ "unlimitedSecretsAndProjects": {
+ "message": "Unlimited secrets and projects"
}
-}
+}
\ No newline at end of file
diff --git a/apps/web/src/locales/uk/messages.json b/apps/web/src/locales/uk/messages.json
index 3fa6e0cceb1..8276161df98 100644
--- a/apps/web/src/locales/uk/messages.json
+++ b/apps/web/src/locales/uk/messages.json
@@ -568,6 +568,9 @@
"cancel": {
"message": "Скасувати"
},
+ "later": {
+ "message": "Later"
+ },
"canceled": {
"message": "Скасовано"
},
@@ -626,6 +629,9 @@
"searchGroups": {
"message": "Пошук в групах"
},
+ "resetSearch": {
+ "message": "Reset search"
+ },
"allItems": {
"message": "Усі записи"
},
@@ -3985,7 +3991,7 @@
}
},
"youDeniedALogInAttemptFromAnotherDevice": {
- "message": "Ви відхилили спробу входу з іншого пристрою. Якщо це були дійсно ви, спробуйте увійти з пристроєм знову."
+ "message": "Ви відхилили спробу входу з іншого пристрою. Якщо це були дійсно ви, спробуйте ввійти з пристроєм знову."
},
"loginRequestApprovedForEmailOnDevice": {
"message": "Login request approved for $EMAIL$ on $DEVICE$",
@@ -4001,7 +4007,7 @@
}
},
"youDeniedLoginAttemptFromAnotherDevice": {
- "message": "You denied a login attempt from another device. If this was you, try to log in with the device again."
+ "message": "Ви відхилили спробу входу з іншого пристрою. Якщо це були ви, спробуйте ввійти з пристроєм знову."
},
"loginRequestHasAlreadyExpired": {
"message": "Термін дії запиту на вхід завершився."
@@ -4626,6 +4632,9 @@
"receiveMarketingEmailsV2": {
"message": "Отримуйте поради, оголошення та можливості дослідження від Bitwarden у своїй поштовій скриньці."
},
+ "subscribe": {
+ "message": "Subscribe"
+ },
"unsubscribe": {
"message": "Відписатися"
},
@@ -9465,6 +9474,12 @@
"deviceManagementDesc": {
"message": "Налаштуйте керування пристроями для Bitwarden, використовуючи посібник із впровадження для вашої платформи."
},
+ "crowdstrikeEventIntegrationDesc": {
+ "message": "Send event data to your Logscale instance"
+ },
+ "failedToSaveIntegration": {
+ "message": "Failed to save integration. Please try again later."
+ },
"deviceIdMissing": {
"message": "Відсутній ID пристрою"
},
@@ -9480,6 +9495,15 @@
"reopenLinkOnDesktop": {
"message": "Відкрийте це посилання з електронної пошти на комп'ютері."
},
+ "connectIntegrationButtonDesc": {
+ "message": "Connect $INTEGRATION$",
+ "placeholders": {
+ "integration": {
+ "content": "$1",
+ "example": "Crowdstrike"
+ }
+ }
+ },
"integrationCardTooltip": {
"message": "Відкрити посібник із впровадження $INTEGRATION$.",
"placeholders": {
@@ -9537,6 +9561,15 @@
"createNewClientToManageAsProvider": {
"message": "Створіть нову організацію клієнта, щоб керувати нею як провайдер. Додаткові місця будуть відображені в наступному платіжному циклі."
},
+ "url": {
+ "message": "URL"
+ },
+ "bearerToken": {
+ "message": "Bearer Token"
+ },
+ "index": {
+ "message": "Index"
+ },
"selectAPlan": {
"message": "Оберіть тарифний план"
},
@@ -10285,8 +10318,8 @@
"organizationUserDeletedDesc": {
"message": "Користувача вилучено з організації. Всі пов'язані дані користувача видалено."
},
- "deletedUserId": {
- "message": "Видалений користувач $ID$ - власник / адміністратор видалив обліковий запис користувача",
+ "deletedUserIdEventMessage": {
+ "message": "Deleted user $ID$",
"placeholders": {
"id": {
"content": "$1",
@@ -10396,8 +10429,8 @@
"domainStatusUnderVerification": {
"message": "Проходить перевірку"
},
- "claimedDomainsDesc": {
- "message": "Заявіть домен, щоб володіти всіма обліковими записами учасників, адреси е-пошти яких відповідають цьому домену. Учасники матимуть змогу пропустити ідентифікацію SSO під час входу в систему. Адміністратори також зможуть видаляти облікові записи учасників."
+ "claimedDomainsDescription": {
+ "message": "Claim a domain to own member accounts. The SSO identifier page will be skipped during login for members with claimed domains and administrators will be able to delete claimed accounts."
},
"invalidDomainNameClaimMessage": {
"message": "Неправильний формат введення. Правильний формат: mydomain.com. Для заявлення піддоменів потрібно ввести окремі записи."
@@ -10896,5 +10929,56 @@
"example": "12-3456789"
}
}
+ },
+ "unpaidInvoices": {
+ "message": "Unpaid invoices"
+ },
+ "unpaidInvoicesForServiceUser": {
+ "message": "Your subscription has not been paid. Contact your provider administrator to restore service to you and your clients.",
+ "description": "A message shown in a non-dismissible dialog to service users of unpaid providers."
+ },
+ "providerSuspended": {
+ "message": "$PROVIDER$ is suspended",
+ "placeholders": {
+ "provider": {
+ "content": "$1",
+ "example": "Acme Industries"
+ }
+ }
+ },
+ "restoreProviderPortalAccessViaCustomerSupport": {
+ "message": "To restore access to your provider portal, contact Bitwarden Customer Support to renew your subscription.",
+ "description": "A message shown in a non-dismissible dialog to any user of a suspended providers."
+ },
+ "restoreProviderPortalAccessViaPaymentMethod": {
+ "message": "Your subscription has not been paid. To restore service to you and your clients, add a payment method by $CANCELLATION_DATE$.",
+ "placeholders": {
+ "cancellation_date": {
+ "content": "$1",
+ "example": "07/10/2025"
+ }
+ },
+ "description": "A message shown in a non-dismissible dialog to admins of unpaid providers."
+ },
+ "subscribetoEnterprise": {
+ "message": "Subscribe to $PLAN$",
+ "placeholders": {
+ "plan": {
+ "content": "$1",
+ "example": "Teams"
+ }
+ }
+ },
+ "subscribeEnterpriseSubtitle": {
+ "message": "Your 7-day $PLAN$ trial starts today. Add a payment method now to continue using these features after your trial ends: ",
+ "placeholders": {
+ "plan": {
+ "content": "$1",
+ "example": "Teams"
+ }
+ }
+ },
+ "unlimitedSecretsAndProjects": {
+ "message": "Unlimited secrets and projects"
}
-}
+}
\ No newline at end of file
diff --git a/apps/web/src/locales/vi/messages.json b/apps/web/src/locales/vi/messages.json
index a9832ed445c..6d2b0902643 100644
--- a/apps/web/src/locales/vi/messages.json
+++ b/apps/web/src/locales/vi/messages.json
@@ -247,7 +247,7 @@
"message": "Thông tin thẻ"
},
"cardBrandDetails": {
- "message": "Chi tiết của $BRAND$",
+ "message": "Chi tiết thẻ $BRAND$",
"placeholders": {
"brand": {
"content": "$1",
@@ -405,7 +405,7 @@
"message": "Mx"
},
"dr": {
- "message": "Dr"
+ "message": "Tiến sĩ/Bác sĩ"
},
"cardExpiredTitle": {
"message": "Thẻ hết hạn"
@@ -568,6 +568,9 @@
"cancel": {
"message": "Hủy bỏ"
},
+ "later": {
+ "message": "Later"
+ },
"canceled": {
"message": "Đã hủy"
},
@@ -626,6 +629,9 @@
"searchGroups": {
"message": "Tìm nhóm"
},
+ "resetSearch": {
+ "message": "Reset search"
+ },
"allItems": {
"message": "Tất cả các mục"
},
@@ -4626,6 +4632,9 @@
"receiveMarketingEmailsV2": {
"message": "Get advice, announcements, and research opportunities from Bitwarden in your inbox."
},
+ "subscribe": {
+ "message": "Subscribe"
+ },
"unsubscribe": {
"message": "Hủy đăng ký"
},
@@ -5153,7 +5162,7 @@
}
},
"sendDetails": {
- "message": "Send details",
+ "message": "Chi tiết Send",
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
},
"sendTypeTextToShare": {
@@ -5251,7 +5260,7 @@
"message": "Đang chờ xóa"
},
"hideTextByDefault": {
- "message": "Mặc định ẩn văn bản"
+ "message": "Ẩn văn bản theo mặc định"
},
"expired": {
"message": "Đã hết hạn"
@@ -9465,6 +9474,12 @@
"deviceManagementDesc": {
"message": "Configure device management for Bitwarden using the implementation guide for your platform."
},
+ "crowdstrikeEventIntegrationDesc": {
+ "message": "Send event data to your Logscale instance"
+ },
+ "failedToSaveIntegration": {
+ "message": "Failed to save integration. Please try again later."
+ },
"deviceIdMissing": {
"message": "Device ID is missing"
},
@@ -9480,6 +9495,15 @@
"reopenLinkOnDesktop": {
"message": "Reopen this link from your email on a desktop."
},
+ "connectIntegrationButtonDesc": {
+ "message": "Connect $INTEGRATION$",
+ "placeholders": {
+ "integration": {
+ "content": "$1",
+ "example": "Crowdstrike"
+ }
+ }
+ },
"integrationCardTooltip": {
"message": "Launch $INTEGRATION$ implementation guide.",
"placeholders": {
@@ -9537,6 +9561,15 @@
"createNewClientToManageAsProvider": {
"message": "Create a new client organization to manage as a Provider. Additional seats will be reflected in the next billing cycle."
},
+ "url": {
+ "message": "URL"
+ },
+ "bearerToken": {
+ "message": "Bearer Token"
+ },
+ "index": {
+ "message": "Index"
+ },
"selectAPlan": {
"message": "Select a plan"
},
@@ -10285,8 +10318,8 @@
"organizationUserDeletedDesc": {
"message": "The user was removed from the organization and all associated user data has been deleted."
},
- "deletedUserId": {
- "message": "Deleted user $ID$ - an owner / admin deleted the user account",
+ "deletedUserIdEventMessage": {
+ "message": "Deleted user $ID$",
"placeholders": {
"id": {
"content": "$1",
@@ -10396,8 +10429,8 @@
"domainStatusUnderVerification": {
"message": "Under verification"
},
- "claimedDomainsDesc": {
- "message": "Claim a domain to own all member accounts whose email address matches the domain. Members will be able to skip the SSO identifier when logging in. Administrators will also be able to delete member accounts."
+ "claimedDomainsDescription": {
+ "message": "Claim a domain to own member accounts. The SSO identifier page will be skipped during login for members with claimed domains and administrators will be able to delete claimed accounts."
},
"invalidDomainNameClaimMessage": {
"message": "Định dạng nhập vào không hợp lệ. Định dạng: mydomain.com. Các tên miền con yêu cầu các mục riêng biệt để được xác nhận."
@@ -10896,5 +10929,56 @@
"example": "12-3456789"
}
}
+ },
+ "unpaidInvoices": {
+ "message": "Unpaid invoices"
+ },
+ "unpaidInvoicesForServiceUser": {
+ "message": "Your subscription has not been paid. Contact your provider administrator to restore service to you and your clients.",
+ "description": "A message shown in a non-dismissible dialog to service users of unpaid providers."
+ },
+ "providerSuspended": {
+ "message": "$PROVIDER$ is suspended",
+ "placeholders": {
+ "provider": {
+ "content": "$1",
+ "example": "Acme Industries"
+ }
+ }
+ },
+ "restoreProviderPortalAccessViaCustomerSupport": {
+ "message": "To restore access to your provider portal, contact Bitwarden Customer Support to renew your subscription.",
+ "description": "A message shown in a non-dismissible dialog to any user of a suspended providers."
+ },
+ "restoreProviderPortalAccessViaPaymentMethod": {
+ "message": "Your subscription has not been paid. To restore service to you and your clients, add a payment method by $CANCELLATION_DATE$.",
+ "placeholders": {
+ "cancellation_date": {
+ "content": "$1",
+ "example": "07/10/2025"
+ }
+ },
+ "description": "A message shown in a non-dismissible dialog to admins of unpaid providers."
+ },
+ "subscribetoEnterprise": {
+ "message": "Subscribe to $PLAN$",
+ "placeholders": {
+ "plan": {
+ "content": "$1",
+ "example": "Teams"
+ }
+ }
+ },
+ "subscribeEnterpriseSubtitle": {
+ "message": "Your 7-day $PLAN$ trial starts today. Add a payment method now to continue using these features after your trial ends: ",
+ "placeholders": {
+ "plan": {
+ "content": "$1",
+ "example": "Teams"
+ }
+ }
+ },
+ "unlimitedSecretsAndProjects": {
+ "message": "Unlimited secrets and projects"
}
-}
+}
\ No newline at end of file
diff --git a/apps/web/src/locales/zh_CN/messages.json b/apps/web/src/locales/zh_CN/messages.json
index 5bc6c472cd1..5a61a42212a 100644
--- a/apps/web/src/locales/zh_CN/messages.json
+++ b/apps/web/src/locales/zh_CN/messages.json
@@ -568,6 +568,9 @@
"cancel": {
"message": "取消"
},
+ "later": {
+ "message": "稍后"
+ },
"canceled": {
"message": "已取消"
},
@@ -626,6 +629,9 @@
"searchGroups": {
"message": "搜索群组"
},
+ "resetSearch": {
+ "message": "重置搜索"
+ },
"allItems": {
"message": "所有项目"
},
@@ -1805,7 +1811,7 @@
"message": "继续操作将使您退出当前会话,并要求您重新登录。其他设备上的活动会话可能会继续保持活动状态长达一小时。"
},
"changePasswordWarning": {
- "message": "更改密码后,您需要使用新密码登录。 在其他设备上的活动会话将在一小时内注销。"
+ "message": "更改密码后,您需要使用新密码登录。其他设备上的活动会话将在一小时内注销。"
},
"emailChanged": {
"message": "电子邮箱已保存"
@@ -2956,7 +2962,7 @@
"message": "付款方式"
},
"noPaymentMethod": {
- "message": "没有付款方式。"
+ "message": "没有存档的付款方式。"
},
"addPaymentMethod": {
"message": "添加付款方式"
@@ -3503,7 +3509,7 @@
"message": "网页密码库"
},
"webApp": {
- "message": "Web app"
+ "message": "网页 App"
},
"cli": {
"message": "CLI"
@@ -3988,7 +3994,7 @@
"message": "您拒绝了一个来自其他设备的登录尝试。若确实是您本人,请尝试再次发起设备登录。"
},
"loginRequestApprovedForEmailOnDevice": {
- "message": "Login request approved for $EMAIL$ on $DEVICE$",
+ "message": "已批准 $EMAIL$ 在 $DEVICE$ 上的登录请求",
"placeholders": {
"email": {
"content": "$1",
@@ -4001,7 +4007,7 @@
}
},
"youDeniedLoginAttemptFromAnotherDevice": {
- "message": "You denied a login attempt from another device. If this was you, try to log in with the device again."
+ "message": "您拒绝了一个来自其他设备的登录尝试。若确实是您本人,请尝试再次发起设备登录。"
},
"loginRequestHasAlreadyExpired": {
"message": "登录请求已过期。"
@@ -4148,7 +4154,7 @@
"message": "审查登录请求"
},
"loginRequest": {
- "message": "Login request"
+ "message": "登录请求"
},
"freeTrialEndPromptCount": {
"message": "您的免费试用将于 $COUNT$ 天后结束。",
@@ -4626,6 +4632,9 @@
"receiveMarketingEmailsV2": {
"message": "获取来自 Bitwarden 的建议、公告和调研电子邮件。"
},
+ "subscribe": {
+ "message": "订阅"
+ },
"unsubscribe": {
"message": "取消订阅"
},
@@ -8288,7 +8297,7 @@
"message": "尝试读取导入的文件时出错"
},
"accessedSecretWithId": {
- "message": "Accessed a secret with identifier: $SECRET_ID$",
+ "message": "访问了标识符为 $SECRET_ID$ 的机密",
"placeholders": {
"secret_id": {
"content": "$1",
@@ -8306,7 +8315,7 @@
}
},
"editedSecretWithId": {
- "message": "Edited a secret with identifier: $SECRET_ID$",
+ "message": "编辑了标识符为 $SECRET_ID$ 的机密",
"placeholders": {
"secret_id": {
"content": "$1",
@@ -8315,7 +8324,7 @@
}
},
"deletedSecretWithId": {
- "message": "Deleted a secret with identifier: $SECRET_ID$",
+ "message": "删除了标识符为 $SECRET_ID$ 的机密",
"placeholders": {
"secret_id": {
"content": "$1",
@@ -8324,7 +8333,7 @@
}
},
"createdSecretWithId": {
- "message": "Created a new secret with identifier: $SECRET_ID$",
+ "message": "创建了标识符为 $SECRET_ID$ 的新机密",
"placeholders": {
"secret_id": {
"content": "$1",
@@ -8508,7 +8517,7 @@
"message": "无法完成登录"
},
"loginOnTrustedDeviceOrAskAdminToAssignPassword": {
- "message": "You need to log in on a trusted device or ask your administrator to assign you a password."
+ "message": "您需要在某个受信任的设备上登录,或要求管理员为您分配一个密码。"
},
"trustedDeviceEncryption": {
"message": "受信任设备加密"
@@ -9465,6 +9474,12 @@
"deviceManagementDesc": {
"message": "使用适合您的平台的实施指南为 Bitwarden 配置设备管理。"
},
+ "crowdstrikeEventIntegrationDesc": {
+ "message": "将事件数据发送到您的 Logscale 实例"
+ },
+ "failedToSaveIntegration": {
+ "message": "Failed to save integration. Please try again later."
+ },
"deviceIdMissing": {
"message": "缺少设备 ID"
},
@@ -9480,6 +9495,15 @@
"reopenLinkOnDesktop": {
"message": "请在桌面端从您的电子邮件中重新打开此链接。"
},
+ "connectIntegrationButtonDesc": {
+ "message": "连接 $INTEGRATION$",
+ "placeholders": {
+ "integration": {
+ "content": "$1",
+ "example": "Crowdstrike"
+ }
+ }
+ },
"integrationCardTooltip": {
"message": "打开 $INTEGRATION$ 实施指南。",
"placeholders": {
@@ -9537,6 +9561,15 @@
"createNewClientToManageAsProvider": {
"message": "创建一个新的客户组织作为提供商来管理。附加席位将反映在下一个计费周期中。"
},
+ "url": {
+ "message": "URL"
+ },
+ "bearerToken": {
+ "message": "Bearer Token"
+ },
+ "index": {
+ "message": "Index"
+ },
"selectAPlan": {
"message": "选择一个计划"
},
@@ -10285,8 +10318,8 @@
"organizationUserDeletedDesc": {
"message": "该用户已从组织中删除,所有关联的用户数据也已删除。"
},
- "deletedUserId": {
- "message": "删除了用户 $ID$ - 某个所有者/管理员删除了此用户账户",
+ "deletedUserIdEventMessage": {
+ "message": "删除了用户 $ID$",
"placeholders": {
"id": {
"content": "$1",
@@ -10396,8 +10429,8 @@
"domainStatusUnderVerification": {
"message": "验证中"
},
- "claimedDomainsDesc": {
- "message": "声明一个域名,以拥有电子邮箱地址与该域名匹配的所有成员账户。成员登录时将可以跳过 SSO 标识符。管理员也可以删除成员账户。"
+ "claimedDomainsDescription": {
+ "message": "声明域名以拥有成员账户。已声明域名的成员登录时将跳过 SSO 标识符页面,管理员也可以删除已声明的账户。"
},
"invalidDomainNameClaimMessage": {
"message": "输入的格式无效。格式:mydomain.com。子域名需要单独的条目进行声明。"
@@ -10785,28 +10818,28 @@
"message": "Get the Bitwarden browser extension and start autofilling today"
},
"getTheExtension": {
- "message": "Get the extension"
+ "message": "获取扩展"
},
"addItLater": {
- "message": "Add it later"
+ "message": "稍后添加"
},
"cannotAutofillPasswordsWithoutExtensionTitle": {
- "message": "You can't autofill passwords without the browser extension"
+ "message": "您无法在没有浏览器扩展的情况下自动填充密码"
},
"cannotAutofillPasswordsWithoutExtensionDesc": {
- "message": "Are you sure you don't want to add the extension now?"
+ "message": "您确定现在不添加该扩展吗?"
},
"skipToWebApp": {
- "message": "Skip to web app"
+ "message": "跳转到网页 App"
},
"bitwardenExtensionInstalled": {
- "message": "Bitwarden extension installed!"
+ "message": "Bitwarden 扩展已安装!"
},
"openExtensionToAutofill": {
- "message": "Open the extension to log in and start autofilling."
+ "message": "打开此扩展以登录并开始自动填充。"
},
"openBitwardenExtension": {
- "message": "Open Bitwarden extension"
+ "message": "打开 Bitwarden 扩展"
},
"gettingStartedWithBitwardenPart1": {
"message": "有关开始使用 Bitwarden 的提示,请访问",
@@ -10821,7 +10854,7 @@
"description": "This will be displayed as part of a larger sentence. The whole sentence reads: 'For tips on getting started with Bitwarden visit the Learning Center and Help Center'"
},
"setupExtensionContentAlt": {
- "message": "With the Bitwarden browser extension you can easily create new logins, access your saved logins directly from your browser toolbar, and sign in to accounts quickly using Bitwarden autofill."
+ "message": "使用 Bitwarden 浏览器扩展,您可以轻松地创建新的登录,直接从您的浏览器工具栏访问已保存的登录,以及使用 Bitwarden 自动填充功能快速登录账户。"
},
"restart": {
"message": "重启"
@@ -10850,28 +10883,28 @@
"description": "Error message shown when trying to add credit to a trialing organization without a billing address."
},
"billingAddress": {
- "message": "Billing address"
+ "message": "计费地址"
},
"addBillingAddress": {
- "message": "Add billing address"
+ "message": "添加计费地址"
},
"editBillingAddress": {
- "message": "Edit billing address"
+ "message": "编辑计费地址"
},
"noBillingAddress": {
- "message": "No address on file."
+ "message": "没有存档的地址。"
},
"billingAddressUpdated": {
- "message": "Your billing address has been updated."
+ "message": "您的计费地址已更新。"
},
"paymentDetails": {
- "message": "Payment details"
+ "message": "付款详细信息"
},
"paymentMethodUpdated": {
- "message": "Your payment method has been updated."
+ "message": "您的付款方式已更新。"
},
"bankAccountVerified": {
- "message": "Your bank account has been verified."
+ "message": "您的银行账户已验证。"
},
"availableCreditAppliedToInvoice": {
"message": "Any available credit will be automatically applied towards invoices generated for this account."
@@ -10889,12 +10922,63 @@
"message": "Payment with a bank account is only available to customers in the United States. You will be required to verify your bank account. We will make a micro-deposit within the next 1-2 business days. Enter the statement descriptor code from this deposit on the Payment Details page to verify the bank account. Failure to verify the bank account will result in a missed payment and your subscription being suspended."
},
"taxId": {
- "message": "Tax ID: $TAX_ID$",
+ "message": "税务 ID:$TAX_ID$",
"placeholders": {
"tax_id": {
"content": "$1",
"example": "12-3456789"
}
}
+ },
+ "unpaidInvoices": {
+ "message": "未付款账单"
+ },
+ "unpaidInvoicesForServiceUser": {
+ "message": "Your subscription has not been paid. Contact your provider administrator to restore service to you and your clients.",
+ "description": "A message shown in a non-dismissible dialog to service users of unpaid providers."
+ },
+ "providerSuspended": {
+ "message": "$PROVIDER$ is suspended",
+ "placeholders": {
+ "provider": {
+ "content": "$1",
+ "example": "Acme Industries"
+ }
+ }
+ },
+ "restoreProviderPortalAccessViaCustomerSupport": {
+ "message": "To restore access to your provider portal, contact Bitwarden Customer Support to renew your subscription.",
+ "description": "A message shown in a non-dismissible dialog to any user of a suspended providers."
+ },
+ "restoreProviderPortalAccessViaPaymentMethod": {
+ "message": "Your subscription has not been paid. To restore service to you and your clients, add a payment method by $CANCELLATION_DATE$.",
+ "placeholders": {
+ "cancellation_date": {
+ "content": "$1",
+ "example": "07/10/2025"
+ }
+ },
+ "description": "A message shown in a non-dismissible dialog to admins of unpaid providers."
+ },
+ "subscribetoEnterprise": {
+ "message": "Subscribe to $PLAN$",
+ "placeholders": {
+ "plan": {
+ "content": "$1",
+ "example": "Teams"
+ }
+ }
+ },
+ "subscribeEnterpriseSubtitle": {
+ "message": "Your 7-day $PLAN$ trial starts today. Add a payment method now to continue using these features after your trial ends: ",
+ "placeholders": {
+ "plan": {
+ "content": "$1",
+ "example": "Teams"
+ }
+ }
+ },
+ "unlimitedSecretsAndProjects": {
+ "message": "不限数量的机密和工程"
}
-}
+}
\ No newline at end of file
diff --git a/apps/web/src/locales/zh_TW/messages.json b/apps/web/src/locales/zh_TW/messages.json
index 4bba75e84ef..e74ac10fc47 100644
--- a/apps/web/src/locales/zh_TW/messages.json
+++ b/apps/web/src/locales/zh_TW/messages.json
@@ -568,6 +568,9 @@
"cancel": {
"message": "取消"
},
+ "later": {
+ "message": "Later"
+ },
"canceled": {
"message": "已取消"
},
@@ -626,6 +629,9 @@
"searchGroups": {
"message": "搜尋群組"
},
+ "resetSearch": {
+ "message": "Reset search"
+ },
"allItems": {
"message": "所有項目"
},
@@ -4626,6 +4632,9 @@
"receiveMarketingEmailsV2": {
"message": "Get advice, announcements, and research opportunities from Bitwarden in your inbox."
},
+ "subscribe": {
+ "message": "Subscribe"
+ },
"unsubscribe": {
"message": "取消訂閱"
},
@@ -9465,6 +9474,12 @@
"deviceManagementDesc": {
"message": "Configure device management for Bitwarden using the implementation guide for your platform."
},
+ "crowdstrikeEventIntegrationDesc": {
+ "message": "Send event data to your Logscale instance"
+ },
+ "failedToSaveIntegration": {
+ "message": "Failed to save integration. Please try again later."
+ },
"deviceIdMissing": {
"message": "Device ID is missing"
},
@@ -9480,6 +9495,15 @@
"reopenLinkOnDesktop": {
"message": "Reopen this link from your email on a desktop."
},
+ "connectIntegrationButtonDesc": {
+ "message": "Connect $INTEGRATION$",
+ "placeholders": {
+ "integration": {
+ "content": "$1",
+ "example": "Crowdstrike"
+ }
+ }
+ },
"integrationCardTooltip": {
"message": "Launch $INTEGRATION$ implementation guide.",
"placeholders": {
@@ -9537,6 +9561,15 @@
"createNewClientToManageAsProvider": {
"message": "Create a new client organization to manage as a Provider. Additional seats will be reflected in the next billing cycle."
},
+ "url": {
+ "message": "URL"
+ },
+ "bearerToken": {
+ "message": "Bearer Token"
+ },
+ "index": {
+ "message": "Index"
+ },
"selectAPlan": {
"message": "選擇一個計劃"
},
@@ -10285,8 +10318,8 @@
"organizationUserDeletedDesc": {
"message": "The user was removed from the organization and all associated user data has been deleted."
},
- "deletedUserId": {
- "message": "Deleted user $ID$ - an owner / admin deleted the user account",
+ "deletedUserIdEventMessage": {
+ "message": "Deleted user $ID$",
"placeholders": {
"id": {
"content": "$1",
@@ -10396,8 +10429,8 @@
"domainStatusUnderVerification": {
"message": "Under verification"
},
- "claimedDomainsDesc": {
- "message": "Claim a domain to own all member accounts whose email address matches the domain. Members will be able to skip the SSO identifier when logging in. Administrators will also be able to delete member accounts."
+ "claimedDomainsDescription": {
+ "message": "Claim a domain to own member accounts. The SSO identifier page will be skipped during login for members with claimed domains and administrators will be able to delete claimed accounts."
},
"invalidDomainNameClaimMessage": {
"message": "Input is not a valid format. Format: mydomain.com. Subdomains require separate entries to be claimed."
@@ -10896,5 +10929,56 @@
"example": "12-3456789"
}
}
+ },
+ "unpaidInvoices": {
+ "message": "Unpaid invoices"
+ },
+ "unpaidInvoicesForServiceUser": {
+ "message": "Your subscription has not been paid. Contact your provider administrator to restore service to you and your clients.",
+ "description": "A message shown in a non-dismissible dialog to service users of unpaid providers."
+ },
+ "providerSuspended": {
+ "message": "$PROVIDER$ is suspended",
+ "placeholders": {
+ "provider": {
+ "content": "$1",
+ "example": "Acme Industries"
+ }
+ }
+ },
+ "restoreProviderPortalAccessViaCustomerSupport": {
+ "message": "To restore access to your provider portal, contact Bitwarden Customer Support to renew your subscription.",
+ "description": "A message shown in a non-dismissible dialog to any user of a suspended providers."
+ },
+ "restoreProviderPortalAccessViaPaymentMethod": {
+ "message": "Your subscription has not been paid. To restore service to you and your clients, add a payment method by $CANCELLATION_DATE$.",
+ "placeholders": {
+ "cancellation_date": {
+ "content": "$1",
+ "example": "07/10/2025"
+ }
+ },
+ "description": "A message shown in a non-dismissible dialog to admins of unpaid providers."
+ },
+ "subscribetoEnterprise": {
+ "message": "Subscribe to $PLAN$",
+ "placeholders": {
+ "plan": {
+ "content": "$1",
+ "example": "Teams"
+ }
+ }
+ },
+ "subscribeEnterpriseSubtitle": {
+ "message": "Your 7-day $PLAN$ trial starts today. Add a payment method now to continue using these features after your trial ends: ",
+ "placeholders": {
+ "plan": {
+ "content": "$1",
+ "example": "Teams"
+ }
+ }
+ },
+ "unlimitedSecretsAndProjects": {
+ "message": "Unlimited secrets and projects"
}
-}
+}
\ No newline at end of file
diff --git a/apps/web/src/scss/variables.scss b/apps/web/src/scss/variables.scss
index 66773999c54..6c5278b2f9a 100644
--- a/apps/web/src/scss/variables.scss
+++ b/apps/web/src/scss/variables.scss
@@ -18,7 +18,7 @@ $theme-colors: (
);
$body-bg: $white;
-$body-color: #333333;
+$body-color: #1b2029;
$font-family-sans-serif:
Roboto, "Helvetica Neue", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji",
@@ -201,7 +201,7 @@ $themes: (
textColor: $body-color,
textDangerColor: $white,
textInfoColor: $white,
- textHeadingColor: #333333,
+ textHeadingColor: $body-color,
textMuted: #6c757d,
textSuccessColor: $white,
textWarningColor: $white,
diff --git a/apps/web/src/theme.ts b/apps/web/src/theme.ts
index 870e30c80e0..04b50de82e2 100644
--- a/apps/web/src/theme.ts
+++ b/apps/web/src/theme.ts
@@ -1,25 +1,22 @@
-// FIXME: Update this file to be type safe and remove this and next line
-// @ts-strict-ignore
-// Set theme on page load
-// This is done outside the Angular app to avoid a flash of unthemed content before it loads
+/**
+ * Set theme on page load based on (in order of priority):
+ * 1. the user's preferred theme from the theme state provider
+ * 2. the user's system theme
+ * 3. default to light theme (theoretically should never happen, but we need some kind of default)
+ *
+ * This is done outside the Angular app to avoid a flash of unthemed content before it loads.
+ */
const setTheme = () => {
- const getLegacyTheme = (): string | null => {
- // MANUAL-STATE-ACCESS: Calling global to get setting before migration
- // has had a chance to run, can be remove in the future.
- // Tracking Issue: https://bitwarden.atlassian.net/browse/PM-6676
- const globalState = window.localStorage.getItem("global");
-
- const parsedGlobalState = JSON.parse(globalState) as { theme?: string } | null;
- return parsedGlobalState ? parsedGlobalState.theme : null;
- };
-
+ /**
+ * If we cannot find a system preference or any other indication of what theme to apply,
+ * then we will default to light. `theme_light` is hardcoded as the default in the web app's
+ * index.html file.
+ */
const defaultTheme = "light";
const htmlEl = document.documentElement;
let theme = defaultTheme;
- // First try the new state providers location, then the legacy location
- const themeFromState =
- window.localStorage.getItem("global_theming_selection") ?? getLegacyTheme();
+ const themeFromState = window.localStorage.getItem("global_theming_selection");
if (themeFromState) {
if (themeFromState.indexOf("system") > -1) {
@@ -27,10 +24,11 @@ const setTheme = () => {
} else if (themeFromState.indexOf("dark") > -1) {
theme = "dark";
}
+ } else {
+ theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
}
if (!htmlEl.classList.contains("theme_" + theme)) {
- // The defaultTheme is also set in the html itself to make sure that some theming is always applied
htmlEl.classList.remove("theme_" + defaultTheme);
htmlEl.classList.add("theme_" + theme);
}
diff --git a/apps/web/tsconfig.build.json b/apps/web/tsconfig.build.json
index 39ab37efbb8..273cddb21d2 100644
--- a/apps/web/tsconfig.build.json
+++ b/apps/web/tsconfig.build.json
@@ -1,8 +1,5 @@
{
"extends": "./tsconfig.json",
"files": ["src/polyfills.ts", "src/main.ts", "src/theme.ts"],
- "include": [
- "src/connectors/*.ts",
- "../../libs/common/src/key-management/crypto/services/encrypt.worker.ts"
- ]
+ "include": ["src/connectors/*.ts"]
}
diff --git a/apps/web/tsconfig.json b/apps/web/tsconfig.json
index 92cec0d6a2c..fd655b0a56b 100644
--- a/apps/web/tsconfig.json
+++ b/apps/web/tsconfig.json
@@ -4,10 +4,5 @@
"strictTemplates": true
},
"files": ["src/polyfills.ts", "src/main.ts", "src/theme.ts"],
- "include": [
- "src/connectors/*.ts",
- "src/**/*.stories.ts",
- "src/**/*.spec.ts",
- "../../libs/common/src/key-management/crypto/services/encrypt.worker.ts"
- ]
+ "include": ["src/connectors/*.ts", "src/**/*.stories.ts", "src/**/*.spec.ts"]
}
diff --git a/bitwarden_license/bit-common/src/admin-console/auth-requests/organization-auth-request.service.ts b/bitwarden_license/bit-common/src/admin-console/auth-requests/organization-auth-request.service.ts
index 941e56e7891..f7b87d8c1a5 100644
--- a/bitwarden_license/bit-common/src/admin-console/auth-requests/organization-auth-request.service.ts
+++ b/bitwarden_license/bit-common/src/admin-console/auth-requests/organization-auth-request.service.ts
@@ -123,7 +123,7 @@ export class OrganizationAuthRequestService {
// Decrypt Organization's encrypted Private Key with org key
const orgSymKey = await this.keyService.getOrgKey(organizationId);
- const decOrgPrivateKey = await this.encryptService.decryptToBytes(
+ const decOrgPrivateKey = await this.encryptService.decryptBytes(
new EncString(encryptedOrgPrivateKey),
orgSymKey,
);
diff --git a/bitwarden_license/bit-common/src/dirt/integrations/index.ts b/bitwarden_license/bit-common/src/dirt/integrations/index.ts
new file mode 100644
index 00000000000..d2c1d173e3c
--- /dev/null
+++ b/bitwarden_license/bit-common/src/dirt/integrations/index.ts
@@ -0,0 +1,6 @@
+export * from "./services";
+export * from "./models/organization-integration-type";
+export * from "./models/organization-integration-request";
+export * from "./models/organization-integration-response";
+export * from "./models/organization-integration-configuration-request";
+export * from "./models/organization-integration-configuration-response";
diff --git a/bitwarden_license/bit-common/src/dirt/integrations/models/organization-integration-configuration-request.ts b/bitwarden_license/bit-common/src/dirt/integrations/models/organization-integration-configuration-request.ts
new file mode 100644
index 00000000000..58c59304479
--- /dev/null
+++ b/bitwarden_license/bit-common/src/dirt/integrations/models/organization-integration-configuration-request.ts
@@ -0,0 +1,20 @@
+import { EventType } from "@bitwarden/common/enums";
+
+export class OrganizationIntegrationConfigurationRequest {
+ eventType?: EventType | null = null;
+ configuration?: string | null = null;
+ filters?: string | null = null;
+ template?: string | null = null;
+
+ constructor(
+ eventType?: EventType | null,
+ configuration?: string | null,
+ filters?: string | null,
+ template?: string | null,
+ ) {
+ this.eventType = eventType;
+ this.configuration = configuration;
+ this.filters = filters;
+ this.template = template;
+ }
+}
diff --git a/bitwarden_license/bit-common/src/dirt/integrations/models/organization-integration-configuration-response.ts b/bitwarden_license/bit-common/src/dirt/integrations/models/organization-integration-configuration-response.ts
new file mode 100644
index 00000000000..47baf3276ad
--- /dev/null
+++ b/bitwarden_license/bit-common/src/dirt/integrations/models/organization-integration-configuration-response.ts
@@ -0,0 +1,20 @@
+import { EventType } from "@bitwarden/common/enums";
+import { BaseResponse } from "@bitwarden/common/models/response/base.response";
+import { OrganizationIntegrationConfigurationId } from "@bitwarden/common/types/guid";
+
+export class OrganizationIntegrationConfigurationResponse extends BaseResponse {
+ id: OrganizationIntegrationConfigurationId;
+ eventType?: EventType;
+ configuration?: string;
+ filters?: string;
+ template?: string;
+
+ constructor(response: any) {
+ super(response);
+ this.id = this.getResponseProperty("Id");
+ this.eventType = this.getResponseProperty("EventType");
+ this.configuration = this.getResponseProperty("Configuration");
+ this.filters = this.getResponseProperty("Filters");
+ this.template = this.getResponseProperty("Template");
+ }
+}
diff --git a/bitwarden_license/bit-common/src/dirt/integrations/models/organization-integration-request.ts b/bitwarden_license/bit-common/src/dirt/integrations/models/organization-integration-request.ts
new file mode 100644
index 00000000000..95f7d180dae
--- /dev/null
+++ b/bitwarden_license/bit-common/src/dirt/integrations/models/organization-integration-request.ts
@@ -0,0 +1,11 @@
+import { OrganizationIntegrationType } from "./organization-integration-type";
+
+export class OrganizationIntegrationRequest {
+ type: OrganizationIntegrationType;
+ configuration?: string;
+
+ constructor(integrationType: OrganizationIntegrationType, configuration?: string) {
+ this.type = integrationType;
+ this.configuration = configuration;
+ }
+}
diff --git a/bitwarden_license/bit-common/src/dirt/integrations/models/organization-integration-response.ts b/bitwarden_license/bit-common/src/dirt/integrations/models/organization-integration-response.ts
new file mode 100644
index 00000000000..d4a836df4c3
--- /dev/null
+++ b/bitwarden_license/bit-common/src/dirt/integrations/models/organization-integration-response.ts
@@ -0,0 +1,17 @@
+import { BaseResponse } from "@bitwarden/common/models/response/base.response";
+import { OrganizationIntegrationId } from "@bitwarden/common/types/guid";
+
+import { OrganizationIntegrationType } from "./organization-integration-type";
+
+export class OrganizationIntegrationResponse extends BaseResponse {
+ id: OrganizationIntegrationId;
+ type: OrganizationIntegrationType;
+ configuration: string;
+
+ constructor(response: any) {
+ super(response);
+ this.id = this.getResponseProperty("Id");
+ this.type = this.getResponseProperty("Type");
+ this.configuration = this.getResponseProperty("Configuration");
+ }
+}
diff --git a/bitwarden_license/bit-common/src/dirt/integrations/models/organization-integration-service-type.ts b/bitwarden_license/bit-common/src/dirt/integrations/models/organization-integration-service-type.ts
new file mode 100644
index 00000000000..dd1b4fb3f6c
--- /dev/null
+++ b/bitwarden_license/bit-common/src/dirt/integrations/models/organization-integration-service-type.ts
@@ -0,0 +1,6 @@
+export const OrganizationIntegrationServiceType = Object.freeze({
+ CrowdStrike: "CrowdStrike",
+} as const);
+
+export type OrganizationIntegrationServiceType =
+ (typeof OrganizationIntegrationServiceType)[keyof typeof OrganizationIntegrationServiceType];
diff --git a/bitwarden_license/bit-common/src/dirt/integrations/models/organization-integration-type.ts b/bitwarden_license/bit-common/src/dirt/integrations/models/organization-integration-type.ts
new file mode 100644
index 00000000000..1c98e174836
--- /dev/null
+++ b/bitwarden_license/bit-common/src/dirt/integrations/models/organization-integration-type.ts
@@ -0,0 +1,10 @@
+export const OrganizationIntegrationType = Object.freeze({
+ CloudBillingSync: 1,
+ Scim: 2,
+ Slack: 3,
+ Webhook: 4,
+ Hec: 5,
+} as const);
+
+export type OrganizationIntegrationType =
+ (typeof OrganizationIntegrationType)[keyof typeof OrganizationIntegrationType];
diff --git a/bitwarden_license/bit-common/src/dirt/integrations/services/index.ts b/bitwarden_license/bit-common/src/dirt/integrations/services/index.ts
new file mode 100644
index 00000000000..68a673854ae
--- /dev/null
+++ b/bitwarden_license/bit-common/src/dirt/integrations/services/index.ts
@@ -0,0 +1,2 @@
+export * from "./organization-integration-api.service";
+export * from "./organization-integration-configuration-api.service";
diff --git a/bitwarden_license/bit-common/src/dirt/integrations/services/organization-integration-api.service.spec.ts b/bitwarden_license/bit-common/src/dirt/integrations/services/organization-integration-api.service.spec.ts
new file mode 100644
index 00000000000..10ea87486b4
--- /dev/null
+++ b/bitwarden_license/bit-common/src/dirt/integrations/services/organization-integration-api.service.spec.ts
@@ -0,0 +1,111 @@
+import { mock } from "jest-mock-extended";
+
+import { ApiService } from "@bitwarden/common/abstractions/api.service";
+import { OrganizationId, OrganizationIntegrationId } from "@bitwarden/common/types/guid";
+
+import { OrganizationIntegrationRequest } from "../models/organization-integration-request";
+import { OrganizationIntegrationServiceType } from "../models/organization-integration-service-type";
+import { OrganizationIntegrationType } from "../models/organization-integration-type";
+
+import { OrganizationIntegrationApiService } from "./organization-integration-api.service";
+
+export const mockIntegrationResponse: any = {
+ id: "1" as OrganizationIntegrationId,
+ type: OrganizationIntegrationType.Hec,
+};
+
+export const mockIntegrationResponses: any[] = [
+ {
+ id: "1" as OrganizationIntegrationId,
+ type: OrganizationIntegrationType.Hec,
+ },
+ {
+ id: "2" as OrganizationIntegrationId,
+ type: OrganizationIntegrationType.Webhook,
+ },
+];
+
+describe("OrganizationIntegrationApiService", () => {
+ let service: OrganizationIntegrationApiService;
+ const apiService = mock();
+
+ beforeEach(() => {
+ service = new OrganizationIntegrationApiService(apiService);
+ });
+
+ it("should be created", () => {
+ expect(service).toBeTruthy();
+ });
+
+ it("should call apiService.send with correct parameters for getOrganizationIntegrations", async () => {
+ const orgId = "org1" as OrganizationId;
+
+ apiService.send.mockReturnValue(Promise.resolve(mockIntegrationResponses));
+
+ const result = await service.getOrganizationIntegrations(orgId);
+ expect(result).toEqual(mockIntegrationResponses);
+ expect(apiService.send).toHaveBeenCalledWith(
+ "GET",
+ `/organizations/${orgId}/integrations`,
+ null,
+ true,
+ true,
+ );
+ });
+
+ it("should call apiService.send with correct parameters for createOrganizationIntegration", async () => {
+ const request = new OrganizationIntegrationRequest(
+ OrganizationIntegrationType.Hec,
+ `{ 'uri:' 'test.com', 'scheme:' 'bearer', 'token:' '123456789', 'service:' '${OrganizationIntegrationServiceType.CrowdStrike}' }`,
+ );
+ const orgId = "org1" as OrganizationId;
+
+ apiService.send.mockReturnValue(Promise.resolve(mockIntegrationResponse));
+
+ const result = await service.createOrganizationIntegration(orgId, request);
+ expect(result.type).toEqual(mockIntegrationResponse.type);
+ expect(apiService.send).toHaveBeenCalledWith(
+ "POST",
+ `/organizations/${orgId.toString()}/integrations`,
+ request,
+ true,
+ true,
+ );
+ });
+
+ it("should call apiService.send with the correct parameters for updateOrganizationIntegration", async () => {
+ const request = new OrganizationIntegrationRequest(
+ OrganizationIntegrationType.Hec,
+ `{ 'uri:' 'test.com', 'scheme:' 'bearer', 'token:' '123456789', 'service:' '${OrganizationIntegrationServiceType.CrowdStrike}' }`,
+ );
+ const orgId = "org1" as OrganizationId;
+ const integrationId = "integration1" as OrganizationIntegrationId;
+
+ apiService.send.mockReturnValue(Promise.resolve(mockIntegrationResponse));
+
+ const result = await service.updateOrganizationIntegration(orgId, integrationId, request);
+ expect(result.type).toEqual(mockIntegrationResponse.type);
+ expect(apiService.send).toHaveBeenCalledWith(
+ "PUT",
+ `/organizations/${orgId}/integrations/${integrationId}`,
+ request,
+ true,
+ true,
+ );
+ });
+
+ it("should call apiService.send with the correct parameters for deleteOrganizationIntegration", async () => {
+ const orgId = "org1" as OrganizationId;
+ const integrationId = "integration1" as OrganizationIntegrationId;
+
+ await service.deleteOrganizationIntegration(orgId, integrationId);
+
+ expect(apiService.send).toHaveBeenCalledWith(
+ "DELETE",
+ `/organizations/${orgId}/integrations/${integrationId}`,
+ null,
+ true,
+ false,
+ );
+ });
+});
diff --git a/bitwarden_license/bit-common/src/dirt/integrations/services/organization-integration-api.service.ts b/bitwarden_license/bit-common/src/dirt/integrations/services/organization-integration-api.service.ts
new file mode 100644
index 00000000000..2c2266940e0
--- /dev/null
+++ b/bitwarden_license/bit-common/src/dirt/integrations/services/organization-integration-api.service.ts
@@ -0,0 +1,67 @@
+import { Injectable } from "@angular/core";
+
+import { ApiService } from "@bitwarden/common/abstractions/api.service";
+import { OrganizationId, OrganizationIntegrationId } from "@bitwarden/common/types/guid";
+
+import { OrganizationIntegrationRequest } from "../models/organization-integration-request";
+import { OrganizationIntegrationResponse } from "../models/organization-integration-response";
+
+@Injectable()
+export class OrganizationIntegrationApiService {
+ constructor(private apiService: ApiService) {}
+
+ async getOrganizationIntegrations(
+ orgId: OrganizationId,
+ ): Promise {
+ const response = await this.apiService.send(
+ "GET",
+ `/organizations/${orgId}/integrations`,
+ null,
+ true,
+ true,
+ );
+ return response;
+ }
+
+ async createOrganizationIntegration(
+ orgId: OrganizationId,
+ request: OrganizationIntegrationRequest,
+ ): Promise {
+ const response = await this.apiService.send(
+ "POST",
+ `/organizations/${orgId}/integrations`,
+ request,
+ true,
+ true,
+ );
+ return response;
+ }
+
+ async updateOrganizationIntegration(
+ orgId: OrganizationId,
+ integrationId: OrganizationIntegrationId,
+ request: OrganizationIntegrationRequest,
+ ): Promise {
+ const response = await this.apiService.send(
+ "PUT",
+ `/organizations/${orgId}/integrations/${integrationId}`,
+ request,
+ true,
+ true,
+ );
+ return response;
+ }
+
+ async deleteOrganizationIntegration(
+ orgId: OrganizationId,
+ integrationId: OrganizationIntegrationId,
+ ): Promise {
+ await this.apiService.send(
+ "DELETE",
+ `/organizations/${orgId}/integrations/${integrationId}`,
+ null,
+ true,
+ false,
+ );
+ }
+}
diff --git a/bitwarden_license/bit-common/src/dirt/integrations/services/organization-integration-configuration-api.service.spec.ts b/bitwarden_license/bit-common/src/dirt/integrations/services/organization-integration-configuration-api.service.spec.ts
new file mode 100644
index 00000000000..48612efdd13
--- /dev/null
+++ b/bitwarden_license/bit-common/src/dirt/integrations/services/organization-integration-configuration-api.service.spec.ts
@@ -0,0 +1,132 @@
+import { mock } from "jest-mock-extended";
+
+import { ApiService } from "@bitwarden/common/abstractions/api.service";
+import {
+ OrganizationId,
+ OrganizationIntegrationId,
+ OrganizationIntegrationConfigurationId,
+} from "@bitwarden/common/types/guid";
+
+import { OrganizationIntegrationConfigurationRequest } from "../models/organization-integration-configuration-request";
+
+import { OrganizationIntegrationConfigurationApiService } from "./organization-integration-configuration-api.service";
+
+export const mockConfigurationResponse: any = {
+ id: "1" as OrganizationIntegrationConfigurationId,
+ template: "{ 'event': '#EventMessage#', 'source': 'Bitwarden', 'index': 'testIndex' }",
+};
+
+export const mockConfigurationResponses: any[] = [
+ {
+ id: "1" as OrganizationIntegrationConfigurationId,
+ template: "{ 'event': '#EventMessage#', 'source': 'Bitwarden', 'index': 'testIndex' }",
+ },
+ {
+ id: "2" as OrganizationIntegrationConfigurationId,
+ template: "{ 'event': '#EventMessage#', 'source': 'Bitwarden', 'index': 'otherIndex' }",
+ },
+];
+
+describe("OrganizationIntegrationConfigurationApiService", () => {
+ let service: OrganizationIntegrationConfigurationApiService;
+ const apiService = mock();
+
+ beforeEach(() => {
+ service = new OrganizationIntegrationConfigurationApiService(apiService);
+ });
+
+ it("should be created", () => {
+ expect(service).toBeTruthy();
+ });
+
+ it("should call apiService.send with correct parameters for getOrganizationIntegrationConfigurations", async () => {
+ const orgId = "org1" as OrganizationId;
+ const integrationId = "integration1" as OrganizationIntegrationId;
+
+ apiService.send.mockReturnValue(Promise.resolve(mockConfigurationResponses));
+
+ const result = await service.getOrganizationIntegrationConfigurations(orgId, integrationId);
+ expect(result).toEqual(mockConfigurationResponses);
+ expect(apiService.send).toHaveBeenCalledWith(
+ "GET",
+ `organizations/${orgId}/integrations/${integrationId}/configurations`,
+ null,
+ true,
+ true,
+ );
+ });
+
+ it("should call apiService.send with correct parameters for createOrganizationIntegrationConfiguration", async () => {
+ const request = new OrganizationIntegrationConfigurationRequest(
+ null,
+ null,
+ null,
+ "{ 'event': '#EventMessage#', 'source': 'Bitwarden', 'index': 'testIndex' }",
+ );
+ const orgId = "org1" as OrganizationId;
+ const integrationId = "integration1" as OrganizationIntegrationId;
+
+ apiService.send.mockReturnValue(Promise.resolve(mockConfigurationResponse));
+
+ const result = await service.createOrganizationIntegrationConfiguration(
+ orgId,
+ integrationId,
+ request,
+ );
+ expect(result.eventType).toEqual(mockConfigurationResponse.eventType);
+ expect(result.template).toEqual(mockConfigurationResponse.template);
+ expect(apiService.send).toHaveBeenCalledWith(
+ "POST",
+ `organizations/${orgId}/integrations/${integrationId}/configurations`,
+ request,
+ true,
+ true,
+ );
+ });
+
+ it("should call apiService.send with correct parameters for updateOrganizationIntegrationConfiguration", async () => {
+ const request = new OrganizationIntegrationConfigurationRequest(
+ null,
+ null,
+ null,
+ "{ 'event': '#EventMessage#', 'source': 'Bitwarden', 'index': 'testIndex' }",
+ );
+ const orgId = "org1" as OrganizationId;
+ const integrationId = "integration1" as OrganizationIntegrationId;
+ const configurationId = "configurationId" as OrganizationIntegrationConfigurationId;
+
+ apiService.send.mockReturnValue(Promise.resolve(mockConfigurationResponse));
+
+ const result = await service.updateOrganizationIntegrationConfiguration(
+ orgId,
+ integrationId,
+ configurationId,
+ request,
+ );
+ expect(result.eventType).toEqual(mockConfigurationResponse.eventType);
+ expect(result.template).toEqual(mockConfigurationResponse.template);
+ expect(apiService.send).toHaveBeenCalledWith(
+ "PUT",
+ `organizations/${orgId}/integrations/${integrationId}/configurations/${configurationId}`,
+ request,
+ true,
+ true,
+ );
+ });
+
+ it("should call apiService.send with correct parameters for deleteOrganizationIntegrationConfiguration", async () => {
+ const orgId = "org1" as OrganizationId;
+ const integrationId = "integration1" as OrganizationIntegrationId;
+ const configurationId = "configurationId" as OrganizationIntegrationConfigurationId;
+
+ await service.deleteOrganizationIntegrationConfiguration(orgId, integrationId, configurationId);
+
+ expect(apiService.send).toHaveBeenCalledWith(
+ "DELETE",
+ `organizations/${orgId}/integrations/${integrationId}/configurations/${configurationId}`,
+ null,
+ true,
+ false,
+ );
+ });
+});
diff --git a/bitwarden_license/bit-common/src/dirt/integrations/services/organization-integration-configuration-api.service.ts b/bitwarden_license/bit-common/src/dirt/integrations/services/organization-integration-configuration-api.service.ts
new file mode 100644
index 00000000000..b5bac73b280
--- /dev/null
+++ b/bitwarden_license/bit-common/src/dirt/integrations/services/organization-integration-configuration-api.service.ts
@@ -0,0 +1,75 @@
+import { Injectable } from "@angular/core";
+
+import { ApiService } from "@bitwarden/common/abstractions/api.service";
+import {
+ OrganizationId,
+ OrganizationIntegrationConfigurationId,
+ OrganizationIntegrationId,
+} from "@bitwarden/common/types/guid";
+
+import { OrganizationIntegrationConfigurationRequest } from "../models/organization-integration-configuration-request";
+import { OrganizationIntegrationConfigurationResponse } from "../models/organization-integration-configuration-response";
+
+@Injectable()
+export class OrganizationIntegrationConfigurationApiService {
+ constructor(private apiService: ApiService) {}
+
+ async getOrganizationIntegrationConfigurations(
+ orgId: OrganizationId,
+ integrationId: OrganizationIntegrationId,
+ ): Promise {
+ const responses = await this.apiService.send(
+ "GET",
+ `organizations/${orgId}/integrations/${integrationId}/configurations`,
+ null,
+ true,
+ true,
+ );
+ return responses;
+ }
+
+ async createOrganizationIntegrationConfiguration(
+ orgId: OrganizationId,
+ integrationId: OrganizationIntegrationId,
+ request: OrganizationIntegrationConfigurationRequest,
+ ): Promise {
+ const response = await this.apiService.send(
+ "POST",
+ `organizations/${orgId}/integrations/${integrationId}/configurations`,
+ request,
+ true,
+ true,
+ );
+ return response;
+ }
+
+ async updateOrganizationIntegrationConfiguration(
+ orgId: OrganizationId,
+ integrationId: OrganizationIntegrationId,
+ configurationId: OrganizationIntegrationConfigurationId,
+ request: OrganizationIntegrationConfigurationRequest,
+ ): Promise {
+ const response = await this.apiService.send(
+ "PUT",
+ `organizations/${orgId}/integrations/${integrationId}/configurations/${configurationId}`,
+ request,
+ true,
+ true,
+ );
+ return response;
+ }
+
+ async deleteOrganizationIntegrationConfiguration(
+ orgId: OrganizationId,
+ integrationId: OrganizationIntegrationId,
+ configurationId: OrganizationIntegrationConfigurationId,
+ ): Promise {
+ await this.apiService.send(
+ "DELETE",
+ `organizations/${orgId}/integrations/${integrationId}/configurations/${configurationId}`,
+ null,
+ true,
+ false,
+ );
+ }
+}
diff --git a/bitwarden_license/bit-web/src/app/admin-console/policies/activate-autofill.component.ts b/bitwarden_license/bit-web/src/app/admin-console/policies/activate-autofill.component.ts
index 61e2133d059..821509b43e2 100644
--- a/bitwarden_license/bit-web/src/app/admin-console/policies/activate-autofill.component.ts
+++ b/bitwarden_license/bit-web/src/app/admin-console/policies/activate-autofill.component.ts
@@ -1,7 +1,9 @@
import { Component } from "@angular/core";
+import { of } from "rxjs";
import { PolicyType } from "@bitwarden/common/admin-console/enums";
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
+import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
import {
BasePolicy,
BasePolicyComponent,
@@ -13,8 +15,8 @@ export class ActivateAutofillPolicy extends BasePolicy {
type = PolicyType.ActivateAutofill;
component = ActivateAutofillPolicyComponent;
- display(organization: Organization) {
- return organization.useActivateAutofillPolicy;
+ display(organization: Organization, configService: ConfigService) {
+ return of(organization.useActivateAutofillPolicy);
}
}
diff --git a/bitwarden_license/bit-web/src/app/admin-console/providers/clients/clients.component.html b/bitwarden_license/bit-web/src/app/admin-console/providers/clients/clients.component.html
index de1db6f0c7a..8f740384305 100644
--- a/bitwarden_license/bit-web/src/app/admin-console/providers/clients/clients.component.html
+++ b/bitwarden_license/bit-web/src/app/admin-console/providers/clients/clients.component.html
@@ -1,10 +1,11 @@
+@let isAdminOrServiceUser = isAdminOrServiceUser$ | async;
-
+
{{ "newClient" | i18n }}
@@ -12,7 +13,7 @@
type="button"
bitButton
(click)="addExistingOrganization()"
- *ngIf="manageOrganizations && showAddExisting"
+ *ngIf="isAdminOrServiceUser && showAddExisting"
>
{{ "addExistingOrganization" | i18n }}
@@ -52,7 +53,7 @@
{{ row.plan }}
=
new TableDataSource();
protected searchControl = new FormControl("", { nonNullable: true });
+ protected providerId$: Observable =
+ this.activatedRoute.parent?.params.pipe(map((params) => params.providerId as string)) ??
+ new Observable();
+
+ protected provider$ = this.providerId$.pipe(
+ switchMap((providerId) => this.providerService.get$(providerId)),
+ );
+
+ protected isAdminOrServiceUser$ = this.provider$.pipe(
+ map(
+ (provider) =>
+ provider?.type === ProviderUserType.ProviderAdmin ||
+ provider?.type === ProviderUserType.ServiceUser,
+ ),
+ );
+
constructor(
private router: Router,
private providerService: ProviderService,
@@ -81,24 +95,17 @@ export class ClientsComponent {
this.searchControl.setValue(queryParams.search);
});
- this.activatedRoute.parent?.params
- ?.pipe(
- switchMap((params) => {
- this.providerId = params.providerId;
- return this.providerService.get$(this.providerId).pipe(
- map((provider) => provider?.providerStatus === ProviderStatusType.Billable),
- map((isBillable) => {
- if (isBillable) {
- return from(
- this.router.navigate(["../manage-client-organizations"], {
- relativeTo: this.activatedRoute,
- }),
- );
- } else {
- return from(this.load());
- }
- }),
- );
+ this.provider$
+ .pipe(
+ map((provider) => {
+ if (provider?.providerStatus === ProviderStatusType.Billable) {
+ return from(
+ this.router.navigate(["../manage-client-organizations"], {
+ relativeTo: this.activatedRoute,
+ }),
+ );
+ }
+ return from(this.load());
}),
takeUntilDestroyed(),
)
@@ -124,7 +131,8 @@ export class ClientsComponent {
}
try {
- await this.webProviderService.detachOrganization(this.providerId, organization.id);
+ const providerId = await firstValueFrom(this.providerId$);
+ await this.webProviderService.detachOrganization(providerId, organization.id);
this.toastService.showToast({
variant: "success",
title: "",
@@ -137,12 +145,11 @@ export class ClientsComponent {
}
async load() {
- const response = await this.apiService.getProviderClients(this.providerId);
+ const providerId = await firstValueFrom(this.providerId$);
+ const response = await this.apiService.getProviderClients(providerId);
const userId = await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId));
const clients = response.data != null && response.data.length > 0 ? response.data : [];
this.dataSource.data = clients;
- this.manageOrganizations =
- (await this.providerService.get(this.providerId)).type === ProviderUserType.ProviderAdmin;
const candidateOrgs = (
await firstValueFrom(this.organizationService.organizations$(userId))
).filter((o) => o.isOwner && o.providerId == null);
@@ -158,8 +165,9 @@ export class ClientsComponent {
}
async addExistingOrganization() {
+ const providerId = await firstValueFrom(this.providerId$);
const dialogRef = AddOrganizationComponent.open(this.dialogService, {
- providerId: this.providerId,
+ providerId: providerId,
organizations: this.addableOrganizations,
});
diff --git a/bitwarden_license/bit-web/src/app/admin-console/providers/manage/members.component.ts b/bitwarden_license/bit-web/src/app/admin-console/providers/manage/members.component.ts
index 9cbe8115008..69d02214717 100644
--- a/bitwarden_license/bit-web/src/app/admin-console/providers/manage/members.component.ts
+++ b/bitwarden_license/bit-web/src/app/admin-console/providers/manage/members.component.ts
@@ -52,8 +52,8 @@ export class MembersComponent extends BaseMembersComponent {
dataSource = new MembersTableDataSource();
loading = true;
providerId: string;
- rowHeight = 69;
- rowHeightClass = `tw-h-[69px]`;
+ rowHeight = 70;
+ rowHeightClass = `tw-h-[70px]`;
status: ProviderUserStatusType = null;
userStatusType = ProviderUserStatusType;
diff --git a/bitwarden_license/bit-web/src/app/admin-console/providers/providers-layout.component.html b/bitwarden_license/bit-web/src/app/admin-console/providers/providers-layout.component.html
index 0a084848dbe..b61b1ce7840 100644
--- a/bitwarden_license/bit-web/src/app/admin-console/providers/providers-layout.component.html
+++ b/bitwarden_license/bit-web/src/app/admin-console/providers/providers-layout.component.html
@@ -10,7 +10,15 @@
icon="bwi-provider"
[text]="clientsTranslationKey$ | async | i18n"
[route]="(isBillable | async) ? 'manage-client-organizations' : 'clients'"
- >
+ >
+
+
;
protected managePaymentDetailsOutsideCheckout$: Observable;
+ protected providerPortalTakeover$: Observable;
constructor(
private route: ActivatedRoute,
private providerService: ProviderService,
private configService: ConfigService,
+ private providerWarningsService: ProviderWarningsService,
) {}
ngOnInit() {
document.body.classList.remove("layout_frontend");
- this.provider$ = this.route.params.pipe(
- switchMap((params) => this.providerService.get$(params.providerId)),
+ const providerId$: Observable = this.route.params.pipe(
+ map((params) => params.providerId),
+ );
+
+ this.provider$ = providerId$.pipe(
+ switchMap((providerId) => this.providerService.get$(providerId)),
takeUntil(this.destroy$),
);
@@ -77,6 +89,19 @@ export class ProvidersLayoutComponent implements OnInit, OnDestroy {
this.managePaymentDetailsOutsideCheckout$ = this.configService.getFeatureFlag$(
FeatureFlag.PM21881_ManagePaymentDetailsOutsideCheckout,
);
+
+ providerId$
+ .pipe(
+ switchMap((providerId) =>
+ this.providerWarningsService.showProviderSuspendedDialog$(providerId),
+ ),
+ takeUntil(this.destroy$),
+ )
+ .subscribe();
+
+ this.providerPortalTakeover$ = this.configService.getFeatureFlag$(
+ FeatureFlag.PM21821_ProviderPortalTakeover,
+ );
}
ngOnDestroy() {
diff --git a/bitwarden_license/bit-web/src/app/billing/providers/clients/manage-clients.component.html b/bitwarden_license/bit-web/src/app/billing/providers/clients/manage-clients.component.html
index fa2e347e9c8..043ce65b961 100644
--- a/bitwarden_license/bit-web/src/app/billing/providers/clients/manage-clients.component.html
+++ b/bitwarden_license/bit-web/src/app/billing/providers/clients/manage-clients.component.html
@@ -1,3 +1,5 @@
+@let isSuspensionActive = suspensionActive$ | async;
+@let provider = provider$ | async;
{{ "add" | i18n }}
-
+
{{ newClientButtonLabel }}
-
+
{{ "existingOrganization" | i18n }}
@@ -73,26 +89,41 @@
appA11yTitle="{{ 'options' | i18n }}"
>
-
+
{{ "updateName" | i18n }}
-
+
{{ "manageSubscription" | i18n }}
-
-
- {{ "unlinkOrganization" | i18n }}
-
-
+ @if (provider?.type === ProviderUserType.ProviderAdmin) {
+
+
+ {{ "unlinkOrganization" | i18n }}
+
+
+ }
diff --git a/bitwarden_license/bit-web/src/app/billing/providers/clients/manage-clients.component.ts b/bitwarden_license/bit-web/src/app/billing/providers/clients/manage-clients.component.ts
index de9e63cd509..27f45cb250e 100644
--- a/bitwarden_license/bit-web/src/app/billing/providers/clients/manage-clients.component.ts
+++ b/bitwarden_license/bit-web/src/app/billing/providers/clients/manage-clients.component.ts
@@ -2,8 +2,16 @@ import { Component } from "@angular/core";
import { takeUntilDestroyed } from "@angular/core/rxjs-interop";
import { FormControl } from "@angular/forms";
import { ActivatedRoute, Router } from "@angular/router";
-import { firstValueFrom, from, lastValueFrom, map } from "rxjs";
-import { debounceTime, first, switchMap } from "rxjs/operators";
+import {
+ firstValueFrom,
+ from,
+ lastValueFrom,
+ map,
+ combineLatest,
+ switchMap,
+ Observable,
+} from "rxjs";
+import { debounceTime, first } from "rxjs/operators";
import { ProviderService } from "@bitwarden/common/admin-console/abstractions/provider.service";
import {
@@ -15,6 +23,8 @@ import { Provider } from "@bitwarden/common/admin-console/models/domain/provider
import { ProviderOrganizationOrganizationDetailsResponse } from "@bitwarden/common/admin-console/models/response/provider/provider-organization.response";
import { BillingApiServiceAbstraction } from "@bitwarden/common/billing/abstractions";
import { PlanResponse } from "@bitwarden/common/billing/models/response/plan.response";
+import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
+import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { ValidationService } from "@bitwarden/common/platform/abstractions/validation.service";
import {
@@ -61,20 +71,49 @@ import { ReplacePipe } from "./replace.pipe";
],
})
export class ManageClientsComponent {
- providerId: string = "";
- provider: Provider | undefined;
loading = true;
- isProviderAdmin = false;
dataSource: TableDataSource =
new TableDataSource();
protected searchControl = new FormControl("", { nonNullable: true });
protected plans: PlanResponse[] = [];
+ protected ProviderUserType = ProviderUserType;
pageTitle = this.i18nService.t("clients");
clientColumnHeader = this.i18nService.t("client");
newClientButtonLabel = this.i18nService.t("newClient");
+ protected providerId$: Observable =
+ this.activatedRoute.parent?.params.pipe(map((params) => params.providerId as string)) ??
+ new Observable();
+
+ protected provider$ = this.providerId$.pipe(
+ switchMap((providerId) => this.providerService.get$(providerId)),
+ );
+
+ protected isAdminOrServiceUser$ = this.provider$.pipe(
+ map(
+ (provider) =>
+ provider?.type === ProviderUserType.ProviderAdmin ||
+ provider?.type === ProviderUserType.ServiceUser,
+ ),
+ );
+
+ protected providerPortalTakeover$ = this.configService.getFeatureFlag$(
+ FeatureFlag.PM21821_ProviderPortalTakeover,
+ );
+
+ protected suspensionActive$ = combineLatest([
+ this.isAdminOrServiceUser$,
+ this.providerPortalTakeover$,
+ this.provider$.pipe(map((provider) => provider?.enabled ?? false)),
+ ]).pipe(
+ map(
+ ([isAdminOrServiceUser, portalTakeoverEnabled, providerEnabled]) =>
+ isAdminOrServiceUser && portalTakeoverEnabled && !providerEnabled,
+ ),
+ );
+
constructor(
private billingApiService: BillingApiServiceAbstraction,
private providerService: ProviderService,
@@ -86,29 +125,23 @@ export class ManageClientsComponent {
private validationService: ValidationService,
private webProviderService: WebProviderService,
private billingNotificationService: BillingNotificationService,
+ private configService: ConfigService,
) {
this.activatedRoute.queryParams.pipe(first(), takeUntilDestroyed()).subscribe((queryParams) => {
this.searchControl.setValue(queryParams.search);
});
- this.activatedRoute.parent?.params
- ?.pipe(
- switchMap((params) => {
- this.providerId = params.providerId;
- return this.providerService.get$(this.providerId).pipe(
- map((provider: Provider) => provider?.providerStatus === ProviderStatusType.Billable),
- map((isBillable) => {
- if (!isBillable) {
- return from(
- this.router.navigate(["../clients"], {
- relativeTo: this.activatedRoute,
- }),
- );
- } else {
- return from(this.load());
- }
- }),
- );
+ this.provider$
+ .pipe(
+ map((provider: Provider) => {
+ if (provider?.providerStatus !== ProviderStatusType.Billable) {
+ return from(
+ this.router.navigate(["../clients"], {
+ relativeTo: this.activatedRoute,
+ }),
+ );
+ }
+ return from(this.load());
}),
takeUntilDestroyed(),
)
@@ -124,15 +157,15 @@ export class ManageClientsComponent {
async load() {
try {
- this.provider = await firstValueFrom(this.providerService.get$(this.providerId));
- if (this.provider?.providerType === ProviderType.BusinessUnit) {
+ const providerId = await firstValueFrom(this.providerId$);
+ const provider = await firstValueFrom(this.providerService.get$(providerId));
+ if (provider?.providerType === ProviderType.BusinessUnit) {
this.pageTitle = this.i18nService.t("businessUnits");
this.clientColumnHeader = this.i18nService.t("businessUnit");
this.newClientButtonLabel = this.i18nService.t("newBusinessUnit");
}
- this.isProviderAdmin = this.provider?.type === ProviderUserType.ProviderAdmin;
this.dataSource.data = (
- await this.billingApiService.getProviderClientOrganizations(this.providerId)
+ await this.billingApiService.getProviderClientOrganizations(providerId)
).data;
this.plans = (await this.billingApiService.getPlans()).data;
this.loading = false;
@@ -142,10 +175,11 @@ export class ManageClientsComponent {
}
addExistingOrganization = async () => {
- if (this.provider) {
+ const provider = await firstValueFrom(this.provider$);
+ if (provider) {
const reference = AddExistingOrganizationDialogComponent.open(this.dialogService, {
data: {
- provider: this.provider,
+ provider: provider,
},
});
@@ -158,9 +192,10 @@ export class ManageClientsComponent {
};
createClient = async () => {
+ const providerId = await firstValueFrom(this.providerId$);
const reference = openCreateClientDialog(this.dialogService, {
data: {
- providerId: this.providerId,
+ providerId: providerId,
plans: this.plans,
},
});
@@ -173,9 +208,10 @@ export class ManageClientsComponent {
};
manageClientName = async (organization: ProviderOrganizationOrganizationDetailsResponse) => {
+ const providerId = await firstValueFrom(this.providerId$);
const dialogRef = openManageClientNameDialog(this.dialogService, {
data: {
- providerId: this.providerId,
+ providerId: providerId,
organization: {
id: organization.id,
name: organization.organizationName,
@@ -194,10 +230,11 @@ export class ManageClientsComponent {
manageClientSubscription = async (
organization: ProviderOrganizationOrganizationDetailsResponse,
) => {
+ const provider = await firstValueFrom(this.provider$);
const dialogRef = openManageClientSubscriptionDialog(this.dialogService, {
data: {
organization,
- provider: this.provider!,
+ provider: provider!,
},
});
@@ -220,7 +257,8 @@ export class ManageClientsComponent {
}
try {
- await this.webProviderService.detachOrganization(this.providerId, organization.id);
+ const providerId = await firstValueFrom(this.providerId$);
+ await this.webProviderService.detachOrganization(providerId, organization.id);
this.toastService.showToast({
variant: "success",
title: "",
diff --git a/bitwarden_license/bit-web/src/app/billing/providers/clients/no-clients.component.ts b/bitwarden_license/bit-web/src/app/billing/providers/clients/no-clients.component.ts
index 768f22c5738..039c42de302 100644
--- a/bitwarden_license/bit-web/src/app/billing/providers/clients/no-clients.component.ts
+++ b/bitwarden_license/bit-web/src/app/billing/providers/clients/no-clients.component.ts
@@ -30,6 +30,7 @@ const gearIcon = svgIcon`
{{ "noClients" | i18n }}
this.addNewOrganizationClicked.emit();
diff --git a/bitwarden_license/bit-web/src/app/billing/providers/services/provider-warnings.service.spec.ts b/bitwarden_license/bit-web/src/app/billing/providers/services/provider-warnings.service.spec.ts
new file mode 100644
index 00000000000..752df86ed7e
--- /dev/null
+++ b/bitwarden_license/bit-web/src/app/billing/providers/services/provider-warnings.service.spec.ts
@@ -0,0 +1,187 @@
+import { TestBed } from "@angular/core/testing";
+import { ActivatedRoute, Router } from "@angular/router";
+import { mock, MockProxy } from "jest-mock-extended";
+import { of } from "rxjs";
+
+import { ProviderService } from "@bitwarden/common/admin-console/abstractions/provider.service";
+import { ProviderUserType } from "@bitwarden/common/admin-console/enums";
+import { Provider } from "@bitwarden/common/admin-console/models/domain/provider";
+import { BillingApiServiceAbstraction } from "@bitwarden/common/billing/abstractions";
+import { ProviderSubscriptionResponse } from "@bitwarden/common/billing/models/response/provider-subscription-response";
+import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
+import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
+import { SyncService } from "@bitwarden/common/platform/sync";
+import { DialogRef, DialogService } from "@bitwarden/components";
+import {
+ RequirePaymentMethodDialogComponent,
+ SubmitPaymentMethodDialogResult,
+} from "@bitwarden/web-vault/app/billing/payment/components";
+
+import { ProviderWarningsService } from "./provider-warnings.service";
+
+describe("ProviderWarningsService", () => {
+ let service: ProviderWarningsService;
+ let configService: MockProxy;
+ let dialogService: MockProxy;
+ let providerService: MockProxy;
+ let billingApiService: MockProxy;
+ let i18nService: MockProxy;
+ let router: MockProxy;
+ let syncService: MockProxy;
+
+ beforeEach(() => {
+ billingApiService = mock();
+ configService = mock();
+ dialogService = mock();
+ i18nService = mock();
+ providerService = mock();
+ router = mock();
+ syncService = mock();
+
+ TestBed.configureTestingModule({
+ providers: [
+ ProviderWarningsService,
+ { provide: ActivatedRoute, useValue: {} },
+ { provide: BillingApiServiceAbstraction, useValue: billingApiService },
+ { provide: ConfigService, useValue: configService },
+ { provide: DialogService, useValue: dialogService },
+ { provide: I18nService, useValue: i18nService },
+ { provide: ProviderService, useValue: providerService },
+ { provide: Router, useValue: router },
+ { provide: SyncService, useValue: syncService },
+ ],
+ });
+
+ service = TestBed.inject(ProviderWarningsService);
+ });
+
+ it("should create the service", () => {
+ expect(service).toBeTruthy();
+ });
+
+ describe("showProviderSuspendedDialog$", () => {
+ const providerId = "test-provider-id";
+
+ it("should not show any dialog when the 'pm-21821-provider-portal-takeover' flag is disabled", (done) => {
+ const provider = { enabled: false } as Provider;
+ const subscription = { status: "unpaid" } as ProviderSubscriptionResponse;
+
+ providerService.get$.mockReturnValue(of(provider));
+ billingApiService.getProviderSubscription.mockResolvedValue(subscription);
+ configService.getFeatureFlag$.mockReturnValue(of(false));
+
+ const requirePaymentMethodDialogComponentOpenSpy = jest.spyOn(
+ RequirePaymentMethodDialogComponent,
+ "open",
+ );
+
+ service.showProviderSuspendedDialog$(providerId).subscribe(() => {
+ expect(dialogService.openSimpleDialog).not.toHaveBeenCalled();
+ expect(requirePaymentMethodDialogComponentOpenSpy).not.toHaveBeenCalled();
+ done();
+ });
+ });
+
+ it("should not show any dialog when the provider is enabled", (done) => {
+ const provider = { enabled: true } as Provider;
+ const subscription = { status: "unpaid" } as ProviderSubscriptionResponse;
+
+ providerService.get$.mockReturnValue(of(provider));
+ billingApiService.getProviderSubscription.mockResolvedValue(subscription);
+ configService.getFeatureFlag$.mockReturnValue(of(true));
+
+ const requirePaymentMethodDialogComponentOpenSpy = jest.spyOn(
+ RequirePaymentMethodDialogComponent,
+ "open",
+ );
+
+ service.showProviderSuspendedDialog$(providerId).subscribe(() => {
+ expect(dialogService.openSimpleDialog).not.toHaveBeenCalled();
+ expect(requirePaymentMethodDialogComponentOpenSpy).not.toHaveBeenCalled();
+ done();
+ });
+ });
+
+ it("should show the require payment method dialog for an admin of a provider with an unpaid subscription", (done) => {
+ const provider = {
+ enabled: false,
+ type: ProviderUserType.ProviderAdmin,
+ name: "Test Provider",
+ } as Provider;
+ const subscription = {
+ status: "unpaid",
+ cancelAt: "2024-12-31",
+ } as ProviderSubscriptionResponse;
+
+ providerService.get$.mockReturnValue(of(provider));
+ billingApiService.getProviderSubscription.mockResolvedValue(subscription);
+ configService.getFeatureFlag$.mockReturnValue(of(true));
+
+ const dialogRef = {
+ closed: of({ type: "success" }),
+ } as DialogRef;
+ jest.spyOn(RequirePaymentMethodDialogComponent, "open").mockReturnValue(dialogRef);
+
+ service.showProviderSuspendedDialog$(providerId).subscribe(() => {
+ expect(RequirePaymentMethodDialogComponent.open).toHaveBeenCalled();
+ expect(syncService.fullSync).toHaveBeenCalled();
+ expect(router.navigate).toHaveBeenCalled();
+ done();
+ });
+ });
+
+ it("should show the simple, unpaid invoices dialog for a service user of a provider with an unpaid subscription", (done) => {
+ const provider = {
+ enabled: false,
+ type: ProviderUserType.ServiceUser,
+ name: "Test Provider",
+ } as Provider;
+ const subscription = { status: "unpaid" } as ProviderSubscriptionResponse;
+
+ providerService.get$.mockReturnValue(of(provider));
+ billingApiService.getProviderSubscription.mockResolvedValue(subscription);
+ dialogService.openSimpleDialog.mockResolvedValue(true);
+ configService.getFeatureFlag$.mockReturnValue(of(true));
+
+ i18nService.t.mockImplementation((key: string) => key);
+
+ service.showProviderSuspendedDialog$(providerId).subscribe(() => {
+ expect(dialogService.openSimpleDialog).toHaveBeenCalledWith({
+ type: "danger",
+ title: "unpaidInvoices",
+ content: "unpaidInvoicesForServiceUser",
+ disableClose: true,
+ });
+ done();
+ });
+ });
+
+ it("should show the provider suspended dialog to all users of a provider that's suspended, but not unpaid", (done) => {
+ const provider = {
+ enabled: false,
+ name: "Test Provider",
+ } as Provider;
+ const subscription = { status: "active" } as ProviderSubscriptionResponse;
+
+ providerService.get$.mockReturnValue(of(provider));
+ billingApiService.getProviderSubscription.mockResolvedValue(subscription);
+ dialogService.openSimpleDialog.mockResolvedValue(true);
+ configService.getFeatureFlag$.mockReturnValue(of(true));
+
+ i18nService.t.mockImplementation((key: string) => key);
+
+ service.showProviderSuspendedDialog$(providerId).subscribe(() => {
+ expect(dialogService.openSimpleDialog).toHaveBeenCalledWith({
+ type: "danger",
+ title: "providerSuspended",
+ content: "restoreProviderPortalAccessViaCustomerSupport",
+ disableClose: true,
+ acceptButtonText: "contactSupportShort",
+ cancelButtonText: null,
+ acceptAction: expect.any(Function),
+ });
+ done();
+ });
+ });
+ });
+});
diff --git a/bitwarden_license/bit-web/src/app/billing/providers/services/provider-warnings.service.ts b/bitwarden_license/bit-web/src/app/billing/providers/services/provider-warnings.service.ts
new file mode 100644
index 00000000000..d888cc6b8d9
--- /dev/null
+++ b/bitwarden_license/bit-web/src/app/billing/providers/services/provider-warnings.service.ts
@@ -0,0 +1,104 @@
+import { Injectable } from "@angular/core";
+import { ActivatedRoute, Router } from "@angular/router";
+import { combineLatest, from, lastValueFrom, Observable, switchMap } from "rxjs";
+
+import { ProviderService } from "@bitwarden/common/admin-console/abstractions/provider.service";
+import { ProviderUserType } from "@bitwarden/common/admin-console/enums";
+import { BillingApiServiceAbstraction } from "@bitwarden/common/billing/abstractions";
+import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
+import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
+import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
+import { SyncService } from "@bitwarden/common/platform/sync";
+import { DialogService } from "@bitwarden/components";
+import { RequirePaymentMethodDialogComponent } from "@bitwarden/web-vault/app/billing/payment/components";
+
+@Injectable()
+export class ProviderWarningsService {
+ constructor(
+ private activatedRoute: ActivatedRoute,
+ private billingApiService: BillingApiServiceAbstraction,
+ private configService: ConfigService,
+ private dialogService: DialogService,
+ private i18nService: I18nService,
+ private providerService: ProviderService,
+ private router: Router,
+ private syncService: SyncService,
+ ) {}
+
+ showProviderSuspendedDialog$ = (providerId: string): Observable =>
+ combineLatest([
+ this.configService.getFeatureFlag$(FeatureFlag.PM21821_ProviderPortalTakeover),
+ this.providerService.get$(providerId),
+ from(this.billingApiService.getProviderSubscription(providerId)),
+ ]).pipe(
+ switchMap(async ([providerPortalTakeover, provider, subscription]) => {
+ if (!providerPortalTakeover || provider.enabled) {
+ return;
+ }
+
+ if (subscription.status === "unpaid") {
+ switch (provider.type) {
+ case ProviderUserType.ProviderAdmin: {
+ const cancelAt = subscription.cancelAt
+ ? new Date(subscription.cancelAt).toLocaleDateString("en-US", {
+ month: "short",
+ day: "2-digit",
+ year: "numeric",
+ })
+ : null;
+
+ const dialogRef = RequirePaymentMethodDialogComponent.open(this.dialogService, {
+ data: {
+ owner: {
+ type: "provider",
+ data: provider,
+ },
+ callout: {
+ type: "danger",
+ title: this.i18nService.t("unpaidInvoices"),
+ message: this.i18nService.t(
+ "restoreProviderPortalAccessViaPaymentMethod",
+ cancelAt ?? undefined,
+ ),
+ },
+ },
+ });
+
+ const result = await lastValueFrom(dialogRef.closed);
+
+ if (result?.type === "success") {
+ await this.syncService.fullSync(true);
+ await this.router.navigate(["."], {
+ relativeTo: this.activatedRoute,
+ onSameUrlNavigation: "reload",
+ });
+ }
+ break;
+ }
+ case ProviderUserType.ServiceUser: {
+ await this.dialogService.openSimpleDialog({
+ type: "danger",
+ title: this.i18nService.t("unpaidInvoices"),
+ content: this.i18nService.t("unpaidInvoicesForServiceUser"),
+ disableClose: true,
+ });
+ break;
+ }
+ }
+ } else {
+ await this.dialogService.openSimpleDialog({
+ type: "danger",
+ title: this.i18nService.t("providerSuspended", provider.name),
+ content: this.i18nService.t("restoreProviderPortalAccessViaCustomerSupport"),
+ disableClose: true,
+ acceptButtonText: this.i18nService.t("contactSupportShort"),
+ cancelButtonText: null,
+ acceptAction: async () => {
+ window.open("https://bitwarden.com/contact/", "_blank");
+ return Promise.resolve();
+ },
+ });
+ }
+ }),
+ );
+}
diff --git a/bitwarden_license/bit-web/src/app/billing/providers/subscription/provider-subscription-status.component.ts b/bitwarden_license/bit-web/src/app/billing/providers/subscription/provider-subscription-status.component.ts
index 974dc9c460f..f9ff006de24 100644
--- a/bitwarden_license/bit-web/src/app/billing/providers/subscription/provider-subscription-status.component.ts
+++ b/bitwarden_license/bit-web/src/app/billing/providers/subscription/provider-subscription-status.component.ts
@@ -158,7 +158,7 @@ export class ProviderSubscriptionStatusComponent {
}
case "incomplete_expired":
case "canceled": {
- const canceledText = this.i18nService.t("canceled");
+ const canceledText = this.i18nService.t("providersubscriptionCanceled");
return {
status: {
label: defaultStatusLabel,
@@ -171,7 +171,7 @@ export class ProviderSubscriptionStatusComponent {
callout: {
severity: "danger",
header: canceledText,
- body: this.i18nService.t("subscriptionCanceled"),
+ body: this.i18nService.t("providersubCanceledmessage"),
},
};
}
diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/integrations/integrations.component.spec.ts b/bitwarden_license/bit-web/src/app/secrets-manager/integrations/integrations.component.spec.ts
index b563591f32f..be4b5725ecc 100644
--- a/bitwarden_license/bit-web/src/app/secrets-manager/integrations/integrations.component.spec.ts
+++ b/bitwarden_license/bit-web/src/app/secrets-manager/integrations/integrations.component.spec.ts
@@ -1,6 +1,7 @@
import { Component } from "@angular/core";
import { ComponentFixture, TestBed } from "@angular/core/testing";
import { By } from "@angular/platform-browser";
+import { ActivatedRoute } from "@angular/router";
import { mock } from "jest-mock-extended";
import { of } from "rxjs";
@@ -8,9 +9,12 @@ import {} from "@bitwarden/web-vault/app/shared";
import { JslibModule } from "@bitwarden/angular/jslib.module";
import { SYSTEM_THEME_OBSERVABLE } from "@bitwarden/angular/services/injection-tokens";
+import { OrganizationIntegrationApiService } from "@bitwarden/bit-common/dirt/integrations";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { ThemeType } from "@bitwarden/common/platform/enums";
import { ThemeStateService } from "@bitwarden/common/platform/theming/theme-state.service";
+import { ToastService } from "@bitwarden/components";
+import { I18nPipe } from "@bitwarden/ui-common";
import { IntegrationCardComponent } from "@bitwarden/web-vault/app/admin-console/organizations/shared/components/integrations/integration-card/integration-card.component";
import { IntegrationGridComponent } from "@bitwarden/web-vault/app/admin-console/organizations/shared/components/integrations/integration-grid/integration-grid.component";
@@ -33,23 +37,25 @@ class MockNewMenuComponent {}
describe("IntegrationsComponent", () => {
let fixture: ComponentFixture;
+ const mockOrgIntegrationApiService = mock();
+ const activatedRouteMock = {
+ snapshot: { paramMap: { get: jest.fn() } },
+ };
+ const mockI18nService = mock();
+
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [IntegrationsComponent, MockHeaderComponent, MockNewMenuComponent],
imports: [JslibModule, IntegrationGridComponent, IntegrationCardComponent],
providers: [
- {
- provide: I18nService,
- useValue: mock(),
- },
- {
- provide: ThemeStateService,
- useValue: mock(),
- },
- {
- provide: SYSTEM_THEME_OBSERVABLE,
- useValue: of(ThemeType.Light),
- },
+ { provide: I18nService, useValue: mock() },
+ { provide: ThemeStateService, useValue: mock() },
+ { provide: SYSTEM_THEME_OBSERVABLE, useValue: of(ThemeType.Light) },
+ { provide: ActivatedRoute, useValue: activatedRouteMock },
+ { provide: OrganizationIntegrationApiService, useValue: mockOrgIntegrationApiService },
+ { provide: ToastService, useValue: mock() },
+ { provide: I18nPipe, useValue: mock() },
+ { provide: I18nService, useValue: mockI18nService },
],
}).compileComponents();
fixture = TestBed.createComponent(IntegrationsComponent);
diff --git a/bitwarden_license/bit-web/tsconfig.build.json b/bitwarden_license/bit-web/tsconfig.build.json
index 6313ce27863..66c475051ed 100644
--- a/bitwarden_license/bit-web/tsconfig.build.json
+++ b/bitwarden_license/bit-web/tsconfig.build.json
@@ -7,8 +7,5 @@
"../../bitwarden_license/bit-web/src/main.ts"
],
- "include": [
- "../../apps/web/src/connectors/*.ts",
- "../../libs/common/src/key-management/crypto/services/encrypt.worker.ts"
- ]
+ "include": ["../../apps/web/src/connectors/*.ts"]
}
diff --git a/bitwarden_license/bit-web/tsconfig.json b/bitwarden_license/bit-web/tsconfig.json
index 7ec0441f4c1..0836d3d54ad 100644
--- a/bitwarden_license/bit-web/tsconfig.json
+++ b/bitwarden_license/bit-web/tsconfig.json
@@ -11,8 +11,6 @@
"../../apps/web/src/connectors/*.ts",
"../../apps/web/src/**/*.stories.ts",
"../../apps/web/src/**/*.spec.ts",
- "../../libs/common/src/key-management/crypto/services/encrypt.worker.ts",
-
"src/**/*.stories.ts",
"src/**/*.spec.ts"
]
diff --git a/eslint.config.mjs b/eslint.config.mjs
index f08523d5878..c4018b7625e 100644
--- a/eslint.config.mjs
+++ b/eslint.config.mjs
@@ -310,6 +310,26 @@ export default tseslint.config(
"no-console": "off",
},
},
+ // Tailwind migrated clients & libs
+ {
+ files: ["apps/web/**/*.html", "bitwarden_license/bit-web/**/*.html", "libs/**/*.html"],
+ rules: {
+ "tailwindcss/no-custom-classname": [
+ "error",
+ {
+ // In migrated clients we only allow tailwind classes plus the following exceptions
+ whitelist: [
+ "((bwi)\\-?).*", // Font icons
+ "logo",
+ "logo-themed",
+ "file-selector",
+ "mfaType.*",
+ "filter.*", // Temporary until filters are migrated
+ ],
+ },
+ ],
+ },
+ },
/// Bandaids for keeping existing circular dependencies from getting worse and new ones from being created
/// Will be removed after Nx is implemented and existing circular dependencies are removed.
{
diff --git a/libs/admin-console/src/common/collections/abstractions/collection-admin.service.ts b/libs/admin-console/src/common/collections/abstractions/collection-admin.service.ts
index 36222b16794..61faabb16b8 100644
--- a/libs/admin-console/src/common/collections/abstractions/collection-admin.service.ts
+++ b/libs/admin-console/src/common/collections/abstractions/collection-admin.service.ts
@@ -1,18 +1,23 @@
-// FIXME: Update this file to be type safe and remove this and next line
-// @ts-strict-ignore
import { CollectionDetailsResponse } from "@bitwarden/admin-console/common";
+import { UserId } from "@bitwarden/common/types/guid";
import { CollectionAccessSelectionView, CollectionAdminView } from "../models";
export abstract class CollectionAdminService {
- getAll: (organizationId: string) => Promise;
- get: (organizationId: string, collectionId: string) => Promise;
- save: (collection: CollectionAdminView) => Promise;
- delete: (organizationId: string, collectionId: string) => Promise;
- bulkAssignAccess: (
+ abstract getAll(organizationId: string): Promise;
+ abstract get(
+ organizationId: string,
+ collectionId: string,
+ ): Promise;
+ abstract save(
+ collection: CollectionAdminView,
+ userId: UserId,
+ ): Promise;
+ abstract delete(organizationId: string, collectionId: string): Promise;
+ abstract bulkAssignAccess(
organizationId: string,
collectionIds: string[],
users: CollectionAccessSelectionView[],
groups: CollectionAccessSelectionView[],
- ) => Promise;
+ ): Promise;
}
diff --git a/libs/admin-console/src/common/collections/abstractions/collection.service.ts b/libs/admin-console/src/common/collections/abstractions/collection.service.ts
index 61fc94b271c..dabaf078e16 100644
--- a/libs/admin-console/src/common/collections/abstractions/collection.service.ts
+++ b/libs/admin-console/src/common/collections/abstractions/collection.service.ts
@@ -1,5 +1,3 @@
-// FIXME: Update this file to be type safe and remove this and next line
-// @ts-strict-ignore
import { Observable } from "rxjs";
import { CollectionId, OrganizationId, UserId } from "@bitwarden/common/types/guid";
@@ -9,27 +7,25 @@ import { TreeNode } from "@bitwarden/common/vault/models/domain/tree-node";
import { CollectionData, Collection, CollectionView } from "../models";
export abstract class CollectionService {
- encryptedCollections$: Observable;
- decryptedCollections$: Observable;
-
- clearActiveUserCache: () => Promise;
- encrypt: (model: CollectionView) => Promise;
- decryptedCollectionViews$: (ids: CollectionId[]) => Observable;
+ abstract encryptedCollections$(userId: UserId): Observable;
+ abstract decryptedCollections$(userId: UserId): Observable;
+ abstract upsert(collection: CollectionData, userId: UserId): Promise;
+ abstract replace(collections: { [id: string]: CollectionData }, userId: UserId): Promise;
/**
- * @deprecated This method will soon be made private
- * See PM-12375
+ * @deprecated This method will soon be made private, use `decryptedCollections$` instead.
*/
- decryptMany: (
+ abstract decryptMany$(
collections: Collection[],
- orgKeys?: Record,
- ) => Promise;
- get: (id: string) => Promise;
- getAll: () => Promise;
- getAllDecrypted: () => Promise;
- getAllNested: (collections?: CollectionView[]) => Promise[]>;
- getNested: (id: string) => Promise>;
- upsert: (collection: CollectionData | CollectionData[]) => Promise;
- replace: (collections: { [id: string]: CollectionData }, userId: UserId) => Promise;
- clear: (userId?: string) => Promise;
- delete: (id: string | string[]) => Promise;
+ orgKeys: Record,
+ ): Observable;
+ abstract delete(ids: CollectionId[], userId: UserId): Promise;
+ abstract encrypt(model: CollectionView, userId: UserId): Promise;
+ /**
+ * Transforms the input CollectionViews into TreeNodes
+ */
+ abstract getAllNested(collections: CollectionView[]): TreeNode[];
+ /*
+ * Transforms the input CollectionViews into TreeNodes and then returns the Treenode with the specified id
+ */
+ abstract getNested(collections: CollectionView[], id: string): TreeNode;
}
diff --git a/libs/admin-console/src/common/collections/abstractions/vnext-collection.service.ts b/libs/admin-console/src/common/collections/abstractions/vnext-collection.service.ts
deleted file mode 100644
index e1b2a5759a1..00000000000
--- a/libs/admin-console/src/common/collections/abstractions/vnext-collection.service.ts
+++ /dev/null
@@ -1,43 +0,0 @@
-// FIXME: Update this file to be type safe and remove this and next line
-// @ts-strict-ignore
-import { Observable } from "rxjs";
-
-import { OrganizationId, UserId } from "@bitwarden/common/types/guid";
-import { OrgKey } from "@bitwarden/common/types/key";
-import { TreeNode } from "@bitwarden/common/vault/models/domain/tree-node";
-
-import { CollectionData, Collection, CollectionView } from "../models";
-
-export abstract class vNextCollectionService {
- encryptedCollections$: (userId: UserId) => Observable;
- decryptedCollections$: (userId: UserId) => Observable;
- upsert: (collection: CollectionData | CollectionData[], userId: UserId) => Promise;
- replace: (collections: { [id: string]: CollectionData }, userId: UserId) => Promise;
- /**
- * Clear decrypted state without affecting encrypted state.
- * Used for locking the vault.
- */
- clearDecryptedState: (userId: UserId) => Promise;
- /**
- * Clear decrypted and encrypted state.
- * Used for logging out.
- */
- clear: (userId: UserId) => Promise;
- delete: (id: string | string[], userId: UserId) => Promise;
- encrypt: (model: CollectionView) => Promise;
- /**
- * @deprecated This method will soon be made private, use `decryptedCollections$` instead.
- */
- decryptMany: (
- collections: Collection[],
- orgKeys?: Record | null,
- ) => Promise;
- /**
- * Transforms the input CollectionViews into TreeNodes
- */
- getAllNested: (collections: CollectionView[]) => TreeNode[];
- /**
- * Transforms the input CollectionViews into TreeNodes and then returns the Treenode with the specified id
- */
- getNested: (collections: CollectionView[], id: string) => TreeNode;
-}
diff --git a/libs/admin-console/src/common/collections/models/collection-admin.view.ts b/libs/admin-console/src/common/collections/models/collection-admin.view.ts
index dd7a57013ca..dcc88551551 100644
--- a/libs/admin-console/src/common/collections/models/collection-admin.view.ts
+++ b/libs/admin-console/src/common/collections/models/collection-admin.view.ts
@@ -4,7 +4,10 @@ import { CollectionAccessSelectionView } from "./collection-access-selection.vie
import { CollectionAccessDetailsResponse } from "./collection.response";
import { CollectionView } from "./collection.view";
+// TODO: this is used to represent the pseudo "Unassigned" collection as well as
+// the user's personal vault (as a pseudo organization). This should be separated out into different values.
export const Unassigned = "unassigned";
+export type Unassigned = typeof Unassigned;
export class CollectionAdminView extends CollectionView {
groups: CollectionAccessSelectionView[] = [];
diff --git a/libs/admin-console/src/common/collections/models/collection.data.ts b/libs/admin-console/src/common/collections/models/collection.data.ts
index b28a066509c..27c5c0c0efa 100644
--- a/libs/admin-console/src/common/collections/models/collection.data.ts
+++ b/libs/admin-console/src/common/collections/models/collection.data.ts
@@ -26,7 +26,10 @@ export class CollectionData {
this.type = response.type;
}
- static fromJSON(obj: Jsonify