From f2294f693af94e27782197ff1a47a53871b5073f Mon Sep 17 00:00:00 2001 From: James Gunn Date: Fri, 3 Nov 2023 09:23:22 +0000 Subject: [PATCH] Prevent users who have prohibitions accessing services (#754) --- .../AuthenticationState.cs | 1 + .../Controllers/AuthorizationController.cs | 25 +++++++++++++- .../Pages/SignIn/Complete.cshtml | 16 +++++++-- .../Pages/SignIn/Complete.cshtml.cs | 33 ++++++++++++++++--- .../src/TeacherIdentity.TestClient/Program.cs | 6 ++++ .../Views/Home/Index.cshtml | 2 +- .../EndpointTests/SignIn/CompleteTests.cs | 8 ++--- 7 files changed, 78 insertions(+), 13 deletions(-) diff --git a/dotnet-authserver/src/TeacherIdentity.AuthServer/AuthenticationState.cs b/dotnet-authserver/src/TeacherIdentity.AuthServer/AuthenticationState.cs index c6e5956b4..4b3310010 100644 --- a/dotnet-authserver/src/TeacherIdentity.AuthServer/AuthenticationState.cs +++ b/dotnet-authserver/src/TeacherIdentity.AuthServer/AuthenticationState.cs @@ -759,6 +759,7 @@ public OAuthAuthorizationState(string clientId, string scope, string? redirectUr public string? RedirectUri { get; } public TrnRequirementType? TrnRequirementType { get; init; } public TrnMatchPolicy? TrnMatchPolicy { get; set; } + public bool? BlockProhibitedTeachers { get; set; } public string ResolveServiceUrl(Application application) { diff --git a/dotnet-authserver/src/TeacherIdentity.AuthServer/Controllers/AuthorizationController.cs b/dotnet-authserver/src/TeacherIdentity.AuthServer/Controllers/AuthorizationController.cs index 84d05152a..39306f4f5 100644 --- a/dotnet-authserver/src/TeacherIdentity.AuthServer/Controllers/AuthorizationController.cs +++ b/dotnet-authserver/src/TeacherIdentity.AuthServer/Controllers/AuthorizationController.cs @@ -93,6 +93,7 @@ public async Task Authorize() TrnRequirementType? trnRequirementType = null; TrnMatchPolicy? trnMatchPolicy = null; + bool? blockProhibitedTeachers = null; if (userRequirements.HasFlag(UserRequirements.TrnHolder)) { @@ -140,10 +141,31 @@ public async Task Authorize() })); } } + + var requestedBlockProhibitedTeachers = request["block_prohibited_teachers"]; + if (requestedBlockProhibitedTeachers.HasValue) + { + if (bool.TryParse(requestedBlockProhibitedTeachers?.Value as string, out var parsedBlockProhibitedTeachers)) + { + blockProhibitedTeachers = parsedBlockProhibitedTeachers; + } + else + { + return Forbid( + authenticationSchemes: AuthenticationSchemes.Oidc, + properties: new AuthenticationProperties(new Dictionary() + { + [OpenIddictServerAspNetCoreConstants.Properties.Error] = Errors.InvalidRequest, + [OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] = + "Invalid block_prohibited_teachers specified." + })); + } + } } trnRequirementType ??= client.TrnRequirementType; trnMatchPolicy ??= client.TrnMatchPolicy; + blockProhibitedTeachers ??= client.BlockProhibitedTeachers; } var sessionId = request["session_id"]?.Value as string; @@ -157,7 +179,8 @@ public async Task Authorize() oAuthState: new OAuthAuthorizationState(request.ClientId!, request.Scope!, request.RedirectUri) { TrnRequirementType = trnRequirementType, - TrnMatchPolicy = trnMatchPolicy + TrnMatchPolicy = trnMatchPolicy, + BlockProhibitedTeachers = blockProhibitedTeachers }, authenticateResult.Succeeded != true); diff --git a/dotnet-authserver/src/TeacherIdentity.AuthServer/Pages/SignIn/Complete.cshtml b/dotnet-authserver/src/TeacherIdentity.AuthServer/Pages/SignIn/Complete.cshtml index 4dccef1e2..7f07ebdc8 100644 --- a/dotnet-authserver/src/TeacherIdentity.AuthServer/Pages/SignIn/Complete.cshtml +++ b/dotnet-authserver/src/TeacherIdentity.AuthServer/Pages/SignIn/Complete.cshtml @@ -5,7 +5,7 @@ @{ ViewBag.Title = Model.TrnVerificationElevationSuccessful == true ? "The information you gave has been verified" : Model.TrnVerificationElevationSuccessful == false ? "The information you gave could not be verified" : - !Model.CanAccessService ? "You cannot access this service yet" : + !Model.CanAccessService ? $"You cannot {Model.ClientDisplayName} online" : Model.FirstTimeSignInForEmail ? "You’ve created a DfE Identity account" : "You’ve signed in to your DfE Identity account"; @@ -50,7 +50,19 @@ } else if (Model.TrnRequirementType == TrnRequirementType.Required) { - if (Model.TrnLookupStatus == TrnLookupStatus.Found) + if (Model.TrnLookupStatus == TrnLookupStatus.Found && !Model.CanAccessService) + { + if (Model.FirstTimeSignInForEmail) + { +

You’ve created a DfE Identity account but cannot use it for this service.

+ } + +

