diff --git a/src/Npgsql/Internal/NpgsqlConnector.Auth.cs b/src/Npgsql/Internal/NpgsqlConnector.Auth.cs index 3f73b4ed58..fad990158f 100644 --- a/src/Npgsql/Internal/NpgsqlConnector.Auth.cs +++ b/src/Npgsql/Internal/NpgsqlConnector.Auth.cs @@ -334,7 +334,10 @@ internal async Task AuthenticateGSS(bool async) { var targetName = $"{KerberosServiceName}/{Host}"; - using var authContext = new NegotiateAuthentication(new NegotiateAuthenticationClientOptions{ TargetName = targetName}); + var clientOptions = new NegotiateAuthenticationClientOptions { TargetName = targetName }; + NegotiateOptionsCallback?.Invoke(clientOptions); + + using var authContext = new NegotiateAuthentication(clientOptions); var data = authContext.GetOutgoingBlob(ReadOnlySpan.Empty, out var statusCode)!; Debug.Assert(statusCode == NegotiateAuthenticationStatusCode.ContinueNeeded); await WritePassword(data, 0, data.Length, async, UserCancellationToken).ConfigureAwait(false); diff --git a/src/Npgsql/Internal/NpgsqlConnector.cs b/src/Npgsql/Internal/NpgsqlConnector.cs index d1be574cf8..b2127d562d 100644 --- a/src/Npgsql/Internal/NpgsqlConnector.cs +++ b/src/Npgsql/Internal/NpgsqlConnector.cs @@ -61,6 +61,10 @@ public sealed partial class NpgsqlConnector ProvidePasswordCallback? ProvidePasswordCallback { get; } #pragma warning restore CS0618 +#if NET7_0_OR_GREATER + Action? NegotiateOptionsCallback { get; } +#endif + public Encoding TextEncoding { get; private set; } = default!; /// @@ -366,6 +370,10 @@ internal NpgsqlConnector(NpgsqlDataSource dataSource, NpgsqlConnection conn) ClientCertificatesCallback = dataSource.ClientCertificatesCallback; UserCertificateValidationCallback = dataSource.UserCertificateValidationCallback; +#if NET7_0_OR_GREATER + NegotiateOptionsCallback = dataSource.Configuration.NegotiateOptionsCallback; +#endif + State = ConnectorState.Closed; TransactionStatus = TransactionStatus.Idle; Settings = dataSource.Settings; diff --git a/src/Npgsql/NpgsqlDataSource.cs b/src/Npgsql/NpgsqlDataSource.cs index ba5fdf255a..67b8c154ac 100644 --- a/src/Npgsql/NpgsqlDataSource.cs +++ b/src/Npgsql/NpgsqlDataSource.cs @@ -108,7 +108,11 @@ internal NpgsqlDataSource( var resolverChain, _defaultNameTranslator, ConnectionInitializer, - ConnectionInitializerAsync) + ConnectionInitializerAsync +#if NET7_0_OR_GREATER + ,_ +#endif + ) = dataSourceConfig; _connectionLogger = LoggingConfiguration.ConnectionLogger; diff --git a/src/Npgsql/NpgsqlDataSourceBuilder.cs b/src/Npgsql/NpgsqlDataSourceBuilder.cs index e304a559cc..73e13a11f9 100644 --- a/src/Npgsql/NpgsqlDataSourceBuilder.cs +++ b/src/Npgsql/NpgsqlDataSourceBuilder.cs @@ -325,6 +325,24 @@ public NpgsqlDataSourceBuilder UsePasswordProvider( return this; } +#if NET7_0_OR_GREATER + /// + /// When using Kerberos, this is a callback that allows customizing default settings for Kerberos authentication. + /// + /// The callback containing logic to customize Kerberos authentication settings. + /// + /// + /// See . + /// + /// + /// The same builder instance so that multiple calls can be chained. + public NpgsqlDataSourceBuilder UseNegotiateOptionsCallback(Action? negotiateOptionsCallback) + { + _internalBuilder.UseNegotiateOptionsCallback(negotiateOptionsCallback); + return this; + } +#endif + #endregion Authentication #region Type mapping diff --git a/src/Npgsql/NpgsqlDataSourceConfiguration.cs b/src/Npgsql/NpgsqlDataSourceConfiguration.cs index 075df28aa8..83539bd17b 100644 --- a/src/Npgsql/NpgsqlDataSourceConfiguration.cs +++ b/src/Npgsql/NpgsqlDataSourceConfiguration.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using System.Net.Security; using System.Security.Cryptography.X509Certificates; using System.Threading; @@ -22,4 +21,8 @@ sealed record NpgsqlDataSourceConfiguration(string? Name, PgTypeInfoResolverChain ResolverChain, INpgsqlNameTranslator DefaultNameTranslator, Action? ConnectionInitializer, - Func? ConnectionInitializerAsync); + Func? ConnectionInitializerAsync +#if NET7_0_OR_GREATER + ,Action? NegotiateOptionsCallback +#endif + ); diff --git a/src/Npgsql/NpgsqlSlimDataSourceBuilder.cs b/src/Npgsql/NpgsqlSlimDataSourceBuilder.cs index 31a82dc04d..3ee4cd80dc 100644 --- a/src/Npgsql/NpgsqlSlimDataSourceBuilder.cs +++ b/src/Npgsql/NpgsqlSlimDataSourceBuilder.cs @@ -34,6 +34,10 @@ public sealed class NpgsqlSlimDataSourceBuilder : INpgsqlTypeMapper RemoteCertificateValidationCallback? _userCertificateValidationCallback; Action? _clientCertificatesCallback; +#if NET7_0_OR_GREATER + Action? _negotiateOptionsCallback; +#endif + IntegratedSecurityHandler _integratedSecurityHandler = new(); Func? _passwordProvider; @@ -289,6 +293,25 @@ public NpgsqlSlimDataSourceBuilder UsePasswordProvider( return this; } +#if NET7_0_OR_GREATER + /// + /// When using Kerberos, this is a callback that allows customizing default settings for Kerberos authentication. + /// + /// The callback containing logic to customize Kerberos authentication settings. + /// + /// + /// See . + /// + /// + /// The same builder instance so that multiple calls can be chained. + public NpgsqlSlimDataSourceBuilder UseNegotiateOptionsCallback(Action? negotiateOptionsCallback) + { + _negotiateOptionsCallback = negotiateOptionsCallback; + + return this; + } +#endif + #endregion Authentication #region Type mapping @@ -602,7 +625,7 @@ public NpgsqlDataSource Build() var config = PrepareConfiguration(); var connectionStringBuilder = ConnectionStringBuilder.Clone(); - if (ConnectionStringBuilder.Host!.Contains(",")) + if (ConnectionStringBuilder.Host!.Contains(',')) { ValidateMultiHost(); @@ -667,7 +690,11 @@ _loggerFactory is null _resolverChainBuilder.Build(ConfigureResolverChain), DefaultNameTranslator, _connectionInitializer, - _connectionInitializerAsync); + _connectionInitializerAsync +#if NET7_0_OR_GREATER + ,_negotiateOptionsCallback +#endif + ); } void ValidateMultiHost() diff --git a/src/Npgsql/PublicAPI.Unshipped.txt b/src/Npgsql/PublicAPI.Unshipped.txt index aa6649d3e6..92e625df4c 100644 --- a/src/Npgsql/PublicAPI.Unshipped.txt +++ b/src/Npgsql/PublicAPI.Unshipped.txt @@ -1,7 +1,9 @@ #nullable enable +Npgsql.NpgsqlDataSourceBuilder.UseNegotiateOptionsCallback(System.Action? negotiateOptionsCallback) -> Npgsql.NpgsqlDataSourceBuilder! Npgsql.NpgsqlSlimDataSourceBuilder.EnableGeometricTypes() -> Npgsql.NpgsqlSlimDataSourceBuilder! Npgsql.NpgsqlSlimDataSourceBuilder.EnableJsonTypes() -> Npgsql.NpgsqlSlimDataSourceBuilder! Npgsql.NpgsqlSlimDataSourceBuilder.EnableNetworkTypes() -> Npgsql.NpgsqlSlimDataSourceBuilder! +Npgsql.NpgsqlSlimDataSourceBuilder.UseNegotiateOptionsCallback(System.Action? negotiateOptionsCallback) -> Npgsql.NpgsqlSlimDataSourceBuilder! Npgsql.Replication.PgOutput.ReplicationValue.GetFieldName() -> string! Npgsql.Replication.PgOutput.Messages.ParallelStreamAbortMessage Npgsql.Replication.PgOutput.Messages.ParallelStreamAbortMessage.AbortLsn.get -> NpgsqlTypes.NpgsqlLogSequenceNumber