diff --git a/src/WebJobs.Script.WebHost/Security/KeyManagement/ScriptSecrets.cs b/src/WebJobs.Script.WebHost/Security/KeyManagement/ScriptSecrets.cs index f68fc975c0..f9bbbcc209 100644 --- a/src/WebJobs.Script.WebHost/Security/KeyManagement/ScriptSecrets.cs +++ b/src/WebJobs.Script.WebHost/Security/KeyManagement/ScriptSecrets.cs @@ -4,9 +4,11 @@ using System; using System.Collections; using System.Collections.Generic; +using System.Globalization; using System.Linq; using System.Text; using System.Threading.Tasks; +using Microsoft.Azure.WebJobs.Script.Config; using Newtonsoft.Json; namespace Microsoft.Azure.WebJobs.Script.WebHost @@ -15,6 +17,9 @@ public abstract class ScriptSecrets { protected ScriptSecrets() { + HostName = ScriptSettingsManager.Instance.GetSetting(EnvironmentSettingNames.AzureWebsiteHostName); + InstanceId = ScriptSettingsManager.Instance.InstanceId; + Source = ScriptConstants.Runtime; } [JsonIgnore] @@ -23,6 +28,15 @@ protected ScriptSecrets() [JsonIgnore] public abstract ScriptSecretsType SecretsType { get; } + [JsonProperty(PropertyName = "hostName")] + public string HostName { get; set; } + + [JsonProperty(PropertyName = "instanceId")] + public string InstanceId { get; set; } + + [JsonProperty(PropertyName = "source")] + public string Source { get; set; } + protected abstract ICollection GetKeys(string keyScope); public abstract ScriptSecrets Refresh(IKeyValueConverterFactory factory); diff --git a/src/WebJobs.Script/Config/ScriptSettingsManager.cs b/src/WebJobs.Script/Config/ScriptSettingsManager.cs index 9ee01890c2..36aa2a9346 100644 --- a/src/WebJobs.Script/Config/ScriptSettingsManager.cs +++ b/src/WebJobs.Script/Config/ScriptSettingsManager.cs @@ -76,6 +76,17 @@ public virtual string AzureWebsiteUniqueSlotName } } + public virtual string InstanceId + { + get + { + string instanceId = GetSetting(EnvironmentSettingNames.AzureWebsiteInstanceId) + ?? Environment.MachineName.GetHashCode().ToString("X").PadLeft(32, '0'); + + return instanceId.Substring(0, 32); + } + } + public virtual string ApplicationInsightsInstrumentationKey { get => GetSettingFromCache(EnvironmentSettingNames.AppInsightsInstrumentationKey); diff --git a/src/WebJobs.Script/Host/ScriptHost.cs b/src/WebJobs.Script/Host/ScriptHost.cs index 3d08dabbae..dfd282f933 100644 --- a/src/WebJobs.Script/Host/ScriptHost.cs +++ b/src/WebJobs.Script/Host/ScriptHost.cs @@ -122,10 +122,7 @@ public string InstanceId { if (_instanceId == null) { - _instanceId = _settingsManager.GetSetting(EnvironmentSettingNames.AzureWebsiteInstanceId) - ?? Environment.MachineName.GetHashCode().ToString("X").PadLeft(32, '0'); - - _instanceId = _instanceId.Substring(0, 32); + _instanceId = _settingsManager.InstanceId; } return _instanceId; diff --git a/src/WebJobs.Script/ScriptConstants.cs b/src/WebJobs.Script/ScriptConstants.cs index 5b8d283305..6a1edd5adf 100644 --- a/src/WebJobs.Script/ScriptConstants.cs +++ b/src/WebJobs.Script/ScriptConstants.cs @@ -70,6 +70,7 @@ public static class ScriptConstants public static readonly ImmutableArray HttpMethods = ImmutableArray.Create("get", "post", "delete", "head", "patch", "put", "options"); public const string HttpMethodConstraintName = "httpMethod"; public static readonly ImmutableArray AssemblyFileTypes = ImmutableArray.Create(".dll", ".exe"); + public const string Runtime = "runtime"; public const int MaximumHostIdLength = 32; public const int DynamicSkuConnectionLimit = 50; diff --git a/test/WebJobs.Script.Tests/Security/ScriptSecretSerializerV1Tests.cs b/test/WebJobs.Script.Tests/Security/ScriptSecretSerializerV1Tests.cs index 3893fc522a..6e0bc80ce8 100644 --- a/test/WebJobs.Script.Tests/Security/ScriptSecretSerializerV1Tests.cs +++ b/test/WebJobs.Script.Tests/Security/ScriptSecretSerializerV1Tests.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Linq; +using Microsoft.Azure.WebJobs.Script.Config; using Microsoft.Azure.WebJobs.Script.WebHost; using Newtonsoft.Json.Linq; using Xunit; @@ -40,16 +41,23 @@ public void SerializeFunctionSecrets_ReturnsExpectedResult() var jsonObject = JObject.Parse(serializedSecret); var serializedSecrets = jsonObject.Property("keys")?.Value?.ToObject>(); + var source = jsonObject.Property("source")?.Value; + var hostName = jsonObject.Property("hostName")?.Value; + var instanceId = jsonObject.Property("instanceId")?.Value; Assert.NotNull(serializedSecret); AssertKeyCollectionsEquality(secrets.Keys, serializedSecrets); + Assert.Equal(source, secrets.Source); + Assert.Equal(hostName, secrets.HostName); + Assert.Equal(instanceId, secrets.InstanceId); } - [Fact] - public void DeserializeFunctionSecrets_ReturnsExpectedResult() + [Theory] + [InlineData("{ 'keys': [ { 'name': 'Key1', 'value': 'Value1', 'encrypted': false }, { 'name': 'Key2', 'value': 'Value2', 'encrypted': true } ] }", null)] + [InlineData("{ 'keys': [ { 'name': 'Key1', 'value': 'Value1', 'encrypted': false }, { 'name': 'Key2', 'value': 'Value2', 'encrypted': true } ], 'hostName': 'test', 'source': 'runtime'}", "test")] + public void DeserializeFunctionSecrets_ReturnsExpectedResult(string serializedSecret, string hostName) { var serializer = new ScriptSecretSerializerV1(); - var serializedSecret = "{ 'keys': [ { 'name': 'Key1', 'value': 'Value1', 'encrypted': false }, { 'name': 'Key2', 'value': 'Value2', 'encrypted': true } ] }"; var expected = new List { new Key @@ -67,14 +75,16 @@ public void DeserializeFunctionSecrets_ReturnsExpectedResult() }; FunctionSecrets actual = serializer.DeserializeSecrets(JObject.Parse(serializedSecret)); + Assert.Equal(hostName, actual.HostName); AssertKeyCollectionsEquality(expected, actual.Keys); } - [Fact] - public void DeserializeHostSecrets_ReturnsExpectedResult() + [Theory] + [InlineData("{'masterKey':{'name':'master','value':'1234','encrypted':false},'functionKeys':[{'name':'Key1','value':'Value1','encrypted':false},{'name':'Key2','value':'Value2','encrypted':true}]}", null)] + [InlineData("{'masterKey':{'name':'master','value':'1234','encrypted':false},'functionKeys':[{'name':'Key1','value':'Value1','encrypted':false},{'name':'Key2','value':'Value2','encrypted':true}], 'hostName': 'test', 'source': 'runtime' }", "test")] + public void DeserializeHostSecrets_ReturnsExpectedResult(string serializedSecret, string hostName) { var serializer = new ScriptSecretSerializerV1(); - var serializedSecret = "{'masterKey':{'name':'master','value':'1234','encrypted':false},'functionKeys':[{'name':'Key1','value':'Value1','encrypted':false},{'name':'Key2','value':'Value2','encrypted':true}]}"; var expected = new HostSecrets { MasterKey = new Key { Name = "master", Value = "1234" }, @@ -92,13 +102,16 @@ public void DeserializeHostSecrets_ReturnsExpectedResult() Value = "Value2", IsEncrypted = true } - } + }, + HostName = hostName }; HostSecrets actual = serializer.DeserializeSecrets(JObject.Parse(serializedSecret)); Assert.NotNull(actual); Assert.Equal(expected.MasterKey, actual.MasterKey); + Assert.Equal(actual.HostName, hostName); + Assert.Equal(expected.Source, ScriptConstants.Runtime); AssertKeyCollectionsEquality(expected.FunctionKeys, actual.FunctionKeys); } @@ -132,10 +145,12 @@ public void SerializeHostSecrets_ReturnsExpectedResult() var jsonObject = JObject.Parse(serializedSecret); var functionSecrets = jsonObject.Property("functionKeys")?.Value?.ToObject>(); var masterKey = jsonObject.Property("masterKey")?.Value?.ToObject(); + var instanceId = jsonObject.Property("instanceId")?.Value; Assert.NotNull(serializedSecret); Assert.Equal(secrets.MasterKey, masterKey); AssertKeyCollectionsEquality(secrets.FunctionKeys, functionSecrets); + Assert.Equal(instanceId, secrets.InstanceId); } [Theory] @@ -143,6 +158,7 @@ public void SerializeHostSecrets_ReturnsExpectedResult() [InlineData(typeof(FunctionSecrets), false, "{'key':'functionKeySecretString'}")] [InlineData(typeof(HostSecrets), true, "{'masterKey': {'name': 'master','value': '1234','encrypted': false},'functionKeys': [{'name': 'Key1','value': 'Value1','encrypted': false},{'name': 'Key2','value': 'Value2','encrypted': true}]}")] [InlineData(typeof(FunctionSecrets), true, "{'keys': [{'name': 'Key1','value': 'Value1','encrypted': false},{'name': 'Key2','value': 'Value2','encrypted': true}]}")] + [InlineData(typeof(HostSecrets), false, "{'masterKey': 'masterKeySecretString','functionKey': 'functionKeySecretString', 'hostName': 'test1', 'instanceId': 'test2', 'source': 'test3'}")] public void CanSerialize_WithValidHostPayload_ReturnsTrue(Type type, bool expectedResult, string input) { var serializer = new ScriptSecretSerializerV1();