Merge branch 'main' into sm/sm-910

This commit is contained in:
Thomas Avery 2024-04-16 10:55:32 -05:00 committed by GitHub
commit e39d643fd9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
343 changed files with 21214 additions and 2164 deletions

View File

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

View File

@ -3005,5 +3005,11 @@
},
"passkeyRemoved": {
"message": "Passkey removed"
},
"unassignedItemsBanner": {
"message": "Notice: Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."
},
"unassignedItemsBannerSelfHost": {
"message": "Notice: On May 2, 2024, unassigned organization items will no longer be visible in the All Vaults view and will only be accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."
}
}

View File

@ -3005,5 +3005,11 @@
},
"passkeyRemoved": {
"message": "Parol silindi"
},
"unassignedItemsBanner": {
"message": "Bildiriş: Təyin edilməmiş təşkilat elementləri artıq Bütün Anbarlar görünüşündə görünməyəndir və yalnız Admin Konsolu vasitəsilə əlçatandır. Bu elementləri görünən etmək üçün Admin Konsolundan bir kolleksiyaya təyin edin."
},
"unassignedItemsBannerSelfHost": {
"message": "Bildiriş: 2 May 2024-cü ildən etibarən təyin edilməmiş təşkilat elementləri artıq Bütün Anbarlar görünüşündə görünməyən və yalnız Admin Konsolu vasitəsilə əlçatan olacaq. Bu elementləri görünən etmək üçün Admin Konsolundan bir kolleksiyaya təyin edin."
}
}

View File

@ -3005,5 +3005,11 @@
},
"passkeyRemoved": {
"message": "Passkey removed"
},
"unassignedItemsBanner": {
"message": "Notice: Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."
},
"unassignedItemsBannerSelfHost": {
"message": "Notice: On May 2, 2024, unassigned organization items will no longer be visible in the All Vaults view and will only be accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."
}
}

View File

@ -3005,5 +3005,11 @@
},
"passkeyRemoved": {
"message": "Секретният ключ е премахнат"
},
"unassignedItemsBanner": {
"message": "Известие: неразпределените елементи на организацията вече не се виждат в изгледа с „Всички трезори“, а са достъпни само през Административната конзола. Добавете тези елементи към някоя колекция в Административната конзола, за да станат видими."
},
"unassignedItemsBannerSelfHost": {
"message": "Известие: от 2 май 2024г. неразпределените елементи на организациите вече няма се виждат в изгледа с „Всички трезори“, а ще бъдат достъпни само през Административната конзола. Добавете тези елементи към някоя колекция в Административната конзола, за да станат видими."
}
}

View File

@ -3005,5 +3005,11 @@
},
"passkeyRemoved": {
"message": "Passkey removed"
},
"unassignedItemsBanner": {
"message": "Notice: Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."
},
"unassignedItemsBannerSelfHost": {
"message": "Notice: On May 2, 2024, unassigned organization items will no longer be visible in the All Vaults view and will only be accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."
}
}

View File

@ -3005,5 +3005,11 @@
},
"passkeyRemoved": {
"message": "Passkey removed"
},
"unassignedItemsBanner": {
"message": "Notice: Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."
},
"unassignedItemsBannerSelfHost": {
"message": "Notice: On May 2, 2024, unassigned organization items will no longer be visible in the All Vaults view and will only be accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."
}
}

View File

@ -3001,9 +3001,15 @@
"description": "Notification message for when saving credentials has failed."
},
"removePasskey": {
"message": "Remove passkey"
"message": "Suprimeix la clau de pas"
},
"passkeyRemoved": {
"message": "Passkey removed"
"message": "Clau de pas suprimida"
},
"unassignedItemsBanner": {
"message": "Nota: els elements de l'organització sense assignar ja no es veuran a la vista \"Totes les caixes fortes\" i només es veuran des de la consola d'administració. Assigneu-los-hi una col·lecció des de la consola per fer-los visibles."
},
"unassignedItemsBannerSelfHost": {
"message": "Notice: On May 2, 2024, unassigned organization items will no longer be visible in the All Vaults view and will only be accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."
}
}

View File

@ -3005,5 +3005,11 @@
},
"passkeyRemoved": {
"message": "Přístupový klíč byl odebrán"
},
"unassignedItemsBanner": {
"message": "Upozornění: Nepřiřazené položky organizace již nejsou viditelné ve Vašem zobrazení všech trezorů a jsou nyní přístupné jen v konzoli správce. Přiřaďte tyto položky do kolekce z konzole pro správce, aby byly viditelné."
},
"unassignedItemsBannerSelfHost": {
"message": "Upozornění: Dne 2. května 2024 již nebudou nepřiřazené položky organizace viditelné v zobrazení Všechny trezory a budou přístupné jen prostřednictvím konzoly správce. Přiřaďte tyto položky do kolekce z konzoly pro správce, aby byly viditelné."
}
}

View File

@ -3005,5 +3005,11 @@
},
"passkeyRemoved": {
"message": "Passkey removed"
},
"unassignedItemsBanner": {
"message": "Notice: Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."
},
"unassignedItemsBannerSelfHost": {
"message": "Notice: On May 2, 2024, unassigned organization items will no longer be visible in the All Vaults view and will only be accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."
}
}

View File

@ -3005,5 +3005,11 @@
},
"passkeyRemoved": {
"message": "Adgangsnøgle fjernet"
},
"unassignedItemsBanner": {
"message": "Bemærk: Utildelte organisationsemner er ikke længere synlige i Alle Bokse-visningen og er kun tilgængelige via Adminkonsollen. Føj disse emner til en samling fra Adminkonsollen for at gøre dem synlige."
},
"unassignedItemsBannerSelfHost": {
"message": "Bemærk: Pr. 2. maj 2024 vil utildelte organisationsemner ikke længere være synlige i Alle Bokse-visningen og vil kun være tilgængelige via Admin-konsollen. Tildel disse emner til en samling via Admin-konsollen for at gøre dem synlige."
}
}

View File

@ -2706,7 +2706,7 @@
"message": "Für dein Konto ist die Duo Zwei-Faktor-Authentifizierung erforderlich."
},
"popoutTheExtensionToCompleteLogin": {
"message": "Popout the extension to complete login."
"message": "Koppel die Erweiterung ab, um die Anmeldung abzuschließen."
},
"popoutExtension": {
"message": "Popout-Erweiterung"
@ -3001,9 +3001,15 @@
"description": "Notification message for when saving credentials has failed."
},
"removePasskey": {
"message": "Passkey löschen"
"message": "Passkey entfernen"
},
"passkeyRemoved": {
"message": "Passkey gelöscht"
"message": "Passkey entfernt"
},
"unassignedItemsBanner": {
"message": "Hinweis: Nicht zugeordnete Organisationseinträge sind nicht mehr in der Ansicht aller Tresore sichtbar und nur über die Administrator-Konsole zugänglich. Weise diese Einträge einer Sammlung aus der Administrator-Konsole zu, um sie sichtbar zu machen."
},
"unassignedItemsBannerSelfHost": {
"message": "Hinweis: Ab dem 2. Mai 2024 werden nicht zugewiesene Organisationseinträge nicht mehr in der Ansicht aller Tresore sichtbar sein und sind nur über die Administrator-Konsole zugänglich. Weise diese Elemente einer Sammlung aus der Administrator-Konsole zu, um sie sichtbar zu machen."
}
}

View File

@ -3005,5 +3005,11 @@
},
"passkeyRemoved": {
"message": "Passkey removed"
},
"unassignedItemsBanner": {
"message": "Notice: Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."
},
"unassignedItemsBannerSelfHost": {
"message": "Notice: On May 2, 2024, unassigned organization items will no longer be visible in the All Vaults view and will only be accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."
}
}

View File

@ -3005,5 +3005,11 @@
},
"passkeyRemoved": {
"message": "Passkey removed"
},
"unassignedItemsBanner": {
"message": "Notice: Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."
},
"unassignedItemsBannerSelfHost": {
"message": "Notice: On May 2, 2024, unassigned organization items will no longer be visible in the All Vaults view and will only be accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."
}
}

View File

@ -3005,5 +3005,11 @@
},
"passkeyRemoved": {
"message": "Passkey removed"
},
"unassignedItemsBanner": {
"message": "Notice: Unassigned organisation items are no longer visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."
},
"unassignedItemsBannerSelfHost": {
"message": "Notice: On May 2, 2024, unassigned organisation items will no longer be visible in the All Vaults view and will only be accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."
}
}

View File

@ -3005,5 +3005,11 @@
},
"passkeyRemoved": {
"message": "Passkey removed"
},
"unassignedItemsBanner": {
"message": "Notice: Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."
},
"unassignedItemsBannerSelfHost": {
"message": "Notice: On May 2, 2024, unassigned organization items will no longer be visible in the All Vaults view and will only be accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."
}
}

View File

@ -3005,5 +3005,11 @@
},
"passkeyRemoved": {
"message": "Passkey removed"
},
"unassignedItemsBanner": {
"message": "Notice: Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."
},
"unassignedItemsBannerSelfHost": {
"message": "Notice: On May 2, 2024, unassigned organization items will no longer be visible in the All Vaults view and will only be accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."
}
}

View File

@ -3001,9 +3001,15 @@
"description": "Notification message for when saving credentials has failed."
},
"removePasskey": {
"message": "Remove passkey"
"message": "Eemalda pääsuvõti"
},
"passkeyRemoved": {
"message": "Passkey removed"
"message": "Pääsuvõti on eemaldatud"
},
"unassignedItemsBanner": {
"message": "Notice: Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."
},
"unassignedItemsBannerSelfHost": {
"message": "Notice: On May 2, 2024, unassigned organization items will no longer be visible in the All Vaults view and will only be accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."
}
}

View File

