diff --git a/apps/browser/src/_locales/ar/messages.json b/apps/browser/src/_locales/ar/messages.json index ff9fa87252e..83ea8f797e9 100644 --- a/apps/browser/src/_locales/ar/messages.json +++ b/apps/browser/src/_locales/ar/messages.json @@ -19,6 +19,18 @@ "createAccount": { "message": "إنشاء حساب" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" + }, "setAStrongPassword": { "message": "Set a strong password" }, @@ -107,6 +119,9 @@ "copyPassword": { "message": "نسخ كلمة المرور" }, + "copyPassphrase": { + "message": "Copy passphrase" + }, "copyNote": { "message": "نسخ الملاحظة" }, @@ -152,6 +167,10 @@ "copyNotes": { "message": "Copy notes" }, + "fill": { + "message": "Fill", + "description": "This string is used on the vault page to indicate autofilling. Horizontal space is limited in the interface here so try and keep translations as concise as possible." + }, "autoFill": { "message": "التعبئة التلقائية" }, @@ -407,6 +426,9 @@ "generatePassword": { "message": "توليد كلمة مرور" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "regeneratePassword": { "message": "إعادة توليد كلمة المرور" }, @@ -568,6 +590,15 @@ "launchWebsite": { "message": "Launch website" }, + "launchWebsiteName": { + "message": "Launch website $ITEMNAME$", + "placeholders": { + "itemname": { + "content": "$1", + "example": "Secret item" + } + } + }, "website": { "message": "الموقع الإلكتروني" }, @@ -814,6 +845,9 @@ "logIn": { "message": "Log in" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "restartRegistration": { "message": "Restart registration" }, @@ -1389,6 +1423,10 @@ "baseUrl": { "message": "رابط الخادم" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "رابط خادم API" }, @@ -2464,8 +2502,8 @@ "message": "Optionally require a password for users to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendPasswordDescV2": { - "message": "Require this password to view the Send.", + "sendPasswordDescV3": { + "message": "Add an optional password for recipients to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendNotesDesc": { @@ -2827,6 +2865,9 @@ "generateUsername": { "message": "إنشاء اسم المستخدم" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "نوع اسم المستخدم" }, @@ -2867,6 +2908,14 @@ "forwardedEmailDesc": { "message": "إنشاء بريد إلكتروني مستعار مع خدمة إعادة توجيه خارجية." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ error: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -4494,9 +4543,15 @@ "itemLocation": { "message": "Item Location" }, + "fileSend": { + "message": "File Send" + }, "fileSends": { "message": "File Sends" }, + "textSend": { + "message": "Text Send" + }, "textSends": { "message": "Text Sends" }, @@ -4559,5 +4614,158 @@ }, "authenticating": { "message": "Authenticating" + }, + "fillGeneratedPassword": { + "message": "Fill generated password", + "description": "Heading for the password generator within the inline menu" + }, + "passwordRegenerated": { + "message": "Password regenerated", + "description": "Notification message for when a password has been regenerated" + }, + "saveLoginToBitwarden": { + "message": "Save login to Bitwarden?", + "description": "Confirmation message for saving a login to Bitwarden" + }, + "spaceCharacterDescriptor": { + "message": "Space", + "description": "Represents the space key in screen reader content as a readable word" + }, + "tildeCharacterDescriptor": { + "message": "Tilde", + "description": "Represents the ~ key in screen reader content as a readable word" + }, + "backtickCharacterDescriptor": { + "message": "Backtick", + "description": "Represents the ` key in screen reader content as a readable word" + }, + "exclamationCharacterDescriptor": { + "message": "Exclamation mark", + "description": "Represents the ! key in screen reader content as a readable word" + }, + "atSignCharacterDescriptor": { + "message": "At sign", + "description": "Represents the @ key in screen reader content as a readable word" + }, + "hashSignCharacterDescriptor": { + "message": "Hash sign", + "description": "Represents the # key in screen reader content as a readable word" + }, + "dollarSignCharacterDescriptor": { + "message": "Dollar sign", + "description": "Represents the $ key in screen reader content as a readable word" + }, + "percentSignCharacterDescriptor": { + "message": "Percent sign", + "description": "Represents the % key in screen reader content as a readable word" + }, + "caretCharacterDescriptor": { + "message": "Caret", + "description": "Represents the ^ key in screen reader content as a readable word" + }, + "ampersandCharacterDescriptor": { + "message": "Ampersand", + "description": "Represents the & key in screen reader content as a readable word" + }, + "asteriskCharacterDescriptor": { + "message": "Asterisk", + "description": "Represents the * key in screen reader content as a readable word" + }, + "parenLeftCharacterDescriptor": { + "message": "Left parenthesis", + "description": "Represents the ( key in screen reader content as a readable word" + }, + "parenRightCharacterDescriptor": { + "message": "Right parenthesis", + "description": "Represents the ) key in screen reader content as a readable word" + }, + "hyphenCharacterDescriptor": { + "message": "Underscore", + "description": "Represents the _ key in screen reader content as a readable word" + }, + "underscoreCharacterDescriptor": { + "message": "Hyphen", + "description": "Represents the - key in screen reader content as a readable word" + }, + "plusCharacterDescriptor": { + "message": "Plus", + "description": "Represents the + key in screen reader content as a readable word" + }, + "equalsCharacterDescriptor": { + "message": "Equals", + "description": "Represents the = key in screen reader content as a readable word" + }, + "braceLeftCharacterDescriptor": { + "message": "Left brace", + "description": "Represents the { key in screen reader content as a readable word" + }, + "braceRightCharacterDescriptor": { + "message": "Right brace", + "description": "Represents the } key in screen reader content as a readable word" + }, + "bracketLeftCharacterDescriptor": { + "message": "Left bracket", + "description": "Represents the [ key in screen reader content as a readable word" + }, + "bracketRightCharacterDescriptor": { + "message": "Right bracket", + "description": "Represents the ] key in screen reader content as a readable word" + }, + "pipeCharacterDescriptor": { + "message": "Pipe", + "description": "Represents the | key in screen reader content as a readable word" + }, + "backSlashCharacterDescriptor": { + "message": "Back slash", + "description": "Represents the back slash key in screen reader content as a readable word" + }, + "colonCharacterDescriptor": { + "message": "Colon", + "description": "Represents the : key in screen reader content as a readable word" + }, + "semicolonCharacterDescriptor": { + "message": "Semicolon", + "description": "Represents the ; key in screen reader content as a readable word" + }, + "doubleQuoteCharacterDescriptor": { + "message": "Double quote", + "description": "Represents the double quote key in screen reader content as a readable word" + }, + "singleQuoteCharacterDescriptor": { + "message": "Single quote", + "description": "Represents the ' key in screen reader content as a readable word" + }, + "lessThanCharacterDescriptor": { + "message": "Less than", + "description": "Represents the < key in screen reader content as a readable word" + }, + "greaterThanCharacterDescriptor": { + "message": "Greater than", + "description": "Represents the > key in screen reader content as a readable word" + }, + "commaCharacterDescriptor": { + "message": "Comma", + "description": "Represents the , key in screen reader content as a readable word" + }, + "periodCharacterDescriptor": { + "message": "Period", + "description": "Represents the . key in screen reader content as a readable word" + }, + "questionCharacterDescriptor": { + "message": "Question mark", + "description": "Represents the ? key in screen reader content as a readable word" + }, + "forwardSlashCharacterDescriptor": { + "message": "Forward slash", + "description": "Represents the / key in screen reader content as a readable word" + }, + "lowercaseAriaLabel": { + "message": "Lowercase" + }, + "uppercaseAriaLabel": { + "message": "Uppercase" + }, + "generatedPassword": { + "message": "Generated password" } } diff --git a/apps/browser/src/_locales/az/messages.json b/apps/browser/src/_locales/az/messages.json index ca4f3e5a0ef..930624e723d 100644 --- a/apps/browser/src/_locales/az/messages.json +++ b/apps/browser/src/_locales/az/messages.json @@ -19,6 +19,18 @@ "createAccount": { "message": "Hesab yarat" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" + }, "setAStrongPassword": { "message": "Güclü bir parol təyin et" }, @@ -107,6 +119,9 @@ "copyPassword": { "message": "Parolu kopyala" }, + "copyPassphrase": { + "message": "Copy passphrase" + }, "copyNote": { "message": "Notu kopyala" }, @@ -152,6 +167,10 @@ "copyNotes": { "message": "Notları kopyala" }, + "fill": { + "message": "Fill", + "description": "This string is used on the vault page to indicate autofilling. Horizontal space is limited in the interface here so try and keep translations as concise as possible." + }, "autoFill": { "message": "Avto-doldurma" }, @@ -407,6 +426,9 @@ "generatePassword": { "message": "Parol yarat" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "regeneratePassword": { "message": "Parolu yenidən yarat" }, @@ -568,6 +590,15 @@ "launchWebsite": { "message": "Veb saytı başlat" }, + "launchWebsiteName": { + "message": "Launch website $ITEMNAME$", + "placeholders": { + "itemname": { + "content": "$1", + "example": "Secret item" + } + } + }, "website": { "message": "Veb sayt" }, @@ -814,6 +845,9 @@ "logIn": { "message": "Giriş et" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "restartRegistration": { "message": "Qeydiyyatı yenidən başlat" }, @@ -1389,6 +1423,10 @@ "baseUrl": { "message": "Server URL-si" }, + "selfHostBaseUrl": { + "message": "Self-host server URL-si", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "API server URL-si" }, @@ -2464,8 +2502,8 @@ "message": "İstəyinizə görə istifadəçilərdən bu \"Send\"ə müraciət edərkən parol tələb edə bilərsiniz.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendPasswordDescV2": { - "message": "Send-ə baxmaq üçün bu parol tələb edilsin.", + "sendPasswordDescV3": { + "message": "Alıcıların bu \"Send\"ə müraciət etməsi üçün ixtiyari bir parol əlavə edin.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendNotesDesc": { @@ -2827,6 +2865,9 @@ "generateUsername": { "message": "İstifadəçi adı yarat" }, + "generateEmail": { + "message": "E-poçt yarat" + }, "usernameType": { "message": "İstifadəçi adı növü" }, @@ -2867,6 +2908,14 @@ "forwardedEmailDesc": { "message": "Xarici yönləndirmə xidməti ilə e-poçt ləqəbi yaradın." }, + "forwarderDomainName": { + "message": "E-poçt domeni", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Seçilmiş xidmət tərəfindən dəstəklənən bir domen seçin", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ xətası: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -4494,9 +4543,15 @@ "itemLocation": { "message": "Element yeri" }, + "fileSend": { + "message": "Fayl \"Send\"i" + }, "fileSends": { "message": "Fayl \"Send\"ləri" }, + "textSend": { + "message": "Mətn \"Send\"i" + }, "textSends": { "message": "Mətn \"Send\"ləri" }, @@ -4559,5 +4614,158 @@ }, "authenticating": { "message": "Kimlik doğrulama" + }, + "fillGeneratedPassword": { + "message": "Fill generated password", + "description": "Heading for the password generator within the inline menu" + }, + "passwordRegenerated": { + "message": "Password regenerated", + "description": "Notification message for when a password has been regenerated" + }, + "saveLoginToBitwarden": { + "message": "Save login to Bitwarden?", + "description": "Confirmation message for saving a login to Bitwarden" + }, + "spaceCharacterDescriptor": { + "message": "Space", + "description": "Represents the space key in screen reader content as a readable word" + }, + "tildeCharacterDescriptor": { + "message": "Tilde", + "description": "Represents the ~ key in screen reader content as a readable word" + }, + "backtickCharacterDescriptor": { + "message": "Backtick", + "description": "Represents the ` key in screen reader content as a readable word" + }, + "exclamationCharacterDescriptor": { + "message": "Exclamation mark", + "description": "Represents the ! key in screen reader content as a readable word" + }, + "atSignCharacterDescriptor": { + "message": "At sign", + "description": "Represents the @ key in screen reader content as a readable word" + }, + "hashSignCharacterDescriptor": { + "message": "Hash sign", + "description": "Represents the # key in screen reader content as a readable word" + }, + "dollarSignCharacterDescriptor": { + "message": "Dollar sign", + "description": "Represents the $ key in screen reader content as a readable word" + }, + "percentSignCharacterDescriptor": { + "message": "Percent sign", + "description": "Represents the % key in screen reader content as a readable word" + }, + "caretCharacterDescriptor": { + "message": "Caret", + "description": "Represents the ^ key in screen reader content as a readable word" + }, + "ampersandCharacterDescriptor": { + "message": "Ampersand", + "description": "Represents the & key in screen reader content as a readable word" + }, + "asteriskCharacterDescriptor": { + "message": "Asterisk", + "description": "Represents the * key in screen reader content as a readable word" + }, + "parenLeftCharacterDescriptor": { + "message": "Left parenthesis", + "description": "Represents the ( key in screen reader content as a readable word" + }, + "parenRightCharacterDescriptor": { + "message": "Right parenthesis", + "description": "Represents the ) key in screen reader content as a readable word" + }, + "hyphenCharacterDescriptor": { + "message": "Underscore", + "description": "Represents the _ key in screen reader content as a readable word" + }, + "underscoreCharacterDescriptor": { + "message": "Hyphen", + "description": "Represents the - key in screen reader content as a readable word" + }, + "plusCharacterDescriptor": { + "message": "Plus", + "description": "Represents the + key in screen reader content as a readable word" + }, + "equalsCharacterDescriptor": { + "message": "Equals", + "description": "Represents the = key in screen reader content as a readable word" + }, + "braceLeftCharacterDescriptor": { + "message": "Left brace", + "description": "Represents the { key in screen reader content as a readable word" + }, + "braceRightCharacterDescriptor": { + "message": "Right brace", + "description": "Represents the } key in screen reader content as a readable word" + }, + "bracketLeftCharacterDescriptor": { + "message": "Left bracket", + "description": "Represents the [ key in screen reader content as a readable word" + }, + "bracketRightCharacterDescriptor": { + "message": "Right bracket", + "description": "Represents the ] key in screen reader content as a readable word" + }, + "pipeCharacterDescriptor": { + "message": "Pipe", + "description": "Represents the | key in screen reader content as a readable word" + }, + "backSlashCharacterDescriptor": { + "message": "Back slash", + "description": "Represents the back slash key in screen reader content as a readable word" + }, + "colonCharacterDescriptor": { + "message": "Colon", + "description": "Represents the : key in screen reader content as a readable word" + }, + "semicolonCharacterDescriptor": { + "message": "Semicolon", + "description": "Represents the ; key in screen reader content as a readable word" + }, + "doubleQuoteCharacterDescriptor": { + "message": "Double quote", + "description": "Represents the double quote key in screen reader content as a readable word" + }, + "singleQuoteCharacterDescriptor": { + "message": "Single quote", + "description": "Represents the ' key in screen reader content as a readable word" + }, + "lessThanCharacterDescriptor": { + "message": "Less than", + "description": "Represents the < key in screen reader content as a readable word" + }, + "greaterThanCharacterDescriptor": { + "message": "Greater than", + "description": "Represents the > key in screen reader content as a readable word" + }, + "commaCharacterDescriptor": { + "message": "Comma", + "description": "Represents the , key in screen reader content as a readable word" + }, + "periodCharacterDescriptor": { + "message": "Period", + "description": "Represents the . key in screen reader content as a readable word" + }, + "questionCharacterDescriptor": { + "message": "Question mark", + "description": "Represents the ? key in screen reader content as a readable word" + }, + "forwardSlashCharacterDescriptor": { + "message": "Forward slash", + "description": "Represents the / key in screen reader content as a readable word" + }, + "lowercaseAriaLabel": { + "message": "Lowercase" + }, + "uppercaseAriaLabel": { + "message": "Uppercase" + }, + "generatedPassword": { + "message": "Generated password" } } diff --git a/apps/browser/src/_locales/be/messages.json b/apps/browser/src/_locales/be/messages.json index 4ce7691c943..690278de85c 100644 --- a/apps/browser/src/_locales/be/messages.json +++ b/apps/browser/src/_locales/be/messages.json @@ -3,7 +3,7 @@ "message": "Bitwarden" }, "extName": { - "message": "Bitwarden Password Manager", + "message": "Менеджар пароляў Bitwarden", "description": "Extension name, MUST be less than 40 characters (Safari restriction)" }, "extDesc": { @@ -14,11 +14,23 @@ "message": "Увайдзіце або стварыце новы ўліковы запіс для доступу да бяспечнага сховішча." }, "inviteAccepted": { - "message": "Invitation accepted" + "message": "Запрашэнне прынята" }, "createAccount": { "message": "Стварыць уліковы запіс" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" + }, "setAStrongPassword": { "message": "Set a strong password" }, @@ -107,6 +119,9 @@ "copyPassword": { "message": "Скапіяваць пароль" }, + "copyPassphrase": { + "message": "Copy passphrase" + }, "copyNote": { "message": "Скапіяваць нататку" }, @@ -123,7 +138,7 @@ "message": "Скапіяваць код бяспекі" }, "copyName": { - "message": "Copy name" + "message": "Скапіяваць імя" }, "copyCompany": { "message": "Copy company" @@ -150,7 +165,11 @@ "message": "Copy website" }, "copyNotes": { - "message": "Copy notes" + "message": "Скапіяваць нататкі" + }, + "fill": { + "message": "Fill", + "description": "This string is used on the vault page to indicate autofilling. Horizontal space is limited in the interface here so try and keep translations as concise as possible." }, "autoFill": { "message": "Аўтазапаўненне" @@ -281,13 +300,13 @@ "message": "Выйсці" }, "aboutBitwarden": { - "message": "About Bitwarden" + "message": "Пра Bitwarden" }, "about": { "message": "Пра Bitwarden" }, "moreFromBitwarden": { - "message": "More from Bitwarden" + "message": "Больш ад Bitwarden" }, "continueToBitwardenDotCom": { "message": "Continue to bitwarden.com?" @@ -338,10 +357,10 @@ "message": "Рэдагаваць папку" }, "newFolder": { - "message": "New folder" + "message": "Новая папка" }, "folderName": { - "message": "Folder name" + "message": "Назва папкі" }, "folderHintText": { "message": "Nest a folder by adding the parent folder's name followed by a “/”. Example: Social/Forums" @@ -396,7 +415,7 @@ "message": "Аўтаматычна генерыруйце надзейныя і ўнікальныя паролі для вашых лагінаў." }, "bitWebVaultApp": { - "message": "Bitwarden web app" + "message": "Вэб-праграма Bitwarden" }, "importItems": { "message": "Імпартаванне элементаў" @@ -407,6 +426,9 @@ "generatePassword": { "message": "Генерыраваць пароль" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "regeneratePassword": { "message": "Паўторна генерыраваць пароль" }, @@ -568,6 +590,15 @@ "launchWebsite": { "message": "Launch website" }, + "launchWebsiteName": { + "message": "Launch website $ITEMNAME$", + "placeholders": { + "itemname": { + "content": "$1", + "example": "Secret item" + } + } + }, "website": { "message": "Вэб-сайт" }, @@ -581,7 +612,7 @@ "message": "Iншае" }, "unlockMethods": { - "message": "Unlock options" + "message": "Параметры блакіроўкі" }, "unlockMethodNeededToChangeTimeoutActionDesc": { "message": "Наладзіць метад разблакіроўкі для змянення дзеяння часу чакання вашага сховішча." @@ -620,7 +651,7 @@ "message": "Your account is locked" }, "or": { - "message": "or" + "message": "або" }, "unlock": { "message": "Разблакіраваць" @@ -814,6 +845,9 @@ "logIn": { "message": "Log in" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "restartRegistration": { "message": "Restart registration" }, @@ -894,7 +928,7 @@ "message": "Новы URI" }, "addDomain": { - "message": "Add domain", + "message": "Дадаць дамен", "description": "'Domain' here refers to an internet domain name (e.g. 'bitwarden.com') and the message in whole described the act of putting a domain value into the context." }, "addedItem": { @@ -1389,6 +1423,10 @@ "baseUrl": { "message": "URL-адрас сервера" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "Сервер URL-адраса API" }, @@ -2464,8 +2502,8 @@ "message": "Па магчымасці запытваць у карыстальнікаў пароль для доступу да гэтага Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendPasswordDescV2": { - "message": "Require this password to view the Send.", + "sendPasswordDescV3": { + "message": "Add an optional password for recipients to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendNotesDesc": { @@ -2827,6 +2865,9 @@ "generateUsername": { "message": "Генерыраваць імя карыстальніка" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Тып імя карыстальніка" }, @@ -2867,6 +2908,14 @@ "forwardedEmailDesc": { "message": "Генерыраваць псеўданім электроннай пошты са знешнім сэрвісам перасылкі." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ error: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -4036,13 +4085,13 @@ "message": "Admin Console" }, "accountSecurity": { - "message": "Account security" + "message": "Бяспеке акаўнта" }, "notifications": { - "message": "Notifications" + "message": "Апавяшчэнні" }, "appearance": { - "message": "Appearance" + "message": "Знешні выгляд" }, "errorAssigningTargetCollection": { "message": "Error assigning target collection." @@ -4274,7 +4323,7 @@ } }, "enableAnimations": { - "message": "Enable animations" + "message": "Уключыць анімацыі" }, "showAnimations": { "message": "Show animations" @@ -4494,9 +4543,15 @@ "itemLocation": { "message": "Item Location" }, + "fileSend": { + "message": "File Send" + }, "fileSends": { "message": "File Sends" }, + "textSend": { + "message": "Text Send" + }, "textSends": { "message": "Text Sends" }, @@ -4559,5 +4614,158 @@ }, "authenticating": { "message": "Authenticating" + }, + "fillGeneratedPassword": { + "message": "Fill generated password", + "description": "Heading for the password generator within the inline menu" + }, + "passwordRegenerated": { + "message": "Password regenerated", + "description": "Notification message for when a password has been regenerated" + }, + "saveLoginToBitwarden": { + "message": "Save login to Bitwarden?", + "description": "Confirmation message for saving a login to Bitwarden" + }, + "spaceCharacterDescriptor": { + "message": "Space", + "description": "Represents the space key in screen reader content as a readable word" + }, + "tildeCharacterDescriptor": { + "message": "Tilde", + "description": "Represents the ~ key in screen reader content as a readable word" + }, + "backtickCharacterDescriptor": { + "message": "Backtick", + "description": "Represents the ` key in screen reader content as a readable word" + }, + "exclamationCharacterDescriptor": { + "message": "Exclamation mark", + "description": "Represents the ! key in screen reader content as a readable word" + }, + "atSignCharacterDescriptor": { + "message": "At sign", + "description": "Represents the @ key in screen reader content as a readable word" + }, + "hashSignCharacterDescriptor": { + "message": "Hash sign", + "description": "Represents the # key in screen reader content as a readable word" + }, + "dollarSignCharacterDescriptor": { + "message": "Dollar sign", + "description": "Represents the $ key in screen reader content as a readable word" + }, + "percentSignCharacterDescriptor": { + "message": "Percent sign", + "description": "Represents the % key in screen reader content as a readable word" + }, + "caretCharacterDescriptor": { + "message": "Caret", + "description": "Represents the ^ key in screen reader content as a readable word" + }, + "ampersandCharacterDescriptor": { + "message": "Ampersand", + "description": "Represents the & key in screen reader content as a readable word" + }, + "asteriskCharacterDescriptor": { + "message": "Asterisk", + "description": "Represents the * key in screen reader content as a readable word" + }, + "parenLeftCharacterDescriptor": { + "message": "Left parenthesis", + "description": "Represents the ( key in screen reader content as a readable word" + }, + "parenRightCharacterDescriptor": { + "message": "Right parenthesis", + "description": "Represents the ) key in screen reader content as a readable word" + }, + "hyphenCharacterDescriptor": { + "message": "Underscore", + "description": "Represents the _ key in screen reader content as a readable word" + }, + "underscoreCharacterDescriptor": { + "message": "Hyphen", + "description": "Represents the - key in screen reader content as a readable word" + }, + "plusCharacterDescriptor": { + "message": "Plus", + "description": "Represents the + key in screen reader content as a readable word" + }, + "equalsCharacterDescriptor": { + "message": "Equals", + "description": "Represents the = key in screen reader content as a readable word" + }, + "braceLeftCharacterDescriptor": { + "message": "Left brace", + "description": "Represents the { key in screen reader content as a readable word" + }, + "braceRightCharacterDescriptor": { + "message": "Right brace", + "description": "Represents the } key in screen reader content as a readable word" + }, + "bracketLeftCharacterDescriptor": { + "message": "Left bracket", + "description": "Represents the [ key in screen reader content as a readable word" + }, + "bracketRightCharacterDescriptor": { + "message": "Right bracket", + "description": "Represents the ] key in screen reader content as a readable word" + }, + "pipeCharacterDescriptor": { + "message": "Pipe", + "description": "Represents the | key in screen reader content as a readable word" + }, + "backSlashCharacterDescriptor": { + "message": "Back slash", + "description": "Represents the back slash key in screen reader content as a readable word" + }, + "colonCharacterDescriptor": { + "message": "Colon", + "description": "Represents the : key in screen reader content as a readable word" + }, + "semicolonCharacterDescriptor": { + "message": "Semicolon", + "description": "Represents the ; key in screen reader content as a readable word" + }, + "doubleQuoteCharacterDescriptor": { + "message": "Double quote", + "description": "Represents the double quote key in screen reader content as a readable word" + }, + "singleQuoteCharacterDescriptor": { + "message": "Single quote", + "description": "Represents the ' key in screen reader content as a readable word" + }, + "lessThanCharacterDescriptor": { + "message": "Less than", + "description": "Represents the < key in screen reader content as a readable word" + }, + "greaterThanCharacterDescriptor": { + "message": "Greater than", + "description": "Represents the > key in screen reader content as a readable word" + }, + "commaCharacterDescriptor": { + "message": "Comma", + "description": "Represents the , key in screen reader content as a readable word" + }, + "periodCharacterDescriptor": { + "message": "Period", + "description": "Represents the . key in screen reader content as a readable word" + }, + "questionCharacterDescriptor": { + "message": "Question mark", + "description": "Represents the ? key in screen reader content as a readable word" + }, + "forwardSlashCharacterDescriptor": { + "message": "Forward slash", + "description": "Represents the / key in screen reader content as a readable word" + }, + "lowercaseAriaLabel": { + "message": "Lowercase" + }, + "uppercaseAriaLabel": { + "message": "Uppercase" + }, + "generatedPassword": { + "message": "Generated password" } } diff --git a/apps/browser/src/_locales/bg/messages.json b/apps/browser/src/_locales/bg/messages.json index 6165f4832ed..d0646bbf513 100644 --- a/apps/browser/src/_locales/bg/messages.json +++ b/apps/browser/src/_locales/bg/messages.json @@ -19,6 +19,18 @@ "createAccount": { "message": "Създаване на акаунт" }, + "newToBitwarden": { + "message": "За пръв път ли ползвате Битуорден?" + }, + "logInWithPasskey": { + "message": "Вписване със секретен ключ" + }, + "useSingleSignOn": { + "message": "Използване на еднократна идентификация" + }, + "welcomeBack": { + "message": "Добре дошли отново" + }, "setAStrongPassword": { "message": "Използвайте сложна парола" }, @@ -107,6 +119,9 @@ "copyPassword": { "message": "Копиране на паролата" }, + "copyPassphrase": { + "message": "Копиране на паролата-фраза" + }, "copyNote": { "message": "Копиране на бележката" }, @@ -152,6 +167,10 @@ "copyNotes": { "message": "Копиране на бележките" }, + "fill": { + "message": "Попълване", + "description": "This string is used on the vault page to indicate autofilling. Horizontal space is limited in the interface here so try and keep translations as concise as possible." + }, "autoFill": { "message": "Автоматично дописване" }, @@ -407,6 +426,9 @@ "generatePassword": { "message": "Нова парола" }, + "generatePassphrase": { + "message": "Генериране на парола-фраза" + }, "regeneratePassword": { "message": "Регенериране на паролата" }, @@ -568,6 +590,15 @@ "launchWebsite": { "message": "Посещаване на уеб сайта" }, + "launchWebsiteName": { + "message": "Отваряне на уеб сайта $ITEMNAME$", + "placeholders": { + "itemname": { + "content": "$1", + "example": "Secret item" + } + } + }, "website": { "message": "Сайт" }, @@ -814,6 +845,9 @@ "logIn": { "message": "Вписване" }, + "logInToBitwarden": { + "message": "Впишете се в Битуорден" + }, "restartRegistration": { "message": "Рестартиране на регистрацията" }, @@ -1389,6 +1423,10 @@ "baseUrl": { "message": "Адрес на сървъра" }, + "selfHostBaseUrl": { + "message": "Адрес на собствения сървър", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "Адрес на ППИ-сървъра" }, @@ -2464,8 +2502,8 @@ "message": "Изискване на парола за достъп до това изпращане.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendPasswordDescV2": { - "message": "Изискване на тази парола за преглеждане на Изпращането.", + "sendPasswordDescV3": { + "message": "Добавете незадължителна парола, с която получателите да имат достъп до това Изпращане.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendNotesDesc": { @@ -2827,6 +2865,9 @@ "generateUsername": { "message": "Генериране на потр. име" }, + "generateEmail": { + "message": "Генериране на електронна поща" + }, "usernameType": { "message": "Тип потребителско име" }, @@ -2867,6 +2908,14 @@ "forwardedEmailDesc": { "message": "Създайте псевдоним на е-поща с външна услуга за препращане." }, + "forwarderDomainName": { + "message": "Домейн на електронната поща", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Изберете домейн, който се поддържа от избраната услуга", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "Грешка от $SERVICENAME$: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -4494,9 +4543,15 @@ "itemLocation": { "message": "Местоположение на елемента" }, + "fileSend": { + "message": "Файлово изпращане" + }, "fileSends": { "message": "Файлови изпращания" }, + "textSend": { + "message": "Текстово изпращане" + }, "textSends": { "message": "Текстови изпращания" }, @@ -4559,5 +4614,158 @@ }, "authenticating": { "message": "Удостоверяване" + }, + "fillGeneratedPassword": { + "message": "Попълване на генерираната парола", + "description": "Heading for the password generator within the inline menu" + }, + "passwordRegenerated": { + "message": "Паролата е прегенерирана", + "description": "Notification message for when a password has been regenerated" + }, + "saveLoginToBitwarden": { + "message": "Запазване на данните за вписване в Битуорден?", + "description": "Confirmation message for saving a login to Bitwarden" + }, + "spaceCharacterDescriptor": { + "message": "Интервал", + "description": "Represents the space key in screen reader content as a readable word" + }, + "tildeCharacterDescriptor": { + "message": "Вълничка", + "description": "Represents the ~ key in screen reader content as a readable word" + }, + "backtickCharacterDescriptor": { + "message": "Backtick", + "description": "Represents the ` key in screen reader content as a readable word" + }, + "exclamationCharacterDescriptor": { + "message": "Удивителен знак", + "description": "Represents the ! key in screen reader content as a readable word" + }, + "atSignCharacterDescriptor": { + "message": "Кльомба", + "description": "Represents the @ key in screen reader content as a readable word" + }, + "hashSignCharacterDescriptor": { + "message": "Диез", + "description": "Represents the # key in screen reader content as a readable word" + }, + "dollarSignCharacterDescriptor": { + "message": "Знак за долар", + "description": "Represents the $ key in screen reader content as a readable word" + }, + "percentSignCharacterDescriptor": { + "message": "Процент", + "description": "Represents the % key in screen reader content as a readable word" + }, + "caretCharacterDescriptor": { + "message": "Колибка", + "description": "Represents the ^ key in screen reader content as a readable word" + }, + "ampersandCharacterDescriptor": { + "message": "Амперсанд", + "description": "Represents the & key in screen reader content as a readable word" + }, + "asteriskCharacterDescriptor": { + "message": "Звездичка", + "description": "Represents the * key in screen reader content as a readable word" + }, + "parenLeftCharacterDescriptor": { + "message": "Лява скоба", + "description": "Represents the ( key in screen reader content as a readable word" + }, + "parenRightCharacterDescriptor": { + "message": "Дясна скоба", + "description": "Represents the ) key in screen reader content as a readable word" + }, + "hyphenCharacterDescriptor": { + "message": "Долна черта", + "description": "Represents the _ key in screen reader content as a readable word" + }, + "underscoreCharacterDescriptor": { + "message": "Тире", + "description": "Represents the - key in screen reader content as a readable word" + }, + "plusCharacterDescriptor": { + "message": "Плюс", + "description": "Represents the + key in screen reader content as a readable word" + }, + "equalsCharacterDescriptor": { + "message": "Равно", + "description": "Represents the = key in screen reader content as a readable word" + }, + "braceLeftCharacterDescriptor": { + "message": "Лява фигурна скоба", + "description": "Represents the { key in screen reader content as a readable word" + }, + "braceRightCharacterDescriptor": { + "message": "Дясна фигурна скоба", + "description": "Represents the } key in screen reader content as a readable word" + }, + "bracketLeftCharacterDescriptor": { + "message": "Лява квадратна скоба", + "description": "Represents the [ key in screen reader content as a readable word" + }, + "bracketRightCharacterDescriptor": { + "message": "Дясна квадратна скоба", + "description": "Represents the ] key in screen reader content as a readable word" + }, + "pipeCharacterDescriptor": { + "message": "Вертикална черта", + "description": "Represents the | key in screen reader content as a readable word" + }, + "backSlashCharacterDescriptor": { + "message": "Обратна наклонена черта", + "description": "Represents the back slash key in screen reader content as a readable word" + }, + "colonCharacterDescriptor": { + "message": "Двоеточие", + "description": "Represents the : key in screen reader content as a readable word" + }, + "semicolonCharacterDescriptor": { + "message": "Точка и запетая", + "description": "Represents the ; key in screen reader content as a readable word" + }, + "doubleQuoteCharacterDescriptor": { + "message": "Двойна кавичка", + "description": "Represents the double quote key in screen reader content as a readable word" + }, + "singleQuoteCharacterDescriptor": { + "message": "Единична кавичка", + "description": "Represents the ' key in screen reader content as a readable word" + }, + "lessThanCharacterDescriptor": { + "message": "По-малко", + "description": "Represents the < key in screen reader content as a readable word" + }, + "greaterThanCharacterDescriptor": { + "message": "По-голямо", + "description": "Represents the > key in screen reader content as a readable word" + }, + "commaCharacterDescriptor": { + "message": "Запетая", + "description": "Represents the , key in screen reader content as a readable word" + }, + "periodCharacterDescriptor": { + "message": "Точка", + "description": "Represents the . key in screen reader content as a readable word" + }, + "questionCharacterDescriptor": { + "message": "Въпросителен знак", + "description": "Represents the ? key in screen reader content as a readable word" + }, + "forwardSlashCharacterDescriptor": { + "message": "Наклонена черта", + "description": "Represents the / key in screen reader content as a readable word" + }, + "lowercaseAriaLabel": { + "message": "Малки букви" + }, + "uppercaseAriaLabel": { + "message": "Главни букви" + }, + "generatedPassword": { + "message": "Генерирана парола" } } diff --git a/apps/browser/src/_locales/bn/messages.json b/apps/browser/src/_locales/bn/messages.json index 9c6f2a5a99e..c694b3b5724 100644 --- a/apps/browser/src/_locales/bn/messages.json +++ b/apps/browser/src/_locales/bn/messages.json @@ -19,6 +19,18 @@ "createAccount": { "message": "অ্যাকাউন্ট তৈরি করুন" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" + }, "setAStrongPassword": { "message": "Set a strong password" }, @@ -107,6 +119,9 @@ "copyPassword": { "message": "পাসওয়ার্ড অনুলিপিত করুন" }, + "copyPassphrase": { + "message": "Copy passphrase" + }, "copyNote": { "message": "নোট অনুলিপিত করুন" }, @@ -152,6 +167,10 @@ "copyNotes": { "message": "Copy notes" }, + "fill": { + "message": "Fill", + "description": "This string is used on the vault page to indicate autofilling. Horizontal space is limited in the interface here so try and keep translations as concise as possible." + }, "autoFill": { "message": "স্বতঃপূরণ" }, @@ -407,6 +426,9 @@ "generatePassword": { "message": "পাসওয়ার্ড তৈরি করুন" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "regeneratePassword": { "message": "পাসওয়ার্ড পুনঃতৈরি করুন" }, @@ -568,6 +590,15 @@ "launchWebsite": { "message": "Launch website" }, + "launchWebsiteName": { + "message": "Launch website $ITEMNAME$", + "placeholders": { + "itemname": { + "content": "$1", + "example": "Secret item" + } + } + }, "website": { "message": "ওয়েবসাইট" }, @@ -814,6 +845,9 @@ "logIn": { "message": "Log in" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "restartRegistration": { "message": "Restart registration" }, @@ -1389,6 +1423,10 @@ "baseUrl": { "message": "সার্ভার URL" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "এপিআই সার্ভার URL" }, @@ -2464,8 +2502,8 @@ "message": "Optionally require a password for users to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendPasswordDescV2": { - "message": "Require this password to view the Send.", + "sendPasswordDescV3": { + "message": "Add an optional password for recipients to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendNotesDesc": { @@ -2827,6 +2865,9 @@ "generateUsername": { "message": "Generate username" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Username type" }, @@ -2867,6 +2908,14 @@ "forwardedEmailDesc": { "message": "Generate an email alias with an external forwarding service." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ error: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -4494,9 +4543,15 @@ "itemLocation": { "message": "Item Location" }, + "fileSend": { + "message": "File Send" + }, "fileSends": { "message": "File Sends" }, + "textSend": { + "message": "Text Send" + }, "textSends": { "message": "Text Sends" }, @@ -4559,5 +4614,158 @@ }, "authenticating": { "message": "Authenticating" + }, + "fillGeneratedPassword": { + "message": "Fill generated password", + "description": "Heading for the password generator within the inline menu" + }, + "passwordRegenerated": { + "message": "Password regenerated", + "description": "Notification message for when a password has been regenerated" + }, + "saveLoginToBitwarden": { + "message": "Save login to Bitwarden?", + "description": "Confirmation message for saving a login to Bitwarden" + }, + "spaceCharacterDescriptor": { + "message": "Space", + "description": "Represents the space key in screen reader content as a readable word" + }, + "tildeCharacterDescriptor": { + "message": "Tilde", + "description": "Represents the ~ key in screen reader content as a readable word" + }, + "backtickCharacterDescriptor": { + "message": "Backtick", + "description": "Represents the ` key in screen reader content as a readable word" + }, + "exclamationCharacterDescriptor": { + "message": "Exclamation mark", + "description": "Represents the ! key in screen reader content as a readable word" + }, + "atSignCharacterDescriptor": { + "message": "At sign", + "description": "Represents the @ key in screen reader content as a readable word" + }, + "hashSignCharacterDescriptor": { + "message": "Hash sign", + "description": "Represents the # key in screen reader content as a readable word" + }, + "dollarSignCharacterDescriptor": { + "message": "Dollar sign", + "description": "Represents the $ key in screen reader content as a readable word" + }, + "percentSignCharacterDescriptor": { + "message": "Percent sign", + "description": "Represents the % key in screen reader content as a readable word" + }, + "caretCharacterDescriptor": { + "message": "Caret", + "description": "Represents the ^ key in screen reader content as a readable word" + }, + "ampersandCharacterDescriptor": { + "message": "Ampersand", + "description": "Represents the & key in screen reader content as a readable word" + }, + "asteriskCharacterDescriptor": { + "message": "Asterisk", + "description": "Represents the * key in screen reader content as a readable word" + }, + "parenLeftCharacterDescriptor": { + "message": "Left parenthesis", + "description": "Represents the ( key in screen reader content as a readable word" + }, + "parenRightCharacterDescriptor": { + "message": "Right parenthesis", + "description": "Represents the ) key in screen reader content as a readable word" + }, + "hyphenCharacterDescriptor": { + "message": "Underscore", + "description": "Represents the _ key in screen reader content as a readable word" + }, + "underscoreCharacterDescriptor": { + "message": "Hyphen", + "description": "Represents the - key in screen reader content as a readable word" + }, + "plusCharacterDescriptor": { + "message": "Plus", + "description": "Represents the + key in screen reader content as a readable word" + }, + "equalsCharacterDescriptor": { + "message": "Equals", + "description": "Represents the = key in screen reader content as a readable word" + }, + "braceLeftCharacterDescriptor": { + "message": "Left brace", + "description": "Represents the { key in screen reader content as a readable word" + }, + "braceRightCharacterDescriptor": { + "message": "Right brace", + "description": "Represents the } key in screen reader content as a readable word" + }, + "bracketLeftCharacterDescriptor": { + "message": "Left bracket", + "description": "Represents the [ key in screen reader content as a readable word" + }, + "bracketRightCharacterDescriptor": { + "message": "Right bracket", + "description": "Represents the ] key in screen reader content as a readable word" + }, + "pipeCharacterDescriptor": { + "message": "Pipe", + "description": "Represents the | key in screen reader content as a readable word" + }, + "backSlashCharacterDescriptor": { + "message": "Back slash", + "description": "Represents the back slash key in screen reader content as a readable word" + }, + "colonCharacterDescriptor": { + "message": "Colon", + "description": "Represents the : key in screen reader content as a readable word" + }, + "semicolonCharacterDescriptor": { + "message": "Semicolon", + "description": "Represents the ; key in screen reader content as a readable word" + }, + "doubleQuoteCharacterDescriptor": { + "message": "Double quote", + "description": "Represents the double quote key in screen reader content as a readable word" + }, + "singleQuoteCharacterDescriptor": { + "message": "Single quote", + "description": "Represents the ' key in screen reader content as a readable word" + }, + "lessThanCharacterDescriptor": { + "message": "Less than", + "description": "Represents the < key in screen reader content as a readable word" + }, + "greaterThanCharacterDescriptor": { + "message": "Greater than", + "description": "Represents the > key in screen reader content as a readable word" + }, + "commaCharacterDescriptor": { + "message": "Comma", + "description": "Represents the , key in screen reader content as a readable word" + }, + "periodCharacterDescriptor": { + "message": "Period", + "description": "Represents the . key in screen reader content as a readable word" + }, + "questionCharacterDescriptor": { + "message": "Question mark", + "description": "Represents the ? key in screen reader content as a readable word" + }, + "forwardSlashCharacterDescriptor": { + "message": "Forward slash", + "description": "Represents the / key in screen reader content as a readable word" + }, + "lowercaseAriaLabel": { + "message": "Lowercase" + }, + "uppercaseAriaLabel": { + "message": "Uppercase" + }, + "generatedPassword": { + "message": "Generated password" } } diff --git a/apps/browser/src/_locales/bs/messages.json b/apps/browser/src/_locales/bs/messages.json index 95f90988df4..0702287b27e 100644 --- a/apps/browser/src/_locales/bs/messages.json +++ b/apps/browser/src/_locales/bs/messages.json @@ -19,6 +19,18 @@ "createAccount": { "message": "Napravi račun" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" + }, "setAStrongPassword": { "message": "Set a strong password" }, @@ -107,6 +119,9 @@ "copyPassword": { "message": "Copy password" }, + "copyPassphrase": { + "message": "Copy passphrase" + }, "copyNote": { "message": "Copy note" }, @@ -152,6 +167,10 @@ "copyNotes": { "message": "Copy notes" }, + "fill": { + "message": "Fill", + "description": "This string is used on the vault page to indicate autofilling. Horizontal space is limited in the interface here so try and keep translations as concise as possible." + }, "autoFill": { "message": "Autofill" }, @@ -407,6 +426,9 @@ "generatePassword": { "message": "Generate password" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "regeneratePassword": { "message": "Regenerate password" }, @@ -568,6 +590,15 @@ "launchWebsite": { "message": "Launch website" }, + "launchWebsiteName": { + "message": "Launch website $ITEMNAME$", + "placeholders": { + "itemname": { + "content": "$1", + "example": "Secret item" + } + } + }, "website": { "message": "Website" }, @@ -814,6 +845,9 @@ "logIn": { "message": "Log in" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "restartRegistration": { "message": "Restart registration" }, @@ -1389,6 +1423,10 @@ "baseUrl": { "message": "Server URL" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "API server URL" }, @@ -2464,8 +2502,8 @@ "message": "Optionally require a password for users to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendPasswordDescV2": { - "message": "Require this password to view the Send.", + "sendPasswordDescV3": { + "message": "Add an optional password for recipients to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendNotesDesc": { @@ -2827,6 +2865,9 @@ "generateUsername": { "message": "Generate username" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Username type" }, @@ -2867,6 +2908,14 @@ "forwardedEmailDesc": { "message": "Generate an email alias with an external forwarding service." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ error: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -4494,9 +4543,15 @@ "itemLocation": { "message": "Item Location" }, + "fileSend": { + "message": "File Send" + }, "fileSends": { "message": "File Sends" }, + "textSend": { + "message": "Text Send" + }, "textSends": { "message": "Text Sends" }, @@ -4559,5 +4614,158 @@ }, "authenticating": { "message": "Authenticating" + }, + "fillGeneratedPassword": { + "message": "Fill generated password", + "description": "Heading for the password generator within the inline menu" + }, + "passwordRegenerated": { + "message": "Password regenerated", + "description": "Notification message for when a password has been regenerated" + }, + "saveLoginToBitwarden": { + "message": "Save login to Bitwarden?", + "description": "Confirmation message for saving a login to Bitwarden" + }, + "spaceCharacterDescriptor": { + "message": "Space", + "description": "Represents the space key in screen reader content as a readable word" + }, + "tildeCharacterDescriptor": { + "message": "Tilde", + "description": "Represents the ~ key in screen reader content as a readable word" + }, + "backtickCharacterDescriptor": { + "message": "Backtick", + "description": "Represents the ` key in screen reader content as a readable word" + }, + "exclamationCharacterDescriptor": { + "message": "Exclamation mark", + "description": "Represents the ! key in screen reader content as a readable word" + }, + "atSignCharacterDescriptor": { + "message": "At sign", + "description": "Represents the @ key in screen reader content as a readable word" + }, + "hashSignCharacterDescriptor": { + "message": "Hash sign", + "description": "Represents the # key in screen reader content as a readable word" + }, + "dollarSignCharacterDescriptor": { + "message": "Dollar sign", + "description": "Represents the $ key in screen reader content as a readable word" + }, + "percentSignCharacterDescriptor": { + "message": "Percent sign", + "description": "Represents the % key in screen reader content as a readable word" + }, + "caretCharacterDescriptor": { + "message": "Caret", + "description": "Represents the ^ key in screen reader content as a readable word" + }, + "ampersandCharacterDescriptor": { + "message": "Ampersand", + "description": "Represents the & key in screen reader content as a readable word" + }, + "asteriskCharacterDescriptor": { + "message": "Asterisk", + "description": "Represents the * key in screen reader content as a readable word" + }, + "parenLeftCharacterDescriptor": { + "message": "Left parenthesis", + "description": "Represents the ( key in screen reader content as a readable word" + }, + "parenRightCharacterDescriptor": { + "message": "Right parenthesis", + "description": "Represents the ) key in screen reader content as a readable word" + }, + "hyphenCharacterDescriptor": { + "message": "Underscore", + "description": "Represents the _ key in screen reader content as a readable word" + }, + "underscoreCharacterDescriptor": { + "message": "Hyphen", + "description": "Represents the - key in screen reader content as a readable word" + }, + "plusCharacterDescriptor": { + "message": "Plus", + "description": "Represents the + key in screen reader content as a readable word" + }, + "equalsCharacterDescriptor": { + "message": "Equals", + "description": "Represents the = key in screen reader content as a readable word" + }, + "braceLeftCharacterDescriptor": { + "message": "Left brace", + "description": "Represents the { key in screen reader content as a readable word" + }, + "braceRightCharacterDescriptor": { + "message": "Right brace", + "description": "Represents the } key in screen reader content as a readable word" + }, + "bracketLeftCharacterDescriptor": { + "message": "Left bracket", + "description": "Represents the [ key in screen reader content as a readable word" + }, + "bracketRightCharacterDescriptor": { + "message": "Right bracket", + "description": "Represents the ] key in screen reader content as a readable word" + }, + "pipeCharacterDescriptor": { + "message": "Pipe", + "description": "Represents the | key in screen reader content as a readable word" + }, + "backSlashCharacterDescriptor": { + "message": "Back slash", + "description": "Represents the back slash key in screen reader content as a readable word" + }, + "colonCharacterDescriptor": { + "message": "Colon", + "description": "Represents the : key in screen reader content as a readable word" + }, + "semicolonCharacterDescriptor": { + "message": "Semicolon", + "description": "Represents the ; key in screen reader content as a readable word" + }, + "doubleQuoteCharacterDescriptor": { + "message": "Double quote", + "description": "Represents the double quote key in screen reader content as a readable word" + }, + "singleQuoteCharacterDescriptor": { + "message": "Single quote", + "description": "Represents the ' key in screen reader content as a readable word" + }, + "lessThanCharacterDescriptor": { + "message": "Less than", + "description": "Represents the < key in screen reader content as a readable word" + }, + "greaterThanCharacterDescriptor": { + "message": "Greater than", + "description": "Represents the > key in screen reader content as a readable word" + }, + "commaCharacterDescriptor": { + "message": "Comma", + "description": "Represents the , key in screen reader content as a readable word" + }, + "periodCharacterDescriptor": { + "message": "Period", + "description": "Represents the . key in screen reader content as a readable word" + }, + "questionCharacterDescriptor": { + "message": "Question mark", + "description": "Represents the ? key in screen reader content as a readable word" + }, + "forwardSlashCharacterDescriptor": { + "message": "Forward slash", + "description": "Represents the / key in screen reader content as a readable word" + }, + "lowercaseAriaLabel": { + "message": "Lowercase" + }, + "uppercaseAriaLabel": { + "message": "Uppercase" + }, + "generatedPassword": { + "message": "Generated password" } } diff --git a/apps/browser/src/_locales/ca/messages.json b/apps/browser/src/_locales/ca/messages.json index ec0fd8bef7c..6829e84ba3d 100644 --- a/apps/browser/src/_locales/ca/messages.json +++ b/apps/browser/src/_locales/ca/messages.json @@ -19,6 +19,18 @@ "createAccount": { "message": "Crea un compte" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" + }, "setAStrongPassword": { "message": "Estableix una contrasenya segura" }, @@ -72,7 +84,7 @@ "message": "Uneix-te a l'organització" }, "joinOrganizationName": { - "message": "Join $ORGANIZATIONNAME$", + "message": "Uniu-vos a $ORGANIZATIONNAME$", "placeholders": { "organizationName": { "content": "$1", @@ -107,6 +119,9 @@ "copyPassword": { "message": "Copia contrasenya" }, + "copyPassphrase": { + "message": "Copia clau de pas" + }, "copyNote": { "message": "Copia nota" }, @@ -152,6 +167,10 @@ "copyNotes": { "message": "Copia notes" }, + "fill": { + "message": "Fill", + "description": "This string is used on the vault page to indicate autofilling. Horizontal space is limited in the interface here so try and keep translations as concise as possible." + }, "autoFill": { "message": "Emplenament automàtic" }, @@ -407,6 +426,9 @@ "generatePassword": { "message": "Genera contrasenya" }, + "generatePassphrase": { + "message": "Genera clau de pas" + }, "regeneratePassword": { "message": "Regenera contrasenya" }, @@ -440,7 +462,7 @@ "description": "Card header for password generator include block" }, "uppercaseDescription": { - "message": "Include uppercase characters", + "message": "Inclou majúscules", "description": "Tooltip for the password generator uppercase character checkbox" }, "uppercaseLabel": { @@ -448,7 +470,7 @@ "description": "Label for the password generator uppercase character checkbox" }, "lowercaseDescription": { - "message": "Include lowercase characters", + "message": "Inclou minúscules", "description": "Full description for the password generator lowercase character checkbox" }, "lowercaseLabel": { @@ -456,7 +478,7 @@ "description": "Label for the password generator lowercase character checkbox" }, "numbersDescription": { - "message": "Include numbers", + "message": "Inclou números", "description": "Full description for the password generator numbers checkbox" }, "numbersLabel": { @@ -464,7 +486,7 @@ "description": "Label for the password generator numbers checkbox" }, "specialCharactersDescription": { - "message": "Include special characters", + "message": "Inclou caràcters especials", "description": "Full description for the password generator special characters checkbox" }, "specialCharactersLabel": { @@ -495,7 +517,7 @@ "description": "deprecated. Use avoidAmbiguous instead." }, "avoidAmbiguous": { - "message": "Avoid ambiguous characters", + "message": "Eviteu caràcters ambigus", "description": "Label for the avoid ambiguous characters checkbox." }, "generatorPolicyInEffect": { @@ -568,6 +590,15 @@ "launchWebsite": { "message": "Obri la web" }, + "launchWebsiteName": { + "message": "Launch website $ITEMNAME$", + "placeholders": { + "itemname": { + "content": "$1", + "example": "Secret item" + } + } + }, "website": { "message": "Lloc web" }, @@ -593,7 +624,7 @@ "message": "Temps de sessió" }, "vaultTimeoutHeader": { - "message": "Vault timeout" + "message": "Temps d'espera de la caixa forta" }, "otherOptions": { "message": "Altres opcions" @@ -614,13 +645,13 @@ "message": "La caixa forta està bloquejada. Comproveu la contrasenya mestra per continuar." }, "yourVaultIsLockedV2": { - "message": "Your vault is locked" + "message": "La caixa forta està bloquejada" }, "yourAccountIsLocked": { - "message": "Your account is locked" + "message": "El compte està bloquejat" }, "or": { - "message": "or" + "message": "o" }, "unlock": { "message": "Desbloqueja" @@ -814,6 +845,9 @@ "logIn": { "message": "Inicia sessió" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "restartRegistration": { "message": "Restart registration" }, @@ -1389,6 +1423,10 @@ "baseUrl": { "message": "URL del servidor" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "URL del servidor API" }, @@ -2464,8 +2502,8 @@ "message": "Opcionalment, necessiteu una contrasenya perquè els usuaris accedisquen a aquest Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendPasswordDescV2": { - "message": "Require this password to view the Send.", + "sendPasswordDescV3": { + "message": "Add an optional password for recipients to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendNotesDesc": { @@ -2827,6 +2865,9 @@ "generateUsername": { "message": "Genera un nom d'usuari" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Tipus de nom d'usuari" }, @@ -2867,6 +2908,14 @@ "forwardedEmailDesc": { "message": "Genera un àlies de correu electrònic amb un servei de reenviament extern." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "Error de $SERVICENAME$: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -4494,9 +4543,15 @@ "itemLocation": { "message": "Item Location" }, + "fileSend": { + "message": "File Send" + }, "fileSends": { "message": "File Sends" }, + "textSend": { + "message": "Text Send" + }, "textSends": { "message": "Text Sends" }, @@ -4559,5 +4614,158 @@ }, "authenticating": { "message": "Authenticating" + }, + "fillGeneratedPassword": { + "message": "Fill generated password", + "description": "Heading for the password generator within the inline menu" + }, + "passwordRegenerated": { + "message": "Password regenerated", + "description": "Notification message for when a password has been regenerated" + }, + "saveLoginToBitwarden": { + "message": "Save login to Bitwarden?", + "description": "Confirmation message for saving a login to Bitwarden" + }, + "spaceCharacterDescriptor": { + "message": "Space", + "description": "Represents the space key in screen reader content as a readable word" + }, + "tildeCharacterDescriptor": { + "message": "Tilde", + "description": "Represents the ~ key in screen reader content as a readable word" + }, + "backtickCharacterDescriptor": { + "message": "Backtick", + "description": "Represents the ` key in screen reader content as a readable word" + }, + "exclamationCharacterDescriptor": { + "message": "Exclamation mark", + "description": "Represents the ! key in screen reader content as a readable word" + }, + "atSignCharacterDescriptor": { + "message": "At sign", + "description": "Represents the @ key in screen reader content as a readable word" + }, + "hashSignCharacterDescriptor": { + "message": "Hash sign", + "description": "Represents the # key in screen reader content as a readable word" + }, + "dollarSignCharacterDescriptor": { + "message": "Dollar sign", + "description": "Represents the $ key in screen reader content as a readable word" + }, + "percentSignCharacterDescriptor": { + "message": "Percent sign", + "description": "Represents the % key in screen reader content as a readable word" + }, + "caretCharacterDescriptor": { + "message": "Caret", + "description": "Represents the ^ key in screen reader content as a readable word" + }, + "ampersandCharacterDescriptor": { + "message": "Ampersand", + "description": "Represents the & key in screen reader content as a readable word" + }, + "asteriskCharacterDescriptor": { + "message": "Asterisk", + "description": "Represents the * key in screen reader content as a readable word" + }, + "parenLeftCharacterDescriptor": { + "message": "Left parenthesis", + "description": "Represents the ( key in screen reader content as a readable word" + }, + "parenRightCharacterDescriptor": { + "message": "Right parenthesis", + "description": "Represents the ) key in screen reader content as a readable word" + }, + "hyphenCharacterDescriptor": { + "message": "Underscore", + "description": "Represents the _ key in screen reader content as a readable word" + }, + "underscoreCharacterDescriptor": { + "message": "Hyphen", + "description": "Represents the - key in screen reader content as a readable word" + }, + "plusCharacterDescriptor": { + "message": "Plus", + "description": "Represents the + key in screen reader content as a readable word" + }, + "equalsCharacterDescriptor": { + "message": "Equals", + "description": "Represents the = key in screen reader content as a readable word" + }, + "braceLeftCharacterDescriptor": { + "message": "Left brace", + "description": "Represents the { key in screen reader content as a readable word" + }, + "braceRightCharacterDescriptor": { + "message": "Right brace", + "description": "Represents the } key in screen reader content as a readable word" + }, + "bracketLeftCharacterDescriptor": { + "message": "Left bracket", + "description": "Represents the [ key in screen reader content as a readable word" + }, + "bracketRightCharacterDescriptor": { + "message": "Right bracket", + "description": "Represents the ] key in screen reader content as a readable word" + }, + "pipeCharacterDescriptor": { + "message": "Pipe", + "description": "Represents the | key in screen reader content as a readable word" + }, + "backSlashCharacterDescriptor": { + "message": "Back slash", + "description": "Represents the back slash key in screen reader content as a readable word" + }, + "colonCharacterDescriptor": { + "message": "Colon", + "description": "Represents the : key in screen reader content as a readable word" + }, + "semicolonCharacterDescriptor": { + "message": "Semicolon", + "description": "Represents the ; key in screen reader content as a readable word" + }, + "doubleQuoteCharacterDescriptor": { + "message": "Double quote", + "description": "Represents the double quote key in screen reader content as a readable word" + }, + "singleQuoteCharacterDescriptor": { + "message": "Single quote", + "description": "Represents the ' key in screen reader content as a readable word" + }, + "lessThanCharacterDescriptor": { + "message": "Less than", + "description": "Represents the < key in screen reader content as a readable word" + }, + "greaterThanCharacterDescriptor": { + "message": "Greater than", + "description": "Represents the > key in screen reader content as a readable word" + }, + "commaCharacterDescriptor": { + "message": "Comma", + "description": "Represents the , key in screen reader content as a readable word" + }, + "periodCharacterDescriptor": { + "message": "Period", + "description": "Represents the . key in screen reader content as a readable word" + }, + "questionCharacterDescriptor": { + "message": "Question mark", + "description": "Represents the ? key in screen reader content as a readable word" + }, + "forwardSlashCharacterDescriptor": { + "message": "Forward slash", + "description": "Represents the / key in screen reader content as a readable word" + }, + "lowercaseAriaLabel": { + "message": "Lowercase" + }, + "uppercaseAriaLabel": { + "message": "Uppercase" + }, + "generatedPassword": { + "message": "Generated password" } } diff --git a/apps/browser/src/_locales/cs/messages.json b/apps/browser/src/_locales/cs/messages.json index bdc245099c1..3baa325fe8e 100644 --- a/apps/browser/src/_locales/cs/messages.json +++ b/apps/browser/src/_locales/cs/messages.json @@ -19,6 +19,18 @@ "createAccount": { "message": "Vytvořit účet" }, + "newToBitwarden": { + "message": "Jste noví na Bitwardenu?" + }, + "logInWithPasskey": { + "message": "Přihlásit se pomocí přístupového klíče" + }, + "useSingleSignOn": { + "message": "Použít jednotné přihlášení" + }, + "welcomeBack": { + "message": "Vítejte zpět" + }, "setAStrongPassword": { "message": "Nastavit hlavní heslo" }, @@ -107,6 +119,9 @@ "copyPassword": { "message": "Kopírovat heslo" }, + "copyPassphrase": { + "message": "Kopírovat heslovou frázi" + }, "copyNote": { "message": "Kopírovat poznámku" }, @@ -152,6 +167,10 @@ "copyNotes": { "message": "Kopírovat poznámky" }, + "fill": { + "message": "Vyplnit", + "description": "This string is used on the vault page to indicate autofilling. Horizontal space is limited in the interface here so try and keep translations as concise as possible." + }, "autoFill": { "message": "Automatické vyplňování" }, @@ -407,6 +426,9 @@ "generatePassword": { "message": "Vygenerovat heslo" }, + "generatePassphrase": { + "message": "Vygenerovat heslovou frázi" + }, "regeneratePassword": { "message": "Vygenerovat jiné heslo" }, @@ -568,6 +590,15 @@ "launchWebsite": { "message": "Otevřít webovou stránku" }, + "launchWebsiteName": { + "message": "Spustit web $ITEMNAME$", + "placeholders": { + "itemname": { + "content": "$1", + "example": "Secret item" + } + } + }, "website": { "message": "Webová stránka" }, @@ -814,6 +845,9 @@ "logIn": { "message": "Přihlásit se" }, + "logInToBitwarden": { + "message": "Přihlásit se do Bitwardenu" + }, "restartRegistration": { "message": "Restartovat registraci" }, @@ -1389,6 +1423,10 @@ "baseUrl": { "message": "URL serveru" }, + "selfHostBaseUrl": { + "message": "Adresa URL serveru vlastního hostování", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "URL API serveru" }, @@ -2464,8 +2502,8 @@ "message": "Volitelně vyžadovat heslo pro přístup k tomuto Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendPasswordDescV2": { - "message": "Pro zobrazení Send bude vyžadováno toto heslo.", + "sendPasswordDescV3": { + "message": "Přidá volitelné heslo pro příjemce pro přístup k tomuto Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendNotesDesc": { @@ -2827,6 +2865,9 @@ "generateUsername": { "message": "Vygenerovat uživatelské jméno" }, + "generateEmail": { + "message": "Vygenerovat e-mail" + }, "usernameType": { "message": "Typ uživatelského jména" }, @@ -2867,6 +2908,14 @@ "forwardedEmailDesc": { "message": "Vygenerovat e-mailový alias pomocí externí služby pro přesměrování." }, + "forwarderDomainName": { + "message": "E-mailová doména", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Vyberte doménu, která je podporována vybranou službou", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "Chyba $SERVICENAME$: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -4494,9 +4543,15 @@ "itemLocation": { "message": "Umístění položky" }, + "fileSend": { + "message": "Send souboru" + }, "fileSends": { "message": "Sends se soubory" }, + "textSend": { + "message": "Send textu" + }, "textSends": { "message": "Sends s texty" }, @@ -4559,5 +4614,158 @@ }, "authenticating": { "message": "Ověřování" + }, + "fillGeneratedPassword": { + "message": "Vyplnit vygenerované heslo", + "description": "Heading for the password generator within the inline menu" + }, + "passwordRegenerated": { + "message": "Heslo bylo znovu vygenerováno", + "description": "Notification message for when a password has been regenerated" + }, + "saveLoginToBitwarden": { + "message": "Uložit přihlášení do Bitwardenu?", + "description": "Confirmation message for saving a login to Bitwarden" + }, + "spaceCharacterDescriptor": { + "message": "Mezera", + "description": "Represents the space key in screen reader content as a readable word" + }, + "tildeCharacterDescriptor": { + "message": "Tilda", + "description": "Represents the ~ key in screen reader content as a readable word" + }, + "backtickCharacterDescriptor": { + "message": "Zpětný apostrof", + "description": "Represents the ` key in screen reader content as a readable word" + }, + "exclamationCharacterDescriptor": { + "message": "Vykřičník", + "description": "Represents the ! key in screen reader content as a readable word" + }, + "atSignCharacterDescriptor": { + "message": "Zavináč", + "description": "Represents the @ key in screen reader content as a readable word" + }, + "hashSignCharacterDescriptor": { + "message": "Mřížka", + "description": "Represents the # key in screen reader content as a readable word" + }, + "dollarSignCharacterDescriptor": { + "message": "Dolar", + "description": "Represents the $ key in screen reader content as a readable word" + }, + "percentSignCharacterDescriptor": { + "message": "Procento", + "description": "Represents the % key in screen reader content as a readable word" + }, + "caretCharacterDescriptor": { + "message": "Stříška", + "description": "Represents the ^ key in screen reader content as a readable word" + }, + "ampersandCharacterDescriptor": { + "message": "Ampersand", + "description": "Represents the & key in screen reader content as a readable word" + }, + "asteriskCharacterDescriptor": { + "message": "Hvězdička", + "description": "Represents the * key in screen reader content as a readable word" + }, + "parenLeftCharacterDescriptor": { + "message": "Levá závorka", + "description": "Represents the ( key in screen reader content as a readable word" + }, + "parenRightCharacterDescriptor": { + "message": "Pravá závorka", + "description": "Represents the ) key in screen reader content as a readable word" + }, + "hyphenCharacterDescriptor": { + "message": "Podtržítko", + "description": "Represents the _ key in screen reader content as a readable word" + }, + "underscoreCharacterDescriptor": { + "message": "Spojovník", + "description": "Represents the - key in screen reader content as a readable word" + }, + "plusCharacterDescriptor": { + "message": "Plus", + "description": "Represents the + key in screen reader content as a readable word" + }, + "equalsCharacterDescriptor": { + "message": "Rovnítko", + "description": "Represents the = key in screen reader content as a readable word" + }, + "braceLeftCharacterDescriptor": { + "message": "Levá složená závorka", + "description": "Represents the { key in screen reader content as a readable word" + }, + "braceRightCharacterDescriptor": { + "message": "Pravá složená závorka", + "description": "Represents the } key in screen reader content as a readable word" + }, + "bracketLeftCharacterDescriptor": { + "message": "Levá hranatá závorka", + "description": "Represents the [ key in screen reader content as a readable word" + }, + "bracketRightCharacterDescriptor": { + "message": "Pravá hranatá závorka", + "description": "Represents the ] key in screen reader content as a readable word" + }, + "pipeCharacterDescriptor": { + "message": "Svislá čára", + "description": "Represents the | key in screen reader content as a readable word" + }, + "backSlashCharacterDescriptor": { + "message": "Zpětné lomítko", + "description": "Represents the back slash key in screen reader content as a readable word" + }, + "colonCharacterDescriptor": { + "message": "Dvojtečka", + "description": "Represents the : key in screen reader content as a readable word" + }, + "semicolonCharacterDescriptor": { + "message": "Středník", + "description": "Represents the ; key in screen reader content as a readable word" + }, + "doubleQuoteCharacterDescriptor": { + "message": "Dvojitá uvozovka", + "description": "Represents the double quote key in screen reader content as a readable word" + }, + "singleQuoteCharacterDescriptor": { + "message": "Apostrof", + "description": "Represents the ' key in screen reader content as a readable word" + }, + "lessThanCharacterDescriptor": { + "message": "Menší než", + "description": "Represents the < key in screen reader content as a readable word" + }, + "greaterThanCharacterDescriptor": { + "message": "Větší než", + "description": "Represents the > key in screen reader content as a readable word" + }, + "commaCharacterDescriptor": { + "message": "Čárka", + "description": "Represents the , key in screen reader content as a readable word" + }, + "periodCharacterDescriptor": { + "message": "Tečka", + "description": "Represents the . key in screen reader content as a readable word" + }, + "questionCharacterDescriptor": { + "message": "Otazník", + "description": "Represents the ? key in screen reader content as a readable word" + }, + "forwardSlashCharacterDescriptor": { + "message": "Lomítko", + "description": "Represents the / key in screen reader content as a readable word" + }, + "lowercaseAriaLabel": { + "message": "Všechna malá písmena" + }, + "uppercaseAriaLabel": { + "message": "Všechna velká písmena" + }, + "generatedPassword": { + "message": "Vygenerované heslo" } } diff --git a/apps/browser/src/_locales/cy/messages.json b/apps/browser/src/_locales/cy/messages.json index e5b77b001ec..90b5987a1cb 100644 --- a/apps/browser/src/_locales/cy/messages.json +++ b/apps/browser/src/_locales/cy/messages.json @@ -19,6 +19,18 @@ "createAccount": { "message": "Creu cyfrif" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" + }, "setAStrongPassword": { "message": "Gosod cyfrinair cryf" }, @@ -107,6 +119,9 @@ "copyPassword": { "message": "Copïo cyfrinair" }, + "copyPassphrase": { + "message": "Copy passphrase" + }, "copyNote": { "message": "Copïo'r nodyn" }, @@ -152,6 +167,10 @@ "copyNotes": { "message": "Copy notes" }, + "fill": { + "message": "Fill", + "description": "This string is used on the vault page to indicate autofilling. Horizontal space is limited in the interface here so try and keep translations as concise as possible." + }, "autoFill": { "message": "Llenwi'n awtomatig" }, @@ -407,6 +426,9 @@ "generatePassword": { "message": "Cynhyrchu cyfrinair" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "regeneratePassword": { "message": "Ailgynhyrchu cyfrinair" }, @@ -568,6 +590,15 @@ "launchWebsite": { "message": "Launch website" }, + "launchWebsiteName": { + "message": "Launch website $ITEMNAME$", + "placeholders": { + "itemname": { + "content": "$1", + "example": "Secret item" + } + } + }, "website": { "message": "Gwefan" }, @@ -814,6 +845,9 @@ "logIn": { "message": "Log in" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "restartRegistration": { "message": "Restart registration" }, @@ -1389,6 +1423,10 @@ "baseUrl": { "message": "Server URL" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "API server URL" }, @@ -2464,8 +2502,8 @@ "message": "Optionally require a password for users to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendPasswordDescV2": { - "message": "Require this password to view the Send.", + "sendPasswordDescV3": { + "message": "Add an optional password for recipients to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendNotesDesc": { @@ -2827,6 +2865,9 @@ "generateUsername": { "message": "Cynhyrchu enw defnyddiwr" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Math o enw defnyddiwr" }, @@ -2867,6 +2908,14 @@ "forwardedEmailDesc": { "message": "Generate an email alias with an external forwarding service." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ error: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -4494,9 +4543,15 @@ "itemLocation": { "message": "Item Location" }, + "fileSend": { + "message": "File Send" + }, "fileSends": { "message": "File Sends" }, + "textSend": { + "message": "Text Send" + }, "textSends": { "message": "Text Sends" }, @@ -4559,5 +4614,158 @@ }, "authenticating": { "message": "Authenticating" + }, + "fillGeneratedPassword": { + "message": "Fill generated password", + "description": "Heading for the password generator within the inline menu" + }, + "passwordRegenerated": { + "message": "Password regenerated", + "description": "Notification message for when a password has been regenerated" + }, + "saveLoginToBitwarden": { + "message": "Save login to Bitwarden?", + "description": "Confirmation message for saving a login to Bitwarden" + }, + "spaceCharacterDescriptor": { + "message": "Space", + "description": "Represents the space key in screen reader content as a readable word" + }, + "tildeCharacterDescriptor": { + "message": "Tilde", + "description": "Represents the ~ key in screen reader content as a readable word" + }, + "backtickCharacterDescriptor": { + "message": "Backtick", + "description": "Represents the ` key in screen reader content as a readable word" + }, + "exclamationCharacterDescriptor": { + "message": "Exclamation mark", + "description": "Represents the ! key in screen reader content as a readable word" + }, + "atSignCharacterDescriptor": { + "message": "At sign", + "description": "Represents the @ key in screen reader content as a readable word" + }, + "hashSignCharacterDescriptor": { + "message": "Hash sign", + "description": "Represents the # key in screen reader content as a readable word" + }, + "dollarSignCharacterDescriptor": { + "message": "Dollar sign", + "description": "Represents the $ key in screen reader content as a readable word" + }, + "percentSignCharacterDescriptor": { + "message": "Percent sign", + "description": "Represents the % key in screen reader content as a readable word" + }, + "caretCharacterDescriptor": { + "message": "Caret", + "description": "Represents the ^ key in screen reader content as a readable word" + }, + "ampersandCharacterDescriptor": { + "message": "Ampersand", + "description": "Represents the & key in screen reader content as a readable word" + }, + "asteriskCharacterDescriptor": { + "message": "Asterisk", + "description": "Represents the * key in screen reader content as a readable word" + }, + "parenLeftCharacterDescriptor": { + "message": "Left parenthesis", + "description": "Represents the ( key in screen reader content as a readable word" + }, + "parenRightCharacterDescriptor": { + "message": "Right parenthesis", + "description": "Represents the ) key in screen reader content as a readable word" + }, + "hyphenCharacterDescriptor": { + "message": "Underscore", + "description": "Represents the _ key in screen reader content as a readable word" + }, + "underscoreCharacterDescriptor": { + "message": "Hyphen", + "description": "Represents the - key in screen reader content as a readable word" + }, + "plusCharacterDescriptor": { + "message": "Plus", + "description": "Represents the + key in screen reader content as a readable word" + }, + "equalsCharacterDescriptor": { + "message": "Equals", + "description": "Represents the = key in screen reader content as a readable word" + }, + "braceLeftCharacterDescriptor": { + "message": "Left brace", + "description": "Represents the { key in screen reader content as a readable word" + }, + "braceRightCharacterDescriptor": { + "message": "Right brace", + "description": "Represents the } key in screen reader content as a readable word" + }, + "bracketLeftCharacterDescriptor": { + "message": "Left bracket", + "description": "Represents the [ key in screen reader content as a readable word" + }, + "bracketRightCharacterDescriptor": { + "message": "Right bracket", + "description": "Represents the ] key in screen reader content as a readable word" + }, + "pipeCharacterDescriptor": { + "message": "Pipe", + "description": "Represents the | key in screen reader content as a readable word" + }, + "backSlashCharacterDescriptor": { + "message": "Back slash", + "description": "Represents the back slash key in screen reader content as a readable word" + }, + "colonCharacterDescriptor": { + "message": "Colon", + "description": "Represents the : key in screen reader content as a readable word" + }, + "semicolonCharacterDescriptor": { + "message": "Semicolon", + "description": "Represents the ; key in screen reader content as a readable word" + }, + "doubleQuoteCharacterDescriptor": { + "message": "Double quote", + "description": "Represents the double quote key in screen reader content as a readable word" + }, + "singleQuoteCharacterDescriptor": { + "message": "Single quote", + "description": "Represents the ' key in screen reader content as a readable word" + }, + "lessThanCharacterDescriptor": { + "message": "Less than", + "description": "Represents the < key in screen reader content as a readable word" + }, + "greaterThanCharacterDescriptor": { + "message": "Greater than", + "description": "Represents the > key in screen reader content as a readable word" + }, + "commaCharacterDescriptor": { + "message": "Comma", + "description": "Represents the , key in screen reader content as a readable word" + }, + "periodCharacterDescriptor": { + "message": "Period", + "description": "Represents the . key in screen reader content as a readable word" + }, + "questionCharacterDescriptor": { + "message": "Question mark", + "description": "Represents the ? key in screen reader content as a readable word" + }, + "forwardSlashCharacterDescriptor": { + "message": "Forward slash", + "description": "Represents the / key in screen reader content as a readable word" + }, + "lowercaseAriaLabel": { + "message": "Lowercase" + }, + "uppercaseAriaLabel": { + "message": "Uppercase" + }, + "generatedPassword": { + "message": "Generated password" } } diff --git a/apps/browser/src/_locales/da/messages.json b/apps/browser/src/_locales/da/messages.json index 8ec111967fa..d14b62a3dd8 100644 --- a/apps/browser/src/_locales/da/messages.json +++ b/apps/browser/src/_locales/da/messages.json @@ -19,6 +19,18 @@ "createAccount": { "message": "Opret konto" }, + "newToBitwarden": { + "message": "Ny på Bitwarden?" + }, + "logInWithPasskey": { + "message": "Log ind med adgangsnøgle" + }, + "useSingleSignOn": { + "message": "Brug Single Sign-On" + }, + "welcomeBack": { + "message": "Velkommen tilbage" + }, "setAStrongPassword": { "message": "Indstil en stærk adgangskode" }, @@ -107,6 +119,9 @@ "copyPassword": { "message": "Kopiér adgangskode" }, + "copyPassphrase": { + "message": "Kopiér adgangssætning" + }, "copyNote": { "message": "Kopiér notat" }, @@ -152,6 +167,10 @@ "copyNotes": { "message": "Kopiér notater" }, + "fill": { + "message": "Udfyld", + "description": "This string is used on the vault page to indicate autofilling. Horizontal space is limited in the interface here so try and keep translations as concise as possible." + }, "autoFill": { "message": "Autoudfyld" }, @@ -407,6 +426,9 @@ "generatePassword": { "message": "Generér adgangskode" }, + "generatePassphrase": { + "message": "Generér adgangssætning" + }, "regeneratePassword": { "message": "Regenerér adgangskode" }, @@ -545,7 +567,7 @@ "message": "Notater" }, "privateNote": { - "message": "Private note" + "message": "Privat notat" }, "note": { "message": "Notat" @@ -568,6 +590,15 @@ "launchWebsite": { "message": "Åbn websted" }, + "launchWebsiteName": { + "message": "Åbn webstedet $ITEMNAME$", + "placeholders": { + "itemname": { + "content": "$1", + "example": "Secret item" + } + } + }, "website": { "message": "Hjemmeside" }, @@ -814,6 +845,9 @@ "logIn": { "message": "Log ind" }, + "logInToBitwarden": { + "message": "Log ind på Bitwarden" + }, "restartRegistration": { "message": "Genstart registrering" }, @@ -1389,6 +1423,10 @@ "baseUrl": { "message": "Server URL" }, + "selfHostBaseUrl": { + "message": "URL til selv-hostet server", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "API-server URL" }, @@ -2302,14 +2340,14 @@ "message": "Ekskluderet domæne-ændringer gemt" }, "limitSendViews": { - "message": "Limit views" + "message": "Begræns visninger" }, "limitSendViewsHint": { - "message": "No one can view this Send after the limit is reached.", + "message": "Ingen kan se denne Send efter grænsen er nået.", "description": "Displayed under the limit views field on Send" }, "limitSendViewsCount": { - "message": "$ACCESSCOUNT$ views left", + "message": "$ACCESSCOUNT$ visninger tilbage", "description": "Displayed under the limit views field on Send", "placeholders": { "accessCount": { @@ -2464,8 +2502,8 @@ "message": "Valgfrit brugeradgangskodekrav for at tilgå denne Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendPasswordDescV2": { - "message": "Require this password to view the Send.", + "sendPasswordDescV3": { + "message": "Tilføj en valgfri adgangskode til modtagere for adgang til denne Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendNotesDesc": { @@ -2554,7 +2592,7 @@ "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendFilePopoutDialogDesc": { - "message": "For at oprette en fil-Send, skal udvidelsen poppes ud til et nyt vindue.", + "message": "For at oprette en fil-Send, skal udvidelsen poppes ud i et nyt vindue.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendLinuxChromiumFileWarning": { @@ -2603,7 +2641,7 @@ "message": "Skjul min e-mailadresse for modtagere." }, "hideYourEmail": { - "message": "Hide your email address from viewers." + "message": "Skjul e-mailadressen for modtagere." }, "sendOptionsPolicyInEffect": { "message": "Én eller flere organisationspolitikker påvirker dine Send-valgmuligheder." @@ -2827,6 +2865,9 @@ "generateUsername": { "message": "Generér brugernavn" }, + "generateEmail": { + "message": "Generér e-mail" + }, "usernameType": { "message": "Brugernavnstype" }, @@ -2867,6 +2908,14 @@ "forwardedEmailDesc": { "message": "Generér et e-mail alias med en ekstern viderestillingstjeneste." }, + "forwarderDomainName": { + "message": "E-maildomæne", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Vælg et domæne understøttet af den valgte tjeneste", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$-fejl: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -4494,9 +4543,15 @@ "itemLocation": { "message": "Emneplacering" }, + "fileSend": { + "message": "Fil Send" + }, "fileSends": { "message": "Fil-Sends" }, + "textSend": { + "message": "Tekst Send" + }, "textSends": { "message": "Tekst-Sends" }, @@ -4559,5 +4614,158 @@ }, "authenticating": { "message": "Godkender" + }, + "fillGeneratedPassword": { + "message": "Udfyld genereret adgangskode", + "description": "Heading for the password generator within the inline menu" + }, + "passwordRegenerated": { + "message": "Adgangskode genereret igen", + "description": "Notification message for when a password has been regenerated" + }, + "saveLoginToBitwarden": { + "message": "Gem login til Bitwarden?", + "description": "Confirmation message for saving a login to Bitwarden" + }, + "spaceCharacterDescriptor": { + "message": "Mellemrum", + "description": "Represents the space key in screen reader content as a readable word" + }, + "tildeCharacterDescriptor": { + "message": "Tilde", + "description": "Represents the ~ key in screen reader content as a readable word" + }, + "backtickCharacterDescriptor": { + "message": "Accent grave", + "description": "Represents the ` key in screen reader content as a readable word" + }, + "exclamationCharacterDescriptor": { + "message": "Udråbstegn", + "description": "Represents the ! key in screen reader content as a readable word" + }, + "atSignCharacterDescriptor": { + "message": "Snabel-a", + "description": "Represents the @ key in screen reader content as a readable word" + }, + "hashSignCharacterDescriptor": { + "message": "Hash-tegn", + "description": "Represents the # key in screen reader content as a readable word" + }, + "dollarSignCharacterDescriptor": { + "message": "Dollar-tegn", + "description": "Represents the $ key in screen reader content as a readable word" + }, + "percentSignCharacterDescriptor": { + "message": "Procenttegn", + "description": "Represents the % key in screen reader content as a readable word" + }, + "caretCharacterDescriptor": { + "message": "Cirkumfleks", + "description": "Represents the ^ key in screen reader content as a readable word" + }, + "ampersandCharacterDescriptor": { + "message": "Og-tegn", + "description": "Represents the & key in screen reader content as a readable word" + }, + "asteriskCharacterDescriptor": { + "message": "Stjerne", + "description": "Represents the * key in screen reader content as a readable word" + }, + "parenLeftCharacterDescriptor": { + "message": "Venstre parentes", + "description": "Represents the ( key in screen reader content as a readable word" + }, + "parenRightCharacterDescriptor": { + "message": "Højre parentes", + "description": "Represents the ) key in screen reader content as a readable word" + }, + "hyphenCharacterDescriptor": { + "message": "Understregning", + "description": "Represents the _ key in screen reader content as a readable word" + }, + "underscoreCharacterDescriptor": { + "message": "Bindestreg", + "description": "Represents the - key in screen reader content as a readable word" + }, + "plusCharacterDescriptor": { + "message": "Plus", + "description": "Represents the + key in screen reader content as a readable word" + }, + "equalsCharacterDescriptor": { + "message": "Lighedstegn", + "description": "Represents the = key in screen reader content as a readable word" + }, + "braceLeftCharacterDescriptor": { + "message": "Venstre tuborg", + "description": "Represents the { key in screen reader content as a readable word" + }, + "braceRightCharacterDescriptor": { + "message": "Højre tuborg", + "description": "Represents the } key in screen reader content as a readable word" + }, + "bracketLeftCharacterDescriptor": { + "message": "Venstre klamme", + "description": "Represents the [ key in screen reader content as a readable word" + }, + "bracketRightCharacterDescriptor": { + "message": "Højre klamme", + "description": "Represents the ] key in screen reader content as a readable word" + }, + "pipeCharacterDescriptor": { + "message": "Lodret streg", + "description": "Represents the | key in screen reader content as a readable word" + }, + "backSlashCharacterDescriptor": { + "message": "Omvendt skråstreg", + "description": "Represents the back slash key in screen reader content as a readable word" + }, + "colonCharacterDescriptor": { + "message": "Kolon", + "description": "Represents the : key in screen reader content as a readable word" + }, + "semicolonCharacterDescriptor": { + "message": "Semikolon", + "description": "Represents the ; key in screen reader content as a readable word" + }, + "doubleQuoteCharacterDescriptor": { + "message": "Dobbelt anførselstegn", + "description": "Represents the double quote key in screen reader content as a readable word" + }, + "singleQuoteCharacterDescriptor": { + "message": "Enkelt anførselstegn", + "description": "Represents the ' key in screen reader content as a readable word" + }, + "lessThanCharacterDescriptor": { + "message": "Mindre end", + "description": "Represents the < key in screen reader content as a readable word" + }, + "greaterThanCharacterDescriptor": { + "message": "Større end", + "description": "Represents the > key in screen reader content as a readable word" + }, + "commaCharacterDescriptor": { + "message": "Komma", + "description": "Represents the , key in screen reader content as a readable word" + }, + "periodCharacterDescriptor": { + "message": "Punktum", + "description": "Represents the . key in screen reader content as a readable word" + }, + "questionCharacterDescriptor": { + "message": "Spørgsmålstegn", + "description": "Represents the ? key in screen reader content as a readable word" + }, + "forwardSlashCharacterDescriptor": { + "message": "Skråstreg", + "description": "Represents the / key in screen reader content as a readable word" + }, + "lowercaseAriaLabel": { + "message": "Minuskel" + }, + "uppercaseAriaLabel": { + "message": "Majuskel" + }, + "generatedPassword": { + "message": "Genereret adgangskode" } } diff --git a/apps/browser/src/_locales/de/messages.json b/apps/browser/src/_locales/de/messages.json index a28c8f947b6..c0748518f54 100644 --- a/apps/browser/src/_locales/de/messages.json +++ b/apps/browser/src/_locales/de/messages.json @@ -19,6 +19,18 @@ "createAccount": { "message": "Konto erstellen" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" + }, "setAStrongPassword": { "message": "Lege ein starkes Passwort fest" }, @@ -107,6 +119,9 @@ "copyPassword": { "message": "Passwort kopieren" }, + "copyPassphrase": { + "message": "Passphrase kopieren" + }, "copyNote": { "message": "Notiz kopieren" }, @@ -152,6 +167,10 @@ "copyNotes": { "message": "Notizen kopieren" }, + "fill": { + "message": "Ausfüllen", + "description": "This string is used on the vault page to indicate autofilling. Horizontal space is limited in the interface here so try and keep translations as concise as possible." + }, "autoFill": { "message": "Auto-Ausfüllen" }, @@ -407,6 +426,9 @@ "generatePassword": { "message": "Passwort generieren" }, + "generatePassphrase": { + "message": "Passphrase generieren" + }, "regeneratePassword": { "message": "Passwort neu generieren" }, @@ -568,6 +590,15 @@ "launchWebsite": { "message": "Website öffnen" }, + "launchWebsiteName": { + "message": "Website aufrufen: $ITEMNAME$", + "placeholders": { + "itemname": { + "content": "$1", + "example": "Secret item" + } + } + }, "website": { "message": "Webseite" }, @@ -814,6 +845,9 @@ "logIn": { "message": "Anmelden" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "restartRegistration": { "message": "Registrierung neu starten" }, @@ -1389,6 +1423,10 @@ "baseUrl": { "message": "Server URL" }, + "selfHostBaseUrl": { + "message": "Selbst gehostete Server-URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "API Server-URL" }, @@ -1418,10 +1456,10 @@ "message": "Vorschläge zum Auto-Ausfüllen in Formularfeldern anzeigen" }, "showInlineMenuIdentitiesLabel": { - "message": "Zeige Identitäten als Vorschläge" + "message": "Identitäten als Vorschläge anzeigen" }, "showInlineMenuCardsLabel": { - "message": "Zeige Karten als Vorschläge" + "message": "Karten als Vorschläge anzeigen" }, "showInlineMenuOnIconSelectionLabel": { "message": "Vorschläge anzeigen, wenn Symbol ausgewählt ist" @@ -2464,8 +2502,8 @@ "message": "Optional ein Passwort verlangen, damit Benutzer auf dieses Send zugreifen können.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendPasswordDescV2": { - "message": "Dieses Passwort zum Anzeigen des Sends verlangen.", + "sendPasswordDescV3": { + "message": "Füge ein optionales Passwort hinzu, mit dem Empfänger auf dieses Send zugreifen können.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendNotesDesc": { @@ -2827,6 +2865,9 @@ "generateUsername": { "message": "Benutzername generieren" }, + "generateEmail": { + "message": "E-Mail generieren" + }, "usernameType": { "message": "Benutzernamenstyp" }, @@ -2867,6 +2908,14 @@ "forwardedEmailDesc": { "message": "Generiere ein E-Mail-Alias mit einem externen Weiterleitungsdienst." }, + "forwarderDomainName": { + "message": "E-Mail-Domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Wähle eine Domain aus, die vom ausgewählten Dienst unterstützt wird", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ Fehler: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -4494,9 +4543,15 @@ "itemLocation": { "message": "Eintrags-Standort" }, + "fileSend": { + "message": "Datei-Send" + }, "fileSends": { "message": "Datei-Sends" }, + "textSend": { + "message": "Text-Send" + }, "textSends": { "message": "Text-Sends" }, @@ -4559,5 +4614,158 @@ }, "authenticating": { "message": "Authentifizierung" + }, + "fillGeneratedPassword": { + "message": "Fill generated password", + "description": "Heading for the password generator within the inline menu" + }, + "passwordRegenerated": { + "message": "Password regenerated", + "description": "Notification message for when a password has been regenerated" + }, + "saveLoginToBitwarden": { + "message": "Save login to Bitwarden?", + "description": "Confirmation message for saving a login to Bitwarden" + }, + "spaceCharacterDescriptor": { + "message": "Space", + "description": "Represents the space key in screen reader content as a readable word" + }, + "tildeCharacterDescriptor": { + "message": "Tilde", + "description": "Represents the ~ key in screen reader content as a readable word" + }, + "backtickCharacterDescriptor": { + "message": "Backtick", + "description": "Represents the ` key in screen reader content as a readable word" + }, + "exclamationCharacterDescriptor": { + "message": "Exclamation mark", + "description": "Represents the ! key in screen reader content as a readable word" + }, + "atSignCharacterDescriptor": { + "message": "At sign", + "description": "Represents the @ key in screen reader content as a readable word" + }, + "hashSignCharacterDescriptor": { + "message": "Hash sign", + "description": "Represents the # key in screen reader content as a readable word" + }, + "dollarSignCharacterDescriptor": { + "message": "Dollar sign", + "description": "Represents the $ key in screen reader content as a readable word" + }, + "percentSignCharacterDescriptor": { + "message": "Percent sign", + "description": "Represents the % key in screen reader content as a readable word" + }, + "caretCharacterDescriptor": { + "message": "Caret", + "description": "Represents the ^ key in screen reader content as a readable word" + }, + "ampersandCharacterDescriptor": { + "message": "Ampersand", + "description": "Represents the & key in screen reader content as a readable word" + }, + "asteriskCharacterDescriptor": { + "message": "Asterisk", + "description": "Represents the * key in screen reader content as a readable word" + }, + "parenLeftCharacterDescriptor": { + "message": "Left parenthesis", + "description": "Represents the ( key in screen reader content as a readable word" + }, + "parenRightCharacterDescriptor": { + "message": "Right parenthesis", + "description": "Represents the ) key in screen reader content as a readable word" + }, + "hyphenCharacterDescriptor": { + "message": "Underscore", + "description": "Represents the _ key in screen reader content as a readable word" + }, + "underscoreCharacterDescriptor": { + "message": "Hyphen", + "description": "Represents the - key in screen reader content as a readable word" + }, + "plusCharacterDescriptor": { + "message": "Plus", + "description": "Represents the + key in screen reader content as a readable word" + }, + "equalsCharacterDescriptor": { + "message": "Equals", + "description": "Represents the = key in screen reader content as a readable word" + }, + "braceLeftCharacterDescriptor": { + "message": "Left brace", + "description": "Represents the { key in screen reader content as a readable word" + }, + "braceRightCharacterDescriptor": { + "message": "Right brace", + "description": "Represents the } key in screen reader content as a readable word" + }, + "bracketLeftCharacterDescriptor": { + "message": "Left bracket", + "description": "Represents the [ key in screen reader content as a readable word" + }, + "bracketRightCharacterDescriptor": { + "message": "Right bracket", + "description": "Represents the ] key in screen reader content as a readable word" + }, + "pipeCharacterDescriptor": { + "message": "Pipe", + "description": "Represents the | key in screen reader content as a readable word" + }, + "backSlashCharacterDescriptor": { + "message": "Back slash", + "description": "Represents the back slash key in screen reader content as a readable word" + }, + "colonCharacterDescriptor": { + "message": "Colon", + "description": "Represents the : key in screen reader content as a readable word" + }, + "semicolonCharacterDescriptor": { + "message": "Semicolon", + "description": "Represents the ; key in screen reader content as a readable word" + }, + "doubleQuoteCharacterDescriptor": { + "message": "Double quote", + "description": "Represents the double quote key in screen reader content as a readable word" + }, + "singleQuoteCharacterDescriptor": { + "message": "Single quote", + "description": "Represents the ' key in screen reader content as a readable word" + }, + "lessThanCharacterDescriptor": { + "message": "Less than", + "description": "Represents the < key in screen reader content as a readable word" + }, + "greaterThanCharacterDescriptor": { + "message": "Greater than", + "description": "Represents the > key in screen reader content as a readable word" + }, + "commaCharacterDescriptor": { + "message": "Comma", + "description": "Represents the , key in screen reader content as a readable word" + }, + "periodCharacterDescriptor": { + "message": "Period", + "description": "Represents the . key in screen reader content as a readable word" + }, + "questionCharacterDescriptor": { + "message": "Question mark", + "description": "Represents the ? key in screen reader content as a readable word" + }, + "forwardSlashCharacterDescriptor": { + "message": "Forward slash", + "description": "Represents the / key in screen reader content as a readable word" + }, + "lowercaseAriaLabel": { + "message": "Lowercase" + }, + "uppercaseAriaLabel": { + "message": "Uppercase" + }, + "generatedPassword": { + "message": "Generated password" } } diff --git a/apps/browser/src/_locales/el/messages.json b/apps/browser/src/_locales/el/messages.json index 3318e057024..3cbcf58ac7c 100644 --- a/apps/browser/src/_locales/el/messages.json +++ b/apps/browser/src/_locales/el/messages.json @@ -19,6 +19,18 @@ "createAccount": { "message": "Δημιουργία λογαριασμού" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" + }, "setAStrongPassword": { "message": "Ορίστε έναν ισχυρό κωδικό πρόσβασης" }, @@ -107,6 +119,9 @@ "copyPassword": { "message": "Αντιγραφή κωδικού πρόσβασης" }, + "copyPassphrase": { + "message": "Copy passphrase" + }, "copyNote": { "message": "Αντιγραφή σημείωσης" }, @@ -152,6 +167,10 @@ "copyNotes": { "message": "Αντιγραφή σημειώσεων" }, + "fill": { + "message": "Fill", + "description": "This string is used on the vault page to indicate autofilling. Horizontal space is limited in the interface here so try and keep translations as concise as possible." + }, "autoFill": { "message": "Αυτόματη συμπλήρωση" }, @@ -407,6 +426,9 @@ "generatePassword": { "message": "Δημιουργία κωδικού πρόσβασης" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "regeneratePassword": { "message": "Επαναδημιουργία κωδικού πρόσβασης" }, @@ -568,6 +590,15 @@ "launchWebsite": { "message": "Εκκίνηση ιστοσελίδας" }, + "launchWebsiteName": { + "message": "Launch website $ITEMNAME$", + "placeholders": { + "itemname": { + "content": "$1", + "example": "Secret item" + } + } + }, "website": { "message": "Ιστοσελίδα" }, @@ -814,6 +845,9 @@ "logIn": { "message": "Σύνδεση" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "restartRegistration": { "message": "Επανεκκίνηση εγγραφής" }, @@ -1389,6 +1423,10 @@ "baseUrl": { "message": "URL Διακομιστή" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "URL διακομιστή API" }, @@ -2464,8 +2502,8 @@ "message": "Προαιρετικά απαιτείται κωδικός πρόσβασης για τους χρήστες για να έχουν πρόσβαση σε αυτό το Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendPasswordDescV2": { - "message": "Να απαιτείται αυτός ο κωδικός πρόσβασης για την προβολή του Send.", + "sendPasswordDescV3": { + "message": "Add an optional password for recipients to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendNotesDesc": { @@ -2827,6 +2865,9 @@ "generateUsername": { "message": "Δημιουργία ονόματος χρήστη" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Τύπος ονόματος χρήστη" }, @@ -2867,6 +2908,14 @@ "forwardedEmailDesc": { "message": "Δημιουργήστε ένα alias email με μια εξωτερική υπηρεσία προώθησης." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ σφάλμα: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -4494,9 +4543,15 @@ "itemLocation": { "message": "Τοποθεσία Αντικειμένου" }, + "fileSend": { + "message": "Send αρχείου" + }, "fileSends": { "message": "Send αρχείων" }, + "textSend": { + "message": "Send κειμένου" + }, "textSends": { "message": "Send κειμένων" }, @@ -4559,5 +4614,158 @@ }, "authenticating": { "message": "Ταυτοποίηση" + }, + "fillGeneratedPassword": { + "message": "Fill generated password", + "description": "Heading for the password generator within the inline menu" + }, + "passwordRegenerated": { + "message": "Password regenerated", + "description": "Notification message for when a password has been regenerated" + }, + "saveLoginToBitwarden": { + "message": "Save login to Bitwarden?", + "description": "Confirmation message for saving a login to Bitwarden" + }, + "spaceCharacterDescriptor": { + "message": "Space", + "description": "Represents the space key in screen reader content as a readable word" + }, + "tildeCharacterDescriptor": { + "message": "Tilde", + "description": "Represents the ~ key in screen reader content as a readable word" + }, + "backtickCharacterDescriptor": { + "message": "Backtick", + "description": "Represents the ` key in screen reader content as a readable word" + }, + "exclamationCharacterDescriptor": { + "message": "Exclamation mark", + "description": "Represents the ! key in screen reader content as a readable word" + }, + "atSignCharacterDescriptor": { + "message": "At sign", + "description": "Represents the @ key in screen reader content as a readable word" + }, + "hashSignCharacterDescriptor": { + "message": "Hash sign", + "description": "Represents the # key in screen reader content as a readable word" + }, + "dollarSignCharacterDescriptor": { + "message": "Dollar sign", + "description": "Represents the $ key in screen reader content as a readable word" + }, + "percentSignCharacterDescriptor": { + "message": "Percent sign", + "description": "Represents the % key in screen reader content as a readable word" + }, + "caretCharacterDescriptor": { + "message": "Caret", + "description": "Represents the ^ key in screen reader content as a readable word" + }, + "ampersandCharacterDescriptor": { + "message": "Ampersand", + "description": "Represents the & key in screen reader content as a readable word" + }, + "asteriskCharacterDescriptor": { + "message": "Asterisk", + "description": "Represents the * key in screen reader content as a readable word" + }, + "parenLeftCharacterDescriptor": { + "message": "Left parenthesis", + "description": "Represents the ( key in screen reader content as a readable word" + }, + "parenRightCharacterDescriptor": { + "message": "Right parenthesis", + "description": "Represents the ) key in screen reader content as a readable word" + }, + "hyphenCharacterDescriptor": { + "message": "Underscore", + "description": "Represents the _ key in screen reader content as a readable word" + }, + "underscoreCharacterDescriptor": { + "message": "Hyphen", + "description": "Represents the - key in screen reader content as a readable word" + }, + "plusCharacterDescriptor": { + "message": "Plus", + "description": "Represents the + key in screen reader content as a readable word" + }, + "equalsCharacterDescriptor": { + "message": "Equals", + "description": "Represents the = key in screen reader content as a readable word" + }, + "braceLeftCharacterDescriptor": { + "message": "Left brace", + "description": "Represents the { key in screen reader content as a readable word" + }, + "braceRightCharacterDescriptor": { + "message": "Right brace", + "description": "Represents the } key in screen reader content as a readable word" + }, + "bracketLeftCharacterDescriptor": { + "message": "Left bracket", + "description": "Represents the [ key in screen reader content as a readable word" + }, + "bracketRightCharacterDescriptor": { + "message": "Right bracket", + "description": "Represents the ] key in screen reader content as a readable word" + }, + "pipeCharacterDescriptor": { + "message": "Pipe", + "description": "Represents the | key in screen reader content as a readable word" + }, + "backSlashCharacterDescriptor": { + "message": "Back slash", + "description": "Represents the back slash key in screen reader content as a readable word" + }, + "colonCharacterDescriptor": { + "message": "Colon", + "description": "Represents the : key in screen reader content as a readable word" + }, + "semicolonCharacterDescriptor": { + "message": "Semicolon", + "description": "Represents the ; key in screen reader content as a readable word" + }, + "doubleQuoteCharacterDescriptor": { + "message": "Double quote", + "description": "Represents the double quote key in screen reader content as a readable word" + }, + "singleQuoteCharacterDescriptor": { + "message": "Single quote", + "description": "Represents the ' key in screen reader content as a readable word" + }, + "lessThanCharacterDescriptor": { + "message": "Less than", + "description": "Represents the < key in screen reader content as a readable word" + }, + "greaterThanCharacterDescriptor": { + "message": "Greater than", + "description": "Represents the > key in screen reader content as a readable word" + }, + "commaCharacterDescriptor": { + "message": "Comma", + "description": "Represents the , key in screen reader content as a readable word" + }, + "periodCharacterDescriptor": { + "message": "Period", + "description": "Represents the . key in screen reader content as a readable word" + }, + "questionCharacterDescriptor": { + "message": "Question mark", + "description": "Represents the ? key in screen reader content as a readable word" + }, + "forwardSlashCharacterDescriptor": { + "message": "Forward slash", + "description": "Represents the / key in screen reader content as a readable word" + }, + "lowercaseAriaLabel": { + "message": "Lowercase" + }, + "uppercaseAriaLabel": { + "message": "Uppercase" + }, + "generatedPassword": { + "message": "Generated password" } } diff --git a/apps/browser/src/_locales/en/messages.json b/apps/browser/src/_locales/en/messages.json index e72daaa1717..b8c263652ed 100644 --- a/apps/browser/src/_locales/en/messages.json +++ b/apps/browser/src/_locales/en/messages.json @@ -19,6 +19,18 @@ "createAccount": { "message": "Create account" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" + }, "setAStrongPassword": { "message": "Set a strong password" }, @@ -155,6 +167,10 @@ "copyNotes": { "message": "Copy notes" }, + "fill":{ + "message": "Fill", + "description": "This string is used on the vault page to indicate autofilling. Horizontal space is limited in the interface here so try and keep translations as concise as possible." + }, "autoFill": { "message": "Autofill" }, @@ -574,6 +590,15 @@ "launchWebsite": { "message": "Launch website" }, + "launchWebsiteName": { + "message": "Launch website $ITEMNAME$", + "placeholders": { + "itemname": { + "content": "$1", + "example": "Secret item" + } + } + }, "website": { "message": "Website" }, @@ -820,6 +845,9 @@ "logIn": { "message": "Log in" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "restartRegistration": { "message": "Restart registration" }, @@ -4586,5 +4614,158 @@ }, "authenticating": { "message": "Authenticating" + }, + "fillGeneratedPassword": { + "message": "Fill generated password", + "description": "Heading for the password generator within the inline menu" + }, + "passwordRegenerated": { + "message": "Password regenerated", + "description": "Notification message for when a password has been regenerated" + }, + "saveLoginToBitwarden": { + "message": "Save login to Bitwarden?", + "description": "Confirmation message for saving a login to Bitwarden" + }, + "spaceCharacterDescriptor": { + "message": "Space", + "description": "Represents the space key in screen reader content as a readable word" + }, + "tildeCharacterDescriptor": { + "message": "Tilde", + "description": "Represents the ~ key in screen reader content as a readable word" + }, + "backtickCharacterDescriptor": { + "message": "Backtick", + "description": "Represents the ` key in screen reader content as a readable word" + }, + "exclamationCharacterDescriptor": { + "message": "Exclamation mark", + "description": "Represents the ! key in screen reader content as a readable word" + }, + "atSignCharacterDescriptor": { + "message": "At sign", + "description": "Represents the @ key in screen reader content as a readable word" + }, + "hashSignCharacterDescriptor": { + "message": "Hash sign", + "description": "Represents the # key in screen reader content as a readable word" + }, + "dollarSignCharacterDescriptor": { + "message": "Dollar sign", + "description": "Represents the $ key in screen reader content as a readable word" + }, + "percentSignCharacterDescriptor": { + "message": "Percent sign", + "description": "Represents the % key in screen reader content as a readable word" + }, + "caretCharacterDescriptor": { + "message": "Caret", + "description": "Represents the ^ key in screen reader content as a readable word" + }, + "ampersandCharacterDescriptor": { + "message": "Ampersand", + "description": "Represents the & key in screen reader content as a readable word" + }, + "asteriskCharacterDescriptor": { + "message": "Asterisk", + "description": "Represents the * key in screen reader content as a readable word" + }, + "parenLeftCharacterDescriptor": { + "message": "Left parenthesis", + "description": "Represents the ( key in screen reader content as a readable word" + }, + "parenRightCharacterDescriptor": { + "message": "Right parenthesis", + "description": "Represents the ) key in screen reader content as a readable word" + }, + "hyphenCharacterDescriptor": { + "message": "Underscore", + "description": "Represents the _ key in screen reader content as a readable word" + }, + "underscoreCharacterDescriptor": { + "message": "Hyphen", + "description": "Represents the - key in screen reader content as a readable word" + }, + "plusCharacterDescriptor": { + "message": "Plus", + "description": "Represents the + key in screen reader content as a readable word" + }, + "equalsCharacterDescriptor": { + "message": "Equals", + "description": "Represents the = key in screen reader content as a readable word" + }, + "braceLeftCharacterDescriptor": { + "message": "Left brace", + "description": "Represents the { key in screen reader content as a readable word" + }, + "braceRightCharacterDescriptor": { + "message": "Right brace", + "description": "Represents the } key in screen reader content as a readable word" + }, + "bracketLeftCharacterDescriptor": { + "message": "Left bracket", + "description": "Represents the [ key in screen reader content as a readable word" + }, + "bracketRightCharacterDescriptor": { + "message": "Right bracket", + "description": "Represents the ] key in screen reader content as a readable word" + }, + "pipeCharacterDescriptor": { + "message": "Pipe", + "description": "Represents the | key in screen reader content as a readable word" + }, + "backSlashCharacterDescriptor": { + "message": "Back slash", + "description": "Represents the back slash key in screen reader content as a readable word" + }, + "colonCharacterDescriptor": { + "message": "Colon", + "description": "Represents the : key in screen reader content as a readable word" + }, + "semicolonCharacterDescriptor": { + "message": "Semicolon", + "description": "Represents the ; key in screen reader content as a readable word" + }, + "doubleQuoteCharacterDescriptor": { + "message": "Double quote", + "description": "Represents the double quote key in screen reader content as a readable word" + }, + "singleQuoteCharacterDescriptor": { + "message": "Single quote", + "description": "Represents the ' key in screen reader content as a readable word" + }, + "lessThanCharacterDescriptor": { + "message": "Less than", + "description": "Represents the < key in screen reader content as a readable word" + }, + "greaterThanCharacterDescriptor": { + "message": "Greater than", + "description": "Represents the > key in screen reader content as a readable word" + }, + "commaCharacterDescriptor": { + "message": "Comma", + "description": "Represents the , key in screen reader content as a readable word" + }, + "periodCharacterDescriptor": { + "message": "Period", + "description": "Represents the . key in screen reader content as a readable word" + }, + "questionCharacterDescriptor": { + "message": "Question mark", + "description": "Represents the ? key in screen reader content as a readable word" + }, + "forwardSlashCharacterDescriptor": { + "message": "Forward slash", + "description": "Represents the / key in screen reader content as a readable word" + }, + "lowercaseAriaLabel": { + "message": "Lowercase" + }, + "uppercaseAriaLabel": { + "message": "Uppercase" + }, + "generatedPassword": { + "message": "Generated password" } } diff --git a/apps/browser/src/_locales/en_GB/messages.json b/apps/browser/src/_locales/en_GB/messages.json index 4d295f1c9c5..0aae94aee47 100644 --- a/apps/browser/src/_locales/en_GB/messages.json +++ b/apps/browser/src/_locales/en_GB/messages.json @@ -19,6 +19,18 @@ "createAccount": { "message": "Create account" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" + }, "setAStrongPassword": { "message": "Set a strong password" }, @@ -107,6 +119,9 @@ "copyPassword": { "message": "Copy password" }, + "copyPassphrase": { + "message": "Copy passphrase" + }, "copyNote": { "message": "Copy note" }, @@ -152,6 +167,10 @@ "copyNotes": { "message": "Copy notes" }, + "fill": { + "message": "Fill", + "description": "This string is used on the vault page to indicate autofilling. Horizontal space is limited in the interface here so try and keep translations as concise as possible." + }, "autoFill": { "message": "Auto-fill" }, @@ -407,6 +426,9 @@ "generatePassword": { "message": "Generate password" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "regeneratePassword": { "message": "Regenerate password" }, @@ -568,6 +590,15 @@ "launchWebsite": { "message": "Launch website" }, + "launchWebsiteName": { + "message": "Launch website $ITEMNAME$", + "placeholders": { + "itemname": { + "content": "$1", + "example": "Secret item" + } + } + }, "website": { "message": "Website" }, @@ -814,6 +845,9 @@ "logIn": { "message": "Log in" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "restartRegistration": { "message": "Restart registration" }, @@ -1389,6 +1423,10 @@ "baseUrl": { "message": "Server URL" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "API server URL" }, @@ -2464,8 +2502,8 @@ "message": "Optionally require a password for users to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendPasswordDescV2": { - "message": "Require this password to view the Send.", + "sendPasswordDescV3": { + "message": "Add an optional password for recipients to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendNotesDesc": { @@ -2827,6 +2865,9 @@ "generateUsername": { "message": "Generate username" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Username type" }, @@ -2867,6 +2908,14 @@ "forwardedEmailDesc": { "message": "Generate an email alias with an external forwarding service." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ error: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -4494,9 +4543,15 @@ "itemLocation": { "message": "Item Location" }, + "fileSend": { + "message": "File Send" + }, "fileSends": { "message": "File Sends" }, + "textSend": { + "message": "Text Send" + }, "textSends": { "message": "Text Sends" }, @@ -4559,5 +4614,158 @@ }, "authenticating": { "message": "Authenticating" + }, + "fillGeneratedPassword": { + "message": "Fill generated password", + "description": "Heading for the password generator within the inline menu" + }, + "passwordRegenerated": { + "message": "Password regenerated", + "description": "Notification message for when a password has been regenerated" + }, + "saveLoginToBitwarden": { + "message": "Save login to Bitwarden?", + "description": "Confirmation message for saving a login to Bitwarden" + }, + "spaceCharacterDescriptor": { + "message": "Space", + "description": "Represents the space key in screen reader content as a readable word" + }, + "tildeCharacterDescriptor": { + "message": "Tilde", + "description": "Represents the ~ key in screen reader content as a readable word" + }, + "backtickCharacterDescriptor": { + "message": "Backtick", + "description": "Represents the ` key in screen reader content as a readable word" + }, + "exclamationCharacterDescriptor": { + "message": "Exclamation mark", + "description": "Represents the ! key in screen reader content as a readable word" + }, + "atSignCharacterDescriptor": { + "message": "At sign", + "description": "Represents the @ key in screen reader content as a readable word" + }, + "hashSignCharacterDescriptor": { + "message": "Hash sign", + "description": "Represents the # key in screen reader content as a readable word" + }, + "dollarSignCharacterDescriptor": { + "message": "Dollar sign", + "description": "Represents the $ key in screen reader content as a readable word" + }, + "percentSignCharacterDescriptor": { + "message": "Percent sign", + "description": "Represents the % key in screen reader content as a readable word" + }, + "caretCharacterDescriptor": { + "message": "Caret", + "description": "Represents the ^ key in screen reader content as a readable word" + }, + "ampersandCharacterDescriptor": { + "message": "Ampersand", + "description": "Represents the & key in screen reader content as a readable word" + }, + "asteriskCharacterDescriptor": { + "message": "Asterisk", + "description": "Represents the * key in screen reader content as a readable word" + }, + "parenLeftCharacterDescriptor": { + "message": "Left parenthesis", + "description": "Represents the ( key in screen reader content as a readable word" + }, + "parenRightCharacterDescriptor": { + "message": "Right parenthesis", + "description": "Represents the ) key in screen reader content as a readable word" + }, + "hyphenCharacterDescriptor": { + "message": "Underscore", + "description": "Represents the _ key in screen reader content as a readable word" + }, + "underscoreCharacterDescriptor": { + "message": "Hyphen", + "description": "Represents the - key in screen reader content as a readable word" + }, + "plusCharacterDescriptor": { + "message": "Plus", + "description": "Represents the + key in screen reader content as a readable word" + }, + "equalsCharacterDescriptor": { + "message": "Equals", + "description": "Represents the = key in screen reader content as a readable word" + }, + "braceLeftCharacterDescriptor": { + "message": "Left brace", + "description": "Represents the { key in screen reader content as a readable word" + }, + "braceRightCharacterDescriptor": { + "message": "Right brace", + "description": "Represents the } key in screen reader content as a readable word" + }, + "bracketLeftCharacterDescriptor": { + "message": "Left bracket", + "description": "Represents the [ key in screen reader content as a readable word" + }, + "bracketRightCharacterDescriptor": { + "message": "Right bracket", + "description": "Represents the ] key in screen reader content as a readable word" + }, + "pipeCharacterDescriptor": { + "message": "Pipe", + "description": "Represents the | key in screen reader content as a readable word" + }, + "backSlashCharacterDescriptor": { + "message": "Back slash", + "description": "Represents the back slash key in screen reader content as a readable word" + }, + "colonCharacterDescriptor": { + "message": "Colon", + "description": "Represents the : key in screen reader content as a readable word" + }, + "semicolonCharacterDescriptor": { + "message": "Semicolon", + "description": "Represents the ; key in screen reader content as a readable word" + }, + "doubleQuoteCharacterDescriptor": { + "message": "Double quote", + "description": "Represents the double quote key in screen reader content as a readable word" + }, + "singleQuoteCharacterDescriptor": { + "message": "Single quote", + "description": "Represents the ' key in screen reader content as a readable word" + }, + "lessThanCharacterDescriptor": { + "message": "Less than", + "description": "Represents the < key in screen reader content as a readable word" + }, + "greaterThanCharacterDescriptor": { + "message": "Greater than", + "description": "Represents the > key in screen reader content as a readable word" + }, + "commaCharacterDescriptor": { + "message": "Comma", + "description": "Represents the , key in screen reader content as a readable word" + }, + "periodCharacterDescriptor": { + "message": "Full stop", + "description": "Represents the . key in screen reader content as a readable word" + }, + "questionCharacterDescriptor": { + "message": "Question mark", + "description": "Represents the ? key in screen reader content as a readable word" + }, + "forwardSlashCharacterDescriptor": { + "message": "Forward slash", + "description": "Represents the / key in screen reader content as a readable word" + }, + "lowercaseAriaLabel": { + "message": "Lowercase" + }, + "uppercaseAriaLabel": { + "message": "Uppercase" + }, + "generatedPassword": { + "message": "Generated password" } } diff --git a/apps/browser/src/_locales/en_IN/messages.json b/apps/browser/src/_locales/en_IN/messages.json index 6104f15f630..4eab6adf74a 100644 --- a/apps/browser/src/_locales/en_IN/messages.json +++ b/apps/browser/src/_locales/en_IN/messages.json @@ -19,6 +19,18 @@ "createAccount": { "message": "Create account" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" + }, "setAStrongPassword": { "message": "Set a strong password" }, @@ -107,6 +119,9 @@ "copyPassword": { "message": "Copy password" }, + "copyPassphrase": { + "message": "Copy passphrase" + }, "copyNote": { "message": "Copy note" }, @@ -152,6 +167,10 @@ "copyNotes": { "message": "Copy notes" }, + "fill": { + "message": "Fill", + "description": "This string is used on the vault page to indicate autofilling. Horizontal space is limited in the interface here so try and keep translations as concise as possible." + }, "autoFill": { "message": "Auto-fill" }, @@ -407,6 +426,9 @@ "generatePassword": { "message": "Generate password" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "regeneratePassword": { "message": "Regenerate password" }, @@ -568,6 +590,15 @@ "launchWebsite": { "message": "Launch website" }, + "launchWebsiteName": { + "message": "Launch website $ITEMNAME$", + "placeholders": { + "itemname": { + "content": "$1", + "example": "Secret item" + } + } + }, "website": { "message": "Website" }, @@ -814,6 +845,9 @@ "logIn": { "message": "Log in" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "restartRegistration": { "message": "Restart registration" }, @@ -1389,6 +1423,10 @@ "baseUrl": { "message": "Server URL" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "API server URL" }, @@ -2464,8 +2502,8 @@ "message": "Optionally require a password for users to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendPasswordDescV2": { - "message": "Require this password to view the Send.", + "sendPasswordDescV3": { + "message": "Add an optional password for recipients to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendNotesDesc": { @@ -2827,6 +2865,9 @@ "generateUsername": { "message": "Generate Username" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Username Type" }, @@ -2867,6 +2908,14 @@ "forwardedEmailDesc": { "message": "Generate an email alias with an external forwarding service." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ error: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -4494,9 +4543,15 @@ "itemLocation": { "message": "Item Location" }, + "fileSend": { + "message": "File Send" + }, "fileSends": { "message": "File Sends" }, + "textSend": { + "message": "Text Send" + }, "textSends": { "message": "Text Sends" }, @@ -4559,5 +4614,158 @@ }, "authenticating": { "message": "Authenticating" + }, + "fillGeneratedPassword": { + "message": "Fill generated password", + "description": "Heading for the password generator within the inline menu" + }, + "passwordRegenerated": { + "message": "Password regenerated", + "description": "Notification message for when a password has been regenerated" + }, + "saveLoginToBitwarden": { + "message": "Save login to Bitwarden?", + "description": "Confirmation message for saving a login to Bitwarden" + }, + "spaceCharacterDescriptor": { + "message": "Space", + "description": "Represents the space key in screen reader content as a readable word" + }, + "tildeCharacterDescriptor": { + "message": "Tilde", + "description": "Represents the ~ key in screen reader content as a readable word" + }, + "backtickCharacterDescriptor": { + "message": "Backtick", + "description": "Represents the ` key in screen reader content as a readable word" + }, + "exclamationCharacterDescriptor": { + "message": "Exclamation mark", + "description": "Represents the ! key in screen reader content as a readable word" + }, + "atSignCharacterDescriptor": { + "message": "At sign", + "description": "Represents the @ key in screen reader content as a readable word" + }, + "hashSignCharacterDescriptor": { + "message": "Hash sign", + "description": "Represents the # key in screen reader content as a readable word" + }, + "dollarSignCharacterDescriptor": { + "message": "Dollar sign", + "description": "Represents the $ key in screen reader content as a readable word" + }, + "percentSignCharacterDescriptor": { + "message": "Percent sign", + "description": "Represents the % key in screen reader content as a readable word" + }, + "caretCharacterDescriptor": { + "message": "Caret", + "description": "Represents the ^ key in screen reader content as a readable word" + }, + "ampersandCharacterDescriptor": { + "message": "Ampersand", + "description": "Represents the & key in screen reader content as a readable word" + }, + "asteriskCharacterDescriptor": { + "message": "Asterisk", + "description": "Represents the * key in screen reader content as a readable word" + }, + "parenLeftCharacterDescriptor": { + "message": "Left parenthesis", + "description": "Represents the ( key in screen reader content as a readable word" + }, + "parenRightCharacterDescriptor": { + "message": "Right parenthesis", + "description": "Represents the ) key in screen reader content as a readable word" + }, + "hyphenCharacterDescriptor": { + "message": "Underscore", + "description": "Represents the _ key in screen reader content as a readable word" + }, + "underscoreCharacterDescriptor": { + "message": "Hyphen", + "description": "Represents the - key in screen reader content as a readable word" + }, + "plusCharacterDescriptor": { + "message": "Plus", + "description": "Represents the + key in screen reader content as a readable word" + }, + "equalsCharacterDescriptor": { + "message": "Equals", + "description": "Represents the = key in screen reader content as a readable word" + }, + "braceLeftCharacterDescriptor": { + "message": "Left brace", + "description": "Represents the { key in screen reader content as a readable word" + }, + "braceRightCharacterDescriptor": { + "message": "Right brace", + "description": "Represents the } key in screen reader content as a readable word" + }, + "bracketLeftCharacterDescriptor": { + "message": "Left bracket", + "description": "Represents the [ key in screen reader content as a readable word" + }, + "bracketRightCharacterDescriptor": { + "message": "Right bracket", + "description": "Represents the ] key in screen reader content as a readable word" + }, + "pipeCharacterDescriptor": { + "message": "Pipe", + "description": "Represents the | key in screen reader content as a readable word" + }, + "backSlashCharacterDescriptor": { + "message": "Back slash", + "description": "Represents the back slash key in screen reader content as a readable word" + }, + "colonCharacterDescriptor": { + "message": "Colon", + "description": "Represents the : key in screen reader content as a readable word" + }, + "semicolonCharacterDescriptor": { + "message": "Semicolon", + "description": "Represents the ; key in screen reader content as a readable word" + }, + "doubleQuoteCharacterDescriptor": { + "message": "Double quote", + "description": "Represents the double quote key in screen reader content as a readable word" + }, + "singleQuoteCharacterDescriptor": { + "message": "Single quote", + "description": "Represents the ' key in screen reader content as a readable word" + }, + "lessThanCharacterDescriptor": { + "message": "Less than", + "description": "Represents the < key in screen reader content as a readable word" + }, + "greaterThanCharacterDescriptor": { + "message": "Greater than", + "description": "Represents the > key in screen reader content as a readable word" + }, + "commaCharacterDescriptor": { + "message": "Comma", + "description": "Represents the , key in screen reader content as a readable word" + }, + "periodCharacterDescriptor": { + "message": "Period", + "description": "Represents the . key in screen reader content as a readable word" + }, + "questionCharacterDescriptor": { + "message": "Question mark", + "description": "Represents the ? key in screen reader content as a readable word" + }, + "forwardSlashCharacterDescriptor": { + "message": "Forward slash", + "description": "Represents the / key in screen reader content as a readable word" + }, + "lowercaseAriaLabel": { + "message": "Lowercase" + }, + "uppercaseAriaLabel": { + "message": "Uppercase" + }, + "generatedPassword": { + "message": "Generated password" } } diff --git a/apps/browser/src/_locales/es/messages.json b/apps/browser/src/_locales/es/messages.json index 0eadf338669..28d63aca133 100644 --- a/apps/browser/src/_locales/es/messages.json +++ b/apps/browser/src/_locales/es/messages.json @@ -19,6 +19,18 @@ "createAccount": { "message": "Crear cuenta" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" + }, "setAStrongPassword": { "message": "Establece una contraseña fuerte" }, @@ -107,6 +119,9 @@ "copyPassword": { "message": "Copiar contraseña" }, + "copyPassphrase": { + "message": "Copy passphrase" + }, "copyNote": { "message": "Copiar nota" }, @@ -152,6 +167,10 @@ "copyNotes": { "message": "Copy notes" }, + "fill": { + "message": "Fill", + "description": "This string is used on the vault page to indicate autofilling. Horizontal space is limited in the interface here so try and keep translations as concise as possible." + }, "autoFill": { "message": "Autorrellenar" }, @@ -407,6 +426,9 @@ "generatePassword": { "message": "Generar contraseña" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "regeneratePassword": { "message": "Regenerar contraseña" }, @@ -568,6 +590,15 @@ "launchWebsite": { "message": "Iniciar página web" }, + "launchWebsiteName": { + "message": "Launch website $ITEMNAME$", + "placeholders": { + "itemname": { + "content": "$1", + "example": "Secret item" + } + } + }, "website": { "message": "Web" }, @@ -814,6 +845,9 @@ "logIn": { "message": "Acceder" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "restartRegistration": { "message": "Reiniciar registro" }, @@ -1389,6 +1423,10 @@ "baseUrl": { "message": "URL del servidor" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "URL del servidor de la API" }, @@ -2464,8 +2502,8 @@ "message": "Opcionalmente se requiere una contraseña para que los usuarios accedan a este Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendPasswordDescV2": { - "message": "Require this password to view the Send.", + "sendPasswordDescV3": { + "message": "Add an optional password for recipients to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendNotesDesc": { @@ -2827,6 +2865,9 @@ "generateUsername": { "message": "Generar nombre de usuario" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Tipo de nombre de usuario" }, @@ -2867,6 +2908,14 @@ "forwardedEmailDesc": { "message": "Genera un alias de correo electrónico con un servicio de reenvío externo." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ error: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -4494,9 +4543,15 @@ "itemLocation": { "message": "Ubicación del elemento" }, + "fileSend": { + "message": "File Send" + }, "fileSends": { "message": "File Sends" }, + "textSend": { + "message": "Text Send" + }, "textSends": { "message": "Text Sends" }, @@ -4559,5 +4614,158 @@ }, "authenticating": { "message": "Authenticating" + }, + "fillGeneratedPassword": { + "message": "Fill generated password", + "description": "Heading for the password generator within the inline menu" + }, + "passwordRegenerated": { + "message": "Password regenerated", + "description": "Notification message for when a password has been regenerated" + }, + "saveLoginToBitwarden": { + "message": "Save login to Bitwarden?", + "description": "Confirmation message for saving a login to Bitwarden" + }, + "spaceCharacterDescriptor": { + "message": "Space", + "description": "Represents the space key in screen reader content as a readable word" + }, + "tildeCharacterDescriptor": { + "message": "Tilde", + "description": "Represents the ~ key in screen reader content as a readable word" + }, + "backtickCharacterDescriptor": { + "message": "Backtick", + "description": "Represents the ` key in screen reader content as a readable word" + }, + "exclamationCharacterDescriptor": { + "message": "Exclamation mark", + "description": "Represents the ! key in screen reader content as a readable word" + }, + "atSignCharacterDescriptor": { + "message": "At sign", + "description": "Represents the @ key in screen reader content as a readable word" + }, + "hashSignCharacterDescriptor": { + "message": "Hash sign", + "description": "Represents the # key in screen reader content as a readable word" + }, + "dollarSignCharacterDescriptor": { + "message": "Dollar sign", + "description": "Represents the $ key in screen reader content as a readable word" + }, + "percentSignCharacterDescriptor": { + "message": "Percent sign", + "description": "Represents the % key in screen reader content as a readable word" + }, + "caretCharacterDescriptor": { + "message": "Caret", + "description": "Represents the ^ key in screen reader content as a readable word" + }, + "ampersandCharacterDescriptor": { + "message": "Ampersand", + "description": "Represents the & key in screen reader content as a readable word" + }, + "asteriskCharacterDescriptor": { + "message": "Asterisk", + "description": "Represents the * key in screen reader content as a readable word" + }, + "parenLeftCharacterDescriptor": { + "message": "Left parenthesis", + "description": "Represents the ( key in screen reader content as a readable word" + }, + "parenRightCharacterDescriptor": { + "message": "Right parenthesis", + "description": "Represents the ) key in screen reader content as a readable word" + }, + "hyphenCharacterDescriptor": { + "message": "Underscore", + "description": "Represents the _ key in screen reader content as a readable word" + }, + "underscoreCharacterDescriptor": { + "message": "Hyphen", + "description": "Represents the - key in screen reader content as a readable word" + }, + "plusCharacterDescriptor": { + "message": "Plus", + "description": "Represents the + key in screen reader content as a readable word" + }, + "equalsCharacterDescriptor": { + "message": "Equals", + "description": "Represents the = key in screen reader content as a readable word" + }, + "braceLeftCharacterDescriptor": { + "message": "Left brace", + "description": "Represents the { key in screen reader content as a readable word" + }, + "braceRightCharacterDescriptor": { + "message": "Right brace", + "description": "Represents the } key in screen reader content as a readable word" + }, + "bracketLeftCharacterDescriptor": { + "message": "Left bracket", + "description": "Represents the [ key in screen reader content as a readable word" + }, + "bracketRightCharacterDescriptor": { + "message": "Right bracket", + "description": "Represents the ] key in screen reader content as a readable word" + }, + "pipeCharacterDescriptor": { + "message": "Pipe", + "description": "Represents the | key in screen reader content as a readable word" + }, + "backSlashCharacterDescriptor": { + "message": "Back slash", + "description": "Represents the back slash key in screen reader content as a readable word" + }, + "colonCharacterDescriptor": { + "message": "Colon", + "description": "Represents the : key in screen reader content as a readable word" + }, + "semicolonCharacterDescriptor": { + "message": "Semicolon", + "description": "Represents the ; key in screen reader content as a readable word" + }, + "doubleQuoteCharacterDescriptor": { + "message": "Double quote", + "description": "Represents the double quote key in screen reader content as a readable word" + }, + "singleQuoteCharacterDescriptor": { + "message": "Single quote", + "description": "Represents the ' key in screen reader content as a readable word" + }, + "lessThanCharacterDescriptor": { + "message": "Less than", + "description": "Represents the < key in screen reader content as a readable word" + }, + "greaterThanCharacterDescriptor": { + "message": "Greater than", + "description": "Represents the > key in screen reader content as a readable word" + }, + "commaCharacterDescriptor": { + "message": "Comma", + "description": "Represents the , key in screen reader content as a readable word" + }, + "periodCharacterDescriptor": { + "message": "Period", + "description": "Represents the . key in screen reader content as a readable word" + }, + "questionCharacterDescriptor": { + "message": "Question mark", + "description": "Represents the ? key in screen reader content as a readable word" + }, + "forwardSlashCharacterDescriptor": { + "message": "Forward slash", + "description": "Represents the / key in screen reader content as a readable word" + }, + "lowercaseAriaLabel": { + "message": "Lowercase" + }, + "uppercaseAriaLabel": { + "message": "Uppercase" + }, + "generatedPassword": { + "message": "Generated password" } } diff --git a/apps/browser/src/_locales/et/messages.json b/apps/browser/src/_locales/et/messages.json index 83562adbb75..13b521c47a1 100644 --- a/apps/browser/src/_locales/et/messages.json +++ b/apps/browser/src/_locales/et/messages.json @@ -19,6 +19,18 @@ "createAccount": { "message": "Konto loomine" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" + }, "setAStrongPassword": { "message": "Määra tugev parool" }, @@ -107,6 +119,9 @@ "copyPassword": { "message": "Kopeeri parool" }, + "copyPassphrase": { + "message": "Copy passphrase" + }, "copyNote": { "message": "Kopeeri märkus" }, @@ -152,6 +167,10 @@ "copyNotes": { "message": "Copy notes" }, + "fill": { + "message": "Fill", + "description": "This string is used on the vault page to indicate autofilling. Horizontal space is limited in the interface here so try and keep translations as concise as possible." + }, "autoFill": { "message": "Automaatne täitmine" }, @@ -407,6 +426,9 @@ "generatePassword": { "message": "Loo parool" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "regeneratePassword": { "message": "Genereeri parool uuesti" }, @@ -568,6 +590,15 @@ "launchWebsite": { "message": "Ava Veebileht" }, + "launchWebsiteName": { + "message": "Launch website $ITEMNAME$", + "placeholders": { + "itemname": { + "content": "$1", + "example": "Secret item" + } + } + }, "website": { "message": "Veebileht" }, @@ -814,6 +845,9 @@ "logIn": { "message": "Logi sisse" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "restartRegistration": { "message": "Alusta registreerimist uuesti" }, @@ -1389,6 +1423,10 @@ "baseUrl": { "message": "Serveri URL" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "API serveri URL" }, @@ -2464,8 +2502,8 @@ "message": "Soovi korral nõua parooli, millega Sendile ligi pääseb.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendPasswordDescV2": { - "message": "Require this password to view the Send.", + "sendPasswordDescV3": { + "message": "Add an optional password for recipients to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendNotesDesc": { @@ -2827,6 +2865,9 @@ "generateUsername": { "message": "Genereeri kasutajanimi" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Kasutajanime tüüp" }, @@ -2867,6 +2908,14 @@ "forwardedEmailDesc": { "message": "Genereeri e-posti alias, kasutades selleks välist teenuspakkujat." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ error: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -4494,9 +4543,15 @@ "itemLocation": { "message": "Item Location" }, + "fileSend": { + "message": "File Send" + }, "fileSends": { "message": "File Sends" }, + "textSend": { + "message": "Text Send" + }, "textSends": { "message": "Text Sends" }, @@ -4559,5 +4614,158 @@ }, "authenticating": { "message": "Authenticating" + }, + "fillGeneratedPassword": { + "message": "Fill generated password", + "description": "Heading for the password generator within the inline menu" + }, + "passwordRegenerated": { + "message": "Password regenerated", + "description": "Notification message for when a password has been regenerated" + }, + "saveLoginToBitwarden": { + "message": "Save login to Bitwarden?", + "description": "Confirmation message for saving a login to Bitwarden" + }, + "spaceCharacterDescriptor": { + "message": "Space", + "description": "Represents the space key in screen reader content as a readable word" + }, + "tildeCharacterDescriptor": { + "message": "Tilde", + "description": "Represents the ~ key in screen reader content as a readable word" + }, + "backtickCharacterDescriptor": { + "message": "Backtick", + "description": "Represents the ` key in screen reader content as a readable word" + }, + "exclamationCharacterDescriptor": { + "message": "Exclamation mark", + "description": "Represents the ! key in screen reader content as a readable word" + }, + "atSignCharacterDescriptor": { + "message": "At sign", + "description": "Represents the @ key in screen reader content as a readable word" + }, + "hashSignCharacterDescriptor": { + "message": "Hash sign", + "description": "Represents the # key in screen reader content as a readable word" + }, + "dollarSignCharacterDescriptor": { + "message": "Dollar sign", + "description": "Represents the $ key in screen reader content as a readable word" + }, + "percentSignCharacterDescriptor": { + "message": "Percent sign", + "description": "Represents the % key in screen reader content as a readable word" + }, + "caretCharacterDescriptor": { + "message": "Caret", + "description": "Represents the ^ key in screen reader content as a readable word" + }, + "ampersandCharacterDescriptor": { + "message": "Ampersand", + "description": "Represents the & key in screen reader content as a readable word" + }, + "asteriskCharacterDescriptor": { + "message": "Asterisk", + "description": "Represents the * key in screen reader content as a readable word" + }, + "parenLeftCharacterDescriptor": { + "message": "Left parenthesis", + "description": "Represents the ( key in screen reader content as a readable word" + }, + "parenRightCharacterDescriptor": { + "message": "Right parenthesis", + "description": "Represents the ) key in screen reader content as a readable word" + }, + "hyphenCharacterDescriptor": { + "message": "Underscore", + "description": "Represents the _ key in screen reader content as a readable word" + }, + "underscoreCharacterDescriptor": { + "message": "Hyphen", + "description": "Represents the - key in screen reader content as a readable word" + }, + "plusCharacterDescriptor": { + "message": "Plus", + "description": "Represents the + key in screen reader content as a readable word" + }, + "equalsCharacterDescriptor": { + "message": "Equals", + "description": "Represents the = key in screen reader content as a readable word" + }, + "braceLeftCharacterDescriptor": { + "message": "Left brace", + "description": "Represents the { key in screen reader content as a readable word" + }, + "braceRightCharacterDescriptor": { + "message": "Right brace", + "description": "Represents the } key in screen reader content as a readable word" + }, + "bracketLeftCharacterDescriptor": { + "message": "Left bracket", + "description": "Represents the [ key in screen reader content as a readable word" + }, + "bracketRightCharacterDescriptor": { + "message": "Right bracket", + "description": "Represents the ] key in screen reader content as a readable word" + }, + "pipeCharacterDescriptor": { + "message": "Pipe", + "description": "Represents the | key in screen reader content as a readable word" + }, + "backSlashCharacterDescriptor": { + "message": "Back slash", + "description": "Represents the back slash key in screen reader content as a readable word" + }, + "colonCharacterDescriptor": { + "message": "Colon", + "description": "Represents the : key in screen reader content as a readable word" + }, + "semicolonCharacterDescriptor": { + "message": "Semicolon", + "description": "Represents the ; key in screen reader content as a readable word" + }, + "doubleQuoteCharacterDescriptor": { + "message": "Double quote", + "description": "Represents the double quote key in screen reader content as a readable word" + }, + "singleQuoteCharacterDescriptor": { + "message": "Single quote", + "description": "Represents the ' key in screen reader content as a readable word" + }, + "lessThanCharacterDescriptor": { + "message": "Less than", + "description": "Represents the < key in screen reader content as a readable word" + }, + "greaterThanCharacterDescriptor": { + "message": "Greater than", + "description": "Represents the > key in screen reader content as a readable word" + }, + "commaCharacterDescriptor": { + "message": "Comma", + "description": "Represents the , key in screen reader content as a readable word" + }, + "periodCharacterDescriptor": { + "message": "Period", + "description": "Represents the . key in screen reader content as a readable word" + }, + "questionCharacterDescriptor": { + "message": "Question mark", + "description": "Represents the ? key in screen reader content as a readable word" + }, + "forwardSlashCharacterDescriptor": { + "message": "Forward slash", + "description": "Represents the / key in screen reader content as a readable word" + }, + "lowercaseAriaLabel": { + "message": "Lowercase" + }, + "uppercaseAriaLabel": { + "message": "Uppercase" + }, + "generatedPassword": { + "message": "Generated password" } } diff --git a/apps/browser/src/_locales/eu/messages.json b/apps/browser/src/_locales/eu/messages.json index 39c1d323627..037873bbc5f 100644 --- a/apps/browser/src/_locales/eu/messages.json +++ b/apps/browser/src/_locales/eu/messages.json @@ -19,6 +19,18 @@ "createAccount": { "message": "Sortu kontua" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" + }, "setAStrongPassword": { "message": "Set a strong password" }, @@ -107,6 +119,9 @@ "copyPassword": { "message": "Kopiatu pasahitza" }, + "copyPassphrase": { + "message": "Copy passphrase" + }, "copyNote": { "message": "Kopiatu oharra" }, @@ -152,6 +167,10 @@ "copyNotes": { "message": "Copy notes" }, + "fill": { + "message": "Fill", + "description": "This string is used on the vault page to indicate autofilling. Horizontal space is limited in the interface here so try and keep translations as concise as possible." + }, "autoFill": { "message": "Auto-betetzea" }, @@ -407,6 +426,9 @@ "generatePassword": { "message": "Sortu pasahitza" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "regeneratePassword": { "message": "Berrezarri pasahitza" }, @@ -568,6 +590,15 @@ "launchWebsite": { "message": "Launch website" }, + "launchWebsiteName": { + "message": "Launch website $ITEMNAME$", + "placeholders": { + "itemname": { + "content": "$1", + "example": "Secret item" + } + } + }, "website": { "message": "Webgunea" }, @@ -814,6 +845,9 @@ "logIn": { "message": "Log in" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "restartRegistration": { "message": "Restart registration" }, @@ -1389,6 +1423,10 @@ "baseUrl": { "message": "Zerbitzariaren URL-a" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "API zerbitzariaren URL-a" }, @@ -2464,8 +2502,8 @@ "message": "Nahi izanez gero, pasahitza eskatu erabiltzaileak Send honetara sar daitezen.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendPasswordDescV2": { - "message": "Require this password to view the Send.", + "sendPasswordDescV3": { + "message": "Add an optional password for recipients to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendNotesDesc": { @@ -2827,6 +2865,9 @@ "generateUsername": { "message": "Sortu erabiltzaile izena" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Erabiltzaile izen mota" }, @@ -2867,6 +2908,14 @@ "forwardedEmailDesc": { "message": "Emaileko ezizen bat sortu kanpoko bidalketa zerbitzu batekin." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ error: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -4494,9 +4543,15 @@ "itemLocation": { "message": "Item Location" }, + "fileSend": { + "message": "File Send" + }, "fileSends": { "message": "File Sends" }, + "textSend": { + "message": "Text Send" + }, "textSends": { "message": "Text Sends" }, @@ -4559,5 +4614,158 @@ }, "authenticating": { "message": "Authenticating" + }, + "fillGeneratedPassword": { + "message": "Fill generated password", + "description": "Heading for the password generator within the inline menu" + }, + "passwordRegenerated": { + "message": "Password regenerated", + "description": "Notification message for when a password has been regenerated" + }, + "saveLoginToBitwarden": { + "message": "Save login to Bitwarden?", + "description": "Confirmation message for saving a login to Bitwarden" + }, + "spaceCharacterDescriptor": { + "message": "Space", + "description": "Represents the space key in screen reader content as a readable word" + }, + "tildeCharacterDescriptor": { + "message": "Tilde", + "description": "Represents the ~ key in screen reader content as a readable word" + }, + "backtickCharacterDescriptor": { + "message": "Backtick", + "description": "Represents the ` key in screen reader content as a readable word" + }, + "exclamationCharacterDescriptor": { + "message": "Exclamation mark", + "description": "Represents the ! key in screen reader content as a readable word" + }, + "atSignCharacterDescriptor": { + "message": "At sign", + "description": "Represents the @ key in screen reader content as a readable word" + }, + "hashSignCharacterDescriptor": { + "message": "Hash sign", + "description": "Represents the # key in screen reader content as a readable word" + }, + "dollarSignCharacterDescriptor": { + "message": "Dollar sign", + "description": "Represents the $ key in screen reader content as a readable word" + }, + "percentSignCharacterDescriptor": { + "message": "Percent sign", + "description": "Represents the % key in screen reader content as a readable word" + }, + "caretCharacterDescriptor": { + "message": "Caret", + "description": "Represents the ^ key in screen reader content as a readable word" + }, + "ampersandCharacterDescriptor": { + "message": "Ampersand", + "description": "Represents the & key in screen reader content as a readable word" + }, + "asteriskCharacterDescriptor": { + "message": "Asterisk", + "description": "Represents the * key in screen reader content as a readable word" + }, + "parenLeftCharacterDescriptor": { + "message": "Left parenthesis", + "description": "Represents the ( key in screen reader content as a readable word" + }, + "parenRightCharacterDescriptor": { + "message": "Right parenthesis", + "description": "Represents the ) key in screen reader content as a readable word" + }, + "hyphenCharacterDescriptor": { + "message": "Underscore", + "description": "Represents the _ key in screen reader content as a readable word" + }, + "underscoreCharacterDescriptor": { + "message": "Hyphen", + "description": "Represents the - key in screen reader content as a readable word" + }, + "plusCharacterDescriptor": { + "message": "Plus", + "description": "Represents the + key in screen reader content as a readable word" + }, + "equalsCharacterDescriptor": { + "message": "Equals", + "description": "Represents the = key in screen reader content as a readable word" + }, + "braceLeftCharacterDescriptor": { + "message": "Left brace", + "description": "Represents the { key in screen reader content as a readable word" + }, + "braceRightCharacterDescriptor": { + "message": "Right brace", + "description": "Represents the } key in screen reader content as a readable word" + }, + "bracketLeftCharacterDescriptor": { + "message": "Left bracket", + "description": "Represents the [ key in screen reader content as a readable word" + }, + "bracketRightCharacterDescriptor": { + "message": "Right bracket", + "description": "Represents the ] key in screen reader content as a readable word" + }, + "pipeCharacterDescriptor": { + "message": "Pipe", + "description": "Represents the | key in screen reader content as a readable word" + }, + "backSlashCharacterDescriptor": { + "message": "Back slash", + "description": "Represents the back slash key in screen reader content as a readable word" + }, + "colonCharacterDescriptor": { + "message": "Colon", + "description": "Represents the : key in screen reader content as a readable word" + }, + "semicolonCharacterDescriptor": { + "message": "Semicolon", + "description": "Represents the ; key in screen reader content as a readable word" + }, + "doubleQuoteCharacterDescriptor": { + "message": "Double quote", + "description": "Represents the double quote key in screen reader content as a readable word" + }, + "singleQuoteCharacterDescriptor": { + "message": "Single quote", + "description": "Represents the ' key in screen reader content as a readable word" + }, + "lessThanCharacterDescriptor": { + "message": "Less than", + "description": "Represents the < key in screen reader content as a readable word" + }, + "greaterThanCharacterDescriptor": { + "message": "Greater than", + "description": "Represents the > key in screen reader content as a readable word" + }, + "commaCharacterDescriptor": { + "message": "Comma", + "description": "Represents the , key in screen reader content as a readable word" + }, + "periodCharacterDescriptor": { + "message": "Period", + "description": "Represents the . key in screen reader content as a readable word" + }, + "questionCharacterDescriptor": { + "message": "Question mark", + "description": "Represents the ? key in screen reader content as a readable word" + }, + "forwardSlashCharacterDescriptor": { + "message": "Forward slash", + "description": "Represents the / key in screen reader content as a readable word" + }, + "lowercaseAriaLabel": { + "message": "Lowercase" + }, + "uppercaseAriaLabel": { + "message": "Uppercase" + }, + "generatedPassword": { + "message": "Generated password" } } diff --git a/apps/browser/src/_locales/fa/messages.json b/apps/browser/src/_locales/fa/messages.json index 3d7ea582616..42005f3f45c 100644 --- a/apps/browser/src/_locales/fa/messages.json +++ b/apps/browser/src/_locales/fa/messages.json @@ -19,6 +19,18 @@ "createAccount": { "message": "ایجاد حساب کاربری" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" + }, "setAStrongPassword": { "message": "تنظیم رمز عبور قوی" }, @@ -107,6 +119,9 @@ "copyPassword": { "message": "کپی کلمه عبور" }, + "copyPassphrase": { + "message": "Copy passphrase" + }, "copyNote": { "message": "کپی یادداشت" }, @@ -152,6 +167,10 @@ "copyNotes": { "message": "Copy notes" }, + "fill": { + "message": "Fill", + "description": "This string is used on the vault page to indicate autofilling. Horizontal space is limited in the interface here so try and keep translations as concise as possible." + }, "autoFill": { "message": "پر کردن خودکار" }, @@ -407,6 +426,9 @@ "generatePassword": { "message": "تولید کلمه عبور" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "regeneratePassword": { "message": "تولید مجدد کلمه عبور" }, @@ -568,6 +590,15 @@ "launchWebsite": { "message": "Launch website" }, + "launchWebsiteName": { + "message": "Launch website $ITEMNAME$", + "placeholders": { + "itemname": { + "content": "$1", + "example": "Secret item" + } + } + }, "website": { "message": "وب‌سایت" }, @@ -814,6 +845,9 @@ "logIn": { "message": "Log in" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "restartRegistration": { "message": "Restart registration" }, @@ -1389,6 +1423,10 @@ "baseUrl": { "message": "نشانی اینترنتی سرور" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "نشانی API سرور" }, @@ -2464,8 +2502,8 @@ "message": "به صورت اختیاری برای دسترسی کاربران به این ارسال به یک کلمه عبور نیاز دارید.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendPasswordDescV2": { - "message": "Require this password to view the Send.", + "sendPasswordDescV3": { + "message": "Add an optional password for recipients to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendNotesDesc": { @@ -2827,6 +2865,9 @@ "generateUsername": { "message": "ایجاد نام کاربری" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "نوع نام کاربری" }, @@ -2867,6 +2908,14 @@ "forwardedEmailDesc": { "message": "یک نام مستعار ایمیل با یک سرویس ارسال خارجی ایجاد کنید." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ error: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -4494,9 +4543,15 @@ "itemLocation": { "message": "Item Location" }, + "fileSend": { + "message": "File Send" + }, "fileSends": { "message": "File Sends" }, + "textSend": { + "message": "Text Send" + }, "textSends": { "message": "Text Sends" }, @@ -4559,5 +4614,158 @@ }, "authenticating": { "message": "Authenticating" + }, + "fillGeneratedPassword": { + "message": "Fill generated password", + "description": "Heading for the password generator within the inline menu" + }, + "passwordRegenerated": { + "message": "Password regenerated", + "description": "Notification message for when a password has been regenerated" + }, + "saveLoginToBitwarden": { + "message": "Save login to Bitwarden?", + "description": "Confirmation message for saving a login to Bitwarden" + }, + "spaceCharacterDescriptor": { + "message": "Space", + "description": "Represents the space key in screen reader content as a readable word" + }, + "tildeCharacterDescriptor": { + "message": "Tilde", + "description": "Represents the ~ key in screen reader content as a readable word" + }, + "backtickCharacterDescriptor": { + "message": "Backtick", + "description": "Represents the ` key in screen reader content as a readable word" + }, + "exclamationCharacterDescriptor": { + "message": "Exclamation mark", + "description": "Represents the ! key in screen reader content as a readable word" + }, + "atSignCharacterDescriptor": { + "message": "At sign", + "description": "Represents the @ key in screen reader content as a readable word" + }, + "hashSignCharacterDescriptor": { + "message": "Hash sign", + "description": "Represents the # key in screen reader content as a readable word" + }, + "dollarSignCharacterDescriptor": { + "message": "Dollar sign", + "description": "Represents the $ key in screen reader content as a readable word" + }, + "percentSignCharacterDescriptor": { + "message": "Percent sign", + "description": "Represents the % key in screen reader content as a readable word" + }, + "caretCharacterDescriptor": { + "message": "Caret", + "description": "Represents the ^ key in screen reader content as a readable word" + }, + "ampersandCharacterDescriptor": { + "message": "Ampersand", + "description": "Represents the & key in screen reader content as a readable word" + }, + "asteriskCharacterDescriptor": { + "message": "Asterisk", + "description": "Represents the * key in screen reader content as a readable word" + }, + "parenLeftCharacterDescriptor": { + "message": "Left parenthesis", + "description": "Represents the ( key in screen reader content as a readable word" + }, + "parenRightCharacterDescriptor": { + "message": "Right parenthesis", + "description": "Represents the ) key in screen reader content as a readable word" + }, + "hyphenCharacterDescriptor": { + "message": "Underscore", + "description": "Represents the _ key in screen reader content as a readable word" + }, + "underscoreCharacterDescriptor": { + "message": "Hyphen", + "description": "Represents the - key in screen reader content as a readable word" + }, + "plusCharacterDescriptor": { + "message": "Plus", + "description": "Represents the + key in screen reader content as a readable word" + }, + "equalsCharacterDescriptor": { + "message": "Equals", + "description": "Represents the = key in screen reader content as a readable word" + }, + "braceLeftCharacterDescriptor": { + "message": "Left brace", + "description": "Represents the { key in screen reader content as a readable word" + }, + "braceRightCharacterDescriptor": { + "message": "Right brace", + "description": "Represents the } key in screen reader content as a readable word" + }, + "bracketLeftCharacterDescriptor": { + "message": "Left bracket", + "description": "Represents the [ key in screen reader content as a readable word" + }, + "bracketRightCharacterDescriptor": { + "message": "Right bracket", + "description": "Represents the ] key in screen reader content as a readable word" + }, + "pipeCharacterDescriptor": { + "message": "Pipe", + "description": "Represents the | key in screen reader content as a readable word" + }, + "backSlashCharacterDescriptor": { + "message": "Back slash", + "description": "Represents the back slash key in screen reader content as a readable word" + }, + "colonCharacterDescriptor": { + "message": "Colon", + "description": "Represents the : key in screen reader content as a readable word" + }, + "semicolonCharacterDescriptor": { + "message": "Semicolon", + "description": "Represents the ; key in screen reader content as a readable word" + }, + "doubleQuoteCharacterDescriptor": { + "message": "Double quote", + "description": "Represents the double quote key in screen reader content as a readable word" + }, + "singleQuoteCharacterDescriptor": { + "message": "Single quote", + "description": "Represents the ' key in screen reader content as a readable word" + }, + "lessThanCharacterDescriptor": { + "message": "Less than", + "description": "Represents the < key in screen reader content as a readable word" + }, + "greaterThanCharacterDescriptor": { + "message": "Greater than", + "description": "Represents the > key in screen reader content as a readable word" + }, + "commaCharacterDescriptor": { + "message": "Comma", + "description": "Represents the , key in screen reader content as a readable word" + }, + "periodCharacterDescriptor": { + "message": "Period", + "description": "Represents the . key in screen reader content as a readable word" + }, + "questionCharacterDescriptor": { + "message": "Question mark", + "description": "Represents the ? key in screen reader content as a readable word" + }, + "forwardSlashCharacterDescriptor": { + "message": "Forward slash", + "description": "Represents the / key in screen reader content as a readable word" + }, + "lowercaseAriaLabel": { + "message": "Lowercase" + }, + "uppercaseAriaLabel": { + "message": "Uppercase" + }, + "generatedPassword": { + "message": "Generated password" } } diff --git a/apps/browser/src/_locales/fi/messages.json b/apps/browser/src/_locales/fi/messages.json index 6f325a2f6a9..dcd38ff6c7a 100644 --- a/apps/browser/src/_locales/fi/messages.json +++ b/apps/browser/src/_locales/fi/messages.json @@ -19,6 +19,18 @@ "createAccount": { "message": "Luo tili" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" + }, "setAStrongPassword": { "message": "Aseta vahva salasana" }, @@ -107,6 +119,9 @@ "copyPassword": { "message": "Kopioi salasana" }, + "copyPassphrase": { + "message": "Copy passphrase" + }, "copyNote": { "message": "Kopioi merkinnät" }, @@ -152,6 +167,10 @@ "copyNotes": { "message": "Kopioi merkinnät" }, + "fill": { + "message": "Fill", + "description": "This string is used on the vault page to indicate autofilling. Horizontal space is limited in the interface here so try and keep translations as concise as possible." + }, "autoFill": { "message": "Automaattitäyttö" }, @@ -407,6 +426,9 @@ "generatePassword": { "message": "Luo salasana" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "regeneratePassword": { "message": "Luo uusi salasana" }, @@ -568,6 +590,15 @@ "launchWebsite": { "message": "Avaa verkkosivusto" }, + "launchWebsiteName": { + "message": "Launch website $ITEMNAME$", + "placeholders": { + "itemname": { + "content": "$1", + "example": "Secret item" + } + } + }, "website": { "message": "Verkkosivusto" }, @@ -814,6 +845,9 @@ "logIn": { "message": "Kirjaudu" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "restartRegistration": { "message": "Aloita rekisteröityminen alusta" }, @@ -1389,6 +1423,10 @@ "baseUrl": { "message": "Palvelimen URL" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "API-palvelimen URL" }, @@ -2464,8 +2502,8 @@ "message": "Halutessasi, vaadi käyttäjiä syöttämään salasana Sendin avaamiseksi.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendPasswordDescV2": { - "message": "Vaadi tämä salasana Sendin avaukseen.", + "sendPasswordDescV3": { + "message": "Lisää valinnainen salasana vastaanottajille tähän Sendiin.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendNotesDesc": { @@ -2827,6 +2865,9 @@ "generateUsername": { "message": "Luo käyttäjätunnus" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Käyttäjätunnuksen tyyppi" }, @@ -2867,6 +2908,14 @@ "forwardedEmailDesc": { "message": "Luo sähköpostialias ulkoisella ohjauspalvelulla." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ -virhe: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -4494,9 +4543,15 @@ "itemLocation": { "message": "Kohteen sijainti" }, + "fileSend": { + "message": "Tiedosto-Send" + }, "fileSends": { "message": "Tiedosto-Sendit" }, + "textSend": { + "message": "Teksti-Send" + }, "textSends": { "message": "Teksti-Sendit" }, @@ -4559,5 +4614,158 @@ }, "authenticating": { "message": "Todennetaan" + }, + "fillGeneratedPassword": { + "message": "Fill generated password", + "description": "Heading for the password generator within the inline menu" + }, + "passwordRegenerated": { + "message": "Password regenerated", + "description": "Notification message for when a password has been regenerated" + }, + "saveLoginToBitwarden": { + "message": "Save login to Bitwarden?", + "description": "Confirmation message for saving a login to Bitwarden" + }, + "spaceCharacterDescriptor": { + "message": "Space", + "description": "Represents the space key in screen reader content as a readable word" + }, + "tildeCharacterDescriptor": { + "message": "Tilde", + "description": "Represents the ~ key in screen reader content as a readable word" + }, + "backtickCharacterDescriptor": { + "message": "Backtick", + "description": "Represents the ` key in screen reader content as a readable word" + }, + "exclamationCharacterDescriptor": { + "message": "Exclamation mark", + "description": "Represents the ! key in screen reader content as a readable word" + }, + "atSignCharacterDescriptor": { + "message": "At sign", + "description": "Represents the @ key in screen reader content as a readable word" + }, + "hashSignCharacterDescriptor": { + "message": "Hash sign", + "description": "Represents the # key in screen reader content as a readable word" + }, + "dollarSignCharacterDescriptor": { + "message": "Dollar sign", + "description": "Represents the $ key in screen reader content as a readable word" + }, + "percentSignCharacterDescriptor": { + "message": "Percent sign", + "description": "Represents the % key in screen reader content as a readable word" + }, + "caretCharacterDescriptor": { + "message": "Caret", + "description": "Represents the ^ key in screen reader content as a readable word" + }, + "ampersandCharacterDescriptor": { + "message": "Ampersand", + "description": "Represents the & key in screen reader content as a readable word" + }, + "asteriskCharacterDescriptor": { + "message": "Asterisk", + "description": "Represents the * key in screen reader content as a readable word" + }, + "parenLeftCharacterDescriptor": { + "message": "Left parenthesis", + "description": "Represents the ( key in screen reader content as a readable word" + }, + "parenRightCharacterDescriptor": { + "message": "Right parenthesis", + "description": "Represents the ) key in screen reader content as a readable word" + }, + "hyphenCharacterDescriptor": { + "message": "Underscore", + "description": "Represents the _ key in screen reader content as a readable word" + }, + "underscoreCharacterDescriptor": { + "message": "Hyphen", + "description": "Represents the - key in screen reader content as a readable word" + }, + "plusCharacterDescriptor": { + "message": "Plus", + "description": "Represents the + key in screen reader content as a readable word" + }, + "equalsCharacterDescriptor": { + "message": "Equals", + "description": "Represents the = key in screen reader content as a readable word" + }, + "braceLeftCharacterDescriptor": { + "message": "Left brace", + "description": "Represents the { key in screen reader content as a readable word" + }, + "braceRightCharacterDescriptor": { + "message": "Right brace", + "description": "Represents the } key in screen reader content as a readable word" + }, + "bracketLeftCharacterDescriptor": { + "message": "Left bracket", + "description": "Represents the [ key in screen reader content as a readable word" + }, + "bracketRightCharacterDescriptor": { + "message": "Right bracket", + "description": "Represents the ] key in screen reader content as a readable word" + }, + "pipeCharacterDescriptor": { + "message": "Pipe", + "description": "Represents the | key in screen reader content as a readable word" + }, + "backSlashCharacterDescriptor": { + "message": "Back slash", + "description": "Represents the back slash key in screen reader content as a readable word" + }, + "colonCharacterDescriptor": { + "message": "Colon", + "description": "Represents the : key in screen reader content as a readable word" + }, + "semicolonCharacterDescriptor": { + "message": "Semicolon", + "description": "Represents the ; key in screen reader content as a readable word" + }, + "doubleQuoteCharacterDescriptor": { + "message": "Double quote", + "description": "Represents the double quote key in screen reader content as a readable word" + }, + "singleQuoteCharacterDescriptor": { + "message": "Single quote", + "description": "Represents the ' key in screen reader content as a readable word" + }, + "lessThanCharacterDescriptor": { + "message": "Less than", + "description": "Represents the < key in screen reader content as a readable word" + }, + "greaterThanCharacterDescriptor": { + "message": "Greater than", + "description": "Represents the > key in screen reader content as a readable word" + }, + "commaCharacterDescriptor": { + "message": "Comma", + "description": "Represents the , key in screen reader content as a readable word" + }, + "periodCharacterDescriptor": { + "message": "Period", + "description": "Represents the . key in screen reader content as a readable word" + }, + "questionCharacterDescriptor": { + "message": "Question mark", + "description": "Represents the ? key in screen reader content as a readable word" + }, + "forwardSlashCharacterDescriptor": { + "message": "Forward slash", + "description": "Represents the / key in screen reader content as a readable word" + }, + "lowercaseAriaLabel": { + "message": "Lowercase" + }, + "uppercaseAriaLabel": { + "message": "Uppercase" + }, + "generatedPassword": { + "message": "Generated password" } } diff --git a/apps/browser/src/_locales/fil/messages.json b/apps/browser/src/_locales/fil/messages.json index 80e8361cb34..8a52a1a09c8 100644 --- a/apps/browser/src/_locales/fil/messages.json +++ b/apps/browser/src/_locales/fil/messages.json @@ -19,6 +19,18 @@ "createAccount": { "message": "Gumawa ng Account" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" + }, "setAStrongPassword": { "message": "Set a strong password" }, @@ -107,6 +119,9 @@ "copyPassword": { "message": "Kopyahin ang Password" }, + "copyPassphrase": { + "message": "Copy passphrase" + }, "copyNote": { "message": "Kopyahin ang Note" }, @@ -152,6 +167,10 @@ "copyNotes": { "message": "Copy notes" }, + "fill": { + "message": "Fill", + "description": "This string is used on the vault page to indicate autofilling. Horizontal space is limited in the interface here so try and keep translations as concise as possible." + }, "autoFill": { "message": "Auto-fill sa Filipino ay Awtomatikong Pagpuno" }, @@ -407,6 +426,9 @@ "generatePassword": { "message": "Magtatag ng Password" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "regeneratePassword": { "message": "Muling I-generate ang Password" }, @@ -568,6 +590,15 @@ "launchWebsite": { "message": "Launch website" }, + "launchWebsiteName": { + "message": "Launch website $ITEMNAME$", + "placeholders": { + "itemname": { + "content": "$1", + "example": "Secret item" + } + } + }, "website": { "message": "Website" }, @@ -814,6 +845,9 @@ "logIn": { "message": "Log in" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "restartRegistration": { "message": "Restart registration" }, @@ -1389,6 +1423,10 @@ "baseUrl": { "message": "URL ng Server" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "API Server URL" }, @@ -2464,8 +2502,8 @@ "message": "Maipapayo na mag-require ng password para sa mga user na ma-access ang Send na ito.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendPasswordDescV2": { - "message": "Require this password to view the Send.", + "sendPasswordDescV3": { + "message": "Add an optional password for recipients to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendNotesDesc": { @@ -2827,6 +2865,9 @@ "generateUsername": { "message": "Lumikha ng username" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Uri ng username" }, @@ -2867,6 +2908,14 @@ "forwardedEmailDesc": { "message": "Bumuo ng isang email alias na may isang panlabas na serbisyo sa pagpapasa." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ error: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -4494,9 +4543,15 @@ "itemLocation": { "message": "Item Location" }, + "fileSend": { + "message": "File Send" + }, "fileSends": { "message": "File Sends" }, + "textSend": { + "message": "Text Send" + }, "textSends": { "message": "Text Sends" }, @@ -4559,5 +4614,158 @@ }, "authenticating": { "message": "Authenticating" + }, + "fillGeneratedPassword": { + "message": "Fill generated password", + "description": "Heading for the password generator within the inline menu" + }, + "passwordRegenerated": { + "message": "Password regenerated", + "description": "Notification message for when a password has been regenerated" + }, + "saveLoginToBitwarden": { + "message": "Save login to Bitwarden?", + "description": "Confirmation message for saving a login to Bitwarden" + }, + "spaceCharacterDescriptor": { + "message": "Space", + "description": "Represents the space key in screen reader content as a readable word" + }, + "tildeCharacterDescriptor": { + "message": "Tilde", + "description": "Represents the ~ key in screen reader content as a readable word" + }, + "backtickCharacterDescriptor": { + "message": "Backtick", + "description": "Represents the ` key in screen reader content as a readable word" + }, + "exclamationCharacterDescriptor": { + "message": "Exclamation mark", + "description": "Represents the ! key in screen reader content as a readable word" + }, + "atSignCharacterDescriptor": { + "message": "At sign", + "description": "Represents the @ key in screen reader content as a readable word" + }, + "hashSignCharacterDescriptor": { + "message": "Hash sign", + "description": "Represents the # key in screen reader content as a readable word" + }, + "dollarSignCharacterDescriptor": { + "message": "Dollar sign", + "description": "Represents the $ key in screen reader content as a readable word" + }, + "percentSignCharacterDescriptor": { + "message": "Percent sign", + "description": "Represents the % key in screen reader content as a readable word" + }, + "caretCharacterDescriptor": { + "message": "Caret", + "description": "Represents the ^ key in screen reader content as a readable word" + }, + "ampersandCharacterDescriptor": { + "message": "Ampersand", + "description": "Represents the & key in screen reader content as a readable word" + }, + "asteriskCharacterDescriptor": { + "message": "Asterisk", + "description": "Represents the * key in screen reader content as a readable word" + }, + "parenLeftCharacterDescriptor": { + "message": "Left parenthesis", + "description": "Represents the ( key in screen reader content as a readable word" + }, + "parenRightCharacterDescriptor": { + "message": "Right parenthesis", + "description": "Represents the ) key in screen reader content as a readable word" + }, + "hyphenCharacterDescriptor": { + "message": "Underscore", + "description": "Represents the _ key in screen reader content as a readable word" + }, + "underscoreCharacterDescriptor": { + "message": "Hyphen", + "description": "Represents the - key in screen reader content as a readable word" + }, + "plusCharacterDescriptor": { + "message": "Plus", + "description": "Represents the + key in screen reader content as a readable word" + }, + "equalsCharacterDescriptor": { + "message": "Equals", + "description": "Represents the = key in screen reader content as a readable word" + }, + "braceLeftCharacterDescriptor": { + "message": "Left brace", + "description": "Represents the { key in screen reader content as a readable word" + }, + "braceRightCharacterDescriptor": { + "message": "Right brace", + "description": "Represents the } key in screen reader content as a readable word" + }, + "bracketLeftCharacterDescriptor": { + "message": "Left bracket", + "description": "Represents the [ key in screen reader content as a readable word" + }, + "bracketRightCharacterDescriptor": { + "message": "Right bracket", + "description": "Represents the ] key in screen reader content as a readable word" + }, + "pipeCharacterDescriptor": { + "message": "Pipe", + "description": "Represents the | key in screen reader content as a readable word" + }, + "backSlashCharacterDescriptor": { + "message": "Back slash", + "description": "Represents the back slash key in screen reader content as a readable word" + }, + "colonCharacterDescriptor": { + "message": "Colon", + "description": "Represents the : key in screen reader content as a readable word" + }, + "semicolonCharacterDescriptor": { + "message": "Semicolon", + "description": "Represents the ; key in screen reader content as a readable word" + }, + "doubleQuoteCharacterDescriptor": { + "message": "Double quote", + "description": "Represents the double quote key in screen reader content as a readable word" + }, + "singleQuoteCharacterDescriptor": { + "message": "Single quote", + "description": "Represents the ' key in screen reader content as a readable word" + }, + "lessThanCharacterDescriptor": { + "message": "Less than", + "description": "Represents the < key in screen reader content as a readable word" + }, + "greaterThanCharacterDescriptor": { + "message": "Greater than", + "description": "Represents the > key in screen reader content as a readable word" + }, + "commaCharacterDescriptor": { + "message": "Comma", + "description": "Represents the , key in screen reader content as a readable word" + }, + "periodCharacterDescriptor": { + "message": "Period", + "description": "Represents the . key in screen reader content as a readable word" + }, + "questionCharacterDescriptor": { + "message": "Question mark", + "description": "Represents the ? key in screen reader content as a readable word" + }, + "forwardSlashCharacterDescriptor": { + "message": "Forward slash", + "description": "Represents the / key in screen reader content as a readable word" + }, + "lowercaseAriaLabel": { + "message": "Lowercase" + }, + "uppercaseAriaLabel": { + "message": "Uppercase" + }, + "generatedPassword": { + "message": "Generated password" } } diff --git a/apps/browser/src/_locales/fr/messages.json b/apps/browser/src/_locales/fr/messages.json index aa18348cfda..f8e7dc41687 100644 --- a/apps/browser/src/_locales/fr/messages.json +++ b/apps/browser/src/_locales/fr/messages.json @@ -19,6 +19,18 @@ "createAccount": { "message": "Créer un compte" }, + "newToBitwarden": { + "message": "Nouveau sur Bitwarden ?" + }, + "logInWithPasskey": { + "message": "Se connecter avec une clé d'accès" + }, + "useSingleSignOn": { + "message": "Utiliser l'authentification unique" + }, + "welcomeBack": { + "message": "Content de vous revoir" + }, "setAStrongPassword": { "message": "Définir un mot de passe fort" }, @@ -107,6 +119,9 @@ "copyPassword": { "message": "Copier le mot de passe" }, + "copyPassphrase": { + "message": "Copy passphrase" + }, "copyNote": { "message": "Copier la note" }, @@ -152,6 +167,10 @@ "copyNotes": { "message": "Copier les notes" }, + "fill": { + "message": "Remplir", + "description": "This string is used on the vault page to indicate autofilling. Horizontal space is limited in the interface here so try and keep translations as concise as possible." + }, "autoFill": { "message": "Saisie automatique" }, @@ -407,6 +426,9 @@ "generatePassword": { "message": "Générer un mot de passe" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "regeneratePassword": { "message": "Régénérer un mot de passe" }, @@ -568,6 +590,15 @@ "launchWebsite": { "message": "Ouvrir le site web" }, + "launchWebsiteName": { + "message": "Lancer le site Web $ITEMNAME$", + "placeholders": { + "itemname": { + "content": "$1", + "example": "Secret item" + } + } + }, "website": { "message": "Site web" }, @@ -814,6 +845,9 @@ "logIn": { "message": "Se connecter" }, + "logInToBitwarden": { + "message": "Se connecter à Bitwarden" + }, "restartRegistration": { "message": "Redémarrer l'inscription" }, @@ -1389,6 +1423,10 @@ "baseUrl": { "message": "URL du serveur" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "URL du serveur de l'API" }, @@ -2464,8 +2502,8 @@ "message": "Vous pouvez, si vous le souhaitez, exiger un mot de passe pour accéder à ce Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendPasswordDescV2": { - "message": "Require this password to view the Send.", + "sendPasswordDescV3": { + "message": "Ajouter un mot de passe facultatif pour que les destinataires puissent accéder à ce Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendNotesDesc": { @@ -2827,6 +2865,9 @@ "generateUsername": { "message": "Générer un nom d'utilisateur" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Type de nom d'utilisateur" }, @@ -2867,6 +2908,14 @@ "forwardedEmailDesc": { "message": "Générer un alias de courriel avec un service de transfert externe." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "Erreur $SERVICENAME$ : $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -4494,9 +4543,15 @@ "itemLocation": { "message": "Emplacement de l'élément" }, + "fileSend": { + "message": "File Send" + }, "fileSends": { "message": "File Sends" }, + "textSend": { + "message": "Text Send" + }, "textSends": { "message": "Text Sends" }, @@ -4559,5 +4614,158 @@ }, "authenticating": { "message": "Authentification" + }, + "fillGeneratedPassword": { + "message": "Remplir le mot de passe généré", + "description": "Heading for the password generator within the inline menu" + }, + "passwordRegenerated": { + "message": "Mot de passe régénéré", + "description": "Notification message for when a password has been regenerated" + }, + "saveLoginToBitwarden": { + "message": "Enregistrer l'identifiant sur Bitwarden ?", + "description": "Confirmation message for saving a login to Bitwarden" + }, + "spaceCharacterDescriptor": { + "message": "Espace", + "description": "Represents the space key in screen reader content as a readable word" + }, + "tildeCharacterDescriptor": { + "message": "Tilde", + "description": "Represents the ~ key in screen reader content as a readable word" + }, + "backtickCharacterDescriptor": { + "message": "Backtick", + "description": "Represents the ` key in screen reader content as a readable word" + }, + "exclamationCharacterDescriptor": { + "message": "Point d'exclamation", + "description": "Represents the ! key in screen reader content as a readable word" + }, + "atSignCharacterDescriptor": { + "message": "At sign", + "description": "Represents the @ key in screen reader content as a readable word" + }, + "hashSignCharacterDescriptor": { + "message": "Hash sign", + "description": "Represents the # key in screen reader content as a readable word" + }, + "dollarSignCharacterDescriptor": { + "message": "Signe du dollar", + "description": "Represents the $ key in screen reader content as a readable word" + }, + "percentSignCharacterDescriptor": { + "message": "Percent sign", + "description": "Represents the % key in screen reader content as a readable word" + }, + "caretCharacterDescriptor": { + "message": "Caret", + "description": "Represents the ^ key in screen reader content as a readable word" + }, + "ampersandCharacterDescriptor": { + "message": "Ampersand", + "description": "Represents the & key in screen reader content as a readable word" + }, + "asteriskCharacterDescriptor": { + "message": "Astérisque", + "description": "Represents the * key in screen reader content as a readable word" + }, + "parenLeftCharacterDescriptor": { + "message": "Left parenthesis", + "description": "Represents the ( key in screen reader content as a readable word" + }, + "parenRightCharacterDescriptor": { + "message": "Right parenthesis", + "description": "Represents the ) key in screen reader content as a readable word" + }, + "hyphenCharacterDescriptor": { + "message": "Tiret bas", + "description": "Represents the _ key in screen reader content as a readable word" + }, + "underscoreCharacterDescriptor": { + "message": "Trait d'union", + "description": "Represents the - key in screen reader content as a readable word" + }, + "plusCharacterDescriptor": { + "message": "Plus", + "description": "Represents the + key in screen reader content as a readable word" + }, + "equalsCharacterDescriptor": { + "message": "Egal à", + "description": "Represents the = key in screen reader content as a readable word" + }, + "braceLeftCharacterDescriptor": { + "message": "Accolade gauche", + "description": "Represents the { key in screen reader content as a readable word" + }, + "braceRightCharacterDescriptor": { + "message": "Accolade droite", + "description": "Represents the } key in screen reader content as a readable word" + }, + "bracketLeftCharacterDescriptor": { + "message": "Crochet gauche", + "description": "Represents the [ key in screen reader content as a readable word" + }, + "bracketRightCharacterDescriptor": { + "message": "Crochet droit", + "description": "Represents the ] key in screen reader content as a readable word" + }, + "pipeCharacterDescriptor": { + "message": "Pipe", + "description": "Represents the | key in screen reader content as a readable word" + }, + "backSlashCharacterDescriptor": { + "message": "Barre oblique inverse", + "description": "Represents the back slash key in screen reader content as a readable word" + }, + "colonCharacterDescriptor": { + "message": "Colon", + "description": "Represents the : key in screen reader content as a readable word" + }, + "semicolonCharacterDescriptor": { + "message": "Point-virgule", + "description": "Represents the ; key in screen reader content as a readable word" + }, + "doubleQuoteCharacterDescriptor": { + "message": "Guillemets doubles", + "description": "Represents the double quote key in screen reader content as a readable word" + }, + "singleQuoteCharacterDescriptor": { + "message": "Guillemets simples", + "description": "Represents the ' key in screen reader content as a readable word" + }, + "lessThanCharacterDescriptor": { + "message": "Inférieure à", + "description": "Represents the < key in screen reader content as a readable word" + }, + "greaterThanCharacterDescriptor": { + "message": "Supérieur à", + "description": "Represents the > key in screen reader content as a readable word" + }, + "commaCharacterDescriptor": { + "message": "Virgule", + "description": "Represents the , key in screen reader content as a readable word" + }, + "periodCharacterDescriptor": { + "message": "Point", + "description": "Represents the . key in screen reader content as a readable word" + }, + "questionCharacterDescriptor": { + "message": "Point d'interrogation", + "description": "Represents the ? key in screen reader content as a readable word" + }, + "forwardSlashCharacterDescriptor": { + "message": "Barre oblique", + "description": "Represents the / key in screen reader content as a readable word" + }, + "lowercaseAriaLabel": { + "message": "Minuscule" + }, + "uppercaseAriaLabel": { + "message": "Majuscule" + }, + "generatedPassword": { + "message": "Mot de passe généré" } } diff --git a/apps/browser/src/_locales/gl/messages.json b/apps/browser/src/_locales/gl/messages.json index 139cb2ac4bf..dfaa32981dc 100644 --- a/apps/browser/src/_locales/gl/messages.json +++ b/apps/browser/src/_locales/gl/messages.json @@ -19,6 +19,18 @@ "createAccount": { "message": "Crea unha conta" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" + }, "setAStrongPassword": { "message": "Set a strong password" }, @@ -107,6 +119,9 @@ "copyPassword": { "message": "Copiar contrasinal" }, + "copyPassphrase": { + "message": "Copy passphrase" + }, "copyNote": { "message": "Copiar nota" }, @@ -152,6 +167,10 @@ "copyNotes": { "message": "Copy notes" }, + "fill": { + "message": "Fill", + "description": "This string is used on the vault page to indicate autofilling. Horizontal space is limited in the interface here so try and keep translations as concise as possible." + }, "autoFill": { "message": "Auto-encher" }, @@ -407,6 +426,9 @@ "generatePassword": { "message": "Xerar contrasinal" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "regeneratePassword": { "message": "Volver xerar contrasinal" }, @@ -568,6 +590,15 @@ "launchWebsite": { "message": "Launch website" }, + "launchWebsiteName": { + "message": "Launch website $ITEMNAME$", + "placeholders": { + "itemname": { + "content": "$1", + "example": "Secret item" + } + } + }, "website": { "message": "Sitio web" }, @@ -814,6 +845,9 @@ "logIn": { "message": "Log in" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "restartRegistration": { "message": "Restart registration" }, @@ -1389,6 +1423,10 @@ "baseUrl": { "message": "URL do servidor" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "URL do servidor da API" }, @@ -2464,8 +2502,8 @@ "message": "Optionally require a password for users to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendPasswordDescV2": { - "message": "Require this password to view the Send.", + "sendPasswordDescV3": { + "message": "Add an optional password for recipients to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendNotesDesc": { @@ -2827,6 +2865,9 @@ "generateUsername": { "message": "Generate username" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Username type" }, @@ -2867,6 +2908,14 @@ "forwardedEmailDesc": { "message": "Generate an email alias with an external forwarding service." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ error: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -4494,9 +4543,15 @@ "itemLocation": { "message": "Item Location" }, + "fileSend": { + "message": "File Send" + }, "fileSends": { "message": "File Sends" }, + "textSend": { + "message": "Text Send" + }, "textSends": { "message": "Text Sends" }, @@ -4559,5 +4614,158 @@ }, "authenticating": { "message": "Authenticating" + }, + "fillGeneratedPassword": { + "message": "Fill generated password", + "description": "Heading for the password generator within the inline menu" + }, + "passwordRegenerated": { + "message": "Password regenerated", + "description": "Notification message for when a password has been regenerated" + }, + "saveLoginToBitwarden": { + "message": "Save login to Bitwarden?", + "description": "Confirmation message for saving a login to Bitwarden" + }, + "spaceCharacterDescriptor": { + "message": "Space", + "description": "Represents the space key in screen reader content as a readable word" + }, + "tildeCharacterDescriptor": { + "message": "Tilde", + "description": "Represents the ~ key in screen reader content as a readable word" + }, + "backtickCharacterDescriptor": { + "message": "Backtick", + "description": "Represents the ` key in screen reader content as a readable word" + }, + "exclamationCharacterDescriptor": { + "message": "Exclamation mark", + "description": "Represents the ! key in screen reader content as a readable word" + }, + "atSignCharacterDescriptor": { + "message": "At sign", + "description": "Represents the @ key in screen reader content as a readable word" + }, + "hashSignCharacterDescriptor": { + "message": "Hash sign", + "description": "Represents the # key in screen reader content as a readable word" + }, + "dollarSignCharacterDescriptor": { + "message": "Dollar sign", + "description": "Represents the $ key in screen reader content as a readable word" + }, + "percentSignCharacterDescriptor": { + "message": "Percent sign", + "description": "Represents the % key in screen reader content as a readable word" + }, + "caretCharacterDescriptor": { + "message": "Caret", + "description": "Represents the ^ key in screen reader content as a readable word" + }, + "ampersandCharacterDescriptor": { + "message": "Ampersand", + "description": "Represents the & key in screen reader content as a readable word" + }, + "asteriskCharacterDescriptor": { + "message": "Asterisk", + "description": "Represents the * key in screen reader content as a readable word" + }, + "parenLeftCharacterDescriptor": { + "message": "Left parenthesis", + "description": "Represents the ( key in screen reader content as a readable word" + }, + "parenRightCharacterDescriptor": { + "message": "Right parenthesis", + "description": "Represents the ) key in screen reader content as a readable word" + }, + "hyphenCharacterDescriptor": { + "message": "Underscore", + "description": "Represents the _ key in screen reader content as a readable word" + }, + "underscoreCharacterDescriptor": { + "message": "Hyphen", + "description": "Represents the - key in screen reader content as a readable word" + }, + "plusCharacterDescriptor": { + "message": "Plus", + "description": "Represents the + key in screen reader content as a readable word" + }, + "equalsCharacterDescriptor": { + "message": "Equals", + "description": "Represents the = key in screen reader content as a readable word" + }, + "braceLeftCharacterDescriptor": { + "message": "Left brace", + "description": "Represents the { key in screen reader content as a readable word" + }, + "braceRightCharacterDescriptor": { + "message": "Right brace", + "description": "Represents the } key in screen reader content as a readable word" + }, + "bracketLeftCharacterDescriptor": { + "message": "Left bracket", + "description": "Represents the [ key in screen reader content as a readable word" + }, + "bracketRightCharacterDescriptor": { + "message": "Right bracket", + "description": "Represents the ] key in screen reader content as a readable word" + }, + "pipeCharacterDescriptor": { + "message": "Pipe", + "description": "Represents the | key in screen reader content as a readable word" + }, + "backSlashCharacterDescriptor": { + "message": "Back slash", + "description": "Represents the back slash key in screen reader content as a readable word" + }, + "colonCharacterDescriptor": { + "message": "Colon", + "description": "Represents the : key in screen reader content as a readable word" + }, + "semicolonCharacterDescriptor": { + "message": "Semicolon", + "description": "Represents the ; key in screen reader content as a readable word" + }, + "doubleQuoteCharacterDescriptor": { + "message": "Double quote", + "description": "Represents the double quote key in screen reader content as a readable word" + }, + "singleQuoteCharacterDescriptor": { + "message": "Single quote", + "description": "Represents the ' key in screen reader content as a readable word" + }, + "lessThanCharacterDescriptor": { + "message": "Less than", + "description": "Represents the < key in screen reader content as a readable word" + }, + "greaterThanCharacterDescriptor": { + "message": "Greater than", + "description": "Represents the > key in screen reader content as a readable word" + }, + "commaCharacterDescriptor": { + "message": "Comma", + "description": "Represents the , key in screen reader content as a readable word" + }, + "periodCharacterDescriptor": { + "message": "Period", + "description": "Represents the . key in screen reader content as a readable word" + }, + "questionCharacterDescriptor": { + "message": "Question mark", + "description": "Represents the ? key in screen reader content as a readable word" + }, + "forwardSlashCharacterDescriptor": { + "message": "Forward slash", + "description": "Represents the / key in screen reader content as a readable word" + }, + "lowercaseAriaLabel": { + "message": "Lowercase" + }, + "uppercaseAriaLabel": { + "message": "Uppercase" + }, + "generatedPassword": { + "message": "Generated password" } } diff --git a/apps/browser/src/_locales/he/messages.json b/apps/browser/src/_locales/he/messages.json index 8a97a47966f..a991d57b488 100644 --- a/apps/browser/src/_locales/he/messages.json +++ b/apps/browser/src/_locales/he/messages.json @@ -19,6 +19,18 @@ "createAccount": { "message": "צור חשבון" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" + }, "setAStrongPassword": { "message": "Set a strong password" }, @@ -107,6 +119,9 @@ "copyPassword": { "message": "העתק סיסמה" }, + "copyPassphrase": { + "message": "Copy passphrase" + }, "copyNote": { "message": "העתק פתק" }, @@ -152,6 +167,10 @@ "copyNotes": { "message": "Copy notes" }, + "fill": { + "message": "Fill", + "description": "This string is used on the vault page to indicate autofilling. Horizontal space is limited in the interface here so try and keep translations as concise as possible." + }, "autoFill": { "message": "השלמה אוטומטית" }, @@ -407,6 +426,9 @@ "generatePassword": { "message": "צור סיסמה" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "regeneratePassword": { "message": "צור סיסמה חדשה" }, @@ -568,6 +590,15 @@ "launchWebsite": { "message": "Launch website" }, + "launchWebsiteName": { + "message": "Launch website $ITEMNAME$", + "placeholders": { + "itemname": { + "content": "$1", + "example": "Secret item" + } + } + }, "website": { "message": "אתר" }, @@ -814,6 +845,9 @@ "logIn": { "message": "Log in" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "restartRegistration": { "message": "Restart registration" }, @@ -1389,6 +1423,10 @@ "baseUrl": { "message": "כתובת שרת" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "כתובת שרת הAPI" }, @@ -2464,8 +2502,8 @@ "message": "Optionally require a password for users to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendPasswordDescV2": { - "message": "Require this password to view the Send.", + "sendPasswordDescV3": { + "message": "Add an optional password for recipients to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendNotesDesc": { @@ -2827,6 +2865,9 @@ "generateUsername": { "message": "Generate username" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "סוג שם משתמש" }, @@ -2867,6 +2908,14 @@ "forwardedEmailDesc": { "message": "יצירת כינוי דוא״ל עם שירות העברה חיצוני." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ error: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -4494,9 +4543,15 @@ "itemLocation": { "message": "Item Location" }, + "fileSend": { + "message": "File Send" + }, "fileSends": { "message": "File Sends" }, + "textSend": { + "message": "Text Send" + }, "textSends": { "message": "Text Sends" }, @@ -4559,5 +4614,158 @@ }, "authenticating": { "message": "Authenticating" + }, + "fillGeneratedPassword": { + "message": "Fill generated password", + "description": "Heading for the password generator within the inline menu" + }, + "passwordRegenerated": { + "message": "Password regenerated", + "description": "Notification message for when a password has been regenerated" + }, + "saveLoginToBitwarden": { + "message": "Save login to Bitwarden?", + "description": "Confirmation message for saving a login to Bitwarden" + }, + "spaceCharacterDescriptor": { + "message": "Space", + "description": "Represents the space key in screen reader content as a readable word" + }, + "tildeCharacterDescriptor": { + "message": "Tilde", + "description": "Represents the ~ key in screen reader content as a readable word" + }, + "backtickCharacterDescriptor": { + "message": "Backtick", + "description": "Represents the ` key in screen reader content as a readable word" + }, + "exclamationCharacterDescriptor": { + "message": "Exclamation mark", + "description": "Represents the ! key in screen reader content as a readable word" + }, + "atSignCharacterDescriptor": { + "message": "At sign", + "description": "Represents the @ key in screen reader content as a readable word" + }, + "hashSignCharacterDescriptor": { + "message": "Hash sign", + "description": "Represents the # key in screen reader content as a readable word" + }, + "dollarSignCharacterDescriptor": { + "message": "Dollar sign", + "description": "Represents the $ key in screen reader content as a readable word" + }, + "percentSignCharacterDescriptor": { + "message": "Percent sign", + "description": "Represents the % key in screen reader content as a readable word" + }, + "caretCharacterDescriptor": { + "message": "Caret", + "description": "Represents the ^ key in screen reader content as a readable word" + }, + "ampersandCharacterDescriptor": { + "message": "Ampersand", + "description": "Represents the & key in screen reader content as a readable word" + }, + "asteriskCharacterDescriptor": { + "message": "Asterisk", + "description": "Represents the * key in screen reader content as a readable word" + }, + "parenLeftCharacterDescriptor": { + "message": "Left parenthesis", + "description": "Represents the ( key in screen reader content as a readable word" + }, + "parenRightCharacterDescriptor": { + "message": "Right parenthesis", + "description": "Represents the ) key in screen reader content as a readable word" + }, + "hyphenCharacterDescriptor": { + "message": "Underscore", + "description": "Represents the _ key in screen reader content as a readable word" + }, + "underscoreCharacterDescriptor": { + "message": "Hyphen", + "description": "Represents the - key in screen reader content as a readable word" + }, + "plusCharacterDescriptor": { + "message": "Plus", + "description": "Represents the + key in screen reader content as a readable word" + }, + "equalsCharacterDescriptor": { + "message": "Equals", + "description": "Represents the = key in screen reader content as a readable word" + }, + "braceLeftCharacterDescriptor": { + "message": "Left brace", + "description": "Represents the { key in screen reader content as a readable word" + }, + "braceRightCharacterDescriptor": { + "message": "Right brace", + "description": "Represents the } key in screen reader content as a readable word" + }, + "bracketLeftCharacterDescriptor": { + "message": "Left bracket", + "description": "Represents the [ key in screen reader content as a readable word" + }, + "bracketRightCharacterDescriptor": { + "message": "Right bracket", + "description": "Represents the ] key in screen reader content as a readable word" + }, + "pipeCharacterDescriptor": { + "message": "Pipe", + "description": "Represents the | key in screen reader content as a readable word" + }, + "backSlashCharacterDescriptor": { + "message": "Back slash", + "description": "Represents the back slash key in screen reader content as a readable word" + }, + "colonCharacterDescriptor": { + "message": "Colon", + "description": "Represents the : key in screen reader content as a readable word" + }, + "semicolonCharacterDescriptor": { + "message": "Semicolon", + "description": "Represents the ; key in screen reader content as a readable word" + }, + "doubleQuoteCharacterDescriptor": { + "message": "Double quote", + "description": "Represents the double quote key in screen reader content as a readable word" + }, + "singleQuoteCharacterDescriptor": { + "message": "Single quote", + "description": "Represents the ' key in screen reader content as a readable word" + }, + "lessThanCharacterDescriptor": { + "message": "Less than", + "description": "Represents the < key in screen reader content as a readable word" + }, + "greaterThanCharacterDescriptor": { + "message": "Greater than", + "description": "Represents the > key in screen reader content as a readable word" + }, + "commaCharacterDescriptor": { + "message": "Comma", + "description": "Represents the , key in screen reader content as a readable word" + }, + "periodCharacterDescriptor": { + "message": "Period", + "description": "Represents the . key in screen reader content as a readable word" + }, + "questionCharacterDescriptor": { + "message": "Question mark", + "description": "Represents the ? key in screen reader content as a readable word" + }, + "forwardSlashCharacterDescriptor": { + "message": "Forward slash", + "description": "Represents the / key in screen reader content as a readable word" + }, + "lowercaseAriaLabel": { + "message": "Lowercase" + }, + "uppercaseAriaLabel": { + "message": "Uppercase" + }, + "generatedPassword": { + "message": "Generated password" } } diff --git a/apps/browser/src/_locales/hi/messages.json b/apps/browser/src/_locales/hi/messages.json index 8d4d1521988..85d99becefc 100644 --- a/apps/browser/src/_locales/hi/messages.json +++ b/apps/browser/src/_locales/hi/messages.json @@ -19,6 +19,18 @@ "createAccount": { "message": "Create Account" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" + }, "setAStrongPassword": { "message": "मजबूत पासवर्ड सेट करें" }, @@ -107,6 +119,9 @@ "copyPassword": { "message": "Copy Password" }, + "copyPassphrase": { + "message": "Copy passphrase" + }, "copyNote": { "message": "Copy Note" }, @@ -152,6 +167,10 @@ "copyNotes": { "message": "Copy notes" }, + "fill": { + "message": "Fill", + "description": "This string is used on the vault page to indicate autofilling. Horizontal space is limited in the interface here so try and keep translations as concise as possible." + }, "autoFill": { "message": "स्वत:भरण" }, @@ -407,6 +426,9 @@ "generatePassword": { "message": "Generate Password" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "regeneratePassword": { "message": "Regenerate Password" }, @@ -568,6 +590,15 @@ "launchWebsite": { "message": "Launch website" }, + "launchWebsiteName": { + "message": "Launch website $ITEMNAME$", + "placeholders": { + "itemname": { + "content": "$1", + "example": "Secret item" + } + } + }, "website": { "message": "वेबसाइट" }, @@ -814,6 +845,9 @@ "logIn": { "message": "Log in" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "restartRegistration": { "message": "Restart registration" }, @@ -1389,6 +1423,10 @@ "baseUrl": { "message": "सर्वर URL" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "API Server URL" }, @@ -2464,8 +2502,8 @@ "message": "वैकल्पिक रूप से उपयोगकर्ताओं को इस सेंड तक पहुंचने के लिए पासवर्ड की आवश्यकता होगी।", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendPasswordDescV2": { - "message": "Require this password to view the Send.", + "sendPasswordDescV3": { + "message": "Add an optional password for recipients to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendNotesDesc": { @@ -2827,6 +2865,9 @@ "generateUsername": { "message": "उपयोगकर्ता नाम बनाएँ" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Username type" }, @@ -2867,6 +2908,14 @@ "forwardedEmailDesc": { "message": "Generate an email alias with an external forwarding service." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ error: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -4494,9 +4543,15 @@ "itemLocation": { "message": "Item Location" }, + "fileSend": { + "message": "File Send" + }, "fileSends": { "message": "File Sends" }, + "textSend": { + "message": "Text Send" + }, "textSends": { "message": "Text Sends" }, @@ -4559,5 +4614,158 @@ }, "authenticating": { "message": "Authenticating" + }, + "fillGeneratedPassword": { + "message": "Fill generated password", + "description": "Heading for the password generator within the inline menu" + }, + "passwordRegenerated": { + "message": "Password regenerated", + "description": "Notification message for when a password has been regenerated" + }, + "saveLoginToBitwarden": { + "message": "Save login to Bitwarden?", + "description": "Confirmation message for saving a login to Bitwarden" + }, + "spaceCharacterDescriptor": { + "message": "Space", + "description": "Represents the space key in screen reader content as a readable word" + }, + "tildeCharacterDescriptor": { + "message": "Tilde", + "description": "Represents the ~ key in screen reader content as a readable word" + }, + "backtickCharacterDescriptor": { + "message": "Backtick", + "description": "Represents the ` key in screen reader content as a readable word" + }, + "exclamationCharacterDescriptor": { + "message": "Exclamation mark", + "description": "Represents the ! key in screen reader content as a readable word" + }, + "atSignCharacterDescriptor": { + "message": "At sign", + "description": "Represents the @ key in screen reader content as a readable word" + }, + "hashSignCharacterDescriptor": { + "message": "Hash sign", + "description": "Represents the # key in screen reader content as a readable word" + }, + "dollarSignCharacterDescriptor": { + "message": "Dollar sign", + "description": "Represents the $ key in screen reader content as a readable word" + }, + "percentSignCharacterDescriptor": { + "message": "Percent sign", + "description": "Represents the % key in screen reader content as a readable word" + }, + "caretCharacterDescriptor": { + "message": "Caret", + "description": "Represents the ^ key in screen reader content as a readable word" + }, + "ampersandCharacterDescriptor": { + "message": "Ampersand", + "description": "Represents the & key in screen reader content as a readable word" + }, + "asteriskCharacterDescriptor": { + "message": "Asterisk", + "description": "Represents the * key in screen reader content as a readable word" + }, + "parenLeftCharacterDescriptor": { + "message": "Left parenthesis", + "description": "Represents the ( key in screen reader content as a readable word" + }, + "parenRightCharacterDescriptor": { + "message": "Right parenthesis", + "description": "Represents the ) key in screen reader content as a readable word" + }, + "hyphenCharacterDescriptor": { + "message": "Underscore", + "description": "Represents the _ key in screen reader content as a readable word" + }, + "underscoreCharacterDescriptor": { + "message": "Hyphen", + "description": "Represents the - key in screen reader content as a readable word" + }, + "plusCharacterDescriptor": { + "message": "Plus", + "description": "Represents the + key in screen reader content as a readable word" + }, + "equalsCharacterDescriptor": { + "message": "Equals", + "description": "Represents the = key in screen reader content as a readable word" + }, + "braceLeftCharacterDescriptor": { + "message": "Left brace", + "description": "Represents the { key in screen reader content as a readable word" + }, + "braceRightCharacterDescriptor": { + "message": "Right brace", + "description": "Represents the } key in screen reader content as a readable word" + }, + "bracketLeftCharacterDescriptor": { + "message": "Left bracket", + "description": "Represents the [ key in screen reader content as a readable word" + }, + "bracketRightCharacterDescriptor": { + "message": "Right bracket", + "description": "Represents the ] key in screen reader content as a readable word" + }, + "pipeCharacterDescriptor": { + "message": "Pipe", + "description": "Represents the | key in screen reader content as a readable word" + }, + "backSlashCharacterDescriptor": { + "message": "Back slash", + "description": "Represents the back slash key in screen reader content as a readable word" + }, + "colonCharacterDescriptor": { + "message": "Colon", + "description": "Represents the : key in screen reader content as a readable word" + }, + "semicolonCharacterDescriptor": { + "message": "Semicolon", + "description": "Represents the ; key in screen reader content as a readable word" + }, + "doubleQuoteCharacterDescriptor": { + "message": "Double quote", + "description": "Represents the double quote key in screen reader content as a readable word" + }, + "singleQuoteCharacterDescriptor": { + "message": "Single quote", + "description": "Represents the ' key in screen reader content as a readable word" + }, + "lessThanCharacterDescriptor": { + "message": "Less than", + "description": "Represents the < key in screen reader content as a readable word" + }, + "greaterThanCharacterDescriptor": { + "message": "Greater than", + "description": "Represents the > key in screen reader content as a readable word" + }, + "commaCharacterDescriptor": { + "message": "Comma", + "description": "Represents the , key in screen reader content as a readable word" + }, + "periodCharacterDescriptor": { + "message": "Period", + "description": "Represents the . key in screen reader content as a readable word" + }, + "questionCharacterDescriptor": { + "message": "Question mark", + "description": "Represents the ? key in screen reader content as a readable word" + }, + "forwardSlashCharacterDescriptor": { + "message": "Forward slash", + "description": "Represents the / key in screen reader content as a readable word" + }, + "lowercaseAriaLabel": { + "message": "Lowercase" + }, + "uppercaseAriaLabel": { + "message": "Uppercase" + }, + "generatedPassword": { + "message": "Generated password" } } diff --git a/apps/browser/src/_locales/hr/messages.json b/apps/browser/src/_locales/hr/messages.json index 4767362352a..d23fed9cda3 100644 --- a/apps/browser/src/_locales/hr/messages.json +++ b/apps/browser/src/_locales/hr/messages.json @@ -19,6 +19,18 @@ "createAccount": { "message": "Stvori račun" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" + }, "setAStrongPassword": { "message": "Postavi jaku lozinku" }, @@ -107,6 +119,9 @@ "copyPassword": { "message": "Kopiraj lozinku" }, + "copyPassphrase": { + "message": "Copy passphrase" + }, "copyNote": { "message": "Kopiraj bilješku" }, @@ -152,6 +167,10 @@ "copyNotes": { "message": "Kopiraj bilješke" }, + "fill": { + "message": "Fill", + "description": "This string is used on the vault page to indicate autofilling. Horizontal space is limited in the interface here so try and keep translations as concise as possible." + }, "autoFill": { "message": "Auto-ispuna" }, @@ -407,6 +426,9 @@ "generatePassword": { "message": "Generiraj lozinku" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "regeneratePassword": { "message": "Ponovno generiraj lozinku" }, @@ -568,6 +590,15 @@ "launchWebsite": { "message": "Pokreni web stranicu" }, + "launchWebsiteName": { + "message": "Launch website $ITEMNAME$", + "placeholders": { + "itemname": { + "content": "$1", + "example": "Secret item" + } + } + }, "website": { "message": "Web stranica" }, @@ -814,6 +845,9 @@ "logIn": { "message": "Prijavi se" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "restartRegistration": { "message": "Ponovno pokreni registraciju" }, @@ -1389,6 +1423,10 @@ "baseUrl": { "message": "URL poslužitelja" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "URL API poslužitelja" }, @@ -2464,8 +2502,8 @@ "message": "Neobavezno zahtijevaj korisnika lozinku za pristup ovom Sendu.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendPasswordDescV2": { - "message": "Zahtijevaj lozinku za pregled Senda.", + "sendPasswordDescV3": { + "message": "Add an optional password for recipients to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendNotesDesc": { @@ -2827,6 +2865,9 @@ "generateUsername": { "message": "Generiraj korisničko ime" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Tip korisničkog imena" }, @@ -2867,6 +2908,14 @@ "forwardedEmailDesc": { "message": "Generiraj pseudonim e-pošte s vanjskom uslugom prosljeđivanja." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ greška: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -4494,9 +4543,15 @@ "itemLocation": { "message": "Lokacija stavke" }, + "fileSend": { + "message": "File Send" + }, "fileSends": { "message": "Send datoteke" }, + "textSend": { + "message": "Text Send" + }, "textSends": { "message": "Send tekstovi" }, @@ -4559,5 +4614,158 @@ }, "authenticating": { "message": "Autentifikacija" + }, + "fillGeneratedPassword": { + "message": "Fill generated password", + "description": "Heading for the password generator within the inline menu" + }, + "passwordRegenerated": { + "message": "Password regenerated", + "description": "Notification message for when a password has been regenerated" + }, + "saveLoginToBitwarden": { + "message": "Save login to Bitwarden?", + "description": "Confirmation message for saving a login to Bitwarden" + }, + "spaceCharacterDescriptor": { + "message": "Space", + "description": "Represents the space key in screen reader content as a readable word" + }, + "tildeCharacterDescriptor": { + "message": "Tilde", + "description": "Represents the ~ key in screen reader content as a readable word" + }, + "backtickCharacterDescriptor": { + "message": "Backtick", + "description": "Represents the ` key in screen reader content as a readable word" + }, + "exclamationCharacterDescriptor": { + "message": "Exclamation mark", + "description": "Represents the ! key in screen reader content as a readable word" + }, + "atSignCharacterDescriptor": { + "message": "At sign", + "description": "Represents the @ key in screen reader content as a readable word" + }, + "hashSignCharacterDescriptor": { + "message": "Hash sign", + "description": "Represents the # key in screen reader content as a readable word" + }, + "dollarSignCharacterDescriptor": { + "message": "Dollar sign", + "description": "Represents the $ key in screen reader content as a readable word" + }, + "percentSignCharacterDescriptor": { + "message": "Percent sign", + "description": "Represents the % key in screen reader content as a readable word" + }, + "caretCharacterDescriptor": { + "message": "Caret", + "description": "Represents the ^ key in screen reader content as a readable word" + }, + "ampersandCharacterDescriptor": { + "message": "Ampersand", + "description": "Represents the & key in screen reader content as a readable word" + }, + "asteriskCharacterDescriptor": { + "message": "Asterisk", + "description": "Represents the * key in screen reader content as a readable word" + }, + "parenLeftCharacterDescriptor": { + "message": "Left parenthesis", + "description": "Represents the ( key in screen reader content as a readable word" + }, + "parenRightCharacterDescriptor": { + "message": "Right parenthesis", + "description": "Represents the ) key in screen reader content as a readable word" + }, + "hyphenCharacterDescriptor": { + "message": "Underscore", + "description": "Represents the _ key in screen reader content as a readable word" + }, + "underscoreCharacterDescriptor": { + "message": "Hyphen", + "description": "Represents the - key in screen reader content as a readable word" + }, + "plusCharacterDescriptor": { + "message": "Plus", + "description": "Represents the + key in screen reader content as a readable word" + }, + "equalsCharacterDescriptor": { + "message": "Equals", + "description": "Represents the = key in screen reader content as a readable word" + }, + "braceLeftCharacterDescriptor": { + "message": "Left brace", + "description": "Represents the { key in screen reader content as a readable word" + }, + "braceRightCharacterDescriptor": { + "message": "Right brace", + "description": "Represents the } key in screen reader content as a readable word" + }, + "bracketLeftCharacterDescriptor": { + "message": "Left bracket", + "description": "Represents the [ key in screen reader content as a readable word" + }, + "bracketRightCharacterDescriptor": { + "message": "Right bracket", + "description": "Represents the ] key in screen reader content as a readable word" + }, + "pipeCharacterDescriptor": { + "message": "Pipe", + "description": "Represents the | key in screen reader content as a readable word" + }, + "backSlashCharacterDescriptor": { + "message": "Back slash", + "description": "Represents the back slash key in screen reader content as a readable word" + }, + "colonCharacterDescriptor": { + "message": "Colon", + "description": "Represents the : key in screen reader content as a readable word" + }, + "semicolonCharacterDescriptor": { + "message": "Semicolon", + "description": "Represents the ; key in screen reader content as a readable word" + }, + "doubleQuoteCharacterDescriptor": { + "message": "Double quote", + "description": "Represents the double quote key in screen reader content as a readable word" + }, + "singleQuoteCharacterDescriptor": { + "message": "Single quote", + "description": "Represents the ' key in screen reader content as a readable word" + }, + "lessThanCharacterDescriptor": { + "message": "Less than", + "description": "Represents the < key in screen reader content as a readable word" + }, + "greaterThanCharacterDescriptor": { + "message": "Greater than", + "description": "Represents the > key in screen reader content as a readable word" + }, + "commaCharacterDescriptor": { + "message": "Comma", + "description": "Represents the , key in screen reader content as a readable word" + }, + "periodCharacterDescriptor": { + "message": "Period", + "description": "Represents the . key in screen reader content as a readable word" + }, + "questionCharacterDescriptor": { + "message": "Question mark", + "description": "Represents the ? key in screen reader content as a readable word" + }, + "forwardSlashCharacterDescriptor": { + "message": "Forward slash", + "description": "Represents the / key in screen reader content as a readable word" + }, + "lowercaseAriaLabel": { + "message": "Lowercase" + }, + "uppercaseAriaLabel": { + "message": "Uppercase" + }, + "generatedPassword": { + "message": "Generated password" } } diff --git a/apps/browser/src/_locales/hu/messages.json b/apps/browser/src/_locales/hu/messages.json index ef6ba5a859e..2582fc75153 100644 --- a/apps/browser/src/_locales/hu/messages.json +++ b/apps/browser/src/_locales/hu/messages.json @@ -19,6 +19,18 @@ "createAccount": { "message": "Fiók létrehozása" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" + }, "setAStrongPassword": { "message": "Erős jelszó beállítása" }, @@ -107,6 +119,9 @@ "copyPassword": { "message": "Jelszó másolása" }, + "copyPassphrase": { + "message": "Jelmondat másolása" + }, "copyNote": { "message": "Jegyzet másolása" }, @@ -152,6 +167,10 @@ "copyNotes": { "message": "Jegyzet másolása" }, + "fill": { + "message": "Kitöltés", + "description": "This string is used on the vault page to indicate autofilling. Horizontal space is limited in the interface here so try and keep translations as concise as possible." + }, "autoFill": { "message": "Automatikus kitöltés" }, @@ -407,6 +426,9 @@ "generatePassword": { "message": "Jelszó generálása" }, + "generatePassphrase": { + "message": "Jelmondat generálás" + }, "regeneratePassword": { "message": "Jelszó újragenerálása" }, @@ -568,6 +590,15 @@ "launchWebsite": { "message": "Webhely indítása" }, + "launchWebsiteName": { + "message": "$ITEMNAME$ webhely elindítása", + "placeholders": { + "itemname": { + "content": "$1", + "example": "Secret item" + } + } + }, "website": { "message": "Weboldal" }, @@ -814,6 +845,9 @@ "logIn": { "message": "Log in" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "restartRegistration": { "message": "Restart registration" }, @@ -1389,6 +1423,10 @@ "baseUrl": { "message": "Szerver URL" }, + "selfHostBaseUrl": { + "message": "Saját üzemeltetésű szerver webcím", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "API szerver webcím" }, @@ -2464,8 +2502,8 @@ "message": "Opcionálisan megadhatunk egy jelszót a felhasználók számára a Küldés eléréséhez. ", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendPasswordDescV2": { - "message": "Jelszó szükséges a Send elem megtekintéséhez.", + "sendPasswordDescV3": { + "message": "Adjunk meg egy opcionális jelszót a címzetteknek a Send eléréséhez.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendNotesDesc": { @@ -2827,6 +2865,9 @@ "generateUsername": { "message": "Felhasználónév generálása" }, + "generateEmail": { + "message": "Email generálása" + }, "usernameType": { "message": "Felhasználónév típusa" }, @@ -2867,6 +2908,14 @@ "forwardedEmailDesc": { "message": "Email álnév generálása külső továbbító szolgáltatással." }, + "forwarderDomainName": { + "message": "Email tartomány", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Válasszunk a kiválasztott szolgáltatás által támogatott tartományt.", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ hiba: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -4494,9 +4543,15 @@ "itemLocation": { "message": "Elem helyek" }, + "fileSend": { + "message": "Fájl típusú Send" + }, "fileSends": { "message": "Fájl küldés" }, + "textSend": { + "message": "Szöveg típusú Send" + }, "textSends": { "message": "Szöveg küldés" }, @@ -4559,5 +4614,158 @@ }, "authenticating": { "message": "Authenticating" + }, + "fillGeneratedPassword": { + "message": "Fill generated password", + "description": "Heading for the password generator within the inline menu" + }, + "passwordRegenerated": { + "message": "Password regenerated", + "description": "Notification message for when a password has been regenerated" + }, + "saveLoginToBitwarden": { + "message": "Save login to Bitwarden?", + "description": "Confirmation message for saving a login to Bitwarden" + }, + "spaceCharacterDescriptor": { + "message": "Space", + "description": "Represents the space key in screen reader content as a readable word" + }, + "tildeCharacterDescriptor": { + "message": "Tilde", + "description": "Represents the ~ key in screen reader content as a readable word" + }, + "backtickCharacterDescriptor": { + "message": "Backtick", + "description": "Represents the ` key in screen reader content as a readable word" + }, + "exclamationCharacterDescriptor": { + "message": "Exclamation mark", + "description": "Represents the ! key in screen reader content as a readable word" + }, + "atSignCharacterDescriptor": { + "message": "At sign", + "description": "Represents the @ key in screen reader content as a readable word" + }, + "hashSignCharacterDescriptor": { + "message": "Hash sign", + "description": "Represents the # key in screen reader content as a readable word" + }, + "dollarSignCharacterDescriptor": { + "message": "Dollar sign", + "description": "Represents the $ key in screen reader content as a readable word" + }, + "percentSignCharacterDescriptor": { + "message": "Percent sign", + "description": "Represents the % key in screen reader content as a readable word" + }, + "caretCharacterDescriptor": { + "message": "Caret", + "description": "Represents the ^ key in screen reader content as a readable word" + }, + "ampersandCharacterDescriptor": { + "message": "Ampersand", + "description": "Represents the & key in screen reader content as a readable word" + }, + "asteriskCharacterDescriptor": { + "message": "Asterisk", + "description": "Represents the * key in screen reader content as a readable word" + }, + "parenLeftCharacterDescriptor": { + "message": "Left parenthesis", + "description": "Represents the ( key in screen reader content as a readable word" + }, + "parenRightCharacterDescriptor": { + "message": "Right parenthesis", + "description": "Represents the ) key in screen reader content as a readable word" + }, + "hyphenCharacterDescriptor": { + "message": "Underscore", + "description": "Represents the _ key in screen reader content as a readable word" + }, + "underscoreCharacterDescriptor": { + "message": "Hyphen", + "description": "Represents the - key in screen reader content as a readable word" + }, + "plusCharacterDescriptor": { + "message": "Plus", + "description": "Represents the + key in screen reader content as a readable word" + }, + "equalsCharacterDescriptor": { + "message": "Equals", + "description": "Represents the = key in screen reader content as a readable word" + }, + "braceLeftCharacterDescriptor": { + "message": "Left brace", + "description": "Represents the { key in screen reader content as a readable word" + }, + "braceRightCharacterDescriptor": { + "message": "Right brace", + "description": "Represents the } key in screen reader content as a readable word" + }, + "bracketLeftCharacterDescriptor": { + "message": "Left bracket", + "description": "Represents the [ key in screen reader content as a readable word" + }, + "bracketRightCharacterDescriptor": { + "message": "Right bracket", + "description": "Represents the ] key in screen reader content as a readable word" + }, + "pipeCharacterDescriptor": { + "message": "Pipe", + "description": "Represents the | key in screen reader content as a readable word" + }, + "backSlashCharacterDescriptor": { + "message": "Back slash", + "description": "Represents the back slash key in screen reader content as a readable word" + }, + "colonCharacterDescriptor": { + "message": "Colon", + "description": "Represents the : key in screen reader content as a readable word" + }, + "semicolonCharacterDescriptor": { + "message": "Semicolon", + "description": "Represents the ; key in screen reader content as a readable word" + }, + "doubleQuoteCharacterDescriptor": { + "message": "Double quote", + "description": "Represents the double quote key in screen reader content as a readable word" + }, + "singleQuoteCharacterDescriptor": { + "message": "Single quote", + "description": "Represents the ' key in screen reader content as a readable word" + }, + "lessThanCharacterDescriptor": { + "message": "Less than", + "description": "Represents the < key in screen reader content as a readable word" + }, + "greaterThanCharacterDescriptor": { + "message": "Greater than", + "description": "Represents the > key in screen reader content as a readable word" + }, + "commaCharacterDescriptor": { + "message": "Comma", + "description": "Represents the , key in screen reader content as a readable word" + }, + "periodCharacterDescriptor": { + "message": "Period", + "description": "Represents the . key in screen reader content as a readable word" + }, + "questionCharacterDescriptor": { + "message": "Question mark", + "description": "Represents the ? key in screen reader content as a readable word" + }, + "forwardSlashCharacterDescriptor": { + "message": "Forward slash", + "description": "Represents the / key in screen reader content as a readable word" + }, + "lowercaseAriaLabel": { + "message": "Lowercase" + }, + "uppercaseAriaLabel": { + "message": "Uppercase" + }, + "generatedPassword": { + "message": "Generated password" } } diff --git a/apps/browser/src/_locales/id/messages.json b/apps/browser/src/_locales/id/messages.json index 8181bdea9c0..2ee5e6855de 100644 --- a/apps/browser/src/_locales/id/messages.json +++ b/apps/browser/src/_locales/id/messages.json @@ -19,6 +19,18 @@ "createAccount": { "message": "Buat Akun" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" + }, "setAStrongPassword": { "message": "Set a strong password" }, @@ -107,6 +119,9 @@ "copyPassword": { "message": "Salin Kata Sandi" }, + "copyPassphrase": { + "message": "Copy passphrase" + }, "copyNote": { "message": "Salin Catatan" }, @@ -152,6 +167,10 @@ "copyNotes": { "message": "Copy notes" }, + "fill": { + "message": "Fill", + "description": "This string is used on the vault page to indicate autofilling. Horizontal space is limited in the interface here so try and keep translations as concise as possible." + }, "autoFill": { "message": "Isi otomatis" }, @@ -407,6 +426,9 @@ "generatePassword": { "message": "Buat Kata Sandi" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "regeneratePassword": { "message": "Buat Ulang Kata Sandi" }, @@ -568,6 +590,15 @@ "launchWebsite": { "message": "Launch website" }, + "launchWebsiteName": { + "message": "Launch website $ITEMNAME$", + "placeholders": { + "itemname": { + "content": "$1", + "example": "Secret item" + } + } + }, "website": { "message": "Situs Web" }, @@ -814,6 +845,9 @@ "logIn": { "message": "Log in" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "restartRegistration": { "message": "Restart registration" }, @@ -1389,6 +1423,10 @@ "baseUrl": { "message": "URL Server" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "URL Server API" }, @@ -2464,8 +2502,8 @@ "message": "Secara opsional, minta kata sandi bagi pengguna untuk mengakses Send ini.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendPasswordDescV2": { - "message": "Require this password to view the Send.", + "sendPasswordDescV3": { + "message": "Add an optional password for recipients to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendNotesDesc": { @@ -2827,6 +2865,9 @@ "generateUsername": { "message": "Buat nama pengguna baru" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Jenis nama pengguna" }, @@ -2867,6 +2908,14 @@ "forwardedEmailDesc": { "message": "Generate an email alias with an external forwarding service." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ error: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -4494,9 +4543,15 @@ "itemLocation": { "message": "Item Location" }, + "fileSend": { + "message": "File Send" + }, "fileSends": { "message": "File Sends" }, + "textSend": { + "message": "Text Send" + }, "textSends": { "message": "Text Sends" }, @@ -4559,5 +4614,158 @@ }, "authenticating": { "message": "Authenticating" + }, + "fillGeneratedPassword": { + "message": "Fill generated password", + "description": "Heading for the password generator within the inline menu" + }, + "passwordRegenerated": { + "message": "Password regenerated", + "description": "Notification message for when a password has been regenerated" + }, + "saveLoginToBitwarden": { + "message": "Save login to Bitwarden?", + "description": "Confirmation message for saving a login to Bitwarden" + }, + "spaceCharacterDescriptor": { + "message": "Space", + "description": "Represents the space key in screen reader content as a readable word" + }, + "tildeCharacterDescriptor": { + "message": "Tilde", + "description": "Represents the ~ key in screen reader content as a readable word" + }, + "backtickCharacterDescriptor": { + "message": "Backtick", + "description": "Represents the ` key in screen reader content as a readable word" + }, + "exclamationCharacterDescriptor": { + "message": "Exclamation mark", + "description": "Represents the ! key in screen reader content as a readable word" + }, + "atSignCharacterDescriptor": { + "message": "At sign", + "description": "Represents the @ key in screen reader content as a readable word" + }, + "hashSignCharacterDescriptor": { + "message": "Hash sign", + "description": "Represents the # key in screen reader content as a readable word" + }, + "dollarSignCharacterDescriptor": { + "message": "Dollar sign", + "description": "Represents the $ key in screen reader content as a readable word" + }, + "percentSignCharacterDescriptor": { + "message": "Percent sign", + "description": "Represents the % key in screen reader content as a readable word" + }, + "caretCharacterDescriptor": { + "message": "Caret", + "description": "Represents the ^ key in screen reader content as a readable word" + }, + "ampersandCharacterDescriptor": { + "message": "Ampersand", + "description": "Represents the & key in screen reader content as a readable word" + }, + "asteriskCharacterDescriptor": { + "message": "Asterisk", + "description": "Represents the * key in screen reader content as a readable word" + }, + "parenLeftCharacterDescriptor": { + "message": "Left parenthesis", + "description": "Represents the ( key in screen reader content as a readable word" + }, + "parenRightCharacterDescriptor": { + "message": "Right parenthesis", + "description": "Represents the ) key in screen reader content as a readable word" + }, + "hyphenCharacterDescriptor": { + "message": "Underscore", + "description": "Represents the _ key in screen reader content as a readable word" + }, + "underscoreCharacterDescriptor": { + "message": "Hyphen", + "description": "Represents the - key in screen reader content as a readable word" + }, + "plusCharacterDescriptor": { + "message": "Plus", + "description": "Represents the + key in screen reader content as a readable word" + }, + "equalsCharacterDescriptor": { + "message": "Equals", + "description": "Represents the = key in screen reader content as a readable word" + }, + "braceLeftCharacterDescriptor": { + "message": "Left brace", + "description": "Represents the { key in screen reader content as a readable word" + }, + "braceRightCharacterDescriptor": { + "message": "Right brace", + "description": "Represents the } key in screen reader content as a readable word" + }, + "bracketLeftCharacterDescriptor": { + "message": "Left bracket", + "description": "Represents the [ key in screen reader content as a readable word" + }, + "bracketRightCharacterDescriptor": { + "message": "Right bracket", + "description": "Represents the ] key in screen reader content as a readable word" + }, + "pipeCharacterDescriptor": { + "message": "Pipe", + "description": "Represents the | key in screen reader content as a readable word" + }, + "backSlashCharacterDescriptor": { + "message": "Back slash", + "description": "Represents the back slash key in screen reader content as a readable word" + }, + "colonCharacterDescriptor": { + "message": "Colon", + "description": "Represents the : key in screen reader content as a readable word" + }, + "semicolonCharacterDescriptor": { + "message": "Semicolon", + "description": "Represents the ; key in screen reader content as a readable word" + }, + "doubleQuoteCharacterDescriptor": { + "message": "Double quote", + "description": "Represents the double quote key in screen reader content as a readable word" + }, + "singleQuoteCharacterDescriptor": { + "message": "Single quote", + "description": "Represents the ' key in screen reader content as a readable word" + }, + "lessThanCharacterDescriptor": { + "message": "Less than", + "description": "Represents the < key in screen reader content as a readable word" + }, + "greaterThanCharacterDescriptor": { + "message": "Greater than", + "description": "Represents the > key in screen reader content as a readable word" + }, + "commaCharacterDescriptor": { + "message": "Comma", + "description": "Represents the , key in screen reader content as a readable word" + }, + "periodCharacterDescriptor": { + "message": "Period", + "description": "Represents the . key in screen reader content as a readable word" + }, + "questionCharacterDescriptor": { + "message": "Question mark", + "description": "Represents the ? key in screen reader content as a readable word" + }, + "forwardSlashCharacterDescriptor": { + "message": "Forward slash", + "description": "Represents the / key in screen reader content as a readable word" + }, + "lowercaseAriaLabel": { + "message": "Lowercase" + }, + "uppercaseAriaLabel": { + "message": "Uppercase" + }, + "generatedPassword": { + "message": "Generated password" } } diff --git a/apps/browser/src/_locales/it/messages.json b/apps/browser/src/_locales/it/messages.json index 3b912c2e039..912fba56266 100644 --- a/apps/browser/src/_locales/it/messages.json +++ b/apps/browser/src/_locales/it/messages.json @@ -19,6 +19,18 @@ "createAccount": { "message": "Crea account" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" + }, "setAStrongPassword": { "message": "Imposta una password robusta" }, @@ -107,6 +119,9 @@ "copyPassword": { "message": "Copia password" }, + "copyPassphrase": { + "message": "Copy passphrase" + }, "copyNote": { "message": "Copia nota" }, @@ -152,6 +167,10 @@ "copyNotes": { "message": "Copy notes" }, + "fill": { + "message": "Fill", + "description": "This string is used on the vault page to indicate autofilling. Horizontal space is limited in the interface here so try and keep translations as concise as possible." + }, "autoFill": { "message": "Riempimento automatico" }, @@ -407,6 +426,9 @@ "generatePassword": { "message": "Genera password" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "regeneratePassword": { "message": "Rigenera password" }, @@ -568,6 +590,15 @@ "launchWebsite": { "message": "Avvia il sito web" }, + "launchWebsiteName": { + "message": "Launch website $ITEMNAME$", + "placeholders": { + "itemname": { + "content": "$1", + "example": "Secret item" + } + } + }, "website": { "message": "Sito web" }, @@ -814,6 +845,9 @@ "logIn": { "message": "Accedi" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "restartRegistration": { "message": "Riprova la registrazione" }, @@ -1389,6 +1423,10 @@ "baseUrl": { "message": "URL del server" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "URL del server API" }, @@ -2464,8 +2502,8 @@ "message": "Richiedi una password agli utenti per accedere a questo Send (facoltativo).", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendPasswordDescV2": { - "message": "Require this password to view the Send.", + "sendPasswordDescV3": { + "message": "Add an optional password for recipients to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendNotesDesc": { @@ -2827,6 +2865,9 @@ "generateUsername": { "message": "Genera nome utente" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Tipo di nome utente" }, @@ -2867,6 +2908,14 @@ "forwardedEmailDesc": { "message": "Genera un alias email con un servizio di inoltro esterno." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "Errore $SERVICENAME$: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -4494,9 +4543,15 @@ "itemLocation": { "message": "Posizione elemento" }, + "fileSend": { + "message": "File Send" + }, "fileSends": { "message": "Send File" }, + "textSend": { + "message": "Text Send" + }, "textSends": { "message": "Send Testo" }, @@ -4559,5 +4614,158 @@ }, "authenticating": { "message": "Authenticating" + }, + "fillGeneratedPassword": { + "message": "Fill generated password", + "description": "Heading for the password generator within the inline menu" + }, + "passwordRegenerated": { + "message": "Password regenerated", + "description": "Notification message for when a password has been regenerated" + }, + "saveLoginToBitwarden": { + "message": "Save login to Bitwarden?", + "description": "Confirmation message for saving a login to Bitwarden" + }, + "spaceCharacterDescriptor": { + "message": "Space", + "description": "Represents the space key in screen reader content as a readable word" + }, + "tildeCharacterDescriptor": { + "message": "Tilde", + "description": "Represents the ~ key in screen reader content as a readable word" + }, + "backtickCharacterDescriptor": { + "message": "Backtick", + "description": "Represents the ` key in screen reader content as a readable word" + }, + "exclamationCharacterDescriptor": { + "message": "Exclamation mark", + "description": "Represents the ! key in screen reader content as a readable word" + }, + "atSignCharacterDescriptor": { + "message": "At sign", + "description": "Represents the @ key in screen reader content as a readable word" + }, + "hashSignCharacterDescriptor": { + "message": "Hash sign", + "description": "Represents the # key in screen reader content as a readable word" + }, + "dollarSignCharacterDescriptor": { + "message": "Dollar sign", + "description": "Represents the $ key in screen reader content as a readable word" + }, + "percentSignCharacterDescriptor": { + "message": "Percent sign", + "description": "Represents the % key in screen reader content as a readable word" + }, + "caretCharacterDescriptor": { + "message": "Caret", + "description": "Represents the ^ key in screen reader content as a readable word" + }, + "ampersandCharacterDescriptor": { + "message": "Ampersand", + "description": "Represents the & key in screen reader content as a readable word" + }, + "asteriskCharacterDescriptor": { + "message": "Asterisk", + "description": "Represents the * key in screen reader content as a readable word" + }, + "parenLeftCharacterDescriptor": { + "message": "Left parenthesis", + "description": "Represents the ( key in screen reader content as a readable word" + }, + "parenRightCharacterDescriptor": { + "message": "Right parenthesis", + "description": "Represents the ) key in screen reader content as a readable word" + }, + "hyphenCharacterDescriptor": { + "message": "Underscore", + "description": "Represents the _ key in screen reader content as a readable word" + }, + "underscoreCharacterDescriptor": { + "message": "Hyphen", + "description": "Represents the - key in screen reader content as a readable word" + }, + "plusCharacterDescriptor": { + "message": "Plus", + "description": "Represents the + key in screen reader content as a readable word" + }, + "equalsCharacterDescriptor": { + "message": "Equals", + "description": "Represents the = key in screen reader content as a readable word" + }, + "braceLeftCharacterDescriptor": { + "message": "Left brace", + "description": "Represents the { key in screen reader content as a readable word" + }, + "braceRightCharacterDescriptor": { + "message": "Right brace", + "description": "Represents the } key in screen reader content as a readable word" + }, + "bracketLeftCharacterDescriptor": { + "message": "Left bracket", + "description": "Represents the [ key in screen reader content as a readable word" + }, + "bracketRightCharacterDescriptor": { + "message": "Right bracket", + "description": "Represents the ] key in screen reader content as a readable word" + }, + "pipeCharacterDescriptor": { + "message": "Pipe", + "description": "Represents the | key in screen reader content as a readable word" + }, + "backSlashCharacterDescriptor": { + "message": "Back slash", + "description": "Represents the back slash key in screen reader content as a readable word" + }, + "colonCharacterDescriptor": { + "message": "Colon", + "description": "Represents the : key in screen reader content as a readable word" + }, + "semicolonCharacterDescriptor": { + "message": "Semicolon", + "description": "Represents the ; key in screen reader content as a readable word" + }, + "doubleQuoteCharacterDescriptor": { + "message": "Double quote", + "description": "Represents the double quote key in screen reader content as a readable word" + }, + "singleQuoteCharacterDescriptor": { + "message": "Single quote", + "description": "Represents the ' key in screen reader content as a readable word" + }, + "lessThanCharacterDescriptor": { + "message": "Less than", + "description": "Represents the < key in screen reader content as a readable word" + }, + "greaterThanCharacterDescriptor": { + "message": "Greater than", + "description": "Represents the > key in screen reader content as a readable word" + }, + "commaCharacterDescriptor": { + "message": "Comma", + "description": "Represents the , key in screen reader content as a readable word" + }, + "periodCharacterDescriptor": { + "message": "Period", + "description": "Represents the . key in screen reader content as a readable word" + }, + "questionCharacterDescriptor": { + "message": "Question mark", + "description": "Represents the ? key in screen reader content as a readable word" + }, + "forwardSlashCharacterDescriptor": { + "message": "Forward slash", + "description": "Represents the / key in screen reader content as a readable word" + }, + "lowercaseAriaLabel": { + "message": "Lowercase" + }, + "uppercaseAriaLabel": { + "message": "Uppercase" + }, + "generatedPassword": { + "message": "Generated password" } } diff --git a/apps/browser/src/_locales/ja/messages.json b/apps/browser/src/_locales/ja/messages.json index b7da0c89584..59fe8fcf708 100644 --- a/apps/browser/src/_locales/ja/messages.json +++ b/apps/browser/src/_locales/ja/messages.json @@ -19,6 +19,18 @@ "createAccount": { "message": "アカウントの作成" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" + }, "setAStrongPassword": { "message": "強力なパスワードを設定する" }, @@ -107,6 +119,9 @@ "copyPassword": { "message": "パスワードをコピー" }, + "copyPassphrase": { + "message": "Copy passphrase" + }, "copyNote": { "message": "メモをコピー" }, @@ -152,6 +167,10 @@ "copyNotes": { "message": "メモをコピー" }, + "fill": { + "message": "Fill", + "description": "This string is used on the vault page to indicate autofilling. Horizontal space is limited in the interface here so try and keep translations as concise as possible." + }, "autoFill": { "message": "自動入力" }, @@ -407,6 +426,9 @@ "generatePassword": { "message": "パスワードの自動生成" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "regeneratePassword": { "message": "パスワードの再生成" }, @@ -568,6 +590,15 @@ "launchWebsite": { "message": "ウェブサイトを開く" }, + "launchWebsiteName": { + "message": "Launch website $ITEMNAME$", + "placeholders": { + "itemname": { + "content": "$1", + "example": "Secret item" + } + } + }, "website": { "message": "ウェブサイト" }, @@ -814,6 +845,9 @@ "logIn": { "message": "ログイン" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "restartRegistration": { "message": "登録を再度始める" }, @@ -1389,6 +1423,10 @@ "baseUrl": { "message": "サーバー URL" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "API サーバー URL" }, @@ -2464,8 +2502,8 @@ "message": "必要に応じて、ユーザーがこの Send にアクセスするためのパスワードを要求します。", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendPasswordDescV2": { - "message": "Send を表示するにはこのパスワードが必要になります。", + "sendPasswordDescV3": { + "message": "Add an optional password for recipients to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendNotesDesc": { @@ -2827,6 +2865,9 @@ "generateUsername": { "message": "ユーザー名を生成" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "ユーザー名の種類" }, @@ -2867,6 +2908,14 @@ "forwardedEmailDesc": { "message": "外部転送サービスを使用してメールエイリアスを生成します。" }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ エラー: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -4494,9 +4543,15 @@ "itemLocation": { "message": "アイテムの場所" }, + "fileSend": { + "message": "File Send" + }, "fileSends": { "message": "ファイル Send" }, + "textSend": { + "message": "Text Send" + }, "textSends": { "message": "テキスト Send" }, @@ -4559,5 +4614,158 @@ }, "authenticating": { "message": "認証中" + }, + "fillGeneratedPassword": { + "message": "Fill generated password", + "description": "Heading for the password generator within the inline menu" + }, + "passwordRegenerated": { + "message": "Password regenerated", + "description": "Notification message for when a password has been regenerated" + }, + "saveLoginToBitwarden": { + "message": "Save login to Bitwarden?", + "description": "Confirmation message for saving a login to Bitwarden" + }, + "spaceCharacterDescriptor": { + "message": "Space", + "description": "Represents the space key in screen reader content as a readable word" + }, + "tildeCharacterDescriptor": { + "message": "Tilde", + "description": "Represents the ~ key in screen reader content as a readable word" + }, + "backtickCharacterDescriptor": { + "message": "Backtick", + "description": "Represents the ` key in screen reader content as a readable word" + }, + "exclamationCharacterDescriptor": { + "message": "Exclamation mark", + "description": "Represents the ! key in screen reader content as a readable word" + }, + "atSignCharacterDescriptor": { + "message": "At sign", + "description": "Represents the @ key in screen reader content as a readable word" + }, + "hashSignCharacterDescriptor": { + "message": "Hash sign", + "description": "Represents the # key in screen reader content as a readable word" + }, + "dollarSignCharacterDescriptor": { + "message": "Dollar sign", + "description": "Represents the $ key in screen reader content as a readable word" + }, + "percentSignCharacterDescriptor": { + "message": "Percent sign", + "description": "Represents the % key in screen reader content as a readable word" + }, + "caretCharacterDescriptor": { + "message": "Caret", + "description": "Represents the ^ key in screen reader content as a readable word" + }, + "ampersandCharacterDescriptor": { + "message": "Ampersand", + "description": "Represents the & key in screen reader content as a readable word" + }, + "asteriskCharacterDescriptor": { + "message": "Asterisk", + "description": "Represents the * key in screen reader content as a readable word" + }, + "parenLeftCharacterDescriptor": { + "message": "Left parenthesis", + "description": "Represents the ( key in screen reader content as a readable word" + }, + "parenRightCharacterDescriptor": { + "message": "Right parenthesis", + "description": "Represents the ) key in screen reader content as a readable word" + }, + "hyphenCharacterDescriptor": { + "message": "Underscore", + "description": "Represents the _ key in screen reader content as a readable word" + }, + "underscoreCharacterDescriptor": { + "message": "Hyphen", + "description": "Represents the - key in screen reader content as a readable word" + }, + "plusCharacterDescriptor": { + "message": "Plus", + "description": "Represents the + key in screen reader content as a readable word" + }, + "equalsCharacterDescriptor": { + "message": "Equals", + "description": "Represents the = key in screen reader content as a readable word" + }, + "braceLeftCharacterDescriptor": { + "message": "Left brace", + "description": "Represents the { key in screen reader content as a readable word" + }, + "braceRightCharacterDescriptor": { + "message": "Right brace", + "description": "Represents the } key in screen reader content as a readable word" + }, + "bracketLeftCharacterDescriptor": { + "message": "Left bracket", + "description": "Represents the [ key in screen reader content as a readable word" + }, + "bracketRightCharacterDescriptor": { + "message": "Right bracket", + "description": "Represents the ] key in screen reader content as a readable word" + }, + "pipeCharacterDescriptor": { + "message": "Pipe", + "description": "Represents the | key in screen reader content as a readable word" + }, + "backSlashCharacterDescriptor": { + "message": "Back slash", + "description": "Represents the back slash key in screen reader content as a readable word" + }, + "colonCharacterDescriptor": { + "message": "Colon", + "description": "Represents the : key in screen reader content as a readable word" + }, + "semicolonCharacterDescriptor": { + "message": "Semicolon", + "description": "Represents the ; key in screen reader content as a readable word" + }, + "doubleQuoteCharacterDescriptor": { + "message": "Double quote", + "description": "Represents the double quote key in screen reader content as a readable word" + }, + "singleQuoteCharacterDescriptor": { + "message": "Single quote", + "description": "Represents the ' key in screen reader content as a readable word" + }, + "lessThanCharacterDescriptor": { + "message": "Less than", + "description": "Represents the < key in screen reader content as a readable word" + }, + "greaterThanCharacterDescriptor": { + "message": "Greater than", + "description": "Represents the > key in screen reader content as a readable word" + }, + "commaCharacterDescriptor": { + "message": "Comma", + "description": "Represents the , key in screen reader content as a readable word" + }, + "periodCharacterDescriptor": { + "message": "Period", + "description": "Represents the . key in screen reader content as a readable word" + }, + "questionCharacterDescriptor": { + "message": "Question mark", + "description": "Represents the ? key in screen reader content as a readable word" + }, + "forwardSlashCharacterDescriptor": { + "message": "Forward slash", + "description": "Represents the / key in screen reader content as a readable word" + }, + "lowercaseAriaLabel": { + "message": "Lowercase" + }, + "uppercaseAriaLabel": { + "message": "Uppercase" + }, + "generatedPassword": { + "message": "Generated password" } } diff --git a/apps/browser/src/_locales/ka/messages.json b/apps/browser/src/_locales/ka/messages.json index 3c487c9dfa1..1d38c4b53ea 100644 --- a/apps/browser/src/_locales/ka/messages.json +++ b/apps/browser/src/_locales/ka/messages.json @@ -19,6 +19,18 @@ "createAccount": { "message": "ანგარიშის შექმნა" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" + }, "setAStrongPassword": { "message": "Set a strong password" }, @@ -107,6 +119,9 @@ "copyPassword": { "message": "პაროლის კოპირება" }, + "copyPassphrase": { + "message": "Copy passphrase" + }, "copyNote": { "message": "Copy note" }, @@ -152,6 +167,10 @@ "copyNotes": { "message": "Copy notes" }, + "fill": { + "message": "Fill", + "description": "This string is used on the vault page to indicate autofilling. Horizontal space is limited in the interface here so try and keep translations as concise as possible." + }, "autoFill": { "message": "ავტომატური შევსება" }, @@ -407,6 +426,9 @@ "generatePassword": { "message": "პაროლის გენერირება" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "regeneratePassword": { "message": "Regenerate password" }, @@ -568,6 +590,15 @@ "launchWebsite": { "message": "Launch website" }, + "launchWebsiteName": { + "message": "Launch website $ITEMNAME$", + "placeholders": { + "itemname": { + "content": "$1", + "example": "Secret item" + } + } + }, "website": { "message": "ვებგვერდი" }, @@ -814,6 +845,9 @@ "logIn": { "message": "შესვლა" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "restartRegistration": { "message": "Restart registration" }, @@ -1389,6 +1423,10 @@ "baseUrl": { "message": "სერვერის URL" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "API server URL" }, @@ -2464,8 +2502,8 @@ "message": "Optionally require a password for users to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendPasswordDescV2": { - "message": "Require this password to view the Send.", + "sendPasswordDescV3": { + "message": "Add an optional password for recipients to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendNotesDesc": { @@ -2827,6 +2865,9 @@ "generateUsername": { "message": "Generate username" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Username type" }, @@ -2867,6 +2908,14 @@ "forwardedEmailDesc": { "message": "Generate an email alias with an external forwarding service." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ error: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -4494,9 +4543,15 @@ "itemLocation": { "message": "Item Location" }, + "fileSend": { + "message": "File Send" + }, "fileSends": { "message": "File Sends" }, + "textSend": { + "message": "Text Send" + }, "textSends": { "message": "Text Sends" }, @@ -4559,5 +4614,158 @@ }, "authenticating": { "message": "ავთენტიკაცია" + }, + "fillGeneratedPassword": { + "message": "Fill generated password", + "description": "Heading for the password generator within the inline menu" + }, + "passwordRegenerated": { + "message": "Password regenerated", + "description": "Notification message for when a password has been regenerated" + }, + "saveLoginToBitwarden": { + "message": "Save login to Bitwarden?", + "description": "Confirmation message for saving a login to Bitwarden" + }, + "spaceCharacterDescriptor": { + "message": "Space", + "description": "Represents the space key in screen reader content as a readable word" + }, + "tildeCharacterDescriptor": { + "message": "Tilde", + "description": "Represents the ~ key in screen reader content as a readable word" + }, + "backtickCharacterDescriptor": { + "message": "Backtick", + "description": "Represents the ` key in screen reader content as a readable word" + }, + "exclamationCharacterDescriptor": { + "message": "Exclamation mark", + "description": "Represents the ! key in screen reader content as a readable word" + }, + "atSignCharacterDescriptor": { + "message": "At sign", + "description": "Represents the @ key in screen reader content as a readable word" + }, + "hashSignCharacterDescriptor": { + "message": "Hash sign", + "description": "Represents the # key in screen reader content as a readable word" + }, + "dollarSignCharacterDescriptor": { + "message": "Dollar sign", + "description": "Represents the $ key in screen reader content as a readable word" + }, + "percentSignCharacterDescriptor": { + "message": "Percent sign", + "description": "Represents the % key in screen reader content as a readable word" + }, + "caretCharacterDescriptor": { + "message": "Caret", + "description": "Represents the ^ key in screen reader content as a readable word" + }, + "ampersandCharacterDescriptor": { + "message": "Ampersand", + "description": "Represents the & key in screen reader content as a readable word" + }, + "asteriskCharacterDescriptor": { + "message": "Asterisk", + "description": "Represents the * key in screen reader content as a readable word" + }, + "parenLeftCharacterDescriptor": { + "message": "Left parenthesis", + "description": "Represents the ( key in screen reader content as a readable word" + }, + "parenRightCharacterDescriptor": { + "message": "Right parenthesis", + "description": "Represents the ) key in screen reader content as a readable word" + }, + "hyphenCharacterDescriptor": { + "message": "Underscore", + "description": "Represents the _ key in screen reader content as a readable word" + }, + "underscoreCharacterDescriptor": { + "message": "Hyphen", + "description": "Represents the - key in screen reader content as a readable word" + }, + "plusCharacterDescriptor": { + "message": "Plus", + "description": "Represents the + key in screen reader content as a readable word" + }, + "equalsCharacterDescriptor": { + "message": "Equals", + "description": "Represents the = key in screen reader content as a readable word" + }, + "braceLeftCharacterDescriptor": { + "message": "Left brace", + "description": "Represents the { key in screen reader content as a readable word" + }, + "braceRightCharacterDescriptor": { + "message": "Right brace", + "description": "Represents the } key in screen reader content as a readable word" + }, + "bracketLeftCharacterDescriptor": { + "message": "Left bracket", + "description": "Represents the [ key in screen reader content as a readable word" + }, + "bracketRightCharacterDescriptor": { + "message": "Right bracket", + "description": "Represents the ] key in screen reader content as a readable word" + }, + "pipeCharacterDescriptor": { + "message": "Pipe", + "description": "Represents the | key in screen reader content as a readable word" + }, + "backSlashCharacterDescriptor": { + "message": "Back slash", + "description": "Represents the back slash key in screen reader content as a readable word" + }, + "colonCharacterDescriptor": { + "message": "Colon", + "description": "Represents the : key in screen reader content as a readable word" + }, + "semicolonCharacterDescriptor": { + "message": "Semicolon", + "description": "Represents the ; key in screen reader content as a readable word" + }, + "doubleQuoteCharacterDescriptor": { + "message": "Double quote", + "description": "Represents the double quote key in screen reader content as a readable word" + }, + "singleQuoteCharacterDescriptor": { + "message": "Single quote", + "description": "Represents the ' key in screen reader content as a readable word" + }, + "lessThanCharacterDescriptor": { + "message": "Less than", + "description": "Represents the < key in screen reader content as a readable word" + }, + "greaterThanCharacterDescriptor": { + "message": "Greater than", + "description": "Represents the > key in screen reader content as a readable word" + }, + "commaCharacterDescriptor": { + "message": "Comma", + "description": "Represents the , key in screen reader content as a readable word" + }, + "periodCharacterDescriptor": { + "message": "Period", + "description": "Represents the . key in screen reader content as a readable word" + }, + "questionCharacterDescriptor": { + "message": "Question mark", + "description": "Represents the ? key in screen reader content as a readable word" + }, + "forwardSlashCharacterDescriptor": { + "message": "Forward slash", + "description": "Represents the / key in screen reader content as a readable word" + }, + "lowercaseAriaLabel": { + "message": "Lowercase" + }, + "uppercaseAriaLabel": { + "message": "Uppercase" + }, + "generatedPassword": { + "message": "Generated password" } } diff --git a/apps/browser/src/_locales/km/messages.json b/apps/browser/src/_locales/km/messages.json index 290663f4347..4037887cb28 100644 --- a/apps/browser/src/_locales/km/messages.json +++ b/apps/browser/src/_locales/km/messages.json @@ -19,6 +19,18 @@ "createAccount": { "message": "Create account" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" + }, "setAStrongPassword": { "message": "Set a strong password" }, @@ -107,6 +119,9 @@ "copyPassword": { "message": "Copy password" }, + "copyPassphrase": { + "message": "Copy passphrase" + }, "copyNote": { "message": "Copy note" }, @@ -152,6 +167,10 @@ "copyNotes": { "message": "Copy notes" }, + "fill": { + "message": "Fill", + "description": "This string is used on the vault page to indicate autofilling. Horizontal space is limited in the interface here so try and keep translations as concise as possible." + }, "autoFill": { "message": "Autofill" }, @@ -407,6 +426,9 @@ "generatePassword": { "message": "Generate password" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "regeneratePassword": { "message": "Regenerate password" }, @@ -568,6 +590,15 @@ "launchWebsite": { "message": "Launch website" }, + "launchWebsiteName": { + "message": "Launch website $ITEMNAME$", + "placeholders": { + "itemname": { + "content": "$1", + "example": "Secret item" + } + } + }, "website": { "message": "Website" }, @@ -814,6 +845,9 @@ "logIn": { "message": "Log in" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "restartRegistration": { "message": "Restart registration" }, @@ -1389,6 +1423,10 @@ "baseUrl": { "message": "Server URL" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "API server URL" }, @@ -2464,8 +2502,8 @@ "message": "Optionally require a password for users to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendPasswordDescV2": { - "message": "Require this password to view the Send.", + "sendPasswordDescV3": { + "message": "Add an optional password for recipients to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendNotesDesc": { @@ -2827,6 +2865,9 @@ "generateUsername": { "message": "Generate username" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Username type" }, @@ -2867,6 +2908,14 @@ "forwardedEmailDesc": { "message": "Generate an email alias with an external forwarding service." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ error: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -4494,9 +4543,15 @@ "itemLocation": { "message": "Item Location" }, + "fileSend": { + "message": "File Send" + }, "fileSends": { "message": "File Sends" }, + "textSend": { + "message": "Text Send" + }, "textSends": { "message": "Text Sends" }, @@ -4559,5 +4614,158 @@ }, "authenticating": { "message": "Authenticating" + }, + "fillGeneratedPassword": { + "message": "Fill generated password", + "description": "Heading for the password generator within the inline menu" + }, + "passwordRegenerated": { + "message": "Password regenerated", + "description": "Notification message for when a password has been regenerated" + }, + "saveLoginToBitwarden": { + "message": "Save login to Bitwarden?", + "description": "Confirmation message for saving a login to Bitwarden" + }, + "spaceCharacterDescriptor": { + "message": "Space", + "description": "Represents the space key in screen reader content as a readable word" + }, + "tildeCharacterDescriptor": { + "message": "Tilde", + "description": "Represents the ~ key in screen reader content as a readable word" + }, + "backtickCharacterDescriptor": { + "message": "Backtick", + "description": "Represents the ` key in screen reader content as a readable word" + }, + "exclamationCharacterDescriptor": { + "message": "Exclamation mark", + "description": "Represents the ! key in screen reader content as a readable word" + }, + "atSignCharacterDescriptor": { + "message": "At sign", + "description": "Represents the @ key in screen reader content as a readable word" + }, + "hashSignCharacterDescriptor": { + "message": "Hash sign", + "description": "Represents the # key in screen reader content as a readable word" + }, + "dollarSignCharacterDescriptor": { + "message": "Dollar sign", + "description": "Represents the $ key in screen reader content as a readable word" + }, + "percentSignCharacterDescriptor": { + "message": "Percent sign", + "description": "Represents the % key in screen reader content as a readable word" + }, + "caretCharacterDescriptor": { + "message": "Caret", + "description": "Represents the ^ key in screen reader content as a readable word" + }, + "ampersandCharacterDescriptor": { + "message": "Ampersand", + "description": "Represents the & key in screen reader content as a readable word" + }, + "asteriskCharacterDescriptor": { + "message": "Asterisk", + "description": "Represents the * key in screen reader content as a readable word" + }, + "parenLeftCharacterDescriptor": { + "message": "Left parenthesis", + "description": "Represents the ( key in screen reader content as a readable word" + }, + "parenRightCharacterDescriptor": { + "message": "Right parenthesis", + "description": "Represents the ) key in screen reader content as a readable word" + }, + "hyphenCharacterDescriptor": { + "message": "Underscore", + "description": "Represents the _ key in screen reader content as a readable word" + }, + "underscoreCharacterDescriptor": { + "message": "Hyphen", + "description": "Represents the - key in screen reader content as a readable word" + }, + "plusCharacterDescriptor": { + "message": "Plus", + "description": "Represents the + key in screen reader content as a readable word" + }, + "equalsCharacterDescriptor": { + "message": "Equals", + "description": "Represents the = key in screen reader content as a readable word" + }, + "braceLeftCharacterDescriptor": { + "message": "Left brace", + "description": "Represents the { key in screen reader content as a readable word" + }, + "braceRightCharacterDescriptor": { + "message": "Right brace", + "description": "Represents the } key in screen reader content as a readable word" + }, + "bracketLeftCharacterDescriptor": { + "message": "Left bracket", + "description": "Represents the [ key in screen reader content as a readable word" + }, + "bracketRightCharacterDescriptor": { + "message": "Right bracket", + "description": "Represents the ] key in screen reader content as a readable word" + }, + "pipeCharacterDescriptor": { + "message": "Pipe", + "description": "Represents the | key in screen reader content as a readable word" + }, + "backSlashCharacterDescriptor": { + "message": "Back slash", + "description": "Represents the back slash key in screen reader content as a readable word" + }, + "colonCharacterDescriptor": { + "message": "Colon", + "description": "Represents the : key in screen reader content as a readable word" + }, + "semicolonCharacterDescriptor": { + "message": "Semicolon", + "description": "Represents the ; key in screen reader content as a readable word" + }, + "doubleQuoteCharacterDescriptor": { + "message": "Double quote", + "description": "Represents the double quote key in screen reader content as a readable word" + }, + "singleQuoteCharacterDescriptor": { + "message": "Single quote", + "description": "Represents the ' key in screen reader content as a readable word" + }, + "lessThanCharacterDescriptor": { + "message": "Less than", + "description": "Represents the < key in screen reader content as a readable word" + }, + "greaterThanCharacterDescriptor": { + "message": "Greater than", + "description": "Represents the > key in screen reader content as a readable word" + }, + "commaCharacterDescriptor": { + "message": "Comma", + "description": "Represents the , key in screen reader content as a readable word" + }, + "periodCharacterDescriptor": { + "message": "Period", + "description": "Represents the . key in screen reader content as a readable word" + }, + "questionCharacterDescriptor": { + "message": "Question mark", + "description": "Represents the ? key in screen reader content as a readable word" + }, + "forwardSlashCharacterDescriptor": { + "message": "Forward slash", + "description": "Represents the / key in screen reader content as a readable word" + }, + "lowercaseAriaLabel": { + "message": "Lowercase" + }, + "uppercaseAriaLabel": { + "message": "Uppercase" + }, + "generatedPassword": { + "message": "Generated password" } } diff --git a/apps/browser/src/_locales/kn/messages.json b/apps/browser/src/_locales/kn/messages.json index acf6340df91..c58b6e5ff80 100644 --- a/apps/browser/src/_locales/kn/messages.json +++ b/apps/browser/src/_locales/kn/messages.json @@ -19,6 +19,18 @@ "createAccount": { "message": "ಖಾತೆ ತೆರೆ" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" + }, "setAStrongPassword": { "message": "Set a strong password" }, @@ -107,6 +119,9 @@ "copyPassword": { "message": "ಪಾಸ್ವರ್ಡ್ ನಕಲಿಸಿ" }, + "copyPassphrase": { + "message": "Copy passphrase" + }, "copyNote": { "message": "ಟಿಪ್ಪಣಿ ನಕಲಿಸಿ" }, @@ -152,6 +167,10 @@ "copyNotes": { "message": "Copy notes" }, + "fill": { + "message": "Fill", + "description": "This string is used on the vault page to indicate autofilling. Horizontal space is limited in the interface here so try and keep translations as concise as possible." + }, "autoFill": { "message": "ಸ್ವಯಂ ಭರ್ತಿ" }, @@ -407,6 +426,9 @@ "generatePassword": { "message": "ಪಾಸ್ವರ್ಡ್ ರಚಿಸಿ" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "regeneratePassword": { "message": "ಪಾಸ್ವರ್ಡ್ ಅನ್ನು ಪುನರುತ್ಪಾದಿಸಿ" }, @@ -568,6 +590,15 @@ "launchWebsite": { "message": "Launch website" }, + "launchWebsiteName": { + "message": "Launch website $ITEMNAME$", + "placeholders": { + "itemname": { + "content": "$1", + "example": "Secret item" + } + } + }, "website": { "message": "ಜಾಲತಾಣ" }, @@ -814,6 +845,9 @@ "logIn": { "message": "Log in" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "restartRegistration": { "message": "Restart registration" }, @@ -1389,6 +1423,10 @@ "baseUrl": { "message": "ಸರ್ವರ್ URL" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "API ಸರ್ವರ್ URL" }, @@ -2464,8 +2502,8 @@ "message": "ಈ ಕಳುಹಿಸುವಿಕೆಯನ್ನು ಪ್ರವೇಶಿಸಲು ಬಳಕೆದಾರರಿಗೆ ಪಾಸ್‌ವರ್ಡ್ ಐಚ್ ಗತ್ಯವಿದೆ.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendPasswordDescV2": { - "message": "Require this password to view the Send.", + "sendPasswordDescV3": { + "message": "Add an optional password for recipients to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendNotesDesc": { @@ -2827,6 +2865,9 @@ "generateUsername": { "message": "Generate username" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Username type" }, @@ -2867,6 +2908,14 @@ "forwardedEmailDesc": { "message": "Generate an email alias with an external forwarding service." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ error: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -4494,9 +4543,15 @@ "itemLocation": { "message": "Item Location" }, + "fileSend": { + "message": "File Send" + }, "fileSends": { "message": "File Sends" }, + "textSend": { + "message": "Text Send" + }, "textSends": { "message": "Text Sends" }, @@ -4559,5 +4614,158 @@ }, "authenticating": { "message": "Authenticating" + }, + "fillGeneratedPassword": { + "message": "Fill generated password", + "description": "Heading for the password generator within the inline menu" + }, + "passwordRegenerated": { + "message": "Password regenerated", + "description": "Notification message for when a password has been regenerated" + }, + "saveLoginToBitwarden": { + "message": "Save login to Bitwarden?", + "description": "Confirmation message for saving a login to Bitwarden" + }, + "spaceCharacterDescriptor": { + "message": "Space", + "description": "Represents the space key in screen reader content as a readable word" + }, + "tildeCharacterDescriptor": { + "message": "Tilde", + "description": "Represents the ~ key in screen reader content as a readable word" + }, + "backtickCharacterDescriptor": { + "message": "Backtick", + "description": "Represents the ` key in screen reader content as a readable word" + }, + "exclamationCharacterDescriptor": { + "message": "Exclamation mark", + "description": "Represents the ! key in screen reader content as a readable word" + }, + "atSignCharacterDescriptor": { + "message": "At sign", + "description": "Represents the @ key in screen reader content as a readable word" + }, + "hashSignCharacterDescriptor": { + "message": "Hash sign", + "description": "Represents the # key in screen reader content as a readable word" + }, + "dollarSignCharacterDescriptor": { + "message": "Dollar sign", + "description": "Represents the $ key in screen reader content as a readable word" + }, + "percentSignCharacterDescriptor": { + "message": "Percent sign", + "description": "Represents the % key in screen reader content as a readable word" + }, + "caretCharacterDescriptor": { + "message": "Caret", + "description": "Represents the ^ key in screen reader content as a readable word" + }, + "ampersandCharacterDescriptor": { + "message": "Ampersand", + "description": "Represents the & key in screen reader content as a readable word" + }, + "asteriskCharacterDescriptor": { + "message": "Asterisk", + "description": "Represents the * key in screen reader content as a readable word" + }, + "parenLeftCharacterDescriptor": { + "message": "Left parenthesis", + "description": "Represents the ( key in screen reader content as a readable word" + }, + "parenRightCharacterDescriptor": { + "message": "Right parenthesis", + "description": "Represents the ) key in screen reader content as a readable word" + }, + "hyphenCharacterDescriptor": { + "message": "Underscore", + "description": "Represents the _ key in screen reader content as a readable word" + }, + "underscoreCharacterDescriptor": { + "message": "Hyphen", + "description": "Represents the - key in screen reader content as a readable word" + }, + "plusCharacterDescriptor": { + "message": "Plus", + "description": "Represents the + key in screen reader content as a readable word" + }, + "equalsCharacterDescriptor": { + "message": "Equals", + "description": "Represents the = key in screen reader content as a readable word" + }, + "braceLeftCharacterDescriptor": { + "message": "Left brace", + "description": "Represents the { key in screen reader content as a readable word" + }, + "braceRightCharacterDescriptor": { + "message": "Right brace", + "description": "Represents the } key in screen reader content as a readable word" + }, + "bracketLeftCharacterDescriptor": { + "message": "Left bracket", + "description": "Represents the [ key in screen reader content as a readable word" + }, + "bracketRightCharacterDescriptor": { + "message": "Right bracket", + "description": "Represents the ] key in screen reader content as a readable word" + }, + "pipeCharacterDescriptor": { + "message": "Pipe", + "description": "Represents the | key in screen reader content as a readable word" + }, + "backSlashCharacterDescriptor": { + "message": "Back slash", + "description": "Represents the back slash key in screen reader content as a readable word" + }, + "colonCharacterDescriptor": { + "message": "Colon", + "description": "Represents the : key in screen reader content as a readable word" + }, + "semicolonCharacterDescriptor": { + "message": "Semicolon", + "description": "Represents the ; key in screen reader content as a readable word" + }, + "doubleQuoteCharacterDescriptor": { + "message": "Double quote", + "description": "Represents the double quote key in screen reader content as a readable word" + }, + "singleQuoteCharacterDescriptor": { + "message": "Single quote", + "description": "Represents the ' key in screen reader content as a readable word" + }, + "lessThanCharacterDescriptor": { + "message": "Less than", + "description": "Represents the < key in screen reader content as a readable word" + }, + "greaterThanCharacterDescriptor": { + "message": "Greater than", + "description": "Represents the > key in screen reader content as a readable word" + }, + "commaCharacterDescriptor": { + "message": "Comma", + "description": "Represents the , key in screen reader content as a readable word" + }, + "periodCharacterDescriptor": { + "message": "Period", + "description": "Represents the . key in screen reader content as a readable word" + }, + "questionCharacterDescriptor": { + "message": "Question mark", + "description": "Represents the ? key in screen reader content as a readable word" + }, + "forwardSlashCharacterDescriptor": { + "message": "Forward slash", + "description": "Represents the / key in screen reader content as a readable word" + }, + "lowercaseAriaLabel": { + "message": "Lowercase" + }, + "uppercaseAriaLabel": { + "message": "Uppercase" + }, + "generatedPassword": { + "message": "Generated password" } } diff --git a/apps/browser/src/_locales/ko/messages.json b/apps/browser/src/_locales/ko/messages.json index 7a03c87713a..e9b012cc277 100644 --- a/apps/browser/src/_locales/ko/messages.json +++ b/apps/browser/src/_locales/ko/messages.json @@ -19,6 +19,18 @@ "createAccount": { "message": "계정 만들기" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" + }, "setAStrongPassword": { "message": "비밀번호 설정" }, @@ -107,6 +119,9 @@ "copyPassword": { "message": "비밀번호 복사" }, + "copyPassphrase": { + "message": "Copy passphrase" + }, "copyNote": { "message": "메모 복사" }, @@ -152,6 +167,10 @@ "copyNotes": { "message": "Copy notes" }, + "fill": { + "message": "Fill", + "description": "This string is used on the vault page to indicate autofilling. Horizontal space is limited in the interface here so try and keep translations as concise as possible." + }, "autoFill": { "message": "자동 완성" }, @@ -407,6 +426,9 @@ "generatePassword": { "message": "비밀번호 생성" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "regeneratePassword": { "message": "비밀번호 재생성" }, @@ -568,6 +590,15 @@ "launchWebsite": { "message": "웹사이트 열기" }, + "launchWebsiteName": { + "message": "Launch website $ITEMNAME$", + "placeholders": { + "itemname": { + "content": "$1", + "example": "Secret item" + } + } + }, "website": { "message": "웹 사이트" }, @@ -814,6 +845,9 @@ "logIn": { "message": "로그인" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "restartRegistration": { "message": "Restart registration" }, @@ -1389,6 +1423,10 @@ "baseUrl": { "message": "서버 URL" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "API 서버 URL" }, @@ -2464,8 +2502,8 @@ "message": "이 Send에 접근하기 위해 암호를 입력하도록 선택적으로 요구합니다.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendPasswordDescV2": { - "message": "Require this password to view the Send.", + "sendPasswordDescV3": { + "message": "Add an optional password for recipients to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendNotesDesc": { @@ -2827,6 +2865,9 @@ "generateUsername": { "message": "아이디 생성" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "아이디 유형" }, @@ -2867,6 +2908,14 @@ "forwardedEmailDesc": { "message": "Generate an email alias with an external forwarding service." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ error: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -4494,9 +4543,15 @@ "itemLocation": { "message": "Item Location" }, + "fileSend": { + "message": "File Send" + }, "fileSends": { "message": "File Sends" }, + "textSend": { + "message": "Text Send" + }, "textSends": { "message": "Text Sends" }, @@ -4559,5 +4614,158 @@ }, "authenticating": { "message": "Authenticating" + }, + "fillGeneratedPassword": { + "message": "Fill generated password", + "description": "Heading for the password generator within the inline menu" + }, + "passwordRegenerated": { + "message": "Password regenerated", + "description": "Notification message for when a password has been regenerated" + }, + "saveLoginToBitwarden": { + "message": "Save login to Bitwarden?", + "description": "Confirmation message for saving a login to Bitwarden" + }, + "spaceCharacterDescriptor": { + "message": "Space", + "description": "Represents the space key in screen reader content as a readable word" + }, + "tildeCharacterDescriptor": { + "message": "Tilde", + "description": "Represents the ~ key in screen reader content as a readable word" + }, + "backtickCharacterDescriptor": { + "message": "Backtick", + "description": "Represents the ` key in screen reader content as a readable word" + }, + "exclamationCharacterDescriptor": { + "message": "Exclamation mark", + "description": "Represents the ! key in screen reader content as a readable word" + }, + "atSignCharacterDescriptor": { + "message": "At sign", + "description": "Represents the @ key in screen reader content as a readable word" + }, + "hashSignCharacterDescriptor": { + "message": "Hash sign", + "description": "Represents the # key in screen reader content as a readable word" + }, + "dollarSignCharacterDescriptor": { + "message": "Dollar sign", + "description": "Represents the $ key in screen reader content as a readable word" + }, + "percentSignCharacterDescriptor": { + "message": "Percent sign", + "description": "Represents the % key in screen reader content as a readable word" + }, + "caretCharacterDescriptor": { + "message": "Caret", + "description": "Represents the ^ key in screen reader content as a readable word" + }, + "ampersandCharacterDescriptor": { + "message": "Ampersand", + "description": "Represents the & key in screen reader content as a readable word" + }, + "asteriskCharacterDescriptor": { + "message": "Asterisk", + "description": "Represents the * key in screen reader content as a readable word" + }, + "parenLeftCharacterDescriptor": { + "message": "Left parenthesis", + "description": "Represents the ( key in screen reader content as a readable word" + }, + "parenRightCharacterDescriptor": { + "message": "Right parenthesis", + "description": "Represents the ) key in screen reader content as a readable word" + }, + "hyphenCharacterDescriptor": { + "message": "Underscore", + "description": "Represents the _ key in screen reader content as a readable word" + }, + "underscoreCharacterDescriptor": { + "message": "Hyphen", + "description": "Represents the - key in screen reader content as a readable word" + }, + "plusCharacterDescriptor": { + "message": "Plus", + "description": "Represents the + key in screen reader content as a readable word" + }, + "equalsCharacterDescriptor": { + "message": "Equals", + "description": "Represents the = key in screen reader content as a readable word" + }, + "braceLeftCharacterDescriptor": { + "message": "Left brace", + "description": "Represents the { key in screen reader content as a readable word" + }, + "braceRightCharacterDescriptor": { + "message": "Right brace", + "description": "Represents the } key in screen reader content as a readable word" + }, + "bracketLeftCharacterDescriptor": { + "message": "Left bracket", + "description": "Represents the [ key in screen reader content as a readable word" + }, + "bracketRightCharacterDescriptor": { + "message": "Right bracket", + "description": "Represents the ] key in screen reader content as a readable word" + }, + "pipeCharacterDescriptor": { + "message": "Pipe", + "description": "Represents the | key in screen reader content as a readable word" + }, + "backSlashCharacterDescriptor": { + "message": "Back slash", + "description": "Represents the back slash key in screen reader content as a readable word" + }, + "colonCharacterDescriptor": { + "message": "Colon", + "description": "Represents the : key in screen reader content as a readable word" + }, + "semicolonCharacterDescriptor": { + "message": "Semicolon", + "description": "Represents the ; key in screen reader content as a readable word" + }, + "doubleQuoteCharacterDescriptor": { + "message": "Double quote", + "description": "Represents the double quote key in screen reader content as a readable word" + }, + "singleQuoteCharacterDescriptor": { + "message": "Single quote", + "description": "Represents the ' key in screen reader content as a readable word" + }, + "lessThanCharacterDescriptor": { + "message": "Less than", + "description": "Represents the < key in screen reader content as a readable word" + }, + "greaterThanCharacterDescriptor": { + "message": "Greater than", + "description": "Represents the > key in screen reader content as a readable word" + }, + "commaCharacterDescriptor": { + "message": "Comma", + "description": "Represents the , key in screen reader content as a readable word" + }, + "periodCharacterDescriptor": { + "message": "Period", + "description": "Represents the . key in screen reader content as a readable word" + }, + "questionCharacterDescriptor": { + "message": "Question mark", + "description": "Represents the ? key in screen reader content as a readable word" + }, + "forwardSlashCharacterDescriptor": { + "message": "Forward slash", + "description": "Represents the / key in screen reader content as a readable word" + }, + "lowercaseAriaLabel": { + "message": "Lowercase" + }, + "uppercaseAriaLabel": { + "message": "Uppercase" + }, + "generatedPassword": { + "message": "Generated password" } } diff --git a/apps/browser/src/_locales/lt/messages.json b/apps/browser/src/_locales/lt/messages.json index fe3d17678ee..660cf62b697 100644 --- a/apps/browser/src/_locales/lt/messages.json +++ b/apps/browser/src/_locales/lt/messages.json @@ -19,6 +19,18 @@ "createAccount": { "message": "Sukurti paskyrą" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" + }, "setAStrongPassword": { "message": "Nustatyti stiprų slaptažodį" }, @@ -107,6 +119,9 @@ "copyPassword": { "message": "Kopijuoti slaptažodį" }, + "copyPassphrase": { + "message": "Copy passphrase" + }, "copyNote": { "message": "Kopijuoti pastabą" }, @@ -152,6 +167,10 @@ "copyNotes": { "message": "Copy notes" }, + "fill": { + "message": "Fill", + "description": "This string is used on the vault page to indicate autofilling. Horizontal space is limited in the interface here so try and keep translations as concise as possible." + }, "autoFill": { "message": "Automatinis užpildymas" }, @@ -407,6 +426,9 @@ "generatePassword": { "message": "Sugeneruoti slaptažodį" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "regeneratePassword": { "message": "Generuoti slaptažodį iš naujo" }, @@ -568,6 +590,15 @@ "launchWebsite": { "message": "Atidaryti svetainę" }, + "launchWebsiteName": { + "message": "Launch website $ITEMNAME$", + "placeholders": { + "itemname": { + "content": "$1", + "example": "Secret item" + } + } + }, "website": { "message": "Tinklapis" }, @@ -814,6 +845,9 @@ "logIn": { "message": "Log in" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "restartRegistration": { "message": "Restart registration" }, @@ -1389,6 +1423,10 @@ "baseUrl": { "message": "Serverio URL" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "API serverio nuoroda" }, @@ -2464,8 +2502,8 @@ "message": "Pasirinktinai reikalauti slaptažodžio, kad vartotojai galėtų pasiekti šį „Send“.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendPasswordDescV2": { - "message": "Require this password to view the Send.", + "sendPasswordDescV3": { + "message": "Add an optional password for recipients to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendNotesDesc": { @@ -2827,6 +2865,9 @@ "generateUsername": { "message": "Generuoti vartotojo vardą" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Vartotojo prisijungimo vardo tipas" }, @@ -2867,6 +2908,14 @@ "forwardedEmailDesc": { "message": "Sugeneruoti el. pašto slapyvardį su išorine persiuntimo paslauga." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "„$SERVICENAME$“ klaida: $ERRORMESSAGE$.", "description": "Reports an error returned by a forwarding service to the user.", @@ -4494,9 +4543,15 @@ "itemLocation": { "message": "Item Location" }, + "fileSend": { + "message": "File Send" + }, "fileSends": { "message": "File Sends" }, + "textSend": { + "message": "Text Send" + }, "textSends": { "message": "Text Sends" }, @@ -4559,5 +4614,158 @@ }, "authenticating": { "message": "Authenticating" + }, + "fillGeneratedPassword": { + "message": "Fill generated password", + "description": "Heading for the password generator within the inline menu" + }, + "passwordRegenerated": { + "message": "Password regenerated", + "description": "Notification message for when a password has been regenerated" + }, + "saveLoginToBitwarden": { + "message": "Save login to Bitwarden?", + "description": "Confirmation message for saving a login to Bitwarden" + }, + "spaceCharacterDescriptor": { + "message": "Space", + "description": "Represents the space key in screen reader content as a readable word" + }, + "tildeCharacterDescriptor": { + "message": "Tilde", + "description": "Represents the ~ key in screen reader content as a readable word" + }, + "backtickCharacterDescriptor": { + "message": "Backtick", + "description": "Represents the ` key in screen reader content as a readable word" + }, + "exclamationCharacterDescriptor": { + "message": "Exclamation mark", + "description": "Represents the ! key in screen reader content as a readable word" + }, + "atSignCharacterDescriptor": { + "message": "At sign", + "description": "Represents the @ key in screen reader content as a readable word" + }, + "hashSignCharacterDescriptor": { + "message": "Hash sign", + "description": "Represents the # key in screen reader content as a readable word" + }, + "dollarSignCharacterDescriptor": { + "message": "Dollar sign", + "description": "Represents the $ key in screen reader content as a readable word" + }, + "percentSignCharacterDescriptor": { + "message": "Percent sign", + "description": "Represents the % key in screen reader content as a readable word" + }, + "caretCharacterDescriptor": { + "message": "Caret", + "description": "Represents the ^ key in screen reader content as a readable word" + }, + "ampersandCharacterDescriptor": { + "message": "Ampersand", + "description": "Represents the & key in screen reader content as a readable word" + }, + "asteriskCharacterDescriptor": { + "message": "Asterisk", + "description": "Represents the * key in screen reader content as a readable word" + }, + "parenLeftCharacterDescriptor": { + "message": "Left parenthesis", + "description": "Represents the ( key in screen reader content as a readable word" + }, + "parenRightCharacterDescriptor": { + "message": "Right parenthesis", + "description": "Represents the ) key in screen reader content as a readable word" + }, + "hyphenCharacterDescriptor": { + "message": "Underscore", + "description": "Represents the _ key in screen reader content as a readable word" + }, + "underscoreCharacterDescriptor": { + "message": "Hyphen", + "description": "Represents the - key in screen reader content as a readable word" + }, + "plusCharacterDescriptor": { + "message": "Plus", + "description": "Represents the + key in screen reader content as a readable word" + }, + "equalsCharacterDescriptor": { + "message": "Equals", + "description": "Represents the = key in screen reader content as a readable word" + }, + "braceLeftCharacterDescriptor": { + "message": "Left brace", + "description": "Represents the { key in screen reader content as a readable word" + }, + "braceRightCharacterDescriptor": { + "message": "Right brace", + "description": "Represents the } key in screen reader content as a readable word" + }, + "bracketLeftCharacterDescriptor": { + "message": "Left bracket", + "description": "Represents the [ key in screen reader content as a readable word" + }, + "bracketRightCharacterDescriptor": { + "message": "Right bracket", + "description": "Represents the ] key in screen reader content as a readable word" + }, + "pipeCharacterDescriptor": { + "message": "Pipe", + "description": "Represents the | key in screen reader content as a readable word" + }, + "backSlashCharacterDescriptor": { + "message": "Back slash", + "description": "Represents the back slash key in screen reader content as a readable word" + }, + "colonCharacterDescriptor": { + "message": "Colon", + "description": "Represents the : key in screen reader content as a readable word" + }, + "semicolonCharacterDescriptor": { + "message": "Semicolon", + "description": "Represents the ; key in screen reader content as a readable word" + }, + "doubleQuoteCharacterDescriptor": { + "message": "Double quote", + "description": "Represents the double quote key in screen reader content as a readable word" + }, + "singleQuoteCharacterDescriptor": { + "message": "Single quote", + "description": "Represents the ' key in screen reader content as a readable word" + }, + "lessThanCharacterDescriptor": { + "message": "Less than", + "description": "Represents the < key in screen reader content as a readable word" + }, + "greaterThanCharacterDescriptor": { + "message": "Greater than", + "description": "Represents the > key in screen reader content as a readable word" + }, + "commaCharacterDescriptor": { + "message": "Comma", + "description": "Represents the , key in screen reader content as a readable word" + }, + "periodCharacterDescriptor": { + "message": "Period", + "description": "Represents the . key in screen reader content as a readable word" + }, + "questionCharacterDescriptor": { + "message": "Question mark", + "description": "Represents the ? key in screen reader content as a readable word" + }, + "forwardSlashCharacterDescriptor": { + "message": "Forward slash", + "description": "Represents the / key in screen reader content as a readable word" + }, + "lowercaseAriaLabel": { + "message": "Lowercase" + }, + "uppercaseAriaLabel": { + "message": "Uppercase" + }, + "generatedPassword": { + "message": "Generated password" } } diff --git a/apps/browser/src/_locales/lv/messages.json b/apps/browser/src/_locales/lv/messages.json index 72622132b4b..56321c97cba 100644 --- a/apps/browser/src/_locales/lv/messages.json +++ b/apps/browser/src/_locales/lv/messages.json @@ -19,6 +19,18 @@ "createAccount": { "message": "Izveidot kontu" }, + "newToBitwarden": { + "message": "Bitwarden iepriekš nav izmantots?" + }, + "logInWithPasskey": { + "message": "Pieteikties ar piekļuves atslēgu" + }, + "useSingleSignOn": { + "message": "Izmantot vienoto pieteikšanos" + }, + "welcomeBack": { + "message": "Laipni lūdzam atpakaļ" + }, "setAStrongPassword": { "message": "Jāiestata droša parole" }, @@ -107,6 +119,9 @@ "copyPassword": { "message": "Ievietot paroli starpliktuvē" }, + "copyPassphrase": { + "message": "Ievietot paroles vārdkopu starpliktuvē" + }, "copyNote": { "message": "Ievietot piezīmi starpliktuvē" }, @@ -152,6 +167,10 @@ "copyNotes": { "message": "Ievietot piezīmes starpliktuvē" }, + "fill": { + "message": "Aizpildīt", + "description": "This string is used on the vault page to indicate autofilling. Horizontal space is limited in the interface here so try and keep translations as concise as possible." + }, "autoFill": { "message": "Automātiskā aizpilde" }, @@ -407,6 +426,9 @@ "generatePassword": { "message": "Veidot paroli" }, + "generatePassphrase": { + "message": "Izveidot paroles vārdkopu" + }, "regeneratePassword": { "message": "Pārizveidot paroli" }, @@ -568,6 +590,15 @@ "launchWebsite": { "message": "Atvērt tīmekļvietni" }, + "launchWebsiteName": { + "message": "Palaist tīmekļvietni $ITEMNAME$", + "placeholders": { + "itemname": { + "content": "$1", + "example": "Secret item" + } + } + }, "website": { "message": "Tīmekļa vietne" }, @@ -814,6 +845,9 @@ "logIn": { "message": "Pieteikties" }, + "logInToBitwarden": { + "message": "Pieteikties Bitwarden" + }, "restartRegistration": { "message": "Sākt reģistrēšanos no jauna" }, @@ -1389,6 +1423,10 @@ "baseUrl": { "message": "Servera URL" }, + "selfHostBaseUrl": { + "message": "Pašmitināta servera URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "API servera URL" }, @@ -2464,8 +2502,8 @@ "message": "Pēc izvēles pieprasīt paroli, lai lietotāji varētu piekļūt šim Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendPasswordDescV2": { - "message": "Pieprasīt šo paroli, lai apskatītu Send.", + "sendPasswordDescV3": { + "message": "Pēc izvēles pievieno paroli, lai saņēmēji varētu piekļūt šim Send!", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendNotesDesc": { @@ -2827,6 +2865,9 @@ "generateUsername": { "message": "Izveidot lietotājvārdu" }, + "generateEmail": { + "message": "Izveidot e-pastu" + }, "usernameType": { "message": "Lietotājvārda veids" }, @@ -2867,6 +2908,14 @@ "forwardedEmailDesc": { "message": "Izveidot e-pastu aizstājvārdu ar ārēju pārvirzīšanas pakalpojumu." }, + "forwarderDomainName": { + "message": "E-pasta domēns", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Jāizvēlas domēns, kuru atbalsta atlasītais pakalpojums", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ kļūda: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -4494,9 +4543,15 @@ "itemLocation": { "message": "Vienuma atrašanās vieta" }, + "fileSend": { + "message": "Datnes Send" + }, "fileSends": { "message": "Datņu Send" }, + "textSend": { + "message": "Teksta Send" + }, "textSends": { "message": "Teksta Send" }, @@ -4559,5 +4614,158 @@ }, "authenticating": { "message": "Autentificē" + }, + "fillGeneratedPassword": { + "message": "Aizpildīt izveidoto paroli", + "description": "Heading for the password generator within the inline menu" + }, + "passwordRegenerated": { + "message": "Parole pārizveidota", + "description": "Notification message for when a password has been regenerated" + }, + "saveLoginToBitwarden": { + "message": "Saglabāt pieteikšanās vienumu Bitwarden?", + "description": "Confirmation message for saving a login to Bitwarden" + }, + "spaceCharacterDescriptor": { + "message": "Atstarpe", + "description": "Represents the space key in screen reader content as a readable word" + }, + "tildeCharacterDescriptor": { + "message": "Tilde", + "description": "Represents the ~ key in screen reader content as a readable word" + }, + "backtickCharacterDescriptor": { + "message": "Atpakaļpēdiņa", + "description": "Represents the ` key in screen reader content as a readable word" + }, + "exclamationCharacterDescriptor": { + "message": "Izsaukuma zīme", + "description": "Represents the ! key in screen reader content as a readable word" + }, + "atSignCharacterDescriptor": { + "message": "At zīme", + "description": "Represents the @ key in screen reader content as a readable word" + }, + "hashSignCharacterDescriptor": { + "message": "Restes zīme", + "description": "Represents the # key in screen reader content as a readable word" + }, + "dollarSignCharacterDescriptor": { + "message": "Dolāra zīme", + "description": "Represents the $ key in screen reader content as a readable word" + }, + "percentSignCharacterDescriptor": { + "message": "Procentu zīme", + "description": "Represents the % key in screen reader content as a readable word" + }, + "caretCharacterDescriptor": { + "message": "Jumtiņš", + "description": "Represents the ^ key in screen reader content as a readable word" + }, + "ampersandCharacterDescriptor": { + "message": "Un zīme", + "description": "Represents the & key in screen reader content as a readable word" + }, + "asteriskCharacterDescriptor": { + "message": "Asterisks", + "description": "Represents the * key in screen reader content as a readable word" + }, + "parenLeftCharacterDescriptor": { + "message": "Kreisās iekavas", + "description": "Represents the ( key in screen reader content as a readable word" + }, + "parenRightCharacterDescriptor": { + "message": "Labās iekavas", + "description": "Represents the ) key in screen reader content as a readable word" + }, + "hyphenCharacterDescriptor": { + "message": "Apakšsvītra", + "description": "Represents the _ key in screen reader content as a readable word" + }, + "underscoreCharacterDescriptor": { + "message": "Defise", + "description": "Represents the - key in screen reader content as a readable word" + }, + "plusCharacterDescriptor": { + "message": "Pluss", + "description": "Represents the + key in screen reader content as a readable word" + }, + "equalsCharacterDescriptor": { + "message": "Vienādības zīme", + "description": "Represents the = key in screen reader content as a readable word" + }, + "braceLeftCharacterDescriptor": { + "message": "Kreisā figūriekava", + "description": "Represents the { key in screen reader content as a readable word" + }, + "braceRightCharacterDescriptor": { + "message": "Labā figūriekava", + "description": "Represents the } key in screen reader content as a readable word" + }, + "bracketLeftCharacterDescriptor": { + "message": "Kreisā kvadrātiekava", + "description": "Represents the [ key in screen reader content as a readable word" + }, + "bracketRightCharacterDescriptor": { + "message": "Labā kvadrātiekava", + "description": "Represents the ] key in screen reader content as a readable word" + }, + "pipeCharacterDescriptor": { + "message": "Stateniska svītra", + "description": "Represents the | key in screen reader content as a readable word" + }, + "backSlashCharacterDescriptor": { + "message": "Atpakaļslīpsvītra", + "description": "Represents the back slash key in screen reader content as a readable word" + }, + "colonCharacterDescriptor": { + "message": "Kols", + "description": "Represents the : key in screen reader content as a readable word" + }, + "semicolonCharacterDescriptor": { + "message": "Semikols", + "description": "Represents the ; key in screen reader content as a readable word" + }, + "doubleQuoteCharacterDescriptor": { + "message": "Divkāršās pēdiņas", + "description": "Represents the double quote key in screen reader content as a readable word" + }, + "singleQuoteCharacterDescriptor": { + "message": "Vienpēdiņas", + "description": "Represents the ' key in screen reader content as a readable word" + }, + "lessThanCharacterDescriptor": { + "message": "Mazāks par", + "description": "Represents the < key in screen reader content as a readable word" + }, + "greaterThanCharacterDescriptor": { + "message": "Lielāks par", + "description": "Represents the > key in screen reader content as a readable word" + }, + "commaCharacterDescriptor": { + "message": "Komats", + "description": "Represents the , key in screen reader content as a readable word" + }, + "periodCharacterDescriptor": { + "message": "Punkts", + "description": "Represents the . key in screen reader content as a readable word" + }, + "questionCharacterDescriptor": { + "message": "Jautājuma zīme", + "description": "Represents the ? key in screen reader content as a readable word" + }, + "forwardSlashCharacterDescriptor": { + "message": "Slīpsvītra", + "description": "Represents the / key in screen reader content as a readable word" + }, + "lowercaseAriaLabel": { + "message": "Mazie burti" + }, + "uppercaseAriaLabel": { + "message": "Lielie burti" + }, + "generatedPassword": { + "message": "Izveidotā parole" } } diff --git a/apps/browser/src/_locales/ml/messages.json b/apps/browser/src/_locales/ml/messages.json index 603324f548f..45576592038 100644 --- a/apps/browser/src/_locales/ml/messages.json +++ b/apps/browser/src/_locales/ml/messages.json @@ -19,6 +19,18 @@ "createAccount": { "message": "അക്കൗണ്ട് സൃഷ്ടിക്കുക" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" + }, "setAStrongPassword": { "message": "Set a strong password" }, @@ -107,6 +119,9 @@ "copyPassword": { "message": "പാസ്‌വേഡ് പകർത്തുക" }, + "copyPassphrase": { + "message": "Copy passphrase" + }, "copyNote": { "message": "കുറിപ്പ് പകർത്തുക" }, @@ -152,6 +167,10 @@ "copyNotes": { "message": "Copy notes" }, + "fill": { + "message": "Fill", + "description": "This string is used on the vault page to indicate autofilling. Horizontal space is limited in the interface here so try and keep translations as concise as possible." + }, "autoFill": { "message": "ഓട്ടോഫിൽ" }, @@ -407,6 +426,9 @@ "generatePassword": { "message": "പാസ്‌വേഡ് സൃഷ്ടിക്കുക" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "regeneratePassword": { "message": "പാസ്സ്‌വേഡ് വീണ്ടും സൃഷ്ടിക്കുക" }, @@ -568,6 +590,15 @@ "launchWebsite": { "message": "Launch website" }, + "launchWebsiteName": { + "message": "Launch website $ITEMNAME$", + "placeholders": { + "itemname": { + "content": "$1", + "example": "Secret item" + } + } + }, "website": { "message": "വെബ്സൈറ്റ്" }, @@ -814,6 +845,9 @@ "logIn": { "message": "Log in" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "restartRegistration": { "message": "Restart registration" }, @@ -1389,6 +1423,10 @@ "baseUrl": { "message": "സെർവർ URL" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "API സെർവർ URL" }, @@ -2464,8 +2502,8 @@ "message": "Optionally require a password for users to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendPasswordDescV2": { - "message": "Require this password to view the Send.", + "sendPasswordDescV3": { + "message": "Add an optional password for recipients to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendNotesDesc": { @@ -2827,6 +2865,9 @@ "generateUsername": { "message": "Generate username" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Username type" }, @@ -2867,6 +2908,14 @@ "forwardedEmailDesc": { "message": "Generate an email alias with an external forwarding service." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ error: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -4494,9 +4543,15 @@ "itemLocation": { "message": "Item Location" }, + "fileSend": { + "message": "File Send" + }, "fileSends": { "message": "File Sends" }, + "textSend": { + "message": "Text Send" + }, "textSends": { "message": "Text Sends" }, @@ -4559,5 +4614,158 @@ }, "authenticating": { "message": "Authenticating" + }, + "fillGeneratedPassword": { + "message": "Fill generated password", + "description": "Heading for the password generator within the inline menu" + }, + "passwordRegenerated": { + "message": "Password regenerated", + "description": "Notification message for when a password has been regenerated" + }, + "saveLoginToBitwarden": { + "message": "Save login to Bitwarden?", + "description": "Confirmation message for saving a login to Bitwarden" + }, + "spaceCharacterDescriptor": { + "message": "Space", + "description": "Represents the space key in screen reader content as a readable word" + }, + "tildeCharacterDescriptor": { + "message": "Tilde", + "description": "Represents the ~ key in screen reader content as a readable word" + }, + "backtickCharacterDescriptor": { + "message": "Backtick", + "description": "Represents the ` key in screen reader content as a readable word" + }, + "exclamationCharacterDescriptor": { + "message": "Exclamation mark", + "description": "Represents the ! key in screen reader content as a readable word" + }, + "atSignCharacterDescriptor": { + "message": "At sign", + "description": "Represents the @ key in screen reader content as a readable word" + }, + "hashSignCharacterDescriptor": { + "message": "Hash sign", + "description": "Represents the # key in screen reader content as a readable word" + }, + "dollarSignCharacterDescriptor": { + "message": "Dollar sign", + "description": "Represents the $ key in screen reader content as a readable word" + }, + "percentSignCharacterDescriptor": { + "message": "Percent sign", + "description": "Represents the % key in screen reader content as a readable word" + }, + "caretCharacterDescriptor": { + "message": "Caret", + "description": "Represents the ^ key in screen reader content as a readable word" + }, + "ampersandCharacterDescriptor": { + "message": "Ampersand", + "description": "Represents the & key in screen reader content as a readable word" + }, + "asteriskCharacterDescriptor": { + "message": "Asterisk", + "description": "Represents the * key in screen reader content as a readable word" + }, + "parenLeftCharacterDescriptor": { + "message": "Left parenthesis", + "description": "Represents the ( key in screen reader content as a readable word" + }, + "parenRightCharacterDescriptor": { + "message": "Right parenthesis", + "description": "Represents the ) key in screen reader content as a readable word" + }, + "hyphenCharacterDescriptor": { + "message": "Underscore", + "description": "Represents the _ key in screen reader content as a readable word" + }, + "underscoreCharacterDescriptor": { + "message": "Hyphen", + "description": "Represents the - key in screen reader content as a readable word" + }, + "plusCharacterDescriptor": { + "message": "Plus", + "description": "Represents the + key in screen reader content as a readable word" + }, + "equalsCharacterDescriptor": { + "message": "Equals", + "description": "Represents the = key in screen reader content as a readable word" + }, + "braceLeftCharacterDescriptor": { + "message": "Left brace", + "description": "Represents the { key in screen reader content as a readable word" + }, + "braceRightCharacterDescriptor": { + "message": "Right brace", + "description": "Represents the } key in screen reader content as a readable word" + }, + "bracketLeftCharacterDescriptor": { + "message": "Left bracket", + "description": "Represents the [ key in screen reader content as a readable word" + }, + "bracketRightCharacterDescriptor": { + "message": "Right bracket", + "description": "Represents the ] key in screen reader content as a readable word" + }, + "pipeCharacterDescriptor": { + "message": "Pipe", + "description": "Represents the | key in screen reader content as a readable word" + }, + "backSlashCharacterDescriptor": { + "message": "Back slash", + "description": "Represents the back slash key in screen reader content as a readable word" + }, + "colonCharacterDescriptor": { + "message": "Colon", + "description": "Represents the : key in screen reader content as a readable word" + }, + "semicolonCharacterDescriptor": { + "message": "Semicolon", + "description": "Represents the ; key in screen reader content as a readable word" + }, + "doubleQuoteCharacterDescriptor": { + "message": "Double quote", + "description": "Represents the double quote key in screen reader content as a readable word" + }, + "singleQuoteCharacterDescriptor": { + "message": "Single quote", + "description": "Represents the ' key in screen reader content as a readable word" + }, + "lessThanCharacterDescriptor": { + "message": "Less than", + "description": "Represents the < key in screen reader content as a readable word" + }, + "greaterThanCharacterDescriptor": { + "message": "Greater than", + "description": "Represents the > key in screen reader content as a readable word" + }, + "commaCharacterDescriptor": { + "message": "Comma", + "description": "Represents the , key in screen reader content as a readable word" + }, + "periodCharacterDescriptor": { + "message": "Period", + "description": "Represents the . key in screen reader content as a readable word" + }, + "questionCharacterDescriptor": { + "message": "Question mark", + "description": "Represents the ? key in screen reader content as a readable word" + }, + "forwardSlashCharacterDescriptor": { + "message": "Forward slash", + "description": "Represents the / key in screen reader content as a readable word" + }, + "lowercaseAriaLabel": { + "message": "Lowercase" + }, + "uppercaseAriaLabel": { + "message": "Uppercase" + }, + "generatedPassword": { + "message": "Generated password" } } diff --git a/apps/browser/src/_locales/mr/messages.json b/apps/browser/src/_locales/mr/messages.json index ae0112d1dd0..083e61c3b94 100644 --- a/apps/browser/src/_locales/mr/messages.json +++ b/apps/browser/src/_locales/mr/messages.json @@ -19,6 +19,18 @@ "createAccount": { "message": "खाते तयार करा" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" + }, "setAStrongPassword": { "message": "Set a strong password" }, @@ -107,6 +119,9 @@ "copyPassword": { "message": "पासवर्ड कॉपी करा" }, + "copyPassphrase": { + "message": "Copy passphrase" + }, "copyNote": { "message": "टीप कॉपी करा" }, @@ -152,6 +167,10 @@ "copyNotes": { "message": "Copy notes" }, + "fill": { + "message": "Fill", + "description": "This string is used on the vault page to indicate autofilling. Horizontal space is limited in the interface here so try and keep translations as concise as possible." + }, "autoFill": { "message": "स्वयंभरण" }, @@ -407,6 +426,9 @@ "generatePassword": { "message": "Generate password" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "regeneratePassword": { "message": "पासवर्ड पुनर्जनित करा" }, @@ -568,6 +590,15 @@ "launchWebsite": { "message": "Launch website" }, + "launchWebsiteName": { + "message": "Launch website $ITEMNAME$", + "placeholders": { + "itemname": { + "content": "$1", + "example": "Secret item" + } + } + }, "website": { "message": "संकेतस्थळ" }, @@ -814,6 +845,9 @@ "logIn": { "message": "Log in" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "restartRegistration": { "message": "Restart registration" }, @@ -1389,6 +1423,10 @@ "baseUrl": { "message": "Server URL" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "API server URL" }, @@ -2464,8 +2502,8 @@ "message": "Optionally require a password for users to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendPasswordDescV2": { - "message": "Require this password to view the Send.", + "sendPasswordDescV3": { + "message": "Add an optional password for recipients to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendNotesDesc": { @@ -2827,6 +2865,9 @@ "generateUsername": { "message": "Generate username" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Username type" }, @@ -2867,6 +2908,14 @@ "forwardedEmailDesc": { "message": "Generate an email alias with an external forwarding service." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ error: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -4494,9 +4543,15 @@ "itemLocation": { "message": "Item Location" }, + "fileSend": { + "message": "File Send" + }, "fileSends": { "message": "File Sends" }, + "textSend": { + "message": "Text Send" + }, "textSends": { "message": "Text Sends" }, @@ -4559,5 +4614,158 @@ }, "authenticating": { "message": "Authenticating" + }, + "fillGeneratedPassword": { + "message": "Fill generated password", + "description": "Heading for the password generator within the inline menu" + }, + "passwordRegenerated": { + "message": "Password regenerated", + "description": "Notification message for when a password has been regenerated" + }, + "saveLoginToBitwarden": { + "message": "Save login to Bitwarden?", + "description": "Confirmation message for saving a login to Bitwarden" + }, + "spaceCharacterDescriptor": { + "message": "Space", + "description": "Represents the space key in screen reader content as a readable word" + }, + "tildeCharacterDescriptor": { + "message": "Tilde", + "description": "Represents the ~ key in screen reader content as a readable word" + }, + "backtickCharacterDescriptor": { + "message": "Backtick", + "description": "Represents the ` key in screen reader content as a readable word" + }, + "exclamationCharacterDescriptor": { + "message": "Exclamation mark", + "description": "Represents the ! key in screen reader content as a readable word" + }, + "atSignCharacterDescriptor": { + "message": "At sign", + "description": "Represents the @ key in screen reader content as a readable word" + }, + "hashSignCharacterDescriptor": { + "message": "Hash sign", + "description": "Represents the # key in screen reader content as a readable word" + }, + "dollarSignCharacterDescriptor": { + "message": "Dollar sign", + "description": "Represents the $ key in screen reader content as a readable word" + }, + "percentSignCharacterDescriptor": { + "message": "Percent sign", + "description": "Represents the % key in screen reader content as a readable word" + }, + "caretCharacterDescriptor": { + "message": "Caret", + "description": "Represents the ^ key in screen reader content as a readable word" + }, + "ampersandCharacterDescriptor": { + "message": "Ampersand", + "description": "Represents the & key in screen reader content as a readable word" + }, + "asteriskCharacterDescriptor": { + "message": "Asterisk", + "description": "Represents the * key in screen reader content as a readable word" + }, + "parenLeftCharacterDescriptor": { + "message": "Left parenthesis", + "description": "Represents the ( key in screen reader content as a readable word" + }, + "parenRightCharacterDescriptor": { + "message": "Right parenthesis", + "description": "Represents the ) key in screen reader content as a readable word" + }, + "hyphenCharacterDescriptor": { + "message": "Underscore", + "description": "Represents the _ key in screen reader content as a readable word" + }, + "underscoreCharacterDescriptor": { + "message": "Hyphen", + "description": "Represents the - key in screen reader content as a readable word" + }, + "plusCharacterDescriptor": { + "message": "Plus", + "description": "Represents the + key in screen reader content as a readable word" + }, + "equalsCharacterDescriptor": { + "message": "Equals", + "description": "Represents the = key in screen reader content as a readable word" + }, + "braceLeftCharacterDescriptor": { + "message": "Left brace", + "description": "Represents the { key in screen reader content as a readable word" + }, + "braceRightCharacterDescriptor": { + "message": "Right brace", + "description": "Represents the } key in screen reader content as a readable word" + }, + "bracketLeftCharacterDescriptor": { + "message": "Left bracket", + "description": "Represents the [ key in screen reader content as a readable word" + }, + "bracketRightCharacterDescriptor": { + "message": "Right bracket", + "description": "Represents the ] key in screen reader content as a readable word" + }, + "pipeCharacterDescriptor": { + "message": "Pipe", + "description": "Represents the | key in screen reader content as a readable word" + }, + "backSlashCharacterDescriptor": { + "message": "Back slash", + "description": "Represents the back slash key in screen reader content as a readable word" + }, + "colonCharacterDescriptor": { + "message": "Colon", + "description": "Represents the : key in screen reader content as a readable word" + }, + "semicolonCharacterDescriptor": { + "message": "Semicolon", + "description": "Represents the ; key in screen reader content as a readable word" + }, + "doubleQuoteCharacterDescriptor": { + "message": "Double quote", + "description": "Represents the double quote key in screen reader content as a readable word" + }, + "singleQuoteCharacterDescriptor": { + "message": "Single quote", + "description": "Represents the ' key in screen reader content as a readable word" + }, + "lessThanCharacterDescriptor": { + "message": "Less than", + "description": "Represents the < key in screen reader content as a readable word" + }, + "greaterThanCharacterDescriptor": { + "message": "Greater than", + "description": "Represents the > key in screen reader content as a readable word" + }, + "commaCharacterDescriptor": { + "message": "Comma", + "description": "Represents the , key in screen reader content as a readable word" + }, + "periodCharacterDescriptor": { + "message": "Period", + "description": "Represents the . key in screen reader content as a readable word" + }, + "questionCharacterDescriptor": { + "message": "Question mark", + "description": "Represents the ? key in screen reader content as a readable word" + }, + "forwardSlashCharacterDescriptor": { + "message": "Forward slash", + "description": "Represents the / key in screen reader content as a readable word" + }, + "lowercaseAriaLabel": { + "message": "Lowercase" + }, + "uppercaseAriaLabel": { + "message": "Uppercase" + }, + "generatedPassword": { + "message": "Generated password" } } diff --git a/apps/browser/src/_locales/my/messages.json b/apps/browser/src/_locales/my/messages.json index 290663f4347..4037887cb28 100644 --- a/apps/browser/src/_locales/my/messages.json +++ b/apps/browser/src/_locales/my/messages.json @@ -19,6 +19,18 @@ "createAccount": { "message": "Create account" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" + }, "setAStrongPassword": { "message": "Set a strong password" }, @@ -107,6 +119,9 @@ "copyPassword": { "message": "Copy password" }, + "copyPassphrase": { + "message": "Copy passphrase" + }, "copyNote": { "message": "Copy note" }, @@ -152,6 +167,10 @@ "copyNotes": { "message": "Copy notes" }, + "fill": { + "message": "Fill", + "description": "This string is used on the vault page to indicate autofilling. Horizontal space is limited in the interface here so try and keep translations as concise as possible." + }, "autoFill": { "message": "Autofill" }, @@ -407,6 +426,9 @@ "generatePassword": { "message": "Generate password" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "regeneratePassword": { "message": "Regenerate password" }, @@ -568,6 +590,15 @@ "launchWebsite": { "message": "Launch website" }, + "launchWebsiteName": { + "message": "Launch website $ITEMNAME$", + "placeholders": { + "itemname": { + "content": "$1", + "example": "Secret item" + } + } + }, "website": { "message": "Website" }, @@ -814,6 +845,9 @@ "logIn": { "message": "Log in" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "restartRegistration": { "message": "Restart registration" }, @@ -1389,6 +1423,10 @@ "baseUrl": { "message": "Server URL" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "API server URL" }, @@ -2464,8 +2502,8 @@ "message": "Optionally require a password for users to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendPasswordDescV2": { - "message": "Require this password to view the Send.", + "sendPasswordDescV3": { + "message": "Add an optional password for recipients to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendNotesDesc": { @@ -2827,6 +2865,9 @@ "generateUsername": { "message": "Generate username" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Username type" }, @@ -2867,6 +2908,14 @@ "forwardedEmailDesc": { "message": "Generate an email alias with an external forwarding service." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ error: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -4494,9 +4543,15 @@ "itemLocation": { "message": "Item Location" }, + "fileSend": { + "message": "File Send" + }, "fileSends": { "message": "File Sends" }, + "textSend": { + "message": "Text Send" + }, "textSends": { "message": "Text Sends" }, @@ -4559,5 +4614,158 @@ }, "authenticating": { "message": "Authenticating" + }, + "fillGeneratedPassword": { + "message": "Fill generated password", + "description": "Heading for the password generator within the inline menu" + }, + "passwordRegenerated": { + "message": "Password regenerated", + "description": "Notification message for when a password has been regenerated" + }, + "saveLoginToBitwarden": { + "message": "Save login to Bitwarden?", + "description": "Confirmation message for saving a login to Bitwarden" + }, + "spaceCharacterDescriptor": { + "message": "Space", + "description": "Represents the space key in screen reader content as a readable word" + }, + "tildeCharacterDescriptor": { + "message": "Tilde", + "description": "Represents the ~ key in screen reader content as a readable word" + }, + "backtickCharacterDescriptor": { + "message": "Backtick", + "description": "Represents the ` key in screen reader content as a readable word" + }, + "exclamationCharacterDescriptor": { + "message": "Exclamation mark", + "description": "Represents the ! key in screen reader content as a readable word" + }, + "atSignCharacterDescriptor": { + "message": "At sign", + "description": "Represents the @ key in screen reader content as a readable word" + }, + "hashSignCharacterDescriptor": { + "message": "Hash sign", + "description": "Represents the # key in screen reader content as a readable word" + }, + "dollarSignCharacterDescriptor": { + "message": "Dollar sign", + "description": "Represents the $ key in screen reader content as a readable word" + }, + "percentSignCharacterDescriptor": { + "message": "Percent sign", + "description": "Represents the % key in screen reader content as a readable word" + }, + "caretCharacterDescriptor": { + "message": "Caret", + "description": "Represents the ^ key in screen reader content as a readable word" + }, + "ampersandCharacterDescriptor": { + "message": "Ampersand", + "description": "Represents the & key in screen reader content as a readable word" + }, + "asteriskCharacterDescriptor": { + "message": "Asterisk", + "description": "Represents the * key in screen reader content as a readable word" + }, + "parenLeftCharacterDescriptor": { + "message": "Left parenthesis", + "description": "Represents the ( key in screen reader content as a readable word" + }, + "parenRightCharacterDescriptor": { + "message": "Right parenthesis", + "description": "Represents the ) key in screen reader content as a readable word" + }, + "hyphenCharacterDescriptor": { + "message": "Underscore", + "description": "Represents the _ key in screen reader content as a readable word" + }, + "underscoreCharacterDescriptor": { + "message": "Hyphen", + "description": "Represents the - key in screen reader content as a readable word" + }, + "plusCharacterDescriptor": { + "message": "Plus", + "description": "Represents the + key in screen reader content as a readable word" + }, + "equalsCharacterDescriptor": { + "message": "Equals", + "description": "Represents the = key in screen reader content as a readable word" + }, + "braceLeftCharacterDescriptor": { + "message": "Left brace", + "description": "Represents the { key in screen reader content as a readable word" + }, + "braceRightCharacterDescriptor": { + "message": "Right brace", + "description": "Represents the } key in screen reader content as a readable word" + }, + "bracketLeftCharacterDescriptor": { + "message": "Left bracket", + "description": "Represents the [ key in screen reader content as a readable word" + }, + "bracketRightCharacterDescriptor": { + "message": "Right bracket", + "description": "Represents the ] key in screen reader content as a readable word" + }, + "pipeCharacterDescriptor": { + "message": "Pipe", + "description": "Represents the | key in screen reader content as a readable word" + }, + "backSlashCharacterDescriptor": { + "message": "Back slash", + "description": "Represents the back slash key in screen reader content as a readable word" + }, + "colonCharacterDescriptor": { + "message": "Colon", + "description": "Represents the : key in screen reader content as a readable word" + }, + "semicolonCharacterDescriptor": { + "message": "Semicolon", + "description": "Represents the ; key in screen reader content as a readable word" + }, + "doubleQuoteCharacterDescriptor": { + "message": "Double quote", + "description": "Represents the double quote key in screen reader content as a readable word" + }, + "singleQuoteCharacterDescriptor": { + "message": "Single quote", + "description": "Represents the ' key in screen reader content as a readable word" + }, + "lessThanCharacterDescriptor": { + "message": "Less than", + "description": "Represents the < key in screen reader content as a readable word" + }, + "greaterThanCharacterDescriptor": { + "message": "Greater than", + "description": "Represents the > key in screen reader content as a readable word" + }, + "commaCharacterDescriptor": { + "message": "Comma", + "description": "Represents the , key in screen reader content as a readable word" + }, + "periodCharacterDescriptor": { + "message": "Period", + "description": "Represents the . key in screen reader content as a readable word" + }, + "questionCharacterDescriptor": { + "message": "Question mark", + "description": "Represents the ? key in screen reader content as a readable word" + }, + "forwardSlashCharacterDescriptor": { + "message": "Forward slash", + "description": "Represents the / key in screen reader content as a readable word" + }, + "lowercaseAriaLabel": { + "message": "Lowercase" + }, + "uppercaseAriaLabel": { + "message": "Uppercase" + }, + "generatedPassword": { + "message": "Generated password" } } diff --git a/apps/browser/src/_locales/nb/messages.json b/apps/browser/src/_locales/nb/messages.json index 3f634e272a6..19c1fd0e89e 100644 --- a/apps/browser/src/_locales/nb/messages.json +++ b/apps/browser/src/_locales/nb/messages.json @@ -19,6 +19,18 @@ "createAccount": { "message": "Opprett en konto" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" + }, "setAStrongPassword": { "message": "Set a strong password" }, @@ -107,6 +119,9 @@ "copyPassword": { "message": "Kopier passordet" }, + "copyPassphrase": { + "message": "Copy passphrase" + }, "copyNote": { "message": "Kopier notatet" }, @@ -152,6 +167,10 @@ "copyNotes": { "message": "Copy notes" }, + "fill": { + "message": "Fill", + "description": "This string is used on the vault page to indicate autofilling. Horizontal space is limited in the interface here so try and keep translations as concise as possible." + }, "autoFill": { "message": "Auto-utfylling" }, @@ -407,6 +426,9 @@ "generatePassword": { "message": "Generer et passord" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "regeneratePassword": { "message": "Omgenerer et passord" }, @@ -568,6 +590,15 @@ "launchWebsite": { "message": "Launch website" }, + "launchWebsiteName": { + "message": "Launch website $ITEMNAME$", + "placeholders": { + "itemname": { + "content": "$1", + "example": "Secret item" + } + } + }, "website": { "message": "Nettsted" }, @@ -814,6 +845,9 @@ "logIn": { "message": "Log in" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "restartRegistration": { "message": "Restart registration" }, @@ -1389,6 +1423,10 @@ "baseUrl": { "message": "Tjener-nettadresse" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "API-tjenernettadresse" }, @@ -2464,8 +2502,8 @@ "message": "Eventuelt krever et passord for brukere å få tilgang til denne Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendPasswordDescV2": { - "message": "Require this password to view the Send.", + "sendPasswordDescV3": { + "message": "Add an optional password for recipients to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendNotesDesc": { @@ -2827,6 +2865,9 @@ "generateUsername": { "message": "Generer brukernavn" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Brukernavntype" }, @@ -2867,6 +2908,14 @@ "forwardedEmailDesc": { "message": "Generer et e-postalias med en ekstern videresendingstjeneste." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ error: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -4494,9 +4543,15 @@ "itemLocation": { "message": "Item Location" }, + "fileSend": { + "message": "File Send" + }, "fileSends": { "message": "File Sends" }, + "textSend": { + "message": "Text Send" + }, "textSends": { "message": "Text Sends" }, @@ -4559,5 +4614,158 @@ }, "authenticating": { "message": "Authenticating" + }, + "fillGeneratedPassword": { + "message": "Fill generated password", + "description": "Heading for the password generator within the inline menu" + }, + "passwordRegenerated": { + "message": "Password regenerated", + "description": "Notification message for when a password has been regenerated" + }, + "saveLoginToBitwarden": { + "message": "Save login to Bitwarden?", + "description": "Confirmation message for saving a login to Bitwarden" + }, + "spaceCharacterDescriptor": { + "message": "Space", + "description": "Represents the space key in screen reader content as a readable word" + }, + "tildeCharacterDescriptor": { + "message": "Tilde", + "description": "Represents the ~ key in screen reader content as a readable word" + }, + "backtickCharacterDescriptor": { + "message": "Backtick", + "description": "Represents the ` key in screen reader content as a readable word" + }, + "exclamationCharacterDescriptor": { + "message": "Exclamation mark", + "description": "Represents the ! key in screen reader content as a readable word" + }, + "atSignCharacterDescriptor": { + "message": "At sign", + "description": "Represents the @ key in screen reader content as a readable word" + }, + "hashSignCharacterDescriptor": { + "message": "Hash sign", + "description": "Represents the # key in screen reader content as a readable word" + }, + "dollarSignCharacterDescriptor": { + "message": "Dollar sign", + "description": "Represents the $ key in screen reader content as a readable word" + }, + "percentSignCharacterDescriptor": { + "message": "Percent sign", + "description": "Represents the % key in screen reader content as a readable word" + }, + "caretCharacterDescriptor": { + "message": "Caret", + "description": "Represents the ^ key in screen reader content as a readable word" + }, + "ampersandCharacterDescriptor": { + "message": "Ampersand", + "description": "Represents the & key in screen reader content as a readable word" + }, + "asteriskCharacterDescriptor": { + "message": "Asterisk", + "description": "Represents the * key in screen reader content as a readable word" + }, + "parenLeftCharacterDescriptor": { + "message": "Left parenthesis", + "description": "Represents the ( key in screen reader content as a readable word" + }, + "parenRightCharacterDescriptor": { + "message": "Right parenthesis", + "description": "Represents the ) key in screen reader content as a readable word" + }, + "hyphenCharacterDescriptor": { + "message": "Underscore", + "description": "Represents the _ key in screen reader content as a readable word" + }, + "underscoreCharacterDescriptor": { + "message": "Hyphen", + "description": "Represents the - key in screen reader content as a readable word" + }, + "plusCharacterDescriptor": { + "message": "Plus", + "description": "Represents the + key in screen reader content as a readable word" + }, + "equalsCharacterDescriptor": { + "message": "Equals", + "description": "Represents the = key in screen reader content as a readable word" + }, + "braceLeftCharacterDescriptor": { + "message": "Left brace", + "description": "Represents the { key in screen reader content as a readable word" + }, + "braceRightCharacterDescriptor": { + "message": "Right brace", + "description": "Represents the } key in screen reader content as a readable word" + }, + "bracketLeftCharacterDescriptor": { + "message": "Left bracket", + "description": "Represents the [ key in screen reader content as a readable word" + }, + "bracketRightCharacterDescriptor": { + "message": "Right bracket", + "description": "Represents the ] key in screen reader content as a readable word" + }, + "pipeCharacterDescriptor": { + "message": "Pipe", + "description": "Represents the | key in screen reader content as a readable word" + }, + "backSlashCharacterDescriptor": { + "message": "Back slash", + "description": "Represents the back slash key in screen reader content as a readable word" + }, + "colonCharacterDescriptor": { + "message": "Colon", + "description": "Represents the : key in screen reader content as a readable word" + }, + "semicolonCharacterDescriptor": { + "message": "Semicolon", + "description": "Represents the ; key in screen reader content as a readable word" + }, + "doubleQuoteCharacterDescriptor": { + "message": "Double quote", + "description": "Represents the double quote key in screen reader content as a readable word" + }, + "singleQuoteCharacterDescriptor": { + "message": "Single quote", + "description": "Represents the ' key in screen reader content as a readable word" + }, + "lessThanCharacterDescriptor": { + "message": "Less than", + "description": "Represents the < key in screen reader content as a readable word" + }, + "greaterThanCharacterDescriptor": { + "message": "Greater than", + "description": "Represents the > key in screen reader content as a readable word" + }, + "commaCharacterDescriptor": { + "message": "Comma", + "description": "Represents the , key in screen reader content as a readable word" + }, + "periodCharacterDescriptor": { + "message": "Period", + "description": "Represents the . key in screen reader content as a readable word" + }, + "questionCharacterDescriptor": { + "message": "Question mark", + "description": "Represents the ? key in screen reader content as a readable word" + }, + "forwardSlashCharacterDescriptor": { + "message": "Forward slash", + "description": "Represents the / key in screen reader content as a readable word" + }, + "lowercaseAriaLabel": { + "message": "Lowercase" + }, + "uppercaseAriaLabel": { + "message": "Uppercase" + }, + "generatedPassword": { + "message": "Generated password" } } diff --git a/apps/browser/src/_locales/ne/messages.json b/apps/browser/src/_locales/ne/messages.json index 290663f4347..4037887cb28 100644 --- a/apps/browser/src/_locales/ne/messages.json +++ b/apps/browser/src/_locales/ne/messages.json @@ -19,6 +19,18 @@ "createAccount": { "message": "Create account" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" + }, "setAStrongPassword": { "message": "Set a strong password" }, @@ -107,6 +119,9 @@ "copyPassword": { "message": "Copy password" }, + "copyPassphrase": { + "message": "Copy passphrase" + }, "copyNote": { "message": "Copy note" }, @@ -152,6 +167,10 @@ "copyNotes": { "message": "Copy notes" }, + "fill": { + "message": "Fill", + "description": "This string is used on the vault page to indicate autofilling. Horizontal space is limited in the interface here so try and keep translations as concise as possible." + }, "autoFill": { "message": "Autofill" }, @@ -407,6 +426,9 @@ "generatePassword": { "message": "Generate password" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "regeneratePassword": { "message": "Regenerate password" }, @@ -568,6 +590,15 @@ "launchWebsite": { "message": "Launch website" }, + "launchWebsiteName": { + "message": "Launch website $ITEMNAME$", + "placeholders": { + "itemname": { + "content": "$1", + "example": "Secret item" + } + } + }, "website": { "message": "Website" }, @@ -814,6 +845,9 @@ "logIn": { "message": "Log in" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "restartRegistration": { "message": "Restart registration" }, @@ -1389,6 +1423,10 @@ "baseUrl": { "message": "Server URL" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "API server URL" }, @@ -2464,8 +2502,8 @@ "message": "Optionally require a password for users to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendPasswordDescV2": { - "message": "Require this password to view the Send.", + "sendPasswordDescV3": { + "message": "Add an optional password for recipients to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendNotesDesc": { @@ -2827,6 +2865,9 @@ "generateUsername": { "message": "Generate username" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Username type" }, @@ -2867,6 +2908,14 @@ "forwardedEmailDesc": { "message": "Generate an email alias with an external forwarding service." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ error: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -4494,9 +4543,15 @@ "itemLocation": { "message": "Item Location" }, + "fileSend": { + "message": "File Send" + }, "fileSends": { "message": "File Sends" }, + "textSend": { + "message": "Text Send" + }, "textSends": { "message": "Text Sends" }, @@ -4559,5 +4614,158 @@ }, "authenticating": { "message": "Authenticating" + }, + "fillGeneratedPassword": { + "message": "Fill generated password", + "description": "Heading for the password generator within the inline menu" + }, + "passwordRegenerated": { + "message": "Password regenerated", + "description": "Notification message for when a password has been regenerated" + }, + "saveLoginToBitwarden": { + "message": "Save login to Bitwarden?", + "description": "Confirmation message for saving a login to Bitwarden" + }, + "spaceCharacterDescriptor": { + "message": "Space", + "description": "Represents the space key in screen reader content as a readable word" + }, + "tildeCharacterDescriptor": { + "message": "Tilde", + "description": "Represents the ~ key in screen reader content as a readable word" + }, + "backtickCharacterDescriptor": { + "message": "Backtick", + "description": "Represents the ` key in screen reader content as a readable word" + }, + "exclamationCharacterDescriptor": { + "message": "Exclamation mark", + "description": "Represents the ! key in screen reader content as a readable word" + }, + "atSignCharacterDescriptor": { + "message": "At sign", + "description": "Represents the @ key in screen reader content as a readable word" + }, + "hashSignCharacterDescriptor": { + "message": "Hash sign", + "description": "Represents the # key in screen reader content as a readable word" + }, + "dollarSignCharacterDescriptor": { + "message": "Dollar sign", + "description": "Represents the $ key in screen reader content as a readable word" + }, + "percentSignCharacterDescriptor": { + "message": "Percent sign", + "description": "Represents the % key in screen reader content as a readable word" + }, + "caretCharacterDescriptor": { + "message": "Caret", + "description": "Represents the ^ key in screen reader content as a readable word" + }, + "ampersandCharacterDescriptor": { + "message": "Ampersand", + "description": "Represents the & key in screen reader content as a readable word" + }, + "asteriskCharacterDescriptor": { + "message": "Asterisk", + "description": "Represents the * key in screen reader content as a readable word" + }, + "parenLeftCharacterDescriptor": { + "message": "Left parenthesis", + "description": "Represents the ( key in screen reader content as a readable word" + }, + "parenRightCharacterDescriptor": { + "message": "Right parenthesis", + "description": "Represents the ) key in screen reader content as a readable word" + }, + "hyphenCharacterDescriptor": { + "message": "Underscore", + "description": "Represents the _ key in screen reader content as a readable word" + }, + "underscoreCharacterDescriptor": { + "message": "Hyphen", + "description": "Represents the - key in screen reader content as a readable word" + }, + "plusCharacterDescriptor": { + "message": "Plus", + "description": "Represents the + key in screen reader content as a readable word" + }, + "equalsCharacterDescriptor": { + "message": "Equals", + "description": "Represents the = key in screen reader content as a readable word" + }, + "braceLeftCharacterDescriptor": { + "message": "Left brace", + "description": "Represents the { key in screen reader content as a readable word" + }, + "braceRightCharacterDescriptor": { + "message": "Right brace", + "description": "Represents the } key in screen reader content as a readable word" + }, + "bracketLeftCharacterDescriptor": { + "message": "Left bracket", + "description": "Represents the [ key in screen reader content as a readable word" + }, + "bracketRightCharacterDescriptor": { + "message": "Right bracket", + "description": "Represents the ] key in screen reader content as a readable word" + }, + "pipeCharacterDescriptor": { + "message": "Pipe", + "description": "Represents the | key in screen reader content as a readable word" + }, + "backSlashCharacterDescriptor": { + "message": "Back slash", + "description": "Represents the back slash key in screen reader content as a readable word" + }, + "colonCharacterDescriptor": { + "message": "Colon", + "description": "Represents the : key in screen reader content as a readable word" + }, + "semicolonCharacterDescriptor": { + "message": "Semicolon", + "description": "Represents the ; key in screen reader content as a readable word" + }, + "doubleQuoteCharacterDescriptor": { + "message": "Double quote", + "description": "Represents the double quote key in screen reader content as a readable word" + }, + "singleQuoteCharacterDescriptor": { + "message": "Single quote", + "description": "Represents the ' key in screen reader content as a readable word" + }, + "lessThanCharacterDescriptor": { + "message": "Less than", + "description": "Represents the < key in screen reader content as a readable word" + }, + "greaterThanCharacterDescriptor": { + "message": "Greater than", + "description": "Represents the > key in screen reader content as a readable word" + }, + "commaCharacterDescriptor": { + "message": "Comma", + "description": "Represents the , key in screen reader content as a readable word" + }, + "periodCharacterDescriptor": { + "message": "Period", + "description": "Represents the . key in screen reader content as a readable word" + }, + "questionCharacterDescriptor": { + "message": "Question mark", + "description": "Represents the ? key in screen reader content as a readable word" + }, + "forwardSlashCharacterDescriptor": { + "message": "Forward slash", + "description": "Represents the / key in screen reader content as a readable word" + }, + "lowercaseAriaLabel": { + "message": "Lowercase" + }, + "uppercaseAriaLabel": { + "message": "Uppercase" + }, + "generatedPassword": { + "message": "Generated password" } } diff --git a/apps/browser/src/_locales/nl/messages.json b/apps/browser/src/_locales/nl/messages.json index 878518531f8..c36a0018b90 100644 --- a/apps/browser/src/_locales/nl/messages.json +++ b/apps/browser/src/_locales/nl/messages.json @@ -19,6 +19,18 @@ "createAccount": { "message": "Account aanmaken" }, + "newToBitwarden": { + "message": "Nieuw bij Bitwarden?" + }, + "logInWithPasskey": { + "message": "Inloggen met passkey" + }, + "useSingleSignOn": { + "message": "Single sign-on gebruiken" + }, + "welcomeBack": { + "message": "Welkom terug" + }, "setAStrongPassword": { "message": "Sterk wachtwoord instellen" }, @@ -107,6 +119,9 @@ "copyPassword": { "message": "Wachtwoord kopiëren" }, + "copyPassphrase": { + "message": "Wachtwoordzin kopiëren" + }, "copyNote": { "message": "Notitie kopiëren" }, @@ -152,6 +167,10 @@ "copyNotes": { "message": "Notities kopiëren" }, + "fill": { + "message": "Invullen", + "description": "This string is used on the vault page to indicate autofilling. Horizontal space is limited in the interface here so try and keep translations as concise as possible." + }, "autoFill": { "message": "Auto-invullen" }, @@ -407,6 +426,9 @@ "generatePassword": { "message": "Wachtwoord genereren" }, + "generatePassphrase": { + "message": "Wachtwoordzin genereren" + }, "regeneratePassword": { "message": "Wachtwoord opnieuw genereren" }, @@ -568,6 +590,15 @@ "launchWebsite": { "message": "Website openen" }, + "launchWebsiteName": { + "message": "Start website $ITEMNAME$", + "placeholders": { + "itemname": { + "content": "$1", + "example": "Secret item" + } + } + }, "website": { "message": "Website" }, @@ -814,6 +845,9 @@ "logIn": { "message": "Inloggen" }, + "logInToBitwarden": { + "message": "Inloggen op Bitwarden" + }, "restartRegistration": { "message": "Registratie herstarten" }, @@ -1389,6 +1423,10 @@ "baseUrl": { "message": "Server-URL" }, + "selfHostBaseUrl": { + "message": "URL zelfgehoste server", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "API server-URL" }, @@ -2464,8 +2502,8 @@ "message": "Vereis optioneel een wachtwoord voor gebruikers om toegang te krijgen tot deze Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendPasswordDescV2": { - "message": "Dit wachtwoord vereisen voor het weergeven van de Send.", + "sendPasswordDescV3": { + "message": "Voeg een optioneel wachtwoord toe voor ontvangers om toegang te krijgen tot deze Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendNotesDesc": { @@ -2827,6 +2865,9 @@ "generateUsername": { "message": "Gebruikersnamen genereren" }, + "generateEmail": { + "message": "E-mailadres genereren" + }, "usernameType": { "message": "Type gebruikersnaam" }, @@ -2867,6 +2908,14 @@ "forwardedEmailDesc": { "message": "Genereer een e-mailalias met een externe doorschakelservice." }, + "forwarderDomainName": { + "message": "E-maildomein", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Kies een domein dat wordt ondersteund door de geselecteerde dienst", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ error: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -4494,9 +4543,15 @@ "itemLocation": { "message": "Itemlocatie" }, + "fileSend": { + "message": "Bestand-Sends" + }, "fileSends": { "message": "Bestand-Sends" }, + "textSend": { + "message": "Text Send" + }, "textSends": { "message": "Tekst-Sends" }, @@ -4559,5 +4614,158 @@ }, "authenticating": { "message": "Aan het inloggen" + }, + "fillGeneratedPassword": { + "message": "Fill generated password", + "description": "Heading for the password generator within the inline menu" + }, + "passwordRegenerated": { + "message": "Password regenerated", + "description": "Notification message for when a password has been regenerated" + }, + "saveLoginToBitwarden": { + "message": "Save login to Bitwarden?", + "description": "Confirmation message for saving a login to Bitwarden" + }, + "spaceCharacterDescriptor": { + "message": "Space", + "description": "Represents the space key in screen reader content as a readable word" + }, + "tildeCharacterDescriptor": { + "message": "Tilde", + "description": "Represents the ~ key in screen reader content as a readable word" + }, + "backtickCharacterDescriptor": { + "message": "Backtick", + "description": "Represents the ` key in screen reader content as a readable word" + }, + "exclamationCharacterDescriptor": { + "message": "Exclamation mark", + "description": "Represents the ! key in screen reader content as a readable word" + }, + "atSignCharacterDescriptor": { + "message": "At sign", + "description": "Represents the @ key in screen reader content as a readable word" + }, + "hashSignCharacterDescriptor": { + "message": "Hash sign", + "description": "Represents the # key in screen reader content as a readable word" + }, + "dollarSignCharacterDescriptor": { + "message": "Dollar sign", + "description": "Represents the $ key in screen reader content as a readable word" + }, + "percentSignCharacterDescriptor": { + "message": "Percent sign", + "description": "Represents the % key in screen reader content as a readable word" + }, + "caretCharacterDescriptor": { + "message": "Caret", + "description": "Represents the ^ key in screen reader content as a readable word" + }, + "ampersandCharacterDescriptor": { + "message": "Ampersand", + "description": "Represents the & key in screen reader content as a readable word" + }, + "asteriskCharacterDescriptor": { + "message": "Asterisk", + "description": "Represents the * key in screen reader content as a readable word" + }, + "parenLeftCharacterDescriptor": { + "message": "Left parenthesis", + "description": "Represents the ( key in screen reader content as a readable word" + }, + "parenRightCharacterDescriptor": { + "message": "Right parenthesis", + "description": "Represents the ) key in screen reader content as a readable word" + }, + "hyphenCharacterDescriptor": { + "message": "Underscore", + "description": "Represents the _ key in screen reader content as a readable word" + }, + "underscoreCharacterDescriptor": { + "message": "Hyphen", + "description": "Represents the - key in screen reader content as a readable word" + }, + "plusCharacterDescriptor": { + "message": "Plus", + "description": "Represents the + key in screen reader content as a readable word" + }, + "equalsCharacterDescriptor": { + "message": "Equals", + "description": "Represents the = key in screen reader content as a readable word" + }, + "braceLeftCharacterDescriptor": { + "message": "Left brace", + "description": "Represents the { key in screen reader content as a readable word" + }, + "braceRightCharacterDescriptor": { + "message": "Right brace", + "description": "Represents the } key in screen reader content as a readable word" + }, + "bracketLeftCharacterDescriptor": { + "message": "Left bracket", + "description": "Represents the [ key in screen reader content as a readable word" + }, + "bracketRightCharacterDescriptor": { + "message": "Right bracket", + "description": "Represents the ] key in screen reader content as a readable word" + }, + "pipeCharacterDescriptor": { + "message": "Pipe", + "description": "Represents the | key in screen reader content as a readable word" + }, + "backSlashCharacterDescriptor": { + "message": "Back slash", + "description": "Represents the back slash key in screen reader content as a readable word" + }, + "colonCharacterDescriptor": { + "message": "Colon", + "description": "Represents the : key in screen reader content as a readable word" + }, + "semicolonCharacterDescriptor": { + "message": "Semicolon", + "description": "Represents the ; key in screen reader content as a readable word" + }, + "doubleQuoteCharacterDescriptor": { + "message": "Double quote", + "description": "Represents the double quote key in screen reader content as a readable word" + }, + "singleQuoteCharacterDescriptor": { + "message": "Single quote", + "description": "Represents the ' key in screen reader content as a readable word" + }, + "lessThanCharacterDescriptor": { + "message": "Less than", + "description": "Represents the < key in screen reader content as a readable word" + }, + "greaterThanCharacterDescriptor": { + "message": "Greater than", + "description": "Represents the > key in screen reader content as a readable word" + }, + "commaCharacterDescriptor": { + "message": "Comma", + "description": "Represents the , key in screen reader content as a readable word" + }, + "periodCharacterDescriptor": { + "message": "Period", + "description": "Represents the . key in screen reader content as a readable word" + }, + "questionCharacterDescriptor": { + "message": "Question mark", + "description": "Represents the ? key in screen reader content as a readable word" + }, + "forwardSlashCharacterDescriptor": { + "message": "Forward slash", + "description": "Represents the / key in screen reader content as a readable word" + }, + "lowercaseAriaLabel": { + "message": "Lowercase" + }, + "uppercaseAriaLabel": { + "message": "Uppercase" + }, + "generatedPassword": { + "message": "Generated password" } } diff --git a/apps/browser/src/_locales/nn/messages.json b/apps/browser/src/_locales/nn/messages.json index 290663f4347..4037887cb28 100644 --- a/apps/browser/src/_locales/nn/messages.json +++ b/apps/browser/src/_locales/nn/messages.json @@ -19,6 +19,18 @@ "createAccount": { "message": "Create account" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" + }, "setAStrongPassword": { "message": "Set a strong password" }, @@ -107,6 +119,9 @@ "copyPassword": { "message": "Copy password" }, + "copyPassphrase": { + "message": "Copy passphrase" + }, "copyNote": { "message": "Copy note" }, @@ -152,6 +167,10 @@ "copyNotes": { "message": "Copy notes" }, + "fill": { + "message": "Fill", + "description": "This string is used on the vault page to indicate autofilling. Horizontal space is limited in the interface here so try and keep translations as concise as possible." + }, "autoFill": { "message": "Autofill" }, @@ -407,6 +426,9 @@ "generatePassword": { "message": "Generate password" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "regeneratePassword": { "message": "Regenerate password" }, @@ -568,6 +590,15 @@ "launchWebsite": { "message": "Launch website" }, + "launchWebsiteName": { + "message": "Launch website $ITEMNAME$", + "placeholders": { + "itemname": { + "content": "$1", + "example": "Secret item" + } + } + }, "website": { "message": "Website" }, @@ -814,6 +845,9 @@ "logIn": { "message": "Log in" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "restartRegistration": { "message": "Restart registration" }, @@ -1389,6 +1423,10 @@ "baseUrl": { "message": "Server URL" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "API server URL" }, @@ -2464,8 +2502,8 @@ "message": "Optionally require a password for users to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendPasswordDescV2": { - "message": "Require this password to view the Send.", + "sendPasswordDescV3": { + "message": "Add an optional password for recipients to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendNotesDesc": { @@ -2827,6 +2865,9 @@ "generateUsername": { "message": "Generate username" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Username type" }, @@ -2867,6 +2908,14 @@ "forwardedEmailDesc": { "message": "Generate an email alias with an external forwarding service." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ error: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -4494,9 +4543,15 @@ "itemLocation": { "message": "Item Location" }, + "fileSend": { + "message": "File Send" + }, "fileSends": { "message": "File Sends" }, + "textSend": { + "message": "Text Send" + }, "textSends": { "message": "Text Sends" }, @@ -4559,5 +4614,158 @@ }, "authenticating": { "message": "Authenticating" + }, + "fillGeneratedPassword": { + "message": "Fill generated password", + "description": "Heading for the password generator within the inline menu" + }, + "passwordRegenerated": { + "message": "Password regenerated", + "description": "Notification message for when a password has been regenerated" + }, + "saveLoginToBitwarden": { + "message": "Save login to Bitwarden?", + "description": "Confirmation message for saving a login to Bitwarden" + }, + "spaceCharacterDescriptor": { + "message": "Space", + "description": "Represents the space key in screen reader content as a readable word" + }, + "tildeCharacterDescriptor": { + "message": "Tilde", + "description": "Represents the ~ key in screen reader content as a readable word" + }, + "backtickCharacterDescriptor": { + "message": "Backtick", + "description": "Represents the ` key in screen reader content as a readable word" + }, + "exclamationCharacterDescriptor": { + "message": "Exclamation mark", + "description": "Represents the ! key in screen reader content as a readable word" + }, + "atSignCharacterDescriptor": { + "message": "At sign", + "description": "Represents the @ key in screen reader content as a readable word" + }, + "hashSignCharacterDescriptor": { + "message": "Hash sign", + "description": "Represents the # key in screen reader content as a readable word" + }, + "dollarSignCharacterDescriptor": { + "message": "Dollar sign", + "description": "Represents the $ key in screen reader content as a readable word" + }, + "percentSignCharacterDescriptor": { + "message": "Percent sign", + "description": "Represents the % key in screen reader content as a readable word" + }, + "caretCharacterDescriptor": { + "message": "Caret", + "description": "Represents the ^ key in screen reader content as a readable word" + }, + "ampersandCharacterDescriptor": { + "message": "Ampersand", + "description": "Represents the & key in screen reader content as a readable word" + }, + "asteriskCharacterDescriptor": { + "message": "Asterisk", + "description": "Represents the * key in screen reader content as a readable word" + }, + "parenLeftCharacterDescriptor": { + "message": "Left parenthesis", + "description": "Represents the ( key in screen reader content as a readable word" + }, + "parenRightCharacterDescriptor": { + "message": "Right parenthesis", + "description": "Represents the ) key in screen reader content as a readable word" + }, + "hyphenCharacterDescriptor": { + "message": "Underscore", + "description": "Represents the _ key in screen reader content as a readable word" + }, + "underscoreCharacterDescriptor": { + "message": "Hyphen", + "description": "Represents the - key in screen reader content as a readable word" + }, + "plusCharacterDescriptor": { + "message": "Plus", + "description": "Represents the + key in screen reader content as a readable word" + }, + "equalsCharacterDescriptor": { + "message": "Equals", + "description": "Represents the = key in screen reader content as a readable word" + }, + "braceLeftCharacterDescriptor": { + "message": "Left brace", + "description": "Represents the { key in screen reader content as a readable word" + }, + "braceRightCharacterDescriptor": { + "message": "Right brace", + "description": "Represents the } key in screen reader content as a readable word" + }, + "bracketLeftCharacterDescriptor": { + "message": "Left bracket", + "description": "Represents the [ key in screen reader content as a readable word" + }, + "bracketRightCharacterDescriptor": { + "message": "Right bracket", + "description": "Represents the ] key in screen reader content as a readable word" + }, + "pipeCharacterDescriptor": { + "message": "Pipe", + "description": "Represents the | key in screen reader content as a readable word" + }, + "backSlashCharacterDescriptor": { + "message": "Back slash", + "description": "Represents the back slash key in screen reader content as a readable word" + }, + "colonCharacterDescriptor": { + "message": "Colon", + "description": "Represents the : key in screen reader content as a readable word" + }, + "semicolonCharacterDescriptor": { + "message": "Semicolon", + "description": "Represents the ; key in screen reader content as a readable word" + }, + "doubleQuoteCharacterDescriptor": { + "message": "Double quote", + "description": "Represents the double quote key in screen reader content as a readable word" + }, + "singleQuoteCharacterDescriptor": { + "message": "Single quote", + "description": "Represents the ' key in screen reader content as a readable word" + }, + "lessThanCharacterDescriptor": { + "message": "Less than", + "description": "Represents the < key in screen reader content as a readable word" + }, + "greaterThanCharacterDescriptor": { + "message": "Greater than", + "description": "Represents the > key in screen reader content as a readable word" + }, + "commaCharacterDescriptor": { + "message": "Comma", + "description": "Represents the , key in screen reader content as a readable word" + }, + "periodCharacterDescriptor": { + "message": "Period", + "description": "Represents the . key in screen reader content as a readable word" + }, + "questionCharacterDescriptor": { + "message": "Question mark", + "description": "Represents the ? key in screen reader content as a readable word" + }, + "forwardSlashCharacterDescriptor": { + "message": "Forward slash", + "description": "Represents the / key in screen reader content as a readable word" + }, + "lowercaseAriaLabel": { + "message": "Lowercase" + }, + "uppercaseAriaLabel": { + "message": "Uppercase" + }, + "generatedPassword": { + "message": "Generated password" } } diff --git a/apps/browser/src/_locales/or/messages.json b/apps/browser/src/_locales/or/messages.json index 290663f4347..4037887cb28 100644 --- a/apps/browser/src/_locales/or/messages.json +++ b/apps/browser/src/_locales/or/messages.json @@ -19,6 +19,18 @@ "createAccount": { "message": "Create account" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" + }, "setAStrongPassword": { "message": "Set a strong password" }, @@ -107,6 +119,9 @@ "copyPassword": { "message": "Copy password" }, + "copyPassphrase": { + "message": "Copy passphrase" + }, "copyNote": { "message": "Copy note" }, @@ -152,6 +167,10 @@ "copyNotes": { "message": "Copy notes" }, + "fill": { + "message": "Fill", + "description": "This string is used on the vault page to indicate autofilling. Horizontal space is limited in the interface here so try and keep translations as concise as possible." + }, "autoFill": { "message": "Autofill" }, @@ -407,6 +426,9 @@ "generatePassword": { "message": "Generate password" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "regeneratePassword": { "message": "Regenerate password" }, @@ -568,6 +590,15 @@ "launchWebsite": { "message": "Launch website" }, + "launchWebsiteName": { + "message": "Launch website $ITEMNAME$", + "placeholders": { + "itemname": { + "content": "$1", + "example": "Secret item" + } + } + }, "website": { "message": "Website" }, @@ -814,6 +845,9 @@ "logIn": { "message": "Log in" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "restartRegistration": { "message": "Restart registration" }, @@ -1389,6 +1423,10 @@ "baseUrl": { "message": "Server URL" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "API server URL" }, @@ -2464,8 +2502,8 @@ "message": "Optionally require a password for users to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendPasswordDescV2": { - "message": "Require this password to view the Send.", + "sendPasswordDescV3": { + "message": "Add an optional password for recipients to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendNotesDesc": { @@ -2827,6 +2865,9 @@ "generateUsername": { "message": "Generate username" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Username type" }, @@ -2867,6 +2908,14 @@ "forwardedEmailDesc": { "message": "Generate an email alias with an external forwarding service." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ error: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -4494,9 +4543,15 @@ "itemLocation": { "message": "Item Location" }, + "fileSend": { + "message": "File Send" + }, "fileSends": { "message": "File Sends" }, + "textSend": { + "message": "Text Send" + }, "textSends": { "message": "Text Sends" }, @@ -4559,5 +4614,158 @@ }, "authenticating": { "message": "Authenticating" + }, + "fillGeneratedPassword": { + "message": "Fill generated password", + "description": "Heading for the password generator within the inline menu" + }, + "passwordRegenerated": { + "message": "Password regenerated", + "description": "Notification message for when a password has been regenerated" + }, + "saveLoginToBitwarden": { + "message": "Save login to Bitwarden?", + "description": "Confirmation message for saving a login to Bitwarden" + }, + "spaceCharacterDescriptor": { + "message": "Space", + "description": "Represents the space key in screen reader content as a readable word" + }, + "tildeCharacterDescriptor": { + "message": "Tilde", + "description": "Represents the ~ key in screen reader content as a readable word" + }, + "backtickCharacterDescriptor": { + "message": "Backtick", + "description": "Represents the ` key in screen reader content as a readable word" + }, + "exclamationCharacterDescriptor": { + "message": "Exclamation mark", + "description": "Represents the ! key in screen reader content as a readable word" + }, + "atSignCharacterDescriptor": { + "message": "At sign", + "description": "Represents the @ key in screen reader content as a readable word" + }, + "hashSignCharacterDescriptor": { + "message": "Hash sign", + "description": "Represents the # key in screen reader content as a readable word" + }, + "dollarSignCharacterDescriptor": { + "message": "Dollar sign", + "description": "Represents the $ key in screen reader content as a readable word" + }, + "percentSignCharacterDescriptor": { + "message": "Percent sign", + "description": "Represents the % key in screen reader content as a readable word" + }, + "caretCharacterDescriptor": { + "message": "Caret", + "description": "Represents the ^ key in screen reader content as a readable word" + }, + "ampersandCharacterDescriptor": { + "message": "Ampersand", + "description": "Represents the & key in screen reader content as a readable word" + }, + "asteriskCharacterDescriptor": { + "message": "Asterisk", + "description": "Represents the * key in screen reader content as a readable word" + }, + "parenLeftCharacterDescriptor": { + "message": "Left parenthesis", + "description": "Represents the ( key in screen reader content as a readable word" + }, + "parenRightCharacterDescriptor": { + "message": "Right parenthesis", + "description": "Represents the ) key in screen reader content as a readable word" + }, + "hyphenCharacterDescriptor": { + "message": "Underscore", + "description": "Represents the _ key in screen reader content as a readable word" + }, + "underscoreCharacterDescriptor": { + "message": "Hyphen", + "description": "Represents the - key in screen reader content as a readable word" + }, + "plusCharacterDescriptor": { + "message": "Plus", + "description": "Represents the + key in screen reader content as a readable word" + }, + "equalsCharacterDescriptor": { + "message": "Equals", + "description": "Represents the = key in screen reader content as a readable word" + }, + "braceLeftCharacterDescriptor": { + "message": "Left brace", + "description": "Represents the { key in screen reader content as a readable word" + }, + "braceRightCharacterDescriptor": { + "message": "Right brace", + "description": "Represents the } key in screen reader content as a readable word" + }, + "bracketLeftCharacterDescriptor": { + "message": "Left bracket", + "description": "Represents the [ key in screen reader content as a readable word" + }, + "bracketRightCharacterDescriptor": { + "message": "Right bracket", + "description": "Represents the ] key in screen reader content as a readable word" + }, + "pipeCharacterDescriptor": { + "message": "Pipe", + "description": "Represents the | key in screen reader content as a readable word" + }, + "backSlashCharacterDescriptor": { + "message": "Back slash", + "description": "Represents the back slash key in screen reader content as a readable word" + }, + "colonCharacterDescriptor": { + "message": "Colon", + "description": "Represents the : key in screen reader content as a readable word" + }, + "semicolonCharacterDescriptor": { + "message": "Semicolon", + "description": "Represents the ; key in screen reader content as a readable word" + }, + "doubleQuoteCharacterDescriptor": { + "message": "Double quote", + "description": "Represents the double quote key in screen reader content as a readable word" + }, + "singleQuoteCharacterDescriptor": { + "message": "Single quote", + "description": "Represents the ' key in screen reader content as a readable word" + }, + "lessThanCharacterDescriptor": { + "message": "Less than", + "description": "Represents the < key in screen reader content as a readable word" + }, + "greaterThanCharacterDescriptor": { + "message": "Greater than", + "description": "Represents the > key in screen reader content as a readable word" + }, + "commaCharacterDescriptor": { + "message": "Comma", + "description": "Represents the , key in screen reader content as a readable word" + }, + "periodCharacterDescriptor": { + "message": "Period", + "description": "Represents the . key in screen reader content as a readable word" + }, + "questionCharacterDescriptor": { + "message": "Question mark", + "description": "Represents the ? key in screen reader content as a readable word" + }, + "forwardSlashCharacterDescriptor": { + "message": "Forward slash", + "description": "Represents the / key in screen reader content as a readable word" + }, + "lowercaseAriaLabel": { + "message": "Lowercase" + }, + "uppercaseAriaLabel": { + "message": "Uppercase" + }, + "generatedPassword": { + "message": "Generated password" } } diff --git a/apps/browser/src/_locales/pl/messages.json b/apps/browser/src/_locales/pl/messages.json index 7340d09f8df..b71035cfaff 100644 --- a/apps/browser/src/_locales/pl/messages.json +++ b/apps/browser/src/_locales/pl/messages.json @@ -19,6 +19,18 @@ "createAccount": { "message": "Utwórz konto" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" + }, "setAStrongPassword": { "message": "Ustaw silne hasło" }, @@ -107,6 +119,9 @@ "copyPassword": { "message": "Kopiuj hasło" }, + "copyPassphrase": { + "message": "Copy passphrase" + }, "copyNote": { "message": "Kopiuj notatkę" }, @@ -152,6 +167,10 @@ "copyNotes": { "message": "Kopiuj notatki" }, + "fill": { + "message": "Fill", + "description": "This string is used on the vault page to indicate autofilling. Horizontal space is limited in the interface here so try and keep translations as concise as possible." + }, "autoFill": { "message": "Autouzupełnianie" }, @@ -407,6 +426,9 @@ "generatePassword": { "message": "Wygeneruj hasło" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "regeneratePassword": { "message": "Wygeneruj ponownie hasło" }, @@ -568,6 +590,15 @@ "launchWebsite": { "message": "Otwórz stronę" }, + "launchWebsiteName": { + "message": "Launch website $ITEMNAME$", + "placeholders": { + "itemname": { + "content": "$1", + "example": "Secret item" + } + } + }, "website": { "message": "Strona" }, @@ -814,6 +845,9 @@ "logIn": { "message": "Zaloguj się" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "restartRegistration": { "message": "Zrestartuj rejestrację" }, @@ -1389,6 +1423,10 @@ "baseUrl": { "message": "Adres URL serwera" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "Adres URL serwera API" }, @@ -2464,8 +2502,8 @@ "message": "Opcjonalne hasło dla użytkownika, aby uzyskać dostęp do wysyłki.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendPasswordDescV2": { - "message": "Wymagaj tego hasła aby wyświetlić Send.", + "sendPasswordDescV3": { + "message": "Add an optional password for recipients to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendNotesDesc": { @@ -2827,6 +2865,9 @@ "generateUsername": { "message": "Wygeneruj nazwę użytkownika" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Rodzaj nazwy użytkownika" }, @@ -2867,6 +2908,14 @@ "forwardedEmailDesc": { "message": "Wygeneruj alias adresu e-mail z zewnętrznej usługi przekierowania." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "Błąd $SERVICENAME$: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -4494,9 +4543,15 @@ "itemLocation": { "message": "Lokalizacja elementu" }, + "fileSend": { + "message": "File Send" + }, "fileSends": { "message": "File Sends" }, + "textSend": { + "message": "Text Send" + }, "textSends": { "message": "Text Sends" }, @@ -4559,5 +4614,158 @@ }, "authenticating": { "message": "Authenticating" + }, + "fillGeneratedPassword": { + "message": "Fill generated password", + "description": "Heading for the password generator within the inline menu" + }, + "passwordRegenerated": { + "message": "Password regenerated", + "description": "Notification message for when a password has been regenerated" + }, + "saveLoginToBitwarden": { + "message": "Save login to Bitwarden?", + "description": "Confirmation message for saving a login to Bitwarden" + }, + "spaceCharacterDescriptor": { + "message": "Space", + "description": "Represents the space key in screen reader content as a readable word" + }, + "tildeCharacterDescriptor": { + "message": "Tilde", + "description": "Represents the ~ key in screen reader content as a readable word" + }, + "backtickCharacterDescriptor": { + "message": "Backtick", + "description": "Represents the ` key in screen reader content as a readable word" + }, + "exclamationCharacterDescriptor": { + "message": "Exclamation mark", + "description": "Represents the ! key in screen reader content as a readable word" + }, + "atSignCharacterDescriptor": { + "message": "At sign", + "description": "Represents the @ key in screen reader content as a readable word" + }, + "hashSignCharacterDescriptor": { + "message": "Hash sign", + "description": "Represents the # key in screen reader content as a readable word" + }, + "dollarSignCharacterDescriptor": { + "message": "Dollar sign", + "description": "Represents the $ key in screen reader content as a readable word" + }, + "percentSignCharacterDescriptor": { + "message": "Percent sign", + "description": "Represents the % key in screen reader content as a readable word" + }, + "caretCharacterDescriptor": { + "message": "Caret", + "description": "Represents the ^ key in screen reader content as a readable word" + }, + "ampersandCharacterDescriptor": { + "message": "Ampersand", + "description": "Represents the & key in screen reader content as a readable word" + }, + "asteriskCharacterDescriptor": { + "message": "Asterisk", + "description": "Represents the * key in screen reader content as a readable word" + }, + "parenLeftCharacterDescriptor": { + "message": "Left parenthesis", + "description": "Represents the ( key in screen reader content as a readable word" + }, + "parenRightCharacterDescriptor": { + "message": "Right parenthesis", + "description": "Represents the ) key in screen reader content as a readable word" + }, + "hyphenCharacterDescriptor": { + "message": "Underscore", + "description": "Represents the _ key in screen reader content as a readable word" + }, + "underscoreCharacterDescriptor": { + "message": "Hyphen", + "description": "Represents the - key in screen reader content as a readable word" + }, + "plusCharacterDescriptor": { + "message": "Plus", + "description": "Represents the + key in screen reader content as a readable word" + }, + "equalsCharacterDescriptor": { + "message": "Equals", + "description": "Represents the = key in screen reader content as a readable word" + }, + "braceLeftCharacterDescriptor": { + "message": "Left brace", + "description": "Represents the { key in screen reader content as a readable word" + }, + "braceRightCharacterDescriptor": { + "message": "Right brace", + "description": "Represents the } key in screen reader content as a readable word" + }, + "bracketLeftCharacterDescriptor": { + "message": "Left bracket", + "description": "Represents the [ key in screen reader content as a readable word" + }, + "bracketRightCharacterDescriptor": { + "message": "Right bracket", + "description": "Represents the ] key in screen reader content as a readable word" + }, + "pipeCharacterDescriptor": { + "message": "Pipe", + "description": "Represents the | key in screen reader content as a readable word" + }, + "backSlashCharacterDescriptor": { + "message": "Back slash", + "description": "Represents the back slash key in screen reader content as a readable word" + }, + "colonCharacterDescriptor": { + "message": "Colon", + "description": "Represents the : key in screen reader content as a readable word" + }, + "semicolonCharacterDescriptor": { + "message": "Semicolon", + "description": "Represents the ; key in screen reader content as a readable word" + }, + "doubleQuoteCharacterDescriptor": { + "message": "Double quote", + "description": "Represents the double quote key in screen reader content as a readable word" + }, + "singleQuoteCharacterDescriptor": { + "message": "Single quote", + "description": "Represents the ' key in screen reader content as a readable word" + }, + "lessThanCharacterDescriptor": { + "message": "Less than", + "description": "Represents the < key in screen reader content as a readable word" + }, + "greaterThanCharacterDescriptor": { + "message": "Greater than", + "description": "Represents the > key in screen reader content as a readable word" + }, + "commaCharacterDescriptor": { + "message": "Comma", + "description": "Represents the , key in screen reader content as a readable word" + }, + "periodCharacterDescriptor": { + "message": "Period", + "description": "Represents the . key in screen reader content as a readable word" + }, + "questionCharacterDescriptor": { + "message": "Question mark", + "description": "Represents the ? key in screen reader content as a readable word" + }, + "forwardSlashCharacterDescriptor": { + "message": "Forward slash", + "description": "Represents the / key in screen reader content as a readable word" + }, + "lowercaseAriaLabel": { + "message": "Lowercase" + }, + "uppercaseAriaLabel": { + "message": "Uppercase" + }, + "generatedPassword": { + "message": "Generated password" } } diff --git a/apps/browser/src/_locales/pt_BR/messages.json b/apps/browser/src/_locales/pt_BR/messages.json index 11a277c41b6..a5509efc487 100644 --- a/apps/browser/src/_locales/pt_BR/messages.json +++ b/apps/browser/src/_locales/pt_BR/messages.json @@ -19,6 +19,18 @@ "createAccount": { "message": "Criar Conta" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" + }, "setAStrongPassword": { "message": "Defina uma senha forte" }, @@ -107,6 +119,9 @@ "copyPassword": { "message": "Copiar Senha" }, + "copyPassphrase": { + "message": "Copy passphrase" + }, "copyNote": { "message": "Copiar Nota" }, @@ -152,6 +167,10 @@ "copyNotes": { "message": "Copiar Notas" }, + "fill": { + "message": "Fill", + "description": "This string is used on the vault page to indicate autofilling. Horizontal space is limited in the interface here so try and keep translations as concise as possible." + }, "autoFill": { "message": "Autopreencher" }, @@ -407,6 +426,9 @@ "generatePassword": { "message": "Gerar Senha" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "regeneratePassword": { "message": "Gerar Nova Senha" }, @@ -568,6 +590,15 @@ "launchWebsite": { "message": "Abrir site" }, + "launchWebsiteName": { + "message": "Launch website $ITEMNAME$", + "placeholders": { + "itemname": { + "content": "$1", + "example": "Secret item" + } + } + }, "website": { "message": "Site" }, @@ -814,6 +845,9 @@ "logIn": { "message": "Fazer login" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "restartRegistration": { "message": "Reiniciar registro" }, @@ -1389,6 +1423,10 @@ "baseUrl": { "message": "URL do Servidor" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "URL do Servidor da API" }, @@ -2464,8 +2502,8 @@ "message": "Exigir opcionalmente uma senha para os usuários acessarem este Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendPasswordDescV2": { - "message": "Exigir essa senha para visualizar o Envio.", + "sendPasswordDescV3": { + "message": "Add an optional password for recipients to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendNotesDesc": { @@ -2827,6 +2865,9 @@ "generateUsername": { "message": "Gerar Usuário" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Tipo de usuário" }, @@ -2867,6 +2908,14 @@ "forwardedEmailDesc": { "message": "Gere um apelido de e-mail com um serviço de encaminhamento externo." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "Erro $SERVICENAME$: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -4494,9 +4543,15 @@ "itemLocation": { "message": "Localização do Item" }, + "fileSend": { + "message": "File Send" + }, "fileSends": { "message": "Arquivos enviados" }, + "textSend": { + "message": "Text Send" + }, "textSends": { "message": "Texto enviado" }, @@ -4559,5 +4614,158 @@ }, "authenticating": { "message": "Autenticando" + }, + "fillGeneratedPassword": { + "message": "Fill generated password", + "description": "Heading for the password generator within the inline menu" + }, + "passwordRegenerated": { + "message": "Password regenerated", + "description": "Notification message for when a password has been regenerated" + }, + "saveLoginToBitwarden": { + "message": "Save login to Bitwarden?", + "description": "Confirmation message for saving a login to Bitwarden" + }, + "spaceCharacterDescriptor": { + "message": "Space", + "description": "Represents the space key in screen reader content as a readable word" + }, + "tildeCharacterDescriptor": { + "message": "Tilde", + "description": "Represents the ~ key in screen reader content as a readable word" + }, + "backtickCharacterDescriptor": { + "message": "Backtick", + "description": "Represents the ` key in screen reader content as a readable word" + }, + "exclamationCharacterDescriptor": { + "message": "Exclamation mark", + "description": "Represents the ! key in screen reader content as a readable word" + }, + "atSignCharacterDescriptor": { + "message": "At sign", + "description": "Represents the @ key in screen reader content as a readable word" + }, + "hashSignCharacterDescriptor": { + "message": "Hash sign", + "description": "Represents the # key in screen reader content as a readable word" + }, + "dollarSignCharacterDescriptor": { + "message": "Dollar sign", + "description": "Represents the $ key in screen reader content as a readable word" + }, + "percentSignCharacterDescriptor": { + "message": "Percent sign", + "description": "Represents the % key in screen reader content as a readable word" + }, + "caretCharacterDescriptor": { + "message": "Caret", + "description": "Represents the ^ key in screen reader content as a readable word" + }, + "ampersandCharacterDescriptor": { + "message": "Ampersand", + "description": "Represents the & key in screen reader content as a readable word" + }, + "asteriskCharacterDescriptor": { + "message": "Asterisk", + "description": "Represents the * key in screen reader content as a readable word" + }, + "parenLeftCharacterDescriptor": { + "message": "Left parenthesis", + "description": "Represents the ( key in screen reader content as a readable word" + }, + "parenRightCharacterDescriptor": { + "message": "Right parenthesis", + "description": "Represents the ) key in screen reader content as a readable word" + }, + "hyphenCharacterDescriptor": { + "message": "Underscore", + "description": "Represents the _ key in screen reader content as a readable word" + }, + "underscoreCharacterDescriptor": { + "message": "Hyphen", + "description": "Represents the - key in screen reader content as a readable word" + }, + "plusCharacterDescriptor": { + "message": "Plus", + "description": "Represents the + key in screen reader content as a readable word" + }, + "equalsCharacterDescriptor": { + "message": "Equals", + "description": "Represents the = key in screen reader content as a readable word" + }, + "braceLeftCharacterDescriptor": { + "message": "Left brace", + "description": "Represents the { key in screen reader content as a readable word" + }, + "braceRightCharacterDescriptor": { + "message": "Right brace", + "description": "Represents the } key in screen reader content as a readable word" + }, + "bracketLeftCharacterDescriptor": { + "message": "Left bracket", + "description": "Represents the [ key in screen reader content as a readable word" + }, + "bracketRightCharacterDescriptor": { + "message": "Right bracket", + "description": "Represents the ] key in screen reader content as a readable word" + }, + "pipeCharacterDescriptor": { + "message": "Pipe", + "description": "Represents the | key in screen reader content as a readable word" + }, + "backSlashCharacterDescriptor": { + "message": "Back slash", + "description": "Represents the back slash key in screen reader content as a readable word" + }, + "colonCharacterDescriptor": { + "message": "Colon", + "description": "Represents the : key in screen reader content as a readable word" + }, + "semicolonCharacterDescriptor": { + "message": "Semicolon", + "description": "Represents the ; key in screen reader content as a readable word" + }, + "doubleQuoteCharacterDescriptor": { + "message": "Double quote", + "description": "Represents the double quote key in screen reader content as a readable word" + }, + "singleQuoteCharacterDescriptor": { + "message": "Single quote", + "description": "Represents the ' key in screen reader content as a readable word" + }, + "lessThanCharacterDescriptor": { + "message": "Less than", + "description": "Represents the < key in screen reader content as a readable word" + }, + "greaterThanCharacterDescriptor": { + "message": "Greater than", + "description": "Represents the > key in screen reader content as a readable word" + }, + "commaCharacterDescriptor": { + "message": "Comma", + "description": "Represents the , key in screen reader content as a readable word" + }, + "periodCharacterDescriptor": { + "message": "Period", + "description": "Represents the . key in screen reader content as a readable word" + }, + "questionCharacterDescriptor": { + "message": "Question mark", + "description": "Represents the ? key in screen reader content as a readable word" + }, + "forwardSlashCharacterDescriptor": { + "message": "Forward slash", + "description": "Represents the / key in screen reader content as a readable word" + }, + "lowercaseAriaLabel": { + "message": "Lowercase" + }, + "uppercaseAriaLabel": { + "message": "Uppercase" + }, + "generatedPassword": { + "message": "Generated password" } } diff --git a/apps/browser/src/_locales/pt_PT/messages.json b/apps/browser/src/_locales/pt_PT/messages.json index 2bdb2b5a935..244f700dac3 100644 --- a/apps/browser/src/_locales/pt_PT/messages.json +++ b/apps/browser/src/_locales/pt_PT/messages.json @@ -19,6 +19,18 @@ "createAccount": { "message": "Criar conta" }, + "newToBitwarden": { + "message": "Novo no Bitwarden?" + }, + "logInWithPasskey": { + "message": "Iniciar sessão com a chave de acesso" + }, + "useSingleSignOn": { + "message": "Utilizar início de sessão único" + }, + "welcomeBack": { + "message": "Bem-vindo de volta" + }, "setAStrongPassword": { "message": "Defina uma palavra-passe forte" }, @@ -107,6 +119,9 @@ "copyPassword": { "message": "Copiar palavra-passe" }, + "copyPassphrase": { + "message": "Copiar frase de acesso" + }, "copyNote": { "message": "Copiar nota" }, @@ -152,6 +167,10 @@ "copyNotes": { "message": "Copiar notas" }, + "fill": { + "message": "Preencher", + "description": "This string is used on the vault page to indicate autofilling. Horizontal space is limited in the interface here so try and keep translations as concise as possible." + }, "autoFill": { "message": "Preencher automaticamente" }, @@ -407,6 +426,9 @@ "generatePassword": { "message": "Gerar palavra-passe" }, + "generatePassphrase": { + "message": "Gerar frase de acesso" + }, "regeneratePassword": { "message": "Regenerar palavra-passe" }, @@ -568,6 +590,15 @@ "launchWebsite": { "message": "Iniciar site" }, + "launchWebsiteName": { + "message": "Iniciar site $ITEMNAME$", + "placeholders": { + "itemname": { + "content": "$1", + "example": "Secret item" + } + } + }, "website": { "message": "Site" }, @@ -814,6 +845,9 @@ "logIn": { "message": "Iniciar sessão" }, + "logInToBitwarden": { + "message": "Iniciar sessão no Bitwarden" + }, "restartRegistration": { "message": "Reiniciar registo" }, @@ -1389,6 +1423,10 @@ "baseUrl": { "message": "URL do servidor" }, + "selfHostBaseUrl": { + "message": "URL do servidor auto-hospedado", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "URL do servidor da API" }, @@ -2464,8 +2502,8 @@ "message": "Opcionalmente, exigir uma palavra-passe para os utilizadores acederem a este Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendPasswordDescV2": { - "message": "Esta palavra-passe é necessária para visualizar o Send.", + "sendPasswordDescV3": { + "message": "Adicione uma palavra-passe opcional para os destinatários acederem a este Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendNotesDesc": { @@ -2827,6 +2865,9 @@ "generateUsername": { "message": "Gerar nome de utilizador" }, + "generateEmail": { + "message": "Gerar e-mail" + }, "usernameType": { "message": "Tipo de nome de utilizador" }, @@ -2867,6 +2908,14 @@ "forwardedEmailDesc": { "message": "Gerar um alias de e-mail com um serviço de reencaminhamento externo." }, + "forwarderDomainName": { + "message": "Domínio de e-mail", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Escolha um domínio que seja suportado pelo serviço selecionado", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "Erro no $SERVICENAME$: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -4494,9 +4543,15 @@ "itemLocation": { "message": "Localização do item" }, + "fileSend": { + "message": "Send de ficheiro" + }, "fileSends": { "message": "Sends de ficheiros" }, + "textSend": { + "message": "Send de texto" + }, "textSends": { "message": "Sends de texto" }, @@ -4559,5 +4614,158 @@ }, "authenticating": { "message": "A autenticar" + }, + "fillGeneratedPassword": { + "message": "Preencher a palavra-passe gerada", + "description": "Heading for the password generator within the inline menu" + }, + "passwordRegenerated": { + "message": "Palavra-passe gerada novamente", + "description": "Notification message for when a password has been regenerated" + }, + "saveLoginToBitwarden": { + "message": "Guardar credencial no Bitwarden?", + "description": "Confirmation message for saving a login to Bitwarden" + }, + "spaceCharacterDescriptor": { + "message": "Espaço", + "description": "Represents the space key in screen reader content as a readable word" + }, + "tildeCharacterDescriptor": { + "message": "Til", + "description": "Represents the ~ key in screen reader content as a readable word" + }, + "backtickCharacterDescriptor": { + "message": "Backtick", + "description": "Represents the ` key in screen reader content as a readable word" + }, + "exclamationCharacterDescriptor": { + "message": "Ponto de exclamação", + "description": "Represents the ! key in screen reader content as a readable word" + }, + "atSignCharacterDescriptor": { + "message": "At sign", + "description": "Represents the @ key in screen reader content as a readable word" + }, + "hashSignCharacterDescriptor": { + "message": "Hash sign", + "description": "Represents the # key in screen reader content as a readable word" + }, + "dollarSignCharacterDescriptor": { + "message": "Dollar sign", + "description": "Represents the $ key in screen reader content as a readable word" + }, + "percentSignCharacterDescriptor": { + "message": "Percent sign", + "description": "Represents the % key in screen reader content as a readable word" + }, + "caretCharacterDescriptor": { + "message": "Caret", + "description": "Represents the ^ key in screen reader content as a readable word" + }, + "ampersandCharacterDescriptor": { + "message": "Ampersand", + "description": "Represents the & key in screen reader content as a readable word" + }, + "asteriskCharacterDescriptor": { + "message": "Asterisco", + "description": "Represents the * key in screen reader content as a readable word" + }, + "parenLeftCharacterDescriptor": { + "message": "Parêntesis esquerdo", + "description": "Represents the ( key in screen reader content as a readable word" + }, + "parenRightCharacterDescriptor": { + "message": "Parêntesis direito", + "description": "Represents the ) key in screen reader content as a readable word" + }, + "hyphenCharacterDescriptor": { + "message": "Underscore", + "description": "Represents the _ key in screen reader content as a readable word" + }, + "underscoreCharacterDescriptor": { + "message": "Hífen", + "description": "Represents the - key in screen reader content as a readable word" + }, + "plusCharacterDescriptor": { + "message": "Mais", + "description": "Represents the + key in screen reader content as a readable word" + }, + "equalsCharacterDescriptor": { + "message": "Igual", + "description": "Represents the = key in screen reader content as a readable word" + }, + "braceLeftCharacterDescriptor": { + "message": "Left brace", + "description": "Represents the { key in screen reader content as a readable word" + }, + "braceRightCharacterDescriptor": { + "message": "Right brace", + "description": "Represents the } key in screen reader content as a readable word" + }, + "bracketLeftCharacterDescriptor": { + "message": "Left bracket", + "description": "Represents the [ key in screen reader content as a readable word" + }, + "bracketRightCharacterDescriptor": { + "message": "Right bracket", + "description": "Represents the ] key in screen reader content as a readable word" + }, + "pipeCharacterDescriptor": { + "message": "Pipe", + "description": "Represents the | key in screen reader content as a readable word" + }, + "backSlashCharacterDescriptor": { + "message": "Back slash", + "description": "Represents the back slash key in screen reader content as a readable word" + }, + "colonCharacterDescriptor": { + "message": "Dois pontos", + "description": "Represents the : key in screen reader content as a readable word" + }, + "semicolonCharacterDescriptor": { + "message": "Ponto e vírgula", + "description": "Represents the ; key in screen reader content as a readable word" + }, + "doubleQuoteCharacterDescriptor": { + "message": "Double quote", + "description": "Represents the double quote key in screen reader content as a readable word" + }, + "singleQuoteCharacterDescriptor": { + "message": "Single quote", + "description": "Represents the ' key in screen reader content as a readable word" + }, + "lessThanCharacterDescriptor": { + "message": "Menor", + "description": "Represents the < key in screen reader content as a readable word" + }, + "greaterThanCharacterDescriptor": { + "message": "Maior", + "description": "Represents the > key in screen reader content as a readable word" + }, + "commaCharacterDescriptor": { + "message": "Vírgula", + "description": "Represents the , key in screen reader content as a readable word" + }, + "periodCharacterDescriptor": { + "message": "Ponto final", + "description": "Represents the . key in screen reader content as a readable word" + }, + "questionCharacterDescriptor": { + "message": "Ponto de interrogação", + "description": "Represents the ? key in screen reader content as a readable word" + }, + "forwardSlashCharacterDescriptor": { + "message": "Forward slash", + "description": "Represents the / key in screen reader content as a readable word" + }, + "lowercaseAriaLabel": { + "message": "Minúsculas" + }, + "uppercaseAriaLabel": { + "message": "Maiúsculas" + }, + "generatedPassword": { + "message": "Palavra-passe gerada" } } diff --git a/apps/browser/src/_locales/ro/messages.json b/apps/browser/src/_locales/ro/messages.json index 7b506d5a3e9..4847fecf059 100644 --- a/apps/browser/src/_locales/ro/messages.json +++ b/apps/browser/src/_locales/ro/messages.json @@ -19,6 +19,18 @@ "createAccount": { "message": "Creare cont" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" + }, "setAStrongPassword": { "message": "Setați o parolă puternică" }, @@ -72,7 +84,7 @@ "message": "Alăturați-vă organizației" }, "joinOrganizationName": { - "message": "Join $ORGANIZATIONNAME$", + "message": "Alătură-te $ORGANIZATIONNAME$", "placeholders": { "organizationName": { "content": "$1", @@ -107,6 +119,9 @@ "copyPassword": { "message": "Copiere parolă" }, + "copyPassphrase": { + "message": "Copy passphrase" + }, "copyNote": { "message": "Copiere notă" }, @@ -138,7 +153,7 @@ "message": "Copiați numărul de licență" }, "copyCustomField": { - "message": "Copy $FIELD$", + "message": "Copiază $FIELD$", "placeholders": { "field": { "content": "$1", @@ -147,10 +162,14 @@ } }, "copyWebsite": { - "message": "Copy website" + "message": "Copiază site-ul" }, "copyNotes": { - "message": "Copy notes" + "message": "Copiază notițele" + }, + "fill": { + "message": "Fill", + "description": "This string is used on the vault page to indicate autofilling. Horizontal space is limited in the interface here so try and keep translations as concise as possible." }, "autoFill": { "message": "Auto-completare" @@ -204,16 +223,16 @@ "message": "Adăugare articol" }, "accountEmail": { - "message": "Account email" + "message": "Adresa de email a contului" }, "requestHint": { - "message": "Request hint" + "message": "Solicită indiciu" }, "requestPasswordHint": { - "message": "Request password hint" + "message": "Solicită indiciu parolă" }, "enterYourAccountEmailAddressAndYourPasswordHintWillBeSentToYou": { - "message": "Enter your account email address and your password hint will be sent to you" + "message": "Introduceți adresa de e-mail a contului și indiciul pentru parolă va fi trimis pe email" }, "passwordHint": { "message": "Indiciu parolă" @@ -338,10 +357,10 @@ "message": "Editare dosar" }, "newFolder": { - "message": "New folder" + "message": "Folder nou" }, "folderName": { - "message": "Folder name" + "message": "Numele folderului" }, "folderHintText": { "message": "Nest a folder by adding the parent folder's name followed by a “/”. Example: Social/Forums" @@ -407,6 +426,9 @@ "generatePassword": { "message": "Generare parolă" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "regeneratePassword": { "message": "Regenerare parolă" }, @@ -568,6 +590,15 @@ "launchWebsite": { "message": "Lansați siteul web" }, + "launchWebsiteName": { + "message": "Launch website $ITEMNAME$", + "placeholders": { + "itemname": { + "content": "$1", + "example": "Secret item" + } + } + }, "website": { "message": "Sait web" }, @@ -814,6 +845,9 @@ "logIn": { "message": "Autentificare" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "restartRegistration": { "message": "Reporniți înregistrarea" }, @@ -1389,6 +1423,10 @@ "baseUrl": { "message": "URL server" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "URL server API" }, @@ -2464,8 +2502,8 @@ "message": "Opțional, este necesară o parolă pentru ca utilizatorii să acceseze acest Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendPasswordDescV2": { - "message": "Require this password to view the Send.", + "sendPasswordDescV3": { + "message": "Add an optional password for recipients to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendNotesDesc": { @@ -2827,6 +2865,9 @@ "generateUsername": { "message": "Generare nume de utilizator" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Tip de nume de utilizator" }, @@ -2867,6 +2908,14 @@ "forwardedEmailDesc": { "message": "Generați un alias de e-mail cu un serviciu de redirecționare extern." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ error: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -4494,9 +4543,15 @@ "itemLocation": { "message": "Item Location" }, + "fileSend": { + "message": "File Send" + }, "fileSends": { "message": "File Sends" }, + "textSend": { + "message": "Text Send" + }, "textSends": { "message": "Text Sends" }, @@ -4559,5 +4614,158 @@ }, "authenticating": { "message": "Authenticating" + }, + "fillGeneratedPassword": { + "message": "Fill generated password", + "description": "Heading for the password generator within the inline menu" + }, + "passwordRegenerated": { + "message": "Password regenerated", + "description": "Notification message for when a password has been regenerated" + }, + "saveLoginToBitwarden": { + "message": "Save login to Bitwarden?", + "description": "Confirmation message for saving a login to Bitwarden" + }, + "spaceCharacterDescriptor": { + "message": "Space", + "description": "Represents the space key in screen reader content as a readable word" + }, + "tildeCharacterDescriptor": { + "message": "Tilde", + "description": "Represents the ~ key in screen reader content as a readable word" + }, + "backtickCharacterDescriptor": { + "message": "Backtick", + "description": "Represents the ` key in screen reader content as a readable word" + }, + "exclamationCharacterDescriptor": { + "message": "Exclamation mark", + "description": "Represents the ! key in screen reader content as a readable word" + }, + "atSignCharacterDescriptor": { + "message": "At sign", + "description": "Represents the @ key in screen reader content as a readable word" + }, + "hashSignCharacterDescriptor": { + "message": "Hash sign", + "description": "Represents the # key in screen reader content as a readable word" + }, + "dollarSignCharacterDescriptor": { + "message": "Dollar sign", + "description": "Represents the $ key in screen reader content as a readable word" + }, + "percentSignCharacterDescriptor": { + "message": "Percent sign", + "description": "Represents the % key in screen reader content as a readable word" + }, + "caretCharacterDescriptor": { + "message": "Caret", + "description": "Represents the ^ key in screen reader content as a readable word" + }, + "ampersandCharacterDescriptor": { + "message": "Ampersand", + "description": "Represents the & key in screen reader content as a readable word" + }, + "asteriskCharacterDescriptor": { + "message": "Asterisk", + "description": "Represents the * key in screen reader content as a readable word" + }, + "parenLeftCharacterDescriptor": { + "message": "Left parenthesis", + "description": "Represents the ( key in screen reader content as a readable word" + }, + "parenRightCharacterDescriptor": { + "message": "Right parenthesis", + "description": "Represents the ) key in screen reader content as a readable word" + }, + "hyphenCharacterDescriptor": { + "message": "Underscore", + "description": "Represents the _ key in screen reader content as a readable word" + }, + "underscoreCharacterDescriptor": { + "message": "Hyphen", + "description": "Represents the - key in screen reader content as a readable word" + }, + "plusCharacterDescriptor": { + "message": "Plus", + "description": "Represents the + key in screen reader content as a readable word" + }, + "equalsCharacterDescriptor": { + "message": "Equals", + "description": "Represents the = key in screen reader content as a readable word" + }, + "braceLeftCharacterDescriptor": { + "message": "Left brace", + "description": "Represents the { key in screen reader content as a readable word" + }, + "braceRightCharacterDescriptor": { + "message": "Right brace", + "description": "Represents the } key in screen reader content as a readable word" + }, + "bracketLeftCharacterDescriptor": { + "message": "Left bracket", + "description": "Represents the [ key in screen reader content as a readable word" + }, + "bracketRightCharacterDescriptor": { + "message": "Right bracket", + "description": "Represents the ] key in screen reader content as a readable word" + }, + "pipeCharacterDescriptor": { + "message": "Pipe", + "description": "Represents the | key in screen reader content as a readable word" + }, + "backSlashCharacterDescriptor": { + "message": "Back slash", + "description": "Represents the back slash key in screen reader content as a readable word" + }, + "colonCharacterDescriptor": { + "message": "Colon", + "description": "Represents the : key in screen reader content as a readable word" + }, + "semicolonCharacterDescriptor": { + "message": "Semicolon", + "description": "Represents the ; key in screen reader content as a readable word" + }, + "doubleQuoteCharacterDescriptor": { + "message": "Double quote", + "description": "Represents the double quote key in screen reader content as a readable word" + }, + "singleQuoteCharacterDescriptor": { + "message": "Single quote", + "description": "Represents the ' key in screen reader content as a readable word" + }, + "lessThanCharacterDescriptor": { + "message": "Less than", + "description": "Represents the < key in screen reader content as a readable word" + }, + "greaterThanCharacterDescriptor": { + "message": "Greater than", + "description": "Represents the > key in screen reader content as a readable word" + }, + "commaCharacterDescriptor": { + "message": "Comma", + "description": "Represents the , key in screen reader content as a readable word" + }, + "periodCharacterDescriptor": { + "message": "Period", + "description": "Represents the . key in screen reader content as a readable word" + }, + "questionCharacterDescriptor": { + "message": "Question mark", + "description": "Represents the ? key in screen reader content as a readable word" + }, + "forwardSlashCharacterDescriptor": { + "message": "Forward slash", + "description": "Represents the / key in screen reader content as a readable word" + }, + "lowercaseAriaLabel": { + "message": "Lowercase" + }, + "uppercaseAriaLabel": { + "message": "Uppercase" + }, + "generatedPassword": { + "message": "Generated password" } } diff --git a/apps/browser/src/_locales/ru/messages.json b/apps/browser/src/_locales/ru/messages.json index 56a58cfb9f1..9df9fc8d567 100644 --- a/apps/browser/src/_locales/ru/messages.json +++ b/apps/browser/src/_locales/ru/messages.json @@ -19,6 +19,18 @@ "createAccount": { "message": "Создать аккаунт" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" + }, "setAStrongPassword": { "message": "Задайте надежный пароль" }, @@ -107,6 +119,9 @@ "copyPassword": { "message": "Скопировать пароль" }, + "copyPassphrase": { + "message": "Скопировать парольную фразу" + }, "copyNote": { "message": "Скопировать заметку" }, @@ -152,6 +167,10 @@ "copyNotes": { "message": "Скопировать заметки" }, + "fill": { + "message": "Fill", + "description": "This string is used on the vault page to indicate autofilling. Horizontal space is limited in the interface here so try and keep translations as concise as possible." + }, "autoFill": { "message": "Автозаполнение" }, @@ -407,6 +426,9 @@ "generatePassword": { "message": "Сгенерировать пароль" }, + "generatePassphrase": { + "message": "Создать парольную фразу" + }, "regeneratePassword": { "message": "Создать новый пароль" }, @@ -568,6 +590,15 @@ "launchWebsite": { "message": "Открыть сайт" }, + "launchWebsiteName": { + "message": "Launch website $ITEMNAME$", + "placeholders": { + "itemname": { + "content": "$1", + "example": "Secret item" + } + } + }, "website": { "message": "Сайт" }, @@ -814,6 +845,9 @@ "logIn": { "message": "Войти" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "restartRegistration": { "message": "Перезапустить регистрацию" }, @@ -1389,6 +1423,10 @@ "baseUrl": { "message": "URL сервера" }, + "selfHostBaseUrl": { + "message": "URL собственного сервера", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "URL API сервера" }, @@ -2464,8 +2502,8 @@ "message": "По возможности запрашивать у пользователей пароль для доступа к этой Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendPasswordDescV2": { - "message": "Требовать этот пароль для просмотра Send.", + "sendPasswordDescV3": { + "message": "Добавьте опциональный пароль для доступа получателей к этой Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendNotesDesc": { @@ -2827,6 +2865,9 @@ "generateUsername": { "message": "Создать имя пользователя" }, + "generateEmail": { + "message": "Сгенерировать email" + }, "usernameType": { "message": "Тип имени пользователя" }, @@ -2867,6 +2908,14 @@ "forwardedEmailDesc": { "message": "Создать псевдоним электронной почты для внешней службы пересылки." }, + "forwarderDomainName": { + "message": "Домен электронной почты", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Выберите домен, который поддерживается выбранным сервисом", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "Ошибка $SERVICENAME$: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -4494,9 +4543,15 @@ "itemLocation": { "message": "Расположение элемента" }, + "fileSend": { + "message": "Файловая Send" + }, "fileSends": { "message": "Файловая Send" }, + "textSend": { + "message": "Текстовая Send" + }, "textSends": { "message": "Текстовая Send" }, @@ -4559,5 +4614,158 @@ }, "authenticating": { "message": "Аутентификация" + }, + "fillGeneratedPassword": { + "message": "Fill generated password", + "description": "Heading for the password generator within the inline menu" + }, + "passwordRegenerated": { + "message": "Password regenerated", + "description": "Notification message for when a password has been regenerated" + }, + "saveLoginToBitwarden": { + "message": "Save login to Bitwarden?", + "description": "Confirmation message for saving a login to Bitwarden" + }, + "spaceCharacterDescriptor": { + "message": "Space", + "description": "Represents the space key in screen reader content as a readable word" + }, + "tildeCharacterDescriptor": { + "message": "Tilde", + "description": "Represents the ~ key in screen reader content as a readable word" + }, + "backtickCharacterDescriptor": { + "message": "Backtick", + "description": "Represents the ` key in screen reader content as a readable word" + }, + "exclamationCharacterDescriptor": { + "message": "Exclamation mark", + "description": "Represents the ! key in screen reader content as a readable word" + }, + "atSignCharacterDescriptor": { + "message": "At sign", + "description": "Represents the @ key in screen reader content as a readable word" + }, + "hashSignCharacterDescriptor": { + "message": "Hash sign", + "description": "Represents the # key in screen reader content as a readable word" + }, + "dollarSignCharacterDescriptor": { + "message": "Dollar sign", + "description": "Represents the $ key in screen reader content as a readable word" + }, + "percentSignCharacterDescriptor": { + "message": "Percent sign", + "description": "Represents the % key in screen reader content as a readable word" + }, + "caretCharacterDescriptor": { + "message": "Caret", + "description": "Represents the ^ key in screen reader content as a readable word" + }, + "ampersandCharacterDescriptor": { + "message": "Ampersand", + "description": "Represents the & key in screen reader content as a readable word" + }, + "asteriskCharacterDescriptor": { + "message": "Asterisk", + "description": "Represents the * key in screen reader content as a readable word" + }, + "parenLeftCharacterDescriptor": { + "message": "Left parenthesis", + "description": "Represents the ( key in screen reader content as a readable word" + }, + "parenRightCharacterDescriptor": { + "message": "Right parenthesis", + "description": "Represents the ) key in screen reader content as a readable word" + }, + "hyphenCharacterDescriptor": { + "message": "Underscore", + "description": "Represents the _ key in screen reader content as a readable word" + }, + "underscoreCharacterDescriptor": { + "message": "Hyphen", + "description": "Represents the - key in screen reader content as a readable word" + }, + "plusCharacterDescriptor": { + "message": "Plus", + "description": "Represents the + key in screen reader content as a readable word" + }, + "equalsCharacterDescriptor": { + "message": "Equals", + "description": "Represents the = key in screen reader content as a readable word" + }, + "braceLeftCharacterDescriptor": { + "message": "Left brace", + "description": "Represents the { key in screen reader content as a readable word" + }, + "braceRightCharacterDescriptor": { + "message": "Right brace", + "description": "Represents the } key in screen reader content as a readable word" + }, + "bracketLeftCharacterDescriptor": { + "message": "Left bracket", + "description": "Represents the [ key in screen reader content as a readable word" + }, + "bracketRightCharacterDescriptor": { + "message": "Right bracket", + "description": "Represents the ] key in screen reader content as a readable word" + }, + "pipeCharacterDescriptor": { + "message": "Pipe", + "description": "Represents the | key in screen reader content as a readable word" + }, + "backSlashCharacterDescriptor": { + "message": "Back slash", + "description": "Represents the back slash key in screen reader content as a readable word" + }, + "colonCharacterDescriptor": { + "message": "Colon", + "description": "Represents the : key in screen reader content as a readable word" + }, + "semicolonCharacterDescriptor": { + "message": "Semicolon", + "description": "Represents the ; key in screen reader content as a readable word" + }, + "doubleQuoteCharacterDescriptor": { + "message": "Double quote", + "description": "Represents the double quote key in screen reader content as a readable word" + }, + "singleQuoteCharacterDescriptor": { + "message": "Single quote", + "description": "Represents the ' key in screen reader content as a readable word" + }, + "lessThanCharacterDescriptor": { + "message": "Less than", + "description": "Represents the < key in screen reader content as a readable word" + }, + "greaterThanCharacterDescriptor": { + "message": "Greater than", + "description": "Represents the > key in screen reader content as a readable word" + }, + "commaCharacterDescriptor": { + "message": "Comma", + "description": "Represents the , key in screen reader content as a readable word" + }, + "periodCharacterDescriptor": { + "message": "Period", + "description": "Represents the . key in screen reader content as a readable word" + }, + "questionCharacterDescriptor": { + "message": "Question mark", + "description": "Represents the ? key in screen reader content as a readable word" + }, + "forwardSlashCharacterDescriptor": { + "message": "Forward slash", + "description": "Represents the / key in screen reader content as a readable word" + }, + "lowercaseAriaLabel": { + "message": "Lowercase" + }, + "uppercaseAriaLabel": { + "message": "Uppercase" + }, + "generatedPassword": { + "message": "Generated password" } } diff --git a/apps/browser/src/_locales/si/messages.json b/apps/browser/src/_locales/si/messages.json index 1636f483ed6..a4b8c3b8149 100644 --- a/apps/browser/src/_locales/si/messages.json +++ b/apps/browser/src/_locales/si/messages.json @@ -19,6 +19,18 @@ "createAccount": { "message": "ගිණුමක් සාදන්න" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" + }, "setAStrongPassword": { "message": "Set a strong password" }, @@ -107,6 +119,9 @@ "copyPassword": { "message": "මුරපදය පිටපත් කරන්න" }, + "copyPassphrase": { + "message": "Copy passphrase" + }, "copyNote": { "message": "සටහන පිටපත් කරන්න" }, @@ -152,6 +167,10 @@ "copyNotes": { "message": "Copy notes" }, + "fill": { + "message": "Fill", + "description": "This string is used on the vault page to indicate autofilling. Horizontal space is limited in the interface here so try and keep translations as concise as possible." + }, "autoFill": { "message": "ස්වයං-පිරවීම" }, @@ -407,6 +426,9 @@ "generatePassword": { "message": "මුරපදය ජනනය කරන්න" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "regeneratePassword": { "message": "මුරපදය ප්රතිජනනය" }, @@ -568,6 +590,15 @@ "launchWebsite": { "message": "Launch website" }, + "launchWebsiteName": { + "message": "Launch website $ITEMNAME$", + "placeholders": { + "itemname": { + "content": "$1", + "example": "Secret item" + } + } + }, "website": { "message": "වියමන අඩවිය" }, @@ -814,6 +845,9 @@ "logIn": { "message": "Log in" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "restartRegistration": { "message": "Restart registration" }, @@ -1389,6 +1423,10 @@ "baseUrl": { "message": "සේවාදායකය URL" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "API සේවාදායකය URL" }, @@ -2464,8 +2502,8 @@ "message": "විකල්පයක් ලෙස පරිශීලකයින්ට මෙම යවන්න වෙත ප්රවේශ වීමට මුරපදයක් අවශ්ය වේ.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendPasswordDescV2": { - "message": "Require this password to view the Send.", + "sendPasswordDescV3": { + "message": "Add an optional password for recipients to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendNotesDesc": { @@ -2827,6 +2865,9 @@ "generateUsername": { "message": "Generate username" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Username type" }, @@ -2867,6 +2908,14 @@ "forwardedEmailDesc": { "message": "Generate an email alias with an external forwarding service." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ error: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -4494,9 +4543,15 @@ "itemLocation": { "message": "Item Location" }, + "fileSend": { + "message": "File Send" + }, "fileSends": { "message": "File Sends" }, + "textSend": { + "message": "Text Send" + }, "textSends": { "message": "Text Sends" }, @@ -4559,5 +4614,158 @@ }, "authenticating": { "message": "Authenticating" + }, + "fillGeneratedPassword": { + "message": "Fill generated password", + "description": "Heading for the password generator within the inline menu" + }, + "passwordRegenerated": { + "message": "Password regenerated", + "description": "Notification message for when a password has been regenerated" + }, + "saveLoginToBitwarden": { + "message": "Save login to Bitwarden?", + "description": "Confirmation message for saving a login to Bitwarden" + }, + "spaceCharacterDescriptor": { + "message": "Space", + "description": "Represents the space key in screen reader content as a readable word" + }, + "tildeCharacterDescriptor": { + "message": "Tilde", + "description": "Represents the ~ key in screen reader content as a readable word" + }, + "backtickCharacterDescriptor": { + "message": "Backtick", + "description": "Represents the ` key in screen reader content as a readable word" + }, + "exclamationCharacterDescriptor": { + "message": "Exclamation mark", + "description": "Represents the ! key in screen reader content as a readable word" + }, + "atSignCharacterDescriptor": { + "message": "At sign", + "description": "Represents the @ key in screen reader content as a readable word" + }, + "hashSignCharacterDescriptor": { + "message": "Hash sign", + "description": "Represents the # key in screen reader content as a readable word" + }, + "dollarSignCharacterDescriptor": { + "message": "Dollar sign", + "description": "Represents the $ key in screen reader content as a readable word" + }, + "percentSignCharacterDescriptor": { + "message": "Percent sign", + "description": "Represents the % key in screen reader content as a readable word" + }, + "caretCharacterDescriptor": { + "message": "Caret", + "description": "Represents the ^ key in screen reader content as a readable word" + }, + "ampersandCharacterDescriptor": { + "message": "Ampersand", + "description": "Represents the & key in screen reader content as a readable word" + }, + "asteriskCharacterDescriptor": { + "message": "Asterisk", + "description": "Represents the * key in screen reader content as a readable word" + }, + "parenLeftCharacterDescriptor": { + "message": "Left parenthesis", + "description": "Represents the ( key in screen reader content as a readable word" + }, + "parenRightCharacterDescriptor": { + "message": "Right parenthesis", + "description": "Represents the ) key in screen reader content as a readable word" + }, + "hyphenCharacterDescriptor": { + "message": "Underscore", + "description": "Represents the _ key in screen reader content as a readable word" + }, + "underscoreCharacterDescriptor": { + "message": "Hyphen", + "description": "Represents the - key in screen reader content as a readable word" + }, + "plusCharacterDescriptor": { + "message": "Plus", + "description": "Represents the + key in screen reader content as a readable word" + }, + "equalsCharacterDescriptor": { + "message": "Equals", + "description": "Represents the = key in screen reader content as a readable word" + }, + "braceLeftCharacterDescriptor": { + "message": "Left brace", + "description": "Represents the { key in screen reader content as a readable word" + }, + "braceRightCharacterDescriptor": { + "message": "Right brace", + "description": "Represents the } key in screen reader content as a readable word" + }, + "bracketLeftCharacterDescriptor": { + "message": "Left bracket", + "description": "Represents the [ key in screen reader content as a readable word" + }, + "bracketRightCharacterDescriptor": { + "message": "Right bracket", + "description": "Represents the ] key in screen reader content as a readable word" + }, + "pipeCharacterDescriptor": { + "message": "Pipe", + "description": "Represents the | key in screen reader content as a readable word" + }, + "backSlashCharacterDescriptor": { + "message": "Back slash", + "description": "Represents the back slash key in screen reader content as a readable word" + }, + "colonCharacterDescriptor": { + "message": "Colon", + "description": "Represents the : key in screen reader content as a readable word" + }, + "semicolonCharacterDescriptor": { + "message": "Semicolon", + "description": "Represents the ; key in screen reader content as a readable word" + }, + "doubleQuoteCharacterDescriptor": { + "message": "Double quote", + "description": "Represents the double quote key in screen reader content as a readable word" + }, + "singleQuoteCharacterDescriptor": { + "message": "Single quote", + "description": "Represents the ' key in screen reader content as a readable word" + }, + "lessThanCharacterDescriptor": { + "message": "Less than", + "description": "Represents the < key in screen reader content as a readable word" + }, + "greaterThanCharacterDescriptor": { + "message": "Greater than", + "description": "Represents the > key in screen reader content as a readable word" + }, + "commaCharacterDescriptor": { + "message": "Comma", + "description": "Represents the , key in screen reader content as a readable word" + }, + "periodCharacterDescriptor": { + "message": "Period", + "description": "Represents the . key in screen reader content as a readable word" + }, + "questionCharacterDescriptor": { + "message": "Question mark", + "description": "Represents the ? key in screen reader content as a readable word" + }, + "forwardSlashCharacterDescriptor": { + "message": "Forward slash", + "description": "Represents the / key in screen reader content as a readable word" + }, + "lowercaseAriaLabel": { + "message": "Lowercase" + }, + "uppercaseAriaLabel": { + "message": "Uppercase" + }, + "generatedPassword": { + "message": "Generated password" } } diff --git a/apps/browser/src/_locales/sk/messages.json b/apps/browser/src/_locales/sk/messages.json index 1bf25cac9f8..f08b18f33ba 100644 --- a/apps/browser/src/_locales/sk/messages.json +++ b/apps/browser/src/_locales/sk/messages.json @@ -19,6 +19,18 @@ "createAccount": { "message": "Vytvoriť účet" }, + "newToBitwarden": { + "message": "Ste noví na Bitwardene?" + }, + "logInWithPasskey": { + "message": "Prihlásiť sa s prístupovým kľúčom" + }, + "useSingleSignOn": { + "message": "Použiť jednotné prihlásenie" + }, + "welcomeBack": { + "message": "Vitajte späť" + }, "setAStrongPassword": { "message": "Nastavte silné heslo" }, @@ -107,6 +119,9 @@ "copyPassword": { "message": "Kopírovať heslo" }, + "copyPassphrase": { + "message": "Kopírovať prístupovú frázu" + }, "copyNote": { "message": "Kopírovať poznámku" }, @@ -152,6 +167,10 @@ "copyNotes": { "message": "Kopírovať poznámky" }, + "fill": { + "message": "Vyplniť", + "description": "This string is used on the vault page to indicate autofilling. Horizontal space is limited in the interface here so try and keep translations as concise as possible." + }, "autoFill": { "message": "Automatické vypĺňanie" }, @@ -407,6 +426,9 @@ "generatePassword": { "message": "Generovať heslo" }, + "generatePassphrase": { + "message": "Generovať prístupovú frázu" + }, "regeneratePassword": { "message": "Vygenerovať nové heslo" }, @@ -568,6 +590,15 @@ "launchWebsite": { "message": "Otvoriť stránku" }, + "launchWebsiteName": { + "message": "Otvoriť stránku $ITEMNAME$", + "placeholders": { + "itemname": { + "content": "$1", + "example": "Secret item" + } + } + }, "website": { "message": "Webstránka" }, @@ -814,6 +845,9 @@ "logIn": { "message": "Prihlásiť sa" }, + "logInToBitwarden": { + "message": "Prihlásiť sa do Bitwardenu" + }, "restartRegistration": { "message": "Zopakovať registráciu" }, @@ -1389,6 +1423,10 @@ "baseUrl": { "message": "URL servera" }, + "selfHostBaseUrl": { + "message": "Adresa URL vlastného hostingu", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "URL API servera" }, @@ -2464,8 +2502,8 @@ "message": "Voliteľne môžete vyžadovať heslo pre používateľov na prístup k tomuto Sendu.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendPasswordDescV2": { - "message": "Na zobrazenie tohto Sendu vyžadovať toto heslo.", + "sendPasswordDescV3": { + "message": "Pridajte voliteľné heslo pre príjemcov na prístup k tomuto Sendu.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendNotesDesc": { @@ -2827,6 +2865,9 @@ "generateUsername": { "message": "Vygenerovať používateľské meno" }, + "generateEmail": { + "message": "Generovať e-mail" + }, "usernameType": { "message": "Typ používateľského mena" }, @@ -2867,6 +2908,14 @@ "forwardedEmailDesc": { "message": "Vytvoriť e-mailový alias pomocou externej služby preposielania." }, + "forwarderDomainName": { + "message": "E-mailová doména", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Vyberte doménu, ktorá je podporovaná vybranou službou", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ chyba: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -4494,9 +4543,15 @@ "itemLocation": { "message": "Umiestnenie položky" }, + "fileSend": { + "message": "Send so súborom" + }, "fileSends": { "message": "Sendy so súborom" }, + "textSend": { + "message": "Textový Send" + }, "textSends": { "message": "Textové Sendy" }, @@ -4559,5 +4614,158 @@ }, "authenticating": { "message": "Overuje sa" + }, + "fillGeneratedPassword": { + "message": "Vložiť vygenerované heslo", + "description": "Heading for the password generator within the inline menu" + }, + "passwordRegenerated": { + "message": "Vygenerované nové heslo", + "description": "Notification message for when a password has been regenerated" + }, + "saveLoginToBitwarden": { + "message": "Uložiť prihlasovacie údaje do Bitwardenu?", + "description": "Confirmation message for saving a login to Bitwarden" + }, + "spaceCharacterDescriptor": { + "message": "Medzera", + "description": "Represents the space key in screen reader content as a readable word" + }, + "tildeCharacterDescriptor": { + "message": "Tilda", + "description": "Represents the ~ key in screen reader content as a readable word" + }, + "backtickCharacterDescriptor": { + "message": "Opačný dĺžeň", + "description": "Represents the ` key in screen reader content as a readable word" + }, + "exclamationCharacterDescriptor": { + "message": "Výkričník", + "description": "Represents the ! key in screen reader content as a readable word" + }, + "atSignCharacterDescriptor": { + "message": "Zavináč", + "description": "Represents the @ key in screen reader content as a readable word" + }, + "hashSignCharacterDescriptor": { + "message": "Mriežka", + "description": "Represents the # key in screen reader content as a readable word" + }, + "dollarSignCharacterDescriptor": { + "message": "Dolár", + "description": "Represents the $ key in screen reader content as a readable word" + }, + "percentSignCharacterDescriptor": { + "message": "Percento", + "description": "Represents the % key in screen reader content as a readable word" + }, + "caretCharacterDescriptor": { + "message": "Striežka", + "description": "Represents the ^ key in screen reader content as a readable word" + }, + "ampersandCharacterDescriptor": { + "message": "Ampersand", + "description": "Represents the & key in screen reader content as a readable word" + }, + "asteriskCharacterDescriptor": { + "message": "Hviezdička", + "description": "Represents the * key in screen reader content as a readable word" + }, + "parenLeftCharacterDescriptor": { + "message": "Ľavá zátvorka", + "description": "Represents the ( key in screen reader content as a readable word" + }, + "parenRightCharacterDescriptor": { + "message": "Pravá zátvorka", + "description": "Represents the ) key in screen reader content as a readable word" + }, + "hyphenCharacterDescriptor": { + "message": "Podčiarkovník", + "description": "Represents the _ key in screen reader content as a readable word" + }, + "underscoreCharacterDescriptor": { + "message": "Spojovník", + "description": "Represents the - key in screen reader content as a readable word" + }, + "plusCharacterDescriptor": { + "message": "Plus", + "description": "Represents the + key in screen reader content as a readable word" + }, + "equalsCharacterDescriptor": { + "message": "Rovná sa", + "description": "Represents the = key in screen reader content as a readable word" + }, + "braceLeftCharacterDescriptor": { + "message": "Ľavá zložená zátvorka", + "description": "Represents the { key in screen reader content as a readable word" + }, + "braceRightCharacterDescriptor": { + "message": "Pravá zložená zátvorka", + "description": "Represents the } key in screen reader content as a readable word" + }, + "bracketLeftCharacterDescriptor": { + "message": "Ľavá hranatá zátvorka", + "description": "Represents the [ key in screen reader content as a readable word" + }, + "bracketRightCharacterDescriptor": { + "message": "Pravá hranatá zátvorka", + "description": "Represents the ] key in screen reader content as a readable word" + }, + "pipeCharacterDescriptor": { + "message": "Rúra", + "description": "Represents the | key in screen reader content as a readable word" + }, + "backSlashCharacterDescriptor": { + "message": "Spätná lomka", + "description": "Represents the back slash key in screen reader content as a readable word" + }, + "colonCharacterDescriptor": { + "message": "Dvojbodka", + "description": "Represents the : key in screen reader content as a readable word" + }, + "semicolonCharacterDescriptor": { + "message": "Bodkočiarka", + "description": "Represents the ; key in screen reader content as a readable word" + }, + "doubleQuoteCharacterDescriptor": { + "message": "Dvojité úvodzovky", + "description": "Represents the double quote key in screen reader content as a readable word" + }, + "singleQuoteCharacterDescriptor": { + "message": "Jednoduché úvodzovky", + "description": "Represents the ' key in screen reader content as a readable word" + }, + "lessThanCharacterDescriptor": { + "message": "Ľavá lomená zátvorka", + "description": "Represents the < key in screen reader content as a readable word" + }, + "greaterThanCharacterDescriptor": { + "message": "Pravá lomená zátvorka", + "description": "Represents the > key in screen reader content as a readable word" + }, + "commaCharacterDescriptor": { + "message": "Čiarka", + "description": "Represents the , key in screen reader content as a readable word" + }, + "periodCharacterDescriptor": { + "message": "Bodka", + "description": "Represents the . key in screen reader content as a readable word" + }, + "questionCharacterDescriptor": { + "message": "Otáznik", + "description": "Represents the ? key in screen reader content as a readable word" + }, + "forwardSlashCharacterDescriptor": { + "message": "Lomka", + "description": "Represents the / key in screen reader content as a readable word" + }, + "lowercaseAriaLabel": { + "message": "Malé písmená" + }, + "uppercaseAriaLabel": { + "message": "Veľké písmená" + }, + "generatedPassword": { + "message": "Vygenerované heslo" } } diff --git a/apps/browser/src/_locales/sl/messages.json b/apps/browser/src/_locales/sl/messages.json index 9fab3ca10df..ea2d29dfe3c 100644 --- a/apps/browser/src/_locales/sl/messages.json +++ b/apps/browser/src/_locales/sl/messages.json @@ -19,6 +19,18 @@ "createAccount": { "message": "Ustvari račun" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" + }, "setAStrongPassword": { "message": "Set a strong password" }, @@ -107,6 +119,9 @@ "copyPassword": { "message": "Kopiraj geslo" }, + "copyPassphrase": { + "message": "Copy passphrase" + }, "copyNote": { "message": "Kopiraj opombo" }, @@ -152,6 +167,10 @@ "copyNotes": { "message": "Copy notes" }, + "fill": { + "message": "Fill", + "description": "This string is used on the vault page to indicate autofilling. Horizontal space is limited in the interface here so try and keep translations as concise as possible." + }, "autoFill": { "message": "Samodejno izpolnjevanje" }, @@ -407,6 +426,9 @@ "generatePassword": { "message": "Generiraj geslo" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "regeneratePassword": { "message": "Ponovno ustvari geslo" }, @@ -568,6 +590,15 @@ "launchWebsite": { "message": "Launch website" }, + "launchWebsiteName": { + "message": "Launch website $ITEMNAME$", + "placeholders": { + "itemname": { + "content": "$1", + "example": "Secret item" + } + } + }, "website": { "message": "Spletna stran" }, @@ -814,6 +845,9 @@ "logIn": { "message": "Log in" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "restartRegistration": { "message": "Restart registration" }, @@ -1389,6 +1423,10 @@ "baseUrl": { "message": "URL naslov strežnika" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "URL naslov API strežnika" }, @@ -2464,8 +2502,8 @@ "message": "Za dostop do te pošiljke lahko nastavite geslo.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendPasswordDescV2": { - "message": "Require this password to view the Send.", + "sendPasswordDescV3": { + "message": "Add an optional password for recipients to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendNotesDesc": { @@ -2827,6 +2865,9 @@ "generateUsername": { "message": "Ustvari uporabniško ime" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Vrsta uporabniškega imena" }, @@ -2867,6 +2908,14 @@ "forwardedEmailDesc": { "message": "Generate an email alias with an external forwarding service." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ error: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -4494,9 +4543,15 @@ "itemLocation": { "message": "Item Location" }, + "fileSend": { + "message": "File Send" + }, "fileSends": { "message": "File Sends" }, + "textSend": { + "message": "Text Send" + }, "textSends": { "message": "Text Sends" }, @@ -4559,5 +4614,158 @@ }, "authenticating": { "message": "Authenticating" + }, + "fillGeneratedPassword": { + "message": "Fill generated password", + "description": "Heading for the password generator within the inline menu" + }, + "passwordRegenerated": { + "message": "Password regenerated", + "description": "Notification message for when a password has been regenerated" + }, + "saveLoginToBitwarden": { + "message": "Save login to Bitwarden?", + "description": "Confirmation message for saving a login to Bitwarden" + }, + "spaceCharacterDescriptor": { + "message": "Space", + "description": "Represents the space key in screen reader content as a readable word" + }, + "tildeCharacterDescriptor": { + "message": "Tilde", + "description": "Represents the ~ key in screen reader content as a readable word" + }, + "backtickCharacterDescriptor": { + "message": "Backtick", + "description": "Represents the ` key in screen reader content as a readable word" + }, + "exclamationCharacterDescriptor": { + "message": "Exclamation mark", + "description": "Represents the ! key in screen reader content as a readable word" + }, + "atSignCharacterDescriptor": { + "message": "At sign", + "description": "Represents the @ key in screen reader content as a readable word" + }, + "hashSignCharacterDescriptor": { + "message": "Hash sign", + "description": "Represents the # key in screen reader content as a readable word" + }, + "dollarSignCharacterDescriptor": { + "message": "Dollar sign", + "description": "Represents the $ key in screen reader content as a readable word" + }, + "percentSignCharacterDescriptor": { + "message": "Percent sign", + "description": "Represents the % key in screen reader content as a readable word" + }, + "caretCharacterDescriptor": { + "message": "Caret", + "description": "Represents the ^ key in screen reader content as a readable word" + }, + "ampersandCharacterDescriptor": { + "message": "Ampersand", + "description": "Represents the & key in screen reader content as a readable word" + }, + "asteriskCharacterDescriptor": { + "message": "Asterisk", + "description": "Represents the * key in screen reader content as a readable word" + }, + "parenLeftCharacterDescriptor": { + "message": "Left parenthesis", + "description": "Represents the ( key in screen reader content as a readable word" + }, + "parenRightCharacterDescriptor": { + "message": "Right parenthesis", + "description": "Represents the ) key in screen reader content as a readable word" + }, + "hyphenCharacterDescriptor": { + "message": "Underscore", + "description": "Represents the _ key in screen reader content as a readable word" + }, + "underscoreCharacterDescriptor": { + "message": "Hyphen", + "description": "Represents the - key in screen reader content as a readable word" + }, + "plusCharacterDescriptor": { + "message": "Plus", + "description": "Represents the + key in screen reader content as a readable word" + }, + "equalsCharacterDescriptor": { + "message": "Equals", + "description": "Represents the = key in screen reader content as a readable word" + }, + "braceLeftCharacterDescriptor": { + "message": "Left brace", + "description": "Represents the { key in screen reader content as a readable word" + }, + "braceRightCharacterDescriptor": { + "message": "Right brace", + "description": "Represents the } key in screen reader content as a readable word" + }, + "bracketLeftCharacterDescriptor": { + "message": "Left bracket", + "description": "Represents the [ key in screen reader content as a readable word" + }, + "bracketRightCharacterDescriptor": { + "message": "Right bracket", + "description": "Represents the ] key in screen reader content as a readable word" + }, + "pipeCharacterDescriptor": { + "message": "Pipe", + "description": "Represents the | key in screen reader content as a readable word" + }, + "backSlashCharacterDescriptor": { + "message": "Back slash", + "description": "Represents the back slash key in screen reader content as a readable word" + }, + "colonCharacterDescriptor": { + "message": "Colon", + "description": "Represents the : key in screen reader content as a readable word" + }, + "semicolonCharacterDescriptor": { + "message": "Semicolon", + "description": "Represents the ; key in screen reader content as a readable word" + }, + "doubleQuoteCharacterDescriptor": { + "message": "Double quote", + "description": "Represents the double quote key in screen reader content as a readable word" + }, + "singleQuoteCharacterDescriptor": { + "message": "Single quote", + "description": "Represents the ' key in screen reader content as a readable word" + }, + "lessThanCharacterDescriptor": { + "message": "Less than", + "description": "Represents the < key in screen reader content as a readable word" + }, + "greaterThanCharacterDescriptor": { + "message": "Greater than", + "description": "Represents the > key in screen reader content as a readable word" + }, + "commaCharacterDescriptor": { + "message": "Comma", + "description": "Represents the , key in screen reader content as a readable word" + }, + "periodCharacterDescriptor": { + "message": "Period", + "description": "Represents the . key in screen reader content as a readable word" + }, + "questionCharacterDescriptor": { + "message": "Question mark", + "description": "Represents the ? key in screen reader content as a readable word" + }, + "forwardSlashCharacterDescriptor": { + "message": "Forward slash", + "description": "Represents the / key in screen reader content as a readable word" + }, + "lowercaseAriaLabel": { + "message": "Lowercase" + }, + "uppercaseAriaLabel": { + "message": "Uppercase" + }, + "generatedPassword": { + "message": "Generated password" } } diff --git a/apps/browser/src/_locales/sr/messages.json b/apps/browser/src/_locales/sr/messages.json index 484a44af4d1..ea1a030e07b 100644 --- a/apps/browser/src/_locales/sr/messages.json +++ b/apps/browser/src/_locales/sr/messages.json @@ -19,6 +19,18 @@ "createAccount": { "message": "Креирај налог" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" + }, "setAStrongPassword": { "message": "Поставите јаку лозинку" }, @@ -107,6 +119,9 @@ "copyPassword": { "message": "Копирај лозинку" }, + "copyPassphrase": { + "message": "Copy passphrase" + }, "copyNote": { "message": "Копирај белешку" }, @@ -152,6 +167,10 @@ "copyNotes": { "message": "Копирати белешке" }, + "fill": { + "message": "Fill", + "description": "This string is used on the vault page to indicate autofilling. Horizontal space is limited in the interface here so try and keep translations as concise as possible." + }, "autoFill": { "message": "Аутоматско допуњавање" }, @@ -407,6 +426,9 @@ "generatePassword": { "message": "Генерисање лозинке" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "regeneratePassword": { "message": "Поново генериши лозинку" }, @@ -568,6 +590,15 @@ "launchWebsite": { "message": "Покрените веб локацију" }, + "launchWebsiteName": { + "message": "Launch website $ITEMNAME$", + "placeholders": { + "itemname": { + "content": "$1", + "example": "Secret item" + } + } + }, "website": { "message": "Веб сајт" }, @@ -814,6 +845,9 @@ "logIn": { "message": "Пријави се" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "restartRegistration": { "message": "Поново покрените регистрацију" }, @@ -1389,6 +1423,10 @@ "baseUrl": { "message": "УРЛ Сервера" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "УРЛ АПИ Сервера" }, @@ -2464,8 +2502,8 @@ "message": "Опционално захтевајте лозинку за приступ корисницима „Send“-у.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendPasswordDescV2": { - "message": "Захтева ову лозинку за преглед Send-а.", + "sendPasswordDescV3": { + "message": "Add an optional password for recipients to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendNotesDesc": { @@ -2827,6 +2865,9 @@ "generateUsername": { "message": "Генериши име" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Тип имена" }, @@ -2867,6 +2908,14 @@ "forwardedEmailDesc": { "message": "Генеришите псеудоним е-поште помоћу екстерне услуге прослеђивања." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ грешка: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -4494,9 +4543,15 @@ "itemLocation": { "message": "Смештај ставке" }, + "fileSend": { + "message": "File Send" + }, "fileSends": { "message": "Датотека „Send“" }, + "textSend": { + "message": "Text Send" + }, "textSends": { "message": "Текст „Send“" }, @@ -4559,5 +4614,158 @@ }, "authenticating": { "message": "Аутентификација" + }, + "fillGeneratedPassword": { + "message": "Fill generated password", + "description": "Heading for the password generator within the inline menu" + }, + "passwordRegenerated": { + "message": "Password regenerated", + "description": "Notification message for when a password has been regenerated" + }, + "saveLoginToBitwarden": { + "message": "Save login to Bitwarden?", + "description": "Confirmation message for saving a login to Bitwarden" + }, + "spaceCharacterDescriptor": { + "message": "Space", + "description": "Represents the space key in screen reader content as a readable word" + }, + "tildeCharacterDescriptor": { + "message": "Tilde", + "description": "Represents the ~ key in screen reader content as a readable word" + }, + "backtickCharacterDescriptor": { + "message": "Backtick", + "description": "Represents the ` key in screen reader content as a readable word" + }, + "exclamationCharacterDescriptor": { + "message": "Exclamation mark", + "description": "Represents the ! key in screen reader content as a readable word" + }, + "atSignCharacterDescriptor": { + "message": "At sign", + "description": "Represents the @ key in screen reader content as a readable word" + }, + "hashSignCharacterDescriptor": { + "message": "Hash sign", + "description": "Represents the # key in screen reader content as a readable word" + }, + "dollarSignCharacterDescriptor": { + "message": "Dollar sign", + "description": "Represents the $ key in screen reader content as a readable word" + }, + "percentSignCharacterDescriptor": { + "message": "Percent sign", + "description": "Represents the % key in screen reader content as a readable word" + }, + "caretCharacterDescriptor": { + "message": "Caret", + "description": "Represents the ^ key in screen reader content as a readable word" + }, + "ampersandCharacterDescriptor": { + "message": "Ampersand", + "description": "Represents the & key in screen reader content as a readable word" + }, + "asteriskCharacterDescriptor": { + "message": "Asterisk", + "description": "Represents the * key in screen reader content as a readable word" + }, + "parenLeftCharacterDescriptor": { + "message": "Left parenthesis", + "description": "Represents the ( key in screen reader content as a readable word" + }, + "parenRightCharacterDescriptor": { + "message": "Right parenthesis", + "description": "Represents the ) key in screen reader content as a readable word" + }, + "hyphenCharacterDescriptor": { + "message": "Underscore", + "description": "Represents the _ key in screen reader content as a readable word" + }, + "underscoreCharacterDescriptor": { + "message": "Hyphen", + "description": "Represents the - key in screen reader content as a readable word" + }, + "plusCharacterDescriptor": { + "message": "Plus", + "description": "Represents the + key in screen reader content as a readable word" + }, + "equalsCharacterDescriptor": { + "message": "Equals", + "description": "Represents the = key in screen reader content as a readable word" + }, + "braceLeftCharacterDescriptor": { + "message": "Left brace", + "description": "Represents the { key in screen reader content as a readable word" + }, + "braceRightCharacterDescriptor": { + "message": "Right brace", + "description": "Represents the } key in screen reader content as a readable word" + }, + "bracketLeftCharacterDescriptor": { + "message": "Left bracket", + "description": "Represents the [ key in screen reader content as a readable word" + }, + "bracketRightCharacterDescriptor": { + "message": "Right bracket", + "description": "Represents the ] key in screen reader content as a readable word" + }, + "pipeCharacterDescriptor": { + "message": "Pipe", + "description": "Represents the | key in screen reader content as a readable word" + }, + "backSlashCharacterDescriptor": { + "message": "Back slash", + "description": "Represents the back slash key in screen reader content as a readable word" + }, + "colonCharacterDescriptor": { + "message": "Colon", + "description": "Represents the : key in screen reader content as a readable word" + }, + "semicolonCharacterDescriptor": { + "message": "Semicolon", + "description": "Represents the ; key in screen reader content as a readable word" + }, + "doubleQuoteCharacterDescriptor": { + "message": "Double quote", + "description": "Represents the double quote key in screen reader content as a readable word" + }, + "singleQuoteCharacterDescriptor": { + "message": "Single quote", + "description": "Represents the ' key in screen reader content as a readable word" + }, + "lessThanCharacterDescriptor": { + "message": "Less than", + "description": "Represents the < key in screen reader content as a readable word" + }, + "greaterThanCharacterDescriptor": { + "message": "Greater than", + "description": "Represents the > key in screen reader content as a readable word" + }, + "commaCharacterDescriptor": { + "message": "Comma", + "description": "Represents the , key in screen reader content as a readable word" + }, + "periodCharacterDescriptor": { + "message": "Period", + "description": "Represents the . key in screen reader content as a readable word" + }, + "questionCharacterDescriptor": { + "message": "Question mark", + "description": "Represents the ? key in screen reader content as a readable word" + }, + "forwardSlashCharacterDescriptor": { + "message": "Forward slash", + "description": "Represents the / key in screen reader content as a readable word" + }, + "lowercaseAriaLabel": { + "message": "Lowercase" + }, + "uppercaseAriaLabel": { + "message": "Uppercase" + }, + "generatedPassword": { + "message": "Generated password" } } diff --git a/apps/browser/src/_locales/sv/messages.json b/apps/browser/src/_locales/sv/messages.json index bbc33e9456e..175f4824179 100644 --- a/apps/browser/src/_locales/sv/messages.json +++ b/apps/browser/src/_locales/sv/messages.json @@ -19,6 +19,18 @@ "createAccount": { "message": "Skapa konto" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" + }, "setAStrongPassword": { "message": "Ställ in ett starkt lösenord" }, @@ -107,6 +119,9 @@ "copyPassword": { "message": "Kopiera lösenord" }, + "copyPassphrase": { + "message": "Copy passphrase" + }, "copyNote": { "message": "Kopiera anteckning" }, @@ -152,6 +167,10 @@ "copyNotes": { "message": "Kopiera anteckningar" }, + "fill": { + "message": "Fill", + "description": "This string is used on the vault page to indicate autofilling. Horizontal space is limited in the interface here so try and keep translations as concise as possible." + }, "autoFill": { "message": "Fyll i automatiskt" }, @@ -407,6 +426,9 @@ "generatePassword": { "message": "Generera lösenord" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "regeneratePassword": { "message": "Återskapa lösenord" }, @@ -568,6 +590,15 @@ "launchWebsite": { "message": "Launch website" }, + "launchWebsiteName": { + "message": "Launch website $ITEMNAME$", + "placeholders": { + "itemname": { + "content": "$1", + "example": "Secret item" + } + } + }, "website": { "message": "Webbplats" }, @@ -814,6 +845,9 @@ "logIn": { "message": "Logga in" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "restartRegistration": { "message": "Restart registration" }, @@ -1389,6 +1423,10 @@ "baseUrl": { "message": "Server-URL" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "API-server-URL" }, @@ -2464,8 +2502,8 @@ "message": "Kräv valfritt ett lösenord för att användare ska komma åt denna Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendPasswordDescV2": { - "message": "Require this password to view the Send.", + "sendPasswordDescV3": { + "message": "Add an optional password for recipients to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendNotesDesc": { @@ -2827,6 +2865,9 @@ "generateUsername": { "message": "Generera användarnamn" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Typ av användarnamn" }, @@ -2867,6 +2908,14 @@ "forwardedEmailDesc": { "message": "Skapa ett e-postalias med en extern vidarebefordranstjänst." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$-fel: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -4494,9 +4543,15 @@ "itemLocation": { "message": "Item Location" }, + "fileSend": { + "message": "File Send" + }, "fileSends": { "message": "File Sends" }, + "textSend": { + "message": "Text Send" + }, "textSends": { "message": "Text Sends" }, @@ -4559,5 +4614,158 @@ }, "authenticating": { "message": "Authenticating" + }, + "fillGeneratedPassword": { + "message": "Fill generated password", + "description": "Heading for the password generator within the inline menu" + }, + "passwordRegenerated": { + "message": "Password regenerated", + "description": "Notification message for when a password has been regenerated" + }, + "saveLoginToBitwarden": { + "message": "Save login to Bitwarden?", + "description": "Confirmation message for saving a login to Bitwarden" + }, + "spaceCharacterDescriptor": { + "message": "Space", + "description": "Represents the space key in screen reader content as a readable word" + }, + "tildeCharacterDescriptor": { + "message": "Tilde", + "description": "Represents the ~ key in screen reader content as a readable word" + }, + "backtickCharacterDescriptor": { + "message": "Backtick", + "description": "Represents the ` key in screen reader content as a readable word" + }, + "exclamationCharacterDescriptor": { + "message": "Exclamation mark", + "description": "Represents the ! key in screen reader content as a readable word" + }, + "atSignCharacterDescriptor": { + "message": "At sign", + "description": "Represents the @ key in screen reader content as a readable word" + }, + "hashSignCharacterDescriptor": { + "message": "Hash sign", + "description": "Represents the # key in screen reader content as a readable word" + }, + "dollarSignCharacterDescriptor": { + "message": "Dollar sign", + "description": "Represents the $ key in screen reader content as a readable word" + }, + "percentSignCharacterDescriptor": { + "message": "Percent sign", + "description": "Represents the % key in screen reader content as a readable word" + }, + "caretCharacterDescriptor": { + "message": "Caret", + "description": "Represents the ^ key in screen reader content as a readable word" + }, + "ampersandCharacterDescriptor": { + "message": "Ampersand", + "description": "Represents the & key in screen reader content as a readable word" + }, + "asteriskCharacterDescriptor": { + "message": "Asterisk", + "description": "Represents the * key in screen reader content as a readable word" + }, + "parenLeftCharacterDescriptor": { + "message": "Left parenthesis", + "description": "Represents the ( key in screen reader content as a readable word" + }, + "parenRightCharacterDescriptor": { + "message": "Right parenthesis", + "description": "Represents the ) key in screen reader content as a readable word" + }, + "hyphenCharacterDescriptor": { + "message": "Underscore", + "description": "Represents the _ key in screen reader content as a readable word" + }, + "underscoreCharacterDescriptor": { + "message": "Hyphen", + "description": "Represents the - key in screen reader content as a readable word" + }, + "plusCharacterDescriptor": { + "message": "Plus", + "description": "Represents the + key in screen reader content as a readable word" + }, + "equalsCharacterDescriptor": { + "message": "Equals", + "description": "Represents the = key in screen reader content as a readable word" + }, + "braceLeftCharacterDescriptor": { + "message": "Left brace", + "description": "Represents the { key in screen reader content as a readable word" + }, + "braceRightCharacterDescriptor": { + "message": "Right brace", + "description": "Represents the } key in screen reader content as a readable word" + }, + "bracketLeftCharacterDescriptor": { + "message": "Left bracket", + "description": "Represents the [ key in screen reader content as a readable word" + }, + "bracketRightCharacterDescriptor": { + "message": "Right bracket", + "description": "Represents the ] key in screen reader content as a readable word" + }, + "pipeCharacterDescriptor": { + "message": "Pipe", + "description": "Represents the | key in screen reader content as a readable word" + }, + "backSlashCharacterDescriptor": { + "message": "Back slash", + "description": "Represents the back slash key in screen reader content as a readable word" + }, + "colonCharacterDescriptor": { + "message": "Colon", + "description": "Represents the : key in screen reader content as a readable word" + }, + "semicolonCharacterDescriptor": { + "message": "Semicolon", + "description": "Represents the ; key in screen reader content as a readable word" + }, + "doubleQuoteCharacterDescriptor": { + "message": "Double quote", + "description": "Represents the double quote key in screen reader content as a readable word" + }, + "singleQuoteCharacterDescriptor": { + "message": "Single quote", + "description": "Represents the ' key in screen reader content as a readable word" + }, + "lessThanCharacterDescriptor": { + "message": "Less than", + "description": "Represents the < key in screen reader content as a readable word" + }, + "greaterThanCharacterDescriptor": { + "message": "Greater than", + "description": "Represents the > key in screen reader content as a readable word" + }, + "commaCharacterDescriptor": { + "message": "Comma", + "description": "Represents the , key in screen reader content as a readable word" + }, + "periodCharacterDescriptor": { + "message": "Period", + "description": "Represents the . key in screen reader content as a readable word" + }, + "questionCharacterDescriptor": { + "message": "Question mark", + "description": "Represents the ? key in screen reader content as a readable word" + }, + "forwardSlashCharacterDescriptor": { + "message": "Forward slash", + "description": "Represents the / key in screen reader content as a readable word" + }, + "lowercaseAriaLabel": { + "message": "Lowercase" + }, + "uppercaseAriaLabel": { + "message": "Uppercase" + }, + "generatedPassword": { + "message": "Generated password" } } diff --git a/apps/browser/src/_locales/te/messages.json b/apps/browser/src/_locales/te/messages.json index 290663f4347..4037887cb28 100644 --- a/apps/browser/src/_locales/te/messages.json +++ b/apps/browser/src/_locales/te/messages.json @@ -19,6 +19,18 @@ "createAccount": { "message": "Create account" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" + }, "setAStrongPassword": { "message": "Set a strong password" }, @@ -107,6 +119,9 @@ "copyPassword": { "message": "Copy password" }, + "copyPassphrase": { + "message": "Copy passphrase" + }, "copyNote": { "message": "Copy note" }, @@ -152,6 +167,10 @@ "copyNotes": { "message": "Copy notes" }, + "fill": { + "message": "Fill", + "description": "This string is used on the vault page to indicate autofilling. Horizontal space is limited in the interface here so try and keep translations as concise as possible." + }, "autoFill": { "message": "Autofill" }, @@ -407,6 +426,9 @@ "generatePassword": { "message": "Generate password" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "regeneratePassword": { "message": "Regenerate password" }, @@ -568,6 +590,15 @@ "launchWebsite": { "message": "Launch website" }, + "launchWebsiteName": { + "message": "Launch website $ITEMNAME$", + "placeholders": { + "itemname": { + "content": "$1", + "example": "Secret item" + } + } + }, "website": { "message": "Website" }, @@ -814,6 +845,9 @@ "logIn": { "message": "Log in" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "restartRegistration": { "message": "Restart registration" }, @@ -1389,6 +1423,10 @@ "baseUrl": { "message": "Server URL" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "API server URL" }, @@ -2464,8 +2502,8 @@ "message": "Optionally require a password for users to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendPasswordDescV2": { - "message": "Require this password to view the Send.", + "sendPasswordDescV3": { + "message": "Add an optional password for recipients to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendNotesDesc": { @@ -2827,6 +2865,9 @@ "generateUsername": { "message": "Generate username" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Username type" }, @@ -2867,6 +2908,14 @@ "forwardedEmailDesc": { "message": "Generate an email alias with an external forwarding service." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ error: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -4494,9 +4543,15 @@ "itemLocation": { "message": "Item Location" }, + "fileSend": { + "message": "File Send" + }, "fileSends": { "message": "File Sends" }, + "textSend": { + "message": "Text Send" + }, "textSends": { "message": "Text Sends" }, @@ -4559,5 +4614,158 @@ }, "authenticating": { "message": "Authenticating" + }, + "fillGeneratedPassword": { + "message": "Fill generated password", + "description": "Heading for the password generator within the inline menu" + }, + "passwordRegenerated": { + "message": "Password regenerated", + "description": "Notification message for when a password has been regenerated" + }, + "saveLoginToBitwarden": { + "message": "Save login to Bitwarden?", + "description": "Confirmation message for saving a login to Bitwarden" + }, + "spaceCharacterDescriptor": { + "message": "Space", + "description": "Represents the space key in screen reader content as a readable word" + }, + "tildeCharacterDescriptor": { + "message": "Tilde", + "description": "Represents the ~ key in screen reader content as a readable word" + }, + "backtickCharacterDescriptor": { + "message": "Backtick", + "description": "Represents the ` key in screen reader content as a readable word" + }, + "exclamationCharacterDescriptor": { + "message": "Exclamation mark", + "description": "Represents the ! key in screen reader content as a readable word" + }, + "atSignCharacterDescriptor": { + "message": "At sign", + "description": "Represents the @ key in screen reader content as a readable word" + }, + "hashSignCharacterDescriptor": { + "message": "Hash sign", + "description": "Represents the # key in screen reader content as a readable word" + }, + "dollarSignCharacterDescriptor": { + "message": "Dollar sign", + "description": "Represents the $ key in screen reader content as a readable word" + }, + "percentSignCharacterDescriptor": { + "message": "Percent sign", + "description": "Represents the % key in screen reader content as a readable word" + }, + "caretCharacterDescriptor": { + "message": "Caret", + "description": "Represents the ^ key in screen reader content as a readable word" + }, + "ampersandCharacterDescriptor": { + "message": "Ampersand", + "description": "Represents the & key in screen reader content as a readable word" + }, + "asteriskCharacterDescriptor": { + "message": "Asterisk", + "description": "Represents the * key in screen reader content as a readable word" + }, + "parenLeftCharacterDescriptor": { + "message": "Left parenthesis", + "description": "Represents the ( key in screen reader content as a readable word" + }, + "parenRightCharacterDescriptor": { + "message": "Right parenthesis", + "description": "Represents the ) key in screen reader content as a readable word" + }, + "hyphenCharacterDescriptor": { + "message": "Underscore", + "description": "Represents the _ key in screen reader content as a readable word" + }, + "underscoreCharacterDescriptor": { + "message": "Hyphen", + "description": "Represents the - key in screen reader content as a readable word" + }, + "plusCharacterDescriptor": { + "message": "Plus", + "description": "Represents the + key in screen reader content as a readable word" + }, + "equalsCharacterDescriptor": { + "message": "Equals", + "description": "Represents the = key in screen reader content as a readable word" + }, + "braceLeftCharacterDescriptor": { + "message": "Left brace", + "description": "Represents the { key in screen reader content as a readable word" + }, + "braceRightCharacterDescriptor": { + "message": "Right brace", + "description": "Represents the } key in screen reader content as a readable word" + }, + "bracketLeftCharacterDescriptor": { + "message": "Left bracket", + "description": "Represents the [ key in screen reader content as a readable word" + }, + "bracketRightCharacterDescriptor": { + "message": "Right bracket", + "description": "Represents the ] key in screen reader content as a readable word" + }, + "pipeCharacterDescriptor": { + "message": "Pipe", + "description": "Represents the | key in screen reader content as a readable word" + }, + "backSlashCharacterDescriptor": { + "message": "Back slash", + "description": "Represents the back slash key in screen reader content as a readable word" + }, + "colonCharacterDescriptor": { + "message": "Colon", + "description": "Represents the : key in screen reader content as a readable word" + }, + "semicolonCharacterDescriptor": { + "message": "Semicolon", + "description": "Represents the ; key in screen reader content as a readable word" + }, + "doubleQuoteCharacterDescriptor": { + "message": "Double quote", + "description": "Represents the double quote key in screen reader content as a readable word" + }, + "singleQuoteCharacterDescriptor": { + "message": "Single quote", + "description": "Represents the ' key in screen reader content as a readable word" + }, + "lessThanCharacterDescriptor": { + "message": "Less than", + "description": "Represents the < key in screen reader content as a readable word" + }, + "greaterThanCharacterDescriptor": { + "message": "Greater than", + "description": "Represents the > key in screen reader content as a readable word" + }, + "commaCharacterDescriptor": { + "message": "Comma", + "description": "Represents the , key in screen reader content as a readable word" + }, + "periodCharacterDescriptor": { + "message": "Period", + "description": "Represents the . key in screen reader content as a readable word" + }, + "questionCharacterDescriptor": { + "message": "Question mark", + "description": "Represents the ? key in screen reader content as a readable word" + }, + "forwardSlashCharacterDescriptor": { + "message": "Forward slash", + "description": "Represents the / key in screen reader content as a readable word" + }, + "lowercaseAriaLabel": { + "message": "Lowercase" + }, + "uppercaseAriaLabel": { + "message": "Uppercase" + }, + "generatedPassword": { + "message": "Generated password" } } diff --git a/apps/browser/src/_locales/th/messages.json b/apps/browser/src/_locales/th/messages.json index 66903ea6e6e..e157ac74fed 100644 --- a/apps/browser/src/_locales/th/messages.json +++ b/apps/browser/src/_locales/th/messages.json @@ -19,6 +19,18 @@ "createAccount": { "message": "สร้างบัญชี" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" + }, "setAStrongPassword": { "message": "Set a strong password" }, @@ -107,6 +119,9 @@ "copyPassword": { "message": "คัดลอกรหัสผ่าน" }, + "copyPassphrase": { + "message": "Copy passphrase" + }, "copyNote": { "message": "Copy Note" }, @@ -152,6 +167,10 @@ "copyNotes": { "message": "Copy notes" }, + "fill": { + "message": "Fill", + "description": "This string is used on the vault page to indicate autofilling. Horizontal space is limited in the interface here so try and keep translations as concise as possible." + }, "autoFill": { "message": "กรอกข้อมูลอัตโนมัติ" }, @@ -407,6 +426,9 @@ "generatePassword": { "message": "Generate Password" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "regeneratePassword": { "message": "Regenerate Password" }, @@ -568,6 +590,15 @@ "launchWebsite": { "message": "Launch website" }, + "launchWebsiteName": { + "message": "Launch website $ITEMNAME$", + "placeholders": { + "itemname": { + "content": "$1", + "example": "Secret item" + } + } + }, "website": { "message": "เว็บไซต์" }, @@ -814,6 +845,9 @@ "logIn": { "message": "Log in" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "restartRegistration": { "message": "Restart registration" }, @@ -1389,6 +1423,10 @@ "baseUrl": { "message": "URL ของเซิร์ฟเวอร์" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "API Server URL" }, @@ -2464,8 +2502,8 @@ "message": "Optionally require a password for users to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendPasswordDescV2": { - "message": "Require this password to view the Send.", + "sendPasswordDescV3": { + "message": "Add an optional password for recipients to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendNotesDesc": { @@ -2827,6 +2865,9 @@ "generateUsername": { "message": "Generate username" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Username type" }, @@ -2867,6 +2908,14 @@ "forwardedEmailDesc": { "message": "Generate an email alias with an external forwarding service." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ error: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -4494,9 +4543,15 @@ "itemLocation": { "message": "Item Location" }, + "fileSend": { + "message": "File Send" + }, "fileSends": { "message": "File Sends" }, + "textSend": { + "message": "Text Send" + }, "textSends": { "message": "Text Sends" }, @@ -4559,5 +4614,158 @@ }, "authenticating": { "message": "Authenticating" + }, + "fillGeneratedPassword": { + "message": "Fill generated password", + "description": "Heading for the password generator within the inline menu" + }, + "passwordRegenerated": { + "message": "Password regenerated", + "description": "Notification message for when a password has been regenerated" + }, + "saveLoginToBitwarden": { + "message": "Save login to Bitwarden?", + "description": "Confirmation message for saving a login to Bitwarden" + }, + "spaceCharacterDescriptor": { + "message": "Space", + "description": "Represents the space key in screen reader content as a readable word" + }, + "tildeCharacterDescriptor": { + "message": "Tilde", + "description": "Represents the ~ key in screen reader content as a readable word" + }, + "backtickCharacterDescriptor": { + "message": "Backtick", + "description": "Represents the ` key in screen reader content as a readable word" + }, + "exclamationCharacterDescriptor": { + "message": "Exclamation mark", + "description": "Represents the ! key in screen reader content as a readable word" + }, + "atSignCharacterDescriptor": { + "message": "At sign", + "description": "Represents the @ key in screen reader content as a readable word" + }, + "hashSignCharacterDescriptor": { + "message": "Hash sign", + "description": "Represents the # key in screen reader content as a readable word" + }, + "dollarSignCharacterDescriptor": { + "message": "Dollar sign", + "description": "Represents the $ key in screen reader content as a readable word" + }, + "percentSignCharacterDescriptor": { + "message": "Percent sign", + "description": "Represents the % key in screen reader content as a readable word" + }, + "caretCharacterDescriptor": { + "message": "Caret", + "description": "Represents the ^ key in screen reader content as a readable word" + }, + "ampersandCharacterDescriptor": { + "message": "Ampersand", + "description": "Represents the & key in screen reader content as a readable word" + }, + "asteriskCharacterDescriptor": { + "message": "Asterisk", + "description": "Represents the * key in screen reader content as a readable word" + }, + "parenLeftCharacterDescriptor": { + "message": "Left parenthesis", + "description": "Represents the ( key in screen reader content as a readable word" + }, + "parenRightCharacterDescriptor": { + "message": "Right parenthesis", + "description": "Represents the ) key in screen reader content as a readable word" + }, + "hyphenCharacterDescriptor": { + "message": "Underscore", + "description": "Represents the _ key in screen reader content as a readable word" + }, + "underscoreCharacterDescriptor": { + "message": "Hyphen", + "description": "Represents the - key in screen reader content as a readable word" + }, + "plusCharacterDescriptor": { + "message": "Plus", + "description": "Represents the + key in screen reader content as a readable word" + }, + "equalsCharacterDescriptor": { + "message": "Equals", + "description": "Represents the = key in screen reader content as a readable word" + }, + "braceLeftCharacterDescriptor": { + "message": "Left brace", + "description": "Represents the { key in screen reader content as a readable word" + }, + "braceRightCharacterDescriptor": { + "message": "Right brace", + "description": "Represents the } key in screen reader content as a readable word" + }, + "bracketLeftCharacterDescriptor": { + "message": "Left bracket", + "description": "Represents the [ key in screen reader content as a readable word" + }, + "bracketRightCharacterDescriptor": { + "message": "Right bracket", + "description": "Represents the ] key in screen reader content as a readable word" + }, + "pipeCharacterDescriptor": { + "message": "Pipe", + "description": "Represents the | key in screen reader content as a readable word" + }, + "backSlashCharacterDescriptor": { + "message": "Back slash", + "description": "Represents the back slash key in screen reader content as a readable word" + }, + "colonCharacterDescriptor": { + "message": "Colon", + "description": "Represents the : key in screen reader content as a readable word" + }, + "semicolonCharacterDescriptor": { + "message": "Semicolon", + "description": "Represents the ; key in screen reader content as a readable word" + }, + "doubleQuoteCharacterDescriptor": { + "message": "Double quote", + "description": "Represents the double quote key in screen reader content as a readable word" + }, + "singleQuoteCharacterDescriptor": { + "message": "Single quote", + "description": "Represents the ' key in screen reader content as a readable word" + }, + "lessThanCharacterDescriptor": { + "message": "Less than", + "description": "Represents the < key in screen reader content as a readable word" + }, + "greaterThanCharacterDescriptor": { + "message": "Greater than", + "description": "Represents the > key in screen reader content as a readable word" + }, + "commaCharacterDescriptor": { + "message": "Comma", + "description": "Represents the , key in screen reader content as a readable word" + }, + "periodCharacterDescriptor": { + "message": "Period", + "description": "Represents the . key in screen reader content as a readable word" + }, + "questionCharacterDescriptor": { + "message": "Question mark", + "description": "Represents the ? key in screen reader content as a readable word" + }, + "forwardSlashCharacterDescriptor": { + "message": "Forward slash", + "description": "Represents the / key in screen reader content as a readable word" + }, + "lowercaseAriaLabel": { + "message": "Lowercase" + }, + "uppercaseAriaLabel": { + "message": "Uppercase" + }, + "generatedPassword": { + "message": "Generated password" } } diff --git a/apps/browser/src/_locales/tr/messages.json b/apps/browser/src/_locales/tr/messages.json index a7ea5dfd146..7ed5f94e29d 100644 --- a/apps/browser/src/_locales/tr/messages.json +++ b/apps/browser/src/_locales/tr/messages.json @@ -19,6 +19,18 @@ "createAccount": { "message": "Hesap oluştur" }, + "newToBitwarden": { + "message": "Bitwarden'da yeni misiniz?" + }, + "logInWithPasskey": { + "message": "Geçiş anahtarıyla giriş yap" + }, + "useSingleSignOn": { + "message": "Çoklu oturum açma kullan" + }, + "welcomeBack": { + "message": "Tekrar hoş geldiniz" + }, "setAStrongPassword": { "message": "Güçlü bir parola belirleyin" }, @@ -107,6 +119,9 @@ "copyPassword": { "message": "Parolayı kopyala" }, + "copyPassphrase": { + "message": "Copy passphrase" + }, "copyNote": { "message": "Notu kopyala" }, @@ -152,6 +167,10 @@ "copyNotes": { "message": "Notları kopyala" }, + "fill": { + "message": "Doldur", + "description": "This string is used on the vault page to indicate autofilling. Horizontal space is limited in the interface here so try and keep translations as concise as possible." + }, "autoFill": { "message": "Otomatik doldur" }, @@ -407,6 +426,9 @@ "generatePassword": { "message": "Parola oluştur" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "regeneratePassword": { "message": "Yeni parola oluştur" }, @@ -568,6 +590,15 @@ "launchWebsite": { "message": "Web sitesini aç" }, + "launchWebsiteName": { + "message": "$ITEMNAME$ web sitesini aç", + "placeholders": { + "itemname": { + "content": "$1", + "example": "Secret item" + } + } + }, "website": { "message": "Web sitesi" }, @@ -814,6 +845,9 @@ "logIn": { "message": "Giriş yap" }, + "logInToBitwarden": { + "message": "Bitwarden'a giriş yapın" + }, "restartRegistration": { "message": "Kaydı yeniden başlat" }, @@ -1389,6 +1423,10 @@ "baseUrl": { "message": "Sunucu URL'si" }, + "selfHostBaseUrl": { + "message": "Kendi kendine barındırılan sunucu URL'si", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "API sunucu URL'si" }, @@ -2464,8 +2502,8 @@ "message": "Kullanıcıların bu Send'e erişmek için parola girmelerini isteyebilirsiniz.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendPasswordDescV2": { - "message": "Send'i görüntülemek için bu parolayı iste.", + "sendPasswordDescV3": { + "message": "Alıcıların bu Send'e erişmesi için isterseniz parola ekleyebilirsiniz.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendNotesDesc": { @@ -2827,6 +2865,9 @@ "generateUsername": { "message": "Kullanıcı adı oluştur" }, + "generateEmail": { + "message": "E-posta oluştur" + }, "usernameType": { "message": "Kullanıcı adı türü" }, @@ -2867,6 +2908,14 @@ "forwardedEmailDesc": { "message": "Harici bir yönlendirme servisiyle e-posta maskesi oluştur." }, + "forwarderDomainName": { + "message": "E-posta alan adı", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Seçtiğiniz servisin desteklediği bir alan adı seçin", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ hatası: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -4494,9 +4543,15 @@ "itemLocation": { "message": "Kayıt konumu" }, + "fileSend": { + "message": "Dosya Send'i" + }, "fileSends": { "message": "Dosya Send'leri" }, + "textSend": { + "message": "Metin Send'i" + }, "textSends": { "message": "Metin Send'leri" }, @@ -4559,5 +4614,158 @@ }, "authenticating": { "message": "Kimlik doğrulanıyor" + }, + "fillGeneratedPassword": { + "message": "Üretilen parolayı doldur", + "description": "Heading for the password generator within the inline menu" + }, + "passwordRegenerated": { + "message": "Parola yeniden üretildi", + "description": "Notification message for when a password has been regenerated" + }, + "saveLoginToBitwarden": { + "message": "Hesap Bitwarden'a kaydedilsin mi?", + "description": "Confirmation message for saving a login to Bitwarden" + }, + "spaceCharacterDescriptor": { + "message": "Boşluk", + "description": "Represents the space key in screen reader content as a readable word" + }, + "tildeCharacterDescriptor": { + "message": "Tilde", + "description": "Represents the ~ key in screen reader content as a readable word" + }, + "backtickCharacterDescriptor": { + "message": "Ters tırnak", + "description": "Represents the ` key in screen reader content as a readable word" + }, + "exclamationCharacterDescriptor": { + "message": "Ünlem işareti", + "description": "Represents the ! key in screen reader content as a readable word" + }, + "atSignCharacterDescriptor": { + "message": "At işareti", + "description": "Represents the @ key in screen reader content as a readable word" + }, + "hashSignCharacterDescriptor": { + "message": "Kare işareti", + "description": "Represents the # key in screen reader content as a readable word" + }, + "dollarSignCharacterDescriptor": { + "message": "Dolar işareti", + "description": "Represents the $ key in screen reader content as a readable word" + }, + "percentSignCharacterDescriptor": { + "message": "Yüzde işareti", + "description": "Represents the % key in screen reader content as a readable word" + }, + "caretCharacterDescriptor": { + "message": "Düzeltme işareti", + "description": "Represents the ^ key in screen reader content as a readable word" + }, + "ampersandCharacterDescriptor": { + "message": "Ve işareti", + "description": "Represents the & key in screen reader content as a readable word" + }, + "asteriskCharacterDescriptor": { + "message": "Yıldız işareti", + "description": "Represents the * key in screen reader content as a readable word" + }, + "parenLeftCharacterDescriptor": { + "message": "Sol parantez", + "description": "Represents the ( key in screen reader content as a readable word" + }, + "parenRightCharacterDescriptor": { + "message": "Sağ parantez", + "description": "Represents the ) key in screen reader content as a readable word" + }, + "hyphenCharacterDescriptor": { + "message": "Alt çizgi", + "description": "Represents the _ key in screen reader content as a readable word" + }, + "underscoreCharacterDescriptor": { + "message": "Tire", + "description": "Represents the - key in screen reader content as a readable word" + }, + "plusCharacterDescriptor": { + "message": "Artı", + "description": "Represents the + key in screen reader content as a readable word" + }, + "equalsCharacterDescriptor": { + "message": "Eşittir", + "description": "Represents the = key in screen reader content as a readable word" + }, + "braceLeftCharacterDescriptor": { + "message": "Sol küme parantezi", + "description": "Represents the { key in screen reader content as a readable word" + }, + "braceRightCharacterDescriptor": { + "message": "Sağ küme parantezi", + "description": "Represents the } key in screen reader content as a readable word" + }, + "bracketLeftCharacterDescriptor": { + "message": "Sol köşeli parantez", + "description": "Represents the [ key in screen reader content as a readable word" + }, + "bracketRightCharacterDescriptor": { + "message": "Sağ köşeli parantez", + "description": "Represents the ] key in screen reader content as a readable word" + }, + "pipeCharacterDescriptor": { + "message": "Çubuk", + "description": "Represents the | key in screen reader content as a readable word" + }, + "backSlashCharacterDescriptor": { + "message": "Ters eğik çizgi", + "description": "Represents the back slash key in screen reader content as a readable word" + }, + "colonCharacterDescriptor": { + "message": "İki nokta", + "description": "Represents the : key in screen reader content as a readable word" + }, + "semicolonCharacterDescriptor": { + "message": "Noktalı virgül", + "description": "Represents the ; key in screen reader content as a readable word" + }, + "doubleQuoteCharacterDescriptor": { + "message": "Çift tırnak", + "description": "Represents the double quote key in screen reader content as a readable word" + }, + "singleQuoteCharacterDescriptor": { + "message": "Tek tırnak", + "description": "Represents the ' key in screen reader content as a readable word" + }, + "lessThanCharacterDescriptor": { + "message": "Küçüktür", + "description": "Represents the < key in screen reader content as a readable word" + }, + "greaterThanCharacterDescriptor": { + "message": "Büyüktür", + "description": "Represents the > key in screen reader content as a readable word" + }, + "commaCharacterDescriptor": { + "message": "Virgül", + "description": "Represents the , key in screen reader content as a readable word" + }, + "periodCharacterDescriptor": { + "message": "Nokta", + "description": "Represents the . key in screen reader content as a readable word" + }, + "questionCharacterDescriptor": { + "message": "Soru işareti", + "description": "Represents the ? key in screen reader content as a readable word" + }, + "forwardSlashCharacterDescriptor": { + "message": "Bölü işareti", + "description": "Represents the / key in screen reader content as a readable word" + }, + "lowercaseAriaLabel": { + "message": "Küçük harf" + }, + "uppercaseAriaLabel": { + "message": "Büyük harf" + }, + "generatedPassword": { + "message": "Üretilen parola" } } diff --git a/apps/browser/src/_locales/uk/messages.json b/apps/browser/src/_locales/uk/messages.json index d5b6d5ade70..74544eca2f2 100644 --- a/apps/browser/src/_locales/uk/messages.json +++ b/apps/browser/src/_locales/uk/messages.json @@ -19,6 +19,18 @@ "createAccount": { "message": "Створити обліковий запис" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" + }, "setAStrongPassword": { "message": "Встановіть надійний пароль" }, @@ -107,6 +119,9 @@ "copyPassword": { "message": "Копіювати пароль" }, + "copyPassphrase": { + "message": "Копіювати парольну фразу" + }, "copyNote": { "message": "Копіювати нотатку" }, @@ -152,6 +167,10 @@ "copyNotes": { "message": "Копіювати нотатки" }, + "fill": { + "message": "Fill", + "description": "This string is used on the vault page to indicate autofilling. Horizontal space is limited in the interface here so try and keep translations as concise as possible." + }, "autoFill": { "message": "Автозаповнення" }, @@ -407,6 +426,9 @@ "generatePassword": { "message": "Генерувати пароль" }, + "generatePassphrase": { + "message": "Генерувати парольну фразу" + }, "regeneratePassword": { "message": "Генерувати новий" }, @@ -568,6 +590,15 @@ "launchWebsite": { "message": "Відкрити вебсайт" }, + "launchWebsiteName": { + "message": "Launch website $ITEMNAME$", + "placeholders": { + "itemname": { + "content": "$1", + "example": "Secret item" + } + } + }, "website": { "message": "Вебсайт" }, @@ -814,6 +845,9 @@ "logIn": { "message": "Увійти" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "restartRegistration": { "message": "Перезапустити реєстрацію" }, @@ -1389,6 +1423,10 @@ "baseUrl": { "message": "URL-адреса сервера" }, + "selfHostBaseUrl": { + "message": "URL-адреса власного сервера", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "URL-адреса сервера API" }, @@ -2464,8 +2502,8 @@ "message": "Ви можете встановити пароль для доступу до цього відправлення.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendPasswordDescV2": { - "message": "Вимагати цей пароль для перегляду відправлення.", + "sendPasswordDescV3": { + "message": "За бажання додайте пароль для отримувачів цього відправлення.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendNotesDesc": { @@ -2827,6 +2865,9 @@ "generateUsername": { "message": "Генерувати ім'я користувача" }, + "generateEmail": { + "message": "Генерувати е-пошту" + }, "usernameType": { "message": "Тип імені користувача" }, @@ -2867,6 +2908,14 @@ "forwardedEmailDesc": { "message": "Згенеруйте псевдонім е-пошти зі стороннім сервісом пересилання." }, + "forwarderDomainName": { + "message": "Домен електронної пошти", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Виберіть домен, який підтримується вибраною службою", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "Помилка $SERVICENAME$: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -4494,9 +4543,15 @@ "itemLocation": { "message": "Розташування запису" }, + "fileSend": { + "message": "Відправлення файлу" + }, "fileSends": { "message": "Відправлення файлів" }, + "textSend": { + "message": "Відправлення тексту" + }, "textSends": { "message": "Відправлення тексту" }, @@ -4559,5 +4614,158 @@ }, "authenticating": { "message": "Аутентифікація" + }, + "fillGeneratedPassword": { + "message": "Fill generated password", + "description": "Heading for the password generator within the inline menu" + }, + "passwordRegenerated": { + "message": "Password regenerated", + "description": "Notification message for when a password has been regenerated" + }, + "saveLoginToBitwarden": { + "message": "Save login to Bitwarden?", + "description": "Confirmation message for saving a login to Bitwarden" + }, + "spaceCharacterDescriptor": { + "message": "Space", + "description": "Represents the space key in screen reader content as a readable word" + }, + "tildeCharacterDescriptor": { + "message": "Tilde", + "description": "Represents the ~ key in screen reader content as a readable word" + }, + "backtickCharacterDescriptor": { + "message": "Backtick", + "description": "Represents the ` key in screen reader content as a readable word" + }, + "exclamationCharacterDescriptor": { + "message": "Exclamation mark", + "description": "Represents the ! key in screen reader content as a readable word" + }, + "atSignCharacterDescriptor": { + "message": "At sign", + "description": "Represents the @ key in screen reader content as a readable word" + }, + "hashSignCharacterDescriptor": { + "message": "Hash sign", + "description": "Represents the # key in screen reader content as a readable word" + }, + "dollarSignCharacterDescriptor": { + "message": "Dollar sign", + "description": "Represents the $ key in screen reader content as a readable word" + }, + "percentSignCharacterDescriptor": { + "message": "Percent sign", + "description": "Represents the % key in screen reader content as a readable word" + }, + "caretCharacterDescriptor": { + "message": "Caret", + "description": "Represents the ^ key in screen reader content as a readable word" + }, + "ampersandCharacterDescriptor": { + "message": "Ampersand", + "description": "Represents the & key in screen reader content as a readable word" + }, + "asteriskCharacterDescriptor": { + "message": "Asterisk", + "description": "Represents the * key in screen reader content as a readable word" + }, + "parenLeftCharacterDescriptor": { + "message": "Left parenthesis", + "description": "Represents the ( key in screen reader content as a readable word" + }, + "parenRightCharacterDescriptor": { + "message": "Right parenthesis", + "description": "Represents the ) key in screen reader content as a readable word" + }, + "hyphenCharacterDescriptor": { + "message": "Underscore", + "description": "Represents the _ key in screen reader content as a readable word" + }, + "underscoreCharacterDescriptor": { + "message": "Hyphen", + "description": "Represents the - key in screen reader content as a readable word" + }, + "plusCharacterDescriptor": { + "message": "Plus", + "description": "Represents the + key in screen reader content as a readable word" + }, + "equalsCharacterDescriptor": { + "message": "Equals", + "description": "Represents the = key in screen reader content as a readable word" + }, + "braceLeftCharacterDescriptor": { + "message": "Left brace", + "description": "Represents the { key in screen reader content as a readable word" + }, + "braceRightCharacterDescriptor": { + "message": "Right brace", + "description": "Represents the } key in screen reader content as a readable word" + }, + "bracketLeftCharacterDescriptor": { + "message": "Left bracket", + "description": "Represents the [ key in screen reader content as a readable word" + }, + "bracketRightCharacterDescriptor": { + "message": "Right bracket", + "description": "Represents the ] key in screen reader content as a readable word" + }, + "pipeCharacterDescriptor": { + "message": "Pipe", + "description": "Represents the | key in screen reader content as a readable word" + }, + "backSlashCharacterDescriptor": { + "message": "Back slash", + "description": "Represents the back slash key in screen reader content as a readable word" + }, + "colonCharacterDescriptor": { + "message": "Colon", + "description": "Represents the : key in screen reader content as a readable word" + }, + "semicolonCharacterDescriptor": { + "message": "Semicolon", + "description": "Represents the ; key in screen reader content as a readable word" + }, + "doubleQuoteCharacterDescriptor": { + "message": "Double quote", + "description": "Represents the double quote key in screen reader content as a readable word" + }, + "singleQuoteCharacterDescriptor": { + "message": "Single quote", + "description": "Represents the ' key in screen reader content as a readable word" + }, + "lessThanCharacterDescriptor": { + "message": "Less than", + "description": "Represents the < key in screen reader content as a readable word" + }, + "greaterThanCharacterDescriptor": { + "message": "Greater than", + "description": "Represents the > key in screen reader content as a readable word" + }, + "commaCharacterDescriptor": { + "message": "Comma", + "description": "Represents the , key in screen reader content as a readable word" + }, + "periodCharacterDescriptor": { + "message": "Period", + "description": "Represents the . key in screen reader content as a readable word" + }, + "questionCharacterDescriptor": { + "message": "Question mark", + "description": "Represents the ? key in screen reader content as a readable word" + }, + "forwardSlashCharacterDescriptor": { + "message": "Forward slash", + "description": "Represents the / key in screen reader content as a readable word" + }, + "lowercaseAriaLabel": { + "message": "Lowercase" + }, + "uppercaseAriaLabel": { + "message": "Uppercase" + }, + "generatedPassword": { + "message": "Generated password" } } diff --git a/apps/browser/src/_locales/vi/messages.json b/apps/browser/src/_locales/vi/messages.json index a378a56aa9f..37dfd796c6b 100644 --- a/apps/browser/src/_locales/vi/messages.json +++ b/apps/browser/src/_locales/vi/messages.json @@ -19,6 +19,18 @@ "createAccount": { "message": "Tạo tài khoản" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" + }, "setAStrongPassword": { "message": "Đặt mật khẩu mạnh" }, @@ -107,6 +119,9 @@ "copyPassword": { "message": "Sao chép mật khẩu" }, + "copyPassphrase": { + "message": "Copy passphrase" + }, "copyNote": { "message": "Sao chép ghi chú" }, @@ -152,6 +167,10 @@ "copyNotes": { "message": "Copy notes" }, + "fill": { + "message": "Fill", + "description": "This string is used on the vault page to indicate autofilling. Horizontal space is limited in the interface here so try and keep translations as concise as possible." + }, "autoFill": { "message": "Tự động điền" }, @@ -407,6 +426,9 @@ "generatePassword": { "message": "Tạo mật khẩu" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "regeneratePassword": { "message": "Tạo lại mật khẩu" }, @@ -568,6 +590,15 @@ "launchWebsite": { "message": "Mở trang web" }, + "launchWebsiteName": { + "message": "Launch website $ITEMNAME$", + "placeholders": { + "itemname": { + "content": "$1", + "example": "Secret item" + } + } + }, "website": { "message": "Trang web" }, @@ -814,6 +845,9 @@ "logIn": { "message": "Đăng nhập" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "restartRegistration": { "message": "Tiến hành đăng ký lại" }, @@ -1389,6 +1423,10 @@ "baseUrl": { "message": "URL máy chủ" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "Địa chỉ API máy chủ" }, @@ -2464,8 +2502,8 @@ "message": "Yêu cầu nhập mật khẩu khi người dùng truy cập vào phần Gửi này.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendPasswordDescV2": { - "message": "Require this password to view the Send.", + "sendPasswordDescV3": { + "message": "Add an optional password for recipients to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendNotesDesc": { @@ -2827,6 +2865,9 @@ "generateUsername": { "message": "Tạo tên người dùng" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Loại tên người dùng" }, @@ -2867,6 +2908,14 @@ "forwardedEmailDesc": { "message": "Tạo bí danh email với dịch vụ chuyển tiếp bên ngoài." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "Lỗi $SERVICENAME$: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -4494,9 +4543,15 @@ "itemLocation": { "message": "Vị trí mục" }, + "fileSend": { + "message": "File Send" + }, "fileSends": { "message": "File Sends" }, + "textSend": { + "message": "Text Send" + }, "textSends": { "message": "Text Sends" }, @@ -4559,5 +4614,158 @@ }, "authenticating": { "message": "Authenticating" + }, + "fillGeneratedPassword": { + "message": "Fill generated password", + "description": "Heading for the password generator within the inline menu" + }, + "passwordRegenerated": { + "message": "Password regenerated", + "description": "Notification message for when a password has been regenerated" + }, + "saveLoginToBitwarden": { + "message": "Save login to Bitwarden?", + "description": "Confirmation message for saving a login to Bitwarden" + }, + "spaceCharacterDescriptor": { + "message": "Space", + "description": "Represents the space key in screen reader content as a readable word" + }, + "tildeCharacterDescriptor": { + "message": "Tilde", + "description": "Represents the ~ key in screen reader content as a readable word" + }, + "backtickCharacterDescriptor": { + "message": "Backtick", + "description": "Represents the ` key in screen reader content as a readable word" + }, + "exclamationCharacterDescriptor": { + "message": "Exclamation mark", + "description": "Represents the ! key in screen reader content as a readable word" + }, + "atSignCharacterDescriptor": { + "message": "At sign", + "description": "Represents the @ key in screen reader content as a readable word" + }, + "hashSignCharacterDescriptor": { + "message": "Hash sign", + "description": "Represents the # key in screen reader content as a readable word" + }, + "dollarSignCharacterDescriptor": { + "message": "Dollar sign", + "description": "Represents the $ key in screen reader content as a readable word" + }, + "percentSignCharacterDescriptor": { + "message": "Percent sign", + "description": "Represents the % key in screen reader content as a readable word" + }, + "caretCharacterDescriptor": { + "message": "Caret", + "description": "Represents the ^ key in screen reader content as a readable word" + }, + "ampersandCharacterDescriptor": { + "message": "Ampersand", + "description": "Represents the & key in screen reader content as a readable word" + }, + "asteriskCharacterDescriptor": { + "message": "Asterisk", + "description": "Represents the * key in screen reader content as a readable word" + }, + "parenLeftCharacterDescriptor": { + "message": "Left parenthesis", + "description": "Represents the ( key in screen reader content as a readable word" + }, + "parenRightCharacterDescriptor": { + "message": "Right parenthesis", + "description": "Represents the ) key in screen reader content as a readable word" + }, + "hyphenCharacterDescriptor": { + "message": "Underscore", + "description": "Represents the _ key in screen reader content as a readable word" + }, + "underscoreCharacterDescriptor": { + "message": "Hyphen", + "description": "Represents the - key in screen reader content as a readable word" + }, + "plusCharacterDescriptor": { + "message": "Plus", + "description": "Represents the + key in screen reader content as a readable word" + }, + "equalsCharacterDescriptor": { + "message": "Equals", + "description": "Represents the = key in screen reader content as a readable word" + }, + "braceLeftCharacterDescriptor": { + "message": "Left brace", + "description": "Represents the { key in screen reader content as a readable word" + }, + "braceRightCharacterDescriptor": { + "message": "Right brace", + "description": "Represents the } key in screen reader content as a readable word" + }, + "bracketLeftCharacterDescriptor": { + "message": "Left bracket", + "description": "Represents the [ key in screen reader content as a readable word" + }, + "bracketRightCharacterDescriptor": { + "message": "Right bracket", + "description": "Represents the ] key in screen reader content as a readable word" + }, + "pipeCharacterDescriptor": { + "message": "Pipe", + "description": "Represents the | key in screen reader content as a readable word" + }, + "backSlashCharacterDescriptor": { + "message": "Back slash", + "description": "Represents the back slash key in screen reader content as a readable word" + }, + "colonCharacterDescriptor": { + "message": "Colon", + "description": "Represents the : key in screen reader content as a readable word" + }, + "semicolonCharacterDescriptor": { + "message": "Semicolon", + "description": "Represents the ; key in screen reader content as a readable word" + }, + "doubleQuoteCharacterDescriptor": { + "message": "Double quote", + "description": "Represents the double quote key in screen reader content as a readable word" + }, + "singleQuoteCharacterDescriptor": { + "message": "Single quote", + "description": "Represents the ' key in screen reader content as a readable word" + }, + "lessThanCharacterDescriptor": { + "message": "Less than", + "description": "Represents the < key in screen reader content as a readable word" + }, + "greaterThanCharacterDescriptor": { + "message": "Greater than", + "description": "Represents the > key in screen reader content as a readable word" + }, + "commaCharacterDescriptor": { + "message": "Comma", + "description": "Represents the , key in screen reader content as a readable word" + }, + "periodCharacterDescriptor": { + "message": "Period", + "description": "Represents the . key in screen reader content as a readable word" + }, + "questionCharacterDescriptor": { + "message": "Question mark", + "description": "Represents the ? key in screen reader content as a readable word" + }, + "forwardSlashCharacterDescriptor": { + "message": "Forward slash", + "description": "Represents the / key in screen reader content as a readable word" + }, + "lowercaseAriaLabel": { + "message": "Lowercase" + }, + "uppercaseAriaLabel": { + "message": "Uppercase" + }, + "generatedPassword": { + "message": "Generated password" } } diff --git a/apps/browser/src/_locales/zh_CN/messages.json b/apps/browser/src/_locales/zh_CN/messages.json index ad89f8efc79..20711539af5 100644 --- a/apps/browser/src/_locales/zh_CN/messages.json +++ b/apps/browser/src/_locales/zh_CN/messages.json @@ -19,6 +19,18 @@ "createAccount": { "message": "创建账户" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" + }, "setAStrongPassword": { "message": "设置强密码" }, @@ -107,6 +119,9 @@ "copyPassword": { "message": "复制密码" }, + "copyPassphrase": { + "message": "Copy passphrase" + }, "copyNote": { "message": "复制备注" }, @@ -152,6 +167,10 @@ "copyNotes": { "message": "复制备注" }, + "fill": { + "message": "Fill", + "description": "This string is used on the vault page to indicate autofilling. Horizontal space is limited in the interface here so try and keep translations as concise as possible." + }, "autoFill": { "message": "自动填充" }, @@ -407,6 +426,9 @@ "generatePassword": { "message": "生成密码" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "regeneratePassword": { "message": "重新生成密码" }, @@ -568,6 +590,15 @@ "launchWebsite": { "message": "启动网站" }, + "launchWebsiteName": { + "message": "Launch website $ITEMNAME$", + "placeholders": { + "itemname": { + "content": "$1", + "example": "Secret item" + } + } + }, "website": { "message": "网站" }, @@ -814,6 +845,9 @@ "logIn": { "message": "登录" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "restartRegistration": { "message": "重新开始注册" }, @@ -1389,6 +1423,10 @@ "baseUrl": { "message": "服务器 URL" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "API 服务器 URL" }, @@ -2464,8 +2502,8 @@ "message": "可选,用户需要提供密码才能访问此 Send。", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendPasswordDescV2": { - "message": "需要此密码才能查看此 Send。", + "sendPasswordDescV3": { + "message": "Add an optional password for recipients to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendNotesDesc": { @@ -2827,6 +2865,9 @@ "generateUsername": { "message": "生成用户名" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "用户名类型" }, @@ -2867,6 +2908,14 @@ "forwardedEmailDesc": { "message": "使用外部转发服务生成一个电子邮件别名。" }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ 错误:$ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -4494,9 +4543,15 @@ "itemLocation": { "message": "项目位置" }, + "fileSend": { + "message": "文件 Send" + }, "fileSends": { "message": "文件 Send" }, + "textSend": { + "message": "文本 Send" + }, "textSends": { "message": "文本 Send" }, @@ -4559,5 +4614,158 @@ }, "authenticating": { "message": "正在验证" + }, + "fillGeneratedPassword": { + "message": "Fill generated password", + "description": "Heading for the password generator within the inline menu" + }, + "passwordRegenerated": { + "message": "Password regenerated", + "description": "Notification message for when a password has been regenerated" + }, + "saveLoginToBitwarden": { + "message": "Save login to Bitwarden?", + "description": "Confirmation message for saving a login to Bitwarden" + }, + "spaceCharacterDescriptor": { + "message": "Space", + "description": "Represents the space key in screen reader content as a readable word" + }, + "tildeCharacterDescriptor": { + "message": "Tilde", + "description": "Represents the ~ key in screen reader content as a readable word" + }, + "backtickCharacterDescriptor": { + "message": "Backtick", + "description": "Represents the ` key in screen reader content as a readable word" + }, + "exclamationCharacterDescriptor": { + "message": "Exclamation mark", + "description": "Represents the ! key in screen reader content as a readable word" + }, + "atSignCharacterDescriptor": { + "message": "At sign", + "description": "Represents the @ key in screen reader content as a readable word" + }, + "hashSignCharacterDescriptor": { + "message": "Hash sign", + "description": "Represents the # key in screen reader content as a readable word" + }, + "dollarSignCharacterDescriptor": { + "message": "Dollar sign", + "description": "Represents the $ key in screen reader content as a readable word" + }, + "percentSignCharacterDescriptor": { + "message": "Percent sign", + "description": "Represents the % key in screen reader content as a readable word" + }, + "caretCharacterDescriptor": { + "message": "Caret", + "description": "Represents the ^ key in screen reader content as a readable word" + }, + "ampersandCharacterDescriptor": { + "message": "Ampersand", + "description": "Represents the & key in screen reader content as a readable word" + }, + "asteriskCharacterDescriptor": { + "message": "Asterisk", + "description": "Represents the * key in screen reader content as a readable word" + }, + "parenLeftCharacterDescriptor": { + "message": "Left parenthesis", + "description": "Represents the ( key in screen reader content as a readable word" + }, + "parenRightCharacterDescriptor": { + "message": "Right parenthesis", + "description": "Represents the ) key in screen reader content as a readable word" + }, + "hyphenCharacterDescriptor": { + "message": "Underscore", + "description": "Represents the _ key in screen reader content as a readable word" + }, + "underscoreCharacterDescriptor": { + "message": "Hyphen", + "description": "Represents the - key in screen reader content as a readable word" + }, + "plusCharacterDescriptor": { + "message": "Plus", + "description": "Represents the + key in screen reader content as a readable word" + }, + "equalsCharacterDescriptor": { + "message": "Equals", + "description": "Represents the = key in screen reader content as a readable word" + }, + "braceLeftCharacterDescriptor": { + "message": "Left brace", + "description": "Represents the { key in screen reader content as a readable word" + }, + "braceRightCharacterDescriptor": { + "message": "Right brace", + "description": "Represents the } key in screen reader content as a readable word" + }, + "bracketLeftCharacterDescriptor": { + "message": "Left bracket", + "description": "Represents the [ key in screen reader content as a readable word" + }, + "bracketRightCharacterDescriptor": { + "message": "Right bracket", + "description": "Represents the ] key in screen reader content as a readable word" + }, + "pipeCharacterDescriptor": { + "message": "Pipe", + "description": "Represents the | key in screen reader content as a readable word" + }, + "backSlashCharacterDescriptor": { + "message": "Back slash", + "description": "Represents the back slash key in screen reader content as a readable word" + }, + "colonCharacterDescriptor": { + "message": "Colon", + "description": "Represents the : key in screen reader content as a readable word" + }, + "semicolonCharacterDescriptor": { + "message": "Semicolon", + "description": "Represents the ; key in screen reader content as a readable word" + }, + "doubleQuoteCharacterDescriptor": { + "message": "Double quote", + "description": "Represents the double quote key in screen reader content as a readable word" + }, + "singleQuoteCharacterDescriptor": { + "message": "Single quote", + "description": "Represents the ' key in screen reader content as a readable word" + }, + "lessThanCharacterDescriptor": { + "message": "Less than", + "description": "Represents the < key in screen reader content as a readable word" + }, + "greaterThanCharacterDescriptor": { + "message": "Greater than", + "description": "Represents the > key in screen reader content as a readable word" + }, + "commaCharacterDescriptor": { + "message": "Comma", + "description": "Represents the , key in screen reader content as a readable word" + }, + "periodCharacterDescriptor": { + "message": "Period", + "description": "Represents the . key in screen reader content as a readable word" + }, + "questionCharacterDescriptor": { + "message": "Question mark", + "description": "Represents the ? key in screen reader content as a readable word" + }, + "forwardSlashCharacterDescriptor": { + "message": "Forward slash", + "description": "Represents the / key in screen reader content as a readable word" + }, + "lowercaseAriaLabel": { + "message": "Lowercase" + }, + "uppercaseAriaLabel": { + "message": "Uppercase" + }, + "generatedPassword": { + "message": "Generated password" } } diff --git a/apps/browser/src/_locales/zh_TW/messages.json b/apps/browser/src/_locales/zh_TW/messages.json index 496d7b8159a..a6a8d933b82 100644 --- a/apps/browser/src/_locales/zh_TW/messages.json +++ b/apps/browser/src/_locales/zh_TW/messages.json @@ -19,6 +19,18 @@ "createAccount": { "message": "建立帳戶" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" + }, "setAStrongPassword": { "message": "設定一個強密碼" }, @@ -107,6 +119,9 @@ "copyPassword": { "message": "複製密碼" }, + "copyPassphrase": { + "message": "Copy passphrase" + }, "copyNote": { "message": "複製備註" }, @@ -152,6 +167,10 @@ "copyNotes": { "message": "複製備註" }, + "fill": { + "message": "Fill", + "description": "This string is used on the vault page to indicate autofilling. Horizontal space is limited in the interface here so try and keep translations as concise as possible." + }, "autoFill": { "message": "自動填入" }, @@ -407,6 +426,9 @@ "generatePassword": { "message": "產生密碼" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "regeneratePassword": { "message": "重新產生密碼" }, @@ -568,6 +590,15 @@ "launchWebsite": { "message": "開啟網站" }, + "launchWebsiteName": { + "message": "Launch website $ITEMNAME$", + "placeholders": { + "itemname": { + "content": "$1", + "example": "Secret item" + } + } + }, "website": { "message": "網站" }, @@ -814,6 +845,9 @@ "logIn": { "message": "登入" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "restartRegistration": { "message": "Restart registration" }, @@ -1389,6 +1423,10 @@ "baseUrl": { "message": "伺服器 URL" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "API 伺服器網址" }, @@ -2464,8 +2502,8 @@ "message": "選用功能。使用者需提供密碼才能存取此 Send。", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendPasswordDescV2": { - "message": "Require this password to view the Send.", + "sendPasswordDescV3": { + "message": "Add an optional password for recipients to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendNotesDesc": { @@ -2827,6 +2865,9 @@ "generateUsername": { "message": "產生使用者名稱" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "使用者名稱類型" }, @@ -2867,6 +2908,14 @@ "forwardedEmailDesc": { "message": "使用外部轉寄服務產生一個電子郵件別名。" }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ error: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -4494,9 +4543,15 @@ "itemLocation": { "message": "Item Location" }, + "fileSend": { + "message": "File Send" + }, "fileSends": { "message": "File Sends" }, + "textSend": { + "message": "Text Send" + }, "textSends": { "message": "Text Sends" }, @@ -4559,5 +4614,158 @@ }, "authenticating": { "message": "Authenticating" + }, + "fillGeneratedPassword": { + "message": "Fill generated password", + "description": "Heading for the password generator within the inline menu" + }, + "passwordRegenerated": { + "message": "Password regenerated", + "description": "Notification message for when a password has been regenerated" + }, + "saveLoginToBitwarden": { + "message": "Save login to Bitwarden?", + "description": "Confirmation message for saving a login to Bitwarden" + }, + "spaceCharacterDescriptor": { + "message": "Space", + "description": "Represents the space key in screen reader content as a readable word" + }, + "tildeCharacterDescriptor": { + "message": "Tilde", + "description": "Represents the ~ key in screen reader content as a readable word" + }, + "backtickCharacterDescriptor": { + "message": "Backtick", + "description": "Represents the ` key in screen reader content as a readable word" + }, + "exclamationCharacterDescriptor": { + "message": "Exclamation mark", + "description": "Represents the ! key in screen reader content as a readable word" + }, + "atSignCharacterDescriptor": { + "message": "At sign", + "description": "Represents the @ key in screen reader content as a readable word" + }, + "hashSignCharacterDescriptor": { + "message": "Hash sign", + "description": "Represents the # key in screen reader content as a readable word" + }, + "dollarSignCharacterDescriptor": { + "message": "Dollar sign", + "description": "Represents the $ key in screen reader content as a readable word" + }, + "percentSignCharacterDescriptor": { + "message": "Percent sign", + "description": "Represents the % key in screen reader content as a readable word" + }, + "caretCharacterDescriptor": { + "message": "Caret", + "description": "Represents the ^ key in screen reader content as a readable word" + }, + "ampersandCharacterDescriptor": { + "message": "Ampersand", + "description": "Represents the & key in screen reader content as a readable word" + }, + "asteriskCharacterDescriptor": { + "message": "Asterisk", + "description": "Represents the * key in screen reader content as a readable word" + }, + "parenLeftCharacterDescriptor": { + "message": "Left parenthesis", + "description": "Represents the ( key in screen reader content as a readable word" + }, + "parenRightCharacterDescriptor": { + "message": "Right parenthesis", + "description": "Represents the ) key in screen reader content as a readable word" + }, + "hyphenCharacterDescriptor": { + "message": "Underscore", + "description": "Represents the _ key in screen reader content as a readable word" + }, + "underscoreCharacterDescriptor": { + "message": "Hyphen", + "description": "Represents the - key in screen reader content as a readable word" + }, + "plusCharacterDescriptor": { + "message": "Plus", + "description": "Represents the + key in screen reader content as a readable word" + }, + "equalsCharacterDescriptor": { + "message": "Equals", + "description": "Represents the = key in screen reader content as a readable word" + }, + "braceLeftCharacterDescriptor": { + "message": "Left brace", + "description": "Represents the { key in screen reader content as a readable word" + }, + "braceRightCharacterDescriptor": { + "message": "Right brace", + "description": "Represents the } key in screen reader content as a readable word" + }, + "bracketLeftCharacterDescriptor": { + "message": "Left bracket", + "description": "Represents the [ key in screen reader content as a readable word" + }, + "bracketRightCharacterDescriptor": { + "message": "Right bracket", + "description": "Represents the ] key in screen reader content as a readable word" + }, + "pipeCharacterDescriptor": { + "message": "Pipe", + "description": "Represents the | key in screen reader content as a readable word" + }, + "backSlashCharacterDescriptor": { + "message": "Back slash", + "description": "Represents the back slash key in screen reader content as a readable word" + }, + "colonCharacterDescriptor": { + "message": "Colon", + "description": "Represents the : key in screen reader content as a readable word" + }, + "semicolonCharacterDescriptor": { + "message": "Semicolon", + "description": "Represents the ; key in screen reader content as a readable word" + }, + "doubleQuoteCharacterDescriptor": { + "message": "Double quote", + "description": "Represents the double quote key in screen reader content as a readable word" + }, + "singleQuoteCharacterDescriptor": { + "message": "Single quote", + "description": "Represents the ' key in screen reader content as a readable word" + }, + "lessThanCharacterDescriptor": { + "message": "Less than", + "description": "Represents the < key in screen reader content as a readable word" + }, + "greaterThanCharacterDescriptor": { + "message": "Greater than", + "description": "Represents the > key in screen reader content as a readable word" + }, + "commaCharacterDescriptor": { + "message": "Comma", + "description": "Represents the , key in screen reader content as a readable word" + }, + "periodCharacterDescriptor": { + "message": "Period", + "description": "Represents the . key in screen reader content as a readable word" + }, + "questionCharacterDescriptor": { + "message": "Question mark", + "description": "Represents the ? key in screen reader content as a readable word" + }, + "forwardSlashCharacterDescriptor": { + "message": "Forward slash", + "description": "Represents the / key in screen reader content as a readable word" + }, + "lowercaseAriaLabel": { + "message": "Lowercase" + }, + "uppercaseAriaLabel": { + "message": "Uppercase" + }, + "generatedPassword": { + "message": "Generated password" } } diff --git a/apps/browser/src/auth/popup/lock.component.ts b/apps/browser/src/auth/popup/lock.component.ts index 75fcfc58f6a..c7fb108de80 100644 --- a/apps/browser/src/auth/popup/lock.component.ts +++ b/apps/browser/src/auth/popup/lock.component.ts @@ -16,7 +16,6 @@ import { KdfConfigService } from "@bitwarden/common/auth/abstractions/kdf-config import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/auth/abstractions/master-password.service.abstraction"; import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction"; import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status"; -import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service"; import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; @@ -26,7 +25,7 @@ import { StateService } from "@bitwarden/common/platform/abstractions/state.serv import { PasswordStrengthServiceAbstraction } from "@bitwarden/common/tools/password-strength"; import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction"; import { DialogService, ToastService } from "@bitwarden/components"; -import { BiometricsService, BiometricStateService } from "@bitwarden/key-management"; +import { KeyService, BiometricsService, BiometricStateService } from "@bitwarden/key-management"; import { BiometricErrors, BiometricErrorTypes } from "../../models/biometricErrors"; import { BrowserRouterService } from "../../platform/popup/services/browser-router.service"; @@ -49,7 +48,7 @@ export class LockComponent extends BaseLockComponent implements OnInit { i18nService: I18nService, platformUtilsService: PlatformUtilsService, messagingService: MessagingService, - cryptoService: CryptoService, + keyService: KeyService, vaultTimeoutService: VaultTimeoutService, vaultTimeoutSettingsService: VaultTimeoutSettingsService, environmentService: EnvironmentService, @@ -79,7 +78,7 @@ export class LockComponent extends BaseLockComponent implements OnInit { i18nService, platformUtilsService, messagingService, - cryptoService, + keyService, vaultTimeoutService, vaultTimeoutSettingsService, environmentService, diff --git a/apps/browser/src/auth/popup/login.component.html b/apps/browser/src/auth/popup/login-v1.component.html similarity index 100% rename from apps/browser/src/auth/popup/login.component.html rename to apps/browser/src/auth/popup/login-v1.component.html diff --git a/apps/browser/src/auth/popup/login.component.ts b/apps/browser/src/auth/popup/login-v1.component.ts similarity index 95% rename from apps/browser/src/auth/popup/login.component.ts rename to apps/browser/src/auth/popup/login-v1.component.ts index fd4d9bc547a..eee1bcc4d3f 100644 --- a/apps/browser/src/auth/popup/login.component.ts +++ b/apps/browser/src/auth/popup/login-v1.component.ts @@ -3,7 +3,7 @@ import { FormBuilder } from "@angular/forms"; import { ActivatedRoute, Router } from "@angular/router"; import { firstValueFrom } from "rxjs"; -import { LoginComponent as BaseLoginComponent } from "@bitwarden/angular/auth/components/login.component"; +import { LoginComponentV1 as BaseLoginComponent } from "@bitwarden/angular/auth/components/login-v1.component"; import { FormValidationErrorsService } from "@bitwarden/angular/platform/abstractions/form-validation-errors.service"; import { LoginStrategyServiceAbstraction, @@ -29,9 +29,9 @@ import { flagEnabled } from "../../platform/flags"; @Component({ selector: "app-login", - templateUrl: "login.component.html", + templateUrl: "login-v1.component.html", }) -export class LoginComponent extends BaseLoginComponent implements OnInit { +export class LoginComponentV1 extends BaseLoginComponent implements OnInit { showPasswordless = false; constructor( devicesApiService: DevicesApiServiceAbstraction, diff --git a/apps/browser/src/auth/popup/login-via-auth-request.component.ts b/apps/browser/src/auth/popup/login-via-auth-request.component.ts index 33ec2acf387..9dc0d7d5454 100644 --- a/apps/browser/src/auth/popup/login-via-auth-request.component.ts +++ b/apps/browser/src/auth/popup/login-via-auth-request.component.ts @@ -15,7 +15,6 @@ import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service"; import { DeviceTrustServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust.service.abstraction"; import { AppIdService } from "@bitwarden/common/platform/abstractions/app-id.service"; import { CryptoFunctionService } from "@bitwarden/common/platform/abstractions/crypto-function.service"; -import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service"; import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; @@ -24,6 +23,7 @@ import { ValidationService } from "@bitwarden/common/platform/abstractions/valid import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction"; import { ToastService } from "@bitwarden/components"; import { PasswordGenerationServiceAbstraction } from "@bitwarden/generator-legacy"; +import { KeyService } from "@bitwarden/key-management"; @Component({ selector: "app-login-via-auth-request", @@ -32,7 +32,7 @@ import { PasswordGenerationServiceAbstraction } from "@bitwarden/generator-legac export class LoginViaAuthRequestComponent extends BaseLoginWithDeviceComponent { constructor( router: Router, - cryptoService: CryptoService, + keyService: KeyService, cryptoFunctionService: CryptoFunctionService, appIdService: AppIdService, passwordGenerationService: PasswordGenerationServiceAbstraction, @@ -55,7 +55,7 @@ export class LoginViaAuthRequestComponent extends BaseLoginWithDeviceComponent { ) { super( router, - cryptoService, + keyService, cryptoFunctionService, appIdService, passwordGenerationService, diff --git a/apps/browser/src/auth/popup/login/extension-login-component.service.spec.ts b/apps/browser/src/auth/popup/login/extension-login-component.service.spec.ts new file mode 100644 index 00000000000..a7e29171015 --- /dev/null +++ b/apps/browser/src/auth/popup/login/extension-login-component.service.spec.ts @@ -0,0 +1,85 @@ +import { TestBed } from "@angular/core/testing"; +import { MockProxy, mock } from "jest-mock-extended"; + +import { DefaultLoginComponentService } from "@bitwarden/auth/angular"; +import { SsoLoginServiceAbstraction } from "@bitwarden/common/auth/abstractions/sso-login.service.abstraction"; +import { CryptoFunctionService } from "@bitwarden/common/platform/abstractions/crypto-function.service"; +import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service"; +import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; +import { PasswordGenerationServiceAbstraction } from "@bitwarden/generator-legacy"; + +import { flagEnabled } from "../../../platform/flags"; +import { BrowserPlatformUtilsService } from "../../../platform/services/platform-utils/browser-platform-utils.service"; +import { ExtensionAnonLayoutWrapperDataService } from "../extension-anon-layout-wrapper/extension-anon-layout-wrapper-data.service"; + +import { ExtensionLoginComponentService } from "./extension-login-component.service"; + +jest.mock("../../../platform/flags", () => ({ + flagEnabled: jest.fn(), +})); + +describe("ExtensionLoginComponentService", () => { + let service: ExtensionLoginComponentService; + let cryptoFunctionService: MockProxy; + let environmentService: MockProxy; + let passwordGenerationService: MockProxy; + let platformUtilsService: MockProxy; + let ssoLoginService: MockProxy; + let extensionAnonLayoutWrapperDataService: MockProxy; + beforeEach(() => { + cryptoFunctionService = mock(); + environmentService = mock(); + passwordGenerationService = mock(); + platformUtilsService = mock(); + ssoLoginService = mock(); + extensionAnonLayoutWrapperDataService = mock(); + TestBed.configureTestingModule({ + providers: [ + { + provide: ExtensionLoginComponentService, + useFactory: () => + new ExtensionLoginComponentService( + cryptoFunctionService, + environmentService, + passwordGenerationService, + platformUtilsService, + ssoLoginService, + extensionAnonLayoutWrapperDataService, + ), + }, + { provide: DefaultLoginComponentService, useExisting: ExtensionLoginComponentService }, + { provide: CryptoFunctionService, useValue: cryptoFunctionService }, + { provide: EnvironmentService, useValue: environmentService }, + { provide: PasswordGenerationServiceAbstraction, useValue: passwordGenerationService }, + { provide: PlatformUtilsService, useValue: platformUtilsService }, + { provide: SsoLoginServiceAbstraction, useValue: ssoLoginService }, + ], + }); + service = TestBed.inject(ExtensionLoginComponentService); + }); + + it("creates the service", () => { + expect(service).toBeTruthy(); + }); + + describe("isLoginViaAuthRequestSupported", () => { + it("returns true if showPasswordless flag is enabled", () => { + (flagEnabled as jest.Mock).mockReturnValue(true); + expect(service.isLoginViaAuthRequestSupported()).toBe(true); + }); + + it("returns false if showPasswordless flag is disabled", () => { + (flagEnabled as jest.Mock).mockReturnValue(false); + expect(service.isLoginViaAuthRequestSupported()).toBeFalsy(); + }); + }); + + describe("showBackButton", () => { + it("sets showBackButton in extensionAnonLayoutWrapperDataService", () => { + service.showBackButton(true); + expect(extensionAnonLayoutWrapperDataService.setAnonLayoutWrapperData).toHaveBeenCalledWith({ + showBackButton: true, + }); + }); + }); +}); diff --git a/apps/browser/src/auth/popup/login/extension-login-component.service.ts b/apps/browser/src/auth/popup/login/extension-login-component.service.ts new file mode 100644 index 00000000000..8630030e8e2 --- /dev/null +++ b/apps/browser/src/auth/popup/login/extension-login-component.service.ts @@ -0,0 +1,43 @@ +import { Injectable } from "@angular/core"; + +import { DefaultLoginComponentService, LoginComponentService } from "@bitwarden/auth/angular"; +import { SsoLoginServiceAbstraction } from "@bitwarden/common/auth/abstractions/sso-login.service.abstraction"; +import { CryptoFunctionService } from "@bitwarden/common/platform/abstractions/crypto-function.service"; +import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service"; +import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; +import { PasswordGenerationServiceAbstraction } from "@bitwarden/generator-legacy"; + +import { flagEnabled } from "../../../platform/flags"; +import { ExtensionAnonLayoutWrapperDataService } from "../extension-anon-layout-wrapper/extension-anon-layout-wrapper-data.service"; + +@Injectable() +export class ExtensionLoginComponentService + extends DefaultLoginComponentService + implements LoginComponentService +{ + constructor( + cryptoFunctionService: CryptoFunctionService, + environmentService: EnvironmentService, + passwordGenerationService: PasswordGenerationServiceAbstraction, + platformUtilsService: PlatformUtilsService, + ssoLoginService: SsoLoginServiceAbstraction, + private extensionAnonLayoutWrapperDataService: ExtensionAnonLayoutWrapperDataService, + ) { + super( + cryptoFunctionService, + environmentService, + passwordGenerationService, + platformUtilsService, + ssoLoginService, + ); + this.clientType = this.platformUtilsService.getClientType(); + } + + isLoginViaAuthRequestSupported(): boolean { + return flagEnabled("showPasswordless"); + } + + showBackButton(showBackButton: boolean): void { + this.extensionAnonLayoutWrapperDataService.setAnonLayoutWrapperData({ showBackButton }); + } +} diff --git a/apps/browser/src/auth/popup/register.component.ts b/apps/browser/src/auth/popup/register.component.ts index dab1e62f850..7c785d1912a 100644 --- a/apps/browser/src/auth/popup/register.component.ts +++ b/apps/browser/src/auth/popup/register.component.ts @@ -7,7 +7,6 @@ import { FormValidationErrorsService } from "@bitwarden/angular/platform/abstrac import { LoginStrategyServiceAbstraction } from "@bitwarden/auth/common"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { AuditService } from "@bitwarden/common/abstractions/audit.service"; -import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service"; import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; @@ -15,6 +14,7 @@ import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/pl import { StateService } from "@bitwarden/common/platform/abstractions/state.service"; import { DialogService, ToastService } from "@bitwarden/components"; import { PasswordGenerationServiceAbstraction } from "@bitwarden/generator-legacy"; +import { KeyService } from "@bitwarden/key-management"; @Component({ selector: "app-register", @@ -30,7 +30,7 @@ export class RegisterComponent extends BaseRegisterComponent { loginStrategyService: LoginStrategyServiceAbstraction, router: Router, i18nService: I18nService, - cryptoService: CryptoService, + keyService: KeyService, apiService: ApiService, stateService: StateService, platformUtilsService: PlatformUtilsService, @@ -47,7 +47,7 @@ export class RegisterComponent extends BaseRegisterComponent { loginStrategyService, router, i18nService, - cryptoService, + keyService, apiService, stateService, platformUtilsService, diff --git a/apps/browser/src/auth/popup/settings/account-security-v1.component.ts b/apps/browser/src/auth/popup/settings/account-security-v1.component.ts index d2a515b2599..db45b3adb73 100644 --- a/apps/browser/src/auth/popup/settings/account-security-v1.component.ts +++ b/apps/browser/src/auth/popup/settings/account-security-v1.component.ts @@ -25,7 +25,6 @@ import { PolicyType } from "@bitwarden/common/admin-console/enums"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction"; import { VaultTimeoutAction } from "@bitwarden/common/enums/vault-timeout-action.enum"; -import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service"; import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service"; @@ -37,7 +36,7 @@ import { VaultTimeoutStringType, } from "@bitwarden/common/types/vault-timeout.type"; import { DialogService } from "@bitwarden/components"; -import { BiometricStateService, BiometricsService } from "@bitwarden/key-management"; +import { KeyService, BiometricStateService, BiometricsService } from "@bitwarden/key-management"; import { BiometricErrors, BiometricErrorTypes } from "../../../models/biometricErrors"; import { BrowserApi } from "../../../platform/browser/browser-api"; @@ -87,7 +86,7 @@ export class AccountSecurityComponent implements OnInit, OnDestroy { private vaultTimeoutSettingsService: VaultTimeoutSettingsService, public messagingService: MessagingService, private environmentService: EnvironmentService, - private cryptoService: CryptoService, + private keyService: KeyService, private stateService: StateService, private userVerificationService: UserVerificationService, private dialogService: DialogService, @@ -386,7 +385,7 @@ export class AccountSecurityComponent implements OnInit, OnDestroy { const awaitDesktopDialogRef = AwaitDesktopDialogComponent.open(this.dialogService); const awaitDesktopDialogClosed = firstValueFrom(awaitDesktopDialogRef.closed); - await this.cryptoService.refreshAdditionalKeys(); + await this.keyService.refreshAdditionalKeys(); await Promise.race([ awaitDesktopDialogClosed.then(async (result) => { @@ -465,9 +464,7 @@ export class AccountSecurityComponent implements OnInit, OnDestroy { } async fingerprint() { - const fingerprint = await this.cryptoService.getFingerprint( - await this.stateService.getUserId(), - ); + const fingerprint = await this.keyService.getFingerprint(await this.stateService.getUserId()); const dialogRef = FingerprintDialogComponent.open(this.dialogService, { fingerprint, diff --git a/apps/browser/src/auth/popup/settings/account-security.component.ts b/apps/browser/src/auth/popup/settings/account-security.component.ts index 20286435edb..1617ed84767 100644 --- a/apps/browser/src/auth/popup/settings/account-security.component.ts +++ b/apps/browser/src/auth/popup/settings/account-security.component.ts @@ -27,7 +27,6 @@ import { PolicyType } from "@bitwarden/common/admin-console/enums"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction"; import { VaultTimeoutAction } from "@bitwarden/common/enums/vault-timeout-action.enum"; -import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service"; import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service"; @@ -52,7 +51,7 @@ import { TypographyModule, ToastService, } from "@bitwarden/components"; -import { BiometricsService, BiometricStateService } from "@bitwarden/key-management"; +import { KeyService, BiometricsService, BiometricStateService } from "@bitwarden/key-management"; import { BiometricErrors, BiometricErrorTypes } from "../../../models/biometricErrors"; import { BrowserApi } from "../../../platform/browser/browser-api"; @@ -127,7 +126,7 @@ export class AccountSecurityComponent implements OnInit, OnDestroy { private vaultTimeoutSettingsService: VaultTimeoutSettingsService, public messagingService: MessagingService, private environmentService: EnvironmentService, - private cryptoService: CryptoService, + private keyService: KeyService, private stateService: StateService, private userVerificationService: UserVerificationService, private dialogService: DialogService, @@ -436,7 +435,7 @@ export class AccountSecurityComponent implements OnInit, OnDestroy { return; } - await this.cryptoService.refreshAdditionalKeys(); + await this.keyService.refreshAdditionalKeys(); const successful = await this.trySetupBiometrics(); this.form.controls.biometric.setValue(successful); @@ -562,8 +561,8 @@ export class AccountSecurityComponent implements OnInit, OnDestroy { const activeUserId = await firstValueFrom( this.accountService.activeAccount$.pipe(map((a) => a?.id)), ); - const publicKey = await firstValueFrom(this.cryptoService.userPublicKey$(activeUserId)); - const fingerprint = await this.cryptoService.getFingerprint(activeUserId, publicKey); + const publicKey = await firstValueFrom(this.keyService.userPublicKey$(activeUserId)); + const fingerprint = await this.keyService.getFingerprint(activeUserId, publicKey); const dialogRef = FingerprintDialogComponent.open(this.dialogService, { fingerprint, diff --git a/apps/browser/src/autofill/background/abstractions/overlay.background.ts b/apps/browser/src/autofill/background/abstractions/overlay.background.ts index abe7d097016..68d3f32b80f 100644 --- a/apps/browser/src/autofill/background/abstractions/overlay.background.ts +++ b/apps/browser/src/autofill/background/abstractions/overlay.background.ts @@ -2,6 +2,7 @@ import { CipherType } from "@bitwarden/common/vault/enums"; import { CipherRepromptType } from "@bitwarden/common/vault/enums/cipher-reprompt-type"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; +import { InlineMenuFillTypes } from "../../enums/autofill-overlay.enum"; import AutofillPageDetails from "../../models/autofill-page-details"; import { PageDetail } from "../../services/abstractions/autofill.service"; @@ -32,14 +33,18 @@ export type WebsiteIconData = { icon: string; }; +export type UpdateOverlayCiphersParams = { + updateAllCipherTypes: boolean; + refocusField: boolean; +}; + export type FocusedFieldData = { focusedFieldStyles: Partial; focusedFieldRects: Partial; - filledByCipherType?: CipherType; + inlineMenuFillType?: InlineMenuFillTypes; tabId?: number; frameId?: number; accountCreationFieldType?: string; - showInlineMenuAccountCreation?: boolean; showPasskeys?: boolean; }; @@ -111,6 +116,12 @@ export type ToggleInlineMenuHiddenMessage = { setTransparentInlineMenu?: boolean; }; +export type UpdateInlineMenuVisibilityMessage = { + overlayElement?: string; + isVisible?: boolean; + forceUpdate?: boolean; +}; + export type OverlayBackgroundExtensionMessage = { command: string; portKey?: string; @@ -119,14 +130,15 @@ export type OverlayBackgroundExtensionMessage = { details?: AutofillPageDetails; isFieldCurrentlyFocused?: boolean; isFieldCurrentlyFilling?: boolean; - isVisible?: boolean; subFrameData?: SubFrameOffsetData; focusedFieldData?: FocusedFieldData; + isOpeningFullInlineMenu?: boolean; styles?: Partial; data?: LockedVaultPendingNotificationsData; } & OverlayAddNewItemMessage & CloseInlineMenuMessage & - ToggleInlineMenuHiddenMessage; + ToggleInlineMenuHiddenMessage & + UpdateInlineMenuVisibilityMessage; export type OverlayPortMessage = { [key: string]: any; @@ -188,16 +200,12 @@ export type OverlayBackgroundExtensionMessageHandlers = { updateIsFieldCurrentlyFilling: ({ message }: BackgroundMessageParam) => void; checkIsFieldCurrentlyFilling: () => boolean; getAutofillInlineMenuVisibility: () => void; + openAutofillInlineMenu: ({ message, sender }: BackgroundOnMessageHandlerParams) => Promise; getInlineMenuCardsVisibility: () => void; getInlineMenuIdentitiesVisibility: () => void; - openAutofillInlineMenu: () => void; closeAutofillInlineMenu: ({ message, sender }: BackgroundOnMessageHandlerParams) => void; checkAutofillInlineMenuFocused: ({ sender }: BackgroundSenderParam) => void; focusAutofillInlineMenuList: () => void; - updateAutofillInlineMenuPosition: ({ - message, - sender, - }: BackgroundOnMessageHandlerParams) => Promise; getAutofillInlineMenuPosition: () => InlineMenuPosition; updateAutofillInlineMenuElementIsVisibleStatus: ({ message, @@ -219,6 +227,7 @@ export type OverlayBackgroundExtensionMessageHandlers = { addEditCipherSubmitted: () => void; editedCipher: () => void; deletedCipher: () => void; + bgSaveCipher: () => void; fido2AbortRequest: ({ message, sender }: BackgroundOnMessageHandlerParams) => void; }; @@ -241,14 +250,16 @@ export type InlineMenuButtonPortMessageHandlers = { export type InlineMenuListPortMessageHandlers = { [key: string]: CallableFunction; - checkAutofillInlineMenuButtonFocused: () => void; - autofillInlineMenuBlurred: () => void; + checkAutofillInlineMenuButtonFocused: ({ port }: PortConnectionParam) => void; + autofillInlineMenuBlurred: ({ port }: PortConnectionParam) => void; unlockVault: ({ port }: PortConnectionParam) => void; fillAutofillInlineMenuCipher: ({ message, port }: PortOnMessageHandlerParams) => void; addNewVaultItem: ({ message, port }: PortOnMessageHandlerParams) => void; viewSelectedCipher: ({ message, port }: PortOnMessageHandlerParams) => void; redirectAutofillInlineMenuFocusOut: ({ message, port }: PortOnMessageHandlerParams) => void; updateAutofillInlineMenuListHeight: ({ message, port }: PortOnMessageHandlerParams) => void; + refreshGeneratedPassword: () => Promise; + fillGeneratedPassword: ({ port }: PortConnectionParam) => Promise; }; export interface OverlayBackground { diff --git a/apps/browser/src/autofill/background/notification.background.spec.ts b/apps/browser/src/autofill/background/notification.background.spec.ts index 0ede9b96091..e043dbfdd2e 100644 --- a/apps/browser/src/autofill/background/notification.background.spec.ts +++ b/apps/browser/src/autofill/background/notification.background.spec.ts @@ -1114,8 +1114,9 @@ describe("NotificationBackground", () => { it("skips saving the domain as a never value if the tab url does not match the queue message domain", async () => { const tab = createChromeTabMock({ id: 2, url: "https://example.com" }); - const sender = mock({ tab }); const message: NotificationBackgroundExtensionMessage = { command: "bgNeverSave" }; + const secondaryTab = createChromeTabMock({ id: 3, url: "https://another.com" }); + const sender = mock({ tab: secondaryTab }); notificationBackground["notificationQueue"] = [ mock({ type: NotificationQueueMessageType.AddLogin, diff --git a/apps/browser/src/autofill/background/notification.background.ts b/apps/browser/src/autofill/background/notification.background.ts index 683e3d8f581..e77996fe903 100644 --- a/apps/browser/src/autofill/background/notification.background.ts +++ b/apps/browser/src/autofill/background/notification.background.ts @@ -173,13 +173,8 @@ export default class NotificationBackground { } private async doNotificationQueueCheck(tab: chrome.tabs.Tab): Promise { - const tabDomain = Utils.getDomain(tab?.url); - if (!tabDomain) { - return; - } - const queueMessage = this.notificationQueue.find( - (message) => message.tab.id === tab.id && message.domain === tabDomain, + (message) => message.tab.id === tab.id && this.queueMessageIsFromTabOrigin(message, tab), ); if (queueMessage) { await this.sendNotificationQueueMessage(tab, queueMessage); @@ -537,8 +532,7 @@ export default class NotificationBackground { continue; } - const tabDomain = Utils.getDomain(tab.url); - if (tabDomain != null && tabDomain !== queueMessage.domain) { + if (!this.queueMessageIsFromTabOrigin(queueMessage, tab)) { continue; } @@ -685,8 +679,7 @@ export default class NotificationBackground { continue; } - const tabDomain = Utils.getDomain(tab.url); - if (tabDomain != null && tabDomain !== queueMessage.domain) { + if (!this.queueMessageIsFromTabOrigin(queueMessage, tab)) { continue; } @@ -829,4 +822,18 @@ export default class NotificationBackground { .catch((error) => this.logService.error(error)); return true; }; + + /** + * Validates whether the queue message is associated with the passed tab. + * + * @param queueMessage - The queue message to check + * @param tab - The tab to check the queue message against + */ + private queueMessageIsFromTabOrigin( + queueMessage: NotificationQueueMessageItem, + tab: chrome.tabs.Tab, + ) { + const tabDomain = Utils.getDomain(tab.url); + return tabDomain === queueMessage.domain || tabDomain === Utils.getDomain(queueMessage.tab.url); + } } diff --git a/apps/browser/src/autofill/background/overlay-notifications.background.spec.ts b/apps/browser/src/autofill/background/overlay-notifications.background.spec.ts index 8bac8ea6913..57930496978 100644 --- a/apps/browser/src/autofill/background/overlay-notifications.background.spec.ts +++ b/apps/browser/src/autofill/background/overlay-notifications.background.spec.ts @@ -60,6 +60,27 @@ describe("OverlayNotificationsBackground", () => { jest.clearAllTimers(); }); + describe("feature flag behavior", () => { + let runtimeRemoveListenerSpy: jest.SpyInstance; + + beforeEach(() => { + runtimeRemoveListenerSpy = jest.spyOn(chrome.runtime.onMessage, "removeListener"); + }); + + it("removes the extension listeners if the current flag value is set to `false`", () => { + getFeatureFlagMock$.next(false); + + expect(runtimeRemoveListenerSpy).toHaveBeenCalled(); + }); + + it("ignores the feature flag change if the previous flag value is equal to the current flag value", () => { + getFeatureFlagMock$.next(false); + getFeatureFlagMock$.next(false); + + expect(runtimeRemoveListenerSpy).toHaveBeenCalledTimes(1); + }); + }); + describe("setting up the form submission listeners", () => { let fields: MockProxy[]; let details: MockProxy; @@ -180,6 +201,40 @@ describe("OverlayNotificationsBackground", () => { await flushPromises(); }); + it("ignores the store request if the sender is not within the website origins set", () => { + sendMockExtensionMessage( + { + command: "formFieldSubmitted", + uri: "example.com", + username: "username", + password: "password", + newPassword: "newPassword", + }, + mock({ tab: { id: 2 } }), + ); + + expect( + overlayNotificationsBackground["modifyLoginCipherFormData"].get(sender.tab.id), + ).toBeUndefined(); + }); + + it("ignores the store request if the form submission does not include a username, password, or newPassword", () => { + sendMockExtensionMessage( + { + command: "formFieldSubmitted", + uri: "example.com", + username: "", + password: "", + newPassword: "", + }, + sender, + ); + + expect( + overlayNotificationsBackground["modifyLoginCipherFormData"].get(sender.tab.id), + ).toBeUndefined(); + }); + it("stores the modified login cipher form data", async () => { sendMockExtensionMessage( { @@ -203,6 +258,41 @@ describe("OverlayNotificationsBackground", () => { }); }); + it("overrides previously stored modified login cipher form data with a subsequent store request", async () => { + sendMockExtensionMessage( + { + command: "formFieldSubmitted", + uri: "example.com", + username: "oldUsername", + password: "oldPassword", + newPassword: "oldNewPassword", + }, + sender, + ); + await flushPromises(); + + sendMockExtensionMessage( + { + command: "formFieldSubmitted", + uri: "example.com", + username: "username", + password: "", + newPassword: "", + }, + sender, + ); + await flushPromises(); + + expect( + overlayNotificationsBackground["modifyLoginCipherFormData"].get(sender.tab.id), + ).toEqual({ + uri: "example.com", + username: "username", + password: "oldPassword", + newPassword: "oldNewPassword", + }); + }); + it("clears the modified login cipher form data after 5 seconds", () => { sendMockExtensionMessage( { @@ -323,10 +413,9 @@ describe("OverlayNotificationsBackground", () => { it("ignores requests that are not part of an active form submission", async () => { triggerWebRequestOnCompletedEvent( - mock({ + mock({ url: sender.url, tabId: sender.tab.id, - method: "POST", requestId: "123345", }), ); @@ -348,6 +437,25 @@ describe("OverlayNotificationsBackground", () => { await flushPromises(); triggerWebRequestOnCompletedEvent( + mock({ + url: sender.url, + tabId: sender.tab.id, + requestId, + }), + ); + + expect(notificationChangedPasswordSpy).not.toHaveBeenCalled(); + expect(notificationAddLoginSpy).not.toHaveBeenCalled(); + }); + + it("clears the notification fallback timeout if the request is completed with an invalid status code", async () => { + const clearFallbackSpy = jest.spyOn( + overlayNotificationsBackground as any, + "clearNotificationFallbackTimeout", + ); + + const requestId = "123345"; + triggerWebRequestOnBeforeRequestEvent( mock({ url: sender.url, tabId: sender.tab.id, @@ -355,9 +463,19 @@ describe("OverlayNotificationsBackground", () => { requestId, }), ); + await flushPromises(); - expect(notificationChangedPasswordSpy).not.toHaveBeenCalled(); - expect(notificationAddLoginSpy).not.toHaveBeenCalled(); + triggerWebRequestOnCompletedEvent( + mock({ + url: sender.url, + tabId: sender.tab.id, + statusCode: 404, + requestId, + }), + ); + await flushPromises(); + + expect(clearFallbackSpy).toHaveBeenCalled(); }); }); @@ -402,10 +520,9 @@ describe("OverlayNotificationsBackground", () => { ); }); triggerWebRequestOnCompletedEvent( - mock({ + mock({ url: sender.url, tabId: sender.tab.id, - method: "POST", requestId, }), ); @@ -452,10 +569,9 @@ describe("OverlayNotificationsBackground", () => { }); triggerWebRequestOnCompletedEvent( - mock({ + mock({ url: sender.url, tabId: sender.tab.id, - method: "POST", requestId, }), ); @@ -560,14 +676,59 @@ describe("OverlayNotificationsBackground", () => { expect(overlayNotificationsBackground["websiteOriginsWithFields"].size).toBe(0); }); - it("clears all associated data with a tab that is entering a `loading` state", () => { - triggerTabOnUpdatedEvent( - sender.tab.id, - mock({ status: "loading" }), - mock({ status: "loading" }), - ); + describe("tab onUpdated", () => { + it("skips clearing the website origins if the changeInfo does not contain a `loading` status", () => { + triggerTabOnUpdatedEvent( + sender.tab.id, + mock({ status: "complete" }), + mock({ status: "complete" }), + ); - expect(overlayNotificationsBackground["websiteOriginsWithFields"].size).toBe(0); + expect(overlayNotificationsBackground["websiteOriginsWithFields"].size).toBe(1); + }); + + it("skips clearing the website origins if the changeInfo does not contain a url", () => { + triggerTabOnUpdatedEvent( + sender.tab.id, + mock({ status: "loading", url: "" }), + mock({ status: "loading" }), + ); + + expect(overlayNotificationsBackground["websiteOriginsWithFields"].size).toBe(1); + }); + + it("skips clearing the website origins if the tab does not contain known website origins", () => { + triggerTabOnUpdatedEvent( + 199, + mock({ status: "loading", url: "https://example.com" }), + mock({ status: "loading", id: 199 }), + ); + + expect(overlayNotificationsBackground["websiteOriginsWithFields"].size).toBe(1); + }); + + it("skips clearing the website origins if the changeInfo's url is present as part of the know website origin match patterns", () => { + triggerTabOnUpdatedEvent( + sender.tab.id, + mock({ + status: "loading", + url: "https://subdomain.example.com", + }), + mock({ status: "loading" }), + ); + + expect(overlayNotificationsBackground["websiteOriginsWithFields"].size).toBe(1); + }); + + it("clears all associated data with a tab that is entering a `loading` state", () => { + triggerTabOnUpdatedEvent( + sender.tab.id, + mock({ status: "loading" }), + mock({ status: "loading" }), + ); + + expect(overlayNotificationsBackground["websiteOriginsWithFields"].size).toBe(0); + }); }); }); }); diff --git a/apps/browser/src/autofill/background/overlay-notifications.background.ts b/apps/browser/src/autofill/background/overlay-notifications.background.ts index 5ea3e8b8d6b..3472bfcc72f 100644 --- a/apps/browser/src/autofill/background/overlay-notifications.background.ts +++ b/apps/browser/src/autofill/background/overlay-notifications.background.ts @@ -333,7 +333,7 @@ export class OverlayNotificationsBackground implements OverlayNotificationsBackg const response = (await BrowserApi.tabSendMessage( tab, - { command: "getFormFieldDataForNotification" }, + { command: "getInlineMenuFormFieldData" }, { frameId }, )) as OverlayNotificationsExtensionMessage; if (response) { @@ -471,7 +471,7 @@ export class OverlayNotificationsBackground implements OverlayNotificationsBackg private shouldTriggerChangePasswordNotification = ( modifyLoginData: ModifyLoginCipherFormData, ) => { - return modifyLoginData.newPassword && !modifyLoginData.username; + return modifyLoginData?.newPassword && !modifyLoginData.username; }; /** @@ -480,7 +480,7 @@ export class OverlayNotificationsBackground implements OverlayNotificationsBackg * @param modifyLoginData - The modified login form data */ private shouldTriggerAddLoginNotification = (modifyLoginData: ModifyLoginCipherFormData) => { - return modifyLoginData.username && (modifyLoginData.password || modifyLoginData.newPassword); + return modifyLoginData?.username && (modifyLoginData.password || modifyLoginData.newPassword); }; /** @@ -576,8 +576,20 @@ export class OverlayNotificationsBackground implements OverlayNotificationsBackg * @param changeInfo - The change info of the tab */ private handleTabUpdated = (tabId: number, changeInfo: chrome.tabs.TabChangeInfo) => { - if (changeInfo.status === "loading" && this.websiteOriginsWithFields.has(tabId)) { - this.websiteOriginsWithFields.delete(tabId); + if (changeInfo.status !== "loading" || !changeInfo.url) { + return; } + + const originPatterns = this.websiteOriginsWithFields.get(tabId); + if (!originPatterns) { + return; + } + + const matchPatters = generateDomainMatchPatterns(changeInfo.url); + if (matchPatters.some((pattern) => originPatterns.has(pattern))) { + return; + } + + this.websiteOriginsWithFields.delete(tabId); }; } diff --git a/apps/browser/src/autofill/background/overlay.background.spec.ts b/apps/browser/src/autofill/background/overlay.background.spec.ts index b6a04f63d54..ebd73fa4cc0 100644 --- a/apps/browser/src/autofill/background/overlay.background.spec.ts +++ b/apps/browser/src/autofill/background/overlay.background.spec.ts @@ -42,11 +42,16 @@ import { BrowserPlatformUtilsService } from "../../platform/services/platform-ut import { AutofillOverlayElement, AutofillOverlayPort, + InlineMenuAccountCreationFieldType, + InlineMenuFillType, MAX_SUB_FRAME_DEPTH, RedirectFocusDirection, } from "../enums/autofill-overlay.enum"; +import { InlineMenuFormFieldData } from "../services/abstractions/autofill-overlay-content.service"; import { AutofillService } from "../services/abstractions/autofill.service"; +import { InlineMenuFieldQualificationService } from "../services/inline-menu-field-qualification.service"; import { + createAutofillFieldMock, createAutofillPageDetailsMock, createChromeTabMock, createFocusedFieldDataMock, @@ -66,6 +71,7 @@ import { import { FocusedFieldData, + InlineMenuPosition, PageDetailsForTab, SubFrameOffsetData, SubFrameOffsetsForTab, @@ -73,6 +79,9 @@ import { import { OverlayBackground } from "./overlay.background"; describe("OverlayBackground", () => { + const generatedPassword = "generated-password"; + const generatedPasswordCallbackMock = jest.fn().mockResolvedValue(generatedPassword); + const addPasswordCallbackMock = jest.fn(); const mockUserId = Utils.newGuid() as UserId; const sendResponse = jest.fn(); let accountService: FakeAccountService; @@ -95,6 +104,7 @@ describe("OverlayBackground", () => { let vaultSettingsServiceMock: MockProxy; let fido2ActiveRequestManager: Fido2ActiveRequestManager; let selectedThemeMock$: BehaviorSubject; + let inlineMenuFieldQualificationService: InlineMenuFieldQualificationService; let themeStateService: MockProxy; let overlayBackground: OverlayBackground; let portKeyForTabSpy: Record; @@ -117,6 +127,7 @@ describe("OverlayBackground", () => { const { initList, initButton } = options; if (initButton) { triggerPortOnConnectEvent(createPortSpyMock(AutofillOverlayPort.Button)); + await flushPromises(); buttonPortSpy = overlayBackground["inlineMenuButtonPort"]; buttonMessageConnectorSpy = createPortSpyMock(AutofillOverlayPort.ButtonMessageConnector); @@ -125,6 +136,7 @@ describe("OverlayBackground", () => { if (initList) { triggerPortOnConnectEvent(createPortSpyMock(AutofillOverlayPort.List)); + await flushPromises(); listPortSpy = overlayBackground["inlineMenuListPort"]; listMessageConnectorSpy = createPortSpyMock(AutofillOverlayPort.ListMessageConnector); @@ -143,7 +155,9 @@ describe("OverlayBackground", () => { domainSettingsService.showFavicons$ = showFaviconsMock$; domainSettingsService.neverDomains$ = neverDomainsMock$; logService = mock(); - cipherService = mock(); + cipherService = mock({ + getAllDecryptedForUrl: jest.fn().mockResolvedValue([]), + }); autofillService = mock(); activeAccountStatusMock$ = new BehaviorSubject(AuthenticationStatus.Unlocked); authService = mock(); @@ -167,6 +181,7 @@ describe("OverlayBackground", () => { vaultSettingsServiceMock.enablePasskeys$ = enablePasskeysMock$; fido2ActiveRequestManager = new Fido2ActiveRequestManager(); selectedThemeMock$ = new BehaviorSubject(ThemeType.Light); + inlineMenuFieldQualificationService = new InlineMenuFieldQualificationService(); themeStateService = mock(); themeStateService.selectedTheme$ = selectedThemeMock$; overlayBackground = new OverlayBackground( @@ -181,7 +196,10 @@ describe("OverlayBackground", () => { platformUtilsService, vaultSettingsServiceMock, fido2ActiveRequestManager, + inlineMenuFieldQualificationService, themeStateService, + generatedPasswordCallbackMock, + addPasswordCallbackMock, ); portKeyForTabSpy = overlayBackground["portKeyForTab"]; pageDetailsForTabSpy = overlayBackground["pageDetailsForTab"]; @@ -552,7 +570,10 @@ describe("OverlayBackground", () => { command: "updateIsFieldCurrentlyFocused", isFieldCurrentlyFocused: false, }, - mock({ frameId: 20 }), + mock({ + tab: createChromeTabMock({ id: 1 }), + frameId: 20, + }), ); sendMockExtensionMessage({ command: "triggerAutofillOverlayReposition" }, sender); @@ -609,7 +630,7 @@ describe("OverlayBackground", () => { it("skips updating the inline menu list if the user has the inline menu set to open on button click", async () => { inlineMenuVisibilityMock$.next(AutofillOverlayVisibility.OnButtonClick); tabsSendMessageSpy.mockImplementation((_tab, message, _options) => { - if (message.command === "checkMostRecentlyFocusedFieldHasValue") { + if (message.command === "checkFocusedFieldHasValue") { return Promise.resolve(true); } @@ -640,7 +661,7 @@ describe("OverlayBackground", () => { it("skips updating the inline menu list if the focused field has a value and the user status is not unlocked", async () => { activeAccountStatusMock$.next(AuthenticationStatus.Locked); tabsSendMessageSpy.mockImplementation((_tab, message, _options) => { - if (message.command === "checkMostRecentlyFocusedFieldHasValue") { + if (message.command === "checkFocusedFieldHasValue") { return Promise.resolve(true); } @@ -792,21 +813,6 @@ describe("OverlayBackground", () => { expect(cipherService.getAllDecryptedForUrl).not.toHaveBeenCalled(); }); - it("closes the inline menu on the focused field's tab if the user's auth status is not unlocked", async () => { - activeAccountStatusMock$.next(AuthenticationStatus.Locked); - const previousTab = mock({ id: 1 }); - overlayBackground["focusedFieldData"] = createFocusedFieldDataMock({ tabId: 1 }); - getTabSpy.mockResolvedValueOnce(previousTab); - - await overlayBackground.updateOverlayCiphers(); - - expect(tabsSendMessageSpy).toHaveBeenCalledWith( - previousTab, - { command: "closeAutofillInlineMenu", overlayElement: undefined }, - { frameId: 0 }, - ); - }); - it("closes the inline menu on the focused field's tab if current tab is different", async () => { getTabFromCurrentWindowIdSpy.mockResolvedValueOnce(tab); cipherService.getAllDecryptedForUrl.mockResolvedValue([loginCipher1, cardCipher]); @@ -816,6 +822,7 @@ describe("OverlayBackground", () => { getTabSpy.mockResolvedValueOnce(previousTab); await overlayBackground.updateOverlayCiphers(); + await flushPromises(); expect(tabsSendMessageSpy).toHaveBeenCalledWith( previousTab, @@ -830,6 +837,7 @@ describe("OverlayBackground", () => { cipherService.sortCiphersByLastUsedThenName.mockReturnValue(-1); await overlayBackground.updateOverlayCiphers(); + await flushPromises(); expect(BrowserApi.getTabFromCurrentWindowId).toHaveBeenCalled(); expect(cipherService.getAllDecryptedForUrl).toHaveBeenCalledWith(url, [ @@ -852,6 +860,7 @@ describe("OverlayBackground", () => { cipherService.sortCiphersByLastUsedThenName.mockReturnValue(-1); await overlayBackground.updateOverlayCiphers(false); + await flushPromises(); expect(BrowserApi.getTabFromCurrentWindowId).toHaveBeenCalled(); expect(cipherService.getAllDecryptedForUrl).toHaveBeenCalledWith(url); @@ -870,6 +879,7 @@ describe("OverlayBackground", () => { cipherService.sortCiphersByLastUsedThenName.mockReturnValue(-1); await overlayBackground.updateOverlayCiphers(false); + await flushPromises(); expect(BrowserApi.getTabFromCurrentWindowId).toHaveBeenCalled(); expect(cipherService.getAllDecryptedForUrl).toHaveBeenCalledWith(url, [ @@ -885,18 +895,20 @@ describe("OverlayBackground", () => { ); }); - it("posts an `updateOverlayListCiphers` message to the overlay list port, and send a `updateAutofillInlineMenuListCiphers` message to the tab indicating that the list of ciphers is populated", async () => { + it("posts an `updateAutofillInlineMenuListCiphers` message to the overlay list port, and send a `updateAutofillInlineMenuListCiphers` message to the tab indicating that the list of ciphers is populated", async () => { overlayBackground["focusedFieldData"] = createFocusedFieldDataMock({ tabId: tab.id }); cipherService.getAllDecryptedForUrl.mockResolvedValue([loginCipher1]); cipherService.sortCiphersByLastUsedThenName.mockReturnValue(-1); getTabFromCurrentWindowIdSpy.mockResolvedValueOnce(tab); await overlayBackground.updateOverlayCiphers(); + await flushPromises(); expect(listPortSpy.postMessage).toHaveBeenCalledWith({ command: "updateAutofillInlineMenuListCiphers", showInlineMenuAccountCreation: false, showPasskeysLabels: false, + focusedFieldHasValue: false, ciphers: [ { accountCreationFieldType: undefined, @@ -923,18 +935,20 @@ describe("OverlayBackground", () => { it("updates the inline menu list with card ciphers", async () => { overlayBackground["focusedFieldData"] = createFocusedFieldDataMock({ tabId: tab.id, - filledByCipherType: CipherType.Card, + inlineMenuFillType: CipherType.Card, }); cipherService.getAllDecryptedForUrl.mockResolvedValue([loginCipher1, cardCipher]); cipherService.sortCiphersByLastUsedThenName.mockReturnValue(-1); getTabFromCurrentWindowIdSpy.mockResolvedValueOnce(tab); await overlayBackground.updateOverlayCiphers(); + await flushPromises(); expect(listPortSpy.postMessage).toHaveBeenCalledWith({ command: "updateAutofillInlineMenuListCiphers", showInlineMenuAccountCreation: false, showPasskeysLabels: false, + focusedFieldHasValue: false, ciphers: [ { accountCreationFieldType: undefined, @@ -960,18 +974,19 @@ describe("OverlayBackground", () => { overlayBackground["focusedFieldData"] = createFocusedFieldDataMock({ tabId: tab.id, accountCreationFieldType: "text", - showInlineMenuAccountCreation: true, }); cipherService.getAllDecryptedForUrl.mockResolvedValue([identityCipher, cardCipher]); cipherService.sortCiphersByLastUsedThenName.mockReturnValue(-1); getTabFromCurrentWindowIdSpy.mockResolvedValueOnce(tab); await overlayBackground.updateOverlayCiphers(); + await flushPromises(); expect(listPortSpy.postMessage).toHaveBeenCalledWith({ command: "updateAutofillInlineMenuListCiphers", showInlineMenuAccountCreation: true, showPasskeysLabels: false, + focusedFieldHasValue: false, ciphers: [ { accountCreationFieldType: "text", @@ -999,18 +1014,20 @@ describe("OverlayBackground", () => { overlayBackground["focusedFieldData"] = createFocusedFieldDataMock({ tabId: tab.id, accountCreationFieldType: "text", - showInlineMenuAccountCreation: true, + inlineMenuFillType: InlineMenuFillType.AccountCreationUsername, }); cipherService.getAllDecryptedForUrl.mockResolvedValue([loginCipher1, identityCipher]); cipherService.sortCiphersByLastUsedThenName.mockReturnValue(-1); getTabFromCurrentWindowIdSpy.mockResolvedValueOnce(tab); await overlayBackground.updateOverlayCiphers(); + await flushPromises(); expect(listPortSpy.postMessage).toHaveBeenCalledWith({ command: "updateAutofillInlineMenuListCiphers", showInlineMenuAccountCreation: true, showPasskeysLabels: false, + focusedFieldHasValue: false, ciphers: [ { accountCreationFieldType: "text", @@ -1056,7 +1073,6 @@ describe("OverlayBackground", () => { overlayBackground["focusedFieldData"] = createFocusedFieldDataMock({ tabId: tab.id, accountCreationFieldType: "email", - showInlineMenuAccountCreation: true, }); const identityCipherWithoutUsername = mock({ id: "id-5", @@ -1076,11 +1092,13 @@ describe("OverlayBackground", () => { getTabFromCurrentWindowIdSpy.mockResolvedValueOnce(tab); await overlayBackground.updateOverlayCiphers(); + await flushPromises(); expect(listPortSpy.postMessage).toHaveBeenCalledWith({ command: "updateAutofillInlineMenuListCiphers", showInlineMenuAccountCreation: true, showPasskeysLabels: false, + focusedFieldHasValue: false, ciphers: [ { accountCreationFieldType: "email", @@ -1108,18 +1126,19 @@ describe("OverlayBackground", () => { overlayBackground["focusedFieldData"] = createFocusedFieldDataMock({ tabId: tab.id, accountCreationFieldType: "password", - showInlineMenuAccountCreation: true, }); cipherService.getAllDecryptedForUrl.mockResolvedValue([identityCipher]); cipherService.sortCiphersByLastUsedThenName.mockReturnValue(-1); getTabFromCurrentWindowIdSpy.mockResolvedValueOnce(tab); await overlayBackground.updateOverlayCiphers(); + await flushPromises(); expect(listPortSpy.postMessage).toHaveBeenCalledWith({ command: "updateAutofillInlineMenuListCiphers", showInlineMenuAccountCreation: true, showPasskeysLabels: false, + focusedFieldHasValue: false, ciphers: [], }); }); @@ -1133,7 +1152,7 @@ describe("OverlayBackground", () => { ); overlayBackground["focusedFieldData"] = createFocusedFieldDataMock({ tabId: tab.id, - filledByCipherType: CipherType.Login, + inlineMenuFillType: CipherType.Login, showPasskeys: true, }); cipherService.getAllDecryptedForUrl.mockResolvedValue([loginCipher1, passkeyCipher]); @@ -1141,6 +1160,7 @@ describe("OverlayBackground", () => { getTabFromCurrentWindowIdSpy.mockResolvedValueOnce(tab); await overlayBackground.updateOverlayCiphers(); + await flushPromises(); expect(listPortSpy.postMessage).toHaveBeenCalledWith({ command: "updateAutofillInlineMenuListCiphers", @@ -1205,6 +1225,7 @@ describe("OverlayBackground", () => { ], showInlineMenuAccountCreation: false, showPasskeysLabels: true, + focusedFieldHasValue: false, }); }); @@ -1216,7 +1237,7 @@ describe("OverlayBackground", () => { ); overlayBackground["focusedFieldData"] = createFocusedFieldDataMock({ tabId: tab.id, - filledByCipherType: CipherType.Login, + inlineMenuFillType: CipherType.Login, showPasskeys: true, }); cipherService.getAllDecryptedForUrl.mockResolvedValue([loginCipher1, passkeyCipher]); @@ -1225,6 +1246,7 @@ describe("OverlayBackground", () => { neverDomainsMock$.next({ "jest-testing-website.com": null }); await overlayBackground.updateOverlayCiphers(); + await flushPromises(); expect(listPortSpy.postMessage).toHaveBeenCalledWith({ command: "updateAutofillInlineMenuListCiphers", @@ -1268,6 +1290,7 @@ describe("OverlayBackground", () => { ], showInlineMenuAccountCreation: false, showPasskeysLabels: false, + focusedFieldHasValue: false, }); }); @@ -1280,7 +1303,7 @@ describe("OverlayBackground", () => { ); overlayBackground["focusedFieldData"] = createFocusedFieldDataMock({ tabId: tab.id, - filledByCipherType: CipherType.Login, + inlineMenuFillType: CipherType.Login, showPasskeys: true, }); cipherService.getAllDecryptedForUrl.mockResolvedValue([loginCipher1, passkeyCipher]); @@ -1288,6 +1311,7 @@ describe("OverlayBackground", () => { getTabFromCurrentWindowIdSpy.mockResolvedValueOnce(tab); await overlayBackground.updateOverlayCiphers(); + await flushPromises(); expect(listPortSpy.postMessage).toHaveBeenCalledWith({ command: "updateAutofillInlineMenuListCiphers", @@ -1331,8 +1355,70 @@ describe("OverlayBackground", () => { ], showInlineMenuAccountCreation: false, showPasskeysLabels: false, + focusedFieldHasValue: false, }); }); + + it("updates the inline menu list with login ciphers when the field fill type is for updating the current password", async () => { + sendMockExtensionMessage( + { + command: "updateFocusedFieldData", + focusedFieldData: createFocusedFieldDataMock({ + inlineMenuFillType: InlineMenuFillType.CurrentPasswordUpdate, + }), + }, + mock({ tab }), + ); + getTabFromCurrentWindowIdSpy.mockResolvedValueOnce(tab); + cipherService.getAllDecryptedForUrl.mockResolvedValue([loginCipher2, loginCipher1]); + cipherService.sortCiphersByLastUsedThenName.mockReturnValue(-1); + + await overlayBackground.updateOverlayCiphers(false); + await flushPromises(); + + expect(listPortSpy.postMessage).toHaveBeenCalledWith( + expect.objectContaining({ + ciphers: [ + { + id: "inline-menu-cipher-0", + name: loginCipher1.name, + type: CipherType.Login, + reprompt: loginCipher1.reprompt, + favorite: loginCipher1.favorite, + icon: { + fallbackImage: "images/bwi-globe.png", + icon: "bwi-globe", + image: "https://icons.bitwarden.com//jest-testing-website.com/icon.png", + imageEnabled: true, + }, + accountCreationFieldType: undefined, + login: { + username: loginCipher1.login.username, + passkey: null, + }, + }, + { + id: "inline-menu-cipher-1", + name: loginCipher2.name, + type: CipherType.Login, + reprompt: loginCipher2.reprompt, + favorite: loginCipher2.favorite, + icon: { + fallbackImage: "images/bwi-globe.png", + icon: "bwi-globe", + image: "https://icons.bitwarden.com//jest-testing-website.com/icon.png", + imageEnabled: true, + }, + accountCreationFieldType: undefined, + login: { + username: loginCipher2.login.username, + passkey: null, + }, + }, + ], + }), + ); + }); }); describe("extension message handlers", () => { @@ -1874,7 +1960,6 @@ describe("OverlayBackground", () => { const focusedFieldData = createFocusedFieldDataMock({ tabId: tab.id, frameId: sender.frameId, - showInlineMenuAccountCreation: true, }); sendMockExtensionMessage({ command: "updateFocusedFieldData", focusedFieldData }, sender); @@ -1885,6 +1970,7 @@ describe("OverlayBackground", () => { ciphers: [], showInlineMenuAccountCreation: true, showPasskeysLabels: false, + focusedFieldHasValue: false, }); }); @@ -1895,7 +1981,7 @@ describe("OverlayBackground", () => { const focusedFieldData = createFocusedFieldDataMock({ tabId: tab.id, frameId: sender.frameId, - filledByCipherType: CipherType.Login, + inlineMenuFillType: CipherType.Login, }); sendMockExtensionMessage({ command: "updateFocusedFieldData", focusedFieldData }, sender); await flushPromises(); @@ -1903,7 +1989,7 @@ describe("OverlayBackground", () => { const newFocusedFieldData = createFocusedFieldDataMock({ tabId: tab.id, frameId: sender.frameId, - filledByCipherType: CipherType.Card, + inlineMenuFillType: CipherType.Card, }); sendMockExtensionMessage( { command: "updateFocusedFieldData", focusedFieldData: newFocusedFieldData }, @@ -1913,6 +1999,109 @@ describe("OverlayBackground", () => { expect(updateOverlayCiphersSpy).toHaveBeenCalled(); }); + + describe("displaying the password generation menu", () => { + const tab = createChromeTabMock({ id: 2 }); + const sender = mock({ tab, frameId: 100 }); + let focusedFieldData: FocusedFieldData; + + beforeEach(async () => { + await initOverlayElementPorts(); + activeAccountStatusMock$.next(AuthenticationStatus.Unlocked); + overlayBackground["focusedFieldData"] = createFocusedFieldDataMock(); + overlayBackground["isInlineMenuButtonVisible"] = true; + focusedFieldData = createFocusedFieldDataMock({ + tabId: tab.id, + frameId: sender.frameId, + }); + }); + + it("displays the password generator when the focused field is for password generation", async () => { + focusedFieldData.inlineMenuFillType = InlineMenuFillType.PasswordGeneration; + + sendMockExtensionMessage({ command: "updateFocusedFieldData", focusedFieldData }, sender); + await flushPromises(); + + expect(listPortSpy.postMessage).toHaveBeenCalledWith( + expect.objectContaining({ + command: "updateAutofillInlineMenuGeneratedPassword", + }), + ); + }); + + it("displays the password generator when the focused field is for login and the field has an account creation type of password", async () => { + focusedFieldData.inlineMenuFillType = CipherType.Login; + focusedFieldData.accountCreationFieldType = InlineMenuAccountCreationFieldType.Password; + + sendMockExtensionMessage({ command: "updateFocusedFieldData", focusedFieldData }, sender); + await flushPromises(); + + expect(listPortSpy.postMessage).toHaveBeenCalledWith( + expect.objectContaining({ + command: "updateAutofillInlineMenuGeneratedPassword", + }), + ); + }); + }); + + describe("displaying the save login menu", () => { + const tab = createChromeTabMock({ id: 2 }); + const sender = mock({ tab, frameId: 100 }); + let focusedFieldData: FocusedFieldData; + let formData: InlineMenuFormFieldData; + + beforeEach(async () => { + await initOverlayElementPorts(); + activeAccountStatusMock$.next(AuthenticationStatus.Unlocked); + overlayBackground["focusedFieldData"] = createFocusedFieldDataMock(); + overlayBackground["isInlineMenuButtonVisible"] = true; + focusedFieldData = createFocusedFieldDataMock({ + tabId: tab.id, + frameId: sender.frameId, + }); + formData = { + uri: "https://example.com", + username: "username", + password: "password", + newPassword: "newPassword", + }; + tabsSendMessageSpy.mockImplementation((_tab, message) => { + if (message.command === "getInlineMenuFormFieldData") { + return Promise.resolve(formData); + } + + return Promise.resolve(); + }); + }); + + it("shows the save login menu when the focused field type is for password generation and the field is filled", async () => { + focusedFieldData.inlineMenuFillType = InlineMenuFillType.PasswordGeneration; + + sendMockExtensionMessage( + { command: "updateFocusedFieldData", focusedFieldData, focusedFieldHasValue: true }, + sender, + ); + await flushPromises(); + + expect(listPortSpy.postMessage).toHaveBeenCalledWith({ + command: "showSaveLoginInlineMenuList", + }); + }); + + it("shows the save login menu when the focused field type is for a login cipher and the field is filled", async () => { + focusedFieldData.inlineMenuFillType = CipherType.Login; + + sendMockExtensionMessage( + { command: "updateFocusedFieldData", focusedFieldData, focusedFieldHasValue: true }, + sender, + ); + await flushPromises(); + + expect(listPortSpy.postMessage).toHaveBeenCalledWith({ + command: "showSaveLoginInlineMenuList", + }); + }); + }); }); describe("updateIsFieldCurrentlyFocused message handler", () => { @@ -2004,48 +2193,195 @@ describe("OverlayBackground", () => { describe("openAutofillInlineMenu message handler", () => { let sender: chrome.runtime.MessageSender; + const topFrameSendOptions = { frameId: 0 }; beforeEach(() => { - sender = mock({ tab: { id: 1 } }); + sender = mock({ + tab: createChromeTabMock({ id: 1, url: "https://jest-testing-website.com" }), + }); getTabFromCurrentWindowIdSpy.mockResolvedValue(sender.tab); tabsSendMessageSpy.mockImplementation(); + sendMockExtensionMessage( + { command: "updateFocusedFieldData", focusedFieldData: createFocusedFieldDataMock() }, + sender, + ); }); - it("opens the autofill inline menu by sending a message to the current tab", async () => { - sendMockExtensionMessage({ command: "openAutofillInlineMenu" }, sender); + it("updates the inline menu position of both button and list elements if the inline menu is being forced open", async () => { + sendMockExtensionMessage( + { command: "openAutofillInlineMenu", isOpeningFullInlineMenu: true }, + sender, + ); await flushPromises(); expect(tabsSendMessageSpy).toHaveBeenCalledWith( sender.tab, { - command: "openAutofillInlineMenu", - isFocusingFieldElement: false, - isOpeningFullInlineMenu: false, - authStatus: AuthenticationStatus.Unlocked, + command: "appendAutofillInlineMenuToDom", + overlayElement: AutofillOverlayElement.Button, }, - { frameId: 0 }, + topFrameSendOptions, ); - }); - - it("sends the open menu message to the focused field's frameId", async () => { - sender.frameId = 10; - sendMockExtensionMessage({ command: "updateFocusedFieldData" }, sender); - await flushPromises(); - - sendMockExtensionMessage({ command: "openAutofillInlineMenu" }, sender); - await flushPromises(); - expect(tabsSendMessageSpy).toHaveBeenCalledWith( sender.tab, { - command: "openAutofillInlineMenu", - isFocusingFieldElement: false, - isOpeningFullInlineMenu: false, - authStatus: AuthenticationStatus.Unlocked, + command: "appendAutofillInlineMenuToDom", + overlayElement: AutofillOverlayElement.List, }, - { frameId: 10 }, + topFrameSendOptions, ); }); + + describe("when the focused field does not have a value", () => { + beforeEach(() => { + jest + .spyOn(overlayBackground as any, "checkFocusedFieldHasValue") + .mockResolvedValue(false); + }); + + it("updates the position of the both button and list elements if the user has the inline menu set to show on field focus", async () => { + inlineMenuVisibilityMock$.next(AutofillOverlayVisibility.OnFieldFocus); + + sendMockExtensionMessage({ command: "openAutofillInlineMenu" }, sender); + await flushPromises(); + + expect(tabsSendMessageSpy).toHaveBeenCalledWith( + sender.tab, + { + command: "appendAutofillInlineMenuToDom", + overlayElement: AutofillOverlayElement.Button, + }, + topFrameSendOptions, + ); + expect(tabsSendMessageSpy).toHaveBeenCalledWith( + sender.tab, + { + command: "appendAutofillInlineMenuToDom", + overlayElement: AutofillOverlayElement.List, + }, + topFrameSendOptions, + ); + }); + + it("closes the list if the user has the inline menu set to show on button click and the list is open", async () => { + overlayBackground["isInlineMenuListVisible"] = true; + inlineMenuVisibilityMock$.next(AutofillOverlayVisibility.OnButtonClick); + + sendMockExtensionMessage({ command: "openAutofillInlineMenu" }, sender); + await flushPromises(); + + expect(tabsSendMessageSpy).toHaveBeenCalledWith( + sender.tab, + { command: "closeAutofillInlineMenu", overlayElement: AutofillOverlayElement.List }, + topFrameSendOptions, + ); + }); + + it("updates the position of the button if the user has the inline menu set to show on button click", async () => { + inlineMenuVisibilityMock$.next(AutofillOverlayVisibility.OnButtonClick); + + sendMockExtensionMessage({ command: "openAutofillInlineMenu" }, sender); + await flushPromises(); + + expect(tabsSendMessageSpy).toHaveBeenCalledWith( + sender.tab, + { + command: "appendAutofillInlineMenuToDom", + overlayElement: AutofillOverlayElement.Button, + }, + topFrameSendOptions, + ); + expect(tabsSendMessageSpy).not.toHaveBeenCalledWith( + sender.tab, + { + command: "appendAutofillInlineMenuToDom", + overlayElement: AutofillOverlayElement.List, + }, + topFrameSendOptions, + ); + }); + }); + + describe("when the focused field has a value", () => { + beforeEach(() => { + overlayBackground["inlineMenuCiphers"] = new Map([ + ["inline-menu-cipher-1", mock({ id: "inline-menu-cipher-1" })], + ]); + jest.spyOn(overlayBackground as any, "checkFocusedFieldHasValue").mockResolvedValue(true); + }); + + it("updates the position of both button and list elements if the inline menu is showing the save login view", async () => { + overlayBackground["inlineMenuCiphers"] = new Map([]); + const formData = { + uri: "https://example.com", + username: "username", + password: "password", + newPassword: "newPassword", + }; + tabsSendMessageSpy.mockImplementation((_tab, message) => { + if (message.command === "getInlineMenuFormFieldData") { + return Promise.resolve(formData); + } + + return Promise.resolve(); + }); + + sendMockExtensionMessage({ command: "openAutofillInlineMenu" }, sender); + await flushPromises(); + + expect(tabsSendMessageSpy).toHaveBeenCalledWith( + sender.tab, + { + command: "appendAutofillInlineMenuToDom", + overlayElement: AutofillOverlayElement.Button, + }, + topFrameSendOptions, + ); + expect(tabsSendMessageSpy).toHaveBeenCalledWith( + sender.tab, + { + command: "appendAutofillInlineMenuToDom", + overlayElement: AutofillOverlayElement.List, + }, + topFrameSendOptions, + ); + }); + + it("closes the inline menu list if it is visible", async () => { + overlayBackground["isInlineMenuListVisible"] = true; + + sendMockExtensionMessage({ command: "openAutofillInlineMenu" }, sender); + await flushPromises(); + + expect(tabsSendMessageSpy).toHaveBeenCalledWith( + sender.tab, + { command: "closeAutofillInlineMenu", overlayElement: AutofillOverlayElement.List }, + topFrameSendOptions, + ); + }); + + it("updates the position of the inline menu button", async () => { + sendMockExtensionMessage({ command: "openAutofillInlineMenu" }, sender); + await flushPromises(); + + expect(tabsSendMessageSpy).toHaveBeenCalledWith( + sender.tab, + { + command: "appendAutofillInlineMenuToDom", + overlayElement: AutofillOverlayElement.Button, + }, + topFrameSendOptions, + ); + expect(tabsSendMessageSpy).not.toHaveBeenCalledWith( + sender.tab, + { + command: "appendAutofillInlineMenuToDom", + overlayElement: AutofillOverlayElement.List, + }, + topFrameSendOptions, + ); + }); + }); }); describe("closeAutofillInlineMenu", () => { @@ -2239,192 +2575,19 @@ describe("OverlayBackground", () => { }); }); - describe("updateAutofillInlineMenuPosition message handler", () => { - beforeEach(async () => { - await initOverlayElementPorts(); - }); - - it("ignores updating the position if the overlay element type is not provided", () => { - sendMockExtensionMessage({ command: "updateAutofillInlineMenuPosition" }); - - expect(listPortSpy.postMessage).not.toHaveBeenCalledWith({ - command: "updateIframePosition", - styles: expect.anything(), - }); - expect(buttonPortSpy.postMessage).not.toHaveBeenCalledWith({ - command: "updateIframePosition", - styles: expect.anything(), - }); - }); - - it("skips updating the position if the most recently focused field is different than the message sender", () => { - const sender = mock({ tab: { id: 1 } }); - const focusedFieldData = createFocusedFieldDataMock({ tabId: 2 }); - sendMockExtensionMessage({ command: "updateFocusedFieldData", focusedFieldData }); - - sendMockExtensionMessage({ command: "updateAutofillInlineMenuPosition" }, sender); - - expect(listPortSpy.postMessage).not.toHaveBeenCalledWith({ - command: "updateIframePosition", - styles: expect.anything(), - }); - expect(buttonPortSpy.postMessage).not.toHaveBeenCalledWith({ - command: "updateIframePosition", - styles: expect.anything(), - }); - }); - - it("updates the inline menu button's position", async () => { - const focusedFieldData = createFocusedFieldDataMock(); - sendMockExtensionMessage({ command: "updateFocusedFieldData", focusedFieldData }); - - sendMockExtensionMessage({ - command: "updateAutofillInlineMenuPosition", - overlayElement: AutofillOverlayElement.Button, - }); - await flushPromises(); - - expect(buttonPortSpy.postMessage).toHaveBeenCalledWith({ - command: "updateAutofillInlineMenuPosition", - styles: { height: "2px", left: "4px", top: "2px", width: "2px" }, - }); - }); - - it("modifies the inline menu button's height for medium sized input elements", async () => { - const focusedFieldData = createFocusedFieldDataMock({ - focusedFieldRects: { top: 1, left: 2, height: 35, width: 4 }, - }); - sendMockExtensionMessage({ command: "updateFocusedFieldData", focusedFieldData }); - - sendMockExtensionMessage({ - command: "updateAutofillInlineMenuPosition", - overlayElement: AutofillOverlayElement.Button, - }); - await flushPromises(); - - expect(buttonPortSpy.postMessage).toHaveBeenCalledWith({ - command: "updateAutofillInlineMenuPosition", - styles: { height: "20px", left: "-22px", top: "8px", width: "20px" }, - }); - }); - - it("modifies the inline menu button's height for large sized input elements", async () => { - const focusedFieldData = createFocusedFieldDataMock({ - focusedFieldRects: { top: 1, left: 2, height: 50, width: 4 }, - }); - sendMockExtensionMessage({ command: "updateFocusedFieldData", focusedFieldData }); - - sendMockExtensionMessage({ - command: "updateAutofillInlineMenuPosition", - overlayElement: AutofillOverlayElement.Button, - }); - await flushPromises(); - - expect(buttonPortSpy.postMessage).toHaveBeenCalledWith({ - command: "updateAutofillInlineMenuPosition", - styles: { height: "27px", left: "-32px", top: "13px", width: "27px" }, - }); - }); - - it("takes into account the right padding of the focused field in positioning the button if the right padding of the field is larger than the left padding", async () => { - const focusedFieldData = createFocusedFieldDataMock({ - focusedFieldStyles: { paddingRight: "20px", paddingLeft: "6px" }, - }); - sendMockExtensionMessage({ command: "updateFocusedFieldData", focusedFieldData }); - - sendMockExtensionMessage({ - command: "updateAutofillInlineMenuPosition", - overlayElement: AutofillOverlayElement.Button, - }); - await flushPromises(); - - expect(buttonPortSpy.postMessage).toHaveBeenCalledWith({ - command: "updateAutofillInlineMenuPosition", - styles: { height: "2px", left: "-18px", top: "2px", width: "2px" }, - }); - }); - - it("updates the inline menu list's position", async () => { - const focusedFieldData = createFocusedFieldDataMock(); - sendMockExtensionMessage({ command: "updateFocusedFieldData", focusedFieldData }); - - sendMockExtensionMessage({ - command: "updateAutofillInlineMenuPosition", - overlayElement: AutofillOverlayElement.List, - }); - await flushPromises(); - - expect(listPortSpy.postMessage).toHaveBeenCalledWith({ - command: "updateAutofillInlineMenuPosition", - styles: { left: "2px", top: "4px", width: "4px" }, - }); - }); - - it("sends a message that triggers a simultaneous fade in for both inline menu elements", async () => { - jest.useFakeTimers(); - const focusedFieldData = createFocusedFieldDataMock(); - sendMockExtensionMessage({ command: "updateFocusedFieldData", focusedFieldData }); - - sendMockExtensionMessage({ - command: "updateAutofillInlineMenuPosition", - overlayElement: AutofillOverlayElement.List, - }); - await flushPromises(); - jest.advanceTimersByTime(150); - - expect(buttonPortSpy.postMessage).toHaveBeenCalledWith({ - command: "fadeInAutofillInlineMenuIframe", - }); - expect(listPortSpy.postMessage).toHaveBeenCalledWith({ - command: "fadeInAutofillInlineMenuIframe", - }); - }); - - describe("getAutofillInlineMenuPosition", () => { - it("returns the current inline menu position", async () => { - overlayBackground["inlineMenuPosition"] = { - button: { left: 1, top: 2, width: 3, height: 4 }, - }; - - sendMockExtensionMessage( - { command: "getAutofillInlineMenuPosition" }, - mock(), - sendResponse, - ); - await flushPromises(); - - expect(sendResponse).toHaveBeenCalledWith({ - button: { left: 1, top: 2, width: 3, height: 4 }, - }); - }); - }); - - it("triggers a debounced reposition of the inline menu if the sender frame has a `null` sub frame offsets value", async () => { - jest.useFakeTimers(); - const focusedFieldData = createFocusedFieldDataMock(); - const sender = mock({ - tab: { id: focusedFieldData.tabId }, - frameId: focusedFieldData.frameId, - }); - sendMockExtensionMessage({ command: "updateFocusedFieldData", focusedFieldData }, sender); - overlayBackground["subFrameOffsetsForTab"][focusedFieldData.tabId] = new Map([ - [focusedFieldData.frameId, null], - ]); - jest.spyOn(overlayBackground as any, "updateInlineMenuPositionAfterRepositionEvent"); + describe("getAutofillInlineMenuPosition", () => { + it("returns the current inline menu positio", async () => { + const inlineMenuPosition: InlineMenuPosition = mock(); + overlayBackground["inlineMenuPosition"] = inlineMenuPosition; sendMockExtensionMessage( - { - command: "updateAutofillInlineMenuPosition", - overlayElement: AutofillOverlayElement.List, - }, - sender, + { command: "getAutofillInlineMenuPosition" }, + mock(), + sendResponse, ); await flushPromises(); - jest.advanceTimersByTime(150); - expect( - overlayBackground["updateInlineMenuPositionAfterRepositionEvent"], - ).toHaveBeenCalled(); + expect(sendResponse).toHaveBeenCalledWith(inlineMenuPosition); }); }); @@ -2553,10 +2716,8 @@ describe("OverlayBackground", () => { }); describe("unlockCompleted", () => { - let updateInlineMenuCiphersSpy: jest.SpyInstance; - beforeEach(async () => { - updateInlineMenuCiphersSpy = jest.spyOn(overlayBackground, "updateOverlayCiphers"); + jest.spyOn(overlayBackground, "updateOverlayCiphers"); await initOverlayElementPorts(); }); @@ -2578,8 +2739,8 @@ describe("OverlayBackground", () => { expect(updateInlineMenuCiphersSpy).toHaveBeenCalled(); }); - it("opens the inline menu if a retry command is present in the message", async () => { - updateInlineMenuCiphersSpy.mockImplementation(); + it("focuses the most recently focused field if a retry command is present in the message", async () => { + activeAccountStatusMock$.next(AuthenticationStatus.Unlocked); getTabFromCurrentWindowIdSpy.mockResolvedValueOnce(createChromeTabMock({ id: 1 })); sendMockExtensionMessage({ command: "unlockCompleted", @@ -2589,16 +2750,9 @@ describe("OverlayBackground", () => { }); await flushPromises(); - expect(tabsSendMessageSpy).toHaveBeenCalledWith( - expect.any(Object), - { - command: "openAutofillInlineMenu", - isFocusingFieldElement: true, - isOpeningFullInlineMenu: false, - authStatus: AuthenticationStatus.Unlocked, - }, - { frameId: 0 }, - ); + expect(tabsSendMessageSpy).toHaveBeenCalledWith(expect.any(Object), { + command: "focusMostRecentlyFocusedField", + }); }); }); @@ -2667,7 +2821,7 @@ describe("OverlayBackground", () => { const portKey = "inlineMenuButtonPort"; beforeEach(async () => { - sender = mock({ tab: { id: 1 } }); + sender = mock({ tab: createChromeTabMock({ id: 1 }) }); portKeyForTabSpy[sender.tab.id] = portKey; activeAccountStatusMock$.next(AuthenticationStatus.Unlocked); await initOverlayElementPorts(); @@ -2703,6 +2857,16 @@ describe("OverlayBackground", () => { it("opens the inline menu if the user auth status is unlocked", async () => { getTabFromCurrentWindowIdSpy.mockResolvedValueOnce(sender.tab); + sendMockExtensionMessage( + { + command: "updateFocusedFieldData", + focusedFieldData: mock(), + }, + mock({ tab: buttonMessageConnectorSpy.sender.tab }), + ); + jest + .spyOn(overlayBackground as any, "getInlineMenuButtonPosition") + .mockReturnValueOnce({ x: 0, y: 0 }); sendPortMessage(buttonMessageConnectorSpy, { command: "autofillInlineMenuButtonClicked", portKey, @@ -2712,10 +2876,8 @@ describe("OverlayBackground", () => { expect(tabsSendMessageSpy).toHaveBeenCalledWith( sender.tab, { - command: "openAutofillInlineMenu", - isFocusingFieldElement: false, - isOpeningFullInlineMenu: true, - authStatus: AuthenticationStatus.Unlocked, + command: "appendAutofillInlineMenuToDom", + overlayElement: AutofillOverlayElement.List, }, { frameId: 0 }, ); @@ -2936,7 +3098,7 @@ describe("OverlayBackground", () => { expect(autofillService.doAutoFill).not.toHaveBeenCalled(); }); - it("autofills the selected cipher and move it to the top of the front of the ciphers map", async () => { + it("autofills the selected cipher and moves it to the top of the front of the ciphers map", async () => { const cipher1 = mock({ id: "inline-menu-cipher-1" }); const cipher2 = mock({ id: "inline-menu-cipher-2" }); const cipher3 = mock({ id: "inline-menu-cipher-3" }); @@ -3094,6 +3256,54 @@ describe("OverlayBackground", () => { }); }); }); + + it("fills the current password fields exclusively when filling for a current password update", async () => { + globalThis.structuredClone = jest.fn((value) => value); + sendMockExtensionMessage( + { + command: "updateFocusedFieldData", + focusedFieldData: createFocusedFieldDataMock({ + inlineMenuFillType: InlineMenuFillType.CurrentPasswordUpdate, + }), + }, + sender, + ); + const currentPasswordField = createAutofillFieldMock({ + autoCompleteType: "current-password", + title: "Current Password", + type: "password", + }); + const newPasswordField = createAutofillFieldMock({ + autoCompleteType: "new-password", + title: "New Password", + type: "password", + }); + const confirmNewPasswordField = createAutofillFieldMock({ + autoCompleteType: "new-password", + title: "Confirm New Password", + type: "password", + }); + const pageDetails = createAutofillPageDetailsMock({ + fields: [currentPasswordField, newPasswordField, confirmNewPasswordField], + }); + overlayBackground["pageDetailsForTab"][sender.tab.id] = new Map([ + [sender.frameId, { frameId: sender.frameId, tab: sender.tab, details: pageDetails }], + ]); + + sendPortMessage(listMessageConnectorSpy, { + command: "fillAutofillInlineMenuCipher", + inlineMenuCipherId: "inline-menu-cipher-2", + portKey, + }); + await flushPromises(); + + pageDetails.fields = [currentPasswordField]; + expect(autofillService.doAutoFill).toHaveBeenCalledWith( + expect.objectContaining({ + pageDetails: [expect.objectContaining({ details: pageDetails })], + }), + ); + }); }); describe("addNewVaultItem message handler", () => { @@ -3102,10 +3312,17 @@ describe("OverlayBackground", () => { sendMockExtensionMessage({ command: "updateFocusedFieldData", focusedFieldData }); await flushPromises(); - sendPortMessage(listMessageConnectorSpy, { command: "addNewVaultItem", portKey }); + sendPortMessage(listMessageConnectorSpy, { + command: "addNewVaultItem", + portKey, + addNewCipherType: CipherType.Login, + }); await flushPromises(); - expect(tabsSendMessageSpy).not.toHaveBeenCalled(); + expect(tabsSendMessageSpy).not.toHaveBeenCalledWith(sender.tab, { + command: "addNewVaultItemFromOverlay", + addNewCipherType: CipherType.Login, + }); }); it("sends a message to the tab to add a new vault item", async () => { @@ -3219,6 +3436,113 @@ describe("OverlayBackground", () => { }); }); }); + + describe("refreshGeneratedPassword", () => { + it("refreshes the generated password", async () => { + overlayBackground["generatedPassword"] = "populated"; + + sendPortMessage(listMessageConnectorSpy, { command: "refreshGeneratedPassword", portKey }); + await flushPromises(); + + expect(generatedPasswordCallbackMock).toHaveBeenCalled(); + }); + + it("sends a message to the list port indicating that the generated password should be updated", async () => { + sendPortMessage(listMessageConnectorSpy, { command: "refreshGeneratedPassword", portKey }); + await flushPromises(); + + expect(listPortSpy.postMessage).toHaveBeenCalledWith({ + command: "updateAutofillInlineMenuGeneratedPassword", + generatedPassword, + refreshPassword: true, + }); + }); + }); + + describe("fillGeneratedPassword", () => { + const focusedFieldData = createFocusedFieldDataMock({ + inlineMenuFillType: InlineMenuFillType.PasswordGeneration, + }); + + beforeEach(() => { + globalThis.structuredClone = jest.fn((value) => value); + sendMockExtensionMessage( + { + command: "updateFocusedFieldData", + focusedFieldData, + }, + sender, + ); + overlayBackground["generatedPassword"] = generatedPassword; + overlayBackground["pageDetailsForTab"][sender.tab.id] = new Map([ + [sender.frameId, createPageDetailMock()], + ]); + }); + + describe("skipping filling the generated password", () => { + it("skips filling when the password has not been created", () => { + overlayBackground["generatedPassword"] = ""; + + sendPortMessage(listMessageConnectorSpy, { command: "fillGeneratedPassword", portKey }); + + expect(autofillService.doAutoFill).not.toHaveBeenCalled(); + }); + + it("skips filling when the page details for the tab are not set", () => { + overlayBackground["pageDetailsForTab"][sender.tab.id] = undefined; + + sendPortMessage(listMessageConnectorSpy, { command: "fillGeneratedPassword", portKey }); + + expect(autofillService.doAutoFill).not.toHaveBeenCalled(); + }); + + it("skips filling when the page details for the tab does not contain a value", () => { + overlayBackground["pageDetailsForTab"][sender.tab.id] = new Map([]); + + sendPortMessage(listMessageConnectorSpy, { command: "fillGeneratedPassword", portKey }); + + expect(autofillService.doAutoFill).not.toHaveBeenCalled(); + }); + }); + + it("filters the page details to only include the new password fields before filling", async () => { + sendPortMessage(listMessageConnectorSpy, { command: "fillGeneratedPassword", portKey }); + await flushPromises(); + + expect(autofillService.doAutoFill).toHaveBeenCalledWith({ + tab: sender.tab, + cipher: expect.any(Object), + pageDetails: [overlayBackground["pageDetailsForTab"][sender.tab.id].get(sender.frameId)], + fillNewPassword: true, + allowTotpAutofill: false, + }); + }); + + it("opens the inline menu for fields that fill a generated password", async () => { + jest.useFakeTimers(); + const formData = { + uri: "https://example.com", + username: "username", + password: "password", + newPassword: "newPassword", + }; + tabsSendMessageSpy.mockImplementation((_tab, message) => { + if (message.command === "getInlineMenuFormFieldData") { + return Promise.resolve(formData); + } + + return Promise.resolve(); + }); + const openInlineMenuSpy = jest.spyOn(overlayBackground as any, "openInlineMenu"); + + sendPortMessage(listMessageConnectorSpy, { command: "fillGeneratedPassword", portKey }); + await flushPromises(); + jest.advanceTimersByTime(400); + await flushPromises(); + + expect(openInlineMenuSpy).toHaveBeenCalled(); + }); + }); }); describe("handle web navigation on committed events", () => { @@ -3302,6 +3626,20 @@ describe("OverlayBackground", () => { expect(overlayBackground["expiredPorts"].length).toBe(1); }); + + it("generates a password for the password generator view", async () => { + activeAccountStatusMock$.next(AuthenticationStatus.Unlocked); + const focusedFieldData = createFocusedFieldDataMock({ + inlineMenuFillType: CipherType.Login, + accountCreationFieldType: InlineMenuAccountCreationFieldType.Password, + }); + sendMockExtensionMessage({ command: "updateFocusedFieldData", focusedFieldData }); + + await initOverlayElementPorts(); + await flushPromises(); + + expect(generatedPasswordCallbackMock).toHaveBeenCalled(); + }); }); describe("handle overlay element port onMessage", () => { diff --git a/apps/browser/src/autofill/background/overlay.background.ts b/apps/browser/src/autofill/background/overlay.background.ts index 49788d67404..2b8f2c273c7 100644 --- a/apps/browser/src/autofill/background/overlay.background.ts +++ b/apps/browser/src/autofill/background/overlay.background.ts @@ -1,13 +1,13 @@ import { + debounceTime, firstValueFrom, + map, merge, + Observable, ReplaySubject, Subject, - throttleTime, switchMap, - debounceTime, - Observable, - map, + throttleTime, } from "rxjs"; import { parse } from "tldts"; @@ -52,13 +52,21 @@ import { import { AutofillOverlayElement, AutofillOverlayPort, + InlineMenuAccountCreationFieldType, + InlineMenuAccountCreationFieldTypes, + InlineMenuFillType, + InlineMenuFillTypes, MAX_SUB_FRAME_DEPTH, } from "../enums/autofill-overlay.enum"; -import { AutofillService } from "../services/abstractions/autofill.service"; +import AutofillField from "../models/autofill-field"; +import { InlineMenuFormFieldData } from "../services/abstractions/autofill-overlay-content.service"; +import { AutofillService, PageDetail } from "../services/abstractions/autofill.service"; +import { InlineMenuFieldQualificationService } from "../services/abstractions/inline-menu-field-qualifications.service"; import { generateDomainMatchPatterns, generateRandomChars, isInvalidResponseStatusCode, + specialCharacterToKeyMap, } from "../utils"; import { LockedVaultPendingNotificationsData } from "./abstractions/notification.background"; @@ -83,33 +91,39 @@ import { SubFrameOffsetData, SubFrameOffsetsForTab, ToggleInlineMenuHiddenMessage, + UpdateInlineMenuVisibilityMessage, + UpdateOverlayCiphersParams, } from "./abstractions/overlay.background"; export class OverlayBackground implements OverlayBackgroundInterface { private readonly openUnlockPopout = openUnlockPopout; private readonly openViewVaultItemPopout = openViewVaultItemPopout; private readonly openAddEditVaultItemPopout = openAddEditVaultItemPopout; - private readonly storeInlineMenuFido2CredentialsSubject = new ReplaySubject(1); + private readonly updateOverlayCiphers$ = new Subject(); + private readonly storeInlineMenuFido2Credentials$ = new ReplaySubject(1); + private readonly startInlineMenuDelayedClose$ = new Subject(); + private readonly cancelInlineMenuDelayedClose$ = new Subject(); + private readonly startInlineMenuFadeIn$ = new Subject(); + private readonly cancelInlineMenuFadeIn$ = new Subject(); + private readonly startUpdateInlineMenuPosition$ = new Subject(); + private readonly cancelUpdateInlineMenuPosition$ = new Subject(); + private readonly repositionInlineMenu$ = new Subject(); + private readonly rebuildSubFrameOffsets$ = new Subject(); + private readonly addNewVaultItem$ = new Subject(); private pageDetailsForTab: PageDetailsForTab = {}; private subFrameOffsetsForTab: SubFrameOffsetsForTab = {}; private portKeyForTab: Record = {}; private expiredPorts: chrome.runtime.Port[] = []; private inlineMenuButtonPort: chrome.runtime.Port; + private inlineMenuButtonMessageConnectorPort: chrome.runtime.Port; private inlineMenuListPort: chrome.runtime.Port; + private inlineMenuListMessageConnectorPort: chrome.runtime.Port; private inlineMenuCiphers: Map = new Map(); private inlineMenuFido2Credentials: Set = new Set(); private inlineMenuPageTranslations: Record; private inlineMenuPosition: InlineMenuPosition = {}; private cardAndIdentityCiphers: Set | null = null; private currentInlineMenuCiphersCount: number = 0; - private delayedCloseTimeout: number | NodeJS.Timeout; - private startInlineMenuFadeInSubject = new Subject(); - private cancelInlineMenuFadeInSubject = new Subject(); - private startUpdateInlineMenuPositionSubject = new Subject(); - private cancelUpdateInlineMenuPositionSubject = new Subject(); - private repositionInlineMenuSubject = new Subject(); - private rebuildSubFrameOffsetsSubject = new Subject(); - private addNewVaultItemSubject = new Subject(); private currentAddNewItemData: CurrentAddNewItemData; private focusedFieldData: FocusedFieldData; private isFieldCurrentlyFocused: boolean = false; @@ -118,6 +132,13 @@ export class OverlayBackground implements OverlayBackgroundInterface { private isInlineMenuListVisible: boolean = false; private showPasskeysLabelsWithinInlineMenu: boolean = false; private iconsServerUrl: string; + private generatedPassword: string; + private readonly validPortConnections: Set = new Set([ + AutofillOverlayPort.Button, + AutofillOverlayPort.ButtonMessageConnector, + AutofillOverlayPort.List, + AutofillOverlayPort.ListMessageConnector, + ]); private readonly extensionMessageHandlers: OverlayBackgroundExtensionMessageHandlers = { autofillOverlayElementClosed: ({ message, sender }) => this.overlayElementClosed(message, sender), @@ -132,14 +153,13 @@ export class OverlayBackground implements OverlayBackgroundInterface { updateIsFieldCurrentlyFilling: ({ message }) => this.updateIsFieldCurrentlyFilling(message), checkIsFieldCurrentlyFilling: () => this.checkIsFieldCurrentlyFilling(), getAutofillInlineMenuVisibility: () => this.getInlineMenuVisibility(), + openAutofillInlineMenu: ({ message, sender }) => + this.openInlineMenu(sender, message.isOpeningFullInlineMenu), getInlineMenuCardsVisibility: () => this.getInlineMenuCardsVisibility(), getInlineMenuIdentitiesVisibility: () => this.getInlineMenuIdentitiesVisibility(), - openAutofillInlineMenu: () => this.openInlineMenu(false), closeAutofillInlineMenu: ({ message, sender }) => this.closeInlineMenu(sender, message), checkAutofillInlineMenuFocused: ({ sender }) => this.checkInlineMenuFocused(sender), focusAutofillInlineMenuList: () => this.focusInlineMenuList(), - updateAutofillInlineMenuPosition: ({ message, sender }) => - this.updateInlineMenuPosition(message, sender), getAutofillInlineMenuPosition: () => this.getInlineMenuPosition(), updateAutofillInlineMenuElementIsVisibleStatus: ({ message, sender }) => this.updateInlineMenuElementIsVisibleStatus(message, sender), @@ -157,10 +177,11 @@ export class OverlayBackground implements OverlayBackgroundInterface { addEditCipherSubmitted: () => this.updateOverlayCiphers(), editedCipher: () => this.updateOverlayCiphers(), deletedCipher: () => this.updateOverlayCiphers(), + bgSaveCipher: () => this.updateOverlayCiphers(), fido2AbortRequest: ({ sender }) => this.abortFido2ActiveRequest(sender.tab.id), }; private readonly inlineMenuButtonPortMessageHandlers: InlineMenuButtonPortMessageHandlers = { - triggerDelayedAutofillInlineMenuClosure: () => this.triggerDelayedInlineMenuClosure(), + triggerDelayedAutofillInlineMenuClosure: () => this.startInlineMenuDelayedClose$.next(), autofillInlineMenuButtonClicked: ({ port }) => this.handleInlineMenuButtonClicked(port), autofillInlineMenuBlurred: () => this.checkInlineMenuListFocused(), redirectAutofillInlineMenuFocusOut: ({ message, port }) => @@ -168,8 +189,9 @@ export class OverlayBackground implements OverlayBackgroundInterface { updateAutofillInlineMenuColorScheme: () => this.updateInlineMenuButtonColorScheme(), }; private readonly inlineMenuListPortMessageHandlers: InlineMenuListPortMessageHandlers = { - checkAutofillInlineMenuButtonFocused: () => this.checkInlineMenuButtonFocused(), - autofillInlineMenuBlurred: () => this.checkInlineMenuButtonFocused(), + checkAutofillInlineMenuButtonFocused: ({ port }) => + this.checkInlineMenuButtonFocused(port.sender), + autofillInlineMenuBlurred: ({ port }) => this.checkInlineMenuButtonFocused(port.sender), unlockVault: ({ port }) => this.unlockVault(port), fillAutofillInlineMenuCipher: ({ message, port }) => this.fillInlineMenuCipher(message, port), addNewVaultItem: ({ message, port }) => this.getNewVaultItemDetails(message, port), @@ -177,6 +199,8 @@ export class OverlayBackground implements OverlayBackgroundInterface { redirectAutofillInlineMenuFocusOut: ({ message, port }) => this.redirectInlineMenuFocusOut(message, port), updateAutofillInlineMenuListHeight: ({ message }) => this.updateInlineMenuListHeight(message), + refreshGeneratedPassword: () => this.updateGeneratedPassword(true), + fillGeneratedPassword: ({ port }) => this.fillGeneratedPassword(port), }; constructor( @@ -191,7 +215,10 @@ export class OverlayBackground implements OverlayBackgroundInterface { private platformUtilsService: PlatformUtilsService, private vaultSettingsService: VaultSettingsService, private fido2ActiveRequestManager: Fido2ActiveRequestManager, + private inlineMenuFieldQualificationService: InlineMenuFieldQualificationService, private themeStateService: ThemeStateService, + private generatePasswordCallback: () => Promise, + private addPasswordCallback: (password: string) => Promise, ) { this.initOverlayEventObservables(); } @@ -210,22 +237,30 @@ export class OverlayBackground implements OverlayBackgroundInterface { * Initializes event observables that handle events which affect the overlay's behavior. */ private initOverlayEventObservables() { - this.storeInlineMenuFido2CredentialsSubject + this.updateOverlayCiphers$ + .pipe( + throttleTime(100, null, { leading: true, trailing: true }), + switchMap((updateOverlayCiphersParams) => + this.handleOverlayCiphersUpdate(updateOverlayCiphersParams), + ), + ) + .subscribe(); + this.storeInlineMenuFido2Credentials$ .pipe(switchMap((tabId) => this.availablePasskeyAuthCredentials$(tabId))) .subscribe((credentials) => this.storeInlineMenuFido2Credentials(credentials)); - this.repositionInlineMenuSubject + this.repositionInlineMenu$ .pipe( debounceTime(1000), switchMap((sender) => this.repositionInlineMenu(sender)), ) .subscribe(); - this.rebuildSubFrameOffsetsSubject + this.rebuildSubFrameOffsets$ .pipe( - throttleTime(100), + throttleTime(100, null, { leading: true, trailing: true }), switchMap((sender) => this.rebuildSubFrameOffsets(sender)), ) .subscribe(); - this.addNewVaultItemSubject + this.addNewVaultItem$ .pipe( debounceTime(100), switchMap((addNewItemData) => @@ -234,19 +269,24 @@ export class OverlayBackground implements OverlayBackgroundInterface { ) .subscribe(); + // Delayed close of the inline menu + merge( + this.startInlineMenuDelayedClose$.pipe(debounceTime(100)), + this.cancelInlineMenuDelayedClose$, + ) + .pipe(switchMap((cancelSignal) => this.triggerDelayedInlineMenuClosure(!!cancelSignal))) + .subscribe(); + // Debounce used to update inline menu position merge( - this.startUpdateInlineMenuPositionSubject.pipe(debounceTime(150)), - this.cancelUpdateInlineMenuPositionSubject, + this.startUpdateInlineMenuPosition$.pipe(debounceTime(150)), + this.cancelUpdateInlineMenuPosition$, ) .pipe(switchMap((sender) => this.updateInlineMenuPositionAfterRepositionEvent(sender))) .subscribe(); // FadeIn Observable behavior - merge( - this.startInlineMenuFadeInSubject.pipe(debounceTime(150)), - this.cancelInlineMenuFadeInSubject, - ) + merge(this.startInlineMenuFadeIn$.pipe(debounceTime(150)), this.cancelInlineMenuFadeIn$) .pipe(switchMap((cancelSignal) => this.triggerInlineMenuFadeIn(!!cancelSignal))) .subscribe(); } @@ -266,25 +306,43 @@ export class OverlayBackground implements OverlayBackgroundInterface { if (this.portKeyForTab[tabId]) { delete this.portKeyForTab[tabId]; } + + this.generatedPassword = null; + this.focusedFieldData = null; } /** * Updates the inline menu list's ciphers and sends the updated list to the inline menu list iframe. * Queries all ciphers for the given url, and sorts them by last used. Will not update the * list of ciphers if the extension is not unlocked. + * + * @param updateAllCipherTypes - Identifies credit card and identity cipher types should also be updated + * @param refocusField - Identifies whether the most recently focused field should be refocused */ - async updateOverlayCiphers(updateAllCipherTypes = true) { + async updateOverlayCiphers(updateAllCipherTypes = true, refocusField = false) { const authStatus = await firstValueFrom(this.authService.activeAccountStatus$); - if (authStatus !== AuthenticationStatus.Unlocked) { - if (this.focusedFieldData) { - this.closeInlineMenuAfterCiphersUpdate().catch((error) => this.logService.error(error)); - } - return; + if (authStatus === AuthenticationStatus.Unlocked) { + this.inlineMenuCiphers = new Map(); + this.updateOverlayCiphers$.next({ updateAllCipherTypes, refocusField }); } + } + /** + * Handles a throttled update of the inline menu ciphers, acting on the emission of a value from + * an observable. Will update on the first and last emissions within a 100ms time frame. + * + * @param updateAllCipherTypes - Identifies credit card and identity cipher types should also be updated + * @param refocusField - Identifies whether the most recently focused field should be refocused + */ + async handleOverlayCiphersUpdate({ + updateAllCipherTypes, + refocusField, + }: UpdateOverlayCiphersParams) { const currentTab = await BrowserApi.getTabFromCurrentWindowId(); + if (this.focusedFieldData && currentTab?.id !== this.focusedFieldData.tabId) { - this.closeInlineMenuAfterCiphersUpdate().catch((error) => this.logService.error(error)); + const focusedFieldTab = await BrowserApi.getTab(this.focusedFieldData.tabId); + this.closeInlineMenu({ tab: focusedFieldTab }, { forceCloseInlineMenu: true }); } if (!currentTab || !currentTab.url?.startsWith("http")) { @@ -300,20 +358,34 @@ export class OverlayBackground implements OverlayBackgroundInterface { } this.inlineMenuFido2Credentials.clear(); - this.storeInlineMenuFido2CredentialsSubject.next(currentTab.id); + this.storeInlineMenuFido2Credentials$.next(currentTab.id); + + await this.generatePassword(); - this.inlineMenuCiphers = new Map(); const ciphersViews = await this.getCipherViews(currentTab, updateAllCipherTypes); for (let cipherIndex = 0; cipherIndex < ciphersViews.length; cipherIndex++) { this.inlineMenuCiphers.set(`inline-menu-cipher-${cipherIndex}`, ciphersViews[cipherIndex]); } - const ciphers = await this.getInlineMenuCipherData(); - this.inlineMenuListPort?.postMessage({ + await this.updateInlineMenuListCiphers(currentTab); + + if (refocusField) { + await BrowserApi.tabSendMessage(currentTab, { command: "focusMostRecentlyFocusedField" }); + } + } + + /** + * Updates the inline menu list's ciphers and sends the updated list to the inline menu list iframe. + * + * @param tab - The current tab + */ + private async updateInlineMenuListCiphers(tab: chrome.tabs.Tab) { + this.postMessageToPort(this.inlineMenuListPort, { command: "updateAutofillInlineMenuListCiphers", - ciphers, - showInlineMenuAccountCreation: this.showInlineMenuAccountCreation(), + ciphers: await this.getInlineMenuCipherData(), + showInlineMenuAccountCreation: this.shouldShowInlineMenuAccountCreation(), showPasskeysLabels: this.showPasskeysLabelsWithinInlineMenu, + focusedFieldHasValue: await this.checkFocusedFieldHasValue(tab), }); } @@ -357,6 +429,11 @@ export class OverlayBackground implements OverlayBackgroundInterface { CipherType.Identity, ]) ).sort((a, b) => this.cipherService.sortCiphersByLastUsedThenName(a, b)); + + if (!this.cardAndIdentityCiphers) { + return cipherViews; + } + for (let cipherIndex = 0; cipherIndex < cipherViews.length; cipherIndex++) { const cipherView = cipherViews[cipherIndex]; if ( @@ -384,7 +461,7 @@ export class OverlayBackground implements OverlayBackgroundInterface { let inlineMenuCipherData: InlineMenuCipherData[]; this.showPasskeysLabelsWithinInlineMenu = false; - if (this.showInlineMenuAccountCreation()) { + if (this.shouldShowInlineMenuAccountCreation()) { inlineMenuCipherData = this.buildInlineMenuAccountCreationCiphers( inlineMenuCiphersArray, true, @@ -476,7 +553,7 @@ export class OverlayBackground implements OverlayBackgroundInterface { for (let cipherIndex = 0; cipherIndex < inlineMenuCiphersArray.length; cipherIndex++) { const [inlineMenuCipherId, cipher] = inlineMenuCiphersArray[cipherIndex]; - if (this.focusedFieldData?.filledByCipherType !== cipher.type) { + if (!this.focusedFieldMatchesFillType(cipher.type)) { continue; } @@ -617,29 +694,66 @@ export class OverlayBackground implements OverlayBackgroundInterface { if ( !showInlineMenuAccountCreation || !this.focusedFieldData?.accountCreationFieldType || - this.focusedFieldData.accountCreationFieldType === "password" + this.focusedFieldMatchesAccountCreationType(InlineMenuAccountCreationFieldType.Password) ) { return { fullName }; } return { fullName, - username: - this.focusedFieldData.accountCreationFieldType === "email" - ? cipher.identity.email - : cipher.identity.username, + username: this.focusedFieldMatchesAccountCreationType( + InlineMenuAccountCreationFieldType.Email, + ) + ? cipher.identity.email + : cipher.identity.username, }; } + /** + * Validates whether the currently focused field has an account + * creation field type that matches the provided field type. + * + * @param fieldType - The field type to validate against + */ + private focusedFieldMatchesAccountCreationType(fieldType: InlineMenuAccountCreationFieldTypes) { + return this.focusedFieldData?.accountCreationFieldType === fieldType; + } + + /** + * Validates whether the most recently focused field has a fill + * type value that matches the provided fill type. + * + * @param fillType - The fill type to validate against + * @param focusedFieldData - Optional focused field data to validate against + */ + private focusedFieldMatchesFillType( + fillType: InlineMenuFillTypes, + focusedFieldData?: FocusedFieldData, + ) { + const focusedFieldFillType = focusedFieldData + ? focusedFieldData.inlineMenuFillType + : this.focusedFieldData?.inlineMenuFillType; + + // When updating the current password for a field, it should fill with a login cipher + if ( + focusedFieldFillType === InlineMenuFillType.CurrentPasswordUpdate && + fillType === CipherType.Login + ) { + return true; + } + + return focusedFieldFillType === fillType; + } + /** * Identifies whether the inline menu is being shown on an account creation field. */ - private showInlineMenuAccountCreation(): boolean { - if (typeof this.focusedFieldData?.showInlineMenuAccountCreation !== "undefined") { - return this.focusedFieldData?.showInlineMenuAccountCreation; + private shouldShowInlineMenuAccountCreation(): boolean { + if (this.focusedFieldMatchesFillType(InlineMenuFillType.AccountCreationUsername)) { + return true; } - if (this.focusedFieldData?.filledByCipherType !== CipherType.Login) { + if (!this.focusedFieldMatchesFillType(CipherType.Login)) { return false; } @@ -692,14 +806,6 @@ export class OverlayBackground implements OverlayBackgroundInterface { return await firstValueFrom(this.domainSettingsService.neverDomains$); } - /** - * Gets the currently focused field and closes the inline menu on that tab. - */ - private async closeInlineMenuAfterCiphersUpdate() { - const focusedFieldTab = await BrowserApi.getTab(this.focusedFieldData.tabId); - this.closeInlineMenu({ tab: focusedFieldTab }, { forceCloseInlineMenu: true }); - } - /** * Handles aggregation of page details for a tab. Stores the page details * in association with the tabId of the tab that sent the message. @@ -864,8 +970,8 @@ export class OverlayBackground implements OverlayBackgroundInterface { * @param sender - The sender of the message */ private async rebuildSubFrameOffsets(sender: chrome.runtime.MessageSender) { - this.cancelUpdateInlineMenuPositionSubject.next(); - this.clearDelayedInlineMenuClosure(); + this.cancelUpdateInlineMenuPosition$.next(); + this.cancelInlineMenuDelayedClose$.next(true); const subFrameOffsetsForTab = this.subFrameOffsetsForTab[sender.tab.id]; if (subFrameOffsetsForTab) { @@ -897,14 +1003,8 @@ export class OverlayBackground implements OverlayBackgroundInterface { ).catch((error) => this.logService.error(error)); } - this.updateInlineMenuPosition({ overlayElement: AutofillOverlayElement.Button }, sender).catch( - (error) => this.logService.error(error), - ); - - const mostRecentlyFocusedFieldHasValue = await BrowserApi.tabSendMessage( - sender.tab, - { command: "checkMostRecentlyFocusedFieldHasValue" }, - { frameId: this.focusedFieldData?.frameId }, + this.updateInlineMenuPosition(sender, AutofillOverlayElement.Button).catch((error) => + this.logService.error(error), ); if ((await this.getInlineMenuVisibility()) === AutofillOverlayVisibility.OnButtonClick) { @@ -912,18 +1012,31 @@ export class OverlayBackground implements OverlayBackgroundInterface { } if ( - mostRecentlyFocusedFieldHasValue && + (await this.checkFocusedFieldHasValue(sender.tab)) && (this.checkIsInlineMenuCiphersPopulated(sender) || (await this.getAuthStatus()) !== AuthenticationStatus.Unlocked) ) { return; } - this.updateInlineMenuPosition({ overlayElement: AutofillOverlayElement.List }, sender).catch( - (error) => this.logService.error(error), + this.updateInlineMenuPosition(sender, AutofillOverlayElement.List).catch((error) => + this.logService.error(error), ); } + /** + * Indicates whether the most recently focused field contains a value. + * + * @param tab - The tab to check the focused field for + */ + private async checkFocusedFieldHasValue(tab: chrome.tabs.Tab) { + return !!(await BrowserApi.tabSendMessage( + tab, + { command: "checkMostRecentlyFocusedFieldHasValue" }, + { frameId: this.focusedFieldData?.frameId || 0 }, + )); + } + /** * Triggers autofill for the selected cipher in the inline menu list. Also places * the selected cipher at the top of the list of ciphers. @@ -936,8 +1049,8 @@ export class OverlayBackground implements OverlayBackgroundInterface { { inlineMenuCipherId, usePasskey }: OverlayPortMessage, { sender }: chrome.runtime.Port, ) { - const pageDetails = this.pageDetailsForTab[sender.tab.id]; - if (!inlineMenuCipherId || !pageDetails?.size) { + const pageDetailsForTab = this.pageDetailsForTab[sender.tab.id]; + if (!inlineMenuCipherId || !pageDetailsForTab?.size) { return; } @@ -956,10 +1069,19 @@ export class OverlayBackground implements OverlayBackgroundInterface { if (await this.autofillService.isPasswordRepromptRequired(cipher, sender.tab)) { return; } + + let pageDetails = Array.from(pageDetailsForTab.values()); + if (this.focusedFieldMatchesFillType(InlineMenuFillType.CurrentPasswordUpdate)) { + pageDetails = this.getFilteredPageDetails( + pageDetails, + this.inlineMenuFieldQualificationService.isUpdateCurrentPasswordField, + ); + } + const totpCode = await this.autofillService.doAutoFill({ tab: sender.tab, - cipher: cipher, - pageDetails: Array.from(pageDetails.values()), + cipher, + pageDetails, fillNewPassword: true, allowTotpAutofill: true, }); @@ -971,6 +1093,30 @@ export class OverlayBackground implements OverlayBackgroundInterface { this.updateLastUsedInlineMenuCipher(inlineMenuCipherId, cipher); } + /** + * Filters the passed page details in order to selectively fill elements based + * on the provided callback. + * + * @param pageDetails - The page details to filter + * @param fieldsFilter - The callback to filter the fields + */ + private getFilteredPageDetails( + pageDetails: PageDetail[], + fieldsFilter: (field: AutofillField) => boolean, + ): PageDetail[] { + let filteredPageDetails: PageDetail[] = structuredClone(pageDetails); + if (!filteredPageDetails?.length) { + return []; + } + + filteredPageDetails = filteredPageDetails.map((pageDetail) => { + pageDetail.details.fields = pageDetail.details.fields.filter(fieldsFilter); + return pageDetail; + }); + + return filteredPageDetails; + } + /** * Triggers a FIDO2 authentication from the inline menu using the passed credential ID. * @@ -1040,21 +1186,32 @@ export class OverlayBackground implements OverlayBackgroundInterface { return; } - this.checkInlineMenuButtonFocused(); + this.checkInlineMenuButtonFocused(sender); } /** * Posts a message to the inline menu button iframe to check if it is focused. + * + * @param sender - The sender of the port message */ - private checkInlineMenuButtonFocused() { - this.inlineMenuButtonPort?.postMessage({ command: "checkAutofillInlineMenuButtonFocused" }); + private checkInlineMenuButtonFocused(sender: chrome.runtime.MessageSender) { + if (!this.inlineMenuButtonPort) { + this.closeInlineMenu(sender, { forceCloseInlineMenu: true }); + return; + } + + this.postMessageToPort(this.inlineMenuButtonPort, { + command: "checkAutofillInlineMenuButtonFocused", + }); } /** * Posts a message to the inline menu list iframe to check if it is focused. */ private checkInlineMenuListFocused() { - this.inlineMenuListPort?.postMessage({ command: "checkAutofillInlineMenuListFocused" }); + this.postMessageToPort(this.inlineMenuListPort, { + command: "checkAutofillInlineMenuListFocused", + }); } /** @@ -1070,12 +1227,15 @@ export class OverlayBackground implements OverlayBackgroundInterface { ) { const command = "closeAutofillInlineMenu"; const sendOptions = { frameId: 0 }; + const updateVisibilityDefaults = { overlayElement, isVisible: false, forceUpdate: true }; + this.generatedPassword = null; + if (forceCloseInlineMenu) { BrowserApi.tabSendMessage(sender.tab, { command, overlayElement }, sendOptions).catch( (error) => this.logService.error(error), ); - this.isInlineMenuButtonVisible = false; - this.isInlineMenuListVisible = false; + this.updateInlineMenuElementIsVisibleStatus(updateVisibilityDefaults, sender); + return; } @@ -1089,26 +1249,17 @@ export class OverlayBackground implements OverlayBackgroundInterface { { command, overlayElement: AutofillOverlayElement.List }, sendOptions, ).catch((error) => this.logService.error(error)); - this.isInlineMenuListVisible = false; + this.updateInlineMenuElementIsVisibleStatus( + Object.assign(updateVisibilityDefaults, { overlayElement: AutofillOverlayElement.List }), + sender, + ); return; } - if (overlayElement === AutofillOverlayElement.Button) { - this.isInlineMenuButtonVisible = false; - } - - if (overlayElement === AutofillOverlayElement.List) { - this.isInlineMenuListVisible = false; - } - - if (!overlayElement) { - this.isInlineMenuButtonVisible = false; - this.isInlineMenuListVisible = false; - } - BrowserApi.tabSendMessage(sender.tab, { command, overlayElement }, sendOptions).catch((error) => this.logService.error(error), ); + this.updateInlineMenuElementIsVisibleStatus(updateVisibilityDefaults, sender); } /** @@ -1116,27 +1267,14 @@ export class OverlayBackground implements OverlayBackgroundInterface { * This is used to ensure that we capture click events on the inline menu in the case * that some on page programmatic method attempts to force focus redirection. */ - private triggerDelayedInlineMenuClosure() { - if (this.isFieldCurrentlyFocused) { + private async triggerDelayedInlineMenuClosure(cancelDelayedClose: boolean = false) { + if (cancelDelayedClose || this.isFieldCurrentlyFocused) { return; } - this.clearDelayedInlineMenuClosure(); - this.delayedCloseTimeout = globalThis.setTimeout(() => { - const message = { command: "triggerDelayedAutofillInlineMenuClosure" }; - this.inlineMenuButtonPort?.postMessage(message); - this.inlineMenuListPort?.postMessage(message); - }, 100); - } - - /** - * Clears the delayed closure timeout for the inline menu, effectively - * cancelling the event from occurring. - */ - private clearDelayedInlineMenuClosure() { - if (this.delayedCloseTimeout) { - clearTimeout(this.delayedCloseTimeout); - } + const message = { command: "triggerDelayedAutofillInlineMenuClosure" }; + this.postMessageToPort(this.inlineMenuButtonPort, message); + this.postMessageToPort(this.inlineMenuListPort, message); } /** @@ -1160,6 +1298,8 @@ export class OverlayBackground implements OverlayBackgroundInterface { if (overlayElement === AutofillOverlayElement.Button) { this.inlineMenuButtonPort?.disconnect(); this.inlineMenuButtonPort = null; + this.inlineMenuButtonMessageConnectorPort?.disconnect(); + this.inlineMenuButtonMessageConnectorPort = null; this.isInlineMenuButtonVisible = false; return; @@ -1167,6 +1307,8 @@ export class OverlayBackground implements OverlayBackgroundInterface { this.inlineMenuListPort?.disconnect(); this.inlineMenuListPort = null; + this.inlineMenuListMessageConnectorPort?.disconnect(); + this.inlineMenuListMessageConnectorPort = null; this.isInlineMenuListVisible = false; } @@ -1174,12 +1316,12 @@ export class OverlayBackground implements OverlayBackgroundInterface { * Updates the position of either the inline menu list or button. The position * is based on the focused field's position and dimensions. * - * @param overlayElement - The overlay element to update, either the list or button * @param sender - The sender of the port message + * @param overlayElement - The overlay element to update, either the list or button */ private async updateInlineMenuPosition( - { overlayElement }: { overlayElement?: string }, sender: chrome.runtime.MessageSender, + overlayElement?: string, ) { if (!overlayElement || !this.senderTabHasFocusedField(sender)) { return; @@ -1193,32 +1335,32 @@ export class OverlayBackground implements OverlayBackgroundInterface { { frameId: 0 }, ); - const subFrameOffsetsForTab = this.subFrameOffsetsForTab[this.focusedFieldData.tabId]; + const subFrameOffsetsForTab = this.subFrameOffsetsForTab[this.focusedFieldData?.tabId]; let subFrameOffsets: SubFrameOffsetData; if (subFrameOffsetsForTab) { subFrameOffsets = subFrameOffsetsForTab.get(this.focusedFieldData.frameId); if (subFrameOffsets === null) { - this.rebuildSubFrameOffsetsSubject.next(sender); - this.startUpdateInlineMenuPositionSubject.next(sender); + this.rebuildSubFrameOffsets$.next(sender); + this.startUpdateInlineMenuPosition$.next(sender); return; } } if (overlayElement === AutofillOverlayElement.Button) { - this.inlineMenuButtonPort?.postMessage({ + this.postMessageToPort(this.inlineMenuButtonPort, { command: "updateAutofillInlineMenuPosition", styles: this.getInlineMenuButtonPosition(subFrameOffsets), }); - this.startInlineMenuFadeIn(); + this.startInlineMenuFadeIn$.next(); return; } - this.inlineMenuListPort?.postMessage({ + this.postMessageToPort(this.inlineMenuListPort, { command: "updateAutofillInlineMenuPosition", styles: this.getInlineMenuListPosition(subFrameOffsets), }); - this.startInlineMenuFadeIn(); + this.startInlineMenuFadeIn$.next(); } /** @@ -1229,20 +1371,18 @@ export class OverlayBackground implements OverlayBackgroundInterface { * @param sender - The sender of the port message */ private updateInlineMenuElementIsVisibleStatus( - message: OverlayBackgroundExtensionMessage, + { overlayElement, isVisible, forceUpdate }: UpdateInlineMenuVisibilityMessage, sender: chrome.runtime.MessageSender, ) { - if (!this.senderTabHasFocusedField(sender)) { + if (!forceUpdate && !this.senderTabHasFocusedField(sender)) { return; } - const { overlayElement, isVisible } = message; - if (overlayElement === AutofillOverlayElement.Button) { + if (!overlayElement || overlayElement === AutofillOverlayElement.Button) { this.isInlineMenuButtonVisible = isVisible; - return; } - if (overlayElement === AutofillOverlayElement.List) { + if (!overlayElement || overlayElement === AutofillOverlayElement.List) { this.isInlineMenuListVisible = isVisible; } } @@ -1254,22 +1394,6 @@ export class OverlayBackground implements OverlayBackgroundInterface { return this.inlineMenuPosition; } - /** - * Handles updating the opacity of both the inline menu button and list. - * This is used to simultaneously fade in the inline menu elements. - */ - private startInlineMenuFadeIn() { - this.cancelInlineMenuFadeIn(); - this.startInlineMenuFadeInSubject.next(); - } - - /** - * Clears the timeout used to fade in the inline menu elements. - */ - private cancelInlineMenuFadeIn() { - this.cancelInlineMenuFadeInSubject.next(true); - } - /** * Posts a message to the inline menu elements to trigger a fade in of the inline menu. * @@ -1281,8 +1405,8 @@ export class OverlayBackground implements OverlayBackgroundInterface { } const message = { command: "fadeInAutofillInlineMenuIframe" }; - this.inlineMenuButtonPort?.postMessage(message); - this.inlineMenuListPort?.postMessage(message); + this.postMessageToPort(this.inlineMenuButtonPort, message); + this.postMessageToPort(this.inlineMenuListPort, message); } /** @@ -1359,7 +1483,11 @@ export class OverlayBackground implements OverlayBackgroundInterface { { focusedFieldData }: OverlayBackgroundExtensionMessage, sender: chrome.runtime.MessageSender, ) { - if (this.focusedFieldData && !this.senderFrameHasFocusedField(sender)) { + if ( + this.focusedFieldData && + this.senderTabHasFocusedField(sender) && + !this.senderFrameHasFocusedField(sender) + ) { BrowserApi.tabSendMessage( sender.tab, { command: "unsetMostRecentlyFocusedField" }, @@ -1371,31 +1499,76 @@ export class OverlayBackground implements OverlayBackgroundInterface { this.focusedFieldData = { ...focusedFieldData, tabId: sender.tab.id, frameId: sender.frameId }; this.isFieldCurrentlyFocused = true; - const accountCreationFieldBlurred = - previousFocusedFieldData?.showInlineMenuAccountCreation && - !this.focusedFieldData.showInlineMenuAccountCreation; - - if (accountCreationFieldBlurred || this.showInlineMenuAccountCreation()) { - this.updateIdentityCiphersOnLoginField(previousFocusedFieldData).catch((error) => + if (this.shouldUpdatePasswordGeneratorMenuOnFieldFocus()) { + this.updateInlineMenuGeneratedPasswordOnFocus(sender.tab).catch((error) => this.logService.error(error), ); return; } - if (previousFocusedFieldData?.filledByCipherType !== focusedFieldData?.filledByCipherType) { - const updateAllCipherTypes = focusedFieldData.filledByCipherType !== CipherType.Login; + if (this.shouldUpdateAccountCreationMenuOnFieldFocus(previousFocusedFieldData)) { + this.updateInlineMenuAccountCreationDataOnFocus(previousFocusedFieldData, sender).catch( + (error) => this.logService.error(error), + ); + return; + } + + if ( + !this.focusedFieldMatchesFillType( + focusedFieldData?.inlineMenuFillType, + previousFocusedFieldData, + ) + ) { + const updateAllCipherTypes = !this.focusedFieldMatchesFillType( + CipherType.Login, + focusedFieldData, + ); this.updateOverlayCiphers(updateAllCipherTypes).catch((error) => this.logService.error(error), ); } } + /** + * Identifies if a recently focused field should update as a password generation field. + */ + private shouldUpdatePasswordGeneratorMenuOnFieldFocus() { + return ( + this.isInlineMenuButtonVisible && + this.focusedFieldMatchesFillType(InlineMenuFillType.PasswordGeneration) + ); + } + + /** + * Handles updating the inline menu password generator on focus of a field. + * In the case that the field has a value, will show the save login view. + * + * @param tab - The tab that the field is focused within + */ + private async updateInlineMenuGeneratedPasswordOnFocus(tab: chrome.tabs.Tab) { + if (await this.shouldShowSaveLoginInlineMenuList(tab)) { + this.showSaveLoginInlineMenuList(); + return; + } + + await this.updateGeneratedPassword(); + } + /** * Triggers an update of populated identity ciphers when a login field is focused. * * @param previousFocusedFieldData - The data set of the previously focused field + * @param sender - The sender of the extension message */ - private async updateIdentityCiphersOnLoginField(previousFocusedFieldData: FocusedFieldData) { + private async updateInlineMenuAccountCreationDataOnFocus( + previousFocusedFieldData: FocusedFieldData, + sender: chrome.runtime.MessageSender, + ) { + if (await this.shouldShowSaveLoginInlineMenuList(sender.tab)) { + this.showSaveLoginInlineMenuList(); + return; + } + if ( !previousFocusedFieldData || !this.isInlineMenuButtonVisible || @@ -1404,14 +1577,158 @@ export class OverlayBackground implements OverlayBackgroundInterface { return; } - this.inlineMenuListPort?.postMessage({ - command: "updateAutofillInlineMenuListCiphers", - ciphers: await this.getInlineMenuCipherData(), - showInlineMenuAccountCreation: this.showInlineMenuAccountCreation(), - showPasskeysLabels: this.showPasskeysLabelsWithinInlineMenu, + if ( + this.focusedFieldMatchesFillType(CipherType.Login) && + this.focusedFieldMatchesAccountCreationType(InlineMenuAccountCreationFieldType.Password) + ) { + await this.updateGeneratedPassword(); + return; + } + + await this.updateInlineMenuListCiphers(sender.tab); + } + + /** + * Identifies whether a newly focused field should trigger an update that + * displays the account creation view within the inline menu. + * + * @param previousFocusedFieldData - The data set of the previously focused field + */ + private shouldUpdateAccountCreationMenuOnFieldFocus(previousFocusedFieldData: FocusedFieldData) { + const accountCreationFieldBlurred = + this.focusedFieldMatchesFillType( + InlineMenuFillType.AccountCreationUsername, + previousFocusedFieldData, + ) && !this.focusedFieldMatchesFillType(InlineMenuFillType.AccountCreationUsername); + return accountCreationFieldBlurred || this.shouldShowInlineMenuAccountCreation(); + } + + /** + * Sends a message to the list to show the save login inline menu list view. This view + * is shown after a field is filled with a generated password. + */ + private showSaveLoginInlineMenuList() { + this.postMessageToPort(this.inlineMenuListPort, { command: "showSaveLoginInlineMenuList" }); + } + + /** + * Generates a password based on the user defined password generation options. + */ + private async generatePassword(): Promise { + this.generatedPassword = await this.generatePasswordCallback(); + await this.addPasswordCallback(this.generatedPassword); + } + + /** + * Updates the generated password in the inline menu list. + * + * @param refreshPassword - Identifies whether the generated password should be refreshed + */ + private async updateGeneratedPassword(refreshPassword: boolean = false) { + if (!this.generatedPassword || refreshPassword) { + await this.generatePassword(); + } + + this.postMessageToPort(this.inlineMenuListPort, { + command: "updateAutofillInlineMenuGeneratedPassword", + generatedPassword: this.generatedPassword, + refreshPassword, }); } + /** + * Triggers a fill of the generated password into the current tab. Will trigger + * a focus of the last focused field after filling the password. + * + * @param port - The port of the sender + */ + private async fillGeneratedPassword(port: chrome.runtime.Port) { + if (!this.generatedPassword) { + return; + } + + const pageDetailsForTab = this.pageDetailsForTab[port.sender.tab.id]; + if (!pageDetailsForTab) { + return; + } + + let pageDetails: PageDetail[] = Array.from(pageDetailsForTab.values()); + if (!pageDetails.length) { + return; + } + + // If our currently focused field is for a login form, we want to fill the current password field. + // Otherwise, map over all page details and filter out fields that are not new password fields. + if (!this.focusedFieldMatchesFillType(CipherType.Login)) { + pageDetails = this.getFilteredPageDetails( + pageDetails, + this.inlineMenuFieldQualificationService.isNewPasswordField, + ); + } + + const cipher = this.buildLoginCipherView({ + username: "", + password: this.generatedPassword, + hostname: "", + uri: "", + }); + + await this.autofillService.doAutoFill({ + tab: port.sender.tab, + cipher, + pageDetails, + fillNewPassword: true, + allowTotpAutofill: false, + }); + + globalThis.setTimeout(async () => { + if (await this.shouldShowSaveLoginInlineMenuList(port.sender.tab)) { + await this.openInlineMenu(port.sender, true); + } + }, 300); + } + + /** + * Verifies whether the save login inline menu view should be shown. This requires that + * the login data on the page contains a username and either a current or new password. + * + * @param tab - The tab to check for login data + */ + private async shouldShowSaveLoginInlineMenuList(tab: chrome.tabs.Tab) { + if (this.focusedFieldData?.tabId !== tab.id) { + return false; + } + + const loginData = await this.getInlineMenuFormFieldData(tab); + if (!loginData) { + return false; + } + + return ( + (this.shouldShowInlineMenuAccountCreation() || + this.focusedFieldMatchesFillType(InlineMenuFillType.PasswordGeneration)) && + !!(loginData.username && (loginData.password || loginData.newPassword)) + ); + } + + /** + * Gets the inline menu form field data from the provided tab. + * + * @param tab - The tab to get the form field data from + */ + private async getInlineMenuFormFieldData(tab: chrome.tabs.Tab): Promise { + return await BrowserApi.tabSendMessage( + tab, + { + command: "getInlineMenuFormFieldData", + ignoreFieldFocus: true, + }, + { + frameId: this.focusedFieldData.frameId || 0, + }, + ); + } + /** * Updates the inline menu's visibility based on the display property passed in the extension message. * @@ -1426,7 +1743,7 @@ export class OverlayBackground implements OverlayBackgroundInterface { return; } - this.cancelInlineMenuFadeIn(); + this.cancelInlineMenuFadeIn$.next(true); const display = isInlineMenuHidden ? "none" : "block"; let styles: { display: string; opacity?: string } = { display }; @@ -1437,45 +1754,94 @@ export class OverlayBackground implements OverlayBackgroundInterface { const portMessage = { command: "toggleAutofillInlineMenuHidden", styles }; if (this.inlineMenuButtonPort) { - this.isInlineMenuButtonVisible = !isInlineMenuHidden; - this.inlineMenuButtonPort.postMessage(portMessage); + this.updateInlineMenuElementIsVisibleStatus( + { overlayElement: AutofillOverlayElement.Button, isVisible: !isInlineMenuHidden }, + sender, + ); + this.postMessageToPort(this.inlineMenuButtonPort, portMessage); } if (this.inlineMenuListPort) { this.isInlineMenuListVisible = !isInlineMenuHidden; - this.inlineMenuListPort.postMessage(portMessage); + this.updateInlineMenuElementIsVisibleStatus( + { overlayElement: AutofillOverlayElement.List, isVisible: !isInlineMenuHidden }, + sender, + ); + this.postMessageToPort(this.inlineMenuListPort, portMessage); } if (setTransparentInlineMenu) { - this.startInlineMenuFadeIn(); + this.startInlineMenuFadeIn$.next(); } } /** * Sends a message to the currently active tab to open the autofill inline menu. * - * @param isFocusingFieldElement - Identifies whether the field element should be focused when the inline menu is opened + * @param sender - The sender of the port message * @param isOpeningFullInlineMenu - Identifies whether the full inline menu should be forced open regardless of other states */ - private async openInlineMenu(isFocusingFieldElement = false, isOpeningFullInlineMenu = false) { - this.clearDelayedInlineMenuClosure(); - const currentTab = await BrowserApi.getTabFromCurrentWindowId(); - if (!currentTab) { + private async openInlineMenu( + sender: chrome.runtime.MessageSender, + isOpeningFullInlineMenu = false, + ) { + this.cancelInlineMenuDelayedClose$.next(true); + + if (isOpeningFullInlineMenu) { + await this.updateInlineMenuPosition(sender, AutofillOverlayElement.Button); + await this.updateInlineMenuPosition(sender, AutofillOverlayElement.List); return; } - await BrowserApi.tabSendMessage( - currentTab, - { - command: "openAutofillInlineMenu", - isFocusingFieldElement, - isOpeningFullInlineMenu, - authStatus: await this.getAuthStatus(), - }, - { - frameId: this.focusedFieldData?.tabId === currentTab.id ? this.focusedFieldData.frameId : 0, - }, - ); + if (!(await this.checkFocusedFieldHasValue(sender.tab))) { + await this.openInlineMenuOnEmptyField(sender); + return; + } + + await this.openInlineMenuOnFilledField(sender); + } + + /** + * Triggers logic that handles opening the inline menu on an empty form field. + * + * @param sender - The sender of the port message + */ + private async openInlineMenuOnEmptyField(sender: chrome.runtime.MessageSender) { + if ((await this.getInlineMenuVisibility()) === AutofillOverlayVisibility.OnFieldFocus) { + await this.updateInlineMenuPosition(sender, AutofillOverlayElement.Button); + await this.updateInlineMenuPosition(sender, AutofillOverlayElement.List); + + return; + } + + if (this.isInlineMenuListVisible) { + this.closeInlineMenu(sender, { + forceCloseInlineMenu: true, + overlayElement: AutofillOverlayElement.List, + }); + } + await this.updateInlineMenuPosition(sender, AutofillOverlayElement.Button); + } + + /** + * Triggers logic that handles opening the inline menu on a form field that has a value. + * + * @param sender - The sender of the port message + */ + private async openInlineMenuOnFilledField(sender: chrome.runtime.MessageSender) { + if (await this.shouldShowSaveLoginInlineMenuList(sender.tab)) { + await this.updateInlineMenuPosition(sender, AutofillOverlayElement.Button); + await this.updateInlineMenuPosition(sender, AutofillOverlayElement.List); + return; + } + + if (this.isInlineMenuListVisible) { + this.closeInlineMenu(sender, { + forceCloseInlineMenu: true, + overlayElement: AutofillOverlayElement.List, + }); + } + await this.updateInlineMenuPosition(sender, AutofillOverlayElement.Button); } /** @@ -1510,7 +1876,7 @@ export class OverlayBackground implements OverlayBackgroundInterface { * Sends a message to the inline menu button to update its authentication status. */ private async updateInlineMenuButtonAuthStatus() { - this.inlineMenuButtonPort?.postMessage({ + this.postMessageToPort(this.inlineMenuButtonPort, { command: "updateInlineMenuButtonAuthStatus", authStatus: await this.getAuthStatus(), }); @@ -1524,7 +1890,7 @@ export class OverlayBackground implements OverlayBackgroundInterface { * @param port - The port of the inline menu button */ private async handleInlineMenuButtonClicked(port: chrome.runtime.Port) { - this.clearDelayedInlineMenuClosure(); + this.cancelInlineMenuDelayedClose$.next(true); this.cancelInlineMenuFadeInAndPositionUpdate(); if ((await this.getAuthStatus()) !== AuthenticationStatus.Unlocked) { @@ -1532,7 +1898,7 @@ export class OverlayBackground implements OverlayBackgroundInterface { return; } - await this.openInlineMenu(false, true); + await this.openInlineMenu(port.sender, true); } /** @@ -1543,7 +1909,7 @@ export class OverlayBackground implements OverlayBackgroundInterface { private async unlockVault(port: chrome.runtime.Port) { const { sender } = port; - this.closeInlineMenu(port.sender); + this.closeInlineMenu(port.sender, { forceCloseInlineMenu: true }); const retryMessage: LockedVaultPendingNotificationsData = { commandToRetry: { message: { command: "openAutofillInlineMenu" }, sender }, target: "overlay.background", @@ -1582,7 +1948,7 @@ export class OverlayBackground implements OverlayBackgroundInterface { * Facilitates redirecting focus to the inline menu list. */ private focusInlineMenuList() { - this.inlineMenuListPort?.postMessage({ command: "focusAutofillInlineMenuList" }); + this.postMessageToPort(this.inlineMenuListPort, { command: "focusAutofillInlineMenuList" }); } /** @@ -1593,11 +1959,10 @@ export class OverlayBackground implements OverlayBackgroundInterface { */ private async unlockCompleted(message: OverlayBackgroundExtensionMessage) { await this.updateInlineMenuButtonAuthStatus(); - await this.updateOverlayCiphers(); - if (message.data?.commandToRetry?.message?.command === "openAutofillInlineMenu") { - await this.openInlineMenu(true); - } + const openInlineMenu = + message.data?.commandToRetry?.message?.command === "openAutofillInlineMenu"; + await this.updateOverlayCiphers(true, openInlineMenu); } /** @@ -1605,33 +1970,45 @@ export class OverlayBackground implements OverlayBackgroundInterface { */ private getInlineMenuTranslations() { if (!this.inlineMenuPageTranslations) { - this.inlineMenuPageTranslations = { - locale: BrowserApi.getUILanguage(), - opensInANewWindow: this.i18nService.translate("opensInANewWindow"), - buttonPageTitle: this.i18nService.translate("bitwardenOverlayButton"), - toggleBitwardenVaultOverlay: this.i18nService.translate("toggleBitwardenVaultOverlay"), - listPageTitle: this.i18nService.translate("bitwardenVault"), - unlockYourAccount: this.i18nService.translate("unlockYourAccountToViewAutofillSuggestions"), - unlockAccount: this.i18nService.translate("unlockAccount"), - unlockAccountAria: this.i18nService.translate("unlockAccountAria"), - fillCredentialsFor: this.i18nService.translate("fillCredentialsFor"), - username: this.i18nService.translate("username")?.toLowerCase(), - view: this.i18nService.translate("view"), - noItemsToShow: this.i18nService.translate("noItemsToShow"), - newItem: this.i18nService.translate("newItem"), - addNewVaultItem: this.i18nService.translate("addNewVaultItem"), - newLogin: this.i18nService.translate("newLogin"), - addNewLoginItem: this.i18nService.translate("addNewLoginItemAria"), - newCard: this.i18nService.translate("newCard"), - addNewCardItem: this.i18nService.translate("addNewCardItemAria"), - newIdentity: this.i18nService.translate("newIdentity"), - addNewIdentityItem: this.i18nService.translate("addNewIdentityItemAria"), - cardNumberEndsWith: this.i18nService.translate("cardNumberEndsWith"), - passkeys: this.i18nService.translate("passkeys"), - passwords: this.i18nService.translate("passwords"), - logInWithPasskey: this.i18nService.translate("logInWithPasskeyAriaLabel"), - authenticating: this.i18nService.translate("authenticating"), - }; + const translationKeys = [ + "opensInANewWindow", + "toggleBitwardenVaultOverlay", + "unlockYourAccountToViewAutofillSuggestions", + "unlockAccount", + "unlockAccountAria", + "fillCredentialsFor", + "username", + "view", + "noItemsToShow", + "newItem", + "addNewVaultItem", + "newLogin", + "addNewLoginItemAria", + "newCard", + "addNewCardItemAria", + "newIdentity", + "addNewIdentityItemAria", + "cardNumberEndsWith", + "passkeys", + "passwords", + "logInWithPasskeyAriaLabel", + "authenticating", + "fillGeneratedPassword", + "regeneratePassword", + "passwordRegenerated", + "saveLoginToBitwarden", + "lowercaseAriaLabel", + "uppercaseAriaLabel", + "generatedPassword", + ...Object.values(specialCharacterToKeyMap), + ]; + this.inlineMenuPageTranslations = translationKeys.reduce( + (acc: Record, key) => { + acc[key] = this.i18nService.translate(key); + return acc; + }, + {}, + ); } return this.inlineMenuPageTranslations; @@ -1714,7 +2091,7 @@ export class OverlayBackground implements OverlayBackgroundInterface { this.updateCurrentAddNewItemIdentity(identity); } - this.addNewVaultItemSubject.next(this.currentAddNewItemData); + this.addNewVaultItem$.next(this.currentAddNewItemData); } /** @@ -2101,7 +2478,7 @@ export class OverlayBackground implements OverlayBackgroundInterface { * the same value as the page's meta "color-scheme" value. */ private updateInlineMenuButtonColorScheme() { - this.inlineMenuButtonPort?.postMessage({ + this.postMessageToPort(this.inlineMenuButtonPort, { command: "updateAutofillInlineMenuColorScheme", }); } @@ -2117,7 +2494,7 @@ export class OverlayBackground implements OverlayBackgroundInterface { this.inlineMenuPosition.list.height = parsedHeight; } - this.inlineMenuListPort?.postMessage({ + this.postMessageToPort(this.inlineMenuListPort, { command: "updateAutofillInlineMenuPosition", styles: message.styles, }); @@ -2165,7 +2542,12 @@ export class OverlayBackground implements OverlayBackgroundInterface { * @param sender - The sender of the message */ private senderFrameHasFocusedField(sender: chrome.runtime.MessageSender) { - return sender.frameId === this.focusedFieldData?.frameId; + if (!this.focusedFieldData) { + return false; + } + + const { tabId, frameId } = this.focusedFieldData; + return sender.tab.id === tabId && sender.frameId === frameId; } /** @@ -2184,7 +2566,7 @@ export class OverlayBackground implements OverlayBackgroundInterface { this.toggleInlineMenuHidden({ isInlineMenuHidden: true }, sender).catch((error) => this.logService.error(error), ); - this.repositionInlineMenuSubject.next(sender); + this.repositionInlineMenu$.next(sender); } /** @@ -2208,8 +2590,8 @@ export class OverlayBackground implements OverlayBackgroundInterface { */ private async triggerSubFrameFocusInRebuild(sender: chrome.runtime.MessageSender) { this.cancelInlineMenuFadeInAndPositionUpdate(); - this.rebuildSubFrameOffsetsSubject.next(sender); - this.repositionInlineMenuSubject.next(sender); + this.rebuildSubFrameOffsets$.next(sender); + this.repositionInlineMenu$.next(sender); } /** @@ -2228,25 +2610,25 @@ export class OverlayBackground implements OverlayBackgroundInterface { const isFieldWithinViewport = await BrowserApi.tabSendMessage( sender.tab, { command: "checkIsMostRecentlyFocusedFieldWithinViewport" }, - { frameId: this.focusedFieldData.frameId }, + { frameId: this.focusedFieldData?.frameId }, ); if (!isFieldWithinViewport) { await this.closeInlineMenuAfterReposition(sender); return; } - if (this.focusedFieldData.frameId > 0) { - this.rebuildSubFrameOffsetsSubject.next(sender); + if (this.focusedFieldData?.frameId > 0) { + this.rebuildSubFrameOffsets$.next(sender); } - this.startUpdateInlineMenuPositionSubject.next(sender); + this.startUpdateInlineMenuPosition$.next(sender); }; /** * Triggers a closure of the inline menu during a reposition event. * * @param sender - The sender of the message -| */ + */ private async closeInlineMenuAfterReposition(sender: chrome.runtime.MessageSender) { await this.toggleInlineMenuHidden( { isInlineMenuHidden: false, setTransparentInlineMenu: true }, @@ -2259,8 +2641,8 @@ export class OverlayBackground implements OverlayBackgroundInterface { * Cancels the observables that update the position and fade in of the inline menu. */ private cancelInlineMenuFadeInAndPositionUpdate() { - this.cancelInlineMenuFadeIn(); - this.cancelUpdateInlineMenuPositionSubject.next(); + this.cancelInlineMenuFadeIn$.next(true); + this.cancelUpdateInlineMenuPosition$.next(); } /** @@ -2330,14 +2712,13 @@ export class OverlayBackground implements OverlayBackgroundInterface { * @param port - The port that connected to the extension background */ private handlePortOnConnect = async (port: chrome.runtime.Port) => { - const isInlineMenuListMessageConnector = port.name === AutofillOverlayPort.ListMessageConnector; - const isInlineMenuButtonMessageConnector = - port.name === AutofillOverlayPort.ButtonMessageConnector; - if (isInlineMenuListMessageConnector || isInlineMenuButtonMessageConnector) { - port.onMessage.addListener(this.handleOverlayElementPortMessage); + if (!this.validPortConnections.has(port.name)) { return; } + this.storeOverlayPort(port); + port.onMessage.addListener(this.handleOverlayElementPortMessage); + const isInlineMenuListPort = port.name === AutofillOverlayPort.List; const isInlineMenuButtonPort = port.name === AutofillOverlayPort.Button; if (!isInlineMenuListPort && !isInlineMenuButtonPort) { @@ -2348,10 +2729,20 @@ export class OverlayBackground implements OverlayBackgroundInterface { this.portKeyForTab[port.sender.tab.id] = generateRandomChars(12); } - this.storeOverlayPort(port); port.onDisconnect.addListener(this.handlePortOnDisconnect); - port.onMessage.addListener(this.handleOverlayElementPortMessage); - port.postMessage({ + + const authStatus = await this.getAuthStatus(); + const showInlineMenuAccountCreation = this.shouldShowInlineMenuAccountCreation(); + const showInlineMenuPasswordGenerator = await this.shouldInitInlineMenuPasswordGenerator( + authStatus, + isInlineMenuListPort, + showInlineMenuAccountCreation, + ); + const showSaveLoginMenu = + (await this.checkFocusedFieldHasValue(port.sender.tab)) && + (await this.shouldShowSaveLoginInlineMenuList(port.sender.tab)); + + this.postMessageToPort(port, { command: `initAutofillInlineMenu${isInlineMenuListPort ? "List" : "Button"}`, iframeUrl: chrome.runtime.getURL( `overlay/menu-${isInlineMenuListPort ? "list" : "button"}.html`, @@ -2359,7 +2750,6 @@ export class OverlayBackground implements OverlayBackgroundInterface { pageTitle: chrome.i18n.getMessage( isInlineMenuListPort ? "bitwardenVault" : "bitwardenOverlayButton", ), - authStatus: await this.getAuthStatus(), styleSheetUrl: chrome.runtime.getURL( `overlay/menu-${isInlineMenuListPort ? "list" : "button"}.css`, ), @@ -2370,25 +2760,42 @@ export class OverlayBackground implements OverlayBackgroundInterface { portName: isInlineMenuListPort ? AutofillOverlayPort.ListMessageConnector : AutofillOverlayPort.ButtonMessageConnector, - filledByCipherType: this.focusedFieldData?.filledByCipherType, - showInlineMenuAccountCreation: this.showInlineMenuAccountCreation(), + inlineMenuFillType: this.focusedFieldData?.inlineMenuFillType, showPasskeysLabels: this.showPasskeysLabelsWithinInlineMenu, + generatedPassword: showInlineMenuPasswordGenerator ? this.generatedPassword : null, + showSaveLoginMenu, + showInlineMenuAccountCreation, + authStatus, }); this.updateInlineMenuPosition( - { - overlayElement: isInlineMenuListPort - ? AutofillOverlayElement.List - : AutofillOverlayElement.Button, - }, port.sender, + isInlineMenuListPort ? AutofillOverlayElement.List : AutofillOverlayElement.Button, ).catch((error) => this.logService.error(error)); }; + /** + * Wraps the port.postMessage method to handle any errors that may occur. + * + * @param port - The port to send the message to + * @param message - The message to send to the port + */ + private postMessageToPort = (port: chrome.runtime.Port, message: Record) => { + if (!port) { + return; + } + + try { + port.postMessage(message); + } catch { + // Catch when the port.postMessage call triggers an error to ensure login execution continues. + } + }; + /** * Stores the connected overlay port and sets up any existing ports to be disconnected. * * @param port - The port to store -| */ + */ private storeOverlayPort(port: chrome.runtime.Port) { if (port.name === AutofillOverlayPort.List) { this.storeExpiredOverlayPort(this.inlineMenuListPort); @@ -2399,6 +2806,19 @@ export class OverlayBackground implements OverlayBackgroundInterface { if (port.name === AutofillOverlayPort.Button) { this.storeExpiredOverlayPort(this.inlineMenuButtonPort); this.inlineMenuButtonPort = port; + return; + } + + if (port.name === AutofillOverlayPort.ButtonMessageConnector) { + this.storeExpiredOverlayPort(this.inlineMenuButtonMessageConnectorPort); + this.inlineMenuButtonMessageConnectorPort = port; + return; + } + + if (port.name === AutofillOverlayPort.ListMessageConnector) { + this.storeExpiredOverlayPort(this.inlineMenuListMessageConnectorPort); + this.inlineMenuListMessageConnectorPort = port; + return; } } @@ -2415,6 +2835,38 @@ export class OverlayBackground implements OverlayBackgroundInterface { } } + /** + * Identifies if the focused field should show the inline menu + * password generator when the inline menu is opened. + * + * @param authStatus - The current authentication status + * @param isInlineMenuListPort - Identifies if the port is for the inline menu list + * @param showInlineMenuAccountCreation - Identifies if the inline menu account creation should be shown + */ + private async shouldInitInlineMenuPasswordGenerator( + authStatus: AuthenticationStatus, + isInlineMenuListPort: boolean, + showInlineMenuAccountCreation: boolean, + ) { + if (!isInlineMenuListPort || authStatus !== AuthenticationStatus.Unlocked) { + return false; + } + + const focusFieldShouldShowPasswordGenerator = + this.focusedFieldMatchesFillType(InlineMenuFillType.PasswordGeneration) || + (showInlineMenuAccountCreation && + this.focusedFieldMatchesAccountCreationType(InlineMenuAccountCreationFieldType.Password)); + if (!focusFieldShouldShowPasswordGenerator) { + return false; + } + + if (!this.generatedPassword) { + await this.generatePassword(); + } + + return true; + } + /** * Handles messages sent to the overlay list or button ports. * @@ -2455,15 +2907,27 @@ export class OverlayBackground implements OverlayBackgroundInterface { * @param port - The port that was disconnected */ private handlePortOnDisconnect = (port: chrome.runtime.Port) => { + const updateVisibilityDefaults = { isVisible: false, forceUpdate: true }; + if (port.name === AutofillOverlayPort.List) { this.inlineMenuListPort = null; - this.isInlineMenuListVisible = false; + this.inlineMenuListMessageConnectorPort?.disconnect(); + this.inlineMenuListMessageConnectorPort = null; + this.updateInlineMenuElementIsVisibleStatus( + Object.assign(updateVisibilityDefaults, { overlayElement: AutofillOverlayElement.List }), + port.sender, + ); this.inlineMenuPosition.list = null; } if (port.name === AutofillOverlayPort.Button) { this.inlineMenuButtonPort = null; - this.isInlineMenuButtonVisible = false; + this.inlineMenuButtonMessageConnectorPort?.disconnect(); + this.inlineMenuButtonMessageConnectorPort = null; + this.updateInlineMenuElementIsVisibleStatus( + Object.assign(updateVisibilityDefaults, { overlayElement: AutofillOverlayElement.List }), + port.sender, + ); this.inlineMenuPosition.button = null; } }; diff --git a/apps/browser/src/autofill/background/tabs.background.ts b/apps/browser/src/autofill/background/tabs.background.ts index 0513220c277..ae57bd51cea 100644 --- a/apps/browser/src/autofill/background/tabs.background.ts +++ b/apps/browser/src/autofill/background/tabs.background.ts @@ -92,7 +92,7 @@ export default class TabsBackground { FeatureFlag.InlineMenuPositioningImprovements, ); const removePageDetailsStatus = new Set(["loading", "unloaded"]); - if (!!overlayImprovementsFlag && removePageDetailsStatus.has(changeInfo.status)) { + if (!overlayImprovementsFlag && removePageDetailsStatus.has(changeInfo.status)) { this.overlayBackground.removePageDetails(tabId); } diff --git a/apps/browser/src/autofill/content/abstractions/autofill-init.ts b/apps/browser/src/autofill/content/abstractions/autofill-init.ts index 529607949db..8a9c97e67dd 100644 --- a/apps/browser/src/autofill/content/abstractions/autofill-init.ts +++ b/apps/browser/src/autofill/content/abstractions/autofill-init.ts @@ -1,5 +1,4 @@ import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status"; -import { InlineMenuVisibilitySetting } from "@bitwarden/common/autofill/types"; import { CipherType } from "@bitwarden/common/vault/enums"; import { AutofillOverlayElementType } from "../../enums/autofill-overlay.enum"; @@ -21,10 +20,10 @@ export type AutofillExtensionMessage = { authStatus?: AuthenticationStatus; isOpeningFullInlineMenu?: boolean; addNewCipherType?: CipherType; + ignoreFieldFocus?: boolean; data?: { direction?: "previous" | "next" | "current"; forceCloseInlineMenu?: boolean; - newSettingValue?: InlineMenuVisibilitySetting; }; }; diff --git a/apps/browser/src/autofill/content/autofill-init.spec.ts b/apps/browser/src/autofill/content/autofill-init.spec.ts index b98d297d136..d612e63f82c 100644 --- a/apps/browser/src/autofill/content/autofill-init.spec.ts +++ b/apps/browser/src/autofill/content/autofill-init.spec.ts @@ -4,6 +4,7 @@ import AutofillPageDetails from "../models/autofill-page-details"; import AutofillScript from "../models/autofill-script"; import { AutofillInlineMenuContentService } from "../overlay/inline-menu/content/autofill-inline-menu-content.service"; import { OverlayNotificationsContentService } from "../overlay/notifications/abstractions/overlay-notifications-content.service"; +import { DomElementVisibilityService } from "../services/abstractions/dom-element-visibility.service"; import { DomQueryService } from "../services/abstractions/dom-query.service"; import { AutofillOverlayContentService } from "../services/autofill-overlay-content.service"; import { @@ -17,6 +18,7 @@ import AutofillInit from "./autofill-init"; describe("AutofillInit", () => { let domQueryService: MockProxy; + let domElementVisibilityService: MockProxy; let overlayNotificationsContentService: MockProxy; let inlineMenuElements: MockProxy; let autofillOverlayContentService: MockProxy; @@ -32,11 +34,13 @@ describe("AutofillInit", () => { }, }); domQueryService = mock(); + domElementVisibilityService = mock(); overlayNotificationsContentService = mock(); inlineMenuElements = mock(); autofillOverlayContentService = mock(); autofillInit = new AutofillInit( domQueryService, + domElementVisibilityService, autofillOverlayContentService, inlineMenuElements, overlayNotificationsContentService, diff --git a/apps/browser/src/autofill/content/autofill-init.ts b/apps/browser/src/autofill/content/autofill-init.ts index 6c34508cb00..42933c57b1e 100644 --- a/apps/browser/src/autofill/content/autofill-init.ts +++ b/apps/browser/src/autofill/content/autofill-init.ts @@ -4,9 +4,9 @@ import AutofillPageDetails from "../models/autofill-page-details"; import { AutofillInlineMenuContentService } from "../overlay/inline-menu/abstractions/autofill-inline-menu-content.service"; import { OverlayNotificationsContentService } from "../overlay/notifications/abstractions/overlay-notifications-content.service"; import { AutofillOverlayContentService } from "../services/abstractions/autofill-overlay-content.service"; +import { DomElementVisibilityService } from "../services/abstractions/dom-element-visibility.service"; import { DomQueryService } from "../services/abstractions/dom-query.service"; import { CollectAutofillContentService } from "../services/collect-autofill-content.service"; -import DomElementVisibilityService from "../services/dom-element-visibility.service"; import InsertAutofillContentService from "../services/insert-autofill-content.service"; import { sendExtensionMessage } from "../utils"; @@ -18,7 +18,6 @@ import { class AutofillInit implements AutofillInitInterface { private readonly sendExtensionMessage = sendExtensionMessage; - private readonly domElementVisibilityService: DomElementVisibilityService; private readonly collectAutofillContentService: CollectAutofillContentService; private readonly insertAutofillContentService: InsertAutofillContentService; private collectPageDetailsOnLoadTimeout: number | NodeJS.Timeout | undefined; @@ -33,26 +32,25 @@ class AutofillInit implements AutofillInitInterface { * CollectAutofillContentService and InsertAutofillContentService classes. * * @param domQueryService - Service used to handle DOM queries. + * @param domElementVisibilityService - Used to check if an element is viewable. * @param autofillOverlayContentService - The autofill overlay content service, potentially undefined. * @param autofillInlineMenuContentService - The inline menu content service, potentially undefined. * @param overlayNotificationsContentService - The overlay notifications content service, potentially undefined. */ constructor( domQueryService: DomQueryService, + domElementVisibilityService: DomElementVisibilityService, private autofillOverlayContentService?: AutofillOverlayContentService, private autofillInlineMenuContentService?: AutofillInlineMenuContentService, private overlayNotificationsContentService?: OverlayNotificationsContentService, ) { - this.domElementVisibilityService = new DomElementVisibilityService( - this.autofillInlineMenuContentService, - ); this.collectAutofillContentService = new CollectAutofillContentService( - this.domElementVisibilityService, + domElementVisibilityService, domQueryService, this.autofillOverlayContentService, ); this.insertAutofillContentService = new InsertAutofillContentService( - this.domElementVisibilityService, + domElementVisibilityService, this.collectAutofillContentService, ); } diff --git a/apps/browser/src/autofill/content/bootstrap-autofill-overlay-menu.ts b/apps/browser/src/autofill/content/bootstrap-autofill-overlay-menu.ts index aed0f6cb940..35930647921 100644 --- a/apps/browser/src/autofill/content/bootstrap-autofill-overlay-menu.ts +++ b/apps/browser/src/autofill/content/bootstrap-autofill-overlay-menu.ts @@ -1,5 +1,6 @@ import { AutofillInlineMenuContentService } from "../overlay/inline-menu/content/autofill-inline-menu-content.service"; import { AutofillOverlayContentService } from "../services/autofill-overlay-content.service"; +import DomElementVisibilityService from "../services/dom-element-visibility.service"; import { DomQueryService } from "../services/dom-query.service"; import { InlineMenuFieldQualificationService } from "../services/inline-menu-field-qualification.service"; import { setupAutofillInitDisconnectAction } from "../utils"; @@ -8,20 +9,25 @@ import AutofillInit from "./autofill-init"; (function (windowContext) { if (!windowContext.bitwardenAutofillInit) { + let inlineMenuContentService: AutofillInlineMenuContentService; + if (globalThis.self === globalThis.top) { + inlineMenuContentService = new AutofillInlineMenuContentService(); + } + const domQueryService = new DomQueryService(); + const domElementVisibilityService = new DomElementVisibilityService(inlineMenuContentService); const inlineMenuFieldQualificationService = new InlineMenuFieldQualificationService(); const autofillOverlayContentService = new AutofillOverlayContentService( domQueryService, + domElementVisibilityService, inlineMenuFieldQualificationService, ); - let inlineMenuElements: AutofillInlineMenuContentService; - if (globalThis.self === globalThis.top) { - inlineMenuElements = new AutofillInlineMenuContentService(); - } + windowContext.bitwardenAutofillInit = new AutofillInit( domQueryService, + domElementVisibilityService, autofillOverlayContentService, - inlineMenuElements, + inlineMenuContentService, ); setupAutofillInitDisconnectAction(windowContext); diff --git a/apps/browser/src/autofill/content/bootstrap-autofill-overlay-notifications.ts b/apps/browser/src/autofill/content/bootstrap-autofill-overlay-notifications.ts index 0a810c68f56..6fbb076389e 100644 --- a/apps/browser/src/autofill/content/bootstrap-autofill-overlay-notifications.ts +++ b/apps/browser/src/autofill/content/bootstrap-autofill-overlay-notifications.ts @@ -1,5 +1,6 @@ import { OverlayNotificationsContentService } from "../overlay/notifications/content/overlay-notifications-content.service"; import { AutofillOverlayContentService } from "../services/autofill-overlay-content.service"; +import DomElementVisibilityService from "../services/dom-element-visibility.service"; import { DomQueryService } from "../services/dom-query.service"; import { InlineMenuFieldQualificationService } from "../services/inline-menu-field-qualification.service"; import { setupAutofillInitDisconnectAction } from "../utils"; @@ -9,9 +10,11 @@ import AutofillInit from "./autofill-init"; (function (windowContext) { if (!windowContext.bitwardenAutofillInit) { const domQueryService = new DomQueryService(); + const domElementVisibilityService = new DomElementVisibilityService(); const inlineMenuFieldQualificationService = new InlineMenuFieldQualificationService(); const autofillOverlayContentService = new AutofillOverlayContentService( domQueryService, + domElementVisibilityService, inlineMenuFieldQualificationService, ); @@ -22,6 +25,7 @@ import AutofillInit from "./autofill-init"; windowContext.bitwardenAutofillInit = new AutofillInit( domQueryService, + domElementVisibilityService, autofillOverlayContentService, null, overlayNotificationsContentService, diff --git a/apps/browser/src/autofill/content/bootstrap-autofill-overlay.ts b/apps/browser/src/autofill/content/bootstrap-autofill-overlay.ts index 6df9397f6d8..174a695b769 100644 --- a/apps/browser/src/autofill/content/bootstrap-autofill-overlay.ts +++ b/apps/browser/src/autofill/content/bootstrap-autofill-overlay.ts @@ -1,6 +1,7 @@ import { AutofillInlineMenuContentService } from "../overlay/inline-menu/content/autofill-inline-menu-content.service"; import { OverlayNotificationsContentService } from "../overlay/notifications/content/overlay-notifications-content.service"; import { AutofillOverlayContentService } from "../services/autofill-overlay-content.service"; +import DomElementVisibilityService from "../services/dom-element-visibility.service"; import { DomQueryService } from "../services/dom-query.service"; import { InlineMenuFieldQualificationService } from "../services/inline-menu-field-qualification.service"; import { setupAutofillInitDisconnectAction } from "../utils"; @@ -9,24 +10,27 @@ import AutofillInit from "./autofill-init"; (function (windowContext) { if (!windowContext.bitwardenAutofillInit) { - const domQueryService = new DomQueryService(); - const inlineMenuFieldQualificationService = new InlineMenuFieldQualificationService(); - const autofillOverlayContentService = new AutofillOverlayContentService( - domQueryService, - inlineMenuFieldQualificationService, - ); - - let inlineMenuElements: AutofillInlineMenuContentService; + let inlineMenuContentService: AutofillInlineMenuContentService; let overlayNotificationsContentService: OverlayNotificationsContentService; if (globalThis.self === globalThis.top) { - inlineMenuElements = new AutofillInlineMenuContentService(); + inlineMenuContentService = new AutofillInlineMenuContentService(); overlayNotificationsContentService = new OverlayNotificationsContentService(); } + const domQueryService = new DomQueryService(); + const domElementVisibilityService = new DomElementVisibilityService(inlineMenuContentService); + const inlineMenuFieldQualificationService = new InlineMenuFieldQualificationService(); + const autofillOverlayContentService = new AutofillOverlayContentService( + domQueryService, + domElementVisibilityService, + inlineMenuFieldQualificationService, + ); + windowContext.bitwardenAutofillInit = new AutofillInit( domQueryService, + domElementVisibilityService, autofillOverlayContentService, - inlineMenuElements, + inlineMenuContentService, overlayNotificationsContentService, ); setupAutofillInitDisconnectAction(windowContext); diff --git a/apps/browser/src/autofill/content/bootstrap-autofill.ts b/apps/browser/src/autofill/content/bootstrap-autofill.ts index 3de750cd671..ada66f233cb 100644 --- a/apps/browser/src/autofill/content/bootstrap-autofill.ts +++ b/apps/browser/src/autofill/content/bootstrap-autofill.ts @@ -1,3 +1,4 @@ +import DomElementVisibilityService from "../services/dom-element-visibility.service"; import { DomQueryService } from "../services/dom-query.service"; import { setupAutofillInitDisconnectAction } from "../utils"; @@ -6,7 +7,11 @@ import AutofillInit from "./autofill-init"; (function (windowContext) { if (!windowContext.bitwardenAutofillInit) { const domQueryService = new DomQueryService(); - windowContext.bitwardenAutofillInit = new AutofillInit(domQueryService); + const domElementVisibilityService = new DomElementVisibilityService(); + windowContext.bitwardenAutofillInit = new AutofillInit( + domQueryService, + domElementVisibilityService, + ); setupAutofillInitDisconnectAction(windowContext); windowContext.bitwardenAutofillInit.init(); diff --git a/apps/browser/src/autofill/deprecated/services/autofill-overlay-content.service.deprecated.ts b/apps/browser/src/autofill/deprecated/services/autofill-overlay-content.service.deprecated.ts index 87af2518ddc..27ec68bc678 100644 --- a/apps/browser/src/autofill/deprecated/services/autofill-overlay-content.service.deprecated.ts +++ b/apps/browser/src/autofill/deprecated/services/autofill-overlay-content.service.deprecated.ts @@ -73,6 +73,10 @@ class LegacyAutofillOverlayContentService implements LegacyAutofillOverlayConten * Satisfy the AutofillOverlayContentService interface. */ messageHandlers = {} as AutofillOverlayContentExtensionMessageHandlers; + clearUserFilledFields() { + // do nothing + } + async setupOverlayListeners( autofillFieldElement: ElementWithOpId, autofillFieldData: AutofillField, diff --git a/apps/browser/src/autofill/enums/autofill-overlay.enum.ts b/apps/browser/src/autofill/enums/autofill-overlay.enum.ts index 53f325d520f..66ad0da546d 100644 --- a/apps/browser/src/autofill/enums/autofill-overlay.enum.ts +++ b/apps/browser/src/autofill/enums/autofill-overlay.enum.ts @@ -1,3 +1,5 @@ +import { CipherType } from "@bitwarden/common/vault/enums"; + export const AutofillOverlayElement = { Button: "autofill-inline-menu-button", List: "autofill-inline-menu-list", @@ -19,4 +21,20 @@ export const RedirectFocusDirection = { Next: "next", } as const; +export enum InlineMenuFillType { + AccountCreationUsername = 5, + PasswordGeneration = 6, + CurrentPasswordUpdate = 7, +} +export type InlineMenuFillTypes = InlineMenuFillType | CipherType; + +export const InlineMenuAccountCreationFieldType = { + Text: "text", + Email: "email", + Password: "password", +} as const; + +export type InlineMenuAccountCreationFieldTypes = + (typeof InlineMenuAccountCreationFieldType)[keyof typeof InlineMenuAccountCreationFieldType]; + export const MAX_SUB_FRAME_DEPTH = 8; diff --git a/apps/browser/src/autofill/models/autofill-field.ts b/apps/browser/src/autofill/models/autofill-field.ts index 0701ef5f65a..cc9ba61f4ee 100644 --- a/apps/browser/src/autofill/models/autofill-field.ts +++ b/apps/browser/src/autofill/models/autofill-field.ts @@ -1,6 +1,8 @@ -import { CipherType } from "@bitwarden/common/vault/enums"; - import { AutofillFieldQualifierType } from "../enums/autofill-field.enums"; +import { + InlineMenuAccountCreationFieldTypes, + InlineMenuFillTypes, +} from "../enums/autofill-overlay.enum"; /** * Represents a single field that is collected from the page source and is potentially autofilled. @@ -107,15 +109,17 @@ export default class AutofillField { */ maxLength?: number | null; + dataSetValues?: string; + rel?: string | null; checked?: boolean; - filledByCipherType?: CipherType; - - showInlineMenuAccountCreation?: boolean; + inlineMenuFillType?: InlineMenuFillTypes; showPasskeys?: boolean; fieldQualifier?: AutofillFieldQualifierType; + + accountCreationFieldType?: InlineMenuAccountCreationFieldTypes; } diff --git a/apps/browser/src/autofill/overlay/inline-menu/abstractions/autofill-inline-menu-iframe.service.ts b/apps/browser/src/autofill/overlay/inline-menu/abstractions/autofill-inline-menu-iframe.service.ts index f5aff5d65f6..f55faec887a 100644 --- a/apps/browser/src/autofill/overlay/inline-menu/abstractions/autofill-inline-menu-iframe.service.ts +++ b/apps/browser/src/autofill/overlay/inline-menu/abstractions/autofill-inline-menu-iframe.service.ts @@ -3,6 +3,8 @@ export type AutofillInlineMenuIframeExtensionMessage = { styles?: Partial; theme?: string; portKey?: string; + generatedPassword?: string; + refreshPassword?: boolean; }; export type AutofillInlineMenuIframeExtensionMessageParam = { @@ -23,6 +25,9 @@ export type BackgroundPortMessageHandlers = { }: AutofillInlineMenuIframeExtensionMessageParam) => void; updateAutofillInlineMenuColorScheme: () => void; fadeInAutofillInlineMenuIframe: () => void; + updateAutofillInlineMenuGeneratedPassword: ({ + message, + }: AutofillInlineMenuIframeExtensionMessageParam) => void; }; export interface AutofillInlineMenuIframeService { diff --git a/apps/browser/src/autofill/overlay/inline-menu/abstractions/autofill-inline-menu-list.ts b/apps/browser/src/autofill/overlay/inline-menu/abstractions/autofill-inline-menu-list.ts index ea584165b4d..a20bd3c5312 100644 --- a/apps/browser/src/autofill/overlay/inline-menu/abstractions/autofill-inline-menu-list.ts +++ b/apps/browser/src/autofill/overlay/inline-menu/abstractions/autofill-inline-menu-list.ts @@ -1,25 +1,34 @@ import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status"; -import { CipherType } from "@bitwarden/common/vault/enums"; import { InlineMenuCipherData } from "../../../background/abstractions/overlay.background"; +import { InlineMenuFillTypes } from "../../../enums/autofill-overlay.enum"; type AutofillInlineMenuListMessage = { command: string }; -export type UpdateAutofillInlineMenuListCiphersMessage = AutofillInlineMenuListMessage & { +export type UpdateAutofillInlineMenuListCiphersParams = { ciphers: InlineMenuCipherData[]; showInlineMenuAccountCreation?: boolean; }; +export type UpdateAutofillInlineMenuListCiphersMessage = AutofillInlineMenuListMessage & + UpdateAutofillInlineMenuListCiphersParams; + +export type UpdateAutofillInlineMenuGeneratedPasswordMessage = AutofillInlineMenuListMessage & { + generatedPassword: string; +}; + export type InitAutofillInlineMenuListMessage = AutofillInlineMenuListMessage & { authStatus: AuthenticationStatus; styleSheetUrl: string; theme: string; translations: Record; ciphers?: InlineMenuCipherData[]; - filledByCipherType?: CipherType; + inlineMenuFillType?: InlineMenuFillTypes; showInlineMenuAccountCreation?: boolean; showPasskeysLabels?: boolean; portKey: string; + generatedPassword?: string; + showSaveLoginMenu?: boolean; }; export type AutofillInlineMenuListWindowMessageHandlers = { @@ -31,5 +40,10 @@ export type AutofillInlineMenuListWindowMessageHandlers = { }: { message: UpdateAutofillInlineMenuListCiphersMessage; }) => void; + updateAutofillInlineMenuGeneratedPassword: ({ + message, + }: { + message: UpdateAutofillInlineMenuGeneratedPasswordMessage; + }) => void; focusAutofillInlineMenuList: () => void; }; diff --git a/apps/browser/src/autofill/overlay/inline-menu/content/autofill-inline-menu-content.service.spec.ts b/apps/browser/src/autofill/overlay/inline-menu/content/autofill-inline-menu-content.service.spec.ts index c9d86cffc5c..8a3a7e6fa8d 100644 --- a/apps/browser/src/autofill/overlay/inline-menu/content/autofill-inline-menu-content.service.spec.ts +++ b/apps/browser/src/autofill/overlay/inline-menu/content/autofill-inline-menu-content.service.spec.ts @@ -3,6 +3,7 @@ import { mock, MockProxy } from "jest-mock-extended"; import AutofillInit from "../../../content/autofill-init"; import { AutofillOverlayElement } from "../../../enums/autofill-overlay.enum"; import { DomQueryService } from "../../../services/abstractions/dom-query.service"; +import DomElementVisibilityService from "../../../services/dom-element-visibility.service"; import { createMutationRecordMock } from "../../../spec/autofill-mocks"; import { flushPromises, sendMockExtensionMessage } from "../../../spec/testing-utils"; import { ElementWithOpId } from "../../../types"; @@ -11,6 +12,7 @@ import { AutofillInlineMenuContentService } from "./autofill-inline-menu-content describe("AutofillInlineMenuContentService", () => { let domQueryService: MockProxy; + let domElementVisibilityService: DomElementVisibilityService; let autofillInlineMenuContentService: AutofillInlineMenuContentService; let autofillInit: AutofillInit; let sendExtensionMessageSpy: jest.SpyInstance; @@ -22,8 +24,14 @@ describe("AutofillInlineMenuContentService", () => { globalThis.document.body.innerHTML = ""; globalThis.requestIdleCallback = jest.fn((cb, options) => setTimeout(cb, 100)); domQueryService = mock(); + domElementVisibilityService = new DomElementVisibilityService(); autofillInlineMenuContentService = new AutofillInlineMenuContentService(); - autofillInit = new AutofillInit(domQueryService, null, autofillInlineMenuContentService); + autofillInit = new AutofillInit( + domQueryService, + domElementVisibilityService, + null, + autofillInlineMenuContentService, + ); autofillInit.init(); observeContainerMutationsSpy = jest.spyOn( autofillInlineMenuContentService["containerElementMutationObserver"] as any, @@ -37,6 +45,11 @@ describe("AutofillInlineMenuContentService", () => { afterEach(() => { jest.clearAllMocks(); + + Object.defineProperty(document, "activeElement", { + value: null, + writable: true, + }); }); describe("isElementInlineMenu", () => { @@ -197,6 +210,31 @@ describe("AutofillInlineMenuContentService", () => { ); }); }); + + it("appends the inline menu element to a containing `dialog` element if the element is a modal", async () => { + isInlineMenuButtonVisibleSpy.mockResolvedValue(false); + const dialogElement = document.createElement("dialog"); + dialogElement.setAttribute("open", "true"); + jest.spyOn(dialogElement, "matches").mockReturnValue(true); + const dialogAppendSpy = jest.spyOn(dialogElement, "appendChild"); + const inputElement = document.createElement("input"); + dialogElement.appendChild(inputElement); + document.body.appendChild(dialogElement); + Object.defineProperty(document, "activeElement", { + value: inputElement, + writable: true, + }); + + sendMockExtensionMessage({ + command: "appendAutofillInlineMenuToDom", + overlayElement: AutofillOverlayElement.Button, + }); + await flushPromises(); + + expect(dialogAppendSpy).toHaveBeenCalledWith( + autofillInlineMenuContentService["buttonElement"], + ); + }); }); }); diff --git a/apps/browser/src/autofill/overlay/inline-menu/content/autofill-inline-menu-content.service.ts b/apps/browser/src/autofill/overlay/inline-menu/content/autofill-inline-menu-content.service.ts index 110c1be7db8..da274291731 100644 --- a/apps/browser/src/autofill/overlay/inline-menu/content/autofill-inline-menu-content.service.ts +++ b/apps/browser/src/autofill/overlay/inline-menu/content/autofill-inline-menu-content.service.ts @@ -88,7 +88,7 @@ export class AutofillInlineMenuContentService implements AutofillInlineMenuConte /** * Removes the autofill inline menu from the page. This will initially - * unobserve the body element to ensure the mutation observer no + * unobserve the menu container to ensure the mutation observer no * longer triggers. */ private closeInlineMenu = (message?: AutofillExtensionMessage) => { @@ -190,15 +190,15 @@ export class AutofillInlineMenuContentService implements AutofillInlineMenuConte } /** - * Appends the inline menu element to the body element. This method will also - * observe the body element to ensure that the inline menu element is not + * Appends the inline menu element to the menu container. This method will also + * observe the menu container to ensure that the inline menu element is not * interfered with by any DOM changes. * - * @param element - The inline menu element to append to the body element. + * @param element - The inline menu element to append to the menu container. */ private appendInlineMenuElementToDom(element: HTMLElement) { const parentDialogElement = globalThis.document.activeElement?.closest("dialog"); - if (parentDialogElement && parentDialogElement.open && parentDialogElement.matches(":modal")) { + if (parentDialogElement?.open && parentDialogElement.matches(":modal")) { this.observeContainerElement(parentDialogElement); parentDialogElement.appendChild(element); return; @@ -273,10 +273,10 @@ export class AutofillInlineMenuContentService implements AutofillInlineMenuConte } /** - * Sets up mutation observers for the inline menu elements, the body element, and + * Sets up mutation observers for the inline menu elements, the menu container, and * the document element. The mutation observers are used to remove any styles that * are added to the inline menu elements by the website. They are also used to ensure - * that the inline menu elements are always present at the bottom of the body element. + * that the inline menu elements are always present at the bottom of the menu container. */ private setupMutationObserver = () => { this.inlineMenuElementsMutationObserver = new MutationObserver( @@ -441,10 +441,10 @@ export class AutofillInlineMenuContentService implements AutofillInlineMenuConte /** * Handles the behavior of a persistent child element that is forcing itself to - * the bottom of the body element. This method will ensure that the inline menu + * the bottom of the menu container. This method will ensure that the inline menu * elements are not obscured by the persistent child element. * - * @param lastChild - The last child of the body element. + * @param lastChild - The last child of the menu container. */ private handlePersistentLastChildOverride(lastChild: Element) { const lastChildZIndex = parseInt((lastChild as HTMLElement).style.zIndex); @@ -460,11 +460,11 @@ export class AutofillInlineMenuContentService implements AutofillInlineMenuConte } /** - * Verifies if the last child of the body element is overlaying the inline menu elements. - * This is triggered when the last child of the body is being forced by some script to - * be an element other than the inline menu elements. + * Verifies if the last child of the menu container is overlaying the inline menu elements. + * This is triggered when the last child of the menu container is being forced by some + * script to be an element other than the inline menu elements. * - * @param lastChild - The last child of the body element. + * @param lastChild - The last child of the menu container. */ private verifyInlineMenuIsNotObscured = async (lastChild: Element) => { const inlineMenuPosition: InlineMenuPosition = await this.sendExtensionMessage( @@ -495,7 +495,7 @@ export class AutofillInlineMenuContentService implements AutofillInlineMenuConte } /** - * Clears the timeout that is used to verify that the last child of the body element + * Clears the timeout that is used to verify that the last child of the menu container * is not overlaying the inline menu elements. */ private clearPersistentLastChildOverrideTimeout() { diff --git a/apps/browser/src/autofill/overlay/inline-menu/iframe-content/__snapshots__/autofill-inline-menu-iframe.service.spec.ts.snap b/apps/browser/src/autofill/overlay/inline-menu/iframe-content/__snapshots__/autofill-inline-menu-iframe.service.spec.ts.snap index 4400b528d0f..8aac4f3c431 100644 --- a/apps/browser/src/autofill/overlay/inline-menu/iframe-content/__snapshots__/autofill-inline-menu-iframe.service.spec.ts.snap +++ b/apps/browser/src/autofill/overlay/inline-menu/iframe-content/__snapshots__/autofill-inline-menu-iframe.service.spec.ts.snap @@ -3,6 +3,7 @@ exports[`AutofillInlineMenuIframeService initMenuIframe sets up the iframe's attributes 1`] = ` + +
+ + + + + +
{{ "or" | i18n }}
+ + +
+ + + + + +
+ + diff --git a/libs/auth/src/angular/login/login.component.ts b/libs/auth/src/angular/login/login.component.ts new file mode 100644 index 00000000000..ad17a0a97a3 --- /dev/null +++ b/libs/auth/src/angular/login/login.component.ts @@ -0,0 +1,561 @@ +import { CommonModule } from "@angular/common"; +import { Component, ElementRef, Input, NgZone, OnDestroy, OnInit, ViewChild } from "@angular/core"; +import { FormBuilder, FormControl, ReactiveFormsModule, Validators } from "@angular/forms"; +import { ActivatedRoute, Router, RouterModule } from "@angular/router"; +import { firstValueFrom, Subject, take, takeUntil } from "rxjs"; + +import { JslibModule } from "@bitwarden/angular/jslib.module"; +import { + LoginEmailServiceAbstraction, + LoginStrategyServiceAbstraction, + PasswordLoginCredentials, + RegisterRouteService, +} from "@bitwarden/auth/common"; +import { InternalPolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; +import { MasterPasswordPolicyOptions } from "@bitwarden/common/admin-console/models/domain/master-password-policy-options"; +import { Policy } from "@bitwarden/common/admin-console/models/domain/policy"; +import { DevicesApiServiceAbstraction } from "@bitwarden/common/auth/abstractions/devices-api.service.abstraction"; +import { CaptchaIFrame } from "@bitwarden/common/auth/captcha-iframe"; +import { AuthResult } from "@bitwarden/common/auth/models/domain/auth-result"; +import { ForceSetPasswordReason } from "@bitwarden/common/auth/models/domain/force-set-password-reason"; +import { ClientType } from "@bitwarden/common/enums"; +import { AppIdService } from "@bitwarden/common/platform/abstractions/app-id.service"; +import { BroadcasterService } from "@bitwarden/common/platform/abstractions/broadcaster.service"; +import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service"; +import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; +import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; +import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service"; +import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; +import { Utils } from "@bitwarden/common/platform/misc/utils"; +import { SyncService } from "@bitwarden/common/platform/sync"; +import { PasswordStrengthServiceAbstraction } from "@bitwarden/common/tools/password-strength"; +import { + AsyncActionsModule, + ButtonModule, + CheckboxModule, + FormFieldModule, + IconButtonModule, + LinkModule, + ToastService, +} from "@bitwarden/components"; + +import { AnonLayoutWrapperDataService } from "../anon-layout/anon-layout-wrapper-data.service"; +import { VaultIcon, WaveIcon } from "../icons"; + +import { LoginComponentService } from "./login-component.service"; + +const BroadcasterSubscriptionId = "LoginComponent"; + +export enum LoginUiState { + EMAIL_ENTRY = "EmailEntry", + MASTER_PASSWORD_ENTRY = "MasterPasswordEntry", +} + +@Component({ + standalone: true, + templateUrl: "./login.component.html", + imports: [ + AsyncActionsModule, + ButtonModule, + CheckboxModule, + CommonModule, + FormFieldModule, + IconButtonModule, + LinkModule, + JslibModule, + ReactiveFormsModule, + RouterModule, + ], +}) +export class LoginComponent implements OnInit, OnDestroy { + @ViewChild("masterPasswordInputRef") masterPasswordInputRef: ElementRef; + @Input() captchaSiteKey: string = null; + + private destroy$ = new Subject(); + private enforcedMasterPasswordOptions: MasterPasswordPolicyOptions = undefined; + readonly Icons = { WaveIcon, VaultIcon }; + + captcha: CaptchaIFrame; + captchaToken: string = null; + clientType: ClientType; + ClientType = ClientType; + LoginUiState = LoginUiState; + registerRoute$ = this.registerRouteService.registerRoute$(); // TODO: remove when email verification flag is removed + isKnownDevice = false; + loginUiState: LoginUiState = LoginUiState.EMAIL_ENTRY; + + formGroup = this.formBuilder.group( + { + email: ["", [Validators.required, Validators.email]], + masterPassword: [ + "", + [Validators.required, Validators.minLength(Utils.originalMinimumPasswordLength)], + ], + rememberEmail: [false], + }, + { updateOn: "submit" }, + ); + + get emailFormControl(): FormControl { + return this.formGroup.controls.email; + } + + /** + * LoginViaAuthRequestSupported is a boolean that determines if we show the Login with device button. + * An AuthRequest is the mechanism that allows users to login to the client via a device that is already logged in. + */ + loginViaAuthRequestSupported = false; + + // Web properties + enforcedPasswordPolicyOptions: MasterPasswordPolicyOptions; + policies: Policy[]; + showResetPasswordAutoEnrollWarning = false; + + // Desktop properties + deferFocus: boolean | null = null; + + constructor( + private activatedRoute: ActivatedRoute, + private anonLayoutWrapperDataService: AnonLayoutWrapperDataService, + private appIdService: AppIdService, + private broadcasterService: BroadcasterService, + private devicesApiService: DevicesApiServiceAbstraction, + private environmentService: EnvironmentService, + private formBuilder: FormBuilder, + private i18nService: I18nService, + private loginEmailService: LoginEmailServiceAbstraction, + private loginComponentService: LoginComponentService, + private loginStrategyService: LoginStrategyServiceAbstraction, + private messagingService: MessagingService, + private ngZone: NgZone, + private passwordStrengthService: PasswordStrengthServiceAbstraction, + private platformUtilsService: PlatformUtilsService, + private policyService: InternalPolicyService, + private registerRouteService: RegisterRouteService, + private router: Router, + private syncService: SyncService, + private toastService: ToastService, + private logService: LogService, + ) { + this.clientType = this.platformUtilsService.getClientType(); + this.loginViaAuthRequestSupported = this.loginComponentService.isLoginViaAuthRequestSupported(); + } + + async ngOnInit(): Promise { + await this.defaultOnInit(); + + if (this.clientType === ClientType.Desktop) { + await this.desktopOnInit(); + } + } + + ngOnDestroy(): void { + if (this.clientType === ClientType.Desktop) { + // TODO: refactor to not use deprecated broadcaster service. + this.broadcasterService.unsubscribe(BroadcasterSubscriptionId); + } + + this.destroy$.next(); + this.destroy$.complete(); + } + + submit = async (): Promise => { + if (this.clientType === ClientType.Desktop) { + if (this.loginUiState !== LoginUiState.MASTER_PASSWORD_ENTRY) { + return; + } + } + + const { email, masterPassword } = this.formGroup.value; + + await this.setupCaptcha(); + + this.formGroup.markAllAsTouched(); + if (this.formGroup.invalid) { + return; + } + + const credentials = new PasswordLoginCredentials( + email, + masterPassword, + this.captchaToken, + null, + ); + + const authResult = await this.loginStrategyService.logIn(credentials); + + await this.saveEmailSettings(); + await this.handleAuthResult(authResult); + + if (this.clientType === ClientType.Desktop) { + if (this.captchaSiteKey) { + const content = document.getElementById("content") as HTMLDivElement; + content.setAttribute("style", "width:335px"); + } + } + }; + + /** + * Handles the result of the authentication process. + * + * @param authResult + * @returns A simple `return` statement for each conditional check. + * If you update this method, do not forget to add a `return` + * to each if-condition block where necessary to stop code execution. + */ + private async handleAuthResult(authResult: AuthResult): Promise { + if (this.handleCaptchaRequired(authResult)) { + this.captchaSiteKey = authResult.captchaSiteKey; + this.captcha.init(authResult.captchaSiteKey); + return; + } + + if (authResult.requiresEncryptionKeyMigration) { + /* Legacy accounts used the master key to encrypt data. + Migration is required but only performed on Web. */ + if (this.clientType === ClientType.Web) { + await this.router.navigate(["migrate-legacy-encryption"]); + } else { + this.toastService.showToast({ + variant: "error", + title: this.i18nService.t("errorOccured"), + message: this.i18nService.t("encryptionKeyMigrationRequired"), + }); + } + return; + } + + if (authResult.requiresTwoFactor) { + await this.router.navigate(["2fa"]); + return; + } + + await this.syncService.fullSync(true); + + if (authResult.forcePasswordReset != ForceSetPasswordReason.None) { + this.loginEmailService.clearValues(); + await this.router.navigate(["update-temp-password"]); + return; + } + + // If none of the above cases are true, proceed with login... + await this.evaluatePassword(); + + this.loginEmailService.clearValues(); + + if (this.clientType === ClientType.Browser) { + await this.router.navigate(["/tabs/vault"]); + } else { + await this.router.navigate(["vault"]); + } + } + + protected async launchSsoBrowserWindow(clientId: "browser" | "desktop"): Promise { + await this.loginComponentService.launchSsoBrowserWindow(this.emailFormControl.value, clientId); + } + + protected async evaluatePassword(): Promise { + try { + // If we do not have any saved policies, attempt to load them from the service + if (this.enforcedMasterPasswordOptions == undefined) { + this.enforcedMasterPasswordOptions = await firstValueFrom( + this.policyService.masterPasswordPolicyOptions$(), + ); + } + + if (this.requirePasswordChange()) { + await this.router.navigate(["update-password"]); + return; + } + } catch (e) { + // Do not prevent unlock if there is an error evaluating policies + this.logService.error(e); + } + } + + /** + * Checks if the master password meets the enforced policy requirements + * If not, returns false + */ + private requirePasswordChange(): boolean { + if ( + this.enforcedMasterPasswordOptions == undefined || + !this.enforcedMasterPasswordOptions.enforceOnLogin + ) { + return false; + } + + const masterPassword = this.formGroup.controls.masterPassword.value; + + const passwordStrength = this.passwordStrengthService.getPasswordStrength( + masterPassword, + this.formGroup.value.email, + )?.score; + + return !this.policyService.evaluateMasterPassword( + passwordStrength, + masterPassword, + this.enforcedMasterPasswordOptions, + ); + } + + protected showCaptcha(): boolean { + return !Utils.isNullOrWhitespace(this.captchaSiteKey); + } + + protected async startAuthRequestLogin(): Promise { + this.formGroup.get("masterPassword")?.clearValidators(); + this.formGroup.get("masterPassword")?.updateValueAndValidity(); + + if (!this.formGroup.valid) { + return; + } + + await this.saveEmailSettings(); + await this.router.navigate(["/login-with-device"]); + } + + protected async validateEmail(): Promise { + this.formGroup.controls.email.markAsTouched(); + return this.formGroup.controls.email.valid; + } + + protected async toggleLoginUiState(value: LoginUiState): Promise { + this.loginUiState = value; + + if (this.loginUiState === LoginUiState.EMAIL_ENTRY) { + this.loginComponentService.showBackButton(false); + + this.anonLayoutWrapperDataService.setAnonLayoutWrapperData({ + pageTitle: { key: "logInToBitwarden" }, + pageIcon: this.Icons.VaultIcon, + pageSubtitle: null, // remove subtitle when going back to email entry + }); + + // Reset master password only when going from validated to not validated so that autofill can work properly + this.formGroup.controls.masterPassword.reset(); + + if (this.loginViaAuthRequestSupported) { + // Reset known device state when going back to email entry if it is supported + this.isKnownDevice = false; + } + } else if (this.loginUiState === LoginUiState.MASTER_PASSWORD_ENTRY) { + this.loginComponentService.showBackButton(true); + this.anonLayoutWrapperDataService.setAnonLayoutWrapperData({ + pageTitle: { key: "welcomeBack" }, + pageSubtitle: this.emailFormControl.value, + pageIcon: this.Icons.WaveIcon, + }); + + // Mark MP as untouched so that, when users enter email and hit enter, the MP field doesn't load with validation errors + this.formGroup.controls.masterPassword.markAsUntouched(); + + // When email is validated, focus on master password after waiting for input to be rendered + if (this.ngZone.isStable) { + this.masterPasswordInputRef?.nativeElement?.focus(); + } else { + this.ngZone.onStable.pipe(take(1), takeUntil(this.destroy$)).subscribe(() => { + this.masterPasswordInputRef?.nativeElement?.focus(); + }); + } + + if (this.loginViaAuthRequestSupported) { + await this.getKnownDevice(this.emailFormControl.value); + } + } + } + + /** + * Set the email value from the input field. + * @param event The event object from the input field. + */ + onEmailBlur(event: Event) { + const emailInput = event.target as HTMLInputElement; + this.formGroup.controls.email.setValue(emailInput.value); + // Call setLoginEmail so that the email is pre-populated when navigating to the "enter password" screen. + this.loginEmailService.setLoginEmail(this.formGroup.value.email); + } + + isLoginWithPasskeySupported() { + return this.loginComponentService.isLoginWithPasskeySupported(); + } + + protected async goToHint(): Promise { + await this.saveEmailSettings(); + await this.router.navigateByUrl("/hint"); + } + + protected async goToRegister(): Promise { + // TODO: remove when email verification flag is removed + const registerRoute = await firstValueFrom(this.registerRoute$); + + if (this.emailFormControl.valid) { + await this.router.navigate([registerRoute], { + queryParams: { email: this.emailFormControl.value }, + }); + return; + } + + await this.router.navigate([registerRoute]); + } + + protected async saveEmailSettings(): Promise { + await this.loginEmailService.setLoginEmail(this.formGroup.value.email); + this.loginEmailService.setRememberEmail(this.formGroup.value.rememberEmail); + await this.loginEmailService.saveEmailSettings(); + } + + protected async continue(): Promise { + if (await this.validateEmail()) { + await this.toggleLoginUiState(LoginUiState.MASTER_PASSWORD_ENTRY); + } + } + + /** + * Call to check if the device is known. + * Known means that the user has logged in with this device before. + * @param email - The user's email + */ + private async getKnownDevice(email: string): Promise { + try { + const deviceIdentifier = await this.appIdService.getAppId(); + this.isKnownDevice = await this.devicesApiService.getKnownDevice(email, deviceIdentifier); + } catch (e) { + this.isKnownDevice = false; + } + } + + private async setupCaptcha(): Promise { + const env = await firstValueFrom(this.environmentService.environment$); + const webVaultUrl = env.getWebVaultUrl(); + + this.captcha = new CaptchaIFrame( + window, + webVaultUrl, + this.i18nService, + (token: string) => { + this.captchaToken = token; + }, + (error: string) => { + this.toastService.showToast({ + variant: "error", + title: this.i18nService.t("errorOccurred"), + message: error, + }); + }, + (info: string) => { + this.toastService.showToast({ + variant: "info", + title: this.i18nService.t("info"), + message: info, + }); + }, + ); + } + + private handleCaptchaRequired(authResult: AuthResult): boolean { + return !Utils.isNullOrWhitespace(authResult.captchaSiteKey); + } + + private async loadEmailSettings(): Promise { + // Try to load the email from memory first + const email = await firstValueFrom(this.loginEmailService.loginEmail$); + const rememberEmail = this.loginEmailService.getRememberEmail(); + + if (email) { + this.formGroup.controls.email.setValue(email); + this.formGroup.controls.rememberEmail.setValue(rememberEmail); + } else { + // If there is no email in memory, check for a storedEmail on disk + const storedEmail = await firstValueFrom(this.loginEmailService.storedEmail$); + + if (storedEmail) { + this.formGroup.controls.email.setValue(storedEmail); + // If there is a storedEmail, rememberEmail defaults to true + this.formGroup.controls.rememberEmail.setValue(true); + } + } + } + + private focusInput() { + document + .getElementById( + this.emailFormControl.value == null || this.emailFormControl.value === "" + ? "email" + : "masterPassword", + ) + ?.focus(); + } + + private async defaultOnInit(): Promise { + // If there's an existing org invite, use it to get the password policies + const orgPolicies = await this.loginComponentService.getOrgPolicies(); + + this.policies = orgPolicies?.policies; + this.showResetPasswordAutoEnrollWarning = orgPolicies?.isPolicyAndAutoEnrollEnabled; + + let paramEmailIsSet = false; + + const params = await firstValueFrom(this.activatedRoute.queryParams); + + if (params) { + const qParamsEmail = params.email; + + // If there is an email in the query params, set that email as the form field value + if (qParamsEmail != null && qParamsEmail.indexOf("@") > -1) { + this.formGroup.controls.email.setValue(qParamsEmail); + paramEmailIsSet = true; + } + } + + // If there are no params or no email in the query params, loadEmailSettings from state + if (!paramEmailIsSet) { + await this.loadEmailSettings(); + } + + if (this.loginViaAuthRequestSupported) { + await this.getKnownDevice(this.emailFormControl.value); + } + + // Backup check to handle unknown case where activatedRoute is not available + // This shouldn't happen under normal circumstances + if (!this.activatedRoute) { + await this.loadEmailSettings(); + } + } + + private async desktopOnInit(): Promise { + // TODO: refactor to not use deprecated broadcaster service. + this.broadcasterService.subscribe(BroadcasterSubscriptionId, async (message: any) => { + this.ngZone.run(() => { + switch (message.command) { + case "windowIsFocused": + if (this.deferFocus === null) { + this.deferFocus = !message.windowIsFocused; + if (!this.deferFocus) { + this.focusInput(); + } + } else if (this.deferFocus && message.windowIsFocused) { + this.focusInput(); + this.deferFocus = false; + } + break; + default: + } + }); + }); + + this.messagingService.send("getWindowIsFocused"); + } + + /** + * Helper function to determine if the back button should be shown. + * @returns true if the back button should be shown. + */ + protected shouldShowBackButton(): boolean { + return ( + this.loginUiState === LoginUiState.MASTER_PASSWORD_ENTRY && + this.clientType !== ClientType.Browser + ); + } +} diff --git a/libs/auth/src/angular/registration/registration-finish/default-registration-finish.service.spec.ts b/libs/auth/src/angular/registration/registration-finish/default-registration-finish.service.spec.ts index fe6b9b2c7dc..e034e23de43 100644 --- a/libs/auth/src/angular/registration/registration-finish/default-registration-finish.service.spec.ts +++ b/libs/auth/src/angular/registration/registration-finish/default-registration-finish.service.spec.ts @@ -2,11 +2,11 @@ import { MockProxy, mock } from "jest-mock-extended"; import { AccountApiService } from "@bitwarden/common/auth/abstractions/account-api.service"; import { DEFAULT_KDF_CONFIG } from "@bitwarden/common/auth/models/domain/kdf-config"; -import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service"; import { EncString } from "@bitwarden/common/platform/models/domain/enc-string"; import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key"; import { CsprngArray } from "@bitwarden/common/types/csprng"; import { MasterKey, UserKey } from "@bitwarden/common/types/key"; +import { KeyService } from "@bitwarden/key-management"; import { PasswordInputResult } from "../../input-password/password-input-result"; @@ -15,14 +15,14 @@ import { DefaultRegistrationFinishService } from "./default-registration-finish. describe("DefaultRegistrationFinishService", () => { let service: DefaultRegistrationFinishService; - let cryptoService: MockProxy; + let keyService: MockProxy; let accountApiService: MockProxy; beforeEach(() => { - cryptoService = mock(); + keyService = mock(); accountApiService = mock(); - service = new DefaultRegistrationFinishService(cryptoService, accountApiService); + service = new DefaultRegistrationFinishService(keyService, accountApiService); }); it("instantiates", () => { @@ -76,7 +76,7 @@ describe("DefaultRegistrationFinishService", () => { }); it("throws an error if the user key cannot be created", async () => { - cryptoService.makeUserKey.mockResolvedValue([null, null]); + keyService.makeUserKey.mockResolvedValue([null, null]); await expect(service.finishRegistration(email, passwordInputResult)).rejects.toThrow( "User key could not be created", @@ -84,8 +84,8 @@ describe("DefaultRegistrationFinishService", () => { }); it("registers the user and returns a captcha bypass token when given valid email verification input", async () => { - cryptoService.makeUserKey.mockResolvedValue([userKey, userKeyEncString]); - cryptoService.makeKeyPair.mockResolvedValue(userKeyPair); + keyService.makeUserKey.mockResolvedValue([userKey, userKeyEncString]); + keyService.makeKeyPair.mockResolvedValue(userKeyPair); accountApiService.registerFinish.mockResolvedValue(capchaBypassToken); const result = await service.finishRegistration( @@ -96,8 +96,8 @@ describe("DefaultRegistrationFinishService", () => { expect(result).toEqual(capchaBypassToken); - expect(cryptoService.makeUserKey).toHaveBeenCalledWith(masterKey); - expect(cryptoService.makeKeyPair).toHaveBeenCalledWith(userKey); + expect(keyService.makeUserKey).toHaveBeenCalledWith(masterKey); + expect(keyService.makeKeyPair).toHaveBeenCalledWith(userKey); expect(accountApiService.registerFinish).toHaveBeenCalledWith( expect.objectContaining({ email, @@ -113,8 +113,14 @@ describe("DefaultRegistrationFinishService", () => { kdfIterations: passwordInputResult.kdfConfig.iterations, kdfMemory: undefined, kdfParallelism: undefined, - orgInviteToken: undefined, // OrgInvite only handled in web - organizationUserId: undefined, // OrgInvite only handled in web + // Web only fields should be undefined + orgInviteToken: undefined, + organizationUserId: undefined, + orgSponsoredFreeFamilyPlanToken: undefined, + acceptEmergencyAccessInviteToken: undefined, + acceptEmergencyAccessId: undefined, + providerInviteToken: undefined, + providerUserId: undefined, }), ); }); diff --git a/libs/auth/src/angular/registration/registration-finish/default-registration-finish.service.ts b/libs/auth/src/angular/registration/registration-finish/default-registration-finish.service.ts index 6d77c777491..f2c4d4c98b6 100644 --- a/libs/auth/src/angular/registration/registration-finish/default-registration-finish.service.ts +++ b/libs/auth/src/angular/registration/registration-finish/default-registration-finish.service.ts @@ -2,8 +2,8 @@ import { MasterPasswordPolicyOptions } from "@bitwarden/common/admin-console/mod import { AccountApiService } from "@bitwarden/common/auth/abstractions/account-api.service"; import { RegisterFinishRequest } from "@bitwarden/common/auth/models/request/registration/register-finish.request"; import { KeysRequest } from "@bitwarden/common/models/request/keys.request"; -import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service"; import { EncryptedString, EncString } from "@bitwarden/common/platform/models/domain/enc-string"; +import { KeyService } from "@bitwarden/key-management"; import { PasswordInputResult } from "../../input-password/password-input-result"; @@ -11,7 +11,7 @@ import { RegistrationFinishService } from "./registration-finish.service"; export class DefaultRegistrationFinishService implements RegistrationFinishService { constructor( - protected cryptoService: CryptoService, + protected keyService: KeyService, protected accountApiService: AccountApiService, ) {} @@ -30,15 +30,17 @@ export class DefaultRegistrationFinishService implements RegistrationFinishServi orgSponsoredFreeFamilyPlanToken?: string, acceptEmergencyAccessInviteToken?: string, emergencyAccessId?: string, + providerInviteToken?: string, + providerUserId?: string, ): Promise { - const [newUserKey, newEncUserKey] = await this.cryptoService.makeUserKey( + const [newUserKey, newEncUserKey] = await this.keyService.makeUserKey( passwordInputResult.masterKey, ); if (!newUserKey || !newEncUserKey) { throw new Error("User key could not be created"); } - const userAsymmetricKeys = await this.cryptoService.makeKeyPair(newUserKey); + const userAsymmetricKeys = await this.keyService.makeKeyPair(newUserKey); const registerRequest = await this.buildRegisterRequest( email, @@ -49,6 +51,8 @@ export class DefaultRegistrationFinishService implements RegistrationFinishServi orgSponsoredFreeFamilyPlanToken, acceptEmergencyAccessInviteToken, emergencyAccessId, + providerInviteToken, + providerUserId, ); const capchaBypassToken = await this.accountApiService.registerFinish(registerRequest); @@ -65,6 +69,8 @@ export class DefaultRegistrationFinishService implements RegistrationFinishServi orgSponsoredFreeFamilyPlanToken?: string, // web only acceptEmergencyAccessInviteToken?: string, // web only emergencyAccessId?: string, // web only + providerInviteToken?: string, // web only + providerUserId?: string, // web only ): Promise { const userAsymmetricKeysRequest = new KeysRequest( userAsymmetricKeys[0], diff --git a/libs/auth/src/angular/registration/registration-finish/registration-finish.component.ts b/libs/auth/src/angular/registration/registration-finish/registration-finish.component.ts index 3a6d26ef939..7cfa85ec3d4 100644 --- a/libs/auth/src/angular/registration/registration-finish/registration-finish.component.ts +++ b/libs/auth/src/angular/registration/registration-finish/registration-finish.component.ts @@ -49,6 +49,10 @@ export class RegistrationFinishComponent implements OnInit, OnDestroy { acceptEmergencyAccessInviteToken: string; emergencyAccessId: string; + // This token is provided when the user is coming from an emailed invite to accept a provider invite + providerInviteToken: string; + providerUserId: string; + masterPasswordPolicyOptions: MasterPasswordPolicyOptions | null = null; constructor( @@ -104,6 +108,11 @@ export class RegistrationFinishComponent implements OnInit, OnDestroy { this.acceptEmergencyAccessInviteToken = qParams.acceptEmergencyAccessInviteToken; this.emergencyAccessId = qParams.emergencyAccessId; } + + if (qParams.providerInviteToken != null && qParams.providerUserId != null) { + this.providerInviteToken = qParams.providerInviteToken; + this.providerUserId = qParams.providerUserId; + } } private async initOrgInviteFlowIfPresent(): Promise { @@ -140,6 +149,8 @@ export class RegistrationFinishComponent implements OnInit, OnDestroy { this.orgSponsoredFreeFamilyPlanToken, this.acceptEmergencyAccessInviteToken, this.emergencyAccessId, + this.providerInviteToken, + this.providerUserId, ); } catch (e) { this.validationService.showError(e); diff --git a/libs/auth/src/angular/registration/registration-finish/registration-finish.service.ts b/libs/auth/src/angular/registration/registration-finish/registration-finish.service.ts index b7abd381084..3746e37b84a 100644 --- a/libs/auth/src/angular/registration/registration-finish/registration-finish.service.ts +++ b/libs/auth/src/angular/registration/registration-finish/registration-finish.service.ts @@ -25,6 +25,8 @@ export abstract class RegistrationFinishService { * @param orgSponsoredFreeFamilyPlanToken The optional org sponsored free family plan token. * @param acceptEmergencyAccessInviteToken The optional accept emergency access invite token. * @param emergencyAccessId The optional emergency access id which is required to validate the emergency access invite token. + * @param providerInviteToken The optional provider invite token. + * @param providerUserId The optional provider user id which is required to validate the provider invite token. * @returns a promise which resolves to the captcha bypass token string upon a successful account creation. */ abstract finishRegistration( @@ -34,5 +36,7 @@ export abstract class RegistrationFinishService { orgSponsoredFreeFamilyPlanToken?: string, acceptEmergencyAccessInviteToken?: string, emergencyAccessId?: string, + providerInviteToken?: string, + providerUserId?: string, ): Promise; } diff --git a/libs/auth/src/angular/registration/registration-start/registration-start.component.ts b/libs/auth/src/angular/registration/registration-start/registration-start.component.ts index 258f811ec8b..5ce606b5ec6 100644 --- a/libs/auth/src/angular/registration/registration-start/registration-start.component.ts +++ b/libs/auth/src/angular/registration/registration-start/registration-start.component.ts @@ -18,6 +18,7 @@ import { LinkModule, } from "@bitwarden/components"; +import { LoginEmailService } from "../../../common"; import { AnonLayoutWrapperDataService } from "../../anon-layout/anon-layout-wrapper-data.service"; import { RegistrationUserAddIcon } from "../../icons"; import { RegistrationCheckEmailIcon } from "../../icons/registration-check-email.icon"; @@ -89,6 +90,7 @@ export class RegistrationStartComponent implements OnInit, OnDestroy { private platformUtilsService: PlatformUtilsService, private accountApiService: AccountApiService, private router: Router, + private loginEmailService: LoginEmailService, private anonLayoutWrapperDataService: AnonLayoutWrapperDataService, ) { this.isSelfHost = platformUtilsService.isSelfHost(); @@ -99,6 +101,15 @@ export class RegistrationStartComponent implements OnInit, OnDestroy { this.registrationStartStateChange.emit(this.state); this.listenForQueryParamChanges(); + + /** + * If the user has a login email, set the email field to the login email. + */ + this.loginEmailService.loginEmail$.pipe(takeUntil(this.destroy$)).subscribe((email) => { + if (email) { + this.formGroup.patchValue({ email }); + } + }); } private listenForQueryParamChanges() { diff --git a/libs/auth/src/angular/registration/registration-start/registration-start.stories.ts b/libs/auth/src/angular/registration/registration-start/registration-start.stories.ts index f7f6185280a..fa3ad2ae2b9 100644 --- a/libs/auth/src/angular/registration/registration-start/registration-start.stories.ts +++ b/libs/auth/src/angular/registration/registration-start/registration-start.stories.ts @@ -4,7 +4,7 @@ import { BrowserAnimationsModule } from "@angular/platform-browser/animations"; import { ActivatedRoute, Params } from "@angular/router"; import { RouterTestingModule } from "@angular/router/testing"; import { Meta, StoryObj, applicationConfig, moduleMetadata } from "@storybook/angular"; -import { of } from "rxjs"; +import { of, BehaviorSubject } from "rxjs"; import { AccountApiService } from "@bitwarden/common/auth/abstractions/account-api.service"; import { ClientType } from "@bitwarden/common/enums"; @@ -30,6 +30,7 @@ import { // FIXME: remove `/apps` import from `/libs` // eslint-disable-next-line import/no-restricted-paths import { PreloadedEnglishI18nModule } from "../../../../../../apps/web/src/app/core/tests"; +import { LoginEmailService } from "../../../common"; import { AnonLayoutWrapperDataService } from "../../anon-layout/anon-layout-wrapper-data.service"; import { AnonLayoutWrapperData } from "../../anon-layout/anon-layout-wrapper.component"; @@ -45,6 +46,7 @@ const decorators = (options: { queryParams?: Params; clientType?: ClientType; defaultRegion?: Region; + initialLoginEmail?: string; }) => { return [ moduleMetadata({ @@ -90,6 +92,12 @@ const decorators = (options: { getClientType: () => options.clientType || ClientType.Web, } as Partial, }, + { + provide: LoginEmailService, + useValue: { + loginEmail$: new BehaviorSubject(options.initialLoginEmail || null), + } as Partial, + }, { provide: AnonLayoutWrapperDataService, useValue: { @@ -159,6 +167,21 @@ export const WebUSRegionQueryParamsExample: Story = { }), }; +export const WebUSRegionWithInitialLoginEmailExample: Story = { + render: (args) => ({ + props: args, + template: ` + + `, + }), + decorators: decorators({ + clientType: ClientType.Web, + queryParams: {}, + defaultRegion: Region.US, + initialLoginEmail: "example@bitwarden.com", + }), +}; + export const DesktopUSRegionExample: Story = { render: (args) => ({ props: args, diff --git a/libs/auth/src/angular/set-password-jit/default-set-password-jit.service.spec.ts b/libs/auth/src/angular/set-password-jit/default-set-password-jit.service.spec.ts index f36283e0c06..da49067d7b6 100644 --- a/libs/auth/src/angular/set-password-jit/default-set-password-jit.service.spec.ts +++ b/libs/auth/src/angular/set-password-jit/default-set-password-jit.service.spec.ts @@ -14,7 +14,6 @@ import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/auth import { DEFAULT_KDF_CONFIG } from "@bitwarden/common/auth/models/domain/kdf-config"; import { SetPasswordRequest } from "@bitwarden/common/auth/models/request/set-password.request"; import { KeysRequest } from "@bitwarden/common/models/request/keys.request"; -import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service"; import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { Utils } from "@bitwarden/common/platform/misc/utils"; @@ -23,6 +22,7 @@ import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/sym import { CsprngArray } from "@bitwarden/common/types/csprng"; import { UserId } from "@bitwarden/common/types/guid"; import { MasterKey, UserKey } from "@bitwarden/common/types/key"; +import { KeyService } from "@bitwarden/key-management"; import { PasswordInputResult } from "../input-password/password-input-result"; @@ -33,7 +33,7 @@ describe("DefaultSetPasswordJitService", () => { let sut: DefaultSetPasswordJitService; let apiService: MockProxy; - let cryptoService: MockProxy; + let keyService: MockProxy; let encryptService: MockProxy; let i18nService: MockProxy; let kdfConfigService: MockProxy; @@ -44,7 +44,7 @@ describe("DefaultSetPasswordJitService", () => { beforeEach(() => { apiService = mock(); - cryptoService = mock(); + keyService = mock(); encryptService = mock(); i18nService = mock(); kdfConfigService = mock(); @@ -55,7 +55,7 @@ describe("DefaultSetPasswordJitService", () => { sut = new DefaultSetPasswordJitService( apiService, - cryptoService, + keyService, encryptService, i18nService, kdfConfigService, @@ -141,14 +141,14 @@ describe("DefaultSetPasswordJitService", () => { function setupSetPasswordMocks(hasUserKey = true) { if (!hasUserKey) { - cryptoService.userKey$.mockReturnValue(of(null)); - cryptoService.makeUserKey.mockResolvedValue(protectedUserKey); + keyService.userKey$.mockReturnValue(of(null)); + keyService.makeUserKey.mockResolvedValue(protectedUserKey); } else { - cryptoService.userKey$.mockReturnValue(of(userKey)); - cryptoService.encryptUserKeyWithMasterKey.mockResolvedValue(protectedUserKey); + keyService.userKey$.mockReturnValue(of(userKey)); + keyService.encryptUserKeyWithMasterKey.mockResolvedValue(protectedUserKey); } - cryptoService.makeKeyPair.mockResolvedValue(keyPair); + keyService.makeKeyPair.mockResolvedValue(keyPair); apiService.setPassword.mockResolvedValue(undefined); masterPasswordService.setForceSetPasswordReason.mockResolvedValue(undefined); @@ -156,9 +156,9 @@ describe("DefaultSetPasswordJitService", () => { userDecryptionOptionsSubject.next(new UserDecryptionOptions({ hasMasterPassword: true })); userDecryptionOptionsService.setUserDecryptionOptions.mockResolvedValue(undefined); kdfConfigService.setKdfConfig.mockResolvedValue(undefined); - cryptoService.setUserKey.mockResolvedValue(undefined); + keyService.setUserKey.mockResolvedValue(undefined); - cryptoService.setPrivateKey.mockResolvedValue(undefined); + keyService.setPrivateKey.mockResolvedValue(undefined); masterPasswordService.setMasterKeyHash.mockResolvedValue(undefined); } @@ -171,7 +171,7 @@ describe("DefaultSetPasswordJitService", () => { return; } - cryptoService.userKey$.mockReturnValue(of(userKey)); + keyService.userKey$.mockReturnValue(of(userKey)); encryptService.rsaEncrypt.mockResolvedValue(userKeyEncString); organizationUserApiService.putOrganizationUserResetPasswordEnrollment.mockResolvedValue( diff --git a/libs/auth/src/angular/set-password-jit/default-set-password-jit.service.ts b/libs/auth/src/angular/set-password-jit/default-set-password-jit.service.ts index 1993877966f..76477a0e5df 100644 --- a/libs/auth/src/angular/set-password-jit/default-set-password-jit.service.ts +++ b/libs/auth/src/angular/set-password-jit/default-set-password-jit.service.ts @@ -13,13 +13,13 @@ import { ForceSetPasswordReason } from "@bitwarden/common/auth/models/domain/for import { PBKDF2KdfConfig } from "@bitwarden/common/auth/models/domain/kdf-config"; import { SetPasswordRequest } from "@bitwarden/common/auth/models/request/set-password.request"; import { KeysRequest } from "@bitwarden/common/models/request/keys.request"; -import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service"; import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { Utils } from "@bitwarden/common/platform/misc/utils"; import { EncString } from "@bitwarden/common/platform/models/domain/enc-string"; import { UserId } from "@bitwarden/common/types/guid"; import { MasterKey, UserKey } from "@bitwarden/common/types/key"; +import { KeyService } from "@bitwarden/key-management"; import { SetPasswordCredentials, @@ -29,7 +29,7 @@ import { export class DefaultSetPasswordJitService implements SetPasswordJitService { constructor( protected apiService: ApiService, - protected cryptoService: CryptoService, + protected keyService: KeyService, protected encryptService: EncryptService, protected i18nService: I18nService, protected kdfConfigService: KdfConfigService, @@ -85,7 +85,7 @@ export class DefaultSetPasswordJitService implements SetPasswordJitService { // User now has a password so update account decryption options in state await this.updateAccountDecryptionProperties(masterKey, kdfConfig, protectedUserKey, userId); - await this.cryptoService.setPrivateKey(keyPair[1].encryptedString, userId); + await this.keyService.setPrivateKey(keyPair[1].encryptedString, userId); await this.masterPasswordService.setMasterKeyHash(localMasterKeyHash, userId); @@ -100,12 +100,12 @@ export class DefaultSetPasswordJitService implements SetPasswordJitService { ): Promise<[UserKey, EncString]> { let protectedUserKey: [UserKey, EncString] = null; - const userKey = await firstValueFrom(this.cryptoService.userKey$(userId)); + const userKey = await firstValueFrom(this.keyService.userKey$(userId)); if (userKey == null) { - protectedUserKey = await this.cryptoService.makeUserKey(masterKey); + protectedUserKey = await this.keyService.makeUserKey(masterKey); } else { - protectedUserKey = await this.cryptoService.encryptUserKeyWithMasterKey(masterKey); + protectedUserKey = await this.keyService.encryptUserKeyWithMasterKey(masterKey); } return protectedUserKey; @@ -114,7 +114,7 @@ export class DefaultSetPasswordJitService implements SetPasswordJitService { private async makeKeyPairAndRequest( protectedUserKey: [UserKey, EncString], ): Promise<[[string, EncString], KeysRequest]> { - const keyPair = await this.cryptoService.makeKeyPair(protectedUserKey[0]); + const keyPair = await this.keyService.makeKeyPair(protectedUserKey[0]); if (keyPair == null) { throw new Error("keyPair not found. Could not set password."); } @@ -136,7 +136,7 @@ export class DefaultSetPasswordJitService implements SetPasswordJitService { await this.userDecryptionOptionsService.setUserDecryptionOptions(userDecryptionOpts); await this.kdfConfigService.setKdfConfig(userId, kdfConfig); await this.masterPasswordService.setMasterKey(masterKey, userId); - await this.cryptoService.setUserKey(protectedUserKey[0], userId); + await this.keyService.setUserKey(protectedUserKey[0], userId); } private async handleResetPasswordAutoEnroll( @@ -153,7 +153,7 @@ export class DefaultSetPasswordJitService implements SetPasswordJitService { const publicKey = Utils.fromB64ToArray(organizationKeys.publicKey); // RSA Encrypt user key with organization public key - const userKey = await firstValueFrom(this.cryptoService.userKey$(userId)); + const userKey = await firstValueFrom(this.keyService.userKey$(userId)); if (userKey == null) { throw new Error("userKey not found. Could not handle reset password auto enroll."); diff --git a/libs/auth/src/common/login-strategies/auth-request-login.strategy.spec.ts b/libs/auth/src/common/login-strategies/auth-request-login.strategy.spec.ts index efc6da51d9f..c0e7d2c00ae 100644 --- a/libs/auth/src/common/login-strategies/auth-request-login.strategy.spec.ts +++ b/libs/auth/src/common/login-strategies/auth-request-login.strategy.spec.ts @@ -11,7 +11,6 @@ import { FakeMasterPasswordService } from "@bitwarden/common/auth/services/maste import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service"; import { VaultTimeoutAction } from "@bitwarden/common/enums/vault-timeout-action.enum"; import { AppIdService } from "@bitwarden/common/platform/abstractions/app-id.service"; -import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service"; import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service"; @@ -24,6 +23,7 @@ import { FakeAccountService, mockAccountServiceWith } from "@bitwarden/common/sp import { CsprngArray } from "@bitwarden/common/types/csprng"; import { UserId } from "@bitwarden/common/types/guid"; import { MasterKey, UserKey } from "@bitwarden/common/types/key"; +import { KeyService } from "@bitwarden/key-management"; import { InternalUserDecryptionOptionsServiceAbstraction } from "../abstractions/user-decryption-options.service.abstraction"; import { AuthRequestLoginCredentials } from "../models/domain/login-credentials"; @@ -37,7 +37,7 @@ import { identityTokenResponseFactory } from "./login.strategy.spec"; describe("AuthRequestLoginStrategy", () => { let cache: AuthRequestLoginStrategyData; - let cryptoService: MockProxy; + let keyService: MockProxy; let encryptService: MockProxy; let apiService: MockProxy; let tokenService: MockProxy; @@ -73,7 +73,7 @@ describe("AuthRequestLoginStrategy", () => { const decMasterKeyHash = "LOCAL_PASSWORD_HASH"; beforeEach(async () => { - cryptoService = mock(); + keyService = mock(); apiService = mock(); tokenService = mock(); appIdService = mock(); @@ -102,7 +102,7 @@ describe("AuthRequestLoginStrategy", () => { deviceTrustService, accountService, masterPasswordService, - cryptoService, + keyService, encryptService, apiService, tokenService, @@ -161,13 +161,13 @@ describe("AuthRequestLoginStrategy", () => { decMasterKeyHash, mockUserId, ); - expect(cryptoService.setMasterKeyEncryptedUserKey).toHaveBeenCalledWith( + expect(keyService.setMasterKeyEncryptedUserKey).toHaveBeenCalledWith( tokenResponse.key, mockUserId, ); - expect(cryptoService.setUserKey).toHaveBeenCalledWith(userKey, mockUserId); + expect(keyService.setUserKey).toHaveBeenCalledWith(userKey, mockUserId); expect(deviceTrustService.trustDeviceIfRequired).toHaveBeenCalled(); - expect(cryptoService.setPrivateKey).toHaveBeenCalledWith(tokenResponse.privateKey, mockUserId); + expect(keyService.setPrivateKey).toHaveBeenCalledWith(tokenResponse.privateKey, mockUserId); }); it("sets keys after a successful authentication when only userKey provided in login credentials", async () => { @@ -189,12 +189,12 @@ describe("AuthRequestLoginStrategy", () => { expect(masterPasswordService.mock.setMasterKeyHash).not.toHaveBeenCalled(); // setMasterKeyEncryptedUserKey, setUserKey, and setPrivateKey should still be called - expect(cryptoService.setMasterKeyEncryptedUserKey).toHaveBeenCalledWith( + expect(keyService.setMasterKeyEncryptedUserKey).toHaveBeenCalledWith( tokenResponse.key, mockUserId, ); - expect(cryptoService.setUserKey).toHaveBeenCalledWith(decUserKey, mockUserId); - expect(cryptoService.setPrivateKey).toHaveBeenCalledWith(tokenResponse.privateKey, mockUserId); + expect(keyService.setUserKey).toHaveBeenCalledWith(decUserKey, mockUserId); + expect(keyService.setPrivateKey).toHaveBeenCalledWith(tokenResponse.privateKey, mockUserId); // trustDeviceIfRequired should be called expect(deviceTrustService.trustDeviceIfRequired).not.toHaveBeenCalled(); diff --git a/libs/auth/src/common/login-strategies/auth-request-login.strategy.ts b/libs/auth/src/common/login-strategies/auth-request-login.strategy.ts index ae0024d2181..3f7e107fa98 100644 --- a/libs/auth/src/common/login-strategies/auth-request-login.strategy.ts +++ b/libs/auth/src/common/login-strategies/auth-request-login.strategy.ts @@ -99,10 +99,10 @@ export class AuthRequestLoginStrategy extends LoginStrategy { const authRequestCredentials = this.cache.value.authRequestCredentials; // User now may or may not have a master password // but set the master key encrypted user key if it exists regardless - await this.cryptoService.setMasterKeyEncryptedUserKey(response.key, userId); + await this.keyService.setMasterKeyEncryptedUserKey(response.key, userId); if (authRequestCredentials.decryptedUserKey) { - await this.cryptoService.setUserKey(authRequestCredentials.decryptedUserKey, userId); + await this.keyService.setUserKey(authRequestCredentials.decryptedUserKey, userId); } else { await this.trySetUserKeyWithMasterKey(userId); @@ -115,7 +115,7 @@ export class AuthRequestLoginStrategy extends LoginStrategy { const masterKey = await firstValueFrom(this.masterPasswordService.masterKey$(userId)); if (masterKey) { const userKey = await this.masterPasswordService.decryptUserKeyWithMasterKey(masterKey); - await this.cryptoService.setUserKey(userKey, userId); + await this.keyService.setUserKey(userKey, userId); } } @@ -123,7 +123,7 @@ export class AuthRequestLoginStrategy extends LoginStrategy { response: IdentityTokenResponse, userId: UserId, ): Promise { - await this.cryptoService.setPrivateKey( + await this.keyService.setPrivateKey( response.privateKey ?? (await this.createKeyPairForOldAccount(userId)), userId, ); diff --git a/libs/auth/src/common/login-strategies/login.strategy.spec.ts b/libs/auth/src/common/login-strategies/login.strategy.spec.ts index 35d62ca76b3..49140cc2cc0 100644 --- a/libs/auth/src/common/login-strategies/login.strategy.spec.ts +++ b/libs/auth/src/common/login-strategies/login.strategy.spec.ts @@ -21,7 +21,6 @@ import { FakeMasterPasswordService } from "@bitwarden/common/auth/services/maste import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service"; import { VaultTimeoutAction } from "@bitwarden/common/enums/vault-timeout-action.enum"; import { AppIdService } from "@bitwarden/common/platform/abstractions/app-id.service"; -import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service"; import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service"; @@ -39,6 +38,7 @@ import { import { CsprngArray } from "@bitwarden/common/types/csprng"; import { UserId } from "@bitwarden/common/types/guid"; import { UserKey, MasterKey } from "@bitwarden/common/types/key"; +import { KeyService } from "@bitwarden/key-management"; import { LoginStrategyServiceAbstraction } from "../abstractions"; import { InternalUserDecryptionOptionsServiceAbstraction } from "../abstractions/user-decryption-options.service.abstraction"; @@ -104,7 +104,7 @@ describe("LoginStrategy", () => { let masterPasswordService: FakeMasterPasswordService; let loginStrategyService: MockProxy; - let cryptoService: MockProxy; + let keyService: MockProxy; let encryptService: MockProxy; let apiService: MockProxy; let tokenService: MockProxy; @@ -129,7 +129,7 @@ describe("LoginStrategy", () => { masterPasswordService = new FakeMasterPasswordService(); loginStrategyService = mock(); - cryptoService = mock(); + keyService = mock(); encryptService = mock(); apiService = mock(); tokenService = mock(); @@ -158,7 +158,7 @@ describe("LoginStrategy", () => { loginStrategyService, accountService, masterPasswordService, - cryptoService, + keyService, encryptService, apiService, tokenService, @@ -321,7 +321,7 @@ describe("LoginStrategy", () => { it("makes a new public and private key for an old account", async () => { const tokenResponse = identityTokenResponseFactory(); tokenResponse.privateKey = null; - cryptoService.makeKeyPair.mockResolvedValue(["PUBLIC_KEY", new EncString("PRIVATE_KEY")]); + keyService.makeKeyPair.mockResolvedValue(["PUBLIC_KEY", new EncString("PRIVATE_KEY")]); apiService.postIdentityToken.mockResolvedValue(tokenResponse); masterPasswordService.masterKeySubject.next(masterKey); @@ -330,10 +330,10 @@ describe("LoginStrategy", () => { await passwordLoginStrategy.logIn(credentials); // User symmetric key must be set before the new RSA keypair is generated - expect(cryptoService.setUserKey).toHaveBeenCalled(); - expect(cryptoService.makeKeyPair).toHaveBeenCalled(); - expect(cryptoService.setUserKey.mock.invocationCallOrder[0]).toBeLessThan( - cryptoService.makeKeyPair.mock.invocationCallOrder[0], + expect(keyService.setUserKey).toHaveBeenCalled(); + expect(keyService.makeKeyPair).toHaveBeenCalled(); + expect(keyService.setUserKey.mock.invocationCallOrder[0]).toBeLessThan( + keyService.makeKeyPair.mock.invocationCallOrder[0], ); expect(apiService.postAccountKeys).toHaveBeenCalled(); @@ -470,7 +470,7 @@ describe("LoginStrategy", () => { loginStrategyService, accountService, masterPasswordService, - cryptoService, + keyService, encryptService, apiService, tokenService, diff --git a/libs/auth/src/common/login-strategies/login.strategy.ts b/libs/auth/src/common/login-strategies/login.strategy.ts index 2e881f978dc..67a286d8195 100644 --- a/libs/auth/src/common/login-strategies/login.strategy.ts +++ b/libs/auth/src/common/login-strategies/login.strategy.ts @@ -25,7 +25,6 @@ import { ClientType } from "@bitwarden/common/enums"; import { VaultTimeoutAction } from "@bitwarden/common/enums/vault-timeout-action.enum"; import { KeysRequest } from "@bitwarden/common/models/request/keys.request"; import { AppIdService } from "@bitwarden/common/platform/abstractions/app-id.service"; -import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service"; import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service"; @@ -34,6 +33,7 @@ import { StateService } from "@bitwarden/common/platform/abstractions/state.serv import { KdfType } from "@bitwarden/common/platform/enums"; import { Account, AccountProfile } from "@bitwarden/common/platform/models/domain/account"; import { UserId } from "@bitwarden/common/types/guid"; +import { KeyService } from "@bitwarden/key-management"; import { InternalUserDecryptionOptionsServiceAbstraction } from "../abstractions/user-decryption-options.service.abstraction"; import { @@ -66,7 +66,7 @@ export abstract class LoginStrategy { constructor( protected accountService: AccountService, protected masterPasswordService: InternalMasterPasswordServiceAbstraction, - protected cryptoService: CryptoService, + protected keyService: KeyService, protected encryptService: EncryptService, protected apiService: ApiService, protected tokenService: TokenService, @@ -284,8 +284,8 @@ export abstract class LoginStrategy { protected async createKeyPairForOldAccount(userId: UserId) { try { - const userKey = await this.cryptoService.getUserKeyWithLegacySupport(userId); - const [publicKey, privateKey] = await this.cryptoService.makeKeyPair(userKey); + const userKey = await this.keyService.getUserKeyWithLegacySupport(userId); + const [publicKey, privateKey] = await this.keyService.makeKeyPair(userKey); await this.apiService.postAccountKeys(new KeysRequest(publicKey, privateKey.encryptedString)); return privateKey.encryptedString; } catch (e) { diff --git a/libs/auth/src/common/login-strategies/password-login.strategy.spec.ts b/libs/auth/src/common/login-strategies/password-login.strategy.spec.ts index 07cbf2424ab..4da6272ccab 100644 --- a/libs/auth/src/common/login-strategies/password-login.strategy.spec.ts +++ b/libs/auth/src/common/login-strategies/password-login.strategy.spec.ts @@ -15,7 +15,6 @@ import { FakeMasterPasswordService } from "@bitwarden/common/auth/services/maste import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service"; import { VaultTimeoutAction } from "@bitwarden/common/enums/vault-timeout-action.enum"; import { AppIdService } from "@bitwarden/common/platform/abstractions/app-id.service"; -import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service"; import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service"; @@ -33,6 +32,7 @@ import { import { CsprngArray } from "@bitwarden/common/types/csprng"; import { UserId } from "@bitwarden/common/types/guid"; import { MasterKey, UserKey } from "@bitwarden/common/types/key"; +import { KeyService } from "@bitwarden/key-management"; import { LoginStrategyServiceAbstraction } from "../abstractions"; import { InternalUserDecryptionOptionsServiceAbstraction } from "../abstractions/user-decryption-options.service.abstraction"; @@ -63,7 +63,7 @@ describe("PasswordLoginStrategy", () => { let masterPasswordService: FakeMasterPasswordService; let loginStrategyService: MockProxy; - let cryptoService: MockProxy; + let keyService: MockProxy; let encryptService: MockProxy; let apiService: MockProxy; let tokenService: MockProxy; @@ -89,7 +89,7 @@ describe("PasswordLoginStrategy", () => { masterPasswordService = new FakeMasterPasswordService(); loginStrategyService = mock(); - cryptoService = mock(); + keyService = mock(); encryptService = mock(); apiService = mock(); tokenService = mock(); @@ -113,10 +113,10 @@ describe("PasswordLoginStrategy", () => { loginStrategyService.makePreloginKey.mockResolvedValue(masterKey); - cryptoService.hashMasterKey + keyService.hashMasterKey .calledWith(masterPassword, expect.anything(), undefined) .mockResolvedValue(hashedPassword); - cryptoService.hashMasterKey + keyService.hashMasterKey .calledWith(masterPassword, expect.anything(), HashPurpose.LocalAuthorization) .mockResolvedValue(localHashedPassword); @@ -129,7 +129,7 @@ describe("PasswordLoginStrategy", () => { loginStrategyService, accountService, masterPasswordService, - cryptoService, + keyService, encryptService, apiService, tokenService, @@ -198,12 +198,9 @@ describe("PasswordLoginStrategy", () => { localHashedPassword, userId, ); - expect(cryptoService.setMasterKeyEncryptedUserKey).toHaveBeenCalledWith( - tokenResponse.key, - userId, - ); - expect(cryptoService.setUserKey).toHaveBeenCalledWith(userKey, userId); - expect(cryptoService.setPrivateKey).toHaveBeenCalledWith(tokenResponse.privateKey, userId); + expect(keyService.setMasterKeyEncryptedUserKey).toHaveBeenCalledWith(tokenResponse.key, userId); + expect(keyService.setUserKey).toHaveBeenCalledWith(userKey, userId); + expect(keyService.setPrivateKey).toHaveBeenCalledWith(tokenResponse.privateKey, userId); }); it("does not force the user to update their master password when there are no requirements", async () => { diff --git a/libs/auth/src/common/login-strategies/password-login.strategy.ts b/libs/auth/src/common/login-strategies/password-login.strategy.ts index 7f73898ff6e..55e869e8229 100644 --- a/libs/auth/src/common/login-strategies/password-login.strategy.ts +++ b/libs/auth/src/common/login-strategies/password-login.strategy.ts @@ -83,15 +83,12 @@ export class PasswordLoginStrategy extends LoginStrategy { data.userEnteredEmail = email; // Hash the password early (before authentication) so we don't persist it in memory in plaintext - data.localMasterKeyHash = await this.cryptoService.hashMasterKey( + data.localMasterKeyHash = await this.keyService.hashMasterKey( masterPassword, data.masterKey, HashPurpose.LocalAuthorization, ); - const serverMasterKeyHash = await this.cryptoService.hashMasterKey( - masterPassword, - data.masterKey, - ); + const serverMasterKeyHash = await this.keyService.hashMasterKey(masterPassword, data.masterKey); data.tokenRequest = new PasswordTokenRequest( email, @@ -182,12 +179,12 @@ export class PasswordLoginStrategy extends LoginStrategy { if (this.encryptionKeyMigrationRequired(response)) { return; } - await this.cryptoService.setMasterKeyEncryptedUserKey(response.key, userId); + await this.keyService.setMasterKeyEncryptedUserKey(response.key, userId); const masterKey = await firstValueFrom(this.masterPasswordService.masterKey$(userId)); if (masterKey) { const userKey = await this.masterPasswordService.decryptUserKeyWithMasterKey(masterKey); - await this.cryptoService.setUserKey(userKey, userId); + await this.keyService.setUserKey(userKey, userId); } } @@ -195,7 +192,7 @@ export class PasswordLoginStrategy extends LoginStrategy { response: IdentityTokenResponse, userId: UserId, ): Promise { - await this.cryptoService.setPrivateKey( + await this.keyService.setPrivateKey( response.privateKey ?? (await this.createKeyPairForOldAccount(userId)), userId, ); diff --git a/libs/auth/src/common/login-strategies/sso-login.strategy.spec.ts b/libs/auth/src/common/login-strategies/sso-login.strategy.spec.ts index f5de10766c0..d9827c2e287 100644 --- a/libs/auth/src/common/login-strategies/sso-login.strategy.spec.ts +++ b/libs/auth/src/common/login-strategies/sso-login.strategy.spec.ts @@ -16,7 +16,6 @@ import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abs import { VaultTimeoutAction } from "@bitwarden/common/enums/vault-timeout-action.enum"; import { ErrorResponse } from "@bitwarden/common/models/response/error.response"; import { AppIdService } from "@bitwarden/common/platform/abstractions/app-id.service"; -import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service"; import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; @@ -30,6 +29,7 @@ import { FakeAccountService, mockAccountServiceWith } from "@bitwarden/common/sp import { CsprngArray } from "@bitwarden/common/types/csprng"; import { UserId } from "@bitwarden/common/types/guid"; import { DeviceKey, UserKey, MasterKey } from "@bitwarden/common/types/key"; +import { KeyService } from "@bitwarden/key-management"; import { AuthRequestServiceAbstraction, @@ -44,7 +44,7 @@ describe("SsoLoginStrategy", () => { let accountService: FakeAccountService; let masterPasswordService: FakeMasterPasswordService; - let cryptoService: MockProxy; + let keyService: MockProxy; let encryptService: MockProxy; let apiService: MockProxy; let tokenService: MockProxy; @@ -79,7 +79,7 @@ describe("SsoLoginStrategy", () => { accountService = mockAccountServiceWith(userId); masterPasswordService = new FakeMasterPasswordService(); - cryptoService = mock(); + keyService = mock(); encryptService = mock(); apiService = mock(); tokenService = mock(); @@ -127,7 +127,7 @@ describe("SsoLoginStrategy", () => { i18nService, accountService, masterPasswordService, - cryptoService, + keyService, encryptService, apiService, tokenService, @@ -174,8 +174,8 @@ describe("SsoLoginStrategy", () => { await ssoLoginStrategy.logIn(credentials); expect(masterPasswordService.mock.setMasterKey).not.toHaveBeenCalled(); - expect(cryptoService.setUserKey).not.toHaveBeenCalled(); - expect(cryptoService.setPrivateKey).not.toHaveBeenCalled(); + expect(keyService.setUserKey).not.toHaveBeenCalled(); + expect(keyService.setPrivateKey).not.toHaveBeenCalled(); }); it("sets master key encrypted user key for existing SSO users", async () => { @@ -187,11 +187,8 @@ describe("SsoLoginStrategy", () => { await ssoLoginStrategy.logIn(credentials); // Assert - expect(cryptoService.setMasterKeyEncryptedUserKey).toHaveBeenCalledTimes(1); - expect(cryptoService.setMasterKeyEncryptedUserKey).toHaveBeenCalledWith( - tokenResponse.key, - userId, - ); + expect(keyService.setMasterKeyEncryptedUserKey).toHaveBeenCalledTimes(1); + expect(keyService.setMasterKeyEncryptedUserKey).toHaveBeenCalledWith(tokenResponse.key, userId); }); describe("Trusted Device Decryption", () => { @@ -247,7 +244,7 @@ describe("SsoLoginStrategy", () => { deviceTrustService.getDeviceKey.mockResolvedValue(mockDeviceKey); deviceTrustService.decryptUserKeyWithDeviceKey.mockResolvedValue(mockUserKey); - const cryptoSvcSetUserKeySpy = jest.spyOn(cryptoService, "setUserKey"); + const cryptoSvcSetUserKeySpy = jest.spyOn(keyService, "setUserKey"); // Act await ssoLoginStrategy.logIn(credentials); @@ -274,7 +271,7 @@ describe("SsoLoginStrategy", () => { await ssoLoginStrategy.logIn(credentials); // Assert - expect(cryptoService.setUserKey).not.toHaveBeenCalled(); + expect(keyService.setUserKey).not.toHaveBeenCalled(); }); describe.each([ @@ -295,7 +292,7 @@ describe("SsoLoginStrategy", () => { await ssoLoginStrategy.logIn(credentials); // Assert - expect(cryptoService.setUserKey).not.toHaveBeenCalled(); + expect(keyService.setUserKey).not.toHaveBeenCalled(); }); }); @@ -314,7 +311,7 @@ describe("SsoLoginStrategy", () => { await ssoLoginStrategy.logIn(credentials); // Assert - expect(cryptoService.setUserKey).not.toHaveBeenCalled(); + expect(keyService.setUserKey).not.toHaveBeenCalled(); }); it("logs when a device key is found but no decryption keys were recieved in token response", async () => { @@ -365,7 +362,7 @@ describe("SsoLoginStrategy", () => { it("sets the user key using master key and hash from approved admin request if exists", async () => { apiService.postIdentityToken.mockResolvedValue(tokenResponse); - cryptoService.hasUserKey.mockResolvedValue(true); + keyService.hasUserKey.mockResolvedValue(true); const adminAuthResponse = { id: "1", publicKey: "PRIVATE" as any, @@ -383,7 +380,7 @@ describe("SsoLoginStrategy", () => { it("sets the user key from approved admin request if exists", async () => { apiService.postIdentityToken.mockResolvedValue(tokenResponse); - cryptoService.hasUserKey.mockResolvedValue(true); + keyService.hasUserKey.mockResolvedValue(true); const adminAuthResponse = { id: "1", publicKey: "PRIVATE" as any, @@ -400,7 +397,7 @@ describe("SsoLoginStrategy", () => { it("attempts to establish a trusted device if successful", async () => { apiService.postIdentityToken.mockResolvedValue(tokenResponse); - cryptoService.hasUserKey.mockResolvedValue(true); + keyService.hasUserKey.mockResolvedValue(true); const adminAuthResponse = { id: "1", publicKey: "PRIVATE" as any, @@ -438,7 +435,7 @@ describe("SsoLoginStrategy", () => { requestApproved: true, }; apiService.getAuthRequest.mockResolvedValue(adminAuthResponse as AuthRequestResponse); - cryptoService.hasUserKey.mockResolvedValue(false); + keyService.hasUserKey.mockResolvedValue(false); deviceTrustService.getDeviceKey.mockResolvedValue("DEVICE_KEY" as any); await ssoLoginStrategy.logIn(credentials); @@ -502,7 +499,7 @@ describe("SsoLoginStrategy", () => { undefined, undefined, ); - expect(cryptoService.setUserKey).toHaveBeenCalledWith(userKey, userId); + expect(keyService.setUserKey).toHaveBeenCalledWith(userKey, userId); }); }); @@ -558,7 +555,7 @@ describe("SsoLoginStrategy", () => { undefined, undefined, ); - expect(cryptoService.setUserKey).toHaveBeenCalledWith(userKey, userId); + expect(keyService.setUserKey).toHaveBeenCalledWith(userKey, userId); }); }); }); diff --git a/libs/auth/src/common/login-strategies/sso-login.strategy.ts b/libs/auth/src/common/login-strategies/sso-login.strategy.ts index 5ddf7428d24..d2660eef8a2 100644 --- a/libs/auth/src/common/login-strategies/sso-login.strategy.ts +++ b/libs/auth/src/common/login-strategies/sso-login.strategy.ts @@ -192,7 +192,7 @@ export class SsoLoginStrategy extends LoginStrategy { if (masterKeyEncryptedUserKey) { // set the master key encrypted user key if it exists - await this.cryptoService.setMasterKeyEncryptedUserKey(masterKeyEncryptedUserKey, userId); + await this.keyService.setMasterKeyEncryptedUserKey(masterKeyEncryptedUserKey, userId); } const userDecryptionOptions = tokenResponse?.userDecryptionOptions; @@ -205,7 +205,7 @@ export class SsoLoginStrategy extends LoginStrategy { // Using it will clear it from state and future requests will use the device key. await this.trySetUserKeyWithApprovedAdminRequestIfExists(userId); - const hasUserKey = await this.cryptoService.hasUserKey(userId); + const hasUserKey = await this.keyService.hasUserKey(userId); // Only try to set user key with device key if admin approval request was not successful. if (!hasUserKey) { @@ -267,7 +267,7 @@ export class SsoLoginStrategy extends LoginStrategy { ); } - if (await this.cryptoService.hasUserKey()) { + if (await this.keyService.hasUserKey()) { // Now that we have a decrypted user key in memory, we can check if we // need to establish trust on the current device await this.deviceTrustService.trustDeviceIfRequired(userId); @@ -323,7 +323,7 @@ export class SsoLoginStrategy extends LoginStrategy { ); if (userKey) { - await this.cryptoService.setUserKey(userKey, userId); + await this.keyService.setUserKey(userKey, userId); } } @@ -339,7 +339,7 @@ export class SsoLoginStrategy extends LoginStrategy { } const userKey = await this.masterPasswordService.decryptUserKeyWithMasterKey(masterKey); - await this.cryptoService.setUserKey(userKey, userId); + await this.keyService.setUserKey(userKey, userId); } protected override async setPrivateKey( @@ -349,7 +349,7 @@ export class SsoLoginStrategy extends LoginStrategy { const newSsoUser = tokenResponse.key == null; if (!newSsoUser) { - await this.cryptoService.setPrivateKey( + await this.keyService.setPrivateKey( tokenResponse.privateKey ?? (await this.createKeyPairForOldAccount(userId)), userId, ); diff --git a/libs/auth/src/common/login-strategies/user-api-login.strategy.spec.ts b/libs/auth/src/common/login-strategies/user-api-login.strategy.spec.ts index d299a8e0ced..14fafcb58c3 100644 --- a/libs/auth/src/common/login-strategies/user-api-login.strategy.spec.ts +++ b/libs/auth/src/common/login-strategies/user-api-login.strategy.spec.ts @@ -10,7 +10,6 @@ import { FakeMasterPasswordService } from "@bitwarden/common/auth/services/maste import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service"; import { VaultTimeoutAction } from "@bitwarden/common/enums/vault-timeout-action.enum"; import { AppIdService } from "@bitwarden/common/platform/abstractions/app-id.service"; -import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service"; import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; import { Environment, @@ -27,6 +26,7 @@ import { FakeAccountService, mockAccountServiceWith } from "@bitwarden/common/sp import { CsprngArray } from "@bitwarden/common/types/csprng"; import { UserId } from "@bitwarden/common/types/guid"; import { UserKey, MasterKey } from "@bitwarden/common/types/key"; +import { KeyService } from "@bitwarden/key-management"; import { InternalUserDecryptionOptionsServiceAbstraction } from "../abstractions/user-decryption-options.service.abstraction"; import { UserApiLoginCredentials } from "../models/domain/login-credentials"; @@ -39,7 +39,7 @@ describe("UserApiLoginStrategy", () => { let accountService: FakeAccountService; let masterPasswordService: FakeMasterPasswordService; - let cryptoService: MockProxy; + let keyService: MockProxy; let encryptService: MockProxy; let apiService: MockProxy; let tokenService: MockProxy; @@ -72,7 +72,7 @@ describe("UserApiLoginStrategy", () => { accountService = mockAccountServiceWith(userId); masterPasswordService = new FakeMasterPasswordService(); - cryptoService = mock(); + keyService = mock(); apiService = mock(); tokenService = mock(); appIdService = mock(); @@ -100,7 +100,7 @@ describe("UserApiLoginStrategy", () => { keyConnectorService, accountService, masterPasswordService, - cryptoService, + keyService, encryptService, apiService, tokenService, @@ -175,11 +175,8 @@ describe("UserApiLoginStrategy", () => { await apiLogInStrategy.logIn(credentials); - expect(cryptoService.setMasterKeyEncryptedUserKey).toHaveBeenCalledWith( - tokenResponse.key, - userId, - ); - expect(cryptoService.setPrivateKey).toHaveBeenCalledWith(tokenResponse.privateKey, userId); + expect(keyService.setMasterKeyEncryptedUserKey).toHaveBeenCalledWith(tokenResponse.key, userId); + expect(keyService.setPrivateKey).toHaveBeenCalledWith(tokenResponse.privateKey, userId); }); it("gets and sets the master key if Key Connector is enabled", async () => { @@ -219,6 +216,6 @@ describe("UserApiLoginStrategy", () => { undefined, undefined, ); - expect(cryptoService.setUserKey).toHaveBeenCalledWith(userKey, userId); + expect(keyService.setUserKey).toHaveBeenCalledWith(userKey, userId); }); }); diff --git a/libs/auth/src/common/login-strategies/user-api-login.strategy.ts b/libs/auth/src/common/login-strategies/user-api-login.strategy.ts index 3b112c79a0f..4ae95fdbc70 100644 --- a/libs/auth/src/common/login-strategies/user-api-login.strategy.ts +++ b/libs/auth/src/common/login-strategies/user-api-login.strategy.ts @@ -64,13 +64,13 @@ export class UserApiLoginStrategy extends LoginStrategy { response: IdentityTokenResponse, userId: UserId, ): Promise { - await this.cryptoService.setMasterKeyEncryptedUserKey(response.key, userId); + await this.keyService.setMasterKeyEncryptedUserKey(response.key, userId); if (response.apiUseKeyConnector) { const masterKey = await firstValueFrom(this.masterPasswordService.masterKey$(userId)); if (masterKey) { const userKey = await this.masterPasswordService.decryptUserKeyWithMasterKey(masterKey); - await this.cryptoService.setUserKey(userKey, userId); + await this.keyService.setUserKey(userKey, userId); } } } @@ -79,7 +79,7 @@ export class UserApiLoginStrategy extends LoginStrategy { response: IdentityTokenResponse, userId: UserId, ): Promise { - await this.cryptoService.setPrivateKey( + await this.keyService.setPrivateKey( response.privateKey ?? (await this.createKeyPairForOldAccount(userId)), userId, ); diff --git a/libs/auth/src/common/login-strategies/webauthn-login.strategy.spec.ts b/libs/auth/src/common/login-strategies/webauthn-login.strategy.spec.ts index e4b1f740310..88392b57c53 100644 --- a/libs/auth/src/common/login-strategies/webauthn-login.strategy.spec.ts +++ b/libs/auth/src/common/login-strategies/webauthn-login.strategy.spec.ts @@ -13,7 +13,6 @@ import { WebAuthnLoginAssertionResponseRequest } from "@bitwarden/common/auth/se import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service"; import { VaultTimeoutAction } from "@bitwarden/common/enums/vault-timeout-action.enum"; import { AppIdService } from "@bitwarden/common/platform/abstractions/app-id.service"; -import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service"; import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service"; @@ -25,6 +24,7 @@ import { VaultTimeoutSettingsService } from "@bitwarden/common/services/vault-ti import { FakeAccountService, mockAccountServiceWith } from "@bitwarden/common/spec"; import { UserId } from "@bitwarden/common/types/guid"; import { PrfKey, UserKey } from "@bitwarden/common/types/key"; +import { KeyService } from "@bitwarden/key-management"; import { InternalUserDecryptionOptionsServiceAbstraction } from "../abstractions/user-decryption-options.service.abstraction"; import { WebAuthnLoginCredentials } from "../models/domain/login-credentials"; @@ -37,7 +37,7 @@ describe("WebAuthnLoginStrategy", () => { let accountService: FakeAccountService; let masterPasswordService: FakeMasterPasswordService; - let cryptoService!: MockProxy; + let keyService!: MockProxy; let encryptService!: MockProxy; let apiService!: MockProxy; let tokenService!: MockProxy; @@ -80,7 +80,7 @@ describe("WebAuthnLoginStrategy", () => { accountService = mockAccountServiceWith(userId); masterPasswordService = new FakeMasterPasswordService(); - cryptoService = mock(); + keyService = mock(); encryptService = mock(); apiService = mock(); tokenService = mock(); @@ -105,7 +105,7 @@ describe("WebAuthnLoginStrategy", () => { cache, accountService, masterPasswordService, - cryptoService, + keyService, encryptService, apiService, tokenService, @@ -233,8 +233,8 @@ describe("WebAuthnLoginStrategy", () => { // Assert // Master key encrypted user key should be set - expect(cryptoService.setMasterKeyEncryptedUserKey).toHaveBeenCalledTimes(1); - expect(cryptoService.setMasterKeyEncryptedUserKey).toHaveBeenCalledWith( + expect(keyService.setMasterKeyEncryptedUserKey).toHaveBeenCalledTimes(1); + expect(keyService.setMasterKeyEncryptedUserKey).toHaveBeenCalledWith( idTokenResponse.key, userId, ); @@ -249,8 +249,8 @@ describe("WebAuthnLoginStrategy", () => { idTokenResponse.userDecryptionOptions.webAuthnPrfOption.encryptedUserKey, mockPrfPrivateKey, ); - expect(cryptoService.setUserKey).toHaveBeenCalledWith(mockUserKey, userId); - expect(cryptoService.setPrivateKey).toHaveBeenCalledWith(idTokenResponse.privateKey, userId); + expect(keyService.setUserKey).toHaveBeenCalledWith(mockUserKey, userId); + expect(keyService.setPrivateKey).toHaveBeenCalledWith(idTokenResponse.privateKey, userId); // Master key and private key should not be set expect(masterPasswordService.mock.setMasterKey).not.toHaveBeenCalled(); @@ -274,7 +274,7 @@ describe("WebAuthnLoginStrategy", () => { // Assert expect(encryptService.decryptToBytes).not.toHaveBeenCalled(); expect(encryptService.rsaDecrypt).not.toHaveBeenCalled(); - expect(cryptoService.setUserKey).not.toHaveBeenCalled(); + expect(keyService.setUserKey).not.toHaveBeenCalled(); }); describe.each([ @@ -294,7 +294,7 @@ describe("WebAuthnLoginStrategy", () => { await webAuthnLoginStrategy.logIn(webAuthnCredentials); // Assert - expect(cryptoService.setUserKey).not.toHaveBeenCalled(); + expect(keyService.setUserKey).not.toHaveBeenCalled(); }); }); @@ -313,7 +313,7 @@ describe("WebAuthnLoginStrategy", () => { await webAuthnLoginStrategy.logIn(webAuthnCredentials); // Assert - expect(cryptoService.setUserKey).not.toHaveBeenCalled(); + expect(keyService.setUserKey).not.toHaveBeenCalled(); }); it("does not set the user key when the encrypted user key decryption fails", async () => { @@ -331,7 +331,7 @@ describe("WebAuthnLoginStrategy", () => { await webAuthnLoginStrategy.logIn(webAuthnCredentials); // Assert - expect(cryptoService.setUserKey).not.toHaveBeenCalled(); + expect(keyService.setUserKey).not.toHaveBeenCalled(); }); }); diff --git a/libs/auth/src/common/login-strategies/webauthn-login.strategy.ts b/libs/auth/src/common/login-strategies/webauthn-login.strategy.ts index c5451d13df5..df671080986 100644 --- a/libs/auth/src/common/login-strategies/webauthn-login.strategy.ts +++ b/libs/auth/src/common/login-strategies/webauthn-login.strategy.ts @@ -66,7 +66,7 @@ export class WebAuthnLoginStrategy extends LoginStrategy { if (masterKeyEncryptedUserKey) { // set the master key encrypted user key if it exists - await this.cryptoService.setMasterKeyEncryptedUserKey(masterKeyEncryptedUserKey, userId); + await this.keyService.setMasterKeyEncryptedUserKey(masterKeyEncryptedUserKey, userId); } const userDecryptionOptions = idTokenResponse?.userDecryptionOptions; @@ -93,7 +93,7 @@ export class WebAuthnLoginStrategy extends LoginStrategy { ); if (userKey) { - await this.cryptoService.setUserKey(new SymmetricCryptoKey(userKey) as UserKey, userId); + await this.keyService.setUserKey(new SymmetricCryptoKey(userKey) as UserKey, userId); } } } @@ -102,7 +102,7 @@ export class WebAuthnLoginStrategy extends LoginStrategy { response: IdentityTokenResponse, userId: UserId, ): Promise { - await this.cryptoService.setPrivateKey( + await this.keyService.setPrivateKey( response.privateKey ?? (await this.createKeyPairForOldAccount(userId)), userId, ); diff --git a/libs/auth/src/common/services/auth-request/auth-request.service.spec.ts b/libs/auth/src/common/services/auth-request/auth-request.service.spec.ts index 58dbae6d789..a4f1d5d9724 100644 --- a/libs/auth/src/common/services/auth-request/auth-request.service.spec.ts +++ b/libs/auth/src/common/services/auth-request/auth-request.service.spec.ts @@ -5,7 +5,6 @@ import { AuthRequestResponse } from "@bitwarden/common/auth/models/response/auth import { FakeMasterPasswordService } from "@bitwarden/common/auth/services/master-password/fake-master-password.service"; import { AuthRequestPushNotification } from "@bitwarden/common/models/response/notification.response"; import { AppIdService } from "@bitwarden/common/platform/abstractions/app-id.service"; -import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service"; import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; import { Utils } from "@bitwarden/common/platform/misc/utils"; import { EncString } from "@bitwarden/common/platform/models/domain/enc-string"; @@ -14,6 +13,7 @@ import { StateProvider } from "@bitwarden/common/platform/state"; import { FakeAccountService, mockAccountServiceWith } from "@bitwarden/common/spec"; import { UserId } from "@bitwarden/common/types/guid"; import { MasterKey, UserKey } from "@bitwarden/common/types/key"; +import { KeyService } from "@bitwarden/key-management"; import { AuthRequestService } from "./auth-request.service"; @@ -24,7 +24,7 @@ describe("AuthRequestService", () => { let accountService: FakeAccountService; let masterPasswordService: FakeMasterPasswordService; const appIdService = mock(); - const cryptoService = mock(); + const keyService = mock(); const encryptService = mock(); const apiService = mock(); @@ -41,7 +41,7 @@ describe("AuthRequestService", () => { appIdService, accountService, masterPasswordService, - cryptoService, + keyService, encryptService, apiService, stateProvider, @@ -115,7 +115,7 @@ describe("AuthRequestService", () => { }); it("should use the user key if the master key and hash do not exist", async () => { - cryptoService.getUserKey.mockResolvedValueOnce({ key: new Uint8Array(64) } as UserKey); + keyService.getUserKey.mockResolvedValueOnce({ key: new Uint8Array(64) } as UserKey); await sut.approveOrDenyAuthRequest( true, @@ -135,7 +135,7 @@ describe("AuthRequestService", () => { const mockDecryptedUserKey = {} as UserKey; jest.spyOn(sut, "decryptPubKeyEncryptedUserKey").mockResolvedValueOnce(mockDecryptedUserKey); - cryptoService.setUserKey.mockResolvedValueOnce(undefined); + keyService.setUserKey.mockResolvedValueOnce(undefined); // Act await sut.setUserKeyAfterDecryptingSharedUserKey( @@ -149,7 +149,7 @@ describe("AuthRequestService", () => { mockAuthReqResponse.key, mockPrivateKey, ); - expect(cryptoService.setUserKey).toBeCalledWith(mockDecryptedUserKey, mockUserId); + expect(keyService.setUserKey).toBeCalledWith(mockDecryptedUserKey, mockUserId); }); }); @@ -175,7 +175,7 @@ describe("AuthRequestService", () => { masterPasswordService.mock.decryptUserKeyWithMasterKey.mockResolvedValue( mockDecryptedUserKey, ); - cryptoService.setUserKey.mockResolvedValueOnce(undefined); + keyService.setUserKey.mockResolvedValueOnce(undefined); // Act await sut.setKeysAfterDecryptingSharedMasterKeyAndHash( @@ -203,7 +203,7 @@ describe("AuthRequestService", () => { undefined, undefined, ); - expect(cryptoService.setUserKey).toHaveBeenCalledWith(mockDecryptedUserKey, mockUserId); + expect(keyService.setUserKey).toHaveBeenCalledWith(mockDecryptedUserKey, mockUserId); }); }); diff --git a/libs/auth/src/common/services/auth-request/auth-request.service.ts b/libs/auth/src/common/services/auth-request/auth-request.service.ts index 51926d65983..0e416a4a255 100644 --- a/libs/auth/src/common/services/auth-request/auth-request.service.ts +++ b/libs/auth/src/common/services/auth-request/auth-request.service.ts @@ -9,7 +9,6 @@ import { PasswordlessAuthRequest } from "@bitwarden/common/auth/models/request/p import { AuthRequestResponse } from "@bitwarden/common/auth/models/response/auth-request.response"; import { AuthRequestPushNotification } from "@bitwarden/common/models/response/notification.response"; import { AppIdService } from "@bitwarden/common/platform/abstractions/app-id.service"; -import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service"; import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; import { Utils } from "@bitwarden/common/platform/misc/utils"; import { EncString } from "@bitwarden/common/platform/models/domain/enc-string"; @@ -21,6 +20,7 @@ import { } from "@bitwarden/common/platform/state"; import { UserId } from "@bitwarden/common/types/guid"; import { MasterKey, UserKey } from "@bitwarden/common/types/key"; +import { KeyService } from "@bitwarden/key-management"; import { AuthRequestServiceAbstraction } from "../../abstractions/auth-request.service.abstraction"; @@ -45,7 +45,7 @@ export class AuthRequestService implements AuthRequestServiceAbstraction { private appIdService: AppIdService, private accountService: AccountService, private masterPasswordService: InternalMasterPasswordServiceAbstraction, - private cryptoService: CryptoService, + private keyService: KeyService, private encryptService: EncryptService, private apiService: ApiService, private stateProvider: StateProvider, @@ -111,7 +111,7 @@ export class AuthRequestService implements AuthRequestServiceAbstraction { ); keyToEncrypt = masterKey.encKey; } else { - const userKey = await this.cryptoService.getUserKey(); + const userKey = await this.keyService.getUserKey(); keyToEncrypt = userKey.key; } @@ -135,7 +135,7 @@ export class AuthRequestService implements AuthRequestServiceAbstraction { authReqResponse.key, authReqPrivateKey, ); - await this.cryptoService.setUserKey(userKey, userId); + await this.keyService.setUserKey(userKey, userId); } async setKeysAfterDecryptingSharedMasterKeyAndHash( @@ -156,7 +156,7 @@ export class AuthRequestService implements AuthRequestServiceAbstraction { await this.masterPasswordService.setMasterKey(masterKey, userId); await this.masterPasswordService.setMasterKeyHash(masterKeyHash, userId); - await this.cryptoService.setUserKey(userKey, userId); + await this.keyService.setUserKey(userKey, userId); } // Decryption helpers @@ -203,6 +203,6 @@ export class AuthRequestService implements AuthRequestServiceAbstraction { } async getFingerprintPhrase(email: string, publicKey: Uint8Array): Promise { - return (await this.cryptoService.getFingerprint(email.toLowerCase(), publicKey)).join("-"); + return (await this.keyService.getFingerprint(email.toLowerCase(), publicKey)).join("-"); } } diff --git a/libs/auth/src/common/services/login-strategies/login-strategy.service.spec.ts b/libs/auth/src/common/services/login-strategies/login-strategy.service.spec.ts index 14662bb4b89..8647260ce5a 100644 --- a/libs/auth/src/common/services/login-strategies/login-strategy.service.spec.ts +++ b/libs/auth/src/common/services/login-strategies/login-strategy.service.spec.ts @@ -20,7 +20,6 @@ import { FakeMasterPasswordService } from "@bitwarden/common/auth/services/maste import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service"; import { VaultTimeoutAction } from "@bitwarden/common/enums/vault-timeout-action.enum"; import { AppIdService } from "@bitwarden/common/platform/abstractions/app-id.service"; -import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service"; import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; @@ -38,6 +37,7 @@ import { } from "@bitwarden/common/spec"; import { PasswordStrengthServiceAbstraction } from "@bitwarden/common/tools/password-strength"; import { UserId } from "@bitwarden/common/types/guid"; +import { KeyService } from "@bitwarden/key-management"; import { AuthRequestServiceAbstraction, @@ -54,7 +54,7 @@ describe("LoginStrategyService", () => { let accountService: FakeAccountService; let masterPasswordService: FakeMasterPasswordService; - let cryptoService: MockProxy; + let keyService: MockProxy; let apiService: MockProxy; let tokenService: MockProxy; let appIdService: MockProxy; @@ -85,7 +85,7 @@ describe("LoginStrategyService", () => { beforeEach(() => { accountService = mockAccountServiceWith(userId); masterPasswordService = new FakeMasterPasswordService(); - cryptoService = mock(); + keyService = mock(); apiService = mock(); tokenService = mock(); appIdService = mock(); @@ -112,7 +112,7 @@ describe("LoginStrategyService", () => { sut = new LoginStrategyService( accountService, masterPasswordService, - cryptoService, + keyService, apiService, tokenService, appIdService, diff --git a/libs/auth/src/common/services/login-strategies/login-strategy.service.ts b/libs/auth/src/common/services/login-strategies/login-strategy.service.ts index 35f2b90bbda..721ee984974 100644 --- a/libs/auth/src/common/services/login-strategies/login-strategy.service.ts +++ b/libs/auth/src/common/services/login-strategies/login-strategy.service.ts @@ -29,7 +29,6 @@ import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abs import { PreloginRequest } from "@bitwarden/common/models/request/prelogin.request"; import { ErrorResponse } from "@bitwarden/common/models/response/error.response"; import { AppIdService } from "@bitwarden/common/platform/abstractions/app-id.service"; -import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service"; import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; @@ -43,6 +42,7 @@ import { GlobalState, GlobalStateProvider } from "@bitwarden/common/platform/sta import { DeviceTrustServiceAbstraction } from "@bitwarden/common/src/auth/abstractions/device-trust.service.abstraction"; import { PasswordStrengthServiceAbstraction } from "@bitwarden/common/tools/password-strength"; import { MasterKey } from "@bitwarden/common/types/key"; +import { KeyService } from "@bitwarden/key-management"; import { AuthRequestServiceAbstraction, LoginStrategyServiceAbstraction } from "../../abstractions"; import { InternalUserDecryptionOptionsServiceAbstraction } from "../../abstractions/user-decryption-options.service.abstraction"; @@ -91,7 +91,7 @@ export class LoginStrategyService implements LoginStrategyServiceAbstraction { constructor( protected accountService: AccountService, protected masterPasswordService: InternalMasterPasswordServiceAbstraction, - protected cryptoService: CryptoService, + protected keyService: KeyService, protected apiService: ApiService, protected tokenService: TokenService, protected appIdService: AppIdService, @@ -267,7 +267,7 @@ export class LoginStrategyService implements LoginStrategyServiceAbstraction { kdfConfig.validateKdfConfigForPrelogin(); - return await this.cryptoService.makeMasterKey(masterPassword, email, kdfConfig); + return await this.keyService.makeMasterKey(masterPassword, email, kdfConfig); } private async clearCache(): Promise { @@ -319,7 +319,7 @@ export class LoginStrategyService implements LoginStrategyServiceAbstraction { const sharedDeps: ConstructorParameters = [ this.accountService, this.masterPasswordService, - this.cryptoService, + this.keyService, this.encryptService, this.apiService, this.tokenService, diff --git a/libs/common/src/auth/abstractions/webauthn/webauthn-login-prf-crypto.service.abstraction.ts b/libs/common/src/auth/abstractions/webauthn/webauthn-login-prf-key.service.abstraction.ts similarity index 88% rename from libs/common/src/auth/abstractions/webauthn/webauthn-login-prf-crypto.service.abstraction.ts rename to libs/common/src/auth/abstractions/webauthn/webauthn-login-prf-key.service.abstraction.ts index b2c59e76afb..c3c09466091 100644 --- a/libs/common/src/auth/abstractions/webauthn/webauthn-login-prf-crypto.service.abstraction.ts +++ b/libs/common/src/auth/abstractions/webauthn/webauthn-login-prf-key.service.abstraction.ts @@ -3,7 +3,7 @@ import { PrfKey } from "../../../types/key"; /** * Contains methods for all crypto operations specific to the WebAuthn login flow. */ -export abstract class WebAuthnLoginPrfCryptoServiceAbstraction { +export abstract class WebAuthnLoginPrfKeyServiceAbstraction { /** * Get the salt used to generate the PRF-output used when logging in with WebAuthn. */ diff --git a/libs/common/src/auth/models/request/registration/register-finish.request.ts b/libs/common/src/auth/models/request/registration/register-finish.request.ts index 6a36bf82139..7ffac6bfe6c 100644 --- a/libs/common/src/auth/models/request/registration/register-finish.request.ts +++ b/libs/common/src/auth/models/request/registration/register-finish.request.ts @@ -21,6 +21,8 @@ export class RegisterFinishRequest { public orgSponsoredFreeFamilyPlanToken?: string, public acceptEmergencyAccessInviteToken?: string, public acceptEmergencyAccessId?: string, + public providerInviteToken?: string, + public providerUserId?: string, // Org Invite data (only applies on web) public organizationUserId?: string, diff --git a/libs/common/src/auth/services/auth.service.spec.ts b/libs/common/src/auth/services/auth.service.spec.ts index 9a93a4207b7..5663384714d 100644 --- a/libs/common/src/auth/services/auth.service.spec.ts +++ b/libs/common/src/auth/services/auth.service.spec.ts @@ -1,6 +1,7 @@ import { MockProxy, mock } from "jest-mock-extended"; import { firstValueFrom, of } from "rxjs"; +import { KeyService } from "../../../../key-management/src/abstractions/key.service"; import { FakeAccountService, makeStaticByteArray, @@ -8,7 +9,6 @@ import { trackEmissions, } from "../../../spec"; import { ApiService } from "../../abstractions/api.service"; -import { CryptoService } from "../../platform/abstractions/crypto.service"; import { MessagingService } from "../../platform/abstractions/messaging.service"; import { StateService } from "../../platform/abstractions/state.service"; import { Utils } from "../../platform/misc/utils"; @@ -25,7 +25,7 @@ describe("AuthService", () => { let accountService: FakeAccountService; let messagingService: MockProxy; - let cryptoService: MockProxy; + let keyService: MockProxy; let apiService: MockProxy; let stateService: MockProxy; let tokenService: MockProxy; @@ -36,7 +36,7 @@ describe("AuthService", () => { beforeEach(() => { accountService = mockAccountServiceWith(userId); messagingService = mock(); - cryptoService = mock(); + keyService = mock(); apiService = mock(); stateService = mock(); tokenService = mock(); @@ -44,7 +44,7 @@ describe("AuthService", () => { sut = new AuthService( accountService, messagingService, - cryptoService, + keyService, apiService, stateService, tokenService, @@ -63,7 +63,7 @@ describe("AuthService", () => { beforeEach(() => { accountService.activeAccountSubject.next(accountInfo); tokenService.hasAccessToken$.mockReturnValue(of(true)); - cryptoService.getInMemoryUserKeyFor$.mockReturnValue(of(undefined)); + keyService.getInMemoryUserKeyFor$.mockReturnValue(of(undefined)); }); it("emits LoggedOut when there is no active account", async () => { @@ -84,7 +84,7 @@ describe("AuthService", () => { it("emits LoggedOut when there is no access token but has a user key", async () => { tokenService.hasAccessToken$.mockReturnValue(of(false)); - cryptoService.getInMemoryUserKeyFor$.mockReturnValue(of(userKey)); + keyService.getInMemoryUserKeyFor$.mockReturnValue(of(userKey)); expect(await firstValueFrom(sut.activeAccountStatus$)).toEqual( AuthenticationStatus.LoggedOut, @@ -93,14 +93,14 @@ describe("AuthService", () => { it("emits Locked when there is an access token and no user key", async () => { tokenService.hasAccessToken$.mockReturnValue(of(true)); - cryptoService.getInMemoryUserKeyFor$.mockReturnValue(of(undefined)); + keyService.getInMemoryUserKeyFor$.mockReturnValue(of(undefined)); expect(await firstValueFrom(sut.activeAccountStatus$)).toEqual(AuthenticationStatus.Locked); }); it("emits Unlocked when there is an access token and user key", async () => { tokenService.hasAccessToken$.mockReturnValue(of(true)); - cryptoService.getInMemoryUserKeyFor$.mockReturnValue(of(userKey)); + keyService.getInMemoryUserKeyFor$.mockReturnValue(of(userKey)); expect(await firstValueFrom(sut.activeAccountStatus$)).toEqual(AuthenticationStatus.Unlocked); }); @@ -117,7 +117,7 @@ describe("AuthService", () => { const emissions = trackEmissions(sut.activeAccountStatus$); tokenService.hasAccessToken$.mockReturnValue(of(true)); - cryptoService.getInMemoryUserKeyFor$.mockReturnValue(of(userKey)); + keyService.getInMemoryUserKeyFor$.mockReturnValue(of(userKey)); accountService.activeAccountSubject.next(accountInfo2); expect(emissions).toEqual([AuthenticationStatus.Locked, AuthenticationStatus.Unlocked]); @@ -150,7 +150,7 @@ describe("AuthService", () => { describe("authStatusFor$", () => { beforeEach(() => { tokenService.hasAccessToken$.mockReturnValue(of(true)); - cryptoService.getInMemoryUserKeyFor$.mockReturnValue(of(undefined)); + keyService.getInMemoryUserKeyFor$.mockReturnValue(of(undefined)); }); it.each([null, undefined, "not a userId"])( @@ -172,14 +172,14 @@ describe("AuthService", () => { it("emits Locked when there is an access token and no user key", async () => { tokenService.hasAccessToken$.mockReturnValue(of(true)); - cryptoService.getInMemoryUserKeyFor$.mockReturnValue(of(undefined)); + keyService.getInMemoryUserKeyFor$.mockReturnValue(of(undefined)); expect(await firstValueFrom(sut.authStatusFor$(userId))).toEqual(AuthenticationStatus.Locked); }); it("emits Unlocked when there is an access token and user key", async () => { tokenService.hasAccessToken$.mockReturnValue(of(true)); - cryptoService.getInMemoryUserKeyFor$.mockReturnValue(of(userKey)); + keyService.getInMemoryUserKeyFor$.mockReturnValue(of(userKey)); expect(await firstValueFrom(sut.authStatusFor$(userId))).toEqual( AuthenticationStatus.Unlocked, diff --git a/libs/common/src/auth/services/auth.service.ts b/libs/common/src/auth/services/auth.service.ts index 307da55a5eb..2b8cd7919fd 100644 --- a/libs/common/src/auth/services/auth.service.ts +++ b/libs/common/src/auth/services/auth.service.ts @@ -9,8 +9,8 @@ import { switchMap, } from "rxjs"; +import { KeyService } from "../../../../key-management/src/abstractions/key.service"; import { ApiService } from "../../abstractions/api.service"; -import { CryptoService } from "../../platform/abstractions/crypto.service"; import { StateService } from "../../platform/abstractions/state.service"; import { MessageSender } from "../../platform/messaging"; import { Utils } from "../../platform/misc/utils"; @@ -27,7 +27,7 @@ export class AuthService implements AuthServiceAbstraction { constructor( protected accountService: AccountService, protected messageSender: MessageSender, - protected cryptoService: CryptoService, + protected keyService: KeyService, protected apiService: ApiService, protected stateService: StateService, private tokenService: TokenService, @@ -69,7 +69,7 @@ export class AuthService implements AuthServiceAbstraction { } return combineLatest([ - this.cryptoService.getInMemoryUserKeyFor$(userId), + this.keyService.getInMemoryUserKeyFor$(userId), this.tokenService.hasAccessToken$(userId), ]).pipe( map(([userKey, hasAccessToken]) => { diff --git a/libs/common/src/auth/services/device-trust.service.implementation.ts b/libs/common/src/auth/services/device-trust.service.implementation.ts index 178f4b06545..1738ab10bb6 100644 --- a/libs/common/src/auth/services/device-trust.service.implementation.ts +++ b/libs/common/src/auth/services/device-trust.service.implementation.ts @@ -2,10 +2,10 @@ import { firstValueFrom, map, Observable } from "rxjs"; import { UserDecryptionOptionsServiceAbstraction } from "@bitwarden/auth/common"; +import { KeyService } from "../../../../key-management/src/abstractions/key.service"; import { AppIdService } from "../../platform/abstractions/app-id.service"; import { ConfigService } from "../../platform/abstractions/config/config.service"; import { CryptoFunctionService } from "../../platform/abstractions/crypto-function.service"; -import { CryptoService } from "../../platform/abstractions/crypto.service"; import { EncryptService } from "../../platform/abstractions/encrypt.service"; import { I18nService } from "../../platform/abstractions/i18n.service"; import { KeyGenerationService } from "../../platform/abstractions/key-generation.service"; @@ -64,7 +64,7 @@ export class DeviceTrustService implements DeviceTrustServiceAbstraction { constructor( private keyGenerationService: KeyGenerationService, private cryptoFunctionService: CryptoFunctionService, - private cryptoService: CryptoService, + private keyService: KeyService, private encryptService: EncryptService, private appIdService: AppIdService, private devicesApiService: DevicesApiServiceAbstraction, @@ -124,7 +124,7 @@ export class DeviceTrustService implements DeviceTrustServiceAbstraction { } // Attempt to get user key - const userKey: UserKey = await this.cryptoService.getUserKey(userId); + const userKey: UserKey = await this.keyService.getUserKey(userId); // If user key is not found, throw error if (!userKey) { @@ -175,6 +175,7 @@ export class DeviceTrustService implements DeviceTrustServiceAbstraction { newUserKey: UserKey, masterPasswordHash: string, ): Promise { + this.logService.info("[Device trust rotation] Rotating device trust..."); if (!userId) { throw new Error("UserId is required. Cannot rotate device's trust."); } @@ -183,11 +184,15 @@ export class DeviceTrustService implements DeviceTrustServiceAbstraction { if (currentDeviceKey == null) { // If the current device doesn't have a device key available to it, then we can't // rotate any trust at all, so early return. + this.logService.info("[Device trust rotation] No device key available to rotate trust!"); return; } // At this point of rotating their keys, they should still have their old user key in state - const oldUserKey = await firstValueFrom(this.cryptoService.userKey$(userId)); + const oldUserKey = await firstValueFrom(this.keyService.userKey$(userId)); + if (oldUserKey == newUserKey) { + this.logService.info("[Device trust rotation] Old user key is the same as the new user key."); + } const deviceIdentifier = await this.appIdService.getAppId(); const secretVerificationRequest = new SecretVerificationRequest(); @@ -229,7 +234,12 @@ export class DeviceTrustService implements DeviceTrustServiceAbstraction { trustRequest.currentDevice = currentDeviceUpdateRequest; trustRequest.otherDevices = []; + this.logService.info( + "[Device trust rotation] Posting device trust update with current device:", + deviceIdentifier, + ); await this.devicesApiService.updateTrust(trustRequest, deviceIdentifier); + this.logService.info("[Device trust rotation] Device trust update posted successfully."); } async getDeviceKey(userId: UserId): Promise { diff --git a/libs/common/src/auth/services/device-trust.service.spec.ts b/libs/common/src/auth/services/device-trust.service.spec.ts index 1171ae2918a..66a91a693e5 100644 --- a/libs/common/src/auth/services/device-trust.service.spec.ts +++ b/libs/common/src/auth/services/device-trust.service.spec.ts @@ -4,6 +4,7 @@ import { BehaviorSubject, of } from "rxjs"; import { UserDecryptionOptionsServiceAbstraction } from "@bitwarden/auth/common"; import { UserDecryptionOptions } from "../../../../auth/src/common/models/domain/user-decryption-options"; +import { KeyService } from "../../../../key-management/src/abstractions/key.service"; import { FakeAccountService, mockAccountServiceWith } from "../../../spec/fake-account-service"; import { FakeActiveUserState } from "../../../spec/fake-state"; import { FakeStateProvider } from "../../../spec/fake-state-provider"; @@ -11,7 +12,6 @@ import { DeviceType } from "../../enums"; import { AppIdService } from "../../platform/abstractions/app-id.service"; import { ConfigService } from "../../platform/abstractions/config/config.service"; import { CryptoFunctionService } from "../../platform/abstractions/crypto-function.service"; -import { CryptoService } from "../../platform/abstractions/crypto.service"; import { EncryptService } from "../../platform/abstractions/encrypt.service"; import { I18nService } from "../../platform/abstractions/i18n.service"; import { KeyGenerationService } from "../../platform/abstractions/key-generation.service"; @@ -43,7 +43,7 @@ describe("deviceTrustService", () => { const keyGenerationService = mock(); const cryptoFunctionService = mock(); - const cryptoService = mock(); + const keyService = mock(); const encryptService = mock(); const appIdService = mock(); const devicesApiService = mock(); @@ -368,7 +368,7 @@ describe("deviceTrustService", () => { .mockResolvedValue(mockDeviceRsaKeyPair); cryptoSvcGetUserKeySpy = jest - .spyOn(cryptoService, "getUserKey") + .spyOn(keyService, "getUserKey") .mockResolvedValue(mockUserKey); cryptoSvcRsaEncryptSpy = jest @@ -623,7 +623,7 @@ describe("deviceTrustService", () => { const fakeNewUserKeyData = new Uint8Array(64); fakeNewUserKeyData.fill(FakeNewUserKeyMarker, 0, 1); fakeNewUserKey = new SymmetricCryptoKey(fakeNewUserKeyData) as UserKey; - cryptoService.userKey$.mockReturnValue(of(fakeNewUserKey)); + keyService.userKey$.mockReturnValue(of(fakeNewUserKey)); }); it("throws an error when a null user id is passed in", async () => { @@ -659,7 +659,7 @@ describe("deviceTrustService", () => { fakeOldUserKeyData.fill(FakeOldUserKeyMarker, 0, 1); // Mock the retrieval of a user key that differs from the new one passed into the method - cryptoService.userKey$.mockReturnValue( + keyService.userKey$.mockReturnValue( of(new SymmetricCryptoKey(fakeOldUserKeyData) as UserKey), ); @@ -749,7 +749,7 @@ describe("deviceTrustService", () => { return new DeviceTrustService( keyGenerationService, cryptoFunctionService, - cryptoService, + keyService, encryptService, appIdService, devicesApiService, diff --git a/libs/common/src/auth/services/key-connector.service.spec.ts b/libs/common/src/auth/services/key-connector.service.spec.ts index eb3e4cfc0e6..b1bf87693c1 100644 --- a/libs/common/src/auth/services/key-connector.service.spec.ts +++ b/libs/common/src/auth/services/key-connector.service.spec.ts @@ -1,12 +1,12 @@ import { mock } from "jest-mock-extended"; +import { KeyService } from "../../../../key-management/src/abstractions/key.service"; import { FakeAccountService, FakeStateProvider, mockAccountServiceWith } from "../../../spec"; import { ApiService } from "../../abstractions/api.service"; import { OrganizationService } from "../../admin-console/abstractions/organization/organization.service.abstraction"; import { OrganizationData } from "../../admin-console/models/data/organization.data"; import { Organization } from "../../admin-console/models/domain/organization"; import { ProfileOrganizationResponse } from "../../admin-console/models/response/profile-organization.response"; -import { CryptoService } from "../../platform/abstractions/crypto.service"; import { LogService } from "../../platform/abstractions/log.service"; import { Utils } from "../../platform/misc/utils"; import { SymmetricCryptoKey } from "../../platform/models/domain/symmetric-crypto-key"; @@ -27,7 +27,7 @@ import { TokenService } from "./token.service"; describe("KeyConnectorService", () => { let keyConnectorService: KeyConnectorService; - const cryptoService = mock(); + const keyService = mock(); const apiService = mock(); const tokenService = mock(); const logService = mock(); @@ -56,7 +56,7 @@ describe("KeyConnectorService", () => { keyConnectorService = new KeyConnectorService( accountService, masterPasswordService, - cryptoService, + keyService, apiService, tokenService, logService, diff --git a/libs/common/src/auth/services/key-connector.service.ts b/libs/common/src/auth/services/key-connector.service.ts index ad9b7081cdf..111f82e6e52 100644 --- a/libs/common/src/auth/services/key-connector.service.ts +++ b/libs/common/src/auth/services/key-connector.service.ts @@ -2,12 +2,12 @@ import { firstValueFrom } from "rxjs"; import { LogoutReason } from "@bitwarden/auth/common"; +import { KeyService } from "../../../../key-management/src/abstractions/key.service"; import { ApiService } from "../../abstractions/api.service"; import { OrganizationService } from "../../admin-console/abstractions/organization/organization.service.abstraction"; import { OrganizationUserType } from "../../admin-console/enums"; import { Organization } from "../../admin-console/models/domain/organization"; import { KeysRequest } from "../../models/request/keys.request"; -import { CryptoService } from "../../platform/abstractions/crypto.service"; import { KeyGenerationService } from "../../platform/abstractions/key-generation.service"; import { LogService } from "../../platform/abstractions/log.service"; import { KdfType } from "../../platform/enums/kdf-type.enum"; @@ -54,7 +54,7 @@ export class KeyConnectorService implements KeyConnectorServiceAbstraction { constructor( private accountService: AccountService, private masterPasswordService: InternalMasterPasswordServiceAbstraction, - private cryptoService: CryptoService, + private keyService: KeyService, private apiService: ApiService, private tokenService: TokenService, private logService: LogService, @@ -146,7 +146,7 @@ export class KeyConnectorService implements KeyConnectorServiceAbstraction { ? new PBKDF2KdfConfig(kdfIterations) : new Argon2KdfConfig(kdfIterations, kdfMemory, kdfParallelism); - const masterKey = await this.cryptoService.makeMasterKey( + const masterKey = await this.keyService.makeMasterKey( password.keyB64, await this.tokenService.getEmail(), kdfConfig, @@ -154,11 +154,11 @@ export class KeyConnectorService implements KeyConnectorServiceAbstraction { const keyConnectorRequest = new KeyConnectorUserKeyRequest(masterKey.encKeyB64); await this.masterPasswordService.setMasterKey(masterKey, userId); - const userKey = await this.cryptoService.makeUserKey(masterKey); - await this.cryptoService.setUserKey(userKey[0], userId); - await this.cryptoService.setMasterKeyEncryptedUserKey(userKey[1].encryptedString, userId); + const userKey = await this.keyService.makeUserKey(masterKey); + await this.keyService.setUserKey(userKey[0], userId); + await this.keyService.setMasterKeyEncryptedUserKey(userKey[1].encryptedString, userId); - const [pubKey, privKey] = await this.cryptoService.makeKeyPair(userKey[0]); + const [pubKey, privKey] = await this.keyService.makeKeyPair(userKey[0]); try { const keyConnectorUrl = diff --git a/libs/common/src/auth/services/password-reset-enrollment.service.implementation.spec.ts b/libs/common/src/auth/services/password-reset-enrollment.service.implementation.spec.ts index b78ef52f07f..088ce960794 100644 --- a/libs/common/src/auth/services/password-reset-enrollment.service.implementation.spec.ts +++ b/libs/common/src/auth/services/password-reset-enrollment.service.implementation.spec.ts @@ -5,9 +5,9 @@ import { OrganizationUserApiService } from "@bitwarden/admin-console/common"; import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; import { UserId } from "../../../../common/src/types/guid"; +import { KeyService } from "../../../../key-management/src/abstractions/key.service"; import { OrganizationApiServiceAbstraction } from "../../admin-console/abstractions/organization/organization-api.service.abstraction"; import { OrganizationAutoEnrollStatusResponse } from "../../admin-console/models/response/organization-auto-enroll-status.response"; -import { CryptoService } from "../../platform/abstractions/crypto.service"; import { I18nService } from "../../platform/abstractions/i18n.service"; import { AccountInfo, AccountService } from "../abstractions/account.service"; @@ -18,7 +18,7 @@ describe("PasswordResetEnrollmentServiceImplementation", () => { let organizationApiService: MockProxy; let accountService: MockProxy; - let cryptoService: MockProxy; + let keyService: MockProxy; let encryptService: MockProxy; let organizationUserApiService: MockProxy; let i18nService: MockProxy; @@ -28,14 +28,14 @@ describe("PasswordResetEnrollmentServiceImplementation", () => { organizationApiService = mock(); accountService = mock(); accountService.activeAccount$ = activeAccountSubject; - cryptoService = mock(); + keyService = mock(); encryptService = mock(); organizationUserApiService = mock(); i18nService = mock(); service = new PasswordResetEnrollmentServiceImplementation( organizationApiService, accountService, - cryptoService, + keyService, encryptService, organizationUserApiService, i18nService, @@ -99,7 +99,7 @@ describe("PasswordResetEnrollmentServiceImplementation", () => { }; activeAccountSubject.next(Object.assign(user1AccountInfo, { id: "userId" as UserId })); - cryptoService.getUserKey.mockResolvedValue({ key: "key" } as any); + keyService.getUserKey.mockResolvedValue({ key: "key" } as any); encryptService.rsaEncrypt.mockResolvedValue(encryptedKey as any); await service.enroll("orgId"); diff --git a/libs/common/src/auth/services/password-reset-enrollment.service.implementation.ts b/libs/common/src/auth/services/password-reset-enrollment.service.implementation.ts index 7dc5f449959..9adcd0b7c17 100644 --- a/libs/common/src/auth/services/password-reset-enrollment.service.implementation.ts +++ b/libs/common/src/auth/services/password-reset-enrollment.service.implementation.ts @@ -6,8 +6,8 @@ import { } from "@bitwarden/admin-console/common"; import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; +import { KeyService } from "../../../../key-management/src/abstractions/key.service"; import { OrganizationApiServiceAbstraction } from "../../admin-console/abstractions/organization/organization-api.service.abstraction"; -import { CryptoService } from "../../platform/abstractions/crypto.service"; import { I18nService } from "../../platform/abstractions/i18n.service"; import { Utils } from "../../platform/misc/utils"; import { UserKey } from "../../types/key"; @@ -20,7 +20,7 @@ export class PasswordResetEnrollmentServiceImplementation constructor( protected organizationApiService: OrganizationApiServiceAbstraction, protected accountService: AccountService, - protected cryptoService: CryptoService, + protected keyService: KeyService, protected encryptService: EncryptService, protected organizationUserApiService: OrganizationUserApiService, protected i18nService: I18nService, @@ -47,7 +47,7 @@ export class PasswordResetEnrollmentServiceImplementation userId = userId ?? (await firstValueFrom(this.accountService.activeAccount$.pipe(map((a) => a?.id)))); - userKey = userKey ?? (await this.cryptoService.getUserKey(userId)); + userKey = userKey ?? (await this.keyService.getUserKey(userId)); // RSA Encrypt user's userKey.key with organization public key const encryptedKey = await this.encryptService.rsaEncrypt(userKey.key, orgPublicKey); diff --git a/libs/common/src/auth/services/user-verification/user-verification.service.spec.ts b/libs/common/src/auth/services/user-verification/user-verification.service.spec.ts index 73a97cbc8bb..02cd6056efb 100644 --- a/libs/common/src/auth/services/user-verification/user-verification.service.spec.ts +++ b/libs/common/src/auth/services/user-verification/user-verification.service.spec.ts @@ -8,9 +8,9 @@ import { UserDecryptionOptionsServiceAbstraction, } from "@bitwarden/auth/common"; +import { KeyService } from "../../../../../key-management/src/abstractions/key.service"; import { FakeAccountService, mockAccountServiceWith } from "../../../../spec"; import { VaultTimeoutSettingsService } from "../../../abstractions/vault-timeout/vault-timeout-settings.service"; -import { CryptoService } from "../../../platform/abstractions/crypto.service"; import { I18nService } from "../../../platform/abstractions/i18n.service"; import { LogService } from "../../../platform/abstractions/log.service"; import { PlatformUtilsService } from "../../../platform/abstractions/platform-utils.service"; @@ -31,7 +31,7 @@ import { UserVerificationService } from "./user-verification.service"; describe("UserVerificationService", () => { let sut: UserVerificationService; - const cryptoService = mock(); + const keyService = mock(); const masterPasswordService = mock(); const i18nService = mock(); const userVerificationApiService = mock(); @@ -50,7 +50,7 @@ describe("UserVerificationService", () => { accountService = mockAccountServiceWith(mockUserId); sut = new UserVerificationService( - cryptoService, + keyService, accountService, masterPasswordService, i18nService, @@ -132,7 +132,7 @@ describe("UserVerificationService", () => { setMasterPasswordAvailability(false); setPinAvailability("DISABLED"); vaultTimeoutSettingsService.isBiometricLockSet.mockResolvedValue(isBiometricsLockSet); - cryptoService.hasUserKeyStored.mockResolvedValue(isBiometricsUserKeyStored); + keyService.hasUserKeyStored.mockResolvedValue(isBiometricsUserKeyStored); platformUtilsService.supportsSecureStorage.mockReturnValue(platformSupportSecureStorage); const result = await sut.getAvailableVerificationOptions("client"); @@ -205,7 +205,7 @@ describe("UserVerificationService", () => { kdfConfigService.getKdfConfig.mockResolvedValue("kdfConfig" as unknown as KdfConfig); masterPasswordService.masterKey$.mockReturnValue(of("masterKey" as unknown as MasterKey)); - cryptoService.hashMasterKey + keyService.hashMasterKey .calledWith("password", "masterKey" as unknown as MasterKey, HashPurpose.LocalAuthorization) .mockResolvedValue("localHash"); }); @@ -216,7 +216,7 @@ describe("UserVerificationService", () => { }); it("returns if verification is successful", async () => { - cryptoService.compareAndUpdateKeyHash.mockResolvedValueOnce(true); + keyService.compareAndUpdateKeyHash.mockResolvedValueOnce(true); const result = await sut.verifyUserByMasterPassword( { @@ -227,7 +227,7 @@ describe("UserVerificationService", () => { "email", ); - expect(cryptoService.compareAndUpdateKeyHash).toHaveBeenCalled(); + expect(keyService.compareAndUpdateKeyHash).toHaveBeenCalled(); expect(masterPasswordService.setMasterKeyHash).toHaveBeenCalledWith( "localHash", mockUserId, @@ -240,7 +240,7 @@ describe("UserVerificationService", () => { }); it("throws if verification fails", async () => { - cryptoService.compareAndUpdateKeyHash.mockResolvedValueOnce(false); + keyService.compareAndUpdateKeyHash.mockResolvedValueOnce(false); await expect( sut.verifyUserByMasterPassword( @@ -253,7 +253,7 @@ describe("UserVerificationService", () => { ), ).rejects.toThrow("Invalid master password"); - expect(cryptoService.compareAndUpdateKeyHash).toHaveBeenCalled(); + expect(keyService.compareAndUpdateKeyHash).toHaveBeenCalled(); expect(masterPasswordService.setMasterKeyHash).not.toHaveBeenCalledWith(); expect(masterPasswordService.setMasterKey).not.toHaveBeenCalledWith(); }); @@ -265,7 +265,7 @@ describe("UserVerificationService", () => { }); it("returns if verification is successful", async () => { - cryptoService.hashMasterKey + keyService.hashMasterKey .calledWith( "password", "masterKey" as unknown as MasterKey, @@ -285,7 +285,7 @@ describe("UserVerificationService", () => { "email", ); - expect(cryptoService.compareAndUpdateKeyHash).not.toHaveBeenCalled(); + expect(keyService.compareAndUpdateKeyHash).not.toHaveBeenCalled(); expect(masterPasswordService.setMasterKeyHash).toHaveBeenCalledWith( "localHash", mockUserId, @@ -298,7 +298,7 @@ describe("UserVerificationService", () => { }); it("throws if verification fails", async () => { - cryptoService.hashMasterKey + keyService.hashMasterKey .calledWith( "password", "masterKey" as unknown as MasterKey, @@ -318,7 +318,7 @@ describe("UserVerificationService", () => { ), ).rejects.toThrow("Invalid master password"); - expect(cryptoService.compareAndUpdateKeyHash).not.toHaveBeenCalled(); + expect(keyService.compareAndUpdateKeyHash).not.toHaveBeenCalled(); expect(masterPasswordService.setMasterKeyHash).not.toHaveBeenCalledWith(); expect(masterPasswordService.setMasterKey).not.toHaveBeenCalledWith(); }); @@ -380,7 +380,7 @@ describe("UserVerificationService", () => { it("throws if master key cannot be created", async () => { kdfConfigService.getKdfConfig.mockResolvedValueOnce("kdfConfig" as unknown as KdfConfig); masterPasswordService.masterKey$.mockReturnValueOnce(of(null)); - cryptoService.makeMasterKey.mockResolvedValueOnce(null); + keyService.makeMasterKey.mockResolvedValueOnce(null); await expect( sut.verifyUserByMasterPassword( diff --git a/libs/common/src/auth/services/user-verification/user-verification.service.ts b/libs/common/src/auth/services/user-verification/user-verification.service.ts index 3b133891c95..b31ba59c983 100644 --- a/libs/common/src/auth/services/user-verification/user-verification.service.ts +++ b/libs/common/src/auth/services/user-verification/user-verification.service.ts @@ -3,8 +3,8 @@ import { firstValueFrom, map } from "rxjs"; import { UserDecryptionOptionsServiceAbstraction } from "@bitwarden/auth/common"; import { PinServiceAbstraction } from "../../../../../auth/src/common/abstractions/pin.service.abstraction"; +import { KeyService } from "../../../../../key-management/src/abstractions/key.service"; import { VaultTimeoutSettingsService as VaultTimeoutSettingsServiceAbstraction } from "../../../abstractions/vault-timeout/vault-timeout-settings.service"; -import { CryptoService } from "../../../platform/abstractions/crypto.service"; import { I18nService } from "../../../platform/abstractions/i18n.service"; import { LogService } from "../../../platform/abstractions/log.service"; import { PlatformUtilsService } from "../../../platform/abstractions/platform-utils.service"; @@ -39,7 +39,7 @@ import { */ export class UserVerificationService implements UserVerificationServiceAbstraction { constructor( - private cryptoService: CryptoService, + private keyService: KeyService, private accountService: AccountService, private masterPasswordService: InternalMasterPasswordServiceAbstraction, private i18nService: I18nService, @@ -66,7 +66,7 @@ export class UserVerificationService implements UserVerificationServiceAbstracti this.hasMasterPasswordAndMasterKeyHash(userId), this.pinService.isPinDecryptionAvailable(userId), this.vaultTimeoutSettingsService.isBiometricLockSet(userId), - this.cryptoService.hasUserKeyStored(KeySuffixOptions.Biometric, userId), + this.keyService.hasUserKeyStored(KeySuffixOptions.Biometric, userId), ]); // note: we do not need to check this.platformUtilsService.supportsBiometric() because @@ -119,7 +119,7 @@ export class UserVerificationService implements UserVerificationServiceAbstracti ); let masterKey = await firstValueFrom(this.masterPasswordService.masterKey$(userId)); if (!masterKey && !alreadyHashed) { - masterKey = await this.cryptoService.makeMasterKey( + masterKey = await this.keyService.makeMasterKey( verification.secret, email, await this.kdfConfigService.getKdfConfig(), @@ -127,7 +127,7 @@ export class UserVerificationService implements UserVerificationServiceAbstracti } request.masterPasswordHash = alreadyHashed ? verification.secret - : await this.cryptoService.hashMasterKey(verification.secret, masterKey); + : await this.keyService.hashMasterKey(verification.secret, masterKey); } return request; @@ -196,7 +196,7 @@ export class UserVerificationService implements UserVerificationServiceAbstracti let masterKey = await firstValueFrom(this.masterPasswordService.masterKey$(userId)); if (!masterKey) { - masterKey = await this.cryptoService.makeMasterKey(verification.secret, email, kdfConfig); + masterKey = await this.keyService.makeMasterKey(verification.secret, email, kdfConfig); } if (!masterKey) { @@ -206,7 +206,7 @@ export class UserVerificationService implements UserVerificationServiceAbstracti let policyOptions: MasterPasswordPolicyResponse | null; // Client-side verification if (await this.hasMasterPasswordAndMasterKeyHash(userId)) { - const passwordValid = await this.cryptoService.compareAndUpdateKeyHash( + const passwordValid = await this.keyService.compareAndUpdateKeyHash( verification.secret, masterKey, ); @@ -217,7 +217,7 @@ export class UserVerificationService implements UserVerificationServiceAbstracti } else { // Server-side verification const request = new SecretVerificationRequest(); - const serverKeyHash = await this.cryptoService.hashMasterKey( + const serverKeyHash = await this.keyService.hashMasterKey( verification.secret, masterKey, HashPurpose.ServerAuthorization, @@ -230,7 +230,7 @@ export class UserVerificationService implements UserVerificationServiceAbstracti } } - const localKeyHash = await this.cryptoService.hashMasterKey( + const localKeyHash = await this.keyService.hashMasterKey( verification.secret, masterKey, HashPurpose.LocalAuthorization, @@ -254,7 +254,7 @@ export class UserVerificationService implements UserVerificationServiceAbstracti let userKey: UserKey; // Biometrics crashes and doesn't return a value if the user cancels the prompt try { - userKey = await this.cryptoService.getUserKeyFromStorage(KeySuffixOptions.Biometric); + userKey = await this.keyService.getUserKeyFromStorage(KeySuffixOptions.Biometric); } catch (e) { this.logService.error(`Biometrics User Verification failed: ${e.message}`); // So, any failures should be treated as a failed verification diff --git a/libs/common/src/auth/services/webauthn-login/webauthn-login-prf-crypto.service.spec.ts b/libs/common/src/auth/services/webauthn-login/webauthn-login-prf-key.service.spec.ts similarity index 78% rename from libs/common/src/auth/services/webauthn-login/webauthn-login-prf-crypto.service.spec.ts rename to libs/common/src/auth/services/webauthn-login/webauthn-login-prf-key.service.spec.ts index 96eb466b206..f1fe07a996f 100644 --- a/libs/common/src/auth/services/webauthn-login/webauthn-login-prf-crypto.service.spec.ts +++ b/libs/common/src/auth/services/webauthn-login/webauthn-login-prf-key.service.spec.ts @@ -2,15 +2,15 @@ import { mock, MockProxy } from "jest-mock-extended"; import { CryptoFunctionService } from "../../../platform/abstractions/crypto-function.service"; -import { WebAuthnLoginPrfCryptoService } from "./webauthn-login-prf-crypto.service"; +import { WebAuthnLoginPrfKeyService } from "./webauthn-login-prf-key.service"; -describe("WebAuthnLoginPrfCryptoService", () => { +describe("WebAuthnLoginPrfKeyService", () => { let cryptoFunctionService: MockProxy; - let service: WebAuthnLoginPrfCryptoService; + let service: WebAuthnLoginPrfKeyService; beforeEach(() => { cryptoFunctionService = mock(); - service = new WebAuthnLoginPrfCryptoService(cryptoFunctionService); + service = new WebAuthnLoginPrfKeyService(cryptoFunctionService); }); describe("createSymmetricKeyFromPrf", () => { diff --git a/libs/common/src/auth/services/webauthn-login/webauthn-login-prf-crypto.service.ts b/libs/common/src/auth/services/webauthn-login/webauthn-login-prf-key.service.ts similarity index 82% rename from libs/common/src/auth/services/webauthn-login/webauthn-login-prf-crypto.service.ts rename to libs/common/src/auth/services/webauthn-login/webauthn-login-prf-key.service.ts index 29032cd5877..92cc03e7592 100644 --- a/libs/common/src/auth/services/webauthn-login/webauthn-login-prf-crypto.service.ts +++ b/libs/common/src/auth/services/webauthn-login/webauthn-login-prf-key.service.ts @@ -1,11 +1,11 @@ import { CryptoFunctionService } from "../../../platform/abstractions/crypto-function.service"; import { SymmetricCryptoKey } from "../../../platform/models/domain/symmetric-crypto-key"; import { PrfKey } from "../../../types/key"; -import { WebAuthnLoginPrfCryptoServiceAbstraction } from "../../abstractions/webauthn/webauthn-login-prf-crypto.service.abstraction"; +import { WebAuthnLoginPrfKeyServiceAbstraction } from "../../abstractions/webauthn/webauthn-login-prf-key.service.abstraction"; const LoginWithPrfSalt = "passwordless-login"; -export class WebAuthnLoginPrfCryptoService implements WebAuthnLoginPrfCryptoServiceAbstraction { +export class WebAuthnLoginPrfKeyService implements WebAuthnLoginPrfKeyServiceAbstraction { constructor(private cryptoFunctionService: CryptoFunctionService) {} async getLoginWithPrfSalt(): Promise { diff --git a/libs/common/src/auth/services/webauthn-login/webauthn-login.service.spec.ts b/libs/common/src/auth/services/webauthn-login/webauthn-login.service.spec.ts index 1c7f045461b..10444062349 100644 --- a/libs/common/src/auth/services/webauthn-login/webauthn-login.service.spec.ts +++ b/libs/common/src/auth/services/webauthn-login/webauthn-login.service.spec.ts @@ -7,7 +7,7 @@ import { Utils } from "../../../platform/misc/utils"; import { SymmetricCryptoKey } from "../../../platform/models/domain/symmetric-crypto-key"; import { PrfKey } from "../../../types/key"; import { WebAuthnLoginApiServiceAbstraction } from "../../abstractions/webauthn/webauthn-login-api.service.abstraction"; -import { WebAuthnLoginPrfCryptoServiceAbstraction } from "../../abstractions/webauthn/webauthn-login-prf-crypto.service.abstraction"; +import { WebAuthnLoginPrfKeyServiceAbstraction } from "../../abstractions/webauthn/webauthn-login-prf-key.service.abstraction"; import { AuthResult } from "../../models/domain/auth-result"; import { WebAuthnLoginCredentialAssertionOptionsView } from "../../models/view/webauthn-login/webauthn-login-credential-assertion-options.view"; import { WebAuthnLoginCredentialAssertionView } from "../../models/view/webauthn-login/webauthn-login-credential-assertion.view"; @@ -21,7 +21,7 @@ describe("WebAuthnLoginService", () => { const webAuthnLoginApiService = mock(); const loginStrategyService = mock(); - const webAuthnLoginPrfCryptoService = mock(); + const webAuthnLoginPrfKeyService = mock(); const navigatorCredentials = mock(); const logService = mock(); @@ -72,7 +72,7 @@ describe("WebAuthnLoginService", () => { return new WebAuthnLoginService( webAuthnLoginApiService, loginStrategyService, - webAuthnLoginPrfCryptoService, + webAuthnLoginPrfKeyService, window, logService, ); @@ -141,8 +141,8 @@ describe("WebAuthnLoginService", () => { publicKeyCredential.getClientExtensionResults().prf?.results?.first; const prfKey = new SymmetricCryptoKey(new Uint8Array(prfResult)) as PrfKey; - webAuthnLoginPrfCryptoService.getLoginWithPrfSalt.mockResolvedValue(saltArrayBuffer); - webAuthnLoginPrfCryptoService.createSymmetricKeyFromPrf.mockResolvedValue(prfKey); + webAuthnLoginPrfKeyService.getLoginWithPrfSalt.mockResolvedValue(saltArrayBuffer); + webAuthnLoginPrfKeyService.createSymmetricKeyFromPrf.mockResolvedValue(prfKey); // Mock implementations navigatorCredentials.get.mockResolvedValue(publicKeyCredential); @@ -152,7 +152,7 @@ describe("WebAuthnLoginService", () => { // Assert - expect(webAuthnLoginPrfCryptoService.getLoginWithPrfSalt).toHaveBeenCalled(); + expect(webAuthnLoginPrfKeyService.getLoginWithPrfSalt).toHaveBeenCalled(); expect(navigatorCredentials.get).toHaveBeenCalledWith( expect.objectContaining({ @@ -169,9 +169,7 @@ describe("WebAuthnLoginService", () => { }), ); - expect(webAuthnLoginPrfCryptoService.createSymmetricKeyFromPrf).toHaveBeenCalledWith( - prfResult, - ); + expect(webAuthnLoginPrfKeyService.createSymmetricKeyFromPrf).toHaveBeenCalledWith(prfResult); expect(result).toBeInstanceOf(WebAuthnLoginCredentialAssertionView); expect(result.token).toEqual(credentialAssertionOptions.token); diff --git a/libs/common/src/auth/services/webauthn-login/webauthn-login.service.ts b/libs/common/src/auth/services/webauthn-login/webauthn-login.service.ts index 7fca20e6159..41f4994fab0 100644 --- a/libs/common/src/auth/services/webauthn-login/webauthn-login.service.ts +++ b/libs/common/src/auth/services/webauthn-login/webauthn-login.service.ts @@ -3,7 +3,7 @@ import { LoginStrategyServiceAbstraction, WebAuthnLoginCredentials } from "@bitw import { LogService } from "../../../platform/abstractions/log.service"; import { PrfKey } from "../../../types/key"; import { WebAuthnLoginApiServiceAbstraction } from "../../abstractions/webauthn/webauthn-login-api.service.abstraction"; -import { WebAuthnLoginPrfCryptoServiceAbstraction } from "../../abstractions/webauthn/webauthn-login-prf-crypto.service.abstraction"; +import { WebAuthnLoginPrfKeyServiceAbstraction } from "../../abstractions/webauthn/webauthn-login-prf-key.service.abstraction"; import { WebAuthnLoginServiceAbstraction } from "../../abstractions/webauthn/webauthn-login.service.abstraction"; import { AuthResult } from "../../models/domain/auth-result"; import { WebAuthnLoginCredentialAssertionOptionsView } from "../../models/view/webauthn-login/webauthn-login-credential-assertion-options.view"; @@ -17,7 +17,7 @@ export class WebAuthnLoginService implements WebAuthnLoginServiceAbstraction { constructor( private webAuthnLoginApiService: WebAuthnLoginApiServiceAbstraction, private loginStrategyService: LoginStrategyServiceAbstraction, - private webAuthnLoginPrfCryptoService: WebAuthnLoginPrfCryptoServiceAbstraction, + private webAuthnLoginPrfKeyService: WebAuthnLoginPrfKeyServiceAbstraction, private window: Window, private logService?: LogService, ) { @@ -37,7 +37,7 @@ export class WebAuthnLoginService implements WebAuthnLoginServiceAbstraction { }; // TODO: Remove `any` when typescript typings add support for PRF nativeOptions.publicKey.extensions = { - prf: { eval: { first: await this.webAuthnLoginPrfCryptoService.getLoginWithPrfSalt() } }, + prf: { eval: { first: await this.webAuthnLoginPrfKeyService.getLoginWithPrfSalt() } }, } as any; try { @@ -50,7 +50,7 @@ export class WebAuthnLoginService implements WebAuthnLoginServiceAbstraction { let symmetricPrfKey: PrfKey | undefined; if (prfResult != undefined) { symmetricPrfKey = - await this.webAuthnLoginPrfCryptoService.createSymmetricKeyFromPrf(prfResult); + await this.webAuthnLoginPrfKeyService.createSymmetricKeyFromPrf(prfResult); } const deviceResponse = new WebAuthnLoginAssertionResponseRequest(response); diff --git a/libs/common/src/autofill/constants/index.ts b/libs/common/src/autofill/constants/index.ts index 9333fa23368..62ad10e9a90 100644 --- a/libs/common/src/autofill/constants/index.ts +++ b/libs/common/src/autofill/constants/index.ts @@ -24,6 +24,7 @@ export const EVENTS = { MOUSEENTER: "mouseenter", MOUSELEAVE: "mouseleave", MOUSEUP: "mouseup", + MOUSEOUT: "mouseout", SUBMIT: "submit", } as const; diff --git a/libs/common/src/billing/services/organization-billing.service.ts b/libs/common/src/billing/services/organization-billing.service.ts index 6b326472c97..eebea0ca74e 100644 --- a/libs/common/src/billing/services/organization-billing.service.ts +++ b/libs/common/src/billing/services/organization-billing.service.ts @@ -1,9 +1,9 @@ +import { KeyService } from "../../../../key-management/src/abstractions/key.service"; import { ApiService } from "../../abstractions/api.service"; import { OrganizationApiServiceAbstraction as OrganizationApiService } from "../../admin-console/abstractions/organization/organization-api.service.abstraction"; import { OrganizationCreateRequest } from "../../admin-console/models/request/organization-create.request"; import { OrganizationKeysRequest } from "../../admin-console/models/request/organization-keys.request"; import { OrganizationResponse } from "../../admin-console/models/response/organization.response"; -import { CryptoService } from "../../platform/abstractions/crypto.service"; import { EncryptService } from "../../platform/abstractions/encrypt.service"; import { I18nService } from "../../platform/abstractions/i18n.service"; import { EncString } from "../../platform/models/domain/enc-string"; @@ -28,7 +28,7 @@ interface OrganizationKeys { export class OrganizationBillingService implements OrganizationBillingServiceAbstraction { constructor( private apiService: ApiService, - private cryptoService: CryptoService, + private keyService: KeyService, private encryptService: EncryptService, private i18nService: I18nService, private organizationApiService: OrganizationApiService, @@ -78,8 +78,8 @@ export class OrganizationBillingService implements OrganizationBillingServiceAbs } private async makeOrganizationKeys(): Promise { - const [encryptedKey, key] = await this.cryptoService.makeOrgKey(); - const [publicKey, encryptedPrivateKey] = await this.cryptoService.makeKeyPair(key); + const [encryptedKey, key] = await this.keyService.makeOrgKey(); + const [publicKey, encryptedPrivateKey] = await this.keyService.makeKeyPair(key); const encryptedCollectionName = await this.encryptService.encrypt( this.i18nService.t("defaultCollection"), key, diff --git a/libs/common/src/enums/feature-flag.enum.ts b/libs/common/src/enums/feature-flag.enum.ts index 905d7299489..84cf5ed521e 100644 --- a/libs/common/src/enums/feature-flag.enum.ts +++ b/libs/common/src/enums/feature-flag.enum.ts @@ -33,7 +33,6 @@ export enum FeatureFlag { CipherKeyEncryption = "cipher-key-encryption", VerifiedSsoDomainEndpoint = "pm-12337-refactor-sso-details-endpoint", PM11901_RefactorSelfHostingLicenseUploader = "PM-11901-refactor-self-hosting-license-uploader", - Pm3478RefactorOrganizationUserApi = "pm-3478-refactor-organizationuser-api", AccessIntelligence = "pm-13227-access-intelligence", Pm13322AddPolicyDefinitions = "pm-13322-add-policy-definitions", LimitCollectionCreationDeletionSplit = "pm-10863-limit-collection-creation-deletion-split", @@ -80,7 +79,6 @@ export const DefaultFeatureFlagValue = { [FeatureFlag.CipherKeyEncryption]: FALSE, [FeatureFlag.VerifiedSsoDomainEndpoint]: FALSE, [FeatureFlag.PM11901_RefactorSelfHostingLicenseUploader]: FALSE, - [FeatureFlag.Pm3478RefactorOrganizationUserApi]: FALSE, [FeatureFlag.AccessIntelligence]: FALSE, [FeatureFlag.Pm13322AddPolicyDefinitions]: FALSE, [FeatureFlag.LimitCollectionCreationDeletionSplit]: FALSE, diff --git a/libs/common/src/platform/misc/utils.ts b/libs/common/src/platform/misc/utils.ts index 326ed5e8e8d..a7cc05bbf64 100644 --- a/libs/common/src/platform/misc/utils.ts +++ b/libs/common/src/platform/misc/utils.ts @@ -6,7 +6,7 @@ import { Observable, of, switchMap } from "rxjs"; import { getHostname, parse } from "tldts"; import { Merge } from "type-fest"; -import { CryptoService } from "../abstractions/crypto.service"; +import { KeyService } from "../../../../key-management/src/abstractions/key.service"; import { EncryptService } from "../abstractions/encrypt.service"; import { I18nService } from "../abstractions/i18n.service"; @@ -18,7 +18,7 @@ declare global { } interface BitwardenContainerService { - getCryptoService: () => CryptoService; + getKeyService: () => KeyService; getEncryptService: () => EncryptService; } diff --git a/libs/common/src/platform/models/domain/enc-string.spec.ts b/libs/common/src/platform/models/domain/enc-string.spec.ts index 39d58831772..462a977ff8c 100644 --- a/libs/common/src/platform/models/domain/enc-string.spec.ts +++ b/libs/common/src/platform/models/domain/enc-string.spec.ts @@ -1,10 +1,10 @@ import { mock, MockProxy } from "jest-mock-extended"; +import { KeyService } from "../../../../../key-management/src/abstractions/key.service"; import { makeEncString, makeStaticByteArray } from "../../../../spec"; import { EncryptService } from "../../../platform/abstractions/encrypt.service"; import { SymmetricCryptoKey } from "../../../platform/models/domain/symmetric-crypto-key"; import { UserKey, OrgKey } from "../../../types/key"; -import { CryptoService } from "../../abstractions/crypto.service"; import { EncryptionType } from "../../enums"; import { Utils } from "../../misc/utils"; import { ContainerService } from "../../services/container.service"; @@ -81,9 +81,9 @@ describe("EncString", () => { describe("decrypt", () => { const encString = new EncString(EncryptionType.Rsa2048_OaepSha256_B64, "data"); - const cryptoService = mock(); - cryptoService.hasUserKey.mockResolvedValue(true); - cryptoService.getUserKeyWithLegacySupport.mockResolvedValue( + const keyService = mock(); + keyService.hasUserKey.mockResolvedValue(true); + keyService.getUserKeyWithLegacySupport.mockResolvedValue( new SymmetricCryptoKey(makeStaticByteArray(32)) as UserKey, ); @@ -94,7 +94,7 @@ describe("EncString", () => { beforeEach(() => { (window as any).bitwardenContainerService = new ContainerService( - cryptoService, + keyService, encryptService, ); }); @@ -117,7 +117,7 @@ describe("EncString", () => { describe("decryptWithKey", () => { const encString = new EncString(EncryptionType.Rsa2048_OaepSha256_B64, "data"); - const cryptoService = mock(); + const keyService = mock(); const encryptService = mock(); encryptService.decryptToUtf8 .calledWith(encString, expect.anything()) @@ -140,10 +140,7 @@ describe("EncString", () => { } beforeEach(() => { - (window as any).bitwardenContainerService = new ContainerService( - cryptoService, - encryptService, - ); + (window as any).bitwardenContainerService = new ContainerService(keyService, encryptService); }); it("decrypts using the provided key and encryptService", async () => { @@ -321,28 +318,22 @@ describe("EncString", () => { }); describe("decrypt", () => { - let cryptoService: MockProxy; + let keyService: MockProxy; let encryptService: MockProxy; let encString: EncString; beforeEach(() => { - cryptoService = mock(); + keyService = mock(); encryptService = mock(); encString = new EncString(null); - (window as any).bitwardenContainerService = new ContainerService( - cryptoService, - encryptService, - ); + (window as any).bitwardenContainerService = new ContainerService(keyService, encryptService); }); it("handles value it can't decrypt", async () => { encryptService.decryptToUtf8.mockRejectedValue("error"); - (window as any).bitwardenContainerService = new ContainerService( - cryptoService, - encryptService, - ); + (window as any).bitwardenContainerService = new ContainerService(keyService, encryptService); const decrypted = await encString.decrypt(null); @@ -354,34 +345,34 @@ describe("EncString", () => { }); }); - it("uses provided key without depending on CryptoService", async () => { + it("uses provided key without depending on KeyService", async () => { const key = mock(); await encString.decrypt(null, key); - expect(cryptoService.getUserKeyWithLegacySupport).not.toHaveBeenCalled(); + expect(keyService.getUserKeyWithLegacySupport).not.toHaveBeenCalled(); expect(encryptService.decryptToUtf8).toHaveBeenCalledWith(encString, key); }); it("gets an organization key if required", async () => { const orgKey = mock(); - cryptoService.getOrgKey.calledWith("orgId").mockResolvedValue(orgKey); + keyService.getOrgKey.calledWith("orgId").mockResolvedValue(orgKey); await encString.decrypt("orgId", null); - expect(cryptoService.getOrgKey).toHaveBeenCalledWith("orgId"); + expect(keyService.getOrgKey).toHaveBeenCalledWith("orgId"); expect(encryptService.decryptToUtf8).toHaveBeenCalledWith(encString, orgKey); }); it("gets the user's decryption key if required", async () => { const userKey = mock(); - cryptoService.getUserKeyWithLegacySupport.mockResolvedValue(userKey); + keyService.getUserKeyWithLegacySupport.mockResolvedValue(userKey); await encString.decrypt(null, null); - expect(cryptoService.getUserKeyWithLegacySupport).toHaveBeenCalledWith(); + expect(keyService.getUserKeyWithLegacySupport).toHaveBeenCalledWith(); expect(encryptService.decryptToUtf8).toHaveBeenCalledWith(encString, userKey); }); }); diff --git a/libs/common/src/platform/models/domain/enc-string.ts b/libs/common/src/platform/models/domain/enc-string.ts index 0b0a597acd3..40f36306bf1 100644 --- a/libs/common/src/platform/models/domain/enc-string.ts +++ b/libs/common/src/platform/models/domain/enc-string.ts @@ -189,10 +189,10 @@ export class EncString implements Encrypted { return this.decryptedValue; } private async getKeyForDecryption(orgId: string) { - const cryptoService = Utils.getContainerService().getCryptoService(); + const keyService = Utils.getContainerService().getKeyService(); return orgId != null - ? await cryptoService.getOrgKey(orgId) - : await cryptoService.getUserKeyWithLegacySupport(); + ? await keyService.getOrgKey(orgId) + : await keyService.getUserKeyWithLegacySupport(); } } diff --git a/libs/common/src/platform/services/container.service.ts b/libs/common/src/platform/services/container.service.ts index 2e0748a32ab..6022e097ab0 100644 --- a/libs/common/src/platform/services/container.service.ts +++ b/libs/common/src/platform/services/container.service.ts @@ -1,9 +1,9 @@ -import { CryptoService } from "../abstractions/crypto.service"; +import { KeyService } from "../../../../key-management/src/abstractions/key.service"; import { EncryptService } from "../abstractions/encrypt.service"; export class ContainerService { constructor( - private cryptoService: CryptoService, + private keyService: KeyService, private encryptService: EncryptService, ) {} @@ -14,13 +14,13 @@ export class ContainerService { } /** - * @throws Will throw if CryptoService was not instantiated and provided to the ContainerService constructor + * @throws Will throw if KeyService was not instantiated and provided to the ContainerService constructor */ - getCryptoService(): CryptoService { - if (this.cryptoService == null) { - throw new Error("ContainerService.cryptoService not initialized."); + getKeyService(): KeyService { + if (this.keyService == null) { + throw new Error("ContainerService.keyService not initialized."); } - return this.cryptoService; + return this.keyService; } /** diff --git a/libs/common/src/platform/services/sdk/default-sdk.service.spec.ts b/libs/common/src/platform/services/sdk/default-sdk.service.spec.ts index dad99401f75..ff82b3aa764 100644 --- a/libs/common/src/platform/services/sdk/default-sdk.service.spec.ts +++ b/libs/common/src/platform/services/sdk/default-sdk.service.spec.ts @@ -1,6 +1,7 @@ import { mock, MockProxy } from "jest-mock-extended"; import { BehaviorSubject, firstValueFrom, of } from "rxjs"; +import { KeyService } from "@bitwarden/key-management"; import { BitwardenClient } from "@bitwarden/sdk-internal"; import { ApiService } from "../../../abstractions/api.service"; @@ -9,7 +10,6 @@ import { KdfConfigService } from "../../../auth/abstractions/kdf-config.service" import { PBKDF2KdfConfig } from "../../../auth/models/domain/kdf-config"; import { UserId } from "../../../types/guid"; import { UserKey } from "../../../types/key"; -import { CryptoService } from "../../abstractions/crypto.service"; import { Environment, EnvironmentService } from "../../abstractions/environment.service"; import { PlatformUtilsService } from "../../abstractions/platform-utils.service"; import { SdkClientFactory } from "../../abstractions/sdk/sdk-client-factory"; @@ -25,7 +25,7 @@ describe("DefaultSdkService", () => { let platformUtilsService!: MockProxy; let accountService!: MockProxy; let kdfConfigService!: MockProxy; - let cryptoService!: MockProxy; + let keyService!: MockProxy; let apiService!: MockProxy; let service!: DefaultSdkService; @@ -37,7 +37,7 @@ describe("DefaultSdkService", () => { platformUtilsService = mock(); accountService = mock(); kdfConfigService = mock(); - cryptoService = mock(); + keyService = mock(); apiService = mock(); // Can't use `of(mock())` for some reason @@ -49,7 +49,7 @@ describe("DefaultSdkService", () => { platformUtilsService, accountService, kdfConfigService, - cryptoService, + keyService, apiService, ); @@ -68,13 +68,13 @@ describe("DefaultSdkService", () => { kdfConfigService.getKdfConfig$ .calledWith(userId) .mockReturnValue(of(new PBKDF2KdfConfig())); - cryptoService.userKey$ + keyService.userKey$ .calledWith(userId) .mockReturnValue(of(new SymmetricCryptoKey(new Uint8Array(64)) as UserKey)); - cryptoService.userEncryptedPrivateKey$ + keyService.userEncryptedPrivateKey$ .calledWith(userId) .mockReturnValue(of("private-key" as EncryptedString)); - cryptoService.encryptedOrgKeys$.calledWith(userId).mockReturnValue(of({})); + keyService.encryptedOrgKeys$.calledWith(userId).mockReturnValue(of({})); }); it("creates an SDK client when called the first time", async () => { @@ -115,7 +115,7 @@ describe("DefaultSdkService", () => { it("destroys the SDK client when the userKey is unset (i.e. lock or logout)", async () => { const userKey$ = new BehaviorSubject(new SymmetricCryptoKey(new Uint8Array(64)) as UserKey); - cryptoService.userKey$.calledWith(userId).mockReturnValue(userKey$); + keyService.userKey$.calledWith(userId).mockReturnValue(userKey$); const subject = new BehaviorSubject(undefined); service.userClient$(userId).subscribe(subject); diff --git a/libs/common/src/platform/services/sdk/default-sdk.service.ts b/libs/common/src/platform/services/sdk/default-sdk.service.ts index 1b7a9a939a4..adea07becc7 100644 --- a/libs/common/src/platform/services/sdk/default-sdk.service.ts +++ b/libs/common/src/platform/services/sdk/default-sdk.service.ts @@ -10,6 +10,7 @@ import { switchMap, } from "rxjs"; +import { KeyService } from "@bitwarden/key-management"; import { BitwardenClient, ClientSettings, @@ -25,7 +26,6 @@ import { KdfConfig } from "../../../auth/models/domain/kdf-config"; import { DeviceType } from "../../../enums/device-type.enum"; import { OrganizationId, UserId } from "../../../types/guid"; import { UserKey } from "../../../types/key"; -import { CryptoService } from "../../abstractions/crypto.service"; import { Environment, EnvironmentService } from "../../abstractions/environment.service"; import { PlatformUtilsService } from "../../abstractions/platform-utils.service"; import { SdkClientFactory } from "../../abstractions/sdk/sdk-client-factory"; @@ -57,7 +57,7 @@ export class DefaultSdkService implements SdkService { private platformUtilsService: PlatformUtilsService, private accountService: AccountService, private kdfConfigService: KdfConfigService, - private cryptoService: CryptoService, + private keyService: KeyService, private apiService: ApiService, // Yes we shouldn't import ApiService, but it's temporary private userAgent: string = null, ) {} @@ -73,11 +73,11 @@ export class DefaultSdkService implements SdkService { distinctUntilChanged(), ); const kdfParams$ = this.kdfConfigService.getKdfConfig$(userId).pipe(distinctUntilChanged()); - const privateKey$ = this.cryptoService + const privateKey$ = this.keyService .userEncryptedPrivateKey$(userId) .pipe(distinctUntilChanged()); - const userKey$ = this.cryptoService.userKey$(userId).pipe(distinctUntilChanged()); - const orgKeys$ = this.cryptoService.encryptedOrgKeys$(userId).pipe( + const userKey$ = this.keyService.userKey$(userId).pipe(distinctUntilChanged()); + const orgKeys$ = this.keyService.encryptedOrgKeys$(userId).pipe( distinctUntilChanged(compareValues), // The upstream observable emits different objects with the same values ); diff --git a/libs/common/src/platform/services/user-auto-unlock-key.service.spec.ts b/libs/common/src/platform/services/user-auto-unlock-key.service.spec.ts index f0d60158c18..23a8ba3138b 100644 --- a/libs/common/src/platform/services/user-auto-unlock-key.service.spec.ts +++ b/libs/common/src/platform/services/user-auto-unlock-key.service.spec.ts @@ -1,5 +1,6 @@ import { mock } from "jest-mock-extended"; +import { DefaultKeyService } from "../../../../key-management/src/key.service"; import { CsprngArray } from "../../types/csprng"; import { UserId } from "../../types/guid"; import { UserKey } from "../../types/key"; @@ -7,7 +8,6 @@ import { KeySuffixOptions } from "../enums"; import { Utils } from "../misc/utils"; import { SymmetricCryptoKey } from "../models/domain/symmetric-crypto-key"; -import { CryptoService } from "./crypto.service"; import { UserAutoUnlockKeyService } from "./user-auto-unlock-key.service"; describe("UserAutoUnlockKeyService", () => { @@ -15,10 +15,10 @@ describe("UserAutoUnlockKeyService", () => { const mockUserId = Utils.newGuid() as UserId; - const cryptoService = mock(); + const keyService = mock(); beforeEach(() => { - userAutoUnlockKeyService = new UserAutoUnlockKeyService(cryptoService); + userAutoUnlockKeyService = new UserAutoUnlockKeyService(keyService); }); describe("setUserKeyInMemoryIfAutoUserKeySet", () => { @@ -27,25 +27,22 @@ describe("UserAutoUnlockKeyService", () => { await (userAutoUnlockKeyService as any).setUserKeyInMemoryIfAutoUserKeySet(null); // Assert - expect(cryptoService.getUserKeyFromStorage).not.toHaveBeenCalled(); - expect(cryptoService.setUserKey).not.toHaveBeenCalled(); + expect(keyService.getUserKeyFromStorage).not.toHaveBeenCalled(); + expect(keyService.setUserKey).not.toHaveBeenCalled(); }); it("does nothing if the autoUserKey is null", async () => { // Arrange const userId = mockUserId; - cryptoService.getUserKeyFromStorage.mockResolvedValue(null); + keyService.getUserKeyFromStorage.mockResolvedValue(null); // Act await (userAutoUnlockKeyService as any).setUserKeyInMemoryIfAutoUserKeySet(userId); // Assert - expect(cryptoService.getUserKeyFromStorage).toHaveBeenCalledWith( - KeySuffixOptions.Auto, - userId, - ); - expect(cryptoService.setUserKey).not.toHaveBeenCalled(); + expect(keyService.getUserKeyFromStorage).toHaveBeenCalledWith(KeySuffixOptions.Auto, userId); + expect(keyService.setUserKey).not.toHaveBeenCalled(); }); it("sets the user key in memory if the autoUserKey is not null", async () => { @@ -55,17 +52,14 @@ describe("UserAutoUnlockKeyService", () => { const mockRandomBytes = new Uint8Array(64) as CsprngArray; const mockAutoUserKey: UserKey = new SymmetricCryptoKey(mockRandomBytes) as UserKey; - cryptoService.getUserKeyFromStorage.mockResolvedValue(mockAutoUserKey); + keyService.getUserKeyFromStorage.mockResolvedValue(mockAutoUserKey); // Act await (userAutoUnlockKeyService as any).setUserKeyInMemoryIfAutoUserKeySet(userId); // Assert - expect(cryptoService.getUserKeyFromStorage).toHaveBeenCalledWith( - KeySuffixOptions.Auto, - userId, - ); - expect(cryptoService.setUserKey).toHaveBeenCalledWith(mockAutoUserKey, userId); + expect(keyService.getUserKeyFromStorage).toHaveBeenCalledWith(KeySuffixOptions.Auto, userId); + expect(keyService.setUserKey).toHaveBeenCalledWith(mockAutoUserKey, userId); }); }); }); diff --git a/libs/common/src/platform/services/user-auto-unlock-key.service.ts b/libs/common/src/platform/services/user-auto-unlock-key.service.ts index b4a154133c1..abb8993c39c 100644 --- a/libs/common/src/platform/services/user-auto-unlock-key.service.ts +++ b/libs/common/src/platform/services/user-auto-unlock-key.service.ts @@ -1,15 +1,15 @@ +import { KeyService } from "../../../../key-management/src/abstractions/key.service"; import { UserId } from "../../types/guid"; -import { CryptoService } from "../abstractions/crypto.service"; import { KeySuffixOptions } from "../enums"; -// TODO: this is a half measure improvement which allows us to reduce some side effects today (cryptoService.getUserKey setting user key in memory if auto key exists) -// but ideally, in the future, we would be able to put this logic into the cryptoService +// TODO: this is a half measure improvement which allows us to reduce some side effects today (keyService.getUserKey setting user key in memory if auto key exists) +// but ideally, in the future, we would be able to put this logic into the keyService // after the vault timeout settings service is transitioned to state provider so that // the getUserKey logic can simply go to the correct location based on the vault timeout settings // similar to the TokenService (it would either go to secure storage for the auto user key or memory for the user key) export class UserAutoUnlockKeyService { - constructor(private cryptoService: CryptoService) {} + constructor(private keyService: KeyService) {} /** * The presence of the user key in memory dictates whether the user's vault is locked or unlocked. @@ -23,16 +23,13 @@ export class UserAutoUnlockKeyService { return false; } - const autoUserKey = await this.cryptoService.getUserKeyFromStorage( - KeySuffixOptions.Auto, - userId, - ); + const autoUserKey = await this.keyService.getUserKeyFromStorage(KeySuffixOptions.Auto, userId); if (autoUserKey == null) { return false; } - await this.cryptoService.setUserKey(autoUserKey, userId); + await this.keyService.setUserKey(autoUserKey, userId); return true; } } diff --git a/libs/common/src/platform/sync/default-sync.service.ts b/libs/common/src/platform/sync/default-sync.service.ts index 66a6c8e3503..eaf804d2866 100644 --- a/libs/common/src/platform/sync/default-sync.service.ts +++ b/libs/common/src/platform/sync/default-sync.service.ts @@ -8,6 +8,7 @@ import { import { UserDecryptionOptionsServiceAbstraction } from "../../../../auth/src/common/abstractions"; import { LogoutReason } from "../../../../auth/src/common/types"; +import { KeyService } from "../../../../key-management/src/abstractions/key.service"; import { ApiService } from "../../abstractions/api.service"; import { InternalOrganizationServiceAbstraction } from "../../admin-console/abstractions/organization/organization.service.abstraction"; import { InternalPolicyService } from "../../admin-console/abstractions/policy/policy.service.abstraction"; @@ -41,7 +42,6 @@ import { CipherData } from "../../vault/models/data/cipher.data"; import { FolderData } from "../../vault/models/data/folder.data"; import { CipherResponse } from "../../vault/models/response/cipher.response"; import { FolderResponse } from "../../vault/models/response/folder.response"; -import { CryptoService } from "../abstractions/crypto.service"; import { LogService } from "../abstractions/log.service"; import { StateService } from "../abstractions/state.service"; import { MessageSender } from "../messaging"; @@ -60,7 +60,7 @@ export class DefaultSyncService extends CoreSyncService { private domainSettingsService: DomainSettingsService, folderService: InternalFolderService, cipherService: CipherService, - private cryptoService: CryptoService, + private keyService: KeyService, collectionService: CollectionService, messageSender: MessageSender, private policyService: InternalPolicyService, @@ -178,10 +178,10 @@ export class DefaultSyncService extends CoreSyncService { throw new Error("Stamp has changed"); } - await this.cryptoService.setMasterKeyEncryptedUserKey(response.key, response.id); - await this.cryptoService.setPrivateKey(response.privateKey, response.id); - await this.cryptoService.setProviderKeys(response.providers, response.id); - await this.cryptoService.setOrgKeys( + await this.keyService.setMasterKeyEncryptedUserKey(response.key, response.id); + await this.keyService.setPrivateKey(response.privateKey, response.id); + await this.keyService.setProviderKeys(response.providers, response.id); + await this.keyService.setOrgKeys( response.organizations, response.providerOrganizations, response.id, diff --git a/libs/common/src/services/notifications.service.ts b/libs/common/src/services/notifications.service.ts index 8e6a664a0af..d443193c9b7 100644 --- a/libs/common/src/services/notifications.service.ts +++ b/libs/common/src/services/notifications.service.ts @@ -193,6 +193,7 @@ export class NotificationsService implements NotificationsServiceAbstraction { break; case NotificationType.LogOut: if (isAuthenticated) { + this.logService.info("[Notifications Service] Received logout notification"); // 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.logoutCallback("logoutNotification"); diff --git a/libs/common/src/services/vault-timeout/vault-timeout-settings.service.spec.ts b/libs/common/src/services/vault-timeout/vault-timeout-settings.service.spec.ts index d90388f866f..540f26bba2d 100644 --- a/libs/common/src/services/vault-timeout/vault-timeout-settings.service.spec.ts +++ b/libs/common/src/services/vault-timeout/vault-timeout-settings.service.spec.ts @@ -10,13 +10,13 @@ import { Utils } from "@bitwarden/common/platform/misc/utils"; import { UserId } from "@bitwarden/common/types/guid"; import { BiometricStateService } from "@bitwarden/key-management"; +import { KeyService } from "../../../../key-management/src/abstractions/key.service"; import { FakeAccountService, mockAccountServiceWith, FakeStateProvider } from "../../../spec"; import { VaultTimeoutSettingsService as VaultTimeoutSettingsServiceAbstraction } from "../../abstractions/vault-timeout/vault-timeout-settings.service"; import { PolicyService } from "../../admin-console/abstractions/policy/policy.service.abstraction"; import { Policy } from "../../admin-console/models/domain/policy"; import { TokenService } from "../../auth/abstractions/token.service"; import { VaultTimeoutAction } from "../../enums/vault-timeout-action.enum"; -import { CryptoService } from "../../platform/abstractions/crypto.service"; import { LogService } from "../../platform/abstractions/log.service"; import { VAULT_TIMEOUT, @@ -30,7 +30,7 @@ describe("VaultTimeoutSettingsService", () => { let accountService: FakeAccountService; let pinService: MockProxy; let userDecryptionOptionsService: MockProxy; - let cryptoService: MockProxy; + let keyService: MockProxy; let tokenService: MockProxy; let policyService: MockProxy; const biometricStateService = mock(); @@ -46,7 +46,7 @@ describe("VaultTimeoutSettingsService", () => { accountService = mockAccountServiceWith(mockUserId); pinService = mock(); userDecryptionOptionsService = mock(); - cryptoService = mock(); + keyService = mock(); tokenService = mock(); policyService = mock(); @@ -342,7 +342,7 @@ describe("VaultTimeoutSettingsService", () => { stateProvider.singleUser.getFake(mockUserId, VAULT_TIMEOUT).nextMock, ).toHaveBeenCalledWith(timeout); - expect(cryptoService.refreshAdditionalKeys).toHaveBeenCalled(); + expect(keyService.refreshAdditionalKeys).toHaveBeenCalled(); }); it("should clear the tokens when the timeout is not never and the action is log out", async () => { @@ -377,7 +377,7 @@ describe("VaultTimeoutSettingsService", () => { accountService, pinService, userDecryptionOptionsService, - cryptoService, + keyService, tokenService, policyService, biometricStateService, diff --git a/libs/common/src/services/vault-timeout/vault-timeout-settings.service.ts b/libs/common/src/services/vault-timeout/vault-timeout-settings.service.ts index a90842b208c..a1bc93144b7 100644 --- a/libs/common/src/services/vault-timeout/vault-timeout-settings.service.ts +++ b/libs/common/src/services/vault-timeout/vault-timeout-settings.service.ts @@ -19,6 +19,7 @@ import { } from "@bitwarden/auth/common"; import { BiometricStateService } from "@bitwarden/key-management"; +import { KeyService } from "../../../../key-management/src/abstractions/key.service"; import { VaultTimeoutSettingsService as VaultTimeoutSettingsServiceAbstraction } from "../../abstractions/vault-timeout/vault-timeout-settings.service"; import { PolicyService } from "../../admin-console/abstractions/policy/policy.service.abstraction"; import { PolicyType } from "../../admin-console/enums"; @@ -26,7 +27,6 @@ import { Policy } from "../../admin-console/models/domain/policy"; import { AccountService } from "../../auth/abstractions/account.service"; import { TokenService } from "../../auth/abstractions/token.service"; import { VaultTimeoutAction } from "../../enums/vault-timeout-action.enum"; -import { CryptoService } from "../../platform/abstractions/crypto.service"; import { LogService } from "../../platform/abstractions/log.service"; import { StateProvider } from "../../platform/state"; import { UserId } from "../../types/guid"; @@ -39,7 +39,7 @@ export class VaultTimeoutSettingsService implements VaultTimeoutSettingsServiceA private accountService: AccountService, private pinService: PinServiceAbstraction, private userDecryptionOptionsService: UserDecryptionOptionsServiceAbstraction, - private cryptoService: CryptoService, + private keyService: KeyService, private tokenService: TokenService, private policyService: PolicyService, private biometricStateService: BiometricStateService, @@ -87,7 +87,7 @@ export class VaultTimeoutSettingsService implements VaultTimeoutSettingsServiceA clientSecret, ]); - await this.cryptoService.refreshAdditionalKeys(); + await this.keyService.refreshAdditionalKeys(); } availableVaultTimeoutActions$(userId?: string): Observable { @@ -287,7 +287,7 @@ export class VaultTimeoutSettingsService implements VaultTimeoutSettingsServiceA } async clear(userId?: string): Promise { - await this.cryptoService.clearPinKeys(userId); + await this.keyService.clearPinKeys(userId); } private async userHasMasterPassword(userId: string): Promise { diff --git a/libs/common/src/tools/send/models/domain/send.spec.ts b/libs/common/src/tools/send/models/domain/send.spec.ts index 5b1d7e73dae..74c0e77b394 100644 --- a/libs/common/src/tools/send/models/domain/send.spec.ts +++ b/libs/common/src/tools/send/models/domain/send.spec.ts @@ -3,8 +3,8 @@ import { mock } from "jest-mock-extended"; import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key"; import { UserKey } from "@bitwarden/common/types/key"; +import { KeyService } from "../../../../../../key-management/src/abstractions/key.service"; import { makeStaticByteArray, mockEnc } from "../../../../../spec"; -import { CryptoService } from "../../../../platform/abstractions/crypto.service"; import { EncryptService } from "../../../../platform/abstractions/encrypt.service"; import { ContainerService } from "../../../../platform/services/container.service"; import { SendType } from "../../enums/send-type"; @@ -111,14 +111,14 @@ describe("Send", () => { send.hideEmail = true; const encryptService = mock(); - const cryptoService = mock(); + const keyService = mock(); encryptService.decryptToBytes .calledWith(send.key, userKey) .mockResolvedValue(makeStaticByteArray(32)); - cryptoService.makeSendKey.mockResolvedValue("cryptoKey" as any); - cryptoService.getUserKey.mockResolvedValue(userKey); + keyService.makeSendKey.mockResolvedValue("cryptoKey" as any); + keyService.getUserKey.mockResolvedValue(userKey); - (window as any).bitwardenContainerService = new ContainerService(cryptoService, encryptService); + (window as any).bitwardenContainerService = new ContainerService(keyService, encryptService); const view = await send.decrypt(); diff --git a/libs/common/src/tools/send/models/domain/send.ts b/libs/common/src/tools/send/models/domain/send.ts index 41d1fecc10b..6e53813a368 100644 --- a/libs/common/src/tools/send/models/domain/send.ts +++ b/libs/common/src/tools/send/models/domain/send.ts @@ -72,13 +72,13 @@ export class Send extends Domain { async decrypt(): Promise { const model = new SendView(this); - const cryptoService = Utils.getContainerService().getCryptoService(); + const keyService = Utils.getContainerService().getKeyService(); const encryptService = Utils.getContainerService().getEncryptService(); try { - const sendKeyEncryptionKey = await cryptoService.getUserKey(); + const sendKeyEncryptionKey = await keyService.getUserKey(); model.key = await encryptService.decryptToBytes(this.key, sendKeyEncryptionKey); - model.cryptoKey = await cryptoService.makeSendKey(model.key); + model.cryptoKey = await keyService.makeSendKey(model.key); } catch (e) { // TODO: error? } diff --git a/libs/common/src/tools/send/services/send.service.spec.ts b/libs/common/src/tools/send/services/send.service.spec.ts index 5743eff481b..5aca3a4b5c9 100644 --- a/libs/common/src/tools/send/services/send.service.spec.ts +++ b/libs/common/src/tools/send/services/send.service.spec.ts @@ -4,6 +4,7 @@ import { firstValueFrom, of } from "rxjs"; import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service"; import { SelfHostedEnvironment } from "@bitwarden/common/platform/services/default-environment.service"; +import { KeyService } from "../../../../../key-management/src/abstractions/key.service"; import { FakeAccountService, FakeActiveUserState, @@ -11,7 +12,6 @@ import { awaitAsync, mockAccountServiceWith, } from "../../../../spec"; -import { CryptoService } from "../../../platform/abstractions/crypto.service"; import { EncryptService } from "../../../platform/abstractions/encrypt.service"; import { I18nService } from "../../../platform/abstractions/i18n.service"; import { KeyGenerationService } from "../../../platform/abstractions/key-generation.service"; @@ -40,7 +40,7 @@ import { } from "./test-data/send-tests.data"; describe("SendService", () => { - const cryptoService = mock(); + const keyService = mock(); const i18nService = mock(); const keyGenerationService = mock(); const encryptService = mock(); @@ -65,7 +65,7 @@ describe("SendService", () => { get: () => of(new SelfHostedEnvironment({ webVault: "https://example.com" })), }); - (window as any).bitwardenContainerService = new ContainerService(cryptoService, encryptService); + (window as any).bitwardenContainerService = new ContainerService(keyService, encryptService); accountService.activeAccountSubject.next({ id: mockUserId, @@ -84,7 +84,7 @@ describe("SendService", () => { decryptedState.nextState([testSendViewData("1", "Test Send")]); sendService = new SendService( - cryptoService, + keyService, i18nService, keyGenerationService, sendStateProvider, diff --git a/libs/common/src/tools/send/services/send.service.ts b/libs/common/src/tools/send/services/send.service.ts index 25937e7da1f..3ba1cb92e2c 100644 --- a/libs/common/src/tools/send/services/send.service.ts +++ b/libs/common/src/tools/send/services/send.service.ts @@ -1,7 +1,7 @@ import { Observable, concatMap, distinctUntilChanged, firstValueFrom, map } from "rxjs"; +import { KeyService } from "../../../../../key-management/src/abstractions/key.service"; import { PBKDF2KdfConfig } from "../../../auth/models/domain/kdf-config"; -import { CryptoService } from "../../../platform/abstractions/crypto.service"; import { EncryptService } from "../../../platform/abstractions/encrypt.service"; import { I18nService } from "../../../platform/abstractions/i18n.service"; import { KeyGenerationService } from "../../../platform/abstractions/key-generation.service"; @@ -37,7 +37,7 @@ export class SendService implements InternalSendServiceAbstraction { ); constructor( - private cryptoService: CryptoService, + private keyService: KeyService, private i18nService: I18nService, private keyGenerationService: KeyGenerationService, private stateProvider: SendStateProvider, @@ -77,7 +77,7 @@ export class SendService implements InternalSendServiceAbstraction { send.password = passwordKey.keyB64; } if (key == null) { - key = await this.cryptoService.getUserKey(); + key = await this.keyService.getUserKey(); } send.key = await this.encryptService.encrypt(model.key, key); send.name = await this.encryptService.encrypt(model.name, model.cryptoKey); @@ -197,7 +197,7 @@ export class SendService implements InternalSendServiceAbstraction { } decSends = []; - const hasKey = await this.cryptoService.hasUserKey(); + const hasKey = await this.keyService.hasUserKey(); if (!hasKey) { throw new Error("No user key found."); } @@ -322,7 +322,7 @@ export class SendService implements InternalSendServiceAbstraction { key: SymmetricCryptoKey, ): Promise<[EncString, EncArrayBuffer]> { if (key == null) { - key = await this.cryptoService.getUserKey(); + key = await this.keyService.getUserKey(); } const encFileName = await this.encryptService.encrypt(fileName, key); const encFileData = await this.encryptService.encryptToBytes(new Uint8Array(data), key); diff --git a/libs/common/src/vault/models/domain/attachment.spec.ts b/libs/common/src/vault/models/domain/attachment.spec.ts index 690866e173a..14dec8dea0c 100644 --- a/libs/common/src/vault/models/domain/attachment.spec.ts +++ b/libs/common/src/vault/models/domain/attachment.spec.ts @@ -1,7 +1,7 @@ import { mock, MockProxy } from "jest-mock-extended"; +import { KeyService } from "../../../../../key-management/src/abstractions/key.service"; import { makeStaticByteArray, mockEnc, mockFromJson } from "../../../../spec"; -import { CryptoService } from "../../../platform/abstractions/crypto.service"; import { EncryptService } from "../../../platform/abstractions/encrypt.service"; import { EncryptedString, EncString } from "../../../platform/models/domain/enc-string"; import { SymmetricCryptoKey } from "../../../platform/models/domain/symmetric-crypto-key"; @@ -57,17 +57,14 @@ describe("Attachment", () => { }); describe("decrypt", () => { - let cryptoService: MockProxy; + let keyService: MockProxy; let encryptService: MockProxy; beforeEach(() => { - cryptoService = mock(); + keyService = mock(); encryptService = mock(); - (window as any).bitwardenContainerService = new ContainerService( - cryptoService, - encryptService, - ); + (window as any).bitwardenContainerService = new ContainerService(keyService, encryptService); }); it("expected output", async () => { @@ -101,32 +98,32 @@ describe("Attachment", () => { attachment.key = mock(); }); - it("uses the provided key without depending on CryptoService", async () => { + it("uses the provided key without depending on KeyService", async () => { const providedKey = mock(); await attachment.decrypt(null, providedKey); - expect(cryptoService.getUserKeyWithLegacySupport).not.toHaveBeenCalled(); + expect(keyService.getUserKeyWithLegacySupport).not.toHaveBeenCalled(); expect(encryptService.decryptToBytes).toHaveBeenCalledWith(attachment.key, providedKey); }); it("gets an organization key if required", async () => { const orgKey = mock(); - cryptoService.getOrgKey.calledWith("orgId").mockResolvedValue(orgKey); + keyService.getOrgKey.calledWith("orgId").mockResolvedValue(orgKey); await attachment.decrypt("orgId", null); - expect(cryptoService.getOrgKey).toHaveBeenCalledWith("orgId"); + expect(keyService.getOrgKey).toHaveBeenCalledWith("orgId"); expect(encryptService.decryptToBytes).toHaveBeenCalledWith(attachment.key, orgKey); }); it("gets the user's decryption key if required", async () => { const userKey = mock(); - cryptoService.getUserKeyWithLegacySupport.mockResolvedValue(userKey); + keyService.getUserKeyWithLegacySupport.mockResolvedValue(userKey); await attachment.decrypt(null, null); - expect(cryptoService.getUserKeyWithLegacySupport).toHaveBeenCalled(); + expect(keyService.getUserKeyWithLegacySupport).toHaveBeenCalled(); expect(encryptService.decryptToBytes).toHaveBeenCalledWith(attachment.key, userKey); }); }); diff --git a/libs/common/src/vault/models/domain/attachment.ts b/libs/common/src/vault/models/domain/attachment.ts index 7a234fa21ce..117b3b26e92 100644 --- a/libs/common/src/vault/models/domain/attachment.ts +++ b/libs/common/src/vault/models/domain/attachment.ts @@ -68,10 +68,10 @@ export class Attachment extends Domain { } private async getKeyForDecryption(orgId: string) { - const cryptoService = Utils.getContainerService().getCryptoService(); + const keyService = Utils.getContainerService().getKeyService(); return orgId != null - ? await cryptoService.getOrgKey(orgId) - : await cryptoService.getUserKeyWithLegacySupport(); + ? await keyService.getOrgKey(orgId) + : await keyService.getUserKeyWithLegacySupport(); } toAttachmentData(): AttachmentData { diff --git a/libs/common/src/vault/models/domain/cipher.spec.ts b/libs/common/src/vault/models/domain/cipher.spec.ts index f10884b55ae..509a17a8a0e 100644 --- a/libs/common/src/vault/models/domain/cipher.spec.ts +++ b/libs/common/src/vault/models/domain/cipher.spec.ts @@ -3,9 +3,9 @@ import { Jsonify } from "type-fest"; import { UserId } from "@bitwarden/common/types/guid"; +import { KeyService } from "../../../../../key-management/src/abstractions/key.service"; import { makeStaticByteArray, mockEnc, mockFromJson } from "../../../../spec/utils"; import { UriMatchStrategy } from "../../../models/domain/domain-service"; -import { CryptoService } from "../../../platform/abstractions/crypto.service"; import { EncryptService } from "../../../platform/abstractions/encrypt.service"; import { EncString } from "../../../platform/models/domain/enc-string"; import { ContainerService } from "../../../platform/services/container.service"; @@ -237,16 +237,13 @@ describe("Cipher DTO", () => { login.decrypt.mockResolvedValue(loginView); cipher.login = login; - const cryptoService = mock(); + const keyService = mock(); const encryptService = mock(); const cipherService = mock(); encryptService.decryptToBytes.mockResolvedValue(makeStaticByteArray(64)); - (window as any).bitwardenContainerService = new ContainerService( - cryptoService, - encryptService, - ); + (window as any).bitwardenContainerService = new ContainerService(keyService, encryptService); const cipherView = await cipher.decrypt( await cipherService.getKeyForCipherKeyDecryption(cipher, mockUserId), @@ -357,16 +354,13 @@ describe("Cipher DTO", () => { cipher.secureNote.type = SecureNoteType.Generic; cipher.key = mockEnc("EncKey"); - const cryptoService = mock(); + const keyService = mock(); const encryptService = mock(); const cipherService = mock(); encryptService.decryptToBytes.mockResolvedValue(makeStaticByteArray(64)); - (window as any).bitwardenContainerService = new ContainerService( - cryptoService, - encryptService, - ); + (window as any).bitwardenContainerService = new ContainerService(keyService, encryptService); const cipherView = await cipher.decrypt( await cipherService.getKeyForCipherKeyDecryption(cipher, mockUserId), @@ -495,16 +489,13 @@ describe("Cipher DTO", () => { card.decrypt.mockResolvedValue(cardView); cipher.card = card; - const cryptoService = mock(); + const keyService = mock(); const encryptService = mock(); const cipherService = mock(); encryptService.decryptToBytes.mockResolvedValue(makeStaticByteArray(64)); - (window as any).bitwardenContainerService = new ContainerService( - cryptoService, - encryptService, - ); + (window as any).bitwardenContainerService = new ContainerService(keyService, encryptService); const cipherView = await cipher.decrypt( await cipherService.getKeyForCipherKeyDecryption(cipher, mockUserId), @@ -657,16 +648,13 @@ describe("Cipher DTO", () => { identity.decrypt.mockResolvedValue(identityView); cipher.identity = identity; - const cryptoService = mock(); + const keyService = mock(); const encryptService = mock(); const cipherService = mock(); encryptService.decryptToBytes.mockResolvedValue(makeStaticByteArray(64)); - (window as any).bitwardenContainerService = new ContainerService( - cryptoService, - encryptService, - ); + (window as any).bitwardenContainerService = new ContainerService(keyService, encryptService); const cipherView = await cipher.decrypt( await cipherService.getKeyForCipherKeyDecryption(cipher, mockUserId), diff --git a/libs/common/src/vault/models/domain/login-uri.spec.ts b/libs/common/src/vault/models/domain/login-uri.spec.ts index c42b0cc9d12..a1ecb473597 100644 --- a/libs/common/src/vault/models/domain/login-uri.spec.ts +++ b/libs/common/src/vault/models/domain/login-uri.spec.ts @@ -70,7 +70,7 @@ describe("LoginUri", () => { encryptService = mock(); global.bitwardenContainerService = { getEncryptService: () => encryptService, - getCryptoService: () => null, + getKeyService: () => null, }; }); diff --git a/libs/common/src/vault/models/domain/login-uri.ts b/libs/common/src/vault/models/domain/login-uri.ts index 0a0c5765a34..e5943929f2d 100644 --- a/libs/common/src/vault/models/domain/login-uri.ts +++ b/libs/common/src/vault/models/domain/login-uri.ts @@ -47,8 +47,8 @@ export class LoginUri extends Domain { return false; } - const cryptoService = Utils.getContainerService().getEncryptService(); - const localChecksum = await cryptoService.hash(clearTextUri, "sha256"); + const keyService = Utils.getContainerService().getEncryptService(); + const localChecksum = await keyService.hash(clearTextUri, "sha256"); const remoteChecksum = await this.uriChecksum.decrypt(orgId, encKey); return remoteChecksum === localChecksum; diff --git a/libs/common/src/vault/models/view/cipher.view.ts b/libs/common/src/vault/models/view/cipher.view.ts index 028b582db26..3ea3f109be1 100644 --- a/libs/common/src/vault/models/view/cipher.view.ts +++ b/libs/common/src/vault/models/view/cipher.view.ts @@ -2,9 +2,8 @@ import { View } from "../../../models/view/view"; import { InitializerMetadata } from "../../../platform/interfaces/initializer-metadata.interface"; import { InitializerKey } from "../../../platform/services/cryptography/initializer-key"; import { DeepJsonify } from "../../../types/deep-jsonify"; -import { LinkedIdType } from "../../enums"; +import { CipherType, LinkedIdType } from "../../enums"; import { CipherRepromptType } from "../../enums/cipher-reprompt-type"; -import { CipherType } from "../../enums/cipher-type"; import { LocalData } from "../data/local.data"; import { Cipher } from "../domain/cipher"; @@ -132,6 +131,13 @@ export class CipherView implements View, InitializerMetadata { ); } + /** + * Determines if the cipher can be launched in a new browser tab. + */ + get canLaunch(): boolean { + return this.type === CipherType.Login && this.login.canLaunch; + } + linkedFieldValue(id: LinkedIdType) { const linkedFieldOption = this.linkedFieldOptions?.get(id); if (linkedFieldOption == null) { diff --git a/libs/common/src/vault/services/cipher-authorization.service.spec.ts b/libs/common/src/vault/services/cipher-authorization.service.spec.ts index 3155825d4d0..cccd29ad697 100644 --- a/libs/common/src/vault/services/cipher-authorization.service.spec.ts +++ b/libs/common/src/vault/services/cipher-authorization.service.spec.ts @@ -1,5 +1,5 @@ import { mock } from "jest-mock-extended"; -import { of } from "rxjs"; +import { firstValueFrom, of } from "rxjs"; import { CollectionService, CollectionView } from "@bitwarden/admin-console/common"; import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; @@ -39,10 +39,16 @@ describe("CipherAuthorizationService", () => { allowAdminAccessToAllCollectionItems = false, canEditAllCiphers = false, canEditUnassignedCiphers = false, + isAdmin = false, + editAnyCollection = false, } = {}) => ({ allowAdminAccessToAllCollectionItems, canEditAllCiphers, canEditUnassignedCiphers, + isAdmin, + permissions: { + editAnyCollection, + }, }); beforeEach(() => { @@ -197,4 +203,73 @@ describe("CipherAuthorizationService", () => { }); }); }); + + describe("canCloneCipher$", () => { + it("should return true if cipher has no organizationId", async () => { + const cipher = createMockCipher(null, []) as CipherView; + + const result = await firstValueFrom(cipherAuthorizationService.canCloneCipher$(cipher)); + expect(result).toBe(true); + }); + + describe("isAdminConsoleAction is true", () => { + it("should return true for admin users", async () => { + const cipher = createMockCipher("org1", []) as CipherView; + const organization = createMockOrganization({ isAdmin: true }); + mockOrganizationService.get$.mockReturnValue(of(organization as Organization)); + + const result = await firstValueFrom( + cipherAuthorizationService.canCloneCipher$(cipher, true), + ); + expect(result).toBe(true); + }); + + it("should return true for custom user with canEditAnyCollection", async () => { + const cipher = createMockCipher("org1", []) as CipherView; + const organization = createMockOrganization({ editAnyCollection: true }); + mockOrganizationService.get$.mockReturnValue(of(organization as Organization)); + + const result = await firstValueFrom( + cipherAuthorizationService.canCloneCipher$(cipher, true), + ); + expect(result).toBe(true); + }); + }); + + describe("isAdminConsoleAction is false", () => { + it("should return true if at least one cipher collection has manage permission", async () => { + const cipher = createMockCipher("org1", ["col1", "col2"]) as CipherView; + const organization = createMockOrganization(); + mockOrganizationService.get$.mockReturnValue(of(organization as Organization)); + + const allCollections = [ + createMockCollection("col1", true), + createMockCollection("col2", false), + ]; + mockCollectionService.decryptedCollectionViews$.mockReturnValue( + of(allCollections as CollectionView[]), + ); + + const result = await firstValueFrom(cipherAuthorizationService.canCloneCipher$(cipher)); + expect(result).toBe(true); + }); + + it("should return false if no collection has manage permission", async () => { + const cipher = createMockCipher("org1", ["col1", "col2"]) as CipherView; + const organization = createMockOrganization(); + mockOrganizationService.get$.mockReturnValue(of(organization as Organization)); + + const allCollections = [ + createMockCollection("col1", false), + createMockCollection("col2", false), + ]; + mockCollectionService.decryptedCollectionViews$.mockReturnValue( + of(allCollections as CollectionView[]), + ); + + const result = await firstValueFrom(cipherAuthorizationService.canCloneCipher$(cipher)); + expect(result).toBe(false); + }); + }); + }); }); diff --git a/libs/common/src/vault/services/cipher-authorization.service.ts b/libs/common/src/vault/services/cipher-authorization.service.ts index 00c7c412d61..eb6211848ae 100644 --- a/libs/common/src/vault/services/cipher-authorization.service.ts +++ b/libs/common/src/vault/services/cipher-authorization.service.ts @@ -1,4 +1,4 @@ -import { map, Observable, of, switchMap } from "rxjs"; +import { map, Observable, of, shareReplay, switchMap } from "rxjs"; import { CollectionService } from "@bitwarden/admin-console/common"; import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; @@ -30,6 +30,16 @@ export abstract class CipherAuthorizationService { allowedCollections?: CollectionId[], isAdminConsoleAction?: boolean, ) => Observable; + + /** + * Determines if the user can clone the specified cipher. + * + * @param {CipherLike} cipher - The cipher object to evaluate for cloning permissions. + * @param {boolean} isAdminConsoleAction - Optional. A flag indicating if the action is being performed from the admin console. + * + * @returns {Observable} - An observable that emits a boolean value indicating if the user can clone the cipher. + */ + canCloneCipher$: (cipher: CipherLike, isAdminConsoleAction?: boolean) => Observable; } /** @@ -83,4 +93,30 @@ export class DefaultCipherAuthorizationService implements CipherAuthorizationSer }), ); } + + /** + * {@link CipherAuthorizationService.canCloneCipher$} + */ + canCloneCipher$(cipher: CipherLike, isAdminConsoleAction?: boolean): Observable { + if (cipher.organizationId == null) { + return of(true); + } + + return this.organizationService.get$(cipher.organizationId).pipe( + switchMap((organization) => { + // Admins and custom users can always clone when in the Admin Console + if ( + isAdminConsoleAction && + (organization.isAdmin || organization.permissions?.editAnyCollection) + ) { + return of(true); + } + + return this.collectionService + .decryptedCollectionViews$(cipher.collectionIds as CollectionId[]) + .pipe(map((allCollections) => allCollections.some((collection) => collection.manage))); + }), + shareReplay({ bufferSize: 1, refCount: false }), + ); + } } diff --git a/libs/common/src/vault/services/cipher.service.spec.ts b/libs/common/src/vault/services/cipher.service.spec.ts index 3e8ec843fd8..961bc03bbb0 100644 --- a/libs/common/src/vault/services/cipher.service.spec.ts +++ b/libs/common/src/vault/services/cipher.service.spec.ts @@ -3,6 +3,10 @@ import { BehaviorSubject, map, of } from "rxjs"; import { BulkEncryptService } from "@bitwarden/common/platform/abstractions/bulk-encrypt.service"; +import { + CipherDecryptionKeys, + KeyService, +} from "../../../../key-management/src/abstractions/key.service"; import { FakeAccountService, mockAccountServiceWith } from "../../../spec/fake-account-service"; import { FakeStateProvider } from "../../../spec/fake-state-provider"; import { makeStaticByteArray } from "../../../spec/utils"; @@ -12,7 +16,6 @@ import { AutofillSettingsService } from "../../autofill/services/autofill-settin import { DomainSettingsService } from "../../autofill/services/domain-settings.service"; import { UriMatchStrategy } from "../../models/domain/domain-service"; import { ConfigService } from "../../platform/abstractions/config/config.service"; -import { CipherDecryptionKeys, CryptoService } from "../../platform/abstractions/crypto.service"; import { EncryptService } from "../../platform/abstractions/encrypt.service"; import { I18nService } from "../../platform/abstractions/i18n.service"; import { StateService } from "../../platform/abstractions/state.service"; @@ -107,7 +110,7 @@ const mockUserId = Utils.newGuid() as UserId; let accountService: FakeAccountService; describe("Cipher Service", () => { - const cryptoService = mock(); + const keyService = mock(); const stateService = mock(); const autofillSettingsService = mock(); const domainSettingsService = mock(); @@ -130,10 +133,10 @@ describe("Cipher Service", () => { encryptService.encryptToBytes.mockReturnValue(Promise.resolve(ENCRYPTED_BYTES)); encryptService.encrypt.mockReturnValue(Promise.resolve(new EncString(ENCRYPTED_TEXT))); - (window as any).bitwardenContainerService = new ContainerService(cryptoService, encryptService); + (window as any).bitwardenContainerService = new ContainerService(keyService, encryptService); cipherService = new CipherService( - cryptoService, + keyService, domainSettingsService, apiService, i18nService, @@ -159,10 +162,10 @@ describe("Cipher Service", () => { it("should upload encrypted file contents with save attachments", async () => { const fileName = "filename"; const fileData = new Uint8Array(10); - cryptoService.getOrgKey.mockReturnValue( + keyService.getOrgKey.mockReturnValue( Promise.resolve(new SymmetricCryptoKey(new Uint8Array(32)) as OrgKey), ); - cryptoService.makeDataEncKey.mockReturnValue( + keyService.makeDataEncKey.mockReturnValue( Promise.resolve(new SymmetricCryptoKey(new Uint8Array(32))), ); @@ -271,7 +274,7 @@ describe("Cipher Service", () => { encryptService.decryptToBytes.mockReturnValue(Promise.resolve(makeStaticByteArray(64))); configService.checkServerMeetsVersionRequirement$.mockReturnValue(of(true)); - cryptoService.makeCipherKey.mockReturnValue( + keyService.makeCipherKey.mockReturnValue( Promise.resolve(new SymmetricCryptoKey(makeStaticByteArray(64)) as CipherKey), ); encryptService.encrypt.mockImplementation(encryptText); @@ -286,7 +289,7 @@ describe("Cipher Service", () => { { uri: "uri", match: UriMatchStrategy.RegularExpression } as LoginUriView, ]; - cryptoService.getOrgKey.mockReturnValue( + keyService.getOrgKey.mockReturnValue( Promise.resolve(new SymmetricCryptoKey(new Uint8Array(32)) as OrgKey), ); @@ -306,7 +309,7 @@ describe("Cipher Service", () => { it("is null when feature flag is false", async () => { configService.getFeatureFlag.mockResolvedValue(false); - cryptoService.getOrgKey.mockReturnValue( + keyService.getOrgKey.mockReturnValue( Promise.resolve(new SymmetricCryptoKey(new Uint8Array(32)) as OrgKey), ); const cipher = await cipherService.encrypt(cipherView, userId); @@ -330,7 +333,7 @@ describe("Cipher Service", () => { it("is not called when feature flag is false", async () => { configService.getFeatureFlag.mockResolvedValue(false); - cryptoService.getOrgKey.mockReturnValue( + keyService.getOrgKey.mockReturnValue( Promise.resolve(new SymmetricCryptoKey(new Uint8Array(32)) as OrgKey), ); @@ -341,7 +344,7 @@ describe("Cipher Service", () => { it("is called when feature flag is true", async () => { configService.getFeatureFlag.mockResolvedValue(true); - cryptoService.getOrgKey.mockReturnValue( + keyService.getOrgKey.mockReturnValue( Promise.resolve(new SymmetricCryptoKey(new Uint8Array(32)) as OrgKey), ); @@ -368,7 +371,7 @@ describe("Cipher Service", () => { const keys = { userKey: originalUserKey, } as CipherDecryptionKeys; - cryptoService.cipherDecryptionKeys$.mockReturnValue(of(keys)); + keyService.cipherDecryptionKeys$.mockReturnValue(of(keys)); const cipher1 = new CipherView(cipherObj); cipher1.id = "Cipher 1"; @@ -387,7 +390,7 @@ describe("Cipher Service", () => { encryptedKey = new EncString("Re-encrypted Cipher Key"); encryptService.encrypt.mockResolvedValue(encryptedKey); - cryptoService.makeCipherKey.mockResolvedValue( + keyService.makeCipherKey.mockResolvedValue( new SymmetricCryptoKey(new Uint8Array(32)) as CipherKey, ); }); diff --git a/libs/common/src/vault/services/cipher.service.ts b/libs/common/src/vault/services/cipher.service.ts index 207a5da3cbf..154042601e9 100644 --- a/libs/common/src/vault/services/cipher.service.ts +++ b/libs/common/src/vault/services/cipher.service.ts @@ -15,6 +15,7 @@ import { AccountService } from "@bitwarden/common/auth/abstractions/account.serv import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; import { BulkEncryptService } from "@bitwarden/common/platform/abstractions/bulk-encrypt.service"; +import { KeyService } from "../../../../key-management/src/abstractions/key.service"; import { ApiService } from "../../abstractions/api.service"; import { SearchService } from "../../abstractions/search.service"; import { AutofillSettingsServiceAbstraction } from "../../autofill/services/autofill-settings.service"; @@ -24,7 +25,6 @@ import { ErrorResponse } from "../../models/response/error.response"; import { ListResponse } from "../../models/response/list.response"; import { View } from "../../models/view/view"; import { ConfigService } from "../../platform/abstractions/config/config.service"; -import { CryptoService } from "../../platform/abstractions/crypto.service"; import { EncryptService } from "../../platform/abstractions/encrypt.service"; import { I18nService } from "../../platform/abstractions/i18n.service"; import { StateService } from "../../platform/abstractions/state.service"; @@ -112,7 +112,7 @@ export class CipherService implements CipherServiceAbstraction { private addEditCipherInfoState: ActiveUserState; constructor( - private cryptoService: CryptoService, + private keyService: KeyService, private domainSettingsService: DomainSettingsService, private apiService: ApiService, private i18nService: I18nService, @@ -400,7 +400,7 @@ export class CipherService implements CipherServiceAbstraction { } private async decryptCiphers(ciphers: Cipher[], userId: UserId) { - const keys = await firstValueFrom(this.cryptoService.cipherDecryptionKeys$(userId, true)); + const keys = await firstValueFrom(this.keyService.cipherDecryptionKeys$(userId, true)); if (keys == null || (keys.userKey == null && Object.keys(keys.orgKeys).length === 0)) { // return early if there are no keys to decrypt with @@ -550,7 +550,7 @@ export class CipherService implements CipherServiceAbstraction { } const ciphers = response.data.map((cr) => new Cipher(new CipherData(cr))); - const key = await this.cryptoService.getOrgKey(organizationId); + const key = await this.keyService.getOrgKey(organizationId); let decCiphers: CipherView[] = []; if (await this.configService.getFeatureFlag(FeatureFlag.PM4154_BulkEncryptionService)) { decCiphers = await this.bulkEncryptService.decryptItems(ciphers, key); @@ -848,7 +848,7 @@ export class CipherService implements CipherServiceAbstraction { const encFileName = await this.encryptService.encrypt(filename, cipherEncKey); - const dataEncKey = await this.cryptoService.makeDataEncKey(cipherEncKey); + const dataEncKey = await this.keyService.makeDataEncKey(cipherEncKey); const encData = await this.encryptService.encryptToBytes(new Uint8Array(data), dataEncKey[0]); const response = await this.cipherFileUploadService.upload( @@ -1245,8 +1245,8 @@ export class CipherService implements CipherServiceAbstraction { async getKeyForCipherKeyDecryption(cipher: Cipher, userId: UserId): Promise { return ( - (await this.cryptoService.getOrgKey(cipher.organizationId)) || - ((await this.cryptoService.getUserKeyWithLegacySupport(userId)) as UserKey) + (await this.keyService.getOrgKey(cipher.organizationId)) || + ((await this.keyService.getUserKeyWithLegacySupport(userId)) as UserKey) ); } @@ -1294,7 +1294,7 @@ export class CipherService implements CipherServiceAbstraction { // In the case of a cipher that is being shared with an organization, we want to decrypt the // cipher key with the user's key and then re-encrypt it with the organization's key. private async encryptSharedCipher(model: CipherView, userId: UserId): Promise { - const keyForCipherKeyDecryption = await this.cryptoService.getUserKeyWithLegacySupport(userId); + const keyForCipherKeyDecryption = await this.keyService.getUserKeyWithLegacySupport(userId); return await this.encrypt(model, userId, null, keyForCipherKeyDecryption); } @@ -1371,14 +1371,14 @@ export class CipherService implements CipherServiceAbstraction { const encBuf = await EncArrayBuffer.fromResponse(attachmentResponse); const activeUserId = await firstValueFrom(this.accountService.activeAccount$); - const userKey = await this.cryptoService.getUserKeyWithLegacySupport(activeUserId.id); + const userKey = await this.keyService.getUserKeyWithLegacySupport(activeUserId.id); const decBuf = await this.encryptService.decryptToBytes(encBuf, userKey); let encKey: UserKey | OrgKey; - encKey = await this.cryptoService.getOrgKey(organizationId); - encKey ||= (await this.cryptoService.getUserKeyWithLegacySupport()) as UserKey; + encKey = await this.keyService.getOrgKey(organizationId); + encKey ||= (await this.keyService.getUserKeyWithLegacySupport()) as UserKey; - const dataEncKey = await this.cryptoService.makeDataEncKey(encKey); + const dataEncKey = await this.keyService.makeDataEncKey(encKey); const encFileName = await this.encryptService.encrypt(attachmentView.fileName, encKey); const encData = await this.encryptService.encryptToBytes(new Uint8Array(decBuf), dataEncKey[0]); @@ -1679,7 +1679,7 @@ export class CipherService implements CipherServiceAbstraction { // First, we get the key for cipher key encryption, in its decrypted form let decryptedCipherKey: SymmetricCryptoKey; if (cipher.key == null) { - decryptedCipherKey = await this.cryptoService.makeCipherKey(); + decryptedCipherKey = await this.keyService.makeCipherKey(); } else { decryptedCipherKey = new SymmetricCryptoKey( await this.encryptService.decryptToBytes(cipher.key, keyForCipherKeyDecryption), diff --git a/libs/common/src/vault/services/folder/folder.service.spec.ts b/libs/common/src/vault/services/folder/folder.service.spec.ts index 05e1cdebc93..193d0e85e61 100644 --- a/libs/common/src/vault/services/folder/folder.service.spec.ts +++ b/libs/common/src/vault/services/folder/folder.service.spec.ts @@ -1,11 +1,11 @@ import { mock, MockProxy } from "jest-mock-extended"; import { firstValueFrom } from "rxjs"; +import { KeyService } from "../../../../../key-management/src/abstractions/key.service"; import { makeStaticByteArray } from "../../../../spec"; import { FakeAccountService, mockAccountServiceWith } from "../../../../spec/fake-account-service"; import { FakeActiveUserState } from "../../../../spec/fake-state"; import { FakeStateProvider } from "../../../../spec/fake-state-provider"; -import { CryptoService } from "../../../platform/abstractions/crypto.service"; import { EncryptService } from "../../../platform/abstractions/encrypt.service"; import { I18nService } from "../../../platform/abstractions/i18n.service"; import { Utils } from "../../../platform/misc/utils"; @@ -22,7 +22,7 @@ import { FOLDER_ENCRYPTED_FOLDERS } from "../key-state/folder.state"; describe("Folder Service", () => { let folderService: FolderService; - let cryptoService: MockProxy; + let keyService: MockProxy; let encryptService: MockProxy; let i18nService: MockProxy; let cipherService: MockProxy; @@ -33,7 +33,7 @@ describe("Folder Service", () => { let folderState: FakeActiveUserState>; beforeEach(() => { - cryptoService = mock(); + keyService = mock(); encryptService = mock(); i18nService = mock(); cipherService = mock(); @@ -43,14 +43,14 @@ describe("Folder Service", () => { i18nService.collator = new Intl.Collator("en"); - cryptoService.hasUserKey.mockResolvedValue(true); - cryptoService.getUserKeyWithLegacySupport.mockResolvedValue( + keyService.hasUserKey.mockResolvedValue(true); + keyService.getUserKeyWithLegacySupport.mockResolvedValue( new SymmetricCryptoKey(makeStaticByteArray(32)) as UserKey, ); encryptService.decryptToUtf8.mockResolvedValue("DEC"); folderService = new FolderService( - cryptoService, + keyService, encryptService, i18nService, cipherService, diff --git a/libs/common/src/vault/services/folder/folder.service.ts b/libs/common/src/vault/services/folder/folder.service.ts index 2adbc8c6d0e..2a76e82f3b7 100644 --- a/libs/common/src/vault/services/folder/folder.service.ts +++ b/libs/common/src/vault/services/folder/folder.service.ts @@ -2,7 +2,7 @@ import { Observable, firstValueFrom, map, shareReplay } from "rxjs"; import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; -import { CryptoService } from "../../../platform/abstractions/crypto.service"; +import { KeyService } from "../../../../../key-management/src/abstractions/key.service"; import { I18nService } from "../../../platform/abstractions/i18n.service"; import { Utils } from "../../../platform/misc/utils"; import { SymmetricCryptoKey } from "../../../platform/models/domain/symmetric-crypto-key"; @@ -26,7 +26,7 @@ export class FolderService implements InternalFolderServiceAbstraction { private decryptedFoldersState: DerivedState; constructor( - private cryptoService: CryptoService, + private keyService: KeyService, private encryptService: EncryptService, private i18nService: I18nService, private cipherService: CipherService, @@ -36,7 +36,7 @@ export class FolderService implements InternalFolderServiceAbstraction { this.decryptedFoldersState = this.stateProvider.getDerived( this.encryptedFoldersState.state$, FOLDER_DECRYPTED_FOLDERS, - { folderService: this, cryptoService: this.cryptoService }, + { folderService: this, keyService: this.keyService }, ); this.folders$ = this.encryptedFoldersState.state$.pipe( diff --git a/libs/common/src/vault/services/key-state/folder.state.spec.ts b/libs/common/src/vault/services/key-state/folder.state.spec.ts index 072372f55d8..ece66b5d451 100644 --- a/libs/common/src/vault/services/key-state/folder.state.spec.ts +++ b/libs/common/src/vault/services/key-state/folder.state.spec.ts @@ -1,6 +1,6 @@ import { mock } from "jest-mock-extended"; -import { CryptoService } from "../../../platform/abstractions/crypto.service"; +import { KeyService } from "../../../../../key-management/src/abstractions/key.service"; import { FolderService } from "../../abstractions/folder/folder.service.abstraction"; import { FolderData } from "../../models/data/folder.data"; import { Folder } from "../../models/domain/folder"; @@ -31,7 +31,7 @@ describe("encrypted folders", () => { }); describe("derived decrypted folders", () => { - const cryptoService = mock(); + const keyService = mock(); const folderService = mock(); const sut = FOLDER_DECRYPTED_FOLDERS; let data: FolderData; @@ -64,13 +64,13 @@ describe("derived decrypted folders", () => { it("should derive encrypted folders", async () => { const folderViewMock = new FolderView(new Folder(data)); - cryptoService.hasUserKey.mockResolvedValue(true); + keyService.hasUserKey.mockResolvedValue(true); folderService.decryptFolders.mockResolvedValue([folderViewMock]); const encryptedFoldersState = { id: data }; const derivedStateResult = await sut.derive(encryptedFoldersState, { folderService, - cryptoService, + keyService, }); expect(derivedStateResult).toEqual([folderViewMock]); diff --git a/libs/common/src/vault/services/key-state/folder.state.ts b/libs/common/src/vault/services/key-state/folder.state.ts index 1a45c88d6f2..7262d72d58e 100644 --- a/libs/common/src/vault/services/key-state/folder.state.ts +++ b/libs/common/src/vault/services/key-state/folder.state.ts @@ -1,6 +1,6 @@ import { Jsonify } from "type-fest"; -import { CryptoService } from "../../../platform/abstractions/crypto.service"; +import { KeyService } from "../../../../../key-management/src/abstractions/key.service"; import { DeriveDefinition, FOLDER_DISK, UserKeyDefinition } from "../../../platform/state"; import { FolderService } from "../../abstractions/folder/folder.service.abstraction"; import { FolderData } from "../../models/data/folder.data"; @@ -19,13 +19,13 @@ export const FOLDER_ENCRYPTED_FOLDERS = UserKeyDefinition.record( export const FOLDER_DECRYPTED_FOLDERS = DeriveDefinition.from< Record, FolderView[], - { folderService: FolderService; cryptoService: CryptoService } + { folderService: FolderService; keyService: KeyService } >(FOLDER_ENCRYPTED_FOLDERS, { deserializer: (obj) => obj.map((f) => FolderView.fromJSON(f)), - derive: async (from, { folderService, cryptoService }) => { + derive: async (from, { folderService, keyService }) => { const folders = Object.values(from || {}).map((f) => new Folder(f)); - if (await cryptoService.hasUserKey()) { + if (await keyService.hasUserKey()) { return await folderService.decryptFolders(folders); } else { return []; diff --git a/libs/common/tsconfig.json b/libs/common/tsconfig.json index 11cdb4e44c0..99c58f3cf24 100644 --- a/libs/common/tsconfig.json +++ b/libs/common/tsconfig.json @@ -1,5 +1,12 @@ { "extends": "../shared/tsconfig.libs", - "include": ["src", "spec", "./custom-matchers.d.ts"], + "include": [ + "src", + "spec", + "./custom-matchers.d.ts", + "../key-management/src/key.service.spec.ts", + "../key-management/src/key.service.ts", + "../key-management/src/abstractions/key.service.ts" + ], "exclude": ["node_modules", "dist"] } diff --git a/libs/components/src/async-actions/in-forms.mdx b/libs/components/src/async-actions/in-forms.mdx index e0715fed419..6b1ab864248 100644 --- a/libs/components/src/async-actions/in-forms.mdx +++ b/libs/components/src/async-actions/in-forms.mdx @@ -42,7 +42,7 @@ class Component { return; } - await this.cryptoService.encrypt(/* ... */); + await this.keyService.encrypt(/* ... */); // `formGroup.invalid` will always return `true` here diff --git a/libs/importer/spec/bitwarden-password-protected-importer.spec.ts b/libs/importer/spec/bitwarden-password-protected-importer.spec.ts index e5100e49900..d15aa61c8a7 100644 --- a/libs/importer/spec/bitwarden-password-protected-importer.spec.ts +++ b/libs/importer/spec/bitwarden-password-protected-importer.spec.ts @@ -2,12 +2,12 @@ import { mock, MockProxy } from "jest-mock-extended"; import { PinServiceAbstraction } from "@bitwarden/auth/common"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; -import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service"; import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { KdfType } from "@bitwarden/common/platform/enums"; import { Utils } from "@bitwarden/common/platform/misc/utils"; import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; +import { KeyService } from "@bitwarden/key-management"; import { BitwardenPasswordProtectedImporter, @@ -19,7 +19,7 @@ import { emptyUnencryptedExport } from "./test-data/bitwarden-json/unencrypted.j describe("BitwardenPasswordProtectedImporter", () => { let importer: BitwardenPasswordProtectedImporter; - let cryptoService: MockProxy; + let keyService: MockProxy; let encryptService: MockProxy; let i18nService: MockProxy; let cipherService: MockProxy; @@ -31,7 +31,7 @@ describe("BitwardenPasswordProtectedImporter", () => { }; beforeEach(() => { - cryptoService = mock(); + keyService = mock(); encryptService = mock(); i18nService = mock(); cipherService = mock(); @@ -39,7 +39,7 @@ describe("BitwardenPasswordProtectedImporter", () => { accountService = mock(); importer = new BitwardenPasswordProtectedImporter( - cryptoService, + keyService, encryptService, i18nService, cipherService, diff --git a/libs/importer/src/components/import.component.ts b/libs/importer/src/components/import.component.ts index 9be0428c865..1ffe2728b05 100644 --- a/libs/importer/src/components/import.component.ts +++ b/libs/importer/src/components/import.component.ts @@ -30,7 +30,6 @@ import { PolicyType } from "@bitwarden/common/admin-console/enums"; import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { ClientType } from "@bitwarden/common/enums"; -import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service"; import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; @@ -56,6 +55,7 @@ import { SelectModule, ToastService, } from "@bitwarden/components"; +import { KeyService } from "@bitwarden/key-management"; import { ImportOption, ImportResult, ImportType } from "../models"; import { @@ -88,7 +88,7 @@ const safeProviders: SafeProvider[] = [ ImportApiServiceAbstraction, I18nService, CollectionService, - CryptoService, + KeyService, EncryptService, PinServiceAbstraction, AccountService, diff --git a/libs/importer/src/importers/bitwarden/bitwarden-json-importer.ts b/libs/importer/src/importers/bitwarden/bitwarden-json-importer.ts index 160a55e6f28..42033b3d61a 100644 --- a/libs/importer/src/importers/bitwarden/bitwarden-json-importer.ts +++ b/libs/importer/src/importers/bitwarden/bitwarden-json-importer.ts @@ -8,7 +8,6 @@ import { CollectionWithIdExport, FolderWithIdExport, } from "@bitwarden/common/models/export"; -import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service"; import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { EncString } from "@bitwarden/common/platform/models/domain/enc-string"; @@ -16,6 +15,7 @@ import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/sym import { OrganizationId } from "@bitwarden/common/types/guid"; import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; import { FolderView } from "@bitwarden/common/vault/models/view/folder.view"; +import { KeyService } from "@bitwarden/key-management"; import { BitwardenEncryptedIndividualJsonExport, BitwardenEncryptedOrgJsonExport, @@ -32,7 +32,7 @@ export class BitwardenJsonImporter extends BaseImporter implements Importer { private result: ImportResult; protected constructor( - protected cryptoService: CryptoService, + protected keyService: KeyService, protected encryptService: EncryptService, protected i18nService: I18nService, protected cipherService: CipherService, @@ -63,11 +63,11 @@ export class BitwardenJsonImporter extends BaseImporter implements Importer { results: BitwardenEncryptedIndividualJsonExport | BitwardenEncryptedOrgJsonExport, ) { if (results.encKeyValidation_DO_NOT_EDIT != null) { - let keyForDecryption: SymmetricCryptoKey = await this.cryptoService.getOrgKey( + let keyForDecryption: SymmetricCryptoKey = await this.keyService.getOrgKey( this.organizationId, ); if (keyForDecryption == null) { - keyForDecryption = await this.cryptoService.getUserKeyWithLegacySupport(); + keyForDecryption = await this.keyService.getUserKeyWithLegacySupport(); } const encKeyValidation = new EncString(results.encKeyValidation_DO_NOT_EDIT); const encKeyValidationDecrypt = await this.encryptService.decryptToUtf8( @@ -210,8 +210,8 @@ export class BitwardenJsonImporter extends BaseImporter implements Importer { if (data.encrypted) { const collection = CollectionWithIdExport.toDomain(c); collection.organizationId = this.organizationId; - collectionView = await firstValueFrom(this.cryptoService.activeUserOrgKeys$).then( - (orgKeys) => collection.decrypt(orgKeys[c.organizationId as OrganizationId]), + collectionView = await firstValueFrom(this.keyService.activeUserOrgKeys$).then((orgKeys) => + collection.decrypt(orgKeys[c.organizationId as OrganizationId]), ); } else { collectionView = CollectionWithIdExport.toView(c); diff --git a/libs/importer/src/importers/bitwarden/bitwarden-password-protected-importer.ts b/libs/importer/src/importers/bitwarden/bitwarden-password-protected-importer.ts index 35a0ec0f22c..fa19e3c0001 100644 --- a/libs/importer/src/importers/bitwarden/bitwarden-password-protected-importer.ts +++ b/libs/importer/src/importers/bitwarden/bitwarden-password-protected-importer.ts @@ -5,13 +5,13 @@ import { KdfConfig, PBKDF2KdfConfig, } from "@bitwarden/common/auth/models/domain/kdf-config"; -import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service"; import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { KdfType } from "@bitwarden/common/platform/enums"; import { EncString } from "@bitwarden/common/platform/models/domain/enc-string"; import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key"; import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; +import { KeyService } from "@bitwarden/key-management"; import { BitwardenPasswordProtectedFileFormat } from "@bitwarden/vault-export-core"; import { ImportResult } from "../../models/import-result"; @@ -23,7 +23,7 @@ export class BitwardenPasswordProtectedImporter extends BitwardenJsonImporter im private key: SymmetricCryptoKey; constructor( - cryptoService: CryptoService, + keyService: KeyService, encryptService: EncryptService, i18nService: I18nService, cipherService: CipherService, @@ -31,7 +31,7 @@ export class BitwardenPasswordProtectedImporter extends BitwardenJsonImporter im accountService: AccountService, private promptForPassword_callback: () => Promise, ) { - super(cryptoService, encryptService, i18nService, cipherService, pinService, accountService); + super(keyService, encryptService, i18nService, cipherService, pinService, accountService); } async parse(data: string): Promise { diff --git a/libs/importer/src/services/import.service.spec.ts b/libs/importer/src/services/import.service.spec.ts index c221e15aa7f..8b497beac93 100644 --- a/libs/importer/src/services/import.service.spec.ts +++ b/libs/importer/src/services/import.service.spec.ts @@ -3,7 +3,6 @@ import { mock, MockProxy } from "jest-mock-extended"; import { CollectionService, CollectionView } from "@bitwarden/admin-console/common"; import { PinServiceAbstraction } from "@bitwarden/auth/common"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; -import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service"; import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { Utils } from "@bitwarden/common/platform/misc/utils"; @@ -11,6 +10,7 @@ import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.servi import { FolderService } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; import { FolderView } from "@bitwarden/common/vault/models/view/folder.view"; +import { KeyService } from "@bitwarden/key-management"; import { BitwardenPasswordProtectedImporter } from "../importers/bitwarden/bitwarden-password-protected-importer"; import { Importer } from "../importers/importer"; @@ -26,7 +26,7 @@ describe("ImportService", () => { let importApiService: MockProxy; let i18nService: MockProxy; let collectionService: MockProxy; - let cryptoService: MockProxy; + let keyService: MockProxy; let encryptService: MockProxy; let pinService: MockProxy; let accountService: MockProxy; @@ -37,7 +37,7 @@ describe("ImportService", () => { importApiService = mock(); i18nService = mock(); collectionService = mock(); - cryptoService = mock(); + keyService = mock(); encryptService = mock(); pinService = mock(); @@ -47,7 +47,7 @@ describe("ImportService", () => { importApiService, i18nService, collectionService, - cryptoService, + keyService, encryptService, pinService, accountService, diff --git a/libs/importer/src/services/import.service.ts b/libs/importer/src/services/import.service.ts index 1e983aa3d1b..17695c29d57 100644 --- a/libs/importer/src/services/import.service.ts +++ b/libs/importer/src/services/import.service.ts @@ -11,7 +11,6 @@ import { ImportCiphersRequest } from "@bitwarden/common/models/request/import-ci import { ImportOrganizationCiphersRequest } from "@bitwarden/common/models/request/import-organization-ciphers.request"; import { KvpRequest } from "@bitwarden/common/models/request/kvp.request"; import { ErrorResponse } from "@bitwarden/common/models/response/error.response"; -import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service"; import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { Utils } from "@bitwarden/common/platform/misc/utils"; @@ -22,6 +21,7 @@ import { CipherRequest } from "@bitwarden/common/vault/models/request/cipher.req import { FolderWithIdRequest } from "@bitwarden/common/vault/models/request/folder-with-id.request"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; import { FolderView } from "@bitwarden/common/vault/models/view/folder.view"; +import { KeyService } from "@bitwarden/key-management"; import { AscendoCsvImporter, @@ -106,7 +106,7 @@ export class ImportService implements ImportServiceAbstraction { private importApiService: ImportApiServiceAbstraction, private i18nService: I18nService, private collectionService: CollectionService, - private cryptoService: CryptoService, + private keyService: KeyService, private encryptService: EncryptService, private pinService: PinServiceAbstraction, private accountService: AccountService, @@ -210,7 +210,7 @@ export class ImportService implements ImportServiceAbstraction { case "bitwardenjson": case "bitwardenpasswordprotected": return new BitwardenPasswordProtectedImporter( - this.cryptoService, + this.keyService, this.encryptService, this.i18nService, this.cipherService, @@ -349,7 +349,7 @@ export class ImportService implements ImportServiceAbstraction { const c = await this.cipherService.encrypt(importResult.ciphers[i], activeUserId); request.ciphers.push(new CipherRequest(c)); } - const userKey = await this.cryptoService.getUserKeyWithLegacySupport(activeUserId); + const userKey = await this.keyService.getUserKeyWithLegacySupport(activeUserId); if (importResult.folders != null) { for (let i = 0; i < importResult.folders.length; i++) { const f = await this.folderService.encrypt(importResult.folders[i], userKey); diff --git a/libs/common/src/platform/abstractions/crypto.service.ts b/libs/key-management/src/abstractions/key.service.ts similarity index 94% rename from libs/common/src/platform/abstractions/crypto.service.ts rename to libs/key-management/src/abstractions/key.service.ts index 0a554f6249b..55ffea9db79 100644 --- a/libs/common/src/platform/abstractions/crypto.service.ts +++ b/libs/key-management/src/abstractions/key.service.ts @@ -1,11 +1,15 @@ import { Observable } from "rxjs"; -import { EncryptedOrganizationKeyData } from "../../admin-console/models/data/encrypted-organization-key.data"; -import { ProfileOrganizationResponse } from "../../admin-console/models/response/profile-organization.response"; -import { ProfileProviderOrganizationResponse } from "../../admin-console/models/response/profile-provider-organization.response"; -import { ProfileProviderResponse } from "../../admin-console/models/response/profile-provider.response"; -import { KdfConfig } from "../../auth/models/domain/kdf-config"; -import { OrganizationId, UserId } from "../../types/guid"; +import { EncryptedOrganizationKeyData } from "@bitwarden/common/admin-console/models/data/encrypted-organization-key.data"; + +import { ProfileOrganizationResponse } from "../../../common/src/admin-console/models/response/profile-organization.response"; +import { ProfileProviderOrganizationResponse } from "../../../common/src/admin-console/models/response/profile-provider-organization.response"; +import { ProfileProviderResponse } from "../../../common/src/admin-console/models/response/profile-provider.response"; +import { KdfConfig } from "../../../common/src/auth/models/domain/kdf-config"; +import { KeySuffixOptions, HashPurpose } from "../../../common/src/platform/enums"; +import { EncryptedString, EncString } from "../../../common/src/platform/models/domain/enc-string"; +import { SymmetricCryptoKey } from "../../../common/src/platform/models/domain/symmetric-crypto-key"; +import { OrganizationId, UserId } from "../../../common/src/types/guid"; import { UserKey, MasterKey, @@ -14,10 +18,7 @@ import { CipherKey, UserPrivateKey, UserPublicKey, -} from "../../types/key"; -import { KeySuffixOptions, HashPurpose } from "../enums"; -import { EncryptedString, EncString } from "../models/domain/enc-string"; -import { SymmetricCryptoKey } from "../models/domain/symmetric-crypto-key"; +} from "../../../common/src/types/key"; export class UserPrivateKeyDecryptionFailedError extends Error { constructor() { @@ -40,7 +41,7 @@ export type CipherDecryptionKeys = { orgKeys: Record; }; -export abstract class CryptoService { +export abstract class KeyService { /** * Retrieves a stream of the given users {@see UserKey} values. Can emit null if the user does not have a user key, e.g. the user * is in a locked or logged out state. diff --git a/libs/key-management/src/index.ts b/libs/key-management/src/index.ts index 298ffd145fd..f2bb5e30166 100644 --- a/libs/key-management/src/index.ts +++ b/libs/key-management/src/index.ts @@ -4,3 +4,6 @@ export { } from "./biometrics/biometric-state.service"; export { BiometricsService } from "./biometrics/biometric.service"; export * from "./biometrics/biometric.state"; + +export { KeyService } from "./abstractions/key.service"; +export { DefaultKeyService } from "./key.service"; diff --git a/libs/common/src/platform/services/crypto.service.spec.ts b/libs/key-management/src/key.service.spec.ts similarity index 80% rename from libs/common/src/platform/services/crypto.service.spec.ts rename to libs/key-management/src/key.service.spec.ts index 769e6942b05..263779f59b3 100644 --- a/libs/common/src/platform/services/crypto.service.spec.ts +++ b/libs/key-management/src/key.service.spec.ts @@ -1,48 +1,48 @@ import { mock } from "jest-mock-extended"; import { bufferCount, firstValueFrom, lastValueFrom, of, take, tap } from "rxjs"; -import { PinServiceAbstraction } from "../../../../auth/src/common/abstractions"; +import { PinServiceAbstraction } from "../../auth/src/common/abstractions"; import { awaitAsync, makeEncString, makeStaticByteArray, makeSymmetricCryptoKey, -} from "../../../spec"; -import { FakeAccountService, mockAccountServiceWith } from "../../../spec/fake-account-service"; -import { FakeActiveUserState, FakeSingleUserState } from "../../../spec/fake-state"; -import { FakeStateProvider } from "../../../spec/fake-state-provider"; -import { EncryptedOrganizationKeyData } from "../../admin-console/models/data/encrypted-organization-key.data"; -import { KdfConfigService } from "../../auth/abstractions/kdf-config.service"; -import { FakeMasterPasswordService } from "../../auth/services/master-password/fake-master-password.service"; -import { VAULT_TIMEOUT } from "../../services/vault-timeout/vault-timeout-settings.state"; -import { CsprngArray } from "../../types/csprng"; -import { OrganizationId, UserId } from "../../types/guid"; -import { UserKey, MasterKey } from "../../types/key"; -import { VaultTimeoutStringType } from "../../types/vault-timeout.type"; -import { CryptoFunctionService } from "../abstractions/crypto-function.service"; -import { UserPrivateKeyDecryptionFailedError } from "../abstractions/crypto.service"; -import { EncryptService } from "../abstractions/encrypt.service"; -import { KeyGenerationService } from "../abstractions/key-generation.service"; -import { LogService } from "../abstractions/log.service"; -import { PlatformUtilsService } from "../abstractions/platform-utils.service"; -import { StateService } from "../abstractions/state.service"; -import { Encrypted } from "../interfaces/encrypted"; -import { Utils } from "../misc/utils"; -import { EncString, EncryptedString } from "../models/domain/enc-string"; -import { SymmetricCryptoKey } from "../models/domain/symmetric-crypto-key"; -import { CryptoService } from "../services/crypto.service"; -import { UserKeyDefinition } from "../state"; - -import { USER_ENCRYPTED_ORGANIZATION_KEYS } from "./key-state/org-keys.state"; -import { USER_ENCRYPTED_PROVIDER_KEYS } from "./key-state/provider-keys.state"; +} from "../../common/spec"; +import { FakeAccountService, mockAccountServiceWith } from "../../common/spec/fake-account-service"; +import { FakeActiveUserState, FakeSingleUserState } from "../../common/spec/fake-state"; +import { FakeStateProvider } from "../../common/spec/fake-state-provider"; +import { EncryptedOrganizationKeyData } from "../../common/src/admin-console/models/data/encrypted-organization-key.data"; +import { KdfConfigService } from "../../common/src/auth/abstractions/kdf-config.service"; +import { FakeMasterPasswordService } from "../../common/src/auth/services/master-password/fake-master-password.service"; +import { CryptoFunctionService } from "../../common/src/platform/abstractions/crypto-function.service"; +import { EncryptService } from "../../common/src/platform/abstractions/encrypt.service"; +import { KeyGenerationService } from "../../common/src/platform/abstractions/key-generation.service"; +import { LogService } from "../../common/src/platform/abstractions/log.service"; +import { PlatformUtilsService } from "../../common/src/platform/abstractions/platform-utils.service"; +import { StateService } from "../../common/src/platform/abstractions/state.service"; +import { Encrypted } from "../../common/src/platform/interfaces/encrypted"; +import { Utils } from "../../common/src/platform/misc/utils"; +import { EncString, EncryptedString } from "../../common/src/platform/models/domain/enc-string"; +import { SymmetricCryptoKey } from "../../common/src/platform/models/domain/symmetric-crypto-key"; +import { USER_ENCRYPTED_ORGANIZATION_KEYS } from "../../common/src/platform/services/key-state/org-keys.state"; +import { USER_ENCRYPTED_PROVIDER_KEYS } from "../../common/src/platform/services/key-state/provider-keys.state"; import { USER_ENCRYPTED_PRIVATE_KEY, USER_EVER_HAD_USER_KEY, USER_KEY, -} from "./key-state/user-key.state"; +} from "../../common/src/platform/services/key-state/user-key.state"; +import { UserKeyDefinition } from "../../common/src/platform/state"; +import { VAULT_TIMEOUT } from "../../common/src/services/vault-timeout/vault-timeout-settings.state"; +import { CsprngArray } from "../../common/src/types/csprng"; +import { OrganizationId, UserId } from "../../common/src/types/guid"; +import { UserKey, MasterKey } from "../../common/src/types/key"; +import { VaultTimeoutStringType } from "../../common/src/types/vault-timeout.type"; -describe("cryptoService", () => { - let cryptoService: CryptoService; +import { UserPrivateKeyDecryptionFailedError } from "./abstractions/key.service"; +import { DefaultKeyService } from "./key.service"; + +describe("keyService", () => { + let keyService: DefaultKeyService; const pinService = mock(); const keyGenerationService = mock(); @@ -63,7 +63,7 @@ describe("cryptoService", () => { masterPasswordService = new FakeMasterPasswordService(); stateProvider = new FakeStateProvider(accountService); - cryptoService = new CryptoService( + keyService = new DefaultKeyService( pinService, masterPasswordService, keyGenerationService, @@ -83,7 +83,7 @@ describe("cryptoService", () => { }); it("instantiates", () => { - expect(cryptoService).not.toBeFalsy(); + expect(keyService).not.toBeFalsy(); }); describe("getUserKey", () => { @@ -95,7 +95,7 @@ describe("cryptoService", () => { }); it("retrieves the key state of the requested user", async () => { - await cryptoService.getUserKey(mockUserId); + await keyService.getUserKey(mockUserId); expect(stateProvider.mock.getUserState$).toHaveBeenCalledWith(USER_KEY, mockUserId); }); @@ -103,13 +103,13 @@ describe("cryptoService", () => { it("returns the User Key if available", async () => { stateProvider.singleUser.getFake(mockUserId, USER_KEY).nextState(mockUserKey); - const userKey = await cryptoService.getUserKey(mockUserId); + const userKey = await keyService.getUserKey(mockUserId); expect(userKey).toEqual(mockUserKey); }); it("returns nullish if the user key is not set", async () => { - const userKey = await cryptoService.getUserKey(mockUserId); + const userKey = await keyService.getUserKey(mockUserId); expect(userKey).toBeFalsy(); }); @@ -129,12 +129,12 @@ describe("cryptoService", () => { stateProvider.singleUser .getFake(mockUserId, USER_KEY) .nextState(hasKey ? mockUserKey : null); - expect(await cryptoService[method](mockUserId)).toBe(hasKey); + expect(await keyService[method](mockUserId)).toBe(hasKey); }); it("returns false when no active userId is set", async () => { accountService.activeAccountSubject.next(null); - expect(await cryptoService[method]()).toBe(false); + expect(await keyService[method]()).toBe(false); }); it.each([true, false])( @@ -144,7 +144,7 @@ describe("cryptoService", () => { stateProvider.singleUser .getFake(mockUserId, USER_KEY) .nextState(hasKey ? mockUserKey : null); - expect(await cryptoService[method]()).toBe(hasKey); + expect(await keyService[method]()).toBe(hasKey); }, ); }, @@ -165,9 +165,9 @@ describe("cryptoService", () => { it("returns the User Key if available", async () => { stateProvider.singleUser.getFake(mockUserId, USER_KEY).nextState(mockUserKey); - const getKeySpy = jest.spyOn(cryptoService, "getUserKey"); + const getKeySpy = jest.spyOn(keyService, "getUserKey"); - const userKey = await cryptoService.getUserKeyWithLegacySupport(mockUserId); + const userKey = await keyService.getUserKeyWithLegacySupport(mockUserId); expect(getKeySpy).toHaveBeenCalledWith(mockUserId); expect(getMasterKey).not.toHaveBeenCalled(); @@ -178,7 +178,7 @@ describe("cryptoService", () => { it("returns the user's master key when User Key is not available", async () => { masterPasswordService.masterKeySubject.next(mockMasterKey); - const userKey = await cryptoService.getUserKeyWithLegacySupport(mockUserId); + const userKey = await keyService.getUserKeyWithLegacySupport(mockUserId); expect(getMasterKey).toHaveBeenCalledWith(mockUserId); expect(userKey).toEqual(mockMasterKey); @@ -195,19 +195,19 @@ describe("cryptoService", () => { it("should return true when stored value is true", async () => { everHadUserKeyState.nextState(true); - expect(await firstValueFrom(cryptoService.everHadUserKey$)).toBe(true); + expect(await firstValueFrom(keyService.everHadUserKey$)).toBe(true); }); it("should return false when stored value is false", async () => { everHadUserKeyState.nextState(false); - expect(await firstValueFrom(cryptoService.everHadUserKey$)).toBe(false); + expect(await firstValueFrom(keyService.everHadUserKey$)).toBe(false); }); it("should return false when stored value is null", async () => { everHadUserKeyState.nextState(null); - expect(await firstValueFrom(cryptoService.everHadUserKey$)).toBe(false); + expect(await firstValueFrom(keyService.everHadUserKey$)).toBe(false); }); }); @@ -225,7 +225,7 @@ describe("cryptoService", () => { }); it("should set everHadUserKey if key is not null to true", async () => { - await cryptoService.setUserKey(mockUserKey, mockUserId); + await keyService.setUserKey(mockUserKey, mockUserId); expect(await firstValueFrom(everHadUserKeyState.state$)).toBe(true); }); @@ -234,7 +234,7 @@ describe("cryptoService", () => { it("sets an Auto key if vault timeout is set to 'never'", async () => { await stateProvider.setUserState(VAULT_TIMEOUT, VaultTimeoutStringType.Never, mockUserId); - await cryptoService.setUserKey(mockUserKey, mockUserId); + await keyService.setUserKey(mockUserKey, mockUserId); expect(stateService.setUserKeyAutoUnlock).toHaveBeenCalledWith(mockUserKey.keyB64, { userId: mockUserId, @@ -244,7 +244,7 @@ describe("cryptoService", () => { it("clears the Auto key if vault timeout is set to anything other than null", async () => { await stateProvider.setUserState(VAULT_TIMEOUT, 10, mockUserId); - await cryptoService.setUserKey(mockUserKey, mockUserId); + await keyService.setUserKey(mockUserKey, mockUserId); expect(stateService.setUserKeyAutoUnlock).toHaveBeenCalledWith(null, { userId: mockUserId, @@ -252,7 +252,7 @@ describe("cryptoService", () => { }); it("clears the old deprecated Auto key whenever a User Key is set", async () => { - await cryptoService.setUserKey(mockUserKey, mockUserId); + await keyService.setUserKey(mockUserKey, mockUserId); expect(stateService.setCryptoMasterKeyAuto).toHaveBeenCalledWith(null, { userId: mockUserId, @@ -261,13 +261,11 @@ describe("cryptoService", () => { }); it("throws if key is null", async () => { - await expect(cryptoService.setUserKey(null, mockUserId)).rejects.toThrow("No key provided."); + await expect(keyService.setUserKey(null, mockUserId)).rejects.toThrow("No key provided."); }); it("throws if userId is null", async () => { - await expect(cryptoService.setUserKey(mockUserKey, null)).rejects.toThrow( - "No userId provided.", - ); + await expect(keyService.setUserKey(mockUserKey, null)).rejects.toThrow("No userId provided."); }); describe("Pin Key refresh", () => { @@ -285,7 +283,7 @@ describe("cryptoService", () => { mockPinKeyEncryptedUserKey, ); - await cryptoService.setUserKey(mockUserKey, mockUserId); + await keyService.setUserKey(mockUserKey, mockUserId); expect(pinService.storePinKeyEncryptedUserKey).toHaveBeenCalledWith( mockPinKeyEncryptedUserKey, @@ -299,7 +297,7 @@ describe("cryptoService", () => { pinService.getUserKeyEncryptedPin.mockResolvedValue(mockUserKeyEncryptedPin); pinService.getPinKeyEncryptedUserKeyPersistent.mockResolvedValue(null); - await cryptoService.setUserKey(mockUserKey, mockUserId); + await keyService.setUserKey(mockUserKey, mockUserId); expect(pinService.storePinKeyEncryptedUserKey).toHaveBeenCalledWith( mockPinKeyEncryptedUserKey, @@ -311,7 +309,7 @@ describe("cryptoService", () => { it("clears the pinKeyEncryptedUserKeyPersistent and pinKeyEncryptedUserKeyEphemeral if the UserKeyEncryptedPin is not set", async () => { pinService.getUserKeyEncryptedPin.mockResolvedValue(null); - await cryptoService.setUserKey(mockUserKey, mockUserId); + await keyService.setUserKey(mockUserKey, mockUserId); expect(pinService.clearPinKeyEncryptedUserKeyPersistent).toHaveBeenCalledWith(mockUserId); expect(pinService.clearPinKeyEncryptedUserKeyEphemeral).toHaveBeenCalledWith(mockUserId); @@ -338,19 +336,19 @@ describe("cryptoService", () => { }); it("throws if userKey is null", async () => { - await expect(cryptoService.setUserKeys(null, mockEncPrivateKey, mockUserId)).rejects.toThrow( + await expect(keyService.setUserKeys(null, mockEncPrivateKey, mockUserId)).rejects.toThrow( "No userKey provided.", ); }); it("throws if encPrivateKey is null", async () => { - await expect(cryptoService.setUserKeys(mockUserKey, null, mockUserId)).rejects.toThrow( + await expect(keyService.setUserKeys(mockUserKey, null, mockUserId)).rejects.toThrow( "No encPrivateKey provided.", ); }); it("throws if userId is null", async () => { - await expect(cryptoService.setUserKeys(mockUserKey, mockEncPrivateKey, null)).rejects.toThrow( + await expect(keyService.setUserKeys(mockUserKey, mockEncPrivateKey, null)).rejects.toThrow( "No userId provided.", ); }); @@ -359,15 +357,15 @@ describe("cryptoService", () => { encryptService.decryptToBytes.mockResolvedValue(null); await expect( - cryptoService.setUserKeys(mockUserKey, mockEncPrivateKey, mockUserId), + keyService.setUserKeys(mockUserKey, mockEncPrivateKey, mockUserId), ).rejects.toThrow(UserPrivateKeyDecryptionFailedError); }); // We already have tests for setUserKey, so we just need to test that the correct methods are called it("calls setUserKey with the userKey and userId", async () => { - const setUserKeySpy = jest.spyOn(cryptoService, "setUserKey"); + const setUserKeySpy = jest.spyOn(keyService, "setUserKey"); - await cryptoService.setUserKeys(mockUserKey, mockEncPrivateKey, mockUserId); + await keyService.setUserKeys(mockUserKey, mockEncPrivateKey, mockUserId); expect(setUserKeySpy).toHaveBeenCalledWith(mockUserKey, mockUserId); }); @@ -375,9 +373,9 @@ describe("cryptoService", () => { // We already have tests for setPrivateKey, so we just need to test that the correct methods are called // TODO: Move those tests into here since `setPrivateKey` will be converted to a private method it("calls setPrivateKey with the encPrivateKey and userId", async () => { - const setEncryptedPrivateKeySpy = jest.spyOn(cryptoService, "setPrivateKey"); + const setEncryptedPrivateKeySpy = jest.spyOn(keyService, "setPrivateKey"); - await cryptoService.setUserKeys(mockUserKey, mockEncPrivateKey, mockUserId); + await keyService.setUserKeys(mockUserKey, mockEncPrivateKey, mockUserId); expect(setEncryptedPrivateKeySpy).toHaveBeenCalledWith(mockEncPrivateKey, mockUserId); }); @@ -388,7 +386,7 @@ describe("cryptoService", () => { let callCount = 0; stateProvider.activeUserId$ = stateProvider.activeUserId$.pipe(tap(() => callCount++)); - await cryptoService.clearKeys(null); + await keyService.clearKeys(null); expect(callCount).toBe(1); // revert to the original state @@ -402,7 +400,7 @@ describe("cryptoService", () => { USER_KEY, ])("key removal", (key: UserKeyDefinition) => { it(`clears ${key.key} for active user when unspecified`, async () => { - await cryptoService.clearKeys(null); + await keyService.clearKeys(null); const encryptedOrgKeyState = stateProvider.singleUser.getFake(mockUserId, key); expect(encryptedOrgKeyState.nextMock).toHaveBeenCalledTimes(1); @@ -411,7 +409,7 @@ describe("cryptoService", () => { it(`clears ${key.key} for the specified user when specified`, async () => { const userId = "someOtherUser" as UserId; - await cryptoService.clearKeys(userId); + await keyService.clearKeys(userId); const encryptedOrgKeyState = stateProvider.singleUser.getFake(userId, key); expect(encryptedOrgKeyState.nextMock).toHaveBeenCalledTimes(1); @@ -458,7 +456,7 @@ describe("cryptoService", () => { const fakeUserPublicKey = makeStaticByteArray(10, 2); cryptoFunctionService.rsaExtractPublicKey.mockResolvedValue(fakeUserPublicKey); - const userPrivateKey = await firstValueFrom(cryptoService.userPrivateKey$(mockUserId)); + const userPrivateKey = await firstValueFrom(keyService.userPrivateKey$(mockUserId)); expect(encryptService.decryptToBytes).toHaveBeenCalledWith( fakeEncryptedUserPrivateKey, @@ -471,7 +469,7 @@ describe("cryptoService", () => { it("returns null user private key when no user key is found", async () => { setupKeys({ makeMasterKey: false, makeUserKey: false }); - const userPrivateKey = await firstValueFrom(cryptoService.userPrivateKey$(mockUserId)); + const userPrivateKey = await firstValueFrom(keyService.userPrivateKey$(mockUserId)); expect(encryptService.decryptToBytes).not.toHaveBeenCalled(); @@ -487,7 +485,7 @@ describe("cryptoService", () => { ); encryptedUserPrivateKeyState.nextState(null); - const userPrivateKey = await firstValueFrom(cryptoService.userPrivateKey$(mockUserId)); + const userPrivateKey = await firstValueFrom(keyService.userPrivateKey$(mockUserId)); expect(userPrivateKey).toBeFalsy(); }); }); @@ -568,7 +566,7 @@ describe("cryptoService", () => { encryptedPrivateKey: makeEncString("privateKey"), }); - const decryptionKeys = await firstValueFrom(cryptoService.cipherDecryptionKeys$(mockUserId)); + const decryptionKeys = await firstValueFrom(keyService.cipherDecryptionKeys$(mockUserId)); expect(decryptionKeys).not.toBeNull(); expect(decryptionKeys.userKey).not.toBeNull(); @@ -584,7 +582,7 @@ describe("cryptoService", () => { }, }); - const decryptionKeys = await firstValueFrom(cryptoService.cipherDecryptionKeys$(mockUserId)); + const decryptionKeys = await firstValueFrom(keyService.cipherDecryptionKeys$(mockUserId)); expect(decryptionKeys).not.toBeNull(); expect(decryptionKeys.userKey).not.toBeNull(); @@ -605,7 +603,7 @@ describe("cryptoService", () => { providerKeys: {}, }); - const decryptionKeys = await firstValueFrom(cryptoService.cipherDecryptionKeys$(mockUserId)); + const decryptionKeys = await firstValueFrom(keyService.cipherDecryptionKeys$(mockUserId)); expect(decryptionKeys).not.toBeNull(); expect(decryptionKeys.userKey).not.toBeNull(); @@ -634,7 +632,7 @@ describe("cryptoService", () => { }, }); - const decryptionKeys = await firstValueFrom(cryptoService.cipherDecryptionKeys$(mockUserId)); + const decryptionKeys = await firstValueFrom(keyService.cipherDecryptionKeys$(mockUserId)); expect(decryptionKeys).not.toBeNull(); expect(decryptionKeys.userKey).not.toBeNull(); @@ -653,7 +651,7 @@ describe("cryptoService", () => { it("returns a stream that pays attention to updates of all data", async () => { // Start listening until there have been 6 emissions const promise = lastValueFrom( - cryptoService.cipherDecryptionKeys$(mockUserId).pipe(bufferCount(6), take(1)), + keyService.cipherDecryptionKeys$(mockUserId).pipe(bufferCount(6), take(1)), ); // User has their UserKey set diff --git a/libs/common/src/platform/services/crypto.service.ts b/libs/key-management/src/key.service.ts similarity index 91% rename from libs/common/src/platform/services/crypto.service.ts rename to libs/key-management/src/key.service.ts index a6db9a2c1bf..b12db176cec 100644 --- a/libs/common/src/platform/services/crypto.service.ts +++ b/libs/key-management/src/key.service.ts @@ -10,20 +10,39 @@ import { switchMap, } from "rxjs"; -import { PinServiceAbstraction } from "../../../../auth/src/common/abstractions"; -import { EncryptedOrganizationKeyData } from "../../admin-console/models/data/encrypted-organization-key.data"; -import { BaseEncryptedOrganizationKey } from "../../admin-console/models/domain/encrypted-organization-key"; -import { ProfileOrganizationResponse } from "../../admin-console/models/response/profile-organization.response"; -import { ProfileProviderOrganizationResponse } from "../../admin-console/models/response/profile-provider-organization.response"; -import { ProfileProviderResponse } from "../../admin-console/models/response/profile-provider.response"; -import { AccountService } from "../../auth/abstractions/account.service"; -import { KdfConfigService } from "../../auth/abstractions/kdf-config.service"; -import { InternalMasterPasswordServiceAbstraction } from "../../auth/abstractions/master-password.service.abstraction"; -import { KdfConfig } from "../../auth/models/domain/kdf-config"; -import { Utils } from "../../platform/misc/utils"; -import { VAULT_TIMEOUT } from "../../services/vault-timeout/vault-timeout-settings.state"; -import { CsprngArray } from "../../types/csprng"; -import { OrganizationId, ProviderId, UserId } from "../../types/guid"; +import { PinServiceAbstraction } from "../../auth/src/common/abstractions"; +import { EncryptedOrganizationKeyData } from "../../common/src/admin-console/models/data/encrypted-organization-key.data"; +import { BaseEncryptedOrganizationKey } from "../../common/src/admin-console/models/domain/encrypted-organization-key"; +import { ProfileOrganizationResponse } from "../../common/src/admin-console/models/response/profile-organization.response"; +import { ProfileProviderOrganizationResponse } from "../../common/src/admin-console/models/response/profile-provider-organization.response"; +import { ProfileProviderResponse } from "../../common/src/admin-console/models/response/profile-provider.response"; +import { AccountService } from "../../common/src/auth/abstractions/account.service"; +import { KdfConfigService } from "../../common/src/auth/abstractions/kdf-config.service"; +import { InternalMasterPasswordServiceAbstraction } from "../../common/src/auth/abstractions/master-password.service.abstraction"; +import { KdfConfig } from "../../common/src/auth/models/domain/kdf-config"; +import { CryptoFunctionService } from "../../common/src/platform/abstractions/crypto-function.service"; +import { EncryptService } from "../../common/src/platform/abstractions/encrypt.service"; +import { KeyGenerationService } from "../../common/src/platform/abstractions/key-generation.service"; +import { LogService } from "../../common/src/platform/abstractions/log.service"; +import { PlatformUtilsService } from "../../common/src/platform/abstractions/platform-utils.service"; +import { StateService } from "../../common/src/platform/abstractions/state.service"; +import { KeySuffixOptions, HashPurpose } from "../../common/src/platform/enums"; +import { convertValues } from "../../common/src/platform/misc/convert-values"; +import { Utils } from "../../common/src/platform/misc/utils"; +import { EFFLongWordList } from "../../common/src/platform/misc/wordlist"; +import { EncString, EncryptedString } from "../../common/src/platform/models/domain/enc-string"; +import { SymmetricCryptoKey } from "../../common/src/platform/models/domain/symmetric-crypto-key"; +import { USER_ENCRYPTED_ORGANIZATION_KEYS } from "../../common/src/platform/services/key-state/org-keys.state"; +import { USER_ENCRYPTED_PROVIDER_KEYS } from "../../common/src/platform/services/key-state/provider-keys.state"; +import { + USER_ENCRYPTED_PRIVATE_KEY, + USER_EVER_HAD_USER_KEY, + USER_KEY, +} from "../../common/src/platform/services/key-state/user-key.state"; +import { ActiveUserState, StateProvider } from "../../common/src/platform/state"; +import { VAULT_TIMEOUT } from "../../common/src/services/vault-timeout/vault-timeout-settings.state"; +import { CsprngArray } from "../../common/src/types/csprng"; +import { OrganizationId, ProviderId, UserId } from "../../common/src/types/guid"; import { OrgKey, UserKey, @@ -32,35 +51,16 @@ import { CipherKey, UserPrivateKey, UserPublicKey, -} from "../../types/key"; -import { VaultTimeoutStringType } from "../../types/vault-timeout.type"; -import { CryptoFunctionService } from "../abstractions/crypto-function.service"; +} from "../../common/src/types/key"; +import { VaultTimeoutStringType } from "../../common/src/types/vault-timeout.type"; + import { CipherDecryptionKeys, - CryptoService as CryptoServiceAbstraction, + KeyService as KeyServiceAbstraction, UserPrivateKeyDecryptionFailedError, -} from "../abstractions/crypto.service"; -import { EncryptService } from "../abstractions/encrypt.service"; -import { KeyGenerationService } from "../abstractions/key-generation.service"; -import { LogService } from "../abstractions/log.service"; -import { PlatformUtilsService } from "../abstractions/platform-utils.service"; -import { StateService } from "../abstractions/state.service"; -import { KeySuffixOptions, HashPurpose } from "../enums"; -import { convertValues } from "../misc/convert-values"; -import { EFFLongWordList } from "../misc/wordlist"; -import { EncString, EncryptedString } from "../models/domain/enc-string"; -import { SymmetricCryptoKey } from "../models/domain/symmetric-crypto-key"; -import { ActiveUserState, StateProvider } from "../state"; +} from "./abstractions/key.service"; -import { USER_ENCRYPTED_ORGANIZATION_KEYS } from "./key-state/org-keys.state"; -import { USER_ENCRYPTED_PROVIDER_KEYS } from "./key-state/provider-keys.state"; -import { - USER_ENCRYPTED_PRIVATE_KEY, - USER_EVER_HAD_USER_KEY, - USER_KEY, -} from "./key-state/user-key.state"; - -export class CryptoService implements CryptoServiceAbstraction { +export class DefaultKeyService implements KeyServiceAbstraction { private readonly activeUserEverHadUserKey: ActiveUserState; readonly everHadUserKey$: Observable; diff --git a/libs/tools/export/vault-export/vault-export-core/src/services/individual-vault-export.service.spec.ts b/libs/tools/export/vault-export/vault-export-core/src/services/individual-vault-export.service.spec.ts index 1a66fe92256..d264991ae40 100644 --- a/libs/tools/export/vault-export/vault-export-core/src/services/individual-vault-export.service.spec.ts +++ b/libs/tools/export/vault-export/vault-export-core/src/services/individual-vault-export.service.spec.ts @@ -10,7 +10,6 @@ import { } from "@bitwarden/common/auth/models/domain/kdf-config"; import { CipherWithIdExport } from "@bitwarden/common/models/export/cipher-with-ids.export"; import { CryptoFunctionService } from "@bitwarden/common/platform/abstractions/crypto-function.service"; -import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service"; import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; import { KdfType } from "@bitwarden/common/platform/enums"; import { Utils } from "@bitwarden/common/platform/misc/utils"; @@ -25,6 +24,7 @@ import { Login } from "@bitwarden/common/vault/models/domain/login"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; import { FolderView } from "@bitwarden/common/vault/models/view/folder.view"; import { LoginView } from "@bitwarden/common/vault/models/view/login.view"; +import { KeyService } from "@bitwarden/key-management"; import { BuildTestObject, GetUniqueString } from "../../../../../../common/spec"; @@ -152,7 +152,7 @@ describe("VaultExportService", () => { let cipherService: MockProxy; let pinService: MockProxy; let folderService: MockProxy; - let cryptoService: MockProxy; + let keyService: MockProxy; let encryptService: MockProxy; let kdfConfigService: MockProxy; let accountService: MockProxy; @@ -162,12 +162,12 @@ describe("VaultExportService", () => { cipherService = mock(); pinService = mock(); folderService = mock(); - cryptoService = mock(); + keyService = mock(); encryptService = mock(); kdfConfigService = mock(); accountService = mock(); - cryptoService.userKey$.mockReturnValue(new BehaviorSubject("mockOriginalUserKey" as any)); + keyService.userKey$.mockReturnValue(new BehaviorSubject("mockOriginalUserKey" as any)); const userId = "" as UserId; const accountInfo: AccountInfo = { @@ -187,7 +187,7 @@ describe("VaultExportService", () => { folderService, cipherService, pinService, - cryptoService, + keyService, encryptService, cryptoFunctionService, kdfConfigService, diff --git a/libs/tools/export/vault-export/vault-export-core/src/services/individual-vault-export.service.ts b/libs/tools/export/vault-export/vault-export-core/src/services/individual-vault-export.service.ts index d6d37b28ac7..04dba1299d7 100644 --- a/libs/tools/export/vault-export/vault-export-core/src/services/individual-vault-export.service.ts +++ b/libs/tools/export/vault-export/vault-export-core/src/services/individual-vault-export.service.ts @@ -6,7 +6,6 @@ import { AccountService } from "@bitwarden/common/auth/abstractions/account.serv import { KdfConfigService } from "@bitwarden/common/auth/abstractions/kdf-config.service"; import { CipherWithIdExport, FolderWithIdExport } from "@bitwarden/common/models/export"; import { CryptoFunctionService } from "@bitwarden/common/platform/abstractions/crypto-function.service"; -import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service"; import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; import { Utils } from "@bitwarden/common/platform/misc/utils"; import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; @@ -16,6 +15,7 @@ import { Cipher } from "@bitwarden/common/vault/models/domain/cipher"; import { Folder } from "@bitwarden/common/vault/models/domain/folder"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; import { FolderView } from "@bitwarden/common/vault/models/view/folder.view"; +import { KeyService } from "@bitwarden/key-management"; import { BitwardenCsvIndividualExportType, @@ -35,7 +35,7 @@ export class IndividualVaultExportService private folderService: FolderService, private cipherService: CipherService, pinService: PinServiceAbstraction, - private cryptoService: CryptoService, + private keyService: KeyService, encryptService: EncryptService, cryptoFunctionService: CryptoFunctionService, kdfConfigService: KdfConfigService, @@ -104,7 +104,7 @@ export class IndividualVaultExportService const activeUserId = await firstValueFrom( this.accountService.activeAccount$.pipe(map((a) => a?.id)), ); - const userKey = await this.cryptoService.getUserKeyWithLegacySupport(activeUserId); + const userKey = await this.keyService.getUserKeyWithLegacySupport(activeUserId); const encKeyValidation = await this.encryptService.encrypt(Utils.newGuid(), userKey); const jsonDoc: BitwardenEncryptedIndividualJsonExport = { diff --git a/libs/tools/export/vault-export/vault-export-core/src/services/org-vault-export.service.ts b/libs/tools/export/vault-export/vault-export-core/src/services/org-vault-export.service.ts index 28ffa8e83dc..4e23a0ed25c 100644 --- a/libs/tools/export/vault-export/vault-export-core/src/services/org-vault-export.service.ts +++ b/libs/tools/export/vault-export/vault-export-core/src/services/org-vault-export.service.ts @@ -14,7 +14,6 @@ import { AccountService } from "@bitwarden/common/auth/abstractions/account.serv import { KdfConfigService } from "@bitwarden/common/auth/abstractions/kdf-config.service"; import { CipherWithIdExport, CollectionWithIdExport } from "@bitwarden/common/models/export"; import { CryptoFunctionService } from "@bitwarden/common/platform/abstractions/crypto-function.service"; -import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service"; import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; import { Utils } from "@bitwarden/common/platform/misc/utils"; import { OrganizationId } from "@bitwarden/common/types/guid"; @@ -23,6 +22,7 @@ import { CipherType } from "@bitwarden/common/vault/enums"; import { CipherData } from "@bitwarden/common/vault/models/data/cipher.data"; import { Cipher } from "@bitwarden/common/vault/models/domain/cipher"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; +import { KeyService } from "@bitwarden/key-management"; import { BitwardenCsvOrgExportType, @@ -42,7 +42,7 @@ export class OrganizationVaultExportService private cipherService: CipherService, private apiService: ApiService, pinService: PinServiceAbstraction, - private cryptoService: CryptoService, + private keyService: KeyService, encryptService: EncryptService, cryptoFunctionService: CryptoFunctionService, private collectionService: CollectionService, @@ -105,7 +105,7 @@ export class OrganizationVaultExportService exportData.collections.forEach((c) => { const collection = new Collection(new CollectionData(c as CollectionDetailsResponse)); exportPromises.push( - firstValueFrom(this.cryptoService.activeUserOrgKeys$) + firstValueFrom(this.keyService.activeUserOrgKeys$) .then((keys) => collection.decrypt(keys[organizationId as OrganizationId])) .then((decCol) => { decCollections.push(decCol); @@ -245,7 +245,7 @@ export class OrganizationVaultExportService collections: Collection[], ciphers: Cipher[], ): Promise { - const orgKey = await this.cryptoService.getOrgKey(organizationId); + const orgKey = await this.keyService.getOrgKey(organizationId); const encKeyValidation = await this.encryptService.encrypt(Utils.newGuid(), orgKey); const jsonDoc: BitwardenEncryptedOrgJsonExport = { diff --git a/libs/tools/export/vault-export/vault-export-core/src/services/vault-export.service.spec.ts b/libs/tools/export/vault-export/vault-export-core/src/services/vault-export.service.spec.ts index 7e93c78fc51..525e769957f 100644 --- a/libs/tools/export/vault-export/vault-export-core/src/services/vault-export.service.spec.ts +++ b/libs/tools/export/vault-export/vault-export-core/src/services/vault-export.service.spec.ts @@ -10,7 +10,6 @@ import { } from "@bitwarden/common/auth/models/domain/kdf-config"; import { CipherWithIdExport } from "@bitwarden/common/models/export/cipher-with-ids.export"; import { CryptoFunctionService } from "@bitwarden/common/platform/abstractions/crypto-function.service"; -import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service"; import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; import { KdfType } from "@bitwarden/common/platform/enums"; import { Utils } from "@bitwarden/common/platform/misc/utils"; @@ -25,6 +24,7 @@ import { Login } from "@bitwarden/common/vault/models/domain/login"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; import { FolderView } from "@bitwarden/common/vault/models/view/folder.view"; import { LoginView } from "@bitwarden/common/vault/models/view/login.view"; +import { KeyService } from "@bitwarden/key-management"; import { BuildTestObject, GetUniqueString } from "../../../../../../common/spec"; @@ -152,7 +152,7 @@ describe("VaultExportService", () => { let cipherService: MockProxy; let pinService: MockProxy; let folderService: MockProxy; - let cryptoService: MockProxy; + let keyService: MockProxy; let encryptService: MockProxy; let accountService: MockProxy; let kdfConfigService: MockProxy; @@ -162,7 +162,7 @@ describe("VaultExportService", () => { cipherService = mock(); pinService = mock(); folderService = mock(); - cryptoService = mock(); + keyService = mock(); encryptService = mock(); accountService = mock(); @@ -172,7 +172,7 @@ describe("VaultExportService", () => { folderService.getAllFromState.mockResolvedValue(UserFolders); kdfConfigService.getKdfConfig.mockResolvedValue(DEFAULT_KDF_CONFIG); encryptService.encrypt.mockResolvedValue(new EncString("encrypted")); - cryptoService.userKey$.mockReturnValue(new BehaviorSubject("mockOriginalUserKey" as any)); + keyService.userKey$.mockReturnValue(new BehaviorSubject("mockOriginalUserKey" as any)); const userId = "" as UserId; const accountInfo: AccountInfo = { email: "", @@ -186,7 +186,7 @@ describe("VaultExportService", () => { folderService, cipherService, pinService, - cryptoService, + keyService, encryptService, cryptoFunctionService, kdfConfigService, diff --git a/libs/tools/generator/components/src/generator.module.ts b/libs/tools/generator/components/src/generator.module.ts index 58117bec495..2d1cedca400 100644 --- a/libs/tools/generator/components/src/generator.module.ts +++ b/libs/tools/generator/components/src/generator.module.ts @@ -7,7 +7,6 @@ import { safeProvider } from "@bitwarden/angular/platform/utils/safe-provider"; import { SafeInjectionToken } from "@bitwarden/angular/services/injection-tokens"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; -import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service"; import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { StateProvider } from "@bitwarden/common/platform/state"; @@ -30,6 +29,7 @@ import { CredentialGeneratorService, Randomizer, } from "@bitwarden/generator-core"; +import { KeyService } from "@bitwarden/key-management"; import { CatchallSettingsComponent } from "./catchall-settings.component"; import { CredentialGeneratorComponent } from "./credential-generator.component"; @@ -66,7 +66,7 @@ const RANDOMIZER = new SafeInjectionToken("Randomizer"); safeProvider({ provide: RANDOMIZER, useFactory: createRandomizer, - deps: [CryptoService], + deps: [KeyService], }), safeProvider({ provide: CredentialGeneratorService, @@ -78,7 +78,7 @@ const RANDOMIZER = new SafeInjectionToken("Randomizer"); ApiService, I18nService, EncryptService, - CryptoService, + KeyService, ], }), ], diff --git a/libs/tools/generator/core/src/data/default-passphrase-boundaries.ts b/libs/tools/generator/core/src/data/default-passphrase-boundaries.ts index d4aca717093..875a15051a2 100644 --- a/libs/tools/generator/core/src/data/default-passphrase-boundaries.ts +++ b/libs/tools/generator/core/src/data/default-passphrase-boundaries.ts @@ -1,6 +1,6 @@ function initializeBoundaries() { const numWords = Object.freeze({ - min: 3, + min: 6, max: 20, }); diff --git a/libs/tools/generator/core/src/data/default-passphrase-generation-options.ts b/libs/tools/generator/core/src/data/default-passphrase-generation-options.ts index 59fb6069003..b66cc74e267 100644 --- a/libs/tools/generator/core/src/data/default-passphrase-generation-options.ts +++ b/libs/tools/generator/core/src/data/default-passphrase-generation-options.ts @@ -3,7 +3,7 @@ import { PassphraseGenerationOptions } from "../types"; /** The default options for passphrase generation. */ export const DefaultPassphraseGenerationOptions: Partial = Object.freeze({ - numWords: 3, + numWords: 6, wordSeparator: "-", capitalize: false, includeNumber: false, diff --git a/libs/tools/generator/core/src/engine/index.ts b/libs/tools/generator/core/src/engine/index.ts index c3d2aefef1b..2d272e7c11b 100644 --- a/libs/tools/generator/core/src/engine/index.ts +++ b/libs/tools/generator/core/src/engine/index.ts @@ -1,4 +1,4 @@ -export { CryptoServiceRandomizer } from "./crypto-service-randomizer"; +export { KeyServiceRandomizer } from "./key-service-randomizer"; export { ForwarderConfiguration, AccountRequest } from "./forwarder-configuration"; export { ForwarderContext } from "./forwarder-context"; export * from "./settings"; diff --git a/libs/tools/generator/core/src/engine/crypto-service-randomizer.spec.ts b/libs/tools/generator/core/src/engine/key-service-randomizer.spec.ts similarity index 58% rename from libs/tools/generator/core/src/engine/crypto-service-randomizer.spec.ts rename to libs/tools/generator/core/src/engine/key-service-randomizer.spec.ts index c3908bc86a8..459a05618f9 100644 --- a/libs/tools/generator/core/src/engine/crypto-service-randomizer.spec.ts +++ b/libs/tools/generator/core/src/engine/key-service-randomizer.spec.ts @@ -1,11 +1,11 @@ import { mock } from "jest-mock-extended"; -import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service"; +import { KeyService } from "@bitwarden/key-management"; -import { CryptoServiceRandomizer } from "./crypto-service-randomizer"; +import { KeyServiceRandomizer } from "./key-service-randomizer"; -describe("CryptoServiceRandomizer", () => { - const cryptoService = mock(); +describe("KeyServiceRandomizer", () => { + const keyService = mock(); afterEach(() => { jest.resetAllMocks(); @@ -13,7 +13,7 @@ describe("CryptoServiceRandomizer", () => { describe("pick", () => { it.each([[null], [undefined], [[]]])("throws when the list is %p", async (list) => { - const randomizer = new CryptoServiceRandomizer(cryptoService); + const randomizer = new KeyServiceRandomizer(keyService); await expect(() => randomizer.pick(list)).rejects.toBeInstanceOf(Error); @@ -21,8 +21,8 @@ describe("CryptoServiceRandomizer", () => { }); it("picks an item from the list", async () => { - const randomizer = new CryptoServiceRandomizer(cryptoService); - cryptoService.randomNumber.mockResolvedValue(1); + const randomizer = new KeyServiceRandomizer(keyService); + keyService.randomNumber.mockResolvedValue(1); const result = await randomizer.pick([0, 1]); @@ -32,7 +32,7 @@ describe("CryptoServiceRandomizer", () => { describe("pickWord", () => { it.each([[null], [undefined], [[]]])("throws when the list is %p", async (list) => { - const randomizer = new CryptoServiceRandomizer(cryptoService); + const randomizer = new KeyServiceRandomizer(keyService); await expect(() => randomizer.pickWord(list)).rejects.toBeInstanceOf(Error); @@ -40,8 +40,8 @@ describe("CryptoServiceRandomizer", () => { }); it("picks a word from the list", async () => { - const randomizer = new CryptoServiceRandomizer(cryptoService); - cryptoService.randomNumber.mockResolvedValue(1); + const randomizer = new KeyServiceRandomizer(keyService); + keyService.randomNumber.mockResolvedValue(1); const result = await randomizer.pickWord(["foo", "bar"]); @@ -49,8 +49,8 @@ describe("CryptoServiceRandomizer", () => { }); it("capitalizes the word when options.titleCase is true", async () => { - const randomizer = new CryptoServiceRandomizer(cryptoService); - cryptoService.randomNumber.mockResolvedValue(1); + const randomizer = new KeyServiceRandomizer(keyService); + keyService.randomNumber.mockResolvedValue(1); const result = await randomizer.pickWord(["foo", "bar"], { titleCase: true }); @@ -58,9 +58,9 @@ describe("CryptoServiceRandomizer", () => { }); it("appends a random number when options.number is true", async () => { - const randomizer = new CryptoServiceRandomizer(cryptoService); - cryptoService.randomNumber.mockResolvedValueOnce(1); - cryptoService.randomNumber.mockResolvedValueOnce(2); + const randomizer = new KeyServiceRandomizer(keyService); + keyService.randomNumber.mockResolvedValueOnce(1); + keyService.randomNumber.mockResolvedValueOnce(2); const result = await randomizer.pickWord(["foo", "bar"], { number: true }); @@ -70,7 +70,7 @@ describe("CryptoServiceRandomizer", () => { describe("shuffle", () => { it.each([[null], [undefined], [[]]])("throws when the list is %p", async (list) => { - const randomizer = new CryptoServiceRandomizer(cryptoService); + const randomizer = new KeyServiceRandomizer(keyService); await expect(() => randomizer.shuffle(list)).rejects.toBeInstanceOf(Error); @@ -78,18 +78,18 @@ describe("CryptoServiceRandomizer", () => { }); it("returns a copy of the list without shuffling it when theres only one entry", async () => { - const randomizer = new CryptoServiceRandomizer(cryptoService); + const randomizer = new KeyServiceRandomizer(keyService); const result = await randomizer.shuffle(["foo"]); expect(result).toEqual(["foo"]); expect(result).not.toBe(["foo"]); - expect(cryptoService.randomNumber).not.toHaveBeenCalled(); + expect(keyService.randomNumber).not.toHaveBeenCalled(); }); it("shuffles the tail of the list", async () => { - const randomizer = new CryptoServiceRandomizer(cryptoService); - cryptoService.randomNumber.mockResolvedValueOnce(0); + const randomizer = new KeyServiceRandomizer(keyService); + keyService.randomNumber.mockResolvedValueOnce(0); const result = await randomizer.shuffle(["bar", "foo"]); @@ -97,9 +97,9 @@ describe("CryptoServiceRandomizer", () => { }); it("shuffles the list", async () => { - const randomizer = new CryptoServiceRandomizer(cryptoService); - cryptoService.randomNumber.mockResolvedValueOnce(0); - cryptoService.randomNumber.mockResolvedValueOnce(1); + const randomizer = new KeyServiceRandomizer(keyService); + keyService.randomNumber.mockResolvedValueOnce(0); + keyService.randomNumber.mockResolvedValueOnce(1); const result = await randomizer.shuffle(["baz", "bar", "foo"]); @@ -107,8 +107,8 @@ describe("CryptoServiceRandomizer", () => { }); it("returns the input list when options.copy is false", async () => { - const randomizer = new CryptoServiceRandomizer(cryptoService); - cryptoService.randomNumber.mockResolvedValueOnce(0); + const randomizer = new KeyServiceRandomizer(keyService); + keyService.randomNumber.mockResolvedValueOnce(0); const expectedResult = ["foo"]; const result = await randomizer.shuffle(expectedResult, { copy: false }); @@ -119,7 +119,7 @@ describe("CryptoServiceRandomizer", () => { describe("chars", () => { it("returns an empty string when the length is 0", async () => { - const randomizer = new CryptoServiceRandomizer(cryptoService); + const randomizer = new KeyServiceRandomizer(keyService); const result = await randomizer.chars(0); @@ -127,8 +127,8 @@ describe("CryptoServiceRandomizer", () => { }); it("returns an arbitrary lowercase ascii character", async () => { - const randomizer = new CryptoServiceRandomizer(cryptoService); - cryptoService.randomNumber.mockResolvedValueOnce(0); + const randomizer = new KeyServiceRandomizer(keyService); + keyService.randomNumber.mockResolvedValueOnce(0); const result = await randomizer.chars(1); @@ -136,38 +136,38 @@ describe("CryptoServiceRandomizer", () => { }); it("returns a number of ascii characters based on the length", async () => { - const randomizer = new CryptoServiceRandomizer(cryptoService); - cryptoService.randomNumber.mockResolvedValue(0); + const randomizer = new KeyServiceRandomizer(keyService); + keyService.randomNumber.mockResolvedValue(0); const result = await randomizer.chars(2); expect(result).toEqual("aa"); - expect(cryptoService.randomNumber).toHaveBeenCalledTimes(2); + expect(keyService.randomNumber).toHaveBeenCalledTimes(2); }); it("returns a new random character each time its called", async () => { - const randomizer = new CryptoServiceRandomizer(cryptoService); - cryptoService.randomNumber.mockResolvedValueOnce(0); - cryptoService.randomNumber.mockResolvedValueOnce(1); + const randomizer = new KeyServiceRandomizer(keyService); + keyService.randomNumber.mockResolvedValueOnce(0); + keyService.randomNumber.mockResolvedValueOnce(1); const resultA = await randomizer.chars(1); const resultB = await randomizer.chars(1); expect(resultA).toEqual("a"); expect(resultB).toEqual("b"); - expect(cryptoService.randomNumber).toHaveBeenCalledTimes(2); + expect(keyService.randomNumber).toHaveBeenCalledTimes(2); }); }); describe("uniform", () => { it("forwards requests to the crypto service", async () => { - const randomizer = new CryptoServiceRandomizer(cryptoService); - cryptoService.randomNumber.mockResolvedValue(5); + const randomizer = new KeyServiceRandomizer(keyService); + keyService.randomNumber.mockResolvedValue(5); const result = await randomizer.uniform(0, 5); expect(result).toBe(5); - expect(cryptoService.randomNumber).toHaveBeenCalledWith(0, 5); + expect(keyService.randomNumber).toHaveBeenCalledWith(0, 5); }); }); }); diff --git a/libs/tools/generator/core/src/engine/crypto-service-randomizer.ts b/libs/tools/generator/core/src/engine/key-service-randomizer.ts similarity index 79% rename from libs/tools/generator/core/src/engine/crypto-service-randomizer.ts rename to libs/tools/generator/core/src/engine/key-service-randomizer.ts index cc7ceef4bbe..5fc719042b7 100644 --- a/libs/tools/generator/core/src/engine/crypto-service-randomizer.ts +++ b/libs/tools/generator/core/src/engine/key-service-randomizer.ts @@ -1,14 +1,14 @@ -import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service"; +import { KeyService } from "@bitwarden/key-management"; import { Randomizer } from "../abstractions"; import { WordOptions } from "../types"; -/** A randomizer backed by a CryptoService. */ -export class CryptoServiceRandomizer implements Randomizer { +/** A randomizer backed by a KeyService. */ +export class KeyServiceRandomizer implements Randomizer { /** instantiates the type. - * @param crypto generates random numbers + * @param keyService generates random numbers */ - constructor(private crypto: CryptoService) {} + constructor(private keyService: KeyService) {} async pick(list: Array): Promise { const length = list?.length ?? 0; @@ -28,7 +28,7 @@ export class CryptoServiceRandomizer implements Randomizer { } if (options?.number ?? false) { - const num = await this.crypto.randomNumber(1, 9); + const num = await this.keyService.randomNumber(1, 9); word = word + num.toString(); } @@ -63,6 +63,6 @@ export class CryptoServiceRandomizer implements Randomizer { } async uniform(min: number, max: number) { - return this.crypto.randomNumber(min, max); + return this.keyService.randomNumber(min, max); } } diff --git a/libs/tools/generator/core/src/factories.ts b/libs/tools/generator/core/src/factories.ts index 6c09b8d315a..479545c78fe 100644 --- a/libs/tools/generator/core/src/factories.ts +++ b/libs/tools/generator/core/src/factories.ts @@ -1,11 +1,11 @@ // contains logic that constructs generator services dynamically given // a generator id. -import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service"; +import { KeyService } from "@bitwarden/key-management"; import { Randomizer } from "./abstractions"; -import { CryptoServiceRandomizer } from "./engine/crypto-service-randomizer"; +import { KeyServiceRandomizer } from "./engine/key-service-randomizer"; -export function createRandomizer(cryptoService: CryptoService): Randomizer { - return new CryptoServiceRandomizer(cryptoService); +export function createRandomizer(keyService: KeyService): Randomizer { + return new KeyServiceRandomizer(keyService); } diff --git a/libs/tools/generator/core/src/policies/passphrase-generator-options-evaluator.spec.ts b/libs/tools/generator/core/src/policies/passphrase-generator-options-evaluator.spec.ts index 688315e9297..3b1eb799391 100644 --- a/libs/tools/generator/core/src/policies/passphrase-generator-options-evaluator.spec.ts +++ b/libs/tools/generator/core/src/policies/passphrase-generator-options-evaluator.spec.ts @@ -164,7 +164,7 @@ describe("Password generator options builder", () => { }, ); - it.each([3, 8, 18, 20])( + it.each([6, 8, 18, 20])( "should set `numWords` (= %i) to the input value when it is within the boundaries", (numWords) => { expect(numWords).toBeGreaterThanOrEqual(DefaultPassphraseBoundaries.numWords.min); diff --git a/libs/tools/generator/core/src/policies/passphrase-policy-constraints.spec.ts b/libs/tools/generator/core/src/policies/passphrase-policy-constraints.spec.ts index 034a8234223..7565be7936f 100644 --- a/libs/tools/generator/core/src/policies/passphrase-policy-constraints.spec.ts +++ b/libs/tools/generator/core/src/policies/passphrase-policy-constraints.spec.ts @@ -72,9 +72,9 @@ describe("PassphrasePolicyConstraints", () => { }); it.each([ - [1, 3], + [1, 6], [21, 20], - ])("fits numWords (=%p) within the default bounds (3 <= %p <= 20)", (value, expected) => { + ])("fits numWords (=%p) within the default bounds (6 <= %p <= 20)", (value, expected) => { const policy = new PassphrasePolicyConstraints(Policies.Passphrase.disabledValue); const { numWords } = policy.adjust({ ...SomeSettings, numWords: value }); @@ -83,7 +83,7 @@ describe("PassphrasePolicyConstraints", () => { }); it.each([ - [1, 4, 4], + [1, 6, 6], [21, 20, 20], ])( "fits numWords (=%p) within the policy bounds (%p <= %p <= 20)", diff --git a/libs/tools/generator/core/src/services/credential-generator.service.spec.ts b/libs/tools/generator/core/src/services/credential-generator.service.spec.ts index e11e555d6aa..edb817361d0 100644 --- a/libs/tools/generator/core/src/services/credential-generator.service.spec.ts +++ b/libs/tools/generator/core/src/services/credential-generator.service.spec.ts @@ -5,13 +5,13 @@ import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; import { PolicyType } from "@bitwarden/common/admin-console/enums"; import { Policy } from "@bitwarden/common/admin-console/models/domain/policy"; -import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service"; import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { GENERATOR_DISK, UserKeyDefinition } from "@bitwarden/common/platform/state"; import { StateConstraints } from "@bitwarden/common/tools/types"; import { OrganizationId, PolicyId, UserId } from "@bitwarden/common/types/guid"; import { UserKey } from "@bitwarden/common/types/key"; +import { KeyService } from "@bitwarden/key-management"; import { FakeStateProvider, @@ -175,7 +175,7 @@ const apiService = mock(); const encryptService = mock(); -const cryptoService = mock(); +const keyService = mock(); describe("CredentialGeneratorService", () => { beforeEach(async () => { @@ -184,7 +184,7 @@ describe("CredentialGeneratorService", () => { i18nService.t.mockImplementation((key) => key); apiService.fetch.mockImplementation(() => Promise.resolve(mock())); const keyAvailable = new BehaviorSubject({} as UserKey); - cryptoService.userKey$.mockReturnValue(keyAvailable); + keyService.userKey$.mockReturnValue(keyAvailable); jest.clearAllMocks(); }); @@ -199,7 +199,7 @@ describe("CredentialGeneratorService", () => { apiService, i18nService, encryptService, - cryptoService, + keyService, ); const generated = new ObservableTracker(generator.generate$(SomeConfiguration)); @@ -220,7 +220,7 @@ describe("CredentialGeneratorService", () => { apiService, i18nService, encryptService, - cryptoService, + keyService, ); const generated = new ObservableTracker(generator.generate$(SomeConfiguration)); @@ -245,7 +245,7 @@ describe("CredentialGeneratorService", () => { apiService, i18nService, encryptService, - cryptoService, + keyService, ); const generated = new ObservableTracker(generator.generate$(SomeConfiguration)); @@ -273,7 +273,7 @@ describe("CredentialGeneratorService", () => { apiService, i18nService, encryptService, - cryptoService, + keyService, ); const website$ = new BehaviorSubject("some website"); const generated = new ObservableTracker(generator.generate$(SomeConfiguration, { website$ })); @@ -294,7 +294,7 @@ describe("CredentialGeneratorService", () => { apiService, i18nService, encryptService, - cryptoService, + keyService, ); const website$ = new BehaviorSubject("some website"); let error = null; @@ -319,7 +319,7 @@ describe("CredentialGeneratorService", () => { apiService, i18nService, encryptService, - cryptoService, + keyService, ); const website$ = new BehaviorSubject("some website"); let completed = false; @@ -345,7 +345,7 @@ describe("CredentialGeneratorService", () => { apiService, i18nService, encryptService, - cryptoService, + keyService, ); const userId$ = new BehaviorSubject(AnotherUser).asObservable(); const generated = new ObservableTracker(generator.generate$(SomeConfiguration, { userId$ })); @@ -365,7 +365,7 @@ describe("CredentialGeneratorService", () => { apiService, i18nService, encryptService, - cryptoService, + keyService, ); const userId = new BehaviorSubject(SomeUser); const userId$ = userId.pipe(filter((u) => !!u)); @@ -389,7 +389,7 @@ describe("CredentialGeneratorService", () => { apiService, i18nService, encryptService, - cryptoService, + keyService, ); const userId$ = new BehaviorSubject(SomeUser); let error = null; @@ -414,7 +414,7 @@ describe("CredentialGeneratorService", () => { apiService, i18nService, encryptService, - cryptoService, + keyService, ); const userId$ = new BehaviorSubject(SomeUser); let completed = false; @@ -440,7 +440,7 @@ describe("CredentialGeneratorService", () => { apiService, i18nService, encryptService, - cryptoService, + keyService, ); const on$ = new Subject(); const results: any[] = []; @@ -482,7 +482,7 @@ describe("CredentialGeneratorService", () => { apiService, i18nService, encryptService, - cryptoService, + keyService, ); const on$ = new Subject(); let error: any = null; @@ -508,7 +508,7 @@ describe("CredentialGeneratorService", () => { apiService, i18nService, encryptService, - cryptoService, + keyService, ); const on$ = new Subject(); let complete = false; @@ -539,7 +539,7 @@ describe("CredentialGeneratorService", () => { apiService, i18nService, encryptService, - cryptoService, + keyService, ); const result = generator.algorithms("password"); @@ -560,7 +560,7 @@ describe("CredentialGeneratorService", () => { apiService, i18nService, encryptService, - cryptoService, + keyService, ); const result = generator.algorithms("username"); @@ -580,7 +580,7 @@ describe("CredentialGeneratorService", () => { apiService, i18nService, encryptService, - cryptoService, + keyService, ); const result = generator.algorithms("email"); @@ -601,7 +601,7 @@ describe("CredentialGeneratorService", () => { apiService, i18nService, encryptService, - cryptoService, + keyService, ); const result = generator.algorithms(["username", "email"]); @@ -626,7 +626,7 @@ describe("CredentialGeneratorService", () => { apiService, i18nService, encryptService, - cryptoService, + keyService, ); const result = await firstValueFrom(generator.algorithms$("password")); @@ -643,7 +643,7 @@ describe("CredentialGeneratorService", () => { apiService, i18nService, encryptService, - cryptoService, + keyService, ); const result = await firstValueFrom(generator.algorithms$("username")); @@ -659,7 +659,7 @@ describe("CredentialGeneratorService", () => { apiService, i18nService, encryptService, - cryptoService, + keyService, ); const result = await firstValueFrom(generator.algorithms$("email")); @@ -676,7 +676,7 @@ describe("CredentialGeneratorService", () => { apiService, i18nService, encryptService, - cryptoService, + keyService, ); const result = await firstValueFrom(generator.algorithms$(["username", "email"])); @@ -698,7 +698,7 @@ describe("CredentialGeneratorService", () => { apiService, i18nService, encryptService, - cryptoService, + keyService, ); const result = await firstValueFrom(generator.algorithms$(["password"])); @@ -723,7 +723,7 @@ describe("CredentialGeneratorService", () => { apiService, i18nService, encryptService, - cryptoService, + keyService, ); const results: any = []; const sub = generator.algorithms$("password").subscribe((r) => results.push(r)); @@ -760,7 +760,7 @@ describe("CredentialGeneratorService", () => { apiService, i18nService, encryptService, - cryptoService, + keyService, ); const userId$ = new BehaviorSubject(AnotherUser).asObservable(); @@ -781,7 +781,7 @@ describe("CredentialGeneratorService", () => { apiService, i18nService, encryptService, - cryptoService, + keyService, ); const userId = new BehaviorSubject(SomeUser); const userId$ = userId.asObservable(); @@ -811,7 +811,7 @@ describe("CredentialGeneratorService", () => { apiService, i18nService, encryptService, - cryptoService, + keyService, ); const userId = new BehaviorSubject(SomeUser); const userId$ = userId.asObservable(); @@ -837,7 +837,7 @@ describe("CredentialGeneratorService", () => { apiService, i18nService, encryptService, - cryptoService, + keyService, ); const userId = new BehaviorSubject(SomeUser); const userId$ = userId.asObservable(); @@ -863,7 +863,7 @@ describe("CredentialGeneratorService", () => { apiService, i18nService, encryptService, - cryptoService, + keyService, ); const userId = new BehaviorSubject(SomeUser); const userId$ = userId.asObservable(); @@ -895,7 +895,7 @@ describe("CredentialGeneratorService", () => { apiService, i18nService, encryptService, - cryptoService, + keyService, ); const result = await firstValueFrom(generator.settings$(SomeConfiguration)); @@ -913,7 +913,7 @@ describe("CredentialGeneratorService", () => { apiService, i18nService, encryptService, - cryptoService, + keyService, ); const result = await firstValueFrom(generator.settings$(SomeConfiguration)); @@ -933,7 +933,7 @@ describe("CredentialGeneratorService", () => { apiService, i18nService, encryptService, - cryptoService, + keyService, ); const result = await firstValueFrom(generator.settings$(SomeConfiguration)); @@ -958,7 +958,7 @@ describe("CredentialGeneratorService", () => { apiService, i18nService, encryptService, - cryptoService, + keyService, ); const results: any = []; const sub = generator.settings$(SomeConfiguration).subscribe((r) => results.push(r)); @@ -983,7 +983,7 @@ describe("CredentialGeneratorService", () => { apiService, i18nService, encryptService, - cryptoService, + keyService, ); const userId$ = new BehaviorSubject(AnotherUser).asObservable(); @@ -1004,7 +1004,7 @@ describe("CredentialGeneratorService", () => { apiService, i18nService, encryptService, - cryptoService, + keyService, ); const userId = new BehaviorSubject(SomeUser); const userId$ = userId.asObservable(); @@ -1031,7 +1031,7 @@ describe("CredentialGeneratorService", () => { apiService, i18nService, encryptService, - cryptoService, + keyService, ); const userId = new BehaviorSubject(SomeUser); const userId$ = userId.asObservable(); @@ -1057,7 +1057,7 @@ describe("CredentialGeneratorService", () => { apiService, i18nService, encryptService, - cryptoService, + keyService, ); const userId = new BehaviorSubject(SomeUser); const userId$ = userId.asObservable(); @@ -1083,7 +1083,7 @@ describe("CredentialGeneratorService", () => { apiService, i18nService, encryptService, - cryptoService, + keyService, ); const userId = new BehaviorSubject(SomeUser); const userId$ = userId.asObservable(); @@ -1115,7 +1115,7 @@ describe("CredentialGeneratorService", () => { apiService, i18nService, encryptService, - cryptoService, + keyService, ); const subject = await generator.settings(SomeConfiguration, { singleUserId$ }); @@ -1136,7 +1136,7 @@ describe("CredentialGeneratorService", () => { apiService, i18nService, encryptService, - cryptoService, + keyService, ); let completed = false; @@ -1162,7 +1162,7 @@ describe("CredentialGeneratorService", () => { apiService, i18nService, encryptService, - cryptoService, + keyService, ); const userId$ = new BehaviorSubject(SomeUser).asObservable(); @@ -1179,7 +1179,7 @@ describe("CredentialGeneratorService", () => { apiService, i18nService, encryptService, - cryptoService, + keyService, ); const userId$ = new BehaviorSubject(SomeUser).asObservable(); const policy$ = new BehaviorSubject([somePolicy]); @@ -1198,7 +1198,7 @@ describe("CredentialGeneratorService", () => { apiService, i18nService, encryptService, - cryptoService, + keyService, ); const userId = new BehaviorSubject(SomeUser); const userId$ = userId.asObservable(); @@ -1227,7 +1227,7 @@ describe("CredentialGeneratorService", () => { apiService, i18nService, encryptService, - cryptoService, + keyService, ); const userId = new BehaviorSubject(SomeUser); const userId$ = userId.asObservable(); @@ -1257,7 +1257,7 @@ describe("CredentialGeneratorService", () => { apiService, i18nService, encryptService, - cryptoService, + keyService, ); const userId = new BehaviorSubject(SomeUser); const userId$ = userId.asObservable(); @@ -1283,7 +1283,7 @@ describe("CredentialGeneratorService", () => { apiService, i18nService, encryptService, - cryptoService, + keyService, ); const userId = new BehaviorSubject(SomeUser); const userId$ = userId.asObservable(); diff --git a/libs/tools/generator/core/src/services/credential-generator.service.ts b/libs/tools/generator/core/src/services/credential-generator.service.ts index a137c153a64..ebcc2077150 100644 --- a/libs/tools/generator/core/src/services/credential-generator.service.ts +++ b/libs/tools/generator/core/src/services/credential-generator.service.ts @@ -23,7 +23,6 @@ import { Simplify } from "type-fest"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; import { PolicyType } from "@bitwarden/common/admin-console/enums"; -import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service"; import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { StateProvider } from "@bitwarden/common/platform/state"; @@ -41,6 +40,7 @@ import { UserEncryptor } from "@bitwarden/common/tools/state/user-encryptor.abst import { UserKeyEncryptor } from "@bitwarden/common/tools/state/user-key-encryptor"; import { UserStateSubject } from "@bitwarden/common/tools/state/user-state-subject"; import { UserId } from "@bitwarden/common/types/guid"; +import { KeyService } from "@bitwarden/key-management"; import { Randomizer } from "../abstractions"; import { @@ -97,7 +97,7 @@ export class CredentialGeneratorService { private readonly apiService: ApiService, private readonly i18nService: I18nService, private readonly encryptService: EncryptService, - private readonly cryptoService: CryptoService, + private readonly keyService: KeyService, ) {} private getDependencyProvider(): GeneratorDependencyProvider { @@ -272,7 +272,7 @@ export class CredentialGeneratorService { private encryptor$(userId: UserId) { const packer = new PaddedDataPacker(OPTIONS_FRAME_SIZE); - const encryptor$ = this.cryptoService.userKey$(userId).pipe( + const encryptor$ = this.keyService.userKey$(userId).pipe( // complete when the account locks takeWhile((key) => !!key), map((key) => { diff --git a/libs/tools/generator/core/src/strategies/forwarder-generator-strategy.spec.ts b/libs/tools/generator/core/src/strategies/forwarder-generator-strategy.spec.ts index 09f3ccd87a5..f57a1e5f2b6 100644 --- a/libs/tools/generator/core/src/strategies/forwarder-generator-strategy.spec.ts +++ b/libs/tools/generator/core/src/strategies/forwarder-generator-strategy.spec.ts @@ -5,13 +5,13 @@ import { PolicyType } from "@bitwarden/common/admin-console/enums"; // FIXME: use index.ts imports once policy abstractions and models // implement ADR-0002 import { Policy } from "@bitwarden/common/admin-console/models/domain/policy"; -import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service"; import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { RestClient } from "@bitwarden/common/tools/integration/rpc"; import { BufferedState } from "@bitwarden/common/tools/state/buffered-state"; import { UserId } from "@bitwarden/common/types/guid"; import { UserKey } from "@bitwarden/common/types/key"; +import { KeyService } from "@bitwarden/key-management"; import { FakeStateProvider, mockAccountServiceWith } from "../../../../../common/spec"; import { AddyIo, Fastmail, FirefoxRelay } from "../integration"; @@ -30,7 +30,7 @@ const SomePolicy = mock({ describe("ForwarderGeneratorStrategy", () => { const encryptService = mock(); - const keyService = mock(); + const keyService = mock(); const stateProvider = new FakeStateProvider(mockAccountServiceWith(SomeUser)); const restClient = mock(); const i18nService = mock(); diff --git a/libs/tools/generator/core/src/strategies/forwarder-generator-strategy.ts b/libs/tools/generator/core/src/strategies/forwarder-generator-strategy.ts index 04989cce196..9163d4c3a28 100644 --- a/libs/tools/generator/core/src/strategies/forwarder-generator-strategy.ts +++ b/libs/tools/generator/core/src/strategies/forwarder-generator-strategy.ts @@ -2,7 +2,6 @@ import { filter, map } from "rxjs"; import { Jsonify } from "type-fest"; import { PolicyType } from "@bitwarden/common/admin-console/enums"; -import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service"; import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { SingleUserState, StateProvider } from "@bitwarden/common/platform/state"; @@ -17,6 +16,7 @@ import { SecretKeyDefinition } from "@bitwarden/common/tools/state/secret-key-de import { SecretState } from "@bitwarden/common/tools/state/secret-state"; import { UserKeyEncryptor } from "@bitwarden/common/tools/state/user-key-encryptor"; import { UserId } from "@bitwarden/common/types/guid"; +import { KeyService } from "@bitwarden/key-management"; import { GeneratorStrategy } from "../abstractions"; import { ForwarderConfiguration, AccountRequest, ForwarderContext } from "../engine"; @@ -45,7 +45,7 @@ export class ForwarderGeneratorStrategy< private client: RestClient, private i18nService: I18nService, private readonly encryptService: EncryptService, - private readonly keyService: CryptoService, + private readonly keyService: KeyService, private stateProvider: StateProvider, ) { super(); diff --git a/libs/tools/generator/core/src/strategies/passphrase-generator-strategy.spec.ts b/libs/tools/generator/core/src/strategies/passphrase-generator-strategy.spec.ts index 6591b179fc2..5abcea82493 100644 --- a/libs/tools/generator/core/src/strategies/passphrase-generator-strategy.spec.ts +++ b/libs/tools/generator/core/src/strategies/passphrase-generator-strategy.spec.ts @@ -98,7 +98,7 @@ describe("Passphrase generation strategy", () => { const strategy = new PassphraseGeneratorStrategy(randomizer, null); const result = await strategy.generate({ - numWords: 4, + numWords: 6, capitalize: true, includeNumber: true, wordSeparator: "!", @@ -106,7 +106,7 @@ describe("Passphrase generation strategy", () => { expect(result).toEqual("passphrase"); expect(randomizer.randomEffLongWords).toHaveBeenCalledWith({ - numberOfWords: 4, + numberOfWords: 6, capitalize: true, number: true, separator: "!", @@ -135,14 +135,14 @@ describe("Passphrase generation strategy", () => { const strategy = new PassphraseGeneratorStrategy(randomizer, null); const result = await strategy.generate({ - numWords: 4, + numWords: 6, includeNumber: true, wordSeparator: "!", }); expect(result).toEqual("passphrase"); expect(randomizer.randomEffLongWords).toHaveBeenCalledWith({ - numberOfWords: 4, + numberOfWords: 6, capitalize: DefaultPassphraseGenerationOptions.capitalize, number: true, separator: "!", @@ -153,14 +153,14 @@ describe("Passphrase generation strategy", () => { const strategy = new PassphraseGeneratorStrategy(randomizer, null); const result = await strategy.generate({ - numWords: 4, + numWords: 6, capitalize: true, wordSeparator: "!", }); expect(result).toEqual("passphrase"); expect(randomizer.randomEffLongWords).toHaveBeenCalledWith({ - numberOfWords: 4, + numberOfWords: 6, capitalize: true, number: DefaultPassphraseGenerationOptions.includeNumber, separator: "!", @@ -171,14 +171,14 @@ describe("Passphrase generation strategy", () => { const strategy = new PassphraseGeneratorStrategy(randomizer, null); const result = await strategy.generate({ - numWords: 4, + numWords: 6, capitalize: true, includeNumber: true, }); expect(result).toEqual("passphrase"); expect(randomizer.randomEffLongWords).toHaveBeenCalledWith({ - numberOfWords: 4, + numberOfWords: 6, capitalize: true, number: true, separator: DefaultPassphraseGenerationOptions.wordSeparator, diff --git a/libs/tools/generator/core/src/util.spec.ts b/libs/tools/generator/core/src/util.spec.ts index 7ffd869535b..8ed95a9f268 100644 --- a/libs/tools/generator/core/src/util.spec.ts +++ b/libs/tools/generator/core/src/util.spec.ts @@ -350,14 +350,14 @@ describe("optionsToRandomAsciiRequest", () => { describe("optionsToEffWordListRequest", () => { it("should map options", async () => { const result = optionsToEffWordListRequest({ - numWords: 4, + numWords: 6, capitalize: true, includeNumber: true, wordSeparator: "!", }); expect(result).toEqual({ - numberOfWords: 4, + numberOfWords: 6, capitalize: true, number: true, separator: "!", @@ -381,13 +381,13 @@ describe("optionsToEffWordListRequest", () => { it("should default capitalize", async () => { const result = optionsToEffWordListRequest({ - numWords: 4, + numWords: 6, includeNumber: true, wordSeparator: "!", }); expect(result).toEqual({ - numberOfWords: 4, + numberOfWords: 6, capitalize: DefaultPassphraseGenerationOptions.capitalize, number: true, separator: "!", @@ -396,13 +396,13 @@ describe("optionsToEffWordListRequest", () => { it("should default includeNumber", async () => { const result = optionsToEffWordListRequest({ - numWords: 4, + numWords: 6, capitalize: true, wordSeparator: "!", }); expect(result).toEqual({ - numberOfWords: 4, + numberOfWords: 6, capitalize: true, number: DefaultPassphraseGenerationOptions.includeNumber, separator: "!", @@ -411,13 +411,13 @@ describe("optionsToEffWordListRequest", () => { it("should default wordSeparator", async () => { const result = optionsToEffWordListRequest({ - numWords: 4, + numWords: 6, capitalize: true, includeNumber: true, }); expect(result).toEqual({ - numberOfWords: 4, + numberOfWords: 6, capitalize: true, number: true, separator: DefaultPassphraseGenerationOptions.wordSeparator, diff --git a/libs/tools/generator/extensions/history/src/legacy-password-history-decryptor.ts b/libs/tools/generator/extensions/history/src/legacy-password-history-decryptor.ts index 5769d79da47..6a27ad476ae 100644 --- a/libs/tools/generator/extensions/history/src/legacy-password-history-decryptor.ts +++ b/libs/tools/generator/extensions/history/src/legacy-password-history-decryptor.ts @@ -1,7 +1,7 @@ -import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service"; import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; import { EncString } from "@bitwarden/common/platform/models/domain/enc-string"; import { UserId } from "@bitwarden/common/types/guid"; +import { KeyService } from "@bitwarden/key-management"; import { GeneratedPasswordHistory } from "./generated-password-history"; @@ -9,13 +9,13 @@ import { GeneratedPasswordHistory } from "./generated-password-history"; export class LegacyPasswordHistoryDecryptor { constructor( private userId: UserId, - private cryptoService: CryptoService, + private keyService: KeyService, private encryptService: EncryptService, ) {} /** Decrypts a password history. */ async decrypt(history: GeneratedPasswordHistory[]): Promise { - const key = await this.cryptoService.getUserKey(this.userId); + const key = await this.keyService.getUserKey(this.userId); const promises = (history ?? []).map(async (item) => { const encrypted = new EncString(item.password); diff --git a/libs/tools/generator/extensions/history/src/local-generator-history.service.spec.ts b/libs/tools/generator/extensions/history/src/local-generator-history.service.spec.ts index 1fbc956bc59..3936b03acc9 100644 --- a/libs/tools/generator/extensions/history/src/local-generator-history.service.spec.ts +++ b/libs/tools/generator/extensions/history/src/local-generator-history.service.spec.ts @@ -1,13 +1,13 @@ import { mock } from "jest-mock-extended"; import { firstValueFrom, of } from "rxjs"; -import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service"; import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; import { EncString } from "@bitwarden/common/platform/models/domain/enc-string"; import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key"; import { CsprngArray } from "@bitwarden/common/types/csprng"; import { UserId } from "@bitwarden/common/types/guid"; import { UserKey } from "@bitwarden/common/types/key"; +import { KeyService } from "@bitwarden/key-management"; import { FakeStateProvider, awaitAsync, mockAccountServiceWith } from "../../../../../common/spec"; @@ -18,7 +18,7 @@ const AnotherUser = "AnotherUser" as UserId; describe("LocalGeneratorHistoryService", () => { const encryptService = mock(); - const keyService = mock(); + const keyService = mock(); const userKey = new SymmetricCryptoKey(new Uint8Array(64) as CsprngArray) as UserKey; beforeEach(() => { diff --git a/libs/tools/generator/extensions/history/src/local-generator-history.service.ts b/libs/tools/generator/extensions/history/src/local-generator-history.service.ts index 99497f7ad50..7a5743f21b1 100644 --- a/libs/tools/generator/extensions/history/src/local-generator-history.service.ts +++ b/libs/tools/generator/extensions/history/src/local-generator-history.service.ts @@ -1,6 +1,5 @@ import { filter, map } from "rxjs"; -import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service"; import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; import { SingleUserState, StateProvider } from "@bitwarden/common/platform/state"; import { BufferedState } from "@bitwarden/common/tools/state/buffered-state"; @@ -9,6 +8,7 @@ import { SecretState } from "@bitwarden/common/tools/state/secret-state"; import { UserKeyEncryptor } from "@bitwarden/common/tools/state/user-key-encryptor"; import { UserId } from "@bitwarden/common/types/guid"; import { CredentialAlgorithm } from "@bitwarden/generator-core"; +import { KeyService } from "@bitwarden/key-management"; import { GeneratedCredential } from "./generated-credential"; import { GeneratorHistoryService } from "./generator-history.abstraction"; @@ -24,7 +24,7 @@ const OPTIONS_FRAME_SIZE = 2048; export class LocalGeneratorHistoryService extends GeneratorHistoryService { constructor( private readonly encryptService: EncryptService, - private readonly keyService: CryptoService, + private readonly keyService: KeyService, private readonly stateProvider: StateProvider, private readonly options: HistoryServiceOptions = { maxTotal: 200 }, ) { diff --git a/libs/tools/generator/extensions/legacy/src/create-legacy-password-generation-service.ts b/libs/tools/generator/extensions/legacy/src/create-legacy-password-generation-service.ts index 8ef14a3a9eb..a76fba9759e 100644 --- a/libs/tools/generator/extensions/legacy/src/create-legacy-password-generation-service.ts +++ b/libs/tools/generator/extensions/legacy/src/create-legacy-password-generation-service.ts @@ -1,28 +1,28 @@ import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; -import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service"; import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; import { StateProvider } from "@bitwarden/common/platform/state"; import { engine, services, strategies } from "@bitwarden/generator-core"; import { LocalGeneratorHistoryService } from "@bitwarden/generator-history"; import { DefaultGeneratorNavigationService } from "@bitwarden/generator-navigation"; +import { KeyService } from "@bitwarden/key-management"; import { LegacyPasswordGenerationService } from "./legacy-password-generation.service"; import { PasswordGenerationServiceAbstraction } from "./password-generation.service.abstraction"; const { PassphraseGeneratorStrategy, PasswordGeneratorStrategy } = strategies; -const { CryptoServiceRandomizer, PasswordRandomizer } = engine; +const { KeyServiceRandomizer, PasswordRandomizer } = engine; const DefaultGeneratorService = services.DefaultGeneratorService; export function legacyPasswordGenerationServiceFactory( encryptService: EncryptService, - cryptoService: CryptoService, + keyService: KeyService, policyService: PolicyService, accountService: AccountService, stateProvider: StateProvider, ): PasswordGenerationServiceAbstraction { - const randomizer = new CryptoServiceRandomizer(cryptoService); + const randomizer = new KeyServiceRandomizer(keyService); const passwordRandomizer = new PasswordRandomizer(randomizer); const passwords = new DefaultGeneratorService( @@ -37,7 +37,7 @@ export function legacyPasswordGenerationServiceFactory( const navigation = new DefaultGeneratorNavigationService(stateProvider, policyService); - const history = new LocalGeneratorHistoryService(encryptService, cryptoService, stateProvider); + const history = new LocalGeneratorHistoryService(encryptService, keyService, stateProvider); return new LegacyPasswordGenerationService( accountService, diff --git a/libs/tools/generator/extensions/legacy/src/create-legacy-username-generation-service.ts b/libs/tools/generator/extensions/legacy/src/create-legacy-username-generation-service.ts index 8626ef81f90..1d8a36eeb05 100644 --- a/libs/tools/generator/extensions/legacy/src/create-legacy-username-generation-service.ts +++ b/libs/tools/generator/extensions/legacy/src/create-legacy-username-generation-service.ts @@ -1,18 +1,18 @@ import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; -import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service"; import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { StateProvider } from "@bitwarden/common/platform/state"; import { RestClient } from "@bitwarden/common/tools/integration/rpc"; import { engine, services, strategies, Integrations } from "@bitwarden/generator-core"; import { DefaultGeneratorNavigationService } from "@bitwarden/generator-navigation"; +import { KeyService } from "@bitwarden/key-management"; import { LegacyUsernameGenerationService } from "./legacy-username-generation.service"; import { UsernameGenerationServiceAbstraction } from "./username-generation.service.abstraction"; -const { CryptoServiceRandomizer, UsernameRandomizer, EmailRandomizer, EmailCalculator } = engine; +const { KeyServiceRandomizer, UsernameRandomizer, EmailRandomizer, EmailCalculator } = engine; const DefaultGeneratorService = services.DefaultGeneratorService; const { CatchallGeneratorStrategy, @@ -24,13 +24,13 @@ const { export function legacyUsernameGenerationServiceFactory( apiService: ApiService, i18nService: I18nService, - cryptoService: CryptoService, + keyService: KeyService, encryptService: EncryptService, policyService: PolicyService, accountService: AccountService, stateProvider: StateProvider, ): UsernameGenerationServiceAbstraction { - const randomizer = new CryptoServiceRandomizer(cryptoService); + const randomizer = new KeyServiceRandomizer(keyService); const restClient = new RestClient(apiService, i18nService); const usernameRandomizer = new UsernameRandomizer(randomizer); const emailRandomizer = new EmailRandomizer(randomizer); @@ -57,7 +57,7 @@ export function legacyUsernameGenerationServiceFactory( restClient, i18nService, encryptService, - cryptoService, + keyService, stateProvider, ), policyService, @@ -69,7 +69,7 @@ export function legacyUsernameGenerationServiceFactory( restClient, i18nService, encryptService, - cryptoService, + keyService, stateProvider, ), policyService, @@ -81,7 +81,7 @@ export function legacyUsernameGenerationServiceFactory( restClient, i18nService, encryptService, - cryptoService, + keyService, stateProvider, ), policyService, @@ -93,7 +93,7 @@ export function legacyUsernameGenerationServiceFactory( restClient, i18nService, encryptService, - cryptoService, + keyService, stateProvider, ), policyService, @@ -105,7 +105,7 @@ export function legacyUsernameGenerationServiceFactory( restClient, i18nService, encryptService, - cryptoService, + keyService, stateProvider, ), policyService, @@ -117,7 +117,7 @@ export function legacyUsernameGenerationServiceFactory( restClient, i18nService, encryptService, - cryptoService, + keyService, stateProvider, ), policyService, diff --git a/libs/tools/generator/extensions/legacy/src/legacy-password-generation.service.spec.ts b/libs/tools/generator/extensions/legacy/src/legacy-password-generation.service.spec.ts index 21fbcb26273..d932d013199 100644 --- a/libs/tools/generator/extensions/legacy/src/legacy-password-generation.service.spec.ts +++ b/libs/tools/generator/extensions/legacy/src/legacy-password-generation.service.spec.ts @@ -348,7 +348,7 @@ describe("LegacyPasswordGenerationService", () => { const innerPassphrase = createPassphraseGenerator( {}, { - minNumberWords: 5, + minNumberWords: 6, capitalize: true, includeNumber: true, }, @@ -370,7 +370,7 @@ describe("LegacyPasswordGenerationService", () => { expect(result).toBe(options); expect(result).toMatchObject({ - numWords: 5, + numWords: 6, capitalize: true, includeNumber: true, }); diff --git a/libs/tools/send/send-ui/src/send-form/components/options/send-options.component.html b/libs/tools/send/send-ui/src/send-form/components/options/send-options.component.html index 98da24b5188..fe05cd71b94 100644 --- a/libs/tools/send/send-ui/src/send-form/components/options/send-options.component.html +++ b/libs/tools/send/send-ui/src/send-form/components/options/send-options.component.html @@ -12,7 +12,7 @@ > - {{ "password" | i18n }} + {{ (passwordRemoved ? "newPassword" : "password") | i18n }}