Skip to content

Commit

Permalink
Additional AAD authentication options (dotnet#560)
Browse files Browse the repository at this point in the history
# Resolved Conflicts:
#	src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs
  • Loading branch information
David Engel authored and karinazhou committed Jun 10, 2020
1 parent 025da7f commit 9d27529
Show file tree
Hide file tree
Showing 38 changed files with 788 additions and 293 deletions.
2 changes: 2 additions & 0 deletions BUILDGUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,8 @@ Manual Tests require the below setup to run:
|NPConnectionString | Connection String for a Named Pipes enabled SQL Server instance.| `Server=\\{servername}\pipe\sql\query;Database={Database_Name};Trusted_Connection=True;` <br/> OR <br/> `Data Source=np:{servername};Initial Catalog={Database_Name};Integrated Security=True;`|
|AADAuthorityURL | (Optional) Identifies the OAuth2 authority resource for `Server` specified in `AADPasswordConnectionString` | `https://login.windows.net/<tenant>`, where `<tenant>` is the tenant ID of the Azure Active Directory (Azure AD) tenant |
|AADPasswordConnectionString | (Optional) Connection String for testing Azure Active Directory Password Authentication. | `Data Source={server.database.windows.net}; Initial Catalog={Azure_DB_Name};Authentication=Active Directory Password; User ID={AAD_User}; Password={AAD_User_Password};`|
|AADSecurePrincipalId | (Optional) The Application Id of a registered application which has been granted permission to the database defined in the AADPasswordConnectionString. | {Application ID} |
|AADSecurePrincipalSecret | (Optional) A Secret defined for a registered application which has been granted permission to the database defined in the AADPasswordConnectionString. | {Secret} |
|AzureKeyVaultURL | (Optional) Azure Key Vault Identifier URL | `https://{keyvaultname}.vault.azure.net/` |
|AzureKeyVaultClientId | (Optional) "Application (client) ID" of an Active Directory registered application, granted access to the Azure Key Vault specified in `AZURE_KEY_VAULT_URL`. Requires the key permissions Get, List, Import, Decrypt, Encrypt, Unwrap, Wrap, Verify, and Sign. | _{Client Application ID}_ |
|AzureKeyVaultClientSecret | (Optional) "Client Secret" of the Active Directory registered application, granted access to the Azure Key Vault specified in `AZURE_KEY_VAULT_URL` | _{Client Application Secret}_ |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,16 @@
<value>2</value>
</ActiveDirectoryPassword>
<ActiveDirectoryIntegrated>
<summary>The authentication method uses Active Directory Integrated. Use Active Directory Integrated to connect to a SQL Database using integrated Windows authentication. Available for .NET Framework applications only.</summary>
<summary>The authentication method uses Active Directory Integrated. Use Active Directory Integrated to connect to a SQL Database using integrated Windows authentication.</summary>
<value>3</value>
</ActiveDirectoryIntegrated>
<ActiveDirectoryInteractive>
<summary>The authentication method uses Active Directory Interactive. Available since the .NET Framework 4.7.2 and for .NET Framework applications only.</summary>
<summary>The authentication method uses Active Directory Interactive. Use Active Directory Interactive to connect to a SQL Database with an interactive authentication flow.</summary>
<value>4</value>
</ActiveDirectoryInteractive>
<ActiveDirectoryServicePrincipal>
<summary>The authentication method uses Active Directory Service Principal. Use Active Directory Service Principal to connect to a SQL Database using the client ID and secret of a service principal identity.</summary>
<value>5</value>
</ActiveDirectoryServicePrincipal>
</members>
</docs>
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@ public enum SqlAuthenticationMethod
ActiveDirectoryInteractive = 4,
/// <include file='../../../../doc/snippets/Microsoft.Data.SqlClient/SqlAuthenticationMethod.xml' path='docs/members[@name="SqlAuthenticationMethod"]/ActiveDirectoryPassword/*'/>
ActiveDirectoryPassword = 2,
/// <include file='../../../../doc/snippets/Microsoft.Data.SqlClient/SqlAuthenticationMethod.xml' path='docs/members[@name="SqlAuthenticationMethod"]/ActiveDirectoryServicePrincipal/*'/>
ActiveDirectoryServicePrincipal = 5,
/// <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
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,12 @@ protected internal void DoomThisConnection()
SqlClientEventSource.Log.PoolerTraceEvent("<prov.DbConnectionInternal.DoomThisConnection|RES|INFO|CPOOL> {0}, Dooming", ObjectID);
}

// Reset connection doomed status so it can be re-connected and pooled.
protected internal void UnDoomThisConnection()
{
_connectionIsDoomed = false;
}

