using System;
using System.Collections.Generic;
using Crestron.SimplSharp;
namespace PepperDash.Core.PasswordManagement
{
///
/// Allows passwords to be stored and managed
///
public class PasswordManager
{
///
/// Public dictionary of known passwords
///
public static Dictionary Passwords = new Dictionary();
///
/// Indicates whether the manager has been initialized
///
public static bool IsInitialized { get; private set; }
///
/// Tracks keys changed during the debounce window for notification
///
private readonly List _pendingChanges = new List();
///
/// Timer used to wait until password changes have stopped before updating the dictionary
///
CTimer _passwordTimer;
///
/// Timer length
///
public long PasswordTimerElapsedMs = 5000;
///
/// Boolean event
///
public event EventHandler BoolChange;
///
/// Ushort event
///
public event EventHandler UshrtChange;
///
/// String event
///
public event EventHandler StringChange;
///
/// Event to notify clients of an updated password at the specified index (uint)
///
public static event EventHandler PasswordChange;
///
/// Event to notify clients when the manager has been initialized
///
public static event EventHandler Initialized;
///
/// Constructor (empty constructor required by S+)
///
public PasswordManager()
{
}
///
/// Initialize password manager
///
public void Initialize()
{
if (Passwords == null)
{
Passwords = new Dictionary();
}
OnBoolChange(true, 0, PasswordConstants.Initialized);
IsInitialized = true;
var handler = Initialized;
if (handler != null)
handler(this, new BoolChangeEventArgs(true, PasswordConstants.Initialized));
}
///
/// Updates password stored in the dictonary
///
///
///
public void UpdatePassword(string key, string password)
{
// validate the parameters
if (string.IsNullOrEmpty(key) || string.IsNullOrEmpty(password))
{
Debug.Console(1, string.Format("PasswordManager.UpdatePassword: key [{0}] or password are not valid", key, password));
return;
}
try
{
Passwords[key] = password;
if (!_pendingChanges.Contains(key))
_pendingChanges.Add(key);
Debug.Console(1, string.Format("PasswordManager.UpdatePassword: Passwords[{0}] = {1}", key, Passwords[key]));
if (_passwordTimer == null)
{
_passwordTimer = new CTimer((o) => PasswordTimerElapsed(), PasswordTimerElapsedMs);
Debug.Console(1, string.Format("PasswordManager.UpdatePassword: CTimer Started"));
OnBoolChange(true, 0, PasswordConstants.UpdateBusy);
}
else
{
_passwordTimer.Reset(PasswordTimerElapsedMs);
Debug.Console(1, string.Format("PasswordManager.UpdatePassword: CTimer Reset"));
}
}
catch (Exception e)
{
var msg = string.Format("PasswordManager.UpdatePassword key-value[{0}, {1}] failed:\r{2}", key, password, e);
Debug.Console(1, msg);
}
}
///
/// Helper method for debugging to see what passwords are in the lists
///
public void ListEntries()
{
Debug.Console(0, "PasswordManager.ListEntries:\r");
foreach (var pw in Passwords)
{
var maskedValue = string.IsNullOrEmpty(pw.Value) ? string.Empty : new string('*', pw.Value.Length);
Debug.Console(0, "Passwords[{0}]: {1}\r", pw.Key, maskedValue);
}
}
///
/// Validates the username against the known entries
///
///
public static PasswordValidationResult ValidateUsername(string username)
{
if (Passwords == null)
return PasswordValidationResult.Failure("No entires defined, verify list is initilaized");
if (string.IsNullOrEmpty(username))
return PasswordValidationResult.Failure("Username or password is empty");
string storedPassword;
return !Passwords.TryGetValue(username, out storedPassword)
? PasswordValidationResult.Failure(string.Format("Username '{0}' not found", username))
: PasswordValidationResult.Success("Valid username", storedPassword);
}
///
/// Validates the username password against the known entries
///
///
///
public static PasswordValidationResult ValidateUsernameAndPassword(string username, string password)
{
if (Passwords == null)
return PasswordValidationResult.Failure("No entires defined, verify list is initilaized");
if (string.IsNullOrEmpty(username))
return PasswordValidationResult.Failure("Username is empty or null");
if(string.IsNullOrEmpty(password))
return PasswordValidationResult.Failure("Password is empty or null");
string storedPassword;
if (!Passwords.TryGetValue(username, out storedPassword))
return PasswordValidationResult.Failure(string.Format("Username '{0}' not found", username));
return !string.Equals(storedPassword, password, StringComparison.Ordinal)
? PasswordValidationResult.Failure("Invalid password, verify password and try again")
: PasswordValidationResult.Success("Valid credentials, login was successfull", storedPassword);
}
///
/// CTimer callback function
///
private void PasswordTimerElapsed()
{
try
{
_passwordTimer.Stop();
Debug.Console(1, string.Format("PasswordManager.PasswordTimerElapsed: CTimer Stopped"));
OnBoolChange(false, 0, PasswordConstants.UpdateBusy);
foreach (var key in _pendingChanges)
{
string value;
if (!Passwords.TryGetValue(key, out value)) continue;
Debug.Console(1, string.Format("PasswordManager.PasswordTimerElapsed: Notifying change for [{0}]", key));
OnPasswordChange(key, 0, PasswordConstants.PasswordUpdated);
}
_pendingChanges.Clear();
OnUshrtChange((ushort)Passwords.Count, 0, PasswordConstants.Count);
}
catch (Exception e)
{
var msg = string.Format("PasswordManager.PasswordTimerElapsed failed:\r{0}", e);
Debug.Console(1, msg);
}
}
///
/// Method to change the default timer value, (default 5000ms/5s)
///
///
public void PasswordTimerMs(ushort time)
{
PasswordTimerElapsedMs = Convert.ToInt64(time);
}
#region event handlers
///
/// Protected boolean change event handler
///
///
///
///
protected void OnBoolChange(bool state, ushort index, ushort type)
{
var handler = BoolChange;
if (handler == null) return;
var args = new BoolChangeEventArgs(state, type) {Index = index};
BoolChange(this, args);
}
///
/// Protected ushort change event handler
///
///
///
///
protected void OnUshrtChange(ushort value, ushort index, ushort type)
{
var handler = UshrtChange;
if (handler == null) return;
var args = new UshrtChangeEventArgs(value, type) {Index = index};
UshrtChange(this, args);
}
///
/// Protected string change event handler
///
///
///
///
protected void OnStringChange(string value, ushort index, ushort type)
{
var handler = StringChange;
if (handler == null) return;
var args = new StringChangeEventArgs(value, type) {Index = index};
StringChange(this, args);
}
///
/// Protected password change event handler
///
///
///
///
protected void OnPasswordChange(string value, ushort index, ushort type)
{
var handler = PasswordChange;
if (handler == null) return;
var args = new StringChangeEventArgs(value, type) {Index = index};
PasswordChange(this, args);
}
#endregion
}
}