diff --git a/ConcernsCaseWork/ConcernsCaseWork.API/ConcernsCaseWork.API.csproj b/ConcernsCaseWork/ConcernsCaseWork.API/ConcernsCaseWork.API.csproj
index 12c4a43e5..3d9d9044b 100644
--- a/ConcernsCaseWork/ConcernsCaseWork.API/ConcernsCaseWork.API.csproj
+++ b/ConcernsCaseWork/ConcernsCaseWork.API/ConcernsCaseWork.API.csproj
@@ -24,6 +24,7 @@
+
diff --git a/ConcernsCaseWork/ConcernsCaseWork.API/Startup.cs b/ConcernsCaseWork/ConcernsCaseWork.API/Startup.cs
index a3f5024ca..1aa1b77fe 100644
--- a/ConcernsCaseWork/ConcernsCaseWork.API/Startup.cs
+++ b/ConcernsCaseWork/ConcernsCaseWork.API/Startup.cs
@@ -1,50 +1,53 @@
-using ConcernsCaseWork.API.Extensions;
-using ConcernsCaseWork.API.Middleware;
-using ConcernsCaseWork.API.StartupConfiguration;
-using ConcernsCaseWork.Middleware;
-using Microsoft.AspNetCore.Mvc.ApiExplorer;
-
-namespace ConcernsCaseWork.API
-{
- ///
- /// THIS STARTUP ISN'T USED WHEN API IS HOSTED THROUGH WEBSITE. It is used when running API tests
- ///
- public class Startup
- {
- public Startup(IConfiguration configuration)
- {
- Configuration = configuration;
- }
-
- public IConfiguration Configuration { get; }
-
- public void ConfigureServices(IServiceCollection services)
- {
- services.AddConcernsApiProject(Configuration);
- }
-
- public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IApiVersionDescriptionProvider provider)
- {
- app.UseConcernsCaseworkSwagger(provider);
-
- if (env.IsDevelopment())
- {
- app.UseDeveloperExceptionPage();
- }
-
- app.UseMiddleware();
- app.UseMiddleware();
- app.UseMiddleware();
- app.UseMiddleware();
- app.UseMiddleware();
-
- app.UseHttpsRedirection();
-
- app.UseRouting();
-
- app.UseAuthorization();
-
- app.UseConcernsCaseworkEndpoints();
- }
- }
-}
+using ConcernsCaseWork.API.Extensions;
+using ConcernsCaseWork.API.Middleware;
+using ConcernsCaseWork.API.StartupConfiguration;
+using ConcernsCaseWork.Middleware;
+using Microsoft.AspNetCore.Mvc.ApiExplorer;
+
+namespace ConcernsCaseWork.API
+{
+ ///
+ /// THIS STARTUP ISN'T USED WHEN API IS HOSTED THROUGH WEBSITE. It is used when running API tests
+ ///
+ public class Startup
+ {
+ public Startup(IConfiguration configuration)
+ {
+ Configuration = configuration;
+ }
+
+ public IConfiguration Configuration { get; }
+
+ public void ConfigureServices(IServiceCollection services)
+ {
+ services.AddConcernsApiProject(Configuration);
+ }
+
+ public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IApiVersionDescriptionProvider provider)
+ {
+ app.UseConcernsCaseworkSwagger(provider);
+
+ if (env.IsDevelopment())
+ {
+ app.UseDeveloperExceptionPage();
+ }
+
+ app.UseMiddleware();
+ app.UseMiddleware();
+ app.UseMiddleware();
+ app.UseMiddleware();
+ app.UseMiddleware();
+
+ app.UseHttpsRedirection();
+
+ app.UseRouting();
+
+ app.UseAuthorization();
+
+ app.UseConcernsCaseworkEndpoints();
+
+ // Add Health Checks
+ app.UseHealthChecks("/health");
+ }
+ }
+}
diff --git a/ConcernsCaseWork/ConcernsCaseWork.API/StartupConfiguration/DatabaseConfigurationExtensions.cs b/ConcernsCaseWork/ConcernsCaseWork.API/StartupConfiguration/DatabaseConfigurationExtensions.cs
index 1bf86e7da..cfa7bc30e 100644
--- a/ConcernsCaseWork/ConcernsCaseWork.API/StartupConfiguration/DatabaseConfigurationExtensions.cs
+++ b/ConcernsCaseWork/ConcernsCaseWork.API/StartupConfiguration/DatabaseConfigurationExtensions.cs
@@ -1,16 +1,18 @@
-using ConcernsCaseWork.Data;
-
-namespace ConcernsCaseWork.API.StartupConfiguration;
-
-public static class DatabaseConfigurationExtensions
-{
- public static IServiceCollection AddDatabase(this IServiceCollection services, IConfiguration configuration)
- {
- var connectionString = configuration.GetConnectionString("DefaultConnection");
- services.AddDbContext(options =>
- options.UseConcernsSqlServer(connectionString)
- );
-
- return services;
- }
-}
\ No newline at end of file
+using ConcernsCaseWork.Data;
+using Microsoft.Extensions.Diagnostics.HealthChecks;
+namespace ConcernsCaseWork.API.StartupConfiguration;
+
+public static class DatabaseConfigurationExtensions
+{
+ public static IServiceCollection AddDatabase(this IServiceCollection services, IConfiguration configuration)
+ {
+ var connectionString = configuration.GetConnectionString("DefaultConnection");
+ services.AddDbContext(options =>
+ options.UseConcernsSqlServer(connectionString)
+ );
+ services.AddHealthChecks()
+ .AddDbContextCheck("Concerns Database");
+
+ return services;
+ }
+}
diff --git a/ConcernsCaseWork/ConcernsCaseWork.Tests/Security/AuthorizeAttributeTests.cs b/ConcernsCaseWork/ConcernsCaseWork.Tests/Security/AuthorizeAttributeTests.cs
index 7c4287807..3ed522208 100644
--- a/ConcernsCaseWork/ConcernsCaseWork.Tests/Security/AuthorizeAttributeTests.cs
+++ b/ConcernsCaseWork/ConcernsCaseWork.Tests/Security/AuthorizeAttributeTests.cs
@@ -1,77 +1,55 @@
-using ConcernsCaseWork.Pages;
-using ConcernsCaseWork.Pages.Base;
-using FluentAssertions;
-using FluentAssertions.Execution;
-using Microsoft.AspNetCore.Authorization;
-using Microsoft.AspNetCore.Mvc.RazorPages;
-using NUnit.Framework;
-using System;
-using System.Linq;
-using System.Reflection;
-
-namespace ConcernsCaseWork.Tests.Security
-{
- public class AuthorizeAttributeTests
- {
- public AuthorizeAttributeTests()
- {
- this.UnauthorizedPages = new Type[]
- {
- typeof(HealthModel),
- typeof(ErrorPageModel),
- };
- }
-
- public Type[] UnauthorizedPages { get; }
-
- [Test]
- public void All_Pages_Include_Authorize_Attribute()
- {
- var pages = this.GetAllPagesExceptUnauthorizedPages();
-
- pages.Length.Should().BeGreaterThan(0);
-
- using (var scope = new AssertionScope())
- {
- foreach (Type page in pages)
- {
- var authAttributes = page.GetCustomAttributes();
- if (authAttributes == null || !authAttributes.Any())
- {
- scope.AddPreFormattedFailure($"Could not find [Authorize] attribute and no exemption for the following page type: {page.Name} ({page.FullName})");
- }
- }
- }
- }
-
- [Test]
- public void Open_Pages_Do_Not_Require_Authorization()
- {
- Type[] UnauthorizedPages = new[]
- {
- typeof(HealthModel),
- };
-
- using (var scope = new AssertionScope())
- {
- foreach (Type page in this.UnauthorizedPages)
- {
- var authAttribute = page.GetCustomAttribute();
- if (authAttribute != null)
- {
- scope.AddPreFormattedFailure($"Expected page to be open and not require authorisation. But found [Authorize] attribute on the following page type: {page.Name} ({page.FullName})");
- }
- }
- }
- }
-
- private Type[] GetAllPagesExceptUnauthorizedPages()
- {
- return Assembly
- .GetAssembly(typeof(AbstractPageModel))
- .GetTypes()
- .Where(x => x.IsAssignableTo(typeof(PageModel)) && !this.UnauthorizedPages.Contains(x))
- .ToArray();
- }
- }
-}
\ No newline at end of file
+using ConcernsCaseWork.Pages;
+using ConcernsCaseWork.Pages.Base;
+using FluentAssertions;
+using FluentAssertions.Execution;
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Mvc.RazorPages;
+using NUnit.Framework;
+using System;
+using System.Linq;
+using System.Reflection;
+
+namespace ConcernsCaseWork.Tests.Security
+{
+ public class AuthorizeAttributeTests
+ {
+ public AuthorizeAttributeTests()
+ {
+ this.UnauthorizedPages = new Type[]
+ {
+ typeof(ErrorPageModel),
+ };
+ }
+
+ public Type[] UnauthorizedPages { get; }
+
+ [Test]
+ public void All_Pages_Include_Authorize_Attribute()
+ {
+ var pages = this.GetAllPagesExceptUnauthorizedPages();
+
+ pages.Length.Should().BeGreaterThan(0);
+
+ using (var scope = new AssertionScope())
+ {
+ foreach (Type page in pages)
+ {
+ var authAttributes = page.GetCustomAttributes();
+ if (authAttributes == null || !authAttributes.Any())
+ {
+ scope.AddPreFormattedFailure($"Could not find [Authorize] attribute and no exemption for the following page type: {page.Name} ({page.FullName})");
+ }
+ }
+ }
+ }
+
+ private Type[] GetAllPagesExceptUnauthorizedPages()
+ {
+ return Assembly
+ .GetAssembly(typeof(AbstractPageModel))
+ .GetTypes()
+ .Where(x => x.IsAssignableTo(typeof(PageModel)) && !this.UnauthorizedPages.Contains(x))
+ .ToArray();
+ }
+ }
+}
diff --git a/ConcernsCaseWork/ConcernsCaseWork/ConcernsCaseWork.csproj b/ConcernsCaseWork/ConcernsCaseWork/ConcernsCaseWork.csproj
index ed2817c2a..a383efe9b 100644
--- a/ConcernsCaseWork/ConcernsCaseWork/ConcernsCaseWork.csproj
+++ b/ConcernsCaseWork/ConcernsCaseWork/ConcernsCaseWork.csproj
@@ -33,6 +33,7 @@
+
diff --git a/ConcernsCaseWork/ConcernsCaseWork/Extensions/StartupExtension.cs b/ConcernsCaseWork/ConcernsCaseWork/Extensions/StartupExtension.cs
index 0fbfe4fd8..47f80b5f6 100644
--- a/ConcernsCaseWork/ConcernsCaseWork/Extensions/StartupExtension.cs
+++ b/ConcernsCaseWork/ConcernsCaseWork/Extensions/StartupExtension.cs
@@ -1,211 +1,213 @@
-using ConcernsCaseWork.API.Contracts.Configuration;
-using ConcernsCaseWork.API.StartupConfiguration;
-using ConcernsCaseWork.Authorization;
-using ConcernsCaseWork.Logging;
-using ConcernsCaseWork.Pages.Validators;
-using ConcernsCaseWork.Redis.Base;
-using ConcernsCaseWork.Redis.Configuration;
-using ConcernsCaseWork.Redis.Nti;
-using ConcernsCaseWork.Redis.NtiWarningLetter;
-using ConcernsCaseWork.Redis.Trusts;
-using ConcernsCaseWork.Redis.Users;
-using ConcernsCaseWork.Service.CaseActions;
-using ConcernsCaseWork.Service.Cases;
-using ConcernsCaseWork.Service.Decision;
-using ConcernsCaseWork.Service.FinancialPlan;
-using ConcernsCaseWork.Service.Nti;
-using ConcernsCaseWork.Service.NtiUnderConsideration;
-using ConcernsCaseWork.Service.NtiWarningLetter;
-using ConcernsCaseWork.Service.Permissions;
-using ConcernsCaseWork.Service.Records;
-using ConcernsCaseWork.Service.TargetedTrustEngagement;
-using ConcernsCaseWork.Service.Teams;
-using ConcernsCaseWork.Service.TrustFinancialForecast;
-using ConcernsCaseWork.Service.Trusts;
-using ConcernsCaseWork.Services.Actions;
-using ConcernsCaseWork.Services.Cases;
-using ConcernsCaseWork.Services.Cases.Create;
-using ConcernsCaseWork.Services.Decisions;
-using ConcernsCaseWork.Services.FinancialPlan;
-using ConcernsCaseWork.Services.Nti;
-using ConcernsCaseWork.Services.NtiUnderConsideration;
-using ConcernsCaseWork.Services.NtiWarningLetter;
-using ConcernsCaseWork.Services.PageHistory;
-using ConcernsCaseWork.Services.Records;
-using ConcernsCaseWork.Services.Teams;
-using ConcernsCaseWork.Services.Trusts;
-using ConcernsCaseWork.UserContext;
-using Microsoft.AspNetCore.Authorization;
-using Microsoft.AspNetCore.DataProtection;
-using Microsoft.Extensions.Configuration;
-using Microsoft.Extensions.DependencyInjection;
-using Newtonsoft.Json.Linq;
-using StackExchange.Redis;
-using System;
-using System.Net.Mime;
-using System.Threading.Tasks;
-
-namespace ConcernsCaseWork.Extensions
-{
- public static class StartupExtension
- {
- private static IConnectionMultiplexer _redisConnectionMultiplexer;
- //private static ILogger _logger;
-
-
- public static void AddRedis(this IServiceCollection services, IConfiguration configuration)
- {
-
- try
- {
- var vCapConfiguration = JObject.Parse(configuration["VCAP_SERVICES"]) ?? throw new Exception("AddRedis::VCAP_SERVICES missing");
- var redisCredentials = vCapConfiguration["redis"]?[0]?["credentials"] ?? throw new Exception("AddRedis::Credentials missing");
- var password = (string)redisCredentials["password"] ?? throw new Exception("AddRedis::Credentials::password missing");
- var host = (string)redisCredentials["host"] ?? throw new Exception("AddRedis::Credentials::host missing");
- var port = (string)redisCredentials["port"] ?? throw new Exception("AddRedis::Credentials::port missing");
- var tls = (bool)redisCredentials["tls_enabled"];
-
- /*_logger.LogInformation("Starting Redis Server Host - {Host}", host);
- _logger.LogInformation("Starting Redis Server Port - {Port}", port);
- _logger.LogInformation("Starting Redis Server TLS - {Tls}", tls);*/
-
- var redisConfigurationOptions = new ConfigurationOptions { Password = password, EndPoints = { $"{host}:{port}" }, Ssl = tls, AsyncTimeout = 15000, SyncTimeout = 15000 };
-
- var preventThreadTheftStr = configuration["PreventRedisThreadTheft"] ?? "false";
- if (bool.TryParse(preventThreadTheftStr, out bool preventThreadTheft) && preventThreadTheft)
- {
- // https://stackexchange.github.io/StackExchange.Redis/ThreadTheft.html
- ConnectionMultiplexer.SetFeatureFlag("preventthreadtheft", true);
- }
-
- _redisConnectionMultiplexer = ConnectionMultiplexer.Connect(redisConfigurationOptions);
- services.AddDataProtection().PersistKeysToStackExchangeRedis(_redisConnectionMultiplexer, "DataProtectionKeys");
-
- services.AddStackExchangeRedisCache(
- options =>
- {
- options.ConfigurationOptions = redisConfigurationOptions;
- options.InstanceName = $"Redis-{Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT")}";
- options.ConnectionMultiplexerFactory = () => Task.FromResult(_redisConnectionMultiplexer);
- });
-
- }
- catch (Exception ex)
- {
- // _logger.LogError("AddRedis::Exception::{Message}", ex.Message);
- throw new Exception($"AddRedis::Exception::{ex.Message}");
- }
- }
-
- ///
- /// HttpFactory for Trams API
- ///
- ///
- ///
- ///
- public static void AddTramsApi(this IServiceCollection services, IConfiguration configuration)
- {
- var tramsApiEndpoint = configuration["trams:api_endpoint"];
- var tramsApiKey = configuration["trams:api_key"];
-
- if (string.IsNullOrEmpty(tramsApiEndpoint) || string.IsNullOrEmpty(tramsApiKey))
- throw new Exception("AddTramsApi::missing configuration");
-
- //_logger.LogInformation("Starting Trams API Endpoint - {TramsApiEndpoint}", tramsApiEndpoint);
-
- services.AddHttpClient("TramsClient", client =>
- {
- client.BaseAddress = new Uri(tramsApiEndpoint);
- client.DefaultRequestHeaders.Add("ApiKey", tramsApiKey);
- client.DefaultRequestHeaders.Add("ContentType", MediaTypeNames.Application.Json);
- });
- }
-
- ///
- /// HttpFactory for Concerns API
- ///
- ///
- ///
- ///
- public static void AddConcernsApi(this IServiceCollection services, IConfiguration configuration)
- {
- services.AddConcernsApiProject(configuration);
- }
-
- public static void AddInternalServices(this IServiceCollection services)
- {
- services.AddSingleton();
- services.AddSingleton();
- // Web application services
- services.AddScoped();
- services.AddScoped();
- services.AddScoped();
- services.AddScoped();
- services.AddScoped();
- services.AddScoped();
- services.AddScoped();
- services.AddScoped();
- services.AddScoped();
- services.AddScoped();
- services.AddScoped();
- services.AddScoped();
- services.AddScoped();
- services.AddScoped();
- services.AddScoped();
- services.AddScoped();
- services.AddScoped();
- services.AddScoped();
- services.AddScoped();
- services.AddScoped();
- services.AddScoped();
- services.AddScoped();
- services.AddScoped();
- services.AddScoped();
-
- // api services
- services.AddScoped();
- services.AddScoped();
- services.AddScoped();
- services.AddScoped();
- services.AddScoped();
- services.AddScoped();
- services.AddScoped();
- services.AddScoped();
- services.AddScoped();
- services.AddScoped();
- services.AddScoped();
- services.AddScoped();
- services.AddScoped();
- services.AddScoped();
- services.AddScoped();
- services.AddScoped();
- services.AddScoped();
- services.AddScoped();
- services.AddScoped();
-
-
- // Redis services
- services.AddSingleton();
- services.AddScoped();
- services.AddScoped();
- services.AddScoped();
- services.AddScoped();
- services.AddScoped();
-
- services.AddScoped();
-
- services.AddHttpContextAccessor();
- services.AddScoped();
- services.AddSingleton();
- }
-
- public static void AddConfigurationOptions(this IServiceCollection services, IConfiguration configuration)
- {
- var test = configuration.GetSection(TrustSearchOptions.Cache);
- services.Configure(configuration.GetSection(CacheOptions.Cache));
- services.Configure(configuration.GetSection(TrustSearchOptions.Cache));
- services.Configure(configuration.GetSection(SiteOptions.Site));
- services.Configure(configuration.GetSection("FakeTrusts"));
- }
- }
-}
\ No newline at end of file
+using ConcernsCaseWork.API.Contracts.Configuration;
+using ConcernsCaseWork.API.StartupConfiguration;
+using ConcernsCaseWork.Authorization;
+using ConcernsCaseWork.Logging;
+using ConcernsCaseWork.Pages.Validators;
+using ConcernsCaseWork.Redis.Base;
+using ConcernsCaseWork.Redis.Configuration;
+using ConcernsCaseWork.Redis.Nti;
+using ConcernsCaseWork.Redis.NtiWarningLetter;
+using ConcernsCaseWork.Redis.Trusts;
+using ConcernsCaseWork.Redis.Users;
+using ConcernsCaseWork.Service.CaseActions;
+using ConcernsCaseWork.Service.Cases;
+using ConcernsCaseWork.Service.Decision;
+using ConcernsCaseWork.Service.FinancialPlan;
+using ConcernsCaseWork.Service.Nti;
+using ConcernsCaseWork.Service.NtiUnderConsideration;
+using ConcernsCaseWork.Service.NtiWarningLetter;
+using ConcernsCaseWork.Service.Permissions;
+using ConcernsCaseWork.Service.Records;
+using ConcernsCaseWork.Service.TargetedTrustEngagement;
+using ConcernsCaseWork.Service.Teams;
+using ConcernsCaseWork.Service.TrustFinancialForecast;
+using ConcernsCaseWork.Service.Trusts;
+using ConcernsCaseWork.Services.Actions;
+using ConcernsCaseWork.Services.Cases;
+using ConcernsCaseWork.Services.Cases.Create;
+using ConcernsCaseWork.Services.Decisions;
+using ConcernsCaseWork.Services.FinancialPlan;
+using ConcernsCaseWork.Services.Nti;
+using ConcernsCaseWork.Services.NtiUnderConsideration;
+using ConcernsCaseWork.Services.NtiWarningLetter;
+using ConcernsCaseWork.Services.PageHistory;
+using ConcernsCaseWork.Services.Records;
+using ConcernsCaseWork.Services.Teams;
+using ConcernsCaseWork.Services.Trusts;
+using ConcernsCaseWork.UserContext;
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.DataProtection;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.DependencyInjection;
+using Newtonsoft.Json.Linq;
+using StackExchange.Redis;
+using System;
+using System.Net.Mime;
+using System.Threading.Tasks;
+
+namespace ConcernsCaseWork.Extensions
+{
+ public static class StartupExtension
+ {
+ private static IConnectionMultiplexer _redisConnectionMultiplexer;
+ //private static ILogger _logger;
+
+
+ public static void AddRedis(this IServiceCollection services, IConfiguration configuration)
+ {
+
+ try
+ {
+ var vCapConfiguration = JObject.Parse(configuration["VCAP_SERVICES"]) ?? throw new Exception("AddRedis::VCAP_SERVICES missing");
+ var redisCredentials = vCapConfiguration["redis"]?[0]?["credentials"] ?? throw new Exception("AddRedis::Credentials missing");
+ var password = (string)redisCredentials["password"] ?? throw new Exception("AddRedis::Credentials::password missing");
+ var host = (string)redisCredentials["host"] ?? throw new Exception("AddRedis::Credentials::host missing");
+ var port = (string)redisCredentials["port"] ?? throw new Exception("AddRedis::Credentials::port missing");
+ var tls = (bool)redisCredentials["tls_enabled"];
+
+ /*_logger.LogInformation("Starting Redis Server Host - {Host}", host);
+ _logger.LogInformation("Starting Redis Server Port - {Port}", port);
+ _logger.LogInformation("Starting Redis Server TLS - {Tls}", tls);*/
+
+ var redisConfigurationOptions = new ConfigurationOptions { Password = password, EndPoints = { $"{host}:{port}" }, Ssl = tls, AsyncTimeout = 15000, SyncTimeout = 15000 };
+
+ var preventThreadTheftStr = configuration["PreventRedisThreadTheft"] ?? "false";
+ if (bool.TryParse(preventThreadTheftStr, out bool preventThreadTheft) && preventThreadTheft)
+ {
+ // https://stackexchange.github.io/StackExchange.Redis/ThreadTheft.html
+ ConnectionMultiplexer.SetFeatureFlag("preventthreadtheft", true);
+ }
+
+ _redisConnectionMultiplexer = ConnectionMultiplexer.Connect(redisConfigurationOptions);
+ services.AddDataProtection().PersistKeysToStackExchangeRedis(_redisConnectionMultiplexer, "DataProtectionKeys");
+
+ services.AddStackExchangeRedisCache(
+ options =>
+ {
+ options.ConfigurationOptions = redisConfigurationOptions;
+ options.InstanceName = $"Redis-{Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT")}";
+ options.ConnectionMultiplexerFactory = () => Task.FromResult(_redisConnectionMultiplexer);
+ });
+
+ services.AddHealthChecks().AddRedis(_redisConnectionMultiplexer);
+ }
+ catch (Exception ex)
+ {
+ // _logger.LogError("AddRedis::Exception::{Message}", ex.Message);
+ throw new Exception($"AddRedis::Exception::{ex.Message}");
+ }
+ }
+
+ ///
+ /// HttpFactory for Trams API
+ ///
+ ///
+ ///
+ ///
+ public static void AddTramsApi(this IServiceCollection services, IConfiguration configuration)
+ {
+ var tramsApiEndpoint = configuration["trams:api_endpoint"];
+ var tramsApiKey = configuration["trams:api_key"];
+
+ if (string.IsNullOrEmpty(tramsApiEndpoint) || string.IsNullOrEmpty(tramsApiKey))
+ throw new Exception("AddTramsApi::missing configuration");
+
+ //_logger.LogInformation("Starting Trams API Endpoint - {TramsApiEndpoint}", tramsApiEndpoint);
+
+ services.AddHttpClient("TramsClient", client =>
+ {
+ client.BaseAddress = new Uri(tramsApiEndpoint);
+ client.DefaultRequestHeaders.Add("ApiKey", tramsApiKey);
+ client.DefaultRequestHeaders.Add("ContentType", MediaTypeNames.Application.Json);
+ client.DefaultRequestHeaders.Add("User-Agent", "RecordConcernsSupportTrusts/1.0");
+ });
+ }
+
+ ///
+ /// HttpFactory for Concerns API
+ ///
+ ///
+ ///
+ ///
+ public static void AddConcernsApi(this IServiceCollection services, IConfiguration configuration)
+ {
+ services.AddConcernsApiProject(configuration);
+ }
+
+ public static void AddInternalServices(this IServiceCollection services)
+ {
+ services.AddSingleton();
+ services.AddSingleton();
+ // Web application services
+ services.AddScoped();
+ services.AddScoped();
+ services.AddScoped();
+ services.AddScoped();
+ services.AddScoped();
+ services.AddScoped();
+ services.AddScoped();
+ services.AddScoped();
+ services.AddScoped();
+ services.AddScoped();
+ services.AddScoped();
+ services.AddScoped();
+ services.AddScoped();
+ services.AddScoped();
+ services.AddScoped();
+ services.AddScoped();
+ services.AddScoped();
+ services.AddScoped();
+ services.AddScoped();
+ services.AddScoped();
+ services.AddScoped();
+ services.AddScoped();
+ services.AddScoped();
+ services.AddScoped();
+
+ // api services
+ services.AddScoped();
+ services.AddScoped();
+ services.AddScoped();
+ services.AddScoped();
+ services.AddScoped();
+ services.AddScoped();
+ services.AddScoped();
+ services.AddScoped();
+ services.AddScoped();
+ services.AddScoped();
+ services.AddScoped();
+ services.AddScoped();
+ services.AddScoped();
+ services.AddScoped();
+ services.AddScoped();
+ services.AddScoped();
+ services.AddScoped();
+ services.AddScoped();
+ services.AddScoped();
+
+
+ // Redis services
+ services.AddSingleton();
+ services.AddScoped();
+ services.AddScoped();
+ services.AddScoped();
+ services.AddScoped();
+ services.AddScoped();
+
+ services.AddScoped();
+
+ services.AddHttpContextAccessor();
+ services.AddScoped();
+ services.AddSingleton();
+ }
+
+ public static void AddConfigurationOptions(this IServiceCollection services, IConfiguration configuration)
+ {
+ var test = configuration.GetSection(TrustSearchOptions.Cache);
+ services.Configure(configuration.GetSection(CacheOptions.Cache));
+ services.Configure(configuration.GetSection(TrustSearchOptions.Cache));
+ services.Configure(configuration.GetSection(SiteOptions.Site));
+ services.Configure(configuration.GetSection("FakeTrusts"));
+ }
+ }
+}
diff --git a/ConcernsCaseWork/ConcernsCaseWork/Pages/Health.cshtml b/ConcernsCaseWork/ConcernsCaseWork/Pages/Health.cshtml
deleted file mode 100644
index 1e83a5be7..000000000
--- a/ConcernsCaseWork/ConcernsCaseWork/Pages/Health.cshtml
+++ /dev/null
@@ -1,6 +0,0 @@
-@page
-@model ConcernsCaseWork.Pages.HealthModel
-@{
- Layout = null;
- @Model.BuildGuid
-}
diff --git a/ConcernsCaseWork/ConcernsCaseWork/Pages/Health.cshtml.cs b/ConcernsCaseWork/ConcernsCaseWork/Pages/Health.cshtml.cs
deleted file mode 100644
index 94a7f1e4d..000000000
--- a/ConcernsCaseWork/ConcernsCaseWork/Pages/Health.cshtml.cs
+++ /dev/null
@@ -1,20 +0,0 @@
-using ConcernsCaseWork.Attributes;
-using Microsoft.AspNetCore.Mvc;
-using Microsoft.AspNetCore.Mvc.RazorPages;
-using System.Reflection;
-
-
-namespace ConcernsCaseWork.Pages
-{
- [ResponseCache(NoStore = true, Duration = 0)]
- public class HealthModel : PageModel
- {
- public string BuildGuid { get; set; }
-
- public void OnGet()
- {
- var assembly = Assembly.GetEntryAssembly();
- this.BuildGuid = assembly.GetCustomAttribute().BuildGuid;
- }
- }
-}
diff --git a/ConcernsCaseWork/ConcernsCaseWork/Program.cs b/ConcernsCaseWork/ConcernsCaseWork/Program.cs
index 75980a2a4..61ecf43f9 100644
--- a/ConcernsCaseWork/ConcernsCaseWork/Program.cs
+++ b/ConcernsCaseWork/ConcernsCaseWork/Program.cs
@@ -1,24 +1,23 @@
-using Microsoft.AspNetCore.Hosting;
-using Microsoft.Extensions.Hosting;
-
-
-namespace ConcernsCaseWork
-{
- public class Program
- {
- public static void Main(string[] args)
- {
-
- CreateHostBuilder(args).Build().Run();
- }
-
- private static IHostBuilder CreateHostBuilder(string[] args) =>
- Host.CreateDefaultBuilder(args)
- .ConfigureWebHostDefaults(webBuilder =>
- {
- webBuilder
- .ConfigureKestrel(options => options.AddServerHeader = false)
- .UseStartup();
- });
- }
-}
+using Microsoft.AspNetCore.Hosting;
+using Microsoft.Extensions.Hosting;
+
+
+namespace ConcernsCaseWork
+{
+ public class Program
+ {
+ public static void Main(string[] args)
+ {
+ CreateHostBuilder(args).Build().Run();
+ }
+
+ private static IHostBuilder CreateHostBuilder(string[] args) =>
+ Host.CreateDefaultBuilder(args)
+ .ConfigureWebHostDefaults(webBuilder =>
+ {
+ webBuilder
+ .ConfigureKestrel(options => options.AddServerHeader = false)
+ .UseStartup();
+ });
+ }
+}
diff --git a/ConcernsCaseWork/ConcernsCaseWork/Startup.cs b/ConcernsCaseWork/ConcernsCaseWork/Startup.cs
index 56609aebb..1e8b09cac 100644
--- a/ConcernsCaseWork/ConcernsCaseWork/Startup.cs
+++ b/ConcernsCaseWork/ConcernsCaseWork/Startup.cs
@@ -9,15 +9,12 @@
using ConcernsCaseWork.Security;
using ConcernsCaseWork.Services.PageHistory;
using ConcernsCaseWork.UserContext;
-using FluentAssertions.Common;
using Microsoft.AspNetCore.Authentication.Cookies;
-using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.HttpOverrides;
-using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc.ApiExplorer;
using Microsoft.AspNetCore.Routing;
using Microsoft.Extensions.Configuration;
@@ -35,205 +32,207 @@
namespace ConcernsCaseWork
{
- public class Startup
- {
- public Startup(IConfiguration configuration)
- {
- Configuration = configuration;
- }
-
- private IConfiguration Configuration { get; }
-
- // This method gets called by the runtime. Use this method to add services to the container.
- public void ConfigureServices(IServiceCollection services)
- {
- services.AddRazorPages(options =>
- {
- options.Conventions.AddPageRoute("/home", "");
- options.Conventions.AddPageRoute("/notfound", "/error/404");
- options.Conventions.AddPageRoute("/notfound", "/error/{code:int}");
- options.Conventions.AddPageRoute("/case/management/action/NtiWarningLetter/add", "/case/{urn:long}/management/action/NtiWarningLetter/add");
- options.Conventions.AddPageRoute("/case/management/action/NtiWarningLetter/addConditions", "/case/{urn:long}/management/action/NtiWarningLetter/conditions");
- options.Conventions.AddPageRoute("/case/management/action/Nti/add", "/case/{urn:long}/management/action/nti/add");
- options.Conventions.AddPageRoute("/case/management/action/Nti/addConditions", "/case/{urn:long}/management/action/nti/conditions");
-
-
- // TODO:
- // Consider adding: options.Conventions.AuthorizeFolder("/");
- }).AddViewOptions(options =>
- {
- options.HtmlHelperOptions.ClientValidationEnabled = false;
- });
-
- services.AddFeatureManagement();
-
- // Configuration options
- services.AddConfigurationOptions(Configuration);
-
- // Azure AD
- services.AddAuthorization(options =>
- {
- options.DefaultPolicy = SetupAuthorizationPolicyBuilder().Build();
- options.AddPolicy("CanDelete", builder =>
- {
- builder.RequireClaim(ClaimTypes.Role, Claims.CaseDeleteRoleClaim);
- });
- });
-
- services.AddMicrosoftIdentityWebAppAuthentication(Configuration);
- services.Configure(CookieAuthenticationDefaults.AuthenticationScheme,
- options =>
- {
- options.Cookie.Name = ".ConcernsCasework.Login";
- options.Cookie.HttpOnly = true;
- options.Cookie.IsEssential = true;
- options.ExpireTimeSpan = TimeSpan.FromMinutes(int.Parse(Configuration["AuthenticationExpirationInMinutes"]));
- options.SlidingExpiration = true;
- options.Cookie.SecurePolicy = CookieSecurePolicy.Always; // in A2B this was only if string.IsNullOrEmpty(Configuration["CI"]), but why not always?
- options.AccessDeniedPath = "/access-denied";
- });
-
- services.AddAntiforgery(options =>
- {
- options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
- });
-
- // Redis
- services.AddRedis(Configuration);
-
- // APIs
- services.AddTramsApi(Configuration);
- services.AddConcernsApi(Configuration);
-
- // AutoMapper
- services.ConfigureAndAddAutoMapper();
-
-
- // Route options
- services.Configure(options => { options.LowercaseUrls = true; });
-
- // Internal Service
- services.AddInternalServices();
-
- // Session
- services.AddSession(options =>
- {
- options.IdleTimeout = TimeSpan.FromHours(24);
- options.Cookie.Name = ".ConcernsCasework.Session";
- options.Cookie.HttpOnly = true;
- options.Cookie.IsEssential = true;
- options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
- });
-
- services.AddRouting(options =>
- {
- options.ConstraintMap.Add("fpEditModes", typeof(FinancialPlanEditModeConstraint));
- });
- services.AddApplicationInsightsTelemetry(options =>
- {
- options.ConnectionString = Configuration["ApplicationInsights:ConnectionString"];
- });
- // Enforce HTTPS in ASP.NET Core
- // @link https://learn.microsoft.com/en-us/aspnet/core/security/enforcing-ssl?
- services.AddHsts(options =>
- {
- options.Preload = true;
- options.IncludeSubDomains = true;
- options.MaxAge = TimeSpan.FromDays(365);
- });
- }
-
- // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
- public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IApiVersionDescriptionProvider provider, IMapper mapper)
- {
- // Ensure we do not lose X-Forwarded-* Headers when behind a Proxy
- var forwardOptions = new ForwardedHeadersOptions {
- ForwardedHeaders = ForwardedHeaders.All,
- RequireHeaderSymmetry = false
- };
- forwardOptions.KnownNetworks.Clear();
- forwardOptions.KnownProxies.Clear();
- app.UseForwardedHeaders(forwardOptions);
-
- AbstractPageModel.PageHistoryStorageHandler = app.ApplicationServices.GetService();
-
- app.UseConcernsCaseworkSwagger(provider);
-
- if (env.IsDevelopment())
- {
- app.UseDeveloperExceptionPage();
- }
- else
- {
- app.UseExceptionHandler("/Error");
- }
-
- app.UseMiddleware();
- app.UseMiddleware();
-
- // Security headers
- app.UseSecurityHeaders(
- SecurityHeadersDefinitions.GetHeaderPolicyCollection(env.IsDevelopment()));
- app.UseHsts();
-
- // Combined with razor routing 404 display custom page NotFound
- app.UseStatusCodePagesWithReExecute("/error/{0}");
-
- app.UseHttpsRedirection();
-
- app.UseStaticFiles();
-
- // Enable session for the application
- app.UseSession();
-
-
- app.UseMiddleware();
-
-
- app.UseRouting();
-
- app.UseAuthentication();
- app.UseAuthorization();
- app.UseMiddleware();
- app.UseMiddleware();
- app.UseMiddleware();
- app.UseMiddlewareForFeature(FeatureFlags.IsMaintenanceModeEnabled);
-
- app.UseConcernsCaseworkEndpoints();
-
- app.UseEndpoints(endpoints =>
- {
- endpoints.MapRazorPages();
- });
-
- mapper.CompileAndValidate();
-
- // If our application gets hit really hard, then threads need to be spawned
- // By default the number of threads that exist in the threadpool is the amount of CPUs (1)
- // Each time we have to spawn a new thread it gets delayed by 500ms
- // Setting the min higher means there will not be that delay in creating threads up to the min
- // Re-evaluate this based on performance tests
- // Found because redis kept timing out because it was delayed too long waiting for a thread to execute
- ThreadPool.SetMinThreads(400, 400);
- }
-
- ///
- /// Builds Authorization policy
- /// Ensure authenticated user and restrict roles if they are provided in configuration
- ///
- /// AuthorizationPolicyBuilder
- private AuthorizationPolicyBuilder SetupAuthorizationPolicyBuilder()
- {
- var policyBuilder = new AuthorizationPolicyBuilder();
- var allowedRoles = Configuration.GetSection("AzureAd")["AllowedRoles"];
- policyBuilder.RequireAuthenticatedUser();
- // Allows us to add in role support later.
- if (!string.IsNullOrWhiteSpace(allowedRoles))
- {
- policyBuilder.RequireClaim(ClaimTypes.Role, allowedRoles.Split(','));
- }
-
- return policyBuilder;
- }
- }
+ public class Startup
+ {
+ public Startup(IConfiguration configuration)
+ {
+ Configuration = configuration;
+ }
+
+ private IConfiguration Configuration { get; }
+
+ // This method gets called by the runtime. Use this method to add services to the container.
+ public void ConfigureServices(IServiceCollection services)
+ {
+ services.AddRazorPages(options =>
+ {
+ options.Conventions.AddPageRoute("/home", "");
+ options.Conventions.AddPageRoute("/notfound", "/error/404");
+ options.Conventions.AddPageRoute("/notfound", "/error/{code:int}");
+ options.Conventions.AddPageRoute("/case/management/action/NtiWarningLetter/add", "/case/{urn:long}/management/action/NtiWarningLetter/add");
+ options.Conventions.AddPageRoute("/case/management/action/NtiWarningLetter/addConditions", "/case/{urn:long}/management/action/NtiWarningLetter/conditions");
+ options.Conventions.AddPageRoute("/case/management/action/Nti/add", "/case/{urn:long}/management/action/nti/add");
+ options.Conventions.AddPageRoute("/case/management/action/Nti/addConditions", "/case/{urn:long}/management/action/nti/conditions");
+
+
+ // TODO:
+ // Consider adding: options.Conventions.AuthorizeFolder("/");
+ }).AddViewOptions(options =>
+ {
+ options.HtmlHelperOptions.ClientValidationEnabled = false;
+ });
+
+ services.AddFeatureManagement();
+
+ // Configuration options
+ services.AddConfigurationOptions(Configuration);
+
+ // Azure AD
+ services.AddAuthorization(options =>
+ {
+ options.DefaultPolicy = SetupAuthorizationPolicyBuilder().Build();
+ options.AddPolicy("CanDelete", builder =>
+ {
+ builder.RequireClaim(ClaimTypes.Role, Claims.CaseDeleteRoleClaim);
+ });
+ });
+
+ services.AddMicrosoftIdentityWebAppAuthentication(Configuration);
+ services.Configure(CookieAuthenticationDefaults.AuthenticationScheme,
+ options =>
+ {
+ options.Cookie.Name = ".ConcernsCasework.Login";
+ options.Cookie.HttpOnly = true;
+ options.Cookie.IsEssential = true;
+ options.ExpireTimeSpan = TimeSpan.FromMinutes(int.Parse(Configuration["AuthenticationExpirationInMinutes"]));
+ options.SlidingExpiration = true;
+ options.Cookie.SecurePolicy = CookieSecurePolicy.Always; // in A2B this was only if string.IsNullOrEmpty(Configuration["CI"]), but why not always?
+ options.AccessDeniedPath = "/access-denied";
+ });
+
+ services.AddAntiforgery(options =>
+ {
+ options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
+ });
+
+ // Redis
+ services.AddRedis(Configuration);
+
+ // APIs
+ services.AddTramsApi(Configuration);
+ services.AddConcernsApi(Configuration);
+
+ // AutoMapper
+ services.ConfigureAndAddAutoMapper();
+
+ // Route options
+ services.Configure(options => { options.LowercaseUrls = true; });
+
+ // Internal Service
+ services.AddInternalServices();
+
+ // Session
+ services.AddSession(options =>
+ {
+ options.IdleTimeout = TimeSpan.FromHours(24);
+ options.Cookie.Name = ".ConcernsCasework.Session";
+ options.Cookie.HttpOnly = true;
+ options.Cookie.IsEssential = true;
+ options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
+ });
+
+ services.AddRouting(options =>
+ {
+ options.ConstraintMap.Add("fpEditModes", typeof(FinancialPlanEditModeConstraint));
+ });
+ services.AddApplicationInsightsTelemetry(options =>
+ {
+ options.ConnectionString = Configuration["ApplicationInsights:ConnectionString"];
+ });
+ // Enforce HTTPS in ASP.NET Core
+ // @link https://learn.microsoft.com/en-us/aspnet/core/security/enforcing-ssl?
+ services.AddHsts(options =>
+ {
+ options.Preload = true;
+ options.IncludeSubDomains = true;
+ options.MaxAge = TimeSpan.FromDays(365);
+ });
+ }
+
+ // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
+ public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IApiVersionDescriptionProvider provider, IMapper mapper)
+ {
+ // Ensure we do not lose X-Forwarded-* Headers when behind a Proxy
+ var forwardOptions = new ForwardedHeadersOptions {
+ ForwardedHeaders = ForwardedHeaders.All,
+ RequireHeaderSymmetry = false
+ };
+ forwardOptions.KnownNetworks.Clear();
+ forwardOptions.KnownProxies.Clear();
+ app.UseForwardedHeaders(forwardOptions);
+
+ AbstractPageModel.PageHistoryStorageHandler = app.ApplicationServices.GetService();
+
+ app.UseConcernsCaseworkSwagger(provider);
+
+ if (env.IsDevelopment())
+ {
+ app.UseDeveloperExceptionPage();
+ }
+ else
+ {
+ app.UseExceptionHandler("/Error");
+ }
+
+ app.UseMiddleware();
+ app.UseMiddleware();
+
+ // Security headers
+ app.UseSecurityHeaders(
+ SecurityHeadersDefinitions.GetHeaderPolicyCollection(env.IsDevelopment()));
+ app.UseHsts();
+
+ // Combined with razor routing 404 display custom page NotFound
+ app.UseStatusCodePagesWithReExecute("/error/{0}");
+
+ app.UseHttpsRedirection();
+
+ app.UseStaticFiles();
+
+ // Enable session for the application
+ app.UseSession();
+
+
+ app.UseMiddleware();
+
+
+ app.UseRouting();
+
+ app.UseAuthentication();
+ app.UseAuthorization();
+ app.UseMiddleware();
+ app.UseMiddleware();
+ app.UseMiddleware();
+ app.UseMiddlewareForFeature(FeatureFlags.IsMaintenanceModeEnabled);
+
+ app.UseConcernsCaseworkEndpoints();
+
+ app.UseEndpoints(endpoints =>
+ {
+ endpoints.MapRazorPages();
+ });
+
+ mapper.CompileAndValidate();
+
+ // If our application gets hit really hard, then threads need to be spawned
+ // By default the number of threads that exist in the threadpool is the amount of CPUs (1)
+ // Each time we have to spawn a new thread it gets delayed by 500ms
+ // Setting the min higher means there will not be that delay in creating threads up to the min
+ // Re-evaluate this based on performance tests
+ // Found because redis kept timing out because it was delayed too long waiting for a thread to execute
+ ThreadPool.SetMinThreads(400, 400);
+
+ // Add Health Checks
+ app.UseHealthChecks("/health");
+ }
+
+ ///
+ /// Builds Authorization policy
+ /// Ensure authenticated user and restrict roles if they are provided in configuration
+ ///
+ /// AuthorizationPolicyBuilder
+ private AuthorizationPolicyBuilder SetupAuthorizationPolicyBuilder()
+ {
+ var policyBuilder = new AuthorizationPolicyBuilder();
+ var allowedRoles = Configuration.GetSection("AzureAd")["AllowedRoles"];
+ policyBuilder.RequireAuthenticatedUser();
+ // Allows us to add in role support later.
+ if (!string.IsNullOrWhiteSpace(allowedRoles))
+ {
+ policyBuilder.RequireClaim(ClaimTypes.Role, allowedRoles.Split(','));
+ }
+
+ return policyBuilder;
+ }
+ }
}