From bac1f9042b27d506d0b52310fc6efcdd1ace1511 Mon Sep 17 00:00:00 2001 From: Sandro Ciervo Date: Thu, 15 Apr 2021 01:01:30 +0200 Subject: [PATCH] When no valid password could be generated, throw an Exception if desired Fixes Issue #13 - Should throw instead of returning "Try again" when a valid password cannot be found --- PasswordGenerator.Tests/BasicTests.cs | 17 +++++++++++ .../MaximumAttemptsExceededException.cs | 21 +++++++++++++ PasswordGenerator/Password.cs | 30 +++++++++++++++++-- 3 files changed, 66 insertions(+), 2 deletions(-) create mode 100644 PasswordGenerator/MaximumAttemptsExceededException.cs diff --git a/PasswordGenerator.Tests/BasicTests.cs b/PasswordGenerator.Tests/BasicTests.cs index 1af55ee..c939074 100644 --- a/PasswordGenerator.Tests/BasicTests.cs +++ b/PasswordGenerator.Tests/BasicTests.cs @@ -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(() => + { + // 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() diff --git a/PasswordGenerator/MaximumAttemptsExceededException.cs b/PasswordGenerator/MaximumAttemptsExceededException.cs new file mode 100644 index 0000000..120bbfc --- /dev/null +++ b/PasswordGenerator/MaximumAttemptsExceededException.cs @@ -0,0 +1,21 @@ +using System; +using System.Runtime.Serialization; + +namespace PasswordGenerator +{ + /// + /// Represents errors that occurs during random password generation when the configured maximum attempts are exceeded. + /// + [Serializable] + public sealed class MaximumAttemptsExceededException : Exception + { + public MaximumAttemptsExceededException() + { + } + + private MaximumAttemptsExceededException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + } +} \ No newline at end of file diff --git a/PasswordGenerator/Password.cs b/PasswordGenerator/Password.cs index a09685c..24326c0 100644 --- a/PasswordGenerator/Password.cs +++ b/PasswordGenerator/Password.cs @@ -6,7 +6,7 @@ namespace PasswordGenerator { -/// + /// /// Generates random passwords and validates that they meet the rules passed in /// public class Password : IPassword @@ -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() { @@ -106,10 +107,25 @@ public IPassword LengthRequired(int passwordLength) Settings.PasswordLength = passwordLength; return this; } + + /// + /// Configures the password generator to throw an exception instead of returning "Try again" as a password when the maximum number of attempts exceeded. + /// + /// See: https://github.com/prjseal/PasswordGenerator/issues/13 + /// The instance. + public IPassword ThrowOnMaximumAttemptsExceeded() + { + _throwOnMaximumAttemptsExceeded = true; + return this; + } /// /// Gets the next random password which meets the requirements /// + /// + /// Thrown when no valid password could be generated (within the configured attempts threshold). + /// Only throws when throwing is configured. + /// /// A password as a string public string Next() { @@ -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;