Ignore:
Timestamp:
Apr 24, 2023, 2:40:34 PM (19 months ago)
Author:
alloc
Message:

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

Location:
binary-improvements2/WebServer
Files:
1 added
18 edited
1 moved

Legend:

Unmodified
Added
Removed
  • binary-improvements2/WebServer/ModInfo.xml

    r425 r426  
    55        <Description value="Integrated Webserver for the Web Dashboard and server APIs" />
    66        <Author value="The Fun Pimps LLC" />
    7         <Version value="21.0.258.0" />
     7        <Version value="21.0.270.0" />
    88        <Website value="" />
    99</xml>
  • binary-improvements2/WebServer/WebServer.csproj

    r425 r426  
    101101  </ItemGroup>
    102102  <ItemGroup>
     103    <Compile Include="src\ERequestMethod.cs" />
    103104    <Compile Include="src\FileCache\AbstractCache.cs" />
    104105    <Compile Include="src\FileCache\DirectAccess.cs" />
     
    133134    <Compile Include="src\WebAPI\APIs\ServerInfo.cs" />
    134135    <Compile Include="src\WebAPI\APIs\ServerStats.cs" />
    135     <Compile Include="src\WebAPI\APIs\WebMods.cs" />
     136    <Compile Include="src\WebAPI\APIs\Mods.cs" />
    136137    <Compile Include="src\WebAPI\APIs\WebUiUpdates.cs" />
    137138    <Compile Include="src\WebAPI\JsonCommons.cs" />
  • binary-improvements2/WebServer/src/Commands/WebPermissionsCmd.cs

    r419 r426  
    1515
    1616                protected override string getHelp () {
    17                         return "Set/get permission levels required to access a given web functionality. Default\n" +
    18                                "level required for functions that are not explicitly specified is 0.\n" +
    19                                "Usage:\n" +
    20                                "   webpermission add <webfunction> <level>\n" +
    21                                "   webpermission remove <webfunction>\n" +
    22                                "   webpermission list [includedefaults]";
     17                        return @"
     18                                |Set/get permission levels required to access a given web functionality. Default
     19                            |level required for functions that are not explicitly specified is 0.
     20                            |Usage:
     21                                |   1. webpermission add <webfunction> <method> <level>
     22                            |   2. webpermission remove <webfunction>
     23                            |   3. webpermission list [includedefaults]
     24                                |1. Add a new override (or replace the existing one) for the given function. Method must be a HTTP method (like 'GET', 'POST')
     25                                                supported by the function or the keyword 'global' for a per-API permission level. Use the permission level keyword
     26                                                'inherit' to use the per-API permission level for the specified method instead of a custom one for just the single method.
     27                                |2. Removes any custom overrides for the specified function.
     28                                |3. List all permissions. Pass in 'true' for the includedefaults argument to also show functions that do not have a custom override defined.
     29                                ".Unindent ();
    2330                }
    2431
     
    4047
    4148                private void ExecuteAdd (List<string> _params) {
    42                         if (_params.Count != 3) {
    43                                 SdtdConsole.Instance.Output ($"Wrong number of arguments, expected 3, found {_params.Count}.");
     49                        if (_params.Count != 4) {
     50                                SdtdConsole.Instance.Output ($"Wrong number of arguments, expected 4, found {_params.Count}.");
    4451                                return;
    4552                        }
    4653
    47                         if (!AdminWebModules.Instance.IsKnownModule (_params [1])) {
    48                                 SdtdConsole.Instance.Output ($"\"{_params [1]}\" is not a valid web function.");
     54                        string moduleString = _params [1];
     55                        string methodString = _params [2];
     56                        string permissionLevelString = _params [3];
     57
     58                        ERequestMethod method = ERequestMethod.Count;
     59                        bool isGlobal = false;
     60                        bool isInherit = false;
     61                        int level;
     62                       
     63                        if (!AdminWebModules.Instance.IsKnownModule (moduleString)) {
     64                                SdtdConsole.Instance.Output ($"\"{moduleString}\" is not a valid web function.");
    4965                                return;
    5066                        }
    5167
    52                         if (!int.TryParse (_params [2], out int level)) {
    53                                 SdtdConsole.Instance.Output ($"\"{_params [2]}\" is not a valid integer.");
    54                                 return;
     68                        AdminWebModules.WebModule module = AdminWebModules.Instance.GetModule (moduleString);
     69
     70                        if (methodString.EqualsCaseInsensitive ("global")) {
     71                                isGlobal = true;
     72                        } else {
     73                                if (!EnumUtils.TryParse (methodString, out method, true)) {
     74                                        SdtdConsole.Instance.Output ($"\"{methodString}\" is neither a valid HTTP method nor the 'global' keyword.");
     75                                        return;
     76                                }
     77
     78                                if (module.LevelPerMethod == null || module.LevelPerMethod [(int)method] == AdminWebModules.MethodLevelNotSupported) {
     79                                        SdtdConsole.Instance.Output ($"\"{methodString}\" is not a method supported by the \"{moduleString}\" function.");
     80                                        return;
     81                                }
    5582                        }
    5683
    57                         AdminWebModules.Instance.AddModule (_params [1], level);
    58                         SdtdConsole.Instance.Output ($"{_params [1]} added with permission level of {level}.");
     84                        if (permissionLevelString.EqualsCaseInsensitive ("inherit")) {
     85                                if (isGlobal) {
     86                                        SdtdConsole.Instance.Output ($"Permission level can not use the 'inherit' keyword with the 'global' method keyword.");
     87                                        return;
     88                                }
     89                               
     90                                isInherit = true;
     91                                level = AdminWebModules.MethodLevelInheritGlobal;
     92                        } else {
     93                                if (!int.TryParse (permissionLevelString, out level)) {
     94                                        SdtdConsole.Instance.Output ($"\"{permissionLevelString}\" is neither a valid integer nor the 'inherit' keyword.");
     95                                        return;
     96                                }
     97                        }
     98
     99                        module.IsDefault = false;
     100                        if (isGlobal) {
     101                                module.LevelGlobal = level;
     102                        } else {
     103                                module.LevelPerMethod [(int)method] = level;
     104                        }
     105                       
     106                        AdminWebModules.Instance.AddModule (module);
     107                       
     108                        SdtdConsole.Instance.Output (
     109                                $"{moduleString}, method {methodString} added {(isInherit ? ", inheriting the APIs global permission level" : "with permission level " + level)}.");
    59110                }
    60111
     
    78129                       
    79130                        SdtdConsole.Instance.Output ("Defined web function permissions:");
    80                         SdtdConsole.Instance.Output ("  Level: Web function");
    81131                       
    82132                        List<AdminWebModules.WebModule> wmps = AdminWebModules.Instance.GetModules ();
    83                         for (int i = 0; i < wmps.Count; i++) {
    84                                 AdminWebModules.WebModule wmp = wmps [i];
     133                        for (int iModule = 0; iModule < wmps.Count; iModule++) {
     134                                AdminWebModules.WebModule wmp = wmps [iModule];
    85135
    86136                                if (!includeDefaults && wmp.IsDefault) {
     
    88138                                }
    89139
    90                                 if (wmp.IsDefault) {
    91                                         if (wmp.PermissionLevel == int.MinValue) {
    92                                                 SdtdConsole.Instance.Output ($"    -  : {wmp.Name} (default permission)");
    93                                         } else {
    94                                                 SdtdConsole.Instance.Output ($"  {wmp.PermissionLevel,5}: {wmp.Name} (default permission)");
     140                                SdtdConsole.Instance.Output ($"  {wmp.Name,-25}: {wmp.LevelGlobal,4}{(wmp.IsDefault ? " (default permissions)" : "")}");
     141                                if (wmp.LevelPerMethod != null) {
     142                                        for (int iMethod = 0; iMethod < wmp.LevelPerMethod.Length; iMethod++) {
     143                                                int methodLevel = wmp.LevelPerMethod [iMethod];
     144                                                ERequestMethod method = (ERequestMethod)iMethod;
     145                                               
     146                                                if (methodLevel == AdminWebModules.MethodLevelNotSupported) {
     147                                                        continue;
     148                                                }
     149                                               
     150                                                if (methodLevel == AdminWebModules.MethodLevelInheritGlobal) {
     151                                                        SdtdConsole.Instance.Output ($"  {method.ToStringCached (),25}: {wmp.LevelGlobal,4} (Using API level)");
     152                                                } else {
     153                                                        SdtdConsole.Instance.Output ($"  {method.ToStringCached (),25}: {methodLevel,4}");
     154                                                }
    95155                                        }
    96                                 } else {
    97                                         SdtdConsole.Instance.Output ($"  {wmp.PermissionLevel,5}: {wmp.Name}");
    98156                                }
    99157                        }
  • binary-improvements2/WebServer/src/Permissions/AdminWebModules.cs

    r418 r426  
    11using System.Collections.Generic;
    22using System.Xml;
     3using UnityEngine;
    34
    45namespace Webserver.Permissions {
     
    4041
    4142
    42                 public void AddModule (string _module, int _permissionLevel) {
    43                         WebModule p = new WebModule (_module, _permissionLevel, false);
     43                public void AddModule (WebModule _module) {
    4444                        lock (this) {
    4545                                allModulesList.Clear ();
    4646                               
    47                                 modules [_module] = p;
     47                                modules [_module.Name] = _module;
    4848                                Parent.Save ();
    4949                        }
     
    8080
    8181#endregion
    82 
    83                 public readonly struct WebModule {
    84                         public readonly string Name;
    85                         public readonly int PermissionLevel;
    86                         public readonly bool IsDefault;
    87 
    88                         public WebModule (string _name, int _permissionLevel, bool _isDefault) {
     82               
     83                public struct WebModule {
     84                        public string Name;
     85                        public int LevelGlobal;
     86                        public int[] LevelPerMethod;
     87                        public bool IsDefault;
     88
     89                        public WebModule (string _name, int _level, bool _isDefault = false) {
     90                                LevelPerMethod = null;
     91                               
    8992                                Name = _name;
    90                                 PermissionLevel = _permissionLevel;
     93                                LevelGlobal = _level;
     94                                IsDefault = _isDefault;
     95                        }
     96
     97                        public WebModule (string _name, int _levelGlobal, int[] _levelPerMethod, bool _isDefault = false) {
     98                                if (_levelPerMethod == null || _levelPerMethod.Length != (int)ERequestMethod.Count) {
     99                                        LevelPerMethod = createDefaultPerMethodArray ();
     100
     101                                        for (int i = 0; i < (int)ERequestMethod.Count; i++) {
     102                                                if (_levelPerMethod != null && i < _levelPerMethod.Length) {
     103                                                        LevelPerMethod [i] = _levelPerMethod [i];
     104                                                }
     105                                        }
     106                                } else {
     107                                        LevelPerMethod = _levelPerMethod;
     108                                }
     109
     110                                Name = _name;
     111                                LevelGlobal = _levelGlobal;
    91112                                IsDefault = _isDefault;
    92113                        }
    93114                       
    94115                        public void ToXml (XmlElement _parent) {
    95                                 _parent.AddXmlElement ("module")
    96                                                 .SetAttrib ("name", Name)
    97                                                 .SetAttrib ("permission_level", PermissionLevel.ToString ());
     116                                bool hasPerMethodLevels = LevelPerMethod != null;
     117
     118                                XmlElement permissionElement = _parent.AddXmlElement ("module")
     119                                        .SetAttrib ("name", Name)
     120                                        .SetAttrib ("permission_level", LevelGlobal.ToString ());
     121                               
     122                                if (!hasPerMethodLevels) {
     123                                        return;
     124                                }
     125
     126                                for (int i = 0; i < LevelPerMethod.Length; i++) {
     127                                        ERequestMethod method = (ERequestMethod)i;
     128                                        int level = LevelPerMethod [i];
     129                                       
     130                                        if (level == MethodLevelNotSupported) {
     131                                                continue;
     132                                        }
     133
     134                                        permissionElement.AddXmlElement ("method")
     135                                                .SetAttrib ("name", method.ToStringCached ())
     136                                                .SetAttrib ("permission_level", level.ToString ());
     137                                }
    98138                        }
    99139
     
    117157                                        return false;
    118158                                }
    119                                
    120                                 _result = new WebModule (name, permissionLevel, false);
     159
     160                                int[] perMethodLevels = null;
     161                               
     162                                foreach (XmlNode child in _element.ChildNodes) {
     163                                        if (child.NodeType != XmlNodeType.Element) {
     164                                                continue;
     165                                        }
     166
     167                                        XmlElement childElem = (XmlElement)child;
     168                                        if (childElem.Name != "method") {
     169                                                Log.Warning ($"[Web] [Perms] Ignoring module child element, invalid element name: {childElem.OuterXml}");
     170                                                continue;
     171                                        }
     172
     173                                        if (!childElem.TryGetAttribute ("name", out string methodName)) {
     174                                                Log.Warning ($"[Web] [Perms] Ignoring module child element, missing 'name' attribute: {childElem.OuterXml}");
     175                                                continue;
     176                                        }
     177
     178                                        if (!EnumUtils.TryParse (methodName, out ERequestMethod method, true)) {
     179                                                Log.Warning (
     180                                                        $"[Web] [Perms] Ignoring module child element, unknown method name in 'name' attribute: {childElem.OuterXml}");
     181                                                continue;
     182                                        }
     183
     184                                        if (method >= ERequestMethod.Count) {
     185                                                Log.Warning (
     186                                                        $"[Web] [Perms] Ignoring module child element, invalid method name in 'name' attribute: {childElem.OuterXml}");
     187                                                continue;
     188                                        }
     189                                       
     190                                        if (!childElem.TryGetAttribute ("permission_level", out permissionLevelString)) {
     191                                                Log.Warning ($"[Web] [Perms] Ignoring module child element, missing 'permission_level' attribute: {childElem.OuterXml}");
     192                                                continue;
     193                                        }
     194
     195                                        if (!int.TryParse (permissionLevelString, out int methodPermissionLevel)) {
     196                                                Log.Warning (
     197                                                        $"[Web] [Perms] Ignoring module child element, invalid (non-numeric) value for 'permission_level' attribute: {childElem.OuterXml}");
     198                                                continue;
     199                                        }
     200
     201                                        perMethodLevels ??= createDefaultPerMethodArray ();
     202                                        perMethodLevels [(int)method] = methodPermissionLevel;
     203                                }
     204
     205                                _result = perMethodLevels != null ? new WebModule (name, permissionLevel, perMethodLevels) : new WebModule (name, permissionLevel);
     206
    121207                                return true;
    122208                        }
     209
     210                        private static int[] createDefaultPerMethodArray () {
     211                                int[] result = new int[(int)ERequestMethod.Count];
     212
     213                                for (int i = 0; i < (int)ERequestMethod.Count; i++) {
     214                                        result [i] = MethodLevelNotSupported;
     215                                }
     216
     217                                return result;
     218                        }
    123219                }
    124220
     
    127223
    128224                /// <summary>
     225                /// Use global (API based) permission level for the method
     226                /// </summary>
     227                public const int MethodLevelInheritGlobal = int.MinValue;
     228               
     229                /// <summary>
     230                /// Method not supported
     231                /// </summary>
     232                public const int MethodLevelNotSupported = int.MinValue + 1;
     233
     234                public const int PermissionLevelUser = 1000;
     235                public const int PermissionLevelGuest = 2000;
     236
     237                /// <summary>
    129238                /// Contains all registered modules and their default permission
    130239                /// </summary>
     
    136245                private readonly List<WebModule> allModulesList = new List<WebModule> ();
    137246
    138                 public void AddKnownModule (string _module, int _defaultPermission) {
    139                         if (string.IsNullOrEmpty (_module)) {
     247                public void AddKnownModule (WebModule _module) {
     248                        if (string.IsNullOrEmpty (_module.Name)) {
    140249                                return;
    141250                        }
    142251
    143                         WebModule p = new WebModule (_module, _defaultPermission, true);
    144 
     252                        _module.IsDefault = true;
     253                       
    145254                        lock (this) {
    146255                                allModulesList.Clear ();
    147                                 knownModules [_module] = p;
     256                                knownModules [_module.Name] = _module;
    148257                        }
    149258                }
     
    160269
    161270                public bool ModuleAllowedWithLevel (string _module, int _level) {
    162                         WebModule permInfo = GetModule (_module)!.Value;
    163                         return permInfo.PermissionLevel >= _level;
    164                 }
    165 
    166                 public WebModule? GetModule (string _module, bool _returnDefaults = true) {
     271                        return GetModule (_module).LevelGlobal >= _level;
     272                }
     273
     274                public WebModule GetModule (string _module) {
    167275                        if (modules.TryGetValue (_module, out WebModule result)) {
    168276                                return result;
    169                         }
    170 
    171                         if (!_returnDefaults) {
    172                                 return null;
    173277                        }
    174278
  • binary-improvements2/WebServer/src/RequestContext.cs

    r415 r426  
    22
    33namespace Webserver {
    4         public enum ERequestMethod {
    5                 Other,
    6                 // ReSharper disable InconsistentNaming
    7                 GET,
    8                 PUT,
    9                 POST,
    10                 DELETE,
    11                 HEAD,
    12                 OPTIONS,
    13                 // ReSharper restore InconsistentNaming
    14                 Count
    15         }
    16        
    174        public class RequestContext {
    185                public string RequestPath;
  • binary-improvements2/WebServer/src/UrlHandlers/AbsHandler.cs

    r404 r426  
    1212                protected AbsHandler (string _moduleName, int _defaultPermissionLevel = 0) {
    1313                        moduleName = _moduleName;
    14                         AdminWebModules.Instance.AddKnownModule (_moduleName, _defaultPermissionLevel);
     14                        AdminWebModules.Instance.AddKnownModule (new AdminWebModules.WebModule(_moduleName, _defaultPermissionLevel, true));
    1515                }
    1616
  • binary-improvements2/WebServer/src/UrlHandlers/SseHandler.cs

    r404 r426  
    5353                public void AddEvent (string _eventName, AbsEvent _eventInstance) {
    5454                        events.Add (_eventName, _eventInstance);
    55                         AdminWebModules.Instance.AddKnownModule ($"webevent.{_eventName}", _eventInstance.DefaultPermissionLevel ());
     55                        AdminWebModules.Instance.AddKnownModule (new AdminWebModules.WebModule($"webevent.{_eventName}", _eventInstance.DefaultPermissionLevel (), true));
    5656                }
    5757
  • binary-improvements2/WebServer/src/UrlHandlers/UserStatusHandler.cs

    r404 r426  
    1616                private static readonly byte[] jsonAllowedKey = JsonWriter.GetEncodedPropertyNameWithPrefixValueSeparator ("allowed");
    1717
     18                private static readonly byte[][] jsonMethodNameKeys;
     19
     20                static UserStatusHandler () {
     21                        jsonMethodNameKeys = new byte[(int)ERequestMethod.Count][];
     22                        for (int i = 0; i < jsonMethodNameKeys.Length; i++) {
     23                                ERequestMethod method = (ERequestMethod)i;
     24                                jsonMethodNameKeys [i] = JsonWriter.GetEncodedPropertyName (method.ToStringCached ());
     25                        }
     26                }
     27
    1828                public override void HandleRequest (RequestContext _context) {
    1929                        WebUtils.PrepareEnvelopedResult (out JsonWriter writer);
     
    3242
    3343                        List<AdminWebModules.WebModule> list = AdminWebModules.Instance.GetModules ();
    34                         for (int i = 0; i < list.Count; i++) {
    35                                 AdminWebModules.WebModule perm = list [i];
     44                        for (int iModule = 0; iModule < list.Count; iModule++) {
     45                                AdminWebModules.WebModule perm = list [iModule];
    3646                               
    37                                 if (i > 0) {
     47                                if (iModule > 0) {
    3848                                        writer.WriteValueSeparator ();
    3949                                }
     
    4353
    4454                                writer.WriteRaw (jsonAllowedKey);
    45                                 writer.WriteBoolean (perm.PermissionLevel >= _context.PermissionLevel);
     55                               
     56                                writer.WriteBeginObject ();
     57
     58                                if (perm.LevelPerMethod == null) {
     59                                        writer.WriteRaw (jsonMethodNameKeys [(int)ERequestMethod.GET]);
     60                                        writer.WriteBoolean (perm.LevelGlobal >= _context.PermissionLevel);
     61                                } else {
     62                                        bool first = true;
     63                                        for (int iMethod = 0; iMethod < perm.LevelPerMethod.Length; iMethod++) {
     64                                                int methodLevel = perm.LevelPerMethod [iMethod];
     65                                               
     66                                                if (methodLevel == AdminWebModules.MethodLevelNotSupported) {
     67                                                        continue;
     68                                                }
     69
     70                                                if (methodLevel == AdminWebModules.MethodLevelInheritGlobal) {
     71                                                        methodLevel = perm.LevelGlobal;
     72                                                }
     73
     74                                                if (!first) {
     75                                                        writer.WriteValueSeparator ();
     76                                                }
     77
     78                                                first = false;
     79                                               
     80                                                writer.WriteRaw (jsonMethodNameKeys [iMethod]);
     81                                                writer.WriteBoolean (methodLevel >= _context.PermissionLevel);
     82                                        }
     83                                }
     84
     85                                writer.WriteEndObject ();
     86                               
    4687
    4788                                writer.WriteEndObject ();
  • binary-improvements2/WebServer/src/Web.cs

    r415 r426  
    1616                public static event Action<Web> ServerInitialized;
    1717               
    18                 private const int guestPermissionLevel = 2000;
    1918                private const string indexPageUrl = "/app";
    2019               
     
    5150                                }
    5251
    53                                 // TODO: Read from config
    54                                 bool useCacheForStatic = StringParsers.ParseBool ("false");
    55 
    56                                 string webfilesFolder = DetectWebserverFolder (_modInstancePath);
    57 
    5852                                ConnectionHandler = new ConnectionHandler ();
    5953                               
    60                                 RegisterPathHandler ("/", new RewriteHandler ("/files/"));
    61 
    62                                 // React virtual routing
    63                                 RegisterPathHandler ("/app", new RewriteHandler ("/files/index.html", true));
    64                                
    65                                 // Do mods relatively early as they should not be requested a lot, unlike the later registrations, especially for API and map tiles
    66                                 RegisterWebMods (useCacheForStatic);
    67 
    68                                 RegisterPathHandler ("/session/", new SessionHandler (ConnectionHandler));
    69                                 RegisterPathHandler ("/userstatus", new UserStatusHandler ());
    70                                 RegisterPathHandler ("/sse/", new SseHandler ());
    71                                 RegisterPathHandler ("/files/", new StaticHandler (
    72                                         webfilesFolder,
    73                                         useCacheForStatic ? new SimpleCache () : new DirectAccess (),
    74                                         false)
    75                                 );
    76                                 RegisterPathHandler ("/itemicons/", new ItemIconHandler (true));
    77                                 RegisterPathHandler ("/api/", new ApiHandler ());
     54                                RegisterDefaultHandlers (_modInstancePath);
    7855                               
    7956                                // Allow other code to add their stuff
     
    9471                }
    9572
     73                private void RegisterDefaultHandlers (string _modInstancePath) {
     74                        // TODO: Read from config
     75                        bool useCacheForStatic = StringParsers.ParseBool ("false");
     76
     77                        string webfilesFolder = DetectWebserverFolder (_modInstancePath);
     78
     79                        RegisterPathHandler ("/", new RewriteHandler ("/files/"));
     80
     81                        // React virtual routing
     82                        RegisterPathHandler ("/app", new RewriteHandler ("/files/index.html", true));
     83                               
     84                        // Do mods relatively early as they should not be requested a lot, unlike the later registrations, especially for API and map tiles
     85                        RegisterWebMods (useCacheForStatic);
     86
     87                        RegisterPathHandler ("/session/", new SessionHandler (ConnectionHandler));
     88                        RegisterPathHandler ("/userstatus", new UserStatusHandler ());
     89                        RegisterPathHandler ("/sse/", new SseHandler ());
     90                        RegisterPathHandler ("/files/", new StaticHandler (
     91                                webfilesFolder,
     92                                useCacheForStatic ? new SimpleCache () : new DirectAccess (),
     93                                false)
     94                        );
     95                        RegisterPathHandler ("/itemicons/", new ItemIconHandler (true));
     96                        RegisterPathHandler ("/api/", new ApiHandler ());
     97                }
     98
    9699                private static string DetectWebserverFolder (string _modInstancePath) {
    97100                        string webserverFolder = $"{_modInstancePath}/webserver";
     
    127130                        foreach (Mod mod in ModManager.GetLoadedMods ()) {
    128131                                try {
    129                                         string webModPath = $"{mod.Path}/WebMod";
    130                                         if (!Directory.Exists (webModPath)) {
    131                                                 continue;
    132                                         }
    133 
    134132                                        try {
    135133                                                WebMod webMod = new WebMod (this, mod, _useStaticCache);
     
    298296                        if (reqRemoteEndPoint == null) {
    299297                                Log.Warning ("[Web] No RemoteEndPoint on web request");
    300                                 return guestPermissionLevel;
     298                                return AdminWebModules.PermissionLevelGuest;
    301299                        }
    302300                       
     
    316314                        if (!_req.Headers.TryGetValue ("X-SDTD-API-TOKENNAME", out string apiTokenName) ||
    317315                            !_req.Headers.TryGetValue ("X-SDTD-API-SECRET", out string apiTokenSecret)) {
    318                                 return guestPermissionLevel;
     316                                return AdminWebModules.PermissionLevelGuest;
    319317                        }
    320318
     
    326324                        Log.Warning ($"[Web] Invalid Admintoken used from {reqRemoteEndPoint}");
    327325
    328                         return guestPermissionLevel;
     326                        return AdminWebModules.PermissionLevelGuest;
    329327                }
    330328        }
  • binary-improvements2/WebServer/src/WebAPI/APIs/Command.cs

    r408 r426  
    33using JetBrains.Annotations;
    44using Utf8Json;
     5using Webserver.Permissions;
    56
    67namespace Webserver.WebAPI.APIs {
     
    130131                }
    131132
    132                 public override int DefaultPermissionLevel () => 2000;
     133                public override int DefaultPermissionLevel () => AdminWebModules.PermissionLevelGuest;
    133134        }
    134135}
  • binary-improvements2/WebServer/src/WebAPI/APIs/Mods.cs

    r425 r426  
    11using JetBrains.Annotations;
    22using Utf8Json;
     3using Webserver.Permissions;
    34
    45namespace Webserver.WebAPI.APIs {
    56        [UsedImplicitly]
    6         public class WebMods : AbsRestApi {
     7        public class Mods : AbsRestApi {
    78                private readonly byte[] loadedWebMods;
    89
    9                 public WebMods (Web _parent) {
     10                public Mods (Web _parent) {
    1011                        JsonWriter writer = new JsonWriter ();
    1112                        writer.WriteBeginArray ();
     
    1314                        for (int i = 0; i < _parent.webMods.Count; i++) {
    1415                                WebMod webMod = _parent.webMods [i];
    15                                
     16
    1617                                if (i > 0) {
    1718                                        writer.WriteValueSeparator ();
    1819                                }
    19 
     20                               
    2021                                writer.WriteBeginObject ();
    2122
    22                                 writer.WriteString ("name");
    23                                 writer.WriteNameSeparator ();
    24                                 writer.WriteString (webMod.ParentMod.Name);
     23                                writeModJson (ref writer, webMod);
    2524
    26                                 string webModReactBundle = webMod.ReactBundle;
    27                                 if (webModReactBundle != null) {
     25                                if (webMod.ReactBundle != null || webMod.CssPath != null) {
    2826                                        writer.WriteValueSeparator ();
    29                                         writer.WriteString ("bundle");
    30                                         writer.WriteNameSeparator ();
    31                                         writer.WriteString (webModReactBundle);
    32                                 }
    3327
    34                                 string webModCssFile = webMod.CssPath;
    35                                 if (webModCssFile != null) {
    36                                         writer.WriteValueSeparator ();
    37                                         writer.WriteString ("css");
    38                                         writer.WriteNameSeparator ();
    39                                         writer.WriteString (webModCssFile);
     28                                        writer.WritePropertyName ("web");
     29                                        writer.WriteBeginObject ();
     30                                       
     31                                        string webModReactBundle = webMod.ReactBundle;
     32                                        if (webModReactBundle != null) {
     33                                                writer.WritePropertyName ("bundle");
     34                                                writer.WriteString (webModReactBundle);
     35                                        }
     36
     37                                        string webModCssFile = webMod.CssPath;
     38                                        if (webModCssFile != null) {
     39                                                if (webModReactBundle != null) {
     40                                                        writer.WriteValueSeparator ();
     41                                                }
     42
     43                                                writer.WritePropertyName ("css");
     44                                                writer.WriteString (webModCssFile);
     45                                        }
     46                                       
     47                                        writer.WriteEndObject ();
    4048                                }
    4149
     
    5462                }
    5563
    56                 public override int DefaultPermissionLevel () => 2000;
     64                public override int DefaultPermissionLevel () => AdminWebModules.PermissionLevelGuest;
     65
     66                private void writeModJson (ref JsonWriter _writer, WebMod _webMod) {
     67                        _writer.WritePropertyName ("name");
     68                        _writer.WriteString (_webMod.ParentMod.Name);
     69                       
     70                        _writer.WriteValueSeparator ();
     71                        _writer.WritePropertyName ("displayName");
     72                        JsonCommons.WriteStringOrNull (ref _writer, _webMod.ParentMod.DisplayName);
     73
     74                        _writer.WriteValueSeparator ();
     75                        _writer.WritePropertyName ("description");
     76                        JsonCommons.WriteStringOrNull (ref _writer, _webMod.ParentMod.Description);
     77
     78                        _writer.WriteValueSeparator ();
     79                        _writer.WritePropertyName ("author");
     80                        JsonCommons.WriteStringOrNull (ref _writer, _webMod.ParentMod.Author);
     81
     82                        _writer.WriteValueSeparator ();
     83                        _writer.WritePropertyName ("version");
     84                        JsonCommons.WriteStringOrNull (ref _writer, _webMod.ParentMod.VersionString);
     85
     86                        _writer.WriteValueSeparator ();
     87                        _writer.WritePropertyName ("website");
     88                        JsonCommons.WriteStringOrNull (ref _writer, _webMod.ParentMod.Website);
     89                }
    5790        }
    5891}
  • binary-improvements2/WebServer/src/WebAPI/APIs/Player.cs

    r425 r426  
    256256                }
    257257
    258                 public override int DefaultPermissionLevel () => 2000;
     258                public override int DefaultPermissionLevel () => AdminWebModules.PermissionLevelGuest;
    259259        }
    260260}
  • binary-improvements2/WebServer/src/WebAPI/APIs/RegisterUser.cs

    r416 r426  
    8080                        }
    8181                       
    82                         // TODO: Check if username is already used!
     82                        // TODO: Check if username is already used by someone else!
     83                        // TODO: Remove existing username if player already had one!
    8384
    8485                        AdminWebUsers.Instance.AddUser (username, password, regData.PlatformUserId, regData.CrossPlatformUserId);
     
    9596                }
    9697
    97                 public override int DefaultPermissionLevel () => 2000;
     98                public override int DefaultPermissionLevel () => AdminWebModules.PermissionLevelGuest;
    9899        }
    99100}
  • binary-improvements2/WebServer/src/WebAPI/APIs/ServerStats.cs

    r408 r426  
    22using Utf8Json;
    33using Webserver.LiveData;
     4using Webserver.Permissions;
    45
    56namespace Webserver.WebAPI.APIs {
     
    4748                }
    4849
    49                 public override int DefaultPermissionLevel () => 2000;
     50                public override int DefaultPermissionLevel () => AdminWebModules.PermissionLevelGuest;
    5051        }
    5152}
  • binary-improvements2/WebServer/src/WebAPI/APIs/WebUiUpdates.cs

    r408 r426  
    22using Utf8Json;
    33using Webserver.LiveData;
     4using Webserver.Permissions;
    45
    56namespace Webserver.WebAPI.APIs {
     
    5758                }
    5859
    59                 public override int DefaultPermissionLevel () => 2000;
     60                public override int DefaultPermissionLevel () => AdminWebModules.PermissionLevelGuest;
    6061        }
    6162}
  • binary-improvements2/WebServer/src/WebAPI/AbsRestApi.cs

    r418 r426  
    1010                private static readonly UnityEngine.Profiling.CustomSampler jsonDeserializeSampler = UnityEngine.Profiling.CustomSampler.Create ("JSON_Deserialize");
    1111
    12                 protected readonly string[] CachedPerMethodModuleNames = new string[(int)ERequestMethod.Count];
    13 
    1412                protected AbsRestApi (string _name = null) : this(null, _name) {
    1513                }
     
    1917
    2018                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                         }
     19                        AdminWebModules.Instance.AddKnownModule (new AdminWebModules.WebModule (CachedApiModuleName, DefaultPermissionLevel (),
     20                                DefaultMethodPermissionLevels (), true));
    3321                }
    3422
     
    136124
    137125                public override bool Authorized (RequestContext _context) {
    138                         return ActiveMethodPermissionLevel (_context.Method) >= _context.PermissionLevel;
     126                        AdminWebModules.WebModule module = AdminWebModules.Instance.GetModule (CachedApiModuleName);
     127
     128                        if (module.LevelPerMethod != null) {
     129                                int perMethodLevel = module.LevelPerMethod [(int)_context.Method];
     130                                if (perMethodLevel == AdminWebModules.MethodLevelNotSupported) {
     131                                        return false;
     132                                }
     133
     134                                if (perMethodLevel != AdminWebModules.MethodLevelInheritGlobal) {
     135                                        return perMethodLevel >= _context.PermissionLevel;
     136                                }
     137                        }
     138
     139                        return module.LevelGlobal >= _context.PermissionLevel;
    139140                }
    140141
    141142                /// <summary>
    142                 /// Define default permission levels per HTTP method
     143                /// Define default permission levels per HTTP method as an array of levels. See <see cref="ERequestMethod"/> for the order of entries.
    143144                /// </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                 }
     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                };
    173153
    174154#region Helpers
  • binary-improvements2/WebServer/src/WebAPI/AbsWebAPI.cs

    r418 r426  
    1919
    2020                protected virtual void RegisterPermissions () {
    21                         AdminWebModules.Instance.AddKnownModule ($"webapi.{Name}", DefaultPermissionLevel ());
     21                        AdminWebModules.Instance.AddKnownModule (new AdminWebModules.WebModule(CachedApiModuleName, DefaultPermissionLevel (), true));
    2222                }
    2323
     
    2525
    2626                public virtual bool Authorized (RequestContext _context) {
    27                         return AdminWebModules.Instance.ModuleAllowedWithLevel (CachedApiModuleName, _context.PermissionLevel);
     27                        return AdminWebModules.Instance.GetModule (CachedApiModuleName).LevelGlobal >= _context.PermissionLevel;
    2828                }
    2929
  • binary-improvements2/WebServer/src/WebAPI/JsonCommons.cs

    r425 r426  
    6060                        _writer.WriteString (_dateTime.ToString ("o"));
    6161                }
     62
     63                public static void WriteStringOrNull (ref JsonWriter _writer, string _string) {
     64                        if (_string == null) {
     65                                _writer.WriteNull ();
     66                        } else {
     67                                _writer.WriteString (_string);
     68                        }
     69                }
    6270        }
    6371}
  • binary-improvements2/WebServer/src/WebMod.cs

    r402 r426  
    1212                public readonly string ReactBundle; // Absolute web path to the React bundle if the mod has one, e.g. "/webmods/myMod/bundle.js"
    1313                public readonly string CssPath; // Absolute web path to a CSS if the mod has one, e.g. "/webmods/myMod/styling.css";
     14                public readonly bool IsWebMod;
    1415
    1516                public WebMod (Web _parentWeb, Mod _parentMod, bool _useStaticCache) {
    16                         string folder = $"{_parentMod.Path}/WebMod";
    17                         if (!Directory.Exists (folder)) {
    18                                 throw new InvalidDataException("No WebMod folder in mod");
    19                         }
    20 
    21                         string urlWebModBase = $"{modsBaseUrl}{_parentMod.FolderName}/";
    22 
    23                         ReactBundle = $"{folder}/{reactBundleName}";
    24                         ReactBundle = File.Exists (ReactBundle) ? $"{urlWebModBase}{reactBundleName}" : null;
    25 
    26                         CssPath = $"{folder}/{stylingFileName}";
    27                         CssPath = File.Exists (CssPath) ? $"{urlWebModBase}{stylingFileName}" : null;
    28 
    29                         if (ReactBundle == null && CssPath == null) {
    30                                 throw new InvalidDataException($"WebMod folder has neither a {reactBundleName} nor a {stylingFileName}");
    31                         }
    32 
    3317                        ParentMod = _parentMod;
    3418
    35                         _parentWeb.RegisterPathHandler (urlWebModBase, new StaticHandler (
    36                                 folder,
    37                                 _useStaticCache ? new SimpleCache () : new DirectAccess (),
    38                                 false)
    39                         );
     19                        string folder = $"{_parentMod.Path}/WebMod";
     20                        IsWebMod = Directory.Exists (folder);
     21
     22                        if (IsWebMod) {
     23                                string urlWebModBase = $"{modsBaseUrl}{_parentMod.FolderName}/";
     24
     25                                ReactBundle = $"{folder}/{reactBundleName}";
     26                                ReactBundle = File.Exists (ReactBundle) ? $"{urlWebModBase}{reactBundleName}" : null;
     27
     28                                CssPath = $"{folder}/{stylingFileName}";
     29                                CssPath = File.Exists (CssPath) ? $"{urlWebModBase}{stylingFileName}" : null;
     30
     31                                _parentWeb.RegisterPathHandler (urlWebModBase, new StaticHandler (
     32                                        folder,
     33                                        _useStaticCache ? new SimpleCache () : new DirectAccess (),
     34                                        false)
     35                                );
     36                        }
    4037                }
    4138        }
Note: See TracChangeset for help on using the changeset viewer.