Skip to content

Commit 5108fdd

Browse files
committed
Refactor tests for cloudfoundryapplication endpoint
- Order the returned list of endpoints by ID - Critical fix: block /cloudfoundryapplication and log warning when security middleware not added - Critical fix: don't turn off all security when the /cloudfoundryapplication hypermedia endpoint is turned off
1 parent 0b30015 commit 5108fdd

28 files changed

+714
-855
lines changed

src/Management/src/Endpoint/ActuatorMapper.cs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,16 +35,23 @@ protected ActuatorMapper(IEnumerable<IEndpointMiddleware> middlewares, IOptionsM
3535

3636
protected IEnumerable<(string RoutePattern, IEndpointMiddleware Middleware)> GetEndpointsToMap()
3737
{
38-
string? basePath = _managementOptionsMonitor.CurrentValue.Path;
38+
ManagementOptions managementOptions = _managementOptionsMonitor.CurrentValue;
3939

4040
foreach (IEndpointMiddleware middleware in _middlewares.Where(middleware => middleware is not CloudFoundryEndpointMiddleware))
4141
{
42-
string routePattern = middleware.EndpointOptions.GetPathMatchPattern(basePath);
42+
string routePattern = middleware.EndpointOptions.GetPathMatchPattern(managementOptions.Path);
4343
yield return (routePattern, middleware);
4444
}
4545

4646
if (Platform.IsCloudFoundry)
4747
{
48+
if (managementOptions is { IsCloudFoundryEnabled: true, HasCloudFoundrySecurity: false })
49+
{
50+
_logger.LogWarning(
51+
$"Actuators at the {ConfigureManagementOptions.DefaultCloudFoundryPath} endpoint are disabled because the Cloud Foundry security middleware is not active. " +
52+
$"Call {nameof(EndpointApplicationBuilderExtensions.UseCloudFoundrySecurity)}() from your custom middleware pipeline to enable them.");
53+
}
54+
4855
foreach (IEndpointMiddleware middleware in _middlewares.Where(middleware => middleware is not HypermediaEndpointMiddleware))
4956
{
5057
string routePattern = middleware.EndpointOptions.GetPathMatchPattern(ConfigureManagementOptions.DefaultCloudFoundryPath);

src/Management/src/Endpoint/Actuators/CloudFoundry/CloudFoundrySecurityMiddleware.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -54,12 +54,12 @@ public async Task InvokeAsync(HttpContext context)
5454
ArgumentNullException.ThrowIfNull(context);
5555

5656
_logger.LogDebug("InvokeAsync({RequestPath})", context.Request.Path.Value);
57-
CloudFoundryEndpointOptions endpointOptions = _endpointOptionsMonitor.CurrentValue;
5857
ManagementOptions managementOptions = _managementOptionsMonitor.CurrentValue;
5958

60-
if (Platform.IsCloudFoundry && endpointOptions.IsEnabled(managementOptions) && managementOptions.IsCloudFoundryEnabled &&
61-
PermissionsProvider.IsCloudFoundryRequest(context.Request.Path))
59+
if (Platform.IsCloudFoundry && managementOptions.IsCloudFoundryEnabled && PermissionsProvider.IsCloudFoundryRequest(context.Request.Path))
6260
{
61+
CloudFoundryEndpointOptions endpointOptions = _endpointOptionsMonitor.CurrentValue;
62+
6363
if (string.IsNullOrEmpty(endpointOptions.ApplicationId))
6464
{
6565
_logger.LogError(

src/Management/src/Endpoint/Actuators/CloudFoundry/EndpointApplicationBuilderExtensions.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@ public static IApplicationBuilder UseCloudFoundrySecurity(this IApplicationBuild
3030

3131
builder.UseMiddleware<CloudFoundrySecurityMiddleware>();
3232

33+
var marker = builder.ApplicationServices.GetRequiredService<HasCloudFoundrySecurityMiddlewareMarker>();
34+
marker.Value = true;
35+
3336
return builder;
3437
}
3538
}

src/Management/src/Endpoint/Actuators/Hypermedia/HypermediaService.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ public Links Invoke(Uri baseUrl)
6868
bool skipExposureCheck = PermissionsProvider.IsCloudFoundryRequest(baseUrl.PathAndQuery);
6969
string? basePath = managementOptions.GetBasePath(baseUrl.AbsolutePath);
7070

71-
foreach (EndpointOptions endpointOptions in _endpointOptionsMonitorProviders.Select(provider => provider.Get()))
71+
foreach (EndpointOptions endpointOptions in _endpointOptionsMonitorProviders.Select(provider => provider.Get()).OrderBy(options => options.Id))
7272
{
7373
if (endpointOptions.Id == null || !endpointOptions.IsEnabled(managementOptions))
7474
{

src/Management/src/Endpoint/Configuration/ConfigureManagementOptions.cs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,17 @@ internal sealed class ConfigureManagementOptions : IConfigureOptionsWithKey<Mana
1616
internal const string DefaultCloudFoundryPath = "/cloudfoundryapplication";
1717

1818
private readonly IConfiguration _configuration;
19+
private readonly HasCloudFoundrySecurityMiddlewareMarker _hasCloudFoundrySecurityMiddlewareMarker;
1920

2021
public string ConfigurationKey => "Management:Endpoints";
2122

22-
public ConfigureManagementOptions(IConfiguration configuration)
23+
public ConfigureManagementOptions(IConfiguration configuration, HasCloudFoundrySecurityMiddlewareMarker hasCloudFoundrySecurityMiddlewareMarker)
2324
{
2425
ArgumentNullException.ThrowIfNull(configuration);
26+
ArgumentNullException.ThrowIfNull(hasCloudFoundrySecurityMiddlewareMarker);
2527

2628
_configuration = configuration;
29+
_hasCloudFoundrySecurityMiddlewareMarker = hasCloudFoundrySecurityMiddlewareMarker;
2730
}
2831

2932
public void Configure(ManagementOptions options)
@@ -32,13 +35,13 @@ public void Configure(ManagementOptions options)
3235

3336
_configuration.GetSection(ConfigurationKey).Bind(options);
3437

35-
options.IsCloudFoundryEnabled = true;
36-
3738
if (bool.TryParse(_configuration[CloudFoundryEnabledConfigurationKey], out bool isEnabled))
3839
{
3940
options.IsCloudFoundryEnabled = isEnabled;
4041
}
4142

43+
options.HasCloudFoundrySecurity = _hasCloudFoundrySecurityMiddlewareMarker.Value;
44+
4245
ConfigureSerializerOptions(options);
4346

4447
options.Path ??= DefaultPath;

src/Management/src/Endpoint/Configuration/ManagementOptions.cs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,16 @@ public sealed class ManagementOptions
1313
{
1414
internal const string UseStatusCodeFromResponseHeaderName = "X-Use-Status-Code-From-Response";
1515

16-
internal bool IsCloudFoundryEnabled { get; set; }
16+
/// <summary>
17+
/// Gets or sets a value indicating whether ANY endpoint starting with /cloudfoundryapplication is accessible. Not to be confused with the accessibility
18+
/// of the /cloudfoundryapplication hypermedia endpoint.
19+
/// </summary>
20+
internal bool IsCloudFoundryEnabled { get; set; } = true;
21+
22+
/// <summary>
23+
/// Gets or sets a value indicating whether the Cloud Foundry security middleware has been added to the pipeline.
24+
/// </summary>
25+
internal bool HasCloudFoundrySecurity { get; set; }
1726

1827
/// <summary>
1928
/// Gets which management endpoints are included and/or excluded.

src/Management/src/Endpoint/CoreActuatorServiceCollectionExtensions.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,8 @@ private static void AddCommonActuatorServices(IServiceCollection services, bool
9595
}
9696

9797
services.TryAddEnumerable(ServiceDescriptor.Singleton<IStartupFilter, ManagementPortStartupFilter>());
98+
99+
services.TryAddSingleton<HasCloudFoundrySecurityMiddlewareMarker>();
98100
}
99101

100102
internal static void ConfigureEndpointOptions<TOptions, TConfigureOptions>(this IServiceCollection services)

src/Management/src/Endpoint/EndpointOptionsExtensions.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,9 +48,9 @@ public static bool CanInvoke(this EndpointOptions endpointOptions, PathString re
4848

4949
bool isEnabled = IsEnabled(endpointOptions, managementOptions);
5050
bool isExposed = IsExposed(endpointOptions, managementOptions);
51-
5251
bool isAtCloudFoundryPath = requestPath.StartsWithSegments(ConfigureManagementOptions.DefaultCloudFoundryPath);
53-
return isAtCloudFoundryPath ? managementOptions.IsCloudFoundryEnabled && isEnabled : isEnabled && isExposed;
52+
53+
return isAtCloudFoundryPath ? isEnabled && managementOptions is { IsCloudFoundryEnabled: true, HasCloudFoundrySecurity: true } : isEnabled && isExposed;
5454
}
5555

5656
public static string GetPathMatchPattern(this EndpointOptions endpointOptions, string? basePath)
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the Apache 2.0 License.
3+
// See the LICENSE file in the project root for more information.
4+
5+
namespace Steeltoe.Management.Endpoint;
6+
7+
/// <summary>
8+
/// Used to detect if the Cloud Foundry security middleware has been added to the pipeline.
9+
/// </summary>
10+
internal sealed class HasCloudFoundrySecurityMiddlewareMarker
11+
{
12+
public bool Value { get; set; }
13+
}

src/Management/src/Endpoint/SpringBootAdminClient/ServiceCollectionExtensions.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ public static IServiceCollection AddSpringBootAdminClient(this IServiceCollectio
3232
services.ConfigureOptionsWithChangeTokenSource<ManagementOptions, ConfigureManagementOptions>();
3333
services.ConfigureOptionsWithChangeTokenSource<SpringBootAdminClientOptions, ConfigureSpringBootAdminClientOptions>();
3434
services.ConfigureEndpointOptions<HealthEndpointOptions, ConfigureHealthEndpointOptions>();
35+
services.TryAddSingleton<HasCloudFoundrySecurityMiddlewareMarker>();
3536

3637
ConfigureHttpClient(services);
3738

0 commit comments

Comments
 (0)