Imported Upstream version 6.0.0.172

Former-commit-id: f3cc9b82f3e5bd8f0fd3ebc098f789556b44e9cd
This commit is contained in:
Xamarin Public Jenkins (auto-signing)
2019-04-12 14:10:50 +00:00
parent 8016999e4d
commit 64ac736ec5
32155 changed files with 3981439 additions and 75368 deletions

View File

@@ -2,7 +2,8 @@
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="..\dir.props" />
<PropertyGroup>
<AssemblyVersion>4.4.0.0</AssemblyVersion>
<PackageVersion>4.6.0</PackageVersion>
<AssemblyVersion>4.5.0.0</AssemblyVersion>
<AssemblyKey>MSFT</AssemblyKey>
<IsUAP>true</IsUAP>
</PropertyGroup>

View File

@@ -3,7 +3,7 @@
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.props))\dir.props" />
<ItemGroup>
<ProjectReference Include="..\ref\System.Data.SqlClient.csproj">
<SupportedFramework>net461;netcoreapp2.0;uap10.0.16299;$(UAPvNextTFM);$(AllXamarinFrameworks)</SupportedFramework>
<SupportedFramework>net461;netcoreapp2.0;uap10.0.16299;$(AllXamarinFrameworks)</SupportedFramework>
</ProjectReference>
<ProjectReference Include="..\src\System.Data.SqlClient.csproj" />
<HarvestIncludePaths Include="ref/net451;lib/net451;runtimes/win/lib/net451" />

View File

@@ -495,6 +495,7 @@ namespace System.Data.SqlClient
public System.Collections.IDictionary RetrieveStatistics() { throw null; }
public static void ChangePassword(string connectionString, string newPassword) { throw null; }
public static void ChangePassword(string connectionString, System.Data.SqlClient.SqlCredential credential, System.Security.SecureString newPassword) { throw null; }
public string AccessToken { get { throw null; } set { } }
}
public sealed partial class SqlConnectionStringBuilder : System.Data.Common.DbConnectionStringBuilder

View File

@@ -12,9 +12,9 @@
netcoreapp-Unix;
netcoreapp-Windows_NT;
uap10.0.16299-Windows_NT;
uap-Windows_NT;
</PackageConfigurations>
<BuildConfigurations>
uap-Windows_NT;
$(PackageConfigurations)
</BuildConfigurations>
</PropertyGroup>

View File

@@ -1296,4 +1296,25 @@
<data name="SQL_ChangePasswordUseOfUnallowedKey" xml:space="preserve">
<value>The keyword '{0}' must not be specified in the connectionString argument to ChangePassword.</value>
</data>
<data name="SQL_ParsingErrorWithState" xml:space="preserve">
<value>Internal connection fatal error. Error state: {0}.</value>
</data>
<data name="SQL_ParsingErrorValue" xml:space="preserve">
<value>Internal connection fatal error. Error state: {0}, Value: {1}.</value>
</data>
<data name="ADP_InvalidMixedUsageOfAccessTokenAndIntegratedSecurity" xml:space="preserve">
<value>Cannot set the AccessToken property if the 'Integrated Security' connection string keyword has been set to 'true' or 'SSPI'.</value>
</data>
<data name="ADP_InvalidMixedUsageOfAccessTokenAndUserIDPassword" xml:space="preserve">
<value>Cannot set the AccessToken property if 'UserID', 'UID', 'Password', or 'PWD' has been specified in connection string.</value>
</data>
<data name="ADP_InvalidMixedUsageOfCredentialAndAccessToken" xml:space="preserve">
<value>Cannot set the Credential property if the AccessToken property is already set.</value>
</data>
<data name="SQL_ParsingErrorFeatureId" xml:space="preserve">
<value>Internal connection fatal error. Error state: {0}, Feature Id: {1}.</value>
</data>
<data name="SQL_ParsingErrorAuthLibraryType" xml:space="preserve">
<value>Internal connection fatal error. Error state: {0}, Authentication Library Type: {1}.</value>
</data>
</root>

