mirror of
https://github.com/bitwarden/browser
synced 2026-02-10 05:30:01 +00:00
Add new metadata, feature flag, and engine for sdk generator
This commit is contained in:
@@ -27,6 +27,7 @@ export enum FeatureFlag {
|
||||
EnableRiskInsightsNotifications = "enable-risk-insights-notifications",
|
||||
DesktopSendUIRefresh = "desktop-send-ui-refresh",
|
||||
ExportAttachments = "export-attachments",
|
||||
SDKGenerators = "sdk-generators",
|
||||
|
||||
/* Vault */
|
||||
PM9111ExtensionPersistAddEditForm = "pm-9111-extension-persist-add-edit-form",
|
||||
@@ -83,6 +84,7 @@ export const DefaultFeatureFlagValue = {
|
||||
[FeatureFlag.EnableRiskInsightsNotifications]: FALSE,
|
||||
[FeatureFlag.DesktopSendUIRefresh]: FALSE,
|
||||
[FeatureFlag.ExportAttachments]: FALSE,
|
||||
[FeatureFlag.SDKGenerators]: FALSE,
|
||||
|
||||
/* Vault */
|
||||
[FeatureFlag.PM9111ExtensionPersistAddEditForm]: FALSE,
|
||||
|
||||
@@ -5,4 +5,5 @@ export * from "./settings";
|
||||
export { EmailRandomizer } from "./email-randomizer";
|
||||
export { EmailCalculator } from "./email-calculator";
|
||||
export { PasswordRandomizer } from "./password-randomizer";
|
||||
export { SDKPasswordRandomizer } from "./sdk-password-randomizer";
|
||||
export { UsernameRandomizer } from "./username-randomizer";
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
import {
|
||||
CredentialGenerator,
|
||||
GenerateRequest,
|
||||
GeneratedCredential,
|
||||
PassphraseGenerationOptions,
|
||||
PasswordGenerationOptions,
|
||||
} from "../types";
|
||||
import { optionsToEffWordListRequest, optionsToRandomAsciiRequest } from "../util";
|
||||
|
||||
import { EffWordListRequest, RandomAsciiRequest } from "./types";
|
||||
|
||||
/** Generation algorithms that produce randomized secrets */
|
||||
export class SDKPasswordRandomizer
|
||||
implements
|
||||
CredentialGenerator<PassphraseGenerationOptions>,
|
||||
CredentialGenerator<PasswordGenerationOptions>
|
||||
{
|
||||
/** Instantiates the password randomizer
|
||||
*/
|
||||
constructor() {}
|
||||
|
||||
/** create a password from ASCII codepoints
|
||||
* @param request refines the generated password
|
||||
* @returns a promise that completes with the generated password
|
||||
*/
|
||||
async randomSDKAscii(request: RandomAsciiRequest) {
|
||||
// TODO
|
||||
return "";
|
||||
}
|
||||
|
||||
/** create a passphrase from the EFF's "5 dice" word list
|
||||
* @param request refines the generated passphrase
|
||||
* @returns a promise that completes with the generated passphrase
|
||||
*/
|
||||
async randomSDKEffLongWords(request: EffWordListRequest) {
|
||||
// TODO
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
generate(
|
||||
request: GenerateRequest,
|
||||
settings: PasswordGenerationOptions,
|
||||
): Promise<GeneratedCredential>;
|
||||
generate(
|
||||
request: GenerateRequest,
|
||||
settings: PassphraseGenerationOptions,
|
||||
): Promise<GeneratedCredential>;
|
||||
async generate(
|
||||
request: GenerateRequest,
|
||||
settings: PasswordGenerationOptions | PassphraseGenerationOptions,
|
||||
) {
|
||||
if (isPasswordGenerationOptions(settings)) {
|
||||
const req = optionsToRandomAsciiRequest(settings);
|
||||
const password = await this.randomSDKAscii(req);
|
||||
|
||||
return new GeneratedCredential(
|
||||
password,
|
||||
"password",
|
||||
Date.now(),
|
||||
request.source,
|
||||
request.website,
|
||||
);
|
||||
} else if (isPassphraseGenerationOptions(settings)) {
|
||||
const req = optionsToEffWordListRequest(settings);
|
||||
const passphrase = await this.randomSDKEffLongWords(req);
|
||||
|
||||
return new GeneratedCredential(
|
||||
passphrase,
|
||||
"passphrase",
|
||||
Date.now(),
|
||||
request.source,
|
||||
request.website,
|
||||
);
|
||||
}
|
||||
|
||||
throw new Error("Invalid settings received by generator.");
|
||||
}
|
||||
}
|
||||
|
||||
function isPasswordGenerationOptions(settings: any): settings is PasswordGenerationOptions {
|
||||
return "length" in (settings ?? {});
|
||||
}
|
||||
|
||||
function isPassphraseGenerationOptions(settings: any): settings is PassphraseGenerationOptions {
|
||||
return "numWords" in (settings ?? {});
|
||||
}
|
||||
@@ -8,6 +8,12 @@ export const Algorithm = Object.freeze({
|
||||
/** A password composed of random words from the EFF word list */
|
||||
passphrase: "passphrase",
|
||||
|
||||
/** A password composed of random characters generated from the SDK */
|
||||
sdkPassword: "sdkpassword",
|
||||
|
||||
/** A password composed of random words from the EFF word list generated from the SDK */
|
||||
sdkPassphrase: "sdkpassphrase",
|
||||
|
||||
/** A username composed of words from the EFF word list */
|
||||
username: "username",
|
||||
|
||||
@@ -38,7 +44,12 @@ export const Profile = Object.freeze({
|
||||
/** Credential generation algorithms grouped by purpose. */
|
||||
export const AlgorithmsByType = deepFreeze({
|
||||
/** Algorithms that produce passwords */
|
||||
[Type.password]: [Algorithm.password, Algorithm.passphrase] as const,
|
||||
[Type.password]: [
|
||||
Algorithm.password,
|
||||
Algorithm.passphrase,
|
||||
Algorithm.sdkPassword,
|
||||
Algorithm.sdkPassphrase,
|
||||
] as const,
|
||||
|
||||
/** Algorithms that produce usernames */
|
||||
[Type.username]: [Algorithm.username] as const,
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
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 { SDKPasswordRandomizer } from "../../engine";
|
||||
import { passphraseLeastPrivilege, PassphrasePolicyConstraints } from "../../policies";
|
||||
import {
|
||||
CredentialGenerator,
|
||||
GeneratorDependencyProvider,
|
||||
PassphraseGenerationOptions,
|
||||
} from "../../types";
|
||||
import { Algorithm, Profile, Type } from "../data";
|
||||
import { GeneratorMetadata } from "../generator-metadata";
|
||||
|
||||
const sdkPassphrase: GeneratorMetadata<PassphraseGenerationOptions> = {
|
||||
id: Algorithm.sdkPassphrase,
|
||||
category: Type.password,
|
||||
weight: 130,
|
||||
i18nKeys: {
|
||||
name: "passphrase",
|
||||
credentialType: "passphrase",
|
||||
generateCredential: "generatePassphrase",
|
||||
credentialGenerated: "passphraseGenerated",
|
||||
copyCredential: "copyPassphrase",
|
||||
useCredential: "useThisPassphrase",
|
||||
},
|
||||
capabilities: {
|
||||
autogenerate: false,
|
||||
fields: [],
|
||||
},
|
||||
engine: {
|
||||
create(
|
||||
dependencies: GeneratorDependencyProvider,
|
||||
): CredentialGenerator<PassphraseGenerationOptions> {
|
||||
return new SDKPasswordRandomizer();
|
||||
},
|
||||
},
|
||||
profiles: {
|
||||
[Profile.account]: {
|
||||
type: "core",
|
||||
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>,
|
||||
constraints: {
|
||||
type: PolicyType.PasswordGenerator,
|
||||
default: {
|
||||
wordSeparator: { maxLength: 1 },
|
||||
numWords: {
|
||||
min: 3,
|
||||
max: 20,
|
||||
recommendation: 6,
|
||||
},
|
||||
},
|
||||
create(policies, context) {
|
||||
const initial = {
|
||||
minNumberWords: 0,
|
||||
capitalize: false,
|
||||
includeNumber: false,
|
||||
};
|
||||
const policy = policies.reduce(passphraseLeastPrivilege, initial);
|
||||
const constraints = new PassphrasePolicyConstraints(policy, context.defaultConstraints);
|
||||
return constraints;
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export default sdkPassphrase;
|
||||
@@ -0,0 +1,117 @@
|
||||
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 { deepFreeze } from "@bitwarden/common/tools/util";
|
||||
|
||||
import { SDKPasswordRandomizer } from "../../engine";
|
||||
import { DynamicPasswordPolicyConstraints, passwordLeastPrivilege } from "../../policies";
|
||||
import {
|
||||
CredentialGenerator,
|
||||
GeneratorDependencyProvider,
|
||||
PasswordGeneratorSettings,
|
||||
} from "../../types";
|
||||
import { Algorithm, Profile, Type } from "../data";
|
||||
import { GeneratorMetadata } from "../generator-metadata";
|
||||
|
||||
const sdkPassword: GeneratorMetadata<PasswordGeneratorSettings> = deepFreeze({
|
||||
id: Algorithm.sdkPassphrase,
|
||||
category: Type.password,
|
||||
weight: 120,
|
||||
i18nKeys: {
|
||||
name: "password",
|
||||
generateCredential: "generatePassword",
|
||||
credentialGenerated: "passwordGenerated",
|
||||
credentialType: "password",
|
||||
copyCredential: "copyPassword",
|
||||
useCredential: "useThisPassword",
|
||||
},
|
||||
capabilities: {
|
||||
autogenerate: true,
|
||||
fields: [],
|
||||
},
|
||||
engine: {
|
||||
create(
|
||||
dependencies: GeneratorDependencyProvider,
|
||||
): CredentialGenerator<PasswordGeneratorSettings> {
|
||||
return new SDKPasswordRandomizer();
|
||||
},
|
||||
},
|
||||
profiles: {
|
||||
[Profile.account]: {
|
||||
type: "core",
|
||||
storage: {
|
||||
key: "passwordGeneratorSettings",
|
||||
target: "object",
|
||||
format: "plain",
|
||||
classifier: new PublicClassifier<PasswordGeneratorSettings>([
|
||||
"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"],
|
||||
},
|
||||
},
|
||||
constraints: {
|
||||
type: PolicyType.PasswordGenerator,
|
||||
default: {
|
||||
length: {
|
||||
min: 5,
|
||||
max: 128,
|
||||
recommendation: 14,
|
||||
},
|
||||
minNumber: {
|
||||
min: 0,
|
||||
max: 9,
|
||||
},
|
||||
minSpecial: {
|
||||
min: 0,
|
||||
max: 9,
|
||||
},
|
||||
},
|
||||
create(policies, context) {
|
||||
const initial = {
|
||||
minLength: 0,
|
||||
useUppercase: false,
|
||||
useLowercase: false,
|
||||
useNumbers: false,
|
||||
numberCount: 0,
|
||||
useSpecial: false,
|
||||
specialCount: 0,
|
||||
};
|
||||
const policy = policies.reduce(passwordLeastPrivilege, initial);
|
||||
const constraints = new DynamicPasswordPolicyConstraints(
|
||||
policy,
|
||||
context.defaultConstraints,
|
||||
);
|
||||
return constraints;
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export default sdkPassword;
|
||||
Reference in New Issue
Block a user