| 1 | using System.Collections.Generic;
 | 
|---|
| 2 | using System.Xml;
 | 
|---|
| 3 | 
 | 
|---|
| 4 | namespace Webserver.Permissions {
 | 
|---|
| 5 |         public class AdminApiTokens : AdminSectionAbs {
 | 
|---|
| 6 |                 public static AdminApiTokens Instance { get; private set; }
 | 
|---|
| 7 | 
 | 
|---|
| 8 |                 public AdminApiTokens (AdminTools _parent) : base (_parent, "apitokens") {
 | 
|---|
| 9 |                         Instance = this;
 | 
|---|
| 10 |                 }
 | 
|---|
| 11 | 
 | 
|---|
| 12 |                 private readonly Dictionary<string, ApiToken> tokens = new CaseInsensitiveStringDictionary<ApiToken> ();
 | 
|---|
| 13 | 
 | 
|---|
| 14 | 
 | 
|---|
| 15 | #region IO
 | 
|---|
| 16 | 
 | 
|---|
| 17 |                 public override void Clear () {
 | 
|---|
| 18 |                         tokens.Clear ();
 | 
|---|
| 19 |                 }
 | 
|---|
| 20 | 
 | 
|---|
| 21 |                 public override void ParseElement (XmlElement _childElement) {
 | 
|---|
| 22 |                         if (ApiToken.TryParse (_childElement, out ApiToken apiToken)) {
 | 
|---|
| 23 |                                 tokens [apiToken.Name] = apiToken;
 | 
|---|
| 24 |                         }
 | 
|---|
| 25 |                 }
 | 
|---|
| 26 | 
 | 
|---|
| 27 |                 public override void Save (XmlElement _root) {
 | 
|---|
| 28 |                         XmlElement adminTokensElem = _root.AddXmlElement (SectionTypeName);
 | 
|---|
| 29 |                         adminTokensElem.AddXmlComment (" <token name=\"adminuser1\" secret=\"supersecrettoken\" permission_level=\"0\" /> ");
 | 
|---|
| 30 | 
 | 
|---|
| 31 |                         foreach ((string _, ApiToken apiToken) in tokens) {
 | 
|---|
| 32 |                                 apiToken.ToXml (adminTokensElem);
 | 
|---|
| 33 |                         }
 | 
|---|
| 34 |                 }
 | 
|---|
| 35 | 
 | 
|---|
| 36 | #endregion
 | 
|---|
| 37 | 
 | 
|---|
| 38 | 
 | 
|---|
| 39 | #region Runtime interaction
 | 
|---|
| 40 | 
 | 
|---|
| 41 |                 public void AddToken (string _name, string _secret, int _permissionLevel) {
 | 
|---|
| 42 |                         lock (Parent) {
 | 
|---|
| 43 |                                 ApiToken apiToken = new ApiToken (_name, _secret, _permissionLevel);
 | 
|---|
| 44 |                                 tokens[_name] = apiToken;
 | 
|---|
| 45 |                                 Parent.Save ();
 | 
|---|
| 46 |                         }
 | 
|---|
| 47 |                 }
 | 
|---|
| 48 | 
 | 
|---|
| 49 |                 public bool RemoveToken (string _name) {
 | 
|---|
| 50 |                         lock (Parent) {
 | 
|---|
| 51 |                                 bool removed = tokens.Remove (_name);
 | 
|---|
| 52 |                                 if (removed) {
 | 
|---|
| 53 |                                         Parent.Save ();
 | 
|---|
| 54 |                                 }
 | 
|---|
| 55 | 
 | 
|---|
| 56 |                                 return removed;
 | 
|---|
| 57 |                         }
 | 
|---|
| 58 |                 }
 | 
|---|
| 59 | 
 | 
|---|
| 60 |                 public Dictionary<string, ApiToken> GetTokens () {
 | 
|---|
| 61 |                         lock (Parent) {
 | 
|---|
| 62 |                                 return tokens;
 | 
|---|
| 63 |                         }
 | 
|---|
| 64 |                 }
 | 
|---|
| 65 | 
 | 
|---|
| 66 | #endregion
 | 
|---|
| 67 | 
 | 
|---|
| 68 |                 public readonly struct ApiToken {
 | 
|---|
| 69 |                         public readonly string Name;
 | 
|---|
| 70 |                         public readonly string Secret;
 | 
|---|
| 71 |                         public readonly int PermissionLevel;
 | 
|---|
| 72 | 
 | 
|---|
| 73 |                         public ApiToken (string _name, string _secret, int _permissionLevel) {
 | 
|---|
| 74 |                                 Name = _name;
 | 
|---|
| 75 |                                 Secret = _secret;
 | 
|---|
| 76 |                                 PermissionLevel = _permissionLevel;
 | 
|---|
| 77 |                         }
 | 
|---|
| 78 |                         
 | 
|---|
| 79 |                         public void ToXml (XmlElement _parent) {
 | 
|---|
| 80 |                                 _parent.AddXmlElement ("token")
 | 
|---|
| 81 |                                         .SetAttrib ("name", Name)
 | 
|---|
| 82 |                                         .SetAttrib ("secret", Secret)
 | 
|---|
| 83 |                                         .SetAttrib ("permission_level", PermissionLevel.ToString ());
 | 
|---|
| 84 |                         }
 | 
|---|
| 85 | 
 | 
|---|
| 86 |                         public static bool TryParse (XmlElement _element, out ApiToken _result) {
 | 
|---|
| 87 |                                 _result = default;
 | 
|---|
| 88 | 
 | 
|---|
| 89 | 
 | 
|---|
| 90 |                                 if (!_element.TryGetAttribute ("name", out string name)) {
 | 
|---|
| 91 |                                         Log.Warning ($"[Web] [Perms] Ignoring apitoken-entry because of missing 'name' attribute: {_element.OuterXml}");
 | 
|---|
| 92 |                                         return false;
 | 
|---|
| 93 |                                 }
 | 
|---|
| 94 | 
 | 
|---|
| 95 |                                 if (!_element.TryGetAttribute ("secret", out string secret)) {
 | 
|---|
| 96 |                                         Log.Warning ($"[Web] [Perms] Ignoring apitoken-entry because of missing 'secret' attribute: {_element.OuterXml}");
 | 
|---|
| 97 |                                         return false;
 | 
|---|
| 98 |                                 }
 | 
|---|
| 99 | 
 | 
|---|
| 100 |                                 if (!_element.TryGetAttribute ("permission_level", out string permissionLevelString)) {
 | 
|---|
| 101 |                                         Log.Warning ($"[Web] [Perms] Ignoring apitoken-entry because of missing 'permission_level' attribute: {_element.OuterXml}");
 | 
|---|
| 102 |                                         return false;
 | 
|---|
| 103 |                                 }
 | 
|---|
| 104 | 
 | 
|---|
| 105 |                                 if (!int.TryParse (permissionLevelString, out int permissionLevel)) {
 | 
|---|
| 106 |                                         Log.Warning (
 | 
|---|
| 107 |                                                 $"[Web] [Perms] Ignoring apitoken-entry because of invalid (non-numeric) value for 'permission_level' attribute: {_element.OuterXml}");
 | 
|---|
| 108 |                                         return false;
 | 
|---|
| 109 |                                 }
 | 
|---|
| 110 | 
 | 
|---|
| 111 |                                 
 | 
|---|
| 112 |                                 _result = new ApiToken (name, secret, permissionLevel);
 | 
|---|
| 113 |                                 return true;
 | 
|---|
| 114 |                         }
 | 
|---|
| 115 |                 }
 | 
|---|
| 116 | 
 | 
|---|
| 117 | 
 | 
|---|
| 118 | #region Specials
 | 
|---|
| 119 | 
 | 
|---|
| 120 |                 public int GetPermissionLevel (string _name, string _secret) {
 | 
|---|
| 121 |                         lock (Parent) {
 | 
|---|
| 122 |                                 if (tokens.TryGetValue (_name, out ApiToken apiToken) && apiToken.Secret == _secret) {
 | 
|---|
| 123 |                                         return apiToken.PermissionLevel;
 | 
|---|
| 124 |                                 }
 | 
|---|
| 125 | 
 | 
|---|
| 126 |                                 if (IsCommandlineToken (_name, _secret)) {
 | 
|---|
| 127 |                                         return 0;
 | 
|---|
| 128 |                                 }
 | 
|---|
| 129 | 
 | 
|---|
| 130 |                                 return int.MaxValue;
 | 
|---|
| 131 |                         }
 | 
|---|
| 132 |                 }
 | 
|---|
| 133 | 
 | 
|---|
| 134 |                 private bool commandlineChecked;
 | 
|---|
| 135 |                 private string commandlineTokenName;
 | 
|---|
| 136 |                 private string commandlineTokenSecret;
 | 
|---|
| 137 | 
 | 
|---|
| 138 |                 private bool IsCommandlineToken (string _name, string _secret) {
 | 
|---|
| 139 |                         if (!commandlineChecked) {
 | 
|---|
| 140 |                                 commandlineTokenName = GameUtils.GetLaunchArgument ("webapitokenname");
 | 
|---|
| 141 |                                 commandlineTokenSecret = GameUtils.GetLaunchArgument ("webapitokensecret");
 | 
|---|
| 142 | 
 | 
|---|
| 143 |                                 commandlineChecked = true;
 | 
|---|
| 144 |                         }
 | 
|---|
| 145 | 
 | 
|---|
| 146 |                         if (string.IsNullOrEmpty (commandlineTokenName) || string.IsNullOrEmpty (commandlineTokenSecret)) {
 | 
|---|
| 147 |                                 return false;
 | 
|---|
| 148 |                         }
 | 
|---|
| 149 | 
 | 
|---|
| 150 |                         return _name == commandlineTokenName && _secret == commandlineTokenSecret;
 | 
|---|
| 151 |                 }
 | 
|---|
| 152 | 
 | 
|---|
| 153 | #endregion
 | 
|---|
| 154 |         }
 | 
|---|
| 155 | }
 | 
|---|