using e_suite.API.Common.exceptions; using e_suite.API.Common.models; using e_suite.Database.Core.Models; using e_suite.Database.Core.Tables.UserManager; using eSuite.Core.MailService; using Moq; using NUnit.Framework; using UserManager.UnitTests.Helpers; namespace UserManager.UnitTests.UserManager; [TestFixture] public class CompleteEmailActionUnitTests : UserManagerTestBase { [SetUp] public override async Task Setup() { await base.Setup(); } [Test] public Task CompleteEmailAction_TokenNotFound_ThrowsTokenInvalidException() { //Arrange var token = new EmailActionToken { Email = "evil.hacker@sun-strategy.com", Token = new Guid("{84CC5C02-0A14-465B-AB14-6D5125D50E7B}") }; //Act & Assert Assert.ThrowsAsync(async () => { await UserManager.CompleteEmailAction(token); }); return Task.CompletedTask; } [Test] public async Task CompleteEmailAction_TokenOutOfDate_ThrowsTokenInvalidException() { //Arrange var actualUser = new User { Id = 1, Email = "testuser@sun-strategy.com" }; await UserManagerRepository.AddUser(AuditUserDetails, actualUser, default); var token = new EmailActionToken { Email = actualUser.Email, Token = new Guid("{F5035CA7-E2B1-46B3-8E1A-B121107E66AD}") }; var emailUserAction = new EmailUserAction { EmailActionType = EmailUserActionType.ConfirmEmailAddress, Token = token.Token, User = actualUser, UserId = actualUser.Id, Created = new DateTimeOffset(2022, 08, 03, 11, 43, 0, TimeSpan.Zero), Expires = new DateTimeOffset(2022, 08, 05, 11, 43, 0, TimeSpan.Zero), }; await UserManagerRepository.AddEmailUserAction(AuditUserDetails, emailUserAction, default); _fakeClock.DateTime = new DateTimeOffset(2022, 08, 06, 11, 43, 0, TimeSpan.Zero); //Act & Assert Assert.ThrowsAsync(async () => { await UserManager.CompleteEmailAction(token); }); } [Test] public async Task CompleteEmailAction_UserEmailDoesNotMatchTheToken_ThrowsInvalidEmailException() { //Arrange var actualUser = new User { Id = 2, Email = "testuser@sun-strategy.com" }; await UserManagerRepository.AddUser(AuditUserDetails, actualUser, default); var emailUserAction = new EmailUserAction { EmailActionType = EmailUserActionType.ConfirmEmailAddress, Token = new Guid("{632B559B-4641-4973-A1C0-38D198816201}"), User = actualUser, UserId = actualUser.Id, Created = new DateTimeOffset(2022, 08, 03, 11, 43, 0, TimeSpan.Zero), Expires = new DateTimeOffset(2022, 08, 05, 11, 43, 0, TimeSpan.Zero), }; await UserManagerRepository.AddEmailUserAction(AuditUserDetails, emailUserAction, default); var token = new EmailActionToken { Email = "evil.hacker@sun-strategy.com", Token = emailUserAction.Token, Password = "P@ssword12345" }; _fakeClock.DateTime = new DateTimeOffset(2022, 08, 04, 11, 43, 0, TimeSpan.Zero); //Act & Assert Assert.ThrowsAsync(async () => { await UserManager.CompleteEmailAction(token); }); } [Test] public async Task CompleteEmailAction_ConfirmEmail_CompletedSuccessfully() { //Arrange var actualUser = new User { Id = 3, Email = "testuser2@sun-strategy.com", EmailConfirmed = false, Password = "aA1!12345789" }; var oldPassword = actualUser.Password; await UserManagerRepository.AddUser(AuditUserDetails, actualUser, default); var token = new EmailActionToken { Email = "testuser2@sun-strategy.com", Token = new Guid("{9275C68F-A88A-4AD5-963B-64678E3ABFF9}"), Password = "Pa55W0rD-3d1ce44c-86ce-4136!b6ad-8b24303e1cfc" }; var emailUserAction = new EmailUserAction { EmailActionType = EmailUserActionType.ConfirmEmailAddress, Token = token.Token, User = actualUser, UserId = actualUser.Id, Created = new DateTimeOffset(2022, 08, 03, 11, 43, 0, TimeSpan.Zero), Expires = new DateTimeOffset(2022, 08, 05, 11, 43, 0, TimeSpan.Zero), }; var hashedPassword = "owekjhrtlkerjthbwerlkjrthbw3"; CustomPasswordHasherMock.Setup(x => x.HashPassword(It.IsAny(), It.IsAny())) .Returns(hashedPassword); await UserManagerRepository.AddEmailUserAction(AuditUserDetails, emailUserAction, default); _fakeClock.DateTime = new DateTimeOffset(2022, 08, 04, 11, 43, 0, TimeSpan.Zero); //Act var exception = Assert.ThrowsAsync(async () => { await UserManager.CompleteEmailAction(token); }); //Assert Assert.That(exception!.Message, Is.EqualTo($"EmailActionType {emailUserAction.EmailActionType} not supported, use the UI instead.")); } [Test] public async Task CompleteEmailAction_ResetPasswordNewPasswordMissing_ThrowsException() { //Arrange var actualUser = new User { Id = 4, Email = "testuser@sun-strategy.com", EmailConfirmed = false, Password = "I don't know aA1!12345789" }; await UserManagerRepository.AddUser(AuditUserDetails, actualUser, default); var token = new EmailActionToken { Email = actualUser.Email, Token = new Guid("{CAA1C1E2-7876-4ACB-A050-8CF94466CC4E}"), }; var oldPassword = actualUser.Password; var emailUserAction = new EmailUserAction { EmailActionType = EmailUserActionType.PasswordReset, Token = token.Token, User = actualUser, UserId = actualUser.Id, Created = new DateTimeOffset(2022, 08, 03, 11, 43, 0, TimeSpan.Zero), Expires = new DateTimeOffset(2022, 08, 05, 11, 43, 0, TimeSpan.Zero), }; await UserManagerRepository.AddEmailUserAction(AuditUserDetails, emailUserAction, default); _fakeClock.DateTime = new DateTimeOffset(2022, 08, 04, 11, 43, 0, TimeSpan.Zero); var hashedPassword = "owekjhrtlkerjthbwerlkjrthbw3"; CustomPasswordHasherMock.Setup(x => x.HashPassword(It.IsAny(), It.IsAny())) .Returns(hashedPassword); //Act var exception = Assert.ThrowsAsync(async () => await UserManager.CompleteEmailAction(token)); //Assert Assert.That(exception?.Message, Is.EqualTo($"Password must be at least 12 characters")); } [Test] public async Task CompleteEmailAction_ResetPasswordNewPasswordIsTooShort_ThrowsException() { //Arrange var actualUser = new User { Id = 4, Email = "testuser@sun-strategy.com", EmailConfirmed = false, Password = "I don't know aA1!12345789" }; await UserManagerRepository.AddUser(AuditUserDetails, actualUser, default); var token = new EmailActionToken { Email = actualUser.Email, Token = new Guid("{CAA1C1E2-7876-4ACB-A050-8CF94466CC4E}"), Password = "test" }; var oldPassword = actualUser.Password; var emailUserAction = new EmailUserAction { EmailActionType = EmailUserActionType.PasswordReset, Token = token.Token, User = actualUser, UserId = actualUser.Id, Created = new DateTimeOffset(2022, 08, 03, 11, 43, 0, TimeSpan.Zero), Expires = new DateTimeOffset(2022, 08, 05, 11, 43, 0, TimeSpan.Zero), }; await UserManagerRepository.AddEmailUserAction(AuditUserDetails, emailUserAction, default); _fakeClock.DateTime = new DateTimeOffset(2022, 08, 04, 11, 43, 0, TimeSpan.Zero); var hashedPassword = "owekjhrtlkerjthbwerlkjrthbw3"; CustomPasswordHasherMock.Setup(x => x.HashPassword(It.IsAny(), It.IsAny())) .Returns(hashedPassword); //Act var exception = Assert.ThrowsAsync(async () => await UserManager.CompleteEmailAction(token)); //Assert Assert.That(exception?.Message, Is.EqualTo("Password must be at least 12 characters")); } [Test] public async Task CompleteEmailAction_ResetPasswordDoesNotContainAValidSymbol_ThrowsException() { //Arrange var actualUser = new User { Id = 4, Email = "testuser@sun-strategy.com", EmailConfirmed = false, Password = "I don't know aA1!12345789" }; await UserManagerRepository.AddUser(AuditUserDetails, actualUser, default); var token = new EmailActionToken { Email = actualUser.Email, Token = new Guid("{CAA1C1E2-7876-4ACB-A050-8CF94466CC4E}"), Password = "testtesttest" }; var oldPassword = actualUser.Password; var emailUserAction = new EmailUserAction { EmailActionType = EmailUserActionType.PasswordReset, Token = token.Token, User = actualUser, UserId = actualUser.Id, Created = new DateTimeOffset(2022, 08, 03, 11, 43, 0, TimeSpan.Zero), Expires = new DateTimeOffset(2022, 08, 05, 11, 43, 0, TimeSpan.Zero), }; await UserManagerRepository.AddEmailUserAction(AuditUserDetails, emailUserAction, default); _fakeClock.DateTime = new DateTimeOffset(2022, 08, 04, 11, 43, 0, TimeSpan.Zero); var hashedPassword = "owekjhrtlkerjthbwerlkjrthbw3"; CustomPasswordHasherMock.Setup(x => x.HashPassword(It.IsAny(), It.IsAny())) .Returns(hashedPassword); //Act var exception = Assert.ThrowsAsync(async () => await UserManager.CompleteEmailAction(token)); //Assert Assert.That(exception?.Message, Is.EqualTo($"Password must contain at least one symbol: ~`! @#$%^&*()_+={{[}}|\\:;\"'<,->.?/")); } [TestCase('~')] [TestCase('`')] [TestCase('!')] [TestCase(' ')] [TestCase('@')] [TestCase('#')] [TestCase('$')] [TestCase('%')] [TestCase('^')] [TestCase('&')] [TestCase('(')] [TestCase(')')] [TestCase('_')] [TestCase('+')] [TestCase('=')] [TestCase('{')] [TestCase('[')] [TestCase('}')] [TestCase('|')] [TestCase('\\')] [TestCase(':')] [TestCase(';')] [TestCase('\"')] [TestCase('\'')] [TestCase('<')] [TestCase(',')] [TestCase('-')] [TestCase('>')] [TestCase('.')] [TestCase('?')] [TestCase('/')] [TestCase('"')] public async Task CompleteEmailAction_ResetPasswordDoesNotContainAValidNumber_ThrowsException(char symbol) { //Arrange var actualUser = new User { Id = 4, Email = "testuser@sun-strategy.com", EmailConfirmed = false, Password = "I don't know aA1!12345789" }; await UserManagerRepository.AddUser(AuditUserDetails, actualUser, default); var token = new EmailActionToken { Email = actualUser.Email, Token = new Guid("{CAA1C1E2-7876-4ACB-A050-8CF94466CC4E}"), Password = $"t{symbol}sttesttest" }; var oldPassword = actualUser.Password; var emailUserAction = new EmailUserAction { EmailActionType = EmailUserActionType.PasswordReset, Token = token.Token, User = actualUser, UserId = actualUser.Id, Created = new DateTimeOffset(2022, 08, 03, 11, 43, 0, TimeSpan.Zero), Expires = new DateTimeOffset(2022, 08, 05, 11, 43, 0, TimeSpan.Zero), }; await UserManagerRepository.AddEmailUserAction(AuditUserDetails, emailUserAction, default); _fakeClock.DateTime = new DateTimeOffset(2022, 08, 04, 11, 43, 0, TimeSpan.Zero); var hashedPassword = "owekjhrtlkerjthbwerlkjrthbw3"; CustomPasswordHasherMock.Setup(x => x.HashPassword(It.IsAny(), It.IsAny())) .Returns(hashedPassword); //Act var exception = Assert.ThrowsAsync(async () => await UserManager.CompleteEmailAction(token)); //Assert Assert.That(exception?.Message, Is.EqualTo($"Password must contain at least one number")); } [TestCase('0')] [TestCase('1')] [TestCase('2')] [TestCase('3')] [TestCase('4')] [TestCase('5')] [TestCase('6')] [TestCase('7')] [TestCase('8')] [TestCase('9')] public async Task CompleteEmailAction_ResetPasswordDoesNotContainAUpperCaseCharacter_ThrowsException(char number) { //Arrange var actualUser = new User { Id = 4, Email = "testuser@sun-strategy.com", EmailConfirmed = false, Password = "I don't know aA1!12345789" }; await UserManagerRepository.AddUser(AuditUserDetails, actualUser, default); var token = new EmailActionToken { Email = actualUser.Email, Token = new Guid("{CAA1C1E2-7876-4ACB-A050-8CF94466CC4E}"), Password = $"t*{number}ttesttest" }; var oldPassword = actualUser.Password; var emailUserAction = new EmailUserAction { EmailActionType = EmailUserActionType.PasswordReset, Token = token.Token, User = actualUser, UserId = actualUser.Id, Created = new DateTimeOffset(2022, 08, 03, 11, 43, 0, TimeSpan.Zero), Expires = new DateTimeOffset(2022, 08, 05, 11, 43, 0, TimeSpan.Zero), }; await UserManagerRepository.AddEmailUserAction(AuditUserDetails, emailUserAction, default); _fakeClock.DateTime = new DateTimeOffset(2022, 08, 04, 11, 43, 0, TimeSpan.Zero); var hashedPassword = "owekjhrtlkerjthbwerlkjrthbw3"; CustomPasswordHasherMock.Setup(x => x.HashPassword(It.IsAny(), It.IsAny())) .Returns(hashedPassword); //Act var exception = Assert.ThrowsAsync(async () => await UserManager.CompleteEmailAction(token)); //Assert Assert.That(exception?.Message, Is.EqualTo($"Password must contain at least one upper case character")); } [Test] public async Task CompleteEmailAction_ResetPasswordDoesNotContainALowerCaseCharacter_ThrowsException() { //Arrange var actualUser = new User { Id = 4, Email = "testuser@sun-strategy.com", EmailConfirmed = false, Password = "I don't know aA1!12345789" }; await UserManagerRepository.AddUser(AuditUserDetails, actualUser, default); var token = new EmailActionToken { Email = actualUser.Email, Token = new Guid("{CAA1C1E2-7876-4ACB-A050-8CF94466CC4E}"), Password = $"T*1TTESTTEST" }; var oldPassword = actualUser.Password; var emailUserAction = new EmailUserAction { EmailActionType = EmailUserActionType.PasswordReset, Token = token.Token, User = actualUser, UserId = actualUser.Id, Created = new DateTimeOffset(2022, 08, 03, 11, 43, 0, TimeSpan.Zero), Expires = new DateTimeOffset(2022, 08, 05, 11, 43, 0, TimeSpan.Zero), }; await UserManagerRepository.AddEmailUserAction(AuditUserDetails, emailUserAction, default); _fakeClock.DateTime = new DateTimeOffset(2022, 08, 04, 11, 43, 0, TimeSpan.Zero); var hashedPassword = "owekjhrtlkerjthbwerlkjrthbw3"; CustomPasswordHasherMock.Setup(x => x.HashPassword(It.IsAny(), It.IsAny())) .Returns(hashedPassword); //Act var exception = Assert.ThrowsAsync(async () => await UserManager.CompleteEmailAction(token)); //Assert Assert.That(exception?.Message, Is.EqualTo($"Password must contain at least one lower case character")); } [Test] public async Task CompleteEmailAction_ResetPassword_CompletedSuccessfully() { //Arrange var actualUser = new User { Id = 5, Email = "testuser@sun-strategy.com", EmailConfirmed = false, Password = "I've forgotten aA1!12345789" }; await UserManagerRepository.AddUser(AuditUserDetails, actualUser, default); var token = new EmailActionToken { Email = actualUser.Email, Token = new Guid("{CAA1C1E2-7876-4ACB-A050-8CF94466CC4E}"), Password = "Pa55W0rD-3d1ce44c-86ce-4136-b6ad-8b24303e1cfc" }; var oldPassword = actualUser.Password; var emailUserAction = new EmailUserAction { EmailActionType = EmailUserActionType.PasswordReset, Token = token.Token, User = actualUser, UserId = actualUser.Id, Created = new DateTimeOffset(2022, 08, 03, 11, 43, 0, TimeSpan.Zero), Expires = new DateTimeOffset(2022, 08, 05, 11, 43, 0, TimeSpan.Zero), }; await UserManagerRepository.AddEmailUserAction(AuditUserDetails, emailUserAction, default); _fakeClock.DateTime = new DateTimeOffset(2022, 08, 04, 11, 43, 0, TimeSpan.Zero); var hashedPassword = "owekjhrtlkerjthbwerlkjrthbw3"; CustomPasswordHasherMock.Setup(x => x.HashPassword(It.IsAny(), It.IsAny())) .Returns(hashedPassword); //Act await UserManager.CompleteEmailAction(token); //Assert var databaseRow = UserManagerRepository.EmailUserActions.SingleOrDefault(x => x.Token == token.Token); Assert.That(databaseRow, Is.Null); var user = UserManagerRepository.Users.Single(x => x.Email == token.Email); Assert.That(user.Password, Is.Not.EqualTo(oldPassword)); Assert.That(user.Password, Is.EqualTo(hashedPassword)); MailServiceMock.Verify(x => x.RequestEMailAsync(It.IsAny(), It.IsAny()), Times.Once); } [Test] public async Task CompleteEmailAction_DisableTwoFactorAuthentivation_CompletedSuccessfully() { //Arrange var actualUser = new User { Id = 6, Email = "testuser3@sun-strategy.com", EmailConfirmed = false, Password = "I've forgotten aA1!12345789", UsingTwoFactorAuthentication = true }; await UserManagerRepository.AddUser(AuditUserDetails, actualUser, default); var emailUserAction = new EmailUserAction { EmailActionType = EmailUserActionType.DisableAuthenticator, Token = new Guid("{9B44A60C-AB66-4161-BF17-EEAC2162EAC2}"), User = actualUser, UserId = actualUser.Id, Created = new DateTimeOffset(2022, 08, 03, 11, 43, 0, TimeSpan.Zero), Expires = new DateTimeOffset(2022, 08, 05, 11, 43, 0, TimeSpan.Zero), }; await UserManagerRepository.AddEmailUserAction(AuditUserDetails, emailUserAction, default); var token = new EmailActionToken { Email = actualUser.Email, Token = emailUserAction.Token }; _fakeClock.DateTime = new DateTimeOffset(2022, 08, 04, 11, 43, 0, TimeSpan.Zero); //Act await UserManager.CompleteEmailAction(token); //Assert var databaseRow = UserManagerRepository.EmailUserActions.SingleOrDefault(x => x.Token == token.Token); Assert.That(databaseRow, Is.Null); var user = UserManagerRepository.Users.Single(x => x.Email == token.Email); Assert.That(user.UsingTwoFactorAuthentication, Is.False); } }