diff --git a/TGS.ControlPanel/App.config b/TGS.ControlPanel/App.config index f753d0fbb0..e5a8ddbf55 100644 --- a/TGS.ControlPanel/App.config +++ b/TGS.ControlPanel/App.config @@ -26,24 +26,9 @@ 0 - - - - - - - - 38607 - False - - - - - - diff --git a/TGS.ControlPanel/CountedForm.cs b/TGS.ControlPanel/CountedForm.cs index 03ac979ab5..8f79f1c400 100644 --- a/TGS.ControlPanel/CountedForm.cs +++ b/TGS.ControlPanel/CountedForm.cs @@ -6,10 +6,9 @@ namespace TGS.ControlPanel /// Calls when all s are d /// #if !DEBUG - abstract class CountedForm : ServerOpForm -#else - class CountedForm : ServerOpForm + abstract #endif + class CountedForm : ServerOpForm { /// /// The current number of active s diff --git a/TGS.ControlPanel/Login.Designer.cs b/TGS.ControlPanel/Login.Designer.cs index 2e7a9afbc6..e4f9a30693 100644 --- a/TGS.ControlPanel/Login.Designer.cs +++ b/TGS.ControlPanel/Login.Designer.cs @@ -31,16 +31,17 @@ System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(Login)); this.LocalLoginButton = new System.Windows.Forms.Button(); this.CurrentRevisionTitle = new System.Windows.Forms.Label(); - this.IPTextBox = new System.Windows.Forms.TextBox(); this.UsernameTextBox = new System.Windows.Forms.TextBox(); this.PasswordTextBox = new System.Windows.Forms.TextBox(); this.RemoteLoginButton = new System.Windows.Forms.Button(); - this.label1 = new System.Windows.Forms.Label(); + this.AddressLabel = new System.Windows.Forms.Label(); this.label2 = new System.Windows.Forms.Label(); this.label3 = new System.Windows.Forms.Label(); this.label4 = new System.Windows.Forms.Label(); this.PortSelector = new System.Windows.Forms.NumericUpDown(); this.SavePasswordCheckBox = new System.Windows.Forms.CheckBox(); + this.IPComboBox = new System.Windows.Forms.ComboBox(); + this.DeleteLoginButton = new System.Windows.Forms.Button(); ((System.ComponentModel.ISupportInitialize)(this.PortSelector)).BeginInit(); this.SuspendLayout(); // @@ -50,7 +51,7 @@ this.LocalLoginButton.Location = new System.Drawing.Point(102, 12); this.LocalLoginButton.Name = "LocalLoginButton"; this.LocalLoginButton.Size = new System.Drawing.Size(157, 25); - this.LocalLoginButton.TabIndex = 13; + this.LocalLoginButton.TabIndex = 1; this.LocalLoginButton.Text = "Connect to Local Service"; this.LocalLoginButton.UseVisualStyleBackColor = true; this.LocalLoginButton.Click += new System.EventHandler(this.LocalLoginButton_Click); @@ -67,13 +68,6 @@ this.CurrentRevisionTitle.Text = "Remote Login:"; this.CurrentRevisionTitle.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; // - // IPTextBox - // - this.IPTextBox.Location = new System.Drawing.Point(129, 99); - this.IPTextBox.Name = "IPTextBox"; - this.IPTextBox.Size = new System.Drawing.Size(146, 20); - this.IPTextBox.TabIndex = 15; - // // UsernameTextBox // this.UsernameTextBox.Location = new System.Drawing.Point(129, 132); @@ -100,17 +94,17 @@ this.RemoteLoginButton.UseVisualStyleBackColor = true; this.RemoteLoginButton.Click += new System.EventHandler(this.RemoteLoginButton_Click); // - // label1 + // AddressLabel // - this.label1.AutoSize = true; - this.label1.Font = new System.Drawing.Font("Verdana", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.label1.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(248)))), ((int)(((byte)(248)))), ((int)(((byte)(242))))); - this.label1.Location = new System.Drawing.Point(9, 99); - this.label1.Name = "label1"; - this.label1.Size = new System.Drawing.Size(120, 18); - this.label1.TabIndex = 19; - this.label1.Text = "Address/Port:"; - this.label1.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; + this.AddressLabel.AutoSize = true; + this.AddressLabel.Font = new System.Drawing.Font("Verdana", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.AddressLabel.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(248)))), ((int)(((byte)(248)))), ((int)(((byte)(242))))); + this.AddressLabel.Location = new System.Drawing.Point(9, 99); + this.AddressLabel.Name = "AddressLabel"; + this.AddressLabel.Size = new System.Drawing.Size(80, 18); + this.AddressLabel.TabIndex = 19; + this.AddressLabel.Text = "Address:"; + this.AddressLabel.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; // // label2 // @@ -165,7 +159,7 @@ this.PortSelector.Size = new System.Drawing.Size(80, 20); this.PortSelector.TabIndex = 16; this.PortSelector.Value = new decimal(new int[] { - 1, + 38607, 0, 0, 0}); @@ -182,22 +176,42 @@ this.SavePasswordCheckBox.UseVisualStyleBackColor = true; this.SavePasswordCheckBox.CheckedChanged += new System.EventHandler(this.SavePasswordCheckBox_CheckedChanged); // + // IPComboBox + // + this.IPComboBox.FormattingEnabled = true; + this.IPComboBox.Location = new System.Drawing.Point(130, 98); + this.IPComboBox.Name = "IPComboBox"; + this.IPComboBox.Size = new System.Drawing.Size(146, 21); + this.IPComboBox.TabIndex = 24; + // + // DeleteLoginButton + // + this.DeleteLoginButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.DeleteLoginButton.Location = new System.Drawing.Point(105, 98); + this.DeleteLoginButton.Name = "DeleteLoginButton"; + this.DeleteLoginButton.Size = new System.Drawing.Size(19, 19); + this.DeleteLoginButton.TabIndex = 25; + this.DeleteLoginButton.Text = "x"; + this.DeleteLoginButton.UseVisualStyleBackColor = true; + this.DeleteLoginButton.Click += new System.EventHandler(this.DeleteLoginButton_Click); + // // Login // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(39)))), ((int)(((byte)(40)))), ((int)(((byte)(34))))); this.ClientSize = new System.Drawing.Size(374, 237); + this.Controls.Add(this.DeleteLoginButton); + this.Controls.Add(this.IPComboBox); this.Controls.Add(this.SavePasswordCheckBox); this.Controls.Add(this.PortSelector); this.Controls.Add(this.label4); this.Controls.Add(this.label3); this.Controls.Add(this.label2); - this.Controls.Add(this.label1); + this.Controls.Add(this.AddressLabel); this.Controls.Add(this.RemoteLoginButton); this.Controls.Add(this.PasswordTextBox); this.Controls.Add(this.UsernameTextBox); - this.Controls.Add(this.IPTextBox); this.Controls.Add(this.CurrentRevisionTitle); this.Controls.Add(this.LocalLoginButton); this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle; @@ -216,15 +230,16 @@ private System.Windows.Forms.Button LocalLoginButton; private System.Windows.Forms.Label CurrentRevisionTitle; - private System.Windows.Forms.TextBox IPTextBox; private System.Windows.Forms.TextBox UsernameTextBox; private System.Windows.Forms.TextBox PasswordTextBox; private System.Windows.Forms.Button RemoteLoginButton; - private System.Windows.Forms.Label label1; + private System.Windows.Forms.Label AddressLabel; private System.Windows.Forms.Label label2; private System.Windows.Forms.Label label3; private System.Windows.Forms.Label label4; private System.Windows.Forms.NumericUpDown PortSelector; private System.Windows.Forms.CheckBox SavePasswordCheckBox; + private System.Windows.Forms.ComboBox IPComboBox; + private System.Windows.Forms.Button DeleteLoginButton; } } \ No newline at end of file diff --git a/TGS.ControlPanel/Login.cs b/TGS.ControlPanel/Login.cs index 4708c515bf..1a1328cbda 100644 --- a/TGS.ControlPanel/Login.cs +++ b/TGS.ControlPanel/Login.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Specialized; using System.Windows.Forms; using TGS.Interface; @@ -7,58 +8,123 @@ namespace TGS.ControlPanel sealed partial class Login : CountedForm { /// - /// Create a form + /// Currently selected saved /// + RemoteLoginInfo currentLoginInfo; + + bool updatingFields; + + /// + /// Construct a + /// public Login() { InitializeComponent(); var Config = Properties.Settings.Default; - IPTextBox.Text = Config.RemoteIP; - UsernameTextBox.Text = Config.RemoteUsername; - PortSelector.Value = Config.RemotePort; AcceptButton = RemoteLoginButton; if(Config.RemoteDefault) RemoteLoginButton.TabIndex = 0; //make this the first thing selected when loading - var decrypted = Helpers.DecryptData(Config.RemotePassword, Config.RemoteEntropy); - if (decrypted != null) + + IPComboBox.SelectedIndexChanged += IPComboBox_SelectedIndexChanged; + var loginInfo = Config.RemoteLoginInfo; + if (loginInfo != null) { - PasswordTextBox.Text = decrypted; - SavePasswordCheckBox.Checked = true; + foreach(var I in loginInfo) + IPComboBox.Items.Add(new RemoteLoginInfo(I)); + if (IPComboBox.Items.Count > 0) + IPComboBox.SelectedIndex = 0; } + + UsernameTextBox.TextChanged += (a, b) => ClearFields(); + PortSelector.ValueChanged += (a, b) => ClearFields(); + PasswordTextBox.TextChanged += (a, b) => ClearFields(); + IPComboBox.TextChanged += (a, b) => ClearFields(); } - private void RemoteLoginButton_Click(object sender, EventArgs e) + void ClearFields() { - IPTextBox.Text = IPTextBox.Text.Trim(); - UsernameTextBox.Text = UsernameTextBox.Text.Trim(); - using (var I = new ServerInterface(new RemoteLoginInfo(IPTextBox.Text, (ushort)PortSelector.Value, UsernameTextBox.Text, PasswordTextBox.Text))) + if (updatingFields || currentLoginInfo == null) + return; + updatingFields = true; + currentLoginInfo = null; + IPComboBox.Text = ""; + PortSelector.Value = 38607; + UsernameTextBox.Text = ""; + PasswordTextBox.Text = ""; + updatingFields = false; + } + + void IPComboBox_SelectedIndexChanged(object sender, EventArgs e) + { + currentLoginInfo = IPComboBox.SelectedItem as RemoteLoginInfo; + //new thing + if (currentLoginInfo == null) + { + ClearFields(); + return; + } + updatingFields = true; + IPComboBox.Text = currentLoginInfo.IP; + PortSelector.Value = currentLoginInfo.Port; + UsernameTextBox.Text = currentLoginInfo.Username; + SavePasswordCheckBox.Checked = currentLoginInfo.HasPassword; + if (currentLoginInfo.HasPassword) + PasswordTextBox.Text = "************"; + else + PasswordTextBox.Text = ""; + updatingFields = false; + } + + void RemoteLoginButton_Click(object sender, EventArgs e) + { + if (String.IsNullOrWhiteSpace(PasswordTextBox.Text) || String.IsNullOrWhiteSpace(UsernameTextBox.Text) || String.IsNullOrWhiteSpace(IPComboBox.Text) || PortSelector.Value == 0) + return; + + RemoteLoginInfo loginInfo; + if (currentLoginInfo == null) + { + loginInfo = new RemoteLoginInfo(IPComboBox.Text, (ushort)PortSelector.Value, UsernameTextBox.Text.Trim(), PasswordTextBox.Text); + } + else + { + loginInfo = (RemoteLoginInfo)IPComboBox.SelectedItem; + if (!loginInfo.HasPassword) + loginInfo.Password = PasswordTextBox.Text; + } + + using (var I = new ServerInterface(loginInfo)) { var Config = Properties.Settings.Default; - Config.RemoteIP = IPTextBox.Text; - Config.RemotePort = (ushort)PortSelector.Value; - Config.RemoteUsername = UsernameTextBox.Text; - if (SavePasswordCheckBox.Checked) + //This needs to be read here because V&C Closing us will corrupt the data + var savePassword = SavePasswordCheckBox.Checked; + if (VerifyAndConnect(I)) { - Config.RemotePassword = Helpers.EncryptData(PasswordTextBox.Text, out string entrop); - Config.RemoteEntropy = entrop; + Config.RemoteDefault = true; + + if (!savePassword) + loginInfo.Password = null; + + Config.RemoteLoginInfo = new StringCollection { loginInfo.ToJSON() }; + + foreach (RemoteLoginInfo info in IPComboBox.Items) + if (!info.Equals(loginInfo)) + Config.RemoteLoginInfo.Add(info.ToJSON()); } - else - { - Config.RemotePassword = null; - Config.RemoteEntropy = null; - } - Config.RemoteDefault = true; - VerifyAndConnect(I); } } - private void LocalLoginButton_Click(object sender, EventArgs e) + void LocalLoginButton_Click(object sender, EventArgs e) { Properties.Settings.Default.RemoteDefault = false; VerifyAndConnect(new ServerInterface()); } - void VerifyAndConnect(IServerInterface I) + /// + /// Attempts a connection on a given + /// + /// The to attempt a connection on + /// if the connection was made and authenticated, otherwise + bool VerifyAndConnect(IServerInterface I) { try { @@ -66,19 +132,20 @@ namespace TGS.ControlPanel if (!res.HasFlag(ConnectivityLevel.Connected)) { MessageBox.Show("Unable to connect to service! Error: " + error); - return; + return false; } if (!res.HasFlag(ConnectivityLevel.Authenticated)) { MessageBox.Show("Authentication error: Username/password/windows identity is not authorized! Ensure you are a system administrator or in the correct Windows group on the service machine."); - return; + return false; } if (I.VersionMismatch(out error) && MessageBox.Show(error, "Warning", MessageBoxButtons.OKCancel) == DialogResult.Cancel) - return; + return true; new InstanceSelector(I).Show(); Close(); + return true; } catch { @@ -87,14 +154,26 @@ namespace TGS.ControlPanel } } - private void SavePasswordCheckBox_CheckedChanged(object sender, EventArgs e) + void SavePasswordCheckBox_CheckedChanged(object sender, EventArgs e) { - if (!SavePasswordCheckBox.Checked) + if (!updatingFields && !SavePasswordCheckBox.Checked && currentLoginInfo != null) { - var Config = Properties.Settings.Default; - Config.RemotePassword = null; - Config.RemoteEntropy = null; + currentLoginInfo = null; + PasswordTextBox.Text = ""; } } + + /// + /// Removes the selected item from + /// + /// The sender of the event + /// The + void DeleteLoginButton_Click(object sender, EventArgs e) + { + //make sure we're trying to delete a real item + if (IPComboBox.SelectedItem as RemoteLoginInfo == null) + return; + IPComboBox.Items.RemoveAt(IPComboBox.SelectedIndex); + } } } diff --git a/TGS.ControlPanel/Properties/Settings.Designer.cs b/TGS.ControlPanel/Properties/Settings.Designer.cs index 50131930ce..391f1eca5b 100644 --- a/TGS.ControlPanel/Properties/Settings.Designer.cs +++ b/TGS.ControlPanel/Properties/Settings.Designer.cs @@ -73,37 +73,12 @@ namespace TGS.ControlPanel.Properties { [global::System.Configuration.UserScopedSettingAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("")] - public string RemoteIP { + public global::System.Collections.Specialized.StringCollection RemoteLoginInfo { get { - return ((string)(this["RemoteIP"])); + return ((global::System.Collections.Specialized.StringCollection)(this["RemoteLoginInfo"])); } set { - this["RemoteIP"] = value; - } - } - - [global::System.Configuration.UserScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("")] - public string RemoteUsername { - get { - return ((string)(this["RemoteUsername"])); - } - set { - this["RemoteUsername"] = value; - } - } - - [global::System.Configuration.UserScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("38607")] - public ushort RemotePort { - get { - return ((ushort)(this["RemotePort"])); - } - set { - this["RemotePort"] = value; + this["RemoteLoginInfo"] = value; } } @@ -119,30 +94,6 @@ namespace TGS.ControlPanel.Properties { } } - [global::System.Configuration.UserScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("")] - public string RemotePassword { - get { - return ((string)(this["RemotePassword"])); - } - set { - this["RemotePassword"] = value; - } - } - - [global::System.Configuration.UserScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("")] - public string RemoteEntropy { - get { - return ((string)(this["RemoteEntropy"])); - } - set { - this["RemoteEntropy"] = value; - } - } - [global::System.Configuration.UserScopedSettingAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Configuration.DefaultSettingValueAttribute("")] diff --git a/TGS.ControlPanel/Properties/Settings.settings b/TGS.ControlPanel/Properties/Settings.settings index 2a1efd2454..f2be0bac77 100644 --- a/TGS.ControlPanel/Properties/Settings.settings +++ b/TGS.ControlPanel/Properties/Settings.settings @@ -14,24 +14,12 @@ 0 - + - - - - - 38607 - False - - - - - - diff --git a/TGS.ControlPanel/ServerOpForm.cs b/TGS.ControlPanel/ServerOpForm.cs index d3843c7c2b..c8aba31166 100644 --- a/TGS.ControlPanel/ServerOpForm.cs +++ b/TGS.ControlPanel/ServerOpForm.cs @@ -8,10 +8,9 @@ namespace TGS.ControlPanel /// Used to provide an ATP function for calls into an /// #if !DEBUG - abstract class ServerOpForm : Form -#else - class ServerOpForm : Form + abstract #endif + class ServerOpForm : Form { /// /// Used to wrap calls in a non-blocking fashion while disabling the and enabling the wait cursor diff --git a/TGS.Interface/RemoteLoginInfo.cs b/TGS.Interface/RemoteLoginInfo.cs index 06568fbe6d..abdd76c4ee 100644 --- a/TGS.Interface/RemoteLoginInfo.cs +++ b/TGS.Interface/RemoteLoginInfo.cs @@ -1,34 +1,40 @@ using System; +using System.Collections.Generic; +using System.Web.Script.Serialization; namespace TGS.Interface { /// /// Information representing a remote server connection /// - [Serializable] - public sealed class RemoteLoginInfo + public sealed class RemoteLoginInfo : IEquatable { + /// + /// Used for serialization + /// + const string EntropyFormatter = "{0}Entropy"; + /// /// The IP address or URL of the target server /// - public string IP { get; } + public string IP { get { return _ip; } } /// /// The port to connect to the target server /// - public ushort Port { get; } + public ushort Port { get { return _port; } } /// /// A Windows username for the target server /// - public string Username { get; } - /// - /// Check if the has been initialized with a - /// - public bool HasPassword { get { return !String.IsNullOrWhiteSpace(Password); } } - + public string Username { get { return _username; } } /// /// The Windows password for /// - internal string Password { get; } + public string Password { internal get; set; } + /// + /// Check if the has been initialized with a + /// + [ScriptIgnore] + public bool HasPassword { get { return !String.IsNullOrWhiteSpace(Password); } } /// /// Backing field for @@ -42,10 +48,6 @@ namespace TGS.Interface /// Backing field for /// readonly string _username; - /// - /// Backing field for - /// - readonly string _password; /// /// Construct a @@ -59,13 +61,73 @@ namespace TGS.Interface if (String.IsNullOrWhiteSpace(ip)) throw new InvalidOperationException("ip must be set!"); _ip = ip; - if(port == 0) + if (port == 0) throw new InvalidOperationException("port may not be 0!"); _port = port; if (String.IsNullOrWhiteSpace(username)) throw new InvalidOperationException("username must be set!"); _username = username; - _password = password; + Password = password; + } + + /// + /// Construct a from JSON + /// + /// The result of a call to + public RemoteLoginInfo(string json) + { + var dic = new JavaScriptSerializer().Deserialize>(json); + var ip = (string)dic[nameof(IP)]; + if (String.IsNullOrWhiteSpace(ip)) + throw new InvalidOperationException("ip must be set!"); + _ip = ip; + var port = (ushort)(int)dic[nameof(Port)]; + if (port == 0) + throw new InvalidOperationException("port may not be 0!"); + _port = port; + var username = (string)dic[nameof(Username)]; + if (String.IsNullOrWhiteSpace(username)) + throw new InvalidOperationException("username must be set!"); + _username = username; + if(dic.ContainsKey(nameof(Password))) + Password = Helpers.DecryptData((string)dic[nameof(Password)], (string)dic[String.Format(EntropyFormatter, nameof(Password))]); + } + + /// + /// Returns + /// + /// + public override string ToString() + { + return IP; + } + + /// + /// Checks if another matches one + /// + /// Another + /// if and have the same , , and + public bool Equals(RemoteLoginInfo other) + { + return other != null && IP == other.IP && Port == other.Port && Username == other.Username; + } + + /// + /// Returns a JSON representation of the with the encrypted + /// + /// A JSON representation of the + public string ToJSON() + { + //serialize it to a dic first so we can store the entropy + var serializer = new JavaScriptSerializer(); + var raw = serializer.Serialize(this); + var dic = serializer.Deserialize>(raw); + if (HasPassword) + { + dic.Add(nameof(Password), Helpers.EncryptData(Password, out string entropy)); + dic.Add(String.Format(EntropyFormatter, nameof(Password)), entropy); + } + return serializer.Serialize(dic); } } } diff --git a/TGS.Interface/ServerInterface.cs b/TGS.Interface/ServerInterface.cs index fcb7852243..142e08357d 100644 --- a/TGS.Interface/ServerInterface.cs +++ b/TGS.Interface/ServerInterface.cs @@ -402,7 +402,8 @@ namespace TGS.Interface binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.None; binding.Security.Mode = requireAuth ? SecurityMode.TransportWithMessageCredential : SecurityMode.Transport; //do not require auth for a connectivity check binding.Security.Message.ClientCredentialType = requireAuth ? MessageCredentialType.UserName : MessageCredentialType.None; - var address = new EndpointAddress(String.Format("https://{0}:{1}/{2}/{3}", LoginInfo.IP, LoginInfo.Password, accessPath, InterfaceName)); + var url = String.Format("https://{0}:{1}/{2}/{3}", LoginInfo.IP, LoginInfo.Port, accessPath, InterfaceName); + var address = new EndpointAddress(url); var res = new ChannelFactory(binding, address); if (requireAuth) { @@ -426,7 +427,7 @@ namespace TGS.Interface { GetComponentImpl(false).VerifyConnection(); } - catch (CommunicationException e) + catch (Exception e) { error = e.ToString(); return ConnectivityLevel.None;