Adds a [Altcha]-decorated ProtectedActionCommand with IHasAltchaSolution and a StubAltchaVerifier that treats the literal "valid-solution" as passing PoW. Exercises both the HTTP MinimalApi and gRPC pipelines without requiring an external altcha service. Validated 4 scenarios on each transport (8 total, all pass): HTTP /api/command/protectedAction POST 6001 gRPC :6000 ------------------------------------------------------------------- no AltchaSolution 401 Unauthenticated AltchaSolution = "wrong" 401 Unauthenticated AltchaSolution = "valid-solution" 200 result OK + result addUser (no [Altcha]) 200 result OK + result The last row confirms backward compatibility: a request type that isn't decorated with [Altcha] bypasses the check entirely — the AltchaAuthorizationCheck self-applies and no-ops, and any consumer that doesn't call AddSvrntyAltcha() sees zero behavior change. Generated CommandServiceImpl.g.cs verified to include the ICommandAuthorizationCheck loop after validation, before handler invocation, with the materialized command instance in ctx.Command. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
126 lines
2.7 KiB
Protocol Buffer
126 lines
2.7 KiB
Protocol Buffer
syntax = "proto3";
|
|
|
|
option csharp_namespace = "Svrnty.Sample.Grpc";
|
|
|
|
package cqrs;
|
|
|
|
// Command service for CQRS operations
|
|
service CommandService {
|
|
// AddUserCommand operation
|
|
rpc AddUser (AddUserCommandRequest) returns (AddUserCommandResponse);
|
|
|
|
// ProtectedActionCommand operation
|
|
rpc ProtectedAction (ProtectedActionCommandRequest) returns (ProtectedActionCommandResponse);
|
|
|
|
// RemoveUserCommand operation
|
|
rpc RemoveUser (RemoveUserCommandRequest) returns (RemoveUserCommandResponse);
|
|
|
|
}
|
|
|
|
// Query service for CQRS operations
|
|
service QueryService {
|
|
// FetchUserQuery operation
|
|
rpc FetchUser (FetchUserQueryRequest) returns (FetchUserQueryResponse);
|
|
|
|
}
|
|
|
|
// DynamicQuery service for CQRS operations
|
|
service DynamicQueryService {
|
|
// Dynamic query for User
|
|
rpc QueryUsers (DynamicQueryUsersRequest) returns (DynamicQueryUsersResponse);
|
|
|
|
}
|
|
|
|
// Request message for AddUserCommand
|
|
message AddUserCommandRequest {
|
|
string name = 1;
|
|
string email = 2;
|
|
int32 age = 3;
|
|
}
|
|
|
|
// Response message for AddUserCommand
|
|
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;
|
|
}
|
|
|
|
// Response message for RemoveUserCommand
|
|
message RemoveUserCommandResponse {
|
|
}
|
|
|
|
// Request message for FetchUserQuery
|
|
message FetchUserQueryRequest {
|
|
int32 user_id = 1;
|
|
}
|
|
|
|
// Response message for FetchUserQuery
|
|
message FetchUserQueryResponse {
|
|
User result = 1;
|
|
}
|
|
|
|
// User entity
|
|
message User {
|
|
int32 id = 1;
|
|
string name = 2;
|
|
string email = 3;
|
|
}
|
|
|
|
// Dynamic query filter with AND/OR support
|
|
message DynamicQueryFilter {
|
|
string path = 1;
|
|
int32 type = 2; // PoweredSoft.DynamicQuery.Core.FilterType
|
|
string value = 3;
|
|
repeated DynamicQueryFilter and = 4;
|
|
repeated DynamicQueryFilter or = 5;
|
|
}
|
|
|
|
// Dynamic query sort
|
|
message DynamicQuerySort {
|
|
string path = 1;
|
|
bool ascending = 2;
|
|
}
|
|
|
|
// Dynamic query group
|
|
message DynamicQueryGroup {
|
|
string path = 1;
|
|
}
|
|
|
|
// Dynamic query aggregate
|
|
message DynamicQueryAggregate {
|
|
string path = 1;
|
|
int32 type = 2; // PoweredSoft.DynamicQuery.Core.AggregateType
|
|
}
|
|
|
|
// Dynamic query request for User
|
|
message DynamicQueryUsersRequest {
|
|
int32 page = 1;
|
|
int32 page_size = 2;
|
|
repeated DynamicQueryFilter filters = 3;
|
|
repeated DynamicQuerySort sorts = 4;
|
|
repeated DynamicQueryGroup groups = 5;
|
|
repeated DynamicQueryAggregate aggregates = 6;
|
|
}
|
|
|
|
// Dynamic query response for User
|
|
message DynamicQueryUsersResponse {
|
|
repeated User data = 1;
|
|
int64 total_records = 2;
|
|
int32 number_of_pages = 3;
|
|
}
|
|
|