public async Task Alias_IndexPatchOldSameAsNew_Conflict() { using (IdentityWsDbContext ef = CreateEf()) { ef.Aliases.Add(new Alias { EmailAddress = "*****@*****.**", Being = new Being { SaltedHashedPassword = Sha512Util.SaltAndHashNewPassword("password1") } }); await ef.SaveChangesAsync(); AliasesController patient = new AliasesController(ef, dummyLog, now, dummyRunner); AliasesController.IndexPatchRequestBody body = new AliasesController.IndexPatchRequestBody { oldPassword = "******", password = "******" }; IActionResult result = await patient.IndexPatch("*****@*****.**", body); result.Should().BeOfType <StatusCodeResult>() .Which.StatusCode.Should().Be(409, $"'{nameof(body.password)}' must differ from '{nameof(body.oldPassword)}'"); } }
public async Task Alias_IndexPatchMatchingOld_NoContentWithChangedPassword() { using (IdentityWsDbContext ef = CreateEf()) { Being being = new Being { SaltedHashedPassword = Sha512Util.SaltAndHashNewPassword("password1") }; ef.Aliases.Add(new Alias { EmailAddress = "*****@*****.**", Being = being }); await ef.SaveChangesAsync(); AliasesController patient = new AliasesController(ef, dummyLog, now, dummyRunner); AliasesController.IndexPatchRequestBody body = new AliasesController.IndexPatchRequestBody { oldPassword = "******", password = "******" }; IActionResult result = await patient.IndexPatch("*****@*****.**", body); result.Should().BeOfType <NoContentResult>($"'{nameof(body.oldPassword)}' matched"); Sha512Util.TestPassword("p@ssword1", being.SaltedHashedPassword).Should().BeTrue("the password should have been changed"); } }
public async Task Alias_IndexPatchExpiredReset_NotAuthorized() { using (IdentityWsDbContext ef = CreateEf()) { ef.Aliases.Add(new Alias { EmailAddress = "*****@*****.**", Being = new Being { PasswordResetToken = "abracadabra", PasswordResetTokenValidUntil = now.UtcNow, SaltedHashedPassword = Sha512Util.SaltAndHashNewPassword("password1") } }); await ef.SaveChangesAsync(); AliasesController patient = new AliasesController(ef, dummyLog, now, dummyRunner); AliasesController.IndexPatchRequestBody body = new AliasesController.IndexPatchRequestBody { resetToken = "abracadabra", password = "******" }; IActionResult result = await patient.IndexPatch("*****@*****.**", body); result.Should().BeOfType <UnauthorizedResult>($"the reset token is expired"); } }
public async Task BeingAndResetToken_ResetPost_JsonNewResetToken() { string old_tok = "Prev"; using (IdentityWsDbContext ef = CreateEf()) { Being being = new Being { PasswordResetToken = old_tok, PasswordResetTokenValidUntil = now.UtcNow.AddMinutes(2) }; ef.Aliases.Add(new Alias { EmailAddress = "*****@*****.**", Being = being }); await ef.SaveChangesAsync(); AliasesController patient = new AliasesController(ef, dummyLog, now, dummyRunner); IActionResult result = await patient.ResetPost("*****@*****.**"); result.Should().BeOfType <JsonResult>() .Which.Value.Should().BeOfType <Dictionary <string, string> >() .Which["resetToken"].Should().NotBe(old_tok, "a new token should have been generated"); being.PasswordResetTokenValidUntil.Should().Be(now.UtcNow.AddHours(1), "the validity period should also reset"); } }
public async Task Alias_IndexPatchMismatchingOld_NotAuthorized() { using (IdentityWsDbContext ef = CreateEf()) { ef.Aliases.Add(new Alias { EmailAddress = "*****@*****.**", Being = new Being { SaltedHashedPassword = Sha512Util.SaltAndHashNewPassword("password1") } }); await ef.SaveChangesAsync(); AliasesController patient = new AliasesController(ef, dummyLog, now, dummyRunner); AliasesController.IndexPatchRequestBody body = new AliasesController.IndexPatchRequestBody { oldPassword = "******", password = "******" }; IActionResult result = await patient.IndexPatch("*****@*****.**", body); result.Should().BeOfType <UnauthorizedResult>($"'{nameof(body.oldPassword)}' does not match"); } }
public async Task OneAlias_IndexDelete_Forbidden() { using (IdentityWsDbContext ef = CreateEf()) { ef.Beings.Add(new Being { Aliases = new HashSet <Alias> { new Alias { EmailAddress = "*****@*****.**" } } }); await ef.SaveChangesAsync(); AliasesController patient = new AliasesController(ef, dummyLog, now, dummyRunner); IActionResult result = await patient.IndexDelete("*****@*****.**"); result.Should().BeOfType <StatusCodeResult>() .Which.StatusCode.Should().Be(StatusCodes.Status403Forbidden, "a being must not be without an alias"); (await ef.Aliases.CountAsync()).Should().Be(1, "the single alias was not deleted"); } }
public async Task NoBeing_ResetPost_NotFound() { using (IdentityWsDbContext ef = CreateEf()) { AliasesController patient = new AliasesController(ef, dummyLog, now, dummyRunner); IActionResult result = await patient.ResetPost("*****@*****.**"); result.Should().BeOfType <NotFoundResult>("the being does not exist"); } }
public async Task NoAlias_IndexGet_NotFound() { using (IdentityWsDbContext ef = CreateEf()) { AliasesController patient = new AliasesController(ef, dummyLog, now, dummyRunner); IActionResult result = await patient.Index("*****@*****.**"); result.Should().BeOfType <NotFoundResult>("the alias does not exist in the database"); } }
public async Task NoAlias_Confirm_NotFound() { using (IdentityWsDbContext ef = CreateEf()) { AliasesController patient = new AliasesController(ef, dummyLog, now, dummyRunner); IActionResult result = await patient.ConfirmPost("*****@*****.**", new AliasesController.ConfirmPostRequestBody { confirmToken = "abc" }); result.Should().BeOfType <NotFoundResult>("the alias does not exist"); } }
public async Task NoAlias_IndexPatch_NotFound() { using (IdentityWsDbContext ef = CreateEf()) { AliasesController patient = new AliasesController(ef, dummyLog, now, dummyRunner); AliasesController.IndexPatchRequestBody body = new AliasesController.IndexPatchRequestBody { password = "******" }; IActionResult result = await patient.IndexPatch("*****@*****.**", body); result.Should().BeOfType <NotFoundResult>("the alias does not exist in the database"); } }
public async Task Alias_IndexPostWithNeitherPasswordNorEmail_BadRequest() { using (IdentityWsDbContext ef = CreateEf()) { ef.Aliases.Add(new Alias { EmailAddress = "*****@*****.**" }); await ef.SaveChangesAsync(); AliasesController patient = new AliasesController(ef, dummyLog, now, dummyRunner); AliasesController.IndexPostRequestBody body = new AliasesController.IndexPostRequestBody(); IActionResult result = await patient.IndexPost("*****@*****.**", body); result.Should().BeOfType <BadRequestResult>($"exactly one of '{nameof(body.otherEmailAddress)}' or '{nameof(body.password)} is required'"); } }
public async Task UnconfirmedAlias_Index_ConfirmationToken() { using (IdentityWsDbContext ef = CreateEf()) { ef.Aliases.Add(new Alias { EmailAddress = "*****@*****.**" }); await ef.SaveChangesAsync(); AliasesController patient = new AliasesController(ef, dummyLog, now, dummyRunner); IActionResult result = await patient.Index("*****@*****.**"); result.Should().BeOfType <JsonResult>() .Which.Value.Should().BeOfType <Dictionary <string, string> >() .Which["confirmToken"].Should().NotBeNull("the alias has not yet been confirmed"); } }
public async Task WrongAlias_Email_NotFound() { using (IdentityWsDbContext ef = CreateEf()) { Alias alias; ef.Aliases.Add(alias = new Alias { EmailAddress = "*****@*****.**" }); await ef.SaveChangesAsync(); AliasesController patient = new AliasesController(ef, dummyLog, now, dummyRunner); IActionResult result = await patient.Email("*****@*****.**", new AliasesController.EmailRequestBody()); result.Should().BeOfType <NotFoundResult>("the email address doesn't match"); } }
public async Task TwoAliases_IndexDelete_NoContentWithCascadingDeletion() { using (IdentityWsDbContext ef = CreateEf()) { ef.Beings.Add(new Being { Aliases = new HashSet <Alias> { new Alias { EmailAddress = "*****@*****.**", LoginAttempts = new HashSet <LoginAttempt> { new LoginAttempt { ClientName = "Test" } }, Emails = new HashSet <Email> { new Email { From = "*****@*****.**", Subject = "Deletion" } } }, new Alias { EmailAddress = "*****@*****.**" } } }); await ef.SaveChangesAsync(); AliasesController patient = new AliasesController(ef, dummyLog, now, dummyRunner); IActionResult result = await patient.IndexDelete("*****@*****.**"); result.Should().BeOfType <NoContentResult>("the alias may be deleted"); (await ef.Aliases.CountAsync()).Should().Be(1, "one of the aliases should be deleted"); (await ef.Emails.AnyAsync()).Should().Be(false, "the deletion should cascade to emails"); (await ef.LoginAttempts.AnyAsync()).Should().Be(false, "the deletion should cascade to login attempts"); } }
public async Task Alias_IndexPostWithExistingEmail_Conflict() { using (IdentityWsDbContext ef = CreateEf()) { ef.Aliases.Add(new Alias { EmailAddress = "*****@*****.**" }); await ef.SaveChangesAsync(); AliasesController patient = new AliasesController(ef, dummyLog, now, dummyRunner); IActionResult result = await patient.IndexPost("*****@*****.**", new AliasesController.IndexPostRequestBody { password = "******" }); result.Should().BeOfType <StatusCodeResult>().Which.StatusCode.Should().Be(StatusCodes.Status409Conflict, "the alias exists in the database"); } }
public async Task Alias_IndexPostWithPassword_NoContentWithNewAliasAndBeing() { using (IdentityWsDbContext ef = CreateEf()) { ef.Aliases.Add(new Alias { EmailAddress = "*****@*****.**" }); await ef.SaveChangesAsync(); AliasesController patient = new AliasesController(ef, dummyLog, now, dummyRunner); IActionResult result = await patient.IndexPost("*****@*****.**", new AliasesController.IndexPostRequestBody { password = "******" }); result.Should().BeOfType <NoContentResult>("a non-existing email address was supplied along with a password"); ef.Aliases.Include(a => a.Being).Should().Contain(a => a.EmailAddress == "*****@*****.**", "a new alias should have been created") .Which.Being.Should().NotBeNull("a new being should have been created"); } }
public async Task Alias_IndexPostWithWrongOtherEmail_NotFound() { using (IdentityWsDbContext ef = CreateEf()) { ef.Aliases.Add(new Alias { EmailAddress = "*****@*****.**" }); await ef.SaveChangesAsync(); AliasesController patient = new AliasesController(ef, dummyLog, now, dummyRunner); AliasesController.IndexPostRequestBody body = new AliasesController.IndexPostRequestBody { otherEmailAddress = "*****@*****.**" }; IActionResult result = await patient.IndexPost("*****@*****.**", body); result.Should().BeOfType <NotFoundResult>($"the '{nameof(body.otherEmailAddress)}' does not exist"); } }
public async Task UnconfirmedAlias_Confirm_NoContentWithUpdatedDateConfirmed() { using (IdentityWsDbContext ef = CreateEf()) { Alias alias; ef.Aliases.Add(alias = new Alias { EmailAddress = "*****@*****.**" }); await ef.SaveChangesAsync(); AliasesController patient = new AliasesController(ef, dummyLog, now, dummyRunner); IActionResult result = await patient.ConfirmPost("*****@*****.**", new AliasesController.ConfirmPostRequestBody { confirmToken = alias.ConfirmationToken }); result.Should().BeOfType <NoContentResult>("the confirmation token matches"); alias.DateConfirmed.Should().Be(now.UtcNow, "the alias is newly confirmed"); } }
public async Task UnconfirmedAliasWithMismatchingToken_Confirm_Unauthorized() { using (IdentityWsDbContext ef = CreateEf()) { Alias alias; ef.Aliases.Add(alias = new Alias { EmailAddress = "*****@*****.**", ConfirmationToken = "abc" }); await ef.SaveChangesAsync(); AliasesController patient = new AliasesController(ef, dummyLog, now, dummyRunner); IActionResult result = await patient.ConfirmPost("*****@*****.**", new AliasesController.ConfirmPostRequestBody { confirmToken = "αβγ" }); result.Should().BeOfType <UnauthorizedResult>("the confirmation token does not match"); } }
public async Task Alias_IndexPostWithOtherEmail_NoContentWithNewAliasAndLinkedBeing() { using (IdentityWsDbContext ef = CreateEf()) { ef.Aliases.Add(new Alias { EmailAddress = "*****@*****.**", Being = new Being() }); await ef.SaveChangesAsync(); AliasesController patient = new AliasesController(ef, dummyLog, now, dummyRunner); AliasesController.IndexPostRequestBody body = new AliasesController.IndexPostRequestBody { otherEmailAddress = "*****@*****.**" }; IActionResult result = await patient.IndexPost("*****@*****.**", body); result.Should().BeOfType <NoContentResult>("an existing email address (only) was supplied"); ef.Aliases.Include(a => a.Being).Should().Contain(a => a.EmailAddress == "*****@*****.**", "a new alias should have been created") .Which.Being.Aliases.Should().HaveCount(2, "the new alias should have been linked to the existing being"); } }
public async Task ConfirmedAlias_Confirm_NoContentWithNoUpdate() { DateTime yesterday = now.UtcNow.AddDays(-1); using (IdentityWsDbContext ef = CreateEf()) { Alias alias; ef.Aliases.Add(alias = new Alias { EmailAddress = "*****@*****.**", DateConfirmed = yesterday }); await ef.SaveChangesAsync(); AliasesController patient = new AliasesController(ef, dummyLog, now, dummyRunner); IActionResult result = await patient.ConfirmPost("*****@*****.**", new AliasesController.ConfirmPostRequestBody { confirmToken = alias.ConfirmationToken }); result.Should().BeOfType <NoContentResult>("the confirmation token matches"); alias.DateConfirmed.Should().Be(yesterday, "the previous confirmation date should not be overridden"); } }
public async Task RightAlias_Email_NewDbRecordAndNudge() { using (IdentityWsDbContext ef = CreateEf()) { Alias alias; ef.Aliases.Add(alias = new Alias { EmailAddress = "*****@*****.**" }); await ef.SaveChangesAsync(); bool nudged = false; Mock <IBackgroundJobRunner <EmailQueueProcessor> > mock_runner = new Mock <IBackgroundJobRunner <EmailQueueProcessor> >(); mock_runner.Setup(m => m.Nudge()).Callback(() => nudged = true); AliasesController patient = new AliasesController(ef, dummyLog, now, mock_runner.Object); IActionResult result = await patient.Email("*****@*****.**", new AliasesController.EmailRequestBody { from = "*****@*****.**", replyTo = "*****@*****.**", subject = "Testing", bodyText = "Hello, World!" }); Email email = await ef.Emails.FirstAsync(e => e.AliasID == alias.AliasID); result.Should().BeOfType <NoContentResult>("email processing is asynchronous"); email.Should().Match <Email>(e => e.From == "*****@*****.**" && e.ReplyTo == "*****@*****.**" && e.Subject == "Testing" && e.BodyText == "Hello, World!" && !e.SendIfUnconfirmed, "all values should be stored as-is in the database except SendIfUnconfirmed, which defaults to false"); nudged.Should().BeTrue("a new queue run should be triggered sooner rather than later"); } }
public async Task Alias_IndexPatchNoOldPasswordOrResetToken_BadRequest() { using (IdentityWsDbContext ef = CreateEf()) { ef.Aliases.Add(new Alias { EmailAddress = "*****@*****.**", Being = new Being { SaltedHashedPassword = Sha512Util.SaltAndHashNewPassword("password1") } }); await ef.SaveChangesAsync(); AliasesController patient = new AliasesController(ef, dummyLog, now, dummyRunner); AliasesController.IndexPatchRequestBody body = new AliasesController.IndexPatchRequestBody { password = "******" }; IActionResult result = await patient.IndexPatch("*****@*****.**", body); result.Should().BeOfType <BadRequestResult>($"neither {nameof(body.resetToken)} nor {nameof(body.oldPassword)} was provided"); } }