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,
     }
 
-
 }