From e0486bec7df8daa2c519d83cff7b53fe2f026228 Mon Sep 17 00:00:00 2001 From: David Sparer Date: Fri, 28 Dec 2018 11:45:03 -0600 Subject: [PATCH] simplified some of the credential management classes --- .../CredentialServiceFacadeTests.cs | 17 ++- mRemoteV1/App/Export.cs | 2 +- .../App/Initialization/CredsAndConsSetup.cs | 4 +- mRemoteV1/App/Runtime.cs | 4 +- .../Config/Connections/CsvConnectionsSaver.cs | 2 +- .../Connections/XmlConnectionsLoader.cs | 4 +- .../Config/CredentialRepositoryListLoader.cs | 31 ----- .../CredentialRepositoryListPersistor.cs | 39 +++++++ .../Config/CredentialRepositoryListSaver.cs | 28 ----- .../CredentialRepositoryListDeserializer.cs | 45 ++++--- .../XmlCredentialManagerUpgrader.cs | 32 +++-- .../Connection/AbstractConnectionRecord.cs | 2 +- mRemoteV1/Connection/ConnectionsService.cs | 4 +- .../CredentialRecordTypeConverter.cs | 2 +- mRemoteV1/Credential/CredentialService.cs | 110 ++++++++++++++++++ .../Credential/CredentialServiceFacade.cs | 83 ------------- .../Credential/CredentialServiceFactory.cs | 25 ++-- ...tialRepositoryTypeNotSupportedException.cs | 21 ++++ .../ICredentialRepositoryFactory.cs | 20 ++++ .../Repositories/XmlCredentialRepository.cs | 42 +++++-- .../XmlCredentialRepositoryFactory.cs | 53 ++++----- .../FullyObservableCollection.cs | 4 +- mRemoteV1/Tools/Optional.cs | 3 +- .../Adapters/CredentialRecordListAdaptor.cs | 4 +- mRemoteV1/UI/Controls/ISelectionTarget.cs | 3 + .../CredentialEditorPage.cs | 2 + .../CredentialRepositoriesPage.cs | 52 ++++++--- .../CredentialRepositoryPageEditorFactory.cs | 8 +- .../XmlCredentialRepositoryEditorPage.cs | 40 +++---- .../KeePassRepositorySelector.cs | 8 ++ .../XmlCredentialRepositorySelector.cs | 26 +++++ .../CredentialRepositoryTypeSelectionPage.cs | 31 ++--- .../UI/Forms/CredentialManagerUpgradeForm.cs | 2 +- mRemoteV1/UI/Forms/frmMain.Designer.cs | 1 - mRemoteV1/UI/Forms/frmMain.cs | 21 +++- mRemoteV1/UI/Menu/ToolsMenu.cs | 6 +- mRemoteV1/mRemoteV1.csproj | 7 +- 37 files changed, 459 insertions(+), 329 deletions(-) delete mode 100644 mRemoteV1/Config/CredentialRepositoryListLoader.cs create mode 100644 mRemoteV1/Config/CredentialRepositoryListPersistor.cs delete mode 100644 mRemoteV1/Config/CredentialRepositoryListSaver.cs create mode 100644 mRemoteV1/Credential/CredentialService.cs delete mode 100644 mRemoteV1/Credential/CredentialServiceFacade.cs create mode 100644 mRemoteV1/Credential/Repositories/CredentialRepositoryTypeNotSupportedException.cs create mode 100644 mRemoteV1/Credential/Repositories/ICredentialRepositoryFactory.cs diff --git a/mRemoteNGTests/Credential/CredentialServiceFacadeTests.cs b/mRemoteNGTests/Credential/CredentialServiceFacadeTests.cs index 3557f6e57..74caf8c3a 100644 --- a/mRemoteNGTests/Credential/CredentialServiceFacadeTests.cs +++ b/mRemoteNGTests/Credential/CredentialServiceFacadeTests.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using mRemoteNG.Config; using mRemoteNG.Credential; +using mRemoteNG.Credential.Repositories; using NSubstitute; using NUnit.Framework; // ReSharper disable ObjectCreationAsStatement @@ -10,7 +11,7 @@ namespace mRemoteNGTests.Credential { public class CredentialServiceFacadeTests { - private CredentialServiceFacade _credentialService; + private CredentialService _credentialService; private ICredentialRepositoryList _credentialRepositoryList; private ILoader> _loader; private ISaver> _saver; @@ -21,25 +22,31 @@ namespace mRemoteNGTests.Credential _credentialRepositoryList = Substitute.For(); _loader = Substitute.For>>(); _saver = Substitute.For>>(); - _credentialService = new CredentialServiceFacade(_credentialRepositoryList, _loader, _saver); + _credentialService = new CredentialService(_credentialRepositoryList, new List(), _loader, _saver); } [Test] public void CantProvideNullRepoListToCtor() { - Assert.Throws(() => new CredentialServiceFacade(null, _loader, _saver)); + Assert.Throws(() => new CredentialService(null, new List(), _loader, _saver)); + } + + [Test] + public void CantProvideNullFactoryListToCtor() + { + Assert.Throws(() => new CredentialService(_credentialRepositoryList, null, _loader, _saver)); } [Test] public void CantProvideNullRepoLoaderToCtor() { - Assert.Throws(() => new CredentialServiceFacade(_credentialRepositoryList, null, _saver)); + Assert.Throws(() => new CredentialService(_credentialRepositoryList, new List(), null, _saver)); } [Test] public void CantProvideNullRepoSaverToCtor() { - Assert.Throws(() => new CredentialServiceFacade(_credentialRepositoryList, _loader, null)); + Assert.Throws(() => new CredentialService(_credentialRepositoryList, new List(), _loader, null)); } [Test] diff --git a/mRemoteV1/App/Export.cs b/mRemoteV1/App/Export.cs index a6feb2295..d8f6230b3 100644 --- a/mRemoteV1/App/Export.cs +++ b/mRemoteV1/App/Export.cs @@ -86,7 +86,7 @@ namespace mRemoteNG.App serializer = new XmlConnectionsSerializer(cryptographyProvider, connectionNodeSerializer); break; case SaveFormat.mRCSV: - serializer = new CsvConnectionsSerializerMremotengFormat(saveFilter, Runtime.CredentialProviderCatalog); + serializer = new CsvConnectionsSerializerMremotengFormat(saveFilter, Runtime.CredentialService.RepositoryList); break; default: throw new ArgumentOutOfRangeException(nameof(saveFormat), saveFormat, null); diff --git a/mRemoteV1/App/Initialization/CredsAndConsSetup.cs b/mRemoteV1/App/Initialization/CredsAndConsSetup.cs index 524f83f04..a281e183d 100644 --- a/mRemoteV1/App/Initialization/CredsAndConsSetup.cs +++ b/mRemoteV1/App/Initialization/CredsAndConsSetup.cs @@ -9,7 +9,7 @@ namespace mRemoteNG.App.Initialization { public class CredsAndConsSetup { - public void LoadCredsAndCons(ConnectionsService connectionsService, CredentialServiceFacade credentialService) + public void LoadCredsAndCons(ConnectionsService connectionsService, CredentialService credentialService) { new SaveConnectionsOnEdit(connectionsService); @@ -21,7 +21,7 @@ namespace mRemoteNG.App.Initialization Runtime.LoadConnections(); } - private void LoadDefaultConnectionCredentials(CredentialServiceFacade credentialService) + private void LoadDefaultConnectionCredentials(CredentialService credentialService) { var defaultCredId = Settings.Default.ConDefaultCredentialRecord; var matchedCredentials = credentialService diff --git a/mRemoteV1/App/Runtime.cs b/mRemoteV1/App/Runtime.cs index b732b8688..117e4f5bd 100644 --- a/mRemoteV1/App/Runtime.cs +++ b/mRemoteV1/App/Runtime.cs @@ -2,7 +2,6 @@ using mRemoteNG.App.Info; using mRemoteNG.Config.Putty; using mRemoteNG.Connection; using mRemoteNG.Credential; -using mRemoteNG.Credential.Repositories; using mRemoteNG.Messages; using mRemoteNG.Security; using mRemoteNG.Tools; @@ -42,8 +41,7 @@ namespace mRemoteNG.App public static NotificationAreaIcon NotificationAreaIcon { get; set; } public static ExternalToolsService ExternalToolsService { get; } = new ExternalToolsService(); public static SecureString EncryptionKey { get; set; } = new RootNodeInfo(RootNodeType.Connection).PasswordString.ConvertToSecureString(); - public static ICredentialRepositoryList CredentialProviderCatalog { get; } = new CredentialRepositoryList(); - public static CredentialServiceFacade CredentialService { get; } = new CredentialServiceFactory().Build(); + public static CredentialService CredentialService { get; } = new CredentialServiceFactory().Build(); public static ConnectionsService ConnectionsService { get; } = new ConnectionsService(PuttySessionsManager.Instance, CredentialService); #region Connections Loading/Saving diff --git a/mRemoteV1/Config/Connections/CsvConnectionsSaver.cs b/mRemoteV1/Config/Connections/CsvConnectionsSaver.cs index 598cea19f..06de8404f 100644 --- a/mRemoteV1/Config/Connections/CsvConnectionsSaver.cs +++ b/mRemoteV1/Config/Connections/CsvConnectionsSaver.cs @@ -26,7 +26,7 @@ namespace mRemoteNG.Config.Connections public void Save(ConnectionTreeModel connectionTreeModel, string propertyNameTrigger = "") { - var csvConnectionsSerializer = new CsvConnectionsSerializerMremotengFormat(_saveFilter, Runtime.CredentialProviderCatalog); + var csvConnectionsSerializer = new CsvConnectionsSerializerMremotengFormat(_saveFilter, Runtime.CredentialService.RepositoryList); var dataProvider = new FileDataProvider(_connectionFileName); var csvContent = csvConnectionsSerializer.Serialize(connectionTreeModel); dataProvider.Save(csvContent); diff --git a/mRemoteV1/Config/Connections/XmlConnectionsLoader.cs b/mRemoteV1/Config/Connections/XmlConnectionsLoader.cs index b5f9a91c7..771291071 100644 --- a/mRemoteV1/Config/Connections/XmlConnectionsLoader.cs +++ b/mRemoteV1/Config/Connections/XmlConnectionsLoader.cs @@ -18,9 +18,9 @@ namespace mRemoteNG.Config.Connections private readonly string _credentialFilePath = Path.Combine(CredentialsFileInfo.CredentialsPath, CredentialsFileInfo.CredentialsFile); private readonly string _connectionFilePath; private readonly ConnectionsService _connectionsService; - private readonly CredentialServiceFacade _credentialService; + private readonly CredentialService _credentialService; - public XmlConnectionsLoader(string connectionFilePath, CredentialServiceFacade credentialService, ConnectionsService connectionsService) + public XmlConnectionsLoader(string connectionFilePath, CredentialService credentialService, ConnectionsService connectionsService) { if (string.IsNullOrEmpty(connectionFilePath)) throw new ArgumentException($"{nameof(connectionFilePath)} cannot be null or empty"); diff --git a/mRemoteV1/Config/CredentialRepositoryListLoader.cs b/mRemoteV1/Config/CredentialRepositoryListLoader.cs deleted file mode 100644 index cd9088f03..000000000 --- a/mRemoteV1/Config/CredentialRepositoryListLoader.cs +++ /dev/null @@ -1,31 +0,0 @@ -using System; -using System.Collections.Generic; -using mRemoteNG.Config.DataProviders; -using mRemoteNG.Config.Serializers.CredentialProviderSerializer; -using mRemoteNG.Credential; - -namespace mRemoteNG.Config -{ - public class CredentialRepositoryListLoader : ILoader> - { - private readonly IDataProvider _dataProvider; - private readonly CredentialRepositoryListDeserializer _deserializer; - - public CredentialRepositoryListLoader(IDataProvider dataProvider, CredentialRepositoryListDeserializer deserializer) - { - if (dataProvider == null) - throw new ArgumentNullException(nameof(dataProvider)); - if (deserializer == null) - throw new ArgumentNullException(nameof(deserializer)); - - _dataProvider = dataProvider; - _deserializer = deserializer; - } - - public IEnumerable Load() - { - var data = _dataProvider.Load(); - return _deserializer.Deserialize(data); - } - } -} \ No newline at end of file diff --git a/mRemoteV1/Config/CredentialRepositoryListPersistor.cs b/mRemoteV1/Config/CredentialRepositoryListPersistor.cs new file mode 100644 index 000000000..805e6ca20 --- /dev/null +++ b/mRemoteV1/Config/CredentialRepositoryListPersistor.cs @@ -0,0 +1,39 @@ +using System.Collections.Generic; +using mRemoteNG.Config.DataProviders; +using mRemoteNG.Config.Serializers.CredentialProviderSerializer; +using mRemoteNG.Credential; +using mRemoteNG.Credential.Repositories; +using mRemoteNG.Tools; + +namespace mRemoteNG.Config +{ + public class CredentialRepositoryListPersistor : ISaver>, ILoader> + { + private readonly IReadOnlyCollection _repositoryFactories; + private readonly IDataProvider _dataProvider; + private readonly CredentialRepositoryListDeserializer _deserializer; + private readonly CredentialRepositoryListSerializer _serializer; + + public CredentialRepositoryListPersistor( + IDataProvider dataProvider, + IReadOnlyCollection repositoryFactories) + { + _repositoryFactories = repositoryFactories.ThrowIfNull(nameof(repositoryFactories)); + _dataProvider = dataProvider.ThrowIfNull(nameof(dataProvider)); + _deserializer = new CredentialRepositoryListDeserializer(); + _serializer = new CredentialRepositoryListSerializer(); + } + + public IEnumerable Load() + { + var data = _dataProvider.Load(); + return _deserializer.Deserialize(data, _repositoryFactories); + } + + public void Save(IEnumerable repositories, string propertyNameTrigger = "") + { + var data = _serializer.Serialize(repositories); + _dataProvider.Save(data); + } + } +} diff --git a/mRemoteV1/Config/CredentialRepositoryListSaver.cs b/mRemoteV1/Config/CredentialRepositoryListSaver.cs deleted file mode 100644 index 6200eb729..000000000 --- a/mRemoteV1/Config/CredentialRepositoryListSaver.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System; -using System.Collections.Generic; -using mRemoteNG.Config.DataProviders; -using mRemoteNG.Config.Serializers.CredentialProviderSerializer; -using mRemoteNG.Credential; - -namespace mRemoteNG.Config -{ - public class CredentialRepositoryListSaver : ISaver> - { - private readonly IDataProvider _dataProvider; - - public CredentialRepositoryListSaver(IDataProvider dataProvider) - { - if (dataProvider == null) - throw new ArgumentNullException(nameof(dataProvider)); - - _dataProvider = dataProvider; - } - - public void Save(IEnumerable repositories, string propertyNameTrigger = "") - { - var serializer = new CredentialRepositoryListSerializer(); - var data = serializer.Serialize(repositories); - _dataProvider.Save(data); - } - } -} diff --git a/mRemoteV1/Config/Serializers/CredentialProviderSerializer/CredentialRepositoryListDeserializer.cs b/mRemoteV1/Config/Serializers/CredentialProviderSerializer/CredentialRepositoryListDeserializer.cs index 618fe206f..04e13e60f 100644 --- a/mRemoteV1/Config/Serializers/CredentialProviderSerializer/CredentialRepositoryListDeserializer.cs +++ b/mRemoteV1/Config/Serializers/CredentialProviderSerializer/CredentialRepositoryListDeserializer.cs @@ -9,27 +9,38 @@ namespace mRemoteNG.Config.Serializers.CredentialProviderSerializer { public class CredentialRepositoryListDeserializer { - private readonly ISecureSerializer, string> _serializer; - private readonly ISecureDeserializer> _deserializer; - - public CredentialRepositoryListDeserializer(ISecureSerializer, string> serializer, ISecureDeserializer> deserializer) + public IEnumerable Deserialize(string xml, IEnumerable factories) { - if (serializer == null) - throw new ArgumentNullException(nameof(serializer)); - if (deserializer == null) - throw new ArgumentNullException(nameof(deserializer)); + if (string.IsNullOrEmpty(xml)) + return new ICredentialRepository[0]; - _serializer = serializer; - _deserializer = deserializer; - } - - public IEnumerable Deserialize(string xml) - { - if (string.IsNullOrEmpty(xml)) return new ICredentialRepository[0]; var xdoc = XDocument.Parse(xml); var repoEntries = xdoc.Descendants("CredentialRepository"); - var xmlRepoFactory = new XmlCredentialRepositoryFactory(_serializer, _deserializer); - return repoEntries.Select(xmlRepoFactory.Build); + + return repoEntries + .Select(ParseConfigEntries) + .Select(config => + factories + .FirstOrDefault(f => string.Equals(f.SupportsConfigType, config.TypeName))? + .Build(config)); + } + + public ICredentialRepositoryConfig ParseConfigEntries(XElement repositoryXElement) + { + var stringId = repositoryXElement.Attribute("Id")?.Value; + Guid.TryParse(stringId, out var id); + + if (id.Equals(Guid.Empty)) + id = Guid.NewGuid(); + + var config = new CredentialRepositoryConfig(id) + { + TypeName = repositoryXElement.Attribute("TypeName")?.Value, + Title = repositoryXElement.Attribute("Title")?.Value, + Source = repositoryXElement.Attribute("Source")?.Value + }; + + return config; } } } \ No newline at end of file diff --git a/mRemoteV1/Config/Serializers/Versioning/XmlCredentialManagerUpgrader.cs b/mRemoteV1/Config/Serializers/Versioning/XmlCredentialManagerUpgrader.cs index 14fd4f958..717e5048d 100644 --- a/mRemoteV1/Config/Serializers/Versioning/XmlCredentialManagerUpgrader.cs +++ b/mRemoteV1/Config/Serializers/Versioning/XmlCredentialManagerUpgrader.cs @@ -17,7 +17,7 @@ namespace mRemoteNG.Config.Serializers.Versioning { public class XmlCredentialManagerUpgrader : IDeserializer { - private readonly CredentialServiceFacade _credentialsService; + private readonly CredentialService _credentialsService; private readonly IDeserializer _decoratedDeserializer; private readonly SecureString _newRepoPassword; @@ -25,7 +25,7 @@ namespace mRemoteNG.Config.Serializers.Versioning public XmlCredentialManagerUpgrader( - CredentialServiceFacade credentialsService, + CredentialService credentialsService, string credentialFilePath, IDeserializer decoratedDeserializer, SecureString newRepoPassword) @@ -103,22 +103,20 @@ namespace mRemoteNG.Config.Serializers.Versioning private ICredentialRepository BuildXmlCredentialRepo(SecureString newCredRepoKey) { - var cryptoFromSettings = new CryptoProviderFactoryFromSettings(); - var credRepoSerializer = new XmlCredentialPasswordEncryptorDecorator( - cryptoFromSettings.Build(), - new XmlCredentialRecordSerializer()); - var credRepoDeserializer = new XmlCredentialPasswordDecryptorDecorator(new XmlCredentialRecordDeserializer()); + var repositoryConfig = new CredentialRepositoryConfig + { + Source = CredentialFilePath, + Title = "Converted Credentials", + TypeName = "Xml", + Key = newCredRepoKey + }; + + var xmlRepoFactory = _credentialsService.GetRepositoryFactoryForConfig(repositoryConfig); - var xmlRepoFactory = new XmlCredentialRepositoryFactory(credRepoSerializer, credRepoDeserializer); - var newRepo = xmlRepoFactory.Build( - new CredentialRepositoryConfig - { - Source = CredentialFilePath, - Title = "Converted Credentials", - TypeName = "Xml", - Key = newCredRepoKey - } - ); + if (!xmlRepoFactory.Any()) + throw new CredentialRepositoryTypeNotSupportedException(repositoryConfig.TypeName); + + var newRepo = xmlRepoFactory.First().Build(repositoryConfig); newRepo.LoadCredentials(newCredRepoKey); return newRepo; } diff --git a/mRemoteV1/Connection/AbstractConnectionRecord.cs b/mRemoteV1/Connection/AbstractConnectionRecord.cs index 25ec76397..2d7719aaf 100644 --- a/mRemoteV1/Connection/AbstractConnectionRecord.cs +++ b/mRemoteV1/Connection/AbstractConnectionRecord.cs @@ -189,7 +189,7 @@ namespace mRemoteNG.Connection get { var credential = CredentialRecordId - .Select(guid => Runtime.CredentialProviderCatalog.GetCredentialRecord(guid)) + .Select(guid => Runtime.CredentialService.RepositoryList.GetCredentialRecord(guid)) .FirstOrDefault(); return credential ?? new PlaceholderCredentialRecord(CredentialRecordId); } diff --git a/mRemoteV1/Connection/ConnectionsService.cs b/mRemoteV1/Connection/ConnectionsService.cs index c28e136f1..e4e425cba 100644 --- a/mRemoteV1/Connection/ConnectionsService.cs +++ b/mRemoteV1/Connection/ConnectionsService.cs @@ -30,7 +30,7 @@ namespace mRemoteNG.Connection private bool _batchingSaves = false; private bool _saveRequested = false; private bool _saveAsyncRequested = false; - private readonly CredentialServiceFacade _credentialService; + private readonly CredentialService _credentialService; public bool IsConnectionsFileLoaded { get; set; } public bool UsingDatabase { get; private set; } @@ -40,7 +40,7 @@ namespace mRemoteNG.Connection public ConnectionTreeModel ConnectionTreeModel { get; private set; } - public ConnectionsService(PuttySessionsManager puttySessionsManager, CredentialServiceFacade credentialService) + public ConnectionsService(PuttySessionsManager puttySessionsManager, CredentialService credentialService) { _puttySessionsManager = puttySessionsManager.ThrowIfNull(nameof(puttySessionsManager)); _credentialService = credentialService.ThrowIfNull(nameof(credentialService)); diff --git a/mRemoteV1/Credential/CredentialRecordTypeConverter.cs b/mRemoteV1/Credential/CredentialRecordTypeConverter.cs index 357c38ca3..e19a6d99c 100644 --- a/mRemoteV1/Credential/CredentialRecordTypeConverter.cs +++ b/mRemoteV1/Credential/CredentialRecordTypeConverter.cs @@ -34,7 +34,7 @@ namespace mRemoteNG.Credential public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) { if (!(value is Guid)) return base.ConvertFrom(context, culture, value); - var matchedCredentials = Runtime.CredentialProviderCatalog.GetCredentialRecords().Where(record => record.Id.Equals(value)).ToArray(); + var matchedCredentials = Runtime.CredentialService.RepositoryList.GetCredentialRecords().Where(record => record.Id.Equals(value)).ToArray(); return matchedCredentials.Any() ? matchedCredentials.First() : null; } } diff --git a/mRemoteV1/Credential/CredentialService.cs b/mRemoteV1/Credential/CredentialService.cs new file mode 100644 index 000000000..d5684a4da --- /dev/null +++ b/mRemoteV1/Credential/CredentialService.cs @@ -0,0 +1,110 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using mRemoteNG.Config; +using mRemoteNG.Credential.Repositories; +using mRemoteNG.Tools; +using mRemoteNG.Tools.CustomCollections; + +namespace mRemoteNG.Credential +{ + public class CredentialService + { + private readonly List _repositoryFactories; + private readonly ILoader> _loader; + private readonly ISaver> _saver; + + public ICredentialRepositoryList RepositoryList { get; } + + public IReadOnlyCollection RepositoryFactories => _repositoryFactories; + + public CredentialService( + ICredentialRepositoryList repositoryList, + List repositoryFactories, + ILoader> loader, + ISaver> saver) + { + RepositoryList = repositoryList.ThrowIfNull(nameof(repositoryList)); + _repositoryFactories = repositoryFactories.ThrowIfNull(nameof(repositoryFactories)); + _loader = loader.ThrowIfNull(nameof(loader)); + _saver = saver.ThrowIfNull(nameof(saver)); + + SetupEventHandlers(); + } + + public void SaveRepositoryList() + { + _saver.Save(RepositoryList); + } + + public void LoadRepositoryList() + { + var loadedRepositories = _loader.Load(); + + foreach (var repository in loadedRepositories) + { + RepositoryList.AddProvider(repository); + } + } + + public void AddRepository(ICredentialRepository repository) + { + RepositoryList.AddProvider(repository); + } + + public void RemoveRepository(ICredentialRepository repository) + { + RepositoryList.RemoveProvider(repository); + } + + public IEnumerable GetCredentialRecords() + { + return RepositoryList.GetCredentialRecords(); + } + + public ICredentialRecord GetCredentialRecord(Guid id) + { + return RepositoryList.GetCredentialRecord(id); + } + + /// + /// Registers an for + /// use throughout the application. + /// + /// + public void RegisterRepositoryFactory(ICredentialRepositoryFactory factory) + { + if (_repositoryFactories.Contains(factory)) + return; + + _repositoryFactories.Add(factory); + } + + public Optional GetRepositoryFactoryForConfig(ICredentialRepositoryConfig repositoryConfig) + { + return new Optional( + RepositoryFactories + .FirstOrDefault(factory => + string.Equals(factory.SupportsConfigType, repositoryConfig.TypeName))); + } + + #region Setup + private void SetupEventHandlers() + { + RepositoryList.RepositoriesUpdated += HandleRepositoriesUpdatedEvent; + RepositoryList.CredentialsUpdated += HandleCredentialsUpdatedEvent; + } + + private void HandleRepositoriesUpdatedEvent(object sender, CollectionUpdatedEventArgs collectionUpdatedEventArgs) + { + SaveRepositoryList(); + } + + private void HandleCredentialsUpdatedEvent(object sender, CollectionUpdatedEventArgs collectionUpdatedEventArgs) + { + var repo = sender as ICredentialRepository; + repo?.SaveCredentials(repo.Config.Key); + } + #endregion + } +} \ No newline at end of file diff --git a/mRemoteV1/Credential/CredentialServiceFacade.cs b/mRemoteV1/Credential/CredentialServiceFacade.cs deleted file mode 100644 index a7e430eea..000000000 --- a/mRemoteV1/Credential/CredentialServiceFacade.cs +++ /dev/null @@ -1,83 +0,0 @@ -using System; -using System.Collections.Generic; -using mRemoteNG.Config; -using mRemoteNG.Tools.CustomCollections; - -namespace mRemoteNG.Credential -{ - public class CredentialServiceFacade - { - private readonly ICredentialRepositoryList _repositoryList; - private readonly ILoader> _loader; - private readonly ISaver> _saver; - - public IEnumerable CredentialRepositories => _repositoryList; - - public CredentialServiceFacade(ICredentialRepositoryList repositoryList, ILoader> loader, ISaver> saver) - { - if (repositoryList == null) - throw new ArgumentNullException(nameof(repositoryList)); - if (loader == null) - throw new ArgumentNullException(nameof(loader)); - if (saver == null) - throw new ArgumentNullException(nameof(saver)); - - _repositoryList = repositoryList; - _loader = loader; - _saver = saver; - SetupEventHandlers(); - } - - public void SaveRepositoryList() - { - _saver.Save(_repositoryList); - } - - public void LoadRepositoryList() - { - foreach (var repository in _loader.Load()) - { - _repositoryList.AddProvider(repository); - } - } - - public void AddRepository(ICredentialRepository repository) - { - _repositoryList.AddProvider(repository); - } - - public void RemoveRepository(ICredentialRepository repository) - { - _repositoryList.RemoveProvider(repository); - } - - public IEnumerable GetCredentialRecords() - { - return _repositoryList.GetCredentialRecords(); - } - - public ICredentialRecord GetCredentialRecord(Guid id) - { - return _repositoryList.GetCredentialRecord(id); - } - - #region Setup - private void SetupEventHandlers() - { - _repositoryList.RepositoriesUpdated += HandleRepositoriesUpdatedEvent; - _repositoryList.CredentialsUpdated += HandleCredentialsUpdatedEvent; - } - - private void HandleRepositoriesUpdatedEvent(object sender, CollectionUpdatedEventArgs collectionUpdatedEventArgs) - { - SaveRepositoryList(); - } - - private void HandleCredentialsUpdatedEvent(object sender, CollectionUpdatedEventArgs collectionUpdatedEventArgs) - { - var repo = sender as ICredentialRepository; - repo?.SaveCredentials(repo.Config.Key); - } - #endregion - } -} \ No newline at end of file diff --git a/mRemoteV1/Credential/CredentialServiceFactory.cs b/mRemoteV1/Credential/CredentialServiceFactory.cs index d45e0d607..8700152c8 100644 --- a/mRemoteV1/Credential/CredentialServiceFactory.cs +++ b/mRemoteV1/Credential/CredentialServiceFactory.cs @@ -1,33 +1,24 @@ -using System.IO; -using mRemoteNG.App; +using System.Collections.Generic; +using System.IO; using mRemoteNG.App.Info; using mRemoteNG.Config; using mRemoteNG.Config.DataProviders; -using mRemoteNG.Config.Serializers.CredentialProviderSerializer; -using mRemoteNG.Config.Serializers.CredentialSerializer; -using mRemoteNG.Security.Factories; +using mRemoteNG.Credential.Repositories; namespace mRemoteNG.Credential { public class CredentialServiceFactory { // When we get a true CompositionRoot we can move this to that class. We should only require 1 instance of this service at a time - public CredentialServiceFacade Build() + public CredentialService Build() { - var cryptoFromSettings = new CryptoProviderFactoryFromSettings(); - var credRepoSerializer = new XmlCredentialPasswordEncryptorDecorator( - cryptoFromSettings.Build(), - new XmlCredentialRecordSerializer()); - var credRepoDeserializer = new XmlCredentialPasswordDecryptorDecorator(new XmlCredentialRecordDeserializer()); - + var repositoryList = new CredentialRepositoryList(); var credentialRepoListPath = Path.Combine(SettingsFileInfo.SettingsPath, "credentialRepositories.xml"); var repoListDataProvider = new FileDataProvider(credentialRepoListPath); - var repoListLoader = new CredentialRepositoryListLoader( - repoListDataProvider, - new CredentialRepositoryListDeserializer(credRepoSerializer, credRepoDeserializer)); - var repoListSaver = new CredentialRepositoryListSaver(repoListDataProvider); + var repositoryFactories = new List(); + var persistor = new CredentialRepositoryListPersistor(repoListDataProvider, repositoryFactories); - return new CredentialServiceFacade(Runtime.CredentialProviderCatalog, repoListLoader, repoListSaver); + return new CredentialService(repositoryList, repositoryFactories, persistor, persistor); } } } \ No newline at end of file diff --git a/mRemoteV1/Credential/Repositories/CredentialRepositoryTypeNotSupportedException.cs b/mRemoteV1/Credential/Repositories/CredentialRepositoryTypeNotSupportedException.cs new file mode 100644 index 000000000..d0fbd24e4 --- /dev/null +++ b/mRemoteV1/Credential/Repositories/CredentialRepositoryTypeNotSupportedException.cs @@ -0,0 +1,21 @@ +using System; + +namespace mRemoteNG.Credential.Repositories +{ + public class CredentialRepositoryTypeNotSupportedException : Exception + { + public CredentialRepositoryTypeNotSupportedException() + { + } + + public CredentialRepositoryTypeNotSupportedException(string message) + : base(message) + { + } + + public CredentialRepositoryTypeNotSupportedException(string message, Exception innerException) + : base(message, innerException) + { + } + } +} diff --git a/mRemoteV1/Credential/Repositories/ICredentialRepositoryFactory.cs b/mRemoteV1/Credential/Repositories/ICredentialRepositoryFactory.cs new file mode 100644 index 000000000..933fd6f12 --- /dev/null +++ b/mRemoteV1/Credential/Repositories/ICredentialRepositoryFactory.cs @@ -0,0 +1,20 @@ +namespace mRemoteNG.Credential.Repositories +{ + public interface ICredentialRepositoryFactory + { + /// + /// The that this factory can build. + /// + string SupportsConfigType { get; } + + /// + /// Builds a new given the + /// that describes it. The must match this factory's + /// property. + /// + /// + /// + /// + ICredentialRepository Build(ICredentialRepositoryConfig config, bool isLoaded = false); + } +} \ No newline at end of file diff --git a/mRemoteV1/Credential/Repositories/XmlCredentialRepository.cs b/mRemoteV1/Credential/Repositories/XmlCredentialRepository.cs index bb0268809..c2ef9fd1a 100644 --- a/mRemoteV1/Credential/Repositories/XmlCredentialRepository.cs +++ b/mRemoteV1/Credential/Repositories/XmlCredentialRepository.cs @@ -4,6 +4,7 @@ using System.ComponentModel; using System.Linq; using System.Security; using mRemoteNG.Config; +using mRemoteNG.Tools; using mRemoteNG.Tools.CustomCollections; namespace mRemoteNG.Credential.Repositories @@ -17,21 +18,33 @@ namespace mRemoteNG.Credential.Repositories public IList CredentialRecords { get; } public bool IsLoaded { get; private set; } - public XmlCredentialRepository(ICredentialRepositoryConfig config, CredentialRecordSaver credentialRecordSaver, CredentialRecordLoader credentialRecordLoader) + /// + /// Creates a new instance, + /// providing access to load and save credentials stored in an XML + /// format. + /// + /// + /// The config representing this repository + /// + /// + /// + /// + /// Does this instance represent a repository that is already loaded? + /// + public XmlCredentialRepository( + ICredentialRepositoryConfig config, + CredentialRecordSaver credentialRecordSaver, + CredentialRecordLoader credentialRecordLoader, + bool isLoaded = false) { - if (config == null) - throw new ArgumentNullException(nameof(config)); - if (credentialRecordSaver == null) - throw new ArgumentNullException(nameof(credentialRecordSaver)); - if (credentialRecordLoader == null) - throw new ArgumentNullException(nameof(credentialRecordLoader)); + Config = config.ThrowIfNull(nameof(config)); + _credentialRecordSaver = credentialRecordSaver.ThrowIfNull(nameof(credentialRecordSaver)); + _credentialRecordLoader = credentialRecordLoader.ThrowIfNull(nameof(credentialRecordLoader)); + IsLoaded = isLoaded; - Config = config; CredentialRecords = new FullyObservableCollection(); ((FullyObservableCollection) CredentialRecords).CollectionUpdated += RaiseCredentialsUpdatedEvent; Config.PropertyChanged += (sender, args) => RaiseRepositoryConfigUpdatedEvent(args); - _credentialRecordSaver = credentialRecordSaver; - _credentialRecordLoader = credentialRecordLoader; } public void LoadCredentials(SecureString key) @@ -39,9 +52,12 @@ namespace mRemoteNG.Credential.Repositories var credentials = _credentialRecordLoader.Load(key); foreach (var newCredential in credentials) { - if (ThisIsADuplicateCredentialRecord(newCredential)) continue; + if (ThisIsADuplicateCredentialRecord(newCredential)) + continue; + CredentialRecords.Add(newCredential); } + IsLoaded = true; Config.Key = key; } @@ -59,7 +75,9 @@ namespace mRemoteNG.Credential.Repositories public void SaveCredentials(SecureString key) { - if (!IsLoaded) return; + if (!IsLoaded) + return; + _credentialRecordSaver.Save(CredentialRecords, key); } diff --git a/mRemoteV1/Credential/Repositories/XmlCredentialRepositoryFactory.cs b/mRemoteV1/Credential/Repositories/XmlCredentialRepositoryFactory.cs index 0cebc69db..b038101f8 100644 --- a/mRemoteV1/Credential/Repositories/XmlCredentialRepositoryFactory.cs +++ b/mRemoteV1/Credential/Repositories/XmlCredentialRepositoryFactory.cs @@ -1,54 +1,41 @@ -using System; -using System.Collections.Generic; -using System.Xml.Linq; +using System.Collections.Generic; using mRemoteNG.Config; using mRemoteNG.Config.DataProviders; using mRemoteNG.Config.Serializers; +using mRemoteNG.Tools; namespace mRemoteNG.Credential.Repositories { - public class XmlCredentialRepositoryFactory + public class XmlCredentialRepositoryFactory : ICredentialRepositoryFactory { private readonly ISecureSerializer, string> _serializer; private readonly ISecureDeserializer> _deserializer; - public XmlCredentialRepositoryFactory(ISecureSerializer, string> serializer, ISecureDeserializer> deserializer) + public XmlCredentialRepositoryFactory( + ISecureSerializer, string> serializer, + ISecureDeserializer> deserializer) { - if (serializer == null) - throw new ArgumentNullException(nameof(serializer)); - if (deserializer == null) - throw new ArgumentNullException(nameof(deserializer)); - - _serializer = serializer; - _deserializer = deserializer; + _serializer = serializer.ThrowIfNull(nameof(serializer)); + _deserializer = deserializer.ThrowIfNull(nameof(deserializer)); } - public ICredentialRepository Build(ICredentialRepositoryConfig config) - { - return BuildXmlRepo(config); - } + public string SupportsConfigType { get; } = "Xml"; - public ICredentialRepository Build(XElement repositoryXElement) - { - var stringId = repositoryXElement.Attribute("Id")?.Value; - Guid id; - Guid.TryParse(stringId, out id); - if (id.Equals(Guid.Empty)) id = Guid.NewGuid(); - var config = new CredentialRepositoryConfig(id) - { - TypeName = repositoryXElement.Attribute("TypeName")?.Value, - Title = repositoryXElement.Attribute("Title")?.Value, - Source = repositoryXElement.Attribute("Source")?.Value - }; - return BuildXmlRepo(config); - } - - private ICredentialRepository BuildXmlRepo(ICredentialRepositoryConfig config) + /// + /// Creates a new instance for + /// the given . + /// + /// + /// + /// Does this instance represent a repository that is already loaded? + /// + public ICredentialRepository Build(ICredentialRepositoryConfig config, bool isLoaded = false) { var dataProvider = new FileDataProvider(config.Source); var saver = new CredentialRecordSaver(dataProvider, _serializer); var loader = new CredentialRecordLoader(dataProvider, _deserializer); - return new XmlCredentialRepository(config, saver, loader); + + return new XmlCredentialRepository(config, saver, loader, isLoaded); } } } \ No newline at end of file diff --git a/mRemoteV1/Tools/CustomCollections/FullyObservableCollection.cs b/mRemoteV1/Tools/CustomCollections/FullyObservableCollection.cs index c9f4ed0d2..943954655 100644 --- a/mRemoteV1/Tools/CustomCollections/FullyObservableCollection.cs +++ b/mRemoteV1/Tools/CustomCollections/FullyObservableCollection.cs @@ -17,8 +17,8 @@ namespace mRemoteNG.Tools.CustomCollections public T this[int index] { - get { return _list[index]; } - set { _list[index] = value; } + get => _list[index]; + set => _list[index] = value; } public FullyObservableCollection() diff --git a/mRemoteV1/Tools/Optional.cs b/mRemoteV1/Tools/Optional.cs index dc1dc0c18..edbf644a5 100644 --- a/mRemoteV1/Tools/Optional.cs +++ b/mRemoteV1/Tools/Optional.cs @@ -43,7 +43,8 @@ namespace mRemoteNG.Tools return new Optional(value); } - public static Optional FromNullable(TOut? value) where TOut : struct + public static Optional FromNullable(TOut? value) + where TOut : struct { return value.HasValue ? new Optional(value.Value) diff --git a/mRemoteV1/UI/Controls/Adapters/CredentialRecordListAdaptor.cs b/mRemoteV1/UI/Controls/Adapters/CredentialRecordListAdaptor.cs index 6ea56b0b7..cb80cc131 100644 --- a/mRemoteV1/UI/Controls/Adapters/CredentialRecordListAdaptor.cs +++ b/mRemoteV1/UI/Controls/Adapters/CredentialRecordListAdaptor.cs @@ -21,9 +21,7 @@ namespace mRemoteNG.UI.Controls.Adapters _editorService = provider.GetService(typeof(IWindowsFormsEditorService)) as IWindowsFormsEditorService; if (_editorService == null) return value; - var credentialManager = Runtime.CredentialProviderCatalog; - - var listBox = new CredentialRecordListBox(credentialManager.GetCredentialRecords()); + var listBox = new CredentialRecordListBox(Runtime.CredentialService.RepositoryList.GetCredentialRecords()); listBox.SelectedValueChanged += ListBoxOnSelectedValueChanged; _editorService.DropDownControl(listBox); diff --git a/mRemoteV1/UI/Controls/ISelectionTarget.cs b/mRemoteV1/UI/Controls/ISelectionTarget.cs index bb6ad6fce..249428c02 100644 --- a/mRemoteV1/UI/Controls/ISelectionTarget.cs +++ b/mRemoteV1/UI/Controls/ISelectionTarget.cs @@ -1,4 +1,6 @@ using System.Drawing; +using mRemoteNG.Credential; +using mRemoteNG.UI.Controls.PageSequence; namespace mRemoteNG.UI.Controls { @@ -7,5 +9,6 @@ namespace mRemoteNG.UI.Controls string Text { get; set; } Image Image { get; } T Config { get; } + SequencedControl BuildEditorPage(ICredentialRepositoryList repositoryList); } } \ No newline at end of file diff --git a/mRemoteV1/UI/Forms/CredentialManagerPages/CredentialEditorPage.cs b/mRemoteV1/UI/Forms/CredentialManagerPages/CredentialEditorPage.cs index 45188fdc5..8ae1f92d5 100644 --- a/mRemoteV1/UI/Forms/CredentialManagerPages/CredentialEditorPage.cs +++ b/mRemoteV1/UI/Forms/CredentialManagerPages/CredentialEditorPage.cs @@ -60,8 +60,10 @@ namespace mRemoteNG.UI.Forms.CredentialManagerPages private void buttonAccept_Click_1(object sender, EventArgs e) { SaveFormToCredential(); + if (!_credentialRepository.CredentialRecords.Contains(_credentialRecord)) _credentialRepository.CredentialRecords.Add(_credentialRecord); + RaiseNextPageEvent(); } diff --git a/mRemoteV1/UI/Forms/CredentialManagerPages/CredentialRepositoriesPage.cs b/mRemoteV1/UI/Forms/CredentialManagerPages/CredentialRepositoriesPage.cs index 72b3ba0d9..91467e65f 100644 --- a/mRemoteV1/UI/Forms/CredentialManagerPages/CredentialRepositoriesPage.cs +++ b/mRemoteV1/UI/Forms/CredentialManagerPages/CredentialRepositoriesPage.cs @@ -1,8 +1,10 @@ using System; using System.Drawing; +using System.Linq; using System.Windows.Forms; using mRemoteNG.Credential; using mRemoteNG.Credential.Repositories; +using mRemoteNG.Tools; using mRemoteNG.UI.Controls; using mRemoteNG.UI.Controls.PageSequence; using mRemoteNG.UI.Forms.CredentialManagerPages.CredentialRepositoryEditorPages; @@ -12,24 +14,20 @@ namespace mRemoteNG.UI.Forms.CredentialManagerPages { public sealed partial class CredentialRepositoriesPage : SequencedControl, ICredentialManagerPage { - private readonly ICredentialRepositoryList _providerCatalog; + private readonly CredentialService _credentialService; private readonly UnlockerFormFactory _unlockerFactory; public string PageName { get; } = "Sources"; public Image PageIcon { get; } = Resources.folder_key; - public CredentialRepositoriesPage(ICredentialRepositoryList providerCatalog, UnlockerFormFactory unlockerFactory) + public CredentialRepositoriesPage(CredentialService credentialService, UnlockerFormFactory unlockerFactory) { - if (providerCatalog == null) - throw new ArgumentNullException(nameof(providerCatalog)); - if (unlockerFactory == null) - throw new ArgumentNullException(nameof(unlockerFactory)); + _credentialService = credentialService.ThrowIfNull(nameof(credentialService)); + _unlockerFactory = unlockerFactory.ThrowIfNull(nameof(unlockerFactory)); - _providerCatalog = providerCatalog; - _unlockerFactory = unlockerFactory; InitializeComponent(); ApplyTheme(); - credentialRepositoryListView.CredentialRepositoryList = providerCatalog; + credentialRepositoryListView.CredentialRepositoryList = credentialService.RepositoryList; credentialRepositoryListView.SelectionChanged += (sender, args) => UpdateUi(); credentialRepositoryListView.DoubleClickHandler = EditRepository; } @@ -52,10 +50,10 @@ namespace mRemoteNG.UI.Forms.CredentialManagerPages new CredentialRepositoryTypeSelectionPage( new ISelectionTarget[] { - new XmlCredentialRepositorySelector(), + new XmlCredentialRepositorySelector(_credentialService), //new KeePassRepositorySelector() }, - _providerCatalog + _credentialService.RepositoryList ) { Dock = DockStyle.Fill }, new SequencedControl(), @@ -67,19 +65,33 @@ namespace mRemoteNG.UI.Forms.CredentialManagerPages private void buttonEdit_Click(object sender, EventArgs e) { var selectedRepository = credentialRepositoryListView.SelectedRepository; - if (selectedRepository == null) return; + if (selectedRepository == null) + return; + EditRepository(selectedRepository); } private bool EditRepository(ICredentialRepository repository) { - if (!repository.IsLoaded) return false; - var editorPage = CredentialRepositoryPageEditorFactory.BuildXmlCredentialRepositoryEditorPage(repository.Config, _providerCatalog); + if (!repository.IsLoaded) + return false; + + var repositoryFactory = _credentialService.GetRepositoryFactoryForConfig(repository.Config); + + if (!repositoryFactory.Any()) + throw new CredentialRepositoryTypeNotSupportedException(repository.Config.TypeName); + + var editorPage = new XmlCredentialRepositoryEditorPage(repository.Config, _credentialService.RepositoryList, repositoryFactory.First()) + { + Dock = DockStyle.Fill + }; + var pageSequence = new PageSequence(Parent, this, editorPage, this ); + RaiseNextPageEvent(); return true; } @@ -87,14 +99,18 @@ namespace mRemoteNG.UI.Forms.CredentialManagerPages private void buttonRemove_Click(object sender, EventArgs e) { var selectedRepository = credentialRepositoryListView.SelectedRepository; - if (selectedRepository == null) return; - if (_providerCatalog.Contains(selectedRepository.Config)) - _providerCatalog.RemoveProvider(selectedRepository); + if (selectedRepository == null) + return; + + if (_credentialService.RepositoryList.Contains(selectedRepository.Config)) + _credentialService.RepositoryList.RemoveProvider(selectedRepository); } private void UpdateLoadToggleButton(ICredentialRepository selectedRepository) { - if (selectedRepository == null) return; + if (selectedRepository == null) + return; + buttonToggleLoad.Text = selectedRepository.IsLoaded ? "Unload" : "Load"; } diff --git a/mRemoteV1/UI/Forms/CredentialManagerPages/CredentialRepositoryEditorPages/CredentialRepositoryPageEditorFactory.cs b/mRemoteV1/UI/Forms/CredentialManagerPages/CredentialRepositoryEditorPages/CredentialRepositoryPageEditorFactory.cs index 76940ac90..8fa41af25 100644 --- a/mRemoteV1/UI/Forms/CredentialManagerPages/CredentialRepositoryEditorPages/CredentialRepositoryPageEditorFactory.cs +++ b/mRemoteV1/UI/Forms/CredentialManagerPages/CredentialRepositoryEditorPages/CredentialRepositoryPageEditorFactory.cs @@ -7,9 +7,13 @@ namespace mRemoteNG.UI.Forms.CredentialManagerPages.CredentialRepositoryEditorPa { public class CredentialRepositoryPageEditorFactory { - public static SequencedControl BuildXmlCredentialRepositoryEditorPage(T config, ICredentialRepositoryList repositoryList) where T : ICredentialRepositoryConfig + public static SequencedControl BuildXmlCredentialRepositoryEditorPage(T config, ICredentialRepositoryList repositoryList, XmlCredentialRepositoryFactory repositoryFactory) + where T : ICredentialRepositoryConfig { - return new XmlCredentialRepositoryEditorPage(config, repositoryList) {Dock = DockStyle.Fill}; + return new XmlCredentialRepositoryEditorPage(config, repositoryList, repositoryFactory) + { + Dock = DockStyle.Fill + }; } } } \ No newline at end of file diff --git a/mRemoteV1/UI/Forms/CredentialManagerPages/CredentialRepositoryEditorPages/XmlCredentialRepositoryEditorPage.cs b/mRemoteV1/UI/Forms/CredentialManagerPages/CredentialRepositoryEditorPages/XmlCredentialRepositoryEditorPage.cs index 570aa2d6e..2687f70a1 100644 --- a/mRemoteV1/UI/Forms/CredentialManagerPages/CredentialRepositoryEditorPages/XmlCredentialRepositoryEditorPage.cs +++ b/mRemoteV1/UI/Forms/CredentialManagerPages/CredentialRepositoryEditorPages/XmlCredentialRepositoryEditorPage.cs @@ -1,11 +1,8 @@ using System; using System.Windows.Forms; -using mRemoteNG.Config; -using mRemoteNG.Config.DataProviders; -using mRemoteNG.Config.Serializers.CredentialSerializer; using mRemoteNG.Credential; using mRemoteNG.Credential.Repositories; -using mRemoteNG.Security.Factories; +using mRemoteNG.Tools; using mRemoteNG.UI.Controls.PageSequence; namespace mRemoteNG.UI.Forms.CredentialManagerPages.CredentialRepositoryEditorPages @@ -14,8 +11,12 @@ namespace mRemoteNG.UI.Forms.CredentialManagerPages.CredentialRepositoryEditorPa { private readonly ICredentialRepositoryConfig _repositoryConfig; private readonly ICredentialRepositoryList _repositoryList; + private readonly ICredentialRepositoryFactory _repositoryFactory; - public XmlCredentialRepositoryEditorPage(ICredentialRepositoryConfig repositoryConfig, ICredentialRepositoryList repositoryList) + public XmlCredentialRepositoryEditorPage( + ICredentialRepositoryConfig repositoryConfig, + ICredentialRepositoryList repositoryList, + ICredentialRepositoryFactory repositoryFactory) { if (repositoryConfig == null) throw new ArgumentNullException(nameof(repositoryConfig)); @@ -24,6 +25,7 @@ namespace mRemoteNG.UI.Forms.CredentialManagerPages.CredentialRepositoryEditorPa _repositoryConfig = repositoryConfig; _repositoryList = repositoryList; + _repositoryFactory = repositoryFactory.ThrowIfNull(nameof(repositoryFactory)); InitializeComponent(); PopulateFields(); } @@ -47,6 +49,7 @@ namespace mRemoteNG.UI.Forms.CredentialManagerPages.CredentialRepositoryEditorPa { if (!string.IsNullOrEmpty(_repositoryConfig.Source)) selectFilePathDialog.FileName = _repositoryConfig.Source; + var dialogResult = selectFilePathDialog.ShowDialog(this); if (dialogResult == DialogResult.OK) { @@ -56,31 +59,24 @@ namespace mRemoteNG.UI.Forms.CredentialManagerPages.CredentialRepositoryEditorPa private void buttonConfirm_Click(object sender, EventArgs e) { - if (!AllRequiredFieldsFilledOut()) return; + if (!AllRequiredFieldsFilledOut()) + return; + SaveValuesToConfig(); + if (!_repositoryList.Contains(_repositoryConfig)) { - var newCredentialRepository = BuildXmlRepoFromSettings(_repositoryConfig); - _repositoryList.AddProvider(newCredentialRepository); - newCredentialRepository.SaveCredentials(_repositoryConfig.Key); + AddNewRepo(); } + RaiseNextPageEvent(); } - private ICredentialRepository BuildXmlRepoFromSettings(ICredentialRepositoryConfig config) + private void AddNewRepo() { - var cryptoFromSettings = new CryptoProviderFactoryFromSettings(); - var credRepoDataProvider = new FileDataProvider(config.Source); - var credRepoSerializer = new XmlCredentialPasswordEncryptorDecorator( - cryptoFromSettings.Build(), - new XmlCredentialRecordSerializer()); - var credRepoDeserializer = new XmlCredentialPasswordDecryptorDecorator(new XmlCredentialRecordDeserializer()); - - return new XmlCredentialRepository( - config, - new CredentialRecordSaver(credRepoDataProvider, credRepoSerializer), - new CredentialRecordLoader(credRepoDataProvider, credRepoDeserializer) - ); + var newCredentialRepository = _repositoryFactory.Build(_repositoryConfig, true); + _repositoryList.AddProvider(newCredentialRepository); + newCredentialRepository.SaveCredentials(_repositoryConfig.Key); } private bool AllRequiredFieldsFilledOut() diff --git a/mRemoteV1/UI/Forms/CredentialManagerPages/CredentialRepositorySelectors/KeePassRepositorySelector.cs b/mRemoteV1/UI/Forms/CredentialManagerPages/CredentialRepositorySelectors/KeePassRepositorySelector.cs index debdecb2a..5c16bb5e7 100644 --- a/mRemoteV1/UI/Forms/CredentialManagerPages/CredentialRepositorySelectors/KeePassRepositorySelector.cs +++ b/mRemoteV1/UI/Forms/CredentialManagerPages/CredentialRepositorySelectors/KeePassRepositorySelector.cs @@ -1,6 +1,8 @@ using System.Drawing; +using mRemoteNG.Credential; using mRemoteNG.Credential.Repositories; using mRemoteNG.UI.Controls; +using mRemoteNG.UI.Controls.PageSequence; namespace mRemoteNG.UI.Forms.CredentialManagerPages.CredentialRepositorySelectors { @@ -9,5 +11,11 @@ namespace mRemoteNG.UI.Forms.CredentialManagerPages.CredentialRepositorySelector public string Text { get; set; } = "KeePass"; public Image Image { get; } = Resources.keepass_32x32; public ICredentialRepositoryConfig Config { get; } = new CredentialRepositoryConfig {TypeName = "KeePass"}; + + public SequencedControl BuildEditorPage(ICredentialRepositoryList repositoryList) + { + // TODO + return new SequencedControl(); + } } } \ No newline at end of file diff --git a/mRemoteV1/UI/Forms/CredentialManagerPages/CredentialRepositorySelectors/XmlCredentialRepositorySelector.cs b/mRemoteV1/UI/Forms/CredentialManagerPages/CredentialRepositorySelectors/XmlCredentialRepositorySelector.cs index 88e7ebe54..d5166f536 100644 --- a/mRemoteV1/UI/Forms/CredentialManagerPages/CredentialRepositorySelectors/XmlCredentialRepositorySelector.cs +++ b/mRemoteV1/UI/Forms/CredentialManagerPages/CredentialRepositorySelectors/XmlCredentialRepositorySelector.cs @@ -1,13 +1,39 @@ using System.Drawing; +using System.Linq; +using System.Windows.Forms; +using mRemoteNG.Credential; using mRemoteNG.Credential.Repositories; +using mRemoteNG.Tools; using mRemoteNG.UI.Controls; +using mRemoteNG.UI.Controls.PageSequence; +using mRemoteNG.UI.Forms.CredentialManagerPages.CredentialRepositoryEditorPages; namespace mRemoteNG.UI.Forms.CredentialManagerPages.CredentialRepositorySelectors { public class XmlCredentialRepositorySelector : ISelectionTarget { + private readonly CredentialService _credentialService; + public string Text { get; set; } = "XML"; public Image Image { get; } = Resources.xml; public ICredentialRepositoryConfig Config { get; } = new CredentialRepositoryConfig {TypeName = "Xml"}; + + public XmlCredentialRepositorySelector(CredentialService credentialService) + { + _credentialService = credentialService.ThrowIfNull(nameof(credentialService)); + } + + public SequencedControl BuildEditorPage(ICredentialRepositoryList repositoryList) + { + var repositoryFactory = _credentialService.GetRepositoryFactoryForConfig(Config); + + if (!repositoryFactory.Any()) + throw new CredentialRepositoryTypeNotSupportedException(Config.TypeName); + + return new XmlCredentialRepositoryEditorPage(Config, repositoryList, repositoryFactory.First()) + { + Dock = DockStyle.Fill + }; + } } } \ No newline at end of file diff --git a/mRemoteV1/UI/Forms/CredentialManagerPages/CredentialRepositoryTypeSelectionPage.cs b/mRemoteV1/UI/Forms/CredentialManagerPages/CredentialRepositoryTypeSelectionPage.cs index 2e5cd31c7..f6a7122c6 100644 --- a/mRemoteV1/UI/Forms/CredentialManagerPages/CredentialRepositoryTypeSelectionPage.cs +++ b/mRemoteV1/UI/Forms/CredentialManagerPages/CredentialRepositoryTypeSelectionPage.cs @@ -1,12 +1,11 @@ using System; using System.Collections.Generic; using System.Windows.Forms; -using BrightIdeasSoftware; using mRemoteNG.Credential; using mRemoteNG.Credential.Repositories; +using mRemoteNG.Tools; using mRemoteNG.UI.Controls; using mRemoteNG.UI.Controls.PageSequence; -using mRemoteNG.UI.Forms.CredentialManagerPages.CredentialRepositoryEditorPages; namespace mRemoteNG.UI.Forms.CredentialManagerPages { @@ -18,10 +17,8 @@ namespace mRemoteNG.UI.Forms.CredentialManagerPages { if (selectionTargets == null) throw new ArgumentNullException(nameof(selectionTargets)); - if (repositoryList == null) - throw new ArgumentNullException(nameof(repositoryList)); - _repositoryList = repositoryList; + _repositoryList = repositoryList.ThrowIfNull(nameof(repositoryList)); InitializeComponent(); ApplyTheme(); SetupListView(selectionTargets); @@ -37,8 +34,9 @@ namespace mRemoteNG.UI.Forms.CredentialManagerPages private object ImageGetter(object rowObject) { - var selection = rowObject as ISelectionTarget; - if (selection == null) return ""; + if (!(rowObject is ISelectionTarget selection)) + return ""; + var imgHash = selection.Image.GetHashCode().ToString(); if (!objectListView.LargeImageList.Images.ContainsKey(imgHash)) objectListView.LargeImageList.Images.Add(imgHash, selection.Image); @@ -47,18 +45,21 @@ namespace mRemoteNG.UI.Forms.CredentialManagerPages private void ObjectListViewOnMouseDoubleClick(object sender, MouseEventArgs mouseEventArgs) { - if (mouseEventArgs.Clicks < 2) return; - OLVColumn column; - var listItem = objectListView.GetItemAt(mouseEventArgs.X, mouseEventArgs.Y, out column); - var clickedNode = listItem.RowObject as ISelectionTarget; - if (clickedNode == null) return; + if (mouseEventArgs.Clicks < 2) + return; + + var listItem = objectListView.GetItemAt(mouseEventArgs.X, mouseEventArgs.Y, out var column); + if (!(listItem.RowObject is ISelectionTarget clickedNode)) + return; + NextPage(clickedNode); } private void buttonContinue_Click(object sender, EventArgs e) { - var selection = objectListView.SelectedObject as ISelectionTarget; - if (selection == null) return; + if (!(objectListView.SelectedObject is ISelectionTarget selection)) + return; + NextPage(selection); } @@ -71,7 +72,7 @@ namespace mRemoteNG.UI.Forms.CredentialManagerPages private SequencedControl BuildEditorPage(ISelectionTarget selection) { - var editorPage = CredentialRepositoryPageEditorFactory.BuildXmlCredentialRepositoryEditorPage(selection.Config, _repositoryList); + var editorPage = selection.BuildEditorPage(_repositoryList); editorPage.Dock = DockStyle.Fill; return editorPage; } diff --git a/mRemoteV1/UI/Forms/CredentialManagerUpgradeForm.cs b/mRemoteV1/UI/Forms/CredentialManagerUpgradeForm.cs index 0239be7e1..f98aa8893 100644 --- a/mRemoteV1/UI/Forms/CredentialManagerUpgradeForm.cs +++ b/mRemoteV1/UI/Forms/CredentialManagerUpgradeForm.cs @@ -18,7 +18,7 @@ namespace mRemoteNG.UI.Forms public IDeserializer ConnectionDeserializer { get; set; } public ConnectionsService ConnectionsService { get; set; } - public CredentialServiceFacade CredentialService { get; set; } + public CredentialService CredentialService { get; set; } public string ConnectionFilePath { diff --git a/mRemoteV1/UI/Forms/frmMain.Designer.cs b/mRemoteV1/UI/Forms/frmMain.Designer.cs index 64f07327a..3475af4f2 100644 --- a/mRemoteV1/UI/Forms/frmMain.Designer.cs +++ b/mRemoteV1/UI/Forms/frmMain.Designer.cs @@ -105,7 +105,6 @@ namespace mRemoteNG.UI.Forms // // toolsMenu // - this.toolsMenu.CredentialProviderCatalog = null; this.toolsMenu.MainForm = null; this.toolsMenu.Margin = new System.Windows.Forms.Padding(0, 3, 0, 3); this.toolsMenu.Name = "mMenTools"; diff --git a/mRemoteV1/UI/Forms/frmMain.cs b/mRemoteV1/UI/Forms/frmMain.cs index 34b4244bf..fd35f0560 100644 --- a/mRemoteV1/UI/Forms/frmMain.cs +++ b/mRemoteV1/UI/Forms/frmMain.cs @@ -16,11 +16,14 @@ using mRemoteNG.Config; using mRemoteNG.Config.Connections; using mRemoteNG.Config.DataProviders; using mRemoteNG.Config.Putty; +using mRemoteNG.Config.Serializers.CredentialSerializer; using mRemoteNG.Config.Settings; using mRemoteNG.Connection; using mRemoteNG.Credential; +using mRemoteNG.Credential.Repositories; using mRemoteNG.Messages; using mRemoteNG.Messages.MessageWriters; +using mRemoteNG.Security.Factories; using mRemoteNG.Themes; using mRemoteNG.Tools; using mRemoteNG.UI.Menu; @@ -157,6 +160,8 @@ namespace mRemoteNG.UI.Forms _fpChainedWindowHandle = NativeMethods.SetClipboardViewer(Handle); + InitializeCredRepoFactories(); + Runtime.WindowList = new WindowList(); if (Settings.Default.ResetPanels) @@ -196,6 +201,18 @@ namespace mRemoteNG.UI.Forms frmSplashScreen.Close(); } + private static void InitializeCredRepoFactories() + { + var cryptoFromSettings = new CryptoProviderFactoryFromSettings(); + var credRepoSerializer = new XmlCredentialPasswordEncryptorDecorator( + cryptoFromSettings.Build(), + new XmlCredentialRecordSerializer()); + var credRepoDeserializer = new XmlCredentialPasswordDecryptorDecorator(new XmlCredentialRecordDeserializer()); + var xmlRepoFactory = new XmlCredentialRepositoryFactory(credRepoSerializer, credRepoDeserializer); + + Runtime.CredentialService.RegisterRepositoryFactory(xmlRepoFactory); + } + private void ApplyLanguage() { fileMenu.ApplyLanguage(); @@ -249,7 +266,7 @@ namespace mRemoteNG.UI.Forms viewMenu.MainForm = this; toolsMenu.MainForm = this; - toolsMenu.CredentialProviderCatalog = Runtime.CredentialProviderCatalog; + toolsMenu.CredentialService = Runtime.CredentialService; toolsMenu.UnlockerFormFactory = _credRepoUnlockerFormFactory; _quickConnectToolStrip.ConnectionInitiator = connectionInitiator; @@ -292,7 +309,7 @@ namespace mRemoteNG.UI.Forms { PromptForUpdatesPreference(); CheckForUpdates(); - UnlockRepositories(Runtime.CredentialProviderCatalog, this); + UnlockRepositories(Runtime.CredentialService.RepositoryList, this); } private void PromptForUpdatesPreference() diff --git a/mRemoteV1/UI/Menu/ToolsMenu.cs b/mRemoteV1/UI/Menu/ToolsMenu.cs index 86f0ce9e7..1d9fc9c98 100644 --- a/mRemoteV1/UI/Menu/ToolsMenu.cs +++ b/mRemoteV1/UI/Menu/ToolsMenu.cs @@ -20,7 +20,7 @@ namespace mRemoteNG.UI.Menu private ToolStripMenuItem _credentialManagerToolStripMenuItem; public Form MainForm { get; set; } - public ICredentialRepositoryList CredentialProviderCatalog { get; set; } + public CredentialService CredentialService { get; set; } public UnlockerFormFactory UnlockerFormFactory { get; set; } public ToolsMenu() @@ -143,11 +143,11 @@ namespace mRemoteNG.UI.Menu { var pages = new UserControl[] { - new CredentialListPage(CredentialProviderCatalog) + new CredentialListPage(CredentialService.RepositoryList) { DeletionConfirmer = new CredentialDeletionMsgBoxConfirmer(MessageBox.Show) }, - new CredentialRepositoriesPage(CredentialProviderCatalog, UnlockerFormFactory) + new CredentialRepositoriesPage(CredentialService, UnlockerFormFactory) }; var credentialManagerForm = new CredentialManagerForm(pages); diff --git a/mRemoteV1/mRemoteV1.csproj b/mRemoteV1/mRemoteV1.csproj index a524ceac4..3c706c864 100644 --- a/mRemoteV1/mRemoteV1.csproj +++ b/mRemoteV1/mRemoteV1.csproj @@ -150,7 +150,7 @@ - + @@ -165,7 +165,6 @@ - @@ -252,9 +251,11 @@ + - + + True True