1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-17 16:53:34 +00:00

Merge branch 'master' of https://github.com/bitwarden/clients into tools/pm-147/import-error-states-usability-improvements

This commit is contained in:
Daniel James Smith
2023-07-25 17:46:59 +02:00
186 changed files with 15827 additions and 2803 deletions

2
.github/CODEOWNERS vendored
View File

@@ -13,7 +13,7 @@ bitwarden_license/bit-web/src/app/secrets-manager @bitwarden/team-secrets-manage
apps/browser/src/auth @bitwarden/team-auth-dev apps/browser/src/auth @bitwarden/team-auth-dev
apps/cli/src/auth @bitwarden/team-auth-dev apps/cli/src/auth @bitwarden/team-auth-dev
apps/desktop/src/auth @bitwarden/team-auth-dev apps/desktop/src/auth @bitwarden/team-auth-dev
apps/web/src/auth @bitwarden/team-auth-dev apps/web/src/app/auth @bitwarden/team-auth-dev
# web connectors used for auth # web connectors used for auth
apps/web/src/connectors @bitwarden/team-auth-dev apps/web/src/connectors @bitwarden/team-auth-dev
bitwarden_license/bit-web/src/app/auth @bitwarden/team-auth-dev bitwarden_license/bit-web/src/app/auth @bitwarden/team-auth-dev

View File

@@ -145,8 +145,8 @@ jobs:
- name: Build - name: Build
run: npm run dist run: npm run dist
- name: Build Manifest v3 # - name: Build Manifest v3
run: npm run dist:mv3 # run: npm run dist:mv3
- name: Gulp - name: Gulp
run: gulp ci run: gulp ci
@@ -179,12 +179,12 @@ jobs:
path: apps/browser/dist/dist-opera.zip path: apps/browser/dist/dist-opera.zip
if-no-files-found: error if-no-files-found: error
- name: Upload Opera MV3 artifact # - name: Upload Opera MV3 artifact
uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 # uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2
with: # with:
name: dist-opera-MV3-${{ env._BUILD_NUMBER }}.zip # name: dist-opera-MV3-${{ env._BUILD_NUMBER }}.zip
path: apps/browser/dist/dist-opera-mv3.zip # path: apps/browser/dist/dist-opera-mv3.zip
if-no-files-found: error # if-no-files-found: error
- name: Upload Chrome artifact - name: Upload Chrome artifact
uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2
@@ -193,12 +193,12 @@ jobs:
path: apps/browser/dist/dist-chrome.zip path: apps/browser/dist/dist-chrome.zip
if-no-files-found: error if-no-files-found: error
- name: Upload Chrome MV3 artifact # - name: Upload Chrome MV3 artifact
uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 # uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2
with: # with:
name: dist-chrome-MV3-${{ env._BUILD_NUMBER }}.zip # name: dist-chrome-MV3-${{ env._BUILD_NUMBER }}.zip
path: apps/browser/dist/dist-chrome-mv3.zip # path: apps/browser/dist/dist-chrome-mv3.zip
if-no-files-found: error # if-no-files-found: error
- name: Upload Firefox artifact - name: Upload Firefox artifact
uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2
@@ -214,12 +214,12 @@ jobs:
path: apps/browser/dist/dist-edge.zip path: apps/browser/dist/dist-edge.zip
if-no-files-found: error if-no-files-found: error
- name: Upload Edge MV3 artifact # - name: Upload Edge MV3 artifact
uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 # uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2
with: # with:
name: dist-edge-MV3-${{ env._BUILD_NUMBER }}.zip # name: dist-edge-MV3-${{ env._BUILD_NUMBER }}.zip
path: apps/browser/dist/dist-edge-mv3.zip # path: apps/browser/dist/dist-edge-mv3.zip
if-no-files-found: error # if-no-files-found: error
- name: Upload browser source - name: Upload browser source
uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2

View File

@@ -243,7 +243,7 @@ jobs:
shell: pwsh shell: pwsh
run: | run: |
cd dist cd dist
choco push choco push --source=https://push.chocolatey.org/
npm: npm:
name: Publish NPM name: Publish NPM

View File

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

View File

@@ -0,0 +1,4 @@
/* eslint-disable no-undef */
module.exports = {
plugins: [require("tailwindcss"), require("autoprefixer"), require("postcss-nested")],
};

View File

@@ -2236,10 +2236,10 @@
"description": "United States" "description": "United States"
}, },
"accessDenied": { "accessDenied": {
"message": "Достъпът е отказан. Нямате право за преглед на тази страница." "message": "Отказан достъп. Нямате право за преглед на страницата."
}, },
"general": { "general": {
"message": "General" "message": "Общи"
}, },
"display": { "display": {
"message": "Display" "message": "Display"

View File

@@ -2242,6 +2242,6 @@
"message": "General" "message": "General"
}, },
"display": { "display": {
"message": "Display" "message": "Mostra"
} }
} }

File diff suppressed because it is too large Load Diff

View File

@@ -2236,12 +2236,12 @@
"description": "United States" "description": "United States"
}, },
"accessDenied": { "accessDenied": {
"message": "Access denied. You do not have permission to view this page." "message": "Ligipääs keelatud. Sul pole lubatud seda lehekülge vaadata."
}, },
"general": { "general": {
"message": "General" "message": "Üldine"
}, },
"display": { "display": {
"message": "Display" "message": "Kuvamine"
} }
} }

View File

@@ -196,7 +196,7 @@
"message": "Laguntza eta iritziak" "message": "Laguntza eta iritziak"
}, },
"helpCenter": { "helpCenter": {
"message": "Bitwarden Help center" "message": "Bitwarden Laguntza zentroa"
}, },
"communityForums": { "communityForums": {
"message": "Explore Bitwarden community forums" "message": "Explore Bitwarden community forums"

View File

@@ -2239,9 +2239,9 @@
"message": "دسترسی رد شد. شما اجازه مشاهده این صفحه را ندارید." "message": "دسترسی رد شد. شما اجازه مشاهده این صفحه را ندارید."
}, },
"general": { "general": {
"message": "General" "message": "عمومی"
}, },
"display": { "display": {
"message": "Display" "message": "نمایش"
} }
} }

View File

@@ -288,7 +288,7 @@
"message": "Näytä" "message": "Näytä"
}, },
"noItemsInList": { "noItemsInList": {
"message": "Ei näytettäviä kohteita." "message": "Näytettäviä kohteita ei ole."
}, },
"itemInformation": { "itemInformation": {
"message": "Kohteen tiedot" "message": "Kohteen tiedot"
@@ -2123,7 +2123,7 @@
"message": "Laitteella kirjautuminen" "message": "Laitteella kirjautuminen"
}, },
"loginWithDeviceEnabledInfo": { "loginWithDeviceEnabledInfo": {
"message": "Laitteella kirjautuminen on määritettävä Bitwarden-mobiilisovelluksen asetuksista. Tarvitsetko eri vaihtoehdon?" "message": "Laitteella kirjautuminen on määritettävä Bitwarden-sovelluksen asetuksista. Tarvitsetko eri vaihtoehdon?"
}, },
"fingerprintPhraseHeader": { "fingerprintPhraseHeader": {
"message": "Tunnistelauseke" "message": "Tunnistelauseke"

View File

@@ -56,7 +56,7 @@
"message": "Saugykla" "message": "Saugykla"
}, },
"myVault": { "myVault": {
"message": "Saugykla" "message": "Mano saugykla"
}, },
"allVaults": { "allVaults": {
"message": "Visos saugyklos" "message": "Visos saugyklos"
@@ -613,10 +613,10 @@
"description": "Clipboard is the operating system thing where you copy/paste data to on your device." "description": "Clipboard is the operating system thing where you copy/paste data to on your device."
}, },
"notificationAddDesc": { "notificationAddDesc": {
"message": "Ar Bitwarden turėtų prisiminti šį slaptažodį?" "message": "Ar Bitwarden turėtų įsiminti šį slaptažodį už tave?"
}, },
"notificationAddSave": { "notificationAddSave": {
"message": "Taip, išsaugoti dabar" "message": "Išsaugoti"
}, },
"enableChangedPasswordNotification": { "enableChangedPasswordNotification": {
"message": "Paprašyti atnaujinti esamą prisijungimą" "message": "Paprašyti atnaujinti esamą prisijungimą"
@@ -625,10 +625,10 @@
"message": "Paprašyti atnaujinti prisijungimo slaptažodį, kai pakeitimas aptiktas svetainėje." "message": "Paprašyti atnaujinti prisijungimo slaptažodį, kai pakeitimas aptiktas svetainėje."
}, },
"notificationChangeDesc": { "notificationChangeDesc": {
"message": "Ar norite atnaujinti šį slaptažodį Bitwarden?" "message": "Ar nori atnaujinti šį slaptažodį Bitwarden?"
}, },
"notificationChangeSave": { "notificationChangeSave": {
"message": "Taip, atnaujinti dabar" "message": "Atnaujinti"
}, },
"enableContextMenuItem": { "enableContextMenuItem": {
"message": "Rodyti kontekstinio meniu pasririnkimus" "message": "Rodyti kontekstinio meniu pasririnkimus"
@@ -658,7 +658,7 @@
"description": "Light color" "description": "Light color"
}, },
"solarizedDark": { "solarizedDark": {
"message": "Solarized dark", "message": "Saulėtas tamsą",
"description": "'Solarized' is a noun and the name of a color scheme. It should not be translated." "description": "'Solarized' is a noun and the name of a color scheme. It should not be translated."
}, },
"exportVault": { "exportVault": {
@@ -748,7 +748,7 @@
"message": "Priedų nėra." "message": "Priedų nėra."
}, },
"attachmentSaved": { "attachmentSaved": {
"message": "Priedas buvo išsaugotas." "message": "Priedas išsaugotas"
}, },
"file": { "file": {
"message": "Failas" "message": "Failas"
@@ -757,13 +757,13 @@
"message": "Pasirinkite failą." "message": "Pasirinkite failą."
}, },
"maxFileSize": { "maxFileSize": {
"message": "Failai negali būti didesni už 500 MB." "message": "Didžiausias failo dydis 500 MB."
}, },
"featureUnavailable": { "featureUnavailable": {
"message": "Funkcija neprieinama" "message": "Funkcija neprieinama"
}, },
"updateKey": { "updateKey": {
"message": "Negalite naudoti šios funkcijos, kol neatnaujinsite šifravimo raktą." "message": "Negali naudotis šia funkcija, kol neatnaujinsi šifravimo raktą."
}, },
"premiumMembership": { "premiumMembership": {
"message": "Premium narystė" "message": "Premium narystė"
@@ -781,7 +781,7 @@
"message": "Neturite Premium narystės." "message": "Neturite Premium narystės."
}, },
"premiumSignUpAndGet": { "premiumSignUpAndGet": {
"message": "Prisijungite prie Premium narystės ir gaukite:" "message": "Prisijunk prie Premium narystės ir gauk:"
}, },
"ppremiumSignUpStorage": { "ppremiumSignUpStorage": {
"message": "1 GB užšifruotos vietos diske bylų prisegimams." "message": "1 GB užšifruotos vietos diske bylų prisegimams."
@@ -790,10 +790,10 @@
"message": "Papildomos dviejų žingsių prisijungimo opcijos, tokios kaip YubiKey, FIDO U2F ir Duo." "message": "Papildomos dviejų žingsių prisijungimo opcijos, tokios kaip YubiKey, FIDO U2F ir Duo."
}, },
"ppremiumSignUpReports": { "ppremiumSignUpReports": {
"message": "Slaptažodžio higiena, prieigos sveikata ir duomenų nutekinimo ataskaitos, kad jūsų seifas būtų saugus." "message": "Slaptažodžio higiena, prieigos sveikata ir duomenų nutekinimo ataskaitos, kad tavo saugyklas būtų saugus."
}, },
"ppremiumSignUpTotp": { "ppremiumSignUpTotp": {
"message": "TOTP patvirtinimo kodų (2FA) generatorius prisijungimams prie jūsų saugyklos." "message": "TOTP patvirtinimo kodų (2FA) generatorius prisijungimams prie tavo saugyklos."
}, },
"ppremiumSignUpSupport": { "ppremiumSignUpSupport": {
"message": "Prioritetinis klientų aptarnavimas." "message": "Prioritetinis klientų aptarnavimas."
@@ -802,13 +802,13 @@
"message": "Visos būsimos Premium savybės. Daugiau jau greitai!" "message": "Visos būsimos Premium savybės. Daugiau jau greitai!"
}, },
"premiumPurchase": { "premiumPurchase": {
"message": "Įsigyti Premium planą" "message": "Įsigyti Premium"
}, },
"premiumPurchaseAlert": { "premiumPurchaseAlert": {
"message": "Jūs galite įsigyti Premium narystę bitwarden.com puslapyje. Ar norite aplankyti šį puslapį dabar?" "message": "Gali įsigyti Premium narystę bitwarden.com interneto saugykloje. Ar nori aplankyti svetainėje dabar?"
}, },
"premiumCurrentMember": { "premiumCurrentMember": {
"message": "Jūs esate Premium narys!" "message": "Tu esi Premium narys!"
}, },
"premiumCurrentMemberThanks": { "premiumCurrentMemberThanks": {
"message": "Dėkojame, kad remiate Bitwarden." "message": "Dėkojame, kad remiate Bitwarden."
@@ -835,16 +835,16 @@
"message": "Paleidžiant patvirtinti biometrinius duomenis" "message": "Paleidžiant patvirtinti biometrinius duomenis"
}, },
"premiumRequired": { "premiumRequired": {
"message": "Tik su Premium naryste" "message": "Premium reikalinga"
}, },
"premiumRequiredDesc": { "premiumRequiredDesc": {
"message": "Premium narystė reikalinga šiai funkcijai naudoti." "message": "Premium narystė reikalinga šiai funkcijai naudoti."
}, },
"enterVerificationCodeApp": { "enterVerificationCodeApp": {
"message": "Įveskite 6 skaitmenų patvirtinimo kodą iš jūsų autentifikavimo aplikacijos." "message": "Įvesk 6 skaitmenų patvirtinimo kodą iš tavo autentifikavimo aplikacijos."
}, },
"enterVerificationCodeEmail": { "enterVerificationCodeEmail": {
"message": "Įveskite 6 skaitmenų prisijungimo kodą, kuris buvo išsiųstas $EMAIL$ el. paštu.", "message": "Įvesk 6 skaitmenų prisijungimo kodą, kuris buvo išsiųstas $EMAIL$ el. paštu.",
"placeholders": { "placeholders": {
"email": { "email": {
"content": "$1", "content": "$1",
@@ -871,34 +871,34 @@
"message": "Naudoti dar vieną dviejų žingsnių prisijungimo metodą" "message": "Naudoti dar vieną dviejų žingsnių prisijungimo metodą"
}, },
"insertYubiKey": { "insertYubiKey": {
"message": "Insert your YubiKey into your computer's USB port, then touch its button." "message": "Įkišk YubiKey į savo kompiuterio USB prievadą, tada paliesk jo mygtuką."
}, },
"insertU2f": { "insertU2f": {
"message": "Insert your security key into your computer's USB port. If it has a button, touch it." "message": "Įkišk savo saugos raktą į kompiuterio USB prievadą. Jei jame yra mygtukas, paliesk jį."
}, },
"webAuthnNewTab": { "webAuthnNewTab": {
"message": "To start the WebAuthn 2FA verification. Click the button below to open a new tab and follow the instructions provided in the new tab." "message": "Norint pradėti WebAuthn 2FA patikrinimą. Spustelėk toliau esantį mygtuką, kad atsidarytų naujas skirtukas, ir sek naujame skirtuke pateiktas instrukcijas."
}, },
"webAuthnNewTabOpen": { "webAuthnNewTabOpen": {
"message": "Atidaryti naują skirtuką" "message": "Atidaryti naują skirtuką"
}, },
"webAuthnAuthenticate": { "webAuthnAuthenticate": {
"message": "Authenticate WebAuthn" "message": "Autentifikuoti WebAuthn"
}, },
"loginUnavailable": { "loginUnavailable": {
"message": "Prisijungimas nepasiekiamas" "message": "Prisijungimas nepasiekiamas"
}, },
"noTwoStepProviders": { "noTwoStepProviders": {
"message": "This account has two-step login set up, however, none of the configured two-step providers are supported by this web browser." "message": "Šioje paskyroje nustatytas dviejų žingsnių prisijungimas, tačiau, nė vienas iš sukonfigūruotų dviejų žingsnių paslaugų teikėjų nėra palaikomas šioje interneto naršyklėje."
}, },
"noTwoStepProviders2": { "noTwoStepProviders2": {
"message": "Please use a supported web browser (such as Chrome) and/or add additional providers that are better supported across web browsers (such as an authenticator app)." "message": "Prašome naudoti palaikomą interneto naršyklę (pvz., Chrome) ir/arba pridėti papildomus paslaugų teikėjus, kurie geriau palaikomi įvairiose interneto naršyklėse (pvz., autentifikavimo programėlę)."
}, },
"twoStepOptions": { "twoStepOptions": {
"message": "Dviejų žingsnių prisijungimo parinktys" "message": "Dviejų žingsnių prisijungimo parinktys"
}, },
"recoveryCodeDesc": { "recoveryCodeDesc": {
"message": "Lost access to all of your two-factor providers? Use your recovery code to turn off all two-factor providers from your account." "message": "Praradai prieigą prie visų savo dviejų veiksnių teikėjų? Naudok atkūrimo kodą, kad iš savo paskyros išjungtum visus dviejų veiksnių teikėjus."
}, },
"recoveryCodeTitle": { "recoveryCodeTitle": {
"message": "Atkūrimo kodas" "message": "Atkūrimo kodas"
@@ -907,46 +907,46 @@
"message": "Autentifikavimo programa" "message": "Autentifikavimo programa"
}, },
"authenticatorAppDesc": { "authenticatorAppDesc": {
"message": "Use an authenticator app (such as Authy or Google Authenticator) to generate time-based verification codes.", "message": "Naudok autentifikatoriaus programėlę (pvz., Authy arba Google Autentifikatorius), kad sugeneruotum laiko patikrinimo kodus.",
"description": "'Authy' and 'Google Authenticator' are product names and should not be translated." "description": "'Authy' and 'Google Authenticator' are product names and should not be translated."
}, },
"yubiKeyTitle": { "yubiKeyTitle": {
"message": "YubiKey OTP Security Key" "message": "YubiKey OTP saugumo raktas"
}, },
"yubiKeyDesc": { "yubiKeyDesc": {
"message": "Use a YubiKey to access your account. Works with YubiKey 4, 4 Nano, 4C, and NEO devices." "message": "Naudok YubiKey, kad prisijungtum prie savo paskyros. Veikia su YubiKey 4, 4 Nano, 4C ir NEO įrenginiais."
}, },
"duoDesc": { "duoDesc": {
"message": "Verify with Duo Security using the Duo Mobile app, SMS, phone call, or U2F security key.", "message": "Patvirtink su Duo Security naudodami Duo Mobile programą, SMS žinutę, telefono skambutį arba U2F saugumo raktą.",
"description": "'Duo Security' and 'Duo Mobile' are product names and should not be translated." "description": "'Duo Security' and 'Duo Mobile' are product names and should not be translated."
}, },
"duoOrganizationDesc": { "duoOrganizationDesc": {
"message": "Verify with Duo Security for your organization using the Duo Mobile app, SMS, phone call, or U2F security key.", "message": "Patikrink su Duo Security savo organizacijai naudodamasis Duo Mobile programą, SMS žinutę, telefono skambutį arba U2F saugumo raktą.",
"description": "'Duo Security' and 'Duo Mobile' are product names and should not be translated." "description": "'Duo Security' and 'Duo Mobile' are product names and should not be translated."
}, },
"webAuthnTitle": { "webAuthnTitle": {
"message": "FIDO2 WebAuthn" "message": "FIDO2 WebAuthn"
}, },
"webAuthnDesc": { "webAuthnDesc": {
"message": "Use any WebAuthn compatible security key to access your account." "message": "Naudok bet kurį WebAuthn palaikantį saugumo raktą, kad galėtum naudotis savo paskyra."
}, },
"emailTitle": { "emailTitle": {
"message": "El. paštas" "message": "El. paštas"
}, },
"emailDesc": { "emailDesc": {
"message": "Verification codes will be emailed to you." "message": "Patvirtinimo kodai bus atsiųsti el. paštu tau."
}, },
"selfHostedEnvironment": { "selfHostedEnvironment": {
"message": "Self-hosted environment" "message": "Savarankiškai sukurta aplinka"
}, },
"selfHostedEnvironmentFooter": { "selfHostedEnvironmentFooter": {
"message": "Specify the base URL of your on-premises hosted Bitwarden installation." "message": "Nurodyk pagrindinį URL adresą savo patalpose esančio Bitwarden diegimo."
}, },
"customEnvironment": { "customEnvironment": {
"message": "Individualizuota aplinka" "message": "Individualizuota aplinka"
}, },
"customEnvironmentFooter": { "customEnvironmentFooter": {
"message": "For advanced users. You can specify the base URL of each service independently." "message": "Pažengusiems naudotojams. Galite nurodyti kiekvienos paslaugos pagrindinį URL adresą atskirai."
}, },
"baseUrl": { "baseUrl": {
"message": "Serverio URL" "message": "Serverio URL"
@@ -967,13 +967,13 @@
"message": "Piktogramų serverio URL" "message": "Piktogramų serverio URL"
}, },
"environmentSaved": { "environmentSaved": {
"message": "Environment URLs saved" "message": "Aplinkos URL adresai išsaugoti"
}, },
"enableAutoFillOnPageLoad": { "enableAutoFillOnPageLoad": {
"message": "Automatiškai užpildyti užsikrovus puslapiui" "message": "Automatiškai užpildyti užsikrovus puslapiui"
}, },
"enableAutoFillOnPageLoadDesc": { "enableAutoFillOnPageLoadDesc": {
"message": "If a login form is detected, auto-fill when the web page loads." "message": "Jei aptikta prisijungimo forma, automatiškai užpildyti, kai kraunamas tinklalapis."
}, },
"experimentalFeature": { "experimentalFeature": {
"message": "Compromised or untrusted websites can exploit auto-fill on page load." "message": "Compromised or untrusted websites can exploit auto-fill on page load."
@@ -2105,10 +2105,10 @@
} }
}, },
"loginWithMasterPassword": { "loginWithMasterPassword": {
"message": "Log in with master password" "message": "Prisijungti su pagrindiniu slaptažodžiu"
}, },
"loggingInAs": { "loggingInAs": {
"message": "Logging in as" "message": "Prisijungimas kaip"
}, },
"notYou": { "notYou": {
"message": "Ne jūs?" "message": "Ne jūs?"
@@ -2117,13 +2117,13 @@
"message": "Ar jūs naujas čia?" "message": "Ar jūs naujas čia?"
}, },
"rememberEmail": { "rememberEmail": {
"message": "Remember email" "message": "Prisiminti el. paštą"
}, },
"loginWithDevice": { "loginWithDevice": {
"message": "Prisijunkite naudodami įrenginį" "message": "Prisijunkite naudodami įrenginį"
}, },
"loginWithDeviceEnabledInfo": { "loginWithDeviceEnabledInfo": {
"message": "Log in with device must be set up in the settings of the Bitwarden app. Need another option?" "message": "Prisijungti su įrenginiu turi būti nustatyta Bitwarden aplikacijos nustatymuose. Reikia kito pasirinkimo?"
}, },
"fingerprintPhraseHeader": { "fingerprintPhraseHeader": {
"message": "Fingerprint phrase" "message": "Fingerprint phrase"
@@ -2177,10 +2177,10 @@
"message": "Your organization policies have turned on auto-fill on page load." "message": "Your organization policies have turned on auto-fill on page load."
}, },
"howToAutofill": { "howToAutofill": {
"message": "How to auto-fill" "message": "Kaip automatiškai užpildyti"
}, },
"autofillSelectInfoWithCommand": { "autofillSelectInfoWithCommand": {
"message": "Select an item from this page or use the shortcut: $COMMAND$", "message": "Pasirink elementą iš šio puslapio arba naudok trumpąjį klavišą: $COMMAND$",
"placeholders": { "placeholders": {
"command": { "command": {
"content": "$1", "content": "$1",
@@ -2228,20 +2228,20 @@
"message": "Opens in a new window" "message": "Opens in a new window"
}, },
"eu": { "eu": {
"message": "EU", "message": "ES",
"description": "European Union" "description": "European Union"
}, },
"us": { "us": {
"message": "US", "message": "JAV",
"description": "United States" "description": "United States"
}, },
"accessDenied": { "accessDenied": {
"message": "Access denied. You do not have permission to view this page." "message": "Prieiga uždrausta. Neturi teisės peržiūrėti šį puslapį."
}, },
"general": { "general": {
"message": "General" "message": "Bendra"
}, },
"display": { "display": {
"message": "Display" "message": "Rodyti"
} }
} }

File diff suppressed because it is too large Load Diff

View File

@@ -2239,9 +2239,9 @@
"message": "Одбијен приступ. Немате дозволу да видите ову страницу." "message": "Одбијен приступ. Немате дозволу да видите ову страницу."
}, },
"general": { "general": {
"message": "General" "message": "Опште"
}, },
"display": { "display": {
"message": "Display" "message": "Приказ"
} }
} }

View File

@@ -675,7 +675,7 @@
"message": "Підтвердити експорт сховища" "message": "Підтвердити експорт сховища"
}, },
"exportWarningDesc": { "exportWarningDesc": {
"message": "Експортовані дані вашого сховища знаходяться в незашифрованому вигляді. Вам не слід зберігати чи надсилати їх через незахищені канали (наприклад, е-поштою). Після використання негайно видаліть їх." "message": "Ваші експортовані дані сховища незашифровані. Не зберігайте і не надсилайте їх незахищеними каналами (як-от електронна пошта). Після використання негайно видаліть їх."
}, },
"encExportKeyWarningDesc": { "encExportKeyWarningDesc": {
"message": "Цей експорт шифрує ваші дані за допомогою ключа шифрування облікового запису. Якщо ви коли-небудь оновите ключ шифрування облікового запису, необхідно виконати експорт знову, оскільки не зможете розшифрувати цей файл експорту." "message": "Цей експорт шифрує ваші дані за допомогою ключа шифрування облікового запису. Якщо ви коли-небудь оновите ключ шифрування облікового запису, необхідно виконати експорт знову, оскільки не зможете розшифрувати цей файл експорту."
@@ -2239,9 +2239,9 @@
"message": "Доступ заборонено. У вас немає дозволу на перегляд цієї сторінки." "message": "Доступ заборонено. У вас немає дозволу на перегляд цієї сторінки."
}, },
"general": { "general": {
"message": "General" "message": "Загальні"
}, },
"display": { "display": {
"message": "Display" "message": "Екран"
} }
} }

View File

