-
Notifications
You must be signed in to change notification settings - Fork 286
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
Access Token Support for SqlConnection in Manual Tests #2654
Access Token Support for SqlConnection in Manual Tests #2654
Conversation
This seems like it would make it difficult to change test connection strings due to validation on the AccessToken property. What problem is this PR solving? |
Since assigning a UAMI directly to an agent pool is not recommended + our Mac agent pool, we are using a service connection in ADO and have federated credentials to it on a UAMI which has access to the database. As a result, we have an Azure Cli step in ADO to retrieve the token and assign it to the connection to authenticate. |
…where NRE comes from.
….com/dauinsight/SqlClient into dev/daniel/azure-assign-access-token
….com/dauinsight/SqlClient into dev/daniel/azure-assign-access-token
….com/dauinsight/SqlClient into dev/daniel/azure-assign-access-token
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good to me.
using (SqlConnection con = (SqlConnection) provider.CreateConnection()) | ||
{ | ||
// Testing in macOS Azure SQL container requires access token authentication | ||
if (!DataTestUtility.AuthenticatingWithoutAccessToken) | ||
{ | ||
con.AccessToken = DataTestUtility.AADAccessToken; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you introduce an extension method your change will be simpler without repeating yourself.
using (SqlConnection con = (SqlConnection) provider.CreateConnection()) | |
{ | |
// Testing in macOS Azure SQL container requires access token authentication | |
if (!DataTestUtility.AuthenticatingWithoutAccessToken) | |
{ | |
con.AccessToken = DataTestUtility.AADAccessToken; | |
} | |
using (DbConnection con = provider.CreateConnectionEx()) |
static DbConnection CreateConnectionEx(this DbProviderFactory provider)
{
DbConnection conn = provider.CreateConnection();
// Testing in macOS Azure SQL container requires access token authentication
if (!DataTestUtility.AuthenticatingWithoutAccessToken && conn is SqlConnection sqlConn)
{
sqlConn.AccessToken = DataTestUtility.AADAccessToken;
return sqlConn;
}
return conn;
}
public static bool AuthenticatingWithoutAccessToken | ||
{ | ||
get | ||
{ | ||
return !RuntimeInformation.IsOSPlatform(OSPlatform.OSX); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This makes more sense with positive naming. Feel free to modify the naming as you like.
public static bool AuthenticatingWithoutAccessToken | |
{ | |
get | |
{ | |
return !RuntimeInformation.IsOSPlatform(OSPlatform.OSX); | |
public static bool ForceAuthenticationWithAccessToken | |
{ | |
get | |
{ | |
return RuntimeInformation.IsOSPlatform(OSPlatform.OSX); |
@@ -186,7 +186,8 @@ public void ExecuteReaderWithCommandBehaviorTest() | |||
} | |||
|
|||
// Synapse: Parse error at line: 1, column: 27: Incorrect syntax near 'for'. | |||
[ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsNotAzureSynapse))] | |||
// Skip: Access token authentication does not work with RemoteExecutor (#31362) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same thing is applied for the similar comments.
// Skip: Access token authentication does not work with RemoteExecutor (#31362) | |
// Skip: Access token authentication does not work with RemoteExecutor (active issue #31362) |
{ | ||
Console.WriteLine($"Generating new access token..."); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Console.WriteLine($"Generating new access token..."); |
Console.WriteLine($"TCPConnectionString = {DataTestUtility.TCPConnectionString}"); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Console.WriteLine($"TCPConnectionString = {DataTestUtility.TCPConnectionString}"); |
{ | ||
string currentDb = connection.Database; | ||
SqlConnectionStringBuilder builder = new SqlConnectionStringBuilder(connection.ConnectionString); | ||
SqlConnectionStringBuilder builder = new SqlConnectionStringBuilder(DataTestUtility.TCPConnectionString); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe the point of this test is building and reading the connection string properties from a SqlConnection.ConnectionString
. So, instead of skipping these kind of tests you can force the using of the DataTestUtility.TCPConnectionString
through the helper method by adding a new argument and bypassing the internal logic.
SqlConnectionStringBuilder builder = new SqlConnectionStringBuilder(DataTestUtility.TCPConnectionString); | |
SqlConnectionStringBuilder builder = new SqlConnectionStringBuilder(connection.ConnectionString); |
{ | ||
// Testing in macOS Azure SQL container requires access token authentication | ||
if (!DataTestUtility.AuthenticatingWithoutAccessToken) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can add another property in DataTestUtility
and keep it positive, instead of multiplying a negative property by adding a negative sign, which is a bit tricky to follow.
// Skip: We cannot assign an access token to the connection in this test (#31362) | ||
[ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.AuthenticatingWithoutAccessToken))] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It doesn't make sense to me that authentication could affect this test. Could you double-check it?
@@ -53,7 +53,7 @@ public static void LocalDBMarsTest() | |||
[ConditionalFact(nameof(IsLocalDBEnvironmentSet))] | |||
public static void InvalidLocalDBTest() | |||
{ | |||
using var connection = new SqlConnection(s_badConnectionString); | |||
using var connection = DataTestUtility.GetSqlConnection(s_badConnectionString); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LocalDb doesn't require Access Token for authentication.
using var connection = DataTestUtility.GetSqlConnection(s_badConnectionString); | |
using var connection = new SqlConnection(s_badConnectionString); |
@@ -189,7 +189,7 @@ private static void ConnectionTest(string connectionString) | |||
|
|||
private static void OpenConnection(string connString) | |||
{ | |||
using SqlConnection connection = new(connString); | |||
using SqlConnection connection = DataTestUtility.GetSqlConnection(connString); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
using SqlConnection connection = DataTestUtility.GetSqlConnection(connString); | |
using SqlConnection connection = new(connString); |
@@ -16,7 +16,7 @@ public class KerberosTests | |||
public void IsKerBerosSetupTestAsync(string connectionStr) | |||
{ | |||
KerberosTicketManagemnt.Init(DataTestUtility.KerberosDomainUser, DataTestUtility.KerberosDomainPassword); | |||
using SqlConnection conn = new(connectionStr); | |||
using SqlConnection conn = DataTestUtility.GetSqlConnection(connectionStr); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This change shouldn't be required.
using SqlConnection conn = DataTestUtility.GetSqlConnection(connectionStr); | |
using SqlConnection conn = new(connectionStr); |
@@ -58,7 +58,7 @@ public static void IntegratedAuthenticationTest_ServerSPN() | |||
|
|||
private static void TryOpenConnectionWithIntegratedAuthentication(string connectionString) | |||
{ | |||
using (SqlConnection connection = new SqlConnection(connectionString)) | |||
using (SqlConnection connection = DataTestUtility.GetSqlConnection(connectionString)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Integrated auth doesn't required access token.
using (SqlConnection connection = DataTestUtility.GetSqlConnection(connectionString)) | |
using (SqlConnection connection = new SqlConnection(connectionString)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This class skips Azure and could be reverted.
@@ -43,7 +43,8 @@ public class TvpTest | |||
private readonly string _connStr; | |||
|
|||
// Synapse: The statement failed. Column 'blob' has a data type that cannot participate in a columnstore index. | |||
[ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsNotAzureSynapse))] | |||
// TODO: Investigate access token authentication to Azure SQL DB (#31362) | |||
[ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsNotAzureSynapse), nameof(DataTestUtility.AuthenticatingWithoutAccessToken))] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you revert all the TVPTest
related changes under the ParameterTest
folder since they've already skipped on Mac?
Can we simply use Authentication=ActiveDirectoryDefault for the connection string on the mac jobs and take advantage of the fact that AzureCliCredential is part of the credential chain? I feel like requiring all tests to wrap their connection strings in DataTestUtility.GetSqlConnection is adding unnecessary complexity to test configuration. If AD Default doesn't work, another alternative that would be much simpler/cleaner is to create a custom authentication provider that overrode the ActiveDirectoryMSI option to return the access token configured on the job. |
Codecov ReportAll modified and coverable lines are covered by tests ✅
Additional details and impacted files@@ Coverage Diff @@
## main #2654 +/- ##
==========================================
- Coverage 72.81% 71.63% -1.19%
==========================================
Files 311 308 -3
Lines 61694 61532 -162
==========================================
- Hits 44922 44077 -845
- Misses 16772 17455 +683
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. |
@David-Engel I agree that this adds a little bit of complexity, but i haven't found another way to use our service connection in ADO to grant MI to the agent using an access token. ActiveDirectoryDefault works locally if we login to our machine via For more context into the implementation of this PR: https://msazure.visualstudio.com/One/_wiki/wikis/Microsoft%20Graph%20Engineering/675144/SFI-MI?anchor=mi-for-e2es-in-dev-environment-and-azure-devops(ado) |
After reading that, in addition to the custom provider option I mentioned, prioritizing #2557 could also provide a better solution that could be used. As it is, I would NOT merge this PR. Dynamically modifying the connection string like that across all tests is just asking for maintenance headaches and added cost. Additionally, I noticed the AZURE_DB_SP_TCP_CONN_STRING was removed from the test config variable group. That could have been used for macOS, too. We have an exception for that for continuing to test AD SP auth. |
I strongly agree with @David-Engel |
Okay that makes sense, for now i'll look to have the service principal set up again. |
In our manual tests we are changing the way we authenticate to Azure SQL DB by using managed identity.
In order to support this, when we make a connection from a macOS container, we must assign an access token to the connection. This authorizes the instance with access to the managed identity and hence the DB.
A few tests have been skipped (when running macOS) as we're unable to assign the access token to the connection from the test.