diff --git a/README.md b/README.md index 1e5d207..ad18632 100644 --- a/README.md +++ b/README.md @@ -20,16 +20,16 @@ A lightweight, secure, and flexible password generator library for .NET, built w --- -```c# +```csharp using KPasswordGenerator; // Define your password policy PasswordSettings settings = new( [ - new CharSet(2, "ABCDEFGHJKLMNPQRSTUVWXYZ"), // At least 2 uppercase letters (no I, O) - new CharSet(3, "abcdefghijkmnopqrstuvwxyz"), // At least 3 lowercase letters (no l) - new CharSet(4, "23456789"), // At least 4 digits (no 0, 1) - new CharSet(2, "!@$?_-") // At least 2 symbols + new CharacterRequirement(minRequired: 2, characterPool: "ABCDEFGHJKLMNPQRSTUVWXYZ"), + new CharacterRequirement(3, "abcdefghijkmnopqrstuvwxyz"), // At least 3 lowercase letters (no l) + new CharacterRequirement(4, "23456789"), // At least 4 digits (no 0, 1) + new CharacterRequirement(2, "!@$?_-") // At least 2 symbols ]); PasswordGenerator generator = new(settings); @@ -44,4 +44,4 @@ Console.WriteLine(password); // Example output: kAj79uV@E?m7_8eS Install via NuGet: ```bash -dotnet add package KPasswordGenerator \ No newline at end of file +dotnet add package KPasswordGenerator diff --git a/src/KPasswordGenerator.Tests/PasswordGeneratorTests.cs b/src/KPasswordGenerator.Tests/PasswordGeneratorTests.cs index bbd9025..d171147 100644 --- a/src/KPasswordGenerator.Tests/PasswordGeneratorTests.cs +++ b/src/KPasswordGenerator.Tests/PasswordGeneratorTests.cs @@ -2,9 +2,9 @@ public class PasswordGeneratorTests { - private static readonly CharSet _lowerCase = new(2, "abcdef"); - private static readonly CharSet _upperCase = new(2, "ABCDEF"); - private static readonly CharSet _digits = new(2, "012345"); + private static readonly CharacterRequirement _lowerCase = new(2, "abcdef"); + private static readonly CharacterRequirement _upperCase = new(2, "ABCDEF"); + private static readonly CharacterRequirement _digits = new(2, "012345"); private static PasswordSettings CreateSettings() => new([_lowerCase, _upperCase, _digits]); @@ -44,9 +44,9 @@ public void Generate_UsesAtLeastRequiredCountFromEachCharSet() string password = generator.Generate(10); // Ensure at least 2 of each set - Assert.True(password.Count(_lowerCase.Chars.Contains) >= _lowerCase.Count); - Assert.True(password.Count(_upperCase.Chars.Contains) >= _upperCase.Count); - Assert.True(password.Count(_digits.Chars.Contains) >= _digits.Count); + Assert.True(password.Count(_lowerCase.CharacterPool.Contains) >= _lowerCase.MinRequired); + Assert.True(password.Count(_upperCase.CharacterPool.Contains) >= _upperCase.MinRequired); + Assert.True(password.Count(_digits.CharacterPool.Contains) >= _digits.MinRequired); } [Fact] diff --git a/src/KPasswordGenerator/CharSet.cs b/src/KPasswordGenerator/CharSet.cs deleted file mode 100644 index 29f20b7..0000000 --- a/src/KPasswordGenerator/CharSet.cs +++ /dev/null @@ -1,17 +0,0 @@ -namespace KPasswordGenerator; - -public sealed class CharSet -{ - public CharSet(int count, string chars) - { - ArgumentOutOfRangeException.ThrowIfNegativeOrZero(count); - ArgumentException.ThrowIfNullOrEmpty(chars); - - Count = count; - Chars = chars; - } - - public int Count { get; } - - public string Chars { get; } -} \ No newline at end of file diff --git a/src/KPasswordGenerator/CharSets.cs b/src/KPasswordGenerator/CharacterPools.cs similarity index 87% rename from src/KPasswordGenerator/CharSets.cs rename to src/KPasswordGenerator/CharacterPools.cs index e04d324..afcb516 100644 --- a/src/KPasswordGenerator/CharSets.cs +++ b/src/KPasswordGenerator/CharacterPools.cs @@ -1,6 +1,6 @@ namespace KPasswordGenerator; -public static class CharSets +public static class CharacterPools { public const string LowerCase = "abcdefghijklmnopqrstuvwxyz"; diff --git a/src/KPasswordGenerator/CharacterRequirement.cs b/src/KPasswordGenerator/CharacterRequirement.cs new file mode 100644 index 0000000..26b4865 --- /dev/null +++ b/src/KPasswordGenerator/CharacterRequirement.cs @@ -0,0 +1,17 @@ +namespace KPasswordGenerator; + +public sealed class CharacterRequirement +{ + public CharacterRequirement(int minRequired, string characterPool) + { + ArgumentOutOfRangeException.ThrowIfNegativeOrZero(minRequired); + ArgumentException.ThrowIfNullOrEmpty(characterPool); + + MinRequired = minRequired; + CharacterPool = characterPool; + } + + public int MinRequired { get; } + + public string CharacterPool { get; } +} \ No newline at end of file diff --git a/src/KPasswordGenerator/PasswordGenerator.cs b/src/KPasswordGenerator/PasswordGenerator.cs index 4577547..05be9ad 100644 --- a/src/KPasswordGenerator/PasswordGenerator.cs +++ b/src/KPasswordGenerator/PasswordGenerator.cs @@ -15,23 +15,23 @@ public PasswordGenerator(PasswordSettings passwordSettings) public string Generate(int passwordLength) { - ArgumentOutOfRangeException.ThrowIfLessThan(passwordLength, _passwordSettings._minimumPasswordLength); + ArgumentOutOfRangeException.ThrowIfLessThan(passwordLength, _passwordSettings.MinimumPasswordLength); - Span result = passwordLength <= 512 ? + Span buffer = passwordLength <= 512 ? stackalloc char[passwordLength] : new char[passwordLength]; int index = 0; - foreach (var charSet in _passwordSettings.CharSets) + foreach (var requirement in _passwordSettings.CharacterRequirements) { - RandomNumberGenerator.GetItems(charSet.Chars, result.Slice(index, charSet.Count)); - index += charSet.Count; + RandomNumberGenerator.GetItems(requirement.CharacterPool, buffer.Slice(index, requirement.MinRequired)); + index += requirement.MinRequired; } - RandomNumberGenerator.GetItems(_passwordSettings._allChars, result[index..]); - RandomNumberGenerator.Shuffle(result); + RandomNumberGenerator.GetItems(_passwordSettings.AllChars, buffer[index..]); + RandomNumberGenerator.Shuffle(buffer); - return result.ToString(); + return buffer.ToString(); } } \ No newline at end of file diff --git a/src/KPasswordGenerator/PasswordSettings.cs b/src/KPasswordGenerator/PasswordSettings.cs index 92da4a6..8a26fc1 100644 --- a/src/KPasswordGenerator/PasswordSettings.cs +++ b/src/KPasswordGenerator/PasswordSettings.cs @@ -2,18 +2,18 @@ public sealed class PasswordSettings { - internal readonly string _allChars; - internal readonly int _minimumPasswordLength; + internal readonly string AllChars; + internal readonly int MinimumPasswordLength; - public PasswordSettings(ICollection charSets) + public PasswordSettings(ICollection characterRequirements) { - ArgumentNullException.ThrowIfNull(charSets); + ArgumentNullException.ThrowIfNull(characterRequirements); - _minimumPasswordLength = charSets.Sum(c => c.Count); - CharSets = charSets; - _allChars = string.Concat(charSets.Select(c => c.Chars)); + MinimumPasswordLength = characterRequirements.Sum(c => c.MinRequired); + CharacterRequirements = characterRequirements; + AllChars = string.Concat(characterRequirements.Select(c => c.CharacterPool)); } - public ICollection CharSets { get; } + public ICollection CharacterRequirements { get; } } \ No newline at end of file