mirror of
https://github.com/bitwarden/browser
synced 2026-02-11 22:13:32 +00:00
flesh out metadata organization
This commit is contained in:
49
libs/tools/generator/core/src/metadata/algorithm-metadata.ts
Normal file
49
libs/tools/generator/core/src/metadata/algorithm-metadata.ts
Normal file
@@ -0,0 +1,49 @@
|
||||
import { CredentialAlgorithm, CredentialType } from "./type";
|
||||
|
||||
/** Credential generator metadata common across credential generators */
|
||||
export type AlgorithmMetadata = {
|
||||
/** Uniquely identifies the credential configuration
|
||||
* @example
|
||||
* // Use `isForwarderIntegration(algorithm: CredentialAlgorithm)`
|
||||
* // to pattern test whether the credential describes a forwarder algorithm
|
||||
* const meta : AlgorithmMetadata = // ...
|
||||
* const { forwarder } = isForwarderIntegration(meta.id) ? credentialId : {};
|
||||
*/
|
||||
id: CredentialAlgorithm;
|
||||
|
||||
/** The kind of credential generated by this configuration */
|
||||
category: CredentialType;
|
||||
|
||||
/** Localization keys */
|
||||
i18nKeys: {
|
||||
/** descriptive name of the algorithm */
|
||||
name: string;
|
||||
|
||||
/** explanatory text for the algorithm */
|
||||
description?: string;
|
||||
|
||||
/** labels the generate action */
|
||||
generateCredential: string;
|
||||
|
||||
/** labels the generated output */
|
||||
credentialGenerated: string;
|
||||
|
||||
/** labels the copy output action */
|
||||
copyCredential: string;
|
||||
};
|
||||
|
||||
/** fine-tunings for generator user experiences */
|
||||
capabilities: {
|
||||
/** `true` when the generator supports autogeneration
|
||||
* @remarks this property is useful when credential generation
|
||||
* carries side effects, such as configuring a service external
|
||||
* to Bitwarden.
|
||||
*/
|
||||
autogenerate: boolean;
|
||||
|
||||
/** Well-known fields to display on the options panel or collect from the environment.
|
||||
* @remarks: at present, this is only used by forwarders
|
||||
*/
|
||||
fields: string[];
|
||||
};
|
||||
};
|
||||
@@ -1,5 +1,6 @@
|
||||
import { deepFreeze } from "../util";
|
||||
|
||||
/** algorithms for generating credentials */
|
||||
export const Algorithm = Object.freeze({
|
||||
/** A password composed of random characters */
|
||||
password: "password",
|
||||
@@ -14,26 +15,37 @@ export const Algorithm = Object.freeze({
|
||||
catchall: "catchall",
|
||||
|
||||
/** An email username composed of words from the EFF word list */
|
||||
plusAddress: "plus-address",
|
||||
plusAddress: "subaddress",
|
||||
|
||||
/** An integrated email forwarding service */
|
||||
forwarder: "forwarder",
|
||||
} as const);
|
||||
|
||||
export const Category = Object.freeze({
|
||||
/** categorizes credentials according to their use-case outside of Bitwarden */
|
||||
export const Type = Object.freeze({
|
||||
password: "password",
|
||||
username: "username",
|
||||
email: "email",
|
||||
} as const);
|
||||
|
||||
/** Credential generation algorithm identifiers grouped by category. */
|
||||
export const CategorizedAlgorithm = deepFreeze({
|
||||
/** Lists algorithms in the "password" credential category */
|
||||
[Category.password]: [Algorithm.password, Algorithm.passphrase] as const,
|
||||
/** categorizes settings according to their expected use-case within Bitwarden */
|
||||
export const Purpose = Object.freeze({
|
||||
/** account-level generator options. This is the default.
|
||||
* @remarks these are the options displayed on the generator tab
|
||||
*/
|
||||
account: "account",
|
||||
|
||||
/** Lists algorithms in the "username" credential category */
|
||||
[Category.username]: [Algorithm.username] as const,
|
||||
// FIXME: consider adding a purpose for bitwarden's master password
|
||||
});
|
||||
|
||||
/** Lists algorithms in the "email" credential category */
|
||||
[Category.email]: [Algorithm.catchall, Algorithm.plusAddress, Algorithm.forwarder] as const,
|
||||
/** Credential generation algorithms grouped by purpose. */
|
||||
export const AlgorithmsByType = deepFreeze({
|
||||
/** Algorithms that produce passwords */
|
||||
[Type.password]: [Algorithm.password, Algorithm.passphrase] as const,
|
||||
|
||||
/** Algorithms that produce usernames */
|
||||
[Type.username]: [Algorithm.username] as const,
|
||||
|
||||
/** Algorithms that produce email addresses */
|
||||
[Type.email]: [Algorithm.catchall, Algorithm.plusAddress, Algorithm.forwarder] as const,
|
||||
} as const);
|
||||
|
||||
76
libs/tools/generator/core/src/metadata/email/catchall.ts
Normal file
76
libs/tools/generator/core/src/metadata/email/catchall.ts
Normal file
@@ -0,0 +1,76 @@
|
||||
import { PolicyType } from "@bitwarden/common/admin-console/enums";
|
||||
import { Policy } from "@bitwarden/common/admin-console/models/domain/policy";
|
||||
import { GENERATOR_DISK } from "@bitwarden/common/platform/state";
|
||||
import { PublicClassifier } from "@bitwarden/common/tools/public-classifier";
|
||||
|
||||
import { EmailRandomizer } from "../../engine";
|
||||
import { CatchallConstraints } from "../../policies/catchall-constraints";
|
||||
import {
|
||||
CatchallGenerationOptions,
|
||||
CredentialGenerator,
|
||||
GeneratorDependencyProvider,
|
||||
NoPolicy,
|
||||
} from "../../types";
|
||||
import { deepFreeze } from "../../util";
|
||||
import { Algorithm, Type } from "../data";
|
||||
import { GeneratorMetadata } from "../generator-metadata";
|
||||
|
||||
const catchall: GeneratorMetadata<CatchallGenerationOptions, NoPolicy> = deepFreeze({
|
||||
id: Algorithm.catchall,
|
||||
category: Type.email,
|
||||
i18nKeys: {
|
||||
name: "catchallEmail",
|
||||
description: "catchallEmailDesc",
|
||||
generateCredential: "generateEmail",
|
||||
credentialGenerated: "email",
|
||||
copyCredential: "copyEmail",
|
||||
},
|
||||
capabilities: {
|
||||
autogenerate: true,
|
||||
fields: [],
|
||||
},
|
||||
engine: {
|
||||
create(
|
||||
dependencies: GeneratorDependencyProvider,
|
||||
): CredentialGenerator<CatchallGenerationOptions> {
|
||||
return new EmailRandomizer(dependencies.randomizer);
|
||||
},
|
||||
},
|
||||
options: {
|
||||
constraints: { catchallDomain: { minLength: 1 } },
|
||||
account: {
|
||||
storage: {
|
||||
key: "catchallGeneratorSettings",
|
||||
target: "object",
|
||||
format: "plain",
|
||||
classifier: new PublicClassifier<CatchallGenerationOptions>([
|
||||
"catchallType",
|
||||
"catchallDomain",
|
||||
]),
|
||||
state: GENERATOR_DISK,
|
||||
initial: {
|
||||
catchallType: "random",
|
||||
catchallDomain: "",
|
||||
},
|
||||
options: {
|
||||
deserializer: (value) => value,
|
||||
clearOn: ["logout"],
|
||||
},
|
||||
},
|
||||
policy: {
|
||||
type: PolicyType.PasswordGenerator,
|
||||
disabledValue: {},
|
||||
},
|
||||
},
|
||||
},
|
||||
policy: {
|
||||
combine(_acc: NoPolicy, _policy: Policy) {
|
||||
return {};
|
||||
},
|
||||
toConstraints(_policy: NoPolicy, email: string) {
|
||||
return new CatchallConstraints(email);
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export default catchall;
|
||||
@@ -0,0 +1,59 @@
|
||||
import { PolicyType } from "@bitwarden/common/admin-console/enums";
|
||||
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 { ForwarderConfiguration } from "../../engine";
|
||||
import { Forwarder } from "../../engine/forwarder";
|
||||
import { GeneratorDependencyProvider, NoPolicy } from "../../types";
|
||||
import { deepFreeze } from "../../util";
|
||||
import { Purpose, Type } from "../data";
|
||||
import { GeneratorMetadata } from "../generator-metadata";
|
||||
import { toForwarderIntegration } from "../util";
|
||||
|
||||
export function toGeneratorMetadata<Settings extends ApiSettings = ApiSettings>(
|
||||
configuration: ForwarderConfiguration<Settings>,
|
||||
): GeneratorMetadata<Settings, NoPolicy> {
|
||||
const forwarder = deepFreeze({
|
||||
id: toForwarderIntegration(configuration),
|
||||
category: Type.email,
|
||||
i18nKeys: {
|
||||
name: configuration.name,
|
||||
description: "forwardedEmailDesc",
|
||||
generateCredential: "generateEmail",
|
||||
credentialGenerated: "email",
|
||||
copyCredential: "copyEmail",
|
||||
},
|
||||
capabilities: {
|
||||
autogenerate: false,
|
||||
fields: configuration.forwarder.request as string[],
|
||||
},
|
||||
engine: {
|
||||
create(dependencies: GeneratorDependencyProvider) {
|
||||
// FIXME: figure out why `configuration` fails to typecheck
|
||||
const config: any = configuration;
|
||||
return new Forwarder(config, dependencies.client, dependencies.i18nService);
|
||||
},
|
||||
},
|
||||
options: {
|
||||
constraints: configuration.forwarder.settingsConstraints,
|
||||
[Purpose.account]: {
|
||||
storage: configuration.forwarder.local.settings,
|
||||
policy: {
|
||||
type: PolicyType.PasswordGenerator,
|
||||
disabledValue: {},
|
||||
},
|
||||
},
|
||||
},
|
||||
policy: {
|
||||
combine(_acc: NoPolicy, _policy: Policy) {
|
||||
return {};
|
||||
},
|
||||
toConstraints(_policy: NoPolicy) {
|
||||
return new IdentityConstraint<Settings>();
|
||||
},
|
||||
},
|
||||
} satisfies GeneratorMetadata<Settings, NoPolicy>);
|
||||
|
||||
return forwarder;
|
||||
}
|
||||
|
||||
79
libs/tools/generator/core/src/metadata/email/plus-address.ts
Normal file
79
libs/tools/generator/core/src/metadata/email/plus-address.ts
Normal file
@@ -0,0 +1,79 @@
|
||||
import { PolicyType } from "@bitwarden/common/admin-console/enums";
|
||||
import { Policy } from "@bitwarden/common/admin-console/models/domain/policy";
|
||||
import { GENERATOR_DISK } from "@bitwarden/common/platform/state";
|
||||
import { PublicClassifier } from "@bitwarden/common/tools/public-classifier";
|
||||
|
||||
import { EmailRandomizer } from "../../engine";
|
||||
import { SubaddressConstraints } from "../../policies/subaddress-constraints";
|
||||
import {
|
||||
CredentialGenerator,
|
||||
GeneratorDependencyProvider,
|
||||
NoPolicy,
|
||||
SubaddressGenerationOptions,
|
||||
} from "../../types";
|
||||
import { deepFreeze } from "../../util";
|
||||
import { Algorithm, Purpose, Type } from "../data";
|
||||
import { GeneratorMetadata } from "../generator-metadata";
|
||||
|
||||
const plusAddress: GeneratorMetadata<SubaddressGenerationOptions, NoPolicy> = deepFreeze({
|
||||
id: Algorithm.plusAddress,
|
||||
category: Type.email,
|
||||
i18nKeys: {
|
||||
name: "plusAddressedEmail",
|
||||
description: "plusAddressedEmailDesc",
|
||||
generateCredential: "generateEmail",
|
||||
credentialGenerated: "email",
|
||||
copyCredential: "copyEmail",
|
||||
},
|
||||
capabilities: {
|
||||
autogenerate: true,
|
||||
fields: [],
|
||||
},
|
||||
engine: {
|
||||
create(
|
||||
dependencies: GeneratorDependencyProvider,
|
||||
): CredentialGenerator<SubaddressGenerationOptions> {
|
||||
return new EmailRandomizer(dependencies.randomizer);
|
||||
},
|
||||
},
|
||||
options: {
|
||||
constraints: {},
|
||||
[Purpose.account]: {
|
||||
storage: {
|
||||
key: "subaddressGeneratorSettings",
|
||||
target: "object",
|
||||
format: "plain",
|
||||
classifier: new PublicClassifier<SubaddressGenerationOptions>([
|
||||
"subaddressType",
|
||||
"subaddressEmail",
|
||||
]),
|
||||
state: GENERATOR_DISK,
|
||||
initial: {
|
||||
subaddressType: "random",
|
||||
subaddressEmail: "",
|
||||
},
|
||||
options: {
|
||||
deserializer(value) {
|
||||
return value;
|
||||
},
|
||||
clearOn: ["logout"],
|
||||
},
|
||||
},
|
||||
policy: {
|
||||
type: PolicyType.PasswordGenerator,
|
||||
disabledValue: {},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
policy: {
|
||||
combine(_acc: NoPolicy, _policy: Policy) {
|
||||
return {};
|
||||
},
|
||||
toConstraints(_policy: NoPolicy, email: string) {
|
||||
return new SubaddressConstraints(email);
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export default plusAddress;
|
||||
67
libs/tools/generator/core/src/metadata/generator-metadata.ts
Normal file
67
libs/tools/generator/core/src/metadata/generator-metadata.ts
Normal file
@@ -0,0 +1,67 @@
|
||||
import { PolicyType } from "@bitwarden/common/admin-console/enums";
|
||||
import { Policy as AdminPolicy } from "@bitwarden/common/admin-console/models/domain/policy";
|
||||
import { ObjectKey } from "@bitwarden/common/tools/state/object-key";
|
||||
import { Constraints } from "@bitwarden/common/tools/types";
|
||||
|
||||
import { CredentialGenerator, GeneratorConstraints, GeneratorDependencyProvider } from "../types";
|
||||
|
||||
import { AlgorithmMetadata } from "./algorithm-metadata";
|
||||
import { Purpose } from "./data";
|
||||
|
||||
/** Extends the algorithm metadata with storage and engine configurations.
|
||||
* @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 GeneratorMetadata<Options, Policy> = AlgorithmMetadata & {
|
||||
/** An algorithm that generates credentials when ran. */
|
||||
engine: {
|
||||
/** Factory for the generator
|
||||
*/
|
||||
create: (randomizer: GeneratorDependencyProvider) => CredentialGenerator<Options>;
|
||||
};
|
||||
|
||||
/** Defines parameters for credential generation */
|
||||
options: {
|
||||
/** global constraints; these apply to *all* generators */
|
||||
constraints: Constraints<Options>;
|
||||
|
||||
/** account-local generator options */
|
||||
[Purpose.account]: {
|
||||
/** plaintext import buffer */
|
||||
import?: ObjectKey<Options, Record<string, never>, Options> & { format: "plain" };
|
||||
|
||||
/** persistent storage location */
|
||||
storage: ObjectKey<Options>;
|
||||
|
||||
/** policy enforced when saving the options */
|
||||
policy: {
|
||||
/** policy administration storage location for the policy */
|
||||
type: PolicyType;
|
||||
|
||||
/** The value of the policy when it is not in effect. */
|
||||
disabledValue: Policy;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
policy: {
|
||||
/** Combines multiple policies set by the administrative console into
|
||||
* a single policy.
|
||||
*/
|
||||
combine: (acc: Policy, policy: AdminPolicy) => Policy;
|
||||
|
||||
/** Converts policy service data into actionable policy constraints.
|
||||
*
|
||||
* @param policy - the policy to map into policy constraints.
|
||||
* @param email - the default email to extend.
|
||||
*
|
||||
* @remarks this version includes constraints needed for the reactive forms;
|
||||
* it was introduced so that the constraints can be incrementally introduced
|
||||
* as the new UI is built.
|
||||
*/
|
||||
toConstraints: (policy: Policy, email: string) => GeneratorConstraints<Options>;
|
||||
};
|
||||
};
|
||||
@@ -4,32 +4,29 @@ import { PublicClassifier } from "@bitwarden/common/tools/public-classifier";
|
||||
import { ObjectKey } from "@bitwarden/common/tools/state/object-key";
|
||||
|
||||
import { PasswordRandomizer } from "../../engine";
|
||||
import {
|
||||
PassphraseGeneratorOptionsEvaluator,
|
||||
passphraseLeastPrivilege,
|
||||
PassphrasePolicyConstraints,
|
||||
} from "../../policies";
|
||||
import { passphraseLeastPrivilege, PassphrasePolicyConstraints } from "../../policies";
|
||||
import {
|
||||
CredentialGenerator,
|
||||
CredentialGeneratorConfiguration,
|
||||
GeneratorDependencyProvider,
|
||||
PassphraseGenerationOptions,
|
||||
PassphraseGeneratorPolicy,
|
||||
} from "../../types";
|
||||
import { Algorithm, Category } from "../data";
|
||||
import { Algorithm, Purpose, Type } from "../data";
|
||||
import { GeneratorMetadata } from "../generator-metadata";
|
||||
|
||||
const passphrase: CredentialGeneratorConfiguration<
|
||||
PassphraseGenerationOptions,
|
||||
PassphraseGeneratorPolicy
|
||||
> = {
|
||||
const passphrase: GeneratorMetadata<PassphraseGenerationOptions, PassphraseGeneratorPolicy> = {
|
||||
id: Algorithm.passphrase,
|
||||
category: Category.password,
|
||||
nameKey: "passphrase",
|
||||
generateKey: "generatePassphrase",
|
||||
generatedValueKey: "passphrase",
|
||||
copyKey: "copyPassphrase",
|
||||
onlyOnRequest: false,
|
||||
request: [],
|
||||
category: Type.password,
|
||||
i18nKeys: {
|
||||
name: "passphrase",
|
||||
generateCredential: "generatePassphrase",
|
||||
credentialGenerated: "passphrase",
|
||||
copyCredential: "copyPassphrase",
|
||||
},
|
||||
capabilities: {
|
||||
autogenerate: false,
|
||||
fields: [],
|
||||
},
|
||||
engine: {
|
||||
create(
|
||||
dependencies: GeneratorDependencyProvider,
|
||||
@@ -37,13 +34,7 @@ const passphrase: CredentialGeneratorConfiguration<
|
||||
return new PasswordRandomizer(dependencies.randomizer);
|
||||
},
|
||||
},
|
||||
settings: {
|
||||
initial: {
|
||||
numWords: 6,
|
||||
wordSeparator: "-",
|
||||
capitalize: false,
|
||||
includeNumber: false,
|
||||
},
|
||||
options: {
|
||||
constraints: {
|
||||
numWords: {
|
||||
min: 3,
|
||||
@@ -52,44 +43,45 @@ const passphrase: CredentialGeneratorConfiguration<
|
||||
},
|
||||
wordSeparator: { maxLength: 1 },
|
||||
},
|
||||
account: {
|
||||
key: "passphraseGeneratorSettings",
|
||||
target: "object",
|
||||
format: "plain",
|
||||
classifier: new PublicClassifier<PassphraseGenerationOptions>([
|
||||
"numWords",
|
||||
"wordSeparator",
|
||||
"capitalize",
|
||||
"includeNumber",
|
||||
]),
|
||||
state: GENERATOR_DISK,
|
||||
initial: {
|
||||
numWords: 6,
|
||||
wordSeparator: "-",
|
||||
capitalize: false,
|
||||
includeNumber: false,
|
||||
},
|
||||
options: {
|
||||
deserializer(value) {
|
||||
return value;
|
||||
[Purpose.account]: {
|
||||
storage: {
|
||||
key: "passphraseGeneratorSettings",
|
||||
target: "object",
|
||||
format: "plain",
|
||||
classifier: new PublicClassifier<PassphraseGenerationOptions>([
|
||||
"numWords",
|
||||
"wordSeparator",
|
||||
"capitalize",
|
||||
"includeNumber",
|
||||
]),
|
||||
state: GENERATOR_DISK,
|
||||
initial: {
|
||||
numWords: 6,
|
||||
wordSeparator: "-",
|
||||
capitalize: false,
|
||||
includeNumber: false,
|
||||
},
|
||||
options: {
|
||||
deserializer(value) {
|
||||
return value;
|
||||
},
|
||||
clearOn: ["logout"],
|
||||
},
|
||||
} satisfies ObjectKey<PassphraseGenerationOptions>,
|
||||
policy: {
|
||||
type: PolicyType.PasswordGenerator,
|
||||
disabledValue: {
|
||||
minNumberWords: 0,
|
||||
capitalize: false,
|
||||
includeNumber: false,
|
||||
},
|
||||
clearOn: ["logout"],
|
||||
},
|
||||
} satisfies ObjectKey<PassphraseGenerationOptions>,
|
||||
},
|
||||
},
|
||||
policy: {
|
||||
type: PolicyType.PasswordGenerator,
|
||||
disabledValue: {
|
||||
minNumberWords: 0,
|
||||
capitalize: false,
|
||||
includeNumber: false,
|
||||
},
|
||||
combine: passphraseLeastPrivilege,
|
||||
createEvaluator(policy) {
|
||||
return new PassphraseGeneratorOptionsEvaluator(policy);
|
||||
},
|
||||
toConstraints(policy) {
|
||||
return new PassphrasePolicyConstraints(policy, passphrase.settings.constraints);
|
||||
return new PassphrasePolicyConstraints(policy, passphrase.options.constraints);
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1,36 +1,32 @@
|
||||
import { PolicyType } from "@bitwarden/common/admin-console/enums";
|
||||
import { GENERATOR_DISK } from "@bitwarden/common/platform/state";
|
||||
import { PublicClassifier } from "@bitwarden/common/tools/public-classifier";
|
||||
import { ObjectKey } from "@bitwarden/common/tools/state/object-key";
|
||||
|
||||
import { PasswordRandomizer } from "../../engine";
|
||||
import {
|
||||
DynamicPasswordPolicyConstraints,
|
||||
PasswordGeneratorOptionsEvaluator,
|
||||
passwordLeastPrivilege,
|
||||
} from "../../policies";
|
||||
import { DynamicPasswordPolicyConstraints, passwordLeastPrivilege } from "../../policies";
|
||||
import {
|
||||
CredentialGenerator,
|
||||
CredentialGeneratorConfiguration,
|
||||
GeneratorDependencyProvider,
|
||||
PasswordGenerationOptions,
|
||||
PasswordGeneratorPolicy,
|
||||
} from "../../types";
|
||||
import { deepFreeze } from "../../util";
|
||||
import { Algorithm, Category } from "../data";
|
||||
import { Algorithm, Purpose, Type } from "../data";
|
||||
import { GeneratorMetadata } from "../generator-metadata";
|
||||
|
||||
const password: CredentialGeneratorConfiguration<
|
||||
PasswordGenerationOptions,
|
||||
PasswordGeneratorPolicy
|
||||
> = deepFreeze({
|
||||
const password: GeneratorMetadata<PasswordGenerationOptions, PasswordGeneratorPolicy> = deepFreeze({
|
||||
id: Algorithm.password,
|
||||
category: Category.password,
|
||||
nameKey: "password",
|
||||
generateKey: "generatePassword",
|
||||
generatedValueKey: "password",
|
||||
copyKey: "copyPassword",
|
||||
onlyOnRequest: false,
|
||||
request: [],
|
||||
category: Type.password,
|
||||
i18nKeys: {
|
||||
name: "password",
|
||||
generateCredential: "generatePassword",
|
||||
credentialGenerated: "password",
|
||||
copyCredential: "copyPassword",
|
||||
},
|
||||
capabilities: {
|
||||
autogenerate: true,
|
||||
fields: [],
|
||||
},
|
||||
engine: {
|
||||
create(
|
||||
dependencies: GeneratorDependencyProvider,
|
||||
@@ -38,19 +34,7 @@ const password: CredentialGeneratorConfiguration<
|
||||
return new PasswordRandomizer(dependencies.randomizer);
|
||||
},
|
||||
},
|
||||
settings: {
|
||||
initial: {
|
||||
length: 14,
|
||||
ambiguous: true,
|
||||
uppercase: true,
|
||||
minUppercase: 1,
|
||||
lowercase: true,
|
||||
minLowercase: 1,
|
||||
number: true,
|
||||
minNumber: 1,
|
||||
special: false,
|
||||
minSpecial: 0,
|
||||
},
|
||||
options: {
|
||||
constraints: {
|
||||
length: {
|
||||
min: 5,
|
||||
@@ -66,60 +50,61 @@ const password: CredentialGeneratorConfiguration<
|
||||
max: 9,
|
||||
},
|
||||
},
|
||||
account: {
|
||||
key: "passwordGeneratorSettings",
|
||||
target: "object",
|
||||
format: "plain",
|
||||
classifier: new PublicClassifier<PasswordGenerationOptions>([
|
||||
"length",
|
||||
"ambiguous",
|
||||
"uppercase",
|
||||
"minUppercase",
|
||||
"lowercase",
|
||||
"minLowercase",
|
||||
"number",
|
||||
"minNumber",
|
||||
"special",
|
||||
"minSpecial",
|
||||
]),
|
||||
state: GENERATOR_DISK,
|
||||
initial: {
|
||||
length: 14,
|
||||
ambiguous: true,
|
||||
uppercase: true,
|
||||
minUppercase: 1,
|
||||
lowercase: true,
|
||||
minLowercase: 1,
|
||||
number: true,
|
||||
minNumber: 1,
|
||||
special: false,
|
||||
minSpecial: 0,
|
||||
},
|
||||
options: {
|
||||
deserializer(value) {
|
||||
return value;
|
||||
[Purpose.account]: {
|
||||
storage: {
|
||||
key: "passwordGeneratorSettings",
|
||||
target: "object",
|
||||
format: "plain",
|
||||
classifier: new PublicClassifier<PasswordGenerationOptions>([
|
||||
"length",
|
||||
"ambiguous",
|
||||
"uppercase",
|
||||
"minUppercase",
|
||||
"lowercase",
|
||||
"minLowercase",
|
||||
"number",
|
||||
"minNumber",
|
||||
"special",
|
||||
"minSpecial",
|
||||
]),
|
||||
state: GENERATOR_DISK,
|
||||
initial: {
|
||||
length: 14,
|
||||
ambiguous: true,
|
||||
uppercase: true,
|
||||
minUppercase: 1,
|
||||
lowercase: true,
|
||||
minLowercase: 1,
|
||||
number: true,
|
||||
minNumber: 1,
|
||||
special: false,
|
||||
minSpecial: 0,
|
||||
},
|
||||
options: {
|
||||
deserializer(value) {
|
||||
return value;
|
||||
},
|
||||
clearOn: ["logout"],
|
||||
},
|
||||
clearOn: ["logout"],
|
||||
},
|
||||
} satisfies ObjectKey<PasswordGenerationOptions>,
|
||||
policy: {
|
||||
type: PolicyType.PasswordGenerator,
|
||||
disabledValue: {
|
||||
minLength: 0,
|
||||
useUppercase: false,
|
||||
useLowercase: false,
|
||||
useNumbers: false,
|
||||
numberCount: 0,
|
||||
useSpecial: false,
|
||||
specialCount: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
policy: {
|
||||
type: PolicyType.PasswordGenerator,
|
||||
disabledValue: {
|
||||
minLength: 0,
|
||||
useUppercase: false,
|
||||
useLowercase: false,
|
||||
useNumbers: false,
|
||||
numberCount: 0,
|
||||
useSpecial: false,
|
||||
specialCount: 0,
|
||||
},
|
||||
combine: passwordLeastPrivilege,
|
||||
createEvaluator(policy) {
|
||||
return new PasswordGeneratorOptionsEvaluator(policy);
|
||||
},
|
||||
toConstraints(policy) {
|
||||
return new DynamicPasswordPolicyConstraints(policy, password.settings.constraints);
|
||||
return new DynamicPasswordPolicyConstraints(policy, password.options.constraints);
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,25 +1,28 @@
|
||||
import { IntegrationId } from "@bitwarden/common/tools/integration";
|
||||
|
||||
import { CategorizedAlgorithm, Category } from "./data";
|
||||
import { AlgorithmsByType, Purpose, Type } from "./data";
|
||||
|
||||
/** A collection of credentials that all fulfill a specific purpose. */
|
||||
export type CredentialCategory = keyof typeof Category;
|
||||
/** categorizes credentials according to their use-case outside of Bitwarden */
|
||||
export type CredentialType = keyof typeof Type;
|
||||
|
||||
/** categorizes credentials according to their expected use-case within Bitwarden */
|
||||
export type CredentialPurpose = keyof typeof Purpose;
|
||||
|
||||
/** A type of password that may be generated by the credential generator. */
|
||||
export type PasswordAlgorithm = (typeof CategorizedAlgorithm.password)[number];
|
||||
export type PasswordAlgorithm = (typeof AlgorithmsByType.password)[number];
|
||||
|
||||
/** A type of username that may be generated by the credential generator. */
|
||||
export type UsernameAlgorithm = (typeof CategorizedAlgorithm.username)[number];
|
||||
export type UsernameAlgorithm = (typeof AlgorithmsByType.username)[number];
|
||||
|
||||
/** A type of email address that may be generated by the credential generator. */
|
||||
export type EmailAlgorithm = (typeof CategorizedAlgorithm.email)[number] | ForwarderIntegration;
|
||||
export type EmailAlgorithm = (typeof AlgorithmsByType.email)[number] | ForwarderIntegration;
|
||||
|
||||
/** Identifies a forwarding service */
|
||||
export type ForwarderIntegration = { forwarder: IntegrationId };
|
||||
|
||||
/** A type of credential that may be generated by the credential generator. */
|
||||
// this is defined in terms of `CategorizedAlgorithm` to typecheck the keys of
|
||||
// `CredentialCategory` against the keys of `Category`.
|
||||
/** A type of credential that can be generated by the credential generator. */
|
||||
// this is defined in terms of `AlgorithmsByType` to typecheck the keys of
|
||||
// `AlgorithmsByType` against the keys of `CredentialType`.
|
||||
export type CredentialAlgorithm =
|
||||
| (typeof CategorizedAlgorithm)[CredentialCategory][number]
|
||||
| (typeof AlgorithmsByType)[CredentialType][number]
|
||||
| ForwarderIntegration;
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
import { PolicyType } from "@bitwarden/common/admin-console/enums";
|
||||
import { Policy } from "@bitwarden/common/admin-console/models/domain/policy";
|
||||
import { GENERATOR_DISK } from "@bitwarden/common/platform/state";
|
||||
import { PublicClassifier } from "@bitwarden/common/tools/public-classifier";
|
||||
import { IdentityConstraint } from "@bitwarden/common/tools/state/identity-state-constraint";
|
||||
|
||||
import { UsernameRandomizer } from "../../engine";
|
||||
import {
|
||||
CredentialGenerator,
|
||||
EffUsernameGenerationOptions,
|
||||
GeneratorDependencyProvider,
|
||||
NoPolicy,
|
||||
} from "../../types";
|
||||
import { deepFreeze } from "../../util";
|
||||
import { Algorithm, Purpose, Type } from "../data";
|
||||
import { GeneratorMetadata } from "../generator-metadata";
|
||||
|
||||
const effWordList: GeneratorMetadata<EffUsernameGenerationOptions, NoPolicy> = deepFreeze({
|
||||
id: Algorithm.username,
|
||||
category: Type.username,
|
||||
i18nKeys: {
|
||||
name: "randomWord",
|
||||
generateCredential: "generateUsername",
|
||||
credentialGenerated: "username",
|
||||
copyCredential: "copyUsername",
|
||||
},
|
||||
capabilities: {
|
||||
autogenerate: true,
|
||||
fields: [],
|
||||
},
|
||||
engine: {
|
||||
create(
|
||||
dependencies: GeneratorDependencyProvider,
|
||||
): CredentialGenerator<EffUsernameGenerationOptions> {
|
||||
return new UsernameRandomizer(dependencies.randomizer);
|
||||
},
|
||||
},
|
||||
options: {
|
||||
constraints: {},
|
||||
[Purpose.account]: {
|
||||
storage: {
|
||||
key: "effUsernameGeneratorSettings",
|
||||
target: "object",
|
||||
format: "plain",
|
||||
classifier: new PublicClassifier<EffUsernameGenerationOptions>([
|
||||
"wordCapitalize",
|
||||
"wordIncludeNumber",
|
||||
]),
|
||||
state: GENERATOR_DISK,
|
||||
initial: {
|
||||
wordCapitalize: false,
|
||||
wordIncludeNumber: false,
|
||||
website: null,
|
||||
},
|
||||
options: {
|
||||
deserializer: (value) => value,
|
||||
clearOn: ["logout"],
|
||||
},
|
||||
},
|
||||
policy: {
|
||||
type: PolicyType.PasswordGenerator,
|
||||
disabledValue: {},
|
||||
},
|
||||
},
|
||||
},
|
||||
policy: {
|
||||
combine(_acc: NoPolicy, _policy: Policy) {
|
||||
return {};
|
||||
},
|
||||
toConstraints(_policy: NoPolicy) {
|
||||
return new IdentityConstraint<EffUsernameGenerationOptions>();
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export default effWordList;
|
||||
|
||||
@@ -1,4 +1,10 @@
|
||||
import { CategorizedAlgorithm } from "./data";
|
||||
import {
|
||||
IntegrationId,
|
||||
IntegrationIds,
|
||||
IntegrationMetadata,
|
||||
} from "@bitwarden/common/tools/integration";
|
||||
|
||||
import { AlgorithmsByType } from "./data";
|
||||
import {
|
||||
CredentialAlgorithm,
|
||||
EmailAlgorithm,
|
||||
@@ -11,14 +17,14 @@ import {
|
||||
export function isPasswordAlgorithm(
|
||||
algorithm: CredentialAlgorithm,
|
||||
): algorithm is PasswordAlgorithm {
|
||||
return CategorizedAlgorithm.password.includes(algorithm as any);
|
||||
return AlgorithmsByType.password.includes(algorithm as any);
|
||||
}
|
||||
|
||||
/** Returns true when the input algorithm is a username algorithm. */
|
||||
export function isUsernameAlgorithm(
|
||||
algorithm: CredentialAlgorithm,
|
||||
): algorithm is UsernameAlgorithm {
|
||||
return CategorizedAlgorithm.username.includes(algorithm as any);
|
||||
return AlgorithmsByType.username.includes(algorithm as any);
|
||||
}
|
||||
|
||||
/** Returns true when the input algorithm is a forwarder integration. */
|
||||
@@ -30,7 +36,7 @@ export function isForwarderIntegration(
|
||||
|
||||
/** Returns true when the input algorithm is an email algorithm. */
|
||||
export function isEmailAlgorithm(algorithm: CredentialAlgorithm): algorithm is EmailAlgorithm {
|
||||
return CategorizedAlgorithm.email.includes(algorithm as any) || isForwarderIntegration(algorithm);
|
||||
return AlgorithmsByType.email.includes(algorithm as any) || isForwarderIntegration(algorithm);
|
||||
}
|
||||
|
||||
export function isSameAlgorithm(lhs: CredentialAlgorithm, rhs: CredentialAlgorithm) {
|
||||
@@ -42,3 +48,28 @@ export function isSameAlgorithm(lhs: CredentialAlgorithm, rhs: CredentialAlgorit
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export function toForwarderIntegration(value: IntegrationMetadata): ForwarderIntegration;
|
||||
export function toForwarderIntegration(value: IntegrationId): ForwarderIntegration;
|
||||
export function toForwarderIntegration(
|
||||
value: IntegrationId | IntegrationMetadata,
|
||||
): ForwarderIntegration {
|
||||
if (value == null) {
|
||||
throw new Error("`value` cannot be `null` or `undefined`");
|
||||
}
|
||||
|
||||
let possibleId = undefined;
|
||||
if (typeof value === "string") {
|
||||
possibleId = value;
|
||||
} else if (typeof value === "object" && "id" in value) {
|
||||
possibleId = typeof value.id === "string" ? value.id : undefined;
|
||||
} else {
|
||||
throw new Error("Invalid `value` received.");
|
||||
}
|
||||
|
||||
if (possibleId && IntegrationIds.includes(possibleId)) {
|
||||
return { forwarder: possibleId } satisfies ForwarderIntegration;
|
||||
} else {
|
||||
throw new Error("Invalid `value` received.");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user