From 0a4ff82af0f98ecf4ead70ce43a85bb91fec3d7d Mon Sep 17 00:00:00 2001 From: Trevor Payne Date: Thu, 15 Apr 2021 13:47:46 -0500 Subject: [PATCH 1/4] Added SecretsManager Added ISecrets Added ISecretsProvider --- PepperDashEssentials/ControlSystem.cs | 1 + .../Comm and IR/GenericComm.cs | 1 + .../Config/DeviceConfig.cs | 14 +- .../Factory/DeviceFactory.cs | 98 ++++++-- .../PepperDash_Essentials_Core.csproj | 4 + .../Secrets/CrestronSecretsProvider.cs | 84 +++++++ .../Secrets/Interfaces.cs | 18 ++ .../Secrets/SecretsManager.cs | 220 ++++++++++++++++++ .../Secrets/SecretsPropertiesConfig.cs | 17 ++ 9 files changed, 438 insertions(+), 19 deletions(-) create mode 100644 essentials-framework/Essentials Core/PepperDashEssentialsBase/Secrets/CrestronSecretsProvider.cs create mode 100644 essentials-framework/Essentials Core/PepperDashEssentialsBase/Secrets/Interfaces.cs create mode 100644 essentials-framework/Essentials Core/PepperDashEssentialsBase/Secrets/SecretsManager.cs create mode 100644 essentials-framework/Essentials Core/PepperDashEssentialsBase/Secrets/SecretsPropertiesConfig.cs diff --git a/PepperDashEssentials/ControlSystem.cs b/PepperDashEssentials/ControlSystem.cs index 5df5c1a2..37ef79a8 100644 --- a/PepperDashEssentials/ControlSystem.cs +++ b/PepperDashEssentials/ControlSystem.cs @@ -36,6 +36,7 @@ namespace PepperDash.Essentials Thread.MaxNumberOfUserThreads = 400; Global.ControlSystem = this; DeviceManager.Initialize(this); + SecretsManager.Initialize(); SystemMonitor.ProgramInitialization.ProgramInitializationUnderUserControl = true; } diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Comm and IR/GenericComm.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Comm and IR/GenericComm.cs index 8380a290..7648a379 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Comm and IR/GenericComm.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Comm and IR/GenericComm.cs @@ -25,6 +25,7 @@ namespace PepperDash.Essentials.Core public GenericComm(DeviceConfig config) : base(config) { + PropertiesConfig = CommFactory.GetControlPropertiesConfig(config); var commPort = CommFactory.CreateCommForDevice(config); diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Config/DeviceConfig.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Config/DeviceConfig.cs index bb95da01..1d9ed1c2 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Config/DeviceConfig.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Config/DeviceConfig.cs @@ -30,7 +30,19 @@ namespace PepperDash.Essentials.Core.Config [JsonProperty("properties")] [JsonConverter(typeof(DevicePropertiesConverter))] - public JToken Properties { get; set; } + public JToken Properties { get; set; } + + public DeviceConfig(DeviceConfig dc) + { + Key = dc.Key; + Uid = dc.Uid; + Name = dc.Name; + Group = dc.Group; + Type = dc.Type; + Properties = JToken.FromObject(dc.Properties); + } + + public DeviceConfig() {} } /// diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Factory/DeviceFactory.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Factory/DeviceFactory.cs index 3f38e2d5..a0108490 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Factory/DeviceFactory.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Factory/DeviceFactory.cs @@ -7,6 +7,8 @@ using Crestron.SimplSharpPro; using Crestron.SimplSharpPro.GeneralIO; using Crestron.SimplSharp.Reflection; using PepperDash.Core; +using Newtonsoft.Json.Linq; +using Newtonsoft.Json; using PepperDash.Essentials.Core; using PepperDash.Essentials.Core.Config; using PepperDash.Essentials.Core.CrestronIO; @@ -91,24 +93,84 @@ namespace PepperDash.Essentials.Core /// /// /// - public static IKeyed GetDevice(DeviceConfig dc) - { - var key = dc.Key; - var name = dc.Name; - var type = dc.Type; - var properties = dc.Properties; - - var typeName = dc.Type.ToLower(); - - // Check for types that have been added by plugin dlls. - if (FactoryMethods.ContainsKey(typeName)) - { - Debug.Console(0, Debug.ErrorLogLevel.Notice, "Loading '{0}' from Essentials Core", dc.Type); - return FactoryMethods[typeName].FactoryMethod(dc); - } - - return null; - } + public static IKeyed GetDevice(DeviceConfig dc) + { + try + { + Debug.Console(0, Debug.ErrorLogLevel.Notice, "Loading '{0}' from Essentials Core", dc.Type); + + var localDc = new DeviceConfig(dc); + + var key = localDc.Key; + var name = localDc.Name; + var type = localDc.Type; + var properties = localDc.Properties; + + + + var typeName = localDc.Type.ToLower(); + + Debug.Console(2, "typeName = {0}", typeName); + // Check for types that have been added by plugin dlls. + if (FactoryMethods.ContainsKey(typeName)) + { + //look for secret in username + var userSecretToken = properties["control"]["tcpSshProperties"]["username"]["secret"]; + + if (userSecretToken != null) + { + Debug.Console(2, "Found a secret for {0} - attempting to retrieve it!", name); + var userSecretResult = + JsonConvert.DeserializeObject(userSecretToken.ToString()); + var userProvider = SecretsManager.GetSecretProviderByKey(userSecretResult.Provider); + if (userProvider != null) + { + var user = userProvider.GetSecret(userSecretResult.Key); + if (user == null) + { + Debug.Console(1, + "Unable to retrieve secret for {0} - Make sure you've added it to the secrets provider"); + return null; + } + properties["control"]["tcpSshProperties"]["username"] = (string) user.Value; + } + } + + //look for secret in password + var passwordSecretToken = properties["control"]["tcpSshProperties"]["password"]["secret"]; + + if (passwordSecretToken != null) + { + Debug.Console(2, "Found a secret for {0} - attempting to retrieve it!", name); + + var passwordSecretResult = + JsonConvert.DeserializeObject(passwordSecretToken.ToString()); + var passwordProvider = SecretsManager.GetSecretProviderByKey(passwordSecretResult.Provider); + if (passwordProvider != null) + { + var password = passwordProvider.GetSecret(passwordSecretResult.Key); + if (password == null) + { + Debug.Console(1, + "Unable to retrieve secret for {0} - Make sure you've added it to the secrets provider"); + return null; + } + properties["control"]["tcpSshProperties"]["password"] = (string) password.Value; + } + } + + Debug.Console(0, "{0}", localDc.Properties.ToString()); + + return FactoryMethods[typeName].FactoryMethod(localDc); + } + return null; + } + catch (Exception ex) + { + Debug.Console(2, "Issue with getting device - {0}", ex.Message); + return null; + } + } /// /// Prints the type names and associated metadata from the FactoryMethods collection. diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/PepperDash_Essentials_Core.csproj b/essentials-framework/Essentials Core/PepperDashEssentialsBase/PepperDash_Essentials_Core.csproj index e4c775d0..f202434e 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/PepperDash_Essentials_Core.csproj +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/PepperDash_Essentials_Core.csproj @@ -320,6 +320,10 @@ + + + + diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Secrets/CrestronSecretsProvider.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Secrets/CrestronSecretsProvider.cs new file mode 100644 index 00000000..54a3f3c8 --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Secrets/CrestronSecretsProvider.cs @@ -0,0 +1,84 @@ +using System; +using Crestron.SimplSharp; +using Crestron.SimplSharp.CrestronDataStore; +using PepperDash.Core; + + +namespace PepperDash.Essentials.Core +{ + public class CrestronSecretsProvider : ISecretProvider + { + public string Key { get; set; } + //Added for reference + //private readonly bool _secureSupported; + public CrestronSecretsProvider(string key) + { + Key = key; + //Added for future encrypted reference + //_secureSupported = CrestronSecureStorage.Supported; + + //if (_secureSupported) + //{ + // return; + //} + CrestronDataStoreStatic.InitCrestronDataStore(); + + } + + + public void SetSecret(string key, object value) + { + var secret = value as string; + if (String.IsNullOrEmpty(secret)) + { + Debug.Console(2, this, "Unable to set secret for {0}:{1} - value is empty.", Key, key); + return; + } + Debug.Console(2, this, "Attempting to set Secret to {0}", secret); + var setErrorCode = CrestronDataStoreStatic.SetLocalStringValue(key, secret); + switch (setErrorCode) + { + case CrestronDataStore.CDS_ERROR.CDS_SUCCESS: + Debug.Console(2, this,"Secret Successfully Set for {0}:{1}", Key, key); + break; + default: + Debug.Console(2, this, Debug.ErrorLogLevel.Notice, "Unable to set secret for {0}:{1} - {2}", Key, key, setErrorCode.ToString()); + break; + } + } + + public ISecret GetSecret(string key) + { + string mySecret; + var getErrorCode = CrestronDataStoreStatic.GetLocalStringValue(key, out mySecret); + + switch (getErrorCode) + { + case CrestronDataStore.CDS_ERROR.CDS_SUCCESS: + Debug.Console(2, this, "Secret Successfully retrieved for {0}:{1}", Key, key); + Debug.Console(2, this, "Retreived Secret = {0}", mySecret); + return new CrestronSecret(key, mySecret, this); + default: + Debug.Console(2, this, Debug.ErrorLogLevel.Notice, "Unable to retrieve secret for {0}:{1} - {2}", + Key, key, getErrorCode.ToString()); + return null; + } + } + } + + public class CrestronSecret : ISecret + { + public ISecretProvider Provider { get; private set; } + public string Key { get; private set; } + + public object Value { get; private set; } + + public CrestronSecret(string key, string value, ISecretProvider provider) + { + Key = key; + Value = value; + Provider = provider; + } + + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Secrets/Interfaces.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Secrets/Interfaces.cs new file mode 100644 index 00000000..51b0b389 --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Secrets/Interfaces.cs @@ -0,0 +1,18 @@ +using PepperDash.Core; + +namespace PepperDash.Essentials.Core +{ + public interface ISecretProvider : IKeyed + { + void SetSecret(string key, object value); + + ISecret GetSecret(string key); + } + + public interface ISecret + { + ISecretProvider Provider { get; } + string Key { get; } + object Value { get; } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Secrets/SecretsManager.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Secrets/SecretsManager.cs new file mode 100644 index 00000000..c14bfdc6 --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Secrets/SecretsManager.cs @@ -0,0 +1,220 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Crestron.SimplSharp; +using PepperDash.Core; + + +namespace PepperDash.Essentials.Core +{ + public static class SecretsManager + { + public static List Secrets { get; set; } + + public static void Initialize() + { + Secrets = new List {new CrestronSecretsProvider("default")}; + + CrestronConsole.AddNewConsoleCommand(SetSecretProcess, "setsecret", + "Adds secrets to secret provider", + ConsoleAccessLevelEnum.AccessOperator); + + CrestronConsole.AddNewConsoleCommand(UpdateSecretProcess, "updatesecret", + "Updates secrets in secret provider", + ConsoleAccessLevelEnum.AccessAdministrator); + + CrestronConsole.AddNewConsoleCommand(DeleteSecretProcess, "deletesecret", + "Deletes secrets in secret provider", + ConsoleAccessLevelEnum.AccessAdministrator); + + + } + + public static ISecretProvider GetSecretProviderByKey(string key) + { + var secret = Secrets.FirstOrDefault(o => o.Key == key); + if (secret == null) + { + Debug.Console(1, "SecretsManager unable to retrieve SecretProvider with the key '{0}'", key); + } + return secret; + } + + private static void SetSecretProcess(string cmd) + { + string response; + var args = cmd.Split(' '); + + if (args.Length == 0) + { + //some Instructional Text + response = "Adds secrets to secret provider. Format 'setsecret "; + CrestronConsole.ConsoleCommandResponse(response); + return; + } + + if (args.Length == 1 && args[0] == "?") + { + response = "Adds secrets to secret provider. Format 'setsecret "; + CrestronConsole.ConsoleCommandResponse(response); + return; + } + + if (args.Length < 3) + { + response = "Improper number of arguments"; + CrestronConsole.ConsoleCommandResponse(response); + return; + + } + + var provider = Secrets.FirstOrDefault(o => o.Key == args[0]); + + if (provider == null) + { + //someFail + response = "Provider key invalid"; + CrestronConsole.ConsoleCommandResponse(response); + return; + + } + + var key = args[1]; + var secret = args[2]; + + if (provider.GetSecret(key) == null) + { + provider.SetSecret(key, secret); + response = + String.Format( + "Secret successfully set for {0}:{1}", + provider.Key, key); + CrestronConsole.ConsoleCommandResponse(response); + return; + } + response = + String.Format( + "Unable to set secret for {0}:{1} - Please use the 'UpdateSecret' command to modify it"); + CrestronConsole.ConsoleCommandResponse(response); + } + + private static void UpdateSecretProcess(string cmd) + { + string response; + var args = cmd.Split(' '); + + if (args.Length == 0) + { + //some Instructional Text + response = "Updates secrets in secret provider. Format 'updatesecret "; + CrestronConsole.ConsoleCommandResponse(response); + return; + + } + + if (args.Length == 1 && args[0] == "?") + { + response = "Updates secrets in secret provider. Format 'updatesecret "; + CrestronConsole.ConsoleCommandResponse(response); + return; + } + + + if (args.Length < 3) + { + //someFail + response = "Improper number of arguments"; + CrestronConsole.ConsoleCommandResponse(response); + return; + + } + + var provider = Secrets.FirstOrDefault(o => o.Key == args[0]); + + if (provider == null) + { + //someFail + response = "Provider key invalid"; + CrestronConsole.ConsoleCommandResponse(response); + return; + + } + + var key = args[1]; + var secret = args[2]; + + if (provider.GetSecret(key) != null) + { + provider.SetSecret(key, secret); + response = + String.Format( + "Secret successfully updated for {0}:{1}", + provider.Key, key); + CrestronConsole.ConsoleCommandResponse(response); + return; + } + + response = + String.Format( + "Unable to update secret for {0}:{1} - Please use the 'SetSecret' command to create a new secret"); + CrestronConsole.ConsoleCommandResponse(response); + } + + private static void DeleteSecretProcess(string cmd) + { + string response; + var args = cmd.Split(' '); + + if (args.Length == 0) + { + //some Instructional Text + response = "Deletes secrets in secret provider. Format 'deletesecret "; + CrestronConsole.ConsoleCommandResponse(response); + return; + + } + if (args.Length == 1 && args[0] == "?") + { + response = "Deletes secrets in secret provider. Format 'deletesecret "; + CrestronConsole.ConsoleCommandResponse(response); + return; + } + + + + if (args.Length < 2) + { + //someFail + response = "Improper number of arguments"; + CrestronConsole.ConsoleCommandResponse(response); + return; + + } + + var provider = Secrets.FirstOrDefault(o => o.Key == args[0]); + + if (provider == null) + { + //someFail + response = "Provider key invalid"; + CrestronConsole.ConsoleCommandResponse(response); + return; + + } + + var key = args[1]; + + + provider.SetSecret(key, ""); + response = + String.Format( + "Secret successfully deleted for {0}:{1}", + provider.Key, key); + CrestronConsole.ConsoleCommandResponse(response); + + + } + } + + +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Secrets/SecretsPropertiesConfig.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Secrets/SecretsPropertiesConfig.cs new file mode 100644 index 00000000..b17b9000 --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Secrets/SecretsPropertiesConfig.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using Newtonsoft.Json; + +namespace PepperDash.Essentials.Core +{ + public class SecretsPropertiesConfig + { + [JsonProperty("provider")] + public string Provider { get; set; } + [JsonProperty("key")] + public string Key { get; set; } + } +} \ No newline at end of file From babc3e4f1a9cf46e8bfdf0d5677a285f350f477d Mon Sep 17 00:00:00 2001 From: Trevor Payne Date: Thu, 15 Apr 2021 14:36:43 -0500 Subject: [PATCH 2/4] fixed minor registration error --- .../Extensions/JsonExtensions.cs | 42 ++++++++ .../Factory/DeviceFactory.cs | 99 ++++++++++--------- .../PepperDash_Essentials_Core.csproj | 1 + 3 files changed, 94 insertions(+), 48 deletions(-) create mode 100644 essentials-framework/Essentials Core/PepperDashEssentialsBase/Extensions/JsonExtensions.cs diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Extensions/JsonExtensions.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Extensions/JsonExtensions.cs new file mode 100644 index 00000000..cdf723ea --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Extensions/JsonExtensions.cs @@ -0,0 +1,42 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + +namespace PepperDash.Essentials.Core +{ + public static class JsonExtensions + { + public static List FindTokens(this JToken containerToken, string name) + { + List matches = new List(); + FindTokens(containerToken, name, matches); + return matches; + } + + private static void FindTokens(JToken containerToken, string name, List matches) + { + if (containerToken.Type == JTokenType.Object) + { + foreach (JProperty child in containerToken.Children()) + { + if (child.Name == name) + { + matches.Add(child.Value); + } + FindTokens(child.Value, name, matches); + } + } + else if (containerToken.Type == JTokenType.Array) + { + foreach (JToken child in containerToken.Children()) + { + FindTokens(child, name, matches); + } + } + } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Factory/DeviceFactory.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Factory/DeviceFactory.cs index a0108490..cb7dd0b7 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Factory/DeviceFactory.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Factory/DeviceFactory.cs @@ -105,65 +105,68 @@ namespace PepperDash.Essentials.Core var name = localDc.Name; var type = localDc.Type; var properties = localDc.Properties; - - + //var propRecurse = properties; var typeName = localDc.Type.ToLower(); Debug.Console(2, "typeName = {0}", typeName); // Check for types that have been added by plugin dlls. - if (FactoryMethods.ContainsKey(typeName)) + if (!FactoryMethods.ContainsKey(typeName)) return null; + + /*foreach (var obj in (propRecurse as JObject).FindTokens("secret").OfType()) + { + Debug.Console(2, obj.ToString()); + }*/ + + + //look for secret in username + var userSecretToken = properties["control"]["tcpSshProperties"]["username"]["secret"]; + + if (userSecretToken != null) { - //look for secret in username - var userSecretToken = properties["control"]["tcpSshProperties"]["username"]["secret"]; - - if (userSecretToken != null) + Debug.Console(2, "Found a secret for {0} - attempting to retrieve it!", name); + var userSecretResult = + JsonConvert.DeserializeObject(userSecretToken.ToString()); + var userProvider = SecretsManager.GetSecretProviderByKey(userSecretResult.Provider); + if (userProvider != null) { - Debug.Console(2, "Found a secret for {0} - attempting to retrieve it!", name); - var userSecretResult = - JsonConvert.DeserializeObject(userSecretToken.ToString()); - var userProvider = SecretsManager.GetSecretProviderByKey(userSecretResult.Provider); - if (userProvider != null) + var user = userProvider.GetSecret(userSecretResult.Key); + if (user == null) { - var user = userProvider.GetSecret(userSecretResult.Key); - if (user == null) - { - Debug.Console(1, - "Unable to retrieve secret for {0} - Make sure you've added it to the secrets provider"); - return null; - } - properties["control"]["tcpSshProperties"]["username"] = (string) user.Value; + Debug.Console(1, + "Unable to retrieve secret for {0} - Make sure you've added it to the secrets provider"); + return null; } + properties["control"]["tcpSshProperties"]["username"] = (string) user.Value; } - - //look for secret in password - var passwordSecretToken = properties["control"]["tcpSshProperties"]["password"]["secret"]; - - if (passwordSecretToken != null) - { - Debug.Console(2, "Found a secret for {0} - attempting to retrieve it!", name); - - var passwordSecretResult = - JsonConvert.DeserializeObject(passwordSecretToken.ToString()); - var passwordProvider = SecretsManager.GetSecretProviderByKey(passwordSecretResult.Provider); - if (passwordProvider != null) - { - var password = passwordProvider.GetSecret(passwordSecretResult.Key); - if (password == null) - { - Debug.Console(1, - "Unable to retrieve secret for {0} - Make sure you've added it to the secrets provider"); - return null; - } - properties["control"]["tcpSshProperties"]["password"] = (string) password.Value; - } - } - - Debug.Console(0, "{0}", localDc.Properties.ToString()); - - return FactoryMethods[typeName].FactoryMethod(localDc); } - return null; + + //look for secret in password + var passwordSecretToken = properties["control"]["tcpSshProperties"]["password"]["secret"]; + + if (passwordSecretToken != null) + { + Debug.Console(2, "Found a secret for {0} - attempting to retrieve it!", name); + + var passwordSecretResult = + JsonConvert.DeserializeObject(passwordSecretToken.ToString()); + var passwordProvider = SecretsManager.GetSecretProviderByKey(passwordSecretResult.Provider); + if (passwordProvider != null) + { + var password = passwordProvider.GetSecret(passwordSecretResult.Key); + if (password == null) + { + Debug.Console(1, + "Unable to retrieve secret for {0} - Make sure you've added it to the secrets provider"); + return null; + } + properties["control"]["tcpSshProperties"]["password"] = (string)password.Value; + } + } + + Debug.Console(0, "{0}", localDc.Properties.ToString()); + + return FactoryMethods[typeName].FactoryMethod(localDc); } catch (Exception ex) { diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/PepperDash_Essentials_Core.csproj b/essentials-framework/Essentials Core/PepperDashEssentialsBase/PepperDash_Essentials_Core.csproj index f202434e..e65b081b 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/PepperDash_Essentials_Core.csproj +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/PepperDash_Essentials_Core.csproj @@ -209,6 +209,7 @@ + From 2787c7fc523551eb76d05d75aefd33f63c7a99c0 Mon Sep 17 00:00:00 2001 From: Trevor Payne Date: Thu, 15 Apr 2021 18:47:13 -0500 Subject: [PATCH 3/4] Close #685 Adds support for Secrets --- .../Factory/DeviceFactory.cs | 147 ++++++++---------- .../Secrets/CrestronSecretsProvider.cs | 16 +- .../Secrets/Interfaces.cs | 6 + .../Secrets/SecretsManager.cs | 8 + .../Secrets/SecretsPropertiesConfig.cs | 3 + 5 files changed, 95 insertions(+), 85 deletions(-) diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Factory/DeviceFactory.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Factory/DeviceFactory.cs index cb7dd0b7..66bf5d2b 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Factory/DeviceFactory.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Factory/DeviceFactory.cs @@ -85,96 +85,79 @@ namespace PepperDash.Essentials.Core var wrapper = new DeviceFactoryWrapper() { CType = cType, Description = description, FactoryMethod = method }; DeviceFactory.FactoryMethods.Add(typeName, wrapper); - } - - /// - /// The factory method for Core "things". Also iterates the Factory methods that have - /// been loaded from plugins - /// - /// - /// - public static IKeyed GetDevice(DeviceConfig dc) - { - try - { + } + + private static void CheckForSecrets(IEnumerable obj) + { + foreach (var prop in obj.Where(prop => prop.Value as JObject != null)) + { + if (prop.Name.ToLower() == "secret") + { + var secret = GetSecret(JsonConvert.DeserializeObject(prop.Children().First().ToString())); + prop.Parent.Replace(secret); + } + var recurseProp = prop.Value as JObject; + if (recurseProp == null) return; + CheckForSecrets(recurseProp.Properties()); + } + } + + private static string GetSecret(SecretsPropertiesConfig data) + { + var secretProvider = SecretsManager.GetSecretProviderByKey(data.Provider); + if (secretProvider == null) return null; + var secret = secretProvider.GetSecret(data.Key); + if (secret != null) return (string) secret.Value; + Debug.Console(1, + "Unable to retrieve secret {0}{1} - Make sure you've added it to the secrets provider", + data.Provider, data.Key); + return null; + } + + + /// + /// The factory method for Core "things". Also iterates the Factory methods that have + /// been loaded from plugins + /// + /// + /// + public static + IKeyed GetDevice(DeviceConfig dc) + { + try + { Debug.Console(0, Debug.ErrorLogLevel.Notice, "Loading '{0}' from Essentials Core", dc.Type); - var localDc = new DeviceConfig(dc); + var localDc = new DeviceConfig(dc); - var key = localDc.Key; - var name = localDc.Name; - var type = localDc.Type; + var key = localDc.Key; + var name = localDc.Name; + var type = localDc.Type; var properties = localDc.Properties; - //var propRecurse = properties; + //var propRecurse = properties; - var typeName = localDc.Type.ToLower(); + var typeName = localDc.Type.ToLower(); + + + var jObject = properties as JObject; + if (jObject != null) + { + var jProp = jObject.Properties(); + + CheckForSecrets(jProp); + } Debug.Console(2, "typeName = {0}", typeName); - // Check for types that have been added by plugin dlls. - if (!FactoryMethods.ContainsKey(typeName)) return null; + // Check for types that have been added by plugin dlls. + return !FactoryMethods.ContainsKey(typeName) ? null : FactoryMethods[typeName].FactoryMethod(localDc); + } + catch (Exception ex) + { + Debug.Console(2, "Issue with getting device - {0}", ex.Message); + return null; + } + } - /*foreach (var obj in (propRecurse as JObject).FindTokens("secret").OfType()) - { - Debug.Console(2, obj.ToString()); - }*/ - - - //look for secret in username - var userSecretToken = properties["control"]["tcpSshProperties"]["username"]["secret"]; - - if (userSecretToken != null) - { - Debug.Console(2, "Found a secret for {0} - attempting to retrieve it!", name); - var userSecretResult = - JsonConvert.DeserializeObject(userSecretToken.ToString()); - var userProvider = SecretsManager.GetSecretProviderByKey(userSecretResult.Provider); - if (userProvider != null) - { - var user = userProvider.GetSecret(userSecretResult.Key); - if (user == null) - { - Debug.Console(1, - "Unable to retrieve secret for {0} - Make sure you've added it to the secrets provider"); - return null; - } - properties["control"]["tcpSshProperties"]["username"] = (string) user.Value; - } - } - - //look for secret in password - var passwordSecretToken = properties["control"]["tcpSshProperties"]["password"]["secret"]; - - if (passwordSecretToken != null) - { - Debug.Console(2, "Found a secret for {0} - attempting to retrieve it!", name); - - var passwordSecretResult = - JsonConvert.DeserializeObject(passwordSecretToken.ToString()); - var passwordProvider = SecretsManager.GetSecretProviderByKey(passwordSecretResult.Provider); - if (passwordProvider != null) - { - var password = passwordProvider.GetSecret(passwordSecretResult.Key); - if (password == null) - { - Debug.Console(1, - "Unable to retrieve secret for {0} - Make sure you've added it to the secrets provider"); - return null; - } - properties["control"]["tcpSshProperties"]["password"] = (string)password.Value; - } - } - - Debug.Console(0, "{0}", localDc.Properties.ToString()); - - return FactoryMethods[typeName].FactoryMethod(localDc); - } - catch (Exception ex) - { - Debug.Console(2, "Issue with getting device - {0}", ex.Message); - return null; - } - } - /// /// Prints the type names and associated metadata from the FactoryMethods collection. /// diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Secrets/CrestronSecretsProvider.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Secrets/CrestronSecretsProvider.cs index 54a3f3c8..87dddea7 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Secrets/CrestronSecretsProvider.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Secrets/CrestronSecretsProvider.cs @@ -25,7 +25,11 @@ namespace PepperDash.Essentials.Core } - + /// + /// Set secret for item in the CrestronSecretsProvider + /// + /// Secret Key + /// Secret Value public void SetSecret(string key, object value) { var secret = value as string; @@ -34,7 +38,6 @@ namespace PepperDash.Essentials.Core Debug.Console(2, this, "Unable to set secret for {0}:{1} - value is empty.", Key, key); return; } - Debug.Console(2, this, "Attempting to set Secret to {0}", secret); var setErrorCode = CrestronDataStoreStatic.SetLocalStringValue(key, secret); switch (setErrorCode) { @@ -47,6 +50,11 @@ namespace PepperDash.Essentials.Core } } + /// + /// Retrieve secret for item in the CrestronSecretsProvider + /// + /// Secret Key + /// ISecret Object containing key, provider, and value public ISecret GetSecret(string key) { string mySecret; @@ -56,7 +64,6 @@ namespace PepperDash.Essentials.Core { case CrestronDataStore.CDS_ERROR.CDS_SUCCESS: Debug.Console(2, this, "Secret Successfully retrieved for {0}:{1}", Key, key); - Debug.Console(2, this, "Retreived Secret = {0}", mySecret); return new CrestronSecret(key, mySecret, this); default: Debug.Console(2, this, Debug.ErrorLogLevel.Notice, "Unable to retrieve secret for {0}:{1} - {2}", @@ -66,6 +73,9 @@ namespace PepperDash.Essentials.Core } } + /// + /// Special container class for CrestronSecret provider + /// public class CrestronSecret : ISecret { public ISecretProvider Provider { get; private set; } diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Secrets/Interfaces.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Secrets/Interfaces.cs index 51b0b389..86ae65cf 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Secrets/Interfaces.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Secrets/Interfaces.cs @@ -2,6 +2,9 @@ namespace PepperDash.Essentials.Core { + /// + /// All ISecrecretProvider classes must implement this interface. + /// public interface ISecretProvider : IKeyed { void SetSecret(string key, object value); @@ -9,6 +12,9 @@ namespace PepperDash.Essentials.Core ISecret GetSecret(string key); } + /// + /// interface for delivering secrets in Essentials. + /// public interface ISecret { ISecretProvider Provider { get; } diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Secrets/SecretsManager.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Secrets/SecretsManager.cs index c14bfdc6..f50051da 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Secrets/SecretsManager.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Secrets/SecretsManager.cs @@ -11,6 +11,9 @@ namespace PepperDash.Essentials.Core { public static List Secrets { get; set; } + /// + /// Initialize the SecretsManager + /// public static void Initialize() { Secrets = new List {new CrestronSecretsProvider("default")}; @@ -30,6 +33,11 @@ namespace PepperDash.Essentials.Core } + /// + /// Method to return a ISecretProvider to Set, Get, and Delete Secrets + /// + /// Secret Provider Key + /// public static ISecretProvider GetSecretProviderByKey(string key) { var secret = Secrets.FirstOrDefault(o => o.Key == key); diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Secrets/SecretsPropertiesConfig.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Secrets/SecretsPropertiesConfig.cs index b17b9000..68026aa1 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Secrets/SecretsPropertiesConfig.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Secrets/SecretsPropertiesConfig.cs @@ -7,6 +7,9 @@ using Newtonsoft.Json; namespace PepperDash.Essentials.Core { + /// + /// Provide a way to easily deserialize into a secret object from config + /// public class SecretsPropertiesConfig { [JsonProperty("provider")] From 8643ed2cafbd32a737019500bf2aaf82d6727477 Mon Sep 17 00:00:00 2001 From: Trevor Payne Date: Thu, 15 Apr 2021 19:14:24 -0500 Subject: [PATCH 4/4] #685 - Requested Fixes per AWelker --- .../Secrets/CrestronSecretsProvider.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Secrets/CrestronSecretsProvider.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Secrets/CrestronSecretsProvider.cs index 87dddea7..609aba64 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Secrets/CrestronSecretsProvider.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Secrets/CrestronSecretsProvider.cs @@ -66,9 +66,9 @@ namespace PepperDash.Essentials.Core Debug.Console(2, this, "Secret Successfully retrieved for {0}:{1}", Key, key); return new CrestronSecret(key, mySecret, this); default: - Debug.Console(2, this, Debug.ErrorLogLevel.Notice, "Unable to retrieve secret for {0}:{1} - {2}", + Debug.Console(0, this, Debug.ErrorLogLevel.Notice, "Unable to retrieve secret for {0}:{1} - {2}", Key, key, getErrorCode.ToString()); - return null; + return new CrestronSecret(key, String.Empty, this); } } }