Merge branch 'main' of https://github.com/bitwarden/clients into ac/ac-217/migrate-to-cl-banners

This commit is contained in:
nick-livefront 2024-04-25 10:37:18 -05:00
commit f0af9a933a
No known key found for this signature in database
GPG Key ID: FF670021ABCAB82E
82 changed files with 544 additions and 594 deletions

View File

@ -955,11 +955,7 @@ jobs:
keyvault: "bitwarden-ci" keyvault: "bitwarden-ci"
secrets: "aws-electron-access-id, secrets: "aws-electron-access-id,
aws-electron-access-key, aws-electron-access-key,
aws-electron-bucket-name, aws-electron-bucket-name"
r2-electron-access-id,
r2-electron-access-key,
r2-electron-bucket-name,
cf-prod-account"
- name: Download all artifacts - name: Download all artifacts
uses: actions/download-artifact@c850b930e6ba138125429b7e5c93fc707a7f8427 # v4.1.4 uses: actions/download-artifact@c850b930e6ba138125429b7e5c93fc707a7f8427 # v4.1.4
@ -985,20 +981,6 @@ jobs:
--recursive \ --recursive \
--quiet --quiet
- name: Publish artifacts to R2
env:
AWS_ACCESS_KEY_ID: ${{ steps.retrieve-secrets.outputs.r2-electron-access-id }}
AWS_SECRET_ACCESS_KEY: ${{ steps.retrieve-secrets.outputs.r2-electron-access-key }}
AWS_DEFAULT_REGION: 'us-east-1'
AWS_S3_BUCKET_NAME: ${{ steps.retrieve-secrets.outputs.r2-electron-bucket-name }}
CF_ACCOUNT: ${{ steps.retrieve-secrets.outputs.cf-prod-account }}
working-directory: apps/desktop/artifacts
run: |
aws s3 cp ./ $AWS_S3_BUCKET_NAME/desktop/ \
--recursive \
--quiet \
--endpoint-url https://${CF_ACCOUNT}.r2.cloudflarestorage.com
- name: Update deployment status to Success - name: Update deployment status to Success
if: ${{ success() }} if: ${{ success() }}
uses: chrnorm/deployment-status@9a72af4586197112e0491ea843682b5dc280d806 # v2.0.3 uses: chrnorm/deployment-status@9a72af4586197112e0491ea843682b5dc280d806 # v2.0.3

View File

@ -115,11 +115,7 @@ jobs:
keyvault: "bitwarden-ci" keyvault: "bitwarden-ci"
secrets: "aws-electron-access-id, secrets: "aws-electron-access-id,
aws-electron-access-key, aws-electron-access-key,
aws-electron-bucket-name, aws-electron-bucket-name"
r2-electron-access-id,
r2-electron-access-key,
r2-electron-bucket-name,
cf-prod-account"
- name: Download all artifacts - name: Download all artifacts
if: ${{ github.event.inputs.release_type != 'Dry Run' }} if: ${{ github.event.inputs.release_type != 'Dry Run' }}
@ -169,21 +165,6 @@ jobs:
--recursive \ --recursive \
--quiet --quiet
- name: Publish artifacts to R2
if: ${{ github.event.inputs.release_type != 'Dry Run' && github.event.inputs.electron_publish == 'true' }}
env:
AWS_ACCESS_KEY_ID: ${{ steps.retrieve-secrets.outputs.r2-electron-access-id }}
AWS_SECRET_ACCESS_KEY: ${{ steps.retrieve-secrets.outputs.r2-electron-access-key }}
AWS_DEFAULT_REGION: 'us-east-1'
AWS_S3_BUCKET_NAME: ${{ steps.retrieve-secrets.outputs.r2-electron-bucket-name }}
CF_ACCOUNT: ${{ steps.retrieve-secrets.outputs.cf-prod-account }}
working-directory: apps/desktop/artifacts
run: |
aws s3 cp ./ $AWS_S3_BUCKET_NAME/desktop/ \
--recursive \
--quiet \
--endpoint-url https://${CF_ACCOUNT}.r2.cloudflarestorage.com
- name: Get checksum files - name: Get checksum files
uses: bitwarden/gh-actions/get-checksum@main uses: bitwarden/gh-actions/get-checksum@main
with: with:

View File

@ -31,29 +31,21 @@ jobs:
keyvault: "bitwarden-ci" keyvault: "bitwarden-ci"
secrets: "aws-electron-access-id, secrets: "aws-electron-access-id,
aws-electron-access-key, aws-electron-access-key,
aws-electron-bucket-name, aws-electron-bucket-name"
r2-electron-access-id,
r2-electron-access-key,
r2-electron-bucket-name,
cf-prod-account"
- name: Download channel update info files from R2 - name: Download channel update info files from S3
env: env:
AWS_ACCESS_KEY_ID: ${{ steps.retrieve-secrets.outputs.r2-electron-access-id }} AWS_ACCESS_KEY_ID: ${{ steps.retrieve-secrets.outputs.aws-electron-access-id }}
AWS_SECRET_ACCESS_KEY: ${{ steps.retrieve-secrets.outputs.r2-electron-access-key }} AWS_SECRET_ACCESS_KEY: ${{ steps.retrieve-secrets.outputs.aws-electron-access-key }}
AWS_DEFAULT_REGION: 'us-east-1' AWS_DEFAULT_REGION: 'us-west-2'
AWS_S3_BUCKET_NAME: ${{ steps.retrieve-secrets.outputs.r2-electron-bucket-name }} AWS_S3_BUCKET_NAME: ${{ steps.retrieve-secrets.outputs.aws-electron-bucket-name }}
CF_ACCOUNT: ${{ steps.retrieve-secrets.outputs.cf-prod-account }}
run: | run: |
aws s3 cp $AWS_S3_BUCKET_NAME/desktop/latest.yml . \ aws s3 cp $AWS_S3_BUCKET_NAME/desktop/latest.yml . \
--quiet \ --quiet \
--endpoint-url https://${CF_ACCOUNT}.r2.cloudflarestorage.com
aws s3 cp $AWS_S3_BUCKET_NAME/desktop/latest-linux.yml . \ aws s3 cp $AWS_S3_BUCKET_NAME/desktop/latest-linux.yml . \
--quiet \ --quiet \
--endpoint-url https://${CF_ACCOUNT}.r2.cloudflarestorage.com
aws s3 cp $AWS_S3_BUCKET_NAME/desktop/latest-mac.yml . \ aws s3 cp $AWS_S3_BUCKET_NAME/desktop/latest-mac.yml . \
--quiet \ --quiet \
--endpoint-url https://${CF_ACCOUNT}.r2.cloudflarestorage.com
- name: Check new rollout percentage - name: Check new rollout percentage
env: env:
@ -95,20 +87,3 @@ jobs:
aws s3 cp latest-mac.yml $AWS_S3_BUCKET_NAME/desktop/ \ aws s3 cp latest-mac.yml $AWS_S3_BUCKET_NAME/desktop/ \
--acl "public-read" --acl "public-read"
- name: Publish channel update info files to R2
env:
AWS_ACCESS_KEY_ID: ${{ steps.retrieve-secrets.outputs.r2-electron-access-id }}
AWS_SECRET_ACCESS_KEY: ${{ steps.retrieve-secrets.outputs.r2-electron-access-key }}
AWS_DEFAULT_REGION: 'us-east-1'
AWS_S3_BUCKET_NAME: ${{ steps.retrieve-secrets.outputs.r2-electron-bucket-name }}
CF_ACCOUNT: ${{ steps.retrieve-secrets.outputs.cf-prod-account }}
run: |
aws s3 cp latest.yml $AWS_S3_BUCKET_NAME/desktop/ \
--endpoint-url https://${CF_ACCOUNT}.r2.cloudflarestorage.com
aws s3 cp latest-linux.yml $AWS_S3_BUCKET_NAME/desktop/ \
--endpoint-url https://${CF_ACCOUNT}.r2.cloudflarestorage.com
aws s3 cp latest-mac.yml $AWS_S3_BUCKET_NAME/desktop/ \
--endpoint-url https://${CF_ACCOUNT}.r2.cloudflarestorage.com

View File

