source: binary-improvements2/WebServer/src/Permissions/AdminWebModules.cs@ 434

Last change on this file since 434 was 434, checked in by alloc, 18 months ago

Added permission management APIs

File size: 8.5 KB
Line 
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
42 public void AddModule (WebModule _module) {
43 lock (this) {
44 allModulesList.Clear ();
45
46 modules [_module.Name] = _module;
47 Parent.Save ();
48 }
49 }
50
51 public bool RemoveModule (string _module) {
52 lock (this) {
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 () {
65 lock (this) {
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
81
82 public struct WebModule {
83 private const string methodLevelInheritKeyword = "inherit";
84
85 public string Name;
86 public int LevelGlobal;
87 public int[] LevelPerMethod;
88 public bool IsDefault;
89
90 public WebModule (string _name, int _level, bool _isDefault = false) {
91 LevelPerMethod = null;
92
93 Name = _name;
94 LevelGlobal = _level;
95 IsDefault = _isDefault;
96 }
97
98 public WebModule (string _name, int _levelGlobal, int[] _levelPerMethod, bool _isDefault = false) {
99 if (_levelPerMethod == null || _levelPerMethod.Length != (int)ERequestMethod.Count) {
100 LevelPerMethod = createDefaultPerMethodArray ();
101
102 for (int i = 0; i < (int)ERequestMethod.Count; i++) {
103 if (_levelPerMethod != null && i < _levelPerMethod.Length) {
104 LevelPerMethod [i] = _levelPerMethod [i];
105 }
106 }
107 } else {
108 LevelPerMethod = _levelPerMethod;
109 }
110
111 Name = _name;
112 LevelGlobal = _levelGlobal;
113 IsDefault = _isDefault;
114 }
115
116 public void ToXml (XmlElement _parent) {
117 bool hasPerMethodLevels = LevelPerMethod != null;
118
119 XmlElement permissionElement = _parent.AddXmlElement ("module")
120 .SetAttrib ("name", Name)
121 .SetAttrib ("permission_level", LevelGlobal.ToString ());
122
123 if (!hasPerMethodLevels) {
124 return;
125 }
126
127 for (int i = 0; i < LevelPerMethod.Length; i++) {
128 ERequestMethod method = (ERequestMethod)i;
129 int level = LevelPerMethod [i];
130
131 if (level == MethodLevelNotSupported) {
132 continue;
133 }
134
135 permissionElement.AddXmlElement ("method")
136 .SetAttrib ("name", method.ToStringCached ())
137 .SetAttrib ("permission_level", level == MethodLevelInheritGlobal ? methodLevelInheritKeyword : level.ToString ());
138 }
139 }
140
141 public static bool TryParse (XmlElement _element, out WebModule _result) {
142 _result = default;
143
144
145 if (!_element.TryGetAttribute ("name", out string name)) {
146 Log.Warning ($"[Web] [Perms] Ignoring module-entry because of missing 'name' attribute: {_element.OuterXml}");
147 return false;
148 }
149
150 if (!_element.TryGetAttribute ("permission_level", out string permissionLevelString)) {
151 Log.Warning ($"[Web] [Perms] Ignoring module-entry because of missing 'permission_level' attribute: {_element.OuterXml}");
152 return false;
153 }
154
155 if (!int.TryParse (permissionLevelString, out int permissionLevel)) {
156 Log.Warning (
157 $"[Web] [Perms] Ignoring module-entry because of invalid (non-numeric) value for 'permission_level' attribute: {_element.OuterXml}");
158 return false;
159 }
160
161 int[] perMethodLevels = null;
162
163 foreach (XmlNode child in _element.ChildNodes) {
164 if (child.NodeType != XmlNodeType.Element) {
165 continue;
166 }
167
168 XmlElement childElem = (XmlElement)child;
169 if (childElem.Name != "method") {
170 Log.Warning ($"[Web] [Perms] Ignoring module child element, invalid element name: {childElem.OuterXml}");
171 continue;
172 }
173
174 if (!childElem.TryGetAttribute ("name", out string methodName)) {
175 Log.Warning ($"[Web] [Perms] Ignoring module child element, missing 'name' attribute: {childElem.OuterXml}");
176 continue;
177 }
178
179 if (!EnumUtils.TryParse (methodName, out ERequestMethod method, true)) {
180 Log.Warning (
181 $"[Web] [Perms] Ignoring module child element, unknown method name in 'name' attribute: {childElem.OuterXml}");
182 continue;
183 }
184
185 if (method >= ERequestMethod.Count) {
186 Log.Warning (
187 $"[Web] [Perms] Ignoring module child element, invalid method name in 'name' attribute: {childElem.OuterXml}");
188 continue;
189 }
190
191 if (!childElem.TryGetAttribute ("permission_level", out permissionLevelString)) {
192 Log.Warning ($"[Web] [Perms] Ignoring module child element, missing 'permission_level' attribute: {childElem.OuterXml}");
193 continue;
194 }
195
196 int methodPermissionLevel;
197 if (permissionLevelString.EqualsCaseInsensitive (methodLevelInheritKeyword)) {
198 methodPermissionLevel = MethodLevelInheritGlobal;
199 } else if (!int.TryParse (permissionLevelString, out methodPermissionLevel)) {
200 Log.Warning (
201 $"[Web] [Perms] Ignoring module child element, invalid (non-numeric) value for 'permission_level' attribute: {childElem.OuterXml}");
202 continue;
203 }
204
205 perMethodLevels ??= createDefaultPerMethodArray ();
206 perMethodLevels [(int)method] = methodPermissionLevel;
207 }
208
209 _result = perMethodLevels != null ? new WebModule (name, permissionLevel, perMethodLevels) : new WebModule (name, permissionLevel);
210
211 return true;
212 }
213
214 private static int[] createDefaultPerMethodArray () {
215 int[] result = new int[(int)ERequestMethod.Count];
216
217 for (int i = 0; i < (int)ERequestMethod.Count; i++) {
218 result [i] = MethodLevelNotSupported;
219 }
220
221 return result;
222 }
223 }
224
225
226#region Specials
227
228 /// <summary>
229 /// Use global (API based) permission level for the method
230 /// </summary>
231 public const int MethodLevelInheritGlobal = int.MinValue;
232
233 /// <summary>
234 /// Method not supported
235 /// </summary>
236 public const int MethodLevelNotSupported = int.MinValue + 1;
237
238 public const int PermissionLevelUser = 1000;
239 public const int PermissionLevelGuest = 2000;
240
241 /// <summary>
242 /// Contains all registered modules and their default permission
243 /// </summary>
244 private readonly Dictionary<string, WebModule> knownModules = new CaseInsensitiveStringDictionary<WebModule> ();
245
246 /// <summary>
247 /// Public list of all modules, both those with custom permissions as well as those that do not with their default permission
248 /// </summary>
249 private readonly List<WebModule> allModulesList = new List<WebModule> ();
250
251 public void AddKnownModule (WebModule _module) {
252 if (string.IsNullOrEmpty (_module.Name)) {
253 return;
254 }
255
256 _module.IsDefault = true;
257
258 lock (this) {
259 allModulesList.Clear ();
260 knownModules [_module.Name] = _module;
261 }
262 }
263
264 public bool IsKnownModule (string _module) {
265 if (string.IsNullOrEmpty (_module)) {
266 return false;
267 }
268
269 lock (this) {
270 return knownModules.ContainsKey (_module);
271 }
272 }
273
274 public bool ModuleAllowedWithLevel (string _module, int _level) {
275 return GetModule (_module).LevelGlobal >= _level;
276 }
277
278 public WebModule GetModule (string _module) {
279 if (modules.TryGetValue (_module, out WebModule result)) {
280 return result;
281 }
282
283 return knownModules.TryGetValue (_module, out result) ? result : defaultModulePermission;
284 }
285
286 private readonly WebModule defaultModulePermission = new WebModule ("", 0, true);
287
288#endregion
289 }
290}
Note: See TracBrowser for help on using the repository browser.