mirror of
https://github.com/bitwarden/browser.git
synced 2024-10-22 07:50:04 +02:00
forwarder lookup and generation support
This commit is contained in:
parent
0791191ce0
commit
3aebbe9a64
@ -1,7 +1,13 @@
|
||||
import { Opaque } from "type-fest";
|
||||
|
||||
export const IntegrationIds = [
|
||||
"anonaddy",
|
||||
"duckduckgo",
|
||||
"fastmail",
|
||||
"firefoxrelay",
|
||||
"forwardemail",
|
||||
"simplelogin",
|
||||
] as const;
|
||||
|
||||
/** Identifies a vendor integrated into bitwarden */
|
||||
export type IntegrationId = Opaque<
|
||||
"anonaddy" | "duckduckgo" | "fastmail" | "firefoxrelay" | "forwardemail" | "simplelogin",
|
||||
"IntegrationId"
|
||||
>;
|
||||
export type IntegrationId = Opaque<(typeof IntegrationIds)[number], "IntegrationId">;
|
||||
|
@ -25,10 +25,13 @@ import {
|
||||
CredentialGeneratorService,
|
||||
GeneratedCredential,
|
||||
Generators,
|
||||
getForwarderConfiguration,
|
||||
isEmailAlgorithm,
|
||||
isForwarderIntegration,
|
||||
isPasswordAlgorithm,
|
||||
isUsernameAlgorithm,
|
||||
PasswordAlgorithm,
|
||||
toCredentialGeneratorConfiguration,
|
||||
} from "@bitwarden/generator-core";
|
||||
|
||||
/** root category that drills into username and email categories */
|
||||
@ -247,10 +250,15 @@ export class CredentialGeneratorComponent implements OnInit, OnDestroy {
|
||||
|
||||
case "passphrase":
|
||||
return this.generatorService.generate$(Generators.passphrase, dependencies);
|
||||
|
||||
default:
|
||||
throw new Error(`Invalid generator type: "${type}"`);
|
||||
}
|
||||
|
||||
if (isForwarderIntegration(type)) {
|
||||
const forwarder = getForwarderConfiguration(type.forwarder);
|
||||
const configuration = toCredentialGeneratorConfiguration(forwarder);
|
||||
return this.generatorService.generate$(configuration, dependencies);
|
||||
}
|
||||
|
||||
throw new Error(`Invalid generator type: "${type}"`);
|
||||
}
|
||||
|
||||
/** Lists the credential types of the username algorithm box. */
|
||||
|
@ -22,9 +22,11 @@ import {
|
||||
CredentialGeneratorService,
|
||||
GeneratedCredential,
|
||||
Generators,
|
||||
getForwarderConfiguration,
|
||||
isEmailAlgorithm,
|
||||
isForwarderIntegration,
|
||||
isUsernameAlgorithm,
|
||||
toCredentialGeneratorConfiguration,
|
||||
} from "@bitwarden/generator-core";
|
||||
|
||||
/** Component that generates usernames and emails */
|
||||
@ -79,10 +81,13 @@ export class UsernameGeneratorComponent implements OnInit, OnDestroy {
|
||||
this.generatorService
|
||||
.algorithms$(["email", "username"], { userId$: this.userId$ })
|
||||
.pipe(
|
||||
map((algorithms) => [
|
||||
this.toOptions(algorithms.filter(a => !isForwarderIntegration(a.id))),
|
||||
this.toOptions(algorithms.filter(a => isForwarderIntegration(a.id)))
|
||||
] as const),
|
||||
map(
|
||||
(algorithms) =>
|
||||
[
|
||||
this.toOptions(algorithms.filter((a) => !isForwarderIntegration(a.id))),
|
||||
this.toOptions(algorithms.filter((a) => isForwarderIntegration(a.id))),
|
||||
] as const,
|
||||
),
|
||||
takeUntil(this.destroyed),
|
||||
)
|
||||
.subscribe(([type, forwarder]) => {
|
||||
@ -182,10 +187,15 @@ export class UsernameGeneratorComponent implements OnInit, OnDestroy {
|
||||
|
||||
case "username":
|
||||
return this.generatorService.generate$(Generators.username, dependencies);
|
||||
|
||||
default:
|
||||
throw new Error(`Invalid generator type: "${type}"`);
|
||||
}
|
||||
|
||||
if (isForwarderIntegration(type)) {
|
||||
const forwarder = getForwarderConfiguration(type.forwarder);
|
||||
const configuration = toCredentialGeneratorConfiguration(forwarder);
|
||||
return this.generatorService.generate$(configuration, dependencies);
|
||||
}
|
||||
|
||||
throw new Error(`Invalid generator type: "${type}"`);
|
||||
}
|
||||
|
||||
/** Lists the credential types supported by the component. */
|
||||
|
@ -3,7 +3,12 @@ import { Policy } from "@bitwarden/common/admin-console/models/domain/policy";
|
||||
import { ApiSettings } from "@bitwarden/common/tools/integration/rpc";
|
||||
import { IdentityConstraint } from "@bitwarden/common/tools/state/identity-state-constraint";
|
||||
|
||||
import { EmailRandomizer, PasswordRandomizer, UsernameRandomizer } from "../engine";
|
||||
import {
|
||||
EmailRandomizer,
|
||||
ForwarderConfiguration,
|
||||
PasswordRandomizer,
|
||||
UsernameRandomizer,
|
||||
} from "../engine";
|
||||
import { Forwarder } from "../engine/forwarder";
|
||||
import {
|
||||
DefaultPolicyEvaluator,
|
||||
@ -26,7 +31,6 @@ import {
|
||||
CredentialGenerator,
|
||||
CredentialGeneratorConfiguration,
|
||||
EffUsernameGenerationOptions,
|
||||
ForwarderIntegration,
|
||||
GeneratorDependencyProvider,
|
||||
NoPolicy,
|
||||
PassphraseGenerationOptions,
|
||||
@ -43,8 +47,6 @@ import { DefaultPassphraseGenerationOptions } from "./default-passphrase-generat
|
||||
import { DefaultPasswordBoundaries } from "./default-password-boundaries";
|
||||
import { DefaultPasswordGenerationOptions } from "./default-password-generation-options";
|
||||
import { DefaultSubaddressOptions } from "./default-subaddress-generator-options";
|
||||
import { getForwarderConfiguration } from "./integrations";
|
||||
|
||||
|
||||
const PASSPHRASE = Object.freeze({
|
||||
id: "passphrase",
|
||||
@ -52,7 +54,9 @@ const PASSPHRASE = Object.freeze({
|
||||
nameKey: "passphrase",
|
||||
onlyOnRequest: false,
|
||||
engine: {
|
||||
create(dependencies: GeneratorDependencyProvider): CredentialGenerator<PassphraseGenerationOptions> {
|
||||
create(
|
||||
dependencies: GeneratorDependencyProvider,
|
||||
): CredentialGenerator<PassphraseGenerationOptions> {
|
||||
return new PasswordRandomizer(dependencies.randomizer);
|
||||
},
|
||||
},
|
||||
@ -89,7 +93,9 @@ const PASSWORD = Object.freeze({
|
||||
nameKey: "password",
|
||||
onlyOnRequest: false,
|
||||
engine: {
|
||||
create(dependencies: GeneratorDependencyProvider): CredentialGenerator<PasswordGenerationOptions> {
|
||||
create(
|
||||
dependencies: GeneratorDependencyProvider,
|
||||
): CredentialGenerator<PasswordGenerationOptions> {
|
||||
return new PasswordRandomizer(dependencies.randomizer);
|
||||
},
|
||||
},
|
||||
@ -134,7 +140,9 @@ const USERNAME = Object.freeze({
|
||||
nameKey: "randomWord",
|
||||
onlyOnRequest: false,
|
||||
engine: {
|
||||
create(dependencies: GeneratorDependencyProvider): CredentialGenerator<EffUsernameGenerationOptions> {
|
||||
create(
|
||||
dependencies: GeneratorDependencyProvider,
|
||||
): CredentialGenerator<EffUsernameGenerationOptions> {
|
||||
return new UsernameRandomizer(dependencies.randomizer);
|
||||
},
|
||||
},
|
||||
@ -165,7 +173,9 @@ const CATCHALL = Object.freeze({
|
||||
descriptionKey: "catchallEmailDesc",
|
||||
onlyOnRequest: false,
|
||||
engine: {
|
||||
create(dependencies: GeneratorDependencyProvider): CredentialGenerator<CatchallGenerationOptions> {
|
||||
create(
|
||||
dependencies: GeneratorDependencyProvider,
|
||||
): CredentialGenerator<CatchallGenerationOptions> {
|
||||
return new EmailRandomizer(dependencies.randomizer);
|
||||
},
|
||||
},
|
||||
@ -196,7 +206,9 @@ const SUBADDRESS = Object.freeze({
|
||||
descriptionKey: "plusAddressedEmailDesc",
|
||||
onlyOnRequest: false,
|
||||
engine: {
|
||||
create(dependencies: GeneratorDependencyProvider): CredentialGenerator<SubaddressGenerationOptions> {
|
||||
create(
|
||||
dependencies: GeneratorDependencyProvider,
|
||||
): CredentialGenerator<SubaddressGenerationOptions> {
|
||||
return new EmailRandomizer(dependencies.randomizer);
|
||||
},
|
||||
},
|
||||
@ -220,13 +232,9 @@ const SUBADDRESS = Object.freeze({
|
||||
},
|
||||
} satisfies CredentialGeneratorConfiguration<SubaddressGenerationOptions, NoPolicy>);
|
||||
|
||||
export function toCredentialGeneratorConfiguration<Settings extends ApiSettings = ApiSettings>(id :ForwarderIntegration) {
|
||||
// TODO: eliminate the type erasure
|
||||
const configuration = getForwarderConfiguration(id.forwarder) as any;
|
||||
if(!configuration) {
|
||||
throw new Error(`Invalid forwarder id: ${id.forwarder}`);
|
||||
}
|
||||
|
||||
export function toCredentialGeneratorConfiguration<Settings extends ApiSettings = ApiSettings>(
|
||||
configuration: ForwarderConfiguration<Settings>,
|
||||
) {
|
||||
const forwarder = Object.freeze({
|
||||
id: { forwarder: configuration.id },
|
||||
category: "email",
|
||||
@ -234,13 +242,15 @@ export function toCredentialGeneratorConfiguration<Settings extends ApiSettings
|
||||
onlyOnRequest: true,
|
||||
engine: {
|
||||
create(dependencies: GeneratorDependencyProvider) {
|
||||
return new Forwarder(configuration, dependencies.client, dependencies.i18nService);
|
||||
}
|
||||
// FIXME: figure out why `configuration` fails to typecheck
|
||||
const config: any = configuration;
|
||||
return new Forwarder(config, dependencies.client, dependencies.i18nService);
|
||||
},
|
||||
},
|
||||
settings: {
|
||||
initial: configuration.forwarder.defaultSettings,
|
||||
constraints: {},
|
||||
account: configuration.forwarder.settings
|
||||
account: configuration.forwarder.settings,
|
||||
},
|
||||
policy: {
|
||||
type: PolicyType.PasswordGenerator,
|
||||
@ -254,7 +264,7 @@ export function toCredentialGeneratorConfiguration<Settings extends ApiSettings
|
||||
toConstraints(_policy: NoPolicy) {
|
||||
return new IdentityConstraint<Settings>();
|
||||
},
|
||||
}
|
||||
},
|
||||
} satisfies CredentialGeneratorConfiguration<Settings, NoPolicy>);
|
||||
|
||||
return forwarder;
|
||||
|
@ -9,6 +9,13 @@ import { FirefoxRelay } from "../integration/firefox-relay";
|
||||
import { ForwardEmail } from "../integration/forward-email";
|
||||
import { SimpleLogin } from "../integration/simple-login";
|
||||
|
||||
/** Fixed list of integrations available to the application
|
||||
* @example
|
||||
*
|
||||
* // Use `toCredentialGeneratorConfiguration(id :ForwarderIntegration)`
|
||||
* // to convert an integration to a generator configuration
|
||||
* const generator = toCredentialGeneratorConfiguration(Integrations.AddyIo);
|
||||
*/
|
||||
export const Integrations = Object.freeze({
|
||||
AddyIo,
|
||||
DuckDuckGo,
|
||||
@ -18,13 +25,13 @@ export const Integrations = Object.freeze({
|
||||
SimpleLogin,
|
||||
} as const);
|
||||
|
||||
const integrations = Object.fromEntries(Object.values(Integrations).map((i) => [i.id, i as ForwarderConfiguration<object>]));
|
||||
const integrations = new Map(Object.values(Integrations).map((i) => [i.id, i]));
|
||||
|
||||
export function getForwarderConfiguration(id: IntegrationId) : ForwarderConfiguration<ApiSettings> {
|
||||
const maybeForwarder = integrations[id as string];
|
||||
export function getForwarderConfiguration(id: IntegrationId): ForwarderConfiguration<ApiSettings> {
|
||||
const maybeForwarder = integrations.get(id);
|
||||
|
||||
if("forwarder" in maybeForwarder) {
|
||||
return maybeForwarder as ForwarderConfiguration<ApiSettings>
|
||||
if ("forwarder" in maybeForwarder) {
|
||||
return maybeForwarder as ForwarderConfiguration<ApiSettings>;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
|
@ -36,7 +36,7 @@ import { isDynamic } from "@bitwarden/common/tools/state/state-constraints-depen
|
||||
import { UserStateSubject } from "@bitwarden/common/tools/state/user-state-subject";
|
||||
|
||||
import { Randomizer } from "../abstractions";
|
||||
import { Generators, toCredentialGeneratorConfiguration } from "../data";
|
||||
import { Generators, getForwarderConfiguration, toCredentialGeneratorConfiguration } from "../data";
|
||||
import { availableAlgorithms } from "../policies/available-algorithms-policy";
|
||||
import { mapPolicyToConstraints } from "../rx";
|
||||
import {
|
||||
@ -45,9 +45,12 @@ import {
|
||||
CredentialCategory,
|
||||
CredentialGeneratorInfo,
|
||||
CredentialPreference,
|
||||
isForwarderIntegration
|
||||
isForwarderIntegration,
|
||||
} from "../types";
|
||||
import { CredentialGeneratorConfiguration as Configuration, GeneratorDependencyProvider } from "../types/credential-generator-configuration";
|
||||
import {
|
||||
CredentialGeneratorConfiguration as Configuration,
|
||||
GeneratorDependencyProvider,
|
||||
} from "../types/credential-generator-configuration";
|
||||
import { GeneratorConstraints } from "../types/generator-constraints";
|
||||
|
||||
import { PREFERENCES } from "./credential-preferences";
|
||||
@ -65,7 +68,7 @@ type Generate$Dependencies = Simplify<Partial<OnDependency> & Partial<UserDepend
|
||||
*/
|
||||
website$?: Observable<string>;
|
||||
|
||||
integration$?: Observable<IntegrationId>
|
||||
integration$?: Observable<IntegrationId>;
|
||||
};
|
||||
|
||||
type Algorithms$Dependencies = Partial<UserDependency>;
|
||||
@ -76,14 +79,14 @@ export class CredentialGeneratorService {
|
||||
private stateProvider: StateProvider,
|
||||
private policyService: PolicyService,
|
||||
private apiService: ApiService,
|
||||
private i18nService: I18nService
|
||||
private i18nService: I18nService,
|
||||
) {}
|
||||
|
||||
private getDependencyProvider() : GeneratorDependencyProvider {
|
||||
private getDependencyProvider(): GeneratorDependencyProvider {
|
||||
return {
|
||||
client: new RestClient(this.apiService, this.i18nService),
|
||||
i18nService: this.i18nService,
|
||||
randomizer: this.randomizer
|
||||
randomizer: this.randomizer,
|
||||
};
|
||||
}
|
||||
|
||||
@ -105,10 +108,7 @@ export class CredentialGeneratorService {
|
||||
|
||||
// stream blocks until all of these values are received
|
||||
const website$ = dependencies?.website$ ?? new BehaviorSubject<string>(null);
|
||||
const integration$ = dependencies?.integration$ ?? new BehaviorSubject<IntegrationId>(null);
|
||||
const request$ = combineLatest([website$, integration$]).pipe(
|
||||
map(([website, integration]) => ({ website, integration }))
|
||||
);
|
||||
const request$ = website$.pipe(map((website) => ({ website })));
|
||||
const settings$ = this.settings$(configuration, dependencies);
|
||||
|
||||
// monitor completion
|
||||
@ -183,7 +183,9 @@ export class CredentialGeneratorService {
|
||||
return policies$;
|
||||
}),
|
||||
map((available) => {
|
||||
const filtered = algorithms.filter((c) => isForwarderIntegration(c.id) || available.has(c.id));
|
||||
const filtered = algorithms.filter(
|
||||
(c) => isForwarderIntegration(c.id) || available.has(c.id),
|
||||
);
|
||||
return filtered;
|
||||
}),
|
||||
);
|
||||
@ -212,8 +214,14 @@ export class CredentialGeneratorService {
|
||||
* @returns the requested metadata, or `null` if the metadata wasn't found.
|
||||
*/
|
||||
algorithm(id: CredentialAlgorithm): CredentialGeneratorInfo {
|
||||
if(isForwarderIntegration(id)) {
|
||||
return toCredentialGeneratorConfiguration(id);
|
||||
if (isForwarderIntegration(id)) {
|
||||
const forwarder = getForwarderConfiguration(id.forwarder);
|
||||
if (!forwarder) {
|
||||
throw new Error(`Invalid forwarder id: ${id.forwarder}`);
|
||||
}
|
||||
|
||||
const generator = toCredentialGeneratorConfiguration(forwarder);
|
||||
return generator;
|
||||
} else {
|
||||
return Generators[id];
|
||||
}
|
||||
|
@ -9,14 +9,19 @@ import { CredentialAlgorithm, CredentialCategory, PolicyConfiguration } from "..
|
||||
import { CredentialGenerator } from "./credential-generator";
|
||||
|
||||
export type GeneratorDependencyProvider = {
|
||||
randomizer: Randomizer,
|
||||
client: RestClient,
|
||||
i18nService: I18nService
|
||||
randomizer: Randomizer;
|
||||
client: RestClient;
|
||||
i18nService: I18nService;
|
||||
};
|
||||
|
||||
/** Credential generator metadata common across credential generators */
|
||||
export type CredentialGeneratorInfo = {
|
||||
/** Uniquely identifies the credential configuration
|
||||
* @example
|
||||
* // Use `isForwarderIntegration(algorithm: CredentialAlgorithm)`
|
||||
* // to pattern test whether the credential describes a forwarder algorithm
|
||||
* const meta : CredentialGeneratorInfo = // ...
|
||||
* const { forwarder } = isForwarderIntegration(meta.id) ? credentialId : {};
|
||||
*/
|
||||
id: CredentialAlgorithm;
|
||||
|
||||
@ -37,7 +42,13 @@ export type CredentialGeneratorInfo = {
|
||||
onlyOnRequest: boolean;
|
||||
};
|
||||
|
||||
/** Credential generator metadata that relies upon typed setting and policy definitions. */
|
||||
/** Credential generator metadata that relies upon typed setting and policy definitions.
|
||||
* @example
|
||||
* // Use `isForwarderIntegration(algorithm: CredentialAlgorithm)`
|
||||
* // to pattern test whether the credential describes a forwarder algorithm
|
||||
* const meta : CredentialGeneratorInfo = // ...
|
||||
* const { forwarder } = isForwarderIntegration(meta.id) ? credentialId : {};
|
||||
*/
|
||||
export type CredentialGeneratorConfiguration<Settings, Policy> = CredentialGeneratorInfo & {
|
||||
/** An algorithm that generates credentials when ran. */
|
||||
engine: {
|
||||
|
Loading…
Reference in New Issue
Block a user