From e11712e97ef924e3cd8594de7384345b6f7060a1 Mon Sep 17 00:00:00 2001 From: v-arellegue Date: Thu, 14 Dec 2023 17:50:34 -0800 Subject: [PATCH 01/15] Fixed token refreshing logic and added unit test. --- .../SqlClient/SqlInternalConnectionTds.cs | 7 + .../SqlClient/SqlInternalConnectionTds.cs | 7 + ....Data.SqlClient.ManualTesting.Tests.csproj | 34 ++--- .../AADFedAuthTokenRefreshTest.cs | 140 ++++++++++++++++++ 4 files changed, 166 insertions(+), 22 deletions(-) create mode 100644 src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/AADFedAuthTokenRefreshTest/AADFedAuthTokenRefreshTest.cs diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs index 6a0ee2e0e0..601957a7f0 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs @@ -2254,6 +2254,13 @@ internal void OnFedAuthInfo(SqlFedAuthInfo fedAuthInfo) { // GetFedAuthToken should have updated _newDbConnectionPoolAuthenticationContext. Debug.Assert(_newDbConnectionPoolAuthenticationContext != null, "_newDbConnectionPoolAuthenticationContext should not be null."); + + if (_newDbConnectionPoolAuthenticationContext != null) + { + // Try adding this new _newDbConnectionPoolAuthenticationContext to the _dbConnectionPool's AuthenticationContextKeys if it is not in there yet. + // The DbConnectionPoolAuthenticationContextKeys collection is used to refresh a cached token just before it expires within 10 minutes. + _dbConnectionPool.AuthenticationContexts.TryAdd(new DbConnectionPoolAuthenticationContextKey(fedAuthInfo.stsurl, fedAuthInfo.spn), _newDbConnectionPoolAuthenticationContext); + } } } else if (!attemptRefreshTokenLocked) diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs index 068b37dc71..e54ed8515e 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs @@ -2680,6 +2680,13 @@ internal void OnFedAuthInfo(SqlFedAuthInfo fedAuthInfo) { // GetFedAuthToken should have updated _newDbConnectionPoolAuthenticationContext. Debug.Assert(_newDbConnectionPoolAuthenticationContext != null, "_newDbConnectionPoolAuthenticationContext should not be null."); + + if (_newDbConnectionPoolAuthenticationContext != null) + { + // Try adding this new _newDbConnectionPoolAuthenticationContext to the _dbConnectionPool's AuthenticationContextKeys if it is not in there yet. + // The DbConnectionPoolAuthenticationContextKeys collection is used to refresh a cached token just before it expires within 10 minutes. + _dbConnectionPool.AuthenticationContexts.TryAdd(new DbConnectionPoolAuthenticationContextKey(fedAuthInfo.stsurl, fedAuthInfo.spn), _newDbConnectionPoolAuthenticationContext); + } } } else if (!attemptRefreshTokenLocked) diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/Microsoft.Data.SqlClient.ManualTesting.Tests.csproj b/src/Microsoft.Data.SqlClient/tests/ManualTests/Microsoft.Data.SqlClient.ManualTesting.Tests.csproj index 407dd9af1a..060b996780 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/Microsoft.Data.SqlClient.ManualTesting.Tests.csproj +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/Microsoft.Data.SqlClient.ManualTesting.Tests.csproj @@ -170,6 +170,7 @@ + @@ -267,6 +268,7 @@ + @@ -298,30 +300,19 @@ - - - - - - - + + + + + + + - + - + runtime; build; native; contentfiles; analyzers; buildtransitive all @@ -334,8 +325,7 @@ all - + diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/AADFedAuthTokenRefreshTest/AADFedAuthTokenRefreshTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/AADFedAuthTokenRefreshTest/AADFedAuthTokenRefreshTest.cs new file mode 100644 index 0000000000..e6e912077f --- /dev/null +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/AADFedAuthTokenRefreshTest/AADFedAuthTokenRefreshTest.cs @@ -0,0 +1,140 @@ +using System; +using System.Collections; +using System.Linq; +using System.Reflection; +using System.Security.Cryptography; +using System.Text; +using Xunit; + +namespace Microsoft.Data.SqlClient.ManualTesting.Tests +{ + public class AADFedAuthTokenRefreshTest + { + [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.IsAADPasswordConnStrSetup))] + public void FedAuthTokenRefreshTest() + { + SqlConnectionStringBuilder builder = new(DataTestUtility.AADPasswordConnectionString); + string dataSourceStr = builder.DataSource; + // Clean up tcp info as it will always be added later on and we don't want to duplicate if it is there already. + dataSourceStr = dataSourceStr.Replace("tcp:", ""); + dataSourceStr = dataSourceStr.Replace(",1433", ""); + dataSourceStr = dataSourceStr.Replace(", 1433", ""); + + // set user id and password from AADPasswordConnectionString + string user = builder.UserID; + string password = builder.Password; + + // Set Environment variables used for ActiveDirectoryDefault authentication type + Environment.SetEnvironmentVariable("AZURE_USERNAME", $"{user}"); + Environment.SetEnvironmentVariable("AZURE_PASSWORD", $"{password}"); + + string userEnvVar = Environment.GetEnvironmentVariable("AZURE_USERNAME"); + string passwordEnvVar = Environment.GetEnvironmentVariable("AZURE_PASSWORD"); + Assert.True($"{user}" == userEnvVar, @"AZURE_USERNAME environment variable must be set"); + Assert.True($"{password}" == passwordEnvVar, @"AZURE_PASSWORD environment variable must be set"); + + // This is the format of connection string that works + string connStr = $"Server=tcp:{dataSourceStr},1433;Persist Security Info=False;User ID={user};MultipleActiveResultSets=False;Encrypt=True;TrustServerCertificate=False;Authentication=ActiveDirectoryDefault;Timeout=90"; + + using var connection = new SqlConnection(connStr); + connection.Open(); + + // Set the token expiry to expire in 1 minute from now to force token refresh + string tokenHash1 = ""; + DateTime? oldExpiry = GetOrSetTokenExpiryDateTime(connection, true, out tokenHash1); + Assert.True(oldExpiry != null, "Failed to make token expiry to expire in one minute."); + + // Display old expiry in local time which should be in 1 minutes from now + DateTime oldLocalTime = TimeZoneInfo.ConvertTimeFromUtc((DateTime)oldExpiry, TimeZoneInfo.Local); + Console.WriteLine($"Token: {tokenHash1} Old Expiry: {oldLocalTime}"); + TimeSpan diff = oldLocalTime - DateTime.Now; + Assert.True(diff.TotalSeconds <= 60, "Failed to set expiry after 1 minute from current time."); + + // Check if connection is alive + string result = ""; + var cmd = connection.CreateCommand(); + cmd.CommandText = "select @@version"; + result = $"{cmd.ExecuteScalar()}"; + Assert.True(result != string.Empty, "The connection's command must return a value"); + + // The new connection will use the same FedAuthToken but will refresh first + using var connection2 = new SqlConnection(connStr); + connection2.Open(); + + // Check again if connection is alive + cmd = connection2.CreateCommand(); + cmd.CommandText = "select 1"; + result = $"{cmd.ExecuteScalar()}"; + Assert.True(result != string.Empty, "The connection's command must return a value after a token refresh."); + + // Get the refreshed token expiry + string tokenHash2 = ""; + DateTime? newExpiry = GetOrSetTokenExpiryDateTime(connection2, false, out tokenHash2); + // Display new expiry in local time + DateTime newLocalTime = TimeZoneInfo.ConvertTimeFromUtc((DateTime)newExpiry, TimeZoneInfo.Local); + Console.WriteLine($"Token: {tokenHash2} New Expiry: {newLocalTime}"); + + Assert.True(tokenHash1 == tokenHash2, "The FedAuthToken failed to refresh correctly."); + Assert.True(newLocalTime > oldLocalTime, "The FedAuthToken failed to refresh correctly."); + + connection.Close(); + connection2.Close(); + } + + private DateTime? GetOrSetTokenExpiryDateTime(SqlConnection connection, bool setExpiry, out string tokenHash) + { + try + { + // Get the inner connection + object innerConnectionObj = connection.GetType().GetProperty("InnerConnection", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(connection); + + // Get the db connection pool + object poolObj = innerConnectionObj.GetType().GetProperty("Pool", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(innerConnectionObj); + + // Get the Authentication Contexts + IEnumerable authContextCollection = (IEnumerable)poolObj.GetType().GetProperty("AuthenticationContexts", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(poolObj, null); + + // Get the first authentication context + object authContextObj = authContextCollection.Cast().FirstOrDefault(); + + // Get the token object from the authentication context + object tokenObj = authContextObj.GetType().GetProperty("Value").GetValue(authContextObj, null); + + DateTime expiry = (DateTime)tokenObj.GetType().GetProperty("ExpirationTime", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(tokenObj, null); + + if (setExpiry) + { + // Token refresh trigger is within 10 minutes. So, 1 minute expiry should trigger token refresh. + expiry = DateTime.UtcNow.AddMinutes(1); + + // Apply the expiry to the token object + FieldInfo expirationTime = tokenObj.GetType().GetField("_expirationTime", BindingFlags.NonPublic | BindingFlags.Instance); + expirationTime.SetValue(tokenObj, expiry); + + } + + byte[] tokenBytes = (byte[])tokenObj.GetType().GetProperty("AccessToken", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(tokenObj, null); + + tokenHash = GetTokenHash(tokenBytes); + + return expiry; + } + catch (Exception) + { + tokenHash = ""; + return null; + } + } + + private string GetTokenHash(byte[] tokenBytes) + { + string token = Encoding.Unicode.GetString(tokenBytes); + var bytesInUtf8 = Encoding.UTF8.GetBytes(token); + using (var sha256 = SHA256.Create()) + { + var hash = sha256.ComputeHash(bytesInUtf8); + return Convert.ToBase64String(hash); + } + } + } +} From d474331abea9fef69f36024195514ffa104c37fb Mon Sep 17 00:00:00 2001 From: v-arellegue Date: Fri, 15 Dec 2023 13:26:07 -0800 Subject: [PATCH 02/15] Use DataTestUtility.AADPasswordConnectionString. --- .../AADFedAuthTokenRefreshTest.cs | 53 +++++++++---------- 1 file changed, 26 insertions(+), 27 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/AADFedAuthTokenRefreshTest/AADFedAuthTokenRefreshTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/AADFedAuthTokenRefreshTest/AADFedAuthTokenRefreshTest.cs index e6e912077f..f0cc5f3971 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/AADFedAuthTokenRefreshTest/AADFedAuthTokenRefreshTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/AADFedAuthTokenRefreshTest/AADFedAuthTokenRefreshTest.cs @@ -13,30 +13,30 @@ public class AADFedAuthTokenRefreshTest [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.IsAADPasswordConnStrSetup))] public void FedAuthTokenRefreshTest() { - SqlConnectionStringBuilder builder = new(DataTestUtility.AADPasswordConnectionString); - string dataSourceStr = builder.DataSource; - // Clean up tcp info as it will always be added later on and we don't want to duplicate if it is there already. - dataSourceStr = dataSourceStr.Replace("tcp:", ""); - dataSourceStr = dataSourceStr.Replace(",1433", ""); - dataSourceStr = dataSourceStr.Replace(", 1433", ""); + // for local testing in PC + //SqlConnectionStringBuilder builder = new(DataTestUtility.AADPasswordConnectionString); + //string dataSourceStr = builder.DataSource; - // set user id and password from AADPasswordConnectionString - string user = builder.UserID; - string password = builder.Password; + //// set user id and password from AADPasswordConnectionString + //string user = builder.UserID; + //string password = builder.Password; - // Set Environment variables used for ActiveDirectoryDefault authentication type - Environment.SetEnvironmentVariable("AZURE_USERNAME", $"{user}"); - Environment.SetEnvironmentVariable("AZURE_PASSWORD", $"{password}"); + //// Set Environment variables used for ActiveDirectoryDefault authentication type + //Environment.SetEnvironmentVariable("AZURE_USERNAME", $"{user}"); + //Environment.SetEnvironmentVariable("AZURE_PASSWORD", $"{password}"); - string userEnvVar = Environment.GetEnvironmentVariable("AZURE_USERNAME"); - string passwordEnvVar = Environment.GetEnvironmentVariable("AZURE_PASSWORD"); - Assert.True($"{user}" == userEnvVar, @"AZURE_USERNAME environment variable must be set"); - Assert.True($"{password}" == passwordEnvVar, @"AZURE_PASSWORD environment variable must be set"); + //string userEnvVar = Environment.GetEnvironmentVariable("AZURE_USERNAME"); + //string passwordEnvVar = Environment.GetEnvironmentVariable("AZURE_PASSWORD"); + //Assert.True($"{user}" == userEnvVar, @"AZURE_USERNAME environment variable must be set"); + //Assert.True($"{password}" == passwordEnvVar, @"AZURE_PASSWORD environment variable must be set"); - // This is the format of connection string that works - string connStr = $"Server=tcp:{dataSourceStr},1433;Persist Security Info=False;User ID={user};MultipleActiveResultSets=False;Encrypt=True;TrustServerCertificate=False;Authentication=ActiveDirectoryDefault;Timeout=90"; + //// This is the format of connection string that works + //string connStr = $"Server={dataSourceStr};Persist Security Info=False;User ID={user};MultipleActiveResultSets=False;Encrypt=True;TrustServerCertificate=False;Authentication=ActiveDirectoryDefault;Timeout=90"; + string connStr = DataTestUtility.AADPasswordConnectionString; + //using var connection = new SqlConnection(connStr); using var connection = new SqlConnection(connStr); + connection.Open(); // Set the token expiry to expire in 1 minute from now to force token refresh @@ -44,11 +44,11 @@ public void FedAuthTokenRefreshTest() DateTime? oldExpiry = GetOrSetTokenExpiryDateTime(connection, true, out tokenHash1); Assert.True(oldExpiry != null, "Failed to make token expiry to expire in one minute."); - // Display old expiry in local time which should be in 1 minutes from now - DateTime oldLocalTime = TimeZoneInfo.ConvertTimeFromUtc((DateTime)oldExpiry, TimeZoneInfo.Local); - Console.WriteLine($"Token: {tokenHash1} Old Expiry: {oldLocalTime}"); - TimeSpan diff = oldLocalTime - DateTime.Now; - Assert.True(diff.TotalSeconds <= 60, "Failed to set expiry after 1 minute from current time."); + // Display old expiry in local time which should be in 1 minute from now + DateTime oldLocalExpiryTime = TimeZoneInfo.ConvertTimeFromUtc((DateTime)oldExpiry, TimeZoneInfo.Local); + Console.WriteLine($"Token: {tokenHash1} Old Expiry: {oldLocalExpiryTime}"); + TimeSpan timeDiff = oldLocalExpiryTime - DateTime.Now; + Assert.True(timeDiff.TotalSeconds <= 60, "Failed to set expiry after 1 minute from current time."); // Check if connection is alive string result = ""; @@ -57,7 +57,7 @@ public void FedAuthTokenRefreshTest() result = $"{cmd.ExecuteScalar()}"; Assert.True(result != string.Empty, "The connection's command must return a value"); - // The new connection will use the same FedAuthToken but will refresh first + // The new connection will use the same FedAuthToken but will or supposed to refresh first using var connection2 = new SqlConnection(connStr); connection2.Open(); @@ -75,7 +75,7 @@ public void FedAuthTokenRefreshTest() Console.WriteLine($"Token: {tokenHash2} New Expiry: {newLocalTime}"); Assert.True(tokenHash1 == tokenHash2, "The FedAuthToken failed to refresh correctly."); - Assert.True(newLocalTime > oldLocalTime, "The FedAuthToken failed to refresh correctly."); + Assert.True(newLocalTime > oldLocalExpiryTime, "The FedAuthToken failed to refresh correctly."); connection.Close(); connection2.Close(); @@ -104,13 +104,12 @@ public void FedAuthTokenRefreshTest() if (setExpiry) { - // Token refresh trigger is within 10 minutes. So, 1 minute expiry should trigger token refresh. + // Forcing 1 minute expiry to trigger token refresh. expiry = DateTime.UtcNow.AddMinutes(1); // Apply the expiry to the token object FieldInfo expirationTime = tokenObj.GetType().GetField("_expirationTime", BindingFlags.NonPublic | BindingFlags.Instance); expirationTime.SetValue(tokenObj, expiry); - } byte[] tokenBytes = (byte[])tokenObj.GetType().GetProperty("AccessToken", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(tokenObj, null); From 7dcd060c0e577a26d1a6824e421e3c0da4636157 Mon Sep 17 00:00:00 2001 From: v-arellegue Date: Fri, 15 Dec 2023 14:20:52 -0800 Subject: [PATCH 03/15] Added ITestOutputHelper so can see some logs in pipeline. --- .../AADFedAuthTokenRefreshTest.cs | 26 +++++++++++++------ 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/AADFedAuthTokenRefreshTest/AADFedAuthTokenRefreshTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/AADFedAuthTokenRefreshTest/AADFedAuthTokenRefreshTest.cs index f0cc5f3971..8657ef6378 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/AADFedAuthTokenRefreshTest/AADFedAuthTokenRefreshTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/AADFedAuthTokenRefreshTest/AADFedAuthTokenRefreshTest.cs @@ -5,15 +5,23 @@ using System.Security.Cryptography; using System.Text; using Xunit; +using Xunit.Abstractions; namespace Microsoft.Data.SqlClient.ManualTesting.Tests { public class AADFedAuthTokenRefreshTest { + private readonly ITestOutputHelper _output; + + public AADFedAuthTokenRefreshTest(ITestOutputHelper output) + { + _output = output; + } + [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.IsAADPasswordConnStrSetup))] - public void FedAuthTokenRefreshTest() + public void FedAuthTokenRefreshTest(ITestOutputHelper output) { - // for local testing in PC + // ------------------ Use settings below for local environment testing in your PC ------------------------ //SqlConnectionStringBuilder builder = new(DataTestUtility.AADPasswordConnectionString); //string dataSourceStr = builder.DataSource; @@ -32,11 +40,13 @@ public void FedAuthTokenRefreshTest() //// This is the format of connection string that works //string connStr = $"Server={dataSourceStr};Persist Security Info=False;User ID={user};MultipleActiveResultSets=False;Encrypt=True;TrustServerCertificate=False;Authentication=ActiveDirectoryDefault;Timeout=90"; - string connStr = DataTestUtility.AADPasswordConnectionString; + + // ------------------ End of local environment settings ---------------------------------------------------- - //using var connection = new SqlConnection(connStr); - using var connection = new SqlConnection(connStr); + string connStr = DataTestUtility.AADPasswordConnectionString; + // Create a new connection object and open it + SqlConnection connection = new SqlConnection(connStr); connection.Open(); // Set the token expiry to expire in 1 minute from now to force token refresh @@ -46,7 +56,7 @@ public void FedAuthTokenRefreshTest() // Display old expiry in local time which should be in 1 minute from now DateTime oldLocalExpiryTime = TimeZoneInfo.ConvertTimeFromUtc((DateTime)oldExpiry, TimeZoneInfo.Local); - Console.WriteLine($"Token: {tokenHash1} Old Expiry: {oldLocalExpiryTime}"); + _output.WriteLine($"Token: {tokenHash1} Old Expiry: {oldLocalExpiryTime}"); TimeSpan timeDiff = oldLocalExpiryTime - DateTime.Now; Assert.True(timeDiff.TotalSeconds <= 60, "Failed to set expiry after 1 minute from current time."); @@ -58,7 +68,7 @@ public void FedAuthTokenRefreshTest() Assert.True(result != string.Empty, "The connection's command must return a value"); // The new connection will use the same FedAuthToken but will or supposed to refresh first - using var connection2 = new SqlConnection(connStr); + SqlConnection connection2 = new SqlConnection(connStr); connection2.Open(); // Check again if connection is alive @@ -72,7 +82,7 @@ public void FedAuthTokenRefreshTest() DateTime? newExpiry = GetOrSetTokenExpiryDateTime(connection2, false, out tokenHash2); // Display new expiry in local time DateTime newLocalTime = TimeZoneInfo.ConvertTimeFromUtc((DateTime)newExpiry, TimeZoneInfo.Local); - Console.WriteLine($"Token: {tokenHash2} New Expiry: {newLocalTime}"); + _output.WriteLine($"Token: {tokenHash2} New Expiry: {newLocalTime}"); Assert.True(tokenHash1 == tokenHash2, "The FedAuthToken failed to refresh correctly."); Assert.True(newLocalTime > oldLocalExpiryTime, "The FedAuthToken failed to refresh correctly."); From 2e7a22493726afd986e1542202da8659c2a21092 Mon Sep 17 00:00:00 2001 From: v-arellegue Date: Fri, 15 Dec 2023 14:58:34 -0800 Subject: [PATCH 04/15] Use SqlCommand type instead of var. --- .../AADFedAuthTokenRefreshTest.cs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/AADFedAuthTokenRefreshTest/AADFedAuthTokenRefreshTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/AADFedAuthTokenRefreshTest/AADFedAuthTokenRefreshTest.cs index 8657ef6378..dd6964ce02 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/AADFedAuthTokenRefreshTest/AADFedAuthTokenRefreshTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/AADFedAuthTokenRefreshTest/AADFedAuthTokenRefreshTest.cs @@ -38,11 +38,12 @@ public void FedAuthTokenRefreshTest(ITestOutputHelper output) //Assert.True($"{user}" == userEnvVar, @"AZURE_USERNAME environment variable must be set"); //Assert.True($"{password}" == passwordEnvVar, @"AZURE_PASSWORD environment variable must be set"); - //// This is the format of connection string that works + //// This is the format of connection string that works for me //string connStr = $"Server={dataSourceStr};Persist Security Info=False;User ID={user};MultipleActiveResultSets=False;Encrypt=True;TrustServerCertificate=False;Authentication=ActiveDirectoryDefault;Timeout=90"; // ------------------ End of local environment settings ---------------------------------------------------- + // Use this connection string when running in a pipeline string connStr = DataTestUtility.AADPasswordConnectionString; // Create a new connection object and open it @@ -56,13 +57,13 @@ public void FedAuthTokenRefreshTest(ITestOutputHelper output) // Display old expiry in local time which should be in 1 minute from now DateTime oldLocalExpiryTime = TimeZoneInfo.ConvertTimeFromUtc((DateTime)oldExpiry, TimeZoneInfo.Local); - _output.WriteLine($"Token: {tokenHash1} Old Expiry: {oldLocalExpiryTime}"); + _output?.WriteLine($"Token: {tokenHash1} Old Expiry: {oldLocalExpiryTime}"); TimeSpan timeDiff = oldLocalExpiryTime - DateTime.Now; Assert.True(timeDiff.TotalSeconds <= 60, "Failed to set expiry after 1 minute from current time."); // Check if connection is alive string result = ""; - var cmd = connection.CreateCommand(); + SqlCommand cmd = connection.CreateCommand(); cmd.CommandText = "select @@version"; result = $"{cmd.ExecuteScalar()}"; Assert.True(result != string.Empty, "The connection's command must return a value"); @@ -82,10 +83,10 @@ public void FedAuthTokenRefreshTest(ITestOutputHelper output) DateTime? newExpiry = GetOrSetTokenExpiryDateTime(connection2, false, out tokenHash2); // Display new expiry in local time DateTime newLocalTime = TimeZoneInfo.ConvertTimeFromUtc((DateTime)newExpiry, TimeZoneInfo.Local); - _output.WriteLine($"Token: {tokenHash2} New Expiry: {newLocalTime}"); + _output?.WriteLine($"Token: {tokenHash2} New Expiry: {newLocalTime}"); - Assert.True(tokenHash1 == tokenHash2, "The FedAuthToken failed to refresh correctly."); - Assert.True(newLocalTime > oldLocalExpiryTime, "The FedAuthToken failed to refresh correctly."); + Assert.True(tokenHash1 == tokenHash2, "The token's hash before and after token refresh must be identical."); + Assert.True(newLocalTime > oldLocalExpiryTime, "The refreshed token must have a later expiry time."); connection.Close(); connection2.Close(); From 7756df08b34843d62d760385b0778cbcbf0ae200 Mon Sep 17 00:00:00 2001 From: v-arellegue Date: Fri, 15 Dec 2023 15:19:22 -0800 Subject: [PATCH 05/15] Add a wrapper for _output, LogInfo, since ITestOutputHelper can not be executed directly inside a function with [ConditionalFact] annotation. --- .../AADFedAuthTokenRefreshTest.cs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/AADFedAuthTokenRefreshTest/AADFedAuthTokenRefreshTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/AADFedAuthTokenRefreshTest/AADFedAuthTokenRefreshTest.cs index dd6964ce02..cb8791343a 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/AADFedAuthTokenRefreshTest/AADFedAuthTokenRefreshTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/AADFedAuthTokenRefreshTest/AADFedAuthTokenRefreshTest.cs @@ -57,7 +57,7 @@ public void FedAuthTokenRefreshTest(ITestOutputHelper output) // Display old expiry in local time which should be in 1 minute from now DateTime oldLocalExpiryTime = TimeZoneInfo.ConvertTimeFromUtc((DateTime)oldExpiry, TimeZoneInfo.Local); - _output?.WriteLine($"Token: {tokenHash1} Old Expiry: {oldLocalExpiryTime}"); + LogInfo($"Token: {tokenHash1} Old Expiry: {oldLocalExpiryTime}"); TimeSpan timeDiff = oldLocalExpiryTime - DateTime.Now; Assert.True(timeDiff.TotalSeconds <= 60, "Failed to set expiry after 1 minute from current time."); @@ -83,7 +83,7 @@ public void FedAuthTokenRefreshTest(ITestOutputHelper output) DateTime? newExpiry = GetOrSetTokenExpiryDateTime(connection2, false, out tokenHash2); // Display new expiry in local time DateTime newLocalTime = TimeZoneInfo.ConvertTimeFromUtc((DateTime)newExpiry, TimeZoneInfo.Local); - _output?.WriteLine($"Token: {tokenHash2} New Expiry: {newLocalTime}"); + LogInfo($"Token: {tokenHash2} New Expiry: {newLocalTime}"); Assert.True(tokenHash1 == tokenHash2, "The token's hash before and after token refresh must be identical."); Assert.True(newLocalTime > oldLocalExpiryTime, "The refreshed token must have a later expiry time."); @@ -92,6 +92,11 @@ public void FedAuthTokenRefreshTest(ITestOutputHelper output) connection2.Close(); } + private void LogInfo(string message) + { + _output?.WriteLine(message); + } + private DateTime? GetOrSetTokenExpiryDateTime(SqlConnection connection, bool setExpiry, out string tokenHash) { try From 67ab66155997fc55fcb0ec1419d42d8ea1305636 Mon Sep 17 00:00:00 2001 From: v-arellegue Date: Fri, 15 Dec 2023 15:34:32 -0800 Subject: [PATCH 06/15] Remove ITestOutputHelper as test is failing because of it. --- .../AADFedAuthTokenRefreshTest.cs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/AADFedAuthTokenRefreshTest/AADFedAuthTokenRefreshTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/AADFedAuthTokenRefreshTest/AADFedAuthTokenRefreshTest.cs index cb8791343a..cde7b89716 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/AADFedAuthTokenRefreshTest/AADFedAuthTokenRefreshTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/AADFedAuthTokenRefreshTest/AADFedAuthTokenRefreshTest.cs @@ -11,12 +11,12 @@ namespace Microsoft.Data.SqlClient.ManualTesting.Tests { public class AADFedAuthTokenRefreshTest { - private readonly ITestOutputHelper _output; + //private readonly ITestOutputHelper _output; - public AADFedAuthTokenRefreshTest(ITestOutputHelper output) - { - _output = output; - } + //public AADFedAuthTokenRefreshTest(ITestOutputHelper output) + //{ + // _output = output; + //} [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.IsAADPasswordConnStrSetup))] public void FedAuthTokenRefreshTest(ITestOutputHelper output) @@ -94,7 +94,8 @@ public void FedAuthTokenRefreshTest(ITestOutputHelper output) private void LogInfo(string message) { - _output?.WriteLine(message); + //_output?.WriteLine(message); + Console.WriteLine(message); } private DateTime? GetOrSetTokenExpiryDateTime(SqlConnection connection, bool setExpiry, out string tokenHash) From f8cf6664208244de5a9bebf9cbac746fd1adb1bb Mon Sep 17 00:00:00 2001 From: v-arellegue Date: Fri, 15 Dec 2023 15:50:35 -0800 Subject: [PATCH 07/15] Removed misplaced ITestOutputHelper output parameter declaration. --- .../AADFedAuthTokenRefreshTest.cs | 22 ++++++++++--------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/AADFedAuthTokenRefreshTest/AADFedAuthTokenRefreshTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/AADFedAuthTokenRefreshTest/AADFedAuthTokenRefreshTest.cs index cde7b89716..58f1042ef2 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/AADFedAuthTokenRefreshTest/AADFedAuthTokenRefreshTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/AADFedAuthTokenRefreshTest/AADFedAuthTokenRefreshTest.cs @@ -11,17 +11,17 @@ namespace Microsoft.Data.SqlClient.ManualTesting.Tests { public class AADFedAuthTokenRefreshTest { - //private readonly ITestOutputHelper _output; + private readonly ITestOutputHelper _output; - //public AADFedAuthTokenRefreshTest(ITestOutputHelper output) - //{ - // _output = output; - //} + public AADFedAuthTokenRefreshTest(ITestOutputHelper output) + { + _output = output; + } [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.IsAADPasswordConnStrSetup))] - public void FedAuthTokenRefreshTest(ITestOutputHelper output) + public void FedAuthTokenRefreshTest() { - // ------------------ Use settings below for local environment testing in your PC ------------------------ + // ------------------ Use settings below for local environment testing ------------------------ //SqlConnectionStringBuilder builder = new(DataTestUtility.AADPasswordConnectionString); //string dataSourceStr = builder.DataSource; @@ -38,13 +38,15 @@ public void FedAuthTokenRefreshTest(ITestOutputHelper output) //Assert.True($"{user}" == userEnvVar, @"AZURE_USERNAME environment variable must be set"); //Assert.True($"{password}" == passwordEnvVar, @"AZURE_PASSWORD environment variable must be set"); - //// This is the format of connection string that works for me + //// Local environment connection string //string connStr = $"Server={dataSourceStr};Persist Security Info=False;User ID={user};MultipleActiveResultSets=False;Encrypt=True;TrustServerCertificate=False;Authentication=ActiveDirectoryDefault;Timeout=90"; // ------------------ End of local environment settings ---------------------------------------------------- + // ------------------ Pipeline environment setting --------------------------------------------------------- // Use this connection string when running in a pipeline string connStr = DataTestUtility.AADPasswordConnectionString; + // -------------------End of Pipeline environment setting -------------------------------------------------- // Create a new connection object and open it SqlConnection connection = new SqlConnection(connStr); @@ -94,8 +96,8 @@ public void FedAuthTokenRefreshTest(ITestOutputHelper output) private void LogInfo(string message) { - //_output?.WriteLine(message); - Console.WriteLine(message); + //Console.WriteLine(message); + _output.WriteLine(message); } private DateTime? GetOrSetTokenExpiryDateTime(SqlConnection connection, bool setExpiry, out string tokenHash) From af57c8ef8ad3d935c9e5164604068bb5242c4b9d Mon Sep 17 00:00:00 2001 From: v-arellegue Date: Sun, 17 Dec 2023 12:32:46 -0800 Subject: [PATCH 08/15] Use a more meaningful variable names. --- .../AADFedAuthTokenRefreshTest.cs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/AADFedAuthTokenRefreshTest/AADFedAuthTokenRefreshTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/AADFedAuthTokenRefreshTest/AADFedAuthTokenRefreshTest.cs index 58f1042ef2..325f558cfa 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/AADFedAuthTokenRefreshTest/AADFedAuthTokenRefreshTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/AADFedAuthTokenRefreshTest/AADFedAuthTokenRefreshTest.cs @@ -11,11 +11,11 @@ namespace Microsoft.Data.SqlClient.ManualTesting.Tests { public class AADFedAuthTokenRefreshTest { - private readonly ITestOutputHelper _output; + private readonly ITestOutputHelper _testOutputHelper; - public AADFedAuthTokenRefreshTest(ITestOutputHelper output) + public AADFedAuthTokenRefreshTest(ITestOutputHelper testOutputHelper) { - _output = output; + _testOutputHelper = testOutputHelper; } [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.IsAADPasswordConnStrSetup))] @@ -70,7 +70,7 @@ public void FedAuthTokenRefreshTest() result = $"{cmd.ExecuteScalar()}"; Assert.True(result != string.Empty, "The connection's command must return a value"); - // The new connection will use the same FedAuthToken but will or supposed to refresh first + // The new connection will use the same FedAuthToken but will refresh it first as it will expire in 1 minute. SqlConnection connection2 = new SqlConnection(connStr); connection2.Open(); @@ -84,11 +84,11 @@ public void FedAuthTokenRefreshTest() string tokenHash2 = ""; DateTime? newExpiry = GetOrSetTokenExpiryDateTime(connection2, false, out tokenHash2); // Display new expiry in local time - DateTime newLocalTime = TimeZoneInfo.ConvertTimeFromUtc((DateTime)newExpiry, TimeZoneInfo.Local); - LogInfo($"Token: {tokenHash2} New Expiry: {newLocalTime}"); + DateTime newLocalExpiryTime = TimeZoneInfo.ConvertTimeFromUtc((DateTime)newExpiry, TimeZoneInfo.Local); + LogInfo($"Token: {tokenHash2} New Expiry: {newLocalExpiryTime}"); Assert.True(tokenHash1 == tokenHash2, "The token's hash before and after token refresh must be identical."); - Assert.True(newLocalTime > oldLocalExpiryTime, "The refreshed token must have a later expiry time."); + Assert.True(newLocalExpiryTime > oldLocalExpiryTime, "The refreshed token must have a new or later expiry time."); connection.Close(); connection2.Close(); @@ -97,7 +97,7 @@ public void FedAuthTokenRefreshTest() private void LogInfo(string message) { //Console.WriteLine(message); - _output.WriteLine(message); + _testOutputHelper.WriteLine(message); } private DateTime? GetOrSetTokenExpiryDateTime(SqlConnection connection, bool setExpiry, out string tokenHash) From 79b3483f1541f6e3d269334ed8976f926953bdb5 Mon Sep 17 00:00:00 2001 From: v-arellegue Date: Sun, 17 Dec 2023 12:52:07 -0800 Subject: [PATCH 09/15] Only add AADFedAuthTokenRefreshTest to test group 3. --- .../Microsoft.Data.SqlClient.ManualTesting.Tests.csproj | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/Microsoft.Data.SqlClient.ManualTesting.Tests.csproj b/src/Microsoft.Data.SqlClient/tests/ManualTests/Microsoft.Data.SqlClient.ManualTesting.Tests.csproj index 060b996780..02d70d25fa 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/Microsoft.Data.SqlClient.ManualTesting.Tests.csproj +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/Microsoft.Data.SqlClient.ManualTesting.Tests.csproj @@ -170,7 +170,6 @@ - From 6bc021eb65bc6808bb1273cb5193b32627455fcc Mon Sep 17 00:00:00 2001 From: v-arellegue Date: Thu, 18 Jan 2024 10:20:49 -0800 Subject: [PATCH 10/15] Removed commented out Console.Writeline. --- .../AADFedAuthTokenRefreshTest.cs | 113 +++++++----------- 1 file changed, 44 insertions(+), 69 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/AADFedAuthTokenRefreshTest/AADFedAuthTokenRefreshTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/AADFedAuthTokenRefreshTest/AADFedAuthTokenRefreshTest.cs index 325f558cfa..8ecc075b54 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/AADFedAuthTokenRefreshTest/AADFedAuthTokenRefreshTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/AADFedAuthTokenRefreshTest/AADFedAuthTokenRefreshTest.cs @@ -21,82 +21,57 @@ public AADFedAuthTokenRefreshTest(ITestOutputHelper testOutputHelper) [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.IsAADPasswordConnStrSetup))] public void FedAuthTokenRefreshTest() { - // ------------------ Use settings below for local environment testing ------------------------ - //SqlConnectionStringBuilder builder = new(DataTestUtility.AADPasswordConnectionString); - //string dataSourceStr = builder.DataSource; - - //// set user id and password from AADPasswordConnectionString - //string user = builder.UserID; - //string password = builder.Password; - - //// Set Environment variables used for ActiveDirectoryDefault authentication type - //Environment.SetEnvironmentVariable("AZURE_USERNAME", $"{user}"); - //Environment.SetEnvironmentVariable("AZURE_PASSWORD", $"{password}"); - - //string userEnvVar = Environment.GetEnvironmentVariable("AZURE_USERNAME"); - //string passwordEnvVar = Environment.GetEnvironmentVariable("AZURE_PASSWORD"); - //Assert.True($"{user}" == userEnvVar, @"AZURE_USERNAME environment variable must be set"); - //Assert.True($"{password}" == passwordEnvVar, @"AZURE_PASSWORD environment variable must be set"); - - //// Local environment connection string - //string connStr = $"Server={dataSourceStr};Persist Security Info=False;User ID={user};MultipleActiveResultSets=False;Encrypt=True;TrustServerCertificate=False;Authentication=ActiveDirectoryDefault;Timeout=90"; - - // ------------------ End of local environment settings ---------------------------------------------------- - - // ------------------ Pipeline environment setting --------------------------------------------------------- - // Use this connection string when running in a pipeline string connStr = DataTestUtility.AADPasswordConnectionString; - // -------------------End of Pipeline environment setting -------------------------------------------------- // Create a new connection object and open it - SqlConnection connection = new SqlConnection(connStr); - connection.Open(); - - // Set the token expiry to expire in 1 minute from now to force token refresh - string tokenHash1 = ""; - DateTime? oldExpiry = GetOrSetTokenExpiryDateTime(connection, true, out tokenHash1); - Assert.True(oldExpiry != null, "Failed to make token expiry to expire in one minute."); - - // Display old expiry in local time which should be in 1 minute from now - DateTime oldLocalExpiryTime = TimeZoneInfo.ConvertTimeFromUtc((DateTime)oldExpiry, TimeZoneInfo.Local); - LogInfo($"Token: {tokenHash1} Old Expiry: {oldLocalExpiryTime}"); - TimeSpan timeDiff = oldLocalExpiryTime - DateTime.Now; - Assert.True(timeDiff.TotalSeconds <= 60, "Failed to set expiry after 1 minute from current time."); - - // Check if connection is alive - string result = ""; - SqlCommand cmd = connection.CreateCommand(); - cmd.CommandText = "select @@version"; - result = $"{cmd.ExecuteScalar()}"; - Assert.True(result != string.Empty, "The connection's command must return a value"); - - // The new connection will use the same FedAuthToken but will refresh it first as it will expire in 1 minute. - SqlConnection connection2 = new SqlConnection(connStr); - connection2.Open(); - - // Check again if connection is alive - cmd = connection2.CreateCommand(); - cmd.CommandText = "select 1"; - result = $"{cmd.ExecuteScalar()}"; - Assert.True(result != string.Empty, "The connection's command must return a value after a token refresh."); - - // Get the refreshed token expiry - string tokenHash2 = ""; - DateTime? newExpiry = GetOrSetTokenExpiryDateTime(connection2, false, out tokenHash2); - // Display new expiry in local time - DateTime newLocalExpiryTime = TimeZoneInfo.ConvertTimeFromUtc((DateTime)newExpiry, TimeZoneInfo.Local); - LogInfo($"Token: {tokenHash2} New Expiry: {newLocalExpiryTime}"); - - Assert.True(tokenHash1 == tokenHash2, "The token's hash before and after token refresh must be identical."); - Assert.True(newLocalExpiryTime > oldLocalExpiryTime, "The refreshed token must have a new or later expiry time."); - - connection.Close(); - connection2.Close(); + using (SqlConnection connection = new SqlConnection(connStr)) + { + connection.Open(); + + // Set the token expiry to expire in 1 minute from now to force token refresh + string tokenHash1 = ""; + DateTime? oldExpiry = GetOrSetTokenExpiryDateTime(connection, true, out tokenHash1); + Assert.True(oldExpiry != null, "Failed to make token expiry to expire in one minute."); + + // Display old expiry in local time which should be in 1 minute from now + DateTime oldLocalExpiryTime = TimeZoneInfo.ConvertTimeFromUtc((DateTime)oldExpiry, TimeZoneInfo.Local); + LogInfo($"Token: {tokenHash1} Old Expiry: {oldLocalExpiryTime}"); + TimeSpan timeDiff = oldLocalExpiryTime - DateTime.Now; + Assert.True(timeDiff.TotalSeconds <= 60, "Failed to set expiry after 1 minute from current time."); + + // Check if connection is alive + string result = ""; + SqlCommand cmd = connection.CreateCommand(); + cmd.CommandText = "select @@version"; + result = $"{cmd.ExecuteScalar()}"; + Assert.True(result != string.Empty, "The connection's command must return a value"); + + // The new connection will use the same FedAuthToken but will refresh it first as it will expire in 1 minute. + using (SqlConnection connection2 = new SqlConnection(connStr)) + { + connection2.Open(); + + // Check again if connection is alive + cmd = connection2.CreateCommand(); + cmd.CommandText = "select 1"; + result = $"{cmd.ExecuteScalar()}"; + Assert.True(result != string.Empty, "The connection's command must return a value after a token refresh."); + + // Get the refreshed token expiry + string tokenHash2 = ""; + DateTime? newExpiry = GetOrSetTokenExpiryDateTime(connection2, false, out tokenHash2); + // Display new expiry in local time + DateTime newLocalExpiryTime = TimeZoneInfo.ConvertTimeFromUtc((DateTime)newExpiry, TimeZoneInfo.Local); + LogInfo($"Token: {tokenHash2} New Expiry: {newLocalExpiryTime}"); + + Assert.True(tokenHash1 == tokenHash2, "The token's hash before and after token refresh must be identical."); + Assert.True(newLocalExpiryTime > oldLocalExpiryTime, "The refreshed token must have a new or later expiry time."); + } + } } private void LogInfo(string message) { - //Console.WriteLine(message); _testOutputHelper.WriteLine(message); } From bcb49366e15d6f71e58b9f68c831a981af2f99a7 Mon Sep 17 00:00:00 2001 From: v-arellegue Date: Thu, 18 Jan 2024 11:33:28 -0800 Subject: [PATCH 11/15] Add meaningful comment to trigger build in pipeline. --- .../AADFedAuthTokenRefreshTest/AADFedAuthTokenRefreshTest.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/AADFedAuthTokenRefreshTest/AADFedAuthTokenRefreshTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/AADFedAuthTokenRefreshTest/AADFedAuthTokenRefreshTest.cs index 8ecc075b54..7359af653a 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/AADFedAuthTokenRefreshTest/AADFedAuthTokenRefreshTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/AADFedAuthTokenRefreshTest/AADFedAuthTokenRefreshTest.cs @@ -33,13 +33,13 @@ public void FedAuthTokenRefreshTest() DateTime? oldExpiry = GetOrSetTokenExpiryDateTime(connection, true, out tokenHash1); Assert.True(oldExpiry != null, "Failed to make token expiry to expire in one minute."); - // Display old expiry in local time which should be in 1 minute from now + // Convert and display the old expiry into local time which should be in 1 minute from now DateTime oldLocalExpiryTime = TimeZoneInfo.ConvertTimeFromUtc((DateTime)oldExpiry, TimeZoneInfo.Local); LogInfo($"Token: {tokenHash1} Old Expiry: {oldLocalExpiryTime}"); TimeSpan timeDiff = oldLocalExpiryTime - DateTime.Now; Assert.True(timeDiff.TotalSeconds <= 60, "Failed to set expiry after 1 minute from current time."); - // Check if connection is alive + // Check if connection is still alive to continue further testing string result = ""; SqlCommand cmd = connection.CreateCommand(); cmd.CommandText = "select @@version"; From fa913b19322fe31adff7e770c2c8b2ec12c4b817 Mon Sep 17 00:00:00 2001 From: v-arellegue Date: Tue, 23 Jan 2024 18:40:15 -0800 Subject: [PATCH 12/15] Refactor as suggested by creating hekper class for Federated Authentication Helper using reflection. Also, applied SOLID and DRY principles. --- .../SqlClient/SqlInternalConnectionTds.cs | 3 +- .../SqlClient/SqlInternalConnectionTds.cs | 3 +- ....Data.SqlClient.ManualTesting.Tests.csproj | 5 +- .../AADFedAuthTokenRefreshTest.cs | 93 +++------------- .../SystemDataInternals/FedAuthTokenHelper.cs | 104 ++++++++++++++++++ 5 files changed, 126 insertions(+), 82 deletions(-) create mode 100644 src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/Common/SystemDataInternals/FedAuthTokenHelper.cs diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs index 601957a7f0..41ffec9525 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs @@ -2259,7 +2259,8 @@ internal void OnFedAuthInfo(SqlFedAuthInfo fedAuthInfo) { // Try adding this new _newDbConnectionPoolAuthenticationContext to the _dbConnectionPool's AuthenticationContextKeys if it is not in there yet. // The DbConnectionPoolAuthenticationContextKeys collection is used to refresh a cached token just before it expires within 10 minutes. - _dbConnectionPool.AuthenticationContexts.TryAdd(new DbConnectionPoolAuthenticationContextKey(fedAuthInfo.stsurl, fedAuthInfo.spn), _newDbConnectionPoolAuthenticationContext); + //_dbConnectionPool.AuthenticationContexts.TryAdd(new DbConnectionPoolAuthenticationContextKey(fedAuthInfo.stsurl, fedAuthInfo.spn), _newDbConnectionPoolAuthenticationContext); + _dbConnectionPool.AuthenticationContexts.TryAdd(_dbConnectionPoolAuthenticationContextKey, _newDbConnectionPoolAuthenticationContext); } } } diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs index e54ed8515e..7cb7a54cde 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs @@ -2685,7 +2685,8 @@ internal void OnFedAuthInfo(SqlFedAuthInfo fedAuthInfo) { // Try adding this new _newDbConnectionPoolAuthenticationContext to the _dbConnectionPool's AuthenticationContextKeys if it is not in there yet. // The DbConnectionPoolAuthenticationContextKeys collection is used to refresh a cached token just before it expires within 10 minutes. - _dbConnectionPool.AuthenticationContexts.TryAdd(new DbConnectionPoolAuthenticationContextKey(fedAuthInfo.stsurl, fedAuthInfo.spn), _newDbConnectionPoolAuthenticationContext); + // _dbConnectionPool.AuthenticationContexts.TryAdd(new DbConnectionPoolAuthenticationContextKey(fedAuthInfo.stsurl, fedAuthInfo.spn), _newDbConnectionPoolAuthenticationContext); + _dbConnectionPool.AuthenticationContexts.TryAdd(_dbConnectionPoolAuthenticationContextKey, _newDbConnectionPoolAuthenticationContext); } } } diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/Microsoft.Data.SqlClient.ManualTesting.Tests.csproj b/src/Microsoft.Data.SqlClient/tests/ManualTests/Microsoft.Data.SqlClient.ManualTesting.Tests.csproj index 02d70d25fa..24c84ed402 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/Microsoft.Data.SqlClient.ManualTesting.Tests.csproj +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/Microsoft.Data.SqlClient.ManualTesting.Tests.csproj @@ -170,6 +170,7 @@ + @@ -267,7 +268,6 @@ - @@ -276,6 +276,7 @@ + @@ -341,7 +342,7 @@ - + diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/AADFedAuthTokenRefreshTest/AADFedAuthTokenRefreshTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/AADFedAuthTokenRefreshTest/AADFedAuthTokenRefreshTest.cs index 7359af653a..0794b63cff 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/AADFedAuthTokenRefreshTest/AADFedAuthTokenRefreshTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/AADFedAuthTokenRefreshTest/AADFedAuthTokenRefreshTest.cs @@ -1,9 +1,5 @@ using System; -using System.Collections; -using System.Linq; -using System.Reflection; -using System.Security.Cryptography; -using System.Text; +using Microsoft.Data.SqlClient.ManualTesting.Tests.SQL.Common.SystemDataInternals; using Xunit; using Xunit.Abstractions; @@ -21,21 +17,19 @@ public AADFedAuthTokenRefreshTest(ITestOutputHelper testOutputHelper) [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.IsAADPasswordConnStrSetup))] public void FedAuthTokenRefreshTest() { - string connStr = DataTestUtility.AADPasswordConnectionString; + string connectionString = DataTestUtility.AADPasswordConnectionString; - // Create a new connection object and open it - using (SqlConnection connection = new SqlConnection(connStr)) + using (SqlConnection connection = new SqlConnection(connectionString)) { connection.Open(); - // Set the token expiry to expire in 1 minute from now to force token refresh - string tokenHash1 = ""; - DateTime? oldExpiry = GetOrSetTokenExpiryDateTime(connection, true, out tokenHash1); - Assert.True(oldExpiry != null, "Failed to make token expiry to expire in one minute."); + string oldTokenHash = ""; + DateTime? oldExpiryDateTime = FedAuthTokenHelper.SetTokenExpiryDateTime(connection, minutesToExpire: 1, out oldTokenHash); + Assert.True(oldExpiryDateTime != null, "Failed to make token expiry to expire in one minute."); // Convert and display the old expiry into local time which should be in 1 minute from now - DateTime oldLocalExpiryTime = TimeZoneInfo.ConvertTimeFromUtc((DateTime)oldExpiry, TimeZoneInfo.Local); - LogInfo($"Token: {tokenHash1} Old Expiry: {oldLocalExpiryTime}"); + DateTime oldLocalExpiryTime = TimeZoneInfo.ConvertTimeFromUtc((DateTime)oldExpiryDateTime, TimeZoneInfo.Local); + LogInfo($"Token: {oldTokenHash} Old Expiry: {oldLocalExpiryTime}"); TimeSpan timeDiff = oldLocalExpiryTime - DateTime.Now; Assert.True(timeDiff.TotalSeconds <= 60, "Failed to set expiry after 1 minute from current time."); @@ -47,24 +41,22 @@ public void FedAuthTokenRefreshTest() Assert.True(result != string.Empty, "The connection's command must return a value"); // The new connection will use the same FedAuthToken but will refresh it first as it will expire in 1 minute. - using (SqlConnection connection2 = new SqlConnection(connStr)) + using (SqlConnection connection2 = new SqlConnection(connectionString)) { connection2.Open(); - // Check again if connection is alive + // Check if connection is alive cmd = connection2.CreateCommand(); cmd.CommandText = "select 1"; result = $"{cmd.ExecuteScalar()}"; Assert.True(result != string.Empty, "The connection's command must return a value after a token refresh."); - // Get the refreshed token expiry - string tokenHash2 = ""; - DateTime? newExpiry = GetOrSetTokenExpiryDateTime(connection2, false, out tokenHash2); - // Display new expiry in local time - DateTime newLocalExpiryTime = TimeZoneInfo.ConvertTimeFromUtc((DateTime)newExpiry, TimeZoneInfo.Local); - LogInfo($"Token: {tokenHash2} New Expiry: {newLocalExpiryTime}"); + string newTokenHash = ""; + DateTime? newExpiryDateTime = FedAuthTokenHelper.GetTokenExpiryDateTime(connection2, out newTokenHash); + DateTime newLocalExpiryTime = TimeZoneInfo.ConvertTimeFromUtc((DateTime)newExpiryDateTime, TimeZoneInfo.Local); + LogInfo($"Token: {newTokenHash} New Expiry: {newLocalExpiryTime}"); - Assert.True(tokenHash1 == tokenHash2, "The token's hash before and after token refresh must be identical."); + Assert.True(oldTokenHash == newTokenHash, "The token's hash before and after token refresh must be identical."); Assert.True(newLocalExpiryTime > oldLocalExpiryTime, "The refreshed token must have a new or later expiry time."); } } @@ -74,60 +66,5 @@ private void LogInfo(string message) { _testOutputHelper.WriteLine(message); } - - private DateTime? GetOrSetTokenExpiryDateTime(SqlConnection connection, bool setExpiry, out string tokenHash) - { - try - { - // Get the inner connection - object innerConnectionObj = connection.GetType().GetProperty("InnerConnection", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(connection); - - // Get the db connection pool - object poolObj = innerConnectionObj.GetType().GetProperty("Pool", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(innerConnectionObj); - - // Get the Authentication Contexts - IEnumerable authContextCollection = (IEnumerable)poolObj.GetType().GetProperty("AuthenticationContexts", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(poolObj, null); - - // Get the first authentication context - object authContextObj = authContextCollection.Cast().FirstOrDefault(); - - // Get the token object from the authentication context - object tokenObj = authContextObj.GetType().GetProperty("Value").GetValue(authContextObj, null); - - DateTime expiry = (DateTime)tokenObj.GetType().GetProperty("ExpirationTime", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(tokenObj, null); - - if (setExpiry) - { - // Forcing 1 minute expiry to trigger token refresh. - expiry = DateTime.UtcNow.AddMinutes(1); - - // Apply the expiry to the token object - FieldInfo expirationTime = tokenObj.GetType().GetField("_expirationTime", BindingFlags.NonPublic | BindingFlags.Instance); - expirationTime.SetValue(tokenObj, expiry); - } - - byte[] tokenBytes = (byte[])tokenObj.GetType().GetProperty("AccessToken", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(tokenObj, null); - - tokenHash = GetTokenHash(tokenBytes); - - return expiry; - } - catch (Exception) - { - tokenHash = ""; - return null; - } - } - - private string GetTokenHash(byte[] tokenBytes) - { - string token = Encoding.Unicode.GetString(tokenBytes); - var bytesInUtf8 = Encoding.UTF8.GetBytes(token); - using (var sha256 = SHA256.Create()) - { - var hash = sha256.ComputeHash(bytesInUtf8); - return Convert.ToBase64String(hash); - } - } } } diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/Common/SystemDataInternals/FedAuthTokenHelper.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/Common/SystemDataInternals/FedAuthTokenHelper.cs new file mode 100644 index 0000000000..42f9ca4ba7 --- /dev/null +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/Common/SystemDataInternals/FedAuthTokenHelper.cs @@ -0,0 +1,104 @@ +using System; +using System.Collections; +using System.Linq; +using System.Reflection; + +namespace Microsoft.Data.SqlClient.ManualTesting.Tests.SQL.Common.SystemDataInternals +{ + internal static class FedAuthTokenHelper + { + internal static DateTime? GetTokenExpiryDateTime(SqlConnection connection, out string tokenHash) + { + try + { + object authenticationContextValueObj = GetAuthenticationContextValue(connection); + + DateTime expirationTimeProperty = (DateTime)authenticationContextValueObj.GetType().GetProperty("ExpirationTime", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(authenticationContextValueObj, null); + + tokenHash = GetTokenHash(authenticationContextValueObj); + + return expirationTimeProperty; + } + catch (Exception) + { + tokenHash = ""; + return null; + } + } + + internal static DateTime? SetTokenExpiryDateTime(SqlConnection connection, int minutesToExpire, out string tokenHash) + { + try + { + object authenticationContextValueObj = GetAuthenticationContextValue(connection); + + DateTime expirationTimeProperty = (DateTime)authenticationContextValueObj.GetType().GetProperty("ExpirationTime", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(authenticationContextValueObj, null); + + expirationTimeProperty = DateTime.UtcNow.AddMinutes(minutesToExpire); + + FieldInfo expirationTimeInfo = authenticationContextValueObj.GetType().GetField("_expirationTime", BindingFlags.NonPublic | BindingFlags.Instance); + expirationTimeInfo.SetValue(authenticationContextValueObj, expirationTimeProperty); + + tokenHash = GetTokenHash(authenticationContextValueObj); + + return expirationTimeProperty; + } + catch (Exception) + { + tokenHash = ""; + return null; + } + } + + internal static string GetTokenHash(object authenticationContextValueObj) + { + try + { + Assembly sqlConnectionAssembly = Assembly.GetAssembly(typeof(SqlConnection)); + + Type sqlFedAuthTokenType = sqlConnectionAssembly.GetType("Microsoft.Data.SqlClient.SqlFedAuthToken"); + + Type[] sqlFedAuthTokenTypeArray = new Type[] { sqlFedAuthTokenType }; + + ConstructorInfo sqlFedAuthTokenConstructorInfo = sqlFedAuthTokenType.GetConstructor(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, CallingConventions.Any, Type.EmptyTypes, null); + + Type activeDirectoryAuthenticationTimeoutRetryHelperType = sqlConnectionAssembly.GetType("Microsoft.Data.SqlClient.ActiveDirectoryAuthenticationTimeoutRetryHelper"); + + ConstructorInfo activeDirectoryAuthenticationTimeoutRetryHelperConstructorInfo = activeDirectoryAuthenticationTimeoutRetryHelperType.GetConstructor(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, CallingConventions.Any, Type.EmptyTypes, null); + + object activeDirectoryAuthenticationTimeoutRetryHelperObj = activeDirectoryAuthenticationTimeoutRetryHelperConstructorInfo.Invoke(new object[] { }); + + MethodInfo tokenHashInfo = activeDirectoryAuthenticationTimeoutRetryHelperObj.GetType().GetMethod("GetTokenHash", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, CallingConventions.Any, sqlFedAuthTokenTypeArray, null); + + byte[] tokenBytes = (byte[])authenticationContextValueObj.GetType().GetProperty("AccessToken", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(authenticationContextValueObj, null); + + object sqlFedAuthTokenObj = sqlFedAuthTokenConstructorInfo.Invoke(new object[] { }); + FieldInfo accessTokenInfo = sqlFedAuthTokenObj.GetType().GetField("accessToken", BindingFlags.NonPublic | BindingFlags.Instance); + accessTokenInfo.SetValue(sqlFedAuthTokenObj, tokenBytes); + + string tokenHash = (string)tokenHashInfo.Invoke(activeDirectoryAuthenticationTimeoutRetryHelperObj, new object[] { sqlFedAuthTokenObj }); + + return tokenHash; + } + catch (Exception) + { + return ""; + } + } + + internal static object GetAuthenticationContextValue(SqlConnection connection) + { + object innerConnectionObj = connection.GetType().GetProperty("InnerConnection", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(connection); + + object databaseConnectionPoolObj = innerConnectionObj.GetType().GetProperty("Pool", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(innerConnectionObj); + + IEnumerable authenticationContexts = (IEnumerable)databaseConnectionPoolObj.GetType().GetProperty("AuthenticationContexts", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(databaseConnectionPoolObj, null); + + object authenticationContextObj = authenticationContexts.Cast().FirstOrDefault(); + + object authenticationContextValueObj = authenticationContextObj.GetType().GetProperty("Value").GetValue(authenticationContextObj, null); + + return authenticationContextValueObj; + } + } +} From 96462af73bd78d7edc43ddaf762761e76858b7fc Mon Sep 17 00:00:00 2001 From: v-arellegue Date: Wed, 24 Jan 2024 08:34:25 -0800 Subject: [PATCH 13/15] Change in range test to use Assert.InRange as suggested. --- .../AADFedAuthTokenRefreshTest/AADFedAuthTokenRefreshTest.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/AADFedAuthTokenRefreshTest/AADFedAuthTokenRefreshTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/AADFedAuthTokenRefreshTest/AADFedAuthTokenRefreshTest.cs index 0794b63cff..b9d3132778 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/AADFedAuthTokenRefreshTest/AADFedAuthTokenRefreshTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/AADFedAuthTokenRefreshTest/AADFedAuthTokenRefreshTest.cs @@ -31,7 +31,7 @@ public void FedAuthTokenRefreshTest() DateTime oldLocalExpiryTime = TimeZoneInfo.ConvertTimeFromUtc((DateTime)oldExpiryDateTime, TimeZoneInfo.Local); LogInfo($"Token: {oldTokenHash} Old Expiry: {oldLocalExpiryTime}"); TimeSpan timeDiff = oldLocalExpiryTime - DateTime.Now; - Assert.True(timeDiff.TotalSeconds <= 60, "Failed to set expiry after 1 minute from current time."); + Assert.InRange(timeDiff.TotalSeconds, 0, 60); // Check if connection is still alive to continue further testing string result = ""; From 25d26267a422eadcccd814c7a3e65a020061ede5 Mon Sep 17 00:00:00 2001 From: v-arellegue Date: Wed, 24 Jan 2024 11:11:08 -0800 Subject: [PATCH 14/15] Removed unnecessary comment. --- .../src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs | 3 --- .../src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs | 3 --- 2 files changed, 6 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs index 41ffec9525..fb9d62c7e6 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs @@ -2257,9 +2257,6 @@ internal void OnFedAuthInfo(SqlFedAuthInfo fedAuthInfo) if (_newDbConnectionPoolAuthenticationContext != null) { - // Try adding this new _newDbConnectionPoolAuthenticationContext to the _dbConnectionPool's AuthenticationContextKeys if it is not in there yet. - // The DbConnectionPoolAuthenticationContextKeys collection is used to refresh a cached token just before it expires within 10 minutes. - //_dbConnectionPool.AuthenticationContexts.TryAdd(new DbConnectionPoolAuthenticationContextKey(fedAuthInfo.stsurl, fedAuthInfo.spn), _newDbConnectionPoolAuthenticationContext); _dbConnectionPool.AuthenticationContexts.TryAdd(_dbConnectionPoolAuthenticationContextKey, _newDbConnectionPoolAuthenticationContext); } } diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs index 7cb7a54cde..294f9c8b7e 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs @@ -2683,9 +2683,6 @@ internal void OnFedAuthInfo(SqlFedAuthInfo fedAuthInfo) if (_newDbConnectionPoolAuthenticationContext != null) { - // Try adding this new _newDbConnectionPoolAuthenticationContext to the _dbConnectionPool's AuthenticationContextKeys if it is not in there yet. - // The DbConnectionPoolAuthenticationContextKeys collection is used to refresh a cached token just before it expires within 10 minutes. - // _dbConnectionPool.AuthenticationContexts.TryAdd(new DbConnectionPoolAuthenticationContextKey(fedAuthInfo.stsurl, fedAuthInfo.spn), _newDbConnectionPoolAuthenticationContext); _dbConnectionPool.AuthenticationContexts.TryAdd(_dbConnectionPoolAuthenticationContextKey, _newDbConnectionPoolAuthenticationContext); } } From 0950949c5f6380215020d53030ffa36e0b3c7d6e Mon Sep 17 00:00:00 2001 From: DavoudEshtehari <61173489+DavoudEshtehari@users.noreply.github.com> Date: Wed, 24 Jan 2024 12:55:34 -0800 Subject: [PATCH 15/15] Apply suggestions from code review --- .../AADFedAuthTokenRefreshTest/AADFedAuthTokenRefreshTest.cs | 4 ++++ .../SQL/Common/SystemDataInternals/FedAuthTokenHelper.cs | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/AADFedAuthTokenRefreshTest/AADFedAuthTokenRefreshTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/AADFedAuthTokenRefreshTest/AADFedAuthTokenRefreshTest.cs index b9d3132778..875d755a59 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/AADFedAuthTokenRefreshTest/AADFedAuthTokenRefreshTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/AADFedAuthTokenRefreshTest/AADFedAuthTokenRefreshTest.cs @@ -1,3 +1,7 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + using System; using Microsoft.Data.SqlClient.ManualTesting.Tests.SQL.Common.SystemDataInternals; using Xunit; diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/Common/SystemDataInternals/FedAuthTokenHelper.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/Common/SystemDataInternals/FedAuthTokenHelper.cs index 42f9ca4ba7..26d2477b9b 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/Common/SystemDataInternals/FedAuthTokenHelper.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/Common/SystemDataInternals/FedAuthTokenHelper.cs @@ -1,3 +1,7 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + using System; using System.Collections; using System.Linq;