public async Task GetAsync_WithValidArguments_ReturnsValidCredentials() { var expectation = new TestExpectation( operationClaims: new[] { OperationClaim.Authentication }, connectionOptions: ConnectionOptions.CreateDefault(), pluginVersion: ProtocolConstants.CurrentVersion, uri: _uri, authenticationUsername: _username, authenticationPassword: _password, success: true); using (var test = new PluginManagerMock( pluginFilePath: "a", pluginFileState: PluginFileState.Valid, expectations: expectation)) { var discoveryResult = new PluginDiscoveryResult(new PluginFile("a", new Lazy <PluginFileState>(() => PluginFileState.Valid))); var provider = new SecurePluginCredentialProvider(test.PluginManager, discoveryResult, canShowDialog: true, logger: NullLogger.Instance); IWebProxy proxy = null; var credType = CredentialRequestType.Unauthorized; var message = "nothing"; var isRetry = false; var isInteractive = false; var token = CancellationToken.None; var credentialResponse = await provider.GetAsync(_uri, proxy, credType, message, isRetry, isInteractive, token); Assert.True(credentialResponse.Status == CredentialStatus.Success); Assert.NotNull(credentialResponse.Credentials); Assert.Equal(_username, credentialResponse.Credentials.GetCredential(_uri, authType: null).UserName); Assert.Equal(_password, credentialResponse.Credentials.GetCredential(_uri, authType: null).Password); } }
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 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(); }
/// <summary> /// Create a credential provider based on provided plugin /// </summary> /// <param name="pluginManager"></param> /// <param name="pluginDiscoveryResult"></param> /// <param name="logger"></param> /// <exception cref="ArgumentNullException">if <paramref name="pluginDiscoveryResult"/> is null</exception> /// <exception cref="ArgumentNullException">if <paramref name="logger"/> is null</exception> /// <exception cref="ArgumentNullException">if <paramref name="pluginManager"/> is null</exception> /// <exception cref="ArgumentException">if plugin file is not valid</exception> public SecurePluginCredentialProvider(IPluginManager pluginManager, PluginDiscoveryResult pluginDiscoveryResult, ILogger logger) { _pluginManager = pluginManager ?? throw new ArgumentNullException(nameof(pluginManager)); _discoveredPlugin = pluginDiscoveryResult ?? throw new ArgumentNullException(nameof(pluginDiscoveryResult)); _logger = logger ?? throw new ArgumentNullException(nameof(logger)); Id = $"{nameof(SecurePluginCredentialProvider)}_{pluginDiscoveryResult.PluginFile.Path}"; }
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_InitializesProperties() { var pluginFile = new PluginFile(filePath: "a", state: new Lazy <PluginFileState>(() => PluginFileState.InvalidEmbeddedSignature)); var result = new PluginDiscoveryResult(pluginFile); Assert.Same(pluginFile, result.PluginFile); }
/// <summary> /// Creates a plugin from the given pluginDiscoveryResult. /// This plugin's operations will be source agnostic ones /// </summary> /// <param name="pluginDiscoveryResult"></param> /// <param name="cancellationToken"></param> /// <returns>A PluginCreationResult</returns> public Task <PluginCreationResult> CreateSourceAgnosticPluginAsync(PluginDiscoveryResult pluginDiscoveryResult, CancellationToken cancellationToken) { if (pluginDiscoveryResult == null) { throw new ArgumentNullException(nameof(pluginDiscoveryResult)); } return(CreatePluginAsync(pluginDiscoveryResult, new PluginRequestKey(pluginDiscoveryResult.PluginFile.Path, "Source-Agnostic"), null, null, cancellationToken)); }
public void Constructor_InitializesProperties() { var pluginFile = new PluginFile(filePath: "a", state: PluginFileState.InvalidEmbeddedSignature); var result = new PluginDiscoveryResult(pluginFile, message: "b"); Assert.Same(pluginFile, result.PluginFile); Assert.Equal("b", result.Message); }
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); } }
/// <summary> /// Create a credential provider based on provided plugin /// </summary> /// <param name="pluginManager"></param> /// <param name="pluginDiscoveryResult"></param> /// <param name="logger"></param> /// <exception cref="ArgumentNullException">if <paramref name="pluginDiscoveryResult"/> is null</exception> /// <exception cref="ArgumentNullException">if <paramref name="logger"/> is null</exception> /// <exception cref="ArgumentNullException">if <paramref name="pluginManager"/> is null</exception> /// <exception cref="ArgumentException">if plugin file is not valid</exception> public SecurePluginCredentialProvider(IPluginManager pluginManager, PluginDiscoveryResult pluginDiscoveryResult, ILogger logger) { if (pluginDiscoveryResult == null) { throw new ArgumentNullException(nameof(pluginDiscoveryResult)); } if (pluginDiscoveryResult.PluginFile.State != PluginFileState.Valid) { throw new ArgumentException(string.Format(Resources.SecureCredentialProvider_InvalidPluginFile, pluginDiscoveryResult.PluginFile.State, pluginDiscoveryResult.Message), nameof(pluginDiscoveryResult)); } _pluginManager = pluginManager ?? throw new ArgumentNullException(nameof(pluginManager)); _discoveredPlugin = pluginDiscoveryResult; _logger = logger ?? throw new ArgumentNullException(nameof(logger)); Id = $"{nameof(SecurePluginCredentialProvider)}_{pluginDiscoveryResult.PluginFile.Path}"; }
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(); }
public void TryCreate_SetsProxyCredentials() { var uri = new Uri("https://api.nuget.org/v3/index.json"); var authUsername = "******"; var authPassword = "******"; var proxyUsername = "******"; var proxyPassword = "******"; var expectation = new TestExpectation( serviceIndexJson: null, sourceUri: null, operationClaims: new[] { OperationClaim.Authentication }, connectionOptions: ConnectionOptions.CreateDefault(), pluginVersion: ProtocolConstants.CurrentVersion, uri: uri, authenticationUsername: authUsername, authenticationPassword: authPassword, success: true, proxyUsername: proxyUsername, proxyPassword: proxyPassword ); using (var test = new PluginManagerMock( pluginFilePath: "a", pluginFileState: PluginFileState.Valid, expectations: expectation)) { var discoveryResult = new PluginDiscoveryResult(new PluginFile("a", new Lazy <PluginFileState>(() => PluginFileState.Valid))); var provider = new SecurePluginCredentialProvider(test.PluginManager, discoveryResult, NullLogger.Instance); var proxy = new System.Net.WebProxy() { Credentials = new NetworkCredential(proxyUsername, proxyPassword) }; var credType = CredentialRequestType.Unauthorized; var message = "nothing"; var isRetry = false; var isInteractive = false; var token = CancellationToken.None; var credentialResponse = provider.GetAsync(uri, proxy, credType, message, isRetry, isInteractive, token).Result; Assert.True(credentialResponse.Status == CredentialStatus.Success); Assert.NotNull(credentialResponse.Credentials); Assert.Equal(authUsername, credentialResponse.Credentials.GetCredential(uri, null).UserName); Assert.Equal(authPassword, credentialResponse.Credentials.GetCredential(uri, null).Password); } }
public void TryCreate_DoesNotCreateNonCredentialsPluginTwice() { var uri = new Uri("https://api.nuget.org/v3/index.json"); var authUsername = "******"; var authPassword = "******"; var expectation = new TestExpectation( serviceIndexJson: null, sourceUri: null, operationClaims: new[] { OperationClaim.DownloadPackage }, connectionOptions: ConnectionOptions.CreateDefault(), pluginVersion: ProtocolConstants.CurrentVersion, uri: uri, authenticationUsername: authUsername, authenticationPassword: authPassword, success: false ); using (var test = new PluginManagerMock( pluginFilePath: "a", pluginFileState: PluginFileState.Valid, expectations: expectation)) { var discoveryResult = new PluginDiscoveryResult(new PluginFile("a", PluginFileState.Valid)); var provider = new SecurePluginCredentialProvider(test.PluginManager, discoveryResult, NullLogger.Instance); System.Net.IWebProxy proxy = null; var credType = CredentialRequestType.Unauthorized; var message = "nothing"; var isRetry = false; var isInteractive = false; var token = CancellationToken.None; var credentialResponse = provider.GetAsync(uri, proxy, credType, message, isRetry, isInteractive, token).Result; Assert.True(credentialResponse.Status == CredentialStatus.ProviderNotApplicable); Assert.Null(credentialResponse.Credentials); var credentialResponse2 = provider.GetAsync(uri, proxy, credType, message, isRetry, isInteractive, token).Result; Assert.True(credentialResponse2.Status == CredentialStatus.ProviderNotApplicable); Assert.Null(credentialResponse2.Credentials); } }
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); }
public async Task GetAsync_WhenCalledMultipleTimes_DoesNotCreateMultipleInstancesOfANonCredentialsPlugin() { var expectation = new TestExpectation( operationClaims: new[] { OperationClaim.DownloadPackage }, connectionOptions: ConnectionOptions.CreateDefault(), pluginVersion: ProtocolConstants.CurrentVersion, uri: _uri, authenticationUsername: _username, authenticationPassword: _password, success: false); using (var test = new PluginManagerMock( pluginFilePath: "a", pluginFileState: PluginFileState.Valid, expectations: expectation)) { var discoveryResult = new PluginDiscoveryResult(new PluginFile("a", new Lazy <PluginFileState>(() => PluginFileState.Valid))); var provider = new SecurePluginCredentialProvider(test.PluginManager, discoveryResult, canShowDialog: true, logger: NullLogger.Instance); IWebProxy proxy = null; var credType = CredentialRequestType.Unauthorized; var message = "nothing"; var isRetry = false; var isInteractive = false; var token = CancellationToken.None; var credentialResponse = await provider.GetAsync(_uri, proxy, credType, message, isRetry, isInteractive, token); Assert.True(credentialResponse.Status == CredentialStatus.ProviderNotApplicable); Assert.Null(credentialResponse.Credentials); var credentialResponse2 = await provider.GetAsync(_uri, proxy, credType, message, isRetry, isInteractive, token); Assert.True(credentialResponse2.Status == CredentialStatus.ProviderNotApplicable); Assert.Null(credentialResponse2.Credentials); } }
/// <summary> /// Creates a plugin from the given pluginDiscoveryResult. /// This plugin's operations will be source agnostic ones (Authentication) /// </summary> /// <param name="pluginDiscoveryResult">plugin discovery result</param> /// <param name="requestedOperationClaim">The requested operation claim</param> /// <param name="cancellationToken">cancellation token</param> /// <returns>A plugin creation result, null if the requested plugin cannot handle the given operation claim</returns> public Task <Tuple <bool, PluginCreationResult> > TryGetSourceAgnosticPluginAsync(PluginDiscoveryResult pluginDiscoveryResult, OperationClaim requestedOperationClaim, CancellationToken cancellationToken) { if (pluginDiscoveryResult == null) { throw new ArgumentNullException(nameof(pluginDiscoveryResult)); } return(TryCreatePluginAsync( pluginDiscoveryResult, requestedOperationClaim, new PluginRequestKey(pluginDiscoveryResult.PluginFile.Path, "Source-Agnostic"), packageSourceRepository: null, serviceIndex: null, cancellationToken: cancellationToken)); }