Esempio n. 1
0
        public void GetFunctionSecrets_WhenNoSecretFileExists_ReturnsEmptySecretsAndDoesNotPersistsFile()
        {
            var secretsPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());

            try
            {
                using (var variables = new TestScopedEnvironmentVariables("AzureWebJobsFeatureFlags", "MultiKey"))
                {
                    Mock <IKeyValueConverterFactory> mockValueConverterFactory = GetConverterFactoryMock(false);

                    IDictionary <string, string> functionSecrets;
                    using (var secretManager = new SecretManager(secretsPath, mockValueConverterFactory.Object))
                    {
                        functionSecrets = secretManager.GetFunctionSecrets("TestFunction");
                    }

                    bool functionSecretsExists = File.Exists(Path.Combine(secretsPath, "testfunction.json"));

                    Assert.NotNull(functionSecrets);
                    Assert.False(functionSecretsExists);
                    Assert.Equal(0, functionSecrets.Count);
                }
            }
            finally
            {
                Directory.Delete(secretsPath, true);
            }
        }
        public void GetFunctionSecrets_WhenNoSecretFileExists_CreatesDefaultSecretAndPersistsFile()
        {
            var secretsPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());

            try
            {
                Mock <IKeyValueConverterFactory> mockValueConverterFactory = GetConverterFactoryMock(false);

                IDictionary <string, string> functionSecrets;
                using (var secretManager = new SecretManager(secretsPath, mockValueConverterFactory.Object))
                {
                    functionSecrets = secretManager.GetFunctionSecrets("TestFunction");
                }

                bool functionSecretsExists = File.Exists(Path.Combine(secretsPath, "testfunction.json"));

                Assert.NotNull(functionSecrets);
                Assert.True(functionSecretsExists);
                Assert.Equal(1, functionSecrets.Count);
                Assert.Equal(ScriptConstants.DefaultFunctionKeyName, functionSecrets.Keys.First());
            }
            finally
            {
                Directory.Delete(secretsPath, true);
            }
        }
        public void GetFunctionSecrets_WhenNoSecretFileExists_CreatesDefaultSecretAndPersistsFile()
        {
            using (var directory = new TempDirectory())
            {
                string functionName         = "TestFunction";
                string expectedTraceMessage = string.Format(Resources.TraceFunctionSecretGeneration, functionName);

                Mock <IKeyValueConverterFactory> mockValueConverterFactory = GetConverterFactoryMock(false, false);

                IDictionary <string, string> functionSecrets;
                var traceWriter = new TestTraceWriter(TraceLevel.Verbose);
                using (var secretManager = new SecretManager(directory.Path, mockValueConverterFactory.Object, traceWriter))
                {
                    functionSecrets = secretManager.GetFunctionSecrets(functionName);
                }

                bool functionSecretsExists = File.Exists(Path.Combine(directory.Path, "testfunction.json"));

                Assert.NotNull(functionSecrets);
                Assert.True(functionSecretsExists);
                Assert.Equal(1, functionSecrets.Count);
                Assert.Equal(ScriptConstants.DefaultFunctionKeyName, functionSecrets.Keys.First());
                Assert.True(traceWriter.Traces.Any(
                                t => t.Level == TraceLevel.Verbose && t.Message.IndexOf(expectedTraceMessage, StringComparison.OrdinalIgnoreCase) > -1),
                            "Expected Trace message not found");
            }
        }
        public void MergedSecrets_PrioritizesFunctionSecrets()
        {
            using (var directory = new TempDirectory())
            {
                string hostSecrets =
                    @"{
    'masterKey': {
        'name': 'master',
        'value': '1234',
        'encrypted': false
    },
    'functionKeys': [
        {
            'name': 'Key1',
            'value': 'HostValue1',
            'encrypted': false
        },
        {
            'name': 'Key3',
            'value': 'HostValue3',
            'encrypted': false
        }
    ]
}";
                string functionSecrets =
                    @"{
    'keys': [
        {
            'name': 'Key1',
            'value': 'FunctionValue1',
            'encrypted': false
        },
        {
            'name': 'Key2',
            'value': 'FunctionValue2',
            'encrypted': false
        }
    ]
}";
                File.WriteAllText(Path.Combine(directory.Path, ScriptConstants.HostMetadataFileName), hostSecrets);
                File.WriteAllText(Path.Combine(directory.Path, "testfunction.json"), functionSecrets);

                IDictionary<string, string> result;
                using (var secretManager = new SecretManager(_settingsManager, directory.Path, NullTraceWriter.Instance))
                {
                    result = secretManager.GetFunctionSecrets("testfunction", true);
                }

                Assert.Contains("Key1", result.Keys);
                Assert.Contains("Key2", result.Keys);
                Assert.Contains("Key3", result.Keys);
                Assert.Equal("FunctionValue1", result["Key1"]);
                Assert.Equal("FunctionValue2", result["Key2"]);
                Assert.Equal("HostValue3", result["Key3"]);
            }
        }
        public void MergedSecrets_PrioritizesFunctionSecrets()
        {
            using (var directory = new TempDirectory())
            {
                string hostSecrets =
                    @"{
    'masterKey': {
        'name': 'master',
        'value': '1234',
        'encrypted': false
    },
    'functionKeys': [
        {
            'name': 'Key1',
            'value': 'HostValue1',
            'encrypted': false
        },
        {
            'name': 'Key3',
            'value': 'HostValue3',
            'encrypted': false
        }
    ]
}";
                string functionSecrets =
                    @"{
    'keys': [
        {
            'name': 'Key1',
            'value': 'FunctionValue1',
            'encrypted': false
        },
        {
            'name': 'Key2',
            'value': 'FunctionValue2',
            'encrypted': false
        }
    ]
}";
                File.WriteAllText(Path.Combine(directory.Path, ScriptConstants.HostMetadataFileName), hostSecrets);
                File.WriteAllText(Path.Combine(directory.Path, "testfunction.json"), functionSecrets);

                IDictionary <string, string> result;
                using (var secretManager = new SecretManager(_settingsManager, directory.Path, NullTraceWriter.Instance))
                {
                    result = secretManager.GetFunctionSecrets("testfunction", true);
                }

                Assert.Contains("Key1", result.Keys);
                Assert.Contains("Key2", result.Keys);
                Assert.Contains("Key3", result.Keys);
                Assert.Equal("FunctionValue1", result["Key1"]);
                Assert.Equal("FunctionValue2", result["Key2"]);
                Assert.Equal("HostValue3", result["Key3"]);
            }
        }
        public Task <string> GetReceiverConfigAsync(string name, string id)
        {
            // "id" will be the function name
            // we ignore the "name" parameter since we only allow a function
            // to be mapped to a single receiver
            FunctionSecrets secrets = _secretManager.GetFunctionSecrets(id);

            if (secrets != null)
            {
                return(Task.FromResult(secrets.Key));
            }

            return(null);
        }
