public virtual HostSecretsInfo GetHostSecrets() { if (_hostSecrets == null) { HostSecrets hostSecrets; if (!TryLoadSecrets(_hostSecretsPath, out hostSecrets)) { hostSecrets = GenerateHostSecrets(); PersistSecrets(hostSecrets, _hostSecretsPath); } // Host secrets will be in the original persisted state at this point (e.g. encrypted), // so we read the secrets running them through the appropriate readers hostSecrets = ReadHostSecrets(hostSecrets); // If the persistence state of any of our secrets is stale (e.g. the encryption key has been rotated), update // the state and persist the secrets if (hostSecrets.HasStaleKeys) { RefreshSecrets(hostSecrets, _hostSecretsPath); } _hostSecrets = new HostSecretsInfo { MasterKey = hostSecrets.MasterKey.Value, FunctionKeys = hostSecrets.FunctionKeys.ToDictionary(s => s.Name, s => s.Value) }; } return(_hostSecrets); }
public async virtual Task <HostSecretsInfo> GetHostSecretsAsync() { if (_hostSecrets == null) { HostSecrets hostSecrets = await LoadSecretsAsync <HostSecrets>(); if (hostSecrets == null) { _traceWriter.Verbose(Resources.TraceHostSecretGeneration); hostSecrets = GenerateHostSecrets(); await PersistSecretsAsync(hostSecrets); } // Host secrets will be in the original persisted state at this point (e.g. encrypted), // so we read the secrets running them through the appropriate readers hostSecrets = ReadHostSecrets(hostSecrets); // If the persistence state of any of our secrets is stale (e.g. the encryption key has been rotated), update // the state and persist the secrets if (hostSecrets.HasStaleKeys) { _traceWriter.Verbose(Resources.TraceStaleHostSecretRefresh); await RefreshSecretsAsync(hostSecrets); } _hostSecrets = new HostSecretsInfo { MasterKey = hostSecrets.MasterKey.Value, FunctionKeys = hostSecrets.FunctionKeys.ToDictionary(s => s.Name, s => s.Value) }; } return(_hostSecrets); }
internal static async Task <(string, AuthorizationLevel)> GetAuthorizationLevelAsync(ISecretManager secretManager, string keyValue, string functionName = null) { // see if the key specified is the master key HostSecretsInfo hostSecrets = await secretManager.GetHostSecretsAsync(); if (!string.IsNullOrEmpty(hostSecrets.MasterKey) && Key.SecretValueEquals(keyValue, hostSecrets.MasterKey)) { return(ScriptConstants.DefaultMasterKeyName, AuthorizationLevel.Admin); } if (HasMatchingKey(hostSecrets.SystemKeys, keyValue, out string keyName)) { return(keyName, AuthorizationLevel.System); } // see if the key specified matches the host function key if (HasMatchingKey(hostSecrets.FunctionKeys, keyValue, out keyName)) { return(keyName, AuthorizationLevel.Function); } // If there is a function specific key specified try to match against that if (functionName != null) { IDictionary <string, string> functionSecrets = await secretManager.GetFunctionSecretsAsync(functionName); if (HasMatchingKey(functionSecrets, keyValue, out keyName)) { return(keyName, AuthorizationLevel.Function); } } return(null, AuthorizationLevel.Anonymous); }
public async Task <(string, AuthorizationLevel)> GetAuthorizationLevelOrNullAsync(string keyValue, string functionName = null) { if (keyValue != null) { // Before authorizing, check the cache load state. Do this first because checking the auth level will // cause the secrets to be loaded into cache - we want to know if they were cached BEFORE this check. bool secretsCached = _hostSecrets != null || _functionSecrets.Any(); var result = await GetAuthorizationLevelAsync(this, keyValue, functionName); if (result.Item2 != AuthorizationLevel.Anonymous) { // key match return(result); } else { // A key was presented but there wasn't a match. If we used cached key values, // reset cache and try once more. // We throttle resets, to ensure invalid requests can't force us to slam storage. if (secretsCached && ((DateTime.UtcNow - _lastCacheResetTime) > TimeSpan.FromMinutes(1))) { _hostSecrets = null; _functionSecrets.Clear(); _lastCacheResetTime = DateTime.UtcNow; return(await GetAuthorizationLevelAsync(this, keyValue, functionName)); } } } // no key match return(null, AuthorizationLevel.Anonymous); }
public async virtual Task <IDictionary <string, string> > GetFunctionSecretsAsync(string functionName, bool merged = false) { if (string.IsNullOrEmpty(functionName)) { throw new ArgumentNullException(nameof(functionName)); } functionName = functionName.ToLowerInvariant(); Dictionary <string, string> functionSecrets; _secretsMap.TryGetValue(functionName, out functionSecrets); if (functionSecrets == null) { FunctionSecrets secrets = await LoadFunctionSecretsAsync(functionName); if (secrets == null) { string message = string.Format(Resources.TraceFunctionSecretGeneration, functionName); _logger?.LogDebug(message); secrets = new FunctionSecrets { Keys = new List <Key> { GenerateKey(ScriptConstants.DefaultFunctionKeyName) } }; await PersistSecretsAsync(secrets, functionName); } // Read all secrets, which will run the keys through the appropriate readers secrets.Keys = secrets.Keys.Select(k => _keyValueConverterFactory.ReadKey(k)).ToList(); if (secrets.HasStaleKeys) { _logger?.LogDebug(string.Format(Resources.TraceStaleFunctionSecretRefresh, functionName)); await RefreshSecretsAsync(secrets, functionName); } Dictionary <string, string> result = secrets.Keys.ToDictionary(s => s.Name, s => s.Value); functionSecrets = _secretsMap.AddOrUpdate(functionName, result, (n, r) => result); } if (merged) { // If merged is true, we combine function specific keys with host level function keys, // prioritizing function specific keys HostSecretsInfo hostSecrets = await GetHostSecretsAsync(); Dictionary <string, string> hostFunctionSecrets = hostSecrets.FunctionKeys; functionSecrets = functionSecrets.Union(hostFunctionSecrets.Where(s => !functionSecrets.ContainsKey(s.Key))) .ToDictionary(kv => kv.Key, kv => kv.Value); } return(functionSecrets); }
public async virtual Task <HostSecretsInfo> GetHostSecretsAsync() { if (_hostSecrets == null) { HostSecrets hostSecrets; // Allow only one thread to modify the secrets await _hostSecretsLock.WaitAsync(); try { hostSecrets = await LoadSecretsAsync <HostSecrets>(); if (hostSecrets == null) { // host secrets do not yet exist so generate them _logger.LogDebug(Resources.TraceHostSecretGeneration); hostSecrets = GenerateHostSecrets(); await PersistSecretsAsync(hostSecrets); } try { // Host secrets will be in the original persisted state at this point (e.g. encrypted), // so we read the secrets running them through the appropriate readers hostSecrets = ReadHostSecrets(hostSecrets); } catch (CryptographicException) { _logger?.LogDebug(Resources.TraceNonDecryptedHostSecretRefresh); await PersistSecretsAsync(hostSecrets, null, true); hostSecrets = GenerateHostSecrets(hostSecrets); await RefreshSecretsAsync(hostSecrets); } // If the persistence state of any of our secrets is stale (e.g. the encryption key has been rotated), update // the state and persist the secrets if (hostSecrets.HasStaleKeys) { _logger.LogDebug(Resources.TraceStaleHostSecretRefresh); await RefreshSecretsAsync(hostSecrets); } _hostSecrets = new HostSecretsInfo { MasterKey = hostSecrets.MasterKey.Value, FunctionKeys = hostSecrets.FunctionKeys.ToDictionary(s => s.Name, s => s.Value), SystemKeys = hostSecrets.SystemKeys.ToDictionary(s => s.Name, s => s.Value) }; } finally { _hostSecretsLock.Release(); } } return(_hostSecrets); }
private void InitializeCache() { var cachedFunctionSecrets = _startupContextProvider.GetFunctionSecretsOrNull(); _functionSecrets = cachedFunctionSecrets != null ? new ConcurrentDictionary <string, IDictionary <string, string> >(cachedFunctionSecrets, StringComparer.OrdinalIgnoreCase) : new ConcurrentDictionary <string, IDictionary <string, string> >(StringComparer.OrdinalIgnoreCase); _hostSecrets = _startupContextProvider.GetHostSecretsOrNull(); }
private void OnSecretsChanged(object sender, SecretsChangedEventArgs e) { // clear the cached secrets if they exist // they'll be reloaded on demand next time if (e.SecretsType == ScriptSecretsType.Host) { _hostSecrets = null; } else { Dictionary <string, string> secrets; _secretsMap.TryRemove(e.Name, out secrets); } }
private void OnChanged(object sender, FileSystemEventArgs e) { // clear the cached secrets if they exist // they'll be reloaded on demand next time if (string.Compare(Path.GetFileName(e.FullPath), ScriptConstants.HostMetadataFileName, StringComparison.OrdinalIgnoreCase) == 0) { _hostSecrets = null; } else { Dictionary <string, string> secrets; string name = Path.GetFileNameWithoutExtension(e.FullPath).ToLowerInvariant(); _secretsMap.TryRemove(name, out secrets); } }
public virtual HostSecretsInfo GetHostSecretsOrNull() { if (Context?.Secrets?.Host != null) { var hostSecrets = Context.Secrets.Host; var secretsInfo = new HostSecretsInfo { MasterKey = hostSecrets.Master, FunctionKeys = new Dictionary <string, string>(hostSecrets.Function, StringComparer.OrdinalIgnoreCase), SystemKeys = new Dictionary <string, string>(hostSecrets.System, StringComparer.OrdinalIgnoreCase) }; _logger.LogDebug("Loaded host keys from startup context"); return(secretsInfo); } return(null); }
private void OnSecretsChanged(object sender, SecretsChangedEventArgs e) { // clear the cached secrets if they exist // they'll be reloaded on demand next time if (e.SecretsType == ScriptSecretsType.Host) { _hostSecrets = null; } else { if (string.IsNullOrEmpty(e.Name)) { _secretsMap.Clear(); } else { _secretsMap.TryRemove(e.Name, out _); } } }
private void ClearCacheOnChange(ScriptSecretsType secretsType, string functionName) { // clear the cached secrets if they exist // they'll be reloaded on demand next time if (secretsType == ScriptSecretsType.Host && _hostSecrets != null) { _logger.LogInformation("Host keys change detected. Clearing cache."); _hostSecrets = null; } else { if (!string.IsNullOrEmpty(functionName) && _functionSecrets.ContainsKey(functionName)) { _logger.LogInformation($"Function keys change detected. Clearing cache for function '{functionName}'."); _functionSecrets.TryRemove(functionName, out _); } else if (_functionSecrets.Any()) { _logger.LogInformation("Function keys change detected. Clearing all cached function keys."); _functionSecrets.Clear(); } } }
public async virtual Task <IDictionary <string, string> > GetFunctionSecretsAsync(string functionName, bool merged = false) { using (_metricsLogger.LatencyEvent(GetMetricEventName(MetricEventNames.SecretManagerGetFunctionSecrets), functionName)) { if (string.IsNullOrEmpty(functionName)) { throw new ArgumentNullException(nameof(functionName)); } functionName = functionName.ToLowerInvariant(); Dictionary <string, string> functionSecrets; _secretsMap.TryGetValue(functionName, out functionSecrets); if (functionSecrets == null) { FunctionSecrets secrets = await LoadFunctionSecretsAsync(functionName); if (secrets == null) { // no secrets exist for this function so generate them string message = string.Format(Resources.TraceFunctionSecretGeneration, functionName); _logger.LogDebug(message); secrets = GenerateFunctionSecrets(); await PersistSecretsAsync(secrets, functionName); } try { // Read all secrets, which will run the keys through the appropriate readers secrets.Keys = secrets.Keys.Select(k => _keyValueConverterFactory.ReadKey(k)).ToList(); } catch (CryptographicException ex) { string message = string.Format(Resources.TraceNonDecryptedFunctionSecretRefresh, functionName, ex); _logger?.LogDebug(message); await PersistSecretsAsync(secrets, functionName, true); secrets = GenerateFunctionSecrets(secrets); await RefreshSecretsAsync(secrets, functionName); } if (secrets.HasStaleKeys) { _logger.LogDebug(string.Format(Resources.TraceStaleFunctionSecretRefresh, functionName)); await RefreshSecretsAsync(secrets, functionName); } Dictionary <string, string> result = secrets.Keys.ToDictionary(s => s.Name, s => s.Value); functionSecrets = _secretsMap.AddOrUpdate(functionName, result, (n, r) => result); } if (merged) { // If merged is true, we combine function specific keys with host level function keys, // prioritizing function specific keys HostSecretsInfo hostSecrets = await GetHostSecretsAsync(); Dictionary <string, string> hostFunctionSecrets = hostSecrets.FunctionKeys; functionSecrets = functionSecrets.Union(hostFunctionSecrets.Where(s => !functionSecrets.ContainsKey(s.Key))) .ToDictionary(kv => kv.Key, kv => kv.Value); } return(functionSecrets); } }