Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -44,4 +44,4 @@ Console.WriteLine(password); // Example output: kAj79uV@E?m7_8eS
Install via NuGet:

```bash
dotnet add package KPasswordGenerator
dotnet add package KPasswordGenerator
12 changes: 6 additions & 6 deletions src/KPasswordGenerator.Tests/PasswordGeneratorTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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]);
Expand Down Expand Up @@ -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]
Expand Down
17 changes: 0 additions & 17 deletions src/KPasswordGenerator/CharSet.cs

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
namespace KPasswordGenerator;

public static class CharSets
public static class CharacterPools
{
public const string LowerCase = "abcdefghijklmnopqrstuvwxyz";

Expand Down
17 changes: 17 additions & 0 deletions src/KPasswordGenerator/CharacterRequirement.cs
Original file line number Diff line number Diff line change
@@ -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; }
}
16 changes: 8 additions & 8 deletions src/KPasswordGenerator/PasswordGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,23 +15,23 @@ public PasswordGenerator(PasswordSettings passwordSettings)

public string Generate(int passwordLength)
{
ArgumentOutOfRangeException.ThrowIfLessThan(passwordLength, _passwordSettings._minimumPasswordLength);
ArgumentOutOfRangeException.ThrowIfLessThan(passwordLength, _passwordSettings.MinimumPasswordLength);

Span<char> result = passwordLength <= 512 ?
Span<char> 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();
}
}
16 changes: 8 additions & 8 deletions src/KPasswordGenerator/PasswordSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<CharSet> charSets)
public PasswordSettings(ICollection<CharacterRequirement> 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<CharSet> CharSets { get; }
public ICollection<CharacterRequirement> CharacterRequirements { get; }
}