Skip to content

Commit

Permalink
Allow users trn verification level to elevated
Browse files Browse the repository at this point in the history
  • Loading branch information
MrKevJoy committed Oct 18, 2023
1 parent ce4382b commit 03a285d
Show file tree
Hide file tree
Showing 4 changed files with 219 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
@page "/admin/users/{userId}/elevate"
@model TeacherIdentity.AuthServer.Pages.Admin.ElevateUserTrnVerificationModel
@{
ViewBag.Title = "Confirm TRN verification level elevation";
}

@section BeforeContent {
<govuk-back-link asp-page="SelectUser" asp-route-userId="@Model.UserId" />
}

<div class="govuk-grid-row">
<div class="govuk-grid-column-two-thirds-from-desktop">
<form asp-page="Confirm" method="post">
<h1 class="govuk-heading-l">@ViewBag.Title</h1>

<govuk-summary-list>
<govuk-summary-list-row>
<govuk-summary-list-row-key>Email address</govuk-summary-list-row-key>
<govuk-summary-list-row-value>@Model.User!.EmailAddress</govuk-summary-list-row-value>
</govuk-summary-list-row>
<govuk-summary-list-row>
<govuk-summary-list-row-key>Name</govuk-summary-list-row-key>
<govuk-summary-list-row-value>@Model.User!.FirstName @Model.User!.LastName</govuk-summary-list-row-value>
</govuk-summary-list-row>
<govuk-summary-list-row>
<govuk-summary-list-row-key>TRN</govuk-summary-list-row-key>
<govuk-summary-list-row-value>@(Model.User!.Trn ?? "None")</govuk-summary-list-row-value>
</govuk-summary-list-row>
</govuk-summary-list>

<govuk-button type="submit">Continue</govuk-button>
</form>
</div>
</div>

Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.AspNetCore.Mvc.RazorPages;
using TeacherIdentity.AuthServer.Infrastructure.Security;
using TeacherIdentity.AuthServer.Models;

namespace TeacherIdentity.AuthServer.Pages.Admin;