@ -95,10 +95,10 @@
"message": "Auto-fill login"
},
"autoFillCard": {
"message": "Auto-fill card"
"message": "Auto-bete txartela"
},
"autoFillIdentity": {
"message": "Auto-fill identity"
"message": "Auto-bete nortasuna"
},
"generatePasswordCopied": {
"message": "Sortu pasahitza (kopiatuta)"
@ -110,19 +110,19 @@
"message": "Bat datozen saio-hasierarik gabe"
},
"noCards": {
"message": "No cards"
"message": "Txartelik ez"
},
"noIdentities": {
"message": "No identities"
"message": "Nortasunik ez"
},
"addLoginMenu": {
"message": "Add login"
},
"addCardMenu": {
"message": "Add card"
"message": "Gehitu txartela"
},
"addIdentityMenu": {
"message": "Add identity"
"message": "Gehitu nortasuna"
},
"unlockVaultMenu": {
"message": "Desblokeatu kutxa gotorra"
@ -223,10 +223,10 @@
"message": "Bitwarden Laguntza zentroa"
},
"communityForums": {
"message": "Explore Bitwarden community forums"
"message": "Esploratu Bitwarden komunitatearen foroak"
},
"contactSupport": {
"message": "Contact Bitwarden support"
"message": "Jarri harremanetan Bitwardeneko laguntza taldearekin"
},
"sync": {
"message": "Sinkronizatu"
@ -269,7 +269,7 @@
"message": "Luzera"
},
"passwordMinLength": {
"message": "Minimum password length"
"message": "Pasahitzaren gutxieneko luzera"
},
"uppercase": {
"message": "Letra larria (A-Z)"
@ -1064,7 +1064,7 @@
"message": "Edit browser settings."
},
"autofillOverlayVisibilityOff": {
"message": "Off",
"message": "Itzalita",
"description": "Overlay setting select option for disabling autofill overlay"
},
"autofillOverlayVisibilityOnFieldFocus": {
@ -1592,10 +1592,10 @@
"message": "Ezarri pasahitz nagusia"
},
"currentMasterPass": {
"message": "Current master password"
"message": "Oraingo pasahitz nagusia"
},
"newMasterPass": {
"message": "New master password"
"message": "Pasahitz nagusi berria"
},
"confirmNewMasterPass": {
"message": "Confirm new master password"
@ -2266,10 +2266,10 @@
"message": "Please make sure your vault is unlocked and the Fingerprint phrase matches on the other device."
},
"resendNotification": {
"message": "Resend notification"
"message": "Berbidali jakinarazpena"
},
"viewAllLoginOptions": {
"message": "View all log in options"
"message": "Ikusi erregistro guztiak ezarpenetan"
},
"notificationSentDevice": {
"message": "A notification has been sent to your device."
@ -2293,13 +2293,13 @@
"message": "Check known data breaches for this password"
},
"important": {
"message": "Important:"
"message": "Garrantzitsua:"
},
"masterPasswordHint": {
"message": "Your master password cannot be recovered if you forget it!"
},
"characterMinimum": {
"message": "$LENGTH$ character minimum",
"message": "$LENGTH$ karaktere gutxienez",
"placeholders": {
"length": {
"content": "$1",
@ -2326,7 +2326,7 @@
"message": "Select an item from this screen, or explore other options in settings."
},
"gotIt": {
"message": "Got it"
"message": "Ulertuta"
},
"autofillSettings": {
"message": "Auto-fill settings"
@ -2359,25 +2359,25 @@
"message": "Logging in on"
},
"opensInANewWindow": {
"message": "Opens in a new window"
"message": "Leiho berri batean irekitzen da"
},
"deviceApprovalRequired": {
"message": "Device approval required. Select an approval option below:"
},
"rememberThisDevice": {
"message": "Remember this device"
"message": "Gogoratu gailu hau"
},
"uncheckIfPublicDevice": {
"message": "Uncheck if using a public device"
},
"approveFromYourOtherDevice": {
"message": "Approve from your other device"
"message": "Onartu zure beste gailutik"
},
"requestAdminApproval": {
"message": "Request admin approval"
"message": "Eskatu administratzailearen onarpena"
},
"approveWithMasterPassword": {
"message": "Approve with master password"
"message": "Onartu pasahitz nagusiarekin"
},
"ssoIdentifierRequired": {
"message": "Organization SSO identifier is required."
@ -2390,31 +2390,31 @@
"message": "Access denied. You do not have permission to view this page."
},
"general": {
"message": "General"
"message": "Orokorra"
},
"display": {
"message": "Display"
"message": "Bistaratzea"
},
"accountSuccessfullyCreated": {
"message": "Account successfully created!"
"message": "Kontua zuzen sortu da!"
},
"adminApprovalRequested": {
"message": "Admin approval requested"
"message": "Administratzailearen onarpena eskatuta"
},
"adminApprovalRequestSentToAdmins": {
"message": "Your request has been sent to your admin."
"message": "Zure eskaera zure administratzaileari bidali zaio."
},
"youWillBeNotifiedOnceApproved": {
"message": "You will be notified once approved."
"message": "Jakinaraziko zaizu onartzen denean."
},
"troubleLoggingIn": {
"message": "Trouble logging in?"
"message": "Arazoak saioa hasterakoan?"
},
"loginApproved": {
"message": "Login approved"
},
"userEmailMissing": {
"message": "User email missing"
"message": "Erabiltzailearen emaila falta da"
},
"deviceTrusted": {
"message": "Device trusted"
@ -2540,19 +2540,19 @@
"description": "Notification button text for starting a fileless import."
},
"importing": {
"message": "Importing...",
"message": "Inportatzen...",
"description": "Notification message for when an import is in progress."
},
"dataSuccessfullyImported": {
"message": "Data successfully imported!",
"message": "Datuak zuzen inportatu dira!",
"description": "Notification message for when an import has completed successfully."
},
"dataImportFailed": {
"message": "Error importing. Check console for details.",
"message": "Errorea gertatu da inportatzean. Begiratu xehetasunak kontsolan.",
"description": "Notification message for when an import has failed."
},
"importNetworkError": {
"message": "Network error encountered during import.",
"message": "Sareko errorea gertatu da inportatzerakoan.",
"description": "Notification message for when an import has failed due to a network error."
},
"aliasDomain": {
@ -2602,11 +2602,11 @@
"description": "Screen reader text for when a login item is focused where a partial username is displayed. SR will announce this phrase before reading the text of the partial username"
},
"noItemsToShow": {
"message": "No items to show",
"message": "Ez dago elementurik erakusteko",
"description": "Text to show in overlay if there are no matching items"
},
"newItem": {
"message": "New item",
"message": "Elementu berria",
"description": "Button text to display in overlay when there are no matching items"
},
"addNewVaultItem": {
@ -2618,32 +2618,32 @@
"description": "Screen reader text for announcing when the overlay opens on the page"
},
"turnOn": {
"message": "Turn on"
"message": "Piztu"
},
"ignore": {
"message": "Ignore"
"message": "Ezikusi"
},
"importData": {
"message": "Import data",
"message": "Inportatu datuak",
"description": "Used for the header of the import dialog, the import button and within the file-password-prompt"
},
"importError": {
"message": "Import error"
"message": "Errorea inportatzerakoan"
},
"importErrorDesc": {
"message": "There was a problem with the data you tried to import. Please resolve the errors listed below in your source file and try again."
"message": "Inportatzen saiatu zaren datuekin arazo bat egon da. Mesedez, konpondu ondoren adierazten diren akatsak eta saiatu berriro."
},
"resolveTheErrorsBelowAndTryAgain": {
"message": "Resolve the errors below and try again."
"message": "Konpondu beheko akatsak eta saiatu berriro."
},
"description": {
"message": "Description"
"message": "Deskribapena"
},
"importSuccess": {
"message": "Data successfully imported"
"message": "Datuak zuzen inportatu dira"
},
"importSuccessNumberOfItems": {
"message": "A total of $AMOUNT$ items were imported.",
"message": "Guztira $AMOUNT$ elementu inportatu dira.",
"placeholders": {
"amount": {
"content": "$1",
@ -2652,7 +2652,7 @@
}
},
"tryAgain": {
"message": "Try again"
"message": "Saiatu berriro"
},
"verificationRequiredForActionSetPinToContinue": {
"message": "Verification required for this action. Set a PIN to continue."
@ -2661,10 +2661,10 @@
"message": "Set PIN"
},
"verifyWithBiometrics": {
"message": "Verify with biometrics"
"message": "Egiaztatu biometria erabiliz"
},
"awaitingConfirmation": {
"message": "Awaiting confirmation"
"message": "Baieztapenaren zain"
},
"couldNotCompleteBiometrics": {
"message": "Could not complete biometrics."
@ -2673,13 +2673,13 @@
"message": "Need a different method?"
},
"useMasterPassword": {
"message": "Use master password"
"message": "Erabili pasahitz nagusia"
},
"usePin": {
"message": "Use PIN"
"message": "Erabili PIN kodea"
},
"useBiometrics": {
"message": "Use biometrics"
"message": "Erabili biometria"
},
"enterVerificationCodeSentToEmail": {
"message": "Enter the verification code that was sent to your email."
@ -2688,10 +2688,10 @@
"message": "Resend code"
},
"total": {
"message": "Total"
"message": "Guztira"
},
"importWarning": {
"message": "You are importing data to $ORGANIZATION$. Your data may be shared with members of this organization. Do you want to proceed?",
"message": "$ORGANIZATION$(e)ra datuak inportatzen ari zara. Zure datuak erakunde horretako kideekin parteka daitezke. Jarraitu nahi duzu?",
"placeholders": {
"organization": {
"content": "$1",
@ -2810,7 +2810,7 @@
"message": "You do not have a matching login for this site."
},
"confirm": {
"message": "Confirm"
"message": "Berretsi"
},
"savePasskey": {
"message": "Save passkey"
@ -3005,5 +3005,11 @@
},
"passkeyRemoved": {
"message": "Passkey removed"
},
"unassignedItemsBanner": {
"message": "Notice: Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."
},
"unassignedItemsBannerSelfHost": {
"message": "Notice: On May 2, 2024, unassigned organization items will no longer be visible in the All Vaults view and will only be accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."
}
}

View File

@ -3005,5 +3005,11 @@
},
"passkeyRemoved": {
"message": "Passkey removed"
},
"unassignedItemsBanner": {
"message": "Notice: Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."
},
"unassignedItemsBannerSelfHost": {
"message": "Notice: On May 2, 2024, unassigned organization items will no longer be visible in the All Vaults view and will only be accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."
}
}

View File

@ -11,7 +11,7 @@
"description": "Extension description"
},
"loginOrCreateNewAccount": {
"message": "Käytä salattua holviasi kirjautumalla sisään tai tai luo uusi tili."
"message": "Käytä salattua holviasi kirjautumalla sisään tai luo uusi tili."
},
"createAccount": {
"message": "Luo tili"
@ -802,7 +802,7 @@
"message": "Lue lisää"
},
"authenticatorKeyTotp": {
"message": "Todennusaavain (TOTP)"
"message": "Todennusavain (TOTP)"
},
"verificationCodeTotp": {
"message": "Todennuskoodi (TOTP)"
@ -3005,5 +3005,11 @@
},
"passkeyRemoved": {
"message": "Suojausavain poistettiin"
},
"unassignedItemsBanner": {
"message": "Huomautus: Organisaatioiden kokoelmiin määrittämättömät kohteet eivät enää näy laitteiden \"Kaikki holvit\" -näkymissä, vaan ne ovat nähtävissä vain Hallintapaneelista. Määritä kohteet kokoelmiin Hallintapaneelista, jotta ne ovat jatkossakin käytettävissä kaikilta laitteilta."
},
"unassignedItemsBannerSelfHost": {
"message": "Huomautus: 2.5.2024 alkaen kokoelmiin määrittämättömät organisaatioiden kohteet eivät enää näy laitteiden \"Kaikki holvit\" -näkymissä, vaan ne ovat nähtävissä vain Hallintapaneelista. Määritä kohteet kokoelmiin Hallintapaneelista, jotta ne ovat jatkossakin käytettävissä kaikilta laitteilta."
}
}

View File

@ -3005,5 +3005,11 @@
},
"passkeyRemoved": {
"message": "Passkey removed"
},
"unassignedItemsBanner": {
"message": "Notice: Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."
},
"unassignedItemsBannerSelfHost": {
"message": "Notice: On May 2, 2024, unassigned organization items will no longer be visible in the All Vaults view and will only be accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."
}
}

View File

@ -3005,5 +3005,11 @@
},
"passkeyRemoved": {
"message": "Clé d'identification (passkey) retirée"
},
"unassignedItemsBanner": {
"message": "Notice : les éléments d'organisation non assignés ne sont plus visibles dans la vue Tous les coffres et sont uniquement accessibles via la console d'administration. Assignez ces éléments à une collection à partir de la console d'administration pour les rendre visibles."
},
"unassignedItemsBannerSelfHost": {
"message": "Remarque : au 2 mai 2024, les éléments d'organisation non assignés ne sont plus visibles dans votre vue Tous les coffres sur tous les appareils et sont uniquement accessibles via la Console d'administration. Assignez ces éléments à une collection à partir de la Console d'administration pour les rendre visibles."
}
}

View File

@ -3005,5 +3005,11 @@
},
"passkeyRemoved": {
"message": "Passkey removed"
},
"unassignedItemsBanner": {
"message": "Notice: Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."
},
"unassignedItemsBannerSelfHost": {
"message": "Notice: On May 2, 2024, unassigned organization items will no longer be visible in the All Vaults view and will only be accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."
}
}

View File

@ -3005,5 +3005,11 @@
},
"passkeyRemoved": {
"message": "Passkey removed"
},
"unassignedItemsBanner": {
"message": "Notice: Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."
},
"unassignedItemsBannerSelfHost": {
"message": "Notice: On May 2, 2024, unassigned organization items will no longer be visible in the All Vaults view and will only be accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."
}
}

View File

@ -3005,5 +3005,11 @@
},
"passkeyRemoved": {
"message": "Passkey removed"
},
"unassignedItemsBanner": {
"message": "Notice: Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."
},
"unassignedItemsBannerSelfHost": {
"message": "Notice: On May 2, 2024, unassigned organization items will no longer be visible in the All Vaults view and will only be accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."
}
}

View File

@ -3005,5 +3005,11 @@
},
"passkeyRemoved": {
"message": "Passkey removed"
},
"unassignedItemsBanner": {
"message": "Notice: Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."
},
"unassignedItemsBannerSelfHost": {
"message": "Notice: On May 2, 2024, unassigned organization items will no longer be visible in the All Vaults view and will only be accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."
}
}

View File

@ -3005,5 +3005,11 @@
},
"passkeyRemoved": {
"message": "A jelszó eltávolításra került."
},
"unassignedItemsBanner": {
"message": "Megjegyzés: A nem hozzá nem rendelt szervezeti elemek már nem láthatók az Összes széf nézetben és csak az Adminisztrátori konzolon keresztül érhetők el. Rendeljük ezeket az elemeket egy gyűjteményhez az Adminisztrátor konzolból, hogy láthatóvá tegyük azokat."
},
"unassignedItemsBannerSelfHost": {
"message": "Figyelmeztetés: 2024. május 2-án a nem hozzá rendelt szervezeti elemek többé nem lesznek láthatók az Összes széf nézetben a különböző eszközökön és csak a Felügyeleti konzolon keresztül lesznek elérhetők. Rendeljük ezeket az elemeket egy gyűjteményhez az Adminisztrátori konzolból, hogy láthatóvá tegyük azokat."
}
}

View File

@ -3005,5 +3005,11 @@
},
"passkeyRemoved": {
"message": "Passkey removed"
},
"unassignedItemsBanner": {
"message": "Notice: Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."
},
"unassignedItemsBannerSelfHost": {
"message": "Notice: On May 2, 2024, unassigned organization items will no longer be visible in the All Vaults view and will only be accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."
}
}

View File

