mirror of
https://github.com/yuto-yuto/node-red-contrib-password-generator.git
synced 2024-11-21 19:06:44 +01:00
Add options
This commit is contained in:
parent
456f4332e3
commit
4af3cb4be1
@ -7,14 +7,35 @@ const AsciiRange = {
|
|||||||
/** childe mark ~ */
|
/** childe mark ~ */
|
||||||
max: 126,
|
max: 126,
|
||||||
}
|
}
|
||||||
|
const optRegexes = {
|
||||||
|
space: " ",
|
||||||
|
special: "!\"#\\$%&'\\(\\)\\*\\+,-./:;<=>\\?@\\[\\\\\\]\\^_`\\{|\\}~",
|
||||||
|
number: "\\d",
|
||||||
|
};
|
||||||
|
|
||||||
|
export interface DisableOptions {
|
||||||
|
space?: boolean;
|
||||||
|
special?: boolean;
|
||||||
|
number?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function generatePassword(length: number, disableOpts?: DisableOptions): Promise<string> {
|
||||||
|
if (length < 5) {
|
||||||
|
throw new Error("Password length must be longer than 5.");
|
||||||
|
}
|
||||||
|
|
||||||
export async function generatePassword(length: number): Promise<string> {
|
|
||||||
let result = "";
|
let result = "";
|
||||||
|
const regex = createRegex(disableOpts)
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
const bytes = await promisify(crypto.randomBytes)(length * 2);
|
const bytes = await promisify(crypto.randomBytes)(length * 2);
|
||||||
const byteArray = Array.from(bytes);
|
const byteArray = Array.from(bytes);
|
||||||
const filtered = byteArray.filter(isInAsciiRange);
|
const filtered = byteArray.filter(isInAsciiRange);
|
||||||
result += String.fromCharCode(...filtered);
|
result += String.fromCharCode(...filtered);
|
||||||
|
if (regex) {
|
||||||
|
result = result.replace(regex, "");
|
||||||
|
}
|
||||||
|
|
||||||
if (result.length >= length) {
|
if (result.length >= length) {
|
||||||
result = result.slice(0, length);
|
result = result.slice(0, length);
|
||||||
break;
|
break;
|
||||||
@ -25,4 +46,16 @@ export async function generatePassword(length: number): Promise<string> {
|
|||||||
|
|
||||||
function isInAsciiRange(value: number) {
|
function isInAsciiRange(value: number) {
|
||||||
return AsciiRange.min <= value && value <= AsciiRange.max;
|
return AsciiRange.min <= value && value <= AsciiRange.max;
|
||||||
|
}
|
||||||
|
|
||||||
|
function createRegex(disableOpts?: DisableOptions): RegExp | undefined {
|
||||||
|
if (!disableOpts) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
let reg = "";
|
||||||
|
if (disableOpts?.space) { reg += optRegexes.space };
|
||||||
|
if (disableOpts?.special) { reg += optRegexes.special };
|
||||||
|
if (disableOpts?.number) { reg += optRegexes.number };
|
||||||
|
|
||||||
|
return new RegExp(`[${reg}]`, "g");
|
||||||
}
|
}
|
@ -5,8 +5,11 @@
|
|||||||
color: "#D8BFD8",
|
color: "#D8BFD8",
|
||||||
defaults: {
|
defaults: {
|
||||||
name: { value: "" },
|
name: { value: "" },
|
||||||
length: { value: "", required: true, validate: RED.validators.number() },
|
length: { value: "", required: true, validate: (v) => v > 4 },
|
||||||
setTo: { value: "" },
|
setTo: { value: "" },
|
||||||
|
isNumberDisabled: { value: false, require: true },
|
||||||
|
isSpecialCharsDisabled: { value: false, require: true },
|
||||||
|
isSpaceDisabled: { value: false, require: true },
|
||||||
},
|
},
|
||||||
inputs: 1,
|
inputs: 1,
|
||||||
outputs: 1,
|
outputs: 1,
|
||||||
@ -26,9 +29,21 @@
|
|||||||
<input type="text" id="node-input-length">
|
<input type="text" id="node-input-length">
|
||||||
</div>
|
</div>
|
||||||
<div class="form-row">
|
<div class="form-row">
|
||||||
<label for="node-input-setTo"><i class="fa fa-ellipsis-he"></i> to</label>
|
<label for="node-input-setTo"><i class="fa fa-ellipsis-h"></i> to</label>
|
||||||
<input type="text" id="node-input-setTo">
|
<input type="text" id="node-input-setTo">
|
||||||
</div>
|
</div>
|
||||||
|
<div class="form-row">
|
||||||
|
<label for="node-input-isNumberDisabled"> Disable Number</label>
|
||||||
|
<input type="checkbox" id="node-input-isNumberDisabled">
|
||||||
|
</div>
|
||||||
|
<div class="form-row">
|
||||||
|
<label for="node-input-isSpecialCharsDisabled"> Disable Special chars</label>
|
||||||
|
<input type="checkbox" id="node-input-isSpecialCharsDisabled">
|
||||||
|
</div>
|
||||||
|
<div class="form-row">
|
||||||
|
<label for="node-input-isSpaceDisabled"> Disable Space</label>
|
||||||
|
<input type="checkbox" id="node-input-isSpaceDisabled">
|
||||||
|
</div>
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script type="text/html" data-help-name="password-generator">
|
<script type="text/html" data-help-name="password-generator">
|
||||||
@ -40,5 +55,7 @@
|
|||||||
<dd> Password length. </dd>
|
<dd> Password length. </dd>
|
||||||
<dt>to</dt>
|
<dt>to</dt>
|
||||||
<dd> The property to set generated password. </dd>
|
<dd> The property to set generated password. </dd>
|
||||||
|
<dt>Disable options</dt>
|
||||||
|
<dd> Excludes the characters from the generated password. </dd>
|
||||||
</dl>
|
</dl>
|
||||||
</script>
|
</script>
|
@ -1,22 +1,60 @@
|
|||||||
import "mocha";
|
import "mocha";
|
||||||
import * as crypto from "crypto";
|
|
||||||
import { expect } from "chai";
|
import { expect } from "chai";
|
||||||
import * as sinon from "sinon";
|
import * as sinon from "sinon";
|
||||||
import { generatePassword } from "../lib/PasswordGenerator";
|
import { generatePassword, DisableOptions } from "../lib/PasswordGenerator";
|
||||||
|
|
||||||
describe("PasswordGenerator", () => {
|
describe("PasswordGenerator", () => {
|
||||||
it("should return true for the first time", async () => {
|
afterEach(() => {
|
||||||
const result = await generatePassword(10);
|
sinon.restore();
|
||||||
expect(result).to.lengthOf(10);
|
});
|
||||||
|
|
||||||
|
it("should throw an error when length is 4", (done) => {
|
||||||
|
generatePassword(4)
|
||||||
|
.then(() => done("Error didn't occur."))
|
||||||
|
.catch(() => done());
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return password when length is 5", async () => {
|
||||||
|
const result = await generatePassword(5);
|
||||||
|
expect(result).to.lengthOf(5);
|
||||||
|
});
|
||||||
|
|
||||||
|
[
|
||||||
|
{ regex: /0123456789/, title: "number" },
|
||||||
|
{ regex: / /, title: "space" },
|
||||||
|
{ regex: /abcdefghijklmnopqrstuvwxyz/, title: "lowercase" },
|
||||||
|
{ regex: /ABCDEFGHIJKLMNOPQRSTUVWXYZ/, title: "uppercase" },
|
||||||
|
{ regex: /!\"#\$%&'\(\)\*\+,-.\//, title: "special characters group 1" },
|
||||||
|
{ regex: /:;<=>\?@/, title: "special characters group 2" },
|
||||||
|
{ regex: /\[\\\]\^_`/, title: "special characters group 3" },
|
||||||
|
{ regex: /\{|\}~/, title: "special characters group 4" },
|
||||||
|
].forEach((testData) => {
|
||||||
|
it(`should contain ${testData.title} without option`, async () => {
|
||||||
|
const fakeData = Array.from(Array(256).keys());
|
||||||
|
sinon.stub(Array, "from").returns(fakeData);
|
||||||
|
const result = await generatePassword(256);
|
||||||
|
expect(result).to.match(testData.regex);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should not contain number when option number is true", async () => {
|
||||||
|
const fakeData = Array.from(Array(256).keys());
|
||||||
|
sinon.stub(Array, "from").returns(fakeData);
|
||||||
|
const result = await generatePassword(256, { number: true });
|
||||||
|
expect(result).not.to.match(/\d/);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should not contain space when option space is true", async () => {
|
||||||
|
const fakeData = Array.from(Array(256).keys());
|
||||||
|
sinon.stub(Array, "from").returns(fakeData);
|
||||||
|
const result = await generatePassword(256, { space: true });
|
||||||
|
expect(result).not.to.contain(" ");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should not contain special characters when option space is true", async () => {
|
||||||
|
const fakeData = Array.from(Array(256).keys());
|
||||||
|
sinon.stub(Array, "from").returns(fakeData);
|
||||||
|
const result = await generatePassword(256, { special: true });
|
||||||
|
expect(result).not.to.match(/[\[!\"#\$%&'\(\)\*\+,\-./:;<=>\?@\[\\\]\^_`{|}\]]/);
|
||||||
});
|
});
|
||||||
// it.only("should return true for the first time", async () => {
|
|
||||||
// const testData = Buffer.from([31, 32, 126, 127]);
|
|
||||||
// sinon.stub(crypto, "randomBytes")
|
|
||||||
// .callsFake((size: number, cb: (err: Error | null, buf: Buffer) => void) => {
|
|
||||||
// console.log("HEY")
|
|
||||||
// cb(null, testData);
|
|
||||||
// });
|
|
||||||
// const result = await generatePassword(10);
|
|
||||||
// expect(result).to.equal(" ~");
|
|
||||||
// });
|
|
||||||
});
|
});
|
Loading…
Reference in New Issue
Block a user