/// <inheritdoc />
        public Task <RetrievedSecrets> RetrieveSecretsAsync(List <RetrieveSecretsRequest> requests, CancellationToken token)
        {
            if (_secretsCommunicationKind == CrossProcessSecretsCommunicationKind.Environment)
            {
                // Default mode for the launcher
                return(RetrieveSecretsCoreAsync(requests, token));
            }
            else if (_secretsCommunicationKind == CrossProcessSecretsCommunicationKind.EnvironmentSingleEntry)
            {
                var secretsResult = LazyInitializer.EnsureInitialized(ref _secrets, () => DeserializeFromEnvironmentVariable());

                secretsResult.ThrowIfFailure();
                return(Task.FromResult(secretsResult.Value));
            }
            else if (_secretsCommunicationKind == CrossProcessSecretsCommunicationKind.MemoryMappedFile)
            {
                // 'ReadExposedSecrets' returns a disposable object, but the secrets obtained here are long-lived.
                RetrievedSecrets secrets = InterProcessSecretsCommunicator.ReadExposedSecrets(new OperationContext(_tracingContext));
                return(Task.FromResult(secrets));
            }
            else
            {
                throw Contract.AssertFailure($"Unknown {nameof(CrossProcessSecretsCommunicationKind)}: {_secretsCommunicationKind}.");
            }
        }
示例#2
0
    public void TestSerialization()
    {
        var secretsMap = new Dictionary <string, Secret>
        {
            ["cbcache-test-redis-dm_s1"] =
                new PlainTextSecret("Fake secret that is quite long to emulate the size of the serialized entry."),
            ["cbcache-test-redis-secondary-dm_s1"] =
                new PlainTextSecret("Fake secret that is quite long to emulate the size of the serialized entry."),
            ["cbcache-test-event-hub-dm_s1"] =
                new PlainTextSecret("Fake secret that is quite long to emulate the size of the serialized entry."),
            ["cbcacheteststorage-dm_s1-sas"] =
                new UpdatingSasToken(new SasToken("token_name", "storage_account", "resource_path")),
            ["ContentMetadataBlobSecretName-dm_s1"] = new PlainTextSecret(
                "Fake secret that is quite long to emulate the size of the serialized entry.")
        };
        var secrets = new RetrievedSecrets(secretsMap);

        var text = RetrievedSecretsSerializer.Serialize(secrets);

        var deserializedSecretsMap = RetrievedSecretsSerializer.Deserialize(text).ShouldBeSuccess().Value.Secrets;

        Assert.Equal(secretsMap.Count, deserializedSecretsMap.Count);

        foreach (var kvp in secretsMap)
        {
            Assert.Equal(kvp.Value, deserializedSecretsMap[kvp.Key]);
            Assert.Equal(kvp.Value, deserializedSecretsMap[kvp.Key]);
        }
    }
        public static string Serialize(RetrievedSecrets secrets)
        {
            var secretsList = secrets.Secrets
                              .Select(kvp => SecretData.FromSecret(kvp.Key, kvp.Value))
                              .OrderBy(s => s.Name)
                              .ToList();

            return(JsonSerializer.Serialize(secretsList));
        }
 private static void UpdateSecrets(RetrievedSecrets original, RetrievedSecrets @new)
 {
     foreach (var(name, secret) in @new.Secrets)
     {
         if (secret is UpdatingSasToken updatingToken)
         {
             // The secret types can't change.
             var originalSecret = (UpdatingSasToken)original.Secrets[name];
             originalSecret.UpdateToken(updatingToken.Token);
         }
     }
 }
示例#5
0
        private static void AssertSecretsAreEqual(RetrievedSecrets left, RetrievedSecrets right)
        {
            Assert.Equal(left.Secrets.Count, right.Secrets.Count);

            foreach (var(name, secret) in left.Secrets)
            {
                if (right.Secrets.TryGetValue(name, out var rightSecret))
                {
                    Assert.Equal(secret, rightSecret);
                }
                else
                {
                    Assert.True(false, $"Can't find a secret with name '{name}' in the 'right' variable.");
                }
            }
        }
示例#6
0
        public void TestUpdatableTokens()
        {
            var updatingToken   = new UpdatingSasToken(new SasToken(token: "Token 1", "Storage Account 1", "Resource Path 1"));
            var originalSecrets = new RetrievedSecrets(
                new Dictionary <string, Secret>()
            {
                ["Secret 1"] = new PlainTextSecret("Secret Value 1"),
                ["Secret 2"] = updatingToken
            });

            var context = new OperationContext(new Context(Logger));

            using var secretsExposer = InterProcessSecretsCommunicator.Expose(context, originalSecrets);

            using var readSecrets = InterProcessSecretsCommunicator.ReadExposedSecrets(context, pollingIntervalInSeconds: 10_000);

            AssertSecretsAreEqual(originalSecrets, readSecrets);

            int tokenUpdated = 0;

            ((UpdatingSasToken)readSecrets.Secrets["Secret 2"]).TokenUpdated += (sender, token) =>
            {
                tokenUpdated++;
            };

            // Updating the token
            updatingToken.UpdateToken(new SasToken("1", "2", "3"));

            readSecrets.RefreshSecrets(context);
            AssertSecretsAreEqual(originalSecrets, readSecrets);

            Assert.Equal(1, tokenUpdated); // An event should be raised

            // Updating token once again
            updatingToken.UpdateToken(new SasToken("2", "2", "3"));

            readSecrets.RefreshSecrets(context);

            AssertSecretsAreEqual(originalSecrets, readSecrets);

            Assert.Equal(2, tokenUpdated); // An event should be raised
        }
示例#7
0
 public CacheServiceWrapper(CacheServiceWrapperConfiguration configuration, ServiceLifetimeManager serviceLifetimeManager, RetrievedSecrets secrets)
 {
     _configuration          = configuration;
     _serviceLifetimeManager = serviceLifetimeManager;
     _secrets = secrets;
 }
        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
            }
        }