mirror of
https://github.com/aliasvault/aliasvault.git
synced 2026-05-11 17:03:33 -04:00
Add shared libraries to AliasVault.Client (#896)
This commit is contained in:
committed by
Leendert de Borst
parent
a7ffc33d56
commit
260aec34ce
@@ -14,15 +14,13 @@ using System.Net.Http;
|
||||
using System.Net.Http.Json;
|
||||
using System.Threading.Tasks;
|
||||
using AliasClientDb;
|
||||
using AliasVault.Generators.Identity.Implementations.Factories;
|
||||
using AliasVault.Generators.Identity.Models;
|
||||
using AliasVault.Shared.Models.WebApi.Favicon;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
/// <summary>
|
||||
/// Service class for credential operations.
|
||||
/// </summary>
|
||||
public sealed class CredentialService(HttpClient httpClient, DbService dbService, Config config)
|
||||
public sealed class CredentialService(HttpClient httpClient, DbService dbService, Config config, JsInteropService jsInteropService)
|
||||
{
|
||||
/// <summary>
|
||||
/// The default service URL used as placeholder in forms. When this value is set, the URL field is considered empty
|
||||
@@ -73,15 +71,15 @@ public sealed class CredentialService(HttpClient httpClient, DbService dbService
|
||||
|
||||
do
|
||||
{
|
||||
// Generate a random identity using the IIdentityGenerator implementation
|
||||
var identity = await IdentityGeneratorFactory.CreateIdentityGenerator(dbService.Settings.DefaultIdentityLanguage).GenerateRandomIdentityAsync();
|
||||
// Generate a random identity using the TypeScript library
|
||||
var identity = await jsInteropService.GenerateRandomIdentityAsync(dbService.Settings.DefaultIdentityLanguage);
|
||||
|
||||
// Generate random values for the Identity properties
|
||||
credential.Username = identity.NickName;
|
||||
credential.Alias.FirstName = identity.FirstName;
|
||||
credential.Alias.LastName = identity.LastName;
|
||||
credential.Alias.NickName = identity.NickName;
|
||||
credential.Alias.Gender = identity.Gender == Gender.Male ? "Male" : "Female";
|
||||
credential.Alias.Gender = identity.Gender;
|
||||
credential.Alias.BirthDate = identity.BirthDate;
|
||||
|
||||
// Set the email
|
||||
@@ -105,9 +103,9 @@ public sealed class CredentialService(HttpClient httpClient, DbService dbService
|
||||
}
|
||||
while (isEmailTaken && attempts < MaxAttempts);
|
||||
|
||||
// Generate password
|
||||
// Generate password using the TypeScript library
|
||||
var passwordSettings = dbService.Settings.PasswordSettings;
|
||||
credential.Passwords.First().Value = GenerateRandomPassword(passwordSettings);
|
||||
credential.Passwords.First().Value = await jsInteropService.GenerateRandomPasswordAsync(passwordSettings);
|
||||
|
||||
return credential;
|
||||
}
|
||||
|
||||
@@ -9,7 +9,6 @@ namespace AliasVault.Client.Services;
|
||||
|
||||
using System.Security.Cryptography;
|
||||
using System.Text.Json;
|
||||
using Microsoft.EntityFrameworkCore.Metadata.Internal;
|
||||
using Microsoft.JSInterop;
|
||||
|
||||
/// <summary>
|
||||
@@ -18,6 +17,19 @@ using Microsoft.JSInterop;
|
||||
/// <param name="jsRuntime">IJSRuntime.</param>
|
||||
public sealed class JsInteropService(IJSRuntime jsRuntime)
|
||||
{
|
||||
private IJSObjectReference? _identityGeneratorModule;
|
||||
private IJSObjectReference? _passwordGeneratorModule;
|
||||
|
||||
/// <summary>
|
||||
/// Initialize the identity generator module.
|
||||
/// </summary>
|
||||
/// <returns>A task representing the asynchronous operation.</returns>
|
||||
public async Task InitializeAsync()
|
||||
{
|
||||
_identityGeneratorModule = await jsRuntime.InvokeAsync<IJSObjectReference>("import", "./js/shared/identity-generator/index.mjs");
|
||||
_passwordGeneratorModule = await jsRuntime.InvokeAsync<IJSObjectReference>("import", "./js/shared/password-generator/index.mjs");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Symmetrically encrypts a string using the provided encryption key.
|
||||
/// </summary>
|
||||
@@ -275,6 +287,73 @@ public sealed class JsInteropService(IJSRuntime jsRuntime)
|
||||
return await jsRuntime.InvokeAsync<byte[]>("cryptoInterop.decryptBytes", base64Ciphertext, encryptionKey);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates a random identity using the specified language.
|
||||
/// </summary>
|
||||
/// <param name="language">The language to use for generating the identity (e.g. "en", "nl").</param>
|
||||
/// <returns>A tuple containing the generated identity information.</returns>
|
||||
public async Task<(string FirstName, string LastName, string NickName, string EmailPrefix, string Gender, DateTime BirthDate)> GenerateRandomIdentityAsync(string language)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (_identityGeneratorModule == null)
|
||||
{
|
||||
await InitializeAsync();
|
||||
if (_identityGeneratorModule == null)
|
||||
{
|
||||
throw new InvalidOperationException("Failed to initialize identity generator module");
|
||||
}
|
||||
}
|
||||
|
||||
var generatorInstance = await _identityGeneratorModule.InvokeAsync<IJSObjectReference>("createGenerator", language);
|
||||
var result = await generatorInstance.InvokeAsync<JsonElement>("generateRandomIdentity");
|
||||
|
||||
return (
|
||||
result.GetProperty("firstName").GetString()!,
|
||||
result.GetProperty("lastName").GetString()!,
|
||||
result.GetProperty("nickName").GetString()!,
|
||||
result.GetProperty("emailPrefix").GetString()!,
|
||||
result.GetProperty("gender").GetString()!,
|
||||
result.GetProperty("birthDate").GetDateTime());
|
||||
}
|
||||
catch (JSException ex)
|
||||
{
|
||||
await Console.Error.WriteLineAsync($"JavaScript error generating identity: {ex.Message}");
|
||||
throw new InvalidOperationException("Failed to generate random identity", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates a random password using the specified settings.
|
||||
/// </summary>
|
||||
/// <param name="settings">The password settings to use.</param>
|
||||
/// <returns>The generated password.</returns>
|
||||
public async Task<string> GenerateRandomPasswordAsync(PasswordSettings settings)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (_passwordGeneratorModule == null)
|
||||
{
|
||||
await InitializeAsync();
|
||||
if (_passwordGeneratorModule == null)
|
||||
{
|
||||
throw new InvalidOperationException("Failed to initialize password generator module");
|
||||
}
|
||||
}
|
||||
|
||||
var generatorInstance = await _passwordGeneratorModule.InvokeAsync<IJSObjectReference>("createPasswordGenerator", settings);
|
||||
|
||||
var result = await generatorInstance.InvokeAsync<string>("generateRandomPassword");
|
||||
|
||||
return result;
|
||||
}
|
||||
catch (JSException ex)
|
||||
{
|
||||
await Console.Error.WriteLineAsync($"JavaScript error generating password: {ex.Message}");
|
||||
throw new InvalidOperationException("Failed to generate random password", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents the result of a WebAuthn get credential operation.
|
||||
/// </summary>
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
# ⚠️ Auto-Generated Files
|
||||
|
||||
This folder contains the output of the shared `identity-generator` module from the `/shared` directory in the AliasVault project.
|
||||
|
||||
**Do not edit any of these files manually.**
|
||||
|
||||
To make changes:
|
||||
1. Update the source files in `packages/identity-generator/src`
|
||||
2. Run the `build-and-distribute.sh` script at the root of the project to regenerate the outputs and copy them here.
|
||||
142
apps/server/AliasVault.Client/wwwroot/js/shared/identity-generator/index.d.ts
vendored
Normal file
142
apps/server/AliasVault.Client/wwwroot/js/shared/identity-generator/index.d.ts
vendored
Normal file
@@ -0,0 +1,142 @@
|
||||
declare enum Gender {
|
||||
Male = "Male",
|
||||
Female = "Female",
|
||||
Other = "Other"
|
||||
}
|
||||
|
||||
/**
|
||||
* Identity.
|
||||
*/
|
||||
type Identity = {
|
||||
firstName: string;
|
||||
lastName: string;
|
||||
gender: Gender;
|
||||
birthDate: Date;
|
||||
emailPrefix: string;
|
||||
nickName: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Generate a username or email prefix.
|
||||
*/
|
||||
declare class UsernameEmailGenerator {
|
||||
private static readonly MIN_LENGTH;
|
||||
private static readonly MAX_LENGTH;
|
||||
private readonly symbols;
|
||||
/**
|
||||
* Generate a username based on an identity.
|
||||
*/
|
||||
generateUsername(identity: Identity): string;
|
||||
/**
|
||||
* Generate an email prefix based on an identity.
|
||||
*/
|
||||
generateEmailPrefix(identity: Identity): string;
|
||||
/**
|
||||
* Sanitize an email prefix.
|
||||
*/
|
||||
private sanitizeEmailPrefix;
|
||||
/**
|
||||
* Get a random symbol.
|
||||
*/
|
||||
private getRandomSymbol;
|
||||
/**
|
||||
* Generate a random string.
|
||||
*/
|
||||
private generateRandomString;
|
||||
/**
|
||||
* Generate a secure random integer between 0 (inclusive) and max (exclusive)
|
||||
*/
|
||||
private getSecureRandom;
|
||||
}
|
||||
|
||||
interface IIdentityGenerator {
|
||||
generateRandomIdentity(): Identity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Base identity generator.
|
||||
*/
|
||||
declare abstract class BaseIdentityGenerator implements IIdentityGenerator {
|
||||
protected firstNamesMale: string[];
|
||||
protected firstNamesFemale: string[];
|
||||
protected lastNames: string[];
|
||||
private readonly random;
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
constructor();
|
||||
protected abstract getFirstNamesMaleJson(): string[];
|
||||
protected abstract getFirstNamesFemaleJson(): string[];
|
||||
protected abstract getLastNamesJson(): string[];
|
||||
/**
|
||||
* Generate a random date of birth.
|
||||
*/
|
||||
protected generateRandomDateOfBirth(): Date;
|
||||
/**
|
||||
* Generate a random identity.
|
||||
*/
|
||||
generateRandomIdentity(): Identity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Identity generator for English language using English word dictionaries.
|
||||
*/
|
||||
declare class IdentityGeneratorEn extends BaseIdentityGenerator {
|
||||
/**
|
||||
* Get the male first names.
|
||||
*/
|
||||
protected getFirstNamesMaleJson(): string[];
|
||||
/**
|
||||
* Get the female first names.
|
||||
*/
|
||||
protected getFirstNamesFemaleJson(): string[];
|
||||
/**
|
||||
* Get the last names.
|
||||
*/
|
||||
protected getLastNamesJson(): string[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Identity generator for Dutch language using Dutch word dictionaries.
|
||||
*/
|
||||
declare class IdentityGeneratorNl extends BaseIdentityGenerator {
|
||||
/**
|
||||
* Get the male first names.
|
||||
*/
|
||||
protected getFirstNamesMaleJson(): string[];
|
||||
/**
|
||||
* Get the female first names.
|
||||
*/
|
||||
protected getFirstNamesFemaleJson(): string[];
|
||||
/**
|
||||
* Get the last names.
|
||||
*/
|
||||
protected getLastNamesJson(): string[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper utilities for identity generation that can be used by multiple client applications.
|
||||
*/
|
||||
declare class IdentityHelperUtils {
|
||||
/**
|
||||
* Normalize a birth date for display.
|
||||
*/
|
||||
static normalizeBirthDateForDisplay(birthDate: string | undefined): string;
|
||||
/**
|
||||
* Normalize a birth date for database.
|
||||
*/
|
||||
static normalizeBirthDateForDb(input: string | undefined): string;
|
||||
/**
|
||||
* Check if a birth date is valid.
|
||||
*/
|
||||
static isValidBirthDate(input: string | undefined): boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new identity generator based on the language.
|
||||
* @param language - The language to use for generating the identity (e.g. "en", "nl").
|
||||
* @returns A new identity generator instance.
|
||||
*/
|
||||
declare const createGenerator: (language: string) => IIdentityGenerator;
|
||||
|
||||
export { BaseIdentityGenerator, Gender, type Identity, IdentityGeneratorEn, IdentityGeneratorNl, IdentityHelperUtils, UsernameEmailGenerator, createGenerator };
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,9 @@
|
||||
# ⚠️ Auto-Generated Files
|
||||
|
||||
This folder contains the output of the shared `password-generator` module from the `/shared` directory in the AliasVault project.
|
||||
|
||||
**Do not edit any of these files manually.**
|
||||
|
||||
To make changes:
|
||||
1. Update the source files in `packages/password-generator/src`
|
||||
2. Run the `build-and-distribute.sh` script at the root of the project to regenerate the outputs and copy them here.
|
||||
119
apps/server/AliasVault.Client/wwwroot/js/shared/password-generator/index.d.ts
vendored
Normal file
119
apps/server/AliasVault.Client/wwwroot/js/shared/password-generator/index.d.ts
vendored
Normal file
@@ -0,0 +1,119 @@
|
||||
/**
|
||||
* Settings for password generation stored in SQLite database settings table as string.
|
||||
*/
|
||||
type PasswordSettings = {
|
||||
/**
|
||||
* The length of the password.
|
||||
*/
|
||||
Length: number;
|
||||
/**
|
||||
* Whether to use lowercase letters.
|
||||
*/
|
||||
UseLowercase: boolean;
|
||||
/**
|
||||
* Whether to use uppercase letters.
|
||||
*/
|
||||
UseUppercase: boolean;
|
||||
/**
|
||||
* Whether to use numbers.
|
||||
*/
|
||||
UseNumbers: boolean;
|
||||
/**
|
||||
* Whether to use special characters.
|
||||
*/
|
||||
UseSpecialChars: boolean;
|
||||
/**
|
||||
* Whether to use non-ambiguous characters.
|
||||
*/
|
||||
UseNonAmbiguousChars: boolean;
|
||||
};
|
||||
|
||||
/**
|
||||
* Generate a random password.
|
||||
*/
|
||||
declare class PasswordGenerator {
|
||||
private readonly lowercaseChars;
|
||||
private readonly uppercaseChars;
|
||||
private readonly numberChars;
|
||||
private readonly specialChars;
|
||||
private readonly ambiguousChars;
|
||||
private length;
|
||||
private useLowercase;
|
||||
private useUppercase;
|
||||
private useNumbers;
|
||||
private useSpecial;
|
||||
private useNonAmbiguous;
|
||||
/**
|
||||
* Create a new instance of PasswordGenerator.
|
||||
* @param settings Optional password settings to initialize with.
|
||||
*/
|
||||
constructor(settings?: PasswordSettings);
|
||||
/**
|
||||
* Apply password settings to this generator.
|
||||
*/
|
||||
applySettings(settings: PasswordSettings): this;
|
||||
/**
|
||||
* Set the length of the password.
|
||||
*/
|
||||
setLength(length: number): this;
|
||||
/**
|
||||
* Set if lowercase letters should be used.
|
||||
*/
|
||||
useLowercaseLetters(use: boolean): this;
|
||||
/**
|
||||
* Set if uppercase letters should be used.
|
||||
*/
|
||||
useUppercaseLetters(use: boolean): this;
|
||||
/**
|
||||
* Set if numeric characters should be used.
|
||||
*/
|
||||
useNumericCharacters(use: boolean): this;
|
||||
/**
|
||||
* Set if special characters should be used.
|
||||
*/
|
||||
useSpecialCharacters(use: boolean): this;
|
||||
/**
|
||||
* Set if only non-ambiguous characters should be used.
|
||||
*/
|
||||
useNonAmbiguousCharacters(use: boolean): this;
|
||||
/**
|
||||
* Get a random index from the crypto module.
|
||||
*/
|
||||
private getUnbiasedRandomIndex;
|
||||
/**
|
||||
* Generate a random password.
|
||||
*/
|
||||
generateRandomPassword(): string;
|
||||
/**
|
||||
* Build character set based on selected options.
|
||||
*/
|
||||
private buildCharacterSet;
|
||||
/**
|
||||
* Remove ambiguous characters from a character set.
|
||||
*/
|
||||
private removeAmbiguousCharacters;
|
||||
/**
|
||||
* Generate initial random password.
|
||||
*/
|
||||
private generateInitialPassword;
|
||||
/**
|
||||
* Ensure the generated password meets all specified requirements.
|
||||
*/
|
||||
private ensureRequirements;
|
||||
/**
|
||||
* Get a character set with ambiguous characters removed if needed.
|
||||
*/
|
||||
private getSafeCharacterSet;
|
||||
/**
|
||||
* Add a character from the given set at a random position in the password.
|
||||
*/
|
||||
private addCharacterFromSet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new password generator.
|
||||
* @returns A new password generator instance.
|
||||
*/
|
||||
declare const createPasswordGenerator: () => PasswordGenerator;
|
||||
|
||||
export { PasswordGenerator, type PasswordSettings, createPasswordGenerator };
|
||||
@@ -0,0 +1,244 @@
|
||||
"use strict";
|
||||
var __defProp = Object.defineProperty;
|
||||
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
||||
var __getOwnPropNames = Object.getOwnPropertyNames;
|
||||
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
||||
var __export = (target, all) => {
|
||||
for (var name in all)
|
||||
__defProp(target, name, { get: all[name], enumerable: true });
|
||||
};
|
||||
var __copyProps = (to, from, except, desc) => {
|
||||
if (from && typeof from === "object" || typeof from === "function") {
|
||||
for (let key of __getOwnPropNames(from))
|
||||
if (!__hasOwnProp.call(to, key) && key !== except)
|
||||
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
||||
}
|
||||
return to;
|
||||
};
|
||||
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
||||
|
||||
// src/index.ts
|
||||
var index_exports = {};
|
||||
__export(index_exports, {
|
||||
PasswordGenerator: () => PasswordGenerator,
|
||||
createPasswordGenerator: () => createPasswordGenerator
|
||||
});
|
||||
module.exports = __toCommonJS(index_exports);
|
||||
|
||||
// src/utils/PasswordGenerator.ts
|
||||
var PasswordGenerator = class {
|
||||
/**
|
||||
* Create a new instance of PasswordGenerator.
|
||||
* @param settings Optional password settings to initialize with.
|
||||
*/
|
||||
constructor(settings) {
|
||||
this.lowercaseChars = "abcdefghijklmnopqrstuvwxyz";
|
||||
this.uppercaseChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||
this.numberChars = "0123456789";
|
||||
this.specialChars = "!@#$%^&*()_+-=[]{}|;:,.<>?";
|
||||
this.ambiguousChars = "Il1O0";
|
||||
this.length = 18;
|
||||
this.useLowercase = true;
|
||||
this.useUppercase = true;
|
||||
this.useNumbers = true;
|
||||
this.useSpecial = true;
|
||||
this.useNonAmbiguous = false;
|
||||
if (settings) {
|
||||
this.applySettings(settings);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Apply password settings to this generator.
|
||||
*/
|
||||
applySettings(settings) {
|
||||
this.length = settings.Length;
|
||||
this.useLowercase = settings.UseLowercase;
|
||||
this.useUppercase = settings.UseUppercase;
|
||||
this.useNumbers = settings.UseNumbers;
|
||||
this.useSpecial = settings.UseSpecialChars;
|
||||
this.useNonAmbiguous = settings.UseNonAmbiguousChars;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Set the length of the password.
|
||||
*/
|
||||
setLength(length) {
|
||||
this.length = length;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Set if lowercase letters should be used.
|
||||
*/
|
||||
useLowercaseLetters(use) {
|
||||
this.useLowercase = use;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Set if uppercase letters should be used.
|
||||
*/
|
||||
useUppercaseLetters(use) {
|
||||
this.useUppercase = use;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Set if numeric characters should be used.
|
||||
*/
|
||||
useNumericCharacters(use) {
|
||||
this.useNumbers = use;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Set if special characters should be used.
|
||||
*/
|
||||
useSpecialCharacters(use) {
|
||||
this.useSpecial = use;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Set if only non-ambiguous characters should be used.
|
||||
*/
|
||||
useNonAmbiguousCharacters(use) {
|
||||
this.useNonAmbiguous = use;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Get a random index from the crypto module.
|
||||
*/
|
||||
getUnbiasedRandomIndex(max) {
|
||||
const limit = Math.floor(2 ** 32 / max) * max;
|
||||
while (true) {
|
||||
const array = new Uint32Array(1);
|
||||
crypto.getRandomValues(array);
|
||||
const value = array[0];
|
||||
if (value < limit) {
|
||||
return value % max;
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Generate a random password.
|
||||
*/
|
||||
generateRandomPassword() {
|
||||
const chars = this.buildCharacterSet();
|
||||
let password = this.generateInitialPassword(chars);
|
||||
password = this.ensureRequirements(password);
|
||||
return password;
|
||||
}
|
||||
/**
|
||||
* Build character set based on selected options.
|
||||
*/
|
||||
buildCharacterSet() {
|
||||
let chars = "";
|
||||
if (this.useLowercase) {
|
||||
chars += this.lowercaseChars;
|
||||
}
|
||||
if (this.useUppercase) {
|
||||
chars += this.uppercaseChars;
|
||||
}
|
||||
if (this.useNumbers) {
|
||||
chars += this.numberChars;
|
||||
}
|
||||
if (this.useSpecial) {
|
||||
chars += this.specialChars;
|
||||
}
|
||||
if (chars.length === 0) {
|
||||
chars = this.lowercaseChars;
|
||||
}
|
||||
if (this.useNonAmbiguous) {
|
||||
chars = this.removeAmbiguousCharacters(chars);
|
||||
}
|
||||
return chars;
|
||||
}
|
||||
/**
|
||||
* Remove ambiguous characters from a character set.
|
||||
*/
|
||||
removeAmbiguousCharacters(chars) {
|
||||
for (const ambChar of this.ambiguousChars) {
|
||||
chars = chars.replace(ambChar, "");
|
||||
}
|
||||
return chars;
|
||||
}
|
||||
/**
|
||||
* Generate initial random password.
|
||||
*/
|
||||
generateInitialPassword(chars) {
|
||||
let password = "";
|
||||
for (let i = 0; i < this.length; i++) {
|
||||
password += chars[this.getUnbiasedRandomIndex(chars.length)];
|
||||
}
|
||||
return password;
|
||||
}
|
||||
/**
|
||||
* Ensure the generated password meets all specified requirements.
|
||||
*/
|
||||
ensureRequirements(password) {
|
||||
if (this.useLowercase && !/[a-z]/.exec(password)) {
|
||||
password = this.addCharacterFromSet(
|
||||
password,
|
||||
this.getSafeCharacterSet(this.lowercaseChars, true)
|
||||
);
|
||||
}
|
||||
if (this.useUppercase && !/[A-Z]/.exec(password)) {
|
||||
password = this.addCharacterFromSet(
|
||||
password,
|
||||
this.getSafeCharacterSet(this.uppercaseChars, true)
|
||||
);
|
||||
}
|
||||
if (this.useNumbers && !/\d/.exec(password)) {
|
||||
password = this.addCharacterFromSet(
|
||||
password,
|
||||
this.getSafeCharacterSet(this.numberChars, false)
|
||||
);
|
||||
}
|
||||
if (this.useSpecial && !/[!@#$%^&*()_+\-=[\]{}|;:,.<>?]/.exec(password)) {
|
||||
password = this.addCharacterFromSet(
|
||||
password,
|
||||
this.specialChars
|
||||
);
|
||||
}
|
||||
return password;
|
||||
}
|
||||
/**
|
||||
* Get a character set with ambiguous characters removed if needed.
|
||||
*/
|
||||
getSafeCharacterSet(charSet, isAlpha) {
|
||||
if (!this.useNonAmbiguous) {
|
||||
return charSet;
|
||||
}
|
||||
let safeSet = charSet;
|
||||
for (const ambChar of this.ambiguousChars) {
|
||||
if (!isAlpha && !/\d/.test(ambChar)) {
|
||||
continue;
|
||||
}
|
||||
let charToRemove = ambChar;
|
||||
if (isAlpha) {
|
||||
if (charSet === this.lowercaseChars) {
|
||||
charToRemove = ambChar.toLowerCase();
|
||||
} else {
|
||||
charToRemove = ambChar.toUpperCase();
|
||||
}
|
||||
}
|
||||
safeSet = safeSet.replace(charToRemove, "");
|
||||
}
|
||||
return safeSet;
|
||||
}
|
||||
/**
|
||||
* Add a character from the given set at a random position in the password.
|
||||
*/
|
||||
addCharacterFromSet(password, charSet) {
|
||||
const pos = this.getUnbiasedRandomIndex(this.length);
|
||||
const char = charSet[this.getUnbiasedRandomIndex(charSet.length)];
|
||||
return password.substring(0, pos) + char + password.substring(pos + 1);
|
||||
}
|
||||
};
|
||||
|
||||
// src/factories/PasswordGeneratorFactory.ts
|
||||
var createPasswordGenerator = () => {
|
||||
return new PasswordGenerator();
|
||||
};
|
||||
// Annotate the CommonJS export names for ESM import in node:
|
||||
0 && (module.exports = {
|
||||
PasswordGenerator,
|
||||
createPasswordGenerator
|
||||
});
|
||||
//# sourceMappingURL=index.js.map
|
||||
@@ -0,0 +1,216 @@
|
||||
// src/utils/PasswordGenerator.ts
|
||||
var PasswordGenerator = class {
|
||||
/**
|
||||
* Create a new instance of PasswordGenerator.
|
||||
* @param settings Optional password settings to initialize with.
|
||||
*/
|
||||
constructor(settings) {
|
||||
this.lowercaseChars = "abcdefghijklmnopqrstuvwxyz";
|
||||
this.uppercaseChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||
this.numberChars = "0123456789";
|
||||
this.specialChars = "!@#$%^&*()_+-=[]{}|;:,.<>?";
|
||||
this.ambiguousChars = "Il1O0";
|
||||
this.length = 18;
|
||||
this.useLowercase = true;
|
||||
this.useUppercase = true;
|
||||
this.useNumbers = true;
|
||||
this.useSpecial = true;
|
||||
this.useNonAmbiguous = false;
|
||||
if (settings) {
|
||||
this.applySettings(settings);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Apply password settings to this generator.
|
||||
*/
|
||||
applySettings(settings) {
|
||||
this.length = settings.Length;
|
||||
this.useLowercase = settings.UseLowercase;
|
||||
this.useUppercase = settings.UseUppercase;
|
||||
this.useNumbers = settings.UseNumbers;
|
||||
this.useSpecial = settings.UseSpecialChars;
|
||||
this.useNonAmbiguous = settings.UseNonAmbiguousChars;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Set the length of the password.
|
||||
*/
|
||||
setLength(length) {
|
||||
this.length = length;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Set if lowercase letters should be used.
|
||||
*/
|
||||
useLowercaseLetters(use) {
|
||||
this.useLowercase = use;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Set if uppercase letters should be used.
|
||||
*/
|
||||
useUppercaseLetters(use) {
|
||||
this.useUppercase = use;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Set if numeric characters should be used.
|
||||
*/
|
||||
useNumericCharacters(use) {
|
||||
this.useNumbers = use;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Set if special characters should be used.
|
||||
*/
|
||||
useSpecialCharacters(use) {
|
||||
this.useSpecial = use;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Set if only non-ambiguous characters should be used.
|
||||
*/
|
||||
useNonAmbiguousCharacters(use) {
|
||||
this.useNonAmbiguous = use;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Get a random index from the crypto module.
|
||||
*/
|
||||
getUnbiasedRandomIndex(max) {
|
||||
const limit = Math.floor(2 ** 32 / max) * max;
|
||||
while (true) {
|
||||
const array = new Uint32Array(1);
|
||||
crypto.getRandomValues(array);
|
||||
const value = array[0];
|
||||
if (value < limit) {
|
||||
return value % max;
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Generate a random password.
|
||||
*/
|
||||
generateRandomPassword() {
|
||||
const chars = this.buildCharacterSet();
|
||||
let password = this.generateInitialPassword(chars);
|
||||
password = this.ensureRequirements(password);
|
||||
return password;
|
||||
}
|
||||
/**
|
||||
* Build character set based on selected options.
|
||||
*/
|
||||
buildCharacterSet() {
|
||||
let chars = "";
|
||||
if (this.useLowercase) {
|
||||
chars += this.lowercaseChars;
|
||||
}
|
||||
if (this.useUppercase) {
|
||||
chars += this.uppercaseChars;
|
||||
}
|
||||
if (this.useNumbers) {
|
||||
chars += this.numberChars;
|
||||
}
|
||||
if (this.useSpecial) {
|
||||
chars += this.specialChars;
|
||||
}
|
||||
if (chars.length === 0) {
|
||||
chars = this.lowercaseChars;
|
||||
}
|
||||
if (this.useNonAmbiguous) {
|
||||
chars = this.removeAmbiguousCharacters(chars);
|
||||
}
|
||||
return chars;
|
||||
}
|
||||
/**
|
||||
* Remove ambiguous characters from a character set.
|
||||
*/
|
||||
removeAmbiguousCharacters(chars) {
|
||||
for (const ambChar of this.ambiguousChars) {
|
||||
chars = chars.replace(ambChar, "");
|
||||
}
|
||||
return chars;
|
||||
}
|
||||
/**
|
||||
* Generate initial random password.
|
||||
*/
|
||||
generateInitialPassword(chars) {
|
||||
let password = "";
|
||||
for (let i = 0; i < this.length; i++) {
|
||||
password += chars[this.getUnbiasedRandomIndex(chars.length)];
|
||||
}
|
||||
return password;
|
||||
}
|
||||
/**
|
||||
* Ensure the generated password meets all specified requirements.
|
||||
*/
|
||||
ensureRequirements(password) {
|
||||
if (this.useLowercase && !/[a-z]/.exec(password)) {
|
||||
password = this.addCharacterFromSet(
|
||||
password,
|
||||
this.getSafeCharacterSet(this.lowercaseChars, true)
|
||||
);
|
||||
}
|
||||
if (this.useUppercase && !/[A-Z]/.exec(password)) {
|
||||
password = this.addCharacterFromSet(
|
||||
password,
|
||||
this.getSafeCharacterSet(this.uppercaseChars, true)
|
||||
);
|
||||
}
|
||||
if (this.useNumbers && !/\d/.exec(password)) {
|
||||
password = this.addCharacterFromSet(
|
||||
password,
|
||||
this.getSafeCharacterSet(this.numberChars, false)
|
||||
);
|
||||
}
|
||||
if (this.useSpecial && !/[!@#$%^&*()_+\-=[\]{}|;:,.<>?]/.exec(password)) {
|
||||
password = this.addCharacterFromSet(
|
||||
password,
|
||||
this.specialChars
|
||||
);
|
||||
}
|
||||
return password;
|
||||
}
|
||||
/**
|
||||
* Get a character set with ambiguous characters removed if needed.
|
||||
*/
|
||||
getSafeCharacterSet(charSet, isAlpha) {
|
||||
if (!this.useNonAmbiguous) {
|
||||
return charSet;
|
||||
}
|
||||
let safeSet = charSet;
|
||||
for (const ambChar of this.ambiguousChars) {
|
||||
if (!isAlpha && !/\d/.test(ambChar)) {
|
||||
continue;
|
||||
}
|
||||
let charToRemove = ambChar;
|
||||
if (isAlpha) {
|
||||
if (charSet === this.lowercaseChars) {
|
||||
charToRemove = ambChar.toLowerCase();
|
||||
} else {
|
||||
charToRemove = ambChar.toUpperCase();
|
||||
}
|
||||
}
|
||||
safeSet = safeSet.replace(charToRemove, "");
|
||||
}
|
||||
return safeSet;
|
||||
}
|
||||
/**
|
||||
* Add a character from the given set at a random position in the password.
|
||||
*/
|
||||
addCharacterFromSet(password, charSet) {
|
||||
const pos = this.getUnbiasedRandomIndex(this.length);
|
||||
const char = charSet[this.getUnbiasedRandomIndex(charSet.length)];
|
||||
return password.substring(0, pos) + char + password.substring(pos + 1);
|
||||
}
|
||||
};
|
||||
|
||||
// src/factories/PasswordGeneratorFactory.ts
|
||||
var createPasswordGenerator = () => {
|
||||
return new PasswordGenerator();
|
||||
};
|
||||
export {
|
||||
PasswordGenerator,
|
||||
createPasswordGenerator
|
||||
};
|
||||
//# sourceMappingURL=index.mjs.map
|
||||
Reference in New Issue
Block a user