mirror of
https://github.com/bitwarden/browser
synced 2025-12-06 00:13:28 +00:00
[PM-21451] [Vault] [CLI] Changes to Enforce "Remove card item type policy" (#15187)
* Created new service to get restricted types for the CLI * Created service for cli to get restricted types * Utilized restriction service in commands * Renamed function * Refactored service and made it simpler to check when a cipher type is restricted or not * Moved service to common so it can be utilized on the cli * Refactored service to use restricted type service * Removed userId passing from commands * Exclude restrict types from export * Added missing dependency * Added missing dependency * Added missing dependency * Added service utils commit from desktop PR * refactored to use reusable function * updated reference * updated reference * Fixed merge conflicts * Refactired services to use isCipherRestricted * Refactored restricted item types service * Updated services to use the reafctored item types service
This commit is contained in:
@@ -25,6 +25,10 @@ import { AttachmentView } from "@bitwarden/common/vault/models/view/attachment.v
|
||||
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
|
||||
import { FolderView } from "@bitwarden/common/vault/models/view/folder.view";
|
||||
import { LoginView } from "@bitwarden/common/vault/models/view/login.view";
|
||||
import {
|
||||
RestrictedCipherType,
|
||||
RestrictedItemTypesService,
|
||||
} from "@bitwarden/common/vault/services/restricted-item-types.service";
|
||||
import {
|
||||
DEFAULT_KDF_CONFIG,
|
||||
PBKDF2KdfConfig,
|
||||
@@ -170,6 +174,8 @@ describe("VaultExportService", () => {
|
||||
let kdfConfigService: MockProxy<KdfConfigService>;
|
||||
let accountService: MockProxy<AccountService>;
|
||||
let apiService: MockProxy<ApiService>;
|
||||
let restrictedSubject: BehaviorSubject<RestrictedCipherType[]>;
|
||||
let restrictedItemTypesService: Partial<RestrictedItemTypesService>;
|
||||
let fetchMock: jest.Mock;
|
||||
|
||||
const userId = "" as UserId;
|
||||
@@ -186,6 +192,12 @@ describe("VaultExportService", () => {
|
||||
apiService = mock<ApiService>();
|
||||
|
||||
keyService.userKey$.mockReturnValue(new BehaviorSubject("mockOriginalUserKey" as any));
|
||||
restrictedSubject = new BehaviorSubject<RestrictedCipherType[]>([]);
|
||||
restrictedItemTypesService = {
|
||||
restricted$: new BehaviorSubject<RestrictedCipherType[]>([]),
|
||||
isCipherRestricted: jest.fn().mockReturnValue(false),
|
||||
isCipherRestricted$: jest.fn().mockReturnValue(of(false)),
|
||||
};
|
||||
|
||||
const accountInfo: AccountInfo = {
|
||||
email: "",
|
||||
@@ -223,6 +235,7 @@ describe("VaultExportService", () => {
|
||||
kdfConfigService,
|
||||
accountService,
|
||||
apiService,
|
||||
restrictedItemTypesService as RestrictedItemTypesService,
|
||||
);
|
||||
});
|
||||
|
||||
@@ -262,6 +275,46 @@ describe("VaultExportService", () => {
|
||||
expectEqualCiphers(UserCipherDomains.slice(0, 2), exportedData.data);
|
||||
});
|
||||
|
||||
it("does not unencrypted export restricted user items", async () => {
|
||||
restrictedSubject.next([{ cipherType: CipherType.Card, allowViewOrgIds: [] }]);
|
||||
const cardCipher = generateCipherView(false);
|
||||
cardCipher.type = CipherType.Card;
|
||||
|
||||
(restrictedItemTypesService.isCipherRestricted as jest.Mock)
|
||||
.mockReturnValueOnce(false)
|
||||
.mockReturnValueOnce(true) // cardCipher - restricted
|
||||
.mockReturnValueOnce(false);
|
||||
|
||||
const testCiphers = [UserCipherViews[0], cardCipher, UserCipherViews[1]];
|
||||
cipherService.getAllDecrypted.mockResolvedValue(testCiphers);
|
||||
|
||||
const actual = await exportService.getExport("json");
|
||||
expect(typeof actual.data).toBe("string");
|
||||
const exportedData = actual as ExportedVaultAsString;
|
||||
|
||||
expectEqualCiphers([UserCipherViews[0], UserCipherViews[1]], exportedData.data);
|
||||
});
|
||||
|
||||
it("does not encrypted export restricted user items", async () => {
|
||||
restrictedSubject.next([{ cipherType: CipherType.Card, allowViewOrgIds: [] }]);
|
||||
const cardCipher = generateCipherDomain(false);
|
||||
cardCipher.type = CipherType.Card;
|
||||
|
||||
(restrictedItemTypesService.isCipherRestricted as jest.Mock)
|
||||
.mockReturnValueOnce(false)
|
||||
.mockReturnValueOnce(true) // cardCipher - restricted
|
||||
.mockReturnValueOnce(false);
|
||||
|
||||
const testCiphers = [UserCipherDomains[0], cardCipher, UserCipherDomains[1]];
|
||||
cipherService.getAll.mockResolvedValue(testCiphers);
|
||||
|
||||
const actual = await exportService.getExport("encrypted_json");
|
||||
expect(typeof actual.data).toBe("string");
|
||||
const exportedData = actual as ExportedVaultAsString;
|
||||
|
||||
expectEqualCiphers([UserCipherDomains[0], UserCipherDomains[1]], exportedData.data);
|
||||
});
|
||||
|
||||
describe("zip export", () => {
|
||||
it("contains data.json", async () => {
|
||||
cipherService.getAllDecrypted.mockResolvedValue([]);
|
||||
|
||||
@@ -20,6 +20,7 @@ import { Cipher } from "@bitwarden/common/vault/models/domain/cipher";
|
||||
import { Folder } from "@bitwarden/common/vault/models/domain/folder";
|
||||
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
|
||||
import { FolderView } from "@bitwarden/common/vault/models/view/folder.view";
|
||||
import { RestrictedItemTypesService } from "@bitwarden/common/vault/services/restricted-item-types.service";
|
||||
import { KdfConfigService, KeyService } from "@bitwarden/key-management";
|
||||
|
||||
import {
|
||||
@@ -50,6 +51,7 @@ export class IndividualVaultExportService
|
||||
kdfConfigService: KdfConfigService,
|
||||
private accountService: AccountService,
|
||||
private apiService: ApiService,
|
||||
private restrictedItemTypesService: RestrictedItemTypesService,
|
||||
) {
|
||||
super(pinService, encryptService, cryptoFunctionService, kdfConfigService);
|
||||
}
|
||||
@@ -169,9 +171,15 @@ export class IndividualVaultExportService
|
||||
}),
|
||||
);
|
||||
|
||||
const restrictions = await firstValueFrom(this.restrictedItemTypesService.restricted$);
|
||||
|
||||
promises.push(
|
||||
this.cipherService.getAllDecrypted(activeUserId).then((ciphers) => {
|
||||
decCiphers = ciphers.filter((f) => f.deletedDate == null);
|
||||
decCiphers = ciphers.filter(
|
||||
(f) =>
|
||||
f.deletedDate == null &&
|
||||
!this.restrictedItemTypesService.isCipherRestricted(f, restrictions),
|
||||
);
|
||||
}),
|
||||
);
|
||||
|
||||
@@ -203,9 +211,15 @@ export class IndividualVaultExportService
|
||||
}),
|
||||
);
|
||||
|
||||
const restrictions = await firstValueFrom(this.restrictedItemTypesService.restricted$);
|
||||
|
||||
promises.push(
|
||||
this.cipherService.getAll(activeUserId).then((c) => {
|
||||
ciphers = c.filter((f) => f.deletedDate == null);
|
||||
ciphers = c.filter(
|
||||
(f) =>
|
||||
f.deletedDate == null &&
|
||||
!this.restrictedItemTypesService.isCipherRestricted(f, restrictions),
|
||||
);
|
||||
}),
|
||||
);
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ import { CipherType } from "@bitwarden/common/vault/enums";
|
||||
import { CipherData } from "@bitwarden/common/vault/models/data/cipher.data";
|
||||
import { Cipher } from "@bitwarden/common/vault/models/domain/cipher";
|
||||
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
|
||||
import { RestrictedItemTypesService } from "@bitwarden/common/vault/services/restricted-item-types.service";
|
||||
import { KdfConfigService, KeyService } from "@bitwarden/key-management";
|
||||
|
||||
import {
|
||||
@@ -52,6 +53,7 @@ export class OrganizationVaultExportService
|
||||
private collectionService: CollectionService,
|
||||
kdfConfigService: KdfConfigService,
|
||||
private accountService: AccountService,
|
||||
private restrictedItemTypesService: RestrictedItemTypesService,
|
||||
) {
|
||||
super(pinService, encryptService, cryptoFunctionService, kdfConfigService);
|
||||
}
|
||||
@@ -133,6 +135,8 @@ export class OrganizationVaultExportService
|
||||
const decCiphers: CipherView[] = [];
|
||||
const promises = [];
|
||||
|
||||
const restrictions = await firstValueFrom(this.restrictedItemTypesService.restricted$);
|
||||
|
||||
promises.push(
|
||||
this.apiService.getOrganizationExport(organizationId).then((exportData) => {
|
||||
const exportPromises: any = [];
|
||||
@@ -156,7 +160,11 @@ export class OrganizationVaultExportService
|
||||
const cipher = new Cipher(new CipherData(c));
|
||||
exportPromises.push(
|
||||
this.cipherService.decrypt(cipher, activeUserId).then((decCipher) => {
|
||||
decCiphers.push(decCipher);
|
||||
if (
|
||||
!this.restrictedItemTypesService.isCipherRestricted(decCipher, restrictions)
|
||||
) {
|
||||
decCiphers.push(decCipher);
|
||||
}
|
||||
}),
|
||||
);
|
||||
});
|
||||
@@ -176,7 +184,7 @@ export class OrganizationVaultExportService
|
||||
|
||||
private async getOrganizationEncryptedExport(organizationId: string): Promise<string> {
|
||||
const collections: Collection[] = [];
|
||||
const ciphers: Cipher[] = [];
|
||||
let ciphers: Cipher[] = [];
|
||||
const promises = [];
|
||||
|
||||
promises.push(
|
||||
@@ -190,15 +198,17 @@ export class OrganizationVaultExportService
|
||||
}),
|
||||
);
|
||||
|
||||
const restrictions = await firstValueFrom(this.restrictedItemTypesService.restricted$);
|
||||
|
||||
promises.push(
|
||||
this.apiService.getCiphersOrganization(organizationId).then((c) => {
|
||||
if (c != null && c.data != null && c.data.length > 0) {
|
||||
c.data
|
||||
ciphers = c.data
|
||||
.filter((item) => item.deletedDate === null)
|
||||
.forEach((item) => {
|
||||
const cipher = new Cipher(new CipherData(item));
|
||||
ciphers.push(cipher);
|
||||
});
|
||||
.map((item) => new Cipher(new CipherData(item)))
|
||||
.filter(
|
||||
(cipher) => !this.restrictedItemTypesService.isCipherRestricted(cipher, restrictions),
|
||||
);
|
||||
}
|
||||
}),
|
||||
);
|
||||
@@ -231,11 +241,14 @@ export class OrganizationVaultExportService
|
||||
);
|
||||
await Promise.all(promises);
|
||||
|
||||
const restrictions = await firstValueFrom(this.restrictedItemTypesService.restricted$);
|
||||
|
||||
decCiphers = allDecCiphers.filter(
|
||||
(f) =>
|
||||
f.deletedDate == null &&
|
||||
f.organizationId == organizationId &&
|
||||
decCollections.some((dC) => f.collectionIds.some((cId) => dC.id === cId)),
|
||||
decCollections.some((dC) => f.collectionIds.some((cId) => dC.id === cId)) &&
|
||||
!this.restrictedItemTypesService.isCipherRestricted(f, restrictions),
|
||||
);
|
||||
|
||||
if (format === "csv") {
|
||||
@@ -267,11 +280,14 @@ export class OrganizationVaultExportService
|
||||
|
||||
await Promise.all(promises);
|
||||
|
||||
const restrictions = await firstValueFrom(this.restrictedItemTypesService.restricted$);
|
||||
|
||||
encCiphers = allCiphers.filter(
|
||||
(f) =>
|
||||
f.deletedDate == null &&
|
||||
f.organizationId == organizationId &&
|
||||
encCollections.some((eC) => f.collectionIds.some((cId) => eC.id === cId)),
|
||||
encCollections.some((eC) => f.collectionIds.some((cId) => eC.id === cId)) &&
|
||||
!this.restrictedItemTypesService.isCipherRestricted(f, restrictions),
|
||||
);
|
||||
|
||||
return this.BuildEncryptedExport(organizationId, encCollections, encCiphers);
|
||||
|
||||
@@ -19,6 +19,10 @@ import { Login } from "@bitwarden/common/vault/models/domain/login";
|
||||
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
|
||||
import { FolderView } from "@bitwarden/common/vault/models/view/folder.view";
|
||||
import { LoginView } from "@bitwarden/common/vault/models/view/login.view";
|
||||
import {
|
||||
RestrictedCipherType,
|
||||
RestrictedItemTypesService,
|
||||
} from "@bitwarden/common/vault/services/restricted-item-types.service";
|
||||
import {
|
||||
DEFAULT_KDF_CONFIG,
|
||||
PBKDF2KdfConfig,
|
||||
@@ -159,6 +163,7 @@ describe("VaultExportService", () => {
|
||||
let accountService: MockProxy<AccountService>;
|
||||
let kdfConfigService: MockProxy<KdfConfigService>;
|
||||
let apiService: MockProxy<ApiService>;
|
||||
let restrictedItemTypesService: Partial<RestrictedItemTypesService>;
|
||||
|
||||
beforeEach(() => {
|
||||
cryptoFunctionService = mock<CryptoFunctionService>();
|
||||
@@ -186,6 +191,12 @@ describe("VaultExportService", () => {
|
||||
const activeAccount = { id: userId, ...accountInfo };
|
||||
accountService.activeAccount$ = new BehaviorSubject(activeAccount);
|
||||
|
||||
restrictedItemTypesService = {
|
||||
restricted$: new BehaviorSubject<RestrictedCipherType[]>([]),
|
||||
isCipherRestricted: jest.fn().mockReturnValue(false),
|
||||
isCipherRestricted$: jest.fn().mockReturnValue(of(false)),
|
||||
};
|
||||
|
||||
exportService = new IndividualVaultExportService(
|
||||
folderService,
|
||||
cipherService,
|
||||
@@ -196,6 +207,7 @@ describe("VaultExportService", () => {
|
||||
kdfConfigService,
|
||||
accountService,
|
||||
apiService,
|
||||
restrictedItemTypesService as RestrictedItemTypesService,
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user