+ Email @(Configuration["SupportEmail"]) + to find out how to @Model.ClientDisplayName. +

+ } + else if (Model.TrnLookupStatus == TrnLookupStatus.Found) { if (Model.FirstTimeSignInForEmail) { diff --git a/dotnet-authserver/src/TeacherIdentity.AuthServer/Pages/SignIn/Complete.cshtml.cs b/dotnet-authserver/src/TeacherIdentity.AuthServer/Pages/SignIn/Complete.cshtml.cs index 7b45ab2f1..73edcd037 100644 --- a/dotnet-authserver/src/TeacherIdentity.AuthServer/Pages/SignIn/Complete.cshtml.cs +++ b/dotnet-authserver/src/TeacherIdentity.AuthServer/Pages/SignIn/Complete.cshtml.cs @@ -5,6 +5,7 @@ using TeacherIdentity.AuthServer.Journeys; using TeacherIdentity.AuthServer.Models; using TeacherIdentity.AuthServer.Oidc; +using TeacherIdentity.AuthServer.Services.DqtApi; using TeacherIdentity.AuthServer.State; namespace TeacherIdentity.AuthServer.Pages.SignIn; @@ -15,15 +16,18 @@ public class CompleteModel : PageModel private readonly SignInJourney _journey; private readonly TeacherIdentityApplicationManager _applicationManager; private readonly TeacherIdentityServerDbContext _dbContext; + private readonly IDqtApiClient _dqtApiClient; public CompleteModel( SignInJourney journey, TeacherIdentityApplicationManager applicationManager, - TeacherIdentityServerDbContext dbContext) + TeacherIdentityServerDbContext dbContext, + IDqtApiClient dqtApiClient) { _journey = journey; _applicationManager = applicationManager; _dbContext = dbContext; + _dqtApiClient = dqtApiClient; } public string? Email { get; set; } @@ -60,22 +64,41 @@ public async Task OnGet() RedirectUri = authenticationState.OAuthState.RedirectUri; ResponseMode = authenticationState.OAuthState.AuthorizationResponseMode!; ResponseParameters = authenticationState.OAuthState.AuthorizationResponseParameters!; - CanAccessService = authenticationState.OAuthState?.TrnRequirementType != Models.TrnRequirementType.Required || authenticationState.Trn is not null; Email = authenticationState.EmailAddress; FirstTimeSignInForEmail = authenticationState.FirstTimeSignInForEmail!.Value; Trn = authenticationState.Trn; TrnVerificationElevationSuccessful = authenticationState.TrnVerificationElevationSuccessful; TrnLookupStatus = authenticationState.TrnLookupStatus; - TrnRequirementType = authenticationState.OAuthState?.TrnRequirementType; - TrnMatchPolicy = authenticationState.OAuthState?.TrnMatchPolicy; + TrnRequirementType = authenticationState.OAuthState.TrnRequirementType; + TrnMatchPolicy = authenticationState.OAuthState.TrnMatchPolicy; var user = await _dbContext.Users.SingleAsync(u => u.UserId == authenticationState.UserId); TrnLookupSupportTicketCreated = user?.TrnLookupSupportTicketCreated == true; - var clientId = authenticationState.OAuthState?.ClientId; + var clientId = authenticationState.OAuthState.ClientId; var client = await _applicationManager.FindByClientIdAsync(clientId!); ClientDisplayName = await _applicationManager.GetDisplayNameAsync(client!); + CanAccessService = authenticationState.OAuthState.TrnRequirementType switch + { + Models.TrnRequirementType.Required => authenticationState.Trn is not null, + _ => true + }; + + if (authenticationState.OAuthState.TrnRequirementType == Models.TrnRequirementType.Required && + authenticationState.OAuthState.BlockProhibitedTeachers == true && + authenticationState.Trn is string trn && + CanAccessService) + { + var dqtUser = await _dqtApiClient.GetTeacherByTrn(trn) ?? + throw new Exception($"Failed to retreive teacher with TRN {trn} from DQT."); + + if (dqtUser.Alerts.Any(a => a.AlertType == AlertType.Prohibition)) + { + CanAccessService = false; + } + } + HttpContext.Features.Get()?.Event.AddTag(FirstTimeSignInForEmail ? "FirstTimeUser" : "ReturningUser"); } diff --git a/dotnet-authserver/src/TeacherIdentity.TestClient/Program.cs b/dotnet-authserver/src/TeacherIdentity.TestClient/Program.cs index ae0b929a5..a610dddbb 100644 --- a/dotnet-authserver/src/TeacherIdentity.TestClient/Program.cs +++ b/dotnet-authserver/src/TeacherIdentity.TestClient/Program.cs @@ -81,6 +81,12 @@ public static void Main(string[] args) ctx.ProtocolMessage.SetParameter("trn_match_policy", trnMatchPolicy); } + var blockProhibitedTeachers = ctx.HttpContext.Request.Query["block_prohibited_teachers"].ToString(); + if (!string.IsNullOrEmpty(blockProhibitedTeachers)) + { + ctx.ProtocolMessage.SetParameter("block_prohibited_teachers", blockProhibitedTeachers); + } + var trnToken = ctx.HttpContext.Request.Query["trn_token"].ToString(); if (!string.IsNullOrEmpty(trnToken)) { diff --git a/dotnet-authserver/src/TeacherIdentity.TestClient/Views/Home/Index.cshtml b/dotnet-authserver/src/TeacherIdentity.TestClient/Views/Home/Index.cshtml index f72063c1e..f76ea1e29 100644 --- a/dotnet-authserver/src/TeacherIdentity.TestClient/Views/Home/Index.cshtml +++ b/dotnet-authserver/src/TeacherIdentity.TestClient/Views/Home/Index.cshtml @@ -7,7 +7,7 @@
  • - + Core + TRN lookup (Access your teaching qualifications)
  • diff --git a/dotnet-authserver/tests/TeacherIdentity.AuthServer.Tests/EndpointTests/SignIn/CompleteTests.cs b/dotnet-authserver/tests/TeacherIdentity.AuthServer.Tests/EndpointTests/SignIn/CompleteTests.cs index d6475ac40..16ff00c1a 100644 --- a/dotnet-authserver/tests/TeacherIdentity.AuthServer.Tests/EndpointTests/SignIn/CompleteTests.cs +++ b/dotnet-authserver/tests/TeacherIdentity.AuthServer.Tests/EndpointTests/SignIn/CompleteTests.cs @@ -234,7 +234,7 @@ public async Task Get_ValidRequest_RendersExpectedContent( false, new[] { - "You cannot access this service yet", + $"You cannot {TestClients.DefaultClient.DisplayName} online", "This could be because you do not have teaching qualifications, for example, qualified teacher status (QTS).", "You’ve created a DfE Identity account. When you’re eligible to use this service, you can sign in just using your email" } @@ -250,7 +250,7 @@ public async Task Get_ValidRequest_RendersExpectedContent( true, new[] { - "You cannot access this service yet", + $"You cannot {TestClients.DefaultClient.DisplayName} online", "We need to do some more checks to see if your details are in our records.", "We’ll email you when we’ve completed those checks - we may need some more information.", "You’ve created a DfE Identity account. To sign in to this service in the future, you’ll just need your email address" @@ -282,7 +282,7 @@ public async Task Get_ValidRequest_RendersExpectedContent( false, new[] { - "You cannot access this service yet", + $"You cannot {TestClients.DefaultClient.DisplayName} online", "This could be because you do not have teaching qualifications, for example, qualified teacher status (QTS).", "You’ve signed in to your DfE Identity account" } @@ -313,7 +313,7 @@ public async Task Get_ValidRequest_RendersExpectedContent( true, new[] { - "You cannot access this service yet", + $"You cannot {TestClients.DefaultClient.DisplayName} online", "We need to do some more checks to see if your details are in our records.", "We’ll email you when we’ve completed those checks - we may need some more information.", "You’ve signed in to your DfE Identity account"