mirror of
https://github.com/bitwarden/browser.git
synced 2024-10-22 07:50:04 +02:00
component rough draft
This commit is contained in:
parent
7b5403a5e8
commit
0791191ce0
@ -160,7 +160,14 @@ export class CredentialGeneratorComponent implements OnInit, OnDestroy {
|
||||
if (root.nav === IDENTIFIER) {
|
||||
return concat(of(this.username.value), this.username.valueChanges);
|
||||
} else {
|
||||
return of(root as { nav: PasswordAlgorithm });
|
||||
return of(root as { nav: CredentialAlgorithm });
|
||||
}
|
||||
}),
|
||||
switchMap((tier1) => {
|
||||
if (tier1.nav === FORWARDER) {
|
||||
return concat(of(this.forwarder.value), this.forwarder.valueChanges);
|
||||
} else {
|
||||
return of(tier1 as { nav: CredentialAlgorithm });
|
||||
}
|
||||
}),
|
||||
filter(({ nav }) => !!nav),
|
||||
|
@ -31,6 +31,13 @@
|
||||
credentialTypeHint$ | async
|
||||
}}</bit-hint>
|
||||
</bit-form-field>
|
||||
<bit-form-field>
|
||||
<bit-label>{{ "forwarder" | i18n }}</bit-label>
|
||||
<bit-select [items]="forwarderOptions$ | async" formControlName="forwarder"> </bit-select>
|
||||
<bit-hint *ngIf="!!(forwarderTypeHint$ | async)">{{
|
||||
forwarderTypeHint$ | async
|
||||
}}</bit-hint>
|
||||
</bit-form-field>
|
||||
</form>
|
||||
<tools-catchall-settings
|
||||
*ngIf="(algorithm$ | async)?.id === 'catchall'"
|
||||
@ -47,6 +54,7 @@
|
||||
[userId]="this.userId$ | async"
|
||||
(onUpdated)="generate$.next()"
|
||||
/>
|
||||
<!-- TODO: list forwarder settings components -->
|
||||
</bit-card>
|
||||
</div>
|
||||
</bit-section>
|
||||
|
@ -23,6 +23,7 @@ import {
|
||||
GeneratedCredential,
|
||||
Generators,
|
||||
isEmailAlgorithm,
|
||||
isForwarderIntegration,
|
||||
isUsernameAlgorithm,
|
||||
} from "@bitwarden/generator-core";
|
||||
|
||||
@ -78,10 +79,16 @@ export class UsernameGeneratorComponent implements OnInit, OnDestroy {
|
||||
this.generatorService
|
||||
.algorithms$(["email", "username"], { userId$: this.userId$ })
|
||||
.pipe(
|
||||
map((algorithms) => this.toOptions(algorithms)),
|
||||
map((algorithms) => [
|
||||
this.toOptions(algorithms.filter(a => !isForwarderIntegration(a.id))),
|
||||
this.toOptions(algorithms.filter(a => isForwarderIntegration(a.id)))
|
||||
] as const),
|
||||
takeUntil(this.destroyed),
|
||||
)
|
||||
.subscribe(this.typeOptions$);
|
||||
.subscribe(([type, forwarder]) => {
|
||||
this.typeOptions$.next(type);
|
||||
this.forwarderOptions$.next(forwarder);
|
||||
});
|
||||
|
||||
this.algorithm$
|
||||
.pipe(
|
||||
@ -184,6 +191,9 @@ export class UsernameGeneratorComponent implements OnInit, OnDestroy {
|
||||
/** Lists the credential types supported by the component. */
|
||||
protected typeOptions$ = new BehaviorSubject<Option<CredentialAlgorithm>[]>([]);
|
||||
|
||||
/** Lists the credential types supported by the component. */
|
||||
protected forwarderOptions$ = new BehaviorSubject<Option<CredentialAlgorithm>[]>([]);
|
||||
|
||||
/** tracks the currently selected credential type */
|
||||
protected algorithm$ = new ReplaySubject<CredentialGeneratorInfo>(1);
|
||||
|
||||
|
@ -5,7 +5,7 @@ export const PasswordAlgorithms = Object.freeze(["password", "passphrase"] as co
|
||||
export const UsernameAlgorithms = Object.freeze(["username"] as const);
|
||||
|
||||
/** Types of email addresses that may be generated by the credential generator */
|
||||
export const EmailAlgorithms = Object.freeze(["catchall", "forwarder", "subaddress", "addyio"] as const);
|
||||
export const EmailAlgorithms = Object.freeze(["catchall", "subaddress"] as const);
|
||||
|
||||
/** All types of credentials that may be generated by the credential generator */
|
||||
export const CredentialAlgorithms = Object.freeze([
|
||||
|
@ -1,11 +1,10 @@
|
||||
import { PolicyType } from "@bitwarden/common/admin-console/enums";
|
||||
import { Policy } from "@bitwarden/common/admin-console/models/domain/policy";
|
||||
import { GENERATOR_DISK, UserKeyDefinition } from "@bitwarden/common/platform/state";
|
||||
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 { Forwarder } from "../engine/forwarder";
|
||||
import { AddyIoSettings } from "../integration";
|
||||
import {
|
||||
DefaultPolicyEvaluator,
|
||||
DynamicPasswordPolicyConstraints,
|
||||
@ -27,6 +26,7 @@ import {
|
||||
CredentialGenerator,
|
||||
CredentialGeneratorConfiguration,
|
||||
EffUsernameGenerationOptions,
|
||||
ForwarderIntegration,
|
||||
GeneratorDependencyProvider,
|
||||
NoPolicy,
|
||||
PassphraseGenerationOptions,
|
||||
@ -36,7 +36,6 @@ import {
|
||||
SubaddressGenerationOptions,
|
||||
} from "../types";
|
||||
|
||||
import { DefaultAddyIoOptions } from "./default-addy-io-options";
|
||||
import { DefaultCatchallOptions } from "./default-catchall-options";
|
||||
import { DefaultEffUsernameOptions } from "./default-eff-username-options";
|
||||
import { DefaultPassphraseBoundaries } from "./default-passphrase-boundaries";
|
||||
@ -44,6 +43,8 @@ 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",
|
||||
@ -219,26 +220,27 @@ const SUBADDRESS = Object.freeze({
|
||||
},
|
||||
} satisfies CredentialGeneratorConfiguration<SubaddressGenerationOptions, NoPolicy>);
|
||||
|
||||
// FIXME: forwarders should dynamically extend generators; they shouldn't
|
||||
// have their own entries. They're included here solely in order to quickly
|
||||
// create generator configurations during UI modernization
|
||||
const ADDYIO = Object.freeze({
|
||||
id: "addyio",
|
||||
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}`);
|
||||
}
|
||||
|
||||
const forwarder = Object.freeze({
|
||||
id: { forwarder: configuration.id },
|
||||
category: "email",
|
||||
nameKey: "addyIoKey",
|
||||
nameKey: configuration.name,
|
||||
onlyOnRequest: true,
|
||||
engine: {
|
||||
create(dependencies: GeneratorDependencyProvider) {
|
||||
return new Forwarder(dependencies.client, dependencies.i18nService);
|
||||
return new Forwarder(configuration, dependencies.client, dependencies.i18nService);
|
||||
}
|
||||
},
|
||||
settings: {
|
||||
initial: DefaultAddyIoOptions,
|
||||
initial: configuration.forwarder.defaultSettings,
|
||||
constraints: {},
|
||||
account: new UserKeyDefinition<AddyIoSettings>(GENERATOR_DISK, "addyIoGenerator", {
|
||||
deserializer: (value) => value,
|
||||
clearOn: [],
|
||||
})
|
||||
account: configuration.forwarder.settings
|
||||
},
|
||||
policy: {
|
||||
type: PolicyType.PasswordGenerator,
|
||||
@ -247,13 +249,16 @@ const ADDYIO = Object.freeze({
|
||||
return {};
|
||||
},
|
||||
createEvaluator(_policy: NoPolicy) {
|
||||
return new DefaultPolicyEvaluator<AddyIoSettings>();
|
||||
return new DefaultPolicyEvaluator<Settings>();
|
||||
},
|
||||
toConstraints(_policy: NoPolicy) {
|
||||
return new IdentityConstraint<AddyIoSettings>();
|
||||
return new IdentityConstraint<Settings>();
|
||||
},
|
||||
}
|
||||
} satisfies CredentialGeneratorConfiguration<AddyIoSettings, NoPolicy>);
|
||||
} satisfies CredentialGeneratorConfiguration<Settings, NoPolicy>);
|
||||
|
||||
return forwarder;
|
||||
}
|
||||
|
||||
/** Generator configurations */
|
||||
export const Generators = Object.freeze({
|
||||
@ -271,6 +276,4 @@ export const Generators = Object.freeze({
|
||||
|
||||
/** Email subaddress generator configuration */
|
||||
subaddress: SUBADDRESS,
|
||||
|
||||
addyio: ADDYIO,
|
||||
});
|
||||
|
@ -20,6 +20,12 @@ export const Integrations = Object.freeze({
|
||||
|
||||
const integrations = Object.fromEntries(Object.values(Integrations).map((i) => [i.id, i as ForwarderConfiguration<object>]));
|
||||
|
||||
export function getIntegration(id: IntegrationId) : ForwarderConfiguration<ApiSettings> {
|
||||
return integrations[id as string]
|
||||
export function getForwarderConfiguration(id: IntegrationId) : ForwarderConfiguration<ApiSettings> {
|
||||
const maybeForwarder = integrations[id as string];
|
||||
|
||||
if("forwarder" in maybeForwarder) {
|
||||
return maybeForwarder as ForwarderConfiguration<ApiSettings>
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
@ -35,9 +35,11 @@ export type ForwarderConfiguration<
|
||||
defaultSettings: Partial<Settings>;
|
||||
|
||||
/** forwarder settings storage */
|
||||
// FIXME: this should be a `SubjectConfiguration<Settings>`
|
||||
settings: UserKeyDefinition<Settings>;
|
||||
|
||||
/** forwarder settings import buffer; `undefined` when there is no buffer. */
|
||||
// FIXME: this should be a `SubjectConfiguration<Settings>`
|
||||
importBuffer?: BufferedKeyDefinition<Settings>;
|
||||
|
||||
/** createForwardingEmail RPC definition */
|
||||
|
@ -2,7 +2,6 @@ import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.servic
|
||||
import { ApiSettings, IntegrationRequest, RestClient } from "@bitwarden/common/tools/integration/rpc";
|
||||
import { GenerationRequest } from "@bitwarden/common/tools/types";
|
||||
|
||||
import { getIntegration } from "../data";
|
||||
import {
|
||||
CredentialGenerator,
|
||||
GeneratedCredential,
|
||||
@ -20,32 +19,24 @@ export class Forwarder
|
||||
/** Instantiates the email randomizer
|
||||
* @param random data source for random data
|
||||
*/
|
||||
constructor(private client: RestClient, private i18nService: I18nService) {}
|
||||
constructor(private configuration: ForwarderConfiguration<ApiSettings>, private client: RestClient, private i18nService: I18nService) {}
|
||||
|
||||
async generate(
|
||||
request: GenerationRequest,
|
||||
settings: ApiSettings,
|
||||
) {
|
||||
if(!request.integration) {
|
||||
throw new Error("Invalid integration request received by generator.");
|
||||
}
|
||||
|
||||
const integration = getIntegration(request.integration);
|
||||
if(!integration) {
|
||||
throw new Error("Invalid integration request received by generator.");
|
||||
}
|
||||
|
||||
const requestOptions: IntegrationRequest & AccountRequest = { website: request.website };
|
||||
|
||||
const getAccount = await this.getAccountId(integration, settings);
|
||||
const getAccount = await this.getAccountId(this.configuration, settings);
|
||||
if (getAccount) {
|
||||
requestOptions.accountId = await this.client.fetchJson(getAccount, requestOptions);
|
||||
}
|
||||
|
||||
const create = this.createForwardingAddress(integration, settings);
|
||||
const create = this.createForwardingAddress(this.configuration, settings);
|
||||
const result = await this.client.fetchJson(create, requestOptions);
|
||||
const id = { forwarder: this.configuration.id };
|
||||
|
||||
return new GeneratedCredential(result, "forwarder", Date.now());
|
||||
return new GeneratedCredential(result, id, Date.now());
|
||||
}
|
||||
|
||||
private createContext<Settings>(
|
||||
|
@ -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 } from "../data";
|
||||
import { Generators, toCredentialGeneratorConfiguration } from "../data";
|
||||
import { availableAlgorithms } from "../policies/available-algorithms-policy";
|
||||
import { mapPolicyToConstraints } from "../rx";
|
||||
import {
|
||||
@ -45,6 +45,7 @@ import {
|
||||
CredentialCategory,
|
||||
CredentialGeneratorInfo,
|
||||
CredentialPreference,
|
||||
isForwarderIntegration
|
||||
} from "../types";
|
||||
import { CredentialGeneratorConfiguration as Configuration, GeneratorDependencyProvider } from "../types/credential-generator-configuration";
|
||||
import { GeneratorConstraints } from "../types/generator-constraints";
|
||||
@ -182,7 +183,7 @@ export class CredentialGeneratorService {
|
||||
return policies$;
|
||||
}),
|
||||
map((available) => {
|
||||
const filtered = algorithms.filter((c) => available.has(c.id));
|
||||
const filtered = algorithms.filter((c) => isForwarderIntegration(c.id) || available.has(c.id));
|
||||
return filtered;
|
||||
}),
|
||||
);
|
||||
@ -200,7 +201,7 @@ export class CredentialGeneratorService {
|
||||
const categories = Array.isArray(category) ? category : [category];
|
||||
const algorithms = categories
|
||||
.flatMap((c) => CredentialCategories[c])
|
||||
.map((c) => (c === "forwarder" ? null : Generators[c]))
|
||||
.map((id) => this.algorithm(id))
|
||||
.filter((info) => info !== null);
|
||||
|
||||
return algorithms;
|
||||
@ -211,7 +212,11 @@ export class CredentialGeneratorService {
|
||||
* @returns the requested metadata, or `null` if the metadata wasn't found.
|
||||
*/
|
||||
algorithm(id: CredentialAlgorithm): CredentialGeneratorInfo {
|
||||
return (id === "forwarder" ? null : Generators[id]) ?? null;
|
||||
if(isForwarderIntegration(id)) {
|
||||
return toCredentialGeneratorConfiguration(id);
|
||||
} else {
|
||||
return Generators[id];
|
||||
}
|
||||
}
|
||||
|
||||
/** Get the settings for the provided configuration
|
||||
|
@ -60,6 +60,9 @@ export type CredentialGeneratorConfiguration<Settings, Policy> = CredentialGener
|
||||
|
||||
/** storage location for account-global settings */
|
||||
account: UserKeyDefinition<Settings>;
|
||||
|
||||
/** storage location for *plaintext* settings imports */
|
||||
import?: UserKeyDefinition<Settings>;
|
||||
};
|
||||
|
||||
/** defines how to construct policy for this settings instance */
|
||||
|
@ -1,3 +1,5 @@
|
||||
import { IntegrationId } from "@bitwarden/common/tools/integration";
|
||||
|
||||
import { EmailAlgorithms, PasswordAlgorithms, UsernameAlgorithms } from "../data/generator-types";
|
||||
|
||||
/** A type of password that may be generated by the credential generator. */
|
||||
@ -9,8 +11,15 @@ export type UsernameAlgorithm = (typeof UsernameAlgorithms)[number];
|
||||
/** A type of email address that may be generated by the credential generator. */
|
||||
export type EmailAlgorithm = (typeof EmailAlgorithms)[number];
|
||||
|
||||
export type ForwarderIntegration = { forwarder: IntegrationId };
|
||||
|
||||
/** Returns true when the input algorithm is a forwarder integration. */
|
||||
export function isForwarderIntegration(algorithm: CredentialAlgorithm) : algorithm is ForwarderIntegration {
|
||||
return (typeof algorithm === "object") && "forwarder" in algorithm;
|
||||
}
|
||||
|
||||
/** A type of credential that may be generated by the credential generator. */
|
||||
export type CredentialAlgorithm = PasswordAlgorithm | UsernameAlgorithm | EmailAlgorithm;
|
||||
export type CredentialAlgorithm = PasswordAlgorithm | UsernameAlgorithm | EmailAlgorithm | ForwarderIntegration;
|
||||
|
||||
/** Compound credential types supported by the credential generator. */
|
||||
export const CredentialCategories = Object.freeze({
|
||||
|
Loading…
Reference in New Issue
Block a user