// Copyright (c) Microsoft. All rights reserved.
//
// Licensed under the MIT license.
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using NuGet.Protocol.Plugins;
using NuGetCredentialProvider.CredentialProviders;
using NuGetCredentialProvider.Logging;
using NuGetCredentialProvider.Util;
namespace NuGetCredentialProvider.RequestHandlers
{
///
/// Handles a and replies with credentials.
///
internal class GetAuthenticationCredentialsRequestHandler : RequestHandlerBase
{
private readonly ICache cache;
private readonly IReadOnlyCollection credentialProviders;
private readonly TimeSpan progressReporterTimeSpan = TimeSpan.FromSeconds(2);
///
/// Initializes a new instance of the class.
///
/// A to use for logging.
/// An containing credential providers.
/// An cache to store found credentials.
public GetAuthenticationCredentialsRequestHandler(ILogger logger, IReadOnlyCollection credentialProviders, ICache cache)
: base(logger)
{
this.credentialProviders = credentialProviders ?? throw new ArgumentNullException(nameof(credentialProviders));
this.cache = cache;
}
public GetAuthenticationCredentialsRequestHandler(ILogger logger, IReadOnlyCollection credentialProviders, CancellationToken cancellationToken)
: this(logger, credentialProviders, null)
{
this.cache = GetSessionTokenCache(logger, cancellationToken);
}
public override async Task HandleRequestAsync(GetAuthenticationCredentialsRequest request)
{
Logger.Verbose(string.Format(Resources.HandlingAuthRequest, request.Uri.AbsoluteUri, request.IsRetry, request.IsNonInteractive, request.CanShowDialog));
if (request?.Uri == null)
{
return new GetAuthenticationCredentialsResponse(
username: null,
password: null,
message: Resources.RequestUriNull,
authenticationTypes: null,
responseCode: MessageResponseCode.Error);
}
Logger.Verbose(string.Format(Resources.Uri, request.Uri.AbsoluteUri));
foreach (ICredentialProvider credentialProvider in credentialProviders)
{
if (await credentialProvider.CanProvideCredentialsAsync(request.Uri) == false)
{
Logger.Verbose(string.Format(Resources.SkippingCredentialProvider, credentialProvider, request.Uri.AbsoluteUri));
continue;
}
Logger.Verbose(string.Format(Resources.UsingCredentialProvider, credentialProvider, request.Uri.AbsoluteUri));
if (credentialProvider.IsCachable && TryCache(request, out string cachedToken))
{
return new GetAuthenticationCredentialsResponse(
username: "VssSessionToken",
password: cachedToken,
message: null,
authenticationTypes: new List { "Basic" },
responseCode: MessageResponseCode.Success);
}
try
{
GetAuthenticationCredentialsResponse response = await credentialProvider.HandleRequestAsync(request, CancellationToken).ConfigureAwait(continueOnCapturedContext: false);
if (response != null && response.ResponseCode == MessageResponseCode.Success)
{
if (cache != null && credentialProvider.IsCachable)
{
Logger.Verbose(string.Format(Resources.CachingSessionToken, request.Uri.AbsoluteUri));
cache[request.Uri] = response.Password;
}
return response;
}
else if (!string.IsNullOrWhiteSpace(response?.Message))
{
Logger.Verbose(response.Message);
}
}
catch (Exception e)
{
Logger.Error(string.Format(Resources.AcquireSessionTokenFailed, e.ToString()));
return new GetAuthenticationCredentialsResponse(
username: null,
password: null,
message: e.Message,
authenticationTypes: null,
responseCode: MessageResponseCode.Error);
}
}
Logger.Verbose(Resources.CredentialsNotFound);
return new GetAuthenticationCredentialsResponse(
username: null,
password: null,
message: null,
authenticationTypes: null,
responseCode: MessageResponseCode.NotFound);
}
protected override AutomaticProgressReporter GetProgressReporter(IConnection connection, Message message, CancellationToken cancellationToken)
{
Logger.Verbose(string.Format(Resources.CreatingProgressReporter, progressReporterTimeSpan.ToString()));
return AutomaticProgressReporter.Create(connection, message, progressReporterTimeSpan, cancellationToken);
}
private static ICache GetSessionTokenCache(ILogger logger, CancellationToken cancellationToken)
{
if (EnvUtil.SessionTokenCacheEnabled())
{
logger.Verbose(string.Format(Resources.SessionTokenCacheLocation, EnvUtil.SessionTokenCacheLocation));
return new SessionTokenCache(EnvUtil.SessionTokenCacheLocation, logger, cancellationToken);
}
logger.Verbose(Resources.SessionTokenCacheDisabled);
return new NoOpCache();
}
private bool TryCache(GetAuthenticationCredentialsRequest request, out string cachedToken)
{
cachedToken = null;
Logger.Verbose(string.Format(Resources.IsRetry, request.IsRetry));
if (request.IsRetry)
{
Logger.Verbose(string.Format(Resources.InvalidatingCachedSessionToken, request.Uri.AbsoluteUri));
cache?.Remove(request.Uri);
return false;
}
else if (cache.TryGetValue(request.Uri, out string password))
{
Logger.Verbose(string.Format(Resources.FoundCachedSessionToken, request.Uri.AbsoluteUri));
cachedToken = password;
return true;
}
Logger.Verbose(string.Format(Resources.CachedSessionTokenNotFound, request.Uri.AbsoluteUri));
return false;
}
}
}