mirror of
https://github.com/bitwarden/browser
synced 2025-12-19 01:33:33 +00:00
Merge branch 'main' into autofill/pm-5189-fix-issues-present-with-inline-menu-rendering-in-iframes
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@bitwarden/browser",
|
||||
"version": "2024.3.1",
|
||||
"version": "2024.4.1",
|
||||
"scripts": {
|
||||
"build": "webpack",
|
||||
"build:mv3": "cross-env MANIFEST_VERSION=3 webpack",
|
||||
|
||||
@@ -3005,5 +3005,8 @@
|
||||
},
|
||||
"passkeyRemoved": {
|
||||
"message": "Passkey removed"
|
||||
},
|
||||
"unassignedItemsBanner": {
|
||||
"message": "Notice: Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -95,10 +95,10 @@
|
||||
"message": "Auto-fill login"
|
||||
},
|
||||
"autoFillCard": {
|
||||
"message": "Auto-fill card"
|
||||
"message": "Auto-bete txartela"
|
||||
},
|
||||
"autoFillIdentity": {
|
||||
"message": "Auto-fill identity"
|
||||
"message": "Auto-bete nortasuna"
|
||||
},
|
||||
"generatePasswordCopied": {
|
||||
"message": "Sortu pasahitza (kopiatuta)"
|
||||
@@ -110,19 +110,19 @@
|
||||
"message": "Bat datozen saio-hasierarik gabe"
|
||||
},
|
||||
"noCards": {
|
||||
"message": "No cards"
|
||||
"message": "Txartelik ez"
|
||||
},
|
||||
"noIdentities": {
|
||||
"message": "No identities"
|
||||
"message": "Nortasunik ez"
|
||||
},
|
||||
"addLoginMenu": {
|
||||
"message": "Add login"
|
||||
},
|
||||
"addCardMenu": {
|
||||
"message": "Add card"
|
||||
"message": "Gehitu txartela"
|
||||
},
|
||||
"addIdentityMenu": {
|
||||
"message": "Add identity"
|
||||
"message": "Gehitu nortasuna"
|
||||
},
|
||||
"unlockVaultMenu": {
|
||||
"message": "Desblokeatu kutxa gotorra"
|
||||
@@ -223,10 +223,10 @@
|
||||
"message": "Bitwarden Laguntza zentroa"
|
||||
},
|
||||
"communityForums": {
|
||||
"message": "Explore Bitwarden community forums"
|
||||
"message": "Esploratu Bitwarden komunitatearen foroak"
|
||||
},
|
||||
"contactSupport": {
|
||||
"message": "Contact Bitwarden support"
|
||||
"message": "Jarri harremanetan Bitwardeneko laguntza taldearekin"
|
||||
},
|
||||
"sync": {
|
||||
"message": "Sinkronizatu"
|
||||
@@ -269,7 +269,7 @@
|
||||
"message": "Luzera"
|
||||
},
|
||||
"passwordMinLength": {
|
||||
"message": "Minimum password length"
|
||||
"message": "Pasahitzaren gutxieneko luzera"
|
||||
},
|
||||
"uppercase": {
|
||||
"message": "Letra larria (A-Z)"
|
||||
@@ -1064,7 +1064,7 @@
|
||||
"message": "Edit browser settings."
|
||||
},
|
||||
"autofillOverlayVisibilityOff": {
|
||||
"message": "Off",
|
||||
"message": "Itzalita",
|
||||
"description": "Overlay setting select option for disabling autofill overlay"
|
||||
},
|
||||
"autofillOverlayVisibilityOnFieldFocus": {
|
||||
@@ -1592,10 +1592,10 @@
|
||||
"message": "Ezarri pasahitz nagusia"
|
||||
},
|
||||
"currentMasterPass": {
|
||||
"message": "Current master password"
|
||||
"message": "Oraingo pasahitz nagusia"
|
||||
},
|
||||
"newMasterPass": {
|
||||
"message": "New master password"
|
||||
"message": "Pasahitz nagusi berria"
|
||||
},
|
||||
"confirmNewMasterPass": {
|
||||
"message": "Confirm new master password"
|
||||
@@ -2266,10 +2266,10 @@
|
||||
"message": "Please make sure your vault is unlocked and the Fingerprint phrase matches on the other device."
|
||||
},
|
||||
"resendNotification": {
|
||||
"message": "Resend notification"
|
||||
"message": "Berbidali jakinarazpena"
|
||||
},
|
||||
"viewAllLoginOptions": {
|
||||
"message": "View all log in options"
|
||||
"message": "Ikusi erregistro guztiak ezarpenetan"
|
||||
},
|
||||
"notificationSentDevice": {
|
||||
"message": "A notification has been sent to your device."
|
||||
@@ -2293,13 +2293,13 @@
|
||||
"message": "Check known data breaches for this password"
|
||||
},
|
||||
"important": {
|
||||
"message": "Important:"
|
||||
"message": "Garrantzitsua:"
|
||||
},
|
||||
"masterPasswordHint": {
|
||||
"message": "Your master password cannot be recovered if you forget it!"
|
||||
},
|
||||
"characterMinimum": {
|
||||
"message": "$LENGTH$ character minimum",
|
||||
"message": "$LENGTH$ karaktere gutxienez",
|
||||
"placeholders": {
|
||||
"length": {
|
||||
"content": "$1",
|
||||
@@ -2326,7 +2326,7 @@
|
||||
"message": "Select an item from this screen, or explore other options in settings."
|
||||
},
|
||||
"gotIt": {
|
||||
"message": "Got it"
|
||||
"message": "Ulertuta"
|
||||
},
|
||||
"autofillSettings": {
|
||||
"message": "Auto-fill settings"
|
||||
@@ -2359,25 +2359,25 @@
|
||||
"message": "Logging in on"
|
||||
},
|
||||
"opensInANewWindow": {
|
||||
"message": "Opens in a new window"
|
||||
"message": "Leiho berri batean irekitzen da"
|
||||
},
|
||||
"deviceApprovalRequired": {
|
||||
"message": "Device approval required. Select an approval option below:"
|
||||
},
|
||||
"rememberThisDevice": {
|
||||
"message": "Remember this device"
|
||||
"message": "Gogoratu gailu hau"
|
||||
},
|
||||
"uncheckIfPublicDevice": {
|
||||
"message": "Uncheck if using a public device"
|
||||
},
|
||||
"approveFromYourOtherDevice": {
|
||||
"message": "Approve from your other device"
|
||||
"message": "Onartu zure beste gailutik"
|
||||
},
|
||||
"requestAdminApproval": {
|
||||
"message": "Request admin approval"
|
||||
"message": "Eskatu administratzailearen onarpena"
|
||||
},
|
||||
"approveWithMasterPassword": {
|
||||
"message": "Approve with master password"
|
||||
"message": "Onartu pasahitz nagusiarekin"
|
||||
},
|
||||
"ssoIdentifierRequired": {
|
||||
"message": "Organization SSO identifier is required."
|
||||
@@ -2390,31 +2390,31 @@
|
||||
"message": "Access denied. You do not have permission to view this page."
|
||||
},
|
||||
"general": {
|
||||
"message": "General"
|
||||
"message": "Orokorra"
|
||||
},
|
||||
"display": {
|
||||
"message": "Display"
|
||||
"message": "Bistaratzea"
|
||||
},
|
||||
"accountSuccessfullyCreated": {
|
||||
"message": "Account successfully created!"
|
||||
"message": "Kontua zuzen sortu da!"
|
||||
},
|
||||
"adminApprovalRequested": {
|
||||
"message": "Admin approval requested"
|
||||
"message": "Administratzailearen onarpena eskatuta"
|
||||
},
|
||||
"adminApprovalRequestSentToAdmins": {
|
||||
"message": "Your request has been sent to your admin."
|
||||
"message": "Zure eskaera zure administratzaileari bidali zaio."
|
||||
},
|
||||
"youWillBeNotifiedOnceApproved": {
|
||||
"message": "You will be notified once approved."
|
||||
"message": "Jakinaraziko zaizu onartzen denean."
|
||||
},
|
||||
"troubleLoggingIn": {
|
||||
"message": "Trouble logging in?"
|
||||
"message": "Arazoak saioa hasterakoan?"
|
||||
},
|
||||
"loginApproved": {
|
||||
"message": "Login approved"
|
||||
},
|
||||
"userEmailMissing": {
|
||||
"message": "User email missing"
|
||||
"message": "Erabiltzailearen emaila falta da"
|
||||
},
|
||||
"deviceTrusted": {
|
||||
"message": "Device trusted"
|
||||
@@ -2540,19 +2540,19 @@
|
||||
"description": "Notification button text for starting a fileless import."
|
||||
},
|
||||
"importing": {
|
||||
"message": "Importing...",
|
||||
"message": "Inportatzen...",
|
||||
"description": "Notification message for when an import is in progress."
|
||||
},
|
||||
"dataSuccessfullyImported": {
|
||||
"message": "Data successfully imported!",
|
||||
"message": "Datuak zuzen inportatu dira!",
|
||||
"description": "Notification message for when an import has completed successfully."
|
||||
},
|
||||
"dataImportFailed": {
|
||||
"message": "Error importing. Check console for details.",
|
||||
"message": "Errorea gertatu da inportatzean. Begiratu xehetasunak kontsolan.",
|
||||
"description": "Notification message for when an import has failed."
|
||||
},
|
||||
"importNetworkError": {
|
||||
"message": "Network error encountered during import.",
|
||||
"message": "Sareko errorea gertatu da inportatzerakoan.",
|
||||
"description": "Notification message for when an import has failed due to a network error."
|
||||
},
|
||||
"aliasDomain": {
|
||||
@@ -2602,11 +2602,11 @@
|
||||
"description": "Screen reader text for when a login item is focused where a partial username is displayed. SR will announce this phrase before reading the text of the partial username"
|
||||
},
|
||||
"noItemsToShow": {
|
||||
"message": "No items to show",
|
||||
"message": "Ez dago elementurik erakusteko",
|
||||
"description": "Text to show in overlay if there are no matching items"
|
||||
},
|
||||
"newItem": {
|
||||
"message": "New item",
|
||||
"message": "Elementu berria",
|
||||
"description": "Button text to display in overlay when there are no matching items"
|
||||
},
|
||||
"addNewVaultItem": {
|
||||
@@ -2618,32 +2618,32 @@
|
||||
"description": "Screen reader text for announcing when the overlay opens on the page"
|
||||
},
|
||||
"turnOn": {
|
||||
"message": "Turn on"
|
||||
"message": "Piztu"
|
||||
},
|
||||
"ignore": {
|
||||
"message": "Ignore"
|
||||
"message": "Ezikusi"
|
||||
},
|
||||
"importData": {
|
||||
"message": "Import data",
|
||||
"message": "Inportatu datuak",
|
||||
"description": "Used for the header of the import dialog, the import button and within the file-password-prompt"
|
||||
},
|
||||
"importError": {
|
||||
"message": "Import error"
|
||||
"message": "Errorea inportatzerakoan"
|
||||
},
|
||||
"importErrorDesc": {
|
||||
"message": "There was a problem with the data you tried to import. Please resolve the errors listed below in your source file and try again."
|
||||
"message": "Inportatzen saiatu zaren datuekin arazo bat egon da. Mesedez, konpondu ondoren adierazten diren akatsak eta saiatu berriro."
|
||||
},
|
||||
"resolveTheErrorsBelowAndTryAgain": {
|
||||
"message": "Resolve the errors below and try again."
|
||||
"message": "Konpondu beheko akatsak eta saiatu berriro."
|
||||
},
|
||||
"description": {
|
||||
"message": "Description"
|
||||
"message": "Deskribapena"
|
||||
},
|
||||
"importSuccess": {
|
||||
"message": "Data successfully imported"
|
||||
"message": "Datuak zuzen inportatu dira"
|
||||
},
|
||||
"importSuccessNumberOfItems": {
|
||||
"message": "A total of $AMOUNT$ items were imported.",
|
||||
"message": "Guztira $AMOUNT$ elementu inportatu dira.",
|
||||
"placeholders": {
|
||||
"amount": {
|
||||
"content": "$1",
|
||||
@@ -2652,7 +2652,7 @@
|
||||
}
|
||||
},
|
||||
"tryAgain": {
|
||||
"message": "Try again"
|
||||
"message": "Saiatu berriro"
|
||||
},
|
||||
"verificationRequiredForActionSetPinToContinue": {
|
||||
"message": "Verification required for this action. Set a PIN to continue."
|
||||
@@ -2661,10 +2661,10 @@
|
||||
"message": "Set PIN"
|
||||
},
|
||||
"verifyWithBiometrics": {
|
||||
"message": "Verify with biometrics"
|
||||
"message": "Egiaztatu biometria erabiliz"
|
||||
},
|
||||
"awaitingConfirmation": {
|
||||
"message": "Awaiting confirmation"
|
||||
"message": "Baieztapenaren zain"
|
||||
},
|
||||
"couldNotCompleteBiometrics": {
|
||||
"message": "Could not complete biometrics."
|
||||
@@ -2673,13 +2673,13 @@
|
||||
"message": "Need a different method?"
|
||||
},
|
||||
"useMasterPassword": {
|
||||
"message": "Use master password"
|
||||
"message": "Erabili pasahitz nagusia"
|
||||
},
|
||||
"usePin": {
|
||||
"message": "Use PIN"
|
||||
"message": "Erabili PIN kodea"
|
||||
},
|
||||
"useBiometrics": {
|
||||
"message": "Use biometrics"
|
||||
"message": "Erabili biometria"
|
||||
},
|
||||
"enterVerificationCodeSentToEmail": {
|
||||
"message": "Enter the verification code that was sent to your email."
|
||||
@@ -2688,10 +2688,10 @@
|
||||
"message": "Resend code"
|
||||
},
|
||||
"total": {
|
||||
"message": "Total"
|
||||
"message": "Guztira"
|
||||
},
|
||||
"importWarning": {
|
||||
"message": "You are importing data to $ORGANIZATION$. Your data may be shared with members of this organization. Do you want to proceed?",
|
||||
"message": "$ORGANIZATION$(e)ra datuak inportatzen ari zara. Zure datuak erakunde horretako kideekin parteka daitezke. Jarraitu nahi duzu?",
|
||||
"placeholders": {
|
||||
"organization": {
|
||||
"content": "$1",
|
||||
@@ -2810,7 +2810,7 @@
|
||||
"message": "You do not have a matching login for this site."
|
||||
},
|
||||
"confirm": {
|
||||
"message": "Confirm"
|
||||
"message": "Berretsi"
|
||||
},
|
||||
"savePasskey": {
|
||||
"message": "Save passkey"
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
"description": "Extension description"
|
||||
},
|
||||
"loginOrCreateNewAccount": {
|
||||
"message": "Käytä salattua holviasi kirjautumalla sisään tai tai luo uusi tili."
|
||||
"message": "Käytä salattua holviasi kirjautumalla sisään tai luo uusi tili."
|
||||
},
|
||||
"createAccount": {
|
||||
"message": "Luo tili"
|
||||
@@ -802,7 +802,7 @@
|
||||
"message": "Lue lisää"
|
||||
},
|
||||
"authenticatorKeyTotp": {
|
||||
"message": "Todennusaavain (TOTP)"
|
||||
"message": "Todennusavain (TOTP)"
|
||||
},
|
||||
"verificationCodeTotp": {
|
||||
"message": "Todennuskoodi (TOTP)"
|
||||
|
||||
@@ -17,18 +17,21 @@ import {
|
||||
FactoryOptions,
|
||||
factory,
|
||||
} from "../../../platform/background/service-factories/factory-options";
|
||||
|
||||
import { accountServiceFactory, AccountServiceInitOptions } from "./account-service.factory";
|
||||
import {
|
||||
stateServiceFactory,
|
||||
StateServiceInitOptions,
|
||||
} from "../../../platform/background/service-factories/state-service.factory";
|
||||
internalMasterPasswordServiceFactory,
|
||||
MasterPasswordServiceInitOptions,
|
||||
} from "./master-password-service.factory";
|
||||
|
||||
type AuthRequestServiceFactoryOptions = FactoryOptions;
|
||||
|
||||
export type AuthRequestServiceInitOptions = AuthRequestServiceFactoryOptions &
|
||||
AppIdServiceInitOptions &
|
||||
AccountServiceInitOptions &
|
||||
MasterPasswordServiceInitOptions &
|
||||
CryptoServiceInitOptions &
|
||||
ApiServiceInitOptions &
|
||||
StateServiceInitOptions;
|
||||
ApiServiceInitOptions;
|
||||
|
||||
export function authRequestServiceFactory(
|
||||
cache: { authRequestService?: AuthRequestServiceAbstraction } & CachedServices,
|
||||
@@ -41,9 +44,10 @@ export function authRequestServiceFactory(
|
||||
async () =>
|
||||
new AuthRequestService(
|
||||
await appIdServiceFactory(cache, opts),
|
||||
await accountServiceFactory(cache, opts),
|
||||
await internalMasterPasswordServiceFactory(cache, opts),
|
||||
await cryptoServiceFactory(cache, opts),
|
||||
await apiServiceFactory(cache, opts),
|
||||
await stateServiceFactory(cache, opts),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -31,6 +31,11 @@ import {
|
||||
StateProviderInitOptions,
|
||||
} from "../../../platform/background/service-factories/state-provider.factory";
|
||||
|
||||
import { accountServiceFactory, AccountServiceInitOptions } from "./account-service.factory";
|
||||
import {
|
||||
internalMasterPasswordServiceFactory,
|
||||
MasterPasswordServiceInitOptions,
|
||||
} from "./master-password-service.factory";
|
||||
import { TokenServiceInitOptions, tokenServiceFactory } from "./token-service.factory";
|
||||
|
||||
type KeyConnectorServiceFactoryOptions = FactoryOptions & {
|
||||
@@ -40,6 +45,8 @@ type KeyConnectorServiceFactoryOptions = FactoryOptions & {
|
||||
};
|
||||
|
||||
export type KeyConnectorServiceInitOptions = KeyConnectorServiceFactoryOptions &
|
||||
AccountServiceInitOptions &
|
||||
MasterPasswordServiceInitOptions &
|
||||
CryptoServiceInitOptions &
|
||||
ApiServiceInitOptions &
|
||||
TokenServiceInitOptions &
|
||||
@@ -58,6 +65,8 @@ export function keyConnectorServiceFactory(
|
||||
opts,
|
||||
async () =>
|
||||
new KeyConnectorService(
|
||||
await accountServiceFactory(cache, opts),
|
||||
await internalMasterPasswordServiceFactory(cache, opts),
|
||||
await cryptoServiceFactory(cache, opts),
|
||||
await apiServiceFactory(cache, opts),
|
||||
await tokenServiceFactory(cache, opts),
|
||||
|
||||
@@ -59,6 +59,7 @@ import {
|
||||
PasswordStrengthServiceInitOptions,
|
||||
} from "../../../tools/background/service_factories/password-strength-service.factory";
|
||||
|
||||
import { accountServiceFactory, AccountServiceInitOptions } from "./account-service.factory";
|
||||
import {
|
||||
authRequestServiceFactory,
|
||||
AuthRequestServiceInitOptions,
|
||||
@@ -71,6 +72,10 @@ import {
|
||||
keyConnectorServiceFactory,
|
||||
KeyConnectorServiceInitOptions,
|
||||
} from "./key-connector-service.factory";
|
||||
import {
|
||||
internalMasterPasswordServiceFactory,
|
||||
MasterPasswordServiceInitOptions,
|
||||
} from "./master-password-service.factory";
|
||||
import { tokenServiceFactory, TokenServiceInitOptions } from "./token-service.factory";
|
||||
import { twoFactorServiceFactory, TwoFactorServiceInitOptions } from "./two-factor-service.factory";
|
||||
import {
|
||||
@@ -81,6 +86,8 @@ import {
|
||||
type LoginStrategyServiceFactoryOptions = FactoryOptions;
|
||||
|
||||
export type LoginStrategyServiceInitOptions = LoginStrategyServiceFactoryOptions &
|
||||
AccountServiceInitOptions &
|
||||
MasterPasswordServiceInitOptions &
|
||||
CryptoServiceInitOptions &
|
||||
ApiServiceInitOptions &
|
||||
TokenServiceInitOptions &
|
||||
@@ -111,6 +118,8 @@ export function loginStrategyServiceFactory(
|
||||
opts,
|
||||
async () =>
|
||||
new LoginStrategyService(
|
||||
await accountServiceFactory(cache, opts),
|
||||
await internalMasterPasswordServiceFactory(cache, opts),
|
||||
await cryptoServiceFactory(cache, opts),
|
||||
await apiServiceFactory(cache, opts),
|
||||
await tokenServiceFactory(cache, opts),
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
import {
|
||||
InternalMasterPasswordServiceAbstraction,
|
||||
MasterPasswordServiceAbstraction,
|
||||
} from "@bitwarden/common/auth/abstractions/master-password.service.abstraction";
|
||||
import { MasterPasswordService } from "@bitwarden/common/auth/services/master-password/master-password.service";
|
||||
|
||||
import {
|
||||
CachedServices,
|
||||
factory,
|
||||
FactoryOptions,
|
||||
} from "../../../platform/background/service-factories/factory-options";
|
||||
import {
|
||||
stateProviderFactory,
|
||||
StateProviderInitOptions,
|
||||
} from "../../../platform/background/service-factories/state-provider.factory";
|
||||
|
||||
type MasterPasswordServiceFactoryOptions = FactoryOptions;
|
||||
|
||||
export type MasterPasswordServiceInitOptions = MasterPasswordServiceFactoryOptions &
|
||||
StateProviderInitOptions;
|
||||
|
||||
export function internalMasterPasswordServiceFactory(
|
||||
cache: { masterPasswordService?: InternalMasterPasswordServiceAbstraction } & CachedServices,
|
||||
opts: MasterPasswordServiceInitOptions,
|
||||
): Promise<InternalMasterPasswordServiceAbstraction> {
|
||||
return factory(
|
||||
cache,
|
||||
"masterPasswordService",
|
||||
opts,
|
||||
async () => new MasterPasswordService(await stateProviderFactory(cache, opts)),
|
||||
);
|
||||
}
|
||||
|
||||
export async function masterPasswordServiceFactory(
|
||||
cache: { masterPasswordService?: InternalMasterPasswordServiceAbstraction } & CachedServices,
|
||||
opts: MasterPasswordServiceInitOptions,
|
||||
): Promise<MasterPasswordServiceAbstraction> {
|
||||
return (await internalMasterPasswordServiceFactory(
|
||||
cache,
|
||||
opts,
|
||||
)) as MasterPasswordServiceAbstraction;
|
||||
}
|
||||
@@ -31,6 +31,11 @@ import {
|
||||
stateServiceFactory,
|
||||
} from "../../../platform/background/service-factories/state-service.factory";
|
||||
|
||||
import { accountServiceFactory, AccountServiceInitOptions } from "./account-service.factory";
|
||||
import {
|
||||
internalMasterPasswordServiceFactory,
|
||||
MasterPasswordServiceInitOptions,
|
||||
} from "./master-password-service.factory";
|
||||
import { PinCryptoServiceInitOptions, pinCryptoServiceFactory } from "./pin-crypto-service.factory";
|
||||
import {
|
||||
userDecryptionOptionsServiceFactory,
|
||||
@@ -46,6 +51,8 @@ type UserVerificationServiceFactoryOptions = FactoryOptions;
|
||||
export type UserVerificationServiceInitOptions = UserVerificationServiceFactoryOptions &
|
||||
StateServiceInitOptions &
|
||||
CryptoServiceInitOptions &
|
||||
AccountServiceInitOptions &
|
||||
MasterPasswordServiceInitOptions &
|
||||
I18nServiceInitOptions &
|
||||
UserVerificationApiServiceInitOptions &
|
||||
UserDecryptionOptionsServiceInitOptions &
|
||||
@@ -66,6 +73,8 @@ export function userVerificationServiceFactory(
|
||||
new UserVerificationService(
|
||||
await stateServiceFactory(cache, opts),
|
||||
await cryptoServiceFactory(cache, opts),
|
||||
await accountServiceFactory(cache, opts),
|
||||
await internalMasterPasswordServiceFactory(cache, opts),
|
||||
await i18nServiceFactory(cache, opts),
|
||||
await userVerificationApiServiceFactory(cache, opts),
|
||||
await userDecryptionOptionsServiceFactory(cache, opts),
|
||||
|
||||
@@ -12,6 +12,7 @@ import { InternalPolicyService } from "@bitwarden/common/admin-console/abstracti
|
||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||
import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service";
|
||||
import { DeviceTrustCryptoServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust-crypto.service.abstraction";
|
||||
import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/auth/abstractions/master-password.service.abstraction";
|
||||
import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction";
|
||||
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
|
||||
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
|
||||
@@ -41,6 +42,7 @@ export class LockComponent extends BaseLockComponent {
|
||||
fido2PopoutSessionData$ = fido2PopoutSessionData$();
|
||||
|
||||
constructor(
|
||||
masterPasswordService: InternalMasterPasswordServiceAbstraction,
|
||||
router: Router,
|
||||
i18nService: I18nService,
|
||||
platformUtilsService: PlatformUtilsService,
|
||||
@@ -66,6 +68,7 @@ export class LockComponent extends BaseLockComponent {
|
||||
accountService: AccountService,
|
||||
) {
|
||||
super(
|
||||
masterPasswordService,
|
||||
router,
|
||||
i18nService,
|
||||
platformUtilsService,
|
||||
|
||||
@@ -1,65 +1,9 @@
|
||||
import { Component } from "@angular/core";
|
||||
import { ActivatedRoute, Router } from "@angular/router";
|
||||
|
||||
import { SetPasswordComponent as BaseSetPasswordComponent } from "@bitwarden/angular/auth/components/set-password.component";
|
||||
import { InternalUserDecryptionOptionsServiceAbstraction } from "@bitwarden/auth/common";
|
||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||
import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.service.abstraction";
|
||||
import { OrganizationUserService } from "@bitwarden/common/admin-console/abstractions/organization-user/organization-user.service";
|
||||
import { PolicyApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/policy/policy-api.service.abstraction";
|
||||
import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
|
||||
import { SsoLoginServiceAbstraction } from "@bitwarden/common/auth/abstractions/sso-login.service.abstraction";
|
||||
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
|
||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
|
||||
import { PasswordGenerationServiceAbstraction } from "@bitwarden/common/tools/generator/password";
|
||||
import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction";
|
||||
import { DialogService } from "@bitwarden/components";
|
||||
|
||||
@Component({
|
||||
selector: "app-set-password",
|
||||
templateUrl: "set-password.component.html",
|
||||
})
|
||||
export class SetPasswordComponent extends BaseSetPasswordComponent {
|
||||
constructor(
|
||||
apiService: ApiService,
|
||||
i18nService: I18nService,
|
||||
cryptoService: CryptoService,
|
||||
messagingService: MessagingService,
|
||||
stateService: StateService,
|
||||
passwordGenerationService: PasswordGenerationServiceAbstraction,
|
||||
platformUtilsService: PlatformUtilsService,
|
||||
policyApiService: PolicyApiServiceAbstraction,
|
||||
policyService: PolicyService,
|
||||
router: Router,
|
||||
syncService: SyncService,
|
||||
route: ActivatedRoute,
|
||||
organizationApiService: OrganizationApiServiceAbstraction,
|
||||
organizationUserService: OrganizationUserService,
|
||||
userDecryptionOptionsService: InternalUserDecryptionOptionsServiceAbstraction,
|
||||
ssoLoginService: SsoLoginServiceAbstraction,
|
||||
dialogService: DialogService,
|
||||
) {
|
||||
super(
|
||||
i18nService,
|
||||
cryptoService,
|
||||
messagingService,
|
||||
passwordGenerationService,
|
||||
platformUtilsService,
|
||||
policyApiService,
|
||||
policyService,
|
||||
router,
|
||||
apiService,
|
||||
syncService,
|
||||
route,
|
||||
stateService,
|
||||
organizationApiService,
|
||||
organizationUserService,
|
||||
userDecryptionOptionsService,
|
||||
ssoLoginService,
|
||||
dialogService,
|
||||
);
|
||||
}
|
||||
}
|
||||
export class SetPasswordComponent extends BaseSetPasswordComponent {}
|
||||
|
||||
@@ -9,7 +9,9 @@ import {
|
||||
UserDecryptionOptionsServiceAbstraction,
|
||||
} from "@bitwarden/auth/common";
|
||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||
import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service";
|
||||
import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/auth/abstractions/master-password.service.abstraction";
|
||||
import { SsoLoginServiceAbstraction } from "@bitwarden/common/auth/abstractions/sso-login.service.abstraction";
|
||||
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
|
||||
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
||||
@@ -45,7 +47,9 @@ export class SsoComponent extends BaseSsoComponent {
|
||||
logService: LogService,
|
||||
userDecryptionOptionsService: UserDecryptionOptionsServiceAbstraction,
|
||||
configService: ConfigService,
|
||||
protected authService: AuthService,
|
||||
masterPasswordService: InternalMasterPasswordServiceAbstraction,
|
||||
accountService: AccountService,
|
||||
private authService: AuthService,
|
||||
@Inject(WINDOW) private win: Window,
|
||||
) {
|
||||
super(
|
||||
@@ -63,6 +67,8 @@ export class SsoComponent extends BaseSsoComponent {
|
||||
logService,
|
||||
userDecryptionOptionsService,
|
||||
configService,
|
||||
masterPasswordService,
|
||||
accountService,
|
||||
);
|
||||
|
||||
environmentService.environment$.pipe(takeUntilDestroyed()).subscribe((env) => {
|
||||
|
||||
@@ -11,6 +11,8 @@ import {
|
||||
UserDecryptionOptionsServiceAbstraction,
|
||||
} from "@bitwarden/auth/common";
|
||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||
import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/auth/abstractions/master-password.service.abstraction";
|
||||
import { SsoLoginServiceAbstraction } from "@bitwarden/common/auth/abstractions/sso-login.service.abstraction";
|
||||
import { TwoFactorService } from "@bitwarden/common/auth/abstractions/two-factor.service";
|
||||
import { TwoFactorProviderType } from "@bitwarden/common/auth/enums/two-factor-provider-type";
|
||||
@@ -58,6 +60,8 @@ export class TwoFactorComponent extends BaseTwoFactorComponent {
|
||||
configService: ConfigService,
|
||||
ssoLoginService: SsoLoginServiceAbstraction,
|
||||
private dialogService: DialogService,
|
||||
masterPasswordService: InternalMasterPasswordServiceAbstraction,
|
||||
accountService: AccountService,
|
||||
@Inject(WINDOW) protected win: Window,
|
||||
private browserMessagingApi: ZonedMessageListenerService,
|
||||
) {
|
||||
@@ -78,6 +82,8 @@ export class TwoFactorComponent extends BaseTwoFactorComponent {
|
||||
userDecryptionOptionsService,
|
||||
ssoLoginService,
|
||||
configService,
|
||||
masterPasswordService,
|
||||
accountService,
|
||||
);
|
||||
super.onSuccessfulLogin = async () => {
|
||||
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
|
||||
|
||||
@@ -8,7 +8,6 @@ import {
|
||||
generateRandomCustomElementName,
|
||||
sendExtensionMessage,
|
||||
setElementStyles,
|
||||
getFromLocalStorage,
|
||||
setupExtensionDisconnectAction,
|
||||
setupAutofillInitDisconnectAction,
|
||||
} from "./index";
|
||||
@@ -124,33 +123,6 @@ describe("setElementStyles", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("getFromLocalStorage", () => {
|
||||
it("returns a promise with the storage object pulled from the extension storage api", async () => {
|
||||
const localStorage: Record<string, any> = {
|
||||
testValue: "test",
|
||||
another: "another",
|
||||
};
|
||||
jest.spyOn(chrome.storage.local, "get").mockImplementation((keys, callback) => {
|
||||
const localStorageObject: Record<string, string> = {};
|
||||
|
||||
if (typeof keys === "string") {
|
||||
localStorageObject[keys] = localStorage[keys];
|
||||
} else if (Array.isArray(keys)) {
|
||||
for (const key of keys) {
|
||||
localStorageObject[key] = localStorage[key];
|
||||
}
|
||||
}
|
||||
|
||||
callback(localStorageObject);
|
||||
});
|
||||
|
||||
const returnValue = await getFromLocalStorage("testValue");
|
||||
|
||||
expect(chrome.storage.local.get).toHaveBeenCalled();
|
||||
expect(returnValue).toEqual({ testValue: "test" });
|
||||
});
|
||||
});
|
||||
|
||||
describe("setupExtensionDisconnectAction", () => {
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks();
|
||||
|
||||
@@ -106,18 +106,6 @@ function setElementStyles(
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get data from local storage based on the keys provided.
|
||||
*
|
||||
* @param keys - String or array of strings of keys to get from local storage
|
||||
* @deprecated Do not call this, use state-relevant services instead
|
||||
*/
|
||||
async function getFromLocalStorage(keys: string | string[]): Promise<Record<string, any>> {
|
||||
return new Promise((resolve) => {
|
||||
chrome.storage.local.get(keys, (storage: Record<string, any>) => resolve(storage));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up a long-lived connection with the extension background
|
||||
* and triggers an onDisconnect event if the extension context
|
||||
@@ -279,7 +267,6 @@ export {
|
||||
buildSvgDomElement,
|
||||
sendExtensionMessage,
|
||||
setElementStyles,
|
||||
getFromLocalStorage,
|
||||
setupExtensionDisconnectAction,
|
||||
setupAutofillInitDisconnectAction,
|
||||
elementIsFillableFormField,
|
||||
|
||||
@@ -10,6 +10,7 @@ import {
|
||||
AuthRequestServiceAbstraction,
|
||||
AuthRequestService,
|
||||
LoginEmailServiceAbstraction,
|
||||
LoginEmailService,
|
||||
} from "@bitwarden/auth/common";
|
||||
import { ApiService as ApiServiceAbstraction } from "@bitwarden/common/abstractions/api.service";
|
||||
import { AuditService as AuditServiceAbstraction } from "@bitwarden/common/abstractions/audit.service";
|
||||
@@ -32,6 +33,7 @@ import { DeviceTrustCryptoServiceAbstraction } from "@bitwarden/common/auth/abst
|
||||
import { DevicesServiceAbstraction } from "@bitwarden/common/auth/abstractions/devices/devices.service.abstraction";
|
||||
import { DevicesApiServiceAbstraction } from "@bitwarden/common/auth/abstractions/devices-api.service.abstraction";
|
||||
import { KeyConnectorService as KeyConnectorServiceAbstraction } from "@bitwarden/common/auth/abstractions/key-connector.service";
|
||||
import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/auth/abstractions/master-password.service.abstraction";
|
||||
import { SsoLoginServiceAbstraction } from "@bitwarden/common/auth/abstractions/sso-login.service.abstraction";
|
||||
import { TokenService as TokenServiceAbstraction } from "@bitwarden/common/auth/abstractions/token.service";
|
||||
import { TwoFactorService as TwoFactorServiceAbstraction } from "@bitwarden/common/auth/abstractions/two-factor.service";
|
||||
@@ -46,6 +48,7 @@ import { DeviceTrustCryptoService } from "@bitwarden/common/auth/services/device
|
||||
import { DevicesServiceImplementation } from "@bitwarden/common/auth/services/devices/devices.service.implementation";
|
||||
import { DevicesApiServiceImplementation } from "@bitwarden/common/auth/services/devices-api.service.implementation";
|
||||
import { KeyConnectorService } from "@bitwarden/common/auth/services/key-connector.service";
|
||||
import { MasterPasswordService } from "@bitwarden/common/auth/services/master-password/master-password.service";
|
||||
import { SsoLoginService } from "@bitwarden/common/auth/services/sso-login.service";
|
||||
import { TokenService } from "@bitwarden/common/auth/services/token.service";
|
||||
import { TwoFactorService } from "@bitwarden/common/auth/services/two-factor.service";
|
||||
@@ -232,7 +235,7 @@ import RuntimeBackground from "./runtime.background";
|
||||
|
||||
export default class MainBackground {
|
||||
messagingService: MessagingServiceAbstraction;
|
||||
storageService: AbstractStorageService & ObservableStorageService;
|
||||
storageService: BrowserLocalStorageService;
|
||||
secureStorageService: AbstractStorageService;
|
||||
memoryStorageService: AbstractMemoryStorageService;
|
||||
memoryStorageForStateProviders: AbstractMemoryStorageService & ObservableStorageService;
|
||||
@@ -242,6 +245,7 @@ export default class MainBackground {
|
||||
keyGenerationService: KeyGenerationServiceAbstraction;
|
||||
cryptoService: CryptoServiceAbstraction;
|
||||
cryptoFunctionService: CryptoFunctionServiceAbstraction;
|
||||
masterPasswordService: InternalMasterPasswordServiceAbstraction;
|
||||
tokenService: TokenServiceAbstraction;
|
||||
appIdService: AppIdServiceAbstraction;
|
||||
apiService: ApiServiceAbstraction;
|
||||
@@ -361,9 +365,10 @@ export default class MainBackground {
|
||||
const logoutCallback = async (expired: boolean, userId?: UserId) =>
|
||||
await this.logout(expired, userId);
|
||||
|
||||
this.messagingService = this.popupOnlyContext
|
||||
? new BrowserMessagingPrivateModeBackgroundService()
|
||||
: new BrowserMessagingService();
|
||||
this.messagingService =
|
||||
this.isPrivateMode && BrowserApi.isManifestVersion(2)
|
||||
? new BrowserMessagingPrivateModeBackgroundService()
|
||||
: new BrowserMessagingService();
|
||||
this.logService = new ConsoleLogService(false);
|
||||
this.cryptoFunctionService = new WebCryptoFunctionService(self);
|
||||
this.keyGenerationService = new KeyGenerationService(this.cryptoFunctionService);
|
||||
@@ -405,13 +410,14 @@ export default class MainBackground {
|
||||
storageServiceProvider,
|
||||
);
|
||||
|
||||
this.encryptService = flagEnabled("multithreadDecryption")
|
||||
? new MultithreadEncryptServiceImplementation(
|
||||
this.cryptoFunctionService,
|
||||
this.logService,
|
||||
true,
|
||||
)
|
||||
: new EncryptServiceImplementation(this.cryptoFunctionService, this.logService, true);
|
||||
this.encryptService =
|
||||
flagEnabled("multithreadDecryption") && BrowserApi.isManifestVersion(2)
|
||||
? new MultithreadEncryptServiceImplementation(
|
||||
this.cryptoFunctionService,
|
||||
this.logService,
|
||||
true,
|
||||
)
|
||||
: new EncryptServiceImplementation(this.cryptoFunctionService, this.logService, true);
|
||||
|
||||
this.singleUserStateProvider = new DefaultSingleUserStateProvider(
|
||||
storageServiceProvider,
|
||||
@@ -480,8 +486,11 @@ export default class MainBackground {
|
||||
|
||||
const themeStateService = new DefaultThemeStateService(this.globalStateProvider);
|
||||
|
||||
this.masterPasswordService = new MasterPasswordService(this.stateProvider);
|
||||
|
||||
this.i18nService = new I18nService(BrowserApi.getUILanguage(), this.globalStateProvider);
|
||||
this.cryptoService = new BrowserCryptoService(
|
||||
this.masterPasswordService,
|
||||
this.keyGenerationService,
|
||||
this.cryptoFunctionService,
|
||||
this.encryptService,
|
||||
@@ -508,7 +517,7 @@ export default class MainBackground {
|
||||
this.apiService,
|
||||
this.fileUploadService,
|
||||
);
|
||||
this.searchService = new SearchService(this.logService, this.i18nService);
|
||||
this.searchService = new SearchService(this.logService, this.i18nService, this.stateProvider);
|
||||
|
||||
this.collectionService = new CollectionService(
|
||||
this.cryptoService,
|
||||
@@ -525,6 +534,8 @@ export default class MainBackground {
|
||||
this.badgeSettingsService = new BadgeSettingsService(this.stateProvider);
|
||||
this.policyApiService = new PolicyApiService(this.policyService, this.apiService);
|
||||
this.keyConnectorService = new KeyConnectorService(
|
||||
this.accountService,
|
||||
this.masterPasswordService,
|
||||
this.cryptoService,
|
||||
this.apiService,
|
||||
this.tokenService,
|
||||
@@ -550,10 +561,13 @@ export default class MainBackground {
|
||||
const backgroundMessagingService = new (class extends MessagingServiceAbstraction {
|
||||
// AuthService should send the messages to the background not popup.
|
||||
send = (subscriber: string, arg: any = {}) => {
|
||||
if (BrowserApi.isManifestVersion(3)) {
|
||||
that.messagingService.send(subscriber, arg);
|
||||
return;
|
||||
}
|
||||
|
||||
const message = Object.assign({}, { command: subscriber }, arg);
|
||||
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
that.runtimeBackground.processMessage(message, that as any);
|
||||
void that.runtimeBackground.processMessage(message, that as any);
|
||||
};
|
||||
})();
|
||||
|
||||
@@ -578,9 +592,10 @@ export default class MainBackground {
|
||||
|
||||
this.authRequestService = new AuthRequestService(
|
||||
this.appIdService,
|
||||
this.accountService,
|
||||
this.masterPasswordService,
|
||||
this.cryptoService,
|
||||
this.apiService,
|
||||
this.stateService,
|
||||
);
|
||||
|
||||
this.authService = new AuthService(
|
||||
@@ -596,7 +611,11 @@ export default class MainBackground {
|
||||
this.stateProvider,
|
||||
);
|
||||
|
||||
this.loginEmailService = new LoginEmailService(this.stateProvider);
|
||||
|
||||
this.loginStrategyService = new LoginStrategyService(
|
||||
this.accountService,
|
||||
this.masterPasswordService,
|
||||
this.cryptoService,
|
||||
this.apiService,
|
||||
this.tokenService,
|
||||
@@ -672,6 +691,8 @@ export default class MainBackground {
|
||||
this.userVerificationService = new UserVerificationService(
|
||||
this.stateService,
|
||||
this.cryptoService,
|
||||
this.accountService,
|
||||
this.masterPasswordService,
|
||||
this.i18nService,
|
||||
this.userVerificationApiService,
|
||||
this.userDecryptionOptionsService,
|
||||
@@ -694,6 +715,8 @@ export default class MainBackground {
|
||||
this.vaultSettingsService = new VaultSettingsService(this.stateProvider);
|
||||
|
||||
this.vaultTimeoutService = new VaultTimeoutService(
|
||||
this.accountService,
|
||||
this.masterPasswordService,
|
||||
this.cipherService,
|
||||
this.folderService,
|
||||
this.collectionService,
|
||||
@@ -729,6 +752,8 @@ export default class MainBackground {
|
||||
this.providerService = new ProviderService(this.stateProvider);
|
||||
|
||||
this.syncService = new SyncService(
|
||||
this.masterPasswordService,
|
||||
this.accountService,
|
||||
this.apiService,
|
||||
this.domainSettingsService,
|
||||
this.folderService,
|
||||
@@ -878,6 +903,8 @@ export default class MainBackground {
|
||||
this.fido2Service,
|
||||
);
|
||||
this.nativeMessagingBackground = new NativeMessagingBackground(
|
||||
this.accountService,
|
||||
this.masterPasswordService,
|
||||
this.cryptoService,
|
||||
this.cryptoFunctionService,
|
||||
this.runtimeBackground,
|
||||
@@ -1108,7 +1135,7 @@ export default class MainBackground {
|
||||
|
||||
const status = await this.authService.getAuthStatus(userId);
|
||||
const forcePasswordReset =
|
||||
(await this.stateService.getForceSetPasswordReason({ userId: userId })) !=
|
||||
(await firstValueFrom(this.masterPasswordService.forceSetPasswordReason$(userId))) !=
|
||||
ForceSetPasswordReason.None;
|
||||
|
||||
await this.systemService.clearPendingClipboard();
|
||||
@@ -1159,10 +1186,10 @@ export default class MainBackground {
|
||||
const newActiveUser = await this.stateService.clean({ userId: userId });
|
||||
|
||||
if (userId == null || userId === currentUserId) {
|
||||
this.searchService.clearIndex();
|
||||
await this.searchService.clearIndex();
|
||||
}
|
||||
|
||||
await this.stateEventRunnerService.handleEvent("logout", currentUserId as UserId);
|
||||
await this.stateEventRunnerService.handleEvent("logout", userId);
|
||||
|
||||
if (newActiveUser != null) {
|
||||
// we have a new active user, do not continue tearing down application
|
||||
@@ -1237,18 +1264,8 @@ export default class MainBackground {
|
||||
return;
|
||||
}
|
||||
|
||||
const getStorage = (): Promise<any> =>
|
||||
new Promise((resolve) => {
|
||||
chrome.storage.local.get(null, (o: any) => resolve(o));
|
||||
});
|
||||
|
||||
const clearStorage = (): Promise<void> =>
|
||||
new Promise((resolve) => {
|
||||
chrome.storage.local.clear(() => resolve());
|
||||
});
|
||||
|
||||
const storage = await getStorage();
|
||||
await clearStorage();
|
||||
const storage = await this.storageService.getAll();
|
||||
await this.storageService.clear();
|
||||
|
||||
for (const key in storage) {
|
||||
// eslint-disable-next-line
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import { firstValueFrom } from "rxjs";
|
||||
|
||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||
import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service";
|
||||
import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/auth/abstractions/master-password.service.abstraction";
|
||||
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
|
||||
import { AppIdService } from "@bitwarden/common/platform/abstractions/app-id.service";
|
||||
import { CryptoFunctionService } from "@bitwarden/common/platform/abstractions/crypto-function.service";
|
||||
@@ -71,6 +73,8 @@ export class NativeMessagingBackground {
|
||||
private validatingFingerprint: boolean;
|
||||
|
||||
constructor(
|
||||
private accountService: AccountService,
|
||||
private masterPasswordService: InternalMasterPasswordServiceAbstraction,
|
||||
private cryptoService: CryptoService,
|
||||
private cryptoFunctionService: CryptoFunctionService,
|
||||
private runtimeBackground: RuntimeBackground,
|
||||
@@ -336,10 +340,14 @@ export class NativeMessagingBackground {
|
||||
) as UserKey;
|
||||
await this.cryptoService.setUserKey(userKey);
|
||||
} else if (message.keyB64) {
|
||||
const userId = (await firstValueFrom(this.accountService.activeAccount$))?.id;
|
||||
// Backwards compatibility to support cases in which the user hasn't updated their desktop app
|
||||
// TODO: Remove after 2023.10 release (https://bitwarden.atlassian.net/browse/PM-3472)
|
||||
let encUserKey = await this.stateService.getEncryptedCryptoSymmetricKey();
|
||||
encUserKey ||= await this.stateService.getMasterKeyEncryptedUserKey();
|
||||
const encUserKeyPrim = await this.stateService.getEncryptedCryptoSymmetricKey();
|
||||
const encUserKey =
|
||||
encUserKeyPrim != null
|
||||
? new EncString(encUserKeyPrim)
|
||||
: await this.masterPasswordService.getMasterKeyEncryptedUserKey(userId);
|
||||
if (!encUserKey) {
|
||||
throw new Error("No encrypted user key found");
|
||||
}
|
||||
@@ -348,9 +356,9 @@ export class NativeMessagingBackground {
|
||||
) as MasterKey;
|
||||
const userKey = await this.cryptoService.decryptUserKeyWithMasterKey(
|
||||
masterKey,
|
||||
new EncString(encUserKey),
|
||||
encUserKey,
|
||||
);
|
||||
await this.cryptoService.setMasterKey(masterKey);
|
||||
await this.masterPasswordService.setMasterKey(masterKey, userId);
|
||||
await this.cryptoService.setUserKey(userKey);
|
||||
} else {
|
||||
throw new Error("No key received");
|
||||
|
||||
@@ -14,12 +14,17 @@ import {
|
||||
logServiceFactory,
|
||||
LogServiceInitOptions,
|
||||
} from "../../platform/background/service-factories/log-service.factory";
|
||||
import {
|
||||
stateProviderFactory,
|
||||
StateProviderInitOptions,
|
||||
} from "../../platform/background/service-factories/state-provider.factory";
|
||||
|
||||
type SearchServiceFactoryOptions = FactoryOptions;
|
||||
|
||||
export type SearchServiceInitOptions = SearchServiceFactoryOptions &
|
||||
LogServiceInitOptions &
|
||||
I18nServiceInitOptions;
|
||||
I18nServiceInitOptions &
|
||||
StateProviderInitOptions;
|
||||
|
||||
export function searchServiceFactory(
|
||||
cache: { searchService?: AbstractSearchService } & CachedServices,
|
||||
@@ -33,6 +38,7 @@ export function searchServiceFactory(
|
||||
new SearchService(
|
||||
await logServiceFactory(cache, opts),
|
||||
await i18nServiceFactory(cache, opts),
|
||||
await stateProviderFactory(cache, opts),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,9 +1,17 @@
|
||||
import { VaultTimeoutService as AbstractVaultTimeoutService } from "@bitwarden/common/abstractions/vault-timeout/vault-timeout.service";
|
||||
|
||||
import {
|
||||
accountServiceFactory,
|
||||
AccountServiceInitOptions,
|
||||
} from "../../auth/background/service-factories/account-service.factory";
|
||||
import {
|
||||
authServiceFactory,
|
||||
AuthServiceInitOptions,
|
||||
} from "../../auth/background/service-factories/auth-service.factory";
|
||||
import {
|
||||
internalMasterPasswordServiceFactory,
|
||||
MasterPasswordServiceInitOptions,
|
||||
} from "../../auth/background/service-factories/master-password-service.factory";
|
||||
import {
|
||||
CryptoServiceInitOptions,
|
||||
cryptoServiceFactory,
|
||||
@@ -57,6 +65,8 @@ type VaultTimeoutServiceFactoryOptions = FactoryOptions & {
|
||||
};
|
||||
|
||||
export type VaultTimeoutServiceInitOptions = VaultTimeoutServiceFactoryOptions &
|
||||
AccountServiceInitOptions &
|
||||
MasterPasswordServiceInitOptions &
|
||||
CipherServiceInitOptions &
|
||||
FolderServiceInitOptions &
|
||||
CollectionServiceInitOptions &
|
||||
@@ -79,6 +89,8 @@ export function vaultTimeoutServiceFactory(
|
||||
opts,
|
||||
async () =>
|
||||
new VaultTimeoutService(
|
||||
await accountServiceFactory(cache, opts),
|
||||
await internalMasterPasswordServiceFactory(cache, opts),
|
||||
await cipherServiceFactory(cache, opts),
|
||||
await folderServiceFactory(cache, opts),
|
||||
await collectionServiceFactory(cache, opts),
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"manifest_version": 2,
|
||||
"name": "__MSG_extName__",
|
||||
"short_name": "__MSG_appName__",
|
||||
"version": "2024.3.1",
|
||||
"version": "2024.4.1",
|
||||
"description": "__MSG_extDesc__",
|
||||
"default_locale": "en",
|
||||
"author": "Bitwarden Inc.",
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"minimum_chrome_version": "102.0",
|
||||
"name": "__MSG_extName__",
|
||||
"short_name": "__MSG_appName__",
|
||||
"version": "2024.3.1",
|
||||
"version": "2024.4.1",
|
||||
"description": "__MSG_extDesc__",
|
||||
"default_locale": "en",
|
||||
"author": "Bitwarden Inc.",
|
||||
|
||||
@@ -4,6 +4,10 @@ import {
|
||||
AccountServiceInitOptions,
|
||||
accountServiceFactory,
|
||||
} from "../../../auth/background/service-factories/account-service.factory";
|
||||
import {
|
||||
internalMasterPasswordServiceFactory,
|
||||
MasterPasswordServiceInitOptions,
|
||||
} from "../../../auth/background/service-factories/master-password-service.factory";
|
||||
import {
|
||||
StateServiceInitOptions,
|
||||
stateServiceFactory,
|
||||
@@ -34,6 +38,7 @@ import { StateProviderInitOptions, stateProviderFactory } from "./state-provider
|
||||
type CryptoServiceFactoryOptions = FactoryOptions;
|
||||
|
||||
export type CryptoServiceInitOptions = CryptoServiceFactoryOptions &
|
||||
MasterPasswordServiceInitOptions &
|
||||
KeyGenerationServiceInitOptions &
|
||||
CryptoFunctionServiceInitOptions &
|
||||
EncryptServiceInitOptions &
|
||||
@@ -53,6 +58,7 @@ export function cryptoServiceFactory(
|
||||
opts,
|
||||
async () =>
|
||||
new BrowserCryptoService(
|
||||
await internalMasterPasswordServiceFactory(cache, opts),
|
||||
await keyGenerationServiceFactory(cache, opts),
|
||||
await cryptoFunctionServiceFactory(cache, opts),
|
||||
await encryptServiceFactory(cache, opts),
|
||||
|
||||
@@ -93,6 +93,10 @@ export class SessionSyncer {
|
||||
}
|
||||
|
||||
async update(serializedValue: any) {
|
||||
if (!serializedValue) {
|
||||
return;
|
||||
}
|
||||
|
||||
const unBuiltValue = JSON.parse(serializedValue);
|
||||
if (!BrowserApi.isManifestVersion(3) && BrowserApi.isBackgroundPage(self)) {
|
||||
await this.memoryStorageService.save(this.metaData.sessionKey, serializedValue);
|
||||
@@ -104,6 +108,10 @@ export class SessionSyncer {
|
||||
}
|
||||
|
||||
private async updateSession(value: any) {
|
||||
if (!value) {
|
||||
return;
|
||||
}
|
||||
|
||||
const serializedValue = JSON.stringify(value);
|
||||
if (BrowserApi.isManifestVersion(3) || BrowserApi.isBackgroundPage(self)) {
|
||||
await this.memoryStorageService.save(this.metaData.sessionKey, serializedValue);
|
||||
|
||||
@@ -10,6 +10,8 @@ import { fromChromeEvent } from "../../browser/from-chrome-event";
|
||||
|
||||
export const serializationIndicator = "__json__";
|
||||
|
||||
type serializedObject = { [serializationIndicator]: true; value: string };
|
||||
|
||||
export const objToStore = (obj: any) => {
|
||||
if (obj == null) {
|
||||
return null;
|
||||
@@ -61,11 +63,7 @@ export default abstract class AbstractChromeStorageService
|
||||
return new Promise((resolve) => {
|
||||
this.chromeStorageApi.get(key, (obj: any) => {
|
||||
if (obj != null && obj[key] != null) {
|
||||
let value = obj[key];
|
||||
if (value[serializationIndicator] && typeof value.value === "string") {
|
||||
value = JSON.parse(value.value);
|
||||
}
|
||||
resolve(value as T);
|
||||
resolve(this.processGetObject(obj[key]));
|
||||
return;
|
||||
}
|
||||
resolve(null);
|
||||
@@ -95,4 +93,22 @@ export default abstract class AbstractChromeStorageService
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/** Backwards compatible resolution of retrieved object with new serialized storage */
|
||||
protected processGetObject<T>(obj: T | serializedObject): T | null {
|
||||
if (this.isSerialized(obj)) {
|
||||
obj = JSON.parse(obj.value);
|
||||
}
|
||||
return obj as T;
|
||||
}
|
||||
|
||||
/** Type guard for whether an object is tagged as serialized */
|
||||
protected isSerialized<T>(value: T | serializedObject): value is serializedObject {
|
||||
const asSerialized = value as serializedObject;
|
||||
return (
|
||||
asSerialized != null &&
|
||||
asSerialized[serializationIndicator] &&
|
||||
typeof asSerialized.value === "string"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,6 +66,7 @@ describe("ChromeStorageApiService", () => {
|
||||
|
||||
describe("get", () => {
|
||||
let getMock: jest.Mock;
|
||||
const key = "key";
|
||||
|
||||
beforeEach(() => {
|
||||
// setup get
|
||||
@@ -76,7 +77,6 @@ describe("ChromeStorageApiService", () => {
|
||||
});
|
||||
|
||||
it("returns a stored value when it is serialized", async () => {
|
||||
const key = "key";
|
||||
const value = { key: "value" };
|
||||
store[key] = objToStore(value);
|
||||
const result = await service.get(key);
|
||||
@@ -84,7 +84,6 @@ describe("ChromeStorageApiService", () => {
|
||||
});
|
||||
|
||||
it("returns a stored value when it is not serialized", async () => {
|
||||
const key = "key";
|
||||
const value = "value";
|
||||
store[key] = value;
|
||||
const result = await service.get(key);
|
||||
@@ -95,5 +94,12 @@ describe("ChromeStorageApiService", () => {
|
||||
const result = await service.get("key");
|
||||
expect(result).toBeNull();
|
||||
});
|
||||
|
||||
it("returns null when the stored object is null", async () => {
|
||||
store[key] = null;
|
||||
|
||||
const result = await service.get(key);
|
||||
expect(result).toBeNull();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { firstValueFrom } from "rxjs";
|
||||
|
||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||
import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/auth/abstractions/master-password.service.abstraction";
|
||||
import { CryptoFunctionService } from "@bitwarden/common/platform/abstractions/crypto-function.service";
|
||||
import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service";
|
||||
import { KeyGenerationService } from "@bitwarden/common/platform/abstractions/key-generation.service";
|
||||
@@ -17,6 +18,7 @@ import { UserKey } from "@bitwarden/common/types/key";
|
||||
|
||||
export class BrowserCryptoService extends CryptoService {
|
||||
constructor(
|
||||
masterPasswordService: InternalMasterPasswordServiceAbstraction,
|
||||
keyGenerationService: KeyGenerationService,
|
||||
cryptoFunctionService: CryptoFunctionService,
|
||||
encryptService: EncryptService,
|
||||
@@ -28,6 +30,7 @@ export class BrowserCryptoService extends CryptoService {
|
||||
private biometricStateService: BiometricStateService,
|
||||
) {
|
||||
super(
|
||||
masterPasswordService,
|
||||
keyGenerationService,
|
||||
cryptoFunctionService,
|
||||
encryptService,
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
import { objToStore } from "./abstractions/abstract-chrome-storage-api.service";
|
||||
import BrowserLocalStorageService from "./browser-local-storage.service";
|
||||
|
||||
describe("BrowserLocalStorageService", () => {
|
||||
let service: BrowserLocalStorageService;
|
||||
let store: Record<any, any>;
|
||||
|
||||
beforeEach(() => {
|
||||
store = {};
|
||||
|
||||
service = new BrowserLocalStorageService();
|
||||
});
|
||||
|
||||
describe("clear", () => {
|
||||
let clearMock: jest.Mock;
|
||||
|
||||
beforeEach(() => {
|
||||
clearMock = chrome.storage.local.clear as jest.Mock;
|
||||
});
|
||||
|
||||
it("uses the api to clear", async () => {
|
||||
await service.clear();
|
||||
|
||||
expect(clearMock).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe("getAll", () => {
|
||||
let getMock: jest.Mock;
|
||||
|
||||
beforeEach(() => {
|
||||
// setup get
|
||||
getMock = chrome.storage.local.get as jest.Mock;
|
||||
getMock.mockImplementation((key, callback) => {
|
||||
if (key == null) {
|
||||
callback(store);
|
||||
} else {
|
||||
callback({ [key]: store[key] });
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it("returns all values", async () => {
|
||||
store["key1"] = "string";
|
||||
store["key2"] = 0;
|
||||
const result = await service.getAll();
|
||||
|
||||
expect(result).toEqual(store);
|
||||
});
|
||||
|
||||
it("handles empty stores", async () => {
|
||||
const result = await service.getAll();
|
||||
|
||||
expect(result).toEqual({});
|
||||
});
|
||||
|
||||
it("handles stores with null values", async () => {
|
||||
store["key"] = null;
|
||||
|
||||
const result = await service.getAll();
|
||||
expect(result).toEqual(store);
|
||||
});
|
||||
|
||||
it("handles values processed for storage", async () => {
|
||||
const obj = { test: 2 };
|
||||
const key = "key";
|
||||
store[key] = objToStore(obj);
|
||||
|
||||
const result = await service.getAll();
|
||||
|
||||
expect(result).toEqual({
|
||||
[key]: obj,
|
||||
});
|
||||
});
|
||||
|
||||
// This is a test of backwards compatibility before local storage was serialized.
|
||||
it("handles values that were stored without processing for storage", async () => {
|
||||
const obj = { test: 2 };
|
||||
const key = "key";
|
||||
store[key] = obj;
|
||||
|
||||
const result = await service.getAll();
|
||||
|
||||
expect(result).toEqual({
|
||||
[key]: obj,
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -4,4 +4,32 @@ export default class BrowserLocalStorageService extends AbstractChromeStorageSer
|
||||
constructor() {
|
||||
super(chrome.storage.local);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears local storage
|
||||
*/
|
||||
async clear() {
|
||||
await chrome.storage.local.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves all objects stored in local storage.
|
||||
*
|
||||
* @remarks This method processes values prior to resolving, do not use `chrome.storage.local` directly
|
||||
* @returns Promise resolving to keyed object of all stored data
|
||||
*/
|
||||
async getAll(): Promise<Record<string, unknown>> {
|
||||
return new Promise((resolve) => {
|
||||
this.chromeStorageApi.get(null, (allStorage) => {
|
||||
const resolved = Object.entries(allStorage).reduce(
|
||||
(agg, [key, value]) => {
|
||||
agg[key] = this.processGetObject(value);
|
||||
return agg;
|
||||
},
|
||||
{} as Record<string, unknown>,
|
||||
);
|
||||
resolve(resolved);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,17 +1,14 @@
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||
import { StateProvider } from "@bitwarden/common/platform/state";
|
||||
import { SearchService } from "@bitwarden/common/services/search.service";
|
||||
|
||||
export class PopupSearchService extends SearchService {
|
||||
constructor(
|
||||
private mainSearchService: SearchService,
|
||||
logService: LogService,
|
||||
i18nService: I18nService,
|
||||
) {
|
||||
super(logService, i18nService);
|
||||
constructor(logService: LogService, i18nService: I18nService, stateProvider: StateProvider) {
|
||||
super(logService, i18nService, stateProvider);
|
||||
}
|
||||
|
||||
clearIndex() {
|
||||
clearIndex(): Promise<void> {
|
||||
throw new Error("Not available.");
|
||||
}
|
||||
|
||||
@@ -19,7 +16,7 @@ export class PopupSearchService extends SearchService {
|
||||
throw new Error("Not available.");
|
||||
}
|
||||
|
||||
getIndexForSearch() {
|
||||
return this.mainSearchService.getIndexForSearch();
|
||||
async getIndexForSearch() {
|
||||
return await super.getIndexForSearch();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,6 +62,7 @@ import { StateService as BaseStateServiceAbstraction } from "@bitwarden/common/p
|
||||
import {
|
||||
AbstractMemoryStorageService,
|
||||
AbstractStorageService,
|
||||
ObservableStorageService,
|
||||
} from "@bitwarden/common/platform/abstractions/storage.service";
|
||||
import { StateFactory } from "@bitwarden/common/platform/factories/state-factory";
|
||||
import { GlobalState } from "@bitwarden/common/platform/models/domain/global-state";
|
||||
@@ -74,17 +75,15 @@ import {
|
||||
GlobalStateProvider,
|
||||
StateProvider,
|
||||
} from "@bitwarden/common/platform/state";
|
||||
import { SearchService } from "@bitwarden/common/services/search.service";
|
||||
import { PasswordGenerationServiceAbstraction } from "@bitwarden/common/tools/generator/password";
|
||||
import { UsernameGenerationServiceAbstraction } from "@bitwarden/common/tools/generator/username";
|
||||
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
|
||||
import { CollectionService } from "@bitwarden/common/vault/abstractions/collection.service";
|
||||
import { CipherFileUploadService } from "@bitwarden/common/vault/abstractions/file-upload/cipher-file-upload.service";
|
||||
import { FolderService as FolderServiceAbstraction } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction";
|
||||
import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction";
|
||||
import { TotpService } from "@bitwarden/common/vault/abstractions/totp.service";
|
||||
import { TotpService as TotpServiceAbstraction } from "@bitwarden/common/vault/abstractions/totp.service";
|
||||
import { TotpService } from "@bitwarden/common/vault/services/totp.service";
|
||||
import { DialogService } from "@bitwarden/components";
|
||||
import { VaultExportServiceAbstraction } from "@bitwarden/vault-export-core";
|
||||
|
||||
import { UnauthGuardService } from "../../auth/popup/services";
|
||||
import { AutofillService as AutofillServiceAbstraction } from "../../autofill/services/abstractions/autofill.service";
|
||||
@@ -159,7 +158,7 @@ const safeProviders: SafeProvider[] = [
|
||||
safeProvider({
|
||||
provide: MessagingService,
|
||||
useFactory: () => {
|
||||
return needsBackgroundInit
|
||||
return needsBackgroundInit && BrowserApi.isManifestVersion(2)
|
||||
? new BrowserMessagingPrivateModePopupService()
|
||||
: new BrowserMessagingService();
|
||||
},
|
||||
@@ -187,19 +186,8 @@ const safeProviders: SafeProvider[] = [
|
||||
}),
|
||||
safeProvider({
|
||||
provide: SearchServiceAbstraction,
|
||||
useFactory: (logService: LogService, i18nService: I18nServiceAbstraction) => {
|
||||
return new PopupSearchService(
|
||||
getBgService<SearchService>("searchService")(),
|
||||
logService,
|
||||
i18nService,
|
||||
);
|
||||
},
|
||||
deps: [LogService, I18nServiceAbstraction],
|
||||
}),
|
||||
safeProvider({
|
||||
provide: CipherFileUploadService,
|
||||
useFactory: getBgService<CipherFileUploadService>("cipherFileUploadService"),
|
||||
deps: [],
|
||||
useClass: PopupSearchService,
|
||||
deps: [LogService, I18nServiceAbstraction, StateProvider],
|
||||
}),
|
||||
safeProvider({
|
||||
provide: CipherService,
|
||||
@@ -231,11 +219,6 @@ const safeProviders: SafeProvider[] = [
|
||||
useClass: BrowserEnvironmentService,
|
||||
deps: [LogService, StateProvider, AccountServiceAbstraction],
|
||||
}),
|
||||
safeProvider({
|
||||
provide: TotpService,
|
||||
useFactory: getBgService<TotpService>("totpService"),
|
||||
deps: [],
|
||||
}),
|
||||
safeProvider({
|
||||
provide: I18nServiceAbstraction,
|
||||
useFactory: (globalStateProvider: GlobalStateProvider) => {
|
||||
@@ -252,6 +235,11 @@ const safeProviders: SafeProvider[] = [
|
||||
},
|
||||
deps: [EncryptService],
|
||||
}),
|
||||
safeProvider({
|
||||
provide: TotpServiceAbstraction,
|
||||
useClass: TotpService,
|
||||
deps: [CryptoFunctionService, LogService],
|
||||
}),
|
||||
safeProvider({
|
||||
provide: AuthRequestServiceAbstraction,
|
||||
useFactory: getBgService<AuthRequestServiceAbstraction>("authRequestService"),
|
||||
@@ -325,7 +313,7 @@ const safeProviders: SafeProvider[] = [
|
||||
deps: [
|
||||
CipherService,
|
||||
AutofillSettingsServiceAbstraction,
|
||||
TotpService,
|
||||
TotpServiceAbstraction,
|
||||
EventCollectionServiceAbstraction,
|
||||
LogService,
|
||||
DomainSettingsService,
|
||||
@@ -333,11 +321,6 @@ const safeProviders: SafeProvider[] = [
|
||||
BillingAccountProfileStateService,
|
||||
],
|
||||
}),
|
||||
safeProvider({
|
||||
provide: VaultExportServiceAbstraction,
|
||||
useFactory: getBgService<VaultExportServiceAbstraction>("exportService"),
|
||||
deps: [],
|
||||
}),
|
||||
safeProvider({
|
||||
provide: KeyConnectorService,
|
||||
useFactory: getBgService<KeyConnectorService>("keyConnectorService"),
|
||||
@@ -387,7 +370,15 @@ const safeProviders: SafeProvider[] = [
|
||||
}),
|
||||
safeProvider({
|
||||
provide: OBSERVABLE_MEMORY_STORAGE,
|
||||
useClass: ForegroundMemoryStorageService,
|
||||
useFactory: () => {
|
||||
if (BrowserApi.isManifestVersion(2)) {
|
||||
return new ForegroundMemoryStorageService();
|
||||
}
|
||||
|
||||
return getBgService<AbstractStorageService & ObservableStorageService>(
|
||||
"memoryStorageForStateProviders",
|
||||
)();
|
||||
},
|
||||
deps: [],
|
||||
}),
|
||||
safeProvider({
|
||||
|
||||
@@ -171,9 +171,7 @@ export class SendGroupingsComponent extends BaseSendComponent {
|
||||
}
|
||||
|
||||
showSearching() {
|
||||
return (
|
||||
this.hasSearched || (!this.searchPending && this.searchService.isSearchable(this.searchText))
|
||||
);
|
||||
return this.hasSearched || (!this.searchPending && this.isSearchable);
|
||||
}
|
||||
|
||||
private calculateTypeCounts() {
|
||||
|
||||
@@ -6,7 +6,7 @@ import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abs
|
||||
import { EventType } from "@bitwarden/common/enums";
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||
import { TotpService } from "@bitwarden/common/vault/abstractions/totp.service";
|
||||
import { TotpService as TotpServiceAbstraction } from "@bitwarden/common/vault/abstractions/totp.service";
|
||||
import { CipherType } from "@bitwarden/common/vault/enums";
|
||||
import { CipherRepromptType } from "@bitwarden/common/vault/enums/cipher-reprompt-type";
|
||||
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
|
||||
@@ -31,7 +31,7 @@ export class ActionButtonsComponent implements OnInit, OnDestroy {
|
||||
private i18nService: I18nService,
|
||||
private platformUtilsService: PlatformUtilsService,
|
||||
private eventCollectionService: EventCollectionService,
|
||||
private totpService: TotpService,
|
||||
private totpService: TotpServiceAbstraction,
|
||||
private passwordRepromptService: PasswordRepromptService,
|
||||
private billingAccountProfileStateService: BillingAccountProfileStateService,
|
||||
) {}
|
||||
|
||||
@@ -311,7 +311,7 @@ export class Fido2Component implements OnInit, OnDestroy {
|
||||
}
|
||||
|
||||
protected async search() {
|
||||
this.hasSearched = this.searchService.isSearchable(this.searchText);
|
||||
this.hasSearched = await this.searchService.isSearchable(this.searchText);
|
||||
this.searchPending = true;
|
||||
if (this.hasSearched) {
|
||||
this.displayedCiphers = await this.searchService.searchCiphers(
|
||||
|
||||
@@ -36,19 +36,32 @@
|
||||
</div>
|
||||
<ng-container *ngIf="loaded">
|
||||
<app-vault-select (onVaultSelectionChanged)="load()"></app-vault-select>
|
||||
<app-callout *ngIf="showHowToAutofill" type="info" title="{{ 'howToAutofill' | i18n }}">
|
||||
<p>{{ autofillCalloutText }}</p>
|
||||
<app-callout
|
||||
*ngIf="
|
||||
(unassignedItemsBannerEnabled$ | async) &&
|
||||
(unassignedItemsBannerService.showBanner$ | async)
|
||||
"
|
||||
type="info"
|
||||
>
|
||||
<p>
|
||||
{{ "unassignedItemsBanner" | i18n }}
|
||||
<a
|
||||
href="https://bitwarden.com/help/unassigned-vault-items-moved-to-admin-console"
|
||||
bitLink
|
||||
linkType="contrast"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>{{ "learnMore" | i18n }}</a
|
||||
>
|
||||
</p>
|
||||
<button
|
||||
type="button"
|
||||
class="btn primary callout-half"
|
||||
appStopClick
|
||||
(click)="dismissCallout()"
|
||||
(click)="unassignedItemsBannerService.hideBanner()"
|
||||
>
|
||||
{{ "gotIt" | i18n }}
|
||||
</button>
|
||||
<button type="button" class="btn callout-half" appStopClick (click)="goToSettings()">
|
||||
{{ "autofillSettings" | i18n }}
|
||||
</button>
|
||||
</app-callout>
|
||||
<div class="box list" *ngIf="loginCiphers">
|
||||
<h2 class="box-header">
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
import { ChangeDetectorRef, Component, NgZone, OnDestroy, OnInit } from "@angular/core";
|
||||
import { Router } from "@angular/router";
|
||||
import { Subject, firstValueFrom } from "rxjs";
|
||||
import { debounceTime, takeUntil } from "rxjs/operators";
|
||||
import { Subject, firstValueFrom, from } from "rxjs";
|
||||
import { debounceTime, switchMap, takeUntil } from "rxjs/operators";
|
||||
|
||||
import { UnassignedItemsBannerService } from "@bitwarden/angular/services/unassigned-items-banner.service";
|
||||
import { SearchService } from "@bitwarden/common/abstractions/search.service";
|
||||
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
|
||||
import { AutofillOverlayVisibility } from "@bitwarden/common/autofill/constants";
|
||||
import { AutofillSettingsServiceAbstraction } from "@bitwarden/common/autofill/services/autofill-settings.service";
|
||||
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
|
||||
import { BroadcasterService } from "@bitwarden/common/platform/abstractions/broadcaster.service";
|
||||
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
||||
@@ -53,6 +56,11 @@ export class CurrentTabComponent implements OnInit, OnDestroy {
|
||||
private totpTimeout: number;
|
||||
private loadedTimeout: number;
|
||||
private searchTimeout: number;
|
||||
private initPageDetailsTimeout: number;
|
||||
|
||||
protected unassignedItemsBannerEnabled$ = this.configService.getFeatureFlag$(
|
||||
FeatureFlag.UnassignedItemsBanner,
|
||||
);
|
||||
|
||||
constructor(
|
||||
private platformUtilsService: PlatformUtilsService,
|
||||
@@ -70,6 +78,8 @@ export class CurrentTabComponent implements OnInit, OnDestroy {
|
||||
private organizationService: OrganizationService,
|
||||
private vaultFilterService: VaultFilterService,
|
||||
private vaultSettingsService: VaultSettingsService,
|
||||
private configService: ConfigService,
|
||||
protected unassignedItemsBannerService: UnassignedItemsBannerService,
|
||||
) {}
|
||||
|
||||
async ngOnInit() {
|
||||
@@ -120,8 +130,14 @@ export class CurrentTabComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
|
||||
this.search$
|
||||
.pipe(debounceTime(500), takeUntil(this.destroy$))
|
||||
.subscribe(() => this.searchVault());
|
||||
.pipe(
|
||||
debounceTime(500),
|
||||
switchMap(() => {
|
||||
return from(this.searchVault());
|
||||
}),
|
||||
takeUntil(this.destroy$),
|
||||
)
|
||||
.subscribe();
|
||||
|
||||
const autofillOnPageLoadOrgPolicy = await firstValueFrom(
|
||||
this.autofillSettingsService.activateAutofillOnPageLoadFromPolicy$,
|
||||
@@ -232,14 +248,12 @@ export class CurrentTabComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
}
|
||||
|
||||
searchVault() {
|
||||
if (!this.searchService.isSearchable(this.searchText)) {
|
||||
async searchVault() {
|
||||
if (!(await this.searchService.isSearchable(this.searchText))) {
|
||||
return;
|
||||
}
|
||||
|
||||
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
this.router.navigate(["/tabs/vault"], { queryParams: { searchText: this.searchText } });
|
||||
await this.router.navigate(["/tabs/vault"], { queryParams: { searchText: this.searchText } });
|
||||
}
|
||||
|
||||
closeOnEsc(e: KeyboardEvent) {
|
||||
@@ -303,18 +317,13 @@ export class CurrentTabComponent implements OnInit, OnDestroy {
|
||||
});
|
||||
|
||||
if (this.loginCiphers.length) {
|
||||
void BrowserApi.tabSendMessage(this.tab, {
|
||||
command: "collectPageDetails",
|
||||
tab: this.tab,
|
||||
sender: BroadcasterSubscriptionId,
|
||||
});
|
||||
|
||||
this.loginCiphers = this.loginCiphers.sort((a, b) =>
|
||||
this.cipherService.sortCiphersByLastUsedThenName(a, b),
|
||||
);
|
||||
}
|
||||
|
||||
this.isLoading = this.loaded = true;
|
||||
this.collectTabPageDetails();
|
||||
}
|
||||
|
||||
async goToSettings() {
|
||||
@@ -352,4 +361,19 @@ export class CurrentTabComponent implements OnInit, OnDestroy {
|
||||
this.autofillCalloutText = this.i18nService.t("autofillSelectInfoWithoutCommand");
|
||||
}
|
||||
}
|
||||
|
||||
private collectTabPageDetails() {
|
||||
void BrowserApi.tabSendMessage(this.tab, {
|
||||
command: "collectPageDetails",
|
||||
tab: this.tab,
|
||||
sender: BroadcasterSubscriptionId,
|
||||
});
|
||||
|
||||
window.clearTimeout(this.initPageDetailsTimeout);
|
||||
this.initPageDetailsTimeout = window.setTimeout(() => {
|
||||
if (this.pageDetails.length === 0) {
|
||||
this.collectTabPageDetails();
|
||||
}
|
||||
}, 250);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { Location } from "@angular/common";
|
||||
import { ChangeDetectorRef, Component, NgZone, OnDestroy, OnInit } from "@angular/core";
|
||||
import { ActivatedRoute, Router } from "@angular/router";
|
||||
import { firstValueFrom } from "rxjs";
|
||||
import { first } from "rxjs/operators";
|
||||
import { BehaviorSubject, Subject, firstValueFrom, from } from "rxjs";
|
||||
import { first, switchMap, takeUntil } from "rxjs/operators";
|
||||
|
||||
import { VaultFilter } from "@bitwarden/angular/vault/vault-filter/models/vault-filter.model";
|
||||
import { SearchService } from "@bitwarden/common/abstractions/search.service";
|
||||
@@ -53,7 +53,6 @@ export class VaultFilterComponent implements OnInit, OnDestroy {
|
||||
folderCounts = new Map<string, number>();
|
||||
collectionCounts = new Map<string, number>();
|
||||
typeCounts = new Map<CipherType, number>();
|
||||
searchText: string;
|
||||
state: BrowserGroupingsComponentState;
|
||||
showLeftHeader = true;
|
||||
searchPending = false;
|
||||
@@ -71,6 +70,16 @@ export class VaultFilterComponent implements OnInit, OnDestroy {
|
||||
private hasSearched = false;
|
||||
private hasLoadedAllCiphers = false;
|
||||
private allCiphers: CipherView[] = null;
|
||||
private destroy$ = new Subject<void>();
|
||||
private _searchText$ = new BehaviorSubject<string>("");
|
||||
private isSearchable: boolean = false;
|
||||
|
||||
get searchText() {
|
||||
return this._searchText$.value;
|
||||
}
|
||||
set searchText(value: string) {
|
||||
this._searchText$.next(value);
|
||||
}
|
||||
|
||||
constructor(
|
||||
private i18nService: I18nService,
|
||||
@@ -148,6 +157,15 @@ export class VaultFilterComponent implements OnInit, OnDestroy {
|
||||
BrowserPopupUtils.setContentScrollY(window, this.state?.scrollY);
|
||||
}
|
||||
});
|
||||
|
||||
this._searchText$
|
||||
.pipe(
|
||||
switchMap((searchText) => from(this.searchService.isSearchable(searchText))),
|
||||
takeUntil(this.destroy$),
|
||||
)
|
||||
.subscribe((isSearchable) => {
|
||||
this.isSearchable = isSearchable;
|
||||
});
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
@@ -161,6 +179,8 @@ export class VaultFilterComponent implements OnInit, OnDestroy {
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
this.saveState();
|
||||
this.broadcasterService.unsubscribe(ComponentId);
|
||||
this.destroy$.next();
|
||||
this.destroy$.complete();
|
||||
}
|
||||
|
||||
async load() {
|
||||
@@ -181,7 +201,7 @@ export class VaultFilterComponent implements OnInit, OnDestroy {
|
||||
async loadCiphers() {
|
||||
this.allCiphers = await this.cipherService.getAllDecrypted();
|
||||
if (!this.hasLoadedAllCiphers) {
|
||||
this.hasLoadedAllCiphers = !this.searchService.isSearchable(this.searchText);
|
||||
this.hasLoadedAllCiphers = !(await this.searchService.isSearchable(this.searchText));
|
||||
}
|
||||
await this.search(null);
|
||||
this.getCounts();
|
||||
@@ -210,7 +230,7 @@ export class VaultFilterComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
const filterDeleted = (c: CipherView) => !c.isDeleted;
|
||||
if (timeout == null) {
|
||||
this.hasSearched = this.searchService.isSearchable(this.searchText);
|
||||
this.hasSearched = this.isSearchable;
|
||||
this.ciphers = await this.searchService.searchCiphers(
|
||||
this.searchText,
|
||||
filterDeleted,
|
||||
@@ -223,7 +243,7 @@ export class VaultFilterComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
this.searchPending = true;
|
||||
this.searchTimeout = setTimeout(async () => {
|
||||
this.hasSearched = this.searchService.isSearchable(this.searchText);
|
||||
this.hasSearched = this.isSearchable;
|
||||
if (!this.hasLoadedAllCiphers && !this.hasSearched) {
|
||||
await this.loadCiphers();
|
||||
} else {
|
||||
@@ -381,9 +401,7 @@ export class VaultFilterComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
|
||||
showSearching() {
|
||||
return (
|
||||
this.hasSearched || (!this.searchPending && this.searchService.isSearchable(this.searchText))
|
||||
);
|
||||
return this.hasSearched || (!this.searchPending && this.isSearchable);
|
||||
}
|
||||
|
||||
closeOnEsc(e: KeyboardEvent) {
|
||||
|
||||
@@ -20,7 +20,7 @@ import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/pl
|
||||
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
|
||||
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
|
||||
import { FolderService } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction";
|
||||
import { TotpService } from "@bitwarden/common/vault/abstractions/totp.service";
|
||||
import { TotpService as TotpServiceAbstraction } from "@bitwarden/common/vault/abstractions/totp.service";
|
||||
import { CipherType } from "@bitwarden/common/vault/enums";
|
||||
import { Cipher } from "@bitwarden/common/vault/models/domain/cipher";
|
||||
import { LoginUriView } from "@bitwarden/common/vault/models/view/login-uri.view";
|
||||
@@ -74,7 +74,7 @@ export class ViewComponent extends BaseViewComponent {
|
||||
constructor(
|
||||
cipherService: CipherService,
|
||||
folderService: FolderService,
|
||||
totpService: TotpService,
|
||||
totpService: TotpServiceAbstraction,
|
||||
tokenService: TokenService,
|
||||
i18nService: I18nService,
|
||||
cryptoService: CryptoService,
|
||||
|
||||
Reference in New Issue
Block a user