Introduces a non-breaking, multi-instance authorization-check pipeline
that runs alongside the existing single-instance auth services.
Motivation
- Cross-cutting checks (proof-of-work, mobile attestation, rate-limit
gates, IP allow-lists) don't belong in consumer auth services — they
ship from framework modules and self-apply via attributes.
- The existing ICommandAuthorizationService takes only a Type; checks
need the request *instance* to read payload fields (e.g. an Altcha
solution carried on the command).
Shape
- New abstractions: ICommandAuthorizationCheck, IQueryAuthorizationCheck,
CommandAuthorizationCheckContext, QueryAuthorizationCheckContext.
- Context carries (Type, Instance, IServiceProvider, Items dict). The
Items dict lets sibling checks signal one another — e.g. a future
mobile-attestation check stamps "mobile_attested" for the Altcha
check to read as a bypass.
- AND semantics: framework resolves IEnumerable<…Check>, runs each in
registration order, first non-Allowed short-circuits.
- Wired into MinimalApi (commands + queries, POST + GET) and the
Svrnty.CQRS.Grpc.Generators source generator (commands, queries,
dynamic queries). In all paths the checks run AFTER the instance
is materialized and validated, BEFORE handler invocation.
Backward compatibility
- No registered checks = today's behavior exactly.
- ICommandAuthorizationService / IQueryAuthorizationService signatures
unchanged; consumers' existing auth services keep working untouched.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Generated CommandServiceImpl.g.cs had warnings like:
Slug = request.Slug?.ToList(), // CS8601 if Slug is non-nullable List<T>
The ?. was over-defensive: proto3 repeated fields are emitted as
RepeatedField<T> in C# and are NEVER null. The conditional access
made the result List<T>? which then triggered CS8601 when assigned
to a non-nullable target on the command POCO.
Dropped ?. in 4 emission sites in GrpcGenerator.cs covering:
- Top-level primitive list mapping (line 872)
- Top-level Guid list mapping (line 861)
- Nested primitive list mapping in NestedPropertyAssignment (line 1083)
- Complex list .Select chain in GenerateComplexListMapping (line 974,
conditional: kept ?. for value-type collections where source.Items is
read off a possibly-null wrapper message)
Real fix in the generator instead of CS8601 NoWarn suppression in
consumer csprojs. Consumers can drop the suppression after bumping.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Generated code was using locale-dependent parsing for decimal values.
On systems with comma decimal separator (e.g., French locale), parsing
"0.95" would throw FormatException because the system expected "0,95".
Switched all 4 decimal.Parse() call sites in the generated proto→domain
mappers to pass System.Globalization.CultureInfo.InvariantCulture for
consistent behavior across locales.
Inspired by JP's commit 599204d on feat/grpc-generator-improvements
(applied manually since cherry-pick had heavy context conflicts).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Automated formatting: BOM removal, using sort order, final newlines,
whitespace normalization across all projects.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Generic types like Translation<T> now produce qualified message names
(e.g. TranslationOfFaqTranslationQueryItem) to avoid duplicate message
definitions in generated .proto files.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace ProtoFileSourceGenerator and WriteProtoFileTask with a new
GenerateProtoFileTask that creates its own Roslyn compilation. This
solves the timing issue where source generators run too late for
Grpc.Tools to process the generated proto files.
The new task runs after ResolveAssemblyReferences but before
_gRPC_GetProtoc and CoreCompile, ensuring the proto file exists
when Grpc.Tools needs it.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
If a proto file already exists (committed to repo), don't overwrite it
with a placeholder. This allows first-time builds to work correctly
when the proto file is already in the repository.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Grpc.Tools needs service definitions to generate the base classes
(CommandService+CommandServiceBase, etc.) that GrpcGenerator looks for.
Without these, the service bases wouldn't exist on first build.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The WriteProtoFileTask now receives RootNamespace and AssemblyName from
MSBuild and uses them for the placeholder proto's csharp_namespace instead
of hardcoded "Generated.Grpc". This ensures GrpcGenerator can find the
service base types on first build, enabling gRPC service registration
to work without requiring a second build.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The generated proto file holder class was internal, preventing
reflection-based service registration from discovering it across
assembly boundaries.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add ICommandAuthorizationService check to CommandServiceImpl
- Add IQueryAuthorizationService check to QueryServiceImpl
- Add IQueryAuthorizationService check to DynamicQueryServiceImpl
- Return Unauthenticated/PermissionDenied gRPC status codes
- Use global:: prefix for Grpc.Core namespace to avoid conflicts
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Resolve ICommandAuthorizationService and IQueryAuthorizationService from request-scoped serviceProvider
- Allows Scoped authorization services that depend on DbContext
- Updated both MinimalApi and DynamicQuery.MinimalApi
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add DateTime/Timestamp conversion in nested property mapping
- Add IsReadOnly property detection to skip computed properties
- Extract ElementNestedProperties for complex list element types
- Skip read-only properties in GenerateComplexObjectMapping and GenerateComplexListMapping
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add proper complex type mapping for command results (same as queries already had)
- Handle nullable primitives (long?, int?, etc.) with default value fallback
- Fixes CS0029 and CS0266 compilation errors in generated gRPC service implementations
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add IsCollectionTypeByInterface() to detect types implementing IList<T>, ICollection<T>, IEnumerable<T>
- Add GetCollectionElementTypeByInterface() to extract element type from collection interfaces
- Add IsCollectionInternalProperty() to filter out Count, Capacity, IsReadOnly, etc.
- Update GenerateComplexTypeMessage to generate `repeated T items` for collection types
- Filter out indexers (!p.IsIndexer) and collection-internal properties from all property extraction
This fixes the invalid proto syntax where C# indexers (this[]) were being generated
as proto fields, causing proto compilation errors.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The MapToProtoModel function was silently failing when mapping Guid
properties to proto string fields, causing IDs to be empty in gRPC
responses. Added explicit Guid → string conversion handling.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>