Compare commits

..

14 Commits

Author SHA1 Message Date
khwezi c06cf13add Merge pull request 'Added data protection database based support' (#122) from dataprotection into master
Reviewed-on: #122
2026-06-14 09:58:17 +02:00
Khwezi Mngoma 4e9e428ab5 Added data protection database based support
continuous-integration/drone/pr Build is passing
2026-06-14 09:57:24 +02:00
khwezi 92c60e6616 Merge pull request 'Refactored AddLiteCharmsWebSecurity to be OS aware when it handles data protection keys' (#121) from dataprotection into master
Reviewed-on: #121
2026-06-13 23:41:30 +02:00
Khwezi Mngoma 9099610185 Refactored AddLiteCharmsWebSecurity to be OS aware when it handles data protection keys
continuous-integration/drone/pr Build is passing
2026-06-13 23:41:02 +02:00
khwezi 21788c66a3 Merge pull request 'Added data protection keys to web iodc middleware regirtration method' (#120) from dataprotection into master
Reviewed-on: #120
2026-06-13 23:34:35 +02:00
Khwezi Mngoma dfaa62ea75 Added data protection keys to web iodc middleware regirtration method
continuous-integration/drone/pr Build is passing
2026-06-13 23:34:07 +02:00
khwezi 54ef7a6e5f Merge pull request 'Fixed cookie and header collision issue on signout' (#119) from logout-fix into master
Reviewed-on: #119
2026-06-13 23:07:22 +02:00
Khwezi Mngoma 0ec7ef4861 Fixed cookie and header collision issue on signout
continuous-integration/drone/pr Build is passing
2026-06-13 23:06:53 +02:00
khwezi 6594e0aecd Merge pull request 'Fixed the redirect URI on logout so its passed by the caller' (#118) from logout-fix into master
Reviewed-on: #118
2026-06-13 22:51:48 +02:00
Khwezi Mngoma 088e64f28f Fixed the redirect URI on logout so its passed by the caller
continuous-integration/drone/pr Build is passing
2026-06-13 22:51:07 +02:00
Khwezi Mngoma 3803ae2999 Merged incoming changes 2026-06-13 21:39:43 +02:00
Khwezi Mngoma 398a8d3827 Refactored service bus lifetiemes to singleton 2026-06-13 21:34:59 +02:00
khwezi b09af460f1 Merge pull request 'Refactored usaged of merchant payment id usage' (#117) from payments into master
Reviewed-on: #117
2026-06-13 21:21:21 +02:00
Khwezi Mngoma 7c5b5f1728 Refactored usaged of merchant payment id usage
continuous-integration/drone/pr Build is passing
2026-06-13 21:20:30 +02:00
14 changed files with 454 additions and 9 deletions
@@ -0,0 +1,40 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<SignAssembly>True</SignAssembly>
<AssemblyOriginatorKeyFile>..\LiteCharms.snk</AssemblyOriginatorKeyFile>
</PropertyGroup>
<!-- Nuget Package Details -->
<PropertyGroup>
<PackageId>LiteCharms.Abstractions</PackageId>
<Version>1.0.20</Version>
<Authors>Khwezi Mngoma</Authors>
<Company>Lite Charms (PTY) Ltd</Company>
<Description>Shared abstractions for Lite Charms applications.</Description>
<PackageProjectUrl>https://gitea.khongisa.co.za/litecharms/components</PackageProjectUrl>
<RepositoryUrl>https://gitea.khongisa.co.za/litecharms/components.git</RepositoryUrl>
<RepositoryType>git</RepositoryType>
<PackageLicenseFile>LICENSE</PackageLicenseFile>
<PackageTags>utility;dotnet</PackageTags>
<PackageIcon>icon.png</PackageIcon>
</PropertyGroup>
<ItemGroup>
<None Include="..\LICENSE" Pack="true" PackagePath="\" />
<None Include="..\icon.png" Pack="true" PackagePath="\" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="FluentResults" Version="4.0.0" />
<PackageReference Include="Mediator.Abstractions" Version="3.0.2" />
<Using Include="Mediator" />
<Using Include="FluentResults" />
<Using Include="System.Threading.Channels" />
</ItemGroup>
</Project>
@@ -0,0 +1,45 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<SignAssembly>True</SignAssembly>
<AssemblyOriginatorKeyFile>..\LiteCharms.snk</AssemblyOriginatorKeyFile>
</PropertyGroup>
<!-- Nuget Package Details -->
<PropertyGroup>
<PackageId>LiteCharms.Entities</PackageId>
<Version>1.0.20</Version>
<Authors>Khwezi Mngoma</Authors>
<Company>Lite Charms (PTY) Ltd</Company>
<Description>Shared entities for Lite Charms applications.</Description>
<PackageProjectUrl>https://gitea.khongisa.co.za/litecharms/components</PackageProjectUrl>
<RepositoryUrl>https://gitea.khongisa.co.za/litecharms/components.git</RepositoryUrl>
<RepositoryType>git</RepositoryType>
<PackageLicenseFile>LICENSE</PackageLicenseFile>
<PackageTags>utility;dotnet</PackageTags>
<PackageIcon>icon.png</PackageIcon>
</PropertyGroup>
<ItemGroup>
<None Include="..\LICENSE" Pack="true" PackagePath="\"/>
<None Include="..\icon.png" Pack="true" PackagePath="\" />
</ItemGroup>
<!-- Database -->
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="10.0.7" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="10.0.7" />
<!-- Global Usings -->
<Using Include="Microsoft.EntityFrameworkCore" />
<Using Include="Microsoft.EntityFrameworkCore.Metadata.Builders" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\LiteCharms.Models\LiteCharms.Models.csproj" />
</ItemGroup>
</Project>
@@ -66,7 +66,7 @@ public sealed class PayfastPaymentConfirmationReceivedEventHandler(IServiceProvi
{
OrderId = orderResult.Value.Id,
PaymentId = paymentResult.Value.Id,
PaymentGatewayReference = payload.PaymentId!,
PaymentGatewayReference = payload.MerchantPaymentId!,
Status = LedgerStatuses.Completed,
CustomerId = orderResult.Value.CustomerId,
}, cancellationToken);
@@ -91,7 +91,7 @@ public sealed class PayfastPaymentConfirmationReceivedEventHandler(IServiceProvi
{
OrderId = orderResult.Value.Id,
PaymentId = paymentResult.Value.Id,
PaymentGatewayReference = payload.PaymentId!,
PaymentGatewayReference = payload.MerchantPaymentId!,
Status = ledgerStatus,
CustomerId = orderResult.Value.CustomerId,
}, cancellationToken);
@@ -0,0 +1,17 @@
{
"payfast-local": {
"baseUrl": "https://localhost:7196",
"paymentId": "jdPB2zaKM3Z",
"signature": "6aeff59bb74f2448ff2c3d81b2ec95de",
"item_name": "System Architecture Book",
"amount": "350.00"
},
"payfast-uat": {
"baseUrl": "https://api.uat.midrandbooks.co.za",
"paymentId": "jdPB2zaKM3Z",
"signature": "6aeff59bb74f2448ff2c3d81b2ec95de",
"item_name": "System Architecture Book",
"amount": "350.00"
}
}
+18 -5
View File
@@ -2,6 +2,9 @@
using LiteCharms.Features.Api;
using LiteCharms.Features.Api.Configuration;
using LiteCharms.Features.Api.Sdk;
using LiteCharms.Features.Postgres;
using Microsoft.AspNetCore.Hosting;
using System.Runtime.InteropServices;
namespace LiteCharms.Features.Extensions;
@@ -18,7 +21,7 @@ public static class Api
return services;
}
public static IServiceCollection AddSecurityApiSdk(this IServiceCollection services, IConfiguration configuration)
{
var configSection = configuration.GetSection(nameof(LiteCharmsClientSettings));
@@ -53,6 +56,9 @@ public static class Api
public static IServiceCollection AddLiteCharmsWebSecurity(this IServiceCollection services, IConfiguration configuration)
{
services.AddDataProtection().PersistKeysToDbContext<DataProtectionDbContext>()
.SetApplicationName("LiteCharmsApp");
var configSection = configuration.GetSection(nameof(LiteCharmsSettings));
var authOptions = new LiteCharmsSettings();
@@ -77,6 +83,8 @@ public static class Api
options.SaveTokens = true;
options.GetClaimsFromUserInfoEndpoint = true;
options.ForwardSignOut = CookieAuthenticationDefaults.AuthenticationScheme;
options.Scope.Clear();
options.Scope.Add("openid");
options.Scope.Add("profile");
@@ -143,17 +151,22 @@ public static class Api
});
});
app.MapGet("/logout", async (HttpContext context) =>
app.MapGet("/logout", async (HttpContext context, string? redirectUri = null) =>
{
var idToken = await context.GetTokenAsync("id_token");
var authProperties = new AuthenticationProperties { RedirectUri = "/", };
if (string.IsNullOrWhiteSpace(redirectUri))
{
var host = context.Request.Host.ToUriComponent();
redirectUri = $"https://{host}/";
}
if (!string.IsNullOrEmpty(idToken))
var authProperties = new AuthenticationProperties { RedirectUri = redirectUri, };
if (!string.IsNullOrEmpty(idToken))
authProperties.Parameters.Add("id_token_hint", idToken);
await context.SignOutAsync(OpenIdConnectDefaults.AuthenticationScheme, authProperties);
await context.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
});
return app;
+23 -2
View File
@@ -1,6 +1,27 @@
namespace LiteCharms.Features.Extensions;
using LiteCharms.Features.Postgres;
namespace LiteCharms.Features.Extensions;
public static class Postgres
{
public const string SchedulerDbConfigName = "PostgresScheduler";
public const string SchedulerDbConfigName = "PostgresScheduler";
public const string DataProtectionDbConfigName = "PostgresDataProtection";
public static IServiceCollection AddDataProtectionDatabase(this IServiceCollection services, IConfiguration configuration)
{
var connectionString = configuration.GetConnectionString(DataProtectionDbConfigName);
var dataSourceBuilder = new NpgsqlDataSourceBuilder(connectionString);
dataSourceBuilder.ConfigureTypeLoading(options => { options.EnableTypeLoading(false); });
var dataSource = dataSourceBuilder.Build();
services.AddSingleton(dataSource);
services.AddPooledDbContextFactory<DataProtectionDbContext>(options =>
options.UseNpgsql(dataSource));
return services;
}
}
@@ -153,9 +153,11 @@
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="10.0.2" />
<PackageReference Include="Microsoft.AspNetCore.DataProtection.EntityFrameworkCore" Version="10.0.9" />
<!-- Global Usings -->
<Using Include="Npgsql" />
<Using Include="Microsoft.AspNetCore.DataProtection.EntityFrameworkCore" />
<Using Include="Microsoft.EntityFrameworkCore" />
<Using Include="Microsoft.EntityFrameworkCore.Design" />
<Using Include="Microsoft.EntityFrameworkCore.Metadata.Builders" />
@@ -194,6 +196,7 @@
<!-- Shared Usings -->
<ItemGroup>
<Using Include="Microsoft.AspNetCore.DataProtection" />
<Using Include="Microsoft.AspNetCore.Components.Server.ProtectedBrowserStorage" />
<Using Include="System.Text.Json.Serialization" />
<Using Include="System.Reflection" />
@@ -0,0 +1,13 @@
namespace LiteCharms.Features.Postgres;
public class DataProtectionDbContext(DbContextOptions<DataProtectionDbContext> options) : DbContext(options), IDataProtectionKeyContext
{
public DbSet<DataProtectionKey> DataProtectionKeys { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<DataProtectionKey>(entity => entity.ToTable(nameof(DataProtectionKeys), schema: "security"));
}
}
@@ -0,0 +1,20 @@
using static LiteCharms.Features.Extensions.Postgres;
namespace LiteCharms.Features.Postgres;
public class DataProtectionDbContextFactory : IDesignTimeDbContextFactory<DataProtectionDbContext>
{
public DataProtectionDbContext CreateDbContext(string[] args)
{
var configuration = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddUserSecrets(typeof(DataProtectionDbContext).Assembly)
.AddEnvironmentVariables()
.Build();
var optionsBuilder = new DbContextOptionsBuilder<DataProtectionDbContext>();
optionsBuilder.UseNpgsql(configuration.GetConnectionString(DataProtectionDbConfigName));
return new DataProtectionDbContext(optionsBuilder.Options);
}
}
@@ -0,0 +1,48 @@
// <auto-generated />
using LiteCharms.Features.Postgres;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
#nullable disable
namespace LiteCharms.Features.Postgres.Migrations
{
[DbContext(typeof(DataProtectionDbContext))]
[Migration("20260614075149_Init")]
partial class Init
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "10.0.9")
.HasAnnotation("Relational:MaxIdentifierLength", 63);
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<string>("FriendlyName")
.HasColumnType("text");
b.Property<string>("Xml")
.HasColumnType("text");
b.HasKey("Id");
b.ToTable("DataProtectionKeys", "security");
});
#pragma warning restore 612, 618
}
}
}
@@ -0,0 +1,41 @@
using Microsoft.EntityFrameworkCore.Migrations;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
#nullable disable
namespace LiteCharms.Features.Postgres.Migrations
{
/// <inheritdoc />
public partial class Init : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.EnsureSchema(
name: "security");
migrationBuilder.CreateTable(
name: "DataProtectionKeys",
schema: "security",
columns: table => new
{
Id = table.Column<int>(type: "integer", nullable: false)
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
FriendlyName = table.Column<string>(type: "text", nullable: true),
Xml = table.Column<string>(type: "text", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_DataProtectionKeys", x => x.Id);
});
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "DataProtectionKeys",
schema: "security");
}
}
}
@@ -0,0 +1,45 @@
// <auto-generated />
using LiteCharms.Features.Postgres;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
#nullable disable
namespace LiteCharms.Features.Postgres.Migrations
{
[DbContext(typeof(DataProtectionDbContext))]
partial class DataProtectionDbContextModelSnapshot : ModelSnapshot
{
protected override void BuildModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "10.0.9")
.HasAnnotation("Relational:MaxIdentifierLength", 63);
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<string>("FriendlyName")
.HasColumnType("text");
b.Property<string>("Xml")
.HasColumnType("text");
b.HasKey("Id");
b.ToTable("DataProtectionKeys", "security");
});
#pragma warning restore 612, 618
}
}
}
@@ -0,0 +1,104 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<UserSecretsId>7770ab3b-72ee-4897-8e06-57d6613e050a</UserSecretsId>
<SignAssembly>True</SignAssembly>
<AssemblyOriginatorKeyFile>..\LiteCharms.snk</AssemblyOriginatorKeyFile>
</PropertyGroup>
<!-- Nuget Package Details -->
<PropertyGroup>
<PackageId>LiteCharms.Infrastructure</PackageId>
<Version>1.0.20</Version>
<Authors>Khwezi Mngoma</Authors>
<Company>Lite Charms (PTY) Ltd</Company>
<Description>Infrastructure components for Lite Charms applications.</Description>
<PackageProjectUrl>https://gitea.khongisa.co.za/litecharms/components</PackageProjectUrl>
<RepositoryUrl>https://gitea.khongisa.co.za/litecharms/components.git</RepositoryUrl>
<RepositoryType>git</RepositoryType>
<PackageLicenseFile>LICENSE</PackageLicenseFile>
<PackageIcon>icon.png</PackageIcon>
</PropertyGroup>
<ItemGroup>
<None Include="..\LICENSE" Pack="true" PackagePath="\" />
<None Include="..\icon.png" Pack="true" PackagePath="\" />
</ItemGroup>
<!-- Quartz Scheduler-->
<ItemGroup>
<PackageReference Include="Quartz" Version="3.18.1" />
<PackageReference Include="Quartz.Plugins" Version="3.18.1" />
<PackageReference Include="Quartz.Plugins.TimeZoneConverter" Version="3.18.1" />
<PackageReference Include="Quartz.Serialization.SystemTextJson" Version="3.18.1" />
<!-- Global Usings -->
<Using Include="Quartz" />
<Using Include="Mediator" />
<Using Include="FluentResults" />
</ItemGroup>
<!-- Configuration -->
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Configuration" Version="10.0.7" />
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="10.0.7" />
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="10.0.7" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="10.0.7" />
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="10.0.7" />
<!-- Global Usings -->
<Using Include="Microsoft.Extensions.Configuration" />
</ItemGroup>
<!-- Health Checks -->
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Diagnostics.HealthChecks" Version="10.0.7" />
<!-- Global Usings -->
<Using Include="Microsoft.Extensions.Diagnostics.HealthChecks" />
</ItemGroup>
<!-- Database -->
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="10.0.7" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="10.0.7">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="10.0.7" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="10.0.7">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="10.0.1" />
<!-- Global Usings -->
<Using Include="Npgsql" />
<Using Include="Microsoft.EntityFrameworkCore" />
<Using Include="Microsoft.EntityFrameworkCore.Design" />
<Using Include="Microsoft.EntityFrameworkCore.Metadata.Builders" />
</ItemGroup>
<!-- Project References -->
<ItemGroup>
<ProjectReference Include="..\LiteCharms.Abstractions\LiteCharms.Abstractions.csproj" />
<ProjectReference Include="..\LiteCharms.Entities\LiteCharms.Entities.csproj" />
<ProjectReference Include="..\LiteCharms.Models\LiteCharms.Models.csproj" />
</ItemGroup>
<!-- Global Usings -->
<ItemGroup>
<Using Include="System.Text.Json" />
<Using Include="Microsoft.Extensions.Hosting" />
</ItemGroup>
<ItemGroup>
<None Update="appsettings.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>
@@ -0,0 +1,35 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<SignAssembly>True</SignAssembly>
<AssemblyOriginatorKeyFile>..\LiteCharms.snk</AssemblyOriginatorKeyFile>
</PropertyGroup>
<!-- Nuget Package Details -->
<PropertyGroup>
<PackageId>LiteCharms.Models</PackageId>
<Version>1.0.20</Version>
<Authors>Khwezi Mngoma</Authors>
<Company>Lite Charms (PTY) Ltd</Company>
<Description>Shared models for Lite Charms applications.</Description>
<PackageProjectUrl>https://gitea.khongisa.co.za/litecharms/components</PackageProjectUrl>
<RepositoryUrl>https://gitea.khongisa.co.za/litecharms/components.git</RepositoryUrl>
<RepositoryType>git</RepositoryType>
<PackageLicenseFile>LICENSE</PackageLicenseFile>
<PackageTags>utility;dotnet</PackageTags>
<PackageIcon>icon.png</PackageIcon>
</PropertyGroup>
<!-- Global Usings -->
<ItemGroup>
<Using Include="System.ComponentModel.DataAnnotations"/>
</ItemGroup>
<ItemGroup>
<None Include="..\LICENSE" Pack="true" PackagePath="\" />
<None Include="..\icon.png" Pack="true" PackagePath="\" />
</ItemGroup>
</Project>