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); }
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); }
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)); } }
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; }