diff --git a/AllReadyApp/Web-App/AllReady.UnitTest/Controllers/AdminControllerTests.cs b/AllReadyApp/Web-App/AllReady.UnitTest/Controllers/AdminControllerTests.cs new file mode 100644 index 000000000..eef536a53 --- /dev/null +++ b/AllReadyApp/Web-App/AllReady.UnitTest/Controllers/AdminControllerTests.cs @@ -0,0 +1,863 @@ +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using AllReady.Controllers; +using AllReady.Extensions; +using AllReady.Models; +using AllReady.Services; +using AllReady.UnitTest.Extensions; +using Microsoft.AspNet.Authorization; +using Microsoft.AspNet.Identity; +using Microsoft.AspNet.Mvc; +using Microsoft.Extensions.OptionsModel; +using Moq; +using Xunit; +using Microsoft.AspNet.Mvc.Routing; +using Microsoft.AspNet.Http; +using Microsoft.AspNet.Mvc.Rendering; + +namespace AllReady.UnitTest.Controllers +{ + public class AdminControllerTests + { + [Fact] + public void RegisterReturnsViewResult() + { + var sut = CreateAdminControllerWithNoInjectedDependencies(); + var result = sut.Register(); + Assert.IsType(result); + } + + [Fact] + public void RegisterHasHttpGetAttribute() + { + var sut = CreateAdminControllerWithNoInjectedDependencies(); + var attribute = sut.GetAttributesOn(x => x.Register()).OfType().SingleOrDefault(); + Assert.NotNull(attribute); + } + + [Fact] + public void RegisterHasAllowAnonymousAttribute() + { + var sut = CreateAdminControllerWithNoInjectedDependencies(); + var attribute = sut.GetAttributesOn(x => x.Register()).OfType().SingleOrDefault(); + Assert.NotNull(attribute); + } + + [Fact] + public async Task RegisterReturnsViewResultWhenModelStateIsNotValid() + { + var sut = CreateAdminControllerWithNoInjectedDependencies(); + sut.AddModelStateError(); + + var result = await sut.Register(It.IsAny()); + + Assert.IsType(result); + } + + [Fact] + public async Task RegisterReturnsCorrectModelWhenModelStateIsNotValid() + { + var model = new RegisterViewModel(); + + var sut = CreateAdminControllerWithNoInjectedDependencies(); + sut.AddModelStateError(); + + var result = await sut.Register(model) as ViewResult; + var modelResult = result.ViewData.Model as RegisterViewModel; + + Assert.IsType(modelResult); + Assert.Same(model, modelResult); + } + + [Fact] + public async Task RegisterInvokesCreateAsyncWithCorrectUserAndPassword() + { + const string defaultTimeZone = "DefaultTimeZone"; + + var model = new RegisterViewModel { Email = "email", Password = "Password" }; + + var generalSettings = new Mock>(); + generalSettings.Setup(x => x.Value).Returns(new GeneralSettings { DefaultTimeZone = defaultTimeZone }); + + var userManager = CreateUserManagerMock(); + userManager.Setup(x => x.CreateAsync(It.IsAny(), It.IsAny())).Returns(() => Task.FromResult(IdentityResult.Failed())); + + var sut = new AdminController(userManager.Object, null, null, null, null, generalSettings.Object); + + await sut.Register(model); + + userManager.Verify(x => x.CreateAsync(It.Is(au => + au.UserName == model.Email && + au.Email == model.Email && + au.TimeZoneId == defaultTimeZone), + model.Password)); + } + + [Fact] + public async Task RegisterInvokesGenerateEmailConfirmationTokenAsyncWithCorrectUserWhenUserCreationIsSuccessful() + { + const string defaultTimeZone = "DefaultTimeZone"; + + var model = new RegisterViewModel { Email = "email", Password = "Password" }; + + var generalSettings = new Mock>(); + generalSettings.Setup(x => x.Value).Returns(new GeneralSettings { DefaultTimeZone = defaultTimeZone }); + + var userManager = CreateUserManagerMock(); + userManager.Setup(x => x.CreateAsync(It.IsAny(), It.IsAny())).Returns(() => Task.FromResult(IdentityResult.Success)); + + var sut = new AdminController(userManager.Object, null, Mock.Of(), null, null, generalSettings.Object); + sut.SetFakeHttpRequestSchemeTo(It.IsAny()); + sut.Url = Mock.Of(); + + await sut.Register(model); + + userManager.Verify(x => x.GenerateEmailConfirmationTokenAsync(It.Is(au => + au.UserName == model.Email && + au.Email == model.Email && + au.TimeZoneId == defaultTimeZone))); + } + + [Fact] + public async Task RegisterInvokesUrlActionWithCorrectParametersWhenUserCreationIsSuccessful() + { + const string requestScheme = "requestScheme"; + + var generalSettings = new Mock>(); + generalSettings.Setup(x => x.Value).Returns(new GeneralSettings()); + + var userManager = CreateUserManagerMock(); + userManager.Setup(x => x.CreateAsync(It.IsAny(), It.IsAny())).Returns(() => Task.FromResult(IdentityResult.Success)); + userManager.Setup(x => x.GenerateEmailConfirmationTokenAsync(It.IsAny())).Returns(() => Task.FromResult(It.IsAny())); + + var sut = new AdminController(userManager.Object, null, Mock.Of(), null, null, generalSettings.Object); + sut.SetFakeHttpRequestSchemeTo(requestScheme); + var urlHelper = new Mock(); + sut.Url = urlHelper.Object; + + await sut.Register(new RegisterViewModel()); + + //note: I can't test the Values part here b/c I do not have control over the Id generation on ApplicationUser b/c it's new'ed up in the controller + urlHelper.Verify(mock => mock.Action(It.Is(uac => + uac.Action == "ConfirmEmail" && + uac.Controller == "Admin" && + uac.Protocol == requestScheme)), + Times.Once); + } + + [Fact] + public async Task RegisterInvokesSendEmailAsyncWithCorrectParametersWhenUserCreationIsSuccessful() + { + const string callbackUrl = "callbackUrl"; + + var model = new RegisterViewModel { Email = "email" }; + + var generalSettings = new Mock>(); + generalSettings.Setup(x => x.Value).Returns(new GeneralSettings()); + + var userManager = CreateUserManagerMock(); + userManager.Setup(x => x.CreateAsync(It.IsAny(), It.IsAny())).Returns(() => Task.FromResult(IdentityResult.Success)); + userManager.Setup(x => x.GenerateEmailConfirmationTokenAsync(It.IsAny())).Returns(() => Task.FromResult(It.IsAny())); + + var urlHelper = new Mock(); + urlHelper.Setup(x => x.Action(It.IsAny())).Returns(callbackUrl); + + var emailSender = new Mock(); + + var sut = new AdminController(userManager.Object, null, emailSender.Object, null, null, generalSettings.Object); + sut.SetFakeHttpRequestSchemeTo(It.IsAny()); + sut.Url = urlHelper.Object; + + await sut.Register(model); + + emailSender.Verify(x => x.SendEmailAsync(model.Email, "Confirm your account", $"Please confirm your account by clicking this link")); + } + + [Fact] + public async Task RegisterRedirectsToCorrectActionWhenUserCreationIsSuccessful() + { + var generalSettings = new Mock>(); + generalSettings.Setup(x => x.Value).Returns(new GeneralSettings()); + + var userManager = CreateUserManagerMock(); + userManager.Setup(x => x.CreateAsync(It.IsAny(), It.IsAny())).Returns(() => Task.FromResult(IdentityResult.Success)); + userManager.Setup(x => x.GenerateEmailConfirmationTokenAsync(It.IsAny())).Returns(() => Task.FromResult(It.IsAny())); + + var urlHelper = new Mock(); + urlHelper.Setup(x => x.Action(It.IsAny())).Returns(It.IsAny()); + + var sut = new AdminController(userManager.Object, null, Mock.Of(), null, null, generalSettings.Object); + sut.SetFakeHttpRequestSchemeTo(It.IsAny()); + sut.Url = urlHelper.Object; + + var result = await sut.Register(new RegisterViewModel()) as RedirectToActionResult; + + Assert.Equal(result.ActionName, nameof(AdminController.DisplayEmail)); + Assert.Equal(result.ControllerName, "Admin"); + } + + [Fact] + public async Task RegisterAddsIdentityResultErrorsToModelStateErrorsWhenUserCreationIsNotSuccessful() + { + var generalSettings = new Mock>(); + generalSettings.Setup(x => x.Value).Returns(new GeneralSettings()); + + var identityResult = IdentityResult.Failed(new IdentityError { Description = "IdentityErrorDescription" }); + + var userManager = CreateUserManagerMock(); + userManager.Setup(x => x.CreateAsync(It.IsAny(), It.IsAny())).Returns(() => Task.FromResult(identityResult)); + + var sut = new AdminController(userManager.Object, null, null, null, null, generalSettings.Object); + sut.SetFakeHttpRequestSchemeTo(It.IsAny()); + + await sut.Register(new RegisterViewModel()); + + var errorMessages = sut.ModelState.GetErrorMessages(); + + Assert.Equal(errorMessages.Single(), identityResult.Errors.Select(x => x.Description).Single()); + } + + [Fact] + public async Task RegisterReturnsViewResultAndCorrectModelWhenUserCreationIsNotSuccessful() + { + var model = new RegisterViewModel(); + + var generalSettings = new Mock>(); + generalSettings.Setup(x => x.Value).Returns(new GeneralSettings()); + + var userManager = CreateUserManagerMock(); + userManager.Setup(x => x.CreateAsync(It.IsAny(), It.IsAny())).Returns(() => Task.FromResult(IdentityResult.Failed())); + + var sut = new AdminController(userManager.Object, null, null, null, null, generalSettings.Object); + sut.SetFakeHttpRequestSchemeTo(It.IsAny()); + + var result = await sut.Register(model) as ViewResult; + var modelResult = result.ViewData.Model as RegisterViewModel; + + Assert.IsType(result); + Assert.IsType(modelResult); + Assert.Same(model, modelResult); + } + + [Fact] + public void DisplayEmailReturnsViewResult() + { + var sut = CreateAdminControllerWithNoInjectedDependencies(); + var result = sut.DisplayEmail(); + Assert.IsType(result); + } + + [Fact] + public void DisplayEmailHasHttpGetAttribute() + { + var sut = CreateAdminControllerWithNoInjectedDependencies(); + var attribute = sut.GetAttributesOn(x => x.DisplayEmail()).OfType().SingleOrDefault(); + Assert.NotNull(attribute); + } + + [Fact] + public void DisplayEmailHasAllowAnonymousAttribute() + { + var sut = CreateAdminControllerWithNoInjectedDependencies(); + var attribute = sut.GetAttributesOn(x => x.DisplayEmail()).OfType().SingleOrDefault(); + Assert.NotNull(attribute); + } + + [Fact] + public async Task ConfirmEmailReturnsErrorWhenCodeIsNull() + { + var sut = CreateAdminControllerWithNoInjectedDependencies(); + var result = await sut.ConfirmEmail(null, null) as ViewResult; + Assert.Equal(result.ViewName, "Error"); + } + + [Fact] + public async Task ConfirmEmailReturnsErrorWhenCannotFindUserByUserId() + { + var userManager = CreateUserManagerMock(); + var sut = new AdminController(userManager.Object, null, null, null, null, null); + var result = await sut.ConfirmEmail(null, "code") as ViewResult; + Assert.Equal(result.ViewName, "Error"); + } + + [Fact] + public async Task ConfirmEmailInvokesFindByIdAsyncWithCorrectUserId() + { + const string userId = "userId"; + var userManager = CreateUserManagerMock(); + var sut = new AdminController(userManager.Object, null, null, null, null, null); + await sut.ConfirmEmail(userId, "code"); + + userManager.Verify(x => x.FindByIdAsync(userId), Times.Once); + } + + [Fact] + public async Task ConfirmEmailInvokesConfirmEmailAsyncWithCorrectUserAndCode() + { + const string code = "code"; + var user = new ApplicationUser(); + + var userManager = CreateUserManagerMock(); + userManager.Setup(x => x.FindByIdAsync(It.IsAny())).Returns(() => Task.FromResult(user)); + userManager.Setup(x => x.ConfirmEmailAsync(It.IsAny(), It.IsAny())).Returns(() => Task.FromResult(IdentityResult.Failed())); + + var sut = new AdminController(userManager.Object, null, null, null, null, null); + await sut.ConfirmEmail(null, code); + + userManager.Verify(x => x.ConfirmEmailAsync(user, code), Times.Once); + } + + [Fact] + public async Task ConfirmEmailInvokesUrlActionWithCorrectParametersWhenUsersEmailIsConfirmedSuccessfully() + { + const string requestScheme = "requestScheme"; + + var userManager = CreateUserManagerMock(); + userManager.Setup(x => x.FindByIdAsync(It.IsAny())).Returns(() => Task.FromResult(new ApplicationUser())); + userManager.Setup(x => x.ConfirmEmailAsync(It.IsAny(), It.IsAny())).Returns(() => Task.FromResult(IdentityResult.Success)); + + var settings = new Mock>(); + settings.Setup(x => x.Value).Returns(new SampleDataSettings()); + + var urlHelper = new Mock(); + + var sut = new AdminController(userManager.Object, null, Mock.Of(), null, settings.Object, null); + sut.SetFakeHttpRequestSchemeTo(requestScheme); + sut.Url = urlHelper.Object; + + await sut.ConfirmEmail(It.IsAny(), "code"); + + //note: I can't test the Values part here b/c I do not have control over the Id generation on ApplicationUser b/c it's new'ed up in the controller + urlHelper.Verify(x => x.Action(It.Is(uac => + uac.Action == "EditUser" && + uac.Controller == "Site" && + uac.Protocol == requestScheme)), + Times.Once); + } + + [Fact] + public async Task ConfirmEmailInvokesSendEmailAsyncWithCorrectParametersWhenUsersEmailIsConfirmedSuccessfully() + { + const string defaultAdminUserName = "requestScheme"; + const string callbackUrl = "callbackUrl"; + + var userManager = CreateUserManagerMock(); + userManager.Setup(x => x.FindByIdAsync(It.IsAny())).Returns(() => Task.FromResult(new ApplicationUser())); + userManager.Setup(x => x.ConfirmEmailAsync(It.IsAny(), It.IsAny())).Returns(() => Task.FromResult(IdentityResult.Success)); + + var settings = new Mock>(); + settings.Setup(x => x.Value).Returns(new SampleDataSettings { DefaultAdminUsername = defaultAdminUserName }); + + var urlHelper = new Mock(); + urlHelper.Setup(x => x.Action(It.IsAny())).Returns(callbackUrl); + + var emailSender = new Mock(); + + var sut = new AdminController(userManager.Object, null, emailSender.Object, null, settings.Object, null); + sut.SetFakeHttpRequestSchemeTo(It.IsAny()); + sut.Url = urlHelper.Object; + + await sut.ConfirmEmail(It.IsAny(), "code"); + + emailSender.Verify(x => x.SendEmailAsync(defaultAdminUserName, "Approve organization user account", + $"Please approve this account by clicking this link")); + } + + [Fact] + public async Task ConfirmEmailReturnsCorrectViewWhenUsersConfirmationIsSuccessful() + { + var userManager = CreateUserManagerMock(); + userManager.Setup(x => x.FindByIdAsync(It.IsAny())).Returns(() => Task.FromResult(new ApplicationUser())); + userManager.Setup(x => x.ConfirmEmailAsync(It.IsAny(), It.IsAny())).Returns(() => Task.FromResult(IdentityResult.Success)); + + var urlHelper = new Mock(); + urlHelper.Setup(x => x.Action(It.IsAny())).Returns(It.IsAny()); + + var settings = new Mock>(); + settings.Setup(x => x.Value).Returns(new SampleDataSettings { DefaultAdminUsername = It.IsAny() }); + + var sut = new AdminController(userManager.Object, null, Mock.Of(), null, settings.Object, null); + sut.SetFakeHttpRequestSchemeTo(It.IsAny()); + sut.Url = urlHelper.Object; + + var result = await sut.ConfirmEmail("userId", "code") as ViewResult; + + Assert.Equal(result.ViewName, "ConfirmEmail"); + } + + [Fact] + public async Task ConfirmEmailReturnsCorrectViewWhenUsersConfirmationIsUnsuccessful() + { + var userManager = CreateUserManagerMock(); + userManager.Setup(x => x.FindByIdAsync(It.IsAny())).Returns(() => Task.FromResult(new ApplicationUser())); + userManager.Setup(x => x.ConfirmEmailAsync(It.IsAny(), It.IsAny())).Returns(() => Task.FromResult(IdentityResult.Failed())); + + var sut = new AdminController(userManager.Object, null, null, null, null, null); + var result = await sut.ConfirmEmail("userId", "code") as ViewResult; + + Assert.Equal(result.ViewName, "Error"); + } + + [Fact] + public void ConfirmEmailHasHttpGetAttribute() + { + var sut = CreateAdminControllerWithNoInjectedDependencies(); + var attribute = sut.GetAttributesOn(x => x.ConfirmEmail(It.IsAny(), It.IsAny())).OfType().SingleOrDefault(); + Assert.NotNull(attribute); + } + + [Fact] + public void ConfirmEmailHasAllowAnonymousAttribute() + { + var sut = CreateAdminControllerWithNoInjectedDependencies(); + var attribute = sut.GetAttributesOn(x => x.ConfirmEmail(It.IsAny(), It.IsAny())).OfType().SingleOrDefault(); + Assert.NotNull(attribute); + } + + [Fact] + public void ForgotPasswordReturnsView() + { + var sut = CreateAdminControllerWithNoInjectedDependencies(); + var result = sut.ForgotPassword(); + Assert.IsType(result); + } + + [Fact] + public void ForgotPasswordHasHttpGetAttribute() + { + var sut = CreateAdminControllerWithNoInjectedDependencies(); + var attribute = sut.GetAttributesOn(x => x.ForgotPassword()).OfType().SingleOrDefault(); + Assert.NotNull(attribute); + } + + [Fact] + public void ForgotPasswordHasAllowAnonymousAttribute() + { + var sut = CreateAdminControllerWithNoInjectedDependencies(); + var attribute = sut.GetAttributesOn(x => x.ForgotPassword()).OfType().SingleOrDefault(); + Assert.NotNull(attribute); + } + + [Fact] + public async Task SendCodeGetInvokesGetTwoFactorAuthenticationUserAsync() + { + var signInManager = CreateSignInManagerMock(); + var sut = new AdminController(null, signInManager.Object, null, null, null, null); + await sut.SendCode(It.IsAny(), It.IsAny()); + + signInManager.Verify(x => x.GetTwoFactorAuthenticationUserAsync(), Times.Once); + } + + [Fact] + public async Task SendCodeGetReturnsErrorViewWhenCannotFindUser() + { + var signInManager = CreateSignInManagerMock(); + var sut = new AdminController(null, signInManager.Object, null, null, null, null); + var result = await sut.SendCode(null, It.IsAny()) as ViewResult; + + Assert.Equal(result.ViewName, "Error"); + } + + [Fact] + public async Task SendCodeGetInvokesGetValidTwoFactorProvidersAsyncWithCorrectUser() + { + var applicationUser = new ApplicationUser(); + + var userManager = CreateUserManagerMock(); + var signInManager = CreateSignInManagerMock(userManager); + + signInManager.Setup(x => x.GetTwoFactorAuthenticationUserAsync()).Returns(() => Task.FromResult(applicationUser)); + userManager.Setup(x => x.GetValidTwoFactorProvidersAsync(It.IsAny())).ReturnsAsync(new List()); + + var sut = new AdminController(userManager.Object, signInManager.Object, null, null, null, null); + + await sut.SendCode(null, It.IsAny()); + + userManager.Verify(x => x.GetValidTwoFactorProvidersAsync(applicationUser), Times.Once); + } + + [Fact] + public async Task SendCodeGetReturnsSendCodeViewModelWithCorrectData() + { + const string returnUrl = "returnUrl"; + const bool rememberMe = true; + + var userFactors = new List { "userFactor1", "userFactor2" }; + var expectedProviders = userFactors.Select(factor => new SelectListItem { Text = factor, Value = factor }).ToList(); + + var userManager = CreateUserManagerMock(); + var signInManager = CreateSignInManagerMock(userManager); + + signInManager.Setup(x => x.GetTwoFactorAuthenticationUserAsync()).Returns(() => Task.FromResult(new ApplicationUser())); + userManager.Setup(x => x.GetValidTwoFactorProvidersAsync(It.IsAny())).ReturnsAsync(userFactors); + + var sut = new AdminController(userManager.Object, signInManager.Object, null, null, null, null); + + var result = await sut.SendCode(returnUrl, rememberMe) as ViewResult; + var modelResult = result.ViewData.Model as SendCodeViewModel; + + Assert.Equal(modelResult.ReturnUrl, returnUrl); + Assert.Equal(modelResult.RememberMe, rememberMe); + Assert.Equal(expectedProviders, modelResult.Providers, new SelectListItemComparer()); + } + + [Fact] + public void SendCodeGetHasHttpGetAttribute() + { + var sut = CreateAdminControllerWithNoInjectedDependencies(); + var attribute = sut.GetAttributesOn(x => x.SendCode(It.IsAny(), It.IsAny())).OfType().SingleOrDefault(); + Assert.NotNull(attribute); + } + + [Fact] + public void SendCodeGetHasAllowAnonymousAttribute() + { + var sut = CreateAdminControllerWithNoInjectedDependencies(); + var attribute = sut.GetAttributesOn(x => x.SendCode(It.IsAny(), It.IsAny())).OfType().SingleOrDefault(); + Assert.NotNull(attribute); + } + + [Fact] + public async Task SendCodePostWithInvalidModelStateReturnsView() + { + var sut = CreateAdminControllerWithNoInjectedDependencies(); + sut.AddModelStateError(); + var result = await sut.SendCode(It.IsAny()); + Assert.IsType(result); + } + + [Fact] + public async Task SendCodePostInvokesGetTwoFactorAuthenticationUserAsync() + { + var signInManager = CreateSignInManagerMock(); + var sut = new AdminController(null, signInManager.Object, null, null, null, null); + await sut.SendCode(It.IsAny()); + + signInManager.Verify(x => x.GetTwoFactorAuthenticationUserAsync(), Times.Once); + } + + [Fact] + public async Task SendCodePosReturnsErrorViewWhenUserIsNotFound() + { + var signInManager = CreateSignInManagerMock(); + + var sut = new AdminController(null, signInManager.Object, null, null, null, null); + var result = await sut.SendCode(It.IsAny()) as ViewResult; + + Assert.Equal(result.ViewName, "Error"); + } + + [Fact] + public async Task SendCodePostInvokesGenerateTwoFactorTokenAsyncWithCorrectUserAndTokenProvider() + { + var applicationUser = new ApplicationUser(); + var model = new SendCodeViewModel { SelectedProvider = "Email" }; + + var userManager = CreateUserManagerMock(); + + var signInManager = CreateSignInManagerMock(userManager); + signInManager.Setup(x => x.GetTwoFactorAuthenticationUserAsync()).ReturnsAsync(applicationUser); + + var sut = new AdminController(userManager.Object, signInManager.Object, null, null, null, null); + await sut.SendCode(model); + + userManager.Verify(x => x.GenerateTwoFactorTokenAsync(applicationUser, model.SelectedProvider), Times.Once); + } + + [Fact] + public async Task SendCodePostReturnsErrorViewWhenAuthenticationTokenIsNull() + { + var userManager = CreateUserManagerMock(); + var signInManager = CreateSignInManagerMock(userManager); + + signInManager.Setup(x => x.GetTwoFactorAuthenticationUserAsync()).ReturnsAsync(new ApplicationUser()); + + var sut = new AdminController(userManager.Object, signInManager.Object, null, null, null, null); + var result = await sut.SendCode(new SendCodeViewModel()) as ViewResult; + + Assert.Equal(result.ViewName, "Error"); + } + + [Fact] + public async Task SendCodePostInvokesSendEmailAsyncWithCorrectParametersWhenSelectedProviderIsEmail() + { + const string token = "token"; + const string usersEmailAddress = "usersEmailAddress"; + var message = $"Your security code is: {token}"; + + var applicationUser = new ApplicationUser(); + var model = new SendCodeViewModel { SelectedProvider = "Email" }; + + var userManager = CreateUserManagerMock(); + var signInManager = CreateSignInManagerMock(userManager); + var emailSender = new Mock(); + + userManager.Setup(x => x.GenerateTwoFactorTokenAsync(It.IsAny(), It.IsAny())).ReturnsAsync(token); + userManager.Setup(x => x.GetEmailAsync(applicationUser)).ReturnsAsync(usersEmailAddress); + signInManager.Setup(x => x.GetTwoFactorAuthenticationUserAsync()).ReturnsAsync(applicationUser); + + var sut = new AdminController(userManager.Object, signInManager.Object, emailSender.Object, null, null, null); + await sut.SendCode(model); + + emailSender.Verify(x => x.SendEmailAsync(usersEmailAddress, "Security Code", message)); + } + + [Fact] + public async Task SendCodePostInvokesSendSmsAsyncWithCorrectParametersWhenSelectedProviderIsPhone() + { + const string token = "token"; + const string usersPhoneNumber = "usersPhoneNumber"; + var message = $"Your security code is: {token}"; + + var applicationUser = new ApplicationUser(); + var model = new SendCodeViewModel { SelectedProvider = "Phone" }; + + var userManager = CreateUserManagerMock(); + var signInManager = CreateSignInManagerMock(userManager); + var smsSender = new Mock(); + + userManager.Setup(x => x.GenerateTwoFactorTokenAsync(It.IsAny(), It.IsAny())).ReturnsAsync(token); + userManager.Setup(x => x.GetPhoneNumberAsync(applicationUser)).ReturnsAsync(usersPhoneNumber); + signInManager.Setup(x => x.GetTwoFactorAuthenticationUserAsync()).ReturnsAsync(applicationUser); + + var sut = new AdminController(userManager.Object, signInManager.Object, null, smsSender.Object, null, null); + await sut.SendCode(model); + + smsSender.Verify(x => x.SendSmsAsync(usersPhoneNumber, message)); + } + + [Fact] + public async Task SendCodePostReturnsRedirectToActionResult() + { + var model = new SendCodeViewModel { SelectedProvider = string.Empty, ReturnUrl = "ReturnUrl", RememberMe = true }; + + var routeValues = new Dictionary + { + ["Provider"] = model.SelectedProvider, + ["ReturnUrl"] = model.ReturnUrl, + ["RememberMe"] = model.RememberMe + }; + + var userManager = CreateUserManagerMock(); + var signInManager = CreateSignInManagerMock(userManager); + + signInManager.Setup(x => x.GetTwoFactorAuthenticationUserAsync()).ReturnsAsync(new ApplicationUser()); + userManager.Setup(x => x.GenerateTwoFactorTokenAsync(It.IsAny(), It.IsAny())).ReturnsAsync("token"); + + var sut = new AdminController(userManager.Object, signInManager.Object, null, null, null, null); + var result = await sut.SendCode(model) as RedirectToActionResult; + + Assert.Equal(result.ActionName, nameof(AdminController.VerifyCode)); + Assert.Equal(result.RouteValues, routeValues); + } + + [Fact] + public void SendCodePostGetHasHttpPostAttribute() + { + var sut = CreateAdminControllerWithNoInjectedDependencies(); + var attribute = sut.GetAttributesOn(x => x.SendCode(It.IsAny())).OfType().SingleOrDefault(); + Assert.NotNull(attribute); + } + + [Fact] + public void SendCodePostHasAllowAnonymousAttribute() + { + var sut = CreateAdminControllerWithNoInjectedDependencies(); + var attribute = sut.GetAttributesOn(x => x.SendCode(It.IsAny())).OfType().SingleOrDefault(); + Assert.NotNull(attribute); + } + + [Fact] + public void SendCodePostHasValidateAntiForgeryTokenAttribute() + { + var sut = CreateAdminControllerWithNoInjectedDependencies(); + var attribute = sut.GetAttributesOn(x => x.SendCode(It.IsAny())).OfType().SingleOrDefault(); + Assert.NotNull(attribute); + } + + [Fact] + public async Task VerifyCodeGetInvokesGetTwoFactorAuthenticationUserAsync() + { + var signInManager = CreateSignInManagerMock(); + var sut = new AdminController(null, signInManager.Object, null, null, null, null); + await sut.VerifyCode(It.IsAny(), It.IsAny(), It.IsAny()); + + signInManager.Verify(x => x.GetTwoFactorAuthenticationUserAsync(), Times.Once); + } + + [Fact] + public async Task VerifyCodeGetReturnsErrorViewWhenUserIsNull() + { + var signInManager = CreateSignInManagerMock(); + var sut = new AdminController(null, signInManager.Object, null, null, null, null); + var result = await sut.VerifyCode(It.IsAny(), It.IsAny(), It.IsAny()) as ViewResult; + + Assert.Equal(result.ViewName, "Error"); + } + + [Fact] + public async Task VerifyCodeGetReturnsCorrectViewModel() + { + const string provider = "provider"; + const bool rememberMe = true; + const string returnUrl = "returnUrl"; + + var signInManager = CreateSignInManagerMock(); + signInManager.Setup(x => x.GetTwoFactorAuthenticationUserAsync()).ReturnsAsync(new ApplicationUser()); + + var sut = new AdminController(null, signInManager.Object, null, null, null, null); + + var result = await sut.VerifyCode(provider, rememberMe, returnUrl) as ViewResult; + var modelResult = result.ViewData.Model as VerifyCodeViewModel; + + Assert.Equal(modelResult.Provider, provider); + Assert.Equal(modelResult.ReturnUrl, returnUrl); + Assert.Equal(modelResult.RememberMe, rememberMe); + } + + [Fact] + public void VerifyCodeGetHasAllowAnonymousAttribute() + { + var sut = CreateAdminControllerWithNoInjectedDependencies(); + var attribute = sut.GetAttributesOn(x => x.VerifyCode(It.IsAny(), It.IsAny(), It.IsAny())).OfType().SingleOrDefault(); + Assert.NotNull(attribute); + } + + [Fact] + public void VerifyCodeGetHasHttpGetAttribute() + { + var sut = CreateAdminControllerWithNoInjectedDependencies(); + var attribute = sut.GetAttributesOn(x => x.VerifyCode(It.IsAny(), It.IsAny(), It.IsAny())).OfType().SingleOrDefault(); + Assert.NotNull(attribute); + } + + [Fact] + public async Task VerifyCodePostReturnsReturnsCorrectViewAndCorrectModelWhenModelStateIsInvalid() + { + var sut = CreateAdminControllerWithNoInjectedDependencies(); + sut.AddModelStateError(); + var result = await sut.VerifyCode(new VerifyCodeViewModel()) as ViewResult; + var modelResult = result.ViewData.Model as VerifyCodeViewModel; + + Assert.IsType(result); + Assert.IsType(modelResult); + } + + [Fact] + public async Task VerifyCodePostInvokesTwoFactorSignInAsyncWithCorrectParameters() + { + var model = new VerifyCodeViewModel + { + Provider = "provider", + Code = "code", + RememberBrowser = true, + RememberMe = true + }; + + var signInManager = CreateSignInManagerMock(); + signInManager.Setup(x => x.TwoFactorSignInAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())).ReturnsAsync(new SignInResult()); + + var sut = new AdminController(null, signInManager.Object, null, null, null, null); + await sut.VerifyCode(model); + + signInManager.Verify(x => x.TwoFactorSignInAsync(model.Provider, model.Code, model.RememberMe, model.RememberBrowser)); + } + + [Fact] + public async Task VerifyCodePostAddsErrorMessageToModelStateErrorWhenTwoFactorSignInAsyncIsNotSuccessful() + { + var signInManager = CreateSignInManagerMock(); + signInManager.Setup(x => x.TwoFactorSignInAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())).ReturnsAsync(new SignInResult()); + + var sut = new AdminController(null, signInManager.Object, null, null, null, null); + await sut.VerifyCode(new VerifyCodeViewModel()); + + var errorMessage = sut.ModelState.GetErrorMessages().Single(); + Assert.Equal(errorMessage, "Invalid code."); + } + + [Fact] + public async Task VerifyCodePostReturnsLockoutViewIfTwoFactorSignInAsyncFailsAndIsLockedOut() + { + var signInManager = CreateSignInManagerMock(); + signInManager.Setup(x => x.TwoFactorSignInAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())).ReturnsAsync(SignInResult.LockedOut); + + var sut = new AdminController(null, signInManager.Object, null, null, null, null); + var result = await sut.VerifyCode(new VerifyCodeViewModel()) as ViewResult; + + Assert.Equal(result.ViewName, "Lockout"); + } + + [Fact] + public async Task VerifyCodePostRedirectsToReturnUrlWhenTwoFactorSignInAsyncSucceedsAndReturnUrlIsLocalUrl() + { + var model = new VerifyCodeViewModel { ReturnUrl = "returnUrl" }; + + var signInManager = CreateSignInManagerMock(); + signInManager.Setup(x => x.TwoFactorSignInAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())).ReturnsAsync(SignInResult.Success); + + var urlHelper = new Mock(); + urlHelper.Setup(x => x.IsLocalUrl(model.ReturnUrl)).Returns(true); + + var sut = new AdminController(null, signInManager.Object, null, null, null, null) { Url = urlHelper.Object }; + var result = await sut.VerifyCode(model) as RedirectResult; + + Assert.Equal(result.Url, model.ReturnUrl); + } + + [Fact] + public async Task VerifyCodePostRedirectsToHomeControllerIndexWhenTwoFactorSignInAsyncSucceedsAndReturnUrlIsNotLocalUrl() + { + var signInManager = CreateSignInManagerMock(); + signInManager.Setup(x => x.TwoFactorSignInAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())).ReturnsAsync(SignInResult.Success); + + var urlHelper = new Mock(); + urlHelper.Setup(x => x.IsLocalUrl(It.IsAny())).Returns(false); + + var sut = new AdminController(null, signInManager.Object, null, null, null, null) { Url = urlHelper.Object }; + var result = await sut.VerifyCode(new VerifyCodeViewModel()) as RedirectToActionResult; + + Assert.Equal(result.ActionName, nameof(HomeController.Index)); + Assert.Equal(result.ControllerName, "Home"); + } + + [Fact] + public void VerifyCodePostHasAllowAnonymousAttribute() + { + var sut = CreateAdminControllerWithNoInjectedDependencies(); + var attribute = sut.GetAttributesOn(x => x.VerifyCode(It.IsAny())).OfType().SingleOrDefault(); + Assert.NotNull(attribute); + } + + [Fact] + public void VerifyCodePostHasHttpPostAttribute() + { + var sut = CreateAdminControllerWithNoInjectedDependencies(); + var attribute = sut.GetAttributesOn(x => x.VerifyCode(It.IsAny())).OfType().SingleOrDefault(); + Assert.NotNull(attribute); + } + + [Fact] + public void VerifyCodePostHasValidateAntiForgeryTokenAttribute() + { + var sut = CreateAdminControllerWithNoInjectedDependencies(); + var attribute = sut.GetAttributesOn(x => x.VerifyCode(It.IsAny())).OfType().SingleOrDefault(); + Assert.NotNull(attribute); + } + + private static Mock> CreateUserManagerMock() => + new Mock>(Mock.Of>(), null, null, null, null, null, null, null, null, null); + + private static Mock> CreateSignInManagerMock(IMock> userManagerMock = null) + { + var contextAccessor = new Mock(); + contextAccessor.Setup(mock => mock.HttpContext).Returns(Mock.Of); + + return new Mock>(userManagerMock == null ? CreateUserManagerMock().Object : userManagerMock.Object, + contextAccessor.Object, Mock.Of>(), null, null); + } + + private static AdminController CreateAdminControllerWithNoInjectedDependencies() => new AdminController(null, null, null, null, null, null); + } +} diff --git a/AllReadyApp/Web-App/AllReady.UnitTest/Extensions/ControllerTestHelpers.cs b/AllReadyApp/Web-App/AllReady.UnitTest/Extensions/ControllerTestHelpers.cs index 7212d6570..8d9e44b43 100644 --- a/AllReadyApp/Web-App/AllReady.UnitTest/Extensions/ControllerTestHelpers.cs +++ b/AllReadyApp/Web-App/AllReady.UnitTest/Extensions/ControllerTestHelpers.cs @@ -82,6 +82,12 @@ private static void SetFakeHttpContextIfNotAlreadySet(Controller controller) controller.SetFakeHttpContext(); } + public static Mock SetFakeIUrlHelper(this Controller controller) + { + controller.Url = new Mock().Object; + return Mock.Get(controller.Url); + } + private static HttpContext FakeHttpContext() { var mockHttpContext = new Mock(); diff --git a/AllReadyApp/Web-App/AllReady.UnitTest/Extensions/EnumerableExtensionsTests.cs b/AllReadyApp/Web-App/AllReady.UnitTest/Extensions/EnumerableExtensionsTests.cs index 7ed35acab..8540bb54e 100644 --- a/AllReadyApp/Web-App/AllReady.UnitTest/Extensions/EnumerableExtensionsTests.cs +++ b/AllReadyApp/Web-App/AllReady.UnitTest/Extensions/EnumerableExtensionsTests.cs @@ -1,7 +1,6 @@ using System.Collections.Generic; using System.Linq; using Xunit; -using AllReady.Extensions; namespace AllReady.UnitTest.Extensions { diff --git a/AllReadyApp/Web-App/AllReady.UnitTest/SelectListItemComparer.cs b/AllReadyApp/Web-App/AllReady.UnitTest/SelectListItemComparer.cs new file mode 100644 index 000000000..d1f12f673 --- /dev/null +++ b/AllReadyApp/Web-App/AllReady.UnitTest/SelectListItemComparer.cs @@ -0,0 +1,30 @@ +using System.Collections.Generic; +using Microsoft.AspNet.Mvc.Rendering; + +namespace AllReady.UnitTest +{ + public class SelectListItemComparer : IEqualityComparer + { + public bool Equals(SelectListItem x, SelectListItem y) + { + if (ReferenceEquals(x, y)) + return true; + + if (ReferenceEquals(x, null) || ReferenceEquals(y, null)) + return false; + + return x.Text.Equals(y.Text) && x.Value.Equals(y.Value); + } + + public int GetHashCode(SelectListItem obj) + { + if (ReferenceEquals(obj, null)) + return 0; + + var hashText = obj.Text?.GetHashCode() ?? 0; + var hashValue = obj.Value.GetHashCode(); + + return hashText ^ hashValue; + } + } +} \ No newline at end of file diff --git a/AllReadyApp/Web-App/AllReady/Controllers/AccountController.cs b/AllReadyApp/Web-App/AllReady/Controllers/AccountController.cs index 45fcfd562..19735158d 100644 --- a/AllReadyApp/Web-App/AllReady/Controllers/AccountController.cs +++ b/AllReadyApp/Web-App/AllReady/Controllers/AccountController.cs @@ -53,6 +53,7 @@ public IActionResult Login(string returnUrl = null) public async Task Login(LoginViewModel model, string returnUrl = null) { ViewData["ReturnUrl"] = returnUrl; + if (ModelState.IsValid) { // Require admin users to have a confirmed email before they can log on. @@ -77,19 +78,20 @@ public async Task Login(LoginViewModel model, string returnUrl = { return RedirectToLocal(returnUrl, user); } + if (result.RequiresTwoFactor) { return RedirectToAction(nameof(AdminController.SendCode), new { ReturnUrl = returnUrl, RememberMe = model.RememberMe }); } + if (result.IsLockedOut) { return View("Lockout"); } - else - { - ModelState.AddModelError(string.Empty, "Invalid login attempt."); - return View(model); - } + + ModelState.AddModelError(string.Empty, "Invalid login attempt."); + + return View(model); } // If we got this far, something failed, redisplay form @@ -123,10 +125,9 @@ public async Task Register(RegisterViewModel model) var result = await _userManager.CreateAsync(user, model.Password); if (result.Succeeded) { - // For more information on how to enable account confirmation and password reset please visit http://go.microsoft.com/fwlink/?LinkID=532713 // Send an email with this link - var code = await _userManager.GenerateEmailConfirmationTokenAsync(user); - var callbackUrl = Url.Action("ConfirmEmail", "Account", new { userId = user.Id, code = code }, protocol: HttpContext.Request.Scheme); + var token = await _userManager.GenerateEmailConfirmationTokenAsync(user); + var callbackUrl = Url.Action("ConfirmEmail", "Account", new { userId = user.Id, token = token }, protocol: HttpContext.Request.Scheme); await _emailSender.SendEmailAsync(model.Email, "Confirm your allReady account", "Please confirm your allReady account by clicking this link: link"); await _userManager.AddClaimAsync(user, new Claim(Security.ClaimTypes.ProfileIncomplete, "NewUser")); @@ -153,27 +154,28 @@ public async Task LogOff() // GET: /Account/ConfirmEmail [HttpGet] [AllowAnonymous] - public async Task ConfirmEmail(string userId, string code) + public async Task ConfirmEmail(string userId, string token) { - if (userId == null || code == null) + if (userId == null || token == null) { return View("Error"); } + var user = await _userManager.FindByIdAsync(userId); if (user == null) { return View("Error"); } - var result = await _userManager.ConfirmEmailAsync(user, code); + + var result = await _userManager.ConfirmEmailAsync(user, token); if (result.Succeeded && user.IsProfileComplete()) { await _mediator.SendAsync(new RemoveUserProfileIncompleteClaimCommand { UserId = user.Id }); if (User.IsSignedIn()) - { await _signInManager.RefreshSignInAsync(user); - } } + return View(result.Succeeded ? "ConfirmEmail" : "Error"); } @@ -235,18 +237,22 @@ public async Task ResetPassword(ResetPasswordViewModel model) { return View(model); } + var user = await _userManager.FindByNameAsync(model.Email); if (user == null) { // Don't reveal that the user does not exist return RedirectToAction(nameof(ResetPasswordConfirmation), "Account"); } + var result = await _userManager.ResetPasswordAsync(user, model.Code, model.Password); if (result.Succeeded) { return RedirectToAction(nameof(ResetPasswordConfirmation), "Account"); } + AddErrors(result); + return View(); } @@ -259,7 +265,6 @@ public IActionResult ResetPasswordConfirmation() return View(); } - // // POST: /Account/ExternalLogin [HttpPost] @@ -294,13 +299,12 @@ public async Task ExternalLoginCallback(string returnUrl = null) var user = await _mediator.SendAsync(new ApplicationUserQuery { UserName = email }); return RedirectToLocal(returnUrl, user); } - else - { - // If the user does not have an account, then ask the user to create an account. - ViewData["ReturnUrl"] = returnUrl; - ViewData["LoginProvider"] = info.LoginProvider; - return View("ExternalLoginConfirmation", new ExternalLoginConfirmationViewModel { Email = email }); - } + + // If the user does not have an account, then ask the user to create an account. + ViewData["ReturnUrl"] = returnUrl; + ViewData["LoginProvider"] = info.LoginProvider; + + return View("ExternalLoginConfirmation", new ExternalLoginConfirmationViewModel { Email = email }); } // @@ -351,8 +355,6 @@ public async Task ExternalLoginConfirmation(ExternalLoginConfirma return View(model); } - - #region Helpers private void AddErrors(IdentityResult result) @@ -363,11 +365,6 @@ private void AddErrors(IdentityResult result) } } - private async Task GetCurrentUserAsync() - { - return await _userManager.FindByIdAsync(HttpContext.User.GetUserId()); - } - private IActionResult RedirectToLocal(string returnUrl, ApplicationUser user) { if (Url.IsLocalUrl(returnUrl)) @@ -379,16 +376,15 @@ private IActionResult RedirectToLocal(string returnUrl, ApplicationUser user) { return RedirectToAction(nameof(SiteController.Index), "Site", new { area = "Admin" }); } - else if (user.IsUserType(UserType.OrgAdmin)) - { - return base.RedirectToAction(nameof(Areas.Admin.Controllers.CampaignController.Index), "Campaign", new { area = "Admin" }); - } - else + + if (user.IsUserType(UserType.OrgAdmin)) { - return RedirectToAction(nameof(HomeController.Index), "Home"); + return RedirectToAction(nameof(Areas.Admin.Controllers.CampaignController.Index), "Campaign", new { area = "Admin" }); } + + return RedirectToAction(nameof(HomeController.Index), "Home"); } #endregion } -} +} \ No newline at end of file diff --git a/AllReadyApp/Web-App/AllReady/Controllers/AdminController.cs b/AllReadyApp/Web-App/AllReady/Controllers/AdminController.cs index 93106b332..b337a85ac 100644 --- a/AllReadyApp/Web-App/AllReady/Controllers/AdminController.cs +++ b/AllReadyApp/Web-App/AllReady/Controllers/AdminController.cs @@ -1,14 +1,13 @@ using System.Linq; -using System.Security.Claims; using System.Threading.Tasks; using AllReady.Areas.Admin.Controllers; using AllReady.Models; using AllReady.Services; -using MediatR; using Microsoft.AspNet.Authorization; using Microsoft.AspNet.Identity; using Microsoft.AspNet.Mvc; using Microsoft.AspNet.Mvc.Rendering; +using Microsoft.AspNet.Mvc.Routing; using Microsoft.Extensions.OptionsModel; namespace AllReady.Controllers @@ -19,8 +18,8 @@ public class AdminController : Controller private readonly SignInManager _signInManager; private readonly IEmailSender _emailSender; private readonly ISmsSender _smsSender; - private readonly SampleDataSettings _settings; - private readonly GeneralSettings _generalSettings; + private readonly IOptions _settings; + private readonly IOptions _generalSettings; public AdminController( UserManager userManager, @@ -34,8 +33,8 @@ public AdminController( _signInManager = signInManager; _emailSender = emailSender; _smsSender = smsSender; - _settings = options.Value; - _generalSettings = generalSettings.Value; + _settings = options; + _generalSettings = generalSettings; } // @@ -60,19 +59,22 @@ public async Task Register(RegisterViewModel model) { UserName = model.Email, Email = model.Email, - TimeZoneId = _generalSettings.DefaultTimeZone + TimeZoneId = _generalSettings.Value.DefaultTimeZone }; + var result = await _userManager.CreateAsync(user, model.Password); if (result.Succeeded) { - var code = await _userManager.GenerateEmailConfirmationTokenAsync(user); - var callbackUrl = Url.Action("ConfirmEmail", "Admin", new { userId = user.Id, code = code }, protocol: HttpContext.Request.Scheme); - await _emailSender.SendEmailAsync(model.Email, "Confirm your account", - "Please confirm your account by clicking this link"); - return RedirectToAction(nameof(AdminController.DisplayEmail), "Admin"); + var token = await _userManager.GenerateEmailConfirmationTokenAsync(user); + //note: had to change Url.Action to take a UrlActionContext b/c "Url.Action" is really an extension method that eventually uses UrlActionContext, and extension methods are not mockable + var callbackUrl = Url.Action(new UrlActionContext { Action = nameof(ConfirmEmail), Controller = "Admin", Values = new { userId = user.Id, token = token }, Protocol = Request.Scheme }); + await _emailSender.SendEmailAsync(model.Email, "Confirm your account", $"Please confirm your account by clicking this link"); + return RedirectToAction(nameof(DisplayEmail), "Admin"); } + AddErrors(result); } + // If we got this far, something failed, redisplay form return View(model); } @@ -88,28 +90,27 @@ public IActionResult DisplayEmail() // GET: /Admin/ConfirmEmail [HttpGet] [AllowAnonymous] - public async Task ConfirmEmail(string userId, string code) + public async Task ConfirmEmail(string userId, string token) { - if (userId == null || code == null) + if (token == null) { return View("Error"); } + var user = await _userManager.FindByIdAsync(userId); if (user == null) { return View("Error"); } - var result = await _userManager.ConfirmEmailAsync(user, code); + + var result = await _userManager.ConfirmEmailAsync(user, token); // If the account confirmation was successful, then send the SiteAdmin an email to approve // this user as a Organization Admin if (result.Succeeded) { - var callbackUrl = Url.Action(nameof(SiteController.EditUser), "Site", new { area = "Admin", userId = user.Id }, protocol: HttpContext.Request.Scheme); - await _emailSender.SendEmailAsync( - _settings.DefaultAdminUsername, - "Approve organization user account", - "Please approve this account by clicking this link"); + var callbackUrl = Url.Action(new UrlActionContext { Action = nameof(SiteController.EditUser), Controller = "Site", Values = new { area = "Admin", userId = user.Id }, Protocol = HttpContext.Request.Scheme }); + await _emailSender.SendEmailAsync(_settings.Value.DefaultAdminUsername, "Approve organization user account", $"Please approve this account by clicking this link"); } return View(result.Succeeded ? "ConfirmEmail" : "Error"); @@ -134,6 +135,7 @@ public async Task SendCode(string returnUrl = null, bool rememberM { return View("Error"); } + var userFactors = await _userManager.GetValidTwoFactorProvidersAsync(user); var factorOptions = userFactors.Select(purpose => new SelectListItem { Text = purpose, Value = purpose }).ToList(); return View(new SendCodeViewModel { Providers = factorOptions, ReturnUrl = returnUrl, RememberMe = rememberMe }); @@ -149,21 +151,21 @@ public async Task SendCode(SendCodeViewModel model) { return View(); } - + var user = await _signInManager.GetTwoFactorAuthenticationUserAsync(); if (user == null) { return View("Error"); } - + // Generate the token and send it - var code = await _userManager.GenerateTwoFactorTokenAsync(user, model.SelectedProvider); - if (string.IsNullOrWhiteSpace(code)) + var token = await _userManager.GenerateTwoFactorTokenAsync(user, model.SelectedProvider); + if (string.IsNullOrWhiteSpace(token)) { return View("Error"); } - - var message = "Your security code is: " + code; + + var message = $"Your security code is: {token}"; if (model.SelectedProvider == "Email") { await _emailSender.SendEmailAsync(await _userManager.GetEmailAsync(user), "Security Code", message); @@ -173,7 +175,7 @@ public async Task SendCode(SendCodeViewModel model) await _smsSender.SendSmsAsync(await _userManager.GetPhoneNumberAsync(user), message); } - return RedirectToAction(nameof(VerifyCode), new { Provider = model.SelectedProvider, ReturnUrl = model.ReturnUrl, RememberMe = model.RememberMe }); + return RedirectToAction(nameof(VerifyCode), new { Provider = model.SelectedProvider, model.ReturnUrl, model.RememberMe }); } // GET: /Admin/VerifyCode @@ -187,6 +189,7 @@ public async Task VerifyCode(string provider, bool rememberMe, st { return View("Error"); } + return View(new VerifyCodeViewModel { Provider = provider, ReturnUrl = returnUrl, RememberMe = rememberMe }); } @@ -197,9 +200,7 @@ public async Task VerifyCode(string provider, bool rememberMe, st public async Task VerifyCode(VerifyCodeViewModel model) { if (!ModelState.IsValid) - { return View(model); - } // The following code protects for brute force attacks against the two factor codes. // If a user enters incorrect codes for a specified amount of time then the user account @@ -207,20 +208,21 @@ public async Task VerifyCode(VerifyCodeViewModel model) var result = await _signInManager.TwoFactorSignInAsync(model.Provider, model.Code, model.RememberMe, model.RememberBrowser); if (result.Succeeded) { - return RedirectToLocal(model.ReturnUrl); + if (Url.IsLocalUrl(model.ReturnUrl)) + return Redirect(model.ReturnUrl); + + return RedirectToAction(nameof(HomeController.Index), "Home"); } + if (result.IsLockedOut) { return View("Lockout"); } - else - { - ModelState.AddModelError("", "Invalid code."); - return View(model); - } - } + + ModelState.AddModelError("", "Invalid code."); - #region Helpers + return View(model); + } private void AddErrors(IdentityResult result) { @@ -229,24 +231,5 @@ private void AddErrors(IdentityResult result) ModelState.AddModelError(string.Empty, error.Description); } } - - private async Task GetCurrentUserAsync() - { - return await _userManager.FindByIdAsync(HttpContext.User.GetUserId()); - } - - private IActionResult RedirectToLocal(string returnUrl) - { - if (Url.IsLocalUrl(returnUrl)) - { - return Redirect(returnUrl); - } - else - { - return RedirectToAction(nameof(HomeController.Index), "Home"); - } - } - - #endregion } } \ No newline at end of file diff --git a/AllReadyApp/Web-App/AllReady/Controllers/ManageController.cs b/AllReadyApp/Web-App/AllReady/Controllers/ManageController.cs index f7d30e1ed..ae2fe5a65 100644 --- a/AllReadyApp/Web-App/AllReady/Controllers/ManageController.cs +++ b/AllReadyApp/Web-App/AllReady/Controllers/ManageController.cs @@ -255,7 +255,7 @@ public async Task DisableTwoFactorAuthentication() // GET: /Account/VerifyPhoneNumber [HttpGet] public IActionResult VerifyPhoneNumber(string phoneNumber) - { + { // Send an SMS to verify the phone number return phoneNumber == null ? View(ERROR_VIEW) : View(new VerifyPhoneNumberViewModel { PhoneNumber = phoneNumber }); }