public void UserManagerTest05_Create_Update_Validate_And_DeleteUser() { // create a new user and save to DB var user = userManager.CreateUser(TESTUSERID, PasswordHashing.EncodePassword("testHash".ToSecureString(), null)); user.isActive = true; // ensure we activate account so we can retrieve it later user.isPasswordExpired = false; // and don't want to be flagged as needing pw change Assert.Throws <SavedFailedException>(() => userManager.SaveUser(user)); user.currentSite = DataRepository.GetDataRepository.ReferenceData["SiteLocation"].FirstOrDefault(x => ((SiteLocation)x).name == "Norfolk") as SiteLocation; // avoid FK constraint failure Assert.DoesNotThrow(() => userManager.SaveUser(user)); // see if we can validate user with wrong passphrase Assert.IsFalse(userManager.ValidateUser(TESTUSERID, null, out user), "1" + nameof(userManager.ValidateUser)); Assert.IsNull(user); Assert.IsFalse(userManager.ValidateUser(TESTUSERID, "BADHash".ToSecureString(), out user), "2" + nameof(userManager.ValidateUser)); Assert.IsNull(user); // and again but with correct passphrase Assert.IsTrue(userManager.ValidateUser(TESTUSERID, "testHash".ToSecureString(), out user), "3" + nameof(userManager.ValidateUser)); Assert.NotNull(user); userManager.RemoveUser(user); // verify actually removed Assert.IsFalse(userManager.ValidateUser(TESTUSERID, "testHash".ToSecureString(), out user), "4" + nameof(userManager.ValidateUser)); Assert.IsNull(user); }
public void TestGenerateAndCompareHashMatch() { byte[] saltAndHash = Convert.FromBase64String(PasswordHashing.EncodePassword(KnownPassword.ToSecureString(), null)); byte[] salt = PasswordHashing.ExtractStoredSalt(saltAndHash); Assert.That(salt, Is.Not.Null); Assert.That(PasswordHashing.HashesMatch(Convert.FromBase64String(PasswordHashing.EncodePassword(KnownPassword.ToSecureString(), salt)), saltAndHash)); }
public void TestHashDoesntMatch() { byte[] storedHash = Convert.FromBase64String(KnownHashAndSalt); byte[] salt = PasswordHashing.GenerateSalt(); byte[] curHash = Convert.FromBase64String(PasswordHashing.EncodePassword(KnownPassword.ToSecureString(), salt)); Assert.That(PasswordHashing.HashesMatch(storedHash, curHash), Is.False); }
public void TestHashMatches() { byte[] storedHash = Convert.FromBase64String(KnownHashAndSalt); byte[] salt = PasswordHashing.ExtractStoredSalt(storedHash); byte[] curHash = Convert.FromBase64String(PasswordHashing.EncodePassword(KnownPassword.ToSecureString(), salt)); Assert.That(PasswordHashing.HashesMatch(storedHash, curHash), Is.True); }
public void TestEncode() { byte[] storedHash = Convert.FromBase64String(KnownHashAndSalt); byte[] salt = PasswordHashing.ExtractStoredSalt(storedHash); byte[] curHash = Convert.FromBase64String(PasswordHashing.EncodePassword(KnownPassword.ToSecureString(), salt)); Assert.That(storedHash, Is.EqualTo(curHash), "Hashed Passphrase 'HACK' should be the same"); }
/// <summary> /// Allows [re]setting stored password hash /// </summary> /// <param name="passphrase">the password/phrase to hash</param> /// <param name="mustChange">defaults true, user must change on next log in; use false if user is setting/changing their own passphrase</param> public static void SetPasswordHash(this UserDetail user, SecureString passphrase, bool mustChange = true) { user.isPasswordExpired = mustChange; user.hashedPassphrase = PasswordHashing.EncodePassword(passphrase, null); }
/// <summary> /// Retrieve user information after validating provided userId and passphrase. /// If valid then returns true and updates user with UserDetails /// Otherwise returns false and sets user to null /// </summary> /// <param name="userId"></param> /// <param name="passphrase"></param> /// <param name="user">creates new instance and returns validated user information</param> /// <returns>true if user input valid and user object container user information; false on any error validating</returns> public bool ValidateUser(string userId, SecureString passphrase, out UserDetail user) { logger.Trace(nameof(ValidateUser)); // ensure we initialize user to null unless a valid userId and passphrase is provided user = null; // if not a valid value for DB key then fail early if (string.IsNullOrWhiteSpace(userId)) { return(false); } try { // retrieve corresponding user information from DB // Note: userId [UserDetail DB table key] is stored in lower case (using CultureInvariant form) var possibleUser = GetUserDetailsFor(userId); // invalid userId or error loading from DB - failed to validate; Note early exit allows determination of usernames if (possibleUser == null) { logger.Warn($"Failed to obtain possible user details for {userId}."); return(false); } // get attempted password, exit early if obviously invalid passphrase (blank) // if (string.IsNullOrWhiteSpace(passphrase.ToString())) return false; -- passphrase is now a SecureString, don't defeat point and convert to string if ((passphrase == null) || (passphrase.Length < 1)) { logger.Warn($"Passphrase provided for {userId} is invalid."); return(false); } // split apart stored hash so we can hash attempted password & compare bool isValid; try { byte[] storedHash = Convert.FromBase64String(possibleUser.hashedPassphrase); byte[] salt = PasswordHashing.ExtractStoredSalt(storedHash); // get hashed version of attempted password byte[] curHash = Convert.FromBase64String(PasswordHashing.EncodePassword(passphrase, salt)); // determine if hashed passphrases are a match isValid = PasswordHashing.HashesMatch(storedHash, curHash); } catch (System.FormatException e) // invalid Base64 string { logger.Warn("Error validating credentials, likely stored passphrase corrupted / not base64 encoded!", e); isValid = false; } // validated supplied password, now confirm user account not currently disabled isValid = isValid && possibleUser.isActive; // only return any information if user credentials successfully validated if (isValid) { logger.Info("Valid user."); user = possibleUser; } else { logger.Warn($"Failed to validate credentials for user {userId}."); } return(isValid); } catch (Exception e) { logger.Error(e, $"Error occured when validating user - {e.Message}"); throw; } }