Esempio n. 7
0
        internal static AuthorizationLevel GetAuthorizationLevel(HttpRequestMessage request, SecretManager secretManager, string functionName = null)
        {
            // TODO: Add support for validating "EasyAuth" headers

            // first see if a key value is specified via headers or query string (header takes precidence)
            IEnumerable <string> values;
            string keyValue = null;

            if (request.Headers.TryGetValues(FunctionsKeyHeaderName, out values))
            {
                keyValue = values.FirstOrDefault();
            }
            else
            {
                var queryParameters = request.GetQueryNameValuePairs().ToDictionary(p => p.Key, p => p.Value, StringComparer.OrdinalIgnoreCase);
                queryParameters.TryGetValue("code", out keyValue);
            }

            if (!string.IsNullOrEmpty(keyValue))
            {
                // see if the key specified is the master key
                HostSecrets hostSecrets = secretManager.GetHostSecrets();
                if (!string.IsNullOrEmpty(hostSecrets.MasterKey) &&
                    SecretEqual(keyValue, hostSecrets.MasterKey))
                {
                    return(AuthorizationLevel.Admin);
                }

                // see if the key specified matches the host function key
                if (!string.IsNullOrEmpty(hostSecrets.FunctionKey) &&
                    SecretEqual(keyValue, hostSecrets.FunctionKey))
                {
                    return(AuthorizationLevel.Function);
                }

                // if there is a function specific key specified try to match against that
                if (functionName != null)
                {
                    FunctionSecrets functionSecrets = secretManager.GetFunctionSecrets(functionName);
                    if (functionSecrets != null &&
                        !string.IsNullOrEmpty(functionSecrets.Key) &&
                        SecretEqual(keyValue, functionSecrets.Key))
                    {
                        return(AuthorizationLevel.Function);
                    }
                }
            }

            return(AuthorizationLevel.Anonymous);
        }
