Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Additional AAD authentication options #560

Merged
merged 22 commits into from
Jun 9, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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">
David-Engel marked this conversation as resolved.
Show resolved Hide resolved
<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 @@ -253,7 +256,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