| | | 1 | | using AsiBackbone.Core.Signing; |
| | | 2 | | using AsiBackbone.DependencyInjection; |
| | | 3 | | using Microsoft.Extensions.DependencyInjection; |
| | | 4 | | |
| | | 5 | | namespace AsiBackbone.Signing.LocalDevelopment; |
| | | 6 | | |
| | | 7 | | /// <summary> |
| | | 8 | | /// Provides explicit builder facade extension methods for local-development signing. |
| | | 9 | | /// </summary> |
| | | 10 | | public static class LocalDevelopmentSigningBuilderExtensions |
| | | 11 | | { |
| | | 12 | | /// <summary> |
| | | 13 | | /// Adds local-development signing and verification through the AsiBackbone builder facade using default options. |
| | | 14 | | /// </summary> |
| | | 15 | | public static IAsiBackboneBuilder UseLocalDevelopmentSigning(this IAsiBackboneBuilder builder) |
| | | 16 | | { |
| | 2 | 17 | | return builder.UseLocalDevelopmentSigning(LocalDevelopmentSigningOptions.Create()); |
| | | 18 | | } |
| | | 19 | | |
| | | 20 | | /// <summary> |
| | | 21 | | /// Adds local-development signing and verification through the AsiBackbone builder facade using configured options. |
| | | 22 | | /// </summary> |
| | | 23 | | /// <exception cref="InvalidOperationException"> |
| | | 24 | | /// Thrown when the configured local-development signing options are invalid. |
| | | 25 | | /// </exception> |
| | | 26 | | public static IAsiBackboneBuilder UseLocalDevelopmentSigning( |
| | | 27 | | this IAsiBackboneBuilder builder, |
| | | 28 | | LocalDevelopmentSigningOptions options) |
| | | 29 | | { |
| | 14 | 30 | | ArgumentNullException.ThrowIfNull(builder); |
| | 12 | 31 | | ArgumentNullException.ThrowIfNull(options); |
| | | 32 | | |
| | | 33 | | // Register a snapshot, and run the validation and production guard against that snapshot. Registering the |
| | | 34 | | // caller's instance let an assignment made after this call, such as setting AllowInProduction once the guard |
| | | 35 | | // had passed, change what the registered provider did. |
| | 11 | 36 | | LocalDevelopmentSigningOptions snapshot = options.Snapshot(); |
| | 11 | 37 | | snapshot.Validate(); |
| | 10 | 38 | | ThrowIfProduction(snapshot); |
| | | 39 | | |
| | | 40 | | // The registered options instance is resolvable and mutable, so the service gets its own snapshot. Closing over |
| | | 41 | | // the registered instance let a change made to the resolved options before the service was first resolved |
| | | 42 | | // reach the provider. |
| | 8 | 43 | | LocalDevelopmentSigningOptions serviceSnapshot = snapshot.Snapshot(); |
| | | 44 | | |
| | 8 | 45 | | _ = builder.Services.AddSingleton(snapshot); |
| | 8 | 46 | | _ = builder.Services.AddSingleton(serviceProvider => new LocalDevelopmentSigningService( |
| | 8 | 47 | | serviceSnapshot, |
| | 8 | 48 | | serviceProvider.GetService<TimeProvider>() ?? TimeProvider.System)); |
| | 8 | 49 | | _ = builder.Services.AddSingleton<IGovernanceSigningService>(serviceProvider => |
| | 8 | 50 | | serviceProvider.GetRequiredService<LocalDevelopmentSigningService>()); |
| | 8 | 51 | | _ = builder.Services.AddSingleton<IGovernanceSignatureVerificationService>(serviceProvider => |
| | 8 | 52 | | serviceProvider.GetRequiredService<LocalDevelopmentSigningService>()); |
| | | 53 | | |
| | 8 | 54 | | return builder; |
| | | 55 | | } |
| | | 56 | | |
| | | 57 | | /// <summary> |
| | | 58 | | /// Rejects registration of the local-development signing provider in a production environment. |
| | | 59 | | /// </summary> |
| | | 60 | | /// <remarks> |
| | | 61 | | /// The provider generates its key per process, so artifacts it signs stop verifying after a restart. An analyzer ca |
| | | 62 | | /// only see a call it can read; this guard covers the unconditional registration that reaches production at runtime |
| | | 63 | | /// </remarks> |
| | | 64 | | /// <exception cref="InvalidOperationException">The environment is production and the options do not allow it.</exce |
| | | 65 | | private static void ThrowIfProduction(LocalDevelopmentSigningOptions options) |
| | | 66 | | { |
| | 10 | 67 | | if (options.AllowInProduction) |
| | | 68 | | { |
| | 1 | 69 | | return; |
| | | 70 | | } |
| | | 71 | | |
| | 9 | 72 | | string? environmentName = options.EnvironmentName |
| | 9 | 73 | | ?? Environment.GetEnvironmentVariable("DOTNET_ENVIRONMENT") |
| | 9 | 74 | | ?? Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT"); |
| | | 75 | | |
| | 9 | 76 | | if (string.Equals(environmentName, "Production", StringComparison.OrdinalIgnoreCase)) |
| | | 77 | | { |
| | 2 | 78 | | throw new InvalidOperationException( |
| | 2 | 79 | | "Local-development signing cannot be registered in the Production environment. Its key is generated per |
| | | 80 | | } |
| | 7 | 81 | | } |
| | | 82 | | } |