protected internal virtual DataTable GetSchema(DbConnectionFactory factory, DbConnectionPoolGroup poolGroup, DbConnection outerConnection, string collectionName, string[] restrictions)
{
Debug.Assert(outerConnection != null, "outerConnection may not be null.");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

using Microsoft.Data.Common;
using System;
using System.Data.Common;
using System.Diagnostics;

namespace Microsoft.Data.ProviderBase
Expand All @@ -24,6 +23,7 @@ internal class TimeoutTimer
//-------------------
private long _timerExpire;
private bool _isInfiniteTimeout;
private long _originalTimerTicks;

//-------------------
// Timeout-setting methods
Expand Down Expand Up @@ -59,7 +59,8 @@ internal static TimeoutTimer StartMillisecondsTimeout(long milliseconds)
//--------------------
// Method body
var timeout = new TimeoutTimer();
timeout._timerExpire = checked(ADP.TimerCurrent() + (milliseconds * TimeSpan.TicksPerMillisecond));
timeout._originalTimerTicks = milliseconds * TimeSpan.TicksPerMillisecond;
timeout._timerExpire = checked(ADP.TimerCurrent() + timeout._originalTimerTicks);
timeout._isInfiniteTimeout = false;

//---------------------
Expand Down Expand Up @@ -88,13 +89,29 @@ internal void SetTimeoutSeconds(int seconds)
else
{
// Stash current time + timeout
_timerExpire = checked(ADP.TimerCurrent() + ADP.TimerFromSeconds(seconds));
_originalTimerTicks = ADP.TimerFromSeconds(seconds);
_timerExpire = checked(ADP.TimerCurrent() + _originalTimerTicks);
_isInfiniteTimeout = false;
}

//---------------------
// Postconditions:None
}

// Reset timer to original duration.
internal void Reset()
{
if (InfiniteTimeout == _originalTimerTicks)
{
_isInfiniteTimeout = true;
}
else
{
_timerExpire = checked(ADP.TimerCurrent() + _originalTimerTicks);
_isInfiniteTimeout = false;
}
}

//-------------------
// Timeout info properties
//-------------------
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,9 @@
<Compile Include="..\..\src\Microsoft\Data\SqlClient\ActiveDirectoryAuthenticationTimeoutRetryHelper.cs">
<Link>Microsoft\Data\SqlClient\ActiveDirectoryAuthenticationTimeoutRetryHelper.cs</Link>
</Compile>
<Compile Include="..\..\src\Microsoft\Data\SqlClient\ActiveDirectoryAuthenticationProvider.cs">
<Link>Microsoft\Data\SqlClient\ActiveDirectoryAuthenticationProvider.cs</Link>
</Compile>
<Compile Include="..\..\src\Microsoft\Data\SqlClient\Server\IBinarySerialize.cs">
<Link>Microsoft\Data\SqlClient\Server\IBinarySerialize.cs</Link>
</Compile>
Expand Down Expand Up @@ -256,7 +259,6 @@
<Compile Include="Microsoft\Data\ProviderBase\DbConnectionPool.cs" />
<Compile Include="Microsoft\Data\ProviderBase\DbConnectionPoolIdentity.cs" />
<Compile Include="Microsoft\Data\ProviderBase\DbConnectionPoolOptions.cs" />
<Compile Include="Microsoft\Data\SqlClient\ActiveDirectoryNativeAuthenticationProvider.cs" />
<Compile Include="Microsoft\Data\SqlClient\SqlAuthenticationProviderManager.cs" />
<Compile Include="Microsoft\Data\SqlClient\SqlAuthenticationToken.cs" />
<Compile Include="Microsoft\Data\SqlClient\Server\InvalidUdtException.cs" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,10 +100,13 @@ internal static string ConvertToString(object value)
private const string ApplicationIntentReadOnlyString = "ReadOnly";
const string SqlPasswordString = "Sql Password";
const string ActiveDirectoryPasswordString = "Active Directory Password";
const string ActiveDirectoryIntegratedString = "Active Directory Integrated";
const string ActiveDirectoryInteractiveString = "Active Directory Interactive";
const string ActiveDirectoryServicePrincipalString = "Active Directory Service Principal";

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

bool isSuccess = false;

