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>
34 lines
1.1 KiB
C#
34 lines
1.1 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
|
|
namespace Svrnty.CQRS.Abstractions.Security;
|
|
|
|
/// <summary>
|
|
/// Shared shape for command and query authorization-check contexts. Checks
|
|
/// receive the request type, the materialized (and validated) request instance,
|
|
/// a scoped <see cref="IServiceProvider"/>, and a free-form <see cref="Items"/>
|
|
/// dictionary that lets checks in the same pipeline pass signals to each other
|
|
/// (e.g. a future mobile-attestation check stamping "mobile_attested" for the
|
|
/// Altcha check to read).
|
|
/// </summary>
|
|
public abstract class AuthorizationCheckContext
|
|
{
|
|
public required IServiceProvider Services { get; init; }
|
|
|
|
public IDictionary<object, object?> Items { get; } = new Dictionary<object, object?>();
|
|
}
|
|
|
|
public sealed class CommandAuthorizationCheckContext : AuthorizationCheckContext
|
|
{
|
|
public required Type CommandType { get; init; }
|
|
|
|
public required object Command { get; init; }
|
|
}
|
|
|
|
public sealed class QueryAuthorizationCheckContext : AuthorizationCheckContext
|
|
{
|
|
public required Type QueryType { get; init; }
|
|
|
|
public required object Query { get; init; }
|
|
}
|