Merge branch 'main' into org-delete-token

This commit is contained in:
Rui Tomé 2024-05-16 13:43:41 +01:00 committed by GitHub
commit 8cf847bab6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
420 changed files with 11283 additions and 4868 deletions

View File

@ -2,10 +2,10 @@
"name": "@bitwarden/browser",
"version": "2024.5.0",
"scripts": {
"build": "webpack",
"build:mv3": "cross-env MANIFEST_VERSION=3 webpack",
"build:watch": "webpack --watch",
"build:watch:mv3": "cross-env MANIFEST_VERSION=3 webpack --watch",
"build": "cross-env MANIFEST_VERSION=3 webpack",
"build:mv2": "webpack",
"build:watch": "cross-env MANIFEST_VERSION=3 webpack --watch",
"build:watch:mv2": "webpack --watch",
"build:prod": "cross-env NODE_ENV=production webpack",
"build:prod:beta": "cross-env BETA_BUILD=1 NODE_ENV=production webpack",
"build:prod:watch": "cross-env NODE_ENV=production webpack --watch",

View File

@ -374,12 +374,21 @@
"other": {
"message": "الأخرى"
},
"unlockMethods": {
"message": "Unlock options"
},
"unlockMethodNeededToChangeTimeoutActionDesc": {
"message": "أعدنّ طريقة إلغاء القُفْل لتغيير إجراء مهلة المخزن الخاص بك."
},
"unlockMethodNeeded": {
"message": "إعداد طريقة إلغاء القفل في الإعدادات"
},
"sessionTimeoutHeader": {
"message": "Session timeout"
},
"otherOptions": {
"message": "Other options"
},
"rateExtension": {
"message": "قيِّم هذه الإضافة"
},
@ -3023,6 +3032,15 @@
"adminConsole": {
"message": "Admin Console"
},
"accountSecurity": {
"message": "Account security"
},
"notifications": {
"message": "Notifications"
},
"appearance": {
"message": "Appearance"
},
"errorAssigningTargetCollection": {
"message": "Error assigning target collection."
},

View File

@ -3,11 +3,11 @@
"message": "Bitwarden"
},
"extName": {
"message": "Bitwarden Password Manager",
"message": "Bitwarden Parol Meneceri",
"description": "Extension name, MUST be less than 40 characters (Safari restriction)"
},
"extDesc": {
"message": "At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information",
"message": "Bitwarden evdə və ya işdə olarkən bütün parol, keçid açarı və həssas məlumatlarınızı asanlıqla qoruyur",
"description": "Extension description, MUST be less than 112 characters (Safari restriction)"
},
"loginOrCreateNewAccount": {
@ -374,12 +374,21 @@
"other": {
"message": "Digər"
},
"unlockMethods": {
"message": "Kilid açma seçimləri"
},
"unlockMethodNeededToChangeTimeoutActionDesc": {
"message": "Anbar vaxt bitməsi əməliyyatınızı dəyişdirmək üçün bir kilid açma üsulu qurun."
},
"unlockMethodNeeded": {
"message": "Ayarlarda bir kilid açma üsulu qurun"
},
"sessionTimeoutHeader": {
"message": "Seans vaxt bitməsi"
},
"otherOptions": {
"message": "Digər seçimlər"
},
"rateExtension": {
"message": "Uzantını qiymətləndir"
},
@ -3023,6 +3032,15 @@
"adminConsole": {
"message": "Admin Konsolu"
},
"accountSecurity": {
"message": "Hesab güvənliyi"
},
"notifications": {
"message": "Bildirişlər"
},
"appearance": {
"message": "Görünüş"
},
"errorAssigningTargetCollection": {
"message": "Hədəf kolleksiyaya təyin etmə xətası."
},

View File

@ -374,12 +374,21 @@
"other": {
"message": "Iншае"
},
"unlockMethods": {
"message": "Unlock options"
},
"unlockMethodNeededToChangeTimeoutActionDesc": {
"message": "Наладзіць метад разблакіроўкі для змянення дзеяння часу чакання вашага сховішча."
},
"unlockMethodNeeded": {
"message": "Set up an unlock method in Settings"
},
"sessionTimeoutHeader": {
"message": "Session timeout"
},
"otherOptions": {
"message": "Other options"
},
"rateExtension": {
"message": "Ацаніць пашырэнне"
},
@ -3023,6 +3032,15 @@
"adminConsole": {
"message": "Admin Console"
},
"accountSecurity": {
"message": "Account security"
},
"notifications": {
"message": "Notifications"
},
"appearance": {
"message": "Appearance"
},
"errorAssigningTargetCollection": {
"message": "Error assigning target collection."
},

View File

@ -3,11 +3,11 @@
"message": "Битуорден (Bitwarden)"
},
"extName": {
"message": "Bitwarden Password Manager",
"message": "Bitwarden — управител на пароли",
"description": "Extension name, MUST be less than 40 characters (Safari restriction)"
},
"extDesc": {
"message": "At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information",
"message": "У дома, на работа или на път Битуорден защитава всички Ваши пароли, секретни ключове и лична информация",
"description": "Extension description, MUST be less than 112 characters (Safari restriction)"
},
"loginOrCreateNewAccount": {
@ -374,12 +374,21 @@
"other": {
"message": "Други"
},
"unlockMethods": {
"message": "Настройки за отключване"
},
"unlockMethodNeededToChangeTimeoutActionDesc": {
"message": "Задайте метод за отключване, за да може да промените действието при изтичане на времето за достъп до трезора."
},
"unlockMethodNeeded": {
"message": "Задайте метод за отключване в Настройките"
},
"sessionTimeoutHeader": {
"message": "Изтичане на времето за сесията"
},
"otherOptions": {
"message": "Други настройки"
},
"rateExtension": {
"message": "Оценяване на разширението"
},
@ -3023,6 +3032,15 @@
"adminConsole": {
"message": "Административна конзола"
},
"accountSecurity": {
"message": "Защита на регистрацията"
},
"notifications": {
"message": "Известия"
},
"appearance": {
"message": "Външен вид"
},
"errorAssigningTargetCollection": {
"message": "Грешка при задаването на целева колекция."
},

View File

@ -374,12 +374,21 @@
"other": {
"message": "অন্যান্য"
},
"unlockMethods": {
"message": "Unlock options"
},
"unlockMethodNeededToChangeTimeoutActionDesc": {
"message": "Set up an unlock method to change your vault timeout action."
},
"unlockMethodNeeded": {
"message": "Set up an unlock method in Settings"
},
"sessionTimeoutHeader": {
"message": "Session timeout"
},
"otherOptions": {
"message": "Other options"
},
"rateExtension": {
"message": "এক্সটেনশনটি মূল্যায়ন করুন"
},
@ -3023,6 +3032,15 @@
"adminConsole": {
"message": "Admin Console"
},
"accountSecurity": {
"message": "Account security"
},
"notifications": {
"message": "Notifications"
},
"appearance": {
"message": "Appearance"
},
"errorAssigningTargetCollection": {
"message": "Error assigning target collection."
},

View File

@ -374,12 +374,21 @@
"other": {
"message": "Other"
},
"unlockMethods": {
"message": "Unlock options"
},
"unlockMethodNeededToChangeTimeoutActionDesc": {
"message": "Set up an unlock method to change your vault timeout action."
},
"unlockMethodNeeded": {
"message": "Set up an unlock method in Settings"
},
"sessionTimeoutHeader": {
"message": "Session timeout"
},
"otherOptions": {
"message": "Other options"
},
"rateExtension": {
"message": "Rate the extension"
},
@ -3023,6 +3032,15 @@
"adminConsole": {
"message": "Admin Console"
},
"accountSecurity": {
"message": "Account security"
},
"notifications": {
"message": "Notifications"
},
"appearance": {
"message": "Appearance"
},
"errorAssigningTargetCollection": {
"message": "Error assigning target collection."
},

View File

@ -374,12 +374,21 @@
"other": {
"message": "Altres"
},
"unlockMethods": {
"message": "Unlock options"
},
"unlockMethodNeededToChangeTimeoutActionDesc": {
"message": "Configura un mètode de desbloqueig per canviar l'acció del temps d'espera de la caixa forta."
},
"unlockMethodNeeded": {
"message": "Configura un mètode de desbloqueig a Configuració"
},
"sessionTimeoutHeader": {
"message": "Session timeout"
},
"otherOptions": {
"message": "Other options"
},
"rateExtension": {
"message": "Valora aquesta extensió"
},
@ -3023,6 +3032,15 @@
"adminConsole": {
"message": "Consola d'administració"
},
"accountSecurity": {
"message": "Account security"
},
"notifications": {
"message": "Notifications"
},
"appearance": {
"message": "Appearance"
},
"errorAssigningTargetCollection": {
"message": "S'ha produït un error en assignar la col·lecció de destinació."
},

View File

@ -374,12 +374,21 @@
"other": {
"message": "Ostatní"
},
"unlockMethods": {
"message": "Volby odemknutí"
},
"unlockMethodNeededToChangeTimeoutActionDesc": {
"message": "Nastavte metodu odemknutí, abyste změnili časový limit Vašeho trezoru."
},
"unlockMethodNeeded": {
"message": "Nastavit metodu odemknutí v Nastavení"
},
"sessionTimeoutHeader": {
"message": "Časový limit relace"
},
"otherOptions": {
"message": "Další volby"
},
"rateExtension": {
"message": "Ohodnotit rozšíření"
},
@ -3023,6 +3032,15 @@
"adminConsole": {
"message": "Konzole správce"
},
"accountSecurity": {
"message": "Zabezpečení účtu"
},
"notifications": {
"message": "Oznámení"
},
"appearance": {
"message": "Vzhled"
},
"errorAssigningTargetCollection": {
"message": "Chyba při přiřazování cílové kolekce."
},

View File

@ -374,12 +374,21 @@
"other": {
"message": "Gosodiadau eraill"
},
"unlockMethods": {
"message": "Unlock options"
},
"unlockMethodNeededToChangeTimeoutActionDesc": {
"message": "Set up an unlock method to change your vault timeout action."
},
"unlockMethodNeeded": {
"message": "Set up an unlock method in Settings"
},
"sessionTimeoutHeader": {
"message": "Session timeout"
},
"otherOptions": {
"message": "Other options"
},
"rateExtension": {
"message": "Rhoi eich barn ar yr estyniad"
},
@ -3023,6 +3032,15 @@
"adminConsole": {
"message": "Admin Console"
},
"accountSecurity": {
"message": "Account security"
},
"notifications": {
"message": "Notifications"
},
"appearance": {
"message": "Appearance"
},
"errorAssigningTargetCollection": {
"message": "Error assigning target collection."
},

View File

@ -3,11 +3,11 @@
"message": "Bitwarden"
},
"extName": {
"message": "Bitwarden Password Manager",
"message": "Bitwarden Adgangskodehåndtering",
"description": "Extension name, MUST be less than 40 characters (Safari restriction)"
},
"extDesc": {
"message": "At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information",
"message": "Hjemme, på arbejde eller på farten sikrer Bitwarden nemt alle adgangskoder, adgangskort og sensitive oplysninger",
"description": "Extension description, MUST be less than 112 characters (Safari restriction)"
},
"loginOrCreateNewAccount": {
@ -374,12 +374,21 @@
"other": {
"message": "Andre"
},
"unlockMethods": {
"message": "Oplåsningsmuligheder"
},
"unlockMethodNeededToChangeTimeoutActionDesc": {
"message": "Opsæt en oplåsningsmetode til at ændre bokstimeouthandlingen."
},
"unlockMethodNeeded": {
"message": "Opsæt en oplåsningsmetode i Indstillinger"
},
"sessionTimeoutHeader": {
"message": "Sessionstimeout"
},
"otherOptions": {
"message": "Andre innstillinger"
},
"rateExtension": {
"message": "Bedøm udvidelsen"
},
@ -2390,7 +2399,7 @@
"message": "Generelt"
},
"display": {
"message": "Display"
"message": "Skærm"
},
"accountSuccessfullyCreated": {
"message": "Konto oprettet!"
@ -3023,6 +3032,15 @@
"adminConsole": {
"message": "Admin-konsol"
},
"accountSecurity": {
"message": "Kontosikkerhed"
},
"notifications": {
"message": "Notifikationer"
},
"appearance": {
"message": "Udseende"
},
"errorAssigningTargetCollection": {
"message": "Fejl ved tildeling af målsamling."
},

View File

@ -7,7 +7,7 @@
"description": "Extension name, MUST be less than 40 characters (Safari restriction)"
},
"extDesc": {
"message": "At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information",
"message": "Zu Hause, am Arbeitsplatz oder unterwegs schützt Bitwarden Passwörter, Passkeys und vertrauliche Informationen",
"description": "Extension description, MUST be less than 112 characters (Safari restriction)"
},
"loginOrCreateNewAccount": {
@ -374,12 +374,21 @@
"other": {
"message": "Sonstige"
},
"unlockMethods": {
"message": "Entsperroptionen"
},
"unlockMethodNeededToChangeTimeoutActionDesc": {
"message": "Richte eine Entsperrmethode ein, um deine Aktion bei Timeout-Timeout zu ändern."
},
"unlockMethodNeeded": {
"message": "Lege eine Entsperrmethode in den Einstellungen fest"
},
"sessionTimeoutHeader": {
"message": "Sitzungs-Timeout"
},
"otherOptions": {
"message": "Andere Optionen"
},
"rateExtension": {
"message": "Erweiterung bewerten"
},
@ -3023,6 +3032,15 @@
"adminConsole": {
"message": "Administrator-Konsole"
},
"accountSecurity": {
"message": "Kontosicherheit"
},
"notifications": {
"message": "Benachrichtigungen"
},
"appearance": {
"message": "Aussehen"
},
"errorAssigningTargetCollection": {
"message": "Fehler beim Zuweisen der Ziel-Sammlung."
},

View File

@ -374,12 +374,21 @@
"other": {
"message": "Άλλες"
},
"unlockMethods": {
"message": "Unlock options"
},
"unlockMethodNeededToChangeTimeoutActionDesc": {
"message": "Ρυθμίστε μια μέθοδο ξεκλειδώματος για να αλλάξετε την ενέργεια χρονικού ορίου θησαυ/κιου."
},
"unlockMethodNeeded": {
"message": "Set up an unlock method in Settings"
},
"sessionTimeoutHeader": {
"message": "Session timeout"
},
"otherOptions": {
"message": "Other options"
},
"rateExtension": {
"message": "Βαθμολογήστε την επέκταση"
},
@ -3023,6 +3032,15 @@
"adminConsole": {
"message": "Admin Console"
},
"accountSecurity": {
"message": "Account security"
},
"notifications": {
"message": "Notifications"
},
"appearance": {
"message": "Appearance"
},
"errorAssigningTargetCollection": {
"message": "Error assigning target collection."
},

View File

@ -2177,6 +2177,108 @@
"forwardedEmailDesc": {
"message": "Generate an email alias with an external forwarding service."
},
"forwarderError": {
"message": "$SERVICENAME$ error: $ERRORMESSAGE$",
"description": "Reports an error returned by a forwarding service to the user.",
"placeholders": {
"servicename": {
"content": "$1",
"example": "SimpleLogin"
},
"errormessage": {
"content": "$2",
"example": "Invalid characters in domain name."
}
}
},
"forwarderGeneratedBy": {
"message": "Generated by Bitwarden.",
"description": "Displayed with the address on the forwarding service's configuration screen."
},
"forwarderGeneratedByWithWebsite": {
"message": "Website: $WEBSITE$. Generated by Bitwarden.",
"description": "Displayed with the address on the forwarding service's configuration screen.",
"placeholders": {
"WEBSITE": {
"content": "$1",
"example": "www.example.com"
}
}
},
"forwaderInvalidToken": {
"message": "Invalid $SERVICENAME$ API token",
"description": "Displayed when the user's API token is empty or rejected by the forwarding service.",
"placeholders": {
"servicename": {
"content": "$1",
"example": "SimpleLogin"
}
}
},
"forwaderInvalidTokenWithMessage": {
"message": "Invalid $SERVICENAME$ API token: $ERRORMESSAGE$",
"description": "Displayed when the user's API token is rejected by the forwarding service with an error message.",
"placeholders": {
"servicename": {
"content": "$1",
"example": "SimpleLogin"
},
"errormessage": {
"content": "$2",
"example": "Please verify your email address to continue."
}
}
},
"forwarderNoAccountId": {
"message": "Unable to obtain $SERVICENAME$ masked email account ID.",
"description": "Displayed when the forwarding service fails to return an account ID.",
"placeholders": {
"servicename": {
"content": "$1",
"example": "SimpleLogin"
}
}
},
"forwarderNoDomain": {
"message": "Invalid $SERVICENAME$ domain.",
"description": "Displayed when the domain is empty or domain authorization failed at the forwarding service.",
"placeholders": {
"servicename": {
"content": "$1",
"example": "SimpleLogin"
}
}
},
"forwarderNoUrl": {
"message": "Invalid $SERVICENAME$ url.",
"description": "Displayed when the url of the forwarding service wasn't supplied.",
"placeholders": {
"servicename": {
"content": "$1",
"example": "SimpleLogin"
}
}
},
"forwarderUnknownError": {
"message": "Unknown $SERVICENAME$ error occurred.",
"description": "Displayed when the forwarding service failed due to an unknown error.",
"placeholders": {
"servicename": {
"content": "$1",
"example": "SimpleLogin"
}
}
},
"forwarderUnknownForwarder": {
"message": "Unknown forwarder: '$SERVICENAME$'.",
"description": "Displayed when the forwarding service is not supported.",
"placeholders": {
"servicename": {
"content": "$1",
"example": "JustTrust.us"
}
}
},
"hostname": {
"message": "Hostname",
"description": "Part of a URL."
@ -3035,6 +3137,12 @@
"accountSecurity": {
"message": "Account security"
},
"notifications": {
"message": "Notifications"
},
"appearance": {
"message": "Appearance"
},
"errorAssigningTargetCollection": {
"message": "Error assigning target collection."
},

View File

@ -374,12 +374,21 @@
"other": {
"message": "Other"
},
"unlockMethods": {
"message": "Unlock options"
},
"unlockMethodNeededToChangeTimeoutActionDesc": {
"message": "Set up an unlock method to change your vault timeout action."
},
"unlockMethodNeeded": {
"message": "Set up an unlock method in Settings"
},
"sessionTimeoutHeader": {
"message": "Session timeout"
},
"otherOptions": {
"message": "Other options"
},
"rateExtension": {
"message": "Rate the extension"
},
@ -3023,6 +3032,15 @@
"adminConsole": {
"message": "Admin Console"
},
"accountSecurity": {
"message": "Account security"
},
"notifications": {
"message": "Notifications"
},
"appearance": {
"message": "Appearance"
},
"errorAssigningTargetCollection": {
"message": "Error assigning target collection."
},

View File

@ -374,12 +374,21 @@
"other": {
"message": "Other"
},
"unlockMethods": {
"message": "Unlock options"
},
"unlockMethodNeededToChangeTimeoutActionDesc": {
"message": "Set up an unlock method to change your vault timeout action."
},
"unlockMethodNeeded": {
"message": "Set up an unlock method in Settings"
},
"sessionTimeoutHeader": {
"message": "Session timeout"
},
"otherOptions": {
"message": "Other options"
},
"rateExtension": {
"message": "Rate the extension"
},
@ -3023,6 +3032,15 @@
"adminConsole": {
"message": "Admin Console"
},
"accountSecurity": {
"message": "Account security"
},
"notifications": {
"message": "Notifications"
},
"appearance": {
"message": "Appearance"
},
"errorAssigningTargetCollection": {
"message": "Error assigning target collection."
},

View File

@ -7,7 +7,7 @@
"description": "Extension name, MUST be less than 40 characters (Safari restriction)"
},
"extDesc": {
"message": "At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information",
"message": "En casa, el trabajo o el viaje, Bitwarden asegura todas sus contraseñas, claves e información confidencial",
"description": "Extension description, MUST be less than 112 characters (Safari restriction)"
},
"loginOrCreateNewAccount": {
@ -374,12 +374,21 @@
"other": {
"message": "Otros"
},
"unlockMethods": {
"message": "Opciones de desbloqueo"
},
"unlockMethodNeededToChangeTimeoutActionDesc": {
"message": "Configura un método de desbloqueo para cambiar tu acción de cierre de la bóveda."
},
"unlockMethodNeeded": {
"message": "Configure un método de desbloqueo en Configuración"
},
"sessionTimeoutHeader": {
"message": "Session timeout"
},
"otherOptions": {
"message": "Otras opciones"
},
"rateExtension": {
"message": "Valora la extensión"
},
@ -3023,6 +3032,15 @@
"adminConsole": {
"message": "Consola de administrador"
},
"accountSecurity": {
"message": "Seguridad de la cuenta"
},
"notifications": {
"message": "Notificaciones"
},
"appearance": {
"message": "Apariencia"
},
"errorAssigningTargetCollection": {
"message": "Error al asignar la colección de destino."
},

View File

@ -374,12 +374,21 @@
"other": {
"message": "Muu"
},
"unlockMethods": {
"message": "Unlock options"
},
"unlockMethodNeededToChangeTimeoutActionDesc": {
"message": "Hoidla ajalõpu tegevuse muutmiseks vali esmalt lahtilukustamise meetod."
},
"unlockMethodNeeded": {
"message": "Set up an unlock method in Settings"
},
"sessionTimeoutHeader": {
"message": "Session timeout"
},
"otherOptions": {
"message": "Other options"
},
"rateExtension": {
"message": "Hinda seda laiendust"
},
@ -3023,6 +3032,15 @@
"adminConsole": {
"message": "Admin Console"
},
"accountSecurity": {
"message": "Account security"
},
"notifications": {
"message": "Notifications"
},
"appearance": {
"message": "Appearance"
},
"errorAssigningTargetCollection": {
"message": "Error assigning target collection."
},

View File

@ -374,12 +374,21 @@
"other": {
"message": "Bestelakoak"
},
"unlockMethods": {
"message": "Unlock options"
},
"unlockMethodNeededToChangeTimeoutActionDesc": {
"message": "Set up an unlock method to change your vault timeout action."
},
"unlockMethodNeeded": {
"message": "Set up an unlock method in Settings"
},
"sessionTimeoutHeader": {
"message": "Session timeout"
},
"otherOptions": {
"message": "Other options"
},
"rateExtension": {
"message": "Baloratu gehigarria"
},
@ -3023,6 +3032,15 @@
"adminConsole": {
"message": "Admin Console"
},
"accountSecurity": {
"message": "Account security"
},
"notifications": {
"message": "Notifications"
},
"appearance": {
"message": "Appearance"
},
"errorAssigningTargetCollection": {
"message": "Error assigning target collection."
},

View File

@ -374,12 +374,21 @@
"other": {
"message": "ساير"
},
"unlockMethods": {
"message": "Unlock options"
},
"unlockMethodNeededToChangeTimeoutActionDesc": {
"message": "یک روش بازگشایی برای پایان زمان مجاز تنظیم کنید."
},
"unlockMethodNeeded": {
"message": "Set up an unlock method in Settings"
},
"sessionTimeoutHeader": {
"message": "Session timeout"
},
"otherOptions": {
"message": "Other options"
},
"rateExtension": {
"message": "به این افزونه امتیاز دهید"
},
@ -3023,6 +3032,15 @@
"adminConsole": {
"message": "Admin Console"
},
"accountSecurity": {
"message": "Account security"
},
"notifications": {
"message": "Notifications"
},
"appearance": {
"message": "Appearance"
},
"errorAssigningTargetCollection": {
"message": "Error assigning target collection."
},

View File

@ -374,12 +374,21 @@
"other": {
"message": "Muut"
},
"unlockMethods": {
"message": "Avausasetukset"
},
"unlockMethodNeededToChangeTimeoutActionDesc": {
"message": "Muuta holvisi aikakatkaisutoimintoa määrittämällä lukituksen avaustapa."
},
"unlockMethodNeeded": {
"message": "Määritä avaustapa asetuksista"
},
"sessionTimeoutHeader": {
"message": "Istunnon aikakatkaisu"
},
"otherOptions": {
"message": "Muut asetukset"
},
"rateExtension": {
"message": "Arvioi laajennus"
},
@ -3023,6 +3032,15 @@
"adminConsole": {
"message": "Hallintapaneelista"
},
"accountSecurity": {
"message": "Tilin suojaus"
},
"notifications": {
"message": "Ilmoitukset"
},
"appearance": {
"message": "Ulkoasu"
},
"errorAssigningTargetCollection": {
"message": "Virhe määritettäessä kohdekokoelmaa."
},

View File

@ -374,12 +374,21 @@
"other": {
"message": "Iba pa"
},
"unlockMethods": {
"message": "Unlock options"
},
"unlockMethodNeededToChangeTimeoutActionDesc": {
"message": "Mag-set up ng paraan ng pag-unlock upang baguhin ang iyong pagkilos sa pag-timeout ng vault."
},
"unlockMethodNeeded": {
"message": "Set up an unlock method in Settings"
},
"sessionTimeoutHeader": {
"message": "Session timeout"
},
"otherOptions": {
"message": "Other options"
},
"rateExtension": {
"message": "I-rate ang extension"
},
@ -3023,6 +3032,15 @@
"adminConsole": {
"message": "Admin Console"
},
"accountSecurity": {
"message": "Account security"
},
"notifications": {
"message": "Notifications"
},
"appearance": {
"message": "Appearance"
},
"errorAssigningTargetCollection": {
"message": "Error assigning target collection."
},

View File

@ -374,12 +374,21 @@
"other": {
"message": "Autre"
},
"unlockMethods": {
"message": "Unlock options"
},
"unlockMethodNeededToChangeTimeoutActionDesc": {
"message": "Configurez une méthode de déverrouillage pour changer le délai d'expiration de votre coffre."
},
"unlockMethodNeeded": {
"message": "Configurer une méthode de déverrouillage dans les Paramètres"
},
"sessionTimeoutHeader": {
"message": "Session timeout"
},
"otherOptions": {
"message": "Other options"
},
"rateExtension": {
"message": "Noter l'extension"
},
@ -3010,7 +3019,7 @@
"message": "Notice: Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console."
},
"unassignedItemsBannerSelfHostNotice": {
"message": "Notice: On May 16, 2024, unassigned organization items will no longer be visible in the All Vaults view and will only be accessible via the Admin Console."
"message": "Remarque : À partir du 16 mai 2024, les éléments d'organisation non assignés ne seront plus visibles dans votre vue Tous les coffres sur les appareils et ne seront maintenant accessibles que via la Console Admin."
},
"unassignedItemsBannerCTAPartOne": {
"message": "Ajouter ces éléments à une collection depuis la",
@ -3023,6 +3032,15 @@
"adminConsole": {
"message": "Console Admin"
},
"accountSecurity": {
"message": "Account security"
},
"notifications": {
"message": "Notifications"
},
"appearance": {
"message": "Appearance"
},
"errorAssigningTargetCollection": {
"message": "Error assigning target collection."
},

View File

@ -3,15 +3,15 @@
"message": "Bitwarden"
},
"extName": {
"message": "Bitwarden Password Manager",
"message": "Bitwarden - Xestor de contrasinais",
"description": "Extension name, MUST be less than 40 characters (Safari restriction)"
},
"extDesc": {
"message": "At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information",
"message": "Na casa, no traballo ou en ruta, Bitwarden protexe os teus contrasinais, chaves de acceso e datos sensíbeis",
"description": "Extension description, MUST be less than 112 characters (Safari restriction)"
},
"loginOrCreateNewAccount": {
"message": "Log in or create a new account to access your secure vault."
"message": "Rexístrate ou crea unha nova conta para acceder á túa caixa forte."
},
"createAccount": {
"message": "Crea unha conta"
@ -20,7 +20,7 @@
"message": "Iniciar sesión"
},
"enterpriseSingleSignOn": {
"message": "Enterprise single sign-on"
"message": "Inicio de sesión único empresarial"
},
"cancel": {
"message": "Cancelar"
@ -29,7 +29,7 @@
"message": "Pechar"
},
"submit": {
"message": "Submit"
"message": "Enviar"
},
"emailAddress": {
"message": "Enderezo de correo electrónico"
@ -38,142 +38,142 @@
"message": "Contrasinal mestre"
},
"masterPassDesc": {
"message": "The master password is the password you use to access your vault. It is very important that you do not forget your master password. There is no way to recover the password in the event that you forget it."
"message": "O contrasinal mestre é a chave que empregas para acceder á túa caixa forte. É moi importante que non esquezas o teu contrasinal mestre. Non hai xeito de recuperala se a esqueces."
},
"masterPassHintDesc": {
"message": "A master password hint can help you remember your password if you forget it."
"message": "Unha pista do contrasinal mestre pode axudarte a lembrar o teu contrasinal se o esqueces."
},
"reTypeMasterPass": {
"message": "Re-type master password"
"message": "Reescriba o contrasinal mestre"
},
"masterPassHint": {
"message": "Master password hint (optional)"
"message": "Pista do contrasinal mestre (opcional)"
},
"tab": {
"message": "Tab"
"message": "Separador"
},
"vault": {
"message": "Vault"
"message": "Caixa forte"
},
"myVault": {
"message": "My vault"
"message": "A miña caixa forte"
},
"allVaults": {
"message": "All vaults"
"message": "Todas as caixas fortes"
},
"tools": {
"message": "Tools"
"message": "Ferramentas"
},
"settings": {
"message": "Settings"
"message": "Axustes"
},
"currentTab": {
"message": "Current tab"
"message": "Separador actual"
},
"copyPassword": {
"message": "Copy password"
"message": "Copiar contrasinal"
},
"copyNote": {
"message": "Copy note"
"message": "Copiar nota"
},
"copyUri": {
"message": "Copy URI"
"message": "Copiar URI"
},
"copyUsername": {
"message": "Copy username"
"message": "Copiar nome de usuario"
},
"copyNumber": {
"message": "Copy number"
"message": "Copiar número"
},
"copySecurityCode": {
"message": "Copy security code"
"message": "Copiar código de seguranza"
},
"autoFill": {
"message": "Auto-fill"
"message": "Auto-encher"
},
"autoFillLogin": {
"message": "Auto-fill login"
"message": "Encher automaticamente inicio de sesión"
},
"autoFillCard": {
"message": "Auto-fill card"
"message": "Encher automaticamente tarxeta"
},
"autoFillIdentity": {
"message": "Auto-fill identity"
"message": "Encher automaticamente identidade"
},
"generatePasswordCopied": {
"message": "Generate password (copied)"
"message": "Xerar contrasinal (copiado)"
},
"copyElementIdentifier": {
"message": "Copy custom field name"
"message": "Copiar nome de campo personalizado"
},
"noMatchingLogins": {
"message": "No matching logins"
"message": "Sen inicios de sesión coincidentes"
},
"noCards": {
"message": "No cards"
"message": "Sen tarxetas"
},
"noIdentities": {
"message": "No identities"
"message": "Sen identidades"
},
"addLoginMenu": {
"message": "Add login"
"message": "Engadir inicio de sesión"
},
"addCardMenu": {
"message": "Add card"
"message": "Engadir tarxeta"
},
"addIdentityMenu": {
"message": "Add identity"
"message": "Engadir identidade"
},
"unlockVaultMenu": {
"message": "Unlock your vault"
"message": "Desbloquear a súa caixa forte"
},
"loginToVaultMenu": {
"message": "Log in to your vault"
"message": "Rexistrarse na súa caixa forte"
},
"autoFillInfo": {
"message": "There are no logins available to auto-fill for the current browser tab."
"message": "Non hai inicios de sesión dispoñíbeis para encher automaticamente para o separador actual do navegador."
},
"addLogin": {
"message": "Add a login"
"message": "Engadir inicio de sesión"
},
"addItem": {
"message": "Add item"
"message": "Engadir elemento"
},
"passwordHint": {
"message": "Password hint"
"message": "Pista do contrasinal"
},
"enterEmailToGetHint": {
"message": "Enter your account email address to receive your master password hint."
"message": "Introduce a dirección de correo da túa conta para recibir a pista do contrasinal mestre."
},
"getMasterPasswordHint": {
"message": "Get master password hint"
"message": "Obter pista do contrasinal mestre"
},
"continue": {
"message": "Continue"
"message": "Continuar"
},
"sendVerificationCode": {
"message": "Send a verification code to your email"
"message": "Envía un código de verificación ao teu correo"
},
"sendCode": {
"message": "Send code"
"message": "Enviar código"
},
"codeSent": {
"message": "Code sent"
"message": "Código enviado"
},
"verificationCode": {
"message": "Verification code"
"message": "Código de verificación"
},
"confirmIdentity": {
"message": "Confirm your identity to continue."
"message": "Confirma a túa identidade para continuar."
},
"account": {
"message": "Account"
"message": "Conta"
},
"changeMasterPassword": {
"message": "Change master password"
"message": "Cambiar o contrasinal mestre"
},
"continueToWebApp": {
"message": "Continue to web app?"
"message": "Continuar á aplicación web?"
},
"changeMasterPasswordOnWebConfirmation": {
"message": "You can change your master password on the Bitwarden web app."
@ -374,12 +374,21 @@
"other": {
"message": "Other"
},
"unlockMethods": {
"message": "Unlock options"
},
"unlockMethodNeededToChangeTimeoutActionDesc": {
"message": "Set up an unlock method to change your vault timeout action."
},
"unlockMethodNeeded": {
"message": "Set up an unlock method in Settings"
},
"sessionTimeoutHeader": {
"message": "Session timeout"
},
"otherOptions": {
"message": "Other options"
},
"rateExtension": {
"message": "Rate the extension"
},
@ -3023,6 +3032,15 @@
"adminConsole": {
"message": "Admin Console"
},
"accountSecurity": {
"message": "Account security"
},
"notifications": {
"message": "Notifications"
},
"appearance": {
"message": "Appearance"
},
"errorAssigningTargetCollection": {
"message": "Error assigning target collection."
},

View File

@ -374,12 +374,21 @@
"other": {
"message": "אחר"
},
"unlockMethods": {
"message": "Unlock options"
},
"unlockMethodNeededToChangeTimeoutActionDesc": {
"message": "Set up an unlock method to change your vault timeout action."
},
"unlockMethodNeeded": {
"message": "Set up an unlock method in Settings"
},
"sessionTimeoutHeader": {
"message": "Session timeout"
},
"otherOptions": {
"message": "Other options"
},
"rateExtension": {
"message": "דירוג הרחבה"
},
@ -3023,6 +3032,15 @@
"adminConsole": {
"message": "Admin Console"
},
"accountSecurity": {
"message": "Account security"
},
"notifications": {
"message": "Notifications"
},
"appearance": {
"message": "Appearance"
},
"errorAssigningTargetCollection": {
"message": "Error assigning target collection."
},

View File

@ -374,12 +374,21 @@
"other": {
"message": "अन्य"
},
"unlockMethods": {
"message": "Unlock options"
},
"unlockMethodNeededToChangeTimeoutActionDesc": {
"message": "Set up an unlock method to change your vault timeout action."
},
"unlockMethodNeeded": {
"message": "Set up an unlock method in Settings"
},
"sessionTimeoutHeader": {
"message": "Session timeout"
},
"otherOptions": {
"message": "Other options"
},
"rateExtension": {
"message": "Rate the Extension"
},
@ -3023,6 +3032,15 @@
"adminConsole": {
"message": "Admin Console"
},
"accountSecurity": {
"message": "Account security"
},
"notifications": {
"message": "Notifications"
},
"appearance": {
"message": "Appearance"
},
"errorAssigningTargetCollection": {
"message": "Error assigning target collection."
},

View File

@ -374,12 +374,21 @@
"other": {
"message": "Ostalo"
},
"unlockMethods": {
"message": "Unlock options"
},
"unlockMethodNeededToChangeTimeoutActionDesc": {
"message": "Za promjenu vremena isteka trezora, odredi način otključavanja."
},
"unlockMethodNeeded": {
"message": "Postavi način otključavanja u Postavkama"
},
"sessionTimeoutHeader": {
"message": "Session timeout"
},
"otherOptions": {
"message": "Other options"
},
"rateExtension": {
"message": "Ocijeni proširenje"
},
@ -3023,6 +3032,15 @@
"adminConsole": {
"message": "Admin Console"
},
"accountSecurity": {
"message": "Account security"
},
"notifications": {
"message": "Notifications"
},
"appearance": {
"message": "Appearance"
},
"errorAssigningTargetCollection": {
"message": "Error assigning target collection."
},

View File

@ -374,12 +374,21 @@
"other": {
"message": "Egyéb"
},
"unlockMethods": {
"message": "Feloldási opciók"
},
"unlockMethodNeededToChangeTimeoutActionDesc": {
"message": "Állítsunk be egy feloldási módot a széf időkifutási műveletének módosításához."
},
"unlockMethodNeeded": {
"message": "Feloldási mód beállítása a Beállításokban"
},
"sessionTimeoutHeader": {
"message": "Munkamenet időkifutás"
},
"otherOptions": {
"message": "Egyéb opciók"
},
"rateExtension": {
"message": "Bővítmény értékelése"
},
@ -3023,6 +3032,15 @@
"adminConsole": {
"message": "Adminisztrátori konzol"
},
"accountSecurity": {
"message": "Fiókbiztonság"
},
"notifications": {
"message": "Értesítések"
},
"appearance": {
"message": "Megjelenés"
},
"errorAssigningTargetCollection": {
"message": "Hiba történt a célgyűjtemény hozzárendelése során."
},

View File

@ -374,12 +374,21 @@
"other": {
"message": "Lainnya"
},
"unlockMethods": {
"message": "Unlock options"
},
"unlockMethodNeededToChangeTimeoutActionDesc": {
"message": "Set up an unlock method to change your vault timeout action."
},
"unlockMethodNeeded": {
"message": "Set up an unlock method in Settings"
},
"sessionTimeoutHeader": {
"message": "Session timeout"
},
"otherOptions": {
"message": "Other options"
},
"rateExtension": {
"message": "Nilai Ekstensi"
},
@ -3023,6 +3032,15 @@
"adminConsole": {
"message": "Admin Console"
},
"accountSecurity": {
"message": "Account security"
},
"notifications": {
"message": "Notifications"
},
"appearance": {
"message": "Appearance"
},
"errorAssigningTargetCollection": {
"message": "Error assigning target collection."
},

View File

@ -374,12 +374,21 @@
"other": {
"message": "Altro"
},
"unlockMethods": {
"message": "Opzioni di sblocco"
},
"unlockMethodNeededToChangeTimeoutActionDesc": {
"message": "Imposta un metodo di sblocco per modificare l'azione timeout cassaforte."
},
"unlockMethodNeeded": {
"message": "Imposta un metodo di sblocco in Impostazioni"
},
"sessionTimeoutHeader": {
"message": "Timeout della sessione"
},
"otherOptions": {
"message": "Altre opzioni"
},
"rateExtension": {
"message": "Valuta l'estensione"
},
@ -3023,6 +3032,15 @@
"adminConsole": {
"message": "Console di amministrazione"
},
"accountSecurity": {
"message": "Sicurezza dell'account"
},
"notifications": {
"message": "Notifiche"
},
"appearance": {
"message": "Aspetto"
},
"errorAssigningTargetCollection": {
"message": "Errore nell'assegnazione della raccolta di destinazione."
},

View File

@ -374,12 +374,21 @@
"other": {
"message": "その他"
},
"unlockMethods": {
"message": "ロック解除オプション"
},
"unlockMethodNeededToChangeTimeoutActionDesc": {
"message": "保管庫のタイムアウト動作を変更するには、ロック解除方法を設定してください。"
},
"unlockMethodNeeded": {
"message": "設定でロック解除方法をセットアップ"
},
"sessionTimeoutHeader": {
"message": "セッションタイムアウト"
},
"otherOptions": {
"message": "その他のオプション"
},
"rateExtension": {
"message": "拡張機能の評価"
},
@ -3023,6 +3032,15 @@
"adminConsole": {
"message": "管理コンソール"
},
"accountSecurity": {
"message": "アカウントのセキュリティ"
},
"notifications": {
"message": "通知"
},
"appearance": {
"message": "外観"
},
"errorAssigningTargetCollection": {
"message": "ターゲットコレクションの割り当てに失敗しました。"
},

View File

@ -374,12 +374,21 @@
"other": {
"message": "სხვა"
},
"unlockMethods": {
"message": "Unlock options"
},
"unlockMethodNeededToChangeTimeoutActionDesc": {
"message": "Set up an unlock method to change your vault timeout action."
},
"unlockMethodNeeded": {
"message": "Set up an unlock method in Settings"
},
"sessionTimeoutHeader": {
"message": "Session timeout"
},
"otherOptions": {
"message": "Other options"
},
"rateExtension": {
"message": "Rate the extension"
},
@ -3023,6 +3032,15 @@
"adminConsole": {
"message": "Admin Console"
},
"accountSecurity": {
"message": "Account security"
},
"notifications": {
"message": "Notifications"
},
"appearance": {
"message": "Appearance"
},
"errorAssigningTargetCollection": {
"message": "Error assigning target collection."
},

View File

@ -374,12 +374,21 @@
"other": {
"message": "Other"
},
"unlockMethods": {
"message": "Unlock options"
},
"unlockMethodNeededToChangeTimeoutActionDesc": {
"message": "Set up an unlock method to change your vault timeout action."
},
"unlockMethodNeeded": {
"message": "Set up an unlock method in Settings"
},
"sessionTimeoutHeader": {
"message": "Session timeout"
},
"otherOptions": {
"message": "Other options"
},
"rateExtension": {
"message": "Rate the extension"
},
@ -3023,6 +3032,15 @@
"adminConsole": {
"message": "Admin Console"
},
"accountSecurity": {
"message": "Account security"
},
"notifications": {
"message": "Notifications"
},
"appearance": {
"message": "Appearance"
},
"errorAssigningTargetCollection": {
"message": "Error assigning target collection."
},

View File

@ -374,12 +374,21 @@
"other": {
"message": "ಇತರೆ"
},
"unlockMethods": {
"message": "Unlock options"
},
"unlockMethodNeededToChangeTimeoutActionDesc": {
"message": "Set up an unlock method to change your vault timeout action."
},
"unlockMethodNeeded": {
"message": "Set up an unlock method in Settings"
},
"sessionTimeoutHeader": {
"message": "Session timeout"
},
"otherOptions": {
"message": "Other options"
},
"rateExtension": {
"message": "ವಿಸ್ತರಣೆಯನ್ನು ರೇಟ್ ಮಾಡಿ"
},
@ -3023,6 +3032,15 @@
"adminConsole": {
"message": "Admin Console"
},
"accountSecurity": {
"message": "Account security"
},
"notifications": {
"message": "Notifications"
},
"appearance": {
"message": "Appearance"
},
"errorAssigningTargetCollection": {
"message": "Error assigning target collection."
},

View File

@ -374,12 +374,21 @@
"other": {
"message": "기타"
},
"unlockMethods": {
"message": "Unlock options"
},
"unlockMethodNeededToChangeTimeoutActionDesc": {
"message": "잠금 해제 방법을 설정하여 보관함의 시간 초과 동작을 변경하세요."
},
"unlockMethodNeeded": {
"message": "설정에서 잠금 해제 수단 설정하기"
},
"sessionTimeoutHeader": {
"message": "Session timeout"
},
"otherOptions": {
"message": "Other options"
},
"rateExtension": {
"message": "확장 프로그램 평가"
},
@ -3023,6 +3032,15 @@
"adminConsole": {
"message": "Admin Console"
},
"accountSecurity": {
"message": "Account security"
},
"notifications": {
"message": "Notifications"
},
"appearance": {
"message": "Appearance"
},
"errorAssigningTargetCollection": {
"message": "Error assigning target collection."
},

View File

@ -374,12 +374,21 @@
"other": {
"message": "Kita"
},
"unlockMethods": {
"message": "Unlock options"
},
"unlockMethodNeededToChangeTimeoutActionDesc": {
"message": "Nustatyk atrakinimo būdą, kad pakeistum saugyklos laiko limito veiksmą."
},
"unlockMethodNeeded": {
"message": "Nustatykite nustatymuose atrakinimo metodą"
},
"sessionTimeoutHeader": {
"message": "Session timeout"
},
"otherOptions": {
"message": "Other options"
},
"rateExtension": {
"message": "Įvertinkite šį plėtinį"
},
@ -3023,6 +3032,15 @@
"adminConsole": {
"message": "Administratoriaus konsolės"
},
"accountSecurity": {
"message": "Account security"
},
"notifications": {
"message": "Notifications"
},
"appearance": {
"message": "Appearance"
},
"errorAssigningTargetCollection": {
"message": "Klaida priskiriant tikslinę kolekciją."
},

View File

@ -374,12 +374,21 @@
"other": {
"message": "Cits"
},
"unlockMethods": {
"message": "Atslēgšanas iespējas"
},
"unlockMethodNeededToChangeTimeoutActionDesc": {
"message": "Jāuzstāda atslēgšanas veids, lai mainītu glabātavas noildzes darbību."
},
"unlockMethodNeeded": {
"message": "Jāuzstāda atslēgšanas veids iestatījumos"
},
"sessionTimeoutHeader": {
"message": "Sesijas noildze"
},
"otherOptions": {
"message": "Citas iespējas"
},
"rateExtension": {
"message": "Novērtēt paplašinājumu"
},
@ -3023,6 +3032,15 @@
"adminConsole": {
"message": "pārvaldības konsolē,"
},
"accountSecurity": {
"message": "Konta drošība"
},
"notifications": {
"message": "Paziņojumi"
},
"appearance": {
"message": "Izskats"
},
"errorAssigningTargetCollection": {
"message": "Kļūda mērķa krājuma piešķiršanā."
},

View File

@ -374,12 +374,21 @@
"other": {
"message": "മറ്റുള്ളവ"
},
"unlockMethods": {
"message": "Unlock options"
},
"unlockMethodNeededToChangeTimeoutActionDesc": {
"message": "Set up an unlock method to change your vault timeout action."
},
"unlockMethodNeeded": {
"message": "Set up an unlock method in Settings"
},
"sessionTimeoutHeader": {
"message": "Session timeout"
},
"otherOptions": {
"message": "Other options"
},
"rateExtension": {
"message": "എക്സ്റ്റൻഷൻ റേറ്റ് ചെയ്യുക "
},
@ -3023,6 +3032,15 @@
"adminConsole": {
"message": "Admin Console"
},
"accountSecurity": {
"message": "Account security"
},
"notifications": {
"message": "Notifications"
},
"appearance": {
"message": "Appearance"
},
"errorAssigningTargetCollection": {
"message": "Error assigning target collection."
},

View File

@ -374,12 +374,21 @@
"other": {
"message": "इतर"
},
"unlockMethods": {
"message": "Unlock options"
},
"unlockMethodNeededToChangeTimeoutActionDesc": {
"message": "Set up an unlock method to change your vault timeout action."
},
"unlockMethodNeeded": {
"message": "Set up an unlock method in Settings"
},
"sessionTimeoutHeader": {
"message": "Session timeout"
},
"otherOptions": {
"message": "Other options"
},
"rateExtension": {
"message": "विस्तारकाचे मूल्यांकन करा"
},
@ -3023,6 +3032,15 @@
"adminConsole": {
"message": "Admin Console"
},
"accountSecurity": {
"message": "Account security"
},
"notifications": {
"message": "Notifications"
},
"appearance": {
"message": "Appearance"
},
"errorAssigningTargetCollection": {
"message": "Error assigning target collection."
},

View File

@ -374,12 +374,21 @@
"other": {
"message": "Other"
},
"unlockMethods": {
"message": "Unlock options"
},
"unlockMethodNeededToChangeTimeoutActionDesc": {
"message": "Set up an unlock method to change your vault timeout action."
},
"unlockMethodNeeded": {
"message": "Set up an unlock method in Settings"
},
"sessionTimeoutHeader": {
"message": "Session timeout"
},
"otherOptions": {
"message": "Other options"
},
"rateExtension": {
"message": "Rate the extension"
},
@ -3023,6 +3032,15 @@
"adminConsole": {
"message": "Admin Console"
},
"accountSecurity": {
"message": "Account security"
},
"notifications": {
"message": "Notifications"
},
"appearance": {
"message": "Appearance"
},
"errorAssigningTargetCollection": {
"message": "Error assigning target collection."
},

View File

@ -374,12 +374,21 @@
"other": {
"message": "Annet"
},
"unlockMethods": {
"message": "Unlock options"
},
"unlockMethodNeededToChangeTimeoutActionDesc": {
"message": "Set up an unlock method to change your vault timeout action."
},
"unlockMethodNeeded": {
"message": "Set up an unlock method in Settings"
},
"sessionTimeoutHeader": {
"message": "Session timeout"
},
"otherOptions": {
"message": "Other options"
},
"rateExtension": {
"message": "Gi denne utvidelsen en vurdering"
},
@ -3023,6 +3032,15 @@
"adminConsole": {
"message": "Admin Console"
},
"accountSecurity": {
"message": "Account security"
},
"notifications": {
"message": "Notifications"
},
"appearance": {
"message": "Appearance"
},
"errorAssigningTargetCollection": {
"message": "Error assigning target collection."
},

View File

@ -374,12 +374,21 @@
"other": {
"message": "Other"
},
"unlockMethods": {
"message": "Unlock options"
},
"unlockMethodNeededToChangeTimeoutActionDesc": {
"message": "Set up an unlock method to change your vault timeout action."
},
"unlockMethodNeeded": {
"message": "Set up an unlock method in Settings"
},
"sessionTimeoutHeader": {
"message": "Session timeout"
},
"otherOptions": {
"message": "Other options"
},
"rateExtension": {
"message": "Rate the extension"
},
@ -3023,6 +3032,15 @@
"adminConsole": {
"message": "Admin Console"
},
"accountSecurity": {
"message": "Account security"
},
"notifications": {
"message": "Notifications"
},
"appearance": {
"message": "Appearance"
},
"errorAssigningTargetCollection": {
"message": "Error assigning target collection."
},

View File

@ -374,12 +374,21 @@
"other": {
"message": "Overig"
},
"unlockMethods": {
"message": "Ontgrendelopties"
},
"unlockMethodNeededToChangeTimeoutActionDesc": {
"message": "Stel een ontgrendelingsmethode in om je kluis time-out actie te wijzigen."
},
"unlockMethodNeeded": {
"message": "Set up an unlock method in Settings"
},
"sessionTimeoutHeader": {
"message": "Sessietime-out"
},
"otherOptions": {
"message": "Andere opties"
},
"rateExtension": {
"message": "Deze extensie beoordelen"
},
@ -3023,6 +3032,15 @@
"adminConsole": {
"message": "Admin Console"
},
"accountSecurity": {
"message": "Accountbeveiliging"
},
"notifications": {
"message": "Notifications"
},
"appearance": {
"message": "Voorkomen"
},
"errorAssigningTargetCollection": {
"message": "Fout bij toewijzen doelverzameling."
},

View File

@ -374,12 +374,21 @@
"other": {
"message": "Other"
},
"unlockMethods": {
"message": "Unlock options"
},
"unlockMethodNeededToChangeTimeoutActionDesc": {
"message": "Set up an unlock method to change your vault timeout action."
},
"unlockMethodNeeded": {
"message": "Set up an unlock method in Settings"
},
"sessionTimeoutHeader": {
"message": "Session timeout"
},
"otherOptions": {
"message": "Other options"
},
"rateExtension": {
"message": "Rate the extension"
},
@ -3023,6 +3032,15 @@
"adminConsole": {
"message": "Admin Console"
},
"accountSecurity": {
"message": "Account security"
},
"notifications": {
"message": "Notifications"
},
"appearance": {
"message": "Appearance"
},
"errorAssigningTargetCollection": {
"message": "Error assigning target collection."
},

View File

@ -374,12 +374,21 @@
"other": {
"message": "Other"
},
"unlockMethods": {
"message": "Unlock options"
},
"unlockMethodNeededToChangeTimeoutActionDesc": {
"message": "Set up an unlock method to change your vault timeout action."
},
"unlockMethodNeeded": {
"message": "Set up an unlock method in Settings"
},
"sessionTimeoutHeader": {
"message": "Session timeout"
},
"otherOptions": {
"message": "Other options"
},
"rateExtension": {
"message": "Rate the extension"
},
@ -3023,6 +3032,15 @@
"adminConsole": {
"message": "Admin Console"
},
"accountSecurity": {
"message": "Account security"
},
"notifications": {
"message": "Notifications"
},
"appearance": {
"message": "Appearance"
},
"errorAssigningTargetCollection": {
"message": "Error assigning target collection."
},

View File

@ -374,12 +374,21 @@
"other": {
"message": "Inne"
},
"unlockMethods": {
"message": "Odblokuj Opcje"
},
"unlockMethodNeededToChangeTimeoutActionDesc": {
"message": "Ustaw metodę odblokowania, aby zmienić czas blokowania sejfu."
},
"unlockMethodNeeded": {
"message": "Ustaw metodę odblokowania w Ustawieniach"
},
"sessionTimeoutHeader": {
"message": "Limit czasu sesji"
},
"otherOptions": {
"message": "Pozostałe opcje"
},
"rateExtension": {
"message": "Oceń rozszerzenie"
},
@ -3023,6 +3032,15 @@
"adminConsole": {
"message": "Konsola Administracyjna"
},
"accountSecurity": {
"message": "Bezpieczeństwo konta"
},
"notifications": {
"message": "Powiadomienia"
},
"appearance": {
"message": "Wygląd"
},
"errorAssigningTargetCollection": {
"message": "Wystąpił błąd podczas przypisywania kolekcji."
},

View File

@ -374,12 +374,21 @@
"other": {
"message": "Outros"
},
"unlockMethods": {
"message": "Unlock options"
},
"unlockMethodNeededToChangeTimeoutActionDesc": {
"message": "Configure um método de desbloqueio para alterar o tempo limite do cofre."
},
"unlockMethodNeeded": {
"message": "Configure um método de desbloqueio nas Configurações"
},
"sessionTimeoutHeader": {
"message": "Session timeout"
},
"otherOptions": {
"message": "Other options"
},
"rateExtension": {
"message": "Avaliar a Extensão"
},
@ -3023,6 +3032,15 @@
"adminConsole": {
"message": "Painel de administração"
},
"accountSecurity": {
"message": "Account security"
},
"notifications": {
"message": "Notifications"
},
"appearance": {
"message": "Appearance"
},
"errorAssigningTargetCollection": {
"message": "Erro ao atribuir coleção de destino."
},

View File

@ -374,12 +374,21 @@
"other": {
"message": "Outros"
},
"unlockMethods": {
"message": "Opções de desbloqueio"
},
"unlockMethodNeededToChangeTimeoutActionDesc": {
"message": "Configure um método de desbloqueio para alterar a ação de tempo limite do seu cofre."
},
"unlockMethodNeeded": {
"message": "Definir um método de desbloqueio nas Definições"
},
"sessionTimeoutHeader": {
"message": "Tempo limite da sessão"
},
"otherOptions": {
"message": "Outras opções"
},
"rateExtension": {
"message": "Avaliar a extensão"
},
@ -3023,6 +3032,15 @@
"adminConsole": {
"message": "Consola de administração"
},
"accountSecurity": {
"message": "Segurança da conta"
},
"notifications": {
"message": "Notificações"
},
"appearance": {
"message": "Aparência"
},
"errorAssigningTargetCollection": {
"message": "Erro ao atribuir a coleção de destino."
},

View File

@ -374,12 +374,21 @@
"other": {
"message": "Altele"
},
"unlockMethods": {
"message": "Unlock options"
},
"unlockMethodNeededToChangeTimeoutActionDesc": {
"message": "Configurați metoda de deblocare care să schimbe acțiunea de expirare a seifului."
},
"unlockMethodNeeded": {
"message": "Set up an unlock method in Settings"
},
"sessionTimeoutHeader": {
"message": "Session timeout"
},
"otherOptions": {
"message": "Other options"
},
"rateExtension": {
"message": "Evaluare extensie"
},
@ -3023,6 +3032,15 @@
"adminConsole": {
"message": "Admin Console"
},
"accountSecurity": {
"message": "Account security"
},
"notifications": {
"message": "Notifications"
},
"appearance": {
"message": "Appearance"
},
"errorAssigningTargetCollection": {
"message": "Error assigning target collection."
},

View File

@ -3,11 +3,11 @@
"message": "Bitwarden"
},
"extName": {
"message": "Bitwarden Password Manager",
"message": "Bitwarden - Менеджер паролей",
"description": "Extension name, MUST be less than 40 characters (Safari restriction)"
},
"extDesc": {
"message": "At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information",
"message": "Дома, на работе или в пути - Bitwarden всегда защитит ваши пароли, passkeys и конфиденциальную информацию",
"description": "Extension description, MUST be less than 112 characters (Safari restriction)"
},
"loginOrCreateNewAccount": {
@ -374,12 +374,21 @@
"other": {
"message": "Прочее"
},
"unlockMethods": {
"message": "Настройки разблокировки"
},
"unlockMethodNeededToChangeTimeoutActionDesc": {
"message": "Настройте способ разблокировки для изменения действия по тайм-ауту хранилища."
},
"unlockMethodNeeded": {
"message": "Установите способ разблокировки в настройках"
},
"sessionTimeoutHeader": {
"message": "Тайм-аут сессии"
},
"otherOptions": {
"message": "Прочие настройки"
},
"rateExtension": {
"message": "Оценить расширение"
},
@ -3023,6 +3032,15 @@
"adminConsole": {
"message": "консоли администратора"
},
"accountSecurity": {
"message": "Безопасность аккаунта"
},
"notifications": {
"message": "Уведомления"
},
"appearance": {
"message": "Внешний вид"
},
"errorAssigningTargetCollection": {
"message": "Ошибка при назначении целевой коллекции."
},

View File

@ -374,12 +374,21 @@
"other": {
"message": "වෙනත්"
},
"unlockMethods": {
"message": "Unlock options"
},
"unlockMethodNeededToChangeTimeoutActionDesc": {
"message": "Set up an unlock method to change your vault timeout action."
},
"unlockMethodNeeded": {
"message": "Set up an unlock method in Settings"
},
"sessionTimeoutHeader": {
"message": "Session timeout"
},
"otherOptions": {
"message": "Other options"
},
"rateExtension": {
"message": "දිගුව අනුපාතය"
},
@ -3023,6 +3032,15 @@
"adminConsole": {
"message": "Admin Console"
},
"accountSecurity": {
"message": "Account security"
},
"notifications": {
"message": "Notifications"
},
"appearance": {
"message": "Appearance"
},
"errorAssigningTargetCollection": {
"message": "Error assigning target collection."
},

View File

@ -374,12 +374,21 @@
"other": {
"message": "Ostatné"
},
"unlockMethods": {
"message": "Možnosti odomknutia"
},
"unlockMethodNeededToChangeTimeoutActionDesc": {
"message": "Nastavte metódu odomknutia, aby ste zmenili akciu pri vypršaní času trezoru."
},
"unlockMethodNeeded": {
"message": "Nastavte metódu odomknutia v Nastaveniach"
},
"sessionTimeoutHeader": {
"message": "Časový limit relácie"
},
"otherOptions": {
"message": "Ďalšie možnosti"
},
"rateExtension": {
"message": "Ohodnotiť rozšírenie"
},
@ -3023,6 +3032,15 @@
"adminConsole": {
"message": "Správcovská konzola"
},
"accountSecurity": {
"message": "Zabezpečenie účtu"
},
"notifications": {
"message": "Upozornenia"
},
"appearance": {
"message": "Vzhľad"
},
"errorAssigningTargetCollection": {
"message": "Chyba pri priraďovaní cieľovej kolekcie."
},

View File

@ -374,12 +374,21 @@
"other": {
"message": "Drugo"
},
"unlockMethods": {
"message": "Unlock options"
},
"unlockMethodNeededToChangeTimeoutActionDesc": {
"message": "Da spremenite časovne omejitve trezorja, nastavite metodo odklepanja."
},
"unlockMethodNeeded": {
"message": "Set up an unlock method in Settings"
},
"sessionTimeoutHeader": {
"message": "Session timeout"
},
"otherOptions": {
"message": "Other options"
},
"rateExtension": {
"message": "Ocenite to razširitev"
},
@ -3023,6 +3032,15 @@
"adminConsole": {
"message": "Admin Console"
},
"accountSecurity": {
"message": "Account security"
},
"notifications": {
"message": "Notifications"
},
"appearance": {
"message": "Appearance"
},
"errorAssigningTargetCollection": {
"message": "Error assigning target collection."
},

View File

@ -374,12 +374,21 @@
"other": {
"message": "Остало"
},
"unlockMethods": {
"message": "Unlock options"
},
"unlockMethodNeededToChangeTimeoutActionDesc": {
"message": "Подесите метод откључавања да бисте променили радњу временског ограничења сефа."
},
"unlockMethodNeeded": {
"message": "Подесите метод откључавања у подешавањима"
},
"sessionTimeoutHeader": {
"message": "Session timeout"
},
"otherOptions": {
"message": "Other options"
},
"rateExtension": {
"message": "Оцени овај додатак"
},
@ -3023,6 +3032,15 @@
"adminConsole": {
"message": "Администраторска конзола"
},
"accountSecurity": {
"message": "Account security"
},
"notifications": {
"message": "Notifications"
},
"appearance": {
"message": "Appearance"
},
"errorAssigningTargetCollection": {
"message": "Грешка при додељивању циљне колекције."
},

View File

@ -374,12 +374,21 @@
"other": {
"message": "Annat"
},
"unlockMethods": {
"message": "Unlock options"
},
"unlockMethodNeededToChangeTimeoutActionDesc": {
"message": "Set up an unlock method to change your vault timeout action."
},
"unlockMethodNeeded": {
"message": "Ställ in en upplåsningsmetod i Inställningar"
},
"sessionTimeoutHeader": {
"message": "Session timeout"
},
"otherOptions": {
"message": "Other options"
},
"rateExtension": {
"message": "Betygsätt tillägget"
},
@ -3023,6 +3032,15 @@
"adminConsole": {
"message": "Admin Console"
},
"accountSecurity": {
"message": "Account security"
},
"notifications": {
"message": "Notifications"
},
"appearance": {
"message": "Utseende"
},
"errorAssigningTargetCollection": {
"message": "Error assigning target collection."
},

View File

@ -374,12 +374,21 @@
"other": {
"message": "Other"
},
"unlockMethods": {
"message": "Unlock options"
},
"unlockMethodNeededToChangeTimeoutActionDesc": {
"message": "Set up an unlock method to change your vault timeout action."
},
"unlockMethodNeeded": {
"message": "Set up an unlock method in Settings"
},
"sessionTimeoutHeader": {
"message": "Session timeout"
},
"otherOptions": {
"message": "Other options"
},
"rateExtension": {
"message": "Rate the extension"
},
@ -3023,6 +3032,15 @@
"adminConsole": {
"message": "Admin Console"
},
"accountSecurity": {
"message": "Account security"
},
"notifications": {
"message": "Notifications"
},
"appearance": {
"message": "Appearance"
},
"errorAssigningTargetCollection": {
"message": "Error assigning target collection."
},

View File

@ -374,12 +374,21 @@
"other": {
"message": "อื่น ๆ"
},
"unlockMethods": {
"message": "Unlock options"
},
"unlockMethodNeededToChangeTimeoutActionDesc": {
"message": "Set up an unlock method to change your vault timeout action."
},
"unlockMethodNeeded": {
"message": "Set up an unlock method in Settings"
},
"sessionTimeoutHeader": {
"message": "Session timeout"
},
"otherOptions": {
"message": "Other options"
},
"rateExtension": {
"message": "Rate the Extension"
},
@ -3023,6 +3032,15 @@
"adminConsole": {
"message": "Admin Console"
},
"accountSecurity": {
"message": "Account security"
},
"notifications": {
"message": "Notifications"
},
"appearance": {
"message": "Appearance"
},
"errorAssigningTargetCollection": {
"message": "Error assigning target collection."
},

View File

@ -374,12 +374,21 @@
"other": {
"message": "Diğer"
},
"unlockMethods": {
"message": "Kilit açma seçenekleri"
},
"unlockMethodNeededToChangeTimeoutActionDesc": {
"message": "Kasa zaman aşımı eyleminizi değiştirmek için kilit açma yönteminizi ayarlayın."
},
"unlockMethodNeeded": {
"message": "Ayarlar'da bir kilit açma yöntemi ayarlayın"
},
"sessionTimeoutHeader": {
"message": "Oturum zaman aşımı"
},
"otherOptions": {
"message": "Diğer seçenekler"
},
"rateExtension": {
"message": "Uzantıyı değerlendirin"
},
@ -3023,10 +3032,19 @@
"adminConsole": {
"message": "Yönetici Konsolu"
},
"accountSecurity": {
"message": "Hesap güvenliği"
},
"notifications": {
"message": "Notifications"
},
"appearance": {
"message": "Appearance"
},
"errorAssigningTargetCollection": {
"message": "Error assigning target collection."
"message": "Hedef koleksiyonu atama hatası."
},
"errorAssigningTargetFolder": {
"message": "Error assigning target folder."
"message": "Hedef klasörü atama hatası."
}
}

View File

@ -3,11 +3,11 @@
"message": "Bitwarden"
},
"extName": {
"message": "Bitwarden Password Manager",
"message": "Bitwarden менеджер паролів",
"description": "Extension name, MUST be less than 40 characters (Safari restriction)"
},
"extDesc": {
"message": "At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information",
"message": "Вдома, на роботі чи в дорозі, Bitwarden захищає ваші паролі, ключі доступу та конфіденційну інформацію",
"description": "Extension description, MUST be less than 112 characters (Safari restriction)"
},
"loginOrCreateNewAccount": {
@ -374,12 +374,21 @@
"other": {
"message": "Інше"
},
"unlockMethods": {
"message": "Налаштування розблокування"
},
"unlockMethodNeededToChangeTimeoutActionDesc": {
"message": "Налаштуйте спосіб розблокування, щоб змінити час очікування сховища."
},
"unlockMethodNeeded": {
"message": "Встановіть спосіб розблокування в налаштуваннях"
},
"sessionTimeoutHeader": {
"message": "Час очікування сеансу"
},
"otherOptions": {
"message": "Інші налаштування"
},
"rateExtension": {
"message": "Оцінити розширення"
},
@ -3023,6 +3032,15 @@
"adminConsole": {
"message": "консолі адміністратора,"
},
"accountSecurity": {
"message": "Безпека облікового запису"
},
"notifications": {
"message": "Сповіщення"
},
"appearance": {
"message": "Вигляд"
},
"errorAssigningTargetCollection": {
"message": "Помилка призначення цільової збірки."
},

View File

@ -374,12 +374,21 @@
"other": {
"message": "Khác"
},
"unlockMethods": {
"message": "Unlock options"
},
"unlockMethodNeededToChangeTimeoutActionDesc": {
"message": "Thiết lập phương thức mở khóa để thay đổi hành động hết thời gian chờ của vault."
},
"unlockMethodNeeded": {
"message": "Thiết lập phương pháp mở khóa trong Cài đặt"
},
"sessionTimeoutHeader": {
"message": "Session timeout"
},
"otherOptions": {
"message": "Other options"
},
"rateExtension": {
"message": "Đánh giá tiện ích mở rộng"
},
@ -3023,6 +3032,15 @@
"adminConsole": {
"message": "Bảng điều khiển dành cho quản trị viên"
},
"accountSecurity": {
"message": "Account security"
},
"notifications": {
"message": "Notifications"
},
"appearance": {
"message": "Appearance"
},
"errorAssigningTargetCollection": {
"message": "Error assigning target collection."
},

View File

@ -374,12 +374,21 @@
"other": {
"message": "其他"
},
"unlockMethods": {
"message": "解锁选项"
},
"unlockMethodNeededToChangeTimeoutActionDesc": {
"message": "设置一个解锁方式以更改您的密码库超时动作。"
},
"unlockMethodNeeded": {
"message": "在设置中设置一个解锁方式"
},
"sessionTimeoutHeader": {
"message": "会话超时"
},
"otherOptions": {
"message": "其他选项"
},
"rateExtension": {
"message": "为本扩展打分"
},
@ -3023,6 +3032,15 @@
"adminConsole": {
"message": "管理控制台"
},
"accountSecurity": {
"message": "账户安全"
},
"notifications": {
"message": "通知"
},
"appearance": {
"message": "外观"
},
"errorAssigningTargetCollection": {
"message": "分配目标集合时出错。"
},

View File

@ -374,12 +374,21 @@
"other": {
"message": "其他"
},
"unlockMethods": {
"message": "Unlock options"
},
"unlockMethodNeededToChangeTimeoutActionDesc": {
"message": "設定一個解鎖方式來變更您的密碼庫逾時動作。"
},
"unlockMethodNeeded": {
"message": "設定中設定解鎖"
},
"sessionTimeoutHeader": {
"message": "Session timeout"
},
"otherOptions": {
"message": "Other options"
},
"rateExtension": {
"message": "為本套件評分"
},
@ -3023,6 +3032,15 @@
"adminConsole": {
"message": "Admin Console"
},
"accountSecurity": {
"message": "Account security"
},
"notifications": {
"message": "Notifications"
},
"appearance": {
"message": "Appearance"
},
"errorAssigningTargetCollection": {
"message": "Error assigning target collection."
},

View File

@ -4,6 +4,10 @@ import {
policyServiceFactory,
PolicyServiceInitOptions,
} from "../../../admin-console/background/service-factories/policy-service.factory";
import {
vaultTimeoutSettingsServiceFactory,
VaultTimeoutSettingsServiceInitOptions,
} from "../../../background/service-factories/vault-timeout-settings-service.factory";
import {
apiServiceFactory,
ApiServiceInitOptions,
@ -108,6 +112,7 @@ export type LoginStrategyServiceInitOptions = LoginStrategyServiceFactoryOptions
UserDecryptionOptionsServiceInitOptions &
GlobalStateProviderInitOptions &
BillingAccountProfileStateServiceInitOptions &
VaultTimeoutSettingsServiceInitOptions &
KdfConfigServiceInitOptions;
export function loginStrategyServiceFactory(
@ -142,6 +147,7 @@ export function loginStrategyServiceFactory(
await internalUserDecryptionOptionServiceFactory(cache, opts),
await globalStateProviderFactory(cache, opts),
await billingAccountProfileStateServiceFactory(cache, opts),
await vaultTimeoutSettingsServiceFactory(cache, opts),
await kdfConfigServiceFactory(cache, opts),
),
);

View File

@ -4,20 +4,35 @@ import {
} from "@bitwarden/common/auth/abstractions/master-password.service.abstraction";
import { MasterPasswordService } from "@bitwarden/common/auth/services/master-password/master-password.service";
import {
encryptServiceFactory,
EncryptServiceInitOptions,
} from "../../../platform/background/service-factories/encrypt-service.factory";
import {
CachedServices,
factory,
FactoryOptions,
} from "../../../platform/background/service-factories/factory-options";
import {
keyGenerationServiceFactory,
KeyGenerationServiceInitOptions,
} from "../../../platform/background/service-factories/key-generation-service.factory";
import {
stateProviderFactory,
StateProviderInitOptions,
} from "../../../platform/background/service-factories/state-provider.factory";
import {
stateServiceFactory,
StateServiceInitOptions,
} from "../../../platform/background/service-factories/state-service.factory";
type MasterPasswordServiceFactoryOptions = FactoryOptions;
export type MasterPasswordServiceInitOptions = MasterPasswordServiceFactoryOptions &
StateProviderInitOptions;
StateProviderInitOptions &
StateServiceInitOptions &
KeyGenerationServiceInitOptions &
EncryptServiceInitOptions;
export function internalMasterPasswordServiceFactory(
cache: { masterPasswordService?: InternalMasterPasswordServiceAbstraction } & CachedServices,
@ -27,7 +42,13 @@ export function internalMasterPasswordServiceFactory(
cache,
"masterPasswordService",
opts,
async () => new MasterPasswordService(await stateProviderFactory(cache, opts)),
async () =>
new MasterPasswordService(
await stateProviderFactory(cache, opts),
await stateServiceFactory(cache, opts),
await keyGenerationServiceFactory(cache, opts),
await encryptServiceFactory(cache, opts),
),
);
}

View File

@ -1,53 +0,0 @@
import { PinCryptoServiceAbstraction, PinCryptoService } from "@bitwarden/auth/common";
import {
VaultTimeoutSettingsServiceInitOptions,
vaultTimeoutSettingsServiceFactory,
} from "../../../background/service-factories/vault-timeout-settings-service.factory";
import {
CryptoServiceInitOptions,
cryptoServiceFactory,
} from "../../../platform/background/service-factories/crypto-service.factory";
import {
FactoryOptions,
CachedServices,
factory,
} from "../../../platform/background/service-factories/factory-options";
import {
LogServiceInitOptions,
logServiceFactory,
} from "../../../platform/background/service-factories/log-service.factory";
import {
StateServiceInitOptions,
stateServiceFactory,
} from "../../../platform/background/service-factories/state-service.factory";
import { KdfConfigServiceInitOptions, kdfConfigServiceFactory } from "./kdf-config-service.factory";
type PinCryptoServiceFactoryOptions = FactoryOptions;
export type PinCryptoServiceInitOptions = PinCryptoServiceFactoryOptions &
StateServiceInitOptions &
CryptoServiceInitOptions &
VaultTimeoutSettingsServiceInitOptions &
LogServiceInitOptions &
KdfConfigServiceInitOptions;
export function pinCryptoServiceFactory(
cache: { pinCryptoService?: PinCryptoServiceAbstraction } & CachedServices,
opts: PinCryptoServiceInitOptions,
): Promise<PinCryptoServiceAbstraction> {
return factory(
cache,
"pinCryptoService",
opts,
async () =>
new PinCryptoService(
await stateServiceFactory(cache, opts),
await cryptoServiceFactory(cache, opts),
await vaultTimeoutSettingsServiceFactory(cache, opts),
await logServiceFactory(cache, opts),
await kdfConfigServiceFactory(cache, opts),
),
);
}

View File

@ -0,0 +1,74 @@
import { PinServiceAbstraction, PinService } from "@bitwarden/auth/common";
import {
CryptoFunctionServiceInitOptions,
cryptoFunctionServiceFactory,
} from "../../../platform/background/service-factories/crypto-function-service.factory";
import {
EncryptServiceInitOptions,
encryptServiceFactory,
} from "../../../platform/background/service-factories/encrypt-service.factory";
import {
FactoryOptions,
CachedServices,
factory,
} from "../../../platform/background/service-factories/factory-options";
import {
KeyGenerationServiceInitOptions,
keyGenerationServiceFactory,
} from "../../../platform/background/service-factories/key-generation-service.factory";
import {
LogServiceInitOptions,
logServiceFactory,
} from "../../../platform/background/service-factories/log-service.factory";
import {
StateProviderInitOptions,
stateProviderFactory,
} from "../../../platform/background/service-factories/state-provider.factory";
import {
StateServiceInitOptions,
stateServiceFactory,
} from "../../../platform/background/service-factories/state-service.factory";
import { AccountServiceInitOptions, accountServiceFactory } from "./account-service.factory";
import { KdfConfigServiceInitOptions, kdfConfigServiceFactory } from "./kdf-config-service.factory";
import {
MasterPasswordServiceInitOptions,
masterPasswordServiceFactory,
} from "./master-password-service.factory";
type PinServiceFactoryOptions = FactoryOptions;
export type PinServiceInitOptions = PinServiceFactoryOptions &
AccountServiceInitOptions &
CryptoFunctionServiceInitOptions &
EncryptServiceInitOptions &
KdfConfigServiceInitOptions &
KeyGenerationServiceInitOptions &
LogServiceInitOptions &
MasterPasswordServiceInitOptions &
StateProviderInitOptions &
StateServiceInitOptions;
export function pinServiceFactory(
cache: { pinService?: PinServiceAbstraction } & CachedServices,
opts: PinServiceInitOptions,
): Promise<PinServiceAbstraction> {
return factory(
cache,
"pinService",
opts,
async () =>
new PinService(
await accountServiceFactory(cache, opts),
await cryptoFunctionServiceFactory(cache, opts),
await encryptServiceFactory(cache, opts),
await kdfConfigServiceFactory(cache, opts),
await keyGenerationServiceFactory(cache, opts),
await logServiceFactory(cache, opts),
await masterPasswordServiceFactory(cache, opts),
await stateProviderFactory(cache, opts),
await stateServiceFactory(cache, opts),
),
);
}

View File

@ -37,7 +37,7 @@ import {
internalMasterPasswordServiceFactory,
MasterPasswordServiceInitOptions,
} from "./master-password-service.factory";
import { PinCryptoServiceInitOptions, pinCryptoServiceFactory } from "./pin-crypto-service.factory";
import { PinServiceInitOptions, pinServiceFactory } from "./pin-service.factory";
import {
userDecryptionOptionsServiceFactory,
UserDecryptionOptionsServiceInitOptions,
@ -57,7 +57,7 @@ export type UserVerificationServiceInitOptions = UserVerificationServiceFactoryO
I18nServiceInitOptions &
UserVerificationApiServiceInitOptions &
UserDecryptionOptionsServiceInitOptions &
PinCryptoServiceInitOptions &
PinServiceInitOptions &
LogServiceInitOptions &
VaultTimeoutSettingsServiceInitOptions &
PlatformUtilsServiceInitOptions &
@ -80,7 +80,7 @@ export function userVerificationServiceFactory(
await i18nServiceFactory(cache, opts),
await userVerificationApiServiceFactory(cache, opts),
await userDecryptionOptionsServiceFactory(cache, opts),
await pinCryptoServiceFactory(cache, opts),
await pinServiceFactory(cache, opts),
await logServiceFactory(cache, opts),
await vaultTimeoutSettingsServiceFactory(cache, opts),
await platformUtilsServiceFactory(cache, opts),

View File

@ -26,7 +26,9 @@ export const fido2AuthGuard: CanActivateFn = async (
const authStatus = await authService.getAuthStatus();
if (authStatus === AuthenticationStatus.Locked) {
routerService.setPreviousUrl(state.url);
// Appending fromLock=true to the query params to indicate that the user is being redirected from the lock screen, this is used for user verification.
const previousUrl = `${state.url}&fromLock=true`;
routerService.setPreviousUrl(previousUrl);
return router.createUrlTree(["/lock"], { queryParams: route.queryParams });
}

View File

@ -1,7 +1,7 @@
import { Location } from "@angular/common";
import { Component, OnDestroy, OnInit } from "@angular/core";
import { Router } from "@angular/router";
import { Subject, firstValueFrom, map, switchMap, takeUntil } from "rxjs";
import { Subject, firstValueFrom, map, of, 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";
@ -49,7 +49,7 @@ export class AccountSwitcherComponent implements OnInit, OnDestroy {
readonly currentAccount$ = this.accountService.activeAccount$.pipe(
switchMap((a) =>
a == null
? null
? of(null)
: this.authService.activeAccountStatus$.pipe(map((s) => ({ ...a, status: s }))),
),
);
@ -106,12 +106,14 @@ export class AccountSwitcherComponent implements OnInit, OnDestroy {
});
if (confirmed) {
this.messagingService.send("logout", { userId });
const result = await this.accountSwitcherService.logoutAccount(userId);
// unlocked logout responses need to be navigated out of the account switcher.
// other responses will be handled by background and app.component
if (result?.status === AuthenticationStatus.Unlocked) {
this.location.back();
}
}
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
// eslint-disable-next-line @typescript-eslint/no-floating-promises
this.router.navigate(["home"]);
this.loading = false;
}
ngOnDestroy() {

View File

@ -1,10 +1,10 @@
import { CommonModule, Location } from "@angular/common";
import { Component, EventEmitter, Input, Output } from "@angular/core";
import { Router } from "@angular/router";
import { JslibModule } from "@bitwarden/angular/jslib.module";
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { AvatarModule } from "@bitwarden/components";
import { AccountSwitcherService, AvailableAccount } from "./services/account-switcher.service";
@ -21,9 +21,9 @@ export class AccountComponent {
constructor(
private accountSwitcherService: AccountSwitcherService,
private router: Router,
private location: Location,
private i18nService: I18nService,
private logService: LogService,
) {}
get specialAccountAddId() {
@ -32,15 +32,19 @@ export class AccountComponent {
async selectAccount(id: string) {
this.loading.emit(true);
await this.accountSwitcherService.selectAccount(id);
let result;
try {
result = await this.accountSwitcherService.selectAccount(id);
} catch (e) {
this.logService.error("Error selecting account", e);
}
if (id === this.specialAccountAddId) {
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
// eslint-disable-next-line @typescript-eslint/no-floating-promises
this.router.navigate(["home"]);
} else {
// Navigate out of account switching for unlocked accounts
// locked or logged out account statuses are handled by background and app.component
if (result?.status === AuthenticationStatus.Unlocked) {
this.location.back();
}
this.loading.emit(false);
}
get status() {

View File

@ -186,4 +186,35 @@ describe("AccountSwitcherService", () => {
expect(removeListenerSpy).toBeCalledTimes(1);
});
});
describe("logout", () => {
const userId1 = "1" as UserId;
const userId2 = "2" as UserId;
it("initiates logout", async () => {
let listener: (
message: { command: string; userId: UserId; status: AuthenticationStatus },
sender: unknown,
sendResponse: unknown,
) => void;
jest.spyOn(chrome.runtime.onMessage, "addListener").mockImplementation((addedListener) => {
listener = addedListener;
});
const removeListenerSpy = jest.spyOn(chrome.runtime.onMessage, "removeListener");
const logoutPromise = accountSwitcherService.logoutAccount(userId1);
listener(
{ command: "switchAccountFinish", userId: userId2, status: AuthenticationStatus.Unlocked },
undefined,
undefined,
);
const result = await logoutPromise;
expect(messagingService.send).toHaveBeenCalledWith("logout", { userId: userId1 });
expect(result).toEqual({ newUserId: userId2, status: AuthenticationStatus.Unlocked });
expect(removeListenerSpy).toBeCalledTimes(1);
});
});
});

View File

@ -41,7 +41,7 @@ export class AccountSwitcherService {
SPECIAL_ADD_ACCOUNT_ID = "addAccount";
availableAccounts$: Observable<AvailableAccount[]>;
switchAccountFinished$: Observable<string>;
switchAccountFinished$: Observable<{ userId: UserId; status: AuthenticationStatus }>;
constructor(
private accountService: AccountService,
@ -111,11 +111,11 @@ export class AccountSwitcherService {
);
// Create a reusable observable that listens to the switchAccountFinish message and returns the userId from the message
this.switchAccountFinished$ = fromChromeEvent<[message: { command: string; userId: string }]>(
chrome.runtime.onMessage,
).pipe(
this.switchAccountFinished$ = fromChromeEvent<
[message: { command: string; userId: UserId; status: AuthenticationStatus }]
>(chrome.runtime.onMessage).pipe(
filter(([message]) => message.command === "switchAccountFinish"),
map(([message]) => message.userId),
map(([message]) => ({ userId: message.userId, status: message.status })),
);
}
@ -127,12 +127,46 @@ export class AccountSwitcherService {
if (id === this.SPECIAL_ADD_ACCOUNT_ID) {
id = null;
}
const userId = id as UserId;
// Creates a subscription to the switchAccountFinished observable but further
// filters it to only care about the current userId.
const switchAccountFinishedPromise = firstValueFrom(
const switchAccountFinishedPromise = this.listenForSwitchAccountFinish(userId);
// Initiate the actions required to make account switching happen
await this.accountService.switchAccount(userId);
this.messagingService.send("switchAccount", { userId }); // This message should cause switchAccountFinish to be sent
// Wait until we receive the switchAccountFinished message
return await switchAccountFinishedPromise;
}
/**
*
* @param userId the user id to logout
* @returns the userId and status of the that has been switch to due to the logout. null on errors.
*/
async logoutAccount(
userId: UserId,
): Promise<{ newUserId: UserId; status: AuthenticationStatus } | null> {
// logout creates an account switch to the next up user, which may be null
const switchPromise = this.listenForSwitchAccountFinish(null);
await this.messagingService.send("logout", { userId });
// wait for account switch to happen, the result will be the new user id and status
const result = await switchPromise;
return { newUserId: result.userId, status: result.status };
}
// Listens for the switchAccountFinish message and returns the userId from the message
// Optionally filters switchAccountFinish to an expected userId
private listenForSwitchAccountFinish(
expectedUserId: UserId | null,
): Promise<{ userId: UserId; status: AuthenticationStatus } | null> {
return firstValueFrom(
this.switchAccountFinished$.pipe(
filter((userId) => userId === id),
filter(({ userId }) => (expectedUserId ? userId === expectedUserId : true)),
timeout({
// Much longer than account switching is expected to take for normal accounts
// but the account switching process includes a possible full sync so we need to account
@ -143,20 +177,13 @@ export class AccountSwitcherService {
throwError(() => new Error(AccountSwitcherService.incompleteAccountSwitchError)),
}),
),
);
// Initiate the actions required to make account switching happen
await this.accountService.switchAccount(id as UserId);
this.messagingService.send("switchAccount", { userId: id }); // This message should cause switchAccountFinish to be sent
// Wait until we recieve the switchAccountFinished message
await switchAccountFinishedPromise.catch((err) => {
).catch((err) => {
if (
err instanceof Error &&
err.message === AccountSwitcherService.incompleteAccountSwitchError
) {
this.logService.warning("message 'switchAccount' never responded.");
return;
return null;
}
throw err;
});

View File

@ -12,8 +12,16 @@
<input class="tw-font-mono" bitInput type="password" formControlName="pin" />
<button type="button" bitIconButton bitSuffix bitPasswordInputToggle></button>
</bit-form-field>
<label class="tw-flex tw-items-start tw-gap-2" *ngIf="showMasterPassOnRestart">
<input class="tw-mt-1" type="checkbox" bitCheckbox formControlName="masterPassOnRestart" />
<label
class="tw-flex tw-items-start tw-gap-2"
*ngIf="showMasterPasswordOnClientRestartOption"
>
<input
class="tw-mt-1"
type="checkbox"
bitCheckbox
formControlName="requireMasterPasswordOnClientRestart"
/>
<span>{{ "lockWithMasterPassOnRestart" | i18n }}</span>
</label>
</div>

View File

@ -3,7 +3,7 @@ import { Router } from "@angular/router";
import { firstValueFrom } from "rxjs";
import { LockComponent as BaseLockComponent } from "@bitwarden/angular/auth/components/lock.component";
import { PinCryptoServiceAbstraction } from "@bitwarden/auth/common";
import { PinServiceAbstraction } from "@bitwarden/auth/common";
import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { VaultTimeoutSettingsService } from "@bitwarden/common/abstractions/vault-timeout/vault-timeout-settings.service";
import { VaultTimeoutService } from "@bitwarden/common/abstractions/vault-timeout/vault-timeout.service";
@ -63,7 +63,7 @@ export class LockComponent extends BaseLockComponent {
dialogService: DialogService,
deviceTrustService: DeviceTrustServiceAbstraction,
userVerificationService: UserVerificationService,
pinCryptoService: PinCryptoServiceAbstraction,
pinService: PinServiceAbstraction,
private routerService: BrowserRouterService,
biometricStateService: BiometricStateService,
accountService: AccountService,
@ -89,7 +89,7 @@ export class LockComponent extends BaseLockComponent {
dialogService,
deviceTrustService,
userVerificationService,
pinCryptoService,
pinService,
biometricStateService,
accountService,
authService,

View File

@ -16,6 +16,7 @@ import {
} from "rxjs";
import { FingerprintDialogComponent } from "@bitwarden/auth/angular";
import { PinServiceAbstraction } from "@bitwarden/auth/common";
import { VaultTimeoutSettingsService } from "@bitwarden/common/abstractions/vault-timeout/vault-timeout-settings.service";
import { VaultTimeoutService } from "@bitwarden/common/abstractions/vault-timeout/vault-timeout.service";
import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
@ -30,6 +31,11 @@ import { MessagingService } from "@bitwarden/common/platform/abstractions/messag
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
import { BiometricStateService } from "@bitwarden/common/platform/biometrics/biometric-state.service";
import {
VaultTimeout,
VaultTimeoutOption,
VaultTimeoutStringType,
} from "@bitwarden/common/types/vault-timeout.type";
import { DialogService } from "@bitwarden/components";
import { BiometricErrors, BiometricErrorTypes } from "../../../models/biometricErrors";
@ -49,7 +55,7 @@ export class AccountSecurityComponent implements OnInit {
protected readonly VaultTimeoutAction = VaultTimeoutAction;
availableVaultTimeoutActions: VaultTimeoutAction[] = [];
vaultTimeoutOptions: any[];
vaultTimeoutOptions: VaultTimeoutOption[];
vaultTimeoutPolicyCallout: Observable<{
timeout: { hours: number; minutes: number };
action: VaultTimeoutAction;
@ -59,7 +65,7 @@ export class AccountSecurityComponent implements OnInit {
accountSwitcherEnabled = false;
form = this.formBuilder.group({
vaultTimeout: [null as number | null],
vaultTimeout: [null as VaultTimeout | null],
vaultTimeoutAction: [VaultTimeoutAction.Lock],
pin: [null as boolean | null],
biometric: false,
@ -71,6 +77,7 @@ export class AccountSecurityComponent implements OnInit {
constructor(
private accountService: AccountService,
private pinService: PinServiceAbstraction,
private policyService: PolicyService,
private formBuilder: FormBuilder,
private platformUtilsService: PlatformUtilsService,
@ -116,22 +123,32 @@ export class AccountSecurityComponent implements OnInit {
{ name: this.i18nService.t("thirtyMinutes"), value: 30 },
{ name: this.i18nService.t("oneHour"), value: 60 },
{ name: this.i18nService.t("fourHours"), value: 240 },
// { name: i18nService.t('onIdle'), value: -4 },
// { name: i18nService.t('onSleep'), value: -3 },
];
if (showOnLocked) {
this.vaultTimeoutOptions.push({ name: this.i18nService.t("onLocked"), value: -2 });
this.vaultTimeoutOptions.push({
name: this.i18nService.t("onLocked"),
value: VaultTimeoutStringType.OnLocked,
});
}
this.vaultTimeoutOptions.push({ name: this.i18nService.t("onRestart"), value: -1 });
this.vaultTimeoutOptions.push({ name: this.i18nService.t("never"), value: null });
this.vaultTimeoutOptions.push({
name: this.i18nService.t("onRestart"),
value: VaultTimeoutStringType.OnRestart,
});
this.vaultTimeoutOptions.push({
name: this.i18nService.t("never"),
value: VaultTimeoutStringType.Never,
});
let timeout = await this.vaultTimeoutSettingsService.getVaultTimeout();
if (timeout === -2 && !showOnLocked) {
timeout = -1;
const activeAccount = await firstValueFrom(this.accountService.activeAccount$);
let timeout = await firstValueFrom(
this.vaultTimeoutSettingsService.getVaultTimeoutByUserId$(activeAccount.id),
);
if (timeout === VaultTimeoutStringType.OnLocked && !showOnLocked) {
timeout = VaultTimeoutStringType.OnRestart;
}
const pinStatus = await this.vaultTimeoutSettingsService.isPinLockSet();
this.form.controls.vaultTimeout.valueChanges
.pipe(
@ -153,12 +170,14 @@ export class AccountSecurityComponent implements OnInit {
)
.subscribe();
const userId = (await firstValueFrom(this.accountService.activeAccount$))?.id;
const initialValues = {
vaultTimeout: timeout,
vaultTimeoutAction: await firstValueFrom(
this.vaultTimeoutSettingsService.vaultTimeoutAction$(),
this.vaultTimeoutSettingsService.getVaultTimeoutActionByUserId$(activeAccount.id),
),
pin: pinStatus !== "DISABLED",
pin: await this.pinService.isPinSet(userId),
biometric: await this.vaultTimeoutSettingsService.isBiometricLockSet(),
enableAutoBiometricsPrompt: await firstValueFrom(
this.biometricStateService.promptAutomatically$,
@ -200,7 +219,7 @@ export class AccountSecurityComponent implements OnInit {
switchMap(() =>
combineLatest([
this.vaultTimeoutSettingsService.availableVaultTimeoutActions$(),
this.vaultTimeoutSettingsService.vaultTimeoutAction$(),
this.vaultTimeoutSettingsService.getVaultTimeoutActionByUserId$(activeAccount.id),
]),
),
takeUntil(this.destroy$),
@ -234,8 +253,8 @@ export class AccountSecurityComponent implements OnInit {
});
}
async saveVaultTimeout(previousValue: number, newValue: number) {
if (newValue == null) {
async saveVaultTimeout(previousValue: VaultTimeout, newValue: VaultTimeout) {
if (newValue === VaultTimeoutStringType.Never) {
const confirmed = await this.dialogService.openSimpleDialog({
title: { key: "warning" },
content: { key: "neverLockWarning" },
@ -259,11 +278,18 @@ export class AccountSecurityComponent implements OnInit {
return;
}
await this.vaultTimeoutSettingsService.setVaultTimeoutOptions(
newValue,
await firstValueFrom(this.vaultTimeoutSettingsService.vaultTimeoutAction$()),
const activeAccount = await firstValueFrom(this.accountService.activeAccount$);
const vaultTimeoutAction = await firstValueFrom(
this.vaultTimeoutSettingsService.getVaultTimeoutActionByUserId$(activeAccount.id),
);
if (newValue == null) {
await this.vaultTimeoutSettingsService.setVaultTimeoutOptions(
activeAccount.id,
newValue,
vaultTimeoutAction,
);
if (newValue === VaultTimeoutStringType.Never) {
this.messagingService.send("bgReseedStorage");
}
}
@ -293,7 +319,10 @@ export class AccountSecurityComponent implements OnInit {
return;
}
const activeAccount = await firstValueFrom(this.accountService.activeAccount$);
await this.vaultTimeoutSettingsService.setVaultTimeoutOptions(
activeAccount.id,
this.form.value.vaultTimeout,
newValue,
);

View File

@ -1,7 +1,7 @@
<form #form (ngSubmit)="submit()">
<header>
<div class="left">
<button type="button" routerLink="/tabs/settings">
<button type="button" routerLink="/notifications">
<span class="header-icon"><i class="bwi bwi-angle-left" aria-hidden="true"></i></span>
<span>{{ "back" | i18n }}</span>
</button>

View File

@ -8,8 +8,8 @@ import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.servic
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { Utils } from "@bitwarden/common/platform/misc/utils";
import { BrowserApi } from "../../platform/browser/browser-api";
import { enableAccountSwitching } from "../../platform/flags";
import { BrowserApi } from "../../../platform/browser/browser-api";
import { enableAccountSwitching } from "../../../platform/flags";
interface ExcludedDomain {
uri: string;

View File

@ -0,0 +1,89 @@
<header>
<div class="left">
<button type="button" routerLink="/tabs/settings">
<span class="header-icon"><i class="bwi bwi-angle-left" aria-hidden="true"></i></span>
<span>{{ "back" | i18n }}</span>
</button>
</div>
<h1 class="center">
<span class="title">{{ "notifications" | i18n }}</span>
</h1>
<div class="right">
<app-pop-out></app-pop-out>
</div>
</header>
<main tabindex="-1">
<div class="box">
<div class="box-content">
<div class="box-content-row box-content-row-checkbox" appBoxRow>
<label for="use-passkeys">{{ "enableUsePasskeys" | i18n }}</label>
<input
id="use-passkeys"
type="checkbox"
aria-describedby="use-passkeysHelp"
(change)="updateEnablePasskeys()"
[(ngModel)]="enablePasskeys"
/>
</div>
</div>
<div id="use-passkeysHelp" class="box-footer">
{{ "usePasskeysDesc" | i18n }}
</div>
</div>
<div class="box">
<div class="box-content">
<div class="box-content-row box-content-row-checkbox" appBoxRow>
<label for="addlogin-notification-bar">{{ "enableAddLoginNotification" | i18n }}</label>
<input
id="addlogin-notification-bar"
type="checkbox"
aria-describedby="addlogin-notification-barHelp"
(change)="updateAddLoginNotification()"
[(ngModel)]="enableAddLoginNotification"
/>
</div>
</div>
<div id="addlogin-notification-barHelp" class="box-footer">
{{
accountSwitcherEnabled
? ("addLoginNotificationDescAlt" | i18n)
: ("addLoginNotificationDesc" | i18n)
}}
</div>
</div>
<div class="box">
<div class="box-content">
<div class="box-content-row box-content-row-checkbox" appBoxRow>
<label for="changedpass-notification-bar">{{
"enableChangedPasswordNotification" | i18n
}}</label>
<input
id="changedpass-notification-bar"
type="checkbox"
aria-describedby="changedpass-notification-barHelp"
(change)="updateChangedPasswordNotification()"
[(ngModel)]="enableChangedPasswordNotification"
/>
</div>
</div>
<div id="changedpass-notification-barHelp" class="box-footer">
{{
accountSwitcherEnabled
? ("changedPasswordNotificationDescAlt" | i18n)
: ("changedPasswordNotificationDesc" | i18n)
}}
</div>
</div>
<div class="box list">
<div class="box-content single-line">
<button
type="button"
class="box-content-row box-content-row-flex text-default"
routerLink="/excluded-domains"
>
<div class="row-main">{{ "excludedDomains" | i18n }}</div>
<i class="bwi bwi-angle-right bwi-lg row-sub-icon" aria-hidden="true"></i>
</button>
</div>
</div>
</main>

View File

@ -0,0 +1,53 @@
import { Component, OnInit } from "@angular/core";
import { firstValueFrom } from "rxjs";
import { UserNotificationSettingsServiceAbstraction } from "@bitwarden/common/autofill/services/user-notification-settings.service";
import { VaultSettingsService } from "@bitwarden/common/vault/abstractions/vault-settings/vault-settings.service";
import { enableAccountSwitching } from "../../../platform/flags";
@Component({
selector: "autofill-notification-settings",
templateUrl: "notifications.component.html",
})
export class NotifcationsSettingsComponent implements OnInit {
enableAddLoginNotification = false;
enableChangedPasswordNotification = false;
enablePasskeys = true;
accountSwitcherEnabled = false;
constructor(
private userNotificationSettingsService: UserNotificationSettingsServiceAbstraction,
private vaultSettingsService: VaultSettingsService,
) {
this.accountSwitcherEnabled = enableAccountSwitching();
}
async ngOnInit() {
this.enableAddLoginNotification = await firstValueFrom(
this.userNotificationSettingsService.enableAddedLoginPrompt$,
);
this.enableChangedPasswordNotification = await firstValueFrom(
this.userNotificationSettingsService.enableChangedPasswordPrompt$,
);
this.enablePasskeys = await firstValueFrom(this.vaultSettingsService.enablePasskeys$);
}
async updateAddLoginNotification() {
await this.userNotificationSettingsService.setEnableAddedLoginPrompt(
this.enableAddLoginNotification,
);
}
async updateChangedPasswordNotification() {
await this.userNotificationSettingsService.setEnableChangedPasswordPrompt(
this.enableChangedPasswordNotification,
);
}
async updateEnablePasskeys() {
await this.vaultSettingsService.setEnablePasskeys(this.enablePasskeys);
}
}

View File

@ -3,7 +3,7 @@ import { mock } from "jest-mock-extended";
import {
AssertCredentialResult,
CreateCredentialResult,
} from "@bitwarden/common/vault/abstractions/fido2/fido2-client.service.abstraction";
} from "@bitwarden/common/platform/abstractions/fido2/fido2-client.service.abstraction";
export function createCredentialCreationOptionsMock(
customFields: Partial<CredentialCreationOptions> = {},

View File

@ -1,5 +1,6 @@
import { Region } from "@bitwarden/common/platform/abstractions/environment.service";
import { VaultTimeoutAction } from "@bitwarden/common/src/enums/vault-timeout-action.enum";
import { VaultTimeout } from "@bitwarden/common/types/vault-timeout.type";
import { CipherType } from "@bitwarden/common/vault/enums";
export type UserSettings = {
@ -31,7 +32,7 @@ export type UserSettings = {
utcDate: string;
version: string;
};
vaultTimeout: number;
vaultTimeout: VaultTimeout;
vaultTimeoutAction: VaultTimeoutAction;
};

View File

@ -1,9 +1,11 @@
import { firstValueFrom } from "rxjs";
import { NotificationsService } from "@bitwarden/common/abstractions/notifications.service";
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 { VaultTimeoutAction } from "@bitwarden/common/enums/vault-timeout-action.enum";
import { VaultTimeoutStringType } from "@bitwarden/common/types/vault-timeout.type";
import { BrowserStateService } from "../platform/services/abstractions/browser-state.service";
@ -19,6 +21,7 @@ export default class IdleBackground {
private stateService: BrowserStateService,
private notificationsService: NotificationsService,
private accountService: AccountService,
private vaultTimeoutSettingsService: VaultTimeoutSettingsService,
) {
this.idle = chrome.idle || (browser != null ? browser.idle : null);
}
@ -54,10 +57,14 @@ export default class IdleBackground {
const allUsers = await firstValueFrom(this.accountService.accounts$);
for (const userId in allUsers) {
// If the screen is locked or the screensaver activates
const timeout = await this.stateService.getVaultTimeout({ userId: userId });
if (timeout === -2) {
const timeout = await firstValueFrom(
this.vaultTimeoutSettingsService.getVaultTimeoutByUserId$(userId),
);
if (timeout === VaultTimeoutStringType.OnLocked) {
// On System Lock vault timeout option
const action = await this.stateService.getVaultTimeoutAction({ userId: userId });
const action = await firstValueFrom(
this.vaultTimeoutSettingsService.getVaultTimeoutActionByUserId$(userId),
);
if (action === VaultTimeoutAction.LogOut) {
await this.vaultTimeoutService.logOut(userId);
} else {

View File

@ -1,8 +1,8 @@
import { Subject, filter, firstValueFrom, map, merge, timeout } from "rxjs";
import {
PinCryptoServiceAbstraction,
PinCryptoService,
PinServiceAbstraction,
PinService,
InternalUserDecryptionOptionsServiceAbstraction,
UserDecryptionOptionsService,
AuthRequestServiceAbstraction,
@ -78,6 +78,9 @@ import { ConfigService } from "@bitwarden/common/platform/abstractions/config/co
import { CryptoFunctionService as CryptoFunctionServiceAbstraction } from "@bitwarden/common/platform/abstractions/crypto-function.service";
import { CryptoService as CryptoServiceAbstraction } from "@bitwarden/common/platform/abstractions/crypto.service";
import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service";
import { Fido2AuthenticatorService as Fido2AuthenticatorServiceAbstraction } from "@bitwarden/common/platform/abstractions/fido2/fido2-authenticator.service.abstraction";
import { Fido2ClientService as Fido2ClientServiceAbstraction } from "@bitwarden/common/platform/abstractions/fido2/fido2-client.service.abstraction";
import { Fido2UserInterfaceService as Fido2UserInterfaceServiceAbstraction } from "@bitwarden/common/platform/abstractions/fido2/fido2-user-interface.service.abstraction";
import { FileUploadService as FileUploadServiceAbstraction } from "@bitwarden/common/platform/abstractions/file-upload/file-upload.service";
import { I18nService as I18nServiceAbstraction } from "@bitwarden/common/platform/abstractions/i18n.service";
import { KeyGenerationService as KeyGenerationServiceAbstraction } from "@bitwarden/common/platform/abstractions/key-generation.service";
@ -97,6 +100,7 @@ import { Message, MessageListener, MessageSender } from "@bitwarden/common/platf
// eslint-disable-next-line no-restricted-imports -- Used for dependency creation
import { SubjectMessageSender } from "@bitwarden/common/platform/messaging/internal";
import { Lazy } from "@bitwarden/common/platform/misc/lazy";
import { clearCaches } from "@bitwarden/common/platform/misc/sequentialize";
import { GlobalState } from "@bitwarden/common/platform/models/domain/global-state";
import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key";
import { AppIdService } from "@bitwarden/common/platform/services/app-id.service";
@ -105,6 +109,8 @@ import { DefaultConfigService } from "@bitwarden/common/platform/services/config
import { ConsoleLogService } from "@bitwarden/common/platform/services/console-log.service";
import { ContainerService } from "@bitwarden/common/platform/services/container.service";
import { EncryptServiceImplementation } from "@bitwarden/common/platform/services/cryptography/encrypt.service.implementation";
import { Fido2AuthenticatorService } from "@bitwarden/common/platform/services/fido2/fido2-authenticator.service";
import { Fido2ClientService } from "@bitwarden/common/platform/services/fido2/fido2-client.service";
import { FileUploadService } from "@bitwarden/common/platform/services/file-upload/file-upload.service";
import { KeyGenerationService } from "@bitwarden/common/platform/services/key-generation.service";
import { MigrationBuilderService } from "@bitwarden/common/platform/services/migration-builder.service";
@ -125,6 +131,7 @@ import { DefaultActiveUserStateProvider } from "@bitwarden/common/platform/state
import { DefaultGlobalStateProvider } from "@bitwarden/common/platform/state/implementations/default-global-state.provider";
import { DefaultSingleUserStateProvider } from "@bitwarden/common/platform/state/implementations/default-single-user-state.provider";
import { DefaultStateProvider } from "@bitwarden/common/platform/state/implementations/default-state.provider";
import { InlineDerivedStateProvider } from "@bitwarden/common/platform/state/implementations/inline-derived-state";
import { StateEventRegistrarService } from "@bitwarden/common/platform/state/state-event-registrar.service";
/* eslint-enable import/no-restricted-paths */
import { DefaultThemeStateService } from "@bitwarden/common/platform/theming/theme-state.service";
@ -153,11 +160,9 @@ import { SendStateProvider } from "@bitwarden/common/tools/send/services/send-st
import { SendService } from "@bitwarden/common/tools/send/services/send.service";
import { InternalSendService as InternalSendServiceAbstraction } from "@bitwarden/common/tools/send/services/send.service.abstraction";
import { UserId } from "@bitwarden/common/types/guid";
import { VaultTimeoutStringType } from "@bitwarden/common/types/vault-timeout.type";
import { CipherService as CipherServiceAbstraction } from "@bitwarden/common/vault/abstractions/cipher.service";
import { CollectionService as CollectionServiceAbstraction } from "@bitwarden/common/vault/abstractions/collection.service";
import { Fido2AuthenticatorService as Fido2AuthenticatorServiceAbstraction } from "@bitwarden/common/vault/abstractions/fido2/fido2-authenticator.service.abstraction";
import { Fido2ClientService as Fido2ClientServiceAbstraction } from "@bitwarden/common/vault/abstractions/fido2/fido2-client.service.abstraction";
import { Fido2UserInterfaceService as Fido2UserInterfaceServiceAbstraction } from "@bitwarden/common/vault/abstractions/fido2/fido2-user-interface.service.abstraction";
import { CipherFileUploadService as CipherFileUploadServiceAbstraction } from "@bitwarden/common/vault/abstractions/file-upload/cipher-file-upload.service";
import { FolderApiServiceAbstraction } from "@bitwarden/common/vault/abstractions/folder/folder-api.service.abstraction";
import { InternalFolderService as InternalFolderServiceAbstraction } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction";
@ -168,8 +173,6 @@ import { VaultSettingsService as VaultSettingsServiceAbstraction } from "@bitwar
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
import { CipherService } from "@bitwarden/common/vault/services/cipher.service";
import { CollectionService } from "@bitwarden/common/vault/services/collection.service";
import { Fido2AuthenticatorService } from "@bitwarden/common/vault/services/fido2/fido2-authenticator.service";
import { Fido2ClientService } from "@bitwarden/common/vault/services/fido2/fido2-client.service";
import { CipherFileUploadService } from "@bitwarden/common/vault/services/file-upload/cipher-file-upload.service";
import { FolderApiService } from "@bitwarden/common/vault/services/folder/folder-api.service";
import { FolderService } from "@bitwarden/common/vault/services/folder/folder.service";
@ -224,10 +227,11 @@ import I18nService from "../platform/services/i18n.service";
import { LocalBackedSessionStorageService } from "../platform/services/local-backed-session-storage.service";
import { BackgroundPlatformUtilsService } from "../platform/services/platform-utils/background-platform-utils.service";
import { BrowserPlatformUtilsService } from "../platform/services/platform-utils/browser-platform-utils.service";
import { BackgroundDerivedStateProvider } from "../platform/state/background-derived-state.provider";
import { BackgroundMemoryStorageService } from "../platform/storage/background-memory-storage.service";
import { BrowserStorageServiceProvider } from "../platform/storage/browser-storage-service.provider";
import { ForegroundMemoryStorageService } from "../platform/storage/foreground-memory-storage.service";
import { ForegroundSyncService } from "../platform/sync/foreground-sync.service";
import { SyncServiceListener } from "../platform/sync/sync-service.listener";
import { fromChromeRuntimeMessaging } from "../platform/utils/from-chrome-runtime-messaging";
import VaultTimeoutService from "../services/vault-timeout/vault-timeout.service";
import FilelessImporterBackground from "../tools/background/fileless-importer.background";
@ -318,7 +322,7 @@ export default class MainBackground {
authRequestService: AuthRequestServiceAbstraction;
accountService: AccountServiceAbstraction;
globalStateProvider: GlobalStateProvider;
pinCryptoService: PinCryptoServiceAbstraction;
pinService: PinServiceAbstraction;
singleUserStateProvider: SingleUserStateProvider;
activeUserStateProvider: ActiveUserStateProvider;
derivedStateProvider: DerivedStateProvider;
@ -337,6 +341,7 @@ export default class MainBackground {
scriptInjectorService: BrowserScriptInjectorService;
kdfConfigService: kdfConfigServiceAbstraction;
offscreenDocumentService: OffscreenDocumentService;
syncServiceListener: SyncServiceListener;
onUpdatedRan: boolean;
onReplacedRan: boolean;
@ -375,7 +380,8 @@ export default class MainBackground {
const logoutCallback = async (expired: boolean, userId?: UserId) =>
await this.logout(expired, userId);
this.logService = new ConsoleLogService(false);
const isDev = process.env.ENV === "development";
this.logService = new ConsoleLogService(isDev);
this.cryptoFunctionService = new WebCryptoFunctionService(self);
this.keyGenerationService = new KeyGenerationService(this.cryptoFunctionService);
this.storageService = new BrowserLocalStorageService();
@ -394,7 +400,7 @@ export default class MainBackground {
),
);
this.offscreenDocumentService = new DefaultOffscreenDocumentService();
this.offscreenDocumentService = new DefaultOffscreenDocumentService(this.logService);
this.platformUtilsService = new BackgroundPlatformUtilsService(
this.messagingService,
@ -495,7 +501,7 @@ export default class MainBackground {
this.accountService,
this.singleUserStateProvider,
);
this.derivedStateProvider = new BackgroundDerivedStateProvider();
this.derivedStateProvider = new InlineDerivedStateProvider();
this.stateProvider = new DefaultStateProvider(
this.activeUserStateProvider,
this.singleUserStateProvider,
@ -542,13 +548,31 @@ export default class MainBackground {
const themeStateService = new DefaultThemeStateService(this.globalStateProvider);
this.masterPasswordService = new MasterPasswordService(this.stateProvider);
this.masterPasswordService = new MasterPasswordService(
this.stateProvider,
this.stateService,
this.keyGenerationService,
this.encryptService,
);
this.i18nService = new I18nService(BrowserApi.getUILanguage(), this.globalStateProvider);
this.kdfConfigService = new KdfConfigService(this.stateProvider);
this.pinService = new PinService(
this.accountService,
this.cryptoFunctionService,
this.encryptService,
this.kdfConfigService,
this.keyGenerationService,
this.logService,
this.masterPasswordService,
this.stateProvider,
this.stateService,
);
this.cryptoService = new BrowserCryptoService(
this.pinService,
this.masterPasswordService,
this.keyGenerationService,
this.cryptoFunctionService,
@ -563,12 +587,30 @@ export default class MainBackground {
);
this.appIdService = new AppIdService(this.globalStateProvider);
this.userDecryptionOptionsService = new UserDecryptionOptionsService(this.stateProvider);
this.organizationService = new OrganizationService(this.stateProvider);
this.policyService = new PolicyService(this.stateProvider, this.organizationService);
this.vaultTimeoutSettingsService = new VaultTimeoutSettingsService(
this.accountService,
this.pinService,
this.userDecryptionOptionsService,
this.cryptoService,
this.tokenService,
this.policyService,
this.biometricStateService,
this.stateProvider,
this.logService,
VaultTimeoutStringType.OnRestart, // default vault timeout
);
this.apiService = new ApiService(
this.tokenService,
this.platformUtilsService,
this.environmentService,
this.appIdService,
this.stateService,
this.vaultTimeoutSettingsService,
(expired: boolean) => this.logout(expired),
);
this.domainSettingsService = new DefaultDomainSettingsService(this.stateProvider);
@ -585,8 +627,7 @@ export default class MainBackground {
this.stateProvider,
);
this.syncNotifierService = new SyncNotifierService();
this.organizationService = new OrganizationService(this.stateProvider);
this.policyService = new PolicyService(this.stateProvider, this.organizationService);
this.autofillSettingsService = new AutofillSettingsService(
this.stateProvider,
this.policyService,
@ -692,23 +733,6 @@ export default class MainBackground {
);
this.folderApiService = new FolderApiService(this.folderService, this.apiService);
this.vaultTimeoutSettingsService = new VaultTimeoutSettingsService(
this.userDecryptionOptionsService,
this.cryptoService,
this.tokenService,
this.policyService,
this.stateService,
this.biometricStateService,
);
this.pinCryptoService = new PinCryptoService(
this.stateService,
this.cryptoService,
this.vaultTimeoutSettingsService,
this.logService,
this.kdfConfigService,
);
this.userVerificationService = new UserVerificationService(
this.stateService,
this.cryptoService,
@ -717,7 +741,7 @@ export default class MainBackground {
this.i18nService,
this.userVerificationApiService,
this.userDecryptionOptionsService,
this.pinCryptoService,
this.pinService,
this.logService,
this.vaultTimeoutSettingsService,
this.platformUtilsService,
@ -772,31 +796,52 @@ export default class MainBackground {
this.providerService = new ProviderService(this.stateProvider);
this.syncService = new SyncService(
this.masterPasswordService,
this.accountService,
this.apiService,
this.domainSettingsService,
this.folderService,
this.cipherService,
this.cryptoService,
this.collectionService,
this.messagingService,
this.policyService,
this.sendService,
this.logService,
this.keyConnectorService,
this.stateService,
this.providerService,
this.folderApiService,
this.organizationService,
this.sendApiService,
this.userDecryptionOptionsService,
this.avatarService,
logoutCallback,
this.billingAccountProfileStateService,
this.tokenService,
);
if (this.popupOnlyContext) {
this.syncService = new ForegroundSyncService(
this.stateService,
this.folderService,
this.folderApiService,
this.messagingService,
this.logService,
this.cipherService,
this.collectionService,
this.apiService,
this.accountService,
this.authService,
this.sendService,
this.sendApiService,
messageListener,
);
} else {
this.syncService = new SyncService(
this.masterPasswordService,
this.accountService,
this.apiService,
this.domainSettingsService,
this.folderService,
this.cipherService,
this.cryptoService,
this.collectionService,
this.messagingService,
this.policyService,
this.sendService,
this.logService,
this.keyConnectorService,
this.stateService,
this.providerService,
this.folderApiService,
this.organizationService,
this.sendApiService,
this.userDecryptionOptionsService,
this.avatarService,
logoutCallback,
this.billingAccountProfileStateService,
this.tokenService,
this.authService,
);
this.syncServiceListener = new SyncServiceListener(this.syncService, messageListener);
}
this.eventUploadService = new EventUploadService(
this.apiService,
this.stateProvider,
@ -839,11 +884,13 @@ export default class MainBackground {
this.i18nService,
this.collectionService,
this.cryptoService,
this.pinService,
);
this.individualVaultExportService = new IndividualVaultExportService(
this.folderService,
this.cipherService,
this.pinService,
this.cryptoService,
this.cryptoFunctionService,
this.kdfConfigService,
@ -852,6 +899,7 @@ export default class MainBackground {
this.organizationVaultExportService = new OrganizationVaultExportService(
this.cipherService,
this.apiService,
this.pinService,
this.cryptoService,
this.cryptoFunctionService,
this.collectionService,
@ -902,6 +950,7 @@ export default class MainBackground {
};
this.systemService = new SystemService(
this.pinService,
this.messagingService,
this.platformUtilsService,
systemUtilsServiceReloadCallback,
@ -1040,6 +1089,7 @@ export default class MainBackground {
this.stateService,
this.notificationsService,
this.accountService,
this.vaultTimeoutSettingsService,
);
this.usernameGenerationService = new UsernameGenerationService(
@ -1115,6 +1165,7 @@ export default class MainBackground {
this.contextMenusBackground?.init();
await this.idleBackground.init();
this.webRequestBackground?.startListening();
this.syncServiceListener?.startListening();
return new Promise<void>((resolve) => {
setTimeout(async () => {
@ -1156,6 +1207,7 @@ export default class MainBackground {
* Switch accounts to indicated userId -- null is no active user
*/
async switchAccount(userId: UserId) {
let nextAccountStatus: AuthenticationStatus;
try {
const currentlyActiveAccount = await firstValueFrom(
this.accountService.activeAccount$.pipe(map((account) => account?.id)),
@ -1163,6 +1215,8 @@ export default class MainBackground {
// can be removed once password generation history is migrated to state providers
await this.stateService.clearDecryptedData(currentlyActiveAccount);
await this.accountService.switchAccount(userId);
// Clear sequentialized caches
clearCaches();
if (userId == null) {
this.loginEmailService.setRememberEmail(false);
@ -1170,11 +1224,12 @@ export default class MainBackground {
await this.refreshBadge();
await this.refreshMenu();
await this.overlayBackground.updateOverlayCiphers();
await this.overlayBackground?.updateOverlayCiphers(); // null in popup only contexts
this.messagingService.send("goHome");
return;
}
const status = await this.authService.getAuthStatus(userId);
nextAccountStatus = await this.authService.getAuthStatus(userId);
const forcePasswordReset =
(await firstValueFrom(this.masterPasswordService.forceSetPasswordReason$(userId))) !=
ForceSetPasswordReason.None;
@ -1182,7 +1237,9 @@ export default class MainBackground {
await this.systemService.clearPendingClipboard();
await this.notificationsService.updateConnection(false);
if (status === AuthenticationStatus.Locked) {
if (nextAccountStatus === AuthenticationStatus.LoggedOut) {
this.messagingService.send("goHome");
} else if (nextAccountStatus === AuthenticationStatus.Locked) {
this.messagingService.send("locked", { userId: userId });
} else if (forcePasswordReset) {
this.messagingService.send("update-temp-password", { userId: userId });
@ -1190,11 +1247,14 @@ export default class MainBackground {
this.messagingService.send("unlocked", { userId: userId });
await this.refreshBadge();
await this.refreshMenu();
await this.overlayBackground.updateOverlayCiphers();
await this.overlayBackground?.updateOverlayCiphers(); // null in popup only contexts
await this.syncService.fullSync(false);
}
} finally {
this.messagingService.send("switchAccountFinish", { userId: userId });
this.messagingService.send("switchAccountFinish", {
userId: userId,
status: nextAccountStatus,
});
}
}
@ -1215,6 +1275,13 @@ export default class MainBackground {
await this.eventUploadService.uploadEvents(userBeingLoggedOut);
const newActiveUser =
userBeingLoggedOut === activeUserId
? await firstValueFrom(this.accountService.nextUpAccount$.pipe(map((a) => a?.id)))
: null;
await this.switchAccount(newActiveUser);
// HACK: We shouldn't wait for the authentication status to change but instead subscribe to the
// authentication status to do various actions.
const logoutPromise = firstValueFrom(
@ -1247,12 +1314,7 @@ export default class MainBackground {
]);
//Needs to be checked before state is cleaned
const needStorageReseed = await this.needsStorageReseed();
const newActiveUser =
userBeingLoggedOut === activeUserId
? await firstValueFrom(this.accountService.nextUpAccount$.pipe(map((a) => a?.id)))
: null;
const needStorageReseed = await this.needsStorageReseed(userId);
await this.stateService.clean({ userId: userBeingLoggedOut });
await this.accountService.clean(userBeingLoggedOut);
@ -1262,16 +1324,10 @@ export default class MainBackground {
// HACK: Wait for the user logging outs authentication status to transition to LoggedOut
await logoutPromise;
await this.switchAccount(newActiveUser);
if (newActiveUser != null) {
// we have a new active user, do not continue tearing down application
this.messagingService.send("switchAccountFinish");
} else {
this.messagingService.send("doneLoggingOut", {
expired: expired,
userId: userBeingLoggedOut,
});
}
this.messagingService.send("doneLoggingOut", {
expired: expired,
userId: userBeingLoggedOut,
});
if (needStorageReseed) {
await this.reseedStorage();
@ -1284,16 +1340,16 @@ export default class MainBackground {
}
await this.refreshBadge();
await this.mainContextMenuHandler?.noAccess();
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
// eslint-disable-next-line @typescript-eslint/no-floating-promises
this.notificationsService.updateConnection(false);
await this.notificationsService.updateConnection(false);
await this.systemService.clearPendingClipboard();
await this.systemService.startProcessReload(this.authService);
}
private async needsStorageReseed(): Promise<boolean> {
const currentVaultTimeout = await this.stateService.getVaultTimeout();
return currentVaultTimeout == null ? false : true;
private async needsStorageReseed(userId: UserId): Promise<boolean> {
const currentVaultTimeout = await firstValueFrom(
this.vaultTimeoutSettingsService.getVaultTimeoutByUserId$(userId),
);
return currentVaultTimeout == VaultTimeoutStringType.Never ? false : true;
}
async collectPageDetailsForContentScript(tab: any, sender: string, frameId: number = null) {

View File

@ -167,6 +167,11 @@ export class NativeMessagingBackground {
cancelButtonText: null,
type: "danger",
});
if (this.resolver) {
this.resolver(message);
}
break;
case "verifyFingerprint": {
if (this.sharedSecret == null) {
@ -356,7 +361,7 @@ export class NativeMessagingBackground {
const masterKey = new SymmetricCryptoKey(
Utils.fromB64ToArray(message.keyB64),
) as MasterKey;
const userKey = await this.cryptoService.decryptUserKeyWithMasterKey(
const userKey = await this.masterPasswordService.decryptUserKeyWithMasterKey(
masterKey,
encUserKey,
);

View File

@ -1,10 +1,19 @@
import { VaultTimeoutSettingsService as AbstractVaultTimeoutSettingsService } from "@bitwarden/common/abstractions/vault-timeout/vault-timeout-settings.service";
import { VaultTimeoutSettingsService } from "@bitwarden/common/services/vault-timeout/vault-timeout-settings.service";
import { VaultTimeoutStringType } from "@bitwarden/common/types/vault-timeout.type";
import {
policyServiceFactory,
PolicyServiceInitOptions,
} from "../../admin-console/background/service-factories/policy-service.factory";
import {
accountServiceFactory,
AccountServiceInitOptions,
} from "../../auth/background/service-factories/account-service.factory";
import {
pinServiceFactory,
PinServiceInitOptions,
} from "../../auth/background/service-factories/pin-service.factory";
import {
tokenServiceFactory,
TokenServiceInitOptions,
@ -27,19 +36,26 @@ import {
FactoryOptions,
} from "../../platform/background/service-factories/factory-options";
import {
StateServiceInitOptions,
stateServiceFactory,
} from "../../platform/background/service-factories/state-service.factory";
logServiceFactory,
LogServiceInitOptions,
} from "../../platform/background/service-factories/log-service.factory";
import {
StateProviderInitOptions,
stateProviderFactory,
} from "../../platform/background/service-factories/state-provider.factory";
type VaultTimeoutSettingsServiceFactoryOptions = FactoryOptions;
export type VaultTimeoutSettingsServiceInitOptions = VaultTimeoutSettingsServiceFactoryOptions &
AccountServiceInitOptions &
PinServiceInitOptions &
UserDecryptionOptionsServiceInitOptions &
CryptoServiceInitOptions &
TokenServiceInitOptions &
PolicyServiceInitOptions &
StateServiceInitOptions &
BiometricStateServiceInitOptions;
BiometricStateServiceInitOptions &
StateProviderInitOptions &
LogServiceInitOptions;
export function vaultTimeoutSettingsServiceFactory(
cache: { vaultTimeoutSettingsService?: AbstractVaultTimeoutSettingsService } & CachedServices,
@ -51,12 +67,16 @@ export function vaultTimeoutSettingsServiceFactory(
opts,
async () =>
new VaultTimeoutSettingsService(
await accountServiceFactory(cache, opts),
await pinServiceFactory(cache, opts),
await userDecryptionOptionsServiceFactory(cache, opts),
await cryptoServiceFactory(cache, opts),
await tokenServiceFactory(cache, opts),
await policyServiceFactory(cache, opts),
await stateServiceFactory(cache, opts),
await biometricStateServiceFactory(cache, opts),
await stateProviderFactory(cache, opts),
await logServiceFactory(cache, opts),
VaultTimeoutStringType.OnRestart, // default vault timeout
),
);
}

View File

@ -1,28 +1,12 @@
import { Jsonify } from "type-fest";
import {
Account as BaseAccount,
AccountSettings as BaseAccountSettings,
} from "@bitwarden/common/platform/models/domain/account";
import { Account as BaseAccount } from "@bitwarden/common/platform/models/domain/account";
import { BrowserComponentState } from "./browserComponentState";
import { BrowserGroupingsComponentState } from "./browserGroupingsComponentState";
import { BrowserSendComponentState } from "./browserSendComponentState";
export class AccountSettings extends BaseAccountSettings {
vaultTimeout = -1; // On Restart
static fromJSON(json: Jsonify<AccountSettings>): AccountSettings {
if (json == null) {
return null;
}
return Object.assign(new AccountSettings(), json, super.fromJSON(json));
}
}
export class Account extends BaseAccount {
settings?: AccountSettings = new AccountSettings();
groupings?: BrowserGroupingsComponentState;
send?: BrowserSendComponentState;
ciphers?: BrowserComponentState;
@ -30,10 +14,7 @@ export class Account extends BaseAccount {
constructor(init: Partial<Account>) {
super(init);
Object.assign(this.settings, {
...new AccountSettings(),
...this.settings,
});
this.groupings = init?.groupings ?? new BrowserGroupingsComponentState();
this.send = init?.send ?? new BrowserSendComponentState();
this.ciphers = init?.ciphers ?? new BrowserComponentState();
@ -46,7 +27,6 @@ export class Account extends BaseAccount {
}
return Object.assign(new Account({}), json, super.fromJSON(json), {
settings: AccountSettings.fromJSON(json.settings),
groupings: BrowserGroupingsComponentState.fromJSON(json.groupings),
send: BrowserSendComponentState.fromJSON(json.send),
ciphers: BrowserComponentState.fromJSON(json.ciphers),

View File

@ -5,6 +5,10 @@ import {
tokenServiceFactory,
TokenServiceInitOptions,
} from "../../../auth/background/service-factories/token-service.factory";
import {
vaultTimeoutSettingsServiceFactory,
VaultTimeoutSettingsServiceInitOptions,
} from "../../../background/service-factories/vault-timeout-settings-service.factory";
import {
CachedServices,
factory,
@ -20,7 +24,6 @@ import {
PlatformUtilsServiceInitOptions,
platformUtilsServiceFactory,
} from "./platform-utils-service.factory";
import { stateServiceFactory, StateServiceInitOptions } from "./state-service.factory";
type ApiServiceFactoryOptions = FactoryOptions & {
apiServiceOptions: {
@ -34,7 +37,7 @@ export type ApiServiceInitOptions = ApiServiceFactoryOptions &
PlatformUtilsServiceInitOptions &
EnvironmentServiceInitOptions &
AppIdServiceInitOptions &
StateServiceInitOptions;
VaultTimeoutSettingsServiceInitOptions;
export function apiServiceFactory(
cache: { apiService?: AbstractApiService } & CachedServices,
@ -50,7 +53,7 @@ export function apiServiceFactory(
await platformUtilsServiceFactory(cache, opts),
await environmentServiceFactory(cache, opts),
await appIdServiceFactory(cache, opts),
await stateServiceFactory(cache, opts),
await vaultTimeoutSettingsServiceFactory(cache, opts),
opts.apiServiceOptions.logoutCallback,
opts.apiServiceOptions.customUserAgent,
),

View File

@ -12,6 +12,10 @@ import {
internalMasterPasswordServiceFactory,
MasterPasswordServiceInitOptions,
} from "../../../auth/background/service-factories/master-password-service.factory";
import {
PinServiceInitOptions,
pinServiceFactory,
} from "../../../auth/background/service-factories/pin-service.factory";
import {
StateServiceInitOptions,
stateServiceFactory,
@ -45,6 +49,7 @@ import { StateProviderInitOptions, stateProviderFactory } from "./state-provider
type CryptoServiceFactoryOptions = FactoryOptions;
export type CryptoServiceInitOptions = CryptoServiceFactoryOptions &
PinServiceInitOptions &
MasterPasswordServiceInitOptions &
KeyGenerationServiceInitOptions &
CryptoFunctionServiceInitOptions &
@ -67,6 +72,7 @@ export function cryptoServiceFactory(
opts,
async () =>
new BrowserCryptoService(
await pinServiceFactory(cache, opts),
await internalMasterPasswordServiceFactory(cache, opts),
await keyGenerationServiceFactory(cache, opts),
await cryptoFunctionServiceFactory(cache, opts),

View File

@ -1,6 +1,6 @@
import { DerivedStateProvider } from "@bitwarden/common/platform/state";
import { BackgroundDerivedStateProvider } from "../../state/background-derived-state.provider";
// eslint-disable-next-line import/no-restricted-paths -- For dependency creation
import { InlineDerivedStateProvider } from "@bitwarden/common/platform/state/implementations/inline-derived-state";
import { CachedServices, FactoryOptions, factory } from "./factory-options";
@ -12,10 +12,5 @@ export async function derivedStateProviderFactory(
cache: { derivedStateProvider?: DerivedStateProvider } & CachedServices,
opts: DerivedStateProviderInitOptions,
): Promise<DerivedStateProvider> {
return factory(
cache,
"derivedStateProvider",
opts,
async () => new BackgroundDerivedStateProvider(),
);
return factory(cache, "derivedStateProvider", opts, async () => new InlineDerivedStateProvider());
}

View File

@ -1,3 +1,7 @@
import { mock } from "jest-mock-extended";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { DefaultOffscreenDocumentService } from "./offscreen-document.service";
class TestCase {
@ -21,6 +25,7 @@ describe.each([
new TestCase("synchronous callback", () => 42),
new TestCase("asynchronous callback", () => Promise.resolve(42)),
])("DefaultOffscreenDocumentService %s", (testCase) => {
const logService = mock<LogService>();
let sut: DefaultOffscreenDocumentService;
const reasons = [chrome.offscreen.Reason.TESTING];
const justification = "justification is testing";
@ -37,7 +42,7 @@ describe.each([
callback = testCase.callback;
chrome.offscreen = api;
sut = new DefaultOffscreenDocumentService();
sut = new DefaultOffscreenDocumentService(logService);
});
afterEach(() => {

View File

@ -1,7 +1,9 @@
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
export class DefaultOffscreenDocumentService implements DefaultOffscreenDocumentService {
private workerCount = 0;
constructor() {}
constructor(private logService: LogService) {}
async withDocument<T>(
reasons: chrome.offscreen.Reason[],
@ -24,11 +26,21 @@ export class DefaultOffscreenDocumentService implements DefaultOffscreenDocument
}
private async create(reasons: chrome.offscreen.Reason[], justification: string): Promise<void> {
await chrome.offscreen.createDocument({
url: "offscreen-document/index.html",
reasons,
justification,
});
try {
await chrome.offscreen.createDocument({
url: "offscreen-document/index.html",
reasons,
justification,
});
} catch (e) {
// gobble multiple offscreen document creation errors
// TODO: remove this when the offscreen document service is fixed PM-8014
if (e.message === "Only a single offscreen document may be created.") {
this.logService.info("Ignoring offscreen document creation error.");
return;
}
throw e;
}
}
private async close(): Promise<void> {

View File

@ -1,12 +1,16 @@
import { CommonModule } from "@angular/common";
import { Component, Input, OnInit } from "@angular/core";
import { JslibModule } from "@bitwarden/angular/jslib.module";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import BrowserPopupUtils from "../../platform/popup/browser-popup-utils";
import BrowserPopupUtils from "../browser-popup-utils";
@Component({
selector: "app-pop-out",
templateUrl: "pop-out.component.html",
standalone: true,
imports: [CommonModule, JslibModule],
})
export class PopOutComponent implements OnInit {
@Input() show = true;
@ -24,9 +28,7 @@ export class PopOutComponent implements OnInit {
}
}
expand() {
// 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
BrowserPopupUtils.openCurrentPagePopout(window);
async expand() {
await BrowserPopupUtils.openCurrentPagePopout(window);
}
}

View File

@ -0,0 +1,11 @@
<div class="tw-flex tw-justify-between tw-items-end tw-gap-1 tw-px-1 tw-pb-1">
<div>
<h2 bitTypography="h6" noMargin class="tw-mb-0 tw-text-headers">
{{ title }}
</h2>
<ng-content select="[slot=title-suffix]"></ng-content>
</div>
<div class="tw-text-muted has-[button]:-tw-mb-1">
<ng-content select="[slot=end]"></ng-content>
</div>
</div>

View File

@ -0,0 +1,13 @@
import { Component, Input } from "@angular/core";
import { TypographyModule } from "@bitwarden/components";
@Component({
standalone: true,
selector: "popup-section-header",
templateUrl: "./popup-section-header.component.html",
imports: [TypographyModule],
})
export class PopupSectionHeaderComponent {
@Input() title: string;
}

View File

@ -0,0 +1,90 @@
import { Meta, StoryObj, moduleMetadata } from "@storybook/angular";
import {
CardComponent,
IconButtonModule,
SectionComponent,
TypographyModule,
} from "@bitwarden/components";
import { PopupSectionHeaderComponent } from "./popup-section-header.component";
export default {
title: "Browser/Popup Section Header",
component: PopupSectionHeaderComponent,
args: {
title: "Title",
},
decorators: [
moduleMetadata({
imports: [SectionComponent, CardComponent, TypographyModule, IconButtonModule],
}),
],
} as Meta<PopupSectionHeaderComponent>;
type Story = StoryObj<PopupSectionHeaderComponent>;
export const OnlyTitle: Story = {
render: (args) => ({
props: args,
template: `
<popup-section-header [title]="title"></popup-section-header>
`,
}),
args: {
title: "Only Title",
},
};
export const TrailingText: Story = {
render: (args) => ({
props: args,
template: `
<popup-section-header [title]="title">
<span bitTypography="body2" slot="end">13</span>
</popup-section-header>
`,
}),
args: {
title: "Trailing Text",
},
};
export const TailingIcon: Story = {
render: (args) => ({
props: args,
template: `
<popup-section-header [title]="title">
<button bitIconButton="bwi-star" size="small" slot="end"></button>
</popup-section-header>
`,
}),
args: {
title: "Trailing Icon",
},
};
export const WithSections: Story = {
render: () => ({
template: `
<div class="tw-bg-background-alt tw-p-2">
<bit-section>
<popup-section-header title="Section 1">
<button bitIconButton="bwi-star" size="small" slot="end"></button>
</popup-section-header>
<bit-card>
<h3 bitTypography="h3">Card 1 Content</h3>
</bit-card>
</bit-section>
<bit-section>
<popup-section-header title="Section 2">
<button bitIconButton="bwi-star" size="small" slot="end"></button>
</popup-section-header>
<bit-card>
<h3 bitTypography="h3">Card 2 Content</h3>
</bit-card>
</bit-section>
</div>
`,
}),
};

View File

@ -1,4 +1,4 @@
import { mergeMap } from "rxjs";
import { filter, mergeMap } from "rxjs";
import {
AbstractStorageService,
@ -34,6 +34,11 @@ export default abstract class AbstractChromeStorageService
constructor(protected chromeStorageApi: chrome.storage.StorageArea) {
this.updates$ = fromChromeEvent(this.chromeStorageApi.onChanged).pipe(
filter(([changes]) => {
// Our storage services support changing only one key at a time. If more are changed, it's due to
// reseeding storage and we should ignore the changes.
return Object.keys(changes).length === 1;
}),
mergeMap(([changes]) => {
return Object.entries(changes).map(([key, change]) => {
// The `newValue` property isn't on the StorageChange object

View File

@ -1,5 +1,6 @@
import { firstValueFrom } from "rxjs";
import { PinServiceAbstraction } from "@bitwarden/auth/common";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { KdfConfigService } from "@bitwarden/common/auth/abstractions/kdf-config.service";
import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/auth/abstractions/master-password.service.abstraction";
@ -19,6 +20,7 @@ import { UserKey } from "@bitwarden/common/types/key";
export class BrowserCryptoService extends CryptoService {
constructor(
pinService: PinServiceAbstraction,
masterPasswordService: InternalMasterPasswordServiceAbstraction,
keyGenerationService: KeyGenerationService,
cryptoFunctionService: CryptoFunctionService,
@ -32,6 +34,7 @@ export class BrowserCryptoService extends CryptoService {
kdfConfigService: KdfConfigService,
) {
super(
pinService,
masterPasswordService,
keyGenerationService,
cryptoFunctionService,

View File

@ -163,6 +163,10 @@ export abstract class BrowserPlatformUtilsService implements PlatformUtilsServic
* the view is open.
*/
async isViewOpen(): Promise<boolean> {
if (this.isSafari()) {
// Query views on safari since chrome.runtime.sendMessage does not timeout and will hang.
return BrowserApi.isPopupOpen();
}
return Boolean(await BrowserApi.sendMessageWithResponse("checkVaultPopupHeartbeat"));
}

View File

@ -1,23 +0,0 @@
import { Observable } from "rxjs";
import { DeriveDefinition, DerivedState } from "@bitwarden/common/platform/state";
// eslint-disable-next-line import/no-restricted-paths -- extending this class for this client
import { DefaultDerivedStateProvider } from "@bitwarden/common/platform/state/implementations/default-derived-state.provider";
import { DerivedStateDependencies } from "@bitwarden/common/src/types/state";
import { BackgroundDerivedState } from "./background-derived-state";
export class BackgroundDerivedStateProvider extends DefaultDerivedStateProvider {
override buildDerivedState<TFrom, TTo, TDeps extends DerivedStateDependencies>(
parentState$: Observable<TFrom>,
deriveDefinition: DeriveDefinition<TFrom, TTo, TDeps>,
dependencies: TDeps,
): DerivedState<TTo> {
return new BackgroundDerivedState(
parentState$,
deriveDefinition,
deriveDefinition.buildCacheKey(),
dependencies,
);
}
}

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