diff --git a/apps/browser/src/_locales/de/messages.json b/apps/browser/src/_locales/de/messages.json index b377809ab1c..7a013add6e7 100644 --- a/apps/browser/src/_locales/de/messages.json +++ b/apps/browser/src/_locales/de/messages.json @@ -2709,7 +2709,7 @@ "message": "Starte DUO und folge den Schritten, um die Anmeldung zu abzuschließen." }, "duoRequiredForAccount": { - "message": "Duo two-step login is required for your account." + "message": "Für dein Konto ist die Duo Zwei-Faktor-Authentifizierung erforderlich." }, "popoutTheExtensionToCompleteLogin": { "message": "Popout the extension to complete login." diff --git a/apps/browser/src/_locales/es/messages.json b/apps/browser/src/_locales/es/messages.json index 471c1981d7a..58172b1a7af 100644 --- a/apps/browser/src/_locales/es/messages.json +++ b/apps/browser/src/_locales/es/messages.json @@ -2005,7 +2005,7 @@ "message": "Seleccione carpeta..." }, "noFoldersFound": { - "message": "No folders found", + "message": "Ninguna carpeta encontrada", "description": "Used as a message within the notification bar when no folders are found" }, "orgPermissionsUpdatedMustSetPassword": { @@ -2670,25 +2670,25 @@ "message": "Verificar biométricamente" }, "awaitingConfirmation": { - "message": "Awaiting confirmation" + "message": "Esperando confirmación" }, "couldNotCompleteBiometrics": { - "message": "Could not complete biometrics." + "message": "No se pudo completar la biométrica." }, "needADifferentMethod": { "message": "¿Necesita un método distinto?" }, "useMasterPassword": { - "message": "Use master password" + "message": "Usar contraseña maestra" }, "usePin": { "message": "Usar NIP" }, "useBiometrics": { - "message": "Use biometrics" + "message": "Usar biométrica" }, "enterVerificationCodeSentToEmail": { - "message": "Enter the verification code that was sent to your email." + "message": "Introduzca el código de verificación que se ha enviado a su correo electrónico." }, "resendCode": { "message": "Volver a enviar código" @@ -2706,19 +2706,19 @@ } }, "launchDuoAndFollowStepsToFinishLoggingIn": { - "message": "Launch Duo and follow the steps to finish logging in." + "message": "Abra Duo y siga los pasos para terminar de iniciar sesión." }, "duoRequiredForAccount": { - "message": "Duo two-step login is required for your account." + "message": "Se requiere el inicio de sesión en dos pasos Duo para su cuenta." }, "popoutTheExtensionToCompleteLogin": { - "message": "Popout the extension to complete login." + "message": "Abra la extensión para completar el inicio de sesión." }, "popoutExtension": { - "message": "Popout extension" + "message": "Abrir extensión" }, "launchDuo": { - "message": "Launch Duo" + "message": "Iniciar Duo" }, "importFormatError": { "message": "Los datos no están formateados correctamente. Por favor, comprueba tu archivo de importación e inténtalo de nuevo." @@ -2995,15 +2995,15 @@ "description": "Button text for the setting that allows overriding the default browser autofill settings" }, "saveCipherAttemptSuccess": { - "message": "Credentials saved successfully!", + "message": "¡Credenciales guardadas con éxito!", "description": "Notification message for when saving credentials has succeeded." }, "updateCipherAttemptSuccess": { - "message": "Credentials updated successfully!", + "message": "¡Credenciales actualizadas con éxito!", "description": "Notification message for when updating credentials has succeeded." }, "saveCipherAttemptFailed": { - "message": "Error saving credentials. Check console for details.", + "message": "Se produjo un error al guardar las credenciales. Revise la consola para obtener detalles.", "description": "Notification message for when saving credentials has failed." } } diff --git a/apps/browser/src/_locales/ko/messages.json b/apps/browser/src/_locales/ko/messages.json index a4186df93bf..2943c7234d9 100644 --- a/apps/browser/src/_locales/ko/messages.json +++ b/apps/browser/src/_locales/ko/messages.json @@ -92,13 +92,13 @@ "message": "자동 완성" }, "autoFillLogin": { - "message": "Auto-fill login" + "message": "로그인 자동 완성" }, "autoFillCard": { - "message": "Auto-fill card" + "message": "카드 자동 완성" }, "autoFillIdentity": { - "message": "Auto-fill identity" + "message": "신원 자동 완성" }, "generatePasswordCopied": { "message": "비밀번호 생성 및 클립보드에 복사" @@ -110,19 +110,19 @@ "message": "사용할 수 있는 로그인이 없습니다." }, "noCards": { - "message": "No cards" + "message": "카드 없음" }, "noIdentities": { - "message": "No identities" + "message": "신원 없음" }, "addLoginMenu": { - "message": "Add login" + "message": "로그인 추가" }, "addCardMenu": { - "message": "Add card" + "message": "카드 추가" }, "addIdentityMenu": { - "message": "Add identity" + "message": "신원 추가" }, "unlockVaultMenu": { "message": "보관함 잠금 해제" @@ -220,7 +220,7 @@ "message": "도움말 및 의견" }, "helpCenter": { - "message": "Bitwarden Help center" + "message": "Bitwarden 도움말 센터" }, "communityForums": { "message": "Explore Bitwarden community forums" diff --git a/apps/browser/src/admin-console/background/service-factories/policy-service.factory.ts b/apps/browser/src/admin-console/background/service-factories/policy-service.factory.ts index efdb743196f..b00693bd564 100644 --- a/apps/browser/src/admin-console/background/service-factories/policy-service.factory.ts +++ b/apps/browser/src/admin-console/background/service-factories/policy-service.factory.ts @@ -1,4 +1,5 @@ import { PolicyService as AbstractPolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; +import { PolicyService } from "@bitwarden/common/admin-console/services/policy/policy.service"; import { CachedServices, @@ -9,11 +10,6 @@ import { stateProviderFactory, StateProviderInitOptions, } from "../../../platform/background/service-factories/state-provider.factory"; -import { - stateServiceFactory as stateServiceFactory, - StateServiceInitOptions, -} from "../../../platform/background/service-factories/state-service.factory"; -import { BrowserPolicyService } from "../../services/browser-policy.service"; import { organizationServiceFactory, @@ -23,7 +19,6 @@ import { type PolicyServiceFactoryOptions = FactoryOptions; export type PolicyServiceInitOptions = PolicyServiceFactoryOptions & - StateServiceInitOptions & StateProviderInitOptions & OrganizationServiceInitOptions; @@ -36,8 +31,7 @@ export function policyServiceFactory( "policyService", opts, async () => - new BrowserPolicyService( - await stateServiceFactory(cache, opts), + new PolicyService( await stateProviderFactory(cache, opts), await organizationServiceFactory(cache, opts), ), diff --git a/apps/browser/src/admin-console/services/browser-policy.service.ts b/apps/browser/src/admin-console/services/browser-policy.service.ts deleted file mode 100644 index 2022cfec583..00000000000 --- a/apps/browser/src/admin-console/services/browser-policy.service.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { BehaviorSubject } from "rxjs"; -import { Jsonify } from "type-fest"; - -import { Policy } from "@bitwarden/common/admin-console/models/domain/policy"; -import { PolicyService } from "@bitwarden/common/admin-console/services/policy/policy.service"; - -import { browserSession, sessionSync } from "../../platform/decorators/session-sync-observable"; - -@browserSession -export class BrowserPolicyService extends PolicyService { - @sessionSync({ - initializer: (obj: Jsonify) => Object.assign(new Policy(), obj), - initializeAs: "array", - }) - protected _policies: BehaviorSubject; -} diff --git a/apps/browser/src/autofill/browser/main-context-menu-handler.spec.ts b/apps/browser/src/autofill/browser/main-context-menu-handler.spec.ts index f2b17d2f08a..b827788d75c 100644 --- a/apps/browser/src/autofill/browser/main-context-menu-handler.spec.ts +++ b/apps/browser/src/autofill/browser/main-context-menu-handler.spec.ts @@ -1,6 +1,8 @@ import { mock, MockProxy } from "jest-mock-extended"; +import { of } from "rxjs"; import { NOOP_COMMAND_SUFFIX } from "@bitwarden/common/autofill/constants"; +import { AutofillSettingsServiceAbstraction } from "@bitwarden/common/autofill/services/autofill-settings.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { CipherType } from "@bitwarden/common/vault/enums"; @@ -13,6 +15,7 @@ import { MainContextMenuHandler } from "./main-context-menu-handler"; describe("context-menu", () => { let stateService: MockProxy; + let autofillSettingsService: MockProxy; let i18nService: MockProxy; let logService: MockProxy; @@ -26,6 +29,7 @@ describe("context-menu", () => { beforeEach(() => { stateService = mock(); + autofillSettingsService = mock(); i18nService = mock(); logService = mock(); @@ -41,14 +45,20 @@ describe("context-menu", () => { }); i18nService.t.mockImplementation((key) => key); - sut = new MainContextMenuHandler(stateService, i18nService, logService); + sut = new MainContextMenuHandler( + stateService, + autofillSettingsService, + i18nService, + logService, + ); + autofillSettingsService.enableContextMenu$ = of(true); }); afterEach(() => jest.resetAllMocks()); describe("init", () => { it("has menu disabled", async () => { - stateService.getDisableContextMenuItem.mockResolvedValue(true); + autofillSettingsService.enableContextMenu$ = of(false); const createdMenu = await sut.init(); expect(createdMenu).toBeFalsy(); @@ -56,8 +66,6 @@ describe("context-menu", () => { }); it("has menu enabled, but does not have premium", async () => { - stateService.getDisableContextMenuItem.mockResolvedValue(false); - stateService.getCanAccessPremium.mockResolvedValue(false); const createdMenu = await sut.init(); @@ -66,8 +74,6 @@ describe("context-menu", () => { }); it("has menu enabled and has premium", async () => { - stateService.getDisableContextMenuItem.mockResolvedValue(false); - stateService.getCanAccessPremium.mockResolvedValue(true); const createdMenu = await sut.init(); diff --git a/apps/browser/src/autofill/browser/main-context-menu-handler.ts b/apps/browser/src/autofill/browser/main-context-menu-handler.ts index 19154fbfb81..998b5c7258b 100644 --- a/apps/browser/src/autofill/browser/main-context-menu-handler.ts +++ b/apps/browser/src/autofill/browser/main-context-menu-handler.ts @@ -1,3 +1,5 @@ +import { firstValueFrom } from "rxjs"; + import { AUTOFILL_CARD_ID, AUTOFILL_ID, @@ -14,6 +16,7 @@ import { ROOT_ID, SEPARATOR_ID, } from "@bitwarden/common/autofill/constants"; +import { AutofillSettingsServiceAbstraction } from "@bitwarden/common/autofill/services/autofill-settings.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { StateFactory } from "@bitwarden/common/platform/factories/state-factory"; @@ -22,6 +25,7 @@ import { GlobalState } from "@bitwarden/common/platform/models/domain/global-sta import { CipherType } from "@bitwarden/common/vault/enums"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; +import { autofillSettingsServiceFactory } from "../../autofill/background/service_factories/autofill-settings-service.factory"; import { Account } from "../../models/account"; import { CachedServices } from "../../platform/background/service-factories/factory-options"; import { @@ -156,6 +160,7 @@ export class MainContextMenuHandler { constructor( private stateService: BrowserStateService, + private autofillSettingsService: AutofillSettingsServiceAbstraction, private i18nService: I18nService, private logService: LogService, ) {} @@ -183,6 +188,7 @@ export class MainContextMenuHandler { return new MainContextMenuHandler( await stateServiceFactory(cachedServices, serviceOptions), + await autofillSettingsServiceFactory(cachedServices, serviceOptions), await i18nServiceFactory(cachedServices, serviceOptions), await logServiceFactory(cachedServices, serviceOptions), ); @@ -193,8 +199,8 @@ export class MainContextMenuHandler { * @returns a boolean showing whether or not items were created */ async init(): Promise { - const menuDisabled = await this.stateService.getDisableContextMenuItem(); - if (menuDisabled) { + const menuEnabled = await firstValueFrom(this.autofillSettingsService.enableContextMenu$); + if (!menuEnabled) { await MainContextMenuHandler.removeAll(); return false; } diff --git a/apps/browser/src/background/main.background.ts b/apps/browser/src/background/main.background.ts index ce0c67efa11..9028a7cfffb 100644 --- a/apps/browser/src/background/main.background.ts +++ b/apps/browser/src/background/main.background.ts @@ -22,6 +22,7 @@ import { PolicyApiServiceAbstraction } from "@bitwarden/common/admin-console/abs import { InternalPolicyService as InternalPolicyServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; import { ProviderService as ProviderServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/provider.service"; import { PolicyApiService } from "@bitwarden/common/admin-console/services/policy/policy-api.service"; +import { PolicyService } from "@bitwarden/common/admin-console/services/policy/policy.service"; import { ProviderService } from "@bitwarden/common/admin-console/services/provider.service"; import { AccountService as AccountServiceAbstraction } from "@bitwarden/common/auth/abstractions/account.service"; import { AuthService as AuthServiceAbstraction } from "@bitwarden/common/auth/abstractions/auth.service"; @@ -175,7 +176,6 @@ import { } from "@bitwarden/vault-export-core"; import { BrowserOrganizationService } from "../admin-console/services/browser-organization.service"; -import { BrowserPolicyService } from "../admin-console/services/browser-policy.service"; import ContextMenusBackground from "../autofill/background/context-menus.background"; import NotificationBackground from "../autofill/background/notification.background"; import OverlayBackground from "../autofill/background/overlay.background"; @@ -501,11 +501,7 @@ export default class MainBackground { this.stateService, this.stateProvider, ); - this.policyService = new BrowserPolicyService( - this.stateService, - this.stateProvider, - this.organizationService, - ); + this.policyService = new PolicyService(this.stateProvider, this.organizationService); this.autofillSettingsService = new AutofillSettingsService( this.stateProvider, this.policyService, @@ -818,6 +814,7 @@ export default class MainBackground { this.stateService, this.autofillSettingsService, this.vaultTimeoutSettingsService, + this.biometricStateService, ); // Other fields @@ -953,6 +950,7 @@ export default class MainBackground { if (!this.popupOnlyContext) { this.mainContextMenuHandler = new MainContextMenuHandler( this.stateService, + this.autofillSettingsService, this.i18nService, this.logService, ); diff --git a/apps/browser/src/background/nativeMessaging.background.ts b/apps/browser/src/background/nativeMessaging.background.ts index e4fb46d960f..2717a7b2b56 100644 --- a/apps/browser/src/background/nativeMessaging.background.ts +++ b/apps/browser/src/background/nativeMessaging.background.ts @@ -84,10 +84,6 @@ export class NativeMessagingBackground { private authService: AuthService, private biometricStateService: BiometricStateService, ) { - // 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.stateService.setBiometricFingerprintValidated(false); - if (chrome?.permissions?.onAdded) { // Reload extension to activate nativeMessaging chrome.permissions.onAdded.addListener((permissions) => { @@ -100,9 +96,7 @@ export class NativeMessagingBackground { async connect() { this.appId = await this.appIdService.getAppId(); - // 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.stateService.setBiometricFingerprintValidated(false); + await this.biometricStateService.setFingerprintValidated(false); return new Promise((resolve, reject) => { this.port = BrowserApi.connectNative("com.8bit.bitwarden"); @@ -148,9 +142,7 @@ export class NativeMessagingBackground { if (this.validatingFingerprint) { this.validatingFingerprint = false; - // 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.stateService.setBiometricFingerprintValidated(true); + await this.biometricStateService.setFingerprintValidated(true); } this.sharedSecret = new SymmetricCryptoKey(decrypted); this.secureSetupResolve(); diff --git a/apps/browser/src/popup/services/services.module.ts b/apps/browser/src/popup/services/services.module.ts index 2e6dfaee7a7..b5138274d36 100644 --- a/apps/browser/src/popup/services/services.module.ts +++ b/apps/browser/src/popup/services/services.module.ts @@ -102,7 +102,6 @@ import { ImportServiceAbstraction } from "@bitwarden/importer/core"; import { VaultExportServiceAbstraction } from "@bitwarden/vault-export-core"; import { BrowserOrganizationService } from "../../admin-console/services/browser-organization.service"; -import { BrowserPolicyService } from "../../admin-console/services/browser-policy.service"; import { UnauthGuardService } from "../../auth/popup/services"; import { AutofillService } from "../../autofill/services/abstractions/autofill.service"; import MainBackground from "../../background/main.background"; @@ -238,8 +237,9 @@ function getBgService(service: keyof MainBackground) { }, { provide: LogServiceAbstraction, - useFactory: getBgService("logService"), - deps: [], + useFactory: (platformUtilsService: PlatformUtilsService) => + new ConsoleLogService(platformUtilsService.isDev()), + deps: [PlatformUtilsService], }, { provide: BrowserEnvironmentService, @@ -293,17 +293,6 @@ function getBgService(service: keyof MainBackground) { useFactory: getBgService("eventCollectionService"), deps: [], }, - { - provide: PolicyService, - useFactory: ( - stateService: StateServiceAbstraction, - stateProvider: StateProvider, - organizationService: OrganizationService, - ) => { - return new BrowserPolicyService(stateService, stateProvider, organizationService); - }, - deps: [StateServiceAbstraction, StateProvider, OrganizationService], - }, { provide: PlatformUtilsService, useFactory: getBgService("platformUtilsService"), diff --git a/apps/browser/src/popup/settings/options.component.ts b/apps/browser/src/popup/settings/options.component.ts index ac416a34930..2af37131710 100644 --- a/apps/browser/src/popup/settings/options.component.ts +++ b/apps/browser/src/popup/settings/options.component.ts @@ -105,7 +105,9 @@ export class OptionsComponent implements OnInit { this.userNotificationSettingsService.enableChangedPasswordPrompt$, ); - this.enableContextMenuItem = !(await this.stateService.getDisableContextMenuItem()); + this.enableContextMenuItem = await firstValueFrom( + this.autofillSettingsService.enableContextMenu$, + ); this.showCardsCurrentTab = !(await this.stateService.getDontShowCardsCurrentTab()); this.showIdentitiesCurrentTab = !(await this.stateService.getDontShowIdentitiesCurrentTab()); @@ -143,7 +145,7 @@ export class OptionsComponent implements OnInit { } async updateContextMenuItem() { - await this.stateService.setDisableContextMenuItem(!this.enableContextMenuItem); + await this.autofillSettingsService.setEnableContextMenu(this.enableContextMenuItem); this.messagingService.send("bgUpdateContextMenu"); } diff --git a/apps/browser/src/popup/settings/settings.component.ts b/apps/browser/src/popup/settings/settings.component.ts index f622cffd3e4..c83c7b5e72c 100644 --- a/apps/browser/src/popup/settings/settings.component.ts +++ b/apps/browser/src/popup/settings/settings.component.ts @@ -415,7 +415,7 @@ export class SettingsComponent implements OnInit { ]); } else { await this.biometricStateService.setBiometricUnlockEnabled(false); - await this.stateService.setBiometricFingerprintValidated(false); + await this.biometricStateService.setFingerprintValidated(false); } } diff --git a/apps/cli/package.json b/apps/cli/package.json index fe1a9e77ae4..289f82d1097 100644 --- a/apps/cli/package.json +++ b/apps/cli/package.json @@ -71,7 +71,7 @@ "papaparse": "5.4.1", "proper-lockfile": "4.1.2", "rxjs": "7.8.1", - "tldts": "6.1.11", + "tldts": "6.1.13", "zxcvbn": "4.4.2" } } diff --git a/apps/cli/src/bw.ts b/apps/cli/src/bw.ts index e46937f71f7..581d37c9e6a 100644 --- a/apps/cli/src/bw.ts +++ b/apps/cli/src/bw.ts @@ -394,11 +394,7 @@ export class Main { this.organizationUserService = new OrganizationUserServiceImplementation(this.apiService); - this.policyService = new PolicyService( - this.stateService, - this.stateProvider, - this.organizationService, - ); + this.policyService = new PolicyService(this.stateProvider, this.organizationService); this.policyApiService = new PolicyApiService(this.policyService, this.apiService); @@ -653,7 +649,7 @@ export class Main { this.cipherService.clear(userId), this.folderService.clear(userId), this.collectionService.clear(userId as UserId), - this.policyService.clear(userId), + this.policyService.clear(userId as UserId), this.passwordGenerationService.clear(), this.providerService.save(null, userId as UserId), ]); diff --git a/apps/desktop/desktop_native/Cargo.lock b/apps/desktop/desktop_native/Cargo.lock index 8905ddabf8d..085ec3f872f 100644 --- a/apps/desktop/desktop_native/Cargo.lock +++ b/apps/desktop/desktop_native/Cargo.lock @@ -510,9 +510,9 @@ checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0" [[package]] name = "gio" -version = "0.18.4" +version = "0.19.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4fc8f532f87b79cbc51a79748f16a6828fb784be93145a322fa14d06d354c73" +checksum = "2eae10b27b6dd27e22ed0d812c6387deba295e6fc004a8b379e459b663b05a02" dependencies = [ "futures-channel", "futures-core", @@ -521,7 +521,6 @@ dependencies = [ "gio-sys", "glib", "libc", - "once_cell", "pin-project-lite", "smallvec", "thiserror", @@ -529,22 +528,22 @@ dependencies = [ [[package]] name = "gio-sys" -version = "0.18.1" +version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37566df850baf5e4cb0dfb78af2e4b9898d817ed9263d1090a2df958c64737d2" +checksum = "bcf8e1d9219bb294636753d307b030c1e8a032062cba74f493c431a5c8b81ce4" dependencies = [ "glib-sys", "gobject-sys", "libc", "system-deps", - "winapi", + "windows-sys 0.52.0", ] [[package]] name = "glib" -version = "0.18.2" +version = "0.19.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c316afb01ce8067c5eaab1fc4f2cd47dc21ce7b6296358605e2ffab23ccbd19" +checksum = "ab9e86540b5d8402e905ad4ce7d6aa544092131ab564f3102175af176b90a053" dependencies = [ "bitflags 2.4.1", "futures-channel", @@ -558,20 +557,18 @@ dependencies = [ "gobject-sys", "libc", "memchr", - "once_cell", "smallvec", "thiserror", ] [[package]] name = "glib-macros" -version = "0.18.2" +version = "0.19.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8da903822b136d42360518653fcf154455defc437d3e7a81475bf9a95ff1e47" +checksum = "0f5897ca27a83e4cdc7b4666850bade0a2e73e17689aabafcc9acddad9d823b8" dependencies = [ "heck", "proc-macro-crate", - "proc-macro-error", "proc-macro2", "quote", "syn 2.0.38", @@ -579,9 +576,9 @@ dependencies = [ [[package]] name = "glib-sys" -version = "0.18.1" +version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "063ce2eb6a8d0ea93d2bf8ba1957e78dbab6be1c2220dd3daca57d5a9d869898" +checksum = "630f097773d7c7a0bb3258df4e8157b47dc98bbfa0e60ad9ab56174813feced4" dependencies = [ "libc", "system-deps", @@ -589,9 +586,9 @@ dependencies = [ [[package]] name = "gobject-sys" -version = "0.18.0" +version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0850127b514d1c4a4654ead6dedadb18198999985908e6ffe4436f53c785ce44" +checksum = "c85e2b1080b9418dd0c58b498da3a5c826030343e0ef07bde6a955d28de54979" dependencies = [ "glib-sys", "libc", @@ -681,9 +678,9 @@ dependencies = [ [[package]] name = "libsecret" -version = "0.4.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac6fae6ebe590e06ef9d01b125e46b7d4c05ccbd5961f12b4aefe2ecd010220f" +checksum = "50c6ccddc706a38eca477b4d7857acd6c76c7d6fba5d47b4b2e7d800e5a17194" dependencies = [ "gio", "glib", @@ -693,9 +690,9 @@ dependencies = [ [[package]] name = "libsecret-sys" -version = "0.4.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b716fc5e1c82eb0d28665882628382ab0e0a156a6d73580e33f0ac6ac8d2540" +checksum = "3a1af48e61f1c8e77e9705296f346e45b637754a92348a79b4c62df84d0654c2" dependencies = [ "gio-sys", "glib-sys", @@ -747,9 +744,9 @@ dependencies = [ [[package]] name = "memchr" -version = "2.6.4" +version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" +checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" [[package]] name = "memoffset" @@ -990,36 +987,11 @@ checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] name = "proc-macro-crate" -version = "1.3.1" +version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" +checksum = "6d37c51ca738a55da99dc0c4a34860fd675453b8b36209178c2249bb13651284" dependencies = [ - "once_cell", - "toml_edit 0.19.15", -] - -[[package]] -name = "proc-macro-error" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" -dependencies = [ - "proc-macro-error-attr", - "proc-macro2", - "quote", - "syn 1.0.109", - "version_check", -] - -[[package]] -name = "proc-macro-error-attr" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" -dependencies = [ - "proc-macro2", - "quote", - "version_check", + "toml_edit 0.21.1", ] [[package]] @@ -1252,9 +1224,9 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.11.1" +version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "942b4a808e05215192e39f4ab80813e599068285906cc91aa64f923db842bd5a" +checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7" [[package]] name = "socket2" @@ -1406,17 +1378,6 @@ dependencies = [ "serde", ] -[[package]] -name = "toml_edit" -version = "0.19.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" -dependencies = [ - "indexmap", - "toml_datetime", - "winnow", -] - [[package]] name = "toml_edit" version = "0.20.7" @@ -1430,6 +1391,17 @@ dependencies = [ "winnow", ] +[[package]] +name = "toml_edit" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1" +dependencies = [ + "indexmap", + "toml_datetime", + "winnow", +] + [[package]] name = "tree_magic_mini" version = "3.0.3" diff --git a/apps/desktop/desktop_native/Cargo.toml b/apps/desktop/desktop_native/Cargo.toml index 5bb0b0831b2..19bcf52f6eb 100644 --- a/apps/desktop/desktop_native/Cargo.toml +++ b/apps/desktop/desktop_native/Cargo.toml @@ -54,5 +54,5 @@ security-framework = "=2.9.2" security-framework-sys = "=2.9.1" [target.'cfg(target_os = "linux")'.dependencies] -gio = "=0.18.4" -libsecret = "=0.4.0" +gio = "=0.19.2" +libsecret = "=0.5.0" diff --git a/apps/desktop/src/app/services/services.module.ts b/apps/desktop/src/app/services/services.module.ts index 2fde9744b90..3b50b6ced7a 100644 --- a/apps/desktop/src/app/services/services.module.ts +++ b/apps/desktop/src/app/services/services.module.ts @@ -126,6 +126,7 @@ const RELOAD_CALLBACK = new InjectionToken<() => any>("RELOAD_CALLBACK"); StateServiceAbstraction, AutofillSettingsServiceAbstraction, VaultTimeoutSettingsService, + BiometricStateService, ], }, { diff --git a/apps/desktop/src/auth/accessibility-cookie.component.html b/apps/desktop/src/auth/accessibility-cookie.component.html index b5de1e766ff..e81f754cd74 100644 --- a/apps/desktop/src/auth/accessibility-cookie.component.html +++ b/apps/desktop/src/auth/accessibility-cookie.component.html @@ -28,7 +28,7 @@ - + diff --git a/apps/desktop/src/auth/accessibility-cookie.component.ts b/apps/desktop/src/auth/accessibility-cookie.component.ts index 5ec0dbfb56b..fc72b1a9d77 100644 --- a/apps/desktop/src/auth/accessibility-cookie.component.ts +++ b/apps/desktop/src/auth/accessibility-cookie.component.ts @@ -2,14 +2,11 @@ import { Component, NgZone } from "@angular/core"; import { UntypedFormControl, UntypedFormGroup, Validators } from "@angular/forms"; import { Router } from "@angular/router"; -import { BroadcasterService } from "@bitwarden/common/platform/abstractions/broadcaster.service"; import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { Utils } from "@bitwarden/common/platform/misc/utils"; -const BroadcasterSubscriptionId = "AccessibilityCookieComponent"; - @Component({ selector: "app-accessibility-cookie", templateUrl: "accessibility-cookie.component.html", @@ -27,40 +24,21 @@ export class AccessibilityCookieComponent { protected platformUtilsService: PlatformUtilsService, protected environmentService: EnvironmentService, protected i18nService: I18nService, - private broadcasterService: BroadcasterService, protected ngZone: NgZone, ) {} - async ngOnInit() { - this.broadcasterService.subscribe(BroadcasterSubscriptionId, async (message: any) => { - this.ngZone.run(() => { - switch (message.command) { - case "windowIsFocused": - if (this.listenForCookie) { - this.listenForCookie = false; - // 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.checkForCookie(); - } - break; - default: - } - }); - }); - } - registerhCaptcha() { this.platformUtilsService.launchUri("https://www.hcaptcha.com/accessibility"); } - async checkForCookie() { - this.hCaptchaWindow.close(); + async close() { const [cookie] = await ipc.auth.getHcaptchaAccessibilityCookie(); if (cookie) { this.onCookieSavedSuccess(); } else { this.onCookieSavedFailure(); } + await this.router.navigate(["/login"]); } onCookieSavedSuccess() { @@ -89,10 +67,6 @@ export class AccessibilityCookieComponent { return; } this.listenForCookie = true; - this.hCaptchaWindow = window.open(this.accessibilityForm.value.link); - } - - ngOnDestroy() { - this.broadcasterService.unsubscribe(BroadcasterSubscriptionId); + window.open(this.accessibilityForm.value.link, "_blank", "noopener noreferrer"); } } diff --git a/apps/desktop/src/main/window.main.ts b/apps/desktop/src/main/window.main.ts index 3154a8ccc19..644e4d5f7da 100644 --- a/apps/desktop/src/main/window.main.ts +++ b/apps/desktop/src/main/window.main.ts @@ -30,7 +30,7 @@ export class WindowMain { private windowStateChangeTimer: NodeJS.Timeout; private windowStates: { [key: string]: WindowState } = {}; private enableAlwaysOnTop = false; - private session: Electron.Session; + session: Electron.Session; readonly defaultWidth = 950; readonly defaultHeight = 600; diff --git a/apps/desktop/src/services/electron-main-messaging.service.ts b/apps/desktop/src/services/electron-main-messaging.service.ts index b7e5712a0c4..71e1b1d7d56 100644 --- a/apps/desktop/src/services/electron-main-messaging.service.ts +++ b/apps/desktop/src/services/electron-main-messaging.service.ts @@ -1,16 +1,6 @@ import * as path from "path"; -import { - app, - dialog, - ipcMain, - Menu, - MenuItem, - nativeTheme, - session, - Notification, - shell, -} from "electron"; +import { app, dialog, ipcMain, Menu, MenuItem, nativeTheme, Notification, shell } from "electron"; import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service"; import { ThemeType } from "@bitwarden/common/platform/enums"; @@ -64,7 +54,7 @@ export class ElectronMainMessagingService implements MessagingService { }); ipcMain.handle("getCookie", async (event, options) => { - return await session.defaultSession.cookies.get(options); + return await this.windowMain.session.cookies.get(options); }); ipcMain.handle("loginRequest", async (event, options) => { diff --git a/apps/web/src/app/auth/settings/emergency-access/confirm/emergency-access-confirm.component.html b/apps/web/src/app/auth/settings/emergency-access/confirm/emergency-access-confirm.component.html index 77620677849..a8a4bd53d90 100644 --- a/apps/web/src/app/auth/settings/emergency-access/confirm/emergency-access-confirm.component.html +++ b/apps/web/src/app/auth/settings/emergency-access/confirm/emergency-access-confirm.component.html @@ -1,52 +1,37 @@ - + {{ "learnMore" | i18n }} +

+

+ {{ fingerprint }} +

+ + + + {{ "dontAskFingerprintAgain" | i18n }} + + +
+ + +
+ + diff --git a/apps/web/src/app/auth/settings/emergency-access/confirm/emergency-access-confirm.component.ts b/apps/web/src/app/auth/settings/emergency-access/confirm/emergency-access-confirm.component.ts index 711a7ba9a5b..4afc60c9be3 100644 --- a/apps/web/src/app/auth/settings/emergency-access/confirm/emergency-access-confirm.component.ts +++ b/apps/web/src/app/auth/settings/emergency-access/confirm/emergency-access-confirm.component.ts @@ -1,39 +1,52 @@ -import { Component, EventEmitter, Input, OnInit, Output } from "@angular/core"; +import { DialogConfig, DialogRef, DIALOG_DATA } from "@angular/cdk/dialog"; +import { Component, OnInit, Inject } from "@angular/core"; +import { FormBuilder } from "@angular/forms"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { StateService } from "@bitwarden/common/platform/abstractions/state.service"; import { Utils } from "@bitwarden/common/platform/misc/utils"; +import { DialogService } from "@bitwarden/components"; +export enum EmergencyAccessConfirmDialogResult { + Confirmed = "confirmed", +} +type EmergencyAccessConfirmDialogData = { + /** display name of the account requesting emergency access */ + name: string; + /** identifies the account requesting emergency access */ + userId: string; + /** traces a unique emergency request */ + emergencyAccessId: string; +}; @Component({ selector: "emergency-access-confirm", templateUrl: "emergency-access-confirm.component.html", }) export class EmergencyAccessConfirmComponent implements OnInit { - @Input() name: string; - @Input() userId: string; - @Input() emergencyAccessId: string; - @Input() formPromise: Promise; - @Output() onConfirmed = new EventEmitter(); - - dontAskAgain = false; loading = true; fingerprint: string; + confirmForm = this.formBuilder.group({ + dontAskAgain: [false], + }); constructor( + @Inject(DIALOG_DATA) protected params: EmergencyAccessConfirmDialogData, + private formBuilder: FormBuilder, private apiService: ApiService, private cryptoService: CryptoService, private stateService: StateService, private logService: LogService, + private dialogRef: DialogRef, ) {} async ngOnInit() { try { - const publicKeyResponse = await this.apiService.getUserPublicKey(this.userId); + const publicKeyResponse = await this.apiService.getUserPublicKey(this.params.userId); if (publicKeyResponse != null) { const publicKey = Utils.fromB64ToArray(publicKeyResponse.publicKey); - const fingerprint = await this.cryptoService.getFingerprint(this.userId, publicKey); + const fingerprint = await this.cryptoService.getFingerprint(this.params.userId, publicKey); if (fingerprint != null) { this.fingerprint = fingerprint.join("-"); } @@ -44,19 +57,33 @@ export class EmergencyAccessConfirmComponent implements OnInit { this.loading = false; } - async submit() { + submit = async () => { if (this.loading) { return; } - if (this.dontAskAgain) { + if (this.confirmForm.get("dontAskAgain").value) { await this.stateService.setAutoConfirmFingerprints(true); } try { - this.onConfirmed.emit(); + this.dialogRef.close(EmergencyAccessConfirmDialogResult.Confirmed); } catch (e) { this.logService.error(e); } + }; + /** + * Strongly typed helper to open a EmergencyAccessConfirmComponent + * @param dialogService Instance of the dialog service that will be used to open the dialog + * @param config Configuration for the dialog + */ + static open( + dialogService: DialogService, + config: DialogConfig, + ) { + return dialogService.open( + EmergencyAccessConfirmComponent, + config, + ); } } diff --git a/apps/web/src/app/auth/settings/emergency-access/emergency-access-add-edit.component.html b/apps/web/src/app/auth/settings/emergency-access/emergency-access-add-edit.component.html index 43961d6a13c..1e61585e422 100644 --- a/apps/web/src/app/auth/settings/emergency-access/emergency-access-add-edit.component.html +++ b/apps/web/src/app/auth/settings/emergency-access/emergency-access-add-edit.component.html @@ -1,142 +1,68 @@ - + + + {{ "view" | i18n }} + {{ "viewDesc" | i18n }} + + + + {{ "takeover" | i18n }} + {{ "takeoverDesc" | i18n }} + + + + + {{ "waitTime" | i18n }} + + + + {{ "waitTimeDesc" | i18n }} + + + + + + + + + diff --git a/apps/web/src/app/auth/settings/emergency-access/emergency-access-add-edit.component.ts b/apps/web/src/app/auth/settings/emergency-access/emergency-access-add-edit.component.ts index 4aec390a56a..d99c693e73e 100644 --- a/apps/web/src/app/auth/settings/emergency-access/emergency-access-add-edit.component.ts +++ b/apps/web/src/app/auth/settings/emergency-access/emergency-access-add-edit.component.ts @@ -1,45 +1,59 @@ -import { Component, EventEmitter, Input, OnInit, Output } from "@angular/core"; +import { DialogConfig, DialogRef, DIALOG_DATA } from "@angular/cdk/dialog"; +import { Component, Inject, OnInit } from "@angular/core"; +import { FormBuilder, Validators } from "@angular/forms"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; +import { DialogService } from "@bitwarden/components"; import { EmergencyAccessService } from "../../emergency-access"; import { EmergencyAccessType } from "../../emergency-access/enums/emergency-access-type"; +export type EmergencyAccessAddEditDialogData = { + /** display name of the account requesting emergency access */ + name: string; + /** traces a unique emergency request */ + emergencyAccessId: string; + /** A boolean indicating whether the emergency access request is in read-only mode (true for view-only, false for editing). */ + readOnly: boolean; +}; + +export enum EmergencyAccessAddEditDialogResult { + Saved = "saved", + Canceled = "canceled", + Deleted = "deleted", +} @Component({ selector: "emergency-access-add-edit", templateUrl: "emergency-access-add-edit.component.html", }) export class EmergencyAccessAddEditComponent implements OnInit { - @Input() name: string; - @Input() emergencyAccessId: string; - @Output() onSaved = new EventEmitter(); - @Output() onDeleted = new EventEmitter(); - loading = true; readOnly = false; editMode = false; title: string; - email: string; type: EmergencyAccessType = EmergencyAccessType.View; - formPromise: Promise; - emergencyAccessType = EmergencyAccessType; waitTimes: { name: string; value: number }[]; - waitTime: number; + addEditForm = this.formBuilder.group({ + email: ["", [Validators.email, Validators.required]], + emergencyAccessType: [this.emergencyAccessType.View], + waitTime: [{ value: null, disabled: this.readOnly }, [Validators.required]], + }); constructor( + @Inject(DIALOG_DATA) protected params: EmergencyAccessAddEditDialogData, + private formBuilder: FormBuilder, private emergencyAccessService: EmergencyAccessService, private i18nService: I18nService, private platformUtilsService: PlatformUtilsService, private logService: LogService, + private dialogRef: DialogRef, ) {} - async ngOnInit() { - this.editMode = this.loading = this.emergencyAccessId != null; - + this.editMode = this.loading = this.params.emergencyAccessId != null; this.waitTimes = [ { name: this.i18nService.t("oneDay"), value: 1 }, { name: this.i18nService.t("days", "2"), value: 2 }, @@ -50,46 +64,72 @@ export class EmergencyAccessAddEditComponent implements OnInit { ]; if (this.editMode) { - this.editMode = true; this.title = this.i18nService.t("editEmergencyContact"); try { const emergencyAccess = await this.emergencyAccessService.getEmergencyAccess( - this.emergencyAccessId, + this.params.emergencyAccessId, ); - this.type = emergencyAccess.type; - this.waitTime = emergencyAccess.waitTimeDays; + this.addEditForm.patchValue({ + email: emergencyAccess.email, + waitTime: emergencyAccess.waitTimeDays, + emergencyAccessType: emergencyAccess.type, + }); } catch (e) { this.logService.error(e); } } else { this.title = this.i18nService.t("inviteEmergencyContact"); - this.waitTime = this.waitTimes[2].value; + this.addEditForm.patchValue({ waitTime: this.waitTimes[2].value }); } this.loading = false; } - async submit() { + submit = async () => { + if (this.addEditForm.invalid) { + this.addEditForm.markAllAsTouched(); + return; + } try { if (this.editMode) { - await this.emergencyAccessService.update(this.emergencyAccessId, this.type, this.waitTime); + await this.emergencyAccessService.update( + this.params.emergencyAccessId, + this.addEditForm.value.emergencyAccessType, + this.addEditForm.value.waitTime, + ); } else { - await this.emergencyAccessService.invite(this.email, this.type, this.waitTime); + await this.emergencyAccessService.invite( + this.addEditForm.value.email, + this.addEditForm.value.emergencyAccessType, + this.addEditForm.value.waitTime, + ); } - - await this.formPromise; this.platformUtilsService.showToast( "success", null, - this.i18nService.t(this.editMode ? "editedUserId" : "invitedUsers", this.name), + this.i18nService.t(this.editMode ? "editedUserId" : "invitedUsers", this.params.name), ); - this.onSaved.emit(); + this.dialogRef.close(EmergencyAccessAddEditDialogResult.Saved); } catch (e) { this.logService.error(e); } - } + }; - async delete() { - this.onDeleted.emit(); - } + delete = async () => { + this.dialogRef.close(EmergencyAccessAddEditDialogResult.Deleted); + }; + /** + * Strongly typed helper to open a EmergencyAccessAddEditComponent + * @param dialogService Instance of the dialog service that will be used to open the dialog + * @param config Configuration for the dialog + */ + static open = ( + dialogService: DialogService, + config: DialogConfig, + ) => { + return dialogService.open( + EmergencyAccessAddEditComponent, + config, + ); + }; } diff --git a/apps/web/src/app/auth/settings/emergency-access/emergency-access.component.html b/apps/web/src/app/auth/settings/emergency-access/emergency-access.component.html index 27bfc673ec4..41c62db6095 100644 --- a/apps/web/src/app/auth/settings/emergency-access/emergency-access.component.html +++ b/apps/web/src/app/auth/settings/emergency-access/emergency-access.component.html @@ -1,264 +1,276 @@ - -

- {{ "emergencyAccessDesc" | i18n }} - - {{ "learnMore" | i18n }}. - -

- -

- {{ "warning" | i18n }}: {{ "emergencyAccessOwnerWarning" | i18n }} -

- -