1
0
mirror of https://github.com/bitwarden/browser synced 2026-02-23 16:13:21 +00:00

Merge branch 'main' into ps/extension-refresh

This commit is contained in:
Will Martin
2024-05-15 15:49:20 -04:00
committed by GitHub
475 changed files with 12286 additions and 6123 deletions

View File

@@ -1,7 +1,7 @@
{
"name": "@bitwarden/desktop",
"description": "A secure and free password manager for all of your devices.",
"version": "2024.4.3",
"version": "2024.5.0",
"keywords": [
"bitwarden",
"password",

View File

@@ -1,12 +1,13 @@
import { Component, OnInit } from "@angular/core";
import { FormBuilder } from "@angular/forms";
import { BehaviorSubject, firstValueFrom, Observable, Subject } from "rxjs";
import { BehaviorSubject, Observable, Subject, firstValueFrom } from "rxjs";
import { concatMap, debounceTime, filter, map, switchMap, takeUntil, tap } from "rxjs/operators";
import { AuthRequestServiceAbstraction } from "@bitwarden/auth/common";
import { AuthRequestServiceAbstraction, PinServiceAbstraction } from "@bitwarden/auth/common";
import { VaultTimeoutSettingsService } from "@bitwarden/common/abstractions/vault-timeout/vault-timeout-settings.service";
import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
import { PolicyType } from "@bitwarden/common/admin-console/enums";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { UserVerificationService as UserVerificationServiceAbstraction } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction";
import { AutofillSettingsServiceAbstraction } from "@bitwarden/common/autofill/services/autofill-settings.service";
import { DomainSettingsService } from "@bitwarden/common/autofill/services/domain-settings.service";
@@ -19,10 +20,15 @@ import { MessagingService } from "@bitwarden/common/platform/abstractions/messag
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
import { BiometricStateService } from "@bitwarden/common/platform/biometrics/biometric-state.service";
import { ThemeType, KeySuffixOptions } from "@bitwarden/common/platform/enums";
import { KeySuffixOptions, ThemeType } from "@bitwarden/common/platform/enums";
import { Utils } from "@bitwarden/common/platform/misc/utils";
import { ThemeStateService } from "@bitwarden/common/platform/theming/theme-state.service";
import { UserId } from "@bitwarden/common/types/guid";
import {
VaultTimeout,
VaultTimeoutOption,
VaultTimeoutStringType,
} from "@bitwarden/common/types/vault-timeout.type";
import { DialogService } from "@bitwarden/components";
import { SetPinComponent } from "../../auth/components/set-pin.component";
@@ -40,7 +46,7 @@ export class SettingsComponent implements OnInit {
protected readonly VaultTimeoutAction = VaultTimeoutAction;
showMinToTray = false;
vaultTimeoutOptions: any[];
vaultTimeoutOptions: VaultTimeoutOption[];
localeOptions: any[];
themeOptions: any[];
clearClipboardOptions: any[];
@@ -71,14 +77,14 @@ export class SettingsComponent implements OnInit {
timeout: { hours: number; minutes: number };
action: "lock" | "logOut";
}>;
previousVaultTimeout: number = null;
previousVaultTimeout: VaultTimeout = null;
userHasMasterPassword: boolean;
userHasPinSet: boolean;
form = this.formBuilder.group({
// Security
vaultTimeout: [null as number | null],
vaultTimeout: [null as VaultTimeout | null],
vaultTimeoutAction: [VaultTimeoutAction.Lock],
pin: [null as boolean | null],
biometric: false,
@@ -111,6 +117,7 @@ export class SettingsComponent implements OnInit {
private destroy$ = new Subject<void>();
constructor(
private accountService: AccountService,
private policyService: PolicyService,
private formBuilder: FormBuilder,
private i18nService: I18nService,
@@ -127,6 +134,7 @@ export class SettingsComponent implements OnInit {
private desktopSettingsService: DesktopSettingsService,
private biometricStateService: BiometricStateService,
private desktopAutofillSettingsService: DesktopAutofillSettingsService,
private pinService: PinServiceAbstraction,
private authRequestService: AuthRequestServiceAbstraction,
private logService: LogService,
private nativeMessagingManifestService: NativeMessagingManifestService,
@@ -156,24 +164,26 @@ export class SettingsComponent implements OnInit {
this.showDuckDuckGoIntegrationOption = isMac;
this.vaultTimeoutOptions = [
// { name: i18nService.t('immediately'), value: 0 },
{ name: this.i18nService.t("oneMinute"), value: 1 },
{ name: this.i18nService.t("fiveMinutes"), value: 5 },
{ name: this.i18nService.t("fifteenMinutes"), value: 15 },
{ name: this.i18nService.t("thirtyMinutes"), value: 30 },
{ name: this.i18nService.t("oneHour"), value: 60 },
{ name: this.i18nService.t("fourHours"), value: 240 },
{ name: this.i18nService.t("onIdle"), value: -4 },
{ name: this.i18nService.t("onSleep"), value: -3 },
{ name: this.i18nService.t("onIdle"), value: VaultTimeoutStringType.OnIdle },
{ name: this.i18nService.t("onSleep"), value: VaultTimeoutStringType.OnSleep },
];
if (this.platformUtilsService.getDevice() !== DeviceType.LinuxDesktop) {
this.vaultTimeoutOptions.push({ name: this.i18nService.t("onLocked"), value: -2 });
this.vaultTimeoutOptions.push({
name: this.i18nService.t("onLocked"),
value: VaultTimeoutStringType.OnLocked,
});
}
this.vaultTimeoutOptions = this.vaultTimeoutOptions.concat([
{ name: this.i18nService.t("onRestart"), value: -1 },
{ name: this.i18nService.t("never"), value: null },
{ name: this.i18nService.t("onRestart"), value: VaultTimeoutStringType.OnRestart },
{ name: this.i18nService.t("never"), value: VaultTimeoutStringType.Never },
]);
const localeOptions: any[] = [];
@@ -243,14 +253,19 @@ export class SettingsComponent implements OnInit {
}),
);
const userId = (await firstValueFrom(this.accountService.activeAccount$))?.id;
// Load initial values
const pinStatus = await this.vaultTimeoutSettingsService.isPinLockSet();
this.userHasPinSet = pinStatus !== "DISABLED";
this.userHasPinSet = await this.pinService.isPinSet(userId);
const activeAccount = await firstValueFrom(this.accountService.activeAccount$);
const initialValues = {
vaultTimeout: await this.vaultTimeoutSettingsService.getVaultTimeout(),
vaultTimeout: await firstValueFrom(
this.vaultTimeoutSettingsService.getVaultTimeoutByUserId$(activeAccount.id),
),
vaultTimeoutAction: await firstValueFrom(
this.vaultTimeoutSettingsService.vaultTimeoutAction$(),
this.vaultTimeoutSettingsService.getVaultTimeoutActionByUserId$(activeAccount.id),
),
pin: this.userHasPinSet,
biometric: await this.vaultTimeoutSettingsService.isBiometricLockSet(),
@@ -295,7 +310,9 @@ export class SettingsComponent implements OnInit {
this.refreshTimeoutSettings$
.pipe(
switchMap(() => this.vaultTimeoutSettingsService.vaultTimeoutAction$()),
switchMap(() =>
this.vaultTimeoutSettingsService.getVaultTimeoutActionByUserId$(activeAccount.id),
),
takeUntil(this.destroy$),
)
.subscribe((action) => {
@@ -353,8 +370,8 @@ export class SettingsComponent implements OnInit {
});
}
async saveVaultTimeout(newValue: number) {
if (newValue == null) {
async saveVaultTimeout(newValue: VaultTimeout) {
if (newValue === VaultTimeoutStringType.Never) {
const confirmed = await this.dialogService.openSimpleDialog({
title: { key: "warning" },
content: { key: "neverLockWarning" },
@@ -383,7 +400,10 @@ export class SettingsComponent implements OnInit {
this.previousVaultTimeout = this.form.value.vaultTimeout;
const activeAccount = await firstValueFrom(this.accountService.activeAccount$);
await this.vaultTimeoutSettingsService.setVaultTimeoutOptions(
activeAccount.id,
newValue,
this.form.value.vaultTimeoutAction,
);
@@ -414,7 +434,10 @@ export class SettingsComponent implements OnInit {
return;
}
const activeAccount = await firstValueFrom(this.accountService.activeAccount$);
await this.vaultTimeoutSettingsService.setVaultTimeoutOptions(
activeAccount.id,
this.form.value.vaultTimeout,
newValue,
);

View File

@@ -38,9 +38,11 @@ import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/pl
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
import { SystemService } from "@bitwarden/common/platform/abstractions/system.service";
import { BiometricStateService } from "@bitwarden/common/platform/biometrics/biometric-state.service";
import { clearCaches } from "@bitwarden/common/platform/misc/sequentialize";
import { StateEventRunnerService } from "@bitwarden/common/platform/state";
import { PasswordGenerationServiceAbstraction } from "@bitwarden/common/tools/generator/password";
import { UserId } from "@bitwarden/common/types/guid";
import { VaultTimeout, VaultTimeoutStringType } from "@bitwarden/common/types/vault-timeout.type";
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
import { CollectionService } from "@bitwarden/common/vault/abstractions/collection.service";
import { InternalFolderService } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction";
@@ -64,12 +66,6 @@ const BroadcasterSubscriptionId = "AppComponent";
const IdleTimeout = 60000 * 10; // 10 minutes
const SyncInterval = 6 * 60 * 60 * 1000; // 6 hours
const systemTimeoutOptions = {
onLock: -2,
onSuspend: -3,
onIdle: -4,
};
@Component({
selector: "app-root",
styles: [],
@@ -401,6 +397,8 @@ export class AppComponent implements OnInit, OnDestroy {
this.router.navigate(["/remove-password"]);
break;
case "switchAccount": {
// Clear sequentialized caches
clearCaches();
if (message.userId != null) {
await this.stateService.clearDecryptedData(message.userId);
await this.accountService.switchAccount(message.userId);
@@ -430,13 +428,13 @@ export class AppComponent implements OnInit, OnDestroy {
break;
}
case "systemSuspended":
await this.checkForSystemTimeout(systemTimeoutOptions.onSuspend);
await this.checkForSystemTimeout(VaultTimeoutStringType.OnSleep);
break;
case "systemLocked":
await this.checkForSystemTimeout(systemTimeoutOptions.onLock);
await this.checkForSystemTimeout(VaultTimeoutStringType.OnLocked);
break;
case "systemIdle":
await this.checkForSystemTimeout(systemTimeoutOptions.onIdle);
await this.checkForSystemTimeout(VaultTimeoutStringType.OnIdle);
break;
case "openLoginApproval":
if (message.notificationId != null) {
@@ -721,7 +719,7 @@ export class AppComponent implements OnInit, OnDestroy {
}
}
private async checkForSystemTimeout(timeout: number): Promise<void> {
private async checkForSystemTimeout(timeout: VaultTimeout): Promise<void> {
const accounts = await firstValueFrom(this.accountService.accounts$);
for (const userId in accounts) {
if (userId == null) {
@@ -738,9 +736,13 @@ export class AppComponent implements OnInit, OnDestroy {
}
}
private async getVaultTimeoutOptions(userId: string): Promise<[number, string]> {
const timeout = await this.stateService.getVaultTimeout({ userId: userId });
const action = await this.stateService.getVaultTimeoutAction({ userId: userId });
private async getVaultTimeoutOptions(userId: string): Promise<[VaultTimeout, string]> {
const timeout = await firstValueFrom(
this.vaultTimeoutSettingsService.getVaultTimeoutByUserId$(userId),
);
const action = await firstValueFrom(
this.vaultTimeoutSettingsService.getVaultTimeoutActionByUserId$(userId),
);
return [timeout, action];
}

View File

@@ -14,6 +14,7 @@ import {
SYSTEM_THEME_OBSERVABLE,
SafeInjectionToken,
STATE_FACTORY,
DEFAULT_VAULT_TIMEOUT,
INTRAPROCESS_MESSAGING_SUBJECT,
CLIENT_TYPE,
} from "@bitwarden/angular/services/injection-tokens";
@@ -56,9 +57,11 @@ import { GlobalStateProvider, StateProvider } from "@bitwarden/common/platform/s
// eslint-disable-next-line import/no-restricted-paths -- Implementation for memory storage
import { MemoryStorageService as MemoryStorageServiceForStateProviders } from "@bitwarden/common/platform/state/storage/memory-storage.service";
import { PasswordGenerationServiceAbstraction } from "@bitwarden/common/tools/generator/password";
import { VaultTimeoutStringType } from "@bitwarden/common/types/vault-timeout.type";
import { CipherService as CipherServiceAbstraction } from "@bitwarden/common/vault/abstractions/cipher.service";
import { DialogService } from "@bitwarden/components";
import { PinServiceAbstraction } from "../../../../../libs/auth/src/common/abstractions";
import { DesktopAutofillSettingsService } from "../../autofill/services/desktop-autofill-settings.service";
import { Account } from "../../models/account";
import { DesktopSettingsService } from "../../platform/services/desktop-settings.service";
@@ -137,6 +140,10 @@ const safeProviders: SafeProvider[] = [
provide: SUPPORTS_SECURE_STORAGE,
useValue: ELECTRON_SUPPORTS_SECURE_STORAGE,
}),
safeProvider({
provide: DEFAULT_VAULT_TIMEOUT,
useValue: VaultTimeoutStringType.OnRestart,
}),
safeProvider({
provide: I18nServiceAbstraction,
useClass: I18nRendererService,
@@ -183,6 +190,7 @@ const safeProviders: SafeProvider[] = [
provide: SystemServiceAbstraction,
useClass: SystemService,
deps: [
PinServiceAbstraction,
MessagingServiceAbstraction,
PlatformUtilsServiceAbstraction,
RELOAD_CALLBACK,
@@ -250,6 +258,7 @@ const safeProviders: SafeProvider[] = [
provide: CryptoServiceAbstraction,
useClass: ElectronCryptoService,
deps: [
PinServiceAbstraction,
InternalMasterPasswordServiceAbstraction,
KeyGenerationServiceAbstraction,
CryptoFunctionServiceAbstraction,

View File

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

View File

@@ -6,7 +6,7 @@ import { of } from "rxjs";
import { LockComponent as BaseLockComponent } from "@bitwarden/angular/auth/components/lock.component";
import { I18nPipe } from "@bitwarden/angular/platform/pipes/i18n.pipe";
import { PinCryptoServiceAbstraction } from "@bitwarden/auth/common";
import { PinServiceAbstraction } from "@bitwarden/auth/common";
import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { VaultTimeoutSettingsService } from "@bitwarden/common/abstractions/vault-timeout/vault-timeout-settings.service";
import { VaultTimeoutService } from "@bitwarden/common/abstractions/vault-timeout/vault-timeout.service";
@@ -155,8 +155,8 @@ describe("LockComponent", () => {
useValue: mock<UserVerificationService>(),
},
{
provide: PinCryptoServiceAbstraction,
useValue: mock<PinCryptoServiceAbstraction>(),
provide: PinServiceAbstraction,
useValue: mock<PinServiceAbstraction>(),
},
{
provide: BiometricStateService,

View File

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

View File

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

View File

@@ -479,7 +479,7 @@
"message": "El tamaño máximo de archivo es de 500MB."
},
"encryptionKeyMigrationRequired": {
"message": "Encryption key migration required. Please login through the web vault to update your encryption key."
"message": "Se requiere migración de la clave de cifrado. Por favor, inicia sesión a través de la caja fuerte web para actualizar su clave de cifrado."
},
"editedFolder": {
"message": "Carpeta editada"
@@ -561,10 +561,10 @@
"message": "¡Tu nueva cuenta ha sido creada! Ahora puedes acceder."
},
"youSuccessfullyLoggedIn": {
"message": "You successfully logged in"
"message": "Has iniciado sesión correctamente"
},
"youMayCloseThisWindow": {
"message": "You may close this window"
"message": "Puedes cerrar esta ventana"
},
"masterPassSent": {
"message": "Te hemos enviado un correo electrónico con la pista de tu contraseña maestra."
@@ -801,10 +801,10 @@
"message": "Cambiar contraseña maestra"
},
"continueToWebApp": {
"message": "Continue to web app?"
"message": "¿Continuar a la aplicación web?"
},
"changeMasterPasswordOnWebConfirmation": {
"message": "You can change your master password on the Bitwarden web app."
"message": "Puedes cambiar tu contraseña maestra en la aplicación web de Bitwarden."
},
"fingerprintPhrase": {
"message": "Frase de huella digital",
@@ -1090,7 +1090,7 @@
"message": "1GB de espacio en disco cifrado."
},
"premiumSignUpTwoStepOptions": {
"message": "Proprietary two-step login options such as YubiKey and Duo."
"message": "Opciones de inicio de sesión con autenticación de dos pasos propietarios como YubiKey y Duo."
},
"premiumSignUpReports": {
"message": "Higiene de contraseña, salud de la cuenta e informes de violaciones de datos para mantener tu caja fuerte segura."
@@ -1402,7 +1402,7 @@
"message": "Código PIN inválido."
},
"tooManyInvalidPinEntryAttemptsLoggingOut": {
"message": "Too many invalid PIN entry attempts. Logging out."
"message": "Demasiados intentos de entrada de PIN no válidos. Cerrando sesión."
},
"unlockWithWindowsHello": {
"message": "Desbloquear con Windows Hello"
@@ -1553,11 +1553,11 @@
"description": "Used as a card title description on the set password page to explain why the user is there"
},
"orgRequiresYouToSetPassword": {
"message": "Your organization requires you to set a master password.",
"message": "Tu organización requiere que establezcas una contraseña maestra.",
"description": "Used as a card title description on the set password page to explain why the user is there"
},
"verificationRequired": {
"message": "Verification required",
"message": "Verificación requerida",
"description": "Default title for the user verification dialog."
},
"currentMasterPass": {
@@ -1633,10 +1633,10 @@
"message": "La integración con el navegador no está soportada"
},
"browserIntegrationErrorTitle": {
"message": "Error enabling browser integration"
"message": "Error al habilitar la integración del navegador"
},
"browserIntegrationErrorDesc": {
"message": "An error has occurred while enabling browser integration."
"message": "Se ha producido un error mientras se habilitaba la integración del navegador."
},
"browserIntegrationMasOnlyDesc": {
"message": "Por desgracia la integración del navegador sólo está soportada por ahora en la versión de la Mac App Store."
@@ -1654,7 +1654,7 @@
"message": "Requiere una capa adicional de seguridad mediante el solicitar la frase de validación de huella dactilar al establecer un enlace entre el escritorio y el navegador. Cuando se activa, requiere intervención del usuario y verificación cada vez que se establece una conexión."
},
"enableHardwareAcceleration": {
"message": "Use hardware acceleration"
"message": "Utilizar aceleración de hardware"
},
"enableHardwareAccelerationDesc": {
"message": "By default this setting is ON. Turn OFF only if you experience graphical issues. Restart is required."
@@ -1898,16 +1898,16 @@
"message": "Su contraseña maestra no cumple con una o más de las políticas de su organización. Para acceder a la caja fuerte, debe actualizar su contraseña maestra ahora. Proceder le desconectará de su sesión actual, requiriendo que vuelva a iniciar sesión. Las sesiones activas en otros dispositivos pueden seguir estando activas durante hasta una hora."
},
"tryAgain": {
"message": "Try again"
"message": "Intentar de nuevo"
},
"verificationRequiredForActionSetPinToContinue": {
"message": "Verification required for this action. Set a PIN to continue."
},
"setPin": {
"message": "Set PIN"
"message": "Establecer PIN"
},
"verifyWithBiometrics": {
"message": "Verify with biometrics"
"message": "Verificar biométricamente"
},
"awaitingConfirmation": {
"message": "Awaiting confirmation"

View File

@@ -2698,7 +2698,7 @@
"description": "Label indicating the most common import formats"
},
"success": {
"message": "Success"
"message": "Успех"
},
"troubleshooting": {
"message": "Решавање проблема"

View File

@@ -4,7 +4,6 @@ import {
} from "@bitwarden/common/platform/models/domain/account";
export class AccountSettings extends BaseAccountSettings {
vaultTimeout = -1; // On Restart
dismissedBiometricRequirePasswordOnStartCallout?: boolean;
}

View File

@@ -1,12 +1,12 @@
{
"name": "@bitwarden/desktop",
"version": "2024.4.3",
"version": "2024.5.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@bitwarden/desktop",
"version": "2024.4.3",
"version": "2024.5.0",
"license": "GPL-3.0",
"dependencies": {
"@bitwarden/desktop-native": "file:../desktop_native",

View File

@@ -2,7 +2,7 @@
"name": "@bitwarden/desktop",
"productName": "Bitwarden",
"description": "A secure and free password manager for all of your devices.",
"version": "2024.4.3",
"version": "2024.5.0",
"author": "Bitwarden Inc. <hello@bitwarden.com> (https://bitwarden.com)",
"homepage": "https://bitwarden.com",
"license": "GPL-3.0",

View File

@@ -1,6 +1,7 @@
import { FakeStateProvider } from "@bitwarden/common/../spec/fake-state-provider";
import { mock } from "jest-mock-extended";
import { PinServiceAbstraction } from "@bitwarden/auth/common";
import { KdfConfigService } from "@bitwarden/common/auth/abstractions/kdf-config.service";
import { FakeMasterPasswordService } from "@bitwarden/common/auth/services/master-password/fake-master-password.service";
import { CryptoFunctionService } from "@bitwarden/common/platform/abstractions/crypto-function.service";
@@ -26,6 +27,7 @@ import { ElectronCryptoService } from "./electron-crypto.service";
describe("electronCryptoService", () => {
let sut: ElectronCryptoService;
const pinService = mock<PinServiceAbstraction>();
const keyGenerationService = mock<KeyGenerationService>();
const cryptoFunctionService = mock<CryptoFunctionService>();
const encryptService = mock<EncryptService>();
@@ -46,6 +48,7 @@ describe("electronCryptoService", () => {
stateProvider = new FakeStateProvider(accountService);
sut = new ElectronCryptoService(
pinService,
masterPasswordService,
keyGenerationService,
cryptoFunctionService,

View File

@@ -1,5 +1,6 @@
import { firstValueFrom } from "rxjs";
import { PinServiceAbstraction } from "@bitwarden/auth/common";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { KdfConfigService } from "@bitwarden/common/auth/abstractions/kdf-config.service";
import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/auth/abstractions/master-password.service.abstraction";
@@ -22,6 +23,7 @@ import { UserKey, MasterKey } from "@bitwarden/common/types/key";
export class ElectronCryptoService extends CryptoService {
constructor(
pinService: PinServiceAbstraction,
masterPasswordService: InternalMasterPasswordServiceAbstraction,
keyGenerationService: KeyGenerationService,
cryptoFunctionService: CryptoFunctionService,
@@ -35,6 +37,7 @@ export class ElectronCryptoService extends CryptoService {
kdfConfigService: KdfConfigService,
) {
super(
pinService,
masterPasswordService,
keyGenerationService,
cryptoFunctionService,
@@ -89,7 +92,9 @@ export class ElectronCryptoService extends CryptoService {
if (keySuffix === KeySuffixOptions.Biometric) {
await this.migrateBiometricKeyIfNeeded(userId);
const userKey = await this.stateService.getUserKeyBiometric({ userId: userId });
return new SymmetricCryptoKey(Utils.fromB64ToArray(userKey)) as UserKey;
return userKey == null
? null
: (new SymmetricCryptoKey(Utils.fromB64ToArray(userKey)) as UserKey);
}
return await super.getKeyFromStorage(keySuffix, userId);
}
@@ -166,7 +171,9 @@ export class ElectronCryptoService extends CryptoService {
// decrypt
const masterKey = new SymmetricCryptoKey(Utils.fromB64ToArray(oldBiometricKey)) as MasterKey;
userId ??= (await firstValueFrom(this.accountService.activeAccount$))?.id;
const encUserKeyPrim = await this.stateService.getEncryptedCryptoSymmetricKey();
const encUserKeyPrim = await this.stateService.getEncryptedCryptoSymmetricKey({
userId: userId,
});
const encUserKey =
encUserKeyPrim != null
? new EncString(encUserKeyPrim)
@@ -174,7 +181,11 @@ export class ElectronCryptoService extends CryptoService {
if (!encUserKey) {
throw new Error("No user key found during biometric migration");
}
const userKey = await this.decryptUserKeyWithMasterKey(masterKey, encUserKey);
const userKey = await this.masterPasswordService.decryptUserKeyWithMasterKey(
masterKey,
encUserKey,
userId,
);
// migrate
await this.storeBiometricKey(userKey, userId);
await this.stateService.setCryptoMasterKeyBiometric(null, { userId });

View File

@@ -2,6 +2,7 @@ import { Component } from "@angular/core";
import { CollectionsComponent as BaseCollectionsComponent } from "@bitwarden/angular/admin-console/components/collections.component";
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
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";
@@ -20,6 +21,7 @@ export class CollectionsComponent extends BaseCollectionsComponent {
platformUtilsService: PlatformUtilsService,
organizationService: OrganizationService,
logService: LogService,
configService: ConfigService,
) {
super(
collectionService,
@@ -28,6 +30,7 @@ export class CollectionsComponent extends BaseCollectionsComponent {
cipherService,
organizationService,
logService,
configService,
);
}
}