@ -3005,5 +3005,11 @@
},
"passkeyRemoved": {
"message": "Passkey rimossa"
},
"unassignedItemsBanner": {
"message": "Avviso: gli elementi dell'organizzazione non assegnati non sono più visibili nella visualizzazione Tutte le Cassaforti su tutti i dispositivi e sono ora accessibili solo tramite la Console di amministrazione. Assegna questi elementi ad una raccolta dalla Console di amministrazione per renderli visibili."
},
"unassignedItemsBannerSelfHost": {
"message": "Avviso: dal 2 maggio 2024, gli elementi dell'organizzazione non assegnati non saranno più visibili nella visualizzazione Tutte le Cassaforti su tutti i dispositivi e saranno accessibili solo tramite la Console di amministrazione. Assegna questi elementi ad una raccolta dalla Console di amministrazione per renderli visibili."
}
}

View File

@ -3005,5 +3005,11 @@
},
"passkeyRemoved": {
"message": "パスキーを削除しました"
},
"unassignedItemsBanner": {
"message": "注意: 割り当てられていない組織項目は、すべての保管庫のビューでは表示されなくなり、管理コンソールからのみアクセスできます。 管理コンソールからコレクションにこれらのアイテムを割り当てると、表示するようにできます。"
},
"unassignedItemsBannerSelfHost": {
"message": "お知らせ2024年5月2日に、 割り当てられていない組織アイテムはデバイス間のすべての保管庫ビューに表示されなくなり、管理コンソールからのみアクセス可能になります。 管理コンソールからコレクションにこれらのアイテムを割り当てると、表示できるようになります。"
}
}

View File

@ -3005,5 +3005,11 @@
},
"passkeyRemoved": {
"message": "Passkey removed"
},
"unassignedItemsBanner": {
"message": "Notice: Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."
},
"unassignedItemsBannerSelfHost": {
"message": "Notice: On May 2, 2024, unassigned organization items will no longer be visible in the All Vaults view and will only be accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."
}
}

View File

@ -3005,5 +3005,11 @@
},
"passkeyRemoved": {
"message": "Passkey removed"
},
"unassignedItemsBanner": {
"message": "Notice: Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."
},
"unassignedItemsBannerSelfHost": {
"message": "Notice: On May 2, 2024, unassigned organization items will no longer be visible in the All Vaults view and will only be accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."
}
}

View File

@ -3005,5 +3005,11 @@
},
"passkeyRemoved": {
"message": "Passkey removed"
},
"unassignedItemsBanner": {
"message": "Notice: Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."
},
"unassignedItemsBannerSelfHost": {
"message": "Notice: On May 2, 2024, unassigned organization items will no longer be visible in the All Vaults view and will only be accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."
}
}

View File

@ -3005,5 +3005,11 @@
},
"passkeyRemoved": {
"message": "Passkey removed"
},
"unassignedItemsBanner": {
"message": "Notice: Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."
},
"unassignedItemsBannerSelfHost": {
"message": "Notice: On May 2, 2024, unassigned organization items will no longer be visible in the All Vaults view and will only be accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."
}
}

View File

@ -3005,5 +3005,11 @@
},
"passkeyRemoved": {
"message": "Pašalintas slaptaraktis"
},
"unassignedItemsBanner": {
"message": "Notice: Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."
},
"unassignedItemsBannerSelfHost": {
"message": "Notice: On May 2, 2024, unassigned organization items will no longer be visible in the All Vaults view and will only be accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."
}
}

View File

@ -3005,5 +3005,11 @@
},
"passkeyRemoved": {
"message": "Piekļuves atslēga noņemta"
},
"unassignedItemsBanner": {
"message": "Jāņem vērā: nepiešķirti apvienības vienumi vairs nav redzami skatā \"Visas glabātavas\" un ir sasniedzami tikai no pārvaldības konsoles, kur šie vienumi jāpiešķir krājumam, lai padarītu tos redzamus."
},
"unassignedItemsBannerSelfHost": {
"message": "Jāņem vērā: 2024. gada 2. maijā nepiešķirti apvienības vienumi vairs nebūs redzami skatā \"Visas glabātavas\" un būs sasniedzami tikai no pārvaldības konsoles, kur šie vienumi jāpiešķir krājumam, lai padarītu tos redzamus."
}
}

View File

@ -3005,5 +3005,11 @@
},
"passkeyRemoved": {
"message": "Passkey removed"
},
"unassignedItemsBanner": {
"message": "Notice: Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."
},
"unassignedItemsBannerSelfHost": {
"message": "Notice: On May 2, 2024, unassigned organization items will no longer be visible in the All Vaults view and will only be accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."
}
}

View File

@ -3005,5 +3005,11 @@
},
"passkeyRemoved": {
"message": "Passkey removed"
},
"unassignedItemsBanner": {
"message": "Notice: Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."
},
"unassignedItemsBannerSelfHost": {
"message": "Notice: On May 2, 2024, unassigned organization items will no longer be visible in the All Vaults view and will only be accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."
}
}

View File

@ -3005,5 +3005,11 @@
},
"passkeyRemoved": {
"message": "Passkey removed"
},
"unassignedItemsBanner": {
"message": "Notice: Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."
},
"unassignedItemsBannerSelfHost": {
"message": "Notice: On May 2, 2024, unassigned organization items will no longer be visible in the All Vaults view and will only be accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."
}
}

View File

@ -3005,5 +3005,11 @@
},
"passkeyRemoved": {
"message": "Passkey removed"
},
"unassignedItemsBanner": {
"message": "Notice: Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."
},
"unassignedItemsBannerSelfHost": {
"message": "Notice: On May 2, 2024, unassigned organization items will no longer be visible in the All Vaults view and will only be accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."
}
}

View File

@ -3005,5 +3005,11 @@
},
"passkeyRemoved": {
"message": "Passkey removed"
},
"unassignedItemsBanner": {
"message": "Notice: Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."
},
"unassignedItemsBannerSelfHost": {
"message": "Notice: On May 2, 2024, unassigned organization items will no longer be visible in the All Vaults view and will only be accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."
}
}

View File

@ -3005,5 +3005,11 @@
},
"passkeyRemoved": {
"message": "Passkey verwijderd"
},
"unassignedItemsBanner": {
"message": "Let op: Niet-toegewezen organisatie-items zijn niet langer zichtbaar in de weergave van alle kluisjes en zijn alleen toegankelijk via de Admin Console. Om deze items zichtbaar te maken, moet je ze toewijzen aan een collectie via de Admin Console."
},
"unassignedItemsBannerSelfHost": {
"message": "Kennisgeving: Vanaf 2 mei 2024 zijn niet-toegewezen organisatie-items op geen enkel apparaat meer zichtbaar in de weergave van alle kluisjes en alleen toegankelijk via de Admin Console. Je kunt deze items in het Admin Console aan een collectie toewijzen om ze zichtbaar te maken."
}
}

View File

@ -3005,5 +3005,11 @@
},
"passkeyRemoved": {
"message": "Passkey removed"
},
"unassignedItemsBanner": {
"message": "Notice: Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."
},
"unassignedItemsBannerSelfHost": {
"message": "Notice: On May 2, 2024, unassigned organization items will no longer be visible in the All Vaults view and will only be accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."
}
}

View File

@ -3005,5 +3005,11 @@
},
"passkeyRemoved": {
"message": "Passkey removed"
},
"unassignedItemsBanner": {
"message": "Notice: Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."
},
"unassignedItemsBannerSelfHost": {
"message": "Notice: On May 2, 2024, unassigned organization items will no longer be visible in the All Vaults view and will only be accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."
}
}

View File

@ -3005,5 +3005,11 @@
},
"passkeyRemoved": {
"message": "Passkey został usunięty"
},
"unassignedItemsBanner": {
"message": "Uwaga: Nieprzypisane elementy w organizacji nie są już widoczne w widoku Wszystkie sejfy i są dostępne tylko przez Konsolę Administracyjną. Przypisz te elementy do kolekcji z konsoli administracyjnej, aby były one widoczne."
},
"unassignedItemsBannerSelfHost": {
"message": "Uwaga: 2 maja 2024 r. nieprzypisane elementy w organizacji nie będą już widoczne w widoku Wszystkie sejfy i będą dostępne tylko przez Konsolę Administracyjną. Przypisz te elementy do kolekcji z konsoli administracyjnej, aby były one widoczne."
}
}

View File

@ -3005,5 +3005,11 @@
},
"passkeyRemoved": {
"message": "Passkey removed"
},
"unassignedItemsBanner": {
"message": "Notice: Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."
},
"unassignedItemsBannerSelfHost": {
"message": "Notice: On May 2, 2024, unassigned organization items will no longer be visible in the All Vaults view and will only be accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."
}
}

View File

@ -3005,5 +3005,11 @@
},
"passkeyRemoved": {
"message": "Chave de acesso removida"
},
"unassignedItemsBanner": {
"message": "Aviso: Os itens da organização não atribuídos já não são visíveis na vista Todos os cofres e só são acessíveis através da consola de administração. Atribua estes itens a uma coleção a partir da Consola de administração para os tornar visíveis."
},
"unassignedItemsBannerSelfHost": {
"message": "Aviso: A 2 de maio de 2024, os itens da organização não atribuídos deixarão de ser visíveis na vista Todos os cofres e só estarão acessíveis através da Consola de administração. Atribua estes itens a uma coleção a partir da Consola de administração para os tornar visíveis."
}
}

View File

@ -3005,5 +3005,11 @@
},
"passkeyRemoved": {
"message": "Passkey removed"
},
"unassignedItemsBanner": {
"message": "Notice: Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."
},
"unassignedItemsBannerSelfHost": {
"message": "Notice: On May 2, 2024, unassigned organization items will no longer be visible in the All Vaults view and will only be accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."
}
}

View File

@ -688,10 +688,10 @@
"message": "Запрос на обновление пароля логина при обнаружении изменений на сайте. Применяется ко всем авторизованным аккаунтам."
},
"enableUsePasskeys": {
"message": "Запрос на сохранение и использование ключей доступа"
"message": "Запрос на сохранение и использование passkey"
},
"usePasskeysDesc": {
"message": "Запрос на сохранение новых ключей или в авторизация с ключами, хранящимися в вашем хранилище. Применяется ко всем авторизованным аккаунтам."
"message": "Запрос на сохранение новых passkey или в авторизация с passkey, хранящимися в вашем хранилище. Применяется ко всем авторизованным аккаунтам."
},
"notificationChangeDesc": {
"message": "Обновить этот пароль в Bitwarden?"
@ -2786,25 +2786,25 @@
"message": "Подтвердите пароль к файлу"
},
"typePasskey": {
"message": "Ключ доступа"
"message": "Passkey"
},
"passkeyNotCopied": {
"message": "Ключ доступа не будет скопирован"
"message": "Passkey не будет скопирован"
},
"passkeyNotCopiedAlert": {
"message": "Ключ доступа не будет скопирован в клонированный элемент. Продолжить клонирование этого элемента?"
"message": "Passkey не будет скопирован в клонированный элемент. Продолжить клонирование этого элемента?"
},
"passkeyFeatureIsNotImplementedForAccountsWithoutMasterPassword": {
"message": "Необходима верификация со стороны инициирующего сайта. Для аккаунтов без мастер-пароля эта возможность пока не реализована."
},
"logInWithPasskey": {
"message": "Войти с ключом доступа?"
"message": "Войти с passkey?"
},
"passkeyAlreadyExists": {
"message": "Для данного приложения уже существует ключ доступа."
"message": "Для данного приложения уже существует passkey."
},
"noPasskeysFoundForThisApplication": {
"message": "Для данного приложения ключей доступа не найдено."
"message": "Для данного приложения не найден passkey."
},
"noMatchingPasskeyLogin": {
"message": "У вас нет подходящего логина для этого сайта."
@ -2813,28 +2813,28 @@
"message": "Подтвердить"
},
"savePasskey": {
"message": "Сохранить ключ доступа"
"message": "Сохранить passkey"
},
"savePasskeyNewLogin": {
"message": "Сохранить ключ доступа как новый логин"
"message": "Сохранить passkey как новый логин"
},
"choosePasskey": {
"message": "Выберите логин, для которого будет сохранен данный ключ доступа"
"message": "Выберите логин, для которого будет сохранен данный passkey"
},
"passkeyItem": {
"message": "Ключ доступа элемента"
"message": "Элемент passkey"
},
"overwritePasskey": {
"message": "Перезаписать ключ доступа?"
"message": "Перезаписать passkey?"
},
"overwritePasskeyAlert": {
"message": "Этот элемент уже содержит ключ доступа. Вы уверены, что хотите перезаписать текущий ключ?"
"message": "Этот элемент уже содержит passkey. Вы уверены, что хотите перезаписать текущий passkey?"
},
"featureNotSupported": {
"message": "Функция пока не поддерживается"
},
"yourPasskeyIsLocked": {
"message": "Для использования ключа доступа необходима аутентификация. Для продолжения работы подтвердите свою личность."
"message": "Для использования passkey необходима аутентификация. Для продолжения работы подтвердите свою личность."
},
"multifactorAuthenticationCancelled": {
"message": "Многофакторная аутентификация отменена"
@ -3005,5 +3005,11 @@
},
"passkeyRemoved": {
"message": "Passkey удален"
},
"unassignedItemsBanner": {
"message": "Обратите внимание: неприсвоенные элементы организации больше не видны в представлении \"Все хранилища\" и доступны только через консоль администратора. Назначьте эти элементы коллекции в консоли администратора, чтобы сделать их видимыми."
},
"unassignedItemsBannerSelfHost": {
"message": "Уведомление: 2 мая 2024 года неприсвоенные элементы организации больше не будут видны в представлении \"Все хранилища\" и будут доступны только через консоль администратора. Назначьте эти элементы коллекции в консоли администратора, чтобы сделать их видимыми."
}
}

