1
0
mirror of https://github.com/XFox111/PasswordGeneratorExtension.git synced 2026-04-22 08:08:01 +03:00
Files
PasswordGeneratorExtension/utils/generators/generatePassword.ts
T
xfox111 a4de3139bf Minor 5.1.0 (#560)
* Force no-wrap for special characters tooltip (resolves #551) (#555)

* Increase width of password count field in advanced generator (resolves #548) (#554)

* Added password length limit in advanced generator (#552)

* Added password length limit in advanced generator (resolves #547)

* Add radix to parseInt

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Remove unnecessary callback dependency

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* npm audit fix (#556)

* Added length slider range limits (#557)

* Added length slider range limits (resolves #546)

* Fix validateMaxLimit logic

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Added password length update on input field blur (resolves #549) (#559)

* Separator option for advanced password generator (#558)

* Added separator option for advanced password generator (resolves #545)

* Fix typos

* Fixed merge typo

* Update package.json

* Bump @fluentui/react-components from 9.64.0 to 9.66.5 (#561)

Bumps [@fluentui/react-components](https://github.com/microsoft/fluentui) from 9.64.0 to 9.66.5.
- [Release notes](https://github.com/microsoft/fluentui/releases)
- [Changelog](https://github.com/microsoft/fluentui/blob/master/azure-pipelines.release.yml)
- [Commits](https://github.com/microsoft/fluentui/compare/@fluentui/react-components_v9.64.0...@fluentui/react-components_v9.66.5)

---
updated-dependencies:
- dependency-name: "@fluentui/react-components"
  dependency-version: 9.66.5
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Bump @typescript-eslint/parser from 8.33.1 to 8.35.1 (#562)

Bumps [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser) from 8.33.1 to 8.35.1.
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/parser/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.35.1/packages/parser)

---
updated-dependencies:
- dependency-name: "@typescript-eslint/parser"
  dependency-version: 8.35.1
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Bump @eslint/js from 9.28.0 to 9.30.0 (#564)

Bumps [@eslint/js](https://github.com/eslint/eslint/tree/HEAD/packages/js) from 9.28.0 to 9.30.0.
- [Release notes](https://github.com/eslint/eslint/releases)
- [Changelog](https://github.com/eslint/eslint/blob/main/CHANGELOG.md)
- [Commits](https://github.com/eslint/eslint/commits/v9.30.0/packages/js)

---
updated-dependencies:
- dependency-name: "@eslint/js"
  dependency-version: 9.30.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Bump eslint from 9.28.0 to 9.30.0 (#566)

Bumps [eslint](https://github.com/eslint/eslint) from 9.28.0 to 9.30.0.
- [Release notes](https://github.com/eslint/eslint/releases)
- [Changelog](https://github.com/eslint/eslint/blob/main/CHANGELOG.md)
- [Commits](https://github.com/eslint/eslint/compare/v9.28.0...v9.30.0)

---
updated-dependencies:
- dependency-name: eslint
  dependency-version: 9.30.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Bump @typescript-eslint/eslint-plugin from 8.33.1 to 8.35.1 (#563)

Bumps [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin) from 8.33.1 to 8.35.1.
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.35.1/packages/eslint-plugin)

---
updated-dependencies:
- dependency-name: "@typescript-eslint/eslint-plugin"
  dependency-version: 8.35.1
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Bump @fluentui/react-icons from 2.0.302 to 2.0.305 (#565)

Bumps [@fluentui/react-icons](https://github.com/microsoft/fluentui-system-icons) from 2.0.302 to 2.0.305.
- [Changelog](https://github.com/microsoft/fluentui-system-icons/blob/main/fluentui-android-system-icons-release.yml)
- [Commits](https://github.com/microsoft/fluentui-system-icons/commits)

---
updated-dependencies:
- dependency-name: "@fluentui/react-icons"
  dependency-version: 2.0.305
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Moved default password lengths to constants (#553)

* Added radix 10 for parseInt (#553)

* Removed ts-expect-error microsoft/fluentui#27090

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-01 11:25:32 +03:00

163 lines
4.4 KiB
TypeScript

import { MIN_PASSWORD_LENGTH } from "../constants";
import { pickRandomFromArray, shuffleString } from "./randomUtils";
const Characters =
{
uppercase: "ABCDEFGHIJKLMNOPQRSTUVWXYZ",
lowercase: "abcdefghijklmnopqrstuvwxyz",
numeric: "1234567890",
special: "!#$%&*+-=?@^_"
};
const similar: string = "iIl1Lo0O";
const ambiguous: string = "{}[]()/\\'\"`~,;:.<>";
export const CharacterHints = { ...Characters, similar, ambiguous };
/**
* Generates a random password
* @param options Options for password generation
* @returns Randomly generated password
* @throws Error if options are invalid
*/
export function generatePassword(options: PasswordProps): string
{
validateOptions(options);
let password: string = getRequiredCharacters(options);
const availableCharacters: string = getAvailableCharacters(options);
for (let i = password.length; i < options.length; i++)
{
const character: string = pickRandomFromArray(availableCharacters);
if (options.excludeRepeating && password.includes(character))
i--;
else
password += character;
}
password = shuffleString(password);
if (options.separator && options.separatorInterval)
password = addSeparator(password, options.separator, options.separatorInterval);
return password;
}
/**
* Validates options for password generation
* @param options Options for password generation
* @throws Error if options are invalid
*/
export function validateOptions(options: PasswordProps): void
{
if (options.length < MIN_PASSWORD_LENGTH)
throw new Error(i18n.t("errors.too_short"));
const availableCharacters: string = getAvailableCharacters(options);
if (availableCharacters.length < 1)
throw new Error(i18n.t("errors.no_characters"));
if (options.excludeRepeating && options.length > availableCharacters.length)
throw new Error(i18n.t("errors.too_long"));
}
// Returns a string containing all characters that are available for password generation
function getAvailableCharacters(options: PasswordProps): string
{
let availableCharacters: string = "";
for (const [key, value] of Object.entries(Characters))
if (options[key as keyof PasswordProps])
availableCharacters += value;
if (options.custom && options.customSet.length > 0)
availableCharacters += options.customSet;
if (options.excludeSimilar)
availableCharacters = availableCharacters.replace(new RegExp(`[${similar}]`, "g"), "");
if (options.special && !options.excludeAmbiguous)
availableCharacters += ambiguous;
if (options.excludeCustom.length > 0)
availableCharacters = availableCharacters.replace(new RegExp(`[${options.excludeCustom}]`, "g"), "");
return availableCharacters;
}
// Returns a string containing all characters from every available set that are required for password generation
function getRequiredCharacters(options: PasswordProps): string
{
let result: string = "";
const characters: Record<string, string> = Object.assign({}, Characters);
if (!options.excludeAmbiguous)
characters.special += ambiguous;
if (options.custom && options.customSet.length > 0)
characters.custom = options.customSet;
for (const key of Object.keys(characters))
{
if (options.excludeSimilar)
characters[key] = characters[key].replace(new RegExp(`[${similar}]`, "g"), "");
if (options.excludeCustom.length > 0)
characters[key] = characters[key].replace(new RegExp(`[${options.excludeCustom}]`, "g"), "");
}
for (const [key, value] of Object.entries(characters))
if (options[key as keyof PasswordProps])
for (let i = 0; i < (options[key as keyof PasswordProps] as number); i++)
{
if (value.length < 1)
continue;
const char = pickRandomFromArray(value);
if (options.excludeRepeating && result.includes(char))
i--;
else
result += char;
}
return result;
}
function addSeparator(password: string, separator: string, separatorInterval: number): string
{
if (!separator || separatorInterval < 1)
return password;
const parts: string[] = [];
for (let i = 0; i < password.length; i += separatorInterval)
parts.push(password.slice(i, i + separatorInterval));
return parts.join(separator);
}
export type PasswordProps =
{
length: number;
special: boolean | number;
numeric: boolean | number;
lowercase: boolean | number;
uppercase: boolean | number;
custom: boolean | number;
customSet: string;
excludeSimilar: boolean;
excludeAmbiguous: boolean;
excludeRepeating: boolean;
excludeCustom: string;
separator?: string;
separatorInterval?: number;
};