[PM-2311] Allow empty passphrase separator (#5473)
* Change passphrase generator's default wordSeparator to the empty string '' * Create DefaultPassphraseGenerationOptions * Use DefaultPassphraseGenerationOptions.wordSeparator in passphrase generation * Add `empty` separator option to passphrase generator CLI and an example * Change DefaultPassphraseGenerationOptions numWords to 3 * Use `DefaultPassphraseGenerationOptions.numWords` in CLI passphrase gen
This commit is contained in:
parent
783ae104a3
commit
bb031f6779
|
@ -322,6 +322,7 @@ export class Program {
|
|||
writeLn(" bw generate -ul");
|
||||
writeLn(" bw generate -p --separator _");
|
||||
writeLn(" bw generate -p --words 5 --separator space");
|
||||
writeLn(" bw generate -p --words 5 --separator empty");
|
||||
writeLn("", true);
|
||||
})
|
||||
.action(async (options) => {
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
|
||||
import { DefaultPassphraseGenerationOptions } from "@bitwarden/common/tools/generator/passphrase";
|
||||
import { PasswordGenerationServiceAbstraction } from "@bitwarden/common/tools/generator/password";
|
||||
import { PasswordGeneratorOptions } from "@bitwarden/common/tools/generator/password/password-generator-options";
|
||||
|
||||
|
@ -65,8 +66,14 @@ class Options {
|
|||
this.ambiguous = CliUtils.convertBooleanOption(passedOptions?.ambiguous);
|
||||
this.length = CliUtils.convertNumberOption(passedOptions?.length, 14);
|
||||
this.type = passedOptions?.passphrase ? "passphrase" : "password";
|
||||
this.separator = CliUtils.convertStringOption(passedOptions?.separator, "-");
|
||||
this.words = CliUtils.convertNumberOption(passedOptions?.words, 3);
|
||||
this.separator = CliUtils.convertStringOption(
|
||||
passedOptions?.separator,
|
||||
DefaultPassphraseGenerationOptions.wordSeparator,
|
||||
);
|
||||
this.words = CliUtils.convertNumberOption(
|
||||
passedOptions?.words,
|
||||
DefaultPassphraseGenerationOptions.numWords,
|
||||
);
|
||||
this.minNumber = CliUtils.convertNumberOption(passedOptions?.minNumber, 1);
|
||||
this.minSpecial = CliUtils.convertNumberOption(passedOptions?.minSpecial, 1);
|
||||
|
||||
|
@ -83,6 +90,8 @@ class Options {
|
|||
}
|
||||
if (this.separator === "space") {
|
||||
this.separator = " ";
|
||||
} else if (this.separator === "empty") {
|
||||
this.separator = "";
|
||||
} else if (this.separator != null && this.separator.length > 1) {
|
||||
this.separator = this.separator[0];
|
||||
}
|
||||
|
|
|
@ -2,3 +2,4 @@
|
|||
export { PassphraseGeneratorOptionsEvaluator } from "./passphrase-generator-options-evaluator";
|
||||
export { PassphraseGeneratorPolicy } from "./passphrase-generator-policy";
|
||||
export { PassphraseGeneratorStrategy } from "./passphrase-generator-strategy";
|
||||
export { DefaultPassphraseGenerationOptions } from "./passphrase-generation-options";
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
*/
|
||||
export type PassphraseGenerationOptions = {
|
||||
/** The number of words to include in the passphrase.
|
||||
* This value defaults to 4.
|
||||
* This value defaults to 3.
|
||||
*/
|
||||
numWords?: number;
|
||||
|
||||
|
@ -24,3 +24,12 @@ export type PassphraseGenerationOptions = {
|
|||
*/
|
||||
includeNumber?: boolean;
|
||||
};
|
||||
|
||||
/** The default options for passphrase generation. */
|
||||
export const DefaultPassphraseGenerationOptions: Partial<PassphraseGenerationOptions> =
|
||||
Object.freeze({
|
||||
numWords: 3,
|
||||
wordSeparator: "-",
|
||||
capitalize: false,
|
||||
includeNumber: false,
|
||||
});
|
||||
|
|
|
@ -239,6 +239,16 @@ describe("Password generator options builder", () => {
|
|||
expect(sanitizedOptions.wordSeparator).toEqual("-");
|
||||
});
|
||||
|
||||
it("should leave `wordSeparator` as the empty string '' when it is the empty string", () => {
|
||||
const policy = Object.assign({}, DisabledPassphraseGeneratorPolicy);
|
||||
const builder = new PassphraseGeneratorOptionsEvaluator(policy);
|
||||
const options = Object.freeze({ wordSeparator: "" });
|
||||
|
||||
const sanitizedOptions = builder.sanitize(options);
|
||||
|
||||
expect(sanitizedOptions.wordSeparator).toEqual("");
|
||||
});
|
||||
|
||||
it("should preserve unknown properties", () => {
|
||||
const policy = Object.assign({}, DisabledPassphraseGeneratorPolicy);
|
||||
const builder = new PassphraseGeneratorOptionsEvaluator(policy);
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
import { PolicyEvaluator } from "../abstractions/policy-evaluator.abstraction";
|
||||
|
||||
import { PassphraseGenerationOptions } from "./passphrase-generation-options";
|
||||
import {
|
||||
DefaultPassphraseGenerationOptions,
|
||||
PassphraseGenerationOptions,
|
||||
} from "./passphrase-generation-options";
|
||||
import { PassphraseGeneratorPolicy } from "./passphrase-generator-policy";
|
||||
|
||||
type Boundary = {
|
||||
|
@ -108,8 +111,11 @@ export class PassphraseGeneratorOptionsEvaluator
|
|||
* @returns A passphrase generation request with cascade applied.
|
||||
*/
|
||||
sanitize(options: PassphraseGenerationOptions): PassphraseGenerationOptions {
|
||||
// ensure words are separated by a single character
|
||||
const wordSeparator = options.wordSeparator?.[0] ?? "-";
|
||||
// ensure words are separated by a single character or the empty string
|
||||
const wordSeparator =
|
||||
options.wordSeparator === ""
|
||||
? ""
|
||||
: options.wordSeparator?.[0] ?? DefaultPassphraseGenerationOptions.wordSeparator;
|
||||
|
||||
return {
|
||||
...options,
|
||||
|
|
|
@ -147,9 +147,6 @@ export class PasswordGenerationService implements PasswordGenerationServiceAbstr
|
|||
if (o.numWords == null || o.numWords <= 2) {
|
||||
o.numWords = DefaultOptions.numWords;
|
||||
}
|
||||
if (o.wordSeparator == null || o.wordSeparator.length === 0 || o.wordSeparator.length > 1) {
|
||||
o.wordSeparator = " ";
|
||||
}
|
||||
if (o.capitalize == null) {
|
||||
o.capitalize = false;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue