Skip to content

Commit 93f60cc

Browse files
committed
Merge 16826b0 into 3ef2b9e
1 parent 4340b31 commit 93f60cc

3 files changed

Lines changed: 178 additions & 0 deletions

File tree

src/Microsoft.OpenApi/Models/OpenApiSecurityScheme.cs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ private void SerializeInternal(IOpenApiWriter writer, OpenApiSpecVersion version
8080
Action<IOpenApiWriter, IOpenApiSerializable> callback)
8181
{
8282
Utils.CheckArgumentNull(writer);
83+
EnsureRequiredPropertiesAreSet(version);
8384

8485
writer.WriteStartObject();
8586

@@ -137,6 +138,7 @@ private void SerializeInternal(IOpenApiWriter writer, OpenApiSpecVersion version
137138
public virtual void SerializeAsV2(IOpenApiWriter writer)
138139
{
139140
Utils.CheckArgumentNull(writer);
141+
EnsureRequiredPropertiesAreSet(OpenApiSpecVersion.OpenApi2_0);
140142

141143
if (Type == SecuritySchemeType.Http && Scheme != OpenApiConstants.Basic)
142144
{
@@ -200,6 +202,25 @@ public virtual void SerializeAsV2(IOpenApiWriter writer)
200202
writer.WriteEndObject();
201203
}
202204

205+
private void EnsureRequiredPropertiesAreSet(OpenApiSpecVersion version)
206+
{
207+
switch (Type)
208+
{
209+
case null:
210+
throw new OpenApiException("The 'type' property is required for security schemes.");
211+
case SecuritySchemeType.ApiKey when string.IsNullOrEmpty(Name):
212+
throw new OpenApiException("The 'name' property is required for apiKey security schemes.");
213+
case SecuritySchemeType.ApiKey when In is null:
214+
throw new OpenApiException("The 'in' property is required for apiKey security schemes.");
215+
case SecuritySchemeType.Http when version >= OpenApiSpecVersion.OpenApi3_0 && string.IsNullOrEmpty(Scheme):
216+
throw new OpenApiException("The 'scheme' property is required for http security schemes.");
217+
case SecuritySchemeType.OAuth2 when Flows is null:
218+
throw new OpenApiException("The 'flows' property is required for oauth2 security schemes.");
219+
case SecuritySchemeType.OpenIdConnect when version >= OpenApiSpecVersion.OpenApi3_0 && OpenIdConnectUrl is null:
220+
throw new OpenApiException("The 'openIdConnectUrl' property is required for openIdConnect security schemes.");
221+
}
222+
}
223+
203224
/// <summary>
204225
/// Arbitrarily chooses one <see cref="OpenApiOAuthFlow"/> object from the <see cref="OpenApiOAuthFlows"/>
205226
/// to populate in V2 security scheme.

test/Microsoft.OpenApi.Tests/Mocks/OpenApiComponentsSerializationTests.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ public OpenApiComponentsSerializationTests()
2626
_components.Responses["200"] = _responseMock.Object;
2727
_components.Parameters["limit"] = _parameterMock.Object;
2828
_components.Headers["x-rate-limit"] = _headerMock.Object;
29+
_securitySchemeMock.Object.Type = SecuritySchemeType.Http;
30+
_securitySchemeMock.Object.Scheme = "bearer";
2931
_components.SecuritySchemes["api_key"] = _securitySchemeMock.Object;
3032
_components.Links["UserRepositories"] = _linkMock.Object;
3133
_components.Callbacks["onData"] = _callbackMock.Object;

test/Microsoft.OpenApi.Tests/Models/OpenApiSecuritySchemeTests.cs

Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using System.Collections.Generic;
55
using System.Globalization;
66
using System.IO;
7+
using System.Text.Json.Nodes;
78
using System.Threading.Tasks;
89
using VerifyXunit;
910
using Xunit;
@@ -159,6 +160,155 @@ public async Task SerializeApiKeySecuritySchemeAsV3YamlWorks()
159160
Assert.Equal(expected, actual);
160161
}
161162

163+
[Theory]
164+
[InlineData(OpenApiSpecVersion.OpenApi2_0)]
165+
[InlineData(OpenApiSpecVersion.OpenApi3_0)]
166+
[InlineData(OpenApiSpecVersion.OpenApi3_1)]
167+
[InlineData(OpenApiSpecVersion.OpenApi3_2)]
168+
public Task SerializeSecuritySchemeWithoutTypeThrows(OpenApiSpecVersion version)
169+
{
170+
// Arrange
171+
var securityScheme = new OpenApiSecurityScheme();
172+
173+
// Act & Assert
174+
return AssertRequiredPropertyThrowsAsync(
175+
securityScheme,
176+
version,
177+
"The 'type' property is required for security schemes.");
178+
}
179+
180+
[Theory]
181+
[InlineData(OpenApiSpecVersion.OpenApi2_0)]
182+
[InlineData(OpenApiSpecVersion.OpenApi3_0)]
183+
[InlineData(OpenApiSpecVersion.OpenApi3_1)]
184+
[InlineData(OpenApiSpecVersion.OpenApi3_2)]
185+
public Task SerializeApiKeySecuritySchemeWithoutNameThrows(OpenApiSpecVersion version)
186+
{
187+
// Arrange
188+
var securityScheme = new OpenApiSecurityScheme
189+
{
190+
Type = SecuritySchemeType.ApiKey,
191+
In = ParameterLocation.Query
192+
};
193+
194+
// Act & Assert
195+
return AssertRequiredPropertyThrowsAsync(
196+
securityScheme,
197+
version,
198+
"The 'name' property is required for apiKey security schemes.");
199+
}
200+
201+
[Theory]
202+
[InlineData(OpenApiSpecVersion.OpenApi2_0)]
203+
[InlineData(OpenApiSpecVersion.OpenApi3_0)]
204+
[InlineData(OpenApiSpecVersion.OpenApi3_1)]
205+
[InlineData(OpenApiSpecVersion.OpenApi3_2)]
206+
public Task SerializeApiKeySecuritySchemeWithoutInThrows(OpenApiSpecVersion version)
207+
{
208+
// Arrange
209+
var securityScheme = new OpenApiSecurityScheme
210+
{
211+
Name = "parameterName",
212+
Type = SecuritySchemeType.ApiKey
213+
};
214+
215+
// Act & Assert
216+
return AssertRequiredPropertyThrowsAsync(
217+
securityScheme,
218+
version,
219+
"The 'in' property is required for apiKey security schemes.");
220+
}
221+
222+
[Theory]
223+
[InlineData(OpenApiSpecVersion.OpenApi3_0)]
224+
[InlineData(OpenApiSpecVersion.OpenApi3_1)]
225+
[InlineData(OpenApiSpecVersion.OpenApi3_2)]
226+
public Task SerializeHttpSecuritySchemeWithoutSchemeThrows(OpenApiSpecVersion version)
227+
{
228+
// Arrange
229+
var securityScheme = new OpenApiSecurityScheme
230+
{
231+
Type = SecuritySchemeType.Http
232+
};
233+
234+
// Act & Assert
235+
return AssertRequiredPropertyThrowsAsync(
236+
securityScheme,
237+
version,
238+
"The 'scheme' property is required for http security schemes.");
239+
}
240+
241+
[Fact]
242+
public async Task SerializeHttpSecuritySchemeWithoutSchemeAsV2DoesNotThrow()
243+
{
244+
// Arrange
245+
var securityScheme = new OpenApiSecurityScheme
246+
{
247+
Type = SecuritySchemeType.Http
248+
};
249+
250+
// Act
251+
var actual = await securityScheme.SerializeAsJsonAsync(OpenApiSpecVersion.OpenApi2_0);
252+
253+
// Assert
254+
Assert.True(JsonNode.DeepEquals(JsonNode.Parse("{}"), JsonNode.Parse(actual)));
255+
}
256+
257+
[Theory]
258+
[InlineData(OpenApiSpecVersion.OpenApi2_0)]
259+
[InlineData(OpenApiSpecVersion.OpenApi3_0)]
260+
[InlineData(OpenApiSpecVersion.OpenApi3_1)]
261+
[InlineData(OpenApiSpecVersion.OpenApi3_2)]
262+
public Task SerializeOAuth2SecuritySchemeWithoutFlowsThrows(OpenApiSpecVersion version)
263+
{
264+
// Arrange
265+
var securityScheme = new OpenApiSecurityScheme
266+
{
267+
Type = SecuritySchemeType.OAuth2
268+
};
269+
270+
// Act & Assert
271+
return AssertRequiredPropertyThrowsAsync(
272+
securityScheme,
273+
version,
274+
"The 'flows' property is required for oauth2 security schemes.");
275+
}
276+
277+
[Theory]
278+
[InlineData(OpenApiSpecVersion.OpenApi3_0)]
279+
[InlineData(OpenApiSpecVersion.OpenApi3_1)]
280+
[InlineData(OpenApiSpecVersion.OpenApi3_2)]
281+
public Task SerializeOpenIdConnectSecuritySchemeWithoutOpenIdConnectUrlThrows(OpenApiSpecVersion version)
282+
{
283+
// Arrange
284+
var securityScheme = new OpenApiSecurityScheme
285+
{
286+
Type = SecuritySchemeType.OpenIdConnect
287+
};
288+
289+
// Act & Assert
290+
return AssertRequiredPropertyThrowsAsync(
291+
securityScheme,
292+
version,
293+
"The 'openIdConnectUrl' property is required for openIdConnect security schemes.");
294+
}
295+
296+
[Fact]
297+
public async Task SerializeOpenIdConnectSecuritySchemeWithoutOpenIdConnectUrlAsV2DoesNotThrow()
298+
{
299+
// Arrange
300+
var securityScheme = new OpenApiSecurityScheme
301+
{
302+
Type = SecuritySchemeType.OpenIdConnect
303+
};
304+
305+
// Act
306+
var actual = await securityScheme.SerializeAsJsonAsync(OpenApiSpecVersion.OpenApi2_0);
307+
308+
// Assert
309+
Assert.True(JsonNode.DeepEquals(JsonNode.Parse("{}"), JsonNode.Parse(actual)));
310+
}
311+
162312
[Fact]
163313
public async Task SerializeHttpBasicSecuritySchemeAsV3JsonWorks()
164314
{
@@ -348,5 +498,10 @@ public async Task SerializeReferencedSecuritySchemeAsV3JsonWithoutReferenceWorks
348498
// Assert
349499
await Verifier.Verify(outputStringWriter).UseParameters(produceTerseOutput);
350500
}
501+
private static async Task AssertRequiredPropertyThrowsAsync(OpenApiSecurityScheme securityScheme, OpenApiSpecVersion version, string message)
502+
{
503+
var exception = await Assert.ThrowsAsync<OpenApiException>(() => securityScheme.SerializeAsJsonAsync(version));
504+
Assert.Contains(message, exception.Message);
505+
}
351506
}
352507
}

0 commit comments

Comments
 (0)