Merge pull request #798 from mRemoteNG/sql_test_connection

Added way to test sql connections on sql options page
This commit is contained in:
David Sparer
2017-11-11 21:18:42 -06:00
committed by GitHub
19 changed files with 1869 additions and 1625 deletions

View File

@@ -13,7 +13,7 @@ namespace mRemoteNGTests.Config.Serializers.Versioning
[SetUp]
public void Setup()
{
var sqlConnector = Substitute.For<SqlDatabaseConnector>();
var sqlConnector = Substitute.For<SqlDatabaseConnector>("", "", "", "");
_versionUpgrader = new SqlVersion22To23Upgrader(sqlConnector);
}

View File

@@ -13,7 +13,7 @@ namespace mRemoteNGTests.Config.Serializers.Versioning
[SetUp]
public void Setup()
{
var sqlConnector = Substitute.For<SqlDatabaseConnector>();
var sqlConnector = Substitute.For<SqlDatabaseConnector>("", "", "", "");
_versionUpgrader = new SqlVersion23To24Upgrader(sqlConnector);
}

View File

@@ -13,7 +13,7 @@ namespace mRemoteNGTests.Config.Serializers.Versioning
[SetUp]
public void Setup()
{
var sqlConnector = Substitute.For<SqlDatabaseConnector>();
var sqlConnector = Substitute.For<SqlDatabaseConnector>("", "", "", "");
_versionUpgrader = new SqlVersion24To25Upgrader(sqlConnector);
}

View File

@@ -13,7 +13,7 @@ namespace mRemoteNGTests.Config.Serializers.Versioning
[SetUp]
public void Setup()
{
var sqlConnector = Substitute.For<SqlDatabaseConnector>();
var sqlConnector = Substitute.For<SqlDatabaseConnector>("", "", "", "");
_versionUpgrader = new SqlVersion25To26Upgrader(sqlConnector);
}

View File

