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();
        }
Beispiel #2
0
        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);
            }
        }
Beispiel #3
0
        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));
            }
        }
Beispiel #4
0
        /// <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);
        }
Beispiel #8
0
        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);
        }
Beispiel #11
0
        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();
        }
Beispiel #12
0
        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));
        }
Beispiel #15
0
        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);
        }