1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-20 18:23: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:
Jonathan Prusik
2024-03-12 15:07:14 -04:00
committed by GitHub
parent a0e0637bb6
commit 0a595ea95e
58 changed files with 945 additions and 455 deletions

View File

@@ -1,26 +1,26 @@
import { UriMatchStrategy, UriMatchStrategySetting } from "../../../models/domain/domain-service";
import { Utils } from "../../../platform/misc/utils";
import { UriMatchType } from "../../enums";
import { LoginUriView } from "./login-uri.view";
const testData = [
{
match: UriMatchType.Host,
match: UriMatchStrategy.Host,
uri: "http://example.com/login",
expected: "http://example.com/login",
},
{
match: UriMatchType.Host,
match: UriMatchStrategy.Host,
uri: "bitwarden.com",
expected: "http://bitwarden.com",
},
{
match: UriMatchType.Host,
match: UriMatchStrategy.Host,
uri: "bitwarden.de",
expected: "http://bitwarden.de",
},
{
match: UriMatchType.Host,
match: UriMatchStrategy.Host,
uri: "bitwarden.br",
expected: "http://bitwarden.br",
},
@@ -41,7 +41,7 @@ const exampleUris = {
describe("LoginUriView", () => {
it("isWebsite() given an invalid domain should return false", async () => {
const uri = new LoginUriView();
Object.assign(uri, { match: UriMatchType.Host, uri: "bit!:_&ward.com" });
Object.assign(uri, { match: UriMatchStrategy.Host, uri: "bit!:_&ward.com" });
expect(uri.isWebsite).toBe(false);
});
@@ -67,32 +67,32 @@ describe("LoginUriView", () => {
it(`canLaunch should return false when MatchDetection is set to Regex`, async () => {
const uri = new LoginUriView();
Object.assign(uri, { match: UriMatchType.RegularExpression, uri: "bitwarden.com" });
Object.assign(uri, { match: UriMatchStrategy.RegularExpression, uri: "bitwarden.com" });
expect(uri.canLaunch).toBe(false);
});
it(`canLaunch() should return false when the given protocol does not match CanLaunchWhiteList`, async () => {
const uri = new LoginUriView();
Object.assign(uri, { match: UriMatchType.Host, uri: "someprotocol://bitwarden.com" });
Object.assign(uri, { match: UriMatchStrategy.Host, uri: "someprotocol://bitwarden.com" });
expect(uri.canLaunch).toBe(false);
});
describe("uri matching", () => {
describe("using domain matching", () => {
it("matches the same domain", () => {
const uri = uriFactory(UriMatchType.Domain, exampleUris.standard);
const uri = uriFactory(UriMatchStrategy.Domain, exampleUris.standard);
const actual = uri.matchesUri(exampleUris.subdomain, exampleUris.noEquivalentDomains());
expect(actual).toBe(true);
});
it("matches equivalent domains", () => {
const uri = uriFactory(UriMatchType.Domain, exampleUris.standard);
const uri = uriFactory(UriMatchStrategy.Domain, exampleUris.standard);
const actual = uri.matchesUri(exampleUris.differentDomain, exampleUris.equivalentDomains());
expect(actual).toBe(true);
});
it("does not match a different domain", () => {
const uri = uriFactory(UriMatchType.Domain, exampleUris.standard);
const uri = uriFactory(UriMatchStrategy.Domain, exampleUris.standard);
const actual = uri.matchesUri(
exampleUris.differentDomain,
exampleUris.noEquivalentDomains(),
@@ -103,7 +103,7 @@ describe("LoginUriView", () => {
// Actual integration test with the real blacklist, not ideal
it("does not match domains that are blacklisted", () => {
const googleEquivalentDomains = new Set(["google.com", "script.google.com"]);
const uri = uriFactory(UriMatchType.Domain, "google.com");
const uri = uriFactory(UriMatchStrategy.Domain, "google.com");
const actual = uri.matchesUri("script.google.com", googleEquivalentDomains);
@@ -113,13 +113,13 @@ describe("LoginUriView", () => {
describe("using host matching", () => {
it("matches the same host", () => {
const uri = uriFactory(UriMatchType.Host, Utils.getHost(exampleUris.standard));
const uri = uriFactory(UriMatchStrategy.Host, Utils.getHost(exampleUris.standard));
const actual = uri.matchesUri(exampleUris.standard, exampleUris.noEquivalentDomains());
expect(actual).toBe(true);
});
it("does not match a different host", () => {
const uri = uriFactory(UriMatchType.Host, Utils.getHost(exampleUris.differentDomain));
const uri = uriFactory(UriMatchStrategy.Host, Utils.getHost(exampleUris.differentDomain));
const actual = uri.matchesUri(exampleUris.standard, exampleUris.noEquivalentDomains());
expect(actual).toBe(false);
});
@@ -127,13 +127,13 @@ describe("LoginUriView", () => {
describe("using exact matching", () => {
it("matches if both uris are the same", () => {
const uri = uriFactory(UriMatchType.Exact, exampleUris.standard);
const uri = uriFactory(UriMatchStrategy.Exact, exampleUris.standard);
const actual = uri.matchesUri(exampleUris.standard, exampleUris.noEquivalentDomains());
expect(actual).toBe(true);
});
it("does not match if the uris are different", () => {
const uri = uriFactory(UriMatchType.Exact, exampleUris.standard);
const uri = uriFactory(UriMatchStrategy.Exact, exampleUris.standard);
const actual = uri.matchesUri(
exampleUris.standard + "#",
exampleUris.noEquivalentDomains(),
@@ -144,7 +144,7 @@ describe("LoginUriView", () => {
describe("using startsWith matching", () => {
it("matches if the target URI starts with the saved URI", () => {
const uri = uriFactory(UriMatchType.StartsWith, exampleUris.standard);
const uri = uriFactory(UriMatchStrategy.StartsWith, exampleUris.standard);
const actual = uri.matchesUri(
exampleUris.standard + "#bookmark",
exampleUris.noEquivalentDomains(),
@@ -153,7 +153,7 @@ describe("LoginUriView", () => {
});
it("does not match if the start of the uri is not the same", () => {
const uri = uriFactory(UriMatchType.StartsWith, exampleUris.standard);
const uri = uriFactory(UriMatchStrategy.StartsWith, exampleUris.standard);
const actual = uri.matchesUri(
exampleUris.standard.slice(1),
exampleUris.noEquivalentDomains(),
@@ -164,13 +164,13 @@ describe("LoginUriView", () => {
describe("using regular expression matching", () => {
it("matches if the regular expression matches", () => {
const uri = uriFactory(UriMatchType.RegularExpression, exampleUris.standard);
const uri = uriFactory(UriMatchStrategy.RegularExpression, exampleUris.standard);
const actual = uri.matchesUri(exampleUris.standardRegex, exampleUris.noEquivalentDomains());
expect(actual).toBe(false);
});
it("does not match if the regular expression does not match", () => {
const uri = uriFactory(UriMatchType.RegularExpression, exampleUris.standardNotMatching);
const uri = uriFactory(UriMatchStrategy.RegularExpression, exampleUris.standardNotMatching);
const actual = uri.matchesUri(exampleUris.standardRegex, exampleUris.noEquivalentDomains());
expect(actual).toBe(false);
});
@@ -178,7 +178,7 @@ describe("LoginUriView", () => {
describe("using never matching", () => {
it("does not match even if uris are identical", () => {
const uri = uriFactory(UriMatchType.Never, exampleUris.standard);
const uri = uriFactory(UriMatchStrategy.Never, exampleUris.standard);
const actual = uri.matchesUri(exampleUris.standard, exampleUris.noEquivalentDomains());
expect(actual).toBe(false);
});
@@ -186,7 +186,7 @@ describe("LoginUriView", () => {
});
});
function uriFactory(match: UriMatchType, uri: string) {
function uriFactory(match: UriMatchStrategySetting, uri: string) {
const loginUri = new LoginUriView();
loginUri.match = match;
loginUri.uri = uri;

View File

@@ -1,13 +1,13 @@
import { Jsonify } from "type-fest";
import { UriMatchStrategy, UriMatchStrategySetting } from "../../../models/domain/domain-service";
import { View } from "../../../models/view/view";
import { SafeUrls } from "../../../platform/misc/safe-urls";
import { Utils } from "../../../platform/misc/utils";
import { UriMatchType } from "../../enums";
import { LoginUri } from "../domain/login-uri";
export class LoginUriView implements View {
match: UriMatchType = null;
match: UriMatchStrategySetting = null;
private _uri: string = null;
private _domain: string = null;
@@ -44,7 +44,7 @@ export class LoginUriView implements View {
}
get hostname(): string {
if (this.match === UriMatchType.RegularExpression) {
if (this.match === UriMatchStrategy.RegularExpression) {
return null;
}
if (this._hostname == null && this.uri != null) {
@@ -58,7 +58,7 @@ export class LoginUriView implements View {
}
get host(): string {
if (this.match === UriMatchType.RegularExpression) {
if (this.match === UriMatchStrategy.RegularExpression) {
return null;
}
if (this._host == null && this.uri != null) {
@@ -92,7 +92,7 @@ export class LoginUriView implements View {
if (this._canLaunch != null) {
return this._canLaunch;
}
if (this.uri != null && this.match !== UriMatchType.RegularExpression) {
if (this.uri != null && this.match !== UriMatchStrategy.RegularExpression) {
this._canLaunch = SafeUrls.canLaunch(this.launchUri);
} else {
this._canLaunch = false;
@@ -113,30 +113,30 @@ export class LoginUriView implements View {
matchesUri(
targetUri: string,
equivalentDomains: Set<string>,
defaultUriMatch: UriMatchType = null,
defaultUriMatch: UriMatchStrategySetting = null,
): boolean {
if (!this.uri || !targetUri) {
return false;
}
let matchType = this.match ?? defaultUriMatch;
matchType ??= UriMatchType.Domain;
matchType ??= UriMatchStrategy.Domain;
const targetDomain = Utils.getDomain(targetUri);
const matchDomains = equivalentDomains.add(targetDomain);
switch (matchType) {
case UriMatchType.Domain:
case UriMatchStrategy.Domain:
return this.matchesDomain(targetUri, matchDomains);
case UriMatchType.Host: {
case UriMatchStrategy.Host: {
const urlHost = Utils.getHost(targetUri);
return urlHost != null && urlHost === Utils.getHost(this.uri);
}
case UriMatchType.Exact:
case UriMatchStrategy.Exact:
return targetUri === this.uri;
case UriMatchType.StartsWith:
case UriMatchStrategy.StartsWith:
return targetUri.startsWith(this.uri);
case UriMatchType.RegularExpression:
case UriMatchStrategy.RegularExpression:
try {
const regex = new RegExp(this.uri, "i");
return regex.test(targetUri);
@@ -144,7 +144,7 @@ export class LoginUriView implements View {
// Invalid regex
return false;
}
case UriMatchType.Never:
case UriMatchStrategy.Never:
return false;
default:
break;

View File

@@ -1,6 +1,7 @@
import { UriMatchStrategySetting } from "../../../models/domain/domain-service";
import { Utils } from "../../../platform/misc/utils";
import { DeepJsonify } from "../../../types/deep-jsonify";
import { LoginLinkedId as LinkedId, UriMatchType } from "../../enums";
import { LoginLinkedId as LinkedId } from "../../enums";
import { linkedFieldOption } from "../../linked-field-option.decorator";
import { Login } from "../domain/login";
@@ -71,7 +72,7 @@ export class LoginView extends ItemView {
matchesUri(
targetUri: string,
equivalentDomains: Set<string>,
defaultUriMatch: UriMatchType = null,
defaultUriMatch: UriMatchStrategySetting = null,
): boolean {
if (this.uris == null) {
return false;