Ignore:
Timestamp:
Feb 27, 2023, 9:40:12 PM (21 months ago)
Author:
alloc
Message:

Refactored API authorization to support per-HTTP-method permission levels

File:
1 edited

Legend:

Unmodified
Added
Removed
  • binary-improvements2/WebServer/src/WebAPI/AbsRestApi.cs

    r410 r418  
    44using System.Net;
    55using Utf8Json;
     6using Webserver.Permissions;
    67
    78namespace Webserver.WebAPI {
     
    910                private static readonly UnityEngine.Profiling.CustomSampler jsonDeserializeSampler = UnityEngine.Profiling.CustomSampler.Create ("JSON_Deserialize");
    1011
     12                protected readonly string[] CachedPerMethodModuleNames = new string[(int)ERequestMethod.Count];
     13
    1114                protected AbsRestApi (string _name = null) : this(null, _name) {
    1215                }
    1316
    1417                protected AbsRestApi (Web _parentWeb, string _name = null) : base(_parentWeb, _name) {
     18                }
     19
     20                protected override void RegisterPermissions () {
     21                        base.RegisterPermissions ();
     22                       
     23                        for (int i = 0; i < (int)ERequestMethod.Count; i++) {
     24                                ERequestMethod method = (ERequestMethod)i;
     25
     26                                if (method is not (ERequestMethod.GET or ERequestMethod.PUT or ERequestMethod.POST or ERequestMethod.DELETE)) {
     27                                        continue;
     28                                }
     29
     30                                CachedPerMethodModuleNames [i] = $"webapi.{Name}:{method.ToStringCached ()}";
     31                                AdminWebModules.Instance.AddKnownModule (CachedPerMethodModuleNames [i], DefaultMethodPermissionLevel (method));
     32                        }
    1533                }
    1634
     
    4462
    4563                        try {
    46                                 switch (_context.Request.HttpMethod) {
    47                                         case "GET":
     64                                switch (_context.Method) {
     65                                        case ERequestMethod.GET:
    4866                                                if (inputJson != null) {
    4967                                                        SendErrorResult (_context, HttpStatusCode.BadRequest, jsonInputData, "GET_WITH_BODY");
     
    5371                                                HandleRestGet (_context);
    5472                                                return;
    55                                         case "POST":
     73                                        case ERequestMethod.POST:
    5674                                                if (!string.IsNullOrEmpty (_context.RequestPath)) {
    5775                                                        SendErrorResult (_context, HttpStatusCode.BadRequest, jsonInputData, "POST_WITH_ID");
     
    6684                                                HandleRestPost (_context, inputJson, jsonInputData);
    6785                                                return;
    68                                         case "PUT":
     86                                        case ERequestMethod.PUT:
    6987                                                if (string.IsNullOrEmpty (_context.RequestPath)) {
    7088                                                        SendErrorResult (_context, HttpStatusCode.BadRequest, jsonInputData, "PUT_WITHOUT_ID");
     
    7997                                                HandleRestPut (_context, inputJson, jsonInputData);
    8098                                                return;
    81                                         case "DELETE":
     99                                        case ERequestMethod.DELETE:
    82100                                                if (string.IsNullOrEmpty (_context.RequestPath)) {
    83101                                                        SendErrorResult (_context, HttpStatusCode.BadRequest, jsonInputData, "DELETE_WITHOUT_ID");
     
    101119                }
    102120
     121                protected virtual void HandleRestGet (RequestContext _context) {
     122                        SendErrorResult (_context, HttpStatusCode.MethodNotAllowed, null, "Unsupported");
     123                }
     124
     125                protected virtual void HandleRestPost (RequestContext _context, IDictionary<string, object> _jsonInput, byte[] _jsonInputData) {
     126                        SendErrorResult (_context, HttpStatusCode.MethodNotAllowed, _jsonInputData, "Unsupported");
     127                }
     128
     129                protected virtual void HandleRestPut (RequestContext _context, IDictionary<string, object> _jsonInput, byte[] _jsonInputData) {
     130                        SendErrorResult (_context, HttpStatusCode.MethodNotAllowed, _jsonInputData, "Unsupported");
     131                }
     132
     133                protected virtual void HandleRestDelete (RequestContext _context) {
     134                        SendErrorResult (_context, HttpStatusCode.MethodNotAllowed, null, "Unsupported");
     135                }
     136
     137                public override bool Authorized (RequestContext _context) {
     138                        return ActiveMethodPermissionLevel (_context.Method) >= _context.PermissionLevel;
     139                }
     140
     141                /// <summary>
     142                /// Define default permission levels per HTTP method
     143                /// </summary>
     144                /// <param name="_method">HTTP method to return the default value for</param>
     145                /// <returns>Default permission level for the given HTTP method. A value of int.MinValue means no per-method default, use per-API default</returns>
     146                public virtual int DefaultMethodPermissionLevel (ERequestMethod _method) => int.MinValue;
     147
     148                public virtual int ActiveMethodPermissionLevel (ERequestMethod _method) {
     149                        string methodApiModuleName = CachedPerMethodModuleNames [(int)_method];
     150
     151                        if (methodApiModuleName == null) {
     152                                return 0;
     153                        }
     154
     155                        AdminWebModules.WebModule? overrideModule = AdminWebModules.Instance.GetModule (methodApiModuleName, false);
     156                        if (overrideModule.HasValue) {
     157                                return overrideModule.Value.PermissionLevel;
     158                        }
     159
     160                        overrideModule = AdminWebModules.Instance.GetModule (CachedApiModuleName, false);
     161                        if (overrideModule.HasValue) {
     162                                return overrideModule.Value.PermissionLevel;
     163                        }
     164
     165                        int defaultMethodPermissionLevel = DefaultMethodPermissionLevel (_method);
     166                        // ReSharper disable once ConvertIfStatementToReturnStatement
     167                        if (defaultMethodPermissionLevel != int.MinValue) {
     168                                return defaultMethodPermissionLevel;
     169                        }
     170
     171                        return DefaultPermissionLevel ();
     172                }
     173
     174#region Helpers
     175
     176                protected static readonly byte[] JsonEmptyData;
     177               
    103178                static AbsRestApi () {
    104179                        JsonWriter writer = new JsonWriter ();
     
    108183                }
    109184
    110                 protected virtual void HandleRestGet (RequestContext _context) {
    111                         SendErrorResult (_context, HttpStatusCode.MethodNotAllowed, null, "Unsupported");
    112                 }
    113 
    114                 protected virtual void HandleRestPost (RequestContext _context, IDictionary<string, object> _jsonInput, byte[] _jsonInputData) {
    115                         SendErrorResult (_context, HttpStatusCode.MethodNotAllowed, _jsonInputData, "Unsupported");
    116                 }
    117 
    118                 protected virtual void HandleRestPut (RequestContext _context, IDictionary<string, object> _jsonInput, byte[] _jsonInputData) {
    119                         SendErrorResult (_context, HttpStatusCode.MethodNotAllowed, _jsonInputData, "Unsupported");
    120                 }
    121 
    122                 protected virtual void HandleRestDelete (RequestContext _context) {
    123                         SendErrorResult (_context, HttpStatusCode.MethodNotAllowed, null, "Unsupported");
    124                 }
    125 
    126 
    127 #region Helpers
    128 
    129                 protected static readonly byte[] JsonEmptyData;
    130                
    131185                protected static void PrepareEnvelopedResult (out JsonWriter _writer) {
    132186                        WebUtils.PrepareEnvelopedResult (out _writer);
Note: See TracChangeset for help on using the changeset viewer.