Skip to content

Commit

Permalink
Feature | Azure Active Directory Managed Identity authentication supp…
Browse files Browse the repository at this point in the history
…ort (#730)
  • Loading branch information
cheenamalhotra committed Oct 8, 2020
1 parent f49c44d commit 0d4c9bb
Show file tree
Hide file tree
Showing 41 changed files with 1,458 additions and 297 deletions.
Expand Up @@ -88,6 +88,8 @@
|<xref:Microsoft.Data.SqlClient.SqlAuthenticationMethod.ActiveDirectoryInteractive%2A>|
|<xref:Microsoft.Data.SqlClient.SqlAuthenticationMethod.ActiveDirectoryServicePrincipal%2A>|
|<xref:Microsoft.Data.SqlClient.SqlAuthenticationMethod.ActiveDirectoryDeviceCodeFlow%2A>|
|<xref:Microsoft.Data.SqlClient.SqlAuthenticationMethod.ActiveDirectoryManagedIdentity%2A>|
|<xref:Microsoft.Data.SqlClient.SqlAuthenticationMethod.ActiveDirectoryMSI%2A>|
## Examples
The following example demonstrates providing a custom device flow callback to SqlClient for the Device Code Flow authentication method:
Expand Down
Expand Up @@ -33,5 +33,13 @@
<summary>The authentication method uses Active Directory Device Code Flow. Use Active Directory Device Code Flow to connect to a SQL Database from devices and operating systems that do not provide a Web browser, using another device to perform interactive authentication.</summary>
<value>6</value>
</ActiveDirectoryDeviceCodeFlow>
<ActiveDirectoryManagedIdentity>
<summary>The authentication method uses Active Directory Managed Identity. Use System Assigned or User Assigned Managed Identity to connect to SQL Database from Azure client environments that have enabled support for Managed Identity. For User Assigned Managed Identity, 'User Id' or 'UID' is required to be set to the object ID of the user identity.</summary>
<value>7</value>
</ActiveDirectoryManagedIdentity>
<ActiveDirectoryMSI>
<summary>Alias for "Active Directory Managed Identity" authentication method. Use System Assigned or User Assigned Managed Identity to connect to SQL Database from Azure client environments that have enabled support for Managed Identity. For User Assigned Managed Identity, 'User Id' or 'UID' is required to be set to the object ID of the user identity.</summary>
<value>8</value>
</ActiveDirectoryMSI>
</members>
</docs>
Expand Up @@ -12,8 +12,7 @@
<param name="userId">The user login name/ID.</param>
<param name="password">The user password.</param>
<param name="connectionId">The connection ID.</param>
<summary>Initializes a new instance of the <see cref="T:Microsoft.Data.SqlClient.SqlAuthenticationParameters" />
class using the specified authentication method, server name, database name, resource URI, authority URI, user login name/ID, user password and connection ID.</summary>
<summary>Initializes a new instance of the <see cref="T:Microsoft.Data.SqlClient.SqlAuthenticationParameters" /> class using the specified authentication method, server name, database name, resource URI, authority URI, user login name/ID, user password and connection ID.</summary>
</ctor>
<AuthenticationMethod>
<summary>Gets the authentication method.</summary>
Expand Down
Expand Up @@ -93,6 +93,10 @@ public enum SqlAuthenticationMethod
ActiveDirectoryServicePrincipal = 5,
/// <include file='../../../../doc/snippets/Microsoft.Data.SqlClient/SqlAuthenticationMethod.xml' path='docs/members[@name="SqlAuthenticationMethod"]/ActiveDirectoryDeviceCodeFlow/*'/>
ActiveDirectoryDeviceCodeFlow = 6,
/// <include file='../../../../doc/snippets/Microsoft.Data.SqlClient/SqlAuthenticationMethod.xml' path='docs/members[@name="SqlAuthenticationMethod"]/ActiveDirectoryManagedIdentity/*'/>
ActiveDirectoryManagedIdentity = 7,
/// <include file='../../../../doc/snippets/Microsoft.Data.SqlClient/SqlAuthenticationMethod.xml' path='docs/members[@name="SqlAuthenticationMethod"]/ActiveDirectoryMSI/*'/>
ActiveDirectoryMSI = 8,
/// <include file='../../../../doc/snippets/Microsoft.Data.SqlClient/SqlAuthenticationMethod.xml' path='docs/members[@name="SqlAuthenticationMethod"]/NotSpecified/*'/>
NotSpecified = 0,
/// <include file='../../../../doc/snippets/Microsoft.Data.SqlClient/SqlAuthenticationMethod.xml' path='docs/members[@name="SqlAuthenticationMethod"]/SqlPassword/*'/>
Expand Down Expand Up @@ -580,12 +584,12 @@ public sealed partial class SqlConnection : System.Data.Common.DbConnection, Sys
[System.ComponentModel.DesignerSerializationVisibilityAttribute(0)]
public System.Guid ClientConnectionId { get { throw null; } }

///
///
/// for internal test only
///
[System.ComponentModel.DesignerSerializationVisibilityAttribute(0)]
internal string SQLDNSCachingSupportedState { get { throw null; } }
///
///
/// for internal test only
///
[System.ComponentModel.DesignerSerializationVisibilityAttribute(0)]
Expand Down
Expand Up @@ -78,6 +78,9 @@
<Compile Include="..\..\src\Microsoft\Data\SqlClient\ActiveDirectoryAuthenticationProvider.cs">
<Link>Microsoft\Data\SqlClient\ActiveDirectoryAuthenticationProvider.cs</Link>
</Compile>
<Compile Include="..\..\src\Microsoft\Data\SqlClient\AzureManagedIdentityAuthenticationProvider.cs">
<Link>Microsoft\Data\SqlClient\AzureManagedIdentityAuthenticationProvider.cs</Link>
</Compile>
<Compile Include="..\..\src\Microsoft\Data\SqlClient\Server\ExtendedClrTypeCode.cs">
<Link>Microsoft\Data\SqlClient\Server\ExtendedClrTypeCode.cs</Link>
</Compile>
Expand Down
Expand Up @@ -104,10 +104,12 @@ internal static string ConvertToString(object value)
const string ActiveDirectoryInteractiveString = "Active Directory Interactive";
const string ActiveDirectoryServicePrincipalString = "Active Directory Service Principal";
const string ActiveDirectoryDeviceCodeFlowString = "Active Directory Device Code Flow";
internal const string ActiveDirectoryManagedIdentityString = "Active Directory Managed Identity";
internal const string ActiveDirectoryMSIString = "Active Directory MSI";

internal static bool TryConvertToAuthenticationType(string value, out SqlAuthenticationMethod result)
{
Debug.Assert(Enum.GetNames(typeof(SqlAuthenticationMethod)).Length == 7, "SqlAuthenticationMethod enum has changed, update needed");
Debug.Assert(Enum.GetNames(typeof(SqlAuthenticationMethod)).Length == 9, "SqlAuthenticationMethod enum has changed, update needed");

bool isSuccess = false;

Expand Down Expand Up @@ -147,6 +149,18 @@ internal static bool TryConvertToAuthenticationType(string value, out SqlAuthent
result = SqlAuthenticationMethod.ActiveDirectoryDeviceCodeFlow;
isSuccess = true;
}
else if (StringComparer.InvariantCultureIgnoreCase.Equals(value, ActiveDirectoryManagedIdentityString)
|| StringComparer.InvariantCultureIgnoreCase.Equals(value, Convert.ToString(SqlAuthenticationMethod.ActiveDirectoryManagedIdentity, CultureInfo.InvariantCulture)))
{
result = SqlAuthenticationMethod.ActiveDirectoryManagedIdentity;
isSuccess = true;
}
else if (StringComparer.InvariantCultureIgnoreCase.Equals(value, ActiveDirectoryMSIString)
|| StringComparer.InvariantCultureIgnoreCase.Equals(value, Convert.ToString(SqlAuthenticationMethod.ActiveDirectoryMSI, CultureInfo.InvariantCulture)))
{
result = SqlAuthenticationMethod.ActiveDirectoryMSI;
isSuccess = true;
}
else
{
result = DbConnectionStringDefaults.Authentication;
Expand Down Expand Up @@ -367,7 +381,7 @@ internal static SqlConnectionAttestationProtocol ConvertToAttestationProtocol(st
}
catch (ArgumentException e)
{
// to be consistent with the messages we send in case of wrong type usage, replace
// to be consistent with the messages we send in case of wrong type usage, replace
// the error with our exception, and keep the original one as inner one for troubleshooting
throw ADP.ConvertFailed(value.GetType(), typeof(SqlConnectionAttestationProtocol), e);
}
Expand Down Expand Up @@ -411,7 +425,7 @@ internal static string ApplicationIntentToString(ApplicationIntent value)
/// * if the value is from type ApplicationIntent, it will be used as is
/// * if the value is from integral type (SByte, Int16, Int32, Int64, Byte, UInt16, UInt32, or UInt64), it will be converted to enum
/// * if the value is another enum or any other type, it will be blocked with an appropriate ArgumentException
///
///
/// in any case above, if the converted value is out of valid range, the method raises ArgumentOutOfRangeException.
/// </summary>
/// <returns>application intent value in the valid range</returns>
Expand Down Expand Up @@ -467,7 +481,7 @@ internal static ApplicationIntent ConvertToApplicationIntent(string keyword, obj
}
catch (ArgumentException e)
{
// to be consistent with the messages we send in case of wrong type usage, replace
// to be consistent with the messages we send in case of wrong type usage, replace
// the error with our exception, and keep the original one as inner one for troubleshooting
throw ADP.ConvertFailed(value.GetType(), typeof(ApplicationIntent), e);
}
Expand All @@ -487,13 +501,15 @@ internal static ApplicationIntent ConvertToApplicationIntent(string keyword, obj

internal static bool IsValidAuthenticationTypeValue(SqlAuthenticationMethod value)
{
Debug.Assert(Enum.GetNames(typeof(SqlAuthenticationMethod)).Length == 7, "SqlAuthenticationMethod enum has changed, update needed");
Debug.Assert(Enum.GetNames(typeof(SqlAuthenticationMethod)).Length == 9, "SqlAuthenticationMethod enum has changed, update needed");
return value == SqlAuthenticationMethod.SqlPassword
|| value == SqlAuthenticationMethod.ActiveDirectoryPassword
|| value == SqlAuthenticationMethod.ActiveDirectoryIntegrated
|| value == SqlAuthenticationMethod.ActiveDirectoryInteractive
|| value == SqlAuthenticationMethod.ActiveDirectoryServicePrincipal
|| value == SqlAuthenticationMethod.ActiveDirectoryDeviceCodeFlow
|| value == SqlAuthenticationMethod.ActiveDirectoryManagedIdentity
|| value == SqlAuthenticationMethod.ActiveDirectoryMSI
|| value == SqlAuthenticationMethod.NotSpecified;
}

Expand All @@ -515,6 +531,10 @@ internal static string AuthenticationTypeToString(SqlAuthenticationMethod value)
return ActiveDirectoryServicePrincipalString;
case SqlAuthenticationMethod.ActiveDirectoryDeviceCodeFlow:
return ActiveDirectoryDeviceCodeFlowString;
case SqlAuthenticationMethod.ActiveDirectoryManagedIdentity:
return ActiveDirectoryManagedIdentityString;
case SqlAuthenticationMethod.ActiveDirectoryMSI:
return ActiveDirectoryMSIString;
default:
return null;
}
Expand Down Expand Up @@ -572,7 +592,7 @@ internal static SqlAuthenticationMethod ConvertToAuthenticationType(string keywo
}
catch (ArgumentException e)
{
// to be consistent with the messages we send in case of wrong type usage, replace
// to be consistent with the messages we send in case of wrong type usage, replace
// the error with our exception, and keep the original one as inner one for troubleshooting
throw ADP.ConvertFailed(value.GetType(), typeof(SqlAuthenticationMethod), e);
}
Expand Down Expand Up @@ -648,7 +668,7 @@ internal static SqlConnectionColumnEncryptionSetting ConvertToColumnEncryptionSe
}
catch (ArgumentException e)
{
// to be consistent with the messages we send in case of wrong type usage, replace
// to be consistent with the messages we send in case of wrong type usage, replace
// the error with our exception, and keep the original one as inner one for troubleshooting
throw ADP.ConvertFailed(value.GetType(), typeof(SqlConnectionColumnEncryptionSetting), e);
}
Expand All @@ -672,40 +692,41 @@ internal static partial class DbConnectionStringDefaults
// all
// internal const string NamedConnection = "";

private const string _emptyString = "";
// SqlClient
internal const ApplicationIntent ApplicationIntent = Microsoft.Data.SqlClient.ApplicationIntent.ReadWrite;
internal const string ApplicationName = "Core Microsoft SqlClient Data Provider";
internal const string AttachDBFilename = "";
internal const string AttachDBFilename = _emptyString;
internal const int CommandTimeout = 30;
internal const int ConnectTimeout = 15;
internal const string CurrentLanguage = "";
internal const string DataSource = "";
internal const string CurrentLanguage = _emptyString;
internal const string DataSource = _emptyString;
internal const bool Encrypt = false;
internal const bool Enlist = true;
internal const string FailoverPartner = "";
internal const string InitialCatalog = "";
internal const string FailoverPartner = _emptyString;
internal const string InitialCatalog = _emptyString;
internal const bool IntegratedSecurity = false;
internal const int LoadBalanceTimeout = 0; // default of 0 means don't use
internal const bool MultipleActiveResultSets = false;
internal const bool MultiSubnetFailover = false;
internal const int MaxPoolSize = 100;
internal const int MinPoolSize = 0;
internal const int PacketSize = 8000;
internal const string Password = "";
internal const string Password = _emptyString;
internal const bool PersistSecurityInfo = false;
internal const bool Pooling = true;
internal const bool TrustServerCertificate = false;
internal const string TypeSystemVersion = "Latest";
internal const string UserID = "";
internal const string UserID = _emptyString;
internal const bool UserInstance = false;
internal const bool Replication = false;
internal const string WorkstationID = "";
internal const string WorkstationID = _emptyString;
internal const string TransactionBinding = "Implicit Unbind";
internal const int ConnectRetryCount = 1;
internal const int ConnectRetryInterval = 10;
internal static readonly SqlAuthenticationMethod Authentication = SqlAuthenticationMethod.NotSpecified;
internal const SqlConnectionColumnEncryptionSetting ColumnEncryptionSetting = SqlConnectionColumnEncryptionSetting.Disabled;
internal const string EnclaveAttestationUrl = "";
internal const string EnclaveAttestationUrl = _emptyString;
internal const SqlConnectionAttestationProtocol AttestationProtocol = SqlConnectionAttestationProtocol.NotSpecified;
}

Expand Down Expand Up @@ -813,7 +834,7 @@ internal static class DbConnectionStringSynonyms

//internal const string MultiSubnetFailover = MULTISUBNETFAILOVER;
internal const string MULTISUBNETFAILOVER = "MultiSubnetFailover";

//internal const string NetworkLibrary = NET+","+NETWORK;
internal const string NET = "net";
internal const string NETWORK = "network";
Expand Down
Expand Up @@ -15,6 +15,7 @@ internal partial class SqlAuthenticationProviderManager

static SqlAuthenticationProviderManager()
{
var azureManagedIdentityAuthenticationProvider = new AzureManagedIdentityAuthenticationProvider();
SqlAuthenticationProviderConfigurationSection configurationSection = null;

try
Expand All @@ -40,6 +41,8 @@ static SqlAuthenticationProviderManager()
Instance.SetProvider(SqlAuthenticationMethod.ActiveDirectoryInteractive, activeDirectoryAuthProvider);
Instance.SetProvider(SqlAuthenticationMethod.ActiveDirectoryServicePrincipal, activeDirectoryAuthProvider);
Instance.SetProvider(SqlAuthenticationMethod.ActiveDirectoryDeviceCodeFlow, activeDirectoryAuthProvider);
Instance.SetProvider(SqlAuthenticationMethod.ActiveDirectoryManagedIdentity, azureManagedIdentityAuthenticationProvider);
Instance.SetProvider(SqlAuthenticationMethod.ActiveDirectoryMSI, azureManagedIdentityAuthenticationProvider);
}

/// <summary>
Expand Down Expand Up @@ -153,6 +156,10 @@ private static SqlAuthenticationMethod AuthenticationEnumFromString(string authe
return SqlAuthenticationMethod.ActiveDirectoryServicePrincipal;
case ActiveDirectoryDeviceCodeFlow:
return SqlAuthenticationMethod.ActiveDirectoryDeviceCodeFlow;
case ActiveDirectoryManagedIdentity:
return SqlAuthenticationMethod.ActiveDirectoryManagedIdentity;
case ActiveDirectoryMSI:
return SqlAuthenticationMethod.ActiveDirectoryMSI;
default:
throw SQL.UnsupportedAuthentication(authentication);
}
Expand Down
Expand Up @@ -8,13 +8,17 @@ internal partial class SqlAuthenticationProviderManager
{
static SqlAuthenticationProviderManager()
{
var azureManagedIdentityAuthenticationProvider = new AzureManagedIdentityAuthenticationProvider();

Instance = new SqlAuthenticationProviderManager();
var activeDirectoryAuthProvider = new ActiveDirectoryAuthenticationProvider(Instance._applicationClientId);
Instance.SetProvider(SqlAuthenticationMethod.ActiveDirectoryPassword, activeDirectoryAuthProvider);
Instance.SetProvider(SqlAuthenticationMethod.ActiveDirectoryIntegrated, activeDirectoryAuthProvider);
Instance.SetProvider(SqlAuthenticationMethod.ActiveDirectoryInteractive, activeDirectoryAuthProvider);
Instance.SetProvider(SqlAuthenticationMethod.ActiveDirectoryServicePrincipal, activeDirectoryAuthProvider);
Instance.SetProvider(SqlAuthenticationMethod.ActiveDirectoryDeviceCodeFlow, activeDirectoryAuthProvider);
Instance.SetProvider(SqlAuthenticationMethod.ActiveDirectoryManagedIdentity, azureManagedIdentityAuthenticationProvider);
Instance.SetProvider(SqlAuthenticationMethod.ActiveDirectoryMSI, azureManagedIdentityAuthenticationProvider);
}
}
}
Expand Up @@ -19,6 +19,8 @@ internal partial class SqlAuthenticationProviderManager
private const string ActiveDirectoryInteractive = "active directory interactive";
private const string ActiveDirectoryServicePrincipal = "active directory service principal";
private const string ActiveDirectoryDeviceCodeFlow = "active directory device code flow";
private const string ActiveDirectoryManagedIdentity = "active directory managed identity";
private const string ActiveDirectoryMSI = "active directory msi";

private readonly string _typeName;
private readonly IReadOnlyCollection<SqlAuthenticationMethod> _authenticationsWithAppSpecifiedProvider;
Expand Down

0 comments on commit 0d4c9bb

Please sign in to comment.