mirror of
https://github.com/bitwarden/browser
synced 2025-12-10 13:23:34 +00:00
[PM-28350] Refactor policies component (#17636)
* refactor policies component * add tests * cleanup * clean up * change trackBy to * change detetction
This commit is contained in:
@@ -1,27 +1,34 @@
|
|||||||
<app-header></app-header>
|
<app-header></app-header>
|
||||||
|
|
||||||
|
@let organization = organization$ | async;
|
||||||
|
@let policiesEnabledMap = policiesEnabledMap$ | async;
|
||||||
|
@let organizationId = organizationId$ | async;
|
||||||
|
|
||||||
<bit-container>
|
<bit-container>
|
||||||
@if (loading) {
|
@if (!organization || !policiesEnabledMap || !organizationId) {
|
||||||
<i
|
<i
|
||||||
class="bwi bwi-spinner bwi-spin tw-text-muted"
|
class="bwi bwi-spinner bwi-spin tw-text-muted"
|
||||||
title="{{ 'loading' | i18n }}"
|
title="{{ 'loading' | i18n }}"
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
></i>
|
></i>
|
||||||
<span class="tw-sr-only">{{ "loading" | i18n }}</span>
|
<span class="tw-sr-only">{{ "loading" | i18n }}</span>
|
||||||
}
|
} @else {
|
||||||
@if (!loading) {
|
|
||||||
<bit-table>
|
<bit-table>
|
||||||
<ng-template body>
|
<ng-template body>
|
||||||
@for (p of policies$ | async; track p.type) {
|
@for (p of policies$ | async; track $index) {
|
||||||
<tr bitRow>
|
@if (p.display$(organization, configService) | async) {
|
||||||
<td bitCell ngPreserveWhitespaces>
|
<tr bitRow>
|
||||||
<button type="button" bitLink (click)="edit(p)">{{ p.name | i18n }}</button>
|
<td bitCell ngPreserveWhitespaces>
|
||||||
@if (policiesEnabledMap.get(p.type)) {
|
<button type="button" bitLink (click)="edit(p, organizationId)">
|
||||||
<span bitBadge variant="success">{{ "on" | i18n }}</span>
|
{{ p.name | i18n }}
|
||||||
}
|
</button>
|
||||||
<small class="tw-text-muted tw-block">{{ p.description | i18n }}</small>
|
@if (policiesEnabledMap.get(p.type)) {
|
||||||
</td>
|
<span bitBadge variant="success">{{ "on" | i18n }}</span>
|
||||||
</tr>
|
}
|
||||||
|
<small class="tw-text-muted tw-block">{{ p.description | i18n }}</small>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</ng-template>
|
</ng-template>
|
||||||
</bit-table>
|
</bit-table>
|
||||||
|
|||||||
@@ -0,0 +1,498 @@
|
|||||||
|
import { NO_ERRORS_SCHEMA } from "@angular/core";
|
||||||
|
import { ComponentFixture, TestBed } from "@angular/core/testing";
|
||||||
|
import { ActivatedRoute } from "@angular/router";
|
||||||
|
import { mock, MockProxy } from "jest-mock-extended";
|
||||||
|
import { BehaviorSubject, of, firstValueFrom } from "rxjs";
|
||||||
|
|
||||||
|
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
|
||||||
|
import { PolicyApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/policy/policy-api.service.abstraction";
|
||||||
|
import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
|
||||||
|
import { PolicyType } from "@bitwarden/common/admin-console/enums";
|
||||||
|
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
|
||||||
|
import { PolicyResponse } from "@bitwarden/common/admin-console/models/response/policy.response";
|
||||||
|
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||||
|
import { ListResponse } from "@bitwarden/common/models/response/list.response";
|
||||||
|
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
||||||
|
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||||
|
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||||
|
import { FakeAccountService, mockAccountServiceWith } from "@bitwarden/common/spec";
|
||||||
|
import { OrganizationId, UserId } from "@bitwarden/common/types/guid";
|
||||||
|
import { DialogService } from "@bitwarden/components";
|
||||||
|
import { newGuid } from "@bitwarden/guid";
|
||||||
|
|
||||||
|
import { BasePolicyEditDefinition } from "./base-policy-edit.component";
|
||||||
|
import { PoliciesComponent } from "./policies.component";
|
||||||
|
import { SingleOrgPolicy } from "./policy-edit-definitions/single-org.component";
|
||||||
|
import { PolicyEditDialogComponent } from "./policy-edit-dialog.component";
|
||||||
|
import { PolicyListService } from "./policy-list.service";
|
||||||
|
import { POLICY_EDIT_REGISTER } from "./policy-register-token";
|
||||||
|
|
||||||
|
describe("PoliciesComponent", () => {
|
||||||
|
let component: PoliciesComponent;
|
||||||
|
let fixture: ComponentFixture<PoliciesComponent>;
|
||||||
|
|
||||||
|
let mockActivatedRoute: ActivatedRoute;
|
||||||
|
let mockOrganizationService: MockProxy<OrganizationService>;
|
||||||
|
let mockAccountService: FakeAccountService;
|
||||||
|
let mockPolicyApiService: MockProxy<PolicyApiServiceAbstraction>;
|
||||||
|
let mockPolicyListService: MockProxy<PolicyListService>;
|
||||||
|
let mockDialogService: MockProxy<DialogService>;
|
||||||
|
let mockPolicyService: MockProxy<PolicyService>;
|
||||||
|
let mockConfigService: MockProxy<ConfigService>;
|
||||||
|
let mockI18nService: MockProxy<I18nService>;
|
||||||
|
let mockPlatformUtilsService: MockProxy<PlatformUtilsService>;
|
||||||
|
|
||||||
|
let routeParamsSubject: BehaviorSubject<any>;
|
||||||
|
let queryParamsSubject: BehaviorSubject<any>;
|
||||||
|
|
||||||
|
const mockUserId = newGuid() as UserId;
|
||||||
|
const mockOrgId = newGuid() as OrganizationId;
|
||||||
|
const mockOrg = {
|
||||||
|
id: mockOrgId,
|
||||||
|
name: "Test Organization",
|
||||||
|
enabled: true,
|
||||||
|
} as Organization;
|
||||||
|
|
||||||
|
const mockPolicyResponse = {
|
||||||
|
id: newGuid(),
|
||||||
|
enabled: true,
|
||||||
|
object: "policy",
|
||||||
|
organizationId: mockOrgId,
|
||||||
|
type: PolicyType.SingleOrg,
|
||||||
|
data: null,
|
||||||
|
};
|
||||||
|
|
||||||
|
const mockPolicy = new SingleOrgPolicy();
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
routeParamsSubject = new BehaviorSubject({ organizationId: mockOrgId });
|
||||||
|
queryParamsSubject = new BehaviorSubject({});
|
||||||
|
|
||||||
|
mockActivatedRoute = {
|
||||||
|
params: routeParamsSubject.asObservable(),
|
||||||
|
queryParams: queryParamsSubject.asObservable(),
|
||||||
|
} as any;
|
||||||
|
|
||||||
|
mockOrganizationService = mock<OrganizationService>();
|
||||||
|
mockOrganizationService.organizations$.mockReturnValue(of([mockOrg]));
|
||||||
|
|
||||||
|
mockAccountService = mockAccountServiceWith(mockUserId);
|
||||||
|
|
||||||
|
mockPolicyApiService = mock<PolicyApiServiceAbstraction>();
|
||||||
|
mockPolicyApiService.getPolicies.mockResolvedValue(
|
||||||
|
new ListResponse({ Data: [mockPolicyResponse], ContinuationToken: null }, PolicyResponse),
|
||||||
|
);
|
||||||
|
|
||||||
|
mockPolicyListService = mock<PolicyListService>();
|
||||||
|
mockPolicyListService.getPolicies.mockReturnValue([mockPolicy]);
|
||||||
|
|
||||||
|
mockDialogService = mock<DialogService>();
|
||||||
|
mockDialogService.open.mockReturnValue({ close: jest.fn() } as any);
|
||||||
|
|
||||||
|
mockPolicyService = mock<PolicyService>();
|
||||||
|
mockPolicyService.policies$.mockReturnValue(of([]));
|
||||||
|
|
||||||
|
mockConfigService = mock<ConfigService>();
|
||||||
|
mockI18nService = mock<I18nService>();
|
||||||
|
mockPlatformUtilsService = mock<PlatformUtilsService>();
|
||||||
|
|
||||||
|
jest.spyOn(PolicyEditDialogComponent, "open").mockReturnValue({ close: jest.fn() } as any);
|
||||||
|
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
imports: [PoliciesComponent],
|
||||||
|
providers: [
|
||||||
|
{ provide: ActivatedRoute, useValue: mockActivatedRoute },
|
||||||
|
{ provide: OrganizationService, useValue: mockOrganizationService },
|
||||||
|
{ provide: AccountService, useValue: mockAccountService },
|
||||||
|
{ provide: PolicyApiServiceAbstraction, useValue: mockPolicyApiService },
|
||||||
|
{ provide: PolicyListService, useValue: mockPolicyListService },
|
||||||
|
{ provide: DialogService, useValue: mockDialogService },
|
||||||
|
{ provide: PolicyService, useValue: mockPolicyService },
|
||||||
|
{ provide: ConfigService, useValue: mockConfigService },
|
||||||
|
{ provide: I18nService, useValue: mockI18nService },
|
||||||
|
{ provide: PlatformUtilsService, useValue: mockPlatformUtilsService },
|
||||||
|
{ provide: POLICY_EDIT_REGISTER, useValue: [] },
|
||||||
|
],
|
||||||
|
schemas: [NO_ERRORS_SCHEMA],
|
||||||
|
})
|
||||||
|
.overrideComponent(PoliciesComponent, {
|
||||||
|
remove: { imports: [] },
|
||||||
|
add: { template: "<div></div>" },
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
|
||||||
|
fixture = TestBed.createComponent(PoliciesComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
if (fixture) {
|
||||||
|
fixture.destroy();
|
||||||
|
}
|
||||||
|
jest.restoreAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should create", () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("organizationId$", () => {
|
||||||
|
it("should extract organizationId from route params", async () => {
|
||||||
|
const orgId = await firstValueFrom(component.organizationId$);
|
||||||
|
expect(orgId).toBe(mockOrgId);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should emit new organizationId when route params change", (done) => {
|
||||||
|
const newOrgId = newGuid() as OrganizationId;
|
||||||
|
const emittedValues: OrganizationId[] = [];
|
||||||
|
|
||||||
|
const subscription = component.organizationId$.subscribe((orgId) => {
|
||||||
|
emittedValues.push(orgId);
|
||||||
|
|
||||||
|
if (emittedValues.length === 2) {
|
||||||
|
expect(emittedValues[0]).toBe(mockOrgId);
|
||||||
|
expect(emittedValues[1]).toBe(newOrgId);
|
||||||
|
subscription.unsubscribe();
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
routeParamsSubject.next({ organizationId: newOrgId });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("organization$", () => {
|
||||||
|
it("should retrieve organization for current user and organizationId", async () => {
|
||||||
|
const org = await firstValueFrom(component.organization$);
|
||||||
|
expect(org).toBe(mockOrg);
|
||||||
|
expect(mockOrganizationService.organizations$).toHaveBeenCalledWith(mockUserId);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should throw error when organization is not found", async () => {
|
||||||
|
mockOrganizationService.organizations$.mockReturnValue(of([]));
|
||||||
|
|
||||||
|
await expect(firstValueFrom(component.organization$)).rejects.toThrow(
|
||||||
|
"No organization found for provided userId",
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("policies$", () => {
|
||||||
|
it("should return policies from PolicyListService", async () => {
|
||||||
|
const policies = await firstValueFrom(component.policies$);
|
||||||
|
|
||||||
|
expect(policies).toBeDefined();
|
||||||
|
expect(Array.isArray(policies)).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("orgPolicies$", () => {
|
||||||
|
it("should fetch policies from API for current organization", async () => {
|
||||||
|
const mockPolicyResponsesData = [
|
||||||
|
{
|
||||||
|
id: newGuid(),
|
||||||
|
organizationId: mockOrgId,
|
||||||
|
type: PolicyType.TwoFactorAuthentication,
|
||||||
|
enabled: true,
|
||||||
|
data: null,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: newGuid(),
|
||||||
|
organizationId: mockOrgId,
|
||||||
|
type: PolicyType.RequireSso,
|
||||||
|
enabled: false,
|
||||||
|
data: null,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const listResponse = new ListResponse(
|
||||||
|
{ Data: mockPolicyResponsesData, ContinuationToken: null },
|
||||||
|
PolicyResponse,
|
||||||
|
);
|
||||||
|
|
||||||
|
mockPolicyApiService.getPolicies.mockResolvedValue(listResponse);
|
||||||
|
|
||||||
|
const policies = await firstValueFrom(component["orgPolicies$"]);
|
||||||
|
expect(policies).toEqual(listResponse.data);
|
||||||
|
expect(mockPolicyApiService.getPolicies).toHaveBeenCalledWith(mockOrgId);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return empty array when API returns no data", async () => {
|
||||||
|
mockPolicyApiService.getPolicies.mockResolvedValue(
|
||||||
|
new ListResponse({ Data: [], ContinuationToken: null }, PolicyResponse),
|
||||||
|
);
|
||||||
|
|
||||||
|
const policies = await firstValueFrom(component["orgPolicies$"]);
|
||||||
|
expect(policies).toEqual([]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return empty array when API returns null data", async () => {
|
||||||
|
mockPolicyApiService.getPolicies.mockResolvedValue(
|
||||||
|
new ListResponse({ Data: null, ContinuationToken: null }, PolicyResponse),
|
||||||
|
);
|
||||||
|
|
||||||
|
const policies = await firstValueFrom(component["orgPolicies$"]);
|
||||||
|
expect(policies).toEqual([]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("policiesEnabledMap$", () => {
|
||||||
|
it("should create a map of policy types to their enabled status", async () => {
|
||||||
|
const mockPolicyResponsesData = [
|
||||||
|
{
|
||||||
|
id: "policy-1",
|
||||||
|
organizationId: mockOrgId,
|
||||||
|
type: PolicyType.TwoFactorAuthentication,
|
||||||
|
enabled: true,
|
||||||
|
data: null,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "policy-2",
|
||||||
|
organizationId: mockOrgId,
|
||||||
|
type: PolicyType.RequireSso,
|
||||||
|
enabled: false,
|
||||||
|
data: null,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "policy-3",
|
||||||
|
organizationId: mockOrgId,
|
||||||
|
type: PolicyType.SingleOrg,
|
||||||
|
enabled: true,
|
||||||
|
data: null,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
mockPolicyApiService.getPolicies.mockResolvedValue(
|
||||||
|
new ListResponse(
|
||||||
|
{ Data: mockPolicyResponsesData, ContinuationToken: null },
|
||||||
|
PolicyResponse,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
const map = await firstValueFrom(component.policiesEnabledMap$);
|
||||||
|
expect(map.size).toBe(3);
|
||||||
|
expect(map.get(PolicyType.TwoFactorAuthentication)).toBe(true);
|
||||||
|
expect(map.get(PolicyType.RequireSso)).toBe(false);
|
||||||
|
expect(map.get(PolicyType.SingleOrg)).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should create empty map when no policies exist", async () => {
|
||||||
|
mockPolicyApiService.getPolicies.mockResolvedValue(
|
||||||
|
new ListResponse({ Data: [], ContinuationToken: null }, PolicyResponse),
|
||||||
|
);
|
||||||
|
|
||||||
|
const map = await firstValueFrom(component.policiesEnabledMap$);
|
||||||
|
expect(map.size).toBe(0);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("constructor subscription", () => {
|
||||||
|
it("should subscribe to policyService.policies$ on initialization", () => {
|
||||||
|
expect(mockPolicyService.policies$).toHaveBeenCalledWith(mockUserId);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should refresh policies when policyService emits", async () => {
|
||||||
|
const policiesSubject = new BehaviorSubject<any[]>([]);
|
||||||
|
mockPolicyService.policies$.mockReturnValue(policiesSubject.asObservable());
|
||||||
|
|
||||||
|
let callCount = 0;
|
||||||
|
mockPolicyApiService.getPolicies.mockImplementation(() => {
|
||||||
|
callCount++;
|
||||||
|
return of(new ListResponse({ Data: [], ContinuationToken: null }, PolicyResponse));
|
||||||
|
});
|
||||||
|
|
||||||
|
const newFixture = TestBed.createComponent(PoliciesComponent);
|
||||||
|
newFixture.detectChanges();
|
||||||
|
|
||||||
|
const initialCallCount = callCount;
|
||||||
|
|
||||||
|
policiesSubject.next([{ type: PolicyType.TwoFactorAuthentication }]);
|
||||||
|
|
||||||
|
expect(callCount).toBeGreaterThan(initialCallCount);
|
||||||
|
|
||||||
|
newFixture.destroy();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("handleLaunchEvent", () => {
|
||||||
|
it("should open policy dialog when policyId is in query params", async () => {
|
||||||
|
const mockPolicyId = newGuid();
|
||||||
|
const mockPolicy: BasePolicyEditDefinition = {
|
||||||
|
name: "Test Policy",
|
||||||
|
description: "Test Description",
|
||||||
|
type: PolicyType.TwoFactorAuthentication,
|
||||||
|
component: {} as any,
|
||||||
|
showDescription: true,
|
||||||
|
display$: () => of(true),
|
||||||
|
};
|
||||||
|
|
||||||
|
const mockPolicyResponseData = {
|
||||||
|
id: mockPolicyId,
|
||||||
|
organizationId: mockOrgId,
|
||||||
|
type: PolicyType.TwoFactorAuthentication,
|
||||||
|
enabled: true,
|
||||||
|
data: null,
|
||||||
|
};
|
||||||
|
|
||||||
|
queryParamsSubject.next({ policyId: mockPolicyId });
|
||||||
|
|
||||||
|
mockPolicyApiService.getPolicies.mockReturnValue(
|
||||||
|
of(
|
||||||
|
new ListResponse(
|
||||||
|
{ Data: [mockPolicyResponseData], ContinuationToken: null },
|
||||||
|
PolicyResponse,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
const dialogOpenSpy = jest
|
||||||
|
.spyOn(PolicyEditDialogComponent, "open")
|
||||||
|
.mockReturnValue({ close: jest.fn() } as any);
|
||||||
|
|
||||||
|
TestBed.resetTestingModule();
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
imports: [PoliciesComponent],
|
||||||
|
providers: [
|
||||||
|
{ provide: ActivatedRoute, useValue: mockActivatedRoute },
|
||||||
|
{ provide: OrganizationService, useValue: mockOrganizationService },
|
||||||
|
{ provide: AccountService, useValue: mockAccountService },
|
||||||
|
{ provide: PolicyApiServiceAbstraction, useValue: mockPolicyApiService },
|
||||||
|
{ provide: PolicyListService, useValue: mockPolicyListService },
|
||||||
|
{ provide: DialogService, useValue: mockDialogService },
|
||||||
|
{ provide: PolicyService, useValue: mockPolicyService },
|
||||||
|
{ provide: ConfigService, useValue: mockConfigService },
|
||||||
|
{ provide: I18nService, useValue: mockI18nService },
|
||||||
|
{ provide: PlatformUtilsService, useValue: mockPlatformUtilsService },
|
||||||
|
{ provide: POLICY_EDIT_REGISTER, useValue: [mockPolicy] },
|
||||||
|
],
|
||||||
|
schemas: [NO_ERRORS_SCHEMA],
|
||||||
|
})
|
||||||
|
.overrideComponent(PoliciesComponent, {
|
||||||
|
remove: { imports: [] },
|
||||||
|
add: { template: "<div></div>" },
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
|
||||||
|
const newFixture = TestBed.createComponent(PoliciesComponent);
|
||||||
|
newFixture.detectChanges();
|
||||||
|
|
||||||
|
expect(dialogOpenSpy).toHaveBeenCalled();
|
||||||
|
const callArgs = dialogOpenSpy.mock.calls[0][1];
|
||||||
|
expect(callArgs.data?.policy.type).toBe(mockPolicy.type);
|
||||||
|
expect(callArgs.data?.organizationId).toBe(mockOrgId);
|
||||||
|
|
||||||
|
newFixture.destroy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should not open dialog when policyId is not in query params", async () => {
|
||||||
|
const editSpy = jest.spyOn(component, "edit");
|
||||||
|
|
||||||
|
queryParamsSubject.next({});
|
||||||
|
|
||||||
|
expect(editSpy).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should not open dialog when policyId does not match any org policy", async () => {
|
||||||
|
const mockPolicy: BasePolicyEditDefinition = {
|
||||||
|
name: "Test Policy",
|
||||||
|
description: "Test Description",
|
||||||
|
type: PolicyType.TwoFactorAuthentication,
|
||||||
|
component: {} as any,
|
||||||
|
showDescription: true,
|
||||||
|
display$: () => of(true),
|
||||||
|
};
|
||||||
|
|
||||||
|
mockPolicyListService.getPolicies.mockReturnValue([mockPolicy]);
|
||||||
|
mockPolicyApiService.getPolicies.mockResolvedValue(
|
||||||
|
new ListResponse({ Data: [], ContinuationToken: null }, PolicyResponse),
|
||||||
|
);
|
||||||
|
|
||||||
|
const editSpy = jest.spyOn(component, "edit");
|
||||||
|
|
||||||
|
queryParamsSubject.next({ policyId: "non-existent-policy-id" });
|
||||||
|
|
||||||
|
expect(editSpy).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("edit", () => {
|
||||||
|
it("should call dialogService.open with correct parameters when no custom dialog is specified", () => {
|
||||||
|
const mockPolicy: BasePolicyEditDefinition = {
|
||||||
|
name: "Test Policy",
|
||||||
|
description: "Test Description",
|
||||||
|
type: PolicyType.TwoFactorAuthentication,
|
||||||
|
component: {} as any,
|
||||||
|
showDescription: true,
|
||||||
|
display$: () => of(true),
|
||||||
|
};
|
||||||
|
|
||||||
|
const openSpy = jest.spyOn(PolicyEditDialogComponent, "open");
|
||||||
|
|
||||||
|
component.edit(mockPolicy, mockOrgId);
|
||||||
|
|
||||||
|
expect(openSpy).toHaveBeenCalled();
|
||||||
|
const callArgs = openSpy.mock.calls[0];
|
||||||
|
expect(callArgs[1]).toEqual({
|
||||||
|
data: {
|
||||||
|
policy: mockPolicy,
|
||||||
|
organizationId: mockOrgId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should call custom dialog open method when specified", () => {
|
||||||
|
const mockDialogRef = { close: jest.fn() };
|
||||||
|
const mockCustomDialog = {
|
||||||
|
open: jest.fn().mockReturnValue(mockDialogRef),
|
||||||
|
};
|
||||||
|
|
||||||
|
const mockPolicy: BasePolicyEditDefinition = {
|
||||||
|
name: "Custom Policy",
|
||||||
|
description: "Custom Description",
|
||||||
|
type: PolicyType.RequireSso,
|
||||||
|
component: {} as any,
|
||||||
|
editDialogComponent: mockCustomDialog as any,
|
||||||
|
showDescription: true,
|
||||||
|
display$: () => of(true),
|
||||||
|
};
|
||||||
|
|
||||||
|
component.edit(mockPolicy, mockOrgId);
|
||||||
|
|
||||||
|
expect(mockCustomDialog.open).toHaveBeenCalled();
|
||||||
|
const callArgs = mockCustomDialog.open.mock.calls[0];
|
||||||
|
expect(callArgs[1]).toEqual({
|
||||||
|
data: {
|
||||||
|
policy: mockPolicy,
|
||||||
|
organizationId: mockOrgId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
expect(PolicyEditDialogComponent.open).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should pass correct organizationId to dialog", () => {
|
||||||
|
const customOrgId = newGuid() as OrganizationId;
|
||||||
|
const mockPolicy: BasePolicyEditDefinition = {
|
||||||
|
name: "Test Policy",
|
||||||
|
description: "Test Description",
|
||||||
|
type: PolicyType.SingleOrg,
|
||||||
|
component: {} as any,
|
||||||
|
showDescription: true,
|
||||||
|
display$: () => of(true),
|
||||||
|
};
|
||||||
|
|
||||||
|
const openSpy = jest.spyOn(PolicyEditDialogComponent, "open");
|
||||||
|
|
||||||
|
component.edit(mockPolicy, customOrgId);
|
||||||
|
|
||||||
|
expect(openSpy).toHaveBeenCalled();
|
||||||
|
const callArgs = openSpy.mock.calls[0];
|
||||||
|
expect(callArgs[1]).toEqual({
|
||||||
|
data: {
|
||||||
|
policy: mockPolicy,
|
||||||
|
organizationId: customOrgId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -1,31 +1,19 @@
|
|||||||
// FIXME: Update this file to be type safe and remove this and next line
|
import { ChangeDetectionStrategy, Component, DestroyRef } from "@angular/core";
|
||||||
// @ts-strict-ignore
|
|
||||||
import { Component, OnInit } from "@angular/core";
|
|
||||||
import { takeUntilDestroyed } from "@angular/core/rxjs-interop";
|
import { takeUntilDestroyed } from "@angular/core/rxjs-interop";
|
||||||
import { ActivatedRoute } from "@angular/router";
|
import { ActivatedRoute } from "@angular/router";
|
||||||
import {
|
import { combineLatest, Observable, of, switchMap, first, map } from "rxjs";
|
||||||
combineLatest,
|
|
||||||
firstValueFrom,
|
|
||||||
Observable,
|
|
||||||
of,
|
|
||||||
switchMap,
|
|
||||||
first,
|
|
||||||
map,
|
|
||||||
withLatestFrom,
|
|
||||||
tap,
|
|
||||||
} from "rxjs";
|
|
||||||
|
|
||||||
import {
|
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
|
||||||
getOrganizationById,
|
|
||||||
OrganizationService,
|
|
||||||
} from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
|
|
||||||
import { PolicyApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/policy/policy-api.service.abstraction";
|
import { PolicyApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/policy/policy-api.service.abstraction";
|
||||||
import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
|
import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
|
||||||
import { PolicyType } from "@bitwarden/common/admin-console/enums";
|
import { PolicyType } from "@bitwarden/common/admin-console/enums";
|
||||||
|
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
|
||||||
import { PolicyResponse } from "@bitwarden/common/admin-console/models/response/policy.response";
|
import { PolicyResponse } from "@bitwarden/common/admin-console/models/response/policy.response";
|
||||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||||
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 { getById } from "@bitwarden/common/platform/misc";
|
||||||
|
import { OrganizationId, UserId } from "@bitwarden/common/types/guid";
|
||||||
import { DialogService } from "@bitwarden/components";
|
import { DialogService } from "@bitwarden/components";
|
||||||
import { safeProvider } from "@bitwarden/ui-common";
|
import { safeProvider } from "@bitwarden/ui-common";
|
||||||
|
|
||||||
@@ -37,8 +25,6 @@ import { PolicyEditDialogComponent } from "./policy-edit-dialog.component";
|
|||||||
import { PolicyListService } from "./policy-list.service";
|
import { PolicyListService } from "./policy-list.service";
|
||||||
import { POLICY_EDIT_REGISTER } from "./policy-register-token";
|
import { POLICY_EDIT_REGISTER } from "./policy-register-token";
|
||||||
|
|
||||||
// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush
|
|
||||||
// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection
|
|
||||||
@Component({
|
@Component({
|
||||||
templateUrl: "policies.component.html",
|
templateUrl: "policies.component.html",
|
||||||
imports: [SharedModule, HeaderModule],
|
imports: [SharedModule, HeaderModule],
|
||||||
@@ -48,14 +34,53 @@ import { POLICY_EDIT_REGISTER } from "./policy-register-token";
|
|||||||
deps: [POLICY_EDIT_REGISTER],
|
deps: [POLICY_EDIT_REGISTER],
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
})
|
})
|
||||||
export class PoliciesComponent implements OnInit {
|
export class PoliciesComponent {
|
||||||
loading = true;
|
private userId$: Observable<UserId> = this.accountService.activeAccount$.pipe(getUserId);
|
||||||
organizationId: string;
|
|
||||||
policies$: Observable<BasePolicyEditDefinition[]>;
|
|
||||||
|
|
||||||
private orgPolicies: PolicyResponse[];
|
protected organizationId$: Observable<OrganizationId> = this.route.params.pipe(
|
||||||
protected policiesEnabledMap: Map<PolicyType, boolean> = new Map<PolicyType, boolean>();
|
map((params) => params.organizationId),
|
||||||
|
);
|
||||||
|
|
||||||
|
protected organization$: Observable<Organization> = combineLatest([
|
||||||
|
this.userId$,
|
||||||
|
this.organizationId$,
|
||||||
|
]).pipe(
|
||||||
|
switchMap(([userId, orgId]) =>
|
||||||
|
this.organizationService.organizations$(userId).pipe(
|
||||||
|
getById(orgId),
|
||||||
|
map((org) => {
|
||||||
|
if (org == null) {
|
||||||
|
throw new Error("No organization found for provided userId");
|
||||||
|
}
|
||||||
|
return org;
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
protected policies$: Observable<readonly BasePolicyEditDefinition[]> = of(
|
||||||
|
this.policyListService.getPolicies(),
|
||||||
|
);
|
||||||
|
|
||||||
|
private orgPolicies$: Observable<PolicyResponse[]> = this.accountService.activeAccount$.pipe(
|
||||||
|
getUserId,
|
||||||
|
switchMap((userId) => this.policyService.policies$(userId)),
|
||||||
|
switchMap(() => this.organizationId$),
|
||||||
|
switchMap((organizationId) => this.policyApiService.getPolicies(organizationId)),
|
||||||
|
map((response) => (response.data != null && response.data.length > 0 ? response.data : [])),
|
||||||
|
);
|
||||||
|
|
||||||
|
protected policiesEnabledMap$: Observable<Map<PolicyType, boolean>> = this.orgPolicies$.pipe(
|
||||||
|
map((orgPolicies) => {
|
||||||
|
const policiesEnabledMap: Map<PolicyType, boolean> = new Map<PolicyType, boolean>();
|
||||||
|
orgPolicies.forEach((op) => {
|
||||||
|
policiesEnabledMap.set(op.type, op.enabled);
|
||||||
|
});
|
||||||
|
return policiesEnabledMap;
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private route: ActivatedRoute,
|
private route: ActivatedRoute,
|
||||||
@@ -66,60 +91,28 @@ export class PoliciesComponent implements OnInit {
|
|||||||
private dialogService: DialogService,
|
private dialogService: DialogService,
|
||||||
private policyService: PolicyService,
|
private policyService: PolicyService,
|
||||||
protected configService: ConfigService,
|
protected configService: ConfigService,
|
||||||
|
private destroyRef: DestroyRef,
|
||||||
) {
|
) {
|
||||||
this.accountService.activeAccount$
|
this.handleLaunchEvent();
|
||||||
.pipe(
|
|
||||||
getUserId,
|
|
||||||
switchMap((userId) => this.policyService.policies$(userId)),
|
|
||||||
tap(async () => await this.load()),
|
|
||||||
takeUntilDestroyed(),
|
|
||||||
)
|
|
||||||
.subscribe();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async ngOnInit() {
|
// Handle policies component launch from Event message
|
||||||
// eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe
|
private handleLaunchEvent() {
|
||||||
this.route.parent.parent.params.subscribe(async (params) => {
|
combineLatest([
|
||||||
this.organizationId = params.organizationId;
|
this.route.queryParams.pipe(first()),
|
||||||
const userId = await firstValueFrom(
|
this.policies$,
|
||||||
this.accountService.activeAccount$.pipe(map((a) => a?.id)),
|
this.organizationId$,
|
||||||
);
|
this.orgPolicies$,
|
||||||
|
])
|
||||||
const organization$ = this.organizationService
|
.pipe(
|
||||||
.organizations$(userId)
|
map(([qParams, policies, organizationId, orgPolicies]) => {
|
||||||
.pipe(getOrganizationById(this.organizationId));
|
|
||||||
|
|
||||||
this.policies$ = organization$.pipe(
|
|
||||||
withLatestFrom(of(this.policyListService.getPolicies())),
|
|
||||||
switchMap(([organization, policies]) => {
|
|
||||||
return combineLatest(
|
|
||||||
policies.map((policy) =>
|
|
||||||
policy
|
|
||||||
.display$(organization, this.configService)
|
|
||||||
.pipe(map((shouldDisplay) => ({ policy, shouldDisplay }))),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}),
|
|
||||||
map((results) =>
|
|
||||||
results.filter((result) => result.shouldDisplay).map((result) => result.policy),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
await this.load();
|
|
||||||
|
|
||||||
// Handle policies component launch from Event message
|
|
||||||
combineLatest([this.route.queryParams.pipe(first()), this.policies$])
|
|
||||||
/* eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe, rxjs/no-nested-subscribe */
|
|
||||||
.subscribe(async ([qParams, policies]) => {
|
|
||||||
if (qParams.policyId != null) {
|
if (qParams.policyId != null) {
|
||||||
const policyIdFromEvents: string = qParams.policyId;
|
const policyIdFromEvents: string = qParams.policyId;
|
||||||
for (const orgPolicy of this.orgPolicies) {
|
for (const orgPolicy of orgPolicies) {
|
||||||
if (orgPolicy.id === policyIdFromEvents) {
|
if (orgPolicy.id === policyIdFromEvents) {
|
||||||
for (let i = 0; i < policies.length; i++) {
|
for (let i = 0; i < policies.length; i++) {
|
||||||
if (policies[i].type === orgPolicy.type) {
|
if (policies[i].type === orgPolicy.type) {
|
||||||
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
|
this.edit(policies[i], organizationId);
|
||||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
||||||
this.edit(policies[i]);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -127,27 +120,19 @@ export class PoliciesComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
}),
|
||||||
});
|
takeUntilDestroyed(this.destroyRef),
|
||||||
|
)
|
||||||
|
.subscribe();
|
||||||
}
|
}
|
||||||
|
|
||||||
async load() {
|
edit(policy: BasePolicyEditDefinition, organizationId: OrganizationId) {
|
||||||
const response = await this.policyApiService.getPolicies(this.organizationId);
|
|
||||||
this.orgPolicies = response.data != null && response.data.length > 0 ? response.data : [];
|
|
||||||
this.orgPolicies.forEach((op) => {
|
|
||||||
this.policiesEnabledMap.set(op.type, op.enabled);
|
|
||||||
});
|
|
||||||
|
|
||||||
this.loading = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
async edit(policy: BasePolicyEditDefinition) {
|
|
||||||
const dialogComponent: PolicyDialogComponent =
|
const dialogComponent: PolicyDialogComponent =
|
||||||
policy.editDialogComponent ?? PolicyEditDialogComponent;
|
policy.editDialogComponent ?? PolicyEditDialogComponent;
|
||||||
dialogComponent.open(this.dialogService, {
|
dialogComponent.open(this.dialogService, {
|
||||||
data: {
|
data: {
|
||||||
policy: policy,
|
policy: policy,
|
||||||
organizationId: this.organizationId,
|
organizationId: organizationId,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,11 @@
|
|||||||
import { Component, OnInit, Signal, TemplateRef, viewChild } from "@angular/core";
|
import {
|
||||||
|
ChangeDetectionStrategy,
|
||||||
|
Component,
|
||||||
|
OnInit,
|
||||||
|
Signal,
|
||||||
|
TemplateRef,
|
||||||
|
viewChild,
|
||||||
|
} from "@angular/core";
|
||||||
import { BehaviorSubject, map, Observable } from "rxjs";
|
import { BehaviorSubject, map, Observable } from "rxjs";
|
||||||
|
|
||||||
import { AutoConfirmSvg } from "@bitwarden/assets/svg";
|
import { AutoConfirmSvg } from "@bitwarden/assets/svg";
|
||||||
@@ -26,11 +33,11 @@ export class AutoConfirmPolicy extends BasePolicyEditDefinition {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush
|
|
||||||
// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection
|
|
||||||
@Component({
|
@Component({
|
||||||
|
selector: "auto-confirm-policy-edit",
|
||||||
templateUrl: "auto-confirm-policy.component.html",
|
templateUrl: "auto-confirm-policy.component.html",
|
||||||
imports: [SharedModule],
|
imports: [SharedModule],
|
||||||
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
})
|
})
|
||||||
export class AutoConfirmPolicyEditComponent extends BasePolicyEditComponent implements OnInit {
|
export class AutoConfirmPolicyEditComponent extends BasePolicyEditComponent implements OnInit {
|
||||||
protected readonly autoConfirmSvg = AutoConfirmSvg;
|
protected readonly autoConfirmSvg = AutoConfirmSvg;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Component } from "@angular/core";
|
import { ChangeDetectionStrategy, Component } from "@angular/core";
|
||||||
|
|
||||||
import { PolicyType } from "@bitwarden/common/admin-console/enums";
|
import { PolicyType } from "@bitwarden/common/admin-console/enums";
|
||||||
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
|
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
|
||||||
@@ -18,10 +18,10 @@ export class DesktopAutotypeDefaultSettingPolicy extends BasePolicyEditDefinitio
|
|||||||
return configService.getFeatureFlag$(FeatureFlag.WindowsDesktopAutotype);
|
return configService.getFeatureFlag$(FeatureFlag.WindowsDesktopAutotype);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush
|
|
||||||
// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection
|
|
||||||
@Component({
|
@Component({
|
||||||
|
selector: "autotype-policy-edit",
|
||||||
templateUrl: "autotype-policy.component.html",
|
templateUrl: "autotype-policy.component.html",
|
||||||
imports: [SharedModule],
|
imports: [SharedModule],
|
||||||
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
})
|
})
|
||||||
export class DesktopAutotypeDefaultSettingPolicyComponent extends BasePolicyEditComponent {}
|
export class DesktopAutotypeDefaultSettingPolicyComponent extends BasePolicyEditComponent {}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Component } from "@angular/core";
|
import { ChangeDetectionStrategy, Component } from "@angular/core";
|
||||||
|
|
||||||
import { PolicyType } from "@bitwarden/common/admin-console/enums";
|
import { PolicyType } from "@bitwarden/common/admin-console/enums";
|
||||||
|
|
||||||
@@ -12,10 +12,10 @@ export class DisableSendPolicy extends BasePolicyEditDefinition {
|
|||||||
component = DisableSendPolicyComponent;
|
component = DisableSendPolicyComponent;
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush
|
|
||||||
// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection
|
|
||||||
@Component({
|
@Component({
|
||||||
|
selector: "disable-send-policy-edit",
|
||||||
templateUrl: "disable-send.component.html",
|
templateUrl: "disable-send.component.html",
|
||||||
imports: [SharedModule],
|
imports: [SharedModule],
|
||||||
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
})
|
})
|
||||||
export class DisableSendPolicyComponent extends BasePolicyEditComponent {}
|
export class DisableSendPolicyComponent extends BasePolicyEditComponent {}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// FIXME: Update this file to be type safe and remove this and next line
|
// FIXME: Update this file to be type safe and remove this and next line
|
||||||
// @ts-strict-ignore
|
// @ts-strict-ignore
|
||||||
import { Component, OnInit } from "@angular/core";
|
import { ChangeDetectionStrategy, Component, OnInit } from "@angular/core";
|
||||||
import { FormBuilder, FormGroup, Validators } from "@angular/forms";
|
import { FormBuilder, FormGroup, Validators } from "@angular/forms";
|
||||||
import { firstValueFrom } from "rxjs";
|
import { firstValueFrom } from "rxjs";
|
||||||
|
|
||||||
@@ -26,11 +26,11 @@ export class MasterPasswordPolicy extends BasePolicyEditDefinition {
|
|||||||
component = MasterPasswordPolicyComponent;
|
component = MasterPasswordPolicyComponent;
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush
|
|
||||||
// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection
|
|
||||||
@Component({
|
@Component({
|
||||||
|
selector: "master-password-policy-edit",
|
||||||
templateUrl: "master-password.component.html",
|
templateUrl: "master-password.component.html",
|
||||||
imports: [SharedModule],
|
imports: [SharedModule],
|
||||||
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
})
|
})
|
||||||
export class MasterPasswordPolicyComponent extends BasePolicyEditComponent implements OnInit {
|
export class MasterPasswordPolicyComponent extends BasePolicyEditComponent implements OnInit {
|
||||||
MinPasswordLength = Utils.minimumPasswordLength;
|
MinPasswordLength = Utils.minimumPasswordLength;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Component } from "@angular/core";
|
import { ChangeDetectionStrategy, Component } from "@angular/core";
|
||||||
import { map, Observable } from "rxjs";
|
import { map, Observable } from "rxjs";
|
||||||
|
|
||||||
import { PolicyType } from "@bitwarden/common/admin-console/enums";
|
import { PolicyType } from "@bitwarden/common/admin-console/enums";
|
||||||
@@ -22,10 +22,10 @@ export class OrganizationDataOwnershipPolicy extends BasePolicyEditDefinition {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush
|
|
||||||
// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection
|
|
||||||
@Component({
|
@Component({
|
||||||
|
selector: "organization-data-ownership-policy-edit",
|
||||||
templateUrl: "organization-data-ownership.component.html",
|
templateUrl: "organization-data-ownership.component.html",
|
||||||
imports: [SharedModule],
|
imports: [SharedModule],
|
||||||
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
})
|
})
|
||||||
export class OrganizationDataOwnershipPolicyComponent extends BasePolicyEditComponent {}
|
export class OrganizationDataOwnershipPolicyComponent extends BasePolicyEditComponent {}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// FIXME: Update this file to be type safe and remove this and next line
|
// FIXME: Update this file to be type safe and remove this and next line
|
||||||
// @ts-strict-ignore
|
// @ts-strict-ignore
|
||||||
import { Component } from "@angular/core";
|
import { ChangeDetectionStrategy, Component } from "@angular/core";
|
||||||
import { takeUntilDestroyed } from "@angular/core/rxjs-interop";
|
import { takeUntilDestroyed } from "@angular/core/rxjs-interop";
|
||||||
import { UntypedFormBuilder, Validators } from "@angular/forms";
|
import { UntypedFormBuilder, Validators } from "@angular/forms";
|
||||||
import { BehaviorSubject, map } from "rxjs";
|
import { BehaviorSubject, map } from "rxjs";
|
||||||
@@ -19,11 +19,11 @@ export class PasswordGeneratorPolicy extends BasePolicyEditDefinition {
|
|||||||
component = PasswordGeneratorPolicyComponent;
|
component = PasswordGeneratorPolicyComponent;
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush
|
|
||||||
// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection
|
|
||||||
@Component({
|
@Component({
|
||||||
|
selector: "password-generator-policy-edit",
|
||||||
templateUrl: "password-generator.component.html",
|
templateUrl: "password-generator.component.html",
|
||||||
imports: [SharedModule],
|
imports: [SharedModule],
|
||||||
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
})
|
})
|
||||||
export class PasswordGeneratorPolicyComponent extends BasePolicyEditComponent {
|
export class PasswordGeneratorPolicyComponent extends BasePolicyEditComponent {
|
||||||
// these properties forward the application default settings to the UI
|
// these properties forward the application default settings to the UI
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Component } from "@angular/core";
|
import { ChangeDetectionStrategy, Component } from "@angular/core";
|
||||||
|
|
||||||
import { PolicyType } from "@bitwarden/common/admin-console/enums";
|
import { PolicyType } from "@bitwarden/common/admin-console/enums";
|
||||||
|
|
||||||
@@ -12,10 +12,10 @@ export class RemoveUnlockWithPinPolicy extends BasePolicyEditDefinition {
|
|||||||
component = RemoveUnlockWithPinPolicyComponent;
|
component = RemoveUnlockWithPinPolicyComponent;
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush
|
|
||||||
// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection
|
|
||||||
@Component({
|
@Component({
|
||||||
|
selector: "remove-unlock-with-pin-policy-edit",
|
||||||
templateUrl: "remove-unlock-with-pin.component.html",
|
templateUrl: "remove-unlock-with-pin.component.html",
|
||||||
imports: [SharedModule],
|
imports: [SharedModule],
|
||||||
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
})
|
})
|
||||||
export class RemoveUnlockWithPinPolicyComponent extends BasePolicyEditComponent {}
|
export class RemoveUnlockWithPinPolicyComponent extends BasePolicyEditComponent {}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Component } from "@angular/core";
|
import { ChangeDetectionStrategy, Component } from "@angular/core";
|
||||||
import { of } from "rxjs";
|
import { of } from "rxjs";
|
||||||
|
|
||||||
import { PolicyType } from "@bitwarden/common/admin-console/enums";
|
import { PolicyType } from "@bitwarden/common/admin-console/enums";
|
||||||
@@ -19,10 +19,10 @@ export class RequireSsoPolicy extends BasePolicyEditDefinition {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush
|
|
||||||
// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection
|
|
||||||
@Component({
|
@Component({
|
||||||
|
selector: "require-sso-policy-edit",
|
||||||
templateUrl: "require-sso.component.html",
|
templateUrl: "require-sso.component.html",
|
||||||
imports: [SharedModule],
|
imports: [SharedModule],
|
||||||
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
})
|
})
|
||||||
export class RequireSsoPolicyComponent extends BasePolicyEditComponent {}
|
export class RequireSsoPolicyComponent extends BasePolicyEditComponent {}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Component, OnInit } from "@angular/core";
|
import { ChangeDetectionStrategy, Component, OnInit } from "@angular/core";
|
||||||
import { FormBuilder } from "@angular/forms";
|
import { FormBuilder } from "@angular/forms";
|
||||||
import { firstValueFrom, of } from "rxjs";
|
import { firstValueFrom, of } from "rxjs";
|
||||||
|
|
||||||
@@ -26,11 +26,11 @@ export class ResetPasswordPolicy extends BasePolicyEditDefinition {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush
|
|
||||||
// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection
|
|
||||||
@Component({
|
@Component({
|
||||||
|
selector: "reset-password-policy-edit",
|
||||||
templateUrl: "reset-password.component.html",
|
templateUrl: "reset-password.component.html",
|
||||||
imports: [SharedModule],
|
imports: [SharedModule],
|
||||||
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
})
|
})
|
||||||
export class ResetPasswordPolicyComponent extends BasePolicyEditComponent implements OnInit {
|
export class ResetPasswordPolicyComponent extends BasePolicyEditComponent implements OnInit {
|
||||||
data = this.formBuilder.group({
|
data = this.formBuilder.group({
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Component } from "@angular/core";
|
import { ChangeDetectionStrategy, Component } from "@angular/core";
|
||||||
|
|
||||||
import { PolicyType } from "@bitwarden/common/admin-console/enums";
|
import { PolicyType } from "@bitwarden/common/admin-console/enums";
|
||||||
|
|
||||||
@@ -12,11 +12,11 @@ export class RestrictedItemTypesPolicy extends BasePolicyEditDefinition {
|
|||||||
component = RestrictedItemTypesPolicyComponent;
|
component = RestrictedItemTypesPolicyComponent;
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush
|
|
||||||
// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection
|
|
||||||
@Component({
|
@Component({
|
||||||
|
selector: "restricted-item-types-policy-edit",
|
||||||
templateUrl: "restricted-item-types.component.html",
|
templateUrl: "restricted-item-types.component.html",
|
||||||
imports: [SharedModule],
|
imports: [SharedModule],
|
||||||
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
})
|
})
|
||||||
export class RestrictedItemTypesPolicyComponent extends BasePolicyEditComponent {
|
export class RestrictedItemTypesPolicyComponent extends BasePolicyEditComponent {
|
||||||
constructor() {
|
constructor() {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Component } from "@angular/core";
|
import { ChangeDetectionStrategy, Component } from "@angular/core";
|
||||||
import { UntypedFormBuilder } from "@angular/forms";
|
import { UntypedFormBuilder } from "@angular/forms";
|
||||||
|
|
||||||
import { PolicyType } from "@bitwarden/common/admin-console/enums";
|
import { PolicyType } from "@bitwarden/common/admin-console/enums";
|
||||||
@@ -13,11 +13,11 @@ export class SendOptionsPolicy extends BasePolicyEditDefinition {
|
|||||||
component = SendOptionsPolicyComponent;
|
component = SendOptionsPolicyComponent;
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush
|
|
||||||
// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection
|
|
||||||
@Component({
|
@Component({
|
||||||
|
selector: "send-options-policy-edit",
|
||||||
templateUrl: "send-options.component.html",
|
templateUrl: "send-options.component.html",
|
||||||
imports: [SharedModule],
|
imports: [SharedModule],
|
||||||
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
})
|
})
|
||||||
export class SendOptionsPolicyComponent extends BasePolicyEditComponent {
|
export class SendOptionsPolicyComponent extends BasePolicyEditComponent {
|
||||||
data = this.formBuilder.group({
|
data = this.formBuilder.group({
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Component, OnInit } from "@angular/core";
|
import { ChangeDetectionStrategy, Component, OnInit } from "@angular/core";
|
||||||
|
|
||||||
import { PolicyType } from "@bitwarden/common/admin-console/enums";
|
import { PolicyType } from "@bitwarden/common/admin-console/enums";
|
||||||
|
|
||||||
@@ -12,11 +12,11 @@ export class SingleOrgPolicy extends BasePolicyEditDefinition {
|
|||||||
component = SingleOrgPolicyComponent;
|
component = SingleOrgPolicyComponent;
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush
|
|
||||||
// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection
|
|
||||||
@Component({
|
@Component({
|
||||||
|
selector: "single-org-policy-edit",
|
||||||
templateUrl: "single-org.component.html",
|
templateUrl: "single-org.component.html",
|
||||||
imports: [SharedModule],
|
imports: [SharedModule],
|
||||||
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
})
|
})
|
||||||
export class SingleOrgPolicyComponent extends BasePolicyEditComponent implements OnInit {
|
export class SingleOrgPolicyComponent extends BasePolicyEditComponent implements OnInit {
|
||||||
async ngOnInit() {
|
async ngOnInit() {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Component } from "@angular/core";
|
import { ChangeDetectionStrategy, Component } from "@angular/core";
|
||||||
|
|
||||||
import { PolicyType } from "@bitwarden/common/admin-console/enums";
|
import { PolicyType } from "@bitwarden/common/admin-console/enums";
|
||||||
|
|
||||||
@@ -12,10 +12,10 @@ export class TwoFactorAuthenticationPolicy extends BasePolicyEditDefinition {
|
|||||||
component = TwoFactorAuthenticationPolicyComponent;
|
component = TwoFactorAuthenticationPolicyComponent;
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush
|
|
||||||
// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection
|
|
||||||
@Component({
|
@Component({
|
||||||
|
selector: "two-factor-authentication-policy-edit",
|
||||||
templateUrl: "two-factor-authentication.component.html",
|
templateUrl: "two-factor-authentication.component.html",
|
||||||
imports: [SharedModule],
|
imports: [SharedModule],
|
||||||
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
})
|
})
|
||||||
export class TwoFactorAuthenticationPolicyComponent extends BasePolicyEditComponent {}
|
export class TwoFactorAuthenticationPolicyComponent extends BasePolicyEditComponent {}
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ export class UriMatchDefaultPolicy extends BasePolicyEditDefinition {
|
|||||||
component = UriMatchDefaultPolicyComponent;
|
component = UriMatchDefaultPolicyComponent;
|
||||||
}
|
}
|
||||||
@Component({
|
@Component({
|
||||||
|
selector: "uri-match-default-policy-edit",
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
templateUrl: "uri-match-default.component.html",
|
templateUrl: "uri-match-default.component.html",
|
||||||
imports: [SharedModule],
|
imports: [SharedModule],
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Component, OnInit, TemplateRef, ViewChild } from "@angular/core";
|
import { ChangeDetectionStrategy, Component, OnInit, TemplateRef, ViewChild } from "@angular/core";
|
||||||
import { lastValueFrom, Observable } from "rxjs";
|
import { lastValueFrom, Observable } from "rxjs";
|
||||||
|
|
||||||
import { PolicyType } from "@bitwarden/common/admin-console/enums";
|
import { PolicyType } from "@bitwarden/common/admin-console/enums";
|
||||||
@@ -34,11 +34,11 @@ export class vNextOrganizationDataOwnershipPolicy extends BasePolicyEditDefiniti
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush
|
|
||||||
// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection
|
|
||||||
@Component({
|
@Component({
|
||||||
|
selector: "vnext-organization-data-ownership-policy-edit",
|
||||||
templateUrl: "vnext-organization-data-ownership.component.html",
|
templateUrl: "vnext-organization-data-ownership.component.html",
|
||||||
imports: [SharedModule],
|
imports: [SharedModule],
|
||||||
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
})
|
})
|
||||||
export class vNextOrganizationDataOwnershipPolicyComponent
|
export class vNextOrganizationDataOwnershipPolicyComponent
|
||||||
extends BasePolicyEditComponent
|
extends BasePolicyEditComponent
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ export class ActivateAutofillPolicy extends BasePolicyEditDefinition {
|
|||||||
// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush
|
// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush
|
||||||
// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection
|
// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection
|
||||||
@Component({
|
@Component({
|
||||||
|
selector: "activate-autofill-policy-edit",
|
||||||
templateUrl: "activate-autofill.component.html",
|
templateUrl: "activate-autofill.component.html",
|
||||||
imports: [SharedModule],
|
imports: [SharedModule],
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ export class AutomaticAppLoginPolicy extends BasePolicyEditDefinition {
|
|||||||
// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush
|
// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush
|
||||||
// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection
|
// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection
|
||||||
@Component({
|
@Component({
|
||||||
|
selector: "automatic-app-login-policy-edit",
|
||||||
templateUrl: "automatic-app-login.component.html",
|
templateUrl: "automatic-app-login.component.html",
|
||||||
imports: [SharedModule],
|
imports: [SharedModule],
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ export class BlockClaimedDomainAccountCreationPolicy extends BasePolicyEditDefin
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
|
selector: "block-claimed-domain-account-creation-policy-edit",
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
templateUrl: "block-claimed-domain-account-creation.component.html",
|
templateUrl: "block-claimed-domain-account-creation.component.html",
|
||||||
imports: [SharedModule],
|
imports: [SharedModule],
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ export class DisablePersonalVaultExportPolicy extends BasePolicyEditDefinition {
|
|||||||
// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush
|
// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush
|
||||||
// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection
|
// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection
|
||||||
@Component({
|
@Component({
|
||||||
|
selector: "disable-personal-vault-export-policy-edit",
|
||||||
templateUrl: "disable-personal-vault-export.component.html",
|
templateUrl: "disable-personal-vault-export.component.html",
|
||||||
imports: [SharedModule],
|
imports: [SharedModule],
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user