Expand All @@ -119,6 +122,24 @@ internal static bool TryConvertToAuthenticationType(string value, out SqlAuthent
result = SqlAuthenticationMethod.ActiveDirectoryPassword;
isSuccess = true;
}
else if (StringComparer.InvariantCultureIgnoreCase.Equals(value, ActiveDirectoryIntegratedString)
|| StringComparer.InvariantCultureIgnoreCase.Equals(value, Convert.ToString(SqlAuthenticationMethod.ActiveDirectoryIntegrated, CultureInfo.InvariantCulture)))
{
result = SqlAuthenticationMethod.ActiveDirectoryIntegrated;
isSuccess = true;
}
else if (StringComparer.InvariantCultureIgnoreCase.Equals(value, ActiveDirectoryInteractiveString)
|| StringComparer.InvariantCultureIgnoreCase.Equals(value, Convert.ToString(SqlAuthenticationMethod.ActiveDirectoryInteractive, CultureInfo.InvariantCulture)))
{
result = SqlAuthenticationMethod.ActiveDirectoryInteractive;
isSuccess = true;
}
else if (StringComparer.InvariantCultureIgnoreCase.Equals(value, ActiveDirectoryServicePrincipalString)
|| StringComparer.InvariantCultureIgnoreCase.Equals(value, Convert.ToString(SqlAuthenticationMethod.ActiveDirectoryServicePrincipal, CultureInfo.InvariantCulture)))
{
result = SqlAuthenticationMethod.ActiveDirectoryServicePrincipal;
isSuccess = true;
}
else
{
result = DbConnectionStringDefaults.Authentication;
Expand Down Expand Up @@ -459,11 +480,12 @@ internal static ApplicationIntent ConvertToApplicationIntent(string keyword, obj

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

Expand All @@ -477,6 +499,12 @@ internal static string AuthenticationTypeToString(SqlAuthenticationMethod value)
return SqlPasswordString;
case SqlAuthenticationMethod.ActiveDirectoryPassword:
return ActiveDirectoryPasswordString;
case SqlAuthenticationMethod.ActiveDirectoryIntegrated:
return ActiveDirectoryIntegratedString;
case SqlAuthenticationMethod.ActiveDirectoryInteractive:
return ActiveDirectoryInteractiveString;
case SqlAuthenticationMethod.ActiveDirectoryServicePrincipal:
return ActiveDirectoryServicePrincipalString;
default:
return null;
}
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -15,20 +15,24 @@ internal partial class SqlAuthenticationProviderManager

static SqlAuthenticationProviderManager()
{
var activeDirectoryAuthNativeProvider = new ActiveDirectoryNativeAuthenticationProvider();
var activeDirectoryAuthProvider = new ActiveDirectoryAuthenticationProvider();
SqlAuthenticationProviderConfigurationSection configurationSection = null;

try
{
configurationSection = (SqlAuthenticationProviderConfigurationSection)ConfigurationManager.GetSection(SqlAuthenticationProviderConfigurationSection.Name);
}
catch (ConfigurationErrorsException)
catch (ConfigurationErrorsException e)
{
// Don't throw an error for invalid config files
SqlClientEventSource.Log.TraceEvent("Unable to load custom SqlAuthenticationProviders. ConfigurationManager failed to load due to configuration errors: {0}", e);
}

Instance = new SqlAuthenticationProviderManager(configurationSection);
Instance.SetProvider(SqlAuthenticationMethod.ActiveDirectoryPassword, activeDirectoryAuthNativeProvider);
Instance.SetProvider(SqlAuthenticationMethod.ActiveDirectoryIntegrated, activeDirectoryAuthProvider);
Instance.SetProvider(SqlAuthenticationMethod.ActiveDirectoryPassword, activeDirectoryAuthProvider);
Instance.SetProvider(SqlAuthenticationMethod.ActiveDirectoryInteractive, activeDirectoryAuthProvider);
Instance.SetProvider(SqlAuthenticationMethod.ActiveDirectoryServicePrincipal, activeDirectoryAuthProvider);
}

/// <summary>
Expand Down Expand Up @@ -104,8 +108,14 @@ private static SqlAuthenticationMethod AuthenticationEnumFromString(string authe
{
switch (authentication.ToLowerInvariant())
{
case ActiveDirectoryIntegrated:
return SqlAuthenticationMethod.ActiveDirectoryIntegrated;
case ActiveDirectoryPassword:
return SqlAuthenticationMethod.ActiveDirectoryPassword;
case ActiveDirectoryInteractive:
return SqlAuthenticationMethod.ActiveDirectoryInteractive;
case ActiveDirectoryServicePrincipal:
return SqlAuthenticationMethod.ActiveDirectoryServicePrincipal;
default:
throw SQL.UnsupportedAuthentication(authentication);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,12 @@ internal partial class SqlAuthenticationProviderManager
{
static SqlAuthenticationProviderManager()
{
var activeDirectoryAuthNativeProvider = new ActiveDirectoryNativeAuthenticationProvider();
var activeDirectoryAuthProvider = new ActiveDirectoryAuthenticationProvider();
Instance = new SqlAuthenticationProviderManager();
Instance.SetProvider(SqlAuthenticationMethod.ActiveDirectoryPassword, activeDirectoryAuthNativeProvider);
Instance.SetProvider(SqlAuthenticationMethod.ActiveDirectoryPassword, activeDirectoryAuthProvider);
Instance.SetProvider(SqlAuthenticationMethod.ActiveDirectoryIntegrated, activeDirectoryAuthProvider);
Instance.SetProvider(SqlAuthenticationMethod.ActiveDirectoryInteractive, activeDirectoryAuthProvider);
Instance.SetProvider(SqlAuthenticationMethod.ActiveDirectoryServicePrincipal, activeDirectoryAuthProvider);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ internal partial class SqlAuthenticationProviderManager
private const string ActiveDirectoryPassword = "active directory password";
private const string ActiveDirectoryIntegrated = "active directory integrated";
private const string ActiveDirectoryInteractive = "active directory interactive";
private const string ActiveDirectoryServicePrincipal = "active directory service principal";

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

0 comments on commit 9d27529

Please sign in to comment.