diff --git a/apps/browser/src/background/main.background.ts b/apps/browser/src/background/main.background.ts index d427c2f4bd6..ac27caa1e23 100644 --- a/apps/browser/src/background/main.background.ts +++ b/apps/browser/src/background/main.background.ts @@ -733,6 +733,7 @@ export default class MainBackground { this.badgeSettingsService = new BadgeSettingsService(this.stateProvider); this.policyApiService = new PolicyApiService(this.policyService, this.apiService); this.keyConnectorService = new KeyConnectorService( + this.accountService, this.masterPasswordService, this.keyService, this.apiService, @@ -742,6 +743,7 @@ export default class MainBackground { this.keyGenerationService, logoutCallback, this.stateProvider, + this.messagingService, ); const sdkClientFactory = flagEnabled("sdk") diff --git a/apps/cli/src/service-container/service-container.ts b/apps/cli/src/service-container/service-container.ts index 7fb6782e7e9..131866d722f 100644 --- a/apps/cli/src/service-container/service-container.ts +++ b/apps/cli/src/service-container/service-container.ts @@ -563,6 +563,7 @@ export class ServiceContainer { this.policyApiService = new PolicyApiService(this.policyService, this.apiService); this.keyConnectorService = new KeyConnectorService( + this.accountService, this.masterPasswordService, this.keyService, this.apiService, @@ -572,6 +573,7 @@ export class ServiceContainer { this.keyGenerationService, logoutCallback, this.stateProvider, + this.messagingService, ); this.twoFactorService = new TwoFactorService( diff --git a/libs/angular/src/services/jslib-services.module.ts b/libs/angular/src/services/jslib-services.module.ts index b8a943edbc0..887efd74059 100644 --- a/libs/angular/src/services/jslib-services.module.ts +++ b/libs/angular/src/services/jslib-services.module.ts @@ -977,6 +977,7 @@ const safeProviders: SafeProvider[] = [ provide: KeyConnectorServiceAbstraction, useClass: KeyConnectorService, deps: [ + AccountServiceAbstraction, InternalMasterPasswordServiceAbstraction, KeyService, ApiServiceAbstraction, @@ -986,6 +987,7 @@ const safeProviders: SafeProvider[] = [ KeyGenerationServiceAbstraction, LOGOUT_CALLBACK, StateProvider, + MessagingServiceAbstraction, ], }), safeProvider({ diff --git a/libs/common/src/key-management/key-connector/services/key-connector.service.spec.ts b/libs/common/src/key-management/key-connector/services/key-connector.service.spec.ts index bf2ea5f2e0f..21df1cbbd69 100644 --- a/libs/common/src/key-management/key-connector/services/key-connector.service.spec.ts +++ b/libs/common/src/key-management/key-connector/services/key-connector.service.spec.ts @@ -3,6 +3,7 @@ import { firstValueFrom, of } from "rxjs"; import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; import { OrganizationUserType } from "@bitwarden/common/admin-console/enums"; +import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service"; import { KeyService } from "@bitwarden/key-management"; import { FakeAccountService, FakeStateProvider, mockAccountServiceWith } from "../../../../spec"; @@ -36,6 +37,7 @@ describe("KeyConnectorService", () => { const logService = mock(); const organizationService = mock(); const keyGenerationService = mock(); + const messagingService = mock(); let stateProvider: FakeStateProvider; @@ -57,6 +59,7 @@ describe("KeyConnectorService", () => { stateProvider = new FakeStateProvider(accountService); keyConnectorService = new KeyConnectorService( + accountService, masterPasswordService, keyService, apiService, @@ -66,6 +69,7 @@ describe("KeyConnectorService", () => { keyGenerationService, async () => {}, stateProvider, + messagingService, ); }); diff --git a/libs/common/src/key-management/key-connector/services/key-connector.service.ts b/libs/common/src/key-management/key-connector/services/key-connector.service.ts index a593566a0c5..e5da277f556 100644 --- a/libs/common/src/key-management/key-connector/services/key-connector.service.ts +++ b/libs/common/src/key-management/key-connector/services/key-connector.service.ts @@ -1,8 +1,10 @@ // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore -import { firstValueFrom } from "rxjs"; +import { combineLatest, filter, firstValueFrom, of, switchMap } from "rxjs"; import { LogoutReason } from "@bitwarden/auth/common"; +import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; +import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service"; import { Argon2KdfConfig, KdfConfig, @@ -58,6 +60,7 @@ export class KeyConnectorService implements KeyConnectorServiceAbstraction { private convertAccountToKeyConnectorState: ActiveUserState; constructor( + accountService: AccountService, private masterPasswordService: InternalMasterPasswordServiceAbstraction, private keyService: KeyService, private apiService: ApiService, @@ -67,11 +70,40 @@ export class KeyConnectorService implements KeyConnectorServiceAbstraction { private keyGenerationService: KeyGenerationService, private logoutCallback: (logoutReason: LogoutReason, userId?: string) => Promise, private stateProvider: StateProvider, + private messagingService: MessagingService, ) { this.usesKeyConnectorState = this.stateProvider.getActive(USES_KEY_CONNECTOR); this.convertAccountToKeyConnectorState = this.stateProvider.getActive( CONVERT_ACCOUNT_TO_KEY_CONNECTOR, ); + + accountService.activeAccount$ + .pipe( + filter((account) => account != null), + switchMap((account) => + combineLatest([ + of(account.id), + this.organizationService + .organizations$(account.id) + .pipe(filter((organizations) => organizations != null)), + tokenService.hasAccessToken$(account.id).pipe(filter((hasToken) => hasToken)), + ]), + ), + switchMap(async ([userId, organizations]) => { + const needsMigration = await this.userNeedsMigration(userId, organizations); + if (needsMigration) { + await this.setConvertAccountRequired(true, userId); + } else { + await this.removeConvertAccountRequired(userId); + } + return needsMigration; + }), + ) + .subscribe((needsMigration) => { + if (needsMigration) { + this.messagingService.send("convertAccountToKeyConnector"); + } + }); } async setUsesKeyConnector(usesKeyConnector: boolean, userId: UserId) { @@ -84,7 +116,7 @@ export class KeyConnectorService implements KeyConnectorServiceAbstraction { ); } - async userNeedsMigration(userId: UserId, organizations: Organization[]) { + async userNeedsMigration(userId: UserId, organizations: Organization[]): Promise { const loggedInUsingSso = await this.tokenService.getIsExternal(userId); const requiredByOrganization = this.findManagingOrganization(organizations) != null; const userIsNotUsingKeyConnector = !(await this.getUsesKeyConnector(userId)); diff --git a/libs/common/src/platform/sync/default-sync.service.ts b/libs/common/src/platform/sync/default-sync.service.ts index dd4d7b82b49..8b551678e2e 100644 --- a/libs/common/src/platform/sync/default-sync.service.ts +++ b/libs/common/src/platform/sync/default-sync.service.ts @@ -7,7 +7,6 @@ import { CollectionData, CollectionDetailsResponse, } from "@bitwarden/admin-console/common"; -import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; import { KeyService } from "@bitwarden/key-management"; // FIXME: remove `src` and fix import @@ -214,14 +213,7 @@ export class DefaultSyncService extends CoreSyncService { await this.providerService.save(providers, response.id); - const organizations = await this.syncProfileOrganizations(response, response.id); - - if (await this.keyConnectorService.userNeedsMigration(response.id, organizations)) { - await this.keyConnectorService.setConvertAccountRequired(true, response.id); - this.messageSender.send("convertAccountToKeyConnector"); - } else { - await this.keyConnectorService.removeConvertAccountRequired(response.id); - } + await this.syncProfileOrganizations(response, response.id); } private async setForceSetPasswordReasonIfNeeded(profileResponse: ProfileResponse) { @@ -292,10 +284,6 @@ export class DefaultSyncService extends CoreSyncService { }); await this.organizationService.replace(organizations, userId); - - return Object.values(organizations).map( - (organizationData) => new Organization(organizationData), - ); } private async syncFolders(response: FolderResponse[], userId: UserId) {