@@ -83,7 +83,7 @@ namespace mRemoteNG.Config.Connections
#region SQL
private void SaveToSql()
{
var sqlConnector = new SqlDatabaseConnector();
var sqlConnector = DatabaseConnectorFactory.SqlDatabaseConnectorFromSettings();
sqlConnector.Connect();
var databaseVersionVerifier = new SqlDatabaseVersionVerifier(sqlConnector);

View File

@@ -19,7 +19,7 @@ namespace mRemoteNG.Config.Connections
public SqlConnectionsUpdateChecker()
{
_sqlConnector = new SqlDatabaseConnector();
_sqlConnector = DatabaseConnectorFactory.SqlDatabaseConnectorFromSettings();
_sqlQuery = new SqlCommand("SELECT * FROM tblUpdate", _sqlConnector.SqlConnection);
_lastUpdateTime = default(DateTime);
_lastDatabaseUpdateTime = default(DateTime);

View File

@@ -10,7 +10,7 @@ namespace mRemoteNG.Config.Connections
{
public ConnectionTreeModel Load()
{
var connector = new SqlDatabaseConnector();
var connector = DatabaseConnectorFactory.SqlDatabaseConnectorFromSettings();
var dataProvider = new SqlDataProvider(connector);
var databaseVersionVerifier = new SqlDatabaseVersionVerifier(connector);
databaseVersionVerifier.VerifyDatabaseVersion();

View File

@@ -0,0 +1,11 @@
namespace mRemoteNG.Config.DatabaseConnectors
{
public enum ConnectionTestResult
{
ConnectionSucceded,
ServerNotAccessible,
UnknownDatabase,
CredentialsRejected,
UnknownError
}
}

View File

@@ -0,0 +1,18 @@
using mRemoteNG.App;
using mRemoteNG.Security.SymmetricEncryption;
namespace mRemoteNG.Config.DatabaseConnectors
{
public class DatabaseConnectorFactory
{
public static SqlDatabaseConnector SqlDatabaseConnectorFromSettings()
{
var sqlHost = mRemoteNG.Settings.Default.SQLHost;
var sqlCatalog = mRemoteNG.Settings.Default.SQLDatabaseName;
var sqlUsername = mRemoteNG.Settings.Default.SQLUser;
var cryptographyProvider = new LegacyRijndaelCryptographyProvider();
var sqlPassword = cryptographyProvider.Decrypt(mRemoteNG.Settings.Default.SQLPass, Runtime.EncryptionKey);
return new SqlDatabaseConnector(sqlHost, sqlCatalog, sqlUsername, sqlPassword);
}
}
}

View File

@@ -0,0 +1,38 @@
using System;
using System.Data.SqlClient;
using System.Threading.Tasks;
namespace mRemoteNG.Config.DatabaseConnectors
{
/// <summary>
/// A helper class for testing database connectivity
/// </summary>
public class SqlDatabaseConnectionTester
{
public async Task<ConnectionTestResult> TestConnectivity(string server, string database, string username, string password)
{
using (var sqlConnector = new SqlDatabaseConnector(server, database, username, password))
{
try
{
await sqlConnector.ConnectAsync();
return ConnectionTestResult.ConnectionSucceded;
}
catch (SqlException sqlException)
{
if (sqlException.Message.Contains("The server was not found"))
return ConnectionTestResult.ServerNotAccessible;
if (sqlException.Message.Contains("Cannot open database"))
return ConnectionTestResult.UnknownDatabase;
if (sqlException.Message.Contains("Login failed for user"))
return ConnectionTestResult.CredentialsRejected;
return ConnectionTestResult.UnknownError;
}
catch (Exception)
{
return ConnectionTestResult.UnknownError;
}
}
}
}
}

View File

@@ -1,7 +1,7 @@
using System.Data;
using System.Data.SqlClient;
using mRemoteNG.App;
using mRemoteNG.Security.SymmetricEncryption;
using System.Threading.Tasks;
// ReSharper disable ArrangeAccessorOwnerBody
namespace mRemoteNG.Config.DatabaseConnectors
@@ -10,18 +10,22 @@ namespace mRemoteNG.Config.DatabaseConnectors
{
public SqlConnection SqlConnection { get; private set; } = default(SqlConnection);
private string _sqlConnectionString = "";
private string _sqlHost;
private string _sqlCatalog;
private string _sqlUsername;
private string _sqlPassword;
private readonly string _sqlHost;
private readonly string _sqlCatalog;
private readonly string _sqlUsername;
private readonly string _sqlPassword;
public bool IsConnected
{
get { return (SqlConnection.State == ConnectionState.Open); }
}
public SqlDatabaseConnector()
public SqlDatabaseConnector(string sqlServer, string catalog, string username, string password)
{
_sqlHost = sqlServer;
_sqlCatalog = catalog;
_sqlUsername = username;
_sqlPassword = password;
Initialize();
}
@@ -33,9 +37,7 @@ namespace mRemoteNG.Config.DatabaseConnectors
private void BuildSqlConnectionString()
{
GetSqlConnectionDataFromSettings();
if (mRemoteNG.Settings.Default.SQLUser != "")
if (_sqlUsername != "")
BuildSqlConnectionStringWithCustomCredentials();
else
BuildSqlConnectionStringWithDefaultCredentials();
@@ -51,20 +53,16 @@ namespace mRemoteNG.Config.DatabaseConnectors
_sqlConnectionString = $"Data Source={_sqlHost};Initial Catalog={_sqlCatalog};Integrated Security=True";
}
private void GetSqlConnectionDataFromSettings()
{
_sqlHost = mRemoteNG.Settings.Default.SQLHost;
_sqlCatalog = mRemoteNG.Settings.Default.SQLDatabaseName;
_sqlUsername = mRemoteNG.Settings.Default.SQLUser;
var cryptographyProvider = new LegacyRijndaelCryptographyProvider();
_sqlPassword = cryptographyProvider.Decrypt(mRemoteNG.Settings.Default.SQLPass, Runtime.EncryptionKey);
}
public void Connect()
{
SqlConnection.Open();
}
public async Task ConnectAsync()
{
await SqlConnection.OpenAsync();
}
public void Disconnect()
{
SqlConnection.Close();

View File

@@ -19,7 +19,7 @@ namespace mRemoteNG {
// class via a tool like ResGen or Visual Studio.
// To add or remove a member, edit your .ResX file then rerun ResGen
// with the /str option, or rebuild your VS project.
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class Resources {
@@ -740,6 +740,16 @@ namespace mRemoteNG {
}
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Bitmap.
/// </summary>
internal static System.Drawing.Bitmap loading_spinner {
get {
object obj = ResourceManager.GetObject("loading_spinner", resourceCulture);
return ((System.Drawing.Bitmap)(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Bitmap.
/// </summary>

View File

@@ -526,4 +526,7 @@
<data name="ConnectedOverlay" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\Images\ConnectedOverlay.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="loading_spinner" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\Images\loading_spinner.gif;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
</root>

Binary file not shown.

After

Width:  |  Height:  |  Size: 606 B

View File

@@ -1,4 +1,4 @@
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.42000
@@ -8,18 +8,18 @@
// </auto-generated>
//------------------------------------------------------------------------------
namespace mRemoteNG
{
/// <summary>
/// A strongly-typed resource class, for looking up localized strings, etc.
/// </summary>
// This class was auto-generated by the StronglyTypedResourceBuilder
// class via a tool like ResGen or Visual Studio.
// To add or remove a member, edit your .ResX file then rerun ResGen
// with the /str option, or rebuild your VS project.
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")]
namespace mRemoteNG {
using System;
/// <summary>
/// A strongly-typed resource class, for looking up localized strings, etc.
/// </summary>
// This class was auto-generated by the StronglyTypedResourceBuilder
// class via a tool like ResGen or Visual Studio.
// To add or remove a member, edit your .ResX file then rerun ResGen
// with the /str option, or rebuild your VS project.
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class Language {
@@ -95,6 +95,15 @@ namespace mRemoteNG
return ResourceManager.GetString("ConfigurationImportFile", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Connection successful.
/// </summary>
internal static string ConnectionSuccessful {
get {
return ResourceManager.GetString("ConnectionSuccessful", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Could not find external tool with name &quot;{0}&quot;.
@@ -123,6 +132,15 @@ namespace mRemoteNG
}
}
/// <summary>
/// Looks up a localized string similar to Database &apos;{0}&apos; not available..
/// </summary>
internal static string DatabaseNotAvailable {
get {
return ResourceManager.GetString("DatabaseNotAvailable", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Incorrect password.
/// </summary>
@@ -132,6 +150,15 @@ namespace mRemoteNG
}
}
/// <summary>
/// Looks up a localized string similar to Login failed for user &apos;{0}&apos;..
/// </summary>
internal static string LoginFailedForUser {
get {
return ResourceManager.GetString("LoginFailedForUser", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Prompt to unlock credential repositories on startup.
/// </summary>
@@ -150,6 +177,15 @@ namespace mRemoteNG
}
}
/// <summary>
/// Looks up a localized string similar to Server &apos;{0}&apos; was not accessible..
/// </summary>
internal static string ServerNotAccessible {
get {
return ResourceManager.GetString("ServerNotAccessible", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Source.
/// </summary>
@@ -7444,6 +7480,15 @@ namespace mRemoteNG
}
}
/// <summary>
/// Looks up a localized string similar to Testing connection.
/// </summary>
internal static string TestingConnection {
get {
return ResourceManager.GetString("TestingConnection", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Unlock.
/// </summary>

View File

@@ -2610,4 +2610,19 @@ This page will walk you through the process of upgrading your connections file o
<data name="ConfigurationCustomPath" xml:space="preserve">
<value>Use a Custom File Path</value>
</data>
</root>
<data name="TestingConnection" xml:space="preserve">
<value>Testing connection</value>
</data>
<data name="ServerNotAccessible" xml:space="preserve">
<value>Server '{0}' was not accessible.</value>
</data>
<data name="ConnectionSuccessful" xml:space="preserve">
<value>Connection successful</value>
</data>
<data name="LoginFailedForUser" xml:space="preserve">
<value>Login failed for user '{0}'.</value>
</data>
<data name="DatabaseNotAvailable" xml:space="preserve">
<value>Database '{0}' not available.</value>
</data>
</root>

View File

@@ -22,9 +22,6 @@ namespace mRemoteNG.UI.Forms.OptionsPages
}
}
//Required by the Windows Form Designer
private System.ComponentModel.Container components = null;
//NOTE: The following procedure is required by the Windows Form Designer
//It can be modified using the Windows Form Designer.
//Do not modify it using the code editor.
@@ -42,6 +39,10 @@ namespace mRemoteNG.UI.Forms.OptionsPages
this.txtSQLUsername = new mRemoteNG.UI.Controls.Base.NGTextBox();
this.txtSQLServer = new mRemoteNG.UI.Controls.Base.NGTextBox();
this.lblSQLPassword = new mRemoteNG.UI.Controls.Base.NGLabel();
this.btnTestConnection = new mRemoteNG.UI.Controls.Base.NGButton();
this.imgConnectionStatus = new System.Windows.Forms.PictureBox();
this.lblTestConnectionResults = new mRemoteNG.UI.Controls.Base.NGLabel();
((System.ComponentModel.ISupportInitialize)(this.imgConnectionStatus)).BeginInit();
this.SuspendLayout();
//
// lblSQLDatabaseName
@@ -161,10 +162,43 @@ namespace mRemoteNG.UI.Forms.OptionsPages
this.lblSQLPassword.Text = "Password:";
this.lblSQLPassword.TextAlign = System.Drawing.ContentAlignment.TopRight;
//
// btnTestConnection
//
this.btnTestConnection._mice = mRemoteNG.UI.Controls.Base.NGButton.MouseState.HOVER;
this.btnTestConnection.Enabled = false;
this.btnTestConnection.Location = new System.Drawing.Point(140, 208);
this.btnTestConnection.Name = "btnTestConnection";
this.btnTestConnection.Size = new System.Drawing.Size(153, 23);
this.btnTestConnection.TabIndex = 11;
this.btnTestConnection.Text = "Test Connection";
this.btnTestConnection.UseVisualStyleBackColor = true;
this.btnTestConnection.Click += new System.EventHandler(this.btnTestConnection_Click);
//
// imgConnectionStatus
//
this.imgConnectionStatus.Image = global::mRemoteNG.Resources.Help;
this.imgConnectionStatus.Location = new System.Drawing.Point(299, 212);
this.imgConnectionStatus.Name = "imgConnectionStatus";
this.imgConnectionStatus.Size = new System.Drawing.Size(16, 16);
this.imgConnectionStatus.SizeMode = System.Windows.Forms.PictureBoxSizeMode.AutoSize;
this.imgConnectionStatus.TabIndex = 12;
this.imgConnectionStatus.TabStop = false;
//
// lblTestConnectionResults
//
this.lblTestConnectionResults.AutoSize = true;
this.lblTestConnectionResults.Location = new System.Drawing.Point(140, 238);
this.lblTestConnectionResults.Name = "lblTestConnectionResults";
this.lblTestConnectionResults.Size = new System.Drawing.Size(0, 13);
this.lblTestConnectionResults.TabIndex = 13;
//
// SqlServerPage
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.Controls.Add(this.lblTestConnectionResults);
this.Controls.Add(this.imgConnectionStatus);
this.Controls.Add(this.btnTestConnection);
this.Controls.Add(this.lblSQLDatabaseName);
this.Controls.Add(this.txtSQLDatabaseName);
this.Controls.Add(this.lblExperimental);
@@ -179,6 +213,7 @@ namespace mRemoteNG.UI.Forms.OptionsPages
this.Name = "SqlServerPage";
this.PageIcon = ((System.Drawing.Icon)(resources.GetObject("$this.PageIcon")));
this.Size = new System.Drawing.Size(610, 489);
((System.ComponentModel.ISupportInitialize)(this.imgConnectionStatus)).EndInit();
this.ResumeLayout(false);
this.PerformLayout();
@@ -194,6 +229,9 @@ namespace mRemoteNG.UI.Forms.OptionsPages
internal Controls.Base.NGTextBox txtSQLUsername;
internal Controls.Base.NGTextBox txtSQLServer;
internal Controls.Base.NGLabel lblSQLPassword;
}
private Controls.Base.NGButton btnTestConnection;
private System.Windows.Forms.PictureBox imgConnectionStatus;
private System.ComponentModel.IContainer components;
private Controls.Base.NGLabel lblTestConnectionResults;
}
}

View File

@@ -2,16 +2,20 @@ using System;
using mRemoteNG.App;
using mRemoteNG.Config.Connections;
using mRemoteNG.Config.Connections.Multiuser;
using mRemoteNG.Config.DatabaseConnectors;
using mRemoteNG.Security.SymmetricEncryption;
namespace mRemoteNG.UI.Forms.OptionsPages
{
public partial class SqlServerPage
{
private SqlDatabaseConnectionTester _databaseConnectionTester;
public SqlServerPage()
{
InitializeComponent();
base.ApplyTheme();
_databaseConnectionTester = new SqlDatabaseConnectionTester();
}
public override string PageName
@@ -32,6 +36,7 @@ namespace mRemoteNG.UI.Forms.OptionsPages
lblSQLDatabaseName.Text = Language.strLabelSQLServerDatabaseName;
lblSQLUsername.Text = Language.strLabelUsername;
lblSQLPassword.Text = Language.strLabelPassword;
btnTestConnection.Text = "Test Connection";
}
public override void LoadSettings()
@@ -88,6 +93,65 @@ namespace mRemoteNG.UI.Forms.OptionsPages
txtSQLDatabaseName.Enabled = chkUseSQLServer.Checked;
txtSQLUsername.Enabled = chkUseSQLServer.Checked;
txtSQLPassword.Enabled = chkUseSQLServer.Checked;
btnTestConnection.Enabled = chkUseSQLServer.Checked;
}
private async void btnTestConnection_Click(object sender, EventArgs e)
{
var server = txtSQLServer.Text;
var database = txtSQLDatabaseName.Text;
var username = txtSQLUsername.Text;
var password = txtSQLPassword.Text;
lblTestConnectionResults.Text = Language.TestingConnection;
imgConnectionStatus.Image = Resources.loading_spinner;
btnTestConnection.Enabled = false;
var connectionTestResult = await _databaseConnectionTester.TestConnectivity(server, database, username, password);
btnTestConnection.Enabled = true;
switch (connectionTestResult)
{
case ConnectionTestResult.ConnectionSucceded:
UpdateConnectionImage(true);
lblTestConnectionResults.Text = Language.ConnectionSuccessful;
break;
case ConnectionTestResult.ServerNotAccessible:
UpdateConnectionImage(false);
lblTestConnectionResults.Text = BuildTestFailedMessage(string.Format(Language.ServerNotAccessible, server));
break;
case ConnectionTestResult.CredentialsRejected:
UpdateConnectionImage(false);
lblTestConnectionResults.Text = BuildTestFailedMessage(string.Format(Language.LoginFailedForUser, username));
break;
case ConnectionTestResult.UnknownDatabase:
UpdateConnectionImage(false);
lblTestConnectionResults.Text = BuildTestFailedMessage(string.Format(Language.DatabaseNotAvailable, database));
break;
case ConnectionTestResult.UnknownError:
UpdateConnectionImage(false);
lblTestConnectionResults.Text = BuildTestFailedMessage(Language.strRdpErrorUnknown);
break;
default:
UpdateConnectionImage(false);
lblTestConnectionResults.Text = BuildTestFailedMessage(Language.strRdpErrorUnknown);
break;
}
}
private void UpdateConnectionImage(bool connectionSuccess)
{
imgConnectionStatus.Image = connectionSuccess
? Resources.tick
: Resources.exclamation;
}
private string BuildTestFailedMessage(string specificMessage)
{
return Language.strConnectionOpenFailed +
Environment.NewLine +
specificMessage;
}
}
}

File diff suppressed because it is too large Load Diff