source: binary-improvements2/WebServer/src/WebPermissions.cs@ 402

Last change on this file since 402 was 402, checked in by alloc, 22 months ago
  • Major refactoring
  • Using Utf8Json for (de)serialization
  • Moving APIs to REST
  • Removing dependencies from WebServer and MapRenderer to ServerFixes
File size: 10.4 KB
Line 
1using System.Collections.Generic;
2using System.Collections.ObjectModel;
3using System.IO;
4using System.Xml;
5
6namespace Webserver {
7 public class WebPermissions {
8 private const string permissionsFileName = "webpermissions.xml";
9 private static WebPermissions instance;
10
11 private readonly FileSystemWatcher fileWatcher;
12
13 private readonly WebModulePermission defaultModulePermission = new WebModulePermission ("", 0);
14
15 /// <summary>
16 /// Registered user/pass admin tokens
17 /// </summary>
18 private readonly Dictionary<string, AdminToken> adminTokens = new CaseInsensitiveStringDictionary<AdminToken> ();
19
20 /// <summary>
21 /// Contains all registered modules and their default permission
22 /// </summary>
23 private readonly Dictionary<string, WebModulePermission> knownModules = new CaseInsensitiveStringDictionary<WebModulePermission> ();
24
25 /// <summary>
26 /// Manually defined module permissions
27 /// </summary>
28 private readonly Dictionary<string, WebModulePermission> modulePermissions =
29 new CaseInsensitiveStringDictionary<WebModulePermission> ();
30
31 /// <summary>
32 /// Public list of all modules, both those with custom permissions as well as those that do not with their default permission
33 /// </summary>
34 private readonly List<WebModulePermission> allModulesList = new List<WebModulePermission> ();
35
36 private readonly ReadOnlyCollection<WebModulePermission> allModulesListRo;
37
38 private static string SettingsFilePath => GamePrefs.GetString (EnumUtils.Parse<EnumGamePrefs> (nameof (EnumGamePrefs.SaveGameFolder)));
39 private static string SettingsFileName => permissionsFileName;
40 private static string SettingsFullPath => $"{SettingsFilePath}/{SettingsFileName}";
41
42 private WebPermissions () {
43 allModulesListRo = new ReadOnlyCollection<WebModulePermission> (allModulesList);
44
45 Directory.CreateDirectory (SettingsFilePath);
46
47 Load ();
48
49 fileWatcher = new FileSystemWatcher (SettingsFilePath, SettingsFileName);
50 fileWatcher.Changed += OnFileChanged;
51 fileWatcher.Created += OnFileChanged;
52 fileWatcher.Deleted += OnFileChanged;
53 fileWatcher.EnableRaisingEvents = true;
54 }
55
56 public static WebPermissions Instance {
57 get {
58 lock (typeof (WebPermissions)) {
59 return instance ??= new WebPermissions ();
60 }
61 }
62 }
63
64
65#region Admin Tokens
66
67 public void AddAdmin (string _name, string _token, int _permissionLevel, bool _save = true) {
68 AdminToken c = new AdminToken (_name, _token, _permissionLevel);
69 lock (this) {
70 adminTokens [_name] = c;
71 if (_save) {
72 Save ();
73 }
74 }
75 }
76
77 public void RemoveAdmin (string _name, bool _save = true) {
78 lock (this) {
79 adminTokens.Remove (_name);
80 if (_save) {
81 Save ();
82 }
83 }
84 }
85
86 public bool IsAdmin (string _name) {
87 return adminTokens.ContainsKey (_name);
88 }
89
90 public AdminToken[] GetAdmins () {
91 AdminToken[] result = new AdminToken[adminTokens.Count];
92 adminTokens.CopyValuesTo (result);
93 return result;
94 }
95
96 public AdminToken GetWebAdmin (string _name, string _token) {
97 if (IsAdmin (_name) && adminTokens [_name].token == _token) {
98 return adminTokens [_name];
99 }
100
101 return null;
102 }
103
104#endregion
105
106
107#region Modules
108
109 public void AddModulePermission (string _module, int _permissionLevel, bool _save = true) {
110 WebModulePermission p = new WebModulePermission (_module, _permissionLevel);
111 lock (this) {
112 allModulesList.Clear ();
113 modulePermissions [_module] = p;
114 if (_save) {
115 Save ();
116 }
117 }
118 }
119
120 public void AddKnownModule (string _module, int _defaultPermission) {
121 if (string.IsNullOrEmpty (_module)) {
122 return;
123 }
124
125 WebModulePermission p = new WebModulePermission (_module, _defaultPermission);
126
127 lock (this) {
128 allModulesList.Clear ();
129 knownModules [_module] = p;
130 }
131 }
132
133 public bool IsKnownModule (string _module) {
134 if (string.IsNullOrEmpty (_module)) {
135 return false;
136 }
137
138 lock (this) {
139 return knownModules.ContainsKey (_module);
140 }
141 }
142
143 public void RemoveModulePermission (string _module, bool _save = true) {
144 lock (this) {
145 allModulesList.Clear ();
146 modulePermissions.Remove (_module);
147 if (_save) {
148 Save ();
149 }
150 }
151 }
152
153 public IList<WebModulePermission> GetModules () {
154 if (allModulesList.Count != 0) {
155 return allModulesListRo;
156 }
157
158 foreach ((string moduleName, WebModulePermission moduleDefaultPerm) in knownModules) {
159 allModulesList.Add (modulePermissions.TryGetValue (moduleName, out WebModulePermission modulePermission)
160 ? modulePermission
161 : moduleDefaultPerm);
162 }
163
164 return allModulesListRo;
165 }
166
167 public bool ModuleAllowedWithLevel (string _module, int _level) {
168 WebModulePermission permInfo = GetModulePermission (_module);
169 return permInfo.permissionLevel >= _level;
170 }
171
172 public WebModulePermission GetModulePermission (string _module) {
173 if (modulePermissions.TryGetValue (_module, out WebModulePermission result)) {
174 return result;
175 }
176
177 return knownModules.TryGetValue (_module, out result) ? result : defaultModulePermission;
178 }
179
180#endregion
181
182
183#region IO Tasks
184
185 private void OnFileChanged (object _source, FileSystemEventArgs _e) {
186 Log.Out ($"[Web] [Perms] Reloading {SettingsFileName}");
187 Load ();
188 }
189
190 public void Load () {
191 adminTokens.Clear ();
192 modulePermissions.Clear ();
193
194 if (!File.Exists (SettingsFullPath)) {
195 Log.Out ($"[Web] [Perms] Permissions file '{SettingsFileName}' not found, creating.");
196 Save ();
197 return;
198 }
199
200 Log.Out ($"[Web] [Perms] Loading permissions file at '{SettingsFullPath}'");
201
202 XmlDocument xmlDoc = new XmlDocument ();
203
204 try {
205 xmlDoc.Load (SettingsFullPath);
206 } catch (XmlException e) {
207 Log.Error ($"[Web] [Perms] Failed loading permissions file: {e.Message}");
208 return;
209 }
210
211 XmlNode adminToolsNode = xmlDoc.DocumentElement;
212
213 if (adminToolsNode == null) {
214 Log.Error ("[Web] [Perms] Failed loading permissions file: No DocumentElement found");
215 return;
216 }
217
218 foreach (XmlNode childNode in adminToolsNode.ChildNodes) {
219 switch (childNode.Name) {
220 case "admintokens":
221 ParseAdminTokens (childNode);
222 break;
223 case "permissions":
224 ParseModulePermissions (childNode);
225 break;
226 }
227 }
228
229 Log.Out ("[Web] [Perms] Loading permissions file done.");
230 }
231
232 private void ParseAdminTokens (XmlNode _baseNode) {
233 foreach (XmlNode subChild in _baseNode.ChildNodes) {
234 if (subChild.NodeType == XmlNodeType.Comment) {
235 continue;
236 }
237
238 if (subChild.NodeType != XmlNodeType.Element) {
239 Log.Warning ($"[Web] [Perms] Unexpected XML node found in 'admintokens' section: {subChild.OuterXml}");
240 continue;
241 }
242
243 XmlElement lineItem = (XmlElement)subChild;
244
245 if (!lineItem.HasAttribute ("name")) {
246 Log.Warning ($"[Web] [Perms] Ignoring admintoken-entry because of missing 'name' attribute: {subChild.OuterXml}");
247 continue;
248 }
249
250 if (!lineItem.HasAttribute ("token")) {
251 Log.Warning ($"[Web] [Perms] Ignoring admintoken-entry because of missing 'token' attribute: {subChild.OuterXml}");
252 continue;
253 }
254
255 if (!lineItem.HasAttribute ("permission_level")) {
256 Log.Warning ($"[Web] [Perms] Ignoring admintoken-entry because of missing 'permission_level' attribute: {subChild.OuterXml}");
257 continue;
258 }
259
260 string name = lineItem.GetAttribute ("name");
261 string token = lineItem.GetAttribute ("token");
262 if (!int.TryParse (lineItem.GetAttribute ("permission_level"), out int permissionLevel)) {
263 Log.Warning (
264 $"[Web] [Perms] Ignoring admintoken-entry because of invalid (non-numeric) value for 'permission_level' attribute: {subChild.OuterXml}");
265 continue;
266 }
267
268 AddAdmin (name, token, permissionLevel, false);
269 }
270 }
271
272 private void ParseModulePermissions (XmlNode _baseNode) {
273 foreach (XmlNode subChild in _baseNode.ChildNodes) {
274 if (subChild.NodeType == XmlNodeType.Comment) {
275 continue;
276 }
277
278 if (subChild.NodeType != XmlNodeType.Element) {
279 Log.Warning ($"[Web] [Perms] Unexpected XML node found in 'permissions' section: {subChild.OuterXml}");
280 continue;
281 }
282
283 XmlElement lineItem = (XmlElement)subChild;
284
285 if (!lineItem.HasAttribute ("module")) {
286 Log.Warning ($"[Web] [Perms] Ignoring permission-entry because of missing 'module' attribute: {subChild.OuterXml}");
287 continue;
288 }
289
290 if (!lineItem.HasAttribute ("permission_level")) {
291 Log.Warning ($"[Web] [Perms] Ignoring permission-entry because of missing 'permission_level' attribute: {subChild.OuterXml}");
292 continue;
293 }
294
295 if (!int.TryParse (lineItem.GetAttribute ("permission_level"), out int permissionLevel)) {
296 Log.Warning (
297 $"[Web] [Perms] Ignoring permission-entry because of invalid (non-numeric) value for 'permission_level' attribute: {subChild.OuterXml}");
298 continue;
299 }
300
301 AddModulePermission (lineItem.GetAttribute ("module"), permissionLevel, false);
302 }
303 }
304
305 public void Save () {
306 XmlDocument xml = new XmlDocument ();
307
308 xml.CreateXmlDeclaration ();
309
310 // xml.AddXmlComment (XmlHeader);
311
312 XmlElement root = xml.AddXmlElement ("webpermissions");
313
314 // AdminTokens
315 XmlElement adminTokensElem = root.AddXmlElement ("admintokens");
316 adminTokensElem.AddXmlComment (" <token name=\"adminuser1\" token=\"supersecrettoken\" permission_level=\"0\" /> ");
317 foreach ((string _, AdminToken adminToken) in adminTokens) {
318 adminTokensElem.AddXmlElement ("token")
319 .SetAttrib ("name", adminToken.name)
320 .SetAttrib ("token", adminToken.token)
321 .SetAttrib ("permission_level", adminToken.permissionLevel.ToString ());
322 }
323
324 XmlElement modulePermissionsElem = root.AddXmlElement ("permissions");
325 foreach ((string _, WebModulePermission webModulePermission) in modulePermissions) {
326 modulePermissionsElem.AddXmlElement ("permission")
327 .SetAttrib ("module", webModulePermission.module)
328 .SetAttrib ("permission_level", webModulePermission.permissionLevel.ToString ());
329 }
330
331
332 fileWatcher.EnableRaisingEvents = false;
333 xml.Save (SettingsFullPath);
334 fileWatcher.EnableRaisingEvents = true;
335 }
336
337#endregion
338
339
340 public class AdminToken {
341 public readonly string name;
342 public readonly int permissionLevel;
343 public readonly string token;
344
345 public AdminToken (string _name, string _token, int _permissionLevel) {
346 name = _name;
347 token = _token;
348 permissionLevel = _permissionLevel;
349 }
350 }
351
352 public struct WebModulePermission {
353 public readonly string module;
354 public readonly int permissionLevel;
355
356 public WebModulePermission (string _module, int _permissionLevel) {
357 module = _module;
358 permissionLevel = _permissionLevel;
359 }
360 }
361 }
362}
Note: See TracBrowser for help on using the repository browser.