source: TFP-WebServer/WebServer/src/Permissions/AdminWebModules.cs@ 500

Last change on this file since 500 was 487, checked in by alloc, 5 months ago

1.1.0.1 Release for V 1.0

File size: 10.7 KB
Line 
1using System;
2using System.Collections.Generic;
3using System.Xml;
4using JetBrains.Annotations;
5using UnityEngine;
6
7namespace Webserver.Permissions {
8 public class AdminWebModules : AdminSectionAbs {
9 public static AdminWebModules Instance { get; private set; }
10
11 public AdminWebModules (AdminTools _parent) : base (_parent, "webmodules") {
12 Instance = this;
13 }
14
15 private readonly Dictionary<string, WebModule> modules = new CaseInsensitiveStringDictionary<WebModule> ();
16
17
18#region IO
19
20 public override void Clear () {
21 allModulesList.Clear ();
22 modules.Clear ();
23 }
24
25 public override void ParseElement (XmlElement _childElement) {
26 allModulesList.Clear ();
27
28 if (!WebModule.TryParse (_childElement, out WebModule webModule)) {
29 return;
30 }
31
32 if (knownModules.TryGetValue (webModule.Name, out WebModule knownModule)) {
33 webModule = webModule.FixPermissionLevelsFromKnownModule (knownModule);
34 }
35
36 modules [webModule.Name] = webModule;
37 }
38
39 public override void Save (XmlElement _root) {
40 XmlElement modulesElement = _root.AddXmlElement (SectionTypeName);
41 // modulesElement.AddXmlComment (" <module name=\"adminuser1\" secret=\"supersecrettoken\" permission_level=\"0\" /> ");
42
43 foreach ((string _, WebModule module) in modules) {
44 module.ToXml (modulesElement);
45 }
46 }
47
48#endregion
49
50
51#region Runtime interaction
52
53
54 public void AddModule (WebModule _module) {
55 lock (Parent) {
56 allModulesList.Clear ();
57
58 modules [_module.Name] = _module;
59 Parent.Save ();
60 }
61 }
62
63 public bool RemoveModule (string _module) {
64 lock (Parent) {
65 allModulesList.Clear ();
66
67 bool removed = modules.Remove (_module);
68 if (removed) {
69 Parent.Save ();
70 }
71
72 return removed;
73 }
74 }
75
76 public List<WebModule> GetModules () {
77 lock (Parent) {
78 if (allModulesList.Count != 0) {
79 return allModulesList;
80 }
81
82 foreach ((string moduleName, WebModule moduleDefaultPerm) in knownModules) {
83 allModulesList.Add (modules.TryGetValue (moduleName, out WebModule modulePermission)
84 ? modulePermission
85 : moduleDefaultPerm);
86 }
87
88 return allModulesList;
89 }
90 }
91
92#endregion
93
94 public readonly struct WebModule {
95 public readonly string Name;
96 public readonly int LevelGlobal;
97 public readonly int[] LevelPerMethod;
98 public readonly bool IsDefault;
99
100 public WebModule (string _name, int _level, bool _isDefault) {
101 LevelPerMethod = null;
102
103 Name = _name;
104 LevelGlobal = _level;
105 IsDefault = _isDefault;
106 }
107
108 public WebModule (string _name, int _levelGlobal, int[] _levelPerMethod, bool _isDefault) {
109 if (_levelPerMethod != null && _levelPerMethod.Length != (int)ERequestMethod.Count) {
110 LevelPerMethod = createDefaultPerMethodArray ();
111
112 for (int i = 0; i < (int)ERequestMethod.Count; i++) {
113 if (_levelPerMethod != null && i < _levelPerMethod.Length) {
114 LevelPerMethod [i] = _levelPerMethod [i];
115 }
116 }
117 } else {
118 LevelPerMethod = _levelPerMethod;
119 }
120
121 Name = _name;
122 LevelGlobal = _levelGlobal;
123 IsDefault = _isDefault;
124 }
125
126 public void ToXml (XmlElement _parent) {
127 bool hasPerMethodLevels = LevelPerMethod != null;
128
129 XmlElement permissionElement = _parent.AddXmlElement ("module")
130 .SetAttrib ("name", Name)
131 .SetAttrib ("permission_level", LevelGlobal.ToString ());
132
133 if (!hasPerMethodLevels) {
134 return;
135 }
136
137 for (int i = 0; i < LevelPerMethod.Length; i++) {
138 ERequestMethod method = (ERequestMethod)i;
139 int level = LevelPerMethod [i];
140
141 if (level == MethodLevelNotSupported) {
142 continue;
143 }
144
145 permissionElement.AddXmlElement ("method")
146 .SetAttrib ("name", method.ToStringCached ())
147 .SetAttrib ("permission_level", level == MethodLevelInheritGlobal ? MethodLevelInheritKeyword : level.ToString ());
148 }
149 }
150
151 public static bool TryParse (XmlElement _element, out WebModule _result) {
152 _result = default;
153
154
155 if (!_element.TryGetAttribute ("name", out string name)) {
156 Log.Warning ($"[Web] [Perms] Ignoring module-entry because of missing 'name' attribute: {_element.OuterXml}");
157 return false;
158 }
159
160 if (!_element.TryGetAttribute ("permission_level", out string permissionLevelString)) {
161 Log.Warning ($"[Web] [Perms] Ignoring module-entry because of missing 'permission_level' attribute: {_element.OuterXml}");
162 return false;
163 }
164
165 if (!int.TryParse (permissionLevelString, out int permissionLevel)) {
166 Log.Warning (
167 $"[Web] [Perms] Ignoring module-entry because of invalid (non-numeric) value for 'permission_level' attribute: {_element.OuterXml}");
168 return false;
169 }
170
171 int[] perMethodLevels = null;
172
173 foreach (XmlNode child in _element.ChildNodes) {
174 if (child.NodeType != XmlNodeType.Element) {
175 continue;
176 }
177
178 XmlElement childElem = (XmlElement)child;
179 if (childElem.Name != "method") {
180 Log.Warning ($"[Web] [Perms] Ignoring module child element, invalid element name: {childElem.OuterXml}");
181 continue;
182 }
183
184 if (!childElem.TryGetAttribute ("name", out string methodName)) {
185 Log.Warning ($"[Web] [Perms] Ignoring module child element, missing 'name' attribute: {childElem.OuterXml}");
186 continue;
187 }
188
189 if (!EnumUtils.TryParse (methodName, out ERequestMethod method, true)) {
190 Log.Warning (
191 $"[Web] [Perms] Ignoring module child element, unknown method name in 'name' attribute: {childElem.OuterXml}");
192 continue;
193 }
194
195 if (method >= ERequestMethod.Count) {
196 Log.Warning (
197 $"[Web] [Perms] Ignoring module child element, invalid method name in 'name' attribute: {childElem.OuterXml}");
198 continue;
199 }
200
201 if (!childElem.TryGetAttribute ("permission_level", out permissionLevelString)) {
202 Log.Warning ($"[Web] [Perms] Ignoring module child element, missing 'permission_level' attribute: {childElem.OuterXml}");
203 continue;
204 }
205
206 int methodPermissionLevel;
207 if (permissionLevelString.EqualsCaseInsensitive (MethodLevelInheritKeyword)) {
208 methodPermissionLevel = MethodLevelInheritGlobal;
209 } else if (!int.TryParse (permissionLevelString, out methodPermissionLevel)) {
210 Log.Warning (
211 $"[Web] [Perms] Ignoring module child element, invalid (non-numeric) value for 'permission_level' attribute: {childElem.OuterXml}");
212 continue;
213 }
214
215 perMethodLevels ??= createDefaultPerMethodArray ();
216 perMethodLevels [(int)method] = methodPermissionLevel;
217 }
218
219 _result = new WebModule (name, permissionLevel, perMethodLevels, false);
220
221 return true;
222 }
223
224 [MustUseReturnValue]
225 public WebModule SetLevelGlobal (int _level) {
226 int[] perMethodClone = LevelPerMethod == null ? null : new int[LevelPerMethod.Length];
227 if (perMethodClone != null) {
228 Array.Copy (LevelPerMethod, perMethodClone, perMethodClone.Length);
229 }
230
231 return new WebModule (Name, _level, perMethodClone, false);
232 }
233
234 [MustUseReturnValue]
235 public WebModule SetLevelForMethod (ERequestMethod _method, int _level) {
236 int[] perMethodClone = createDefaultPerMethodArray ();
237 if (LevelPerMethod != null) {
238 Array.Copy (LevelPerMethod, perMethodClone, perMethodClone.Length);
239 }
240
241 perMethodClone[(int)_method] = _level;
242
243 return new WebModule (Name, LevelGlobal, perMethodClone, false);
244 }
245
246 private static int[] createDefaultPerMethodArray () {
247 int[] result = new int[(int)ERequestMethod.Count];
248
249 for (int i = 0; i < (int)ERequestMethod.Count; i++) {
250 result [i] = MethodLevelNotSupported;
251 }
252
253 return result;
254 }
255
256 [MustUseReturnValue]
257 public WebModule FixPermissionLevelsFromKnownModule (WebModule _knownModule) {
258 if (_knownModule.LevelPerMethod == null) {
259 if (LevelPerMethod != null) {
260 return new WebModule (Name, LevelGlobal, false);
261 }
262 return this;
263 }
264
265 WebModule result = this;
266 for (int i = 0; i < _knownModule.LevelPerMethod.Length; i++) {
267 if (result.LevelPerMethod == null || result.LevelPerMethod[i] == MethodLevelNotSupported) {
268 result = result.SetLevelForMethod ((ERequestMethod)i, _knownModule.LevelPerMethod[i]);
269 }
270 }
271
272 return result;
273 }
274 }
275
276
277#region Specials
278
279 /// <summary>
280 /// Use global (API based) permission level for the method
281 /// </summary>
282 public const int MethodLevelInheritGlobal = int.MinValue;
283
284 /// <summary>
285 /// Keyword used to signal inherit level on user facing levels like admins.xml or WebAPIs
286 /// </summary>
287 public const string MethodLevelInheritKeyword = "inherit";
288
289 /// <summary>
290 /// Method not supported
291 /// </summary>
292 public const int MethodLevelNotSupported = int.MinValue + 1;
293
294 public const int PermissionLevelUser = 1000;
295 public const int PermissionLevelGuest = 2000;
296
297 /// <summary>
298 /// Contains all registered modules and their default permission
299 /// </summary>
300 private readonly Dictionary<string, WebModule> knownModules = new CaseInsensitiveStringDictionary<WebModule> ();
301
302 /// <summary>
303 /// Public list of all modules, both those with custom permissions as well as those that do not with their default permission
304 /// </summary>
305 private readonly List<WebModule> allModulesList = new List<WebModule> ();
306
307 public void AddKnownModule (WebModule _module) {
308 if (!_module.IsDefault) {
309 Log.Warning ($"Call to AddKnownModule with IsDefault==false! From:\n{StackTraceUtility.ExtractStackTrace()}");
310 }
311
312 if (string.IsNullOrEmpty (_module.Name)) {
313 return;
314 }
315
316 lock (Parent) {
317 allModulesList.Clear ();
318 knownModules [_module.Name] = _module;
319
320 if (modules.TryGetValue (_module.Name, out WebModule overrideModule)) {
321 overrideModule = overrideModule.FixPermissionLevelsFromKnownModule (_module);
322 modules[_module.Name] = overrideModule;
323 }
324 }
325 }
326
327 public bool IsKnownModule (string _module) {
328 if (string.IsNullOrEmpty (_module)) {
329 return false;
330 }
331
332 lock (Parent) {
333 return knownModules.ContainsKey (_module);
334 }
335 }
336
337 public bool ModuleAllowedWithLevel (string _module, int _level) {
338 lock (Parent) {
339 return GetModule (_module).LevelGlobal >= _level;
340 }
341 }
342
343 public WebModule GetModule (string _module) {
344 lock (Parent) {
345 if (modules.TryGetValue (_module, out WebModule result)) {
346 return result;
347 }
348
349 return knownModules.TryGetValue (_module, out result) ? result : defaultModulePermission;
350 }
351 }
352
353 private readonly WebModule defaultModulePermission = new WebModule ("", 0, true);
354
355#endregion
356
357 }
358}
Note: See TracBrowser for help on using the repository browser.