Skip to content

Commit cc54039

Browse files
authored
Better organize/deduplicate role-specific config options, separate timed level limit for trusted users (#1065)
Moves certain `GameServerConfig` options, like read-only and blocked asset flags, to child objects inside the root config JSON. This way we can also easily add further role-specific options if we ever want to introduce more (trusted) roles (see #1061), only have to add one more attribute to the root config class per role, instead of another set of duplicate attributes. Also makes timed level limits role specific, making normal and trusted users have separate level limits like that, closing #754. Might need a little more testing before merging.
2 parents 5f7aa00 + d3a5190 commit cc54039

File tree

13 files changed

+247
-84
lines changed

13 files changed

+247
-84
lines changed

Refresh.Core/Configuration/GameServerConfig.cs

Lines changed: 81 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,61 +1,116 @@
11
using System.Diagnostics.CodeAnalysis;
22
using Bunkum.Core.Configuration;
33
using Microsoft.CSharp.RuntimeBinder;
4-
using Refresh.Database.Models.Assets;
5-
using Refresh.Database.Models.Users;
64

75
namespace Refresh.Core.Configuration;
86

97
[SuppressMessage("ReSharper", "AutoPropertyCanBeMadeGetOnly.Global")]
108
[SuppressMessage("ReSharper", "RedundantDefaultMemberInitializer")]
119
public class GameServerConfig : Config
1210
{
13-
public override int CurrentConfigVersion => 26;
11+
public override int CurrentConfigVersion => 27;
1412
public override int Version { get; set; } = 0;
1513

1614
protected override void Migrate(int oldVer, dynamic oldConfig)
1715
{
18-
if (oldVer < 18)
16+
// In version 27, various (mostly already role-specific) perms, like blocked assets and read-only mode, were moved to dedicated child objects,
17+
// to more cleanly split the perms between certain roles, and to make their enforcement easier.
18+
if (oldVer < 27)
1919
{
20-
// Asset safety level was added in config version 2, so dont try to migrate if we are coming from an older version than that
21-
if (oldVer >= 2)
20+
this.NormalUserPermissions = new();
21+
this.TrustedUserPermissions = new();
22+
23+
// filesize quota limit was added during version 11, but the version wasn't bumped, so catch error to be safe
24+
if (oldVer >= 11)
2225
{
23-
int oldSafetyLevel = (int)oldConfig.MaximumAssetSafetyLevel;
24-
this.BlockedAssetFlags = new ConfigAssetFlags
26+
try
27+
{
28+
this.NormalUserPermissions.UserFilesizeQuota = (int)oldConfig.UserFilesizeQuota;
29+
this.TrustedUserPermissions.UserFilesizeQuota = (int)oldConfig.UserFilesizeQuota;
30+
}
31+
catch (RuntimeBinderException)
2532
{
26-
Dangerous = oldSafetyLevel < 3,
27-
Modded = oldSafetyLevel < 2,
28-
Media = oldSafetyLevel < 1,
29-
};
33+
// do nothing
34+
}
3035
}
3136

32-
// Asset safety level for trusted users was added in config version 12, so dont try to migrate if we are coming from a version older than that
33-
if (oldVer >= 12)
37+
if (oldVer >= 18)
3438
{
35-
// There was no version bump for trusted users being added, so we just have to catch this error :/
36-
try
39+
this.NormalUserPermissions.BlockedAssetFlags.Dangerous = (bool)oldConfig.BlockedAssetFlags.Dangerous;
40+
this.NormalUserPermissions.BlockedAssetFlags.Media = (bool)oldConfig.BlockedAssetFlags.Media;
41+
this.NormalUserPermissions.BlockedAssetFlags.Modded = (bool)oldConfig.BlockedAssetFlags.Modded;
42+
43+
this.TrustedUserPermissions.BlockedAssetFlags.Dangerous = (bool)oldConfig.BlockedAssetFlagsForTrustedUsers.Dangerous;
44+
this.TrustedUserPermissions.BlockedAssetFlags.Media = (bool)oldConfig.BlockedAssetFlagsForTrustedUsers.Media;
45+
this.TrustedUserPermissions.BlockedAssetFlags.Modded = (bool)oldConfig.BlockedAssetFlagsForTrustedUsers.Modded;
46+
}
47+
else
48+
{
49+
// Asset safety level was added in config version 2, so dont try to migrate if we are coming from an older version than that
50+
if (oldVer >= 2)
3751
{
38-
int oldTrustedSafetyLevel = (int)oldConfig.MaximumAssetSafetyLevelForTrustedUsers;
39-
this.BlockedAssetFlagsForTrustedUsers = new ConfigAssetFlags
52+
int oldSafetyLevel = (int)oldConfig.MaximumAssetSafetyLevel;
53+
this.NormalUserPermissions.BlockedAssetFlags = new ConfigAssetFlags
4054
{
41-
Dangerous = oldTrustedSafetyLevel < 3,
42-
Modded = oldTrustedSafetyLevel < 2,
43-
Media = oldTrustedSafetyLevel < 1,
55+
Dangerous = oldSafetyLevel < 3,
56+
Modded = oldSafetyLevel < 2,
57+
Media = oldSafetyLevel < 1,
4458
};
4559
}
46-
catch (RuntimeBinderException)
60+
61+
// Asset safety level for trusted users was added in config version 12, so dont try to migrate if we are coming from a version older than that
62+
if (oldVer >= 12)
4763
{
48-
this.BlockedAssetFlagsForTrustedUsers = this.BlockedAssetFlags;
64+
// There was no version bump for trusted users being added, so we just have to catch this error :/
65+
try
66+
{
67+
int oldTrustedSafetyLevel = (int)oldConfig.MaximumAssetSafetyLevelForTrustedUsers;
68+
this.TrustedUserPermissions.BlockedAssetFlags = new ConfigAssetFlags
69+
{
70+
Dangerous = oldTrustedSafetyLevel < 3,
71+
Modded = oldTrustedSafetyLevel < 2,
72+
Media = oldTrustedSafetyLevel < 1,
73+
};
74+
}
75+
catch (RuntimeBinderException)
76+
{
77+
this.TrustedUserPermissions.BlockedAssetFlags = this.NormalUserPermissions.BlockedAssetFlags;
78+
}
4979
}
5080
}
81+
82+
// Timed level upload limits were added in version 19.
83+
if (oldVer >= 19)
84+
{
85+
this.NormalUserPermissions.TimedLevelUploadLimits.Enabled = (bool)oldConfig.TimedLevelUploadLimits.Enabled;
86+
this.NormalUserPermissions.TimedLevelUploadLimits.TimeSpanHours = (int)oldConfig.TimedLevelUploadLimits.TimeSpanHours;
87+
this.NormalUserPermissions.TimedLevelUploadLimits.LevelQuota = (int)oldConfig.TimedLevelUploadLimits.LevelQuota;
88+
89+
this.TrustedUserPermissions.TimedLevelUploadLimits.Enabled = (bool)oldConfig.TimedLevelUploadLimits.Enabled;
90+
this.TrustedUserPermissions.TimedLevelUploadLimits.TimeSpanHours = (int)oldConfig.TimedLevelUploadLimits.TimeSpanHours;
91+
this.TrustedUserPermissions.TimedLevelUploadLimits.LevelQuota = (int)oldConfig.TimedLevelUploadLimits.LevelQuota;
92+
}
93+
94+
// Read-only mode was added for both normal and trusted users in version 20.
95+
if (oldVer >= 20)
96+
{
97+
this.NormalUserPermissions.ReadOnlyMode = (bool)oldConfig.ReadOnlyMode;
98+
this.TrustedUserPermissions.ReadOnlyMode = (bool)oldConfig.ReadonlyModeForTrustedUsers;
99+
}
51100
}
52101
}
53102

54103
public string LicenseText { get; set; } = "Welcome to Refresh!";
55104

56-
public ConfigAssetFlags BlockedAssetFlags { get; set; } = new(AssetFlags.Dangerous | AssetFlags.Modded);
57-
/// <seealso cref="GameUserRole.Trusted"/>
58-
public ConfigAssetFlags BlockedAssetFlagsForTrustedUsers { get; set; } = new(AssetFlags.Dangerous | AssetFlags.Modded);
105+
/// <summary>
106+
/// Role-specific permissions for normal users and below
107+
/// </summary>
108+
public RolePermissions NormalUserPermissions = new();
109+
/// <summary>
110+
/// Role-specific permissions for trusted users and above
111+
/// </summary>
112+
public RolePermissions TrustedUserPermissions = new();
113+
59114
public bool AllowUsersToUseIpAuthentication { get; set; } = false;
60115
public bool PermitPsnLogin { get; set; } = true;
61116
public bool PermitRpcnLogin { get; set; } = true;
@@ -97,20 +152,6 @@ protected override void Migrate(int oldVer, dynamic oldConfig)
97152
/// </summary>
98153
public string GameConfigStorageUrl { get; set; } = "https://refresh.example.com/lbp";
99154
public bool AllowInvalidTextureGuids { get; set; } = false;
100-
public bool ReadOnlyMode { get; set; } = false;
101-
/// <seealso cref="GameUserRole.Trusted"/>
102-
public bool ReadonlyModeForTrustedUsers { get; set; } = false;
103-
/// <summary>
104-
/// The amount of data the user is allowed to upload before all resource uploads get blocked, defaults to 100mb.
105-
/// </summary>
106-
public int UserFilesizeQuota { get; set; } = 100 * 1_048_576;
107-
108-
public TimedLevelUploadLimitProperties TimedLevelUploadLimits { get; set; } = new()
109-
{
110-
Enabled = false,
111-
TimeSpanHours = 24,
112-
LevelQuota = 10,
113-
};
114155

115156
/// <summary>
116157
/// Whether to print the room state whenever a `FindBestRoom` match returns no results
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
using Refresh.Database.Models.Assets;
2+
3+
namespace Refresh.Core.Configuration;
4+
5+
public class RolePermissions
6+
{
7+
public RolePermissions() {}
8+
9+
public bool ReadOnlyMode { get; set; } = false;
10+
public ConfigAssetFlags BlockedAssetFlags { get; set; } = new(AssetFlags.Dangerous | AssetFlags.Modded);
11+
public TimedLevelUploadLimitProperties TimedLevelUploadLimits = new()
12+
{
13+
Enabled = false,
14+
TimeSpanHours = 24,
15+
LevelQuota = 10,
16+
};
17+
18+
/// <summary>
19+
/// The amount of data the user is allowed to upload before all resource uploads get blocked, defaults to 100mb.
20+
/// </summary>
21+
public int UserFilesizeQuota { get; set; } = 100 * 1_048_576;
22+
}

Refresh.Core/Extensions/GameUserExtensions.cs

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,8 @@ public static class GameUserExtensions
77
{
88
public static bool IsWriteBlocked(this GameUser user, GameServerConfig config)
99
{
10-
if (config.ReadOnlyMode && user.Role != GameUserRole.Admin)
11-
{
12-
return user.Role < GameUserRole.Trusted || config.ReadonlyModeForTrustedUsers;
13-
}
14-
15-
return false;
10+
if (user.Role == GameUserRole.Admin) return false;
11+
return GetRolePermissionsForUser(user, config).ReadOnlyMode;
1612
}
1713

1814
public static bool MayModifyUser(this GameUser user, GameUser targetUser)
@@ -27,4 +23,12 @@ public static bool MayModifyUser(this GameUser user, GameUser targetUser)
2723

2824
return true;
2925
}
26+
27+
public static RolePermissions GetRolePermissionsForUser(this GameUser user, GameServerConfig config)
28+
{
29+
if (user.Role >= GameUserRole.Trusted)
30+
return config.TrustedUserPermissions;
31+
32+
return config.NormalUserPermissions;
33+
}
3034
}

Refresh.Interfaces.APIv3/Endpoints/InstanceApiEndpoints.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,8 @@ public ApiResponse<ApiInstanceResponse> GetInstanceInformation(RequestContext co
6262
SoftwareSourceUrl = "https://github.com/LittleBigRefresh/Refresh",
6363
SoftwareLicenseName = "AGPL-3.0",
6464
SoftwareLicenseUrl = "https://www.gnu.org/licenses/agpl-3.0.txt",
65-
BlockedAssetFlags = gameConfig.BlockedAssetFlags,
66-
BlockedAssetFlagsForTrustedUsers = gameConfig.BlockedAssetFlagsForTrustedUsers,
65+
BlockedAssetFlags = gameConfig.NormalUserPermissions.BlockedAssetFlags,
66+
BlockedAssetFlagsForTrustedUsers = gameConfig.TrustedUserPermissions.BlockedAssetFlags,
6767
Announcements = ApiGameAnnouncementResponse.FromOldList(database.GetAnnouncements(), dataContext),
6868
MaintenanceModeEnabled = gameConfig.MaintenanceMode,
6969
RichPresenceConfiguration = ApiRichPresenceConfigurationResponse.FromOld(RichPresenceConfiguration.Create(

Refresh.Interfaces.APIv3/Endpoints/ResourceApiEndpoints.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -193,9 +193,10 @@ IntegrationConfig integration
193193
return new ApiValidationError($"The asset must be under 2MB. Your file was {body.Length:N0} bytes.");
194194
}
195195

196-
if (body.Length + user.FilesizeQuotaUsage > config.UserFilesizeQuota)
196+
RolePermissions rolePerms = user.GetRolePermissionsForUser(config);
197+
if (body.Length + user.FilesizeQuotaUsage > rolePerms.UserFilesizeQuota)
197198
{
198-
context.Logger.LogWarning(BunkumCategory.UserContent, "User {0} has hit the filesize quota ({1} bytes), rejecting.", user.Username, config.UserFilesizeQuota);
199+
context.Logger.LogWarning(BunkumCategory.UserContent, "User {0} has hit the filesize quota ({1} bytes), rejecting.", user.Username, rolePerms.UserFilesizeQuota);
199200
return new ApiValidationError($"You have exceeded your filesize quota.");
200201
}
201202

Refresh.Interfaces.Game/Endpoints/Levels/PublishEndpoints.cs

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -119,15 +119,16 @@ public Response StartPublish(RequestContext context,
119119
GameLevelRequest body,
120120
DataContext dataContext,
121121
GameServerConfig config,
122-
IDateTimeProvider dateTimeProvider)
122+
IDateTimeProvider dateTimeProvider,
123+
GameUser user)
123124
{
124125
if (dataContext.User!.IsWriteBlocked(config))
125126
{
126127
dataContext.Database.AddPublishFailNotification("The server is in read-only mode.", body.Title, dataContext.User!);
127128
return Unauthorized;
128129
}
129130

130-
if (IsTimedLevelLimitReached(dataContext, dataContext.User!, body.Title, config.TimedLevelUploadLimits, dateTimeProvider.Now))
131+
if (IsTimedLevelLimitReached(dataContext, dataContext.User!, body.Title, user.GetRolePermissionsForUser(config).TimedLevelUploadLimits, dateTimeProvider.Now))
131132
return Unauthorized;
132133

133134
//If verifying the request fails, return BadRequest
@@ -182,7 +183,8 @@ public Response PublishLevel(RequestContext context,
182183
if (user.IsWriteBlocked(config))
183184
return Unauthorized;
184185

185-
if (IsTimedLevelLimitReached(dataContext, user, body.Title, config.TimedLevelUploadLimits, dateTimeProvider.Now))
186+
TimedLevelUploadLimitProperties timedLevelLimit = user.GetRolePermissionsForUser(config).TimedLevelUploadLimits;
187+
if (IsTimedLevelLimitReached(dataContext, user, body.Title, timedLevelLimit, dateTimeProvider.Now))
186188
return Unauthorized;
187189

188190
//If verifying the request fails, return BadRequest
@@ -256,9 +258,9 @@ public Response PublishLevel(RequestContext context,
256258

257259
// Only increment if the level can be uploaded (right after the previous checks + adding the level),
258260
// don't want to increment for failed uploads
259-
if (config.TimedLevelUploadLimits.Enabled)
261+
if (timedLevelLimit.Enabled)
260262
{
261-
dataContext.Database.IncrementTimedLevelLimit(user, config.TimedLevelUploadLimits.TimeSpanHours);
263+
dataContext.Database.IncrementTimedLevelLimit(user, timedLevelLimit.TimeSpanHours);
262264
}
263265

264266
// Update the modded status of the level

Refresh.Interfaces.Game/Endpoints/ResourceEndpoints.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -48,9 +48,11 @@ public Response UploadAsset(RequestContext context, string hash, string type, by
4848
if (dataStore.ExistsInStore(assetPath))
4949
return Conflict;
5050

51-
if (body.Length + user.FilesizeQuotaUsage > config.UserFilesizeQuota)
51+
RolePermissions rolePerms = user.GetRolePermissionsForUser(config);
52+
53+
if (body.Length + user.FilesizeQuotaUsage > rolePerms.UserFilesizeQuota)
5254
{
53-
context.Logger.LogWarning(BunkumCategory.UserContent, "User {0} has hit the filesize quota ({1} bytes), rejecting.", user.Username, config.UserFilesizeQuota);
55+
context.Logger.LogWarning(BunkumCategory.UserContent, "User {0} has hit the filesize quota ({1} bytes), rejecting.", user.Username, rolePerms.UserFilesizeQuota);
5456
return RequestEntityTooLarge;
5557
}
5658

@@ -66,9 +68,7 @@ public Response UploadAsset(RequestContext context, string hash, string type, by
6668

6769
gameAsset.UploadDate = DateTimeOffset.FromUnixTimeSeconds(Math.Clamp(gameAsset.UploadDate.ToUnixTimeSeconds(), timeProvider.EarliestDate, timeProvider.TimestampSeconds));
6870

69-
AssetFlags blockedAssetFlags = config.BlockedAssetFlags.ToAssetFlags();
70-
if (user.Role >= GameUserRole.Trusted)
71-
blockedAssetFlags = config.BlockedAssetFlagsForTrustedUsers.ToAssetFlags();
71+
AssetFlags blockedAssetFlags = rolePerms.BlockedAssetFlags.ToAssetFlags();
7272

7373
// Don't block any assets uploaded from PSP, else block any unwanted assets,
7474
// For example, if the "blocked asset flags" has the "Media" bit set, and so does the asset,
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
using Refresh.Core.Configuration;
2+
using Refresh.Database.Models.Assets;
3+
4+
namespace RefreshTests.GameServer.GameServer;
5+
6+
public class TestGameServerConfig : GameServerConfig
7+
{
8+
public void TestMigration()
9+
{
10+
this.Migrate(this.Version, this);
11+
}
12+
13+
// Various attributes to migrate from
14+
public ConfigAssetFlags BlockedAssetFlags { get; set; } = new(AssetFlags.Dangerous | AssetFlags.Modded);
15+
public ConfigAssetFlags BlockedAssetFlagsForTrustedUsers { get; set; } = new(AssetFlags.Dangerous | AssetFlags.Modded);
16+
public bool ReadOnlyMode { get; set; } = false;
17+
public bool ReadonlyModeForTrustedUsers { get; set; } = false;
18+
public TimedLevelUploadLimitProperties TimedLevelUploadLimits { get; set; } = new()
19+
{
20+
Enabled = false,
21+
TimeSpanHours = 24,
22+
LevelQuota = 10,
23+
};
24+
25+
public int MaximumAssetSafetyLevel { get; set; } = 0;
26+
public int MaximumAssetSafetyLevelForTrustedUsers { get; set; } = 0;
27+
public int UserFilesizeQuota { get; set; } = 141;
28+
}

0 commit comments

Comments
 (0)