From 7fa436f66755c924f983632e145a321bca92ecc4 Mon Sep 17 00:00:00 2001 From: SmithThe4th Date: Mon, 19 Jan 2026 10:55:45 -0500 Subject: [PATCH] [PM-29465] Call new organization self revoke endpoint upon rejecting item transfer (#18352) * Added self revoke and toast to the transfer item service * Renamed to a clearer name * Update organization-user-api.service.ts Co-authored-by: Thomas Rittson <31796059+eliykat@users.noreply.github.com> * fixed merge conflcits --------- Co-authored-by: Thomas Rittson <31796059+eliykat@users.noreply.github.com> --- .../abstractions/organization-user-api.service.ts | 7 +++++++ .../services/default-organization-user-api.service.ts | 10 ++++++++++ .../default-vault-items-transfer.service.spec.ts | 11 ++++++++++- .../services/default-vault-items-transfer.service.ts | 10 ++++++++-- 4 files changed, 35 insertions(+), 3 deletions(-) diff --git a/libs/admin-console/src/common/organization-user/abstractions/organization-user-api.service.ts b/libs/admin-console/src/common/organization-user/abstractions/organization-user-api.service.ts index 71d228ff822..cbaece1b442 100644 --- a/libs/admin-console/src/common/organization-user/abstractions/organization-user-api.service.ts +++ b/libs/admin-console/src/common/organization-user/abstractions/organization-user-api.service.ts @@ -264,6 +264,13 @@ export abstract class OrganizationUserApiService { ids: string[], ): Promise>; + /** + * Revoke the current user's access to the organization + * if they decline an item transfer under the Organization Data Ownership policy. + * @param organizationId - Identifier for the organization the user belongs to + */ + abstract revokeSelf(organizationId: string): Promise; + /** * Restore an organization user's access to the organization * @param organizationId - Identifier for the organization the user belongs to diff --git a/libs/admin-console/src/common/organization-user/services/default-organization-user-api.service.ts b/libs/admin-console/src/common/organization-user/services/default-organization-user-api.service.ts index 869d84a8c8e..536afd2b3f6 100644 --- a/libs/admin-console/src/common/organization-user/services/default-organization-user-api.service.ts +++ b/libs/admin-console/src/common/organization-user/services/default-organization-user-api.service.ts @@ -339,6 +339,16 @@ export class DefaultOrganizationUserApiService implements OrganizationUserApiSer return new ListResponse(r, OrganizationUserBulkResponse); } + revokeSelf(organizationId: string): Promise { + return this.apiService.send( + "PUT", + "/organizations/" + organizationId + "/users/revoke-self", + null, + true, + false, + ); + } + restoreOrganizationUser(organizationId: string, id: string): Promise { return this.apiService.send( "PUT", diff --git a/libs/vault/src/services/default-vault-items-transfer.service.spec.ts b/libs/vault/src/services/default-vault-items-transfer.service.spec.ts index 4ad9c53c6f0..51154c3cee9 100644 --- a/libs/vault/src/services/default-vault-items-transfer.service.spec.ts +++ b/libs/vault/src/services/default-vault-items-transfer.service.spec.ts @@ -2,7 +2,7 @@ import { mock, MockProxy } from "jest-mock-extended"; import { firstValueFrom, of, Subject } from "rxjs"; // eslint-disable-next-line no-restricted-imports -import { CollectionService } from "@bitwarden/admin-console/common"; +import { CollectionService, OrganizationUserApiService } from "@bitwarden/admin-console/common"; import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service"; import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; @@ -42,6 +42,7 @@ describe("DefaultVaultItemsTransferService", () => { let mockToastService: MockProxy; let mockEventCollectionService: MockProxy; let mockConfigService: MockProxy; + let mockOrganizationUserApiService: MockProxy; const userId = "user-id" as UserId; const organizationId = "org-id" as OrganizationId; @@ -77,6 +78,7 @@ describe("DefaultVaultItemsTransferService", () => { mockToastService = mock(); mockEventCollectionService = mock(); mockConfigService = mock(); + mockOrganizationUserApiService = mock(); mockI18nService.t.mockImplementation((key) => key); transferInProgressValues = []; @@ -92,6 +94,7 @@ describe("DefaultVaultItemsTransferService", () => { mockToastService, mockEventCollectionService, mockConfigService, + mockOrganizationUserApiService, ); }); @@ -632,9 +635,15 @@ describe("DefaultVaultItemsTransferService", () => { mockDialogService.open .mockReturnValueOnce(createMockDialogRef(TransferItemsDialogResult.Declined)) .mockReturnValueOnce(createMockDialogRef(LeaveConfirmationDialogResult.Confirmed)); + mockOrganizationUserApiService.revokeSelf.mockResolvedValue(undefined); await service.enforceOrganizationDataOwnership(userId); + expect(mockOrganizationUserApiService.revokeSelf).toHaveBeenCalledWith(organizationId); + expect(mockToastService.showToast).toHaveBeenCalledWith({ + variant: "success", + message: "leftOrganization", + }); expect(mockCipherService.shareManyWithServer).not.toHaveBeenCalled(); }); diff --git a/libs/vault/src/services/default-vault-items-transfer.service.ts b/libs/vault/src/services/default-vault-items-transfer.service.ts index c184b2c902e..6009fc97e7c 100644 --- a/libs/vault/src/services/default-vault-items-transfer.service.ts +++ b/libs/vault/src/services/default-vault-items-transfer.service.ts @@ -10,7 +10,7 @@ import { } from "rxjs"; // eslint-disable-next-line no-restricted-imports -import { CollectionService } from "@bitwarden/admin-console/common"; +import { CollectionService, OrganizationUserApiService } from "@bitwarden/admin-console/common"; import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service"; import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; @@ -53,6 +53,7 @@ export class DefaultVaultItemsTransferService implements VaultItemsTransferServi private toastService: ToastService, private eventCollectionService: EventCollectionService, private configService: ConfigService, + private organizationUserApiService: OrganizationUserApiService, ) {} private _transferInProgressSubject = new BehaviorSubject(false); @@ -162,7 +163,12 @@ export class DefaultVaultItemsTransferService implements VaultItemsTransferServi ); if (!userAcceptedTransfer) { - // TODO: Revoke user from organization if they decline migration and show toast PM-29465 + await this.organizationUserApiService.revokeSelf(migrationInfo.enforcingOrganization.id); + + this.toastService.showToast({ + variant: "success", + message: this.i18nService.t("leftOrganization"), + }); await this.eventCollectionService.collect( EventType.Organization_ItemOrganization_Declined,