using System.Collections.Generic; using System.Xml; namespace Webserver.Permissions { public class AdminApiTokens : AdminSectionAbs { public static AdminApiTokens Instance { get; private set; } public AdminApiTokens (AdminTools _parent) : base (_parent, "apitokens") { Instance = this; } private readonly Dictionary tokens = new CaseInsensitiveStringDictionary (); #region IO public override void Clear () { tokens.Clear (); } protected override void ParseElement (XmlElement _childElement) { if (ApiToken.TryParse (_childElement, out ApiToken apiToken)) { tokens [apiToken.Name] = apiToken; } } public override void Save (XmlElement _root) { XmlElement adminTokensElem = _root.AddXmlElement (SectionTypeName); adminTokensElem.AddXmlComment (" "); foreach ((string _, ApiToken apiToken) in tokens) { apiToken.ToXml (adminTokensElem); } } #endregion #region Runtime interaction public void AddToken (string _name, string _secret, int _permissionLevel) { lock (Parent) { ApiToken apiToken = new ApiToken (_name, _secret, _permissionLevel); tokens[_name] = apiToken; Parent.Save (); } } public bool RemoveToken (string _name) { lock (Parent) { bool removed = tokens.Remove (_name); if (removed) { Parent.Save (); } return removed; } } public Dictionary GetTokens () { lock (Parent) { return tokens; } } #endregion public readonly struct ApiToken { public readonly string Name; public readonly string Secret; public readonly int PermissionLevel; public ApiToken (string _name, string _secret, int _permissionLevel) { Name = _name; Secret = _secret; PermissionLevel = _permissionLevel; } public void ToXml (XmlElement _parent) { _parent.AddXmlElement ("token") .SetAttrib ("name", Name) .SetAttrib ("secret", Secret) .SetAttrib ("permission_level", PermissionLevel.ToString ()); } public static bool TryParse (XmlElement _element, out ApiToken _result) { _result = default; if (!_element.TryGetAttribute ("name", out string name)) { Log.Warning ($"[Web] [Perms] Ignoring apitoken-entry because of missing 'name' attribute: {_element.OuterXml}"); return false; } if (!_element.TryGetAttribute ("secret", out string secret)) { Log.Warning ($"[Web] [Perms] Ignoring apitoken-entry because of missing 'secret' attribute: {_element.OuterXml}"); return false; } if (!_element.TryGetAttribute ("permission_level", out string permissionLevelString)) { Log.Warning ($"[Web] [Perms] Ignoring apitoken-entry because of missing 'permission_level' attribute: {_element.OuterXml}"); return false; } if (!int.TryParse (permissionLevelString, out int permissionLevel)) { Log.Warning ( $"[Web] [Perms] Ignoring apitoken-entry because of invalid (non-numeric) value for 'permission_level' attribute: {_element.OuterXml}"); return false; } _result = new ApiToken (name, secret, permissionLevel); return true; } } #region Specials public int GetPermissionLevel (string _name, string _secret) { lock (Parent) { if (tokens.TryGetValue (_name, out ApiToken apiToken) && apiToken.Secret == _secret) { return apiToken.PermissionLevel; } if (IsCommandlineToken (_name, _secret)) { return 0; } return int.MaxValue; } } private bool commandlineChecked; private string commandlineTokenName; private string commandlineTokenSecret; private bool IsCommandlineToken (string _name, string _secret) { if (!commandlineChecked) { commandlineTokenName = GameUtils.GetLaunchArgument ("webapitokenname"); commandlineTokenSecret = GameUtils.GetLaunchArgument ("webapitokensecret"); commandlineChecked = true; } if (string.IsNullOrEmpty (commandlineTokenName) || string.IsNullOrEmpty (commandlineTokenSecret)) { return false; } return _name == commandlineTokenName && _secret == commandlineTokenSecret; } #endregion } }