mirror of
https://github.com/bitwarden/browser.git
synced 2025-02-12 00:41:29 +01:00
Merge branch 'main' into autofill/pm-5189-fix-issues-present-with-inline-menu-rendering-in-iframes
This commit is contained in:
commit
f702d1f08a
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@bitwarden/browser",
|
"name": "@bitwarden/browser",
|
||||||
"version": "2024.3.1",
|
"version": "2024.4.1",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "webpack",
|
"build": "webpack",
|
||||||
"build:mv3": "cross-env MANIFEST_VERSION=3 webpack",
|
"build:mv3": "cross-env MANIFEST_VERSION=3 webpack",
|
||||||
|
@ -3005,5 +3005,8 @@
|
|||||||
},
|
},
|
||||||
"passkeyRemoved": {
|
"passkeyRemoved": {
|
||||||
"message": "Passkey removed"
|
"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"
|
"message": "Auto-fill login"
|
||||||
},
|
},
|
||||||
"autoFillCard": {
|
"autoFillCard": {
|
||||||
"message": "Auto-fill card"
|
"message": "Auto-bete txartela"
|
||||||
},
|
},
|
||||||
"autoFillIdentity": {
|
"autoFillIdentity": {
|
||||||
"message": "Auto-fill identity"
|
"message": "Auto-bete nortasuna"
|
||||||
},
|
},
|
||||||
"generatePasswordCopied": {
|
"generatePasswordCopied": {
|
||||||
"message": "Sortu pasahitza (kopiatuta)"
|
"message": "Sortu pasahitza (kopiatuta)"
|
||||||
@ -110,19 +110,19 @@
|
|||||||
"message": "Bat datozen saio-hasierarik gabe"
|
"message": "Bat datozen saio-hasierarik gabe"
|
||||||
},
|
},
|
||||||
"noCards": {
|
"noCards": {
|
||||||
"message": "No cards"
|
"message": "Txartelik ez"
|
||||||
},
|
},
|
||||||
"noIdentities": {
|
"noIdentities": {
|
||||||
"message": "No identities"
|
"message": "Nortasunik ez"
|
||||||
},
|
},
|
||||||
"addLoginMenu": {
|
"addLoginMenu": {
|
||||||
"message": "Add login"
|
"message": "Add login"
|
||||||
},
|
},
|
||||||
"addCardMenu": {
|
"addCardMenu": {
|
||||||
"message": "Add card"
|
"message": "Gehitu txartela"
|
||||||
},
|
},
|
||||||
"addIdentityMenu": {
|
"addIdentityMenu": {
|
||||||
"message": "Add identity"
|
"message": "Gehitu nortasuna"
|
||||||
},
|
},
|
||||||
"unlockVaultMenu": {
|
"unlockVaultMenu": {
|
||||||
"message": "Desblokeatu kutxa gotorra"
|
"message": "Desblokeatu kutxa gotorra"
|
||||||
@ -223,10 +223,10 @@
|
|||||||
"message": "Bitwarden Laguntza zentroa"
|
"message": "Bitwarden Laguntza zentroa"
|
||||||
},
|
},
|
||||||
"communityForums": {
|
"communityForums": {
|
||||||
"message": "Explore Bitwarden community forums"
|
"message": "Esploratu Bitwarden komunitatearen foroak"
|
||||||
},
|
},
|
||||||
"contactSupport": {
|
"contactSupport": {
|
||||||
"message": "Contact Bitwarden support"
|
"message": "Jarri harremanetan Bitwardeneko laguntza taldearekin"
|
||||||
},
|
},
|
||||||
"sync": {
|
"sync": {
|
||||||
"message": "Sinkronizatu"
|
"message": "Sinkronizatu"
|
||||||
@ -269,7 +269,7 @@
|
|||||||
"message": "Luzera"
|
"message": "Luzera"
|
||||||
},
|
},
|
||||||
"passwordMinLength": {
|
"passwordMinLength": {
|
||||||
"message": "Minimum password length"
|
"message": "Pasahitzaren gutxieneko luzera"
|
||||||
},
|
},
|
||||||
"uppercase": {
|
"uppercase": {
|
||||||
"message": "Letra larria (A-Z)"
|
"message": "Letra larria (A-Z)"
|
||||||
@ -1064,7 +1064,7 @@
|
|||||||
"message": "Edit browser settings."
|
"message": "Edit browser settings."
|
||||||
},
|
},
|
||||||
"autofillOverlayVisibilityOff": {
|
"autofillOverlayVisibilityOff": {
|
||||||
"message": "Off",
|
"message": "Itzalita",
|
||||||
"description": "Overlay setting select option for disabling autofill overlay"
|
"description": "Overlay setting select option for disabling autofill overlay"
|
||||||
},
|
},
|
||||||
"autofillOverlayVisibilityOnFieldFocus": {
|
"autofillOverlayVisibilityOnFieldFocus": {
|
||||||
@ -1592,10 +1592,10 @@
|
|||||||
"message": "Ezarri pasahitz nagusia"
|
"message": "Ezarri pasahitz nagusia"
|
||||||
},
|
},
|
||||||
"currentMasterPass": {
|
"currentMasterPass": {
|
||||||
"message": "Current master password"
|
"message": "Oraingo pasahitz nagusia"
|
||||||
},
|
},
|
||||||
"newMasterPass": {
|
"newMasterPass": {
|
||||||
"message": "New master password"
|
"message": "Pasahitz nagusi berria"
|
||||||
},
|
},
|
||||||
"confirmNewMasterPass": {
|
"confirmNewMasterPass": {
|
||||||
"message": "Confirm new master password"
|
"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."
|
"message": "Please make sure your vault is unlocked and the Fingerprint phrase matches on the other device."
|
||||||
},
|
},
|
||||||
"resendNotification": {
|
"resendNotification": {
|
||||||
"message": "Resend notification"
|
"message": "Berbidali jakinarazpena"
|
||||||
},
|
},
|
||||||
"viewAllLoginOptions": {
|
"viewAllLoginOptions": {
|
||||||
"message": "View all log in options"
|
"message": "Ikusi erregistro guztiak ezarpenetan"
|
||||||
},
|
},
|
||||||
"notificationSentDevice": {
|
"notificationSentDevice": {
|
||||||
"message": "A notification has been sent to your device."
|
"message": "A notification has been sent to your device."
|
||||||
@ -2293,13 +2293,13 @@
|
|||||||
"message": "Check known data breaches for this password"
|
"message": "Check known data breaches for this password"
|
||||||
},
|
},
|
||||||
"important": {
|
"important": {
|
||||||
"message": "Important:"
|
"message": "Garrantzitsua:"
|
||||||
},
|
},
|
||||||
"masterPasswordHint": {
|
"masterPasswordHint": {
|
||||||
"message": "Your master password cannot be recovered if you forget it!"
|
"message": "Your master password cannot be recovered if you forget it!"
|
||||||
},
|
},
|
||||||
"characterMinimum": {
|
"characterMinimum": {
|
||||||
"message": "$LENGTH$ character minimum",
|
"message": "$LENGTH$ karaktere gutxienez",
|
||||||
"placeholders": {
|
"placeholders": {
|
||||||
"length": {
|
"length": {
|
||||||
"content": "$1",
|
"content": "$1",
|
||||||
@ -2326,7 +2326,7 @@
|
|||||||
"message": "Select an item from this screen, or explore other options in settings."
|
"message": "Select an item from this screen, or explore other options in settings."
|
||||||
},
|
},
|
||||||
"gotIt": {
|
"gotIt": {
|
||||||
"message": "Got it"
|
"message": "Ulertuta"
|
||||||
},
|
},
|
||||||
"autofillSettings": {
|
"autofillSettings": {
|
||||||
"message": "Auto-fill settings"
|
"message": "Auto-fill settings"
|
||||||
@ -2359,25 +2359,25 @@
|
|||||||
"message": "Logging in on"
|
"message": "Logging in on"
|
||||||
},
|
},
|
||||||
"opensInANewWindow": {
|
"opensInANewWindow": {
|
||||||
"message": "Opens in a new window"
|
"message": "Leiho berri batean irekitzen da"
|
||||||
},
|
},
|
||||||
"deviceApprovalRequired": {
|
"deviceApprovalRequired": {
|
||||||
"message": "Device approval required. Select an approval option below:"
|
"message": "Device approval required. Select an approval option below:"
|
||||||
},
|
},
|
||||||
"rememberThisDevice": {
|
"rememberThisDevice": {
|
||||||
"message": "Remember this device"
|
"message": "Gogoratu gailu hau"
|
||||||
},
|
},
|
||||||
"uncheckIfPublicDevice": {
|
"uncheckIfPublicDevice": {
|
||||||
"message": "Uncheck if using a public device"
|
"message": "Uncheck if using a public device"
|
||||||
},
|
},
|
||||||
"approveFromYourOtherDevice": {
|
"approveFromYourOtherDevice": {
|
||||||
"message": "Approve from your other device"
|
"message": "Onartu zure beste gailutik"
|
||||||
},
|
},
|
||||||
"requestAdminApproval": {
|
"requestAdminApproval": {
|
||||||
"message": "Request admin approval"
|
"message": "Eskatu administratzailearen onarpena"
|
||||||
},
|
},
|
||||||
"approveWithMasterPassword": {
|
"approveWithMasterPassword": {
|
||||||
"message": "Approve with master password"
|
"message": "Onartu pasahitz nagusiarekin"
|
||||||
},
|
},
|
||||||
"ssoIdentifierRequired": {
|
"ssoIdentifierRequired": {
|
||||||
"message": "Organization SSO identifier is required."
|
"message": "Organization SSO identifier is required."
|
||||||
@ -2390,31 +2390,31 @@
|
|||||||
"message": "Access denied. You do not have permission to view this page."
|
"message": "Access denied. You do not have permission to view this page."
|
||||||
},
|
},
|
||||||
"general": {
|
"general": {
|
||||||
"message": "General"
|
"message": "Orokorra"
|
||||||
},
|
},
|
||||||
"display": {
|
"display": {
|
||||||
"message": "Display"
|
"message": "Bistaratzea"
|
||||||
},
|
},
|
||||||
"accountSuccessfullyCreated": {
|
"accountSuccessfullyCreated": {
|
||||||
"message": "Account successfully created!"
|
"message": "Kontua zuzen sortu da!"
|
||||||
},
|
},
|
||||||
"adminApprovalRequested": {
|
"adminApprovalRequested": {
|
||||||
"message": "Admin approval requested"
|
"message": "Administratzailearen onarpena eskatuta"
|
||||||
},
|
},
|
||||||
"adminApprovalRequestSentToAdmins": {
|
"adminApprovalRequestSentToAdmins": {
|
||||||
"message": "Your request has been sent to your admin."
|
"message": "Zure eskaera zure administratzaileari bidali zaio."
|
||||||
},
|
},
|
||||||
"youWillBeNotifiedOnceApproved": {
|
"youWillBeNotifiedOnceApproved": {
|
||||||
"message": "You will be notified once approved."
|
"message": "Jakinaraziko zaizu onartzen denean."
|
||||||
},
|
},
|
||||||
"troubleLoggingIn": {
|
"troubleLoggingIn": {
|
||||||
"message": "Trouble logging in?"
|
"message": "Arazoak saioa hasterakoan?"
|
||||||
},
|
},
|
||||||
"loginApproved": {
|
"loginApproved": {
|
||||||
"message": "Login approved"
|
"message": "Login approved"
|
||||||
},
|
},
|
||||||
"userEmailMissing": {
|
"userEmailMissing": {
|
||||||
"message": "User email missing"
|
"message": "Erabiltzailearen emaila falta da"
|
||||||
},
|
},
|
||||||
"deviceTrusted": {
|
"deviceTrusted": {
|
||||||
"message": "Device trusted"
|
"message": "Device trusted"
|
||||||
@ -2540,19 +2540,19 @@
|
|||||||
"description": "Notification button text for starting a fileless import."
|
"description": "Notification button text for starting a fileless import."
|
||||||
},
|
},
|
||||||
"importing": {
|
"importing": {
|
||||||
"message": "Importing...",
|
"message": "Inportatzen...",
|
||||||
"description": "Notification message for when an import is in progress."
|
"description": "Notification message for when an import is in progress."
|
||||||
},
|
},
|
||||||
"dataSuccessfullyImported": {
|
"dataSuccessfullyImported": {
|
||||||
"message": "Data successfully imported!",
|
"message": "Datuak zuzen inportatu dira!",
|
||||||
"description": "Notification message for when an import has completed successfully."
|
"description": "Notification message for when an import has completed successfully."
|
||||||
},
|
},
|
||||||
"dataImportFailed": {
|
"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."
|
"description": "Notification message for when an import has failed."
|
||||||
},
|
},
|
||||||
"importNetworkError": {
|
"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."
|
"description": "Notification message for when an import has failed due to a network error."
|
||||||
},
|
},
|
||||||
"aliasDomain": {
|
"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"
|
"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": {
|
"noItemsToShow": {
|
||||||
"message": "No items to show",
|
"message": "Ez dago elementurik erakusteko",
|
||||||
"description": "Text to show in overlay if there are no matching items"
|
"description": "Text to show in overlay if there are no matching items"
|
||||||
},
|
},
|
||||||
"newItem": {
|
"newItem": {
|
||||||
"message": "New item",
|
"message": "Elementu berria",
|
||||||
"description": "Button text to display in overlay when there are no matching items"
|
"description": "Button text to display in overlay when there are no matching items"
|
||||||
},
|
},
|
||||||
"addNewVaultItem": {
|
"addNewVaultItem": {
|
||||||
@ -2618,32 +2618,32 @@
|
|||||||
"description": "Screen reader text for announcing when the overlay opens on the page"
|
"description": "Screen reader text for announcing when the overlay opens on the page"
|
||||||
},
|
},
|
||||||
"turnOn": {
|
"turnOn": {
|
||||||
"message": "Turn on"
|
"message": "Piztu"
|
||||||
},
|
},
|
||||||
"ignore": {
|
"ignore": {
|
||||||
"message": "Ignore"
|
"message": "Ezikusi"
|
||||||
},
|
},
|
||||||
"importData": {
|
"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"
|
"description": "Used for the header of the import dialog, the import button and within the file-password-prompt"
|
||||||
},
|
},
|
||||||
"importError": {
|
"importError": {
|
||||||
"message": "Import error"
|
"message": "Errorea inportatzerakoan"
|
||||||
},
|
},
|
||||||
"importErrorDesc": {
|
"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": {
|
"resolveTheErrorsBelowAndTryAgain": {
|
||||||
"message": "Resolve the errors below and try again."
|
"message": "Konpondu beheko akatsak eta saiatu berriro."
|
||||||
},
|
},
|
||||||
"description": {
|
"description": {
|
||||||
"message": "Description"
|
"message": "Deskribapena"
|
||||||
},
|
},
|
||||||
"importSuccess": {
|
"importSuccess": {
|
||||||
"message": "Data successfully imported"
|
"message": "Datuak zuzen inportatu dira"
|
||||||
},
|
},
|
||||||
"importSuccessNumberOfItems": {
|
"importSuccessNumberOfItems": {
|
||||||
"message": "A total of $AMOUNT$ items were imported.",
|
"message": "Guztira $AMOUNT$ elementu inportatu dira.",
|
||||||
"placeholders": {
|
"placeholders": {
|
||||||
"amount": {
|
"amount": {
|
||||||
"content": "$1",
|
"content": "$1",
|
||||||
@ -2652,7 +2652,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"tryAgain": {
|
"tryAgain": {
|
||||||
"message": "Try again"
|
"message": "Saiatu berriro"
|
||||||
},
|
},
|
||||||
"verificationRequiredForActionSetPinToContinue": {
|
"verificationRequiredForActionSetPinToContinue": {
|
||||||
"message": "Verification required for this action. Set a PIN to continue."
|
"message": "Verification required for this action. Set a PIN to continue."
|
||||||
@ -2661,10 +2661,10 @@
|
|||||||
"message": "Set PIN"
|
"message": "Set PIN"
|
||||||
},
|
},
|
||||||
"verifyWithBiometrics": {
|
"verifyWithBiometrics": {
|
||||||
"message": "Verify with biometrics"
|
"message": "Egiaztatu biometria erabiliz"
|
||||||
},
|
},
|
||||||
"awaitingConfirmation": {
|
"awaitingConfirmation": {
|
||||||
"message": "Awaiting confirmation"
|
"message": "Baieztapenaren zain"
|
||||||
},
|
},
|
||||||
"couldNotCompleteBiometrics": {
|
"couldNotCompleteBiometrics": {
|
||||||
"message": "Could not complete biometrics."
|
"message": "Could not complete biometrics."
|
||||||
@ -2673,13 +2673,13 @@
|
|||||||
"message": "Need a different method?"
|
"message": "Need a different method?"
|
||||||
},
|
},
|
||||||
"useMasterPassword": {
|
"useMasterPassword": {
|
||||||
"message": "Use master password"
|
"message": "Erabili pasahitz nagusia"
|
||||||
},
|
},
|
||||||
"usePin": {
|
"usePin": {
|
||||||
"message": "Use PIN"
|
"message": "Erabili PIN kodea"
|
||||||
},
|
},
|
||||||
"useBiometrics": {
|
"useBiometrics": {
|
||||||
"message": "Use biometrics"
|
"message": "Erabili biometria"
|
||||||
},
|
},
|
||||||
"enterVerificationCodeSentToEmail": {
|
"enterVerificationCodeSentToEmail": {
|
||||||
"message": "Enter the verification code that was sent to your email."
|
"message": "Enter the verification code that was sent to your email."
|
||||||
@ -2688,10 +2688,10 @@
|
|||||||
"message": "Resend code"
|
"message": "Resend code"
|
||||||
},
|
},
|
||||||
"total": {
|
"total": {
|
||||||
"message": "Total"
|
"message": "Guztira"
|
||||||
},
|
},
|
||||||
"importWarning": {
|
"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": {
|
"placeholders": {
|
||||||
"organization": {
|
"organization": {
|
||||||
"content": "$1",
|
"content": "$1",
|
||||||
@ -2810,7 +2810,7 @@
|
|||||||
"message": "You do not have a matching login for this site."
|
"message": "You do not have a matching login for this site."
|
||||||
},
|
},
|
||||||
"confirm": {
|
"confirm": {
|
||||||
"message": "Confirm"
|
"message": "Berretsi"
|
||||||
},
|
},
|
||||||
"savePasskey": {
|
"savePasskey": {
|
||||||
"message": "Save passkey"
|
"message": "Save passkey"
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
"description": "Extension description"
|
"description": "Extension description"
|
||||||
},
|
},
|
||||||
"loginOrCreateNewAccount": {
|
"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": {
|
"createAccount": {
|
||||||
"message": "Luo tili"
|
"message": "Luo tili"
|
||||||
@ -802,7 +802,7 @@
|
|||||||
"message": "Lue lisää"
|
"message": "Lue lisää"
|
||||||
},
|
},
|
||||||
"authenticatorKeyTotp": {
|
"authenticatorKeyTotp": {
|
||||||
"message": "Todennusaavain (TOTP)"
|
"message": "Todennusavain (TOTP)"
|
||||||
},
|
},
|
||||||
"verificationCodeTotp": {
|
"verificationCodeTotp": {
|
||||||
"message": "Todennuskoodi (TOTP)"
|
"message": "Todennuskoodi (TOTP)"
|
||||||
|
@ -17,18 +17,21 @@ import {
|
|||||||
FactoryOptions,
|
FactoryOptions,
|
||||||
factory,
|
factory,
|
||||||
} from "../../../platform/background/service-factories/factory-options";
|
} from "../../../platform/background/service-factories/factory-options";
|
||||||
|
|
||||||
|
import { accountServiceFactory, AccountServiceInitOptions } from "./account-service.factory";
|
||||||
import {
|
import {
|
||||||
stateServiceFactory,
|
internalMasterPasswordServiceFactory,
|
||||||
StateServiceInitOptions,
|
MasterPasswordServiceInitOptions,
|
||||||
} from "../../../platform/background/service-factories/state-service.factory";
|
} from "./master-password-service.factory";
|
||||||
|
|
||||||
type AuthRequestServiceFactoryOptions = FactoryOptions;
|
type AuthRequestServiceFactoryOptions = FactoryOptions;
|
||||||
|
|
||||||
export type AuthRequestServiceInitOptions = AuthRequestServiceFactoryOptions &
|
export type AuthRequestServiceInitOptions = AuthRequestServiceFactoryOptions &
|
||||||
AppIdServiceInitOptions &
|
AppIdServiceInitOptions &
|
||||||
|
AccountServiceInitOptions &
|
||||||
|
MasterPasswordServiceInitOptions &
|
||||||
CryptoServiceInitOptions &
|
CryptoServiceInitOptions &
|
||||||
ApiServiceInitOptions &
|
ApiServiceInitOptions;
|
||||||
StateServiceInitOptions;
|
|
||||||
|
|
||||||
export function authRequestServiceFactory(
|
export function authRequestServiceFactory(
|
||||||
cache: { authRequestService?: AuthRequestServiceAbstraction } & CachedServices,
|
cache: { authRequestService?: AuthRequestServiceAbstraction } & CachedServices,
|
||||||
@ -41,9 +44,10 @@ export function authRequestServiceFactory(
|
|||||||
async () =>
|
async () =>
|
||||||
new AuthRequestService(
|
new AuthRequestService(
|
||||||
await appIdServiceFactory(cache, opts),
|
await appIdServiceFactory(cache, opts),
|
||||||
|
await accountServiceFactory(cache, opts),
|
||||||
|
await internalMasterPasswordServiceFactory(cache, opts),
|
||||||
await cryptoServiceFactory(cache, opts),
|
await cryptoServiceFactory(cache, opts),
|
||||||
await apiServiceFactory(cache, opts),
|
await apiServiceFactory(cache, opts),
|
||||||
await stateServiceFactory(cache, opts),
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -31,6 +31,11 @@ import {
|
|||||||
StateProviderInitOptions,
|
StateProviderInitOptions,
|
||||||
} from "../../../platform/background/service-factories/state-provider.factory";
|
} 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";
|
import { TokenServiceInitOptions, tokenServiceFactory } from "./token-service.factory";
|
||||||
|
|
||||||
type KeyConnectorServiceFactoryOptions = FactoryOptions & {
|
type KeyConnectorServiceFactoryOptions = FactoryOptions & {
|
||||||
@ -40,6 +45,8 @@ type KeyConnectorServiceFactoryOptions = FactoryOptions & {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export type KeyConnectorServiceInitOptions = KeyConnectorServiceFactoryOptions &
|
export type KeyConnectorServiceInitOptions = KeyConnectorServiceFactoryOptions &
|
||||||
|
AccountServiceInitOptions &
|
||||||
|
MasterPasswordServiceInitOptions &
|
||||||
CryptoServiceInitOptions &
|
CryptoServiceInitOptions &
|
||||||
ApiServiceInitOptions &
|
ApiServiceInitOptions &
|
||||||
TokenServiceInitOptions &
|
TokenServiceInitOptions &
|
||||||
@ -58,6 +65,8 @@ export function keyConnectorServiceFactory(
|
|||||||
opts,
|
opts,
|
||||||
async () =>
|
async () =>
|
||||||
new KeyConnectorService(
|
new KeyConnectorService(
|
||||||
|
await accountServiceFactory(cache, opts),
|
||||||
|
await internalMasterPasswordServiceFactory(cache, opts),
|
||||||
await cryptoServiceFactory(cache, opts),
|
await cryptoServiceFactory(cache, opts),
|
||||||
await apiServiceFactory(cache, opts),
|
await apiServiceFactory(cache, opts),
|
||||||
await tokenServiceFactory(cache, opts),
|
await tokenServiceFactory(cache, opts),
|
||||||
|
@ -59,6 +59,7 @@ import {
|
|||||||
PasswordStrengthServiceInitOptions,
|
PasswordStrengthServiceInitOptions,
|
||||||
} from "../../../tools/background/service_factories/password-strength-service.factory";
|
} from "../../../tools/background/service_factories/password-strength-service.factory";
|
||||||
|
|
||||||
|
import { accountServiceFactory, AccountServiceInitOptions } from "./account-service.factory";
|
||||||
import {
|
import {
|
||||||
authRequestServiceFactory,
|
authRequestServiceFactory,
|
||||||
AuthRequestServiceInitOptions,
|
AuthRequestServiceInitOptions,
|
||||||
@ -71,6 +72,10 @@ import {
|
|||||||
keyConnectorServiceFactory,
|
keyConnectorServiceFactory,
|
||||||
KeyConnectorServiceInitOptions,
|
KeyConnectorServiceInitOptions,
|
||||||
} from "./key-connector-service.factory";
|
} from "./key-connector-service.factory";
|
||||||
|
import {
|
||||||
|
internalMasterPasswordServiceFactory,
|
||||||
|
MasterPasswordServiceInitOptions,
|
||||||
|
} from "./master-password-service.factory";
|
||||||
import { tokenServiceFactory, TokenServiceInitOptions } from "./token-service.factory";
|
import { tokenServiceFactory, TokenServiceInitOptions } from "./token-service.factory";
|
||||||
import { twoFactorServiceFactory, TwoFactorServiceInitOptions } from "./two-factor-service.factory";
|
import { twoFactorServiceFactory, TwoFactorServiceInitOptions } from "./two-factor-service.factory";
|
||||||
import {
|
import {
|
||||||
@ -81,6 +86,8 @@ import {
|
|||||||
type LoginStrategyServiceFactoryOptions = FactoryOptions;
|
type LoginStrategyServiceFactoryOptions = FactoryOptions;
|
||||||
|
|
||||||
export type LoginStrategyServiceInitOptions = LoginStrategyServiceFactoryOptions &
|
export type LoginStrategyServiceInitOptions = LoginStrategyServiceFactoryOptions &
|
||||||
|
AccountServiceInitOptions &
|
||||||
|
MasterPasswordServiceInitOptions &
|
||||||
CryptoServiceInitOptions &
|
CryptoServiceInitOptions &
|
||||||
ApiServiceInitOptions &
|
ApiServiceInitOptions &
|
||||||
TokenServiceInitOptions &
|
TokenServiceInitOptions &
|
||||||
@ -111,6 +118,8 @@ export function loginStrategyServiceFactory(
|
|||||||
opts,
|
opts,
|
||||||
async () =>
|
async () =>
|
||||||
new LoginStrategyService(
|
new LoginStrategyService(
|
||||||
|
await accountServiceFactory(cache, opts),
|
||||||
|
await internalMasterPasswordServiceFactory(cache, opts),
|
||||||
await cryptoServiceFactory(cache, opts),
|
await cryptoServiceFactory(cache, opts),
|
||||||
await apiServiceFactory(cache, opts),
|
await apiServiceFactory(cache, opts),
|
||||||
await tokenServiceFactory(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,
|
stateServiceFactory,
|
||||||
} from "../../../platform/background/service-factories/state-service.factory";
|
} 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 { PinCryptoServiceInitOptions, pinCryptoServiceFactory } from "./pin-crypto-service.factory";
|
||||||
import {
|
import {
|
||||||
userDecryptionOptionsServiceFactory,
|
userDecryptionOptionsServiceFactory,
|
||||||
@ -46,6 +51,8 @@ type UserVerificationServiceFactoryOptions = FactoryOptions;
|
|||||||
export type UserVerificationServiceInitOptions = UserVerificationServiceFactoryOptions &
|
export type UserVerificationServiceInitOptions = UserVerificationServiceFactoryOptions &
|
||||||
StateServiceInitOptions &
|
StateServiceInitOptions &
|
||||||
CryptoServiceInitOptions &
|
CryptoServiceInitOptions &
|
||||||
|
AccountServiceInitOptions &
|
||||||
|
MasterPasswordServiceInitOptions &
|
||||||
I18nServiceInitOptions &
|
I18nServiceInitOptions &
|
||||||
UserVerificationApiServiceInitOptions &
|
UserVerificationApiServiceInitOptions &
|
||||||
UserDecryptionOptionsServiceInitOptions &
|
UserDecryptionOptionsServiceInitOptions &
|
||||||
@ -66,6 +73,8 @@ export function userVerificationServiceFactory(
|
|||||||
new UserVerificationService(
|
new UserVerificationService(
|
||||||
await stateServiceFactory(cache, opts),
|
await stateServiceFactory(cache, opts),
|
||||||
await cryptoServiceFactory(cache, opts),
|
await cryptoServiceFactory(cache, opts),
|
||||||
|
await accountServiceFactory(cache, opts),
|
||||||
|
await internalMasterPasswordServiceFactory(cache, opts),
|
||||||
await i18nServiceFactory(cache, opts),
|
await i18nServiceFactory(cache, opts),
|
||||||
await userVerificationApiServiceFactory(cache, opts),
|
await userVerificationApiServiceFactory(cache, opts),
|
||||||
await userDecryptionOptionsServiceFactory(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 { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||||
import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service";
|
import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service";
|
||||||
import { DeviceTrustCryptoServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust-crypto.service.abstraction";
|
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 { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction";
|
||||||
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
|
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
|
||||||
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
|
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
|
||||||
@ -41,6 +42,7 @@ export class LockComponent extends BaseLockComponent {
|
|||||||
fido2PopoutSessionData$ = fido2PopoutSessionData$();
|
fido2PopoutSessionData$ = fido2PopoutSessionData$();
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
|
masterPasswordService: InternalMasterPasswordServiceAbstraction,
|
||||||
router: Router,
|
router: Router,
|
||||||
i18nService: I18nService,
|
i18nService: I18nService,
|
||||||
platformUtilsService: PlatformUtilsService,
|
platformUtilsService: PlatformUtilsService,
|
||||||
@ -66,6 +68,7 @@ export class LockComponent extends BaseLockComponent {
|
|||||||
accountService: AccountService,
|
accountService: AccountService,
|
||||||
) {
|
) {
|
||||||
super(
|
super(
|
||||||
|
masterPasswordService,
|
||||||
router,
|
router,
|
||||||
i18nService,
|
i18nService,
|
||||||
platformUtilsService,
|
platformUtilsService,
|
||||||
|
@ -1,65 +1,9 @@
|
|||||||
import { Component } from "@angular/core";
|
import { Component } from "@angular/core";
|
||||||
import { ActivatedRoute, Router } from "@angular/router";
|
|
||||||
|
|
||||||
import { SetPasswordComponent as BaseSetPasswordComponent } from "@bitwarden/angular/auth/components/set-password.component";
|
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({
|
@Component({
|
||||||
selector: "app-set-password",
|
selector: "app-set-password",
|
||||||
templateUrl: "set-password.component.html",
|
templateUrl: "set-password.component.html",
|
||||||
})
|
})
|
||||||
export class SetPasswordComponent extends BaseSetPasswordComponent {
|
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,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -9,7 +9,9 @@ import {
|
|||||||
UserDecryptionOptionsServiceAbstraction,
|
UserDecryptionOptionsServiceAbstraction,
|
||||||
} from "@bitwarden/auth/common";
|
} from "@bitwarden/auth/common";
|
||||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
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 { 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 { SsoLoginServiceAbstraction } from "@bitwarden/common/auth/abstractions/sso-login.service.abstraction";
|
||||||
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
|
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
|
||||||
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
||||||
@ -45,7 +47,9 @@ export class SsoComponent extends BaseSsoComponent {
|
|||||||
logService: LogService,
|
logService: LogService,
|
||||||
userDecryptionOptionsService: UserDecryptionOptionsServiceAbstraction,
|
userDecryptionOptionsService: UserDecryptionOptionsServiceAbstraction,
|
||||||
configService: ConfigService,
|
configService: ConfigService,
|
||||||
protected authService: AuthService,
|
masterPasswordService: InternalMasterPasswordServiceAbstraction,
|
||||||
|
accountService: AccountService,
|
||||||
|
private authService: AuthService,
|
||||||
@Inject(WINDOW) private win: Window,
|
@Inject(WINDOW) private win: Window,
|
||||||
) {
|
) {
|
||||||
super(
|
super(
|
||||||
@ -63,6 +67,8 @@ export class SsoComponent extends BaseSsoComponent {
|
|||||||
logService,
|
logService,
|
||||||
userDecryptionOptionsService,
|
userDecryptionOptionsService,
|
||||||
configService,
|
configService,
|
||||||
|
masterPasswordService,
|
||||||
|
accountService,
|
||||||
);
|
);
|
||||||
|
|
||||||
environmentService.environment$.pipe(takeUntilDestroyed()).subscribe((env) => {
|
environmentService.environment$.pipe(takeUntilDestroyed()).subscribe((env) => {
|
||||||
|
@ -11,6 +11,8 @@ import {
|
|||||||
UserDecryptionOptionsServiceAbstraction,
|
UserDecryptionOptionsServiceAbstraction,
|
||||||
} from "@bitwarden/auth/common";
|
} from "@bitwarden/auth/common";
|
||||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
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 { SsoLoginServiceAbstraction } from "@bitwarden/common/auth/abstractions/sso-login.service.abstraction";
|
||||||
import { TwoFactorService } from "@bitwarden/common/auth/abstractions/two-factor.service";
|
import { TwoFactorService } from "@bitwarden/common/auth/abstractions/two-factor.service";
|
||||||
import { TwoFactorProviderType } from "@bitwarden/common/auth/enums/two-factor-provider-type";
|
import { TwoFactorProviderType } from "@bitwarden/common/auth/enums/two-factor-provider-type";
|
||||||
@ -58,6 +60,8 @@ export class TwoFactorComponent extends BaseTwoFactorComponent {
|
|||||||
configService: ConfigService,
|
configService: ConfigService,
|
||||||
ssoLoginService: SsoLoginServiceAbstraction,
|
ssoLoginService: SsoLoginServiceAbstraction,
|
||||||
private dialogService: DialogService,
|
private dialogService: DialogService,
|
||||||
|
masterPasswordService: InternalMasterPasswordServiceAbstraction,
|
||||||
|
accountService: AccountService,
|
||||||
@Inject(WINDOW) protected win: Window,
|
@Inject(WINDOW) protected win: Window,
|
||||||
private browserMessagingApi: ZonedMessageListenerService,
|
private browserMessagingApi: ZonedMessageListenerService,
|
||||||
) {
|
) {
|
||||||
@ -78,6 +82,8 @@ export class TwoFactorComponent extends BaseTwoFactorComponent {
|
|||||||
userDecryptionOptionsService,
|
userDecryptionOptionsService,
|
||||||
ssoLoginService,
|
ssoLoginService,
|
||||||
configService,
|
configService,
|
||||||
|
masterPasswordService,
|
||||||
|
accountService,
|
||||||
);
|
);
|
||||||
super.onSuccessfulLogin = async () => {
|
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.
|
// 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,
|
generateRandomCustomElementName,
|
||||||
sendExtensionMessage,
|
sendExtensionMessage,
|
||||||
setElementStyles,
|
setElementStyles,
|
||||||
getFromLocalStorage,
|
|
||||||
setupExtensionDisconnectAction,
|
setupExtensionDisconnectAction,
|
||||||
setupAutofillInitDisconnectAction,
|
setupAutofillInitDisconnectAction,
|
||||||
} from "./index";
|
} 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", () => {
|
describe("setupExtensionDisconnectAction", () => {
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
jest.clearAllMocks();
|
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
|
* Sets up a long-lived connection with the extension background
|
||||||
* and triggers an onDisconnect event if the extension context
|
* and triggers an onDisconnect event if the extension context
|
||||||
@ -279,7 +267,6 @@ export {
|
|||||||
buildSvgDomElement,
|
buildSvgDomElement,
|
||||||
sendExtensionMessage,
|
sendExtensionMessage,
|
||||||
setElementStyles,
|
setElementStyles,
|
||||||
getFromLocalStorage,
|
|
||||||
setupExtensionDisconnectAction,
|
setupExtensionDisconnectAction,
|
||||||
setupAutofillInitDisconnectAction,
|
setupAutofillInitDisconnectAction,
|
||||||
elementIsFillableFormField,
|
elementIsFillableFormField,
|
||||||
|
@ -10,6 +10,7 @@ import {
|
|||||||
AuthRequestServiceAbstraction,
|
AuthRequestServiceAbstraction,
|
||||||
AuthRequestService,
|
AuthRequestService,
|
||||||
LoginEmailServiceAbstraction,
|
LoginEmailServiceAbstraction,
|
||||||
|
LoginEmailService,
|
||||||
} from "@bitwarden/auth/common";
|
} from "@bitwarden/auth/common";
|
||||||
import { ApiService as ApiServiceAbstraction } from "@bitwarden/common/abstractions/api.service";
|
import { ApiService as ApiServiceAbstraction } from "@bitwarden/common/abstractions/api.service";
|
||||||
import { AuditService as AuditServiceAbstraction } from "@bitwarden/common/abstractions/audit.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 { DevicesServiceAbstraction } from "@bitwarden/common/auth/abstractions/devices/devices.service.abstraction";
|
||||||
import { DevicesApiServiceAbstraction } from "@bitwarden/common/auth/abstractions/devices-api.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 { 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 { SsoLoginServiceAbstraction } from "@bitwarden/common/auth/abstractions/sso-login.service.abstraction";
|
||||||
import { TokenService as TokenServiceAbstraction } from "@bitwarden/common/auth/abstractions/token.service";
|
import { TokenService as TokenServiceAbstraction } from "@bitwarden/common/auth/abstractions/token.service";
|
||||||
import { TwoFactorService as TwoFactorServiceAbstraction } from "@bitwarden/common/auth/abstractions/two-factor.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 { DevicesServiceImplementation } from "@bitwarden/common/auth/services/devices/devices.service.implementation";
|
||||||
import { DevicesApiServiceImplementation } from "@bitwarden/common/auth/services/devices-api.service.implementation";
|
import { DevicesApiServiceImplementation } from "@bitwarden/common/auth/services/devices-api.service.implementation";
|
||||||
import { KeyConnectorService } from "@bitwarden/common/auth/services/key-connector.service";
|
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 { SsoLoginService } from "@bitwarden/common/auth/services/sso-login.service";
|
||||||
import { TokenService } from "@bitwarden/common/auth/services/token.service";
|
import { TokenService } from "@bitwarden/common/auth/services/token.service";
|
||||||
import { TwoFactorService } from "@bitwarden/common/auth/services/two-factor.service";
|
import { TwoFactorService } from "@bitwarden/common/auth/services/two-factor.service";
|
||||||
@ -232,7 +235,7 @@ import RuntimeBackground from "./runtime.background";
|
|||||||
|
|
||||||
export default class MainBackground {
|
export default class MainBackground {
|
||||||
messagingService: MessagingServiceAbstraction;
|
messagingService: MessagingServiceAbstraction;
|
||||||
storageService: AbstractStorageService & ObservableStorageService;
|
storageService: BrowserLocalStorageService;
|
||||||
secureStorageService: AbstractStorageService;
|
secureStorageService: AbstractStorageService;
|
||||||
memoryStorageService: AbstractMemoryStorageService;
|
memoryStorageService: AbstractMemoryStorageService;
|
||||||
memoryStorageForStateProviders: AbstractMemoryStorageService & ObservableStorageService;
|
memoryStorageForStateProviders: AbstractMemoryStorageService & ObservableStorageService;
|
||||||
@ -242,6 +245,7 @@ export default class MainBackground {
|
|||||||
keyGenerationService: KeyGenerationServiceAbstraction;
|
keyGenerationService: KeyGenerationServiceAbstraction;
|
||||||
cryptoService: CryptoServiceAbstraction;
|
cryptoService: CryptoServiceAbstraction;
|
||||||
cryptoFunctionService: CryptoFunctionServiceAbstraction;
|
cryptoFunctionService: CryptoFunctionServiceAbstraction;
|
||||||
|
masterPasswordService: InternalMasterPasswordServiceAbstraction;
|
||||||
tokenService: TokenServiceAbstraction;
|
tokenService: TokenServiceAbstraction;
|
||||||
appIdService: AppIdServiceAbstraction;
|
appIdService: AppIdServiceAbstraction;
|
||||||
apiService: ApiServiceAbstraction;
|
apiService: ApiServiceAbstraction;
|
||||||
@ -361,7 +365,8 @@ export default class MainBackground {
|
|||||||
const logoutCallback = async (expired: boolean, userId?: UserId) =>
|
const logoutCallback = async (expired: boolean, userId?: UserId) =>
|
||||||
await this.logout(expired, userId);
|
await this.logout(expired, userId);
|
||||||
|
|
||||||
this.messagingService = this.popupOnlyContext
|
this.messagingService =
|
||||||
|
this.isPrivateMode && BrowserApi.isManifestVersion(2)
|
||||||
? new BrowserMessagingPrivateModeBackgroundService()
|
? new BrowserMessagingPrivateModeBackgroundService()
|
||||||
: new BrowserMessagingService();
|
: new BrowserMessagingService();
|
||||||
this.logService = new ConsoleLogService(false);
|
this.logService = new ConsoleLogService(false);
|
||||||
@ -405,7 +410,8 @@ export default class MainBackground {
|
|||||||
storageServiceProvider,
|
storageServiceProvider,
|
||||||
);
|
);
|
||||||
|
|
||||||
this.encryptService = flagEnabled("multithreadDecryption")
|
this.encryptService =
|
||||||
|
flagEnabled("multithreadDecryption") && BrowserApi.isManifestVersion(2)
|
||||||
? new MultithreadEncryptServiceImplementation(
|
? new MultithreadEncryptServiceImplementation(
|
||||||
this.cryptoFunctionService,
|
this.cryptoFunctionService,
|
||||||
this.logService,
|
this.logService,
|
||||||
@ -480,8 +486,11 @@ export default class MainBackground {
|
|||||||
|
|
||||||
const themeStateService = new DefaultThemeStateService(this.globalStateProvider);
|
const themeStateService = new DefaultThemeStateService(this.globalStateProvider);
|
||||||
|
|
||||||
|
this.masterPasswordService = new MasterPasswordService(this.stateProvider);
|
||||||
|
|
||||||
this.i18nService = new I18nService(BrowserApi.getUILanguage(), this.globalStateProvider);
|
this.i18nService = new I18nService(BrowserApi.getUILanguage(), this.globalStateProvider);
|
||||||
this.cryptoService = new BrowserCryptoService(
|
this.cryptoService = new BrowserCryptoService(
|
||||||
|
this.masterPasswordService,
|
||||||
this.keyGenerationService,
|
this.keyGenerationService,
|
||||||
this.cryptoFunctionService,
|
this.cryptoFunctionService,
|
||||||
this.encryptService,
|
this.encryptService,
|
||||||
@ -508,7 +517,7 @@ export default class MainBackground {
|
|||||||
this.apiService,
|
this.apiService,
|
||||||
this.fileUploadService,
|
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.collectionService = new CollectionService(
|
||||||
this.cryptoService,
|
this.cryptoService,
|
||||||
@ -525,6 +534,8 @@ export default class MainBackground {
|
|||||||
this.badgeSettingsService = new BadgeSettingsService(this.stateProvider);
|
this.badgeSettingsService = new BadgeSettingsService(this.stateProvider);
|
||||||
this.policyApiService = new PolicyApiService(this.policyService, this.apiService);
|
this.policyApiService = new PolicyApiService(this.policyService, this.apiService);
|
||||||
this.keyConnectorService = new KeyConnectorService(
|
this.keyConnectorService = new KeyConnectorService(
|
||||||
|
this.accountService,
|
||||||
|
this.masterPasswordService,
|
||||||
this.cryptoService,
|
this.cryptoService,
|
||||||
this.apiService,
|
this.apiService,
|
||||||
this.tokenService,
|
this.tokenService,
|
||||||
@ -550,10 +561,13 @@ export default class MainBackground {
|
|||||||
const backgroundMessagingService = new (class extends MessagingServiceAbstraction {
|
const backgroundMessagingService = new (class extends MessagingServiceAbstraction {
|
||||||
// AuthService should send the messages to the background not popup.
|
// AuthService should send the messages to the background not popup.
|
||||||
send = (subscriber: string, arg: any = {}) => {
|
send = (subscriber: string, arg: any = {}) => {
|
||||||
|
if (BrowserApi.isManifestVersion(3)) {
|
||||||
|
that.messagingService.send(subscriber, arg);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const message = Object.assign({}, { command: subscriber }, arg);
|
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.
|
void that.runtimeBackground.processMessage(message, that as any);
|
||||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
||||||
that.runtimeBackground.processMessage(message, that as any);
|
|
||||||
};
|
};
|
||||||
})();
|
})();
|
||||||
|
|
||||||
@ -578,9 +592,10 @@ export default class MainBackground {
|
|||||||
|
|
||||||
this.authRequestService = new AuthRequestService(
|
this.authRequestService = new AuthRequestService(
|
||||||
this.appIdService,
|
this.appIdService,
|
||||||
|
this.accountService,
|
||||||
|
this.masterPasswordService,
|
||||||
this.cryptoService,
|
this.cryptoService,
|
||||||
this.apiService,
|
this.apiService,
|
||||||
this.stateService,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
this.authService = new AuthService(
|
this.authService = new AuthService(
|
||||||
@ -596,7 +611,11 @@ export default class MainBackground {
|
|||||||
this.stateProvider,
|
this.stateProvider,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
this.loginEmailService = new LoginEmailService(this.stateProvider);
|
||||||
|
|
||||||
this.loginStrategyService = new LoginStrategyService(
|
this.loginStrategyService = new LoginStrategyService(
|
||||||
|
this.accountService,
|
||||||
|
this.masterPasswordService,
|
||||||
this.cryptoService,
|
this.cryptoService,
|
||||||
this.apiService,
|
this.apiService,
|
||||||
this.tokenService,
|
this.tokenService,
|
||||||
@ -672,6 +691,8 @@ export default class MainBackground {
|
|||||||
this.userVerificationService = new UserVerificationService(
|
this.userVerificationService = new UserVerificationService(
|
||||||
this.stateService,
|
this.stateService,
|
||||||
this.cryptoService,
|
this.cryptoService,
|
||||||
|
this.accountService,
|
||||||
|
this.masterPasswordService,
|
||||||
this.i18nService,
|
this.i18nService,
|
||||||
this.userVerificationApiService,
|
this.userVerificationApiService,
|
||||||
this.userDecryptionOptionsService,
|
this.userDecryptionOptionsService,
|
||||||
@ -694,6 +715,8 @@ export default class MainBackground {
|
|||||||
this.vaultSettingsService = new VaultSettingsService(this.stateProvider);
|
this.vaultSettingsService = new VaultSettingsService(this.stateProvider);
|
||||||
|
|
||||||
this.vaultTimeoutService = new VaultTimeoutService(
|
this.vaultTimeoutService = new VaultTimeoutService(
|
||||||
|
this.accountService,
|
||||||
|
this.masterPasswordService,
|
||||||
this.cipherService,
|
this.cipherService,
|
||||||
this.folderService,
|
this.folderService,
|
||||||
this.collectionService,
|
this.collectionService,
|
||||||
@ -729,6 +752,8 @@ export default class MainBackground {
|
|||||||
this.providerService = new ProviderService(this.stateProvider);
|
this.providerService = new ProviderService(this.stateProvider);
|
||||||
|
|
||||||
this.syncService = new SyncService(
|
this.syncService = new SyncService(
|
||||||
|
this.masterPasswordService,
|
||||||
|
this.accountService,
|
||||||
this.apiService,
|
this.apiService,
|
||||||
this.domainSettingsService,
|
this.domainSettingsService,
|
||||||
this.folderService,
|
this.folderService,
|
||||||
@ -878,6 +903,8 @@ export default class MainBackground {
|
|||||||
this.fido2Service,
|
this.fido2Service,
|
||||||
);
|
);
|
||||||
this.nativeMessagingBackground = new NativeMessagingBackground(
|
this.nativeMessagingBackground = new NativeMessagingBackground(
|
||||||
|
this.accountService,
|
||||||
|
this.masterPasswordService,
|
||||||
this.cryptoService,
|
this.cryptoService,
|
||||||
this.cryptoFunctionService,
|
this.cryptoFunctionService,
|
||||||
this.runtimeBackground,
|
this.runtimeBackground,
|
||||||
@ -1108,7 +1135,7 @@ export default class MainBackground {
|
|||||||
|
|
||||||
const status = await this.authService.getAuthStatus(userId);
|
const status = await this.authService.getAuthStatus(userId);
|
||||||
const forcePasswordReset =
|
const forcePasswordReset =
|
||||||
(await this.stateService.getForceSetPasswordReason({ userId: userId })) !=
|
(await firstValueFrom(this.masterPasswordService.forceSetPasswordReason$(userId))) !=
|
||||||
ForceSetPasswordReason.None;
|
ForceSetPasswordReason.None;
|
||||||
|
|
||||||
await this.systemService.clearPendingClipboard();
|
await this.systemService.clearPendingClipboard();
|
||||||
@ -1159,10 +1186,10 @@ export default class MainBackground {
|
|||||||
const newActiveUser = await this.stateService.clean({ userId: userId });
|
const newActiveUser = await this.stateService.clean({ userId: userId });
|
||||||
|
|
||||||
if (userId == null || userId === currentUserId) {
|
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) {
|
if (newActiveUser != null) {
|
||||||
// we have a new active user, do not continue tearing down application
|
// we have a new active user, do not continue tearing down application
|
||||||
@ -1237,18 +1264,8 @@ export default class MainBackground {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const getStorage = (): Promise<any> =>
|
const storage = await this.storageService.getAll();
|
||||||
new Promise((resolve) => {
|
await this.storageService.clear();
|
||||||
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();
|
|
||||||
|
|
||||||
for (const key in storage) {
|
for (const key in storage) {
|
||||||
// eslint-disable-next-line
|
// eslint-disable-next-line
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
import { firstValueFrom } from "rxjs";
|
import { firstValueFrom } from "rxjs";
|
||||||
|
|
||||||
|
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||||
import { AuthService } from "@bitwarden/common/auth/abstractions/auth.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 { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
|
||||||
import { AppIdService } from "@bitwarden/common/platform/abstractions/app-id.service";
|
import { AppIdService } from "@bitwarden/common/platform/abstractions/app-id.service";
|
||||||
import { CryptoFunctionService } from "@bitwarden/common/platform/abstractions/crypto-function.service";
|
import { CryptoFunctionService } from "@bitwarden/common/platform/abstractions/crypto-function.service";
|
||||||
@ -71,6 +73,8 @@ export class NativeMessagingBackground {
|
|||||||
private validatingFingerprint: boolean;
|
private validatingFingerprint: boolean;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
|
private accountService: AccountService,
|
||||||
|
private masterPasswordService: InternalMasterPasswordServiceAbstraction,
|
||||||
private cryptoService: CryptoService,
|
private cryptoService: CryptoService,
|
||||||
private cryptoFunctionService: CryptoFunctionService,
|
private cryptoFunctionService: CryptoFunctionService,
|
||||||
private runtimeBackground: RuntimeBackground,
|
private runtimeBackground: RuntimeBackground,
|
||||||
@ -336,10 +340,14 @@ export class NativeMessagingBackground {
|
|||||||
) as UserKey;
|
) as UserKey;
|
||||||
await this.cryptoService.setUserKey(userKey);
|
await this.cryptoService.setUserKey(userKey);
|
||||||
} else if (message.keyB64) {
|
} 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
|
// 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)
|
// TODO: Remove after 2023.10 release (https://bitwarden.atlassian.net/browse/PM-3472)
|
||||||
let encUserKey = await this.stateService.getEncryptedCryptoSymmetricKey();
|
const encUserKeyPrim = await this.stateService.getEncryptedCryptoSymmetricKey();
|
||||||
encUserKey ||= await this.stateService.getMasterKeyEncryptedUserKey();
|
const encUserKey =
|
||||||
|
encUserKeyPrim != null
|
||||||
|
? new EncString(encUserKeyPrim)
|
||||||
|
: await this.masterPasswordService.getMasterKeyEncryptedUserKey(userId);
|
||||||
if (!encUserKey) {
|
if (!encUserKey) {
|
||||||
throw new Error("No encrypted user key found");
|
throw new Error("No encrypted user key found");
|
||||||
}
|
}
|
||||||
@ -348,9 +356,9 @@ export class NativeMessagingBackground {
|
|||||||
) as MasterKey;
|
) as MasterKey;
|
||||||
const userKey = await this.cryptoService.decryptUserKeyWithMasterKey(
|
const userKey = await this.cryptoService.decryptUserKeyWithMasterKey(
|
||||||
masterKey,
|
masterKey,
|
||||||
new EncString(encUserKey),
|
encUserKey,
|
||||||
);
|
);
|
||||||
await this.cryptoService.setMasterKey(masterKey);
|
await this.masterPasswordService.setMasterKey(masterKey, userId);
|
||||||
await this.cryptoService.setUserKey(userKey);
|
await this.cryptoService.setUserKey(userKey);
|
||||||
} else {
|
} else {
|
||||||
throw new Error("No key received");
|
throw new Error("No key received");
|
||||||
|
@ -14,12 +14,17 @@ import {
|
|||||||
logServiceFactory,
|
logServiceFactory,
|
||||||
LogServiceInitOptions,
|
LogServiceInitOptions,
|
||||||
} from "../../platform/background/service-factories/log-service.factory";
|
} from "../../platform/background/service-factories/log-service.factory";
|
||||||
|
import {
|
||||||
|
stateProviderFactory,
|
||||||
|
StateProviderInitOptions,
|
||||||
|
} from "../../platform/background/service-factories/state-provider.factory";
|
||||||
|
|
||||||
type SearchServiceFactoryOptions = FactoryOptions;
|
type SearchServiceFactoryOptions = FactoryOptions;
|
||||||
|
|
||||||
export type SearchServiceInitOptions = SearchServiceFactoryOptions &
|
export type SearchServiceInitOptions = SearchServiceFactoryOptions &
|
||||||
LogServiceInitOptions &
|
LogServiceInitOptions &
|
||||||
I18nServiceInitOptions;
|
I18nServiceInitOptions &
|
||||||
|
StateProviderInitOptions;
|
||||||
|
|
||||||
export function searchServiceFactory(
|
export function searchServiceFactory(
|
||||||
cache: { searchService?: AbstractSearchService } & CachedServices,
|
cache: { searchService?: AbstractSearchService } & CachedServices,
|
||||||
@ -33,6 +38,7 @@ export function searchServiceFactory(
|
|||||||
new SearchService(
|
new SearchService(
|
||||||
await logServiceFactory(cache, opts),
|
await logServiceFactory(cache, opts),
|
||||||
await i18nServiceFactory(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 { VaultTimeoutService as AbstractVaultTimeoutService } from "@bitwarden/common/abstractions/vault-timeout/vault-timeout.service";
|
||||||
|
|
||||||
|
import {
|
||||||
|
accountServiceFactory,
|
||||||
|
AccountServiceInitOptions,
|
||||||
|
} from "../../auth/background/service-factories/account-service.factory";
|
||||||
import {
|
import {
|
||||||
authServiceFactory,
|
authServiceFactory,
|
||||||
AuthServiceInitOptions,
|
AuthServiceInitOptions,
|
||||||
} from "../../auth/background/service-factories/auth-service.factory";
|
} from "../../auth/background/service-factories/auth-service.factory";
|
||||||
|
import {
|
||||||
|
internalMasterPasswordServiceFactory,
|
||||||
|
MasterPasswordServiceInitOptions,
|
||||||
|
} from "../../auth/background/service-factories/master-password-service.factory";
|
||||||
import {
|
import {
|
||||||
CryptoServiceInitOptions,
|
CryptoServiceInitOptions,
|
||||||
cryptoServiceFactory,
|
cryptoServiceFactory,
|
||||||
@ -57,6 +65,8 @@ type VaultTimeoutServiceFactoryOptions = FactoryOptions & {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export type VaultTimeoutServiceInitOptions = VaultTimeoutServiceFactoryOptions &
|
export type VaultTimeoutServiceInitOptions = VaultTimeoutServiceFactoryOptions &
|
||||||
|
AccountServiceInitOptions &
|
||||||
|
MasterPasswordServiceInitOptions &
|
||||||
CipherServiceInitOptions &
|
CipherServiceInitOptions &
|
||||||
FolderServiceInitOptions &
|
FolderServiceInitOptions &
|
||||||
CollectionServiceInitOptions &
|
CollectionServiceInitOptions &
|
||||||
@ -79,6 +89,8 @@ export function vaultTimeoutServiceFactory(
|
|||||||
opts,
|
opts,
|
||||||
async () =>
|
async () =>
|
||||||
new VaultTimeoutService(
|
new VaultTimeoutService(
|
||||||
|
await accountServiceFactory(cache, opts),
|
||||||
|
await internalMasterPasswordServiceFactory(cache, opts),
|
||||||
await cipherServiceFactory(cache, opts),
|
await cipherServiceFactory(cache, opts),
|
||||||
await folderServiceFactory(cache, opts),
|
await folderServiceFactory(cache, opts),
|
||||||
await collectionServiceFactory(cache, opts),
|
await collectionServiceFactory(cache, opts),
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
"manifest_version": 2,
|
"manifest_version": 2,
|
||||||
"name": "__MSG_extName__",
|
"name": "__MSG_extName__",
|
||||||
"short_name": "__MSG_appName__",
|
"short_name": "__MSG_appName__",
|
||||||
"version": "2024.3.1",
|
"version": "2024.4.1",
|
||||||
"description": "__MSG_extDesc__",
|
"description": "__MSG_extDesc__",
|
||||||
"default_locale": "en",
|
"default_locale": "en",
|
||||||
"author": "Bitwarden Inc.",
|
"author": "Bitwarden Inc.",
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
"minimum_chrome_version": "102.0",
|
"minimum_chrome_version": "102.0",
|
||||||
"name": "__MSG_extName__",
|
"name": "__MSG_extName__",
|
||||||
"short_name": "__MSG_appName__",
|
"short_name": "__MSG_appName__",
|
||||||
"version": "2024.3.1",
|
"version": "2024.4.1",
|
||||||
"description": "__MSG_extDesc__",
|
"description": "__MSG_extDesc__",
|
||||||
"default_locale": "en",
|
"default_locale": "en",
|
||||||
"author": "Bitwarden Inc.",
|
"author": "Bitwarden Inc.",
|
||||||
|
@ -4,6 +4,10 @@ import {
|
|||||||
AccountServiceInitOptions,
|
AccountServiceInitOptions,
|
||||||
accountServiceFactory,
|
accountServiceFactory,
|
||||||
} from "../../../auth/background/service-factories/account-service.factory";
|
} from "../../../auth/background/service-factories/account-service.factory";
|
||||||
|
import {
|
||||||
|
internalMasterPasswordServiceFactory,
|
||||||
|
MasterPasswordServiceInitOptions,
|
||||||
|
} from "../../../auth/background/service-factories/master-password-service.factory";
|
||||||
import {
|
import {
|
||||||
StateServiceInitOptions,
|
StateServiceInitOptions,
|
||||||
stateServiceFactory,
|
stateServiceFactory,
|
||||||
@ -34,6 +38,7 @@ import { StateProviderInitOptions, stateProviderFactory } from "./state-provider
|
|||||||
type CryptoServiceFactoryOptions = FactoryOptions;
|
type CryptoServiceFactoryOptions = FactoryOptions;
|
||||||
|
|
||||||
export type CryptoServiceInitOptions = CryptoServiceFactoryOptions &
|
export type CryptoServiceInitOptions = CryptoServiceFactoryOptions &
|
||||||
|
MasterPasswordServiceInitOptions &
|
||||||
KeyGenerationServiceInitOptions &
|
KeyGenerationServiceInitOptions &
|
||||||
CryptoFunctionServiceInitOptions &
|
CryptoFunctionServiceInitOptions &
|
||||||
EncryptServiceInitOptions &
|
EncryptServiceInitOptions &
|
||||||
@ -53,6 +58,7 @@ export function cryptoServiceFactory(
|
|||||||
opts,
|
opts,
|
||||||
async () =>
|
async () =>
|
||||||
new BrowserCryptoService(
|
new BrowserCryptoService(
|
||||||
|
await internalMasterPasswordServiceFactory(cache, opts),
|
||||||
await keyGenerationServiceFactory(cache, opts),
|
await keyGenerationServiceFactory(cache, opts),
|
||||||
await cryptoFunctionServiceFactory(cache, opts),
|
await cryptoFunctionServiceFactory(cache, opts),
|
||||||
await encryptServiceFactory(cache, opts),
|
await encryptServiceFactory(cache, opts),
|
||||||
|
@ -93,6 +93,10 @@ export class SessionSyncer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async update(serializedValue: any) {
|
async update(serializedValue: any) {
|
||||||
|
if (!serializedValue) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const unBuiltValue = JSON.parse(serializedValue);
|
const unBuiltValue = JSON.parse(serializedValue);
|
||||||
if (!BrowserApi.isManifestVersion(3) && BrowserApi.isBackgroundPage(self)) {
|
if (!BrowserApi.isManifestVersion(3) && BrowserApi.isBackgroundPage(self)) {
|
||||||
await this.memoryStorageService.save(this.metaData.sessionKey, serializedValue);
|
await this.memoryStorageService.save(this.metaData.sessionKey, serializedValue);
|
||||||
@ -104,6 +108,10 @@ export class SessionSyncer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async updateSession(value: any) {
|
private async updateSession(value: any) {
|
||||||
|
if (!value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const serializedValue = JSON.stringify(value);
|
const serializedValue = JSON.stringify(value);
|
||||||
if (BrowserApi.isManifestVersion(3) || BrowserApi.isBackgroundPage(self)) {
|
if (BrowserApi.isManifestVersion(3) || BrowserApi.isBackgroundPage(self)) {
|
||||||
await this.memoryStorageService.save(this.metaData.sessionKey, serializedValue);
|
await this.memoryStorageService.save(this.metaData.sessionKey, serializedValue);
|
||||||
|
@ -10,6 +10,8 @@ import { fromChromeEvent } from "../../browser/from-chrome-event";
|
|||||||
|
|
||||||
export const serializationIndicator = "__json__";
|
export const serializationIndicator = "__json__";
|
||||||
|
|
||||||
|
type serializedObject = { [serializationIndicator]: true; value: string };
|
||||||
|
|
||||||
export const objToStore = (obj: any) => {
|
export const objToStore = (obj: any) => {
|
||||||
if (obj == null) {
|
if (obj == null) {
|
||||||
return null;
|
return null;
|
||||||
@ -61,11 +63,7 @@ export default abstract class AbstractChromeStorageService
|
|||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
this.chromeStorageApi.get(key, (obj: any) => {
|
this.chromeStorageApi.get(key, (obj: any) => {
|
||||||
if (obj != null && obj[key] != null) {
|
if (obj != null && obj[key] != null) {
|
||||||
let value = obj[key];
|
resolve(this.processGetObject(obj[key]));
|
||||||
if (value[serializationIndicator] && typeof value.value === "string") {
|
|
||||||
value = JSON.parse(value.value);
|
|
||||||
}
|
|
||||||
resolve(value as T);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
resolve(null);
|
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", () => {
|
describe("get", () => {
|
||||||
let getMock: jest.Mock;
|
let getMock: jest.Mock;
|
||||||
|
const key = "key";
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
// setup get
|
// setup get
|
||||||
@ -76,7 +77,6 @@ describe("ChromeStorageApiService", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("returns a stored value when it is serialized", async () => {
|
it("returns a stored value when it is serialized", async () => {
|
||||||
const key = "key";
|
|
||||||
const value = { key: "value" };
|
const value = { key: "value" };
|
||||||
store[key] = objToStore(value);
|
store[key] = objToStore(value);
|
||||||
const result = await service.get(key);
|
const result = await service.get(key);
|
||||||
@ -84,7 +84,6 @@ describe("ChromeStorageApiService", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("returns a stored value when it is not serialized", async () => {
|
it("returns a stored value when it is not serialized", async () => {
|
||||||
const key = "key";
|
|
||||||
const value = "value";
|
const value = "value";
|
||||||
store[key] = value;
|
store[key] = value;
|
||||||
const result = await service.get(key);
|
const result = await service.get(key);
|
||||||
@ -95,5 +94,12 @@ describe("ChromeStorageApiService", () => {
|
|||||||
const result = await service.get("key");
|
const result = await service.get("key");
|
||||||
expect(result).toBeNull();
|
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 { firstValueFrom } from "rxjs";
|
||||||
|
|
||||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
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 { CryptoFunctionService } from "@bitwarden/common/platform/abstractions/crypto-function.service";
|
||||||
import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service";
|
import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service";
|
||||||
import { KeyGenerationService } from "@bitwarden/common/platform/abstractions/key-generation.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 {
|
export class BrowserCryptoService extends CryptoService {
|
||||||
constructor(
|
constructor(
|
||||||
|
masterPasswordService: InternalMasterPasswordServiceAbstraction,
|
||||||
keyGenerationService: KeyGenerationService,
|
keyGenerationService: KeyGenerationService,
|
||||||
cryptoFunctionService: CryptoFunctionService,
|
cryptoFunctionService: CryptoFunctionService,
|
||||||
encryptService: EncryptService,
|
encryptService: EncryptService,
|
||||||
@ -28,6 +30,7 @@ export class BrowserCryptoService extends CryptoService {
|
|||||||
private biometricStateService: BiometricStateService,
|
private biometricStateService: BiometricStateService,
|
||||||
) {
|
) {
|
||||||
super(
|
super(
|
||||||
|
masterPasswordService,
|
||||||
keyGenerationService,
|
keyGenerationService,
|
||||||
cryptoFunctionService,
|
cryptoFunctionService,
|
||||||
encryptService,
|
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() {
|
constructor() {
|
||||||
super(chrome.storage.local);
|
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 { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||||
|
import { StateProvider } from "@bitwarden/common/platform/state";
|
||||||
import { SearchService } from "@bitwarden/common/services/search.service";
|
import { SearchService } from "@bitwarden/common/services/search.service";
|
||||||
|
|
||||||
export class PopupSearchService extends SearchService {
|
export class PopupSearchService extends SearchService {
|
||||||
constructor(
|
constructor(logService: LogService, i18nService: I18nService, stateProvider: StateProvider) {
|
||||||
private mainSearchService: SearchService,
|
super(logService, i18nService, stateProvider);
|
||||||
logService: LogService,
|
|
||||||
i18nService: I18nService,
|
|
||||||
) {
|
|
||||||
super(logService, i18nService);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
clearIndex() {
|
clearIndex(): Promise<void> {
|
||||||
throw new Error("Not available.");
|
throw new Error("Not available.");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -19,7 +16,7 @@ export class PopupSearchService extends SearchService {
|
|||||||
throw new Error("Not available.");
|
throw new Error("Not available.");
|
||||||
}
|
}
|
||||||
|
|
||||||
getIndexForSearch() {
|
async getIndexForSearch() {
|
||||||
return this.mainSearchService.getIndexForSearch();
|
return await super.getIndexForSearch();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -62,6 +62,7 @@ import { StateService as BaseStateServiceAbstraction } from "@bitwarden/common/p
|
|||||||
import {
|
import {
|
||||||
AbstractMemoryStorageService,
|
AbstractMemoryStorageService,
|
||||||
AbstractStorageService,
|
AbstractStorageService,
|
||||||
|
ObservableStorageService,
|
||||||
} from "@bitwarden/common/platform/abstractions/storage.service";
|
} from "@bitwarden/common/platform/abstractions/storage.service";
|
||||||
import { StateFactory } from "@bitwarden/common/platform/factories/state-factory";
|
import { StateFactory } from "@bitwarden/common/platform/factories/state-factory";
|
||||||
import { GlobalState } from "@bitwarden/common/platform/models/domain/global-state";
|
import { GlobalState } from "@bitwarden/common/platform/models/domain/global-state";
|
||||||
@ -74,17 +75,15 @@ import {
|
|||||||
GlobalStateProvider,
|
GlobalStateProvider,
|
||||||
StateProvider,
|
StateProvider,
|
||||||
} from "@bitwarden/common/platform/state";
|
} from "@bitwarden/common/platform/state";
|
||||||
import { SearchService } from "@bitwarden/common/services/search.service";
|
|
||||||
import { PasswordGenerationServiceAbstraction } from "@bitwarden/common/tools/generator/password";
|
import { PasswordGenerationServiceAbstraction } from "@bitwarden/common/tools/generator/password";
|
||||||
import { UsernameGenerationServiceAbstraction } from "@bitwarden/common/tools/generator/username";
|
import { UsernameGenerationServiceAbstraction } from "@bitwarden/common/tools/generator/username";
|
||||||
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
|
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
|
||||||
import { CollectionService } from "@bitwarden/common/vault/abstractions/collection.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 { FolderService as FolderServiceAbstraction } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction";
|
||||||
import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.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 { DialogService } from "@bitwarden/components";
|
||||||
import { VaultExportServiceAbstraction } from "@bitwarden/vault-export-core";
|
|
||||||
|
|
||||||
import { UnauthGuardService } from "../../auth/popup/services";
|
import { UnauthGuardService } from "../../auth/popup/services";
|
||||||
import { AutofillService as AutofillServiceAbstraction } from "../../autofill/services/abstractions/autofill.service";
|
import { AutofillService as AutofillServiceAbstraction } from "../../autofill/services/abstractions/autofill.service";
|
||||||
@ -159,7 +158,7 @@ const safeProviders: SafeProvider[] = [
|
|||||||
safeProvider({
|
safeProvider({
|
||||||
provide: MessagingService,
|
provide: MessagingService,
|
||||||
useFactory: () => {
|
useFactory: () => {
|
||||||
return needsBackgroundInit
|
return needsBackgroundInit && BrowserApi.isManifestVersion(2)
|
||||||
? new BrowserMessagingPrivateModePopupService()
|
? new BrowserMessagingPrivateModePopupService()
|
||||||
: new BrowserMessagingService();
|
: new BrowserMessagingService();
|
||||||
},
|
},
|
||||||
@ -187,19 +186,8 @@ const safeProviders: SafeProvider[] = [
|
|||||||
}),
|
}),
|
||||||
safeProvider({
|
safeProvider({
|
||||||
provide: SearchServiceAbstraction,
|
provide: SearchServiceAbstraction,
|
||||||
useFactory: (logService: LogService, i18nService: I18nServiceAbstraction) => {
|
useClass: PopupSearchService,
|
||||||
return new PopupSearchService(
|
deps: [LogService, I18nServiceAbstraction, StateProvider],
|
||||||
getBgService<SearchService>("searchService")(),
|
|
||||||
logService,
|
|
||||||
i18nService,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
deps: [LogService, I18nServiceAbstraction],
|
|
||||||
}),
|
|
||||||
safeProvider({
|
|
||||||
provide: CipherFileUploadService,
|
|
||||||
useFactory: getBgService<CipherFileUploadService>("cipherFileUploadService"),
|
|
||||||
deps: [],
|
|
||||||
}),
|
}),
|
||||||
safeProvider({
|
safeProvider({
|
||||||
provide: CipherService,
|
provide: CipherService,
|
||||||
@ -231,11 +219,6 @@ const safeProviders: SafeProvider[] = [
|
|||||||
useClass: BrowserEnvironmentService,
|
useClass: BrowserEnvironmentService,
|
||||||
deps: [LogService, StateProvider, AccountServiceAbstraction],
|
deps: [LogService, StateProvider, AccountServiceAbstraction],
|
||||||
}),
|
}),
|
||||||
safeProvider({
|
|
||||||
provide: TotpService,
|
|
||||||
useFactory: getBgService<TotpService>("totpService"),
|
|
||||||
deps: [],
|
|
||||||
}),
|
|
||||||
safeProvider({
|
safeProvider({
|
||||||
provide: I18nServiceAbstraction,
|
provide: I18nServiceAbstraction,
|
||||||
useFactory: (globalStateProvider: GlobalStateProvider) => {
|
useFactory: (globalStateProvider: GlobalStateProvider) => {
|
||||||
@ -252,6 +235,11 @@ const safeProviders: SafeProvider[] = [
|
|||||||
},
|
},
|
||||||
deps: [EncryptService],
|
deps: [EncryptService],
|
||||||
}),
|
}),
|
||||||
|
safeProvider({
|
||||||
|
provide: TotpServiceAbstraction,
|
||||||
|
useClass: TotpService,
|
||||||
|
deps: [CryptoFunctionService, LogService],
|
||||||
|
}),
|
||||||
safeProvider({
|
safeProvider({
|
||||||
provide: AuthRequestServiceAbstraction,
|
provide: AuthRequestServiceAbstraction,
|
||||||
useFactory: getBgService<AuthRequestServiceAbstraction>("authRequestService"),
|
useFactory: getBgService<AuthRequestServiceAbstraction>("authRequestService"),
|
||||||
@ -325,7 +313,7 @@ const safeProviders: SafeProvider[] = [
|
|||||||
deps: [
|
deps: [
|
||||||
CipherService,
|
CipherService,
|
||||||
AutofillSettingsServiceAbstraction,
|
AutofillSettingsServiceAbstraction,
|
||||||
TotpService,
|
TotpServiceAbstraction,
|
||||||
EventCollectionServiceAbstraction,
|
EventCollectionServiceAbstraction,
|
||||||
LogService,
|
LogService,
|
||||||
DomainSettingsService,
|
DomainSettingsService,
|
||||||
@ -333,11 +321,6 @@ const safeProviders: SafeProvider[] = [
|
|||||||
BillingAccountProfileStateService,
|
BillingAccountProfileStateService,
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
safeProvider({
|
|
||||||
provide: VaultExportServiceAbstraction,
|
|
||||||
useFactory: getBgService<VaultExportServiceAbstraction>("exportService"),
|
|
||||||
deps: [],
|
|
||||||
}),
|
|
||||||
safeProvider({
|
safeProvider({
|
||||||
provide: KeyConnectorService,
|
provide: KeyConnectorService,
|
||||||
useFactory: getBgService<KeyConnectorService>("keyConnectorService"),
|
useFactory: getBgService<KeyConnectorService>("keyConnectorService"),
|
||||||
@ -387,7 +370,15 @@ const safeProviders: SafeProvider[] = [
|
|||||||
}),
|
}),
|
||||||
safeProvider({
|
safeProvider({
|
||||||
provide: OBSERVABLE_MEMORY_STORAGE,
|
provide: OBSERVABLE_MEMORY_STORAGE,
|
||||||
useClass: ForegroundMemoryStorageService,
|
useFactory: () => {
|
||||||
|
if (BrowserApi.isManifestVersion(2)) {
|
||||||
|
return new ForegroundMemoryStorageService();
|
||||||
|
}
|
||||||
|
|
||||||
|
return getBgService<AbstractStorageService & ObservableStorageService>(
|
||||||
|
"memoryStorageForStateProviders",
|
||||||
|
)();
|
||||||
|
},
|
||||||
deps: [],
|
deps: [],
|
||||||
}),
|
}),
|
||||||
safeProvider({
|
safeProvider({
|
||||||
|
@ -171,9 +171,7 @@ export class SendGroupingsComponent extends BaseSendComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
showSearching() {
|
showSearching() {
|
||||||
return (
|
return this.hasSearched || (!this.searchPending && this.isSearchable);
|
||||||
this.hasSearched || (!this.searchPending && this.searchService.isSearchable(this.searchText))
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private calculateTypeCounts() {
|
private calculateTypeCounts() {
|
||||||
|
@ -6,7 +6,7 @@ import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abs
|
|||||||
import { EventType } from "@bitwarden/common/enums";
|
import { EventType } from "@bitwarden/common/enums";
|
||||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.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 { CipherType } from "@bitwarden/common/vault/enums";
|
||||||
import { CipherRepromptType } from "@bitwarden/common/vault/enums/cipher-reprompt-type";
|
import { CipherRepromptType } from "@bitwarden/common/vault/enums/cipher-reprompt-type";
|
||||||
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
|
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
|
||||||
@ -31,7 +31,7 @@ export class ActionButtonsComponent implements OnInit, OnDestroy {
|
|||||||
private i18nService: I18nService,
|
private i18nService: I18nService,
|
||||||
private platformUtilsService: PlatformUtilsService,
|
private platformUtilsService: PlatformUtilsService,
|
||||||
private eventCollectionService: EventCollectionService,
|
private eventCollectionService: EventCollectionService,
|
||||||
private totpService: TotpService,
|
private totpService: TotpServiceAbstraction,
|
||||||
private passwordRepromptService: PasswordRepromptService,
|
private passwordRepromptService: PasswordRepromptService,
|
||||||
private billingAccountProfileStateService: BillingAccountProfileStateService,
|
private billingAccountProfileStateService: BillingAccountProfileStateService,
|
||||||
) {}
|
) {}
|
||||||
|
@ -311,7 +311,7 @@ export class Fido2Component implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected async search() {
|
protected async search() {
|
||||||
this.hasSearched = this.searchService.isSearchable(this.searchText);
|
this.hasSearched = await this.searchService.isSearchable(this.searchText);
|
||||||
this.searchPending = true;
|
this.searchPending = true;
|
||||||
if (this.hasSearched) {
|
if (this.hasSearched) {
|
||||||
this.displayedCiphers = await this.searchService.searchCiphers(
|
this.displayedCiphers = await this.searchService.searchCiphers(
|
||||||
|
@ -36,19 +36,32 @@
|
|||||||
</div>
|
</div>
|
||||||
<ng-container *ngIf="loaded">
|
<ng-container *ngIf="loaded">
|
||||||
<app-vault-select (onVaultSelectionChanged)="load()"></app-vault-select>
|
<app-vault-select (onVaultSelectionChanged)="load()"></app-vault-select>
|
||||||
<app-callout *ngIf="showHowToAutofill" type="info" title="{{ 'howToAutofill' | i18n }}">
|
<app-callout
|
||||||
<p>{{ autofillCalloutText }}</p>
|
*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
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="btn primary callout-half"
|
class="btn primary callout-half"
|
||||||
appStopClick
|
appStopClick
|
||||||
(click)="dismissCallout()"
|
(click)="unassignedItemsBannerService.hideBanner()"
|
||||||
>
|
>
|
||||||
{{ "gotIt" | i18n }}
|
{{ "gotIt" | i18n }}
|
||||||
</button>
|
</button>
|
||||||
<button type="button" class="btn callout-half" appStopClick (click)="goToSettings()">
|
|
||||||
{{ "autofillSettings" | i18n }}
|
|
||||||
</button>
|
|
||||||
</app-callout>
|
</app-callout>
|
||||||
<div class="box list" *ngIf="loginCiphers">
|
<div class="box list" *ngIf="loginCiphers">
|
||||||
<h2 class="box-header">
|
<h2 class="box-header">
|
||||||
|
@ -1,13 +1,16 @@
|
|||||||
import { ChangeDetectorRef, Component, NgZone, OnDestroy, OnInit } from "@angular/core";
|
import { ChangeDetectorRef, Component, NgZone, OnDestroy, OnInit } from "@angular/core";
|
||||||
import { Router } from "@angular/router";
|
import { Router } from "@angular/router";
|
||||||
import { Subject, firstValueFrom } from "rxjs";
|
import { Subject, firstValueFrom, from } from "rxjs";
|
||||||
import { debounceTime, takeUntil } from "rxjs/operators";
|
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 { SearchService } from "@bitwarden/common/abstractions/search.service";
|
||||||
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
|
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
|
||||||
import { AutofillOverlayVisibility } from "@bitwarden/common/autofill/constants";
|
import { AutofillOverlayVisibility } from "@bitwarden/common/autofill/constants";
|
||||||
import { AutofillSettingsServiceAbstraction } from "@bitwarden/common/autofill/services/autofill-settings.service";
|
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 { 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 { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||||
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
||||||
@ -53,6 +56,11 @@ export class CurrentTabComponent implements OnInit, OnDestroy {
|
|||||||
private totpTimeout: number;
|
private totpTimeout: number;
|
||||||
private loadedTimeout: number;
|
private loadedTimeout: number;
|
||||||
private searchTimeout: number;
|
private searchTimeout: number;
|
||||||
|
private initPageDetailsTimeout: number;
|
||||||
|
|
||||||
|
protected unassignedItemsBannerEnabled$ = this.configService.getFeatureFlag$(
|
||||||
|
FeatureFlag.UnassignedItemsBanner,
|
||||||
|
);
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private platformUtilsService: PlatformUtilsService,
|
private platformUtilsService: PlatformUtilsService,
|
||||||
@ -70,6 +78,8 @@ export class CurrentTabComponent implements OnInit, OnDestroy {
|
|||||||
private organizationService: OrganizationService,
|
private organizationService: OrganizationService,
|
||||||
private vaultFilterService: VaultFilterService,
|
private vaultFilterService: VaultFilterService,
|
||||||
private vaultSettingsService: VaultSettingsService,
|
private vaultSettingsService: VaultSettingsService,
|
||||||
|
private configService: ConfigService,
|
||||||
|
protected unassignedItemsBannerService: UnassignedItemsBannerService,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async ngOnInit() {
|
async ngOnInit() {
|
||||||
@ -120,8 +130,14 @@ export class CurrentTabComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.search$
|
this.search$
|
||||||
.pipe(debounceTime(500), takeUntil(this.destroy$))
|
.pipe(
|
||||||
.subscribe(() => this.searchVault());
|
debounceTime(500),
|
||||||
|
switchMap(() => {
|
||||||
|
return from(this.searchVault());
|
||||||
|
}),
|
||||||
|
takeUntil(this.destroy$),
|
||||||
|
)
|
||||||
|
.subscribe();
|
||||||
|
|
||||||
const autofillOnPageLoadOrgPolicy = await firstValueFrom(
|
const autofillOnPageLoadOrgPolicy = await firstValueFrom(
|
||||||
this.autofillSettingsService.activateAutofillOnPageLoadFromPolicy$,
|
this.autofillSettingsService.activateAutofillOnPageLoadFromPolicy$,
|
||||||
@ -232,14 +248,12 @@ export class CurrentTabComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
searchVault() {
|
async searchVault() {
|
||||||
if (!this.searchService.isSearchable(this.searchText)) {
|
if (!(await this.searchService.isSearchable(this.searchText))) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
|
await this.router.navigate(["/tabs/vault"], { queryParams: { searchText: this.searchText } });
|
||||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
||||||
this.router.navigate(["/tabs/vault"], { queryParams: { searchText: this.searchText } });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
closeOnEsc(e: KeyboardEvent) {
|
closeOnEsc(e: KeyboardEvent) {
|
||||||
@ -303,18 +317,13 @@ export class CurrentTabComponent implements OnInit, OnDestroy {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (this.loginCiphers.length) {
|
if (this.loginCiphers.length) {
|
||||||
void BrowserApi.tabSendMessage(this.tab, {
|
|
||||||
command: "collectPageDetails",
|
|
||||||
tab: this.tab,
|
|
||||||
sender: BroadcasterSubscriptionId,
|
|
||||||
});
|
|
||||||
|
|
||||||
this.loginCiphers = this.loginCiphers.sort((a, b) =>
|
this.loginCiphers = this.loginCiphers.sort((a, b) =>
|
||||||
this.cipherService.sortCiphersByLastUsedThenName(a, b),
|
this.cipherService.sortCiphersByLastUsedThenName(a, b),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.isLoading = this.loaded = true;
|
this.isLoading = this.loaded = true;
|
||||||
|
this.collectTabPageDetails();
|
||||||
}
|
}
|
||||||
|
|
||||||
async goToSettings() {
|
async goToSettings() {
|
||||||
@ -352,4 +361,19 @@ export class CurrentTabComponent implements OnInit, OnDestroy {
|
|||||||
this.autofillCalloutText = this.i18nService.t("autofillSelectInfoWithoutCommand");
|
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 { Location } from "@angular/common";
|
||||||
import { ChangeDetectorRef, Component, NgZone, OnDestroy, OnInit } from "@angular/core";
|
import { ChangeDetectorRef, Component, NgZone, OnDestroy, OnInit } from "@angular/core";
|
||||||
import { ActivatedRoute, Router } from "@angular/router";
|
import { ActivatedRoute, Router } from "@angular/router";
|
||||||
import { firstValueFrom } from "rxjs";
|
import { BehaviorSubject, Subject, firstValueFrom, from } from "rxjs";
|
||||||
import { first } from "rxjs/operators";
|
import { first, switchMap, takeUntil } from "rxjs/operators";
|
||||||
|
|
||||||
import { VaultFilter } from "@bitwarden/angular/vault/vault-filter/models/vault-filter.model";
|
import { VaultFilter } from "@bitwarden/angular/vault/vault-filter/models/vault-filter.model";
|
||||||
import { SearchService } from "@bitwarden/common/abstractions/search.service";
|
import { SearchService } from "@bitwarden/common/abstractions/search.service";
|
||||||
@ -53,7 +53,6 @@ export class VaultFilterComponent implements OnInit, OnDestroy {
|
|||||||
folderCounts = new Map<string, number>();
|
folderCounts = new Map<string, number>();
|
||||||
collectionCounts = new Map<string, number>();
|
collectionCounts = new Map<string, number>();
|
||||||
typeCounts = new Map<CipherType, number>();
|
typeCounts = new Map<CipherType, number>();
|
||||||
searchText: string;
|
|
||||||
state: BrowserGroupingsComponentState;
|
state: BrowserGroupingsComponentState;
|
||||||
showLeftHeader = true;
|
showLeftHeader = true;
|
||||||
searchPending = false;
|
searchPending = false;
|
||||||
@ -71,6 +70,16 @@ export class VaultFilterComponent implements OnInit, OnDestroy {
|
|||||||
private hasSearched = false;
|
private hasSearched = false;
|
||||||
private hasLoadedAllCiphers = false;
|
private hasLoadedAllCiphers = false;
|
||||||
private allCiphers: CipherView[] = null;
|
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(
|
constructor(
|
||||||
private i18nService: I18nService,
|
private i18nService: I18nService,
|
||||||
@ -148,6 +157,15 @@ export class VaultFilterComponent implements OnInit, OnDestroy {
|
|||||||
BrowserPopupUtils.setContentScrollY(window, this.state?.scrollY);
|
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() {
|
ngOnDestroy() {
|
||||||
@ -161,6 +179,8 @@ export class VaultFilterComponent implements OnInit, OnDestroy {
|
|||||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||||
this.saveState();
|
this.saveState();
|
||||||
this.broadcasterService.unsubscribe(ComponentId);
|
this.broadcasterService.unsubscribe(ComponentId);
|
||||||
|
this.destroy$.next();
|
||||||
|
this.destroy$.complete();
|
||||||
}
|
}
|
||||||
|
|
||||||
async load() {
|
async load() {
|
||||||
@ -181,7 +201,7 @@ export class VaultFilterComponent implements OnInit, OnDestroy {
|
|||||||
async loadCiphers() {
|
async loadCiphers() {
|
||||||
this.allCiphers = await this.cipherService.getAllDecrypted();
|
this.allCiphers = await this.cipherService.getAllDecrypted();
|
||||||
if (!this.hasLoadedAllCiphers) {
|
if (!this.hasLoadedAllCiphers) {
|
||||||
this.hasLoadedAllCiphers = !this.searchService.isSearchable(this.searchText);
|
this.hasLoadedAllCiphers = !(await this.searchService.isSearchable(this.searchText));
|
||||||
}
|
}
|
||||||
await this.search(null);
|
await this.search(null);
|
||||||
this.getCounts();
|
this.getCounts();
|
||||||
@ -210,7 +230,7 @@ export class VaultFilterComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
const filterDeleted = (c: CipherView) => !c.isDeleted;
|
const filterDeleted = (c: CipherView) => !c.isDeleted;
|
||||||
if (timeout == null) {
|
if (timeout == null) {
|
||||||
this.hasSearched = this.searchService.isSearchable(this.searchText);
|
this.hasSearched = this.isSearchable;
|
||||||
this.ciphers = await this.searchService.searchCiphers(
|
this.ciphers = await this.searchService.searchCiphers(
|
||||||
this.searchText,
|
this.searchText,
|
||||||
filterDeleted,
|
filterDeleted,
|
||||||
@ -223,7 +243,7 @@ export class VaultFilterComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
this.searchPending = true;
|
this.searchPending = true;
|
||||||
this.searchTimeout = setTimeout(async () => {
|
this.searchTimeout = setTimeout(async () => {
|
||||||
this.hasSearched = this.searchService.isSearchable(this.searchText);
|
this.hasSearched = this.isSearchable;
|
||||||
if (!this.hasLoadedAllCiphers && !this.hasSearched) {
|
if (!this.hasLoadedAllCiphers && !this.hasSearched) {
|
||||||
await this.loadCiphers();
|
await this.loadCiphers();
|
||||||
} else {
|
} else {
|
||||||
@ -381,9 +401,7 @@ export class VaultFilterComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
showSearching() {
|
showSearching() {
|
||||||
return (
|
return this.hasSearched || (!this.searchPending && this.isSearchable);
|
||||||
this.hasSearched || (!this.searchPending && this.searchService.isSearchable(this.searchText))
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
closeOnEsc(e: KeyboardEvent) {
|
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 { StateService } from "@bitwarden/common/platform/abstractions/state.service";
|
||||||
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
|
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
|
||||||
import { FolderService } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction";
|
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 { CipherType } from "@bitwarden/common/vault/enums";
|
||||||
import { Cipher } from "@bitwarden/common/vault/models/domain/cipher";
|
import { Cipher } from "@bitwarden/common/vault/models/domain/cipher";
|
||||||
import { LoginUriView } from "@bitwarden/common/vault/models/view/login-uri.view";
|
import { LoginUriView } from "@bitwarden/common/vault/models/view/login-uri.view";
|
||||||
@ -74,7 +74,7 @@ export class ViewComponent extends BaseViewComponent {
|
|||||||
constructor(
|
constructor(
|
||||||
cipherService: CipherService,
|
cipherService: CipherService,
|
||||||
folderService: FolderService,
|
folderService: FolderService,
|
||||||
totpService: TotpService,
|
totpService: TotpServiceAbstraction,
|
||||||
tokenService: TokenService,
|
tokenService: TokenService,
|
||||||
i18nService: I18nService,
|
i18nService: I18nService,
|
||||||
cryptoService: CryptoService,
|
cryptoService: CryptoService,
|
||||||
|
@ -1,6 +1,10 @@
|
|||||||
|
import { firstValueFrom } from "rxjs";
|
||||||
|
|
||||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||||
import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.service.abstraction";
|
import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.service.abstraction";
|
||||||
|
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||||
import { KeyConnectorService } from "@bitwarden/common/auth/abstractions/key-connector.service";
|
import { KeyConnectorService } from "@bitwarden/common/auth/abstractions/key-connector.service";
|
||||||
|
import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/auth/abstractions/master-password.service.abstraction";
|
||||||
import { SecretVerificationRequest } from "@bitwarden/common/auth/models/request/secret-verification.request";
|
import { SecretVerificationRequest } from "@bitwarden/common/auth/models/request/secret-verification.request";
|
||||||
import { CryptoFunctionService } from "@bitwarden/common/platform/abstractions/crypto-function.service";
|
import { CryptoFunctionService } from "@bitwarden/common/platform/abstractions/crypto-function.service";
|
||||||
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
|
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
|
||||||
@ -18,6 +22,8 @@ import { CliUtils } from "../../utils";
|
|||||||
|
|
||||||
export class UnlockCommand {
|
export class UnlockCommand {
|
||||||
constructor(
|
constructor(
|
||||||
|
private accountService: AccountService,
|
||||||
|
private masterPasswordService: InternalMasterPasswordServiceAbstraction,
|
||||||
private cryptoService: CryptoService,
|
private cryptoService: CryptoService,
|
||||||
private stateService: StateService,
|
private stateService: StateService,
|
||||||
private cryptoFunctionService: CryptoFunctionService,
|
private cryptoFunctionService: CryptoFunctionService,
|
||||||
@ -45,11 +51,14 @@ export class UnlockCommand {
|
|||||||
const kdf = await this.stateService.getKdfType();
|
const kdf = await this.stateService.getKdfType();
|
||||||
const kdfConfig = await this.stateService.getKdfConfig();
|
const kdfConfig = await this.stateService.getKdfConfig();
|
||||||
const masterKey = await this.cryptoService.makeMasterKey(password, email, kdf, kdfConfig);
|
const masterKey = await this.cryptoService.makeMasterKey(password, email, kdf, kdfConfig);
|
||||||
const storedKeyHash = await this.cryptoService.getMasterKeyHash();
|
const userId = (await firstValueFrom(this.accountService.activeAccount$))?.id;
|
||||||
|
const storedMasterKeyHash = await firstValueFrom(
|
||||||
|
this.masterPasswordService.masterKeyHash$(userId),
|
||||||
|
);
|
||||||
|
|
||||||
let passwordValid = false;
|
let passwordValid = false;
|
||||||
if (masterKey != null) {
|
if (masterKey != null) {
|
||||||
if (storedKeyHash != null) {
|
if (storedMasterKeyHash != null) {
|
||||||
passwordValid = await this.cryptoService.compareAndUpdateKeyHash(password, masterKey);
|
passwordValid = await this.cryptoService.compareAndUpdateKeyHash(password, masterKey);
|
||||||
} else {
|
} else {
|
||||||
const serverKeyHash = await this.cryptoService.hashMasterKey(
|
const serverKeyHash = await this.cryptoService.hashMasterKey(
|
||||||
@ -67,7 +76,7 @@ export class UnlockCommand {
|
|||||||
masterKey,
|
masterKey,
|
||||||
HashPurpose.LocalAuthorization,
|
HashPurpose.LocalAuthorization,
|
||||||
);
|
);
|
||||||
await this.cryptoService.setMasterKeyHash(localKeyHash);
|
await this.masterPasswordService.setMasterKeyHash(localKeyHash, userId);
|
||||||
} catch {
|
} catch {
|
||||||
// Ignore
|
// Ignore
|
||||||
}
|
}
|
||||||
@ -75,7 +84,7 @@ export class UnlockCommand {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (passwordValid) {
|
if (passwordValid) {
|
||||||
await this.cryptoService.setMasterKey(masterKey);
|
await this.masterPasswordService.setMasterKey(masterKey, userId);
|
||||||
const userKey = await this.cryptoService.decryptUserKeyWithMasterKey(masterKey);
|
const userKey = await this.cryptoService.decryptUserKeyWithMasterKey(masterKey);
|
||||||
await this.cryptoService.setUserKey(userKey);
|
await this.cryptoService.setUserKey(userKey);
|
||||||
|
|
||||||
|
@ -28,6 +28,7 @@ import { AccountService } from "@bitwarden/common/auth/abstractions/account.serv
|
|||||||
import { AvatarService as AvatarServiceAbstraction } from "@bitwarden/common/auth/abstractions/avatar.service";
|
import { AvatarService as AvatarServiceAbstraction } from "@bitwarden/common/auth/abstractions/avatar.service";
|
||||||
import { DeviceTrustCryptoServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust-crypto.service.abstraction";
|
import { DeviceTrustCryptoServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust-crypto.service.abstraction";
|
||||||
import { DevicesApiServiceAbstraction } from "@bitwarden/common/auth/abstractions/devices-api.service.abstraction";
|
import { DevicesApiServiceAbstraction } from "@bitwarden/common/auth/abstractions/devices-api.service.abstraction";
|
||||||
|
import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/auth/abstractions/master-password.service.abstraction";
|
||||||
import { AccountServiceImplementation } from "@bitwarden/common/auth/services/account.service";
|
import { AccountServiceImplementation } from "@bitwarden/common/auth/services/account.service";
|
||||||
import { AuthService } from "@bitwarden/common/auth/services/auth.service";
|
import { AuthService } from "@bitwarden/common/auth/services/auth.service";
|
||||||
import { AvatarService } from "@bitwarden/common/auth/services/avatar.service";
|
import { AvatarService } from "@bitwarden/common/auth/services/avatar.service";
|
||||||
@ -168,6 +169,7 @@ export class Main {
|
|||||||
organizationUserService: OrganizationUserService;
|
organizationUserService: OrganizationUserService;
|
||||||
collectionService: CollectionService;
|
collectionService: CollectionService;
|
||||||
vaultTimeoutService: VaultTimeoutService;
|
vaultTimeoutService: VaultTimeoutService;
|
||||||
|
masterPasswordService: InternalMasterPasswordServiceAbstraction;
|
||||||
vaultTimeoutSettingsService: VaultTimeoutSettingsService;
|
vaultTimeoutSettingsService: VaultTimeoutSettingsService;
|
||||||
syncService: SyncService;
|
syncService: SyncService;
|
||||||
eventCollectionService: EventCollectionServiceAbstraction;
|
eventCollectionService: EventCollectionServiceAbstraction;
|
||||||
@ -352,6 +354,7 @@ export class Main {
|
|||||||
);
|
);
|
||||||
|
|
||||||
this.cryptoService = new CryptoService(
|
this.cryptoService = new CryptoService(
|
||||||
|
this.masterPasswordService,
|
||||||
this.keyGenerationService,
|
this.keyGenerationService,
|
||||||
this.cryptoFunctionService,
|
this.cryptoFunctionService,
|
||||||
this.encryptService,
|
this.encryptService,
|
||||||
@ -411,7 +414,7 @@ export class Main {
|
|||||||
this.sendService,
|
this.sendService,
|
||||||
);
|
);
|
||||||
|
|
||||||
this.searchService = new SearchService(this.logService, this.i18nService);
|
this.searchService = new SearchService(this.logService, this.i18nService, this.stateProvider);
|
||||||
|
|
||||||
this.broadcasterService = new BroadcasterService();
|
this.broadcasterService = new BroadcasterService();
|
||||||
|
|
||||||
@ -432,6 +435,8 @@ export class Main {
|
|||||||
this.policyApiService = new PolicyApiService(this.policyService, this.apiService);
|
this.policyApiService = new PolicyApiService(this.policyService, this.apiService);
|
||||||
|
|
||||||
this.keyConnectorService = new KeyConnectorService(
|
this.keyConnectorService = new KeyConnectorService(
|
||||||
|
this.accountService,
|
||||||
|
this.masterPasswordService,
|
||||||
this.cryptoService,
|
this.cryptoService,
|
||||||
this.apiService,
|
this.apiService,
|
||||||
this.tokenService,
|
this.tokenService,
|
||||||
@ -471,9 +476,10 @@ export class Main {
|
|||||||
|
|
||||||
this.authRequestService = new AuthRequestService(
|
this.authRequestService = new AuthRequestService(
|
||||||
this.appIdService,
|
this.appIdService,
|
||||||
|
this.accountService,
|
||||||
|
this.masterPasswordService,
|
||||||
this.cryptoService,
|
this.cryptoService,
|
||||||
this.apiService,
|
this.apiService,
|
||||||
this.stateService,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
this.billingAccountProfileStateService = new DefaultBillingAccountProfileStateService(
|
this.billingAccountProfileStateService = new DefaultBillingAccountProfileStateService(
|
||||||
@ -481,6 +487,8 @@ export class Main {
|
|||||||
);
|
);
|
||||||
|
|
||||||
this.loginStrategyService = new LoginStrategyService(
|
this.loginStrategyService = new LoginStrategyService(
|
||||||
|
this.accountService,
|
||||||
|
this.masterPasswordService,
|
||||||
this.cryptoService,
|
this.cryptoService,
|
||||||
this.apiService,
|
this.apiService,
|
||||||
this.tokenService,
|
this.tokenService,
|
||||||
@ -568,6 +576,8 @@ export class Main {
|
|||||||
this.userVerificationService = new UserVerificationService(
|
this.userVerificationService = new UserVerificationService(
|
||||||
this.stateService,
|
this.stateService,
|
||||||
this.cryptoService,
|
this.cryptoService,
|
||||||
|
this.accountService,
|
||||||
|
this.masterPasswordService,
|
||||||
this.i18nService,
|
this.i18nService,
|
||||||
this.userVerificationApiService,
|
this.userVerificationApiService,
|
||||||
this.userDecryptionOptionsService,
|
this.userDecryptionOptionsService,
|
||||||
@ -578,6 +588,8 @@ export class Main {
|
|||||||
);
|
);
|
||||||
|
|
||||||
this.vaultTimeoutService = new VaultTimeoutService(
|
this.vaultTimeoutService = new VaultTimeoutService(
|
||||||
|
this.accountService,
|
||||||
|
this.masterPasswordService,
|
||||||
this.cipherService,
|
this.cipherService,
|
||||||
this.folderService,
|
this.folderService,
|
||||||
this.collectionService,
|
this.collectionService,
|
||||||
@ -596,6 +608,8 @@ export class Main {
|
|||||||
this.avatarService = new AvatarService(this.apiService, this.stateProvider);
|
this.avatarService = new AvatarService(this.apiService, this.stateProvider);
|
||||||
|
|
||||||
this.syncService = new SyncService(
|
this.syncService = new SyncService(
|
||||||
|
this.masterPasswordService,
|
||||||
|
this.accountService,
|
||||||
this.apiService,
|
this.apiService,
|
||||||
this.domainSettingsService,
|
this.domainSettingsService,
|
||||||
this.folderService,
|
this.folderService,
|
||||||
|
@ -122,6 +122,8 @@ export class ServeCommand {
|
|||||||
this.shareCommand = new ShareCommand(this.main.cipherService);
|
this.shareCommand = new ShareCommand(this.main.cipherService);
|
||||||
this.lockCommand = new LockCommand(this.main.vaultTimeoutService);
|
this.lockCommand = new LockCommand(this.main.vaultTimeoutService);
|
||||||
this.unlockCommand = new UnlockCommand(
|
this.unlockCommand = new UnlockCommand(
|
||||||
|
this.main.accountService,
|
||||||
|
this.main.masterPasswordService,
|
||||||
this.main.cryptoService,
|
this.main.cryptoService,
|
||||||
this.main.stateService,
|
this.main.stateService,
|
||||||
this.main.cryptoFunctionService,
|
this.main.cryptoFunctionService,
|
||||||
|
@ -253,6 +253,8 @@ export class Program {
|
|||||||
if (!cmd.check) {
|
if (!cmd.check) {
|
||||||
await this.exitIfNotAuthed();
|
await this.exitIfNotAuthed();
|
||||||
const command = new UnlockCommand(
|
const command = new UnlockCommand(
|
||||||
|
this.main.accountService,
|
||||||
|
this.main.masterPasswordService,
|
||||||
this.main.cryptoService,
|
this.main.cryptoService,
|
||||||
this.main.stateService,
|
this.main.stateService,
|
||||||
this.main.cryptoFunctionService,
|
this.main.cryptoFunctionService,
|
||||||
@ -613,6 +615,8 @@ export class Program {
|
|||||||
this.processResponse(response, true);
|
this.processResponse(response, true);
|
||||||
} else {
|
} else {
|
||||||
const command = new UnlockCommand(
|
const command = new UnlockCommand(
|
||||||
|
this.main.accountService,
|
||||||
|
this.main.masterPasswordService,
|
||||||
this.main.cryptoService,
|
this.main.cryptoService,
|
||||||
this.main.stateService,
|
this.main.stateService,
|
||||||
this.main.cryptoFunctionService,
|
this.main.cryptoFunctionService,
|
||||||
|
8
apps/desktop/desktop_native/Cargo.lock
generated
8
apps/desktop/desktop_native/Cargo.lock
generated
@ -1237,18 +1237,18 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror"
|
name = "thiserror"
|
||||||
version = "1.0.51"
|
version = "1.0.58"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f11c217e1416d6f036b870f14e0413d480dbf28edbee1f877abaf0206af43bb7"
|
checksum = "03468839009160513471e86a034bb2c5c0e4baae3b43f79ffc55c4a5427b3297"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"thiserror-impl",
|
"thiserror-impl",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror-impl"
|
name = "thiserror-impl"
|
||||||
version = "1.0.51"
|
version = "1.0.58"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "01742297787513b79cf8e29d1056ede1313e2420b7b3b15d0a768b4921f549df"
|
checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
@ -24,7 +24,7 @@ rand = "=0.8.5"
|
|||||||
retry = "=2.0.0"
|
retry = "=2.0.0"
|
||||||
scopeguard = "=1.2.0"
|
scopeguard = "=1.2.0"
|
||||||
sha2 = "=0.10.8"
|
sha2 = "=0.10.8"
|
||||||
thiserror = "=1.0.51"
|
thiserror = "=1.0.58"
|
||||||
typenum = "=1.17.0"
|
typenum = "=1.17.0"
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
|
@ -203,7 +203,7 @@
|
|||||||
"si",
|
"si",
|
||||||
"sk",
|
"sk",
|
||||||
"sl",
|
"sl",
|
||||||
"sr",
|
"sr-cyrl",
|
||||||
"sv",
|
"sv",
|
||||||
"te",
|
"te",
|
||||||
"th",
|
"th",
|
||||||
@ -228,6 +228,7 @@
|
|||||||
"artifactName": "${productName}-${version}-${arch}.${ext}"
|
"artifactName": "${productName}-${version}-${arch}.${ext}"
|
||||||
},
|
},
|
||||||
"snap": {
|
"snap": {
|
||||||
|
"summary": "After installation enable required `password-manager-service` by running `sudo snap connect bitwarden:password-manager-service`.",
|
||||||
"autoStart": true,
|
"autoStart": true,
|
||||||
"base": "core22",
|
"base": "core22",
|
||||||
"confinement": "strict",
|
"confinement": "strict",
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@bitwarden/desktop",
|
"name": "@bitwarden/desktop",
|
||||||
"description": "A secure and free password manager for all of your devices.",
|
"description": "A secure and free password manager for all of your devices.",
|
||||||
"version": "2024.3.2",
|
"version": "2024.4.1",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"bitwarden",
|
"bitwarden",
|
||||||
"password",
|
"password",
|
||||||
|
@ -26,6 +26,7 @@ import { InternalPolicyService } from "@bitwarden/common/admin-console/abstracti
|
|||||||
import { ProviderService } from "@bitwarden/common/admin-console/abstractions/provider.service";
|
import { ProviderService } from "@bitwarden/common/admin-console/abstractions/provider.service";
|
||||||
import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service";
|
import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service";
|
||||||
import { KeyConnectorService } from "@bitwarden/common/auth/abstractions/key-connector.service";
|
import { KeyConnectorService } from "@bitwarden/common/auth/abstractions/key-connector.service";
|
||||||
|
import { MasterPasswordServiceAbstraction } from "@bitwarden/common/auth/abstractions/master-password.service.abstraction";
|
||||||
import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.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 { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
|
||||||
import { ForceSetPasswordReason } from "@bitwarden/common/auth/models/domain/force-set-password-reason";
|
import { ForceSetPasswordReason } from "@bitwarden/common/auth/models/domain/force-set-password-reason";
|
||||||
@ -120,6 +121,7 @@ export class AppComponent implements OnInit, OnDestroy {
|
|||||||
private accountCleanUpInProgress: { [userId: string]: boolean } = {};
|
private accountCleanUpInProgress: { [userId: string]: boolean } = {};
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
|
private masterPasswordService: MasterPasswordServiceAbstraction,
|
||||||
private broadcasterService: BroadcasterService,
|
private broadcasterService: BroadcasterService,
|
||||||
private folderService: InternalFolderService,
|
private folderService: InternalFolderService,
|
||||||
private syncService: SyncService,
|
private syncService: SyncService,
|
||||||
@ -408,8 +410,9 @@ export class AppComponent implements OnInit, OnDestroy {
|
|||||||
(await this.authService.getAuthStatus(message.userId)) ===
|
(await this.authService.getAuthStatus(message.userId)) ===
|
||||||
AuthenticationStatus.Locked;
|
AuthenticationStatus.Locked;
|
||||||
const forcedPasswordReset =
|
const forcedPasswordReset =
|
||||||
(await this.stateService.getForceSetPasswordReason({ userId: message.userId })) !=
|
(await firstValueFrom(
|
||||||
ForceSetPasswordReason.None;
|
this.masterPasswordService.forceSetPasswordReason$(message.userId),
|
||||||
|
)) != ForceSetPasswordReason.None;
|
||||||
if (locked) {
|
if (locked) {
|
||||||
this.messagingService.send("locked", { userId: message.userId });
|
this.messagingService.send("locked", { userId: message.userId });
|
||||||
} else if (forcedPasswordReset) {
|
} else if (forcedPasswordReset) {
|
||||||
@ -606,7 +609,7 @@ export class AppComponent implements OnInit, OnDestroy {
|
|||||||
// This must come last otherwise the logout will prematurely trigger
|
// This must come last otherwise the logout will prematurely trigger
|
||||||
// a process reload before all the state service user data can be cleaned up
|
// a process reload before all the state service user data can be cleaned up
|
||||||
if (userBeingLoggedOut === preLogoutActiveUserId) {
|
if (userBeingLoggedOut === preLogoutActiveUserId) {
|
||||||
this.searchService.clearIndex();
|
await this.searchService.clearIndex();
|
||||||
this.authService.logOut(async () => {
|
this.authService.logOut(async () => {
|
||||||
if (expired) {
|
if (expired) {
|
||||||
this.platformUtilsService.showToast(
|
this.platformUtilsService.showToast(
|
||||||
|
@ -20,6 +20,7 @@ import { VaultTimeoutSettingsService } from "@bitwarden/common/abstractions/vaul
|
|||||||
import { PolicyService as PolicyServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
|
import { PolicyService as PolicyServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
|
||||||
import { AccountService as AccountServiceAbstraction } from "@bitwarden/common/auth/abstractions/account.service";
|
import { AccountService as AccountServiceAbstraction } from "@bitwarden/common/auth/abstractions/account.service";
|
||||||
import { AuthService as AuthServiceAbstraction } from "@bitwarden/common/auth/abstractions/auth.service";
|
import { AuthService as AuthServiceAbstraction } from "@bitwarden/common/auth/abstractions/auth.service";
|
||||||
|
import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/auth/abstractions/master-password.service.abstraction";
|
||||||
import { TokenService } from "@bitwarden/common/auth/abstractions/token.service";
|
import { TokenService } from "@bitwarden/common/auth/abstractions/token.service";
|
||||||
import { AutofillSettingsServiceAbstraction } from "@bitwarden/common/autofill/services/autofill-settings.service";
|
import { AutofillSettingsServiceAbstraction } from "@bitwarden/common/autofill/services/autofill-settings.service";
|
||||||
import { BroadcasterService as BroadcasterServiceAbstraction } from "@bitwarden/common/platform/abstractions/broadcaster.service";
|
import { BroadcasterService as BroadcasterServiceAbstraction } from "@bitwarden/common/platform/abstractions/broadcaster.service";
|
||||||
@ -228,6 +229,7 @@ const safeProviders: SafeProvider[] = [
|
|||||||
provide: CryptoServiceAbstraction,
|
provide: CryptoServiceAbstraction,
|
||||||
useClass: ElectronCryptoService,
|
useClass: ElectronCryptoService,
|
||||||
deps: [
|
deps: [
|
||||||
|
InternalMasterPasswordServiceAbstraction,
|
||||||
KeyGenerationServiceAbstraction,
|
KeyGenerationServiceAbstraction,
|
||||||
CryptoFunctionServiceAbstraction,
|
CryptoFunctionServiceAbstraction,
|
||||||
EncryptService,
|
EncryptService,
|
||||||
|
@ -14,7 +14,9 @@ import { PolicyApiServiceAbstraction } from "@bitwarden/common/admin-console/abs
|
|||||||
import { InternalPolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
|
import { InternalPolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
|
||||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||||
import { DeviceTrustCryptoServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust-crypto.service.abstraction";
|
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 { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction";
|
||||||
|
import { FakeMasterPasswordService } from "@bitwarden/common/auth/services/master-password/fake-master-password.service";
|
||||||
import { BroadcasterService } from "@bitwarden/common/platform/abstractions/broadcaster.service";
|
import { BroadcasterService } from "@bitwarden/common/platform/abstractions/broadcaster.service";
|
||||||
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
|
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
|
||||||
import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service";
|
import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service";
|
||||||
@ -52,6 +54,7 @@ describe("LockComponent", () => {
|
|||||||
let broadcasterServiceMock: MockProxy<BroadcasterService>;
|
let broadcasterServiceMock: MockProxy<BroadcasterService>;
|
||||||
let platformUtilsServiceMock: MockProxy<PlatformUtilsService>;
|
let platformUtilsServiceMock: MockProxy<PlatformUtilsService>;
|
||||||
let activatedRouteMock: MockProxy<ActivatedRoute>;
|
let activatedRouteMock: MockProxy<ActivatedRoute>;
|
||||||
|
let mockMasterPasswordService: FakeMasterPasswordService;
|
||||||
|
|
||||||
const mockUserId = Utils.newGuid() as UserId;
|
const mockUserId = Utils.newGuid() as UserId;
|
||||||
const accountService: FakeAccountService = mockAccountServiceWith(mockUserId);
|
const accountService: FakeAccountService = mockAccountServiceWith(mockUserId);
|
||||||
@ -67,6 +70,8 @@ describe("LockComponent", () => {
|
|||||||
activatedRouteMock = mock<ActivatedRoute>();
|
activatedRouteMock = mock<ActivatedRoute>();
|
||||||
activatedRouteMock.queryParams = mock<ActivatedRoute["queryParams"]>();
|
activatedRouteMock.queryParams = mock<ActivatedRoute["queryParams"]>();
|
||||||
|
|
||||||
|
mockMasterPasswordService = new FakeMasterPasswordService();
|
||||||
|
|
||||||
biometricStateService.dismissedRequirePasswordOnStartCallout$ = of(false);
|
biometricStateService.dismissedRequirePasswordOnStartCallout$ = of(false);
|
||||||
biometricStateService.promptAutomatically$ = of(false);
|
biometricStateService.promptAutomatically$ = of(false);
|
||||||
biometricStateService.promptCancelled$ = of(false);
|
biometricStateService.promptCancelled$ = of(false);
|
||||||
@ -74,6 +79,7 @@ describe("LockComponent", () => {
|
|||||||
await TestBed.configureTestingModule({
|
await TestBed.configureTestingModule({
|
||||||
declarations: [LockComponent, I18nPipe],
|
declarations: [LockComponent, I18nPipe],
|
||||||
providers: [
|
providers: [
|
||||||
|
{ provide: InternalMasterPasswordServiceAbstraction, useValue: mockMasterPasswordService },
|
||||||
{
|
{
|
||||||
provide: I18nService,
|
provide: I18nService,
|
||||||
useValue: mock<I18nService>(),
|
useValue: mock<I18nService>(),
|
||||||
|
@ -11,6 +11,7 @@ import { PolicyApiServiceAbstraction } from "@bitwarden/common/admin-console/abs
|
|||||||
import { InternalPolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
|
import { InternalPolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
|
||||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||||
import { DeviceTrustCryptoServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust-crypto.service.abstraction";
|
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 { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction";
|
||||||
import { DeviceType } from "@bitwarden/common/enums";
|
import { DeviceType } from "@bitwarden/common/enums";
|
||||||
import { BroadcasterService } from "@bitwarden/common/platform/abstractions/broadcaster.service";
|
import { BroadcasterService } from "@bitwarden/common/platform/abstractions/broadcaster.service";
|
||||||
@ -38,6 +39,7 @@ export class LockComponent extends BaseLockComponent {
|
|||||||
private autoPromptBiometric = false;
|
private autoPromptBiometric = false;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
|
masterPasswordService: InternalMasterPasswordServiceAbstraction,
|
||||||
router: Router,
|
router: Router,
|
||||||
i18nService: I18nService,
|
i18nService: I18nService,
|
||||||
platformUtilsService: PlatformUtilsService,
|
platformUtilsService: PlatformUtilsService,
|
||||||
@ -63,6 +65,7 @@ export class LockComponent extends BaseLockComponent {
|
|||||||
accountService: AccountService,
|
accountService: AccountService,
|
||||||
) {
|
) {
|
||||||
super(
|
super(
|
||||||
|
masterPasswordService,
|
||||||
router,
|
router,
|
||||||
i18nService,
|
i18nService,
|
||||||
platformUtilsService,
|
platformUtilsService,
|
||||||
|
@ -8,6 +8,8 @@ import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-conso
|
|||||||
import { OrganizationUserService } from "@bitwarden/common/admin-console/abstractions/organization-user/organization-user.service";
|
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 { 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 { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
|
||||||
|
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 { SsoLoginServiceAbstraction } from "@bitwarden/common/auth/abstractions/sso-login.service.abstraction";
|
||||||
import { BroadcasterService } from "@bitwarden/common/platform/abstractions/broadcaster.service";
|
import { BroadcasterService } from "@bitwarden/common/platform/abstractions/broadcaster.service";
|
||||||
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
|
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
|
||||||
@ -29,6 +31,8 @@ const BroadcasterSubscriptionId = "SetPasswordComponent";
|
|||||||
})
|
})
|
||||||
export class SetPasswordComponent extends BaseSetPasswordComponent implements OnDestroy {
|
export class SetPasswordComponent extends BaseSetPasswordComponent implements OnDestroy {
|
||||||
constructor(
|
constructor(
|
||||||
|
accountService: AccountService,
|
||||||
|
masterPasswordService: InternalMasterPasswordServiceAbstraction,
|
||||||
apiService: ApiService,
|
apiService: ApiService,
|
||||||
i18nService: I18nService,
|
i18nService: I18nService,
|
||||||
cryptoService: CryptoService,
|
cryptoService: CryptoService,
|
||||||
@ -50,6 +54,8 @@ export class SetPasswordComponent extends BaseSetPasswordComponent implements On
|
|||||||
dialogService: DialogService,
|
dialogService: DialogService,
|
||||||
) {
|
) {
|
||||||
super(
|
super(
|
||||||
|
accountService,
|
||||||
|
masterPasswordService,
|
||||||
i18nService,
|
i18nService,
|
||||||
cryptoService,
|
cryptoService,
|
||||||
messagingService,
|
messagingService,
|
||||||
|
@ -7,6 +7,8 @@ import {
|
|||||||
UserDecryptionOptionsServiceAbstraction,
|
UserDecryptionOptionsServiceAbstraction,
|
||||||
} from "@bitwarden/auth/common";
|
} from "@bitwarden/auth/common";
|
||||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
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 { SsoLoginServiceAbstraction } from "@bitwarden/common/auth/abstractions/sso-login.service.abstraction";
|
||||||
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
||||||
import { CryptoFunctionService } from "@bitwarden/common/platform/abstractions/crypto-function.service";
|
import { CryptoFunctionService } from "@bitwarden/common/platform/abstractions/crypto-function.service";
|
||||||
@ -39,6 +41,8 @@ export class SsoComponent extends BaseSsoComponent {
|
|||||||
logService: LogService,
|
logService: LogService,
|
||||||
userDecryptionOptionsService: UserDecryptionOptionsServiceAbstraction,
|
userDecryptionOptionsService: UserDecryptionOptionsServiceAbstraction,
|
||||||
configService: ConfigService,
|
configService: ConfigService,
|
||||||
|
masterPasswordService: InternalMasterPasswordServiceAbstraction,
|
||||||
|
accountService: AccountService,
|
||||||
) {
|
) {
|
||||||
super(
|
super(
|
||||||
ssoLoginService,
|
ssoLoginService,
|
||||||
@ -55,6 +59,8 @@ export class SsoComponent extends BaseSsoComponent {
|
|||||||
logService,
|
logService,
|
||||||
userDecryptionOptionsService,
|
userDecryptionOptionsService,
|
||||||
configService,
|
configService,
|
||||||
|
masterPasswordService,
|
||||||
|
accountService,
|
||||||
);
|
);
|
||||||
super.onSuccessfulLogin = async () => {
|
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.
|
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
|
||||||
|
@ -11,6 +11,8 @@ import {
|
|||||||
UserDecryptionOptionsServiceAbstraction,
|
UserDecryptionOptionsServiceAbstraction,
|
||||||
} from "@bitwarden/auth/common";
|
} from "@bitwarden/auth/common";
|
||||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
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 { SsoLoginServiceAbstraction } from "@bitwarden/common/auth/abstractions/sso-login.service.abstraction";
|
||||||
import { TwoFactorService } from "@bitwarden/common/auth/abstractions/two-factor.service";
|
import { TwoFactorService } from "@bitwarden/common/auth/abstractions/two-factor.service";
|
||||||
import { TwoFactorProviderType } from "@bitwarden/common/auth/enums/two-factor-provider-type";
|
import { TwoFactorProviderType } from "@bitwarden/common/auth/enums/two-factor-provider-type";
|
||||||
@ -60,6 +62,8 @@ export class TwoFactorComponent extends BaseTwoFactorComponent {
|
|||||||
userDecryptionOptionsService: UserDecryptionOptionsServiceAbstraction,
|
userDecryptionOptionsService: UserDecryptionOptionsServiceAbstraction,
|
||||||
ssoLoginService: SsoLoginServiceAbstraction,
|
ssoLoginService: SsoLoginServiceAbstraction,
|
||||||
configService: ConfigService,
|
configService: ConfigService,
|
||||||
|
masterPasswordService: InternalMasterPasswordServiceAbstraction,
|
||||||
|
accountService: AccountService,
|
||||||
@Inject(WINDOW) protected win: Window,
|
@Inject(WINDOW) protected win: Window,
|
||||||
) {
|
) {
|
||||||
super(
|
super(
|
||||||
@ -79,6 +83,8 @@ export class TwoFactorComponent extends BaseTwoFactorComponent {
|
|||||||
userDecryptionOptionsService,
|
userDecryptionOptionsService,
|
||||||
ssoLoginService,
|
ssoLoginService,
|
||||||
configService,
|
configService,
|
||||||
|
masterPasswordService,
|
||||||
|
accountService,
|
||||||
);
|
);
|
||||||
super.onSuccessfulLogin = async () => {
|
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.
|
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
|
||||||
|
@ -494,7 +494,7 @@
|
|||||||
"message": "Kansio poistettiin"
|
"message": "Kansio poistettiin"
|
||||||
},
|
},
|
||||||
"loginOrCreateNewAccount": {
|
"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": {
|
"createAccount": {
|
||||||
"message": "Luo tili"
|
"message": "Luo tili"
|
||||||
@ -2689,7 +2689,7 @@
|
|||||||
"description": "Label indicating the most common import formats"
|
"description": "Label indicating the most common import formats"
|
||||||
},
|
},
|
||||||
"troubleshooting": {
|
"troubleshooting": {
|
||||||
"message": "Vianmääritys"
|
"message": "Vianselvitys"
|
||||||
},
|
},
|
||||||
"disableHardwareAccelerationRestart": {
|
"disableHardwareAccelerationRestart": {
|
||||||
"message": "Poista laitteistokiihdytys käytöstä ja käynnistä sovellus uudelleen"
|
"message": "Poista laitteistokiihdytys käytöstä ja käynnistä sovellus uudelleen"
|
||||||
|
@ -404,7 +404,7 @@
|
|||||||
"message": "長度"
|
"message": "長度"
|
||||||
},
|
},
|
||||||
"passwordMinLength": {
|
"passwordMinLength": {
|
||||||
"message": "Minimum password length"
|
"message": "最小密碼長度"
|
||||||
},
|
},
|
||||||
"uppercase": {
|
"uppercase": {
|
||||||
"message": "大寫 (A-Z)"
|
"message": "大寫 (A-Z)"
|
||||||
@ -561,10 +561,10 @@
|
|||||||
"message": "帳戶已建立!現在可以登入了。"
|
"message": "帳戶已建立!現在可以登入了。"
|
||||||
},
|
},
|
||||||
"youSuccessfullyLoggedIn": {
|
"youSuccessfullyLoggedIn": {
|
||||||
"message": "You successfully logged in"
|
"message": "你已成功登入"
|
||||||
},
|
},
|
||||||
"youMayCloseThisWindow": {
|
"youMayCloseThisWindow": {
|
||||||
"message": "You may close this window"
|
"message": "你可以關閉此視窗"
|
||||||
},
|
},
|
||||||
"masterPassSent": {
|
"masterPassSent": {
|
||||||
"message": "已寄出包含您主密碼提示的電子郵件。"
|
"message": "已寄出包含您主密碼提示的電子郵件。"
|
||||||
@ -1546,15 +1546,15 @@
|
|||||||
"message": "設定主密碼"
|
"message": "設定主密碼"
|
||||||
},
|
},
|
||||||
"orgPermissionsUpdatedMustSetPassword": {
|
"orgPermissionsUpdatedMustSetPassword": {
|
||||||
"message": "Your organization permissions were updated, requiring you to set a master password.",
|
"message": "你的組織權限已更新,要求你設定一個主密碼。",
|
||||||
"description": "Used as a card title description on the set password page to explain why the user is there"
|
"description": "Used as a card title description on the set password page to explain why the user is there"
|
||||||
},
|
},
|
||||||
"orgRequiresYouToSetPassword": {
|
"orgRequiresYouToSetPassword": {
|
||||||
"message": "Your organization requires you to set a master password.",
|
"message": "你的組織要求你設定一個主密碼。",
|
||||||
"description": "Used as a card title description on the set password page to explain why the user is there"
|
"description": "Used as a card title description on the set password page to explain why the user is there"
|
||||||
},
|
},
|
||||||
"verificationRequired": {
|
"verificationRequired": {
|
||||||
"message": "Verification required",
|
"message": "需要驗證",
|
||||||
"description": "Default title for the user verification dialog."
|
"description": "Default title for the user verification dialog."
|
||||||
},
|
},
|
||||||
"currentMasterPass": {
|
"currentMasterPass": {
|
||||||
@ -1645,10 +1645,10 @@
|
|||||||
"message": "在您的桌面和瀏覽器閒建立連綫時,透過要求指紋短語確認,以添加一個額外的安全層。每次建立連綫都需要使用者干預和驗證。"
|
"message": "在您的桌面和瀏覽器閒建立連綫時,透過要求指紋短語確認,以添加一個額外的安全層。每次建立連綫都需要使用者干預和驗證。"
|
||||||
},
|
},
|
||||||
"enableHardwareAcceleration": {
|
"enableHardwareAcceleration": {
|
||||||
"message": "Use hardware acceleration"
|
"message": "使用硬體加速"
|
||||||
},
|
},
|
||||||
"enableHardwareAccelerationDesc": {
|
"enableHardwareAccelerationDesc": {
|
||||||
"message": "By default this setting is ON. Turn OFF only if you experience graphical issues. Restart is required."
|
"message": "此設定預設為開啟。僅當你遇到圖形問題時才關閉。需要重新啟動。"
|
||||||
},
|
},
|
||||||
"approve": {
|
"approve": {
|
||||||
"message": "核准"
|
"message": "核准"
|
||||||
@ -1889,40 +1889,40 @@
|
|||||||
"message": "您的主密碼不符合一個或多個組織原則要求。您必須立即更新您的主密碼才能存取密碼庫。進行此動作將登出您目前的工作階段,需要您重新登入。其他裝置上的工作階段可能繼續長達一小時。"
|
"message": "您的主密碼不符合一個或多個組織原則要求。您必須立即更新您的主密碼才能存取密碼庫。進行此動作將登出您目前的工作階段,需要您重新登入。其他裝置上的工作階段可能繼續長達一小時。"
|
||||||
},
|
},
|
||||||
"tryAgain": {
|
"tryAgain": {
|
||||||
"message": "Try again"
|
"message": "再試一次"
|
||||||
},
|
},
|
||||||
"verificationRequiredForActionSetPinToContinue": {
|
"verificationRequiredForActionSetPinToContinue": {
|
||||||
"message": "Verification required for this action. Set a PIN to continue."
|
"message": "此操作需要驗證。設定 PIN 碼以繼續。"
|
||||||
},
|
},
|
||||||
"setPin": {
|
"setPin": {
|
||||||
"message": "Set PIN"
|
"message": "設定 PIN 碼"
|
||||||
},
|
},
|
||||||
"verifyWithBiometrics": {
|
"verifyWithBiometrics": {
|
||||||
"message": "Verify with biometrics"
|
"message": "使用生物辨識進行驗證"
|
||||||
},
|
},
|
||||||
"awaitingConfirmation": {
|
"awaitingConfirmation": {
|
||||||
"message": "Awaiting confirmation"
|
"message": "正在等待確認"
|
||||||
},
|
},
|
||||||
"couldNotCompleteBiometrics": {
|
"couldNotCompleteBiometrics": {
|
||||||
"message": "Could not complete biometrics."
|
"message": "無法完成生物辨識。"
|
||||||
},
|
},
|
||||||
"needADifferentMethod": {
|
"needADifferentMethod": {
|
||||||
"message": "Need a different method?"
|
"message": "需要不同的方法嗎?"
|
||||||
},
|
},
|
||||||
"useMasterPassword": {
|
"useMasterPassword": {
|
||||||
"message": "Use master password"
|
"message": "使用主密碼"
|
||||||
},
|
},
|
||||||
"usePin": {
|
"usePin": {
|
||||||
"message": "Use PIN"
|
"message": "使用 PIN 碼"
|
||||||
},
|
},
|
||||||
"useBiometrics": {
|
"useBiometrics": {
|
||||||
"message": "Use biometrics"
|
"message": "使用生物辨識"
|
||||||
},
|
},
|
||||||
"enterVerificationCodeSentToEmail": {
|
"enterVerificationCodeSentToEmail": {
|
||||||
"message": "Enter the verification code that was sent to your email."
|
"message": "Enter the verification code that was sent to your email."
|
||||||
},
|
},
|
||||||
"resendCode": {
|
"resendCode": {
|
||||||
"message": "Resend code"
|
"message": "重新傳送驗證碼"
|
||||||
},
|
},
|
||||||
"hours": {
|
"hours": {
|
||||||
"message": "小時"
|
"message": "小時"
|
||||||
@ -2465,7 +2465,7 @@
|
|||||||
"message": "全部清除"
|
"message": "全部清除"
|
||||||
},
|
},
|
||||||
"plusNMore": {
|
"plusNMore": {
|
||||||
"message": "+ $QUANTITY$ more",
|
"message": "+ $QUANTITY$ 個更多",
|
||||||
"placeholders": {
|
"placeholders": {
|
||||||
"quantity": {
|
"quantity": {
|
||||||
"content": "$1",
|
"content": "$1",
|
||||||
@ -2477,7 +2477,7 @@
|
|||||||
"message": "子選單"
|
"message": "子選單"
|
||||||
},
|
},
|
||||||
"skipToContent": {
|
"skipToContent": {
|
||||||
"message": "Skip to content"
|
"message": "跳至內容"
|
||||||
},
|
},
|
||||||
"typePasskey": {
|
"typePasskey": {
|
||||||
"message": "密碼金鑰"
|
"message": "密碼金鑰"
|
||||||
@ -2621,13 +2621,13 @@
|
|||||||
"message": "使用者名稱或密碼不正確"
|
"message": "使用者名稱或密碼不正確"
|
||||||
},
|
},
|
||||||
"incorrectPassword": {
|
"incorrectPassword": {
|
||||||
"message": "Incorrect password"
|
"message": "密碼不正確"
|
||||||
},
|
},
|
||||||
"incorrectCode": {
|
"incorrectCode": {
|
||||||
"message": "Incorrect code"
|
"message": "驗證碼不正確"
|
||||||
},
|
},
|
||||||
"incorrectPin": {
|
"incorrectPin": {
|
||||||
"message": "Incorrect PIN"
|
"message": "PIN 碼不正確"
|
||||||
},
|
},
|
||||||
"multifactorAuthenticationFailed": {
|
"multifactorAuthenticationFailed": {
|
||||||
"message": "多因素驗證失敗"
|
"message": "多因素驗證失敗"
|
||||||
@ -2685,22 +2685,22 @@
|
|||||||
"message": "將與您的 LastPass 帳戶關聯的 YubiKey 插入電腦的 USB 連接埠,然後觸摸其按鈕。"
|
"message": "將與您的 LastPass 帳戶關聯的 YubiKey 插入電腦的 USB 連接埠,然後觸摸其按鈕。"
|
||||||
},
|
},
|
||||||
"commonImportFormats": {
|
"commonImportFormats": {
|
||||||
"message": "Common formats",
|
"message": "常見格式",
|
||||||
"description": "Label indicating the most common import formats"
|
"description": "Label indicating the most common import formats"
|
||||||
},
|
},
|
||||||
"troubleshooting": {
|
"troubleshooting": {
|
||||||
"message": "Troubleshooting"
|
"message": "疑難排解"
|
||||||
},
|
},
|
||||||
"disableHardwareAccelerationRestart": {
|
"disableHardwareAccelerationRestart": {
|
||||||
"message": "Disable hardware acceleration and restart"
|
"message": "停用硬體加速並重新啟動"
|
||||||
},
|
},
|
||||||
"enableHardwareAccelerationRestart": {
|
"enableHardwareAccelerationRestart": {
|
||||||
"message": "Enable hardware acceleration and restart"
|
"message": "啟用硬體加速並重新啟動"
|
||||||
},
|
},
|
||||||
"removePasskey": {
|
"removePasskey": {
|
||||||
"message": "Remove passkey"
|
"message": "移除金鑰"
|
||||||
},
|
},
|
||||||
"passkeyRemoved": {
|
"passkeyRemoved": {
|
||||||
"message": "Passkey removed"
|
"message": "金鑰已移除"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
4
apps/desktop/src/package-lock.json
generated
4
apps/desktop/src/package-lock.json
generated
@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "@bitwarden/desktop",
|
"name": "@bitwarden/desktop",
|
||||||
"version": "2024.3.2",
|
"version": "2024.4.1",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "@bitwarden/desktop",
|
"name": "@bitwarden/desktop",
|
||||||
"version": "2024.3.2",
|
"version": "2024.4.1",
|
||||||
"license": "GPL-3.0",
|
"license": "GPL-3.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@bitwarden/desktop-native": "file:../desktop_native",
|
"@bitwarden/desktop-native": "file:../desktop_native",
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
"name": "@bitwarden/desktop",
|
"name": "@bitwarden/desktop",
|
||||||
"productName": "Bitwarden",
|
"productName": "Bitwarden",
|
||||||
"description": "A secure and free password manager for all of your devices.",
|
"description": "A secure and free password manager for all of your devices.",
|
||||||
"version": "2024.3.2",
|
"version": "2024.4.1",
|
||||||
"author": "Bitwarden Inc. <hello@bitwarden.com> (https://bitwarden.com)",
|
"author": "Bitwarden Inc. <hello@bitwarden.com> (https://bitwarden.com)",
|
||||||
"homepage": "https://bitwarden.com",
|
"homepage": "https://bitwarden.com",
|
||||||
"license": "GPL-3.0",
|
"license": "GPL-3.0",
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { FakeStateProvider } from "@bitwarden/common/../spec/fake-state-provider";
|
import { FakeStateProvider } from "@bitwarden/common/../spec/fake-state-provider";
|
||||||
import { mock } from "jest-mock-extended";
|
import { mock } from "jest-mock-extended";
|
||||||
|
|
||||||
|
import { FakeMasterPasswordService } from "@bitwarden/common/auth/services/master-password/fake-master-password.service";
|
||||||
import { CryptoFunctionService } from "@bitwarden/common/platform/abstractions/crypto-function.service";
|
import { CryptoFunctionService } from "@bitwarden/common/platform/abstractions/crypto-function.service";
|
||||||
import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service";
|
import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service";
|
||||||
import { KeyGenerationService } from "@bitwarden/common/platform/abstractions/key-generation.service";
|
import { KeyGenerationService } from "@bitwarden/common/platform/abstractions/key-generation.service";
|
||||||
@ -30,6 +31,7 @@ describe("electronCryptoService", () => {
|
|||||||
const platformUtilService = mock<PlatformUtilsService>();
|
const platformUtilService = mock<PlatformUtilsService>();
|
||||||
const logService = mock<LogService>();
|
const logService = mock<LogService>();
|
||||||
const stateService = mock<StateService>();
|
const stateService = mock<StateService>();
|
||||||
|
let masterPasswordService: FakeMasterPasswordService;
|
||||||
let accountService: FakeAccountService;
|
let accountService: FakeAccountService;
|
||||||
let stateProvider: FakeStateProvider;
|
let stateProvider: FakeStateProvider;
|
||||||
const biometricStateService = mock<BiometricStateService>();
|
const biometricStateService = mock<BiometricStateService>();
|
||||||
@ -38,9 +40,11 @@ describe("electronCryptoService", () => {
|
|||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
accountService = mockAccountServiceWith("userId" as UserId);
|
accountService = mockAccountServiceWith("userId" as UserId);
|
||||||
|
masterPasswordService = new FakeMasterPasswordService();
|
||||||
stateProvider = new FakeStateProvider(accountService);
|
stateProvider = new FakeStateProvider(accountService);
|
||||||
|
|
||||||
sut = new ElectronCryptoService(
|
sut = new ElectronCryptoService(
|
||||||
|
masterPasswordService,
|
||||||
keyGenerationService,
|
keyGenerationService,
|
||||||
cryptoFunctionService,
|
cryptoFunctionService,
|
||||||
encryptService,
|
encryptService,
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { firstValueFrom } from "rxjs";
|
import { firstValueFrom } from "rxjs";
|
||||||
|
|
||||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
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 { CryptoFunctionService } from "@bitwarden/common/platform/abstractions/crypto-function.service";
|
||||||
import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service";
|
import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service";
|
||||||
import { KeyGenerationService } from "@bitwarden/common/platform/abstractions/key-generation.service";
|
import { KeyGenerationService } from "@bitwarden/common/platform/abstractions/key-generation.service";
|
||||||
@ -20,6 +21,7 @@ import { UserKey, MasterKey } from "@bitwarden/common/types/key";
|
|||||||
|
|
||||||
export class ElectronCryptoService extends CryptoService {
|
export class ElectronCryptoService extends CryptoService {
|
||||||
constructor(
|
constructor(
|
||||||
|
masterPasswordService: InternalMasterPasswordServiceAbstraction,
|
||||||
keyGenerationService: KeyGenerationService,
|
keyGenerationService: KeyGenerationService,
|
||||||
cryptoFunctionService: CryptoFunctionService,
|
cryptoFunctionService: CryptoFunctionService,
|
||||||
encryptService: EncryptService,
|
encryptService: EncryptService,
|
||||||
@ -31,6 +33,7 @@ export class ElectronCryptoService extends CryptoService {
|
|||||||
private biometricStateService: BiometricStateService,
|
private biometricStateService: BiometricStateService,
|
||||||
) {
|
) {
|
||||||
super(
|
super(
|
||||||
|
masterPasswordService,
|
||||||
keyGenerationService,
|
keyGenerationService,
|
||||||
cryptoFunctionService,
|
cryptoFunctionService,
|
||||||
encryptService,
|
encryptService,
|
||||||
@ -159,12 +162,16 @@ export class ElectronCryptoService extends CryptoService {
|
|||||||
const oldBiometricKey = await this.stateService.getCryptoMasterKeyBiometric({ userId });
|
const oldBiometricKey = await this.stateService.getCryptoMasterKeyBiometric({ userId });
|
||||||
// decrypt
|
// decrypt
|
||||||
const masterKey = new SymmetricCryptoKey(Utils.fromB64ToArray(oldBiometricKey)) as MasterKey;
|
const masterKey = new SymmetricCryptoKey(Utils.fromB64ToArray(oldBiometricKey)) as MasterKey;
|
||||||
let encUserKey = await this.stateService.getEncryptedCryptoSymmetricKey();
|
userId ??= (await firstValueFrom(this.accountService.activeAccount$))?.id;
|
||||||
encUserKey = 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) {
|
if (!encUserKey) {
|
||||||
throw new Error("No user key found during biometric migration");
|
throw new Error("No user key found during biometric migration");
|
||||||
}
|
}
|
||||||
const userKey = await this.decryptUserKeyWithMasterKey(masterKey, new EncString(encUserKey));
|
const userKey = await this.decryptUserKeyWithMasterKey(masterKey, encUserKey);
|
||||||
// migrate
|
// migrate
|
||||||
await this.storeBiometricKey(userKey, userId);
|
await this.storeBiometricKey(userKey, userId);
|
||||||
await this.stateService.setCryptoMasterKeyBiometric(null, { userId });
|
await this.stateService.setCryptoMasterKeyBiometric(null, { userId });
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { Injectable, NgZone } from "@angular/core";
|
import { Injectable, NgZone } from "@angular/core";
|
||||||
import { firstValueFrom } from "rxjs";
|
import { firstValueFrom } from "rxjs";
|
||||||
|
|
||||||
|
import { MasterPasswordServiceAbstraction } from "@bitwarden/common/auth/abstractions/master-password.service.abstraction";
|
||||||
import { CryptoFunctionService } from "@bitwarden/common/platform/abstractions/crypto-function.service";
|
import { CryptoFunctionService } from "@bitwarden/common/platform/abstractions/crypto-function.service";
|
||||||
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
|
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
|
||||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||||
@ -30,6 +31,7 @@ export class NativeMessagingService {
|
|||||||
private sharedSecrets = new Map<string, SymmetricCryptoKey>();
|
private sharedSecrets = new Map<string, SymmetricCryptoKey>();
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
|
private masterPasswordService: MasterPasswordServiceAbstraction,
|
||||||
private cryptoFunctionService: CryptoFunctionService,
|
private cryptoFunctionService: CryptoFunctionService,
|
||||||
private cryptoService: CryptoService,
|
private cryptoService: CryptoService,
|
||||||
private platformUtilService: PlatformUtilsService,
|
private platformUtilService: PlatformUtilsService,
|
||||||
@ -162,7 +164,9 @@ export class NativeMessagingService {
|
|||||||
KeySuffixOptions.Biometric,
|
KeySuffixOptions.Biometric,
|
||||||
message.userId,
|
message.userId,
|
||||||
);
|
);
|
||||||
const masterKey = await this.cryptoService.getMasterKey(message.userId);
|
const masterKey = await firstValueFrom(
|
||||||
|
this.masterPasswordService.masterKey$(message.userId as UserId),
|
||||||
|
);
|
||||||
|
|
||||||
if (userKey != null) {
|
if (userKey != null) {
|
||||||
// we send the master key still for backwards compatibility
|
// we send the master key still for backwards compatibility
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { Directive, ViewChild, ViewContainerRef } from "@angular/core";
|
import { Directive, OnDestroy, OnInit, ViewChild, ViewContainerRef } from "@angular/core";
|
||||||
import { firstValueFrom } from "rxjs";
|
import { BehaviorSubject, Subject, firstValueFrom, from, switchMap, takeUntil } from "rxjs";
|
||||||
|
|
||||||
import { SearchPipe } from "@bitwarden/angular/pipes/search.pipe";
|
import { SearchPipe } from "@bitwarden/angular/pipes/search.pipe";
|
||||||
import { UserNamePipe } from "@bitwarden/angular/pipes/user-name.pipe";
|
import { UserNamePipe } from "@bitwarden/angular/pipes/user-name.pipe";
|
||||||
@ -33,7 +33,9 @@ const MaxCheckedCount = 500;
|
|||||||
@Directive()
|
@Directive()
|
||||||
export abstract class BasePeopleComponent<
|
export abstract class BasePeopleComponent<
|
||||||
UserType extends ProviderUserUserDetailsResponse | OrganizationUserView,
|
UserType extends ProviderUserUserDetailsResponse | OrganizationUserView,
|
||||||
> {
|
>
|
||||||
|
implements OnInit, OnDestroy
|
||||||
|
{
|
||||||
@ViewChild("confirmTemplate", { read: ViewContainerRef, static: true })
|
@ViewChild("confirmTemplate", { read: ViewContainerRef, static: true })
|
||||||
confirmModalRef: ViewContainerRef;
|
confirmModalRef: ViewContainerRef;
|
||||||
|
|
||||||
@ -88,7 +90,6 @@ export abstract class BasePeopleComponent<
|
|||||||
status: StatusType;
|
status: StatusType;
|
||||||
users: UserType[] = [];
|
users: UserType[] = [];
|
||||||
pagedUsers: UserType[] = [];
|
pagedUsers: UserType[] = [];
|
||||||
searchText: string;
|
|
||||||
actionPromise: Promise<void>;
|
actionPromise: Promise<void>;
|
||||||
|
|
||||||
protected allUsers: UserType[] = [];
|
protected allUsers: UserType[] = [];
|
||||||
@ -97,7 +98,19 @@ export abstract class BasePeopleComponent<
|
|||||||
protected didScroll = false;
|
protected didScroll = false;
|
||||||
protected pageSize = 100;
|
protected pageSize = 100;
|
||||||
|
|
||||||
|
protected destroy$ = new Subject<void>();
|
||||||
|
|
||||||
private pagedUsersCount = 0;
|
private pagedUsersCount = 0;
|
||||||
|
private _searchText$ = new BehaviorSubject<string>("");
|
||||||
|
private isSearching: boolean = false;
|
||||||
|
|
||||||
|
get searchText() {
|
||||||
|
return this._searchText$.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
set searchText(value: string) {
|
||||||
|
this._searchText$.next(value);
|
||||||
|
}
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
protected apiService: ApiService,
|
protected apiService: ApiService,
|
||||||
@ -122,6 +135,22 @@ export abstract class BasePeopleComponent<
|
|||||||
abstract reinviteUser(id: string): Promise<void>;
|
abstract reinviteUser(id: string): Promise<void>;
|
||||||
abstract confirmUser(user: UserType, publicKey: Uint8Array): Promise<void>;
|
abstract confirmUser(user: UserType, publicKey: Uint8Array): Promise<void>;
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this._searchText$
|
||||||
|
.pipe(
|
||||||
|
switchMap((searchText) => from(this.searchService.isSearchable(searchText))),
|
||||||
|
takeUntil(this.destroy$),
|
||||||
|
)
|
||||||
|
.subscribe((isSearchable) => {
|
||||||
|
this.isSearching = isSearchable;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy(): void {
|
||||||
|
this.destroy$.next();
|
||||||
|
this.destroy$.complete();
|
||||||
|
}
|
||||||
|
|
||||||
async load() {
|
async load() {
|
||||||
const response = await this.getUsers();
|
const response = await this.getUsers();
|
||||||
this.statusMap.clear();
|
this.statusMap.clear();
|
||||||
@ -390,12 +419,8 @@ export abstract class BasePeopleComponent<
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
isSearching() {
|
|
||||||
return this.searchService.isSearchable(this.searchText);
|
|
||||||
}
|
|
||||||
|
|
||||||
isPaging() {
|
isPaging() {
|
||||||
const searching = this.isSearching();
|
const searching = this.isSearching;
|
||||||
if (searching && this.didScroll) {
|
if (searching && this.didScroll) {
|
||||||
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
|
// 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
|
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||||
|
@ -91,15 +91,16 @@ export class GroupsComponent implements OnInit, OnDestroy {
|
|||||||
private pagedGroupsCount = 0;
|
private pagedGroupsCount = 0;
|
||||||
private pagedGroups: GroupDetailsRow[];
|
private pagedGroups: GroupDetailsRow[];
|
||||||
private searchedGroups: GroupDetailsRow[];
|
private searchedGroups: GroupDetailsRow[];
|
||||||
private _searchText: string;
|
private _searchText$ = new BehaviorSubject<string>("");
|
||||||
private destroy$ = new Subject<void>();
|
private destroy$ = new Subject<void>();
|
||||||
private refreshGroups$ = new BehaviorSubject<void>(null);
|
private refreshGroups$ = new BehaviorSubject<void>(null);
|
||||||
|
private isSearching: boolean = false;
|
||||||
|
|
||||||
get searchText() {
|
get searchText() {
|
||||||
return this._searchText;
|
return this._searchText$.value;
|
||||||
}
|
}
|
||||||
set searchText(value: string) {
|
set searchText(value: string) {
|
||||||
this._searchText = value;
|
this._searchText$.next(value);
|
||||||
// Manually update as we are not using the search pipe in the template
|
// Manually update as we are not using the search pipe in the template
|
||||||
this.updateSearchedGroups();
|
this.updateSearchedGroups();
|
||||||
}
|
}
|
||||||
@ -114,7 +115,7 @@ export class GroupsComponent implements OnInit, OnDestroy {
|
|||||||
if (this.isPaging()) {
|
if (this.isPaging()) {
|
||||||
return this.pagedGroups;
|
return this.pagedGroups;
|
||||||
}
|
}
|
||||||
if (this.isSearching()) {
|
if (this.isSearching) {
|
||||||
return this.searchedGroups;
|
return this.searchedGroups;
|
||||||
}
|
}
|
||||||
return this.groups;
|
return this.groups;
|
||||||
@ -180,6 +181,15 @@ export class GroupsComponent implements OnInit, OnDestroy {
|
|||||||
takeUntil(this.destroy$),
|
takeUntil(this.destroy$),
|
||||||
)
|
)
|
||||||
.subscribe();
|
.subscribe();
|
||||||
|
|
||||||
|
this._searchText$
|
||||||
|
.pipe(
|
||||||
|
switchMap((searchText) => this.searchService.isSearchable(searchText)),
|
||||||
|
takeUntil(this.destroy$),
|
||||||
|
)
|
||||||
|
.subscribe((isSearchable) => {
|
||||||
|
this.isSearching = isSearchable;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnDestroy() {
|
ngOnDestroy() {
|
||||||
@ -297,10 +307,6 @@ export class GroupsComponent implements OnInit, OnDestroy {
|
|||||||
this.loadMore();
|
this.loadMore();
|
||||||
}
|
}
|
||||||
|
|
||||||
isSearching() {
|
|
||||||
return this.searchService.isSearchable(this.searchText);
|
|
||||||
}
|
|
||||||
|
|
||||||
check(groupRow: GroupDetailsRow) {
|
check(groupRow: GroupDetailsRow) {
|
||||||
groupRow.checked = !groupRow.checked;
|
groupRow.checked = !groupRow.checked;
|
||||||
}
|
}
|
||||||
@ -310,7 +316,7 @@ export class GroupsComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
isPaging() {
|
isPaging() {
|
||||||
const searching = this.isSearching();
|
const searching = this.isSearching;
|
||||||
if (searching && this.didScroll) {
|
if (searching && this.didScroll) {
|
||||||
this.resetPaging();
|
this.resetPaging();
|
||||||
}
|
}
|
||||||
@ -340,7 +346,7 @@ export class GroupsComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private updateSearchedGroups() {
|
private updateSearchedGroups() {
|
||||||
if (this.searchService.isSearchable(this.searchText)) {
|
if (this.isSearching) {
|
||||||
// Making use of the pipe in the component as we need know which groups where filtered
|
// Making use of the pipe in the component as we need know which groups where filtered
|
||||||
this.searchedGroups = this.searchPipe.transform(
|
this.searchedGroups = this.searchPipe.transform(
|
||||||
this.groups,
|
this.groups,
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { Component, OnDestroy, OnInit, ViewChild, ViewContainerRef } from "@angular/core";
|
import { Component, ViewChild, ViewContainerRef } from "@angular/core";
|
||||||
import { ActivatedRoute, Router } from "@angular/router";
|
import { ActivatedRoute, Router } from "@angular/router";
|
||||||
import {
|
import {
|
||||||
combineLatest,
|
combineLatest,
|
||||||
@ -9,7 +9,6 @@ import {
|
|||||||
map,
|
map,
|
||||||
Observable,
|
Observable,
|
||||||
shareReplay,
|
shareReplay,
|
||||||
Subject,
|
|
||||||
switchMap,
|
switchMap,
|
||||||
takeUntil,
|
takeUntil,
|
||||||
} from "rxjs";
|
} from "rxjs";
|
||||||
@ -73,10 +72,7 @@ import { ResetPasswordComponent } from "./components/reset-password.component";
|
|||||||
selector: "app-org-people",
|
selector: "app-org-people",
|
||||||
templateUrl: "people.component.html",
|
templateUrl: "people.component.html",
|
||||||
})
|
})
|
||||||
export class PeopleComponent
|
export class PeopleComponent extends BasePeopleComponent<OrganizationUserView> {
|
||||||
extends BasePeopleComponent<OrganizationUserView>
|
|
||||||
implements OnInit, OnDestroy
|
|
||||||
{
|
|
||||||
@ViewChild("groupsTemplate", { read: ViewContainerRef, static: true })
|
@ViewChild("groupsTemplate", { read: ViewContainerRef, static: true })
|
||||||
groupsModalRef: ViewContainerRef;
|
groupsModalRef: ViewContainerRef;
|
||||||
@ViewChild("confirmTemplate", { read: ViewContainerRef, static: true })
|
@ViewChild("confirmTemplate", { read: ViewContainerRef, static: true })
|
||||||
@ -99,7 +95,6 @@ export class PeopleComponent
|
|||||||
orgResetPasswordPolicyEnabled = false;
|
orgResetPasswordPolicyEnabled = false;
|
||||||
|
|
||||||
protected canUseSecretsManager$: Observable<boolean>;
|
protected canUseSecretsManager$: Observable<boolean>;
|
||||||
private destroy$ = new Subject<void>();
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
apiService: ApiService,
|
apiService: ApiService,
|
||||||
@ -210,8 +205,7 @@ export class PeopleComponent
|
|||||||
}
|
}
|
||||||
|
|
||||||
ngOnDestroy(): void {
|
ngOnDestroy(): void {
|
||||||
this.destroy$.next();
|
super.ngOnDestroy();
|
||||||
this.destroy$.complete();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async load() {
|
async load() {
|
||||||
|
@ -281,7 +281,7 @@ export class AppComponent implements OnDestroy, OnInit {
|
|||||||
|
|
||||||
await this.stateEventRunnerService.handleEvent("logout", userId as UserId);
|
await this.stateEventRunnerService.handleEvent("logout", userId as UserId);
|
||||||
|
|
||||||
this.searchService.clearIndex();
|
await this.searchService.clearIndex();
|
||||||
this.authService.logOut(async () => {
|
this.authService.logOut(async () => {
|
||||||
if (expired) {
|
if (expired) {
|
||||||
this.platformUtilsService.showToast(
|
this.platformUtilsService.showToast(
|
||||||
|
@ -2,6 +2,7 @@ import { mock, MockProxy } from "jest-mock-extended";
|
|||||||
import { BehaviorSubject } from "rxjs";
|
import { BehaviorSubject } from "rxjs";
|
||||||
|
|
||||||
import { DeviceTrustCryptoServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust-crypto.service.abstraction";
|
import { DeviceTrustCryptoServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust-crypto.service.abstraction";
|
||||||
|
import { FakeMasterPasswordService } from "@bitwarden/common/auth/services/master-password/fake-master-password.service";
|
||||||
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
||||||
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
|
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
|
||||||
import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service";
|
import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service";
|
||||||
@ -9,7 +10,6 @@ import { EncryptionType } from "@bitwarden/common/platform/enums";
|
|||||||
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
||||||
import { EncString } from "@bitwarden/common/platform/models/domain/enc-string";
|
import { EncString } from "@bitwarden/common/platform/models/domain/enc-string";
|
||||||
import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key";
|
import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key";
|
||||||
import { FakeAccountService, mockAccountServiceWith } from "@bitwarden/common/spec";
|
|
||||||
import { Send } from "@bitwarden/common/tools/send/models/domain/send";
|
import { Send } from "@bitwarden/common/tools/send/models/domain/send";
|
||||||
import { SendService } from "@bitwarden/common/tools/send/services/send.service.abstraction";
|
import { SendService } from "@bitwarden/common/tools/send/services/send.service.abstraction";
|
||||||
import { UserId } from "@bitwarden/common/types/guid";
|
import { UserId } from "@bitwarden/common/types/guid";
|
||||||
@ -22,6 +22,10 @@ import { Folder } from "@bitwarden/common/vault/models/domain/folder";
|
|||||||
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
|
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
|
||||||
import { FolderView } from "@bitwarden/common/vault/models/view/folder.view";
|
import { FolderView } from "@bitwarden/common/vault/models/view/folder.view";
|
||||||
|
|
||||||
|
import {
|
||||||
|
FakeAccountService,
|
||||||
|
mockAccountServiceWith,
|
||||||
|
} from "../../../../../../libs/common/spec/fake-account-service";
|
||||||
import { OrganizationUserResetPasswordService } from "../../admin-console/organizations/members/services/organization-user-reset-password/organization-user-reset-password.service";
|
import { OrganizationUserResetPasswordService } from "../../admin-console/organizations/members/services/organization-user-reset-password/organization-user-reset-password.service";
|
||||||
import { StateService } from "../../core";
|
import { StateService } from "../../core";
|
||||||
import { EmergencyAccessService } from "../emergency-access";
|
import { EmergencyAccessService } from "../emergency-access";
|
||||||
@ -46,8 +50,10 @@ describe("KeyRotationService", () => {
|
|||||||
|
|
||||||
const mockUserId = Utils.newGuid() as UserId;
|
const mockUserId = Utils.newGuid() as UserId;
|
||||||
const mockAccountService: FakeAccountService = mockAccountServiceWith(mockUserId);
|
const mockAccountService: FakeAccountService = mockAccountServiceWith(mockUserId);
|
||||||
|
let mockMasterPasswordService: FakeMasterPasswordService = new FakeMasterPasswordService();
|
||||||
|
|
||||||
beforeAll(() => {
|
beforeAll(() => {
|
||||||
|
mockMasterPasswordService = new FakeMasterPasswordService();
|
||||||
mockApiService = mock<UserKeyRotationApiService>();
|
mockApiService = mock<UserKeyRotationApiService>();
|
||||||
mockCipherService = mock<CipherService>();
|
mockCipherService = mock<CipherService>();
|
||||||
mockFolderService = mock<FolderService>();
|
mockFolderService = mock<FolderService>();
|
||||||
@ -61,6 +67,7 @@ describe("KeyRotationService", () => {
|
|||||||
mockConfigService = mock<ConfigService>();
|
mockConfigService = mock<ConfigService>();
|
||||||
|
|
||||||
keyRotationService = new UserKeyRotationService(
|
keyRotationService = new UserKeyRotationService(
|
||||||
|
mockMasterPasswordService,
|
||||||
mockApiService,
|
mockApiService,
|
||||||
mockCipherService,
|
mockCipherService,
|
||||||
mockFolderService,
|
mockFolderService,
|
||||||
@ -174,7 +181,10 @@ describe("KeyRotationService", () => {
|
|||||||
it("saves the master key in state after creation", async () => {
|
it("saves the master key in state after creation", async () => {
|
||||||
await keyRotationService.rotateUserKeyAndEncryptedData("mockMasterPassword");
|
await keyRotationService.rotateUserKeyAndEncryptedData("mockMasterPassword");
|
||||||
|
|
||||||
expect(mockCryptoService.setMasterKey).toHaveBeenCalledWith("mockMasterKey" as any);
|
expect(mockMasterPasswordService.mock.setMasterKey).toHaveBeenCalledWith(
|
||||||
|
"mockMasterKey" as any,
|
||||||
|
mockUserId,
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("uses legacy rotation if feature flag is off", async () => {
|
it("uses legacy rotation if feature flag is off", async () => {
|
||||||
|
@ -3,6 +3,7 @@ import { firstValueFrom } from "rxjs";
|
|||||||
|
|
||||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||||
import { DeviceTrustCryptoServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust-crypto.service.abstraction";
|
import { DeviceTrustCryptoServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust-crypto.service.abstraction";
|
||||||
|
import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/auth/abstractions/master-password.service.abstraction";
|
||||||
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
|
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
|
||||||
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
||||||
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
|
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
|
||||||
@ -25,6 +26,7 @@ import { UserKeyRotationApiService } from "./user-key-rotation-api.service";
|
|||||||
@Injectable()
|
@Injectable()
|
||||||
export class UserKeyRotationService {
|
export class UserKeyRotationService {
|
||||||
constructor(
|
constructor(
|
||||||
|
private masterPasswordService: InternalMasterPasswordServiceAbstraction,
|
||||||
private apiService: UserKeyRotationApiService,
|
private apiService: UserKeyRotationApiService,
|
||||||
private cipherService: CipherService,
|
private cipherService: CipherService,
|
||||||
private folderService: FolderService,
|
private folderService: FolderService,
|
||||||
@ -61,7 +63,8 @@ export class UserKeyRotationService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Set master key again in case it was lost (could be lost on refresh)
|
// Set master key again in case it was lost (could be lost on refresh)
|
||||||
await this.cryptoService.setMasterKey(masterKey);
|
const userId = (await firstValueFrom(this.accountService.activeAccount$))?.id;
|
||||||
|
await this.masterPasswordService.setMasterKey(masterKey, userId);
|
||||||
const [newUserKey, newEncUserKey] = await this.cryptoService.makeUserKey(masterKey);
|
const [newUserKey, newEncUserKey] = await this.cryptoService.makeUserKey(masterKey);
|
||||||
|
|
||||||
if (!newUserKey || !newEncUserKey) {
|
if (!newUserKey || !newEncUserKey) {
|
||||||
|
@ -1,80 +1,12 @@
|
|||||||
import { Component, NgZone } from "@angular/core";
|
import { Component } from "@angular/core";
|
||||||
import { Router } from "@angular/router";
|
|
||||||
|
|
||||||
import { LockComponent as BaseLockComponent } from "@bitwarden/angular/auth/components/lock.component";
|
import { LockComponent as BaseLockComponent } from "@bitwarden/angular/auth/components/lock.component";
|
||||||
import { PinCryptoServiceAbstraction } from "@bitwarden/auth/common";
|
|
||||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
|
||||||
import { VaultTimeoutSettingsService } from "@bitwarden/common/abstractions/vault-timeout/vault-timeout-settings.service";
|
|
||||||
import { VaultTimeoutService } from "@bitwarden/common/abstractions/vault-timeout/vault-timeout.service";
|
|
||||||
import { PolicyApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/policy/policy-api.service.abstraction";
|
|
||||||
import { InternalPolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
|
|
||||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
|
||||||
import { DeviceTrustCryptoServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust-crypto.service.abstraction";
|
|
||||||
import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction";
|
|
||||||
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";
|
|
||||||
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 { BiometricStateService } from "@bitwarden/common/platform/biometrics/biometric-state.service";
|
|
||||||
import { PasswordStrengthServiceAbstraction } from "@bitwarden/common/tools/password-strength";
|
|
||||||
import { DialogService } from "@bitwarden/components";
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: "app-lock",
|
selector: "app-lock",
|
||||||
templateUrl: "lock.component.html",
|
templateUrl: "lock.component.html",
|
||||||
})
|
})
|
||||||
export class LockComponent extends BaseLockComponent {
|
export class LockComponent extends BaseLockComponent {
|
||||||
constructor(
|
|
||||||
router: Router,
|
|
||||||
i18nService: I18nService,
|
|
||||||
platformUtilsService: PlatformUtilsService,
|
|
||||||
messagingService: MessagingService,
|
|
||||||
cryptoService: CryptoService,
|
|
||||||
vaultTimeoutService: VaultTimeoutService,
|
|
||||||
vaultTimeoutSettingsService: VaultTimeoutSettingsService,
|
|
||||||
environmentService: EnvironmentService,
|
|
||||||
stateService: StateService,
|
|
||||||
apiService: ApiService,
|
|
||||||
logService: LogService,
|
|
||||||
ngZone: NgZone,
|
|
||||||
policyApiService: PolicyApiServiceAbstraction,
|
|
||||||
policyService: InternalPolicyService,
|
|
||||||
passwordStrengthService: PasswordStrengthServiceAbstraction,
|
|
||||||
dialogService: DialogService,
|
|
||||||
deviceTrustCryptoService: DeviceTrustCryptoServiceAbstraction,
|
|
||||||
userVerificationService: UserVerificationService,
|
|
||||||
pinCryptoService: PinCryptoServiceAbstraction,
|
|
||||||
biometricStateService: BiometricStateService,
|
|
||||||
accountService: AccountService,
|
|
||||||
) {
|
|
||||||
super(
|
|
||||||
router,
|
|
||||||
i18nService,
|
|
||||||
platformUtilsService,
|
|
||||||
messagingService,
|
|
||||||
cryptoService,
|
|
||||||
vaultTimeoutService,
|
|
||||||
vaultTimeoutSettingsService,
|
|
||||||
environmentService,
|
|
||||||
stateService,
|
|
||||||
apiService,
|
|
||||||
logService,
|
|
||||||
ngZone,
|
|
||||||
policyApiService,
|
|
||||||
policyService,
|
|
||||||
passwordStrengthService,
|
|
||||||
dialogService,
|
|
||||||
deviceTrustCryptoService,
|
|
||||||
userVerificationService,
|
|
||||||
pinCryptoService,
|
|
||||||
biometricStateService,
|
|
||||||
accountService,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
async ngOnInit() {
|
async ngOnInit() {
|
||||||
await super.ngOnInit();
|
await super.ngOnInit();
|
||||||
this.onSuccessfulSubmit = async () => {
|
this.onSuccessfulSubmit = async () => {
|
||||||
|
@ -10,6 +10,8 @@ import {
|
|||||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||||
import { OrgDomainApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization-domain/org-domain-api.service.abstraction";
|
import { OrgDomainApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization-domain/org-domain-api.service.abstraction";
|
||||||
import { OrganizationDomainSsoDetailsResponse } from "@bitwarden/common/admin-console/abstractions/organization-domain/responses/organization-domain-sso-details.response";
|
import { OrganizationDomainSsoDetailsResponse } from "@bitwarden/common/admin-console/abstractions/organization-domain/responses/organization-domain-sso-details.response";
|
||||||
|
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 { SsoLoginServiceAbstraction } from "@bitwarden/common/auth/abstractions/sso-login.service.abstraction";
|
||||||
import { HttpStatusCode } from "@bitwarden/common/enums";
|
import { HttpStatusCode } from "@bitwarden/common/enums";
|
||||||
import { ErrorResponse } from "@bitwarden/common/models/response/error.response";
|
import { ErrorResponse } from "@bitwarden/common/models/response/error.response";
|
||||||
@ -46,6 +48,8 @@ export class SsoComponent extends BaseSsoComponent {
|
|||||||
private validationService: ValidationService,
|
private validationService: ValidationService,
|
||||||
userDecryptionOptionsService: UserDecryptionOptionsServiceAbstraction,
|
userDecryptionOptionsService: UserDecryptionOptionsServiceAbstraction,
|
||||||
configService: ConfigService,
|
configService: ConfigService,
|
||||||
|
masterPasswordService: InternalMasterPasswordServiceAbstraction,
|
||||||
|
accountService: AccountService,
|
||||||
) {
|
) {
|
||||||
super(
|
super(
|
||||||
ssoLoginService,
|
ssoLoginService,
|
||||||
@ -62,6 +66,8 @@ export class SsoComponent extends BaseSsoComponent {
|
|||||||
logService,
|
logService,
|
||||||
userDecryptionOptionsService,
|
userDecryptionOptionsService,
|
||||||
configService,
|
configService,
|
||||||
|
masterPasswordService,
|
||||||
|
accountService,
|
||||||
);
|
);
|
||||||
this.redirectUri = window.location.origin + "/sso-connector.html";
|
this.redirectUri = window.location.origin + "/sso-connector.html";
|
||||||
this.clientId = "web";
|
this.clientId = "web";
|
||||||
|
@ -10,6 +10,8 @@ import {
|
|||||||
UserDecryptionOptionsServiceAbstraction,
|
UserDecryptionOptionsServiceAbstraction,
|
||||||
} from "@bitwarden/auth/common";
|
} from "@bitwarden/auth/common";
|
||||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
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 { SsoLoginServiceAbstraction } from "@bitwarden/common/auth/abstractions/sso-login.service.abstraction";
|
||||||
import { TwoFactorService } from "@bitwarden/common/auth/abstractions/two-factor.service";
|
import { TwoFactorService } from "@bitwarden/common/auth/abstractions/two-factor.service";
|
||||||
import { TwoFactorProviderType } from "@bitwarden/common/auth/enums/two-factor-provider-type";
|
import { TwoFactorProviderType } from "@bitwarden/common/auth/enums/two-factor-provider-type";
|
||||||
@ -50,6 +52,8 @@ export class TwoFactorComponent extends BaseTwoFactorComponent implements OnDest
|
|||||||
userDecryptionOptionsService: UserDecryptionOptionsServiceAbstraction,
|
userDecryptionOptionsService: UserDecryptionOptionsServiceAbstraction,
|
||||||
ssoLoginService: SsoLoginServiceAbstraction,
|
ssoLoginService: SsoLoginServiceAbstraction,
|
||||||
configService: ConfigService,
|
configService: ConfigService,
|
||||||
|
masterPasswordService: InternalMasterPasswordServiceAbstraction,
|
||||||
|
accountService: AccountService,
|
||||||
@Inject(WINDOW) protected win: Window,
|
@Inject(WINDOW) protected win: Window,
|
||||||
) {
|
) {
|
||||||
super(
|
super(
|
||||||
@ -69,6 +73,8 @@ export class TwoFactorComponent extends BaseTwoFactorComponent implements OnDest
|
|||||||
userDecryptionOptionsService,
|
userDecryptionOptionsService,
|
||||||
ssoLoginService,
|
ssoLoginService,
|
||||||
configService,
|
configService,
|
||||||
|
masterPasswordService,
|
||||||
|
accountService,
|
||||||
);
|
);
|
||||||
this.onSuccessfulLoginNavigate = this.goAfterLogIn;
|
this.onSuccessfulLoginNavigate = this.goAfterLogIn;
|
||||||
}
|
}
|
||||||
|
@ -279,10 +279,7 @@
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div class="col-6" *ngIf="showTaxIdCheckbox">
|
||||||
class="col-6"
|
|
||||||
*ngIf="organizationId && taxInfo.country !== 'US' && countrySupportsTax(taxInfo.country)"
|
|
||||||
>
|
|
||||||
<div class="form-group form-check">
|
<div class="form-group form-check">
|
||||||
<input
|
<input
|
||||||
class="form-check-input"
|
class="form-check-input"
|
||||||
@ -295,21 +292,22 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div class="row" *ngIf="showTaxIdFields">
|
||||||
class="row"
|
|
||||||
*ngIf="organizationId && taxInfo.includeTaxId && countrySupportsTax(taxInfo.country)"
|
|
||||||
>
|
|
||||||
<div class="col-6">
|
<div class="col-6">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="taxId">{{ "taxIdNumber" | i18n }}</label>
|
<label for="taxId">{{ "taxIdNumber" | i18n }}</label>
|
||||||
<input id="taxId" class="form-control" type="text" name="taxId" [(ngModel)]="taxInfo.taxId" />
|
<input
|
||||||
|
id="taxId"
|
||||||
|
class="form-control"
|
||||||
|
type="text"
|
||||||
|
name="taxId"
|
||||||
|
[(ngModel)]="taxInfo.taxId"
|
||||||
|
[required]="taxInfo.includeTaxId"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div class="row" *ngIf="showTaxIdFields">
|
||||||
class="row"
|
|
||||||
*ngIf="organizationId && taxInfo.includeTaxId && countrySupportsTax(taxInfo.country)"
|
|
||||||
>
|
|
||||||
<div class="col-6">
|
<div class="col-6">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="addressLine1">{{ "address1" | i18n }}</label>
|
<label for="addressLine1">{{ "address1" | i18n }}</label>
|
||||||
|
@ -3,7 +3,7 @@ import { ActivatedRoute } from "@angular/router";
|
|||||||
|
|
||||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||||
import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.service.abstraction";
|
import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.service.abstraction";
|
||||||
import { OrganizationTaxInfoUpdateRequest } from "@bitwarden/common/billing/models/request/organization-tax-info-update.request";
|
import { ExpandedTaxInfoUpdateRequest } from "@bitwarden/common/billing/models/request/expanded-tax-info-update.request";
|
||||||
import { TaxInfoUpdateRequest } from "@bitwarden/common/billing/models/request/tax-info-update.request";
|
import { TaxInfoUpdateRequest } from "@bitwarden/common/billing/models/request/tax-info-update.request";
|
||||||
import { TaxInfoResponse } from "@bitwarden/common/billing/models/response/tax-info.response";
|
import { TaxInfoResponse } from "@bitwarden/common/billing/models/response/tax-info.response";
|
||||||
import { TaxRateResponse } from "@bitwarden/common/billing/models/response/tax-rate.response";
|
import { TaxRateResponse } from "@bitwarden/common/billing/models/response/tax-rate.response";
|
||||||
@ -29,6 +29,7 @@ export class TaxInfoComponent {
|
|||||||
|
|
||||||
loading = true;
|
loading = true;
|
||||||
organizationId: string;
|
organizationId: string;
|
||||||
|
providerId: string;
|
||||||
taxInfo: TaxInfoView = {
|
taxInfo: TaxInfoView = {
|
||||||
taxId: null,
|
taxId: null,
|
||||||
line1: null,
|
line1: null,
|
||||||
@ -61,6 +62,12 @@ export class TaxInfoComponent {
|
|||||||
) {}
|
) {}
|
||||||
|
|
||||||
async ngOnInit() {
|
async ngOnInit() {
|
||||||
|
// Provider setup
|
||||||
|
// eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe
|
||||||
|
this.route.queryParams.subscribe((params) => {
|
||||||
|
this.providerId = params.providerId;
|
||||||
|
});
|
||||||
|
|
||||||
// eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe
|
// eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe
|
||||||
this.route.parent.parent.params.subscribe(async (params) => {
|
this.route.parent.parent.params.subscribe(async (params) => {
|
||||||
this.organizationId = params.organizationId;
|
this.organizationId = params.organizationId;
|
||||||
@ -126,9 +133,25 @@ export class TaxInfoComponent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get showTaxIdCheckbox() {
|
||||||
|
return (
|
||||||
|
(this.organizationId || this.providerId) &&
|
||||||
|
this.taxInfo.country !== "US" &&
|
||||||
|
this.countrySupportsTax(this.taxInfo.country)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
get showTaxIdFields() {
|
||||||
|
return (
|
||||||
|
(this.organizationId || this.providerId) &&
|
||||||
|
this.taxInfo.includeTaxId &&
|
||||||
|
this.countrySupportsTax(this.taxInfo.country)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
getTaxInfoRequest(): TaxInfoUpdateRequest {
|
getTaxInfoRequest(): TaxInfoUpdateRequest {
|
||||||
if (this.organizationId) {
|
if (this.organizationId || this.providerId) {
|
||||||
const request = new OrganizationTaxInfoUpdateRequest();
|
const request = new ExpandedTaxInfoUpdateRequest();
|
||||||
request.country = this.taxInfo.country;
|
request.country = this.taxInfo.country;
|
||||||
request.postalCode = this.taxInfo.postalCode;
|
request.postalCode = this.taxInfo.postalCode;
|
||||||
|
|
||||||
@ -164,7 +187,7 @@ export class TaxInfoComponent {
|
|||||||
return this.organizationId
|
return this.organizationId
|
||||||
? this.organizationApiService.updateTaxInfo(
|
? this.organizationApiService.updateTaxInfo(
|
||||||
this.organizationId,
|
this.organizationId,
|
||||||
request as OrganizationTaxInfoUpdateRequest,
|
request as ExpandedTaxInfoUpdateRequest,
|
||||||
)
|
)
|
||||||
: this.apiService.putTaxInfo(request);
|
: this.apiService.putTaxInfo(request);
|
||||||
}
|
}
|
||||||
|
@ -1,16 +1,18 @@
|
|||||||
<bit-banner
|
<bit-banner
|
||||||
class="-tw-m-6 tw-flex tw-flex-col tw-pb-6"
|
class="-tw-m-6 tw-flex tw-flex-col tw-pb-6"
|
||||||
(onClose)="webLayoutMigrationBannerService.hideBanner()"
|
(onClose)="unassignedItemsBannerService.hideBanner()"
|
||||||
*ngIf="webLayoutMigrationBannerService.showBanner$ | async"
|
*ngIf="
|
||||||
|
(unassignedItemsBannerEnabled$ | async) && (unassignedItemsBannerService.showBanner$ | async)
|
||||||
|
"
|
||||||
>
|
>
|
||||||
{{ "newWebApp" | i18n }}
|
{{ "unassignedItemsBanner" | i18n }}
|
||||||
<a
|
<a
|
||||||
href="https://bitwarden.com/blog/bitwarden-design-updating-the-navigation-in-the-web-app"
|
href="https://bitwarden.com/help/unassigned-vault-items-moved-to-admin-console"
|
||||||
bitLink
|
bitLink
|
||||||
linkType="contrast"
|
linkType="contrast"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noreferrer"
|
rel="noreferrer"
|
||||||
>{{ "releaseBlog" | i18n }}</a
|
>{{ "learnMore" | i18n }}</a
|
||||||
>
|
>
|
||||||
</bit-banner>
|
</bit-banner>
|
||||||
<header
|
<header
|
||||||
|
@ -2,15 +2,16 @@ import { Component, Input } from "@angular/core";
|
|||||||
import { ActivatedRoute } from "@angular/router";
|
import { ActivatedRoute } from "@angular/router";
|
||||||
import { combineLatest, map, Observable } from "rxjs";
|
import { combineLatest, map, Observable } from "rxjs";
|
||||||
|
|
||||||
|
import { UnassignedItemsBannerService } from "@bitwarden/angular/services/unassigned-items-banner.service";
|
||||||
import { VaultTimeoutSettingsService } from "@bitwarden/common/abstractions/vault-timeout/vault-timeout-settings.service";
|
import { VaultTimeoutSettingsService } from "@bitwarden/common/abstractions/vault-timeout/vault-timeout-settings.service";
|
||||||
|
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
|
||||||
import { VaultTimeoutAction } from "@bitwarden/common/enums/vault-timeout-action.enum";
|
import { VaultTimeoutAction } from "@bitwarden/common/enums/vault-timeout-action.enum";
|
||||||
|
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
||||||
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
|
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
|
||||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||||
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
|
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
|
||||||
import { AccountProfile } from "@bitwarden/common/platform/models/domain/account";
|
import { AccountProfile } from "@bitwarden/common/platform/models/domain/account";
|
||||||
|
|
||||||
import { WebLayoutMigrationBannerService } from "./web-layout-migration-banner.service";
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: "app-header",
|
selector: "app-header",
|
||||||
templateUrl: "./web-header.component.html",
|
templateUrl: "./web-header.component.html",
|
||||||
@ -31,6 +32,9 @@ export class WebHeaderComponent {
|
|||||||
protected canLock$: Observable<boolean>;
|
protected canLock$: Observable<boolean>;
|
||||||
protected selfHosted: boolean;
|
protected selfHosted: boolean;
|
||||||
protected hostname = location.hostname;
|
protected hostname = location.hostname;
|
||||||
|
protected unassignedItemsBannerEnabled$ = this.configService.getFeatureFlag$(
|
||||||
|
FeatureFlag.UnassignedItemsBanner,
|
||||||
|
);
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private route: ActivatedRoute,
|
private route: ActivatedRoute,
|
||||||
@ -38,7 +42,8 @@ export class WebHeaderComponent {
|
|||||||
private platformUtilsService: PlatformUtilsService,
|
private platformUtilsService: PlatformUtilsService,
|
||||||
private vaultTimeoutSettingsService: VaultTimeoutSettingsService,
|
private vaultTimeoutSettingsService: VaultTimeoutSettingsService,
|
||||||
private messagingService: MessagingService,
|
private messagingService: MessagingService,
|
||||||
protected webLayoutMigrationBannerService: WebLayoutMigrationBannerService,
|
protected unassignedItemsBannerService: UnassignedItemsBannerService,
|
||||||
|
private configService: ConfigService,
|
||||||
) {
|
) {
|
||||||
this.routeData$ = this.route.data.pipe(
|
this.routeData$ = this.route.data.pipe(
|
||||||
map((params) => {
|
map((params) => {
|
||||||
|
@ -272,7 +272,7 @@ export class VaultComponent implements OnInit, OnDestroy {
|
|||||||
concatMap(async ([ciphers, filter, searchText]) => {
|
concatMap(async ([ciphers, filter, searchText]) => {
|
||||||
const filterFunction = createFilterFunction(filter);
|
const filterFunction = createFilterFunction(filter);
|
||||||
|
|
||||||
if (this.searchService.isSearchable(searchText)) {
|
if (await this.searchService.isSearchable(searchText)) {
|
||||||
return await this.searchService.searchCiphers(searchText, [filterFunction], ciphers);
|
return await this.searchService.searchCiphers(searchText, [filterFunction], ciphers);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -283,7 +283,7 @@ export class VaultComponent implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
const collections$ = combineLatest([nestedCollections$, filter$, this.currentSearchText$]).pipe(
|
const collections$ = combineLatest([nestedCollections$, filter$, this.currentSearchText$]).pipe(
|
||||||
filter(([collections, filter]) => collections != undefined && filter != undefined),
|
filter(([collections, filter]) => collections != undefined && filter != undefined),
|
||||||
map(([collections, filter, searchText]) => {
|
concatMap(async ([collections, filter, searchText]) => {
|
||||||
if (filter.collectionId === undefined || filter.collectionId === Unassigned) {
|
if (filter.collectionId === undefined || filter.collectionId === Unassigned) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
@ -303,7 +303,7 @@ export class VaultComponent implements OnInit, OnDestroy {
|
|||||||
collectionsToReturn = selectedCollection?.children.map((c) => c.node) ?? [];
|
collectionsToReturn = selectedCollection?.children.map((c) => c.node) ?? [];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.searchService.isSearchable(searchText)) {
|
if (await this.searchService.isSearchable(searchText)) {
|
||||||
collectionsToReturn = this.searchPipe.transform(
|
collectionsToReturn = this.searchPipe.transform(
|
||||||
collectionsToReturn,
|
collectionsToReturn,
|
||||||
searchText,
|
searchText,
|
||||||
|
@ -331,7 +331,7 @@ export class VaultComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.searchService.indexCiphers(ciphers, organization.id);
|
await this.searchService.indexCiphers(ciphers, organization.id);
|
||||||
return ciphers;
|
return ciphers;
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
@ -350,7 +350,7 @@ export class VaultComponent implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
const collections$ = combineLatest([nestedCollections$, filter$, this.currentSearchText$]).pipe(
|
const collections$ = combineLatest([nestedCollections$, filter$, this.currentSearchText$]).pipe(
|
||||||
filter(([collections, filter]) => collections != undefined && filter != undefined),
|
filter(([collections, filter]) => collections != undefined && filter != undefined),
|
||||||
map(([collections, filter, searchText]) => {
|
concatMap(async ([collections, filter, searchText]) => {
|
||||||
if (
|
if (
|
||||||
filter.collectionId === Unassigned ||
|
filter.collectionId === Unassigned ||
|
||||||
(filter.collectionId === undefined && filter.type !== undefined)
|
(filter.collectionId === undefined && filter.type !== undefined)
|
||||||
@ -369,7 +369,7 @@ export class VaultComponent implements OnInit, OnDestroy {
|
|||||||
collectionsToReturn = selectedCollection?.children.map((c) => c.node) ?? [];
|
collectionsToReturn = selectedCollection?.children.map((c) => c.node) ?? [];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.searchService.isSearchable(searchText)) {
|
if (await this.searchService.isSearchable(searchText)) {
|
||||||
collectionsToReturn = this.searchPipe.transform(
|
collectionsToReturn = this.searchPipe.transform(
|
||||||
collectionsToReturn,
|
collectionsToReturn,
|
||||||
searchText,
|
searchText,
|
||||||
@ -436,7 +436,7 @@ export class VaultComponent implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
const filterFunction = createFilterFunction(filter);
|
const filterFunction = createFilterFunction(filter);
|
||||||
|
|
||||||
if (this.searchService.isSearchable(searchText)) {
|
if (await this.searchService.isSearchable(searchText)) {
|
||||||
return await this.searchService.searchCiphers(searchText, [filterFunction], ciphers);
|
return await this.searchService.searchCiphers(searchText, [filterFunction], ciphers);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7899,5 +7899,8 @@
|
|||||||
},
|
},
|
||||||
"machineAccountAccessUpdated": {
|
"machineAccountAccessUpdated": {
|
||||||
"message": "Machine account access updated"
|
"message": "Machine account access updated"
|
||||||
|
},
|
||||||
|
"unassignedItemsBanner": {
|
||||||
|
"message": "Notice: Unassigned organization items are no longer visible in your All Vaults view across devices and are now only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -26,7 +26,7 @@
|
|||||||
<bit-hint>{{ "domainNameInputHint" | i18n }}</bit-hint>
|
<bit-hint>{{ "domainNameInputHint" | i18n }}</bit-hint>
|
||||||
</bit-form-field>
|
</bit-form-field>
|
||||||
|
|
||||||
<bit-form-field>
|
<bit-form-field *ngIf="data?.orgDomain">
|
||||||
<bit-label>{{ "dnsTxtRecord" | i18n }}</bit-label>
|
<bit-label>{{ "dnsTxtRecord" | i18n }}</bit-label>
|
||||||
<input bitInput formControlName="txt" />
|
<input bitInput formControlName="txt" />
|
||||||
<bit-hint>{{ "dnsTxtRecordInputHint" | i18n }}</bit-hint>
|
<bit-hint>{{ "dnsTxtRecordInputHint" | i18n }}</bit-hint>
|
||||||
@ -42,7 +42,7 @@
|
|||||||
</bit-form-field>
|
</bit-form-field>
|
||||||
|
|
||||||
<bit-callout
|
<bit-callout
|
||||||
*ngIf="!data?.orgDomain?.verifiedDate"
|
*ngIf="data?.orgDomain && !data?.orgDomain?.verifiedDate"
|
||||||
type="info"
|
type="info"
|
||||||
title="{{ 'automaticDomainVerification' | i18n }}"
|
title="{{ 'automaticDomainVerification' | i18n }}"
|
||||||
>
|
>
|
||||||
@ -51,7 +51,10 @@
|
|||||||
</div>
|
</div>
|
||||||
<ng-container bitDialogFooter>
|
<ng-container bitDialogFooter>
|
||||||
<button type="submit" bitButton bitFormButton buttonType="primary">
|
<button type="submit" bitButton bitFormButton buttonType="primary">
|
||||||
<span *ngIf="!data?.orgDomain?.verifiedDate">{{ "verifyDomain" | i18n }}</span>
|
<span *ngIf="!data?.orgDomain">{{ "next" | i18n }}</span>
|
||||||
|
<span *ngIf="data?.orgDomain && !data?.orgDomain?.verifiedDate">{{
|
||||||
|
"verifyDomain" | i18n
|
||||||
|
}}</span>
|
||||||
<span *ngIf="data?.orgDomain?.verifiedDate">{{ "reverifyDomain" | i18n }}</span>
|
<span *ngIf="data?.orgDomain?.verifiedDate">{{ "reverifyDomain" | i18n }}</span>
|
||||||
</button>
|
</button>
|
||||||
<button bitButton buttonType="secondary" (click)="dialogRef.close()" type="button">
|
<button bitButton buttonType="secondary" (click)="dialogRef.close()" type="button">
|
||||||
|
@ -13,7 +13,6 @@ import { CryptoFunctionService as CryptoFunctionServiceAbstraction } from "@bitw
|
|||||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||||
import { ValidationService } from "@bitwarden/common/platform/abstractions/validation.service";
|
import { ValidationService } from "@bitwarden/common/platform/abstractions/validation.service";
|
||||||
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
|
||||||
import { DialogService } from "@bitwarden/components";
|
import { DialogService } from "@bitwarden/components";
|
||||||
|
|
||||||
import { domainNameValidator } from "./validators/domain-name.validator";
|
import { domainNameValidator } from "./validators/domain-name.validator";
|
||||||
@ -90,17 +89,6 @@ export class DomainAddEditDialogComponent implements OnInit, OnDestroy {
|
|||||||
// Edit
|
// Edit
|
||||||
this.domainForm.patchValue(this.data.orgDomain);
|
this.domainForm.patchValue(this.data.orgDomain);
|
||||||
this.domainForm.disable();
|
this.domainForm.disable();
|
||||||
} else {
|
|
||||||
// Add
|
|
||||||
|
|
||||||
// Figuring out the proper length of our DNS TXT Record value was fun.
|
|
||||||
// DNS-Based Service Discovery RFC: https://www.ietf.org/rfc/rfc6763.txt; see section 6.1
|
|
||||||
// Google uses 43 chars for their TXT record value: https://support.google.com/a/answer/2716802
|
|
||||||
// So, chose a magic # of 33 bytes to achieve at least that once converted to base 64 (47 char length).
|
|
||||||
const generatedTxt = `bw=${Utils.fromBufferToB64(
|
|
||||||
await this.cryptoFunctionService.randomBytes(33),
|
|
||||||
)}`;
|
|
||||||
this.txtCtrl.setValue(generatedTxt);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.setupFormListeners();
|
this.setupFormListeners();
|
||||||
@ -121,6 +109,7 @@ export class DomainAddEditDialogComponent implements OnInit, OnDestroy {
|
|||||||
// End Form methods
|
// End Form methods
|
||||||
|
|
||||||
// Async Form Actions
|
// Async Form Actions
|
||||||
|
// Creates a new domain record. The DNS TXT Record will be generated server-side and returned in the response.
|
||||||
saveDomain = async (): Promise<void> => {
|
saveDomain = async (): Promise<void> => {
|
||||||
if (this.domainForm.invalid) {
|
if (this.domainForm.invalid) {
|
||||||
this.platformUtilsService.showToast("error", null, this.i18nService.t("domainFormInvalid"));
|
this.platformUtilsService.showToast("error", null, this.i18nService.t("domainFormInvalid"));
|
||||||
@ -130,14 +119,14 @@ export class DomainAddEditDialogComponent implements OnInit, OnDestroy {
|
|||||||
this.domainNameCtrl.disable();
|
this.domainNameCtrl.disable();
|
||||||
|
|
||||||
const request: OrganizationDomainRequest = new OrganizationDomainRequest(
|
const request: OrganizationDomainRequest = new OrganizationDomainRequest(
|
||||||
this.txtCtrl.value,
|
|
||||||
this.domainNameCtrl.value,
|
this.domainNameCtrl.value,
|
||||||
);
|
);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
this.data.orgDomain = await this.orgDomainApiService.post(this.data.organizationId, request);
|
this.data.orgDomain = await this.orgDomainApiService.post(this.data.organizationId, request);
|
||||||
|
// Patch the DNS TXT Record that was generated server-side
|
||||||
|
this.domainForm.controls.txt.patchValue(this.data.orgDomain.txt);
|
||||||
this.platformUtilsService.showToast("success", null, this.i18nService.t("domainSaved"));
|
this.platformUtilsService.showToast("success", null, this.i18nService.t("domainSaved"));
|
||||||
await this.verifyDomain();
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.handleDomainSaveError(e);
|
this.handleDomainSaveError(e);
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { Component, OnInit } from "@angular/core";
|
import { Component, OnInit } from "@angular/core";
|
||||||
import { ActivatedRoute, Router } from "@angular/router";
|
import { ActivatedRoute, Router } from "@angular/router";
|
||||||
import { firstValueFrom } from "rxjs";
|
import { BehaviorSubject, Subject, firstValueFrom, from } from "rxjs";
|
||||||
import { first } from "rxjs/operators";
|
import { first, switchMap, takeUntil } from "rxjs/operators";
|
||||||
|
|
||||||
import { ModalService } from "@bitwarden/angular/services/modal.service";
|
import { ModalService } from "@bitwarden/angular/services/modal.service";
|
||||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||||
@ -39,7 +39,6 @@ const DisallowedPlanTypes = [
|
|||||||
// eslint-disable-next-line rxjs-angular/prefer-takeuntil
|
// eslint-disable-next-line rxjs-angular/prefer-takeuntil
|
||||||
export class ClientsComponent implements OnInit {
|
export class ClientsComponent implements OnInit {
|
||||||
providerId: string;
|
providerId: string;
|
||||||
searchText: string;
|
|
||||||
addableOrganizations: Organization[];
|
addableOrganizations: Organization[];
|
||||||
loading = true;
|
loading = true;
|
||||||
manageOrganizations = false;
|
manageOrganizations = false;
|
||||||
@ -57,6 +56,17 @@ export class ClientsComponent implements OnInit {
|
|||||||
FeatureFlag.EnableConsolidatedBilling,
|
FeatureFlag.EnableConsolidatedBilling,
|
||||||
false,
|
false,
|
||||||
);
|
);
|
||||||
|
private destroy$ = new Subject<void>();
|
||||||
|
private _searchText$ = new BehaviorSubject<string>("");
|
||||||
|
private isSearching: boolean = false;
|
||||||
|
|
||||||
|
get searchText() {
|
||||||
|
return this._searchText$.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
set searchText(value: string) {
|
||||||
|
this._searchText$.next(value);
|
||||||
|
}
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private route: ActivatedRoute,
|
private route: ActivatedRoute,
|
||||||
@ -77,27 +87,41 @@ export class ClientsComponent implements OnInit {
|
|||||||
) {}
|
) {}
|
||||||
|
|
||||||
async ngOnInit() {
|
async ngOnInit() {
|
||||||
// eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe
|
|
||||||
|
|
||||||
const enableConsolidatedBilling = await firstValueFrom(this.enableConsolidatedBilling$);
|
const enableConsolidatedBilling = await firstValueFrom(this.enableConsolidatedBilling$);
|
||||||
|
|
||||||
if (enableConsolidatedBilling) {
|
if (enableConsolidatedBilling) {
|
||||||
await this.router.navigate(["../manage-client-organizations"], { relativeTo: this.route });
|
await this.router.navigate(["../manage-client-organizations"], { relativeTo: this.route });
|
||||||
} else {
|
} else {
|
||||||
// eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe
|
this.route.parent.params
|
||||||
this.route.parent.params.subscribe(async (params) => {
|
.pipe(
|
||||||
|
switchMap((params) => {
|
||||||
this.providerId = params.providerId;
|
this.providerId = params.providerId;
|
||||||
|
return from(this.load());
|
||||||
|
}),
|
||||||
|
takeUntil(this.destroy$),
|
||||||
|
)
|
||||||
|
.subscribe();
|
||||||
|
|
||||||
await this.load();
|
this.route.queryParams.pipe(first(), takeUntil(this.destroy$)).subscribe((qParams) => {
|
||||||
|
|
||||||
/* eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe, rxjs/no-nested-subscribe */
|
|
||||||
this.route.queryParams.pipe(first()).subscribe(async (qParams) => {
|
|
||||||
this.searchText = qParams.search;
|
this.searchText = qParams.search;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this._searchText$
|
||||||
|
.pipe(
|
||||||
|
switchMap((searchText) => from(this.searchService.isSearchable(searchText))),
|
||||||
|
takeUntil(this.destroy$),
|
||||||
|
)
|
||||||
|
.subscribe((isSearchable) => {
|
||||||
|
this.isSearching = isSearchable;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ngOnDestroy() {
|
||||||
|
this.destroy$.next();
|
||||||
|
this.destroy$.complete();
|
||||||
|
}
|
||||||
|
|
||||||
async load() {
|
async load() {
|
||||||
const response = await this.apiService.getProviderClients(this.providerId);
|
const response = await this.apiService.getProviderClients(this.providerId);
|
||||||
this.clients = response.data != null && response.data.length > 0 ? response.data : [];
|
this.clients = response.data != null && response.data.length > 0 ? response.data : [];
|
||||||
@ -118,20 +142,14 @@ export class ClientsComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
isPaging() {
|
isPaging() {
|
||||||
const searching = this.isSearching();
|
const searching = this.isSearching;
|
||||||
if (searching && this.didScroll) {
|
if (searching && this.didScroll) {
|
||||||
// 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.resetPaging();
|
this.resetPaging();
|
||||||
}
|
}
|
||||||
return !searching && this.clients && this.clients.length > this.pageSize;
|
return !searching && this.clients && this.clients.length > this.pageSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
isSearching() {
|
resetPaging() {
|
||||||
return this.searchService.isSearchable(this.searchText);
|
|
||||||
}
|
|
||||||
|
|
||||||
async resetPaging() {
|
|
||||||
this.pagedClients = [];
|
this.pagedClients = [];
|
||||||
this.loadMore();
|
this.loadMore();
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { Component, OnInit, ViewChild, ViewContainerRef } from "@angular/core";
|
import { Component, ViewChild, ViewContainerRef } from "@angular/core";
|
||||||
import { ActivatedRoute, Router } from "@angular/router";
|
import { ActivatedRoute, Router } from "@angular/router";
|
||||||
import { first } from "rxjs/operators";
|
import { first } from "rxjs/operators";
|
||||||
|
|
||||||
@ -34,10 +34,7 @@ import { UserAddEditComponent } from "./user-add-edit.component";
|
|||||||
templateUrl: "people.component.html",
|
templateUrl: "people.component.html",
|
||||||
})
|
})
|
||||||
// eslint-disable-next-line rxjs-angular/prefer-takeuntil
|
// eslint-disable-next-line rxjs-angular/prefer-takeuntil
|
||||||
export class PeopleComponent
|
export class PeopleComponent extends BasePeopleComponent<ProviderUserUserDetailsResponse> {
|
||||||
extends BasePeopleComponent<ProviderUserUserDetailsResponse>
|
|
||||||
implements OnInit
|
|
||||||
{
|
|
||||||
@ViewChild("addEdit", { read: ViewContainerRef, static: true }) addEditModalRef: ViewContainerRef;
|
@ViewChild("addEdit", { read: ViewContainerRef, static: true }) addEditModalRef: ViewContainerRef;
|
||||||
@ViewChild("groupsTemplate", { read: ViewContainerRef, static: true })
|
@ViewChild("groupsTemplate", { read: ViewContainerRef, static: true })
|
||||||
groupsModalRef: ViewContainerRef;
|
groupsModalRef: ViewContainerRef;
|
||||||
@ -119,6 +116,10 @@ export class PeopleComponent
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ngOnDestroy(): void {
|
||||||
|
super.ngOnDestroy();
|
||||||
|
}
|
||||||
|
|
||||||
getUsers(): Promise<ListResponse<ProviderUserUserDetailsResponse>> {
|
getUsers(): Promise<ListResponse<ProviderUserUserDetailsResponse>> {
|
||||||
return this.apiService.getProviderUsers(this.providerId);
|
return this.apiService.getProviderUsers(this.providerId);
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,7 @@ import { FormsModule } from "@angular/forms";
|
|||||||
|
|
||||||
import { JslibModule } from "@bitwarden/angular/jslib.module";
|
import { JslibModule } from "@bitwarden/angular/jslib.module";
|
||||||
import { SearchModule } from "@bitwarden/components";
|
import { SearchModule } from "@bitwarden/components";
|
||||||
import { OrganizationPlansComponent } from "@bitwarden/web-vault/app/billing";
|
import { OrganizationPlansComponent, TaxInfoComponent } from "@bitwarden/web-vault/app/billing";
|
||||||
import { PaymentMethodWarningsModule } from "@bitwarden/web-vault/app/billing/shared";
|
import { PaymentMethodWarningsModule } from "@bitwarden/web-vault/app/billing/shared";
|
||||||
import { OssModule } from "@bitwarden/web-vault/app/oss.module";
|
import { OssModule } from "@bitwarden/web-vault/app/oss.module";
|
||||||
|
|
||||||
@ -39,6 +39,7 @@ import { SetupComponent } from "./setup/setup.component";
|
|||||||
SearchModule,
|
SearchModule,
|
||||||
ProvidersLayoutComponent,
|
ProvidersLayoutComponent,
|
||||||
PaymentMethodWarningsModule,
|
PaymentMethodWarningsModule,
|
||||||
|
TaxInfoComponent,
|
||||||
],
|
],
|
||||||
declarations: [
|
declarations: [
|
||||||
AcceptProviderComponent,
|
AcceptProviderComponent,
|
||||||
|
@ -25,6 +25,9 @@
|
|||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<div *ngIf="enableConsolidatedBilling$ | async" class="form-group col-12">
|
||||||
|
<app-tax-info />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mt-4">
|
<div class="mt-4">
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
import { Component, OnInit } from "@angular/core";
|
import { Component, OnInit, ViewChild } from "@angular/core";
|
||||||
import { ActivatedRoute, Router } from "@angular/router";
|
import { ActivatedRoute, Router } from "@angular/router";
|
||||||
|
import { firstValueFrom } from "rxjs";
|
||||||
import { first } from "rxjs/operators";
|
import { first } from "rxjs/operators";
|
||||||
|
|
||||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||||
import { ProviderSetupRequest } from "@bitwarden/common/admin-console/models/request/provider/provider-setup.request";
|
import { ProviderSetupRequest } from "@bitwarden/common/admin-console/models/request/provider/provider-setup.request";
|
||||||
|
import { ExpandedTaxInfoUpdateRequest } from "@bitwarden/common/billing/models/request/expanded-tax-info-update.request";
|
||||||
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
|
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
|
||||||
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
||||||
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
|
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
|
||||||
@ -12,6 +14,7 @@ import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/pl
|
|||||||
import { ValidationService } from "@bitwarden/common/platform/abstractions/validation.service";
|
import { ValidationService } from "@bitwarden/common/platform/abstractions/validation.service";
|
||||||
import { ProviderKey } from "@bitwarden/common/types/key";
|
import { ProviderKey } from "@bitwarden/common/types/key";
|
||||||
import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction";
|
import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction";
|
||||||
|
import { TaxInfoComponent } from "@bitwarden/web-vault/app/billing";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: "provider-setup",
|
selector: "provider-setup",
|
||||||
@ -19,6 +22,8 @@ import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.serv
|
|||||||
})
|
})
|
||||||
// eslint-disable-next-line rxjs-angular/prefer-takeuntil
|
// eslint-disable-next-line rxjs-angular/prefer-takeuntil
|
||||||
export class SetupComponent implements OnInit {
|
export class SetupComponent implements OnInit {
|
||||||
|
@ViewChild(TaxInfoComponent) taxInfoComponent: TaxInfoComponent;
|
||||||
|
|
||||||
loading = true;
|
loading = true;
|
||||||
authed = false;
|
authed = false;
|
||||||
email: string;
|
email: string;
|
||||||
@ -34,6 +39,11 @@ export class SetupComponent implements OnInit {
|
|||||||
false,
|
false,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
protected enableConsolidatedBilling$ = this.configService.getFeatureFlag$(
|
||||||
|
FeatureFlag.EnableConsolidatedBilling,
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private router: Router,
|
private router: Router,
|
||||||
private platformUtilsService: PlatformUtilsService,
|
private platformUtilsService: PlatformUtilsService,
|
||||||
@ -102,6 +112,22 @@ export class SetupComponent implements OnInit {
|
|||||||
request.token = this.token;
|
request.token = this.token;
|
||||||
request.key = key;
|
request.key = key;
|
||||||
|
|
||||||
|
const enableConsolidatedBilling = await firstValueFrom(this.enableConsolidatedBilling$);
|
||||||
|
|
||||||
|
if (enableConsolidatedBilling) {
|
||||||
|
request.taxInfo = new ExpandedTaxInfoUpdateRequest();
|
||||||
|
const taxInfoView = this.taxInfoComponent.taxInfo;
|
||||||
|
request.taxInfo.country = taxInfoView.country;
|
||||||
|
request.taxInfo.postalCode = taxInfoView.postalCode;
|
||||||
|
if (taxInfoView.includeTaxId) {
|
||||||
|
request.taxInfo.taxId = taxInfoView.taxId;
|
||||||
|
request.taxInfo.line1 = taxInfoView.line1;
|
||||||
|
request.taxInfo.line2 = taxInfoView.line2;
|
||||||
|
request.taxInfo.city = taxInfoView.city;
|
||||||
|
request.taxInfo.state = taxInfoView.state;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const provider = await this.apiService.postProviderSetup(this.providerId, request);
|
const provider = await this.apiService.postProviderSetup(this.providerId, request);
|
||||||
this.platformUtilsService.showToast("success", null, this.i18nService.t("providerSetup"));
|
this.platformUtilsService.showToast("success", null, this.i18nService.t("providerSetup"));
|
||||||
await this.syncService.fullSync(true);
|
await this.syncService.fullSync(true);
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import { SelectionModel } from "@angular/cdk/collections";
|
import { SelectionModel } from "@angular/cdk/collections";
|
||||||
import { Component, OnInit } from "@angular/core";
|
import { Component, OnDestroy, OnInit } from "@angular/core";
|
||||||
import { ActivatedRoute } from "@angular/router";
|
import { ActivatedRoute } from "@angular/router";
|
||||||
import { firstValueFrom } from "rxjs";
|
import { BehaviorSubject, Subject, firstValueFrom, from } from "rxjs";
|
||||||
import { first } from "rxjs/operators";
|
import { first, switchMap, takeUntil } from "rxjs/operators";
|
||||||
|
|
||||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||||
import { SearchService } from "@bitwarden/common/abstractions/search.service";
|
import { SearchService } from "@bitwarden/common/abstractions/search.service";
|
||||||
@ -23,12 +23,22 @@ import { ManageClientOrganizationSubscriptionComponent } from "./manage-client-o
|
|||||||
})
|
})
|
||||||
|
|
||||||
// eslint-disable-next-line rxjs-angular/prefer-takeuntil
|
// eslint-disable-next-line rxjs-angular/prefer-takeuntil
|
||||||
export class ManageClientOrganizationsComponent implements OnInit {
|
export class ManageClientOrganizationsComponent implements OnInit, OnDestroy {
|
||||||
providerId: string;
|
providerId: string;
|
||||||
loading = true;
|
loading = true;
|
||||||
manageOrganizations = false;
|
manageOrganizations = false;
|
||||||
|
|
||||||
|
private destroy$ = new Subject<void>();
|
||||||
|
private _searchText$ = new BehaviorSubject<string>("");
|
||||||
|
private isSearching: boolean = false;
|
||||||
|
|
||||||
|
get searchText() {
|
||||||
|
return this._searchText$.value;
|
||||||
|
}
|
||||||
|
|
||||||
set searchText(search: string) {
|
set searchText(search: string) {
|
||||||
|
this._searchText$.value;
|
||||||
|
|
||||||
this.selection.clear();
|
this.selection.clear();
|
||||||
this.dataSource.filter = search;
|
this.dataSource.filter = search;
|
||||||
}
|
}
|
||||||
@ -67,6 +77,20 @@ export class ManageClientOrganizationsComponent implements OnInit {
|
|||||||
this.searchText = qParams.search;
|
this.searchText = qParams.search;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this._searchText$
|
||||||
|
.pipe(
|
||||||
|
switchMap((searchText) => from(this.searchService.isSearchable(searchText))),
|
||||||
|
takeUntil(this.destroy$),
|
||||||
|
)
|
||||||
|
.subscribe((isSearchable) => {
|
||||||
|
this.isSearching = isSearchable;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy(): void {
|
||||||
|
this.destroy$.next();
|
||||||
|
this.destroy$.complete();
|
||||||
}
|
}
|
||||||
|
|
||||||
async load() {
|
async load() {
|
||||||
@ -80,7 +104,7 @@ export class ManageClientOrganizationsComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
isPaging() {
|
isPaging() {
|
||||||
const searching = this.isSearching();
|
const searching = this.isSearching;
|
||||||
if (searching && this.didScroll) {
|
if (searching && this.didScroll) {
|
||||||
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
|
// 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
|
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||||
@ -89,10 +113,6 @@ export class ManageClientOrganizationsComponent implements OnInit {
|
|||||||
return !searching && this.clients && this.clients.length > this.pageSize;
|
return !searching && this.clients && this.clients.length > this.pageSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
isSearching() {
|
|
||||||
return this.searchService.isSearchable(this.searchText);
|
|
||||||
}
|
|
||||||
|
|
||||||
async resetPaging() {
|
async resetPaging() {
|
||||||
this.pagedClients = [];
|
this.pagedClients = [];
|
||||||
this.loadMore();
|
this.loadMore();
|
||||||
|
@ -3,16 +3,21 @@ import { Observable, map } from "rxjs";
|
|||||||
|
|
||||||
import {
|
import {
|
||||||
ActiveUserState,
|
ActiveUserState,
|
||||||
KeyDefinition,
|
|
||||||
SM_ONBOARDING_DISK,
|
SM_ONBOARDING_DISK,
|
||||||
StateProvider,
|
StateProvider,
|
||||||
|
UserKeyDefinition,
|
||||||
} from "@bitwarden/common/platform/state";
|
} from "@bitwarden/common/platform/state";
|
||||||
|
|
||||||
export type SMOnboardingTasks = Record<string, Record<string, boolean>>;
|
export type SMOnboardingTasks = Record<string, Record<string, boolean>>;
|
||||||
|
|
||||||
const SM_ONBOARDING_TASKS_KEY = new KeyDefinition<SMOnboardingTasks>(SM_ONBOARDING_DISK, "tasks", {
|
const SM_ONBOARDING_TASKS_KEY = new UserKeyDefinition<SMOnboardingTasks>(
|
||||||
|
SM_ONBOARDING_DISK,
|
||||||
|
"tasks",
|
||||||
|
{
|
||||||
deserializer: (b) => b,
|
deserializer: (b) => b,
|
||||||
});
|
clearOn: [], // Used to track tasks completed by a user, we don't want to reshow if they've locked or logged out and came back to the app
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: "root",
|
providedIn: "root",
|
||||||
|
@ -10,7 +10,11 @@ module.exports = {
|
|||||||
displayName: "libs/angular tests",
|
displayName: "libs/angular tests",
|
||||||
preset: "jest-preset-angular",
|
preset: "jest-preset-angular",
|
||||||
setupFilesAfterEnv: ["<rootDir>/test.setup.ts"],
|
setupFilesAfterEnv: ["<rootDir>/test.setup.ts"],
|
||||||
moduleNameMapper: pathsToModuleNameMapper(compilerOptions?.paths || {}, {
|
moduleNameMapper: pathsToModuleNameMapper(
|
||||||
|
// lets us use @bitwarden/common/spec in tests
|
||||||
|
{ "@bitwarden/common/spec": ["../common/spec"], ...(compilerOptions?.paths ?? {}) },
|
||||||
|
{
|
||||||
prefix: "<rootDir>/",
|
prefix: "<rootDir>/",
|
||||||
}),
|
},
|
||||||
|
),
|
||||||
};
|
};
|
||||||
|
@ -12,6 +12,7 @@ import { InternalPolicyService } from "@bitwarden/common/admin-console/abstracti
|
|||||||
import { MasterPasswordPolicyOptions } from "@bitwarden/common/admin-console/models/domain/master-password-policy-options";
|
import { MasterPasswordPolicyOptions } from "@bitwarden/common/admin-console/models/domain/master-password-policy-options";
|
||||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||||
import { DeviceTrustCryptoServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust-crypto.service.abstraction";
|
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 { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction";
|
||||||
import { ForceSetPasswordReason } from "@bitwarden/common/auth/models/domain/force-set-password-reason";
|
import { ForceSetPasswordReason } from "@bitwarden/common/auth/models/domain/force-set-password-reason";
|
||||||
import { SecretVerificationRequest } from "@bitwarden/common/auth/models/request/secret-verification.request";
|
import { SecretVerificationRequest } from "@bitwarden/common/auth/models/request/secret-verification.request";
|
||||||
@ -56,6 +57,7 @@ export class LockComponent implements OnInit, OnDestroy {
|
|||||||
private destroy$ = new Subject<void>();
|
private destroy$ = new Subject<void>();
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
|
protected masterPasswordService: InternalMasterPasswordServiceAbstraction,
|
||||||
protected router: Router,
|
protected router: Router,
|
||||||
protected i18nService: I18nService,
|
protected i18nService: I18nService,
|
||||||
protected platformUtilsService: PlatformUtilsService,
|
protected platformUtilsService: PlatformUtilsService,
|
||||||
@ -206,6 +208,7 @@ export class LockComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async doUnlockWithMasterPassword() {
|
private async doUnlockWithMasterPassword() {
|
||||||
|
const userId = (await firstValueFrom(this.accountService.activeAccount$))?.id;
|
||||||
const kdf = await this.stateService.getKdfType();
|
const kdf = await this.stateService.getKdfType();
|
||||||
const kdfConfig = await this.stateService.getKdfConfig();
|
const kdfConfig = await this.stateService.getKdfConfig();
|
||||||
|
|
||||||
@ -215,11 +218,13 @@ export class LockComponent implements OnInit, OnDestroy {
|
|||||||
kdf,
|
kdf,
|
||||||
kdfConfig,
|
kdfConfig,
|
||||||
);
|
);
|
||||||
const storedPasswordHash = await this.cryptoService.getMasterKeyHash();
|
const storedMasterKeyHash = await firstValueFrom(
|
||||||
|
this.masterPasswordService.masterKeyHash$(userId),
|
||||||
|
);
|
||||||
|
|
||||||
let passwordValid = false;
|
let passwordValid = false;
|
||||||
|
|
||||||
if (storedPasswordHash != null) {
|
if (storedMasterKeyHash != null) {
|
||||||
// Offline unlock possible
|
// Offline unlock possible
|
||||||
passwordValid = await this.cryptoService.compareAndUpdateKeyHash(
|
passwordValid = await this.cryptoService.compareAndUpdateKeyHash(
|
||||||
this.masterPassword,
|
this.masterPassword,
|
||||||
@ -244,7 +249,7 @@ export class LockComponent implements OnInit, OnDestroy {
|
|||||||
masterKey,
|
masterKey,
|
||||||
HashPurpose.LocalAuthorization,
|
HashPurpose.LocalAuthorization,
|
||||||
);
|
);
|
||||||
await this.cryptoService.setMasterKeyHash(localKeyHash);
|
await this.masterPasswordService.setMasterKeyHash(localKeyHash, userId);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.logService.error(e);
|
this.logService.error(e);
|
||||||
} finally {
|
} finally {
|
||||||
@ -262,7 +267,7 @@ export class LockComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const userKey = await this.cryptoService.decryptUserKeyWithMasterKey(masterKey);
|
const userKey = await this.cryptoService.decryptUserKeyWithMasterKey(masterKey);
|
||||||
await this.cryptoService.setMasterKey(masterKey);
|
await this.masterPasswordService.setMasterKey(masterKey, userId);
|
||||||
await this.setUserKeyAndContinue(userKey, true);
|
await this.setUserKeyAndContinue(userKey, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -292,8 +297,10 @@ export class LockComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (this.requirePasswordChange()) {
|
if (this.requirePasswordChange()) {
|
||||||
await this.stateService.setForceSetPasswordReason(
|
const userId = (await firstValueFrom(this.accountService.activeAccount$))?.id;
|
||||||
|
await this.masterPasswordService.setForceSetPasswordReason(
|
||||||
ForceSetPasswordReason.WeakMasterPassword,
|
ForceSetPasswordReason.WeakMasterPassword,
|
||||||
|
userId,
|
||||||
);
|
);
|
||||||
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
|
// 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
|
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||||
|
@ -12,6 +12,8 @@ import { PolicyApiServiceAbstraction } from "@bitwarden/common/admin-console/abs
|
|||||||
import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
|
import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
|
||||||
import { MasterPasswordPolicyOptions } from "@bitwarden/common/admin-console/models/domain/master-password-policy-options";
|
import { MasterPasswordPolicyOptions } from "@bitwarden/common/admin-console/models/domain/master-password-policy-options";
|
||||||
import { OrganizationAutoEnrollStatusResponse } from "@bitwarden/common/admin-console/models/response/organization-auto-enroll-status.response";
|
import { OrganizationAutoEnrollStatusResponse } from "@bitwarden/common/admin-console/models/response/organization-auto-enroll-status.response";
|
||||||
|
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 { SsoLoginServiceAbstraction } from "@bitwarden/common/auth/abstractions/sso-login.service.abstraction";
|
||||||
import { ForceSetPasswordReason } from "@bitwarden/common/auth/models/domain/force-set-password-reason";
|
import { ForceSetPasswordReason } from "@bitwarden/common/auth/models/domain/force-set-password-reason";
|
||||||
import { SetPasswordRequest } from "@bitwarden/common/auth/models/request/set-password.request";
|
import { SetPasswordRequest } from "@bitwarden/common/auth/models/request/set-password.request";
|
||||||
@ -29,6 +31,7 @@ import {
|
|||||||
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
||||||
import { EncString } from "@bitwarden/common/platform/models/domain/enc-string";
|
import { EncString } from "@bitwarden/common/platform/models/domain/enc-string";
|
||||||
import { PasswordGenerationServiceAbstraction } from "@bitwarden/common/tools/generator/password";
|
import { PasswordGenerationServiceAbstraction } from "@bitwarden/common/tools/generator/password";
|
||||||
|
import { UserId } from "@bitwarden/common/types/guid";
|
||||||
import { MasterKey, UserKey } from "@bitwarden/common/types/key";
|
import { MasterKey, UserKey } from "@bitwarden/common/types/key";
|
||||||
import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction";
|
import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction";
|
||||||
import { DialogService } from "@bitwarden/components";
|
import { DialogService } from "@bitwarden/components";
|
||||||
@ -45,11 +48,14 @@ export class SetPasswordComponent extends BaseChangePasswordComponent {
|
|||||||
resetPasswordAutoEnroll = false;
|
resetPasswordAutoEnroll = false;
|
||||||
onSuccessfulChangePassword: () => Promise<void>;
|
onSuccessfulChangePassword: () => Promise<void>;
|
||||||
successRoute = "vault";
|
successRoute = "vault";
|
||||||
|
userId: UserId;
|
||||||
|
|
||||||
forceSetPasswordReason: ForceSetPasswordReason = ForceSetPasswordReason.None;
|
forceSetPasswordReason: ForceSetPasswordReason = ForceSetPasswordReason.None;
|
||||||
ForceSetPasswordReason = ForceSetPasswordReason;
|
ForceSetPasswordReason = ForceSetPasswordReason;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
|
private accountService: AccountService,
|
||||||
|
private masterPasswordService: InternalMasterPasswordServiceAbstraction,
|
||||||
i18nService: I18nService,
|
i18nService: I18nService,
|
||||||
cryptoService: CryptoService,
|
cryptoService: CryptoService,
|
||||||
messagingService: MessagingService,
|
messagingService: MessagingService,
|
||||||
@ -88,7 +94,11 @@ export class SetPasswordComponent extends BaseChangePasswordComponent {
|
|||||||
await this.syncService.fullSync(true);
|
await this.syncService.fullSync(true);
|
||||||
this.syncLoading = false;
|
this.syncLoading = false;
|
||||||
|
|
||||||
this.forceSetPasswordReason = await this.stateService.getForceSetPasswordReason();
|
this.userId = (await firstValueFrom(this.accountService.activeAccount$))?.id;
|
||||||
|
|
||||||
|
this.forceSetPasswordReason = await firstValueFrom(
|
||||||
|
this.masterPasswordService.forceSetPasswordReason$(this.userId),
|
||||||
|
);
|
||||||
|
|
||||||
this.route.queryParams
|
this.route.queryParams
|
||||||
.pipe(
|
.pipe(
|
||||||
@ -176,7 +186,6 @@ export class SetPasswordComponent extends BaseChangePasswordComponent {
|
|||||||
if (response == null) {
|
if (response == null) {
|
||||||
throw new Error(this.i18nService.t("resetPasswordOrgKeysError"));
|
throw new Error(this.i18nService.t("resetPasswordOrgKeysError"));
|
||||||
}
|
}
|
||||||
const userId = await this.stateService.getUserId();
|
|
||||||
const publicKey = Utils.fromB64ToArray(response.publicKey);
|
const publicKey = Utils.fromB64ToArray(response.publicKey);
|
||||||
|
|
||||||
// RSA Encrypt user key with organization public key
|
// RSA Encrypt user key with organization public key
|
||||||
@ -189,7 +198,7 @@ export class SetPasswordComponent extends BaseChangePasswordComponent {
|
|||||||
|
|
||||||
return this.organizationUserService.putOrganizationUserResetPasswordEnrollment(
|
return this.organizationUserService.putOrganizationUserResetPasswordEnrollment(
|
||||||
this.orgId,
|
this.orgId,
|
||||||
userId,
|
this.userId,
|
||||||
resetRequest,
|
resetRequest,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@ -226,7 +235,10 @@ export class SetPasswordComponent extends BaseChangePasswordComponent {
|
|||||||
keyPair: [string, EncString] | null,
|
keyPair: [string, EncString] | null,
|
||||||
) {
|
) {
|
||||||
// Clear force set password reason to allow navigation back to vault.
|
// Clear force set password reason to allow navigation back to vault.
|
||||||
await this.stateService.setForceSetPasswordReason(ForceSetPasswordReason.None);
|
await this.masterPasswordService.setForceSetPasswordReason(
|
||||||
|
ForceSetPasswordReason.None,
|
||||||
|
this.userId,
|
||||||
|
);
|
||||||
|
|
||||||
// User now has a password so update account decryption options in state
|
// User now has a password so update account decryption options in state
|
||||||
const userDecryptionOpts = await firstValueFrom(
|
const userDecryptionOpts = await firstValueFrom(
|
||||||
@ -237,7 +249,7 @@ export class SetPasswordComponent extends BaseChangePasswordComponent {
|
|||||||
|
|
||||||
await this.stateService.setKdfType(this.kdf);
|
await this.stateService.setKdfType(this.kdf);
|
||||||
await this.stateService.setKdfConfig(this.kdfConfig);
|
await this.stateService.setKdfConfig(this.kdfConfig);
|
||||||
await this.cryptoService.setMasterKey(masterKey);
|
await this.masterPasswordService.setMasterKey(masterKey, this.userId);
|
||||||
await this.cryptoService.setUserKey(userKey[0]);
|
await this.cryptoService.setUserKey(userKey[0]);
|
||||||
|
|
||||||
// Set private key only for new JIT provisioned users in MP encryption orgs
|
// Set private key only for new JIT provisioned users in MP encryption orgs
|
||||||
@ -255,6 +267,6 @@ export class SetPasswordComponent extends BaseChangePasswordComponent {
|
|||||||
masterKey,
|
masterKey,
|
||||||
HashPurpose.LocalAuthorization,
|
HashPurpose.LocalAuthorization,
|
||||||
);
|
);
|
||||||
await this.cryptoService.setMasterKeyHash(localMasterKeyHash);
|
await this.masterPasswordService.setMasterKeyHash(localMasterKeyHash, this.userId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,10 +12,13 @@ import {
|
|||||||
UserDecryptionOptionsServiceAbstraction,
|
UserDecryptionOptionsServiceAbstraction,
|
||||||
} from "@bitwarden/auth/common";
|
} from "@bitwarden/auth/common";
|
||||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
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 { SsoLoginServiceAbstraction } from "@bitwarden/common/auth/abstractions/sso-login.service.abstraction";
|
||||||
import { TwoFactorProviderType } from "@bitwarden/common/auth/enums/two-factor-provider-type";
|
import { TwoFactorProviderType } from "@bitwarden/common/auth/enums/two-factor-provider-type";
|
||||||
import { AuthResult } from "@bitwarden/common/auth/models/domain/auth-result";
|
import { AuthResult } from "@bitwarden/common/auth/models/domain/auth-result";
|
||||||
import { ForceSetPasswordReason } from "@bitwarden/common/auth/models/domain/force-set-password-reason";
|
import { ForceSetPasswordReason } from "@bitwarden/common/auth/models/domain/force-set-password-reason";
|
||||||
|
import { FakeMasterPasswordService } from "@bitwarden/common/auth/services/master-password/fake-master-password.service";
|
||||||
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
||||||
import { CryptoFunctionService } from "@bitwarden/common/platform/abstractions/crypto-function.service";
|
import { CryptoFunctionService } from "@bitwarden/common/platform/abstractions/crypto-function.service";
|
||||||
import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service";
|
import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service";
|
||||||
@ -23,7 +26,9 @@ import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.servic
|
|||||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||||
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
|
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
|
||||||
|
import { FakeAccountService, mockAccountServiceWith } from "@bitwarden/common/spec";
|
||||||
import { PasswordGenerationServiceAbstraction } from "@bitwarden/common/tools/generator/password";
|
import { PasswordGenerationServiceAbstraction } from "@bitwarden/common/tools/generator/password";
|
||||||
|
import { UserId } from "@bitwarden/common/types/guid";
|
||||||
|
|
||||||
import { SsoComponent } from "./sso.component";
|
import { SsoComponent } from "./sso.component";
|
||||||
// test component that extends the SsoComponent
|
// test component that extends the SsoComponent
|
||||||
@ -48,6 +53,7 @@ describe("SsoComponent", () => {
|
|||||||
let component: TestSsoComponent;
|
let component: TestSsoComponent;
|
||||||
let _component: SsoComponentProtected;
|
let _component: SsoComponentProtected;
|
||||||
let fixture: ComponentFixture<TestSsoComponent>;
|
let fixture: ComponentFixture<TestSsoComponent>;
|
||||||
|
const userId = "userId" as UserId;
|
||||||
|
|
||||||
// Mock Services
|
// Mock Services
|
||||||
let mockLoginStrategyService: MockProxy<LoginStrategyServiceAbstraction>;
|
let mockLoginStrategyService: MockProxy<LoginStrategyServiceAbstraction>;
|
||||||
@ -67,6 +73,8 @@ describe("SsoComponent", () => {
|
|||||||
let mockLogService: MockProxy<LogService>;
|
let mockLogService: MockProxy<LogService>;
|
||||||
let mockUserDecryptionOptionsService: MockProxy<UserDecryptionOptionsServiceAbstraction>;
|
let mockUserDecryptionOptionsService: MockProxy<UserDecryptionOptionsServiceAbstraction>;
|
||||||
let mockConfigService: MockProxy<ConfigService>;
|
let mockConfigService: MockProxy<ConfigService>;
|
||||||
|
let mockMasterPasswordService: FakeMasterPasswordService;
|
||||||
|
let mockAccountService: FakeAccountService;
|
||||||
|
|
||||||
// Mock authService.logIn params
|
// Mock authService.logIn params
|
||||||
let code: string;
|
let code: string;
|
||||||
@ -117,6 +125,8 @@ describe("SsoComponent", () => {
|
|||||||
mockLogService = mock();
|
mockLogService = mock();
|
||||||
mockUserDecryptionOptionsService = mock();
|
mockUserDecryptionOptionsService = mock();
|
||||||
mockConfigService = mock();
|
mockConfigService = mock();
|
||||||
|
mockAccountService = mockAccountServiceWith(userId);
|
||||||
|
mockMasterPasswordService = new FakeMasterPasswordService();
|
||||||
|
|
||||||
// Mock loginStrategyService.logIn params
|
// Mock loginStrategyService.logIn params
|
||||||
code = "code";
|
code = "code";
|
||||||
@ -199,6 +209,8 @@ describe("SsoComponent", () => {
|
|||||||
},
|
},
|
||||||
{ provide: LogService, useValue: mockLogService },
|
{ provide: LogService, useValue: mockLogService },
|
||||||
{ provide: ConfigService, useValue: mockConfigService },
|
{ provide: ConfigService, useValue: mockConfigService },
|
||||||
|
{ provide: InternalMasterPasswordServiceAbstraction, useValue: mockMasterPasswordService },
|
||||||
|
{ provide: AccountService, useValue: mockAccountService },
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -365,8 +377,9 @@ describe("SsoComponent", () => {
|
|||||||
await _component.logIn(code, codeVerifier, orgIdFromState);
|
await _component.logIn(code, codeVerifier, orgIdFromState);
|
||||||
expect(mockLoginStrategyService.logIn).toHaveBeenCalledTimes(1);
|
expect(mockLoginStrategyService.logIn).toHaveBeenCalledTimes(1);
|
||||||
|
|
||||||
expect(mockStateService.setForceSetPasswordReason).toHaveBeenCalledWith(
|
expect(mockMasterPasswordService.mock.setForceSetPasswordReason).toHaveBeenCalledWith(
|
||||||
ForceSetPasswordReason.TdeUserWithoutPasswordHasPasswordResetPermission,
|
ForceSetPasswordReason.TdeUserWithoutPasswordHasPasswordResetPermission,
|
||||||
|
userId,
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(mockOnSuccessfulLoginTdeNavigate).not.toHaveBeenCalled();
|
expect(mockOnSuccessfulLoginTdeNavigate).not.toHaveBeenCalled();
|
||||||
|
@ -11,6 +11,8 @@ import {
|
|||||||
UserDecryptionOptionsServiceAbstraction,
|
UserDecryptionOptionsServiceAbstraction,
|
||||||
} from "@bitwarden/auth/common";
|
} from "@bitwarden/auth/common";
|
||||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
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 { SsoLoginServiceAbstraction } from "@bitwarden/common/auth/abstractions/sso-login.service.abstraction";
|
||||||
import { AuthResult } from "@bitwarden/common/auth/models/domain/auth-result";
|
import { AuthResult } from "@bitwarden/common/auth/models/domain/auth-result";
|
||||||
import { ForceSetPasswordReason } from "@bitwarden/common/auth/models/domain/force-set-password-reason";
|
import { ForceSetPasswordReason } from "@bitwarden/common/auth/models/domain/force-set-password-reason";
|
||||||
@ -66,6 +68,8 @@ export class SsoComponent {
|
|||||||
protected logService: LogService,
|
protected logService: LogService,
|
||||||
protected userDecryptionOptionsService: UserDecryptionOptionsServiceAbstraction,
|
protected userDecryptionOptionsService: UserDecryptionOptionsServiceAbstraction,
|
||||||
protected configService: ConfigService,
|
protected configService: ConfigService,
|
||||||
|
protected masterPasswordService: InternalMasterPasswordServiceAbstraction,
|
||||||
|
protected accountService: AccountService,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async ngOnInit() {
|
async ngOnInit() {
|
||||||
@ -290,8 +294,10 @@ export class SsoComponent {
|
|||||||
// Set flag so that auth guard can redirect to set password screen after decryption (trusted or untrusted device)
|
// Set flag so that auth guard can redirect to set password screen after decryption (trusted or untrusted device)
|
||||||
// Note: we cannot directly navigate in this scenario as we are in a pre-decryption state, and
|
// Note: we cannot directly navigate in this scenario as we are in a pre-decryption state, and
|
||||||
// if you try to set a new MP before decrypting, you will invalidate the user's data by making a new user key.
|
// if you try to set a new MP before decrypting, you will invalidate the user's data by making a new user key.
|
||||||
await this.stateService.setForceSetPasswordReason(
|
const userId = (await firstValueFrom(this.accountService.activeAccount$))?.id;
|
||||||
|
await this.masterPasswordService.setForceSetPasswordReason(
|
||||||
ForceSetPasswordReason.TdeUserWithoutPasswordHasPasswordResetPermission,
|
ForceSetPasswordReason.TdeUserWithoutPasswordHasPasswordResetPermission,
|
||||||
|
userId,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,11 +15,14 @@ import {
|
|||||||
UserDecryptionOptionsServiceAbstraction,
|
UserDecryptionOptionsServiceAbstraction,
|
||||||
} from "@bitwarden/auth/common";
|
} from "@bitwarden/auth/common";
|
||||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
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 { SsoLoginServiceAbstraction } from "@bitwarden/common/auth/abstractions/sso-login.service.abstraction";
|
||||||
import { TwoFactorService } from "@bitwarden/common/auth/abstractions/two-factor.service";
|
import { TwoFactorService } from "@bitwarden/common/auth/abstractions/two-factor.service";
|
||||||
import { AuthResult } from "@bitwarden/common/auth/models/domain/auth-result";
|
import { AuthResult } from "@bitwarden/common/auth/models/domain/auth-result";
|
||||||
import { ForceSetPasswordReason } from "@bitwarden/common/auth/models/domain/force-set-password-reason";
|
import { ForceSetPasswordReason } from "@bitwarden/common/auth/models/domain/force-set-password-reason";
|
||||||
import { TokenTwoFactorRequest } from "@bitwarden/common/auth/models/request/identity-token/token-two-factor.request";
|
import { TokenTwoFactorRequest } from "@bitwarden/common/auth/models/request/identity-token/token-two-factor.request";
|
||||||
|
import { FakeMasterPasswordService } from "@bitwarden/common/auth/services/master-password/fake-master-password.service";
|
||||||
import { AppIdService } from "@bitwarden/common/platform/abstractions/app-id.service";
|
import { AppIdService } from "@bitwarden/common/platform/abstractions/app-id.service";
|
||||||
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
||||||
import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service";
|
import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service";
|
||||||
@ -27,6 +30,8 @@ import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.servic
|
|||||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||||
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
|
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
|
||||||
|
import { FakeAccountService, mockAccountServiceWith } from "@bitwarden/common/spec";
|
||||||
|
import { UserId } from "@bitwarden/common/types/guid";
|
||||||
|
|
||||||
import { TwoFactorComponent } from "./two-factor.component";
|
import { TwoFactorComponent } from "./two-factor.component";
|
||||||
|
|
||||||
@ -46,6 +51,7 @@ describe("TwoFactorComponent", () => {
|
|||||||
let _component: TwoFactorComponentProtected;
|
let _component: TwoFactorComponentProtected;
|
||||||
|
|
||||||
let fixture: ComponentFixture<TestTwoFactorComponent>;
|
let fixture: ComponentFixture<TestTwoFactorComponent>;
|
||||||
|
const userId = "userId" as UserId;
|
||||||
|
|
||||||
// Mock Services
|
// Mock Services
|
||||||
let mockLoginStrategyService: MockProxy<LoginStrategyServiceAbstraction>;
|
let mockLoginStrategyService: MockProxy<LoginStrategyServiceAbstraction>;
|
||||||
@ -63,6 +69,8 @@ describe("TwoFactorComponent", () => {
|
|||||||
let mockUserDecryptionOptionsService: MockProxy<UserDecryptionOptionsServiceAbstraction>;
|
let mockUserDecryptionOptionsService: MockProxy<UserDecryptionOptionsServiceAbstraction>;
|
||||||
let mockSsoLoginService: MockProxy<SsoLoginServiceAbstraction>;
|
let mockSsoLoginService: MockProxy<SsoLoginServiceAbstraction>;
|
||||||
let mockConfigService: MockProxy<ConfigService>;
|
let mockConfigService: MockProxy<ConfigService>;
|
||||||
|
let mockMasterPasswordService: FakeMasterPasswordService;
|
||||||
|
let mockAccountService: FakeAccountService;
|
||||||
|
|
||||||
let mockUserDecryptionOpts: {
|
let mockUserDecryptionOpts: {
|
||||||
noMasterPassword: UserDecryptionOptions;
|
noMasterPassword: UserDecryptionOptions;
|
||||||
@ -93,6 +101,8 @@ describe("TwoFactorComponent", () => {
|
|||||||
mockUserDecryptionOptionsService = mock<UserDecryptionOptionsServiceAbstraction>();
|
mockUserDecryptionOptionsService = mock<UserDecryptionOptionsServiceAbstraction>();
|
||||||
mockSsoLoginService = mock<SsoLoginServiceAbstraction>();
|
mockSsoLoginService = mock<SsoLoginServiceAbstraction>();
|
||||||
mockConfigService = mock<ConfigService>();
|
mockConfigService = mock<ConfigService>();
|
||||||
|
mockAccountService = mockAccountServiceWith(userId);
|
||||||
|
mockMasterPasswordService = new FakeMasterPasswordService();
|
||||||
|
|
||||||
mockUserDecryptionOpts = {
|
mockUserDecryptionOpts = {
|
||||||
noMasterPassword: new UserDecryptionOptions({
|
noMasterPassword: new UserDecryptionOptions({
|
||||||
@ -170,6 +180,8 @@ describe("TwoFactorComponent", () => {
|
|||||||
},
|
},
|
||||||
{ provide: SsoLoginServiceAbstraction, useValue: mockSsoLoginService },
|
{ provide: SsoLoginServiceAbstraction, useValue: mockSsoLoginService },
|
||||||
{ provide: ConfigService, useValue: mockConfigService },
|
{ provide: ConfigService, useValue: mockConfigService },
|
||||||
|
{ provide: InternalMasterPasswordServiceAbstraction, useValue: mockMasterPasswordService },
|
||||||
|
{ provide: AccountService, useValue: mockAccountService },
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -407,9 +419,9 @@ describe("TwoFactorComponent", () => {
|
|||||||
await component.doSubmit();
|
await component.doSubmit();
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
|
expect(mockMasterPasswordService.mock.setForceSetPasswordReason).toHaveBeenCalledWith(
|
||||||
expect(mockStateService.setForceSetPasswordReason).toHaveBeenCalledWith(
|
|
||||||
ForceSetPasswordReason.TdeUserWithoutPasswordHasPasswordResetPermission,
|
ForceSetPasswordReason.TdeUserWithoutPasswordHasPasswordResetPermission,
|
||||||
|
userId,
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(mockRouter.navigate).toHaveBeenCalledTimes(1);
|
expect(mockRouter.navigate).toHaveBeenCalledTimes(1);
|
||||||
|
@ -14,6 +14,8 @@ import {
|
|||||||
UserDecryptionOptionsServiceAbstraction,
|
UserDecryptionOptionsServiceAbstraction,
|
||||||
} from "@bitwarden/auth/common";
|
} from "@bitwarden/auth/common";
|
||||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
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 { SsoLoginServiceAbstraction } from "@bitwarden/common/auth/abstractions/sso-login.service.abstraction";
|
||||||
import { TwoFactorService } from "@bitwarden/common/auth/abstractions/two-factor.service";
|
import { TwoFactorService } from "@bitwarden/common/auth/abstractions/two-factor.service";
|
||||||
import { AuthenticationType } from "@bitwarden/common/auth/enums/authentication-type";
|
import { AuthenticationType } from "@bitwarden/common/auth/enums/authentication-type";
|
||||||
@ -92,6 +94,8 @@ export class TwoFactorComponent extends CaptchaProtectedComponent implements OnI
|
|||||||
protected userDecryptionOptionsService: UserDecryptionOptionsServiceAbstraction,
|
protected userDecryptionOptionsService: UserDecryptionOptionsServiceAbstraction,
|
||||||
protected ssoLoginService: SsoLoginServiceAbstraction,
|
protected ssoLoginService: SsoLoginServiceAbstraction,
|
||||||
protected configService: ConfigService,
|
protected configService: ConfigService,
|
||||||
|
protected masterPasswordService: InternalMasterPasswordServiceAbstraction,
|
||||||
|
protected accountService: AccountService,
|
||||||
) {
|
) {
|
||||||
super(environmentService, i18nService, platformUtilsService);
|
super(environmentService, i18nService, platformUtilsService);
|
||||||
this.webAuthnSupported = this.platformUtilsService.supportsWebAuthn(win);
|
this.webAuthnSupported = this.platformUtilsService.supportsWebAuthn(win);
|
||||||
@ -342,8 +346,10 @@ export class TwoFactorComponent extends CaptchaProtectedComponent implements OnI
|
|||||||
// Set flag so that auth guard can redirect to set password screen after decryption (trusted or untrusted device)
|
// Set flag so that auth guard can redirect to set password screen after decryption (trusted or untrusted device)
|
||||||
// Note: we cannot directly navigate to the set password screen in this scenario as we are in a pre-decryption state, and
|
// Note: we cannot directly navigate to the set password screen in this scenario as we are in a pre-decryption state, and
|
||||||
// if you try to set a new MP before decrypting, you will invalidate the user's data by making a new user key.
|
// if you try to set a new MP before decrypting, you will invalidate the user's data by making a new user key.
|
||||||
await this.stateService.setForceSetPasswordReason(
|
const userId = (await firstValueFrom(this.accountService.activeAccount$))?.id;
|
||||||
|
await this.masterPasswordService.setForceSetPasswordReason(
|
||||||
ForceSetPasswordReason.TdeUserWithoutPasswordHasPasswordResetPermission,
|
ForceSetPasswordReason.TdeUserWithoutPasswordHasPasswordResetPermission,
|
||||||
|
userId,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,9 +1,12 @@
|
|||||||
import { Directive } from "@angular/core";
|
import { Directive } from "@angular/core";
|
||||||
import { Router } from "@angular/router";
|
import { Router } from "@angular/router";
|
||||||
|
import { firstValueFrom } from "rxjs";
|
||||||
|
|
||||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||||
import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
|
import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
|
||||||
import { MasterPasswordPolicyOptions } from "@bitwarden/common/admin-console/models/domain/master-password-policy-options";
|
import { MasterPasswordPolicyOptions } from "@bitwarden/common/admin-console/models/domain/master-password-policy-options";
|
||||||
|
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||||
|
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 { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction";
|
||||||
import { VerificationType } from "@bitwarden/common/auth/enums/verification-type";
|
import { VerificationType } from "@bitwarden/common/auth/enums/verification-type";
|
||||||
import { ForceSetPasswordReason } from "@bitwarden/common/auth/models/domain/force-set-password-reason";
|
import { ForceSetPasswordReason } from "@bitwarden/common/auth/models/domain/force-set-password-reason";
|
||||||
@ -56,6 +59,8 @@ export class UpdateTempPasswordComponent extends BaseChangePasswordComponent {
|
|||||||
private userVerificationService: UserVerificationService,
|
private userVerificationService: UserVerificationService,
|
||||||
protected router: Router,
|
protected router: Router,
|
||||||
dialogService: DialogService,
|
dialogService: DialogService,
|
||||||
|
private accountService: AccountService,
|
||||||
|
private masterPasswordService: InternalMasterPasswordServiceAbstraction,
|
||||||
) {
|
) {
|
||||||
super(
|
super(
|
||||||
i18nService,
|
i18nService,
|
||||||
@ -72,7 +77,8 @@ export class UpdateTempPasswordComponent extends BaseChangePasswordComponent {
|
|||||||
async ngOnInit() {
|
async ngOnInit() {
|
||||||
await this.syncService.fullSync(true);
|
await this.syncService.fullSync(true);
|
||||||
|
|
||||||
this.reason = await this.stateService.getForceSetPasswordReason();
|
const userId = (await firstValueFrom(this.accountService.activeAccount$))?.id;
|
||||||
|
this.reason = await firstValueFrom(this.masterPasswordService.forceSetPasswordReason$(userId));
|
||||||
|
|
||||||
// If we somehow end up here without a reason, go back to the home page
|
// If we somehow end up here without a reason, go back to the home page
|
||||||
if (this.reason == ForceSetPasswordReason.None) {
|
if (this.reason == ForceSetPasswordReason.None) {
|
||||||
@ -163,7 +169,11 @@ export class UpdateTempPasswordComponent extends BaseChangePasswordComponent {
|
|||||||
this.i18nService.t("updatedMasterPassword"),
|
this.i18nService.t("updatedMasterPassword"),
|
||||||
);
|
);
|
||||||
|
|
||||||
await this.stateService.setForceSetPasswordReason(ForceSetPasswordReason.None);
|
const userId = (await firstValueFrom(this.accountService.activeAccount$))?.id;
|
||||||
|
await this.masterPasswordService.setForceSetPasswordReason(
|
||||||
|
ForceSetPasswordReason.None,
|
||||||
|
userId,
|
||||||
|
);
|
||||||
|
|
||||||
if (this.onSuccessfulChangePassword != null) {
|
if (this.onSuccessfulChangePassword != null) {
|
||||||
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
|
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
|
||||||
|
@ -1,12 +1,14 @@
|
|||||||
import { Injectable } from "@angular/core";
|
import { Injectable } from "@angular/core";
|
||||||
import { ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot } from "@angular/router";
|
import { ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot } from "@angular/router";
|
||||||
|
import { firstValueFrom } from "rxjs";
|
||||||
|
|
||||||
|
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||||
import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service";
|
import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service";
|
||||||
import { KeyConnectorService } from "@bitwarden/common/auth/abstractions/key-connector.service";
|
import { KeyConnectorService } from "@bitwarden/common/auth/abstractions/key-connector.service";
|
||||||
|
import { MasterPasswordServiceAbstraction } from "@bitwarden/common/auth/abstractions/master-password.service.abstraction";
|
||||||
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
|
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
|
||||||
import { ForceSetPasswordReason } from "@bitwarden/common/auth/models/domain/force-set-password-reason";
|
import { ForceSetPasswordReason } from "@bitwarden/common/auth/models/domain/force-set-password-reason";
|
||||||
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
|
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
|
||||||
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
|
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class AuthGuard implements CanActivate {
|
export class AuthGuard implements CanActivate {
|
||||||
@ -15,7 +17,8 @@ export class AuthGuard implements CanActivate {
|
|||||||
private router: Router,
|
private router: Router,
|
||||||
private messagingService: MessagingService,
|
private messagingService: MessagingService,
|
||||||
private keyConnectorService: KeyConnectorService,
|
private keyConnectorService: KeyConnectorService,
|
||||||
private stateService: StateService,
|
private accountService: AccountService,
|
||||||
|
private masterPasswordService: MasterPasswordServiceAbstraction,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async canActivate(route: ActivatedRouteSnapshot, routerState: RouterStateSnapshot) {
|
async canActivate(route: ActivatedRouteSnapshot, routerState: RouterStateSnapshot) {
|
||||||
@ -40,7 +43,10 @@ export class AuthGuard implements CanActivate {
|
|||||||
return this.router.createUrlTree(["/remove-password"]);
|
return this.router.createUrlTree(["/remove-password"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
const forceSetPasswordReason = await this.stateService.getForceSetPasswordReason();
|
const userId = (await firstValueFrom(this.accountService.activeAccount$)).id;
|
||||||
|
const forceSetPasswordReason = await firstValueFrom(
|
||||||
|
this.masterPasswordService.forceSetPasswordReason$(userId),
|
||||||
|
);
|
||||||
|
|
||||||
if (
|
if (
|
||||||
forceSetPasswordReason ===
|
forceSetPasswordReason ===
|
||||||
|
@ -60,6 +60,10 @@ import { DeviceTrustCryptoServiceAbstraction } from "@bitwarden/common/auth/abst
|
|||||||
import { DevicesServiceAbstraction } from "@bitwarden/common/auth/abstractions/devices/devices.service.abstraction";
|
import { DevicesServiceAbstraction } from "@bitwarden/common/auth/abstractions/devices/devices.service.abstraction";
|
||||||
import { DevicesApiServiceAbstraction } from "@bitwarden/common/auth/abstractions/devices-api.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 { KeyConnectorService as KeyConnectorServiceAbstraction } from "@bitwarden/common/auth/abstractions/key-connector.service";
|
||||||
|
import {
|
||||||
|
InternalMasterPasswordServiceAbstraction,
|
||||||
|
MasterPasswordServiceAbstraction,
|
||||||
|
} from "@bitwarden/common/auth/abstractions/master-password.service.abstraction";
|
||||||
import { PasswordResetEnrollmentServiceAbstraction } from "@bitwarden/common/auth/abstractions/password-reset-enrollment.service.abstraction";
|
import { PasswordResetEnrollmentServiceAbstraction } from "@bitwarden/common/auth/abstractions/password-reset-enrollment.service.abstraction";
|
||||||
import { SsoLoginServiceAbstraction } from "@bitwarden/common/auth/abstractions/sso-login.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 { TokenService as TokenServiceAbstraction } from "@bitwarden/common/auth/abstractions/token.service";
|
||||||
@ -78,6 +82,7 @@ import { DeviceTrustCryptoService } from "@bitwarden/common/auth/services/device
|
|||||||
import { DevicesServiceImplementation } from "@bitwarden/common/auth/services/devices/devices.service.implementation";
|
import { DevicesServiceImplementation } from "@bitwarden/common/auth/services/devices/devices.service.implementation";
|
||||||
import { DevicesApiServiceImplementation } from "@bitwarden/common/auth/services/devices-api.service.implementation";
|
import { DevicesApiServiceImplementation } from "@bitwarden/common/auth/services/devices-api.service.implementation";
|
||||||
import { KeyConnectorService } from "@bitwarden/common/auth/services/key-connector.service";
|
import { KeyConnectorService } from "@bitwarden/common/auth/services/key-connector.service";
|
||||||
|
import { MasterPasswordService } from "@bitwarden/common/auth/services/master-password/master-password.service";
|
||||||
import { PasswordResetEnrollmentServiceImplementation } from "@bitwarden/common/auth/services/password-reset-enrollment.service.implementation";
|
import { PasswordResetEnrollmentServiceImplementation } from "@bitwarden/common/auth/services/password-reset-enrollment.service.implementation";
|
||||||
import { SsoLoginService } from "@bitwarden/common/auth/services/sso-login.service";
|
import { SsoLoginService } from "@bitwarden/common/auth/services/sso-login.service";
|
||||||
import { TokenService } from "@bitwarden/common/auth/services/token.service";
|
import { TokenService } from "@bitwarden/common/auth/services/token.service";
|
||||||
@ -359,6 +364,8 @@ const safeProviders: SafeProvider[] = [
|
|||||||
provide: LoginStrategyServiceAbstraction,
|
provide: LoginStrategyServiceAbstraction,
|
||||||
useClass: LoginStrategyService,
|
useClass: LoginStrategyService,
|
||||||
deps: [
|
deps: [
|
||||||
|
AccountServiceAbstraction,
|
||||||
|
InternalMasterPasswordServiceAbstraction,
|
||||||
CryptoServiceAbstraction,
|
CryptoServiceAbstraction,
|
||||||
ApiServiceAbstraction,
|
ApiServiceAbstraction,
|
||||||
TokenServiceAbstraction,
|
TokenServiceAbstraction,
|
||||||
@ -521,6 +528,7 @@ const safeProviders: SafeProvider[] = [
|
|||||||
provide: CryptoServiceAbstraction,
|
provide: CryptoServiceAbstraction,
|
||||||
useClass: CryptoService,
|
useClass: CryptoService,
|
||||||
deps: [
|
deps: [
|
||||||
|
InternalMasterPasswordServiceAbstraction,
|
||||||
KeyGenerationServiceAbstraction,
|
KeyGenerationServiceAbstraction,
|
||||||
CryptoFunctionServiceAbstraction,
|
CryptoFunctionServiceAbstraction,
|
||||||
EncryptService,
|
EncryptService,
|
||||||
@ -587,6 +595,8 @@ const safeProviders: SafeProvider[] = [
|
|||||||
provide: SyncServiceAbstraction,
|
provide: SyncServiceAbstraction,
|
||||||
useClass: SyncService,
|
useClass: SyncService,
|
||||||
deps: [
|
deps: [
|
||||||
|
InternalMasterPasswordServiceAbstraction,
|
||||||
|
AccountServiceAbstraction,
|
||||||
ApiServiceAbstraction,
|
ApiServiceAbstraction,
|
||||||
DomainSettingsService,
|
DomainSettingsService,
|
||||||
InternalFolderService,
|
InternalFolderService,
|
||||||
@ -626,6 +636,8 @@ const safeProviders: SafeProvider[] = [
|
|||||||
provide: VaultTimeoutService,
|
provide: VaultTimeoutService,
|
||||||
useClass: VaultTimeoutService,
|
useClass: VaultTimeoutService,
|
||||||
deps: [
|
deps: [
|
||||||
|
AccountServiceAbstraction,
|
||||||
|
InternalMasterPasswordServiceAbstraction,
|
||||||
CipherServiceAbstraction,
|
CipherServiceAbstraction,
|
||||||
FolderServiceAbstraction,
|
FolderServiceAbstraction,
|
||||||
CollectionServiceAbstraction,
|
CollectionServiceAbstraction,
|
||||||
@ -714,7 +726,7 @@ const safeProviders: SafeProvider[] = [
|
|||||||
safeProvider({
|
safeProvider({
|
||||||
provide: SearchServiceAbstraction,
|
provide: SearchServiceAbstraction,
|
||||||
useClass: SearchService,
|
useClass: SearchService,
|
||||||
deps: [LogService, I18nServiceAbstraction],
|
deps: [LogService, I18nServiceAbstraction, StateProvider],
|
||||||
}),
|
}),
|
||||||
safeProvider({
|
safeProvider({
|
||||||
provide: NotificationsServiceAbstraction,
|
provide: NotificationsServiceAbstraction,
|
||||||
@ -771,10 +783,21 @@ const safeProviders: SafeProvider[] = [
|
|||||||
useClass: PolicyApiService,
|
useClass: PolicyApiService,
|
||||||
deps: [InternalPolicyService, ApiServiceAbstraction],
|
deps: [InternalPolicyService, ApiServiceAbstraction],
|
||||||
}),
|
}),
|
||||||
|
safeProvider({
|
||||||
|
provide: InternalMasterPasswordServiceAbstraction,
|
||||||
|
useClass: MasterPasswordService,
|
||||||
|
deps: [StateProvider],
|
||||||
|
}),
|
||||||
|
safeProvider({
|
||||||
|
provide: MasterPasswordServiceAbstraction,
|
||||||
|
useExisting: InternalMasterPasswordServiceAbstraction,
|
||||||
|
}),
|
||||||
safeProvider({
|
safeProvider({
|
||||||
provide: KeyConnectorServiceAbstraction,
|
provide: KeyConnectorServiceAbstraction,
|
||||||
useClass: KeyConnectorService,
|
useClass: KeyConnectorService,
|
||||||
deps: [
|
deps: [
|
||||||
|
AccountServiceAbstraction,
|
||||||
|
InternalMasterPasswordServiceAbstraction,
|
||||||
CryptoServiceAbstraction,
|
CryptoServiceAbstraction,
|
||||||
ApiServiceAbstraction,
|
ApiServiceAbstraction,
|
||||||
TokenServiceAbstraction,
|
TokenServiceAbstraction,
|
||||||
@ -791,6 +814,8 @@ const safeProviders: SafeProvider[] = [
|
|||||||
deps: [
|
deps: [
|
||||||
StateServiceAbstraction,
|
StateServiceAbstraction,
|
||||||
CryptoServiceAbstraction,
|
CryptoServiceAbstraction,
|
||||||
|
AccountServiceAbstraction,
|
||||||
|
InternalMasterPasswordServiceAbstraction,
|
||||||
I18nServiceAbstraction,
|
I18nServiceAbstraction,
|
||||||
UserVerificationApiServiceAbstraction,
|
UserVerificationApiServiceAbstraction,
|
||||||
UserDecryptionOptionsServiceAbstraction,
|
UserDecryptionOptionsServiceAbstraction,
|
||||||
@ -934,9 +959,10 @@ const safeProviders: SafeProvider[] = [
|
|||||||
useClass: AuthRequestService,
|
useClass: AuthRequestService,
|
||||||
deps: [
|
deps: [
|
||||||
AppIdServiceAbstraction,
|
AppIdServiceAbstraction,
|
||||||
|
AccountServiceAbstraction,
|
||||||
|
InternalMasterPasswordServiceAbstraction,
|
||||||
CryptoServiceAbstraction,
|
CryptoServiceAbstraction,
|
||||||
ApiServiceAbstraction,
|
ApiServiceAbstraction,
|
||||||
StateServiceAbstraction,
|
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
safeProvider({
|
safeProvider({
|
||||||
|
@ -0,0 +1,19 @@
|
|||||||
|
import { Injectable } from "@angular/core";
|
||||||
|
|
||||||
|
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||||
|
|
||||||
|
@Injectable({ providedIn: "root" })
|
||||||
|
export class UnassignedItemsBannerApiService {
|
||||||
|
constructor(private apiService: ApiService) {}
|
||||||
|
|
||||||
|
async getShowUnassignedCiphersBanner(): Promise<boolean> {
|
||||||
|
const r = await this.apiService.send(
|
||||||
|
"GET",
|
||||||
|
"/ciphers/has-unassigned-ciphers",
|
||||||
|
null,
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,51 @@
|
|||||||
|
import { MockProxy, mock } from "jest-mock-extended";
|
||||||
|
import { firstValueFrom } from "rxjs";
|
||||||
|
|
||||||
|
import { FakeStateProvider, mockAccountServiceWith } from "@bitwarden/common/spec";
|
||||||
|
import { UserId } from "@bitwarden/common/types/guid";
|
||||||
|
|
||||||
|
import { UnassignedItemsBannerApiService } from "./unassigned-items-banner.api.service";
|
||||||
|
import { SHOW_BANNER_KEY, UnassignedItemsBannerService } from "./unassigned-items-banner.service";
|
||||||
|
|
||||||
|
describe("UnassignedItemsBanner", () => {
|
||||||
|
let stateProvider: FakeStateProvider;
|
||||||
|
let apiService: MockProxy<UnassignedItemsBannerApiService>;
|
||||||
|
|
||||||
|
const sutFactory = () => new UnassignedItemsBannerService(stateProvider, apiService);
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
const fakeAccountService = mockAccountServiceWith("userId" as UserId);
|
||||||
|
stateProvider = new FakeStateProvider(fakeAccountService);
|
||||||
|
apiService = mock();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("shows the banner if showBanner local state is true", async () => {
|
||||||
|
const showBanner = stateProvider.activeUser.getFake(SHOW_BANNER_KEY);
|
||||||
|
showBanner.nextState(true);
|
||||||
|
|
||||||
|
const sut = sutFactory();
|
||||||
|
expect(await firstValueFrom(sut.showBanner$)).toBe(true);
|
||||||
|
expect(apiService.getShowUnassignedCiphersBanner).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("does not show the banner if showBanner local state is false", async () => {
|
||||||
|
const showBanner = stateProvider.activeUser.getFake(SHOW_BANNER_KEY);
|
||||||
|
showBanner.nextState(false);
|
||||||
|
|
||||||
|
const sut = sutFactory();
|
||||||
|
expect(await firstValueFrom(sut.showBanner$)).toBe(false);
|
||||||
|
expect(apiService.getShowUnassignedCiphersBanner).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("fetches from server if local state has not been set yet", async () => {
|
||||||
|
apiService.getShowUnassignedCiphersBanner.mockResolvedValue(true);
|
||||||
|
|
||||||
|
const showBanner = stateProvider.activeUser.getFake(SHOW_BANNER_KEY);
|
||||||
|
showBanner.nextState(undefined);
|
||||||
|
|
||||||
|
const sut = sutFactory();
|
||||||
|
|
||||||
|
expect(await firstValueFrom(sut.showBanner$)).toBe(true);
|
||||||
|
expect(apiService.getShowUnassignedCiphersBanner).toHaveBeenCalledTimes(1);
|
||||||
|
});
|
||||||
|
});
|
47
libs/angular/src/services/unassigned-items-banner.service.ts
Normal file
47
libs/angular/src/services/unassigned-items-banner.service.ts
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
import { Injectable } from "@angular/core";
|
||||||
|
import { concatMap } from "rxjs";
|
||||||
|
|
||||||
|
import {
|
||||||
|
StateProvider,
|
||||||
|
UNASSIGNED_ITEMS_BANNER_DISK,
|
||||||
|
UserKeyDefinition,
|
||||||
|
} from "@bitwarden/common/platform/state";
|
||||||
|
|
||||||
|
import { UnassignedItemsBannerApiService } from "./unassigned-items-banner.api.service";
|
||||||
|
|
||||||
|
export const SHOW_BANNER_KEY = new UserKeyDefinition<boolean>(
|
||||||
|
UNASSIGNED_ITEMS_BANNER_DISK,
|
||||||
|
"showBanner",
|
||||||
|
{
|
||||||
|
deserializer: (b) => b,
|
||||||
|
clearOn: [],
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
/** Displays a banner that tells users how to move their unassigned items into a collection. */
|
||||||
|
@Injectable({ providedIn: "root" })
|
||||||
|
export class UnassignedItemsBannerService {
|
||||||
|
private _showBanner = this.stateProvider.getActive(SHOW_BANNER_KEY);
|
||||||
|
|
||||||
|
showBanner$ = this._showBanner.state$.pipe(
|
||||||
|
concatMap(async (showBannerState) => {
|
||||||
|
// null indicates that the user has not seen or dismissed the banner yet - get the flag from server
|
||||||
|
if (showBannerState == null) {
|
||||||
|
const showBannerResponse = await this.apiService.getShowUnassignedCiphersBanner();
|
||||||
|
await this._showBanner.update(() => showBannerResponse);
|
||||||
|
return showBannerResponse;
|
||||||
|
}
|
||||||
|
|
||||||
|
return showBannerState;
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private stateProvider: StateProvider,
|
||||||
|
private apiService: UnassignedItemsBannerApiService,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
async hideBanner() {
|
||||||
|
await this._showBanner.update(() => false);
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,13 @@
|
|||||||
import { Directive, NgZone, OnDestroy, OnInit } from "@angular/core";
|
import { Directive, NgZone, OnDestroy, OnInit } from "@angular/core";
|
||||||
import { Subject, firstValueFrom, mergeMap, takeUntil } from "rxjs";
|
import {
|
||||||
|
BehaviorSubject,
|
||||||
|
Subject,
|
||||||
|
firstValueFrom,
|
||||||
|
mergeMap,
|
||||||
|
from,
|
||||||
|
switchMap,
|
||||||
|
takeUntil,
|
||||||
|
} from "rxjs";
|
||||||
|
|
||||||
import { SearchService } from "@bitwarden/common/abstractions/search.service";
|
import { SearchService } from "@bitwarden/common/abstractions/search.service";
|
||||||
import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
|
import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
|
||||||
@ -24,7 +32,6 @@ export class SendComponent implements OnInit, OnDestroy {
|
|||||||
expired = false;
|
expired = false;
|
||||||
type: SendType = null;
|
type: SendType = null;
|
||||||
sends: SendView[] = [];
|
sends: SendView[] = [];
|
||||||
searchText: string;
|
|
||||||
selectedType: SendType;
|
selectedType: SendType;
|
||||||
selectedAll: boolean;
|
selectedAll: boolean;
|
||||||
filter: (cipher: SendView) => boolean;
|
filter: (cipher: SendView) => boolean;
|
||||||
@ -39,6 +46,8 @@ export class SendComponent implements OnInit, OnDestroy {
|
|||||||
private searchTimeout: any;
|
private searchTimeout: any;
|
||||||
private destroy$ = new Subject<void>();
|
private destroy$ = new Subject<void>();
|
||||||
private _filteredSends: SendView[];
|
private _filteredSends: SendView[];
|
||||||
|
private _searchText$ = new BehaviorSubject<string>("");
|
||||||
|
protected isSearchable: boolean = false;
|
||||||
|
|
||||||
get filteredSends(): SendView[] {
|
get filteredSends(): SendView[] {
|
||||||
return this._filteredSends;
|
return this._filteredSends;
|
||||||
@ -48,6 +57,14 @@ export class SendComponent implements OnInit, OnDestroy {
|
|||||||
this._filteredSends = filteredSends;
|
this._filteredSends = filteredSends;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get searchText() {
|
||||||
|
return this._searchText$.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
set searchText(value: string) {
|
||||||
|
this._searchText$.next(value);
|
||||||
|
}
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
protected sendService: SendService,
|
protected sendService: SendService,
|
||||||
protected i18nService: I18nService,
|
protected i18nService: I18nService,
|
||||||
@ -68,6 +85,15 @@ export class SendComponent implements OnInit, OnDestroy {
|
|||||||
.subscribe((policyAppliesToActiveUser) => {
|
.subscribe((policyAppliesToActiveUser) => {
|
||||||
this.disableSend = policyAppliesToActiveUser;
|
this.disableSend = policyAppliesToActiveUser;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this._searchText$
|
||||||
|
.pipe(
|
||||||
|
switchMap((searchText) => from(this.searchService.isSearchable(searchText))),
|
||||||
|
takeUntil(this.destroy$),
|
||||||
|
)
|
||||||
|
.subscribe((isSearchable) => {
|
||||||
|
this.isSearchable = isSearchable;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnDestroy() {
|
ngOnDestroy() {
|
||||||
@ -122,14 +148,14 @@ export class SendComponent implements OnInit, OnDestroy {
|
|||||||
clearTimeout(this.searchTimeout);
|
clearTimeout(this.searchTimeout);
|
||||||
}
|
}
|
||||||
if (timeout == null) {
|
if (timeout == null) {
|
||||||
this.hasSearched = this.searchService.isSearchable(this.searchText);
|
this.hasSearched = this.isSearchable;
|
||||||
this.filteredSends = this.sends.filter((s) => this.filter == null || this.filter(s));
|
this.filteredSends = this.sends.filter((s) => this.filter == null || this.filter(s));
|
||||||
this.applyTextSearch();
|
this.applyTextSearch();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.searchPending = true;
|
this.searchPending = true;
|
||||||
this.searchTimeout = setTimeout(async () => {
|
this.searchTimeout = setTimeout(async () => {
|
||||||
this.hasSearched = this.searchService.isSearchable(this.searchText);
|
this.hasSearched = this.isSearchable;
|
||||||
this.filteredSends = this.sends.filter((s) => this.filter == null || this.filter(s));
|
this.filteredSends = this.sends.filter((s) => this.filter == null || this.filter(s));
|
||||||
this.applyTextSearch();
|
this.applyTextSearch();
|
||||||
this.searchPending = false;
|
this.searchPending = false;
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { Directive, EventEmitter, Input, Output } from "@angular/core";
|
import { Directive, EventEmitter, Input, OnDestroy, OnInit, Output } from "@angular/core";
|
||||||
|
import { BehaviorSubject, Subject, from, switchMap, takeUntil } from "rxjs";
|
||||||
|
|
||||||
import { SearchService } from "@bitwarden/common/abstractions/search.service";
|
import { SearchService } from "@bitwarden/common/abstractions/search.service";
|
||||||
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
|
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
|
||||||
@ -6,7 +7,7 @@ import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.servi
|
|||||||
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
|
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
|
||||||
|
|
||||||
@Directive()
|
@Directive()
|
||||||
export class VaultItemsComponent {
|
export class VaultItemsComponent implements OnInit, OnDestroy {
|
||||||
@Input() activeCipherId: string = null;
|
@Input() activeCipherId: string = null;
|
||||||
@Output() onCipherClicked = new EventEmitter<CipherView>();
|
@Output() onCipherClicked = new EventEmitter<CipherView>();
|
||||||
@Output() onCipherRightClicked = new EventEmitter<CipherView>();
|
@Output() onCipherRightClicked = new EventEmitter<CipherView>();
|
||||||
@ -23,13 +24,15 @@ export class VaultItemsComponent {
|
|||||||
|
|
||||||
protected searchPending = false;
|
protected searchPending = false;
|
||||||
|
|
||||||
|
private destroy$ = new Subject<void>();
|
||||||
private searchTimeout: any = null;
|
private searchTimeout: any = null;
|
||||||
private _searchText: string = null;
|
private isSearchable: boolean = false;
|
||||||
|
private _searchText$ = new BehaviorSubject<string>("");
|
||||||
get searchText() {
|
get searchText() {
|
||||||
return this._searchText;
|
return this._searchText$.value;
|
||||||
}
|
}
|
||||||
set searchText(value: string) {
|
set searchText(value: string) {
|
||||||
this._searchText = value;
|
this._searchText$.next(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@ -37,6 +40,21 @@ export class VaultItemsComponent {
|
|||||||
protected cipherService: CipherService,
|
protected cipherService: CipherService,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this._searchText$
|
||||||
|
.pipe(
|
||||||
|
switchMap((searchText) => from(this.searchService.isSearchable(searchText))),
|
||||||
|
takeUntil(this.destroy$),
|
||||||
|
)
|
||||||
|
.subscribe((isSearchable) => {
|
||||||
|
this.isSearchable = isSearchable;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy(): void {
|
||||||
|
throw new Error("Method not implemented.");
|
||||||
|
}
|
||||||
|
|
||||||
async load(filter: (cipher: CipherView) => boolean = null, deleted = false) {
|
async load(filter: (cipher: CipherView) => boolean = null, deleted = false) {
|
||||||
this.deleted = deleted ?? false;
|
this.deleted = deleted ?? false;
|
||||||
await this.applyFilter(filter);
|
await this.applyFilter(filter);
|
||||||
@ -90,7 +108,7 @@ export class VaultItemsComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
isSearching() {
|
isSearching() {
|
||||||
return !this.searchPending && this.searchService.isSearchable(this.searchText);
|
return !this.searchPending && this.isSearchable;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected deletedFilter: (cipher: CipherView) => boolean = (c) => c.isDeleted === this.deleted;
|
protected deletedFilter: (cipher: CipherView) => boolean = (c) => c.isDeleted === this.deleted;
|
||||||
|
@ -5,6 +5,7 @@ import { DeviceTrustCryptoServiceAbstraction } from "@bitwarden/common/auth/abst
|
|||||||
import { TokenService } from "@bitwarden/common/auth/abstractions/token.service";
|
import { TokenService } from "@bitwarden/common/auth/abstractions/token.service";
|
||||||
import { TwoFactorService } from "@bitwarden/common/auth/abstractions/two-factor.service";
|
import { TwoFactorService } from "@bitwarden/common/auth/abstractions/two-factor.service";
|
||||||
import { IdentityTokenResponse } from "@bitwarden/common/auth/models/response/identity-token.response";
|
import { IdentityTokenResponse } from "@bitwarden/common/auth/models/response/identity-token.response";
|
||||||
|
import { FakeMasterPasswordService } from "@bitwarden/common/auth/services/master-password/fake-master-password.service";
|
||||||
import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service";
|
import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service";
|
||||||
import { AppIdService } from "@bitwarden/common/platform/abstractions/app-id.service";
|
import { AppIdService } from "@bitwarden/common/platform/abstractions/app-id.service";
|
||||||
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
|
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
|
||||||
@ -14,7 +15,9 @@ import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/pl
|
|||||||
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
|
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
|
||||||
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
||||||
import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key";
|
import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key";
|
||||||
|
import { FakeAccountService, mockAccountServiceWith } from "@bitwarden/common/spec";
|
||||||
import { CsprngArray } from "@bitwarden/common/types/csprng";
|
import { CsprngArray } from "@bitwarden/common/types/csprng";
|
||||||
|
import { UserId } from "@bitwarden/common/types/guid";
|
||||||
import { MasterKey, UserKey } from "@bitwarden/common/types/key";
|
import { MasterKey, UserKey } from "@bitwarden/common/types/key";
|
||||||
|
|
||||||
import { InternalUserDecryptionOptionsServiceAbstraction } from "../abstractions/user-decryption-options.service.abstraction";
|
import { InternalUserDecryptionOptionsServiceAbstraction } from "../abstractions/user-decryption-options.service.abstraction";
|
||||||
@ -42,6 +45,10 @@ describe("AuthRequestLoginStrategy", () => {
|
|||||||
let deviceTrustCryptoService: MockProxy<DeviceTrustCryptoServiceAbstraction>;
|
let deviceTrustCryptoService: MockProxy<DeviceTrustCryptoServiceAbstraction>;
|
||||||
let billingAccountProfileStateService: MockProxy<BillingAccountProfileStateService>;
|
let billingAccountProfileStateService: MockProxy<BillingAccountProfileStateService>;
|
||||||
|
|
||||||
|
const mockUserId = Utils.newGuid() as UserId;
|
||||||
|
let accountService: FakeAccountService;
|
||||||
|
let masterPasswordService: FakeMasterPasswordService;
|
||||||
|
|
||||||
let authRequestLoginStrategy: AuthRequestLoginStrategy;
|
let authRequestLoginStrategy: AuthRequestLoginStrategy;
|
||||||
let credentials: AuthRequestLoginCredentials;
|
let credentials: AuthRequestLoginCredentials;
|
||||||
let tokenResponse: IdentityTokenResponse;
|
let tokenResponse: IdentityTokenResponse;
|
||||||
@ -71,12 +78,17 @@ describe("AuthRequestLoginStrategy", () => {
|
|||||||
deviceTrustCryptoService = mock<DeviceTrustCryptoServiceAbstraction>();
|
deviceTrustCryptoService = mock<DeviceTrustCryptoServiceAbstraction>();
|
||||||
billingAccountProfileStateService = mock<BillingAccountProfileStateService>();
|
billingAccountProfileStateService = mock<BillingAccountProfileStateService>();
|
||||||
|
|
||||||
|
accountService = mockAccountServiceWith(mockUserId);
|
||||||
|
masterPasswordService = new FakeMasterPasswordService();
|
||||||
|
|
||||||
tokenService.getTwoFactorToken.mockResolvedValue(null);
|
tokenService.getTwoFactorToken.mockResolvedValue(null);
|
||||||
appIdService.getAppId.mockResolvedValue(deviceId);
|
appIdService.getAppId.mockResolvedValue(deviceId);
|
||||||
tokenService.decodeAccessToken.mockResolvedValue({});
|
tokenService.decodeAccessToken.mockResolvedValue({});
|
||||||
|
|
||||||
authRequestLoginStrategy = new AuthRequestLoginStrategy(
|
authRequestLoginStrategy = new AuthRequestLoginStrategy(
|
||||||
cache,
|
cache,
|
||||||
|
accountService,
|
||||||
|
masterPasswordService,
|
||||||
cryptoService,
|
cryptoService,
|
||||||
apiService,
|
apiService,
|
||||||
tokenService,
|
tokenService,
|
||||||
@ -108,13 +120,16 @@ describe("AuthRequestLoginStrategy", () => {
|
|||||||
const masterKey = new SymmetricCryptoKey(new Uint8Array(64).buffer as CsprngArray) as MasterKey;
|
const masterKey = new SymmetricCryptoKey(new Uint8Array(64).buffer as CsprngArray) as MasterKey;
|
||||||
const userKey = new SymmetricCryptoKey(new Uint8Array(64).buffer as CsprngArray) as UserKey;
|
const userKey = new SymmetricCryptoKey(new Uint8Array(64).buffer as CsprngArray) as UserKey;
|
||||||
|
|
||||||
cryptoService.getMasterKey.mockResolvedValue(masterKey);
|
masterPasswordService.masterKeySubject.next(masterKey);
|
||||||
cryptoService.decryptUserKeyWithMasterKey.mockResolvedValue(userKey);
|
cryptoService.decryptUserKeyWithMasterKey.mockResolvedValue(userKey);
|
||||||
|
|
||||||
await authRequestLoginStrategy.logIn(credentials);
|
await authRequestLoginStrategy.logIn(credentials);
|
||||||
|
|
||||||
expect(cryptoService.setMasterKey).toHaveBeenCalledWith(masterKey);
|
expect(masterPasswordService.mock.setMasterKey).toHaveBeenCalledWith(masterKey, mockUserId);
|
||||||
expect(cryptoService.setMasterKeyHash).toHaveBeenCalledWith(decMasterKeyHash);
|
expect(masterPasswordService.mock.setMasterKeyHash).toHaveBeenCalledWith(
|
||||||
|
decMasterKeyHash,
|
||||||
|
mockUserId,
|
||||||
|
);
|
||||||
expect(cryptoService.setMasterKeyEncryptedUserKey).toHaveBeenCalledWith(tokenResponse.key);
|
expect(cryptoService.setMasterKeyEncryptedUserKey).toHaveBeenCalledWith(tokenResponse.key);
|
||||||
expect(cryptoService.setUserKey).toHaveBeenCalledWith(userKey);
|
expect(cryptoService.setUserKey).toHaveBeenCalledWith(userKey);
|
||||||
expect(deviceTrustCryptoService.trustDeviceIfRequired).toHaveBeenCalled();
|
expect(deviceTrustCryptoService.trustDeviceIfRequired).toHaveBeenCalled();
|
||||||
@ -136,8 +151,8 @@ describe("AuthRequestLoginStrategy", () => {
|
|||||||
await authRequestLoginStrategy.logIn(credentials);
|
await authRequestLoginStrategy.logIn(credentials);
|
||||||
|
|
||||||
// setMasterKey and setMasterKeyHash should not be called
|
// setMasterKey and setMasterKeyHash should not be called
|
||||||
expect(cryptoService.setMasterKey).not.toHaveBeenCalled();
|
expect(masterPasswordService.mock.setMasterKey).not.toHaveBeenCalled();
|
||||||
expect(cryptoService.setMasterKeyHash).not.toHaveBeenCalled();
|
expect(masterPasswordService.mock.setMasterKeyHash).not.toHaveBeenCalled();
|
||||||
|
|
||||||
// setMasterKeyEncryptedUserKey, setUserKey, and setPrivateKey should still be called
|
// setMasterKeyEncryptedUserKey, setUserKey, and setPrivateKey should still be called
|
||||||
expect(cryptoService.setMasterKeyEncryptedUserKey).toHaveBeenCalledWith(tokenResponse.key);
|
expect(cryptoService.setMasterKeyEncryptedUserKey).toHaveBeenCalledWith(tokenResponse.key);
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user