mirror of
https://github.com/bitwarden/browser
synced 2025-12-20 10:13:31 +00:00
[PM-5562] Implement Domain Settings state provider (#8226)
* create domain settings state provider * replace callsites for defaultUriMatch and neverDomains with DomainSettingsService equivalents * replace callsites for equivalentDomains with DomainSettingsService equivalents and clean up unused AccountSettingsSettings * add migrations for domain settings state * do not use enum for URI match strategy constants and types * add getUrlEquivalentDomains test * PR suggestions/cleanup * refactor getUrlEquivalentDomains to return an observable Co-authored-by: Justin Baur <19896123+justindbaur@users.noreply.github.com> Co-authored-by: ✨ Audrey ✨ <ajensen@bitwarden.com> * update tests * add UriMatchStrategy docs notes * service class renames * use service abstraction at callsites previously using service class directly --------- Co-authored-by: Justin Baur <19896123+justindbaur@users.noreply.github.com> Co-authored-by: ✨ Audrey ✨ <ajensen@bitwarden.com>
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
import { NeverDomains } from "@bitwarden/common/models/domain/domain-service";
|
||||
import { FolderView } from "@bitwarden/common/vault/models/view/folder.view";
|
||||
|
||||
import { NotificationQueueMessageTypes } from "../../enums/notification-queue-message-type.enum";
|
||||
@@ -111,6 +112,7 @@ type NotificationBackgroundExtensionMessageHandlers = {
|
||||
collectPageDetailsResponse: ({ message }: BackgroundMessageParam) => Promise<void>;
|
||||
bgGetEnableChangedPasswordPrompt: () => Promise<boolean>;
|
||||
bgGetEnableAddedLoginPrompt: () => Promise<boolean>;
|
||||
bgGetExcludedDomains: () => Promise<NeverDomains>;
|
||||
getWebVaultUrlForNotification: () => string;
|
||||
};
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import { firstValueFrom } from "rxjs";
|
||||
import { PolicyService } from "@bitwarden/common/admin-console/services/policy/policy.service";
|
||||
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
|
||||
import { AuthService } from "@bitwarden/common/auth/services/auth.service";
|
||||
import { DomainSettingsService } from "@bitwarden/common/autofill/services/domain-settings.service";
|
||||
import { UserNotificationSettingsService } from "@bitwarden/common/autofill/services/user-notification-settings.service";
|
||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||
import { EnvironmentService } from "@bitwarden/common/platform/services/environment.service";
|
||||
@@ -47,6 +48,7 @@ describe("NotificationBackground", () => {
|
||||
const folderService = mock<FolderService>();
|
||||
const stateService = mock<BrowserStateService>();
|
||||
const userNotificationSettingsService = mock<UserNotificationSettingsService>();
|
||||
const domainSettingsService = mock<DomainSettingsService>();
|
||||
const environmentService = mock<EnvironmentService>();
|
||||
const logService = mock<LogService>();
|
||||
|
||||
@@ -59,6 +61,7 @@ describe("NotificationBackground", () => {
|
||||
folderService,
|
||||
stateService,
|
||||
userNotificationSettingsService,
|
||||
domainSettingsService,
|
||||
environmentService,
|
||||
logService,
|
||||
);
|
||||
|
||||
@@ -5,7 +5,9 @@ import { PolicyType } from "@bitwarden/common/admin-console/enums";
|
||||
import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service";
|
||||
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
|
||||
import { NOTIFICATION_BAR_LIFESPAN_MS } from "@bitwarden/common/autofill/constants";
|
||||
import { DomainSettingsService } from "@bitwarden/common/autofill/services/domain-settings.service";
|
||||
import { UserNotificationSettingsServiceAbstraction } from "@bitwarden/common/autofill/services/user-notification-settings.service";
|
||||
import { NeverDomains } from "@bitwarden/common/models/domain/domain-service";
|
||||
import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service";
|
||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
||||
@@ -60,6 +62,7 @@ export default class NotificationBackground {
|
||||
bgReopenUnlockPopout: ({ sender }) => this.openUnlockPopout(sender.tab),
|
||||
bgGetEnableChangedPasswordPrompt: () => this.getEnableChangedPasswordPrompt(),
|
||||
bgGetEnableAddedLoginPrompt: () => this.getEnableAddedLoginPrompt(),
|
||||
bgGetExcludedDomains: () => this.getExcludedDomains(),
|
||||
getWebVaultUrlForNotification: () => this.getWebVaultUrl(),
|
||||
};
|
||||
|
||||
@@ -71,6 +74,7 @@ export default class NotificationBackground {
|
||||
private folderService: FolderService,
|
||||
private stateService: BrowserStateService,
|
||||
private userNotificationSettingsService: UserNotificationSettingsServiceAbstraction,
|
||||
private domainSettingsService: DomainSettingsService,
|
||||
private environmentService: EnvironmentService,
|
||||
private logService: LogService,
|
||||
) {}
|
||||
@@ -99,6 +103,13 @@ export default class NotificationBackground {
|
||||
return await firstValueFrom(this.userNotificationSettingsService.enableAddedLoginPrompt$);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the neverDomains setting from the domain settings service.
|
||||
*/
|
||||
async getExcludedDomains(): Promise<NeverDomains> {
|
||||
return await firstValueFrom(this.domainSettingsService.neverDomains$);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the notification queue for any messages that need to be sent to the
|
||||
* specified tab. If no tab is specified, the current tab will be used.
|
||||
|
||||
@@ -6,10 +6,6 @@ import {
|
||||
EventCollectionServiceInitOptions,
|
||||
eventCollectionServiceFactory,
|
||||
} from "../../../background/service-factories/event-collection-service.factory";
|
||||
import {
|
||||
settingsServiceFactory,
|
||||
SettingsServiceInitOptions,
|
||||
} from "../../../background/service-factories/settings-service.factory";
|
||||
import {
|
||||
CachedServices,
|
||||
factory,
|
||||
@@ -38,6 +34,10 @@ import {
|
||||
AutofillSettingsServiceInitOptions,
|
||||
autofillSettingsServiceFactory,
|
||||
} from "./autofill-settings-service.factory";
|
||||
import {
|
||||
DomainSettingsServiceInitOptions,
|
||||
domainSettingsServiceFactory,
|
||||
} from "./domain-settings-service.factory";
|
||||
|
||||
type AutoFillServiceOptions = FactoryOptions;
|
||||
|
||||
@@ -48,8 +48,8 @@ export type AutoFillServiceInitOptions = AutoFillServiceOptions &
|
||||
TotpServiceInitOptions &
|
||||
EventCollectionServiceInitOptions &
|
||||
LogServiceInitOptions &
|
||||
SettingsServiceInitOptions &
|
||||
UserVerificationServiceInitOptions;
|
||||
UserVerificationServiceInitOptions &
|
||||
DomainSettingsServiceInitOptions;
|
||||
|
||||
export function autofillServiceFactory(
|
||||
cache: { autofillService?: AbstractAutoFillService } & CachedServices,
|
||||
@@ -67,7 +67,7 @@ export function autofillServiceFactory(
|
||||
await totpServiceFactory(cache, opts),
|
||||
await eventCollectionServiceFactory(cache, opts),
|
||||
await logServiceFactory(cache, opts),
|
||||
await settingsServiceFactory(cache, opts),
|
||||
await domainSettingsServiceFactory(cache, opts),
|
||||
await userVerificationServiceFactory(cache, opts),
|
||||
),
|
||||
);
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
import { DefaultDomainSettingsService } from "@bitwarden/common/autofill/services/domain-settings.service";
|
||||
|
||||
import {
|
||||
CachedServices,
|
||||
factory,
|
||||
FactoryOptions,
|
||||
} from "../../../platform/background/service-factories/factory-options";
|
||||
import {
|
||||
stateProviderFactory,
|
||||
StateProviderInitOptions,
|
||||
} from "../../../platform/background/service-factories/state-provider.factory";
|
||||
|
||||
export type DomainSettingsServiceInitOptions = FactoryOptions & StateProviderInitOptions;
|
||||
|
||||
export function domainSettingsServiceFactory(
|
||||
cache: { domainSettingsService?: DefaultDomainSettingsService } & CachedServices,
|
||||
opts: DomainSettingsServiceInitOptions,
|
||||
): Promise<DefaultDomainSettingsService> {
|
||||
return factory(
|
||||
cache,
|
||||
"domainSettingsService",
|
||||
opts,
|
||||
async () => new DefaultDomainSettingsService(await stateProviderFactory(cache, opts)),
|
||||
);
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service";
|
||||
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
|
||||
import { UriMatchStrategy } from "@bitwarden/common/models/domain/domain-service";
|
||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
|
||||
import { UriMatchType } from "@bitwarden/common/vault/enums";
|
||||
|
||||
import { BrowserApi } from "../../platform/browser/browser-api";
|
||||
|
||||
@@ -73,7 +73,7 @@ export default class WebRequestBackground {
|
||||
const ciphers = await this.cipherService.getAllDecryptedForUrl(
|
||||
domain,
|
||||
null,
|
||||
UriMatchType.Host,
|
||||
UriMatchStrategy.Host,
|
||||
);
|
||||
if (ciphers == null || ciphers.length !== 1) {
|
||||
error();
|
||||
|
||||
@@ -6,7 +6,7 @@ import AutofillField from "../models/autofill-field";
|
||||
import { WatchedForm } from "../models/watched-form";
|
||||
import { NotificationBarIframeInitData } from "../notification/abstractions/notification-bar";
|
||||
import { FormData } from "../services/abstractions/autofill.service";
|
||||
import { GlobalSettings, UserSettings } from "../types";
|
||||
import { UserSettings } from "../types";
|
||||
import {
|
||||
getFromLocalStorage,
|
||||
sendExtensionMessage,
|
||||
@@ -94,10 +94,11 @@ async function loadNotificationBar() {
|
||||
"bgGetEnableChangedPasswordPrompt",
|
||||
);
|
||||
const enableAddedLoginPrompt = await sendExtensionMessage("bgGetEnableAddedLoginPrompt");
|
||||
const excludedDomains = await sendExtensionMessage("bgGetExcludedDomains");
|
||||
|
||||
let showNotificationBar = true;
|
||||
// Look up the active user id from storage
|
||||
const activeUserIdKey = "activeUserId";
|
||||
const globalStorageKey = "global";
|
||||
let activeUserId: string;
|
||||
|
||||
const activeUserStorageValue = await getFromLocalStorage(activeUserIdKey);
|
||||
@@ -109,9 +110,6 @@ async function loadNotificationBar() {
|
||||
const userSettingsStorageValue = await getFromLocalStorage(activeUserId);
|
||||
if (userSettingsStorageValue[activeUserId]) {
|
||||
const userSettings: UserSettings = userSettingsStorageValue[activeUserId].settings;
|
||||
const globalSettings: GlobalSettings = (await getFromLocalStorage(globalStorageKey))[
|
||||
globalStorageKey
|
||||
];
|
||||
|
||||
// Do not show the notification bar on the Bitwarden vault
|
||||
// because they can add logins and change passwords there
|
||||
@@ -122,8 +120,8 @@ async function loadNotificationBar() {
|
||||
// show the notification bar on (for login detail collection or password change).
|
||||
// It is managed in the Settings > Excluded Domains page in the browser extension.
|
||||
// Example: '{"bitwarden.com":null}'
|
||||
const excludedDomainsDict = globalSettings.neverDomains;
|
||||
if (!excludedDomainsDict || !(window.location.hostname in excludedDomainsDict)) {
|
||||
|
||||
if (!excludedDomains || !(window.location.hostname in excludedDomains)) {
|
||||
if (enableAddedLoginPrompt || enableChangedPasswordPrompt) {
|
||||
// If the user has not disabled both notifications, then handle the initial page change (null -> actual page)
|
||||
handlePageChange();
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
import { Component, OnInit } from "@angular/core";
|
||||
import { firstValueFrom } from "rxjs";
|
||||
|
||||
import { SettingsService } from "@bitwarden/common/abstractions/settings.service";
|
||||
import { AutofillOverlayVisibility } from "@bitwarden/common/autofill/constants";
|
||||
import { AutofillSettingsServiceAbstraction } from "@bitwarden/common/autofill/services/autofill-settings.service";
|
||||
import { DomainSettingsService } from "@bitwarden/common/autofill/services/domain-settings.service";
|
||||
import { InlineMenuVisibilitySetting } from "@bitwarden/common/autofill/types";
|
||||
import {
|
||||
UriMatchStrategy,
|
||||
UriMatchStrategySetting,
|
||||
} from "@bitwarden/common/models/domain/domain-service";
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
|
||||
import { UriMatchType } from "@bitwarden/common/vault/enums";
|
||||
import { DialogService } from "@bitwarden/components";
|
||||
|
||||
import { BrowserApi } from "../../../platform/browser/browser-api";
|
||||
@@ -28,16 +30,15 @@ export class AutofillComponent implements OnInit {
|
||||
enableAutoFillOnPageLoad = false;
|
||||
autoFillOnPageLoadDefault = false;
|
||||
autoFillOnPageLoadOptions: any[];
|
||||
defaultUriMatch = UriMatchType.Domain;
|
||||
defaultUriMatch: UriMatchStrategySetting = UriMatchStrategy.Domain;
|
||||
uriMatchOptions: any[];
|
||||
autofillKeyboardHelperText: string;
|
||||
accountSwitcherEnabled = false;
|
||||
|
||||
constructor(
|
||||
private stateService: StateService,
|
||||
private i18nService: I18nService,
|
||||
private platformUtilsService: PlatformUtilsService,
|
||||
private settingsService: SettingsService,
|
||||
private domainSettingsService: DomainSettingsService,
|
||||
private autofillService: AutofillService,
|
||||
private dialogService: DialogService,
|
||||
private autofillSettingsService: AutofillSettingsServiceAbstraction,
|
||||
@@ -61,12 +62,12 @@ export class AutofillComponent implements OnInit {
|
||||
{ name: i18nService.t("autoFillOnPageLoadNo"), value: false },
|
||||
];
|
||||
this.uriMatchOptions = [
|
||||
{ name: i18nService.t("baseDomain"), value: UriMatchType.Domain },
|
||||
{ name: i18nService.t("host"), value: UriMatchType.Host },
|
||||
{ name: i18nService.t("startsWith"), value: UriMatchType.StartsWith },
|
||||
{ name: i18nService.t("regEx"), value: UriMatchType.RegularExpression },
|
||||
{ name: i18nService.t("exact"), value: UriMatchType.Exact },
|
||||
{ name: i18nService.t("never"), value: UriMatchType.Never },
|
||||
{ name: i18nService.t("baseDomain"), value: UriMatchStrategy.Domain },
|
||||
{ name: i18nService.t("host"), value: UriMatchStrategy.Host },
|
||||
{ name: i18nService.t("startsWith"), value: UriMatchStrategy.StartsWith },
|
||||
{ name: i18nService.t("regEx"), value: UriMatchStrategy.RegularExpression },
|
||||
{ name: i18nService.t("exact"), value: UriMatchStrategy.Exact },
|
||||
{ name: i18nService.t("never"), value: UriMatchStrategy.Never },
|
||||
];
|
||||
|
||||
this.accountSwitcherEnabled = enableAccountSwitching();
|
||||
@@ -94,8 +95,10 @@ export class AutofillComponent implements OnInit {
|
||||
this.autofillSettingsService.autofillOnPageLoadDefault$,
|
||||
);
|
||||
|
||||
const defaultUriMatch = await this.stateService.getDefaultUriMatch();
|
||||
this.defaultUriMatch = defaultUriMatch == null ? UriMatchType.Domain : defaultUriMatch;
|
||||
const defaultUriMatch = await firstValueFrom(
|
||||
this.domainSettingsService.defaultUriMatchStrategy$,
|
||||
);
|
||||
this.defaultUriMatch = defaultUriMatch == null ? UriMatchStrategy.Domain : defaultUriMatch;
|
||||
|
||||
const command = await this.platformUtilsService.getAutofillKeyboardShortcut();
|
||||
await this.setAutofillKeyboardHelperText(command);
|
||||
@@ -119,7 +122,7 @@ export class AutofillComponent implements OnInit {
|
||||
}
|
||||
|
||||
async saveDefaultUriMatch() {
|
||||
await this.stateService.setDefaultUriMatch(this.defaultUriMatch);
|
||||
await this.domainSettingsService.setDefaultUriMatchStrategy(this.defaultUriMatch);
|
||||
}
|
||||
|
||||
private async setAutofillKeyboardHelperText(command: string) {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { UriMatchType, CipherType } from "@bitwarden/common/vault/enums";
|
||||
import { UriMatchStrategySetting } from "@bitwarden/common/models/domain/domain-service";
|
||||
import { CipherType } from "@bitwarden/common/vault/enums";
|
||||
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
|
||||
|
||||
import AutofillField from "../../models/autofill-field";
|
||||
@@ -40,7 +41,7 @@ export interface GenerateFillScriptOptions {
|
||||
allowTotpAutofill: boolean;
|
||||
cipher: CipherView;
|
||||
tabUrl: string;
|
||||
defaultUriMatch: UriMatchType;
|
||||
defaultUriMatch: UriMatchStrategySetting;
|
||||
}
|
||||
|
||||
export abstract class AutofillService {
|
||||
|
||||
@@ -1,19 +1,25 @@
|
||||
import { mock, mockReset } from "jest-mock-extended";
|
||||
import { of } from "rxjs";
|
||||
|
||||
import { UserVerificationService } from "@bitwarden/common/auth/services/user-verification/user-verification.service";
|
||||
import { AutofillOverlayVisibility } from "@bitwarden/common/autofill/constants";
|
||||
import { AutofillSettingsService } from "@bitwarden/common/autofill/services/autofill-settings.service";
|
||||
import { EventType } from "@bitwarden/common/enums";
|
||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||
import { EventCollectionService } from "@bitwarden/common/services/event/event-collection.service";
|
||||
import { SettingsService } from "@bitwarden/common/services/settings.service";
|
||||
import {
|
||||
FieldType,
|
||||
LinkedIdType,
|
||||
LoginLinkedId,
|
||||
UriMatchType,
|
||||
CipherType,
|
||||
} from "@bitwarden/common/vault/enums";
|
||||
DefaultDomainSettingsService,
|
||||
DomainSettingsService,
|
||||
} from "@bitwarden/common/autofill/services/domain-settings.service";
|
||||
import { EventType } from "@bitwarden/common/enums";
|
||||
import { UriMatchStrategy } from "@bitwarden/common/models/domain/domain-service";
|
||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
||||
import { EventCollectionService } from "@bitwarden/common/services/event/event-collection.service";
|
||||
import {
|
||||
FakeStateProvider,
|
||||
FakeAccountService,
|
||||
mockAccountServiceWith,
|
||||
} from "@bitwarden/common/spec";
|
||||
import { UserId } from "@bitwarden/common/types/guid";
|
||||
import { FieldType, LinkedIdType, LoginLinkedId, CipherType } from "@bitwarden/common/vault/enums";
|
||||
import { CipherRepromptType } from "@bitwarden/common/vault/enums/cipher-reprompt-type";
|
||||
import { CardView } from "@bitwarden/common/vault/models/view/card.view";
|
||||
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
|
||||
@@ -47,15 +53,24 @@ import {
|
||||
import { AutoFillConstants, IdentityAutoFillConstants } from "./autofill-constants";
|
||||
import AutofillService from "./autofill.service";
|
||||
|
||||
const mockEquivalentDomains = [
|
||||
["example.com", "exampleapp.com", "example.co.uk", "ejemplo.es"],
|
||||
["bitwarden.com", "bitwarden.co.uk", "sm-bitwarden.com"],
|
||||
["example.co.uk", "exampleapp.co.uk"],
|
||||
];
|
||||
|
||||
describe("AutofillService", () => {
|
||||
let autofillService: AutofillService;
|
||||
const cipherService = mock<CipherService>();
|
||||
const stateService = mock<BrowserStateService>();
|
||||
const autofillSettingsService = mock<AutofillSettingsService>();
|
||||
const mockUserId = Utils.newGuid() as UserId;
|
||||
const accountService: FakeAccountService = mockAccountServiceWith(mockUserId);
|
||||
const fakeStateProvider: FakeStateProvider = new FakeStateProvider(accountService);
|
||||
let domainSettingsService: DomainSettingsService;
|
||||
const totpService = mock<TotpService>();
|
||||
const eventCollectionService = mock<EventCollectionService>();
|
||||
const logService = mock<LogService>();
|
||||
const settingsService = mock<SettingsService>();
|
||||
const userVerificationService = mock<UserVerificationService>();
|
||||
|
||||
beforeEach(() => {
|
||||
@@ -66,9 +81,12 @@ describe("AutofillService", () => {
|
||||
totpService,
|
||||
eventCollectionService,
|
||||
logService,
|
||||
settingsService,
|
||||
domainSettingsService,
|
||||
userVerificationService,
|
||||
);
|
||||
|
||||
domainSettingsService = new DefaultDomainSettingsService(fakeStateProvider);
|
||||
domainSettingsService.equivalentDomains$ = of(mockEquivalentDomains);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
@@ -407,6 +425,8 @@ describe("AutofillService", () => {
|
||||
autofillOptions.cipher.login.matchesUri = jest.fn().mockReturnValue(true);
|
||||
autofillOptions.cipher.login.username = "username";
|
||||
autofillOptions.cipher.login.password = "password";
|
||||
|
||||
jest.spyOn(autofillService, "getDefaultUriMatchStrategy").mockResolvedValue(0);
|
||||
});
|
||||
|
||||
describe("given a set of autofill options that are incomplete", () => {
|
||||
@@ -468,7 +488,6 @@ describe("AutofillService", () => {
|
||||
|
||||
it("will autofill login data for a page", async () => {
|
||||
jest.spyOn(stateService, "getCanAccessPremium");
|
||||
jest.spyOn(stateService, "getDefaultUriMatch");
|
||||
jest.spyOn(autofillService as any, "generateFillScript");
|
||||
jest.spyOn(autofillService as any, "generateLoginFillScript");
|
||||
jest.spyOn(logService, "info");
|
||||
@@ -479,7 +498,7 @@ describe("AutofillService", () => {
|
||||
|
||||
const currentAutofillPageDetails = autofillOptions.pageDetails[0];
|
||||
expect(stateService.getCanAccessPremium).toHaveBeenCalled();
|
||||
expect(stateService.getDefaultUriMatch).toHaveBeenCalled();
|
||||
expect(autofillService["getDefaultUriMatchStrategy"]).toHaveBeenCalled();
|
||||
expect(autofillService["generateFillScript"]).toHaveBeenCalledWith(
|
||||
currentAutofillPageDetails.details,
|
||||
{
|
||||
@@ -1488,7 +1507,7 @@ describe("AutofillService", () => {
|
||||
};
|
||||
defaultLoginUriView = mock<LoginUriView>({
|
||||
uri: "https://www.example.com",
|
||||
match: UriMatchType.Domain,
|
||||
match: UriMatchStrategy.Domain,
|
||||
});
|
||||
options = createGenerateFillScriptOptionsMock();
|
||||
options.cipher.login = mock<LoginView>({
|
||||
@@ -1559,13 +1578,13 @@ describe("AutofillService", () => {
|
||||
]);
|
||||
});
|
||||
|
||||
it("skips adding any login uri views that have a UriMatchType of Never to the list of saved urls", async () => {
|
||||
it("skips adding any login uri views that have a UriMatchStrategySetting of Never to the list of saved urls", async () => {
|
||||
const secondUriView = mock<LoginUriView>({
|
||||
uri: "https://www.second-example.com",
|
||||
});
|
||||
const thirdUriView = mock<LoginUriView>({
|
||||
uri: "https://www.third-example.com",
|
||||
match: UriMatchType.Never,
|
||||
match: UriMatchStrategy.Never,
|
||||
});
|
||||
options.cipher.login.uris = [defaultLoginUriView, secondUriView, thirdUriView];
|
||||
|
||||
@@ -2752,31 +2771,32 @@ describe("AutofillService", () => {
|
||||
});
|
||||
|
||||
describe("inUntrustedIframe", () => {
|
||||
it("returns a false value if the passed pageUrl is equal to the options tabUrl", () => {
|
||||
it("returns a false value if the passed pageUrl is equal to the options tabUrl", async () => {
|
||||
const pageUrl = "https://www.example.com";
|
||||
const tabUrl = "https://www.example.com";
|
||||
const generateFillScriptOptions = createGenerateFillScriptOptionsMock({ tabUrl });
|
||||
generateFillScriptOptions.cipher.login.matchesUri = jest.fn().mockReturnValueOnce(true);
|
||||
jest.spyOn(settingsService, "getEquivalentDomains");
|
||||
|
||||
const result = autofillService["inUntrustedIframe"](pageUrl, generateFillScriptOptions);
|
||||
const result = await autofillService["inUntrustedIframe"](pageUrl, generateFillScriptOptions);
|
||||
|
||||
expect(settingsService.getEquivalentDomains).not.toHaveBeenCalled();
|
||||
expect(generateFillScriptOptions.cipher.login.matchesUri).not.toHaveBeenCalled();
|
||||
expect(result).toBe(false);
|
||||
});
|
||||
|
||||
it("returns a false value if the passed pageUrl matches the domain of the tabUrl", () => {
|
||||
it("returns a false value if the passed pageUrl matches the domain of the tabUrl", async () => {
|
||||
const pageUrl = "https://subdomain.example.com";
|
||||
const tabUrl = "https://www.example.com";
|
||||
const equivalentDomains = new Set(["example.com"]);
|
||||
const equivalentDomains = new Set([
|
||||
"ejemplo.es",
|
||||
"example.co.uk",
|
||||
"example.com",
|
||||
"exampleapp.com",
|
||||
]);
|
||||
const generateFillScriptOptions = createGenerateFillScriptOptionsMock({ tabUrl });
|
||||
generateFillScriptOptions.cipher.login.matchesUri = jest.fn().mockReturnValueOnce(true);
|
||||
jest.spyOn(settingsService as any, "getEquivalentDomains").mockReturnValue(equivalentDomains);
|
||||
|
||||
const result = autofillService["inUntrustedIframe"](pageUrl, generateFillScriptOptions);
|
||||
const result = await autofillService["inUntrustedIframe"](pageUrl, generateFillScriptOptions);
|
||||
|
||||
expect(settingsService.getEquivalentDomains).toHaveBeenCalledWith(pageUrl);
|
||||
expect(generateFillScriptOptions.cipher.login.matchesUri).toHaveBeenCalledWith(
|
||||
pageUrl,
|
||||
equivalentDomains,
|
||||
@@ -2785,17 +2805,21 @@ describe("AutofillService", () => {
|
||||
expect(result).toBe(false);
|
||||
});
|
||||
|
||||
it("returns a true value if the passed pageUrl does not match the domain of the tabUrl", () => {
|
||||
it("returns a true value if the passed pageUrl does not match the domain of the tabUrl", async () => {
|
||||
const equivalentDomains = new Set([
|
||||
"ejemplo.es",
|
||||
"example.co.uk",
|
||||
"example.com",
|
||||
"exampleapp.com",
|
||||
]);
|
||||
domainSettingsService.equivalentDomains$ = of([["not-example.com"]]);
|
||||
const pageUrl = "https://subdomain.example.com";
|
||||
const tabUrl = "https://www.not-example.com";
|
||||
const equivalentDomains = new Set(["not-example.com"]);
|
||||
const generateFillScriptOptions = createGenerateFillScriptOptionsMock({ tabUrl });
|
||||
generateFillScriptOptions.cipher.login.matchesUri = jest.fn().mockReturnValueOnce(false);
|
||||
jest.spyOn(settingsService as any, "getEquivalentDomains").mockReturnValue(equivalentDomains);
|
||||
|
||||
const result = autofillService["inUntrustedIframe"](pageUrl, generateFillScriptOptions);
|
||||
const result = await autofillService["inUntrustedIframe"](pageUrl, generateFillScriptOptions);
|
||||
|
||||
expect(settingsService.getEquivalentDomains).toHaveBeenCalledWith(pageUrl);
|
||||
expect(generateFillScriptOptions.cipher.login.matchesUri).toHaveBeenCalledWith(
|
||||
pageUrl,
|
||||
equivalentDomains,
|
||||
|
||||
@@ -1,15 +1,19 @@
|
||||
import { firstValueFrom } from "rxjs";
|
||||
|
||||
import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service";
|
||||
import { SettingsService } from "@bitwarden/common/abstractions/settings.service";
|
||||
import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction";
|
||||
import { AutofillSettingsServiceAbstraction } from "@bitwarden/common/autofill/services/autofill-settings.service";
|
||||
import { DomainSettingsService } from "@bitwarden/common/autofill/services/domain-settings.service";
|
||||
import { InlineMenuVisibilitySetting } from "@bitwarden/common/autofill/types";
|
||||
import { EventType } from "@bitwarden/common/enums";
|
||||
import {
|
||||
UriMatchStrategySetting,
|
||||
UriMatchStrategy,
|
||||
} from "@bitwarden/common/models/domain/domain-service";
|
||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
|
||||
import { TotpService } from "@bitwarden/common/vault/abstractions/totp.service";
|
||||
import { FieldType, UriMatchType, CipherType } from "@bitwarden/common/vault/enums";
|
||||
import { FieldType, CipherType } from "@bitwarden/common/vault/enums";
|
||||
import { CipherRepromptType } from "@bitwarden/common/vault/enums/cipher-reprompt-type";
|
||||
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
|
||||
import { FieldView } from "@bitwarden/common/vault/models/view/field.view";
|
||||
@@ -48,7 +52,7 @@ export default class AutofillService implements AutofillServiceInterface {
|
||||
private totpService: TotpService,
|
||||
private eventCollectionService: EventCollectionService,
|
||||
private logService: LogService,
|
||||
private settingsService: SettingsService,
|
||||
private domainSettingsService: DomainSettingsService,
|
||||
private userVerificationService: UserVerificationService,
|
||||
) {}
|
||||
|
||||
@@ -215,6 +219,13 @@ export default class AutofillService implements AutofillServiceInterface {
|
||||
return await firstValueFrom(this.autofillSettingsService.autofillOnPageLoad$);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the default URI match strategy setting from the domain settings service.
|
||||
*/
|
||||
async getDefaultUriMatchStrategy(): Promise<UriMatchStrategySetting> {
|
||||
return await firstValueFrom(this.domainSettingsService.defaultUriMatchStrategy$);
|
||||
}
|
||||
|
||||
/**
|
||||
* Autofill a given tab with a given login item
|
||||
* @param {AutoFillOptions} options Instructions about the autofill operation, including tab and login item
|
||||
@@ -229,7 +240,7 @@ export default class AutofillService implements AutofillServiceInterface {
|
||||
let totp: string | null = null;
|
||||
|
||||
const canAccessPremium = await this.stateService.getCanAccessPremium();
|
||||
const defaultUriMatch = (await this.stateService.getDefaultUriMatch()) ?? UriMatchType.Domain;
|
||||
const defaultUriMatch = await this.getDefaultUriMatchStrategy();
|
||||
|
||||
if (!canAccessPremium) {
|
||||
options.cipher.login.totp = null;
|
||||
@@ -579,9 +590,9 @@ export default class AutofillService implements AutofillServiceInterface {
|
||||
let totp: AutofillField = null;
|
||||
const login = options.cipher.login;
|
||||
fillScript.savedUrls =
|
||||
login?.uris?.filter((u) => u.match != UriMatchType.Never).map((u) => u.uri) ?? [];
|
||||
login?.uris?.filter((u) => u.match != UriMatchStrategy.Never).map((u) => u.uri) ?? [];
|
||||
|
||||
fillScript.untrustedIframe = this.inUntrustedIframe(pageDetails.url, options);
|
||||
fillScript.untrustedIframe = await this.inUntrustedIframe(pageDetails.url, options);
|
||||
|
||||
let passwordFields = AutofillService.loadPasswordFields(
|
||||
pageDetails,
|
||||
@@ -1066,7 +1077,10 @@ export default class AutofillService implements AutofillServiceInterface {
|
||||
* @returns {boolean} `true` if the iframe is untrusted and a warning should be shown, `false` otherwise
|
||||
* @private
|
||||
*/
|
||||
private inUntrustedIframe(pageUrl: string, options: GenerateFillScriptOptions): boolean {
|
||||
private async inUntrustedIframe(
|
||||
pageUrl: string,
|
||||
options: GenerateFillScriptOptions,
|
||||
): Promise<boolean> {
|
||||
// If the pageUrl (from the content script) matches the tabUrl (from the sender tab), we are not in an iframe
|
||||
// This also avoids a false positive if no URI is saved and the user triggers auto-fill anyway
|
||||
if (pageUrl === options.tabUrl) {
|
||||
@@ -1076,7 +1090,9 @@ export default class AutofillService implements AutofillServiceInterface {
|
||||
// Check the pageUrl against cipher URIs using the configured match detection.
|
||||
// Remember: if we are in this function, the tabUrl already matches a saved URI for the login.
|
||||
// We need to verify the pageUrl also matches.
|
||||
const equivalentDomains = this.settingsService.getEquivalentDomains(pageUrl);
|
||||
const equivalentDomains = await firstValueFrom(
|
||||
this.domainSettingsService.getUrlEquivalentDomains(pageUrl),
|
||||
);
|
||||
const matchesUri = options.cipher.login.matchesUri(
|
||||
pageUrl,
|
||||
equivalentDomains,
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import { mock } from "jest-mock-extended";
|
||||
|
||||
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
|
||||
import { UriMatchStrategy } from "@bitwarden/common/models/domain/domain-service";
|
||||
import { ThemeType } from "@bitwarden/common/platform/enums";
|
||||
import { UriMatchType, CipherType } from "@bitwarden/common/vault/enums";
|
||||
import { CipherType } from "@bitwarden/common/vault/enums";
|
||||
import { CipherRepromptType } from "@bitwarden/common/vault/enums/cipher-reprompt-type";
|
||||
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
|
||||
|
||||
@@ -112,7 +113,7 @@ function createGenerateFillScriptOptionsMock(customFields = {}): GenerateFillScr
|
||||
allowTotpAutofill: false,
|
||||
cipher: mock<CipherView>(),
|
||||
tabUrl: "https://jest-testing-website.com",
|
||||
defaultUriMatch: UriMatchType.Domain,
|
||||
defaultUriMatch: UriMatchStrategy.Domain,
|
||||
...customFields,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { Region } from "@bitwarden/common/platform/abstractions/environment.service";
|
||||
import { GlobalState } from "@bitwarden/common/platform/models/domain/global-state";
|
||||
import { VaultTimeoutAction } from "@bitwarden/common/src/enums/vault-timeout-action.enum";
|
||||
import { CipherType } from "@bitwarden/common/vault/enums";
|
||||
|
||||
@@ -32,15 +31,10 @@ export type UserSettings = {
|
||||
utcDate: string;
|
||||
version: string;
|
||||
};
|
||||
settings: {
|
||||
equivalentDomains: string[][];
|
||||
};
|
||||
vaultTimeout: number;
|
||||
vaultTimeoutAction: VaultTimeoutAction;
|
||||
};
|
||||
|
||||
export type GlobalSettings = Pick<GlobalState, "neverDomains">;
|
||||
|
||||
/**
|
||||
* A HTMLElement (usually a form element) with additional custom properties added by this script
|
||||
*/
|
||||
|
||||
@@ -56,6 +56,10 @@ import {
|
||||
BadgeSettingsServiceAbstraction,
|
||||
BadgeSettingsService,
|
||||
} from "@bitwarden/common/autofill/services/badge-settings.service";
|
||||
import {
|
||||
DomainSettingsService,
|
||||
DefaultDomainSettingsService,
|
||||
} from "@bitwarden/common/autofill/services/domain-settings.service";
|
||||
import {
|
||||
UserNotificationSettingsService,
|
||||
UserNotificationSettingsServiceAbstraction,
|
||||
@@ -259,6 +263,7 @@ export default class MainBackground {
|
||||
userNotificationSettingsService: UserNotificationSettingsServiceAbstraction;
|
||||
autofillSettingsService: AutofillSettingsServiceAbstraction;
|
||||
badgeSettingsService: BadgeSettingsServiceAbstraction;
|
||||
domainSettingsService: DomainSettingsService;
|
||||
systemService: SystemServiceAbstraction;
|
||||
eventCollectionService: EventCollectionServiceAbstraction;
|
||||
eventUploadService: EventUploadServiceAbstraction;
|
||||
@@ -466,6 +471,7 @@ export default class MainBackground {
|
||||
this.appIdService,
|
||||
(expired: boolean) => this.logout(expired),
|
||||
);
|
||||
this.domainSettingsService = new DefaultDomainSettingsService(this.stateProvider);
|
||||
this.settingsService = new BrowserSettingsService(this.stateService);
|
||||
this.fileUploadService = new FileUploadService(this.logService);
|
||||
this.cipherFileUploadService = new CipherFileUploadService(
|
||||
@@ -591,7 +597,7 @@ export default class MainBackground {
|
||||
|
||||
this.cipherService = new CipherService(
|
||||
this.cryptoService,
|
||||
this.settingsService,
|
||||
this.domainSettingsService,
|
||||
this.apiService,
|
||||
this.i18nService,
|
||||
this.searchService,
|
||||
@@ -678,7 +684,7 @@ export default class MainBackground {
|
||||
this.providerService = new ProviderService(this.stateProvider);
|
||||
this.syncService = new SyncService(
|
||||
this.apiService,
|
||||
this.settingsService,
|
||||
this.domainSettingsService,
|
||||
this.folderService,
|
||||
this.cipherService,
|
||||
this.cryptoService,
|
||||
@@ -715,7 +721,7 @@ export default class MainBackground {
|
||||
this.totpService,
|
||||
this.eventCollectionService,
|
||||
this.logService,
|
||||
this.settingsService,
|
||||
this.domainSettingsService,
|
||||
this.userVerificationService,
|
||||
);
|
||||
this.auditService = new AuditService(this.cryptoFunctionService, this.apiService);
|
||||
@@ -779,6 +785,7 @@ export default class MainBackground {
|
||||
this.authService,
|
||||
this.stateService,
|
||||
this.vaultSettingsService,
|
||||
this.domainSettingsService,
|
||||
this.logService,
|
||||
);
|
||||
|
||||
@@ -848,6 +855,7 @@ export default class MainBackground {
|
||||
this.folderService,
|
||||
this.stateService,
|
||||
this.userNotificationSettingsService,
|
||||
this.domainSettingsService,
|
||||
this.environmentService,
|
||||
this.logService,
|
||||
);
|
||||
@@ -1081,7 +1089,6 @@ export default class MainBackground {
|
||||
await Promise.all([
|
||||
this.syncService.setLastSync(new Date(0), userId),
|
||||
this.cryptoService.clearKeys(userId),
|
||||
this.settingsService.clear(userId),
|
||||
this.cipherService.clear(userId),
|
||||
this.folderService.clear(userId),
|
||||
this.collectionService.clear(userId),
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import { Component, NgZone, OnDestroy, OnInit } from "@angular/core";
|
||||
import { Router } from "@angular/router";
|
||||
import { firstValueFrom } from "rxjs";
|
||||
|
||||
import { DomainSettingsService } from "@bitwarden/common/autofill/services/domain-settings.service";
|
||||
import { BroadcasterService } from "@bitwarden/common/platform/abstractions/broadcaster.service";
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||
@@ -30,6 +32,7 @@ export class ExcludedDomainsComponent implements OnInit, OnDestroy {
|
||||
|
||||
constructor(
|
||||
private stateService: StateService,
|
||||
private domainSettingsService: DomainSettingsService,
|
||||
private i18nService: I18nService,
|
||||
private router: Router,
|
||||
private broadcasterService: BroadcasterService,
|
||||
@@ -40,7 +43,7 @@ export class ExcludedDomainsComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
|
||||
async ngOnInit() {
|
||||
const savedDomains = await this.stateService.getNeverDomains();
|
||||
const savedDomains = await firstValueFrom(this.domainSettingsService.neverDomains$);
|
||||
if (savedDomains) {
|
||||
for (const uri of Object.keys(savedDomains)) {
|
||||
this.excludedDomains.push({ uri: uri, showCurrentUris: false });
|
||||
@@ -107,7 +110,7 @@ export class ExcludedDomainsComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
}
|
||||
|
||||
await this.stateService.setNeverDomains(savedDomains);
|
||||
await this.domainSettingsService.setNeverDomains(savedDomains);
|
||||
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
this.router.navigate(["/tabs/settings"]);
|
||||
|
||||
@@ -5,14 +5,18 @@ import { AbstractThemingService } from "@bitwarden/angular/platform/services/the
|
||||
import { SettingsService } from "@bitwarden/common/abstractions/settings.service";
|
||||
import { AutofillSettingsServiceAbstraction } from "@bitwarden/common/autofill/services/autofill-settings.service";
|
||||
import { BadgeSettingsServiceAbstraction } from "@bitwarden/common/autofill/services/badge-settings.service";
|
||||
import { DomainSettingsService } from "@bitwarden/common/autofill/services/domain-settings.service";
|
||||
import { UserNotificationSettingsServiceAbstraction } from "@bitwarden/common/autofill/services/user-notification-settings.service";
|
||||
import { ClearClipboardDelaySetting } from "@bitwarden/common/autofill/types";
|
||||
import {
|
||||
UriMatchStrategy,
|
||||
UriMatchStrategySetting,
|
||||
} from "@bitwarden/common/models/domain/domain-service";
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
|
||||
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
|
||||
import { ThemeType } from "@bitwarden/common/platform/enums";
|
||||
import { VaultSettingsService } from "@bitwarden/common/vault/abstractions/vault-settings/vault-settings.service";
|
||||
import { UriMatchType } from "@bitwarden/common/vault/enums";
|
||||
|
||||
import { enableAccountSwitching } from "../../platform/flags";
|
||||
|
||||
@@ -36,7 +40,7 @@ export class OptionsComponent implements OnInit {
|
||||
showClearClipboard = true;
|
||||
theme: ThemeType;
|
||||
themeOptions: any[];
|
||||
defaultUriMatch = UriMatchType.Domain;
|
||||
defaultUriMatch: UriMatchStrategySetting = UriMatchStrategy.Domain;
|
||||
uriMatchOptions: any[];
|
||||
clearClipboard: ClearClipboardDelaySetting;
|
||||
clearClipboardOptions: any[];
|
||||
@@ -50,6 +54,7 @@ export class OptionsComponent implements OnInit {
|
||||
private stateService: StateService,
|
||||
private userNotificationSettingsService: UserNotificationSettingsServiceAbstraction,
|
||||
private autofillSettingsService: AutofillSettingsServiceAbstraction,
|
||||
private domainSettingsService: DomainSettingsService,
|
||||
private badgeSettingsService: BadgeSettingsServiceAbstraction,
|
||||
i18nService: I18nService,
|
||||
private themingService: AbstractThemingService,
|
||||
@@ -64,12 +69,12 @@ export class OptionsComponent implements OnInit {
|
||||
{ name: i18nService.t("solarizedDark"), value: ThemeType.SolarizedDark },
|
||||
];
|
||||
this.uriMatchOptions = [
|
||||
{ name: i18nService.t("baseDomain"), value: UriMatchType.Domain },
|
||||
{ name: i18nService.t("host"), value: UriMatchType.Host },
|
||||
{ name: i18nService.t("startsWith"), value: UriMatchType.StartsWith },
|
||||
{ name: i18nService.t("regEx"), value: UriMatchType.RegularExpression },
|
||||
{ name: i18nService.t("exact"), value: UriMatchType.Exact },
|
||||
{ name: i18nService.t("never"), value: UriMatchType.Never },
|
||||
{ name: i18nService.t("baseDomain"), value: UriMatchStrategy.Domain },
|
||||
{ name: i18nService.t("host"), value: UriMatchStrategy.Host },
|
||||
{ name: i18nService.t("startsWith"), value: UriMatchStrategy.StartsWith },
|
||||
{ name: i18nService.t("regEx"), value: UriMatchStrategy.RegularExpression },
|
||||
{ name: i18nService.t("exact"), value: UriMatchStrategy.Exact },
|
||||
{ name: i18nService.t("never"), value: UriMatchStrategy.Never },
|
||||
];
|
||||
this.clearClipboardOptions = [
|
||||
{ name: i18nService.t("never"), value: null },
|
||||
@@ -122,8 +127,10 @@ export class OptionsComponent implements OnInit {
|
||||
|
||||
this.theme = await this.stateService.getTheme();
|
||||
|
||||
const defaultUriMatch = await this.stateService.getDefaultUriMatch();
|
||||
this.defaultUriMatch = defaultUriMatch == null ? UriMatchType.Domain : defaultUriMatch;
|
||||
const defaultUriMatch = await firstValueFrom(
|
||||
this.domainSettingsService.defaultUriMatchStrategy$,
|
||||
);
|
||||
this.defaultUriMatch = defaultUriMatch == null ? UriMatchStrategy.Domain : defaultUriMatch;
|
||||
|
||||
this.clearClipboard = await firstValueFrom(this.autofillSettingsService.clearClipboardDelay$);
|
||||
}
|
||||
@@ -182,10 +189,6 @@ export class OptionsComponent implements OnInit {
|
||||
await this.themingService.updateConfiguredTheme(this.theme);
|
||||
}
|
||||
|
||||
async saveDefaultUriMatch() {
|
||||
await this.stateService.setDefaultUriMatch(this.defaultUriMatch);
|
||||
}
|
||||
|
||||
async saveClearClipboard() {
|
||||
await this.autofillSettingsService.setClearClipboardDelay(this.clearClipboard);
|
||||
}
|
||||
|
||||
@@ -1,15 +1,11 @@
|
||||
import { BehaviorSubject } from "rxjs";
|
||||
|
||||
import { AccountSettingsSettings } from "@bitwarden/common/platform/models/domain/account";
|
||||
import { SettingsService } from "@bitwarden/common/services/settings.service";
|
||||
|
||||
import { browserSession, sessionSync } from "../platform/decorators/session-sync-observable";
|
||||
|
||||
@browserSession
|
||||
export class BrowserSettingsService extends SettingsService {
|
||||
@sessionSync({ initializer: (obj: string[][]) => obj })
|
||||
protected _settings: BehaviorSubject<AccountSettingsSettings>;
|
||||
|
||||
@sessionSync({ initializer: (b: boolean) => b })
|
||||
protected _disableFavicon: BehaviorSubject<boolean>;
|
||||
}
|
||||
|
||||
@@ -5,6 +5,10 @@ import {
|
||||
AutofillSettingsServiceInitOptions,
|
||||
autofillSettingsServiceFactory,
|
||||
} from "../../../autofill/background/service_factories/autofill-settings-service.factory";
|
||||
import {
|
||||
DomainSettingsServiceInitOptions,
|
||||
domainSettingsServiceFactory,
|
||||
} from "../../../autofill/background/service_factories/domain-settings-service.factory";
|
||||
import {
|
||||
CipherFileUploadServiceInitOptions,
|
||||
cipherFileUploadServiceFactory,
|
||||
@@ -13,10 +17,6 @@ import {
|
||||
searchServiceFactory,
|
||||
SearchServiceInitOptions,
|
||||
} from "../../../background/service-factories/search-service.factory";
|
||||
import {
|
||||
SettingsServiceInitOptions,
|
||||
settingsServiceFactory,
|
||||
} from "../../../background/service-factories/settings-service.factory";
|
||||
import {
|
||||
apiServiceFactory,
|
||||
ApiServiceInitOptions,
|
||||
@@ -51,13 +51,13 @@ type CipherServiceFactoryOptions = FactoryOptions;
|
||||
|
||||
export type CipherServiceInitOptions = CipherServiceFactoryOptions &
|
||||
CryptoServiceInitOptions &
|
||||
SettingsServiceInitOptions &
|
||||
ApiServiceInitOptions &
|
||||
CipherFileUploadServiceInitOptions &
|
||||
I18nServiceInitOptions &
|
||||
SearchServiceInitOptions &
|
||||
StateServiceInitOptions &
|
||||
AutofillSettingsServiceInitOptions &
|
||||
DomainSettingsServiceInitOptions &
|
||||
EncryptServiceInitOptions &
|
||||
ConfigServiceInitOptions;
|
||||
|
||||
@@ -72,7 +72,7 @@ export function cipherServiceFactory(
|
||||
async () =>
|
||||
new CipherService(
|
||||
await cryptoServiceFactory(cache, opts),
|
||||
await settingsServiceFactory(cache, opts),
|
||||
await domainSettingsServiceFactory(cache, opts),
|
||||
await apiServiceFactory(cache, opts),
|
||||
await i18nServiceFactory(cache, opts),
|
||||
await searchServiceFactory(cache, opts),
|
||||
|
||||
@@ -3,6 +3,7 @@ import { ConnectedPosition } from "@angular/cdk/overlay";
|
||||
import { Component } from "@angular/core";
|
||||
import { firstValueFrom } from "rxjs";
|
||||
|
||||
import { DomainSettingsService } from "@bitwarden/common/autofill/services/domain-settings.service";
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
|
||||
@@ -52,6 +53,7 @@ export class Fido2UseBrowserLinkComponent {
|
||||
|
||||
constructor(
|
||||
private stateService: StateService,
|
||||
private domainSettingsService: DomainSettingsService,
|
||||
private platformUtilsService: PlatformUtilsService,
|
||||
private i18nService: I18nService,
|
||||
) {}
|
||||
@@ -89,7 +91,7 @@ export class Fido2UseBrowserLinkComponent {
|
||||
* @param uri - The domain uri to exclude from future FIDO2 prompts.
|
||||
*/
|
||||
private async handleDomainExclusion(uri: string) {
|
||||
const exisitingDomains = await this.stateService.getNeverDomains();
|
||||
const exisitingDomains = await firstValueFrom(this.domainSettingsService.neverDomains$);
|
||||
|
||||
const validDomain = Utils.getHostname(uri);
|
||||
const savedDomains: { [name: string]: unknown } = {
|
||||
@@ -97,9 +99,7 @@ export class Fido2UseBrowserLinkComponent {
|
||||
};
|
||||
savedDomains[validDomain] = null;
|
||||
|
||||
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
this.stateService.setNeverDomains(savedDomains);
|
||||
await this.domainSettingsService.setNeverDomains(savedDomains);
|
||||
|
||||
this.platformUtilsService.showToast(
|
||||
"success",
|
||||
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
combineLatest,
|
||||
concatMap,
|
||||
filter,
|
||||
firstValueFrom,
|
||||
map,
|
||||
Observable,
|
||||
Subject,
|
||||
@@ -13,7 +14,7 @@ import {
|
||||
} from "rxjs";
|
||||
|
||||
import { SearchService } from "@bitwarden/common/abstractions/search.service";
|
||||
import { SettingsService } from "@bitwarden/common/abstractions/settings.service";
|
||||
import { DomainSettingsService } from "@bitwarden/common/autofill/services/domain-settings.service";
|
||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
|
||||
@@ -72,7 +73,7 @@ export class Fido2Component implements OnInit, OnDestroy {
|
||||
private cipherService: CipherService,
|
||||
private passwordRepromptService: PasswordRepromptService,
|
||||
private platformUtilsService: PlatformUtilsService,
|
||||
private settingsService: SettingsService,
|
||||
private domainSettingsService: DomainSettingsService,
|
||||
private searchService: SearchService,
|
||||
private logService: LogService,
|
||||
private dialogService: DialogService,
|
||||
@@ -133,7 +134,9 @@ export class Fido2Component implements OnInit, OnDestroy {
|
||||
concatMap(async (message) => {
|
||||
switch (message.type) {
|
||||
case "ConfirmNewCredentialRequest": {
|
||||
const equivalentDomains = this.settingsService.getEquivalentDomains(this.url);
|
||||
const equivalentDomains = await firstValueFrom(
|
||||
this.domainSettingsService.getUrlEquivalentDomains(this.url),
|
||||
);
|
||||
|
||||
this.ciphers = (await this.cipherService.getAllDecrypted()).filter(
|
||||
(cipher) => cipher.type === CipherType.Login && !cipher.isDeleted,
|
||||
@@ -317,7 +320,9 @@ export class Fido2Component implements OnInit, OnDestroy {
|
||||
this.ciphers,
|
||||
);
|
||||
} else {
|
||||
const equivalentDomains = this.settingsService.getEquivalentDomains(this.url);
|
||||
const equivalentDomains = await firstValueFrom(
|
||||
this.domainSettingsService.getUrlEquivalentDomains(this.url),
|
||||
);
|
||||
this.displayedCiphers = this.ciphers.filter((cipher) =>
|
||||
cipher.login.matchesUri(this.url, equivalentDomains),
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user