Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Merging common code bases DBConnectionOptions #1279

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@
<Compile Include="..\..\src\Microsoft\Data\Common\NameValuePair.cs">
<Link>Microsoft\Data\Common\NameValuePair.cs</Link>
</Compile>
<Compile Include="..\..\src\Microsoft\Data\Common\DbConnectionOptions.Common.cs">
<Link>Microsoft\Data\Common\DbConnectionOptions.Common.cs</Link>
</Compile>
<Compile Include="..\..\src\Microsoft\Data\SqlClient\DataClassification\SensitivityClassification.cs">
<Link>Microsoft\Data\SqlClient\DataClassification\SensitivityClassification.cs</Link>
</Compile>
Expand Down Expand Up @@ -438,9 +441,6 @@
</Compile>
<Compile Include="Microsoft\Data\Common\AdapterUtil.SqlClient.cs" />
<Compile Include="Microsoft\Data\Common\DbConnectionOptions.cs" />
<Compile Include="$(CommonPath)\Microsoft\Data\Common\DbConnectionOptions.Common.cs">
<Link>Microsoft\Data\Common\DbConnectionOptions.Common.cs</Link>
</Compile>
<Compile Include="Microsoft\Data\Common\DbConnectionStringCommon.cs" />
<Compile Include="$(CommonPath)\Microsoft\Data\ProviderBase\DbConnectionInternal.cs">
<Link>Common\Microsoft\Data\ProviderBase\DbConnectionInternal.cs</Link>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,113 +3,32 @@
// See the LICENSE file in the project root for more information.

using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Text;

