From 94ce848c8c2522ee5ee4eb8c1c97f3b554da7071 Mon Sep 17 00:00:00 2001 From: Chris Tacke Date: Thu, 14 Mar 2024 11:03:38 -0500 Subject: [PATCH 1/2] adding support required for remote provisioning --- .../Current/Device/DeviceInfoCommand.cs | 18 +++++++- .../Current/Device/DeviceProvisionCommand.cs | 45 ++++++++++++++----- .../Meadow.Cli/Properties/launchSettings.json | 12 +++++ Source/v2/Meadow.Cli/Strings.cs | 3 +- .../Connections/LocalConnection.cs | 36 +++++++++++---- .../Connection/MeadowConnectionManager.cs | 5 +++ 6 files changed, 98 insertions(+), 21 deletions(-) diff --git a/Source/v2/Meadow.Cli/Commands/Current/Device/DeviceInfoCommand.cs b/Source/v2/Meadow.Cli/Commands/Current/Device/DeviceInfoCommand.cs index a56aef4d..732f4fd5 100644 --- a/Source/v2/Meadow.Cli/Commands/Current/Device/DeviceInfoCommand.cs +++ b/Source/v2/Meadow.Cli/Commands/Current/Device/DeviceInfoCommand.cs @@ -6,21 +6,37 @@ namespace Meadow.CLI.Commands.DeviceManagement; [Command("device info", Description = "Get the device info")] public class DeviceInfoCommand : BaseDeviceCommand { + [CommandOption("public-key", 'k', Description = "Include the target device's public key in the output", IsRequired = false)] + public bool GetKey { get; set; } = false; + public DeviceInfoCommand(MeadowConnectionManager connectionManager, ILoggerFactory loggerFactory) : base(connectionManager, loggerFactory) { - Logger?.LogInformation(Strings.GettingDeviceInfo); } protected override async ValueTask ExecuteCommand() { var device = await GetCurrentDevice(); + Logger?.LogInformation($"{Strings.GettingDeviceInfo}..."); + var deviceInfo = await device.GetDeviceInfo(CancellationToken); if (deviceInfo != null) { Logger?.LogInformation(deviceInfo.ToString()); } + + if (GetKey) + { + Logger?.LogInformation($"{Strings.GettingDevicePublicKey}..."); + + var publicKey = await device.GetPublicKey(CancellationToken); + + if (!string.IsNullOrWhiteSpace(publicKey)) + { + Logger?.LogInformation($"{publicKey}"); + } + } } } \ No newline at end of file diff --git a/Source/v2/Meadow.Cli/Commands/Current/Device/DeviceProvisionCommand.cs b/Source/v2/Meadow.Cli/Commands/Current/Device/DeviceProvisionCommand.cs index 7d0c055b..555f3c3e 100644 --- a/Source/v2/Meadow.Cli/Commands/Current/Device/DeviceProvisionCommand.cs +++ b/Source/v2/Meadow.Cli/Commands/Current/Device/DeviceProvisionCommand.cs @@ -24,6 +24,12 @@ public class DeviceProvisionCommand : BaseDeviceCommand [CommandOption("host", 'h', Description = "Optionally set a host (default is https://www.meadowcloud.co)", IsRequired = false)] public string? Host { get; set; } + [CommandOption("public-key", 'k', Description = "The public key of the device to provision. If not provided, it will be queried from the configured device.", IsRequired = false)] + public string? PublicKey { get; set; } + + [CommandOption("id", 'i', Description = "The unique ID/serial number of the device to provision. If not provided, it will be queried from the configured device.", IsRequired = false)] + public string? SerialNumber { get; set; } + public DeviceProvisionCommand(UserService userService, DeviceService deviceService, MeadowConnectionManager connectionManager, ILoggerFactory loggerFactory) : base(connectionManager, loggerFactory) { @@ -76,14 +82,31 @@ protected override async ValueTask ExecuteCommand() CommandExitCode.NotAuthorized); } - var device = await GetCurrentDevice(); + string provisioningID, provisioningName; - var info = await device.GetDeviceInfo(CancellationToken); + if (PublicKey != null) + { + if (SerialNumber == null) + { + throw new CommandException("If a public key is provided, an `id` must also be provided"); + } + provisioningID = SerialNumber; + provisioningName = Name ?? string.Empty; + } + else + { + var device = await GetCurrentDevice(); + + var info = await device.GetDeviceInfo(CancellationToken); - Logger?.LogInformation(Strings.RequestingDevicePublicKey); - var publicKey = await device.GetPublicKey(CancellationToken); + Logger?.LogInformation(Strings.RequestingDevicePublicKey); + PublicKey = await device.GetPublicKey(CancellationToken); + + provisioningID = !string.IsNullOrWhiteSpace(info?.ProcessorId) ? info.ProcessorId : info?.SerialNumber; + provisioningName = !string.IsNullOrWhiteSpace(Name) ? Name : info?.DeviceName; + } - if (string.IsNullOrWhiteSpace(publicKey)) + if (string.IsNullOrWhiteSpace(PublicKey)) { throw new CommandException( Strings.CouldNotRetrievePublicKey, @@ -93,18 +116,20 @@ protected override async ValueTask ExecuteCommand() var delimiters = new string[] { "-----END PUBLIC KEY-----\n", // F7 delimiter - "-----END RSA PUBLIC KEY-----\n" // linux/mac/windows delimiter + "-----END PUBLIC KEY-----", // F7 delimiter + "-----END RSA PUBLIC KEY-----\n", // linux/mac/windows delimiter + "-----END RSA PUBLIC KEY-----" // linux/mac/windows delimiter }; var valid = false; foreach (var delim in delimiters) { - var index = publicKey.IndexOf(delim); + var index = PublicKey.IndexOf(delim); if (index > 0) { valid = true; - publicKey = publicKey.Substring(0, publicKey.IndexOf(delim) + delim.Length); + PublicKey = PublicKey.Substring(0, PublicKey.IndexOf(delim) + delim.Length); break; } } @@ -117,10 +142,8 @@ protected override async ValueTask ExecuteCommand() } Logger?.LogInformation(Strings.ProvisioningWithCloud); - var provisioningID = !string.IsNullOrWhiteSpace(info?.ProcessorId) ? info.ProcessorId : info?.SerialNumber; - var provisioningName = !string.IsNullOrWhiteSpace(Name) ? Name : info?.DeviceName; - var result = await _deviceService.AddDevice(org.Id!, provisioningID!, publicKey, CollectionId, provisioningName, Host, CancellationToken); + var result = await _deviceService.AddDevice(org.Id!, provisioningID!, PublicKey, CollectionId, provisioningName, Host, CancellationToken); if (result.isSuccess) { diff --git a/Source/v2/Meadow.Cli/Properties/launchSettings.json b/Source/v2/Meadow.Cli/Properties/launchSettings.json index c1eee26e..750abc6b 100644 --- a/Source/v2/Meadow.Cli/Properties/launchSettings.json +++ b/Source/v2/Meadow.Cli/Properties/launchSettings.json @@ -31,6 +31,10 @@ "commandName": "Project", "commandLineArgs": "device info" }, + "Device Public Key": { + "commandName": "Project", + "commandLineArgs": "device info -k" + }, "Device Reset": { "commandName": "Project", "commandLineArgs": "device reset" @@ -43,6 +47,10 @@ "commandName": "Project", "commandLineArgs": "device clock now" }, + "Config: Set Route local": { + "commandName": "Project", + "commandLineArgs": "config route local" + }, "Config: Set Route Serial": { "commandName": "Project", "commandLineArgs": "config route COM4" @@ -215,6 +223,10 @@ "commandName": "Project", "commandLineArgs": "device provision" }, + "Device provision other": { + "commandName": "Project", + "commandLineArgs": "device provision -o christacke6612 --id 2258a2fa0739491aa484a0d6b95c3e15 -k \"-----BEGIN RSA PUBLIC KEY-----\nMIIBigKCAYEA2kj+97LvLa9tZhn9rLBGNgJIEw+Wl+qJXHXW1AmAv3zcytXJ5uGJ\nJjrU7V/y/R6R/TizZRY57puz/mG5BMopdtdNx3Cpfb35SaGmIiSiAVpELci/Ldpd\ntsGJphfSxslVkc3/s2LEZ416pybzGoTb4+KGj+L+GcNYoe6rxFgVJEv3PTlpBWbv\nCUZGBb/0twedNmmXZzq9W79UYIMQaB53fgdH9qFOvBIw9zcG2a6MY/MBGU6nr7BY\nwShjMV9AwP3z1ao6X4nbpfPtBRPjyyyGdVH4bKF7dLgBGZnShmvGfhGpaxWJlBIk\n3IJYgJbd8y5MVVhsJv6AmBmm84dyDX2Tn5wKvZ+Fbmpeez87rosm+C80VnO6Kz4E\nFmIErHGGaoBoX9L7cf33yMwty2glCNW7HYUl/YWJPg+8eyuEjOpGHqJSAW4AlfgY\n0FDNj/AR0Pni0wfQ+DUpBt5i2kgUw0MDWlWVYYbgxmO1VbvofWJwYLYgeiCxNPBj\nopp0RZfm/brRAgMBAAE=\n-----END RSA PUBLIC KEY-----\"" + }, "Login": { "commandName": "Project", "commandLineArgs": "login" diff --git a/Source/v2/Meadow.Cli/Strings.cs b/Source/v2/Meadow.Cli/Strings.cs index 2846527f..4f54232e 100644 --- a/Source/v2/Meadow.Cli/Strings.cs +++ b/Source/v2/Meadow.Cli/Strings.cs @@ -7,7 +7,8 @@ public static class Strings public const string SettingDeviceClock = "Setting device clock..."; public const string InvalidApplicationPath = "Invalid application path"; public const string InvalidParameter = "Invalid parameter"; - public const string GettingDeviceInfo = "Getting device info..."; + public const string GettingDeviceInfo = "Getting device info"; + public const string GettingDevicePublicKey = "Getting device public key"; public const string UnableToGetDeviceInfo = "Unable to get device info"; public const string RetrievingUserAndOrgInfo = "Retrieving your user and organization information..."; public const string MemberOfMoreThanOneOrg = "You are a member of more than 1 organization. Please specify the desired orgId for this device provisioning."; diff --git a/Source/v2/Meadow.Hcom/Connections/LocalConnection.cs b/Source/v2/Meadow.Hcom/Connections/LocalConnection.cs index 7332976f..b42a62b5 100755 --- a/Source/v2/Meadow.Hcom/Connections/LocalConnection.cs +++ b/Source/v2/Meadow.Hcom/Connections/LocalConnection.cs @@ -97,18 +97,21 @@ public override Task GetPublicKey(CancellationToken? cancellationToken = var pkFile = Path.Combine(sshFolder.FullName, "id_rsa.pub"); if (!File.Exists(pkFile)) { - throw new Exception("Public key not found"); + throw new Exception("Public key not found. Run 'ssh-keygen -t rsa'"); } - return Task.FromResult(File.ReadAllText(pkFile)); + var pkFileContent = File.ReadAllText(pkFile); + + if (!pkFileContent.Contains("BEGIN RSA PUBLIC KEY")) + { + pkFileContent = ExecuteWindowsCommandLine("ssh-keygen", $"-e -m pem -f {pkFile}"); + } + + return Task.FromResult(pkFileContent); } } - else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) - { - // ssh-agent sh -c 'ssh-add; ssh-add -L' - throw new PlatformNotSupportedException(); - } - else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) + else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) + || RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) { // ssh-agent sh -c 'ssh-add; ssh-add -L' var pubkey = this.ExecuteBashCommandLine("ssh-agent sh -c 'ssh-add; ssh-add -L'"); @@ -127,6 +130,23 @@ public override Task GetPublicKey(CancellationToken? cancellationToken = } } + private string ExecuteWindowsCommandLine(string command, string args) + { + var psi = new ProcessStartInfo() + { + FileName = command, + Arguments = args, + RedirectStandardOutput = true, + UseShellExecute = false, + CreateNoWindow = true + }; + + using var process = Process.Start(psi); + + process?.WaitForExit(); + + return process?.StandardOutput.ReadToEnd() ?? string.Empty; + } diff --git a/Source/v2/Meadow.Tooling.Core/Connection/MeadowConnectionManager.cs b/Source/v2/Meadow.Tooling.Core/Connection/MeadowConnectionManager.cs index b982d1b6..cf6ba45a 100644 --- a/Source/v2/Meadow.Tooling.Core/Connection/MeadowConnectionManager.cs +++ b/Source/v2/Meadow.Tooling.Core/Connection/MeadowConnectionManager.cs @@ -48,6 +48,11 @@ public MeadowConnectionManager(ISettingsManager settingsManager) _currentConnection?.Detach(); _currentConnection?.Dispose(); + if (route == "local") + { + return new LocalConnection(); + } + // try to determine what the route is string? uri = null; if (route.StartsWith("http")) From f8208bc35d8d16661554ee13ce720369d6ea72c9 Mon Sep 17 00:00:00 2001 From: Chris Tacke Date: Thu, 14 Mar 2024 11:04:01 -0500 Subject: [PATCH 2/2] move projects to lower common denominator --- Source/v2/Meadow.UsbLib.Core/ILibUsbDevice.cs | 5 ++++- Source/v2/Meadow.UsbLib.Core/Meadow.UsbLib.Core.csproj | 5 ++--- Source/v2/Meadow.UsbLib/LibUsbDevice.cs | 2 ++ Source/v2/Meadow.UsbLib/Meadow.UsbLib.csproj | 6 +++--- Source/v2/Meadow.UsbLibClassic/ClassicLibUsbDevice.cs | 3 +++ Source/v2/Meadow.UsbLibClassic/Meadow.UsbLibClassic.csproj | 6 +++--- 6 files changed, 17 insertions(+), 10 deletions(-) diff --git a/Source/v2/Meadow.UsbLib.Core/ILibUsbDevice.cs b/Source/v2/Meadow.UsbLib.Core/ILibUsbDevice.cs index a477ccf7..02dcb68b 100644 --- a/Source/v2/Meadow.UsbLib.Core/ILibUsbDevice.cs +++ b/Source/v2/Meadow.UsbLib.Core/ILibUsbDevice.cs @@ -1,4 +1,7 @@ -namespace Meadow.LibUsb; +using System; +using System.Collections.Generic; + +namespace Meadow.LibUsb; public interface ILibUsbProvider { diff --git a/Source/v2/Meadow.UsbLib.Core/Meadow.UsbLib.Core.csproj b/Source/v2/Meadow.UsbLib.Core/Meadow.UsbLib.Core.csproj index fa71b7ae..a20a37f9 100644 --- a/Source/v2/Meadow.UsbLib.Core/Meadow.UsbLib.Core.csproj +++ b/Source/v2/Meadow.UsbLib.Core/Meadow.UsbLib.Core.csproj @@ -1,9 +1,8 @@  - net8.0 - enable - enable + netstandard2.0 + 10 diff --git a/Source/v2/Meadow.UsbLib/LibUsbDevice.cs b/Source/v2/Meadow.UsbLib/LibUsbDevice.cs index cb7e0d23..5df4c400 100644 --- a/Source/v2/Meadow.UsbLib/LibUsbDevice.cs +++ b/Source/v2/Meadow.UsbLib/LibUsbDevice.cs @@ -1,4 +1,6 @@ using LibUsbDotNet.LibUsb; +using System.Collections.Generic; +using System.Linq; namespace Meadow.LibUsb; diff --git a/Source/v2/Meadow.UsbLib/Meadow.UsbLib.csproj b/Source/v2/Meadow.UsbLib/Meadow.UsbLib.csproj index fb7614a7..0894bbf6 100644 --- a/Source/v2/Meadow.UsbLib/Meadow.UsbLib.csproj +++ b/Source/v2/Meadow.UsbLib/Meadow.UsbLib.csproj @@ -1,9 +1,9 @@  - net8.0 - enable - enable + net5.0 + 10 + enable diff --git a/Source/v2/Meadow.UsbLibClassic/ClassicLibUsbDevice.cs b/Source/v2/Meadow.UsbLibClassic/ClassicLibUsbDevice.cs index e8cec879..bfc53253 100644 --- a/Source/v2/Meadow.UsbLibClassic/ClassicLibUsbDevice.cs +++ b/Source/v2/Meadow.UsbLibClassic/ClassicLibUsbDevice.cs @@ -1,5 +1,8 @@ using LibUsbDotNet; using LibUsbDotNet.Main; +using System; +using System.Collections.Generic; +using System.Linq; namespace Meadow.LibUsb; diff --git a/Source/v2/Meadow.UsbLibClassic/Meadow.UsbLibClassic.csproj b/Source/v2/Meadow.UsbLibClassic/Meadow.UsbLibClassic.csproj index 48f224af..608d4def 100644 --- a/Source/v2/Meadow.UsbLibClassic/Meadow.UsbLibClassic.csproj +++ b/Source/v2/Meadow.UsbLibClassic/Meadow.UsbLibClassic.csproj @@ -1,9 +1,9 @@  - net8.0 - enable - enable + netstandard2.0 + 10 + enable