mirror of
https://github.com/bitwarden/browser
synced 2025-12-19 17:53:39 +00:00
[PM-24101] Switching to use the orgKeys$ from the key service instead of getOrgKey (#15781)
* Switching to use the orgKeys$ from the key service instead of getOrgKey * Using account service instead of state provider * First try for fixing test cases * fixing test cases * PM-24101 fix identified by failing test * Error checking on the orgId * Private method did not need error check * Setting OrganizationId type * Fixing test cases for setting org id * Moving the get of critical apps to the init * The critical apps component was being set again --------- Co-authored-by: voommen-livefront <voommen@livefront.com>
This commit is contained in:
@@ -1,14 +1,13 @@
|
|||||||
import { randomUUID } from "crypto";
|
import { randomUUID } from "crypto";
|
||||||
|
|
||||||
import { fakeAsync, flush } from "@angular/core/testing";
|
|
||||||
import { mock } from "jest-mock-extended";
|
import { mock } from "jest-mock-extended";
|
||||||
import { of } from "rxjs";
|
import { of, BehaviorSubject } from "rxjs";
|
||||||
|
|
||||||
import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service";
|
import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service";
|
||||||
import { EncString } from "@bitwarden/common/key-management/crypto/models/enc-string";
|
import { EncString } from "@bitwarden/common/key-management/crypto/models/enc-string";
|
||||||
import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key";
|
import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key";
|
||||||
import { CsprngArray } from "@bitwarden/common/types/csprng";
|
import { CsprngArray } from "@bitwarden/common/types/csprng";
|
||||||
import { OrganizationId } from "@bitwarden/common/types/guid";
|
import { UserId, OrganizationId } from "@bitwarden/common/types/guid";
|
||||||
import { OrgKey } from "@bitwarden/common/types/key";
|
import { OrgKey } from "@bitwarden/common/types/key";
|
||||||
import { KeyService } from "@bitwarden/key-management";
|
import { KeyService } from "@bitwarden/key-management";
|
||||||
|
|
||||||
@@ -21,6 +20,17 @@ import {
|
|||||||
import { CriticalAppsApiService } from "./critical-apps-api.service";
|
import { CriticalAppsApiService } from "./critical-apps-api.service";
|
||||||
import { CriticalAppsService } from "./critical-apps.service";
|
import { CriticalAppsService } from "./critical-apps.service";
|
||||||
|
|
||||||
|
const SomeCsprngArray = new Uint8Array(64) as CsprngArray;
|
||||||
|
const SomeUser = "some user" as UserId;
|
||||||
|
const SomeOrganization = "some organization" as OrganizationId;
|
||||||
|
const AnotherOrganization = "another organization" as OrganizationId;
|
||||||
|
const SomeOrgKey = new SymmetricCryptoKey(SomeCsprngArray) as OrgKey;
|
||||||
|
const AnotherOrgKey = new SymmetricCryptoKey(SomeCsprngArray) as OrgKey;
|
||||||
|
const OrgRecords: Record<OrganizationId, OrgKey> = {
|
||||||
|
[SomeOrganization]: SomeOrgKey,
|
||||||
|
[AnotherOrganization]: AnotherOrgKey,
|
||||||
|
};
|
||||||
|
|
||||||
describe("CriticalAppsService", () => {
|
describe("CriticalAppsService", () => {
|
||||||
let service: CriticalAppsService;
|
let service: CriticalAppsService;
|
||||||
const keyService = mock<KeyService>();
|
const keyService = mock<KeyService>();
|
||||||
@@ -35,10 +45,6 @@ describe("CriticalAppsService", () => {
|
|||||||
|
|
||||||
// reset mocks
|
// reset mocks
|
||||||
jest.resetAllMocks();
|
jest.resetAllMocks();
|
||||||
|
|
||||||
const mockRandomBytes = new Uint8Array(64) as CsprngArray;
|
|
||||||
const mockOrgKey = new SymmetricCryptoKey(mockRandomBytes) as OrgKey;
|
|
||||||
keyService.getOrgKey.mockResolvedValue(mockOrgKey);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should be created", () => {
|
it("should be created", () => {
|
||||||
@@ -50,23 +56,27 @@ describe("CriticalAppsService", () => {
|
|||||||
const criticalApps = ["https://example.com", "https://example.org"];
|
const criticalApps = ["https://example.com", "https://example.org"];
|
||||||
|
|
||||||
const request = [
|
const request = [
|
||||||
{ organizationId: "org1", url: "encryptedUrlName" },
|
{ organizationId: SomeOrganization, url: "encryptedUrlName" },
|
||||||
{ organizationId: "org1", url: "encryptedUrlName" },
|
{ organizationId: SomeOrganization, url: "encryptedUrlName" },
|
||||||
] as PasswordHealthReportApplicationsRequest[];
|
] as PasswordHealthReportApplicationsRequest[];
|
||||||
|
|
||||||
const response = [
|
const response = [
|
||||||
{ id: "id1", organizationId: "org1", uri: "https://example.com" },
|
{ id: "id1", organizationId: SomeOrganization, uri: "https://example.com" },
|
||||||
{ id: "id2", organizationId: "org1", uri: "https://example.org" },
|
{ id: "id2", organizationId: SomeOrganization, uri: "https://example.org" },
|
||||||
] as PasswordHealthReportApplicationsResponse[];
|
] as PasswordHealthReportApplicationsResponse[];
|
||||||
|
|
||||||
encryptService.encryptString.mockResolvedValue(new EncString("encryptedUrlName"));
|
encryptService.encryptString.mockResolvedValue(new EncString("encryptedUrlName"));
|
||||||
criticalAppsApiService.saveCriticalApps.mockReturnValue(of(response));
|
criticalAppsApiService.saveCriticalApps.mockReturnValue(of(response));
|
||||||
|
const orgKey$ = new BehaviorSubject(OrgRecords);
|
||||||
|
keyService.orgKeys$.mockReturnValue(orgKey$);
|
||||||
|
|
||||||
|
service.setOrganizationId(SomeOrganization, SomeUser);
|
||||||
|
|
||||||
// act
|
// act
|
||||||
await service.setCriticalApps("org1", criticalApps);
|
await service.setCriticalApps(SomeOrganization, criticalApps);
|
||||||
|
|
||||||
// expectations
|
// expectations
|
||||||
expect(keyService.getOrgKey).toHaveBeenCalledWith("org1");
|
expect(keyService.orgKeys$).toHaveBeenCalledWith(SomeUser);
|
||||||
expect(encryptService.encryptString).toHaveBeenCalledTimes(2);
|
expect(encryptService.encryptString).toHaveBeenCalledTimes(2);
|
||||||
expect(criticalAppsApiService.saveCriticalApps).toHaveBeenCalledWith(request);
|
expect(criticalAppsApiService.saveCriticalApps).toHaveBeenCalledWith(request);
|
||||||
});
|
});
|
||||||
@@ -77,7 +87,7 @@ describe("CriticalAppsService", () => {
|
|||||||
service.setAppsInListForOrg([
|
service.setAppsInListForOrg([
|
||||||
{
|
{
|
||||||
id: randomUUID() as PasswordHealthReportApplicationId,
|
id: randomUUID() as PasswordHealthReportApplicationId,
|
||||||
organizationId: "org1" as OrganizationId,
|
organizationId: SomeOrganization,
|
||||||
uri: "https://example.com",
|
uri: "https://example.com",
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
@@ -87,59 +97,65 @@ describe("CriticalAppsService", () => {
|
|||||||
|
|
||||||
// expect only one record to be sent to the server
|
// expect only one record to be sent to the server
|
||||||
const request = [
|
const request = [
|
||||||
{ organizationId: "org1", url: "encryptedUrlName" },
|
{ organizationId: SomeOrganization, url: "encryptedUrlName" },
|
||||||
] as PasswordHealthReportApplicationsRequest[];
|
] as PasswordHealthReportApplicationsRequest[];
|
||||||
|
|
||||||
// mocked response
|
// mocked response
|
||||||
const response = [
|
const response = [
|
||||||
{ id: "id1", organizationId: "org1", uri: "test" },
|
{ id: "id1", organizationId: SomeOrganization, uri: "test" },
|
||||||
] as PasswordHealthReportApplicationsResponse[];
|
] as PasswordHealthReportApplicationsResponse[];
|
||||||
|
|
||||||
encryptService.encryptString.mockResolvedValue(new EncString("encryptedUrlName"));
|
encryptService.encryptString.mockResolvedValue(new EncString("encryptedUrlName"));
|
||||||
criticalAppsApiService.saveCriticalApps.mockReturnValue(of(response));
|
criticalAppsApiService.saveCriticalApps.mockReturnValue(of(response));
|
||||||
|
|
||||||
|
// mock org keys
|
||||||
|
const orgKey$ = new BehaviorSubject(OrgRecords);
|
||||||
|
keyService.orgKeys$.mockReturnValue(orgKey$);
|
||||||
|
|
||||||
|
service.setOrganizationId(SomeOrganization, SomeUser);
|
||||||
|
|
||||||
// act
|
// act
|
||||||
await service.setCriticalApps("org1", selectedUrls);
|
await service.setCriticalApps(SomeOrganization, selectedUrls);
|
||||||
|
|
||||||
// expectations
|
// expectations
|
||||||
expect(keyService.getOrgKey).toHaveBeenCalledWith("org1");
|
expect(keyService.orgKeys$).toHaveBeenCalledWith(SomeUser);
|
||||||
expect(encryptService.encryptString).toHaveBeenCalledTimes(1);
|
expect(encryptService.encryptString).toHaveBeenCalledTimes(1);
|
||||||
expect(criticalAppsApiService.saveCriticalApps).toHaveBeenCalledWith(request);
|
expect(criticalAppsApiService.saveCriticalApps).toHaveBeenCalledWith(request);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should get critical apps", fakeAsync(() => {
|
it("should get critical apps", () => {
|
||||||
const orgId = "org1" as OrganizationId;
|
|
||||||
const response = [
|
const response = [
|
||||||
{ id: "id1", organizationId: "org1", uri: "https://example.com" },
|
{ id: "id1", organizationId: SomeOrganization, uri: "https://example.com" },
|
||||||
{ id: "id2", organizationId: "org1", uri: "https://example.org" },
|
{ id: "id2", organizationId: SomeOrganization, uri: "https://example.org" },
|
||||||
] as PasswordHealthReportApplicationsResponse[];
|
] as PasswordHealthReportApplicationsResponse[];
|
||||||
|
|
||||||
encryptService.decryptString.mockResolvedValue("https://example.com");
|
encryptService.decryptString.mockResolvedValue("https://example.com");
|
||||||
criticalAppsApiService.getCriticalApps.mockReturnValue(of(response));
|
criticalAppsApiService.getCriticalApps.mockReturnValue(of(response));
|
||||||
|
|
||||||
const mockRandomBytes = new Uint8Array(64) as CsprngArray;
|
// mock org keys
|
||||||
const mockOrgKey = new SymmetricCryptoKey(mockRandomBytes) as OrgKey;
|
const orgKey$ = new BehaviorSubject(OrgRecords);
|
||||||
keyService.getOrgKey.mockResolvedValue(mockOrgKey);
|
keyService.orgKeys$.mockReturnValue(orgKey$);
|
||||||
|
|
||||||
service.setOrganizationId(orgId as OrganizationId);
|
service.setOrganizationId(SomeOrganization, SomeUser);
|
||||||
flush();
|
|
||||||
|
|
||||||
expect(keyService.getOrgKey).toHaveBeenCalledWith(orgId.toString());
|
expect(keyService.orgKeys$).toHaveBeenCalledWith(SomeUser);
|
||||||
expect(encryptService.decryptString).toHaveBeenCalledTimes(2);
|
expect(encryptService.decryptString).toHaveBeenCalledTimes(2);
|
||||||
expect(criticalAppsApiService.getCriticalApps).toHaveBeenCalledWith(orgId);
|
expect(criticalAppsApiService.getCriticalApps).toHaveBeenCalledWith(SomeOrganization);
|
||||||
}));
|
});
|
||||||
|
|
||||||
it("should get by org id", () => {
|
it("should get by org id", () => {
|
||||||
const orgId = "org1" as OrganizationId;
|
const orgId = "some organization" as OrganizationId;
|
||||||
const response = [
|
const response = [
|
||||||
{ id: "id1", organizationId: "org1", uri: "https://example.com" },
|
{ id: "id1", organizationId: "some organization", uri: "https://example.com" },
|
||||||
{ id: "id2", organizationId: "org1", uri: "https://example.org" },
|
{ id: "id2", organizationId: "some organization", uri: "https://example.org" },
|
||||||
{ id: "id3", organizationId: "org2", uri: "https://example.org" },
|
{ id: "id3", organizationId: "another organization", uri: "https://example.org" },
|
||||||
{ id: "id4", organizationId: "org2", uri: "https://example.org" },
|
{ id: "id4", organizationId: "another organization", uri: "https://example.org" },
|
||||||
] as PasswordHealthReportApplicationsResponse[];
|
] as PasswordHealthReportApplicationsResponse[];
|
||||||
|
|
||||||
|
const orgKey$ = new BehaviorSubject(OrgRecords);
|
||||||
|
keyService.orgKeys$.mockReturnValue(orgKey$);
|
||||||
|
service.setOrganizationId(SomeOrganization, SomeUser);
|
||||||
service.setAppsInListForOrg(response);
|
service.setAppsInListForOrg(response);
|
||||||
|
|
||||||
service.getAppsListForOrg(orgId as OrganizationId).subscribe((res) => {
|
service.getAppsListForOrg(orgId as OrganizationId).subscribe((res) => {
|
||||||
expect(res).toHaveLength(2);
|
expect(res).toHaveLength(2);
|
||||||
});
|
});
|
||||||
@@ -147,26 +163,30 @@ describe("CriticalAppsService", () => {
|
|||||||
|
|
||||||
it("should drop a critical app", async () => {
|
it("should drop a critical app", async () => {
|
||||||
// arrange
|
// arrange
|
||||||
const orgId = "org1" as OrganizationId;
|
|
||||||
const selectedUrl = "https://example.com";
|
const selectedUrl = "https://example.com";
|
||||||
|
|
||||||
const initialList = [
|
const initialList = [
|
||||||
{ id: "id1", organizationId: "org1", uri: "https://example.com" },
|
{ id: "id1", organizationId: SomeOrganization, uri: "https://example.com" },
|
||||||
{ id: "id2", organizationId: "org1", uri: "https://example.org" },
|
{ id: "id2", organizationId: SomeOrganization, uri: "https://example.org" },
|
||||||
] as PasswordHealthReportApplicationsResponse[];
|
] as PasswordHealthReportApplicationsResponse[];
|
||||||
|
|
||||||
|
const orgKey$ = new BehaviorSubject(OrgRecords);
|
||||||
|
keyService.orgKeys$.mockReturnValue(orgKey$);
|
||||||
|
|
||||||
|
service.setOrganizationId(SomeOrganization, SomeUser);
|
||||||
|
|
||||||
service.setAppsInListForOrg(initialList);
|
service.setAppsInListForOrg(initialList);
|
||||||
|
|
||||||
// act
|
// act
|
||||||
await service.dropCriticalApp(orgId, selectedUrl);
|
await service.dropCriticalApp(SomeOrganization, selectedUrl);
|
||||||
|
|
||||||
// expectations
|
// expectations
|
||||||
expect(criticalAppsApiService.dropCriticalApp).toHaveBeenCalledWith({
|
expect(criticalAppsApiService.dropCriticalApp).toHaveBeenCalledWith({
|
||||||
organizationId: orgId,
|
organizationId: SomeOrganization,
|
||||||
passwordHealthReportApplicationIds: ["id1"],
|
passwordHealthReportApplicationIds: ["id1"],
|
||||||
});
|
});
|
||||||
expect(service.getAppsListForOrg(orgId)).toBeTruthy();
|
expect(service.getAppsListForOrg(SomeOrganization)).toBeTruthy();
|
||||||
service.getAppsListForOrg(orgId).subscribe((res) => {
|
service.getAppsListForOrg(SomeOrganization).subscribe((res) => {
|
||||||
expect(res).toHaveLength(1);
|
expect(res).toHaveLength(1);
|
||||||
expect(res[0].uri).toBe("https://example.org");
|
expect(res[0].uri).toBe("https://example.org");
|
||||||
});
|
});
|
||||||
@@ -174,23 +194,27 @@ describe("CriticalAppsService", () => {
|
|||||||
|
|
||||||
it("should not drop a critical app if it does not exist", async () => {
|
it("should not drop a critical app if it does not exist", async () => {
|
||||||
// arrange
|
// arrange
|
||||||
const orgId = "org1" as OrganizationId;
|
|
||||||
const selectedUrl = "https://nonexistent.com";
|
const selectedUrl = "https://nonexistent.com";
|
||||||
|
|
||||||
const initialList = [
|
const initialList = [
|
||||||
{ id: "id1", organizationId: "org1", uri: "https://example.com" },
|
{ id: "id1", organizationId: SomeOrganization, uri: "https://example.com" },
|
||||||
{ id: "id2", organizationId: "org1", uri: "https://example.org" },
|
{ id: "id2", organizationId: SomeOrganization, uri: "https://example.org" },
|
||||||
] as PasswordHealthReportApplicationsResponse[];
|
] as PasswordHealthReportApplicationsResponse[];
|
||||||
|
|
||||||
|
const orgKey$ = new BehaviorSubject(OrgRecords);
|
||||||
|
keyService.orgKeys$.mockReturnValue(orgKey$);
|
||||||
|
|
||||||
|
service.setOrganizationId(SomeOrganization, SomeUser);
|
||||||
|
|
||||||
service.setAppsInListForOrg(initialList);
|
service.setAppsInListForOrg(initialList);
|
||||||
|
|
||||||
// act
|
// act
|
||||||
await service.dropCriticalApp(orgId, selectedUrl);
|
await service.dropCriticalApp(SomeOrganization, selectedUrl);
|
||||||
|
|
||||||
// expectations
|
// expectations
|
||||||
expect(criticalAppsApiService.dropCriticalApp).not.toHaveBeenCalled();
|
expect(criticalAppsApiService.dropCriticalApp).not.toHaveBeenCalled();
|
||||||
expect(service.getAppsListForOrg(orgId)).toBeTruthy();
|
expect(service.getAppsListForOrg(SomeOrganization)).toBeTruthy();
|
||||||
service.getAppsListForOrg(orgId).subscribe((res) => {
|
service.getAppsListForOrg(SomeOrganization).subscribe((res) => {
|
||||||
expect(res).toHaveLength(2);
|
expect(res).toHaveLength(2);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import {
|
import {
|
||||||
BehaviorSubject,
|
BehaviorSubject,
|
||||||
|
filter,
|
||||||
first,
|
first,
|
||||||
firstValueFrom,
|
firstValueFrom,
|
||||||
forkJoin,
|
forkJoin,
|
||||||
from,
|
|
||||||
map,
|
map,
|
||||||
Observable,
|
Observable,
|
||||||
of,
|
of,
|
||||||
@@ -15,7 +15,7 @@ import {
|
|||||||
|
|
||||||
import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service";
|
import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service";
|
||||||
import { EncString } from "@bitwarden/common/key-management/crypto/models/enc-string";
|
import { EncString } from "@bitwarden/common/key-management/crypto/models/enc-string";
|
||||||
import { OrganizationId } from "@bitwarden/common/types/guid";
|
import { OrganizationId, UserId } from "@bitwarden/common/types/guid";
|
||||||
import { OrgKey } from "@bitwarden/common/types/key";
|
import { OrgKey } from "@bitwarden/common/types/key";
|
||||||
import { KeyService } from "@bitwarden/key-management";
|
import { KeyService } from "@bitwarden/key-management";
|
||||||
|
|
||||||
@@ -31,6 +31,7 @@ import { CriticalAppsApiService } from "./critical-apps-api.service";
|
|||||||
*/
|
*/
|
||||||
export class CriticalAppsService {
|
export class CriticalAppsService {
|
||||||
private orgId = new BehaviorSubject<OrganizationId | null>(null);
|
private orgId = new BehaviorSubject<OrganizationId | null>(null);
|
||||||
|
private orgKey$ = new Observable<OrgKey>();
|
||||||
private criticalAppsList = new BehaviorSubject<PasswordHealthReportApplicationsResponse[]>([]);
|
private criticalAppsList = new BehaviorSubject<PasswordHealthReportApplicationsResponse[]>([]);
|
||||||
private teardown = new Subject<void>();
|
private teardown = new Subject<void>();
|
||||||
|
|
||||||
@@ -48,7 +49,11 @@ export class CriticalAppsService {
|
|||||||
) {}
|
) {}
|
||||||
|
|
||||||
// Get a list of critical apps for a given organization
|
// Get a list of critical apps for a given organization
|
||||||
getAppsListForOrg(orgId: string): Observable<PasswordHealthReportApplicationsResponse[]> {
|
getAppsListForOrg(orgId: OrganizationId): Observable<PasswordHealthReportApplicationsResponse[]> {
|
||||||
|
if (orgId != this.orgId.value) {
|
||||||
|
throw new Error("Organization ID mismatch");
|
||||||
|
}
|
||||||
|
|
||||||
return this.criticalAppsList
|
return this.criticalAppsList
|
||||||
.asObservable()
|
.asObservable()
|
||||||
.pipe(map((apps) => apps.filter((app) => app.organizationId === orgId)));
|
.pipe(map((apps) => apps.filter((app) => app.organizationId === orgId)));
|
||||||
@@ -60,17 +65,22 @@ export class CriticalAppsService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Save the selected critical apps for a given organization
|
// Save the selected critical apps for a given organization
|
||||||
async setCriticalApps(orgId: string, selectedUrls: string[]) {
|
async setCriticalApps(orgId: OrganizationId, selectedUrls: string[]) {
|
||||||
const key = await this.keyService.getOrgKey(orgId);
|
if (orgId != this.orgId.value) {
|
||||||
if (key == null) {
|
throw new Error("Organization ID mismatch");
|
||||||
|
}
|
||||||
|
|
||||||
|
const orgKey = await firstValueFrom(this.orgKey$);
|
||||||
|
|
||||||
|
if (orgKey == null) {
|
||||||
throw new Error("Organization key not found");
|
throw new Error("Organization key not found");
|
||||||
}
|
}
|
||||||
|
|
||||||
// only save records that are not already in the database
|
// only save records that are not already in the database
|
||||||
const newEntries = await this.filterNewEntries(orgId as OrganizationId, selectedUrls);
|
const newEntries = await this.filterNewEntries(orgId as OrganizationId, selectedUrls);
|
||||||
const criticalAppsRequests = await this.encryptNewEntries(
|
const criticalAppsRequests = await this.encryptNewEntries(
|
||||||
orgId as OrganizationId,
|
this.orgId.value as OrganizationId,
|
||||||
key,
|
orgKey,
|
||||||
newEntries,
|
newEntries,
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -83,7 +93,7 @@ export class CriticalAppsService {
|
|||||||
for (const responseItem of dbResponse) {
|
for (const responseItem of dbResponse) {
|
||||||
const decryptedUrl = await this.encryptService.decryptString(
|
const decryptedUrl = await this.encryptService.decryptString(
|
||||||
new EncString(responseItem.uri),
|
new EncString(responseItem.uri),
|
||||||
key,
|
orgKey,
|
||||||
);
|
);
|
||||||
if (!updatedList.some((f) => f.uri === decryptedUrl)) {
|
if (!updatedList.some((f) => f.uri === decryptedUrl)) {
|
||||||
updatedList.push({
|
updatedList.push({
|
||||||
@@ -97,13 +107,21 @@ export class CriticalAppsService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Get the critical apps for a given organization
|
// Get the critical apps for a given organization
|
||||||
setOrganizationId(orgId: OrganizationId) {
|
setOrganizationId(orgId: OrganizationId, userId: UserId) {
|
||||||
|
this.orgKey$ = this.keyService.orgKeys$(userId).pipe(
|
||||||
|
filter((OrgKeys) => !!OrgKeys),
|
||||||
|
map((organizationKeysById) => organizationKeysById[orgId as OrganizationId]),
|
||||||
|
);
|
||||||
this.orgId.next(orgId);
|
this.orgId.next(orgId);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Drop a critical app for a given organization
|
// Drop a critical app for a given organization
|
||||||
// Only one app may be dropped at a time
|
// Only one app may be dropped at a time
|
||||||
async dropCriticalApp(orgId: OrganizationId, selectedUrl: string) {
|
async dropCriticalApp(orgId: OrganizationId, selectedUrl: string) {
|
||||||
|
if (orgId != this.orgId.value) {
|
||||||
|
throw new Error("Organization ID mismatch");
|
||||||
|
}
|
||||||
|
|
||||||
const app = this.criticalAppsList.value.find(
|
const app = this.criticalAppsList.value.find(
|
||||||
(f) => f.organizationId === orgId && f.uri === selectedUrl,
|
(f) => f.organizationId === orgId && f.uri === selectedUrl,
|
||||||
);
|
);
|
||||||
@@ -127,10 +145,7 @@ export class CriticalAppsService {
|
|||||||
return of([]);
|
return of([]);
|
||||||
}
|
}
|
||||||
|
|
||||||
const result$ = zip(
|
const result$ = zip(this.criticalAppsApiService.getCriticalApps(orgId), this.orgKey$).pipe(
|
||||||
this.criticalAppsApiService.getCriticalApps(orgId),
|
|
||||||
from(this.keyService.getOrgKey(orgId)),
|
|
||||||
).pipe(
|
|
||||||
switchMap(([response, key]) => {
|
switchMap(([response, key]) => {
|
||||||
if (key == null) {
|
if (key == null) {
|
||||||
throw new Error("Organization key not found");
|
throw new Error("Organization key not found");
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ import { AccountService } from "@bitwarden/common/auth/abstractions/account.serv
|
|||||||
import { getUserId } from "@bitwarden/common/auth/services/account.service";
|
import { getUserId } from "@bitwarden/common/auth/services/account.service";
|
||||||
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
||||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||||
|
import { OrganizationId } from "@bitwarden/common/types/guid";
|
||||||
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
|
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
|
||||||
import {
|
import {
|
||||||
IconButtonModule,
|
IconButtonModule,
|
||||||
@@ -86,7 +87,7 @@ export class AllApplicationsComponent implements OnInit {
|
|||||||
|
|
||||||
combineLatest([
|
combineLatest([
|
||||||
this.dataService.applications$,
|
this.dataService.applications$,
|
||||||
this.criticalAppsService.getAppsListForOrg(organizationId),
|
this.criticalAppsService.getAppsListForOrg(organizationId as OrganizationId),
|
||||||
organization$,
|
organization$,
|
||||||
])
|
])
|
||||||
.pipe(
|
.pipe(
|
||||||
@@ -168,7 +169,7 @@ export class AllApplicationsComponent implements OnInit {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
await this.criticalAppsService.setCriticalApps(
|
await this.criticalAppsService.setCriticalApps(
|
||||||
this.organization.id,
|
this.organization.id as OrganizationId,
|
||||||
Array.from(this.selectedUrls),
|
Array.from(this.selectedUrls),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { Component, DestroyRef, inject, OnInit } from "@angular/core";
|
|||||||
import { takeUntilDestroyed } from "@angular/core/rxjs-interop";
|
import { takeUntilDestroyed } from "@angular/core/rxjs-interop";
|
||||||
import { FormControl } from "@angular/forms";
|
import { FormControl } from "@angular/forms";
|
||||||
import { ActivatedRoute, Router } from "@angular/router";
|
import { ActivatedRoute, Router } from "@angular/router";
|
||||||
import { combineLatest, debounceTime, map, switchMap } from "rxjs";
|
import { combineLatest, debounceTime, firstValueFrom, map, switchMap } from "rxjs";
|
||||||
|
|
||||||
import { Security } from "@bitwarden/assets/svg";
|
import { Security } from "@bitwarden/assets/svg";
|
||||||
import {
|
import {
|
||||||
@@ -17,6 +17,8 @@ import {
|
|||||||
ApplicationHealthReportDetailWithCriticalFlagAndCipher,
|
ApplicationHealthReportDetailWithCriticalFlagAndCipher,
|
||||||
ApplicationHealthReportSummary,
|
ApplicationHealthReportSummary,
|
||||||
} from "@bitwarden/bit-common/dirt/reports/risk-insights/models/password-health";
|
} from "@bitwarden/bit-common/dirt/reports/risk-insights/models/password-health";
|
||||||
|
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||||
|
import { getUserId } from "@bitwarden/common/auth/services/account.service";
|
||||||
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
||||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||||
import { CipherId, OrganizationId } from "@bitwarden/common/types/guid";
|
import { CipherId, OrganizationId } from "@bitwarden/common/types/guid";
|
||||||
@@ -61,10 +63,11 @@ export class CriticalApplicationsComponent implements OnInit {
|
|||||||
|
|
||||||
async ngOnInit() {
|
async ngOnInit() {
|
||||||
this.organizationId = this.activatedRoute.snapshot.paramMap.get("organizationId") ?? "";
|
this.organizationId = this.activatedRoute.snapshot.paramMap.get("organizationId") ?? "";
|
||||||
|
const userId = await firstValueFrom(getUserId(this.accountService.activeAccount$));
|
||||||
|
this.criticalAppsService.setOrganizationId(this.organizationId as OrganizationId, userId);
|
||||||
combineLatest([
|
combineLatest([
|
||||||
this.dataService.applications$,
|
this.dataService.applications$,
|
||||||
this.criticalAppsService.getAppsListForOrg(this.organizationId),
|
this.criticalAppsService.getAppsListForOrg(this.organizationId as OrganizationId),
|
||||||
])
|
])
|
||||||
.pipe(
|
.pipe(
|
||||||
takeUntilDestroyed(this.destroyRef),
|
takeUntilDestroyed(this.destroyRef),
|
||||||
@@ -168,6 +171,7 @@ export class CriticalApplicationsComponent implements OnInit {
|
|||||||
protected i18nService: I18nService,
|
protected i18nService: I18nService,
|
||||||
private configService: ConfigService,
|
private configService: ConfigService,
|
||||||
private adminTaskService: DefaultAdminTaskService,
|
private adminTaskService: DefaultAdminTaskService,
|
||||||
|
private accountService: AccountService,
|
||||||
) {
|
) {
|
||||||
this.searchControl.valueChanges
|
this.searchControl.valueChanges
|
||||||
.pipe(debounceTime(200), takeUntilDestroyed())
|
.pipe(debounceTime(200), takeUntilDestroyed())
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { CommonModule } from "@angular/common";
|
|||||||
import { Component, DestroyRef, OnInit, inject } from "@angular/core";
|
import { Component, DestroyRef, OnInit, inject } from "@angular/core";
|
||||||
import { takeUntilDestroyed } from "@angular/core/rxjs-interop";
|
import { takeUntilDestroyed } from "@angular/core/rxjs-interop";
|
||||||
import { ActivatedRoute, Router } from "@angular/router";
|
import { ActivatedRoute, Router } from "@angular/router";
|
||||||
import { EMPTY, Observable } from "rxjs";
|
import { EMPTY, firstValueFrom, Observable } from "rxjs";
|
||||||
import { map, switchMap } from "rxjs/operators";
|
import { map, switchMap } from "rxjs/operators";
|
||||||
|
|
||||||
import { JslibModule } from "@bitwarden/angular/jslib.module";
|
import { JslibModule } from "@bitwarden/angular/jslib.module";
|
||||||
@@ -15,6 +15,8 @@ import {
|
|||||||
DrawerType,
|
DrawerType,
|
||||||
PasswordHealthReportApplicationsResponse,
|
PasswordHealthReportApplicationsResponse,
|
||||||
} from "@bitwarden/bit-common/dirt/reports/risk-insights/models/password-health";
|
} from "@bitwarden/bit-common/dirt/reports/risk-insights/models/password-health";
|
||||||
|
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||||
|
import { getUserId } from "@bitwarden/common/auth/services/account.service";
|
||||||
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
||||||
import { OrganizationId } from "@bitwarden/common/types/guid";
|
import { OrganizationId } from "@bitwarden/common/types/guid";
|
||||||
import {
|
import {
|
||||||
@@ -78,15 +80,16 @@ export class RiskInsightsComponent implements OnInit {
|
|||||||
private configService: ConfigService,
|
private configService: ConfigService,
|
||||||
protected dataService: RiskInsightsDataService,
|
protected dataService: RiskInsightsDataService,
|
||||||
private criticalAppsService: CriticalAppsService,
|
private criticalAppsService: CriticalAppsService,
|
||||||
|
private accountService: AccountService,
|
||||||
) {
|
) {
|
||||||
this.route.queryParams.pipe(takeUntilDestroyed()).subscribe(({ tabIndex }) => {
|
this.route.queryParams.pipe(takeUntilDestroyed()).subscribe(({ tabIndex }) => {
|
||||||
this.tabIndex = !isNaN(Number(tabIndex)) ? Number(tabIndex) : RiskInsightsTabType.AllApps;
|
this.tabIndex = !isNaN(Number(tabIndex)) ? Number(tabIndex) : RiskInsightsTabType.AllApps;
|
||||||
});
|
});
|
||||||
const orgId = this.route.snapshot.paramMap.get("organizationId") ?? "";
|
|
||||||
this.criticalApps$ = this.criticalAppsService.getAppsListForOrg(orgId);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async ngOnInit() {
|
async ngOnInit() {
|
||||||
|
const userId = await firstValueFrom(getUserId(this.accountService.activeAccount$));
|
||||||
|
|
||||||
this.route.paramMap
|
this.route.paramMap
|
||||||
.pipe(
|
.pipe(
|
||||||
takeUntilDestroyed(this.destroyRef),
|
takeUntilDestroyed(this.destroyRef),
|
||||||
@@ -109,7 +112,11 @@ export class RiskInsightsComponent implements OnInit {
|
|||||||
if (applications) {
|
if (applications) {
|
||||||
this.appsCount = applications.length;
|
this.appsCount = applications.length;
|
||||||
}
|
}
|
||||||
this.criticalAppsService.setOrganizationId(this.organizationId as OrganizationId);
|
|
||||||
|
this.criticalAppsService.setOrganizationId(this.organizationId as OrganizationId, userId);
|
||||||
|
this.criticalApps$ = this.criticalAppsService.getAppsListForOrg(
|
||||||
|
this.organizationId as OrganizationId,
|
||||||
|
);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user