Skip to content

Commit 16c6988

Browse files
committed
Move auth provider registry into Abstractions; remove core reflection bridge
Relocates the authentication provider registry into the shared Abstractions assembly and removes the Abstractions->core reflection bridge. A lazy, core-side AuthenticationBootstrapper performs config-driven and Azure extension provider discovery, seeding the Abstractions registry on first federated authentication. - Add internal AuthenticationProviderRegistry + resource infra (AbstractionsStrings) to Abstractions; wire SqlAuthenticationProvider Get/SetProvider to it directly. - Delete SqlAuthenticationProvider.Internal reflection bridge. - Replace public SqlAuthenticationProviderManager with internal AuthenticationBootstrapper in core; remove it from the public ref surface and notsupported stubs. - Trigger bootstrap from SqlConnectionInternal.GetFedAuthToken. - Redistribute manager tests into Abstractions.Test (registry) and UnitTests (bootstrapper); Azure DefaultAuthProviderTests triggers bootstrap via reflection (TODO: switch to IVT once PR #4385 signs Azure.Test). - Update AotCompatibility tool/docs and doc-comment references.
1 parent 99a0cb9 commit 16c6988

33 files changed

Lines changed: 1443 additions & 838 deletions

aot-auth-provider-proposal.md

Lines changed: 374 additions & 0 deletions
Large diffs are not rendered by default.

doc/snippets/Microsoft.Data.SqlClient/SqlAuthenticationProviderManager.xml

Lines changed: 0 additions & 85 deletions
This file was deleted.

src/Microsoft.Data.SqlClient.Extensions/Abstractions/doc/SqlAuthenticationProvider.xml

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -131,9 +131,7 @@ See the LICENSE file in the project root for more information.
131131
<param name="authenticationMethod">The authentication method.</param>
132132
<returns>The authentication provider or <see langword="null" /> if not found.</returns>
133133
<remarks>
134-
This method is provided for backward compatibility only. New code should use
135-
<see cref="M:Microsoft.Data.SqlClient.SqlAuthenticationProviderManager.GetProvider(Microsoft.Data.SqlClient.SqlAuthenticationMethod)" />
136-
instead. This method will be deprecated in a future version and subsequently removed.
134+
This is the canonical way to retrieve a registered authentication provider.
137135
</remarks>
138136
</GetProvider>
139137
<SetProvider>
@@ -144,9 +142,7 @@ See the LICENSE file in the project root for more information.
144142
<see langword="true" /> if the operation succeeded; otherwise, <see langword="false" /> (for example, the existing provider disallows overriding).
145143
</returns>
146144
<remarks>
147-
This method is provided for backward compatibility only. New code should use
148-
<see cref="M:Microsoft.Data.SqlClient.SqlAuthenticationProviderManager.SetProvider(Microsoft.Data.SqlClient.SqlAuthenticationMethod,Microsoft.Data.SqlClient.SqlAuthenticationProvider)" />
149-
instead. This method will be deprecated in a future version and subsequently removed.
145+
This is the canonical way to register an authentication provider.
150146
</remarks>
151147
</SetProvider>
152148
</members>

src/Microsoft.Data.SqlClient.Extensions/Abstractions/src/Abstractions.csproj

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,12 @@
3535
<!-- Strong name signing ============================================= -->
3636
<!-- This is done in Directory.Build.props -->
3737

38-
<!-- Unsigned: expose internals to the test assembly in any reference mode. -->
38+
<!-- Unsigned: expose internals to the test assembly and to the SqlClient driver in any
39+
reference mode. The driver hosts the authentication provider bootstrapper, which seeds
40+
the AuthenticationProviderRegistry (internal to this assembly). -->
3941
<ItemGroup Condition="'$(SigningKeyPath)' == ''">
4042
<InternalsVisibleTo Include="$(AssemblyName).Test" />
43+
<InternalsVisibleTo Include="Microsoft.Data.SqlClient" />
4144
</ItemGroup>
4245

4346
<!--
@@ -48,6 +51,12 @@
4851
-->
4952
<ItemGroup Condition="'$(SigningKeyPath)' != '' AND '$(ReferenceType)' == 'Package'">
5053
<InternalsVisibleTo Include="$(AssemblyName).Test, PublicKey=00240000048000001401000006020000002400005253413100080000010001003D19684676DA365F331D00CE7BD4B8EF03E74102F39A5681B40622703D68F0298ECACECC723D3FFC1EA9365AF4958578550EA1EBEEC084B0B3757F3762449F5365E872802A4B548056760764FAD062BFEE81ED26183109AD46810E7E6E965419D0A10473680144D20C1BFE1027A5F586CA987523C06F5C126C44EA7D4F51EB023867A9F294315F95775ACEFD2D678186919458DFCCB4DE2E9F53AEFC766C7CBCEC474ED21C1616E5A9414D366D91D121C39F5FE6641295ADC058EF3FB10593BCDE2E82D9F217C2634909EEF496CD53AE78ABBEA572B871D72EBFC5378205950ABA97C7CCC2B9635D96933D5F9C9624D71FF53EE2094CF3A6BD38534D66E414B7" />
54+
<!--
55+
Expose internals to the SqlClient driver, which hosts the authentication provider
56+
bootstrapper that seeds the AuthenticationProviderRegistry. The driver is signed with the
57+
product key (public key token 23ec7fc2d6eaa4a5), which differs from the test key above.
58+
-->
59+
<InternalsVisibleTo Include="Microsoft.Data.SqlClient, PublicKey=0024000004800000940000000602000000240000525341310004000001000100C14C6D7EE398F4910F849A9511EC66BBB621E7FFF7E0802860E283AAA4F3B7FAD06A3AFA95628D8907176D3C61E3F4DF845CDD821B3751A9917FC7A5EC2F191C887AAF72732B5FD85F6D82DE8461BCBA93ECCE724737D48683A62089597DE4EA24F263FB9A70B04DD3F4B9A003CE883DAC51DE59A6DB2BAF429A42BCD72112EE" />
5160
</ItemGroup>
5261

5362
<!-- Build Output ==================================================== -->
Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
// See the LICENSE file in the project root for more information.
4+
5+
using System;
6+
using System.Collections.Concurrent;
7+
using System.Collections.Generic;
8+
using Microsoft.Data.SqlClient.Internal;
9+
10+
namespace Microsoft.Data.SqlClient;
11+
12+
/// <summary>
13+
/// Holds the registry of <see cref="SqlAuthenticationProvider"/> instances keyed by
14+
/// <see cref="SqlAuthenticationMethod"/>. This is the shared store that backs the public
15+
/// <see cref="SqlAuthenticationProvider.GetProvider"/> and
16+
/// <see cref="SqlAuthenticationProvider.SetProvider"/> methods.
17+
/// </summary>
18+
/// <remarks>
19+
/// Providers fall into two categories:
20+
/// <list type="bullet">
21+
/// <item>
22+
/// <description>
23+
/// Permanent providers, registered via <see cref="SetPermanentProvider"/> (e.g. from an
24+
/// application's configuration). These take precedence and cannot be overridden by a later
25+
/// call to <see cref="SetProvider"/>.
26+
/// </description>
27+
/// </item>
28+
/// <item>
29+
/// <description>
30+
/// Overridable providers, registered via <see cref="SetProvider"/>. These can be replaced
31+
/// by subsequent <see cref="SetProvider"/> calls, but never override a permanent provider.
32+
/// </description>
33+
/// </item>
34+
/// </list>
35+
/// </remarks>
36+
internal sealed class AuthenticationProviderRegistry
37+
{
38+
/// <summary>
39+
/// The singleton instance backing the static accessors.
40+
/// </summary>
41+
private static readonly AuthenticationProviderRegistry s_instance = new();
42+
43+
/// <summary>
44+
/// Authentication methods whose provider was registered as permanent (e.g. application
45+
/// specified). These are not overridable via <see cref="SetProvider"/>.
46+
/// </summary>
47+
private readonly HashSet<SqlAuthenticationMethod> _permanentProviders = new();
48+
49+
/// <summary>
50+
/// The registered providers keyed by authentication method.
51+
/// </summary>
52+
private readonly ConcurrentDictionary<SqlAuthenticationMethod, SqlAuthenticationProvider> _providers = new();
53+
54+
private AuthenticationProviderRegistry()
55+
{
56+
}
57+
58+
/// <summary>
59+
/// Gets the provider registered for the given authentication method, or <see langword="null"/>
60+
/// if none is registered.
61+
/// </summary>
62+
internal static SqlAuthenticationProvider? GetProvider(SqlAuthenticationMethod authenticationMethod)
63+
{
64+
return s_instance._providers.TryGetValue(authenticationMethod, out SqlAuthenticationProvider? value)
65+
? value
66+
: null;
67+
}
68+
69+
/// <summary>
70+
/// Registers an overridable provider for the given authentication method.
71+
/// </summary>
72+
/// <returns>
73+
/// <see langword="true"/> if the provider was registered; <see langword="false"/> if a
74+
/// permanent provider is already registered for the authentication method.
75+
/// </returns>
76+
/// <exception cref="NotSupportedException">
77+
/// The provider does not support the given authentication method.
78+
/// </exception>
79+
internal static bool SetProvider(SqlAuthenticationMethod authenticationMethod, SqlAuthenticationProvider provider)
80+
{
81+
if (!provider.IsSupported(authenticationMethod))
82+
{
83+
throw new NotSupportedException(
84+
string.Format(
85+
AbstractionsStrings.SQL_UnsupportedAuthenticationByProvider,
86+
provider.GetType().Name,
87+
authenticationMethod.ToString()));
88+
}
89+
90+
if (s_instance._permanentProviders.Contains(authenticationMethod))
91+
{
92+
SqlClientEventSource.Log.TryTraceEvent(
93+
"AuthenticationProviderRegistry.SetProvider | Failed to add provider {0} because a " +
94+
"permanent provider with type {1} already existed for authentication {2}.",
95+
GetProviderType(provider),
96+
GetProviderType(GetProvider(authenticationMethod)),
97+
authenticationMethod);
98+
99+
// A permanent provider was already specified for this authentication method, so we
100+
// won't override it.
101+
return false;
102+
}
103+
104+
s_instance._providers.AddOrUpdate(
105+
authenticationMethod,
106+
provider,
107+
(SqlAuthenticationMethod key, SqlAuthenticationProvider oldProvider) =>
108+
{
109+
oldProvider?.BeforeUnload(authenticationMethod);
110+
provider.BeforeLoad(authenticationMethod);
111+
112+
SqlClientEventSource.Log.TryTraceEvent(
113+
"AuthenticationProviderRegistry.SetProvider | Added auth provider {0}, overriding " +
114+
"existing provider {1} for authentication {2}.",
115+
GetProviderType(provider),
116+
GetProviderType(oldProvider),
117+
authenticationMethod);
118+
119+
return provider;
120+
});
121+
122+
return true;
123+
}
124+
125+
/// <summary>
126+
/// Registers a permanent provider for the given authentication method. Permanent providers
127+
/// take precedence and cannot be overridden by <see cref="SetProvider"/>.
128+
/// </summary>
129+
/// <remarks>
130+
/// Callers are responsible for verifying that the provider supports the authentication method
131+
/// before registering it.
132+
/// </remarks>
133+
internal static void SetPermanentProvider(SqlAuthenticationMethod authenticationMethod, SqlAuthenticationProvider provider)
134+
{
135+
s_instance._providers[authenticationMethod] = provider;
136+
s_instance._permanentProviders.Add(authenticationMethod);
137+
}
138+
139+
private static string GetProviderType(SqlAuthenticationProvider? provider)
140+
{
141+
if (provider is null)
142+
{
143+
return "null";
144+
}
145+
return provider.GetType().FullName ?? "unknown";
146+
}
147+
}

0 commit comments

Comments
 (0)