View File

@@ -910,5 +910,17 @@ namespace System.Data.Common
{
return Argument(SR.GetString(SR.ADP_InvalidMixedUsageOfSecureCredentialAndIntegratedSecurity));
}
internal static InvalidOperationException InvalidMixedUsageOfAccessTokenAndIntegratedSecurity()
{
return ADP.InvalidOperation(SR.GetString(SR.ADP_InvalidMixedUsageOfAccessTokenAndIntegratedSecurity));
}
internal static InvalidOperationException InvalidMixedUsageOfAccessTokenAndUserIDPassword()
{
return ADP.InvalidOperation(SR.GetString(SR.ADP_InvalidMixedUsageOfAccessTokenAndUserIDPassword));
}
internal static Exception InvalidMixedUsageOfCredentialAndAccessToken()
{
return ADP.InvalidOperation(SR.GetString(SR.ADP_InvalidMixedUsageOfCredentialAndAccessToken));
}
}
}

View File

@@ -34,6 +34,7 @@ namespace System.Data.SqlClient
private SqlCredential _credential;
private string _connectionString;
private int _connectRetryCount;
private string _accessToken; // Access Token to be used for token based authentication
// connection resiliency
private object _reconnectLock = new object();
@@ -97,6 +98,7 @@ namespace System.Data.SqlClient
_credential = new SqlCredential(connection._credential.UserId, password);
}
_accessToken = connection._accessToken;
CacheConnectionStringProperties();
}
@@ -222,12 +224,19 @@ namespace System.Data.SqlClient
}
set
{
if (_credential != null)
if (_credential != null || _accessToken != null)
{
SqlConnectionString connectionOptions = new SqlConnectionString(value);
CheckAndThrowOnInvalidCombinationOfConnectionStringAndSqlCredential(connectionOptions);
if (_credential != null)
{
CheckAndThrowOnInvalidCombinationOfConnectionStringAndSqlCredential(connectionOptions);
}
else
{
CheckAndThrowOnInvalidCombinationOfConnectionOptionAndAccessToken(connectionOptions);
}
}
ConnectionString_Set(new SqlConnectionPoolKey(value, _credential));
ConnectionString_Set(new SqlConnectionPoolKey(value, _credential, _accessToken));
_connectionString = value; // Change _connectionString value only after value is validated
CacheConnectionStringProperties();
}
@@ -242,6 +251,37 @@ namespace System.Data.SqlClient
}
}
// AccessToken: To be used for token based authentication
public string AccessToken
{
get
{
string result = _accessToken;
// When a connection is connecting or is ever opened, make AccessToken available only if "Persist Security Info" is set to true
// otherwise, return null
SqlConnectionString connectionOptions = (SqlConnectionString)UserConnectionOptions;
return InnerConnection.ShouldHidePassword && connectionOptions != null && !connectionOptions.PersistSecurityInfo ? null : _accessToken;
}
set
{
// If a connection is connecting or is ever opened, AccessToken cannot be set
if (!InnerConnection.AllowSetConnectionString)
{
throw ADP.OpenConnectionPropertySet(nameof(AccessToken), InnerConnection.State);
}
if (value != null)
{
// Check if the usage of AccessToken has any conflict with the keys used in connection string and credential
CheckAndThrowOnInvalidCombinationOfConnectionOptionAndAccessToken((SqlConnectionString)ConnectionOptions);
}
// Need to call ConnectionString_Set to do proper pool group check
ConnectionString_Set(new SqlConnectionPoolKey(_connectionString, credential: _credential, accessToken: value));
_accessToken = value;
}
}
public override string Database
{
// if the connection is open, we need to ask the inner connection what it's
@@ -396,12 +436,16 @@ namespace System.Data.SqlClient
if (value != null)
{
CheckAndThrowOnInvalidCombinationOfConnectionStringAndSqlCredential((SqlConnectionString)ConnectionOptions);
if (_accessToken != null)
{
throw ADP.InvalidMixedUsageOfCredentialAndAccessToken();
}
}
_credential = value;
// Need to call ConnectionString_Set to do proper pool group check
ConnectionString_Set(new SqlConnectionPoolKey(_connectionString, _credential));
ConnectionString_Set(new SqlConnectionPoolKey(_connectionString, _credential, accessToken: _accessToken));
}
}
@@ -422,6 +466,29 @@ namespace System.Data.SqlClient
}
}
// CheckAndThrowOnInvalidCombinationOfConnectionOptionAndAccessToken: check if the usage of AccessToken has any conflict
// with the keys used in connection string and credential
// If there is any conflict, it throws InvalidOperationException
// This is to be used setter of ConnectionString and AccessToken properties
private void CheckAndThrowOnInvalidCombinationOfConnectionOptionAndAccessToken(SqlConnectionString connectionOptions)
{
if (UsesClearUserIdOrPassword(connectionOptions))
{
throw ADP.InvalidMixedUsageOfAccessTokenAndUserIDPassword();
}
if (UsesIntegratedSecurity(connectionOptions))
{
throw ADP.InvalidMixedUsageOfAccessTokenAndIntegratedSecurity();
}
// Check if the usage of AccessToken has the conflict with credential
if (_credential != null)
{
throw ADP.InvalidMixedUsageOfCredentialAndAccessToken();
}
}
protected override DbProviderFactory DbProviderFactory
{
get => SqlClientFactory.Instance;
@@ -654,6 +721,7 @@ namespace System.Data.SqlClient
private void DisposeMe(bool disposing)
{
_credential = null;
_accessToken = null;
if (!disposing)
{
@@ -1360,7 +1428,7 @@ namespace System.Data.SqlClient
throw ADP.InvalidArgumentLength(nameof(newPassword), TdsEnums.MAXLEN_NEWPASSWORD);
}
SqlConnectionPoolKey key = new SqlConnectionPoolKey(connectionString, credential: null);
SqlConnectionPoolKey key = new SqlConnectionPoolKey(connectionString, credential: null, accessToken: null);
SqlConnectionString connectionOptions = SqlConnectionFactory.FindSqlConnectionOptions(key);
if (connectionOptions.IntegratedSecurity)
@@ -1403,7 +1471,7 @@ namespace System.Data.SqlClient
throw ADP.InvalidArgumentLength(nameof(newSecurePassword), TdsEnums.MAXLEN_NEWPASSWORD);
}
SqlConnectionPoolKey key = new SqlConnectionPoolKey(connectionString, credential);
SqlConnectionPoolKey key = new SqlConnectionPoolKey(connectionString, credential: null, accessToken: null);
SqlConnectionString connectionOptions = SqlConnectionFactory.FindSqlConnectionOptions(key);
@@ -1441,7 +1509,7 @@ namespace System.Data.SqlClient
if (con != null)
con.Dispose();
}
SqlConnectionPoolKey key = new SqlConnectionPoolKey(connectionString, credential);
SqlConnectionPoolKey key = new SqlConnectionPoolKey(connectionString, credential: null, accessToken: null);
SqlConnectionFactory.SingletonInstance.ClearPool(key);
}

