public AzureAppConfigurationProvider(ConfigurationClient client, AzureAppConfigurationOptions options, bool optional)
        {
            _client   = client ?? throw new ArgumentNullException(nameof(client));
            _options  = options ?? throw new ArgumentNullException(nameof(options));
            _optional = optional;

            IEnumerable <KeyValueWatcher> watchers = options.ChangeWatchers.Union(options.MultiKeyWatchers);

            if (watchers.Any())
            {
                MinCacheExpirationTime = watchers.Min(w => w.CacheExpirationTime);
            }
            else
            {
                MinCacheExpirationTime = AzureAppConfigurationRefreshOptions.DefaultCacheExpirationTime;
            }

            // Enable request tracing if not opt-out
            string requestTracingDisabled = null;

            try
            {
                requestTracingDisabled = Environment.GetEnvironmentVariable(RequestTracingConstants.RequestTracingDisabledEnvironmentVariable);
            }
            catch (SecurityException) { }
            _requestTracingEnabled = bool.TryParse(requestTracingDisabled, out bool tracingDisabled) ? !tracingDisabled : true;

            if (_requestTracingEnabled)
            {
                _hostType = TracingUtils.GetHostType();
            }
        }
예제 #2
0
        /// <summary>
        /// An implementation of <see cref="IOfflineCache.Import(AzureAppConfigurationOptions)"/> that retrieves the cached data from the file system.
        /// </summary>
        public string Import(AzureAppConfigurationOptions options)
        {
            EnsureOptions(options);

            int retry = 0;

            while (retry++ <= retryMax)
            {
                try
                {
                    string data = null, dataHash = null, scopeHash = null;
                    byte[] bytes  = File.ReadAllBytes(_localCachePath);
                    var    reader = new Utf8JsonReader(bytes);

                    while (reader.Read())
                    {
                        if (reader.TokenType == JsonTokenType.PropertyName)
                        {
                            switch (reader.GetString())
                            {
                            case dataProp:
                                data = reader.ReadAsString();
                                break;

                            case hashProp:
                                dataHash = reader.ReadAsString();
                                break;

                            case scopeProp:
                                scopeHash = reader.ReadAsString();
                                break;

                            default:
                                return(null);
                            }
                        }
                    }

                    if ((data != null) && (dataHash != null) && (scopeHash != null))
                    {
                        string newScopeHash = CryptoService.GetHash(Encoding.UTF8.GetBytes(_scopeToken), _options.SignKey);
                        if (string.CompareOrdinal(scopeHash, newScopeHash) == 0)
                        {
                            string newDataHash = CryptoService.GetHash(Convert.FromBase64String(data), _options.SignKey);
                            if (string.CompareOrdinal(dataHash, newDataHash) == 0)
                            {
                                return(CryptoService.AESDecrypt(data, _options.Key, _options.IV));
                            }
                        }
                    }
                }
                catch (IOException ex) when(ex.HResult == ERROR_SHARING_VIOLATION)
                {
                    Task.Delay(new Random().Next(delayRange)).ConfigureAwait(false).GetAwaiter().GetResult();
                }
            }

            return(null);
        }
        public AzureAppConfigurationSource(Action <AzureAppConfigurationOptions> optionsInitializer, bool optional = false, IConfigurationClientFactory configurationClientFactory = null)
        {
            _optionsProvider = () => {
                var options = new AzureAppConfigurationOptions();
                optionsInitializer(options);
                return(options);
            };

            _optional = optional;
            _configurationClientFactory = configurationClientFactory ?? new ConfigurationClientFactory();
        }
