mirror of
https://github.com/bitwarden/browser
synced 2026-02-11 22:13:32 +00:00
consistency updates
This commit is contained in:
@@ -7,7 +7,7 @@ export const AddyIo = {
|
||||
name: "Addy.io",
|
||||
};
|
||||
|
||||
export const AddyIoIntegrations: ExtensionMetadata[] = [
|
||||
export const AddyIoExtensions: ExtensionMetadata[] = [
|
||||
{
|
||||
site: Extension.forwarder,
|
||||
product: {
|
||||
|
||||
@@ -7,7 +7,7 @@ export const DuckDuckGo = {
|
||||
name: "DuckDuckGo",
|
||||
};
|
||||
|
||||
export const DuckDuckGoIntegrations: ExtensionMetadata[] = [
|
||||
export const DuckDuckGoExtensions: ExtensionMetadata[] = [
|
||||
{
|
||||
site: Extension.forwarder,
|
||||
product: {
|
||||
|
||||
@@ -7,8 +7,7 @@ export const Fastmail = {
|
||||
name: "Fastmail",
|
||||
};
|
||||
|
||||
// integration-wide configuration
|
||||
export const FastmailIntegrations: ExtensionMetadata[] = [
|
||||
export const FastmailExtensions: ExtensionMetadata[] = [
|
||||
{
|
||||
site: Extension.forwarder,
|
||||
product: {
|
||||
|
||||
@@ -7,7 +7,7 @@ export const ForwardEmail = {
|
||||
name: "Forward Email",
|
||||
};
|
||||
|
||||
export const ForwardEmailIntegrations: ExtensionMetadata[] = [
|
||||
export const ForwardEmailExtensions: ExtensionMetadata[] = [
|
||||
{
|
||||
site: Extension.forwarder,
|
||||
product: {
|
||||
|
||||
@@ -7,7 +7,7 @@ export const Mozilla = {
|
||||
name: "Mozilla",
|
||||
};
|
||||
|
||||
export const MozillaIntegrations: ExtensionMetadata[] = [
|
||||
export const MozillaExtensions: ExtensionMetadata[] = [
|
||||
{
|
||||
site: Extension.forwarder,
|
||||
product: {
|
||||
|
||||
@@ -7,7 +7,7 @@ export const SimpleLogin: VendorMetadata = {
|
||||
name: "SimpleLogin",
|
||||
};
|
||||
|
||||
export const SimpleLoginIntegrations: ExtensionMetadata[] = [
|
||||
export const SimpleLoginExtensions: ExtensionMetadata[] = [
|
||||
{
|
||||
site: Extension.forwarder,
|
||||
product: {
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { ExtensionSite } from "../extension-site";
|
||||
|
||||
import { ExtensionPermission, ExtensionRegistry } from "./extension-registry.abstraction";
|
||||
import { ExtensionSite } from "./extension-site";
|
||||
import {
|
||||
SiteMetadata,
|
||||
SiteId,
|
||||
@@ -8,21 +7,20 @@ import {
|
||||
ExtensionSet,
|
||||
VendorId,
|
||||
VendorMetadata,
|
||||
} from "./type";
|
||||
} from "./metadata/type";
|
||||
|
||||
/** Tracks extension points and the integrations that extend them. */
|
||||
export class DefaultIntegrationRegistry implements ExtensionRegistry {
|
||||
/** Tracks extension sites and the vendors that extend them. */
|
||||
export class DefaultExtensionRegistry implements ExtensionRegistry {
|
||||
private allRule: ExtensionPermission = "default";
|
||||
|
||||
private siteRegistrations = new Map<SiteId, SiteMetadata>();
|
||||
private siteRules = new Map<SiteId, ExtensionPermission>();
|
||||
private sitePermissions = new Map<SiteId, ExtensionPermission>();
|
||||
|
||||
private vendorRegistrations = new Map<VendorId, VendorMetadata>();
|
||||
private vendorRules = new Map<VendorId, ExtensionPermission>();
|
||||
private vendorPermissions = new Map<VendorId, ExtensionPermission>();
|
||||
|
||||
private integrations = new Array<ExtensionMetadata>();
|
||||
private siteIntegrationsByVendor = new Map<VendorId, Map<SiteId, number>>();
|
||||
private vendorIntegrationsBySite = new Map<SiteId, Map<VendorId, number>>();
|
||||
private extensions = new Array<ExtensionMetadata>();
|
||||
private vendorExtensionsBySite = new Map<SiteId, Map<VendorId, number>>();
|
||||
|
||||
registerSite(meta: SiteMetadata): this {
|
||||
if (!this.siteRegistrations.has(meta.id)) {
|
||||
@@ -37,7 +35,7 @@ export class DefaultIntegrationRegistry implements ExtensionRegistry {
|
||||
|
||||
for (const [k, site] of this.siteRegistrations.entries()) {
|
||||
const s: (typeof sites)[number] = { site };
|
||||
const permission = this.siteRules.get(k);
|
||||
const permission = this.sitePermissions.get(k);
|
||||
if (permission) {
|
||||
s.permission = permission;
|
||||
}
|
||||
@@ -61,7 +59,7 @@ export class DefaultIntegrationRegistry implements ExtensionRegistry {
|
||||
|
||||
for (const [k, vendor] of this.vendorRegistrations.entries()) {
|
||||
const s: (typeof vendors)[number] = { vendor };
|
||||
const permission = this.vendorRules.get(k);
|
||||
const permission = this.vendorPermissions.get(k);
|
||||
if (permission) {
|
||||
s.permission = permission;
|
||||
}
|
||||
@@ -76,11 +74,11 @@ export class DefaultIntegrationRegistry implements ExtensionRegistry {
|
||||
if ("all" in set && set.all) {
|
||||
this.allRule = permission;
|
||||
} else if ("vendor" in set) {
|
||||
this.vendorRules.set(set.vendor, permission);
|
||||
this.vendorPermissions.set(set.vendor, permission);
|
||||
} else if ("site" in set) {
|
||||
this.siteRules.set(set.site, permission);
|
||||
this.sitePermissions.set(set.site, permission);
|
||||
} else {
|
||||
throw new Error(`Unrecognized integration set received: ${JSON.stringify(set)}.`);
|
||||
throw new Error(`Unrecognized extension set received: ${JSON.stringify(set)}.`);
|
||||
}
|
||||
|
||||
return this;
|
||||
@@ -90,9 +88,9 @@ export class DefaultIntegrationRegistry implements ExtensionRegistry {
|
||||
if ("all" in set && set.all) {
|
||||
return this.allRule;
|
||||
} else if ("vendor" in set) {
|
||||
return this.vendorRules.get(set.vendor);
|
||||
return this.vendorPermissions.get(set.vendor);
|
||||
} else if ("site" in set) {
|
||||
return this.siteRules.get(set.site);
|
||||
return this.sitePermissions.get(set.site);
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
@@ -102,11 +100,11 @@ export class DefaultIntegrationRegistry implements ExtensionRegistry {
|
||||
const rules: { set: ExtensionSet; permission: ExtensionPermission }[] = [];
|
||||
rules.push({ set: { all: true }, permission: this.allRule });
|
||||
|
||||
for (const [site, permission] of this.siteRules.entries()) {
|
||||
for (const [site, permission] of this.sitePermissions.entries()) {
|
||||
rules.push({ set: { site }, permission });
|
||||
}
|
||||
|
||||
for (const [vendor, permission] of this.vendorRules.entries()) {
|
||||
for (const [vendor, permission] of this.vendorPermissions.entries()) {
|
||||
rules.push({ set: { vendor }, permission });
|
||||
}
|
||||
|
||||
@@ -120,19 +118,15 @@ export class DefaultIntegrationRegistry implements ExtensionRegistry {
|
||||
throw new Error(`Unrecognized vendor: ${meta.product.vendor.id}`);
|
||||
}
|
||||
|
||||
// is the integration registered?
|
||||
const vendorMap =
|
||||
this.vendorIntegrationsBySite.get(meta.site.id) ?? new Map<VendorId, number>();
|
||||
// is the extension registered?
|
||||
const vendorMap = this.vendorExtensionsBySite.get(meta.site.id) ?? new Map<VendorId, number>();
|
||||
if (vendorMap.has(meta.product.vendor.id)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// if not, register it
|
||||
const siteMap =
|
||||
this.siteIntegrationsByVendor.get(meta.product.vendor.id) ?? new Map<SiteId, number>();
|
||||
const index = this.integrations.push(meta) - 1;
|
||||
const index = this.extensions.push(meta) - 1;
|
||||
vendorMap.set(meta.product.vendor.id, index);
|
||||
siteMap.set(meta.site.id, index);
|
||||
|
||||
return this;
|
||||
}
|
||||
@@ -148,22 +142,22 @@ export class DefaultIntegrationRegistry implements ExtensionRegistry {
|
||||
}
|
||||
|
||||
const extensions = new Map<VendorId, ExtensionMetadata>();
|
||||
const entries = this.vendorIntegrationsBySite.get(id)?.entries() ?? ([] as const);
|
||||
const entries = this.vendorExtensionsBySite.get(id)?.entries() ?? ([] as const);
|
||||
for (const [vendor, maybeIndex] of entries) {
|
||||
// prepare rules
|
||||
const vendorRule = this.vendorRules.get(vendor) ?? this.allRule;
|
||||
const siteRule = this.siteRules.get(id) ?? this.allRule;
|
||||
const vendorRule = this.vendorPermissions.get(vendor) ?? this.allRule;
|
||||
const siteRule = this.sitePermissions.get(id) ?? this.allRule;
|
||||
const rules = [vendorRule, siteRule, this.allRule];
|
||||
|
||||
// evaluate rules
|
||||
const extension = rules.includes("deny")
|
||||
? undefined
|
||||
: rules.includes("allow")
|
||||
? this.integrations[maybeIndex]
|
||||
? this.extensions[maybeIndex]
|
||||
: rules.includes("none")
|
||||
? undefined
|
||||
: rules.includes("default")
|
||||
? this.integrations[maybeIndex]
|
||||
? this.extensions[maybeIndex]
|
||||
: undefined;
|
||||
|
||||
// the presence of an extension indicates it's accessible
|
||||
@@ -1,13 +1,18 @@
|
||||
import { ExtensionSite } from "../extension-site";
|
||||
|
||||
import { SiteMetadata, ExtensionMetadata, ExtensionSet, VendorMetadata, SiteId } from "./type";
|
||||
import { ExtensionSite } from "./extension-site";
|
||||
import {
|
||||
SiteMetadata,
|
||||
ExtensionMetadata,
|
||||
ExtensionSet,
|
||||
VendorMetadata,
|
||||
SiteId,
|
||||
} from "./metadata/type";
|
||||
|
||||
/** Permission levels for metadata.
|
||||
* * default - unless a rule denies access, allow it. This is the
|
||||
* default permission.
|
||||
* * none - unless a rule allows access, deny it.
|
||||
* * allow - access is explicitly granted to use an integration.
|
||||
* * deny - access is explicitly prohibited for this integration. This
|
||||
* * allow - access is explicitly granted to use an extension.
|
||||
* * deny - access is explicitly prohibited for this extension. This
|
||||
* rule overrides allow rules.
|
||||
*/
|
||||
export type ExtensionPermission = "default" | "none" | "allow" | "deny";
|
||||
@@ -20,49 +25,49 @@ export abstract class ExtensionRegistry {
|
||||
*/
|
||||
registerSite: (meta: SiteMetadata) => this;
|
||||
|
||||
/** List all registered extension sites with their integration rule, if any.
|
||||
/** List all registered extension sites with their extension rule, if any.
|
||||
* @returns a list of all extension sites. `rule` is defined when the site
|
||||
* is associated with an integration rule.
|
||||
* is associated with an extension rule.
|
||||
*/
|
||||
sites: () => { site: SiteMetadata; permission?: ExtensionPermission }[];
|
||||
|
||||
/** Registers a vendor providing an integration
|
||||
/** Registers a vendor providing an extension
|
||||
* @param site - identifies the site being extended
|
||||
* @param meta - configures the extension site
|
||||
* @return self for method chaining.
|
||||
*/
|
||||
registerVendor: (meta: VendorMetadata) => this;
|
||||
|
||||
/** List all registered vendors with their integration rule, if any.
|
||||
/** List all registered vendors with their extension rule, if any.
|
||||
* @returns a list of all extension sites. `rule` is defined when the site
|
||||
* is associated with an integration rule.
|
||||
* is associated with an extension rule.
|
||||
*/
|
||||
vendors: () => { vendor: VendorMetadata; permission?: ExtensionPermission }[];
|
||||
|
||||
/** Registers an integration provided by a vendor to an extension site.
|
||||
* The vendor and site MUST be registered before the integration.
|
||||
/** Registers an extension provided by a vendor to an extension site.
|
||||
* The vendor and site MUST be registered before the extension.
|
||||
* @param site - identifies the site being extended
|
||||
* @param meta - configures the extension site
|
||||
* @return self for method chaining.
|
||||
*/
|
||||
registerExtension: (meta: ExtensionMetadata) => this;
|
||||
|
||||
/** Registers a rule. Only 1 rule can be registered for each integration set.
|
||||
/** Registers a rule. Only 1 rule can be registered for each extension set.
|
||||
* The last-registered rule wins.
|
||||
* @param set the collection of integrations affected by the rule
|
||||
* @param set the collection of extensions affected by the rule
|
||||
* @param permission the permission for the collection
|
||||
* @return self for method chaining.
|
||||
*/
|
||||
setPermission: (set: ExtensionSet, permission: ExtensionPermission) => this;
|
||||
|
||||
/** Retrieves the current rule for the given integration set or undefined if a rule
|
||||
/** Retrieves the current rule for the given extension set or undefined if a rule
|
||||
* doesn't exist. */
|
||||
permission: (set: ExtensionSet) => ExtensionPermission | undefined;
|
||||
|
||||
/** Returns all registered integration rules. */
|
||||
/** Returns all registered extension rules. */
|
||||
permissions: () => { set: ExtensionSet; permission: ExtensionPermission }[];
|
||||
|
||||
/** Creates a point-in-time snapshot of the registry's contents with integration
|
||||
/** Creates a point-in-time snapshot of the registry's contents with extension
|
||||
* permissions applied for the provided SiteId.
|
||||
* @returns the extension site, or `undefined` if the site is not registered.
|
||||
*/
|
||||
@@ -2,11 +2,11 @@ import { StateProvider } from "@bitwarden/common/platform/state";
|
||||
|
||||
import { LegacyEncryptorProvider } from "../cryptography/legacy-encryptor-provider";
|
||||
|
||||
import { DefaultIntegrationRegistry } from "./metadata/default-extension-registry";
|
||||
import { DefaultExtensionRegistry } from "./default-extension-registry";
|
||||
|
||||
export class ExtensionService {
|
||||
constructor(
|
||||
private readonly registry: DefaultIntegrationRegistry,
|
||||
private readonly registry: DefaultExtensionRegistry,
|
||||
private readonly stateProvider: StateProvider,
|
||||
private readonly encryptorProvider: LegacyEncryptorProvider,
|
||||
) {}
|
||||
|
||||
36
libs/common/src/tools/extension/factory.ts
Normal file
36
libs/common/src/tools/extension/factory.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
import { AddyIo, AddyIoExtensions } from "./data/addy-io";
|
||||
import { DuckDuckGo, DuckDuckGoExtensions } from "./data/duck-duck-go";
|
||||
import { Fastmail, FastmailExtensions } from "./data/fastmail";
|
||||
import { ForwardEmail, ForwardEmailExtensions } from "./data/forward-email";
|
||||
import { Mozilla, MozillaExtensions } from "./data/mozilla";
|
||||
import { SimpleLogin, SimpleLoginExtensions } from "./data/simple-login";
|
||||
import { DefaultExtensionRegistry } from "./default-extension-registry";
|
||||
import { Extension } from "./metadata/extension";
|
||||
import { ExtensionMetadata, VendorMetadata } from "./metadata/type";
|
||||
|
||||
// FIXME: find a better way to build the registry than a hard-coded factory function
|
||||
|
||||
/** Constructs the extension registry */
|
||||
export function buildRegistry() {
|
||||
function registerAll(vendor: VendorMetadata, extensions: ExtensionMetadata[]) {
|
||||
registry.registerVendor(vendor);
|
||||
for (const extension of extensions) {
|
||||
registry.registerExtension(extension);
|
||||
}
|
||||
}
|
||||
|
||||
const registry = new DefaultExtensionRegistry();
|
||||
|
||||
for (const site of Reflect.ownKeys(Extension) as string[]) {
|
||||
registry.registerSite(Extension[site]);
|
||||
}
|
||||
|
||||
registerAll(AddyIo, AddyIoExtensions);
|
||||
registerAll(DuckDuckGo, DuckDuckGoExtensions);
|
||||
registerAll(Fastmail, FastmailExtensions);
|
||||
registerAll(ForwardEmail, ForwardEmailExtensions);
|
||||
registerAll(Mozilla, MozillaExtensions);
|
||||
registerAll(SimpleLogin, SimpleLoginExtensions);
|
||||
|
||||
return registry;
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
import { AddyIo, AddyIoIntegrations } from "../data/addy-io";
|
||||
import { DuckDuckGo, DuckDuckGoIntegrations } from "../data/duck-duck-go";
|
||||
import { Fastmail, FastmailIntegrations } from "../data/fastmail";
|
||||
import { ForwardEmail, ForwardEmailIntegrations } from "../data/forward-email";
|
||||
import { Mozilla, MozillaIntegrations } from "../data/mozilla";
|
||||
import { SimpleLogin, SimpleLoginIntegrations } from "../data/simple-login";
|
||||
|
||||
import { DefaultIntegrationRegistry } from "./default-extension-registry";
|
||||
import { Extension } from "./extension";
|
||||
import { ExtensionMetadata, VendorMetadata } from "./type";
|
||||
|
||||
/** Constructs the integration registry */
|
||||
export function buildRegistry() {
|
||||
// FIXME: find a better way to build the registry than a hard-coded factory function
|
||||
function registerAll(vendor: VendorMetadata, integrations: ExtensionMetadata[]) {
|
||||
registry.registerVendor(vendor);
|
||||
for (const integration of integrations) {
|
||||
registry.registerExtension(integration);
|
||||
}
|
||||
}
|
||||
|
||||
const registry = new DefaultIntegrationRegistry();
|
||||
|
||||
for (const site of Reflect.ownKeys(Extension) as string[]) {
|
||||
registry.registerSite(Extension[site]);
|
||||
}
|
||||
|
||||
registerAll(AddyIo, AddyIoIntegrations);
|
||||
registerAll(DuckDuckGo, DuckDuckGoIntegrations);
|
||||
registerAll(Fastmail, FastmailIntegrations);
|
||||
registerAll(ForwardEmail, ForwardEmailIntegrations);
|
||||
registerAll(Mozilla, MozillaIntegrations);
|
||||
registerAll(SimpleLogin, SimpleLoginIntegrations);
|
||||
|
||||
return registry;
|
||||
}
|
||||
@@ -1,17 +1,17 @@
|
||||
import { FieldsBySite, Site, VendorsByExtension } from "./data";
|
||||
|
||||
/** well-known name for a feature extensible through an integration. */
|
||||
/** well-known name for a feature extensible through an extension. */
|
||||
export type SiteId = keyof typeof Site;
|
||||
|
||||
/** well-known name for a field surfaced from an extension site to a vendor. */
|
||||
export type DisclosedField = (typeof FieldsBySite)[SiteId][number];
|
||||
|
||||
/** Identifies a vendor integrated into bitwarden */
|
||||
/** Identifies a vendor extending bitwarden */
|
||||
export type VendorId = (typeof VendorsByExtension)[SiteId][number];
|
||||
|
||||
/** The capabilities and descriptive content for an integration */
|
||||
/** The capabilities and descriptive content for an extension */
|
||||
export type SiteMetadata = {
|
||||
/** Uniquely identifies the integrator. */
|
||||
/** Uniquely identifies the extension site. */
|
||||
id: SiteId;
|
||||
|
||||
/** Lists the fields disclosed by the extension to the vendor */
|
||||
@@ -33,7 +33,7 @@ type TokenHeader =
|
||||
authorization: "bearer" | "token" | "basic-username";
|
||||
};
|
||||
|
||||
/** Catalogues an integration's hosting status.
|
||||
/** Catalogues an extension's hosting status.
|
||||
* selfHost: "never" always uses the service's base URL
|
||||
* selfHost: "maybe" allows the user to override the service's
|
||||
* base URL with their own.
|
||||
@@ -46,16 +46,16 @@ export type ApiHost = TokenHeader &
|
||||
| { selfHost: "always" }
|
||||
);
|
||||
|
||||
/** The capabilities and descriptive content for an integration */
|
||||
/** The capabilities and descriptive content for an extension */
|
||||
export type VendorMetadata = {
|
||||
/** Uniquely identifies the integrator. */
|
||||
/** Uniquely identifies the vendor. */
|
||||
id: VendorId;
|
||||
|
||||
/** Brand name of the integrator. */
|
||||
/** Brand name of the service providing the extension. */
|
||||
name: string;
|
||||
};
|
||||
|
||||
/** Describes an integration provided by a vendor */
|
||||
/** Describes an extension provided by a vendor */
|
||||
export type ExtensionMetadata = {
|
||||
/** The part of Bitwarden extended by the vendor's services */
|
||||
site: SiteMetadata;
|
||||
@@ -69,7 +69,7 @@ export type ExtensionMetadata = {
|
||||
name?: string;
|
||||
};
|
||||
|
||||
/** Hosting provider capabilities required by the integration */
|
||||
/** Hosting provider capabilities required by the extension */
|
||||
host: ApiHost;
|
||||
|
||||
/** Lists the fields disclosed by the extension to the vendor.
|
||||
@@ -79,20 +79,20 @@ export type ExtensionMetadata = {
|
||||
requestedFields: DisclosedField[];
|
||||
};
|
||||
|
||||
/** Identifies a collection of integrations.
|
||||
/** Identifies a collection of extensions.
|
||||
*/
|
||||
export type ExtensionSet =
|
||||
| {
|
||||
/** A set of integrations sharing an extension point */
|
||||
/** A set of extensions sharing an extension point */
|
||||
site: SiteId;
|
||||
}
|
||||
| {
|
||||
/** A set of integrations sharing a vendor */
|
||||
/** A set of extensions sharing a vendor */
|
||||
vendor: VendorId;
|
||||
}
|
||||
| {
|
||||
/** The total set of integrations. This is used to set a categorical
|
||||
* rule affecting all integrations.
|
||||
/** The total set of extensions. This is used to set a categorical
|
||||
* rule affecting all extensions.
|
||||
*/
|
||||
all: true;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user