Skip to content

Commit 1bd2624

Browse files
committed
fix: draft security scheme reference proxy design pattern
Signed-off-by: Vincent Biret <[email protected]>
1 parent 21253f6 commit 1bd2624

26 files changed

+174
-241
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using Microsoft.OpenApi.Interfaces;
4+
5+
namespace Microsoft.OpenApi.Models.Interfaces;
6+
7+
/// <summary>
8+
/// Defines the base properties for the security scheme object.
9+
/// This interface is provided for type assertions but should not be implemented by package consumers beyond automatic mocking.
10+
/// </summary>
11+
public interface IOpenApiSecurityScheme : IOpenApiDescribedElement, IOpenApiSerializable, IOpenApiReadOnlyExtensible
12+
{
13+
/// <summary>
14+
/// REQUIRED. The type of the security scheme. Valid values are "apiKey", "http", "oauth2", "openIdConnect".
15+
/// </summary>
16+
public SecuritySchemeType? Type { get; }
17+
18+
/// <summary>
19+
/// REQUIRED. The name of the header, query or cookie parameter to be used.
20+
/// </summary>
21+
public string Name { get; }
22+
23+
/// <summary>
24+
/// REQUIRED. The location of the API key. Valid values are "query", "header" or "cookie".
25+
/// </summary>
26+
public ParameterLocation? In { get; }
27+
28+
/// <summary>
29+
/// REQUIRED. The name of the HTTP Authorization scheme to be used
30+
/// in the Authorization header as defined in RFC7235.
31+
/// </summary>
32+
public string Scheme { get; }
33+
34+
/// <summary>
35+
/// A hint to the client to identify how the bearer token is formatted.
36+
/// Bearer tokens are usually generated by an authorization server,
37+
/// so this information is primarily for documentation purposes.
38+
/// </summary>
39+
public string BearerFormat { get; }
40+
41+
/// <summary>
42+
/// REQUIRED. An object containing configuration information for the flow types supported.
43+
/// </summary>
44+
public OpenApiOAuthFlows Flows { get; }
45+
46+
/// <summary>
47+
/// REQUIRED. OpenId Connect URL to discover OAuth2 configuration values.
48+
/// </summary>
49+
public Uri OpenIdConnectUrl { get; }
50+
}

src/Microsoft.OpenApi/Models/OpenApiComponents.cs

+4-4
Original file line numberDiff line numberDiff line change
@@ -50,10 +50,10 @@ public class OpenApiComponents : IOpenApiSerializable, IOpenApiExtensible
5050
public IDictionary<string, IOpenApiHeader>? Headers { get; set; } = new Dictionary<string, IOpenApiHeader>();
5151

5252
/// <summary>
53-
/// An object to hold reusable <see cref="OpenApiSecurityScheme"/> Objects.
53+
/// An object to hold reusable <see cref="IOpenApiSecurityScheme"/> Objects.
5454
/// </summary>
55-
public IDictionary<string, OpenApiSecurityScheme>? SecuritySchemes { get; set; } =
56-
new Dictionary<string, OpenApiSecurityScheme>();
55+
public IDictionary<string, IOpenApiSecurityScheme>? SecuritySchemes { get; set; } =
56+
new Dictionary<string, IOpenApiSecurityScheme>();
5757

5858
/// <summary>
5959
/// An object to hold reusable <see cref="IOpenApiLink"/> Objects.
@@ -91,7 +91,7 @@ public OpenApiComponents(OpenApiComponents? components)
9191
Examples = components?.Examples != null ? new Dictionary<string, IOpenApiExample>(components.Examples) : null;
9292
RequestBodies = components?.RequestBodies != null ? new Dictionary<string, IOpenApiRequestBody>(components.RequestBodies) : null;
9393
Headers = components?.Headers != null ? new Dictionary<string, IOpenApiHeader>(components.Headers) : null;
94-
SecuritySchemes = components?.SecuritySchemes != null ? new Dictionary<string, OpenApiSecurityScheme>(components.SecuritySchemes) : null;
94+
SecuritySchemes = components?.SecuritySchemes != null ? new Dictionary<string, IOpenApiSecurityScheme>(components.SecuritySchemes) : null;
9595
Links = components?.Links != null ? new Dictionary<string, IOpenApiLink>(components.Links) : null;
9696
Callbacks = components?.Callbacks != null ? new Dictionary<string, IOpenApiCallback>(components.Callbacks) : null;
9797
PathItems = components?.PathItems != null ? new Dictionary<string, IOpenApiPathItem>(components.PathItems) : null;

