Skip to content

Commit bcf002b

Browse files
Merge pull request #195 from OpenReferralUK/feat--enhance-authentication-validation-logic-for-OpenAPI-specifications
feat: enhance authentication validation logic for OpenAPI specifications
2 parents 9cf5fd7 + 94ce6ae commit bcf002b

File tree

1 file changed

+55
-8
lines changed

1 file changed

+55
-8
lines changed

OpenReferralApi.Core/Services/OpenApiValidationService.cs

Lines changed: 55 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1154,17 +1154,64 @@ private List<ValidationError> ValidateJsonAgainstSchema(string jsonData, JSchema
11541154
return null;
11551155
}
11561156

1157-
// Basic sanity checks to ensure the authentication configuration is usable.
1157+
// Perform stricter validation on the authentication configuration.
11581158
// If it fails validation, treat it as if no authentication was provided.
1159-
if (string.IsNullOrWhiteSpace(auth.ApiKey)
1160-
&& string.IsNullOrWhiteSpace(auth.BearerToken)
1161-
&& auth.BasicAuth == null
1162-
&& (auth.CustomHeaders == null || auth.CustomHeaders.Count == 0))
1159+
var hasApiKey = !string.IsNullOrWhiteSpace(auth.ApiKey);
1160+
var hasBearer = !string.IsNullOrWhiteSpace(auth.BearerToken);
1161+
var hasBasic = auth.BasicAuth != null
1162+
&& !string.IsNullOrWhiteSpace(auth.BasicAuth.Username)
1163+
&& !string.IsNullOrWhiteSpace(auth.BasicAuth.Password);
1164+
var hasCustomHeaders = auth.CustomHeaders != null && auth.CustomHeaders.Count > 0;
1165+
1166+
var mechanismsCount = 0;
1167+
if (hasApiKey) mechanismsCount++;
1168+
if (hasBearer) mechanismsCount++;
1169+
if (hasBasic) mechanismsCount++;
1170+
if (hasCustomHeaders) mechanismsCount++;
1171+
1172+
// Require at least one and at most one primary authentication mechanism.
1173+
if (mechanismsCount != 1)
11631174
{
11641175
return null;
11651176
}
11661177

1167-
return auth;
1178+
// Return a sanitised copy so that downstream code does not operate on the original user object.
1179+
var validated = new DataSourceAuthentication();
1180+
1181+
if (hasApiKey)
1182+
{
1183+
validated.ApiKey = auth.ApiKey!.Trim();
1184+
}
1185+
else if (hasBearer)
1186+
{
1187+
validated.BearerToken = auth.BearerToken!.Trim();
1188+
}
1189+
else if (hasBasic)
1190+
{
1191+
validated.BasicAuth = new BasicAuthentication
1192+
{
1193+
Username = auth.BasicAuth!.Username.Trim(),
1194+
Password = auth.BasicAuth.Password
1195+
};
1196+
}
1197+
else if (hasCustomHeaders)
1198+
{
1199+
// Copy only non-empty header names and values.
1200+
validated.CustomHeaders = new Dictionary<string, string>();
1201+
foreach (var kvp in auth.CustomHeaders!)
1202+
{
1203+
if (!string.IsNullOrWhiteSpace(kvp.Key) && !string.IsNullOrWhiteSpace(kvp.Value))
1204+
{
1205+
validated.CustomHeaders[kvp.Key.Trim()] = kvp.Value;
1206+
}
1207+
}
1208+
if (validated.CustomHeaders.Count == 0)
1209+
{
1210+
return null;
1211+
}
1212+
}
1213+
1214+
return validated;
11681215
}
11691216

11701217
private async Task<JObject> FetchOpenApiSpecFromUrlAsync(string specUrl, DataSourceAuthentication? auth, CancellationToken cancellationToken, bool resolveReferences = true)
@@ -1181,7 +1228,7 @@ private async Task<JObject> FetchOpenApiSpecFromUrlAsync(string specUrl, DataSou
11811228

11821229
using var request = new HttpRequestMessage(HttpMethod.Get, specUrl);
11831230

1184-
// Apply authentication only if it passes basic validation
1231+
// Apply authentication only if it passes strict validation
11851232
var validatedAuth = ValidateAuthentication(auth);
11861233
if (validatedAuth != null)
11871234
{
@@ -1198,7 +1245,7 @@ private async Task<JObject> FetchOpenApiSpecFromUrlAsync(string specUrl, DataSou
11981245
// or when endpoints won't be tested
11991246
if (resolveReferences)
12001247
{
1201-
var resolvedContent = await _schemaResolverService.ResolveAsync(content, specUrl, auth);
1248+
var resolvedContent = await _schemaResolverService.ResolveAsync(content, specUrl, validatedAuth);
12021249
return JObject.Parse(resolvedContent);
12031250
}
12041251

0 commit comments

Comments
 (0)