mirror of
https://github.com/bitwarden/browser
synced 2026-02-06 19:53:59 +00:00
Merge branch 'main' into PM-29919-Add-dropdown-to-select-email-verification-and-emails-field-to-Send-when-creating-or-editing-a-Send
This commit is contained in:
@@ -264,6 +264,13 @@ export abstract class OrganizationUserApiService {
|
||||
ids: string[],
|
||||
): Promise<ListResponse<OrganizationUserBulkResponse>>;
|
||||
|
||||
/**
|
||||
* 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<void>;
|
||||
|
||||
/**
|
||||
* Restore an organization user's access to the organization
|
||||
* @param organizationId - Identifier for the organization the user belongs to
|
||||
|
||||
@@ -339,6 +339,16 @@ export class DefaultOrganizationUserApiService implements OrganizationUserApiSer
|
||||
return new ListResponse(r, OrganizationUserBulkResponse);
|
||||
}
|
||||
|
||||
revokeSelf(organizationId: string): Promise<void> {
|
||||
return this.apiService.send(
|
||||
"PUT",
|
||||
"/organizations/" + organizationId + "/users/revoke-self",
|
||||
null,
|
||||
true,
|
||||
false,
|
||||
);
|
||||
}
|
||||
|
||||
restoreOrganizationUser(organizationId: string, id: string): Promise<void> {
|
||||
return this.apiService.send(
|
||||
"PUT",
|
||||
|
||||
@@ -280,8 +280,7 @@ export abstract class KeyService {
|
||||
* encrypted private key at all.
|
||||
*
|
||||
* @param userId The user id of the user to get the data for.
|
||||
* @returns An observable stream of the decrypted private key or null.
|
||||
* @throws Error when decryption of the encrypted private key fails.
|
||||
* @returns An observable stream of the decrypted private key or null if the private key is not present or fails to decrypt
|
||||
*/
|
||||
abstract userPrivateKey$(userId: UserId): Observable<UserPrivateKey | null>;
|
||||
|
||||
|
||||
@@ -437,14 +437,13 @@ describe("keyService", () => {
|
||||
);
|
||||
});
|
||||
|
||||
it("throws an error if unwrapping encrypted private key fails", async () => {
|
||||
it("emits null if unwrapping encrypted private key fails", async () => {
|
||||
encryptService.unwrapDecapsulationKey.mockImplementationOnce(() => {
|
||||
throw new Error("Unwrapping failed");
|
||||
});
|
||||
|
||||
await expect(firstValueFrom(keyService.userPrivateKey$(mockUserId))).rejects.toThrow(
|
||||
"Unwrapping failed",
|
||||
);
|
||||
const result = await firstValueFrom(keyService.userPrivateKey$(mockUserId));
|
||||
expect(result).toBeNull();
|
||||
});
|
||||
|
||||
it("returns null if user key is not set", async () => {
|
||||
|
||||
@@ -791,7 +791,10 @@ export class DefaultKeyService implements KeyServiceAbstraction {
|
||||
return this.stateProvider.getUser(userId, USER_ENCRYPTED_PRIVATE_KEY).state$;
|
||||
}
|
||||
|
||||
private userPrivateKeyHelper$(userId: UserId) {
|
||||
private userPrivateKeyHelper$(userId: UserId): Observable<{
|
||||
userKey: UserKey;
|
||||
userPrivateKey: UserPrivateKey | null;
|
||||
} | null> {
|
||||
const userKey$ = this.userKey$(userId);
|
||||
return userKey$.pipe(
|
||||
switchMap((userKey) => {
|
||||
@@ -801,18 +804,20 @@ export class DefaultKeyService implements KeyServiceAbstraction {
|
||||
|
||||
return this.stateProvider.getUser(userId, USER_ENCRYPTED_PRIVATE_KEY).state$.pipe(
|
||||
switchMap(async (encryptedPrivateKey) => {
|
||||
try {
|
||||
return await this.decryptPrivateKey(encryptedPrivateKey, userKey);
|
||||
} catch (e) {
|
||||
this.logService.error("Failed to decrypt private key for user ", userId, e);
|
||||
throw e;
|
||||
}
|
||||
return await this.decryptPrivateKey(encryptedPrivateKey, userKey);
|
||||
}),
|
||||
// Combine outerscope info with user private key
|
||||
map((userPrivateKey) => ({
|
||||
userKey,
|
||||
userPrivateKey,
|
||||
})),
|
||||
catchError((err: unknown) => {
|
||||
this.logService.error(`Failed to decrypt private key for user ${userId}`);
|
||||
return of({
|
||||
userKey,
|
||||
userPrivateKey: null,
|
||||
});
|
||||
}),
|
||||
);
|
||||
}),
|
||||
);
|
||||
|
||||
@@ -1,17 +1,8 @@
|
||||
import { CommonModule } from "@angular/common";
|
||||
import {
|
||||
ChangeDetectionStrategy,
|
||||
Component,
|
||||
computed,
|
||||
effect,
|
||||
inject,
|
||||
input,
|
||||
output,
|
||||
} from "@angular/core";
|
||||
import { ChangeDetectionStrategy, Component, computed, effect, input, output } from "@angular/core";
|
||||
|
||||
import { JslibModule } from "@bitwarden/angular/jslib.module";
|
||||
import { NoResults, NoSendsIcon } from "@bitwarden/assets/svg";
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import { SendView } from "@bitwarden/common/tools/send/models/view/send.view";
|
||||
import {
|
||||
ButtonModule,
|
||||
@@ -57,8 +48,6 @@ export class SendListComponent {
|
||||
protected readonly noResultsIcon = NoResults;
|
||||
protected readonly sendListState = SendListState;
|
||||
|
||||
private i18nService = inject(I18nService);
|
||||
|
||||
readonly sends = input.required<SendView[]>();
|
||||
readonly loading = input<boolean>(false);
|
||||
readonly disableSend = input<boolean>(false);
|
||||
@@ -70,7 +59,7 @@ export class SendListComponent {
|
||||
);
|
||||
|
||||
protected readonly noSearchResults = computed(
|
||||
() => this.showSearchBar() && (this.sends().length === 0 || this.searchText().length > 0),
|
||||
() => this.showSearchBar() && this.sends().length === 0,
|
||||
);
|
||||
|
||||
// Reusable data source instance - updated reactively when sends change
|
||||
|
||||
@@ -41,7 +41,8 @@ export class ArchiveCipherUtilitiesService {
|
||||
|
||||
const confirmed = await this.dialogService.openSimpleDialog({
|
||||
title: { key: "archiveItem" },
|
||||
content: { key: "archiveItemConfirmDesc" },
|
||||
content: { key: "archiveItemDialogContent" },
|
||||
acceptButtonText: { key: "archiveVerb" },
|
||||
type: "info",
|
||||
});
|
||||
|
||||
|
||||
@@ -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<ToastService>;
|
||||
let mockEventCollectionService: MockProxy<EventCollectionService>;
|
||||
let mockConfigService: MockProxy<ConfigService>;
|
||||
let mockOrganizationUserApiService: MockProxy<OrganizationUserApiService>;
|
||||
|
||||
const userId = "user-id" as UserId;
|
||||
const organizationId = "org-id" as OrganizationId;
|
||||
@@ -77,6 +78,7 @@ describe("DefaultVaultItemsTransferService", () => {
|
||||
mockToastService = mock<ToastService>();
|
||||
mockEventCollectionService = mock<EventCollectionService>();
|
||||
mockConfigService = mock<ConfigService>();
|
||||
mockOrganizationUserApiService = mock<OrganizationUserApiService>();
|
||||
|
||||
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();
|
||||
});
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user