Esempio n. 8
0
        public void GetFunctionSecrets_UpdatesStaleSecrets()
        {
            var secretsPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());

            try
            {
                using (var variables = new TestScopedEnvironmentVariables("AzureWebJobsFeatureFlags", "MultiKey"))
                {
                    Directory.CreateDirectory(secretsPath);
                    string functionSecretsJson =
                        @"{
    'keys': [
        {
            'name': 'Key1',
            'value': 'FunctionValue1',
            'encrypted': false
        },
        {
            'name': 'Key2',
            'value': 'FunctionValue2',
            'encrypted': false
        }
    ]
}";
                    File.WriteAllText(Path.Combine(secretsPath, "testfunction.json"), functionSecretsJson);

                    Mock <IKeyValueConverterFactory> mockValueConverterFactory = GetConverterFactoryMock();

                    IDictionary <string, string> functionSecrets;
                    using (var secretManager = new SecretManager(secretsPath, mockValueConverterFactory.Object))
                    {
                        functionSecrets = secretManager.GetFunctionSecrets("testfunction");
                    }
                    // Read the persisted content
                    var  result = JsonConvert.DeserializeObject <FunctionSecrets>(File.ReadAllText(Path.Combine(secretsPath, "testfunction.json")));
                    bool functionSecretsConverted = functionSecrets.Values.Zip(result.Keys, (r1, r2) => string.Equals("!" + r1, r2.Value)).All(r => r);

                    Assert.Equal(2, result.Keys.Count);
                    Assert.True(functionSecretsConverted, "Function secrets were not persisted");
                }
            }
            finally
            {
                Directory.Delete(secretsPath, true);
            }
        }
        public void GetFunctionSecrets_UpdatesStaleSecrets()
        {
            using (var directory = new TempDirectory())
            {
                string functionName         = "testfunction";
                string expectedTraceMessage = string.Format(Resources.TraceStaleFunctionSecretRefresh, functionName);
                string functionSecretsJson  =
                    @"{
    'keys': [
        {
            'name': 'Key1',
            'value': 'FunctionValue1',
            'encrypted': false
        },
        {
            'name': 'Key2',
            'value': 'FunctionValue2',
            'encrypted': false
        }
    ]
}";
                File.WriteAllText(Path.Combine(directory.Path, functionName + ".json"), functionSecretsJson);

                Mock <IKeyValueConverterFactory> mockValueConverterFactory = GetConverterFactoryMock();

                IDictionary <string, string> functionSecrets;
                var traceWriter = new TestTraceWriter(TraceLevel.Verbose);
                using (var secretManager = new SecretManager(directory.Path, mockValueConverterFactory.Object, traceWriter))
                {
                    functionSecrets = secretManager.GetFunctionSecrets(functionName);
                }
                // Read the persisted content
                var  result = JsonConvert.DeserializeObject <FunctionSecrets>(File.ReadAllText(Path.Combine(directory.Path, functionName + ".json")));
                bool functionSecretsConverted = functionSecrets.Values.Zip(result.Keys, (r1, r2) => string.Equals("!" + r1, r2.Value)).All(r => r);

                Assert.Equal(2, result.Keys.Count);
                Assert.True(functionSecretsConverted, "Function secrets were not persisted");
                Assert.True(traceWriter.Traces.Any(t => t.Level == TraceLevel.Verbose && t.Message.IndexOf(expectedTraceMessage) > -1));
            }
        }