@ -1,5 +1,5 @@
import { DeviceTrustCryptoServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust-crypto.service.abstraction"; import { DeviceTrustServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust.service.abstraction";
import { DeviceTrustCryptoService } from "@bitwarden/common/auth/services/device-trust-crypto.service.implementation"; import { DeviceTrustService } from "@bitwarden/common/auth/services/device-trust.service.implementation";
import { import {
DevicesApiServiceInitOptions, DevicesApiServiceInitOptions,
@ -52,9 +52,9 @@ import {
userDecryptionOptionsServiceFactory, userDecryptionOptionsServiceFactory,
} from "./user-decryption-options-service.factory"; } from "./user-decryption-options-service.factory";
type DeviceTrustCryptoServiceFactoryOptions = FactoryOptions; type DeviceTrustServiceFactoryOptions = FactoryOptions;
export type DeviceTrustCryptoServiceInitOptions = DeviceTrustCryptoServiceFactoryOptions & export type DeviceTrustServiceInitOptions = DeviceTrustServiceFactoryOptions &
KeyGenerationServiceInitOptions & KeyGenerationServiceInitOptions &
CryptoFunctionServiceInitOptions & CryptoFunctionServiceInitOptions &
CryptoServiceInitOptions & CryptoServiceInitOptions &
@ -67,16 +67,16 @@ export type DeviceTrustCryptoServiceInitOptions = DeviceTrustCryptoServiceFactor
SecureStorageServiceInitOptions & SecureStorageServiceInitOptions &
UserDecryptionOptionsServiceInitOptions; UserDecryptionOptionsServiceInitOptions;
export function deviceTrustCryptoServiceFactory( export function deviceTrustServiceFactory(
cache: { deviceTrustCryptoService?: DeviceTrustCryptoServiceAbstraction } & CachedServices, cache: { deviceTrustService?: DeviceTrustServiceAbstraction } & CachedServices,
opts: DeviceTrustCryptoServiceInitOptions, opts: DeviceTrustServiceInitOptions,
): Promise<DeviceTrustCryptoServiceAbstraction> { ): Promise<DeviceTrustServiceAbstraction> {
return factory( return factory(
cache, cache,
"deviceTrustCryptoService", "deviceTrustService",
opts, opts,
async () => async () =>
new DeviceTrustCryptoService( new DeviceTrustService(
await keyGenerationServiceFactory(cache, opts), await keyGenerationServiceFactory(cache, opts),
await cryptoFunctionServiceFactory(cache, opts), await cryptoFunctionServiceFactory(cache, opts),
await cryptoServiceFactory(cache, opts), await cryptoServiceFactory(cache, opts),

View File

@ -65,9 +65,9 @@ import {
AuthRequestServiceInitOptions, AuthRequestServiceInitOptions,
} from "./auth-request-service.factory"; } from "./auth-request-service.factory";
import { import {
deviceTrustCryptoServiceFactory, deviceTrustServiceFactory,
DeviceTrustCryptoServiceInitOptions, DeviceTrustServiceInitOptions,
} from "./device-trust-crypto-service.factory"; } from "./device-trust-service.factory";
import { import {
keyConnectorServiceFactory, keyConnectorServiceFactory,
KeyConnectorServiceInitOptions, KeyConnectorServiceInitOptions,
@ -102,7 +102,7 @@ export type LoginStrategyServiceInitOptions = LoginStrategyServiceFactoryOptions
EncryptServiceInitOptions & EncryptServiceInitOptions &
PolicyServiceInitOptions & PolicyServiceInitOptions &
PasswordStrengthServiceInitOptions & PasswordStrengthServiceInitOptions &
DeviceTrustCryptoServiceInitOptions & DeviceTrustServiceInitOptions &
AuthRequestServiceInitOptions & AuthRequestServiceInitOptions &
UserDecryptionOptionsServiceInitOptions & UserDecryptionOptionsServiceInitOptions &
GlobalStateProviderInitOptions & GlobalStateProviderInitOptions &
@ -135,7 +135,7 @@ export function loginStrategyServiceFactory(
await encryptServiceFactory(cache, opts), await encryptServiceFactory(cache, opts),
await passwordStrengthServiceFactory(cache, opts), await passwordStrengthServiceFactory(cache, opts),
await policyServiceFactory(cache, opts), await policyServiceFactory(cache, opts),
await deviceTrustCryptoServiceFactory(cache, opts), await deviceTrustServiceFactory(cache, opts),
await authRequestServiceFactory(cache, opts), await authRequestServiceFactory(cache, opts),
await internalUserDecryptionOptionServiceFactory(cache, opts), await internalUserDecryptionOptionServiceFactory(cache, opts),
await globalStateProviderFactory(cache, opts), await globalStateProviderFactory(cache, opts),

View File

@ -11,7 +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 { AuthService } from "@bitwarden/common/auth/abstractions/auth.service"; import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service";
import { DeviceTrustCryptoServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust-crypto.service.abstraction"; import { DeviceTrustServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust.service.abstraction";
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";
@ -60,7 +60,7 @@ export class LockComponent extends BaseLockComponent {
passwordStrengthService: PasswordStrengthServiceAbstraction, passwordStrengthService: PasswordStrengthServiceAbstraction,
private authService: AuthService, private authService: AuthService,
dialogService: DialogService, dialogService: DialogService,
deviceTrustCryptoService: DeviceTrustCryptoServiceAbstraction, deviceTrustService: DeviceTrustServiceAbstraction,
userVerificationService: UserVerificationService, userVerificationService: UserVerificationService,
pinCryptoService: PinCryptoServiceAbstraction, pinCryptoService: PinCryptoServiceAbstraction,
private routerService: BrowserRouterService, private routerService: BrowserRouterService,
@ -85,7 +85,7 @@ export class LockComponent extends BaseLockComponent {
policyService, policyService,
passwordStrengthService, passwordStrengthService,
dialogService, dialogService,
deviceTrustCryptoService, deviceTrustService,
userVerificationService, userVerificationService,
pinCryptoService, pinCryptoService,
biometricStateService, biometricStateService,

View File

@ -12,7 +12,7 @@ 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 { AnonymousHubService } from "@bitwarden/common/auth/abstractions/anonymous-hub.service"; import { AnonymousHubService } from "@bitwarden/common/auth/abstractions/anonymous-hub.service";
import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service"; import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service";
import { DeviceTrustCryptoServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust-crypto.service.abstraction"; import { DeviceTrustServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust.service.abstraction";
import { AppIdService } from "@bitwarden/common/platform/abstractions/app-id.service"; import { AppIdService } from "@bitwarden/common/platform/abstractions/app-id.service";
import { CryptoFunctionService } from "@bitwarden/common/platform/abstractions/crypto-function.service"; import { CryptoFunctionService } from "@bitwarden/common/platform/abstractions/crypto-function.service";
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service"; import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
@ -47,7 +47,7 @@ export class LoginViaAuthRequestComponent extends BaseLoginWithDeviceComponent {
stateService: StateService, stateService: StateService,
loginEmailService: LoginEmailServiceAbstraction, loginEmailService: LoginEmailServiceAbstraction,
syncService: SyncService, syncService: SyncService,
deviceTrustCryptoService: DeviceTrustCryptoServiceAbstraction, deviceTrustService: DeviceTrustServiceAbstraction,
authRequestService: AuthRequestServiceAbstraction, authRequestService: AuthRequestServiceAbstraction,
loginStrategyService: LoginStrategyServiceAbstraction, loginStrategyService: LoginStrategyServiceAbstraction,
accountService: AccountService, accountService: AccountService,
@ -69,7 +69,7 @@ export class LoginViaAuthRequestComponent extends BaseLoginWithDeviceComponent {
validationService, validationService,
stateService, stateService,
loginEmailService, loginEmailService,
deviceTrustCryptoService, deviceTrustService,
authRequestService, authRequestService,
loginStrategyService, loginStrategyService,
accountService, accountService,

View File

@ -30,7 +30,7 @@ import { ProviderService } from "@bitwarden/common/admin-console/services/provid
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 { AvatarService as AvatarServiceAbstraction } from "@bitwarden/common/auth/abstractions/avatar.service"; import { AvatarService as AvatarServiceAbstraction } from "@bitwarden/common/auth/abstractions/avatar.service";
import { DeviceTrustCryptoServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust-crypto.service.abstraction"; import { 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 { KeyConnectorService as KeyConnectorServiceAbstraction } from "@bitwarden/common/auth/abstractions/key-connector.service"; import { KeyConnectorService as KeyConnectorServiceAbstraction } from "@bitwarden/common/auth/abstractions/key-connector.service";
@ -45,7 +45,7 @@ import { ForceSetPasswordReason } from "@bitwarden/common/auth/models/domain/for
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 { DeviceTrustCryptoService } from "@bitwarden/common/auth/services/device-trust-crypto.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 { KeyConnectorService } from "@bitwarden/common/auth/services/key-connector.service"; import { KeyConnectorService } from "@bitwarden/common/auth/services/key-connector.service";
@ -318,7 +318,7 @@ export default class MainBackground {
configApiService: ConfigApiServiceAbstraction; configApiService: ConfigApiServiceAbstraction;
devicesApiService: DevicesApiServiceAbstraction; devicesApiService: DevicesApiServiceAbstraction;
devicesService: DevicesServiceAbstraction; devicesService: DevicesServiceAbstraction;
deviceTrustCryptoService: DeviceTrustCryptoServiceAbstraction; deviceTrustService: DeviceTrustServiceAbstraction;
authRequestService: AuthRequestServiceAbstraction; authRequestService: AuthRequestServiceAbstraction;
accountService: AccountServiceAbstraction; accountService: AccountServiceAbstraction;
globalStateProvider: GlobalStateProvider; globalStateProvider: GlobalStateProvider;
@ -612,7 +612,7 @@ export default class MainBackground {
this.userDecryptionOptionsService = new UserDecryptionOptionsService(this.stateProvider); this.userDecryptionOptionsService = new UserDecryptionOptionsService(this.stateProvider);
this.devicesApiService = new DevicesApiServiceImplementation(this.apiService); this.devicesApiService = new DevicesApiServiceImplementation(this.apiService);
this.deviceTrustCryptoService = new DeviceTrustCryptoService( this.deviceTrustService = new DeviceTrustService(
this.keyGenerationService, this.keyGenerationService,
this.cryptoFunctionService, this.cryptoFunctionService,
this.cryptoService, this.cryptoService,
@ -670,7 +670,7 @@ export default class MainBackground {
this.encryptService, this.encryptService,
this.passwordStrengthService, this.passwordStrengthService,
this.policyService, this.policyService,
this.deviceTrustCryptoService, this.deviceTrustService,
this.authRequestService, this.authRequestService,
this.userDecryptionOptionsService, this.userDecryptionOptionsService,
this.globalStateProvider, this.globalStateProvider,
@ -813,6 +813,7 @@ export default class MainBackground {
this.avatarService, this.avatarService,
logoutCallback, logoutCallback,
this.billingAccountProfileStateService, this.billingAccountProfileStateService,
this.tokenService,
); );
this.eventUploadService = new EventUploadService( this.eventUploadService = new EventUploadService(
this.apiService, this.apiService,
@ -1095,7 +1096,8 @@ export default class MainBackground {
async bootstrap() { async bootstrap() {
this.containerService.attachToGlobal(self); this.containerService.attachToGlobal(self);
await this.stateService.init({ runMigrations: !this.isPrivateMode }); // Only the "true" background should run migrations
await this.stateService.init({ runMigrations: !this.popupOnlyContext });
// This is here instead of in in the InitService b/c we don't plan for // This is here instead of in in the InitService b/c we don't plan for
// side effects to run in the Browser InitService. // side effects to run in the Browser InitService.

View File

@ -10,7 +10,7 @@ import { SystemService } from "@bitwarden/common/platform/abstractions/system.se
import { Utils } from "@bitwarden/common/platform/misc/utils"; import { Utils } from "@bitwarden/common/platform/misc/utils";
import { CipherType } from "@bitwarden/common/vault/enums"; import { CipherType } from "@bitwarden/common/vault/enums";
import { MessageListener } from "../../../../libs/common/src/platform/messaging"; import { MessageListener, isExternalMessage } from "../../../../libs/common/src/platform/messaging";
import { import {
closeUnlockPopout, closeUnlockPopout,
openSsoAuthResultPopout, openSsoAuthResultPopout,
@ -266,7 +266,9 @@ export default class RuntimeBackground {
break; break;
} }
case "reloadPopup": case "reloadPopup":
this.messagingService.send("reloadPopup"); if (isExternalMessage(msg)) {
this.messagingService.send("reloadPopup");
}
break; break;
case "emailVerificationRequired": case "emailVerificationRequired":
this.messagingService.send("showDialog", { this.messagingService.send("showDialog", {

View File

@ -30,7 +30,6 @@ import {
type StateServiceFactoryOptions = FactoryOptions & { type StateServiceFactoryOptions = FactoryOptions & {
stateServiceOptions: { stateServiceOptions: {
useAccountCache?: boolean;
stateFactory: StateFactory<GlobalState, Account>; stateFactory: StateFactory<GlobalState, Account>;
}; };
}; };
@ -64,7 +63,6 @@ export async function stateServiceFactory(
await environmentServiceFactory(cache, opts), await environmentServiceFactory(cache, opts),
await tokenServiceFactory(cache, opts), await tokenServiceFactory(cache, opts),
await migrationRunnerFactory(cache, opts), await migrationRunnerFactory(cache, opts),
opts.stateServiceOptions.useAccountCache,
), ),
); );
// TODO: If we run migration through a chrome installed/updated event we can turn off running migrations // TODO: If we run migration through a chrome installed/updated event we can turn off running migrations

View File

@ -27,7 +27,6 @@ describe("Browser State Service", () => {
let diskStorageService: MockProxy<AbstractStorageService>; let diskStorageService: MockProxy<AbstractStorageService>;
let logService: MockProxy<LogService>; let logService: MockProxy<LogService>;
let stateFactory: MockProxy<StateFactory<GlobalState, Account>>; let stateFactory: MockProxy<StateFactory<GlobalState, Account>>;
let useAccountCache: boolean;
let environmentService: MockProxy<EnvironmentService>; let environmentService: MockProxy<EnvironmentService>;
let tokenService: MockProxy<TokenService>; let tokenService: MockProxy<TokenService>;
let migrationRunner: MockProxy<MigrationRunner>; let migrationRunner: MockProxy<MigrationRunner>;
@ -46,8 +45,6 @@ describe("Browser State Service", () => {
environmentService = mock(); environmentService = mock();
tokenService = mock(); tokenService = mock();
migrationRunner = mock(); migrationRunner = mock();
// turn off account cache for tests
useAccountCache = false;
state = new State(new GlobalState()); state = new State(new GlobalState());
state.accounts[userId] = new Account({ state.accounts[userId] = new Account({
@ -78,7 +75,6 @@ describe("Browser State Service", () => {
environmentService, environmentService,
tokenService, tokenService,
migrationRunner, migrationRunner,
useAccountCache,
); );
}); });

View File

@ -15,7 +15,6 @@ import { MigrationRunner } from "@bitwarden/common/platform/services/migration-r
import { StateService as BaseStateService } from "@bitwarden/common/platform/services/state.service"; import { StateService as BaseStateService } from "@bitwarden/common/platform/services/state.service";
import { Account } from "../../models/account"; import { Account } from "../../models/account";
import { BrowserApi } from "../browser/browser-api";
import { browserSession, sessionSync } from "../decorators/session-sync-observable"; import { browserSession, sessionSync } from "../decorators/session-sync-observable";
import { BrowserStateService } from "./abstractions/browser-state.service"; import { BrowserStateService } from "./abstractions/browser-state.service";
@ -45,7 +44,6 @@ export class DefaultBrowserStateService
environmentService: EnvironmentService, environmentService: EnvironmentService,
tokenService: TokenService, tokenService: TokenService,
migrationRunner: MigrationRunner, migrationRunner: MigrationRunner,
useAccountCache = true,
) { ) {
super( super(
storageService, storageService,
@ -57,45 +55,7 @@ export class DefaultBrowserStateService
environmentService, environmentService,
tokenService, tokenService,
migrationRunner, migrationRunner,
useAccountCache,
); );
// TODO: This is a hack to fix having a disk cache on both the popup and
// the background page that can get out of sync. We need to work out the
// best way to handle caching with multiple instances of the state service.
if (useAccountCache) {
BrowserApi.storageChangeListener((changes, namespace) => {
if (namespace === "local") {
for (const key of Object.keys(changes)) {
if (key !== "accountActivity" && this.accountDiskCache.value[key]) {
this.deleteDiskCache(key);
}
}
}
});
BrowserApi.addListener(
chrome.runtime.onMessage,
(message: { command: string }, _, respond) => {
if (message.command === "initializeDiskCache") {
respond(JSON.stringify(this.accountDiskCache.value));
}
},
);
}
}
override async initAccountState(): Promise<void> {
if (this.isRecoveredSession && this.useAccountCache) {
// request cache initialization
const response = await BrowserApi.sendMessageWithResponse<string>("initializeDiskCache");
this.accountDiskCache.next(JSON.parse(response));
return;
}
await super.initAccountState();
} }
async addAccount(account: Account) { async addAccount(account: Account) {

View File

@ -92,9 +92,16 @@ export class LocalBackedSessionStorageService
// This is for observation purposes only. At some point, we don't want to write to local session storage if the value is the same. // This is for observation purposes only. At some point, we don't want to write to local session storage if the value is the same.
if (this.platformUtilsService.isDev()) { if (this.platformUtilsService.isDev()) {
const existingValue = this.cache[key] as T; const existingValue = this.cache[key] as T;
if (this.compareValues<T>(existingValue, obj)) { try {
this.logService.warning(`Possible unnecessary write to local session storage. Key: ${key}`); if (this.compareValues<T>(existingValue, obj)) {
this.logService.warning(obj as any); this.logService.warning(
`Possible unnecessary write to local session storage. Key: ${key}`,
);
this.logService.warning(obj as any);
}
} catch (err) {
this.logService.warning(`Error while comparing values for key: ${key}`);
this.logService.warning(err);
} }
} }

View File

@ -22,7 +22,7 @@ export class InitService {
init() { init() {
return async () => { return async () => {
await this.stateService.init(); await this.stateService.init({ runMigrations: false }); // Browser background is responsible for migrations
await this.i18nService.init(); await this.i18nService.init();
if (!BrowserPopupUtils.inPopup(window)) { if (!BrowserPopupUtils.inPopup(window)) {

View File

@ -28,7 +28,7 @@ import { OrganizationService } from "@bitwarden/common/admin-console/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 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 { DeviceTrustCryptoServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust-crypto.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 { 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";
@ -250,8 +250,8 @@ const safeProviders: SafeProvider[] = [
deps: [], deps: [],
}), }),
safeProvider({ safeProvider({
provide: DeviceTrustCryptoServiceAbstraction, provide: DeviceTrustServiceAbstraction,
useFactory: getBgService<DeviceTrustCryptoServiceAbstraction>("deviceTrustCryptoService"), useFactory: getBgService<DeviceTrustServiceAbstraction>("deviceTrustService"),
deps: [], deps: [],
}), }),
safeProvider({ safeProvider({

View File

@ -28,13 +28,13 @@ import { ProviderApiService } from "@bitwarden/common/admin-console/services/pro
import { ProviderService } from "@bitwarden/common/admin-console/services/provider.service"; import { ProviderService } from "@bitwarden/common/admin-console/services/provider.service";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { AvatarService as AvatarServiceAbstraction } from "@bitwarden/common/auth/abstractions/avatar.service"; import { AvatarService as AvatarServiceAbstraction } from "@bitwarden/common/auth/abstractions/avatar.service";
import { DeviceTrustCryptoServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust-crypto.service.abstraction"; import { 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 { 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 { DeviceTrustCryptoService } from "@bitwarden/common/auth/services/device-trust-crypto.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 { 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";
@ -77,6 +77,7 @@ import { MigrationBuilderService } from "@bitwarden/common/platform/services/mig
import { MigrationRunner } from "@bitwarden/common/platform/services/migration-runner"; import { MigrationRunner } from "@bitwarden/common/platform/services/migration-runner";
import { StateService } from "@bitwarden/common/platform/services/state.service"; import { StateService } from "@bitwarden/common/platform/services/state.service";
import { StorageServiceProvider } from "@bitwarden/common/platform/services/storage-service.provider"; import { StorageServiceProvider } from "@bitwarden/common/platform/services/storage-service.provider";
import { UserKeyInitService } from "@bitwarden/common/platform/services/user-key-init.service";
import { import {
ActiveUserStateProvider, ActiveUserStateProvider,
DerivedStateProvider, DerivedStateProvider,
@ -217,7 +218,7 @@ export class Main {
syncNotifierService: SyncNotifierService; syncNotifierService: SyncNotifierService;
sendApiService: SendApiService; sendApiService: SendApiService;
devicesApiService: DevicesApiServiceAbstraction; devicesApiService: DevicesApiServiceAbstraction;
deviceTrustCryptoService: DeviceTrustCryptoServiceAbstraction; deviceTrustService: DeviceTrustServiceAbstraction;
authRequestService: AuthRequestService; authRequestService: AuthRequestService;
configApiService: ConfigApiServiceAbstraction; configApiService: ConfigApiServiceAbstraction;
configService: ConfigService; configService: ConfigService;
@ -233,6 +234,7 @@ export class Main {
biometricStateService: BiometricStateService; biometricStateService: BiometricStateService;
billingAccountProfileStateService: BillingAccountProfileStateService; billingAccountProfileStateService: BillingAccountProfileStateService;
providerApiService: ProviderApiServiceAbstraction; providerApiService: ProviderApiServiceAbstraction;
userKeyInitService: UserKeyInitService;
constructor() { constructor() {
let p = null; let p = null;
@ -460,7 +462,7 @@ export class Main {
this.userDecryptionOptionsService = new UserDecryptionOptionsService(this.stateProvider); this.userDecryptionOptionsService = new UserDecryptionOptionsService(this.stateProvider);
this.devicesApiService = new DevicesApiServiceImplementation(this.apiService); this.devicesApiService = new DevicesApiServiceImplementation(this.apiService);
this.deviceTrustCryptoService = new DeviceTrustCryptoService( this.deviceTrustService = new DeviceTrustService(
this.keyGenerationService, this.keyGenerationService,
this.cryptoFunctionService, this.cryptoFunctionService,
this.cryptoService, this.cryptoService,
@ -505,7 +507,7 @@ export class Main {
this.encryptService, this.encryptService,
this.passwordStrengthService, this.passwordStrengthService,
this.policyService, this.policyService,
this.deviceTrustCryptoService, this.deviceTrustService,
this.authRequestService, this.authRequestService,
this.userDecryptionOptionsService, this.userDecryptionOptionsService,
this.globalStateProvider, this.globalStateProvider,
@ -631,6 +633,7 @@ export class Main {
this.avatarService, this.avatarService,
async (expired: boolean) => await this.logout(), async (expired: boolean) => await this.logout(),
this.billingAccountProfileStateService, this.billingAccountProfileStateService,
this.tokenService,
); );
this.totpService = new TotpService(this.cryptoFunctionService, this.logService); this.totpService = new TotpService(this.cryptoFunctionService, this.logService);
@ -691,6 +694,12 @@ export class Main {
); );
this.providerApiService = new ProviderApiService(this.apiService); this.providerApiService = new ProviderApiService(this.apiService);
this.userKeyInitService = new UserKeyInitService(
this.accountService,
this.cryptoService,
this.logService,
);
} }
async run() { async run() {
@ -734,6 +743,7 @@ export class Main {
this.containerService.attachToGlobal(global); this.containerService.attachToGlobal(global);
await this.i18nService.init(); await this.i18nService.init();
this.twoFactorService.init(); this.twoFactorService.init();
this.userKeyInitService.listenForActiveUserChangesToSetUserKey();
} }
} }

View File

@ -4,7 +4,6 @@ import { Subject, merge } from "rxjs";
import { SafeProvider, safeProvider } from "@bitwarden/angular/platform/utils/safe-provider"; import { SafeProvider, safeProvider } from "@bitwarden/angular/platform/utils/safe-provider";
import { import {
SECURE_STORAGE, SECURE_STORAGE,
STATE_SERVICE_USE_CACHE,
LOCALES_DIRECTORY, LOCALES_DIRECTORY,
SYSTEM_LANGUAGE, SYSTEM_LANGUAGE,
MEMORY_STORAGE, MEMORY_STORAGE,
@ -205,7 +204,6 @@ const safeProviders: SafeProvider[] = [
EnvironmentService, EnvironmentService,
TokenService, TokenService,
MigrationRunner, MigrationRunner,
STATE_SERVICE_USE_CACHE,
], ],
}), }),
safeProvider({ safeProvider({

View File

@ -13,7 +13,7 @@ import { VaultTimeoutService } from "@bitwarden/common/abstractions/vault-timeou
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 { InternalPolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; import { InternalPolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { DeviceTrustCryptoServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust-crypto.service.abstraction"; import { DeviceTrustServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust.service.abstraction";
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";
@ -145,8 +145,8 @@ describe("LockComponent", () => {
useValue: mock<DialogService>(), useValue: mock<DialogService>(),
}, },
{ {
provide: DeviceTrustCryptoServiceAbstraction, provide: DeviceTrustServiceAbstraction,
useValue: mock<DeviceTrustCryptoServiceAbstraction>(), useValue: mock<DeviceTrustServiceAbstraction>(),
}, },
{ {
provide: UserVerificationService, provide: UserVerificationService,

View File

@ -10,7 +10,7 @@ import { VaultTimeoutService } from "@bitwarden/common/abstractions/vault-timeou
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 { InternalPolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; import { InternalPolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { DeviceTrustCryptoServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust-crypto.service.abstraction"; import { DeviceTrustServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust.service.abstraction";
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";
@ -58,7 +58,7 @@ export class LockComponent extends BaseLockComponent {
passwordStrengthService: PasswordStrengthServiceAbstraction, passwordStrengthService: PasswordStrengthServiceAbstraction,
logService: LogService, logService: LogService,
dialogService: DialogService, dialogService: DialogService,
deviceTrustCryptoService: DeviceTrustCryptoServiceAbstraction, deviceTrustService: DeviceTrustServiceAbstraction,
userVerificationService: UserVerificationService, userVerificationService: UserVerificationService,
pinCryptoService: PinCryptoServiceAbstraction, pinCryptoService: PinCryptoServiceAbstraction,
biometricStateService: BiometricStateService, biometricStateService: BiometricStateService,
@ -82,7 +82,7 @@ export class LockComponent extends BaseLockComponent {
policyService, policyService,
passwordStrengthService, passwordStrengthService,
dialogService, dialogService,
deviceTrustCryptoService, deviceTrustService,
userVerificationService, userVerificationService,
pinCryptoService, pinCryptoService,
biometricStateService, biometricStateService,

View File

@ -13,7 +13,7 @@ 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 { AnonymousHubService } from "@bitwarden/common/auth/abstractions/anonymous-hub.service"; import { AnonymousHubService } from "@bitwarden/common/auth/abstractions/anonymous-hub.service";
import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service"; import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service";
import { DeviceTrustCryptoServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust-crypto.service.abstraction"; import { DeviceTrustServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust.service.abstraction";
import { AppIdService } from "@bitwarden/common/platform/abstractions/app-id.service"; import { AppIdService } from "@bitwarden/common/platform/abstractions/app-id.service";
import { CryptoFunctionService } from "@bitwarden/common/platform/abstractions/crypto-function.service"; import { CryptoFunctionService } from "@bitwarden/common/platform/abstractions/crypto-function.service";
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service"; import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
@ -55,7 +55,7 @@ export class LoginViaAuthRequestComponent extends BaseLoginWithDeviceComponent {
syncService: SyncService, syncService: SyncService,
stateService: StateService, stateService: StateService,
loginEmailService: LoginEmailServiceAbstraction, loginEmailService: LoginEmailServiceAbstraction,
deviceTrustCryptoService: DeviceTrustCryptoServiceAbstraction, deviceTrustService: DeviceTrustServiceAbstraction,
authRequestService: AuthRequestServiceAbstraction, authRequestService: AuthRequestServiceAbstraction,
loginStrategyService: LoginStrategyServiceAbstraction, loginStrategyService: LoginStrategyServiceAbstraction,
accountService: AccountService, accountService: AccountService,
@ -77,7 +77,7 @@ export class LoginViaAuthRequestComponent extends BaseLoginWithDeviceComponent {
validationService, validationService,
stateService, stateService,
loginEmailService, loginEmailService,
deviceTrustCryptoService, deviceTrustService,
authRequestService, authRequestService,
loginStrategyService, loginStrategyService,
accountService, accountService,

View File

@ -205,7 +205,6 @@ export class Main {
this.environmentService, this.environmentService,
this.tokenService, this.tokenService,
this.migrationRunner, this.migrationRunner,
false, // Do not use disk caching because this will get out of sync with the renderer service
); );
this.desktopSettingsService = new DesktopSettingsService(stateProvider); this.desktopSettingsService = new DesktopSettingsService(stateProvider);

View File

@ -37,7 +37,7 @@ import {
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
import { Policy } from "@bitwarden/common/admin-console/models/domain/policy"; import { Policy } from "@bitwarden/common/admin-console/models/domain/policy";
import { OrganizationKeysRequest } from "@bitwarden/common/admin-console/models/request/organization-keys.request"; import { OrganizationKeysRequest } from "@bitwarden/common/admin-console/models/request/organization-keys.request";
import { OrganizationBillingServiceAbstraction as OrganizationBillingService } from "@bitwarden/common/billing/abstractions/organization-billing.service"; import { BillingApiServiceAbstraction } from "@bitwarden/common/billing/abstractions/billilng-api.service.abstraction";
import { ProductType } from "@bitwarden/common/enums"; import { ProductType } from "@bitwarden/common/enums";
import { ListResponse } from "@bitwarden/common/models/response/list.response"; import { ListResponse } from "@bitwarden/common/models/response/list.response";
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service"; import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
@ -121,7 +121,7 @@ export class PeopleComponent extends BasePeopleComponent<OrganizationUserView> {
private groupService: GroupService, private groupService: GroupService,
private collectionService: CollectionService, private collectionService: CollectionService,
organizationManagementPreferencesService: OrganizationManagementPreferencesService, organizationManagementPreferencesService: OrganizationManagementPreferencesService,
private organizationBillingService: OrganizationBillingService, private billingApiService: BillingApiServiceAbstraction,
) { ) {
super( super(
apiService, apiService,
@ -190,10 +190,11 @@ export class PeopleComponent extends BasePeopleComponent<OrganizationUserView> {
.find((p) => p.organizationId === this.organization.id); .find((p) => p.organizationId === this.organization.id);
this.orgResetPasswordPolicyEnabled = resetPasswordPolicy?.enabled; this.orgResetPasswordPolicyEnabled = resetPasswordPolicy?.enabled;
this.orgIsOnSecretsManagerStandalone = const billingMetadata = await this.billingApiService.getOrganizationBillingMetadata(
await this.organizationBillingService.isOnSecretsManagerStandalone( this.organization.id,
this.organization.id, );
);
this.orgIsOnSecretsManagerStandalone = billingMetadata.isOnSecretsManagerStandalone;
await this.load(); await this.load();

View File

@ -1,7 +1,7 @@
import { mock, MockProxy } from "jest-mock-extended"; import { mock, MockProxy } from "jest-mock-extended";
import { BehaviorSubject } from "rxjs"; import { BehaviorSubject } from "rxjs";
import { DeviceTrustCryptoServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust-crypto.service.abstraction"; import { DeviceTrustServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust.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";
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";
@ -42,7 +42,7 @@ describe("KeyRotationService", () => {
let mockSendService: MockProxy<SendService>; let mockSendService: MockProxy<SendService>;
let mockEmergencyAccessService: MockProxy<EmergencyAccessService>; let mockEmergencyAccessService: MockProxy<EmergencyAccessService>;
let mockResetPasswordService: MockProxy<OrganizationUserResetPasswordService>; let mockResetPasswordService: MockProxy<OrganizationUserResetPasswordService>;
let mockDeviceTrustCryptoService: MockProxy<DeviceTrustCryptoServiceAbstraction>; let mockDeviceTrustService: MockProxy<DeviceTrustServiceAbstraction>;
let mockCryptoService: MockProxy<CryptoService>; let mockCryptoService: MockProxy<CryptoService>;
let mockEncryptService: MockProxy<EncryptService>; let mockEncryptService: MockProxy<EncryptService>;
let mockStateService: MockProxy<StateService>; let mockStateService: MockProxy<StateService>;
@ -60,7 +60,7 @@ describe("KeyRotationService", () => {
mockSendService = mock<SendService>(); mockSendService = mock<SendService>();
mockEmergencyAccessService = mock<EmergencyAccessService>(); mockEmergencyAccessService = mock<EmergencyAccessService>();
mockResetPasswordService = mock<OrganizationUserResetPasswordService>(); mockResetPasswordService = mock<OrganizationUserResetPasswordService>();
mockDeviceTrustCryptoService = mock<DeviceTrustCryptoServiceAbstraction>(); mockDeviceTrustService = mock<DeviceTrustServiceAbstraction>();
mockCryptoService = mock<CryptoService>(); mockCryptoService = mock<CryptoService>();
mockEncryptService = mock<EncryptService>(); mockEncryptService = mock<EncryptService>();
mockStateService = mock<StateService>(); mockStateService = mock<StateService>();
@ -74,7 +74,7 @@ describe("KeyRotationService", () => {
mockSendService, mockSendService,
mockEmergencyAccessService, mockEmergencyAccessService,
mockResetPasswordService, mockResetPasswordService,
mockDeviceTrustCryptoService, mockDeviceTrustService,
mockCryptoService, mockCryptoService,
mockEncryptService, mockEncryptService,
mockStateService, mockStateService,

View File

@ -2,7 +2,7 @@ import { Injectable } from "@angular/core";
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 { DeviceTrustCryptoServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust-crypto.service.abstraction"; import { DeviceTrustServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust.service.abstraction";
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";
@ -33,7 +33,7 @@ export class UserKeyRotationService {
private sendService: SendService, private sendService: SendService,
private emergencyAccessService: EmergencyAccessService, private emergencyAccessService: EmergencyAccessService,
private resetPasswordService: OrganizationUserResetPasswordService, private resetPasswordService: OrganizationUserResetPasswordService,
private deviceTrustCryptoService: DeviceTrustCryptoServiceAbstraction, private deviceTrustService: DeviceTrustServiceAbstraction,
private cryptoService: CryptoService, private cryptoService: CryptoService,
private encryptService: EncryptService, private encryptService: EncryptService,
private stateService: StateService, private stateService: StateService,
@ -96,7 +96,7 @@ export class UserKeyRotationService {
} }
const activeAccount = await firstValueFrom(this.accountService.activeAccount$); const activeAccount = await firstValueFrom(this.accountService.activeAccount$);
await this.deviceTrustCryptoService.rotateDevicesTrust( await this.deviceTrustService.rotateDevicesTrust(
activeAccount.id, activeAccount.id,
newUserKey, newUserKey,
masterPasswordHash, masterPasswordHash,

View File

@ -5,7 +5,6 @@ import { SafeProvider, safeProvider } from "@bitwarden/angular/platform/utils/sa
import { import {
SECURE_STORAGE, SECURE_STORAGE,
STATE_FACTORY, STATE_FACTORY,
STATE_SERVICE_USE_CACHE,
LOCALES_DIRECTORY, LOCALES_DIRECTORY,
SYSTEM_LANGUAGE, SYSTEM_LANGUAGE,
MEMORY_STORAGE, MEMORY_STORAGE,
@ -78,10 +77,6 @@ const safeProviders: SafeProvider[] = [
provide: STATE_FACTORY, provide: STATE_FACTORY,
useValue: new StateFactory(GlobalState, Account), useValue: new StateFactory(GlobalState, Account),
}), }),
safeProvider({
provide: STATE_SERVICE_USE_CACHE,
useValue: false,
}),
safeProvider({ safeProvider({
provide: I18nServiceAbstraction, provide: I18nServiceAbstraction,
useClass: I18nService, useClass: I18nService,

View File

@ -4,7 +4,6 @@ import {
MEMORY_STORAGE, MEMORY_STORAGE,
SECURE_STORAGE, SECURE_STORAGE,
STATE_FACTORY, STATE_FACTORY,
STATE_SERVICE_USE_CACHE,
} from "@bitwarden/angular/services/injection-tokens"; } from "@bitwarden/angular/services/injection-tokens";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { TokenService } from "@bitwarden/common/auth/abstractions/token.service"; import { TokenService } from "@bitwarden/common/auth/abstractions/token.service";
@ -34,7 +33,6 @@ export class StateService extends BaseStateService<GlobalState, Account> {
environmentService: EnvironmentService, environmentService: EnvironmentService,
tokenService: TokenService, tokenService: TokenService,
migrationRunner: MigrationRunner, migrationRunner: MigrationRunner,
@Inject(STATE_SERVICE_USE_CACHE) useAccountCache = true,
) { ) {
super( super(
storageService, storageService,
@ -46,7 +44,6 @@ export class StateService extends BaseStateService<GlobalState, Account> {
environmentService, environmentService,
tokenService, tokenService,
migrationRunner, migrationRunner,
useAccountCache,
); );
} }

View File

@ -23,7 +23,7 @@ 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 { OrganizationUserService } from "@bitwarden/common/admin-console/abstractions/organization-user/organization-user.service"; import { OrganizationUserService } from "@bitwarden/common/admin-console/abstractions/organization-user/organization-user.service";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { DeviceTrustCryptoServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust-crypto.service.abstraction"; import { 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 { PasswordResetEnrollmentServiceAbstraction } from "@bitwarden/common/auth/abstractions/password-reset-enrollment.service.abstraction"; import { PasswordResetEnrollmentServiceAbstraction } from "@bitwarden/common/auth/abstractions/password-reset-enrollment.service.abstraction";
import { SsoLoginServiceAbstraction } from "@bitwarden/common/auth/abstractions/sso-login.service.abstraction"; import { SsoLoginServiceAbstraction } from "@bitwarden/common/auth/abstractions/sso-login.service.abstraction";
@ -93,7 +93,7 @@ export class BaseLoginDecryptionOptionsComponent implements OnInit, OnDestroy {
protected apiService: ApiService, protected apiService: ApiService,
protected i18nService: I18nService, protected i18nService: I18nService,
protected validationService: ValidationService, protected validationService: ValidationService,
protected deviceTrustCryptoService: DeviceTrustCryptoServiceAbstraction, protected deviceTrustService: DeviceTrustServiceAbstraction,
protected platformUtilsService: PlatformUtilsService, protected platformUtilsService: PlatformUtilsService,
protected userDecryptionOptionsService: UserDecryptionOptionsServiceAbstraction, protected userDecryptionOptionsService: UserDecryptionOptionsServiceAbstraction,
protected passwordResetEnrollmentService: PasswordResetEnrollmentServiceAbstraction, protected passwordResetEnrollmentService: PasswordResetEnrollmentServiceAbstraction,
@ -156,7 +156,7 @@ export class BaseLoginDecryptionOptionsComponent implements OnInit, OnDestroy {
} }
private async setRememberDeviceDefaultValue() { private async setRememberDeviceDefaultValue() {
const rememberDeviceFromState = await this.deviceTrustCryptoService.getShouldTrustDevice( const rememberDeviceFromState = await this.deviceTrustService.getShouldTrustDevice(
this.activeAccountId, this.activeAccountId,
); );
@ -169,9 +169,7 @@ export class BaseLoginDecryptionOptionsComponent implements OnInit, OnDestroy {
this.rememberDevice.valueChanges this.rememberDevice.valueChanges
.pipe( .pipe(
switchMap((value) => switchMap((value) =>
defer(() => defer(() => this.deviceTrustService.setShouldTrustDevice(this.activeAccountId, value)),
this.deviceTrustCryptoService.setShouldTrustDevice(this.activeAccountId, value),
),
), ),
takeUntil(this.destroy$), takeUntil(this.destroy$),
) )
@ -288,7 +286,7 @@ export class BaseLoginDecryptionOptionsComponent implements OnInit, OnDestroy {
await this.passwordResetEnrollmentService.enroll(this.data.organizationId); await this.passwordResetEnrollmentService.enroll(this.data.organizationId);
if (this.rememberDeviceForm.value.rememberDevice) { if (this.rememberDeviceForm.value.rememberDevice) {
await this.deviceTrustCryptoService.trustDevice(this.activeAccountId); await this.deviceTrustService.trustDevice(this.activeAccountId);
} }
} catch (error) { } catch (error) {
this.validationService.showError(error); this.validationService.showError(error);

View File

@ -11,7 +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 { MasterPasswordPolicyOptions } from "@bitwarden/common/admin-console/models/domain/master-password-policy-options"; import { MasterPasswordPolicyOptions } from "@bitwarden/common/admin-console/models/domain/master-password-policy-options";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { DeviceTrustCryptoServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust-crypto.service.abstraction"; import { DeviceTrustServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust.service.abstraction";
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";
@ -74,7 +74,7 @@ export class LockComponent implements OnInit, OnDestroy {
protected policyService: InternalPolicyService, protected policyService: InternalPolicyService,
protected passwordStrengthService: PasswordStrengthServiceAbstraction, protected passwordStrengthService: PasswordStrengthServiceAbstraction,
protected dialogService: DialogService, protected dialogService: DialogService,
protected deviceTrustCryptoService: DeviceTrustCryptoServiceAbstraction, protected deviceTrustService: DeviceTrustServiceAbstraction,
protected userVerificationService: UserVerificationService, protected userVerificationService: UserVerificationService,
protected pinCryptoService: PinCryptoServiceAbstraction, protected pinCryptoService: PinCryptoServiceAbstraction,
protected biometricStateService: BiometricStateService, protected biometricStateService: BiometricStateService,
@ -277,7 +277,7 @@ export class LockComponent implements OnInit, OnDestroy {
// Now that we have a decrypted user key in memory, we can check if we // Now that we have a decrypted user key in memory, we can check if we
// need to establish trust on the current device // need to establish trust on the current device
const activeAccount = await firstValueFrom(this.accountService.activeAccount$); const activeAccount = await firstValueFrom(this.accountService.activeAccount$);
await this.deviceTrustCryptoService.trustDeviceIfRequired(activeAccount.id); await this.deviceTrustService.trustDeviceIfRequired(activeAccount.id);
await this.doContinue(evaluatePasswordAfterUnlock); await this.doContinue(evaluatePasswordAfterUnlock);
} }

View File

@ -12,7 +12,7 @@ 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 { AnonymousHubService } from "@bitwarden/common/auth/abstractions/anonymous-hub.service"; import { AnonymousHubService } from "@bitwarden/common/auth/abstractions/anonymous-hub.service";
import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service"; import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service";
import { DeviceTrustCryptoServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust-crypto.service.abstraction"; import { DeviceTrustServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust.service.abstraction";
import { AuthRequestType } from "@bitwarden/common/auth/enums/auth-request-type"; import { AuthRequestType } from "@bitwarden/common/auth/enums/auth-request-type";
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status"; import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
import { AdminAuthRequestStorable } from "@bitwarden/common/auth/models/domain/admin-auth-req-storable"; import { AdminAuthRequestStorable } from "@bitwarden/common/auth/models/domain/admin-auth-req-storable";
@ -86,7 +86,7 @@ export class LoginViaAuthRequestComponent
private validationService: ValidationService, private validationService: ValidationService,
private stateService: StateService, private stateService: StateService,
private loginEmailService: LoginEmailServiceAbstraction, private loginEmailService: LoginEmailServiceAbstraction,
private deviceTrustCryptoService: DeviceTrustCryptoServiceAbstraction, private deviceTrustService: DeviceTrustServiceAbstraction,
private authRequestService: AuthRequestServiceAbstraction, private authRequestService: AuthRequestServiceAbstraction,
private loginStrategyService: LoginStrategyServiceAbstraction, private loginStrategyService: LoginStrategyServiceAbstraction,
private accountService: AccountService, private accountService: AccountService,
@ -221,7 +221,8 @@ export class LoginViaAuthRequestComponent
} }
// Request still pending response from admin // Request still pending response from admin
// So, create hub connection so that any approvals will be received via push notification // set keypair and create hub connection so that any approvals will be received via push notification
this.authRequestKeyPair = { privateKey: adminAuthReqStorable.privateKey, publicKey: null };
await this.anonymousHubService.createHubConnection(adminAuthReqStorable.id); await this.anonymousHubService.createHubConnection(adminAuthReqStorable.id);
} }
@ -401,7 +402,7 @@ export class LoginViaAuthRequestComponent
// Now that we have a decrypted user key in memory, we can check if we // Now that we have a decrypted user key in memory, we can check if we
// need to establish trust on the current device // need to establish trust on the current device
const activeAccount = await firstValueFrom(this.accountService.activeAccount$); const activeAccount = await firstValueFrom(this.accountService.activeAccount$);
await this.deviceTrustCryptoService.trustDeviceIfRequired(activeAccount.id); await this.deviceTrustService.trustDeviceIfRequired(activeAccount.id);
// TODO: don't forget to use auto enrollment service everywhere we trust device // TODO: don't forget to use auto enrollment service everywhere we trust device

View File

@ -8,7 +8,7 @@ import {
import { firstValueFrom } from "rxjs"; import { firstValueFrom } from "rxjs";
import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service"; import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service";
import { DeviceTrustCryptoServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust-crypto.service.abstraction"; import { DeviceTrustServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust.service.abstraction";
import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction"; import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction";
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status"; import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
import { ClientType } from "@bitwarden/common/enums"; import { ClientType } from "@bitwarden/common/enums";
@ -30,7 +30,7 @@ export function lockGuard(): CanActivateFn {
) => { ) => {
const authService = inject(AuthService); const authService = inject(AuthService);
const cryptoService = inject(CryptoService); const cryptoService = inject(CryptoService);
const deviceTrustCryptoService = inject(DeviceTrustCryptoServiceAbstraction); const deviceTrustService = inject(DeviceTrustServiceAbstraction);
const platformUtilService = inject(PlatformUtilsService); const platformUtilService = inject(PlatformUtilsService);
const messagingService = inject(MessagingService); const messagingService = inject(MessagingService);
const router = inject(Router); const router = inject(Router);
@ -53,7 +53,7 @@ export function lockGuard(): CanActivateFn {
// User is authN and in locked state. // User is authN and in locked state.
const tdeEnabled = await firstValueFrom(deviceTrustCryptoService.supportsDeviceTrust$); const tdeEnabled = await firstValueFrom(deviceTrustService.supportsDeviceTrust$);
// Create special exception which allows users to go from the login-initiated page to the lock page for the approve w/ MP flow // Create special exception which allows users to go from the login-initiated page to the lock page for the approve w/ MP flow
// The MP check is necessary to prevent direct manual navigation from other locked state pages for users who don't have a MP // The MP check is necessary to prevent direct manual navigation from other locked state pages for users who don't have a MP

View File

@ -3,7 +3,7 @@ import { CanActivateFn, Router } from "@angular/router";
import { firstValueFrom } from "rxjs"; import { firstValueFrom } from "rxjs";
import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service"; import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service";
import { DeviceTrustCryptoServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust-crypto.service.abstraction"; import { DeviceTrustServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust.service.abstraction";
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status"; import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service"; import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
@ -31,7 +31,7 @@ export function redirectGuard(overrides: Partial<RedirectRoutes> = {}): CanActiv
return async (route) => { return async (route) => {
const authService = inject(AuthService); const authService = inject(AuthService);
const cryptoService = inject(CryptoService); const cryptoService = inject(CryptoService);
const deviceTrustCryptoService = inject(DeviceTrustCryptoServiceAbstraction); const deviceTrustService = inject(DeviceTrustServiceAbstraction);
const router = inject(Router); const router = inject(Router);
const authStatus = await authService.getAuthStatus(); const authStatus = await authService.getAuthStatus();
@ -46,7 +46,7 @@ export function redirectGuard(overrides: Partial<RedirectRoutes> = {}): CanActiv
// If locked, TDE is enabled, and the user hasn't decrypted yet, then redirect to the // If locked, TDE is enabled, and the user hasn't decrypted yet, then redirect to the
// login decryption options component. // login decryption options component.
const tdeEnabled = await firstValueFrom(deviceTrustCryptoService.supportsDeviceTrust$); const tdeEnabled = await firstValueFrom(deviceTrustService.supportsDeviceTrust$);
const everHadUserKey = await firstValueFrom(cryptoService.everHadUserKey$); const everHadUserKey = await firstValueFrom(cryptoService.everHadUserKey$);
if (authStatus === AuthenticationStatus.Locked && tdeEnabled && !everHadUserKey) { if (authStatus === AuthenticationStatus.Locked && tdeEnabled && !everHadUserKey) {
return router.createUrlTree([routes.notDecrypted], { queryParams: route.queryParams }); return router.createUrlTree([routes.notDecrypted], { queryParams: route.queryParams });

View File

@ -8,7 +8,7 @@ import {
import { firstValueFrom } from "rxjs"; import { firstValueFrom } from "rxjs";
import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service"; import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service";
import { DeviceTrustCryptoServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust-crypto.service.abstraction"; import { DeviceTrustServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust.service.abstraction";
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status"; import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service"; import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
@ -22,11 +22,11 @@ export function tdeDecryptionRequiredGuard(): CanActivateFn {
return async (_: ActivatedRouteSnapshot, state: RouterStateSnapshot) => { return async (_: ActivatedRouteSnapshot, state: RouterStateSnapshot) => {
const authService = inject(AuthService); const authService = inject(AuthService);
const cryptoService = inject(CryptoService); const cryptoService = inject(CryptoService);
const deviceTrustCryptoService = inject(DeviceTrustCryptoServiceAbstraction); const deviceTrustService = inject(DeviceTrustServiceAbstraction);
const router = inject(Router); const router = inject(Router);
const authStatus = await authService.getAuthStatus(); const authStatus = await authService.getAuthStatus();
const tdeEnabled = await firstValueFrom(deviceTrustCryptoService.supportsDeviceTrust$); const tdeEnabled = await firstValueFrom(deviceTrustService.supportsDeviceTrust$);
const everHadUserKey = await firstValueFrom(cryptoService.everHadUserKey$); const everHadUserKey = await firstValueFrom(cryptoService.everHadUserKey$);
if (authStatus !== AuthenticationStatus.Locked || !tdeEnabled || everHadUserKey) { if (authStatus !== AuthenticationStatus.Locked || !tdeEnabled || everHadUserKey) {
return router.createUrlTree(["/"]); return router.createUrlTree(["/"]);

View File

@ -36,7 +36,6 @@ export const MEMORY_STORAGE = new SafeInjectionToken<AbstractMemoryStorageServic
); );
export const SECURE_STORAGE = new SafeInjectionToken<AbstractStorageService>("SECURE_STORAGE"); export const SECURE_STORAGE = new SafeInjectionToken<AbstractStorageService>("SECURE_STORAGE");
export const STATE_FACTORY = new SafeInjectionToken<StateFactory>("STATE_FACTORY"); export const STATE_FACTORY = new SafeInjectionToken<StateFactory>("STATE_FACTORY");
export const STATE_SERVICE_USE_CACHE = new SafeInjectionToken<boolean>("STATE_SERVICE_USE_CACHE");
export const LOGOUT_CALLBACK = new SafeInjectionToken< export const LOGOUT_CALLBACK = new SafeInjectionToken<
(expired: boolean, userId?: string) => Promise<void> (expired: boolean, userId?: string) => Promise<void>
>("LOGOUT_CALLBACK"); >("LOGOUT_CALLBACK");

View File

@ -60,7 +60,7 @@ import {
import { AnonymousHubService as AnonymousHubServiceAbstraction } from "@bitwarden/common/auth/abstractions/anonymous-hub.service"; import { AnonymousHubService as AnonymousHubServiceAbstraction } from "@bitwarden/common/auth/abstractions/anonymous-hub.service";
import { AuthService as AuthServiceAbstraction } from "@bitwarden/common/auth/abstractions/auth.service"; import { AuthService as AuthServiceAbstraction } from "@bitwarden/common/auth/abstractions/auth.service";
import { AvatarService as AvatarServiceAbstraction } from "@bitwarden/common/auth/abstractions/avatar.service"; import { AvatarService as AvatarServiceAbstraction } from "@bitwarden/common/auth/abstractions/avatar.service";
import { DeviceTrustCryptoServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust-crypto.service.abstraction"; import { 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 { KeyConnectorService as KeyConnectorServiceAbstraction } from "@bitwarden/common/auth/abstractions/key-connector.service"; import { KeyConnectorService as KeyConnectorServiceAbstraction } from "@bitwarden/common/auth/abstractions/key-connector.service";
@ -82,7 +82,7 @@ import { AccountServiceImplementation } from "@bitwarden/common/auth/services/ac
import { AnonymousHubService } from "@bitwarden/common/auth/services/anonymous-hub.service"; import { AnonymousHubService } from "@bitwarden/common/auth/services/anonymous-hub.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 { DeviceTrustCryptoService } from "@bitwarden/common/auth/services/device-trust-crypto.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 { KeyConnectorService } from "@bitwarden/common/auth/services/key-connector.service"; import { KeyConnectorService } from "@bitwarden/common/auth/services/key-connector.service";
@ -269,7 +269,6 @@ import {
SafeInjectionToken, SafeInjectionToken,
SECURE_STORAGE, SECURE_STORAGE,
STATE_FACTORY, STATE_FACTORY,
STATE_SERVICE_USE_CACHE,
SUPPORTS_SECURE_STORAGE, SUPPORTS_SECURE_STORAGE,
SYSTEM_LANGUAGE, SYSTEM_LANGUAGE,
SYSTEM_THEME_OBSERVABLE, SYSTEM_THEME_OBSERVABLE,
@ -313,10 +312,6 @@ const safeProviders: SafeProvider[] = [
provide: STATE_FACTORY, provide: STATE_FACTORY,
useValue: new StateFactory(GlobalState, Account), useValue: new StateFactory(GlobalState, Account),
}), }),
safeProvider({
provide: STATE_SERVICE_USE_CACHE,
useValue: true,
}),
safeProvider({ safeProvider({
provide: LOGOUT_CALLBACK, provide: LOGOUT_CALLBACK,
useFactory: useFactory:
@ -390,7 +385,7 @@ const safeProviders: SafeProvider[] = [
EncryptService, EncryptService,
PasswordStrengthServiceAbstraction, PasswordStrengthServiceAbstraction,
PolicyServiceAbstraction, PolicyServiceAbstraction,
DeviceTrustCryptoServiceAbstraction, DeviceTrustServiceAbstraction,
AuthRequestServiceAbstraction, AuthRequestServiceAbstraction,
InternalUserDecryptionOptionsServiceAbstraction, InternalUserDecryptionOptionsServiceAbstraction,
GlobalStateProvider, GlobalStateProvider,
@ -628,6 +623,7 @@ const safeProviders: SafeProvider[] = [
AvatarServiceAbstraction, AvatarServiceAbstraction,
LOGOUT_CALLBACK, LOGOUT_CALLBACK,
BillingAccountProfileStateService, BillingAccountProfileStateService,
TokenServiceAbstraction,
], ],
}), }),
safeProvider({ safeProvider({
@ -690,7 +686,6 @@ const safeProviders: SafeProvider[] = [
EnvironmentService, EnvironmentService,
TokenServiceAbstraction, TokenServiceAbstraction,
MigrationRunner, MigrationRunner,
STATE_SERVICE_USE_CACHE,
], ],
}), }),
safeProvider({ safeProvider({
@ -954,8 +949,8 @@ const safeProviders: SafeProvider[] = [
deps: [DevicesApiServiceAbstraction], deps: [DevicesApiServiceAbstraction],
}), }),
safeProvider({ safeProvider({
provide: DeviceTrustCryptoServiceAbstraction, provide: DeviceTrustServiceAbstraction,
useClass: DeviceTrustCryptoService, useClass: DeviceTrustService,
deps: [ deps: [
KeyGenerationServiceAbstraction, KeyGenerationServiceAbstraction,
CryptoFunctionServiceAbstraction, CryptoFunctionServiceAbstraction,
@ -1063,7 +1058,6 @@ const safeProviders: SafeProvider[] = [
useClass: OrganizationBillingService, useClass: OrganizationBillingService,
deps: [ deps: [
ApiServiceAbstraction, ApiServiceAbstraction,
BillingApiServiceAbstraction,
CryptoServiceAbstraction, CryptoServiceAbstraction,
EncryptService, EncryptService,
I18nServiceAbstraction, I18nServiceAbstraction,

View File

@ -1,7 +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 { DeviceTrustCryptoServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust-crypto.service.abstraction"; import { DeviceTrustServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust.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 { IdentityTokenResponse } from "@bitwarden/common/auth/models/response/identity-token.response"; import { IdentityTokenResponse } from "@bitwarden/common/auth/models/response/identity-token.response";
@ -42,7 +42,7 @@ describe("AuthRequestLoginStrategy", () => {
let stateService: MockProxy<StateService>; let stateService: MockProxy<StateService>;
let twoFactorService: MockProxy<TwoFactorService>; let twoFactorService: MockProxy<TwoFactorService>;
let userDecryptionOptions: MockProxy<InternalUserDecryptionOptionsServiceAbstraction>; let userDecryptionOptions: MockProxy<InternalUserDecryptionOptionsServiceAbstraction>;
let deviceTrustCryptoService: MockProxy<DeviceTrustCryptoServiceAbstraction>; let deviceTrustService: MockProxy<DeviceTrustServiceAbstraction>;
let billingAccountProfileStateService: MockProxy<BillingAccountProfileStateService>; let billingAccountProfileStateService: MockProxy<BillingAccountProfileStateService>;
const mockUserId = Utils.newGuid() as UserId; const mockUserId = Utils.newGuid() as UserId;
@ -75,7 +75,7 @@ describe("AuthRequestLoginStrategy", () => {
stateService = mock<StateService>(); stateService = mock<StateService>();
twoFactorService = mock<TwoFactorService>(); twoFactorService = mock<TwoFactorService>();
userDecryptionOptions = mock<InternalUserDecryptionOptionsServiceAbstraction>(); userDecryptionOptions = mock<InternalUserDecryptionOptionsServiceAbstraction>();
deviceTrustCryptoService = mock<DeviceTrustCryptoServiceAbstraction>(); deviceTrustService = mock<DeviceTrustServiceAbstraction>();
billingAccountProfileStateService = mock<BillingAccountProfileStateService>(); billingAccountProfileStateService = mock<BillingAccountProfileStateService>();
accountService = mockAccountServiceWith(mockUserId); accountService = mockAccountServiceWith(mockUserId);
@ -99,7 +99,7 @@ describe("AuthRequestLoginStrategy", () => {
stateService, stateService,
twoFactorService, twoFactorService,
userDecryptionOptions, userDecryptionOptions,
deviceTrustCryptoService, deviceTrustService,
billingAccountProfileStateService, billingAccountProfileStateService,
); );
@ -132,7 +132,7 @@ describe("AuthRequestLoginStrategy", () => {
); );
expect(cryptoService.setMasterKeyEncryptedUserKey).toHaveBeenCalledWith(tokenResponse.key); expect(cryptoService.setMasterKeyEncryptedUserKey).toHaveBeenCalledWith(tokenResponse.key);
expect(cryptoService.setUserKey).toHaveBeenCalledWith(userKey); expect(cryptoService.setUserKey).toHaveBeenCalledWith(userKey);
expect(deviceTrustCryptoService.trustDeviceIfRequired).toHaveBeenCalled(); expect(deviceTrustService.trustDeviceIfRequired).toHaveBeenCalled();
expect(cryptoService.setPrivateKey).toHaveBeenCalledWith(tokenResponse.privateKey); expect(cryptoService.setPrivateKey).toHaveBeenCalledWith(tokenResponse.privateKey);
}); });
@ -160,6 +160,6 @@ describe("AuthRequestLoginStrategy", () => {
expect(cryptoService.setPrivateKey).toHaveBeenCalledWith(tokenResponse.privateKey); expect(cryptoService.setPrivateKey).toHaveBeenCalledWith(tokenResponse.privateKey);
// trustDeviceIfRequired should be called // trustDeviceIfRequired should be called
expect(deviceTrustCryptoService.trustDeviceIfRequired).not.toHaveBeenCalled(); expect(deviceTrustService.trustDeviceIfRequired).not.toHaveBeenCalled();
}); });
}); });

View File

@ -3,7 +3,6 @@ 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 { DeviceTrustCryptoServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust-crypto.service.abstraction";
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";
@ -18,6 +17,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 { DeviceTrustServiceAbstraction } from "@bitwarden/common/src/auth/abstractions/device-trust.service.abstraction";
import { UserId } from "@bitwarden/common/types/guid"; import { UserId } from "@bitwarden/common/types/guid";
import { InternalUserDecryptionOptionsServiceAbstraction } from "../abstractions/user-decryption-options.service.abstraction"; import { InternalUserDecryptionOptionsServiceAbstraction } from "../abstractions/user-decryption-options.service.abstraction";
@ -61,7 +61,7 @@ export class AuthRequestLoginStrategy extends LoginStrategy {
stateService: StateService, stateService: StateService,
twoFactorService: TwoFactorService, twoFactorService: TwoFactorService,
userDecryptionOptionsService: InternalUserDecryptionOptionsServiceAbstraction, userDecryptionOptionsService: InternalUserDecryptionOptionsServiceAbstraction,
private deviceTrustCryptoService: DeviceTrustCryptoServiceAbstraction, private deviceTrustService: DeviceTrustServiceAbstraction,
billingAccountProfileStateService: BillingAccountProfileStateService, billingAccountProfileStateService: BillingAccountProfileStateService,
) { ) {
super( super(
@ -147,7 +147,7 @@ export class AuthRequestLoginStrategy extends LoginStrategy {
await this.trySetUserKeyWithMasterKey(); await this.trySetUserKeyWithMasterKey();
// Establish trust if required after setting user key // Establish trust if required after setting user key
await this.deviceTrustCryptoService.trustDeviceIfRequired(userId); await this.deviceTrustService.trustDeviceIfRequired(userId);
} }
} }

View File

@ -27,7 +27,6 @@ import { Utils } from "@bitwarden/common/platform/misc/utils";
import { import {
Account, Account,
AccountProfile, AccountProfile,
AccountTokens,
AccountKeys, AccountKeys,
} from "@bitwarden/common/platform/models/domain/account"; } 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";
@ -213,9 +212,6 @@ describe("LoginStrategy", () => {
kdfType: kdf, kdfType: kdf,
}, },
}, },
tokens: {
...new AccountTokens(),
},
keys: new AccountKeys(), keys: new AccountKeys(),
}), }),
); );

View File

@ -27,11 +27,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 { import { Account, AccountProfile } from "@bitwarden/common/platform/models/domain/account";
Account,
AccountProfile,
AccountTokens,
} from "@bitwarden/common/platform/models/domain/account";
import { UserId } from "@bitwarden/common/types/guid"; import { UserId } from "@bitwarden/common/types/guid";
import { InternalUserDecryptionOptionsServiceAbstraction } from "../abstractions/user-decryption-options.service.abstraction"; import { InternalUserDecryptionOptionsServiceAbstraction } from "../abstractions/user-decryption-options.service.abstraction";
@ -192,9 +188,6 @@ export abstract class LoginStrategy {
kdfType: tokenResponse.kdf, kdfType: tokenResponse.kdf,
}, },
}, },
tokens: {
...new AccountTokens(),
},
}), }),
); );

View File

@ -1,7 +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 { DeviceTrustCryptoServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust-crypto.service.abstraction"; import { DeviceTrustServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust.service.abstraction";
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";
@ -50,7 +50,7 @@ describe("SsoLoginStrategy", () => {
let twoFactorService: MockProxy<TwoFactorService>; let twoFactorService: MockProxy<TwoFactorService>;
let userDecryptionOptionsService: MockProxy<InternalUserDecryptionOptionsServiceAbstraction>; let userDecryptionOptionsService: MockProxy<InternalUserDecryptionOptionsServiceAbstraction>;
let keyConnectorService: MockProxy<KeyConnectorService>; let keyConnectorService: MockProxy<KeyConnectorService>;
let deviceTrustCryptoService: MockProxy<DeviceTrustCryptoServiceAbstraction>; let deviceTrustService: MockProxy<DeviceTrustServiceAbstraction>;
let authRequestService: MockProxy<AuthRequestServiceAbstraction>; let authRequestService: MockProxy<AuthRequestServiceAbstraction>;
let i18nService: MockProxy<I18nService>; let i18nService: MockProxy<I18nService>;
let billingAccountProfileStateService: MockProxy<BillingAccountProfileStateService>; let billingAccountProfileStateService: MockProxy<BillingAccountProfileStateService>;
@ -82,7 +82,7 @@ describe("SsoLoginStrategy", () => {
twoFactorService = mock<TwoFactorService>(); twoFactorService = mock<TwoFactorService>();
userDecryptionOptionsService = mock<InternalUserDecryptionOptionsServiceAbstraction>(); userDecryptionOptionsService = mock<InternalUserDecryptionOptionsServiceAbstraction>();
keyConnectorService = mock<KeyConnectorService>(); keyConnectorService = mock<KeyConnectorService>();
deviceTrustCryptoService = mock<DeviceTrustCryptoServiceAbstraction>(); deviceTrustService = mock<DeviceTrustServiceAbstraction>();
authRequestService = mock<AuthRequestServiceAbstraction>(); authRequestService = mock<AuthRequestServiceAbstraction>();
i18nService = mock<I18nService>(); i18nService = mock<I18nService>();
billingAccountProfileStateService = mock<BillingAccountProfileStateService>(); billingAccountProfileStateService = mock<BillingAccountProfileStateService>();
@ -106,7 +106,7 @@ describe("SsoLoginStrategy", () => {
twoFactorService, twoFactorService,
userDecryptionOptionsService, userDecryptionOptionsService,
keyConnectorService, keyConnectorService,
deviceTrustCryptoService, deviceTrustService,
authRequestService, authRequestService,
i18nService, i18nService,
billingAccountProfileStateService, billingAccountProfileStateService,
@ -209,8 +209,8 @@ describe("SsoLoginStrategy", () => {
); );
apiService.postIdentityToken.mockResolvedValue(idTokenResponse); apiService.postIdentityToken.mockResolvedValue(idTokenResponse);
deviceTrustCryptoService.getDeviceKey.mockResolvedValue(mockDeviceKey); deviceTrustService.getDeviceKey.mockResolvedValue(mockDeviceKey);
deviceTrustCryptoService.decryptUserKeyWithDeviceKey.mockResolvedValue(mockUserKey); deviceTrustService.decryptUserKeyWithDeviceKey.mockResolvedValue(mockUserKey);
const cryptoSvcSetUserKeySpy = jest.spyOn(cryptoService, "setUserKey"); const cryptoSvcSetUserKeySpy = jest.spyOn(cryptoService, "setUserKey");
@ -218,8 +218,8 @@ describe("SsoLoginStrategy", () => {
await ssoLoginStrategy.logIn(credentials); await ssoLoginStrategy.logIn(credentials);
// Assert // Assert
expect(deviceTrustCryptoService.getDeviceKey).toHaveBeenCalledTimes(1); expect(deviceTrustService.getDeviceKey).toHaveBeenCalledTimes(1);
expect(deviceTrustCryptoService.decryptUserKeyWithDeviceKey).toHaveBeenCalledTimes(1); expect(deviceTrustService.decryptUserKeyWithDeviceKey).toHaveBeenCalledTimes(1);
expect(cryptoSvcSetUserKeySpy).toHaveBeenCalledTimes(1); expect(cryptoSvcSetUserKeySpy).toHaveBeenCalledTimes(1);
expect(cryptoSvcSetUserKeySpy).toHaveBeenCalledWith(mockUserKey); expect(cryptoSvcSetUserKeySpy).toHaveBeenCalledWith(mockUserKey);
}); });
@ -232,8 +232,8 @@ describe("SsoLoginStrategy", () => {
); );
apiService.postIdentityToken.mockResolvedValue(idTokenResponse); apiService.postIdentityToken.mockResolvedValue(idTokenResponse);
// Set deviceKey to be null // Set deviceKey to be null
deviceTrustCryptoService.getDeviceKey.mockResolvedValue(null); deviceTrustService.getDeviceKey.mockResolvedValue(null);
deviceTrustCryptoService.decryptUserKeyWithDeviceKey.mockResolvedValue(mockUserKey); deviceTrustService.decryptUserKeyWithDeviceKey.mockResolvedValue(mockUserKey);
// Act // Act
await ssoLoginStrategy.logIn(credentials); await ssoLoginStrategy.logIn(credentials);
@ -254,7 +254,7 @@ describe("SsoLoginStrategy", () => {
// Arrange // Arrange
const idTokenResponse = mockIdTokenResponseWithModifiedTrustedDeviceOption(valueName, null); const idTokenResponse = mockIdTokenResponseWithModifiedTrustedDeviceOption(valueName, null);
apiService.postIdentityToken.mockResolvedValue(idTokenResponse); apiService.postIdentityToken.mockResolvedValue(idTokenResponse);
deviceTrustCryptoService.getDeviceKey.mockResolvedValue(mockDeviceKey); deviceTrustService.getDeviceKey.mockResolvedValue(mockDeviceKey);
// Act // Act
await ssoLoginStrategy.logIn(credentials); await ssoLoginStrategy.logIn(credentials);
@ -271,9 +271,9 @@ describe("SsoLoginStrategy", () => {
userDecryptionOptsServerResponseWithTdeOption, userDecryptionOptsServerResponseWithTdeOption,
); );
apiService.postIdentityToken.mockResolvedValue(idTokenResponse); apiService.postIdentityToken.mockResolvedValue(idTokenResponse);
deviceTrustCryptoService.getDeviceKey.mockResolvedValue(mockDeviceKey); deviceTrustService.getDeviceKey.mockResolvedValue(mockDeviceKey);
// Set userKey to be null // Set userKey to be null
deviceTrustCryptoService.decryptUserKeyWithDeviceKey.mockResolvedValue(null); deviceTrustService.decryptUserKeyWithDeviceKey.mockResolvedValue(null);
// Act // Act
await ssoLoginStrategy.logIn(credentials); await ssoLoginStrategy.logIn(credentials);
@ -321,7 +321,7 @@ describe("SsoLoginStrategy", () => {
await ssoLoginStrategy.logIn(credentials); await ssoLoginStrategy.logIn(credentials);
expect(authRequestService.setKeysAfterDecryptingSharedMasterKeyAndHash).toHaveBeenCalled(); expect(authRequestService.setKeysAfterDecryptingSharedMasterKeyAndHash).toHaveBeenCalled();
expect(deviceTrustCryptoService.decryptUserKeyWithDeviceKey).not.toHaveBeenCalled(); expect(deviceTrustService.decryptUserKeyWithDeviceKey).not.toHaveBeenCalled();
}); });
it("sets the user key from approved admin request if exists", async () => { it("sets the user key from approved admin request if exists", async () => {
@ -338,7 +338,7 @@ describe("SsoLoginStrategy", () => {
await ssoLoginStrategy.logIn(credentials); await ssoLoginStrategy.logIn(credentials);
expect(authRequestService.setUserKeyAfterDecryptingSharedUserKey).toHaveBeenCalled(); expect(authRequestService.setUserKeyAfterDecryptingSharedUserKey).toHaveBeenCalled();
expect(deviceTrustCryptoService.decryptUserKeyWithDeviceKey).not.toHaveBeenCalled(); expect(deviceTrustService.decryptUserKeyWithDeviceKey).not.toHaveBeenCalled();
}); });
it("attempts to establish a trusted device if successful", async () => { it("attempts to establish a trusted device if successful", async () => {
@ -355,7 +355,7 @@ describe("SsoLoginStrategy", () => {
await ssoLoginStrategy.logIn(credentials); await ssoLoginStrategy.logIn(credentials);
expect(authRequestService.setUserKeyAfterDecryptingSharedUserKey).toHaveBeenCalled(); expect(authRequestService.setUserKeyAfterDecryptingSharedUserKey).toHaveBeenCalled();
expect(deviceTrustCryptoService.trustDeviceIfRequired).toHaveBeenCalled(); expect(deviceTrustService.trustDeviceIfRequired).toHaveBeenCalled();
}); });
it("clears the admin auth request if server returns a 404, meaning it was deleted", async () => { it("clears the admin auth request if server returns a 404, meaning it was deleted", async () => {
@ -369,7 +369,7 @@ describe("SsoLoginStrategy", () => {
authRequestService.setKeysAfterDecryptingSharedMasterKeyAndHash, authRequestService.setKeysAfterDecryptingSharedMasterKeyAndHash,
).not.toHaveBeenCalled(); ).not.toHaveBeenCalled();
expect(authRequestService.setUserKeyAfterDecryptingSharedUserKey).not.toHaveBeenCalled(); expect(authRequestService.setUserKeyAfterDecryptingSharedUserKey).not.toHaveBeenCalled();
expect(deviceTrustCryptoService.trustDeviceIfRequired).not.toHaveBeenCalled(); expect(deviceTrustService.trustDeviceIfRequired).not.toHaveBeenCalled();
}); });
it("attempts to login with a trusted device if admin auth request isn't successful", async () => { it("attempts to login with a trusted device if admin auth request isn't successful", async () => {
@ -382,11 +382,11 @@ describe("SsoLoginStrategy", () => {
}; };
apiService.getAuthRequest.mockResolvedValue(adminAuthResponse as AuthRequestResponse); apiService.getAuthRequest.mockResolvedValue(adminAuthResponse as AuthRequestResponse);
cryptoService.hasUserKey.mockResolvedValue(false); cryptoService.hasUserKey.mockResolvedValue(false);
deviceTrustCryptoService.getDeviceKey.mockResolvedValue("DEVICE_KEY" as any); deviceTrustService.getDeviceKey.mockResolvedValue("DEVICE_KEY" as any);
await ssoLoginStrategy.logIn(credentials); await ssoLoginStrategy.logIn(credentials);
expect(deviceTrustCryptoService.decryptUserKeyWithDeviceKey).toHaveBeenCalled(); expect(deviceTrustService.decryptUserKeyWithDeviceKey).toHaveBeenCalled();
}); });
}); });
}); });

View File

@ -3,7 +3,6 @@ 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 { DeviceTrustCryptoServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust-crypto.service.abstraction";
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";
@ -22,6 +21,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 { DeviceTrustServiceAbstraction } from "@bitwarden/common/src/auth/abstractions/device-trust.service.abstraction";
import { UserId } from "@bitwarden/common/types/guid"; import { UserId } from "@bitwarden/common/types/guid";
import { import {
@ -94,7 +94,7 @@ export class SsoLoginStrategy extends LoginStrategy {
twoFactorService: TwoFactorService, twoFactorService: TwoFactorService,
userDecryptionOptionsService: InternalUserDecryptionOptionsServiceAbstraction, userDecryptionOptionsService: InternalUserDecryptionOptionsServiceAbstraction,
private keyConnectorService: KeyConnectorService, private keyConnectorService: KeyConnectorService,
private deviceTrustCryptoService: DeviceTrustCryptoServiceAbstraction, private deviceTrustService: DeviceTrustServiceAbstraction,
private authRequestService: AuthRequestServiceAbstraction, private authRequestService: AuthRequestServiceAbstraction,
private i18nService: I18nService, private i18nService: I18nService,
billingAccountProfileStateService: BillingAccountProfileStateService, billingAccountProfileStateService: BillingAccountProfileStateService,
@ -298,7 +298,7 @@ export class SsoLoginStrategy extends LoginStrategy {
if (await this.cryptoService.hasUserKey()) { if (await this.cryptoService.hasUserKey()) {
// Now that we have a decrypted user key in memory, we can check if we // Now that we have a decrypted user key in memory, we can check if we
// need to establish trust on the current device // need to establish trust on the current device
await this.deviceTrustCryptoService.trustDeviceIfRequired(userId); await this.deviceTrustService.trustDeviceIfRequired(userId);
// if we successfully decrypted the user key, we can delete the admin auth request out of state // if we successfully decrypted the user key, we can delete the admin auth request out of state
// TODO: eventually we post and clean up DB as well once consumed on client // TODO: eventually we post and clean up DB as well once consumed on client
@ -314,7 +314,7 @@ export class SsoLoginStrategy extends LoginStrategy {
const userId = (await this.stateService.getUserId()) as UserId; const userId = (await this.stateService.getUserId()) as UserId;
const deviceKey = await this.deviceTrustCryptoService.getDeviceKey(userId); const deviceKey = await this.deviceTrustService.getDeviceKey(userId);
const encDevicePrivateKey = trustedDeviceOption?.encryptedPrivateKey; const encDevicePrivateKey = trustedDeviceOption?.encryptedPrivateKey;
const encUserKey = trustedDeviceOption?.encryptedUserKey; const encUserKey = trustedDeviceOption?.encryptedUserKey;
@ -322,7 +322,7 @@ export class SsoLoginStrategy extends LoginStrategy {
return; return;
} }
const userKey = await this.deviceTrustCryptoService.decryptUserKeyWithDeviceKey( const userKey = await this.deviceTrustService.decryptUserKeyWithDeviceKey(
userId, userId,
encDevicePrivateKey, encDevicePrivateKey,
encUserKey, encUserKey,

View File

@ -2,7 +2,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 { DeviceTrustCryptoServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust-crypto.service.abstraction"; import { DeviceTrustServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust.service.abstraction";
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";
@ -62,7 +62,7 @@ describe("LoginStrategyService", () => {
let encryptService: MockProxy<EncryptService>; let encryptService: MockProxy<EncryptService>;
let passwordStrengthService: MockProxy<PasswordStrengthServiceAbstraction>; let passwordStrengthService: MockProxy<PasswordStrengthServiceAbstraction>;
let policyService: MockProxy<PolicyService>; let policyService: MockProxy<PolicyService>;
let deviceTrustCryptoService: MockProxy<DeviceTrustCryptoServiceAbstraction>; let deviceTrustService: MockProxy<DeviceTrustServiceAbstraction>;
let authRequestService: MockProxy<AuthRequestServiceAbstraction>; let authRequestService: MockProxy<AuthRequestServiceAbstraction>;
let userDecryptionOptionsService: MockProxy<InternalUserDecryptionOptionsServiceAbstraction>; let userDecryptionOptionsService: MockProxy<InternalUserDecryptionOptionsServiceAbstraction>;
let billingAccountProfileStateService: MockProxy<BillingAccountProfileStateService>; let billingAccountProfileStateService: MockProxy<BillingAccountProfileStateService>;
@ -90,7 +90,7 @@ describe("LoginStrategyService", () => {
encryptService = mock<EncryptService>(); encryptService = mock<EncryptService>();
passwordStrengthService = mock<PasswordStrengthServiceAbstraction>(); passwordStrengthService = mock<PasswordStrengthServiceAbstraction>();
policyService = mock<PolicyService>(); policyService = mock<PolicyService>();
deviceTrustCryptoService = mock<DeviceTrustCryptoServiceAbstraction>(); deviceTrustService = mock<DeviceTrustServiceAbstraction>();
authRequestService = mock<AuthRequestServiceAbstraction>(); authRequestService = mock<AuthRequestServiceAbstraction>();
userDecryptionOptionsService = mock<UserDecryptionOptionsService>(); userDecryptionOptionsService = mock<UserDecryptionOptionsService>();
billingAccountProfileStateService = mock<BillingAccountProfileStateService>(); billingAccountProfileStateService = mock<BillingAccountProfileStateService>();
@ -114,7 +114,7 @@ describe("LoginStrategyService", () => {
encryptService, encryptService,
passwordStrengthService, passwordStrengthService,
policyService, policyService,
deviceTrustCryptoService, deviceTrustService,
authRequestService, authRequestService,
userDecryptionOptionsService, userDecryptionOptionsService,
stateProvider, stateProvider,

View File

@ -10,7 +10,6 @@ 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 { DeviceTrustCryptoServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust-crypto.service.abstraction";
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";
@ -36,6 +35,7 @@ import { StateService } from "@bitwarden/common/platform/abstractions/state.serv
import { KdfType } from "@bitwarden/common/platform/enums"; import { KdfType } from "@bitwarden/common/platform/enums";
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 { PasswordStrengthServiceAbstraction } from "@bitwarden/common/tools/password-strength"; import { PasswordStrengthServiceAbstraction } from "@bitwarden/common/tools/password-strength";
import { MasterKey } from "@bitwarden/common/types/key"; import { MasterKey } from "@bitwarden/common/types/key";
@ -100,7 +100,7 @@ export class LoginStrategyService implements LoginStrategyServiceAbstraction {
protected encryptService: EncryptService, protected encryptService: EncryptService,
protected passwordStrengthService: PasswordStrengthServiceAbstraction, protected passwordStrengthService: PasswordStrengthServiceAbstraction,
protected policyService: PolicyService, protected policyService: PolicyService,
protected deviceTrustCryptoService: DeviceTrustCryptoServiceAbstraction, protected deviceTrustService: DeviceTrustServiceAbstraction,
protected authRequestService: AuthRequestServiceAbstraction, protected authRequestService: AuthRequestServiceAbstraction,
protected userDecryptionOptionsService: InternalUserDecryptionOptionsServiceAbstraction, protected userDecryptionOptionsService: InternalUserDecryptionOptionsServiceAbstraction,
protected stateProvider: GlobalStateProvider, protected stateProvider: GlobalStateProvider,
@ -371,7 +371,7 @@ export class LoginStrategyService implements LoginStrategyServiceAbstraction {
this.twoFactorService, this.twoFactorService,
this.userDecryptionOptionsService, this.userDecryptionOptionsService,
this.keyConnectorService, this.keyConnectorService,
this.deviceTrustCryptoService, this.deviceTrustService,
this.authRequestService, this.authRequestService,
this.i18nService, this.i18nService,
this.billingAccountProfileStateService, this.billingAccountProfileStateService,
@ -410,7 +410,7 @@ export class LoginStrategyService implements LoginStrategyServiceAbstraction {
this.stateService, this.stateService,
this.twoFactorService, this.twoFactorService,
this.userDecryptionOptionsService, this.userDecryptionOptionsService,
this.deviceTrustCryptoService, this.deviceTrustService,
this.billingAccountProfileStateService, this.billingAccountProfileStateService,
); );
case AuthenticationType.WebAuthn: case AuthenticationType.WebAuthn:

View File

@ -3,9 +3,10 @@ import { Observable } from "rxjs";
import { EncString } from "../../platform/models/domain/enc-string"; import { EncString } from "../../platform/models/domain/enc-string";
import { UserId } from "../../types/guid"; import { UserId } from "../../types/guid";
import { DeviceKey, UserKey } from "../../types/key"; import { DeviceKey, UserKey } from "../../types/key";
import { DeviceResponse } from "../abstractions/devices/responses/device.response";
export abstract class DeviceTrustCryptoServiceAbstraction { import { DeviceResponse } from "./devices/responses/device.response";
export abstract class DeviceTrustServiceAbstraction {
supportsDeviceTrust$: Observable<boolean>; supportsDeviceTrust$: Observable<boolean>;
/** /**
* @description Retrieves the users choice to trust the device which can only happen after decryption * @description Retrieves the users choice to trust the device which can only happen after decryption

View File

@ -213,4 +213,10 @@ export abstract class TokenService {
* @returns A promise that resolves with a boolean representing the user's external authN status. * @returns A promise that resolves with a boolean representing the user's external authN status.
*/ */
getIsExternal: () => Promise<boolean>; getIsExternal: () => Promise<boolean>;
/** Gets the active or passed in user's security stamp */
getSecurityStamp: (userId?: UserId) => Promise<string | null>;
/** Sets the security stamp for the active or passed in user */
setSecurityStamp: (securityStamp: string, userId?: UserId) => Promise<void>;
} }

View File

@ -17,7 +17,7 @@ import { SymmetricCryptoKey } from "../../platform/models/domain/symmetric-crypt
import { DEVICE_TRUST_DISK_LOCAL, StateProvider, UserKeyDefinition } from "../../platform/state"; import { DEVICE_TRUST_DISK_LOCAL, StateProvider, UserKeyDefinition } from "../../platform/state";
import { UserId } from "../../types/guid"; import { UserId } from "../../types/guid";
import { UserKey, DeviceKey } from "../../types/key"; import { UserKey, DeviceKey } from "../../types/key";
import { DeviceTrustCryptoServiceAbstraction } from "../abstractions/device-trust-crypto.service.abstraction"; import { DeviceTrustServiceAbstraction } from "../abstractions/device-trust.service.abstraction";
import { DeviceResponse } from "../abstractions/devices/responses/device.response"; import { DeviceResponse } from "../abstractions/devices/responses/device.response";
import { DevicesApiServiceAbstraction } from "../abstractions/devices-api.service.abstraction"; import { DevicesApiServiceAbstraction } from "../abstractions/devices-api.service.abstraction";
import { SecretVerificationRequest } from "../models/request/secret-verification.request"; import { SecretVerificationRequest } from "../models/request/secret-verification.request";
@ -42,7 +42,7 @@ export const SHOULD_TRUST_DEVICE = new UserKeyDefinition<boolean>(
}, },
); );
export class DeviceTrustCryptoService implements DeviceTrustCryptoServiceAbstraction { export class DeviceTrustService implements DeviceTrustServiceAbstraction {
private readonly platformSupportsSecureStorage = private readonly platformSupportsSecureStorage =
this.platformUtilsService.supportsSecureStorage(); this.platformUtilsService.supportsSecureStorage();
private readonly deviceKeySecureStorageKey: string = "_deviceKey"; private readonly deviceKeySecureStorageKey: string = "_deviceKey";

View File

@ -33,11 +33,11 @@ import { ProtectedDeviceResponse } from "../models/response/protected-device.res
import { import {
SHOULD_TRUST_DEVICE, SHOULD_TRUST_DEVICE,
DEVICE_KEY, DEVICE_KEY,
DeviceTrustCryptoService, DeviceTrustService,
} from "./device-trust-crypto.service.implementation"; } from "./device-trust.service.implementation";
describe("deviceTrustCryptoService", () => { describe("deviceTrustService", () => {
let deviceTrustCryptoService: DeviceTrustCryptoService; let deviceTrustService: DeviceTrustService;
const keyGenerationService = mock<KeyGenerationService>(); const keyGenerationService = mock<KeyGenerationService>();
const cryptoFunctionService = mock<CryptoFunctionService>(); const cryptoFunctionService = mock<CryptoFunctionService>();
@ -70,11 +70,11 @@ describe("deviceTrustCryptoService", () => {
jest.clearAllMocks(); jest.clearAllMocks();
const supportsSecureStorage = false; // default to false; tests will override as needed const supportsSecureStorage = false; // default to false; tests will override as needed
// By default all the tests will have a mocked active user in state provider. // By default all the tests will have a mocked active user in state provider.
deviceTrustCryptoService = createDeviceTrustCryptoService(mockUserId, supportsSecureStorage); deviceTrustService = createDeviceTrustService(mockUserId, supportsSecureStorage);
}); });
it("instantiates", () => { it("instantiates", () => {
expect(deviceTrustCryptoService).not.toBeFalsy(); expect(deviceTrustService).not.toBeFalsy();
}); });
describe("User Trust Device Choice For Decryption", () => { describe("User Trust Device Choice For Decryption", () => {
@ -84,7 +84,7 @@ describe("deviceTrustCryptoService", () => {
await stateProvider.setUserState(SHOULD_TRUST_DEVICE, newValue, mockUserId); await stateProvider.setUserState(SHOULD_TRUST_DEVICE, newValue, mockUserId);
const result = await deviceTrustCryptoService.getShouldTrustDevice(mockUserId); const result = await deviceTrustService.getShouldTrustDevice(mockUserId);
expect(result).toEqual(newValue); expect(result).toEqual(newValue);
}); });
@ -95,9 +95,9 @@ describe("deviceTrustCryptoService", () => {
await stateProvider.setUserState(SHOULD_TRUST_DEVICE, false, mockUserId); await stateProvider.setUserState(SHOULD_TRUST_DEVICE, false, mockUserId);
const newValue = true; const newValue = true;
await deviceTrustCryptoService.setShouldTrustDevice(mockUserId, newValue); await deviceTrustService.setShouldTrustDevice(mockUserId, newValue);
const result = await deviceTrustCryptoService.getShouldTrustDevice(mockUserId); const result = await deviceTrustService.getShouldTrustDevice(mockUserId);
expect(result).toEqual(newValue); expect(result).toEqual(newValue);
}); });
}); });
@ -105,25 +105,25 @@ describe("deviceTrustCryptoService", () => {
describe("trustDeviceIfRequired", () => { describe("trustDeviceIfRequired", () => {
it("should trust device and reset when getShouldTrustDevice returns true", async () => { it("should trust device and reset when getShouldTrustDevice returns true", async () => {
jest.spyOn(deviceTrustCryptoService, "getShouldTrustDevice").mockResolvedValue(true); jest.spyOn(deviceTrustService, "getShouldTrustDevice").mockResolvedValue(true);
jest.spyOn(deviceTrustCryptoService, "trustDevice").mockResolvedValue({} as DeviceResponse); jest.spyOn(deviceTrustService, "trustDevice").mockResolvedValue({} as DeviceResponse);
jest.spyOn(deviceTrustCryptoService, "setShouldTrustDevice").mockResolvedValue(); jest.spyOn(deviceTrustService, "setShouldTrustDevice").mockResolvedValue();
await deviceTrustCryptoService.trustDeviceIfRequired(mockUserId); await deviceTrustService.trustDeviceIfRequired(mockUserId);
expect(deviceTrustCryptoService.getShouldTrustDevice).toHaveBeenCalledTimes(1); expect(deviceTrustService.getShouldTrustDevice).toHaveBeenCalledTimes(1);
expect(deviceTrustCryptoService.trustDevice).toHaveBeenCalledTimes(1); expect(deviceTrustService.trustDevice).toHaveBeenCalledTimes(1);
expect(deviceTrustCryptoService.setShouldTrustDevice).toHaveBeenCalledWith(mockUserId, false); expect(deviceTrustService.setShouldTrustDevice).toHaveBeenCalledWith(mockUserId, false);
}); });
it("should not trust device nor reset when getShouldTrustDevice returns false", async () => { it("should not trust device nor reset when getShouldTrustDevice returns false", async () => {
const getShouldTrustDeviceSpy = jest const getShouldTrustDeviceSpy = jest
.spyOn(deviceTrustCryptoService, "getShouldTrustDevice") .spyOn(deviceTrustService, "getShouldTrustDevice")
.mockResolvedValue(false); .mockResolvedValue(false);
const trustDeviceSpy = jest.spyOn(deviceTrustCryptoService, "trustDevice"); const trustDeviceSpy = jest.spyOn(deviceTrustService, "trustDevice");
const setShouldTrustDeviceSpy = jest.spyOn(deviceTrustCryptoService, "setShouldTrustDevice"); const setShouldTrustDeviceSpy = jest.spyOn(deviceTrustService, "setShouldTrustDevice");
await deviceTrustCryptoService.trustDeviceIfRequired(mockUserId); await deviceTrustService.trustDeviceIfRequired(mockUserId);
expect(getShouldTrustDeviceSpy).toHaveBeenCalledTimes(1); expect(getShouldTrustDeviceSpy).toHaveBeenCalledTimes(1);
expect(trustDeviceSpy).not.toHaveBeenCalled(); expect(trustDeviceSpy).not.toHaveBeenCalled();
@ -151,7 +151,7 @@ describe("deviceTrustCryptoService", () => {
it("returns null when there is not an existing device key", async () => { it("returns null when there is not an existing device key", async () => {
await stateProvider.setUserState(DEVICE_KEY, null, mockUserId); await stateProvider.setUserState(DEVICE_KEY, null, mockUserId);
const deviceKey = await deviceTrustCryptoService.getDeviceKey(mockUserId); const deviceKey = await deviceTrustService.getDeviceKey(mockUserId);
expect(deviceKey).toBeNull(); expect(deviceKey).toBeNull();
expect(secureStorageService.get).not.toHaveBeenCalled(); expect(secureStorageService.get).not.toHaveBeenCalled();
@ -160,7 +160,7 @@ describe("deviceTrustCryptoService", () => {
it("returns the device key when there is an existing device key", async () => { it("returns the device key when there is an existing device key", async () => {
await stateProvider.setUserState(DEVICE_KEY, existingDeviceKey, mockUserId); await stateProvider.setUserState(DEVICE_KEY, existingDeviceKey, mockUserId);
const deviceKey = await deviceTrustCryptoService.getDeviceKey(mockUserId); const deviceKey = await deviceTrustService.getDeviceKey(mockUserId);
expect(deviceKey).not.toBeNull(); expect(deviceKey).not.toBeNull();
expect(deviceKey).toBeInstanceOf(SymmetricCryptoKey); expect(deviceKey).toBeInstanceOf(SymmetricCryptoKey);
@ -172,17 +172,14 @@ describe("deviceTrustCryptoService", () => {
describe("Secure Storage supported", () => { describe("Secure Storage supported", () => {
beforeEach(() => { beforeEach(() => {
const supportsSecureStorage = true; const supportsSecureStorage = true;
deviceTrustCryptoService = createDeviceTrustCryptoService( deviceTrustService = createDeviceTrustService(mockUserId, supportsSecureStorage);
mockUserId,
supportsSecureStorage,
);
}); });
it("returns null when there is not an existing device key for the passed in user id", async () => { it("returns null when there is not an existing device key for the passed in user id", async () => {
secureStorageService.get.mockResolvedValue(null); secureStorageService.get.mockResolvedValue(null);
// Act // Act
const deviceKey = await deviceTrustCryptoService.getDeviceKey(mockUserId); const deviceKey = await deviceTrustService.getDeviceKey(mockUserId);
// Assert // Assert
expect(deviceKey).toBeNull(); expect(deviceKey).toBeNull();
@ -193,7 +190,7 @@ describe("deviceTrustCryptoService", () => {
secureStorageService.get.mockResolvedValue(existingDeviceKeyB64); secureStorageService.get.mockResolvedValue(existingDeviceKeyB64);
// Act // Act
const deviceKey = await deviceTrustCryptoService.getDeviceKey(mockUserId); const deviceKey = await deviceTrustService.getDeviceKey(mockUserId);
// Assert // Assert
expect(deviceKey).not.toBeNull(); expect(deviceKey).not.toBeNull();
@ -203,7 +200,7 @@ describe("deviceTrustCryptoService", () => {
}); });
it("throws an error when no user id is passed in", async () => { it("throws an error when no user id is passed in", async () => {
await expect(deviceTrustCryptoService.getDeviceKey(null)).rejects.toThrow( await expect(deviceTrustService.getDeviceKey(null)).rejects.toThrow(
"UserId is required. Cannot get device key.", "UserId is required. Cannot get device key.",
); );
}); });
@ -220,7 +217,7 @@ describe("deviceTrustCryptoService", () => {
// TypeScript will allow calling private methods if the object is of type 'any' // TypeScript will allow calling private methods if the object is of type 'any'
// This is a hacky workaround, but it allows for cleaner tests // This is a hacky workaround, but it allows for cleaner tests
await (deviceTrustCryptoService as any).setDeviceKey(mockUserId, newDeviceKey); await (deviceTrustService as any).setDeviceKey(mockUserId, newDeviceKey);
expect(stateProvider.mock.setUserState).toHaveBeenLastCalledWith( expect(stateProvider.mock.setUserState).toHaveBeenLastCalledWith(
DEVICE_KEY, DEVICE_KEY,
@ -232,10 +229,7 @@ describe("deviceTrustCryptoService", () => {
describe("Secure Storage supported", () => { describe("Secure Storage supported", () => {
beforeEach(() => { beforeEach(() => {
const supportsSecureStorage = true; const supportsSecureStorage = true;
deviceTrustCryptoService = createDeviceTrustCryptoService( deviceTrustService = createDeviceTrustService(mockUserId, supportsSecureStorage);
mockUserId,
supportsSecureStorage,
);
}); });
it("successfully sets the device key in secure storage", async () => { it("successfully sets the device key in secure storage", async () => {
@ -251,7 +245,7 @@ describe("deviceTrustCryptoService", () => {
// Act // Act
// TypeScript will allow calling private methods if the object is of type 'any' // TypeScript will allow calling private methods if the object is of type 'any'
// This is a hacky workaround, but it allows for cleaner tests // This is a hacky workaround, but it allows for cleaner tests
await (deviceTrustCryptoService as any).setDeviceKey(mockUserId, newDeviceKey); await (deviceTrustService as any).setDeviceKey(mockUserId, newDeviceKey);
// Assert // Assert
expect(stateProvider.mock.setUserState).not.toHaveBeenCalledTimes(2); expect(stateProvider.mock.setUserState).not.toHaveBeenCalledTimes(2);
@ -268,9 +262,9 @@ describe("deviceTrustCryptoService", () => {
new Uint8Array(deviceKeyBytesLength) as CsprngArray, new Uint8Array(deviceKeyBytesLength) as CsprngArray,
) as DeviceKey; ) as DeviceKey;
await expect( await expect((deviceTrustService as any).setDeviceKey(null, newDeviceKey)).rejects.toThrow(
(deviceTrustCryptoService as any).setDeviceKey(null, newDeviceKey), "UserId is required. Cannot set device key.",
).rejects.toThrow("UserId is required. Cannot set device key."); );
}); });
}); });
@ -285,7 +279,7 @@ describe("deviceTrustCryptoService", () => {
// TypeScript will allow calling private methods if the object is of type 'any' // TypeScript will allow calling private methods if the object is of type 'any'
// This is a hacky workaround, but it allows for cleaner tests // This is a hacky workaround, but it allows for cleaner tests
const deviceKey = await (deviceTrustCryptoService as any).makeDeviceKey(); const deviceKey = await (deviceTrustService as any).makeDeviceKey();
expect(keyGenSvcGenerateKeySpy).toHaveBeenCalledTimes(1); expect(keyGenSvcGenerateKeySpy).toHaveBeenCalledTimes(1);
expect(keyGenSvcGenerateKeySpy).toHaveBeenCalledWith(deviceKeyBytesLength * 8); expect(keyGenSvcGenerateKeySpy).toHaveBeenCalledWith(deviceKeyBytesLength * 8);
@ -362,7 +356,7 @@ describe("deviceTrustCryptoService", () => {
// TypeScript will allow calling private methods if the object is of type 'any' // TypeScript will allow calling private methods if the object is of type 'any'
makeDeviceKeySpy = jest makeDeviceKeySpy = jest
.spyOn(deviceTrustCryptoService as any, "makeDeviceKey") .spyOn(deviceTrustService as any, "makeDeviceKey")
.mockResolvedValue(mockDeviceKey); .mockResolvedValue(mockDeviceKey);
rsaGenerateKeyPairSpy = jest rsaGenerateKeyPairSpy = jest
@ -398,7 +392,7 @@ describe("deviceTrustCryptoService", () => {
}); });
it("calls the required methods with the correct arguments and returns a DeviceResponse", async () => { it("calls the required methods with the correct arguments and returns a DeviceResponse", async () => {
const response = await deviceTrustCryptoService.trustDevice(mockUserId); const response = await deviceTrustService.trustDevice(mockUserId);
expect(makeDeviceKeySpy).toHaveBeenCalledTimes(1); expect(makeDeviceKeySpy).toHaveBeenCalledTimes(1);
expect(rsaGenerateKeyPairSpy).toHaveBeenCalledTimes(1); expect(rsaGenerateKeyPairSpy).toHaveBeenCalledTimes(1);
@ -429,7 +423,7 @@ describe("deviceTrustCryptoService", () => {
// setup the spy to return null // setup the spy to return null
cryptoSvcGetUserKeySpy.mockResolvedValue(null); cryptoSvcGetUserKeySpy.mockResolvedValue(null);
// check if the expected error is thrown // check if the expected error is thrown
await expect(deviceTrustCryptoService.trustDevice(mockUserId)).rejects.toThrow( await expect(deviceTrustService.trustDevice(mockUserId)).rejects.toThrow(
"User symmetric key not found", "User symmetric key not found",
); );
@ -439,7 +433,7 @@ describe("deviceTrustCryptoService", () => {
// setup the spy to return undefined // setup the spy to return undefined
cryptoSvcGetUserKeySpy.mockResolvedValue(undefined); cryptoSvcGetUserKeySpy.mockResolvedValue(undefined);
// check if the expected error is thrown // check if the expected error is thrown
await expect(deviceTrustCryptoService.trustDevice(mockUserId)).rejects.toThrow( await expect(deviceTrustService.trustDevice(mockUserId)).rejects.toThrow(
"User symmetric key not found", "User symmetric key not found",
); );
}); });
@ -479,9 +473,7 @@ describe("deviceTrustCryptoService", () => {
it(`throws an error if ${method} fails`, async () => { it(`throws an error if ${method} fails`, async () => {
const methodSpy = spy(); const methodSpy = spy();
methodSpy.mockRejectedValue(new Error(errorText)); methodSpy.mockRejectedValue(new Error(errorText));
await expect(deviceTrustCryptoService.trustDevice(mockUserId)).rejects.toThrow( await expect(deviceTrustService.trustDevice(mockUserId)).rejects.toThrow(errorText);
errorText,
);
}); });
test.each([null, undefined])( test.each([null, undefined])(
@ -489,14 +481,14 @@ describe("deviceTrustCryptoService", () => {
async (invalidValue) => { async (invalidValue) => {
const methodSpy = spy(); const methodSpy = spy();
methodSpy.mockResolvedValue(invalidValue); methodSpy.mockResolvedValue(invalidValue);
await expect(deviceTrustCryptoService.trustDevice(mockUserId)).rejects.toThrow(); await expect(deviceTrustService.trustDevice(mockUserId)).rejects.toThrow();
}, },
); );
}, },
); );
it("throws an error when a null user id is passed in", async () => { it("throws an error when a null user id is passed in", async () => {
await expect(deviceTrustCryptoService.trustDevice(null)).rejects.toThrow( await expect(deviceTrustService.trustDevice(null)).rejects.toThrow(
"UserId is required. Cannot trust device.", "UserId is required. Cannot trust device.",
); );
}); });
@ -530,7 +522,7 @@ describe("deviceTrustCryptoService", () => {
it("throws an error when a null user id is passed in", async () => { it("throws an error when a null user id is passed in", async () => {
await expect( await expect(
deviceTrustCryptoService.decryptUserKeyWithDeviceKey( deviceTrustService.decryptUserKeyWithDeviceKey(
null, null,
mockEncryptedDevicePrivateKey, mockEncryptedDevicePrivateKey,
mockEncryptedUserKey, mockEncryptedUserKey,
@ -540,7 +532,7 @@ describe("deviceTrustCryptoService", () => {
}); });
it("returns null when device key isn't provided", async () => { it("returns null when device key isn't provided", async () => {
const result = await deviceTrustCryptoService.decryptUserKeyWithDeviceKey( const result = await deviceTrustService.decryptUserKeyWithDeviceKey(
mockUserId, mockUserId,
mockEncryptedDevicePrivateKey, mockEncryptedDevicePrivateKey,
mockEncryptedUserKey, mockEncryptedUserKey,
@ -558,7 +550,7 @@ describe("deviceTrustCryptoService", () => {
.spyOn(cryptoService, "rsaDecrypt") .spyOn(cryptoService, "rsaDecrypt")
.mockResolvedValue(new Uint8Array(userKeyBytesLength)); .mockResolvedValue(new Uint8Array(userKeyBytesLength));
const result = await deviceTrustCryptoService.decryptUserKeyWithDeviceKey( const result = await deviceTrustService.decryptUserKeyWithDeviceKey(
mockUserId, mockUserId,
mockEncryptedDevicePrivateKey, mockEncryptedDevicePrivateKey,
mockEncryptedUserKey, mockEncryptedUserKey,
@ -574,9 +566,9 @@ describe("deviceTrustCryptoService", () => {
const decryptToBytesSpy = jest const decryptToBytesSpy = jest
.spyOn(encryptService, "decryptToBytes") .spyOn(encryptService, "decryptToBytes")
.mockRejectedValue(new Error("Decryption error")); .mockRejectedValue(new Error("Decryption error"));
const setDeviceKeySpy = jest.spyOn(deviceTrustCryptoService as any, "setDeviceKey"); const setDeviceKeySpy = jest.spyOn(deviceTrustService as any, "setDeviceKey");
const result = await deviceTrustCryptoService.decryptUserKeyWithDeviceKey( const result = await deviceTrustService.decryptUserKeyWithDeviceKey(
mockUserId, mockUserId,
mockEncryptedDevicePrivateKey, mockEncryptedDevicePrivateKey,
mockEncryptedUserKey, mockEncryptedUserKey,
@ -606,7 +598,7 @@ describe("deviceTrustCryptoService", () => {
it("throws an error when a null user id is passed in", async () => { it("throws an error when a null user id is passed in", async () => {
await expect( await expect(
deviceTrustCryptoService.rotateDevicesTrust(null, fakeNewUserKey, ""), deviceTrustService.rotateDevicesTrust(null, fakeNewUserKey, ""),
).rejects.toThrow("UserId is required. Cannot rotate device's trust."); ).rejects.toThrow("UserId is required. Cannot rotate device's trust.");
}); });
@ -615,7 +607,7 @@ describe("deviceTrustCryptoService", () => {
stateProvider.activeUser.getFake(DEVICE_KEY); stateProvider.activeUser.getFake(DEVICE_KEY);
deviceKeyState.nextState(null); deviceKeyState.nextState(null);
await deviceTrustCryptoService.rotateDevicesTrust(mockUserId, fakeNewUserKey, ""); await deviceTrustService.rotateDevicesTrust(mockUserId, fakeNewUserKey, "");
expect(devicesApiService.updateTrust).not.toHaveBeenCalled(); expect(devicesApiService.updateTrust).not.toHaveBeenCalled();
}); });
@ -691,7 +683,7 @@ describe("deviceTrustCryptoService", () => {
); );
}); });
await deviceTrustCryptoService.rotateDevicesTrust( await deviceTrustService.rotateDevicesTrust(
mockUserId, mockUserId,
fakeNewUserKey, fakeNewUserKey,
"my_password_hash", "my_password_hash",
@ -713,10 +705,7 @@ describe("deviceTrustCryptoService", () => {
}); });
// Helpers // Helpers
function createDeviceTrustCryptoService( function createDeviceTrustService(mockUserId: UserId | null, supportsSecureStorage: boolean) {
mockUserId: UserId | null,
supportsSecureStorage: boolean,
) {
accountService = mockAccountServiceWith(mockUserId); accountService = mockAccountServiceWith(mockUserId);
stateProvider = new FakeStateProvider(accountService); stateProvider = new FakeStateProvider(accountService);
@ -725,7 +714,7 @@ describe("deviceTrustCryptoService", () => {
decryptionOptions.next({} as any); decryptionOptions.next({} as any);
userDecryptionOptionsService.userDecryptionOptions$ = decryptionOptions; userDecryptionOptionsService.userDecryptionOptions$ = decryptionOptions;
return new DeviceTrustCryptoService( return new DeviceTrustService(
keyGenerationService, keyGenerationService,
cryptoFunctionService, cryptoFunctionService,
cryptoService, cryptoService,

View File

@ -23,6 +23,7 @@ import {
EMAIL_TWO_FACTOR_TOKEN_RECORD_DISK_LOCAL, EMAIL_TWO_FACTOR_TOKEN_RECORD_DISK_LOCAL,
REFRESH_TOKEN_DISK, REFRESH_TOKEN_DISK,
REFRESH_TOKEN_MEMORY, REFRESH_TOKEN_MEMORY,
SECURITY_STAMP_MEMORY,
} from "./token.state"; } from "./token.state";
describe("TokenService", () => { describe("TokenService", () => {
@ -2191,6 +2192,84 @@ describe("TokenService", () => {
}); });
}); });
describe("Security Stamp methods", () => {
const mockSecurityStamp = "securityStamp";
describe("setSecurityStamp", () => {
it("should throw an error if no user id is provided and there is no active user in global state", async () => {
// Act
// note: don't await here because we want to test the error
const result = tokenService.setSecurityStamp(mockSecurityStamp);
// Assert
await expect(result).rejects.toThrow("User id not found. Cannot set security stamp.");
});
it("should set the security stamp in memory when there is an active user in global state", async () => {
// Arrange
globalStateProvider
.getFake(ACCOUNT_ACTIVE_ACCOUNT_ID)
.stateSubject.next(userIdFromAccessToken);
// Act
await tokenService.setSecurityStamp(mockSecurityStamp);
// Assert
expect(
singleUserStateProvider.getFake(userIdFromAccessToken, SECURITY_STAMP_MEMORY).nextMock,
).toHaveBeenCalledWith(mockSecurityStamp);
});
it("should set the security stamp in memory for the specified user id", async () => {
// Act
await tokenService.setSecurityStamp(mockSecurityStamp, userIdFromAccessToken);
// Assert
expect(
singleUserStateProvider.getFake(userIdFromAccessToken, SECURITY_STAMP_MEMORY).nextMock,
).toHaveBeenCalledWith(mockSecurityStamp);
});
});
describe("getSecurityStamp", () => {
it("should throw an error if no user id is provided and there is no active user in global state", async () => {
// Act
// note: don't await here because we want to test the error
const result = tokenService.getSecurityStamp();
// Assert
await expect(result).rejects.toThrow("User id not found. Cannot get security stamp.");
});
it("should return the security stamp from memory with no user id specified (uses global active user)", async () => {
// Arrange
globalStateProvider
.getFake(ACCOUNT_ACTIVE_ACCOUNT_ID)
.stateSubject.next(userIdFromAccessToken);
singleUserStateProvider
.getFake(userIdFromAccessToken, SECURITY_STAMP_MEMORY)
.stateSubject.next([userIdFromAccessToken, mockSecurityStamp]);
// Act
const result = await tokenService.getSecurityStamp();
// Assert
expect(result).toEqual(mockSecurityStamp);
});
it("should return the security stamp from memory for the specified user id", async () => {
// Arrange
singleUserStateProvider
.getFake(userIdFromAccessToken, SECURITY_STAMP_MEMORY)
.stateSubject.next([userIdFromAccessToken, mockSecurityStamp]);
// Act
const result = await tokenService.getSecurityStamp(userIdFromAccessToken);
// Assert
expect(result).toEqual(mockSecurityStamp);
});
});
});
// Helpers // Helpers
function createTokenService(supportsSecureStorage: boolean) { function createTokenService(supportsSecureStorage: boolean) {
return new TokenService( return new TokenService(

View File

@ -32,6 +32,7 @@ import {
EMAIL_TWO_FACTOR_TOKEN_RECORD_DISK_LOCAL, EMAIL_TWO_FACTOR_TOKEN_RECORD_DISK_LOCAL,
REFRESH_TOKEN_DISK, REFRESH_TOKEN_DISK,
REFRESH_TOKEN_MEMORY, REFRESH_TOKEN_MEMORY,
SECURITY_STAMP_MEMORY,
} from "./token.state"; } from "./token.state";
export enum TokenStorageLocation { export enum TokenStorageLocation {
@ -850,6 +851,30 @@ export class TokenService implements TokenServiceAbstraction {
return Array.isArray(decoded.amr) && decoded.amr.includes("external"); return Array.isArray(decoded.amr) && decoded.amr.includes("external");
} }
async getSecurityStamp(userId?: UserId): Promise<string | null> {
userId ??= await firstValueFrom(this.activeUserIdGlobalState.state$);
if (!userId) {
throw new Error("User id not found. Cannot get security stamp.");
}
const securityStamp = await this.getStateValueByUserIdAndKeyDef(userId, SECURITY_STAMP_MEMORY);
return securityStamp;
}
async setSecurityStamp(securityStamp: string, userId?: UserId): Promise<void> {
userId ??= await firstValueFrom(this.activeUserIdGlobalState.state$);
if (!userId) {
throw new Error("User id not found. Cannot set security stamp.");
}
await this.singleUserStateProvider
.get(userId, SECURITY_STAMP_MEMORY)
.update((_) => securityStamp);
}
private async getStateValueByUserIdAndKeyDef( private async getStateValueByUserIdAndKeyDef(
userId: UserId, userId: UserId,
storageLocation: UserKeyDefinition<string>, storageLocation: UserKeyDefinition<string>,

View File

@ -10,6 +10,7 @@ import {
EMAIL_TWO_FACTOR_TOKEN_RECORD_DISK_LOCAL, EMAIL_TWO_FACTOR_TOKEN_RECORD_DISK_LOCAL,
REFRESH_TOKEN_DISK, REFRESH_TOKEN_DISK,
REFRESH_TOKEN_MEMORY, REFRESH_TOKEN_MEMORY,
SECURITY_STAMP_MEMORY,
} from "./token.state"; } from "./token.state";
describe.each([ describe.each([
@ -22,6 +23,7 @@ describe.each([
[API_KEY_CLIENT_ID_MEMORY, "apiKeyClientIdMemory"], [API_KEY_CLIENT_ID_MEMORY, "apiKeyClientIdMemory"],
[API_KEY_CLIENT_SECRET_DISK, "apiKeyClientSecretDisk"], [API_KEY_CLIENT_SECRET_DISK, "apiKeyClientSecretDisk"],
[API_KEY_CLIENT_SECRET_MEMORY, "apiKeyClientSecretMemory"], [API_KEY_CLIENT_SECRET_MEMORY, "apiKeyClientSecretMemory"],
[SECURITY_STAMP_MEMORY, "securityStamp"],
])( ])(
"deserializes state key definitions", "deserializes state key definitions",
( (

View File

@ -69,3 +69,8 @@ export const API_KEY_CLIENT_SECRET_MEMORY = new UserKeyDefinition<string>(
clearOn: [], // Manually handled clearOn: [], // Manually handled
}, },
); );
export const SECURITY_STAMP_MEMORY = new UserKeyDefinition<string>(TOKEN_MEMORY, "securityStamp", {
deserializer: (securityStamp) => securityStamp,
clearOn: ["logout"],
});

View File

@ -1,4 +1,5 @@
import { SubscriptionCancellationRequest } from "../../billing/models/request/subscription-cancellation.request"; import { SubscriptionCancellationRequest } from "../../billing/models/request/subscription-cancellation.request";
import { OrganizationBillingMetadataResponse } from "../../billing/models/response/organization-billing-metadata.response";
import { OrganizationBillingStatusResponse } from "../../billing/models/response/organization-billing-status.response"; import { OrganizationBillingStatusResponse } from "../../billing/models/response/organization-billing-status.response";
import { OrganizationSubscriptionResponse } from "../../billing/models/response/organization-subscription.response"; import { OrganizationSubscriptionResponse } from "../../billing/models/response/organization-subscription.response";
import { PlanResponse } from "../../billing/models/response/plan.response"; import { PlanResponse } from "../../billing/models/response/plan.response";
@ -12,13 +13,15 @@ export abstract class BillingApiServiceAbstraction {
organizationId: string, organizationId: string,
request: SubscriptionCancellationRequest, request: SubscriptionCancellationRequest,
) => Promise<void>; ) => Promise<void>;
cancelPremiumUserSubscription: (request: SubscriptionCancellationRequest) => Promise<void>; cancelPremiumUserSubscription: (request: SubscriptionCancellationRequest) => Promise<void>;
createClientOrganization: ( createClientOrganization: (
providerId: string, providerId: string,
request: CreateClientOrganizationRequest, request: CreateClientOrganizationRequest,
) => Promise<void>; ) => Promise<void>;
getBillingStatus: (id: string) => Promise<OrganizationBillingStatusResponse>; getBillingStatus: (id: string) => Promise<OrganizationBillingStatusResponse>;
getOrganizationBillingMetadata: (
organizationId: string,
) => Promise<OrganizationBillingMetadataResponse>;
getOrganizationSubscription: ( getOrganizationSubscription: (
organizationId: string, organizationId: string,
) => Promise<OrganizationSubscriptionResponse>; ) => Promise<OrganizationSubscriptionResponse>;

View File

@ -41,8 +41,6 @@ export type SubscriptionInformation = {
}; };
export abstract class OrganizationBillingServiceAbstraction { export abstract class OrganizationBillingServiceAbstraction {
isOnSecretsManagerStandalone: (organizationId: string) => Promise<boolean>;
purchaseSubscription: (subscription: SubscriptionInformation) => Promise<OrganizationResponse>; purchaseSubscription: (subscription: SubscriptionInformation) => Promise<OrganizationResponse>;
startFree: (subscription: SubscriptionInformation) => Promise<OrganizationResponse>; startFree: (subscription: SubscriptionInformation) => Promise<OrganizationResponse>;

View File

@ -0,0 +1,10 @@
import { BaseResponse } from "../../../models/response/base.response";
export class OrganizationBillingMetadataResponse extends BaseResponse {
isOnSecretsManagerStandalone: boolean;
constructor(response: any) {
super(response);
this.isOnSecretsManagerStandalone = this.getResponseProperty("IsOnSecretsManagerStandalone");
}
}

View File

@ -1,3 +1,5 @@
import { OrganizationBillingMetadataResponse } from "@bitwarden/common/billing/models/response/organization-billing-metadata.response";
import { ApiService } from "../../abstractions/api.service"; import { ApiService } from "../../abstractions/api.service";
import { BillingApiServiceAbstraction } from "../../billing/abstractions/billilng-api.service.abstraction"; import { BillingApiServiceAbstraction } from "../../billing/abstractions/billilng-api.service.abstraction";
import { SubscriptionCancellationRequest } from "../../billing/models/request/subscription-cancellation.request"; import { SubscriptionCancellationRequest } from "../../billing/models/request/subscription-cancellation.request";
@ -53,6 +55,20 @@ export class BillingApiService implements BillingApiServiceAbstraction {
return new OrganizationBillingStatusResponse(r); return new OrganizationBillingStatusResponse(r);
} }
async getOrganizationBillingMetadata(
organizationId: string,
): Promise<OrganizationBillingMetadataResponse> {
const r = await this.apiService.send(
"GET",
"/organizations/" + organizationId + "/billing/metadata",
null,
true,
true,
);
return new OrganizationBillingMetadataResponse(r);
}
async getOrganizationSubscription( async getOrganizationSubscription(
organizationId: string, organizationId: string,
): Promise<OrganizationSubscriptionResponse> { ): Promise<OrganizationSubscriptionResponse> {

View File

@ -9,7 +9,6 @@ import { I18nService } from "../../platform/abstractions/i18n.service";
import { EncString } from "../../platform/models/domain/enc-string"; import { EncString } from "../../platform/models/domain/enc-string";
import { OrgKey } from "../../types/key"; import { OrgKey } from "../../types/key";
import { SyncService } from "../../vault/abstractions/sync/sync.service.abstraction"; import { SyncService } from "../../vault/abstractions/sync/sync.service.abstraction";
import { BillingApiServiceAbstraction as BillingApiService } from "../abstractions/billilng-api.service.abstraction";
import { import {
OrganizationBillingServiceAbstraction, OrganizationBillingServiceAbstraction,
OrganizationInformation, OrganizationInformation,
@ -29,7 +28,6 @@ interface OrganizationKeys {
export class OrganizationBillingService implements OrganizationBillingServiceAbstraction { export class OrganizationBillingService implements OrganizationBillingServiceAbstraction {
constructor( constructor(
private apiService: ApiService, private apiService: ApiService,
private billingApiService: BillingApiService,
private cryptoService: CryptoService, private cryptoService: CryptoService,
private encryptService: EncryptService, private encryptService: EncryptService,
private i18nService: I18nService, private i18nService: I18nService,
@ -37,19 +35,6 @@ export class OrganizationBillingService implements OrganizationBillingServiceAbs
private syncService: SyncService, private syncService: SyncService,
) {} ) {}
async isOnSecretsManagerStandalone(organizationId: string): Promise<boolean> {
const response = await this.billingApiService.getOrganizationSubscription(organizationId);
if (response.customerDiscount?.id === "sm-standalone") {
const productIds = response.subscription.items.map((item) => item.productId);
return (
response.customerDiscount?.appliesTo.filter((appliesToProductId) =>
productIds.includes(appliesToProductId),
).length > 0
);
}
return false;
}
async purchaseSubscription(subscription: SubscriptionInformation): Promise<OrganizationResponse> { async purchaseSubscription(subscription: SubscriptionInformation): Promise<OrganizationResponse> {
const request = new OrganizationCreateRequest(); const request = new OrganizationCreateRequest();

View File

@ -2,6 +2,8 @@ import { EncString } from "../../platform/models/domain/enc-string";
import { Card as CardDomain } from "../../vault/models/domain/card"; import { Card as CardDomain } from "../../vault/models/domain/card";
import { CardView } from "../../vault/models/view/card.view"; import { CardView } from "../../vault/models/view/card.view";
import { safeGetString } from "./utils";
export class CardExport { export class CardExport {
static template(): CardExport { static template(): CardExport {
const req = new CardExport(); const req = new CardExport();
@ -46,20 +48,11 @@ export class CardExport {
return; return;
} }
if (o instanceof CardView) { this.cardholderName = safeGetString(o.cardholderName);
this.cardholderName = o.cardholderName; this.brand = safeGetString(o.brand);
this.brand = o.brand; this.number = safeGetString(o.number);
this.number = o.number; this.expMonth = safeGetString(o.expMonth);
this.expMonth = o.expMonth; this.expYear = safeGetString(o.expYear);
this.expYear = o.expYear; this.code = safeGetString(o.code);
this.code = o.code;
} else {
this.cardholderName = o.cardholderName?.encryptedString;
this.brand = o.brand?.encryptedString;
this.number = o.number?.encryptedString;
this.expMonth = o.expMonth?.encryptedString;
this.expYear = o.expYear?.encryptedString;
this.code = o.code?.encryptedString;
}
} }
} }

View File

@ -10,6 +10,7 @@ import { IdentityExport } from "./identity.export";
import { LoginExport } from "./login.export"; import { LoginExport } from "./login.export";
import { PasswordHistoryExport } from "./password-history.export"; import { PasswordHistoryExport } from "./password-history.export";
import { SecureNoteExport } from "./secure-note.export"; import { SecureNoteExport } from "./secure-note.export";
import { safeGetString } from "./utils";
export class CipherExport { export class CipherExport {
static template(): CipherExport { static template(): CipherExport {
@ -145,23 +146,16 @@ export class CipherExport {
this.type = o.type; this.type = o.type;
this.reprompt = o.reprompt; this.reprompt = o.reprompt;
if (o instanceof CipherView) { this.name = safeGetString(o.name);
this.name = o.name; this.notes = safeGetString(o.notes);
this.notes = o.notes; if ("key" in o) {
} else {
this.name = o.name?.encryptedString;
this.notes = o.notes?.encryptedString;
this.key = o.key?.encryptedString; this.key = o.key?.encryptedString;
} }
this.favorite = o.favorite; this.favorite = o.favorite;
if (o.fields != null) { if (o.fields != null) {
if (o instanceof CipherView) { this.fields = o.fields.map((f) => new FieldExport(f));
this.fields = o.fields.map((f) => new FieldExport(f));
} else {
this.fields = o.fields.map((f) => new FieldExport(f));
}
} }
switch (o.type) { switch (o.type) {
@ -180,11 +174,7 @@ export class CipherExport {
} }
if (o.passwordHistory != null) { if (o.passwordHistory != null) {
if (o instanceof CipherView) { this.passwordHistory = o.passwordHistory.map((ph) => new PasswordHistoryExport(ph));
this.passwordHistory = o.passwordHistory.map((ph) => new PasswordHistoryExport(ph));
} else {
this.passwordHistory = o.passwordHistory.map((ph) => new PasswordHistoryExport(ph));
}
} }
this.creationDate = o.creationDate; this.creationDate = o.creationDate;

View File

@ -2,6 +2,8 @@ import { EncString } from "../../platform/models/domain/enc-string";
import { Collection as CollectionDomain } from "../../vault/models/domain/collection"; import { Collection as CollectionDomain } from "../../vault/models/domain/collection";
import { CollectionView } from "../../vault/models/view/collection.view"; import { CollectionView } from "../../vault/models/view/collection.view";
import { safeGetString } from "./utils";
export class CollectionExport { export class CollectionExport {
static template(): CollectionExport { static template(): CollectionExport {
const req = new CollectionExport(); const req = new CollectionExport();
@ -36,11 +38,7 @@ export class CollectionExport {
// Use build method instead of ctor so that we can control order of JSON stringify for pretty print // Use build method instead of ctor so that we can control order of JSON stringify for pretty print
build(o: CollectionView | CollectionDomain) { build(o: CollectionView | CollectionDomain) {
this.organizationId = o.organizationId; this.organizationId = o.organizationId;
if (o instanceof CollectionView) { this.name = safeGetString(o.name);
this.name = o.name;
} else {
this.name = o.name?.encryptedString;
}
this.externalId = o.externalId; this.externalId = o.externalId;
} }
} }

View File

@ -2,6 +2,8 @@ import { EncString } from "../../platform/models/domain/enc-string";
import { Fido2Credential } from "../../vault/models/domain/fido2-credential"; import { Fido2Credential } from "../../vault/models/domain/fido2-credential";
import { Fido2CredentialView } from "../../vault/models/view/fido2-credential.view"; import { Fido2CredentialView } from "../../vault/models/view/fido2-credential.view";
import { safeGetString } from "./utils";
/** /**
* Represents format of Fido2 Credentials in JSON exports. * Represents format of Fido2 Credentials in JSON exports.
*/ */
@ -99,33 +101,18 @@ export class Fido2CredentialExport {
return; return;
} }
if (o instanceof Fido2CredentialView) { this.credentialId = safeGetString(o.credentialId);
this.credentialId = o.credentialId; this.keyType = safeGetString(o.keyType);
this.keyType = o.keyType; this.keyAlgorithm = safeGetString(o.keyAlgorithm);
this.keyAlgorithm = o.keyAlgorithm; this.keyCurve = safeGetString(o.keyCurve);
this.keyCurve = o.keyCurve; this.keyValue = safeGetString(o.keyValue);
this.keyValue = o.keyValue; this.rpId = safeGetString(o.rpId);
this.rpId = o.rpId; this.userHandle = safeGetString(o.userHandle);
this.userHandle = o.userHandle; this.userName = safeGetString(o.userName);
this.userName = o.userName; this.counter = safeGetString(String(o.counter));
this.counter = String(o.counter); this.rpName = safeGetString(o.rpName);
this.rpName = o.rpName; this.userDisplayName = safeGetString(o.userDisplayName);
this.userDisplayName = o.userDisplayName; this.discoverable = safeGetString(String(o.discoverable));
this.discoverable = String(o.discoverable);
} else {
this.credentialId = o.credentialId?.encryptedString;
this.keyType = o.keyType?.encryptedString;
this.keyAlgorithm = o.keyAlgorithm?.encryptedString;
this.keyCurve = o.keyCurve?.encryptedString;
this.keyValue = o.keyValue?.encryptedString;
this.rpId = o.rpId?.encryptedString;
this.userHandle = o.userHandle?.encryptedString;
this.userName = o.userName?.encryptedString;
this.counter = o.counter?.encryptedString;
this.rpName = o.rpName?.encryptedString;
this.userDisplayName = o.userDisplayName?.encryptedString;
this.discoverable = o.discoverable?.encryptedString;
}
this.creationDate = o.creationDate; this.creationDate = o.creationDate;
} }
} }

View File

@ -3,6 +3,8 @@ import { FieldType, LinkedIdType } from "../../vault/enums";
import { Field as FieldDomain } from "../../vault/models/domain/field"; import { Field as FieldDomain } from "../../vault/models/domain/field";
import { FieldView } from "../../vault/models/view/field.view"; import { FieldView } from "../../vault/models/view/field.view";
import { safeGetString } from "./utils";
export class FieldExport { export class FieldExport {
static template(): FieldExport { static template(): FieldExport {
const req = new FieldExport(); const req = new FieldExport();
@ -38,13 +40,8 @@ export class FieldExport {
return; return;
} }
if (o instanceof FieldView) { this.name = safeGetString(o.name);
this.name = o.name; this.value = safeGetString(o.value);
this.value = o.value;
} else {
this.name = o.name?.encryptedString;
this.value = o.value?.encryptedString;
}
this.type = o.type; this.type = o.type;
this.linkedId = o.linkedId; this.linkedId = o.linkedId;
} }

View File

@ -2,6 +2,8 @@ import { EncString } from "../../platform/models/domain/enc-string";
import { Folder as FolderDomain } from "../../vault/models/domain/folder"; import { Folder as FolderDomain } from "../../vault/models/domain/folder";
import { FolderView } from "../../vault/models/view/folder.view"; import { FolderView } from "../../vault/models/view/folder.view";
import { safeGetString } from "./utils";
export class FolderExport { export class FolderExport {
static template(): FolderExport { static template(): FolderExport {
const req = new FolderExport(); const req = new FolderExport();
@ -23,10 +25,6 @@ export class FolderExport {
// Use build method instead of ctor so that we can control order of JSON stringify for pretty print // Use build method instead of ctor so that we can control order of JSON stringify for pretty print
build(o: FolderView | FolderDomain) { build(o: FolderView | FolderDomain) {
if (o instanceof FolderView) { this.name = safeGetString(o.name);
this.name = o.name;
} else {
this.name = o.name?.encryptedString;
}
} }
} }

View File

@ -2,6 +2,8 @@ import { EncString } from "../../platform/models/domain/enc-string";
import { Identity as IdentityDomain } from "../../vault/models/domain/identity"; import { Identity as IdentityDomain } from "../../vault/models/domain/identity";
import { IdentityView } from "../../vault/models/view/identity.view"; import { IdentityView } from "../../vault/models/view/identity.view";
import { safeGetString } from "./utils";
export class IdentityExport { export class IdentityExport {
static template(): IdentityExport { static template(): IdentityExport {
const req = new IdentityExport(); const req = new IdentityExport();
@ -94,44 +96,23 @@ export class IdentityExport {
return; return;
} }
if (o instanceof IdentityView) { this.title = safeGetString(o.title);
this.title = o.title; this.firstName = safeGetString(o.firstName);
this.firstName = o.firstName; this.middleName = safeGetString(o.middleName);
this.middleName = o.middleName; this.lastName = safeGetString(o.lastName);
this.lastName = o.lastName; this.address1 = safeGetString(o.address1);
this.address1 = o.address1; this.address2 = safeGetString(o.address2);
this.address2 = o.address2; this.address3 = safeGetString(o.address3);
this.address3 = o.address3; this.city = safeGetString(o.city);
this.city = o.city; this.state = safeGetString(o.state);
this.state = o.state; this.postalCode = safeGetString(o.postalCode);
this.postalCode = o.postalCode; this.country = safeGetString(o.country);
this.country = o.country; this.company = safeGetString(o.company);
this.company = o.company; this.email = safeGetString(o.email);
this.email = o.email; this.phone = safeGetString(o.phone);
this.phone = o.phone; this.ssn = safeGetString(o.ssn);
this.ssn = o.ssn; this.username = safeGetString(o.username);
this.username = o.username; this.passportNumber = safeGetString(o.passportNumber);
this.passportNumber = o.passportNumber; this.licenseNumber = safeGetString(o.licenseNumber);
this.licenseNumber = o.licenseNumber;
} else {
this.title = o.title?.encryptedString;
this.firstName = o.firstName?.encryptedString;
this.middleName = o.middleName?.encryptedString;
this.lastName = o.lastName?.encryptedString;
this.address1 = o.address1?.encryptedString;
this.address2 = o.address2?.encryptedString;
this.address3 = o.address3?.encryptedString;
this.city = o.city?.encryptedString;
this.state = o.state?.encryptedString;
this.postalCode = o.postalCode?.encryptedString;
this.country = o.country?.encryptedString;
this.company = o.company?.encryptedString;
this.email = o.email?.encryptedString;
this.phone = o.phone?.encryptedString;
this.ssn = o.ssn?.encryptedString;
this.username = o.username?.encryptedString;
this.passportNumber = o.passportNumber?.encryptedString;
this.licenseNumber = o.licenseNumber?.encryptedString;
}
} }
} }

View File

@ -3,6 +3,8 @@ import { EncString } from "../../platform/models/domain/enc-string";
import { LoginUri as LoginUriDomain } from "../../vault/models/domain/login-uri"; import { LoginUri as LoginUriDomain } from "../../vault/models/domain/login-uri";
import { LoginUriView } from "../../vault/models/view/login-uri.view"; import { LoginUriView } from "../../vault/models/view/login-uri.view";
import { safeGetString } from "./utils";
export class LoginUriExport { export class LoginUriExport {
static template(): LoginUriExport { static template(): LoginUriExport {
const req = new LoginUriExport(); const req = new LoginUriExport();
@ -33,10 +35,8 @@ export class LoginUriExport {
return; return;
} }
if (o instanceof LoginUriView) { this.uri = safeGetString(o.uri);
this.uri = o.uri; if ("uriChecksum" in o) {
} else {
this.uri = o.uri?.encryptedString;
this.uriChecksum = o.uriChecksum?.encryptedString; this.uriChecksum = o.uriChecksum?.encryptedString;
} }
this.match = o.match; this.match = o.match;

View File

@ -4,6 +4,7 @@ import { LoginView } from "../../vault/models/view/login.view";
import { Fido2CredentialExport } from "./fido2-credential.export"; import { Fido2CredentialExport } from "./fido2-credential.export";
import { LoginUriExport } from "./login-uri.export"; import { LoginUriExport } from "./login-uri.export";
import { safeGetString } from "./utils";
export class LoginExport { export class LoginExport {
static template(): LoginExport { static template(): LoginExport {
@ -53,25 +54,15 @@ export class LoginExport {
} }
if (o.uris != null) { if (o.uris != null) {
if (o instanceof LoginView) { this.uris = o.uris.map((u) => new LoginUriExport(u));
this.uris = o.uris.map((u) => new LoginUriExport(u));
} else {
this.uris = o.uris.map((u) => new LoginUriExport(u));
}
} }
if (o.fido2Credentials != null) { if (o.fido2Credentials != null) {
this.fido2Credentials = o.fido2Credentials.map((key) => new Fido2CredentialExport(key)); this.fido2Credentials = o.fido2Credentials.map((key) => new Fido2CredentialExport(key));
} }
if (o instanceof LoginView) { this.username = safeGetString(o.username);
this.username = o.username; this.password = safeGetString(o.password);
this.password = o.password; this.totp = safeGetString(o.totp);
this.totp = o.totp;
} else {
this.username = o.username?.encryptedString;
this.password = o.password?.encryptedString;
this.totp = o.totp?.encryptedString;
}
} }
} }

View File

@ -2,6 +2,8 @@ import { EncString } from "../../platform/models/domain/enc-string";
import { Password } from "../../vault/models/domain/password"; import { Password } from "../../vault/models/domain/password";
import { PasswordHistoryView } from "../../vault/models/view/password-history.view"; import { PasswordHistoryView } from "../../vault/models/view/password-history.view";
import { safeGetString } from "./utils";
export class PasswordHistoryExport { export class PasswordHistoryExport {
static template(): PasswordHistoryExport { static template(): PasswordHistoryExport {
const req = new PasswordHistoryExport(); const req = new PasswordHistoryExport();
@ -30,11 +32,7 @@ export class PasswordHistoryExport {
return; return;
} }
if (o instanceof PasswordHistoryView) { this.password = safeGetString(o.password);
this.password = o.password;
} else {
this.password = o.password?.encryptedString;
}
this.lastUsedDate = o.lastUsedDate; this.lastUsedDate = o.lastUsedDate;
} }
} }

View File

@ -0,0 +1,12 @@
import { EncString } from "@bitwarden/common/platform/models/domain/enc-string";
export function safeGetString(value: string | EncString) {
if (value == null) {
return null;
}
if (typeof value == "string") {
return value;
}
return value?.encryptedString;
}

View File

@ -181,8 +181,6 @@ export abstract class StateService<T extends Account = Account> {
* Sets the user's Pin, encrypted by the user key * Sets the user's Pin, encrypted by the user key
*/ */
setProtectedPin: (value: string, options?: StorageOptions) => Promise<void>; setProtectedPin: (value: string, options?: StorageOptions) => Promise<void>;
getSecurityStamp: (options?: StorageOptions) => Promise<string>;
setSecurityStamp: (value: string, options?: StorageOptions) => Promise<void>;
getUserId: (options?: StorageOptions) => Promise<string>; getUserId: (options?: StorageOptions) => Promise<string>;
getVaultTimeout: (options?: StorageOptions) => Promise<number>; getVaultTimeout: (options?: StorageOptions) => Promise<number>;
setVaultTimeout: (value: number, options?: StorageOptions) => Promise<void>; setVaultTimeout: (value: number, options?: StorageOptions) => Promise<void>;

View File

@ -1,9 +0,0 @@
import { AccountTokens } from "./account";
describe("AccountTokens", () => {
describe("fromJSON", () => {
it("should deserialize to an instance of itself", () => {
expect(AccountTokens.fromJSON({})).toBeInstanceOf(AccountTokens);
});
});
});

View File

@ -1,4 +1,4 @@
import { Account, AccountKeys, AccountProfile, AccountSettings, AccountTokens } from "./account"; import { Account, AccountKeys, AccountProfile, AccountSettings } from "./account";
describe("Account", () => { describe("Account", () => {
describe("fromJSON", () => { describe("fromJSON", () => {
@ -10,14 +10,12 @@ describe("Account", () => {
const keysSpy = jest.spyOn(AccountKeys, "fromJSON"); const keysSpy = jest.spyOn(AccountKeys, "fromJSON");
const profileSpy = jest.spyOn(AccountProfile, "fromJSON"); const profileSpy = jest.spyOn(AccountProfile, "fromJSON");
const settingsSpy = jest.spyOn(AccountSettings, "fromJSON"); const settingsSpy = jest.spyOn(AccountSettings, "fromJSON");
const tokensSpy = jest.spyOn(AccountTokens, "fromJSON");
Account.fromJSON({}); Account.fromJSON({});
expect(keysSpy).toHaveBeenCalled(); expect(keysSpy).toHaveBeenCalled();
expect(profileSpy).toHaveBeenCalled(); expect(profileSpy).toHaveBeenCalled();
expect(settingsSpy).toHaveBeenCalled(); expect(settingsSpy).toHaveBeenCalled();
expect(tokensSpy).toHaveBeenCalled();
}); });
}); });
}); });

View File

@ -171,24 +171,11 @@ export class AccountSettings {
} }
} }
export class AccountTokens {
securityStamp?: string;
static fromJSON(obj: Jsonify<AccountTokens>): AccountTokens {
if (obj == null) {
return null;
}
return Object.assign(new AccountTokens(), obj);
}
}
export class Account { export class Account {
data?: AccountData = new AccountData(); data?: AccountData = new AccountData();
keys?: AccountKeys = new AccountKeys(); keys?: AccountKeys = new AccountKeys();
profile?: AccountProfile = new AccountProfile(); profile?: AccountProfile = new AccountProfile();
settings?: AccountSettings = new AccountSettings(); settings?: AccountSettings = new AccountSettings();
tokens?: AccountTokens = new AccountTokens();
constructor(init: Partial<Account>) { constructor(init: Partial<Account>) {
Object.assign(this, { Object.assign(this, {
@ -208,10 +195,6 @@ export class Account {
...new AccountSettings(), ...new AccountSettings(),
...init?.settings, ...init?.settings,
}, },
tokens: {
...new AccountTokens(),
...init?.tokens,
},
}); });
} }
@ -225,7 +208,6 @@ export class Account {
data: AccountData.fromJSON(json?.data), data: AccountData.fromJSON(json?.data),
profile: AccountProfile.fromJSON(json?.profile), profile: AccountProfile.fromJSON(json?.profile),
settings: AccountSettings.fromJSON(json?.settings), settings: AccountSettings.fromJSON(json?.settings),
tokens: AccountTokens.fromJSON(json?.tokens),
}); });
} }
} }

View File

@ -65,8 +65,6 @@ export class StateService<
private hasBeenInited = false; private hasBeenInited = false;
protected isRecoveredSession = false; protected isRecoveredSession = false;
protected accountDiskCache = new BehaviorSubject<Record<string, TAccount>>({});
// default account serializer, must be overridden by child class // default account serializer, must be overridden by child class
protected accountDeserializer = Account.fromJSON as (json: Jsonify<TAccount>) => TAccount; protected accountDeserializer = Account.fromJSON as (json: Jsonify<TAccount>) => TAccount;
@ -80,7 +78,6 @@ export class StateService<
protected environmentService: EnvironmentService, protected environmentService: EnvironmentService,
protected tokenService: TokenService, protected tokenService: TokenService,
private migrationRunner: MigrationRunner, private migrationRunner: MigrationRunner,
protected useAccountCache: boolean = true,
) {} ) {}
async init(initOptions: InitOptions = {}): Promise<void> { async init(initOptions: InitOptions = {}): Promise<void> {
@ -844,23 +841,6 @@ export class StateService<
); );
} }
async getSecurityStamp(options?: StorageOptions): Promise<string> {
return (
await this.getAccount(this.reconcileOptions(options, await this.defaultInMemoryOptions()))
)?.tokens?.securityStamp;
}
async setSecurityStamp(value: string, options?: StorageOptions): Promise<void> {
const account = await this.getAccount(
this.reconcileOptions(options, await this.defaultInMemoryOptions()),
);
account.tokens.securityStamp = value;
await this.saveAccount(
account,
this.reconcileOptions(options, await this.defaultInMemoryOptions()),
);
}
async getUserId(options?: StorageOptions): Promise<string> { async getUserId(options?: StorageOptions): Promise<string> {
return ( return (
await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskOptions())) await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskOptions()))
@ -995,13 +975,6 @@ export class StateService<
return null; return null;
} }
if (this.useAccountCache) {
const cachedAccount = this.accountDiskCache.value[options.userId];
if (cachedAccount != null) {
return cachedAccount;
}
}
const account = options?.useSecureStorage const account = options?.useSecureStorage
? (await this.secureStorageService.get<TAccount>(options.userId, options)) ?? ? (await this.secureStorageService.get<TAccount>(options.userId, options)) ??
(await this.storageService.get<TAccount>( (await this.storageService.get<TAccount>(
@ -1009,8 +982,6 @@ export class StateService<
this.reconcileOptions(options, { htmlStorageLocation: HtmlStorageLocation.Local }), this.reconcileOptions(options, { htmlStorageLocation: HtmlStorageLocation.Local }),
)) ))
: await this.storageService.get<TAccount>(options.userId, options); : await this.storageService.get<TAccount>(options.userId, options);
this.setDiskCache(options.userId, account);
return account; return account;
} }
@ -1040,8 +1011,6 @@ export class StateService<
: this.storageService; : this.storageService;
await storageLocation.save(`${options.userId}`, account, options); await storageLocation.save(`${options.userId}`, account, options);
this.deleteDiskCache(options.userId);
} }
protected async saveAccountToMemory(account: TAccount): Promise<void> { protected async saveAccountToMemory(account: TAccount): Promise<void> {
@ -1241,9 +1210,6 @@ export class StateService<
await this.updateState(async (state) => { await this.updateState(async (state) => {
userId = userId ?? state.activeUserId; userId = userId ?? state.activeUserId;
delete state.accounts[userId]; delete state.accounts[userId];
this.deleteDiskCache(userId);
return state; return state;
}); });
} }
@ -1357,20 +1323,6 @@ export class StateService<
return await this.setState(updatedState); return await this.setState(updatedState);
}); });
} }
private setDiskCache(key: string, value: TAccount, options?: StorageOptions) {
if (this.useAccountCache) {
this.accountDiskCache.value[key] = value;
this.accountDiskCache.next(this.accountDiskCache.value);
}
}
protected deleteDiskCache(key: string) {
if (this.useAccountCache) {
delete this.accountDiskCache.value[key];
this.accountDiskCache.next(this.accountDiskCache.value);
}
}
} }
function withPrototypeForArrayMembers<T>( function withPrototypeForArrayMembers<T>(

View File

@ -49,7 +49,7 @@ import { AddKeyTypeToOrgKeysMigrator } from "./migrations/5-add-key-type-to-org-
import { KeyConnectorMigrator } from "./migrations/50-move-key-connector-to-state-provider"; import { KeyConnectorMigrator } from "./migrations/50-move-key-connector-to-state-provider";
import { RememberedEmailMigrator } from "./migrations/51-move-remembered-email-to-state-providers"; import { RememberedEmailMigrator } from "./migrations/51-move-remembered-email-to-state-providers";
import { DeleteInstalledVersion } from "./migrations/52-delete-installed-version"; import { DeleteInstalledVersion } from "./migrations/52-delete-installed-version";
import { DeviceTrustCryptoServiceStateProviderMigrator } from "./migrations/53-migrate-device-trust-crypto-svc-to-state-providers"; import { DeviceTrustServiceStateProviderMigrator } from "./migrations/53-migrate-device-trust-svc-to-state-providers";
import { SendMigrator } from "./migrations/54-move-encrypted-sends"; import { SendMigrator } from "./migrations/54-move-encrypted-sends";
import { MoveMasterKeyStateToProviderMigrator } from "./migrations/55-move-master-key-state-to-provider"; import { MoveMasterKeyStateToProviderMigrator } from "./migrations/55-move-master-key-state-to-provider";
import { AuthRequestMigrator } from "./migrations/56-move-auth-requests"; import { AuthRequestMigrator } from "./migrations/56-move-auth-requests";
@ -117,7 +117,7 @@ export function createMigrationBuilder() {
.with(KeyConnectorMigrator, 49, 50) .with(KeyConnectorMigrator, 49, 50)
.with(RememberedEmailMigrator, 50, 51) .with(RememberedEmailMigrator, 50, 51)
.with(DeleteInstalledVersion, 51, 52) .with(DeleteInstalledVersion, 51, 52)
.with(DeviceTrustCryptoServiceStateProviderMigrator, 52, 53) .with(DeviceTrustServiceStateProviderMigrator, 52, 53)
.with(SendMigrator, 53, 54) .with(SendMigrator, 53, 54)
.with(MoveMasterKeyStateToProviderMigrator, 54, 55) .with(MoveMasterKeyStateToProviderMigrator, 54, 55)
.with(AuthRequestMigrator, 55, 56) .with(AuthRequestMigrator, 55, 56)

View File

@ -5,9 +5,9 @@ import { mockMigrationHelper } from "../migration-helper.spec";
import { import {
DEVICE_KEY, DEVICE_KEY,
DeviceTrustCryptoServiceStateProviderMigrator, DeviceTrustServiceStateProviderMigrator,
SHOULD_TRUST_DEVICE, SHOULD_TRUST_DEVICE,
} from "./53-migrate-device-trust-crypto-svc-to-state-providers"; } from "./53-migrate-device-trust-svc-to-state-providers";
// Represents data in state service pre-migration // Represents data in state service pre-migration
function preMigrationJson() { function preMigrationJson() {
@ -79,14 +79,14 @@ function rollbackJSON() {
}; };
} }
describe("DeviceTrustCryptoServiceStateProviderMigrator", () => { describe("DeviceTrustServiceStateProviderMigrator", () => {
let helper: MockProxy<MigrationHelper>; let helper: MockProxy<MigrationHelper>;
let sut: DeviceTrustCryptoServiceStateProviderMigrator; let sut: DeviceTrustServiceStateProviderMigrator;
describe("migrate", () => { describe("migrate", () => {
beforeEach(() => { beforeEach(() => {
helper = mockMigrationHelper(preMigrationJson(), 52); helper = mockMigrationHelper(preMigrationJson(), 52);
sut = new DeviceTrustCryptoServiceStateProviderMigrator(52, 53); sut = new DeviceTrustServiceStateProviderMigrator(52, 53);
}); });
// it should remove deviceKey and trustDeviceChoiceForDecryption from all accounts // it should remove deviceKey and trustDeviceChoiceForDecryption from all accounts
@ -126,7 +126,7 @@ describe("DeviceTrustCryptoServiceStateProviderMigrator", () => {
describe("rollback", () => { describe("rollback", () => {
beforeEach(() => { beforeEach(() => {
helper = mockMigrationHelper(rollbackJSON(), 53); helper = mockMigrationHelper(rollbackJSON(), 53);
sut = new DeviceTrustCryptoServiceStateProviderMigrator(52, 53); sut = new DeviceTrustServiceStateProviderMigrator(52, 53);
}); });
it("should null out newly migrated entries in state provider framework", async () => { it("should null out newly migrated entries in state provider framework", async () => {

View File

@ -16,7 +16,7 @@ type ExpectedAccountType = {
}; };
export const DEVICE_KEY: KeyDefinitionLike = { export const DEVICE_KEY: KeyDefinitionLike = {
key: "deviceKey", // matches KeyDefinition.key in DeviceTrustCryptoService key: "deviceKey", // matches KeyDefinition.key in DeviceTrustService
stateDefinition: { stateDefinition: {
name: "deviceTrust", // matches StateDefinition.name in StateDefinitions name: "deviceTrust", // matches StateDefinition.name in StateDefinitions
}, },
@ -29,7 +29,7 @@ export const SHOULD_TRUST_DEVICE: KeyDefinitionLike = {
}, },
}; };
export class DeviceTrustCryptoServiceStateProviderMigrator extends Migrator<52, 53> { export class DeviceTrustServiceStateProviderMigrator extends Migrator<52, 53> {
async migrate(helper: MigrationHelper): Promise<void> { async migrate(helper: MigrationHelper): Promise<void> {
const accounts = await helper.getAccounts<ExpectedAccountType>(); const accounts = await helper.getAccounts<ExpectedAccountType>();
async function migrateAccount(userId: string, account: ExpectedAccountType): Promise<void> { async function migrateAccount(userId: string, account: ExpectedAccountType): Promise<void> {

View File

@ -4,7 +4,7 @@ import { IRREVERSIBLE, Migrator } from "../migrator";
type ExpectedAccountType = NonNullable<unknown>; type ExpectedAccountType = NonNullable<unknown>;
export const REFRESH_TOKEN_MIGRATED_TO_SECURE_STORAGE: KeyDefinitionLike = { export const REFRESH_TOKEN_MIGRATED_TO_SECURE_STORAGE: KeyDefinitionLike = {
key: "refreshTokenMigratedToSecureStorage", // matches KeyDefinition.key in DeviceTrustCryptoService key: "refreshTokenMigratedToSecureStorage", // matches KeyDefinition.key
stateDefinition: { stateDefinition: {
name: "token", // matches StateDefinition.name in StateDefinitions name: "token", // matches StateDefinition.name in StateDefinitions
}, },

View File

@ -15,6 +15,7 @@ import { AccountService } from "../../../auth/abstractions/account.service";
import { AvatarService } from "../../../auth/abstractions/avatar.service"; import { AvatarService } from "../../../auth/abstractions/avatar.service";
import { KeyConnectorService } from "../../../auth/abstractions/key-connector.service"; import { KeyConnectorService } from "../../../auth/abstractions/key-connector.service";
import { InternalMasterPasswordServiceAbstraction } from "../../../auth/abstractions/master-password.service.abstraction"; import { InternalMasterPasswordServiceAbstraction } from "../../../auth/abstractions/master-password.service.abstraction";
import { TokenService } from "../../../auth/abstractions/token.service";
import { ForceSetPasswordReason } from "../../../auth/models/domain/force-set-password-reason"; import { ForceSetPasswordReason } from "../../../auth/models/domain/force-set-password-reason";
import { DomainSettingsService } from "../../../autofill/services/domain-settings.service"; import { DomainSettingsService } from "../../../autofill/services/domain-settings.service";
import { BillingAccountProfileStateService } from "../../../billing/abstractions/account/billing-account-profile-state.service"; import { BillingAccountProfileStateService } from "../../../billing/abstractions/account/billing-account-profile-state.service";
@ -73,6 +74,7 @@ export class SyncService implements SyncServiceAbstraction {
private avatarService: AvatarService, private avatarService: AvatarService,
private logoutCallback: (expired: boolean) => Promise<void>, private logoutCallback: (expired: boolean) => Promise<void>,
private billingAccountProfileStateService: BillingAccountProfileStateService, private billingAccountProfileStateService: BillingAccountProfileStateService,
private tokenService: TokenService,
) {} ) {}
async getLastSync(): Promise<Date> { async getLastSync(): Promise<Date> {
@ -309,7 +311,7 @@ export class SyncService implements SyncServiceAbstraction {
} }
private async syncProfile(response: ProfileResponse) { private async syncProfile(response: ProfileResponse) {
const stamp = await this.stateService.getSecurityStamp(); const stamp = await this.tokenService.getSecurityStamp(response.id as UserId);
if (stamp != null && stamp !== response.securityStamp) { if (stamp != null && stamp !== response.securityStamp) {
if (this.logoutCallback != null) { if (this.logoutCallback != null) {
await this.logoutCallback(true); await this.logoutCallback(true);
@ -323,7 +325,7 @@ export class SyncService implements SyncServiceAbstraction {
await this.cryptoService.setProviderKeys(response.providers); await this.cryptoService.setProviderKeys(response.providers);
await this.cryptoService.setOrgKeys(response.organizations, response.providerOrganizations); await this.cryptoService.setOrgKeys(response.organizations, response.providerOrganizations);
await this.avatarService.setSyncAvatarColor(response.id as UserId, response.avatarColor); await this.avatarService.setSyncAvatarColor(response.id as UserId, response.avatarColor);
await this.stateService.setSecurityStamp(response.securityStamp); await this.tokenService.setSecurityStamp(response.securityStamp, response.id as UserId);
await this.stateService.setEmailVerified(response.emailVerified); await this.stateService.setEmailVerified(response.emailVerified);
await this.billingAccountProfileStateService.setHasPremium( await this.billingAccountProfileStateService.setHasPremium(

View File

@ -0,0 +1,15 @@
import { CommonModule } from "@angular/common";
import { ChangeDetectionStrategy, Component } from "@angular/core";
@Component({
selector: "bit-card",
standalone: true,
imports: [CommonModule],
template: `<ng-content></ng-content>`,
changeDetection: ChangeDetectionStrategy.OnPush,
host: {
class:
"tw-box-border tw-block tw-bg-background tw-text-main tw-border-solid tw-border-b tw-border-0 tw-border-b-secondary-300 tw-rounded-lg tw-py-4 tw-px-3",
},
})
export class CardComponent {}

View File

@ -0,0 +1,62 @@
import { Meta, StoryObj, componentWrapperDecorator, moduleMetadata } from "@storybook/angular";
import { SectionComponent } from "../section";
import { TypographyModule } from "../typography";
import { CardComponent } from "./card.component";
export default {
title: "Component Library/Card",
component: CardComponent,
decorators: [
moduleMetadata({
imports: [TypographyModule, SectionComponent],
}),
componentWrapperDecorator(
(story) => `<div class="tw-bg-background-alt tw-p-10 tw-text-main">${story}</div>`,
),
],
} as Meta;
type Story = StoryObj<CardComponent>;
/** Cards are presentational containers. */
export const Default: Story = {
render: (args) => ({
props: args,
template: /*html*/ `
<bit-card>
<p bitTypography="body1" class="!tw-mb-0">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras vitae congue risus. Interdum et malesuada fames ac ante ipsum primis in faucibus. Nunc elementum odio nibh, eget pellentesque sem ornare vitae. Etiam vel ante et velit fringilla egestas a sed sem. Fusce molestie nisl et nisi accumsan dapibus. Interdum et malesuada fames ac ante ipsum primis in faucibus. Sed eu risus ex. </p>
</bit-card>
`,
}),
};
/** Cards are often paired with [Sections](/docs/component-library-section--docs). */
export const WithinSections: Story = {
render: (args) => ({
props: args,
template: /*html*/ `
<bit-section>
<h2 bitTypography="h5">Bar</h2>
<bit-card>
<p bitTypography="body1" class="!tw-mb-0">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras vitae congue risus. Interdum et malesuada fames ac ante ipsum primis in faucibus. Nunc elementum odio nibh, eget pellentesque sem ornare vitae. Etiam vel ante et velit fringilla egestas a sed sem. Fusce molestie nisl et nisi accumsan dapibus. Interdum et malesuada fames ac ante ipsum primis in faucibus. Sed eu risus ex. </p>
</bit-card>
</bit-section>
<bit-section>
<h2 bitTypography="h5">Bar</h2>
<bit-card>
<p bitTypography="body1" class="!tw-mb-0">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras vitae congue risus. Interdum et malesuada fames ac ante ipsum primis in faucibus. Nunc elementum odio nibh, eget pellentesque sem ornare vitae. Etiam vel ante et velit fringilla egestas a sed sem. Fusce molestie nisl et nisi accumsan dapibus. Interdum et malesuada fames ac ante ipsum primis in faucibus. Sed eu risus ex. </p>
</bit-card>
</bit-section>
<bit-section>
<h2 bitTypography="h5">Bar</h2>
<bit-card>
<p bitTypography="body1" class="!tw-mb-0">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras vitae congue risus. Interdum et malesuada fames ac ante ipsum primis in faucibus. Nunc elementum odio nibh, eget pellentesque sem ornare vitae. Etiam vel ante et velit fringilla egestas a sed sem. Fusce molestie nisl et nisi accumsan dapibus. Interdum et malesuada fames ac ante ipsum primis in faucibus. Sed eu risus ex. </p>
</bit-card>
</bit-section>
`,
}),
};

View File

@ -0,0 +1 @@
export * from "./card.component";

View File

@ -7,6 +7,7 @@ export * from "./breadcrumbs";
export * from "./button"; export * from "./button";
export { ButtonType } from "./shared/button-like.abstraction"; export { ButtonType } from "./shared/button-like.abstraction";
export * from "./callout"; export * from "./callout";
export * from "./card";
export * from "./checkbox"; export * from "./checkbox";
export * from "./color-password"; export * from "./color-password";
export * from "./container"; export * from "./container";

View File

@ -6,7 +6,7 @@ import { Component } from "@angular/core";
standalone: true, standalone: true,
imports: [CommonModule], imports: [CommonModule],
template: ` template: `
<section class="tw-mb-12"> <section class="tw-mb-6 md:tw-mb-12">
<ng-content></ng-content> <ng-content></ng-content>
</section> </section>
`, `,

View File

@ -17,7 +17,7 @@ export default {
type Story = StoryObj<SectionComponent>; type Story = StoryObj<SectionComponent>;
/** Sections are simple containers that apply a bottom margin. They often contain a heading. */ /** Sections are simple containers that apply a responsive bottom margin. They often contain a heading. */
export const Default: Story = { export const Default: Story = {
render: (args) => ({ render: (args) => ({
props: args, props: args,