public void GetHostSecrets_WhenNoHostSecretFileExists_GeneratesSecretsAndPersistsFiles() { using (var directory = new TempDirectory()) { string expectedTraceMessage = Resources.TraceHostSecretGeneration; Mock <IKeyValueConverterFactory> mockValueConverterFactory = GetConverterFactoryMock(false, false); HostSecretsInfo hostSecrets; var traceWriter = new TestTraceWriter(TraceLevel.Verbose); using (var secretManager = new SecretManager(directory.Path, mockValueConverterFactory.Object, traceWriter)) { hostSecrets = secretManager.GetHostSecrets(); } string secretsJson = File.ReadAllText(Path.Combine(directory.Path, ScriptConstants.HostMetadataFileName)); HostSecrets persistedSecrets = ScriptSecretSerializer.DeserializeSecrets <HostSecrets>(secretsJson); Assert.NotNull(hostSecrets); Assert.NotNull(persistedSecrets); Assert.Equal(1, hostSecrets.FunctionKeys.Count); Assert.NotNull(hostSecrets.MasterKey); Assert.Equal(persistedSecrets.MasterKey.Value, hostSecrets.MasterKey); Assert.Equal(persistedSecrets.FunctionKeys.First().Value, hostSecrets.FunctionKeys.First().Value); Assert.True(traceWriter.Traces.Any(t => t.Level == TraceLevel.Verbose && t.Message.IndexOf(expectedTraceMessage) > -1)); } }
public void GetHostSecrets_UpdatesStaleSecrets() { var secretsPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); try { using (var variables = new TestScopedEnvironmentVariables("AzureWebJobsFeatureFlags", "MultiKey")) { Directory.CreateDirectory(secretsPath); string hostSecretsJson = @"{ 'masterKey': { 'name': 'master', 'value': '1234', 'encrypted': false }, 'functionKeys': [ { 'name': 'Key1', 'value': 'HostValue1', 'encrypted': false }, { 'name': 'Key3', 'value': 'HostValue3', 'encrypted': false } ] }"; File.WriteAllText(Path.Combine(secretsPath, ScriptConstants.HostMetadataFileName), hostSecretsJson); Mock <IKeyValueConverterFactory> mockValueConverterFactory = GetConverterFactoryMock(); HostSecretsInfo hostSecrets; using (var secretManager = new SecretManager(secretsPath, mockValueConverterFactory.Object)) { hostSecrets = secretManager.GetHostSecrets(); } // Read the persisted content var result = JsonConvert.DeserializeObject <HostSecrets>(File.ReadAllText(Path.Combine(secretsPath, ScriptConstants.HostMetadataFileName))); bool functionSecretsConverted = hostSecrets.FunctionKeys.Values.Zip(result.FunctionKeys, (r1, r2) => string.Equals("!" + r1, r2.Value)).All(r => r); Assert.Equal(2, result.FunctionKeys.Count); Assert.Equal("!" + hostSecrets.MasterKey, result.MasterKey.Value); Assert.True(functionSecretsConverted, "Function secrets were not persisted"); } } finally { Directory.Delete(secretsPath, true); } }
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); }
internal static void Initialize(ContainerBuilder builder, WebHostSettings settings) { ScriptHostConfiguration scriptHostConfig = new ScriptHostConfiguration() { RootScriptPath = settings.ScriptPath, RootLogPath = settings.LogPath, FileLoggingEnabled = true }; // If running on Azure Web App, derive the host ID from the site name string hostId = Environment.GetEnvironmentVariable("WEBSITE_SITE_NAME"); if (!String.IsNullOrEmpty(hostId)) { // Truncate to the max host name length if needed const int MaximumHostIdLength = 32; if (hostId.Length > MaximumHostIdLength) { hostId = hostId.Substring(0, MaximumHostIdLength); } // Trim any trailing - as they can cause problems with queue names hostId = hostId.TrimEnd('-'); scriptHostConfig.HostConfig.HostId = hostId.ToLowerInvariant(); } WebScriptHostManager scriptHostManager = new WebScriptHostManager(scriptHostConfig); builder.RegisterInstance <WebScriptHostManager>(scriptHostManager); SecretManager secretManager = new SecretManager(settings.SecretsPath); // Make sure that host secrets get created on startup if they don't exist secretManager.GetHostSecrets(); builder.RegisterInstance <SecretManager>(secretManager); WebHookReceiverManager webHookReceiverManager = new WebHookReceiverManager(secretManager); builder.RegisterInstance <WebHookReceiverManager>(webHookReceiverManager); if (!settings.IsSelfHost) { HostingEnvironment.QueueBackgroundWorkItem((ct) => scriptHostManager.RunAndBlock(ct)); } else { Task.Run(() => scriptHostManager.RunAndBlock()); } }
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; }
internal static void Initialize(ContainerBuilder builder, WebHostSettings settings) { ScriptHostConfiguration scriptHostConfig = new ScriptHostConfiguration() { RootScriptPath = settings.ScriptPath, RootLogPath = settings.LogPath, FileLoggingEnabled = true }; // If running on Azure Web App, derive the host ID from the site name string hostId = Environment.GetEnvironmentVariable("WEBSITE_SITE_NAME"); if (!String.IsNullOrEmpty(hostId)) { // Truncate to the max host name length if needed const int MaximumHostIdLength = 32; if (hostId.Length > MaximumHostIdLength) { hostId = hostId.Substring(0, MaximumHostIdLength); } // Trim any trailing - as they can cause problems with queue names hostId = hostId.TrimEnd('-'); scriptHostConfig.HostConfig.HostId = hostId.ToLowerInvariant(); } WebScriptHostManager scriptHostManager = new WebScriptHostManager(scriptHostConfig); builder.RegisterInstance<WebScriptHostManager>(scriptHostManager); SecretManager secretManager = new SecretManager(settings.SecretsPath); // Make sure that host secrets get created on startup if they don't exist secretManager.GetHostSecrets(); builder.RegisterInstance<SecretManager>(secretManager); WebHookReceiverManager webHookReceiverManager = new WebHookReceiverManager(secretManager); builder.RegisterInstance<WebHookReceiverManager>(webHookReceiverManager); if (!settings.IsSelfHost) { HostingEnvironment.QueueBackgroundWorkItem((ct) => scriptHostManager.RunAndBlock(ct)); } else { Task.Run(() => scriptHostManager.RunAndBlock()); } }
public void GetHostSecrets_UpdatesStaleSecrets() { using (var directory = new TempDirectory()) { string expectedTraceMessage = Resources.TraceStaleHostSecretRefresh; string hostSecretsJson = @"{ 'masterKey': { 'name': 'master', 'value': '1234', 'encrypted': false }, 'functionKeys': [ { 'name': 'Key1', 'value': 'HostValue1', 'encrypted': false }, { 'name': 'Key3', 'value': 'HostValue3', 'encrypted': false } ] }"; File.WriteAllText(Path.Combine(directory.Path, ScriptConstants.HostMetadataFileName), hostSecretsJson); Mock <IKeyValueConverterFactory> mockValueConverterFactory = GetConverterFactoryMock(); HostSecretsInfo hostSecrets; var traceWriter = new TestTraceWriter(TraceLevel.Verbose); using (var secretManager = new SecretManager(directory.Path, mockValueConverterFactory.Object, traceWriter)) { hostSecrets = secretManager.GetHostSecrets(); } // Read the persisted content var result = JsonConvert.DeserializeObject <HostSecrets>(File.ReadAllText(Path.Combine(directory.Path, ScriptConstants.HostMetadataFileName))); bool functionSecretsConverted = hostSecrets.FunctionKeys.Values.Zip(result.FunctionKeys, (r1, r2) => string.Equals("!" + r1, r2.Value)).All(r => r); Assert.Equal(2, result.FunctionKeys.Count); Assert.Equal("!" + hostSecrets.MasterKey, result.MasterKey.Value); 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 GetHostSecrets_WhenNoHostSecretFileExists_GeneratesSecretsAndPersistsFiles() { using (var directory = new TempDirectory()) { string expectedTraceMessage = Resources.TraceHostSecretGeneration; Mock<IKeyValueConverterFactory> mockValueConverterFactory = GetConverterFactoryMock(false, false); HostSecretsInfo hostSecrets; var traceWriter = new TestTraceWriter(TraceLevel.Verbose); using (var secretManager = new SecretManager(directory.Path, mockValueConverterFactory.Object, traceWriter)) { hostSecrets = secretManager.GetHostSecrets(); } string secretsJson = File.ReadAllText(Path.Combine(directory.Path, ScriptConstants.HostMetadataFileName)); HostSecrets persistedSecrets = ScriptSecretSerializer.DeserializeSecrets<HostSecrets>(secretsJson); Assert.NotNull(hostSecrets); Assert.NotNull(persistedSecrets); Assert.Equal(1, hostSecrets.FunctionKeys.Count); Assert.NotNull(hostSecrets.MasterKey); Assert.Equal(persistedSecrets.MasterKey.Value, hostSecrets.MasterKey); Assert.Equal(persistedSecrets.FunctionKeys.First().Value, hostSecrets.FunctionKeys.First().Value); Assert.True(traceWriter.Traces.Any(t => t.Level == TraceLevel.Verbose && t.Message.IndexOf(expectedTraceMessage) > -1)); } }
public void GetHostSecrets_UpdatesStaleSecrets() { using (var directory = new TempDirectory()) { string expectedTraceMessage = Resources.TraceStaleHostSecretRefresh; string hostSecretsJson = @"{ 'masterKey': { 'name': 'master', 'value': '1234', 'encrypted': false }, 'functionKeys': [ { 'name': 'Key1', 'value': 'HostValue1', 'encrypted': false }, { 'name': 'Key3', 'value': 'HostValue3', 'encrypted': false } ] }"; File.WriteAllText(Path.Combine(directory.Path, ScriptConstants.HostMetadataFileName), hostSecretsJson); Mock<IKeyValueConverterFactory> mockValueConverterFactory = GetConverterFactoryMock(); HostSecretsInfo hostSecrets; var traceWriter = new TestTraceWriter(TraceLevel.Verbose); using (var secretManager = new SecretManager(directory.Path, mockValueConverterFactory.Object, traceWriter)) { hostSecrets = secretManager.GetHostSecrets(); } // Read the persisted content var result = JsonConvert.DeserializeObject<HostSecrets>(File.ReadAllText(Path.Combine(directory.Path, ScriptConstants.HostMetadataFileName))); bool functionSecretsConverted = hostSecrets.FunctionKeys.Values.Zip(result.FunctionKeys, (r1, r2) => string.Equals("!" + r1, r2.Value)).All(r => r); Assert.Equal(2, result.FunctionKeys.Count); Assert.Equal("!" + hostSecrets.MasterKey, result.MasterKey.Value); Assert.True(functionSecretsConverted, "Function secrets were not persisted"); Assert.True(traceWriter.Traces.Any(t => t.Level == TraceLevel.Verbose && t.Message.IndexOf(expectedTraceMessage) > -1)); } }