Skip to content

Commit b96f620

Browse files
committed
Fix RejectsBadSubject, test failure at startup on empty Subject
1 parent 02e8349 commit b96f620

File tree

2 files changed

+62
-20
lines changed

2 files changed

+62
-20
lines changed

src/Tests/Microsoft.Diagnostics.Monitoring.Tool.FunctionalTests/AuthenticationTests.cs

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -472,9 +472,6 @@ public async Task AllowsConfiguredIssuer()
472472
[Theory]
473473
// Guids that don't match should get rejected
474474
[InlineData("980d2b17-71e1-4313-a084-c077e962680c", "10253b7a-454d-41bb-a3f5-5f2e6b26ed93", HttpStatusCode.Forbidden)]
475-
// Empty string isn't valid even when signed and configured correctly
476-
[InlineData("", "", HttpStatusCode.Unauthorized)]
477-
[InlineData("10253b7a-454d-41bb-a3f5-5f2e6b26ed93", "", HttpStatusCode.Unauthorized)]
478475
[InlineData("", "10253b7a-454d-41bb-a3f5-5f2e6b26ed93", HttpStatusCode.Forbidden)]
479476
public async Task RejectsBadSubject(string jwtSubject, string configSubject, HttpStatusCode expectedError)
480477
{
@@ -501,6 +498,25 @@ public async Task RejectsBadSubject(string jwtSubject, string configSubject, Htt
501498
Assert.Equal(expectedError, statusCodeException.StatusCode);
502499
}
503500

501+
/// <summary>
502+
/// This tests that an empty configured subject fails on startup.
503+
/// </summary>
504+
[Fact]
505+
public async Task DoesNotStart_With_EmptySubject()
506+
{
507+
var configSubject = "";
508+
await using MonitorCollectRunner toolRunner = new(_outputHelper);
509+
toolRunner.DisableMetricsViaCommandLine = true;
510+
511+
_outputHelper.WriteLine("Generating API key.");
512+
513+
JwtPayload newPayload = GetJwtPayload(AuthConstants.ApiKeyJwtAudience, configSubject, AuthConstants.ApiKeyJwtInternalIssuer, AuthConstants.ApiKeyJwtDefaultExpiration);
514+
515+
toolRunner.ConfigurationFromEnvironment.UseApiKey(SecurityAlgorithms.EcdsaSha384, configSubject, newPayload, out string token);
516+
517+
await Assert.ThrowsAsync<InvalidOperationException>(toolRunner.StartAsync);
518+
}
519+
504520
[Fact]
505521
public async Task RejectsMissingExpiration()
506522
{

src/Tools/dotnet-monitor/ValidatableInfoResolver.cs

Lines changed: 43 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
#nullable enable annotations
1+
#nullable enable annotations
22
//------------------------------------------------------------------------------
33
// <auto-generated>
44
// This code was generated by a tool.
@@ -129,7 +129,7 @@ private ValidatableTypeInfo CreateMetricProvider()
129129
containingType: typeof(global::Microsoft.Diagnostics.Monitoring.WebApi.MetricProvider),
130130
propertyType: typeof(string),
131131
name: "ProviderName",
132-
displayName: "Microsoft.Diagnostics.Monitoring.WebApi.OptionsDisplayStrings"
132+
displayName: "ProviderName"
133133
),
134134
]
135135
);
@@ -143,13 +143,13 @@ private ValidatableTypeInfo CreateMetricsOptions()
143143
containingType: typeof(global::Microsoft.Diagnostics.Monitoring.WebApi.MetricsOptions),
144144
propertyType: typeof(int?),
145145
name: "MetricCount",
146-
displayName: "Microsoft.Diagnostics.Monitoring.WebApi.OptionsDisplayStrings"
146+
displayName: "MetricCount"
147147
),
148148
new GeneratedValidatablePropertyInfo(
149149
containingType: typeof(global::Microsoft.Diagnostics.Monitoring.WebApi.MetricsOptions),
150150
propertyType: typeof(global::System.Collections.Generic.List<global::Microsoft.Diagnostics.Monitoring.WebApi.MetricProvider>),
151151
name: "Providers",
152-
displayName: "Microsoft.Diagnostics.Monitoring.WebApi.OptionsDisplayStrings"
152+
displayName: "Providers"
153153
),
154154
]
155155
);
@@ -163,13 +163,13 @@ private ValidatableTypeInfo CreateMonitorApiKeyOptions()
163163
containingType: typeof(global::Microsoft.Diagnostics.Tools.Monitor.MonitorApiKeyOptions),
164164
propertyType: typeof(string),
165165
name: "Subject",
166-
displayName: "Microsoft.Diagnostics.Monitoring.WebApi.OptionsDisplayStrings"
166+
displayName: "Subject"
167167
),
168168
new GeneratedValidatablePropertyInfo(
169169
containingType: typeof(global::Microsoft.Diagnostics.Tools.Monitor.MonitorApiKeyOptions),
170170
propertyType: typeof(string),
171171
name: "PublicKey",
172-
displayName: "Microsoft.Diagnostics.Monitoring.WebApi.OptionsDisplayStrings"
172+
displayName: "PublicKey"
173173
),
174174
]
175175
);
@@ -183,25 +183,25 @@ private ValidatableTypeInfo CreateAzureAdOptions()
183183
containingType: typeof(global::Microsoft.Diagnostics.Tools.Monitor.AzureAdOptions),
184184
propertyType: typeof(string),
185185
name: "TenantId",
186-
displayName: "Microsoft.Diagnostics.Monitoring.WebApi.OptionsDisplayStrings"
186+
displayName: "TenantId"
187187
),
188188
new GeneratedValidatablePropertyInfo(
189189
containingType: typeof(global::Microsoft.Diagnostics.Tools.Monitor.AzureAdOptions),
190190
propertyType: typeof(string),
191191
name: "ClientId",
192-
displayName: "Microsoft.Diagnostics.Monitoring.WebApi.OptionsDisplayStrings"
192+
displayName: "ClientId"
193193
),
194194
new GeneratedValidatablePropertyInfo(
195195
containingType: typeof(global::Microsoft.Diagnostics.Tools.Monitor.AzureAdOptions),
196196
propertyType: typeof(global::System.Uri),
197197
name: "AppIdUri",
198-
displayName: "Microsoft.Diagnostics.Monitoring.WebApi.OptionsDisplayStrings"
198+
displayName: "AppIdUri"
199199
),
200200
new GeneratedValidatablePropertyInfo(
201201
containingType: typeof(global::Microsoft.Diagnostics.Tools.Monitor.AzureAdOptions),
202202
propertyType: typeof(string),
203203
name: "RequiredRole",
204-
displayName: "Microsoft.Diagnostics.Monitoring.WebApi.OptionsDisplayStrings"
204+
displayName: "RequiredRole"
205205
),
206206
]
207207
);
@@ -215,13 +215,13 @@ private ValidatableTypeInfo CreateAuthenticationOptions()
215215
containingType: typeof(global::Microsoft.Diagnostics.Tools.Monitor.AuthenticationOptions),
216216
propertyType: typeof(global::Microsoft.Diagnostics.Tools.Monitor.MonitorApiKeyOptions),
217217
name: "MonitorApiKey",
218-
displayName: "Microsoft.Diagnostics.Monitoring.WebApi.OptionsDisplayStrings"
218+
displayName: "MonitorApiKey"
219219
),
220220
new GeneratedValidatablePropertyInfo(
221221
containingType: typeof(global::Microsoft.Diagnostics.Tools.Monitor.AuthenticationOptions),
222222
propertyType: typeof(global::Microsoft.Diagnostics.Tools.Monitor.AzureAdOptions),
223223
name: "AzureAd",
224-
displayName: "Microsoft.Diagnostics.Monitoring.WebApi.OptionsDisplayStrings"
224+
displayName: "AzureAd"
225225
),
226226
]
227227
);
@@ -260,8 +260,8 @@ static class MyExtensions
260260
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.Http.ValidationsGenerator, Version=10.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60", "10.0.0.0")]
261261
file static class GeneratedServiceCollectionExtensions
262262
{
263-
// [global::System.Runtime.CompilerServices.InterceptsLocationAttribute(1, "M2kOaR09/X9mJ22GvhUC4+4FAABTdGFydHVwLmNz")]
264-
public static global::Microsoft.Extensions.DependencyInjection.IServiceCollection AddValidation(this global::Microsoft.Extensions.DependencyInjection.IServiceCollection services, global::System.Action<ValidationOptions>? configureOptions = null)
263+
// [global::System.Runtime.CompilerServices.InterceptsLocationAttribute(1, "tgKaeQ8w+KgICFY1OfbdmEk4AABWYWxpZGF0YWJsZUluZm9SZXNvbHZlci5jcw==")]
264+
public static global::Microsoft.Extensions.DependencyInjection.IServiceCollection AddValidation(this global::Microsoft.Extensions.DependencyInjection.IServiceCollection services, global::System.Action<global::Microsoft.AspNetCore.Http.Validation.ValidationOptions>? configureOptions = null)
265265
{
266266
// Use non-extension method to avoid infinite recursion.
267267
return global::Microsoft.Extensions.DependencyInjection.ValidationServiceCollectionExtensions.AddValidation(services, options =>
@@ -288,13 +288,39 @@ private sealed record CacheKey([property: global::System.Diagnostics.CodeAnalysi
288288
var key = new CacheKey(containingType, propertyName);
289289
return _cache.GetOrAdd(key, static k =>
290290
{
291+
var results = new global::System.Collections.Generic.List<global::System.ComponentModel.DataAnnotations.ValidationAttribute>();
292+
293+
// Get attributes from the property
291294
var property = k.ContainingType.GetProperty(k.PropertyName);
292-
if (property == null)
295+
if (property != null)
293296
{
294-
return [];
297+
var propertyAttributes = global::System.Reflection.CustomAttributeExtensions
298+
.GetCustomAttributes<global::System.ComponentModel.DataAnnotations.ValidationAttribute>(property, inherit: true);
299+
300+
results.AddRange(propertyAttributes);
301+
}
302+
303+
// Check constructors for parameters that match the property name
304+
// to handle record scenarios
305+
foreach (var constructor in k.ContainingType.GetConstructors())
306+
{
307+
// Look for parameter with matching name (case insensitive)
308+
var parameter = global::System.Linq.Enumerable.FirstOrDefault(
309+
constructor.GetParameters(),
310+
p => string.Equals(p.Name, k.PropertyName, global::System.StringComparison.OrdinalIgnoreCase));
311+
312+
if (parameter != null)
313+
{
314+
var paramAttributes = global::System.Reflection.CustomAttributeExtensions
315+
.GetCustomAttributes<global::System.ComponentModel.DataAnnotations.ValidationAttribute>(parameter, inherit: true);
316+
317+
results.AddRange(paramAttributes);
318+
319+
break;
320+
}
295321
}
296322

297-
return [.. global::System.Reflection.CustomAttributeExtensions.GetCustomAttributes<global::System.ComponentModel.DataAnnotations.ValidationAttribute>(property, inherit: true)];
323+
return results.ToArray();
298324
});
299325
}
300326
}

0 commit comments

Comments
 (0)