Skip to content

Commit 5071753

Browse files
authored
Merge pull request #2107 from microsoft/fix/security-scheme-reference
fix/security scheme reference
2 parents e57d049 + b17a552 commit 5071753

File tree

38 files changed

+269
-386
lines changed

38 files changed

+269
-386
lines changed
Lines changed: 50 additions & 0 deletions
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

Lines changed: 4 additions & 4 deletions
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

Lines changed: 1 addition & 1 deletion
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

Lines changed: 24 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,10 @@
33

44
using System;
55
using System.Collections.Generic;
6+
using System.Linq;
67
using Microsoft.OpenApi.Interfaces;
8+
using Microsoft.OpenApi.Models.Interfaces;
9+
using Microsoft.OpenApi.Models.References;
710
using Microsoft.OpenApi.Writers;
811

912
namespace Microsoft.OpenApi.Models
@@ -16,7 +19,7 @@ namespace Microsoft.OpenApi.Models
1619
/// then the value is a list of scope names required for the execution.
1720
/// For other security scheme types, the array MUST be empty.
1821
/// </summary>
19-
public class OpenApiSecurityRequirement : Dictionary<OpenApiSecurityScheme, IList<string>>,
22+
public class OpenApiSecurityRequirement : Dictionary<OpenApiSecuritySchemeReference, IList<string>>,
2023
IOpenApiSerializable
2124
{
2225
/// <summary>
@@ -34,40 +37,36 @@ public OpenApiSecurityRequirement()
3437
/// </summary>
3538
public void SerializeAsV31(IOpenApiWriter writer)
3639
{
37-
SerializeInternal(writer, (writer, element) => element.SerializeAsV31(writer));
40+
SerializeInternal(writer, (w, s) => w.WritePropertyName(s.Reference.ReferenceV3));
3841
}
3942

4043
/// <summary>
4144
/// Serialize <see cref="OpenApiSecurityRequirement"/> to Open Api v3.0
4245
/// </summary>
4346
public void SerializeAsV3(IOpenApiWriter writer)
4447
{
45-
SerializeInternal(writer, (writer, element) => element.SerializeAsV3(writer));
48+
SerializeInternal(writer, (w, s) => w.WritePropertyName(s.Reference.ReferenceV3));
4649
}
4750

4851
/// <summary>
4952
/// Serialize <see cref="OpenApiSecurityRequirement"/>
5053
/// </summary>
51-
private void SerializeInternal(IOpenApiWriter writer, Action<IOpenApiWriter, IOpenApiSerializable> callback)
54+
private void SerializeInternal(IOpenApiWriter writer, Action<IOpenApiWriter, OpenApiSecuritySchemeReference> callback)
5255
{
53-
Utils.CheckArgumentNull(writer);;
56+
Utils.CheckArgumentNull(writer);
5457

5558
writer.WriteStartObject();
5659

57-
foreach (var securitySchemeAndScopesValuePair in this)
60+
// Reaching this point means the reference to a specific OpenApiSecurityScheme fails.
61+
// We are not able to serialize this SecurityScheme/Scopes key value pair since we do not know what
62+
// string to output.
63+
64+
foreach (var securitySchemeAndScopesValuePair in this.Where(static p => p.Key?.Target is not null))
5865
{
5966
var securityScheme = securitySchemeAndScopesValuePair.Key;
6067
var scopes = securitySchemeAndScopesValuePair.Value;
6168

62-
if (securityScheme.Reference == null)
63-
{
64-
// Reaching this point means the reference to a specific OpenApiSecurityScheme fails.
65-
// We are not able to serialize this SecurityScheme/Scopes key value pair since we do not know what
66-
// string to output.
67-
continue;
68-
}
69-
70-
writer.WritePropertyName(securityScheme.Reference.ReferenceV3);
69+
callback(writer, securityScheme);
7170

7271
writer.WriteStartArray();
7372

@@ -87,48 +86,19 @@ private void SerializeInternal(IOpenApiWriter writer, Action<IOpenApiWriter, IOp
8786
/// </summary>
8887
public void SerializeAsV2(IOpenApiWriter writer)
8988
{
90-
Utils.CheckArgumentNull(writer);;
91-
92-
writer.WriteStartObject();
93-
94-
foreach (var securitySchemeAndScopesValuePair in this)
95-
{
96-
var securityScheme = securitySchemeAndScopesValuePair.Key;
97-
var scopes = securitySchemeAndScopesValuePair.Value;
98-
99-
if (securityScheme.Reference == null)
100-
{
101-
// Reaching this point means the reference to a specific OpenApiSecurityScheme fails.
102-
// We are not able to serialize this SecurityScheme/Scopes key value pair since we do not know what
103-
// string to output.
104-
continue;
105-
}
106-
107-
securityScheme.SerializeAsV2(writer);
108-
109-
writer.WriteStartArray();
110-
111-
foreach (var scope in scopes)
112-
{
113-
writer.WriteValue(scope);
114-
}
115-
116-
writer.WriteEndArray();
117-
}
118-
119-
writer.WriteEndObject();
89+
SerializeInternal(writer, (w, s) => s.SerializeAsV2(w));
12090
}
12191

12292
/// <summary>
12393
/// Comparer for OpenApiSecurityScheme that only considers the Id in the Reference
12494
/// (i.e. the string that will actually be displayed in the written document)
12595
/// </summary>
126-
private class OpenApiSecuritySchemeReferenceEqualityComparer : IEqualityComparer<OpenApiSecurityScheme>
96+
private sealed class OpenApiSecuritySchemeReferenceEqualityComparer : IEqualityComparer<OpenApiSecuritySchemeReference>
12797
{
12898
/// <summary>
12999
/// Determines whether the specified objects are equal.
130100
/// </summary>
131-
public bool Equals(OpenApiSecurityScheme x, OpenApiSecurityScheme y)
101+
public bool Equals(OpenApiSecuritySchemeReference x, OpenApiSecuritySchemeReference y)
132102
{
133103
if (x == null && y == null)
134104
{
@@ -140,20 +110,19 @@ public bool Equals(OpenApiSecurityScheme x, OpenApiSecurityScheme y)
140110
return false;
141111
}
142112

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

151116
/// <summary>
152117
/// Returns a hash code for the specified object.
153118
/// </summary>
154-
public int GetHashCode(OpenApiSecurityScheme obj)
119+
public int GetHashCode(OpenApiSecuritySchemeReference obj)
155120
{
156-
return obj?.Reference?.Id == null ? 0 : obj.Reference.Id.GetHashCode();
121+
if (obj is null)
122+
{
123+
return 0;
124+
}
125+
return string.IsNullOrEmpty(obj?.Reference?.Id) ? 0 : obj.Reference.Id.GetHashCode();
157126
}
158127
}
159128
}

src/Microsoft.OpenApi/Models/OpenApiSecurityScheme.cs

Lines changed: 27 additions & 58 deletions
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

src/Microsoft.OpenApi/Models/References/BaseOpenApiReferenceHolder.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ public T Target
1616
{
1717
get
1818
{
19-
_target ??= Reference.HostDocument.ResolveReferenceTo<T>(Reference);
19+
_target ??= Reference.HostDocument?.ResolveReferenceTo<T>(Reference);
2020
return _target;
2121
}
2222
}

0 commit comments

Comments
 (0)