From f78ca1e9fd9ed9939c5923851d1cf47609abdceb Mon Sep 17 00:00:00 2001 From: David Sparer Date: Mon, 21 Jan 2019 17:52:18 -0600 Subject: [PATCH] some more deserialization refactoring --- .../Xml/XmlConnectionsDeserializerTests.cs | 43 +++------ .../Serializers/DataTableDeserializerTests.cs | 1 + .../XmlSerializationLifeCycleTests.cs | 17 ++-- .../TestHelpers/ConnectionInfoHelpers.cs | 14 --- mRemoteV1/App/Runtime.cs | 2 +- .../Multiuser/RemoteConnectionsSyncronizer.cs | 2 +- .../Connections/XmlConnectionsLoader.cs | 4 +- .../Config/Import/MRemoteNGXmlImporter.cs | 4 +- .../Xml/XmlConnectionsDeserializer.cs | 91 +++++++++++-------- .../XmlCredentialManagerUpgrader.cs | 13 +-- .../Connection/AbstractConnectionRecord.cs | 5 + mRemoteV1/Connection/ConnectionsService.cs | 19 ++-- mRemoteV1/Connection/IConnectionsService.cs | 2 +- mRemoteV1/Tools/Extensions.cs | 18 +++- .../UI/Forms/CredentialManagerUpgradeForm.cs | 13 +-- .../UI/Forms/OptionsPages/SqlServerPage.cs | 2 +- 16 files changed, 136 insertions(+), 114 deletions(-) diff --git a/mRemoteNGTests/Config/Serializers/ConnectionSerializers/Xml/XmlConnectionsDeserializerTests.cs b/mRemoteNGTests/Config/Serializers/ConnectionSerializers/Xml/XmlConnectionsDeserializerTests.cs index 9b20a0e33..6623bb52f 100644 --- a/mRemoteNGTests/Config/Serializers/ConnectionSerializers/Xml/XmlConnectionsDeserializerTests.cs +++ b/mRemoteNGTests/Config/Serializers/ConnectionSerializers/Xml/XmlConnectionsDeserializerTests.cs @@ -1,11 +1,12 @@ using System.Collections; using System.Collections.Generic; using System.Linq; +using mRemoteNG.Config.Serializers; using mRemoteNG.Config.Serializers.Xml; using mRemoteNG.Connection; using mRemoteNG.Container; using mRemoteNG.Security; -using mRemoteNG.Tree; +using mRemoteNG.Tools; using mRemoteNGTests.Properties; using NUnit.Framework; @@ -14,50 +15,40 @@ namespace mRemoteNGTests.Config.Serializers.ConnectionSerializers.Xml public class XmlConnectionsDeserializerTests { private XmlConnectionsDeserializer _xmlConnectionsDeserializer; - private ConnectionTreeModel _connectionTreeModel; + private SerializationResult _serializationResult; public void Setup(string confCons, string password) { _xmlConnectionsDeserializer = new XmlConnectionsDeserializer(() => password.ConvertToSecureString()); - _connectionTreeModel = _xmlConnectionsDeserializer.Deserialize(confCons); + _serializationResult = _xmlConnectionsDeserializer.Deserialize(confCons); } [TearDown] public void Teardown() { _xmlConnectionsDeserializer = null; - _connectionTreeModel = null; + _serializationResult = null; } [TestCaseSource(typeof(XmlConnectionsDeserializerFixtureData), nameof(XmlConnectionsDeserializerFixtureData.FixtureParams))] - public void DeserializingCreatesRootNode(Datagram testData) + public void RootHasThreeChildren(Datagram testData) { Setup(testData.ConfCons, testData.Password); - Assert.That(_connectionTreeModel.RootNodes, Is.Not.Empty); - } - - [TestCaseSource(typeof(XmlConnectionsDeserializerFixtureData), nameof(XmlConnectionsDeserializerFixtureData.FixtureParams))] - public void RootNodeHasThreeChildren(Datagram testData) - { - Setup(testData.ConfCons, testData.Password); - var connectionRoot = _connectionTreeModel.RootNodes[0]; - Assert.That(connectionRoot.Children.Count, Is.EqualTo(3)); + Assert.That(_serializationResult.ConnectionRecords.Count, Is.EqualTo(3)); } [TestCaseSource(typeof(XmlConnectionsDeserializerFixtureData), nameof(XmlConnectionsDeserializerFixtureData.FixtureParams))] public void RootContainsFolder1(Datagram testData) { Setup(testData.ConfCons, testData.Password); - var connectionRoot = _connectionTreeModel.RootNodes[0]; - Assert.That(ContainsNodeNamed("Folder1", connectionRoot.Children), Is.True); + Assert.That(ContainsNodeNamed("Folder1", _serializationResult.ConnectionRecords), Is.True); } [TestCaseSource(typeof(XmlConnectionsDeserializerFixtureData), nameof(XmlConnectionsDeserializerFixtureData.FixtureParams))] public void Folder1ContainsThreeConnections(Datagram testData) { Setup(testData.ConfCons, testData.Password); - var connectionRoot = _connectionTreeModel.RootNodes[0]; - var folder1 = GetFolderNamed("Folder1", connectionRoot.Children); + var folder1 = GetFolderNamed("Folder1", _serializationResult.ConnectionRecords); var folder1ConnectionCount = folder1?.Children.Count(node => !(node is ContainerInfo)); Assert.That(folder1ConnectionCount, Is.EqualTo(3)); } @@ -66,9 +57,8 @@ namespace mRemoteNGTests.Config.Serializers.ConnectionSerializers.Xml public void Folder2ContainsThreeNodes(Datagram testData) { Setup(testData.ConfCons, testData.Password); - var connectionRoot = _connectionTreeModel.RootNodes[0]; - var folder2 = GetFolderNamed("Folder2", connectionRoot.Children); - var folder1Count = folder2?.Children.Count(); + var folder2 = GetFolderNamed("Folder2", _serializationResult.ConnectionRecords); + var folder1Count = folder2?.Children.Count; Assert.That(folder1Count, Is.EqualTo(3)); } @@ -76,8 +66,7 @@ namespace mRemoteNGTests.Config.Serializers.ConnectionSerializers.Xml public void Folder21HasTwoNodes(Datagram testData) { Setup(testData.ConfCons, testData.Password); - var connectionRoot = _connectionTreeModel.RootNodes[0]; - var folder2 = GetFolderNamed("Folder2", connectionRoot.Children); + var folder2 = GetFolderNamed("Folder2", _serializationResult.ConnectionRecords); var folder21 = GetFolderNamed("Folder2.1", folder2.Children); Assert.That(folder21.Children.Count, Is.EqualTo(2)); } @@ -86,8 +75,7 @@ namespace mRemoteNGTests.Config.Serializers.ConnectionSerializers.Xml public void Folder211HasOneConnection(Datagram testData) { Setup(testData.ConfCons, testData.Password); - var connectionRoot = _connectionTreeModel.RootNodes[0]; - var folder2 = GetFolderNamed("Folder2", connectionRoot.Children); + var folder2 = GetFolderNamed("Folder2", _serializationResult.ConnectionRecords); var folder21 = GetFolderNamed("Folder2.1", folder2.Children); var folder211 = GetFolderNamed("Folder2.1.1", folder21.Children); var connectionCount = folder211.Children.Count(node => !(node is ContainerInfo)); @@ -98,8 +86,7 @@ namespace mRemoteNGTests.Config.Serializers.ConnectionSerializers.Xml public void Folder22InheritsUsername(Datagram testData) { Setup(testData.ConfCons, testData.Password); - var connectionRoot = _connectionTreeModel.RootNodes[0]; - var folder2 = GetFolderNamed("Folder2", connectionRoot.Children); + var folder2 = GetFolderNamed("Folder2", _serializationResult.ConnectionRecords); var folder22 = GetFolderNamed("Folder2.2", folder2.Children); Assert.That(folder22.Inheritance.Username, Is.True); } @@ -108,7 +95,7 @@ namespace mRemoteNGTests.Config.Serializers.ConnectionSerializers.Xml public void ExpandedPropertyGetsDeserialized(Datagram testData) { Setup(testData.ConfCons, testData.Password); - var folder1 = GetFolderNamed("Folder1", _connectionTreeModel.GetRecursiveChildList()); + var folder1 = GetFolderNamed("Folder1", _serializationResult.ConnectionRecords.FlattenConnectionTree()); Assert.That(folder1.IsExpanded, Is.True); } diff --git a/mRemoteNGTests/Config/Serializers/DataTableDeserializerTests.cs b/mRemoteNGTests/Config/Serializers/DataTableDeserializerTests.cs index a01c00279..32f88e1cd 100644 --- a/mRemoteNGTests/Config/Serializers/DataTableDeserializerTests.cs +++ b/mRemoteNGTests/Config/Serializers/DataTableDeserializerTests.cs @@ -5,6 +5,7 @@ using mRemoteNG.Config.Serializers.MsSql; using mRemoteNG.Connection; using mRemoteNG.Security; using mRemoteNG.Security.SymmetricEncryption; +using mRemoteNG.Tools; using mRemoteNG.Tree; using mRemoteNGTests.TestHelpers; using NUnit.Framework; diff --git a/mRemoteNGTests/IntegrationTests/XmlSerializationLifeCycleTests.cs b/mRemoteNGTests/IntegrationTests/XmlSerializationLifeCycleTests.cs index 6975b9750..bb4d33e5a 100644 --- a/mRemoteNGTests/IntegrationTests/XmlSerializationLifeCycleTests.cs +++ b/mRemoteNGTests/IntegrationTests/XmlSerializationLifeCycleTests.cs @@ -10,6 +10,7 @@ using NUnit.Framework; using System; using System.Linq; using System.Text; +using mRemoteNG.Tools; namespace mRemoteNGTests.IntegrationTests @@ -45,7 +46,7 @@ namespace mRemoteNGTests.IntegrationTests { var serializedContent = _serializer.Serialize(_originalModel); var deserializedModel = _deserializer.Deserialize(serializedContent); - var nodeNamesFromDeserializedModel = deserializedModel.GetRecursiveChildList().Select(node => node.Name); + var nodeNamesFromDeserializedModel = deserializedModel.ConnectionRecords.FlattenConnectionTree().Select(node => node.Name); var nodeNamesFromOriginalModel = _originalModel.GetRecursiveChildList().Select(node => node.Name); Assert.That(nodeNamesFromDeserializedModel, Is.EquivalentTo(nodeNamesFromOriginalModel)); } @@ -56,7 +57,7 @@ namespace mRemoteNGTests.IntegrationTests _serializer.UseFullEncryption = true; var serializedContent = _serializer.Serialize(_originalModel); var deserializedModel = _deserializer.Deserialize(serializedContent); - var nodeNamesFromDeserializedModel = deserializedModel.GetRecursiveChildList().Select(node => node.Name); + var nodeNamesFromDeserializedModel = deserializedModel.ConnectionRecords.FlattenConnectionTree().Select(node => node.Name); var nodeNamesFromOriginalModel = _originalModel.GetRecursiveChildList().Select(node => node.Name); Assert.That(nodeNamesFromDeserializedModel, Is.EquivalentTo(nodeNamesFromOriginalModel)); } @@ -67,7 +68,7 @@ namespace mRemoteNGTests.IntegrationTests var originalConnectionInfo = new ConnectionInfo {Name = "con1", Description = "£°úg¶┬ä" }; var serializedContent = _serializer.Serialize(originalConnectionInfo); var deserializedModel = _deserializer.Deserialize(serializedContent); - var deserializedConnectionInfo = deserializedModel.GetRecursiveChildList().First(node => node.Name == originalConnectionInfo.Name); + var deserializedConnectionInfo = deserializedModel.ConnectionRecords.FlattenConnectionTree().First(node => node.Name == originalConnectionInfo.Name); Assert.That(deserializedConnectionInfo.Description, Is.EqualTo(originalConnectionInfo.Description)); } @@ -84,7 +85,7 @@ namespace mRemoteNGTests.IntegrationTests _serializer = new XmlConnectionsSerializer(cryptoProvider, nodeSerializer); var serializedContent = _serializer.Serialize(_originalModel); var deserializedModel = _deserializer.Deserialize(serializedContent); - var nodeNamesFromDeserializedModel = deserializedModel.GetRecursiveChildList().Select(node => node.Name); + var nodeNamesFromDeserializedModel = deserializedModel.ConnectionRecords.FlattenConnectionTree().Select(node => node.Name); var nodeNamesFromOriginalModel = _originalModel.GetRecursiveChildList().Select(node => node.Name); Assert.That(nodeNamesFromDeserializedModel, Is.EquivalentTo(nodeNamesFromOriginalModel)); } @@ -99,7 +100,7 @@ namespace mRemoteNGTests.IntegrationTests serializedContent = serializedContent.Replace(originalConnectionInfo.ConstantID, ""); var deserializedModel = _deserializer.Deserialize(serializedContent); - var deserializedConnectionInfo = deserializedModel.GetRecursiveChildList().First(node => node.Name == originalConnectionInfo.Name); + var deserializedConnectionInfo = deserializedModel.ConnectionRecords.FlattenConnectionTree().First(node => node.Name == originalConnectionInfo.Name); Assert.That(Guid.TryParse(deserializedConnectionInfo.ConstantID, out var guid)); } @@ -111,7 +112,8 @@ namespace mRemoteNGTests.IntegrationTests var serializedContent = _serializer.Serialize(originalConnectionInfo); var deserializedModel = _deserializer.Deserialize(serializedContent); var deserializedConnectionInfo = deserializedModel - .GetRecursiveChildList() + .ConnectionRecords + .FlattenConnectionTree() .First(info => info.GetTreeNodeType() == TreeNodeType.Connection); var sb = new StringBuilder(); @@ -139,7 +141,8 @@ namespace mRemoteNGTests.IntegrationTests var serializedContent = _serializer.Serialize(container); var deserializedModel = _deserializer.Deserialize(serializedContent); var deserializedConnectionInfo = deserializedModel - .GetRecursiveChildList() + .ConnectionRecords + .FlattenConnectionTree() .First(info => info.GetTreeNodeType() == TreeNodeType.Connection); var sb = new StringBuilder(); diff --git a/mRemoteNGTests/TestHelpers/ConnectionInfoHelpers.cs b/mRemoteNGTests/TestHelpers/ConnectionInfoHelpers.cs index 67c399293..abef1b47f 100644 --- a/mRemoteNGTests/TestHelpers/ConnectionInfoHelpers.cs +++ b/mRemoteNGTests/TestHelpers/ConnectionInfoHelpers.cs @@ -128,19 +128,5 @@ namespace mRemoteNGTests.TestHelpers var values = Enum.GetValues(typeof(T)); return (T)values.GetValue(_random.Next(values.Length)); } - - internal static IEnumerable FlattenConnectionTree(this IEnumerable connections) - { - foreach (var item in connections) - { - yield return item; - - if (!(item is ContainerInfo container)) - continue; - - foreach (var child in FlattenConnectionTree(container.Children)) - yield return child; - } - } } } diff --git a/mRemoteV1/App/Runtime.cs b/mRemoteV1/App/Runtime.cs index bcce4f050..6aa27f0b3 100644 --- a/mRemoteV1/App/Runtime.cs +++ b/mRemoteV1/App/Runtime.cs @@ -88,7 +88,7 @@ namespace mRemoteNG.App connectionFileName = ConnectionsService.GetStartupConnectionFileName(); } - ConnectionsService.LoadConnections(Settings.Default.UseSQLServer, false, connectionFileName); + ConnectionsService.LoadConnections(Settings.Default.UseSQLServer, connectionFileName); if (Settings.Default.UseSQLServer) { diff --git a/mRemoteV1/Config/Connections/Multiuser/RemoteConnectionsSyncronizer.cs b/mRemoteV1/Config/Connections/Multiuser/RemoteConnectionsSyncronizer.cs index 1a4c2075c..b336ad2d8 100644 --- a/mRemoteV1/Config/Connections/Multiuser/RemoteConnectionsSyncronizer.cs +++ b/mRemoteV1/Config/Connections/Multiuser/RemoteConnectionsSyncronizer.cs @@ -34,7 +34,7 @@ namespace mRemoteNG.Config.Connections.Multiuser private void Load(object sender, ConnectionsUpdateAvailableEventArgs args) { - Runtime.ConnectionsService.LoadConnections(true, false, ""); + Runtime.ConnectionsService.LoadConnections(true, ""); args.Handled = true; } diff --git a/mRemoteV1/Config/Connections/XmlConnectionsLoader.cs b/mRemoteV1/Config/Connections/XmlConnectionsLoader.cs index 6bbe882a1..2b064737e 100644 --- a/mRemoteV1/Config/Connections/XmlConnectionsLoader.cs +++ b/mRemoteV1/Config/Connections/XmlConnectionsLoader.cs @@ -48,9 +48,9 @@ namespace mRemoteNG.Config.Connections ConnectionDeserializer = new XmlConnectionsDeserializer(PromptForPassword) }; - var connectionTreeModel = deserializer.Deserialize(xmlString); + var serializationResult = deserializer.Deserialize(xmlString); - return new SerializationResult(connectionTreeModel.RootNodes.Cast().ToList(), new ConnectionToCredentialMap()); + return serializationResult; } private Optional PromptForPassword() diff --git a/mRemoteV1/Config/Import/MRemoteNGXmlImporter.cs b/mRemoteV1/Config/Import/MRemoteNGXmlImporter.cs index 13c0fe449..19a62964a 100644 --- a/mRemoteV1/Config/Import/MRemoteNGXmlImporter.cs +++ b/mRemoteV1/Config/Import/MRemoteNGXmlImporter.cs @@ -27,10 +27,10 @@ namespace mRemoteNG.Config.Import var dataProvider = new FileDataProvider(fileName); var xmlString = dataProvider.Load(); var xmlConnectionsDeserializer = new XmlConnectionsDeserializer(); - var connectionTreeModel = xmlConnectionsDeserializer.Deserialize(xmlString, true); + var serializationResult = xmlConnectionsDeserializer.Deserialize(xmlString, true); var rootImportContainer = new ContainerInfo { Name = Path.GetFileNameWithoutExtension(fileName) }; - rootImportContainer.AddChildRange(connectionTreeModel.RootNodes.First().Children.ToArray()); + rootImportContainer.AddChildRange(serializationResult.ConnectionRecords); destinationContainer.AddChild(rootImportContainer); } } diff --git a/mRemoteV1/Config/Serializers/ConnectionSerializers/Xml/XmlConnectionsDeserializer.cs b/mRemoteV1/Config/Serializers/ConnectionSerializers/Xml/XmlConnectionsDeserializer.cs index 0ec524941..a9b7cebd5 100644 --- a/mRemoteV1/Config/Serializers/ConnectionSerializers/Xml/XmlConnectionsDeserializer.cs +++ b/mRemoteV1/Config/Serializers/ConnectionSerializers/Xml/XmlConnectionsDeserializer.cs @@ -15,21 +15,23 @@ using mRemoteNG.Tree.Root; using mRemoteNG.UI.Forms; using mRemoteNG.UI.TaskDialog; using System; +using System.Collections.Generic; using System.Globalization; using System.Security; using System.Windows.Forms; using System.Xml; +using mRemoteNG.Credential; namespace mRemoteNG.Config.Serializers.Xml { - public class XmlConnectionsDeserializer : IDeserializer + public class XmlConnectionsDeserializer { private XmlDocument _xmlDocument; private double _confVersion; private XmlConnectionsDecryptor _decryptor; private string ConnectionFileName = ""; private const double MaxSupportedConfVersion = 2.7; - private readonly RootNodeInfo _rootNodeInfo = new RootNodeInfo(RootNodeType.Connection); + private readonly CredentialDomainUserPasswordComparer _credentialComparer = new CredentialDomainUserPasswordComparer(); public Func> AuthenticationRequestor { get; set; } @@ -38,12 +40,12 @@ namespace mRemoteNG.Config.Serializers.Xml AuthenticationRequestor = authenticationRequestor; } - public ConnectionTreeModel Deserialize(string xml) + public SerializationResult Deserialize(string xml) { return Deserialize(xml, false); } - public ConnectionTreeModel Deserialize(string xml, bool import) + public SerializationResult Deserialize(string xml, bool import) { try { @@ -51,16 +53,13 @@ namespace mRemoteNG.Config.Serializers.Xml ValidateConnectionFileVersion(); var rootXmlElement = _xmlDocument.DocumentElement; - InitializeRootNode(rootXmlElement); - CreateDecryptor(_rootNodeInfo, rootXmlElement); - var connectionTreeModel = new ConnectionTreeModel(); - connectionTreeModel.AddRootNode(_rootNodeInfo); + var rootNodeInfo = InitializeRootNode(rootXmlElement); + _decryptor = CreateDecryptor(rootNodeInfo, rootXmlElement); - if (_confVersion > 1.3) { var protectedString = _xmlDocument.DocumentElement?.Attributes["Protected"].Value; - if (!_decryptor.ConnectionsFileIsAuthentic(protectedString, _rootNodeInfo.PasswordString.ConvertToSecureString())) + if (!_decryptor.ConnectionsFileIsAuthentic(protectedString, rootNodeInfo.PasswordString.ConvertToSecureString())) { return null; } @@ -76,12 +75,11 @@ namespace mRemoteNG.Config.Serializers.Xml } } - AddNodesFromXmlRecursive(_xmlDocument.DocumentElement, _rootNodeInfo); + var credentialMap = new ConnectionToCredentialMap(); + var rootNodes = AddNodesFromXmlRecursive(_xmlDocument.DocumentElement, credentialMap); + var serializationResult = new SerializationResult(rootNodes, credentialMap); - if (!import) - Runtime.ConnectionsService.IsConnectionsFileLoaded = true; - - return connectionTreeModel; + return serializationResult; } catch (Exception ex) { @@ -93,8 +91,8 @@ namespace mRemoteNG.Config.Serializers.Xml private void LoadXmlConnectionData(string connections) { - CreateDecryptor(new RootNodeInfo(RootNodeType.Connection)); - connections = _decryptor.LegacyFullFileDecrypt(connections); + var legacyDecryptor = CreateDecryptor(new RootNodeInfo(RootNodeType.Connection)); + connections = legacyDecryptor.LegacyFullFileDecrypt(connections); _xmlDocument = new XmlDocument(); if (connections != "") _xmlDocument.LoadXml(connections); @@ -130,13 +128,16 @@ namespace mRemoteNG.Config.Serializers.Xml ); } - private void InitializeRootNode(XmlElement connectionsRootElement) + private RootNodeInfo InitializeRootNode(XmlElement connectionsRootElement) { var rootNodeName = connectionsRootElement?.Attributes["Name"].Value.Trim(); - _rootNodeInfo.Name = rootNodeName; + return new RootNodeInfo(RootNodeType.Connection) + { + Name = rootNodeName + }; } - private void CreateDecryptor(RootNodeInfo rootNodeInfo, XmlElement connectionsRootElement = null) + private XmlConnectionsDecryptor CreateDecryptor(RootNodeInfo rootNodeInfo, XmlElement connectionsRootElement = null) { if (_confVersion >= 2.6) { @@ -144,23 +145,27 @@ namespace mRemoteNG.Config.Serializers.Xml var mode = connectionsRootElement.GetAttributeAsEnum("BlockCipherMode"); var keyDerivationIterations = connectionsRootElement.GetAttributeAsInt("KdfIterations"); - _decryptor = new XmlConnectionsDecryptor(engine, mode, rootNodeInfo) + return new XmlConnectionsDecryptor(engine, mode, rootNodeInfo) { AuthenticationRequestor = AuthenticationRequestor, KeyDerivationIterations = keyDerivationIterations }; } - else + + return new XmlConnectionsDecryptor(rootNodeInfo) { - _decryptor = new XmlConnectionsDecryptor(_rootNodeInfo) { AuthenticationRequestor = AuthenticationRequestor }; - } + AuthenticationRequestor = AuthenticationRequestor + }; } - private void AddNodesFromXmlRecursive(XmlNode parentXmlNode, ContainerInfo parentContainer) + private List AddNodesFromXmlRecursive(XmlNode parentXmlNode, ConnectionToCredentialMap credentialMap) { try { - if (!parentXmlNode.HasChildNodes) return; + if (!parentXmlNode.HasChildNodes) + return new List(); + + var children = new List(); foreach (XmlNode xmlNode in parentXmlNode.ChildNodes) { var nodeType = xmlNode.GetAttributeAsEnum("Type", TreeNodeType.Connection); @@ -169,24 +174,27 @@ namespace mRemoteNG.Config.Serializers.Xml switch (nodeType) { case TreeNodeType.Connection: - var connectionInfo = GetConnectionInfoFromXml(xmlNode); - parentContainer.AddChild(connectionInfo); + var connectionInfo = GetConnectionInfoFromXml(xmlNode, credentialMap); + children.Add(connectionInfo); break; case TreeNodeType.Container: var containerInfo = new ContainerInfo(); if (_confVersion >= 0.9) - containerInfo.CopyFrom(GetConnectionInfoFromXml(xmlNode)); + containerInfo.CopyFrom(GetConnectionInfoFromXml(xmlNode, credentialMap)); if (_confVersion >= 0.8) { containerInfo.IsExpanded = xmlNode.GetAttributeAsBool("Expanded"); } - parentContainer.AddChild(containerInfo); - AddNodesFromXmlRecursive(xmlNode, containerInfo); + var subChildren = AddNodesFromXmlRecursive(xmlNode, credentialMap); + subChildren.ForEach(info => containerInfo.AddChild(info)); + children.Add(containerInfo); break; } } + + return children; } catch (Exception ex) { @@ -195,7 +203,7 @@ namespace mRemoteNG.Config.Serializers.Xml } } - private ConnectionInfo GetConnectionInfoFromXml(XmlNode xmlnode) + private ConnectionInfo GetConnectionInfoFromXml(XmlNode xmlnode, ConnectionToCredentialMap credentialMap) { if (xmlnode?.Attributes == null) return null; @@ -225,10 +233,19 @@ namespace mRemoteNG.Config.Serializers.Xml if (_confVersion <= 2.6) // 0.2 - 2.6 { - // TODO: harvest - connectionInfo.Username = xmlnode.GetAttributeAsString("Username"); - connectionInfo.Password = _decryptor.Decrypt(xmlnode.GetAttributeAsString("Password")); - connectionInfo.Domain = xmlnode.GetAttributeAsString("Domain"); + var username = xmlnode.GetAttributeAsString("Username"); + var domain = xmlnode.GetAttributeAsString("Domain"); + + var cred = new CredentialRecord + { + Title = domain.Length > 0 ? $"{domain}\\{username}" : username, + Username = username, + Domain = domain, + Password = _decryptor.Decrypt(xmlnode.GetAttributeAsString("Password")).ConvertToSecureString() + }; + + if (!_credentialComparer.Equals(cred, new NullCredentialRecord())) + connectionInfo.CredentialRecordId = cred.Id; } } @@ -502,7 +519,7 @@ namespace mRemoteNG.Config.Serializers.Xml connectionInfo.RedirectClipboard = xmlnode.GetAttributeAsBool("RedirectClipboard"); connectionInfo.Inheritance.RedirectClipboard = xmlnode.GetAttributeAsBool("InheritRedirectClipboard"); - connectionInfo.CredentialRecordId = Guid.TryParse(xmlnode.Attributes["CredentialId"]?.Value, out var credId) + connectionInfo.CredentialRecordId = Guid.TryParse(xmlnode.Attributes?["CredentialId"]?.Value, out var credId) ? credId : Optional.Empty; diff --git a/mRemoteV1/Config/Serializers/Versioning/XmlCredentialManagerUpgrader.cs b/mRemoteV1/Config/Serializers/Versioning/XmlCredentialManagerUpgrader.cs index b2bf975a9..6898eb564 100644 --- a/mRemoteV1/Config/Serializers/Versioning/XmlCredentialManagerUpgrader.cs +++ b/mRemoteV1/Config/Serializers/Versioning/XmlCredentialManagerUpgrader.cs @@ -11,30 +11,31 @@ using System; using System.Collections.Generic; using System.Linq; using System.Xml.Linq; +using mRemoteNG.Config.Serializers.Xml; namespace mRemoteNG.Config.Serializers.Versioning { public class XmlCredentialManagerUpgrader { - private readonly IDeserializer _deserializer; + private readonly XmlConnectionsDeserializer _deserializer; - public XmlCredentialManagerUpgrader(IDeserializer decoratedDeserializer) + public XmlCredentialManagerUpgrader(XmlConnectionsDeserializer decoratedDeserializer) { _deserializer = decoratedDeserializer.ThrowIfNull(nameof(decoratedDeserializer)); } - public ConnectionTreeModel Deserialize(string serializedData, ConnectionToCredentialMap upgradeMap) + public SerializationResult Deserialize(string serializedData, ConnectionToCredentialMap upgradeMap) { var serializedDataAsXDoc = EnsureConnectionXmlElementsHaveIds(serializedData); var serializedDataWithIds = $"{serializedDataAsXDoc.Declaration}{serializedDataAsXDoc}"; - var connectionTreeModel = _deserializer.Deserialize(serializedDataWithIds); + var serializationResult = _deserializer.Deserialize(serializedDataWithIds); if (upgradeMap != null) - ApplyCredentialMapping(upgradeMap, connectionTreeModel.GetRecursiveChildList()); + ApplyCredentialMapping(upgradeMap, serializationResult.ConnectionRecords.FlattenConnectionTree()); - return connectionTreeModel; + return serializationResult; } private XDocument EnsureConnectionXmlElementsHaveIds(string serializedData) diff --git a/mRemoteV1/Connection/AbstractConnectionRecord.cs b/mRemoteV1/Connection/AbstractConnectionRecord.cs index 3e5a30684..460f0e6c1 100644 --- a/mRemoteV1/Connection/AbstractConnectionRecord.cs +++ b/mRemoteV1/Connection/AbstractConnectionRecord.cs @@ -348,6 +348,7 @@ namespace mRemoteNG.Connection [LocalizedAttributes.LocalizedCategory("strCategoryGateway", 4), LocalizedAttributes.LocalizedDisplayName("strPropertyNameRDGatewayUsername"), LocalizedAttributes.LocalizedDescription("strPropertyDescriptionRDGatewayUsername")] + [Obsolete] public string RDGatewayUsername { get => GetPropertyValue("RDGatewayUsername", _rdGatewayUsername).Trim(); @@ -358,6 +359,7 @@ namespace mRemoteNG.Connection LocalizedAttributes.LocalizedDisplayName("strPropertyNameRDGatewayPassword"), LocalizedAttributes.LocalizedDescription("strPropertyNameRDGatewayPassword"), PasswordPropertyText(true)] + [Obsolete] public string RDGatewayPassword { get => GetPropertyValue("RDGatewayPassword", _rdGatewayPassword); @@ -367,6 +369,7 @@ namespace mRemoteNG.Connection [LocalizedAttributes.LocalizedCategory("strCategoryGateway", 4), LocalizedAttributes.LocalizedDisplayName("strPropertyNameRDGatewayDomain"), LocalizedAttributes.LocalizedDescription("strPropertyDescriptionRDGatewayDomain")] + [Obsolete] public string RDGatewayDomain { get => GetPropertyValue("RDGatewayDomain", _rdGatewayDomain).Trim(); @@ -650,6 +653,7 @@ namespace mRemoteNG.Connection Browsable(false), LocalizedAttributes.LocalizedDisplayName("strPropertyNameVNCProxyUsername"), LocalizedAttributes.LocalizedDescription("strPropertyDescriptionVNCProxyUsername")] + [Obsolete] public string VNCProxyUsername { get => GetPropertyValue("VNCProxyUsername", _vncProxyUsername); @@ -661,6 +665,7 @@ namespace mRemoteNG.Connection LocalizedAttributes.LocalizedDisplayName("strPropertyNameVNCProxyPassword"), LocalizedAttributes.LocalizedDescription("strPropertyDescriptionVNCProxyPassword"), PasswordPropertyText(true)] + [Obsolete] public string VNCProxyPassword { get => GetPropertyValue("VNCProxyPassword", _vncProxyPassword); diff --git a/mRemoteV1/Connection/ConnectionsService.cs b/mRemoteV1/Connection/ConnectionsService.cs index 4e2bb1d2f..6a0cbc79a 100644 --- a/mRemoteV1/Connection/ConnectionsService.cs +++ b/mRemoteV1/Connection/ConnectionsService.cs @@ -49,6 +49,8 @@ namespace mRemoteNG.Connection var path = SettingsFileInfo.SettingsPath; _localConnectionPropertiesDataProvider = new FileDataProvider(Path.Combine(path, "LocalConnectionProperties.xml")); _localConnectionPropertiesSerializer = new LocalConnectionPropertiesXmlSerializer(); + + _puttySessionsManager.RootPuttySessionsNodes.ForEach(node => ConnectionTreeModel.AddRootNode(node)); } public void NewConnectionsFile(string filename) @@ -56,10 +58,9 @@ namespace mRemoteNG.Connection try { filename.ThrowIfNullOrEmpty(nameof(filename)); - var newConnectionsModel = new ConnectionTreeModel(); - newConnectionsModel.AddRootNode(new RootNodeInfo(RootNodeType.Connection)); - SaveConnections(newConnectionsModel, false, new SaveFilter(), filename, true); - LoadConnections(false, false, filename); + ConnectionTreeModel.AddRootNode(new RootNodeInfo(RootNodeType.Connection)); + SaveConnections(ConnectionTreeModel, false, new SaveFilter(), filename, true); + LoadConnections(false, filename); } catch (Exception ex) { @@ -112,7 +113,7 @@ namespace mRemoteNG.Connection /// /// /// - public void LoadConnections(bool useDatabase, bool import, string connectionFileName) + public void LoadConnections(bool useDatabase, string connectionFileName) { var oldIsUsingDatabaseValue = UsingDatabase; @@ -135,8 +136,12 @@ namespace mRemoteNG.Connection ConnectionFileName = connectionFileName; UsingDatabase = useDatabase; - ConnectionTreeModel.RemoveRootNode(ConnectionTreeModel.RootNodes.First()); - ConnectionTreeModel.AddRootNode(serializationResult.ConnectionRecords.OfType().First()); + if (ConnectionTreeModel.RootNodes.Any()) + ConnectionTreeModel.RemoveRootNode(ConnectionTreeModel.RootNodes.First()); + + var rootNode = new RootNodeInfo(RootNodeType.Connection); + rootNode.AddChildRange(serializationResult.ConnectionRecords); + ConnectionTreeModel.AddRootNode(rootNode); UpdateCustomConsPathSetting(connectionFileName); RaiseConnectionsLoadedEvent(new List(), new List(), oldIsUsingDatabaseValue, useDatabase, connectionFileName); diff --git a/mRemoteV1/Connection/IConnectionsService.cs b/mRemoteV1/Connection/IConnectionsService.cs index b89a3622e..72c4a8511 100644 --- a/mRemoteV1/Connection/IConnectionsService.cs +++ b/mRemoteV1/Connection/IConnectionsService.cs @@ -19,7 +19,7 @@ namespace mRemoteNG.Connection /// /// /// - void LoadConnections(bool useDatabase, bool import, string connectionFileName); + void LoadConnections(bool useDatabase, string connectionFileName); /// /// When turned on, calls to or diff --git a/mRemoteV1/Tools/Extensions.cs b/mRemoteV1/Tools/Extensions.cs index d1e1d5a06..9d65bd24a 100644 --- a/mRemoteV1/Tools/Extensions.cs +++ b/mRemoteV1/Tools/Extensions.cs @@ -2,6 +2,8 @@ using System; using System.Collections.Generic; using System.Linq; +using mRemoteNG.Connection; +using mRemoteNG.Container; namespace mRemoteNG.Tools { @@ -70,5 +72,19 @@ namespace mRemoteNG.Tools return collection; } - } + + public static IEnumerable FlattenConnectionTree(this IEnumerable connections) + { + foreach (var item in connections) + { + yield return item; + + if (!(item is ContainerInfo container)) + continue; + + foreach (var child in FlattenConnectionTree(container.Children)) + yield return child; + } + } + } } diff --git a/mRemoteV1/UI/Forms/CredentialManagerUpgradeForm.cs b/mRemoteV1/UI/Forms/CredentialManagerUpgradeForm.cs index 6d8dffa3c..599213f39 100644 --- a/mRemoteV1/UI/Forms/CredentialManagerUpgradeForm.cs +++ b/mRemoteV1/UI/Forms/CredentialManagerUpgradeForm.cs @@ -10,6 +10,7 @@ using mRemoteNG.Config.Connections; using mRemoteNG.Config.DataProviders; using mRemoteNG.Config.Serializers; using mRemoteNG.Config.Serializers.Versioning; +using mRemoteNG.Config.Serializers.Xml; using mRemoteNG.Connection; using mRemoteNG.Credential; using mRemoteNG.Credential.Repositories; @@ -19,7 +20,7 @@ using mRemoteNG.Tree; namespace mRemoteNG.UI.Forms { - public partial class CredentialManagerUpgradeForm : Form, IDeserializer + public partial class CredentialManagerUpgradeForm : Form { private string _connectionFilePath; private string _newCredentialRepoPath; @@ -28,7 +29,7 @@ namespace mRemoteNG.UI.Forms private ConnectionToCredentialMap _credentialMap; private readonly ThemeManager _themeManager = ThemeManager.getInstance(); - public IDeserializer ConnectionDeserializer { get; set; } + public XmlConnectionsDeserializer ConnectionDeserializer { get; set; } public ConnectionsService ConnectionsService { get; set; } public CredentialService CredentialService { get; set; } @@ -73,7 +74,7 @@ namespace mRemoteNG.UI.Forms } } - public ConnectionTreeModel Deserialize(string serializedData) + public SerializationResult Deserialize(string serializedData) { _xdoc = XDocument.Parse(serializedData); if (!XmlCredentialManagerUpgrader.CredentialManagerUpgradeNeeded(_xdoc)) @@ -85,15 +86,15 @@ namespace mRemoteNG.UI.Forms var result = ShowDialog(); if (result != DialogResult.OK) - return new ConnectionTreeModel(); + return new SerializationResult(new List(), new ConnectionToCredentialMap()); var newRepoPassword = newRepositoryPasswordEntry.SecureString; SaveCredentialsToNewRepository(_credentialMap.DistinctCredentialRecords, newRepoPassword, _newCredentialRepoPath); - var connectionTreeModel = _upgradingDeserializer.Deserialize(serializedData, _credentialMap); + var serializationResult = _upgradingDeserializer.Deserialize(serializedData, _credentialMap); ConnectionsService.ConnectionsLoaded += ConnectionsServiceOnConnectionsLoaded; - return connectionTreeModel; + return serializationResult; } /// diff --git a/mRemoteV1/UI/Forms/OptionsPages/SqlServerPage.cs b/mRemoteV1/UI/Forms/OptionsPages/SqlServerPage.cs index 7f15ca529..f737b85b7 100644 --- a/mRemoteV1/UI/Forms/OptionsPages/SqlServerPage.cs +++ b/mRemoteV1/UI/Forms/OptionsPages/SqlServerPage.cs @@ -81,7 +81,7 @@ namespace mRemoteNG.UI.Forms.OptionsPages { Runtime.ConnectionsService.RemoteConnectionsSyncronizer?.Dispose(); Runtime.ConnectionsService.RemoteConnectionsSyncronizer = new RemoteConnectionsSyncronizer(new SqlConnectionsUpdateChecker()); - Runtime.ConnectionsService.LoadConnections(true, false, ""); + Runtime.ConnectionsService.LoadConnections(true, ""); } private void DisableSql()