src/Microsoft.OpenApi/Models/OpenApiDocument.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -624,7 +624,7 @@ public bool AddComponent<T>(string id, T componentToRegister)
624624
Components.Headers.Add(id, openApiHeader);
625625
break;
626626
case OpenApiSecurityScheme openApiSecurityScheme:
627-
Components.SecuritySchemes ??= new Dictionary<string, OpenApiSecurityScheme>();
627+
Components.SecuritySchemes ??= new Dictionary<string, IOpenApiSecurityScheme>();
628628
Components.SecuritySchemes.Add(id, openApiSecurityScheme);
629629
break;
630630
default:

src/Microsoft.OpenApi/Models/OpenApiSecurityRequirement.cs

+19-14
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
using System;
55
using System.Collections.Generic;
66
using Microsoft.OpenApi.Interfaces;
7+
using Microsoft.OpenApi.Models.Interfaces;
8+
using Microsoft.OpenApi.Models.References;
79
using Microsoft.OpenApi.Writers;
810

911
namespace Microsoft.OpenApi.Models
@@ -16,7 +18,7 @@ namespace Microsoft.OpenApi.Models
1618
/// then the value is a list of scope names required for the execution.
1719
/// For other security scheme types, the array MUST be empty.
1820
/// </summary>
19-
public class OpenApiSecurityRequirement : Dictionary<OpenApiSecurityScheme, IList<string>>,
21+
public class OpenApiSecurityRequirement : Dictionary<IOpenApiSecurityScheme, IList<string>>,
2022
IOpenApiSerializable
2123
{
2224
/// <summary>
@@ -59,15 +61,15 @@ private void SerializeInternal(IOpenApiWriter writer, Action<IOpenApiWriter, IOp
5961
var securityScheme = securitySchemeAndScopesValuePair.Key;
6062
var scopes = securitySchemeAndScopesValuePair.Value;
6163

62-
if (securityScheme.Reference == null)
64+
if (securityScheme is not OpenApiSecuritySchemeReference schemeReference || schemeReference.Reference is null)
6365
{
6466
// Reaching this point means the reference to a specific OpenApiSecurityScheme fails.
6567
// We are not able to serialize this SecurityScheme/Scopes key value pair since we do not know what
6668
// string to output.
6769
continue;
6870
}
6971

70-
writer.WritePropertyName(securityScheme.Reference.ReferenceV3);
72+
writer.WritePropertyName(schemeReference.Reference.ReferenceV3);
7173

7274
writer.WriteStartArray();
7375

@@ -96,7 +98,7 @@ public void SerializeAsV2(IOpenApiWriter writer)
9698
var securityScheme = securitySchemeAndScopesValuePair.Key;
9799
var scopes = securitySchemeAndScopesValuePair.Value;
98100

99-
if (securityScheme.Reference == null)
101+
if (securityScheme is not OpenApiSecuritySchemeReference schemeReference || schemeReference.Reference is null)
100102
{
101103
// Reaching this point means the reference to a specific OpenApiSecurityScheme fails.
102104
// We are not able to serialize this SecurityScheme/Scopes key value pair since we do not know what
@@ -123,12 +125,12 @@ public void SerializeAsV2(IOpenApiWriter writer)
123125
/// Comparer for OpenApiSecurityScheme that only considers the Id in the Reference
124126
/// (i.e. the string that will actually be displayed in the written document)
125127
/// </summary>
126-
private class OpenApiSecuritySchemeReferenceEqualityComparer : IEqualityComparer<OpenApiSecurityScheme>
128+
private sealed class OpenApiSecuritySchemeReferenceEqualityComparer : IEqualityComparer<IOpenApiSecurityScheme>
127129
{
128130
/// <summary>
129131
/// Determines whether the specified objects are equal.
130132
/// </summary>
131-
public bool Equals(OpenApiSecurityScheme x, OpenApiSecurityScheme y)
133+
public bool Equals(IOpenApiSecurityScheme x, IOpenApiSecurityScheme y)
132134
{
133135
if (x == null && y == null)
134136
{
@@ -140,20 +142,23 @@ public bool Equals(OpenApiSecurityScheme x, OpenApiSecurityScheme y)
140142
return false;
141143
}
142144

143-
if (x.Reference == null || y.Reference == null)
144-
{
145-
return false;
146-
}
147-
148-
return x.Reference.Id == y.Reference.Id;
145+
return GetHashCode(x) == GetHashCode(y);
149146
}
150147

151148
/// <summary>
152149
/// Returns a hash code for the specified object.
153150
/// </summary>
154-
public int GetHashCode(OpenApiSecurityScheme obj)
151+
public int GetHashCode(IOpenApiSecurityScheme obj)
155152
{
156-
return obj?.Reference?.Id == null ? 0 : obj.Reference.Id.GetHashCode();
153+
if (obj is null)
154+
{
155+
return 0;
156+
}
157+
else if (obj is OpenApiSecuritySchemeReference reference)
158+
{
159+
return string.IsNullOrEmpty(reference?.Reference?.Id) ? 0 : reference.Reference.Id.GetHashCode();
160+
}
161+
return obj.GetHashCode();
157162
}
158163
}
159164
}

src/Microsoft.OpenApi/Models/OpenApiSecurityScheme.cs

+27-58
Original file line numberDiff line numberDiff line change
@@ -5,83 +5,54 @@
55
using System.Collections.Generic;
66
using Microsoft.OpenApi.Extensions;
77
using Microsoft.OpenApi.Interfaces;
8+
using Microsoft.OpenApi.Models.Interfaces;
89
using Microsoft.OpenApi.Writers;
910

1011
namespace Microsoft.OpenApi.Models
1112
{
1213
/// <summary>
1314
/// Security Scheme Object.
1415
/// </summary>
15-
public class OpenApiSecurityScheme : IOpenApiReferenceable, IOpenApiExtensible
16+
public class OpenApiSecurityScheme : IOpenApiExtensible, IOpenApiReferenceable, IOpenApiSecurityScheme
1617
{
17-
/// <summary>
18-
/// REQUIRED. The type of the security scheme. Valid values are "apiKey", "http", "oauth2", "openIdConnect".
19-
/// </summary>
20-
public virtual SecuritySchemeType? Type { get; set; }
21-
22-
/// <summary>
23-
/// A short description for security scheme. CommonMark syntax MAY be used for rich text representation.
24-
/// </summary>
25-
public virtual string Description { get; set; }
26-
27-
/// <summary>
28-
/// REQUIRED. The name of the header, query or cookie parameter to be used.
29-
/// </summary>
30-
public virtual string Name { get; set; }
18+
/// <inheritdoc/>
19+
public SecuritySchemeType? Type { get; set; }
3120

32-
/// <summary>
33-
/// REQUIRED. The location of the API key. Valid values are "query", "header" or "cookie".
34-
/// </summary>
35-
public virtual ParameterLocation? In { get; set; }
21+
/// <inheritdoc/>
22+
public string Description { get; set; }
3623

37-
/// <summary>
38-
/// REQUIRED. The name of the HTTP Authorization scheme to be used
39-
/// in the Authorization header as defined in RFC7235.
40-
/// </summary>
41-
public virtual string Scheme { get; set; }
24+
/// <inheritdoc/>
25+
public string Name { get; set; }
4226

43-
/// <summary>
44-
/// A hint to the client to identify how the bearer token is formatted.
45-
/// Bearer tokens are usually generated by an authorization server,
46-
/// so this information is primarily for documentation purposes.
47-
/// </summary>
48-
public virtual string BearerFormat { get; set; }
27+
/// <inheritdoc/>
28+
public ParameterLocation? In { get; set; }
4929

50-
/// <summary>
51-
/// REQUIRED. An object containing configuration information for the flow types supported.
52-
/// </summary>
53-
public virtual OpenApiOAuthFlows Flows { get; set; }
30+
/// <inheritdoc/>
31+
public string Scheme { get; set; }
5432

55-
/// <summary>
56-
/// REQUIRED. OpenId Connect URL to discover OAuth2 configuration values.
57-
/// </summary>
58-
public virtual Uri OpenIdConnectUrl { get; set; }
33+
/// <inheritdoc/>
34+
public string BearerFormat { get; set; }
5935

60-
/// <summary>
61-
/// Specification Extensions.
62-
/// </summary>
63-
public virtual IDictionary<string, IOpenApiExtension> Extensions { get; set; } = new Dictionary<string, IOpenApiExtension>();
36+
/// <inheritdoc/>
37+
public OpenApiOAuthFlows Flows { get; set; }
6438

65-
/// <summary>
66-
/// Indicates if object is populated with data or is just a reference to the data
67-
/// </summary>
68-
public bool UnresolvedReference { get; set; }
39+
/// <inheritdoc/>
40+
public Uri OpenIdConnectUrl { get; set; }
6941

70-
/// <summary>
71-
/// Reference object.
72-
/// </summary>
73-
public OpenApiReference Reference { get; set; }
42+
/// <inheritdoc/>
43+
public IDictionary<string, IOpenApiExtension> Extensions { get; set; } = new Dictionary<string, IOpenApiExtension>();
7444

7545
/// <summary>
7646
/// Parameterless constructor
7747
/// </summary>
7848
public OpenApiSecurityScheme() { }
7949

8050
/// <summary>
81-
/// Initializes a copy of <see cref="OpenApiSecurityScheme"/> object
51+
/// Initializes a copy of <see cref="IOpenApiSecurityScheme"/> object
8252
/// </summary>
83-
public OpenApiSecurityScheme(OpenApiSecurityScheme securityScheme)
53+
public OpenApiSecurityScheme(IOpenApiSecurityScheme securityScheme)
8454
{
55+
Utils.CheckArgumentNull(securityScheme);
8556
Type = securityScheme?.Type;
8657
Description = securityScheme?.Description ?? Description;
8758
Name = securityScheme?.Name ?? Name;
@@ -91,27 +62,25 @@ public OpenApiSecurityScheme(OpenApiSecurityScheme securityScheme)
9162
Flows = securityScheme?.Flows != null ? new(securityScheme?.Flows) : null;
9263
OpenIdConnectUrl = securityScheme?.OpenIdConnectUrl != null ? new Uri(securityScheme.OpenIdConnectUrl.OriginalString, UriKind.RelativeOrAbsolute) : null;
9364
Extensions = securityScheme?.Extensions != null ? new Dictionary<string, IOpenApiExtension>(securityScheme.Extensions) : null;
94-
UnresolvedReference = securityScheme?.UnresolvedReference ?? UnresolvedReference;
95-
Reference = securityScheme?.Reference != null ? new(securityScheme?.Reference) : null;
9665
}
9766

9867
/// <summary>
9968
/// Serialize <see cref="OpenApiSecurityScheme"/> to Open Api v3.1
10069
/// </summary>
101-
public virtual void SerializeAsV31(IOpenApiWriter writer)
70+
public void SerializeAsV31(IOpenApiWriter writer)
10271
{
10372
SerializeInternal(writer, OpenApiSpecVersion.OpenApi3_1, (writer, element) => element.SerializeAsV31(writer));
10473
}
10574

10675
/// <summary>
10776
/// Serialize <see cref="OpenApiSecurityScheme"/> to Open Api v3.0
10877
/// </summary>
109-
public virtual void SerializeAsV3(IOpenApiWriter writer)
78+
public void SerializeAsV3(IOpenApiWriter writer)
11079
{
11180
SerializeInternal(writer, OpenApiSpecVersion.OpenApi3_0, (writer, element) => element.SerializeAsV3(writer));
11281
}
11382

114-
internal virtual void SerializeInternal(IOpenApiWriter writer, OpenApiSpecVersion version,
83+
private void SerializeInternal(IOpenApiWriter writer, OpenApiSpecVersion version,
11584
Action<IOpenApiWriter, IOpenApiSerializable> callback)
11685
{
11786
Utils.CheckArgumentNull(writer);
@@ -161,7 +130,7 @@ internal virtual void SerializeInternal(IOpenApiWriter writer, OpenApiSpecVersio
161130
/// <summary>
162131
/// Serialize <see cref="OpenApiSecurityScheme"/> to Open Api v2.0
163132
/// </summary>
164-
public virtual void SerializeAsV2(IOpenApiWriter writer)
133+
public void SerializeAsV2(IOpenApiWriter writer)
165134
{
166135
Utils.CheckArgumentNull(writer);
167136

0 commit comments

Comments
 (0)