/// <summary>
        /// JSON configuration storage. "config.json"
        /// </summary>
        /// <returns></returns>
        public IConfigurationStorage GetConfigurationStorage(string name, IConfigurationProtectionFactory protection)
        {
            var loader = string.IsNullOrEmpty(name)
                ? new JsonConfigurationFileLoader()
                : new JsonConfigurationFileLoader(name);
            var cache = new JsonConfigurationCache(loader)
            {
                ReadTimeout             = 2000,
                WriteTimeout            = 2000,
                ConfigurationProtection = protection,
            };

            return(new JsonConfigurationStorage(cache));
        }
        public IConfigurationStorage GetConfigurationStorage(string configurationName, IConfigurationProtectionFactory protection)
        {
            if (string.IsNullOrEmpty(configurationName))
            {
                configurationName = "default";
            }
            var loader = new SqliteConfigurationLoader(GetConnection, configurationName);
            var cache  = new JsonConfigurationCache(loader)
            {
                WriteTimeout            = 1000,
                ConfigurationProtection = protection
            };

            return(new JsonConfigurationStorage(cache));
        }
        public static async Task <Auth> ConnectToKeeper(ILogger log)
        {
            if (!await Semaphore.WaitAsync(TimeSpan.FromSeconds(10)))
            {
                throw new Exception("Timed out");
            }
            try
            {
                var configPath        = GetKeeperConfigurationFilePath();
                var jsonCache         = new JsonConfigurationCache(new JsonConfigurationFileLoader(configPath));
                var jsonConfiguration = new JsonConfigurationStorage(jsonCache);
                var auth = new Auth(new AuthUiNoAction(), jsonConfiguration)
                {
                    ResumeSession = true
                };
                await auth.Login(jsonConfiguration.LastLogin);

                jsonCache.Flush();

                var keysRq = new EnterpriseDataCommand
                {
                    include = new[] { "keys" }
                };
                var rs = await auth.ExecuteAuthCommand <EnterpriseDataCommand, EnterpriseDataResponse>(keysRq);

                if (string.IsNullOrEmpty(rs.Keys?.EccEncryptedPrivateKey))
                {
                    throw new Exception("Enterprise does not have EC key pair");
                }

                var encTreeKey = rs.TreeKey.Base64UrlDecode();
                var treeKey    = rs.KeyTypeId switch
                {
                    1 => CryptoUtils.DecryptAesV1(encTreeKey, auth.AuthContext.DataKey),
                    2 => CryptoUtils.DecryptRsa(encTreeKey, auth.AuthContext.PrivateKey),
                    _ => throw new Exception("cannot decrypt tree key")
                };

                var privateKeyData = CryptoUtils.DecryptAesV2(rs.Keys.EccEncryptedPrivateKey.Base64UrlDecode(), treeKey);
                _enterprisePrivateKey = CryptoUtils.LoadPrivateEcKey(privateKeyData);
                return(auth);
            }
            finally
            {
                Semaphore.Release();
            }
        }