mirror of
https://github.com/bitwarden/browser
synced 2025-12-11 13:53:34 +00:00
[PM-18566] Wire up vNextPolicyService for Clients (#13678)
* wire up vNext impl * wire up vNextPolicyService for browser * wire up vNextPolicyService for desktop * wire up vNextPolicyService for cli * fix test * fix missed caller * cleanup * fix missing property assignment * fix QA bug for PM-19205 * fix QA bug for PM-19206 * fix QA bug for pm-19228 * cleanup
This commit is contained in:
@@ -99,7 +99,7 @@ describe("AccountSecurityComponent", () => {
|
|||||||
|
|
||||||
it("pin enabled when RemoveUnlockWithPin policy is not set", async () => {
|
it("pin enabled when RemoveUnlockWithPin policy is not set", async () => {
|
||||||
// @ts-strict-ignore
|
// @ts-strict-ignore
|
||||||
policyService.get$.mockReturnValue(of(null));
|
policyService.policiesByType$.mockReturnValue(of([null]));
|
||||||
|
|
||||||
await component.ngOnInit();
|
await component.ngOnInit();
|
||||||
|
|
||||||
@@ -111,7 +111,7 @@ describe("AccountSecurityComponent", () => {
|
|||||||
policy.type = PolicyType.RemoveUnlockWithPin;
|
policy.type = PolicyType.RemoveUnlockWithPin;
|
||||||
policy.enabled = false;
|
policy.enabled = false;
|
||||||
|
|
||||||
policyService.get$.mockReturnValue(of(policy));
|
policyService.policiesByType$.mockReturnValue(of([policy]));
|
||||||
|
|
||||||
await component.ngOnInit();
|
await component.ngOnInit();
|
||||||
|
|
||||||
@@ -129,7 +129,7 @@ describe("AccountSecurityComponent", () => {
|
|||||||
policy.type = PolicyType.RemoveUnlockWithPin;
|
policy.type = PolicyType.RemoveUnlockWithPin;
|
||||||
policy.enabled = true;
|
policy.enabled = true;
|
||||||
|
|
||||||
policyService.get$.mockReturnValue(of(policy));
|
policyService.policiesByType$.mockReturnValue(of([policy]));
|
||||||
|
|
||||||
await component.ngOnInit();
|
await component.ngOnInit();
|
||||||
|
|
||||||
@@ -143,7 +143,7 @@ describe("AccountSecurityComponent", () => {
|
|||||||
|
|
||||||
it("pin visible when RemoveUnlockWithPin policy is not set", async () => {
|
it("pin visible when RemoveUnlockWithPin policy is not set", async () => {
|
||||||
// @ts-strict-ignore
|
// @ts-strict-ignore
|
||||||
policyService.get$.mockReturnValue(of(null));
|
policyService.policiesByType$.mockReturnValue(of([null]));
|
||||||
|
|
||||||
await component.ngOnInit();
|
await component.ngOnInit();
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
@@ -158,7 +158,7 @@ describe("AccountSecurityComponent", () => {
|
|||||||
policy.type = PolicyType.RemoveUnlockWithPin;
|
policy.type = PolicyType.RemoveUnlockWithPin;
|
||||||
policy.enabled = false;
|
policy.enabled = false;
|
||||||
|
|
||||||
policyService.get$.mockReturnValue(of(policy));
|
policyService.policiesByType$.mockReturnValue(of([policy]));
|
||||||
|
|
||||||
await component.ngOnInit();
|
await component.ngOnInit();
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
@@ -173,7 +173,7 @@ describe("AccountSecurityComponent", () => {
|
|||||||
policy.type = PolicyType.RemoveUnlockWithPin;
|
policy.type = PolicyType.RemoveUnlockWithPin;
|
||||||
policy.enabled = true;
|
policy.enabled = true;
|
||||||
|
|
||||||
policyService.get$.mockReturnValue(of(policy));
|
policyService.policiesByType$.mockReturnValue(of([policy]));
|
||||||
|
|
||||||
pinServiceAbstraction.isPinSet.mockResolvedValue(true);
|
pinServiceAbstraction.isPinSet.mockResolvedValue(true);
|
||||||
|
|
||||||
@@ -190,7 +190,7 @@ describe("AccountSecurityComponent", () => {
|
|||||||
policy.type = PolicyType.RemoveUnlockWithPin;
|
policy.type = PolicyType.RemoveUnlockWithPin;
|
||||||
policy.enabled = true;
|
policy.enabled = true;
|
||||||
|
|
||||||
policyService.get$.mockReturnValue(of(policy));
|
policyService.policiesByType$.mockReturnValue(of([policy]));
|
||||||
|
|
||||||
await component.ngOnInit();
|
await component.ngOnInit();
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
|
|||||||
@@ -27,8 +27,10 @@ import { FingerprintDialogComponent, VaultTimeoutInputComponent } from "@bitward
|
|||||||
import { PinServiceAbstraction } from "@bitwarden/auth/common";
|
import { PinServiceAbstraction } from "@bitwarden/auth/common";
|
||||||
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 { getFirstPolicy } from "@bitwarden/common/admin-console/services/policy/default-policy.service";
|
||||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||||
import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction";
|
import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction";
|
||||||
|
import { getUserId } from "@bitwarden/common/auth/services/account.service";
|
||||||
import { DeviceType } from "@bitwarden/common/enums";
|
import { DeviceType } from "@bitwarden/common/enums";
|
||||||
import {
|
import {
|
||||||
VaultTimeout,
|
VaultTimeout,
|
||||||
@@ -152,8 +154,14 @@ export class AccountSecurityComponent implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
const hasMasterPassword = await this.userVerificationService.hasMasterPassword();
|
const hasMasterPassword = await this.userVerificationService.hasMasterPassword();
|
||||||
this.showMasterPasswordOnClientRestartOption = hasMasterPassword;
|
this.showMasterPasswordOnClientRestartOption = hasMasterPassword;
|
||||||
const maximumVaultTimeoutPolicy = this.policyService.get$(PolicyType.MaximumVaultTimeout);
|
const maximumVaultTimeoutPolicy = this.accountService.activeAccount$.pipe(
|
||||||
if ((await firstValueFrom(this.policyService.get$(PolicyType.MaximumVaultTimeout))) != null) {
|
getUserId,
|
||||||
|
switchMap((userId) =>
|
||||||
|
this.policyService.policiesByType$(PolicyType.MaximumVaultTimeout, userId),
|
||||||
|
),
|
||||||
|
getFirstPolicy,
|
||||||
|
);
|
||||||
|
if ((await firstValueFrom(maximumVaultTimeoutPolicy)) != null) {
|
||||||
this.hasVaultTimeoutPolicy = true;
|
this.hasVaultTimeoutPolicy = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -195,7 +203,12 @@ export class AccountSecurityComponent implements OnInit, OnDestroy {
|
|||||||
timeout = VaultTimeoutStringType.OnRestart;
|
timeout = VaultTimeoutStringType.OnRestart;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.pinEnabled$ = this.policyService.get$(PolicyType.RemoveUnlockWithPin).pipe(
|
this.pinEnabled$ = this.accountService.activeAccount$.pipe(
|
||||||
|
getUserId,
|
||||||
|
switchMap((userId) =>
|
||||||
|
this.policyService.policiesByType$(PolicyType.RemoveUnlockWithPin, userId),
|
||||||
|
),
|
||||||
|
getFirstPolicy,
|
||||||
map((policy) => {
|
map((policy) => {
|
||||||
return policy == null || !policy.enabled;
|
return policy == null || !policy.enabled;
|
||||||
}),
|
}),
|
||||||
|
|||||||
@@ -8,6 +8,9 @@ import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authenticatio
|
|||||||
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
||||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||||
|
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
||||||
|
import { FakeAccountService, mockAccountServiceWith } from "@bitwarden/common/spec";
|
||||||
|
import { UserId } from "@bitwarden/common/types/guid";
|
||||||
|
|
||||||
import { BrowserApi } from "../../platform/browser/browser-api";
|
import { BrowserApi } from "../../platform/browser/browser-api";
|
||||||
import { ScriptInjectorService } from "../../platform/services/abstractions/script-injector.service";
|
import { ScriptInjectorService } from "../../platform/services/abstractions/script-injector.service";
|
||||||
@@ -35,10 +38,12 @@ describe("AutoSubmitLoginBackground", () => {
|
|||||||
let configService: MockProxy<ConfigService>;
|
let configService: MockProxy<ConfigService>;
|
||||||
let platformUtilsService: MockProxy<PlatformUtilsService>;
|
let platformUtilsService: MockProxy<PlatformUtilsService>;
|
||||||
let policyDetails: MockProxy<Policy>;
|
let policyDetails: MockProxy<Policy>;
|
||||||
let automaticAppLogInPolicy$: BehaviorSubject<Policy>;
|
let automaticAppLogInPolicy$: BehaviorSubject<Policy[]>;
|
||||||
let policyAppliesToActiveUser$: BehaviorSubject<boolean>;
|
let policyAppliesToUser$: BehaviorSubject<boolean>;
|
||||||
let policyService: MockProxy<PolicyService>;
|
let policyService: MockProxy<PolicyService>;
|
||||||
let autoSubmitLoginBackground: AutoSubmitLoginBackground;
|
let autoSubmitLoginBackground: AutoSubmitLoginBackground;
|
||||||
|
let accountService: FakeAccountService;
|
||||||
|
const mockUserId = Utils.newGuid() as UserId;
|
||||||
const validIpdUrl1 = "https://example.com";
|
const validIpdUrl1 = "https://example.com";
|
||||||
const validIpdUrl2 = "https://subdomain.example3.com";
|
const validIpdUrl2 = "https://subdomain.example3.com";
|
||||||
const validAutoSubmitHost = "some-valid-url.com";
|
const validAutoSubmitHost = "some-valid-url.com";
|
||||||
@@ -61,12 +66,13 @@ describe("AutoSubmitLoginBackground", () => {
|
|||||||
idpHost: `${validIpdUrl1} , https://example2.com/some/sub-route ,${validIpdUrl2}, [invalidValue] ,,`,
|
idpHost: `${validIpdUrl1} , https://example2.com/some/sub-route ,${validIpdUrl2}, [invalidValue] ,,`,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
automaticAppLogInPolicy$ = new BehaviorSubject<Policy>(policyDetails);
|
automaticAppLogInPolicy$ = new BehaviorSubject<Policy[]>([policyDetails]);
|
||||||
policyAppliesToActiveUser$ = new BehaviorSubject<boolean>(true);
|
policyAppliesToUser$ = new BehaviorSubject<boolean>(true);
|
||||||
policyService = mock<PolicyService>({
|
policyService = mock<PolicyService>({
|
||||||
get$: jest.fn().mockReturnValue(automaticAppLogInPolicy$),
|
policiesByType$: jest.fn().mockReturnValue(automaticAppLogInPolicy$),
|
||||||
policyAppliesToActiveUser$: jest.fn().mockReturnValue(policyAppliesToActiveUser$),
|
policyAppliesToUser$: jest.fn().mockReturnValue(policyAppliesToUser$),
|
||||||
});
|
});
|
||||||
|
accountService = mockAccountServiceWith(mockUserId);
|
||||||
autoSubmitLoginBackground = new AutoSubmitLoginBackground(
|
autoSubmitLoginBackground = new AutoSubmitLoginBackground(
|
||||||
logService,
|
logService,
|
||||||
autofillService,
|
autofillService,
|
||||||
@@ -75,6 +81,7 @@ describe("AutoSubmitLoginBackground", () => {
|
|||||||
configService,
|
configService,
|
||||||
platformUtilsService,
|
platformUtilsService,
|
||||||
policyService,
|
policyService,
|
||||||
|
accountService,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -84,7 +91,7 @@ describe("AutoSubmitLoginBackground", () => {
|
|||||||
|
|
||||||
describe("when the AutoSubmitLoginBackground feature is disabled", () => {
|
describe("when the AutoSubmitLoginBackground feature is disabled", () => {
|
||||||
it("destroys all event listeners when the AutomaticAppLogIn policy is not enabled", async () => {
|
it("destroys all event listeners when the AutomaticAppLogIn policy is not enabled", async () => {
|
||||||
automaticAppLogInPolicy$.next(mock<Policy>({ ...policyDetails, enabled: false }));
|
automaticAppLogInPolicy$.next([mock<Policy>({ ...policyDetails, enabled: false })]);
|
||||||
|
|
||||||
await autoSubmitLoginBackground.init();
|
await autoSubmitLoginBackground.init();
|
||||||
|
|
||||||
@@ -92,7 +99,7 @@ describe("AutoSubmitLoginBackground", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("destroys all event listeners when the AutomaticAppLogIn policy does not apply to the current user", async () => {
|
it("destroys all event listeners when the AutomaticAppLogIn policy does not apply to the current user", async () => {
|
||||||
policyAppliesToActiveUser$.next(false);
|
policyAppliesToUser$.next(false);
|
||||||
|
|
||||||
await autoSubmitLoginBackground.init();
|
await autoSubmitLoginBackground.init();
|
||||||
|
|
||||||
@@ -100,7 +107,7 @@ describe("AutoSubmitLoginBackground", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("destroys all event listeners when the idpHost is not specified in the AutomaticAppLogIn policy", async () => {
|
it("destroys all event listeners when the idpHost is not specified in the AutomaticAppLogIn policy", async () => {
|
||||||
automaticAppLogInPolicy$.next(mock<Policy>({ ...policyDetails, data: { idpHost: "" } }));
|
automaticAppLogInPolicy$.next([mock<Policy>({ ...policyDetails, data: { idpHost: "" } })]);
|
||||||
|
|
||||||
await autoSubmitLoginBackground.init();
|
await autoSubmitLoginBackground.init();
|
||||||
|
|
||||||
@@ -264,6 +271,7 @@ describe("AutoSubmitLoginBackground", () => {
|
|||||||
configService,
|
configService,
|
||||||
platformUtilsService,
|
platformUtilsService,
|
||||||
policyService,
|
policyService,
|
||||||
|
accountService,
|
||||||
);
|
);
|
||||||
jest.spyOn(BrowserApi, "getTabFromCurrentWindow").mockResolvedValue(tab);
|
jest.spyOn(BrowserApi, "getTabFromCurrentWindow").mockResolvedValue(tab);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,12 +1,15 @@
|
|||||||
// 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 { firstValueFrom } from "rxjs";
|
import { firstValueFrom, switchMap } from "rxjs";
|
||||||
|
|
||||||
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 { Policy } from "@bitwarden/common/admin-console/models/domain/policy";
|
import { Policy } from "@bitwarden/common/admin-console/models/domain/policy";
|
||||||
|
import { getFirstPolicy } from "@bitwarden/common/admin-console/services/policy/default-policy.service";
|
||||||
|
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||||
import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service";
|
import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service";
|
||||||
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
|
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
|
||||||
|
import { getUserId } from "@bitwarden/common/auth/services/account.service";
|
||||||
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
|
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
|
||||||
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
||||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||||
@@ -42,6 +45,7 @@ export class AutoSubmitLoginBackground implements AutoSubmitLoginBackgroundAbstr
|
|||||||
private configService: ConfigService,
|
private configService: ConfigService,
|
||||||
private platformUtilsService: PlatformUtilsService,
|
private platformUtilsService: PlatformUtilsService,
|
||||||
private policyService: PolicyService,
|
private policyService: PolicyService,
|
||||||
|
private accountService: AccountService,
|
||||||
) {
|
) {
|
||||||
this.isSafariBrowser = this.platformUtilsService.isSafari();
|
this.isSafariBrowser = this.platformUtilsService.isSafari();
|
||||||
}
|
}
|
||||||
@@ -56,8 +60,14 @@ export class AutoSubmitLoginBackground implements AutoSubmitLoginBackgroundAbstr
|
|||||||
FeatureFlag.IdpAutoSubmitLogin,
|
FeatureFlag.IdpAutoSubmitLogin,
|
||||||
);
|
);
|
||||||
if (featureFlagEnabled) {
|
if (featureFlagEnabled) {
|
||||||
this.policyService
|
this.accountService.activeAccount$
|
||||||
.get$(PolicyType.AutomaticAppLogIn)
|
.pipe(
|
||||||
|
getUserId,
|
||||||
|
switchMap((userId) =>
|
||||||
|
this.policyService.policiesByType$(PolicyType.AutomaticAppLogIn, userId),
|
||||||
|
),
|
||||||
|
getFirstPolicy,
|
||||||
|
)
|
||||||
.subscribe(this.handleAutoSubmitLoginPolicySubscription.bind(this));
|
.subscribe(this.handleAutoSubmitLoginPolicySubscription.bind(this));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -86,7 +96,12 @@ export class AutoSubmitLoginBackground implements AutoSubmitLoginBackgroundAbstr
|
|||||||
*/
|
*/
|
||||||
private applyPolicyToActiveUser = async (policy: Policy) => {
|
private applyPolicyToActiveUser = async (policy: Policy) => {
|
||||||
const policyAppliesToUser = await firstValueFrom(
|
const policyAppliesToUser = await firstValueFrom(
|
||||||
this.policyService.policyAppliesToActiveUser$(PolicyType.AutomaticAppLogIn),
|
this.accountService.activeAccount$.pipe(
|
||||||
|
getUserId,
|
||||||
|
switchMap((userId) =>
|
||||||
|
this.policyService.policyAppliesToUser$(PolicyType.AutomaticAppLogIn, userId),
|
||||||
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!policyAppliesToUser) {
|
if (!policyAppliesToUser) {
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { mock, MockProxy } from "jest-mock-extended";
|
|||||||
import { BehaviorSubject, firstValueFrom } from "rxjs";
|
import { BehaviorSubject, firstValueFrom } from "rxjs";
|
||||||
|
|
||||||
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
|
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
|
||||||
import { PolicyService } from "@bitwarden/common/admin-console/services/policy/policy.service";
|
import { DefaultPolicyService } from "@bitwarden/common/admin-console/services/policy/default-policy.service";
|
||||||
import { AccountInfo, AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
import { AccountInfo, AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||||
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
|
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
|
||||||
import { AuthService } from "@bitwarden/common/auth/services/auth.service";
|
import { AuthService } from "@bitwarden/common/auth/services/auth.service";
|
||||||
@@ -51,7 +51,7 @@ describe("NotificationBackground", () => {
|
|||||||
const cipherService = mock<CipherService>();
|
const cipherService = mock<CipherService>();
|
||||||
let activeAccountStatusMock$: BehaviorSubject<AuthenticationStatus>;
|
let activeAccountStatusMock$: BehaviorSubject<AuthenticationStatus>;
|
||||||
let authService: MockProxy<AuthService>;
|
let authService: MockProxy<AuthService>;
|
||||||
const policyService = mock<PolicyService>();
|
const policyService = mock<DefaultPolicyService>();
|
||||||
const folderService = mock<FolderService>();
|
const folderService = mock<FolderService>();
|
||||||
const userNotificationSettingsService = mock<UserNotificationSettingsService>();
|
const userNotificationSettingsService = mock<UserNotificationSettingsService>();
|
||||||
const domainSettingsService = mock<DomainSettingsService>();
|
const domainSettingsService = mock<DomainSettingsService>();
|
||||||
|
|||||||
@@ -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 { firstValueFrom } from "rxjs";
|
import { firstValueFrom, switchMap } from "rxjs";
|
||||||
|
|
||||||
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
|
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.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";
|
||||||
@@ -8,7 +8,7 @@ import { PolicyType } from "@bitwarden/common/admin-console/enums";
|
|||||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||||
import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service";
|
import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service";
|
||||||
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
|
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
|
||||||
import { getOptionalUserId } from "@bitwarden/common/auth/services/account.service";
|
import { getOptionalUserId, getUserId } from "@bitwarden/common/auth/services/account.service";
|
||||||
import {
|
import {
|
||||||
ExtensionCommand,
|
ExtensionCommand,
|
||||||
ExtensionCommandType,
|
ExtensionCommandType,
|
||||||
@@ -743,7 +743,12 @@ export default class NotificationBackground {
|
|||||||
|
|
||||||
private async removeIndividualVault(): Promise<boolean> {
|
private async removeIndividualVault(): Promise<boolean> {
|
||||||
return await firstValueFrom(
|
return await firstValueFrom(
|
||||||
this.policyService.policyAppliesToActiveUser$(PolicyType.PersonalOwnership),
|
this.accountService.activeAccount$.pipe(
|
||||||
|
getUserId,
|
||||||
|
switchMap((userId) =>
|
||||||
|
this.policyService.policyAppliesToUser$(PolicyType.PersonalOwnership, userId),
|
||||||
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -26,8 +26,8 @@ import { PolicyApiServiceAbstraction } from "@bitwarden/common/admin-console/abs
|
|||||||
import { InternalPolicyService as InternalPolicyServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
|
import { InternalPolicyService as InternalPolicyServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
|
||||||
import { ProviderService as ProviderServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/provider.service";
|
import { ProviderService as ProviderServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/provider.service";
|
||||||
import { DefaultOrganizationService } from "@bitwarden/common/admin-console/services/organization/default-organization.service";
|
import { DefaultOrganizationService } from "@bitwarden/common/admin-console/services/organization/default-organization.service";
|
||||||
|
import { DefaultPolicyService } from "@bitwarden/common/admin-console/services/policy/default-policy.service";
|
||||||
import { PolicyApiService } from "@bitwarden/common/admin-console/services/policy/policy-api.service";
|
import { PolicyApiService } from "@bitwarden/common/admin-console/services/policy/policy-api.service";
|
||||||
import { PolicyService } from "@bitwarden/common/admin-console/services/policy/policy.service";
|
|
||||||
import { ProviderService } from "@bitwarden/common/admin-console/services/provider.service";
|
import { ProviderService } from "@bitwarden/common/admin-console/services/provider.service";
|
||||||
import { AccountService as AccountServiceAbstraction } from "@bitwarden/common/auth/abstractions/account.service";
|
import { AccountService as AccountServiceAbstraction } from "@bitwarden/common/auth/abstractions/account.service";
|
||||||
import { AuthService as AuthServiceAbstraction } from "@bitwarden/common/auth/abstractions/auth.service";
|
import { AuthService as AuthServiceAbstraction } from "@bitwarden/common/auth/abstractions/auth.service";
|
||||||
@@ -685,7 +685,7 @@ export default class MainBackground {
|
|||||||
|
|
||||||
this.userDecryptionOptionsService = new UserDecryptionOptionsService(this.stateProvider);
|
this.userDecryptionOptionsService = new UserDecryptionOptionsService(this.stateProvider);
|
||||||
this.organizationService = new DefaultOrganizationService(this.stateProvider);
|
this.organizationService = new DefaultOrganizationService(this.stateProvider);
|
||||||
this.policyService = new PolicyService(this.stateProvider, this.organizationService);
|
this.policyService = new DefaultPolicyService(this.stateProvider, this.organizationService);
|
||||||
|
|
||||||
this.vaultTimeoutSettingsService = new DefaultVaultTimeoutSettingsService(
|
this.vaultTimeoutSettingsService = new DefaultVaultTimeoutSettingsService(
|
||||||
this.accountService,
|
this.accountService,
|
||||||
@@ -728,9 +728,14 @@ export default class MainBackground {
|
|||||||
this.autofillSettingsService = new AutofillSettingsService(
|
this.autofillSettingsService = new AutofillSettingsService(
|
||||||
this.stateProvider,
|
this.stateProvider,
|
||||||
this.policyService,
|
this.policyService,
|
||||||
|
this.accountService,
|
||||||
);
|
);
|
||||||
this.badgeSettingsService = new BadgeSettingsService(this.stateProvider);
|
this.badgeSettingsService = new BadgeSettingsService(this.stateProvider);
|
||||||
this.policyApiService = new PolicyApiService(this.policyService, this.apiService);
|
this.policyApiService = new PolicyApiService(
|
||||||
|
this.policyService,
|
||||||
|
this.apiService,
|
||||||
|
this.accountService,
|
||||||
|
);
|
||||||
this.keyConnectorService = new KeyConnectorService(
|
this.keyConnectorService = new KeyConnectorService(
|
||||||
this.accountService,
|
this.accountService,
|
||||||
this.masterPasswordService,
|
this.masterPasswordService,
|
||||||
@@ -1202,6 +1207,7 @@ export default class MainBackground {
|
|||||||
this.configService,
|
this.configService,
|
||||||
this.platformUtilsService,
|
this.platformUtilsService,
|
||||||
this.policyService,
|
this.policyService,
|
||||||
|
this.accountService,
|
||||||
);
|
);
|
||||||
|
|
||||||
const contextMenuClickedHandler = new ContextMenuClickedHandler(
|
const contextMenuClickedHandler = new ContextMenuClickedHandler(
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ describe("FamiliesPolicyService", () => {
|
|||||||
organizationService.organizations$.mockReturnValue(of(organizations));
|
organizationService.organizations$.mockReturnValue(of(organizations));
|
||||||
|
|
||||||
const policies = [{ organizationId: "org1", enabled: true }] as Policy[];
|
const policies = [{ organizationId: "org1", enabled: true }] as Policy[];
|
||||||
policyService.getAll$.mockReturnValue(of(policies));
|
policyService.policiesByType$.mockReturnValue(of(policies));
|
||||||
|
|
||||||
const result = await firstValueFrom(service.isFreeFamilyPolicyEnabled$());
|
const result = await firstValueFrom(service.isFreeFamilyPolicyEnabled$());
|
||||||
expect(result).toBe(true);
|
expect(result).toBe(true);
|
||||||
@@ -64,7 +64,7 @@ describe("FamiliesPolicyService", () => {
|
|||||||
organizationService.organizations$.mockReturnValue(of(organizations));
|
organizationService.organizations$.mockReturnValue(of(organizations));
|
||||||
|
|
||||||
const policies = [{ organizationId: "org1", enabled: false }] as Policy[];
|
const policies = [{ organizationId: "org1", enabled: false }] as Policy[];
|
||||||
policyService.getAll$.mockReturnValue(of(policies));
|
policyService.policiesByType$.mockReturnValue(of(policies));
|
||||||
|
|
||||||
const result = await firstValueFrom(service.isFreeFamilyPolicyEnabled$());
|
const result = await firstValueFrom(service.isFreeFamilyPolicyEnabled$());
|
||||||
expect(result).toBe(false);
|
expect(result).toBe(false);
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ export class FamiliesPolicyService {
|
|||||||
map((organizations) => organizations.find((org) => org.canManageSponsorships)?.id),
|
map((organizations) => organizations.find((org) => org.canManageSponsorships)?.id),
|
||||||
switchMap((enterpriseOrgId) =>
|
switchMap((enterpriseOrgId) =>
|
||||||
this.policyService
|
this.policyService
|
||||||
.getAll$(PolicyType.FreeFamiliesSponsorshipPolicy, userId)
|
.policiesByType$(PolicyType.FreeFamiliesSponsorshipPolicy, userId)
|
||||||
.pipe(
|
.pipe(
|
||||||
map(
|
map(
|
||||||
(policies) =>
|
(policies) =>
|
||||||
|
|||||||
@@ -482,7 +482,7 @@ const safeProviders: SafeProvider[] = [
|
|||||||
safeProvider({
|
safeProvider({
|
||||||
provide: AutofillSettingsServiceAbstraction,
|
provide: AutofillSettingsServiceAbstraction,
|
||||||
useClass: AutofillSettingsService,
|
useClass: AutofillSettingsService,
|
||||||
deps: [StateProvider, PolicyService],
|
deps: [StateProvider, PolicyService, AccountService],
|
||||||
}),
|
}),
|
||||||
safeProvider({
|
safeProvider({
|
||||||
provide: UserNotificationSettingsServiceAbstraction,
|
provide: UserNotificationSettingsServiceAbstraction,
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ describe("SendV2Component", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
policyService = mock<PolicyService>();
|
policyService = mock<PolicyService>();
|
||||||
policyService.policyAppliesToActiveUser$.mockReturnValue(of(true)); // Return `true` by default
|
policyService.policyAppliesToUser$.mockReturnValue(of(true)); // Return `true` by default
|
||||||
|
|
||||||
sendListFiltersService = new SendListFiltersService(mock(), new FormBuilder());
|
sendListFiltersService = new SendListFiltersService(mock(), new FormBuilder());
|
||||||
|
|
||||||
|
|||||||
@@ -2,11 +2,13 @@ import { CommonModule } from "@angular/common";
|
|||||||
import { Component, OnDestroy, OnInit } from "@angular/core";
|
import { Component, OnDestroy, OnInit } from "@angular/core";
|
||||||
import { takeUntilDestroyed } from "@angular/core/rxjs-interop";
|
import { takeUntilDestroyed } from "@angular/core/rxjs-interop";
|
||||||
import { RouterLink } from "@angular/router";
|
import { RouterLink } from "@angular/router";
|
||||||
import { combineLatest } from "rxjs";
|
import { combineLatest, switchMap } from "rxjs";
|
||||||
|
|
||||||
import { JslibModule } from "@bitwarden/angular/jslib.module";
|
import { JslibModule } from "@bitwarden/angular/jslib.module";
|
||||||
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 { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||||
|
import { getUserId } from "@bitwarden/common/auth/services/account.service";
|
||||||
import { SendType } from "@bitwarden/common/tools/send/enums/send-type";
|
import { SendType } from "@bitwarden/common/tools/send/enums/send-type";
|
||||||
import { ButtonModule, CalloutModule, Icons, NoItemsModule } from "@bitwarden/components";
|
import { ButtonModule, CalloutModule, Icons, NoItemsModule } from "@bitwarden/components";
|
||||||
import {
|
import {
|
||||||
@@ -66,6 +68,7 @@ export class SendV2Component implements OnInit, OnDestroy {
|
|||||||
protected sendItemsService: SendItemsService,
|
protected sendItemsService: SendItemsService,
|
||||||
protected sendListFiltersService: SendListFiltersService,
|
protected sendListFiltersService: SendListFiltersService,
|
||||||
private policyService: PolicyService,
|
private policyService: PolicyService,
|
||||||
|
private accountService: AccountService,
|
||||||
) {
|
) {
|
||||||
combineLatest([
|
combineLatest([
|
||||||
this.sendItemsService.emptyList$,
|
this.sendItemsService.emptyList$,
|
||||||
@@ -93,9 +96,14 @@ export class SendV2Component implements OnInit, OnDestroy {
|
|||||||
this.listState = null;
|
this.listState = null;
|
||||||
});
|
});
|
||||||
|
|
||||||
this.policyService
|
this.accountService.activeAccount$
|
||||||
.policyAppliesToActiveUser$(PolicyType.DisableSend)
|
.pipe(
|
||||||
.pipe(takeUntilDestroyed())
|
getUserId,
|
||||||
|
switchMap((userId) =>
|
||||||
|
this.policyService.policyAppliesToUser$(PolicyType.DisableSend, userId),
|
||||||
|
),
|
||||||
|
takeUntilDestroyed(),
|
||||||
|
)
|
||||||
.subscribe((sendsDisabled) => {
|
.subscribe((sendsDisabled) => {
|
||||||
this.sendsDisabled = sendsDisabled;
|
this.sendsDisabled = sendsDisabled;
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ describe("VaultPopupListFiltersService", () => {
|
|||||||
let folderViews$ = new BehaviorSubject([]);
|
let folderViews$ = new BehaviorSubject([]);
|
||||||
const cipherViews$ = new BehaviorSubject({});
|
const cipherViews$ = new BehaviorSubject({});
|
||||||
let decryptedCollections$ = new BehaviorSubject<CollectionView[]>([]);
|
let decryptedCollections$ = new BehaviorSubject<CollectionView[]>([]);
|
||||||
const policyAppliesToActiveUser$ = new BehaviorSubject<boolean>(false);
|
const policyAppliesToUser$ = new BehaviorSubject<boolean>(false);
|
||||||
let viewCacheService: {
|
let viewCacheService: {
|
||||||
signal: jest.Mock;
|
signal: jest.Mock;
|
||||||
mockSignal: WritableSignal<CachedFilterState>;
|
mockSignal: WritableSignal<CachedFilterState>;
|
||||||
@@ -65,7 +65,7 @@ describe("VaultPopupListFiltersService", () => {
|
|||||||
} as I18nService;
|
} as I18nService;
|
||||||
|
|
||||||
const policyService = {
|
const policyService = {
|
||||||
policyAppliesToActiveUser$: jest.fn(() => policyAppliesToActiveUser$),
|
policyAppliesToUser$: jest.fn(() => policyAppliesToUser$),
|
||||||
};
|
};
|
||||||
|
|
||||||
const state$ = new BehaviorSubject<boolean>(false);
|
const state$ = new BehaviorSubject<boolean>(false);
|
||||||
@@ -75,8 +75,8 @@ describe("VaultPopupListFiltersService", () => {
|
|||||||
_memberOrganizations$ = new BehaviorSubject<Organization[]>([]); // Fresh instance per test
|
_memberOrganizations$ = new BehaviorSubject<Organization[]>([]); // Fresh instance per test
|
||||||
folderViews$ = new BehaviorSubject([]); // Fresh instance per test
|
folderViews$ = new BehaviorSubject([]); // Fresh instance per test
|
||||||
decryptedCollections$ = new BehaviorSubject<CollectionView[]>([]); // Fresh instance per test
|
decryptedCollections$ = new BehaviorSubject<CollectionView[]>([]); // Fresh instance per test
|
||||||
policyAppliesToActiveUser$.next(false);
|
policyAppliesToUser$.next(false);
|
||||||
policyService.policyAppliesToActiveUser$.mockClear();
|
policyService.policyAppliesToUser$.mockClear();
|
||||||
|
|
||||||
const accountService = mockAccountServiceWith("userId" as UserId);
|
const accountService = mockAccountServiceWith("userId" as UserId);
|
||||||
const mockCachedSignal = createMockSignal<CachedFilterState>({});
|
const mockCachedSignal = createMockSignal<CachedFilterState>({});
|
||||||
@@ -196,14 +196,15 @@ describe("VaultPopupListFiltersService", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe("PersonalOwnership policy", () => {
|
describe("PersonalOwnership policy", () => {
|
||||||
it('calls policyAppliesToActiveUser$ with "PersonalOwnership"', () => {
|
it('calls policyAppliesToUser$ with "PersonalOwnership"', () => {
|
||||||
expect(policyService.policyAppliesToActiveUser$).toHaveBeenCalledWith(
|
expect(policyService.policyAppliesToUser$).toHaveBeenCalledWith(
|
||||||
PolicyType.PersonalOwnership,
|
PolicyType.PersonalOwnership,
|
||||||
|
"userId",
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("returns an empty array when the policy applies and there is a single organization", (done) => {
|
it("returns an empty array when the policy applies and there is a single organization", (done) => {
|
||||||
policyAppliesToActiveUser$.next(true);
|
policyAppliesToUser$.next(true);
|
||||||
_memberOrganizations$.next([
|
_memberOrganizations$.next([
|
||||||
{ name: "bobby's org", id: "1234-3323-23223" },
|
{ name: "bobby's org", id: "1234-3323-23223" },
|
||||||
] as Organization[]);
|
] as Organization[]);
|
||||||
@@ -215,7 +216,7 @@ describe("VaultPopupListFiltersService", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('adds "myVault" when the policy does not apply and there are multiple organizations', (done) => {
|
it('adds "myVault" when the policy does not apply and there are multiple organizations', (done) => {
|
||||||
policyAppliesToActiveUser$.next(false);
|
policyAppliesToUser$.next(false);
|
||||||
const orgs = [
|
const orgs = [
|
||||||
{ name: "bobby's org", id: "1234-3323-23223" },
|
{ name: "bobby's org", id: "1234-3323-23223" },
|
||||||
{ name: "alice's org", id: "2223-4343-99888" },
|
{ name: "alice's org", id: "2223-4343-99888" },
|
||||||
@@ -234,7 +235,7 @@ describe("VaultPopupListFiltersService", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('does not add "myVault" the policy applies and there are multiple organizations', (done) => {
|
it('does not add "myVault" the policy applies and there are multiple organizations', (done) => {
|
||||||
policyAppliesToActiveUser$.next(true);
|
policyAppliesToUser$.next(true);
|
||||||
const orgs = [
|
const orgs = [
|
||||||
{ name: "bobby's org", id: "1234-3323-23223" },
|
{ name: "bobby's org", id: "1234-3323-23223" },
|
||||||
{ name: "alice's org", id: "2223-3242-99888" },
|
{ name: "alice's org", id: "2223-3242-99888" },
|
||||||
@@ -679,7 +680,7 @@ function createSeededVaultPopupListFiltersService(
|
|||||||
} as any;
|
} as any;
|
||||||
|
|
||||||
const policyServiceMock = {
|
const policyServiceMock = {
|
||||||
policyAppliesToActiveUser$: jest.fn(() => new BehaviorSubject(false)),
|
policyAppliesToUser$: jest.fn(() => new BehaviorSubject(false)),
|
||||||
} as any;
|
} as any;
|
||||||
|
|
||||||
const stateProviderMock = {
|
const stateProviderMock = {
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ import {
|
|||||||
filter,
|
filter,
|
||||||
map,
|
map,
|
||||||
Observable,
|
Observable,
|
||||||
of,
|
|
||||||
shareReplay,
|
shareReplay,
|
||||||
startWith,
|
startWith,
|
||||||
switchMap,
|
switchMap,
|
||||||
@@ -23,6 +22,7 @@ import { PolicyService } from "@bitwarden/common/admin-console/abstractions/poli
|
|||||||
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";
|
||||||
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 { ProductTierType } from "@bitwarden/common/billing/enums";
|
import { ProductTierType } from "@bitwarden/common/billing/enums";
|
||||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||||
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
||||||
@@ -288,68 +288,70 @@ export class VaultPopupListFiltersService {
|
|||||||
/**
|
/**
|
||||||
* Organization array structured to be directly passed to `ChipSelectComponent`
|
* Organization array structured to be directly passed to `ChipSelectComponent`
|
||||||
*/
|
*/
|
||||||
organizations$: Observable<ChipSelectOption<Organization>[]> = combineLatest([
|
|
||||||
|
organizations$: Observable<ChipSelectOption<Organization>[]> =
|
||||||
this.accountService.activeAccount$.pipe(
|
this.accountService.activeAccount$.pipe(
|
||||||
switchMap((account) =>
|
getUserId,
|
||||||
account === null ? of([]) : this.organizationService.memberOrganizations$(account.id),
|
switchMap((userId) =>
|
||||||
|
combineLatest([
|
||||||
|
this.organizationService.memberOrganizations$(userId),
|
||||||
|
this.policyService.policyAppliesToUser$(PolicyType.PersonalOwnership, userId),
|
||||||
|
]),
|
||||||
),
|
),
|
||||||
),
|
map(([orgs, personalOwnershipApplies]): [Organization[], boolean] => [
|
||||||
this.policyService.policyAppliesToActiveUser$(PolicyType.PersonalOwnership),
|
orgs.sort(Utils.getSortFunction(this.i18nService, "name")),
|
||||||
]).pipe(
|
personalOwnershipApplies,
|
||||||
map(([orgs, personalOwnershipApplies]): [Organization[], boolean] => [
|
]),
|
||||||
orgs.sort(Utils.getSortFunction(this.i18nService, "name")),
|
map(([orgs, personalOwnershipApplies]) => {
|
||||||
personalOwnershipApplies,
|
// When there are no organizations return an empty array,
|
||||||
]),
|
// resulting in the org filter being hidden
|
||||||
map(([orgs, personalOwnershipApplies]) => {
|
if (!orgs.length) {
|
||||||
// When there are no organizations return an empty array,
|
return [];
|
||||||
// resulting in the org filter being hidden
|
}
|
||||||
if (!orgs.length) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
// When there is only one organization and personal ownership policy applies,
|
// When there is only one organization and personal ownership policy applies,
|
||||||
// return an empty array, resulting in the org filter being hidden
|
// return an empty array, resulting in the org filter being hidden
|
||||||
if (orgs.length === 1 && personalOwnershipApplies) {
|
if (orgs.length === 1 && personalOwnershipApplies) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
const myVaultOrg: ChipSelectOption<Organization>[] = [];
|
const myVaultOrg: ChipSelectOption<Organization>[] = [];
|
||||||
|
|
||||||
// Only add "My vault" if personal ownership policy does not apply
|
// Only add "My vault" if personal ownership policy does not apply
|
||||||
if (!personalOwnershipApplies) {
|
if (!personalOwnershipApplies) {
|
||||||
myVaultOrg.push({
|
myVaultOrg.push({
|
||||||
value: { id: MY_VAULT_ID } as Organization,
|
value: { id: MY_VAULT_ID } as Organization,
|
||||||
label: this.i18nService.t("myVault"),
|
label: this.i18nService.t("myVault"),
|
||||||
icon: "bwi-user",
|
icon: "bwi-user",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return [
|
return [
|
||||||
...myVaultOrg,
|
...myVaultOrg,
|
||||||
...orgs.map((org) => {
|
...orgs.map((org) => {
|
||||||
let icon = "bwi-business";
|
let icon = "bwi-business";
|
||||||
|
|
||||||
if (!org.enabled) {
|
if (!org.enabled) {
|
||||||
// Show a warning icon if the organization is deactivated
|
// Show a warning icon if the organization is deactivated
|
||||||
icon = "bwi-exclamation-triangle tw-text-danger";
|
icon = "bwi-exclamation-triangle tw-text-danger";
|
||||||
} else if (
|
} else if (
|
||||||
org.productTierType === ProductTierType.Families ||
|
org.productTierType === ProductTierType.Families ||
|
||||||
org.productTierType === ProductTierType.Free
|
org.productTierType === ProductTierType.Free
|
||||||
) {
|
) {
|
||||||
// Show a family icon if the organization is a family or free org
|
// Show a family icon if the organization is a family or free org
|
||||||
icon = "bwi-family";
|
icon = "bwi-family";
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
value: org,
|
value: org,
|
||||||
label: org.name,
|
label: org.name,
|
||||||
icon,
|
icon,
|
||||||
};
|
};
|
||||||
}),
|
}),
|
||||||
];
|
];
|
||||||
}),
|
}),
|
||||||
shareReplay({ refCount: true, bufferSize: 1 }),
|
shareReplay({ refCount: true, bufferSize: 1 }),
|
||||||
);
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Folder array structured to be directly passed to `ChipSelectComponent`
|
* Folder array structured to be directly passed to `ChipSelectComponent`
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import * as http from "http";
|
|||||||
import { OptionValues } from "commander";
|
import { OptionValues } from "commander";
|
||||||
import * as inquirer from "inquirer";
|
import * as inquirer from "inquirer";
|
||||||
import Separator from "inquirer/lib/objects/separator";
|
import Separator from "inquirer/lib/objects/separator";
|
||||||
import { firstValueFrom, map } from "rxjs";
|
import { firstValueFrom, map, switchMap } from "rxjs";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
LoginStrategyServiceAbstraction,
|
LoginStrategyServiceAbstraction,
|
||||||
@@ -29,6 +29,7 @@ import { TokenTwoFactorRequest } from "@bitwarden/common/auth/models/request/ide
|
|||||||
import { PasswordRequest } from "@bitwarden/common/auth/models/request/password.request";
|
import { PasswordRequest } from "@bitwarden/common/auth/models/request/password.request";
|
||||||
import { TwoFactorEmailRequest } from "@bitwarden/common/auth/models/request/two-factor-email.request";
|
import { TwoFactorEmailRequest } from "@bitwarden/common/auth/models/request/two-factor-email.request";
|
||||||
import { UpdateTempPasswordRequest } from "@bitwarden/common/auth/models/request/update-temp-password.request";
|
import { UpdateTempPasswordRequest } from "@bitwarden/common/auth/models/request/update-temp-password.request";
|
||||||
|
import { getUserId } from "@bitwarden/common/auth/services/account.service";
|
||||||
import { ClientType } from "@bitwarden/common/enums";
|
import { ClientType } from "@bitwarden/common/enums";
|
||||||
import { KeyConnectorService } from "@bitwarden/common/key-management/key-connector/abstractions/key-connector.service";
|
import { KeyConnectorService } from "@bitwarden/common/key-management/key-connector/abstractions/key-connector.service";
|
||||||
import { ErrorResponse } from "@bitwarden/common/models/response/error.response";
|
import { ErrorResponse } from "@bitwarden/common/models/response/error.response";
|
||||||
@@ -555,7 +556,10 @@ export class LoginCommand {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const enforcedPolicyOptions = await firstValueFrom(
|
const enforcedPolicyOptions = await firstValueFrom(
|
||||||
this.policyService.masterPasswordPolicyOptions$(),
|
this.accountService.activeAccount$.pipe(
|
||||||
|
getUserId,
|
||||||
|
switchMap((userId) => this.policyService.masterPasswordPolicyOptions$(userId)),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
// Verify master password meets policy requirements
|
// Verify master password meets policy requirements
|
||||||
|
|||||||
@@ -28,8 +28,8 @@ import { PolicyApiServiceAbstraction } from "@bitwarden/common/admin-console/abs
|
|||||||
import { ProviderApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/provider/provider-api.service.abstraction";
|
import { ProviderApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/provider/provider-api.service.abstraction";
|
||||||
import { DefaultOrganizationService } from "@bitwarden/common/admin-console/services/organization/default-organization.service";
|
import { DefaultOrganizationService } from "@bitwarden/common/admin-console/services/organization/default-organization.service";
|
||||||
import { OrganizationApiService } from "@bitwarden/common/admin-console/services/organization/organization-api.service";
|
import { OrganizationApiService } from "@bitwarden/common/admin-console/services/organization/organization-api.service";
|
||||||
|
import { DefaultPolicyService } from "@bitwarden/common/admin-console/services/policy/default-policy.service";
|
||||||
import { PolicyApiService } from "@bitwarden/common/admin-console/services/policy/policy-api.service";
|
import { PolicyApiService } from "@bitwarden/common/admin-console/services/policy/policy-api.service";
|
||||||
import { PolicyService } from "@bitwarden/common/admin-console/services/policy/policy.service";
|
|
||||||
import { ProviderApiService } from "@bitwarden/common/admin-console/services/provider/provider-api.service";
|
import { ProviderApiService } from "@bitwarden/common/admin-console/services/provider/provider-api.service";
|
||||||
import { ProviderService } from "@bitwarden/common/admin-console/services/provider.service";
|
import { ProviderService } from "@bitwarden/common/admin-console/services/provider.service";
|
||||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||||
@@ -237,7 +237,7 @@ export class ServiceContainer {
|
|||||||
cryptoFunctionService: NodeCryptoFunctionService;
|
cryptoFunctionService: NodeCryptoFunctionService;
|
||||||
encryptService: EncryptServiceImplementation;
|
encryptService: EncryptServiceImplementation;
|
||||||
authService: AuthService;
|
authService: AuthService;
|
||||||
policyService: PolicyService;
|
policyService: DefaultPolicyService;
|
||||||
policyApiService: PolicyApiServiceAbstraction;
|
policyApiService: PolicyApiServiceAbstraction;
|
||||||
logService: ConsoleLogService;
|
logService: ConsoleLogService;
|
||||||
sendService: SendService;
|
sendService: SendService;
|
||||||
@@ -469,7 +469,7 @@ export class ServiceContainer {
|
|||||||
this.ssoUrlService = new SsoUrlService();
|
this.ssoUrlService = new SsoUrlService();
|
||||||
|
|
||||||
this.organizationService = new DefaultOrganizationService(this.stateProvider);
|
this.organizationService = new DefaultOrganizationService(this.stateProvider);
|
||||||
this.policyService = new PolicyService(this.stateProvider, this.organizationService);
|
this.policyService = new DefaultPolicyService(this.stateProvider, this.organizationService);
|
||||||
|
|
||||||
this.vaultTimeoutSettingsService = new DefaultVaultTimeoutSettingsService(
|
this.vaultTimeoutSettingsService = new DefaultVaultTimeoutSettingsService(
|
||||||
this.accountService,
|
this.accountService,
|
||||||
@@ -560,7 +560,11 @@ export class ServiceContainer {
|
|||||||
|
|
||||||
this.providerService = new ProviderService(this.stateProvider);
|
this.providerService = new ProviderService(this.stateProvider);
|
||||||
|
|
||||||
this.policyApiService = new PolicyApiService(this.policyService, this.apiService);
|
this.policyApiService = new PolicyApiService(
|
||||||
|
this.policyService,
|
||||||
|
this.apiService,
|
||||||
|
this.accountService,
|
||||||
|
);
|
||||||
|
|
||||||
this.keyConnectorService = new KeyConnectorService(
|
this.keyConnectorService = new KeyConnectorService(
|
||||||
this.accountService,
|
this.accountService,
|
||||||
@@ -672,6 +676,7 @@ export class ServiceContainer {
|
|||||||
this.autofillSettingsService = new AutofillSettingsService(
|
this.autofillSettingsService = new AutofillSettingsService(
|
||||||
this.stateProvider,
|
this.stateProvider,
|
||||||
this.policyService,
|
this.policyService,
|
||||||
|
this.accountService,
|
||||||
);
|
);
|
||||||
|
|
||||||
this.cipherService = new CipherService(
|
this.cipherService = new CipherService(
|
||||||
|
|||||||
@@ -2,10 +2,13 @@
|
|||||||
// @ts-strict-ignore
|
// @ts-strict-ignore
|
||||||
import { OptionValues } from "commander";
|
import { OptionValues } from "commander";
|
||||||
import * as inquirer from "inquirer";
|
import * as inquirer from "inquirer";
|
||||||
|
import { firstValueFrom, switchMap } from "rxjs";
|
||||||
|
|
||||||
import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service";
|
import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service";
|
||||||
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 { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||||
|
import { getUserId } from "@bitwarden/common/auth/services/account.service";
|
||||||
import { EventType } from "@bitwarden/common/enums";
|
import { EventType } from "@bitwarden/common/enums";
|
||||||
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
|
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
|
||||||
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
||||||
@@ -26,14 +29,19 @@ export class ExportCommand {
|
|||||||
private exportService: VaultExportServiceAbstraction,
|
private exportService: VaultExportServiceAbstraction,
|
||||||
private policyService: PolicyService,
|
private policyService: PolicyService,
|
||||||
private eventCollectionService: EventCollectionService,
|
private eventCollectionService: EventCollectionService,
|
||||||
|
private accountService: AccountService,
|
||||||
private configService: ConfigService,
|
private configService: ConfigService,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async run(options: OptionValues): Promise<Response> {
|
async run(options: OptionValues): Promise<Response> {
|
||||||
if (
|
const policyApplies$ = this.accountService.activeAccount$.pipe(
|
||||||
options.organizationid == null &&
|
getUserId,
|
||||||
(await this.policyService.policyAppliesToUser(PolicyType.DisablePersonalVaultExport))
|
switchMap((userId) =>
|
||||||
) {
|
this.policyService.policyAppliesToUser$(PolicyType.DisablePersonalVaultExport, userId),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (options.organizationid == null && (await firstValueFrom(policyApplies$))) {
|
||||||
return Response.badRequest(
|
return Response.badRequest(
|
||||||
"One or more organization policies prevents you from exporting your personal vault.",
|
"One or more organization policies prevents you from exporting your personal vault.",
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -501,6 +501,7 @@ export class VaultProgram extends BaseProgram {
|
|||||||
this.serviceContainer.exportService,
|
this.serviceContainer.exportService,
|
||||||
this.serviceContainer.policyService,
|
this.serviceContainer.policyService,
|
||||||
this.serviceContainer.eventCollectionService,
|
this.serviceContainer.eventCollectionService,
|
||||||
|
this.serviceContainer.accountService,
|
||||||
this.serviceContainer.configService,
|
this.serviceContainer.configService,
|
||||||
);
|
);
|
||||||
const response = await command.run(options);
|
const response = await command.run(options);
|
||||||
|
|||||||
@@ -153,7 +153,7 @@ describe("SettingsComponent", () => {
|
|||||||
|
|
||||||
it("pin enabled when RemoveUnlockWithPin policy is not set", async () => {
|
it("pin enabled when RemoveUnlockWithPin policy is not set", async () => {
|
||||||
// @ts-strict-ignore
|
// @ts-strict-ignore
|
||||||
policyService.get$.mockReturnValue(of(null));
|
policyService.policiesByType$.mockReturnValue(of([null]));
|
||||||
|
|
||||||
await component.ngOnInit();
|
await component.ngOnInit();
|
||||||
|
|
||||||
@@ -164,7 +164,7 @@ describe("SettingsComponent", () => {
|
|||||||
const policy = new Policy();
|
const policy = new Policy();
|
||||||
policy.type = PolicyType.RemoveUnlockWithPin;
|
policy.type = PolicyType.RemoveUnlockWithPin;
|
||||||
policy.enabled = false;
|
policy.enabled = false;
|
||||||
policyService.get$.mockReturnValue(of(policy));
|
policyService.policiesByType$.mockReturnValue(of([policy]));
|
||||||
|
|
||||||
await component.ngOnInit();
|
await component.ngOnInit();
|
||||||
|
|
||||||
@@ -175,7 +175,7 @@ describe("SettingsComponent", () => {
|
|||||||
const policy = new Policy();
|
const policy = new Policy();
|
||||||
policy.type = PolicyType.RemoveUnlockWithPin;
|
policy.type = PolicyType.RemoveUnlockWithPin;
|
||||||
policy.enabled = true;
|
policy.enabled = true;
|
||||||
policyService.get$.mockReturnValue(of(policy));
|
policyService.policiesByType$.mockReturnValue(of([policy]));
|
||||||
|
|
||||||
await component.ngOnInit();
|
await component.ngOnInit();
|
||||||
|
|
||||||
@@ -184,7 +184,7 @@ describe("SettingsComponent", () => {
|
|||||||
|
|
||||||
it("pin visible when RemoveUnlockWithPin policy is not set", async () => {
|
it("pin visible when RemoveUnlockWithPin policy is not set", async () => {
|
||||||
// @ts-strict-ignore
|
// @ts-strict-ignore
|
||||||
policyService.get$.mockReturnValue(of(null));
|
policyService.policiesByType$.mockReturnValue(of([null]));
|
||||||
|
|
||||||
await component.ngOnInit();
|
await component.ngOnInit();
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
@@ -201,7 +201,7 @@ describe("SettingsComponent", () => {
|
|||||||
const policy = new Policy();
|
const policy = new Policy();
|
||||||
policy.type = PolicyType.RemoveUnlockWithPin;
|
policy.type = PolicyType.RemoveUnlockWithPin;
|
||||||
policy.enabled = false;
|
policy.enabled = false;
|
||||||
policyService.get$.mockReturnValue(of(policy));
|
policyService.policiesByType$.mockReturnValue(of([policy]));
|
||||||
|
|
||||||
await component.ngOnInit();
|
await component.ngOnInit();
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
@@ -218,7 +218,7 @@ describe("SettingsComponent", () => {
|
|||||||
const policy = new Policy();
|
const policy = new Policy();
|
||||||
policy.type = PolicyType.RemoveUnlockWithPin;
|
policy.type = PolicyType.RemoveUnlockWithPin;
|
||||||
policy.enabled = true;
|
policy.enabled = true;
|
||||||
policyService.get$.mockReturnValue(of(policy));
|
policyService.policiesByType$.mockReturnValue(of([policy]));
|
||||||
pinServiceAbstraction.isPinSet.mockResolvedValue(true);
|
pinServiceAbstraction.isPinSet.mockResolvedValue(true);
|
||||||
|
|
||||||
await component.ngOnInit();
|
await component.ngOnInit();
|
||||||
@@ -236,7 +236,7 @@ describe("SettingsComponent", () => {
|
|||||||
const policy = new Policy();
|
const policy = new Policy();
|
||||||
policy.type = PolicyType.RemoveUnlockWithPin;
|
policy.type = PolicyType.RemoveUnlockWithPin;
|
||||||
policy.enabled = true;
|
policy.enabled = true;
|
||||||
policyService.get$.mockReturnValue(of(policy));
|
policyService.policiesByType$.mockReturnValue(of([policy]));
|
||||||
|
|
||||||
await component.ngOnInit();
|
await component.ngOnInit();
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
@@ -255,7 +255,7 @@ describe("SettingsComponent", () => {
|
|||||||
const policy = new Policy();
|
const policy = new Policy();
|
||||||
policy.type = PolicyType.RemoveUnlockWithPin;
|
policy.type = PolicyType.RemoveUnlockWithPin;
|
||||||
policy.enabled = false;
|
policy.enabled = false;
|
||||||
policyService.get$.mockReturnValue(of(policy));
|
policyService.policiesByType$.mockReturnValue(of([policy]));
|
||||||
platformUtilsService.getDevice.mockReturnValue(DeviceType.WindowsDesktop);
|
platformUtilsService.getDevice.mockReturnValue(DeviceType.WindowsDesktop);
|
||||||
i18nService.t.mockImplementation((id: string) => {
|
i18nService.t.mockImplementation((id: string) => {
|
||||||
if (id === "requirePasswordOnStart") {
|
if (id === "requirePasswordOnStart") {
|
||||||
@@ -290,7 +290,7 @@ describe("SettingsComponent", () => {
|
|||||||
const policy = new Policy();
|
const policy = new Policy();
|
||||||
policy.type = PolicyType.RemoveUnlockWithPin;
|
policy.type = PolicyType.RemoveUnlockWithPin;
|
||||||
policy.enabled = true;
|
policy.enabled = true;
|
||||||
policyService.get$.mockReturnValue(of(policy));
|
policyService.policiesByType$.mockReturnValue(of([policy]));
|
||||||
platformUtilsService.getDevice.mockReturnValue(DeviceType.WindowsDesktop);
|
platformUtilsService.getDevice.mockReturnValue(DeviceType.WindowsDesktop);
|
||||||
i18nService.t.mockImplementation((id: string) => {
|
i18nService.t.mockImplementation((id: string) => {
|
||||||
if (id === "requirePasswordOnStart") {
|
if (id === "requirePasswordOnStart") {
|
||||||
|
|||||||
@@ -17,8 +17,10 @@ import {
|
|||||||
import { PinServiceAbstraction } from "@bitwarden/auth/common";
|
import { PinServiceAbstraction } from "@bitwarden/auth/common";
|
||||||
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 { getFirstPolicy } from "@bitwarden/common/admin-console/services/policy/default-policy.service";
|
||||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||||
import { UserVerificationService as UserVerificationServiceAbstraction } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction";
|
import { UserVerificationService as UserVerificationServiceAbstraction } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction";
|
||||||
|
import { getUserId } from "@bitwarden/common/auth/services/account.service";
|
||||||
import { AutofillSettingsServiceAbstraction } from "@bitwarden/common/autofill/services/autofill-settings.service";
|
import { AutofillSettingsServiceAbstraction } from "@bitwarden/common/autofill/services/autofill-settings.service";
|
||||||
import { DomainSettingsService } from "@bitwarden/common/autofill/services/domain-settings.service";
|
import { DomainSettingsService } from "@bitwarden/common/autofill/services/domain-settings.service";
|
||||||
import { DeviceType } from "@bitwarden/common/enums";
|
import { DeviceType } from "@bitwarden/common/enums";
|
||||||
@@ -235,7 +237,12 @@ export class SettingsComponent implements OnInit, OnDestroy {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Load timeout policy
|
// Load timeout policy
|
||||||
this.vaultTimeoutPolicyCallout = this.policyService.get$(PolicyType.MaximumVaultTimeout).pipe(
|
this.vaultTimeoutPolicyCallout = this.accountService.activeAccount$.pipe(
|
||||||
|
getUserId,
|
||||||
|
switchMap((userId) =>
|
||||||
|
this.policyService.policiesByType$(PolicyType.MaximumVaultTimeout, userId),
|
||||||
|
),
|
||||||
|
getFirstPolicy,
|
||||||
filter((policy) => policy != null),
|
filter((policy) => policy != null),
|
||||||
map((policy) => {
|
map((policy) => {
|
||||||
let timeout;
|
let timeout;
|
||||||
@@ -259,7 +266,12 @@ export class SettingsComponent implements OnInit, OnDestroy {
|
|||||||
// Load initial values
|
// Load initial values
|
||||||
this.userHasPinSet = await this.pinService.isPinSet(activeAccount.id);
|
this.userHasPinSet = await this.pinService.isPinSet(activeAccount.id);
|
||||||
|
|
||||||
this.pinEnabled$ = this.policyService.get$(PolicyType.RemoveUnlockWithPin).pipe(
|
this.pinEnabled$ = this.accountService.activeAccount$.pipe(
|
||||||
|
getUserId,
|
||||||
|
switchMap((userId) =>
|
||||||
|
this.policyService.policiesByType$(PolicyType.RemoveUnlockWithPin, userId),
|
||||||
|
),
|
||||||
|
getFirstPolicy,
|
||||||
map((policy) => {
|
map((policy) => {
|
||||||
return policy == null || !policy.enabled;
|
return policy == null || !policy.enabled;
|
||||||
}),
|
}),
|
||||||
|
|||||||
@@ -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 { firstValueFrom } from "rxjs";
|
import { firstValueFrom, switchMap } from "rxjs";
|
||||||
|
|
||||||
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";
|
||||||
@@ -144,10 +144,14 @@ export class EncryptedMessageHandlerService {
|
|||||||
|
|
||||||
const credentialCreatePayload = payload as CredentialCreatePayload;
|
const credentialCreatePayload = payload as CredentialCreatePayload;
|
||||||
|
|
||||||
if (
|
const policyApplies$ = this.accountService.activeAccount$.pipe(
|
||||||
credentialCreatePayload.name == null ||
|
getUserId,
|
||||||
(await this.policyService.policyAppliesToUser(PolicyType.PersonalOwnership))
|
switchMap((userId) =>
|
||||||
) {
|
this.policyService.policyAppliesToUser$(PolicyType.PersonalOwnership, userId),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (credentialCreatePayload.name == null || (await firstValueFrom(policyApplies$))) {
|
||||||
return { status: "failure" };
|
return { status: "failure" };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import { firstValueFrom, Subject } from "rxjs";
|
|||||||
|
|
||||||
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 { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
|
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
|
||||||
|
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||||
import { BillingApiServiceAbstraction } from "@bitwarden/common/billing/abstractions/billing-api.service.abstraction";
|
import { BillingApiServiceAbstraction } from "@bitwarden/common/billing/abstractions/billing-api.service.abstraction";
|
||||||
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";
|
||||||
@@ -48,6 +49,7 @@ export class VaultFilterComponent
|
|||||||
protected billingApiService: BillingApiServiceAbstraction,
|
protected billingApiService: BillingApiServiceAbstraction,
|
||||||
protected dialogService: DialogService,
|
protected dialogService: DialogService,
|
||||||
protected configService: ConfigService,
|
protected configService: ConfigService,
|
||||||
|
protected accountService: AccountService,
|
||||||
) {
|
) {
|
||||||
super(
|
super(
|
||||||
vaultFilterService,
|
vaultFilterService,
|
||||||
@@ -58,6 +60,7 @@ export class VaultFilterComponent
|
|||||||
billingApiService,
|
billingApiService,
|
||||||
dialogService,
|
dialogService,
|
||||||
configService,
|
configService,
|
||||||
|
accountService,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -118,7 +118,10 @@ export class OrganizationLayoutComponent implements OnInit {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
this.hideNewOrgButton$ = this.policyService.policyAppliesToActiveUser$(PolicyType.SingleOrg);
|
this.hideNewOrgButton$ = this.accountService.activeAccount$.pipe(
|
||||||
|
getUserId,
|
||||||
|
switchMap((userId) => this.policyService.policyAppliesToUser$(PolicyType.SingleOrg, userId)),
|
||||||
|
);
|
||||||
|
|
||||||
const provider$ = this.organization$.pipe(
|
const provider$ = this.organization$.pipe(
|
||||||
switchMap((organization) => this.providerService.get$(organization.providerId)),
|
switchMap((organization) => this.providerService.get$(organization.providerId)),
|
||||||
|
|||||||
@@ -3,11 +3,13 @@
|
|||||||
import { DIALOG_DATA, DialogConfig, DialogRef } from "@angular/cdk/dialog";
|
import { DIALOG_DATA, DialogConfig, DialogRef } from "@angular/cdk/dialog";
|
||||||
import { Component, Inject, OnDestroy, OnInit, ViewChild } from "@angular/core";
|
import { Component, Inject, OnDestroy, OnInit, ViewChild } from "@angular/core";
|
||||||
import { FormBuilder, Validators } from "@angular/forms";
|
import { FormBuilder, Validators } from "@angular/forms";
|
||||||
import { Subject, takeUntil } from "rxjs";
|
import { Subject, switchMap, takeUntil } from "rxjs";
|
||||||
|
|
||||||
import { PasswordStrengthV2Component } from "@bitwarden/angular/tools/password-strength/password-strength-v2.component";
|
import { PasswordStrengthV2Component } from "@bitwarden/angular/tools/password-strength/password-strength-v2.component";
|
||||||
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 { MasterPasswordPolicyOptions } from "@bitwarden/common/admin-console/models/domain/master-password-policy-options";
|
import { MasterPasswordPolicyOptions } from "@bitwarden/common/admin-console/models/domain/master-password-policy-options";
|
||||||
|
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||||
|
import { getUserId } from "@bitwarden/common/auth/services/account.service";
|
||||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||||
@@ -81,12 +83,16 @@ export class ResetPasswordComponent implements OnInit, OnDestroy {
|
|||||||
private toastService: ToastService,
|
private toastService: ToastService,
|
||||||
private formBuilder: FormBuilder,
|
private formBuilder: FormBuilder,
|
||||||
private dialogRef: DialogRef<ResetPasswordDialogResult>,
|
private dialogRef: DialogRef<ResetPasswordDialogResult>,
|
||||||
|
private accountService: AccountService,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async ngOnInit() {
|
async ngOnInit() {
|
||||||
this.policyService
|
this.accountService.activeAccount$
|
||||||
.masterPasswordPolicyOptions$()
|
.pipe(
|
||||||
.pipe(takeUntil(this.destroy$))
|
getUserId,
|
||||||
|
switchMap((userId) => this.policyService.masterPasswordPolicyOptions$(userId)),
|
||||||
|
takeUntil(this.destroy$),
|
||||||
|
)
|
||||||
.subscribe(
|
.subscribe(
|
||||||
(enforcedPasswordPolicyOptions) =>
|
(enforcedPasswordPolicyOptions) =>
|
||||||
(this.enforcedPolicyOptions = enforcedPasswordPolicyOptions),
|
(this.enforcedPolicyOptions = enforcedPasswordPolicyOptions),
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ import { Organization } from "@bitwarden/common/admin-console/models/domain/orga
|
|||||||
import { Policy } from "@bitwarden/common/admin-console/models/domain/policy";
|
import { Policy } from "@bitwarden/common/admin-console/models/domain/policy";
|
||||||
import { OrganizationKeysRequest } from "@bitwarden/common/admin-console/models/request/organization-keys.request";
|
import { OrganizationKeysRequest } from "@bitwarden/common/admin-console/models/request/organization-keys.request";
|
||||||
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 { BillingApiServiceAbstraction } from "@bitwarden/common/billing/abstractions/billing-api.service.abstraction";
|
import { BillingApiServiceAbstraction } from "@bitwarden/common/billing/abstractions/billing-api.service.abstraction";
|
||||||
import { isNotSelfUpgradable, ProductTierType } from "@bitwarden/common/billing/enums";
|
import { isNotSelfUpgradable, ProductTierType } from "@bitwarden/common/billing/enums";
|
||||||
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
|
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
|
||||||
@@ -168,15 +169,18 @@ export class MembersComponent extends BaseMembersComponent<OrganizationUserView>
|
|||||||
|
|
||||||
this.canUseSecretsManager$ = organization$.pipe(map((org) => org.useSecretsManager));
|
this.canUseSecretsManager$ = organization$.pipe(map((org) => org.useSecretsManager));
|
||||||
|
|
||||||
const policies$ = organization$.pipe(
|
const policies$ = combineLatest([
|
||||||
switchMap((organization) => {
|
this.accountService.activeAccount$.pipe(getUserId),
|
||||||
|
organization$,
|
||||||
|
]).pipe(
|
||||||
|
switchMap(([userId, organization]) => {
|
||||||
if (organization.isProviderUser) {
|
if (organization.isProviderUser) {
|
||||||
return from(this.policyApiService.getPolicies(organization.id)).pipe(
|
return from(this.policyApiService.getPolicies(organization.id)).pipe(
|
||||||
map((response) => Policy.fromListResponse(response)),
|
map((response) => Policy.fromListResponse(response)),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.policyService.policies$;
|
return this.policyService.policies$(userId);
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -8,11 +8,15 @@ import { InternalPolicyService } from "@bitwarden/common/admin-console/abstracti
|
|||||||
import { MasterPasswordPolicyOptions } from "@bitwarden/common/admin-console/models/domain/master-password-policy-options";
|
import { MasterPasswordPolicyOptions } from "@bitwarden/common/admin-console/models/domain/master-password-policy-options";
|
||||||
import { Policy } from "@bitwarden/common/admin-console/models/domain/policy";
|
import { Policy } from "@bitwarden/common/admin-console/models/domain/policy";
|
||||||
import { ResetPasswordPolicyOptions } from "@bitwarden/common/admin-console/models/domain/reset-password-policy-options";
|
import { ResetPasswordPolicyOptions } from "@bitwarden/common/admin-console/models/domain/reset-password-policy-options";
|
||||||
|
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||||
import { SsoLoginServiceAbstraction } from "@bitwarden/common/auth/abstractions/sso-login.service.abstraction";
|
import { SsoLoginServiceAbstraction } from "@bitwarden/common/auth/abstractions/sso-login.service.abstraction";
|
||||||
import { CryptoFunctionService } from "@bitwarden/common/platform/abstractions/crypto-function.service";
|
import { CryptoFunctionService } from "@bitwarden/common/platform/abstractions/crypto-function.service";
|
||||||
import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service";
|
import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service";
|
||||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||||
|
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
||||||
|
import { FakeAccountService, mockAccountServiceWith } from "@bitwarden/common/spec";
|
||||||
|
import { UserId } from "@bitwarden/common/types/guid";
|
||||||
import { PasswordGenerationServiceAbstraction } from "@bitwarden/generator-legacy";
|
import { PasswordGenerationServiceAbstraction } from "@bitwarden/generator-legacy";
|
||||||
|
|
||||||
// FIXME: remove `src` and fix import
|
// FIXME: remove `src` and fix import
|
||||||
@@ -38,6 +42,8 @@ describe("WebLoginComponentService", () => {
|
|||||||
let passwordGenerationService: MockProxy<PasswordGenerationServiceAbstraction>;
|
let passwordGenerationService: MockProxy<PasswordGenerationServiceAbstraction>;
|
||||||
let platformUtilsService: MockProxy<PlatformUtilsService>;
|
let platformUtilsService: MockProxy<PlatformUtilsService>;
|
||||||
let ssoLoginService: MockProxy<SsoLoginServiceAbstraction>;
|
let ssoLoginService: MockProxy<SsoLoginServiceAbstraction>;
|
||||||
|
const mockUserId = Utils.newGuid() as UserId;
|
||||||
|
let accountService: FakeAccountService;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
acceptOrganizationInviteService = mock<AcceptOrganizationInviteService>();
|
acceptOrganizationInviteService = mock<AcceptOrganizationInviteService>();
|
||||||
@@ -50,6 +56,7 @@ describe("WebLoginComponentService", () => {
|
|||||||
passwordGenerationService = mock<PasswordGenerationServiceAbstraction>();
|
passwordGenerationService = mock<PasswordGenerationServiceAbstraction>();
|
||||||
platformUtilsService = mock<PlatformUtilsService>();
|
platformUtilsService = mock<PlatformUtilsService>();
|
||||||
ssoLoginService = mock<SsoLoginServiceAbstraction>();
|
ssoLoginService = mock<SsoLoginServiceAbstraction>();
|
||||||
|
accountService = mockAccountServiceWith(mockUserId);
|
||||||
|
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
providers: [
|
providers: [
|
||||||
@@ -65,6 +72,7 @@ describe("WebLoginComponentService", () => {
|
|||||||
{ provide: PasswordGenerationServiceAbstraction, useValue: passwordGenerationService },
|
{ provide: PasswordGenerationServiceAbstraction, useValue: passwordGenerationService },
|
||||||
{ provide: PlatformUtilsService, useValue: platformUtilsService },
|
{ provide: PlatformUtilsService, useValue: platformUtilsService },
|
||||||
{ provide: SsoLoginServiceAbstraction, useValue: ssoLoginService },
|
{ provide: SsoLoginServiceAbstraction, useValue: ssoLoginService },
|
||||||
|
{ provide: AccountService, useValue: accountService },
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
service = TestBed.inject(WebLoginComponentService);
|
service = TestBed.inject(WebLoginComponentService);
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
// @ts-strict-ignore
|
// @ts-strict-ignore
|
||||||
import { Injectable } from "@angular/core";
|
import { Injectable } from "@angular/core";
|
||||||
import { Router } from "@angular/router";
|
import { Router } from "@angular/router";
|
||||||
import { firstValueFrom } from "rxjs";
|
import { firstValueFrom, switchMap } from "rxjs";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
DefaultLoginComponentService,
|
DefaultLoginComponentService,
|
||||||
@@ -12,7 +12,9 @@ import {
|
|||||||
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 { InternalPolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
|
import { InternalPolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
|
||||||
import { Policy } from "@bitwarden/common/admin-console/models/domain/policy";
|
import { Policy } from "@bitwarden/common/admin-console/models/domain/policy";
|
||||||
|
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||||
import { SsoLoginServiceAbstraction } from "@bitwarden/common/auth/abstractions/sso-login.service.abstraction";
|
import { SsoLoginServiceAbstraction } from "@bitwarden/common/auth/abstractions/sso-login.service.abstraction";
|
||||||
|
import { getUserId } from "@bitwarden/common/auth/services/account.service";
|
||||||
import { CryptoFunctionService } from "@bitwarden/common/platform/abstractions/crypto-function.service";
|
import { CryptoFunctionService } from "@bitwarden/common/platform/abstractions/crypto-function.service";
|
||||||
import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service";
|
import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service";
|
||||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||||
@@ -39,6 +41,7 @@ export class WebLoginComponentService
|
|||||||
platformUtilsService: PlatformUtilsService,
|
platformUtilsService: PlatformUtilsService,
|
||||||
ssoLoginService: SsoLoginServiceAbstraction,
|
ssoLoginService: SsoLoginServiceAbstraction,
|
||||||
private router: Router,
|
private router: Router,
|
||||||
|
private accountService: AccountService,
|
||||||
) {
|
) {
|
||||||
super(
|
super(
|
||||||
cryptoFunctionService,
|
cryptoFunctionService,
|
||||||
@@ -93,7 +96,10 @@ export class WebLoginComponentService
|
|||||||
resetPasswordPolicy[1] && resetPasswordPolicy[0].autoEnrollEnabled;
|
resetPasswordPolicy[1] && resetPasswordPolicy[0].autoEnrollEnabled;
|
||||||
|
|
||||||
const enforcedPasswordPolicyOptions = await firstValueFrom(
|
const enforcedPasswordPolicyOptions = await firstValueFrom(
|
||||||
this.policyService.masterPasswordPolicyOptions$(policies),
|
this.accountService.activeAccount$.pipe(
|
||||||
|
getUserId,
|
||||||
|
switchMap((userId) => this.policyService.masterPasswordPolicyOptions$(userId)),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -10,9 +10,12 @@ import { MasterPasswordPolicyOptions } from "@bitwarden/common/admin-console/mod
|
|||||||
import { Policy } from "@bitwarden/common/admin-console/models/domain/policy";
|
import { Policy } from "@bitwarden/common/admin-console/models/domain/policy";
|
||||||
import { AccountApiService } from "@bitwarden/common/auth/abstractions/account-api.service";
|
import { AccountApiService } from "@bitwarden/common/auth/abstractions/account-api.service";
|
||||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||||
|
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
||||||
import { EncString } from "@bitwarden/common/platform/models/domain/enc-string";
|
import { EncString } from "@bitwarden/common/platform/models/domain/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 { FakeAccountService, mockAccountServiceWith } from "@bitwarden/common/spec";
|
||||||
import { CsprngArray } from "@bitwarden/common/types/csprng";
|
import { CsprngArray } from "@bitwarden/common/types/csprng";
|
||||||
|
import { UserId } from "@bitwarden/common/types/guid";
|
||||||
import { MasterKey, UserKey } from "@bitwarden/common/types/key";
|
import { MasterKey, UserKey } from "@bitwarden/common/types/key";
|
||||||
import { DEFAULT_KDF_CONFIG, KeyService } from "@bitwarden/key-management";
|
import { DEFAULT_KDF_CONFIG, KeyService } from "@bitwarden/key-management";
|
||||||
|
|
||||||
@@ -30,6 +33,8 @@ describe("WebRegistrationFinishService", () => {
|
|||||||
let policyApiService: MockProxy<PolicyApiServiceAbstraction>;
|
let policyApiService: MockProxy<PolicyApiServiceAbstraction>;
|
||||||
let logService: MockProxy<LogService>;
|
let logService: MockProxy<LogService>;
|
||||||
let policyService: MockProxy<PolicyService>;
|
let policyService: MockProxy<PolicyService>;
|
||||||
|
const mockUserId = Utils.newGuid() as UserId;
|
||||||
|
let accountService: FakeAccountService;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
keyService = mock<KeyService>();
|
keyService = mock<KeyService>();
|
||||||
@@ -38,6 +43,7 @@ describe("WebRegistrationFinishService", () => {
|
|||||||
policyApiService = mock<PolicyApiServiceAbstraction>();
|
policyApiService = mock<PolicyApiServiceAbstraction>();
|
||||||
logService = mock<LogService>();
|
logService = mock<LogService>();
|
||||||
policyService = mock<PolicyService>();
|
policyService = mock<PolicyService>();
|
||||||
|
accountService = mockAccountServiceWith(mockUserId);
|
||||||
|
|
||||||
service = new WebRegistrationFinishService(
|
service = new WebRegistrationFinishService(
|
||||||
keyService,
|
keyService,
|
||||||
@@ -46,6 +52,7 @@ describe("WebRegistrationFinishService", () => {
|
|||||||
policyApiService,
|
policyApiService,
|
||||||
logService,
|
logService,
|
||||||
policyService,
|
policyService,
|
||||||
|
accountService,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import { PolicyService } from "@bitwarden/common/admin-console/abstractions/poli
|
|||||||
import { MasterPasswordPolicyOptions } from "@bitwarden/common/admin-console/models/domain/master-password-policy-options";
|
import { MasterPasswordPolicyOptions } from "@bitwarden/common/admin-console/models/domain/master-password-policy-options";
|
||||||
import { Policy } from "@bitwarden/common/admin-console/models/domain/policy";
|
import { Policy } from "@bitwarden/common/admin-console/models/domain/policy";
|
||||||
import { AccountApiService } from "@bitwarden/common/auth/abstractions/account-api.service";
|
import { AccountApiService } from "@bitwarden/common/auth/abstractions/account-api.service";
|
||||||
|
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||||
import { RegisterFinishRequest } from "@bitwarden/common/auth/models/request/registration/register-finish.request";
|
import { RegisterFinishRequest } from "@bitwarden/common/auth/models/request/registration/register-finish.request";
|
||||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||||
import { EncryptedString, EncString } from "@bitwarden/common/platform/models/domain/enc-string";
|
import { EncryptedString, EncString } from "@bitwarden/common/platform/models/domain/enc-string";
|
||||||
@@ -30,6 +31,7 @@ export class WebRegistrationFinishService
|
|||||||
private policyApiService: PolicyApiServiceAbstraction,
|
private policyApiService: PolicyApiServiceAbstraction,
|
||||||
private logService: LogService,
|
private logService: LogService,
|
||||||
private policyService: PolicyService,
|
private policyService: PolicyService,
|
||||||
|
private accountService: AccountService,
|
||||||
) {
|
) {
|
||||||
super(keyService, accountApiService);
|
super(keyService, accountApiService);
|
||||||
}
|
}
|
||||||
@@ -68,7 +70,7 @@ export class WebRegistrationFinishService
|
|||||||
}
|
}
|
||||||
|
|
||||||
const masterPasswordPolicyOpts: MasterPasswordPolicyOptions = await firstValueFrom(
|
const masterPasswordPolicyOpts: MasterPasswordPolicyOptions = await firstValueFrom(
|
||||||
this.policyService.masterPasswordPolicyOptions$(policies),
|
this.policyService.masterPasswordPolicyOptions$(null, policies),
|
||||||
);
|
);
|
||||||
|
|
||||||
return masterPasswordPolicyOpts;
|
return masterPasswordPolicyOpts;
|
||||||
|
|||||||
@@ -3,11 +3,12 @@
|
|||||||
import { DialogConfig, DialogRef, DIALOG_DATA } from "@angular/cdk/dialog";
|
import { DialogConfig, DialogRef, DIALOG_DATA } from "@angular/cdk/dialog";
|
||||||
import { Component, OnDestroy, OnInit, Inject, Input } from "@angular/core";
|
import { Component, OnDestroy, OnInit, Inject, Input } from "@angular/core";
|
||||||
import { FormBuilder, Validators } from "@angular/forms";
|
import { FormBuilder, Validators } from "@angular/forms";
|
||||||
import { takeUntil } from "rxjs";
|
import { switchMap, takeUntil } from "rxjs";
|
||||||
|
|
||||||
import { ChangePasswordComponent } from "@bitwarden/angular/auth/components/change-password.component";
|
import { ChangePasswordComponent } from "@bitwarden/angular/auth/components/change-password.component";
|
||||||
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 { 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 { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/key-management/master-password/abstractions/master-password.service.abstraction";
|
import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/key-management/master-password/abstractions/master-password.service.abstraction";
|
||||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||||
@@ -79,9 +80,12 @@ export class EmergencyAccessTakeoverComponent
|
|||||||
const policies = await this.emergencyAccessService.getGrantorPolicies(
|
const policies = await this.emergencyAccessService.getGrantorPolicies(
|
||||||
this.params.emergencyAccessId,
|
this.params.emergencyAccessId,
|
||||||
);
|
);
|
||||||
this.policyService
|
this.accountService.activeAccount$
|
||||||
.masterPasswordPolicyOptions$(policies)
|
.pipe(
|
||||||
.pipe(takeUntil(this.destroy$))
|
getUserId,
|
||||||
|
switchMap((userId) => this.policyService.masterPasswordPolicyOptions$(userId, policies)),
|
||||||
|
takeUntil(this.destroy$),
|
||||||
|
)
|
||||||
.subscribe((enforcedPolicyOptions) => (this.enforcedPolicyOptions = enforcedPolicyOptions));
|
.subscribe((enforcedPolicyOptions) => (this.enforcedPolicyOptions = enforcedPolicyOptions));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ import { TwoFactorDuoResponse } from "@bitwarden/common/auth/models/response/two
|
|||||||
import { TwoFactorEmailResponse } from "@bitwarden/common/auth/models/response/two-factor-email.response";
|
import { TwoFactorEmailResponse } from "@bitwarden/common/auth/models/response/two-factor-email.response";
|
||||||
import { TwoFactorWebAuthnResponse } from "@bitwarden/common/auth/models/response/two-factor-web-authn.response";
|
import { TwoFactorWebAuthnResponse } from "@bitwarden/common/auth/models/response/two-factor-web-authn.response";
|
||||||
import { TwoFactorYubiKeyResponse } from "@bitwarden/common/auth/models/response/two-factor-yubi-key.response";
|
import { TwoFactorYubiKeyResponse } from "@bitwarden/common/auth/models/response/two-factor-yubi-key.response";
|
||||||
|
import { getUserId } from "@bitwarden/common/auth/services/account.service";
|
||||||
import { TwoFactorProviders } from "@bitwarden/common/auth/services/two-factor.service";
|
import { TwoFactorProviders } from "@bitwarden/common/auth/services/two-factor.service";
|
||||||
import { AuthResponse } from "@bitwarden/common/auth/types/auth-response";
|
import { AuthResponse } from "@bitwarden/common/auth/types/auth-response";
|
||||||
import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service";
|
import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service";
|
||||||
@@ -109,13 +110,17 @@ export class TwoFactorSetupComponent implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
this.providers.sort((a: any, b: any) => a.sort - b.sort);
|
this.providers.sort((a: any, b: any) => a.sort - b.sort);
|
||||||
|
|
||||||
this.policyService
|
this.accountService.activeAccount$
|
||||||
.policyAppliesToActiveUser$(PolicyType.TwoFactorAuthentication)
|
.pipe(
|
||||||
.pipe(takeUntil(this.destroy$))
|
getUserId,
|
||||||
|
switchMap((userId) =>
|
||||||
|
this.policyService.policyAppliesToUser$(PolicyType.TwoFactorAuthentication, userId),
|
||||||
|
),
|
||||||
|
takeUntil(this.destroy$),
|
||||||
|
)
|
||||||
.subscribe((policyAppliesToActiveUser) => {
|
.subscribe((policyAppliesToActiveUser) => {
|
||||||
this.twoFactorAuthPolicyAppliesToActiveUser = policyAppliesToActiveUser;
|
this.twoFactorAuthPolicyAppliesToActiveUser = policyAppliesToActiveUser;
|
||||||
});
|
});
|
||||||
|
|
||||||
await this.load();
|
await this.load();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
// 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, HostBinding, OnDestroy, OnInit } from "@angular/core";
|
import { Component, HostBinding, OnDestroy, OnInit } from "@angular/core";
|
||||||
import { Subject, takeUntil } from "rxjs";
|
import { Subject, switchMap, takeUntil } from "rxjs";
|
||||||
|
|
||||||
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 { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||||
|
import { getUserId } from "@bitwarden/common/auth/services/account.service";
|
||||||
import { DialogService } from "@bitwarden/components";
|
import { DialogService } from "@bitwarden/components";
|
||||||
|
|
||||||
import { WebauthnLoginAdminService } from "../../core";
|
import { WebauthnLoginAdminService } from "../../core";
|
||||||
@@ -35,6 +37,7 @@ export class WebauthnLoginSettingsComponent implements OnInit, OnDestroy {
|
|||||||
private webauthnService: WebauthnLoginAdminService,
|
private webauthnService: WebauthnLoginAdminService,
|
||||||
private dialogService: DialogService,
|
private dialogService: DialogService,
|
||||||
private policyService: PolicyService,
|
private policyService: PolicyService,
|
||||||
|
private accountService: AccountService,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
@HostBinding("attr.aria-busy")
|
@HostBinding("attr.aria-busy")
|
||||||
@@ -57,9 +60,14 @@ export class WebauthnLoginSettingsComponent implements OnInit, OnDestroy {
|
|||||||
requireSsoPolicyEnabled = false;
|
requireSsoPolicyEnabled = false;
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.policyService
|
this.accountService.activeAccount$
|
||||||
.policyAppliesToActiveUser$(PolicyType.RequireSso)
|
.pipe(
|
||||||
.pipe(takeUntil(this.destroy$))
|
getUserId,
|
||||||
|
switchMap((userId) =>
|
||||||
|
this.policyService.policyAppliesToUser$(PolicyType.RequireSso, userId),
|
||||||
|
),
|
||||||
|
takeUntil(this.destroy$),
|
||||||
|
)
|
||||||
.subscribe((enabled) => {
|
.subscribe((enabled) => {
|
||||||
this.requireSsoPolicyEnabled = enabled;
|
this.requireSsoPolicyEnabled = enabled;
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import {
|
|||||||
} from "@angular/core";
|
} from "@angular/core";
|
||||||
import { FormBuilder, Validators } from "@angular/forms";
|
import { FormBuilder, Validators } from "@angular/forms";
|
||||||
import { Router } from "@angular/router";
|
import { Router } from "@angular/router";
|
||||||
import { Subject, firstValueFrom, map, takeUntil } from "rxjs";
|
import { Subject, firstValueFrom, map, switchMap, takeUntil } from "rxjs";
|
||||||
|
|
||||||
import { ManageTaxInformationComponent } from "@bitwarden/angular/billing/components";
|
import { ManageTaxInformationComponent } from "@bitwarden/angular/billing/components";
|
||||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||||
@@ -28,6 +28,7 @@ import { Organization } from "@bitwarden/common/admin-console/models/domain/orga
|
|||||||
import { OrganizationKeysRequest } from "@bitwarden/common/admin-console/models/request/organization-keys.request";
|
import { OrganizationKeysRequest } from "@bitwarden/common/admin-console/models/request/organization-keys.request";
|
||||||
import { OrganizationUpgradeRequest } from "@bitwarden/common/admin-console/models/request/organization-upgrade.request";
|
import { OrganizationUpgradeRequest } from "@bitwarden/common/admin-console/models/request/organization-upgrade.request";
|
||||||
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 {
|
import {
|
||||||
BillingApiServiceAbstraction,
|
BillingApiServiceAbstraction,
|
||||||
BillingInformation,
|
BillingInformation,
|
||||||
@@ -265,9 +266,14 @@ export class ChangePlanDialogComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
this.upgradeFlowPrefillForm();
|
this.upgradeFlowPrefillForm();
|
||||||
|
|
||||||
this.policyService
|
this.accountService.activeAccount$
|
||||||
.policyAppliesToActiveUser$(PolicyType.SingleOrg)
|
.pipe(
|
||||||
.pipe(takeUntil(this.destroy$))
|
getUserId,
|
||||||
|
switchMap((userId) =>
|
||||||
|
this.policyService.policyAppliesToUser$(PolicyType.SingleOrg, userId),
|
||||||
|
),
|
||||||
|
takeUntil(this.destroy$),
|
||||||
|
)
|
||||||
.subscribe((policyAppliesToActiveUser) => {
|
.subscribe((policyAppliesToActiveUser) => {
|
||||||
this.singleOrgPolicyAppliesToActiveUser = policyAppliesToActiveUser;
|
this.singleOrgPolicyAppliesToActiveUser = policyAppliesToActiveUser;
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import {
|
|||||||
import { FormBuilder, Validators } from "@angular/forms";
|
import { FormBuilder, Validators } from "@angular/forms";
|
||||||
import { Router } from "@angular/router";
|
import { Router } from "@angular/router";
|
||||||
import { Subject, firstValueFrom, takeUntil } from "rxjs";
|
import { Subject, firstValueFrom, takeUntil } from "rxjs";
|
||||||
import { debounceTime, map } from "rxjs/operators";
|
import { debounceTime, map, switchMap } from "rxjs/operators";
|
||||||
|
|
||||||
import { ManageTaxInformationComponent } from "@bitwarden/angular/billing/components";
|
import { ManageTaxInformationComponent } from "@bitwarden/angular/billing/components";
|
||||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||||
@@ -31,6 +31,7 @@ import { OrganizationUpgradeRequest } from "@bitwarden/common/admin-console/mode
|
|||||||
import { ProviderOrganizationCreateRequest } from "@bitwarden/common/admin-console/models/request/provider/provider-organization-create.request";
|
import { ProviderOrganizationCreateRequest } from "@bitwarden/common/admin-console/models/request/provider/provider-organization-create.request";
|
||||||
import { ProviderResponse } from "@bitwarden/common/admin-console/models/response/provider/provider.response";
|
import { ProviderResponse } from "@bitwarden/common/admin-console/models/response/provider/provider.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 { BillingApiServiceAbstraction } from "@bitwarden/common/billing/abstractions";
|
import { BillingApiServiceAbstraction } from "@bitwarden/common/billing/abstractions";
|
||||||
import { TaxServiceAbstraction } from "@bitwarden/common/billing/abstractions/tax.service.abstraction";
|
import { TaxServiceAbstraction } from "@bitwarden/common/billing/abstractions/tax.service.abstraction";
|
||||||
import { PaymentMethodType, PlanType, ProductTierType } from "@bitwarden/common/billing/enums";
|
import { PaymentMethodType, PlanType, ProductTierType } from "@bitwarden/common/billing/enums";
|
||||||
@@ -240,9 +241,14 @@ export class OrganizationPlansComponent implements OnInit, OnDestroy {
|
|||||||
this.formGroup.controls.billingEmail.addValidators(Validators.required);
|
this.formGroup.controls.billingEmail.addValidators(Validators.required);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.policyService
|
this.accountService.activeAccount$
|
||||||
.policyAppliesToActiveUser$(PolicyType.SingleOrg)
|
.pipe(
|
||||||
.pipe(takeUntil(this.destroy$))
|
getUserId,
|
||||||
|
switchMap((userId) =>
|
||||||
|
this.policyService.policyAppliesToUser$(PolicyType.SingleOrg, userId),
|
||||||
|
),
|
||||||
|
takeUntil(this.destroy$),
|
||||||
|
)
|
||||||
.subscribe((policyAppliesToActiveUser) => {
|
.subscribe((policyAppliesToActiveUser) => {
|
||||||
this.singleOrgPolicyAppliesToActiveUser = policyAppliesToActiveUser;
|
this.singleOrgPolicyAppliesToActiveUser = policyAppliesToActiveUser;
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -96,7 +96,7 @@ export class FreeFamiliesPolicyService {
|
|||||||
return this.accountService.activeAccount$.pipe(
|
return this.accountService.activeAccount$.pipe(
|
||||||
getUserId,
|
getUserId,
|
||||||
switchMap((userId) =>
|
switchMap((userId) =>
|
||||||
this.policyService.getAll$(PolicyType.FreeFamiliesSponsorshipPolicy, userId),
|
this.policyService.policiesByType$(PolicyType.FreeFamiliesSponsorshipPolicy, userId),
|
||||||
),
|
),
|
||||||
map((policies) => ({
|
map((policies) => ({
|
||||||
isFreeFamilyPolicyEnabled: policies.some(
|
isFreeFamilyPolicyEnabled: policies.some(
|
||||||
|
|||||||
@@ -89,7 +89,7 @@ export class SponsoredFamiliesComponent implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
this.availableSponsorshipOrgs$ = combineLatest([
|
this.availableSponsorshipOrgs$ = combineLatest([
|
||||||
this.organizationService.organizations$(userId),
|
this.organizationService.organizations$(userId),
|
||||||
this.policyService.getAll$(PolicyType.FreeFamiliesSponsorshipPolicy, userId),
|
this.policyService.policiesByType$(PolicyType.FreeFamiliesSponsorshipPolicy, userId),
|
||||||
]).pipe(
|
]).pipe(
|
||||||
map(([organizations, policies]) =>
|
map(([organizations, policies]) =>
|
||||||
organizations
|
organizations
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ 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";
|
||||||
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 { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||||
import { DialogService, ToastService } from "@bitwarden/components";
|
import { DialogService, ToastService } from "@bitwarden/components";
|
||||||
@@ -36,7 +35,6 @@ export class SponsoringOrgRowComponent implements OnInit {
|
|||||||
private logService: LogService,
|
private logService: LogService,
|
||||||
private dialogService: DialogService,
|
private dialogService: DialogService,
|
||||||
private toastService: ToastService,
|
private toastService: ToastService,
|
||||||
private configService: ConfigService,
|
|
||||||
private policyService: PolicyService,
|
private policyService: PolicyService,
|
||||||
private accountService: AccountService,
|
private accountService: AccountService,
|
||||||
) {}
|
) {}
|
||||||
@@ -54,7 +52,7 @@ export class SponsoringOrgRowComponent implements OnInit {
|
|||||||
this.isFreeFamilyPolicyEnabled$ = this.accountService.activeAccount$.pipe(
|
this.isFreeFamilyPolicyEnabled$ = this.accountService.activeAccount$.pipe(
|
||||||
getUserId,
|
getUserId,
|
||||||
switchMap((userId) =>
|
switchMap((userId) =>
|
||||||
this.policyService.getAll$(PolicyType.FreeFamiliesSponsorshipPolicy, userId),
|
this.policyService.policiesByType$(PolicyType.FreeFamiliesSponsorshipPolicy, userId),
|
||||||
),
|
),
|
||||||
map(
|
map(
|
||||||
(policies) =>
|
(policies) =>
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { StepperSelectionEvent } from "@angular/cdk/stepper";
|
|||||||
import { Component, OnDestroy, OnInit, ViewChild } from "@angular/core";
|
import { Component, OnDestroy, OnInit, ViewChild } from "@angular/core";
|
||||||
import { FormBuilder, Validators } from "@angular/forms";
|
import { FormBuilder, Validators } from "@angular/forms";
|
||||||
import { ActivatedRoute, Router } from "@angular/router";
|
import { ActivatedRoute, Router } from "@angular/router";
|
||||||
import { firstValueFrom, Subject, takeUntil } from "rxjs";
|
import { firstValueFrom, Subject, switchMap, takeUntil } from "rxjs";
|
||||||
|
|
||||||
import { PasswordInputResult, RegistrationFinishService } from "@bitwarden/auth/angular";
|
import { PasswordInputResult, RegistrationFinishService } from "@bitwarden/auth/angular";
|
||||||
import { LoginStrategyServiceAbstraction, PasswordLoginCredentials } from "@bitwarden/auth/common";
|
import { LoginStrategyServiceAbstraction, PasswordLoginCredentials } from "@bitwarden/auth/common";
|
||||||
@@ -12,6 +12,8 @@ import { PolicyApiServiceAbstraction } from "@bitwarden/common/admin-console/abs
|
|||||||
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 { MasterPasswordPolicyOptions } from "@bitwarden/common/admin-console/models/domain/master-password-policy-options";
|
import { MasterPasswordPolicyOptions } from "@bitwarden/common/admin-console/models/domain/master-password-policy-options";
|
||||||
import { Policy } from "@bitwarden/common/admin-console/models/domain/policy";
|
import { Policy } from "@bitwarden/common/admin-console/models/domain/policy";
|
||||||
|
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||||
|
import { getUserId } from "@bitwarden/common/auth/services/account.service";
|
||||||
import {
|
import {
|
||||||
OrganizationBillingServiceAbstraction as OrganizationBillingService,
|
OrganizationBillingServiceAbstraction as OrganizationBillingService,
|
||||||
OrganizationInformation,
|
OrganizationInformation,
|
||||||
@@ -106,6 +108,7 @@ export class CompleteTrialInitiationComponent implements OnInit, OnDestroy {
|
|||||||
private validationService: ValidationService,
|
private validationService: ValidationService,
|
||||||
private loginStrategyService: LoginStrategyServiceAbstraction,
|
private loginStrategyService: LoginStrategyServiceAbstraction,
|
||||||
private configService: ConfigService,
|
private configService: ConfigService,
|
||||||
|
private accountService: AccountService,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async ngOnInit(): Promise<void> {
|
async ngOnInit(): Promise<void> {
|
||||||
@@ -173,9 +176,12 @@ export class CompleteTrialInitiationComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (policies !== null) {
|
if (policies !== null) {
|
||||||
this.policyService
|
this.accountService.activeAccount$
|
||||||
.masterPasswordPolicyOptions$(policies)
|
.pipe(
|
||||||
.pipe(takeUntil(this.destroy$))
|
getUserId,
|
||||||
|
switchMap((userId) => this.policyService.masterPasswordPolicyOptions$(userId, policies)),
|
||||||
|
takeUntil(this.destroy$),
|
||||||
|
)
|
||||||
.subscribe((enforcedPasswordPolicyOptions) => {
|
.subscribe((enforcedPasswordPolicyOptions) => {
|
||||||
this.enforcedPolicyOptions = enforcedPasswordPolicyOptions;
|
this.enforcedPolicyOptions = enforcedPasswordPolicyOptions;
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -256,6 +256,7 @@ const safeProviders: SafeProvider[] = [
|
|||||||
PolicyApiServiceAbstraction,
|
PolicyApiServiceAbstraction,
|
||||||
LogService,
|
LogService,
|
||||||
PolicyService,
|
PolicyService,
|
||||||
|
AccountService,
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
safeProvider({
|
safeProvider({
|
||||||
@@ -311,6 +312,7 @@ const safeProviders: SafeProvider[] = [
|
|||||||
PlatformUtilsService,
|
PlatformUtilsService,
|
||||||
SsoLoginServiceAbstraction,
|
SsoLoginServiceAbstraction,
|
||||||
Router,
|
Router,
|
||||||
|
AccountService,
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
safeProvider({
|
safeProvider({
|
||||||
|
|||||||
@@ -1,10 +1,13 @@
|
|||||||
// 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 { Injectable } from "@angular/core";
|
import { Injectable } from "@angular/core";
|
||||||
|
import { switchMap } from "rxjs";
|
||||||
|
|
||||||
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 { Policy } from "@bitwarden/common/admin-console/models/domain/policy";
|
import { Policy } from "@bitwarden/common/admin-console/models/domain/policy";
|
||||||
|
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||||
|
import { getUserId } from "@bitwarden/common/auth/services/account.service";
|
||||||
import { DeviceType, EventType } from "@bitwarden/common/enums";
|
import { DeviceType, EventType } from "@bitwarden/common/enums";
|
||||||
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
|
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
|
||||||
import { EventResponse } from "@bitwarden/common/models/response/event.response";
|
import { EventResponse } from "@bitwarden/common/models/response/event.response";
|
||||||
@@ -19,10 +22,16 @@ export class EventService {
|
|||||||
private i18nService: I18nService,
|
private i18nService: I18nService,
|
||||||
policyService: PolicyService,
|
policyService: PolicyService,
|
||||||
private configService: ConfigService,
|
private configService: ConfigService,
|
||||||
|
private accountService: AccountService,
|
||||||
) {
|
) {
|
||||||
policyService.policies$.subscribe((policies) => {
|
accountService.activeAccount$
|
||||||
this.policies = policies;
|
.pipe(
|
||||||
});
|
getUserId,
|
||||||
|
switchMap((userId) => policyService.policies$(userId)),
|
||||||
|
)
|
||||||
|
.subscribe((policies) => {
|
||||||
|
this.policies = policies;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
getDefaultDateFilters() {
|
getDefaultDateFilters() {
|
||||||
|
|||||||
@@ -2,11 +2,23 @@
|
|||||||
// @ts-strict-ignore
|
// @ts-strict-ignore
|
||||||
import { Component, OnDestroy, OnInit } from "@angular/core";
|
import { Component, OnDestroy, OnInit } from "@angular/core";
|
||||||
import { FormBuilder } from "@angular/forms";
|
import { FormBuilder } from "@angular/forms";
|
||||||
import { concatMap, filter, firstValueFrom, map, Observable, Subject, takeUntil, tap } from "rxjs";
|
import {
|
||||||
|
concatMap,
|
||||||
|
filter,
|
||||||
|
firstValueFrom,
|
||||||
|
map,
|
||||||
|
Observable,
|
||||||
|
Subject,
|
||||||
|
switchMap,
|
||||||
|
takeUntil,
|
||||||
|
tap,
|
||||||
|
} from "rxjs";
|
||||||
|
|
||||||
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 { getFirstPolicy } from "@bitwarden/common/admin-console/services/policy/default-policy.service";
|
||||||
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 { DomainSettingsService } from "@bitwarden/common/autofill/services/domain-settings.service";
|
import { DomainSettingsService } from "@bitwarden/common/autofill/services/domain-settings.service";
|
||||||
import {
|
import {
|
||||||
VaultTimeout,
|
VaultTimeout,
|
||||||
@@ -100,7 +112,12 @@ export class PreferencesComponent implements OnInit, OnDestroy {
|
|||||||
this.availableVaultTimeoutActions$ =
|
this.availableVaultTimeoutActions$ =
|
||||||
this.vaultTimeoutSettingsService.availableVaultTimeoutActions$();
|
this.vaultTimeoutSettingsService.availableVaultTimeoutActions$();
|
||||||
|
|
||||||
this.vaultTimeoutPolicyCallout = this.policyService.get$(PolicyType.MaximumVaultTimeout).pipe(
|
this.vaultTimeoutPolicyCallout = this.accountService.activeAccount$.pipe(
|
||||||
|
getUserId,
|
||||||
|
switchMap((userId) =>
|
||||||
|
this.policyService.policiesByType$(PolicyType.MaximumVaultTimeout, userId),
|
||||||
|
),
|
||||||
|
getFirstPolicy,
|
||||||
filter((policy) => policy != null),
|
filter((policy) => policy != null),
|
||||||
map((policy) => {
|
map((policy) => {
|
||||||
let timeout;
|
let timeout;
|
||||||
|
|||||||
@@ -75,8 +75,10 @@ export class OrganizationOptionsComponent implements OnInit, OnDestroy {
|
|||||||
) {}
|
) {}
|
||||||
|
|
||||||
async ngOnInit() {
|
async ngOnInit() {
|
||||||
const resetPasswordPolicies$ = this.policyService.policies$.pipe(
|
const resetPasswordPolicies$ = this.accountService.activeAccount$.pipe(
|
||||||
map((policies) => policies.filter((policy) => policy.type === PolicyType.ResetPassword)),
|
getUserId,
|
||||||
|
switchMap((userId) => this.policyService.policies$(userId)),
|
||||||
|
map((policies) => policies.filter((p) => p.type == PolicyType.ResetPassword)),
|
||||||
);
|
);
|
||||||
|
|
||||||
const userId = await firstValueFrom(getUserId(this.accountService.activeAccount$));
|
const userId = await firstValueFrom(getUserId(this.accountService.activeAccount$));
|
||||||
|
|||||||
@@ -6,6 +6,9 @@ import { firstValueFrom, merge, Subject, switchMap, takeUntil } from "rxjs";
|
|||||||
|
|
||||||
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 { getFirstPolicy } from "@bitwarden/common/admin-console/services/policy/default-policy.service";
|
||||||
|
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||||
|
import { getUserId } from "@bitwarden/common/auth/services/account.service";
|
||||||
import { BillingApiServiceAbstraction } from "@bitwarden/common/billing/abstractions/billing-api.service.abstraction";
|
import { BillingApiServiceAbstraction } from "@bitwarden/common/billing/abstractions/billing-api.service.abstraction";
|
||||||
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";
|
||||||
@@ -101,6 +104,7 @@ export class VaultFilterComponent implements OnInit, OnDestroy {
|
|||||||
protected billingApiService: BillingApiServiceAbstraction,
|
protected billingApiService: BillingApiServiceAbstraction,
|
||||||
protected dialogService: DialogService,
|
protected dialogService: DialogService,
|
||||||
protected configService: ConfigService,
|
protected configService: ConfigService,
|
||||||
|
protected accountService: AccountService,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async ngOnInit(): Promise<void> {
|
async ngOnInit(): Promise<void> {
|
||||||
@@ -110,10 +114,18 @@ export class VaultFilterComponent implements OnInit, OnDestroy {
|
|||||||
this.isLoaded = true;
|
this.isLoaded = true;
|
||||||
|
|
||||||
// Without refactoring the entire component, we need to manually update the organization filter whenever the policies update
|
// Without refactoring the entire component, we need to manually update the organization filter whenever the policies update
|
||||||
merge(
|
this.accountService.activeAccount$
|
||||||
this.policyService.get$(PolicyType.SingleOrg),
|
.pipe(
|
||||||
this.policyService.get$(PolicyType.PersonalOwnership),
|
getUserId,
|
||||||
)
|
switchMap((userId) =>
|
||||||
|
merge(
|
||||||
|
this.policyService.policiesByType$(PolicyType.SingleOrg, userId).pipe(getFirstPolicy),
|
||||||
|
this.policyService
|
||||||
|
.policiesByType$(PolicyType.PersonalOwnership, userId)
|
||||||
|
.pipe(getFirstPolicy),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
.pipe(
|
.pipe(
|
||||||
switchMap(() => this.addOrganizationFilter()),
|
switchMap(() => this.addOrganizationFilter()),
|
||||||
takeUntil(this.destroy$),
|
takeUntil(this.destroy$),
|
||||||
@@ -190,9 +202,22 @@ export class VaultFilterComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected async addOrganizationFilter(): Promise<VaultFilterSection> {
|
protected async addOrganizationFilter(): Promise<VaultFilterSection> {
|
||||||
const singleOrgPolicy = await this.policyService.policyAppliesToUser(PolicyType.SingleOrg);
|
const singleOrgPolicy = await firstValueFrom(
|
||||||
const personalVaultPolicy = await this.policyService.policyAppliesToUser(
|
this.accountService.activeAccount$.pipe(
|
||||||
PolicyType.PersonalOwnership,
|
getUserId,
|
||||||
|
switchMap((userId) =>
|
||||||
|
this.policyService.policyAppliesToUser$(PolicyType.SingleOrg, userId),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
const personalVaultPolicy = await firstValueFrom(
|
||||||
|
this.accountService.activeAccount$.pipe(
|
||||||
|
getUserId,
|
||||||
|
switchMap((userId) =>
|
||||||
|
this.policyService.policyAppliesToUser$(PolicyType.PersonalOwnership, userId),
|
||||||
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
const addAction = !singleOrgPolicy
|
const addAction = !singleOrgPolicy
|
||||||
|
|||||||
@@ -65,11 +65,11 @@ describe("vault filter service", () => {
|
|||||||
organizationService.memberOrganizations$.mockReturnValue(organizations);
|
organizationService.memberOrganizations$.mockReturnValue(organizations);
|
||||||
folderService.folderViews$.mockReturnValue(folderViews);
|
folderService.folderViews$.mockReturnValue(folderViews);
|
||||||
collectionService.decryptedCollections$ = collectionViews;
|
collectionService.decryptedCollections$ = collectionViews;
|
||||||
policyService.policyAppliesToActiveUser$
|
policyService.policyAppliesToUser$
|
||||||
.calledWith(PolicyType.PersonalOwnership)
|
.calledWith(PolicyType.PersonalOwnership, mockUserId)
|
||||||
.mockReturnValue(personalOwnershipPolicy);
|
.mockReturnValue(personalOwnershipPolicy);
|
||||||
policyService.policyAppliesToActiveUser$
|
policyService.policyAppliesToUser$
|
||||||
.calledWith(PolicyType.SingleOrg)
|
.calledWith(PolicyType.SingleOrg, mockUserId)
|
||||||
.mockReturnValue(singleOrgPolicy);
|
.mockReturnValue(singleOrgPolicy);
|
||||||
cipherService.cipherViews$.mockReturnValue(cipherViews);
|
cipherService.cipherViews$.mockReturnValue(cipherViews);
|
||||||
|
|
||||||
|
|||||||
@@ -55,8 +55,14 @@ export class VaultFilterService implements VaultFilterServiceAbstraction {
|
|||||||
|
|
||||||
organizationTree$: Observable<TreeNode<OrganizationFilter>> = combineLatest([
|
organizationTree$: Observable<TreeNode<OrganizationFilter>> = combineLatest([
|
||||||
this.memberOrganizations$,
|
this.memberOrganizations$,
|
||||||
this.policyService.policyAppliesToActiveUser$(PolicyType.SingleOrg),
|
this.activeUserId$.pipe(
|
||||||
this.policyService.policyAppliesToActiveUser$(PolicyType.PersonalOwnership),
|
switchMap((userId) => this.policyService.policyAppliesToUser$(PolicyType.SingleOrg, userId)),
|
||||||
|
),
|
||||||
|
this.activeUserId$.pipe(
|
||||||
|
switchMap((userId) =>
|
||||||
|
this.policyService.policyAppliesToUser$(PolicyType.PersonalOwnership, userId),
|
||||||
|
),
|
||||||
|
),
|
||||||
]).pipe(
|
]).pipe(
|
||||||
switchMap(([orgs, singleOrgPolicy, personalOwnershipPolicy]) =>
|
switchMap(([orgs, singleOrgPolicy, personalOwnershipPolicy]) =>
|
||||||
this.buildOrganizationTree(orgs, singleOrgPolicy, personalOwnershipPolicy),
|
this.buildOrganizationTree(orgs, singleOrgPolicy, personalOwnershipPolicy),
|
||||||
|
|||||||
@@ -142,13 +142,13 @@ describe("VaultOnboardingComponent", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe("individualVaultPolicyCheck", () => {
|
describe("individualVaultPolicyCheck", () => {
|
||||||
it("should set isIndividualPolicyVault to true", async () => {
|
it("should set isIndividualPolicyVault to true", () => {
|
||||||
individualVaultPolicyCheckSpy.mockRestore();
|
individualVaultPolicyCheckSpy.mockRestore();
|
||||||
const spy = jest
|
const spy = jest
|
||||||
.spyOn((component as any).policyService, "policyAppliesToActiveUser$")
|
.spyOn((component as any).policyService, "policyAppliesToUser$")
|
||||||
.mockReturnValue(of(true));
|
.mockReturnValue(of(true));
|
||||||
|
|
||||||
await component.individualVaultPolicyCheck();
|
component.individualVaultPolicyCheck();
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
expect(spy).toHaveBeenCalled();
|
expect(spy).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import {
|
|||||||
SimpleChanges,
|
SimpleChanges,
|
||||||
OnChanges,
|
OnChanges,
|
||||||
} from "@angular/core";
|
} from "@angular/core";
|
||||||
import { Subject, takeUntil, Observable, firstValueFrom, fromEvent } from "rxjs";
|
import { Subject, takeUntil, Observable, firstValueFrom, fromEvent, switchMap } from "rxjs";
|
||||||
|
|
||||||
import { JslibModule } from "@bitwarden/angular/jslib.module";
|
import { JslibModule } from "@bitwarden/angular/jslib.module";
|
||||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||||
@@ -20,7 +20,6 @@ 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";
|
||||||
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 { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||||
import { UserId } from "@bitwarden/common/types/guid";
|
import { UserId } from "@bitwarden/common/types/guid";
|
||||||
import { CipherType } from "@bitwarden/common/vault/enums/cipher-type";
|
import { CipherType } from "@bitwarden/common/vault/enums/cipher-type";
|
||||||
@@ -67,7 +66,6 @@ export class VaultOnboardingComponent implements OnInit, OnChanges, OnDestroy {
|
|||||||
protected policyService: PolicyService,
|
protected policyService: PolicyService,
|
||||||
private apiService: ApiService,
|
private apiService: ApiService,
|
||||||
private vaultOnboardingService: VaultOnboardingServiceAbstraction,
|
private vaultOnboardingService: VaultOnboardingServiceAbstraction,
|
||||||
private configService: ConfigService,
|
|
||||||
private accountService: AccountService,
|
private accountService: AccountService,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
@@ -165,9 +163,14 @@ export class VaultOnboardingComponent implements OnInit, OnChanges, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
individualVaultPolicyCheck() {
|
individualVaultPolicyCheck() {
|
||||||
this.policyService
|
this.accountService.activeAccount$
|
||||||
.policyAppliesToActiveUser$(PolicyType.PersonalOwnership)
|
.pipe(
|
||||||
.pipe(takeUntil(this.destroy$))
|
getUserId,
|
||||||
|
switchMap((userId) =>
|
||||||
|
this.policyService.policyAppliesToUser$(PolicyType.PersonalOwnership, userId),
|
||||||
|
),
|
||||||
|
takeUntil(this.destroy$),
|
||||||
|
)
|
||||||
.subscribe((data) => {
|
.subscribe((data) => {
|
||||||
this.isIndividualPolicyVault = data;
|
this.isIndividualPolicyVault = data;
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ describe("AdminConsoleCipherFormConfigService", () => {
|
|||||||
status: OrganizationUserStatusType.Confirmed,
|
status: OrganizationUserStatusType.Confirmed,
|
||||||
userId: "UserId",
|
userId: "UserId",
|
||||||
};
|
};
|
||||||
const policyAppliesToActiveUser$ = new BehaviorSubject<boolean>(true);
|
const policyAppliesToUser$ = new BehaviorSubject<boolean>(true);
|
||||||
const collection = {
|
const collection = {
|
||||||
id: "12345-5555",
|
id: "12345-5555",
|
||||||
organizationId: "234534-34334",
|
organizationId: "234534-34334",
|
||||||
@@ -75,7 +75,7 @@ describe("AdminConsoleCipherFormConfigService", () => {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
provide: PolicyService,
|
provide: PolicyService,
|
||||||
useValue: { policyAppliesToActiveUser$: () => policyAppliesToActiveUser$ },
|
useValue: { policyAppliesToUser$: () => policyAppliesToUser$ },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
provide: RoutedVaultFilterService,
|
provide: RoutedVaultFilterService,
|
||||||
@@ -129,13 +129,13 @@ describe("AdminConsoleCipherFormConfigService", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("sets `allowPersonalOwnership`", async () => {
|
it("sets `allowPersonalOwnership`", async () => {
|
||||||
policyAppliesToActiveUser$.next(true);
|
policyAppliesToUser$.next(true);
|
||||||
|
|
||||||
let result = await adminConsoleConfigService.buildConfig("clone", cipherId);
|
let result = await adminConsoleConfigService.buildConfig("clone", cipherId);
|
||||||
|
|
||||||
expect(result.allowPersonalOwnership).toBe(false);
|
expect(result.allowPersonalOwnership).toBe(false);
|
||||||
|
|
||||||
policyAppliesToActiveUser$.next(false);
|
policyAppliesToUser$.next(false);
|
||||||
|
|
||||||
result = await adminConsoleConfigService.buildConfig("clone", cipherId);
|
result = await adminConsoleConfigService.buildConfig("clone", cipherId);
|
||||||
|
|
||||||
@@ -143,7 +143,7 @@ describe("AdminConsoleCipherFormConfigService", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("disables personal ownership when not cloning", async () => {
|
it("disables personal ownership when not cloning", async () => {
|
||||||
policyAppliesToActiveUser$.next(false);
|
policyAppliesToUser$.next(false);
|
||||||
|
|
||||||
let result = await adminConsoleConfigService.buildConfig("add", cipherId);
|
let result = await adminConsoleConfigService.buildConfig("add", cipherId);
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import { PolicyService } from "@bitwarden/common/admin-console/abstractions/poli
|
|||||||
import { OrganizationUserStatusType, PolicyType } from "@bitwarden/common/admin-console/enums";
|
import { OrganizationUserStatusType, 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";
|
||||||
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 { CipherId, UserId } from "@bitwarden/common/types/guid";
|
import { CipherId, UserId } from "@bitwarden/common/types/guid";
|
||||||
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
|
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
|
||||||
import { CipherType } from "@bitwarden/common/vault/enums";
|
import { CipherType } from "@bitwarden/common/vault/enums";
|
||||||
@@ -30,9 +31,13 @@ export class AdminConsoleCipherFormConfigService implements CipherFormConfigServ
|
|||||||
private apiService: ApiService = inject(ApiService);
|
private apiService: ApiService = inject(ApiService);
|
||||||
private accountService: AccountService = inject(AccountService);
|
private accountService: AccountService = inject(AccountService);
|
||||||
|
|
||||||
private allowPersonalOwnership$ = this.policyService
|
private allowPersonalOwnership$ = this.accountService.activeAccount$.pipe(
|
||||||
.policyAppliesToActiveUser$(PolicyType.PersonalOwnership)
|
getUserId,
|
||||||
.pipe(map((p) => !p));
|
switchMap((userId) =>
|
||||||
|
this.policyService.policyAppliesToUser$(PolicyType.PersonalOwnership, userId),
|
||||||
|
),
|
||||||
|
map((p) => !p),
|
||||||
|
);
|
||||||
|
|
||||||
private organizationId$ = this.routedVaultFilterService.filter$.pipe(
|
private organizationId$ = this.routedVaultFilterService.filter$.pipe(
|
||||||
map((filter) => filter.organizationId),
|
map((filter) => filter.organizationId),
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import {
|
|||||||
map,
|
map,
|
||||||
Observable,
|
Observable,
|
||||||
Subject,
|
Subject,
|
||||||
|
switchMap,
|
||||||
take,
|
take,
|
||||||
takeUntil,
|
takeUntil,
|
||||||
withLatestFrom,
|
withLatestFrom,
|
||||||
@@ -18,6 +19,8 @@ import { OrgDomainServiceAbstraction } from "@bitwarden/common/admin-console/abs
|
|||||||
import { OrganizationDomainResponse } from "@bitwarden/common/admin-console/abstractions/organization-domain/responses/organization-domain.response";
|
import { OrganizationDomainResponse } from "@bitwarden/common/admin-console/abstractions/organization-domain/responses/organization-domain.response";
|
||||||
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 { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||||
|
import { getUserId } from "@bitwarden/common/auth/services/account.service";
|
||||||
import { HttpStatusCode } from "@bitwarden/common/enums";
|
import { HttpStatusCode } from "@bitwarden/common/enums";
|
||||||
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
|
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
|
||||||
import { ErrorResponse } from "@bitwarden/common/models/response/error.response";
|
import { ErrorResponse } from "@bitwarden/common/models/response/error.response";
|
||||||
@@ -55,6 +58,7 @@ export class DomainVerificationComponent implements OnInit, OnDestroy {
|
|||||||
private toastService: ToastService,
|
private toastService: ToastService,
|
||||||
private configService: ConfigService,
|
private configService: ConfigService,
|
||||||
private policyService: PolicyService,
|
private policyService: PolicyService,
|
||||||
|
private accountService: AccountService,
|
||||||
) {
|
) {
|
||||||
this.accountDeprovisioningEnabled$ = this.configService.getFeatureFlag$(
|
this.accountDeprovisioningEnabled$ = this.configService.getFeatureFlag$(
|
||||||
FeatureFlag.AccountDeprovisioning,
|
FeatureFlag.AccountDeprovisioning,
|
||||||
@@ -83,7 +87,9 @@ export class DomainVerificationComponent implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
if (await this.configService.getFeatureFlag(FeatureFlag.AccountDeprovisioning)) {
|
if (await this.configService.getFeatureFlag(FeatureFlag.AccountDeprovisioning)) {
|
||||||
const singleOrgPolicy = await firstValueFrom(
|
const singleOrgPolicy = await firstValueFrom(
|
||||||
this.policyService.policies$.pipe(
|
this.accountService.activeAccount$.pipe(
|
||||||
|
getUserId,
|
||||||
|
switchMap((userId) => this.policyService.policies$(userId)),
|
||||||
map((policies) =>
|
map((policies) =>
|
||||||
policies.find(
|
policies.find(
|
||||||
(p) => p.type === PolicyType.SingleOrg && p.organizationId === this.organizationId,
|
(p) => p.type === PolicyType.SingleOrg && p.organizationId === this.organizationId,
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
// 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 { Directive, OnDestroy, OnInit } from "@angular/core";
|
import { Directive, OnDestroy, OnInit } from "@angular/core";
|
||||||
import { Subject, firstValueFrom, map, takeUntil } from "rxjs";
|
import { Subject, firstValueFrom, map, switchMap, takeUntil } from "rxjs";
|
||||||
|
|
||||||
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 { MasterPasswordPolicyOptions } from "@bitwarden/common/admin-console/models/domain/master-password-policy-options";
|
import { MasterPasswordPolicyOptions } from "@bitwarden/common/admin-console/models/domain/master-password-policy-options";
|
||||||
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 { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/key-management/master-password/abstractions/master-password.service.abstraction";
|
import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/key-management/master-password/abstractions/master-password.service.abstraction";
|
||||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||||
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
|
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
|
||||||
@@ -52,9 +53,12 @@ export class ChangePasswordComponent implements OnInit, OnDestroy {
|
|||||||
this.email = await firstValueFrom(
|
this.email = await firstValueFrom(
|
||||||
this.accountService.activeAccount$.pipe(map((a) => a?.email)),
|
this.accountService.activeAccount$.pipe(map((a) => a?.email)),
|
||||||
);
|
);
|
||||||
this.policyService
|
this.accountService.activeAccount$
|
||||||
.masterPasswordPolicyOptions$()
|
.pipe(
|
||||||
.pipe(takeUntil(this.destroy$))
|
getUserId,
|
||||||
|
switchMap((userId) => this.policyService.masterPasswordPolicyOptions$(userId)),
|
||||||
|
takeUntil(this.destroy$),
|
||||||
|
)
|
||||||
.subscribe(
|
.subscribe(
|
||||||
(enforcedPasswordPolicyOptions) =>
|
(enforcedPasswordPolicyOptions) =>
|
||||||
(this.enforcedPolicyOptions ??= enforcedPasswordPolicyOptions),
|
(this.enforcedPolicyOptions ??= enforcedPasswordPolicyOptions),
|
||||||
|
|||||||
@@ -75,12 +75,13 @@ import { OrganizationApiService } from "@bitwarden/common/admin-console/services
|
|||||||
import { OrgDomainApiService } from "@bitwarden/common/admin-console/services/organization-domain/org-domain-api.service";
|
import { OrgDomainApiService } from "@bitwarden/common/admin-console/services/organization-domain/org-domain-api.service";
|
||||||
import { OrgDomainService } from "@bitwarden/common/admin-console/services/organization-domain/org-domain.service";
|
import { OrgDomainService } from "@bitwarden/common/admin-console/services/organization-domain/org-domain.service";
|
||||||
import { DefaultOrganizationManagementPreferencesService } from "@bitwarden/common/admin-console/services/organization-management-preferences/default-organization-management-preferences.service";
|
import { DefaultOrganizationManagementPreferencesService } from "@bitwarden/common/admin-console/services/organization-management-preferences/default-organization-management-preferences.service";
|
||||||
|
import { DefaultPolicyService } from "@bitwarden/common/admin-console/services/policy/default-policy.service";
|
||||||
import { PolicyApiService } from "@bitwarden/common/admin-console/services/policy/policy-api.service";
|
import { PolicyApiService } from "@bitwarden/common/admin-console/services/policy/policy-api.service";
|
||||||
import { PolicyService } from "@bitwarden/common/admin-console/services/policy/policy.service";
|
|
||||||
import { ProviderApiService } from "@bitwarden/common/admin-console/services/provider/provider-api.service";
|
import { ProviderApiService } from "@bitwarden/common/admin-console/services/provider/provider-api.service";
|
||||||
import { ProviderService } from "@bitwarden/common/admin-console/services/provider.service";
|
import { ProviderService } from "@bitwarden/common/admin-console/services/provider.service";
|
||||||
import { AccountApiService as AccountApiServiceAbstraction } from "@bitwarden/common/auth/abstractions/account-api.service";
|
import { AccountApiService as AccountApiServiceAbstraction } from "@bitwarden/common/auth/abstractions/account-api.service";
|
||||||
import {
|
import {
|
||||||
|
AccountService,
|
||||||
AccountService as AccountServiceAbstraction,
|
AccountService as AccountServiceAbstraction,
|
||||||
InternalAccountService,
|
InternalAccountService,
|
||||||
} from "@bitwarden/common/auth/abstractions/account.service";
|
} from "@bitwarden/common/auth/abstractions/account.service";
|
||||||
@@ -947,7 +948,7 @@ const safeProviders: SafeProvider[] = [
|
|||||||
}),
|
}),
|
||||||
safeProvider({
|
safeProvider({
|
||||||
provide: InternalPolicyService,
|
provide: InternalPolicyService,
|
||||||
useClass: PolicyService,
|
useClass: DefaultPolicyService,
|
||||||
deps: [StateProvider, OrganizationServiceAbstraction],
|
deps: [StateProvider, OrganizationServiceAbstraction],
|
||||||
}),
|
}),
|
||||||
safeProvider({
|
safeProvider({
|
||||||
@@ -957,7 +958,7 @@ const safeProviders: SafeProvider[] = [
|
|||||||
safeProvider({
|
safeProvider({
|
||||||
provide: PolicyApiServiceAbstraction,
|
provide: PolicyApiServiceAbstraction,
|
||||||
useClass: PolicyApiService,
|
useClass: PolicyApiService,
|
||||||
deps: [InternalPolicyService, ApiServiceAbstraction],
|
deps: [InternalPolicyService, ApiServiceAbstraction, AccountService],
|
||||||
}),
|
}),
|
||||||
safeProvider({
|
safeProvider({
|
||||||
provide: InternalMasterPasswordServiceAbstraction,
|
provide: InternalMasterPasswordServiceAbstraction,
|
||||||
@@ -1259,7 +1260,7 @@ const safeProviders: SafeProvider[] = [
|
|||||||
safeProvider({
|
safeProvider({
|
||||||
provide: AutofillSettingsServiceAbstraction,
|
provide: AutofillSettingsServiceAbstraction,
|
||||||
useClass: AutofillSettingsService,
|
useClass: AutofillSettingsService,
|
||||||
deps: [StateProvider, PolicyServiceAbstraction],
|
deps: [StateProvider, PolicyServiceAbstraction, AccountService],
|
||||||
}),
|
}),
|
||||||
safeProvider({
|
safeProvider({
|
||||||
provide: BadgeSettingsServiceAbstraction,
|
provide: BadgeSettingsServiceAbstraction,
|
||||||
|
|||||||
@@ -155,9 +155,14 @@ export class AddEditComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async ngOnInit() {
|
async ngOnInit() {
|
||||||
this.policyService
|
this.accountService.activeAccount$
|
||||||
.policyAppliesToActiveUser$(PolicyType.DisableSend)
|
.pipe(
|
||||||
.pipe(takeUntil(this.destroy$))
|
getUserId,
|
||||||
|
switchMap((userId) =>
|
||||||
|
this.policyService.policyAppliesToUser$(PolicyType.DisableSend, userId),
|
||||||
|
),
|
||||||
|
takeUntil(this.destroy$),
|
||||||
|
)
|
||||||
.subscribe((policyAppliesToActiveUser) => {
|
.subscribe((policyAppliesToActiveUser) => {
|
||||||
this.disableSend = policyAppliesToActiveUser;
|
this.disableSend = policyAppliesToActiveUser;
|
||||||
if (this.disableSend) {
|
if (this.disableSend) {
|
||||||
@@ -168,7 +173,7 @@ export class AddEditComponent implements OnInit, OnDestroy {
|
|||||||
this.accountService.activeAccount$
|
this.accountService.activeAccount$
|
||||||
.pipe(
|
.pipe(
|
||||||
getUserId,
|
getUserId,
|
||||||
switchMap((userId) => this.policyService.getAll$(PolicyType.SendOptions, userId)),
|
switchMap((userId) => this.policyService.policiesByType$(PolicyType.SendOptions, userId)),
|
||||||
map((policies) => policies?.some((p) => p.data.disableHideEmail)),
|
map((policies) => policies?.some((p) => p.data.disableHideEmail)),
|
||||||
takeUntil(this.destroy$),
|
takeUntil(this.destroy$),
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import {
|
|||||||
from,
|
from,
|
||||||
switchMap,
|
switchMap,
|
||||||
takeUntil,
|
takeUntil,
|
||||||
|
combineLatest,
|
||||||
} from "rxjs";
|
} from "rxjs";
|
||||||
|
|
||||||
import { SearchService } from "@bitwarden/common/abstractions/search.service";
|
import { SearchService } from "@bitwarden/common/abstractions/search.service";
|
||||||
@@ -85,18 +86,23 @@ export class SendComponent implements OnInit, OnDestroy {
|
|||||||
) {}
|
) {}
|
||||||
|
|
||||||
async ngOnInit() {
|
async ngOnInit() {
|
||||||
const userId = await firstValueFrom(getUserId(this.accountService.activeAccount$));
|
this.accountService.activeAccount$
|
||||||
|
.pipe(
|
||||||
this.policyService
|
getUserId,
|
||||||
.policyAppliesToActiveUser$(PolicyType.DisableSend)
|
switchMap((userId) =>
|
||||||
.pipe(takeUntil(this.destroy$))
|
this.policyService.policyAppliesToUser$(PolicyType.DisableSend, userId),
|
||||||
.subscribe((policyAppliesToActiveUser) => {
|
),
|
||||||
this.disableSend = policyAppliesToActiveUser;
|
takeUntil(this.destroy$),
|
||||||
|
)
|
||||||
|
.subscribe((policyAppliesToUser) => {
|
||||||
|
this.disableSend = policyAppliesToUser;
|
||||||
});
|
});
|
||||||
|
|
||||||
this._searchText$
|
combineLatest([this._searchText$, this.accountService.activeAccount$.pipe(getUserId)])
|
||||||
.pipe(
|
.pipe(
|
||||||
switchMap((searchText) => from(this.searchService.isSearchable(userId, searchText))),
|
switchMap(([searchText, userId]) =>
|
||||||
|
from(this.searchService.isSearchable(userId, searchText)),
|
||||||
|
),
|
||||||
takeUntil(this.destroy$),
|
takeUntil(this.destroy$),
|
||||||
)
|
)
|
||||||
.subscribe((isSearchable) => {
|
.subscribe((isSearchable) => {
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
// @ts-strict-ignore
|
// @ts-strict-ignore
|
||||||
import { DatePipe } from "@angular/common";
|
import { DatePipe } from "@angular/common";
|
||||||
import { Directive, EventEmitter, Input, OnDestroy, OnInit, Output } from "@angular/core";
|
import { Directive, EventEmitter, Input, OnDestroy, OnInit, Output } from "@angular/core";
|
||||||
import { concatMap, firstValueFrom, map, Observable, Subject, takeUntil } from "rxjs";
|
import { concatMap, firstValueFrom, map, Observable, Subject, switchMap, takeUntil } from "rxjs";
|
||||||
|
|
||||||
import { CollectionService, CollectionView } from "@bitwarden/admin-console/common";
|
import { CollectionService, CollectionView } from "@bitwarden/admin-console/common";
|
||||||
import { AuditService } from "@bitwarden/common/abstractions/audit.service";
|
import { AuditService } from "@bitwarden/common/abstractions/audit.service";
|
||||||
@@ -193,9 +193,12 @@ export class AddEditComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async ngOnInit() {
|
async ngOnInit() {
|
||||||
this.policyService
|
this.accountService.activeAccount$
|
||||||
.policyAppliesToActiveUser$(PolicyType.PersonalOwnership)
|
|
||||||
.pipe(
|
.pipe(
|
||||||
|
getUserId,
|
||||||
|
switchMap((userId) =>
|
||||||
|
this.policyService.policyAppliesToUser$(PolicyType.PersonalOwnership, userId),
|
||||||
|
),
|
||||||
concatMap(async (policyAppliesToActiveUser) => {
|
concatMap(async (policyAppliesToActiveUser) => {
|
||||||
this.personalOwnershipPolicyAppliesToActiveUser = policyAppliesToActiveUser;
|
this.personalOwnershipPolicyAppliesToActiveUser = policyAppliesToActiveUser;
|
||||||
await this.init();
|
await this.init();
|
||||||
|
|||||||
@@ -112,13 +112,23 @@ export class VaultFilterService implements DeprecatedVaultFilterServiceAbstracti
|
|||||||
|
|
||||||
async checkForSingleOrganizationPolicy(): Promise<boolean> {
|
async checkForSingleOrganizationPolicy(): Promise<boolean> {
|
||||||
return await firstValueFrom(
|
return await firstValueFrom(
|
||||||
this.policyService.policyAppliesToActiveUser$(PolicyType.SingleOrg),
|
this.accountService.activeAccount$.pipe(
|
||||||
|
getUserId,
|
||||||
|
switchMap((userId) =>
|
||||||
|
this.policyService.policyAppliesToUser$(PolicyType.SingleOrg, userId),
|
||||||
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
async checkForPersonalOwnershipPolicy(): Promise<boolean> {
|
async checkForPersonalOwnershipPolicy(): Promise<boolean> {
|
||||||
return await firstValueFrom(
|
return await firstValueFrom(
|
||||||
this.policyService.policyAppliesToActiveUser$(PolicyType.PersonalOwnership),
|
this.accountService.activeAccount$.pipe(
|
||||||
|
getUserId,
|
||||||
|
switchMap((userId) =>
|
||||||
|
this.policyService.policyAppliesToUser$(PolicyType.PersonalOwnership, userId),
|
||||||
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,25 +2,32 @@ import { ComponentFixture, TestBed } from "@angular/core/testing";
|
|||||||
import { BehaviorSubject } from "rxjs";
|
import { BehaviorSubject } from "rxjs";
|
||||||
|
|
||||||
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 { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||||
import {
|
import {
|
||||||
VaultTimeoutSettingsService,
|
VaultTimeoutSettingsService,
|
||||||
VaultTimeoutStringType,
|
VaultTimeoutStringType,
|
||||||
} from "@bitwarden/common/key-management/vault-timeout";
|
} from "@bitwarden/common/key-management/vault-timeout";
|
||||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||||
|
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
||||||
|
import { mockAccountServiceWith } from "@bitwarden/common/spec";
|
||||||
|
import { UserId } from "@bitwarden/common/types/guid";
|
||||||
|
|
||||||
import { VaultTimeoutInputComponent } from "./vault-timeout-input.component";
|
import { VaultTimeoutInputComponent } from "./vault-timeout-input.component";
|
||||||
|
|
||||||
describe("VaultTimeoutInputComponent", () => {
|
describe("VaultTimeoutInputComponent", () => {
|
||||||
let component: VaultTimeoutInputComponent;
|
let component: VaultTimeoutInputComponent;
|
||||||
let fixture: ComponentFixture<VaultTimeoutInputComponent>;
|
let fixture: ComponentFixture<VaultTimeoutInputComponent>;
|
||||||
const get$ = jest.fn().mockReturnValue(new BehaviorSubject({}));
|
const policiesByType$ = jest.fn().mockReturnValue(new BehaviorSubject({}));
|
||||||
const availableVaultTimeoutActions$ = jest.fn().mockReturnValue(new BehaviorSubject([]));
|
const availableVaultTimeoutActions$ = jest.fn().mockReturnValue(new BehaviorSubject([]));
|
||||||
|
const mockUserId = Utils.newGuid() as UserId;
|
||||||
|
const accountService = mockAccountServiceWith(mockUserId);
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
await TestBed.configureTestingModule({
|
await TestBed.configureTestingModule({
|
||||||
imports: [VaultTimeoutInputComponent],
|
imports: [VaultTimeoutInputComponent],
|
||||||
providers: [
|
providers: [
|
||||||
{ provide: PolicyService, useValue: { get$ } },
|
{ provide: PolicyService, useValue: { policiesByType$ } },
|
||||||
|
{ provide: AccountService, useValue: accountService },
|
||||||
{ provide: VaultTimeoutSettingsService, useValue: { availableVaultTimeoutActions$ } },
|
{ provide: VaultTimeoutSettingsService, useValue: { availableVaultTimeoutActions$ } },
|
||||||
{ provide: I18nService, useValue: { t: (key: string) => key } },
|
{ provide: I18nService, useValue: { t: (key: string) => key } },
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -14,12 +14,15 @@ import {
|
|||||||
ValidationErrors,
|
ValidationErrors,
|
||||||
Validator,
|
Validator,
|
||||||
} from "@angular/forms";
|
} from "@angular/forms";
|
||||||
import { filter, map, Observable, Subject, takeUntil } from "rxjs";
|
import { filter, map, Observable, Subject, switchMap, takeUntil } from "rxjs";
|
||||||
|
|
||||||
import { JslibModule } from "@bitwarden/angular/jslib.module";
|
import { JslibModule } from "@bitwarden/angular/jslib.module";
|
||||||
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 { Policy } from "@bitwarden/common/admin-console/models/domain/policy";
|
import { Policy } from "@bitwarden/common/admin-console/models/domain/policy";
|
||||||
|
import { getFirstPolicy } from "@bitwarden/common/admin-console/services/policy/default-policy.service";
|
||||||
|
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||||
|
import { getUserId } from "@bitwarden/common/auth/services/account.service";
|
||||||
import {
|
import {
|
||||||
VaultTimeout,
|
VaultTimeout,
|
||||||
VaultTimeoutAction,
|
VaultTimeoutAction,
|
||||||
@@ -123,12 +126,17 @@ export class VaultTimeoutInputComponent
|
|||||||
private policyService: PolicyService,
|
private policyService: PolicyService,
|
||||||
private vaultTimeoutSettingsService: VaultTimeoutSettingsService,
|
private vaultTimeoutSettingsService: VaultTimeoutSettingsService,
|
||||||
private i18nService: I18nService,
|
private i18nService: I18nService,
|
||||||
|
private accountService: AccountService,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async ngOnInit() {
|
async ngOnInit() {
|
||||||
this.policyService
|
this.accountService.activeAccount$
|
||||||
.get$(PolicyType.MaximumVaultTimeout)
|
|
||||||
.pipe(
|
.pipe(
|
||||||
|
getUserId,
|
||||||
|
switchMap((userId) =>
|
||||||
|
this.policyService.policiesByType$(PolicyType.MaximumVaultTimeout, userId),
|
||||||
|
),
|
||||||
|
getFirstPolicy,
|
||||||
filter((policy) => policy != null),
|
filter((policy) => policy != null),
|
||||||
takeUntil(this.destroy$),
|
takeUntil(this.destroy$),
|
||||||
)
|
)
|
||||||
@@ -136,7 +144,6 @@ export class VaultTimeoutInputComponent
|
|||||||
this.vaultTimeoutPolicy = policy;
|
this.vaultTimeoutPolicy = policy;
|
||||||
this.applyVaultTimeoutPolicy();
|
this.applyVaultTimeoutPolicy();
|
||||||
});
|
});
|
||||||
|
|
||||||
this.form.valueChanges
|
this.form.valueChanges
|
||||||
.pipe(takeUntil(this.destroy$))
|
.pipe(takeUntil(this.destroy$))
|
||||||
.subscribe((value: VaultTimeoutFormValue) => {
|
.subscribe((value: VaultTimeoutFormValue) => {
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
// FIXME: Update this file to be type safe and remove this and next line
|
|
||||||
// @ts-strict-ignore
|
|
||||||
import { ListResponse } from "../../../models/response/list.response";
|
import { ListResponse } from "../../../models/response/list.response";
|
||||||
import { PolicyType } from "../../enums";
|
import { PolicyType } from "../../enums";
|
||||||
import { MasterPasswordPolicyOptions } from "../../models/domain/master-password-policy-options";
|
import { MasterPasswordPolicyOptions } from "../../models/domain/master-password-policy-options";
|
||||||
@@ -7,19 +5,23 @@ import { Policy } from "../../models/domain/policy";
|
|||||||
import { PolicyRequest } from "../../models/request/policy.request";
|
import { PolicyRequest } from "../../models/request/policy.request";
|
||||||
import { PolicyResponse } from "../../models/response/policy.response";
|
import { PolicyResponse } from "../../models/response/policy.response";
|
||||||
|
|
||||||
export class PolicyApiServiceAbstraction {
|
export abstract class PolicyApiServiceAbstraction {
|
||||||
getPolicy: (organizationId: string, type: PolicyType) => Promise<PolicyResponse>;
|
abstract getPolicy: (organizationId: string, type: PolicyType) => Promise<PolicyResponse>;
|
||||||
getPolicies: (organizationId: string) => Promise<ListResponse<PolicyResponse>>;
|
abstract getPolicies: (organizationId: string) => Promise<ListResponse<PolicyResponse>>;
|
||||||
|
|
||||||
getPoliciesByToken: (
|
abstract getPoliciesByToken: (
|
||||||
organizationId: string,
|
organizationId: string,
|
||||||
token: string,
|
token: string,
|
||||||
email: string,
|
email: string,
|
||||||
organizationUserId: string,
|
organizationUserId: string,
|
||||||
) => Promise<Policy[] | undefined>;
|
) => Promise<Policy[] | undefined>;
|
||||||
|
|
||||||
getMasterPasswordPolicyOptsForOrgUser: (
|
abstract getMasterPasswordPolicyOptsForOrgUser: (
|
||||||
orgId: string,
|
orgId: string,
|
||||||
) => Promise<MasterPasswordPolicyOptions | null>;
|
) => Promise<MasterPasswordPolicyOptions | null>;
|
||||||
putPolicy: (organizationId: string, type: PolicyType, request: PolicyRequest) => Promise<any>;
|
abstract putPolicy: (
|
||||||
|
organizationId: string,
|
||||||
|
type: PolicyType,
|
||||||
|
request: PolicyRequest,
|
||||||
|
) => Promise<any>;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
// FIXME: Update this file to be type safe and remove this and next line
|
|
||||||
// @ts-strict-ignore
|
|
||||||
import { Observable } from "rxjs";
|
import { Observable } from "rxjs";
|
||||||
|
|
||||||
import { UserId } from "../../../types/guid";
|
import { UserId } from "../../../types/guid";
|
||||||
@@ -11,43 +9,27 @@ import { ResetPasswordPolicyOptions } from "../../models/domain/reset-password-p
|
|||||||
|
|
||||||
export abstract class PolicyService {
|
export abstract class PolicyService {
|
||||||
/**
|
/**
|
||||||
* All policies for the active user from sync data.
|
* All policies for the provided user from sync data.
|
||||||
* May include policies that are disabled or otherwise do not apply to the user. Be careful using this!
|
* May include policies that are disabled or otherwise do not apply to the user. Be careful using this!
|
||||||
* Consider using {@link get$} or {@link getAll$} instead, which will only return policies that should be enforced against the user.
|
* Consider {@link policiesByType$} instead, which will only return policies that should be enforced against the user.
|
||||||
*/
|
*/
|
||||||
policies$: Observable<Policy[]>;
|
abstract policies$: (userId: UserId) => Observable<Policy[]>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @returns the first {@link Policy} found that applies to the active user.
|
* @returns all {@link Policy} objects of a given type that apply to the specified user.
|
||||||
* A policy "applies" if it is enabled and the user is not exempt (e.g. because they are an Owner).
|
* A policy "applies" if it is enabled and the user is not exempt (e.g. because they are an Owner).
|
||||||
* @param policyType the {@link PolicyType} to search for
|
* @param policyType the {@link PolicyType} to search for
|
||||||
* @see {@link getAll$} if you need all policies of a given type
|
* @param userId the {@link UserId} to search against
|
||||||
*/
|
*/
|
||||||
get$: (policyType: PolicyType) => Observable<Policy>;
|
abstract policiesByType$: (policyType: PolicyType, userId: UserId) => Observable<Policy[]>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @returns all {@link Policy} objects of a given type that apply to the specified user (or the active user if not specified).
|
* @returns true if a policy of the specified type applies to the specified user, otherwise false.
|
||||||
* A policy "applies" if it is enabled and the user is not exempt (e.g. because they are an Owner).
|
* A policy "applies" if it is enabled and the user is not exempt (e.g. because they are an Owner).
|
||||||
* @param policyType the {@link PolicyType} to search for
|
* This does not take into account the policy's configuration - if that is important, use {@link policiesByType$} to get the
|
||||||
*/
|
|
||||||
getAll$: (policyType: PolicyType, userId: UserId) => Observable<Policy[]>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* All {@link Policy} objects for the specified user (from sync data).
|
|
||||||
* May include policies that are disabled or otherwise do not apply to the user.
|
|
||||||
* Consider using {@link getAll$} instead, which will only return policies that should be enforced against the user.
|
|
||||||
*/
|
|
||||||
getAll: (policyType: PolicyType) => Promise<Policy[]>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @returns true if a policy of the specified type applies to the active user, otherwise false.
|
|
||||||
* A policy "applies" if it is enabled and the user is not exempt (e.g. because they are an Owner).
|
|
||||||
* This does not take into account the policy's configuration - if that is important, use {@link getAll$} to get the
|
|
||||||
* {@link Policy} objects and then filter by Policy.data.
|
* {@link Policy} objects and then filter by Policy.data.
|
||||||
*/
|
*/
|
||||||
policyAppliesToActiveUser$: (policyType: PolicyType) => Observable<boolean>;
|
abstract policyAppliesToUser$: (policyType: PolicyType, userId: UserId) => Observable<boolean>;
|
||||||
|
|
||||||
policyAppliesToUser: (policyType: PolicyType) => Promise<boolean>;
|
|
||||||
|
|
||||||
// Policy specific interfaces
|
// Policy specific interfaces
|
||||||
|
|
||||||
@@ -56,28 +38,31 @@ export abstract class PolicyService {
|
|||||||
* @returns a set of options which represent the minimum Master Password settings that the user must
|
* @returns a set of options which represent the minimum Master Password settings that the user must
|
||||||
* comply with in order to comply with **all** Master Password policies.
|
* comply with in order to comply with **all** Master Password policies.
|
||||||
*/
|
*/
|
||||||
masterPasswordPolicyOptions$: (policies?: Policy[]) => Observable<MasterPasswordPolicyOptions>;
|
abstract masterPasswordPolicyOptions$: (
|
||||||
|
userId: UserId,
|
||||||
|
policies?: Policy[],
|
||||||
|
) => Observable<MasterPasswordPolicyOptions | undefined>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Evaluates whether a proposed Master Password complies with all Master Password policies that apply to the user.
|
* Evaluates whether a proposed Master Password complies with all Master Password policies that apply to the user.
|
||||||
*/
|
*/
|
||||||
evaluateMasterPassword: (
|
abstract evaluateMasterPassword: (
|
||||||
passwordStrength: number,
|
passwordStrength: number,
|
||||||
newPassword: string,
|
newPassword: string,
|
||||||
enforcedPolicyOptions?: MasterPasswordPolicyOptions,
|
enforcedPolicyOptions?: MasterPasswordPolicyOptions,
|
||||||
) => boolean;
|
) => boolean;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @returns Reset Password policy options for the specified organization and a boolean indicating whether the policy
|
* @returns {@link ResetPasswordPolicyOptions} for the specified organization and a boolean indicating whether the policy
|
||||||
* is enabled
|
* is enabled
|
||||||
*/
|
*/
|
||||||
getResetPasswordPolicyOptions: (
|
abstract getResetPasswordPolicyOptions: (
|
||||||
policies: Policy[],
|
policies: Policy[],
|
||||||
orgId: string,
|
orgId: string,
|
||||||
) => [ResetPasswordPolicyOptions, boolean];
|
) => [ResetPasswordPolicyOptions, boolean];
|
||||||
}
|
}
|
||||||
|
|
||||||
export abstract class InternalPolicyService extends PolicyService {
|
export abstract class InternalPolicyService extends PolicyService {
|
||||||
upsert: (policy: PolicyData) => Promise<void>;
|
abstract upsert: (policy: PolicyData, userId: UserId) => Promise<void>;
|
||||||
replace: (policies: { [id: string]: PolicyData }, userId: UserId) => Promise<void>;
|
abstract replace: (policies: { [id: string]: PolicyData }, userId: UserId) => Promise<void>;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,68 +0,0 @@
|
|||||||
import { Observable } from "rxjs";
|
|
||||||
|
|
||||||
import { UserId } from "../../../types/guid";
|
|
||||||
import { PolicyType } from "../../enums";
|
|
||||||
import { PolicyData } from "../../models/data/policy.data";
|
|
||||||
import { MasterPasswordPolicyOptions } from "../../models/domain/master-password-policy-options";
|
|
||||||
import { Policy } from "../../models/domain/policy";
|
|
||||||
import { ResetPasswordPolicyOptions } from "../../models/domain/reset-password-policy-options";
|
|
||||||
|
|
||||||
export abstract class vNextPolicyService {
|
|
||||||
/**
|
|
||||||
* All policies for the provided user from sync data.
|
|
||||||
* May include policies that are disabled or otherwise do not apply to the user. Be careful using this!
|
|
||||||
* Consider {@link policiesByType$} instead, which will only return policies that should be enforced against the user.
|
|
||||||
*/
|
|
||||||
abstract policies$: (userId: UserId) => Observable<Policy[]>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @returns all {@link Policy} objects of a given type that apply to the specified user.
|
|
||||||
* A policy "applies" if it is enabled and the user is not exempt (e.g. because they are an Owner).
|
|
||||||
* @param policyType the {@link PolicyType} to search for
|
|
||||||
* @param userId the {@link UserId} to search against
|
|
||||||
*/
|
|
||||||
abstract policiesByType$: (policyType: PolicyType, userId: UserId) => Observable<Policy[]>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @returns true if a policy of the specified type applies to the specified user, otherwise false.
|
|
||||||
* A policy "applies" if it is enabled and the user is not exempt (e.g. because they are an Owner).
|
|
||||||
* This does not take into account the policy's configuration - if that is important, use {@link policiesByType$} to get the
|
|
||||||
* {@link Policy} objects and then filter by Policy.data.
|
|
||||||
*/
|
|
||||||
abstract policyAppliesToUser$: (policyType: PolicyType, userId: UserId) => Observable<boolean>;
|
|
||||||
|
|
||||||
// Policy specific interfaces
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Combines all Master Password policies that apply to the user.
|
|
||||||
* @returns a set of options which represent the minimum Master Password settings that the user must
|
|
||||||
* comply with in order to comply with **all** Master Password policies.
|
|
||||||
*/
|
|
||||||
abstract masterPasswordPolicyOptions$: (
|
|
||||||
userId: UserId,
|
|
||||||
policies?: Policy[],
|
|
||||||
) => Observable<MasterPasswordPolicyOptions | undefined>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Evaluates whether a proposed Master Password complies with all Master Password policies that apply to the user.
|
|
||||||
*/
|
|
||||||
abstract evaluateMasterPassword: (
|
|
||||||
passwordStrength: number,
|
|
||||||
newPassword: string,
|
|
||||||
enforcedPolicyOptions?: MasterPasswordPolicyOptions,
|
|
||||||
) => boolean;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @returns {@link ResetPasswordPolicyOptions} for the specified organization and a boolean indicating whether the policy
|
|
||||||
* is enabled
|
|
||||||
*/
|
|
||||||
abstract getResetPasswordPolicyOptions: (
|
|
||||||
policies: Policy[],
|
|
||||||
orgId: string,
|
|
||||||
) => [ResetPasswordPolicyOptions, boolean];
|
|
||||||
}
|
|
||||||
|
|
||||||
export abstract class vNextInternalPolicyService extends vNextPolicyService {
|
|
||||||
abstract upsert: (policy: PolicyData, userId: UserId) => Promise<void>;
|
|
||||||
abstract replace: (policies: { [id: string]: PolicyData }, userId: UserId) => Promise<void>;
|
|
||||||
}
|
|
||||||
@@ -15,11 +15,11 @@ import { MasterPasswordPolicyOptions } from "../../../admin-console/models/domai
|
|||||||
import { Organization } from "../../../admin-console/models/domain/organization";
|
import { Organization } from "../../../admin-console/models/domain/organization";
|
||||||
import { Policy } from "../../../admin-console/models/domain/policy";
|
import { Policy } from "../../../admin-console/models/domain/policy";
|
||||||
import { ResetPasswordPolicyOptions } from "../../../admin-console/models/domain/reset-password-policy-options";
|
import { ResetPasswordPolicyOptions } from "../../../admin-console/models/domain/reset-password-policy-options";
|
||||||
import { POLICIES } from "../../../admin-console/services/policy/policy.service";
|
|
||||||
import { PolicyId, UserId } from "../../../types/guid";
|
import { PolicyId, UserId } from "../../../types/guid";
|
||||||
import { OrganizationService } from "../../abstractions/organization/organization.service.abstraction";
|
import { OrganizationService } from "../../abstractions/organization/organization.service.abstraction";
|
||||||
|
|
||||||
import { DefaultvNextPolicyService, getFirstPolicy } from "./default-vnext-policy.service";
|
import { DefaultPolicyService, getFirstPolicy } from "./default-policy.service";
|
||||||
|
import { POLICIES } from "./policy-state";
|
||||||
|
|
||||||
describe("PolicyService", () => {
|
describe("PolicyService", () => {
|
||||||
const userId = "userId" as UserId;
|
const userId = "userId" as UserId;
|
||||||
@@ -27,7 +27,7 @@ describe("PolicyService", () => {
|
|||||||
let organizationService: MockProxy<OrganizationService>;
|
let organizationService: MockProxy<OrganizationService>;
|
||||||
let singleUserState: FakeSingleUserState<Record<PolicyId, PolicyData>>;
|
let singleUserState: FakeSingleUserState<Record<PolicyId, PolicyData>>;
|
||||||
|
|
||||||
let policyService: DefaultvNextPolicyService;
|
let policyService: DefaultPolicyService;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
const accountService = mockAccountServiceWith(userId);
|
const accountService = mockAccountServiceWith(userId);
|
||||||
@@ -59,7 +59,7 @@ describe("PolicyService", () => {
|
|||||||
|
|
||||||
organizationService.organizations$.calledWith(userId).mockReturnValue(organizations$);
|
organizationService.organizations$.calledWith(userId).mockReturnValue(organizations$);
|
||||||
|
|
||||||
policyService = new DefaultvNextPolicyService(stateProvider, organizationService);
|
policyService = new DefaultPolicyService(stateProvider, organizationService);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("upsert", async () => {
|
it("upsert", async () => {
|
||||||
@@ -3,7 +3,7 @@ import { combineLatest, map, Observable, of } from "rxjs";
|
|||||||
import { StateProvider } from "../../../platform/state";
|
import { StateProvider } from "../../../platform/state";
|
||||||
import { UserId } from "../../../types/guid";
|
import { UserId } from "../../../types/guid";
|
||||||
import { OrganizationService } from "../../abstractions/organization/organization.service.abstraction";
|
import { OrganizationService } from "../../abstractions/organization/organization.service.abstraction";
|
||||||
import { vNextPolicyService } from "../../abstractions/policy/vnext-policy.service";
|
import { PolicyService } from "../../abstractions/policy/policy.service.abstraction";
|
||||||
import { OrganizationUserStatusType, PolicyType } from "../../enums";
|
import { OrganizationUserStatusType, PolicyType } from "../../enums";
|
||||||
import { PolicyData } from "../../models/data/policy.data";
|
import { PolicyData } from "../../models/data/policy.data";
|
||||||
import { MasterPasswordPolicyOptions } from "../../models/domain/master-password-policy-options";
|
import { MasterPasswordPolicyOptions } from "../../models/domain/master-password-policy-options";
|
||||||
@@ -11,7 +11,7 @@ import { Organization } from "../../models/domain/organization";
|
|||||||
import { Policy } from "../../models/domain/policy";
|
import { Policy } from "../../models/domain/policy";
|
||||||
import { ResetPasswordPolicyOptions } from "../../models/domain/reset-password-policy-options";
|
import { ResetPasswordPolicyOptions } from "../../models/domain/reset-password-policy-options";
|
||||||
|
|
||||||
import { POLICIES } from "./vnext-policy-state";
|
import { POLICIES } from "./policy-state";
|
||||||
|
|
||||||
export function policyRecordToArray(policiesMap: { [id: string]: PolicyData }) {
|
export function policyRecordToArray(policiesMap: { [id: string]: PolicyData }) {
|
||||||
return Object.values(policiesMap || {}).map((f) => new Policy(f));
|
return Object.values(policiesMap || {}).map((f) => new Policy(f));
|
||||||
@@ -21,7 +21,7 @@ export const getFirstPolicy = map<Policy[], Policy | undefined>((policies) => {
|
|||||||
return policies.at(0) ?? undefined;
|
return policies.at(0) ?? undefined;
|
||||||
});
|
});
|
||||||
|
|
||||||
export class DefaultvNextPolicyService implements vNextPolicyService {
|
export class DefaultPolicyService implements PolicyService {
|
||||||
constructor(
|
constructor(
|
||||||
private stateProvider: StateProvider,
|
private stateProvider: StateProvider,
|
||||||
private organizationService: OrganizationService,
|
private organizationService: OrganizationService,
|
||||||
@@ -89,7 +89,7 @@ export class DefaultvNextPolicyService implements vNextPolicyService {
|
|||||||
const policies$ = policies ? of(policies) : this.policies$(userId);
|
const policies$ = policies ? of(policies) : this.policies$(userId);
|
||||||
return policies$.pipe(
|
return policies$.pipe(
|
||||||
map((obsPolicies) => {
|
map((obsPolicies) => {
|
||||||
const enforcedOptions: MasterPasswordPolicyOptions = new MasterPasswordPolicyOptions();
|
let enforcedOptions: MasterPasswordPolicyOptions | undefined = undefined;
|
||||||
const filteredPolicies =
|
const filteredPolicies =
|
||||||
obsPolicies.filter((p) => p.type === PolicyType.MasterPassword) ?? [];
|
obsPolicies.filter((p) => p.type === PolicyType.MasterPassword) ?? [];
|
||||||
|
|
||||||
@@ -102,6 +102,10 @@ export class DefaultvNextPolicyService implements vNextPolicyService {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!enforcedOptions) {
|
||||||
|
enforcedOptions = new MasterPasswordPolicyOptions();
|
||||||
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
currentPolicy.data.minComplexity != null &&
|
currentPolicy.data.minComplexity != null &&
|
||||||
currentPolicy.data.minComplexity > enforcedOptions.minComplexity
|
currentPolicy.data.minComplexity > enforcedOptions.minComplexity
|
||||||
@@ -1,6 +1,8 @@
|
|||||||
import { firstValueFrom } from "rxjs";
|
import { firstValueFrom, map, switchMap } from "rxjs";
|
||||||
|
|
||||||
import { ApiService } from "../../../abstractions/api.service";
|
import { ApiService } from "../../../abstractions/api.service";
|
||||||
|
import { AccountService } from "../../../auth/abstractions/account.service";
|
||||||
|
import { getUserId } from "../../../auth/services/account.service";
|
||||||
import { HttpStatusCode } from "../../../enums";
|
import { HttpStatusCode } from "../../../enums";
|
||||||
import { ErrorResponse } from "../../../models/response/error.response";
|
import { ErrorResponse } from "../../../models/response/error.response";
|
||||||
import { ListResponse } from "../../../models/response/list.response";
|
import { ListResponse } from "../../../models/response/list.response";
|
||||||
@@ -18,6 +20,7 @@ export class PolicyApiService implements PolicyApiServiceAbstraction {
|
|||||||
constructor(
|
constructor(
|
||||||
private policyService: InternalPolicyService,
|
private policyService: InternalPolicyService,
|
||||||
private apiService: ApiService,
|
private apiService: ApiService,
|
||||||
|
private accountService: AccountService,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async getPolicy(organizationId: string, type: PolicyType): Promise<PolicyResponse> {
|
async getPolicy(organizationId: string, type: PolicyType): Promise<PolicyResponse> {
|
||||||
@@ -93,8 +96,14 @@ export class PolicyApiService implements PolicyApiServiceAbstraction {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return await firstValueFrom(
|
return firstValueFrom(
|
||||||
this.policyService.masterPasswordPolicyOptions$([masterPasswordPolicy]),
|
this.accountService.activeAccount$.pipe(
|
||||||
|
getUserId,
|
||||||
|
switchMap((userId) =>
|
||||||
|
this.policyService.masterPasswordPolicyOptions$(userId, [masterPasswordPolicy]),
|
||||||
|
),
|
||||||
|
map((policy) => policy ?? null),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// If policy not found, return null
|
// If policy not found, return null
|
||||||
@@ -114,8 +123,9 @@ export class PolicyApiService implements PolicyApiServiceAbstraction {
|
|||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
);
|
);
|
||||||
|
const userId = await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId));
|
||||||
const response = new PolicyResponse(r);
|
const response = new PolicyResponse(r);
|
||||||
const data = new PolicyData(response);
|
const data = new PolicyData(response);
|
||||||
await this.policyService.upsert(data);
|
await this.policyService.upsert(data, userId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,556 +0,0 @@
|
|||||||
import { mock, MockProxy } from "jest-mock-extended";
|
|
||||||
import { firstValueFrom, of } from "rxjs";
|
|
||||||
|
|
||||||
import { FakeStateProvider, mockAccountServiceWith } from "../../../../spec";
|
|
||||||
import { FakeActiveUserState, FakeSingleUserState } from "../../../../spec/fake-state";
|
|
||||||
import {
|
|
||||||
OrganizationUserStatusType,
|
|
||||||
OrganizationUserType,
|
|
||||||
PolicyType,
|
|
||||||
} from "../../../admin-console/enums";
|
|
||||||
import { PermissionsApi } from "../../../admin-console/models/api/permissions.api";
|
|
||||||
import { OrganizationData } from "../../../admin-console/models/data/organization.data";
|
|
||||||
import { PolicyData } from "../../../admin-console/models/data/policy.data";
|
|
||||||
import { MasterPasswordPolicyOptions } from "../../../admin-console/models/domain/master-password-policy-options";
|
|
||||||
import { Organization } from "../../../admin-console/models/domain/organization";
|
|
||||||
import { Policy } from "../../../admin-console/models/domain/policy";
|
|
||||||
import { ResetPasswordPolicyOptions } from "../../../admin-console/models/domain/reset-password-policy-options";
|
|
||||||
import { POLICIES, PolicyService } from "../../../admin-console/services/policy/policy.service";
|
|
||||||
import { PolicyId, UserId } from "../../../types/guid";
|
|
||||||
import { OrganizationService } from "../../abstractions/organization/organization.service.abstraction";
|
|
||||||
|
|
||||||
describe("PolicyService", () => {
|
|
||||||
const userId = "userId" as UserId;
|
|
||||||
let stateProvider: FakeStateProvider;
|
|
||||||
let organizationService: MockProxy<OrganizationService>;
|
|
||||||
let activeUserState: FakeActiveUserState<Record<PolicyId, PolicyData>>;
|
|
||||||
let singleUserState: FakeSingleUserState<Record<PolicyId, PolicyData>>;
|
|
||||||
|
|
||||||
let policyService: PolicyService;
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
const accountService = mockAccountServiceWith(userId);
|
|
||||||
stateProvider = new FakeStateProvider(accountService);
|
|
||||||
organizationService = mock<OrganizationService>();
|
|
||||||
|
|
||||||
activeUserState = stateProvider.activeUser.getFake(POLICIES);
|
|
||||||
singleUserState = stateProvider.singleUser.getFake(activeUserState.userId, POLICIES);
|
|
||||||
|
|
||||||
const organizations$ = of([
|
|
||||||
// User
|
|
||||||
organization("org1", true, true, OrganizationUserStatusType.Confirmed, false),
|
|
||||||
// Owner
|
|
||||||
organization(
|
|
||||||
"org2",
|
|
||||||
true,
|
|
||||||
true,
|
|
||||||
OrganizationUserStatusType.Confirmed,
|
|
||||||
false,
|
|
||||||
OrganizationUserType.Owner,
|
|
||||||
),
|
|
||||||
// Does not use policies
|
|
||||||
organization("org3", true, false, OrganizationUserStatusType.Confirmed, false),
|
|
||||||
// Another User
|
|
||||||
organization("org4", true, true, OrganizationUserStatusType.Confirmed, false),
|
|
||||||
// Another User
|
|
||||||
organization("org5", true, true, OrganizationUserStatusType.Confirmed, false),
|
|
||||||
// Can manage policies
|
|
||||||
organization("org6", true, true, OrganizationUserStatusType.Confirmed, true),
|
|
||||||
]);
|
|
||||||
|
|
||||||
organizationService.organizations$.mockReturnValue(organizations$);
|
|
||||||
|
|
||||||
policyService = new PolicyService(stateProvider, organizationService);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("upsert", async () => {
|
|
||||||
activeUserState.nextState(
|
|
||||||
arrayToRecord([
|
|
||||||
policyData("1", "test-organization", PolicyType.MaximumVaultTimeout, true, { minutes: 14 }),
|
|
||||||
]),
|
|
||||||
);
|
|
||||||
|
|
||||||
await policyService.upsert(policyData("99", "test-organization", PolicyType.DisableSend, true));
|
|
||||||
|
|
||||||
expect(await firstValueFrom(policyService.policies$)).toEqual([
|
|
||||||
{
|
|
||||||
id: "1",
|
|
||||||
organizationId: "test-organization",
|
|
||||||
type: PolicyType.MaximumVaultTimeout,
|
|
||||||
enabled: true,
|
|
||||||
data: { minutes: 14 },
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "99",
|
|
||||||
organizationId: "test-organization",
|
|
||||||
type: PolicyType.DisableSend,
|
|
||||||
enabled: true,
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("replace", async () => {
|
|
||||||
activeUserState.nextState(
|
|
||||||
arrayToRecord([
|
|
||||||
policyData("1", "test-organization", PolicyType.MaximumVaultTimeout, true, { minutes: 14 }),
|
|
||||||
]),
|
|
||||||
);
|
|
||||||
|
|
||||||
await policyService.replace(
|
|
||||||
{
|
|
||||||
"2": policyData("2", "test-organization", PolicyType.DisableSend, true),
|
|
||||||
},
|
|
||||||
userId,
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(await firstValueFrom(policyService.policies$)).toEqual([
|
|
||||||
{
|
|
||||||
id: "2",
|
|
||||||
organizationId: "test-organization",
|
|
||||||
type: PolicyType.DisableSend,
|
|
||||||
enabled: true,
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("masterPasswordPolicyOptions", () => {
|
|
||||||
it("returns default policy options", async () => {
|
|
||||||
const data: any = {
|
|
||||||
minComplexity: 5,
|
|
||||||
minLength: 20,
|
|
||||||
requireUpper: true,
|
|
||||||
};
|
|
||||||
const model = [
|
|
||||||
new Policy(policyData("1", "test-organization-3", PolicyType.MasterPassword, true, data)),
|
|
||||||
];
|
|
||||||
const result = await firstValueFrom(policyService.masterPasswordPolicyOptions$(model));
|
|
||||||
|
|
||||||
expect(result).toEqual({
|
|
||||||
minComplexity: 5,
|
|
||||||
minLength: 20,
|
|
||||||
requireLower: false,
|
|
||||||
requireNumbers: false,
|
|
||||||
requireSpecial: false,
|
|
||||||
requireUpper: true,
|
|
||||||
enforceOnLogin: false,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it("returns null", async () => {
|
|
||||||
const data: any = {};
|
|
||||||
const model = [
|
|
||||||
new Policy(
|
|
||||||
policyData("3", "test-organization-3", PolicyType.DisablePersonalVaultExport, true, data),
|
|
||||||
),
|
|
||||||
new Policy(
|
|
||||||
policyData("4", "test-organization-3", PolicyType.MaximumVaultTimeout, true, data),
|
|
||||||
),
|
|
||||||
];
|
|
||||||
|
|
||||||
const result = await firstValueFrom(policyService.masterPasswordPolicyOptions$(model));
|
|
||||||
|
|
||||||
expect(result).toEqual(null);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("returns specified policy options", async () => {
|
|
||||||
const data: any = {
|
|
||||||
minLength: 14,
|
|
||||||
};
|
|
||||||
const model = [
|
|
||||||
new Policy(
|
|
||||||
policyData("3", "test-organization-3", PolicyType.DisablePersonalVaultExport, true, data),
|
|
||||||
),
|
|
||||||
new Policy(policyData("4", "test-organization-3", PolicyType.MasterPassword, true, data)),
|
|
||||||
];
|
|
||||||
|
|
||||||
const result = await firstValueFrom(policyService.masterPasswordPolicyOptions$(model));
|
|
||||||
|
|
||||||
expect(result).toEqual({
|
|
||||||
minComplexity: 0,
|
|
||||||
minLength: 14,
|
|
||||||
requireLower: false,
|
|
||||||
requireNumbers: false,
|
|
||||||
requireSpecial: false,
|
|
||||||
requireUpper: false,
|
|
||||||
enforceOnLogin: false,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("evaluateMasterPassword", () => {
|
|
||||||
it("false", async () => {
|
|
||||||
const enforcedPolicyOptions = new MasterPasswordPolicyOptions();
|
|
||||||
enforcedPolicyOptions.minLength = 14;
|
|
||||||
const result = policyService.evaluateMasterPassword(10, "password", enforcedPolicyOptions);
|
|
||||||
|
|
||||||
expect(result).toEqual(false);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("true", async () => {
|
|
||||||
const enforcedPolicyOptions = new MasterPasswordPolicyOptions();
|
|
||||||
const result = policyService.evaluateMasterPassword(0, "password", enforcedPolicyOptions);
|
|
||||||
|
|
||||||
expect(result).toEqual(true);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("getResetPasswordPolicyOptions", () => {
|
|
||||||
it("default", async () => {
|
|
||||||
const result = policyService.getResetPasswordPolicyOptions([], "");
|
|
||||||
|
|
||||||
expect(result).toEqual([new ResetPasswordPolicyOptions(), false]);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("returns autoEnrollEnabled true", async () => {
|
|
||||||
const data: any = {
|
|
||||||
autoEnrollEnabled: true,
|
|
||||||
};
|
|
||||||
const policies = [
|
|
||||||
new Policy(policyData("5", "test-organization-3", PolicyType.ResetPassword, true, data)),
|
|
||||||
];
|
|
||||||
const result = policyService.getResetPasswordPolicyOptions(policies, "test-organization-3");
|
|
||||||
|
|
||||||
expect(result).toEqual([{ autoEnrollEnabled: true }, true]);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("get$", () => {
|
|
||||||
it("returns the specified PolicyType", async () => {
|
|
||||||
activeUserState.nextState(
|
|
||||||
arrayToRecord([
|
|
||||||
policyData("policy1", "org1", PolicyType.ActivateAutofill, true),
|
|
||||||
policyData("policy2", "org1", PolicyType.DisablePersonalVaultExport, true),
|
|
||||||
policyData("policy3", "org1", PolicyType.RemoveUnlockWithPin, true),
|
|
||||||
]),
|
|
||||||
);
|
|
||||||
|
|
||||||
await expect(
|
|
||||||
firstValueFrom(policyService.get$(PolicyType.ActivateAutofill)),
|
|
||||||
).resolves.toMatchObject({
|
|
||||||
id: "policy1",
|
|
||||||
organizationId: "org1",
|
|
||||||
type: PolicyType.ActivateAutofill,
|
|
||||||
enabled: true,
|
|
||||||
});
|
|
||||||
await expect(
|
|
||||||
firstValueFrom(policyService.get$(PolicyType.DisablePersonalVaultExport)),
|
|
||||||
).resolves.toMatchObject({
|
|
||||||
id: "policy2",
|
|
||||||
organizationId: "org1",
|
|
||||||
type: PolicyType.DisablePersonalVaultExport,
|
|
||||||
enabled: true,
|
|
||||||
});
|
|
||||||
await expect(
|
|
||||||
firstValueFrom(policyService.get$(PolicyType.RemoveUnlockWithPin)),
|
|
||||||
).resolves.toMatchObject({
|
|
||||||
id: "policy3",
|
|
||||||
organizationId: "org1",
|
|
||||||
type: PolicyType.RemoveUnlockWithPin,
|
|
||||||
enabled: true,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it("does not return disabled policies", async () => {
|
|
||||||
activeUserState.nextState(
|
|
||||||
arrayToRecord([
|
|
||||||
policyData("policy1", "org1", PolicyType.ActivateAutofill, true),
|
|
||||||
policyData("policy2", "org1", PolicyType.DisablePersonalVaultExport, false),
|
|
||||||
]),
|
|
||||||
);
|
|
||||||
|
|
||||||
const result = await firstValueFrom(
|
|
||||||
policyService.get$(PolicyType.DisablePersonalVaultExport),
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(result).toBeNull();
|
|
||||||
});
|
|
||||||
|
|
||||||
it("does not return policies that do not apply to the user because the user's role is exempt", async () => {
|
|
||||||
activeUserState.nextState(
|
|
||||||
arrayToRecord([
|
|
||||||
policyData("policy1", "org1", PolicyType.ActivateAutofill, true),
|
|
||||||
policyData("policy2", "org2", PolicyType.DisablePersonalVaultExport, false),
|
|
||||||
]),
|
|
||||||
);
|
|
||||||
|
|
||||||
const result = await firstValueFrom(
|
|
||||||
policyService.get$(PolicyType.DisablePersonalVaultExport),
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(result).toBeNull();
|
|
||||||
});
|
|
||||||
|
|
||||||
it.each([
|
|
||||||
["owners", "org2"],
|
|
||||||
["administrators", "org6"],
|
|
||||||
])("returns the password generator policy for %s", async (_, organization) => {
|
|
||||||
activeUserState.nextState(
|
|
||||||
arrayToRecord([
|
|
||||||
policyData("policy1", "org1", PolicyType.ActivateAutofill, false),
|
|
||||||
policyData("policy2", organization, PolicyType.PasswordGenerator, true),
|
|
||||||
]),
|
|
||||||
);
|
|
||||||
|
|
||||||
const result = await firstValueFrom(policyService.get$(PolicyType.PasswordGenerator));
|
|
||||||
|
|
||||||
expect(result).toBeTruthy();
|
|
||||||
});
|
|
||||||
|
|
||||||
it("does not return policies for organizations that do not use policies", async () => {
|
|
||||||
activeUserState.nextState(
|
|
||||||
arrayToRecord([
|
|
||||||
policyData("policy1", "org3", PolicyType.ActivateAutofill, true),
|
|
||||||
policyData("policy2", "org2", PolicyType.DisablePersonalVaultExport, true),
|
|
||||||
]),
|
|
||||||
);
|
|
||||||
|
|
||||||
const result = await firstValueFrom(policyService.get$(PolicyType.ActivateAutofill));
|
|
||||||
|
|
||||||
expect(result).toBeNull();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("getAll$", () => {
|
|
||||||
it("returns the specified PolicyTypes", async () => {
|
|
||||||
singleUserState.nextState(
|
|
||||||
arrayToRecord([
|
|
||||||
policyData("policy1", "org4", PolicyType.DisablePersonalVaultExport, true),
|
|
||||||
policyData("policy2", "org1", PolicyType.ActivateAutofill, true),
|
|
||||||
policyData("policy3", "org5", PolicyType.DisablePersonalVaultExport, true),
|
|
||||||
policyData("policy4", "org1", PolicyType.DisablePersonalVaultExport, true),
|
|
||||||
]),
|
|
||||||
);
|
|
||||||
|
|
||||||
const result = await firstValueFrom(
|
|
||||||
policyService.getAll$(PolicyType.DisablePersonalVaultExport, activeUserState.userId),
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(result).toEqual([
|
|
||||||
{
|
|
||||||
id: "policy1",
|
|
||||||
organizationId: "org4",
|
|
||||||
type: PolicyType.DisablePersonalVaultExport,
|
|
||||||
enabled: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "policy3",
|
|
||||||
organizationId: "org5",
|
|
||||||
type: PolicyType.DisablePersonalVaultExport,
|
|
||||||
enabled: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "policy4",
|
|
||||||
organizationId: "org1",
|
|
||||||
type: PolicyType.DisablePersonalVaultExport,
|
|
||||||
enabled: true,
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("does not return disabled policies", async () => {
|
|
||||||
singleUserState.nextState(
|
|
||||||
arrayToRecord([
|
|
||||||
policyData("policy1", "org4", PolicyType.DisablePersonalVaultExport, true),
|
|
||||||
policyData("policy2", "org1", PolicyType.ActivateAutofill, true),
|
|
||||||
policyData("policy3", "org5", PolicyType.DisablePersonalVaultExport, false), // disabled
|
|
||||||
policyData("policy4", "org1", PolicyType.DisablePersonalVaultExport, true),
|
|
||||||
]),
|
|
||||||
);
|
|
||||||
|
|
||||||
const result = await firstValueFrom(
|
|
||||||
policyService.getAll$(PolicyType.DisablePersonalVaultExport, activeUserState.userId),
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(result).toEqual([
|
|
||||||
{
|
|
||||||
id: "policy1",
|
|
||||||
organizationId: "org4",
|
|
||||||
type: PolicyType.DisablePersonalVaultExport,
|
|
||||||
enabled: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "policy4",
|
|
||||||
organizationId: "org1",
|
|
||||||
type: PolicyType.DisablePersonalVaultExport,
|
|
||||||
enabled: true,
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("does not return policies that do not apply to the user because the user's role is exempt", async () => {
|
|
||||||
singleUserState.nextState(
|
|
||||||
arrayToRecord([
|
|
||||||
policyData("policy1", "org4", PolicyType.DisablePersonalVaultExport, true),
|
|
||||||
policyData("policy2", "org1", PolicyType.ActivateAutofill, true),
|
|
||||||
policyData("policy3", "org5", PolicyType.DisablePersonalVaultExport, true),
|
|
||||||
policyData("policy4", "org2", PolicyType.DisablePersonalVaultExport, true), // owner
|
|
||||||
]),
|
|
||||||
);
|
|
||||||
|
|
||||||
const result = await firstValueFrom(
|
|
||||||
policyService.getAll$(PolicyType.DisablePersonalVaultExport, activeUserState.userId),
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(result).toEqual([
|
|
||||||
{
|
|
||||||
id: "policy1",
|
|
||||||
organizationId: "org4",
|
|
||||||
type: PolicyType.DisablePersonalVaultExport,
|
|
||||||
enabled: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "policy3",
|
|
||||||
organizationId: "org5",
|
|
||||||
type: PolicyType.DisablePersonalVaultExport,
|
|
||||||
enabled: true,
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("does not return policies for organizations that do not use policies", async () => {
|
|
||||||
singleUserState.nextState(
|
|
||||||
arrayToRecord([
|
|
||||||
policyData("policy1", "org4", PolicyType.DisablePersonalVaultExport, true),
|
|
||||||
policyData("policy2", "org1", PolicyType.ActivateAutofill, true),
|
|
||||||
policyData("policy3", "org3", PolicyType.DisablePersonalVaultExport, true), // does not use policies
|
|
||||||
policyData("policy4", "org1", PolicyType.DisablePersonalVaultExport, true),
|
|
||||||
]),
|
|
||||||
);
|
|
||||||
|
|
||||||
const result = await firstValueFrom(
|
|
||||||
policyService.getAll$(PolicyType.DisablePersonalVaultExport, activeUserState.userId),
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(result).toEqual([
|
|
||||||
{
|
|
||||||
id: "policy1",
|
|
||||||
organizationId: "org4",
|
|
||||||
type: PolicyType.DisablePersonalVaultExport,
|
|
||||||
enabled: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "policy4",
|
|
||||||
organizationId: "org1",
|
|
||||||
type: PolicyType.DisablePersonalVaultExport,
|
|
||||||
enabled: true,
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("policyAppliesToActiveUser$", () => {
|
|
||||||
it("returns true when the policyType applies to the user", async () => {
|
|
||||||
activeUserState.nextState(
|
|
||||||
arrayToRecord([
|
|
||||||
policyData("policy1", "org4", PolicyType.DisablePersonalVaultExport, true),
|
|
||||||
policyData("policy2", "org1", PolicyType.ActivateAutofill, true),
|
|
||||||
policyData("policy3", "org5", PolicyType.DisablePersonalVaultExport, true),
|
|
||||||
policyData("policy4", "org1", PolicyType.DisablePersonalVaultExport, true),
|
|
||||||
]),
|
|
||||||
);
|
|
||||||
|
|
||||||
const result = await firstValueFrom(
|
|
||||||
policyService.policyAppliesToActiveUser$(PolicyType.DisablePersonalVaultExport),
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(result).toBe(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("returns false when policyType is disabled", async () => {
|
|
||||||
activeUserState.nextState(
|
|
||||||
arrayToRecord([
|
|
||||||
policyData("policy2", "org1", PolicyType.ActivateAutofill, true),
|
|
||||||
policyData("policy3", "org5", PolicyType.DisablePersonalVaultExport, false), // disabled
|
|
||||||
]),
|
|
||||||
);
|
|
||||||
|
|
||||||
const result = await firstValueFrom(
|
|
||||||
policyService.policyAppliesToActiveUser$(PolicyType.DisablePersonalVaultExport),
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(result).toBe(false);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("returns false when the policyType does not apply to the user because the user's role is exempt", async () => {
|
|
||||||
activeUserState.nextState(
|
|
||||||
arrayToRecord([
|
|
||||||
policyData("policy2", "org1", PolicyType.ActivateAutofill, true),
|
|
||||||
policyData("policy4", "org2", PolicyType.DisablePersonalVaultExport, true), // owner
|
|
||||||
]),
|
|
||||||
);
|
|
||||||
|
|
||||||
const result = await firstValueFrom(
|
|
||||||
policyService.policyAppliesToActiveUser$(PolicyType.DisablePersonalVaultExport),
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(result).toBe(false);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("returns false for organizations that do not use policies", async () => {
|
|
||||||
activeUserState.nextState(
|
|
||||||
arrayToRecord([
|
|
||||||
policyData("policy2", "org1", PolicyType.ActivateAutofill, true),
|
|
||||||
policyData("policy3", "org3", PolicyType.DisablePersonalVaultExport, true), // does not use policies
|
|
||||||
]),
|
|
||||||
);
|
|
||||||
|
|
||||||
const result = await firstValueFrom(
|
|
||||||
policyService.policyAppliesToActiveUser$(PolicyType.DisablePersonalVaultExport),
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(result).toBe(false);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
function policyData(
|
|
||||||
id: string,
|
|
||||||
organizationId: string,
|
|
||||||
type: PolicyType,
|
|
||||||
enabled: boolean,
|
|
||||||
data?: any,
|
|
||||||
) {
|
|
||||||
const policyData = new PolicyData({} as any);
|
|
||||||
policyData.id = id as PolicyId;
|
|
||||||
policyData.organizationId = organizationId;
|
|
||||||
policyData.type = type;
|
|
||||||
policyData.enabled = enabled;
|
|
||||||
policyData.data = data;
|
|
||||||
|
|
||||||
return policyData;
|
|
||||||
}
|
|
||||||
|
|
||||||
function organizationData(
|
|
||||||
id: string,
|
|
||||||
enabled: boolean,
|
|
||||||
usePolicies: boolean,
|
|
||||||
status: OrganizationUserStatusType,
|
|
||||||
managePolicies: boolean,
|
|
||||||
type: OrganizationUserType = OrganizationUserType.User,
|
|
||||||
) {
|
|
||||||
const organizationData = new OrganizationData({} as any, {} as any);
|
|
||||||
organizationData.id = id;
|
|
||||||
organizationData.enabled = enabled;
|
|
||||||
organizationData.usePolicies = usePolicies;
|
|
||||||
organizationData.status = status;
|
|
||||||
organizationData.permissions = new PermissionsApi({ managePolicies: managePolicies } as any);
|
|
||||||
organizationData.type = type;
|
|
||||||
return organizationData;
|
|
||||||
}
|
|
||||||
|
|
||||||
function organization(
|
|
||||||
id: string,
|
|
||||||
enabled: boolean,
|
|
||||||
usePolicies: boolean,
|
|
||||||
status: OrganizationUserStatusType,
|
|
||||||
managePolicies: boolean,
|
|
||||||
type: OrganizationUserType = OrganizationUserType.User,
|
|
||||||
) {
|
|
||||||
return new Organization(
|
|
||||||
organizationData(id, enabled, usePolicies, status, managePolicies, type),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function arrayToRecord(input: PolicyData[]): Record<PolicyId, PolicyData> {
|
|
||||||
return Object.fromEntries(input.map((i) => [i.id, i]));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
@@ -1,257 +0,0 @@
|
|||||||
// FIXME: Update this file to be type safe and remove this and next line
|
|
||||||
// @ts-strict-ignore
|
|
||||||
import { combineLatest, firstValueFrom, map, Observable, of, switchMap } from "rxjs";
|
|
||||||
|
|
||||||
import { UserKeyDefinition, POLICIES_DISK, StateProvider } from "../../../platform/state";
|
|
||||||
import { PolicyId, UserId } from "../../../types/guid";
|
|
||||||
import { OrganizationService } from "../../abstractions/organization/organization.service.abstraction";
|
|
||||||
import { InternalPolicyService as InternalPolicyServiceAbstraction } from "../../abstractions/policy/policy.service.abstraction";
|
|
||||||
import { OrganizationUserStatusType, PolicyType } from "../../enums";
|
|
||||||
import { PolicyData } from "../../models/data/policy.data";
|
|
||||||
import { MasterPasswordPolicyOptions } from "../../models/domain/master-password-policy-options";
|
|
||||||
import { Organization } from "../../models/domain/organization";
|
|
||||||
import { Policy } from "../../models/domain/policy";
|
|
||||||
import { ResetPasswordPolicyOptions } from "../../models/domain/reset-password-policy-options";
|
|
||||||
|
|
||||||
const policyRecordToArray = (policiesMap: { [id: string]: PolicyData }) =>
|
|
||||||
Object.values(policiesMap || {}).map((f) => new Policy(f));
|
|
||||||
|
|
||||||
export const POLICIES = UserKeyDefinition.record<PolicyData, PolicyId>(POLICIES_DISK, "policies", {
|
|
||||||
deserializer: (policyData) => policyData,
|
|
||||||
clearOn: ["logout"],
|
|
||||||
});
|
|
||||||
|
|
||||||
export class PolicyService implements InternalPolicyServiceAbstraction {
|
|
||||||
private activeUserPolicyState = this.stateProvider.getActive(POLICIES);
|
|
||||||
private activeUserPolicies$ = this.activeUserPolicyState.state$.pipe(
|
|
||||||
map((policyData) => policyRecordToArray(policyData)),
|
|
||||||
);
|
|
||||||
|
|
||||||
policies$ = this.activeUserPolicies$;
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
private stateProvider: StateProvider,
|
|
||||||
private organizationService: OrganizationService,
|
|
||||||
) {}
|
|
||||||
|
|
||||||
get$(policyType: PolicyType): Observable<Policy> {
|
|
||||||
const filteredPolicies$ = this.activeUserPolicies$.pipe(
|
|
||||||
map((policies) => policies.filter((p) => p.type === policyType)),
|
|
||||||
);
|
|
||||||
|
|
||||||
const organizations$ = this.stateProvider.activeUserId$.pipe(
|
|
||||||
switchMap((userId) => this.organizationService.organizations$(userId)),
|
|
||||||
);
|
|
||||||
|
|
||||||
return combineLatest([filteredPolicies$, organizations$]).pipe(
|
|
||||||
map(
|
|
||||||
([policies, organizations]) =>
|
|
||||||
this.enforcedPolicyFilter(policies, organizations)?.at(0) ?? null,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
getAll$(policyType: PolicyType, userId: UserId) {
|
|
||||||
const filteredPolicies$ = this.stateProvider.getUserState$(POLICIES, userId).pipe(
|
|
||||||
map((policyData) => policyRecordToArray(policyData)),
|
|
||||||
map((policies) => policies.filter((p) => p.type === policyType)),
|
|
||||||
);
|
|
||||||
|
|
||||||
return combineLatest([filteredPolicies$, this.organizationService.organizations$(userId)]).pipe(
|
|
||||||
map(([policies, organizations]) => this.enforcedPolicyFilter(policies, organizations)),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
async getAll(policyType: PolicyType) {
|
|
||||||
return await firstValueFrom(
|
|
||||||
this.policies$.pipe(map((policies) => policies.filter((p) => p.type === policyType))),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
policyAppliesToActiveUser$(policyType: PolicyType) {
|
|
||||||
return this.get$(policyType).pipe(map((policy) => policy != null));
|
|
||||||
}
|
|
||||||
|
|
||||||
async policyAppliesToUser(policyType: PolicyType) {
|
|
||||||
return await firstValueFrom(this.policyAppliesToActiveUser$(policyType));
|
|
||||||
}
|
|
||||||
|
|
||||||
private enforcedPolicyFilter(policies: Policy[], organizations: Organization[]) {
|
|
||||||
const orgDict = Object.fromEntries(organizations.map((o) => [o.id, o]));
|
|
||||||
return policies.filter((policy) => {
|
|
||||||
const organization = orgDict[policy.organizationId];
|
|
||||||
|
|
||||||
// This shouldn't happen, i.e. the user should only have policies for orgs they are a member of
|
|
||||||
// But if it does, err on the side of enforcing the policy
|
|
||||||
if (organization == null) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
policy.enabled &&
|
|
||||||
organization.status >= OrganizationUserStatusType.Accepted &&
|
|
||||||
organization.usePolicies &&
|
|
||||||
!this.isExemptFromPolicy(policy.type, organization)
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
masterPasswordPolicyOptions$(policies?: Policy[]): Observable<MasterPasswordPolicyOptions> {
|
|
||||||
const observable = policies ? of(policies) : this.policies$;
|
|
||||||
return observable.pipe(
|
|
||||||
map((obsPolicies) => {
|
|
||||||
let enforcedOptions: MasterPasswordPolicyOptions = null;
|
|
||||||
const filteredPolicies = obsPolicies.filter((p) => p.type === PolicyType.MasterPassword);
|
|
||||||
|
|
||||||
if (filteredPolicies == null || filteredPolicies.length === 0) {
|
|
||||||
return enforcedOptions;
|
|
||||||
}
|
|
||||||
|
|
||||||
filteredPolicies.forEach((currentPolicy) => {
|
|
||||||
if (!currentPolicy.enabled || currentPolicy.data == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (enforcedOptions == null) {
|
|
||||||
enforcedOptions = new MasterPasswordPolicyOptions();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
currentPolicy.data.minComplexity != null &&
|
|
||||||
currentPolicy.data.minComplexity > enforcedOptions.minComplexity
|
|
||||||
) {
|
|
||||||
enforcedOptions.minComplexity = currentPolicy.data.minComplexity;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
currentPolicy.data.minLength != null &&
|
|
||||||
currentPolicy.data.minLength > enforcedOptions.minLength
|
|
||||||
) {
|
|
||||||
enforcedOptions.minLength = currentPolicy.data.minLength;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (currentPolicy.data.requireUpper) {
|
|
||||||
enforcedOptions.requireUpper = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (currentPolicy.data.requireLower) {
|
|
||||||
enforcedOptions.requireLower = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (currentPolicy.data.requireNumbers) {
|
|
||||||
enforcedOptions.requireNumbers = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (currentPolicy.data.requireSpecial) {
|
|
||||||
enforcedOptions.requireSpecial = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (currentPolicy.data.enforceOnLogin) {
|
|
||||||
enforcedOptions.enforceOnLogin = true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return enforcedOptions;
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
evaluateMasterPassword(
|
|
||||||
passwordStrength: number,
|
|
||||||
newPassword: string,
|
|
||||||
enforcedPolicyOptions: MasterPasswordPolicyOptions,
|
|
||||||
): boolean {
|
|
||||||
if (enforcedPolicyOptions == null) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
enforcedPolicyOptions.minComplexity > 0 &&
|
|
||||||
enforcedPolicyOptions.minComplexity > passwordStrength
|
|
||||||
) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
enforcedPolicyOptions.minLength > 0 &&
|
|
||||||
enforcedPolicyOptions.minLength > newPassword.length
|
|
||||||
) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (enforcedPolicyOptions.requireUpper && newPassword.toLocaleLowerCase() === newPassword) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (enforcedPolicyOptions.requireLower && newPassword.toLocaleUpperCase() === newPassword) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (enforcedPolicyOptions.requireNumbers && !/[0-9]/.test(newPassword)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// eslint-disable-next-line
|
|
||||||
if (enforcedPolicyOptions.requireSpecial && !/[!@#$%\^&*]/g.test(newPassword)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
getResetPasswordPolicyOptions(
|
|
||||||
policies: Policy[],
|
|
||||||
orgId: string,
|
|
||||||
): [ResetPasswordPolicyOptions, boolean] {
|
|
||||||
const resetPasswordPolicyOptions = new ResetPasswordPolicyOptions();
|
|
||||||
|
|
||||||
if (policies == null || orgId == null) {
|
|
||||||
return [resetPasswordPolicyOptions, false];
|
|
||||||
}
|
|
||||||
|
|
||||||
const policy = policies.find(
|
|
||||||
(p) => p.organizationId === orgId && p.type === PolicyType.ResetPassword && p.enabled,
|
|
||||||
);
|
|
||||||
resetPasswordPolicyOptions.autoEnrollEnabled = policy?.data?.autoEnrollEnabled ?? false;
|
|
||||||
|
|
||||||
return [resetPasswordPolicyOptions, policy?.enabled ?? false];
|
|
||||||
}
|
|
||||||
|
|
||||||
async upsert(policy: PolicyData): Promise<void> {
|
|
||||||
await this.activeUserPolicyState.update((policies) => {
|
|
||||||
policies ??= {};
|
|
||||||
policies[policy.id] = policy;
|
|
||||||
return policies;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async replace(policies: { [id: string]: PolicyData }, userId: UserId): Promise<void> {
|
|
||||||
await this.stateProvider.setUserState(POLICIES, policies, userId);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Determines whether an orgUser is exempt from a specific policy because of their role
|
|
||||||
* Generally orgUsers who can manage policies are exempt from them, but some policies are stricter
|
|
||||||
*/
|
|
||||||
private isExemptFromPolicy(policyType: PolicyType, organization: Organization) {
|
|
||||||
switch (policyType) {
|
|
||||||
case PolicyType.MaximumVaultTimeout:
|
|
||||||
// Max Vault Timeout applies to everyone except owners
|
|
||||||
return organization.isOwner;
|
|
||||||
case PolicyType.PasswordGenerator:
|
|
||||||
// password generation policy applies to everyone
|
|
||||||
return false;
|
|
||||||
case PolicyType.PersonalOwnership:
|
|
||||||
// individual vault policy applies to everyone except admins and owners
|
|
||||||
return organization.isAdmin;
|
|
||||||
case PolicyType.FreeFamiliesSponsorshipPolicy:
|
|
||||||
// free Bitwarden families policy applies to everyone
|
|
||||||
return false;
|
|
||||||
case PolicyType.RemoveUnlockWithPin:
|
|
||||||
// free Remove Unlock with PIN policy applies to everyone
|
|
||||||
return false;
|
|
||||||
default:
|
|
||||||
return organization.canManagePolicies;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,9 +1,11 @@
|
|||||||
// 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 { map, Observable } from "rxjs";
|
import { map, Observable, switchMap } from "rxjs";
|
||||||
|
|
||||||
import { PolicyService } from "../../admin-console/abstractions/policy/policy.service.abstraction";
|
import { PolicyService } from "../../admin-console/abstractions/policy/policy.service.abstraction";
|
||||||
import { PolicyType } from "../../admin-console/enums";
|
import { PolicyType } from "../../admin-console/enums";
|
||||||
|
import { AccountService } from "../../auth/abstractions/account.service";
|
||||||
|
import { getUserId } from "../../auth/services/account.service";
|
||||||
import {
|
import {
|
||||||
AUTOFILL_SETTINGS_DISK,
|
AUTOFILL_SETTINGS_DISK,
|
||||||
AUTOFILL_SETTINGS_DISK_LOCAL,
|
AUTOFILL_SETTINGS_DISK_LOCAL,
|
||||||
@@ -152,6 +154,7 @@ export class AutofillSettingsService implements AutofillSettingsServiceAbstracti
|
|||||||
constructor(
|
constructor(
|
||||||
private stateProvider: StateProvider,
|
private stateProvider: StateProvider,
|
||||||
private policyService: PolicyService,
|
private policyService: PolicyService,
|
||||||
|
private accountService: AccountService,
|
||||||
) {
|
) {
|
||||||
this.autofillOnPageLoadState = this.stateProvider.getActive(AUTOFILL_ON_PAGE_LOAD);
|
this.autofillOnPageLoadState = this.stateProvider.getActive(AUTOFILL_ON_PAGE_LOAD);
|
||||||
this.autofillOnPageLoad$ = this.autofillOnPageLoadState.state$.pipe(map((x) => x ?? false));
|
this.autofillOnPageLoad$ = this.autofillOnPageLoadState.state$.pipe(map((x) => x ?? false));
|
||||||
@@ -169,8 +172,11 @@ export class AutofillSettingsService implements AutofillSettingsServiceAbstracti
|
|||||||
this.autofillOnPageLoadCalloutIsDismissed$ =
|
this.autofillOnPageLoadCalloutIsDismissed$ =
|
||||||
this.autofillOnPageLoadCalloutIsDismissedState.state$.pipe(map((x) => x ?? false));
|
this.autofillOnPageLoadCalloutIsDismissedState.state$.pipe(map((x) => x ?? false));
|
||||||
|
|
||||||
this.activateAutofillOnPageLoadFromPolicy$ = this.policyService.policyAppliesToActiveUser$(
|
this.activateAutofillOnPageLoadFromPolicy$ = this.accountService.activeAccount$.pipe(
|
||||||
PolicyType.ActivateAutofill,
|
getUserId,
|
||||||
|
switchMap((userId) =>
|
||||||
|
this.policyService.policyAppliesToUser$(PolicyType.ActivateAutofill, userId),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
this.autofillOnPageLoadPolicyToastHasDisplayedState = this.stateProvider.getActive(
|
this.autofillOnPageLoadPolicyToastHasDisplayedState = this.stateProvider.getActive(
|
||||||
|
|||||||
@@ -175,7 +175,7 @@ describe("VaultTimeoutSettingsService", () => {
|
|||||||
"returns $expected when policy is $policy, and user preference is $userPreference",
|
"returns $expected when policy is $policy, and user preference is $userPreference",
|
||||||
async ({ policy, userPreference, expected }) => {
|
async ({ policy, userPreference, expected }) => {
|
||||||
userDecryptionOptionsSubject.next(new UserDecryptionOptions({ hasMasterPassword: true }));
|
userDecryptionOptionsSubject.next(new UserDecryptionOptions({ hasMasterPassword: true }));
|
||||||
policyService.getAll$.mockReturnValue(
|
policyService.policiesByType$.mockReturnValue(
|
||||||
of(policy === null ? [] : ([{ data: { action: policy } }] as unknown as Policy[])),
|
of(policy === null ? [] : ([{ data: { action: policy } }] as unknown as Policy[])),
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -213,7 +213,7 @@ describe("VaultTimeoutSettingsService", () => {
|
|||||||
userDecryptionOptionsSubject.next(
|
userDecryptionOptionsSubject.next(
|
||||||
new UserDecryptionOptions({ hasMasterPassword: false }),
|
new UserDecryptionOptions({ hasMasterPassword: false }),
|
||||||
);
|
);
|
||||||
policyService.getAll$.mockReturnValue(
|
policyService.policiesByType$.mockReturnValue(
|
||||||
of(policy === null ? [] : ([{ data: { action: policy } }] as unknown as Policy[])),
|
of(policy === null ? [] : ([{ data: { action: policy } }] as unknown as Policy[])),
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -257,7 +257,7 @@ describe("VaultTimeoutSettingsService", () => {
|
|||||||
"when policy is %s, and vault timeout is %s, returns %s",
|
"when policy is %s, and vault timeout is %s, returns %s",
|
||||||
async (policy, vaultTimeout, expected) => {
|
async (policy, vaultTimeout, expected) => {
|
||||||
userDecryptionOptionsSubject.next(new UserDecryptionOptions({ hasMasterPassword: true }));
|
userDecryptionOptionsSubject.next(new UserDecryptionOptions({ hasMasterPassword: true }));
|
||||||
policyService.getAll$.mockReturnValue(
|
policyService.policiesByType$.mockReturnValue(
|
||||||
of(policy === null ? [] : ([{ data: { minutes: policy } }] as unknown as Policy[])),
|
of(policy === null ? [] : ([{ data: { minutes: policy } }] as unknown as Policy[])),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import {
|
|||||||
distinctUntilChanged,
|
distinctUntilChanged,
|
||||||
firstValueFrom,
|
firstValueFrom,
|
||||||
from,
|
from,
|
||||||
map,
|
|
||||||
shareReplay,
|
shareReplay,
|
||||||
switchMap,
|
switchMap,
|
||||||
tap,
|
tap,
|
||||||
@@ -24,6 +23,7 @@ import { BiometricStateService, KeyService } from "@bitwarden/key-management";
|
|||||||
import { PolicyService } from "../../../admin-console/abstractions/policy/policy.service.abstraction";
|
import { PolicyService } from "../../../admin-console/abstractions/policy/policy.service.abstraction";
|
||||||
import { PolicyType } from "../../../admin-console/enums";
|
import { PolicyType } from "../../../admin-console/enums";
|
||||||
import { Policy } from "../../../admin-console/models/domain/policy";
|
import { Policy } from "../../../admin-console/models/domain/policy";
|
||||||
|
import { getFirstPolicy } from "../../../admin-console/services/policy/default-policy.service";
|
||||||
import { AccountService } from "../../../auth/abstractions/account.service";
|
import { AccountService } from "../../../auth/abstractions/account.service";
|
||||||
import { TokenService } from "../../../auth/abstractions/token.service";
|
import { TokenService } from "../../../auth/abstractions/token.service";
|
||||||
import { LogService } from "../../../platform/abstractions/log.service";
|
import { LogService } from "../../../platform/abstractions/log.service";
|
||||||
@@ -266,8 +266,8 @@ export class VaultTimeoutSettingsService implements VaultTimeoutSettingsServiceA
|
|||||||
}
|
}
|
||||||
|
|
||||||
return this.policyService
|
return this.policyService
|
||||||
.getAll$(PolicyType.MaximumVaultTimeout, userId)
|
.policiesByType$(PolicyType.MaximumVaultTimeout, userId)
|
||||||
.pipe(map((policies) => policies[0] ?? null));
|
.pipe(getFirstPolicy);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async getAvailableVaultTimeoutActions(userId?: string): Promise<VaultTimeoutAction[]> {
|
private async getAvailableVaultTimeoutActions(userId?: string): Promise<VaultTimeoutAction[]> {
|
||||||
|
|||||||
@@ -329,7 +329,12 @@ export class ImportComponent implements OnInit, OnDestroy, AfterViewInit {
|
|||||||
|
|
||||||
private async handlePolicies() {
|
private async handlePolicies() {
|
||||||
combineLatest([
|
combineLatest([
|
||||||
this.policyService.policyAppliesToActiveUser$(PolicyType.PersonalOwnership),
|
this.accountService.activeAccount$.pipe(
|
||||||
|
getUserId,
|
||||||
|
switchMap((userId) =>
|
||||||
|
this.policyService.policyAppliesToUser$(PolicyType.PersonalOwnership, userId),
|
||||||
|
),
|
||||||
|
),
|
||||||
this.organizations$,
|
this.organizations$,
|
||||||
])
|
])
|
||||||
.pipe(takeUntil(this.destroy$))
|
.pipe(takeUntil(this.destroy$))
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import { Account, AccountService } from "@bitwarden/common/auth/abstractions/acc
|
|||||||
import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction";
|
import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction";
|
||||||
import { VerificationType } from "@bitwarden/common/auth/enums/verification-type";
|
import { VerificationType } from "@bitwarden/common/auth/enums/verification-type";
|
||||||
import { ForceSetPasswordReason } from "@bitwarden/common/auth/models/domain/force-set-password-reason";
|
import { ForceSetPasswordReason } from "@bitwarden/common/auth/models/domain/force-set-password-reason";
|
||||||
|
import { getUserId } from "@bitwarden/common/auth/services/account.service";
|
||||||
import {
|
import {
|
||||||
MasterPasswordVerification,
|
MasterPasswordVerification,
|
||||||
MasterPasswordVerificationResponse,
|
MasterPasswordVerificationResponse,
|
||||||
@@ -584,7 +585,10 @@ export class LockComponent implements OnInit, OnDestroy {
|
|||||||
// If we do not have any saved policies, attempt to load them from the service
|
// If we do not have any saved policies, attempt to load them from the service
|
||||||
if (this.enforcedMasterPasswordOptions == undefined) {
|
if (this.enforcedMasterPasswordOptions == undefined) {
|
||||||
this.enforcedMasterPasswordOptions = await firstValueFrom(
|
this.enforcedMasterPasswordOptions = await firstValueFrom(
|
||||||
this.policyService.masterPasswordPolicyOptions$(),
|
this.accountService.activeAccount$.pipe(
|
||||||
|
getUserId,
|
||||||
|
switchMap((userId) => this.policyService.masterPasswordPolicyOptions$(userId)),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ import {
|
|||||||
import { ReactiveFormsModule, UntypedFormBuilder, Validators } from "@angular/forms";
|
import { ReactiveFormsModule, UntypedFormBuilder, Validators } from "@angular/forms";
|
||||||
import {
|
import {
|
||||||
combineLatest,
|
combineLatest,
|
||||||
firstValueFrom,
|
|
||||||
map,
|
map,
|
||||||
merge,
|
merge,
|
||||||
Observable,
|
Observable,
|
||||||
@@ -212,12 +211,18 @@ export class ExportComponent implements OnInit, OnDestroy, AfterViewInit {
|
|||||||
this.formDisabled.emit(c === "DISABLED");
|
this.formDisabled.emit(c === "DISABLED");
|
||||||
});
|
});
|
||||||
|
|
||||||
// policies
|
this.disablePersonalVaultExportPolicy$ = this.accountService.activeAccount$.pipe(
|
||||||
this.disablePersonalVaultExportPolicy$ = this.policyService.policyAppliesToActiveUser$(
|
getUserId,
|
||||||
PolicyType.DisablePersonalVaultExport,
|
switchMap((userId) =>
|
||||||
|
this.policyService.policyAppliesToUser$(PolicyType.DisablePersonalVaultExport, userId),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
this.disablePersonalOwnershipPolicy$ = this.policyService.policyAppliesToActiveUser$(
|
|
||||||
PolicyType.PersonalOwnership,
|
this.disablePersonalOwnershipPolicy$ = this.accountService.activeAccount$.pipe(
|
||||||
|
getUserId,
|
||||||
|
switchMap((userId) =>
|
||||||
|
this.policyService.policyAppliesToUser$(PolicyType.PersonalOwnership, userId),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
merge(
|
merge(
|
||||||
@@ -227,8 +232,6 @@ export class ExportComponent implements OnInit, OnDestroy, AfterViewInit {
|
|||||||
.pipe(startWith(0), takeUntil(this.destroy$))
|
.pipe(startWith(0), takeUntil(this.destroy$))
|
||||||
.subscribe(() => this.adjustValidators());
|
.subscribe(() => this.adjustValidators());
|
||||||
|
|
||||||
const userId = await firstValueFrom(getUserId(this.accountService.activeAccount$));
|
|
||||||
|
|
||||||
// Wire up the password generation for the password-protected export
|
// Wire up the password generation for the password-protected export
|
||||||
const account$ = this.accountService.activeAccount$.pipe(
|
const account$ = this.accountService.activeAccount$.pipe(
|
||||||
pin({
|
pin({
|
||||||
@@ -251,9 +254,14 @@ export class ExportComponent implements OnInit, OnDestroy, AfterViewInit {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (this.organizationId) {
|
if (this.organizationId) {
|
||||||
this.organizations$ = this.organizationService
|
this.organizations$ = this.accountService.activeAccount$.pipe(
|
||||||
.memberOrganizations$(userId)
|
getUserId,
|
||||||
.pipe(map((orgs) => orgs.filter((org) => org.id == this.organizationId)));
|
switchMap((userId) =>
|
||||||
|
this.organizationService
|
||||||
|
.memberOrganizations$(userId)
|
||||||
|
.pipe(map((orgs) => orgs.filter((org) => org.id == this.organizationId))),
|
||||||
|
),
|
||||||
|
);
|
||||||
this.exportForm.controls.vaultSelector.patchValue(this.organizationId);
|
this.exportForm.controls.vaultSelector.patchValue(this.organizationId);
|
||||||
this.exportForm.controls.vaultSelector.disable();
|
this.exportForm.controls.vaultSelector.disable();
|
||||||
|
|
||||||
@@ -263,7 +271,10 @@ export class ExportComponent implements OnInit, OnDestroy, AfterViewInit {
|
|||||||
|
|
||||||
this.organizations$ = combineLatest({
|
this.organizations$ = combineLatest({
|
||||||
collections: this.collectionService.decryptedCollections$,
|
collections: this.collectionService.decryptedCollections$,
|
||||||
memberOrganizations: this.organizationService.memberOrganizations$(userId),
|
memberOrganizations: this.accountService.activeAccount$.pipe(
|
||||||
|
getUserId,
|
||||||
|
switchMap((userId) => this.organizationService.memberOrganizations$(userId)),
|
||||||
|
),
|
||||||
}).pipe(
|
}).pipe(
|
||||||
map(({ collections, memberOrganizations }) => {
|
map(({ collections, memberOrganizations }) => {
|
||||||
const managedCollectionsOrgIds = new Set(
|
const managedCollectionsOrgIds = new Set(
|
||||||
|
|||||||
@@ -207,7 +207,7 @@ const providers = {
|
|||||||
describe("CredentialGeneratorService", () => {
|
describe("CredentialGeneratorService", () => {
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
await accountService.switchAccount(SomeUser);
|
await accountService.switchAccount(SomeUser);
|
||||||
policyService.getAll$.mockImplementation(() => new BehaviorSubject([]).asObservable());
|
policyService.policiesByType$.mockImplementation(() => new BehaviorSubject([]).asObservable());
|
||||||
i18nService.t.mockImplementation((key: string) => key);
|
i18nService.t.mockImplementation((key: string) => key);
|
||||||
apiService.fetch.mockImplementation(() => Promise.resolve(mock<Response>()));
|
apiService.fetch.mockImplementation(() => Promise.resolve(mock<Response>()));
|
||||||
jest.clearAllMocks();
|
jest.clearAllMocks();
|
||||||
@@ -567,7 +567,7 @@ describe("CredentialGeneratorService", () => {
|
|||||||
// awareness; they exercise the logic without being comprehensive
|
// awareness; they exercise the logic without being comprehensive
|
||||||
it("enforces the active user's policy", async () => {
|
it("enforces the active user's policy", async () => {
|
||||||
const policy$ = new BehaviorSubject([passwordOverridePolicy]);
|
const policy$ = new BehaviorSubject([passwordOverridePolicy]);
|
||||||
policyService.getAll$.mockReturnValue(policy$);
|
policyService.policiesByType$.mockReturnValue(policy$);
|
||||||
const generator = new CredentialGeneratorService(
|
const generator = new CredentialGeneratorService(
|
||||||
randomizer,
|
randomizer,
|
||||||
policyService,
|
policyService,
|
||||||
@@ -578,15 +578,22 @@ describe("CredentialGeneratorService", () => {
|
|||||||
|
|
||||||
const result = await firstValueFrom(generator.algorithms$(["password"], { account$ }));
|
const result = await firstValueFrom(generator.algorithms$(["password"], { account$ }));
|
||||||
|
|
||||||
expect(policyService.getAll$).toHaveBeenCalledWith(PolicyType.PasswordGenerator, SomeUser);
|
expect(policyService.policiesByType$).toHaveBeenCalledWith(
|
||||||
|
PolicyType.PasswordGenerator,
|
||||||
|
SomeUser,
|
||||||
|
);
|
||||||
expect(result.some((a) => a.id === Generators.password.id)).toBeTruthy();
|
expect(result.some((a) => a.id === Generators.password.id)).toBeTruthy();
|
||||||
expect(result.some((a) => a.id === Generators.passphrase.id)).toBeFalsy();
|
expect(result.some((a) => a.id === Generators.passphrase.id)).toBeFalsy();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("follows changes to the active user", async () => {
|
it("follows changes to the active user", async () => {
|
||||||
const account$ = new BehaviorSubject(accounts[SomeUser]);
|
const account$ = new BehaviorSubject(accounts[SomeUser]);
|
||||||
policyService.getAll$.mockReturnValueOnce(new BehaviorSubject([passwordOverridePolicy]));
|
policyService.policiesByType$.mockReturnValueOnce(
|
||||||
policyService.getAll$.mockReturnValueOnce(new BehaviorSubject([passphraseOverridePolicy]));
|
new BehaviorSubject([passwordOverridePolicy]),
|
||||||
|
);
|
||||||
|
policyService.policiesByType$.mockReturnValueOnce(
|
||||||
|
new BehaviorSubject([passphraseOverridePolicy]),
|
||||||
|
);
|
||||||
const generator = new CredentialGeneratorService(
|
const generator = new CredentialGeneratorService(
|
||||||
randomizer,
|
randomizer,
|
||||||
policyService,
|
policyService,
|
||||||
@@ -603,7 +610,7 @@ describe("CredentialGeneratorService", () => {
|
|||||||
|
|
||||||
const [someResult, anotherResult] = results;
|
const [someResult, anotherResult] = results;
|
||||||
|
|
||||||
expect(policyService.getAll$).toHaveBeenNthCalledWith(
|
expect(policyService.policiesByType$).toHaveBeenNthCalledWith(
|
||||||
1,
|
1,
|
||||||
PolicyType.PasswordGenerator,
|
PolicyType.PasswordGenerator,
|
||||||
SomeUser,
|
SomeUser,
|
||||||
@@ -611,7 +618,7 @@ describe("CredentialGeneratorService", () => {
|
|||||||
expect(someResult.some((a: any) => a.id === Generators.password.id)).toBeTruthy();
|
expect(someResult.some((a: any) => a.id === Generators.password.id)).toBeTruthy();
|
||||||
expect(someResult.some((a: any) => a.id === Generators.passphrase.id)).toBeFalsy();
|
expect(someResult.some((a: any) => a.id === Generators.passphrase.id)).toBeFalsy();
|
||||||
|
|
||||||
expect(policyService.getAll$).toHaveBeenNthCalledWith(
|
expect(policyService.policiesByType$).toHaveBeenNthCalledWith(
|
||||||
2,
|
2,
|
||||||
PolicyType.PasswordGenerator,
|
PolicyType.PasswordGenerator,
|
||||||
AnotherUser,
|
AnotherUser,
|
||||||
@@ -621,7 +628,9 @@ describe("CredentialGeneratorService", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("reads an arbitrary user's settings", async () => {
|
it("reads an arbitrary user's settings", async () => {
|
||||||
policyService.getAll$.mockReturnValueOnce(new BehaviorSubject([passwordOverridePolicy]));
|
policyService.policiesByType$.mockReturnValueOnce(
|
||||||
|
new BehaviorSubject([passwordOverridePolicy]),
|
||||||
|
);
|
||||||
const generator = new CredentialGeneratorService(
|
const generator = new CredentialGeneratorService(
|
||||||
randomizer,
|
randomizer,
|
||||||
policyService,
|
policyService,
|
||||||
@@ -633,14 +642,21 @@ describe("CredentialGeneratorService", () => {
|
|||||||
|
|
||||||
const result = await firstValueFrom(generator.algorithms$("password", { account$ }));
|
const result = await firstValueFrom(generator.algorithms$("password", { account$ }));
|
||||||
|
|
||||||
expect(policyService.getAll$).toHaveBeenCalledWith(PolicyType.PasswordGenerator, AnotherUser);
|
expect(policyService.policiesByType$).toHaveBeenCalledWith(
|
||||||
|
PolicyType.PasswordGenerator,
|
||||||
|
AnotherUser,
|
||||||
|
);
|
||||||
expect(result.some((a: any) => a.id === Generators.password.id)).toBeTruthy();
|
expect(result.some((a: any) => a.id === Generators.password.id)).toBeTruthy();
|
||||||
expect(result.some((a: any) => a.id === Generators.passphrase.id)).toBeFalsy();
|
expect(result.some((a: any) => a.id === Generators.passphrase.id)).toBeFalsy();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("follows changes to the arbitrary user", async () => {
|
it("follows changes to the arbitrary user", async () => {
|
||||||
policyService.getAll$.mockReturnValueOnce(new BehaviorSubject([passwordOverridePolicy]));
|
policyService.policiesByType$.mockReturnValueOnce(
|
||||||
policyService.getAll$.mockReturnValueOnce(new BehaviorSubject([passphraseOverridePolicy]));
|
new BehaviorSubject([passwordOverridePolicy]),
|
||||||
|
);
|
||||||
|
policyService.policiesByType$.mockReturnValueOnce(
|
||||||
|
new BehaviorSubject([passphraseOverridePolicy]),
|
||||||
|
);
|
||||||
const generator = new CredentialGeneratorService(
|
const generator = new CredentialGeneratorService(
|
||||||
randomizer,
|
randomizer,
|
||||||
policyService,
|
policyService,
|
||||||
@@ -658,17 +674,25 @@ describe("CredentialGeneratorService", () => {
|
|||||||
sub.unsubscribe();
|
sub.unsubscribe();
|
||||||
|
|
||||||
const [someResult, anotherResult] = results;
|
const [someResult, anotherResult] = results;
|
||||||
expect(policyService.getAll$).toHaveBeenCalledWith(PolicyType.PasswordGenerator, SomeUser);
|
expect(policyService.policiesByType$).toHaveBeenCalledWith(
|
||||||
|
PolicyType.PasswordGenerator,
|
||||||
|
SomeUser,
|
||||||
|
);
|
||||||
expect(someResult.some((a: any) => a.id === Generators.password.id)).toBeTruthy();
|
expect(someResult.some((a: any) => a.id === Generators.password.id)).toBeTruthy();
|
||||||
expect(someResult.some((a: any) => a.id === Generators.passphrase.id)).toBeFalsy();
|
expect(someResult.some((a: any) => a.id === Generators.passphrase.id)).toBeFalsy();
|
||||||
|
|
||||||
expect(policyService.getAll$).toHaveBeenCalledWith(PolicyType.PasswordGenerator, AnotherUser);
|
expect(policyService.policiesByType$).toHaveBeenCalledWith(
|
||||||
|
PolicyType.PasswordGenerator,
|
||||||
|
AnotherUser,
|
||||||
|
);
|
||||||
expect(anotherResult.some((a: any) => a.id === Generators.passphrase.id)).toBeTruthy();
|
expect(anotherResult.some((a: any) => a.id === Generators.passphrase.id)).toBeTruthy();
|
||||||
expect(anotherResult.some((a: any) => a.id === Generators.password.id)).toBeFalsy();
|
expect(anotherResult.some((a: any) => a.id === Generators.password.id)).toBeFalsy();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("errors when the arbitrary user's stream errors", async () => {
|
it("errors when the arbitrary user's stream errors", async () => {
|
||||||
policyService.getAll$.mockReturnValueOnce(new BehaviorSubject([passwordOverridePolicy]));
|
policyService.policiesByType$.mockReturnValueOnce(
|
||||||
|
new BehaviorSubject([passwordOverridePolicy]),
|
||||||
|
);
|
||||||
const generator = new CredentialGeneratorService(
|
const generator = new CredentialGeneratorService(
|
||||||
randomizer,
|
randomizer,
|
||||||
policyService,
|
policyService,
|
||||||
@@ -692,7 +716,9 @@ describe("CredentialGeneratorService", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("completes when the arbitrary user's stream completes", async () => {
|
it("completes when the arbitrary user's stream completes", async () => {
|
||||||
policyService.getAll$.mockReturnValueOnce(new BehaviorSubject([passwordOverridePolicy]));
|
policyService.policiesByType$.mockReturnValueOnce(
|
||||||
|
new BehaviorSubject([passwordOverridePolicy]),
|
||||||
|
);
|
||||||
const generator = new CredentialGeneratorService(
|
const generator = new CredentialGeneratorService(
|
||||||
randomizer,
|
randomizer,
|
||||||
policyService,
|
policyService,
|
||||||
@@ -716,7 +742,9 @@ describe("CredentialGeneratorService", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("ignores repeated arbitrary user emissions", async () => {
|
it("ignores repeated arbitrary user emissions", async () => {
|
||||||
policyService.getAll$.mockReturnValueOnce(new BehaviorSubject([passwordOverridePolicy]));
|
policyService.policiesByType$.mockReturnValueOnce(
|
||||||
|
new BehaviorSubject([passwordOverridePolicy]),
|
||||||
|
);
|
||||||
const generator = new CredentialGeneratorService(
|
const generator = new CredentialGeneratorService(
|
||||||
randomizer,
|
randomizer,
|
||||||
policyService,
|
policyService,
|
||||||
@@ -780,7 +808,7 @@ describe("CredentialGeneratorService", () => {
|
|||||||
const settings = { foo: "value" };
|
const settings = { foo: "value" };
|
||||||
await stateProvider.setUserState(SettingsKey, settings, SomeUser);
|
await stateProvider.setUserState(SettingsKey, settings, SomeUser);
|
||||||
const policy$ = new BehaviorSubject([somePolicy]);
|
const policy$ = new BehaviorSubject([somePolicy]);
|
||||||
policyService.getAll$.mockReturnValue(policy$);
|
policyService.policiesByType$.mockReturnValue(policy$);
|
||||||
const generator = new CredentialGeneratorService(
|
const generator = new CredentialGeneratorService(
|
||||||
randomizer,
|
randomizer,
|
||||||
policyService,
|
policyService,
|
||||||
@@ -908,7 +936,7 @@ describe("CredentialGeneratorService", () => {
|
|||||||
);
|
);
|
||||||
const account$ = new BehaviorSubject(accounts[SomeUser]).asObservable();
|
const account$ = new BehaviorSubject(accounts[SomeUser]).asObservable();
|
||||||
const policy$ = new BehaviorSubject([somePolicy]);
|
const policy$ = new BehaviorSubject([somePolicy]);
|
||||||
policyService.getAll$.mockReturnValue(policy$);
|
policyService.policiesByType$.mockReturnValue(policy$);
|
||||||
|
|
||||||
const result = await firstValueFrom(generator.policy$(SomeConfiguration, { account$ }));
|
const result = await firstValueFrom(generator.policy$(SomeConfiguration, { account$ }));
|
||||||
|
|
||||||
@@ -926,7 +954,7 @@ describe("CredentialGeneratorService", () => {
|
|||||||
const account = new BehaviorSubject(accounts[SomeUser]);
|
const account = new BehaviorSubject(accounts[SomeUser]);
|
||||||
const account$ = account.asObservable();
|
const account$ = account.asObservable();
|
||||||
const somePolicySubject = new BehaviorSubject([somePolicy]);
|
const somePolicySubject = new BehaviorSubject([somePolicy]);
|
||||||
policyService.getAll$.mockReturnValueOnce(somePolicySubject.asObservable());
|
policyService.policiesByType$.mockReturnValueOnce(somePolicySubject.asObservable());
|
||||||
const emissions: GeneratorConstraints<SomeSettings>[] = [];
|
const emissions: GeneratorConstraints<SomeSettings>[] = [];
|
||||||
const sub = generator
|
const sub = generator
|
||||||
.policy$(SomeConfiguration, { account$ })
|
.policy$(SomeConfiguration, { account$ })
|
||||||
@@ -954,7 +982,9 @@ describe("CredentialGeneratorService", () => {
|
|||||||
const account$ = account.asObservable();
|
const account$ = account.asObservable();
|
||||||
const somePolicy$ = new BehaviorSubject([somePolicy]).asObservable();
|
const somePolicy$ = new BehaviorSubject([somePolicy]).asObservable();
|
||||||
const anotherPolicy$ = new BehaviorSubject([]).asObservable();
|
const anotherPolicy$ = new BehaviorSubject([]).asObservable();
|
||||||
policyService.getAll$.mockReturnValueOnce(somePolicy$).mockReturnValueOnce(anotherPolicy$);
|
policyService.policiesByType$
|
||||||
|
.mockReturnValueOnce(somePolicy$)
|
||||||
|
.mockReturnValueOnce(anotherPolicy$);
|
||||||
const emissions: GeneratorConstraints<SomeSettings>[] = [];
|
const emissions: GeneratorConstraints<SomeSettings>[] = [];
|
||||||
const sub = generator
|
const sub = generator
|
||||||
.policy$(SomeConfiguration, { account$ })
|
.policy$(SomeConfiguration, { account$ })
|
||||||
|
|||||||
@@ -114,11 +114,13 @@ export class CredentialGeneratorService {
|
|||||||
const algorithms$ = dependencies.account$.pipe(
|
const algorithms$ = dependencies.account$.pipe(
|
||||||
distinctUntilChanged(),
|
distinctUntilChanged(),
|
||||||
switchMap((account) => {
|
switchMap((account) => {
|
||||||
const policies$ = this.policyService.getAll$(PolicyType.PasswordGenerator, account.id).pipe(
|
const policies$ = this.policyService
|
||||||
map((p) => new Set(availableAlgorithms(p))),
|
.policiesByType$(PolicyType.PasswordGenerator, account.id)
|
||||||
// complete policy emissions otherwise `switchMap` holds `algorithms$` open indefinitely
|
.pipe(
|
||||||
takeUntil(anyComplete(dependencies.account$)),
|
map((p) => new Set(availableAlgorithms(p))),
|
||||||
);
|
// complete policy emissions otherwise `switchMap` holds `algorithms$` open indefinitely
|
||||||
|
takeUntil(anyComplete(dependencies.account$)),
|
||||||
|
);
|
||||||
return policies$;
|
return policies$;
|
||||||
}),
|
}),
|
||||||
map((available) => {
|
map((available) => {
|
||||||
@@ -280,7 +282,7 @@ export class CredentialGeneratorService {
|
|||||||
switchMap(({ userId, email }) => {
|
switchMap(({ userId, email }) => {
|
||||||
// complete policy emissions otherwise `switchMap` holds `policies$` open indefinitely
|
// complete policy emissions otherwise `switchMap` holds `policies$` open indefinitely
|
||||||
const policies$ = this.policyService
|
const policies$ = this.policyService
|
||||||
.getAll$(configuration.policy.type, userId)
|
.policiesByType$(configuration.policy.type, userId)
|
||||||
.pipe(
|
.pipe(
|
||||||
mapPolicyToConstraints(configuration.policy, email),
|
mapPolicyToConstraints(configuration.policy, email),
|
||||||
takeUntil(anyComplete(dependencies.account$)),
|
takeUntil(anyComplete(dependencies.account$)),
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ function mockPolicyService(config?: { state?: BehaviorSubject<Policy[]> }) {
|
|||||||
const service = mock<PolicyService>();
|
const service = mock<PolicyService>();
|
||||||
|
|
||||||
const stateValue = config?.state ?? new BehaviorSubject<Policy[]>([null]);
|
const stateValue = config?.state ?? new BehaviorSubject<Policy[]>([null]);
|
||||||
service.getAll$.mockReturnValue(stateValue);
|
service.policiesByType$.mockReturnValue(stateValue);
|
||||||
|
|
||||||
return service;
|
return service;
|
||||||
}
|
}
|
||||||
@@ -103,7 +103,7 @@ describe("Password generator service", () => {
|
|||||||
|
|
||||||
await firstValueFrom(service.evaluator$(SomeUser));
|
await firstValueFrom(service.evaluator$(SomeUser));
|
||||||
|
|
||||||
expect(policy.getAll$).toHaveBeenCalledWith(PolicyType.PasswordGenerator, SomeUser);
|
expect(policy.policiesByType$).toHaveBeenCalledWith(PolicyType.PasswordGenerator, SomeUser);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should map the policy using the generation strategy", async () => {
|
it("should map the policy using the generation strategy", async () => {
|
||||||
@@ -150,7 +150,7 @@ describe("Password generator service", () => {
|
|||||||
await firstValueFrom(service.evaluator$(SomeUser));
|
await firstValueFrom(service.evaluator$(SomeUser));
|
||||||
await firstValueFrom(service.evaluator$(SomeUser));
|
await firstValueFrom(service.evaluator$(SomeUser));
|
||||||
|
|
||||||
expect(policy.getAll$).toHaveBeenCalledTimes(1);
|
expect(policy.policiesByType$).toHaveBeenCalledTimes(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should cache the password generator policy for each user", async () => {
|
it("should cache the password generator policy for each user", async () => {
|
||||||
@@ -161,8 +161,16 @@ describe("Password generator service", () => {
|
|||||||
await firstValueFrom(service.evaluator$(SomeUser));
|
await firstValueFrom(service.evaluator$(SomeUser));
|
||||||
await firstValueFrom(service.evaluator$(AnotherUser));
|
await firstValueFrom(service.evaluator$(AnotherUser));
|
||||||
|
|
||||||
expect(policy.getAll$).toHaveBeenNthCalledWith(1, PolicyType.PasswordGenerator, SomeUser);
|
expect(policy.policiesByType$).toHaveBeenNthCalledWith(
|
||||||
expect(policy.getAll$).toHaveBeenNthCalledWith(2, PolicyType.PasswordGenerator, AnotherUser);
|
1,
|
||||||
|
PolicyType.PasswordGenerator,
|
||||||
|
SomeUser,
|
||||||
|
);
|
||||||
|
expect(policy.policiesByType$).toHaveBeenNthCalledWith(
|
||||||
|
2,
|
||||||
|
PolicyType.PasswordGenerator,
|
||||||
|
AnotherUser,
|
||||||
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ export class DefaultGeneratorService<Options, Policy> implements GeneratorServic
|
|||||||
}
|
}
|
||||||
|
|
||||||
private createEvaluator(userId: UserId) {
|
private createEvaluator(userId: UserId) {
|
||||||
const evaluator$ = this.policy.getAll$(this.strategy.policy, userId).pipe(
|
const evaluator$ = this.policy.policiesByType$(this.strategy.policy, userId).pipe(
|
||||||
// create the evaluator from the policies
|
// create the evaluator from the policies
|
||||||
this.strategy.toEvaluator(),
|
this.strategy.toEvaluator(),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -155,7 +155,7 @@ const NoPolicyProfile: CoreProfileMetadata<SomeSettings> = {
|
|||||||
|
|
||||||
describe("GeneratorProfileProvider", () => {
|
describe("GeneratorProfileProvider", () => {
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
policyService.getAll$.mockImplementation(() => new BehaviorSubject([]).asObservable());
|
policyService.policiesByType$.mockImplementation(() => new BehaviorSubject([]).asObservable());
|
||||||
const encryptor$ = new BehaviorSubject({ userId: SomeUser, encryptor });
|
const encryptor$ = new BehaviorSubject({ userId: SomeUser, encryptor });
|
||||||
encryptorProvider.userEncryptor$.mockReturnValue(encryptor$);
|
encryptorProvider.userEncryptor$.mockReturnValue(encryptor$);
|
||||||
jest.clearAllMocks();
|
jest.clearAllMocks();
|
||||||
@@ -211,7 +211,7 @@ describe("GeneratorProfileProvider", () => {
|
|||||||
const profileProvider = new GeneratorProfileProvider(dependencyProvider, policyService);
|
const profileProvider = new GeneratorProfileProvider(dependencyProvider, policyService);
|
||||||
const account$ = new BehaviorSubject(accounts[SomeUser]).asObservable();
|
const account$ = new BehaviorSubject(accounts[SomeUser]).asObservable();
|
||||||
const policy$ = new BehaviorSubject([somePolicy]);
|
const policy$ = new BehaviorSubject([somePolicy]);
|
||||||
policyService.getAll$.mockReturnValue(policy$);
|
policyService.policiesByType$.mockReturnValue(policy$);
|
||||||
|
|
||||||
const result = await firstValueFrom(profileProvider.constraints$(SomeProfile, { account$ }));
|
const result = await firstValueFrom(profileProvider.constraints$(SomeProfile, { account$ }));
|
||||||
|
|
||||||
@@ -223,7 +223,7 @@ describe("GeneratorProfileProvider", () => {
|
|||||||
const account$ = new BehaviorSubject(accounts[SomeUser]).asObservable();
|
const account$ = new BehaviorSubject(accounts[SomeUser]).asObservable();
|
||||||
const expectedPolicy = [somePolicy];
|
const expectedPolicy = [somePolicy];
|
||||||
const policy$ = new BehaviorSubject(expectedPolicy);
|
const policy$ = new BehaviorSubject(expectedPolicy);
|
||||||
policyService.getAll$.mockReturnValue(policy$);
|
policyService.policiesByType$.mockReturnValue(policy$);
|
||||||
|
|
||||||
await firstValueFrom(profileProvider.constraints$(SomeProfile, { account$ }));
|
await firstValueFrom(profileProvider.constraints$(SomeProfile, { account$ }));
|
||||||
|
|
||||||
@@ -284,7 +284,7 @@ describe("GeneratorProfileProvider", () => {
|
|||||||
const account = new BehaviorSubject(accounts[SomeUser]);
|
const account = new BehaviorSubject(accounts[SomeUser]);
|
||||||
const account$ = account.asObservable();
|
const account$ = account.asObservable();
|
||||||
const somePolicySubject = new BehaviorSubject([somePolicy]);
|
const somePolicySubject = new BehaviorSubject([somePolicy]);
|
||||||
policyService.getAll$.mockReturnValueOnce(somePolicySubject.asObservable());
|
policyService.policiesByType$.mockReturnValueOnce(somePolicySubject.asObservable());
|
||||||
const emissions: GeneratorConstraints<SomeSettings>[] = [];
|
const emissions: GeneratorConstraints<SomeSettings>[] = [];
|
||||||
const sub = profileProvider
|
const sub = profileProvider
|
||||||
.constraints$(SomeProfile, { account$ })
|
.constraints$(SomeProfile, { account$ })
|
||||||
|
|||||||
@@ -86,7 +86,7 @@ export class GeneratorProfileProvider {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const policies$ = profile.constraints.type
|
const policies$ = profile.constraints.type
|
||||||
? this.policyService.getAll$(profile.constraints.type, account.id)
|
? this.policyService.policiesByType$(profile.constraints.type, account.id)
|
||||||
: of([]);
|
: of([]);
|
||||||
|
|
||||||
const context: ProfileContext<Settings> = {
|
const context: ProfileContext<Settings> = {
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ describe("DefaultGeneratorNavigationService", () => {
|
|||||||
describe("evaluator$", () => {
|
describe("evaluator$", () => {
|
||||||
it("emits a GeneratorNavigationEvaluator", async () => {
|
it("emits a GeneratorNavigationEvaluator", async () => {
|
||||||
const policyService = mock<PolicyService>({
|
const policyService = mock<PolicyService>({
|
||||||
getAll$() {
|
policiesByType$() {
|
||||||
return of([]);
|
return of([]);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -62,7 +62,7 @@ describe("DefaultGeneratorNavigationService", () => {
|
|||||||
describe("enforcePolicy", () => {
|
describe("enforcePolicy", () => {
|
||||||
it("applies policy", async () => {
|
it("applies policy", async () => {
|
||||||
const policyService = mock<PolicyService>({
|
const policyService = mock<PolicyService>({
|
||||||
getAll$(_type: PolicyType, _user: UserId) {
|
policiesByType$(_type: PolicyType, _user: UserId) {
|
||||||
return of([
|
return of([
|
||||||
new Policy({
|
new Policy({
|
||||||
id: "" as any,
|
id: "" as any,
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ export class DefaultGeneratorNavigationService implements GeneratorNavigationSer
|
|||||||
* @param userId: Identifies the user making the request
|
* @param userId: Identifies the user making the request
|
||||||
*/
|
*/
|
||||||
evaluator$(userId: UserId) {
|
evaluator$(userId: UserId) {
|
||||||
const evaluator$ = this.policy.getAll$(PolicyType.PasswordGenerator, userId).pipe(
|
const evaluator$ = this.policy.policiesByType$(PolicyType.PasswordGenerator, userId).pipe(
|
||||||
reduceCollection(preferPassword, DisabledGeneratorNavigationPolicy),
|
reduceCollection(preferPassword, DisabledGeneratorNavigationPolicy),
|
||||||
distinctIfShallowMatch(),
|
distinctIfShallowMatch(),
|
||||||
map((policy) => new GeneratorNavigationEvaluator(policy)),
|
map((policy) => new GeneratorNavigationEvaluator(policy)),
|
||||||
|
|||||||
@@ -99,7 +99,7 @@ export class SendOptionsComponent implements OnInit {
|
|||||||
this.accountService.activeAccount$
|
this.accountService.activeAccount$
|
||||||
.pipe(
|
.pipe(
|
||||||
getUserId,
|
getUserId,
|
||||||
switchMap((userId) => this.policyService.getAll$(PolicyType.SendOptions, userId)),
|
switchMap((userId) => this.policyService.policiesByType$(PolicyType.SendOptions, userId)),
|
||||||
map((policies) => policies?.some((p) => p.data.disableHideEmail)),
|
map((policies) => policies?.some((p) => p.data.disableHideEmail)),
|
||||||
takeUntilDestroyed(),
|
takeUntilDestroyed(),
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
// 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 { inject, Injectable } from "@angular/core";
|
import { inject, Injectable } from "@angular/core";
|
||||||
import { combineLatest, firstValueFrom, map } from "rxjs";
|
import { combineLatest, firstValueFrom, map, switchMap } from "rxjs";
|
||||||
|
|
||||||
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 { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||||
|
import { getUserId } from "@bitwarden/common/auth/services/account.service";
|
||||||
import { SendType } from "@bitwarden/common/tools/send/enums/send-type";
|
import { SendType } from "@bitwarden/common/tools/send/enums/send-type";
|
||||||
import { SendService } from "@bitwarden/common/tools/send/services/send.service.abstraction";
|
import { SendService } from "@bitwarden/common/tools/send/services/send.service.abstraction";
|
||||||
import { SendId } from "@bitwarden/common/types/guid";
|
import { SendId } from "@bitwarden/common/types/guid";
|
||||||
@@ -22,6 +24,7 @@ import {
|
|||||||
export class DefaultSendFormConfigService implements SendFormConfigService {
|
export class DefaultSendFormConfigService implements SendFormConfigService {
|
||||||
private policyService: PolicyService = inject(PolicyService);
|
private policyService: PolicyService = inject(PolicyService);
|
||||||
private sendService: SendService = inject(SendService);
|
private sendService: SendService = inject(SendService);
|
||||||
|
private accountService: AccountService = inject(AccountService);
|
||||||
|
|
||||||
async buildConfig(
|
async buildConfig(
|
||||||
mode: SendFormMode,
|
mode: SendFormMode,
|
||||||
@@ -40,9 +43,11 @@ export class DefaultSendFormConfigService implements SendFormConfigService {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private areSendsEnabled$ = this.policyService
|
private areSendsEnabled$ = this.accountService.activeAccount$.pipe(
|
||||||
.policyAppliesToActiveUser$(PolicyType.DisableSend)
|
getUserId,
|
||||||
.pipe(map((p) => !p));
|
switchMap((userId) => this.policyService.policyAppliesToUser$(PolicyType.DisableSend, userId)),
|
||||||
|
map((p) => !p),
|
||||||
|
);
|
||||||
|
|
||||||
private getSend(id?: SendId) {
|
private getSend(id?: SendId) {
|
||||||
if (id == null) {
|
if (id == null) {
|
||||||
|
|||||||
@@ -89,9 +89,13 @@ export class DefaultCipherFormConfigService implements CipherFormConfigService {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private allowPersonalOwnership$ = this.policyService
|
private allowPersonalOwnership$ = this.accountService.activeAccount$.pipe(
|
||||||
.policyAppliesToActiveUser$(PolicyType.PersonalOwnership)
|
getUserId,
|
||||||
.pipe(map((p) => !p));
|
switchMap((userId) =>
|
||||||
|
this.policyService.policyAppliesToUser$(PolicyType.PersonalOwnership, userId),
|
||||||
|
),
|
||||||
|
map((p) => !p),
|
||||||
|
);
|
||||||
|
|
||||||
private getCipher(userId: UserId, id?: CipherId): Promise<Cipher | null> {
|
private getCipher(userId: UserId, id?: CipherId): Promise<Cipher | null> {
|
||||||
if (id == null) {
|
if (id == null) {
|
||||||
|
|||||||
Reference in New Issue
Block a user