private async Task <T> GetKeyObjectFromFile <T>(string name, IKeyJsonOps <T> keyOp) { var secretStorageType = System.Environment.GetEnvironmentVariable(Constants.AzureWebJobsSecretStorageType); if (!string.IsNullOrEmpty(secretStorageType) && secretStorageType.Equals("Blob", StringComparison.OrdinalIgnoreCase)) { throw new InvalidOperationException("Runtime keys are stored on blob storage. This API doesn't support this configuration."); } string keyPath = GetFunctionSecretsFilePath(name); string key = null; if (!FileSystemHelpers.FileExists(keyPath) || FileSystemHelpers.FileInfoFromFileName(keyPath).Length == 0) { FileSystemHelpers.EnsureDirectory(Path.GetDirectoryName(keyPath)); try { using (var fileStream = FileSystemHelpers.OpenFile(keyPath, FileMode.Create, FileAccess.Write, FileShare.None)) // getting the lock early (instead of acquire the lock at "new StreamWriter(fileStream)") // so no redundant work is being done (generate secrets) { string jsonContent = keyOp.GenerateKeyJson(SecurityUtility.GenerateSecretStringsKeyPair(keyOp.NumberOfKeysInDefaultFormat), FunctionSiteExtensionVersion, out key); using (var sw = new StringWriter()) using (var sr = new System.IO.StringReader(jsonContent)) { // write json to memory // since JsonConvert has no method to format a json string new JsonTextWriter(sw) { Formatting = Formatting.Indented }.WriteToken(new JsonTextReader(sr)); using (var streamWriter = new StreamWriter(fileStream)) { await streamWriter.WriteAsync(sw.ToString()); await streamWriter.FlushAsync(); } } } return(keyOp.GenerateKeyObject(key, name)); } catch (IOException) { // failed to open file => function runtime has the handler // fallback to read key files } } string jsonStr = null; int timeOut = 5; while (true) { try { jsonStr = await FileSystemHelpers.ReadAllTextFromFileAsync(keyPath); break; } catch (Exception) { if (timeOut == 0) { throw new TimeoutException($"Fail to read {keyPath}, the file is being held by another process"); } timeOut--; await Task.Delay(250); } } bool isEncrypted; key = keyOp.GetKeyValueFromJson(jsonStr, out isEncrypted); if (isEncrypted) { key = SecurityUtility.DecryptSecretString(key); } return(keyOp.GenerateKeyObject(key, name)); }
private async Task <T> GetKeyObjectFromFile <T>(string name, IKeyJsonOps <T> keyOp) { string keyPath = GetFunctionSecretsFilePath(name); string key = null; if (!FileSystemHelpers.FileExists(keyPath)) { FileSystemHelpers.EnsureDirectory(Path.GetDirectoryName(keyPath)); try { using (var fileStream = FileSystemHelpers.OpenFile(keyPath, FileMode.CreateNew, FileAccess.Write, FileShare.None)) // will fail if file exists, prevent reading prematurely // getting the lock early so no redundant work is being done { string jsonContent = keyOp.GenerateKeyJson(SecurityUtility.GenerateSecretStringsKeyPair(keyOp.NumberOfKeysInDefaultFormat), FunctionSiteExtensionVersion, out key); using (var sw = new StringWriter()) using (var sr = new System.IO.StringReader(jsonContent)) { new JsonTextWriter(sw) { Formatting = Formatting.Indented }.WriteToken(new JsonTextReader(sr)); // if lock acquire lock return false, I wait until write finishes and read keyPath using (var streamWriter = new StreamWriter(fileStream)) { await streamWriter.WriteAsync(sw.ToString()); await streamWriter.FlushAsync(); } } } return(keyOp.GenerateKeyObject(key, name)); } catch (IOException) { // don't throw exception if the file already existed // fallback to read key files } } string jsonStr = null; int timeOut = 5; while (true) { try { jsonStr = await FileSystemHelpers.ReadAllTextFromFileAsync(keyPath); break; } catch (Exception) { if (timeOut == 0) { throw new TimeoutException($"Fail to read {keyPath}, the file is being held by another process"); } timeOut--; await Task.Delay(250); } } bool isEncrypted; key = keyOp.GetKeyValueFromJson(jsonStr, out isEncrypted); if (isEncrypted) { key = SecurityUtility.DecryptSecretString(key); } return(keyOp.GenerateKeyObject(key, name)); }