View File

@ -3005,5 +3005,11 @@
},
"passkeyRemoved": {
"message": "Passkey removed"
},
"unassignedItemsBanner": {
"message": "Notice: Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."
},
"unassignedItemsBannerSelfHost": {
"message": "Notice: On May 2, 2024, unassigned organization items will no longer be visible in the All Vaults view and will only be accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."
}
}

View File

@ -3005,5 +3005,11 @@
},
"passkeyRemoved": {
"message": "Prístupový kľúč bol odstránený"
},
"unassignedItemsBanner": {
"message": "Upozornenie: Nepriradené položky organizácie už nie sú viditeľné v zobrazení Všetky Trezory a sú prístupné len cez administrátorskú konzolu. Aby boli viditeľné, priraďte tieto položky do kolekcie z konzoly administrátora."
},
"unassignedItemsBannerSelfHost": {
"message": "Upozornenie: 2. mája nepriradené položky organizácie už nebudú viditeľné v zobrazení Všetky Trezory a budú prístupné len cez administrátorskú konzolu. Aby boli viditeľné, priraďte tieto položky do kolekcie z konzoly administrátora."
}
}

View File

@ -3005,5 +3005,11 @@
},
"passkeyRemoved": {
"message": "Passkey removed"
},
"unassignedItemsBanner": {
"message": "Notice: Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."
},
"unassignedItemsBannerSelfHost": {
"message": "Notice: On May 2, 2024, unassigned organization items will no longer be visible in the All Vaults view and will only be accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."
}
}

View File

@ -3005,5 +3005,11 @@
},
"passkeyRemoved": {
"message": "Приступачни кључ је уклоњен"
},
"unassignedItemsBanner": {
"message": "Напомена: Недодељене ставке организације више нису видљиве у приказу Сви сефови и доступне су само преко Админ конзоле. Доделите ове ставке колекцији са Админ конзолом да бисте их учинили видљивим."
},
"unassignedItemsBannerSelfHost": {
"message": "Обавештење: 2. маја 2024. недодељене ставке организације више неће бити видљиве у приказу Сви сефови и биће доступне само преко Админ конзоле. Доделите ове ставке колекцији са Админ конзолом да бисте их учинили видљивим."
}
}

View File

@ -2931,7 +2931,7 @@
"message": "active"
},
"locked": {
"message": "locked"
"message": "låst"
},
"unlocked": {
"message": "unlocked"
@ -3005,5 +3005,11 @@
},
"passkeyRemoved": {
"message": "Passkey removed"
},
"unassignedItemsBanner": {
"message": "Notice: Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."
},
"unassignedItemsBannerSelfHost": {
"message": "Notice: On May 2, 2024, unassigned organization items will no longer be visible in the All Vaults view and will only be accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."
}
}

View File

@ -3005,5 +3005,11 @@
},
"passkeyRemoved": {
"message": "Passkey removed"
},
"unassignedItemsBanner": {
"message": "Notice: Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."
},
"unassignedItemsBannerSelfHost": {
"message": "Notice: On May 2, 2024, unassigned organization items will no longer be visible in the All Vaults view and will only be accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."
}
}

View File

@ -3005,5 +3005,11 @@
},
"passkeyRemoved": {
"message": "Passkey removed"
},
"unassignedItemsBanner": {
"message": "Notice: Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."
},
"unassignedItemsBannerSelfHost": {
"message": "Notice: On May 2, 2024, unassigned organization items will no longer be visible in the All Vaults view and will only be accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."
}
}

View File

@ -3005,5 +3005,11 @@
},
"passkeyRemoved": {
"message": "Passkey removed"
},
"unassignedItemsBanner": {
"message": "Notice: Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."
},
"unassignedItemsBannerSelfHost": {
"message": "Notice: On May 2, 2024, unassigned organization items will no longer be visible in the All Vaults view and will only be accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."
}
}

View File

@ -3005,5 +3005,11 @@
},
"passkeyRemoved": {
"message": "Ключ доступу вилучено"
},
"unassignedItemsBanner": {
"message": "Увага: непризначені елементи організації більше не видимі у поданні \"Усі сховища\" і доступні лише в консолі адміністратора. Щоб зробити їх видимими, призначте ці елементи збірці в консолі адміністратора."
},
"unassignedItemsBannerSelfHost": {
"message": "Сповіщення: 2 травня 2024 року, непризначені елементи організації більше не будуть видимі в поданні \"Усі сховища\", і будуть доступні лише через консоль адміністратора. Щоб зробити їх видимими, призначте ці елементи збірці в консолі адміністратора."
}
}

View File

@ -3005,5 +3005,11 @@
},
"passkeyRemoved": {
"message": "Passkey removed"
},
"unassignedItemsBanner": {
"message": "Notice: Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."
},
"unassignedItemsBannerSelfHost": {
"message": "Notice: On May 2, 2024, unassigned organization items will no longer be visible in the All Vaults view and will only be accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."
}
}

View File

@ -3005,5 +3005,11 @@
},
"passkeyRemoved": {
"message": "通行密钥已移除"
},
"unassignedItemsBanner": {
"message": "注意:未分配的组织项目在「所有密码库」视图中不再可见,只能通过管理控制台访问。通过管理控制台将这些项目分配给集合以使其可见。"
},
"unassignedItemsBannerSelfHost": {
"message": "注意:从 2024 年 5 月 2 日起,未分配的组织项目在「所有密码库」视图中将不再可见,只能通过管理控制台访问。通过管理控制台将这些项目分配给集合以使其可见。"
}
}

View File

@ -3005,5 +3005,11 @@
},
"passkeyRemoved": {
"message": "Passkey removed"
},
"unassignedItemsBanner": {
"message": "Notice: Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."
},
"unassignedItemsBannerSelfHost": {
"message": "Notice: On May 2, 2024, unassigned organization items will no longer be visible in the All Vaults view and will only be accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."
}
}

View File

@ -18,17 +18,25 @@ import {
factory,
} from "../../../platform/background/service-factories/factory-options";
import {
stateServiceFactory,
StateServiceInitOptions,
} from "../../../platform/background/service-factories/state-service.factory";
stateProviderFactory,
StateProviderInitOptions,
} from "../../../platform/background/service-factories/state-provider.factory";
import { accountServiceFactory, AccountServiceInitOptions } from "./account-service.factory";
import {
internalMasterPasswordServiceFactory,
MasterPasswordServiceInitOptions,
} from "./master-password-service.factory";
type AuthRequestServiceFactoryOptions = FactoryOptions;
export type AuthRequestServiceInitOptions = AuthRequestServiceFactoryOptions &
AppIdServiceInitOptions &
AccountServiceInitOptions &
MasterPasswordServiceInitOptions &
CryptoServiceInitOptions &
ApiServiceInitOptions &
StateServiceInitOptions;
StateProviderInitOptions;
export function authRequestServiceFactory(
cache: { authRequestService?: AuthRequestServiceAbstraction } & CachedServices,
@ -41,9 +49,11 @@ export function authRequestServiceFactory(
async () =>
new AuthRequestService(
await appIdServiceFactory(cache, opts),
await accountServiceFactory(cache, opts),
await internalMasterPasswordServiceFactory(cache, opts),
await cryptoServiceFactory(cache, opts),
await apiServiceFactory(cache, opts),
await stateServiceFactory(cache, opts),
await stateProviderFactory(cache, opts),
),
);
}

View File

