Skip to content

Commit

Permalink
Add callback to customize Kerberos authentication options (npgsql#5775)
Browse files Browse the repository at this point in the history
  • Loading branch information
vonzshik authored Jul 5, 2024
1 parent 334c907 commit 321d578
Show file tree
Hide file tree
Showing 7 changed files with 71 additions and 6 deletions.
5 changes: 4 additions & 1 deletion src/Npgsql/Internal/NpgsqlConnector.Auth.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<byte>.Empty, out var statusCode)!;
Debug.Assert(statusCode == NegotiateAuthenticationStatusCode.ContinueNeeded);
await WritePassword(data, 0, data.Length, async, UserCancellationToken).ConfigureAwait(false);
Expand Down
8 changes: 8 additions & 0 deletions src/Npgsql/Internal/NpgsqlConnector.cs
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,10 @@ public sealed partial class NpgsqlConnector
ProvidePasswordCallback? ProvidePasswordCallback { get; }
#pragma warning restore CS0618

#if NET7_0_OR_GREATER
Action<NegotiateAuthenticationClientOptions>? NegotiateOptionsCallback { get; }
#endif

public Encoding TextEncoding { get; private set; } = default!;

/// <summary>
Expand Down Expand Up @@ -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;
Expand Down
6 changes: 5 additions & 1 deletion src/Npgsql/NpgsqlDataSource.cs
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,11 @@ internal NpgsqlDataSource(
var resolverChain,
_defaultNameTranslator,
ConnectionInitializer,
ConnectionInitializerAsync)
ConnectionInitializerAsync
#if NET7_0_OR_GREATER
,_
#endif
)
= dataSourceConfig;
_connectionLogger = LoggingConfiguration.ConnectionLogger;

Expand Down
18 changes: 18 additions & 0 deletions src/Npgsql/NpgsqlDataSourceBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,24 @@ public NpgsqlDataSourceBuilder UsePasswordProvider(
return this;
}

#if NET7_0_OR_GREATER
/// <summary>
/// When using Kerberos, this is a callback that allows customizing default settings for Kerberos authentication.
/// </summary>
/// <param name="negotiateOptionsCallback">The callback containing logic to customize Kerberos authentication settings.</param>
/// <remarks>
/// <para>
/// See <see href="https://learn.microsoft.com/en-us/dotnet/api/system.net.security.negotiateauthenticationclientoptions?view=net-7.0"/>.
/// </para>
/// </remarks>
/// <returns>The same builder instance so that multiple calls can be chained.</returns>
public NpgsqlDataSourceBuilder UseNegotiateOptionsCallback(Action<NegotiateAuthenticationClientOptions>? negotiateOptionsCallback)
{
_internalBuilder.UseNegotiateOptionsCallback(negotiateOptionsCallback);
return this;
}
#endif

#endregion Authentication

#region Type mapping
Expand Down
7 changes: 5 additions & 2 deletions src/Npgsql/NpgsqlDataSourceConfiguration.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using System;
using System.Collections.Generic;
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;
using System.Threading;
Expand All @@ -22,4 +21,8 @@ sealed record NpgsqlDataSourceConfiguration(string? Name,
PgTypeInfoResolverChain ResolverChain,
INpgsqlNameTranslator DefaultNameTranslator,
Action<NpgsqlConnection>? ConnectionInitializer,
Func<NpgsqlConnection, Task>? ConnectionInitializerAsync);
Func<NpgsqlConnection, Task>? ConnectionInitializerAsync
#if NET7_0_OR_GREATER
,Action<NegotiateAuthenticationClientOptions>? NegotiateOptionsCallback
#endif
);
31 changes: 29 additions & 2 deletions src/Npgsql/NpgsqlSlimDataSourceBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@ public sealed class NpgsqlSlimDataSourceBuilder : INpgsqlTypeMapper
RemoteCertificateValidationCallback? _userCertificateValidationCallback;
Action<X509CertificateCollection>? _clientCertificatesCallback;

#if NET7_0_OR_GREATER
Action<NegotiateAuthenticationClientOptions>? _negotiateOptionsCallback;
#endif

IntegratedSecurityHandler _integratedSecurityHandler = new();

Func<NpgsqlConnectionStringBuilder, string>? _passwordProvider;
Expand Down Expand Up @@ -289,6 +293,25 @@ public NpgsqlSlimDataSourceBuilder UsePasswordProvider(
return this;
}

#if NET7_0_OR_GREATER
/// <summary>
/// When using Kerberos, this is a callback that allows customizing default settings for Kerberos authentication.
/// </summary>
/// <param name="negotiateOptionsCallback">The callback containing logic to customize Kerberos authentication settings.</param>
/// <remarks>
/// <para>
/// See <see href="https://learn.microsoft.com/en-us/dotnet/api/system.net.security.negotiateauthenticationclientoptions?view=net-7.0"/>.
/// </para>
/// </remarks>
/// <returns>The same builder instance so that multiple calls can be chained.</returns>
public NpgsqlSlimDataSourceBuilder UseNegotiateOptionsCallback(Action<NegotiateAuthenticationClientOptions>? negotiateOptionsCallback)
{
_negotiateOptionsCallback = negotiateOptionsCallback;

return this;
}
#endif

#endregion Authentication

#region Type mapping
Expand Down Expand Up @@ -602,7 +625,7 @@ public NpgsqlDataSource Build()
var config = PrepareConfiguration();
var connectionStringBuilder = ConnectionStringBuilder.Clone();

if (ConnectionStringBuilder.Host!.Contains(","))
if (ConnectionStringBuilder.Host!.Contains(','))
{
ValidateMultiHost();

Expand Down Expand Up @@ -667,7 +690,11 @@ _loggerFactory is null
_resolverChainBuilder.Build(ConfigureResolverChain),
DefaultNameTranslator,
_connectionInitializer,
_connectionInitializerAsync);
_connectionInitializerAsync
#if NET7_0_OR_GREATER
,_negotiateOptionsCallback
#endif
);
}

void ValidateMultiHost()
Expand Down
2 changes: 2 additions & 0 deletions src/Npgsql/PublicAPI.Unshipped.txt
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
#nullable enable
Npgsql.NpgsqlDataSourceBuilder.UseNegotiateOptionsCallback(System.Action<System.Net.Security.NegotiateAuthenticationClientOptions!>? negotiateOptionsCallback) -> Npgsql.NpgsqlDataSourceBuilder!
Npgsql.NpgsqlSlimDataSourceBuilder.EnableGeometricTypes() -> Npgsql.NpgsqlSlimDataSourceBuilder!
Npgsql.NpgsqlSlimDataSourceBuilder.EnableJsonTypes() -> Npgsql.NpgsqlSlimDataSourceBuilder!
Npgsql.NpgsqlSlimDataSourceBuilder.EnableNetworkTypes() -> Npgsql.NpgsqlSlimDataSourceBuilder!
Npgsql.NpgsqlSlimDataSourceBuilder.UseNegotiateOptionsCallback(System.Action<System.Net.Security.NegotiateAuthenticationClientOptions!>? negotiateOptionsCallback) -> Npgsql.NpgsqlSlimDataSourceBuilder!
Npgsql.Replication.PgOutput.ReplicationValue.GetFieldName() -> string!
Npgsql.Replication.PgOutput.Messages.ParallelStreamAbortMessage
Npgsql.Replication.PgOutput.Messages.ParallelStreamAbortMessage.AbortLsn.get -> NpgsqlTypes.NpgsqlLogSequenceNumber
Expand Down

0 comments on commit 321d578

Please sign in to comment.