示例#1
0
        private async Task <HttpResponseMessage> SendRequestToApiAsync(HttpRequestMessage request)
        {
            using (var dependencyMeasurement = DependencyMeasurement.Start())
            {
                HttpResponseMessage response = null;
                try
                {
                    response = await _httpClient.SendAsync(request);

                    _logger.LogRequest(request, response, dependencyMeasurement.Elapsed);

                    return(response);
                }
                finally
                {
                    try
                    {
                        var statusCode = response?.StatusCode ?? HttpStatusCode.InternalServerError;
                        _logger.LogHttpDependency(request, statusCode, dependencyMeasurement);
                    }
                    catch (Exception ex)
                    {
                        _logger.LogWarning("Failed to log HTTP dependency. Reason: {Message}", ex.Message);
                    }
                }
            }
        }
        /// <summary>
        /// Retrieves the secret value, based on the given name
        /// </summary>
        /// <param name="secretName">The name of the secret</param>
        /// <returns>Returns a <see cref="Secret"/> that contains the secret</returns>
        /// <exception cref="ArgumentException">The <paramref name="secretName"/> must not be empty</exception>
        /// <exception cref="ArgumentNullException">The <paramref name="secretName"/> must not be null</exception>
        /// <exception cref="SecretNotFoundException">The secret was not found, using the given name</exception>
        /// <exception cref="KeyVaultErrorException">The call for a secret resulted in an invalid response</exception>
        public virtual async Task <Secret> GetSecretAsync(string secretName)
        {
            Guard.NotNullOrWhitespace(secretName, nameof(secretName), "Requires a non-blank secret name to request a secret in Azure Key Vault");
            Guard.For <FormatException>(() => !SecretNameRegex.IsMatch(secretName), "Requires a secret name in the correct format to request a secret in Azure Key Vault, see https://docs.microsoft.com/en-us/azure/key-vault/general/about-keys-secrets-certificates#objects-identifiers-and-versioning");

            var isSuccessful = false;

            using (DependencyMeasurement measurement = DependencyMeasurement.Start())
            {
                try
                {
                    Secret secret = await GetSecretCoreAsync(secretName);

                    isSuccessful = true;

                    return(secret);
                }
                finally
                {
                    if (_options.TrackDependency)
                    {
                        Logger.LogDependency(DependencyName, secretName, VaultUri, isSuccessful, measurement);
                    }
                }
            }
        }
示例#3
0
        private async Task <SecretData> GetTrackedSecretAsync(string secretName)
        {
            var context = new Dictionary <string, object>
            {
                ["SecretEngine Type"]    = "KeyValue",
                ["SecretEngine Version"] = _options.KeyValueVersion
            };

            var isSuccessful = false;

            using (DependencyMeasurement measurement = DependencyMeasurement.Start())
            {
                try
                {
                    _logger.LogTrace("Getting a secret {SecretName} from HashiCorp Vault {VaultUri}...", secretName, _vaultClient.Settings.VaultServerUriWithPort);
                    SecretData result = await ReadSecretDataAsync(_secretPath);

                    isSuccessful = true;
                    return(result);
                }
                finally
                {
                    _logger.LogTrace("{Result} secret from HashiCorp Vault {VaultUri}", isSuccessful ? "Got" : "Couldn't get", _vaultClient.Settings.VaultServerUriWithPort);
                    if (_options.TrackDependency)
                    {
                        _logger.LogDependency(DependencyName, secretName, _vaultClient.Settings.VaultServerUriWithPort, isSuccessful, measurement, context);
                    }
                }
            }
        }
示例#4
0
        /// <summary>
        /// Retrieves the secret value, based on the given name
        /// </summary>
        /// <param name="secretName">The name of the secret</param>
        /// <returns>Returns a <see cref="Secret"/> that contains the secret</returns>
        /// <exception cref="ArgumentException">The <paramref name="secretName"/> must not be empty</exception>
        /// <exception cref="ArgumentNullException">The <paramref name="secretName"/> must not be null</exception>
        /// <exception cref="SecretNotFoundException">The secret was not found, using the given name</exception>
        /// <exception cref="KeyVaultErrorException">The call for a secret resulted in an invalid response</exception>
        public virtual async Task <Secret> GetSecretAsync(string secretName)
        {
            Guard.NotNullOrWhitespace(secretName, nameof(secretName), "Requires a non-blank secret name to request a secret in Azure Key Vault");

            var isSuccessful = false;

#if NET6_0
            using (DurationMeasurement measurement = DurationMeasurement.Start())
#else
            using (DependencyMeasurement measurement = DependencyMeasurement.Start())
#endif
            {
                try
                {
                    Secret secret = await GetSecretCoreAsync(secretName);

                    isSuccessful = true;

                    return(secret);
                }
                finally
                {
                    if (_options.TrackDependency)
                    {
                        Logger.LogDependency(DependencyName, secretName, VaultUri, isSuccessful, measurement);
                    }
                }
            }
        }
        public async Task DependencyMeasurement_StartMeasuringAction_HoldsStartAndElapsedTime()
        {
            // Act
            using (var measurement = DependencyMeasurement.Start())
            {
                // Assert
                await Task.Delay(TimeSpan.FromMilliseconds(100));

                Assert.NotNull(measurement);
                Assert.True(DateTimeOffset.UtcNow > measurement.StartTime, "Measurement should have lesser start time");
                Assert.True(TimeSpan.Zero < measurement.Elapsed, "Measurement should be running");
            }
        }
        public async Task DependencyMeasurement_StopsMeasuringAction_WhenDisposed()
        {
            // Arrange
            var measurement = DependencyMeasurement.Start();
            await Task.Delay(TimeSpan.FromMilliseconds(100));

            measurement.Dispose();
            var elapsed = measurement.Elapsed;

            // Act
            await Task.Delay(TimeSpan.FromMilliseconds(100));

            // Assert
            Assert.NotNull(measurement);
            Assert.Equal(elapsed, measurement.Elapsed);
        }