[Authorize(AuthorizationPolicies.GetAnIdentityAdmin)]
public class ElevateUserTrnVerificationModel : PageModel
{
private readonly TeacherIdentityServerDbContext _dbContext;
private readonly IClock _clock;
public new User? User { get; set; }
[FromRoute]
public Guid UserId { get; set; }

public ElevateUserTrnVerificationModel(TeacherIdentityServerDbContext dbContext, IClock clock)
{
_dbContext = dbContext;
_clock = clock;
}

public void OnGet()
{
}

public async Task<IActionResult> OnPost()
{
if (!ModelState.IsValid)
{
return this.PageWithErrors();
}

User!.TrnVerificationLevel = TrnVerificationLevel.Medium;
_dbContext.AddEvent(new Events.UserUpdatedEvent
{
Source = Events.UserUpdatedEventSource.SupportUi,
CreatedUtc = _clock.UtcNow,
Changes = Events.UserUpdatedEventChanges.TrnVerificationLevel,
User = User,
UpdatedByUserId = HttpContext.User.GetUserId(),
UpdatedByClientId = null
});
await _dbContext.SaveChangesAsync();

TempData.SetFlashSuccess("TRN verification level elevated");

return RedirectToPage("/Admin/User", new { UserId });
}

public override async Task OnPageHandlerExecutionAsync(PageHandlerExecutingContext context, PageHandlerExecutionDelegate next)
{
User = await GetUser(UserId);

if (User == null)
{
context.Result = NotFound();
return;
}

if (User.EffectiveVerificationLevel != TrnVerificationLevel.Low)
{
context.Result = BadRequest();
return;
}

await next();
}

private async Task<User?> GetUser(Guid userId)
{
var user = await _dbContext.Users.SingleOrDefaultAsync(u => u.UserId == userId);

return (user is null || user.UserType != UserType.Default) ? null : user;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,9 @@
<govuk-summary-list-row>
<govuk-summary-list-row-key>TRN verification level</govuk-summary-list-row-key>
<govuk-summary-list-row-value><govuk-tag class="govuk-tag--yellow">Low</govuk-tag></govuk-summary-list-row-value>
<govuk-summary-list-row-actions>
<govuk-summary-list-row-action asp-page="ElevateUserTrnVerification" asp-route-userId="@Model.UserId" visually-hidden-text="elevate trn verification level">Elevate</govuk-summary-list-row-action>
</govuk-summary-list-row-actions>
</govuk-summary-list-row>
}
else if(Model.EffectiveVerificationLevel == TrnVerificationLevel.Medium)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
using Microsoft.EntityFrameworkCore;
using TeacherIdentity.AuthServer.Events;
using TeacherIdentity.AuthServer.Models;

namespace TeacherIdentity.AuthServer.Tests.EndpointTests.Admin;

public class ElevateUserTrnVerificationTests : TestBase
{
public ElevateUserTrnVerificationTests(HostFixture hostFixture)
: base(hostFixture)
{
}

[Fact]
public async Task Get_UnauthenticatedUser_RedirectsToSignIn()
{
var user = await TestData.CreateUser(userType: UserType.Default, hasTrn: false);

await UnauthenticatedUser_RedirectsToSignIn(HttpMethod.Get, $"/admin/users/{user.UserId}/elevate");
}

[Fact]
public async Task Get_AuthenticatedUserDoesNotHavePermission_ReturnsForbidden()
{
var user = await TestData.CreateUser(userType: UserType.Default, hasTrn: false);

await AuthenticatedUserDoesNotHavePermission_ReturnsForbidden(HttpMethod.Get, $"/admin/users/{user.UserId}/elevate");
}

[Fact]
public async Task Get_UserDoesNotExist_ReturnsNotFound()
{
// Arrange
var userId = Guid.NewGuid();
var _ = await TestData.CreateUser(userType: UserType.Default, hasTrn: true);
var request = new HttpRequestMessage(HttpMethod.Get, $"/admin/users/{userId}/elevate");

// Act
var response = await HttpClient.SendAsync(request);

// Assert
Assert.Equal(StatusCodes.Status404NotFound, (int)response.StatusCode);
}

[Fact]
public async Task Post_UserDoesNotExist_ReturnsNotFound()
{
// Arrange
var userId = Guid.NewGuid();
var request = new HttpRequestMessage(HttpMethod.Post, $"/admin/users/{userId}/elevate");

// Act
var response = await HttpClient.SendAsync(request);

// Assert
Assert.Equal(StatusCodes.Status404NotFound, (int)response.StatusCode);
}

[Fact]
public async Task Post_UserTrnVerificationLevelNotLow_ReturnsBadRequest()
{
// Arrange
var user = await TestData.CreateUser(userType: UserType.Default, hasTrn: true, trnVerificationLevel: TrnVerificationLevel.Medium);
var request = new HttpRequestMessage(HttpMethod.Post, $"/admin/users/{user.UserId}/elevate");

// Act
var response = await HttpClient.SendAsync(request);

// Assert
Assert.Equal(StatusCodes.Status400BadRequest, (int)response.StatusCode);
}

[Fact]
public async Task Post_UserWithLowTrnVerificationLevel_UpdatesVerificationLevelRedirectsToUsers()
{
// Arrange
var user = await TestData.CreateUser(userType: UserType.Default, hasTrn: true, trnVerificationLevel: TrnVerificationLevel.Low);
var request = new HttpRequestMessage(HttpMethod.Post, $"/admin/users/{user.UserId}/elevate");

// Act
var response = await HttpClient.SendAsync(request);

// Assert
Assert.Equal(StatusCodes.Status302Found, (int)response.StatusCode);
Assert.StartsWith($"/admin/users", response.Headers.Location?.OriginalString);
await TestData.WithDbContext(async dbContext =>
{
var fetchedUser= await dbContext.Users.IgnoreQueryFilters().Where(u => u.UserId == user.UserId).SingleOrDefaultAsync();
Assert.NotNull(fetchedUser);
Assert.Equal(TrnVerificationLevel.Medium, fetchedUser.TrnVerificationLevel);
});

EventObserver.AssertEventsSaved(
e =>
{
var userChangedEvent = Assert.IsType<UserUpdatedEvent>(e);
Assert.Equal(Events.UserUpdatedEventChanges.TrnVerificationLevel, userChangedEvent.Changes);
Assert.Equal(user.UserId, userChangedEvent.User.UserId);
Assert.Equal(Clock.UtcNow, userChangedEvent.CreatedUtc);
});
}
}

0 comments on commit 03a285d

Please sign in to comment.