Security #1
@@ -8,18 +8,96 @@
|
|||||||
<AssemblyOriginatorKeyFile>..\PostFundManagement.snk</AssemblyOriginatorKeyFile>
|
<AssemblyOriginatorKeyFile>..\PostFundManagement.snk</AssemblyOriginatorKeyFile>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<!-- Security (IODC)-->
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="10.0.11" />
|
<PackageReference Include="IdentityModel.AspNetCore" Version="4.3.0" />
|
||||||
<PackageReference Include="Microsoft.OpenApi" Version="2.12.0" />
|
<PackageReference Include="IdentityModel.AspNetCore.OAuth2introspection" Version="6.2.0" />
|
||||||
<PackageReference Include="Scalar.AspNetCore" Version="2.16.20" />
|
<PackageReference Include="IdentityServer4.AccessTokenValidation" Version="3.0.1" />
|
||||||
</ItemGroup>
|
<PackageReference Include="IdentityModel" Version="6.2.0" />
|
||||||
|
<PackageReference Include="KubernetesClient" Version="19.0.2" />
|
||||||
|
<PackageReference Include="Microsoft.AspNetCore.Authentication.Certificate" Version="10.0.11" />
|
||||||
|
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="10.0.11" />
|
||||||
|
|
||||||
<!-- Global Usings -->
|
|
||||||
<ItemGroup>
|
|
||||||
<Using Include="System.Security.Cryptography.X509Certificates" />
|
|
||||||
<Using Include="Microsoft.AspNetCore.Authentication" />
|
<Using Include="Microsoft.AspNetCore.Authentication" />
|
||||||
<Using Include="Microsoft.AspNetCore.Authentication.Cookies" />
|
<Using Include="Microsoft.AspNetCore.Authentication.Cookies" />
|
||||||
<Using Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" />
|
<Using Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<!-- Health Checks -->
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="AspNetCore.HealthChecks.UI" Version="9.0.0" />
|
||||||
|
<PackageReference Include="AspNetCore.HealthChecks.UI.Client" Version="9.0.0" />
|
||||||
|
<PackageReference Include="AspNetCore.HealthChecks.UI.InMemory.Storage" Version="9.0.0" />
|
||||||
|
<PackageReference Include="AspNetCore.HealthChecks.NpgSql" Version="9.0.0" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<!-- API Versioning -->
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="AccessTokenClient.Extensions" Version="5.1.0" />
|
||||||
|
<PackageReference Include="Asp.Versioning.Abstractions" Version="10.2.1" />
|
||||||
|
<PackageReference Include="Asp.Versioning.Http" Version="10.2.2" />
|
||||||
|
<PackageReference Include="Asp.Versioning.Mvc.ApiExplorer" Version="10.2.1" />
|
||||||
|
|
||||||
|
<Using Include="Asp.Versioning" />
|
||||||
|
<Using Include="Asp.Versioning.Builder" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<!-- API Documentation -->
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="10.0.11" />
|
||||||
|
<PackageReference Include="Scalar.AspNetCore" Version="2.16.20" />
|
||||||
|
|
||||||
|
<Using Include="Scalar.AspNetCore" />
|
||||||
|
<Using Include="Microsoft.OpenApi" />
|
||||||
|
<Using Include="Microsoft.AspNetCore.OpenApi" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<!-- file nesting -->
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectCapability Include="ConfigurableFileNesting" />
|
||||||
|
<ProjectCapability Include="ConfigurableFileNestingFeatureEnabled" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="LiteCharms.Features" Version="1.137.0" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<!-- UI -->
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="ANM.Blazored.Toast" Version="0.1.1" />
|
||||||
|
<PackageReference Include="LiteCharms.Features.MidrandBooks" Version="1.137.0" />
|
||||||
|
|
||||||
|
<!-- Global Usings -->
|
||||||
|
<Using Include="Blazored.Toast.Services" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<!-- CQRS -->
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Mediator.SourceGenerator" Version="3.0.2">
|
||||||
|
<PrivateAssets>all</PrivateAssets>
|
||||||
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
|
</PackageReference>
|
||||||
|
|
||||||
|
<!-- Global Usings -->
|
||||||
|
<Using Include="FluentResults" />
|
||||||
|
<Using Include="Mediator" />
|
||||||
|
<Using Include="Quartz" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<!-- Health Checks -->
|
||||||
|
<ItemGroup>
|
||||||
|
<Using Include="Microsoft.AspNetCore.Diagnostics.HealthChecks" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<!-- Shared Global Usings -->
|
||||||
|
<ItemGroup>
|
||||||
|
<Using Include="System.Web" />
|
||||||
|
<Using Include="System.Diagnostics" />
|
||||||
|
<Using Include="System.Reflection" />
|
||||||
|
<Using Include="Microsoft.AspNetCore.Mvc" />
|
||||||
|
<Using Include="System.ComponentModel.DataAnnotations" />
|
||||||
|
<Using Include="Microsoft.Extensions.DependencyInjection.Extensions" />
|
||||||
|
<Using Include="System.Security.Cryptography.X509Certificates" />
|
||||||
<Using Include="Microsoft.AspNetCore.DataProtection" />
|
<Using Include="Microsoft.AspNetCore.DataProtection" />
|
||||||
<Using Include="Refit" />
|
<Using Include="Refit" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|||||||
@@ -1,23 +1,100 @@
|
|||||||
using Scalar.AspNetCore;
|
using PostFundManagement.Api.Extensions;
|
||||||
|
using PostFundManagement.Domain.Extensions;
|
||||||
|
using PostFundManagement.Domain.Mediator;
|
||||||
|
using PostFundManagement.Infrastructure.Extensions;
|
||||||
|
using static PostFundManagement.Domain.Extensions.Constants;
|
||||||
|
|
||||||
var builder = WebApplication.CreateBuilder(args);
|
var builder = WebApplication.CreateBuilder(args);
|
||||||
|
|
||||||
builder.Services.AddApiVersioning();
|
builder.Services.AddEndpointsApiExplorer()
|
||||||
builder.Services.AddOpenApi();
|
.AddApiVersioning(options => options.ApiVersionReader = new HeaderApiVersionReader());
|
||||||
|
|
||||||
|
builder.Services.AddApiVersioning().AddApiExplorer();
|
||||||
|
|
||||||
|
builder.Services.AddEndpoints(Assembly.GetExecutingAssembly());
|
||||||
|
builder.Services.AddApiServices(builder.Configuration);
|
||||||
|
|
||||||
|
builder.Services.AddMediator();
|
||||||
|
builder.Services.AddSecurityApiSdk(builder.Configuration);
|
||||||
|
builder.Services.AddWebSecurity(builder.Configuration);
|
||||||
|
|
||||||
|
builder.Services.AddScoped(typeof(IPipelineBehavior<,>), typeof(TelemetryPipelineBehavior<,>));
|
||||||
|
builder.Services.AddScoped(typeof(IPipelineBehavior<,>), typeof(LoggingPipelineBehavior<,>));
|
||||||
|
|
||||||
|
builder.Services.AddQuartzSchedulerClient(DefaultSchedulerName, builder.Configuration);
|
||||||
|
|
||||||
|
builder.Services.AddEmailServices(builder.Configuration);
|
||||||
|
|
||||||
|
builder.Services.AddHttpClient();
|
||||||
|
builder.Services.AddHashServices(builder.Configuration);
|
||||||
|
builder.Services.AddDataProtectionDatabase(builder.Configuration);
|
||||||
|
builder.Services.AddApplicationDbContext(builder.Configuration);
|
||||||
|
|
||||||
|
// TODO: add healthcheck services
|
||||||
|
// builder.Services.AddMidrandShopPostgresHealthCheck();
|
||||||
|
// builder.Services.AddMidrandShopQuartzHealthCheck();
|
||||||
|
// builder.Services.AddHealthChecksSupport(builder.Configuration);
|
||||||
|
|
||||||
var app = builder.Build();
|
var app = builder.Build();
|
||||||
|
|
||||||
if (app.Environment.IsDevelopment())
|
var schedulerFactory = app.Services.GetRequiredService<ISchedulerFactory>();
|
||||||
{
|
var scheduler = await schedulerFactory.GetScheduler(DefaultSchedulerName);
|
||||||
app.MapScalarApiReference(options =>
|
|
||||||
{
|
|
||||||
options.WithTitle("Post-fund Management API")
|
|
||||||
.WithTheme(ScalarTheme.BluePlanet);
|
|
||||||
});
|
|
||||||
|
|
||||||
app.MapOpenApi();
|
if (!scheduler!.IsStarted)
|
||||||
}
|
await scheduler.Start();
|
||||||
|
|
||||||
|
app.UseHsts();
|
||||||
app.UseHttpsRedirection();
|
app.UseHttpsRedirection();
|
||||||
|
|
||||||
|
app.UseRouting();
|
||||||
|
app.UseAuthentication();
|
||||||
|
app.UseAuthorization();
|
||||||
|
|
||||||
|
ApiVersionSet versionSet = app.NewApiVersionSet("v1")
|
||||||
|
.HasApiVersion(new ApiVersion(1))
|
||||||
|
.HasApiVersion(new ApiVersion(2))
|
||||||
|
.ReportApiVersions()
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
var versionGroups = new Dictionary<int, RouteGroupBuilder>
|
||||||
|
{
|
||||||
|
{ 1, app.MapGroup("v{version:apiVersion}").WithApiVersionSet(versionSet) }
|
||||||
|
};
|
||||||
|
|
||||||
|
app.MapEndpoints(versionGroups);
|
||||||
|
|
||||||
|
app.UseHealthChecks("/health", new HealthCheckOptions
|
||||||
|
{
|
||||||
|
Predicate = _ => true,
|
||||||
|
AllowCachingResponses = true,
|
||||||
|
ResponseWriter = HealthChecks.UI.Client.UIResponseWriter.WriteHealthCheckUIResponse
|
||||||
|
});
|
||||||
|
|
||||||
|
app.MapHealthChecksUI(options => { options.UIPath = "/healthui"; });
|
||||||
|
app.UseHealthChecks("/ready");
|
||||||
|
|
||||||
|
app.MapOpenApi();
|
||||||
|
|
||||||
|
var apiVersions = app.DescribeApiVersions()
|
||||||
|
.OrderByDescending(o => o.ApiVersion.MajorVersion)
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
foreach (var description in app.DescribeApiVersions().OrderByDescending(o => o.ApiVersion.MajorVersion))
|
||||||
|
app.MapScalarApiReference($"/openapi/{description.GroupName}", (options, context) =>
|
||||||
|
{
|
||||||
|
options.AddServer(new ScalarServer($"https://{context.Request.Host}"));
|
||||||
|
options.WithOpenApiRoutePattern($"/openapi/{description.GroupName}.json");
|
||||||
|
options.WithTheme(ScalarTheme.DeepSpace);
|
||||||
|
options.Agent = new ScalarAgentOptions { Disabled = true };
|
||||||
|
options.Authentication = new ScalarAuthenticationOptions { PreferredSecuritySchemes = ["Bearer"] };
|
||||||
|
});
|
||||||
|
|
||||||
|
var latestVersionGroup = apiVersions.FirstOrDefault()?.GroupName ?? "v1";
|
||||||
|
|
||||||
|
app.MapGet("/", () => Results.Redirect($"/openapi/{latestVersionGroup}"))
|
||||||
|
.ExcludeFromDescription();
|
||||||
|
|
||||||
|
if (!app.Environment.IsDevelopment())
|
||||||
|
app.UseExceptionHandler("/Error", createScopeForErrors: true);
|
||||||
|
|
||||||
app.Run();
|
app.Run();
|
||||||
@@ -1,5 +1,15 @@
|
|||||||
{
|
{
|
||||||
"MachineIdentity": {
|
"GeneralS3Settings": {
|
||||||
|
"ServiceUrl": "http://192.168.1.177:30900",
|
||||||
|
"Region": "garage",
|
||||||
|
"BucketName": "pfm.general",
|
||||||
|
"CdnBaseUrl": "https://pfm.general.cdn.khongisa.co.za"
|
||||||
|
},
|
||||||
|
"Monitoring": {
|
||||||
|
"Address": "http://aspire-dashboard-service.aspire.svc.cluster.local:18889",
|
||||||
|
"ServiceName": "MidrandBooks.DEV"
|
||||||
|
},
|
||||||
|
"SecuritySettings": {
|
||||||
"Authority": "https://sts.security.khongisa.co.za",
|
"Authority": "https://sts.security.khongisa.co.za",
|
||||||
"Audience": "pfm-api-dev"
|
"Audience": "pfm-api-dev"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ namespace PostFundManagement.Domain.Extensions;
|
|||||||
|
|
||||||
public static class Constants
|
public static class Constants
|
||||||
{
|
{
|
||||||
|
public const string DefaultSchedulerName = "pfm-scheduler";
|
||||||
|
|
||||||
public const string GeneralS3SettingsSection = "PfmS3Settings";
|
public const string GeneralS3SettingsSection = "PfmS3Settings";
|
||||||
|
|
||||||
public const string EvidenceS3SettingsSection = "EvidenceS3Settings";
|
public const string EvidenceS3SettingsSection = "EvidenceS3Settings";
|
||||||
|
|||||||
@@ -0,0 +1,23 @@
|
|||||||
|
using PostFundManagement.Domain.Configuration.Email;
|
||||||
|
using PostFundManagement.Domain.Services;
|
||||||
|
|
||||||
|
namespace PostFundManagement.Domain.Extensions;
|
||||||
|
|
||||||
|
public static class Email
|
||||||
|
{
|
||||||
|
public const string EmailFromName = "PFM Team";
|
||||||
|
public const string EmailFromAddress = "info@pfm.co.za"; // TODO: replace with real address
|
||||||
|
|
||||||
|
public static IServiceCollection AddEmailServices(this IServiceCollection services, IConfiguration configuration)
|
||||||
|
{
|
||||||
|
services.Configure<SmtpSettings>(configuration.GetSection("Email"));
|
||||||
|
|
||||||
|
services.AddSingleton<EmailService>();
|
||||||
|
|
||||||
|
services.AddOpenTelemetry()
|
||||||
|
.WithTracing(tracing => tracing.AddSource("Pfm.EmailService"))
|
||||||
|
.WithMetrics(metrics => metrics.AddMeter("Pfm.EmailService"));
|
||||||
|
|
||||||
|
return services;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,8 +6,6 @@ namespace PostFundManagement.Domain.Extensions;
|
|||||||
|
|
||||||
public static class Quartz
|
public static class Quartz
|
||||||
{
|
{
|
||||||
public const string DefaultSchedulerName = "tech-shop";
|
|
||||||
|
|
||||||
public static IServiceCollection AddQuartzSchedulerClient(this IServiceCollection services, string schedulerName, IConfiguration configuration)
|
public static IServiceCollection AddQuartzSchedulerClient(this IServiceCollection services, string schedulerName, IConfiguration configuration)
|
||||||
{
|
{
|
||||||
var connectionString = configuration.GetConnectionString(DatabaseConfigName);
|
var connectionString = configuration.GetConnectionString(DatabaseConfigName);
|
||||||
|
|||||||
Reference in New Issue
Block a user