示例#7
0
        /// <summary>
        /// Retrieves the secret value in the HashiCorp KeyValue Vault on given <paramref name="secretName"/>
        /// while tracking the dependency interaction call with the vault.
        /// </summary>
        /// <param name="secretName">The name of the HashiCorp secret.</param>
        /// <returns>
        ///     The HashiCorp <see cref="SecretData"/> concrete instance that contains the secret value.
        /// </returns>
        /// <exception cref="ArgumentException">Thrown when the <paramref name="secretName"/> is blank.</exception>
        /// <exception cref="ArgumentOutOfRangeException">
        ///     Thrown when the <see cref="Options"/>'s <see cref="HashiCorpVaultOptions.KeyValueVersion"/> represents an unknown secret engine version.
        /// </exception>
        protected async Task <SecretData> GetTrackedSecretAsync(string secretName)
        {
            Guard.NotNullOrWhitespace(secretName, nameof(secretName),
                                      $"Requires a non-blank secret name to look up the secret in the HashiCorp Vault {Options.KeyValueVersion} KeyValue secret engine");

            var context = new Dictionary <string, object>
            {
                ["SecretEngine Type"]    = "KeyValue",
                ["SecretEngine Version"] = Options.KeyValueVersion
            };

            var isSuccessful = false;

#if NET6_0
            using (DurationMeasurement measurement = DurationMeasurement.Start())
#else
            using (DependencyMeasurement measurement = DependencyMeasurement.Start())
#endif
            {
                try
                {
                    Logger.LogTrace("Getting a secret {SecretName} from HashiCorp Vault {VaultUri}...", secretName, VaultClient.Settings.VaultServerUriWithPort);
                    SecretData result = await ReadSecretDataAsync();

                    Logger.LogTrace("Secret '{SecretName}' was successfully retrieved from HashiCorp Vault {VaultUri}", secretName, VaultClient.Settings.VaultServerUriWithPort);

                    isSuccessful = true;
                    return(result);
                }
                catch (Exception exception)
                {
                    Logger.LogError(exception, "Secret '{SecretName}' was not successfully retrieved from HashiCorp Vault {VaultUri}, cause: {Message}",
                                    secretName, VaultClient.Settings.VaultServerUriWithPort, exception.Message);

                    throw;
                }
                finally
                {
                    if (Options.TrackDependency)
                    {
                        Logger.LogDependency(DependencyName, secretName, VaultClient.Settings.VaultServerUriWithPort, isSuccessful, measurement, context);
                    }
                }
            }
        }
示例#8
0
        private async Task <HttpResponseMessage> SendRequestToApiAsync(HttpRequestMessage request)
        {
            using (var dependencyMeasurement = DependencyMeasurement.Start())
            {
                HttpResponseMessage response = null;
                try
                {
                    _httpClient.BaseAddress = new Uri($"http://{_configuration.CurrentValue.Host}:{_configuration.CurrentValue.Port}");
                    response = await _httpClient.SendAsync(request);

                    _logger.LogRequest(request, response, dependencyMeasurement.Elapsed);

                    return(response);
                }
                finally
                {
                    var statusCode = response?.StatusCode ?? HttpStatusCode.InternalServerError;
                    _logger.LogHttpDependency(request, statusCode, dependencyMeasurement);
                }
            }
        }
示例#9
0
        private async Task <HttpResponseMessage> SendRequestToApiAsync(HttpRequestMessage request)
        {
            var client = CreateHttpClient();

            using (var dependencyMeasurement = DependencyMeasurement.Start())
            {
                HttpResponseMessage response = null;
                try
                {
                    response = await client.SendAsync(request);

                    _logger.LogRequest(request, response, dependencyMeasurement.Elapsed);

                    return(response);
                }
                finally
                {
                    var statusCode = response?.StatusCode ?? HttpStatusCode.InternalServerError;
                    _logger.LogHttpDependency(request, statusCode, dependencyMeasurement);
                }
            }
        }
