From 930dae35ac735bad0e0c01d86db82fc498f45e8c Mon Sep 17 00:00:00 2001 From: jvyden Date: Sat, 6 Apr 2024 17:46:27 -0400 Subject: [PATCH] Allow new 'trusted' users to upload assets during read-only mode --- .../Configuration/GameServerConfig.cs | 3 ++ .../Endpoints/ApiV3/ResourceApiEndpoints.cs | 10 +++- .../Endpoints/Game/ResourceEndpoints.cs | 10 +++- .../Types/Roles/GameUserRole.cs | 4 ++ .../Tests/Assets/AssetUploadTests.cs | 53 +++++++++++++++++++ 5 files changed, 76 insertions(+), 4 deletions(-) diff --git a/Refresh.GameServer/Configuration/GameServerConfig.cs b/Refresh.GameServer/Configuration/GameServerConfig.cs index aadacf93..30c77314 100644 --- a/Refresh.GameServer/Configuration/GameServerConfig.cs +++ b/Refresh.GameServer/Configuration/GameServerConfig.cs @@ -1,6 +1,7 @@ using System.Diagnostics.CodeAnalysis; using Bunkum.Core.Configuration; using Refresh.GameServer.Types.Assets; +using Refresh.GameServer.Types.Roles; namespace Refresh.GameServer.Configuration; @@ -27,6 +28,8 @@ protected override void Migrate(int oldVer, dynamic oldConfig) {} public string WebExternalUrl { get; set; } = "https://refresh.example.com"; public bool AllowInvalidTextureGuids { get; set; } = false; public bool BlockAssetUploads { get; set; } = false; + /// + public bool BlockAssetUploadsForTrustedUsers { get; set; } = false; /// /// The amount of data the user is allowed to upload before all resource uploads get blocked, defaults to 100mb. /// diff --git a/Refresh.GameServer/Endpoints/ApiV3/ResourceApiEndpoints.cs b/Refresh.GameServer/Endpoints/ApiV3/ResourceApiEndpoints.cs index 354d4a7d..99dc9d52 100644 --- a/Refresh.GameServer/Endpoints/ApiV3/ResourceApiEndpoints.cs +++ b/Refresh.GameServer/Endpoints/ApiV3/ResourceApiEndpoints.cs @@ -143,9 +143,15 @@ public ApiResponse UploadImageAsset(RequestContext context [DocSummary("The SHA1 hash of the asset")] string hash, byte[] body, GameUser user) { - //If we block asset uploads, return unauthorized, unless the user is an admin + // If we're blocking asset uploads, throw unless the user is an admin. + // We also have the ability to block asset uploads for trusted users (when they would normally bypass this) if (config.BlockAssetUploads && user.Role != GameUserRole.Admin) - return ApiAuthenticationError.NoPermissionsForCreation; + { + if (user.Role < GameUserRole.Trusted || config.BlockAssetUploadsForTrustedUsers) + { + return ApiAuthenticationError.NoPermissionsForCreation; + } + } if (!CommonPatterns.Sha1Regex().IsMatch(hash)) return ApiValidationError.HashInvalidError; diff --git a/Refresh.GameServer/Endpoints/Game/ResourceEndpoints.cs b/Refresh.GameServer/Endpoints/Game/ResourceEndpoints.cs index 6b0fd169..608ae0eb 100644 --- a/Refresh.GameServer/Endpoints/Game/ResourceEndpoints.cs +++ b/Refresh.GameServer/Endpoints/Game/ResourceEndpoints.cs @@ -29,9 +29,15 @@ public class ResourceEndpoints : EndpointGroup public Response UploadAsset(RequestContext context, string hash, string type, byte[] body, IDataStore dataStore, GameDatabaseContext database, GameUser user, AssetImporter importer, GameServerConfig config, IDateTimeProvider timeProvider, Token token) { - //If we block asset uploads, return unauthorized, unless the user is an admin + // If we're blocking asset uploads, throw unless the user is an admin. + // We also have the ability to block asset uploads for trusted users (when they would normally bypass this) if (config.BlockAssetUploads && user.Role != GameUserRole.Admin) - return Unauthorized; + { + if (user.Role < GameUserRole.Trusted || config.BlockAssetUploadsForTrustedUsers) + { + return Unauthorized; + } + } if (!CommonPatterns.Sha1Regex().IsMatch(hash)) return BadRequest; diff --git a/Refresh.GameServer/Types/Roles/GameUserRole.cs b/Refresh.GameServer/Types/Roles/GameUserRole.cs index 19fb44fe..2cf218dc 100644 --- a/Refresh.GameServer/Types/Roles/GameUserRole.cs +++ b/Refresh.GameServer/Types/Roles/GameUserRole.cs @@ -13,6 +13,10 @@ public enum GameUserRole : sbyte /// Admin = 127, /// + /// A user with special permissions. May upload assets when asset uploads are otherwise disabled. + /// + Trusted = 1, + /// /// A standard user. Can play the game, log in, play levels, review them, etc. /// User = 0, diff --git a/RefreshTests.GameServer/Tests/Assets/AssetUploadTests.cs b/RefreshTests.GameServer/Tests/Assets/AssetUploadTests.cs index e0aa391f..25d3c575 100644 --- a/RefreshTests.GameServer/Tests/Assets/AssetUploadTests.cs +++ b/RefreshTests.GameServer/Tests/Assets/AssetUploadTests.cs @@ -2,6 +2,7 @@ using Refresh.GameServer.Authentication; using Refresh.GameServer.Services; using Refresh.GameServer.Types.Lists; +using Refresh.GameServer.Types.Roles; using Refresh.GameServer.Types.UserData; using RefreshTests.GameServer.Extensions; @@ -87,6 +88,58 @@ public void CannotUploadAssetWhenBlocked(bool psp) HttpResponseMessage response = client.PostAsync("/lbp/upload/" + hash, new ByteArrayContent(data.ToArray())).Result; Assert.That(response.StatusCode, Is.EqualTo(Unauthorized)); } + + [TestCase(false)] + [TestCase(true)] + public void TrustedCanUploadAssetWhenBlocked(bool psp) + { + using TestContext context = this.GetServer(); + context.Server.Value.Server.AddService(); + context.Server.Value.GameServerConfig.BlockAssetUploads = true; + context.Server.Value.GameServerConfig.BlockAssetUploadsForTrustedUsers = false; + + GameUser user = context.CreateUser(); + context.Database.SetUserRole(user, GameUserRole.Trusted); + + using HttpClient client = context.GetAuthenticatedClient(TokenType.Game, user); + if(psp) + client.DefaultRequestHeaders.UserAgent.TryParseAdd("LBPPSP CLIENT"); + + ReadOnlySpan data = "TEX a"u8; + + string hash = BitConverter.ToString(SHA1.HashData(data)) + .Replace("-", "") + .ToLower(); + + HttpResponseMessage response = client.PostAsync("/lbp/upload/" + hash, new ByteArrayContent(data.ToArray())).Result; + Assert.That(response.StatusCode, Is.EqualTo(OK)); + } + + [TestCase(false)] + [TestCase(true)] + public void AdminCanUploadAssetWhenBlocked(bool psp) + { + using TestContext context = this.GetServer(); + context.Server.Value.Server.AddService(); + context.Server.Value.GameServerConfig.BlockAssetUploads = true; + context.Server.Value.GameServerConfig.BlockAssetUploadsForTrustedUsers = true; + + GameUser user = context.CreateUser(); + context.Database.SetUserRole(user, GameUserRole.Admin); + + using HttpClient client = context.GetAuthenticatedClient(TokenType.Game, user); + if(psp) + client.DefaultRequestHeaders.UserAgent.TryParseAdd("LBPPSP CLIENT"); + + ReadOnlySpan data = "TEX a"u8; + + string hash = BitConverter.ToString(SHA1.HashData(data)) + .Replace("-", "") + .ToLower(); + + HttpResponseMessage response = client.PostAsync("/lbp/upload/" + hash, new ByteArrayContent(data.ToArray())).Result; + Assert.That(response.StatusCode, Is.EqualTo(OK)); + } [Test] public void CantUploadAssetWithInvalidHash()