namespace Microsoft.Data.Common
{
internal partial class DbConnectionOptions
{
// instances of this class are intended to be immutable, i.e readonly
// used by pooling classes so it is much easier to verify correctness
// when not worried about the class being modified during execution

public DbConnectionOptions(string connectionString, Dictionary<string, string> synonyms)
{
_parsetable = new Dictionary<string, string>();
_usersConnectionString = ((null != connectionString) ? connectionString : "");

// first pass on parsing, initial syntax check
if (0 < _usersConnectionString.Length)
{
_keyChain = ParseInternal(_parsetable, _usersConnectionString, true, synonyms, false);
HasPasswordKeyword = (_parsetable.ContainsKey(KEY.Password) || _parsetable.ContainsKey(SYNONYM.Pwd));
HasUserIdKeyword = (_parsetable.ContainsKey(KEY.User_ID) || _parsetable.ContainsKey(SYNONYM.UID));
}
}

protected DbConnectionOptions(DbConnectionOptions connectionOptions)
{ // Clone used by SqlConnectionString
_usersConnectionString = connectionOptions._usersConnectionString;
_parsetable = connectionOptions._parsetable;
_keyChain = connectionOptions._keyChain;
HasPasswordKeyword = connectionOptions.HasPasswordKeyword;
HasUserIdKeyword = connectionOptions.HasUserIdKeyword;
}

public bool IsEmpty => _keyChain == null;

internal bool TryGetParsetableValue(string key, out string value) => _parsetable.TryGetValue(key, out value);

// same as Boolean, but with SSPI thrown in as valid yes
public bool ConvertValueToIntegratedSecurity()
internal string ExpandAttachDbFileName(string replacementValue)
{
string value;
return _parsetable.TryGetValue(KEY.Integrated_Security, out value) && value != null ?
ConvertValueToIntegratedSecurityInternal(value) :
false;
}
int copyPosition = 0;

internal bool ConvertValueToIntegratedSecurityInternal(string stringValue)
{
if (CompareInsensitiveInvariant(stringValue, "sspi") || CompareInsensitiveInvariant(stringValue, "true") || CompareInsensitiveInvariant(stringValue, "yes"))
return true;
else if (CompareInsensitiveInvariant(stringValue, "false") || CompareInsensitiveInvariant(stringValue, "no"))
return false;
else
StringBuilder builder = new(_usersConnectionString.Length);
for (NameValuePair current = _keyChain; null != current; current = current.Next)
{
string tmp = stringValue.Trim(); // Remove leading & trailing whitespace.
if (CompareInsensitiveInvariant(tmp, "sspi") || CompareInsensitiveInvariant(tmp, "true") || CompareInsensitiveInvariant(tmp, "yes"))
return true;
else if (CompareInsensitiveInvariant(tmp, "false") || CompareInsensitiveInvariant(tmp, "no"))
return false;
if (string.Equals(current.Name, DbConnectionStringKeywords.AttachDBFilename, StringComparison.InvariantCultureIgnoreCase))
{
builder.Append($"{current.Name}={replacementValue};");
}
else
{
throw ADP.InvalidConnectionOptionValue(KEY.Integrated_Security);
builder.Append(_usersConnectionString, copyPosition, current.Length);
}
copyPosition += current.Length;
}
}

public int ConvertValueToInt32(string keyName, int defaultValue)
{
string value;
return _parsetable.TryGetValue(keyName, out value) && value != null ?
ConvertToInt32Internal(keyName, value) :
defaultValue;
}

internal static int ConvertToInt32Internal(string keyname, string stringValue)
{
try
{
return int.Parse(stringValue, System.Globalization.NumberStyles.Integer, CultureInfo.InvariantCulture);
}
catch (FormatException e)
{
throw ADP.InvalidConnectionOptionValue(keyname, e);
}
catch (OverflowException e)
{
throw ADP.InvalidConnectionOptionValue(keyname, e);
}
}

public string ConvertValueToString(string keyName, string defaultValue)
{
string value;
return _parsetable.TryGetValue(keyName, out value) && value != null ? value : defaultValue;
}

public bool ContainsKey(string keyword)
{
return _parsetable.ContainsKey(keyword);
}

protected internal virtual string Expand()
{
return _usersConnectionString;
return builder.ToString();
}

// SxS notes:
Expand Down Expand Up @@ -151,25 +70,5 @@ internal static string ExpandDataDirectory(string keyword, string value)
return fullPath;
}

internal string ExpandAttachDbFileName(string replacementValue)
{
int copyPosition = 0;

StringBuilder builder = new StringBuilder(_usersConnectionString.Length);
for (NameValuePair current = _keyChain; null != current; current = current.Next)
{
if (current.Name == KEY.AttachDBFileName)
{
builder.Append($"{KEY.AttachDBFileName}={replacementValue};");
}
else
{
builder.Append(_usersConnectionString, copyPosition, current.Length);
}
copyPosition += current.Length;
}

return builder.ToString();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -1820,7 +1820,7 @@ private bool TryOpen(TaskCompletionSource<DbConnectionInternal> retry, SqlConnec
(connectionOptions.Authentication == SqlAuthenticationMethod.SqlPassword ||
connectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryPassword ||
connectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryServicePrincipal) &&
(!connectionOptions.HasUserIdKeyword || !connectionOptions.HasPasswordKeyword) &&
(!connectionOptions._hasUserIdKeyword || !connectionOptions._hasPasswordKeyword) &&
_credential == null)
{
throw SQL.CredentialsNotProvided(connectionOptions.Authentication);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -456,32 +456,32 @@ internal SqlConnectionString(string connectionString) : base(connectionString, G
throw SQL.AuthenticationAndIntegratedSecurity();
}

if (Authentication == SqlClient.SqlAuthenticationMethod.ActiveDirectoryIntegrated && HasPasswordKeyword)
if (Authentication == SqlClient.SqlAuthenticationMethod.ActiveDirectoryIntegrated && _hasPasswordKeyword)
{
throw SQL.IntegratedWithPassword();
}

if (Authentication == SqlAuthenticationMethod.ActiveDirectoryInteractive && HasPasswordKeyword)
if (Authentication == SqlAuthenticationMethod.ActiveDirectoryInteractive && _hasPasswordKeyword)
{
throw SQL.InteractiveWithPassword();
}

if (Authentication == SqlAuthenticationMethod.ActiveDirectoryDeviceCodeFlow && (HasUserIdKeyword || HasPasswordKeyword))
if (Authentication == SqlAuthenticationMethod.ActiveDirectoryDeviceCodeFlow && (_hasUserIdKeyword || _hasPasswordKeyword))
{
throw SQL.DeviceFlowWithUsernamePassword();
}

if (Authentication == SqlAuthenticationMethod.ActiveDirectoryManagedIdentity && HasPasswordKeyword)
if (Authentication == SqlAuthenticationMethod.ActiveDirectoryManagedIdentity && _hasPasswordKeyword)
{
throw SQL.NonInteractiveWithPassword(DbConnectionStringBuilderUtil.ActiveDirectoryManagedIdentityString);
}

if (Authentication == SqlAuthenticationMethod.ActiveDirectoryMSI && HasPasswordKeyword)
if (Authentication == SqlAuthenticationMethod.ActiveDirectoryMSI && _hasPasswordKeyword)
{
throw SQL.NonInteractiveWithPassword(DbConnectionStringBuilderUtil.ActiveDirectoryMSIString);
}

if (Authentication == SqlAuthenticationMethod.ActiveDirectoryDefault && HasPasswordKeyword)
if (Authentication == SqlAuthenticationMethod.ActiveDirectoryDefault && _hasPasswordKeyword)
{
throw SQL.NonInteractiveWithPassword(DbConnectionStringBuilderUtil.ActiveDirectoryDefaultString);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2117,7 +2117,7 @@ internal void OnLoginAck(SqlLoginAck rec)
/// <param name="fedAuthInfo">Federated Authentication Info.</param>
internal void OnFedAuthInfo(SqlFedAuthInfo fedAuthInfo)
{
Debug.Assert((ConnectionOptions.HasUserIdKeyword && ConnectionOptions.HasPasswordKeyword)
Debug.Assert((ConnectionOptions._hasUserIdKeyword && ConnectionOptions._hasPasswordKeyword)
|| _credential != null
|| ConnectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryInteractive
|| ConnectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryDeviceCodeFlow
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,9 @@
<Compile Include="..\..\src\Microsoft\Data\Common\NameValuePair.cs">
<Link>Microsoft\Data\Common\NameValuePair.cs</Link>
</Compile>
<Compile Include="..\..\src\Microsoft\Data\Common\DbConnectionOptions.Common.cs">
<Link>Microsoft\Data\Common\DbConnectionOptions.Common.cs</Link>
</Compile>
<Compile Include="..\..\src\Microsoft\Data\Sql\SqlNotificationRequest.cs">
<Link>Microsoft\Data\Sql\SqlNotificationRequest.cs</Link>
</Compile>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,16 +23,16 @@ internal sealed class DBConnectionString

private static class KEY
{
internal const string Password = "password";
internal const string PersistSecurityInfo = "persist security info";
internal const string Pwd = "pwd";
internal const string Password = DbConnectionStringKeywords.Password;
internal const string PersistSecurityInfo = DbConnectionStringKeywords.PersistSecurityInfo;
internal const string Pwd = DbConnectionStringSynonyms.Pwd;
};

// this class is serializable with Everett, so ugly field names can't be changed
readonly private string _encryptedUsersConnectionString;

// hash of unique keys to values
readonly private Hashtable _parsetable;
readonly private Dictionary<string, string> _parsetable;

// a linked list of key/value and their length in _encryptedUsersConnectionString
readonly private NameValuePair _keychain;
Expand All @@ -52,21 +52,21 @@ private static class KEY
readonly private string _encryptedActualConnectionString;
#pragma warning restore 169

internal DBConnectionString(string value, string restrictions, KeyRestrictionBehavior behavior, Hashtable synonyms, bool useOdbcRules)
: this(new DbConnectionOptions(value, synonyms, useOdbcRules), restrictions, behavior, synonyms, false)
internal DBConnectionString(string value, string restrictions, KeyRestrictionBehavior behavior, Dictionary<string, string> synonyms, bool useOdbcRules)
: this(new DbConnectionOptions(value, synonyms), restrictions, behavior, synonyms, false)
{
// useOdbcRules is only used to parse the connection string, not to parse restrictions because values don't apply there
// the hashtable doesn't need clone since it isn't shared with anything else
}

internal DBConnectionString(DbConnectionOptions connectionOptions)
: this(connectionOptions, (string)null, KeyRestrictionBehavior.AllowOnly, (Hashtable)null, true)
: this(connectionOptions, (string)null, KeyRestrictionBehavior.AllowOnly, null, true)
{
// used by DBDataPermission to convert from DbConnectionOptions to DBConnectionString
// since backward compatibility requires Everett level classes
}

private DBConnectionString(DbConnectionOptions connectionOptions, string restrictions, KeyRestrictionBehavior behavior, Hashtable synonyms, bool mustCloneDictionary)
private DBConnectionString(DbConnectionOptions connectionOptions, string restrictions, KeyRestrictionBehavior behavior, Dictionary<string, string> synonyms, bool mustCloneDictionary)
{ // used by DBDataPermission
Debug.Assert(null != connectionOptions, "null connectionOptions");
switch (behavior)
Expand All @@ -81,9 +81,9 @@ private DBConnectionString(DbConnectionOptions connectionOptions, string restric

// grab all the parsed details from DbConnectionOptions
_encryptedUsersConnectionString = connectionOptions.UsersConnectionString(false);
_hasPassword = connectionOptions.HasPasswordKeyword;
_hasPassword = connectionOptions._hasPasswordKeyword;
_parsetable = connectionOptions.Parsetable;
_keychain = connectionOptions.KeyChain;
_keychain = connectionOptions._keyChain;

// we do not want to serialize out user password unless directed so by "persist security info=true"
// otherwise all instances of user's password will be replaced with "*"
Expand All @@ -94,7 +94,7 @@ private DBConnectionString(DbConnectionOptions connectionOptions, string restric
{
// clone the hashtable to replace user's password/pwd value with "*"
// we only need to clone if coming from DbConnectionOptions and password exists
_parsetable = (Hashtable)_parsetable.Clone();
_parsetable = new Dictionary<string, string>(_parsetable, _parsetable.Comparer);
}

// different than Everett in that instead of removing password/pwd from
Expand Down Expand Up @@ -467,7 +467,7 @@ static private string[] NoDuplicateUnion(string[] a, string[] b)
return restrictionValues;
}

private static string[] ParseRestrictions(string restrictions, Hashtable synonyms)
private static string[] ParseRestrictions(string restrictions, Dictionary<string, string> synonyms)
{
#if DEBUG
SqlClientEventSource.Log.TryAdvancedTraceEvent("<comm.DBConnectionString|INFO|ADV> Restrictions='{0}'", restrictions);
Expand Down
Loading