예제 #4
0
        private void EnsureOptions(AzureAppConfigurationOptions azconfigOptions)
        {
            if (azconfigOptions == null)
            {
                throw new ArgumentNullException(nameof(azconfigOptions));
            }

            if (azconfigOptions.ConnectionString == null)
            {
                throw new InvalidOperationException("An Azure App Configuration connection string is required.");
            }

            OfflineFileCacheOptions options = _options ?? new OfflineFileCacheOptions();

            if ((options.Key == null) || (options.SignKey == null) || (options.IV == null))
            {
                byte[] secret = Convert.FromBase64String(ConnectionStringParser.Parse(azconfigOptions.ConnectionString, "Secret"));
                using (SHA256 sha256 = SHA256.Create())
                {
                    byte[] hash = sha256.ComputeHash(secret);

                    options.Key     = options.Key ?? hash;
                    options.SignKey = options.SignKey ?? hash;
                    options.IV      = options.IV ?? hash.Take(16).ToArray();
                }
            }

            if (string.IsNullOrEmpty(_scopeToken))
            {
                //
                // The default scope token is the configuration store endpoint combined with all of the key-value filters

                string endpoint = ConnectionStringParser.Parse(azconfigOptions.ConnectionString, "Endpoint");

                if (string.IsNullOrWhiteSpace(endpoint))
                {
                    throw new InvalidOperationException("Invalid connection string format.");
                }

                var sb = new StringBuilder($"{endpoint}\0");

                foreach (var selector in azconfigOptions.KeyValueSelectors)
                {
                    sb.Append($"{selector.KeyFilter}\0{selector.LabelFilter}\0");
                }

                _scopeToken = sb.ToString();
            }

            _options = options;
        }
        public AzureAppConfigurationProvider(ConfigurationClient client, AzureAppConfigurationOptions options, bool optional)
        {
            _client   = client ?? throw new ArgumentNullException(nameof(client));
            _options  = options ?? throw new ArgumentNullException(nameof(options));
            _optional = optional;

            // Enable request tracing if not opt-out
            string requestTracingDisabled = null;

            try
            {
                requestTracingDisabled = Environment.GetEnvironmentVariable(RequestTracingConstants.RequestTracingDisabledEnvironmentVariable);
            }
            catch (SecurityException) { }
            _requestTracingEnabled = bool.TryParse(requestTracingDisabled, out bool tracingDisabled) ? !tracingDisabled : true;

            if (_requestTracingEnabled)
            {
                _hostType = TracingUtils.GetHostType();
            }
        }
예제 #6
0
        /// <summary>
        /// An implementation of <see cref="IOfflineCache.Export(AzureAppConfigurationOptions, string)"/> that caches the data in the file system.
        /// </summary>
        public void Export(AzureAppConfigurationOptions options, string data)
        {
            EnsureOptions(options);

            if ((DateTime.Now - File.GetLastWriteTime(_localCachePath)) > TimeSpan.FromMilliseconds(1000))
            {
                Task.Run(async() =>
                {
                    string tempFile = Path.Combine(Path.GetDirectoryName(_localCachePath), $"azconfigTemp-{Path.GetRandomFileName()}");

                    var dataBytes      = Encoding.UTF8.GetBytes(data);
                    var encryptedBytes = CryptoService.AESEncrypt(dataBytes, _options.Key, _options.IV);
                    var dataHash       = CryptoService.GetHash(encryptedBytes, _options.SignKey);
                    var scopeHash      = CryptoService.GetHash(Encoding.UTF8.GetBytes(_scopeToken), _options.SignKey);

                    using (var fileStream = new FileStream(tempFile, FileMode.Create))
                    {
                        using (var memoryStream = new MemoryStream())
                        {
                            using (var writer = new Utf8JsonWriter(memoryStream))
                            {
                                writer.WriteStartObject();
                                writer.WriteString(dataProp, Convert.ToBase64String(encryptedBytes));
                                writer.WriteString(hashProp, dataHash);
                                writer.WriteString(scopeProp, scopeHash);
                                writer.WriteEndObject();
                            }

                            memoryStream.Position = 0;
                            memoryStream.CopyTo(fileStream);
                            fileStream.Flush();
                        }
                    }

                    await this.DoUpdate(tempFile).ConfigureAwait(false);
                });
            }
        }
        public IConfigurationProvider Build(IConfigurationBuilder builder)
        {
            IConfigurationProvider provider = null;

            try
            {
                AzureAppConfigurationOptions options = _optionsProvider();
                ConfigurationClient          client;

                if (options.Client != null)
                {
                    client = options.Client;
                }
                else if (!string.IsNullOrWhiteSpace(options.ConnectionString))
                {
                    client = _configurationClientFactory.CreateConfigurationClient(options.ConnectionString, options.ClientOptions);
                }
                else if (options.Endpoint != null && options.Credential != null)
                {
                    client = _configurationClientFactory.CreateConfigurationClient(options.Endpoint, options.Credential, options.ClientOptions);
                }
                else
                {
                    throw new ArgumentException($"Please call {nameof(AzureAppConfigurationOptions)}.{nameof(AzureAppConfigurationOptions.Connect)} to specify how to connect to Azure App Configuration.");
                }

                provider = new AzureAppConfigurationProvider(client, options, _optional);
            }
            catch (InvalidOperationException e)
            {
                if (!_optional)
                {
                    throw new ArgumentException(e.Message, e);
                }
            }

            return(provider ?? new EmptyConfigurationProvider());
        }