using System; using System.Collections.Generic; using System.Text; using System.Xml; namespace Webserver.Permissions { public class AdminWebUsers : AdminSectionAbs { public static AdminWebUsers Instance { get; private set; } public AdminWebUsers (AdminTools _parent) : base (_parent, "webusers") { Instance = this; } private readonly Dictionary users = new CaseInsensitiveStringDictionary (); #region IO public override void Clear () { users.Clear (); } public override void ParseElement (XmlElement _childElement) { if (WebUser.TryParse (_childElement, out WebUser webUser)) { users [webUser.Name] = webUser; } } public override void Save (XmlElement _root) { XmlElement usersElement = _root.AddXmlElement (SectionTypeName); // modulesElement.AddXmlComment (" "); foreach ((string _, WebUser module) in users) { module.ToXml (usersElement); } } #endregion #region Runtime interaction public void AddUser (string _name, string _password, PlatformUserIdentifierAbs _userIdentifier, PlatformUserIdentifierAbs _crossPlatformIdentifier) { lock (Parent) { WebUser p = new WebUser (_name, _password, _userIdentifier, _crossPlatformIdentifier); // TODO: Check if another name exists with the same (crossplatform)identifier, remove that users[_name] = p; Parent.Save (); } } public bool RemoveUser (string _name) { lock (Parent) { bool removed = users.Remove (_name); if (removed) { Parent.Save (); } return removed; } } public Dictionary GetUsers () { lock (Parent) { return users; } } #endregion public readonly struct WebUser { public readonly string Name; public readonly byte[] PasswordHash; public readonly PlatformUserIdentifierAbs PlatformUser; public readonly PlatformUserIdentifierAbs CrossPlatformUser; public WebUser (string _name, byte[] _passwordHash, PlatformUserIdentifierAbs _platformUser, PlatformUserIdentifierAbs _crossPlatformUser) { Name = _name; PasswordHash = _passwordHash; PlatformUser = _platformUser; CrossPlatformUser = _crossPlatformUser; } public WebUser (string _name, string _password, PlatformUserIdentifierAbs _platformUser, PlatformUserIdentifierAbs _crossPlatformUser) { Name = _name; PasswordHash = Hash (_password); PlatformUser = _platformUser; CrossPlatformUser = _crossPlatformUser; } public void ToXml (XmlElement _parent) { XmlElement elem = _parent.AddXmlElement ("user"); elem.SetAttrib ("name", Name) .SetAttrib ("pass", Convert.ToBase64String(PasswordHash)); PlatformUser.ToXml (elem); CrossPlatformUser?.ToXml (elem, "cross"); } public static bool TryParse (XmlElement _element, out WebUser _result) { _result = default; if (!_element.TryGetAttribute ("name", out string name)) { Log.Warning ($"[Web] [Perms] Ignoring user-entry because of missing 'name' attribute: {_element.OuterXml}"); return false; } if (!_element.TryGetAttribute ("pass", out string passHashString)) { Log.Warning ($"[Web] [Perms] Ignoring user-entry because of missing 'pass' attribute: {_element.OuterXml}"); return false; } PlatformUserIdentifierAbs userIdentifier = PlatformUserIdentifierAbs.FromXml (_element, false); if (userIdentifier == null) { Log.Warning ($"[Web] [Perms] Ignoring user-entry because of missing 'platform' or 'userid' attribute: {_element.OuterXml}"); return false; } PlatformUserIdentifierAbs crossIdentifier = PlatformUserIdentifierAbs.FromXml (_element, false, "cross"); byte[] passHash = Convert.FromBase64String (passHashString); _result = new WebUser (name, passHash, userIdentifier, crossIdentifier); return true; } public bool ValidatePassword (string _password) { byte[] input = Hash (_password); return Utils.ArrayEquals (input, PasswordHash); } } private static byte[] Hash (string _input) { System.Security.Cryptography.MD5Cng hasherMD5 = new System.Security.Cryptography.MD5Cng(); return hasherMD5.ComputeHash (Encoding.UTF8.GetBytes (_input)); } public bool TryGetUser (string _name, string _password, out WebUser _result) { lock (Parent) { if (users.TryGetValue (_name, out _result) && _result.ValidatePassword (_password)) { return true; } _result = default; return false; } } public bool HasUser (PlatformUserIdentifierAbs _platformUser, PlatformUserIdentifierAbs _crossPlatformUser, out WebUser _result) { lock (Parent) { _result = default; foreach ((string _, WebUser webUser) in users) { if (!Equals (webUser.PlatformUser, _platformUser) || !Equals (webUser.CrossPlatformUser, _crossPlatformUser)) { continue; } _result = webUser; return true; } return false; } } } }