diff --git a/Dfe.PrepareConversions/Dfe.PrepareConversions/Startup.cs b/Dfe.PrepareConversions/Dfe.PrepareConversions/Startup.cs index faf5b32cd..fc3690b86 100644 --- a/Dfe.PrepareConversions/Dfe.PrepareConversions/Startup.cs +++ b/Dfe.PrepareConversions/Dfe.PrepareConversions/Startup.cs @@ -1,227 +1,227 @@ -using Dfe.Academisation.CorrelationIdMiddleware; -using Dfe.PrepareConversions.Authorization; -using Dfe.PrepareConversions.Configuration; -using Dfe.PrepareConversions.Data.Features; -using Dfe.PrepareConversions.Data.Models; -using Dfe.PrepareConversions.Data.Services; -using Dfe.PrepareConversions.Data.Services.AzureAd; -using Dfe.PrepareConversions.Data.Services.Interfaces; -using Dfe.PrepareConversions.Models; -using Dfe.PrepareConversions.Routing; -using Dfe.PrepareConversions.Security; -using Dfe.PrepareConversions.Services; -using Microsoft.AspNetCore.Authentication.Cookies; -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.CookiePolicy; -using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.HttpOverrides; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Logging; -using Microsoft.FeatureManagement; -using Microsoft.Identity.Web; -using Microsoft.Identity.Web.UI; -using System; -using System.Security.Claims; -using System.Threading.Tasks; - -namespace Dfe.PrepareConversions; - -public class Startup -{ - private readonly TimeSpan _authenticationExpiration; - - public Startup(IConfiguration configuration) - { - Configuration = configuration; - - _authenticationExpiration = TimeSpan.FromMinutes(int.Parse(Configuration["AuthenticationExpirationInMinutes"] ?? "60")); - } - - private IConfiguration Configuration { get; } - - private IConfigurationSection GetConfigurationSectionFor() - { - string sectionName = typeof(T).Name.Replace("Options", string.Empty); - return Configuration.GetRequiredSection(sectionName); - } - - private T GetTypedConfigurationFor() - { - return GetConfigurationSectionFor().Get(); - } - - public void ConfigureServices(IServiceCollection services) - { - services.AddFeatureManagement(); - services.AddApplicationInsightsTelemetry(); - services.AddHealthChecks(); - services - .AddRazorPages(options => - { - options.Conventions.AuthorizeFolder("/"); - options.Conventions.AllowAnonymousToPage("/public/maintenance"); - options.Conventions.AllowAnonymousToPage("/public/accessibility"); - }) - .AddViewOptions(options => - { - options.HtmlHelperOptions.ClientValidationEnabled = false; - }).AddMvcOptions(options => - { - options.MaxModelValidationErrors = 50; - options.Filters.Add(new MaintenancePageFilter(Configuration)); - }); - - services.AddControllersWithViews() - .AddMicrosoftIdentityUI(); - - services.AddScoped(sp => sp.GetService()?.HttpContext?.Session); - services.AddSession(options => - { - options.IdleTimeout = _authenticationExpiration; - options.Cookie.Name = ".ManageAnAcademyConversion.Session"; - options.Cookie.IsEssential = true; - options.Cookie.HttpOnly = true; - - if (string.IsNullOrWhiteSpace(Configuration["CI"])) - options.Cookie.SecurePolicy = CookieSecurePolicy.Always; - }); - services.AddHttpContextAccessor(); - - services.AddAuthorization(options => { options.DefaultPolicy = SetupAuthorizationPolicyBuilder().Build(); }); - - services.AddMicrosoftIdentityWebAppAuthentication(Configuration); - services.Configure(CookieAuthenticationDefaults.AuthenticationScheme, - options => - { - options.AccessDeniedPath = "/access-denied"; - options.Cookie.Name = ".ManageAnAcademyConversion.Login"; - options.Cookie.HttpOnly = true; - options.Cookie.IsEssential = true; - options.ExpireTimeSpan = _authenticationExpiration; - options.SlidingExpiration = true; - - if (string.IsNullOrEmpty(Configuration["CI"])) - options.Cookie.SecurePolicy = CookieSecurePolicy.Always; - }); - - services.AddScoped(); - services.AddSingleton(); - - services.AddHttpClient(DfeHttpClientFactory.TramsClientName, (sp, client) => - { - TramsApiOptions tramsApiOptions = GetTypedConfigurationFor(); - client.BaseAddress = new Uri(tramsApiOptions.Endpoint); - client.DefaultRequestHeaders.Add("ApiKey", tramsApiOptions.ApiKey); - - }); - - services.AddHttpClient(DfeHttpClientFactory.AcademisationClientName, (sp, client) => - { - AcademisationApiOptions apiOptions = GetTypedConfigurationFor(); - client.BaseAddress = new Uri(apiOptions.BaseUrl); - client.DefaultRequestHeaders.Add("x-api-key", apiOptions.ApiKey); - }); - - services.Configure(GetConfigurationSectionFor()); - services.Configure(GetConfigurationSectionFor()); - - services.AddScoped(); - services.AddScoped(); - services.Decorate(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.Decorate(); - services.AddScoped(); - services.AddSingleton(); - services.AddSingleton(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - - // Initialize the TransfersUrl - var serviceLinkOptions = Configuration.GetSection("ServiceLink").Get(); - Links.InitializeTransfersUrl(serviceLinkOptions.TransfersUrl); - - } - - public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILogger logger) - { - if (env.IsDevelopment()) - { - app.UseDeveloperExceptionPage(); - } - else - { - app.UseExceptionHandler("/Errors"); - // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. - app.UseHsts(); - } - - app.UseSecurityHeaders( - SecurityHeadersDefinitions.GetHeaderPolicyCollection(env.IsDevelopment()) - .AddXssProtectionDisabled() - ); - - app.UseCookiePolicy(new CookiePolicyOptions { Secure = CookieSecurePolicy.Always, HttpOnly = HttpOnlyPolicy.Always }); - - app.UseStatusCodePagesWithReExecute("/Errors", "?statusCode={0}"); - - app.UseHttpsRedirection(); - app.UseHealthChecks("/health"); - - //For Azure AD redirect uri to remain https - ForwardedHeadersOptions forwardOptions = new() { ForwardedHeaders = ForwardedHeaders.All, RequireHeaderSymmetry = false }; - forwardOptions.KnownNetworks.Clear(); - forwardOptions.KnownProxies.Clear(); - app.UseForwardedHeaders(forwardOptions); - - app.UseStaticFiles(); - app.UseRouting(); - app.UseSentryTracing(); - app.UseSession(); - app.UseAuthentication(); - app.UseAuthorization(); - app.UseMiddleware(); - - app.UseEndpoints(endpoints => - { - endpoints.MapGet("/", context => - { - context.Response.Redirect("project-list", false); - return Task.CompletedTask; - }); - endpoints.MapRazorPages(); - endpoints.MapControllerRoute("default", "{controller}/{action}/"); - }); - } - - /// - /// Builds Authorization policy - /// Ensure authenticated user and restrict roles if they are provided in configuration - /// - /// AuthorizationPolicyBuilder - private AuthorizationPolicyBuilder SetupAuthorizationPolicyBuilder() - { - AuthorizationPolicyBuilder policyBuilder = new(); - policyBuilder.RequireAuthenticatedUser(); - - string allowedRoles = Configuration.GetSection("AzureAd")["AllowedRoles"]; - if (string.IsNullOrWhiteSpace(allowedRoles) is false) - { - policyBuilder.RequireClaim(ClaimTypes.Role, allowedRoles.Split(',')); - } - - return policyBuilder; - } -} +using Dfe.Academisation.CorrelationIdMiddleware; +using Dfe.PrepareConversions.Authorization; +using Dfe.PrepareConversions.Configuration; +using Dfe.PrepareConversions.Data.Features; +using Dfe.PrepareConversions.Data.Models; +using Dfe.PrepareConversions.Data.Services; +using Dfe.PrepareConversions.Data.Services.AzureAd; +using Dfe.PrepareConversions.Data.Services.Interfaces; +using Dfe.PrepareConversions.Models; +using Dfe.PrepareConversions.Routing; +using Dfe.PrepareConversions.Security; +using Dfe.PrepareConversions.Services; +using Microsoft.AspNetCore.Authentication.Cookies; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.CookiePolicy; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.HttpOverrides; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using Microsoft.FeatureManagement; +using Microsoft.Identity.Web; +using Microsoft.Identity.Web.UI; +using System; +using System.Security.Claims; +using System.Threading.Tasks; + +namespace Dfe.PrepareConversions; + +public class Startup +{ + private readonly TimeSpan _authenticationExpiration; + + public Startup(IConfiguration configuration) + { + Configuration = configuration; + + _authenticationExpiration = TimeSpan.FromMinutes(int.Parse(Configuration["AuthenticationExpirationInMinutes"] ?? "60")); + } + + private IConfiguration Configuration { get; } + + private IConfigurationSection GetConfigurationSectionFor() + { + string sectionName = typeof(T).Name.Replace("Options", string.Empty); + return Configuration.GetRequiredSection(sectionName); + } + + private T GetTypedConfigurationFor() + { + return GetConfigurationSectionFor().Get(); + } + + public void ConfigureServices(IServiceCollection services) + { + services.AddFeatureManagement(); + services.AddApplicationInsightsTelemetry(); + services.AddHealthChecks(); + services + .AddRazorPages(options => + { + options.Conventions.AuthorizeFolder("/"); + options.Conventions.AllowAnonymousToPage("/public/maintenance"); + options.Conventions.AllowAnonymousToPage("/public/accessibility"); + }) + .AddViewOptions(options => + { + options.HtmlHelperOptions.ClientValidationEnabled = false; + }).AddMvcOptions(options => + { + options.MaxModelValidationErrors = 50; + options.Filters.Add(new MaintenancePageFilter(Configuration)); + }); + + services.AddControllersWithViews() + .AddMicrosoftIdentityUI(); + + services.AddScoped(sp => sp.GetService()?.HttpContext?.Session); + services.AddSession(options => + { + options.IdleTimeout = _authenticationExpiration; + options.Cookie.Name = ".ManageAnAcademyConversion.Session"; + options.Cookie.IsEssential = true; + options.Cookie.HttpOnly = true; + + if (string.IsNullOrWhiteSpace(Configuration["CI"])) + options.Cookie.SecurePolicy = CookieSecurePolicy.Always; + }); + services.AddHttpContextAccessor(); + + services.AddAuthorization(options => { options.DefaultPolicy = SetupAuthorizationPolicyBuilder().Build(); }); + + services.AddMicrosoftIdentityWebAppAuthentication(Configuration); + services.Configure(CookieAuthenticationDefaults.AuthenticationScheme, + options => + { + options.AccessDeniedPath = "/access-denied"; + options.Cookie.Name = ".ManageAnAcademyConversion.Login"; + options.Cookie.HttpOnly = true; + options.Cookie.IsEssential = true; + options.ExpireTimeSpan = _authenticationExpiration; + options.SlidingExpiration = true; + + if (string.IsNullOrEmpty(Configuration["CI"])) + options.Cookie.SecurePolicy = CookieSecurePolicy.Always; + }); + + services.AddScoped(); + services.AddSingleton(); + + services.AddHttpClient(DfeHttpClientFactory.TramsClientName, (sp, client) => + { + TramsApiOptions tramsApiOptions = GetTypedConfigurationFor(); + client.BaseAddress = new Uri(tramsApiOptions.Endpoint); + client.DefaultRequestHeaders.Add("ApiKey", tramsApiOptions.ApiKey); + + }); + + services.AddHttpClient(DfeHttpClientFactory.AcademisationClientName, (sp, client) => + { + AcademisationApiOptions apiOptions = GetTypedConfigurationFor(); + client.BaseAddress = new Uri(apiOptions.BaseUrl); + client.DefaultRequestHeaders.Add("x-api-key", apiOptions.ApiKey); + }); + + services.Configure(GetConfigurationSectionFor()); + services.Configure(GetConfigurationSectionFor()); + + services.AddScoped(); + services.AddScoped(); + services.Decorate(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.Decorate(); + services.AddScoped(); + services.AddSingleton(); + services.AddSingleton(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + + // Initialize the TransfersUrl + var serviceLinkOptions = Configuration.GetSection("ServiceLink").Get(); + Links.InitializeTransfersUrl(serviceLinkOptions.TransfersUrl); + + } + + public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILogger logger) + { + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + } + else + { + app.UseExceptionHandler("/Errors"); + // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. + app.UseHsts(); + } + + app.UseSecurityHeaders( + SecurityHeadersDefinitions.GetHeaderPolicyCollection(env.IsDevelopment()) + .AddXssProtectionDisabled() + ); + + app.UseCookiePolicy(new CookiePolicyOptions { Secure = CookieSecurePolicy.Always, HttpOnly = HttpOnlyPolicy.Always }); + + app.UseStatusCodePagesWithReExecute("/Errors", "?statusCode={0}"); + + app.UseHttpsRedirection(); + app.UseHealthChecks("/health"); + + //For Azure AD redirect uri to remain https + ForwardedHeadersOptions forwardOptions = new() { ForwardedHeaders = ForwardedHeaders.All, RequireHeaderSymmetry = false }; + forwardOptions.KnownNetworks.Clear(); + forwardOptions.KnownProxies.Clear(); + app.UseForwardedHeaders(forwardOptions); + + app.UseStaticFiles(); + app.UseRouting(); + app.UseSentryTracing(); + app.UseSession(); + app.UseAuthentication(); + app.UseAuthorization(); + app.UseMiddleware(); + + app.UseEndpoints(endpoints => + { + endpoints.MapGet("/", context => + { + context.Response.Redirect("project-list", false); + return Task.CompletedTask; + }); + endpoints.MapRazorPages(); + endpoints.MapControllerRoute("default", "{controller}/{action}/"); + }); + } + + /// + /// Builds Authorization policy + /// Ensure authenticated user and restrict roles if they are provided in configuration + /// + /// AuthorizationPolicyBuilder + private AuthorizationPolicyBuilder SetupAuthorizationPolicyBuilder() + { + AuthorizationPolicyBuilder policyBuilder = new(); + policyBuilder.RequireAuthenticatedUser(); + + string allowedRoles = Configuration.GetSection("AzureAd")["AllowedRoles"]; + if (string.IsNullOrWhiteSpace(allowedRoles) is false) + { + policyBuilder.RequireClaim(ClaimTypes.Role, allowedRoles.Split(',')); + } + + return policyBuilder; + } +}