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

Last change on this file since 477 was 474, checked in by alloc, 15 months ago

Added locks on AdminTools instead of local to the individual permission systems

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