source: binary-improvements2/WebServer/src/WebAPI/AbsRestApi.cs@ 428

Last change on this file since 428 was 426, checked in by alloc, 19 months ago

*Updated web permissions system
*Fixed webpermissions command
*Moved API "webmods" to "mods", also lists non-webmod mods

File size: 7.9 KB
RevLine 
[391]1using System;
[402]2using System.Collections.Generic;
[391]3using System.IO;
4using System.Net;
[402]5using Utf8Json;
[418]6using Webserver.Permissions;
[391]7
8namespace Webserver.WebAPI {
9 public abstract class AbsRestApi : AbsWebAPI {
[394]10 private static readonly UnityEngine.Profiling.CustomSampler jsonDeserializeSampler = UnityEngine.Profiling.CustomSampler.Create ("JSON_Deserialize");
11
[410]12 protected AbsRestApi (string _name = null) : this(null, _name) {
13 }
14
15 protected AbsRestApi (Web _parentWeb, string _name = null) : base(_parentWeb, _name) {
16 }
17
[418]18 protected override void RegisterPermissions () {
[426]19 AdminWebModules.Instance.AddKnownModule (new AdminWebModules.WebModule (CachedApiModuleName, DefaultPermissionLevel (),
20 DefaultMethodPermissionLevels (), true));
[418]21 }
22
[391]23 public sealed override void HandleRequest (RequestContext _context) {
[402]24 IDictionary<string, object> inputJson = null;
25 byte[] jsonInputData = null;
26
[391]27 if (_context.Request.HasEntityBody) {
[402]28 Stream requestInputStream = _context.Request.InputStream;
29
30 jsonInputData = new byte[_context.Request.ContentLength64];
31 requestInputStream.Read (jsonInputData, 0, (int)_context.Request.ContentLength64);
32
33 try {
34 jsonDeserializeSampler.Begin ();
35 inputJson = JsonSerializer.Deserialize<IDictionary<string, object>> (jsonInputData);
36
37 // Log.Out ("JSON body:");
38 // foreach ((string key, object value) in inputJson) {
39 // Log.Out ($" - {key} = {value} ({value.GetType ()})");
40 // }
41
42 jsonDeserializeSampler.End ();
43 } catch (Exception e) {
44 jsonDeserializeSampler.End ();
[391]45
[402]46 SendErrorResult (_context, HttpStatusCode.BadRequest, null, "INVALID_BODY", e);
47 return;
[391]48 }
49 }
50
51 try {
[418]52 switch (_context.Method) {
53 case ERequestMethod.GET:
[402]54 if (inputJson != null) {
55 SendErrorResult (_context, HttpStatusCode.BadRequest, jsonInputData, "GET_WITH_BODY");
[391]56 return;
57 }
58
59 HandleRestGet (_context);
60 return;
[418]61 case ERequestMethod.POST:
[391]62 if (!string.IsNullOrEmpty (_context.RequestPath)) {
[402]63 SendErrorResult (_context, HttpStatusCode.BadRequest, jsonInputData, "POST_WITH_ID");
[391]64 return;
65 }
66
[402]67 if (inputJson == null) {
68 SendErrorResult (_context, HttpStatusCode.BadRequest, null, "POST_WITHOUT_BODY");
[391]69 return;
70 }
71
[402]72 HandleRestPost (_context, inputJson, jsonInputData);
[391]73 return;
[418]74 case ERequestMethod.PUT:
[391]75 if (string.IsNullOrEmpty (_context.RequestPath)) {
[402]76 SendErrorResult (_context, HttpStatusCode.BadRequest, jsonInputData, "PUT_WITHOUT_ID");
[391]77 return;
78 }
79
[402]80 if (inputJson == null) {
81 SendErrorResult (_context, HttpStatusCode.BadRequest, null, "PUT_WITHOUT_BODY");
[391]82 return;
83 }
84
[402]85 HandleRestPut (_context, inputJson, jsonInputData);
[391]86 return;
[418]87 case ERequestMethod.DELETE:
[391]88 if (string.IsNullOrEmpty (_context.RequestPath)) {
[402]89 SendErrorResult (_context, HttpStatusCode.BadRequest, jsonInputData, "DELETE_WITHOUT_ID");
[391]90 return;
91 }
92
[402]93 if (inputJson != null) {
94 SendErrorResult (_context, HttpStatusCode.BadRequest, null, "DELETE_WITH_BODY");
[391]95 return;
96 }
97
98 HandleRestDelete (_context);
99 return;
100 default:
[402]101 SendErrorResult (_context, HttpStatusCode.BadRequest, null, "INVALID_METHOD");
[391]102 return;
103 }
104 } catch (Exception e) {
[402]105 SendErrorResult (_context, HttpStatusCode.InternalServerError, jsonInputData, "ERROR_PROCESSING", e);
[391]106 }
107 }
108
[404]109 protected virtual void HandleRestGet (RequestContext _context) {
110 SendErrorResult (_context, HttpStatusCode.MethodNotAllowed, null, "Unsupported");
111 }
112
113 protected virtual void HandleRestPost (RequestContext _context, IDictionary<string, object> _jsonInput, byte[] _jsonInputData) {
114 SendErrorResult (_context, HttpStatusCode.MethodNotAllowed, _jsonInputData, "Unsupported");
115 }
116
117 protected virtual void HandleRestPut (RequestContext _context, IDictionary<string, object> _jsonInput, byte[] _jsonInputData) {
118 SendErrorResult (_context, HttpStatusCode.MethodNotAllowed, _jsonInputData, "Unsupported");
119 }
120
121 protected virtual void HandleRestDelete (RequestContext _context) {
122 SendErrorResult (_context, HttpStatusCode.MethodNotAllowed, null, "Unsupported");
123 }
124
[418]125 public override bool Authorized (RequestContext _context) {
[426]126 AdminWebModules.WebModule module = AdminWebModules.Instance.GetModule (CachedApiModuleName);
[404]127
[426]128 if (module.LevelPerMethod != null) {
129 int perMethodLevel = module.LevelPerMethod [(int)_context.Method];
130 if (perMethodLevel == AdminWebModules.MethodLevelNotSupported) {
131 return false;
132 }
[418]133
[426]134 if (perMethodLevel != AdminWebModules.MethodLevelInheritGlobal) {
135 return perMethodLevel >= _context.PermissionLevel;
136 }
[418]137 }
138
[426]139 return module.LevelGlobal >= _context.PermissionLevel;
140 }
[418]141
[426]142 /// <summary>
143 /// Define default permission levels per HTTP method as an array of levels. See <see cref="ERequestMethod"/> for the order of entries.
144 /// </summary>
145 /// <returns>Default permission levels for supported methods. See <see cref="AdminWebModules.MethodLevelNotSupported"/> and <see cref="AdminWebModules.MethodLevelInheritGlobal"/>.</returns>
146 public virtual int[] DefaultMethodPermissionLevels () => new[] {
147 AdminWebModules.MethodLevelNotSupported,
148 AdminWebModules.MethodLevelInheritGlobal,
149 AdminWebModules.MethodLevelInheritGlobal,
150 AdminWebModules.MethodLevelInheritGlobal,
151 AdminWebModules.MethodLevelInheritGlobal
152 };
[418]153
[404]154#region Helpers
155
[402]156 protected static readonly byte[] JsonEmptyData;
157
[418]158 static AbsRestApi () {
159 JsonWriter writer = new JsonWriter ();
160 writer.WriteBeginArray ();
161 writer.WriteEndArray ();
162 JsonEmptyData = writer.ToUtf8ByteArray ();
163 }
164
[404]165 protected static void PrepareEnvelopedResult (out JsonWriter _writer) {
[402]166 WebUtils.PrepareEnvelopedResult (out _writer);
167 }
[391]168
[404]169 protected static void SendEnvelopedResult (RequestContext _context, ref JsonWriter _writer, HttpStatusCode _statusCode = HttpStatusCode.OK,
[402]170 byte[] _jsonInputData = null, string _errorCode = null, Exception _exception = null) {
171
172 WebUtils.SendEnvelopedResult (_context, ref _writer, _statusCode, _jsonInputData, _errorCode, _exception);
[391]173 }
174
[404]175 protected static void SendErrorResult (RequestContext _context, HttpStatusCode _statusCode, byte[] _jsonInputData = null, string _errorCode = null, Exception _exception = null) {
176 PrepareEnvelopedResult (out JsonWriter writer);
177 writer.WriteRaw (JsonEmptyData);
178 SendEnvelopedResult (_context, ref writer, _statusCode, _jsonInputData, _errorCode, _exception);
179 }
180
181 protected static bool TryGetJsonField (IDictionary<string, object> _jsonObject, string _fieldName, out int _value) {
[391]182 _value = default;
183
[402]184 if (!_jsonObject.TryGetValue (_fieldName, out object fieldNode)) {
[391]185 return false;
186 }
187
[402]188 if (fieldNode is not double value) {
[391]189 return false;
190 }
191
192 try {
[402]193 _value = (int)value;
[391]194 return true;
195 } catch (Exception) {
196 return false;
197 }
198 }
199
[404]200 protected static bool TryGetJsonField (IDictionary<string, object> _jsonObject, string _fieldName, out double _value) {
[391]201 _value = default;
202
[402]203 if (!_jsonObject.TryGetValue (_fieldName, out object fieldNode)) {
[391]204 return false;
205 }
206
[402]207 if (fieldNode is not double value) {
[391]208 return false;
209 }
210
211 try {
[402]212 _value = value;
[391]213 return true;
214 } catch (Exception) {
215 return false;
216 }
217 }
218
[404]219 protected static bool TryGetJsonField (IDictionary<string, object> _jsonObject, string _fieldName, out string _value) {
[391]220 _value = default;
221
[402]222 if (!_jsonObject.TryGetValue (_fieldName, out object fieldNode)) {
[391]223 return false;
224 }
225
[402]226 if (fieldNode is not string value) {
[391]227 return false;
228 }
229
230 try {
[402]231 _value = value;
[391]232 return true;
233 } catch (Exception) {
234 return false;
235 }
236 }
[404]237
[391]238
[404]239#endregion
[391]240 }
241}
Note: See TracBrowser for help on using the repository browser.