Merge branch 'main' into autofill/pm-7710-avoid-re-indexing-ciphers-on-current-tab-component-and-re-setting-null-storage-values-for-popup-component
This commit is contained in:
commit
41a0e6aa27
|
@ -0,0 +1,28 @@
|
||||||
|
import { KdfConfigService as AbstractKdfConfigService } from "@bitwarden/common/auth/abstractions/kdf-config.service";
|
||||||
|
import { KdfConfigService } from "@bitwarden/common/auth/services/kdf-config.service";
|
||||||
|
|
||||||
|
import {
|
||||||
|
FactoryOptions,
|
||||||
|
CachedServices,
|
||||||
|
factory,
|
||||||
|
} from "../../../platform/background/service-factories/factory-options";
|
||||||
|
import {
|
||||||
|
StateProviderInitOptions,
|
||||||
|
stateProviderFactory,
|
||||||
|
} from "../../../platform/background/service-factories/state-provider.factory";
|
||||||
|
|
||||||
|
type KdfConfigServiceFactoryOptions = FactoryOptions;
|
||||||
|
|
||||||
|
export type KdfConfigServiceInitOptions = KdfConfigServiceFactoryOptions & StateProviderInitOptions;
|
||||||
|
|
||||||
|
export function kdfConfigServiceFactory(
|
||||||
|
cache: { kdfConfigService?: AbstractKdfConfigService } & CachedServices,
|
||||||
|
opts: KdfConfigServiceInitOptions,
|
||||||
|
): Promise<AbstractKdfConfigService> {
|
||||||
|
return factory(
|
||||||
|
cache,
|
||||||
|
"kdfConfigService",
|
||||||
|
opts,
|
||||||
|
async () => new KdfConfigService(await stateProviderFactory(cache, opts)),
|
||||||
|
);
|
||||||
|
}
|
|
@ -68,6 +68,7 @@ import {
|
||||||
deviceTrustServiceFactory,
|
deviceTrustServiceFactory,
|
||||||
DeviceTrustServiceInitOptions,
|
DeviceTrustServiceInitOptions,
|
||||||
} from "./device-trust-service.factory";
|
} from "./device-trust-service.factory";
|
||||||
|
import { kdfConfigServiceFactory, KdfConfigServiceInitOptions } from "./kdf-config-service.factory";
|
||||||
import {
|
import {
|
||||||
keyConnectorServiceFactory,
|
keyConnectorServiceFactory,
|
||||||
KeyConnectorServiceInitOptions,
|
KeyConnectorServiceInitOptions,
|
||||||
|
@ -106,7 +107,8 @@ export type LoginStrategyServiceInitOptions = LoginStrategyServiceFactoryOptions
|
||||||
AuthRequestServiceInitOptions &
|
AuthRequestServiceInitOptions &
|
||||||
UserDecryptionOptionsServiceInitOptions &
|
UserDecryptionOptionsServiceInitOptions &
|
||||||
GlobalStateProviderInitOptions &
|
GlobalStateProviderInitOptions &
|
||||||
BillingAccountProfileStateServiceInitOptions;
|
BillingAccountProfileStateServiceInitOptions &
|
||||||
|
KdfConfigServiceInitOptions;
|
||||||
|
|
||||||
export function loginStrategyServiceFactory(
|
export function loginStrategyServiceFactory(
|
||||||
cache: { loginStrategyService?: LoginStrategyServiceAbstraction } & CachedServices,
|
cache: { loginStrategyService?: LoginStrategyServiceAbstraction } & CachedServices,
|
||||||
|
@ -140,6 +142,7 @@ export function loginStrategyServiceFactory(
|
||||||
await internalUserDecryptionOptionServiceFactory(cache, opts),
|
await internalUserDecryptionOptionServiceFactory(cache, opts),
|
||||||
await globalStateProviderFactory(cache, opts),
|
await globalStateProviderFactory(cache, opts),
|
||||||
await billingAccountProfileStateServiceFactory(cache, opts),
|
await billingAccountProfileStateServiceFactory(cache, opts),
|
||||||
|
await kdfConfigServiceFactory(cache, opts),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,13 +22,16 @@ import {
|
||||||
stateServiceFactory,
|
stateServiceFactory,
|
||||||
} from "../../../platform/background/service-factories/state-service.factory";
|
} from "../../../platform/background/service-factories/state-service.factory";
|
||||||
|
|
||||||
|
import { KdfConfigServiceInitOptions, kdfConfigServiceFactory } from "./kdf-config-service.factory";
|
||||||
|
|
||||||
type PinCryptoServiceFactoryOptions = FactoryOptions;
|
type PinCryptoServiceFactoryOptions = FactoryOptions;
|
||||||
|
|
||||||
export type PinCryptoServiceInitOptions = PinCryptoServiceFactoryOptions &
|
export type PinCryptoServiceInitOptions = PinCryptoServiceFactoryOptions &
|
||||||
StateServiceInitOptions &
|
StateServiceInitOptions &
|
||||||
CryptoServiceInitOptions &
|
CryptoServiceInitOptions &
|
||||||
VaultTimeoutSettingsServiceInitOptions &
|
VaultTimeoutSettingsServiceInitOptions &
|
||||||
LogServiceInitOptions;
|
LogServiceInitOptions &
|
||||||
|
KdfConfigServiceInitOptions;
|
||||||
|
|
||||||
export function pinCryptoServiceFactory(
|
export function pinCryptoServiceFactory(
|
||||||
cache: { pinCryptoService?: PinCryptoServiceAbstraction } & CachedServices,
|
cache: { pinCryptoService?: PinCryptoServiceAbstraction } & CachedServices,
|
||||||
|
@ -44,6 +47,7 @@ export function pinCryptoServiceFactory(
|
||||||
await cryptoServiceFactory(cache, opts),
|
await cryptoServiceFactory(cache, opts),
|
||||||
await vaultTimeoutSettingsServiceFactory(cache, opts),
|
await vaultTimeoutSettingsServiceFactory(cache, opts),
|
||||||
await logServiceFactory(cache, opts),
|
await logServiceFactory(cache, opts),
|
||||||
|
await kdfConfigServiceFactory(cache, opts),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,13 @@
|
||||||
import { TwoFactorService as AbstractTwoFactorService } from "@bitwarden/common/auth/abstractions/two-factor.service";
|
import { TwoFactorService as AbstractTwoFactorService } from "@bitwarden/common/auth/abstractions/two-factor.service";
|
||||||
import { TwoFactorService } from "@bitwarden/common/auth/services/two-factor.service";
|
import { TwoFactorService } from "@bitwarden/common/auth/services/two-factor.service";
|
||||||
|
import { GlobalStateProvider } from "@bitwarden/common/platform/state";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
FactoryOptions,
|
FactoryOptions,
|
||||||
CachedServices,
|
CachedServices,
|
||||||
factory,
|
factory,
|
||||||
} from "../../../platform/background/service-factories/factory-options";
|
} from "../../../platform/background/service-factories/factory-options";
|
||||||
|
import { globalStateProviderFactory } from "../../../platform/background/service-factories/global-state-provider.factory";
|
||||||
import {
|
import {
|
||||||
I18nServiceInitOptions,
|
I18nServiceInitOptions,
|
||||||
i18nServiceFactory,
|
i18nServiceFactory,
|
||||||
|
@ -19,7 +21,8 @@ type TwoFactorServiceFactoryOptions = FactoryOptions;
|
||||||
|
|
||||||
export type TwoFactorServiceInitOptions = TwoFactorServiceFactoryOptions &
|
export type TwoFactorServiceInitOptions = TwoFactorServiceFactoryOptions &
|
||||||
I18nServiceInitOptions &
|
I18nServiceInitOptions &
|
||||||
PlatformUtilsServiceInitOptions;
|
PlatformUtilsServiceInitOptions &
|
||||||
|
GlobalStateProvider;
|
||||||
|
|
||||||
export async function twoFactorServiceFactory(
|
export async function twoFactorServiceFactory(
|
||||||
cache: { twoFactorService?: AbstractTwoFactorService } & CachedServices,
|
cache: { twoFactorService?: AbstractTwoFactorService } & CachedServices,
|
||||||
|
@ -33,6 +36,7 @@ export async function twoFactorServiceFactory(
|
||||||
new TwoFactorService(
|
new TwoFactorService(
|
||||||
await i18nServiceFactory(cache, opts),
|
await i18nServiceFactory(cache, opts),
|
||||||
await platformUtilsServiceFactory(cache, opts),
|
await platformUtilsServiceFactory(cache, opts),
|
||||||
|
await globalStateProviderFactory(cache, opts),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
service.init();
|
service.init();
|
||||||
|
|
|
@ -32,6 +32,7 @@ import {
|
||||||
} from "../../../platform/background/service-factories/state-service.factory";
|
} from "../../../platform/background/service-factories/state-service.factory";
|
||||||
|
|
||||||
import { accountServiceFactory, AccountServiceInitOptions } from "./account-service.factory";
|
import { accountServiceFactory, AccountServiceInitOptions } from "./account-service.factory";
|
||||||
|
import { KdfConfigServiceInitOptions, kdfConfigServiceFactory } from "./kdf-config-service.factory";
|
||||||
import {
|
import {
|
||||||
internalMasterPasswordServiceFactory,
|
internalMasterPasswordServiceFactory,
|
||||||
MasterPasswordServiceInitOptions,
|
MasterPasswordServiceInitOptions,
|
||||||
|
@ -59,7 +60,8 @@ export type UserVerificationServiceInitOptions = UserVerificationServiceFactoryO
|
||||||
PinCryptoServiceInitOptions &
|
PinCryptoServiceInitOptions &
|
||||||
LogServiceInitOptions &
|
LogServiceInitOptions &
|
||||||
VaultTimeoutSettingsServiceInitOptions &
|
VaultTimeoutSettingsServiceInitOptions &
|
||||||
PlatformUtilsServiceInitOptions;
|
PlatformUtilsServiceInitOptions &
|
||||||
|
KdfConfigServiceInitOptions;
|
||||||
|
|
||||||
export function userVerificationServiceFactory(
|
export function userVerificationServiceFactory(
|
||||||
cache: { userVerificationService?: AbstractUserVerificationService } & CachedServices,
|
cache: { userVerificationService?: AbstractUserVerificationService } & CachedServices,
|
||||||
|
@ -82,6 +84,7 @@ export function userVerificationServiceFactory(
|
||||||
await logServiceFactory(cache, opts),
|
await logServiceFactory(cache, opts),
|
||||||
await vaultTimeoutSettingsServiceFactory(cache, opts),
|
await vaultTimeoutSettingsServiceFactory(cache, opts),
|
||||||
await platformUtilsServiceFactory(cache, opts),
|
await platformUtilsServiceFactory(cache, opts),
|
||||||
|
await kdfConfigServiceFactory(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 { DeviceTrustServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust.service.abstraction";
|
import { DeviceTrustServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust.service.abstraction";
|
||||||
|
import { KdfConfigService } from "@bitwarden/common/auth/abstractions/kdf-config.service";
|
||||||
import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/auth/abstractions/master-password.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";
|
||||||
|
@ -66,6 +67,7 @@ export class LockComponent extends BaseLockComponent {
|
||||||
private routerService: BrowserRouterService,
|
private routerService: BrowserRouterService,
|
||||||
biometricStateService: BiometricStateService,
|
biometricStateService: BiometricStateService,
|
||||||
accountService: AccountService,
|
accountService: AccountService,
|
||||||
|
kdfConfigService: KdfConfigService,
|
||||||
) {
|
) {
|
||||||
super(
|
super(
|
||||||
masterPasswordService,
|
masterPasswordService,
|
||||||
|
@ -90,6 +92,7 @@ export class LockComponent extends BaseLockComponent {
|
||||||
pinCryptoService,
|
pinCryptoService,
|
||||||
biometricStateService,
|
biometricStateService,
|
||||||
accountService,
|
accountService,
|
||||||
|
kdfConfigService,
|
||||||
);
|
);
|
||||||
this.successRoute = "/tabs/current";
|
this.successRoute = "/tabs/current";
|
||||||
this.isInitialLockScreen = (window as any).previousPopupUrl == null;
|
this.isInitialLockScreen = (window as any).previousPopupUrl == null;
|
||||||
|
|
|
@ -2,7 +2,10 @@ import { Component } from "@angular/core";
|
||||||
import { ActivatedRoute, Router } from "@angular/router";
|
import { ActivatedRoute, Router } from "@angular/router";
|
||||||
|
|
||||||
import { TwoFactorOptionsComponent as BaseTwoFactorOptionsComponent } from "@bitwarden/angular/auth/components/two-factor-options.component";
|
import { TwoFactorOptionsComponent as BaseTwoFactorOptionsComponent } from "@bitwarden/angular/auth/components/two-factor-options.component";
|
||||||
import { TwoFactorService } from "@bitwarden/common/auth/abstractions/two-factor.service";
|
import {
|
||||||
|
TwoFactorProviderDetails,
|
||||||
|
TwoFactorService,
|
||||||
|
} from "@bitwarden/common/auth/abstractions/two-factor.service";
|
||||||
import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service";
|
import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.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";
|
||||||
|
@ -27,9 +30,9 @@ export class TwoFactorOptionsComponent extends BaseTwoFactorOptionsComponent {
|
||||||
this.navigateTo2FA();
|
this.navigateTo2FA();
|
||||||
}
|
}
|
||||||
|
|
||||||
choose(p: any) {
|
override async choose(p: TwoFactorProviderDetails) {
|
||||||
super.choose(p);
|
await super.choose(p);
|
||||||
this.twoFactorService.setSelectedProvider(p.type);
|
await this.twoFactorService.setSelectedProvider(p.type);
|
||||||
|
|
||||||
this.navigateTo2FA();
|
this.navigateTo2FA();
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,8 +3,6 @@ import { Subject, firstValueFrom, merge } from "rxjs";
|
||||||
import {
|
import {
|
||||||
PinCryptoServiceAbstraction,
|
PinCryptoServiceAbstraction,
|
||||||
PinCryptoService,
|
PinCryptoService,
|
||||||
LoginStrategyServiceAbstraction,
|
|
||||||
LoginStrategyService,
|
|
||||||
InternalUserDecryptionOptionsServiceAbstraction,
|
InternalUserDecryptionOptionsServiceAbstraction,
|
||||||
UserDecryptionOptionsService,
|
UserDecryptionOptionsService,
|
||||||
AuthRequestServiceAbstraction,
|
AuthRequestServiceAbstraction,
|
||||||
|
@ -33,11 +31,11 @@ import { AvatarService as AvatarServiceAbstraction } from "@bitwarden/common/aut
|
||||||
import { DeviceTrustServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust.service.abstraction";
|
import { DeviceTrustServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust.service.abstraction";
|
||||||
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 { KdfConfigService as kdfConfigServiceAbstraction } from "@bitwarden/common/auth/abstractions/kdf-config.service";
|
||||||
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 { 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 { UserVerificationApiServiceAbstraction } from "@bitwarden/common/auth/abstractions/user-verification/user-verification-api.service.abstraction";
|
import { UserVerificationApiServiceAbstraction } from "@bitwarden/common/auth/abstractions/user-verification/user-verification-api.service.abstraction";
|
||||||
import { UserVerificationService as UserVerificationServiceAbstraction } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction";
|
import { UserVerificationService as UserVerificationServiceAbstraction } 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";
|
||||||
|
@ -48,11 +46,11 @@ import { AvatarService } from "@bitwarden/common/auth/services/avatar.service";
|
||||||
import { DeviceTrustService } from "@bitwarden/common/auth/services/device-trust.service.implementation";
|
import { DeviceTrustService } from "@bitwarden/common/auth/services/device-trust.service.implementation";
|
||||||
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 { KdfConfigService } from "@bitwarden/common/auth/services/kdf-config.service";
|
||||||
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 { 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 { UserVerificationApiService } from "@bitwarden/common/auth/services/user-verification/user-verification-api.service";
|
import { UserVerificationApiService } from "@bitwarden/common/auth/services/user-verification/user-verification-api.service";
|
||||||
import { UserVerificationService } from "@bitwarden/common/auth/services/user-verification/user-verification.service";
|
import { UserVerificationService } from "@bitwarden/common/auth/services/user-verification/user-verification.service";
|
||||||
import {
|
import {
|
||||||
|
@ -275,7 +273,6 @@ export default class MainBackground {
|
||||||
containerService: ContainerService;
|
containerService: ContainerService;
|
||||||
auditService: AuditServiceAbstraction;
|
auditService: AuditServiceAbstraction;
|
||||||
authService: AuthServiceAbstraction;
|
authService: AuthServiceAbstraction;
|
||||||
loginStrategyService: LoginStrategyServiceAbstraction;
|
|
||||||
loginEmailService: LoginEmailServiceAbstraction;
|
loginEmailService: LoginEmailServiceAbstraction;
|
||||||
importApiService: ImportApiServiceAbstraction;
|
importApiService: ImportApiServiceAbstraction;
|
||||||
importService: ImportServiceAbstraction;
|
importService: ImportServiceAbstraction;
|
||||||
|
@ -299,7 +296,6 @@ export default class MainBackground {
|
||||||
providerService: ProviderServiceAbstraction;
|
providerService: ProviderServiceAbstraction;
|
||||||
keyConnectorService: KeyConnectorServiceAbstraction;
|
keyConnectorService: KeyConnectorServiceAbstraction;
|
||||||
userVerificationService: UserVerificationServiceAbstraction;
|
userVerificationService: UserVerificationServiceAbstraction;
|
||||||
twoFactorService: TwoFactorServiceAbstraction;
|
|
||||||
vaultFilterService: VaultFilterService;
|
vaultFilterService: VaultFilterService;
|
||||||
usernameGenerationService: UsernameGenerationServiceAbstraction;
|
usernameGenerationService: UsernameGenerationServiceAbstraction;
|
||||||
encryptService: EncryptService;
|
encryptService: EncryptService;
|
||||||
|
@ -339,6 +335,7 @@ export default class MainBackground {
|
||||||
intraprocessMessagingSubject: Subject<Message<object>>;
|
intraprocessMessagingSubject: Subject<Message<object>>;
|
||||||
userKeyInitService: UserKeyInitService;
|
userKeyInitService: UserKeyInitService;
|
||||||
scriptInjectorService: BrowserScriptInjectorService;
|
scriptInjectorService: BrowserScriptInjectorService;
|
||||||
|
kdfConfigService: kdfConfigServiceAbstraction;
|
||||||
|
|
||||||
onUpdatedRan: boolean;
|
onUpdatedRan: boolean;
|
||||||
onReplacedRan: boolean;
|
onReplacedRan: boolean;
|
||||||
|
@ -542,6 +539,9 @@ export default class MainBackground {
|
||||||
this.masterPasswordService = new MasterPasswordService(this.stateProvider);
|
this.masterPasswordService = new MasterPasswordService(this.stateProvider);
|
||||||
|
|
||||||
this.i18nService = new I18nService(BrowserApi.getUILanguage(), this.globalStateProvider);
|
this.i18nService = new I18nService(BrowserApi.getUILanguage(), this.globalStateProvider);
|
||||||
|
|
||||||
|
this.kdfConfigService = new KdfConfigService(this.stateProvider);
|
||||||
|
|
||||||
this.cryptoService = new BrowserCryptoService(
|
this.cryptoService = new BrowserCryptoService(
|
||||||
this.masterPasswordService,
|
this.masterPasswordService,
|
||||||
this.keyGenerationService,
|
this.keyGenerationService,
|
||||||
|
@ -553,6 +553,7 @@ export default class MainBackground {
|
||||||
this.accountService,
|
this.accountService,
|
||||||
this.stateProvider,
|
this.stateProvider,
|
||||||
this.biometricStateService,
|
this.biometricStateService,
|
||||||
|
this.kdfConfigService,
|
||||||
);
|
);
|
||||||
|
|
||||||
this.appIdService = new AppIdService(this.globalStateProvider);
|
this.appIdService = new AppIdService(this.globalStateProvider);
|
||||||
|
@ -607,8 +608,6 @@ export default class MainBackground {
|
||||||
this.stateService,
|
this.stateService,
|
||||||
);
|
);
|
||||||
|
|
||||||
this.twoFactorService = new TwoFactorService(this.i18nService, this.platformUtilsService);
|
|
||||||
|
|
||||||
this.userDecryptionOptionsService = new UserDecryptionOptionsService(this.stateProvider);
|
this.userDecryptionOptionsService = new UserDecryptionOptionsService(this.stateProvider);
|
||||||
|
|
||||||
this.devicesApiService = new DevicesApiServiceImplementation(this.apiService);
|
this.devicesApiService = new DevicesApiServiceImplementation(this.apiService);
|
||||||
|
@ -652,31 +651,6 @@ export default class MainBackground {
|
||||||
|
|
||||||
this.loginEmailService = new LoginEmailService(this.stateProvider);
|
this.loginEmailService = new LoginEmailService(this.stateProvider);
|
||||||
|
|
||||||
this.loginStrategyService = new LoginStrategyService(
|
|
||||||
this.accountService,
|
|
||||||
this.masterPasswordService,
|
|
||||||
this.cryptoService,
|
|
||||||
this.apiService,
|
|
||||||
this.tokenService,
|
|
||||||
this.appIdService,
|
|
||||||
this.platformUtilsService,
|
|
||||||
this.messagingService,
|
|
||||||
this.logService,
|
|
||||||
this.keyConnectorService,
|
|
||||||
this.environmentService,
|
|
||||||
this.stateService,
|
|
||||||
this.twoFactorService,
|
|
||||||
this.i18nService,
|
|
||||||
this.encryptService,
|
|
||||||
this.passwordStrengthService,
|
|
||||||
this.policyService,
|
|
||||||
this.deviceTrustService,
|
|
||||||
this.authRequestService,
|
|
||||||
this.userDecryptionOptionsService,
|
|
||||||
this.globalStateProvider,
|
|
||||||
this.billingAccountProfileStateService,
|
|
||||||
);
|
|
||||||
|
|
||||||
this.ssoLoginService = new SsoLoginService(this.stateProvider);
|
this.ssoLoginService = new SsoLoginService(this.stateProvider);
|
||||||
|
|
||||||
this.userVerificationApiService = new UserVerificationApiService(this.apiService);
|
this.userVerificationApiService = new UserVerificationApiService(this.apiService);
|
||||||
|
@ -725,6 +699,7 @@ export default class MainBackground {
|
||||||
this.cryptoService,
|
this.cryptoService,
|
||||||
this.vaultTimeoutSettingsService,
|
this.vaultTimeoutSettingsService,
|
||||||
this.logService,
|
this.logService,
|
||||||
|
this.kdfConfigService,
|
||||||
);
|
);
|
||||||
|
|
||||||
this.userVerificationService = new UserVerificationService(
|
this.userVerificationService = new UserVerificationService(
|
||||||
|
@ -739,6 +714,7 @@ export default class MainBackground {
|
||||||
this.logService,
|
this.logService,
|
||||||
this.vaultTimeoutSettingsService,
|
this.vaultTimeoutSettingsService,
|
||||||
this.platformUtilsService,
|
this.platformUtilsService,
|
||||||
|
this.kdfConfigService,
|
||||||
);
|
);
|
||||||
|
|
||||||
this.vaultFilterService = new VaultFilterService(
|
this.vaultFilterService = new VaultFilterService(
|
||||||
|
@ -861,7 +837,7 @@ export default class MainBackground {
|
||||||
this.cipherService,
|
this.cipherService,
|
||||||
this.cryptoService,
|
this.cryptoService,
|
||||||
this.cryptoFunctionService,
|
this.cryptoFunctionService,
|
||||||
this.stateService,
|
this.kdfConfigService,
|
||||||
);
|
);
|
||||||
|
|
||||||
this.organizationVaultExportService = new OrganizationVaultExportService(
|
this.organizationVaultExportService = new OrganizationVaultExportService(
|
||||||
|
@ -869,8 +845,8 @@ export default class MainBackground {
|
||||||
this.apiService,
|
this.apiService,
|
||||||
this.cryptoService,
|
this.cryptoService,
|
||||||
this.cryptoFunctionService,
|
this.cryptoFunctionService,
|
||||||
this.stateService,
|
|
||||||
this.collectionService,
|
this.collectionService,
|
||||||
|
this.kdfConfigService,
|
||||||
);
|
);
|
||||||
|
|
||||||
this.exportService = new VaultExportService(
|
this.exportService = new VaultExportService(
|
||||||
|
@ -1104,8 +1080,7 @@ export default class MainBackground {
|
||||||
this.userKeyInitService.listenForActiveUserChangesToSetUserKey();
|
this.userKeyInitService.listenForActiveUserChangesToSetUserKey();
|
||||||
|
|
||||||
await (this.i18nService as I18nService).init();
|
await (this.i18nService as I18nService).init();
|
||||||
await (this.eventUploadService as EventUploadService).init(true);
|
(this.eventUploadService as EventUploadService).init(true);
|
||||||
this.twoFactorService.init();
|
|
||||||
|
|
||||||
if (this.popupOnlyContext) {
|
if (this.popupOnlyContext) {
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -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 {
|
||||||
|
KdfConfigServiceInitOptions,
|
||||||
|
kdfConfigServiceFactory,
|
||||||
|
} from "../../../auth/background/service-factories/kdf-config-service.factory";
|
||||||
import {
|
import {
|
||||||
internalMasterPasswordServiceFactory,
|
internalMasterPasswordServiceFactory,
|
||||||
MasterPasswordServiceInitOptions,
|
MasterPasswordServiceInitOptions,
|
||||||
|
@ -18,7 +22,10 @@ import {
|
||||||
} from "../../background/service-factories/log-service.factory";
|
} from "../../background/service-factories/log-service.factory";
|
||||||
import { BrowserCryptoService } from "../../services/browser-crypto.service";
|
import { BrowserCryptoService } from "../../services/browser-crypto.service";
|
||||||
|
|
||||||
import { biometricStateServiceFactory } from "./biometric-state-service.factory";
|
import {
|
||||||
|
BiometricStateServiceInitOptions,
|
||||||
|
biometricStateServiceFactory,
|
||||||
|
} from "./biometric-state-service.factory";
|
||||||
import {
|
import {
|
||||||
cryptoFunctionServiceFactory,
|
cryptoFunctionServiceFactory,
|
||||||
CryptoFunctionServiceInitOptions,
|
CryptoFunctionServiceInitOptions,
|
||||||
|
@ -46,7 +53,9 @@ export type CryptoServiceInitOptions = CryptoServiceFactoryOptions &
|
||||||
LogServiceInitOptions &
|
LogServiceInitOptions &
|
||||||
StateServiceInitOptions &
|
StateServiceInitOptions &
|
||||||
AccountServiceInitOptions &
|
AccountServiceInitOptions &
|
||||||
StateProviderInitOptions;
|
StateProviderInitOptions &
|
||||||
|
BiometricStateServiceInitOptions &
|
||||||
|
KdfConfigServiceInitOptions;
|
||||||
|
|
||||||
export function cryptoServiceFactory(
|
export function cryptoServiceFactory(
|
||||||
cache: { cryptoService?: AbstractCryptoService } & CachedServices,
|
cache: { cryptoService?: AbstractCryptoService } & CachedServices,
|
||||||
|
@ -68,6 +77,7 @@ export function cryptoServiceFactory(
|
||||||
await accountServiceFactory(cache, opts),
|
await accountServiceFactory(cache, opts),
|
||||||
await stateProviderFactory(cache, opts),
|
await stateProviderFactory(cache, opts),
|
||||||
await biometricStateServiceFactory(cache, opts),
|
await biometricStateServiceFactory(cache, opts),
|
||||||
|
await kdfConfigServiceFactory(cache, opts),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -78,6 +78,11 @@ export default abstract class AbstractChromeStorageService
|
||||||
async save(key: string, obj: any): Promise<void> {
|
async save(key: string, obj: any): Promise<void> {
|
||||||
obj = objToStore(obj);
|
obj = objToStore(obj);
|
||||||
|
|
||||||
|
if (obj == null) {
|
||||||
|
// Safari does not support set of null values
|
||||||
|
return this.remove(key);
|
||||||
|
}
|
||||||
|
|
||||||
const keyedObj = { [key]: obj };
|
const keyedObj = { [key]: obj };
|
||||||
return new Promise<void>((resolve) => {
|
return new Promise<void>((resolve) => {
|
||||||
this.chromeStorageApi.set(keyedObj, () => {
|
this.chromeStorageApi.set(keyedObj, () => {
|
||||||
|
|
|
@ -62,6 +62,17 @@ describe("ChromeStorageApiService", () => {
|
||||||
expect.any(Function),
|
expect.any(Function),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("removes the key when the value is null", async () => {
|
||||||
|
const removeMock = chrome.storage.local.remove as jest.Mock;
|
||||||
|
removeMock.mockImplementation((key, callback) => {
|
||||||
|
delete store[key];
|
||||||
|
callback();
|
||||||
|
});
|
||||||
|
const key = "key";
|
||||||
|
await service.save(key, null);
|
||||||
|
expect(removeMock).toHaveBeenCalledWith(key, expect.any(Function));
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("get", () => {
|
describe("get", () => {
|
||||||
|
|
|
@ -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 { KdfConfigService } from "@bitwarden/common/auth/abstractions/kdf-config.service";
|
||||||
import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/auth/abstractions/master-password.service.abstraction";
|
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";
|
||||||
|
@ -28,6 +29,7 @@ export class BrowserCryptoService extends CryptoService {
|
||||||
accountService: AccountService,
|
accountService: AccountService,
|
||||||
stateProvider: StateProvider,
|
stateProvider: StateProvider,
|
||||||
private biometricStateService: BiometricStateService,
|
private biometricStateService: BiometricStateService,
|
||||||
|
kdfConfigService: KdfConfigService,
|
||||||
) {
|
) {
|
||||||
super(
|
super(
|
||||||
masterPasswordService,
|
masterPasswordService,
|
||||||
|
@ -39,6 +41,7 @@ export class BrowserCryptoService extends CryptoService {
|
||||||
stateService,
|
stateService,
|
||||||
accountService,
|
accountService,
|
||||||
stateProvider,
|
stateProvider,
|
||||||
|
kdfConfigService,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
override async hasUserKeyStored(keySuffix: KeySuffixOptions, userId?: UserId): Promise<boolean> {
|
override async hasUserKeyStored(keySuffix: KeySuffixOptions, userId?: UserId): Promise<boolean> {
|
||||||
|
|
|
@ -2,6 +2,7 @@ import { DOCUMENT } from "@angular/common";
|
||||||
import { Inject, Injectable } from "@angular/core";
|
import { Inject, Injectable } from "@angular/core";
|
||||||
|
|
||||||
import { AbstractThemingService } from "@bitwarden/angular/platform/services/theming/theming.service.abstraction";
|
import { AbstractThemingService } from "@bitwarden/angular/platform/services/theming/theming.service.abstraction";
|
||||||
|
import { TwoFactorService } from "@bitwarden/common/auth/abstractions/two-factor.service";
|
||||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||||
import { LogService as LogServiceAbstraction } from "@bitwarden/common/platform/abstractions/log.service";
|
import { LogService as LogServiceAbstraction } 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";
|
||||||
|
@ -15,6 +16,7 @@ export class InitService {
|
||||||
private platformUtilsService: PlatformUtilsService,
|
private platformUtilsService: PlatformUtilsService,
|
||||||
private i18nService: I18nService,
|
private i18nService: I18nService,
|
||||||
private stateService: StateServiceAbstraction,
|
private stateService: StateServiceAbstraction,
|
||||||
|
private twoFactorService: TwoFactorService,
|
||||||
private logService: LogServiceAbstraction,
|
private logService: LogServiceAbstraction,
|
||||||
private themingService: AbstractThemingService,
|
private themingService: AbstractThemingService,
|
||||||
@Inject(DOCUMENT) private document: Document,
|
@Inject(DOCUMENT) private document: Document,
|
||||||
|
@ -24,6 +26,7 @@ export class InitService {
|
||||||
return async () => {
|
return async () => {
|
||||||
await this.stateService.init({ runMigrations: false }); // Browser background is responsible for migrations
|
await this.stateService.init({ runMigrations: false }); // Browser background is responsible for migrations
|
||||||
await this.i18nService.init();
|
await this.i18nService.init();
|
||||||
|
this.twoFactorService.init();
|
||||||
|
|
||||||
if (!BrowserPopupUtils.inPopup(window)) {
|
if (!BrowserPopupUtils.inPopup(window)) {
|
||||||
window.document.body.classList.add("body-full");
|
window.document.body.classList.add("body-full");
|
||||||
|
|
|
@ -15,10 +15,7 @@ import {
|
||||||
INTRAPROCESS_MESSAGING_SUBJECT,
|
INTRAPROCESS_MESSAGING_SUBJECT,
|
||||||
} from "@bitwarden/angular/services/injection-tokens";
|
} from "@bitwarden/angular/services/injection-tokens";
|
||||||
import { JslibServicesModule } from "@bitwarden/angular/services/jslib-services.module";
|
import { JslibServicesModule } from "@bitwarden/angular/services/jslib-services.module";
|
||||||
import {
|
import { AuthRequestServiceAbstraction } from "@bitwarden/auth/common";
|
||||||
AuthRequestServiceAbstraction,
|
|
||||||
LoginStrategyServiceAbstraction,
|
|
||||||
} from "@bitwarden/auth/common";
|
|
||||||
import { EventCollectionService as EventCollectionServiceAbstraction } from "@bitwarden/common/abstractions/event/event-collection.service";
|
import { EventCollectionService as EventCollectionServiceAbstraction } from "@bitwarden/common/abstractions/event/event-collection.service";
|
||||||
import { NotificationsService } from "@bitwarden/common/abstractions/notifications.service";
|
import { NotificationsService } from "@bitwarden/common/abstractions/notifications.service";
|
||||||
import { SearchService as SearchServiceAbstraction } from "@bitwarden/common/abstractions/search.service";
|
import { SearchService as SearchServiceAbstraction } from "@bitwarden/common/abstractions/search.service";
|
||||||
|
@ -33,7 +30,6 @@ import { DevicesServiceAbstraction } from "@bitwarden/common/auth/abstractions/d
|
||||||
import { KeyConnectorService } from "@bitwarden/common/auth/abstractions/key-connector.service";
|
import { KeyConnectorService } from "@bitwarden/common/auth/abstractions/key-connector.service";
|
||||||
import { SsoLoginServiceAbstraction } from "@bitwarden/common/auth/abstractions/sso-login.service.abstraction";
|
import { SsoLoginServiceAbstraction } from "@bitwarden/common/auth/abstractions/sso-login.service.abstraction";
|
||||||
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 { 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 { AuthService } from "@bitwarden/common/auth/services/auth.service";
|
import { AuthService } from "@bitwarden/common/auth/services/auth.service";
|
||||||
import {
|
import {
|
||||||
|
@ -168,21 +164,11 @@ const safeProviders: SafeProvider[] = [
|
||||||
useClass: UnauthGuardService,
|
useClass: UnauthGuardService,
|
||||||
deps: [AuthServiceAbstraction, Router],
|
deps: [AuthServiceAbstraction, Router],
|
||||||
}),
|
}),
|
||||||
safeProvider({
|
|
||||||
provide: TwoFactorService,
|
|
||||||
useFactory: getBgService<TwoFactorService>("twoFactorService"),
|
|
||||||
deps: [],
|
|
||||||
}),
|
|
||||||
safeProvider({
|
safeProvider({
|
||||||
provide: AuthServiceAbstraction,
|
provide: AuthServiceAbstraction,
|
||||||
useFactory: getBgService<AuthService>("authService"),
|
useFactory: getBgService<AuthService>("authService"),
|
||||||
deps: [],
|
deps: [],
|
||||||
}),
|
}),
|
||||||
safeProvider({
|
|
||||||
provide: LoginStrategyServiceAbstraction,
|
|
||||||
useFactory: getBgService<LoginStrategyServiceAbstraction>("loginStrategyService"),
|
|
||||||
deps: [],
|
|
||||||
}),
|
|
||||||
safeProvider({
|
safeProvider({
|
||||||
provide: SsoLoginServiceAbstraction,
|
provide: SsoLoginServiceAbstraction,
|
||||||
useFactory: getBgService<SsoLoginServiceAbstraction>("ssoLoginService"),
|
useFactory: getBgService<SsoLoginServiceAbstraction>("ssoLoginService"),
|
||||||
|
|
|
@ -16,6 +16,7 @@ import { OrganizationService } from "@bitwarden/common/admin-console/abstraction
|
||||||
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 { AuthService } from "@bitwarden/common/auth/abstractions/auth.service";
|
import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service";
|
||||||
|
import { KdfConfigService } from "@bitwarden/common/auth/abstractions/kdf-config.service";
|
||||||
import { KeyConnectorService } from "@bitwarden/common/auth/abstractions/key-connector.service";
|
import { KeyConnectorService } from "@bitwarden/common/auth/abstractions/key-connector.service";
|
||||||
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";
|
||||||
|
@ -68,6 +69,7 @@ export class LoginCommand {
|
||||||
protected policyApiService: PolicyApiServiceAbstraction,
|
protected policyApiService: PolicyApiServiceAbstraction,
|
||||||
protected orgService: OrganizationService,
|
protected orgService: OrganizationService,
|
||||||
protected logoutCallback: () => Promise<void>,
|
protected logoutCallback: () => Promise<void>,
|
||||||
|
protected kdfConfigService: KdfConfigService,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async run(email: string, password: string, options: OptionValues) {
|
async run(email: string, password: string, options: OptionValues) {
|
||||||
|
@ -229,7 +231,7 @@ export class LoginCommand {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (response.requiresTwoFactor) {
|
if (response.requiresTwoFactor) {
|
||||||
const twoFactorProviders = this.twoFactorService.getSupportedProviders(null);
|
const twoFactorProviders = await this.twoFactorService.getSupportedProviders(null);
|
||||||
if (twoFactorProviders.length === 0) {
|
if (twoFactorProviders.length === 0) {
|
||||||
return Response.badRequest("No providers available for this client.");
|
return Response.badRequest("No providers available for this client.");
|
||||||
}
|
}
|
||||||
|
@ -270,7 +272,7 @@ export class LoginCommand {
|
||||||
|
|
||||||
if (
|
if (
|
||||||
twoFactorToken == null &&
|
twoFactorToken == null &&
|
||||||
response.twoFactorProviders.size > 1 &&
|
Object.keys(response.twoFactorProviders).length > 1 &&
|
||||||
selectedProvider.type === TwoFactorProviderType.Email
|
selectedProvider.type === TwoFactorProviderType.Email
|
||||||
) {
|
) {
|
||||||
const emailReq = new TwoFactorEmailRequest();
|
const emailReq = new TwoFactorEmailRequest();
|
||||||
|
@ -563,14 +565,12 @@ export class LoginCommand {
|
||||||
message: "Master Password Hint (optional):",
|
message: "Master Password Hint (optional):",
|
||||||
});
|
});
|
||||||
const masterPasswordHint = hint.input;
|
const masterPasswordHint = hint.input;
|
||||||
const kdf = await this.stateService.getKdfType();
|
const kdfConfig = await this.kdfConfigService.getKdfConfig();
|
||||||
const kdfConfig = await this.stateService.getKdfConfig();
|
|
||||||
|
|
||||||
// Create new key and hash new password
|
// Create new key and hash new password
|
||||||
const newMasterKey = await this.cryptoService.makeMasterKey(
|
const newMasterKey = await this.cryptoService.makeMasterKey(
|
||||||
masterPassword,
|
masterPassword,
|
||||||
this.email.trim().toLowerCase(),
|
this.email.trim().toLowerCase(),
|
||||||
kdf,
|
|
||||||
kdfConfig,
|
kdfConfig,
|
||||||
);
|
);
|
||||||
const newPasswordHash = await this.cryptoService.hashMasterKey(masterPassword, newMasterKey);
|
const newPasswordHash = await this.cryptoService.hashMasterKey(masterPassword, newMasterKey);
|
||||||
|
|
|
@ -3,6 +3,7 @@ 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 { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||||
|
import { KdfConfigService } from "@bitwarden/common/auth/abstractions/kdf-config.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 { 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";
|
||||||
|
@ -34,6 +35,7 @@ export class UnlockCommand {
|
||||||
private syncService: SyncService,
|
private syncService: SyncService,
|
||||||
private organizationApiService: OrganizationApiServiceAbstraction,
|
private organizationApiService: OrganizationApiServiceAbstraction,
|
||||||
private logout: () => Promise<void>,
|
private logout: () => Promise<void>,
|
||||||
|
private kdfConfigService: KdfConfigService,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async run(password: string, cmdOptions: Record<string, any>) {
|
async run(password: string, cmdOptions: Record<string, any>) {
|
||||||
|
@ -48,9 +50,8 @@ export class UnlockCommand {
|
||||||
|
|
||||||
await this.setNewSessionKey();
|
await this.setNewSessionKey();
|
||||||
const email = await this.stateService.getEmail();
|
const email = await this.stateService.getEmail();
|
||||||
const kdf = await this.stateService.getKdfType();
|
const kdfConfig = await this.kdfConfigService.getKdfConfig();
|
||||||
const kdfConfig = await this.stateService.getKdfConfig();
|
const masterKey = await this.cryptoService.makeMasterKey(password, email, kdfConfig);
|
||||||
const masterKey = await this.cryptoService.makeMasterKey(password, email, kdf, kdfConfig);
|
|
||||||
const userId = (await firstValueFrom(this.accountService.activeAccount$))?.id;
|
const userId = (await firstValueFrom(this.accountService.activeAccount$))?.id;
|
||||||
const storedMasterKeyHash = await firstValueFrom(
|
const storedMasterKeyHash = await firstValueFrom(
|
||||||
this.masterPasswordService.masterKeyHash$(userId),
|
this.masterPasswordService.masterKeyHash$(userId),
|
||||||
|
|
|
@ -30,12 +30,14 @@ 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 { DeviceTrustServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust.service.abstraction";
|
import { DeviceTrustServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust.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 { KdfConfigService as KdfConfigServiceAbstraction } from "@bitwarden/common/auth/abstractions/kdf-config.service";
|
||||||
import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/auth/abstractions/master-password.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";
|
||||||
import { DeviceTrustService } from "@bitwarden/common/auth/services/device-trust.service.implementation";
|
import { DeviceTrustService } from "@bitwarden/common/auth/services/device-trust.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 { KdfConfigService } from "@bitwarden/common/auth/services/kdf-config.service";
|
||||||
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 { MasterPasswordService } from "@bitwarden/common/auth/services/master-password/master-password.service";
|
||||||
import { TokenService } from "@bitwarden/common/auth/services/token.service";
|
import { TokenService } from "@bitwarden/common/auth/services/token.service";
|
||||||
|
@ -235,6 +237,7 @@ export class Main {
|
||||||
billingAccountProfileStateService: BillingAccountProfileStateService;
|
billingAccountProfileStateService: BillingAccountProfileStateService;
|
||||||
providerApiService: ProviderApiServiceAbstraction;
|
providerApiService: ProviderApiServiceAbstraction;
|
||||||
userKeyInitService: UserKeyInitService;
|
userKeyInitService: UserKeyInitService;
|
||||||
|
kdfConfigService: KdfConfigServiceAbstraction;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
let p = null;
|
let p = null;
|
||||||
|
@ -357,6 +360,8 @@ export class Main {
|
||||||
|
|
||||||
this.masterPasswordService = new MasterPasswordService(this.stateProvider);
|
this.masterPasswordService = new MasterPasswordService(this.stateProvider);
|
||||||
|
|
||||||
|
this.kdfConfigService = new KdfConfigService(this.stateProvider);
|
||||||
|
|
||||||
this.cryptoService = new CryptoService(
|
this.cryptoService = new CryptoService(
|
||||||
this.masterPasswordService,
|
this.masterPasswordService,
|
||||||
this.keyGenerationService,
|
this.keyGenerationService,
|
||||||
|
@ -367,6 +372,7 @@ export class Main {
|
||||||
this.stateService,
|
this.stateService,
|
||||||
this.accountService,
|
this.accountService,
|
||||||
this.stateProvider,
|
this.stateProvider,
|
||||||
|
this.kdfConfigService,
|
||||||
);
|
);
|
||||||
|
|
||||||
this.appIdService = new AppIdService(this.globalStateProvider);
|
this.appIdService = new AppIdService(this.globalStateProvider);
|
||||||
|
@ -449,7 +455,11 @@ export class Main {
|
||||||
this.stateProvider,
|
this.stateProvider,
|
||||||
);
|
);
|
||||||
|
|
||||||
this.twoFactorService = new TwoFactorService(this.i18nService, this.platformUtilsService);
|
this.twoFactorService = new TwoFactorService(
|
||||||
|
this.i18nService,
|
||||||
|
this.platformUtilsService,
|
||||||
|
this.globalStateProvider,
|
||||||
|
);
|
||||||
|
|
||||||
this.passwordStrengthService = new PasswordStrengthService();
|
this.passwordStrengthService = new PasswordStrengthService();
|
||||||
|
|
||||||
|
@ -512,6 +522,7 @@ export class Main {
|
||||||
this.userDecryptionOptionsService,
|
this.userDecryptionOptionsService,
|
||||||
this.globalStateProvider,
|
this.globalStateProvider,
|
||||||
this.billingAccountProfileStateService,
|
this.billingAccountProfileStateService,
|
||||||
|
this.kdfConfigService,
|
||||||
);
|
);
|
||||||
|
|
||||||
this.authService = new AuthService(
|
this.authService = new AuthService(
|
||||||
|
@ -574,6 +585,7 @@ export class Main {
|
||||||
this.cryptoService,
|
this.cryptoService,
|
||||||
this.vaultTimeoutSettingsService,
|
this.vaultTimeoutSettingsService,
|
||||||
this.logService,
|
this.logService,
|
||||||
|
this.kdfConfigService,
|
||||||
);
|
);
|
||||||
|
|
||||||
this.userVerificationService = new UserVerificationService(
|
this.userVerificationService = new UserVerificationService(
|
||||||
|
@ -588,6 +600,7 @@ export class Main {
|
||||||
this.logService,
|
this.logService,
|
||||||
this.vaultTimeoutSettingsService,
|
this.vaultTimeoutSettingsService,
|
||||||
this.platformUtilsService,
|
this.platformUtilsService,
|
||||||
|
this.kdfConfigService,
|
||||||
);
|
);
|
||||||
|
|
||||||
this.vaultTimeoutService = new VaultTimeoutService(
|
this.vaultTimeoutService = new VaultTimeoutService(
|
||||||
|
@ -654,7 +667,7 @@ export class Main {
|
||||||
this.cipherService,
|
this.cipherService,
|
||||||
this.cryptoService,
|
this.cryptoService,
|
||||||
this.cryptoFunctionService,
|
this.cryptoFunctionService,
|
||||||
this.stateService,
|
this.kdfConfigService,
|
||||||
);
|
);
|
||||||
|
|
||||||
this.organizationExportService = new OrganizationVaultExportService(
|
this.organizationExportService = new OrganizationVaultExportService(
|
||||||
|
@ -662,8 +675,8 @@ export class Main {
|
||||||
this.apiService,
|
this.apiService,
|
||||||
this.cryptoService,
|
this.cryptoService,
|
||||||
this.cryptoFunctionService,
|
this.cryptoFunctionService,
|
||||||
this.stateService,
|
|
||||||
this.collectionService,
|
this.collectionService,
|
||||||
|
this.kdfConfigService,
|
||||||
);
|
);
|
||||||
|
|
||||||
this.exportService = new VaultExportService(
|
this.exportService = new VaultExportService(
|
||||||
|
|
|
@ -134,6 +134,7 @@ export class ServeCommand {
|
||||||
this.main.syncService,
|
this.main.syncService,
|
||||||
this.main.organizationApiService,
|
this.main.organizationApiService,
|
||||||
async () => await this.main.logout(),
|
async () => await this.main.logout(),
|
||||||
|
this.main.kdfConfigService,
|
||||||
);
|
);
|
||||||
|
|
||||||
this.sendCreateCommand = new SendCreateCommand(
|
this.sendCreateCommand = new SendCreateCommand(
|
||||||
|
|
|
@ -156,6 +156,7 @@ export class Program {
|
||||||
this.main.policyApiService,
|
this.main.policyApiService,
|
||||||
this.main.organizationService,
|
this.main.organizationService,
|
||||||
async () => await this.main.logout(),
|
async () => await this.main.logout(),
|
||||||
|
this.main.kdfConfigService,
|
||||||
);
|
);
|
||||||
const response = await command.run(email, password, options);
|
const response = await command.run(email, password, options);
|
||||||
this.processResponse(response, true);
|
this.processResponse(response, true);
|
||||||
|
@ -265,6 +266,7 @@ export class Program {
|
||||||
this.main.syncService,
|
this.main.syncService,
|
||||||
this.main.organizationApiService,
|
this.main.organizationApiService,
|
||||||
async () => await this.main.logout(),
|
async () => await this.main.logout(),
|
||||||
|
this.main.kdfConfigService,
|
||||||
);
|
);
|
||||||
const response = await command.run(password, cmd);
|
const response = await command.run(password, cmd);
|
||||||
this.processResponse(response);
|
this.processResponse(response);
|
||||||
|
@ -627,6 +629,7 @@ export class Program {
|
||||||
this.main.syncService,
|
this.main.syncService,
|
||||||
this.main.organizationApiService,
|
this.main.organizationApiService,
|
||||||
this.main.logout,
|
this.main.logout,
|
||||||
|
this.main.kdfConfigService,
|
||||||
);
|
);
|
||||||
const response = await command.run(null, null);
|
const response = await command.run(null, null);
|
||||||
if (!response.success) {
|
if (!response.success) {
|
||||||
|
|
|
@ -21,6 +21,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 { KdfConfigService as KdfConfigServiceAbstraction } from "@bitwarden/common/auth/abstractions/kdf-config.service";
|
||||||
import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/auth/abstractions/master-password.service.abstraction";
|
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";
|
||||||
|
@ -258,6 +259,7 @@ const safeProviders: SafeProvider[] = [
|
||||||
AccountServiceAbstraction,
|
AccountServiceAbstraction,
|
||||||
StateProvider,
|
StateProvider,
|
||||||
BiometricStateService,
|
BiometricStateService,
|
||||||
|
KdfConfigServiceAbstraction,
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
safeProvider({
|
safeProvider({
|
||||||
|
|
|
@ -14,6 +14,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 { DeviceTrustServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust.service.abstraction";
|
import { DeviceTrustServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust.service.abstraction";
|
||||||
|
import { KdfConfigService } from "@bitwarden/common/auth/abstractions/kdf-config.service";
|
||||||
import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/auth/abstractions/master-password.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 { FakeMasterPasswordService } from "@bitwarden/common/auth/services/master-password/fake-master-password.service";
|
||||||
|
@ -164,6 +165,10 @@ describe("LockComponent", () => {
|
||||||
provide: AccountService,
|
provide: AccountService,
|
||||||
useValue: accountService,
|
useValue: accountService,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
provide: KdfConfigService,
|
||||||
|
useValue: mock<KdfConfigService>(),
|
||||||
|
},
|
||||||
],
|
],
|
||||||
schemas: [NO_ERRORS_SCHEMA],
|
schemas: [NO_ERRORS_SCHEMA],
|
||||||
}).compileComponents();
|
}).compileComponents();
|
||||||
|
|
|
@ -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 { DeviceTrustServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust.service.abstraction";
|
import { DeviceTrustServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust.service.abstraction";
|
||||||
|
import { KdfConfigService } from "@bitwarden/common/auth/abstractions/kdf-config.service";
|
||||||
import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/auth/abstractions/master-password.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";
|
||||||
|
@ -63,6 +64,7 @@ export class LockComponent extends BaseLockComponent {
|
||||||
pinCryptoService: PinCryptoServiceAbstraction,
|
pinCryptoService: PinCryptoServiceAbstraction,
|
||||||
biometricStateService: BiometricStateService,
|
biometricStateService: BiometricStateService,
|
||||||
accountService: AccountService,
|
accountService: AccountService,
|
||||||
|
kdfConfigService: KdfConfigService,
|
||||||
) {
|
) {
|
||||||
super(
|
super(
|
||||||
masterPasswordService,
|
masterPasswordService,
|
||||||
|
@ -87,6 +89,7 @@ export class LockComponent extends BaseLockComponent {
|
||||||
pinCryptoService,
|
pinCryptoService,
|
||||||
biometricStateService,
|
biometricStateService,
|
||||||
accountService,
|
accountService,
|
||||||
|
kdfConfigService,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,7 @@ import { OrganizationUserService } from "@bitwarden/common/admin-console/abstrac
|
||||||
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 { 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";
|
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";
|
||||||
|
@ -52,6 +53,7 @@ export class SetPasswordComponent extends BaseSetPasswordComponent implements On
|
||||||
userDecryptionOptionsService: InternalUserDecryptionOptionsServiceAbstraction,
|
userDecryptionOptionsService: InternalUserDecryptionOptionsServiceAbstraction,
|
||||||
ssoLoginService: SsoLoginServiceAbstraction,
|
ssoLoginService: SsoLoginServiceAbstraction,
|
||||||
dialogService: DialogService,
|
dialogService: DialogService,
|
||||||
|
kdfConfigService: KdfConfigService,
|
||||||
) {
|
) {
|
||||||
super(
|
super(
|
||||||
accountService,
|
accountService,
|
||||||
|
@ -73,6 +75,7 @@ export class SetPasswordComponent extends BaseSetPasswordComponent implements On
|
||||||
userDecryptionOptionsService,
|
userDecryptionOptionsService,
|
||||||
ssoLoginService,
|
ssoLoginService,
|
||||||
dialogService,
|
dialogService,
|
||||||
|
kdfConfigService,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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 { KdfConfigService } from "@bitwarden/common/auth/abstractions/kdf-config.service";
|
||||||
import { FakeMasterPasswordService } from "@bitwarden/common/auth/services/master-password/fake-master-password.service";
|
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";
|
||||||
|
@ -35,6 +36,7 @@ describe("electronCryptoService", () => {
|
||||||
let accountService: FakeAccountService;
|
let accountService: FakeAccountService;
|
||||||
let stateProvider: FakeStateProvider;
|
let stateProvider: FakeStateProvider;
|
||||||
const biometricStateService = mock<BiometricStateService>();
|
const biometricStateService = mock<BiometricStateService>();
|
||||||
|
const kdfConfigService = mock<KdfConfigService>();
|
||||||
|
|
||||||
const mockUserId = "mock user id" as UserId;
|
const mockUserId = "mock user id" as UserId;
|
||||||
|
|
||||||
|
@ -54,6 +56,7 @@ describe("electronCryptoService", () => {
|
||||||
accountService,
|
accountService,
|
||||||
stateProvider,
|
stateProvider,
|
||||||
biometricStateService,
|
biometricStateService,
|
||||||
|
kdfConfigService,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -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 { KdfConfigService } from "@bitwarden/common/auth/abstractions/kdf-config.service";
|
||||||
import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/auth/abstractions/master-password.service.abstraction";
|
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";
|
||||||
|
@ -31,6 +32,7 @@ export class ElectronCryptoService extends CryptoService {
|
||||||
accountService: AccountService,
|
accountService: AccountService,
|
||||||
stateProvider: StateProvider,
|
stateProvider: StateProvider,
|
||||||
private biometricStateService: BiometricStateService,
|
private biometricStateService: BiometricStateService,
|
||||||
|
kdfConfigService: KdfConfigService,
|
||||||
) {
|
) {
|
||||||
super(
|
super(
|
||||||
masterPasswordService,
|
masterPasswordService,
|
||||||
|
@ -42,6 +44,7 @@ export class ElectronCryptoService extends CryptoService {
|
||||||
stateService,
|
stateService,
|
||||||
accountService,
|
accountService,
|
||||||
stateProvider,
|
stateProvider,
|
||||||
|
kdfConfigService,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,10 +7,15 @@ import {
|
||||||
OrganizationUserResetPasswordRequest,
|
OrganizationUserResetPasswordRequest,
|
||||||
OrganizationUserResetPasswordWithIdRequest,
|
OrganizationUserResetPasswordWithIdRequest,
|
||||||
} from "@bitwarden/common/admin-console/abstractions/organization-user/requests";
|
} from "@bitwarden/common/admin-console/abstractions/organization-user/requests";
|
||||||
import { KdfConfig } from "@bitwarden/common/auth/models/domain/kdf-config";
|
import {
|
||||||
|
Argon2KdfConfig,
|
||||||
|
KdfConfig,
|
||||||
|
PBKDF2KdfConfig,
|
||||||
|
} from "@bitwarden/common/auth/models/domain/kdf-config";
|
||||||
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";
|
||||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||||
|
import { KdfType } from "@bitwarden/common/platform/enums";
|
||||||
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
||||||
import { EncryptedString, EncString } from "@bitwarden/common/platform/models/domain/enc-string";
|
import { EncryptedString, 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";
|
||||||
|
@ -90,12 +95,17 @@ export class OrganizationUserResetPasswordService {
|
||||||
const decValue = await this.cryptoService.rsaDecrypt(response.resetPasswordKey, decPrivateKey);
|
const decValue = await this.cryptoService.rsaDecrypt(response.resetPasswordKey, decPrivateKey);
|
||||||
const existingUserKey = new SymmetricCryptoKey(decValue) as UserKey;
|
const existingUserKey = new SymmetricCryptoKey(decValue) as UserKey;
|
||||||
|
|
||||||
|
// determine Kdf Algorithm
|
||||||
|
const kdfConfig: KdfConfig =
|
||||||
|
response.kdf === KdfType.PBKDF2_SHA256
|
||||||
|
? new PBKDF2KdfConfig(response.kdfIterations)
|
||||||
|
: new Argon2KdfConfig(response.kdfIterations, response.kdfMemory, response.kdfParallelism);
|
||||||
|
|
||||||
// Create new master key and hash new password
|
// Create new master key and hash new password
|
||||||
const newMasterKey = await this.cryptoService.makeMasterKey(
|
const newMasterKey = await this.cryptoService.makeMasterKey(
|
||||||
newMasterPassword,
|
newMasterPassword,
|
||||||
email.trim().toLowerCase(),
|
email.trim().toLowerCase(),
|
||||||
response.kdf,
|
kdfConfig,
|
||||||
new KdfConfig(response.kdfIterations, response.kdfMemory, response.kdfParallelism),
|
|
||||||
);
|
);
|
||||||
const newMasterKeyHash = await this.cryptoService.hashMasterKey(
|
const newMasterKeyHash = await this.cryptoService.hashMasterKey(
|
||||||
newMasterPassword,
|
newMasterPassword,
|
||||||
|
|
|
@ -3,10 +3,15 @@ import { Injectable } from "@angular/core";
|
||||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||||
import { PolicyData } from "@bitwarden/common/admin-console/models/data/policy.data";
|
import { PolicyData } from "@bitwarden/common/admin-console/models/data/policy.data";
|
||||||
import { Policy } from "@bitwarden/common/admin-console/models/domain/policy";
|
import { Policy } from "@bitwarden/common/admin-console/models/domain/policy";
|
||||||
import { KdfConfig } from "@bitwarden/common/auth/models/domain/kdf-config";
|
import {
|
||||||
|
Argon2KdfConfig,
|
||||||
|
KdfConfig,
|
||||||
|
PBKDF2KdfConfig,
|
||||||
|
} from "@bitwarden/common/auth/models/domain/kdf-config";
|
||||||
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";
|
||||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||||
|
import { KdfType } from "@bitwarden/common/platform/enums";
|
||||||
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
||||||
import { EncryptedString } from "@bitwarden/common/platform/models/domain/enc-string";
|
import { EncryptedString } 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";
|
||||||
|
@ -231,16 +236,22 @@ export class EmergencyAccessService {
|
||||||
|
|
||||||
const grantorUserKey = new SymmetricCryptoKey(grantorKeyBuffer) as UserKey;
|
const grantorUserKey = new SymmetricCryptoKey(grantorKeyBuffer) as UserKey;
|
||||||
|
|
||||||
const masterKey = await this.cryptoService.makeMasterKey(
|
let config: KdfConfig;
|
||||||
masterPassword,
|
|
||||||
email,
|
switch (takeoverResponse.kdf) {
|
||||||
takeoverResponse.kdf,
|
case KdfType.PBKDF2_SHA256:
|
||||||
new KdfConfig(
|
config = new PBKDF2KdfConfig(takeoverResponse.kdfIterations);
|
||||||
takeoverResponse.kdfIterations,
|
break;
|
||||||
takeoverResponse.kdfMemory,
|
case KdfType.Argon2id:
|
||||||
takeoverResponse.kdfParallelism,
|
config = new Argon2KdfConfig(
|
||||||
),
|
takeoverResponse.kdfIterations,
|
||||||
);
|
takeoverResponse.kdfMemory,
|
||||||
|
takeoverResponse.kdfParallelism,
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
const masterKey = await this.cryptoService.makeMasterKey(masterPassword, email, config);
|
||||||
const masterKeyHash = await this.cryptoService.hashMasterKey(masterPassword, masterKey);
|
const masterKeyHash = await this.cryptoService.hashMasterKey(masterPassword, masterKey);
|
||||||
|
|
||||||
const encKey = await this.cryptoService.encryptUserKeyWithMasterKey(masterKey, grantorUserKey);
|
const encKey = await this.cryptoService.encryptUserKeyWithMasterKey(masterKey, grantorUserKey);
|
||||||
|
|
|
@ -2,6 +2,7 @@ import { mock, MockProxy } from "jest-mock-extended";
|
||||||
import { BehaviorSubject } from "rxjs";
|
import { BehaviorSubject } from "rxjs";
|
||||||
|
|
||||||
import { DeviceTrustServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust.service.abstraction";
|
import { DeviceTrustServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust.service.abstraction";
|
||||||
|
import { KdfConfigService } from "@bitwarden/common/auth/abstractions/kdf-config.service";
|
||||||
import { FakeMasterPasswordService } from "@bitwarden/common/auth/services/master-password/fake-master-password.service";
|
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";
|
||||||
|
@ -47,6 +48,7 @@ describe("KeyRotationService", () => {
|
||||||
let mockEncryptService: MockProxy<EncryptService>;
|
let mockEncryptService: MockProxy<EncryptService>;
|
||||||
let mockStateService: MockProxy<StateService>;
|
let mockStateService: MockProxy<StateService>;
|
||||||
let mockConfigService: MockProxy<ConfigService>;
|
let mockConfigService: MockProxy<ConfigService>;
|
||||||
|
let mockKdfConfigService: MockProxy<KdfConfigService>;
|
||||||
|
|
||||||
const mockUserId = Utils.newGuid() as UserId;
|
const mockUserId = Utils.newGuid() as UserId;
|
||||||
const mockAccountService: FakeAccountService = mockAccountServiceWith(mockUserId);
|
const mockAccountService: FakeAccountService = mockAccountServiceWith(mockUserId);
|
||||||
|
@ -65,6 +67,7 @@ describe("KeyRotationService", () => {
|
||||||
mockEncryptService = mock<EncryptService>();
|
mockEncryptService = mock<EncryptService>();
|
||||||
mockStateService = mock<StateService>();
|
mockStateService = mock<StateService>();
|
||||||
mockConfigService = mock<ConfigService>();
|
mockConfigService = mock<ConfigService>();
|
||||||
|
mockKdfConfigService = mock<KdfConfigService>();
|
||||||
|
|
||||||
keyRotationService = new UserKeyRotationService(
|
keyRotationService = new UserKeyRotationService(
|
||||||
mockMasterPasswordService,
|
mockMasterPasswordService,
|
||||||
|
@ -80,6 +83,7 @@ describe("KeyRotationService", () => {
|
||||||
mockStateService,
|
mockStateService,
|
||||||
mockAccountService,
|
mockAccountService,
|
||||||
mockConfigService,
|
mockConfigService,
|
||||||
|
mockKdfConfigService,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -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 { DeviceTrustServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust.service.abstraction";
|
import { DeviceTrustServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust.service.abstraction";
|
||||||
|
import { KdfConfigService } from "@bitwarden/common/auth/abstractions/kdf-config.service";
|
||||||
import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/auth/abstractions/master-password.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";
|
||||||
|
@ -39,6 +40,7 @@ export class UserKeyRotationService {
|
||||||
private stateService: StateService,
|
private stateService: StateService,
|
||||||
private accountService: AccountService,
|
private accountService: AccountService,
|
||||||
private configService: ConfigService,
|
private configService: ConfigService,
|
||||||
|
private kdfConfigService: KdfConfigService,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -54,8 +56,7 @@ export class UserKeyRotationService {
|
||||||
const masterKey = await this.cryptoService.makeMasterKey(
|
const masterKey = await this.cryptoService.makeMasterKey(
|
||||||
masterPassword,
|
masterPassword,
|
||||||
await this.stateService.getEmail(),
|
await this.stateService.getEmail(),
|
||||||
await this.stateService.getKdfType(),
|
await this.kdfConfigService.getKdfConfig(),
|
||||||
await this.stateService.getKdfConfig(),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!masterKey) {
|
if (!masterKey) {
|
||||||
|
|
|
@ -2,6 +2,7 @@ import { Component, OnInit } from "@angular/core";
|
||||||
import { FormBuilder, Validators } from "@angular/forms";
|
import { FormBuilder, Validators } from "@angular/forms";
|
||||||
|
|
||||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||||
|
import { KdfConfigService } from "@bitwarden/common/auth/abstractions/kdf-config.service";
|
||||||
import { TwoFactorProviderType } from "@bitwarden/common/auth/enums/two-factor-provider-type";
|
import { TwoFactorProviderType } from "@bitwarden/common/auth/enums/two-factor-provider-type";
|
||||||
import { EmailTokenRequest } from "@bitwarden/common/auth/models/request/email-token.request";
|
import { EmailTokenRequest } from "@bitwarden/common/auth/models/request/email-token.request";
|
||||||
import { EmailRequest } from "@bitwarden/common/auth/models/request/email.request";
|
import { EmailRequest } from "@bitwarden/common/auth/models/request/email.request";
|
||||||
|
@ -37,6 +38,7 @@ export class ChangeEmailComponent implements OnInit {
|
||||||
private logService: LogService,
|
private logService: LogService,
|
||||||
private stateService: StateService,
|
private stateService: StateService,
|
||||||
private formBuilder: FormBuilder,
|
private formBuilder: FormBuilder,
|
||||||
|
private kdfConfigService: KdfConfigService,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async ngOnInit() {
|
async ngOnInit() {
|
||||||
|
@ -83,12 +85,10 @@ export class ChangeEmailComponent implements OnInit {
|
||||||
step1Value.masterPassword,
|
step1Value.masterPassword,
|
||||||
await this.cryptoService.getOrDeriveMasterKey(step1Value.masterPassword),
|
await this.cryptoService.getOrDeriveMasterKey(step1Value.masterPassword),
|
||||||
);
|
);
|
||||||
const kdf = await this.stateService.getKdfType();
|
const kdfConfig = await this.kdfConfigService.getKdfConfig();
|
||||||
const kdfConfig = await this.stateService.getKdfConfig();
|
|
||||||
const newMasterKey = await this.cryptoService.makeMasterKey(
|
const newMasterKey = await this.cryptoService.makeMasterKey(
|
||||||
step1Value.masterPassword,
|
step1Value.masterPassword,
|
||||||
newEmail,
|
newEmail,
|
||||||
kdf,
|
|
||||||
kdfConfig,
|
kdfConfig,
|
||||||
);
|
);
|
||||||
request.newMasterPasswordHash = await this.cryptoService.hashMasterKey(
|
request.newMasterPasswordHash = await this.cryptoService.hashMasterKey(
|
||||||
|
|
|
@ -5,6 +5,7 @@ import { ChangePasswordComponent as BaseChangePasswordComponent } from "@bitward
|
||||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||||
import { AuditService } from "@bitwarden/common/abstractions/audit.service";
|
import { AuditService } from "@bitwarden/common/abstractions/audit.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 { KdfConfigService } from "@bitwarden/common/auth/abstractions/kdf-config.service";
|
||||||
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 { PasswordRequest } from "@bitwarden/common/auth/models/request/password.request";
|
import { PasswordRequest } from "@bitwarden/common/auth/models/request/password.request";
|
||||||
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
|
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
|
||||||
|
@ -48,6 +49,7 @@ export class ChangePasswordComponent extends BaseChangePasswordComponent {
|
||||||
dialogService: DialogService,
|
dialogService: DialogService,
|
||||||
private userVerificationService: UserVerificationService,
|
private userVerificationService: UserVerificationService,
|
||||||
private keyRotationService: UserKeyRotationService,
|
private keyRotationService: UserKeyRotationService,
|
||||||
|
kdfConfigService: KdfConfigService,
|
||||||
) {
|
) {
|
||||||
super(
|
super(
|
||||||
i18nService,
|
i18nService,
|
||||||
|
@ -58,6 +60,7 @@ export class ChangePasswordComponent extends BaseChangePasswordComponent {
|
||||||
policyService,
|
policyService,
|
||||||
stateService,
|
stateService,
|
||||||
dialogService,
|
dialogService,
|
||||||
|
kdfConfigService,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,7 @@ import { takeUntil } from "rxjs";
|
||||||
|
|
||||||
import { ChangePasswordComponent } from "@bitwarden/angular/auth/components/change-password.component";
|
import { ChangePasswordComponent } from "@bitwarden/angular/auth/components/change-password.component";
|
||||||
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 { KdfConfigService } from "@bitwarden/common/auth/abstractions/kdf-config.service";
|
||||||
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
|
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
|
||||||
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";
|
||||||
|
@ -58,6 +59,7 @@ export class EmergencyAccessTakeoverComponent
|
||||||
private logService: LogService,
|
private logService: LogService,
|
||||||
dialogService: DialogService,
|
dialogService: DialogService,
|
||||||
private dialogRef: DialogRef<EmergencyAccessTakeoverResultType>,
|
private dialogRef: DialogRef<EmergencyAccessTakeoverResultType>,
|
||||||
|
kdfConfigService: KdfConfigService,
|
||||||
) {
|
) {
|
||||||
super(
|
super(
|
||||||
i18nService,
|
i18nService,
|
||||||
|
@ -68,6 +70,7 @@ export class EmergencyAccessTakeoverComponent
|
||||||
policyService,
|
policyService,
|
||||||
stateService,
|
stateService,
|
||||||
dialogService,
|
dialogService,
|
||||||
|
kdfConfigService,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@ import { Component, Inject } from "@angular/core";
|
||||||
import { FormGroup, FormControl, Validators } from "@angular/forms";
|
import { FormGroup, FormControl, Validators } from "@angular/forms";
|
||||||
|
|
||||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||||
|
import { KdfConfigService } from "@bitwarden/common/auth/abstractions/kdf-config.service";
|
||||||
import { KdfConfig } from "@bitwarden/common/auth/models/domain/kdf-config";
|
import { KdfConfig } from "@bitwarden/common/auth/models/domain/kdf-config";
|
||||||
import { KdfRequest } from "@bitwarden/common/models/request/kdf.request";
|
import { KdfRequest } from "@bitwarden/common/models/request/kdf.request";
|
||||||
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
|
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
|
||||||
|
@ -18,7 +19,6 @@ import { KdfType } from "@bitwarden/common/platform/enums";
|
||||||
templateUrl: "change-kdf-confirmation.component.html",
|
templateUrl: "change-kdf-confirmation.component.html",
|
||||||
})
|
})
|
||||||
export class ChangeKdfConfirmationComponent {
|
export class ChangeKdfConfirmationComponent {
|
||||||
kdf: KdfType;
|
|
||||||
kdfConfig: KdfConfig;
|
kdfConfig: KdfConfig;
|
||||||
|
|
||||||
form = new FormGroup({
|
form = new FormGroup({
|
||||||
|
@ -37,9 +37,9 @@ export class ChangeKdfConfirmationComponent {
|
||||||
private messagingService: MessagingService,
|
private messagingService: MessagingService,
|
||||||
private stateService: StateService,
|
private stateService: StateService,
|
||||||
private logService: LogService,
|
private logService: LogService,
|
||||||
@Inject(DIALOG_DATA) params: { kdf: KdfType; kdfConfig: KdfConfig },
|
private kdfConfigService: KdfConfigService,
|
||||||
|
@Inject(DIALOG_DATA) params: { kdfConfig: KdfConfig },
|
||||||
) {
|
) {
|
||||||
this.kdf = params.kdf;
|
|
||||||
this.kdfConfig = params.kdfConfig;
|
this.kdfConfig = params.kdfConfig;
|
||||||
this.masterPassword = null;
|
this.masterPassword = null;
|
||||||
}
|
}
|
||||||
|
@ -65,22 +65,24 @@ export class ChangeKdfConfirmationComponent {
|
||||||
|
|
||||||
private async makeKeyAndSaveAsync() {
|
private async makeKeyAndSaveAsync() {
|
||||||
const masterPassword = this.form.value.masterPassword;
|
const masterPassword = this.form.value.masterPassword;
|
||||||
|
|
||||||
|
// Ensure the KDF config is valid.
|
||||||
|
this.kdfConfig.validateKdfConfig();
|
||||||
|
|
||||||
const request = new KdfRequest();
|
const request = new KdfRequest();
|
||||||
request.kdf = this.kdf;
|
request.kdf = this.kdfConfig.kdfType;
|
||||||
request.kdfIterations = this.kdfConfig.iterations;
|
request.kdfIterations = this.kdfConfig.iterations;
|
||||||
request.kdfMemory = this.kdfConfig.memory;
|
if (this.kdfConfig.kdfType === KdfType.Argon2id) {
|
||||||
request.kdfParallelism = this.kdfConfig.parallelism;
|
request.kdfMemory = this.kdfConfig.memory;
|
||||||
|
request.kdfParallelism = this.kdfConfig.parallelism;
|
||||||
|
}
|
||||||
const masterKey = await this.cryptoService.getOrDeriveMasterKey(masterPassword);
|
const masterKey = await this.cryptoService.getOrDeriveMasterKey(masterPassword);
|
||||||
request.masterPasswordHash = await this.cryptoService.hashMasterKey(masterPassword, masterKey);
|
request.masterPasswordHash = await this.cryptoService.hashMasterKey(masterPassword, masterKey);
|
||||||
const email = await this.stateService.getEmail();
|
const email = await this.stateService.getEmail();
|
||||||
|
|
||||||
// Ensure the KDF config is valid.
|
|
||||||
this.cryptoService.validateKdfConfig(this.kdf, this.kdfConfig);
|
|
||||||
|
|
||||||
const newMasterKey = await this.cryptoService.makeMasterKey(
|
const newMasterKey = await this.cryptoService.makeMasterKey(
|
||||||
masterPassword,
|
masterPassword,
|
||||||
email,
|
email,
|
||||||
this.kdf,
|
|
||||||
this.kdfConfig,
|
this.kdfConfig,
|
||||||
);
|
);
|
||||||
request.newMasterPasswordHash = await this.cryptoService.hashMasterKey(
|
request.newMasterPasswordHash = await this.cryptoService.hashMasterKey(
|
||||||
|
|
|
@ -19,14 +19,14 @@
|
||||||
<select
|
<select
|
||||||
id="kdf"
|
id="kdf"
|
||||||
name="Kdf"
|
name="Kdf"
|
||||||
[(ngModel)]="kdf"
|
[(ngModel)]="kdfConfig.kdfType"
|
||||||
(ngModelChange)="onChangeKdf($event)"
|
(ngModelChange)="onChangeKdf($event)"
|
||||||
class="form-control mb-3"
|
class="form-control mb-3"
|
||||||
required
|
required
|
||||||
>
|
>
|
||||||
<option *ngFor="let o of kdfOptions" [ngValue]="o.value">{{ o.name }}</option>
|
<option *ngFor="let o of kdfOptions" [ngValue]="o.value">{{ o.name }}</option>
|
||||||
</select>
|
</select>
|
||||||
<ng-container *ngIf="kdf == kdfType.Argon2id">
|
<ng-container *ngIf="isArgon2(kdfConfig)">
|
||||||
<label for="kdfMemory">{{ "kdfMemory" | i18n }}</label>
|
<label for="kdfMemory">{{ "kdfMemory" | i18n }}</label>
|
||||||
<input
|
<input
|
||||||
id="kdfMemory"
|
id="kdfMemory"
|
||||||
|
@ -43,7 +43,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="col-6">
|
<div class="col-6">
|
||||||
<div class="form-group mb-0">
|
<div class="form-group mb-0">
|
||||||
<ng-container *ngIf="kdf == kdfType.PBKDF2_SHA256">
|
<ng-container *ngIf="isPBKDF2(kdfConfig)">
|
||||||
<label for="kdfIterations">{{ "kdfIterations" | i18n }}</label>
|
<label for="kdfIterations">{{ "kdfIterations" | i18n }}</label>
|
||||||
<a
|
<a
|
||||||
class="ml-auto"
|
class="ml-auto"
|
||||||
|
@ -65,7 +65,7 @@
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
<ng-container *ngIf="kdf == kdfType.Argon2id">
|
<ng-container *ngIf="isArgon2(kdfConfig)">
|
||||||
<label for="kdfIterations">{{ "kdfIterations" | i18n }}</label>
|
<label for="kdfIterations">{{ "kdfIterations" | i18n }}</label>
|
||||||
<input
|
<input
|
||||||
id="iterations"
|
id="iterations"
|
||||||
|
@ -92,7 +92,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<ng-container *ngIf="kdf == kdfType.PBKDF2_SHA256">
|
<ng-container *ngIf="isPBKDF2(kdfConfig)">
|
||||||
<p class="small form-text text-muted">
|
<p class="small form-text text-muted">
|
||||||
{{ "kdfIterationsDesc" | i18n: (PBKDF2_ITERATIONS.defaultValue | number) }}
|
{{ "kdfIterationsDesc" | i18n: (PBKDF2_ITERATIONS.defaultValue | number) }}
|
||||||
</p>
|
</p>
|
||||||
|
@ -100,7 +100,7 @@
|
||||||
{{ "kdfIterationsWarning" | i18n: (100000 | number) }}
|
{{ "kdfIterationsWarning" | i18n: (100000 | number) }}
|
||||||
</bit-callout>
|
</bit-callout>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
<ng-container *ngIf="kdf == kdfType.Argon2id">
|
<ng-container *ngIf="isArgon2(kdfConfig)">
|
||||||
<p class="small form-text text-muted">{{ "argon2Desc" | i18n }}</p>
|
<p class="small form-text text-muted">{{ "argon2Desc" | i18n }}</p>
|
||||||
<bit-callout type="warning"> {{ "argon2Warning" | i18n }}</bit-callout>
|
<bit-callout type="warning"> {{ "argon2Warning" | i18n }}</bit-callout>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
|
@ -1,7 +1,11 @@
|
||||||
import { Component, OnInit } from "@angular/core";
|
import { Component, OnInit } from "@angular/core";
|
||||||
|
|
||||||
import { KdfConfig } from "@bitwarden/common/auth/models/domain/kdf-config";
|
import { KdfConfigService } from "@bitwarden/common/auth/abstractions/kdf-config.service";
|
||||||
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
|
import {
|
||||||
|
Argon2KdfConfig,
|
||||||
|
KdfConfig,
|
||||||
|
PBKDF2KdfConfig,
|
||||||
|
} from "@bitwarden/common/auth/models/domain/kdf-config";
|
||||||
import {
|
import {
|
||||||
DEFAULT_KDF_CONFIG,
|
DEFAULT_KDF_CONFIG,
|
||||||
PBKDF2_ITERATIONS,
|
PBKDF2_ITERATIONS,
|
||||||
|
@ -19,7 +23,6 @@ import { ChangeKdfConfirmationComponent } from "./change-kdf-confirmation.compon
|
||||||
templateUrl: "change-kdf.component.html",
|
templateUrl: "change-kdf.component.html",
|
||||||
})
|
})
|
||||||
export class ChangeKdfComponent implements OnInit {
|
export class ChangeKdfComponent implements OnInit {
|
||||||
kdf = KdfType.PBKDF2_SHA256;
|
|
||||||
kdfConfig: KdfConfig = DEFAULT_KDF_CONFIG;
|
kdfConfig: KdfConfig = DEFAULT_KDF_CONFIG;
|
||||||
kdfType = KdfType;
|
kdfType = KdfType;
|
||||||
kdfOptions: any[] = [];
|
kdfOptions: any[] = [];
|
||||||
|
@ -31,8 +34,8 @@ export class ChangeKdfComponent implements OnInit {
|
||||||
protected ARGON2_PARALLELISM = ARGON2_PARALLELISM;
|
protected ARGON2_PARALLELISM = ARGON2_PARALLELISM;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private stateService: StateService,
|
|
||||||
private dialogService: DialogService,
|
private dialogService: DialogService,
|
||||||
|
private kdfConfigService: KdfConfigService,
|
||||||
) {
|
) {
|
||||||
this.kdfOptions = [
|
this.kdfOptions = [
|
||||||
{ name: "PBKDF2 SHA-256", value: KdfType.PBKDF2_SHA256 },
|
{ name: "PBKDF2 SHA-256", value: KdfType.PBKDF2_SHA256 },
|
||||||
|
@ -41,19 +44,22 @@ export class ChangeKdfComponent implements OnInit {
|
||||||
}
|
}
|
||||||
|
|
||||||
async ngOnInit() {
|
async ngOnInit() {
|
||||||
this.kdf = await this.stateService.getKdfType();
|
this.kdfConfig = await this.kdfConfigService.getKdfConfig();
|
||||||
this.kdfConfig = await this.stateService.getKdfConfig();
|
}
|
||||||
|
|
||||||
|
isPBKDF2(t: KdfConfig): t is PBKDF2KdfConfig {
|
||||||
|
return t instanceof PBKDF2KdfConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
isArgon2(t: KdfConfig): t is Argon2KdfConfig {
|
||||||
|
return t instanceof Argon2KdfConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
async onChangeKdf(newValue: KdfType) {
|
async onChangeKdf(newValue: KdfType) {
|
||||||
if (newValue === KdfType.PBKDF2_SHA256) {
|
if (newValue === KdfType.PBKDF2_SHA256) {
|
||||||
this.kdfConfig = new KdfConfig(PBKDF2_ITERATIONS.defaultValue);
|
this.kdfConfig = new PBKDF2KdfConfig();
|
||||||
} else if (newValue === KdfType.Argon2id) {
|
} else if (newValue === KdfType.Argon2id) {
|
||||||
this.kdfConfig = new KdfConfig(
|
this.kdfConfig = new Argon2KdfConfig();
|
||||||
ARGON2_ITERATIONS.defaultValue,
|
|
||||||
ARGON2_MEMORY.defaultValue,
|
|
||||||
ARGON2_PARALLELISM.defaultValue,
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
throw new Error("Unknown KDF type.");
|
throw new Error("Unknown KDF type.");
|
||||||
}
|
}
|
||||||
|
@ -62,7 +68,6 @@ export class ChangeKdfComponent implements OnInit {
|
||||||
async openConfirmationModal() {
|
async openConfirmationModal() {
|
||||||
this.dialogService.open(ChangeKdfConfirmationComponent, {
|
this.dialogService.open(ChangeKdfConfirmationComponent, {
|
||||||
data: {
|
data: {
|
||||||
kdf: this.kdf,
|
|
||||||
kdfConfig: this.kdfConfig,
|
kdfConfig: this.kdfConfig,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
@ -4,6 +4,7 @@ import { Router } from "@angular/router";
|
||||||
import { UpdatePasswordComponent as BaseUpdatePasswordComponent } from "@bitwarden/angular/auth/components/update-password.component";
|
import { UpdatePasswordComponent as BaseUpdatePasswordComponent } from "@bitwarden/angular/auth/components/update-password.component";
|
||||||
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 { KdfConfigService } from "@bitwarden/common/auth/abstractions/kdf-config.service";
|
||||||
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 { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
|
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
|
||||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||||
|
@ -32,6 +33,7 @@ export class UpdatePasswordComponent extends BaseUpdatePasswordComponent {
|
||||||
stateService: StateService,
|
stateService: StateService,
|
||||||
userVerificationService: UserVerificationService,
|
userVerificationService: UserVerificationService,
|
||||||
dialogService: DialogService,
|
dialogService: DialogService,
|
||||||
|
kdfConfigService: KdfConfigService,
|
||||||
) {
|
) {
|
||||||
super(
|
super(
|
||||||
router,
|
router,
|
||||||
|
@ -46,6 +48,7 @@ export class UpdatePasswordComponent extends BaseUpdatePasswordComponent {
|
||||||
userVerificationService,
|
userVerificationService,
|
||||||
logService,
|
logService,
|
||||||
dialogService,
|
dialogService,
|
||||||
|
kdfConfigService,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,6 +35,7 @@ import { EventCollectionService } from "@bitwarden/common/abstractions/event/eve
|
||||||
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 { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
|
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
|
||||||
|
import { KdfConfigService } from "@bitwarden/common/auth/abstractions/kdf-config.service";
|
||||||
import { TokenService } from "@bitwarden/common/auth/abstractions/token.service";
|
import { TokenService } from "@bitwarden/common/auth/abstractions/token.service";
|
||||||
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 { 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";
|
||||||
|
@ -184,6 +185,7 @@ export class VaultComponent implements OnInit, OnDestroy {
|
||||||
private apiService: ApiService,
|
private apiService: ApiService,
|
||||||
private userVerificationService: UserVerificationService,
|
private userVerificationService: UserVerificationService,
|
||||||
private billingAccountProfileStateService: BillingAccountProfileStateService,
|
private billingAccountProfileStateService: BillingAccountProfileStateService,
|
||||||
|
protected kdfConfigService: KdfConfigService,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async ngOnInit() {
|
async ngOnInit() {
|
||||||
|
@ -972,10 +974,10 @@ export class VaultComponent implements OnInit, OnDestroy {
|
||||||
}
|
}
|
||||||
|
|
||||||
async isLowKdfIteration() {
|
async isLowKdfIteration() {
|
||||||
const kdfType = await this.stateService.getKdfType();
|
const kdfConfig = await this.kdfConfigService.getKdfConfig();
|
||||||
const kdfOptions = await this.stateService.getKdfConfig();
|
|
||||||
return (
|
return (
|
||||||
kdfType === KdfType.PBKDF2_SHA256 && kdfOptions.iterations < PBKDF2_ITERATIONS.defaultValue
|
kdfConfig.kdfType === KdfType.PBKDF2_SHA256 &&
|
||||||
|
kdfConfig.iterations < PBKDF2_ITERATIONS.defaultValue
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,130 @@
|
||||||
|
import { SelectionModel } from "@angular/cdk/collections";
|
||||||
|
import { Directive, OnDestroy, OnInit } from "@angular/core";
|
||||||
|
import { ActivatedRoute } from "@angular/router";
|
||||||
|
import { BehaviorSubject, from, Subject, switchMap } from "rxjs";
|
||||||
|
import { first, takeUntil } from "rxjs/operators";
|
||||||
|
|
||||||
|
import { SearchService } from "@bitwarden/common/abstractions/search.service";
|
||||||
|
import { ProviderOrganizationOrganizationDetailsResponse } from "@bitwarden/common/admin-console/models/response/provider/provider-organization.response";
|
||||||
|
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||||
|
import { ValidationService } from "@bitwarden/common/platform/abstractions/validation.service";
|
||||||
|
import { DialogService, TableDataSource, ToastService } from "@bitwarden/components";
|
||||||
|
|
||||||
|
import { WebProviderService } from "../services/web-provider.service";
|
||||||
|
|
||||||
|
@Directive()
|
||||||
|
export abstract class BaseClientsComponent implements OnInit, OnDestroy {
|
||||||
|
protected destroy$ = new Subject<void>();
|
||||||
|
|
||||||
|
private searchText$ = new BehaviorSubject<string>("");
|
||||||
|
|
||||||
|
get searchText() {
|
||||||
|
return this.searchText$.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
set searchText(value: string) {
|
||||||
|
this.searchText$.next(value);
|
||||||
|
this.selection.clear();
|
||||||
|
this.dataSource.filter = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private searching = false;
|
||||||
|
protected scrolled = false;
|
||||||
|
protected pageSize = 100;
|
||||||
|
private pagedClientsCount = 0;
|
||||||
|
protected selection = new SelectionModel<string>(true, []);
|
||||||
|
|
||||||
|
protected clients: ProviderOrganizationOrganizationDetailsResponse[];
|
||||||
|
protected pagedClients: ProviderOrganizationOrganizationDetailsResponse[];
|
||||||
|
protected dataSource = new TableDataSource<ProviderOrganizationOrganizationDetailsResponse>();
|
||||||
|
|
||||||
|
abstract providerId: string;
|
||||||
|
|
||||||
|
protected constructor(
|
||||||
|
protected activatedRoute: ActivatedRoute,
|
||||||
|
protected dialogService: DialogService,
|
||||||
|
private i18nService: I18nService,
|
||||||
|
private searchService: SearchService,
|
||||||
|
private toastService: ToastService,
|
||||||
|
private validationService: ValidationService,
|
||||||
|
private webProviderService: WebProviderService,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
abstract load(): Promise<void>;
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
this.activatedRoute.queryParams
|
||||||
|
.pipe(first(), takeUntil(this.destroy$))
|
||||||
|
.subscribe((queryParams) => {
|
||||||
|
this.searchText = queryParams.search;
|
||||||
|
});
|
||||||
|
|
||||||
|
this.searchText$
|
||||||
|
.pipe(
|
||||||
|
switchMap((searchText) => from(this.searchService.isSearchable(searchText))),
|
||||||
|
takeUntil(this.destroy$),
|
||||||
|
)
|
||||||
|
.subscribe((isSearchable) => {
|
||||||
|
this.searching = isSearchable;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy() {
|
||||||
|
this.destroy$.next();
|
||||||
|
this.destroy$.complete();
|
||||||
|
}
|
||||||
|
|
||||||
|
isPaging() {
|
||||||
|
if (this.searching && this.scrolled) {
|
||||||
|
this.resetPaging();
|
||||||
|
}
|
||||||
|
return !this.searching && this.clients && this.clients.length > this.pageSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
resetPaging() {
|
||||||
|
this.pagedClients = [];
|
||||||
|
this.loadMore();
|
||||||
|
}
|
||||||
|
|
||||||
|
loadMore() {
|
||||||
|
if (!this.clients || this.clients.length <= this.pageSize) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const pagedLength = this.pagedClients.length;
|
||||||
|
let pagedSize = this.pageSize;
|
||||||
|
if (pagedLength === 0 && this.pagedClientsCount > this.pageSize) {
|
||||||
|
pagedSize = this.pagedClientsCount;
|
||||||
|
}
|
||||||
|
if (this.clients.length > pagedLength) {
|
||||||
|
this.pagedClients = this.pagedClients.concat(
|
||||||
|
this.clients.slice(pagedLength, pagedLength + pagedSize),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
this.pagedClientsCount = this.pagedClients.length;
|
||||||
|
this.scrolled = this.pagedClients.length > this.pageSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
async remove(organization: ProviderOrganizationOrganizationDetailsResponse) {
|
||||||
|
const confirmed = await this.dialogService.openSimpleDialog({
|
||||||
|
title: organization.organizationName,
|
||||||
|
content: { key: "detachOrganizationConfirmation" },
|
||||||
|
type: "warning",
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!confirmed) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await this.webProviderService.detachOrganization(this.providerId, organization.id);
|
||||||
|
this.toastService.showToast({
|
||||||
|
variant: "success",
|
||||||
|
title: null,
|
||||||
|
message: this.i18nService.t("detachedOrganization", organization.organizationName),
|
||||||
|
});
|
||||||
|
await this.load();
|
||||||
|
} catch (e) {
|
||||||
|
this.validationService.showError(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,29 +1,26 @@
|
||||||
import { Component, OnInit } from "@angular/core";
|
import { Component } from "@angular/core";
|
||||||
import { ActivatedRoute, Router } from "@angular/router";
|
import { ActivatedRoute, Router } from "@angular/router";
|
||||||
import { BehaviorSubject, Subject, firstValueFrom, from } from "rxjs";
|
import { combineLatest, firstValueFrom, from } from "rxjs";
|
||||||
import { first, switchMap, takeUntil } from "rxjs/operators";
|
import { concatMap, switchMap, takeUntil } from "rxjs/operators";
|
||||||
|
|
||||||
import { ModalService } from "@bitwarden/angular/services/modal.service";
|
|
||||||
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";
|
||||||
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 { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
|
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
|
||||||
import { ProviderService } from "@bitwarden/common/admin-console/abstractions/provider.service";
|
import { ProviderService } from "@bitwarden/common/admin-console/abstractions/provider.service";
|
||||||
import { ProviderUserType } from "@bitwarden/common/admin-console/enums";
|
import { ProviderStatusType, ProviderUserType } from "@bitwarden/common/admin-console/enums";
|
||||||
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
|
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
|
||||||
import { ProviderOrganizationOrganizationDetailsResponse } from "@bitwarden/common/admin-console/models/response/provider/provider-organization.response";
|
|
||||||
import { PlanType } from "@bitwarden/common/billing/enums";
|
import { PlanType } from "@bitwarden/common/billing/enums";
|
||||||
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 { 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 { 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 { DialogService } from "@bitwarden/components";
|
import { DialogService, ToastService } from "@bitwarden/components";
|
||||||
|
|
||||||
import { WebProviderService } from "../services/web-provider.service";
|
import { WebProviderService } from "../services/web-provider.service";
|
||||||
|
|
||||||
import { AddOrganizationComponent } from "./add-organization.component";
|
import { AddOrganizationComponent } from "./add-organization.component";
|
||||||
|
import { BaseClientsComponent } from "./base-clients.component";
|
||||||
|
|
||||||
const DisallowedPlanTypes = [
|
const DisallowedPlanTypes = [
|
||||||
PlanType.Free,
|
PlanType.Free,
|
||||||
|
@ -36,90 +33,76 @@ const DisallowedPlanTypes = [
|
||||||
@Component({
|
@Component({
|
||||||
templateUrl: "clients.component.html",
|
templateUrl: "clients.component.html",
|
||||||
})
|
})
|
||||||
// eslint-disable-next-line rxjs-angular/prefer-takeuntil
|
export class ClientsComponent extends BaseClientsComponent {
|
||||||
export class ClientsComponent implements OnInit {
|
|
||||||
providerId: string;
|
providerId: string;
|
||||||
addableOrganizations: Organization[];
|
addableOrganizations: Organization[];
|
||||||
loading = true;
|
loading = true;
|
||||||
manageOrganizations = false;
|
manageOrganizations = false;
|
||||||
showAddExisting = false;
|
showAddExisting = false;
|
||||||
|
|
||||||
clients: ProviderOrganizationOrganizationDetailsResponse[];
|
protected consolidatedBillingEnabled$ = this.configService.getFeatureFlag$(
|
||||||
pagedClients: ProviderOrganizationOrganizationDetailsResponse[];
|
|
||||||
|
|
||||||
protected didScroll = false;
|
|
||||||
protected pageSize = 100;
|
|
||||||
protected actionPromise: Promise<unknown>;
|
|
||||||
private pagedClientsCount = 0;
|
|
||||||
|
|
||||||
protected enableConsolidatedBilling$ = this.configService.getFeatureFlag$(
|
|
||||||
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 router: Router,
|
private router: Router,
|
||||||
private providerService: ProviderService,
|
private providerService: ProviderService,
|
||||||
private apiService: ApiService,
|
private apiService: ApiService,
|
||||||
private searchService: SearchService,
|
|
||||||
private platformUtilsService: PlatformUtilsService,
|
|
||||||
private i18nService: I18nService,
|
|
||||||
private validationService: ValidationService,
|
|
||||||
private webProviderService: WebProviderService,
|
|
||||||
private logService: LogService,
|
|
||||||
private modalService: ModalService,
|
|
||||||
private organizationService: OrganizationService,
|
private organizationService: OrganizationService,
|
||||||
private organizationApiService: OrganizationApiServiceAbstraction,
|
private organizationApiService: OrganizationApiServiceAbstraction,
|
||||||
private dialogService: DialogService,
|
|
||||||
private configService: ConfigService,
|
private configService: ConfigService,
|
||||||
) {}
|
activatedRoute: ActivatedRoute,
|
||||||
|
dialogService: DialogService,
|
||||||
|
i18nService: I18nService,
|
||||||
|
searchService: SearchService,
|
||||||
|
toastService: ToastService,
|
||||||
|
validationService: ValidationService,
|
||||||
|
webProviderService: WebProviderService,
|
||||||
|
) {
|
||||||
|
super(
|
||||||
|
activatedRoute,
|
||||||
|
dialogService,
|
||||||
|
i18nService,
|
||||||
|
searchService,
|
||||||
|
toastService,
|
||||||
|
validationService,
|
||||||
|
webProviderService,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
async ngOnInit() {
|
ngOnInit() {
|
||||||
const enableConsolidatedBilling = await firstValueFrom(this.enableConsolidatedBilling$);
|
this.activatedRoute.parent.params
|
||||||
|
.pipe(
|
||||||
if (enableConsolidatedBilling) {
|
switchMap((params) => {
|
||||||
await this.router.navigate(["../manage-client-organizations"], { relativeTo: this.route });
|
this.providerId = params.providerId;
|
||||||
} else {
|
return combineLatest([
|
||||||
this.route.parent.params
|
this.providerService.get(this.providerId),
|
||||||
.pipe(
|
this.consolidatedBillingEnabled$,
|
||||||
switchMap((params) => {
|
]).pipe(
|
||||||
this.providerId = params.providerId;
|
concatMap(([provider, consolidatedBillingEnabled]) => {
|
||||||
return from(this.load());
|
if (
|
||||||
}),
|
consolidatedBillingEnabled &&
|
||||||
takeUntil(this.destroy$),
|
provider.providerStatus === ProviderStatusType.Billable
|
||||||
)
|
) {
|
||||||
.subscribe();
|
return from(
|
||||||
|
this.router.navigate(["../manage-client-organizations"], {
|
||||||
this.route.queryParams.pipe(first(), takeUntil(this.destroy$)).subscribe((qParams) => {
|
relativeTo: this.activatedRoute,
|
||||||
this.searchText = qParams.search;
|
}),
|
||||||
});
|
);
|
||||||
|
} else {
|
||||||
this._searchText$
|
return from(this.load());
|
||||||
.pipe(
|
}
|
||||||
switchMap((searchText) => from(this.searchService.isSearchable(searchText))),
|
}),
|
||||||
takeUntil(this.destroy$),
|
);
|
||||||
)
|
}),
|
||||||
.subscribe((isSearchable) => {
|
takeUntil(this.destroy$),
|
||||||
this.isSearching = isSearchable;
|
)
|
||||||
});
|
.subscribe();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnDestroy() {
|
ngOnDestroy() {
|
||||||
this.destroy$.next();
|
super.ngOnDestroy();
|
||||||
this.destroy$.complete();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async load() {
|
async load() {
|
||||||
|
@ -141,37 +124,6 @@ export class ClientsComponent implements OnInit {
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
isPaging() {
|
|
||||||
const searching = this.isSearching;
|
|
||||||
if (searching && this.didScroll) {
|
|
||||||
this.resetPaging();
|
|
||||||
}
|
|
||||||
return !searching && this.clients && this.clients.length > this.pageSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
resetPaging() {
|
|
||||||
this.pagedClients = [];
|
|
||||||
this.loadMore();
|
|
||||||
}
|
|
||||||
|
|
||||||
loadMore() {
|
|
||||||
if (!this.clients || this.clients.length <= this.pageSize) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const pagedLength = this.pagedClients.length;
|
|
||||||
let pagedSize = this.pageSize;
|
|
||||||
if (pagedLength === 0 && this.pagedClientsCount > this.pageSize) {
|
|
||||||
pagedSize = this.pagedClientsCount;
|
|
||||||
}
|
|
||||||
if (this.clients.length > pagedLength) {
|
|
||||||
this.pagedClients = this.pagedClients.concat(
|
|
||||||
this.clients.slice(pagedLength, pagedLength + pagedSize),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
this.pagedClientsCount = this.pagedClients.length;
|
|
||||||
this.didScroll = this.pagedClients.length > this.pageSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
async addExistingOrganization() {
|
async addExistingOrganization() {
|
||||||
const dialogRef = AddOrganizationComponent.open(this.dialogService, {
|
const dialogRef = AddOrganizationComponent.open(this.dialogService, {
|
||||||
providerId: this.providerId,
|
providerId: this.providerId,
|
||||||
|
@ -182,33 +134,4 @@ export class ClientsComponent implements OnInit {
|
||||||
await this.load();
|
await this.load();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async remove(organization: ProviderOrganizationOrganizationDetailsResponse) {
|
|
||||||
const confirmed = await this.dialogService.openSimpleDialog({
|
|
||||||
title: organization.organizationName,
|
|
||||||
content: { key: "detachOrganizationConfirmation" },
|
|
||||||
type: "warning",
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!confirmed) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.actionPromise = this.webProviderService.detachOrganization(
|
|
||||||
this.providerId,
|
|
||||||
organization.id,
|
|
||||||
);
|
|
||||||
try {
|
|
||||||
await this.actionPromise;
|
|
||||||
this.platformUtilsService.showToast(
|
|
||||||
"success",
|
|
||||||
null,
|
|
||||||
this.i18nService.t("detachedOrganization", organization.organizationName),
|
|
||||||
);
|
|
||||||
await this.load();
|
|
||||||
} catch (e) {
|
|
||||||
this.validationService.showError(e);
|
|
||||||
}
|
|
||||||
this.actionPromise = null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,12 @@
|
||||||
<bit-nav-item
|
<bit-nav-item
|
||||||
icon="bwi-bank"
|
icon="bwi-bank"
|
||||||
[text]="'clients' | i18n"
|
[text]="'clients' | i18n"
|
||||||
[route]="(enableConsolidatedBilling$ | async) ? 'manage-client-organizations' : 'clients'"
|
[route]="
|
||||||
|
(enableConsolidatedBilling$ | async) &&
|
||||||
|
provider.providerStatus === ProviderStatusType.Billable
|
||||||
|
? 'manage-client-organizations'
|
||||||
|
: 'clients'
|
||||||
|
"
|
||||||
></bit-nav-item>
|
></bit-nav-item>
|
||||||
<bit-nav-group icon="bwi-sliders" [text]="'manage' | i18n" route="manage" *ngIf="showManageTab">
|
<bit-nav-group icon="bwi-sliders" [text]="'manage' | i18n" route="manage" *ngIf="showManageTab">
|
||||||
<bit-nav-item
|
<bit-nav-item
|
||||||
|
|
|
@ -4,6 +4,7 @@ import { ActivatedRoute, RouterModule } from "@angular/router";
|
||||||
|
|
||||||
import { JslibModule } from "@bitwarden/angular/jslib.module";
|
import { JslibModule } from "@bitwarden/angular/jslib.module";
|
||||||
import { ProviderService } from "@bitwarden/common/admin-console/abstractions/provider.service";
|
import { ProviderService } from "@bitwarden/common/admin-console/abstractions/provider.service";
|
||||||
|
import { ProviderStatusType } from "@bitwarden/common/admin-console/enums";
|
||||||
import { Provider } from "@bitwarden/common/admin-console/models/domain/provider";
|
import { Provider } from "@bitwarden/common/admin-console/models/domain/provider";
|
||||||
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";
|
||||||
|
@ -83,4 +84,6 @@ export class ProvidersLayoutComponent {
|
||||||
return "manage/events";
|
return "manage/events";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected readonly ProviderStatusType = ProviderStatusType;
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,14 +11,7 @@
|
||||||
<bit-label>
|
<bit-label>
|
||||||
{{ "assignedSeats" | i18n }}
|
{{ "assignedSeats" | i18n }}
|
||||||
</bit-label>
|
</bit-label>
|
||||||
<input
|
<input id="assignedSeats" type="number" bitInput required [(ngModel)]="assignedSeats" />
|
||||||
id="assignedSeats"
|
|
||||||
type="number"
|
|
||||||
appAutoFocus
|
|
||||||
bitInput
|
|
||||||
required
|
|
||||||
[(ngModel)]="assignedSeats"
|
|
||||||
/>
|
|
||||||
</bit-form-field>
|
</bit-form-field>
|
||||||
<ng-container *ngIf="remainingOpenSeats > 0">
|
<ng-container *ngIf="remainingOpenSeats > 0">
|
||||||
<p>
|
<p>
|
||||||
|
|
|
@ -1,21 +1,23 @@
|
||||||
import { SelectionModel } from "@angular/cdk/collections";
|
import { Component } from "@angular/core";
|
||||||
import { Component, OnDestroy, OnInit } from "@angular/core";
|
import { ActivatedRoute, Router } from "@angular/router";
|
||||||
import { ActivatedRoute } from "@angular/router";
|
import { combineLatest, firstValueFrom, from, lastValueFrom } from "rxjs";
|
||||||
import { BehaviorSubject, firstValueFrom, from, lastValueFrom, Subject } from "rxjs";
|
import { concatMap, switchMap, takeUntil } 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";
|
||||||
import { ProviderService } from "@bitwarden/common/admin-console/abstractions/provider.service";
|
import { ProviderService } from "@bitwarden/common/admin-console/abstractions/provider.service";
|
||||||
import { ProviderUserType } from "@bitwarden/common/admin-console/enums";
|
import { ProviderStatusType, ProviderUserType } from "@bitwarden/common/admin-console/enums";
|
||||||
|
import { Provider } from "@bitwarden/common/admin-console/models/domain/provider";
|
||||||
import { ProviderOrganizationOrganizationDetailsResponse } from "@bitwarden/common/admin-console/models/response/provider/provider-organization.response";
|
import { ProviderOrganizationOrganizationDetailsResponse } from "@bitwarden/common/admin-console/models/response/provider/provider-organization.response";
|
||||||
import { BillingApiServiceAbstraction as BillingApiService } from "@bitwarden/common/billing/abstractions/billilng-api.service.abstraction";
|
import { BillingApiServiceAbstraction as BillingApiService } from "@bitwarden/common/billing/abstractions/billilng-api.service.abstraction";
|
||||||
import { PlanResponse } from "@bitwarden/common/billing/models/response/plan.response";
|
import { PlanResponse } from "@bitwarden/common/billing/models/response/plan.response";
|
||||||
|
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
|
||||||
|
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 { ValidationService } from "@bitwarden/common/platform/abstractions/validation.service";
|
import { ValidationService } from "@bitwarden/common/platform/abstractions/validation.service";
|
||||||
import { DialogService, TableDataSource } from "@bitwarden/components";
|
import { DialogService, ToastService } from "@bitwarden/components";
|
||||||
|
|
||||||
|
import { BaseClientsComponent } from "../../../admin-console/providers/clients/base-clients.component";
|
||||||
import { WebProviderService } from "../../../admin-console/providers/services/web-provider.service";
|
import { WebProviderService } from "../../../admin-console/providers/services/web-provider.service";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
@ -27,127 +29,91 @@ import { ManageClientOrganizationSubscriptionComponent } from "./manage-client-o
|
||||||
@Component({
|
@Component({
|
||||||
templateUrl: "manage-client-organizations.component.html",
|
templateUrl: "manage-client-organizations.component.html",
|
||||||
})
|
})
|
||||||
|
export class ManageClientOrganizationsComponent extends BaseClientsComponent {
|
||||||
// eslint-disable-next-line rxjs-angular/prefer-takeuntil
|
|
||||||
export class ManageClientOrganizationsComponent implements OnInit, OnDestroy {
|
|
||||||
providerId: string;
|
providerId: string;
|
||||||
|
provider: Provider;
|
||||||
|
|
||||||
loading = true;
|
loading = true;
|
||||||
manageOrganizations = false;
|
manageOrganizations = false;
|
||||||
|
|
||||||
private destroy$ = new Subject<void>();
|
private consolidatedBillingEnabled$ = this.configService.getFeatureFlag$(
|
||||||
private _searchText$ = new BehaviorSubject<string>("");
|
FeatureFlag.EnableConsolidatedBilling,
|
||||||
private isSearching: boolean = false;
|
false,
|
||||||
|
);
|
||||||
|
|
||||||
get searchText() {
|
|
||||||
return this._searchText$.value;
|
|
||||||
}
|
|
||||||
|
|
||||||
set searchText(search: string) {
|
|
||||||
this._searchText$.value;
|
|
||||||
|
|
||||||
this.selection.clear();
|
|
||||||
this.dataSource.filter = search;
|
|
||||||
}
|
|
||||||
|
|
||||||
clients: ProviderOrganizationOrganizationDetailsResponse[];
|
|
||||||
pagedClients: ProviderOrganizationOrganizationDetailsResponse[];
|
|
||||||
|
|
||||||
protected didScroll = false;
|
|
||||||
protected pageSize = 100;
|
|
||||||
protected actionPromise: Promise<unknown>;
|
|
||||||
private pagedClientsCount = 0;
|
|
||||||
selection = new SelectionModel<string>(true, []);
|
|
||||||
protected dataSource = new TableDataSource<ProviderOrganizationOrganizationDetailsResponse>();
|
|
||||||
protected plans: PlanResponse[];
|
protected plans: PlanResponse[];
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private route: ActivatedRoute,
|
|
||||||
private providerService: ProviderService,
|
|
||||||
private apiService: ApiService,
|
private apiService: ApiService,
|
||||||
private searchService: SearchService,
|
|
||||||
private platformUtilsService: PlatformUtilsService,
|
|
||||||
private i18nService: I18nService,
|
|
||||||
private validationService: ValidationService,
|
|
||||||
private webProviderService: WebProviderService,
|
|
||||||
private dialogService: DialogService,
|
|
||||||
private billingApiService: BillingApiService,
|
private billingApiService: BillingApiService,
|
||||||
) {}
|
private configService: ConfigService,
|
||||||
|
private providerService: ProviderService,
|
||||||
async ngOnInit() {
|
private router: Router,
|
||||||
// eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe
|
activatedRoute: ActivatedRoute,
|
||||||
this.route.parent.params.subscribe(async (params) => {
|
dialogService: DialogService,
|
||||||
this.providerId = params.providerId;
|
i18nService: I18nService,
|
||||||
|
searchService: SearchService,
|
||||||
await this.load();
|
toastService: ToastService,
|
||||||
|
validationService: ValidationService,
|
||||||
/* eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe, rxjs/no-nested-subscribe */
|
webProviderService: WebProviderService,
|
||||||
this.route.queryParams.pipe(first()).subscribe(async (qParams) => {
|
) {
|
||||||
this.searchText = qParams.search;
|
super(
|
||||||
});
|
activatedRoute,
|
||||||
});
|
dialogService,
|
||||||
|
i18nService,
|
||||||
this._searchText$
|
searchService,
|
||||||
.pipe(
|
toastService,
|
||||||
switchMap((searchText) => from(this.searchService.isSearchable(searchText))),
|
validationService,
|
||||||
takeUntil(this.destroy$),
|
webProviderService,
|
||||||
)
|
);
|
||||||
.subscribe((isSearchable) => {
|
|
||||||
this.isSearching = isSearchable;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnDestroy(): void {
|
ngOnInit() {
|
||||||
this.destroy$.next();
|
this.activatedRoute.parent.params
|
||||||
this.destroy$.complete();
|
.pipe(
|
||||||
|
switchMap((params) => {
|
||||||
|
this.providerId = params.providerId;
|
||||||
|
return combineLatest([
|
||||||
|
this.providerService.get(this.providerId),
|
||||||
|
this.consolidatedBillingEnabled$,
|
||||||
|
]).pipe(
|
||||||
|
concatMap(([provider, consolidatedBillingEnabled]) => {
|
||||||
|
if (
|
||||||
|
!consolidatedBillingEnabled ||
|
||||||
|
provider.providerStatus !== ProviderStatusType.Billable
|
||||||
|
) {
|
||||||
|
return from(
|
||||||
|
this.router.navigate(["../clients"], {
|
||||||
|
relativeTo: this.activatedRoute,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
this.provider = provider;
|
||||||
|
this.manageOrganizations = this.provider.type === ProviderUserType.ProviderAdmin;
|
||||||
|
return from(this.load());
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
takeUntil(this.destroy$),
|
||||||
|
)
|
||||||
|
.subscribe();
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy() {
|
||||||
|
super.ngOnDestroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
async load() {
|
async load() {
|
||||||
const clientsResponse = await this.apiService.getProviderClients(this.providerId);
|
this.clients = (await this.apiService.getProviderClients(this.providerId)).data;
|
||||||
this.clients =
|
|
||||||
clientsResponse.data != null && clientsResponse.data.length > 0 ? clientsResponse.data : [];
|
|
||||||
this.dataSource.data = this.clients;
|
|
||||||
this.manageOrganizations =
|
|
||||||
(await this.providerService.get(this.providerId)).type === ProviderUserType.ProviderAdmin;
|
|
||||||
|
|
||||||
const plansResponse = await this.billingApiService.getPlans();
|
this.dataSource.data = this.clients;
|
||||||
this.plans = plansResponse.data;
|
|
||||||
|
this.plans = (await this.billingApiService.getPlans()).data;
|
||||||
|
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
isPaging() {
|
|
||||||
const searching = this.isSearching;
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
return !searching && this.clients && this.clients.length > this.pageSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
async resetPaging() {
|
|
||||||
this.pagedClients = [];
|
|
||||||
this.loadMore();
|
|
||||||
}
|
|
||||||
|
|
||||||
loadMore() {
|
|
||||||
if (!this.clients || this.clients.length <= this.pageSize) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const pagedLength = this.pagedClients.length;
|
|
||||||
let pagedSize = this.pageSize;
|
|
||||||
if (pagedLength === 0 && this.pagedClientsCount > this.pageSize) {
|
|
||||||
pagedSize = this.pagedClientsCount;
|
|
||||||
}
|
|
||||||
if (this.clients.length > pagedLength) {
|
|
||||||
this.pagedClients = this.pagedClients.concat(
|
|
||||||
this.clients.slice(pagedLength, pagedLength + pagedSize),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
this.pagedClientsCount = this.pagedClients.length;
|
|
||||||
this.didScroll = this.pagedClients.length > this.pageSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
async manageSubscription(organization: ProviderOrganizationOrganizationDetailsResponse) {
|
async manageSubscription(organization: ProviderOrganizationOrganizationDetailsResponse) {
|
||||||
if (organization == null) {
|
if (organization == null) {
|
||||||
return;
|
return;
|
||||||
|
@ -161,35 +127,6 @@ export class ManageClientOrganizationsComponent implements OnInit, OnDestroy {
|
||||||
await this.load();
|
await this.load();
|
||||||
}
|
}
|
||||||
|
|
||||||
async remove(organization: ProviderOrganizationOrganizationDetailsResponse) {
|
|
||||||
const confirmed = await this.dialogService.openSimpleDialog({
|
|
||||||
title: organization.organizationName,
|
|
||||||
content: { key: "detachOrganizationConfirmation" },
|
|
||||||
type: "warning",
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!confirmed) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.actionPromise = this.webProviderService.detachOrganization(
|
|
||||||
this.providerId,
|
|
||||||
organization.id,
|
|
||||||
);
|
|
||||||
try {
|
|
||||||
await this.actionPromise;
|
|
||||||
this.platformUtilsService.showToast(
|
|
||||||
"success",
|
|
||||||
null,
|
|
||||||
this.i18nService.t("detachedOrganization", organization.organizationName),
|
|
||||||
);
|
|
||||||
await this.load();
|
|
||||||
} catch (e) {
|
|
||||||
this.validationService.showError(e);
|
|
||||||
}
|
|
||||||
this.actionPromise = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
createClientOrganization = async () => {
|
createClientOrganization = async () => {
|
||||||
const reference = openCreateClientOrganizationDialog(this.dialogService, {
|
const reference = openCreateClientOrganizationDialog(this.dialogService, {
|
||||||
data: {
|
data: {
|
||||||
|
|
|
@ -3,13 +3,13 @@ import { Subject, takeUntil } from "rxjs";
|
||||||
|
|
||||||
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 { KdfConfigService } from "@bitwarden/common/auth/abstractions/kdf-config.service";
|
||||||
import { KdfConfig } from "@bitwarden/common/auth/models/domain/kdf-config";
|
import { KdfConfig } from "@bitwarden/common/auth/models/domain/kdf-config";
|
||||||
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
|
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
|
||||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.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 { KdfType } 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 { PasswordGenerationServiceAbstraction } from "@bitwarden/common/tools/generator/password";
|
import { PasswordGenerationServiceAbstraction } from "@bitwarden/common/tools/generator/password";
|
||||||
|
@ -31,7 +31,6 @@ export class ChangePasswordComponent implements OnInit, OnDestroy {
|
||||||
minimumLength = Utils.minimumPasswordLength;
|
minimumLength = Utils.minimumPasswordLength;
|
||||||
|
|
||||||
protected email: string;
|
protected email: string;
|
||||||
protected kdf: KdfType;
|
|
||||||
protected kdfConfig: KdfConfig;
|
protected kdfConfig: KdfConfig;
|
||||||
|
|
||||||
protected destroy$ = new Subject<void>();
|
protected destroy$ = new Subject<void>();
|
||||||
|
@ -45,6 +44,7 @@ export class ChangePasswordComponent implements OnInit, OnDestroy {
|
||||||
protected policyService: PolicyService,
|
protected policyService: PolicyService,
|
||||||
protected stateService: StateService,
|
protected stateService: StateService,
|
||||||
protected dialogService: DialogService,
|
protected dialogService: DialogService,
|
||||||
|
protected kdfConfigService: KdfConfigService,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async ngOnInit() {
|
async ngOnInit() {
|
||||||
|
@ -73,18 +73,14 @@ export class ChangePasswordComponent implements OnInit, OnDestroy {
|
||||||
}
|
}
|
||||||
|
|
||||||
const email = await this.stateService.getEmail();
|
const email = await this.stateService.getEmail();
|
||||||
if (this.kdf == null) {
|
|
||||||
this.kdf = await this.stateService.getKdfType();
|
|
||||||
}
|
|
||||||
if (this.kdfConfig == null) {
|
if (this.kdfConfig == null) {
|
||||||
this.kdfConfig = await this.stateService.getKdfConfig();
|
this.kdfConfig = await this.kdfConfigService.getKdfConfig();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create new master key
|
// Create new master key
|
||||||
const newMasterKey = await this.cryptoService.makeMasterKey(
|
const newMasterKey = await this.cryptoService.makeMasterKey(
|
||||||
this.masterPassword,
|
this.masterPassword,
|
||||||
email.trim().toLowerCase(),
|
email.trim().toLowerCase(),
|
||||||
this.kdf,
|
|
||||||
this.kdfConfig,
|
this.kdfConfig,
|
||||||
);
|
);
|
||||||
const newMasterKeyHash = await this.cryptoService.hashMasterKey(
|
const newMasterKeyHash = await this.cryptoService.hashMasterKey(
|
||||||
|
|
|
@ -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 { DeviceTrustServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust.service.abstraction";
|
import { DeviceTrustServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust.service.abstraction";
|
||||||
|
import { KdfConfigService } from "@bitwarden/common/auth/abstractions/kdf-config.service";
|
||||||
import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/auth/abstractions/master-password.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";
|
||||||
|
@ -79,6 +80,7 @@ export class LockComponent implements OnInit, OnDestroy {
|
||||||
protected pinCryptoService: PinCryptoServiceAbstraction,
|
protected pinCryptoService: PinCryptoServiceAbstraction,
|
||||||
protected biometricStateService: BiometricStateService,
|
protected biometricStateService: BiometricStateService,
|
||||||
protected accountService: AccountService,
|
protected accountService: AccountService,
|
||||||
|
protected kdfConfigService: KdfConfigService,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async ngOnInit() {
|
async ngOnInit() {
|
||||||
|
@ -208,14 +210,12 @@ export class LockComponent implements OnInit, OnDestroy {
|
||||||
}
|
}
|
||||||
|
|
||||||
private async doUnlockWithMasterPassword() {
|
private async doUnlockWithMasterPassword() {
|
||||||
|
const kdfConfig = await this.kdfConfigService.getKdfConfig();
|
||||||
const userId = (await firstValueFrom(this.accountService.activeAccount$))?.id;
|
const userId = (await firstValueFrom(this.accountService.activeAccount$))?.id;
|
||||||
const kdf = await this.stateService.getKdfType();
|
|
||||||
const kdfConfig = await this.stateService.getKdfConfig();
|
|
||||||
|
|
||||||
const masterKey = await this.cryptoService.makeMasterKey(
|
const masterKey = await this.cryptoService.makeMasterKey(
|
||||||
this.masterPassword,
|
this.masterPassword,
|
||||||
this.email,
|
this.email,
|
||||||
kdf,
|
|
||||||
kdfConfig,
|
kdfConfig,
|
||||||
);
|
);
|
||||||
const storedMasterKeyHash = await firstValueFrom(
|
const storedMasterKeyHash = await firstValueFrom(
|
||||||
|
|
|
@ -15,7 +15,7 @@ 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 { DEFAULT_KDF_CONFIG, DEFAULT_KDF_TYPE } from "@bitwarden/common/platform/enums";
|
import { DEFAULT_KDF_CONFIG } from "@bitwarden/common/platform/enums";
|
||||||
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
||||||
import { PasswordGenerationServiceAbstraction } from "@bitwarden/common/tools/generator/password";
|
import { PasswordGenerationServiceAbstraction } from "@bitwarden/common/tools/generator/password";
|
||||||
import { DialogService } from "@bitwarden/components";
|
import { DialogService } from "@bitwarden/components";
|
||||||
|
@ -273,9 +273,8 @@ export class RegisterComponent extends CaptchaProtectedComponent implements OnIn
|
||||||
name: string,
|
name: string,
|
||||||
): Promise<RegisterRequest> {
|
): Promise<RegisterRequest> {
|
||||||
const hint = this.formGroup.value.hint;
|
const hint = this.formGroup.value.hint;
|
||||||
const kdf = DEFAULT_KDF_TYPE;
|
|
||||||
const kdfConfig = DEFAULT_KDF_CONFIG;
|
const kdfConfig = DEFAULT_KDF_CONFIG;
|
||||||
const key = await this.cryptoService.makeMasterKey(masterPassword, email, kdf, kdfConfig);
|
const key = await this.cryptoService.makeMasterKey(masterPassword, email, kdfConfig);
|
||||||
const newUserKey = await this.cryptoService.makeUserKey(key);
|
const newUserKey = await this.cryptoService.makeUserKey(key);
|
||||||
const masterKeyHash = await this.cryptoService.hashMasterKey(masterPassword, key);
|
const masterKeyHash = await this.cryptoService.hashMasterKey(masterPassword, key);
|
||||||
const keys = await this.cryptoService.makeKeyPair(newUserKey[0]);
|
const keys = await this.cryptoService.makeKeyPair(newUserKey[0]);
|
||||||
|
@ -287,10 +286,8 @@ export class RegisterComponent extends CaptchaProtectedComponent implements OnIn
|
||||||
newUserKey[1].encryptedString,
|
newUserKey[1].encryptedString,
|
||||||
this.referenceData,
|
this.referenceData,
|
||||||
this.captchaToken,
|
this.captchaToken,
|
||||||
kdf,
|
kdfConfig.kdfType,
|
||||||
kdfConfig.iterations,
|
kdfConfig.iterations,
|
||||||
kdfConfig.memory,
|
|
||||||
kdfConfig.parallelism,
|
|
||||||
);
|
);
|
||||||
request.keys = new KeysRequest(keys[0], keys[1].encryptedString);
|
request.keys = new KeysRequest(keys[0], keys[1].encryptedString);
|
||||||
const orgInvite = await this.stateService.getOrganizationInvitation();
|
const orgInvite = await this.stateService.getOrganizationInvitation();
|
||||||
|
|
|
@ -13,6 +13,7 @@ import { PolicyService } from "@bitwarden/common/admin-console/abstractions/poli
|
||||||
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 { 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";
|
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";
|
||||||
|
@ -23,11 +24,7 @@ import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.servic
|
||||||
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 {
|
import { HashPurpose, DEFAULT_KDF_CONFIG } from "@bitwarden/common/platform/enums";
|
||||||
HashPurpose,
|
|
||||||
DEFAULT_KDF_TYPE,
|
|
||||||
DEFAULT_KDF_CONFIG,
|
|
||||||
} 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 { PasswordGenerationServiceAbstraction } from "@bitwarden/common/tools/generator/password";
|
import { PasswordGenerationServiceAbstraction } from "@bitwarden/common/tools/generator/password";
|
||||||
|
@ -73,6 +70,7 @@ export class SetPasswordComponent extends BaseChangePasswordComponent {
|
||||||
private userDecryptionOptionsService: InternalUserDecryptionOptionsServiceAbstraction,
|
private userDecryptionOptionsService: InternalUserDecryptionOptionsServiceAbstraction,
|
||||||
private ssoLoginService: SsoLoginServiceAbstraction,
|
private ssoLoginService: SsoLoginServiceAbstraction,
|
||||||
dialogService: DialogService,
|
dialogService: DialogService,
|
||||||
|
kdfConfigService: KdfConfigService,
|
||||||
) {
|
) {
|
||||||
super(
|
super(
|
||||||
i18nService,
|
i18nService,
|
||||||
|
@ -83,6 +81,7 @@ export class SetPasswordComponent extends BaseChangePasswordComponent {
|
||||||
policyService,
|
policyService,
|
||||||
stateService,
|
stateService,
|
||||||
dialogService,
|
dialogService,
|
||||||
|
kdfConfigService,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -139,7 +138,6 @@ export class SetPasswordComponent extends BaseChangePasswordComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
async setupSubmitActions() {
|
async setupSubmitActions() {
|
||||||
this.kdf = DEFAULT_KDF_TYPE;
|
|
||||||
this.kdfConfig = DEFAULT_KDF_CONFIG;
|
this.kdfConfig = DEFAULT_KDF_CONFIG;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -169,10 +167,8 @@ export class SetPasswordComponent extends BaseChangePasswordComponent {
|
||||||
this.hint,
|
this.hint,
|
||||||
this.orgSsoIdentifier,
|
this.orgSsoIdentifier,
|
||||||
keysRequest,
|
keysRequest,
|
||||||
this.kdf,
|
this.kdfConfig.kdfType, //always PBKDF2 --> see this.setupSubmitActions
|
||||||
this.kdfConfig.iterations,
|
this.kdfConfig.iterations,
|
||||||
this.kdfConfig.memory,
|
|
||||||
this.kdfConfig.parallelism,
|
|
||||||
);
|
);
|
||||||
try {
|
try {
|
||||||
if (this.resetPasswordAutoEnroll) {
|
if (this.resetPasswordAutoEnroll) {
|
||||||
|
@ -246,9 +242,7 @@ export class SetPasswordComponent extends BaseChangePasswordComponent {
|
||||||
);
|
);
|
||||||
userDecryptionOpts.hasMasterPassword = true;
|
userDecryptionOpts.hasMasterPassword = true;
|
||||||
await this.userDecryptionOptionsService.setUserDecryptionOptions(userDecryptionOpts);
|
await this.userDecryptionOptionsService.setUserDecryptionOptions(userDecryptionOpts);
|
||||||
|
await this.kdfConfigService.setKdfConfig(this.userId, this.kdfConfig);
|
||||||
await this.stateService.setKdfType(this.kdf);
|
|
||||||
await this.stateService.setKdfConfig(this.kdfConfig);
|
|
||||||
await this.masterPasswordService.setMasterKey(masterKey, this.userId);
|
await this.masterPasswordService.setMasterKey(masterKey, this.userId);
|
||||||
await this.cryptoService.setUserKey(userKey[0]);
|
await this.cryptoService.setUserKey(userKey[0]);
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@ import { DialogRef } from "@angular/cdk/dialog";
|
||||||
import { Directive, OnInit } from "@angular/core";
|
import { Directive, OnInit } from "@angular/core";
|
||||||
import { FormBuilder, Validators } from "@angular/forms";
|
import { FormBuilder, Validators } from "@angular/forms";
|
||||||
|
|
||||||
|
import { KdfConfigService } from "@bitwarden/common/auth/abstractions/kdf-config.service";
|
||||||
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 { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
|
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
|
||||||
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
|
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
|
||||||
|
@ -22,6 +23,7 @@ export class SetPinComponent implements OnInit {
|
||||||
private userVerificationService: UserVerificationService,
|
private userVerificationService: UserVerificationService,
|
||||||
private stateService: StateService,
|
private stateService: StateService,
|
||||||
private formBuilder: FormBuilder,
|
private formBuilder: FormBuilder,
|
||||||
|
private kdfConfigService: KdfConfigService,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async ngOnInit() {
|
async ngOnInit() {
|
||||||
|
@ -43,8 +45,7 @@ export class SetPinComponent implements OnInit {
|
||||||
const pinKey = await this.cryptoService.makePinKey(
|
const pinKey = await this.cryptoService.makePinKey(
|
||||||
pin,
|
pin,
|
||||||
await this.stateService.getEmail(),
|
await this.stateService.getEmail(),
|
||||||
await this.stateService.getKdfType(),
|
await this.kdfConfigService.getKdfConfig(),
|
||||||
await this.stateService.getKdfConfig(),
|
|
||||||
);
|
);
|
||||||
const userKey = await this.cryptoService.getUserKey();
|
const userKey = await this.cryptoService.getUserKey();
|
||||||
const pinProtectedKey = await this.cryptoService.encrypt(userKey.key, pinKey);
|
const pinProtectedKey = await this.cryptoService.encrypt(userKey.key, pinKey);
|
||||||
|
|
|
@ -253,7 +253,7 @@ describe("SsoComponent", () => {
|
||||||
describe("2FA scenarios", () => {
|
describe("2FA scenarios", () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
const authResult = new AuthResult();
|
const authResult = new AuthResult();
|
||||||
authResult.twoFactorProviders = new Map([[TwoFactorProviderType.Authenticator, {}]]);
|
authResult.twoFactorProviders = { [TwoFactorProviderType.Authenticator]: {} };
|
||||||
|
|
||||||
// use standard user with MP because this test is not concerned with password reset.
|
// use standard user with MP because this test is not concerned with password reset.
|
||||||
selectedUserDecryptionOptions.next(mockUserDecryptionOpts.withMasterPassword);
|
selectedUserDecryptionOptions.next(mockUserDecryptionOpts.withMasterPassword);
|
||||||
|
|
|
@ -2,7 +2,10 @@ import { Directive, EventEmitter, OnInit, Output } from "@angular/core";
|
||||||
import { Router } from "@angular/router";
|
import { Router } from "@angular/router";
|
||||||
import { firstValueFrom } from "rxjs";
|
import { firstValueFrom } from "rxjs";
|
||||||
|
|
||||||
import { TwoFactorService } from "@bitwarden/common/auth/abstractions/two-factor.service";
|
import {
|
||||||
|
TwoFactorProviderDetails,
|
||||||
|
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";
|
||||||
import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service";
|
import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service";
|
||||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||||
|
@ -24,11 +27,11 @@ export class TwoFactorOptionsComponent implements OnInit {
|
||||||
protected environmentService: EnvironmentService,
|
protected environmentService: EnvironmentService,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
ngOnInit() {
|
async ngOnInit() {
|
||||||
this.providers = this.twoFactorService.getSupportedProviders(this.win);
|
this.providers = await this.twoFactorService.getSupportedProviders(this.win);
|
||||||
}
|
}
|
||||||
|
|
||||||
choose(p: any) {
|
async choose(p: TwoFactorProviderDetails) {
|
||||||
this.onProviderSelected.emit(p.type);
|
this.onProviderSelected.emit(p.type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -102,7 +102,7 @@ export class TwoFactorComponent extends CaptchaProtectedComponent implements OnI
|
||||||
}
|
}
|
||||||
|
|
||||||
async ngOnInit() {
|
async ngOnInit() {
|
||||||
if (!(await this.authing()) || this.twoFactorService.getProviders() == null) {
|
if (!(await this.authing()) || (await this.twoFactorService.getProviders()) == 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.
|
||||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||||
this.router.navigate([this.loginRoute]);
|
this.router.navigate([this.loginRoute]);
|
||||||
|
@ -145,7 +145,9 @@ export class TwoFactorComponent extends CaptchaProtectedComponent implements OnI
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.selectedProviderType = this.twoFactorService.getDefaultProvider(this.webAuthnSupported);
|
this.selectedProviderType = await this.twoFactorService.getDefaultProvider(
|
||||||
|
this.webAuthnSupported,
|
||||||
|
);
|
||||||
await this.init();
|
await this.init();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -162,12 +164,14 @@ export class TwoFactorComponent extends CaptchaProtectedComponent implements OnI
|
||||||
|
|
||||||
this.cleanupWebAuthn();
|
this.cleanupWebAuthn();
|
||||||
this.title = (TwoFactorProviders as any)[this.selectedProviderType].name;
|
this.title = (TwoFactorProviders as any)[this.selectedProviderType].name;
|
||||||
const providerData = this.twoFactorService.getProviders().get(this.selectedProviderType);
|
const providerData = await this.twoFactorService.getProviders().then((providers) => {
|
||||||
|
return providers.get(this.selectedProviderType);
|
||||||
|
});
|
||||||
switch (this.selectedProviderType) {
|
switch (this.selectedProviderType) {
|
||||||
case TwoFactorProviderType.WebAuthn:
|
case TwoFactorProviderType.WebAuthn:
|
||||||
if (!this.webAuthnNewTab) {
|
if (!this.webAuthnNewTab) {
|
||||||
setTimeout(() => {
|
setTimeout(async () => {
|
||||||
this.authWebAuthn();
|
await this.authWebAuthn();
|
||||||
}, 500);
|
}, 500);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -212,7 +216,7 @@ export class TwoFactorComponent extends CaptchaProtectedComponent implements OnI
|
||||||
break;
|
break;
|
||||||
case TwoFactorProviderType.Email:
|
case TwoFactorProviderType.Email:
|
||||||
this.twoFactorEmail = providerData.Email;
|
this.twoFactorEmail = providerData.Email;
|
||||||
if (this.twoFactorService.getProviders().size > 1) {
|
if ((await this.twoFactorService.getProviders()).size > 1) {
|
||||||
await this.sendEmail(false);
|
await this.sendEmail(false);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -474,8 +478,10 @@ export class TwoFactorComponent extends CaptchaProtectedComponent implements OnI
|
||||||
this.emailPromise = null;
|
this.emailPromise = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
authWebAuthn() {
|
async authWebAuthn() {
|
||||||
const providerData = this.twoFactorService.getProviders().get(this.selectedProviderType);
|
const providerData = await this.twoFactorService.getProviders().then((providers) => {
|
||||||
|
return providers.get(this.selectedProviderType);
|
||||||
|
});
|
||||||
|
|
||||||
if (!this.webAuthnSupported || this.webAuthn == null) {
|
if (!this.webAuthnSupported || this.webAuthn == null) {
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -4,6 +4,7 @@ import { Router } from "@angular/router";
|
||||||
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 { KdfConfigService } from "@bitwarden/common/auth/abstractions/kdf-config.service";
|
||||||
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 { PasswordRequest } from "@bitwarden/common/auth/models/request/password.request";
|
import { PasswordRequest } from "@bitwarden/common/auth/models/request/password.request";
|
||||||
|
@ -44,6 +45,7 @@ export class UpdatePasswordComponent extends BaseChangePasswordComponent {
|
||||||
private userVerificationService: UserVerificationService,
|
private userVerificationService: UserVerificationService,
|
||||||
private logService: LogService,
|
private logService: LogService,
|
||||||
dialogService: DialogService,
|
dialogService: DialogService,
|
||||||
|
kdfConfigService: KdfConfigService,
|
||||||
) {
|
) {
|
||||||
super(
|
super(
|
||||||
i18nService,
|
i18nService,
|
||||||
|
@ -54,6 +56,7 @@ export class UpdatePasswordComponent extends BaseChangePasswordComponent {
|
||||||
policyService,
|
policyService,
|
||||||
stateService,
|
stateService,
|
||||||
dialogService,
|
dialogService,
|
||||||
|
kdfConfigService,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -90,8 +93,7 @@ export class UpdatePasswordComponent extends BaseChangePasswordComponent {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.kdf = await this.stateService.getKdfType();
|
this.kdfConfig = await this.kdfConfigService.getKdfConfig();
|
||||||
this.kdfConfig = await this.stateService.getKdfConfig();
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,7 @@ 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 { 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";
|
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";
|
||||||
|
@ -59,6 +60,7 @@ export class UpdateTempPasswordComponent extends BaseChangePasswordComponent {
|
||||||
private userVerificationService: UserVerificationService,
|
private userVerificationService: UserVerificationService,
|
||||||
protected router: Router,
|
protected router: Router,
|
||||||
dialogService: DialogService,
|
dialogService: DialogService,
|
||||||
|
kdfConfigService: KdfConfigService,
|
||||||
private accountService: AccountService,
|
private accountService: AccountService,
|
||||||
private masterPasswordService: InternalMasterPasswordServiceAbstraction,
|
private masterPasswordService: InternalMasterPasswordServiceAbstraction,
|
||||||
) {
|
) {
|
||||||
|
@ -71,6 +73,7 @@ export class UpdateTempPasswordComponent extends BaseChangePasswordComponent {
|
||||||
policyService,
|
policyService,
|
||||||
stateService,
|
stateService,
|
||||||
dialogService,
|
dialogService,
|
||||||
|
kdfConfigService,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -104,8 +107,7 @@ export class UpdateTempPasswordComponent extends BaseChangePasswordComponent {
|
||||||
|
|
||||||
async setupSubmitActions(): Promise<boolean> {
|
async setupSubmitActions(): Promise<boolean> {
|
||||||
this.email = await this.stateService.getEmail();
|
this.email = await this.stateService.getEmail();
|
||||||
this.kdf = await this.stateService.getKdfType();
|
this.kdfConfig = await this.kdfConfigService.getKdfConfig();
|
||||||
this.kdfConfig = await this.stateService.getKdfConfig();
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -124,7 +126,6 @@ export class UpdateTempPasswordComponent extends BaseChangePasswordComponent {
|
||||||
const newMasterKey = await this.cryptoService.makeMasterKey(
|
const newMasterKey = await this.cryptoService.makeMasterKey(
|
||||||
this.masterPassword,
|
this.masterPassword,
|
||||||
this.email.trim().toLowerCase(),
|
this.email.trim().toLowerCase(),
|
||||||
this.kdf,
|
|
||||||
this.kdfConfig,
|
this.kdfConfig,
|
||||||
);
|
);
|
||||||
const newPasswordHash = await this.cryptoService.hashMasterKey(
|
const newPasswordHash = await this.cryptoService.hashMasterKey(
|
||||||
|
|
|
@ -63,6 +63,7 @@ import { AvatarService as AvatarServiceAbstraction } from "@bitwarden/common/aut
|
||||||
import { DeviceTrustServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust.service.abstraction";
|
import { DeviceTrustServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust.service.abstraction";
|
||||||
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 { KdfConfigService as KdfConfigServiceAbstraction } from "@bitwarden/common/auth/abstractions/kdf-config.service";
|
||||||
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 {
|
import {
|
||||||
InternalMasterPasswordServiceAbstraction,
|
InternalMasterPasswordServiceAbstraction,
|
||||||
|
@ -85,6 +86,7 @@ import { AvatarService } from "@bitwarden/common/auth/services/avatar.service";
|
||||||
import { DeviceTrustService } from "@bitwarden/common/auth/services/device-trust.service.implementation";
|
import { DeviceTrustService } from "@bitwarden/common/auth/services/device-trust.service.implementation";
|
||||||
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 { KdfConfigService } from "@bitwarden/common/auth/services/kdf-config.service";
|
||||||
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 { 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";
|
||||||
|
@ -390,6 +392,7 @@ const safeProviders: SafeProvider[] = [
|
||||||
InternalUserDecryptionOptionsServiceAbstraction,
|
InternalUserDecryptionOptionsServiceAbstraction,
|
||||||
GlobalStateProvider,
|
GlobalStateProvider,
|
||||||
BillingAccountProfileStateService,
|
BillingAccountProfileStateService,
|
||||||
|
KdfConfigServiceAbstraction,
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
safeProvider({
|
safeProvider({
|
||||||
|
@ -543,6 +546,7 @@ const safeProviders: SafeProvider[] = [
|
||||||
StateServiceAbstraction,
|
StateServiceAbstraction,
|
||||||
AccountServiceAbstraction,
|
AccountServiceAbstraction,
|
||||||
StateProvider,
|
StateProvider,
|
||||||
|
KdfConfigServiceAbstraction,
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
safeProvider({
|
safeProvider({
|
||||||
|
@ -713,7 +717,7 @@ const safeProviders: SafeProvider[] = [
|
||||||
CipherServiceAbstraction,
|
CipherServiceAbstraction,
|
||||||
CryptoServiceAbstraction,
|
CryptoServiceAbstraction,
|
||||||
CryptoFunctionServiceAbstraction,
|
CryptoFunctionServiceAbstraction,
|
||||||
StateServiceAbstraction,
|
KdfConfigServiceAbstraction,
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
safeProvider({
|
safeProvider({
|
||||||
|
@ -724,8 +728,8 @@ const safeProviders: SafeProvider[] = [
|
||||||
ApiServiceAbstraction,
|
ApiServiceAbstraction,
|
||||||
CryptoServiceAbstraction,
|
CryptoServiceAbstraction,
|
||||||
CryptoFunctionServiceAbstraction,
|
CryptoFunctionServiceAbstraction,
|
||||||
StateServiceAbstraction,
|
|
||||||
CollectionServiceAbstraction,
|
CollectionServiceAbstraction,
|
||||||
|
KdfConfigServiceAbstraction,
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
safeProvider({
|
safeProvider({
|
||||||
|
@ -834,6 +838,7 @@ const safeProviders: SafeProvider[] = [
|
||||||
LogService,
|
LogService,
|
||||||
VaultTimeoutSettingsServiceAbstraction,
|
VaultTimeoutSettingsServiceAbstraction,
|
||||||
PlatformUtilsServiceAbstraction,
|
PlatformUtilsServiceAbstraction,
|
||||||
|
KdfConfigServiceAbstraction,
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
safeProvider({
|
safeProvider({
|
||||||
|
@ -869,7 +874,7 @@ const safeProviders: SafeProvider[] = [
|
||||||
safeProvider({
|
safeProvider({
|
||||||
provide: TwoFactorServiceAbstraction,
|
provide: TwoFactorServiceAbstraction,
|
||||||
useClass: TwoFactorService,
|
useClass: TwoFactorService,
|
||||||
deps: [I18nServiceAbstraction, PlatformUtilsServiceAbstraction],
|
deps: [I18nServiceAbstraction, PlatformUtilsServiceAbstraction, GlobalStateProvider],
|
||||||
}),
|
}),
|
||||||
safeProvider({
|
safeProvider({
|
||||||
provide: FormValidationErrorsServiceAbstraction,
|
provide: FormValidationErrorsServiceAbstraction,
|
||||||
|
@ -985,6 +990,7 @@ const safeProviders: SafeProvider[] = [
|
||||||
CryptoServiceAbstraction,
|
CryptoServiceAbstraction,
|
||||||
VaultTimeoutSettingsServiceAbstraction,
|
VaultTimeoutSettingsServiceAbstraction,
|
||||||
LogService,
|
LogService,
|
||||||
|
KdfConfigServiceAbstraction,
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
safeProvider({
|
safeProvider({
|
||||||
|
@ -1150,6 +1156,11 @@ const safeProviders: SafeProvider[] = [
|
||||||
useClass: ProviderApiService,
|
useClass: ProviderApiService,
|
||||||
deps: [ApiServiceAbstraction],
|
deps: [ApiServiceAbstraction],
|
||||||
}),
|
}),
|
||||||
|
safeProvider({
|
||||||
|
provide: KdfConfigServiceAbstraction,
|
||||||
|
useClass: KdfConfigService,
|
||||||
|
deps: [StateProvider],
|
||||||
|
}),
|
||||||
];
|
];
|
||||||
|
|
||||||
function encryptServiceFactory(
|
function encryptServiceFactory(
|
||||||
|
|
|
@ -184,8 +184,6 @@ export class AddEditComponent implements OnInit, OnDestroy {
|
||||||
FeatureFlag.FlexibleCollectionsV1,
|
FeatureFlag.FlexibleCollectionsV1,
|
||||||
false,
|
false,
|
||||||
);
|
);
|
||||||
this.writeableCollections = await this.loadCollections();
|
|
||||||
this.canUseReprompt = await this.passwordRepromptService.enabled();
|
|
||||||
|
|
||||||
this.policyService
|
this.policyService
|
||||||
.policyAppliesToActiveUser$(PolicyType.PersonalOwnership)
|
.policyAppliesToActiveUser$(PolicyType.PersonalOwnership)
|
||||||
|
@ -197,6 +195,9 @@ export class AddEditComponent implements OnInit, OnDestroy {
|
||||||
takeUntil(this.destroy$),
|
takeUntil(this.destroy$),
|
||||||
)
|
)
|
||||||
.subscribe();
|
.subscribe();
|
||||||
|
|
||||||
|
this.writeableCollections = await this.loadCollections();
|
||||||
|
this.canUseReprompt = await this.passwordRepromptService.enabled();
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnDestroy() {
|
ngOnDestroy() {
|
||||||
|
|
|
@ -2,6 +2,7 @@ import { mock, MockProxy } from "jest-mock-extended";
|
||||||
|
|
||||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||||
import { DeviceTrustServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust.service.abstraction";
|
import { DeviceTrustServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust.service.abstraction";
|
||||||
|
import { KdfConfigService } from "@bitwarden/common/auth/abstractions/kdf-config.service";
|
||||||
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";
|
||||||
|
@ -44,6 +45,7 @@ describe("AuthRequestLoginStrategy", () => {
|
||||||
let userDecryptionOptions: MockProxy<InternalUserDecryptionOptionsServiceAbstraction>;
|
let userDecryptionOptions: MockProxy<InternalUserDecryptionOptionsServiceAbstraction>;
|
||||||
let deviceTrustService: MockProxy<DeviceTrustServiceAbstraction>;
|
let deviceTrustService: MockProxy<DeviceTrustServiceAbstraction>;
|
||||||
let billingAccountProfileStateService: MockProxy<BillingAccountProfileStateService>;
|
let billingAccountProfileStateService: MockProxy<BillingAccountProfileStateService>;
|
||||||
|
let kdfConfigService: MockProxy<KdfConfigService>;
|
||||||
|
|
||||||
const mockUserId = Utils.newGuid() as UserId;
|
const mockUserId = Utils.newGuid() as UserId;
|
||||||
let accountService: FakeAccountService;
|
let accountService: FakeAccountService;
|
||||||
|
@ -77,13 +79,16 @@ describe("AuthRequestLoginStrategy", () => {
|
||||||
userDecryptionOptions = mock<InternalUserDecryptionOptionsServiceAbstraction>();
|
userDecryptionOptions = mock<InternalUserDecryptionOptionsServiceAbstraction>();
|
||||||
deviceTrustService = mock<DeviceTrustServiceAbstraction>();
|
deviceTrustService = mock<DeviceTrustServiceAbstraction>();
|
||||||
billingAccountProfileStateService = mock<BillingAccountProfileStateService>();
|
billingAccountProfileStateService = mock<BillingAccountProfileStateService>();
|
||||||
|
kdfConfigService = mock<KdfConfigService>();
|
||||||
|
|
||||||
accountService = mockAccountServiceWith(mockUserId);
|
accountService = mockAccountServiceWith(mockUserId);
|
||||||
masterPasswordService = new FakeMasterPasswordService();
|
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({
|
||||||
|
sub: mockUserId,
|
||||||
|
});
|
||||||
|
|
||||||
authRequestLoginStrategy = new AuthRequestLoginStrategy(
|
authRequestLoginStrategy = new AuthRequestLoginStrategy(
|
||||||
cache,
|
cache,
|
||||||
|
@ -101,6 +106,7 @@ describe("AuthRequestLoginStrategy", () => {
|
||||||
userDecryptionOptions,
|
userDecryptionOptions,
|
||||||
deviceTrustService,
|
deviceTrustService,
|
||||||
billingAccountProfileStateService,
|
billingAccountProfileStateService,
|
||||||
|
kdfConfigService,
|
||||||
);
|
);
|
||||||
|
|
||||||
tokenResponse = identityTokenResponseFactory();
|
tokenResponse = identityTokenResponseFactory();
|
||||||
|
|
|
@ -3,6 +3,7 @@ import { Jsonify } from "type-fest";
|
||||||
|
|
||||||
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 { 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";
|
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 { TwoFactorService } from "@bitwarden/common/auth/abstractions/two-factor.service";
|
import { TwoFactorService } from "@bitwarden/common/auth/abstractions/two-factor.service";
|
||||||
|
@ -63,6 +64,7 @@ export class AuthRequestLoginStrategy extends LoginStrategy {
|
||||||
userDecryptionOptionsService: InternalUserDecryptionOptionsServiceAbstraction,
|
userDecryptionOptionsService: InternalUserDecryptionOptionsServiceAbstraction,
|
||||||
private deviceTrustService: DeviceTrustServiceAbstraction,
|
private deviceTrustService: DeviceTrustServiceAbstraction,
|
||||||
billingAccountProfileStateService: BillingAccountProfileStateService,
|
billingAccountProfileStateService: BillingAccountProfileStateService,
|
||||||
|
kdfConfigService: KdfConfigService,
|
||||||
) {
|
) {
|
||||||
super(
|
super(
|
||||||
accountService,
|
accountService,
|
||||||
|
@ -78,6 +80,7 @@ export class AuthRequestLoginStrategy extends LoginStrategy {
|
||||||
twoFactorService,
|
twoFactorService,
|
||||||
userDecryptionOptionsService,
|
userDecryptionOptionsService,
|
||||||
billingAccountProfileStateService,
|
billingAccountProfileStateService,
|
||||||
|
kdfConfigService,
|
||||||
);
|
);
|
||||||
|
|
||||||
this.cache = new BehaviorSubject(data);
|
this.cache = new BehaviorSubject(data);
|
||||||
|
|
|
@ -2,6 +2,7 @@ import { mock, MockProxy } from "jest-mock-extended";
|
||||||
|
|
||||||
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 { KdfConfigService } from "@bitwarden/common/auth/abstractions/kdf-config.service";
|
||||||
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 { TwoFactorProviderType } from "@bitwarden/common/auth/enums/two-factor-provider-type";
|
import { TwoFactorProviderType } from "@bitwarden/common/auth/enums/two-factor-provider-type";
|
||||||
|
@ -24,11 +25,7 @@ import { MessagingService } from "@bitwarden/common/platform/abstractions/messag
|
||||||
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 { Utils } from "@bitwarden/common/platform/misc/utils";
|
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
||||||
import {
|
import { Account, AccountProfile } from "@bitwarden/common/platform/models/domain/account";
|
||||||
Account,
|
|
||||||
AccountProfile,
|
|
||||||
AccountKeys,
|
|
||||||
} from "@bitwarden/common/platform/models/domain/account";
|
|
||||||
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 { FakeAccountService, mockAccountServiceWith } from "@bitwarden/common/spec";
|
||||||
|
@ -117,6 +114,7 @@ describe("LoginStrategy", () => {
|
||||||
let policyService: MockProxy<PolicyService>;
|
let policyService: MockProxy<PolicyService>;
|
||||||
let passwordStrengthService: MockProxy<PasswordStrengthServiceAbstraction>;
|
let passwordStrengthService: MockProxy<PasswordStrengthServiceAbstraction>;
|
||||||
let billingAccountProfileStateService: MockProxy<BillingAccountProfileStateService>;
|
let billingAccountProfileStateService: MockProxy<BillingAccountProfileStateService>;
|
||||||
|
let kdfConfigService: MockProxy<KdfConfigService>;
|
||||||
|
|
||||||
let passwordLoginStrategy: PasswordLoginStrategy;
|
let passwordLoginStrategy: PasswordLoginStrategy;
|
||||||
let credentials: PasswordLoginCredentials;
|
let credentials: PasswordLoginCredentials;
|
||||||
|
@ -136,6 +134,7 @@ describe("LoginStrategy", () => {
|
||||||
stateService = mock<StateService>();
|
stateService = mock<StateService>();
|
||||||
twoFactorService = mock<TwoFactorService>();
|
twoFactorService = mock<TwoFactorService>();
|
||||||
userDecryptionOptionsService = mock<InternalUserDecryptionOptionsServiceAbstraction>();
|
userDecryptionOptionsService = mock<InternalUserDecryptionOptionsServiceAbstraction>();
|
||||||
|
kdfConfigService = mock<KdfConfigService>();
|
||||||
policyService = mock<PolicyService>();
|
policyService = mock<PolicyService>();
|
||||||
passwordStrengthService = mock<PasswordStrengthService>();
|
passwordStrengthService = mock<PasswordStrengthService>();
|
||||||
billingAccountProfileStateService = mock<BillingAccountProfileStateService>();
|
billingAccountProfileStateService = mock<BillingAccountProfileStateService>();
|
||||||
|
@ -162,6 +161,7 @@ describe("LoginStrategy", () => {
|
||||||
policyService,
|
policyService,
|
||||||
loginStrategyService,
|
loginStrategyService,
|
||||||
billingAccountProfileStateService,
|
billingAccountProfileStateService,
|
||||||
|
kdfConfigService,
|
||||||
);
|
);
|
||||||
credentials = new PasswordLoginCredentials(email, masterPassword);
|
credentials = new PasswordLoginCredentials(email, masterPassword);
|
||||||
});
|
});
|
||||||
|
@ -208,11 +208,8 @@ describe("LoginStrategy", () => {
|
||||||
userId: userId,
|
userId: userId,
|
||||||
name: name,
|
name: name,
|
||||||
email: email,
|
email: email,
|
||||||
kdfIterations: kdfIterations,
|
|
||||||
kdfType: kdf,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
keys: new AccountKeys(),
|
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
expect(userDecryptionOptionsService.setUserDecryptionOptions).toHaveBeenCalledWith(
|
expect(userDecryptionOptionsService.setUserDecryptionOptions).toHaveBeenCalledWith(
|
||||||
|
@ -221,6 +218,21 @@ describe("LoginStrategy", () => {
|
||||||
expect(messagingService.send).toHaveBeenCalledWith("loggedIn");
|
expect(messagingService.send).toHaveBeenCalledWith("loggedIn");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("throws if active account isn't found after being initialized", async () => {
|
||||||
|
const idTokenResponse = identityTokenResponseFactory();
|
||||||
|
apiService.postIdentityToken.mockResolvedValue(idTokenResponse);
|
||||||
|
|
||||||
|
const mockVaultTimeoutAction = VaultTimeoutAction.Lock;
|
||||||
|
const mockVaultTimeout = 1000;
|
||||||
|
|
||||||
|
stateService.getVaultTimeoutAction.mockResolvedValue(mockVaultTimeoutAction);
|
||||||
|
stateService.getVaultTimeout.mockResolvedValue(mockVaultTimeout);
|
||||||
|
|
||||||
|
accountService.activeAccountSubject.next(null);
|
||||||
|
|
||||||
|
await expect(async () => await passwordLoginStrategy.logIn(credentials)).rejects.toThrow();
|
||||||
|
});
|
||||||
|
|
||||||
it("builds AuthResult", async () => {
|
it("builds AuthResult", async () => {
|
||||||
const tokenResponse = identityTokenResponseFactory();
|
const tokenResponse = identityTokenResponseFactory();
|
||||||
tokenResponse.forcePasswordReset = true;
|
tokenResponse.forcePasswordReset = true;
|
||||||
|
@ -304,8 +316,10 @@ describe("LoginStrategy", () => {
|
||||||
expect(tokenService.clearTwoFactorToken).toHaveBeenCalled();
|
expect(tokenService.clearTwoFactorToken).toHaveBeenCalled();
|
||||||
|
|
||||||
const expected = new AuthResult();
|
const expected = new AuthResult();
|
||||||
expected.twoFactorProviders = new Map<TwoFactorProviderType, { [key: string]: string }>();
|
expected.twoFactorProviders = { 0: null } as Record<
|
||||||
expected.twoFactorProviders.set(0, null);
|
TwoFactorProviderType,
|
||||||
|
Record<string, string>
|
||||||
|
>;
|
||||||
expect(result).toEqual(expected);
|
expect(result).toEqual(expected);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -334,8 +348,9 @@ describe("LoginStrategy", () => {
|
||||||
expect(messagingService.send).not.toHaveBeenCalled();
|
expect(messagingService.send).not.toHaveBeenCalled();
|
||||||
|
|
||||||
const expected = new AuthResult();
|
const expected = new AuthResult();
|
||||||
expected.twoFactorProviders = new Map<TwoFactorProviderType, { [key: string]: string }>();
|
expected.twoFactorProviders = {
|
||||||
expected.twoFactorProviders.set(1, { Email: "k***@bitwarden.com" });
|
[TwoFactorProviderType.Email]: { Email: "k***@bitwarden.com" },
|
||||||
|
};
|
||||||
expected.email = userEmail;
|
expected.email = userEmail;
|
||||||
expected.ssoEmail2FaSessionToken = ssoEmail2FaSessionToken;
|
expected.ssoEmail2FaSessionToken = ssoEmail2FaSessionToken;
|
||||||
|
|
||||||
|
@ -404,6 +419,7 @@ describe("LoginStrategy", () => {
|
||||||
policyService,
|
policyService,
|
||||||
loginStrategyService,
|
loginStrategyService,
|
||||||
billingAccountProfileStateService,
|
billingAccountProfileStateService,
|
||||||
|
kdfConfigService,
|
||||||
);
|
);
|
||||||
|
|
||||||
apiService.postIdentityToken.mockResolvedValue(identityTokenResponseFactory());
|
apiService.postIdentityToken.mockResolvedValue(identityTokenResponseFactory());
|
||||||
|
|
|
@ -1,13 +1,15 @@
|
||||||
import { BehaviorSubject } from "rxjs";
|
import { BehaviorSubject, filter, firstValueFrom, timeout } from "rxjs";
|
||||||
|
|
||||||
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 { 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";
|
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 { 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";
|
||||||
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 { Argon2KdfConfig, PBKDF2KdfConfig } from "@bitwarden/common/auth/models/domain/kdf-config";
|
||||||
import { DeviceRequest } from "@bitwarden/common/auth/models/request/identity-token/device.request";
|
import { DeviceRequest } from "@bitwarden/common/auth/models/request/identity-token/device.request";
|
||||||
import { PasswordTokenRequest } from "@bitwarden/common/auth/models/request/identity-token/password-token.request";
|
import { PasswordTokenRequest } from "@bitwarden/common/auth/models/request/identity-token/password-token.request";
|
||||||
import { SsoTokenRequest } from "@bitwarden/common/auth/models/request/identity-token/sso-token.request";
|
import { SsoTokenRequest } from "@bitwarden/common/auth/models/request/identity-token/sso-token.request";
|
||||||
|
@ -27,6 +29,7 @@ import { LogService } from "@bitwarden/common/platform/abstractions/log.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 { KdfType } from "@bitwarden/common/platform/enums";
|
||||||
import { Account, AccountProfile } from "@bitwarden/common/platform/models/domain/account";
|
import { Account, AccountProfile } from "@bitwarden/common/platform/models/domain/account";
|
||||||
import { UserId } from "@bitwarden/common/types/guid";
|
import { UserId } from "@bitwarden/common/types/guid";
|
||||||
|
|
||||||
|
@ -72,6 +75,7 @@ export abstract class LoginStrategy {
|
||||||
protected twoFactorService: TwoFactorService,
|
protected twoFactorService: TwoFactorService,
|
||||||
protected userDecryptionOptionsService: InternalUserDecryptionOptionsServiceAbstraction,
|
protected userDecryptionOptionsService: InternalUserDecryptionOptionsServiceAbstraction,
|
||||||
protected billingAccountProfileStateService: BillingAccountProfileStateService,
|
protected billingAccountProfileStateService: BillingAccountProfileStateService,
|
||||||
|
protected KdfConfigService: KdfConfigService,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
abstract exportCache(): CacheData;
|
abstract exportCache(): CacheData;
|
||||||
|
@ -97,7 +101,7 @@ export abstract class LoginStrategy {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async startLogIn(): Promise<[AuthResult, IdentityResponse]> {
|
protected async startLogIn(): Promise<[AuthResult, IdentityResponse]> {
|
||||||
this.twoFactorService.clearSelectedProvider();
|
await this.twoFactorService.clearSelectedProvider();
|
||||||
|
|
||||||
const tokenRequest = this.cache.value.tokenRequest;
|
const tokenRequest = this.cache.value.tokenRequest;
|
||||||
const response = await this.apiService.postIdentityToken(tokenRequest);
|
const response = await this.apiService.postIdentityToken(tokenRequest);
|
||||||
|
@ -155,12 +159,12 @@ export abstract class LoginStrategy {
|
||||||
* It also sets the access token and refresh token in the token service.
|
* It also sets the access token and refresh token in the token service.
|
||||||
*
|
*
|
||||||
* @param {IdentityTokenResponse} tokenResponse - The response from the server containing the identity token.
|
* @param {IdentityTokenResponse} tokenResponse - The response from the server containing the identity token.
|
||||||
* @returns {Promise<void>} - A promise that resolves when the account information has been successfully saved.
|
* @returns {Promise<UserId>} - A promise that resolves the the UserId when the account information has been successfully saved.
|
||||||
*/
|
*/
|
||||||
protected async saveAccountInformation(tokenResponse: IdentityTokenResponse): Promise<UserId> {
|
protected async saveAccountInformation(tokenResponse: IdentityTokenResponse): Promise<UserId> {
|
||||||
const accountInformation = await this.tokenService.decodeAccessToken(tokenResponse.accessToken);
|
const accountInformation = await this.tokenService.decodeAccessToken(tokenResponse.accessToken);
|
||||||
|
|
||||||
const userId = accountInformation.sub;
|
const userId = accountInformation.sub as UserId;
|
||||||
|
|
||||||
const vaultTimeoutAction = await this.stateService.getVaultTimeoutAction({ userId });
|
const vaultTimeoutAction = await this.stateService.getVaultTimeoutAction({ userId });
|
||||||
const vaultTimeout = await this.stateService.getVaultTimeout({ userId });
|
const vaultTimeout = await this.stateService.getVaultTimeout({ userId });
|
||||||
|
@ -182,21 +186,30 @@ export abstract class LoginStrategy {
|
||||||
userId,
|
userId,
|
||||||
name: accountInformation.name,
|
name: accountInformation.name,
|
||||||
email: accountInformation.email,
|
email: accountInformation.email,
|
||||||
kdfIterations: tokenResponse.kdfIterations,
|
|
||||||
kdfMemory: tokenResponse.kdfMemory,
|
|
||||||
kdfParallelism: tokenResponse.kdfParallelism,
|
|
||||||
kdfType: tokenResponse.kdf,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
await this.verifyAccountAdded(userId);
|
||||||
|
|
||||||
await this.userDecryptionOptionsService.setUserDecryptionOptions(
|
await this.userDecryptionOptionsService.setUserDecryptionOptions(
|
||||||
UserDecryptionOptions.fromResponse(tokenResponse),
|
UserDecryptionOptions.fromResponse(tokenResponse),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
await this.KdfConfigService.setKdfConfig(
|
||||||
|
userId as UserId,
|
||||||
|
tokenResponse.kdf === KdfType.PBKDF2_SHA256
|
||||||
|
? new PBKDF2KdfConfig(tokenResponse.kdfIterations)
|
||||||
|
: new Argon2KdfConfig(
|
||||||
|
tokenResponse.kdfIterations,
|
||||||
|
tokenResponse.kdfMemory,
|
||||||
|
tokenResponse.kdfParallelism,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
await this.billingAccountProfileStateService.setHasPremium(accountInformation.premium, false);
|
await this.billingAccountProfileStateService.setHasPremium(accountInformation.premium, false);
|
||||||
return userId as UserId;
|
return userId;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async processTokenResponse(response: IdentityTokenResponse): Promise<AuthResult> {
|
protected async processTokenResponse(response: IdentityTokenResponse): Promise<AuthResult> {
|
||||||
|
@ -273,7 +286,7 @@ export abstract class LoginStrategy {
|
||||||
const result = new AuthResult();
|
const result = new AuthResult();
|
||||||
result.twoFactorProviders = response.twoFactorProviders2;
|
result.twoFactorProviders = response.twoFactorProviders2;
|
||||||
|
|
||||||
this.twoFactorService.setProviders(response);
|
await this.twoFactorService.setProviders(response);
|
||||||
this.cache.next({ ...this.cache.value, captchaBypassToken: response.captchaToken ?? null });
|
this.cache.next({ ...this.cache.value, captchaBypassToken: response.captchaToken ?? null });
|
||||||
result.ssoEmail2FaSessionToken = response.ssoEmail2faSessionToken;
|
result.ssoEmail2FaSessionToken = response.ssoEmail2faSessionToken;
|
||||||
result.email = response.email;
|
result.email = response.email;
|
||||||
|
@ -295,4 +308,24 @@ export abstract class LoginStrategy {
|
||||||
result.captchaSiteKey = response.siteKey;
|
result.captchaSiteKey = response.siteKey;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verifies that the active account is set after initialization.
|
||||||
|
* Note: In browser there is a slight delay between when active account emits in background,
|
||||||
|
* and when it emits in foreground. We're giving the foreground 1 second to catch up.
|
||||||
|
* If nothing is emitted, we throw an error.
|
||||||
|
*/
|
||||||
|
private async verifyAccountAdded(expectedUserId: UserId) {
|
||||||
|
await firstValueFrom(
|
||||||
|
this.accountService.activeAccount$.pipe(
|
||||||
|
filter((account) => account?.id === expectedUserId),
|
||||||
|
timeout({
|
||||||
|
first: 1000,
|
||||||
|
with: () => {
|
||||||
|
throw new Error("Expected user never made active user after initialization.");
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ import { mock, MockProxy } from "jest-mock-extended";
|
||||||
|
|
||||||
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 { KdfConfigService } from "@bitwarden/common/auth/abstractions/kdf-config.service";
|
||||||
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 { TwoFactorProviderType } from "@bitwarden/common/auth/enums/two-factor-provider-type";
|
import { TwoFactorProviderType } from "@bitwarden/common/auth/enums/two-factor-provider-type";
|
||||||
|
@ -71,6 +72,7 @@ describe("PasswordLoginStrategy", () => {
|
||||||
let policyService: MockProxy<PolicyService>;
|
let policyService: MockProxy<PolicyService>;
|
||||||
let passwordStrengthService: MockProxy<PasswordStrengthServiceAbstraction>;
|
let passwordStrengthService: MockProxy<PasswordStrengthServiceAbstraction>;
|
||||||
let billingAccountProfileStateService: MockProxy<BillingAccountProfileStateService>;
|
let billingAccountProfileStateService: MockProxy<BillingAccountProfileStateService>;
|
||||||
|
let kdfConfigService: MockProxy<KdfConfigService>;
|
||||||
|
|
||||||
let passwordLoginStrategy: PasswordLoginStrategy;
|
let passwordLoginStrategy: PasswordLoginStrategy;
|
||||||
let credentials: PasswordLoginCredentials;
|
let credentials: PasswordLoginCredentials;
|
||||||
|
@ -94,9 +96,12 @@ describe("PasswordLoginStrategy", () => {
|
||||||
policyService = mock<PolicyService>();
|
policyService = mock<PolicyService>();
|
||||||
passwordStrengthService = mock<PasswordStrengthService>();
|
passwordStrengthService = mock<PasswordStrengthService>();
|
||||||
billingAccountProfileStateService = mock<BillingAccountProfileStateService>();
|
billingAccountProfileStateService = mock<BillingAccountProfileStateService>();
|
||||||
|
kdfConfigService = mock<KdfConfigService>();
|
||||||
|
|
||||||
appIdService.getAppId.mockResolvedValue(deviceId);
|
appIdService.getAppId.mockResolvedValue(deviceId);
|
||||||
tokenService.decodeAccessToken.mockResolvedValue({});
|
tokenService.decodeAccessToken.mockResolvedValue({
|
||||||
|
sub: userId,
|
||||||
|
});
|
||||||
|
|
||||||
loginStrategyService.makePreloginKey.mockResolvedValue(masterKey);
|
loginStrategyService.makePreloginKey.mockResolvedValue(masterKey);
|
||||||
|
|
||||||
|
@ -127,6 +132,7 @@ describe("PasswordLoginStrategy", () => {
|
||||||
policyService,
|
policyService,
|
||||||
loginStrategyService,
|
loginStrategyService,
|
||||||
billingAccountProfileStateService,
|
billingAccountProfileStateService,
|
||||||
|
kdfConfigService,
|
||||||
);
|
);
|
||||||
credentials = new PasswordLoginCredentials(email, masterPassword);
|
credentials = new PasswordLoginCredentials(email, masterPassword);
|
||||||
tokenResponse = identityTokenResponseFactory(masterPasswordPolicy);
|
tokenResponse = identityTokenResponseFactory(masterPasswordPolicy);
|
||||||
|
|
|
@ -5,6 +5,7 @@ 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 { 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";
|
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 { TwoFactorService } from "@bitwarden/common/auth/abstractions/two-factor.service";
|
import { TwoFactorService } from "@bitwarden/common/auth/abstractions/two-factor.service";
|
||||||
|
@ -89,6 +90,7 @@ export class PasswordLoginStrategy extends LoginStrategy {
|
||||||
private policyService: PolicyService,
|
private policyService: PolicyService,
|
||||||
private loginStrategyService: LoginStrategyServiceAbstraction,
|
private loginStrategyService: LoginStrategyServiceAbstraction,
|
||||||
billingAccountProfileStateService: BillingAccountProfileStateService,
|
billingAccountProfileStateService: BillingAccountProfileStateService,
|
||||||
|
kdfConfigService: KdfConfigService,
|
||||||
) {
|
) {
|
||||||
super(
|
super(
|
||||||
accountService,
|
accountService,
|
||||||
|
@ -104,6 +106,7 @@ export class PasswordLoginStrategy extends LoginStrategy {
|
||||||
twoFactorService,
|
twoFactorService,
|
||||||
userDecryptionOptionsService,
|
userDecryptionOptionsService,
|
||||||
billingAccountProfileStateService,
|
billingAccountProfileStateService,
|
||||||
|
kdfConfigService,
|
||||||
);
|
);
|
||||||
|
|
||||||
this.cache = new BehaviorSubject(data);
|
this.cache = new BehaviorSubject(data);
|
||||||
|
|
|
@ -2,6 +2,7 @@ import { mock, MockProxy } from "jest-mock-extended";
|
||||||
|
|
||||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||||
import { DeviceTrustServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust.service.abstraction";
|
import { DeviceTrustServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust.service.abstraction";
|
||||||
|
import { KdfConfigService } from "@bitwarden/common/auth/abstractions/kdf-config.service";
|
||||||
import { KeyConnectorService } from "@bitwarden/common/auth/abstractions/key-connector.service";
|
import { KeyConnectorService } from "@bitwarden/common/auth/abstractions/key-connector.service";
|
||||||
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";
|
||||||
|
@ -54,6 +55,7 @@ describe("SsoLoginStrategy", () => {
|
||||||
let authRequestService: MockProxy<AuthRequestServiceAbstraction>;
|
let authRequestService: MockProxy<AuthRequestServiceAbstraction>;
|
||||||
let i18nService: MockProxy<I18nService>;
|
let i18nService: MockProxy<I18nService>;
|
||||||
let billingAccountProfileStateService: MockProxy<BillingAccountProfileStateService>;
|
let billingAccountProfileStateService: MockProxy<BillingAccountProfileStateService>;
|
||||||
|
let kdfConfigService: MockProxy<KdfConfigService>;
|
||||||
|
|
||||||
let ssoLoginStrategy: SsoLoginStrategy;
|
let ssoLoginStrategy: SsoLoginStrategy;
|
||||||
let credentials: SsoLoginCredentials;
|
let credentials: SsoLoginCredentials;
|
||||||
|
@ -86,10 +88,13 @@ describe("SsoLoginStrategy", () => {
|
||||||
authRequestService = mock<AuthRequestServiceAbstraction>();
|
authRequestService = mock<AuthRequestServiceAbstraction>();
|
||||||
i18nService = mock<I18nService>();
|
i18nService = mock<I18nService>();
|
||||||
billingAccountProfileStateService = mock<BillingAccountProfileStateService>();
|
billingAccountProfileStateService = mock<BillingAccountProfileStateService>();
|
||||||
|
kdfConfigService = mock<KdfConfigService>();
|
||||||
|
|
||||||
tokenService.getTwoFactorToken.mockResolvedValue(null);
|
tokenService.getTwoFactorToken.mockResolvedValue(null);
|
||||||
appIdService.getAppId.mockResolvedValue(deviceId);
|
appIdService.getAppId.mockResolvedValue(deviceId);
|
||||||
tokenService.decodeAccessToken.mockResolvedValue({});
|
tokenService.decodeAccessToken.mockResolvedValue({
|
||||||
|
sub: userId,
|
||||||
|
});
|
||||||
|
|
||||||
ssoLoginStrategy = new SsoLoginStrategy(
|
ssoLoginStrategy = new SsoLoginStrategy(
|
||||||
null,
|
null,
|
||||||
|
@ -110,6 +115,7 @@ describe("SsoLoginStrategy", () => {
|
||||||
authRequestService,
|
authRequestService,
|
||||||
i18nService,
|
i18nService,
|
||||||
billingAccountProfileStateService,
|
billingAccountProfileStateService,
|
||||||
|
kdfConfigService,
|
||||||
);
|
);
|
||||||
credentials = new SsoLoginCredentials(ssoCode, ssoCodeVerifier, ssoRedirectUrl, ssoOrgId);
|
credentials = new SsoLoginCredentials(ssoCode, ssoCodeVerifier, ssoRedirectUrl, ssoOrgId);
|
||||||
});
|
});
|
||||||
|
|
|
@ -3,6 +3,7 @@ import { Jsonify } from "type-fest";
|
||||||
|
|
||||||
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 { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||||
|
import { KdfConfigService } from "@bitwarden/common/auth/abstractions/kdf-config.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 { 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";
|
||||||
|
@ -98,6 +99,7 @@ export class SsoLoginStrategy extends LoginStrategy {
|
||||||
private authRequestService: AuthRequestServiceAbstraction,
|
private authRequestService: AuthRequestServiceAbstraction,
|
||||||
private i18nService: I18nService,
|
private i18nService: I18nService,
|
||||||
billingAccountProfileStateService: BillingAccountProfileStateService,
|
billingAccountProfileStateService: BillingAccountProfileStateService,
|
||||||
|
kdfConfigService: KdfConfigService,
|
||||||
) {
|
) {
|
||||||
super(
|
super(
|
||||||
accountService,
|
accountService,
|
||||||
|
@ -113,6 +115,7 @@ export class SsoLoginStrategy extends LoginStrategy {
|
||||||
twoFactorService,
|
twoFactorService,
|
||||||
userDecryptionOptionsService,
|
userDecryptionOptionsService,
|
||||||
billingAccountProfileStateService,
|
billingAccountProfileStateService,
|
||||||
|
kdfConfigService,
|
||||||
);
|
);
|
||||||
|
|
||||||
this.cache = new BehaviorSubject(data);
|
this.cache = new BehaviorSubject(data);
|
||||||
|
|
|
@ -2,6 +2,7 @@ import { mock, MockProxy } from "jest-mock-extended";
|
||||||
import { BehaviorSubject } from "rxjs";
|
import { BehaviorSubject } from "rxjs";
|
||||||
|
|
||||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||||
|
import { KdfConfigService } from "@bitwarden/common/auth/abstractions/kdf-config.service";
|
||||||
import { KeyConnectorService } from "@bitwarden/common/auth/abstractions/key-connector.service";
|
import { KeyConnectorService } from "@bitwarden/common/auth/abstractions/key-connector.service";
|
||||||
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";
|
||||||
|
@ -49,6 +50,7 @@ describe("UserApiLoginStrategy", () => {
|
||||||
let keyConnectorService: MockProxy<KeyConnectorService>;
|
let keyConnectorService: MockProxy<KeyConnectorService>;
|
||||||
let environmentService: MockProxy<EnvironmentService>;
|
let environmentService: MockProxy<EnvironmentService>;
|
||||||
let billingAccountProfileStateService: MockProxy<BillingAccountProfileStateService>;
|
let billingAccountProfileStateService: MockProxy<BillingAccountProfileStateService>;
|
||||||
|
let kdfConfigService: MockProxy<KdfConfigService>;
|
||||||
|
|
||||||
let apiLogInStrategy: UserApiLoginStrategy;
|
let apiLogInStrategy: UserApiLoginStrategy;
|
||||||
let credentials: UserApiLoginCredentials;
|
let credentials: UserApiLoginCredentials;
|
||||||
|
@ -76,10 +78,13 @@ describe("UserApiLoginStrategy", () => {
|
||||||
keyConnectorService = mock<KeyConnectorService>();
|
keyConnectorService = mock<KeyConnectorService>();
|
||||||
environmentService = mock<EnvironmentService>();
|
environmentService = mock<EnvironmentService>();
|
||||||
billingAccountProfileStateService = mock<BillingAccountProfileStateService>();
|
billingAccountProfileStateService = mock<BillingAccountProfileStateService>();
|
||||||
|
kdfConfigService = mock<KdfConfigService>();
|
||||||
|
|
||||||
appIdService.getAppId.mockResolvedValue(deviceId);
|
appIdService.getAppId.mockResolvedValue(deviceId);
|
||||||
tokenService.getTwoFactorToken.mockResolvedValue(null);
|
tokenService.getTwoFactorToken.mockResolvedValue(null);
|
||||||
tokenService.decodeAccessToken.mockResolvedValue({});
|
tokenService.decodeAccessToken.mockResolvedValue({
|
||||||
|
sub: userId,
|
||||||
|
});
|
||||||
|
|
||||||
apiLogInStrategy = new UserApiLoginStrategy(
|
apiLogInStrategy = new UserApiLoginStrategy(
|
||||||
cache,
|
cache,
|
||||||
|
@ -98,6 +103,7 @@ describe("UserApiLoginStrategy", () => {
|
||||||
environmentService,
|
environmentService,
|
||||||
keyConnectorService,
|
keyConnectorService,
|
||||||
billingAccountProfileStateService,
|
billingAccountProfileStateService,
|
||||||
|
kdfConfigService,
|
||||||
);
|
);
|
||||||
|
|
||||||
credentials = new UserApiLoginCredentials(apiClientId, apiClientSecret);
|
credentials = new UserApiLoginCredentials(apiClientId, apiClientSecret);
|
||||||
|
|
|
@ -3,6 +3,7 @@ import { Jsonify } from "type-fest";
|
||||||
|
|
||||||
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 { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||||
|
import { KdfConfigService } from "@bitwarden/common/auth/abstractions/kdf-config.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 { 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";
|
||||||
|
@ -57,6 +58,7 @@ export class UserApiLoginStrategy extends LoginStrategy {
|
||||||
private environmentService: EnvironmentService,
|
private environmentService: EnvironmentService,
|
||||||
private keyConnectorService: KeyConnectorService,
|
private keyConnectorService: KeyConnectorService,
|
||||||
billingAccountProfileStateService: BillingAccountProfileStateService,
|
billingAccountProfileStateService: BillingAccountProfileStateService,
|
||||||
|
protected kdfConfigService: KdfConfigService,
|
||||||
) {
|
) {
|
||||||
super(
|
super(
|
||||||
accountService,
|
accountService,
|
||||||
|
@ -72,6 +74,7 @@ export class UserApiLoginStrategy extends LoginStrategy {
|
||||||
twoFactorService,
|
twoFactorService,
|
||||||
userDecryptionOptionsService,
|
userDecryptionOptionsService,
|
||||||
billingAccountProfileStateService,
|
billingAccountProfileStateService,
|
||||||
|
kdfConfigService,
|
||||||
);
|
);
|
||||||
this.cache = new BehaviorSubject(data);
|
this.cache = new BehaviorSubject(data);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { mock, MockProxy } from "jest-mock-extended";
|
import { mock, MockProxy } from "jest-mock-extended";
|
||||||
|
|
||||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||||
|
import { KdfConfigService } from "@bitwarden/common/auth/abstractions/kdf-config.service";
|
||||||
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 { AuthResult } from "@bitwarden/common/auth/models/domain/auth-result";
|
import { AuthResult } from "@bitwarden/common/auth/models/domain/auth-result";
|
||||||
|
@ -17,7 +18,8 @@ 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 } from "@bitwarden/common/spec";
|
import { FakeAccountService, mockAccountServiceWith } from "@bitwarden/common/spec";
|
||||||
|
import { UserId } from "@bitwarden/common/types/guid";
|
||||||
import { PrfKey, UserKey } from "@bitwarden/common/types/key";
|
import { PrfKey, 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,11 +44,13 @@ describe("WebAuthnLoginStrategy", () => {
|
||||||
let twoFactorService!: MockProxy<TwoFactorService>;
|
let twoFactorService!: MockProxy<TwoFactorService>;
|
||||||
let userDecryptionOptionsService: MockProxy<InternalUserDecryptionOptionsServiceAbstraction>;
|
let userDecryptionOptionsService: MockProxy<InternalUserDecryptionOptionsServiceAbstraction>;
|
||||||
let billingAccountProfileStateService: MockProxy<BillingAccountProfileStateService>;
|
let billingAccountProfileStateService: MockProxy<BillingAccountProfileStateService>;
|
||||||
|
let kdfConfigService: MockProxy<KdfConfigService>;
|
||||||
|
|
||||||
let webAuthnLoginStrategy!: WebAuthnLoginStrategy;
|
let webAuthnLoginStrategy!: WebAuthnLoginStrategy;
|
||||||
|
|
||||||
const token = "mockToken";
|
const token = "mockToken";
|
||||||
const deviceId = Utils.newGuid();
|
const deviceId = Utils.newGuid();
|
||||||
|
const userId = Utils.newGuid() as UserId;
|
||||||
|
|
||||||
let webAuthnCredentials!: WebAuthnLoginCredentials;
|
let webAuthnCredentials!: WebAuthnLoginCredentials;
|
||||||
|
|
||||||
|
@ -67,7 +71,7 @@ describe("WebAuthnLoginStrategy", () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
jest.clearAllMocks();
|
jest.clearAllMocks();
|
||||||
|
|
||||||
accountService = new FakeAccountService(null);
|
accountService = mockAccountServiceWith(userId);
|
||||||
masterPasswordService = new FakeMasterPasswordService();
|
masterPasswordService = new FakeMasterPasswordService();
|
||||||
|
|
||||||
cryptoService = mock<CryptoService>();
|
cryptoService = mock<CryptoService>();
|
||||||
|
@ -81,10 +85,13 @@ describe("WebAuthnLoginStrategy", () => {
|
||||||
twoFactorService = mock<TwoFactorService>();
|
twoFactorService = mock<TwoFactorService>();
|
||||||
userDecryptionOptionsService = mock<InternalUserDecryptionOptionsServiceAbstraction>();
|
userDecryptionOptionsService = mock<InternalUserDecryptionOptionsServiceAbstraction>();
|
||||||
billingAccountProfileStateService = mock<BillingAccountProfileStateService>();
|
billingAccountProfileStateService = mock<BillingAccountProfileStateService>();
|
||||||
|
kdfConfigService = mock<KdfConfigService>();
|
||||||
|
|
||||||
tokenService.getTwoFactorToken.mockResolvedValue(null);
|
tokenService.getTwoFactorToken.mockResolvedValue(null);
|
||||||
appIdService.getAppId.mockResolvedValue(deviceId);
|
appIdService.getAppId.mockResolvedValue(deviceId);
|
||||||
tokenService.decodeAccessToken.mockResolvedValue({});
|
tokenService.decodeAccessToken.mockResolvedValue({
|
||||||
|
sub: userId,
|
||||||
|
});
|
||||||
|
|
||||||
webAuthnLoginStrategy = new WebAuthnLoginStrategy(
|
webAuthnLoginStrategy = new WebAuthnLoginStrategy(
|
||||||
cache,
|
cache,
|
||||||
|
@ -101,6 +108,7 @@ describe("WebAuthnLoginStrategy", () => {
|
||||||
twoFactorService,
|
twoFactorService,
|
||||||
userDecryptionOptionsService,
|
userDecryptionOptionsService,
|
||||||
billingAccountProfileStateService,
|
billingAccountProfileStateService,
|
||||||
|
kdfConfigService,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Create credentials
|
// Create credentials
|
||||||
|
|
|
@ -3,6 +3,7 @@ import { Jsonify } from "type-fest";
|
||||||
|
|
||||||
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 { 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";
|
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 { TwoFactorService } from "@bitwarden/common/auth/abstractions/two-factor.service";
|
import { TwoFactorService } from "@bitwarden/common/auth/abstractions/two-factor.service";
|
||||||
|
@ -57,6 +58,7 @@ export class WebAuthnLoginStrategy extends LoginStrategy {
|
||||||
twoFactorService: TwoFactorService,
|
twoFactorService: TwoFactorService,
|
||||||
userDecryptionOptionsService: InternalUserDecryptionOptionsServiceAbstraction,
|
userDecryptionOptionsService: InternalUserDecryptionOptionsServiceAbstraction,
|
||||||
billingAccountProfileStateService: BillingAccountProfileStateService,
|
billingAccountProfileStateService: BillingAccountProfileStateService,
|
||||||
|
kdfConfigService: KdfConfigService,
|
||||||
) {
|
) {
|
||||||
super(
|
super(
|
||||||
accountService,
|
accountService,
|
||||||
|
@ -72,6 +74,7 @@ export class WebAuthnLoginStrategy extends LoginStrategy {
|
||||||
twoFactorService,
|
twoFactorService,
|
||||||
userDecryptionOptionsService,
|
userDecryptionOptionsService,
|
||||||
billingAccountProfileStateService,
|
billingAccountProfileStateService,
|
||||||
|
kdfConfigService,
|
||||||
);
|
);
|
||||||
|
|
||||||
this.cache = new BehaviorSubject(data);
|
this.cache = new BehaviorSubject(data);
|
||||||
|
|
|
@ -3,6 +3,7 @@ import { MockProxy, mock } from "jest-mock-extended";
|
||||||
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 { DeviceTrustServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust.service.abstraction";
|
import { DeviceTrustServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust.service.abstraction";
|
||||||
|
import { KdfConfigService } from "@bitwarden/common/auth/abstractions/kdf-config.service";
|
||||||
import { KeyConnectorService } from "@bitwarden/common/auth/abstractions/key-connector.service";
|
import { KeyConnectorService } from "@bitwarden/common/auth/abstractions/key-connector.service";
|
||||||
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";
|
||||||
|
@ -66,6 +67,7 @@ describe("LoginStrategyService", () => {
|
||||||
let authRequestService: MockProxy<AuthRequestServiceAbstraction>;
|
let authRequestService: MockProxy<AuthRequestServiceAbstraction>;
|
||||||
let userDecryptionOptionsService: MockProxy<InternalUserDecryptionOptionsServiceAbstraction>;
|
let userDecryptionOptionsService: MockProxy<InternalUserDecryptionOptionsServiceAbstraction>;
|
||||||
let billingAccountProfileStateService: MockProxy<BillingAccountProfileStateService>;
|
let billingAccountProfileStateService: MockProxy<BillingAccountProfileStateService>;
|
||||||
|
let kdfConfigService: MockProxy<KdfConfigService>;
|
||||||
|
|
||||||
let stateProvider: FakeGlobalStateProvider;
|
let stateProvider: FakeGlobalStateProvider;
|
||||||
let loginStrategyCacheExpirationState: FakeGlobalState<Date | null>;
|
let loginStrategyCacheExpirationState: FakeGlobalState<Date | null>;
|
||||||
|
@ -95,6 +97,7 @@ describe("LoginStrategyService", () => {
|
||||||
userDecryptionOptionsService = mock<UserDecryptionOptionsService>();
|
userDecryptionOptionsService = mock<UserDecryptionOptionsService>();
|
||||||
billingAccountProfileStateService = mock<BillingAccountProfileStateService>();
|
billingAccountProfileStateService = mock<BillingAccountProfileStateService>();
|
||||||
stateProvider = new FakeGlobalStateProvider();
|
stateProvider = new FakeGlobalStateProvider();
|
||||||
|
kdfConfigService = mock<KdfConfigService>();
|
||||||
|
|
||||||
sut = new LoginStrategyService(
|
sut = new LoginStrategyService(
|
||||||
accountService,
|
accountService,
|
||||||
|
@ -119,6 +122,7 @@ describe("LoginStrategyService", () => {
|
||||||
userDecryptionOptionsService,
|
userDecryptionOptionsService,
|
||||||
stateProvider,
|
stateProvider,
|
||||||
billingAccountProfileStateService,
|
billingAccountProfileStateService,
|
||||||
|
kdfConfigService,
|
||||||
);
|
);
|
||||||
|
|
||||||
loginStrategyCacheExpirationState = stateProvider.getFake(CACHE_EXPIRATION_KEY);
|
loginStrategyCacheExpirationState = stateProvider.getFake(CACHE_EXPIRATION_KEY);
|
||||||
|
|
|
@ -10,13 +10,18 @@ import {
|
||||||
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 { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||||
|
import { KdfConfigService } from "@bitwarden/common/auth/abstractions/kdf-config.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 { 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 { 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";
|
||||||
import { AuthResult } from "@bitwarden/common/auth/models/domain/auth-result";
|
import { AuthResult } from "@bitwarden/common/auth/models/domain/auth-result";
|
||||||
import { KdfConfig } from "@bitwarden/common/auth/models/domain/kdf-config";
|
import {
|
||||||
|
Argon2KdfConfig,
|
||||||
|
KdfConfig,
|
||||||
|
PBKDF2KdfConfig,
|
||||||
|
} from "@bitwarden/common/auth/models/domain/kdf-config";
|
||||||
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 { PasswordlessAuthRequest } from "@bitwarden/common/auth/models/request/passwordless-auth.request";
|
import { PasswordlessAuthRequest } from "@bitwarden/common/auth/models/request/passwordless-auth.request";
|
||||||
import { AuthRequestResponse } from "@bitwarden/common/auth/models/response/auth-request.response";
|
import { AuthRequestResponse } from "@bitwarden/common/auth/models/response/auth-request.response";
|
||||||
|
@ -32,7 +37,7 @@ import { LogService } from "@bitwarden/common/platform/abstractions/log.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 { KdfType } from "@bitwarden/common/platform/enums";
|
import { KdfType } from "@bitwarden/common/platform/enums/kdf-type.enum";
|
||||||
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
||||||
import { GlobalState, GlobalStateProvider } from "@bitwarden/common/platform/state";
|
import { GlobalState, GlobalStateProvider } from "@bitwarden/common/platform/state";
|
||||||
import { DeviceTrustServiceAbstraction } from "@bitwarden/common/src/auth/abstractions/device-trust.service.abstraction";
|
import { DeviceTrustServiceAbstraction } from "@bitwarden/common/src/auth/abstractions/device-trust.service.abstraction";
|
||||||
|
@ -105,6 +110,7 @@ export class LoginStrategyService implements LoginStrategyServiceAbstraction {
|
||||||
protected userDecryptionOptionsService: InternalUserDecryptionOptionsServiceAbstraction,
|
protected userDecryptionOptionsService: InternalUserDecryptionOptionsServiceAbstraction,
|
||||||
protected stateProvider: GlobalStateProvider,
|
protected stateProvider: GlobalStateProvider,
|
||||||
protected billingAccountProfileStateService: BillingAccountProfileStateService,
|
protected billingAccountProfileStateService: BillingAccountProfileStateService,
|
||||||
|
protected kdfConfigService: KdfConfigService,
|
||||||
) {
|
) {
|
||||||
this.currentAuthnTypeState = this.stateProvider.get(CURRENT_LOGIN_STRATEGY_KEY);
|
this.currentAuthnTypeState = this.stateProvider.get(CURRENT_LOGIN_STRATEGY_KEY);
|
||||||
this.loginStrategyCacheState = this.stateProvider.get(CACHE_KEY);
|
this.loginStrategyCacheState = this.stateProvider.get(CACHE_KEY);
|
||||||
|
@ -233,24 +239,25 @@ export class LoginStrategyService implements LoginStrategyServiceAbstraction {
|
||||||
|
|
||||||
async makePreloginKey(masterPassword: string, email: string): Promise<MasterKey> {
|
async makePreloginKey(masterPassword: string, email: string): Promise<MasterKey> {
|
||||||
email = email.trim().toLowerCase();
|
email = email.trim().toLowerCase();
|
||||||
let kdf: KdfType = null;
|
|
||||||
let kdfConfig: KdfConfig = null;
|
let kdfConfig: KdfConfig = null;
|
||||||
try {
|
try {
|
||||||
const preloginResponse = await this.apiService.postPrelogin(new PreloginRequest(email));
|
const preloginResponse = await this.apiService.postPrelogin(new PreloginRequest(email));
|
||||||
if (preloginResponse != null) {
|
if (preloginResponse != null) {
|
||||||
kdf = preloginResponse.kdf;
|
kdfConfig =
|
||||||
kdfConfig = new KdfConfig(
|
preloginResponse.kdf === KdfType.PBKDF2_SHA256
|
||||||
preloginResponse.kdfIterations,
|
? new PBKDF2KdfConfig(preloginResponse.kdfIterations)
|
||||||
preloginResponse.kdfMemory,
|
: new Argon2KdfConfig(
|
||||||
preloginResponse.kdfParallelism,
|
preloginResponse.kdfIterations,
|
||||||
);
|
preloginResponse.kdfMemory,
|
||||||
|
preloginResponse.kdfParallelism,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e == null || e.statusCode !== 404) {
|
if (e == null || e.statusCode !== 404) {
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return await this.cryptoService.makeMasterKey(masterPassword, email, kdf, kdfConfig);
|
return await this.cryptoService.makeMasterKey(masterPassword, email, kdfConfig);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: move to auth request service
|
// TODO: move to auth request service
|
||||||
|
@ -354,6 +361,7 @@ export class LoginStrategyService implements LoginStrategyServiceAbstraction {
|
||||||
this.policyService,
|
this.policyService,
|
||||||
this,
|
this,
|
||||||
this.billingAccountProfileStateService,
|
this.billingAccountProfileStateService,
|
||||||
|
this.kdfConfigService,
|
||||||
);
|
);
|
||||||
case AuthenticationType.Sso:
|
case AuthenticationType.Sso:
|
||||||
return new SsoLoginStrategy(
|
return new SsoLoginStrategy(
|
||||||
|
@ -375,6 +383,7 @@ export class LoginStrategyService implements LoginStrategyServiceAbstraction {
|
||||||
this.authRequestService,
|
this.authRequestService,
|
||||||
this.i18nService,
|
this.i18nService,
|
||||||
this.billingAccountProfileStateService,
|
this.billingAccountProfileStateService,
|
||||||
|
this.kdfConfigService,
|
||||||
);
|
);
|
||||||
case AuthenticationType.UserApiKey:
|
case AuthenticationType.UserApiKey:
|
||||||
return new UserApiLoginStrategy(
|
return new UserApiLoginStrategy(
|
||||||
|
@ -394,6 +403,7 @@ export class LoginStrategyService implements LoginStrategyServiceAbstraction {
|
||||||
this.environmentService,
|
this.environmentService,
|
||||||
this.keyConnectorService,
|
this.keyConnectorService,
|
||||||
this.billingAccountProfileStateService,
|
this.billingAccountProfileStateService,
|
||||||
|
this.kdfConfigService,
|
||||||
);
|
);
|
||||||
case AuthenticationType.AuthRequest:
|
case AuthenticationType.AuthRequest:
|
||||||
return new AuthRequestLoginStrategy(
|
return new AuthRequestLoginStrategy(
|
||||||
|
@ -412,6 +422,7 @@ export class LoginStrategyService implements LoginStrategyServiceAbstraction {
|
||||||
this.userDecryptionOptionsService,
|
this.userDecryptionOptionsService,
|
||||||
this.deviceTrustService,
|
this.deviceTrustService,
|
||||||
this.billingAccountProfileStateService,
|
this.billingAccountProfileStateService,
|
||||||
|
this.kdfConfigService,
|
||||||
);
|
);
|
||||||
case AuthenticationType.WebAuthn:
|
case AuthenticationType.WebAuthn:
|
||||||
return new WebAuthnLoginStrategy(
|
return new WebAuthnLoginStrategy(
|
||||||
|
@ -429,6 +440,7 @@ export class LoginStrategyService implements LoginStrategyServiceAbstraction {
|
||||||
this.twoFactorService,
|
this.twoFactorService,
|
||||||
this.userDecryptionOptionsService,
|
this.userDecryptionOptionsService,
|
||||||
this.billingAccountProfileStateService,
|
this.billingAccountProfileStateService,
|
||||||
|
this.kdfConfigService,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
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 { KdfConfigService } from "@bitwarden/common/auth/abstractions/kdf-config.service";
|
||||||
import { KdfConfig } from "@bitwarden/common/auth/models/domain/kdf-config";
|
import { KdfConfig } from "@bitwarden/common/auth/models/domain/kdf-config";
|
||||||
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";
|
||||||
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
|
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
|
||||||
import { KdfType } from "@bitwarden/common/platform/enums";
|
|
||||||
import { EncString } from "@bitwarden/common/platform/models/domain/enc-string";
|
import { EncString } from "@bitwarden/common/platform/models/domain/enc-string";
|
||||||
import { PinLockType } from "@bitwarden/common/services/vault-timeout/vault-timeout-settings.service";
|
import { PinLockType } from "@bitwarden/common/services/vault-timeout/vault-timeout-settings.service";
|
||||||
import { UserKey } from "@bitwarden/common/types/key";
|
import { UserKey } from "@bitwarden/common/types/key";
|
||||||
|
@ -16,6 +16,7 @@ export class PinCryptoService implements PinCryptoServiceAbstraction {
|
||||||
private cryptoService: CryptoService,
|
private cryptoService: CryptoService,
|
||||||
private vaultTimeoutSettingsService: VaultTimeoutSettingsService,
|
private vaultTimeoutSettingsService: VaultTimeoutSettingsService,
|
||||||
private logService: LogService,
|
private logService: LogService,
|
||||||
|
private kdfConfigService: KdfConfigService,
|
||||||
) {}
|
) {}
|
||||||
async decryptUserKeyWithPin(pin: string): Promise<UserKey | null> {
|
async decryptUserKeyWithPin(pin: string): Promise<UserKey | null> {
|
||||||
try {
|
try {
|
||||||
|
@ -24,8 +25,7 @@ export class PinCryptoService implements PinCryptoServiceAbstraction {
|
||||||
const { pinKeyEncryptedUserKey, oldPinKeyEncryptedMasterKey } =
|
const { pinKeyEncryptedUserKey, oldPinKeyEncryptedMasterKey } =
|
||||||
await this.getPinKeyEncryptedKeys(pinLockType);
|
await this.getPinKeyEncryptedKeys(pinLockType);
|
||||||
|
|
||||||
const kdf: KdfType = await this.stateService.getKdfType();
|
const kdfConfig: KdfConfig = await this.kdfConfigService.getKdfConfig();
|
||||||
const kdfConfig: KdfConfig = await this.stateService.getKdfConfig();
|
|
||||||
let userKey: UserKey;
|
let userKey: UserKey;
|
||||||
const email = await this.stateService.getEmail();
|
const email = await this.stateService.getEmail();
|
||||||
if (oldPinKeyEncryptedMasterKey) {
|
if (oldPinKeyEncryptedMasterKey) {
|
||||||
|
@ -33,7 +33,6 @@ export class PinCryptoService implements PinCryptoServiceAbstraction {
|
||||||
pinLockType === "TRANSIENT",
|
pinLockType === "TRANSIENT",
|
||||||
pin,
|
pin,
|
||||||
email,
|
email,
|
||||||
kdf,
|
|
||||||
kdfConfig,
|
kdfConfig,
|
||||||
oldPinKeyEncryptedMasterKey,
|
oldPinKeyEncryptedMasterKey,
|
||||||
);
|
);
|
||||||
|
@ -41,7 +40,6 @@ export class PinCryptoService implements PinCryptoServiceAbstraction {
|
||||||
userKey = await this.cryptoService.decryptUserKeyWithPin(
|
userKey = await this.cryptoService.decryptUserKeyWithPin(
|
||||||
pin,
|
pin,
|
||||||
email,
|
email,
|
||||||
kdf,
|
|
||||||
kdfConfig,
|
kdfConfig,
|
||||||
pinKeyEncryptedUserKey,
|
pinKeyEncryptedUserKey,
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
import { mock } from "jest-mock-extended";
|
import { mock } from "jest-mock-extended";
|
||||||
|
|
||||||
import { KdfConfig } from "@bitwarden/common/auth/models/domain/kdf-config";
|
import { KdfConfigService } from "@bitwarden/common/auth/abstractions/kdf-config.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";
|
||||||
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
|
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
|
||||||
|
import { DEFAULT_KDF_CONFIG } from "@bitwarden/common/platform/enums";
|
||||||
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 {
|
import {
|
||||||
|
@ -13,6 +14,7 @@ import {
|
||||||
import { UserKey } from "@bitwarden/common/types/key";
|
import { UserKey } from "@bitwarden/common/types/key";
|
||||||
|
|
||||||
import { PinCryptoService } from "./pin-crypto.service.implementation";
|
import { PinCryptoService } from "./pin-crypto.service.implementation";
|
||||||
|
|
||||||
describe("PinCryptoService", () => {
|
describe("PinCryptoService", () => {
|
||||||
let pinCryptoService: PinCryptoService;
|
let pinCryptoService: PinCryptoService;
|
||||||
|
|
||||||
|
@ -20,6 +22,7 @@ describe("PinCryptoService", () => {
|
||||||
const cryptoService = mock<CryptoService>();
|
const cryptoService = mock<CryptoService>();
|
||||||
const vaultTimeoutSettingsService = mock<VaultTimeoutSettingsService>();
|
const vaultTimeoutSettingsService = mock<VaultTimeoutSettingsService>();
|
||||||
const logService = mock<LogService>();
|
const logService = mock<LogService>();
|
||||||
|
const kdfConfigService = mock<KdfConfigService>();
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
jest.clearAllMocks();
|
jest.clearAllMocks();
|
||||||
|
@ -29,6 +32,7 @@ describe("PinCryptoService", () => {
|
||||||
cryptoService,
|
cryptoService,
|
||||||
vaultTimeoutSettingsService,
|
vaultTimeoutSettingsService,
|
||||||
logService,
|
logService,
|
||||||
|
kdfConfigService,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -39,7 +43,6 @@ describe("PinCryptoService", () => {
|
||||||
describe("decryptUserKeyWithPin(...)", () => {
|
describe("decryptUserKeyWithPin(...)", () => {
|
||||||
const mockPin = "1234";
|
const mockPin = "1234";
|
||||||
const mockProtectedPin = "protectedPin";
|
const mockProtectedPin = "protectedPin";
|
||||||
const DEFAULT_PBKDF2_ITERATIONS = 600000;
|
|
||||||
const mockUserEmail = "user@example.com";
|
const mockUserEmail = "user@example.com";
|
||||||
const mockUserKey = new SymmetricCryptoKey(randomBytes(32)) as UserKey;
|
const mockUserKey = new SymmetricCryptoKey(randomBytes(32)) as UserKey;
|
||||||
|
|
||||||
|
@ -49,7 +52,7 @@ describe("PinCryptoService", () => {
|
||||||
) {
|
) {
|
||||||
vaultTimeoutSettingsService.isPinLockSet.mockResolvedValue(pinLockType);
|
vaultTimeoutSettingsService.isPinLockSet.mockResolvedValue(pinLockType);
|
||||||
|
|
||||||
stateService.getKdfConfig.mockResolvedValue(new KdfConfig(DEFAULT_PBKDF2_ITERATIONS));
|
kdfConfigService.getKdfConfig.mockResolvedValue(DEFAULT_KDF_CONFIG);
|
||||||
stateService.getEmail.mockResolvedValue(mockUserEmail);
|
stateService.getEmail.mockResolvedValue(mockUserEmail);
|
||||||
|
|
||||||
if (migrationStatus === "PRE") {
|
if (migrationStatus === "PRE") {
|
||||||
|
|
|
@ -7,3 +7,4 @@ export * from "./provider-type.enum";
|
||||||
export * from "./provider-user-status-type.enum";
|
export * from "./provider-user-status-type.enum";
|
||||||
export * from "./provider-user-type.enum";
|
export * from "./provider-user-type.enum";
|
||||||
export * from "./scim-provider-type.enum";
|
export * from "./scim-provider-type.enum";
|
||||||
|
export * from "./provider-status-type.enum";
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
export enum ProviderStatusType {
|
||||||
|
Pending = 0,
|
||||||
|
Created = 1,
|
||||||
|
Billable = 2,
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
import { ProviderUserStatusType, ProviderUserType } from "../../enums";
|
import { ProviderStatusType, ProviderUserStatusType, ProviderUserType } from "../../enums";
|
||||||
import { ProfileProviderResponse } from "../response/profile-provider.response";
|
import { ProfileProviderResponse } from "../response/profile-provider.response";
|
||||||
|
|
||||||
export class ProviderData {
|
export class ProviderData {
|
||||||
|
@ -9,6 +9,7 @@ export class ProviderData {
|
||||||
enabled: boolean;
|
enabled: boolean;
|
||||||
userId: string;
|
userId: string;
|
||||||
useEvents: boolean;
|
useEvents: boolean;
|
||||||
|
providerStatus: ProviderStatusType;
|
||||||
|
|
||||||
constructor(response: ProfileProviderResponse) {
|
constructor(response: ProfileProviderResponse) {
|
||||||
this.id = response.id;
|
this.id = response.id;
|
||||||
|
@ -18,5 +19,6 @@ export class ProviderData {
|
||||||
this.enabled = response.enabled;
|
this.enabled = response.enabled;
|
||||||
this.userId = response.userId;
|
this.userId = response.userId;
|
||||||
this.useEvents = response.useEvents;
|
this.useEvents = response.useEvents;
|
||||||
|
this.providerStatus = response.providerStatus;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { ProviderUserStatusType, ProviderUserType } from "../../enums";
|
import { ProviderStatusType, ProviderUserStatusType, ProviderUserType } from "../../enums";
|
||||||
import { ProviderData } from "../data/provider.data";
|
import { ProviderData } from "../data/provider.data";
|
||||||
|
|
||||||
export class Provider {
|
export class Provider {
|
||||||
|
@ -9,6 +9,7 @@ export class Provider {
|
||||||
enabled: boolean;
|
enabled: boolean;
|
||||||
userId: string;
|
userId: string;
|
||||||
useEvents: boolean;
|
useEvents: boolean;
|
||||||
|
providerStatus: ProviderStatusType;
|
||||||
|
|
||||||
constructor(obj?: ProviderData) {
|
constructor(obj?: ProviderData) {
|
||||||
if (obj == null) {
|
if (obj == null) {
|
||||||
|
@ -22,6 +23,7 @@ export class Provider {
|
||||||
this.enabled = obj.enabled;
|
this.enabled = obj.enabled;
|
||||||
this.userId = obj.userId;
|
this.userId = obj.userId;
|
||||||
this.useEvents = obj.useEvents;
|
this.useEvents = obj.useEvents;
|
||||||
|
this.providerStatus = obj.providerStatus;
|
||||||
}
|
}
|
||||||
|
|
||||||
get canAccess() {
|
get canAccess() {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { BaseResponse } from "../../../models/response/base.response";
|
import { BaseResponse } from "../../../models/response/base.response";
|
||||||
import { ProviderUserStatusType, ProviderUserType } from "../../enums";
|
import { ProviderStatusType, ProviderUserStatusType, ProviderUserType } from "../../enums";
|
||||||
import { PermissionsApi } from "../api/permissions.api";
|
import { PermissionsApi } from "../api/permissions.api";
|
||||||
|
|
||||||
export class ProfileProviderResponse extends BaseResponse {
|
export class ProfileProviderResponse extends BaseResponse {
|
||||||
|
@ -12,6 +12,7 @@ export class ProfileProviderResponse extends BaseResponse {
|
||||||
permissions: PermissionsApi;
|
permissions: PermissionsApi;
|
||||||
userId: string;
|
userId: string;
|
||||||
useEvents: boolean;
|
useEvents: boolean;
|
||||||
|
providerStatus: ProviderStatusType;
|
||||||
|
|
||||||
constructor(response: any) {
|
constructor(response: any) {
|
||||||
super(response);
|
super(response);
|
||||||
|
@ -24,5 +25,6 @@ export class ProfileProviderResponse extends BaseResponse {
|
||||||
this.permissions = new PermissionsApi(this.getResponseProperty("permissions"));
|
this.permissions = new PermissionsApi(this.getResponseProperty("permissions"));
|
||||||
this.userId = this.getResponseProperty("UserId");
|
this.userId = this.getResponseProperty("UserId");
|
||||||
this.useEvents = this.getResponseProperty("UseEvents");
|
this.useEvents = this.getResponseProperty("UseEvents");
|
||||||
|
this.providerStatus = this.getResponseProperty("ProviderStatus");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@ import { FakeAccountService, FakeStateProvider, mockAccountServiceWith } from ".
|
||||||
import { FakeActiveUserState, FakeSingleUserState } from "../../../spec/fake-state";
|
import { FakeActiveUserState, FakeSingleUserState } from "../../../spec/fake-state";
|
||||||
import { Utils } from "../../platform/misc/utils";
|
import { Utils } from "../../platform/misc/utils";
|
||||||
import { UserId } from "../../types/guid";
|
import { UserId } from "../../types/guid";
|
||||||
import { ProviderUserStatusType, ProviderUserType } from "../enums";
|
import { ProviderStatusType, ProviderUserStatusType, ProviderUserType } from "../enums";
|
||||||
import { ProviderData } from "../models/data/provider.data";
|
import { ProviderData } from "../models/data/provider.data";
|
||||||
import { Provider } from "../models/domain/provider";
|
import { Provider } from "../models/domain/provider";
|
||||||
|
|
||||||
|
@ -64,6 +64,7 @@ describe("PROVIDERS key definition", () => {
|
||||||
enabled: true,
|
enabled: true,
|
||||||
userId: "string",
|
userId: "string",
|
||||||
useEvents: true,
|
useEvents: true,
|
||||||
|
providerStatus: ProviderStatusType.Pending,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
const result = sut.deserializer(JSON.parse(JSON.stringify(expectedResult)));
|
const result = sut.deserializer(JSON.parse(JSON.stringify(expectedResult)));
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
import { UserId } from "../../types/guid";
|
||||||
|
import { KdfConfig } from "../models/domain/kdf-config";
|
||||||
|
|
||||||
|
export abstract class KdfConfigService {
|
||||||
|
setKdfConfig: (userId: UserId, KdfConfig: KdfConfig) => Promise<void>;
|
||||||
|
getKdfConfig: () => Promise<KdfConfig>;
|
||||||
|
}
|
|
@ -12,12 +12,12 @@ export interface TwoFactorProviderDetails {
|
||||||
|
|
||||||
export abstract class TwoFactorService {
|
export abstract class TwoFactorService {
|
||||||
init: () => void;
|
init: () => void;
|
||||||
getSupportedProviders: (win: Window) => TwoFactorProviderDetails[];
|
getSupportedProviders: (win: Window) => Promise<TwoFactorProviderDetails[]>;
|
||||||
getDefaultProvider: (webAuthnSupported: boolean) => TwoFactorProviderType;
|
getDefaultProvider: (webAuthnSupported: boolean) => Promise<TwoFactorProviderType>;
|
||||||
setSelectedProvider: (type: TwoFactorProviderType) => void;
|
setSelectedProvider: (type: TwoFactorProviderType) => Promise<void>;
|
||||||
clearSelectedProvider: () => void;
|
clearSelectedProvider: () => Promise<void>;
|
||||||
|
|
||||||
setProviders: (response: IdentityTwoFactorResponse) => void;
|
setProviders: (response: IdentityTwoFactorResponse) => Promise<void>;
|
||||||
clearProviders: () => void;
|
clearProviders: () => Promise<void>;
|
||||||
getProviders: () => Map<TwoFactorProviderType, { [key: string]: string }>;
|
getProviders: () => Promise<Map<TwoFactorProviderType, { [key: string]: string }>>;
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,7 @@ export class AuthResult {
|
||||||
resetMasterPassword = false;
|
resetMasterPassword = false;
|
||||||
|
|
||||||
forcePasswordReset: ForceSetPasswordReason = ForceSetPasswordReason.None;
|
forcePasswordReset: ForceSetPasswordReason = ForceSetPasswordReason.None;
|
||||||
twoFactorProviders: Map<TwoFactorProviderType, { [key: string]: string }> = null;
|
twoFactorProviders: Partial<Record<TwoFactorProviderType, Record<string, string>>> = null;
|
||||||
ssoEmail2FaSessionToken?: string;
|
ssoEmail2FaSessionToken?: string;
|
||||||
email: string;
|
email: string;
|
||||||
requiresEncryptionKeyMigration: boolean;
|
requiresEncryptionKeyMigration: boolean;
|
||||||
|
|
|
@ -1,11 +1,86 @@
|
||||||
export class KdfConfig {
|
import { Jsonify } from "type-fest";
|
||||||
iterations: number;
|
|
||||||
memory?: number;
|
|
||||||
parallelism?: number;
|
|
||||||
|
|
||||||
constructor(iterations: number, memory?: number, parallelism?: number) {
|
import {
|
||||||
this.iterations = iterations;
|
ARGON2_ITERATIONS,
|
||||||
this.memory = memory;
|
ARGON2_MEMORY,
|
||||||
this.parallelism = parallelism;
|
ARGON2_PARALLELISM,
|
||||||
|
KdfType,
|
||||||
|
PBKDF2_ITERATIONS,
|
||||||
|
} from "../../../platform/enums/kdf-type.enum";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a type safe KDF configuration.
|
||||||
|
*/
|
||||||
|
export type KdfConfig = PBKDF2KdfConfig | Argon2KdfConfig;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Password-Based Key Derivation Function 2 (PBKDF2) KDF configuration.
|
||||||
|
*/
|
||||||
|
export class PBKDF2KdfConfig {
|
||||||
|
kdfType: KdfType.PBKDF2_SHA256 = KdfType.PBKDF2_SHA256;
|
||||||
|
iterations: number;
|
||||||
|
|
||||||
|
constructor(iterations?: number) {
|
||||||
|
this.iterations = iterations ?? PBKDF2_ITERATIONS.defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validates the PBKDF2 KDF configuration.
|
||||||
|
* A Valid PBKDF2 KDF configuration has KDF iterations between the 600_000 and 2_000_000.
|
||||||
|
*/
|
||||||
|
validateKdfConfig(): void {
|
||||||
|
if (!PBKDF2_ITERATIONS.inRange(this.iterations)) {
|
||||||
|
throw new Error(
|
||||||
|
`PBKDF2 iterations must be between ${PBKDF2_ITERATIONS.min} and ${PBKDF2_ITERATIONS.max}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static fromJSON(json: Jsonify<PBKDF2KdfConfig>): PBKDF2KdfConfig {
|
||||||
|
return new PBKDF2KdfConfig(json.iterations);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Argon2 KDF configuration.
|
||||||
|
*/
|
||||||
|
export class Argon2KdfConfig {
|
||||||
|
kdfType: KdfType.Argon2id = KdfType.Argon2id;
|
||||||
|
iterations: number;
|
||||||
|
memory: number;
|
||||||
|
parallelism: number;
|
||||||
|
|
||||||
|
constructor(iterations?: number, memory?: number, parallelism?: number) {
|
||||||
|
this.iterations = iterations ?? ARGON2_ITERATIONS.defaultValue;
|
||||||
|
this.memory = memory ?? ARGON2_MEMORY.defaultValue;
|
||||||
|
this.parallelism = parallelism ?? ARGON2_PARALLELISM.defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validates the Argon2 KDF configuration.
|
||||||
|
* A Valid Argon2 KDF configuration has iterations between 2 and 10, memory between 16mb and 1024mb, and parallelism between 1 and 16.
|
||||||
|
*/
|
||||||
|
validateKdfConfig(): void {
|
||||||
|
if (!ARGON2_ITERATIONS.inRange(this.iterations)) {
|
||||||
|
throw new Error(
|
||||||
|
`Argon2 iterations must be between ${ARGON2_ITERATIONS.min} and ${ARGON2_ITERATIONS.max}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ARGON2_MEMORY.inRange(this.memory)) {
|
||||||
|
throw new Error(
|
||||||
|
`Argon2 memory must be between ${ARGON2_MEMORY.min}mb and ${ARGON2_MEMORY.max}mb`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ARGON2_PARALLELISM.inRange(this.parallelism)) {
|
||||||
|
throw new Error(
|
||||||
|
`Argon2 parallelism must be between ${ARGON2_PARALLELISM.min} and ${ARGON2_PARALLELISM.max}.`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static fromJSON(json: Jsonify<Argon2KdfConfig>): Argon2KdfConfig {
|
||||||
|
return new Argon2KdfConfig(json.iterations, json.memory, json.parallelism);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,18 +11,14 @@ export class SetKeyConnectorKeyRequest {
|
||||||
kdfParallelism?: number;
|
kdfParallelism?: number;
|
||||||
orgIdentifier: string;
|
orgIdentifier: string;
|
||||||
|
|
||||||
constructor(
|
constructor(key: string, kdfConfig: KdfConfig, orgIdentifier: string, keys: KeysRequest) {
|
||||||
key: string,
|
|
||||||
kdf: KdfType,
|
|
||||||
kdfConfig: KdfConfig,
|
|
||||||
orgIdentifier: string,
|
|
||||||
keys: KeysRequest,
|
|
||||||
) {
|
|
||||||
this.key = key;
|
this.key = key;
|
||||||
this.kdf = kdf;
|
this.kdf = kdfConfig.kdfType;
|
||||||
this.kdfIterations = kdfConfig.iterations;
|
this.kdfIterations = kdfConfig.iterations;
|
||||||
this.kdfMemory = kdfConfig.memory;
|
if (kdfConfig.kdfType === KdfType.Argon2id) {
|
||||||
this.kdfParallelism = kdfConfig.parallelism;
|
this.kdfMemory = kdfConfig.memory;
|
||||||
|
this.kdfParallelism = kdfConfig.parallelism;
|
||||||
|
}
|
||||||
this.orgIdentifier = orgIdentifier;
|
this.orgIdentifier = orgIdentifier;
|
||||||
this.keys = keys;
|
this.keys = keys;
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,8 +4,10 @@ import { TwoFactorProviderType } from "../../enums/two-factor-provider-type";
|
||||||
import { MasterPasswordPolicyResponse } from "./master-password-policy.response";
|
import { MasterPasswordPolicyResponse } from "./master-password-policy.response";
|
||||||
|
|
||||||
export class IdentityTwoFactorResponse extends BaseResponse {
|
export class IdentityTwoFactorResponse extends BaseResponse {
|
||||||
|
// contains available two-factor providers
|
||||||
twoFactorProviders: TwoFactorProviderType[];
|
twoFactorProviders: TwoFactorProviderType[];
|
||||||
twoFactorProviders2 = new Map<TwoFactorProviderType, { [key: string]: string }>();
|
// a map of two-factor providers to necessary data for completion
|
||||||
|
twoFactorProviders2: Record<TwoFactorProviderType, Record<string, string>>;
|
||||||
captchaToken: string;
|
captchaToken: string;
|
||||||
ssoEmail2faSessionToken: string;
|
ssoEmail2faSessionToken: string;
|
||||||
email?: string;
|
email?: string;
|
||||||
|
@ -15,15 +17,7 @@ export class IdentityTwoFactorResponse extends BaseResponse {
|
||||||
super(response);
|
super(response);
|
||||||
this.captchaToken = this.getResponseProperty("CaptchaBypassToken");
|
this.captchaToken = this.getResponseProperty("CaptchaBypassToken");
|
||||||
this.twoFactorProviders = this.getResponseProperty("TwoFactorProviders");
|
this.twoFactorProviders = this.getResponseProperty("TwoFactorProviders");
|
||||||
const twoFactorProviders2 = this.getResponseProperty("TwoFactorProviders2");
|
this.twoFactorProviders2 = this.getResponseProperty("TwoFactorProviders2");
|
||||||
if (twoFactorProviders2 != null) {
|
|
||||||
for (const prop in twoFactorProviders2) {
|
|
||||||
// eslint-disable-next-line
|
|
||||||
if (twoFactorProviders2.hasOwnProperty(prop)) {
|
|
||||||
this.twoFactorProviders2.set(parseInt(prop, null), twoFactorProviders2[prop]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.masterPasswordPolicy = new MasterPasswordPolicyResponse(
|
this.masterPasswordPolicy = new MasterPasswordPolicyResponse(
|
||||||
this.getResponseProperty("MasterPasswordPolicy"),
|
this.getResponseProperty("MasterPasswordPolicy"),
|
||||||
);
|
);
|
||||||
|
|
|
@ -0,0 +1,104 @@
|
||||||
|
import { FakeAccountService, FakeStateProvider, mockAccountServiceWith } from "../../../spec";
|
||||||
|
import {
|
||||||
|
ARGON2_ITERATIONS,
|
||||||
|
ARGON2_MEMORY,
|
||||||
|
ARGON2_PARALLELISM,
|
||||||
|
PBKDF2_ITERATIONS,
|
||||||
|
} from "../../platform/enums/kdf-type.enum";
|
||||||
|
import { Utils } from "../../platform/misc/utils";
|
||||||
|
import { UserId } from "../../types/guid";
|
||||||
|
import { Argon2KdfConfig, PBKDF2KdfConfig } from "../models/domain/kdf-config";
|
||||||
|
|
||||||
|
import { KdfConfigService } from "./kdf-config.service";
|
||||||
|
|
||||||
|
describe("KdfConfigService", () => {
|
||||||
|
let sutKdfConfigService: KdfConfigService;
|
||||||
|
|
||||||
|
let fakeStateProvider: FakeStateProvider;
|
||||||
|
let fakeAccountService: FakeAccountService;
|
||||||
|
const mockUserId = Utils.newGuid() as UserId;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
jest.clearAllMocks();
|
||||||
|
|
||||||
|
fakeAccountService = mockAccountServiceWith(mockUserId);
|
||||||
|
fakeStateProvider = new FakeStateProvider(fakeAccountService);
|
||||||
|
sutKdfConfigService = new KdfConfigService(fakeStateProvider);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("setKdfConfig(): should set the KDF config", async () => {
|
||||||
|
const kdfConfig: PBKDF2KdfConfig = new PBKDF2KdfConfig(600_000);
|
||||||
|
await sutKdfConfigService.setKdfConfig(mockUserId, kdfConfig);
|
||||||
|
await expect(sutKdfConfigService.getKdfConfig()).resolves.toEqual(kdfConfig);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("setKdfConfig(): should get the KDF config", async () => {
|
||||||
|
const kdfConfig: Argon2KdfConfig = new Argon2KdfConfig(3, 64, 4);
|
||||||
|
await sutKdfConfigService.setKdfConfig(mockUserId, kdfConfig);
|
||||||
|
await expect(sutKdfConfigService.getKdfConfig()).resolves.toEqual(kdfConfig);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("setKdfConfig(): should throw error KDF cannot be null", async () => {
|
||||||
|
const kdfConfig: Argon2KdfConfig = null;
|
||||||
|
try {
|
||||||
|
await sutKdfConfigService.setKdfConfig(mockUserId, kdfConfig);
|
||||||
|
} catch (e) {
|
||||||
|
expect(e).toEqual(new Error("kdfConfig cannot be null"));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it("setKdfConfig(): should throw error userId cannot be null", async () => {
|
||||||
|
const kdfConfig: Argon2KdfConfig = new Argon2KdfConfig(3, 64, 4);
|
||||||
|
try {
|
||||||
|
await sutKdfConfigService.setKdfConfig(null, kdfConfig);
|
||||||
|
} catch (e) {
|
||||||
|
expect(e).toEqual(new Error("userId cannot be null"));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it("getKdfConfig(): should throw error KdfConfig for active user account state is null", async () => {
|
||||||
|
try {
|
||||||
|
await sutKdfConfigService.getKdfConfig();
|
||||||
|
} catch (e) {
|
||||||
|
expect(e).toEqual(new Error("KdfConfig for active user account state is null"));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it("validateKdfConfig(): should validate the PBKDF2 KDF config", () => {
|
||||||
|
const kdfConfig: PBKDF2KdfConfig = new PBKDF2KdfConfig(600_000);
|
||||||
|
expect(() => kdfConfig.validateKdfConfig()).not.toThrow();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("validateKdfConfig(): should validate the Argon2id KDF config", () => {
|
||||||
|
const kdfConfig: Argon2KdfConfig = new Argon2KdfConfig(3, 64, 4);
|
||||||
|
expect(() => kdfConfig.validateKdfConfig()).not.toThrow();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("validateKdfConfig(): should throw an error for invalid PBKDF2 iterations", () => {
|
||||||
|
const kdfConfig: PBKDF2KdfConfig = new PBKDF2KdfConfig(100);
|
||||||
|
expect(() => kdfConfig.validateKdfConfig()).toThrow(
|
||||||
|
`PBKDF2 iterations must be between ${PBKDF2_ITERATIONS.min} and ${PBKDF2_ITERATIONS.max}`,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("validateKdfConfig(): should throw an error for invalid Argon2 iterations", () => {
|
||||||
|
const kdfConfig: Argon2KdfConfig = new Argon2KdfConfig(11, 64, 4);
|
||||||
|
expect(() => kdfConfig.validateKdfConfig()).toThrow(
|
||||||
|
`Argon2 iterations must be between ${ARGON2_ITERATIONS.min} and ${ARGON2_ITERATIONS.max}`,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("validateKdfConfig(): should throw an error for invalid Argon2 memory", () => {
|
||||||
|
const kdfConfig: Argon2KdfConfig = new Argon2KdfConfig(3, 1025, 4);
|
||||||
|
expect(() => kdfConfig.validateKdfConfig()).toThrow(
|
||||||
|
`Argon2 memory must be between ${ARGON2_MEMORY.min}mb and ${ARGON2_MEMORY.max}mb`,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("validateKdfConfig(): should throw an error for invalid Argon2 parallelism", () => {
|
||||||
|
const kdfConfig: Argon2KdfConfig = new Argon2KdfConfig(3, 64, 17);
|
||||||
|
expect(() => kdfConfig.validateKdfConfig()).toThrow(
|
||||||
|
`Argon2 parallelism must be between ${ARGON2_PARALLELISM.min} and ${ARGON2_PARALLELISM.max}`,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,41 @@
|
||||||
|
import { firstValueFrom } from "rxjs";
|
||||||
|
|
||||||
|
import { KdfType } from "../../platform/enums/kdf-type.enum";
|
||||||
|
import { KDF_CONFIG_DISK, StateProvider, UserKeyDefinition } from "../../platform/state";
|
||||||
|
import { UserId } from "../../types/guid";
|
||||||
|
import { KdfConfigService as KdfConfigServiceAbstraction } from "../abstractions/kdf-config.service";
|
||||||
|
import { Argon2KdfConfig, KdfConfig, PBKDF2KdfConfig } from "../models/domain/kdf-config";
|
||||||
|
|
||||||
|
export const KDF_CONFIG = new UserKeyDefinition<KdfConfig>(KDF_CONFIG_DISK, "kdfConfig", {
|
||||||
|
deserializer: (kdfConfig: KdfConfig) => {
|
||||||
|
if (kdfConfig == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return kdfConfig.kdfType === KdfType.PBKDF2_SHA256
|
||||||
|
? PBKDF2KdfConfig.fromJSON(kdfConfig)
|
||||||
|
: Argon2KdfConfig.fromJSON(kdfConfig);
|
||||||
|
},
|
||||||
|
clearOn: ["logout"],
|
||||||
|
});
|
||||||
|
|
||||||
|
export class KdfConfigService implements KdfConfigServiceAbstraction {
|
||||||
|
constructor(private stateProvider: StateProvider) {}
|
||||||
|
async setKdfConfig(userId: UserId, kdfConfig: KdfConfig) {
|
||||||
|
if (!userId) {
|
||||||
|
throw new Error("userId cannot be null");
|
||||||
|
}
|
||||||
|
if (kdfConfig === null) {
|
||||||
|
throw new Error("kdfConfig cannot be null");
|
||||||
|
}
|
||||||
|
await this.stateProvider.setUserState(KDF_CONFIG, kdfConfig, userId);
|
||||||
|
}
|
||||||
|
|
||||||
|
async getKdfConfig(): Promise<KdfConfig> {
|
||||||
|
const userId = await firstValueFrom(this.stateProvider.activeUserId$);
|
||||||
|
const state = await firstValueFrom(this.stateProvider.getUser(userId, KDF_CONFIG).state$);
|
||||||
|
if (state === null) {
|
||||||
|
throw new Error("KdfConfig for active user account state is null");
|
||||||
|
}
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
}
|
|
@ -7,6 +7,7 @@ import { KeysRequest } from "../../models/request/keys.request";
|
||||||
import { CryptoService } from "../../platform/abstractions/crypto.service";
|
import { CryptoService } from "../../platform/abstractions/crypto.service";
|
||||||
import { KeyGenerationService } from "../../platform/abstractions/key-generation.service";
|
import { KeyGenerationService } from "../../platform/abstractions/key-generation.service";
|
||||||
import { LogService } from "../../platform/abstractions/log.service";
|
import { LogService } from "../../platform/abstractions/log.service";
|
||||||
|
import { KdfType } from "../../platform/enums/kdf-type.enum";
|
||||||
import { Utils } from "../../platform/misc/utils";
|
import { Utils } from "../../platform/misc/utils";
|
||||||
import { SymmetricCryptoKey } from "../../platform/models/domain/symmetric-crypto-key";
|
import { SymmetricCryptoKey } from "../../platform/models/domain/symmetric-crypto-key";
|
||||||
import {
|
import {
|
||||||
|
@ -20,7 +21,7 @@ import { AccountService } from "../abstractions/account.service";
|
||||||
import { KeyConnectorService as KeyConnectorServiceAbstraction } from "../abstractions/key-connector.service";
|
import { KeyConnectorService as KeyConnectorServiceAbstraction } from "../abstractions/key-connector.service";
|
||||||
import { InternalMasterPasswordServiceAbstraction } from "../abstractions/master-password.service.abstraction";
|
import { InternalMasterPasswordServiceAbstraction } from "../abstractions/master-password.service.abstraction";
|
||||||
import { TokenService } from "../abstractions/token.service";
|
import { TokenService } from "../abstractions/token.service";
|
||||||
import { KdfConfig } from "../models/domain/kdf-config";
|
import { Argon2KdfConfig, KdfConfig, PBKDF2KdfConfig } from "../models/domain/kdf-config";
|
||||||
import { KeyConnectorUserKeyRequest } from "../models/request/key-connector-user-key.request";
|
import { KeyConnectorUserKeyRequest } from "../models/request/key-connector-user-key.request";
|
||||||
import { SetKeyConnectorKeyRequest } from "../models/request/set-key-connector-key.request";
|
import { SetKeyConnectorKeyRequest } from "../models/request/set-key-connector-key.request";
|
||||||
import { IdentityTokenResponse } from "../models/response/identity-token.response";
|
import { IdentityTokenResponse } from "../models/response/identity-token.response";
|
||||||
|
@ -133,12 +134,14 @@ export class KeyConnectorService implements KeyConnectorServiceAbstraction {
|
||||||
userDecryptionOptions,
|
userDecryptionOptions,
|
||||||
} = tokenResponse;
|
} = tokenResponse;
|
||||||
const password = await this.keyGenerationService.createKey(512);
|
const password = await this.keyGenerationService.createKey(512);
|
||||||
const kdfConfig = new KdfConfig(kdfIterations, kdfMemory, kdfParallelism);
|
const kdfConfig: KdfConfig =
|
||||||
|
kdf === KdfType.PBKDF2_SHA256
|
||||||
|
? new PBKDF2KdfConfig(kdfIterations)
|
||||||
|
: new Argon2KdfConfig(kdfIterations, kdfMemory, kdfParallelism);
|
||||||
|
|
||||||
const masterKey = await this.cryptoService.makeMasterKey(
|
const masterKey = await this.cryptoService.makeMasterKey(
|
||||||
password.keyB64,
|
password.keyB64,
|
||||||
await this.tokenService.getEmail(),
|
await this.tokenService.getEmail(),
|
||||||
kdf,
|
|
||||||
kdfConfig,
|
kdfConfig,
|
||||||
);
|
);
|
||||||
const keyConnectorRequest = new KeyConnectorUserKeyRequest(masterKey.encKeyB64);
|
const keyConnectorRequest = new KeyConnectorUserKeyRequest(masterKey.encKeyB64);
|
||||||
|
@ -162,7 +165,6 @@ export class KeyConnectorService implements KeyConnectorServiceAbstraction {
|
||||||
const keys = new KeysRequest(pubKey, privKey.encryptedString);
|
const keys = new KeysRequest(pubKey, privKey.encryptedString);
|
||||||
const setPasswordRequest = new SetKeyConnectorKeyRequest(
|
const setPasswordRequest = new SetKeyConnectorKeyRequest(
|
||||||
userKey[1].encryptedString,
|
userKey[1].encryptedString,
|
||||||
kdf,
|
|
||||||
kdfConfig,
|
kdfConfig,
|
||||||
orgId,
|
orgId,
|
||||||
keys,
|
keys,
|
||||||
|
|
|
@ -1,5 +1,9 @@
|
||||||
|
import { firstValueFrom, map } from "rxjs";
|
||||||
|
|
||||||
import { I18nService } from "../../platform/abstractions/i18n.service";
|
import { I18nService } from "../../platform/abstractions/i18n.service";
|
||||||
import { PlatformUtilsService } from "../../platform/abstractions/platform-utils.service";
|
import { PlatformUtilsService } from "../../platform/abstractions/platform-utils.service";
|
||||||
|
import { Utils } from "../../platform/misc/utils";
|
||||||
|
import { GlobalStateProvider, KeyDefinition, TWO_FACTOR_MEMORY } from "../../platform/state";
|
||||||
import {
|
import {
|
||||||
TwoFactorProviderDetails,
|
TwoFactorProviderDetails,
|
||||||
TwoFactorService as TwoFactorServiceAbstraction,
|
TwoFactorService as TwoFactorServiceAbstraction,
|
||||||
|
@ -59,13 +63,36 @@ export const TwoFactorProviders: Partial<Record<TwoFactorProviderType, TwoFactor
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Memory storage as only required during authentication process
|
||||||
|
export const PROVIDERS = KeyDefinition.record<Record<string, string>, TwoFactorProviderType>(
|
||||||
|
TWO_FACTOR_MEMORY,
|
||||||
|
"providers",
|
||||||
|
{
|
||||||
|
deserializer: (obj) => obj,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
// Memory storage as only required during authentication process
|
||||||
|
export const SELECTED_PROVIDER = new KeyDefinition<TwoFactorProviderType>(
|
||||||
|
TWO_FACTOR_MEMORY,
|
||||||
|
"selected",
|
||||||
|
{
|
||||||
|
deserializer: (obj) => obj,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
export class TwoFactorService implements TwoFactorServiceAbstraction {
|
export class TwoFactorService implements TwoFactorServiceAbstraction {
|
||||||
private twoFactorProvidersData: Map<TwoFactorProviderType, { [key: string]: string }>;
|
private providersState = this.globalStateProvider.get(PROVIDERS);
|
||||||
private selectedTwoFactorProviderType: TwoFactorProviderType = null;
|
private selectedState = this.globalStateProvider.get(SELECTED_PROVIDER);
|
||||||
|
readonly providers$ = this.providersState.state$.pipe(
|
||||||
|
map((providers) => Utils.recordToMap(providers)),
|
||||||
|
);
|
||||||
|
readonly selected$ = this.selectedState.state$;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private i18nService: I18nService,
|
private i18nService: I18nService,
|
||||||
private platformUtilsService: PlatformUtilsService,
|
private platformUtilsService: PlatformUtilsService,
|
||||||
|
private globalStateProvider: GlobalStateProvider,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
|
@ -93,63 +120,60 @@ export class TwoFactorService implements TwoFactorServiceAbstraction {
|
||||||
this.i18nService.t("yubiKeyDesc");
|
this.i18nService.t("yubiKeyDesc");
|
||||||
}
|
}
|
||||||
|
|
||||||
getSupportedProviders(win: Window): TwoFactorProviderDetails[] {
|
async getSupportedProviders(win: Window): Promise<TwoFactorProviderDetails[]> {
|
||||||
|
const data = await firstValueFrom(this.providers$);
|
||||||
const providers: any[] = [];
|
const providers: any[] = [];
|
||||||
if (this.twoFactorProvidersData == null) {
|
if (data == null) {
|
||||||
return providers;
|
return providers;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
this.twoFactorProvidersData.has(TwoFactorProviderType.OrganizationDuo) &&
|
data.has(TwoFactorProviderType.OrganizationDuo) &&
|
||||||
this.platformUtilsService.supportsDuo()
|
this.platformUtilsService.supportsDuo()
|
||||||
) {
|
) {
|
||||||
providers.push(TwoFactorProviders[TwoFactorProviderType.OrganizationDuo]);
|
providers.push(TwoFactorProviders[TwoFactorProviderType.OrganizationDuo]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.twoFactorProvidersData.has(TwoFactorProviderType.Authenticator)) {
|
if (data.has(TwoFactorProviderType.Authenticator)) {
|
||||||
providers.push(TwoFactorProviders[TwoFactorProviderType.Authenticator]);
|
providers.push(TwoFactorProviders[TwoFactorProviderType.Authenticator]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.twoFactorProvidersData.has(TwoFactorProviderType.Yubikey)) {
|
if (data.has(TwoFactorProviderType.Yubikey)) {
|
||||||
providers.push(TwoFactorProviders[TwoFactorProviderType.Yubikey]);
|
providers.push(TwoFactorProviders[TwoFactorProviderType.Yubikey]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (data.has(TwoFactorProviderType.Duo) && this.platformUtilsService.supportsDuo()) {
|
||||||
this.twoFactorProvidersData.has(TwoFactorProviderType.Duo) &&
|
|
||||||
this.platformUtilsService.supportsDuo()
|
|
||||||
) {
|
|
||||||
providers.push(TwoFactorProviders[TwoFactorProviderType.Duo]);
|
providers.push(TwoFactorProviders[TwoFactorProviderType.Duo]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
this.twoFactorProvidersData.has(TwoFactorProviderType.WebAuthn) &&
|
data.has(TwoFactorProviderType.WebAuthn) &&
|
||||||
this.platformUtilsService.supportsWebAuthn(win)
|
this.platformUtilsService.supportsWebAuthn(win)
|
||||||
) {
|
) {
|
||||||
providers.push(TwoFactorProviders[TwoFactorProviderType.WebAuthn]);
|
providers.push(TwoFactorProviders[TwoFactorProviderType.WebAuthn]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.twoFactorProvidersData.has(TwoFactorProviderType.Email)) {
|
if (data.has(TwoFactorProviderType.Email)) {
|
||||||
providers.push(TwoFactorProviders[TwoFactorProviderType.Email]);
|
providers.push(TwoFactorProviders[TwoFactorProviderType.Email]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return providers;
|
return providers;
|
||||||
}
|
}
|
||||||
|
|
||||||
getDefaultProvider(webAuthnSupported: boolean): TwoFactorProviderType {
|
async getDefaultProvider(webAuthnSupported: boolean): Promise<TwoFactorProviderType> {
|
||||||
if (this.twoFactorProvidersData == null) {
|
const data = await firstValueFrom(this.providers$);
|
||||||
|
const selected = await firstValueFrom(this.selected$);
|
||||||
|
if (data == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (selected != null && data.has(selected)) {
|
||||||
this.selectedTwoFactorProviderType != null &&
|
return selected;
|
||||||
this.twoFactorProvidersData.has(this.selectedTwoFactorProviderType)
|
|
||||||
) {
|
|
||||||
return this.selectedTwoFactorProviderType;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let providerType: TwoFactorProviderType = null;
|
let providerType: TwoFactorProviderType = null;
|
||||||
let providerPriority = -1;
|
let providerPriority = -1;
|
||||||
this.twoFactorProvidersData.forEach((_value, type) => {
|
data.forEach((_value, type) => {
|
||||||
const provider = (TwoFactorProviders as any)[type];
|
const provider = (TwoFactorProviders as any)[type];
|
||||||
if (provider != null && provider.priority > providerPriority) {
|
if (provider != null && provider.priority > providerPriority) {
|
||||||
if (type === TwoFactorProviderType.WebAuthn && !webAuthnSupported) {
|
if (type === TwoFactorProviderType.WebAuthn && !webAuthnSupported) {
|
||||||
|
@ -164,23 +188,23 @@ export class TwoFactorService implements TwoFactorServiceAbstraction {
|
||||||
return providerType;
|
return providerType;
|
||||||
}
|
}
|
||||||
|
|
||||||
setSelectedProvider(type: TwoFactorProviderType) {
|
async setSelectedProvider(type: TwoFactorProviderType): Promise<void> {
|
||||||
this.selectedTwoFactorProviderType = type;
|
await this.selectedState.update(() => type);
|
||||||
}
|
}
|
||||||
|
|
||||||
clearSelectedProvider() {
|
async clearSelectedProvider(): Promise<void> {
|
||||||
this.selectedTwoFactorProviderType = null;
|
await this.selectedState.update(() => null);
|
||||||
}
|
}
|
||||||
|
|
||||||
setProviders(response: IdentityTwoFactorResponse) {
|
async setProviders(response: IdentityTwoFactorResponse): Promise<void> {
|
||||||
this.twoFactorProvidersData = response.twoFactorProviders2;
|
await this.providersState.update(() => response.twoFactorProviders2);
|
||||||
}
|
}
|
||||||
|
|
||||||
clearProviders() {
|
async clearProviders(): Promise<void> {
|
||||||
this.twoFactorProvidersData = null;
|
await this.providersState.update(() => null);
|
||||||
}
|
}
|
||||||
|
|
||||||
getProviders() {
|
getProviders(): Promise<Map<TwoFactorProviderType, { [key: string]: string }>> {
|
||||||
return this.twoFactorProvidersData;
|
return firstValueFrom(this.providers$);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,6 +13,7 @@ import { KeySuffixOptions } from "../../../platform/enums/key-suffix-options.enu
|
||||||
import { UserId } from "../../../types/guid";
|
import { UserId } from "../../../types/guid";
|
||||||
import { UserKey } from "../../../types/key";
|
import { UserKey } from "../../../types/key";
|
||||||
import { AccountService } from "../../abstractions/account.service";
|
import { AccountService } from "../../abstractions/account.service";
|
||||||
|
import { KdfConfigService } from "../../abstractions/kdf-config.service";
|
||||||
import { InternalMasterPasswordServiceAbstraction } from "../../abstractions/master-password.service.abstraction";
|
import { InternalMasterPasswordServiceAbstraction } from "../../abstractions/master-password.service.abstraction";
|
||||||
import { UserVerificationApiServiceAbstraction } from "../../abstractions/user-verification/user-verification-api.service.abstraction";
|
import { UserVerificationApiServiceAbstraction } from "../../abstractions/user-verification/user-verification-api.service.abstraction";
|
||||||
import { UserVerificationService as UserVerificationServiceAbstraction } from "../../abstractions/user-verification/user-verification.service.abstraction";
|
import { UserVerificationService as UserVerificationServiceAbstraction } from "../../abstractions/user-verification/user-verification.service.abstraction";
|
||||||
|
@ -47,6 +48,7 @@ export class UserVerificationService implements UserVerificationServiceAbstracti
|
||||||
private logService: LogService,
|
private logService: LogService,
|
||||||
private vaultTimeoutSettingsService: VaultTimeoutSettingsServiceAbstraction,
|
private vaultTimeoutSettingsService: VaultTimeoutSettingsServiceAbstraction,
|
||||||
private platformUtilsService: PlatformUtilsService,
|
private platformUtilsService: PlatformUtilsService,
|
||||||
|
private kdfConfigService: KdfConfigService,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async getAvailableVerificationOptions(
|
async getAvailableVerificationOptions(
|
||||||
|
@ -118,8 +120,7 @@ export class UserVerificationService implements UserVerificationServiceAbstracti
|
||||||
masterKey = await this.cryptoService.makeMasterKey(
|
masterKey = await this.cryptoService.makeMasterKey(
|
||||||
verification.secret,
|
verification.secret,
|
||||||
await this.stateService.getEmail(),
|
await this.stateService.getEmail(),
|
||||||
await this.stateService.getKdfType(),
|
await this.kdfConfigService.getKdfConfig(),
|
||||||
await this.stateService.getKdfConfig(),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
request.masterPasswordHash = alreadyHashed
|
request.masterPasswordHash = alreadyHashed
|
||||||
|
@ -176,8 +177,7 @@ export class UserVerificationService implements UserVerificationServiceAbstracti
|
||||||
masterKey = await this.cryptoService.makeMasterKey(
|
masterKey = await this.cryptoService.makeMasterKey(
|
||||||
verification.secret,
|
verification.secret,
|
||||||
await this.stateService.getEmail(),
|
await this.stateService.getEmail(),
|
||||||
await this.stateService.getKdfType(),
|
await this.kdfConfigService.getKdfConfig(),
|
||||||
await this.stateService.getKdfConfig(),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
const passwordValid = await this.cryptoService.compareAndUpdateKeyHash(
|
const passwordValid = await this.cryptoService.compareAndUpdateKeyHash(
|
||||||
|
|
|
@ -6,7 +6,7 @@ import { ProfileProviderResponse } from "../../admin-console/models/response/pro
|
||||||
import { KdfConfig } from "../../auth/models/domain/kdf-config";
|
import { KdfConfig } from "../../auth/models/domain/kdf-config";
|
||||||
import { OrganizationId, ProviderId, UserId } from "../../types/guid";
|
import { OrganizationId, ProviderId, UserId } from "../../types/guid";
|
||||||
import { UserKey, MasterKey, OrgKey, ProviderKey, PinKey, CipherKey } from "../../types/key";
|
import { UserKey, MasterKey, OrgKey, ProviderKey, PinKey, CipherKey } from "../../types/key";
|
||||||
import { KeySuffixOptions, KdfType, HashPurpose } from "../enums";
|
import { KeySuffixOptions, HashPurpose } from "../enums";
|
||||||
import { EncArrayBuffer } from "../models/domain/enc-array-buffer";
|
import { EncArrayBuffer } from "../models/domain/enc-array-buffer";
|
||||||
import { EncString } from "../models/domain/enc-string";
|
import { EncString } from "../models/domain/enc-string";
|
||||||
import { SymmetricCryptoKey } from "../models/domain/symmetric-crypto-key";
|
import { SymmetricCryptoKey } from "../models/domain/symmetric-crypto-key";
|
||||||
|
@ -114,16 +114,10 @@ export abstract class CryptoService {
|
||||||
* Generates a master key from the provided password
|
* Generates a master key from the provided password
|
||||||
* @param password The user's master password
|
* @param password The user's master password
|
||||||
* @param email The user's email
|
* @param email The user's email
|
||||||
* @param kdf The user's selected key derivation function to use
|
|
||||||
* @param KdfConfig The user's key derivation function configuration
|
* @param KdfConfig The user's key derivation function configuration
|
||||||
* @returns A master key derived from the provided password
|
* @returns A master key derived from the provided password
|
||||||
*/
|
*/
|
||||||
abstract makeMasterKey(
|
abstract makeMasterKey(password: string, email: string, KdfConfig: KdfConfig): Promise<MasterKey>;
|
||||||
password: string,
|
|
||||||
email: string,
|
|
||||||
kdf: KdfType,
|
|
||||||
KdfConfig: KdfConfig,
|
|
||||||
): Promise<MasterKey>;
|
|
||||||
/**
|
/**
|
||||||
* Encrypts the existing (or provided) user key with the
|
* Encrypts the existing (or provided) user key with the
|
||||||
* provided master key
|
* provided master key
|
||||||
|
@ -258,16 +252,10 @@ export abstract class CryptoService {
|
||||||
/**
|
/**
|
||||||
* @param pin The user's pin
|
* @param pin The user's pin
|
||||||
* @param salt The user's salt
|
* @param salt The user's salt
|
||||||
* @param kdf The user's kdf
|
|
||||||
* @param kdfConfig The user's kdf config
|
* @param kdfConfig The user's kdf config
|
||||||
* @returns A key derived from the user's pin
|
* @returns A key derived from the user's pin
|
||||||
*/
|
*/
|
||||||
abstract makePinKey(
|
abstract makePinKey(pin: string, salt: string, kdfConfig: KdfConfig): Promise<PinKey>;
|
||||||
pin: string,
|
|
||||||
salt: string,
|
|
||||||
kdf: KdfType,
|
|
||||||
kdfConfig: KdfConfig,
|
|
||||||
): Promise<PinKey>;
|
|
||||||
/**
|
/**
|
||||||
* Clears the user's pin keys from storage
|
* Clears the user's pin keys from storage
|
||||||
* Note: This will remove the stored pin and as a result,
|
* Note: This will remove the stored pin and as a result,
|
||||||
|
@ -279,7 +267,6 @@ export abstract class CryptoService {
|
||||||
* Decrypts the user key with their pin
|
* Decrypts the user key with their pin
|
||||||
* @param pin The user's PIN
|
* @param pin The user's PIN
|
||||||
* @param salt The user's salt
|
* @param salt The user's salt
|
||||||
* @param kdf The user's KDF
|
|
||||||
* @param kdfConfig The user's KDF config
|
* @param kdfConfig The user's KDF config
|
||||||
* @param pinProtectedUserKey The user's PIN protected symmetric key, if not provided
|
* @param pinProtectedUserKey The user's PIN protected symmetric key, if not provided
|
||||||
* it will be retrieved from storage
|
* it will be retrieved from storage
|
||||||
|
@ -288,7 +275,6 @@ export abstract class CryptoService {
|
||||||
abstract decryptUserKeyWithPin(
|
abstract decryptUserKeyWithPin(
|
||||||
pin: string,
|
pin: string,
|
||||||
salt: string,
|
salt: string,
|
||||||
kdf: KdfType,
|
|
||||||
kdfConfig: KdfConfig,
|
kdfConfig: KdfConfig,
|
||||||
protectedKeyCs?: EncString,
|
protectedKeyCs?: EncString,
|
||||||
): Promise<UserKey>;
|
): Promise<UserKey>;
|
||||||
|
@ -298,7 +284,6 @@ export abstract class CryptoService {
|
||||||
* @param masterPasswordOnRestart True if Master Password on Restart is enabled
|
* @param masterPasswordOnRestart True if Master Password on Restart is enabled
|
||||||
* @param pin User's PIN
|
* @param pin User's PIN
|
||||||
* @param email User's email
|
* @param email User's email
|
||||||
* @param kdf User's KdfType
|
|
||||||
* @param kdfConfig User's KdfConfig
|
* @param kdfConfig User's KdfConfig
|
||||||
* @param oldPinKey The old Pin key from state (retrieved from different
|
* @param oldPinKey The old Pin key from state (retrieved from different
|
||||||
* places depending on if Master Password on Restart was enabled)
|
* places depending on if Master Password on Restart was enabled)
|
||||||
|
@ -308,7 +293,6 @@ export abstract class CryptoService {
|
||||||
masterPasswordOnRestart: boolean,
|
masterPasswordOnRestart: boolean,
|
||||||
pin: string,
|
pin: string,
|
||||||
email: string,
|
email: string,
|
||||||
kdf: KdfType,
|
|
||||||
kdfConfig: KdfConfig,
|
kdfConfig: KdfConfig,
|
||||||
oldPinKey: EncString,
|
oldPinKey: EncString,
|
||||||
): Promise<UserKey>;
|
): Promise<UserKey>;
|
||||||
|
@ -358,21 +342,12 @@ export abstract class CryptoService {
|
||||||
privateKey: EncString;
|
privateKey: EncString;
|
||||||
}>;
|
}>;
|
||||||
|
|
||||||
/**
|
|
||||||
* Validate that the KDF config follows the requirements for the given KDF type.
|
|
||||||
*
|
|
||||||
* @remarks
|
|
||||||
* Should always be called before updating a users KDF config.
|
|
||||||
*/
|
|
||||||
abstract validateKdfConfig(kdf: KdfType, kdfConfig: KdfConfig): void;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @deprecated Left for migration purposes. Use decryptUserKeyWithPin instead.
|
* @deprecated Left for migration purposes. Use decryptUserKeyWithPin instead.
|
||||||
*/
|
*/
|
||||||
abstract decryptMasterKeyWithPin(
|
abstract decryptMasterKeyWithPin(
|
||||||
pin: string,
|
pin: string,
|
||||||
salt: string,
|
salt: string,
|
||||||
kdf: KdfType,
|
|
||||||
kdfConfig: KdfConfig,
|
kdfConfig: KdfConfig,
|
||||||
protectedKeyCs?: EncString,
|
protectedKeyCs?: EncString,
|
||||||
): Promise<MasterKey>;
|
): Promise<MasterKey>;
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import { KdfConfig } from "../../auth/models/domain/kdf-config";
|
import { KdfConfig } from "../../auth/models/domain/kdf-config";
|
||||||
import { CsprngArray } from "../../types/csprng";
|
import { CsprngArray } from "../../types/csprng";
|
||||||
import { KdfType } from "../enums";
|
|
||||||
import { SymmetricCryptoKey } from "../models/domain/symmetric-crypto-key";
|
import { SymmetricCryptoKey } from "../models/domain/symmetric-crypto-key";
|
||||||
|
|
||||||
export abstract class KeyGenerationService {
|
export abstract class KeyGenerationService {
|
||||||
|
@ -46,14 +45,12 @@ export abstract class KeyGenerationService {
|
||||||
* Derives a 32 byte key from a password using a key derivation function.
|
* Derives a 32 byte key from a password using a key derivation function.
|
||||||
* @param password Password to derive the key from.
|
* @param password Password to derive the key from.
|
||||||
* @param salt Salt for the key derivation function.
|
* @param salt Salt for the key derivation function.
|
||||||
* @param kdf Key derivation function to use.
|
|
||||||
* @param kdfConfig Configuration for the key derivation function.
|
* @param kdfConfig Configuration for the key derivation function.
|
||||||
* @returns 32 byte derived key.
|
* @returns 32 byte derived key.
|
||||||
*/
|
*/
|
||||||
abstract deriveKeyFromPassword(
|
abstract deriveKeyFromPassword(
|
||||||
password: string | Uint8Array,
|
password: string | Uint8Array,
|
||||||
salt: string | Uint8Array,
|
salt: string | Uint8Array,
|
||||||
kdf: KdfType,
|
|
||||||
kdfConfig: KdfConfig,
|
kdfConfig: KdfConfig,
|
||||||
): Promise<SymmetricCryptoKey>;
|
): Promise<SymmetricCryptoKey>;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,10 @@
|
||||||
import { Observable } from "rxjs";
|
import { Observable } from "rxjs";
|
||||||
|
|
||||||
import { KdfConfig } from "../../auth/models/domain/kdf-config";
|
|
||||||
import { BiometricKey } from "../../auth/types/biometric-key";
|
import { BiometricKey } from "../../auth/types/biometric-key";
|
||||||
import { GeneratorOptions } from "../../tools/generator/generator-options";
|
import { GeneratorOptions } from "../../tools/generator/generator-options";
|
||||||
import { GeneratedPasswordHistory, PasswordGeneratorOptions } from "../../tools/generator/password";
|
import { GeneratedPasswordHistory, PasswordGeneratorOptions } from "../../tools/generator/password";
|
||||||
import { UsernameGeneratorOptions } from "../../tools/generator/username";
|
import { UsernameGeneratorOptions } from "../../tools/generator/username";
|
||||||
import { UserId } from "../../types/guid";
|
import { UserId } from "../../types/guid";
|
||||||
import { KdfType } from "../enums";
|
|
||||||
import { Account } from "../models/domain/account";
|
import { Account } from "../models/domain/account";
|
||||||
import { EncString } from "../models/domain/enc-string";
|
import { EncString } from "../models/domain/enc-string";
|
||||||
import { StorageOptions } from "../models/domain/storage-options";
|
import { StorageOptions } from "../models/domain/storage-options";
|
||||||
|
@ -149,10 +147,6 @@ export abstract class StateService<T extends Account = Account> {
|
||||||
*/
|
*/
|
||||||
setEncryptedPinProtected: (value: string, options?: StorageOptions) => Promise<void>;
|
setEncryptedPinProtected: (value: string, options?: StorageOptions) => Promise<void>;
|
||||||
getIsAuthenticated: (options?: StorageOptions) => Promise<boolean>;
|
getIsAuthenticated: (options?: StorageOptions) => Promise<boolean>;
|
||||||
getKdfConfig: (options?: StorageOptions) => Promise<KdfConfig>;
|
|
||||||
setKdfConfig: (kdfConfig: KdfConfig, options?: StorageOptions) => Promise<void>;
|
|
||||||
getKdfType: (options?: StorageOptions) => Promise<KdfType>;
|
|
||||||
setKdfType: (value: KdfType, options?: StorageOptions) => Promise<void>;
|
|
||||||
getLastActive: (options?: StorageOptions) => Promise<number>;
|
getLastActive: (options?: StorageOptions) => Promise<number>;
|
||||||
setLastActive: (value: number, options?: StorageOptions) => Promise<void>;
|
setLastActive: (value: number, options?: StorageOptions) => Promise<void>;
|
||||||
getLastSync: (options?: StorageOptions) => Promise<string>;
|
getLastSync: (options?: StorageOptions) => Promise<string>;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { KdfConfig } from "../../auth/models/domain/kdf-config";
|
import { PBKDF2KdfConfig } from "../../auth/models/domain/kdf-config";
|
||||||
import { RangeWithDefault } from "../misc/range-with-default";
|
import { RangeWithDefault } from "../misc/range-with-default";
|
||||||
|
|
||||||
export enum KdfType {
|
export enum KdfType {
|
||||||
|
@ -12,4 +12,4 @@ export const ARGON2_ITERATIONS = new RangeWithDefault(2, 10, 3);
|
||||||
|
|
||||||
export const DEFAULT_KDF_TYPE = KdfType.PBKDF2_SHA256;
|
export const DEFAULT_KDF_TYPE = KdfType.PBKDF2_SHA256;
|
||||||
export const PBKDF2_ITERATIONS = new RangeWithDefault(600_000, 2_000_000, 600_000);
|
export const PBKDF2_ITERATIONS = new RangeWithDefault(600_000, 2_000_000, 600_000);
|
||||||
export const DEFAULT_KDF_CONFIG = new KdfConfig(PBKDF2_ITERATIONS.defaultValue);
|
export const DEFAULT_KDF_CONFIG = new PBKDF2KdfConfig(PBKDF2_ITERATIONS.defaultValue);
|
||||||
|
|
|
@ -4,6 +4,7 @@ import { firstValueFrom, of, tap } from "rxjs";
|
||||||
import { FakeAccountService, mockAccountServiceWith } from "../../../spec/fake-account-service";
|
import { FakeAccountService, mockAccountServiceWith } from "../../../spec/fake-account-service";
|
||||||
import { FakeActiveUserState, FakeSingleUserState } from "../../../spec/fake-state";
|
import { FakeActiveUserState, FakeSingleUserState } from "../../../spec/fake-state";
|
||||||
import { FakeStateProvider } from "../../../spec/fake-state-provider";
|
import { FakeStateProvider } from "../../../spec/fake-state-provider";
|
||||||
|
import { KdfConfigService } from "../../auth/abstractions/kdf-config.service";
|
||||||
import { FakeMasterPasswordService } from "../../auth/services/master-password/fake-master-password.service";
|
import { FakeMasterPasswordService } from "../../auth/services/master-password/fake-master-password.service";
|
||||||
import { CsprngArray } from "../../types/csprng";
|
import { CsprngArray } from "../../types/csprng";
|
||||||
import { UserId } from "../../types/guid";
|
import { UserId } from "../../types/guid";
|
||||||
|
@ -37,6 +38,7 @@ describe("cryptoService", () => {
|
||||||
const platformUtilService = mock<PlatformUtilsService>();
|
const platformUtilService = mock<PlatformUtilsService>();
|
||||||
const logService = mock<LogService>();
|
const logService = mock<LogService>();
|
||||||
const stateService = mock<StateService>();
|
const stateService = mock<StateService>();
|
||||||
|
const kdfConfigService = mock<KdfConfigService>();
|
||||||
let stateProvider: FakeStateProvider;
|
let stateProvider: FakeStateProvider;
|
||||||
|
|
||||||
const mockUserId = Utils.newGuid() as UserId;
|
const mockUserId = Utils.newGuid() as UserId;
|
||||||
|
@ -58,6 +60,7 @@ describe("cryptoService", () => {
|
||||||
stateService,
|
stateService,
|
||||||
accountService,
|
accountService,
|
||||||
stateProvider,
|
stateProvider,
|
||||||
|
kdfConfigService,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,7 @@ import { ProfileOrganizationResponse } from "../../admin-console/models/response
|
||||||
import { ProfileProviderOrganizationResponse } from "../../admin-console/models/response/profile-provider-organization.response";
|
import { ProfileProviderOrganizationResponse } from "../../admin-console/models/response/profile-provider-organization.response";
|
||||||
import { ProfileProviderResponse } from "../../admin-console/models/response/profile-provider.response";
|
import { ProfileProviderResponse } from "../../admin-console/models/response/profile-provider.response";
|
||||||
import { AccountService } from "../../auth/abstractions/account.service";
|
import { AccountService } from "../../auth/abstractions/account.service";
|
||||||
|
import { KdfConfigService } from "../../auth/abstractions/kdf-config.service";
|
||||||
import { InternalMasterPasswordServiceAbstraction } from "../../auth/abstractions/master-password.service.abstraction";
|
import { InternalMasterPasswordServiceAbstraction } from "../../auth/abstractions/master-password.service.abstraction";
|
||||||
import { KdfConfig } from "../../auth/models/domain/kdf-config";
|
import { KdfConfig } from "../../auth/models/domain/kdf-config";
|
||||||
import { Utils } from "../../platform/misc/utils";
|
import { Utils } from "../../platform/misc/utils";
|
||||||
|
@ -28,16 +29,7 @@ import { KeyGenerationService } from "../abstractions/key-generation.service";
|
||||||
import { LogService } from "../abstractions/log.service";
|
import { LogService } from "../abstractions/log.service";
|
||||||
import { PlatformUtilsService } from "../abstractions/platform-utils.service";
|
import { PlatformUtilsService } from "../abstractions/platform-utils.service";
|
||||||
import { StateService } from "../abstractions/state.service";
|
import { StateService } from "../abstractions/state.service";
|
||||||
import {
|
import { KeySuffixOptions, HashPurpose, EncryptionType } from "../enums";
|
||||||
KeySuffixOptions,
|
|
||||||
HashPurpose,
|
|
||||||
KdfType,
|
|
||||||
ARGON2_ITERATIONS,
|
|
||||||
ARGON2_MEMORY,
|
|
||||||
ARGON2_PARALLELISM,
|
|
||||||
EncryptionType,
|
|
||||||
PBKDF2_ITERATIONS,
|
|
||||||
} from "../enums";
|
|
||||||
import { sequentialize } from "../misc/sequentialize";
|
import { sequentialize } from "../misc/sequentialize";
|
||||||
import { EFFLongWordList } from "../misc/wordlist";
|
import { EFFLongWordList } from "../misc/wordlist";
|
||||||
import { EncArrayBuffer } from "../models/domain/enc-array-buffer";
|
import { EncArrayBuffer } from "../models/domain/enc-array-buffer";
|
||||||
|
@ -91,6 +83,7 @@ export class CryptoService implements CryptoServiceAbstraction {
|
||||||
protected stateService: StateService,
|
protected stateService: StateService,
|
||||||
protected accountService: AccountService,
|
protected accountService: AccountService,
|
||||||
protected stateProvider: StateProvider,
|
protected stateProvider: StateProvider,
|
||||||
|
protected kdfConfigService: KdfConfigService,
|
||||||
) {
|
) {
|
||||||
// User Key
|
// User Key
|
||||||
this.activeUserKeyState = stateProvider.getActive(USER_KEY);
|
this.activeUserKeyState = stateProvider.getActive(USER_KEY);
|
||||||
|
@ -283,8 +276,7 @@ export class CryptoService implements CryptoServiceAbstraction {
|
||||||
return (masterKey ||= await this.makeMasterKey(
|
return (masterKey ||= await this.makeMasterKey(
|
||||||
password,
|
password,
|
||||||
await this.stateService.getEmail({ userId: userId }),
|
await this.stateService.getEmail({ userId: userId }),
|
||||||
await this.stateService.getKdfType({ userId: userId }),
|
await this.kdfConfigService.getKdfConfig(),
|
||||||
await this.stateService.getKdfConfig({ userId: userId }),
|
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -295,16 +287,10 @@ export class CryptoService implements CryptoServiceAbstraction {
|
||||||
* Does not validate the kdf config to ensure it satisfies the minimum requirements for the given kdf type.
|
* Does not validate the kdf config to ensure it satisfies the minimum requirements for the given kdf type.
|
||||||
* TODO: Move to MasterPasswordService
|
* TODO: Move to MasterPasswordService
|
||||||
*/
|
*/
|
||||||
async makeMasterKey(
|
async makeMasterKey(password: string, email: string, KdfConfig: KdfConfig): Promise<MasterKey> {
|
||||||
password: string,
|
|
||||||
email: string,
|
|
||||||
kdf: KdfType,
|
|
||||||
KdfConfig: KdfConfig,
|
|
||||||
): Promise<MasterKey> {
|
|
||||||
return (await this.keyGenerationService.deriveKeyFromPassword(
|
return (await this.keyGenerationService.deriveKeyFromPassword(
|
||||||
password,
|
password,
|
||||||
email,
|
email,
|
||||||
kdf,
|
|
||||||
KdfConfig,
|
KdfConfig,
|
||||||
)) as MasterKey;
|
)) as MasterKey;
|
||||||
}
|
}
|
||||||
|
@ -560,8 +546,8 @@ export class CryptoService implements CryptoServiceAbstraction {
|
||||||
await this.stateProvider.setUserState(USER_ENCRYPTED_PRIVATE_KEY, null, userId);
|
await this.stateProvider.setUserState(USER_ENCRYPTED_PRIVATE_KEY, null, userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
async makePinKey(pin: string, salt: string, kdf: KdfType, kdfConfig: KdfConfig): Promise<PinKey> {
|
async makePinKey(pin: string, salt: string, kdfConfig: KdfConfig): Promise<PinKey> {
|
||||||
const pinKey = await this.keyGenerationService.deriveKeyFromPassword(pin, salt, kdf, kdfConfig);
|
const pinKey = await this.keyGenerationService.deriveKeyFromPassword(pin, salt, kdfConfig);
|
||||||
return (await this.stretchKey(pinKey)) as PinKey;
|
return (await this.stretchKey(pinKey)) as PinKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -575,7 +561,6 @@ export class CryptoService implements CryptoServiceAbstraction {
|
||||||
async decryptUserKeyWithPin(
|
async decryptUserKeyWithPin(
|
||||||
pin: string,
|
pin: string,
|
||||||
salt: string,
|
salt: string,
|
||||||
kdf: KdfType,
|
|
||||||
kdfConfig: KdfConfig,
|
kdfConfig: KdfConfig,
|
||||||
pinProtectedUserKey?: EncString,
|
pinProtectedUserKey?: EncString,
|
||||||
): Promise<UserKey> {
|
): Promise<UserKey> {
|
||||||
|
@ -584,7 +569,7 @@ export class CryptoService implements CryptoServiceAbstraction {
|
||||||
if (!pinProtectedUserKey) {
|
if (!pinProtectedUserKey) {
|
||||||
throw new Error("No PIN protected key found.");
|
throw new Error("No PIN protected key found.");
|
||||||
}
|
}
|
||||||
const pinKey = await this.makePinKey(pin, salt, kdf, kdfConfig);
|
const pinKey = await this.makePinKey(pin, salt, kdfConfig);
|
||||||
const userKey = await this.encryptService.decryptToBytes(pinProtectedUserKey, pinKey);
|
const userKey = await this.encryptService.decryptToBytes(pinProtectedUserKey, pinKey);
|
||||||
return new SymmetricCryptoKey(userKey) as UserKey;
|
return new SymmetricCryptoKey(userKey) as UserKey;
|
||||||
}
|
}
|
||||||
|
@ -593,7 +578,6 @@ export class CryptoService implements CryptoServiceAbstraction {
|
||||||
async decryptMasterKeyWithPin(
|
async decryptMasterKeyWithPin(
|
||||||
pin: string,
|
pin: string,
|
||||||
salt: string,
|
salt: string,
|
||||||
kdf: KdfType,
|
|
||||||
kdfConfig: KdfConfig,
|
kdfConfig: KdfConfig,
|
||||||
pinProtectedMasterKey?: EncString,
|
pinProtectedMasterKey?: EncString,
|
||||||
): Promise<MasterKey> {
|
): Promise<MasterKey> {
|
||||||
|
@ -604,7 +588,7 @@ export class CryptoService implements CryptoServiceAbstraction {
|
||||||
}
|
}
|
||||||
pinProtectedMasterKey = new EncString(pinProtectedMasterKeyString);
|
pinProtectedMasterKey = new EncString(pinProtectedMasterKeyString);
|
||||||
}
|
}
|
||||||
const pinKey = await this.makePinKey(pin, salt, kdf, kdfConfig);
|
const pinKey = await this.makePinKey(pin, salt, kdfConfig);
|
||||||
const masterKey = await this.encryptService.decryptToBytes(pinProtectedMasterKey, pinKey);
|
const masterKey = await this.encryptService.decryptToBytes(pinProtectedMasterKey, pinKey);
|
||||||
return new SymmetricCryptoKey(masterKey) as MasterKey;
|
return new SymmetricCryptoKey(masterKey) as MasterKey;
|
||||||
}
|
}
|
||||||
|
@ -831,8 +815,7 @@ export class CryptoService implements CryptoServiceAbstraction {
|
||||||
const pinKey = await this.makePinKey(
|
const pinKey = await this.makePinKey(
|
||||||
pin,
|
pin,
|
||||||
await this.stateService.getEmail({ userId: userId }),
|
await this.stateService.getEmail({ userId: userId }),
|
||||||
await this.stateService.getKdfType({ userId: userId }),
|
await this.kdfConfigService.getKdfConfig(),
|
||||||
await this.stateService.getKdfConfig({ userId: userId }),
|
|
||||||
);
|
);
|
||||||
const encPin = await this.encryptService.encrypt(key.key, pinKey);
|
const encPin = await this.encryptService.encrypt(key.key, pinKey);
|
||||||
|
|
||||||
|
@ -873,43 +856,6 @@ export class CryptoService implements CryptoServiceAbstraction {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Validate that the KDF config follows the requirements for the given KDF type.
|
|
||||||
*
|
|
||||||
* @remarks
|
|
||||||
* Should always be called before updating a users KDF config.
|
|
||||||
*/
|
|
||||||
validateKdfConfig(kdf: KdfType, kdfConfig: KdfConfig): void {
|
|
||||||
switch (kdf) {
|
|
||||||
case KdfType.PBKDF2_SHA256:
|
|
||||||
if (!PBKDF2_ITERATIONS.inRange(kdfConfig.iterations)) {
|
|
||||||
throw new Error(
|
|
||||||
`PBKDF2 iterations must be between ${PBKDF2_ITERATIONS.min} and ${PBKDF2_ITERATIONS.max}`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case KdfType.Argon2id:
|
|
||||||
if (!ARGON2_ITERATIONS.inRange(kdfConfig.iterations)) {
|
|
||||||
throw new Error(
|
|
||||||
`Argon2 iterations must be between ${ARGON2_ITERATIONS.min} and ${ARGON2_ITERATIONS.max}`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!ARGON2_MEMORY.inRange(kdfConfig.memory)) {
|
|
||||||
throw new Error(
|
|
||||||
`Argon2 memory must be between ${ARGON2_MEMORY.min}mb and ${ARGON2_MEMORY.max}mb`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!ARGON2_PARALLELISM.inRange(kdfConfig.parallelism)) {
|
|
||||||
throw new Error(
|
|
||||||
`Argon2 parallelism must be between ${ARGON2_PARALLELISM.min} and ${ARGON2_PARALLELISM.max}.`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected async clearAllStoredUserKeys(userId?: UserId): Promise<void> {
|
protected async clearAllStoredUserKeys(userId?: UserId): Promise<void> {
|
||||||
await this.stateService.setUserKeyAutoUnlock(null, { userId: userId });
|
await this.stateService.setUserKeyAutoUnlock(null, { userId: userId });
|
||||||
await this.stateService.setPinKeyEncryptedUserKeyEphemeral(null, { userId: userId });
|
await this.stateService.setPinKeyEncryptedUserKeyEphemeral(null, { userId: userId });
|
||||||
|
@ -1007,16 +953,15 @@ export class CryptoService implements CryptoServiceAbstraction {
|
||||||
masterPasswordOnRestart: boolean,
|
masterPasswordOnRestart: boolean,
|
||||||
pin: string,
|
pin: string,
|
||||||
email: string,
|
email: string,
|
||||||
kdf: KdfType,
|
|
||||||
kdfConfig: KdfConfig,
|
kdfConfig: KdfConfig,
|
||||||
oldPinKey: EncString,
|
oldPinKey: EncString,
|
||||||
): Promise<UserKey> {
|
): Promise<UserKey> {
|
||||||
// Decrypt
|
// Decrypt
|
||||||
const masterKey = await this.decryptMasterKeyWithPin(pin, email, kdf, kdfConfig, oldPinKey);
|
const masterKey = await this.decryptMasterKeyWithPin(pin, email, kdfConfig, oldPinKey);
|
||||||
const encUserKey = await this.stateService.getEncryptedCryptoSymmetricKey();
|
const encUserKey = await this.stateService.getEncryptedCryptoSymmetricKey();
|
||||||
const userKey = await this.decryptUserKeyWithMasterKey(masterKey, new EncString(encUserKey));
|
const userKey = await this.decryptUserKeyWithMasterKey(masterKey, new EncString(encUserKey));
|
||||||
// Migrate
|
// Migrate
|
||||||
const pinKey = await this.makePinKey(pin, email, kdf, kdfConfig);
|
const pinKey = await this.makePinKey(pin, email, kdfConfig);
|
||||||
const pinProtectedKey = await this.encryptService.encrypt(userKey.key, pinKey);
|
const pinProtectedKey = await this.encryptService.encrypt(userKey.key, pinKey);
|
||||||
if (masterPasswordOnRestart) {
|
if (masterPasswordOnRestart) {
|
||||||
await this.stateService.setDecryptedPinProtected(null);
|
await this.stateService.setDecryptedPinProtected(null);
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
import { mock } from "jest-mock-extended";
|
import { mock } from "jest-mock-extended";
|
||||||
|
|
||||||
import { KdfConfig } from "../../auth/models/domain/kdf-config";
|
import { Argon2KdfConfig, PBKDF2KdfConfig } from "../../auth/models/domain/kdf-config";
|
||||||
import { CsprngArray } from "../../types/csprng";
|
import { CsprngArray } from "../../types/csprng";
|
||||||
import { CryptoFunctionService } from "../abstractions/crypto-function.service";
|
import { CryptoFunctionService } from "../abstractions/crypto-function.service";
|
||||||
import { KdfType } from "../enums";
|
|
||||||
|
|
||||||
import { KeyGenerationService } from "./key-generation.service";
|
import { KeyGenerationService } from "./key-generation.service";
|
||||||
|
|
||||||
|
@ -75,12 +74,11 @@ describe("KeyGenerationService", () => {
|
||||||
it("should derive a 32 byte key from a password using pbkdf2", async () => {
|
it("should derive a 32 byte key from a password using pbkdf2", async () => {
|
||||||
const password = "password";
|
const password = "password";
|
||||||
const salt = "salt";
|
const salt = "salt";
|
||||||
const kdf = KdfType.PBKDF2_SHA256;
|
const kdfConfig = new PBKDF2KdfConfig(600_000);
|
||||||
const kdfConfig = new KdfConfig(600_000);
|
|
||||||
|
|
||||||
cryptoFunctionService.pbkdf2.mockResolvedValue(new Uint8Array(32));
|
cryptoFunctionService.pbkdf2.mockResolvedValue(new Uint8Array(32));
|
||||||
|
|
||||||
const key = await sut.deriveKeyFromPassword(password, salt, kdf, kdfConfig);
|
const key = await sut.deriveKeyFromPassword(password, salt, kdfConfig);
|
||||||
|
|
||||||
expect(key.key.length).toEqual(32);
|
expect(key.key.length).toEqual(32);
|
||||||
});
|
});
|
||||||
|
@ -88,13 +86,12 @@ describe("KeyGenerationService", () => {
|
||||||
it("should derive a 32 byte key from a password using argon2id", async () => {
|
it("should derive a 32 byte key from a password using argon2id", async () => {
|
||||||
const password = "password";
|
const password = "password";
|
||||||
const salt = "salt";
|
const salt = "salt";
|
||||||
const kdf = KdfType.Argon2id;
|
const kdfConfig = new Argon2KdfConfig(3, 16, 4);
|
||||||
const kdfConfig = new KdfConfig(600_000, 15);
|
|
||||||
|
|
||||||
cryptoFunctionService.hash.mockResolvedValue(new Uint8Array(32));
|
cryptoFunctionService.hash.mockResolvedValue(new Uint8Array(32));
|
||||||
cryptoFunctionService.argon2.mockResolvedValue(new Uint8Array(32));
|
cryptoFunctionService.argon2.mockResolvedValue(new Uint8Array(32));
|
||||||
|
|
||||||
const key = await sut.deriveKeyFromPassword(password, salt, kdf, kdfConfig);
|
const key = await sut.deriveKeyFromPassword(password, salt, kdfConfig);
|
||||||
|
|
||||||
expect(key.key.length).toEqual(32);
|
expect(key.key.length).toEqual(32);
|
||||||
});
|
});
|
||||||
|
|
|
@ -46,17 +46,16 @@ export class KeyGenerationService implements KeyGenerationServiceAbstraction {
|
||||||
async deriveKeyFromPassword(
|
async deriveKeyFromPassword(
|
||||||
password: string | Uint8Array,
|
password: string | Uint8Array,
|
||||||
salt: string | Uint8Array,
|
salt: string | Uint8Array,
|
||||||
kdf: KdfType,
|
|
||||||
kdfConfig: KdfConfig,
|
kdfConfig: KdfConfig,
|
||||||
): Promise<SymmetricCryptoKey> {
|
): Promise<SymmetricCryptoKey> {
|
||||||
let key: Uint8Array = null;
|
let key: Uint8Array = null;
|
||||||
if (kdf == null || kdf === KdfType.PBKDF2_SHA256) {
|
if (kdfConfig.kdfType == null || kdfConfig.kdfType === KdfType.PBKDF2_SHA256) {
|
||||||
if (kdfConfig.iterations == null) {
|
if (kdfConfig.iterations == null) {
|
||||||
kdfConfig.iterations = PBKDF2_ITERATIONS.defaultValue;
|
kdfConfig.iterations = PBKDF2_ITERATIONS.defaultValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
key = await this.cryptoFunctionService.pbkdf2(password, salt, "sha256", kdfConfig.iterations);
|
key = await this.cryptoFunctionService.pbkdf2(password, salt, "sha256", kdfConfig.iterations);
|
||||||
} else if (kdf == KdfType.Argon2id) {
|
} else if (kdfConfig.kdfType == KdfType.Argon2id) {
|
||||||
if (kdfConfig.iterations == null) {
|
if (kdfConfig.iterations == null) {
|
||||||
kdfConfig.iterations = ARGON2_ITERATIONS.defaultValue;
|
kdfConfig.iterations = ARGON2_ITERATIONS.defaultValue;
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,6 @@ import { Jsonify, JsonValue } from "type-fest";
|
||||||
|
|
||||||
import { AccountService } from "../../auth/abstractions/account.service";
|
import { AccountService } from "../../auth/abstractions/account.service";
|
||||||
import { TokenService } from "../../auth/abstractions/token.service";
|
import { TokenService } from "../../auth/abstractions/token.service";
|
||||||
import { KdfConfig } from "../../auth/models/domain/kdf-config";
|
|
||||||
import { BiometricKey } from "../../auth/types/biometric-key";
|
import { BiometricKey } from "../../auth/types/biometric-key";
|
||||||
import { GeneratorOptions } from "../../tools/generator/generator-options";
|
import { GeneratorOptions } from "../../tools/generator/generator-options";
|
||||||
import { GeneratedPasswordHistory, PasswordGeneratorOptions } from "../../tools/generator/password";
|
import { GeneratedPasswordHistory, PasswordGeneratorOptions } from "../../tools/generator/password";
|
||||||
|
@ -19,7 +18,7 @@ import {
|
||||||
AbstractMemoryStorageService,
|
AbstractMemoryStorageService,
|
||||||
AbstractStorageService,
|
AbstractStorageService,
|
||||||
} from "../abstractions/storage.service";
|
} from "../abstractions/storage.service";
|
||||||
import { HtmlStorageLocation, KdfType, StorageLocation } from "../enums";
|
import { HtmlStorageLocation, StorageLocation } from "../enums";
|
||||||
import { StateFactory } from "../factories/state-factory";
|
import { StateFactory } from "../factories/state-factory";
|
||||||
import { Utils } from "../misc/utils";
|
import { Utils } from "../misc/utils";
|
||||||
import { Account, AccountData, AccountSettings } from "../models/domain/account";
|
import { Account, AccountData, AccountSettings } from "../models/domain/account";
|
||||||
|
@ -643,49 +642,6 @@ export class StateService<
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
async getKdfConfig(options?: StorageOptions): Promise<KdfConfig> {
|
|
||||||
const iterations = (
|
|
||||||
await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskOptions()))
|
|
||||||
)?.profile?.kdfIterations;
|
|
||||||
const memory = (
|
|
||||||
await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskOptions()))
|
|
||||||
)?.profile?.kdfMemory;
|
|
||||||
const parallelism = (
|
|
||||||
await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskOptions()))
|
|
||||||
)?.profile?.kdfParallelism;
|
|
||||||
return new KdfConfig(iterations, memory, parallelism);
|
|
||||||
}
|
|
||||||
|
|
||||||
async setKdfConfig(config: KdfConfig, options?: StorageOptions): Promise<void> {
|
|
||||||
const account = await this.getAccount(
|
|
||||||
this.reconcileOptions(options, await this.defaultOnDiskOptions()),
|
|
||||||
);
|
|
||||||
account.profile.kdfIterations = config.iterations;
|
|
||||||
account.profile.kdfMemory = config.memory;
|
|
||||||
account.profile.kdfParallelism = config.parallelism;
|
|
||||||
await this.saveAccount(
|
|
||||||
account,
|
|
||||||
this.reconcileOptions(options, await this.defaultOnDiskOptions()),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
async getKdfType(options?: StorageOptions): Promise<KdfType> {
|
|
||||||
return (
|
|
||||||
await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskOptions()))
|
|
||||||
)?.profile?.kdfType;
|
|
||||||
}
|
|
||||||
|
|
||||||
async setKdfType(value: KdfType, options?: StorageOptions): Promise<void> {
|
|
||||||
const account = await this.getAccount(
|
|
||||||
this.reconcileOptions(options, await this.defaultOnDiskOptions()),
|
|
||||||
);
|
|
||||||
account.profile.kdfType = value;
|
|
||||||
await this.saveAccount(
|
|
||||||
account,
|
|
||||||
this.reconcileOptions(options, await this.defaultOnDiskOptions()),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
async getLastActive(options?: StorageOptions): Promise<number> {
|
async getLastActive(options?: StorageOptions): Promise<number> {
|
||||||
options = this.reconcileOptions(options, await this.defaultOnDiskOptions());
|
options = this.reconcileOptions(options, await this.defaultOnDiskOptions());
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
import { record } from "./deserialization-helpers";
|
||||||
|
|
||||||
|
describe("deserialization helpers", () => {
|
||||||
|
describe("record", () => {
|
||||||
|
it("deserializes a record when keys are strings", () => {
|
||||||
|
const deserializer = record((value: number) => value);
|
||||||
|
const input = {
|
||||||
|
a: 1,
|
||||||
|
b: 2,
|
||||||
|
};
|
||||||
|
const output = deserializer(input);
|
||||||
|
expect(output).toEqual(input);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("deserializes a record when keys are numbers", () => {
|
||||||
|
const deserializer = record((value: number) => value);
|
||||||
|
const input = {
|
||||||
|
1: 1,
|
||||||
|
2: 2,
|
||||||
|
};
|
||||||
|
const output = deserializer(input);
|
||||||
|
expect(output).toEqual(input);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -21,7 +21,7 @@ export function array<T>(
|
||||||
*
|
*
|
||||||
* @param valueDeserializer
|
* @param valueDeserializer
|
||||||
*/
|
*/
|
||||||
export function record<T, TKey extends string = string>(
|
export function record<T, TKey extends string | number = string>(
|
||||||
valueDeserializer: (value: Jsonify<T>) => T,
|
valueDeserializer: (value: Jsonify<T>) => T,
|
||||||
): (record: Jsonify<Record<TKey, T>>) => Record<TKey, T> {
|
): (record: Jsonify<Record<TKey, T>>) => Record<TKey, T> {
|
||||||
return (jsonValue: Jsonify<Record<TKey, T> | null>) => {
|
return (jsonValue: Jsonify<Record<TKey, T> | null>) => {
|
||||||
|
@ -29,10 +29,10 @@ export function record<T, TKey extends string = string>(
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const output: Record<string, T> = {};
|
const output: Record<TKey, T> = {} as any;
|
||||||
for (const key in jsonValue) {
|
Object.entries(jsonValue).forEach(([key, value]) => {
|
||||||
output[key] = valueDeserializer((jsonValue as Record<string, Jsonify<T>>)[key]);
|
output[key as TKey] = valueDeserializer(value);
|
||||||
}
|
});
|
||||||
return output;
|
return output;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -113,7 +113,7 @@ export class KeyDefinition<T> {
|
||||||
* });
|
* });
|
||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
static record<T, TKey extends string = string>(
|
static record<T, TKey extends string | number = string>(
|
||||||
stateDefinition: StateDefinition,
|
stateDefinition: StateDefinition,
|
||||||
key: string,
|
key: string,
|
||||||
// We have them provide options for the value of the record, depending on future options we add, this could get a little weird.
|
// We have them provide options for the value of the record, depending on future options we add, this could get a little weird.
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue