mirror of
https://github.com/bitwarden/browser
synced 2025-12-16 00:03:56 +00:00
feat(policies): PM-19311 Enforce URI Match Defaults organization policy (#16430)
* feat(policies): Add URI Match Default Policy enum Signed-off-by: Ben Brooks <bbrooks@bitwarden.com> * feat(policies): Add logic to read and set the default from policy data Signed-off-by: Ben Brooks <bbrooks@bitwarden.com> * In settings, set default, disable select and display hint Signed-off-by: Ben Brooks <bbrooks@bitwarden.com> * Move applyUriMatchPolicy to writeValue function Signed-off-by: Ben Brooks <bbrooks@bitwarden.com> * Remove code to disable individual options because we're disabling the entire select Signed-off-by: Ben Brooks <bbrooks@bitwarden.com> * WiP move resolved defaultUriMatch to Domain Settings Service * Merge branch 'main' of github.com:bitwarden/clients into pm-19310-uri-match-policy Signed-off-by: Ben Brooks <bbrooks@bitwarden.com> * Merge branch 'main' of github.com:bitwarden/clients into pm-19310-uri-match-policy Signed-off-by: Ben Brooks <bbrooks@bitwarden.com> * Merge branch 'main' of github.com:bitwarden/clients into pm-19310-uri-match-policy Signed-off-by: Ben Brooks <bbrooks@bitwarden.com> * Merge branch 'main' of github.com:bitwarden/clients into pm-19310-uri-match-policy Signed-off-by: Ben Brooks <bbrooks@bitwarden.com> * Merge branch 'main' of github.com:bitwarden/clients into pm-19310-uri-match-policy Signed-off-by: Ben Brooks <bbrooks@bitwarden.com> * Merge branch 'main' of github.com:bitwarden/clients into pm-19310-uri-match-policy Signed-off-by: Ben Brooks <bbrooks@bitwarden.com> * Address local test failures related to null observables Signed-off-by: Ben Brooks <bbrooks@bitwarden.com> * add missing services * Fix test to use new resolvedDefaultUriMatchStrategy$ Signed-off-by: Ben Brooks <bbrooks@bitwarden.com> * Move definition of defaultMatchDetection$ out of constructor Signed-off-by: Ben Brooks <bbrooks@bitwarden.com> * Update cipher form story to use resolvedDefaultUriMatchStrategy Signed-off-by: Ben Brooks <bbrooks@bitwarden.com> * Merge branch 'pm-19310-uri-match-policy' of github.com:bitwarden/clients into pm-19310-uri-match-policy Signed-off-by: Ben Brooks <bbrooks@bitwarden.com> * Fix incomplete storybook mock in cipher form stories Signed-off-by: Ben Brooks <bbrooks@bitwarden.com> * Add I18n key description Signed-off-by: Ben Brooks <bbrooks@bitwarden.com> * Add comment regarding potential memory leak in domain settings service Signed-off-by: Ben Brooks <bbrooks@bitwarden.com> * Add explicit check for null policy data Signed-off-by: Ben Brooks <bbrooks@bitwarden.com> * Add explicit check for undefined policy data Signed-off-by: Ben Brooks <bbrooks@bitwarden.com> * Merge branch 'pm-19310-uri-match-policy' of github.com:bitwarden/clients into pm-19310-uri-match-policy Signed-off-by: Ben Brooks <bbrooks@bitwarden.com> * Add shareReplay to address potential memory leak Signed-off-by: Ben Brooks <bbrooks@bitwarden.com> * Merge branch 'pm-19310-uri-match-policy' of github.com:bitwarden/clients into pm-19310-uri-match-policy Signed-off-by: Ben Brooks <bbrooks@bitwarden.com> * Merge branch 'main' of github.com:bitwarden/clients into pm-19310-uri-match-policy Signed-off-by: Ben Brooks <bbrooks@bitwarden.com> * Remove outdated comment Signed-off-by: Ben Brooks <bbrooks@bitwarden.com> * Improve type safety/validation and null checks in DefaultDomainSettingsService Signed-off-by: Ben Brooks <bbrooks@bitwarden.com> --------- Signed-off-by: Ben Brooks <bbrooks@bitwarden.com> Co-authored-by: Jonathan Prusik <jprusik@classynemesis.com>
This commit is contained in:
@@ -17,5 +17,6 @@ export enum PolicyType {
|
||||
FreeFamiliesSponsorshipPolicy = 13, // Disables free families plan for organization
|
||||
RemoveUnlockWithPin = 14, // Do not allow members to unlock their account with a PIN.
|
||||
RestrictedItemTypes = 15, // Restricts item types that can be created within an organization
|
||||
UriMatchDefaults = 16, // Sets the default URI matching strategy for all users within an organization
|
||||
AutotypeDefaultSetting = 17, // Sets the default autotype setting for desktop app
|
||||
}
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
import { mock } from "jest-mock-extended";
|
||||
import { firstValueFrom, of } from "rxjs";
|
||||
|
||||
import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
|
||||
|
||||
import { FakeStateProvider, FakeAccountService, mockAccountServiceWith } from "../../../spec";
|
||||
import { Utils } from "../../platform/misc/utils";
|
||||
import { UserId } from "../../types/guid";
|
||||
@@ -10,6 +13,7 @@ describe("DefaultDomainSettingsService", () => {
|
||||
let domainSettingsService: DomainSettingsService;
|
||||
const mockUserId = Utils.newGuid() as UserId;
|
||||
const accountService: FakeAccountService = mockAccountServiceWith(mockUserId);
|
||||
const policyService = mock<PolicyService>();
|
||||
const fakeStateProvider: FakeStateProvider = new FakeStateProvider(accountService);
|
||||
|
||||
const mockEquivalentDomains = [
|
||||
@@ -19,7 +23,11 @@ describe("DefaultDomainSettingsService", () => {
|
||||
];
|
||||
|
||||
beforeEach(() => {
|
||||
domainSettingsService = new DefaultDomainSettingsService(fakeStateProvider);
|
||||
domainSettingsService = new DefaultDomainSettingsService(
|
||||
fakeStateProvider,
|
||||
policyService,
|
||||
accountService,
|
||||
);
|
||||
|
||||
jest.spyOn(domainSettingsService, "getUrlEquivalentDomains");
|
||||
domainSettingsService.equivalentDomains$ = of(mockEquivalentDomains);
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
// FIXME: Update this file to be type safe and remove this and next line
|
||||
// @ts-strict-ignore
|
||||
import { map, Observable } from "rxjs";
|
||||
import { combineLatest, map, Observable, switchMap, shareReplay } from "rxjs";
|
||||
|
||||
import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
|
||||
import { PolicyType } from "@bitwarden/common/admin-console/enums/policy-type.enum";
|
||||
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 {
|
||||
NeverDomains,
|
||||
@@ -87,6 +93,18 @@ export abstract class DomainSettingsService {
|
||||
defaultUriMatchStrategy$: Observable<UriMatchStrategySetting>;
|
||||
setDefaultUriMatchStrategy: (newValue: UriMatchStrategySetting) => Promise<void>;
|
||||
|
||||
/**
|
||||
* Org policy value for default for URI-matching
|
||||
* strategies. Can be overridden by cipher-specific settings.
|
||||
*/
|
||||
defaultUriMatchStrategyPolicy$: Observable<UriMatchStrategySetting>;
|
||||
|
||||
/**
|
||||
* Resolved (concerning user setting, org policy, etc) default for URI-matching
|
||||
* strategies. Can be overridden by cipher-specific settings.
|
||||
*/
|
||||
resolvedDefaultUriMatchStrategy$: Observable<UriMatchStrategySetting>;
|
||||
|
||||
/**
|
||||
* Helper function for the common resolution of a given URL against equivalent domains
|
||||
*/
|
||||
@@ -109,7 +127,15 @@ export class DefaultDomainSettingsService implements DomainSettingsService {
|
||||
private defaultUriMatchStrategyState: ActiveUserState<UriMatchStrategySetting>;
|
||||
readonly defaultUriMatchStrategy$: Observable<UriMatchStrategySetting>;
|
||||
|
||||
constructor(private stateProvider: StateProvider) {
|
||||
readonly defaultUriMatchStrategyPolicy$: Observable<UriMatchStrategySetting>;
|
||||
|
||||
readonly resolvedDefaultUriMatchStrategy$: Observable<UriMatchStrategySetting>;
|
||||
|
||||
constructor(
|
||||
private stateProvider: StateProvider,
|
||||
private policyService: PolicyService,
|
||||
private accountService: AccountService,
|
||||
) {
|
||||
this.showFaviconsState = this.stateProvider.getGlobal(SHOW_FAVICONS);
|
||||
this.showFavicons$ = this.showFaviconsState.state$.pipe(map((x) => x ?? true));
|
||||
|
||||
@@ -129,6 +155,31 @@ export class DefaultDomainSettingsService implements DomainSettingsService {
|
||||
this.defaultUriMatchStrategy$ = this.defaultUriMatchStrategyState.state$.pipe(
|
||||
map((x) => x ?? UriMatchStrategy.Domain),
|
||||
);
|
||||
|
||||
this.defaultUriMatchStrategyPolicy$ = this.accountService.activeAccount$.pipe(
|
||||
getUserId,
|
||||
switchMap((userId) =>
|
||||
this.policyService.policiesByType$(PolicyType.UriMatchDefaults, userId),
|
||||
),
|
||||
getFirstPolicy,
|
||||
map((policy) => {
|
||||
if (!policy?.enabled || policy?.data == null) {
|
||||
return null;
|
||||
}
|
||||
const data = policy.data?.defaultUriMatchStrategy;
|
||||
// Validate that data is a valid UriMatchStrategy value
|
||||
return Object.values(UriMatchStrategy).includes(data) ? data : null;
|
||||
}),
|
||||
shareReplay({ bufferSize: 1, refCount: true }),
|
||||
);
|
||||
|
||||
this.resolvedDefaultUriMatchStrategy$ = combineLatest([
|
||||
this.defaultUriMatchStrategy$,
|
||||
this.defaultUriMatchStrategyPolicy$,
|
||||
]).pipe(
|
||||
map(([userSettingValue, policySettingValue]) => policySettingValue || userSettingValue),
|
||||
shareReplay({ bufferSize: 1, refCount: true }),
|
||||
);
|
||||
}
|
||||
|
||||
async setShowFavicons(newValue: boolean): Promise<void> {
|
||||
|
||||
@@ -634,7 +634,9 @@ export class CipherService implements CipherServiceAbstraction {
|
||||
const equivalentDomains = await firstValueFrom(
|
||||
this.domainSettingsService.getUrlEquivalentDomains(url),
|
||||
);
|
||||
defaultMatch ??= await firstValueFrom(this.domainSettingsService.defaultUriMatchStrategy$);
|
||||
defaultMatch ??= await firstValueFrom(
|
||||
this.domainSettingsService.resolvedDefaultUriMatchStrategy$,
|
||||
);
|
||||
|
||||
const archiveFeatureEnabled = await this.configService.getFeatureFlag(
|
||||
FeatureFlag.PM19148_InnovationArchive,
|
||||
|
||||
Reference in New Issue
Block a user