mirror of
https://github.com/DorkSquadRR/DorkNet.git
synced 2026-10-25 23:18:44 -07:00
Refactor room role permissions: enhance role checks for saving and overlaying data; introduce editable role data for RRO rooms
This commit is contained in:
@@ -1079,7 +1079,9 @@ public class RoomsController(
|
||||
// the all-perms default blob until their first save replaces
|
||||
// it via StorageController; the rec center / paintball / etc.
|
||||
// serve "" so the persistence flow short-circuits.
|
||||
var customisable = room.IsDormRoom || room.CreatorPlayerId != 1;
|
||||
var roleList = roles ?? Array.Empty<RoomRoleEntity>();
|
||||
var editableRro = room.IsAGRoom && roleList.Any(r => r.Accepted);
|
||||
var customisable = room.IsDormRoom || room.CreatorPlayerId != 1 || editableRro;
|
||||
var dataBlobName = !string.IsNullOrEmpty(room.CurrentDataBlobName)
|
||||
? room.CurrentDataBlobName
|
||||
: customisable ? $"room_{room.Id}_v1.dat" : "";
|
||||
@@ -1153,7 +1155,6 @@ public class RoomsController(
|
||||
// Additional grants come from RoomRoleEntity rows when supplied;
|
||||
// callers without role data (clone/myrooms/etc.) ship just the
|
||||
// implicit creator.
|
||||
var roleList = roles ?? Array.Empty<RoomRoleEntity>();
|
||||
int[] PlayersIn(int role, bool accepted) => roleList
|
||||
.Where(r => r.Role == role && r.Accepted == accepted)
|
||||
.Select(r => (int)r.PlayerId)
|
||||
@@ -1194,7 +1195,9 @@ public class RoomsController(
|
||||
string? overrideDataBlobName = null,
|
||||
IReadOnlyList<RoomRoleEntity>? roles = null)
|
||||
{
|
||||
var customisable = room.IsDormRoom || room.CreatorPlayerId != 1;
|
||||
var roleList = roles ?? Array.Empty<RoomRoleEntity>();
|
||||
var editableRro = room.IsAGRoom && roleList.Any(r => r.Accepted);
|
||||
var customisable = room.IsDormRoom || room.CreatorPlayerId != 1 || editableRro;
|
||||
var dataBlobName = !string.IsNullOrEmpty(room.CurrentDataBlobName)
|
||||
? room.CurrentDataBlobName
|
||||
: customisable ? $"room_{room.Id}_v1.dat" : "";
|
||||
@@ -1233,7 +1236,6 @@ public class RoomsController(
|
||||
},
|
||||
};
|
||||
|
||||
var roleList = roles ?? Array.Empty<RoomRoleEntity>();
|
||||
static int WireRole(int role) => role switch
|
||||
{
|
||||
0 => 30, // CoOwner
|
||||
|
||||
@@ -299,10 +299,17 @@ public class RoomsModerationController(DorkNetDbContext db) : ControllerBase
|
||||
return Ok(new { success = true, error = "" });
|
||||
|
||||
var room = await db.Rooms.FirstOrDefaultAsync(r => r.Id == roomId);
|
||||
int role = (room?.CreatorPlayerId == playerId) ? 30 : 0;
|
||||
var acceptedRoles = await db.RoomRoles
|
||||
.Where(r => r.RoomId == roomId && r.PlayerId == playerId && r.Accepted)
|
||||
.Select(r => r.Role)
|
||||
.ToListAsync();
|
||||
int role = (room?.CreatorPlayerId == playerId)
|
||||
? 30
|
||||
: acceptedRoles.Select(RoomRoleRank).DefaultIfEmpty(0).Max();
|
||||
long permissions = role switch
|
||||
{
|
||||
30 => -1L,
|
||||
20 => 0x0FFFFFFFL,
|
||||
10 => 0x0FFFFFFFL,
|
||||
5 => 0x000000FFL,
|
||||
_ => 0x0000000FL,
|
||||
|
||||
@@ -356,7 +356,7 @@ public class CdnController(
|
||||
{
|
||||
try
|
||||
{
|
||||
s3Bytes = roomDataBlob.OverlayAllPermsRoleData(s3Bytes);
|
||||
s3Bytes = roomDataBlob.OverlayRroEditableRoleData(s3Bytes);
|
||||
overlaidRoomRoleData = true;
|
||||
logger.LogInformation("[cdn] overlaid RRO room-role permissions host={Host} file={File} bytes={Bytes}",
|
||||
Request.Host.Host, fileName, s3Bytes.Length);
|
||||
@@ -393,6 +393,16 @@ public class CdnController(
|
||||
return new FileContentResult(TransparentPng, "image/png");
|
||||
}
|
||||
|
||||
if (await ShouldOverlayRroRoomRoleDataAsync(fileName))
|
||||
{
|
||||
logger.LogInformation("[cdn] MISS host={Host} file={File} -> RRO editable role blob",
|
||||
Request.Host.Host, fileName);
|
||||
var rroBlob = roomDataBlob.GetRroEditableBlob();
|
||||
Response.Headers.CacheControl = "public, max-age=60";
|
||||
signatures.AddContentSignature(Response, rroBlob);
|
||||
return new FileContentResult(rroBlob, "application/octet-stream");
|
||||
}
|
||||
|
||||
logger.LogInformation("[cdn] MISS host={Host} file={File} -> default blob",
|
||||
Request.Host.Host, fileName);
|
||||
var fallbackBlob = roomDataBlob.GetDefaultBlob();
|
||||
@@ -405,13 +415,24 @@ public class CdnController(
|
||||
if (string.IsNullOrWhiteSpace(fileName)) return false;
|
||||
|
||||
if (await db.Rooms.AsNoTracking().AnyAsync(r =>
|
||||
r.IsAGRoom && r.CurrentDataBlobName == fileName))
|
||||
r.IsAGRoom &&
|
||||
r.CurrentDataBlobName == fileName &&
|
||||
db.RoomRoles.Any(rr => rr.RoomId == r.Id && rr.Accepted)))
|
||||
return true;
|
||||
|
||||
if (fileName.StartsWith("room_", StringComparison.OrdinalIgnoreCase) &&
|
||||
fileName.EndsWith("_v1.dat", StringComparison.OrdinalIgnoreCase) &&
|
||||
long.TryParse(fileName[5..^7], out var roomId) &&
|
||||
await db.Rooms.AsNoTracking().AnyAsync(r => r.Id == roomId && r.IsAGRoom) &&
|
||||
await db.RoomRoles.AsNoTracking().AnyAsync(r => r.RoomId == roomId && r.Accepted))
|
||||
return true;
|
||||
|
||||
return await (
|
||||
from scene in db.RoomScenes.AsNoTracking()
|
||||
join room in db.Rooms.AsNoTracking() on scene.RoomId equals room.Id
|
||||
where room.IsAGRoom && scene.DataBlobName == fileName
|
||||
where room.IsAGRoom &&
|
||||
scene.DataBlobName == fileName &&
|
||||
db.RoomRoles.Any(rr => rr.RoomId == room.Id && rr.Accepted)
|
||||
select scene.Id
|
||||
).AnyAsync();
|
||||
}
|
||||
|
||||
@@ -28,10 +28,9 @@ namespace DorkNet.Server.Controllers.Storage;
|
||||
/// the room.</item>
|
||||
/// </list>
|
||||
///
|
||||
/// Permission rule: only the room's creator (or the dorm owner for a
|
||||
/// dorm save) can persist a new blob. Saves from other players in the
|
||||
/// same room are silently rejected with 403 — they can build with the
|
||||
/// Maker Pen but their changes won't outlive the session.
|
||||
/// Permission rule: the room's creator, accepted co-owners, and admins
|
||||
/// can persist a new blob. Dorm saves are always scoped to the local
|
||||
/// player's own dorm row.
|
||||
/// </summary>
|
||||
[ApiController]
|
||||
[Authorize]
|
||||
@@ -174,12 +173,14 @@ public class StorageController(
|
||||
if (room.IsDormRoom)
|
||||
return await UploadDormSaveAsync(playerId, bytes, references, room.Id);
|
||||
|
||||
// Permission: only the room owner can save. (Future Phase 5
|
||||
// expansion: co-owners + admins. For now, strict owner-only —
|
||||
// matches the 2020 official server's "RoomRolePermissions
|
||||
// .CanUseMakerPen" behaviour, which rejects upload at the wire
|
||||
// for non-owners.)
|
||||
if (room.CreatorPlayerId != playerId)
|
||||
var canSave = room.CreatorPlayerId == playerId
|
||||
|| await db.Players.AnyAsync(p => p.Id == playerId && p.IsAdmin)
|
||||
|| await db.RoomRoles.AnyAsync(r =>
|
||||
r.RoomId == room.Id &&
|
||||
r.PlayerId == playerId &&
|
||||
r.Accepted &&
|
||||
r.Role == 0);
|
||||
if (!canSave)
|
||||
{
|
||||
logger.LogWarning(
|
||||
"[storage] player {PlayerId} tried to save room {RoomId} (creator={CreatorId})",
|
||||
|
||||
Reference in New Issue
Block a user