public async Task GetAsync_WhenPluginManagerReturnsException_ExceptionIsPropagated() { var expectedMessage = "a"; var expectedException = CreateExceptionWithCallstack("b"); var pluginCreationResult = new PluginCreationResult(expectedMessage, expectedException); var result = new Tuple <bool, PluginCreationResult>(true, pluginCreationResult); var pluginManager = new Mock <IPluginManager>(MockBehavior.Strict); pluginManager.Setup(x => x.TryGetSourceAgnosticPluginAsync( It.IsNotNull <PluginDiscoveryResult>(), It.Is <OperationClaim>(claim => claim == OperationClaim.Authentication), It.IsAny <CancellationToken>())) .ReturnsAsync(result); var pluginDiscoveryResult = new PluginDiscoveryResult(new PluginFile("c", new Lazy <PluginFileState>(() => PluginFileState.Valid))); var logger = new Mock <ILogger>(MockBehavior.Strict); logger.Setup(x => x.LogError(It.Is <string>(data => data == expectedMessage))); logger.Setup(x => x.LogDebug(It.Is <string>(data => data == expectedException.ToString()))); var provider = new SecurePluginCredentialProvider(pluginManager.Object, pluginDiscoveryResult, canShowDialog: false, logger: logger.Object); var exception = await Assert.ThrowsAsync <PluginException>( () => provider.GetAsync(_uri, proxy: null, type: CredentialRequestType.Forbidden, message: null, isRetry: false, nonInteractive: true, cancellationToken: CancellationToken.None)); Assert.Same(expectedException, exception.InnerException); pluginManager.Verify(); logger.Verify(); }
public async Task TryGetSourceAgnosticPluginAsync_WhenSuccessfullyCreated_OperationClaimsAreCached() { var operationClaims = new[] { OperationClaim.Authentication }; using (var test = new PluginManagerTest(PluginFilePath, PluginFileState.Valid, operationClaims)) { Assert.False(File.Exists(test.PluginCacheEntry.CacheFileName)); var discoveryResult = new PluginDiscoveryResult( new PluginFile( PluginFilePath, new Lazy <PluginFileState>(() => PluginFileState.Valid))); Tuple <bool, PluginCreationResult> result = await test.PluginManager.TryGetSourceAgnosticPluginAsync( discoveryResult, OperationClaim.Authentication, CancellationToken.None); bool wasSomethingCreated = result.Item1; PluginCreationResult creationResult = result.Item2; Assert.True(wasSomethingCreated); Assert.NotNull(creationResult); Assert.True(File.Exists(test.PluginCacheEntry.CacheFileName)); Assert.Null(creationResult.Message); Assert.Null(creationResult.Exception); Assert.Same(test.Plugin, creationResult.Plugin); Assert.NotNull(creationResult.PluginMulticlientUtilities); Assert.Equal(operationClaims, creationResult.Claims); } }
public async Task TryGetSourceAgnosticPluginAsync_WhenCacheFileIndicatesIndicatesNoSupportedOperationClaims_PluginIsNotCreated() { var operationClaims = Array.Empty <OperationClaim>(); using (var test = new PluginManagerTest(PluginFilePath, PluginFileState.Valid, operationClaims)) { test.PluginCacheEntry.OperationClaims = operationClaims; await test.PluginCacheEntry.UpdateCacheFileAsync(); Assert.True(File.Exists(test.PluginCacheEntry.CacheFileName)); var discoveryResult = new PluginDiscoveryResult( new PluginFile( PluginFilePath, new Lazy <PluginFileState>(() => PluginFileState.Valid))); Tuple <bool, PluginCreationResult> result = await test.PluginManager.TryGetSourceAgnosticPluginAsync( discoveryResult, OperationClaim.Authentication, CancellationToken.None); bool wasSomethingCreated = result.Item1; PluginCreationResult creationResult = result.Item2; Assert.False(wasSomethingCreated); Assert.Null(creationResult); Assert.True(File.Exists(test.PluginCacheEntry.CacheFileName)); } }
/// <summary> /// Creates a plugin from the discovered plugin. /// We firstly check the cache for the operation claims for the given request key. /// If there is a valid cache entry, and it does contain the requested operation claim, then we start the plugin, and if need be update the cache value itself. /// If there is a valid cache entry, and it does NOT contain the requested operation claim, then we return a null. /// If there is no valid cache entry or an invalid one, we start the plugin as normally, return an active plugin even if the requested claim is not available, and write a cache entry. /// </summary> /// <param name="result">plugin discovery result</param> /// <param name="requestedOperationClaim">The requested operation claim</param> /// <param name="requestKey">plugin request key</param> /// <param name="packageSourceRepository">package source repository</param> /// <param name="serviceIndex">service index</param> /// <param name="cancellationToken">cancellation token</param> /// <returns>A plugin creation result, null if the requested plugin cannot handle the given operation claim</returns> private async Task <Tuple <bool, PluginCreationResult> > TryCreatePluginAsync( PluginDiscoveryResult result, OperationClaim requestedOperationClaim, PluginRequestKey requestKey, string packageSourceRepository, JObject serviceIndex, CancellationToken cancellationToken) { PluginCreationResult pluginCreationResult = null; var cacheEntry = new PluginCacheEntry(_pluginsCacheDirectory.Value, result.PluginFile.Path, requestKey.PackageSourceRepository); return(await ConcurrencyUtilities.ExecuteWithFileLockedAsync( cacheEntry.CacheFileName, action : async lockedToken => { if (cacheEntry.OperationClaims == null || cacheEntry.OperationClaims.Contains(requestedOperationClaim)) { if (result.PluginFile.State.Value == PluginFileState.Valid) { var plugin = await _pluginFactory.GetOrCreateAsync( result.PluginFile.Path, PluginConstants.PluginArguments, new RequestHandlers(), _connectionOptions, cancellationToken); var utilities = await PerformOneTimePluginInitializationAsync(plugin, cancellationToken); // We still make the GetOperationClaims call even if we have the operation claims cached. This is a way to self-update the cache. var operationClaims = await _pluginOperationClaims.GetOrAdd( requestKey, key => new Lazy <Task <IReadOnlyList <OperationClaim> > >(() => GetPluginOperationClaimsAsync( plugin, packageSourceRepository, serviceIndex, cancellationToken))).Value; if (!EqualityUtility.SequenceEqualWithNullCheck(operationClaims, cacheEntry.OperationClaims)) { cacheEntry.OperationClaims = operationClaims; await cacheEntry.UpdateCacheFileAsync(); } pluginCreationResult = new PluginCreationResult( plugin, utilities.Value, operationClaims); } else { pluginCreationResult = new PluginCreationResult(result.Message); } } return new Tuple <bool, PluginCreationResult>(pluginCreationResult != null, pluginCreationResult); }, token : cancellationToken )); }
public void Constructor_Message_InitializesProperties() { var result = new PluginCreationResult(message: "a"); Assert.Null(result.Plugin); Assert.Null(result.PluginMulticlientUtilities); Assert.Null(result.Claims); Assert.Equal("a", result.Message); }
public void Constructor_Message_Exception_InitializesProperties() { var exception = new Exception(); var result = new PluginCreationResult(message: "a", exception: exception); Assert.Null(result.Plugin); Assert.Null(result.PluginMulticlientUtilities); Assert.Null(result.Claims); Assert.Equal("a", result.Message); Assert.Same(exception, result.Exception); }
private async Task SetPluginLogLevelAsync(PluginCreationResult plugin, ILogger logger, CancellationToken cancellationToken) { var logLevel = LogRequestHandler.GetLogLevel(logger); await plugin.PluginMulticlientUtilities.DoOncePerPluginLifetimeAsync( MessageMethod.SetLogLevel.ToString(), () => plugin.Plugin.Connection.SendRequestAndReceiveResponseAsync <SetLogLevelRequest, SetLogLevelResponse>( MessageMethod.SetLogLevel, new SetLogLevelRequest(logLevel), cancellationToken), cancellationToken); }
public async Task PluginManager_CreatePlugin_PrefersFrameworkSpecificEnvironmentVariable(string pluginPath) { var operationClaims = new[] { OperationClaim.Authentication }; var mockReader = new Mock <IEnvironmentVariableReader>(MockBehavior.Strict); mockReader.Setup(x => x.GetEnvironmentVariable( It.Is <string>(value => value == EnvironmentVariableConstants.PluginPaths))) .Returns("badPluginPath"); #if IS_DESKTOP mockReader.Setup(x => x.GetEnvironmentVariable( It.Is <string>(value => value == EnvironmentVariableConstants.DesktopPluginPaths))) .Returns(pluginPath); #else mockReader.Setup(x => x.GetEnvironmentVariable( It.Is <string>(value => value == EnvironmentVariableConstants.CorePluginPaths))) .Returns(pluginPath); #endif mockReader.Setup(x => x.GetEnvironmentVariable( It.Is <string>(value => value == EnvironmentVariableConstants.RequestTimeout))) .Returns("RequestTimeout"); mockReader.Setup(x => x.GetEnvironmentVariable( It.Is <string>(value => value == EnvironmentVariableConstants.IdleTimeout))) .Returns("IdleTimeout"); mockReader.Setup(x => x.GetEnvironmentVariable( It.Is <string>(value => value == EnvironmentVariableConstants.HandshakeTimeout))) .Returns("HandshakeTimeout"); using (var test = new PluginManagerTest(pluginPath, PluginFileState.Valid, operationClaims, mockReader)) { var discoveryResult = new PluginDiscoveryResult( new PluginFile( PluginFilePath, new Lazy <PluginFileState>(() => PluginFileState.Valid))); Tuple <bool, PluginCreationResult> result = await test.PluginManager.TryGetSourceAgnosticPluginAsync( discoveryResult, OperationClaim.Authentication, CancellationToken.None); bool wasSomethingCreated = result.Item1; PluginCreationResult creationResult = result.Item2; Assert.True(wasSomethingCreated); Assert.NotNull(creationResult); Assert.Null(creationResult.Message); Assert.Null(creationResult.Exception); Assert.Same(test.Plugin, creationResult.Plugin); Assert.NotNull(creationResult.PluginMulticlientUtilities); Assert.Equal(operationClaims, creationResult.Claims); } }
public void Constructor_PluginClaims_InitializesProperties() { var plugin = Mock.Of <IPlugin>(); var utilities = Mock.Of <IPluginMulticlientUtilities>(); var claims = new List <OperationClaim>(); var result = new PluginCreationResult(plugin, utilities, claims); Assert.Same(plugin, result.Plugin); Assert.Same(utilities, result.PluginMulticlientUtilities); Assert.Same(claims, result.Claims); Assert.Null(result.Message); }
private static PluginResourceProvider CreatePluginResourceProvider(bool createResource) { var provider = new Mock <PluginResourceProvider>(); provider.Setup(x => x.Name) .Returns(nameof(PluginResourceProvider)); provider.Setup(x => x.ResourceType) .Returns(typeof(PluginResource)); PluginResource pluginResource = null; if (createResource) { var plugin = new Mock <IPlugin>(); var utilities = new Mock <IPluginMulticlientUtilities>(); var connection = new Mock <IConnection>(); var dispatcher = new Mock <IMessageDispatcher>(); dispatcher.SetupGet(x => x.RequestHandlers) .Returns(new RequestHandlers()); connection.SetupGet(x => x.MessageDispatcher) .Returns(dispatcher.Object); plugin.Setup(x => x.Connection) .Returns(connection.Object); var creationResult = new PluginCreationResult( plugin.Object, utilities.Object, new List <OperationClaim>() { OperationClaim.DownloadPackage }); var packageSource = new PackageSource(source: "https://unit.test"); pluginResource = new PluginResource( new[] { creationResult }, packageSource, Mock.Of <ICredentialService>()); } var tryCreateResult = new Tuple <bool, INuGetResource>(pluginResource != null, pluginResource); provider.Setup(x => x.TryCreate(It.IsAny <SourceRepository>(), It.IsAny <CancellationToken>())) .Returns(Task.FromResult(tryCreateResult)); return(provider.Object); }
public async Task TryGetSourceAgnosticPluginAsync_WhenExceptionIsThrownDuringPluginCreation_PropagatesException() { const string pluginFilePath = "a"; const string message = "b"; var reader = Mock.Of <IEnvironmentVariableReader>(); var pluginFactory = new Mock <IPluginFactory>(MockBehavior.Strict); var exception = new Exception(message); pluginFactory.Setup(x => x.GetOrCreateAsync( It.Is <string>(filePath => string.Equals(filePath, pluginFilePath, StringComparison.Ordinal)), It.Is <IEnumerable <string> >(arguments => arguments != null && arguments.Any()), It.IsNotNull <IRequestHandlers>(), It.IsNotNull <ConnectionOptions>(), It.IsAny <CancellationToken>())) .ThrowsAsync(exception); pluginFactory.Setup(x => x.Dispose()); using (var directory = TestDirectory.Create()) using (var pluginManager = new PluginManager( reader, new Lazy <IPluginDiscoverer>(() => Mock.Of <IPluginDiscoverer>()), (TimeSpan idleTimeout) => pluginFactory.Object, new Lazy <string>(() => directory.Path))) { var discoveryResult = new PluginDiscoveryResult( new PluginFile( pluginFilePath, new Lazy <PluginFileState>(() => PluginFileState.Valid))); Tuple <bool, PluginCreationResult> result = await pluginManager.TryGetSourceAgnosticPluginAsync( discoveryResult, OperationClaim.Authentication, CancellationToken.None); bool wasSomethingCreated = result.Item1; PluginCreationResult creationResult = result.Item2; Assert.True(wasSomethingCreated); Assert.NotNull(creationResult); Assert.Equal($"Problem starting the plugin '{pluginFilePath}'. {message}", creationResult.Message); Assert.Same(exception, creationResult.Exception); } pluginFactory.Verify(); }
private async Task <PluginCreationResult> CreatePluginAsync(PluginDiscoveryResult result, PluginRequestKey requestKey, string packageSourceRepository, JObject serviceIndex, CancellationToken cancellationToken) { PluginCreationResult pluginCreationResult = null; if (result.PluginFile.State == PluginFileState.Valid) { var plugin = await _pluginFactory.GetOrCreateAsync( result.PluginFile.Path, PluginConstants.PluginArguments, new RequestHandlers(), _connectionOptions, cancellationToken); var utilities = await PerformOneTimePluginInitializationAsync(plugin, cancellationToken); plugin.Connection.ProtocolVersion.Equals(Plugins.ProtocolConstants.CurrentVersion); var lazyOperationClaims = _pluginOperationClaims.GetOrAdd( requestKey, key => new Lazy <Task <IReadOnlyList <OperationClaim> > >(() => GetPluginOperationClaimsAsync( plugin, packageSourceRepository, serviceIndex, cancellationToken))); await lazyOperationClaims.Value; pluginCreationResult = new PluginCreationResult( plugin, utilities.Value, lazyOperationClaims.Value.Result); } else { pluginCreationResult = new PluginCreationResult(result.Message); } return(pluginCreationResult); }
private async Task SetProxyCredentialsToPlugin(Uri uri, IWebProxy proxy, PluginCreationResult plugin, CancellationToken cancellationToken) { var proxyCredential = proxy.Credentials.GetCredential(uri, _basicAuthenticationType); var key = $"{MessageMethod.SetCredentials}.{Id}"; var proxyCredRequest = new SetCredentialsRequest( uri.AbsolutePath, proxyCredential?.UserName, proxyCredential?.Password, username: null, password: null); await plugin.PluginMulticlientUtilities.DoOncePerPluginLifetimeAsync( key, () => plugin.Plugin.Connection.SendRequestAndReceiveResponseAsync <SetCredentialsRequest, SetCredentialsResponse>( MessageMethod.SetCredentials, proxyCredRequest, cancellationToken), cancellationToken); }
/// <param name="uri">The uri of a web resource for which credentials are needed.</param> /// <param name="proxy">Ignored. Proxy information will not be passed to plugins.</param> /// <param name="type"> /// The type of credential request that is being made. Note that this implementation of /// <see cref="ICredentialProvider"/> does not support providing proxy credenitials and treats /// all other types the same. /// </param> /// <param name="isRetry">If true, credentials were previously supplied by this /// provider for the same uri.</param> /// <param name="message">A message provided by NuGet to show to the user when prompting.</param> /// <param name="nonInteractive">If true, the plugin must not prompt for credentials.</param> /// <param name="cancellationToken">A cancellation token.</param> /// <returns>A credential object.</returns> public async Task <CredentialResponse> GetAsync(Uri uri, IWebProxy proxy, CredentialRequestType type, string message, bool isRetry, bool nonInteractive, CancellationToken cancellationToken) { CredentialResponse taskResponse = null; if (type == CredentialRequestType.Proxy || !_isAnAuthenticationPlugin) { taskResponse = new CredentialResponse(CredentialStatus.ProviderNotApplicable); return(taskResponse); } Tuple <bool, PluginCreationResult> result = await _pluginManager.TryGetSourceAgnosticPluginAsync(_discoveredPlugin, OperationClaim.Authentication, cancellationToken); bool wasSomethingCreated = result.Item1; if (wasSomethingCreated) { PluginCreationResult creationResult = result.Item2; if (!string.IsNullOrEmpty(creationResult.Message)) { // There is a potential here for double logging as the CredentialService itself catches the exceptions and tries to log it. // In reality the logger in the Credential Service will be null because the first request always comes from a resource provider (ServiceIndex provider). _logger.LogError(creationResult.Message); if (creationResult.Exception != null) { _logger.LogDebug(creationResult.Exception.ToString()); } _isAnAuthenticationPlugin = false; throw new PluginException(creationResult.Message, creationResult.Exception); // Throwing here will block authentication and ensure that the complete operation fails. } _isAnAuthenticationPlugin = creationResult.Claims.Contains(OperationClaim.Authentication); if (_isAnAuthenticationPlugin) { AddOrUpdateLogger(creationResult.Plugin); await SetPluginLogLevelAsync(creationResult, _logger, cancellationToken); if (proxy != null) { await SetProxyCredentialsToPlugin(uri, proxy, creationResult, cancellationToken); } var request = new GetAuthenticationCredentialsRequest(uri, isRetry, nonInteractive, _canShowDialog); var credentialResponse = await creationResult.Plugin.Connection.SendRequestAndReceiveResponseAsync <GetAuthenticationCredentialsRequest, GetAuthenticationCredentialsResponse>( MessageMethod.GetAuthenticationCredentials, request, cancellationToken); if (credentialResponse.ResponseCode == MessageResponseCode.NotFound && nonInteractive) { _logger.LogWarning( string.Format( CultureInfo.CurrentCulture, Resources.SecurePluginWarning_UseInteractiveOption)); } taskResponse = GetAuthenticationCredentialsResponseToCredentialResponse(credentialResponse); } } else { _isAnAuthenticationPlugin = false; } return(taskResponse ?? new CredentialResponse(CredentialStatus.ProviderNotApplicable)); }
private async Task <IEnumerable <PluginCreationResult> > GetPluginsForPackageSourceAsync( string packageSourceRepository, ServiceIndexResourceV3 serviceIndex, IEnumerable <PluginDiscoveryResult> results, CancellationToken cancellationToken) { var pluginCreationResults = new List <PluginCreationResult>(); var serviceIndexJson = JObject.Parse(serviceIndex.Json); foreach (var result in results) { PluginCreationResult pluginCreationResult = null; if (result.PluginFile.State == PluginFileState.Valid) { var plugin = await _pluginFactory.GetOrCreateAsync( result.PluginFile.Path, PluginConstants.PluginArguments, new RequestHandlers(), _connectionOptions, cancellationToken); var utilities = _pluginUtilities.GetOrAdd( plugin.Id, path => new Lazy <IPluginMulticlientUtilities>( () => new PluginMulticlientUtilities())); await utilities.Value.DoOncePerPluginLifetimeAsync( MessageMethod.MonitorNuGetProcessExit.ToString(), () => plugin.Connection.SendRequestAndReceiveResponseAsync <MonitorNuGetProcessExitRequest, MonitorNuGetProcessExitResponse>( MessageMethod.MonitorNuGetProcessExit, new MonitorNuGetProcessExitRequest(_currentProcessId.Value), cancellationToken), cancellationToken); await utilities.Value.DoOncePerPluginLifetimeAsync( MessageMethod.Initialize.ToString(), () => InitializePluginAsync(plugin, _connectionOptions.RequestTimeout, cancellationToken), cancellationToken); var lazyOperationClaims = _pluginOperationClaims.GetOrAdd( new PluginPackageSourceKey(result.PluginFile.Path, packageSourceRepository), key => new Lazy <Task <IReadOnlyList <OperationClaim> > >(() => GetPluginOperationClaimsAsync( plugin, packageSourceRepository, serviceIndexJson, cancellationToken))); await lazyOperationClaims.Value; pluginCreationResult = new PluginCreationResult( plugin, utilities.Value, lazyOperationClaims.Value.Result); } else { pluginCreationResult = new PluginCreationResult(result.Message); } pluginCreationResults.Add(pluginCreationResult); } return(pluginCreationResults); }