@@ -946,7 +946,7 @@
"message": "自定义环境" "message": "自定义环境"
}, },
"customEnvironmentFooter": { "customEnvironmentFooter": {
"message": "适用于高级用户。可以分别指定各个服务的基础 URL。" "message": "适用于高级用户。可以分别指定各个服务的基础 URL。"
}, },
"baseUrl": { "baseUrl": {
"message": "服务器 URL" "message": "服务器 URL"
@@ -2123,7 +2123,7 @@
"message": "设备登录" "message": "设备登录"
}, },
"loginWithDeviceEnabledInfo": { "loginWithDeviceEnabledInfo": {
"message": "必须在 Bitwarden 应用程序的设置中启用设备登录。需要其他选项吗?" "message": "设备登录必须在 Bitwarden 应用程序的设置中启用。需要其他选项吗?"
}, },
"fingerprintPhraseHeader": { "fingerprintPhraseHeader": {
"message": "指纹短语" "message": "指纹短语"

View File

@@ -751,8 +751,8 @@
].join('\n\n'); ].join('\n\n');
if ( if (
// At least one of the `savedURLs` uses SSL // At least one of the `savedURLs` uses SSL for the current page
savedURLs.some(url => url.startsWith('https://')) && savedURLs.some(url => url.startsWith(`https://${window.location.hostname}`)) &&
// The current page is not using SSL // The current page is not using SSL
document.location.protocol === 'http:' && document.location.protocol === 'http:' &&
// There are password inputs on the page // There are password inputs on the page

View File

@@ -409,13 +409,6 @@ export default class AutofillService implements AutofillServiceInterface {
continue; continue;
} }
const passwordFieldsForForm: AutofillField[] = [];
passwordFields.forEach((passField) => {
if (formKey === passField.form) {
passwordFieldsForForm.push(passField);
}
});
passwordFields.forEach((passField) => { passwordFields.forEach((passField) => {
pf = passField; pf = passField;
passwords.push(pf); passwords.push(pf);
@@ -438,7 +431,7 @@ export default class AutofillService implements AutofillServiceInterface {
if (!totp && !options.onlyVisibleFields) { if (!totp && !options.onlyVisibleFields) {
// not able to find any viewable totp fields. maybe there are some "hidden" ones? // not able to find any viewable totp fields. maybe there are some "hidden" ones?
totp = this.findTotpField(pageDetails, pf, true, true, true); totp = this.findTotpField(pageDetails, pf, true, true, false);
} }
if (totp) { if (totp) {
@@ -741,6 +734,15 @@ export default class AutofillService implements AutofillServiceInterface {
let exp: string = null; let exp: string = null;
for (let i = 0; i < CreditCardAutoFillConstants.MonthAbbr.length; i++) { for (let i = 0; i < CreditCardAutoFillConstants.MonthAbbr.length; i++) {
if ( if (
this.fieldAttrsContain(
fillFields.exp,
CreditCardAutoFillConstants.MonthAbbr[i] +
"/" +
CreditCardAutoFillConstants.YearAbbrLong[i]
)
) {
exp = fullMonth + "/" + fullYear;
} else if (
this.fieldAttrsContain( this.fieldAttrsContain(
fillFields.exp, fillFields.exp,
CreditCardAutoFillConstants.MonthAbbr[i] + CreditCardAutoFillConstants.MonthAbbr[i] +
@@ -753,12 +755,12 @@ export default class AutofillService implements AutofillServiceInterface {
} else if ( } else if (
this.fieldAttrsContain( this.fieldAttrsContain(
fillFields.exp, fillFields.exp,
CreditCardAutoFillConstants.MonthAbbr[i] + CreditCardAutoFillConstants.YearAbbrLong[i] +
"/" + "/" +
CreditCardAutoFillConstants.YearAbbrLong[i] CreditCardAutoFillConstants.MonthAbbr[i]
) )
) { ) {
exp = fullMonth + "/" + fullYear; exp = fullYear + "/" + fullMonth;
} else if ( } else if (
this.fieldAttrsContain( this.fieldAttrsContain(
fillFields.exp, fillFields.exp,
@@ -772,12 +774,12 @@ export default class AutofillService implements AutofillServiceInterface {
} else if ( } else if (
this.fieldAttrsContain( this.fieldAttrsContain(
fillFields.exp, fillFields.exp,
CreditCardAutoFillConstants.YearAbbrLong[i] + CreditCardAutoFillConstants.MonthAbbr[i] +
"/" + "-" +
CreditCardAutoFillConstants.MonthAbbr[i] CreditCardAutoFillConstants.YearAbbrLong[i]
) )
) { ) {
exp = fullYear + "/" + fullMonth; exp = fullMonth + "-" + fullYear;
} else if ( } else if (
this.fieldAttrsContain( this.fieldAttrsContain(
fillFields.exp, fillFields.exp,
@@ -791,12 +793,12 @@ export default class AutofillService implements AutofillServiceInterface {
} else if ( } else if (
this.fieldAttrsContain( this.fieldAttrsContain(
fillFields.exp, fillFields.exp,
CreditCardAutoFillConstants.MonthAbbr[i] + CreditCardAutoFillConstants.YearAbbrLong[i] +
"-" + "-" +
CreditCardAutoFillConstants.YearAbbrLong[i] CreditCardAutoFillConstants.MonthAbbr[i]
) )
) { ) {
exp = fullMonth + "-" + fullYear; exp = fullYear + "-" + fullMonth;
} else if ( } else if (
this.fieldAttrsContain( this.fieldAttrsContain(
fillFields.exp, fillFields.exp,
@@ -810,12 +812,10 @@ export default class AutofillService implements AutofillServiceInterface {
} else if ( } else if (
this.fieldAttrsContain( this.fieldAttrsContain(
fillFields.exp, fillFields.exp,
CreditCardAutoFillConstants.YearAbbrLong[i] + CreditCardAutoFillConstants.YearAbbrLong[i] + CreditCardAutoFillConstants.MonthAbbr[i]
"-" +
CreditCardAutoFillConstants.MonthAbbr[i]
) )
) { ) {
exp = fullYear + "-" + fullMonth; exp = fullYear + fullMonth;
} else if ( } else if (
this.fieldAttrsContain( this.fieldAttrsContain(
fillFields.exp, fillFields.exp,
@@ -827,10 +827,10 @@ export default class AutofillService implements AutofillServiceInterface {
} else if ( } else if (
this.fieldAttrsContain( this.fieldAttrsContain(
fillFields.exp, fillFields.exp,
CreditCardAutoFillConstants.YearAbbrLong[i] + CreditCardAutoFillConstants.MonthAbbr[i] CreditCardAutoFillConstants.MonthAbbr[i] + CreditCardAutoFillConstants.YearAbbrLong[i]
) )
) { ) {
exp = fullYear + fullMonth; exp = fullMonth + fullYear;
} else if ( } else if (
this.fieldAttrsContain( this.fieldAttrsContain(
fillFields.exp, fillFields.exp,
@@ -839,13 +839,6 @@ export default class AutofillService implements AutofillServiceInterface {
partYear != null partYear != null
) { ) {
exp = fullMonth + partYear; exp = fullMonth + partYear;
} else if (
this.fieldAttrsContain(
fillFields.exp,
CreditCardAutoFillConstants.MonthAbbr[i] + CreditCardAutoFillConstants.YearAbbrLong[i]
)
) {
exp = fullMonth + fullYear;
} }
if (exp != null) { if (exp != null) {
@@ -1340,7 +1333,8 @@ export default class AutofillService implements AutofillServiceInterface {
(canBeReadOnly || !f.readonly) && (canBeReadOnly || !f.readonly) &&
(withoutForm || f.form === passwordField.form) && (withoutForm || f.form === passwordField.form) &&
(canBeHidden || f.viewable) && (canBeHidden || f.viewable) &&
(f.type === "text" || f.type === "number") (f.type === "text" || f.type === "number") &&
AutofillService.fieldIsFuzzyMatch(f, AutoFillConstants.TotpFieldNames)
) { ) {
totpField = f; totpField = f;
@@ -1516,7 +1510,7 @@ export default class AutofillService implements AutofillServiceInterface {
} }
static hasValue(str: string): boolean { static hasValue(str: string): boolean {
return str && str !== ""; return Boolean(str && str !== "");
} }
static setFillScriptForFocus( static setFillScriptForFocus(

View File

@@ -676,6 +676,9 @@ export default class MainBackground {
return new Promise<void>((resolve) => { return new Promise<void>((resolve) => {
setTimeout(async () => { setTimeout(async () => {
await this.environmentService.setUrlsFromStorage(); await this.environmentService.setUrlsFromStorage();
// Workaround to ignore stateService.activeAccount until URLs are set
// TODO: Remove this when implementing ticket PM-2637
this.environmentService.initialized = true;
if (!this.isPrivateMode) { if (!this.isPrivateMode) {
await this.refreshBadge(); await this.refreshBadge();
} }

View File

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

View File

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

View File

@@ -4,6 +4,7 @@ import { platformBrowserDynamic } from "@angular/platform-browser-dynamic";
import BrowserPlatformUtilsService from "../platform/services/browser-platform-utils.service"; import BrowserPlatformUtilsService from "../platform/services/browser-platform-utils.service";
require("./scss/popup.scss"); require("./scss/popup.scss");
require("./scss/tailwind.css");
import { AppModule } from "./app.module"; import { AppModule } from "./app.module";

View File

@@ -0,0 +1,5 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
@import "../../../../../libs/components/src/tw-theme.css";

View File

@@ -30,7 +30,11 @@ export class PremiumComponent extends BasePremiumComponent {
// Support old price string. Can be removed in future once all translations are properly updated. // Support old price string. Can be removed in future once all translations are properly updated.
const thePrice = this.currencyPipe.transform(this.price, "$"); const thePrice = this.currencyPipe.transform(this.price, "$");
this.priceString = i18nService.t("premiumPrice", thePrice); // Safari extension crashes due to $1 appearing in the price string ($10.00). Escape the $ to fix.
const formattedPrice = this.platformUtilsService.isSafari()
? thePrice.replace("$", "$$$")
: thePrice;
this.priceString = i18nService.t("premiumPrice", formattedPrice);
if (this.priceString.indexOf("%price%") > -1) { if (this.priceString.indexOf("%price%") > -1) {
this.priceString = this.priceString.replace("%price%", thePrice); this.priceString = this.priceString.replace("%price%", thePrice);
} }

View File

@@ -19,7 +19,11 @@
{{ "passwordGeneratorPolicyInEffect" | i18n }} {{ "passwordGeneratorPolicyInEffect" | i18n }}
</app-callout> </app-callout>
<div class="generated-block" *ngIf="type === 'password'"> <div class="generated-block" *ngIf="type === 'password'">
<div class="generated-wrapper" [innerHTML]="password | colorPassword" appSelectCopy></div> <div
class="generated-wrapper"
[innerHTML]="password | colorPassword"
[appCopyText]="password"
></div>
<div class="action-buttons"> <div class="action-buttons">
<button <button
type="button" type="button"
@@ -41,7 +45,11 @@
</div> </div>
</div> </div>
<div class="generated-block" *ngIf="type === 'username'"> <div class="generated-block" *ngIf="type === 'username'">
<div class="generated-wrapper" [innerHTML]="username | colorPassword" appSelectCopy></div> <div
class="generated-wrapper"
[innerHTML]="username | colorPassword"
[appCopyText]="username"
></div>
<div class="action-buttons" #form [appApiAction]="usernameGeneratingPromise"> <div class="action-buttons" #form [appApiAction]="usernameGeneratingPromise">
<button <button
type="button" type="button"

View File

@@ -22,7 +22,7 @@
<div class="row-main-content"> <div class="row-main-content">
<div <div
class="monospaced password-wrapper" class="monospaced password-wrapper"
appSelectCopy [appCopyText]="h.password"
[innerHTML]="h.password | colorPassword" [innerHTML]="h.password | colorPassword"
></div> ></div>
<span class="detail">{{ h.date | date : "medium" }}</span> <span class="detail">{{ h.date | date : "medium" }}</span>

View File

@@ -62,7 +62,7 @@ export class VaultSelectComponent implements OnInit, OnDestroy {
selectedVault$: Observable<string | null> = this._selectedVault.asObservable(); selectedVault$: Observable<string | null> = this._selectedVault.asObservable();
enforcePersonalOwnership = false; enforcePersonalOwnership = false;
overlayPostition: ConnectedPosition[] = [ overlayPosition: ConnectedPosition[] = [
{ {
originX: "start", originX: "start",
originY: "bottom", originY: "bottom",
@@ -149,7 +149,7 @@ export class VaultSelectComponent implements OnInit, OnDestroy {
.withPush(true) .withPush(true)
.withViewportMargin(10) .withViewportMargin(10)
.withGrowAfterOpen(true) .withGrowAfterOpen(true)
.withPositions(this.overlayPostition); .withPositions(this.overlayPosition);
this.overlayRef = this.overlay.create({ this.overlayRef = this.overlay.create({
hasBackdrop: true, hasBackdrop: true,

View File

@@ -72,7 +72,7 @@
<div <div
*ngIf="showPassword && !showPasswordCount" *ngIf="showPassword && !showPasswordCount"
class="monospaced password-wrapper" class="monospaced password-wrapper"
appSelectCopy [appCopyText]="cipher.login.password"
[innerHTML]="cipher.login.password | colorPassword" [innerHTML]="cipher.login.password | colorPassword"
></div> ></div>
<div <div

View File

@@ -118,10 +118,10 @@
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader> </resheader>
<data name="Name" xml:space="preserve"> <data name="Name" xml:space="preserve">
<value>Bitwarden Free Password Manager</value> <value>Bitwarden - Rheolydd cyfineiriau am ddim</value>
</data> </data>
<data name="Summary" xml:space="preserve"> <data name="Summary" xml:space="preserve">
<value>A secure and free password manager for all of your devices</value> <value>Rheolydd diogel a rhad ac am ddim ar gyfer eich holl ddyfeisiau</value>
</data> </data>
<data name="Description" xml:space="preserve"> <data name="Description" xml:space="preserve">
<value>Bitwarden, Inc. is the parent company of 8bit Solutions LLC. <value>Bitwarden, Inc. is the parent company of 8bit Solutions LLC.
@@ -152,7 +152,7 @@ Secure and share sensitive data within your Bitwarden Vault from any browser, mo
</value> </value>
</data> </data>
<data name="AssetTitle" xml:space="preserve"> <data name="AssetTitle" xml:space="preserve">
<value>A secure and free password manager for all of your devices</value> <value>Rheolydd diogel a rhad ac am ddim ar gyfer eich holl ddyfeisiau</value>
</data> </data>
<data name="ScreenshotSync" xml:space="preserve"> <data name="ScreenshotSync" xml:space="preserve">
<value>Sync and access your vault from multiple devices</value> <value>Sync and access your vault from multiple devices</value>
@@ -170,6 +170,6 @@ Secure and share sensitive data within your Bitwarden Vault from any browser, mo
<value>Automatically generate strong, random, and secure passwords</value> <value>Automatically generate strong, random, and secure passwords</value>
</data> </data>
<data name="ScreenshotEdit" xml:space="preserve"> <data name="ScreenshotEdit" xml:space="preserve">
<value>Your information is managed securely using AES-256 bit encryption</value> <value>Mae eich gwybodaeth yn cael ei reoli'n ddiogel gan ddefnyddio manyleb amgryptio AES 256 did</value>
</data> </data>
</root> </root>

View File

@@ -124,19 +124,19 @@
<value>Saugi ir nemokama slaptažodžių tvarkyklė visiems įrenginiams</value> <value>Saugi ir nemokama slaptažodžių tvarkyklė visiems įrenginiams</value>
</data> </data>
<data name="Description" xml:space="preserve"> <data name="Description" xml:space="preserve">
<value>Bitwarden, Inc. yra pagrindinė 8bit Solutions LLC įmonė. <value>Bitwarden, Inc. yra pagrindinė 8bit Solutions LLC įmonė.
ĮVARDINTA GERIAUSIU SLAPTAŽODŽIŲ TVARKYTOJU. ĮVARDINTA GERIAUSIU SLAPTAŽODŽIŲ TVARKYTOJU.
Tvarkykite, saugokite, saugokite ir bendrinkite neribotą skaičių slaptažodžių neribotuose įrenginiuose iš bet kurios vietos. Bitwarden teikia atvirojo kodo slaptažodžių valdymo sprendimus visiems tiek namuose, tiek darbe, tiek keliaujant. Tvarkykite, saugokite, saugokite ir bendrinkite neribotą skaičių slaptažodžių neribotuose įrenginiuose iš bet kurios vietos. Bitwarden teikia atvirojo kodo slaptažodžių valdymo sprendimus visiems tiek namuose, tiek darbe, tiek keliaujant.
Generuokite stiprius, unikalius ir atsitiktinius slaptažodžius pagal saugos reikalavimus kiekvienai lankomai svetainei. Generuokite stiprius, unikalius ir atsitiktinius slaptažodžius pagal saugos reikalavimus kiekvienai lankomai svetainei.
Bitwarden Send greitai perduoda užšifruotą informaciją failus ir paprastą tekstą tiesiogiai bet kam. Bitwarden Send greitai perduoda užšifruotą informaciją failus ir paprastą tekstą tiesiogiai bet kam.
Bitwarden siūlo komandų ir įmonių planus įmonėms, kad galėtumėte saugiai dalytis slaptažodžiais su kolegomis. Bitwarden siūlo komandų ir įmonių planus įmonėms, kad galėtumėte saugiai dalytis slaptažodžiais su kolegomis.
Kodėl verta rinktis Bitwarden: Kodėl rinktis Bitwarden:
Pasaulinės klasės šifravimas Pasaulinės klasės šifravimas
Slaptažodžiai yra apsaugoti naudojant pažangų šifravimą nuo galo iki galo (AES-256 bitai, žyma su grotelėmis ir PBKDF2 SHA-256), todėl jūsų duomenys išliks saugūs ir privatūs. Slaptažodžiai yra apsaugoti naudojant pažangų šifravimą nuo galo iki galo (AES-256 bitai, žyma su grotelėmis ir PBKDF2 SHA-256), todėl jūsų duomenys išliks saugūs ir privatūs.
@@ -145,10 +145,10 @@ Integruotas slaptažodžių generatorius
Generuokite stiprius, unikalius ir atsitiktinius slaptažodžius pagal saugos reikalavimus kiekvienai lankomai svetainei. Generuokite stiprius, unikalius ir atsitiktinius slaptažodžius pagal saugos reikalavimus kiekvienai lankomai svetainei.
Pasauliniai vertimai Pasauliniai vertimai
Bitwarden vertimai egzistuoja 40 kalbų ir mūsų pasaulinės bendruomenės dėka auga. Bitwarden vertimai egzistuoja 40 kalbose ir vis daugėja mūsų pasaulinės bendruomenės dėka.
Įvairių platformų programos Įvairių platformų programos
Apsaugokite ir bendrinkite neskelbtinus duomenis savo Bitwarden Vault iš bet kurios naršyklės, mobiliojo įrenginio ar stalinio kompiuterio OS ir kt. Apsaugokite ir bendrinkite neskelbtinus duomenis savo Bitwarden Vault iš bet kurios naršyklės, mobiliojo įrenginio ar stalinio kompiuterio OS ir kt.
</value> </value>
</data> </data>
<data name="AssetTitle" xml:space="preserve"> <data name="AssetTitle" xml:space="preserve">

View File

@@ -0,0 +1,175 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" id="root">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace"/>
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0"/>
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string"/>
<xsd:attribute name="type" type="xsd:string"/>
<xsd:attribute name="mimetype" type="xsd:string"/>
<xsd:attribute ref="xml:space"/>
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string"/>
<xsd:attribute name="name" type="xsd:string"/>
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1"/>
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2"/>
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1"/>
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3"/>
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4"/>
<xsd:attribute ref="xml:space"/>
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1"/>
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required"/>
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="Name" xml:space="preserve">
<value>Bitwarden Free Password Manager</value>
</data>
<data name="Summary" xml:space="preserve">
<value>A secure and free password manager for all of your devices</value>
</data>
<data name="Description" xml:space="preserve">
<value>Bitwarden, Inc. is the parent company of 8bit Solutions LLC.
NAMED BEST PASSWORD MANAGER BY THE VERGE, U.S. NEWS &amp; WORLD REPORT, CNET, AND MORE.
Manage, store, secure, and share unlimited passwords across unlimited devices from anywhere. Bitwarden delivers open source password management solutions to everyone, whether at home, at work, or on the go.
Generate strong, unique, and random passwords based on security requirements for every website you frequent.
Bitwarden Send quickly transmits encrypted information --- files and plaintext -- directly to anyone.
Bitwarden offers Teams and Enterprise plans for companies so you can securely share passwords with colleagues.
Why Choose Bitwarden:
World-Class Encryption
Passwords are protected with advanced end-to-end encryption (AES-256 bit, salted hashing, and PBKDF2 SHA-256) so your data stays secure and private.
Built-in Password Generator
Generate strong, unique, and random passwords based on security requirements for every website you frequent.
Global Translations
Bitwarden translations exist in 40 languages and are growing, thanks to our global community.
Cross-Platform Applications
Secure and share sensitive data within your Bitwarden Vault from any browser, mobile device, or desktop OS, and more.
</value>
</data>
<data name="AssetTitle" xml:space="preserve">
<value>A secure and free password manager for all of your devices</value>
</data>
<data name="ScreenshotSync" xml:space="preserve">
<value>Sync and access your vault from multiple devices</value>
</data>
<data name="ScreenshotVault" xml:space="preserve">
<value>Manage all your logins and passwords from a secure vault</value>
</data>
<data name="ScreenshotAutofill" xml:space="preserve">
<value>Quickly auto-fill your login credentials into any website that you visit</value>
</data>
<data name="ScreenshotMenu" xml:space="preserve">
<value>Your vault is also conveniently accessible from the right-click menu</value>
</data>
<data name="ScreenshotPassword" xml:space="preserve">
<value>Automatically generate strong, random, and secure passwords</value>
</data>
<data name="ScreenshotEdit" xml:space="preserve">
<value>Your information is managed securely using AES-256 bit encryption</value>
</data>
</root>

View File

@@ -0,0 +1,6 @@
/* eslint-disable no-undef, @typescript-eslint/no-var-requires */
const config = require("../../libs/components/tailwind.config.base");
config.content = ["./src/**/*.{html,ts}", "../../libs/components/src/**/*.{html,ts}"];
module.exports = config;

View File

@@ -10,8 +10,9 @@
"sourceMap": true, "sourceMap": true,
"baseUrl": ".", "baseUrl": ".",
"paths": { "paths": {
"@bitwarden/common/*": ["../../libs/common/src/*"],
"@bitwarden/angular/*": ["../../libs/angular/src/*"], "@bitwarden/angular/*": ["../../libs/angular/src/*"],
"@bitwarden/common/*": ["../../libs/common/src/*"],
"@bitwarden/components": ["../../libs/components/src"],
"@bitwarden/exporter/*": ["../../libs/exporter/src/*"] "@bitwarden/exporter/*": ["../../libs/exporter/src/*"]
}, },
"useDefineForClassFields": false "useDefineForClassFields": false

View File

@@ -42,6 +42,16 @@ const moduleRules = [
}, },
type: "asset/resource", type: "asset/resource",
}, },
{
test: /\.css$/,
use: [
{
loader: MiniCssExtractPlugin.loader,
},
"css-loader",
"postcss-loader",
],
},
{ {
test: /\.scss$/, test: /\.scss$/,
use: [ use: [

View File

@@ -1,7 +1,7 @@
{ {
"name": "@bitwarden/desktop", "name": "@bitwarden/desktop",
"description": "A secure and free password manager for all of your devices.", "description": "A secure and free password manager for all of your devices.",
"version": "2023.7.0", "version": "2023.7.1",
"keywords": [ "keywords": [
"bitwarden", "bitwarden",
"password", "password",

View File

@@ -0,0 +1,4 @@
/* eslint-disable no-undef */
module.exports = {
plugins: [require("tailwindcss"), require("autoprefixer"), require("postcss-nested")],
};

View File

@@ -10,6 +10,7 @@ import {
} from "@angular/core"; } from "@angular/core";
import { DomSanitizer } from "@angular/platform-browser"; import { DomSanitizer } from "@angular/platform-browser";
import { Router } from "@angular/router"; import { Router } from "@angular/router";
import { ipcRenderer } from "electron";
import { IndividualConfig, ToastrService } from "ngx-toastr"; import { IndividualConfig, ToastrService } from "ngx-toastr";
import { firstValueFrom, Subject, takeUntil } from "rxjs"; import { firstValueFrom, Subject, takeUntil } from "rxjs";
@@ -221,7 +222,7 @@ export class AppComponent implements OnInit, OnDestroy {
this.systemService.cancelProcessReload(); this.systemService.cancelProcessReload();
break; break;
case "reloadProcess": case "reloadProcess":
(window.location as any).reload(true); ipcRenderer.send("reload-process");
break; break;
case "syncStarted": case "syncStarted":
break; break;

View File

@@ -42,7 +42,7 @@
(backdropClick)="close()" (backdropClick)="close()"
(detach)="close()" (detach)="close()"
[cdkConnectedOverlayOpen]="showSwitcher && isOpen" [cdkConnectedOverlayOpen]="showSwitcher && isOpen"
[cdkConnectedOverlayPositions]="overlayPostition" [cdkConnectedOverlayPositions]="overlayPosition"
cdkConnectedOverlayMinWidth="250px" cdkConnectedOverlayMinWidth="250px"
> >
<div <div

View File

@@ -68,7 +68,7 @@ export class AccountSwitcherComponent implements OnInit, OnDestroy {
activeAccount?: ActiveAccount; activeAccount?: ActiveAccount;
serverUrl: string; serverUrl: string;
authStatus = AuthenticationStatus; authStatus = AuthenticationStatus;
overlayPostition: ConnectedPosition[] = [ overlayPosition: ConnectedPosition[] = [
{ {
originX: "end", originX: "end",
originY: "bottom", originY: "bottom",

View File

@@ -3,8 +3,8 @@ import { platformBrowserDynamic } from "@angular/platform-browser-dynamic";
import { isDev } from "../utils"; import { isDev } from "../utils";
// tslint:disable-next-line
require("../scss/styles.scss"); require("../scss/styles.scss");
require("../scss/tailwind.css");
import { AppModule } from "./app.module"; import { AppModule } from "./app.module";

View File

@@ -44,6 +44,9 @@ export class InitService {
this.nativeMessagingService.init(); this.nativeMessagingService.init();
await this.stateService.init(); await this.stateService.init();
await this.environmentService.setUrlsFromStorage(); await this.environmentService.setUrlsFromStorage();
// Workaround to ignore stateService.activeAccount until URLs are set
// TODO: Remove this when implementing ticket PM-2637
this.environmentService.initialized = true;
this.syncService.fullSync(true); this.syncService.fullSync(true);
(this.vaultTimeoutService as VaultTimeoutService).init(true); (this.vaultTimeoutService as VaultTimeoutService).init(true);
const locale = await this.stateService.getLocale(); const locale = await this.stateService.getLocale();

View File

@@ -12,7 +12,11 @@
{{ "passwordGeneratorPolicyInEffect" | i18n }} {{ "passwordGeneratorPolicyInEffect" | i18n }}
</app-callout> </app-callout>
<div class="generated-block" *ngIf="type === 'password'"> <div class="generated-block" *ngIf="type === 'password'">
<div class="generated-wrapper" [innerHTML]="password | colorPassword" appSelectCopy></div> <div
class="generated-wrapper"
[innerHTML]="password | colorPassword"
[appCopyText]="password"
></div>
<div class="action-buttons"> <div class="action-buttons">
<button <button
type="button" type="button"
@@ -35,7 +39,11 @@
</div> </div>
</div> </div>
<div class="generated-block" *ngIf="type === 'username'"> <div class="generated-block" *ngIf="type === 'username'">
<div class="generated-wrapper" [innerHTML]="username | colorPassword" appSelectCopy></div> <div
class="generated-wrapper"
[innerHTML]="username | colorPassword"
[appCopyText]="username"
></div>
<div class="action-buttons" #form [appApiAction]="usernameGeneratingPromise"> <div class="action-buttons" #form [appApiAction]="usernameGeneratingPromise">
<button <button
type="button" type="button"

View File

@@ -11,7 +11,7 @@
<div class="row-main"> <div class="row-main">
<div <div
class="password-wrapper monospaced" class="password-wrapper monospaced"
appSelectCopy [appCopyText]="h.password"
[innerHTML]="h.password | colorPassword" [innerHTML]="h.password | colorPassword"
></div> ></div>
<span class="detail">{{ h.date | date : "medium" }}</span> <span class="detail">{{ h.date | date : "medium" }}</span>

View File

@@ -2267,6 +2267,6 @@
"message": "Selghehuisves" "message": "Selghehuisves"
}, },
"accessDenied": { "accessDenied": {
"message": "Access denied. You do not have permission to view this page." "message": "Toegang geweier. U het nie toestemming om hierdie blad te sien nie."
} }
} }

View File

@@ -567,7 +567,7 @@
"message": "Kohteen tiedot" "message": "Kohteen tiedot"
}, },
"noItemsInList": { "noItemsInList": {
"message": "Ei näytettäviä kohteita." "message": "Näytettäviä kohteita ei ole."
}, },
"sendVerificationCode": { "sendVerificationCode": {
"message": "Lähetä todennuskoodi sähköpostiisi" "message": "Lähetä todennuskoodi sähköpostiisi"
@@ -2125,7 +2125,7 @@
"message": "Tunnistelauseke" "message": "Tunnistelauseke"
}, },
"needAnotherOption": { "needAnotherOption": {
"message": "Laitteella kirjautuminen on määritettävä Bitwarden-mobiilisovelluksen asetuksista. Tarvitsetko eri vaihtoehdon?" "message": "Laitteella kirjautuminen on määritettävä Bitwarden-sovelluksen asetuksista. Tarvitsetko eri vaihtoehdon?"
}, },
"viewAllLoginOptions": { "viewAllLoginOptions": {
"message": "Näytä kaikki kirjautumisvaihtoehdot" "message": "Näytä kaikki kirjautumisvaihtoehdot"

File diff suppressed because it is too large Load Diff

View File

@@ -1335,7 +1335,7 @@
"message": "Підтвердити експорт сховища" "message": "Підтвердити експорт сховища"
}, },
"exportWarningDesc": { "exportWarningDesc": {
"message": "Експортовані дані вашого сховища знаходяться в незашифрованому вигляді. Вам не слід зберігати чи надсилати їх через незахищені канали (наприклад, е-поштою). Після використання негайно видаліть їх." "message": "Ваші експортовані дані сховища незашифровані. Не зберігайте і не надсилайте їх незахищеними каналами (як-от електронна пошта). Після використання негайно видаліть їх."
}, },
"encExportKeyWarningDesc": { "encExportKeyWarningDesc": {
"message": "Цей експорт шифрує ваші дані за допомогою ключа шифрування облікового запису. Якщо ви коли-небудь оновите ключ шифрування облікового запису, необхідно виконати експорт знову, оскільки не зможете розшифрувати цей файл експорту." "message": "Цей експорт шифрує ваші дані за допомогою ключа шифрування облікового запису. Якщо ви коли-небудь оновите ключ шифрування облікового запису, необхідно виконати експорт знову, оскільки не зможете розшифрувати цей файл експорту."

View File

@@ -690,7 +690,7 @@
"message": "自定义环境" "message": "自定义环境"
}, },
"customEnvironmentFooter": { "customEnvironmentFooter": {
"message": "适用于高级用户。可以分别指定各个服务的基础 URL。" "message": "适用于高级用户。可以分别指定各个服务的基础 URL。"
}, },
"baseUrl": { "baseUrl": {
"message": "服务器 URL" "message": "服务器 URL"
@@ -2125,7 +2125,7 @@
"message": "指纹短语" "message": "指纹短语"
}, },
"needAnotherOption": { "needAnotherOption": {
"message": "必须在 Bitwarden 应用程序的设置中启用设备登录。需要其他选项吗?" "message": "设备登录必须在 Bitwarden 应用程序的设置中启用。需要其他选项吗?"
}, },
"viewAllLoginOptions": { "viewAllLoginOptions": {
"message": "查看所有登录选项" "message": "查看所有登录选项"

View File

@@ -98,6 +98,7 @@ export class Main {
this.windowMain = new WindowMain( this.windowMain = new WindowMain(
this.stateService, this.stateService,
this.logService, this.logService,
this.storageService,
(arg) => this.processDeepLink(arg), (arg) => this.processDeepLink(arg),
(win) => this.trayMain.setupWindowListeners(win) (win) => this.trayMain.setupWindowListeners(win)
); );

View File

@@ -1,11 +1,13 @@
import { once } from "node:events";
import * as path from "path"; import * as path from "path";
import * as url from "url"; import * as url from "url";
import { app, BrowserWindow, screen } from "electron"; import { app, BrowserWindow, ipcMain, nativeTheme, screen, session } from "electron";
import { WindowState } from "@bitwarden/common/models/domain/window-state"; import { WindowState } from "@bitwarden/common/models/domain/window-state";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { StateService } from "@bitwarden/common/platform/abstractions/state.service"; import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
import { AbstractStorageService } from "@bitwarden/common/platform/abstractions/storage.service";
import { cleanUserAgent, isDev, isMacAppStore, isSnapStore } from "../utils"; import { cleanUserAgent, isDev, isMacAppStore, isSnapStore } from "../utils";
@@ -19,6 +21,7 @@ export class WindowMain {
private windowStateChangeTimer: NodeJS.Timer; private windowStateChangeTimer: NodeJS.Timer;
private windowStates: { [key: string]: WindowState } = {}; private windowStates: { [key: string]: WindowState } = {};
private enableAlwaysOnTop = false; private enableAlwaysOnTop = false;
private session: Electron.Session;
readonly defaultWidth = 950; readonly defaultWidth = 950;
readonly defaultHeight = 600; readonly defaultHeight = 600;
@@ -26,11 +29,26 @@ export class WindowMain {
constructor( constructor(
private stateService: StateService, private stateService: StateService,
private logService: LogService, private logService: LogService,
private storageService: AbstractStorageService,
private argvCallback: (argv: string[]) => void = null, private argvCallback: (argv: string[]) => void = null,
private createWindowCallback: (win: BrowserWindow) => void private createWindowCallback: (win: BrowserWindow) => void
) {} ) {}
init(): Promise<any> { init(): Promise<any> {
// Perform a hard reload of the render process by crashing it. This is suboptimal but ensures that all memory gets
// cleared, as the process itself will be completely garbage collected.
ipcMain.on("reload-process", async () => {
// User might have changed theme, ensure the window is updated.
this.win.setBackgroundColor(await this.getBackgroundColor());
const crashEvent = once(this.win.webContents, "render-process-gone");
this.win.webContents.forcefullyCrashRenderer();
await crashEvent;
this.win.webContents.reloadIgnoringCache();
this.session.clearCache();
});
return new Promise<void>((resolve, reject) => { return new Promise<void>((resolve, reject) => {
try { try {
if (!isMacAppStore() && !isSnapStore()) { if (!isMacAppStore() && !isSnapStore()) {
@@ -108,6 +126,8 @@ export class WindowMain {
); );
this.enableAlwaysOnTop = await this.stateService.getEnableAlwaysOnTop(); this.enableAlwaysOnTop = await this.stateService.getEnableAlwaysOnTop();
this.session = session.fromPartition("persist:bitwarden", { cache: false });
// Create the browser window. // Create the browser window.
this.win = new BrowserWindow({ this.win = new BrowserWindow({
width: this.windowStates[mainWindowSizeKey].width, width: this.windowStates[mainWindowSizeKey].width,
@@ -120,13 +140,14 @@ export class WindowMain {
icon: process.platform === "linux" ? path.join(__dirname, "/images/icon.png") : undefined, icon: process.platform === "linux" ? path.join(__dirname, "/images/icon.png") : undefined,
titleBarStyle: process.platform === "darwin" ? "hiddenInset" : undefined, titleBarStyle: process.platform === "darwin" ? "hiddenInset" : undefined,
show: false, show: false,
backgroundColor: "#fff", backgroundColor: await this.getBackgroundColor(),
alwaysOnTop: this.enableAlwaysOnTop, alwaysOnTop: this.enableAlwaysOnTop,
webPreferences: { webPreferences: {
spellcheck: false, spellcheck: false,
nodeIntegration: true, nodeIntegration: true,
backgroundThrottling: false, backgroundThrottling: false,
contextIsolation: false, contextIsolation: false,
session: this.session,
}, },
}); });
@@ -201,6 +222,26 @@ export class WindowMain {
} }
} }
// Retrieve the background color
// Resolves background color missmatch when starting the application.
async getBackgroundColor(): Promise<string> {
const data: { theme?: string } = await this.storageService.get("global");
let theme = data?.theme;
if (theme == null || theme === "system") {
theme = nativeTheme.shouldUseDarkColors ? "dark" : "light";
}
switch (theme) {
case "light":
return "#ededed";
case "dark":
return "#222222";
case "nord":
return "#3b4252";
}
}
async toggleAlwaysOnTop() { async toggleAlwaysOnTop() {
this.enableAlwaysOnTop = !this.win.isAlwaysOnTop(); this.enableAlwaysOnTop = !this.win.isAlwaysOnTop();
this.win.setAlwaysOnTop(this.enableAlwaysOnTop); this.win.setAlwaysOnTop(this.enableAlwaysOnTop);

View File

@@ -1,12 +1,12 @@
{ {
"name": "@bitwarden/desktop", "name": "@bitwarden/desktop",
"version": "2023.7.0", "version": "2023.7.1",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "@bitwarden/desktop", "name": "@bitwarden/desktop",
"version": "2023.7.0", "version": "2023.7.1",
"license": "GPL-3.0", "license": "GPL-3.0",
"dependencies": { "dependencies": {
"@bitwarden/desktop-native": "file:../desktop_native" "@bitwarden/desktop-native": "file:../desktop_native"

View File

@@ -2,7 +2,7 @@
"name": "@bitwarden/desktop", "name": "@bitwarden/desktop",
"productName": "Bitwarden", "productName": "Bitwarden",
"description": "A secure and free password manager for all of your devices.", "description": "A secure and free password manager for all of your devices.",
"version": "2023.7.0", "version": "2023.7.1",
"author": "Bitwarden Inc. <hello@bitwarden.com> (https://bitwarden.com)", "author": "Bitwarden Inc. <hello@bitwarden.com> (https://bitwarden.com)",
"homepage": "https://bitwarden.com", "homepage": "https://bitwarden.com",
"license": "GPL-3.0", "license": "GPL-3.0",

View File

@@ -16,7 +16,10 @@ body {
body { body {
color: $text-color; color: $text-color;
background-color: $background-color-alt2;
// We initially rely on electron to provide the appropiate background color.
// This ensures the background color while reloading is correct to avoid a jarring missmatch.
background-color: transparent;
@include themify($themes) { @include themify($themes) {
color: themed("textColor"); color: themed("textColor");

View File

@@ -0,0 +1,5 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
@import "../../../../libs/components/src/tw-theme.css";

View File

@@ -28,7 +28,7 @@ $brand-primary-accent: #1252a3;
$background-color: white; $background-color: white;
$background-color-alt: #f9f9f9; $background-color-alt: #f9f9f9;
$background-color-alt2: #ededed; $background-color-alt2: #ededed; // Ensure the `window.main.ts` is updated with this value
$box-background-color: $background-color; $box-background-color: $background-color;
$box-background-hover-color: $list-item-hover; $box-background-hover-color: $list-item-hover;
@@ -50,6 +50,7 @@ $themes: (
backgroundColor: $background-color, backgroundColor: $background-color,
borderColorAlt: $border-color-alt, borderColorAlt: $border-color-alt,
backgroundColorAlt: $background-color-alt, backgroundColorAlt: $background-color-alt,
// Ensure the `window.main.ts` is updated with this value
backgroundColorAlt2: $background-color-alt2, backgroundColorAlt2: $background-color-alt2,
scrollbarColor: rgba(100, 100, 100, 0.2), scrollbarColor: rgba(100, 100, 100, 0.2),
scrollbarHoverColor: rgba(100, 100, 100, 0.4), scrollbarHoverColor: rgba(100, 100, 100, 0.4),
@@ -107,6 +108,7 @@ $themes: (
backgroundColor: #363636, backgroundColor: #363636,
borderColorAlt: #6e788a, borderColorAlt: #6e788a,
backgroundColorAlt: #3d3d3d, backgroundColorAlt: #3d3d3d,
// Ensure the `window.main.ts` is updated with this value
backgroundColorAlt2: #222222, backgroundColorAlt2: #222222,
scrollbarColor: #4d4d4d, scrollbarColor: #4d4d4d,
scrollbarHoverColor: #5f5f5f, scrollbarHoverColor: #5f5f5f,
@@ -164,6 +166,7 @@ $themes: (
backgroundColor: $nord2, backgroundColor: $nord2,
borderColorAlt: $nord5, borderColorAlt: $nord5,
backgroundColorAlt: $nord1, backgroundColorAlt: $nord1,
// Ensure the `window.main.ts` is updated with this value
backgroundColorAlt2: $nord1, backgroundColorAlt2: $nord1,
scrollbarColor: $nord4, scrollbarColor: $nord4,
scrollbarHoverColor: $nord6, scrollbarHoverColor: $nord6,

View File

@@ -52,7 +52,7 @@
<div <div
*ngIf="showPassword && !showPasswordCount" *ngIf="showPassword && !showPasswordCount"
class="monospaced password-wrapper" class="monospaced password-wrapper"
appSelectCopy [appCopyText]="cipher.login.password"
[innerHTML]="cipher.login.password | colorPassword" [innerHTML]="cipher.login.password | colorPassword"
></div> ></div>
<div <div

View File

@@ -0,0 +1,6 @@
/* eslint-disable no-undef, @typescript-eslint/no-var-requires */
const config = require("../../libs/components/tailwind.config.base");
config.content = ["./src/**/*.{html,ts}", "../../libs/components/src/**/*.{html,ts}"];
module.exports = config;

View File

@@ -10,8 +10,9 @@
"types": [], "types": [],
"baseUrl": ".", "baseUrl": ".",
"paths": { "paths": {
"@bitwarden/common/*": ["../../libs/common/src/*"],
"@bitwarden/angular/*": ["../../libs/angular/src/*"], "@bitwarden/angular/*": ["../../libs/angular/src/*"],
"@bitwarden/common/*": ["../../libs/common/src/*"],
"@bitwarden/components": ["../../libs/components/src"],
"@bitwarden/exporter/*": ["../../libs/exporter/src/*"] "@bitwarden/exporter/*": ["../../libs/exporter/src/*"]
}, },
"useDefineForClassFields": false "useDefineForClassFields": false

View File

@@ -109,6 +109,16 @@ const renderer = {
}, },
type: "asset/resource", type: "asset/resource",
}, },
{
test: /\.css$/,
use: [
{
loader: MiniCssExtractPlugin.loader,
},
"css-loader",
"postcss-loader",
],
},
{ {
test: /\.scss$/, test: /\.scss$/,
use: [ use: [

View File

@@ -4,6 +4,12 @@
"notifications": "https://notifications.bitwarden.eu", "notifications": "https://notifications.bitwarden.eu",
"scim": "https://scim.bitwarden.eu" "scim": "https://scim.bitwarden.eu"
}, },
"stripeKey": "pk_live_bpN0P37nMxrMQkcaHXtAybJk",
"braintreeKey": "production_qfbsv8kc_njj2zjtyngtjmbjd",
"paypal": {
"businessId": "4ZDA7DLUUJGMN",
"buttonAction": "https://www.paypal.com/cgi-bin/webscr"
},
"flags": { "flags": {
"secretsManager": true, "secretsManager": true,
"showPasswordless": true "showPasswordless": true

View File

@@ -1,40 +1,23 @@
<app-callout type="info" *ngIf="showKeyConnectorInfo"> <bit-callout type="info" *ngIf="showKeyConnectorInfo">
{{ "keyConnectorPolicyRestriction" | i18n }} {{ "keyConnectorPolicyRestriction" | i18n }}
</app-callout> </bit-callout>
<app-callout type="warning"> <bit-callout type="success" [title]="'prerequisite' | i18n" icon="bwi-lightbulb">
{{ "resetPasswordPolicyWarning" | i18n }} {{ "accountRecoverySingleOrgRequirementDesc" | i18n }}
</app-callout> </bit-callout>
<div class="form-group"> <bit-callout type="warning">
<div class="form-check"> {{ "accountRecoveryPolicyWarning" | i18n }}
<input </bit-callout>
class="form-check-input"
type="checkbox"
id="enabled"
[formControl]="enabled"
name="Enabled"
/>
<label class="form-check-label" for="enabled">{{ "turnOn" | i18n }}</label>
</div>
</div>
<div [formGroup]="data"> <bit-form-control class="!tw-mb-1">
<h3 class="mt-4">{{ "resetPasswordPolicyAutoEnroll" | i18n }}</h3> <input type="checkbox" bitCheckbox [formControl]="enabled" id="enabled" />
<p>{{ "resetPasswordPolicyAutoEnrollDescription" | i18n }}</p> <bit-label>{{ "turnOn" | i18n }}</bit-label>
<app-callout type="warning"> </bit-form-control>
{{ "resetPasswordPolicyAutoEnrollWarning" | i18n }}
</app-callout> <ng-container [formGroup]="data">
<div class="form-check"> <bit-form-control class="!tw-mb-1">
<input <input type="checkbox" bitCheckbox formControlName="autoEnrollEnabled" id="autoEnrollEnabled" />
class="form-check-input" <bit-label>{{ "resetPasswordPolicyAutoEnrollCheckbox" | i18n }}</bit-label>
type="checkbox" </bit-form-control>
id="autoEnrollEnabled" </ng-container>
name="AutoEnrollEnabled"
formControlName="autoEnrollEnabled"
/>
<label class="form-check-label" for="autoEnrollEnabled">
{{ "resetPasswordPolicyAutoEnrollCheckbox" | i18n }}
</label>
</div>
</div>

View File

@@ -1,5 +1,5 @@
import { Component } from "@angular/core"; import { Component } from "@angular/core";
import { UntypedFormBuilder } from "@angular/forms"; import { FormBuilder } from "@angular/forms";
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
import { PolicyType } from "@bitwarden/common/admin-console/enums"; import { PolicyType } from "@bitwarden/common/admin-console/enums";
@@ -9,7 +9,7 @@ import { BasePolicy, BasePolicyComponent } from "./base-policy.component";
export class ResetPasswordPolicy extends BasePolicy { export class ResetPasswordPolicy extends BasePolicy {
name = "accountRecoveryPolicy"; name = "accountRecoveryPolicy";
description = "accountRecoveryPolicyDescription"; description = "accountRecoveryPolicyDesc";
type = PolicyType.ResetPassword; type = PolicyType.ResetPassword;
component = ResetPasswordPolicyComponent; component = ResetPasswordPolicyComponent;
@@ -26,14 +26,9 @@ export class ResetPasswordPolicyComponent extends BasePolicyComponent {
data = this.formBuilder.group({ data = this.formBuilder.group({
autoEnrollEnabled: false, autoEnrollEnabled: false,
}); });
defaultTypes: { name: string; value: string }[];
showKeyConnectorInfo = false; showKeyConnectorInfo = false;
constructor( constructor(private formBuilder: FormBuilder, private organizationService: OrganizationService) {
private formBuilder: UntypedFormBuilder,
private organizationService: OrganizationService
) {
super(); super();
} }

View File

@@ -51,7 +51,7 @@ describe("TrialInitiationComponent", () => {
component: BlankComponent, component: BlankComponent,
}, },
{ {
path: `organizations/${testOrgId}/manage/members`, path: `organizations/${testOrgId}/members`,
component: BlankComponent, component: BlankComponent,
}, },
]), ]),
@@ -301,7 +301,7 @@ describe("TrialInitiationComponent", () => {
describe("navigateToOrgVault", () => { describe("navigateToOrgVault", () => {
it("should call verticalStepper.previous()", fakeAsync(() => { it("should call verticalStepper.previous()", fakeAsync(() => {
component.navigateToOrgInvite(); component.navigateToOrgInvite();
expect(routerSpy).toHaveBeenCalledWith(["organizations", testOrgId, "manage", "members"]); expect(routerSpy).toHaveBeenCalledWith(["organizations", testOrgId, "members"]);
})); }));
}); });
}); });

View File

@@ -227,7 +227,7 @@ export class TrialInitiationComponent implements OnInit, OnDestroy {
} }
navigateToOrgInvite() { navigateToOrgInvite() {
this.router.navigate(["organizations", this.orgId, "manage", "members"]); this.router.navigate(["organizations", this.orgId, "members"]);
} }
previousStep() { previousStep() {

View File

@@ -1,18 +1,7 @@
import { Component, EventEmitter, Input, Output } from "@angular/core"; import { Component, EventEmitter, Input, Output } from "@angular/core";
import { UntypedFormBuilder, FormGroup } from "@angular/forms"; import { FormGroup } from "@angular/forms";
import { Router } from "@angular/router";
import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.service.abstraction";
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
import { ProductType } from "@bitwarden/common/enums"; import { ProductType } from "@bitwarden/common/enums";
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.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";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction";
import { OrganizationPlansComponent } from "../../settings/organization-plans.component"; import { OrganizationPlansComponent } from "../../settings/organization-plans.component";
@@ -24,36 +13,6 @@ export class BillingComponent extends OrganizationPlansComponent {
@Input() orgInfoForm: FormGroup; @Input() orgInfoForm: FormGroup;
@Output() previousStep = new EventEmitter(); @Output() previousStep = new EventEmitter();
constructor(
apiService: ApiService,
i18nService: I18nService,
platformUtilsService: PlatformUtilsService,
cryptoService: CryptoService,
router: Router,
syncService: SyncService,
policyService: PolicyService,
organizationService: OrganizationService,
logService: LogService,
messagingService: MessagingService,
formBuilder: UntypedFormBuilder,
organizationApiService: OrganizationApiServiceAbstraction
) {
super(
apiService,
i18nService,
platformUtilsService,
cryptoService,
router,
syncService,
policyService,
organizationService,
logService,
messagingService,
formBuilder,
organizationApiService
);
}
async ngOnInit() { async ngOnInit() {
const additionalSeats = this.product == ProductType.Families ? 0 : 1; const additionalSeats = this.product == ProductType.Families ? 0 : 1;
this.formGroup.patchValue({ this.formGroup.patchValue({

View File

@@ -1,7 +1,7 @@
<form #form (ngSubmit)="submit()" [appApiAction]="formPromise" ngNativeValidate> <form #form (ngSubmit)="submit()" [appApiAction]="formPromise" ngNativeValidate>
<div> <div>
<div class="row"> <div class="row">
<div class="form-group col-6"> <div class="form-group col-8">
<label for="newSeatCount">{{ "subscriptionSeats" | i18n }}</label> <label for="newSeatCount">{{ "subscriptionSeats" | i18n }}</label>
<input <input
id="newSeatCount" id="newSeatCount"
@@ -41,7 +41,7 @@
<label for="maxAutoscaleSeats">{{ "maxSeatLimit" | i18n }}</label> <label for="maxAutoscaleSeats">{{ "maxSeatLimit" | i18n }}</label>
<input <input
id="maxAutoscaleSeats" id="maxAutoscaleSeats"
class="form-control col-6" class="form-control col-8"
type="number" type="number"
name="MaxAutoscaleSeats" name="MaxAutoscaleSeats"
[(ngModel)]="newMaxSeats" [(ngModel)]="newMaxSeats"

View File

@@ -40,7 +40,7 @@ export class AdjustSubscription {
try { try {
const seatAdjustment = this.newSeatCount - this.currentSeatCount; const seatAdjustment = this.newSeatCount - this.currentSeatCount;
const request = new OrganizationSubscriptionUpdateRequest(seatAdjustment, this.newMaxSeats); const request = new OrganizationSubscriptionUpdateRequest(seatAdjustment, this.newMaxSeats);
this.formPromise = this.organizationApiService.updateSubscription( this.formPromise = this.organizationApiService.updatePasswordManagerSeats(
this.organizationId, this.organizationId,
request request
); );

View File

@@ -12,7 +12,7 @@ import { OrganizationBillingRoutingModule } from "./organization-billing-routing
import { OrganizationBillingTabComponent } from "./organization-billing-tab.component"; import { OrganizationBillingTabComponent } from "./organization-billing-tab.component";
import { OrganizationSubscriptionCloudComponent } from "./organization-subscription-cloud.component"; import { OrganizationSubscriptionCloudComponent } from "./organization-subscription-cloud.component";
import { OrganizationSubscriptionSelfhostComponent } from "./organization-subscription-selfhost.component"; import { OrganizationSubscriptionSelfhostComponent } from "./organization-subscription-selfhost.component";
import { SecretsManagerEnrollComponent } from "./secrets-manager/enroll.component"; import { SecretsManagerBillingModule } from "./secrets-manager/sm-billing.module";
import { SubscriptionHiddenComponent } from "./subscription-hidden.component"; import { SubscriptionHiddenComponent } from "./subscription-hidden.component";
@NgModule({ @NgModule({
@@ -21,6 +21,7 @@ import { SubscriptionHiddenComponent } from "./subscription-hidden.component";
LooseComponentsModule, LooseComponentsModule,
OrganizationBillingRoutingModule, OrganizationBillingRoutingModule,
UserVerificationModule, UserVerificationModule,
SecretsManagerBillingModule,
], ],
declarations: [ declarations: [
AdjustSubscription, AdjustSubscription,
@@ -32,7 +33,6 @@ import { SubscriptionHiddenComponent } from "./subscription-hidden.component";
OrganizationSubscriptionSelfhostComponent, OrganizationSubscriptionSelfhostComponent,
OrganizationSubscriptionCloudComponent, OrganizationSubscriptionCloudComponent,
SubscriptionHiddenComponent, SubscriptionHiddenComponent,
SecretsManagerEnrollComponent,
], ],
}) })
export class OrganizationBillingModule {} export class OrganizationBillingModule {}

View File

@@ -1,5 +1,5 @@
<div class="page-header"> <div class="tw-mb-2">
<h1> <h1 bitTypography="h1">
{{ "subscription" | i18n }} {{ "subscription" | i18n }}
<small *ngIf="firstLoaded && loading"> <small *ngIf="firstLoaded && loading">
<i <i
@@ -7,7 +7,7 @@
title="{{ 'loading' | i18n }}" title="{{ 'loading' | i18n }}"
aria-hidden="true" aria-hidden="true"
></i> ></i>
<span class="sr-only">{{ "loading" | i18n }}</span> <span class="tw-sr-only">{{ "loading" | i18n }}</span>
</small> </small>
</h1> </h1>
</div> </div>
@@ -21,7 +21,7 @@
[providerName]="userOrg.providerName" [providerName]="userOrg.providerName"
></app-org-subscription-hidden> ></app-org-subscription-hidden>
<ng-container *ngIf="sub"> <ng-container *ngIf="sub && firstLoaded">
<bit-callout <bit-callout
type="warning" type="warning"
title="{{ 'canceled' | i18n }}" title="{{ 'canceled' | i18n }}"
@@ -40,92 +40,111 @@
</button> </button>
</bit-callout> </bit-callout>
<div class="row"> <dl class="tw-grid tw-grid-flow-col tw-grid-rows-2">
<div class="col-4"> <dt>{{ "billingPlan" | i18n }}</dt>
<dl> <dd>{{ sub.plan.name }}</dd>
<dt>{{ "billingPlan" | i18n }}</dt> <ng-container *ngIf="subscription">
<dd>{{ sub.plan.name }}</dd> <dt>{{ "status" | i18n }}</dt>
<ng-container *ngIf="subscription"> <dd>
<dt>{{ "status" | i18n }}</dt> <span class="tw-capitalize">{{
<dd> isSponsoredSubscription ? "sponsored" : subscription.status || "-"
<span class="text-capitalize">{{ }}</span>
isSponsoredSubscription ? "sponsored" : subscription.status || "-" <span bitBadge badgeType="warning" *ngIf="subscriptionMarkedForCancel">{{
}}</span> "pendingCancellation" | i18n
<span bitBadge badgeType="warning" *ngIf="subscriptionMarkedForCancel">{{ }}</span>
"pendingCancellation" | i18n </dd>
}}</span> <dt [ngClass]="{ 'tw-text-danger': isExpired }">
</dd> {{ "subscriptionExpiration" | i18n }}
<dt [ngClass]="{ 'tw-text-danger': isExpired }"> </dt>
{{ "subscriptionExpiration" | i18n }} <dd [ngClass]="{ 'tw-text-danger': isExpired }">
</dt> {{ nextInvoice ? (nextInvoice.date | date : "mediumDate") : "-" }}
<dd [ngClass]="{ 'tw-text-danger': isExpired }"> </dd>
{{ nextInvoice ? (nextInvoice.date | date : "mediumDate") : "-" }} </ng-container>
</dd> </dl>
</ng-container> <ng-container *ngIf="userOrg.canEditSubscription">
</dl> <div class="tw-flex-col">
</div> <strong class="tw-block tw-border-0 tw-border-b tw-border-solid tw-border-secondary-300">{{
<ng-container *ngIf="userOrg.canEditSubscription"> "details" | i18n
<div class="col-8" *ngIf="subscription"> }}</strong>
<strong class="d-block mb-1">{{ "details" | i18n }}</strong> <bit-table>
<table class="table"> <ng-template body>
<tbody> <ng-container *ngIf="subscription">
<tr *ngFor="let i of subscription.items"> <tr bitRow *ngFor="let i of lineItems">
<td> <td bitCell [ngClass]="{ 'tw-pl-20': i.addonSubscriptionItem }">
<span *ngIf="!i.addonSubscriptionItem"
>{{ productName(i.bitwardenProduct) }} -</span
>
{{ i.name }} {{ i.quantity > 1 ? "&times;" + i.quantity : "" }} @ {{ i.name }} {{ i.quantity > 1 ? "&times;" + i.quantity : "" }} @
{{ i.amount | currency : "$" }} {{ i.amount | currency : "$" }}
</td> </td>
<td>{{ i.quantity * i.amount | currency : "$" }} /{{ i.interval | i18n }}</td> <td bitCell class="tw-text-right">
{{ i.quantity * i.amount | currency : "$" }} /{{ i.interval | i18n }}
</td>
</tr> </tr>
</tbody> </ng-container>
</table> <ng-container *ngIf="userOrg.isFreeOrg">
</div> <tr bitRow *ngIf="userOrg.usePasswordManager">
</ng-container> <td bitCell>{{ "passwordManager" | i18n }} - {{ "freeOrganization" | i18n }}</td>
</div> <td bitCell class="tw-text-right">{{ "free" | i18n }}</td>
<ng-container *ngIf="userOrg.canEditSubscription"> </tr>
<button <tr bitRow *ngIf="userOrg.useSecretsManager">
bitButton <td bitCell>{{ "secretsManager" | i18n }} - {{ "freeOrganization" | i18n }}</td>
buttonType="secondary" <td bitCell class="tw-text-right">{{ "free" | i18n }}</td>
type="button" </tr>
(click)="changePlan()" </ng-container>
*ngIf="showChangePlanButton" </ng-template>
> </bit-table>
{{ "changeBillingPlan" | i18n }} </div>
</button>
<app-change-plan
[organizationId]="organizationId"
(onChanged)="closeChangePlan()"
(onCanceled)="closeChangePlan()"
*ngIf="showChangePlan"
></app-change-plan>
</ng-container> </ng-container>
<ng-container *ngIf="userOrg.canEditSubscription"> <ng-container *ngIf="userOrg.canEditSubscription">
<sm-enroll <div class="tw-mt-7">
*ngIf="isAdmin" <button
[enabled]="sub?.useSecretsManager" bitButton
[organizationId]="organizationId" buttonType="secondary"
></sm-enroll> type="button"
(click)="changePlan()"
*ngIf="showChangePlanButton"
>
{{ "changeBillingPlan" | i18n }}
</button>
<app-change-plan
[organizationId]="organizationId"
(onChanged)="closeChangePlan()"
(onCanceled)="closeChangePlan()"
*ngIf="showChangePlan"
></app-change-plan>
</div>
</ng-container>
<ng-container *ngIf="showSecretsManagerSubscribe">
<div class="tw-mt-7">
<sm-subscribe-standalone
[plan]="sub.secretsManagerPlan"
[organization]="userOrg"
(onSubscribe)="subscriptionAdjusted()"
></sm-subscribe-standalone>
</div>
</ng-container> </ng-container>
<ng-container *ngIf="userOrg.canEditSubscription"> <ng-container *ngIf="userOrg.canEditSubscription">
<h2 class="spaced-header">{{ "manageSubscription" | i18n }}</h2> <h2 bitTypography="h2" class="tw-mt-7">{{ "manageSubscription" | i18n }}</h2>
<p class="mb-4">{{ subscriptionDesc }}</p> <p bitTypography="body1">{{ subscriptionDesc }}</p>
<ng-container <ng-container
*ngIf=" *ngIf="
subscription && canAdjustSeats && !subscription.cancelled && !subscriptionMarkedForCancel subscription && canAdjustSeats && !subscription.cancelled && !subscriptionMarkedForCancel
" "
> >
<div class="mt-3"> <h3 bitTypography="h3" class="tw-mt-7">{{ "passwordManager" | i18n }}</h3>
<app-adjust-subscription <app-adjust-subscription
[seatPrice]="seatPrice" [seatPrice]="seatPrice"
[organizationId]="organizationId" [organizationId]="organizationId"
[interval]="billingInterval" [interval]="billingInterval"
[currentSeatCount]="seats" [currentSeatCount]="seats"
[maxAutoscaleSeats]="maxAutoscaleSeats" [maxAutoscaleSeats]="maxAutoscaleSeats"
(onAdjusted)="subscriptionAdjusted()" (onAdjusted)="subscriptionAdjusted()"
> >
</app-adjust-subscription> </app-adjust-subscription>
</div>
</ng-container> </ng-container>
<button <button
bitButton bitButton
@@ -136,33 +155,18 @@
> >
{{ "removeSponsorship" | i18n }} {{ "removeSponsorship" | i18n }}
</button> </button>
<h2 class="spaced-header">{{ "storage" | i18n }}</h2> <h4 bitTypography="h4" class="tw-mt-9">{{ "storage" | i18n }}</h4>
<p>{{ "subscriptionStorage" | i18n : sub.maxStorageGb || 0 : sub.storageName || "0 MB" }}</p> <p bitTypography="body1">
<div class="progress"> {{ "subscriptionStorage" | i18n : sub.maxStorageGb || 0 : sub.storageName || "0 MB" }}
<div </p>
class="progress-bar bg-success" <bit-progress [barWidth]="storagePercentage" bgColor="success"></bit-progress>
role="progressbar"
[ngStyle]="{ width: storageProgressWidth + '%' }"
[attr.aria-valuenow]="storagePercentage"
aria-valuemin="0"
aria-valuemax="100"
>
{{ storagePercentage / 100 | percent }}
</div>
</div>
<ng-container *ngIf="subscription && !subscription.cancelled && !subscriptionMarkedForCancel"> <ng-container *ngIf="subscription && !subscription.cancelled && !subscriptionMarkedForCancel">
<div class="mt-3"> <div class="tw-mt-3">
<div class="d-flex" *ngIf="!showAdjustStorage"> <div class="tw-flex tw-space-x-2" *ngIf="!showAdjustStorage">
<button bitButton buttonType="secondary" type="button" (click)="adjustStorage(true)"> <button bitButton buttonType="secondary" type="button" (click)="adjustStorage(true)">
{{ "addStorage" | i18n }} {{ "addStorage" | i18n }}
</button> </button>
<button <button bitButton buttonType="secondary" type="button" (click)="adjustStorage(false)">
bitButton
buttonType="secondary"
type="button"
class="ml-1"
(click)="adjustStorage(false)"
>
{{ "removeStorage" | i18n }} {{ "removeStorage" | i18n }}
</button> </button>
</div> </div>
@@ -177,13 +181,21 @@
></app-adjust-storage> ></app-adjust-storage>
</div> </div>
</ng-container> </ng-container>
<ng-container *ngIf="showAdjustSecretsManager">
<h3 bitTypography="h3" class="tw-mt-9">{{ "secretsManager" | i18n }}</h3>
<app-sm-adjust-subscription
[organizationId]="organizationId"
[options]="smOptions"
(onAdjusted)="subscriptionAdjusted()"
></app-sm-adjust-subscription>
</ng-container>
</ng-container> </ng-container>
<h2 class="spaced-header">{{ "selfHostingTitle" | i18n }}</h2> <h2 bitTypography="h2" class="tw-mt-7">{{ "selfHostingTitle" | i18n }}</h2>
<p class="mb-4"> <p bitTypography="body1">
{{ "selfHostingEnterpriseOrganizationSectionCopy" | i18n }} {{ "selfHostingEnterpriseOrganizationSectionCopy" | i18n }}
</p> </p>
<div class="d-flex"> <div class="tw-flex tw-space-x-2">
<button <button
bitButton bitButton
buttonType="secondary" buttonType="secondary"
@@ -198,14 +210,13 @@
bitButton bitButton
buttonType="secondary" buttonType="secondary"
type="button" type="button"
class="ml-1"
(click)="manageBillingSync()" (click)="manageBillingSync()"
*ngIf="canManageBillingSync" *ngIf="canManageBillingSync"
> >
{{ (hasBillingSyncToken ? "manageBillingSync" : "setUpBillingSync") | i18n }} {{ (hasBillingSyncToken ? "manageBillingSync" : "setUpBillingSync") | i18n }}
</button> </button>
</div> </div>
<div class="mt-3" *ngIf="showDownloadLicense"> <div class="tw-mt-3" *ngIf="showDownloadLicense">
<app-download-license <app-download-license
[organizationId]="organizationId" [organizationId]="organizationId"
(onDownloaded)="closeDownloadLicense()" (onDownloaded)="closeDownloadLicense()"
@@ -213,17 +224,16 @@
></app-download-license> ></app-download-license>
</div> </div>
<ng-container *ngIf="userOrg.canEditSubscription"> <ng-container *ngIf="userOrg.canEditSubscription">
<h2 class="spaced-header">{{ "additionalOptions" | i18n }}</h2> <h2 bitTypography="h2" class="tw-mt-7">{{ "additionalOptions" | i18n }}</h2>
<p class="mb-4"> <p bitTypography="body1">
{{ "additionalOptionsDesc" | i18n }} {{ "additionalOptionsDesc" | i18n }}
</p> </p>
<div class="d-flex"> <div class="tw-flex tw-space-x-2">
<button <button
bitButton bitButton
buttonType="danger" buttonType="danger"
[bitAction]="cancel" [bitAction]="cancel"
type="button" type="button"
class="ml-1"
*ngIf="subscription && !subscription.cancelled && !subscriptionMarkedForCancel" *ngIf="subscription && !subscription.cancelled && !subscriptionMarkedForCancel"
> >
{{ "cancelSubscription" | i18n }} {{ "cancelSubscription" | i18n }}

View File

@@ -9,8 +9,11 @@ import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-conso
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
import { OrganizationApiKeyType } from "@bitwarden/common/admin-console/enums"; import { OrganizationApiKeyType } from "@bitwarden/common/admin-console/enums";
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
import { PlanType } from "@bitwarden/common/billing/enums"; import { BitwardenProductType, PlanType } from "@bitwarden/common/billing/enums";
import { OrganizationSubscriptionResponse } from "@bitwarden/common/billing/models/response/organization-subscription.response"; import { OrganizationSubscriptionResponse } from "@bitwarden/common/billing/models/response/organization-subscription.response";
import { BillingSubscriptionItemResponse } from "@bitwarden/common/billing/models/response/subscription.response";
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
import { ConfigServiceAbstraction } from "@bitwarden/common/platform/abstractions/config/config.service.abstraction";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
@@ -19,6 +22,7 @@ import {
BillingSyncApiKeyComponent, BillingSyncApiKeyComponent,
BillingSyncApiModalData, BillingSyncApiModalData,
} from "./billing-sync-api-key.component"; } from "./billing-sync-api-key.component";
import { SecretsManagerSubscriptionOptions } from "./secrets-manager/sm-adjust-subscription.component";
@Component({ @Component({
selector: "app-org-subscription-cloud", selector: "app-org-subscription-cloud",
@@ -26,6 +30,7 @@ import {
}) })
export class OrganizationSubscriptionCloudComponent implements OnInit, OnDestroy { export class OrganizationSubscriptionCloudComponent implements OnInit, OnDestroy {
sub: OrganizationSubscriptionResponse; sub: OrganizationSubscriptionResponse;
lineItems: BillingSubscriptionItemResponse[] = [];
organizationId: string; organizationId: string;
userOrg: Organization; userOrg: Organization;
showChangePlan = false; showChangePlan = false;
@@ -33,6 +38,9 @@ export class OrganizationSubscriptionCloudComponent implements OnInit, OnDestroy
adjustStorageAdd = true; adjustStorageAdd = true;
showAdjustStorage = false; showAdjustStorage = false;
hasBillingSyncToken: boolean; hasBillingSyncToken: boolean;
showAdjustSecretsManager = false;
showSecretsManagerSubscribe = false;
firstLoaded = false; firstLoaded = false;
loading: boolean; loading: boolean;
@@ -48,7 +56,8 @@ export class OrganizationSubscriptionCloudComponent implements OnInit, OnDestroy
private organizationService: OrganizationService, private organizationService: OrganizationService,
private organizationApiService: OrganizationApiServiceAbstraction, private organizationApiService: OrganizationApiServiceAbstraction,
private route: ActivatedRoute, private route: ActivatedRoute,
private dialogService: DialogServiceAbstraction private dialogService: DialogServiceAbstraction,
private configService: ConfigServiceAbstraction
) {} ) {}
async ngOnInit() { async ngOnInit() {
@@ -68,6 +77,17 @@ export class OrganizationSubscriptionCloudComponent implements OnInit, OnDestroy
.subscribe(); .subscribe();
} }
productName(product: BitwardenProductType) {
switch (product) {
case BitwardenProductType.PasswordManager:
return this.i18nService.t("passwordManager");
case BitwardenProductType.SecretsManager:
return this.i18nService.t("secretsManager");
default:
return this.i18nService.t("passwordManager");
}
}
ngOnDestroy() { ngOnDestroy() {
this.destroy$.next(); this.destroy$.next();
this.destroy$.complete(); this.destroy$.complete();
@@ -81,6 +101,7 @@ export class OrganizationSubscriptionCloudComponent implements OnInit, OnDestroy
this.userOrg = this.organizationService.get(this.organizationId); this.userOrg = this.organizationService.get(this.organizationId);
if (this.userOrg.canViewSubscription) { if (this.userOrg.canViewSubscription) {
this.sub = await this.organizationApiService.getSubscription(this.organizationId); this.sub = await this.organizationApiService.getSubscription(this.organizationId);
this.lineItems = this.sub?.subscription?.items?.sort(sortSubscriptionItems) ?? [];
} }
const apiKeyResponse = await this.organizationApiService.getApiKeyInformation( const apiKeyResponse = await this.organizationApiService.getApiKeyInformation(
@@ -90,7 +111,28 @@ export class OrganizationSubscriptionCloudComponent implements OnInit, OnDestroy
(i) => i.keyType === OrganizationApiKeyType.BillingSync (i) => i.keyType === OrganizationApiKeyType.BillingSync
); );
this.showSecretsManagerSubscribe =
this.userOrg.canEditSubscription &&
!this.userOrg.useSecretsManager &&
!this.subscription?.cancelled &&
!this.subscriptionMarkedForCancel;
this.showAdjustSecretsManager =
this.userOrg.canEditSubscription &&
this.userOrg.useSecretsManager &&
this.subscription != null &&
this.sub.secretsManagerPlan?.hasAdditionalSeatsOption &&
!this.subscription.cancelled &&
!this.subscriptionMarkedForCancel;
this.loading = false; this.loading = false;
// Remove the remaining lines when the sm-ga-billing flag is deleted
const smBillingEnabled = await this.configService.getFeatureFlagBool(
FeatureFlag.SecretsManagerBilling
);
this.showSecretsManagerSubscribe = this.showSecretsManagerSubscribe && smBillingEnabled;
this.showAdjustSecretsManager = this.showAdjustSecretsManager && smBillingEnabled;
} }
get subscription() { get subscription() {
@@ -138,6 +180,20 @@ export class OrganizationSubscriptionCloudComponent implements OnInit, OnDestroy
return this.sub.seats; return this.sub.seats;
} }
get smOptions(): SecretsManagerSubscriptionOptions {
return {
seatCount: this.sub.smSeats,
maxAutoscaleSeats: this.sub.maxAutoscaleSmSeats,
seatPrice: this.sub.secretsManagerPlan.seatPrice,
maxAutoscaleServiceAccounts: this.sub.maxAutoscaleSmServiceAccounts,
additionalServiceAccounts:
this.sub.smServiceAccounts - this.sub.secretsManagerPlan.baseServiceAccount,
interval: this.sub.secretsManagerPlan.isAnnual ? "year" : "month",
additionalServiceAccountPrice: this.sub.secretsManagerPlan.additionalPricePerServiceAccount,
baseServiceAccountCount: this.sub.secretsManagerPlan.baseServiceAccount,
};
}
get maxAutoscaleSeats() { get maxAutoscaleSeats() {
return this.sub.maxAutoscaleSeats; return this.sub.maxAutoscaleSeats;
} }
@@ -332,3 +388,23 @@ export class OrganizationSubscriptionCloudComponent implements OnInit, OnDestroy
return this.subscription == null && this.sub.planType === PlanType.Free && !this.showChangePlan; return this.subscription == null && this.sub.planType === PlanType.Free && !this.showChangePlan;
} }
} }
/**
* Helper to sort subscription items by product type and then by addon status
*/
function sortSubscriptionItems(
a: BillingSubscriptionItemResponse,
b: BillingSubscriptionItemResponse
) {
if (a.bitwardenProduct == b.bitwardenProduct) {
if (a.addonSubscriptionItem == b.addonSubscriptionItem) {
return 0;
}
// sort addon items to the bottom
if (a.addonSubscriptionItem) {
return 1;
}
return -1;
}
return a.bitwardenProduct - b.bitwardenProduct;
}

View File

@@ -1,13 +0,0 @@
<form *ngIf="showSecretsManager" [formGroup]="formGroup" [bitSubmit]="submit">
<h2 class="spaced-header">{{ "secretsManagerBeta" | i18n }}</h2>
<p>{{ "secretsManagerSubscriptionDesc" | i18n }}</p>
<bit-form-control>
<input type="checkbox" bitCheckbox formControlName="enabled" />
<bit-label>{{ "secretsManagerEnable" | i18n }}</bit-label>
</bit-form-control>
<button bitButton bitFormButton buttonType="primary" type="submit">
{{ "save" | i18n }}
</button>
</form>

View File

@@ -1,52 +0,0 @@
import { Component, Input, OnInit } from "@angular/core";
import { FormBuilder } from "@angular/forms";
import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.service.abstraction";
import { OrganizationEnrollSecretsManagerRequest } from "@bitwarden/common/admin-console/models/request/organization/organization-enroll-secrets-manager.request";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction";
import { flagEnabled } from "../../../../utils/flags";
@Component({
selector: "sm-enroll",
templateUrl: "enroll.component.html",
})
export class SecretsManagerEnrollComponent implements OnInit {
@Input() enabled: boolean;
@Input() organizationId: string;
protected formGroup = this.formBuilder.group({
enabled: [false],
});
protected showSecretsManager = false;
constructor(
private formBuilder: FormBuilder,
private organizationApiService: OrganizationApiServiceAbstraction,
private platformUtilsService: PlatformUtilsService,
private i18nService: I18nService,
private syncService: SyncService
) {
this.showSecretsManager = flagEnabled("secretsManager");
}
ngOnInit(): void {
this.formGroup.setValue({
enabled: this.enabled,
});
}
protected submit = async () => {
this.formGroup.markAllAsTouched();
const request = new OrganizationEnrollSecretsManagerRequest();
request.enabled = this.formGroup.value.enabled;
await this.organizationApiService.updateEnrollSecretsManager(this.organizationId, request);
await this.syncService.fullSync(true);
this.platformUtilsService.showToast("success", null, this.i18nService.t("subscriptionUpdated"));
};
}

View File

@@ -0,0 +1,3 @@
export * from "./sm-billing.module";
export * from "./sm-subscribe.component";
export * from "./sm-subscribe-standalone.component";

View File

@@ -0,0 +1,93 @@
<form [formGroup]="formGroup" [bitSubmit]="submit">
<bit-form-field class="tw-w-2/3">
<bit-label>{{ "subscriptionSeats" | i18n }}</bit-label>
<input bitInput id="smSeatCount" formControlName="seatCount" type="number" step="1" min="1" />
<bit-hint>
<strong>{{ "total" | i18n }}:</strong>
{{ formGroup.value.seatCount || 0 }} &times; {{ options.seatPrice | currency : "$" }} =
{{ seatTotal | currency : "$" }} / {{ options.interval | i18n }}
</bit-hint>
</bit-form-field>
<bit-form-control>
<bit-label>{{ "limitSubscription" | i18n }}</bit-label>
<input type="checkbox" bitCheckbox id="limitSmSeats" formControlName="limitSeats" />
<bit-hint>
{{ "limitSmSubscriptionDesc" | i18n }}
</bit-hint>
</bit-form-control>
<bit-form-field class="tw-w-2/3" *ngIf="formGroup.value.limitSeats">
<bit-label>{{ "maxSeatLimit" | i18n }}</bit-label>
<input
bitInput
id="smSeatLimit"
formControlName="maxAutoscaleSeats"
type="number"
step="1"
[min]="formGroup.value.seatCount"
/>
<bit-hint>
<strong>{{ "maxSeatCost" | i18n }}:</strong>
{{ formGroup.value.maxAutoscaleSeats || 0 }} &times;
{{ options.seatPrice | currency : "$" }} = {{ maxSeatTotal | currency : "$" }} /
{{ options.interval | i18n }}
</bit-hint>
</bit-form-field>
<bit-form-field class="tw-w-2/3">
<bit-label>{{ "additionalServiceAccounts" | i18n }}</bit-label>
<input
bitInput
id="additionalServiceAccountCount"
formControlName="additionalServiceAccounts"
type="number"
step="1"
min="0"
/>
<bit-hint>
<div>
{{
"additionalServiceAccountsDesc"
| i18n : options.baseServiceAccountCount : (monthlyServiceAccountPrice | currency : "$")
}}
</div>
<div>
<strong>{{ "total" | i18n }}:</strong>
{{ formGroup.value.additionalServiceAccounts || 0 }} &times;
{{ options.additionalServiceAccountPrice | currency : "$" }} =
{{ serviceAccountTotal | currency : "$" }} / {{ options.interval | i18n }}
</div>
</bit-hint>
</bit-form-field>
<bit-form-control>
<bit-label>{{ "limitServiceAccounts" | i18n }}</bit-label>
<input
type="checkbox"
bitCheckbox
id="limitServiceAccounts"
formControlName="limitServiceAccounts"
/>
<bit-hint>
{{ "limitServiceAccountsDesc" | i18n }}
</bit-hint>
</bit-form-control>
<bit-form-field class="tw-w-2/3" *ngIf="formGroup.value.limitServiceAccounts">
<bit-label>{{ "serviceAccountLimit" | i18n }}</bit-label>
<input
bitInput
id="additionalServiceAccountLimit"
formControlName="maxAutoscaleServiceAccounts"
type="number"
step="1"
[min]="formGroup.value.additionalServiceAccounts"
/>
<bit-hint>
<strong>{{ "maxServiceAccountCost" | i18n }}:</strong>
{{ formGroup.value.maxAutoscaleServiceAccounts || 0 }} &times;
{{ options.additionalServiceAccountPrice | currency : "$" }} =
{{ maxServiceAccountTotal | currency : "$" }} / {{ options.interval | i18n }}
</bit-hint>
</bit-form-field>
<button type="submit" bitButton buttonType="primary" bitFormButton>
{{ "save" | i18n }}
</button>
<bit-error-summary [formGroup]="formGroup" class="tw-mt-2"></bit-error-summary>
</form>

View File

@@ -0,0 +1,172 @@
import { Component, EventEmitter, Input, OnDestroy, OnInit, Output } from "@angular/core";
import { FormBuilder, Validators } from "@angular/forms";
import { Subject, takeUntil } from "rxjs";
import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.service.abstraction";
import { OrganizationSmSubscriptionUpdateRequest } from "@bitwarden/common/billing/models/request/organization-sm-subscription-update.request";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
export interface SecretsManagerSubscriptionOptions {
interval: "year" | "month";
/**
* The current number of seats the organization subscribes to.
*/
seatCount: number;
/**
* Optional auto-scaling limit for the number of seats the organization can subscribe to.
*/
maxAutoscaleSeats: number;
/**
* The price per seat for the subscription.
*/
seatPrice: number;
/**
* The number of service accounts that are included in the base subscription.
*/
baseServiceAccountCount: number;
/**
* The current number of additional service accounts the organization subscribes to.
*/
additionalServiceAccounts: number;
/**
* Optional auto-scaling limit for the number of additional service accounts the organization can subscribe to.
*/
maxAutoscaleServiceAccounts: number;
/**
* The price per additional service account for the subscription.
*/
additionalServiceAccountPrice: number;
}
@Component({
selector: "app-sm-adjust-subscription",
templateUrl: "sm-adjust-subscription.component.html",
})
export class SecretsManagerAdjustSubscriptionComponent implements OnInit, OnDestroy {
@Input() organizationId: string;
@Input() options: SecretsManagerSubscriptionOptions;
@Output() onAdjusted = new EventEmitter();
private destroy$ = new Subject<void>();
formGroup = this.formBuilder.group({
seatCount: [0, [Validators.required, Validators.min(1)]],
limitSeats: [false],
maxAutoscaleSeats: [null as number | null],
additionalServiceAccounts: [0, [Validators.required, Validators.min(0)]],
limitServiceAccounts: [false],
maxAutoscaleServiceAccounts: [null as number | null],
});
get monthlyServiceAccountPrice(): number {
return this.options.interval == "month"
? this.options.additionalServiceAccountPrice
: this.options.additionalServiceAccountPrice / 12;
}
get serviceAccountTotal(): number {
return Math.abs(
this.formGroup.value.additionalServiceAccounts * this.options.additionalServiceAccountPrice
);
}
get seatTotal(): number {
return Math.abs(this.formGroup.value.seatCount * this.options.seatPrice);
}
get maxServiceAccountTotal(): number {
return Math.abs(
(this.formGroup.value.maxAutoscaleServiceAccounts ?? 0) *
this.options.additionalServiceAccountPrice
);
}
get maxSeatTotal(): number {
return Math.abs((this.formGroup.value.maxAutoscaleSeats ?? 0) * this.options.seatPrice);
}
constructor(
private formBuilder: FormBuilder,
private organizationApiService: OrganizationApiServiceAbstraction,
private i18nService: I18nService,
private platformUtilsService: PlatformUtilsService
) {}
ngOnInit() {
this.formGroup.valueChanges.pipe(takeUntil(this.destroy$)).subscribe((value) => {
const maxAutoscaleSeatsControl = this.formGroup.controls.maxAutoscaleSeats;
const maxAutoscaleServiceAccountsControl =
this.formGroup.controls.maxAutoscaleServiceAccounts;
if (value.limitSeats) {
maxAutoscaleSeatsControl.setValidators([Validators.min(value.seatCount)]);
maxAutoscaleSeatsControl.enable({ emitEvent: false });
} else {
maxAutoscaleSeatsControl.disable({ emitEvent: false });
}
if (value.limitServiceAccounts) {
maxAutoscaleServiceAccountsControl.setValidators([
Validators.min(value.additionalServiceAccounts),
]);
maxAutoscaleServiceAccountsControl.enable({ emitEvent: false });
} else {
maxAutoscaleServiceAccountsControl.disable({ emitEvent: false });
}
});
this.formGroup.patchValue({
seatCount: this.options.seatCount,
maxAutoscaleSeats: this.options.maxAutoscaleSeats,
additionalServiceAccounts: this.options.additionalServiceAccounts,
maxAutoscaleServiceAccounts: this.options.maxAutoscaleServiceAccounts,
limitSeats: this.options.maxAutoscaleSeats != null,
limitServiceAccounts: this.options.maxAutoscaleServiceAccounts != null,
});
}
submit = async () => {
this.formGroup.markAllAsTouched();
if (this.formGroup.invalid) {
return;
}
const request = new OrganizationSmSubscriptionUpdateRequest();
request.seatAdjustment = this.formGroup.value.seatCount - this.options.seatCount;
request.serviceAccountAdjustment =
this.formGroup.value.additionalServiceAccounts - this.options.additionalServiceAccounts;
request.maxAutoscaleSeats = this.formGroup.value.limitSeats
? this.formGroup.value.maxAutoscaleSeats
: null;
request.maxAutoscaleServiceAccounts = this.formGroup.value.limitServiceAccounts
? this.formGroup.value.maxAutoscaleServiceAccounts
: null;
await this.organizationApiService.updateSecretsManagerSubscription(
this.organizationId,
request
);
await this.platformUtilsService.showToast(
"success",
null,
this.i18nService.t("subscriptionUpdated")
);
this.onAdjusted.emit();
};
ngOnDestroy() {
this.destroy$.next();
this.destroy$.complete();
}
}

View File

@@ -0,0 +1,22 @@
import { NgModule } from "@angular/core";
import { SharedModule } from "../../../shared";
import { SecretsManagerAdjustSubscriptionComponent } from "./sm-adjust-subscription.component";
import { SecretsManagerSubscribeStandaloneComponent } from "./sm-subscribe-standalone.component";
import { SecretsManagerSubscribeComponent } from "./sm-subscribe.component";
@NgModule({
imports: [SharedModule],
declarations: [
SecretsManagerSubscribeComponent,
SecretsManagerSubscribeStandaloneComponent,
SecretsManagerAdjustSubscriptionComponent,
],
exports: [
SecretsManagerSubscribeComponent,
SecretsManagerSubscribeStandaloneComponent,
SecretsManagerAdjustSubscriptionComponent,
],
})
export class SecretsManagerBillingModule {}

View File

@@ -0,0 +1,8 @@
<form [formGroup]="formGroup" [bitSubmit]="submit" *ngIf="plan">
<sm-subscribe
[formGroup]="formGroup"
[selectedPlan]="plan"
[upgradeOrganization]="false"
[showSubmitButton]="true"
></sm-subscribe>
</form>

View File

@@ -0,0 +1,46 @@
import { Component, EventEmitter, Input, Output } from "@angular/core";
import { FormBuilder } from "@angular/forms";
import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.service.abstraction";
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
import { SecretsManagerSubscribeRequest } from "@bitwarden/common/billing/models/request/sm-subscribe.request";
import { PlanResponse } from "@bitwarden/common/billing/models/response/plan.response";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { secretsManagerSubscribeFormFactory } from "./sm-subscribe.component";
@Component({
selector: "sm-subscribe-standalone",
templateUrl: "sm-subscribe-standalone.component.html",
})
export class SecretsManagerSubscribeStandaloneComponent {
@Input() plan: PlanResponse;
@Input() organization: Organization;
@Output() onSubscribe = new EventEmitter<void>();
formGroup = secretsManagerSubscribeFormFactory(this.formBuilder);
constructor(
private formBuilder: FormBuilder,
private platformUtilsService: PlatformUtilsService,
private i18nService: I18nService,
private organizationApiService: OrganizationApiServiceAbstraction
) {}
submit = async () => {
const request = new SecretsManagerSubscribeRequest();
request.additionalSmSeats = this.plan.hasAdditionalSeatsOption
? this.formGroup.value.userSeats
: 0;
request.additionalServiceAccounts = this.plan.hasAdditionalServiceAccountOption
? this.formGroup.value.additionalServiceAccounts
: 0;
await this.organizationApiService.subscribeToSecretsManager(this.organization.id, request);
this.platformUtilsService.showToast("success", null, this.i18nService.t("subscriptionUpdated"));
this.onSubscribe.emit();
};
}

View File

@@ -0,0 +1,71 @@
<div *ngIf="formGroup && selectedPlan != null" [formGroup]="formGroup">
<h3 bitTypography="h3">{{ "moreFromBitwarden" | i18n }}</h3>
<div class="tw-rounded-t tw-bg-background-alt3 tw-p-5">
<div class="tw-w-72">
<bit-icon [icon]="logo"></bit-icon>
</div>
</div>
<div
class="tw-rounded-b tw-border-x tw-border-b tw-border-t-0 tw-border-solid tw-border-secondary-300 tw-p-5"
>
<h4 bitTypography="h4">{{ "secretsManagerForPlan" | i18n : planName }}</h4>
<div class="tw-text-muted">
{{ "secretsManagerForPlanDesc" | i18n }}
<ul>
<li *ngIf="product == productTypes.Free">{{ "limitedUsers" | i18n : maxUsers }}</li>
<li>{{ "unlimitedSecrets" | i18n }}</li>
<li *ngIf="product == productTypes.Free; else unlimitedProjects">
{{ "projectsIncluded" | i18n : maxProjects }}
</li>
<ng-template #unlimitedProjects>
<li>{{ "unlimitedProjects" | i18n }}</li>
</ng-template>
<li>{{ "serviceAccountsIncluded" | i18n : serviceAccountsIncluded }}</li>
<li *ngIf="product != productTypes.Free">
{{
"additionalServiceAccountCost" | i18n : (monthlyCostPerServiceAccount | currency : "$")
}}
</li>
</ul>
</div>
<div class="tw-mb-5">
<span *ngIf="product != productTypes.Free; else freeForever">
{{ "costPerUser" | i18n : (monthlyCostPerUser | currency : "$") }} /{{ "month" | i18n }}
</span>
<ng-template #freeForever>
<span>{{ "freeForever" | i18n }}</span>
</ng-template>
</div>
<bit-form-control>
<input type="checkbox" bitCheckbox formControlName="enabled" />
<bit-label>{{ "addSecretsManager" | i18n }}</bit-label>
<bit-hint *ngIf="upgradeOrganization">{{ "addSecretsManagerUpgradeDesc" | i18n }}</bit-hint>
</bit-form-control>
<ng-container *ngIf="formGroup.value.enabled">
<div *ngIf="selectedPlan.hasAdditionalSeatsOption" class="tw-w-1/2">
<bit-form-field>
<bit-label>{{ "userSeats" | i18n }}</bit-label>
<input bitInput formControlName="userSeats" type="number" />
<bit-hint>{{ "userSeatsHowManyDesc" | i18n }}</bit-hint>
</bit-form-field>
</div>
<div *ngIf="selectedPlan.hasAdditionalServiceAccountOption" class="tw-w-1/2">
<bit-form-field>
<bit-label>{{ "additionalServiceAccounts" | i18n }}</bit-label>
<input bitInput formControlName="additionalServiceAccounts" type="number" />
<bit-hint>{{
"additionalServiceAccountsDesc"
| i18n : serviceAccountsIncluded : (monthlyCostPerServiceAccount | currency : "$")
}}</bit-hint>
</bit-form-field>
</div>
<button *ngIf="showSubmitButton" type="submit" bitButton buttonType="primary" bitFormButton>
{{ "save" | i18n }}
</button>
</ng-container>
</div>
</div>

View File

@@ -0,0 +1,104 @@
import { Component, Input, OnDestroy, OnInit } from "@angular/core";
import { FormBuilder, FormGroup, Validators } from "@angular/forms";
import { Subject, startWith, takeUntil } from "rxjs";
import { ControlsOf } from "@bitwarden/angular/types/controls-of";
import { PlanResponse } from "@bitwarden/common/billing/models/response/plan.response";
import { ProductType } from "@bitwarden/common/enums";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { SecretsManagerLogo } from "../../../../../../../bitwarden_license/bit-web/src/app/secrets-manager/layout/secrets-manager-logo";
export interface SecretsManagerSubscription {
enabled: boolean;
userSeats: number;
additionalServiceAccounts: number;
}
export const secretsManagerSubscribeFormFactory = (
formBuilder: FormBuilder
): FormGroup<ControlsOf<SecretsManagerSubscription>> =>
formBuilder.group({
enabled: [false],
userSeats: [1, [Validators.required, Validators.min(1), Validators.max(100000)]],
additionalServiceAccounts: [
0,
[Validators.required, Validators.min(0), Validators.max(100000)],
],
});
@Component({
selector: "sm-subscribe",
templateUrl: "sm-subscribe.component.html",
})
export class SecretsManagerSubscribeComponent implements OnInit, OnDestroy {
@Input() formGroup: FormGroup<ControlsOf<SecretsManagerSubscription>>;
@Input() upgradeOrganization: boolean;
@Input() showSubmitButton = false;
@Input() selectedPlan: PlanResponse;
logo = SecretsManagerLogo;
productTypes = ProductType;
private destroy$ = new Subject<void>();
constructor(private i18nService: I18nService) {}
ngOnInit() {
this.formGroup.controls.enabled.valueChanges
.pipe(startWith(this.formGroup.value.enabled), takeUntil(this.destroy$))
.subscribe((enabled) => {
if (enabled) {
this.formGroup.controls.userSeats.enable();
this.formGroup.controls.additionalServiceAccounts.enable();
} else {
this.formGroup.controls.userSeats.disable();
this.formGroup.controls.additionalServiceAccounts.disable();
}
});
}
ngOnDestroy(): void {
this.destroy$.next();
this.destroy$.complete();
}
get product() {
return this.selectedPlan.product;
}
get planName() {
switch (this.product) {
case ProductType.Free:
return this.i18nService.t("free2PersonOrganization");
case ProductType.Teams:
return this.i18nService.t("planNameTeams");
case ProductType.Enterprise:
return this.i18nService.t("planNameEnterprise");
}
}
get serviceAccountsIncluded() {
return this.selectedPlan.baseServiceAccount;
}
get monthlyCostPerServiceAccount() {
return this.selectedPlan.isAnnual
? this.selectedPlan.additionalPricePerServiceAccount / 12
: this.selectedPlan.additionalPricePerServiceAccount;
}
get maxUsers() {
return this.selectedPlan.maxUsers;
}
get maxProjects() {
return this.selectedPlan.maxProjects;
}
get monthlyCostPerUser() {
return this.selectedPlan.isAnnual
? this.selectedPlan.seatPrice / 12
: this.selectedPlan.seatPrice;
}
}

View File

@@ -28,7 +28,7 @@
(ngSubmit)="submit()" (ngSubmit)="submit()"
[appApiAction]="formPromise" [appApiAction]="formPromise"
ngNativeValidate ngNativeValidate
*ngIf="!loading && !selfHosted && this.plans" *ngIf="!loading && !selfHosted && this.passwordManagerPlans && this.secretsManagerPlans"
class="tw-pt-6" class="tw-pt-6"
> >
<app-org-info <app-org-info
@@ -119,7 +119,7 @@
<span *ngIf="selectableProduct.product == productTypes.Free">{{ "freeForever" | i18n }}</span> <span *ngIf="selectableProduct.product == productTypes.Free">{{ "freeForever" | i18n }}</span>
</label> </label>
</div> </div>
<div *ngIf="formGroup.controls['product'].value !== productTypes.Free"> <div *ngIf="formGroup.value.product !== productTypes.Free">
<ng-container *ngIf="selectedPlan.hasAdditionalSeatsOption && !selectedPlan.baseSeats"> <ng-container *ngIf="selectedPlan.hasAdditionalSeatsOption && !selectedPlan.baseSeats">
<h2 class="mt-5">{{ "users" | i18n }}</h2> <h2 class="mt-5">{{ "users" | i18n }}</h2>
<div class="row"> <div class="row">
@@ -230,7 +230,8 @@
<span *ngIf="!selectablePlan.baseSeats">{{ "users" | i18n }}:</span> <span *ngIf="!selectablePlan.baseSeats">{{ "users" | i18n }}:</span>
{{ formGroup.controls["additionalSeats"].value || 0 }} &times; {{ formGroup.controls["additionalSeats"].value || 0 }} &times;
{{ selectablePlan.seatPrice / 12 | currency : "$" }} &times; 12 {{ selectablePlan.seatPrice / 12 | currency : "$" }} &times; 12
{{ "monthAbbr" | i18n }} = {{ seatTotal(selectablePlan) | currency : "$" }} /{{ {{ "monthAbbr" | i18n }} =
{{ seatTotal(selectablePlan, formGroup.value.additionalSeats) | currency : "$" }} /{{
"year" | i18n "year" | i18n
}} }}
</small> </small>
@@ -256,7 +257,9 @@
<span *ngIf="!selectablePlan.baseSeats">{{ "users" | i18n }}:</span> <span *ngIf="!selectablePlan.baseSeats">{{ "users" | i18n }}:</span>
{{ formGroup.controls["additionalSeats"].value || 0 }} &times; {{ formGroup.controls["additionalSeats"].value || 0 }} &times;
{{ selectablePlan.seatPrice | currency : "$" }} {{ "monthAbbr" | i18n }} = {{ selectablePlan.seatPrice | currency : "$" }} {{ "monthAbbr" | i18n }} =
{{ seatTotal(selectablePlan) | currency : "$" }} /{{ "month" | i18n }} {{ seatTotal(selectablePlan, formGroup.value.additionalSeats) | currency : "$" }} /{{
"month" | i18n
}}
</small> </small>
<small *ngIf="selectablePlan.hasAdditionalStorageOption"> <small *ngIf="selectablePlan.hasAdditionalStorageOption">
{{ "additionalStorageGb" | i18n }}: {{ "additionalStorageGb" | i18n }}:
@@ -268,8 +271,21 @@
</ng-container> </ng-container>
</label> </label>
</div> </div>
<hr class="my-3" /> </div>
<h2 class="spaced-header mb-4">
<!-- Secrets Manager -->
<div class="tw-my-10">
<sm-subscribe
*ngIf="showSecretsManagerSubscribe && planOffersSecretsManager"
[formGroup]="formGroup.controls.secretsManager"
[selectedPlan]="selectedSecretsManagerPlan"
[upgradeOrganization]="!createOrganization"
></sm-subscribe>
</div>
<!-- Payment info -->
<div *ngIf="formGroup.value.product !== productTypes.Free">
<h2 class="mb-4">
{{ (createOrganization ? "paymentInformation" : "billingInformation") | i18n }} {{ (createOrganization ? "paymentInformation" : "billingInformation") | i18n }}
</h2> </h2>
<small class="text-muted font-italic mb-3 d-block"> <small class="text-muted font-italic mb-3 d-block">
@@ -279,8 +295,12 @@
<app-tax-info (onCountryChanged)="changedCountry()"></app-tax-info> <app-tax-info (onCountryChanged)="changedCountry()"></app-tax-info>
<div id="price" class="my-4"> <div id="price" class="my-4">
<div class="text-muted text-sm"> <div class="text-muted text-sm">
{{ "planPrice" | i18n }}: {{ subtotal | currency : "USD $" }} {{ "passwordManagerPlanPrice" | i18n }}: {{ passwordManagerSubtotal | currency : "USD $" }}
<br /> <br />
<span *ngIf="planOffersSecretsManager && formGroup.value.secretsManager.enabled">
{{ "secretsManagerPlanPrice" | i18n }}: {{ secretsManagerSubtotal | currency : "USD $" }}
<br />
</span>
<ng-container> <ng-container>
{{ "estimatedTax" | i18n }}: {{ taxCharges | currency : "USD $" }} {{ "estimatedTax" | i18n }}: {{ taxCharges | currency : "USD $" }}
</ng-container> </ng-container>

View File

@@ -7,7 +7,7 @@ import {
Output, Output,
ViewChild, ViewChild,
} from "@angular/core"; } from "@angular/core";
import { UntypedFormBuilder, Validators } from "@angular/forms"; import { FormBuilder, Validators } from "@angular/forms";
import { Router } from "@angular/router"; import { Router } from "@angular/router";
import { Subject, takeUntil } from "rxjs"; import { Subject, takeUntil } from "rxjs";
@@ -20,9 +20,11 @@ import { OrganizationCreateRequest } from "@bitwarden/common/admin-console/model
import { OrganizationKeysRequest } from "@bitwarden/common/admin-console/models/request/organization-keys.request"; import { OrganizationKeysRequest } from "@bitwarden/common/admin-console/models/request/organization-keys.request";
import { OrganizationUpgradeRequest } from "@bitwarden/common/admin-console/models/request/organization-upgrade.request"; import { OrganizationUpgradeRequest } from "@bitwarden/common/admin-console/models/request/organization-upgrade.request";
import { ProviderOrganizationCreateRequest } from "@bitwarden/common/admin-console/models/request/provider/provider-organization-create.request"; import { ProviderOrganizationCreateRequest } from "@bitwarden/common/admin-console/models/request/provider/provider-organization-create.request";
import { PaymentMethodType, PlanType } from "@bitwarden/common/billing/enums"; import { BitwardenProductType, PaymentMethodType, PlanType } from "@bitwarden/common/billing/enums";
import { PlanResponse } from "@bitwarden/common/billing/models/response/plan.response"; import { PlanResponse } from "@bitwarden/common/billing/models/response/plan.response";
import { ProductType } from "@bitwarden/common/enums"; import { ProductType } from "@bitwarden/common/enums";
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
import { ConfigServiceAbstraction } from "@bitwarden/common/platform/abstractions/config/config.service.abstraction";
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service"; import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
@@ -32,6 +34,8 @@ import { EncString } from "@bitwarden/common/platform/models/domain/enc-string";
import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key"; import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key";
import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction"; import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction";
import { secretsManagerSubscribeFormFactory } from "../organizations/secrets-manager/sm-subscribe.component";
import { PaymentComponent } from "./payment.component"; import { PaymentComponent } from "./payment.component";
import { TaxInfoComponent } from "./tax-info.component"; import { TaxInfoComponent } from "./tax-info.component";
@@ -51,24 +55,29 @@ export class OrganizationPlansComponent implements OnInit, OnDestroy {
@Input() showFree = true; @Input() showFree = true;
@Input() showCancel = false; @Input() showCancel = false;
@Input() acceptingSponsorship = false; @Input() acceptingSponsorship = false;
@Input() @Input()
get product(): ProductType { get product(): ProductType {
return this._product; return this._product;
} }
set product(product: ProductType) { set product(product: ProductType) {
this._product = product; this._product = product;
this.formGroup?.controls?.product?.setValue(product); this.formGroup?.controls?.product?.setValue(product);
} }
private _product = ProductType.Free; private _product = ProductType.Free;
@Input() @Input()
get plan(): PlanType { get plan(): PlanType {
return this._plan; return this._plan;
} }
set plan(plan: PlanType) { set plan(plan: PlanType) {
this._plan = plan; this._plan = plan;
this.formGroup?.controls?.plan?.setValue(plan); this.formGroup?.controls?.plan?.setValue(plan);
} }
private _plan = PlanType.Free; private _plan = PlanType.Free;
@Input() providerId?: string; @Input() providerId?: string;
@Output() onSuccess = new EventEmitter<OnSuccessArgs>(); @Output() onSuccess = new EventEmitter<OnSuccessArgs>();
@@ -82,6 +91,9 @@ export class OrganizationPlansComponent implements OnInit, OnDestroy {
singleOrgPolicyAppliesToActiveUser = false; singleOrgPolicyAppliesToActiveUser = false;
isInTrialFlow = false; isInTrialFlow = false;
discount = 0; discount = 0;
showSecretsManagerSubscribe: boolean;
secretsManagerSubscription = secretsManagerSubscribeFormFactory(this.formBuilder);
formGroup = this.formBuilder.group({ formGroup = this.formBuilder.group({
name: [""], name: [""],
@@ -94,9 +106,11 @@ export class OrganizationPlansComponent implements OnInit, OnDestroy {
businessName: [""], businessName: [""],
plan: [this.plan], plan: [this.plan],
product: [this.product], product: [this.product],
secretsManager: this.secretsManagerSubscription,
}); });
plans: PlanResponse[]; passwordManagerPlans: PlanResponse[];
secretsManagerPlans: PlanResponse[];
private destroy$ = new Subject<void>(); private destroy$ = new Subject<void>();
@@ -111,8 +125,9 @@ export class OrganizationPlansComponent implements OnInit, OnDestroy {
private organizationService: OrganizationService, private organizationService: OrganizationService,
private logService: LogService, private logService: LogService,
private messagingService: MessagingService, private messagingService: MessagingService,
private formBuilder: UntypedFormBuilder, private formBuilder: FormBuilder,
private organizationApiService: OrganizationApiServiceAbstraction private organizationApiService: OrganizationApiServiceAbstraction,
private configService: ConfigServiceAbstraction
) { ) {
this.selfHosted = platformUtilsService.isSelfHost(); this.selfHosted = platformUtilsService.isSelfHost();
} }
@@ -120,7 +135,13 @@ export class OrganizationPlansComponent implements OnInit, OnDestroy {
async ngOnInit() { async ngOnInit() {
if (!this.selfHosted) { if (!this.selfHosted) {
const plans = await this.apiService.getPlans(); const plans = await this.apiService.getPlans();
this.plans = plans.data; this.passwordManagerPlans = plans.data.filter(
(plan) => plan.bitwardenProduct === BitwardenProductType.PasswordManager
);
this.secretsManagerPlans = plans.data.filter(
(plan) => plan.bitwardenProduct === BitwardenProductType.SecretsManager
);
if (this.product === ProductType.Enterprise || this.product === ProductType.Teams) { if (this.product === ProductType.Enterprise || this.product === ProductType.Teams) {
this.formGroup.controls.businessOwned.setValue(true); this.formGroup.controls.businessOwned.setValue(true);
} }
@@ -131,12 +152,9 @@ export class OrganizationPlansComponent implements OnInit, OnDestroy {
this.changedOwnedBusiness(); this.changedOwnedBusiness();
} }
if (!this.createOrganization || this.acceptingSponsorship) { if (!this.createOrganization) {
this.formGroup.controls.product.setValue(ProductType.Families); this.upgradeFlowPrefillForm();
this.changedProduct(); } else {
}
if (this.createOrganization) {
this.formGroup.controls.name.addValidators([Validators.required, Validators.maxLength(50)]); this.formGroup.controls.name.addValidators([Validators.required, Validators.maxLength(50)]);
this.formGroup.controls.billingEmail.addValidators(Validators.required); this.formGroup.controls.billingEmail.addValidators(Validators.required);
} }
@@ -148,6 +166,11 @@ export class OrganizationPlansComponent implements OnInit, OnDestroy {
this.singleOrgPolicyAppliesToActiveUser = policyAppliesToActiveUser; this.singleOrgPolicyAppliesToActiveUser = policyAppliesToActiveUser;
}); });
this.showSecretsManagerSubscribe = await this.configService.getFeatureFlagBool(
FeatureFlag.SecretsManagerBilling,
false
);
this.loading = false; this.loading = false;
} }
@@ -165,7 +188,15 @@ export class OrganizationPlansComponent implements OnInit, OnDestroy {
} }
get selectedPlan() { get selectedPlan() {
return this.plans.find((plan) => plan.type === this.formGroup.controls.plan.value); return this.passwordManagerPlans.find(
(plan) => plan.type === this.formGroup.controls.plan.value
);
}
get selectedSecretsManagerPlan() {
return this.secretsManagerPlans.find(
(plan) => plan.type === this.formGroup.controls.plan.value
);
} }
get selectedPlanInterval() { get selectedPlanInterval() {
@@ -173,7 +204,7 @@ export class OrganizationPlansComponent implements OnInit, OnDestroy {
} }
get selectableProducts() { get selectableProducts() {
let validPlans = this.plans.filter((plan) => plan.type !== PlanType.Custom); let validPlans = this.passwordManagerPlans.filter((plan) => plan.type !== PlanType.Custom);
if (this.formGroup.controls.businessOwned.value) { if (this.formGroup.controls.businessOwned.value) {
validPlans = validPlans.filter((plan) => plan.canBeUsedByBusiness); validPlans = validPlans.filter((plan) => plan.canBeUsedByBusiness);
@@ -191,7 +222,9 @@ export class OrganizationPlansComponent implements OnInit, OnDestroy {
); );
if (this.acceptingSponsorship) { if (this.acceptingSponsorship) {
const familyPlan = this.plans.find((plan) => plan.type === PlanType.FamiliesAnnually); const familyPlan = this.passwordManagerPlans.find(
(plan) => plan.type === PlanType.FamiliesAnnually
);
this.discount = familyPlan.basePrice; this.discount = familyPlan.basePrice;
validPlans = [familyPlan]; validPlans = [familyPlan];
} }
@@ -200,7 +233,7 @@ export class OrganizationPlansComponent implements OnInit, OnDestroy {
} }
get selectablePlans() { get selectablePlans() {
return this.plans?.filter( return this.passwordManagerPlans?.filter(
(plan) => (plan) =>
!plan.legacyYear && !plan.disabled && plan.product === this.formGroup.controls.product.value !plan.legacyYear && !plan.disabled && plan.product === this.formGroup.controls.product.value
); );
@@ -231,21 +264,32 @@ export class OrganizationPlansComponent implements OnInit, OnDestroy {
); );
} }
seatTotal(plan: PlanResponse): number { seatTotal(plan: PlanResponse, seats: number): number {
if (!plan.hasAdditionalSeatsOption) { if (!plan.hasAdditionalSeatsOption) {
return 0; return 0;
} }
return plan.seatPrice * Math.abs(this.formGroup.controls.additionalSeats.value || 0); return plan.seatPrice * Math.abs(seats || 0);
} }
get subtotal() { additionalServiceAccountTotal(plan: PlanResponse): number {
if (!plan.hasAdditionalServiceAccountOption) {
return 0;
}
return (
plan.additionalPricePerServiceAccount *
Math.abs(this.secretsManagerForm.value.additionalServiceAccounts || 0)
);
}
get passwordManagerSubtotal() {
let subTotal = this.selectedPlan.basePrice; let subTotal = this.selectedPlan.basePrice;
if ( if (
this.selectedPlan.hasAdditionalSeatsOption && this.selectedPlan.hasAdditionalSeatsOption &&
this.formGroup.controls.additionalSeats.value this.formGroup.controls.additionalSeats.value
) { ) {
subTotal += this.seatTotal(this.selectedPlan); subTotal += this.seatTotal(this.selectedPlan, this.formGroup.value.additionalSeats);
} }
if ( if (
this.selectedPlan.hasAdditionalStorageOption && this.selectedPlan.hasAdditionalStorageOption &&
@@ -262,18 +306,39 @@ export class OrganizationPlansComponent implements OnInit, OnDestroy {
return subTotal - this.discount; return subTotal - this.discount;
} }
get secretsManagerSubtotal() {
const plan = this.selectedSecretsManagerPlan;
const formValues = this.secretsManagerForm.value;
if (!this.planOffersSecretsManager || !formValues.enabled) {
return 0;
}
let subTotal = plan.basePrice;
if (plan.hasAdditionalSeatsOption && formValues.userSeats) {
subTotal += this.seatTotal(plan, formValues.userSeats);
}
if (plan.hasAdditionalStorageOption && formValues.additionalServiceAccounts) {
subTotal += this.additionalServiceAccountTotal(this.selectedPlan);
}
return subTotal;
}
get freeTrial() { get freeTrial() {
return this.selectedPlan.trialPeriodDays != null; return this.selectedPlan.trialPeriodDays != null;
} }
get taxCharges() { get taxCharges() {
return this.taxComponent != null && this.taxComponent.taxRate != null return this.taxComponent != null && this.taxComponent.taxRate != null
? (this.taxComponent.taxRate / 100) * this.subtotal ? (this.taxComponent.taxRate / 100) *
(this.passwordManagerSubtotal + this.secretsManagerSubtotal)
: 0; : 0;
} }
get total() { get total() {
return this.subtotal + this.taxCharges || 0; return this.passwordManagerSubtotal + this.secretsManagerSubtotal + this.taxCharges || 0;
} }
get paymentDesc() { get paymentDesc() {
@@ -286,6 +351,14 @@ export class OrganizationPlansComponent implements OnInit, OnDestroy {
} }
} }
get secretsManagerForm() {
return this.formGroup.controls.secretsManager;
}
get planOffersSecretsManager() {
return this.selectedSecretsManagerPlan != null;
}
changedProduct() { changedProduct() {
this.formGroup.controls.plan.setValue(this.selectablePlans[0].type); this.formGroup.controls.plan.setValue(this.selectablePlans[0].type);
if (!this.selectedPlan.hasPremiumAccessOption) { if (!this.selectedPlan.hasPremiumAccessOption) {
@@ -303,6 +376,14 @@ export class OrganizationPlansComponent implements OnInit, OnDestroy {
) { ) {
this.formGroup.controls.additionalSeats.setValue(1); this.formGroup.controls.additionalSeats.setValue(1);
} }
if (this.planOffersSecretsManager) {
this.secretsManagerForm.enable();
} else {
this.secretsManagerForm.disable();
}
this.secretsManagerForm.updateValueAndValidity();
} }
changedOwnedBusiness() { changedOwnedBusiness() {
@@ -407,6 +488,9 @@ export class OrganizationPlansComponent implements OnInit, OnDestroy {
request.billingAddressCountry = this.taxComponent.taxInfo.country; request.billingAddressCountry = this.taxComponent.taxInfo.country;
request.billingAddressPostalCode = this.taxComponent.taxInfo.postalCode; request.billingAddressPostalCode = this.taxComponent.taxInfo.postalCode;
// Secrets Manager
this.buildSecretsManagerRequest(request);
// Retrieve org info to backfill pub/priv key if necessary // Retrieve org info to backfill pub/priv key if necessary
const org = await this.organizationService.get(this.organizationId); const org = await this.organizationService.get(this.organizationId);
if (!org.hasPublicAndPrivateKeys) { if (!org.hasPublicAndPrivateKeys) {
@@ -462,6 +546,9 @@ export class OrganizationPlansComponent implements OnInit, OnDestroy {
} }
} }
// Secrets Manager
this.buildSecretsManagerRequest(request);
if (this.providerId) { if (this.providerId) {
const providerRequest = new ProviderOrganizationCreateRequest( const providerRequest = new ProviderOrganizationCreateRequest(
this.formGroup.controls.clientOwnerEmail.value, this.formGroup.controls.clientOwnerEmail.value,
@@ -517,4 +604,40 @@ export class OrganizationPlansComponent implements OnInit, OnDestroy {
return text; return text;
} }
private buildSecretsManagerRequest(
request: OrganizationCreateRequest | OrganizationUpgradeRequest
): void {
const formValues = this.secretsManagerForm.value;
request.useSecretsManager = this.planOffersSecretsManager && formValues.enabled;
if (!request.useSecretsManager) {
return;
}
if (this.selectedSecretsManagerPlan.hasAdditionalSeatsOption) {
request.additionalSmSeats = formValues.userSeats;
}
if (this.selectedSecretsManagerPlan.hasAdditionalServiceAccountOption) {
request.additionalServiceAccounts = formValues.additionalServiceAccounts;
}
}
private upgradeFlowPrefillForm() {
if (this.acceptingSponsorship) {
this.formGroup.controls.product.setValue(ProductType.Families);
this.changedProduct();
return;
}
// If they already have SM enabled, bump them up to Teams and enable SM to maintain this access
const organization = this.organizationService.get(this.organizationId);
if (organization.useSecretsManager) {
this.formGroup.controls.product.setValue(ProductType.Teams);
this.secretsManagerForm.controls.enabled.setValue(true);
this.changedProduct();
}
}
} }

View File

@@ -38,14 +38,13 @@ export class InitService {
init() { init() {
return async () => { return async () => {
// Workaround to ignore stateService.activeAccount until process.env.URLS are set
// TODO: Remove this when implementing ticket PM-2637
this.environmentService.initialized = false;
await this.stateService.init(); await this.stateService.init();
const urls = process.env.URLS as Urls; const urls = process.env.URLS as Urls;
urls.base ??= this.win.location.origin; urls.base ??= this.win.location.origin;
this.environmentService.setUrls(urls); await this.environmentService.setUrls(urls);
// Workaround to ignore stateService.activeAccount until process.env.URLS are set
// TODO: Remove this when implementing ticket PM-2637
this.environmentService.initialized = true; this.environmentService.initialized = true;
setTimeout(() => this.notificationsService.init(), 3000); setTimeout(() => this.notificationsService.init(), 3000);

View File

@@ -51,6 +51,7 @@ import { UpdatePasswordComponent } from "../auth/update-password.component";
import { UpdateTempPasswordComponent } from "../auth/update-temp-password.component"; import { UpdateTempPasswordComponent } from "../auth/update-temp-password.component";
import { VerifyEmailTokenComponent } from "../auth/verify-email-token.component"; import { VerifyEmailTokenComponent } from "../auth/verify-email-token.component";
import { VerifyRecoverDeleteComponent } from "../auth/verify-recover-delete.component"; import { VerifyRecoverDeleteComponent } from "../auth/verify-recover-delete.component";
import { SecretsManagerBillingModule } from "../billing/organizations/secrets-manager/sm-billing.module";
import { AddCreditComponent } from "../billing/settings/add-credit.component"; import { AddCreditComponent } from "../billing/settings/add-credit.component";
import { AdjustPaymentComponent } from "../billing/settings/adjust-payment.component"; import { AdjustPaymentComponent } from "../billing/settings/adjust-payment.component";
import { BillingHistoryViewComponent } from "../billing/settings/billing-history-view.component"; import { BillingHistoryViewComponent } from "../billing/settings/billing-history-view.component";
@@ -125,6 +126,9 @@ import { SharedModule } from "./shared.module";
DynamicAvatarComponent, DynamicAvatarComponent,
EnvironmentSelectorModule, EnvironmentSelectorModule,
AccountFingerprintComponent, AccountFingerprintComponent,
// To be removed when OrganizationPlansComponent is moved to its own module (see AC-1453)
SecretsManagerBillingModule,
], ],
declarations: [ declarations: [
AcceptEmergencyComponent, AcceptEmergencyComponent,

View File

@@ -23,6 +23,7 @@ import {
LinkModule, LinkModule,
MenuModule, MenuModule,
MultiSelectModule, MultiSelectModule,
ProgressModule,
RadioButtonModule, RadioButtonModule,
SelectModule, SelectModule,
TableModule, TableModule,
@@ -69,6 +70,7 @@ import "./locales";
LinkModule, LinkModule,
MenuModule, MenuModule,
MultiSelectModule, MultiSelectModule,
ProgressModule,
RadioButtonModule, RadioButtonModule,
TableModule, TableModule,
TabsModule, TabsModule,
@@ -103,6 +105,7 @@ import "./locales";
LinkModule, LinkModule,
MenuModule, MenuModule,
MultiSelectModule, MultiSelectModule,
ProgressModule,
RadioButtonModule, RadioButtonModule,
SelectModule, SelectModule,
TableModule, TableModule,

View File

@@ -8,7 +8,7 @@
<div class="card-body"> <div class="card-body">
<bit-color-password <bit-color-password
[password]="type === 'password' ? password : username" [password]="type === 'password' ? password : username"
appSelectCopy [appCopyText]="type === 'password' ? password : username"
></bit-color-password> ></bit-color-password>
</div> </div>
</div> </div>

View File

@@ -10,7 +10,7 @@
<bit-color-password <bit-color-password
[password]="h.password" [password]="h.password"
class="tw-block tw-font-mono" class="tw-block tw-font-mono"
appSelectCopy [appCopyText]="h.password"
></bit-color-password> ></bit-color-password>
<small bitTypography="body2" class="tw-text-muted"> <small bitTypography="body2" class="tw-text-muted">
{{ h.date | date : "medium" }} {{ h.date | date : "medium" }}

File diff suppressed because it is too large Load Diff

View File

@@ -618,8 +618,8 @@
"loginWithDevice": { "loginWithDevice": {
"message": "تسجيل الدخول باستخدام جهاز" "message": "تسجيل الدخول باستخدام جهاز"
}, },
"loginWithDeviceEnabledInfo": { "loginWithDeviceEnabledNote": {
"message": "تسجيل الدخول باستخدام الجهاز يجب أن يكون مفعلاً في إعدادات تطبيق بيتواردن على هاتفك. هل تحتاج إلى خِيار آخر؟" "message": "Log in with device must be set up in the settings of the Bitwarden app. Need another option?"
}, },
"loginWithMasterPassword": { "loginWithMasterPassword": {
"message": "تسجيل الدخول باستخدام كلمة المرور الرئيسية" "message": "تسجيل الدخول باستخدام كلمة المرور الرئيسية"
@@ -4594,21 +4594,18 @@
"accountRecoveryPolicy": { "accountRecoveryPolicy": {
"message": "Account recovery administration" "message": "Account recovery administration"
}, },
"accountRecoveryPolicyDescription": { "accountRecoveryPolicyDesc": {
"message": "Recover member accounts when master password or trusted devices are forgotten or lost. The recovery processes is based on the account encryption method." "message": "Based on the encryption method, recover accounts when master passwords or trusted devices are forgotten or lost."
}, },
"resetPasswordPolicyWarning": { "accountRecoveryPolicyWarning": {
"message": "Members in the organization will need to self-enroll or be auto-enrolled before administrators can reset their master password." "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."
}, },
"resetPasswordPolicyAutoEnroll": { "resetPasswordPolicyAutoEnroll": {
"message": "Automatic enrollment" "message": "Automatic enrollment"
}, },
"resetPasswordPolicyAutoEnrollDescription": {
"message": "All members will be automatically enrolled in password reset once their invite is accepted and will not be allowed to withdraw."
},
"resetPasswordPolicyAutoEnrollWarning": {
"message": "Members already in the organization will not be retroactively enrolled in password reset. They will need to self-enroll before administrators can reset their master password."
},
"resetPasswordPolicyAutoEnrollCheckbox": { "resetPasswordPolicyAutoEnrollCheckbox": {
"message": "Require new members to be enrolled automatically" "message": "Require new members to be enrolled automatically"
}, },
@@ -5224,8 +5221,8 @@
"message": "Use the", "message": "Use the",
"description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Use the require single-sign-on authentication policy to require all members to log in with SSO.'" "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Use the require single-sign-on authentication policy to require all members to log in with SSO.'"
}, },
"ssoPolicyHelpLink": { "ssoPolicyHelpAnchor": {
"message": "require single-sign-on authentication policy", "message": "require single sign-on authentication policy",
"description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Use the require single-sign-on authentication policy to require all members to log in with SSO.'" "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Use the require single-sign-on authentication policy to require all members to log in with SSO.'"
}, },
"ssoPolicyHelpEnd": { "ssoPolicyHelpEnd": {
@@ -6831,17 +6828,25 @@
"trustedDeviceEncryption": { "trustedDeviceEncryption": {
"message": "Trusted device encryption" "message": "Trusted device encryption"
}, },
"memberDecryptionTdeDescriptionStart": { "memberDecryptionTdeDescriptionPartOne": {
"message": "Once authenticated, members will decrypt vault data using a key stored on their device. The", "message": "Once authenticated, members will decrypt vault data using a key stored on their device. The",
"description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Once authenticated, members will decrypt vault data using a key stored on their device. The account recovery administration policy with automatic enrollment will turn on when this option is used.'" "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Once authenticated, members will decrypt vault data using a key stored on their device. The single organization policy and account recovery administration policy with automatic enrollment will turn on when this option is used.'"
}, },
"memberDecryptionTdeDescriptionLink": { "memberDecryptionTdeDescriptionLinkOne": {
"message": "account recovery administration policy", "message": "single organization",
"description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Once authenticated, members will decrypt vault data using a key stored on their device. The account recovery administration policy with automatic enrollment will turn on when this option is used.'" "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Once authenticated, members will decrypt vault data using a key stored on their device. The single organization policy and account recovery administration policy with automatic enrollment will turn on when this option is used.'"
}, },
"memberDecryptionTdeDescriptionEnd": { "memberDecryptionTdeDescriptionPartTwo": {
"message": "with automatic enrollment will turn on when this option is used.", "message": "policy and ",
"description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Once authenticated, members will decrypt vault data using a key stored on their device. The master password reset policy with automatic enrollment will turn on when this option is used.'" "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Once authenticated, members will decrypt vault data using a key stored on their device. The single organization policy and account recovery administration policy with automatic enrollment will turn on when this option is used.'"
},
"memberDecryptionTdeDescriptionLinkTwo": {
"message": "account recovery administration",
"description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Once authenticated, members will decrypt vault data using a key stored on their device. The single organization policy and account recovery administration policy with automatic enrollment will turn on when this option is used.'"
},
"memberDecryptionTdeDescriptionPartThree": {
"message": "policy with automatic enrollment will turn on when this option is used.",
"description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Once authenticated, members will decrypt vault data using a key stored on their device. The single organization policy and account recovery administration policy with automatic enrollment will turn on when this option is used.'"
}, },
"notFound": { "notFound": {
"message": "$RESOURCE$ not found", "message": "$RESOURCE$ not found",

View File

@@ -618,8 +618,8 @@
"loginWithDevice": { "loginWithDevice": {
"message": "Cihazla giriş et" "message": "Cihazla giriş et"
}, },
"loginWithDeviceEnabledInfo": { "loginWithDeviceEnabledNote": {
"message": "Cihazla giriş etmə, Bitwarden mobil tətbiqinin tənzimləmələrində fəallaşdırılmalıdır. Başqa bir seçimə ehtiyacınız var?" "message": "Cihazla giriş etmə, Bitwarden tətbiqinin tənzimləmələrində quraşdırılmalıdır. Başqa bir seçimə ehtiyacınız var?"
}, },
"loginWithMasterPassword": { "loginWithMasterPassword": {
"message": "Ana parolla giriş et" "message": "Ana parolla giriş et"
@@ -4594,21 +4594,18 @@
"accountRecoveryPolicy": { "accountRecoveryPolicy": {
"message": "Hesab bərpa idarəsi" "message": "Hesab bərpa idarəsi"
}, },
"accountRecoveryPolicyDescription": { "accountRecoveryPolicyDesc": {
"message": "Ana parol və ya güvənilən cihazlar unudulanda və ya itəndə üzv hesablarını bərpa edin. Bərpa prosesləri, hesab şifrələmə üsuluna əsaslanır." "message": "Şifrələmə üsuluna əsasən, ana parollar və güvənilən cihazlar unudulduqda və ya itirildikdə hesabları bərpa edin."
}, },
"resetPasswordPolicyWarning": { "accountRecoveryPolicyWarning": {
"message": "Administratorların ana parolları sıfırlaya bilməsi üçün təşkilatdakı istifadəçilər öz-özünə və ya avtomatik olaraq qeydiyyatdan keçməlidir." "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."
}, },
"resetPasswordPolicyAutoEnroll": { "resetPasswordPolicyAutoEnroll": {
"message": "Avtomatik qeydiyyat" "message": "Avtomatik qeydiyyat"
}, },
"resetPasswordPolicyAutoEnrollDescription": {
"message": "Dəvətləri qəbul edən kimi bütün istifadəçilər avtomatik olaraq \"parol sıfırlama\"da qeydiyyata alınacaq."
},
"resetPasswordPolicyAutoEnrollWarning": {
"message": "Hal-hazırda təşkilatdakı istifadəçilər, \"parol sıfırlama\"da retroaktiv (geriyə yönələn) olaraq qeydiyyata alınmayacaq. Administratorlar ana parollarını sıfırlaya bilməsi üçün öz-özünə qeydiyyatdan keçməlidirilər."
},
"resetPasswordPolicyAutoEnrollCheckbox": { "resetPasswordPolicyAutoEnrollCheckbox": {
"message": "Yeni istifadəçiləri avtomatik qeydiyyata al" "message": "Yeni istifadəçiləri avtomatik qeydiyyata al"
}, },
@@ -5224,8 +5221,8 @@
"message": "Bütün üzvlərin SSO ilə", "message": "Bütün üzvlərin SSO ilə",
"description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Use the require single-sign-on authentication policy to require all members to log in with SSO.'" "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Use the require single-sign-on authentication policy to require all members to log in with SSO.'"
}, },
"ssoPolicyHelpLink": { "ssoPolicyHelpAnchor": {
"message": "giriş etməsini məcburi etmək üçün", "message": "require single sign-on authentication policy",
"description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Use the require single-sign-on authentication policy to require all members to log in with SSO.'" "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Use the require single-sign-on authentication policy to require all members to log in with SSO.'"
}, },
"ssoPolicyHelpEnd": { "ssoPolicyHelpEnd": {
@@ -6627,7 +6624,7 @@
"message": "Secrets Manager" "message": "Secrets Manager"
}, },
"secretsManagerAccessDescription": { "secretsManagerAccessDescription": {
"message": "Activate user access to Secrets Manager." "message": "\"Secrets Manager\"ə istifadəçi müraciətini aktivləşdir."
}, },
"userAccessSecretsManagerGA": { "userAccessSecretsManagerGA": {
"message": "This user can access Secrets Manager" "message": "This user can access Secrets Manager"
@@ -6831,17 +6828,25 @@
"trustedDeviceEncryption": { "trustedDeviceEncryption": {
"message": "Güvənli cihaz şifrələməsi" "message": "Güvənli cihaz şifrələməsi"
}, },
"memberDecryptionTdeDescriptionStart": { "memberDecryptionTdeDescriptionPartOne": {
"message": "Kimlik təsdiqləndikdən sonra üzvlər, cihazlarından saxlanılan açarı istifadə edərək anbar verilənlərinin şifrələrini aça biləcək.", "message": "Kimlik təsdiqləndikdən sonra üzvlər, cihazlarından saxlanılan açarı istifadə edərək anbar verilənlərinin şifrələrini aça biləcək",
"description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Once authenticated, members will decrypt vault data using a key stored on their device. The account recovery administration policy with automatic enrollment will turn on when this option is used.'" "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Once authenticated, members will decrypt vault data using a key stored on their device. The single organization policy and account recovery administration policy with automatic enrollment will turn on when this option is used.'"
}, },
"memberDecryptionTdeDescriptionLink": { "memberDecryptionTdeDescriptionLinkOne": {
"message": "Hesab bərpa idarəsi siyasəti", "message": "tək təşkilat",
"description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Once authenticated, members will decrypt vault data using a key stored on their device. The account recovery administration policy with automatic enrollment will turn on when this option is used.'" "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Once authenticated, members will decrypt vault data using a key stored on their device. The single organization policy and account recovery administration policy with automatic enrollment will turn on when this option is used.'"
}, },
"memberDecryptionTdeDescriptionEnd": { "memberDecryptionTdeDescriptionPartTwo": {
"message": "Bu seçim istifadə edildikdə avto-qeydiyyat ilə işə salınacaq.", "message": "siyasət və ",
"description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Once authenticated, members will decrypt vault data using a key stored on their device. The master password reset policy with automatic enrollment will turn on when this option is used.'" "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Once authenticated, members will decrypt vault data using a key stored on their device. The single organization policy and account recovery administration policy with automatic enrollment will turn on when this option is used.'"
},
"memberDecryptionTdeDescriptionLinkTwo": {
"message": "hesab bərpa administrasiyası",
"description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Once authenticated, members will decrypt vault data using a key stored on their device. The single organization policy and account recovery administration policy with automatic enrollment will turn on when this option is used.'"
},
"memberDecryptionTdeDescriptionPartThree": {
"message": "bu seçim istifadə edildikdə avto-qeydiyyat ilə işə salınacaq.",
"description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Once authenticated, members will decrypt vault data using a key stored on their device. The single organization policy and account recovery administration policy with automatic enrollment will turn on when this option is used.'"
}, },
"notFound": { "notFound": {
"message": "$RESOURCE$ tapılmadı", "message": "$RESOURCE$ tapılmadı",
@@ -6859,58 +6864,58 @@
"message": "İstifadəçi, hesab bərpası vasitəsilə verilən bir parolu güncəllədi." "message": "İstifadəçi, hesab bərpası vasitəsilə verilən bir parolu güncəllədi."
}, },
"activatedAccessToSecretsManager": { "activatedAccessToSecretsManager": {
"message": "Activated access to Secrets Manager", "message": "\"Secrets Manager\"ə müraciət aktivləşdirildi",
"description": "Confirmation message that one or more users gained access to Secrets Manager" "description": "Confirmation message that one or more users gained access to Secrets Manager"
}, },
"activateAccess": { "activateAccess": {
"message": "Activate access" "message": "Müraciəti aktivləşdir"
}, },
"bulkEnableSecretsManagerDescription": { "bulkEnableSecretsManagerDescription": {
"message": "Grant the following members access to Secrets Manager. The role granted in the Password Manager will apply to Secrets Manager.", "message": "Grant the following members access to Secrets Manager. The role granted in the Password Manager will apply to Secrets Manager.",
"description": "This description is shown to an admin when they are attempting to add more users to Secrets Manager." "description": "This description is shown to an admin when they are attempting to add more users to Secrets Manager."
}, },
"activateSecretsManager": { "activateSecretsManager": {
"message": "Activate Secrets Manager" "message": "\"Secrets Manager\"i aktivləşdir"
}, },
"yourOrganizationsFingerprint": { "yourOrganizationsFingerprint": {
"message": "Təşkilatınızın barmaq izi ifadəsi", "message": "Təşkilatınızın barmaq izi ifadəsi",
"description": "A 'fingerprint phrase' is a unique word phrase (similar to a passphrase) that a user can use to authenticate their organization's public key with another user, for the purposes of sharing." "description": "A 'fingerprint phrase' is a unique word phrase (similar to a passphrase) that a user can use to authenticate their organization's public key with another user, for the purposes of sharing."
}, },
"deviceApprovals": { "deviceApprovals": {
"message": "Device approvals" "message": "Cihaz təsdiqləri"
}, },
"deviceApprovalsDesc": { "deviceApprovalsDesc": {
"message": "Approve login requests below to allow the requesting member to finish logging in. Unapproved requests expire after 1 week. Verify the members information before approving." "message": "Approve login requests below to allow the requesting member to finish logging in. Unapproved requests expire after 1 week. Verify the members information before approving."
}, },
"deviceInfo": { "deviceInfo": {
"message": "Device info" "message": "Cihaz məlumatı"
}, },
"time": { "time": {
"message": "Time" "message": "Vaxt"
}, },
"denyAllRequests": { "denyAllRequests": {
"message": "Deny all requests" "message": "Bütün tələbləri rədd et"
}, },
"denyRequest": { "denyRequest": {
"message": "Deny request" "message": "Tələbi rədd et"
}, },
"approveRequest": { "approveRequest": {
"message": "Approve request" "message": "Tələbi təsdiqlə"
}, },
"noDeviceRequests": { "noDeviceRequests": {
"message": "No device requests" "message": "Heç bir cihaz tələbi yoxdur"
}, },
"noDeviceRequestsDesc": { "noDeviceRequestsDesc": {
"message": "Member device approval requests will appear here" "message": "Üzv cihaz təsdiq tələbləri burada görünəcək"
}, },
"loginRequestDenied": { "loginRequestDenied": {
"message": "Login request denied" "message": "Giriş tələbi rədd edildi"
}, },
"allLoginRequestsDenied": { "allLoginRequestsDenied": {
"message": "All login requests denied" "message": "Bütün giriş tələbləri rədd edildi"
}, },
"loginRequestApproved": { "loginRequestApproved": {
"message": "Login request approved" "message": "Giriş tələbi təsdiqləndi"
}, },
"removeOrgUserNoMasterPasswordTitle": { "removeOrgUserNoMasterPasswordTitle": {
"message": "Hesabın ana parolu yoxdur" "message": "Hesabın ana parolu yoxdur"

View File

@@ -618,8 +618,8 @@
"loginWithDevice": { "loginWithDevice": {
"message": "Увайсці з прыладай" "message": "Увайсці з прыладай"
}, },
"loginWithDeviceEnabledInfo": { "loginWithDeviceEnabledNote": {
"message": "Неабходна ўключыць уваход з прыладай у наладах мабільнай праграмы Bitwarden. Неабходны іншы варыянт?" "message": "Неабходна наладзіць уваход з прыладай у наладах мабільнай праграмы Bitwarden. Патрабуецца іншы варыянт?"
}, },
"loginWithMasterPassword": { "loginWithMasterPassword": {
"message": "Увайсці з асноўным паролем" "message": "Увайсці з асноўным паролем"
@@ -4594,21 +4594,18 @@
"accountRecoveryPolicy": { "accountRecoveryPolicy": {
"message": "Адміністраванне аднаўлення ўліковага запісу" "message": "Адміністраванне аднаўлення ўліковага запісу"
}, },
"accountRecoveryPolicyDescription": { "accountRecoveryPolicyDesc": {
"message": "Аднаўленне ўліковага запісу ўдзельніка, калі асноўны пароль забыты або давераная прылада страчана. Працэс аднаўлення заснаваны на метадзе шыфравання ўліковага запісу." "message": "Based on the encryption method, recover accounts when master passwords or trusted devices are forgotten or lost."
}, },
"resetPasswordPolicyWarning": { "accountRecoveryPolicyWarning": {
"message": "Карыстальнікі арганізацыі павінны зарэгістравацца самастойна або быць зарэгістраванымі аўтаматычна, каб адміністратары маглі скінуць іх асноўны пароль." "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."
}, },
"resetPasswordPolicyAutoEnroll": { "resetPasswordPolicyAutoEnroll": {
"message": "Аўтаматычная рэгістрацыя" "message": "Аўтаматычная рэгістрацыя"
}, },
"resetPasswordPolicyAutoEnrollDescription": {
"message": "Усе карыстальнікі будуць аўтаматычна зарэгістраваны на скіданне пароля, пасля таго як іх запрашэнне будзе прынята і ім не будзе дазволена адмовіцца ад яго выканання."
},
"resetPasswordPolicyAutoEnrollWarning": {
"message": "Карыстальнікі, якія ўжо ўваходзяць у арганізацыю не змогуць зарэгістравацца на скіданне пароля папярэднім чыслом. Яны павінны будуць самастойна зарэгістравацца, перад тым як адміністратары змогуць скінуць іх асноўны пароль."
},
"resetPasswordPolicyAutoEnrollCheckbox": { "resetPasswordPolicyAutoEnrollCheckbox": {
"message": "Аўтаматычна рэгістраваць новых карыстальнікаў" "message": "Аўтаматычна рэгістраваць новых карыстальнікаў"
}, },
@@ -5224,8 +5221,8 @@
"message": "Выкарыстоўваць", "message": "Выкарыстоўваць",
"description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Use the require single-sign-on authentication policy to require all members to log in with SSO.'" "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Use the require single-sign-on authentication policy to require all members to log in with SSO.'"
}, },
"ssoPolicyHelpLink": { "ssoPolicyHelpAnchor": {
"message": "абавязковую палітыку аўтэнтыфікацыі адзінага ўваходу (SSO)", "message": "require single sign-on authentication policy",
"description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Use the require single-sign-on authentication policy to require all members to log in with SSO.'" "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Use the require single-sign-on authentication policy to require all members to log in with SSO.'"
}, },
"ssoPolicyHelpEnd": { "ssoPolicyHelpEnd": {
@@ -6627,7 +6624,7 @@
"message": "Менеджар сакрэтаў" "message": "Менеджар сакрэтаў"
}, },
"secretsManagerAccessDescription": { "secretsManagerAccessDescription": {
"message": "Activate user access to Secrets Manager." "message": "Актываваць доступ карыстальнікаў да менеджара сакрэтаў."
}, },
"userAccessSecretsManagerGA": { "userAccessSecretsManagerGA": {
"message": "Гэты карыстальнік можа атрымаць доступ да менеджара сакрэтаў" "message": "Гэты карыстальнік можа атрымаць доступ да менеджара сакрэтаў"
@@ -6831,17 +6828,25 @@
"trustedDeviceEncryption": { "trustedDeviceEncryption": {
"message": "Шыфраванне даверанай прылады" "message": "Шыфраванне даверанай прылады"
}, },
"memberDecryptionTdeDescriptionStart": { "memberDecryptionTdeDescriptionPartOne": {
"message": "Пасля аўтэнтыфікацыі ўдзельнікі расшыфроўваюць даныя сховішча з выкарыстаннем ключа, якія захоўваецца на іх прыладах", "message": "Once authenticated, members will decrypt vault data using a key stored on their device. The",
"description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Once authenticated, members will decrypt vault data using a key stored on their device. The account recovery administration policy with automatic enrollment will turn on when this option is used.'" "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Once authenticated, members will decrypt vault data using a key stored on their device. The single organization policy and account recovery administration policy with automatic enrollment will turn on when this option is used.'"
}, },
"memberDecryptionTdeDescriptionLink": { "memberDecryptionTdeDescriptionLinkOne": {
"message": "Палітыка адміністравання аднаўлення ўліковых запісаў", "message": "single organization",
"description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Once authenticated, members will decrypt vault data using a key stored on their device. The account recovery administration policy with automatic enrollment will turn on when this option is used.'" "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Once authenticated, members will decrypt vault data using a key stored on their device. The single organization policy and account recovery administration policy with automatic enrollment will turn on when this option is used.'"
}, },
"memberDecryptionTdeDescriptionEnd": { "memberDecryptionTdeDescriptionPartTwo": {
"message": "з аўтаматычнай рэгістрацыяй уключаецца пры выкарыстанні гэтага параметра.", "message": "policy and ",
"description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Once authenticated, members will decrypt vault data using a key stored on their device. The master password reset policy with automatic enrollment will turn on when this option is used.'" "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Once authenticated, members will decrypt vault data using a key stored on their device. The single organization policy and account recovery administration policy with automatic enrollment will turn on when this option is used.'"
},
"memberDecryptionTdeDescriptionLinkTwo": {
"message": "account recovery administration",
"description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Once authenticated, members will decrypt vault data using a key stored on their device. The single organization policy and account recovery administration policy with automatic enrollment will turn on when this option is used.'"
},
"memberDecryptionTdeDescriptionPartThree": {
"message": "policy with automatic enrollment will turn on when this option is used.",
"description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Once authenticated, members will decrypt vault data using a key stored on their device. The single organization policy and account recovery administration policy with automatic enrollment will turn on when this option is used.'"
}, },
"notFound": { "notFound": {
"message": "$RESOURCE$ не знойдзены", "message": "$RESOURCE$ не знойдзены",
@@ -6859,18 +6864,18 @@
"message": "Карыстальнік абнавіў пароль пры аднаўленні ўліковага запісу." "message": "Карыстальнік абнавіў пароль пры аднаўленні ўліковага запісу."
}, },
"activatedAccessToSecretsManager": { "activatedAccessToSecretsManager": {
"message": "Activated access to Secrets Manager", "message": "Доступ карыстальнікаў да менеджара сакрэтаў актываваны",
"description": "Confirmation message that one or more users gained access to Secrets Manager" "description": "Confirmation message that one or more users gained access to Secrets Manager"
}, },
"activateAccess": { "activateAccess": {
"message": "Activate access" "message": "Актываваць доступ"
}, },
"bulkEnableSecretsManagerDescription": { "bulkEnableSecretsManagerDescription": {
"message": "Забяспечце доступ наступным удзельнікам да менеджара сакрэтаў. Роля, якая забяспечана ў менеджары пароляў будзе ўжывацца і да менеджара сакрэтаў.", "message": "Забяспечце доступ наступным удзельнікам да менеджара сакрэтаў. Роля, якая забяспечана ў менеджары пароляў будзе ўжывацца і да менеджара сакрэтаў.",
"description": "This description is shown to an admin when they are attempting to add more users to Secrets Manager." "description": "This description is shown to an admin when they are attempting to add more users to Secrets Manager."
}, },
"activateSecretsManager": { "activateSecretsManager": {
"message": "Activate Secrets Manager" "message": "Актываваць менеджар сакрэтаў"
}, },
"yourOrganizationsFingerprint": { "yourOrganizationsFingerprint": {
"message": "Фраза адбітку пальца вашай арганізацыі", "message": "Фраза адбітку пальца вашай арганізацыі",

View File

@@ -618,8 +618,8 @@
"loginWithDevice": { "loginWithDevice": {
"message": "Вписване с устройство" "message": "Вписване с устройство"
}, },
"loginWithDeviceEnabledInfo": { "loginWithDeviceEnabledNote": {
"message": "Вписването с устройство трябва да е включено в настройките на мобилното приложение на Битуорден. Друга настройка ли търсите?" "message": "Вписването с устройство трябва да е включено в настройките на приложението на Битуорден. Друга настройка ли търсите?"
}, },
"loginWithMasterPassword": { "loginWithMasterPassword": {
"message": "Вписване с главната парола" "message": "Вписване с главната парола"
@@ -4594,21 +4594,18 @@
"accountRecoveryPolicy": { "accountRecoveryPolicy": {
"message": "Управление на възстановяването на профили" "message": "Управление на възстановяването на профили"
}, },
"accountRecoveryPolicyDescription": { "accountRecoveryPolicyDesc": {
"message": "Можете да възстановявате профилите на потребителите, когато загубят или забравят своята главна парола или доверено устройство. Процесът по възстановяване се основава на метод на шифроване на профила." "message": "Според метода на шифроване, регистрациите ще бъдат възстановени при забравяне на главна парола или загуба на доверено устройство."
}, },
"resetPasswordPolicyWarning": { "accountRecoveryPolicyWarning": {
"message": "Потребителите в организацията ще трябва да се включат сами или да бъдат включени автоматично, преди администраторите да могат да сменят главните им пароли." "message": "Текущо регистрираните потребители използващи главни пароли ще трябва да се включат сами, преди администраторите да могат да възстановят регистрациите им. Автоматичното включване ще активира възстановяването на регистрациите за новите членове."
},
"accountRecoverySingleOrgRequirementDesc": {
"message": "The single organization Enterprise policy must be turned on before activating this policy."
}, },
"resetPasswordPolicyAutoEnroll": { "resetPasswordPolicyAutoEnroll": {
"message": "Автоматично включване" "message": "Автоматично включване"
}, },
"resetPasswordPolicyAutoEnrollDescription": {
"message": "Всички потребители ще бъдат включени автоматично в смяната на пароли след като приемат поканите си."
},
"resetPasswordPolicyAutoEnrollWarning": {
"message": "Потребителите, които вече са част от организацията, няма да бъдат включени в смяната на паролите. Те ще трябва да се включат сами, преди администраторите да могат да сменят главните им пароли."
},
"resetPasswordPolicyAutoEnrollCheckbox": { "resetPasswordPolicyAutoEnrollCheckbox": {
"message": "Автоматично включване на новите потребители" "message": "Автоматично включване на новите потребители"
}, },
@@ -5224,8 +5221,8 @@
"message": "Включете", "message": "Включете",
"description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Use the require single-sign-on authentication policy to require all members to log in with SSO.'" "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Use the require single-sign-on authentication policy to require all members to log in with SSO.'"
}, },
"ssoPolicyHelpLink": { "ssoPolicyHelpAnchor": {
"message": "еднократното удостоверяване", "message": "политиката за изискване на еднократно удостоверяване",
"description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Use the require single-sign-on authentication policy to require all members to log in with SSO.'" "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Use the require single-sign-on authentication policy to require all members to log in with SSO.'"
}, },
"ssoPolicyHelpEnd": { "ssoPolicyHelpEnd": {
@@ -6627,7 +6624,7 @@
"message": "Управление на тайни" "message": "Управление на тайни"
}, },
"secretsManagerAccessDescription": { "secretsManagerAccessDescription": {
"message": "Activate user access to Secrets Manager." "message": "Включване на достъпа на потребителите до Управлението на тайни."
}, },
"userAccessSecretsManagerGA": { "userAccessSecretsManagerGA": {
"message": "Този потребител има достъп до Управлението на тайни" "message": "Този потребител има достъп до Управлението на тайни"
@@ -6831,17 +6828,25 @@
"trustedDeviceEncryption": { "trustedDeviceEncryption": {
"message": "Шифроване чрез доверено устройство" "message": "Шифроване чрез доверено устройство"
}, },
"memberDecryptionTdeDescriptionStart": { "memberDecryptionTdeDescriptionPartOne": {
"message": "След вписване, членовете ще дешифрират данните от трезорите си чрез ключ, който се съхранява на устройство.", "message": "След вписване, членовете ще дешифрират данните от трезорите си чрез ключ, който се съхранява на устройство. Политиката за",
"description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Once authenticated, members will decrypt vault data using a key stored on their device. The account recovery administration policy with automatic enrollment will turn on when this option is used.'" "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Once authenticated, members will decrypt vault data using a key stored on their device. The single organization policy and account recovery administration policy with automatic enrollment will turn on when this option is used.'"
}, },
"memberDecryptionTdeDescriptionLink": { "memberDecryptionTdeDescriptionLinkOne": {
"message": "Политиката за управление на възстановяването на профили", "message": "единствена организация",
"description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Once authenticated, members will decrypt vault data using a key stored on their device. The account recovery administration policy with automatic enrollment will turn on when this option is used.'" "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Once authenticated, members will decrypt vault data using a key stored on their device. The single organization policy and account recovery administration policy with automatic enrollment will turn on when this option is used.'"
}, },
"memberDecryptionTdeDescriptionEnd": { "memberDecryptionTdeDescriptionPartTwo": {
"message": "и тази за ",
"description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Once authenticated, members will decrypt vault data using a key stored on their device. The single organization policy and account recovery administration policy with automatic enrollment will turn on when this option is used.'"
},
"memberDecryptionTdeDescriptionLinkTwo": {
"message": "управление на възстановяването на регистрации",
"description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Once authenticated, members will decrypt vault data using a key stored on their device. The single organization policy and account recovery administration policy with automatic enrollment will turn on when this option is used.'"
},
"memberDecryptionTdeDescriptionPartThree": {
"message": "с автоматично включване ще бъде активирана при използването на тази настройка.", "message": "с автоматично включване ще бъде активирана при използването на тази настройка.",
"description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Once authenticated, members will decrypt vault data using a key stored on their device. The master password reset policy with automatic enrollment will turn on when this option is used.'" "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Once authenticated, members will decrypt vault data using a key stored on their device. The single organization policy and account recovery administration policy with automatic enrollment will turn on when this option is used.'"
}, },
"notFound": { "notFound": {
"message": "Няма намерен $RESOURCE$", "message": "Няма намерен $RESOURCE$",
@@ -6859,18 +6864,18 @@
"message": "Потребителят промени парола издадена чрез възстановяването на профили." "message": "Потребителят промени парола издадена чрез възстановяването на профили."
}, },
"activatedAccessToSecretsManager": { "activatedAccessToSecretsManager": {
"message": "Activated access to Secrets Manager", "message": "Достъпът до Управлението на тайни е включен",
"description": "Confirmation message that one or more users gained access to Secrets Manager" "description": "Confirmation message that one or more users gained access to Secrets Manager"
}, },
"activateAccess": { "activateAccess": {
"message": "Activate access" "message": "Включване на достъпа"
}, },
"bulkEnableSecretsManagerDescription": { "bulkEnableSecretsManagerDescription": {
"message": "Дайте на следните членове достъп до Управлението на тайни. Ролята, която всеки от тях има в Управителя на пароли, ще се приложи и в Управлението на тайни.", "message": "Дайте на следните членове достъп до Управлението на тайни. Ролята, която всеки от тях има в Управителя на пароли, ще се приложи и в Управлението на тайни.",
"description": "This description is shown to an admin when they are attempting to add more users to Secrets Manager." "description": "This description is shown to an admin when they are attempting to add more users to Secrets Manager."
}, },
"activateSecretsManager": { "activateSecretsManager": {
"message": "Activate Secrets Manager" "message": "Включване на Управлението на тайни"
}, },
"yourOrganizationsFingerprint": { "yourOrganizationsFingerprint": {
"message": "Уникална фраза, идентифицираща организацията ви", "message": "Уникална фраза, идентифицираща организацията ви",

View File

@@ -618,8 +618,8 @@
"loginWithDevice": { "loginWithDevice": {
"message": "Log in with device" "message": "Log in with device"
}, },
"loginWithDeviceEnabledInfo": { "loginWithDeviceEnabledNote": {
"message": "Log in with device must be set up in the settings of the Bitwarden mobile app. Need another option?" "message": "Log in with device must be set up in the settings of the Bitwarden app. Need another option?"
}, },
"loginWithMasterPassword": { "loginWithMasterPassword": {
"message": "Log in with master password" "message": "Log in with master password"
@@ -4594,21 +4594,18 @@
"accountRecoveryPolicy": { "accountRecoveryPolicy": {
"message": "Account recovery administration" "message": "Account recovery administration"
}, },
"accountRecoveryPolicyDescription": { "accountRecoveryPolicyDesc": {
"message": "Recover member accounts when master password or trusted devices are forgotten or lost. The recovery processes is based on the account encryption method." "message": "Based on the encryption method, recover accounts when master passwords or trusted devices are forgotten or lost."
}, },
"resetPasswordPolicyWarning": { "accountRecoveryPolicyWarning": {
"message": "Members in the organization will need to self-enroll or be auto-enrolled before administrators can reset their master password." "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."
}, },
"resetPasswordPolicyAutoEnroll": { "resetPasswordPolicyAutoEnroll": {
"message": "Automatic enrollment" "message": "Automatic enrollment"
}, },
"resetPasswordPolicyAutoEnrollDescription": {
"message": "All members will be automatically enrolled in password reset once their invite is accepted and will not be allowed to withdraw."
},
"resetPasswordPolicyAutoEnrollWarning": {
"message": "Members already in the organization will not be retroactively enrolled in password reset. They will need to self-enroll before administrators can reset their master password."
},
"resetPasswordPolicyAutoEnrollCheckbox": { "resetPasswordPolicyAutoEnrollCheckbox": {
"message": "Require new members to be enrolled automatically" "message": "Require new members to be enrolled automatically"
}, },
@@ -5224,8 +5221,8 @@
"message": "Use the", "message": "Use the",
"description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Use the require single-sign-on authentication policy to require all members to log in with SSO.'" "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Use the require single-sign-on authentication policy to require all members to log in with SSO.'"
}, },
"ssoPolicyHelpLink": { "ssoPolicyHelpAnchor": {
"message": "require single-sign-on authentication policy", "message": "require single sign-on authentication policy",
"description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Use the require single-sign-on authentication policy to require all members to log in with SSO.'" "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Use the require single-sign-on authentication policy to require all members to log in with SSO.'"
}, },
"ssoPolicyHelpEnd": { "ssoPolicyHelpEnd": {
@@ -6831,17 +6828,25 @@
"trustedDeviceEncryption": { "trustedDeviceEncryption": {
"message": "Trusted device encryption" "message": "Trusted device encryption"
}, },
"memberDecryptionTdeDescriptionStart": { "memberDecryptionTdeDescriptionPartOne": {
"message": "Once authenticated, members will decrypt vault data using a key stored on their device. The", "message": "Once authenticated, members will decrypt vault data using a key stored on their device. The",
"description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Once authenticated, members will decrypt vault data using a key stored on their device. The account recovery administration policy with automatic enrollment will turn on when this option is used.'" "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Once authenticated, members will decrypt vault data using a key stored on their device. The single organization policy and account recovery administration policy with automatic enrollment will turn on when this option is used.'"
}, },
"memberDecryptionTdeDescriptionLink": { "memberDecryptionTdeDescriptionLinkOne": {
"message": "account recovery administration policy", "message": "single organization",
"description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Once authenticated, members will decrypt vault data using a key stored on their device. The account recovery administration policy with automatic enrollment will turn on when this option is used.'" "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Once authenticated, members will decrypt vault data using a key stored on their device. The single organization policy and account recovery administration policy with automatic enrollment will turn on when this option is used.'"
}, },
"memberDecryptionTdeDescriptionEnd": { "memberDecryptionTdeDescriptionPartTwo": {
"message": "with automatic enrollment will turn on when this option is used.", "message": "policy and ",
"description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Once authenticated, members will decrypt vault data using a key stored on their device. The master password reset policy with automatic enrollment will turn on when this option is used.'" "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Once authenticated, members will decrypt vault data using a key stored on their device. The single organization policy and account recovery administration policy with automatic enrollment will turn on when this option is used.'"
},
"memberDecryptionTdeDescriptionLinkTwo": {
"message": "account recovery administration",
"description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Once authenticated, members will decrypt vault data using a key stored on their device. The single organization policy and account recovery administration policy with automatic enrollment will turn on when this option is used.'"
},
"memberDecryptionTdeDescriptionPartThree": {
"message": "policy with automatic enrollment will turn on when this option is used.",
"description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Once authenticated, members will decrypt vault data using a key stored on their device. The single organization policy and account recovery administration policy with automatic enrollment will turn on when this option is used.'"
}, },
"notFound": { "notFound": {
"message": "$RESOURCE$ not found", "message": "$RESOURCE$ not found",

View File

@@ -618,8 +618,8 @@
"loginWithDevice": { "loginWithDevice": {
"message": "Log in with device" "message": "Log in with device"
}, },
"loginWithDeviceEnabledInfo": { "loginWithDeviceEnabledNote": {
"message": "Log in with device must be set up in the settings of the Bitwarden mobile app. Need another option?" "message": "Log in with device must be set up in the settings of the Bitwarden app. Need another option?"
}, },
"loginWithMasterPassword": { "loginWithMasterPassword": {
"message": "Log in with master password" "message": "Log in with master password"
@@ -4594,21 +4594,18 @@
"accountRecoveryPolicy": { "accountRecoveryPolicy": {
"message": "Account recovery administration" "message": "Account recovery administration"
}, },
"accountRecoveryPolicyDescription": { "accountRecoveryPolicyDesc": {
"message": "Recover member accounts when master password or trusted devices are forgotten or lost. The recovery processes is based on the account encryption method." "message": "Based on the encryption method, recover accounts when master passwords or trusted devices are forgotten or lost."
}, },
"resetPasswordPolicyWarning": { "accountRecoveryPolicyWarning": {
"message": "Members in the organization will need to self-enroll or be auto-enrolled before administrators can reset their master password." "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."
}, },
"resetPasswordPolicyAutoEnroll": { "resetPasswordPolicyAutoEnroll": {
"message": "Automatic enrollment" "message": "Automatic enrollment"
}, },
"resetPasswordPolicyAutoEnrollDescription": {
"message": "All members will be automatically enrolled in password reset once their invite is accepted and will not be allowed to withdraw."
},
"resetPasswordPolicyAutoEnrollWarning": {
"message": "Members already in the organization will not be retroactively enrolled in password reset. They will need to self-enroll before administrators can reset their master password."
},
"resetPasswordPolicyAutoEnrollCheckbox": { "resetPasswordPolicyAutoEnrollCheckbox": {
"message": "Require new members to be enrolled automatically" "message": "Require new members to be enrolled automatically"
}, },
@@ -5224,8 +5221,8 @@
"message": "Use the", "message": "Use the",
"description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Use the require single-sign-on authentication policy to require all members to log in with SSO.'" "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Use the require single-sign-on authentication policy to require all members to log in with SSO.'"
}, },
"ssoPolicyHelpLink": { "ssoPolicyHelpAnchor": {
"message": "require single-sign-on authentication policy", "message": "require single sign-on authentication policy",
"description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Use the require single-sign-on authentication policy to require all members to log in with SSO.'" "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Use the require single-sign-on authentication policy to require all members to log in with SSO.'"
}, },
"ssoPolicyHelpEnd": { "ssoPolicyHelpEnd": {
@@ -6831,17 +6828,25 @@
"trustedDeviceEncryption": { "trustedDeviceEncryption": {
"message": "Trusted device encryption" "message": "Trusted device encryption"
}, },
"memberDecryptionTdeDescriptionStart": { "memberDecryptionTdeDescriptionPartOne": {
"message": "Once authenticated, members will decrypt vault data using a key stored on their device. The", "message": "Once authenticated, members will decrypt vault data using a key stored on their device. The",
"description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Once authenticated, members will decrypt vault data using a key stored on their device. The account recovery administration policy with automatic enrollment will turn on when this option is used.'" "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Once authenticated, members will decrypt vault data using a key stored on their device. The single organization policy and account recovery administration policy with automatic enrollment will turn on when this option is used.'"
}, },
"memberDecryptionTdeDescriptionLink": { "memberDecryptionTdeDescriptionLinkOne": {
"message": "account recovery administration policy", "message": "single organization",
"description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Once authenticated, members will decrypt vault data using a key stored on their device. The account recovery administration policy with automatic enrollment will turn on when this option is used.'" "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Once authenticated, members will decrypt vault data using a key stored on their device. The single organization policy and account recovery administration policy with automatic enrollment will turn on when this option is used.'"
}, },
"memberDecryptionTdeDescriptionEnd": { "memberDecryptionTdeDescriptionPartTwo": {
"message": "with automatic enrollment will turn on when this option is used.", "message": "policy and ",
"description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Once authenticated, members will decrypt vault data using a key stored on their device. The master password reset policy with automatic enrollment will turn on when this option is used.'" "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Once authenticated, members will decrypt vault data using a key stored on their device. The single organization policy and account recovery administration policy with automatic enrollment will turn on when this option is used.'"
},
"memberDecryptionTdeDescriptionLinkTwo": {
"message": "account recovery administration",
"description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Once authenticated, members will decrypt vault data using a key stored on their device. The single organization policy and account recovery administration policy with automatic enrollment will turn on when this option is used.'"
},
"memberDecryptionTdeDescriptionPartThree": {
"message": "policy with automatic enrollment will turn on when this option is used.",
"description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Once authenticated, members will decrypt vault data using a key stored on their device. The single organization policy and account recovery administration policy with automatic enrollment will turn on when this option is used.'"
}, },
"notFound": { "notFound": {
"message": "$RESOURCE$ not found", "message": "$RESOURCE$ not found",

View File

@@ -618,8 +618,8 @@
"loginWithDevice": { "loginWithDevice": {
"message": "Inici de sessió amb dispositiu" "message": "Inici de sessió amb dispositiu"
}, },
"loginWithDeviceEnabledInfo": { "loginWithDeviceEnabledNote": {
"message": "L'inici de sessió amb el dispositiu ha d'estar activat a la configuració de l'aplicació mòbil Bitwarden. Necessiteu una altra opció?" "message": "L'inici de sessió amb el dispositiu ha d'estar activat a la configuració de l'aplicació Bitwarden. Necessiteu una altra opció?"
}, },
"loginWithMasterPassword": { "loginWithMasterPassword": {
"message": "Inici de sessió amb contrasenya mestra" "message": "Inici de sessió amb contrasenya mestra"
@@ -4594,21 +4594,18 @@
"accountRecoveryPolicy": { "accountRecoveryPolicy": {
"message": "Administració de recuperació de comptes" "message": "Administració de recuperació de comptes"
}, },
"accountRecoveryPolicyDescription": { "accountRecoveryPolicyDesc": {
"message": "Recupereu els comptes dels membres quan s'obliden o perden la contrasenya mestra o els dispositius de confiança. El procés de recuperació es basa en el mètode de xifratge del compte." "message": "Based on the encryption method, recover accounts when master passwords or trusted devices are forgotten or lost."
}, },
"resetPasswordPolicyWarning": { "accountRecoveryPolicyWarning": {
"message": "Els usuaris de lorganització hauran de registrar-se o ser registrats abans que els administradors puguen restablir la seua contrasenya mestra." "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."
}, },
"resetPasswordPolicyAutoEnroll": { "resetPasswordPolicyAutoEnroll": {
"message": "Inscripció automàtica" "message": "Inscripció automàtica"
}, },
"resetPasswordPolicyAutoEnrollDescription": {
"message": "Una vegada s'accepte la invitació, tots els usuaris s'inscriuran automàticament al restabliment de la contrasenya."
},
"resetPasswordPolicyAutoEnrollWarning": {
"message": "Els usuaris que ja formen part de lorganització no sinscriuran retroactivament al restabliment de la contrasenya. Hauran de registrar-se abans que els administradors puguen restablir la seua contrasenya mestra."
},
"resetPasswordPolicyAutoEnrollCheckbox": { "resetPasswordPolicyAutoEnrollCheckbox": {
"message": "Inscriviu automàticament usuaris nous" "message": "Inscriviu automàticament usuaris nous"
}, },
@@ -5224,8 +5221,8 @@
"message": "Habilita la", "message": "Habilita la",
"description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Use the require single-sign-on authentication policy to require all members to log in with SSO.'" "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Use the require single-sign-on authentication policy to require all members to log in with SSO.'"
}, },
"ssoPolicyHelpLink": { "ssoPolicyHelpAnchor": {
"message": "Política d'autenticació SSO", "message": "require single sign-on authentication policy",
"description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Use the require single-sign-on authentication policy to require all members to log in with SSO.'" "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Use the require single-sign-on authentication policy to require all members to log in with SSO.'"
}, },
"ssoPolicyHelpEnd": { "ssoPolicyHelpEnd": {
@@ -6627,7 +6624,7 @@
"message": "Administrador de secrets" "message": "Administrador de secrets"
}, },
"secretsManagerAccessDescription": { "secretsManagerAccessDescription": {
"message": "Activate user access to Secrets Manager." "message": "Activa l'accés dels usuaris a l'administrador de secrets."
}, },
"userAccessSecretsManagerGA": { "userAccessSecretsManagerGA": {
"message": "Aquest usuari pot accedir a l'administrador de secrets" "message": "Aquest usuari pot accedir a l'administrador de secrets"
@@ -6831,17 +6828,25 @@
"trustedDeviceEncryption": { "trustedDeviceEncryption": {
"message": "Encriptació de dispositius de confiança" "message": "Encriptació de dispositius de confiança"
}, },
"memberDecryptionTdeDescriptionStart": { "memberDecryptionTdeDescriptionPartOne": {
"message": "Una vegada autenticats, els membres desxifraran les dades de la caixa forta mitjançant una clau emmagatzemada al seu dispositiu. El", "message": "Once authenticated, members will decrypt vault data using a key stored on their device. The",
"description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Once authenticated, members will decrypt vault data using a key stored on their device. The account recovery administration policy with automatic enrollment will turn on when this option is used.'" "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Once authenticated, members will decrypt vault data using a key stored on their device. The single organization policy and account recovery administration policy with automatic enrollment will turn on when this option is used.'"
}, },
"memberDecryptionTdeDescriptionLink": { "memberDecryptionTdeDescriptionLinkOne": {
"message": "política d'administració de recuperació de comptes", "message": "single organization",
"description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Once authenticated, members will decrypt vault data using a key stored on their device. The account recovery administration policy with automatic enrollment will turn on when this option is used.'" "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Once authenticated, members will decrypt vault data using a key stored on their device. The single organization policy and account recovery administration policy with automatic enrollment will turn on when this option is used.'"
}, },
"memberDecryptionTdeDescriptionEnd": { "memberDecryptionTdeDescriptionPartTwo": {
"message": "amb inscripció automàtica s'activarà quan s'utilitze aquesta opció.", "message": "policy and ",
"description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Once authenticated, members will decrypt vault data using a key stored on their device. The master password reset policy with automatic enrollment will turn on when this option is used.'" "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Once authenticated, members will decrypt vault data using a key stored on their device. The single organization policy and account recovery administration policy with automatic enrollment will turn on when this option is used.'"
},
"memberDecryptionTdeDescriptionLinkTwo": {
"message": "account recovery administration",
"description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Once authenticated, members will decrypt vault data using a key stored on their device. The single organization policy and account recovery administration policy with automatic enrollment will turn on when this option is used.'"
},
"memberDecryptionTdeDescriptionPartThree": {
"message": "policy with automatic enrollment will turn on when this option is used.",
"description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Once authenticated, members will decrypt vault data using a key stored on their device. The single organization policy and account recovery administration policy with automatic enrollment will turn on when this option is used.'"
}, },
"notFound": { "notFound": {
"message": "No s'ha trobat $RESOURCE$", "message": "No s'ha trobat $RESOURCE$",
@@ -6859,18 +6864,18 @@
"message": "L'usuari ha actualitzat una contrasenya emesa mitjançant la recuperació del compte." "message": "L'usuari ha actualitzat una contrasenya emesa mitjançant la recuperació del compte."
}, },
"activatedAccessToSecretsManager": { "activatedAccessToSecretsManager": {
"message": "Activated access to Secrets Manager", "message": "Activa l'accés a l'administrador de secrets",
"description": "Confirmation message that one or more users gained access to Secrets Manager" "description": "Confirmation message that one or more users gained access to Secrets Manager"
}, },
"activateAccess": { "activateAccess": {
"message": "Activate access" "message": "Activa l'accés"
}, },
"bulkEnableSecretsManagerDescription": { "bulkEnableSecretsManagerDescription": {
"message": "Concedeix als membres següents accés a l'administrador de secrets. El rol concedit l'administrador de contrasenyes s'aplicarà a l'administrador de secrets.", "message": "Concedeix als membres següents accés a l'administrador de secrets. El rol concedit l'administrador de contrasenyes s'aplicarà a l'administrador de secrets.",
"description": "This description is shown to an admin when they are attempting to add more users to Secrets Manager." "description": "This description is shown to an admin when they are attempting to add more users to Secrets Manager."
}, },
"activateSecretsManager": { "activateSecretsManager": {
"message": "Activate Secrets Manager" "message": "Activa l'administrador de secrets"
}, },
"yourOrganizationsFingerprint": { "yourOrganizationsFingerprint": {
"message": "Frase d'empremta digital de l'organització", "message": "Frase d'empremta digital de l'organització",
@@ -6931,7 +6936,7 @@
"message": "La supressió de membres que no tenen contrasenyes mestres sense establir-ne una pot restringir l'accés al seu compte complet." "message": "La supressió de membres que no tenen contrasenyes mestres sense establir-ne una pot restringir l'accés al seu compte complet."
}, },
"startYour7DayFreeTrialOfBitwardenFor": { "startYour7DayFreeTrialOfBitwardenFor": {
"message": "Start your 7-Day free trial of Bitwarden for $ORG$", "message": "Inicieu la vostra prova gratuïta de 7 dies de Bitwarden per a $ORG$",
"placeholders": { "placeholders": {
"org": { "org": {
"content": "$1", "content": "$1",
@@ -6940,15 +6945,15 @@
} }
}, },
"next": { "next": {
"message": "Next" "message": "Següent"
}, },
"usFlag": { "usFlag": {
"message": "US flag" "message": "Bandera EUA"
}, },
"euFlag": { "euFlag": {
"message": "EU flag" "message": "Bandera UE"
}, },
"selectedRegionFlag": { "selectedRegionFlag": {
"message": "Selected region flag" "message": "Bandera de la regió seleccionada"
} }
} }

View File

@@ -618,8 +618,8 @@
"loginWithDevice": { "loginWithDevice": {
"message": "Přihlásit se zařízením" "message": "Přihlásit se zařízením"
}, },
"loginWithDeviceEnabledInfo": { "loginWithDeviceEnabledNote": {
"message": "Přihlášení zařízením musí být nastaveno v mobilní aplikaci Bitwarden. Potřebujete další volby?" "message": "Přihlášení zařízením musí být nastaveno v aplikaci Bitwarden. Potřebujete další volby?"
}, },
"loginWithMasterPassword": { "loginWithMasterPassword": {
"message": "Přihlásit se pomocí hlavního hesla" "message": "Přihlásit se pomocí hlavního hesla"
@@ -4594,21 +4594,18 @@
"accountRecoveryPolicy": { "accountRecoveryPolicy": {
"message": "Správa obnovení účtu" "message": "Správa obnovení účtu"
}, },
"accountRecoveryPolicyDescription": { "accountRecoveryPolicyDesc": {
"message": "Obnovečlenských účtů při zapomenutí hlavního hesla nebo ztrátě důvěryhodných zařízení. Procesy obnovení jsou založeny na metodě šifrování účtu." "message": "Na základě metody šifrovámůžete obnovit účty v případě zapomenutí nebo ztráty hlavních hesel nebo důvěryhodných zařízení."
}, },
"resetPasswordPolicyWarning": { "accountRecoveryPolicyWarning": {
"message": "Uživatelé v organizaci se budou muset sami zaregistrovat nebo být automaticky zaregistrovaní předtím, než budou moci administrátoři resetovat jejich hlavní heslo." "message": "U stávajících účtů s hlavními hesly se budou muset členové sami přihlásit, aby mohli správci obnovit jejich účty. Automatická registrace zapne obnovení účtu pro nové členy."
},
"accountRecoverySingleOrgRequirementDesc": {
"message": "Před aktivací této zásady musí být povolena jednotná pravidla organizace."
}, },
"resetPasswordPolicyAutoEnroll": { "resetPasswordPolicyAutoEnroll": {
"message": "Automatická registrace" "message": "Automatická registrace"
}, },
"resetPasswordPolicyAutoEnrollDescription": {
"message": "Jakmile bude jejich pozvání přijato, všichni uživatelé budou automaticky zaregistrováni do resetování hesla."
},
"resetPasswordPolicyAutoEnrollWarning": {
"message": "Uživatelé, kteří jsou již v organizaci, nebudou zpětně zaregistrováni do obnovení hesla. Než budou moci správci obnovit hlavní heslo, budou se muset zaregistrovat sami."
},
"resetPasswordPolicyAutoEnrollCheckbox": { "resetPasswordPolicyAutoEnrollCheckbox": {
"message": "Automaticky zaregistrovat nové uživatele" "message": "Automaticky zaregistrovat nové uživatele"
}, },
@@ -5224,7 +5221,7 @@
"message": "Použít", "message": "Použít",
"description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Use the require single-sign-on authentication policy to require all members to log in with SSO.'" "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Use the require single-sign-on authentication policy to require all members to log in with SSO.'"
}, },
"ssoPolicyHelpLink": { "ssoPolicyHelpAnchor": {
"message": "vyžadování zásad ověření jednotným přihlášením", "message": "vyžadování zásad ověření jednotným přihlášením",
"description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Use the require single-sign-on authentication policy to require all members to log in with SSO.'" "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Use the require single-sign-on authentication policy to require all members to log in with SSO.'"
}, },
@@ -6627,7 +6624,7 @@
"message": "Správce tajných klíčů" "message": "Správce tajných klíčů"
}, },
"secretsManagerAccessDescription": { "secretsManagerAccessDescription": {
"message": "Activate user access to Secrets Manager." "message": "Aktivuje přístup uživatele ke správci tajných klíčů."
}, },
"userAccessSecretsManagerGA": { "userAccessSecretsManagerGA": {
"message": "Tento uživatel může přistupovat ke správci tajných klíčů" "message": "Tento uživatel může přistupovat ke správci tajných klíčů"
@@ -6831,17 +6828,25 @@
"trustedDeviceEncryption": { "trustedDeviceEncryption": {
"message": "Šifrování důvěryhodného zařízení" "message": "Šifrování důvěryhodného zařízení"
}, },
"memberDecryptionTdeDescriptionStart": { "memberDecryptionTdeDescriptionPartOne": {
"message": "Po ověření budou členové dešifrovat data v trezoru pomocí klíče uloženého na jejich zařízení. ", "message": "Po ověření budou členové dešifrovat data v trezoru pomocí klíče uloženého na jejich zařízení. ",
"description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Once authenticated, members will decrypt vault data using a key stored on their device. The account recovery administration policy with automatic enrollment will turn on when this option is used.'" "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Once authenticated, members will decrypt vault data using a key stored on their device. The single organization policy and account recovery administration policy with automatic enrollment will turn on when this option is used.'"
}, },
"memberDecryptionTdeDescriptionLink": { "memberDecryptionTdeDescriptionLinkOne": {
"message": "Pokud bude tato volba aktivována, ", "message": "Při použití této možnosti ",
"description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Once authenticated, members will decrypt vault data using a key stored on their device. The account recovery administration policy with automatic enrollment will turn on when this option is used.'" "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Once authenticated, members will decrypt vault data using a key stored on their device. The single organization policy and account recovery administration policy with automatic enrollment will turn on when this option is used.'"
}, },
"memberDecryptionTdeDescriptionEnd": { "memberDecryptionTdeDescriptionPartTwo": {
"message": "budou použity zásady obnovení účtu s automatickým zápisem.", "message": "se zapne zásada jednotné organizace ",
"description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Once authenticated, members will decrypt vault data using a key stored on their device. The master password reset policy with automatic enrollment will turn on when this option is used.'" "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Once authenticated, members will decrypt vault data using a key stored on their device. The single organization policy and account recovery administration policy with automatic enrollment will turn on when this option is used.'"
},
"memberDecryptionTdeDescriptionLinkTwo": {
"message": "a zásada správy obnovení účtu ",
"description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Once authenticated, members will decrypt vault data using a key stored on their device. The single organization policy and account recovery administration policy with automatic enrollment will turn on when this option is used.'"
},
"memberDecryptionTdeDescriptionPartThree": {
"message": "s automatickým zápisem.",
"description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Once authenticated, members will decrypt vault data using a key stored on their device. The single organization policy and account recovery administration policy with automatic enrollment will turn on when this option is used.'"
}, },
"notFound": { "notFound": {
"message": "$RESOURCE$ nebyl nalezen", "message": "$RESOURCE$ nebyl nalezen",
@@ -6859,18 +6864,18 @@
"message": "Uživatel aktualizoval heslo vydané prostřednictvím obnovení účtu." "message": "Uživatel aktualizoval heslo vydané prostřednictvím obnovení účtu."
}, },
"activatedAccessToSecretsManager": { "activatedAccessToSecretsManager": {
"message": "Activated access to Secrets Manager", "message": "Aktivován přístup uživatele ke správci tajných klíčů",
"description": "Confirmation message that one or more users gained access to Secrets Manager" "description": "Confirmation message that one or more users gained access to Secrets Manager"
}, },
"activateAccess": { "activateAccess": {
"message": "Activate access" "message": "Aktivovat přístup"
}, },
"bulkEnableSecretsManagerDescription": { "bulkEnableSecretsManagerDescription": {
"message": "Udělte následujícím členům přístup ke správci tajných klíčů. Role udělená ve správci hesel se bude vztahovat na správce tajných klíčů.", "message": "Udělte následujícím členům přístup ke správci tajných klíčů. Role udělená ve správci hesel se bude vztahovat na správce tajných klíčů.",
"description": "This description is shown to an admin when they are attempting to add more users to Secrets Manager." "description": "This description is shown to an admin when they are attempting to add more users to Secrets Manager."
}, },
"activateSecretsManager": { "activateSecretsManager": {
"message": "Activate Secrets Manager" "message": "Aktivovat správce tajných klíčů"
}, },
"yourOrganizationsFingerprint": { "yourOrganizationsFingerprint": {
"message": "Fráze otisku prstu Vaší organizace", "message": "Fráze otisku prstu Vaší organizace",

View File

@@ -618,8 +618,8 @@
"loginWithDevice": { "loginWithDevice": {
"message": "Log in with device" "message": "Log in with device"
}, },
"loginWithDeviceEnabledInfo": { "loginWithDeviceEnabledNote": {
"message": "Log in with device must be set up in the settings of the Bitwarden mobile app. Need another option?" "message": "Log in with device must be set up in the settings of the Bitwarden app. Need another option?"
}, },
"loginWithMasterPassword": { "loginWithMasterPassword": {
"message": "Log in with master password" "message": "Log in with master password"
@@ -4594,21 +4594,18 @@
"accountRecoveryPolicy": { "accountRecoveryPolicy": {
"message": "Account recovery administration" "message": "Account recovery administration"
}, },
"accountRecoveryPolicyDescription": { "accountRecoveryPolicyDesc": {
"message": "Recover member accounts when master password or trusted devices are forgotten or lost. The recovery processes is based on the account encryption method." "message": "Based on the encryption method, recover accounts when master passwords or trusted devices are forgotten or lost."
}, },
"resetPasswordPolicyWarning": { "accountRecoveryPolicyWarning": {
"message": "Members in the organization will need to self-enroll or be auto-enrolled before administrators can reset their master password." "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."
}, },
"resetPasswordPolicyAutoEnroll": { "resetPasswordPolicyAutoEnroll": {
"message": "Automatic enrollment" "message": "Automatic enrollment"
}, },
"resetPasswordPolicyAutoEnrollDescription": {
"message": "All members will be automatically enrolled in password reset once their invite is accepted and will not be allowed to withdraw."
},
"resetPasswordPolicyAutoEnrollWarning": {
"message": "Members already in the organization will not be retroactively enrolled in password reset. They will need to self-enroll before administrators can reset their master password."
},
"resetPasswordPolicyAutoEnrollCheckbox": { "resetPasswordPolicyAutoEnrollCheckbox": {
"message": "Require new members to be enrolled automatically" "message": "Require new members to be enrolled automatically"
}, },
@@ -5224,8 +5221,8 @@
"message": "Use the", "message": "Use the",
"description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Use the require single-sign-on authentication policy to require all members to log in with SSO.'" "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Use the require single-sign-on authentication policy to require all members to log in with SSO.'"
}, },
"ssoPolicyHelpLink": { "ssoPolicyHelpAnchor": {
"message": "require single-sign-on authentication policy", "message": "require single sign-on authentication policy",
"description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Use the require single-sign-on authentication policy to require all members to log in with SSO.'" "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Use the require single-sign-on authentication policy to require all members to log in with SSO.'"
}, },
"ssoPolicyHelpEnd": { "ssoPolicyHelpEnd": {
@@ -6831,17 +6828,25 @@
"trustedDeviceEncryption": { "trustedDeviceEncryption": {
"message": "Trusted device encryption" "message": "Trusted device encryption"
}, },
"memberDecryptionTdeDescriptionStart": { "memberDecryptionTdeDescriptionPartOne": {
"message": "Once authenticated, members will decrypt vault data using a key stored on their device. The", "message": "Once authenticated, members will decrypt vault data using a key stored on their device. The",
"description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Once authenticated, members will decrypt vault data using a key stored on their device. The account recovery administration policy with automatic enrollment will turn on when this option is used.'" "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Once authenticated, members will decrypt vault data using a key stored on their device. The single organization policy and account recovery administration policy with automatic enrollment will turn on when this option is used.'"
}, },
"memberDecryptionTdeDescriptionLink": { "memberDecryptionTdeDescriptionLinkOne": {
"message": "account recovery administration policy", "message": "single organization",
"description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Once authenticated, members will decrypt vault data using a key stored on their device. The account recovery administration policy with automatic enrollment will turn on when this option is used.'" "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Once authenticated, members will decrypt vault data using a key stored on their device. The single organization policy and account recovery administration policy with automatic enrollment will turn on when this option is used.'"
}, },
"memberDecryptionTdeDescriptionEnd": { "memberDecryptionTdeDescriptionPartTwo": {
"message": "with automatic enrollment will turn on when this option is used.", "message": "policy and ",
"description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Once authenticated, members will decrypt vault data using a key stored on their device. The master password reset policy with automatic enrollment will turn on when this option is used.'" "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Once authenticated, members will decrypt vault data using a key stored on their device. The single organization policy and account recovery administration policy with automatic enrollment will turn on when this option is used.'"
},
"memberDecryptionTdeDescriptionLinkTwo": {
"message": "account recovery administration",
"description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Once authenticated, members will decrypt vault data using a key stored on their device. The single organization policy and account recovery administration policy with automatic enrollment will turn on when this option is used.'"
},
"memberDecryptionTdeDescriptionPartThree": {
"message": "policy with automatic enrollment will turn on when this option is used.",
"description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Once authenticated, members will decrypt vault data using a key stored on their device. The single organization policy and account recovery administration policy with automatic enrollment will turn on when this option is used.'"
}, },
"notFound": { "notFound": {
"message": "$RESOURCE$ not found", "message": "$RESOURCE$ not found",

View File

@@ -618,8 +618,8 @@
"loginWithDevice": { "loginWithDevice": {
"message": "Log ind med enhed" "message": "Log ind med enhed"
}, },
"loginWithDeviceEnabledInfo": { "loginWithDeviceEnabledNote": {
"message": "Log ind med enhed skal være opsat i indstillingerne i Bitwarden mobil-appen. Behov for en anden mulighed?" "message": "Log ind med enhed skal være opsat i indstillingerne i Bitwarden-appen. Brug for en anden mulighed?"
}, },
"loginWithMasterPassword": { "loginWithMasterPassword": {
"message": "Log ind med hovedadgangskode" "message": "Log ind med hovedadgangskode"
@@ -4594,21 +4594,18 @@
"accountRecoveryPolicy": { "accountRecoveryPolicy": {
"message": "Kontogendannelseshåndtering" "message": "Kontogendannelseshåndtering"
}, },
"accountRecoveryPolicyDescription": { "accountRecoveryPolicyDesc": {
"message": "Gendan medlemskonti, når hovedadgangskode eller betroede enheder er glemt eller tabt. Gendannelsesprocesser er baseret på kontokrypteringsmetoden." "message": "Gendanner baseret på krypteringsmetoden konti, når hovedadgangskoder eller betroede enheder er glemt eller tabt."
}, },
"resetPasswordPolicyWarning": { "accountRecoveryPolicyWarning": {
"message": "Organisationsmedlemmer skal selvindrullere eller være auto-indrulleret, før admins kan nulstille deres hovedadgangskode." "message": "For eksisterende konti med hovedadgangskoder kræves, at medlemmer selv-indrullere sig, før administratorer kan gendanne deres konti. Automatisk indrullering vil slå kontogendannelse til for nye medlemmer."
},
"accountRecoverySingleOrgRequirementDesc": {
"message": "Enkeltorganisations Virksomhedspolitik skal være slået til, før denne politik aktiveres."
}, },
"resetPasswordPolicyAutoEnroll": { "resetPasswordPolicyAutoEnroll": {
"message": "Auto-indrullering" "message": "Auto-indrullering"
}, },
"resetPasswordPolicyAutoEnrollDescription": {
"message": "Adgangskodenulstilling indrullerer automatisk alle medlemmer (uden mulighed for afmelding), når deres invitation accepteres."
},
"resetPasswordPolicyAutoEnrollWarning": {
"message": "Adgangskodenulstilling indrullerer ikke allerede eksisterernde organisationmedlemmer med tilbagevirkende kraft. De skal selvindrullere, før admins kan nulstille deres hovedadgangskode."
},
"resetPasswordPolicyAutoEnrollCheckbox": { "resetPasswordPolicyAutoEnrollCheckbox": {
"message": "Kræv auto-indrullering af nye brugere" "message": "Kræv auto-indrullering af nye brugere"
}, },
@@ -5224,7 +5221,7 @@
"message": "Brug", "message": "Brug",
"description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Use the require single-sign-on authentication policy to require all members to log in with SSO.'" "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Use the require single-sign-on authentication policy to require all members to log in with SSO.'"
}, },
"ssoPolicyHelpLink": { "ssoPolicyHelpAnchor": {
"message": "kræv Single Sign-On godkendelsespolitik", "message": "kræv Single Sign-On godkendelsespolitik",
"description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Use the require single-sign-on authentication policy to require all members to log in with SSO.'" "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Use the require single-sign-on authentication policy to require all members to log in with SSO.'"
}, },
@@ -6627,7 +6624,7 @@
"message": "Hemmelighedshåndtering" "message": "Hemmelighedshåndtering"
}, },
"secretsManagerAccessDescription": { "secretsManagerAccessDescription": {
"message": "Activate user access to Secrets Manager." "message": "Aktivér brugeradgang til Hemmelighedshåndtering."
}, },
"userAccessSecretsManagerGA": { "userAccessSecretsManagerGA": {
"message": "Denne bruger kan tilgå Hemmelighedshåndtering" "message": "Denne bruger kan tilgå Hemmelighedshåndtering"
@@ -6831,17 +6828,25 @@
"trustedDeviceEncryption": { "trustedDeviceEncryption": {
"message": "Betroet enhed-kryptering" "message": "Betroet enhed-kryptering"
}, },
"memberDecryptionTdeDescriptionStart": { "memberDecryptionTdeDescriptionPartOne": {
"message": "Når godkendt, dekrypterer medlemmet boksdata vha. en nøgle gemt på enheden.", "message": "Når godkendt, dekrypterer medlemmet boksdata vha. en nøgle gemt på deres enhed. ",
"description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Once authenticated, members will decrypt vault data using a key stored on their device. The account recovery administration policy with automatic enrollment will turn on when this option is used.'" "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Once authenticated, members will decrypt vault data using a key stored on their device. The single organization policy and account recovery administration policy with automatic enrollment will turn on when this option is used.'"
}, },
"memberDecryptionTdeDescriptionLink": { "memberDecryptionTdeDescriptionLinkOne": {
"message": "Kontogendannelseshåndteringspolitikken", "message": "Enkeltorganisationspolitik",
"description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Once authenticated, members will decrypt vault data using a key stored on their device. The account recovery administration policy with automatic enrollment will turn on when this option is used.'" "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Once authenticated, members will decrypt vault data using a key stored on their device. The single organization policy and account recovery administration policy with automatic enrollment will turn on when this option is used.'"
}, },
"memberDecryptionTdeDescriptionEnd": { "memberDecryptionTdeDescriptionPartTwo": {
"message": "med automatisk indrullering aktiveres ved brug af denne indstilling.", "message": "og ",
"description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Once authenticated, members will decrypt vault data using a key stored on their device. The master password reset policy with automatic enrollment will turn on when this option is used.'" "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Once authenticated, members will decrypt vault data using a key stored on their device. The single organization policy and account recovery administration policy with automatic enrollment will turn on when this option is used.'"
},
"memberDecryptionTdeDescriptionLinkTwo": {
"message": "kontogendannelseshåndteringspolitik",
"description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Once authenticated, members will decrypt vault data using a key stored on their device. The single organization policy and account recovery administration policy with automatic enrollment will turn on when this option is used.'"
},
"memberDecryptionTdeDescriptionPartThree": {
"message": "med automatisk indrullering slås til ved brug af denne indstilling.",
"description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Once authenticated, members will decrypt vault data using a key stored on their device. The single organization policy and account recovery administration policy with automatic enrollment will turn on when this option is used.'"
}, },
"notFound": { "notFound": {
"message": "$RESOURCE$ ikke fundet", "message": "$RESOURCE$ ikke fundet",
@@ -6859,18 +6864,18 @@
"message": "Bruger har opdateret en adgangskode udstedt via kontogendannelse." "message": "Bruger har opdateret en adgangskode udstedt via kontogendannelse."
}, },
"activatedAccessToSecretsManager": { "activatedAccessToSecretsManager": {
"message": "Activated access to Secrets Manager", "message": "Aktivér adgang til Hemmelighedshåndtering",
"description": "Confirmation message that one or more users gained access to Secrets Manager" "description": "Confirmation message that one or more users gained access to Secrets Manager"
}, },
"activateAccess": { "activateAccess": {
"message": "Activate access" "message": "Aktivere adgang"
}, },
"bulkEnableSecretsManagerDescription": { "bulkEnableSecretsManagerDescription": {
"message": "Tildel flg. medlemmers adgang til Hemmelighedshåndtering. Den i Adgangskodehåndtering tildelte rolle vil gælde for Hemmelighedshåndtering.", "message": "Tildel flg. medlemmers adgang til Hemmelighedshåndtering. Den i Adgangskodehåndtering tildelte rolle vil gælde for Hemmelighedshåndtering.",
"description": "This description is shown to an admin when they are attempting to add more users to Secrets Manager." "description": "This description is shown to an admin when they are attempting to add more users to Secrets Manager."
}, },
"activateSecretsManager": { "activateSecretsManager": {
"message": "Activate Secrets Manager" "message": "Aktivere Hemmelighedshåndtering"
}, },
"yourOrganizationsFingerprint": { "yourOrganizationsFingerprint": {
"message": "Din organisations fingeraftrykssætning", "message": "Din organisations fingeraftrykssætning",

View File

@@ -618,7 +618,7 @@
"loginWithDevice": { "loginWithDevice": {
"message": "Mit Gerät anmelden" "message": "Mit Gerät anmelden"
}, },
"loginWithDeviceEnabledInfo": { "loginWithDeviceEnabledNote": {
"message": "Die Anmeldung über ein Gerät muss in den Einstellungen der Bitwarden App eingerichtet werden. Benötigst du eine andere Option?" "message": "Die Anmeldung über ein Gerät muss in den Einstellungen der Bitwarden App eingerichtet werden. Benötigst du eine andere Option?"
}, },
"loginWithMasterPassword": { "loginWithMasterPassword": {
@@ -4594,21 +4594,18 @@
"accountRecoveryPolicy": { "accountRecoveryPolicy": {
"message": "Kontowiederherstellungsverwaltung" "message": "Kontowiederherstellungsverwaltung"
}, },
"accountRecoveryPolicyDescription": { "accountRecoveryPolicyDesc": {
"message": "Mitgliederkonten wiederherstellen, falls das Master-Passwort oder vertrauenswürdige Geräte vergessen werden oder verloren gehen. Der Wiederherstellungsprozess basiert auf der Verschlüsselungsmethode des Kontos." "message": "Based on the encryption method, recover accounts when master passwords or trusted devices are forgotten or lost."
}, },
"resetPasswordPolicyWarning": { "accountRecoveryPolicyWarning": {
"message": "Benutzer in der Organisation müssen sich selbst registrieren oder automatisch registriert sein, bevor Administratoren deren Master-Passwort zurücksetzen können." "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."
}, },
"resetPasswordPolicyAutoEnroll": { "resetPasswordPolicyAutoEnroll": {
"message": "Automatische Registrierung" "message": "Automatische Registrierung"
}, },
"resetPasswordPolicyAutoEnrollDescription": {
"message": "Alle Benutzer werden automatisch für die Passwortzurücksetzung registriert, sobald ihre Einladung angenommen wurde und können sich nicht davon abmelden."
},
"resetPasswordPolicyAutoEnrollWarning": {
"message": "Benutzer, die bereits in der Organisation sind, werden nicht rückwirkend für Passwortzurücksetzung registriert. Sie müssen sich selbst registrieren, bevor Administratoren deren Master-Passwort zurücksetzen können."
},
"resetPasswordPolicyAutoEnrollCheckbox": { "resetPasswordPolicyAutoEnrollCheckbox": {
"message": "Neue Mitglieder müssen automatisch registriert werden" "message": "Neue Mitglieder müssen automatisch registriert werden"
}, },
@@ -5224,8 +5221,8 @@
"message": "Aktiviere die", "message": "Aktiviere die",
"description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Use the require single-sign-on authentication policy to require all members to log in with SSO.'" "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Use the require single-sign-on authentication policy to require all members to log in with SSO.'"
}, },
"ssoPolicyHelpLink": { "ssoPolicyHelpAnchor": {
"message": "SSO-Authentifizierungsrichtlinie", "message": "require single sign-on authentication policy",
"description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Use the require single-sign-on authentication policy to require all members to log in with SSO.'" "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Use the require single-sign-on authentication policy to require all members to log in with SSO.'"
}, },
"ssoPolicyHelpEnd": { "ssoPolicyHelpEnd": {
@@ -6627,7 +6624,7 @@
"message": "Secrets Manager" "message": "Secrets Manager"
}, },
"secretsManagerAccessDescription": { "secretsManagerAccessDescription": {
"message": "Activate user access to Secrets Manager." "message": "Benutzerzugriff zum Secrets Manager aktivieren"
}, },
"userAccessSecretsManagerGA": { "userAccessSecretsManagerGA": {
"message": "Dieser Benutzer kann auf Secrets Manager zugreifen." "message": "Dieser Benutzer kann auf Secrets Manager zugreifen."
@@ -6831,17 +6828,25 @@
"trustedDeviceEncryption": { "trustedDeviceEncryption": {
"message": "Vertrauenswürdige Geräteverschlüsselung" "message": "Vertrauenswürdige Geräteverschlüsselung"
}, },
"memberDecryptionTdeDescriptionStart": { "memberDecryptionTdeDescriptionPartOne": {
"message": "Einmal authentifiziert, entschlüsseln Mitglieder Tresordaten mit einem Schlüssel auf ihrem Gerät. Die", "message": "Once authenticated, members will decrypt vault data using a key stored on their device. The",
"description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Once authenticated, members will decrypt vault data using a key stored on their device. The account recovery administration policy with automatic enrollment will turn on when this option is used.'" "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Once authenticated, members will decrypt vault data using a key stored on their device. The single organization policy and account recovery administration policy with automatic enrollment will turn on when this option is used.'"
}, },
"memberDecryptionTdeDescriptionLink": { "memberDecryptionTdeDescriptionLinkOne": {
"message": "Verwaltungsrichtlinie zur Kontowiederherstellung", "message": "single organization",
"description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Once authenticated, members will decrypt vault data using a key stored on their device. The account recovery administration policy with automatic enrollment will turn on when this option is used.'" "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Once authenticated, members will decrypt vault data using a key stored on their device. The single organization policy and account recovery administration policy with automatic enrollment will turn on when this option is used.'"
}, },
"memberDecryptionTdeDescriptionEnd": { "memberDecryptionTdeDescriptionPartTwo": {
"message": "mit automatischer Registrierung wird aktiviert, wenn diese Option verwendet wird.", "message": "policy and ",
"description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Once authenticated, members will decrypt vault data using a key stored on their device. The master password reset policy with automatic enrollment will turn on when this option is used.'" "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Once authenticated, members will decrypt vault data using a key stored on their device. The single organization policy and account recovery administration policy with automatic enrollment will turn on when this option is used.'"
},
"memberDecryptionTdeDescriptionLinkTwo": {
"message": "account recovery administration",
"description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Once authenticated, members will decrypt vault data using a key stored on their device. The single organization policy and account recovery administration policy with automatic enrollment will turn on when this option is used.'"
},
"memberDecryptionTdeDescriptionPartThree": {
"message": "policy with automatic enrollment will turn on when this option is used.",
"description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Once authenticated, members will decrypt vault data using a key stored on their device. The single organization policy and account recovery administration policy with automatic enrollment will turn on when this option is used.'"
}, },
"notFound": { "notFound": {
"message": "$RESOURCE$ nicht gefunden", "message": "$RESOURCE$ nicht gefunden",
@@ -6859,18 +6864,18 @@
"message": "Der Benutzer hat ein Passwort aktualisiert, das durch die Kontowiederherstellung ausgestellt wurde." "message": "Der Benutzer hat ein Passwort aktualisiert, das durch die Kontowiederherstellung ausgestellt wurde."
}, },
"activatedAccessToSecretsManager": { "activatedAccessToSecretsManager": {
"message": "Activated access to Secrets Manager", "message": "Zugriff zum Secrets Manager aktiviert",
"description": "Confirmation message that one or more users gained access to Secrets Manager" "description": "Confirmation message that one or more users gained access to Secrets Manager"
}, },
"activateAccess": { "activateAccess": {
"message": "Activate access" "message": "Zugriff aktivieren"
}, },
"bulkEnableSecretsManagerDescription": { "bulkEnableSecretsManagerDescription": {
"message": "Den folgenden Mitgliedern Zugriff auf Secrets Manager gewähren. Die im Passwort-Manager zugewiesene Rolle wird auf Secrets Manager angewendet.", "message": "Den folgenden Mitgliedern Zugriff auf Secrets Manager gewähren. Die im Passwort-Manager zugewiesene Rolle wird auf Secrets Manager angewendet.",
"description": "This description is shown to an admin when they are attempting to add more users to Secrets Manager." "description": "This description is shown to an admin when they are attempting to add more users to Secrets Manager."
}, },
"activateSecretsManager": { "activateSecretsManager": {
"message": "Activate Secrets Manager" "message": "Secrets Manager aktivieren"
}, },
"yourOrganizationsFingerprint": { "yourOrganizationsFingerprint": {
"message": "Fingerabdruck-Phrase deiner Organisation", "message": "Fingerabdruck-Phrase deiner Organisation",

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