示例#10
0
        public async Task <List <Resource> > QueryAsync(string query, List <string> targetSubscriptions)
        {
            Guard.NotNullOrWhitespace(query, nameof(query));

            var graphClient = await GetOrCreateClient();

            bool isSuccessfulDependency = false;

            using (var dependencyMeasurement = DependencyMeasurement.Start())
            {
                try
                {
                    var queryRequest = new QueryRequest(targetSubscriptions, query);
                    var response     = await graphClient.ResourcesAsync(queryRequest);

                    isSuccessfulDependency = true;

                    var foundResources = ParseQueryResults(response);

                    return(foundResources);
                }
                catch (ErrorResponseException responseException)
                {
                    if (responseException.Response != null)
                    {
                        if (responseException.Response.StatusCode == HttpStatusCode.Forbidden)
                        {
                            var unauthorizedException = new UnauthorizedException(QueryApplicationId, targetSubscriptions);
                            _logger.LogCritical(unauthorizedException, "Unable to query Azure Resource Graph");
                            throw unauthorizedException;
                        }

                        if (responseException.Response.StatusCode == HttpStatusCode.BadRequest)
                        {
                            var response     = JToken.Parse(responseException.Response.Content);
                            var errorDetails = response["error"]?["details"];
                            if (errorDetails != null)
                            {
                                var errorCodes = new List <string>();
                                foreach (var detailEntry in errorDetails)
                                {
                                    errorCodes.Add(detailEntry["code"]?.ToString());
                                }

                                if (errorCodes.Any(errorCode => errorCode.Equals("NoValidSubscriptionsInQueryRequest", StringComparison.InvariantCultureIgnoreCase)))
                                {
                                    var invalidSubscriptionException = new QueryContainsInvalidSubscriptionException(targetSubscriptions);
                                    _logger.LogCritical(invalidSubscriptionException, "Unable to query Azure Resource Graph");
                                    throw invalidSubscriptionException;
                                }
                            }
                        }
                    }

                    throw;
                }
                finally
                {
                    var contextualInformation = new Dictionary <string, object>
                    {
                        { "Query", query },
                        { "Subscriptions", targetSubscriptions }
                    };

                    _logger.LogDependency("Azure Resource Graph", query, "Query", isSuccessfulDependency, dependencyMeasurement, contextualInformation);
                }
            }
        }
示例#11
0
        private async Task <TResponse> InteractWithAzureResourceGraphAsync <TResponse>(string queryName, string query, List <string> targetSubscriptions, Func <ResourceGraphClient, Task <TResponse> > interactionFunc)
        {
            Guard.NotNullOrWhitespace(query, nameof(query));

            var retryPolicy = Policy.Handle <ErrorResponseException>(ex => ex.Response?.StatusCode == HttpStatusCode.Unauthorized)
                              .RetryAsync(retryCount: 3, OnRetryAsync);

            return(await retryPolicy.ExecuteAsync(async() =>
            {
                var graphClient = await GetOrCreateClient();

                bool isSuccessfulDependency = false;
                using (var dependencyMeasurement = DependencyMeasurement.Start())
                {
                    try
                    {
                        var response = await interactionFunc(graphClient);
                        isSuccessfulDependency = true;

                        return response;
                    }
                    catch (ErrorResponseException responseException)
                    {
                        if (responseException.Response != null)
                        {
                            if (responseException.Response.StatusCode == HttpStatusCode.Forbidden)
                            {
                                var unauthorizedException = CreateUnauthorizedException(targetSubscriptions);

                                throw unauthorizedException;
                            }

                            if (responseException.Response.StatusCode == HttpStatusCode.BadRequest)
                            {
                                var response = JToken.Parse(responseException.Response.Content);
                                var errorDetails = response["error"]?["details"];
                                if (errorDetails != null)
                                {
                                    var errorCodes = new List <string>();
                                    foreach (var detailEntry in errorDetails)
                                    {
                                        errorCodes.Add(detailEntry["code"]?.ToString());
                                    }

                                    if (errorCodes.Any(errorCode => errorCode.Equals("NoValidSubscriptionsInQueryRequest", StringComparison.InvariantCultureIgnoreCase)))
                                    {
                                        var invalidSubscriptionException = new QueryContainsInvalidSubscriptionException(targetSubscriptions);
                                        _logger.LogCritical(invalidSubscriptionException, "Unable to query Azure Resource Graph");
                                        throw invalidSubscriptionException;
                                    }
                                }
                            }
                        }

                        throw;
                    }
                    finally
                    {
                        var contextualInformation = new Dictionary <string, object>
                        {
                            { "Query", query },
                            { "QueryName", queryName },
                            { "Subscriptions", targetSubscriptions }
                        };

                        _logger.LogDependency("Azure Resource Graph", query, "Query", isSuccessfulDependency, dependencyMeasurement, contextualInformation);
                    }
                }
            }));
        }