< Summary

Information
Class: dotnet_etcd.AuthenticationInterceptor
Assembly: dotnet-etcd
File(s): /home/runner/work/dotnet-etcd/dotnet-etcd/dotnet-etcd/AuthenticationInterceptor.cs
Line coverage
100%
Covered lines: 58
Uncovered lines: 0
Coverable lines: 58
Total lines: 117
Line coverage: 100%
Branch coverage
92%
Covered branches: 13
Total branches: 14
Branch coverage: 92.8%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%22100%
AsyncUnaryCall(...)100%11100%
AsyncClientStreamingCall(...)100%11100%
AsyncServerStreamingCall(...)100%11100%
AsyncDuplexStreamingCall(...)100%11100%
BlockingUnaryCall(...)100%11100%
AddAuthToken(...)91.66%1212100%

File(s)

/home/runner/work/dotnet-etcd/dotnet-etcd/dotnet-etcd/AuthenticationInterceptor.cs

#LineLine coverage
 1#nullable enable
 2
 3using System;
 4using System.Threading.Tasks;
 5using Etcdserverpb;
 6using Grpc.Core;
 7using Grpc.Core.Interceptors;
 8
 9namespace dotnet_etcd;
 10
 11/// <summary>
 12///     Interceptor that automatically adds authentication tokens to all gRPC calls
 13/// </summary>
 14internal class AuthenticationInterceptor : Interceptor
 15{
 16    private const string AuthorizationHeader = "authorization";
 17
 18    private readonly Func<string?> _getToken;
 19
 8320    public AuthenticationInterceptor(Func<string?> getToken)
 8321    {
 8322        _getToken = getToken ?? throw new ArgumentNullException(nameof(getToken));
 8223    }
 24
 25    public override AsyncUnaryCall<TResponse> AsyncUnaryCall<TRequest, TResponse>(
 26        TRequest request,
 27        ClientInterceptorContext<TRequest, TResponse> context,
 28        AsyncUnaryCallContinuation<TRequest, TResponse> continuation)
 329    {
 330        var headers = AddAuthToken(context.Options.Headers);
 331        var newOptions = context.Options.WithHeaders(headers);
 332        var newContext = new ClientInterceptorContext<TRequest, TResponse>(
 333            context.Method, context.Host, newOptions);
 34
 335        return continuation(request, newContext);
 336    }
 37
 38    public override AsyncClientStreamingCall<TRequest, TResponse> AsyncClientStreamingCall<TRequest, TResponse>(
 39        ClientInterceptorContext<TRequest, TResponse> context,
 40        AsyncClientStreamingCallContinuation<TRequest, TResponse> continuation)
 241    {
 242        var headers = AddAuthToken(context.Options.Headers);
 243        var newOptions = context.Options.WithHeaders(headers);
 244        var newContext = new ClientInterceptorContext<TRequest, TResponse>(
 245            context.Method, context.Host, newOptions);
 46
 247        return continuation(newContext);
 248    }
 49
 50    public override AsyncServerStreamingCall<TResponse> AsyncServerStreamingCall<TRequest, TResponse>(
 51        TRequest request,
 52        ClientInterceptorContext<TRequest, TResponse> context,
 53        AsyncServerStreamingCallContinuation<TRequest, TResponse> continuation)
 254    {
 255        var headers = AddAuthToken(context.Options.Headers);
 256        var newOptions = context.Options.WithHeaders(headers);
 257        var newContext = new ClientInterceptorContext<TRequest, TResponse>(
 258            context.Method, context.Host, newOptions);
 59
 260        return continuation(request, newContext);
 261    }
 62
 63    public override AsyncDuplexStreamingCall<TRequest, TResponse> AsyncDuplexStreamingCall<TRequest, TResponse>(
 64        ClientInterceptorContext<TRequest, TResponse> context,
 65        AsyncDuplexStreamingCallContinuation<TRequest, TResponse> continuation)
 266    {
 267        var headers = AddAuthToken(context.Options.Headers);
 268        var newOptions = context.Options.WithHeaders(headers);
 269        var newContext = new ClientInterceptorContext<TRequest, TResponse>(
 270            context.Method, context.Host, newOptions);
 71
 272        return continuation(newContext);
 273    }
 74
 75    public override TResponse BlockingUnaryCall<TRequest, TResponse>(
 76        TRequest request,
 77        ClientInterceptorContext<TRequest, TResponse> context,
 78        BlockingUnaryCallContinuation<TRequest, TResponse> continuation)
 1079    {
 1080        var headers = AddAuthToken(context.Options.Headers);
 1081        var newOptions = context.Options.WithHeaders(headers);
 1082        var newContext = new ClientInterceptorContext<TRequest, TResponse>(
 1083            context.Method, context.Host, newOptions);
 84
 1085        return continuation(request, newContext);
 1086    }
 87
 88    private Metadata AddAuthToken(Metadata? headers)
 1989    {
 90        // Create new metadata or use existing
 1991        var newHeaders = headers == null ? new Metadata() : new Metadata();
 92
 93        // Preserve all existing headers (except Authorization, which we'll replace if needed)
 1994        if (headers != null)
 495        {
 2896            foreach (var entry in headers)
 897            {
 98                // Skip authorization header - we'll add it if needed
 899                if (!string.Equals(entry.Key, AuthorizationHeader, StringComparison.OrdinalIgnoreCase))
 5100                {
 5101                    newHeaders.Add(entry);
 5102                }
 8103            }
 4104        }
 105
 106        // Get token from provider (this is now just a simple field read, no method calls)
 19107        var token = _getToken?.Invoke();
 108
 109        // Add the authorization header with the token (only if it's not empty or whitespace)
 19110        if (!string.IsNullOrWhiteSpace(token))
 12111        {
 12112            newHeaders.Add(AuthorizationHeader, token);
 12113        }
 114
 19115        return newHeaders;
 19116    }
 117}