@ -31,6 +31,11 @@ import {
StateProviderInitOptions,
} from "../../../platform/background/service-factories/state-provider.factory";
import { accountServiceFactory, AccountServiceInitOptions } from "./account-service.factory";
import {
internalMasterPasswordServiceFactory,
MasterPasswordServiceInitOptions,
} from "./master-password-service.factory";
import { TokenServiceInitOptions, tokenServiceFactory } from "./token-service.factory";
type KeyConnectorServiceFactoryOptions = FactoryOptions & {
@ -40,6 +45,8 @@ type KeyConnectorServiceFactoryOptions = FactoryOptions & {
};
export type KeyConnectorServiceInitOptions = KeyConnectorServiceFactoryOptions &
AccountServiceInitOptions &
MasterPasswordServiceInitOptions &
CryptoServiceInitOptions &
ApiServiceInitOptions &
TokenServiceInitOptions &
@ -58,6 +65,8 @@ export function keyConnectorServiceFactory(
opts,
async () =>
new KeyConnectorService(
await accountServiceFactory(cache, opts),
await internalMasterPasswordServiceFactory(cache, opts),
await cryptoServiceFactory(cache, opts),
await apiServiceFactory(cache, opts),
await tokenServiceFactory(cache, opts),

View File

@ -59,6 +59,7 @@ import {
PasswordStrengthServiceInitOptions,
} from "../../../tools/background/service_factories/password-strength-service.factory";
import { accountServiceFactory, AccountServiceInitOptions } from "./account-service.factory";
import {
authRequestServiceFactory,
AuthRequestServiceInitOptions,
@ -71,6 +72,10 @@ import {
keyConnectorServiceFactory,
KeyConnectorServiceInitOptions,
} from "./key-connector-service.factory";
import {
internalMasterPasswordServiceFactory,
MasterPasswordServiceInitOptions,
} from "./master-password-service.factory";
import { tokenServiceFactory, TokenServiceInitOptions } from "./token-service.factory";
import { twoFactorServiceFactory, TwoFactorServiceInitOptions } from "./two-factor-service.factory";
import {
@ -81,6 +86,8 @@ import {
type LoginStrategyServiceFactoryOptions = FactoryOptions;
export type LoginStrategyServiceInitOptions = LoginStrategyServiceFactoryOptions &
AccountServiceInitOptions &
MasterPasswordServiceInitOptions &
CryptoServiceInitOptions &
ApiServiceInitOptions &
TokenServiceInitOptions &
@ -111,6 +118,8 @@ export function loginStrategyServiceFactory(
opts,
async () =>
new LoginStrategyService(
await accountServiceFactory(cache, opts),
await internalMasterPasswordServiceFactory(cache, opts),
await cryptoServiceFactory(cache, opts),
await apiServiceFactory(cache, opts),
await tokenServiceFactory(cache, opts),

View File

@ -0,0 +1,42 @@
import {
InternalMasterPasswordServiceAbstraction,
MasterPasswordServiceAbstraction,
} from "@bitwarden/common/auth/abstractions/master-password.service.abstraction";
import { MasterPasswordService } from "@bitwarden/common/auth/services/master-password/master-password.service";
import {
CachedServices,
factory,
FactoryOptions,
} from "../../../platform/background/service-factories/factory-options";
import {
stateProviderFactory,
StateProviderInitOptions,
} from "../../../platform/background/service-factories/state-provider.factory";
type MasterPasswordServiceFactoryOptions = FactoryOptions;
export type MasterPasswordServiceInitOptions = MasterPasswordServiceFactoryOptions &
StateProviderInitOptions;
export function internalMasterPasswordServiceFactory(
cache: { masterPasswordService?: InternalMasterPasswordServiceAbstraction } & CachedServices,
opts: MasterPasswordServiceInitOptions,
): Promise<InternalMasterPasswordServiceAbstraction> {
return factory(
cache,
"masterPasswordService",
opts,
async () => new MasterPasswordService(await stateProviderFactory(cache, opts)),
);
}
export async function masterPasswordServiceFactory(
cache: { masterPasswordService?: InternalMasterPasswordServiceAbstraction } & CachedServices,
opts: MasterPasswordServiceInitOptions,
): Promise<MasterPasswordServiceAbstraction> {
return (await internalMasterPasswordServiceFactory(
cache,
opts,
)) as MasterPasswordServiceAbstraction;
}

View File

@ -31,6 +31,11 @@ import {
stateServiceFactory,
} from "../../../platform/background/service-factories/state-service.factory";
import { accountServiceFactory, AccountServiceInitOptions } from "./account-service.factory";
import {
internalMasterPasswordServiceFactory,
MasterPasswordServiceInitOptions,
} from "./master-password-service.factory";
import { PinCryptoServiceInitOptions, pinCryptoServiceFactory } from "./pin-crypto-service.factory";
import {
userDecryptionOptionsServiceFactory,
@ -46,6 +51,8 @@ type UserVerificationServiceFactoryOptions = FactoryOptions;
export type UserVerificationServiceInitOptions = UserVerificationServiceFactoryOptions &
StateServiceInitOptions &
CryptoServiceInitOptions &
AccountServiceInitOptions &
MasterPasswordServiceInitOptions &
I18nServiceInitOptions &
UserVerificationApiServiceInitOptions &
UserDecryptionOptionsServiceInitOptions &
@ -66,6 +73,8 @@ export function userVerificationServiceFactory(
new UserVerificationService(
await stateServiceFactory(cache, opts),
await cryptoServiceFactory(cache, opts),
await accountServiceFactory(cache, opts),
await internalMasterPasswordServiceFactory(cache, opts),
await i18nServiceFactory(cache, opts),
await userVerificationApiServiceFactory(cache, opts),
await userDecryptionOptionsServiceFactory(cache, opts),

View File

@ -6,6 +6,7 @@ import { Subject, firstValueFrom, map, switchMap, takeUntil } from "rxjs";
import { VaultTimeoutSettingsService } from "@bitwarden/common/abstractions/vault-timeout/vault-timeout-settings.service";
import { VaultTimeoutService } from "@bitwarden/common/abstractions/vault-timeout/vault-timeout.service";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service";
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
import { VaultTimeoutAction } from "@bitwarden/common/enums/vault-timeout-action.enum";
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
@ -32,6 +33,7 @@ export class AccountSwitcherComponent implements OnInit, OnDestroy {
private location: Location,
private router: Router,
private vaultTimeoutSettingsService: VaultTimeoutSettingsService,
private authService: AuthService,
) {}
get accountLimit() {
@ -42,13 +44,14 @@ export class AccountSwitcherComponent implements OnInit, OnDestroy {
return this.accountSwitcherService.SPECIAL_ADD_ACCOUNT_ID;
}
get availableAccounts$() {
return this.accountSwitcherService.availableAccounts$;
}
get currentAccount$() {
return this.accountService.activeAccount$;
}
readonly availableAccounts$ = this.accountSwitcherService.availableAccounts$;
readonly currentAccount$ = this.accountService.activeAccount$.pipe(
switchMap((a) =>
a == null
? null
: this.authService.activeAccountStatus$.pipe(map((s) => ({ ...a, status: s }))),
),
);
async ngOnInit() {
const availableVaultTimeoutActions = await firstValueFrom(

View File

@ -4,6 +4,7 @@ import { ActivatedRoute, Router } from "@angular/router";
import { Observable, combineLatest, switchMap } from "rxjs";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service";
import { AvatarService } from "@bitwarden/common/auth/abstractions/avatar.service";
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
import { UserId } from "@bitwarden/common/types/guid";
@ -29,12 +30,14 @@ export class CurrentAccountComponent {
private router: Router,
private location: Location,
private route: ActivatedRoute,
private authService: AuthService,
) {
this.currentAccount$ = combineLatest([
this.accountService.activeAccount$,
this.avatarService.avatarColor$,
this.authService.activeAccountStatus$,
]).pipe(
switchMap(async ([account, avatarColor]) => {
switchMap(async ([account, avatarColor, accountStatus]) => {
if (account == null) {
return null;
}
@ -42,7 +45,7 @@ export class CurrentAccountComponent {
id: account.id,
name: account.name || account.email,
email: account.email,
status: account.status,
status: accountStatus,
avatarColor,
};

View File

@ -1,7 +1,8 @@
import { matches, mock } from "jest-mock-extended";
import { BehaviorSubject, firstValueFrom, of, timeout } from "rxjs";
import { BehaviorSubject, ReplaySubject, firstValueFrom, of, timeout } from "rxjs";
import { AccountInfo, AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service";
import { AvatarService } from "@bitwarden/common/auth/abstractions/avatar.service";
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service";
@ -12,22 +13,29 @@ import { UserId } from "@bitwarden/common/types/guid";
import { AccountSwitcherService } from "./account-switcher.service";
describe("AccountSwitcherService", () => {
const accountsSubject = new BehaviorSubject<Record<UserId, AccountInfo>>(null);
const activeAccountSubject = new BehaviorSubject<{ id: UserId } & AccountInfo>(null);
let accountsSubject: BehaviorSubject<Record<UserId, AccountInfo>>;
let activeAccountSubject: BehaviorSubject<{ id: UserId } & AccountInfo>;
let authStatusSubject: ReplaySubject<Record<UserId, AuthenticationStatus>>;
const accountService = mock<AccountService>();
const avatarService = mock<AvatarService>();
const messagingService = mock<MessagingService>();
const environmentService = mock<EnvironmentService>();
const logService = mock<LogService>();
const authService = mock<AuthService>();
let accountSwitcherService: AccountSwitcherService;
beforeEach(() => {
jest.resetAllMocks();
accountsSubject = new BehaviorSubject<Record<UserId, AccountInfo>>(null);
activeAccountSubject = new BehaviorSubject<{ id: UserId } & AccountInfo>(null);
authStatusSubject = new ReplaySubject<Record<UserId, AuthenticationStatus>>(1);
// Use subject to allow for easy updates
accountService.accounts$ = accountsSubject;
accountService.activeAccount$ = activeAccountSubject;
authService.authStatuses$ = authStatusSubject;
accountSwitcherService = new AccountSwitcherService(
accountService,
@ -35,48 +43,59 @@ describe("AccountSwitcherService", () => {
messagingService,
environmentService,
logService,
authService,
);
});
afterEach(() => {
accountsSubject.complete();
activeAccountSubject.complete();
authStatusSubject.complete();
});
describe("availableAccounts$", () => {
it("should return all accounts and an add account option when accounts are less than 5", async () => {
const user1AccountInfo: AccountInfo = {
it("should return all logged in accounts and an add account option when accounts are less than 5", async () => {
const accountInfo: AccountInfo = {
name: "Test User 1",
email: "test1@email.com",
status: AuthenticationStatus.Unlocked,
};
avatarService.getUserAvatarColor$.mockReturnValue(of("#cccccc"));
accountsSubject.next({
"1": user1AccountInfo,
} as Record<UserId, AccountInfo>);
activeAccountSubject.next(Object.assign(user1AccountInfo, { id: "1" as UserId }));
accountsSubject.next({ ["1" as UserId]: accountInfo, ["2" as UserId]: accountInfo });
authStatusSubject.next({
["1" as UserId]: AuthenticationStatus.Unlocked,
["2" as UserId]: AuthenticationStatus.Locked,
});
activeAccountSubject.next(Object.assign(accountInfo, { id: "1" as UserId }));
const accounts = await firstValueFrom(
accountSwitcherService.availableAccounts$.pipe(timeout(20)),
);
expect(accounts).toHaveLength(2);
expect(accounts).toHaveLength(3);
expect(accounts[0].id).toBe("1");
expect(accounts[0].isActive).toBeTruthy();
expect(accounts[1].id).toBe("addAccount");
expect(accounts[1].id).toBe("2");
expect(accounts[1].isActive).toBeFalsy();
expect(accounts[2].id).toBe("addAccount");
expect(accounts[2].isActive).toBeFalsy();
});
it.each([5, 6])(
"should return only accounts if there are %i accounts",
async (numberOfAccounts) => {
const seedAccounts: Record<UserId, AccountInfo> = {};
const seedStatuses: Record<UserId, AuthenticationStatus> = {};
for (let i = 0; i < numberOfAccounts; i++) {
seedAccounts[`${i}` as UserId] = {
email: `test${i}@email.com`,
name: "Test User ${i}",
status: AuthenticationStatus.Unlocked,
};
seedStatuses[`${i}` as UserId] = AuthenticationStatus.Unlocked;
}
avatarService.getUserAvatarColor$.mockReturnValue(of("#cccccc"));
accountsSubject.next(seedAccounts);
authStatusSubject.next(seedStatuses);
activeAccountSubject.next(
Object.assign(seedAccounts["1" as UserId], { id: "1" as UserId }),
);
@ -89,6 +108,26 @@ describe("AccountSwitcherService", () => {
});
},
);
it("excludes logged out accounts", async () => {
const user1AccountInfo: AccountInfo = {
name: "Test User 1",
email: "",
};
accountsSubject.next({ ["1" as UserId]: user1AccountInfo });
authStatusSubject.next({ ["1" as UserId]: AuthenticationStatus.LoggedOut });
accountsSubject.next({
"1": user1AccountInfo,
} as Record<UserId, AccountInfo>);
const accounts = await firstValueFrom(
accountSwitcherService.availableAccounts$.pipe(timeout(20)),
);
// Add account only
expect(accounts).toHaveLength(1);
expect(accounts[0].id).toBe("addAccount");
});
});
describe("selectAccount", () => {

View File

@ -11,6 +11,7 @@ import {
} from "rxjs";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service";
import { AvatarService } from "@bitwarden/common/auth/abstractions/avatar.service";
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service";
@ -48,25 +49,27 @@ export class AccountSwitcherService {
private messagingService: MessagingService,
private environmentService: EnvironmentService,
private logService: LogService,
authService: AuthService,
) {
this.availableAccounts$ = combineLatest([
this.accountService.accounts$,
accountService.accounts$,
authService.authStatuses$,
this.accountService.activeAccount$,
]).pipe(
switchMap(async ([accounts, activeAccount]) => {
const accountEntries = Object.entries(accounts).filter(
([_, account]) => account.status !== AuthenticationStatus.LoggedOut,
switchMap(async ([accounts, accountStatuses, activeAccount]) => {
const loggedInIds = Object.keys(accounts).filter(
(id: UserId) => accountStatuses[id] !== AuthenticationStatus.LoggedOut,
);
// Accounts shouldn't ever be more than ACCOUNT_LIMIT but just in case do a greater than
const hasMaxAccounts = accountEntries.length >= this.ACCOUNT_LIMIT;
const hasMaxAccounts = loggedInIds.length >= this.ACCOUNT_LIMIT;
const options: AvailableAccount[] = await Promise.all(
accountEntries.map(async ([id, account]) => {
loggedInIds.map(async (id: UserId) => {
return {
name: account.name ?? account.email,
email: account.email,
name: accounts[id].name ?? accounts[id].email,
email: accounts[id].email,
id: id,
server: (await this.environmentService.getEnvironment(id))?.getHostname(),
status: account.status,
status: accountStatuses[id],
isActive: id === activeAccount?.id,
avatarColor: await firstValueFrom(
this.avatarService.getUserAvatarColor$(id as UserId),

View File

@ -12,6 +12,7 @@ import { InternalPolicyService } from "@bitwarden/common/admin-console/abstracti
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service";
import { DeviceTrustCryptoServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust-crypto.service.abstraction";
import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/auth/abstractions/master-password.service.abstraction";
import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction";
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
@ -41,6 +42,7 @@ export class LockComponent extends BaseLockComponent {
fido2PopoutSessionData$ = fido2PopoutSessionData$();
constructor(
masterPasswordService: InternalMasterPasswordServiceAbstraction,
router: Router,
i18nService: I18nService,
platformUtilsService: PlatformUtilsService,
@ -66,6 +68,7 @@ export class LockComponent extends BaseLockComponent {
accountService: AccountService,
) {
super(
masterPasswordService,
router,
i18nService,
platformUtilsService,

View File

@ -1,65 +1,9 @@
import { Component } from "@angular/core";
import { ActivatedRoute, Router } from "@angular/router";
import { SetPasswordComponent as BaseSetPasswordComponent } from "@bitwarden/angular/auth/components/set-password.component";
import { InternalUserDecryptionOptionsServiceAbstraction } from "@bitwarden/auth/common";
import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.service.abstraction";
import { OrganizationUserService } from "@bitwarden/common/admin-console/abstractions/organization-user/organization-user.service";
import { PolicyApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/policy/policy-api.service.abstraction";
import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
import { SsoLoginServiceAbstraction } from "@bitwarden/common/auth/abstractions/sso-login.service.abstraction";
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
import { PasswordGenerationServiceAbstraction } from "@bitwarden/common/tools/generator/password";
import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction";
import { DialogService } from "@bitwarden/components";
@Component({
selector: "app-set-password",
templateUrl: "set-password.component.html",
})
export class SetPasswordComponent extends BaseSetPasswordComponent {
constructor(
apiService: ApiService,
i18nService: I18nService,
cryptoService: CryptoService,
messagingService: MessagingService,
stateService: StateService,
passwordGenerationService: PasswordGenerationServiceAbstraction,
platformUtilsService: PlatformUtilsService,
policyApiService: PolicyApiServiceAbstraction,
policyService: PolicyService,
router: Router,
syncService: SyncService,
route: ActivatedRoute,
organizationApiService: OrganizationApiServiceAbstraction,
organizationUserService: OrganizationUserService,
userDecryptionOptionsService: InternalUserDecryptionOptionsServiceAbstraction,
ssoLoginService: SsoLoginServiceAbstraction,
dialogService: DialogService,
) {
super(
i18nService,
cryptoService,
messagingService,
passwordGenerationService,
platformUtilsService,
policyApiService,
policyService,
router,
apiService,
syncService,
route,
stateService,
organizationApiService,
organizationUserService,
userDecryptionOptionsService,
ssoLoginService,
dialogService,
);
}
}
export class SetPasswordComponent extends BaseSetPasswordComponent {}

View File

@ -9,7 +9,9 @@ import {
UserDecryptionOptionsServiceAbstraction,
} from "@bitwarden/auth/common";
import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service";
import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/auth/abstractions/master-password.service.abstraction";
import { SsoLoginServiceAbstraction } from "@bitwarden/common/auth/abstractions/sso-login.service.abstraction";
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
@ -45,7 +47,9 @@ export class SsoComponent extends BaseSsoComponent {
logService: LogService,
userDecryptionOptionsService: UserDecryptionOptionsServiceAbstraction,
configService: ConfigService,
protected authService: AuthService,
masterPasswordService: InternalMasterPasswordServiceAbstraction,
accountService: AccountService,
private authService: AuthService,
@Inject(WINDOW) private win: Window,
) {
super(
@ -63,6 +67,8 @@ export class SsoComponent extends BaseSsoComponent {
logService,
userDecryptionOptionsService,
configService,
masterPasswordService,
accountService,
);
environmentService.environment$.pipe(takeUntilDestroyed()).subscribe((env) => {

View File

@ -11,6 +11,8 @@ import {
UserDecryptionOptionsServiceAbstraction,
} from "@bitwarden/auth/common";
import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/auth/abstractions/master-password.service.abstraction";
import { SsoLoginServiceAbstraction } from "@bitwarden/common/auth/abstractions/sso-login.service.abstraction";
import { TwoFactorService } from "@bitwarden/common/auth/abstractions/two-factor.service";
import { TwoFactorProviderType } from "@bitwarden/common/auth/enums/two-factor-provider-type";
@ -58,6 +60,8 @@ export class TwoFactorComponent extends BaseTwoFactorComponent {
configService: ConfigService,
ssoLoginService: SsoLoginServiceAbstraction,
private dialogService: DialogService,
masterPasswordService: InternalMasterPasswordServiceAbstraction,
accountService: AccountService,
@Inject(WINDOW) protected win: Window,
private browserMessagingApi: ZonedMessageListenerService,
) {
@ -78,6 +82,8 @@ export class TwoFactorComponent extends BaseTwoFactorComponent {
userDecryptionOptionsService,
ssoLoginService,
configService,
masterPasswordService,
accountService,
);
super.onSuccessfulLogin = async () => {
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.

View File

@ -8,7 +8,6 @@ import {
generateRandomCustomElementName,
sendExtensionMessage,
setElementStyles,
getFromLocalStorage,
setupExtensionDisconnectAction,
setupAutofillInitDisconnectAction,
} from "./index";
@ -124,33 +123,6 @@ describe("setElementStyles", () => {
});
});
describe("getFromLocalStorage", () => {
it("returns a promise with the storage object pulled from the extension storage api", async () => {
const localStorage: Record<string, any> = {
testValue: "test",
another: "another",
};
jest.spyOn(chrome.storage.local, "get").mockImplementation((keys, callback) => {
const localStorageObject: Record<string, string> = {};
if (typeof keys === "string") {
localStorageObject[keys] = localStorage[keys];
} else if (Array.isArray(keys)) {
for (const key of keys) {
localStorageObject[key] = localStorage[key];
}
}
callback(localStorageObject);
});
const returnValue = await getFromLocalStorage("testValue");
expect(chrome.storage.local.get).toHaveBeenCalled();
expect(returnValue).toEqual({ testValue: "test" });
});
});
describe("setupExtensionDisconnectAction", () => {
afterEach(() => {
jest.clearAllMocks();

View File

@ -106,18 +106,6 @@ function setElementStyles(
}
}
/**
* Get data from local storage based on the keys provided.
*
* @param keys - String or array of strings of keys to get from local storage
* @deprecated Do not call this, use state-relevant services instead
*/
async function getFromLocalStorage(keys: string | string[]): Promise<Record<string, any>> {
return new Promise((resolve) => {
chrome.storage.local.get(keys, (storage: Record<string, any>) => resolve(storage));
});
}
/**
* Sets up a long-lived connection with the extension background
* and triggers an onDisconnect event if the extension context
@ -278,7 +266,6 @@ export {
buildSvgDomElement,
sendExtensionMessage,
setElementStyles,
getFromLocalStorage,
setupExtensionDisconnectAction,
setupAutofillInitDisconnectAction,
elementIsFillableFormField,

View File

@ -10,6 +10,7 @@ import {
AuthRequestServiceAbstraction,
AuthRequestService,
LoginEmailServiceAbstraction,
LoginEmailService,
} from "@bitwarden/auth/common";
import { ApiService as ApiServiceAbstraction } from "@bitwarden/common/abstractions/api.service";
import { AuditService as AuditServiceAbstraction } from "@bitwarden/common/abstractions/audit.service";
@ -32,6 +33,7 @@ import { DeviceTrustCryptoServiceAbstraction } from "@bitwarden/common/auth/abst
import { DevicesServiceAbstraction } from "@bitwarden/common/auth/abstractions/devices/devices.service.abstraction";
import { DevicesApiServiceAbstraction } from "@bitwarden/common/auth/abstractions/devices-api.service.abstraction";
import { KeyConnectorService as KeyConnectorServiceAbstraction } from "@bitwarden/common/auth/abstractions/key-connector.service";
import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/auth/abstractions/master-password.service.abstraction";
import { SsoLoginServiceAbstraction } from "@bitwarden/common/auth/abstractions/sso-login.service.abstraction";
import { TokenService as TokenServiceAbstraction } from "@bitwarden/common/auth/abstractions/token.service";
import { TwoFactorService as TwoFactorServiceAbstraction } from "@bitwarden/common/auth/abstractions/two-factor.service";
@ -46,6 +48,7 @@ import { DeviceTrustCryptoService } from "@bitwarden/common/auth/services/device
import { DevicesServiceImplementation } from "@bitwarden/common/auth/services/devices/devices.service.implementation";
import { DevicesApiServiceImplementation } from "@bitwarden/common/auth/services/devices-api.service.implementation";
import { KeyConnectorService } from "@bitwarden/common/auth/services/key-connector.service";
import { MasterPasswordService } from "@bitwarden/common/auth/services/master-password/master-password.service";
import { SsoLoginService } from "@bitwarden/common/auth/services/sso-login.service";
import { TokenService } from "@bitwarden/common/auth/services/token.service";
import { TwoFactorService } from "@bitwarden/common/auth/services/two-factor.service";
@ -232,7 +235,7 @@ import RuntimeBackground from "./runtime.background";
export default class MainBackground {
messagingService: MessagingServiceAbstraction;
storageService: AbstractStorageService & ObservableStorageService;
storageService: BrowserLocalStorageService;
secureStorageService: AbstractStorageService;
memoryStorageService: AbstractMemoryStorageService;
memoryStorageForStateProviders: AbstractMemoryStorageService & ObservableStorageService;
@ -242,6 +245,7 @@ export default class MainBackground {
keyGenerationService: KeyGenerationServiceAbstraction;
cryptoService: CryptoServiceAbstraction;
cryptoFunctionService: CryptoFunctionServiceAbstraction;
masterPasswordService: InternalMasterPasswordServiceAbstraction;
tokenService: TokenServiceAbstraction;
appIdService: AppIdServiceAbstraction;
apiService: ApiServiceAbstraction;
@ -361,9 +365,10 @@ export default class MainBackground {
const logoutCallback = async (expired: boolean, userId?: UserId) =>
await this.logout(expired, userId);
this.messagingService = this.popupOnlyContext
? new BrowserMessagingPrivateModeBackgroundService()
: new BrowserMessagingService();
this.messagingService =
this.isPrivateMode && BrowserApi.isManifestVersion(2)
? new BrowserMessagingPrivateModeBackgroundService()
: new BrowserMessagingService();
this.logService = new ConsoleLogService(false);
this.cryptoFunctionService = new WebCryptoFunctionService(self);
this.keyGenerationService = new KeyGenerationService(this.cryptoFunctionService);
@ -405,13 +410,14 @@ export default class MainBackground {
storageServiceProvider,
);
this.encryptService = flagEnabled("multithreadDecryption")
? new MultithreadEncryptServiceImplementation(
this.cryptoFunctionService,
this.logService,
true,
)
: new EncryptServiceImplementation(this.cryptoFunctionService, this.logService, true);
this.encryptService =
flagEnabled("multithreadDecryption") && BrowserApi.isManifestVersion(2)
? new MultithreadEncryptServiceImplementation(
this.cryptoFunctionService,
this.logService,
true,
)
: new EncryptServiceImplementation(this.cryptoFunctionService, this.logService, true);
this.singleUserStateProvider = new DefaultSingleUserStateProvider(
storageServiceProvider,
@ -480,8 +486,11 @@ export default class MainBackground {
const themeStateService = new DefaultThemeStateService(this.globalStateProvider);
this.masterPasswordService = new MasterPasswordService(this.stateProvider);
this.i18nService = new I18nService(BrowserApi.getUILanguage(), this.globalStateProvider);
this.cryptoService = new BrowserCryptoService(
this.masterPasswordService,
this.keyGenerationService,
this.cryptoFunctionService,
this.encryptService,
@ -508,7 +517,7 @@ export default class MainBackground {
this.apiService,
this.fileUploadService,
);
this.searchService = new SearchService(this.logService, this.i18nService);
this.searchService = new SearchService(this.logService, this.i18nService, this.stateProvider);
this.collectionService = new CollectionService(
this.cryptoService,
@ -525,6 +534,8 @@ export default class MainBackground {
this.badgeSettingsService = new BadgeSettingsService(this.stateProvider);
this.policyApiService = new PolicyApiService(this.policyService, this.apiService);
this.keyConnectorService = new KeyConnectorService(
this.accountService,
this.masterPasswordService,
this.cryptoService,
this.apiService,
this.tokenService,
@ -550,10 +561,13 @@ export default class MainBackground {
const backgroundMessagingService = new (class extends MessagingServiceAbstraction {
// AuthService should send the messages to the background not popup.
send = (subscriber: string, arg: any = {}) => {
if (BrowserApi.isManifestVersion(3)) {
that.messagingService.send(subscriber, arg);
return;
}
const message = Object.assign({}, { command: subscriber }, arg);
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
// eslint-disable-next-line @typescript-eslint/no-floating-promises
that.runtimeBackground.processMessage(message, that as any);
void that.runtimeBackground.processMessage(message, that as any);
};
})();
@ -578,9 +592,11 @@ export default class MainBackground {
this.authRequestService = new AuthRequestService(
this.appIdService,
this.accountService,
this.masterPasswordService,
this.cryptoService,
this.apiService,
this.stateService,
this.stateProvider,
);
this.authService = new AuthService(
@ -596,7 +612,11 @@ export default class MainBackground {
this.stateProvider,
);
this.loginEmailService = new LoginEmailService(this.stateProvider);
this.loginStrategyService = new LoginStrategyService(
this.accountService,
this.masterPasswordService,
this.cryptoService,
this.apiService,
this.tokenService,
@ -672,6 +692,8 @@ export default class MainBackground {
this.userVerificationService = new UserVerificationService(
this.stateService,
this.cryptoService,
this.accountService,
this.masterPasswordService,
this.i18nService,
this.userVerificationApiService,
this.userDecryptionOptionsService,
@ -694,6 +716,8 @@ export default class MainBackground {
this.vaultSettingsService = new VaultSettingsService(this.stateProvider);
this.vaultTimeoutService = new VaultTimeoutService(
this.accountService,
this.masterPasswordService,
this.cipherService,
this.folderService,
this.collectionService,
@ -729,6 +753,8 @@ export default class MainBackground {
this.providerService = new ProviderService(this.stateProvider);
this.syncService = new SyncService(
this.masterPasswordService,
this.accountService,
this.apiService,
this.domainSettingsService,
this.folderService,
@ -754,14 +780,14 @@ export default class MainBackground {
this.apiService,
this.stateProvider,
this.logService,
this.accountService,
this.authService,
);
this.eventCollectionService = new EventCollectionService(
this.cipherService,
this.stateProvider,
this.organizationService,
this.eventUploadService,
this.accountService,
this.authService,
);
this.totpService = new TotpService(this.cryptoFunctionService, this.logService);
@ -819,6 +845,7 @@ export default class MainBackground {
logoutCallback,
this.stateService,
this.authService,
this.authRequestService,
this.messagingService,
);
@ -878,6 +905,8 @@ export default class MainBackground {
this.fido2Service,
);
this.nativeMessagingBackground = new NativeMessagingBackground(
this.accountService,
this.masterPasswordService,
this.cryptoService,
this.cryptoFunctionService,
this.runtimeBackground,
@ -1107,7 +1136,7 @@ export default class MainBackground {
const status = await this.authService.getAuthStatus(userId);
const forcePasswordReset =
(await this.stateService.getForceSetPasswordReason({ userId: userId })) !=
(await firstValueFrom(this.masterPasswordService.forceSetPasswordReason$(userId))) !=
ForceSetPasswordReason.None;
await this.systemService.clearPendingClipboard();
@ -1154,14 +1183,9 @@ export default class MainBackground {
//Needs to be checked before state is cleaned
const needStorageReseed = await this.needsStorageReseed();
const currentUserId = await this.stateService.getUserId();
const newActiveUser = await this.stateService.clean({ userId: userId });
if (userId == null || userId === currentUserId) {
this.searchService.clearIndex();
}
await this.stateEventRunnerService.handleEvent("logout", currentUserId as UserId);
await this.stateEventRunnerService.handleEvent("logout", userId);
if (newActiveUser != null) {
// we have a new active user, do not continue tearing down application
@ -1236,18 +1260,8 @@ export default class MainBackground {
return;
}
const getStorage = (): Promise<any> =>
new Promise((resolve) => {
chrome.storage.local.get(null, (o: any) => resolve(o));
});
const clearStorage = (): Promise<void> =>
new Promise((resolve) => {
chrome.storage.local.clear(() => resolve());
});
const storage = await getStorage();
await clearStorage();
const storage = await this.storageService.getAll();
await this.storageService.clear();
for (const key in storage) {
// eslint-disable-next-line

View File

@ -1,6 +1,8 @@
import { firstValueFrom } from "rxjs";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service";
import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/auth/abstractions/master-password.service.abstraction";
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
import { AppIdService } from "@bitwarden/common/platform/abstractions/app-id.service";
import { CryptoFunctionService } from "@bitwarden/common/platform/abstractions/crypto-function.service";
@ -71,6 +73,8 @@ export class NativeMessagingBackground {
private validatingFingerprint: boolean;
constructor(
private accountService: AccountService,
private masterPasswordService: InternalMasterPasswordServiceAbstraction,
private cryptoService: CryptoService,
private cryptoFunctionService: CryptoFunctionService,
private runtimeBackground: RuntimeBackground,
@ -336,10 +340,14 @@ export class NativeMessagingBackground {
) as UserKey;
await this.cryptoService.setUserKey(userKey);
} else if (message.keyB64) {
const userId = (await firstValueFrom(this.accountService.activeAccount$))?.id;
// Backwards compatibility to support cases in which the user hasn't updated their desktop app
// TODO: Remove after 2023.10 release (https://bitwarden.atlassian.net/browse/PM-3472)
let encUserKey = await this.stateService.getEncryptedCryptoSymmetricKey();
encUserKey ||= await this.stateService.getMasterKeyEncryptedUserKey();
const encUserKeyPrim = await this.stateService.getEncryptedCryptoSymmetricKey();
const encUserKey =
encUserKeyPrim != null
? new EncString(encUserKeyPrim)
: await this.masterPasswordService.getMasterKeyEncryptedUserKey(userId);
if (!encUserKey) {
throw new Error("No encrypted user key found");
}
@ -348,9 +356,9 @@ export class NativeMessagingBackground {
) as MasterKey;
const userKey = await this.cryptoService.decryptUserKeyWithMasterKey(
masterKey,
new EncString(encUserKey),
encUserKey,
);
await this.cryptoService.setMasterKey(masterKey);
await this.masterPasswordService.setMasterKey(masterKey, userId);
await this.cryptoService.setUserKey(userKey);
} else {
throw new Error("No key received");

View File

@ -5,7 +5,10 @@ import {
organizationServiceFactory,
OrganizationServiceInitOptions,
} from "../../admin-console/background/service-factories/organization-service.factory";
import { accountServiceFactory } from "../../auth/background/service-factories/account-service.factory";
import {
authServiceFactory,
AuthServiceInitOptions,
} from "../../auth/background/service-factories/auth-service.factory";
import {
FactoryOptions,
CachedServices,
@ -29,7 +32,8 @@ export type EventCollectionServiceInitOptions = EventCollectionServiceOptions &
CipherServiceInitOptions &
StateServiceInitOptions &
OrganizationServiceInitOptions &
EventUploadServiceInitOptions;
EventUploadServiceInitOptions &
AuthServiceInitOptions;
export function eventCollectionServiceFactory(
cache: { eventCollectionService?: AbstractEventCollectionService } & CachedServices,
@ -45,7 +49,7 @@ export function eventCollectionServiceFactory(
await stateProviderFactory(cache, opts),
await organizationServiceFactory(cache, opts),
await eventUploadServiceFactory(cache, opts),
await accountServiceFactory(cache, opts),
await authServiceFactory(cache, opts),
),
);
}

View File

@ -1,7 +1,10 @@
import { EventUploadService as AbstractEventUploadService } from "@bitwarden/common/abstractions/event/event-upload.service";
import { EventUploadService } from "@bitwarden/common/services/event/event-upload.service";
import { accountServiceFactory } from "../../auth/background/service-factories/account-service.factory";
import {
AuthServiceInitOptions,
authServiceFactory,
} from "../../auth/background/service-factories/auth-service.factory";
import {
ApiServiceInitOptions,
apiServiceFactory,
@ -23,7 +26,8 @@ type EventUploadServiceOptions = FactoryOptions;
export type EventUploadServiceInitOptions = EventUploadServiceOptions &
ApiServiceInitOptions &
StateServiceInitOptions &
LogServiceInitOptions;
LogServiceInitOptions &
AuthServiceInitOptions;
export function eventUploadServiceFactory(
cache: { eventUploadService?: AbstractEventUploadService } & CachedServices,
@ -38,7 +42,7 @@ export function eventUploadServiceFactory(
await apiServiceFactory(cache, opts),
await stateProviderFactory(cache, opts),
await logServiceFactory(cache, opts),
await accountServiceFactory(cache, opts),
await authServiceFactory(cache, opts),
),
);
}

View File

@ -14,12 +14,17 @@ import {
logServiceFactory,
LogServiceInitOptions,
} from "../../platform/background/service-factories/log-service.factory";
import {
stateProviderFactory,
StateProviderInitOptions,
} from "../../platform/background/service-factories/state-provider.factory";
type SearchServiceFactoryOptions = FactoryOptions;
export type SearchServiceInitOptions = SearchServiceFactoryOptions &
LogServiceInitOptions &
I18nServiceInitOptions;
I18nServiceInitOptions &
StateProviderInitOptions;
export function searchServiceFactory(
cache: { searchService?: AbstractSearchService } & CachedServices,
@ -33,6 +38,7 @@ export function searchServiceFactory(
new SearchService(
await logServiceFactory(cache, opts),
await i18nServiceFactory(cache, opts),
await stateProviderFactory(cache, opts),
),
);
}

View File

@ -1,9 +1,17 @@
import { VaultTimeoutService as AbstractVaultTimeoutService } from "@bitwarden/common/abstractions/vault-timeout/vault-timeout.service";
import {
accountServiceFactory,
AccountServiceInitOptions,
} from "../../auth/background/service-factories/account-service.factory";
import {
authServiceFactory,
AuthServiceInitOptions,
} from "../../auth/background/service-factories/auth-service.factory";
import {
internalMasterPasswordServiceFactory,
MasterPasswordServiceInitOptions,
} from "../../auth/background/service-factories/master-password-service.factory";
import {
CryptoServiceInitOptions,
cryptoServiceFactory,
@ -57,6 +65,8 @@ type VaultTimeoutServiceFactoryOptions = FactoryOptions & {
};
export type VaultTimeoutServiceInitOptions = VaultTimeoutServiceFactoryOptions &
AccountServiceInitOptions &
MasterPasswordServiceInitOptions &
CipherServiceInitOptions &
FolderServiceInitOptions &
CollectionServiceInitOptions &
@ -79,6 +89,8 @@ export function vaultTimeoutServiceFactory(
opts,
async () =>
new VaultTimeoutService(
await accountServiceFactory(cache, opts),
await internalMasterPasswordServiceFactory(cache, opts),
await cipherServiceFactory(cache, opts),
await folderServiceFactory(cache, opts),
await collectionServiceFactory(cache, opts),

View File

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

View File

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

View File

@ -4,6 +4,10 @@ import {
AccountServiceInitOptions,
accountServiceFactory,
} from "../../../auth/background/service-factories/account-service.factory";
import {
internalMasterPasswordServiceFactory,
MasterPasswordServiceInitOptions,
} from "../../../auth/background/service-factories/master-password-service.factory";
import {
StateServiceInitOptions,
stateServiceFactory,
@ -34,6 +38,7 @@ import { StateProviderInitOptions, stateProviderFactory } from "./state-provider
type CryptoServiceFactoryOptions = FactoryOptions;
export type CryptoServiceInitOptions = CryptoServiceFactoryOptions &
MasterPasswordServiceInitOptions &
KeyGenerationServiceInitOptions &
CryptoFunctionServiceInitOptions &
EncryptServiceInitOptions &
@ -53,6 +58,7 @@ export function cryptoServiceFactory(
opts,
async () =>
new BrowserCryptoService(
await internalMasterPasswordServiceFactory(cache, opts),
await keyGenerationServiceFactory(cache, opts),
await cryptoFunctionServiceFactory(cache, opts),
await encryptServiceFactory(cache, opts),

View File

@ -93,6 +93,10 @@ export class SessionSyncer {
}
async update(serializedValue: any) {
if (!serializedValue) {
return;
}
const unBuiltValue = JSON.parse(serializedValue);
if (!BrowserApi.isManifestVersion(3) && BrowserApi.isBackgroundPage(self)) {
await this.memoryStorageService.save(this.metaData.sessionKey, serializedValue);
@ -104,6 +108,10 @@ export class SessionSyncer {
}
private async updateSession(value: any) {
if (!value) {
return;
}
const serializedValue = JSON.stringify(value);
if (BrowserApi.isManifestVersion(3) || BrowserApi.isBackgroundPage(self)) {
await this.memoryStorageService.save(this.metaData.sessionKey, serializedValue);

View File

@ -1,8 +1,10 @@
import { Component, Input } from "@angular/core";
import { Observable, map } from "rxjs";
import { Observable, combineLatest, map, of, switchMap } from "rxjs";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service";
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
import { UserId } from "@bitwarden/common/types/guid";
import { enableAccountSwitching } from "../flags";
@ -14,14 +16,18 @@ export class HeaderComponent {
@Input() noTheme = false;
@Input() hideAccountSwitcher = false;
authedAccounts$: Observable<boolean>;
constructor(accountService: AccountService) {
constructor(accountService: AccountService, authService: AuthService) {
this.authedAccounts$ = accountService.accounts$.pipe(
map((accounts) => {
switchMap((accounts) => {
if (!enableAccountSwitching()) {
return false;
return of(false);
}
return Object.values(accounts).some((a) => a.status !== AuthenticationStatus.LoggedOut);
return combineLatest(
Object.keys(accounts).map((id) => authService.authStatusFor$(id as UserId)),
).pipe(
map((statuses) => statuses.some((status) => status !== AuthenticationStatus.LoggedOut)),
);
}),
);
}

View File

@ -10,6 +10,8 @@ import { fromChromeEvent } from "../../browser/from-chrome-event";
export const serializationIndicator = "__json__";
type serializedObject = { [serializationIndicator]: true; value: string };
export const objToStore = (obj: any) => {
if (obj == null) {
return null;
@ -61,11 +63,7 @@ export default abstract class AbstractChromeStorageService
return new Promise((resolve) => {
this.chromeStorageApi.get(key, (obj: any) => {
if (obj != null && obj[key] != null) {
let value = obj[key];
if (value[serializationIndicator] && typeof value.value === "string") {
value = JSON.parse(value.value);
}
resolve(value as T);
resolve(this.processGetObject(obj[key]));
return;
}
resolve(null);
@ -95,4 +93,22 @@ export default abstract class AbstractChromeStorageService
});
});
}
/** Backwards compatible resolution of retrieved object with new serialized storage */
protected processGetObject<T>(obj: T | serializedObject): T | null {
if (this.isSerialized(obj)) {
obj = JSON.parse(obj.value);
}
return obj as T;
}
/** Type guard for whether an object is tagged as serialized */
protected isSerialized<T>(value: T | serializedObject): value is serializedObject {
const asSerialized = value as serializedObject;
return (
asSerialized != null &&
asSerialized[serializationIndicator] &&
typeof asSerialized.value === "string"
);
}
}

View File

@ -66,6 +66,7 @@ describe("ChromeStorageApiService", () => {
describe("get", () => {
let getMock: jest.Mock;
const key = "key";
beforeEach(() => {
// setup get
@ -76,7 +77,6 @@ describe("ChromeStorageApiService", () => {
});
it("returns a stored value when it is serialized", async () => {
const key = "key";
const value = { key: "value" };
store[key] = objToStore(value);
const result = await service.get(key);
@ -84,7 +84,6 @@ describe("ChromeStorageApiService", () => {
});
it("returns a stored value when it is not serialized", async () => {
const key = "key";
const value = "value";
store[key] = value;
const result = await service.get(key);
@ -95,5 +94,12 @@ describe("ChromeStorageApiService", () => {
const result = await service.get("key");
expect(result).toBeNull();
});
it("returns null when the stored object is null", async () => {
store[key] = null;
const result = await service.get(key);
expect(result).toBeNull();
});
});
});

View File

@ -1,6 +1,7 @@
import { firstValueFrom } from "rxjs";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/auth/abstractions/master-password.service.abstraction";
import { CryptoFunctionService } from "@bitwarden/common/platform/abstractions/crypto-function.service";
import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service";
import { KeyGenerationService } from "@bitwarden/common/platform/abstractions/key-generation.service";
@ -17,6 +18,7 @@ import { UserKey } from "@bitwarden/common/types/key";
export class BrowserCryptoService extends CryptoService {
constructor(
masterPasswordService: InternalMasterPasswordServiceAbstraction,
keyGenerationService: KeyGenerationService,
cryptoFunctionService: CryptoFunctionService,
encryptService: EncryptService,
@ -28,6 +30,7 @@ export class BrowserCryptoService extends CryptoService {
private biometricStateService: BiometricStateService,
) {
super(
masterPasswordService,
keyGenerationService,
cryptoFunctionService,
encryptService,

View File

@ -0,0 +1,89 @@
import { objToStore } from "./abstractions/abstract-chrome-storage-api.service";
import BrowserLocalStorageService from "./browser-local-storage.service";
describe("BrowserLocalStorageService", () => {
let service: BrowserLocalStorageService;
let store: Record<any, any>;
beforeEach(() => {
store = {};
service = new BrowserLocalStorageService();
});
describe("clear", () => {
let clearMock: jest.Mock;
beforeEach(() => {
clearMock = chrome.storage.local.clear as jest.Mock;
});
it("uses the api to clear", async () => {
await service.clear();
expect(clearMock).toHaveBeenCalledTimes(1);
});
});
describe("getAll", () => {
let getMock: jest.Mock;
beforeEach(() => {
// setup get
getMock = chrome.storage.local.get as jest.Mock;
getMock.mockImplementation((key, callback) => {
if (key == null) {
callback(store);
} else {
callback({ [key]: store[key] });
}
});
});
it("returns all values", async () => {
store["key1"] = "string";
store["key2"] = 0;
const result = await service.getAll();
expect(result).toEqual(store);
});
it("handles empty stores", async () => {
const result = await service.getAll();
expect(result).toEqual({});
});
it("handles stores with null values", async () => {
store["key"] = null;
const result = await service.getAll();
expect(result).toEqual(store);
});
it("handles values processed for storage", async () => {
const obj = { test: 2 };
const key = "key";
store[key] = objToStore(obj);
const result = await service.getAll();
expect(result).toEqual({
[key]: obj,
});
});
// This is a test of backwards compatibility before local storage was serialized.
it("handles values that were stored without processing for storage", async () => {
const obj = { test: 2 };
const key = "key";
store[key] = obj;
const result = await service.getAll();
expect(result).toEqual({
[key]: obj,
});
});
});
});

View File

@ -4,4 +4,32 @@ export default class BrowserLocalStorageService extends AbstractChromeStorageSer
constructor() {
super(chrome.storage.local);
}
/**
* Clears local storage
*/
async clear() {
await chrome.storage.local.clear();
}
/**
* Retrieves all objects stored in local storage.
*
* @remarks This method processes values prior to resolving, do not use `chrome.storage.local` directly
* @returns Promise resolving to keyed object of all stored data
*/
async getAll(): Promise<Record<string, unknown>> {
return new Promise((resolve) => {
this.chromeStorageApi.get(null, (allStorage) => {
const resolved = Object.entries(allStorage).reduce(
(agg, [key, value]) => {
agg[key] = this.processGetObject(value);
return agg;
},
{} as Record<string, unknown>,
);
resolve(resolved);
});
});
}
}

View File

@ -1,9 +1,10 @@
import { ChangeDetectorRef, Component, NgZone, OnDestroy, OnInit } from "@angular/core";
import { NavigationEnd, Router, RouterOutlet } from "@angular/router";
import { ToastrService } from "ngx-toastr";
import { filter, concatMap, Subject, takeUntil, firstValueFrom } from "rxjs";
import { filter, concatMap, Subject, takeUntil, firstValueFrom, map } from "rxjs";
import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service";
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
import { BroadcasterService } from "@bitwarden/common/platform/abstractions/broadcaster.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { DialogService, SimpleDialogOptions } from "@bitwarden/components";
@ -57,8 +58,9 @@ export class AppComponent implements OnInit, OnDestroy {
this.activeUserId = userId;
});
this.stateService.activeAccountUnlocked$
this.authService.activeAccountStatus$
.pipe(
map((status) => status === AuthenticationStatus.Unlocked),
filter((unlocked) => unlocked),
concatMap(async () => {
await this.recordActivity();

View File

@ -1,17 +1,14 @@
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { StateProvider } from "@bitwarden/common/platform/state";
import { SearchService } from "@bitwarden/common/services/search.service";
export class PopupSearchService extends SearchService {
constructor(
private mainSearchService: SearchService,
logService: LogService,
i18nService: I18nService,
) {
super(logService, i18nService);
constructor(logService: LogService, i18nService: I18nService, stateProvider: StateProvider) {
super(logService, i18nService, stateProvider);
}
clearIndex() {
clearIndex(): Promise<void> {
throw new Error("Not available.");
}
@ -19,7 +16,7 @@ export class PopupSearchService extends SearchService {
throw new Error("Not available.");
}
getIndexForSearch() {
return this.mainSearchService.getIndexForSearch();
async getIndexForSearch() {
return await super.getIndexForSearch();
}
}

View File

@ -62,6 +62,7 @@ import { StateService as BaseStateServiceAbstraction } from "@bitwarden/common/p
import {
AbstractMemoryStorageService,
AbstractStorageService,
ObservableStorageService,
} from "@bitwarden/common/platform/abstractions/storage.service";
import { StateFactory } from "@bitwarden/common/platform/factories/state-factory";
import { GlobalState } from "@bitwarden/common/platform/models/domain/global-state";
@ -74,17 +75,15 @@ import {
GlobalStateProvider,
StateProvider,
} from "@bitwarden/common/platform/state";
import { SearchService } from "@bitwarden/common/services/search.service";
import { PasswordGenerationServiceAbstraction } from "@bitwarden/common/tools/generator/password";
import { UsernameGenerationServiceAbstraction } from "@bitwarden/common/tools/generator/username";
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
import { CollectionService } from "@bitwarden/common/vault/abstractions/collection.service";
import { CipherFileUploadService } from "@bitwarden/common/vault/abstractions/file-upload/cipher-file-upload.service";
import { FolderService as FolderServiceAbstraction } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction";
import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction";
import { TotpService } from "@bitwarden/common/vault/abstractions/totp.service";
import { TotpService as TotpServiceAbstraction } from "@bitwarden/common/vault/abstractions/totp.service";
import { TotpService } from "@bitwarden/common/vault/services/totp.service";
import { DialogService } from "@bitwarden/components";
import { VaultExportServiceAbstraction } from "@bitwarden/vault-export-core";
import { UnauthGuardService } from "../../auth/popup/services";
import { AutofillService as AutofillServiceAbstraction } from "../../autofill/services/abstractions/autofill.service";
@ -159,7 +158,7 @@ const safeProviders: SafeProvider[] = [
safeProvider({
provide: MessagingService,
useFactory: () => {
return needsBackgroundInit
return needsBackgroundInit && BrowserApi.isManifestVersion(2)
? new BrowserMessagingPrivateModePopupService()
: new BrowserMessagingService();
},
@ -187,19 +186,8 @@ const safeProviders: SafeProvider[] = [
}),
safeProvider({
provide: SearchServiceAbstraction,
useFactory: (logService: LogService, i18nService: I18nServiceAbstraction) => {
return new PopupSearchService(
getBgService<SearchService>("searchService")(),
logService,
i18nService,
);
},
deps: [LogService, I18nServiceAbstraction],
}),
safeProvider({
provide: CipherFileUploadService,
useFactory: getBgService<CipherFileUploadService>("cipherFileUploadService"),
deps: [],
useClass: PopupSearchService,
deps: [LogService, I18nServiceAbstraction, StateProvider],
}),
safeProvider({
provide: CipherService,
@ -231,11 +219,6 @@ const safeProviders: SafeProvider[] = [
useClass: BrowserEnvironmentService,
deps: [LogService, StateProvider, AccountServiceAbstraction],
}),
safeProvider({
provide: TotpService,
useFactory: getBgService<TotpService>("totpService"),
deps: [],
}),
safeProvider({
provide: I18nServiceAbstraction,
useFactory: (globalStateProvider: GlobalStateProvider) => {
@ -252,6 +235,11 @@ const safeProviders: SafeProvider[] = [
},
deps: [EncryptService],
}),
safeProvider({
provide: TotpServiceAbstraction,
useClass: TotpService,
deps: [CryptoFunctionService, LogService],
}),
safeProvider({
provide: AuthRequestServiceAbstraction,
useFactory: getBgService<AuthRequestServiceAbstraction>("authRequestService"),
@ -325,7 +313,7 @@ const safeProviders: SafeProvider[] = [
deps: [
CipherService,
AutofillSettingsServiceAbstraction,
TotpService,
TotpServiceAbstraction,
EventCollectionServiceAbstraction,
LogService,
DomainSettingsService,
@ -333,11 +321,6 @@ const safeProviders: SafeProvider[] = [
BillingAccountProfileStateService,
],
}),
safeProvider({
provide: VaultExportServiceAbstraction,
useFactory: getBgService<VaultExportServiceAbstraction>("exportService"),
deps: [],
}),
safeProvider({
provide: KeyConnectorService,
useFactory: getBgService<KeyConnectorService>("keyConnectorService"),
@ -387,7 +370,15 @@ const safeProviders: SafeProvider[] = [
}),
safeProvider({
provide: OBSERVABLE_MEMORY_STORAGE,
useClass: ForegroundMemoryStorageService,
useFactory: () => {
if (BrowserApi.isManifestVersion(2)) {
return new ForegroundMemoryStorageService();
}
return getBgService<AbstractStorageService & ObservableStorageService>(
"memoryStorageForStateProviders",
)();
},
deps: [],
}),
safeProvider({

View File

@ -171,9 +171,7 @@ export class SendGroupingsComponent extends BaseSendComponent {
}
showSearching() {
return (
this.hasSearched || (!this.searchPending && this.searchService.isSearchable(this.searchText))
);
return this.hasSearched || (!this.searchPending && this.isSearchable);
}
private calculateTypeCounts() {

View File

@ -6,7 +6,7 @@ import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abs
import { EventType } from "@bitwarden/common/enums";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { TotpService } from "@bitwarden/common/vault/abstractions/totp.service";
import { TotpService as TotpServiceAbstraction } from "@bitwarden/common/vault/abstractions/totp.service";
import { CipherType } from "@bitwarden/common/vault/enums";
import { CipherRepromptType } from "@bitwarden/common/vault/enums/cipher-reprompt-type";
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
@ -31,7 +31,7 @@ export class ActionButtonsComponent implements OnInit, OnDestroy {
private i18nService: I18nService,
private platformUtilsService: PlatformUtilsService,
private eventCollectionService: EventCollectionService,
private totpService: TotpService,
private totpService: TotpServiceAbstraction,
private passwordRepromptService: PasswordRepromptService,
private billingAccountProfileStateService: BillingAccountProfileStateService,
) {}

View File

@ -311,7 +311,7 @@ export class Fido2Component implements OnInit, OnDestroy {
}
protected async search() {
this.hasSearched = this.searchService.isSearchable(this.searchText);
this.hasSearched = await this.searchService.isSearchable(this.searchText);
this.searchPending = true;
if (this.hasSearched) {
this.displayedCiphers = await this.searchService.searchCiphers(

View File

@ -36,19 +36,33 @@
</div>
<ng-container *ngIf="loaded">
<app-vault-select (onVaultSelectionChanged)="load()"></app-vault-select>
<app-callout *ngIf="showHowToAutofill" type="info" title="{{ 'howToAutofill' | i18n }}">
<p>{{ autofillCalloutText }}</p>
<app-callout
*ngIf="
(unassignedItemsBannerEnabled$ | async) &&
(unassignedItemsBannerService.showBanner$ | async) &&
(unassignedItemsBannerService.bannerText$ | async)
"
type="info"
>
<p>
{{ unassignedItemsBannerService.bannerText$ | async | i18n }}
<a
href="https://bitwarden.com/help/unassigned-vault-items-moved-to-admin-console"
bitLink
linkType="contrast"
target="_blank"
rel="noreferrer"
>{{ "learnMore" | i18n }}</a
>
</p>
<button
type="button"
class="btn primary callout-half"
appStopClick
(click)="dismissCallout()"
(click)="unassignedItemsBannerService.hideBanner()"
>
{{ "gotIt" | i18n }}
</button>
<button type="button" class="btn callout-half" appStopClick (click)="goToSettings()">
{{ "autofillSettings" | i18n }}
</button>
</app-callout>
<div class="box list" *ngIf="loginCiphers">
<h2 class="box-header">

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