#region License // The PostgreSQL License // // Copyright (C) 2015 The Npgsql Development Team // // Permission to use, copy, modify, and distribute this software and its // documentation for any purpose, without fee, and without a written // agreement is hereby granted, provided that the above copyright notice // and this paragraph and the following two paragraphs appear in all copies. // // IN NO EVENT SHALL THE NPGSQL DEVELOPMENT TEAM BE LIABLE TO ANY PARTY // FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, // INCLUDING LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS // DOCUMENTATION, EVEN IF THE NPGSQL DEVELOPMENT TEAM HAS BEEN ADVISED OF // THE POSSIBILITY OF SUCH DAMAGE. // // THE NPGSQL DEVELOPMENT TEAM SPECIFICALLY DISCLAIMS ANY WARRANTIES, // INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY // AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS // ON AN "AS IS" BASIS, AND THE NPGSQL DEVELOPMENT TEAM HAS NO OBLIGATIONS // TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. #endregion using System; using System.Collections.Generic; using System.ComponentModel; using System.Data.Common; using System.Diagnostics.Contracts; using System.Linq; using System.Reflection; using JetBrains.Annotations; #if NET45 || NET451 || DNX451 using System.DirectoryServices; using System.Security.Principal; #endif namespace Npgsql { /// /// Provides a simple way to create and manage the contents of connection strings used by /// the class. /// public sealed class NpgsqlConnectionStringBuilder : DbConnectionStringBuilder { #region Fields /// /// Makes all valid keywords for a property to that property (e.g. User Name -> Username, UserId -> Username...) /// static readonly Dictionary PropertiesByKeyword; /// /// Maps CLR property names (e.g. BufferSize) to their canonical keyword name, which is the /// property's [DisplayName] (e.g. Buffer Size) /// static readonly Dictionary PropertyNameToCanonicalKeyword; /// /// Maps each property to its [DefaultValue] /// static readonly Dictionary PropertyDefaults; static readonly string[] Empty = new string[0]; #endregion #region Constructors /// /// Initializes a new instance of the NpgsqlConnectionStringBuilder class. /// public NpgsqlConnectionStringBuilder() { Init(); } #if NET45 || NET451 || DNX451 /// /// Initializes a new instance of the NpgsqlConnectionStringBuilder class, optionally using ODBC rules for quoting values. /// /// true to use {} to delimit fields; false to use quotation marks. public NpgsqlConnectionStringBuilder(bool useOdbcRules) : base(useOdbcRules) { Init(); } #endif /// /// Initializes a new instance of the NpgsqlConnectionStringBuilder class and sets its . /// public NpgsqlConnectionStringBuilder(string connectionString) { Init(); ConnectionString = connectionString; } void Init() { foreach (var kv in PropertyDefaults) { kv.Key.SetValue(this, kv.Value); base.Clear(); } } #endregion #region Static initialization static NpgsqlConnectionStringBuilder() { var properties = typeof(NpgsqlConnectionStringBuilder) .GetProperties() .Where(p => p.GetCustomAttribute() != null) .ToArray(); Contract.Assume(properties.All(p => p.CanRead && p.CanWrite)); Contract.Assume(properties.All(p => p.GetCustomAttribute() != null)); PropertiesByKeyword = ( from p in properties let displayName = p.GetCustomAttribute().DisplayName.ToUpperInvariant() let propertyName = p.Name.ToUpperInvariant() from k in new[] { displayName } .Concat(propertyName != displayName ? new[] { propertyName } : Empty ) .Concat(p.GetCustomAttribute().Aliases .Select(a => a.ToUpperInvariant()) ) .Select(k => new { Property = p, Keyword = k }) select k ).ToDictionary(t => t.Keyword, t => t.Property); PropertyNameToCanonicalKeyword = properties.ToDictionary( p => p.Name, p => p.GetCustomAttribute().DisplayName ); PropertyDefaults = properties .Where(p => p.GetCustomAttribute() == null) .ToDictionary( p => p, p => p.GetCustomAttribute() != null ? p.GetCustomAttribute().Value : (p.PropertyType.GetTypeInfo().IsValueType ? Activator.CreateInstance(p.PropertyType) : null) ); } #endregion #region Non-static property handling /// /// Gets or sets the value associated with the specified key. /// /// The key of the item to get or set. /// The value associated with the specified key. public override object this[string keyword] { get { object value; if (!TryGetValue(keyword, out value)) { throw new ArgumentException("Keyword not supported: " + keyword, nameof(keyword)); } return value; } set { if (value == null) { Remove(keyword); return; } var p = GetProperty(keyword); try { object convertedValue; if (p.PropertyType.GetTypeInfo().IsEnum && value is string) { convertedValue = Enum.Parse(p.PropertyType, (string)value); } else { convertedValue = Convert.ChangeType(value, p.PropertyType); } p.SetValue(this, convertedValue); } catch (Exception e) { throw new ArgumentException("Couldn't set " + keyword, keyword, e); } } } /// /// Removes the entry with the specified key from the DbConnectionStringBuilder instance. /// /// The key of the key/value pair to be removed from the connection string in this DbConnectionStringBuilder. /// true if the key existed within the connection string and was removed; false if the key did not exist. public override bool Remove(string keyword) { var p = GetProperty(keyword); var removed = base.ContainsKey(p.Name); // Note that string property setters call SetValue, which itself calls base.Remove(). p.SetValue(this, PropertyDefaults[p]); base.Remove(p.Name); return removed; } /// /// Clears the contents of the instance. /// public override void Clear() { Contract.Assert(Keys != null); foreach (var k in Keys.Cast().ToArray()) { Remove(k); } } /// /// Determines whether the contains a specific key. /// /// The key to locate in the . /// true if the contains an entry with the specified key; otherwise false. public override bool ContainsKey(string keyword) { if (keyword == null) throw new ArgumentNullException(nameof(keyword)); Contract.EndContractBlock(); return PropertiesByKeyword.ContainsKey(keyword.ToUpperInvariant()); } PropertyInfo GetProperty(string keyword) { PropertyInfo p; if (!PropertiesByKeyword.TryGetValue(keyword.ToUpperInvariant(), out p)) { throw new ArgumentException("Keyword not supported: " + keyword, nameof(keyword)); } return p; } /// /// Retrieves a value corresponding to the supplied key from this . /// /// The key of the item to retrieve. /// The value corresponding to the key. /// true if keyword was found within the connection string, false otherwise. public override bool TryGetValue(string keyword, [CanBeNull] out object value) { if (keyword == null) throw new ArgumentNullException(nameof(keyword)); Contract.EndContractBlock(); PropertyInfo p; if (!PropertiesByKeyword.TryGetValue(keyword.ToUpperInvariant(), out p)) { value = null; return false; } value = GetProperty(keyword).GetValue(this) ?? ""; return true; } void SetValue(string propertyName, [CanBeNull] object value) { var canonicalKeyword = PropertyNameToCanonicalKeyword[propertyName]; if (value == null) { base.Remove(canonicalKeyword); } else { base[canonicalKeyword] = value; } } #endregion #region Properties - Connection /// /// The hostname or IP address of the PostgreSQL server to connect to. /// [Category("Connection")] [Description("The hostname or IP address of the PostgreSQL server to connect to.")] [DisplayName("Host")] [NpgsqlConnectionStringProperty("Server")] public string Host { get { return _host; } set { _host = value; SetValue(nameof(Host), value); } } string _host; /// /// The TCP/IP port of the PostgreSQL server. /// [Category("Connection")] [Description("The TCP port of the PostgreSQL server.")] [DisplayName("Port")] [NpgsqlConnectionStringProperty] [DefaultValue(NpgsqlConnection.DefaultPort)] public int Port { get { return _port; } set { if (value <= 0) throw new ArgumentOutOfRangeException(nameof(value), value, "Invalid port: " + value); Contract.EndContractBlock(); _port = value; SetValue(nameof(Port), value); } } int _port; /// /// The PostgreSQL database to connect to. /// [Category("Connection")] [Description("The PostgreSQL database to connect to.")] [DisplayName("Database")] [NpgsqlConnectionStringProperty("DB")] public string Database { get { return _database; } set { _database = value; SetValue(nameof(Database), value); } } string _database; /// /// The username to connect with. Not required if using IntegratedSecurity. /// [Category("Connection")] [Description("The username to connect with. Not required if using IntegratedSecurity.")] [DisplayName("Username")] [NpgsqlConnectionStringProperty("User Name", "UserId", "User Id", "UID")] public string Username { get { #if NET45 || NET451 || DNX451 if ((_integratedSecurity) && (String.IsNullOrEmpty(_username))) { _username = GetIntegratedUserName(); } #endif return _username; } set { _username = value; SetValue(nameof(Username), value); } } string _username; /// /// The password to connect with. Not required if using IntegratedSecurity. /// [Category("Connection")] [Description("The password to connect with. Not required if using IntegratedSecurity.")] [PasswordPropertyText(true)] [DisplayName("Password")] [NpgsqlConnectionStringProperty("PSW", "PWD")] public string Password { get { return _password; } set { _password = value; SetValue(nameof(Password), value); } } string _password; /// /// The optional application name parameter to be sent to the backend during connection initiation. /// [Category("Connection")] [Description("The optional application name parameter to be sent to the backend during connection initiation")] [DisplayName("Application Name")] [NpgsqlConnectionStringProperty] public string ApplicationName { get { return _applicationName; } set { _applicationName = value; SetValue(nameof(ApplicationName), value); } } string _applicationName; /// /// Whether to enlist in an ambient TransactionScope. /// [Category("Connection")] [Description("Whether to enlist in an ambient TransactionScope.")] [DisplayName("Enlist")] [NpgsqlConnectionStringProperty] public bool Enlist { get { return _enlist; } set { _enlist = value; SetValue(nameof(Enlist), value); } } bool _enlist; /// /// Gets or sets the schema search path. /// [Category("Connection")] [Description("Gets or sets the schema search path.")] [DisplayName("Search Path")] [NpgsqlConnectionStringProperty] public string SearchPath { get { return _searchpath; } set { _searchpath = value; SetValue(nameof(SearchPath), value); } } string _searchpath; #endregion #region Properties - Security /// /// Controls whether SSL is required, disabled or preferred, depending on server support. /// [Category("Security")] [Description("Controls whether SSL is required, disabled or preferred, depending on server support.")] [DisplayName("SSL Mode")] [NpgsqlConnectionStringProperty] public SslMode SslMode { get { return _sslmode; } set { _sslmode = value; SetValue(nameof(SslMode), value); } } SslMode _sslmode; /// /// Whether to trust the server certificate without validating it. /// [Category("Security")] [Description("Whether to trust the server certificate without validating it.")] [DisplayName("Trust Server Certificate")] [NpgsqlConnectionStringProperty] public bool TrustServerCertificate { get { return _trustServerCertificate; } set { _trustServerCertificate = value; SetValue(nameof(TrustServerCertificate), value); } } bool _trustServerCertificate; /// /// Npgsql uses its own internal implementation of TLS/SSL. Turn this on to use .NET SslStream instead. /// [Category("Security")] [Description("Npgsql uses its own internal implementation of TLS/SSL. Turn this on to use .NET SslStream instead.")] [DisplayName("Use SSL Stream")] [NpgsqlConnectionStringProperty] public bool UseSslStream { get { return _useSslStream; } set { _useSslStream = value; SetValue(nameof(UseSslStream), value); } } bool _useSslStream; /// /// Whether to use Windows integrated security to log in. /// [Category("Security")] [Description("Whether to use Windows integrated security to log in.")] [DisplayName("Integrated Security")] [NpgsqlConnectionStringProperty] public bool IntegratedSecurity { get { return _integratedSecurity; } set { #if !NET40 if (value) CheckIntegratedSecuritySupport(); #endif _integratedSecurity = value; SetValue(nameof(IntegratedSecurity), value); } } bool _integratedSecurity; /// /// The Kerberos service name to be used for authentication. /// [Category("Security")] [Description("The Kerberos service name to be used for authentication.")] [DisplayName("Kerberos Service Name")] [NpgsqlConnectionStringProperty("Krbsrvname")] public string KerberosServiceName { get { return _kerberosServiceName; } set { _kerberosServiceName = value; SetValue(nameof(KerberosServiceName), value); } } string _kerberosServiceName; /// /// The Kerberos realm to be used for authentication. /// [Category("Security")] [Description("The Kerberos realm to be used for authentication.")] [DisplayName("Include Realm")] [NpgsqlConnectionStringProperty] public bool IncludeRealm { get { return _includeRealm; } set { _includeRealm = value; SetValue(nameof(IncludeRealm), value); } } bool _includeRealm; /// /// Gets or sets a Boolean value that indicates if security-sensitive information, such as the password, is not returned as part of the connection if the connection is open or has ever been in an open state. /// [Category("Security")] [Description("Gets or sets a Boolean value that indicates if security-sensitive information, such as the password, is not returned as part of the connection if the connection is open or has ever been in an open state.")] [DisplayName("Persist Security Info")] [NpgsqlConnectionStringProperty] public bool PersistSecurityInfo { get { return _persistSecurityInfo; } set { _persistSecurityInfo = value; SetValue(nameof(PersistSecurityInfo), value); } } bool _persistSecurityInfo; #endregion #region Properties - Pooling /// /// Whether connection pooling should be used. /// [Category("Pooling")] [Description("Whether connection pooling should be used.")] [DisplayName("Pooling")] [NpgsqlConnectionStringProperty] [DefaultValue(true)] public bool Pooling { get { return _pooling; } set { _pooling = value; SetValue(nameof(Pooling), value); } } bool _pooling; /// /// The minimum connection pool size. /// [Category("Pooling")] [Description("The minimum connection pool size.")] [DisplayName("Minimum Pool Size")] [NpgsqlConnectionStringProperty] [DefaultValue(1)] public int MinPoolSize { get { return _minPoolSize; } set { if (value < 0 || value > NpgsqlConnectorPool.PoolSizeLimit) throw new ArgumentOutOfRangeException(nameof(value), value, "MinPoolSize must be between 0 and " + NpgsqlConnectorPool.PoolSizeLimit); Contract.EndContractBlock(); _minPoolSize = value; SetValue(nameof(MinPoolSize), value); } } int _minPoolSize; /// /// The maximum connection pool size. /// [Category("Pooling")] [Description("The maximum connection pool size.")] [DisplayName("Maximum Pool Size")] [NpgsqlConnectionStringProperty] [DefaultValue(20)] public int MaxPoolSize { get { return _maxPoolSize; } set { if (value < 0 || value > NpgsqlConnectorPool.PoolSizeLimit) throw new ArgumentOutOfRangeException(nameof(value), value, "MaxPoolSize must be between 0 and " + NpgsqlConnectorPool.PoolSizeLimit); Contract.EndContractBlock(); _maxPoolSize = value; SetValue(nameof(MaxPoolSize), value); } } int _maxPoolSize; /// /// The time to wait before closing unused connections in the pool if the count /// of all connections exeeds MinPoolSize. /// /// /// If connection pool contains unused connections for ConnectionLifeTime seconds, /// the half of them will be closed. If there will be unused connections in a second /// later then again the half of them will be closed and so on. /// This strategy provide smooth change of connection count in the pool. /// /// The time (in seconds) to wait. The default value is 15 seconds. [Category("Pooling")] [Description("The time to wait before closing unused connections in the pool if the count of all connections exeeds MinPoolSize.")] [DisplayName("Connection Lifetime")] [NpgsqlConnectionStringProperty] [DefaultValue(15)] public int ConnectionLifeTime { get { return _connectionLifeTime; } set { _connectionLifeTime = value; SetValue(nameof(ConnectionLifeTime), value); } } int _connectionLifeTime; #endregion #region Properties - Timeouts /// /// The time to wait (in seconds) while trying to establish a connection before terminating the attempt and generating an error. /// Defaults to 15 seconds. /// [Category("Timeouts")] [Description("The time to wait (in seconds) while trying to establish a connection before terminating the attempt and generating an error.")] [DisplayName("Timeout")] [NpgsqlConnectionStringProperty] [DefaultValue(15)] public int Timeout { get { return _timeout; } set { if (value < 0 || value > NpgsqlConnection.TimeoutLimit) throw new ArgumentOutOfRangeException(nameof(value), value, "Timeout must be between 0 and " + NpgsqlConnection.TimeoutLimit); Contract.EndContractBlock(); _timeout = value; SetValue(nameof(Timeout), value); } } int _timeout; /// /// The time to wait (in seconds) while trying to execute a command before terminating the attempt and generating an error. /// Defaults to 30 seconds. /// [Category("Timeouts")] [Description("The time to wait (in seconds) while trying to execute a command before terminating the attempt and generating an error. Set to zero for infinity.")] [DisplayName("Command Timeout")] [NpgsqlConnectionStringProperty] [DefaultValue(NpgsqlCommand.DefaultTimeout)] public int CommandTimeout { get { return _commandTimeout; } set { if (value < 0) throw new ArgumentOutOfRangeException(nameof(value), value, "CommandTimeout can't be negative"); Contract.EndContractBlock(); _commandTimeout = value; SetValue(nameof(CommandTimeout), value); } } int _commandTimeout; /// /// The time to wait (in seconds) while trying to execute a an internal command before terminating the attempt and generating an error. /// [Category("Timeouts")] [Description("The time to wait (in seconds) while trying to execute a an internal command before terminating the attempt and generating an error. -1 uses CommandTimeout, 0 means no timeout.")] [DisplayName("Internal Command Timeout")] [NpgsqlConnectionStringProperty] [DefaultValue(-1)] public int InternalCommandTimeout { get { return _internalCommandTimeout; } set { if (value != 0 && value != -1 && value < NpgsqlConnector.MinimumInternalCommandTimeout) throw new ArgumentOutOfRangeException(nameof(value), value, $"InternalCommandTimeout must be >= {NpgsqlConnector.MinimumInternalCommandTimeout}, 0 (infinite) or -1 (use CommandTimeout)"); Contract.EndContractBlock(); _internalCommandTimeout = value; SetValue(nameof(InternalCommandTimeout), value); } } int _internalCommandTimeout; /// /// Whether to have the backend enforce and /// via the statement_timeout variable. Defaults to true. /// [Category("Timeouts")] [Description("Whether to have the backend enforce CommandTimeout and InternalCommandTimeout via the statement_timeout variable.")] [DisplayName("Backend Timeouts")] [NpgsqlConnectionStringProperty] [DefaultValue(true)] public bool BackendTimeouts { get { return _backendTimeouts; } set { _backendTimeouts = value; SetValue(nameof(BackendTimeouts), value); } } bool _backendTimeouts; #endregion #region Properties - Entity Framework /// /// The database template to specify when creating a database in Entity Framework. If not specified, /// PostgreSQL defaults to "template1". /// /// /// http://www.postgresql.org/docs/current/static/manage-ag-templatedbs.html /// [Category("Entity Framework")] [Description("The database template to specify when creating a database in Entity Framework. If not specified, PostgreSQL defaults to \"template1\".")] [DisplayName("EF Template Database")] [NpgsqlConnectionStringProperty] public string EntityTemplateDatabase { get { return _entityTemplateDatabase; } set { _entityTemplateDatabase = value; SetValue(nameof(EntityTemplateDatabase), value); } } string _entityTemplateDatabase; /// /// The database admin to specify when creating and dropping a database in Entity Framework. This is needed because /// Npgsql needs to connect to a database in order to send the create/drop database command. /// If not specified, defaults to "template1". Check NpgsqlServices.UsingPostgresDBConnection for more information. /// [Category("Entity Framework")] [Description("The database admin to specify when creating and dropping a database in Entity Framework. If not specified, defaults to \"template1\".")] [DisplayName("EF Admin Database")] [NpgsqlConnectionStringProperty] public string EntityAdminDatabase { get { return _entityAdminDatabase; } set { _entityAdminDatabase = value; SetValue(nameof(EntityAdminDatabase), value); } } string _entityAdminDatabase; #endregion #region Properties - Advanced /// /// Whether to process messages that arrive between command activity. /// [Category("Advanced")] [Description("Whether to process messages that arrive between command activity.")] [DisplayName("Continuous Processing")] [NpgsqlConnectionStringProperty("SyncNotification")] public bool ContinuousProcessing { get { return _continuousProcessing; } set { _continuousProcessing = value; SetValue(nameof(ContinuousProcessing), value); } } bool _continuousProcessing; /// /// The number of seconds of connection inactivity before Npgsql sends a keepalive query. /// Set to 0 (the default) to disable. /// [Category("Advanced")] [Description("The number of seconds of connection inactivity before Npgsql sends a keepalive query.")] [DisplayName("Keepalive")] [NpgsqlConnectionStringProperty] public int KeepAlive { get { return _keepAlive; } set { if (value < 0) throw new ArgumentOutOfRangeException(nameof(value), value, "KeepAlive can't be negative"); Contract.EndContractBlock(); _keepAlive = value; SetValue(nameof(KeepAlive), value); } } int _keepAlive; /// /// Gets or sets the buffer size. /// [Category("Advanced")] [Description("Determines the size of the internal buffer Npgsql uses when reading or writing. Increasing may improve performance if transferring large values from the database.")] [DisplayName("Buffer Size")] [NpgsqlConnectionStringProperty] [DefaultValue(NpgsqlBuffer.DefaultBufferSize)] public int BufferSize { get { return _bufferSize; } set { _bufferSize = value; SetValue(nameof(BufferSize), value); } } int _bufferSize; #endregion #region Properties - Compatibility /// /// A compatibility mode for special PostgreSQL server types. /// [Category("Compatibility")] [Description("A compatibility mode for special PostgreSQL server types.")] [DisplayName("Server Compatibility Mode")] [NpgsqlConnectionStringProperty] public ServerCompatibilityMode ServerCompatibilityMode { get { return _serverCompatibilityMode; } set { _serverCompatibilityMode = value; SetValue(nameof(ServerCompatibilityMode), value); } } ServerCompatibilityMode _serverCompatibilityMode; /// /// Makes MaxValue and MinValue timestamps and dates readable as infinity and negative infinity. /// [Category("Compatibility")] [Description("Makes MaxValue and MinValue timestamps and dates readable as infinity and negative infinity.")] [DisplayName("Convert Infinity DateTime")] [NpgsqlConnectionStringProperty] public bool ConvertInfinityDateTime { get { return _convertInfinityDateTime; } set { _convertInfinityDateTime = value; SetValue(nameof(ConvertInfinityDateTime), value); } } bool _convertInfinityDateTime; #endregion #region Properties - Obsolete /// /// Obsolete, see https://github.com/npgsql/Npgsql/wiki/PreloadReader-Removal /// [Category("Obsolete")] [Description("Obsolete, see https://github.com/npgsql/Npgsql/wiki/PreloadReader-Removal")] [DisplayName("Preload Reader")] [NpgsqlConnectionStringProperty] [Obsolete] public bool PreloadReader { get { return false; } set { throw new NotSupportedException("The PreloadReader parameter is no longer supported. Please see https://github.com/npgsql/Npgsql/wiki/PreloadReader-Removal"); } } /// /// Obsolete, see https://github.com/npgsql/Npgsql/wiki/UseExtendedTypes-Removal /// [Category("Obsolete")] [Description("Obsolete, see https://github.com/npgsql/Npgsql/wiki/UseExtendedTypes-Removal")] [DisplayName("Use Extended Types")] [NpgsqlConnectionStringProperty] [Obsolete] public bool UseExtendedTypes { get { return false; } set { throw new NotSupportedException("The UseExtendedTypes parameter is no longer supported. Please see https://github.com/npgsql/Npgsql/wiki/UseExtendedTypes-Removal"); } } #endregion #region Integrated security support /// /// No integrated security if we're on mono and .NET 4.5 because of ClaimsIdentity, /// see https://github.com/npgsql/Npgsql/issues/133 /// static void CheckIntegratedSecuritySupport() { if (Type.GetType("Mono.Runtime") != null) throw new NotSupportedException("IntegratedSecurity is currently unsupported on mono and .NET 4.5 (see https://github.com/npgsql/Npgsql/issues/133)"); } #if NET45 || NET451 || DNX451 class CachedUpn { public string Upn; public DateTime ExpiryTimeUtc; } static readonly Dictionary CachedUpns = new Dictionary(); private string GetWindowsIdentityUserName() { var identity = WindowsIdentity.GetCurrent(); return identity == null ? string.Empty : identity.Name.Split('\\')[1]; } [CanBeNull] string GetIntegratedUserName() { // Side note: This maintains the hack fix mentioned before for https://github.com/npgsql/Npgsql/issues/133. // In a nutshell, starting with .NET 4.5 WindowsIdentity inherits from ClaimsIdentity // which doesn't exist in mono, and calling a WindowsIdentity method bombs. // The workaround is that this function that actually deals with WindowsIdentity never // gets called on mono, so never gets JITted and the problem goes away. // Gets the current user's username for integrated security purposes var identity = WindowsIdentity.GetCurrent(); if (identity?.User == null) { return null; } CachedUpn cachedUpn; string upn = null; // Check to see if we already have this UPN cached lock (CachedUpns) { if (CachedUpns.TryGetValue(identity.User, out cachedUpn)) { if (cachedUpn.ExpiryTimeUtc > DateTime.UtcNow) upn = cachedUpn.Upn; else CachedUpns.Remove(identity.User); } } try { if (upn == null) { // Try to get the user's UPN in its correct case; this is what the // server will need to verify against a Kerberos/SSPI ticket // If the computer does not belong to a domain, returns Empty. string domainName = System.Net.NetworkInformation.IPGlobalProperties.GetIPGlobalProperties().DomainName; if (domainName.Equals(string.Empty)) { return GetWindowsIdentityUserName(); } // First, find a domain server we can talk to string domainHostName; using (DirectoryEntry rootDse = new DirectoryEntry("LDAP://rootDSE") { AuthenticationType = AuthenticationTypes.Secure }) { domainHostName = (string)rootDse.Properties["dnsHostName"].Value; } // Query the domain server by the current user's SID using (DirectoryEntry entry = new DirectoryEntry("LDAP://" + domainHostName) { AuthenticationType = AuthenticationTypes.Secure }) { DirectorySearcher search = new DirectorySearcher(entry, "(objectSid=" + identity.User.Value + ")", new[] { "userPrincipalName" }); SearchResult result = search.FindOne(); upn = (string)result.Properties["userPrincipalName"][0]; } } if (cachedUpn == null) { // Save this value cachedUpn = new CachedUpn() { Upn = upn, ExpiryTimeUtc = DateTime.UtcNow.AddHours(3.0) }; lock (CachedUpns) { CachedUpns[identity.User] = cachedUpn; } } string[] upnParts = upn.Split('@'); if (_includeRealm) { // Make it Kerberos-y by uppercasing the realm part return upnParts[0] + "@" + upnParts[1].ToUpperInvariant(); } else { return upnParts[0]; } } catch { // Querying the directory failed, so return the SAM name // (which probably won't work, but it's better than nothing) return GetWindowsIdentityUserName(); } } #endif #endregion #region Misc internal NpgsqlConnectionStringBuilder Clone() { return new NpgsqlConnectionStringBuilder(ConnectionString); } #endregion #region Attributes [AttributeUsage(AttributeTargets.Property)] [MeansImplicitUse] class NpgsqlConnectionStringPropertyAttribute : Attribute { internal string[] Aliases { get; } internal NpgsqlConnectionStringPropertyAttribute() { Aliases = Empty; } internal NpgsqlConnectionStringPropertyAttribute(params string[] aliases) { Aliases = aliases; } } #endregion } #region Enums /// /// An option specified in the connection string that activates special compatibility features. /// [PublicAPI] public enum ServerCompatibilityMode { /// /// No special server compatibility mode is active /// None, /// /// The server is an Amazon Redshift instance. /// Redshift, } /// /// Specifies how to manage SSL. /// [PublicAPI] public enum SslMode { /// /// SSL is disabled. If the server requires SSL, the connection will fail. /// Disable, /// /// Prefer SSL connections if the server allows them, but allow connections without SSL. /// Prefer, /// /// Fail the connection if the server doesn't suppotr SSL. /// Require, } #endregion }