View File

@@ -132,7 +132,7 @@ namespace System.Data.SqlClient
opt = new SqlConnectionString(opt, instanceName, userInstance: false, setEnlistValue: null);
poolGroupProviderInfo = null; // null so we do not pass to constructor below...
}
result = new SqlInternalConnectionTds(identity, opt, key.Credential, poolGroupProviderInfo, "", null, redirectedUserInstance, userOpt, recoverySessionData, applyTransientFaultHandling: applyTransientFaultHandling);
result = new SqlInternalConnectionTds(identity, opt, key.Credential, poolGroupProviderInfo, "", null, redirectedUserInstance, userOpt, recoverySessionData, applyTransientFaultHandling: applyTransientFaultHandling, key.AccessToken);
return result;
}

View File

@@ -7,6 +7,7 @@
//------------------------------------------------------------------------------
using System.Data.Common;
using System.Diagnostics;
namespace System.Data.SqlClient
{
@@ -16,16 +17,20 @@ namespace System.Data.SqlClient
{
private int _hashValue;
private SqlCredential _credential;
private readonly string _accessToken;
internal SqlConnectionPoolKey(string connectionString, SqlCredential credential) : base(connectionString)
internal SqlConnectionPoolKey(string connectionString, SqlCredential credential, string accessToken) : base(connectionString)
{
Debug.Assert(_credential == null || _accessToken == null, "Credential and AccessToken can't have the value at the same time.");
_credential = credential;
_accessToken = accessToken;
CalculateHashCode();
}
private SqlConnectionPoolKey(SqlConnectionPoolKey key) : base(key)
{
_credential = key.Credential;
_accessToken = key.AccessToken;
CalculateHashCode();
}
@@ -50,12 +55,12 @@ namespace System.Data.SqlClient
internal SqlCredential Credential => _credential;
internal string AccessToken => _accessToken;
public override bool Equals(object obj)
{
SqlConnectionPoolKey key = obj as SqlConnectionPoolKey;
return (key != null &&
ConnectionString == key.ConnectionString &&
Credential == key.Credential);
return (key != null && _credential == key._credential && ConnectionString == key.ConnectionString && Object.ReferenceEquals(_accessToken, key._accessToken));
}
public override int GetHashCode()
@@ -74,6 +79,13 @@ namespace System.Data.SqlClient
_hashValue = _hashValue * 17 + _credential.GetHashCode();
}
}
else if (_accessToken != null)
{
unchecked
{
_hashValue = _hashValue * 17 + _accessToken.GetHashCode();
}
}
}
}
}

