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

Fix get_ConnectionTimeout property API in SqlAuthenticationParameters #1336

Merged
merged 6 commits into from
Oct 15, 2021
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
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ public enum SqlAuthenticationMethod
SqlPassword = 1
}
/// <include file='../../../../doc/snippets/Microsoft.Data.SqlClient/SqlAuthenticationParameters.xml' path='docs/members[@name="SqlAuthenticationParameters"]/SqlAuthenticationParameters/*'/>
public partial class SqlAuthenticationParameters
public class SqlAuthenticationParameters
{
/// <include file='../../../../doc/snippets/Microsoft.Data.SqlClient/SqlAuthenticationParameters.xml' path='docs/members[@name="SqlAuthenticationParameters"]/ctor/*'/>
protected SqlAuthenticationParameters(Microsoft.Data.SqlClient.SqlAuthenticationMethod authenticationMethod, string serverName, string databaseName, string resource, string authority, string userId, string password, System.Guid connectionId, int connectionTimeout) { }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ public enum SqlAuthenticationMethod
SqlPassword = 1
}
/// <include file='../../../../doc/snippets/Microsoft.Data.SqlClient/SqlAuthenticationParameters.xml' path='docs/members[@name="SqlAuthenticationParameters"]/SqlAuthenticationParameters/*'/>
public partial class SqlAuthenticationParameters
public class SqlAuthenticationParameters
{
/// <include file='../../../../doc/snippets/Microsoft.Data.SqlClient/SqlAuthenticationParameters.xml' path='docs/members[@name="SqlAuthenticationParameters"]/ctor/*'/>
protected SqlAuthenticationParameters(Microsoft.Data.SqlClient.SqlAuthenticationMethod authenticationMethod, string serverName, string databaseName, string resource, string authority, string userId, string password, System.Guid connectionId, int connectionTimeout) { }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ public class SqlAuthenticationParameters
public string DatabaseName { get; }

/// <include file='../../../../../../doc/snippets/Microsoft.Data.SqlClient/SqlAuthenticationParameters.xml' path='docs/members[@name="SqlAuthenticationParameters"]/ConnectionTimeout/*'/>
public int ConnectionTimeout = ADP.DefaultConnectionTimeout;
public int ConnectionTimeout { get; } = ADP.DefaultConnectionTimeout;

/// <include file='../../../../../../doc/snippets/Microsoft.Data.SqlClient/SqlAuthenticationParameters.xml' path='docs/members[@name="SqlAuthenticationParameters"]/ctor/*'/>
protected SqlAuthenticationParameters(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ public static class DataTestUtility
public static string AADAccessToken = null;
public static string AADSystemIdentityAccessToken = null;
public static string AADUserIdentityAccessToken = null;
public const string ApplicationClientId = "2fd908ad-0664-4344-b9be-cd3e8b574c38";
public const string UdtTestDbName = "UdtTestDb";
public const string AKVKeyName = "TestSqlClientAzureKeyVaultProvider";
public const string EventSourcePrefix = "Microsoft.Data.SqlClient";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,57 @@
using System;
using System.Diagnostics;
using System.Security;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Identity.Client;
using Xunit;

namespace Microsoft.Data.SqlClient.ManualTesting.Tests
{
public class AADConnectionsTest
{
class CustomSqlAuthenticationProvider : SqlAuthenticationProvider
{
string _appClientId;

internal CustomSqlAuthenticationProvider(string appClientId)
{
_appClientId = appClientId;
}

public override async Task<SqlAuthenticationToken> AcquireTokenAsync(SqlAuthenticationParameters parameters)
{
string s_defaultScopeSuffix = "/.default";
string scope = parameters.Resource.EndsWith(s_defaultScopeSuffix) ? parameters.Resource : parameters.Resource + s_defaultScopeSuffix;

_ = parameters.ServerName;
_ = parameters.DatabaseName;
_ = parameters.ConnectionId;

var cts = new CancellationTokenSource();
cts.CancelAfter(parameters.ConnectionTimeout * 1000);

string[] scopes = new string[] { scope };
SecureString password = new SecureString();
foreach (char c in parameters.Password)
password.AppendChar(c);
password.MakeReadOnly();

AuthenticationResult result = await PublicClientApplicationBuilder.Create(_appClientId)
.WithAuthority(parameters.Authority)
.Build().AcquireTokenByUsernamePassword(scopes, parameters.UserId, password)
.WithCorrelationId(parameters.ConnectionId)
.ExecuteAsync(cancellationToken: cts.Token);

return new SqlAuthenticationToken(result.AccessToken, result.ExpiresOn);
}

public override bool IsSupported(SqlAuthenticationMethod authenticationMethod)
{
return authenticationMethod.Equals(SqlAuthenticationMethod.ActiveDirectoryPassword);
}
}

private static void ConnectAndDisconnect(string connectionString, SqlCredential credential = null)
{
using (SqlConnection conn = new SqlConnection(connectionString))
Expand Down Expand Up @@ -167,7 +212,6 @@ public static void AADPasswordWithWrongPassword()
Assert.Contains(expectedMessage, e.Message);
}


[ConditionalFact(nameof(IsAADConnStringsSetup))]
public static void GetAccessTokenByPasswordTest()
{
Expand All @@ -181,7 +225,7 @@ public static void GetAccessTokenByPasswordTest()
}

[ConditionalFact(nameof(IsAADConnStringsSetup))]
public static void testADPasswordAuthentication()
public static void TestADPasswordAuthentication()
{
// Connect to Azure DB with password and retrieve user name.
using (SqlConnection conn = new SqlConnection(DataTestUtility.AADPasswordConnectionString))
Expand All @@ -201,6 +245,30 @@ public static void testADPasswordAuthentication()
}
}

[ConditionalFact(nameof(IsAADConnStringsSetup))]
public static void TestCustomProviderAuthentication()
{
SqlAuthenticationProvider.SetProvider(SqlAuthenticationMethod.ActiveDirectoryPassword, new CustomSqlAuthenticationProvider(DataTestUtility.ApplicationClientId));
// Connect to Azure DB with password and retrieve user name using custom authentication provider
using (SqlConnection conn = new SqlConnection(DataTestUtility.AADPasswordConnectionString))
{
conn.Open();
using (SqlCommand sqlCommand = new SqlCommand
(
cmdText: $"SELECT SUSER_SNAME();",
connection: conn,
transaction: null
))
{
string customerId = (string)sqlCommand.ExecuteScalar();
string expected = DataTestUtility.RetrieveValueFromConnStr(DataTestUtility.AADPasswordConnectionString, new string[] { "User ID", "UID" });
Assert.Equal(expected, customerId);
}
}
// Reset to driver internal provider.
SqlAuthenticationProvider.SetProvider(SqlAuthenticationMethod.ActiveDirectoryPassword, new ActiveDirectoryAuthenticationProvider(DataTestUtility.ApplicationClientId));
}

[ConditionalFact(nameof(IsAADConnStringsSetup))]
public static void ActiveDirectoryPasswordWithNoAuthType()
{
Expand Down Expand Up @@ -269,7 +337,7 @@ public static void EmptyCredInConnStrAADPasswordAnyUnix()
string[] removeKeys = { "User ID", "Password", "UID", "PWD" };
string connStr = DataTestUtility.RemoveKeysInConnStr(DataTestUtility.AADPasswordConnectionString, removeKeys) + "User ID=; Password=;";
SqlException e = Assert.Throws<SqlException>(() => ConnectAndDisconnect(connStr));

string expectedMessage = "MSAL cannot determine the username (UPN) of the currently logged in user.For Integrated Windows Authentication and Username/Password flows, please use .WithUsername() before calling ExecuteAsync().";
Assert.Contains(expectedMessage, e.Message);
}
Expand Down Expand Up @@ -504,13 +572,13 @@ public static void ADInteractiveUsingSSPI()
public static void ConnectionSpeed()
{
var connString = DataTestUtility.AADPasswordConnectionString;

//Ensure server endpoints are warm
using (var connectionDrill = new SqlConnection(connString))
{
connectionDrill.Open();
}

SqlConnection.ClearAllPools();
ActiveDirectoryAuthenticationProvider.ClearUserTokenCache();

Expand All @@ -529,7 +597,7 @@ public static void ConnectionSpeed()
secondConnectionTime.Stop();
}
}

// Subsequent AAD connections within a short timeframe should use an auth token cached from the connection pool
// Second connection speed in tests was typically 10-15% of the first connection time. Using 30% since speeds may vary.
Assert.True(((double)secondConnectionTime.ElapsedMilliseconds / firstConnectionTime.ElapsedMilliseconds) < 0.30, $"Second AAD connection too slow ({secondConnectionTime.ElapsedMilliseconds}ms)! (More than 30% of the first ({firstConnectionTime.ElapsedMilliseconds}ms).)");
Expand Down