Mercurial > servermonitor
diff ServerMonitor/Objects/Server.cs @ 17:68d7834dc28e
More comments.
author | Brad Greco <brad@bgreco.net> |
---|---|
date | Sat, 25 May 2019 15:14:26 -0400 |
parents | 052aa62cb42a |
children | 06ff59b59e70 |
line wrap: on
line diff
--- a/ServerMonitor/Objects/Server.cs Tue Apr 30 20:40:58 2019 -0400 +++ b/ServerMonitor/Objects/Server.cs Sat May 25 15:14:26 2019 -0400 @@ -1,17 +1,17 @@ using System; -using System.Collections.Generic; using System.Linq; using System.Text; using System.Security.Cryptography; using System.ComponentModel; using Renci.SshNet; -using System.Runtime.Serialization; using System.Xml.Serialization; namespace ServerMonitorApp { + /// <summary>Types of SSH logins supported by the server monitor.</summary> public enum LoginType { PrivateKey = 0, Password = 1 }; + /// <summary>Remote server that checks can be run against.</summary> public class Server { private string _host; @@ -24,59 +24,78 @@ private byte[] passwordHash; private PrivateKeyFile _privateKeyFile; + /// <summary>Fires when a check belonging to this server is modifed.</summary> public event EventHandler CheckModified; + /// <summary>Fires when the server enabled state changes.</summary> public event EventHandler EnabledChanged; + /// <summary>The checks that belong to this server.</summary> public readonly BindingList<Check> Checks = new BindingList<Check>(); + /// <summary>Internal ID of the server.</summary> public int Id { get; set; } + /// <summary>Name of the server.</summary> public string Name { get; set; } + /// <summary>Hostname of the server.</summary> public string Host { get { return _host; } set { _host = value; InvalidateSshConnection(); } } + /// <summary>Port to use when connecting using SSH.</summary> public int Port { get { return _port; } set { _port = value; InvalidateSshConnection(); } } + /// <summary>Username to use when connecting using SSH.</summary> public string Username { get { return _username; } set { _username = value; InvalidateSshConnection(); } } + /// <summary>Login type to use when connecting using SSH.</summary> public LoginType LoginType { get { return _loginType; } set { _loginType = value; InvalidateSshConnection(); } } + /// <summary>Path to the private key file to use when connecting using SSH.</summary> public string KeyFile { get { return _keyFile; } set { _keyFile = value; InvalidateSshConnection(); } } + /// <summary>Password to use when connecting using SSH.</summary> + /// <remarks>The password is encrypted using the current Windows user account.</remarks> public string Password { get { return passwordHash == null ? null : - Encoding.UTF8.GetString(ProtectedData.Unprotect(passwordHash, Encoding.UTF8.GetBytes("Server".Reverse().ToString()), DataProtectionScope.CurrentUser)); + Encoding.UTF8.GetString(ProtectedData.Unprotect(passwordHash + , Encoding.UTF8.GetBytes("Server".Reverse().ToString()) // Super-secure obfuscation of additional entropy + , DataProtectionScope.CurrentUser)); } set { passwordHash = ProtectedData.Protect(Encoding.UTF8.GetBytes(value), - Encoding.UTF8.GetBytes("Server".Reverse().ToString()), // Minor obfuscation of additional entropy + Encoding.UTF8.GetBytes("Server".Reverse().ToString()), // Super-secure obfuscation of additional entropy DataProtectionScope.CurrentUser); } } + /// <summary>Private key file to use when connecting using SSH.</summary> + /// <remarks> + /// If the private key file is encrypted, will be null until the user enters + /// the decryption key. + /// </remarks> [XmlIgnore] public PrivateKeyFile PrivateKeyFile { @@ -88,11 +107,18 @@ { if (_privateKeyFile == null) { + // The private key has not been opened yet. + // Disable the server until the user enters the decryption key, + // and set the KeyStatus to indicate why the server was disabled. KeyStatus = KeyStatus.Closed; Enabled = false; } else { + // The private key is open and accessible. + // Automatically re-enable the server if it was previously disabled + // due to a locked or inaccessible private key (i.e. disabled + // programatically and not by user request). if (!KeyStatus.In(KeyStatus.Open, KeyStatus.Closed)) Enabled = true; KeyStatus = KeyStatus.Open; @@ -101,13 +127,18 @@ } } + /// <summary>The current status of the private key file.</summary> public KeyStatus KeyStatus { get; set; } + /// <summary>Whether this server's checks will be automatically executed on their schedules.</summary> public bool Enabled { get { return _enabled; } set { + // Do not allow enabling the server if the private key is not accessible. + // Do not fire the EnabledChanged event if the Enabled state is not actually changing + // from its existing value. if ((LoginType == LoginType.PrivateKey && PrivateKeyFile == null && value == true) || value == _enabled) return; _enabled = value; @@ -115,14 +146,23 @@ } } - //public bool WaitingForUser { get; set; } - + /// <summary>The status of the server.</summary> + /// <remarks> + /// The status of the server is the most severe status of all its enabled checks. + /// The integer value of the CheckStatus enum increases with the severity, + /// so the maximum value of all checks gives the most severe status. + /// </remarks> public CheckStatus Status => !Enabled ? CheckStatus.Disabled : Checks .Where(c => c.Enabled) .Select(c => c.LastRunStatus) .DefaultIfEmpty(CheckStatus.Success) .Max(); + /// <summary>The SSH client to use when running checks on the server.</summary> + /// <remarks> + /// The connection is stored and kept open at the server level so it can be reused + /// by all SSH checks. + /// </remarks> public SshClient SshClient { get @@ -136,19 +176,8 @@ } } - /*public Server() { } - - public Server(Server server) - { - Name = server.Name; - Host = server.Host; - Port = server.Port; - Username = server.Username; - LoginType = server.LoginType; - KeyFile = server.KeyFile; - Enabled = server.Enabled; - }*/ - + /// <summary>Deletes a check from the server.</summary> + /// <param name="check">The check to delete.</param> public void DeleteCheck(Check check) { Checks.Remove(check); @@ -156,6 +185,8 @@ CheckModified?.Invoke(check, new EventArgs()); } + /// <summary>Validates server settings.</summary> + /// <returns>An empty string if the server is valid, or the reason the server is invalid.</returns> public string Validate() { string message = string.Empty; @@ -166,11 +197,15 @@ return message.Length > 0 ? message : null; } + /// <summary>Updates a check.</summary> public void UpdateCheck(Check check) { + // See if there is already a check with this ID. Check oldCheck = Checks.FirstOrDefault(c => c.Id == check.Id); if (!ReferenceEquals(check, oldCheck)) { + // If there is already a check, but it is a different object instance, + // replace the old check with the new one (or add it if it is new). int index = Checks.IndexOf(oldCheck); if (index == -1) Checks.Add(check); @@ -180,6 +215,7 @@ CheckModified?.Invoke(check, new EventArgs()); } + /// <summary>Returns true if the server looks empty (no user data has been entered).</summary> public bool IsEmpty() { return Name.IsNullOrEmpty() @@ -187,6 +223,7 @@ && Checks.Count == 0; } + /// <summary>Generates the authentication method based on user preferences.</summary> private AuthenticationMethod GetAuthentication() { if (LoginType == LoginType.Password) @@ -195,6 +232,7 @@ return new PrivateKeyAuthenticationMethod(Username, PrivateKeyFile); } + /// <summary>Releases the open SSH connection.</summary> private void InvalidateSshConnection() { _sshClient?.Dispose(); @@ -207,13 +245,17 @@ } } + /// <summary>Possible statuses of the private key file.</summary> public enum KeyStatus { + /// <summary>The private key file is closed for an unspecified reason.</summary> Closed, + /// <summary>The private key file is accessible and open.</summary> Open, + /// <summary>The private key file is not accessible (missing, access denied, etc).</summary> NotAccessible, + /// <summary>The private key file is encrypted and the user has not entered the password yet.</summary> NeedPassword, } - }