From 012ce25e49a33ce81ed5e6c7ed4276c9fe033503 Mon Sep 17 00:00:00 2001 From: Brandon Treston Date: Tue, 24 Jun 2025 09:34:48 -0400 Subject: [PATCH] add encrypted collection name to confirmUser request (#15156) --- .../members/members.component.ts | 29 +++++--- .../organization-user.service.ts | 69 +++++++++++++++++++ .../organization-user-confirm.request.ts | 7 +- libs/common/src/enums/feature-flag.enum.ts | 2 + 4 files changed, 95 insertions(+), 12 deletions(-) create mode 100644 apps/web/src/app/admin-console/organizations/members/services/organization-user/organization-user.service.ts diff --git a/apps/web/src/app/admin-console/organizations/members/members.component.ts b/apps/web/src/app/admin-console/organizations/members/members.component.ts index 4f453762b5d..49c57f5e5a6 100644 --- a/apps/web/src/app/admin-console/organizations/members/members.component.ts +++ b/apps/web/src/app/admin-console/organizations/members/members.component.ts @@ -83,6 +83,7 @@ import { ResetPasswordDialogResult, } from "./components/reset-password.component"; import { DeleteManagedMemberWarningService } from "./services/delete-managed-member/delete-managed-member-warning.service"; +import { OrganizationUserService } from "./services/organization-user/organization-user.service"; class MembersTableDataSource extends PeopleTableDataSource { protected statusType = OrganizationUserStatusType; @@ -141,6 +142,7 @@ export class MembersComponent extends BaseMembersComponent private billingApiService: BillingApiServiceAbstraction, protected deleteManagedMemberWarningService: DeleteManagedMemberWarningService, private configService: ConfigService, + private organizationUserService: OrganizationUserService, ) { super( apiService, @@ -327,15 +329,24 @@ export class MembersComponent extends BaseMembersComponent } async confirmUser(user: OrganizationUserView, publicKey: Uint8Array): Promise { - const orgKey = await this.keyService.getOrgKey(this.organization.id); - const key = await this.encryptService.encapsulateKeyUnsigned(orgKey, publicKey); - const request = new OrganizationUserConfirmRequest(); - request.key = key.encryptedString; - await this.organizationUserApiService.postOrganizationUserConfirm( - this.organization.id, - user.id, - request, - ); + if ( + await firstValueFrom(this.configService.getFeatureFlag$(FeatureFlag.CreateDefaultLocation)) + ) { + this.organizationUserService + .confirmUser(this.organization, user, publicKey) + .pipe(takeUntilDestroyed()) + .subscribe(); + } else { + const orgKey = await this.keyService.getOrgKey(this.organization.id); + const key = await this.encryptService.encapsulateKeyUnsigned(orgKey, publicKey); + const request = new OrganizationUserConfirmRequest(); + request.key = key.encryptedString; + await this.organizationUserApiService.postOrganizationUserConfirm( + this.organization.id, + user.id, + request, + ); + } } async revoke(user: OrganizationUserView) { diff --git a/apps/web/src/app/admin-console/organizations/members/services/organization-user/organization-user.service.ts b/apps/web/src/app/admin-console/organizations/members/services/organization-user/organization-user.service.ts new file mode 100644 index 00000000000..31dfa865005 --- /dev/null +++ b/apps/web/src/app/admin-console/organizations/members/services/organization-user/organization-user.service.ts @@ -0,0 +1,69 @@ +import { Injectable } from "@angular/core"; +import { combineLatest, filter, map, Observable, switchMap } from "rxjs"; + +import { + OrganizationUserConfirmRequest, + OrganizationUserApiService, +} from "@bitwarden/admin-console/common"; +import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; +import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; +import { getUserId } from "@bitwarden/common/auth/services/account.service"; +import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; +import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; +import { OrganizationId } from "@bitwarden/common/types/guid"; +import { KeyService } from "@bitwarden/key-management"; + +import { OrganizationUserView } from "../../../core/views/organization-user.view"; + +@Injectable({ + providedIn: "root", +}) +export class OrganizationUserService { + constructor( + protected keyService: KeyService, + private encryptService: EncryptService, + private organizationUserApiService: OrganizationUserApiService, + private accountService: AccountService, + private i18nService: I18nService, + ) {} + + private orgKey$(organization: Organization) { + return this.accountService.activeAccount$.pipe( + getUserId, + switchMap((userId) => this.keyService.orgKeys$(userId)), + filter((orgKeys) => !!orgKeys), + map((organizationKeysById) => organizationKeysById[organization.id as OrganizationId]), + ); + } + + confirmUser( + organization: Organization, + user: OrganizationUserView, + publicKey: Uint8Array, + ): Observable { + const encryptedCollectionName$ = this.orgKey$(organization).pipe( + switchMap((orgKey) => + this.encryptService.encryptString(this.i18nService.t("My Itmes"), orgKey), + ), + ); + + const encryptedKey$ = this.orgKey$(organization).pipe( + switchMap((orgKey) => this.encryptService.encapsulateKeyUnsigned(orgKey, publicKey)), + ); + + return combineLatest([encryptedKey$, encryptedCollectionName$]).pipe( + switchMap(([key, collectionName]) => { + const request: OrganizationUserConfirmRequest = { + key: key.encryptedString, + defaultUserCollectionName: collectionName.encryptedString, + }; + + return this.organizationUserApiService.postOrganizationUserConfirm( + organization.id, + user.id, + request, + ); + }), + ); + } +} diff --git a/libs/admin-console/src/common/organization-user/models/requests/organization-user-confirm.request.ts b/libs/admin-console/src/common/organization-user/models/requests/organization-user-confirm.request.ts index 62988801424..104ea7fd472 100644 --- a/libs/admin-console/src/common/organization-user/models/requests/organization-user-confirm.request.ts +++ b/libs/admin-console/src/common/organization-user/models/requests/organization-user-confirm.request.ts @@ -1,5 +1,6 @@ -// FIXME: Update this file to be type safe and remove this and next line -// @ts-strict-ignore +import { EncryptedString } from "@bitwarden/common/platform/models/domain/enc-string"; + export class OrganizationUserConfirmRequest { - key: string; + key: EncryptedString | undefined; + defaultUserCollectionName: EncryptedString | undefined; } diff --git a/libs/common/src/enums/feature-flag.enum.ts b/libs/common/src/enums/feature-flag.enum.ts index 85821c6fe90..df2ee88877d 100644 --- a/libs/common/src/enums/feature-flag.enum.ts +++ b/libs/common/src/enums/feature-flag.enum.ts @@ -13,6 +13,7 @@ export enum FeatureFlag { /* Admin Console Team */ SeparateCustomRolePermissions = "pm-19917-separate-custom-role-permissions", OptimizeNestedTraverseTypescript = "pm-21695-optimize-nested-traverse-typescript", + CreateDefaultLocation = "pm-19467-create-default-location", /* Auth */ PM16117_SetInitialPasswordRefactor = "pm-16117-set-initial-password-refactor", @@ -77,6 +78,7 @@ export const DefaultFeatureFlagValue = { /* Admin Console Team */ [FeatureFlag.SeparateCustomRolePermissions]: FALSE, [FeatureFlag.OptimizeNestedTraverseTypescript]: FALSE, + [FeatureFlag.CreateDefaultLocation]: FALSE, /* Autofill */ [FeatureFlag.BlockBrowserInjectionsByDomain]: FALSE,