diff --git a/Svrnty.Sample/Program.cs b/Svrnty.Sample/Program.cs index ecd5bd3..f77f3ab 100644 --- a/Svrnty.Sample/Program.cs +++ b/Svrnty.Sample/Program.cs @@ -1,6 +1,8 @@ using Microsoft.AspNetCore.Server.Kestrel.Core; using Svrnty.CQRS; using Svrnty.CQRS.Abstractions; +using Svrnty.CQRS.Altcha; +using Svrnty.CQRS.Altcha.Abstractions; using Svrnty.CQRS.DynamicQuery; using Svrnty.CQRS.FluentValidation; using Svrnty.CQRS.Grpc; @@ -27,8 +29,14 @@ builder.Services.AddDynamicQueryWithProvider(); // Register commands and queries with validators builder.Services.AddCommand(); builder.Services.AddCommand(); +builder.Services.AddCommand(); builder.Services.AddQuery(); +// Wire the Altcha check + a stub verifier so [Altcha] commands run +// through the ICommandAuthorizationCheck pipeline. +builder.Services.AddSvrntyAltcha(); +builder.Services.AddSingleton(); + // Configure CQRS with fluent API builder.Services.AddSvrntyCqrs(cqrs => { diff --git a/Svrnty.Sample/ProtectedActionCommand.cs b/Svrnty.Sample/ProtectedActionCommand.cs new file mode 100644 index 0000000..a955b35 --- /dev/null +++ b/Svrnty.Sample/ProtectedActionCommand.cs @@ -0,0 +1,37 @@ +using Svrnty.CQRS.Abstractions; +using Svrnty.CQRS.Altcha.Abstractions; + +namespace Svrnty.Sample; + +// Exercises the ICommandAuthorizationCheck seam at runtime. +// The command is decorated with [Altcha] and carries the solution +// through IHasAltchaSolution. With Svrnty.CQRS.Altcha + a registered +// IAltchaVerifier, the framework's check pipeline reads the field, +// calls the verifier, and short-circuits on failure. +[Altcha] +public sealed class ProtectedActionCommand : IHasAltchaSolution +{ + public string Action { get; set; } = string.Empty; + public string? AltchaSolution { get; set; } +} + +public sealed class ProtectedActionCommandHandler : ICommandHandler +{ + public Task HandleAsync(ProtectedActionCommand command, CancellationToken cancellationToken = default) + { + return Task.FromResult($"executed:{command.Action}"); + } +} + +// Stub verifier that doesn't talk to an external altcha service — +// enough to exercise the check pipeline in isolation. Treats the +// literal string "valid-solution" as a passing PoW solution. +public sealed class StubAltchaVerifier : IAltchaVerifier +{ + public Task VerifyAsync(string payload, CancellationToken cancellationToken = default) + { + if (payload == "valid-solution") + return Task.FromResult(AltchaVerifyResult.Success); + return Task.FromResult(AltchaVerifyResult.Fail("stub-rejected")); + } +} diff --git a/Svrnty.Sample/Protos/cqrs_services.proto b/Svrnty.Sample/Protos/cqrs_services.proto index 10bad1c..8f478ee 100644 --- a/Svrnty.Sample/Protos/cqrs_services.proto +++ b/Svrnty.Sample/Protos/cqrs_services.proto @@ -9,6 +9,9 @@ service CommandService { // AddUserCommand operation rpc AddUser (AddUserCommandRequest) returns (AddUserCommandResponse); + // ProtectedActionCommand operation + rpc ProtectedAction (ProtectedActionCommandRequest) returns (ProtectedActionCommandResponse); + // RemoveUserCommand operation rpc RemoveUser (RemoveUserCommandRequest) returns (RemoveUserCommandResponse); @@ -40,6 +43,17 @@ message AddUserCommandResponse { int32 result = 1; } +// Request message for ProtectedActionCommand +message ProtectedActionCommandRequest { + string action = 1; + string altcha_solution = 2; +} + +// Response message for ProtectedActionCommand +message ProtectedActionCommandResponse { + string result = 1; +} + // Request message for RemoveUserCommand message RemoveUserCommandRequest { int32 user_id = 1; diff --git a/Svrnty.Sample/Svrnty.Sample.csproj b/Svrnty.Sample/Svrnty.Sample.csproj index 2410a08..38231f7 100644 --- a/Svrnty.Sample/Svrnty.Sample.csproj +++ b/Svrnty.Sample/Svrnty.Sample.csproj @@ -33,6 +33,8 @@ + +