diff --git a/services/Directory/FilterLists.Directory.Api/Cors/CorsConfiguration.cs b/services/Directory/FilterLists.Directory.Api/Cors/CorsConfiguration.cs new file mode 100644 index 000000000..3401e7f89 --- /dev/null +++ b/services/Directory/FilterLists.Directory.Api/Cors/CorsConfiguration.cs @@ -0,0 +1,27 @@ +namespace FilterLists.Directory.Api.Cors; + +internal static class CorsConfiguration +{ + internal static readonly Action SetupAction = + (options, configuration) => + { + var corsOptions = configuration.GetSection(CorsOptions.Cors).Get() ?? + throw new InvalidOperationException("CORS options are not configured."); + + options.AddDefaultPolicy(policy => policy + .SetIsOriginAllowed(origin => + { + if (corsOptions.AllowedOrigins.Any(o => string.Equals(o, origin, StringComparison.OrdinalIgnoreCase))) + return true; + + if (corsOptions.StagingOriginPatterns is not null && + origin.StartsWith(corsOptions.StagingOriginPatterns.Start, StringComparison.OrdinalIgnoreCase) && + origin.EndsWith(corsOptions.StagingOriginPatterns.End, StringComparison.OrdinalIgnoreCase)) + return true; + + return false; + }) + .WithMethods("GET") + .AllowAnyHeader()); + }; +} \ No newline at end of file diff --git a/services/Directory/FilterLists.Directory.Api/Cors/CorsOptions.cs b/services/Directory/FilterLists.Directory.Api/Cors/CorsOptions.cs new file mode 100644 index 000000000..803d0a0eb --- /dev/null +++ b/services/Directory/FilterLists.Directory.Api/Cors/CorsOptions.cs @@ -0,0 +1,17 @@ +namespace FilterLists.Directory.Api.Cors; + +internal record CorsOptions +{ + public const string Cors = "Cors"; + public string[] AllowedOrigins { get; init; } = []; + public StagingOriginPatternsSettings? StagingOriginPatterns { get; init; } +} + +/// +/// Azure Static Web Apps staging environment origin patterns. +/// +internal record StagingOriginPatternsSettings +{ + public required string Start { get; init; } + public required string End { get; init; } +} \ No newline at end of file diff --git a/services/Directory/FilterLists.Directory.Api/CorsConfiguration.cs b/services/Directory/FilterLists.Directory.Api/CorsConfiguration.cs deleted file mode 100644 index 14f2242f9..000000000 --- a/services/Directory/FilterLists.Directory.Api/CorsConfiguration.cs +++ /dev/null @@ -1,21 +0,0 @@ -using Microsoft.AspNetCore.Cors.Infrastructure; - -namespace FilterLists.Directory.Api; - -internal static class CorsConfiguration -{ - private const string ProductionClientOrigin = "https://filterlists.com"; - private const string StagingClientOriginStart = "https://nice-water-05873140f"; - private const string StagingClientOriginEnd = ".eastus2.5.azurestaticapps.net"; - private const string LocalClientOrigin = "http://localhost:3000"; - - internal static readonly Action SetupAction = options => options.AddDefaultPolicy(policy => - { - policy.SetIsOriginAllowed(origin => - origin is ProductionClientOrigin or LocalClientOrigin || - (origin.StartsWith(StagingClientOriginStart, StringComparison.InvariantCulture) && - origin.EndsWith(StagingClientOriginEnd, StringComparison.InvariantCulture))) - .WithMethods("GET") - .AllowAnyHeader(); - }); -} \ No newline at end of file diff --git a/services/Directory/FilterLists.Directory.Api/Program.cs b/services/Directory/FilterLists.Directory.Api/Program.cs index 5182d8778..f6118ac88 100644 --- a/services/Directory/FilterLists.Directory.Api/Program.cs +++ b/services/Directory/FilterLists.Directory.Api/Program.cs @@ -1,4 +1,5 @@ using FilterLists.Directory.Api; +using FilterLists.Directory.Api.Cors; using FilterLists.Directory.Api.OpenApi; using FilterLists.Directory.Application; @@ -6,7 +7,7 @@ builder.WebHost.ConfigureKestrel(serverOptions => serverOptions.AddServerHeader = false); builder.AddServiceDefaults(); -builder.Services.AddCors(CorsConfiguration.SetupAction); +builder.Services.AddCors(options => CorsConfiguration.SetupAction(options, builder.Configuration)); builder.Services.AddProblemDetails(); builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(OpenApiGenConfiguration.SetupAction); @@ -16,8 +17,8 @@ app.UseExceptionHandler(); app.UseCors(); -app.MapEndpoints(); app.MapDefaultEndpoints(); +app.MapEndpoints(); app.UseSwagger(o => o.RouteTemplate = "{documentName}/openapi.json"); app.UseSwaggerUI(SwaggerUiConfiguration.SetupAction); diff --git a/services/Directory/FilterLists.Directory.Api/appsettings.Development.json b/services/Directory/FilterLists.Directory.Api/appsettings.Development.json index 0c208ae91..7143805a2 100644 --- a/services/Directory/FilterLists.Directory.Api/appsettings.Development.json +++ b/services/Directory/FilterLists.Directory.Api/appsettings.Development.json @@ -4,5 +4,10 @@ "Default": "Information", "Microsoft.AspNetCore": "Warning" } + }, + "Cors": { + "AllowedOrigins": [ + "http://localhost:3000" + ] } -} +} \ No newline at end of file diff --git a/services/Directory/FilterLists.Directory.Api/appsettings.json b/services/Directory/FilterLists.Directory.Api/appsettings.json index 10f68b8c8..300835aad 100644 --- a/services/Directory/FilterLists.Directory.Api/appsettings.json +++ b/services/Directory/FilterLists.Directory.Api/appsettings.json @@ -5,5 +5,14 @@ "Microsoft.AspNetCore": "Warning" } }, - "AllowedHosts": "*" -} + "AllowedHosts": "*", + "Cors": { + "AllowedOrigins": [ + "https://filterlists.com" + ], + "StagingOriginPatterns": { + "Start": "https://nice-water-05873140f-", + "End": ".eastus2.5.azurestaticapps.net" + } + } +} \ No newline at end of file diff --git a/services/FilterLists.ServiceDefaults/Extensions.cs b/services/FilterLists.ServiceDefaults/Extensions.cs index a2d364643..9efbc0bfa 100644 --- a/services/FilterLists.ServiceDefaults/Extensions.cs +++ b/services/FilterLists.ServiceDefaults/Extensions.cs @@ -63,9 +63,9 @@ public static TBuilder ConfigureOpenTelemetry(this TBuilder builder) w .WithTracing(tracing => { tracing.AddSource(builder.Environment.ApplicationName) - .AddAspNetCoreInstrumentation(tracing => + .AddAspNetCoreInstrumentation(options => // Exclude health check requests from tracing - tracing.Filter = context => + options.Filter = context => !context.Request.Path.StartsWithSegments(HealthEndpointPath) && !context.Request.Path.StartsWithSegments(AlivenessEndpointPath) )