public static MemoryMappedBasedRetrievedSecrets ReadExposedSecrets(OperationContext context, string?fileName = null, int pollingIntervalInSeconds = 10) { string memoryMappedFileName = fileName ?? SecretsFileName; var secrets = context.PerformOperation( Tracer, () => { var content = MemoryMappedFileHelper.ReadContent(memoryMappedFileName); return(RetrievedSecretsSerializer.Deserialize(content)); }, extraStartMessage: $"Obtaining secrets from '{memoryMappedFileName}'", messageFactory: _ => $"Obtained secrets from '{memoryMappedFileName}'").ThrowIfFailure(); TimeSpan pollingInterval = TimeSpan.FromSeconds(pollingIntervalInSeconds); Timer? timer = null; var result = new MemoryMappedBasedRetrievedSecrets(secrets.Secrets, () => timer?.Dispose(), memoryMappedFileName); timer = new Timer( _ => { result.RefreshSecrets(context); try { timer?.Change(pollingInterval, Timeout.InfiniteTimeSpan); } catch (ObjectDisposedException) { } }, state: null, dueTime: pollingInterval, period: Timeout.InfiniteTimeSpan); return(result); }
public void RefreshSecrets(OperationContext context) { var newSecrets = context.PerformOperation( Tracer, () => { var content = MemoryMappedFileHelper.ReadContent(MemoryMappedFileName); return(RetrievedSecretsSerializer.Deserialize(content)); }, extraStartMessage: $"Refreshing secrets from '{MemoryMappedFileName}'", messageFactory: _ => $"Refreshing secrets from '{MemoryMappedFileName}'"); // Now we need to update the secrets originally read from file. if (newSecrets.Succeeded) { context.PerformOperation( Tracer, () => { UpdateSecrets(this, newSecrets.Value); return(BoolResult.Success); }).IgnoreFailure(); // the error was already traced. } }
public static IDisposable Expose(OperationContext context, RetrievedSecrets secrets, string?fileName = null) { string memoryMappedFileName = fileName ?? SecretsFileName; var memoryMappedFile = context.PerformOperation( Tracer, () => { var serializedSecrets = RetrievedSecretsSerializer.Serialize(secrets); return(Result.Success(MemoryMappedFileHelper.CreateMemoryMappedFileWithContent(memoryMappedFileName, serializedSecrets))); }, extraStartMessage: $"Exposing secrets to '{memoryMappedFileName}'", messageFactory: _ => $"Exposed secrets to '{memoryMappedFileName}'" ).ThrowIfFailure(); var disposeActions = new List <Action>(); disposeActions.Add( () => { Tracer.Debug(context, $"Closing memory mapped file '{memoryMappedFileName}'"); memoryMappedFile.Dispose(); }); disposeActions.AddRange(trackSecretsLifetime()); return(new InterProcessSecretsCommunicator(disposeActions)); List <Action> trackSecretsLifetime() { var actions = new List <Action>(); foreach (var(_, secret) in secrets.Secrets) { if (secret is UpdatingSasToken updating) { updating.TokenUpdated += tokenUpdated; actions.Add(() => updating.TokenUpdated -= tokenUpdated); } } if (actions.Count != 0) { // It means that we have at least one 'updatable' token. actions.Insert( 0, () => { Tracer.Debug(context, "Unsubscribing from secret updates."); }); } return(actions); } void tokenUpdated(object?sender, SasToken e) { var newSerializedSecrets = RetrievedSecretsSerializer.Serialize(secrets); context.PerformOperation( Tracer, () => { MemoryMappedFileHelper.UpdateContent(memoryMappedFileName, newSerializedSecrets); return(BoolResult.Success); }, extraStartMessage: $"Updating secrets in '{memoryMappedFileName}'", messageFactory: _ => $"Updated secrets in '{memoryMappedFileName}'", caller: "tokenUpdated") .IgnoreFailure(); // We traced the results } }