Esempio n. 10
0
        public void MergedSecrets_PrioritizesFunctionSecrets()
        {
            var secretsPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());

            try
            {
                Directory.CreateDirectory(secretsPath);
                string hostSecrets =
                    @"{
    'masterKey': {
        'name': 'master',
        'value': '1234',
        'encrypted': false
    },
    'functionKeys': [
        {
            'name': 'Key1',
            'value': 'HostValue1',
            'encrypted': false
        },
        {
            'name': 'Key3',
            'value': 'HostValue3',
            'encrypted': false
        }
    ]
}";
                string functionSecrets =
                    @"{
    'keys': [
        {
            'name': 'Key1',
            'value': 'FunctionValue1',
            'encrypted': false
        },
        {
            'name': 'Key2',
            'value': 'FunctionValue2',
            'encrypted': false
        }
    ]
}";
                File.WriteAllText(Path.Combine(secretsPath, ScriptConstants.HostMetadataFileName), hostSecrets);
                File.WriteAllText(Path.Combine(secretsPath, "testfunction.json"), functionSecrets);

                IDictionary <string, string> result;
                using (var secretManager = new SecretManager(secretsPath))
                {
                    result = secretManager.GetFunctionSecrets("testfunction", true);
                }

                Assert.Contains("Key1", result.Keys);
                Assert.Contains("Key2", result.Keys);
                Assert.Contains("Key3", result.Keys);
                Assert.Equal("FunctionValue1", result["Key1"]);
                Assert.Equal("FunctionValue2", result["Key2"]);
                Assert.Equal("HostValue3", result["Key3"]);
            }
            finally
            {
                Directory.Delete(secretsPath, true);
            }
        }
        public void GetFunctionSecrets_UpdatesStaleSecrets()
        {
            using (var directory = new TempDirectory())
            {
                string functionName = "testfunction";
                string expectedTraceMessage = string.Format(Resources.TraceStaleFunctionSecretRefresh, functionName);
                string functionSecretsJson =
                 @"{
    'keys': [
        {
            'name': 'Key1',
            'value': 'FunctionValue1',
            'encrypted': false
        },
        {
            'name': 'Key2',
            'value': 'FunctionValue2',
            'encrypted': false
        }
    ]
}";
                File.WriteAllText(Path.Combine(directory.Path, functionName + ".json"), functionSecretsJson);

                Mock<IKeyValueConverterFactory> mockValueConverterFactory = GetConverterFactoryMock();

                IDictionary<string, string> functionSecrets;
                var traceWriter = new TestTraceWriter(TraceLevel.Verbose);
                using (var secretManager = new SecretManager(directory.Path, mockValueConverterFactory.Object, traceWriter))
                {
                    functionSecrets = secretManager.GetFunctionSecrets(functionName);
                }
                // Read the persisted content
                var result = JsonConvert.DeserializeObject<FunctionSecrets>(File.ReadAllText(Path.Combine(directory.Path, functionName + ".json")));
                bool functionSecretsConverted = functionSecrets.Values.Zip(result.Keys, (r1, r2) => string.Equals("!" + r1, r2.Value)).All(r => r);

                Assert.Equal(2, result.Keys.Count);
                Assert.True(functionSecretsConverted, "Function secrets were not persisted");
                Assert.True(traceWriter.Traces.Any(t => t.Level == TraceLevel.Verbose && t.Message.IndexOf(expectedTraceMessage) > -1));
            }
        }
        public void GetFunctionSecrets_WhenNoSecretFileExists_CreatesDefaultSecretAndPersistsFile()
        {
            using (var directory = new TempDirectory())
            {
                string functionName = "TestFunction";
                string expectedTraceMessage = string.Format(Resources.TraceFunctionSecretGeneration, functionName);

                Mock<IKeyValueConverterFactory> mockValueConverterFactory = GetConverterFactoryMock(false, false);

                IDictionary<string, string> functionSecrets;
                var traceWriter = new TestTraceWriter(TraceLevel.Verbose);
                using (var secretManager = new SecretManager(directory.Path, mockValueConverterFactory.Object, traceWriter))
                {
                    functionSecrets = secretManager.GetFunctionSecrets(functionName);
                }

                bool functionSecretsExists = File.Exists(Path.Combine(directory.Path, "testfunction.json"));

                Assert.NotNull(functionSecrets);
                Assert.True(functionSecretsExists);
                Assert.Equal(1, functionSecrets.Count);
                Assert.Equal(ScriptConstants.DefaultFunctionKeyName, functionSecrets.Keys.First());
                Assert.True(traceWriter.Traces.Any(
                    t => t.Level == TraceLevel.Verbose && t.Message.IndexOf(expectedTraceMessage, StringComparison.OrdinalIgnoreCase) > -1),
                    "Expected Trace message not found");
            }
        }
        internal static AuthorizationLevel GetAuthorizationLevel(HttpRequestMessage request, SecretManager secretManager, string functionName = null)
        {
            // TODO: Add support for validating "EasyAuth" headers

            // first see if a key value is specified via headers or query string (header takes precidence)
            IEnumerable<string> values;
            string keyValue = null;
            if (request.Headers.TryGetValues(FunctionsKeyHeaderName, out values))
            {
                keyValue = values.FirstOrDefault();
            }
            else
            {
                var queryParameters = request.GetQueryNameValuePairs().ToDictionary(p => p.Key, p => p.Value, StringComparer.OrdinalIgnoreCase);
                queryParameters.TryGetValue("code", out keyValue);
            }

            if (!string.IsNullOrEmpty(keyValue))
            {
                // see if the key specified is the master key
                HostSecrets hostSecrets = secretManager.GetHostSecrets();
                if (!string.IsNullOrEmpty(hostSecrets.MasterKey) &&
                    SecretEqual(keyValue, hostSecrets.MasterKey))
                {
                    return AuthorizationLevel.Admin;
                }

                // see if the key specified matches the host function key
                if (!string.IsNullOrEmpty(hostSecrets.FunctionKey) &&
                    SecretEqual(keyValue, hostSecrets.FunctionKey))
                {
                    return AuthorizationLevel.Function;
                }

                // if there is a function specific key specified try to match against that
                if (functionName != null)
                {
                    FunctionSecrets functionSecrets = secretManager.GetFunctionSecrets(functionName);
                    if (functionSecrets != null &&
                        !string.IsNullOrEmpty(functionSecrets.Key) &&
                        SecretEqual(keyValue, functionSecrets.Key))
                    {
                        return AuthorizationLevel.Function;
                    }
                }
            }

            return AuthorizationLevel.Anonymous;
        }