Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

When no valid password could be generated, throw an Exception if desired #18

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
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
17 changes: 17 additions & 0 deletions PasswordGenerator.Tests/BasicTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,23 @@ public void PasswordGenerator_LengthOnly_ShouldNotThrowAnError()
string result = pwd.Next();
Assert.AreEqual(21,result.Length);
}

[Test]
public void PasswordGenerator_ThrowOnMaximumAttemptsExceededEnabled_ShouldThrow()
{
Assert.Throws<MaximumAttemptsExceededException>(() =>
{
// Multiple attempts are needed to make sure the test throws
for (int i = 0; i < 1000; i++)
{
// Maximum complexity, minimal allowed attempts
var pwd = new Password(includeLowercase: true, includeUppercase: true, includeNumeric: true, includeSpecial: true, passwordLength: 4, maximumAttempts: 1)
.ThrowOnMaximumAttemptsExceeded();

pwd.Next();
}
});
}

[Test]
public void PasswordGenerator_NoLengthTest_ShouldNotThrowAnError()
Expand Down
21 changes: 21 additions & 0 deletions PasswordGenerator/MaximumAttemptsExceededException.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
using System;
using System.Runtime.Serialization;

namespace PasswordGenerator
{
/// <summary>
/// Represents errors that occurs during random password generation when the configured maximum attempts are exceeded.
/// </summary>
[Serializable]
public sealed class MaximumAttemptsExceededException : Exception
{
public MaximumAttemptsExceededException()
{
}

private MaximumAttemptsExceededException(SerializationInfo info, StreamingContext context)
: base(info, context)
{
}
}
}
30 changes: 28 additions & 2 deletions PasswordGenerator/Password.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

namespace PasswordGenerator
{
/// <summary>
/// <summary>
/// Generates random passwords and validates that they meet the rules passed in
/// </summary>
public class Password : IPassword
Expand All @@ -18,6 +18,7 @@ public class Password : IPassword
private const bool DefaultIncludeNumeric = true;
private const bool DefaultIncludeSpecial = true;
private static RNGCryptoServiceProvider _rng;
private bool _throwOnMaximumAttemptsExceeded;

public Password()
{
Expand Down Expand Up @@ -106,10 +107,25 @@ public IPassword LengthRequired(int passwordLength)
Settings.PasswordLength = passwordLength;
return this;
}

/// <summary>
/// Configures the password generator to throw an exception instead of returning "Try again" as a password when the maximum number of attempts exceeded.
/// </summary>
/// <remarks>See: https://github.com/prjseal/PasswordGenerator/issues/13</remarks>
/// <returns>The <see cref="IPassword"/> instance.</returns>
public IPassword ThrowOnMaximumAttemptsExceeded()
{
_throwOnMaximumAttemptsExceeded = true;
return this;
}

/// <summary>
/// Gets the next random password which meets the requirements
/// </summary>
/// <exception cref="MaximumAttemptsExceededException">
/// Thrown when no valid password could be generated (within the configured attempts threshold).
/// Only throws when throwing is configured.
/// </exception>
/// <returns>A password as a string</returns>
public string Next()
{
Expand All @@ -128,7 +144,17 @@ public string Next()
passwordAttempts++;
} while (passwordAttempts < Settings.MaximumAttempts && !PasswordIsValid(Settings, password));

password = PasswordIsValid(Settings, password) ? password : "Try again";
if (PasswordIsValid(Settings, password))
{
return password;
}

if (_throwOnMaximumAttemptsExceeded)
{
throw new MaximumAttemptsExceededException();
}

password = "Try again";
}

return password;
Expand Down