View File

@@ -106,6 +106,7 @@ namespace System.Data.SqlClient
private TdsParser _parser;
private SqlLoginAck _loginAck;
private SqlCredential _credential;
private FederatedAuthenticationFeatureExtensionData? _fedAuthFeatureExtensionData;
// Connection Resiliency
private bool _sessionRecoveryRequested;
@@ -113,6 +114,13 @@ namespace System.Data.SqlClient
internal SessionData _currentSessionData; // internal for use from TdsParser only, other should use CurrentSessionData property that will fix database and language
private SessionData _recoverySessionData;
// Federated Authentication
// Response obtained from the server for FEDAUTHREQUIRED prelogin option.
internal bool _fedAuthRequired;
internal bool _federatedAuthenticationRequested;
internal bool _federatedAuthenticationAcknowledged;
internal byte[] _accessTokenInBytes;
// The errors in the transient error set are contained in
// https://azure.microsoft.com/en-us/documentation/articles/sql-database-develop-error-messages/#transient-faults-connection-loss-and-other-temporary-errors
private static readonly HashSet<int> s_transientErrors = new HashSet<int>
@@ -295,7 +303,7 @@ namespace System.Data.SqlClient
private RoutingInfo _routingInfo = null;
private Guid _originalClientConnectionId = Guid.Empty;
private string _routingDestination = null;
private readonly TimeoutTimer _timeout;
// although the new password is generally not used it must be passed to the ctor
// the new Login7 packet will always write out the new password (or a length of zero and no bytes if not present)
@@ -310,7 +318,9 @@ namespace System.Data.SqlClient
bool redirectedUserInstance,
SqlConnectionString userConnectionOptions = null, // NOTE: userConnectionOptions may be different to connectionOptions if the connection string has been expanded (see SqlConnectionString.Expand)
SessionData reconnectSessionData = null,
bool applyTransientFaultHandling = false) : base(connectionOptions)
bool applyTransientFaultHandling = false,
string accessToken = null) : base(connectionOptions)
{
#if DEBUG
if (reconnectSessionData != null)
@@ -335,6 +345,10 @@ namespace System.Data.SqlClient
}
}
if (accessToken != null)
{
_accessTokenInBytes = System.Text.Encoding.Unicode.GetBytes(accessToken);
}
_identity = identity;
Debug.Assert(newSecurePassword != null || newPassword != null, "cannot have both new secure change password and string based change password to be null");
@@ -355,9 +369,10 @@ namespace System.Data.SqlClient
_parserLock.Wait(canReleaseFromAnyThread: false);
ThreadHasParserLockForClose = true; // In case of error, let ourselves know that we already own the parser lock
try
{
var timeout = TimeoutTimer.StartSecondsTimeout(connectionOptions.ConnectTimeout);
_timeout = TimeoutTimer.StartSecondsTimeout(connectionOptions.ConnectTimeout);
// If transient fault handling is enabled then we can retry the login up to the ConnectRetryCount.
int connectionEstablishCount = applyTransientFaultHandling ? connectionOptions.ConnectRetryCount + 1 : 1;
@@ -366,7 +381,7 @@ namespace System.Data.SqlClient
{
try
{
OpenLoginEnlist(timeout, connectionOptions, credential, newPassword, newSecurePassword, redirectedUserInstance);
OpenLoginEnlist(_timeout, connectionOptions, credential, newPassword, newSecurePassword, redirectedUserInstance);
break;
}
@@ -374,8 +389,8 @@ namespace System.Data.SqlClient
{
if (i + 1 == connectionEstablishCount
|| !applyTransientFaultHandling
|| timeout.IsExpired
|| timeout.MillisecondsRemaining < transientRetryIntervalInMilliSeconds
|| _timeout.IsExpired
|| _timeout.MillisecondsRemaining < transientRetryIntervalInMilliSeconds
|| !IsTransientError(sqlex))
{
throw sqlex;
@@ -1017,6 +1032,10 @@ namespace System.Data.SqlClient
if (_routingInfo == null)
{ // ROR should not affect state of connection recovery
if (_federatedAuthenticationRequested && !_federatedAuthenticationAcknowledged)
{
throw SQL.ParsingError(ParsingErrorState.FedAuthNotAcknowledged);
}
if (!_sessionRecoveryAcknowledged)
{
_currentSessionData = null;
@@ -1125,9 +1144,22 @@ namespace System.Data.SqlClient
_sessionRecoveryRequested = true;
}
if (_accessTokenInBytes != null)
{
requestedFeatures |= TdsEnums.FeatureExtension.FedAuth;
_fedAuthFeatureExtensionData = new FederatedAuthenticationFeatureExtensionData
{
libraryType = TdsEnums.FedAuthLibrary.SecurityToken,
fedAuthRequiredPreLoginResponse = _fedAuthRequired,
accessToken = _accessTokenInBytes
};
// No need any further info from the server for token based authentication. So set _federatedAuthenticationRequested to true
_federatedAuthenticationRequested = true;
}
// The GLOBALTRANSACTIONS feature is implicitly requested
requestedFeatures |= TdsEnums.FeatureExtension.GlobalTransactions;
_parser.TdsLogin(login, requestedFeatures, _recoverySessionData);
_parser.TdsLogin(login, requestedFeatures, _recoverySessionData, _fedAuthFeatureExtensionData);
}
private void LoginFailure()
@@ -1900,6 +1932,32 @@ namespace System.Data.SqlClient
}
break;
}
case TdsEnums.FEATUREEXT_FEDAUTH:
{
if (!_federatedAuthenticationRequested)
{
throw SQL.ParsingErrorFeatureId(ParsingErrorState.UnrequestedFeatureAckReceived, featureId);
}
Debug.Assert(_fedAuthFeatureExtensionData != null, "_fedAuthFeatureExtensionData must not be null when _federatedAuthenticatonRequested == true");
switch (_fedAuthFeatureExtensionData.Value.libraryType)
{
case TdsEnums.FedAuthLibrary.SecurityToken:
// The server shouldn't have sent any additional data with the ack (like a nonce)
if (data.Length != 0)
{
throw SQL.ParsingError(ParsingErrorState.FedAuthFeatureAckContainsExtraData);
}
break;
default:
Debug.Fail("Unknown _fedAuthLibrary type");
throw SQL.ParsingErrorLibraryType(ParsingErrorState.FedAuthFeatureAckUnknownLibraryType, (int)_fedAuthFeatureExtensionData.Value.libraryType);
}
_federatedAuthenticationAcknowledged = true;
break;
}
default:
{

View File

@@ -188,6 +188,10 @@ namespace System.Data.SqlClient
{
return ADP.Argument(SR.GetString(SR.SQL_UserInstanceFailoverNotCompatible));
}
internal static Exception ParsingErrorLibraryType(ParsingErrorState state, int libraryType)
{
return ADP.InvalidOperation(SR.GetString(SR.SQL_ParsingErrorAuthLibraryType, ((int)state).ToString(CultureInfo.InvariantCulture), libraryType));
}
internal static Exception InvalidSQLServerVersionUnknown()
{
return ADP.DataAdapter(SR.GetString(SR.SQL_InvalidSQLServerVersionUnknown));
@@ -407,6 +411,18 @@ namespace System.Data.SqlClient
{
return ADP.InvalidOperation(SR.GetString(SR.SQL_ParsingError));
}
static internal Exception ParsingError(ParsingErrorState state)
{
return ADP.InvalidOperation(SR.GetString(SR.SQL_ParsingErrorWithState, ((int)state).ToString(CultureInfo.InvariantCulture)));
}
static internal Exception ParsingErrorValue(ParsingErrorState state, int value)
{
return ADP.InvalidOperation(SR.GetString(SR.SQL_ParsingErrorValue, ((int)state).ToString(CultureInfo.InvariantCulture), value));
}
static internal Exception ParsingErrorFeatureId(ParsingErrorState state, int featureId)
{
return ADP.InvalidOperation(SR.GetString(SR.SQL_ParsingErrorFeatureId, ((int)state).ToString(CultureInfo.InvariantCulture), featureId));
}
internal static Exception MoneyOverflow(string moneyValue)
{
return ADP.Overflow(SR.GetString(SR.SQL_MoneyOverflow, moneyValue));

View File

@@ -202,15 +202,29 @@ namespace System.Data.SqlClient
public const byte FEATUREEXT_TERMINATOR = 0xFF;
public const byte FEATUREEXT_SRECOVERY = 0x01;
public const byte FEATUREEXT_GLOBALTRANSACTIONS = 0x05;
public const byte FEATUREEXT_FEDAUTH = 0x02;
[Flags]
public enum FeatureExtension : uint
{
None = 0,
SessionRecovery = 1,
FedAuth = 2,
GlobalTransactions = 8,
}
public const byte FEDAUTHLIB_LIVEID = 0X00;
public const byte FEDAUTHLIB_SECURITYTOKEN = 0x01;
public const byte FEDAUTHLIB_ADAL = 0x02;
public const byte FEDAUTHLIB_RESERVED = 0X7F;
public enum FedAuthLibrary : byte
{
LiveId = FEDAUTHLIB_LIVEID,
SecurityToken = FEDAUTHLIB_SECURITYTOKEN,
ADAL = FEDAUTHLIB_ADAL, // For later support
Default = FEDAUTHLIB_RESERVED
}
// Loginrec defines
public const byte MAX_LOG_NAME = 30; // TDS 4.2 login rec max name length
@@ -927,5 +941,30 @@ namespace System.Data.SqlClient
Snix_Close,
Snix_SendRows,
}
internal enum ParsingErrorState
{
Undefined = 0,
FedAuthInfoLengthTooShortForCountOfInfoIds = 1,
FedAuthInfoLengthTooShortForData = 2,
FedAuthInfoFailedToReadCountOfInfoIds = 3,
FedAuthInfoFailedToReadTokenStream = 4,
FedAuthInfoInvalidOffset = 5,
FedAuthInfoFailedToReadData = 6,
FedAuthInfoDataNotUnicode = 7,
FedAuthInfoDoesNotContainStsurlAndSpn = 8,
FedAuthInfoNotReceived = 9,
FedAuthNotAcknowledged = 10,
FedAuthFeatureAckContainsExtraData = 11,
FedAuthFeatureAckUnknownLibraryType = 12,
UnrequestedFeatureAckReceived = 13,
UnknownFeatureAck = 14,
InvalidTdsTokenReceived = 15,
SessionStateLengthTooShort = 16,
SessionStateInvalidStatus = 17,
CorruptedTdsStream = 18,
ProcessSniPacketFailed = 19,
FedAuthRequiredPreLoginResponseInvalidValue = 20,
}
}

View File

@@ -1 +1 @@
eb9067b8e263568b3c08c06c1e834e231647168e
35374eb8e0652b5b4c1be52970b3f37baeb5a287

View File

@@ -46,6 +46,7 @@ namespace System.Data.SqlClient
THREADID,
MARS,
TRACEID,
FEDAUTHREQUIRED,
NUMOPT,
LASTOPT = 255
}
@@ -66,6 +67,16 @@ namespace System.Data.SqlClient
Broken,
}
/// <summary>
/// Struct encapsulating the data to be sent to the server as part of Federated Authentication Feature Extension.
/// </summary>
internal struct FederatedAuthenticationFeatureExtensionData
{
internal TdsEnums.FedAuthLibrary libraryType;
internal bool fedAuthRequiredPreLoginResponse;
internal byte[] accessToken;
}
sealed internal class SqlCollation
{
// First 20 bits of info field represent the lcid, bits 21-25 are compare options

View File

@@ -0,0 +1,46 @@
// 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 Xunit;
namespace System.Data.SqlClient.Tests
{
public class AADAccessTokenTest
{
private SqlConnectionStringBuilder _builder;
private SqlCredential _credential = null;
[Theory]
[InlineData("Test combination of Access Token and IntegratedSecurity", new object[] { "Integrated Security", true })]
[InlineData("Test combination of Access Token and User Id", new object[] { "UID", "sampleUserId" })]
[InlineData("Test combination of Access Token and Password", new object[] { "PWD", "samplePassword" })]
[InlineData("Test combination of Access Token and Credentials", new object[] { "sampleUserId" })]
public void InvalidCombinationOfAccessToken(string description, object[] Params)
{
_builder = new SqlConnectionStringBuilder
{
["Data Source"] = "sample.database.windows.net"
};
if (Params.Length == 1)
{
Security.SecureString password = new Security.SecureString();
password.MakeReadOnly();
_credential = new SqlCredential(Params[0] as string, password);
}
else
{
_builder[Params[0] as string] = Params[1];
}
InvalidCombinationCheck(_credential);
}
private void InvalidCombinationCheck(SqlCredential credential)
{
using (SqlConnection connection = new SqlConnection(_builder.ConnectionString, credential))
{
Assert.Throws<InvalidOperationException>(() => connection.AccessToken = "SampleAccessToken");
}
}
}
}

View File

@@ -9,6 +9,7 @@
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'netstandard-Windows_NT-Debug|AnyCPU'" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'netstandard-Windows_NT-Release|AnyCPU'" />
<ItemGroup>
<Compile Include="AADAccessTokenTest.cs" />
<Compile Include="CloneTests.cs" />
<Compile Include="BaseProviderAsyncTest\BaseProviderAsyncTest.cs" />
<Compile Include="BaseProviderAsyncTest\MockCommand.cs" />

View File

@@ -17,6 +17,10 @@ namespace System.Data.SqlClient.ManualTesting.Tests
private static readonly Type s_tdsParserStateObjectFactory = s_systemDotData?.GetType("System.Data.SqlClient.TdsParserStateObjectFactory");
private static readonly PropertyInfo s_useManagedSNI = s_tdsParserStateObjectFactory?.GetProperty("UseManagedSNI", BindingFlags.Static | BindingFlags.Public);
private static readonly string[] s_azureSqlServerEndpoints = {".database.windows.net",
".database.cloudapi.de",
".database.usgovcloudapi.net",
".database.chinacloudapi.cn"};
static DataTestUtility()
{
NpConnStr = Environment.GetEnvironmentVariable("TEST_NP_CONN_STR");
@@ -64,6 +68,42 @@ namespace System.Data.SqlClient.ManualTesting.Tests
public static bool IsIntegratedSecuritySetup() => int.TryParse(Environment.GetEnvironmentVariable("TEST_INTEGRATEDSECURITY_SETUP"), out int result) ? result == 1 : false;
public static string getAccessToken()
{
return Environment.GetEnvironmentVariable("TEST_ACCESSTOKEN_SETUP");
}
public static bool IsAccessTokenSetup() => string.IsNullOrEmpty(getAccessToken()) ? false : true;
// This method assumes dataSource parameter is in TCP connection string format.
public static bool IsAzureSqlServer(string dataSource)
{
int i = dataSource.LastIndexOf(',');
if (i >= 0)
{
dataSource = dataSource.Substring(0, i);
}
i = dataSource.LastIndexOf('\\');
if (i >= 0)
{
dataSource = dataSource.Substring(0, i);
}
// trim redundant whitespace
dataSource = dataSource.Trim();
// check if servername end with any azure endpoints
for (i = 0; i < s_azureSqlServerEndpoints.Length; i++)
{
if (dataSource.EndsWith(s_azureSqlServerEndpoints[i], StringComparison.OrdinalIgnoreCase))
{
return true;
}
}
return false;
}
private static bool CheckException<TException>(Exception ex, string exceptionMessage, bool innerExceptionMustBeNull) where TException : Exception
{
return ((ex != null) && (ex is TException) &&

View File

@@ -0,0 +1,34 @@
// 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.Text.RegularExpressions;
using Xunit;
namespace System.Data.SqlClient.ManualTesting.Tests
{
public class AADAccessTokenTest
{
private static bool IsAccessTokenSetup() => DataTestUtility.IsAccessTokenSetup();
private static bool IsAzureServer() => DataTestUtility.IsAzureSqlServer(GetDataSource());
[ConditionalFact(nameof(IsAccessTokenSetup), nameof(IsAzureServer))]
public static void AccessTokenTest()
{
using (SqlConnection connection = new SqlConnection(DataTestUtility.TcpConnStr))
{
connection.AccessToken = DataTestUtility.getAccessToken();
connection.Open();
}
}
private static string GetDataSource()
{
// Obtain Data source from connection string
string tcpConnStr = DataTestUtility.TcpConnStr.Replace(" ", string.Empty);
Regex regex = new Regex("DataSource=(.*?);");
Match match = regex.Match(tcpConnStr);
return match.Groups[1].ToString();
}
}
}

View File

@@ -29,6 +29,7 @@
<Compile Include="DataCommon\DataTestUtility.cs" />
<Compile Include="DataCommon\ProxyServer.cs" />
<Compile Include="DataCommon\SystemDataResourceManager.cs" />
<Compile Include="SQL\ConnectivityTests\AADAccessTokenTest.cs" />
<Compile Include="SQL\ParameterTest\DateTimeVariantTest.cs" />
<Compile Include="SQL\ParameterTest\OutputParameter.cs" />
<Compile Include="SQL\ParameterTest\ParametersTest.cs" />