public void ParsingChallengeResponderShouldWorkIfCallerHasMsiAccessToStorage() { var az = new Mock <IAzureHelper>(); var factory = new Mock <IStorageFactory>(); var storage = new Mock <IStorageProvider>(); // check is used by parser to verify MSI access storage.Setup(x => x.ExistsAsync(RenewalOptionParser.FileNameForPermissionCheck, It.IsAny <CancellationToken>())) .Returns(Task.FromResult(false)); factory.Setup(x => x.FromMsiAsync("example", new StorageProperties().ContainerName, It.IsAny <CancellationToken>())) .Returns(Task.FromResult(storage.Object)); var kvFactory = new Mock <IKeyVaultFactory>(); var client = new Mock <CertificateClient>(); kvFactory.Setup(x => x.CreateCertificateClient("example")) .Returns(client.Object); var log = new Mock <ILoggerFactory>(); IRenewalOptionParser parser = new RenewalOptionParser( az.Object, kvFactory.Object, factory.Object, new Mock <IAzureAppServiceClient>().Object, new Mock <IAzureCdnClient>().Object, log.Object); var cfg = TestHelper.LoadConfig("config"); new Func <Task>(async() => _ = await parser.ParseChallengeResponderAsync(cfg.Certificates[0], CancellationToken.None)).Should().NotThrow(); }
public void ParsingChallengeResponderShouldFailIfCallerHasNoMsiAccessToStorageAndFallbacksAreNotAvailable() { var az = new Mock <IAzureHelper>(); var kv = new Mock <IKeyVaultClient>(); var factory = new Mock <IStorageFactory>(); var storage = new Mock <IStorageProvider>(); // check is used by parser to verify MSI access storage.Setup(x => x.ExistsAsync(RenewalOptionParser.FileNameForPermissionCheck, It.IsAny <CancellationToken>())) .Throws(new StorageException(new RequestResult { HttpStatusCode = (int)HttpStatusCode.Forbidden }, "Access denied, due to missing MSI permissions", null)); // fallback is keyvault -> secret not found. kv.Setup(x => x.GetSecretWithHttpMessagesAsync(It.IsAny <string>(), It.IsAny <string>(), It.IsAny <string>(), It.IsAny <Dictionary <string, List <string> > >(), It.IsAny <CancellationToken>())) .Throws(new KeyVaultErrorException("denied") { Response = new HttpResponseMessageWrapper(new HttpResponseMessage { StatusCode = HttpStatusCode.NotFound }, "denied") }); factory.Setup(x => x.FromMsiAsync("example", new StorageProperties().ContainerName, It.IsAny <CancellationToken>())) .Returns(Task.FromResult(storage.Object)); var log = new Mock <ILogger>(); IRenewalOptionParser parser = new RenewalOptionParser(az.Object, kv.Object, factory.Object, log.Object); var cfg = TestHelper.LoadConfig("config"); new Func <Task>(async() => _ = await parser.ParseChallengeResponderAsync(cfg.Certificates[0], CancellationToken.None)).Should().Throw <InvalidOperationException>(); kv.Verify(x => x.GetSecretWithHttpMessagesAsync("https://example.vault.azure.net", new StorageProperties().SecretName, "", null, CancellationToken.None), Times.Once); }
public async Task ParsingChallengeResponderShouldSucceedIfCallerHasNoMsiAccessToConnectionStringFallbackIsAvailable() { var az = new Mock <IAzureHelper>(); var kv = new Mock <IKeyVaultClient>(); var factory = new Mock <IStorageFactory>(); var storage = new Mock <IStorageProvider>(); // check is used by parser to verify MSI access storage.Setup(x => x.ExistsAsync(RenewalOptionParser.FileNameForPermissionCheck, It.IsAny <CancellationToken>())) .Throws(new StorageException(new RequestResult { HttpStatusCode = (int)HttpStatusCode.Forbidden }, "Access denied, due to missing MSI permissions", null)); // fallback is connectionString const string connectionString = "this will grant me access"; factory.Setup(x => x.FromMsiAsync("example", new StorageProperties().ContainerName, It.IsAny <CancellationToken>())) .Returns(Task.FromResult(storage.Object)); // fallback factory.Setup(x => x.FromConnectionString(connectionString, new StorageProperties().ContainerName)) .Returns(storage.Object); var log = new Mock <ILogger>(); IRenewalOptionParser parser = new RenewalOptionParser(az.Object, kv.Object, factory.Object, log.Object); var cfg = TestHelper.LoadConfig("config+connectionstring"); var r = await parser.ParseChallengeResponderAsync(cfg.Certificates[0], CancellationToken.None); // keyvault should not be tried if connectionstring is found kv.VerifyNoOtherCalls(); factory.Verify(x => x.FromConnectionString(connectionString, new StorageProperties().ContainerName), Times.Once); }
public async Task LoadingConfigWithCustomStoragePathShouldUseIt() { IConfigurationProcessor processor = new ConfigurationProcessor(); var content = File.ReadAllText("Files/config+custompath.json"); var cfg = processor.ValidateAndLoad(content); var cert = cfg.Certificates[0]; cert.HostNames.Should().BeEquivalentTo(new[] { "example.com", "www.example.com" }); cert.ChallengeResponder.Should().NotBeNull(); // fake grant MSI access var factory = new Mock <IStorageFactory>(); var storage = new Mock <IStorageProvider>(); storage.Setup(x => x.ExistsAsync(It.IsAny <string>(), It.IsAny <CancellationToken>())) .Returns(Task.FromResult(true)); factory.Setup(x => x.FromMsiAsync(It.IsAny <string>(), It.IsAny <string>(), It.IsAny <CancellationToken>())) .Returns(Task.FromResult(storage.Object)); var parser = new RenewalOptionParser( new Mock <IAzureHelper>().Object, new Mock <IKeyVaultClient>().Object, factory.Object, new Mock <IAzureAppServiceClient>().Object, new Mock <IAzureCdnClient>().Object, new Mock <ILoggerFactory>().Object); var responder = await parser.ParseChallengeResponderAsync(cert, CancellationToken.None); var ctx = new Mock <IChallengeContext>(); // Certes .Http() extension method internall filters for this type ctx.SetupGet(x => x.Type) .Returns("http-01"); ctx.SetupGet(x => x.Token) .Returns("fileNAME"); ctx.SetupGet(x => x.KeyAuthz) .Returns("$content"); var auth = new Mock <IAuthorizationContext>(); auth.Setup(x => x.Challenges()) .Returns(Task.FromResult(new[] { ctx.Object }.AsEnumerable())); var order = new Mock <IOrderContext>(); order.Setup(x => x.Authorizations()) .Returns(Task.FromResult(new[] { auth.Object }.AsEnumerable())); _ = await responder.InitiateChallengesAsync(order.Object, CancellationToken.None); const string pathPrefix = "not/well-known/"; storage.Verify(x => x.SetAsync(pathPrefix + "fileNAME", "$content", It.IsAny <CancellationToken>()), Times.Once); }
public async Task ParsingChallengeResponderShouldSucceedIfCallerHasNoMsiAccessToStorageButKeyVaultFallbackIsAvailable() { var az = new Mock <IAzureHelper>(); var factory = new Mock <IStorageFactory>(); var storage = new Mock <IStorageProvider>(); // check is used by parser to verify MSI access storage.Setup(x => x.ExistsAsync(RenewalOptionParser.FileNameForPermissionCheck, It.IsAny <CancellationToken>())) .Throws(new RequestFailedException((int)HttpStatusCode.Forbidden, "Access denied, due to missing MSI permissions")); factory.Setup(x => x.FromMsiAsync("example", new StorageProperties().ContainerName, It.IsAny <CancellationToken>())) .Returns(Task.FromResult(storage.Object)); var log = new Mock <ILoggerFactory>(); log.Setup(x => x.CreateLogger(It.IsAny <string>())) .Returns(new Mock <ILogger>().Object); var kvFactory = new Mock <IKeyVaultFactory>(); var client = new Mock <CertificateClient>(); var secretClient = new Mock <SecretClient>(); // fallback is keyvault const string connectionString = "this will grant me access"; var response = new Mock <Response <KeyVaultSecret> >(); response.SetupGet(x => x.Value) .Returns(new KeyVaultSecret(new StorageProperties().SecretName, connectionString)); secretClient.Setup(x => x.GetSecretAsync(It.IsAny <string>(), It.IsAny <string>(), It.IsAny <CancellationToken>())) .ReturnsAsync(response.Object); kvFactory.Setup(x => x.CreateSecretClient("example")) .Returns(secretClient.Object); kvFactory.Setup(x => x.CreateCertificateClient("example")) .Returns(client.Object); // fallback factory.Setup(x => x.FromConnectionString(connectionString, new StorageProperties().ContainerName)) .Returns(storage.Object); IRenewalOptionParser parser = new RenewalOptionParser( az.Object, kvFactory.Object, factory.Object, new Mock <IAzureAppServiceClient>().Object, new Mock <IAzureCdnClient>().Object, log.Object); var cfg = TestHelper.LoadConfig("config"); var r = await parser.ParseChallengeResponderAsync(cfg.Certificates[0], CancellationToken.None); secretClient.Verify(x => x.GetSecretAsync(new StorageProperties().SecretName, null, CancellationToken.None), Times.Once); factory.Verify(x => x.FromConnectionString(connectionString, new StorageProperties().ContainerName), Times.Once); }
public async Task ParsingChallengeResponderShouldSucceedIfCallerHasNoMsiAccessToStorageButKeyVaultFallbackIsAvailable() { var az = new Mock <IAzureHelper>(); var kv = new Mock <IKeyVaultClient>(); var factory = new Mock <IStorageFactory>(); var storage = new Mock <IStorageProvider>(); // check is used by parser to verify MSI access storage.Setup(x => x.ExistsAsync(RenewalOptionParser.FileNameForPermissionCheck, It.IsAny <CancellationToken>())) .Throws(new StorageException(new RequestResult { HttpStatusCode = (int)HttpStatusCode.Forbidden }, "Access denied, due to missing MSI permissions", null)); // fallback is keyvault const string connectionString = "this will grant me access"; kv.Setup(x => x.GetSecretWithHttpMessagesAsync(It.IsAny <string>(), It.IsAny <string>(), It.IsAny <string>(), It.IsAny <Dictionary <string, List <string> > >(), It.IsAny <CancellationToken>())) .Returns(Task.FromResult(new AzureOperationResponse <SecretBundle> { Body = new SecretBundle(connectionString) })); factory.Setup(x => x.FromMsiAsync("example", new StorageProperties().ContainerName, It.IsAny <CancellationToken>())) .Returns(Task.FromResult(storage.Object)); // fallback factory.Setup(x => x.FromConnectionString(connectionString, new StorageProperties().ContainerName)) .Returns(storage.Object); var log = new Mock <ILoggerFactory>(); log.Setup(x => x.CreateLogger(It.IsAny <string>())) .Returns(new Mock <ILogger>().Object); IRenewalOptionParser parser = new RenewalOptionParser( az.Object, kv.Object, factory.Object, new Mock <IAzureAppServiceClient>().Object, new Mock <IAzureCdnClient>().Object, log.Object); var cfg = TestHelper.LoadConfig("config"); var r = await parser.ParseChallengeResponderAsync(cfg.Certificates[0], CancellationToken.None); kv.Verify(x => x.GetSecretWithHttpMessagesAsync("https://example.vault.azure.net", new StorageProperties().SecretName, "", null, CancellationToken.None), Times.Once); factory.Verify(x => x.FromConnectionString(connectionString, new StorageProperties().ContainerName), Times.Once); }
public void ParsingChallengeResponderShouldFailIfCallerHasNoMsiAccessToStorageAndFallbacksAreNotAvailable() { var az = new Mock <IAzureHelper>(); var factory = new Mock <IStorageFactory>(); var storage = new Mock <IStorageProvider>(); // check is used by parser to verify MSI access storage.Setup(x => x.ExistsAsync(RenewalOptionParser.FileNameForPermissionCheck, It.IsAny <CancellationToken>())) .Throws(new RequestFailedException((int)HttpStatusCode.Forbidden, "Access denied, due to missing MSI permissions")); factory.Setup(x => x.FromMsiAsync("example", new StorageProperties().ContainerName, It.IsAny <CancellationToken>())) .Returns(Task.FromResult(storage.Object)); var log = new Mock <ILoggerFactory>(); log.Setup(x => x.CreateLogger(It.IsAny <string>())) .Returns(new Mock <ILogger>().Object); var kvFactory = new Mock <IKeyVaultFactory>(); var client = new Mock <CertificateClient>(); var secretClient = new Mock <SecretClient>(); // fallback is keyvault -> secret not found. secretClient.Setup(x => x.GetSecretAsync(It.IsAny <string>(), It.IsAny <string>(), It.IsAny <CancellationToken>())) .Throws(new RequestFailedException((int)HttpStatusCode.NotFound, "denied")); kvFactory.Setup(x => x.CreateSecretClient("example")) .Returns(secretClient.Object); kvFactory.Setup(x => x.CreateCertificateClient("example")) .Returns(client.Object); IRenewalOptionParser parser = new RenewalOptionParser( az.Object, kvFactory.Object, factory.Object, new Mock <IAzureAppServiceClient>().Object, new Mock <IAzureCdnClient>().Object, log.Object); var cfg = TestHelper.LoadConfig("config"); new Func <Task>(async() => _ = await parser.ParseChallengeResponderAsync(cfg.Certificates[0], CancellationToken.None)).Should().Throw <InvalidOperationException>(); secretClient.Verify(x => x.GetSecretAsync(new StorageProperties().SecretName, null, CancellationToken.None), Times.Once); }