Skip to content

Commit 5715e28

Browse files
dooly123claude
andcommitted
Add admin permissions window with server-side permission management
Extends the AdminRequest protocol with permission CRUD operations (GetPermissions, SetUserGroup, SetUserNode, SetGroupNode, CreateGroup, DeleteGroup, SetGroupParent). Any authenticated user can view the permission snapshot; only admins can modify groups, nodes, and user assignments. Server-side changes persist to permissions.xml via the existing PermissionManager. Client-side adds a new Permissions tab in Settings with read-only display for normal users and full edit controls for admins. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 0d89d01 commit 5715e28

File tree

6 files changed

+772
-7
lines changed

6 files changed

+772
-7
lines changed

Basis Server/BasisNetworkServer/Security/BasisPlayerModeration.cs

Lines changed: 161 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using Basis.Network.Core;
22
using BasisNetworkCore;
3+
using BasisPermissions;
34
using System;
45
using System.Collections.Concurrent;
56
using System.Collections.Generic;
@@ -9,6 +10,7 @@
910
using System.Text;
1011
using System.Xml.Serialization;
1112
using static BasisNetworkCore.Serializable.SerializableBasis;
13+
using static BasisPermissions.PermissionManager;
1214

1315
namespace BasisNetworkServer.Security
1416
{
@@ -259,16 +261,29 @@ public static void OnAdminMessage(NetPeer peer, NetPacketReader reader)
259261
SendBackMessage(peer, msg);
260262
return;
261263
}
264+
265+
AdminRequest AdminRequest = new AdminRequest();
266+
AdminRequest.Deserialize(reader);
267+
AdminRequestMode Mode = AdminRequest.GetAdminRequestMode();
268+
269+
// GetPermissions is allowed for all authenticated users (read-only view)
270+
if (Mode == AdminRequestMode.GetPermissions)
271+
{
272+
HandleGetPermissions(peer, UUID);
273+
reader.Recycle();
274+
return;
275+
}
276+
277+
// All other admin operations require admin privileges
262278
if (!NetworkServer.AuthIdentity.IsNetPeerAdmin(UUID))
263279
{
264280
string msg = $"Was not admin! {UUID}";
265281
BNL.LogError(msg);
266282
SendBackMessage(peer, msg);
283+
reader.Recycle();
267284
return;
268285
}
269-
AdminRequest AdminRequest = new AdminRequest();
270-
AdminRequest.Deserialize(reader);
271-
AdminRequestMode Mode = AdminRequest.GetAdminRequestMode();
286+
272287
switch (Mode)
273288
{
274289
case AdminRequestMode.Ban:
@@ -324,10 +339,6 @@ public static void OnAdminMessage(NetPeer peer, NetPacketReader reader)
324339
}
325340
SendBackMessage(peer, ReturnMessage);
326341
break;
327-
// case AdminRequestMode.RequestBannedPlayers:
328-
// break;
329-
// case AdminRequestMode.TeleportTo:
330-
// break;
331342
case AdminRequestMode.TeleportAll:
332343

333344
Writer = NetworkServer.RentWriter();
@@ -371,6 +382,27 @@ public static void OnAdminMessage(NetPeer peer, NetPacketReader reader)
371382
NetworkServer.TrySend(peer, Writer, BasisNetworkCommons.AdminChannel, DeliveryMethod.ReliableOrdered);
372383
NetworkServer.ReturnWriter(Writer);
373384
break;
385+
386+
// --- Permission management ---
387+
case AdminRequestMode.SetUserGroup:
388+
HandleSetUserGroup(peer, reader);
389+
break;
390+
case AdminRequestMode.SetUserNode:
391+
HandleSetUserNode(peer, reader);
392+
break;
393+
case AdminRequestMode.SetGroupNode:
394+
HandleSetGroupNode(peer, reader);
395+
break;
396+
case AdminRequestMode.CreateGroup:
397+
HandleCreateGroup(peer, reader);
398+
break;
399+
case AdminRequestMode.DeleteGroup:
400+
HandleDeleteGroup(peer, reader);
401+
break;
402+
case AdminRequestMode.SetGroupParent:
403+
HandleSetGroupParent(peer, reader);
404+
break;
405+
374406
default:
375407
BNL.LogError("Missing Mode!");
376408
ReturnMessage = "Missing mode";
@@ -379,6 +411,128 @@ public static void OnAdminMessage(NetPeer peer, NetPacketReader reader)
379411
}
380412
reader.Recycle();
381413
}
414+
415+
#region Permission Handlers
416+
417+
/// <summary>
418+
/// Serializes the full permission store snapshot and sends it back to the requesting peer.
419+
/// Any authenticated user can call this (read-only).
420+
/// Also includes a bool indicating whether the requesting user is an admin.
421+
/// </summary>
422+
private static void HandleGetPermissions(NetPeer peer, string requestingUUID)
423+
{
424+
PermissionStore snapshot = PermissionIntegration.Manager.Snapshot();
425+
bool isAdmin = NetworkServer.AuthIdentity.IsNetPeerAdmin(requestingUUID);
426+
427+
NetDataWriter writer = NetworkServer.RentWriter();
428+
AdminRequest outRequest = new AdminRequest();
429+
outRequest.Serialize(writer, AdminRequestMode.GetPermissions);
430+
writer.Put(isAdmin);
431+
432+
// Serialize groups
433+
writer.Put(snapshot.Groups.Count);
434+
foreach (var g in snapshot.Groups.Values)
435+
{
436+
writer.Put(g.Name);
437+
writer.Put(g.Nodes.Count);
438+
foreach (string node in g.Nodes)
439+
writer.Put(node);
440+
writer.Put(g.Parents.Count);
441+
foreach (string parent in g.Parents)
442+
writer.Put(parent);
443+
}
444+
445+
// Serialize users
446+
writer.Put(snapshot.Users.Count);
447+
foreach (var u in snapshot.Users.Values)
448+
{
449+
writer.Put(u.Uuid);
450+
writer.Put(u.Groups.Count);
451+
foreach (string group in u.Groups)
452+
writer.Put(group);
453+
writer.Put(u.Nodes.Count);
454+
foreach (string node in u.Nodes)
455+
writer.Put(node);
456+
}
457+
458+
NetworkServer.TrySend(peer, writer, BasisNetworkCommons.AdminChannel, DeliveryMethod.ReliableOrdered);
459+
NetworkServer.ReturnWriter(writer);
460+
BNL.Log($"Sent permission snapshot to {requestingUUID} (admin={isAdmin})");
461+
}
462+
463+
private static void HandleSetUserGroup(NetPeer peer, NetPacketReader reader)
464+
{
465+
string uuid = reader.GetString();
466+
string group = reader.GetString();
467+
bool add = reader.GetBool();
468+
469+
if (add)
470+
PermissionIntegration.Manager.AddUserToGroup(uuid, group);
471+
else
472+
PermissionIntegration.Manager.RemoveUserFromGroup(uuid, group);
473+
474+
SendBackMessage(peer, $"{(add ? "Added" : "Removed")} user {uuid} {(add ? "to" : "from")} group '{group}'");
475+
}
476+
477+
private static void HandleSetUserNode(NetPeer peer, NetPacketReader reader)
478+
{
479+
string uuid = reader.GetString();
480+
string node = reader.GetString();
481+
bool add = reader.GetBool();
482+
483+
if (add)
484+
PermissionIntegration.Manager.AddUserNode(uuid, node);
485+
else
486+
PermissionIntegration.Manager.RemoveUserNode(uuid, node);
487+
488+
SendBackMessage(peer, $"{(add ? "Added" : "Removed")} node '{node}' {(add ? "to" : "from")} user {uuid}");
489+
}
490+
491+
private static void HandleSetGroupNode(NetPeer peer, NetPacketReader reader)
492+
{
493+
string groupName = reader.GetString();
494+
string node = reader.GetString();
495+
bool add = reader.GetBool();
496+
497+
if (add)
498+
PermissionIntegration.Manager.AddGroupNode(groupName, node);
499+
else
500+
PermissionIntegration.Manager.RemoveGroupNode(groupName, node);
501+
502+
SendBackMessage(peer, $"{(add ? "Added" : "Removed")} node '{node}' {(add ? "to" : "from")} group '{groupName}'");
503+
}
504+
505+
private static void HandleCreateGroup(NetPeer peer, NetPacketReader reader)
506+
{
507+
string groupName = reader.GetString();
508+
PermissionIntegration.Manager.GetOrCreateGroup(groupName);
509+
SendBackMessage(peer, $"Created group '{groupName}'");
510+
}
511+
512+
private static void HandleDeleteGroup(NetPeer peer, NetPacketReader reader)
513+
{
514+
string groupName = reader.GetString();
515+
if (PermissionIntegration.Manager.DeleteGroup(groupName))
516+
SendBackMessage(peer, $"Deleted group '{groupName}'");
517+
else
518+
SendBackMessage(peer, $"Failed to delete group '{groupName}' (not found)");
519+
}
520+
521+
private static void HandleSetGroupParent(NetPeer peer, NetPacketReader reader)
522+
{
523+
string groupName = reader.GetString();
524+
string parentName = reader.GetString();
525+
bool add = reader.GetBool();
526+
527+
if (add)
528+
PermissionIntegration.Manager.AddGroupParent(groupName, parentName);
529+
else
530+
PermissionIntegration.Manager.RemoveGroupParent(groupName, parentName);
531+
532+
SendBackMessage(peer, $"{(add ? "Added" : "Removed")} parent '{parentName}' {(add ? "to" : "from")} group '{groupName}'");
533+
}
534+
535+
#endregion
382536
public static void SendBackMessage(NetPeer Peer, string ReturnMessage)
383537
{
384538
if (string.IsNullOrEmpty(ReturnMessage))

Basis Server/BasisNetworkServer/Security/PermissionManager.cs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -428,6 +428,32 @@ public void RemoveGroupParent(string groupName, string parentName)
428428
SaveToXmlDebounced();
429429
}
430430

431+
public bool DeleteGroup(string groupName)
432+
{
433+
if (string.IsNullOrWhiteSpace(groupName)) return false;
434+
435+
_lock.EnterWriteLock();
436+
try
437+
{
438+
if (!_store.Groups.Remove(groupName))
439+
return false;
440+
441+
// Remove this group from all users that reference it
442+
foreach (var u in _store.Users.Values)
443+
u.Groups.Remove(groupName);
444+
445+
// Remove this group as a parent from other groups
446+
foreach (var g in _store.Groups.Values)
447+
g.Parents.Remove(groupName);
448+
449+
TouchAll();
450+
}
451+
finally { _lock.ExitWriteLock(); }
452+
453+
SaveToXmlDebounced();
454+
return true;
455+
}
456+
431457
// Snapshot store for saving or admin viewing
432458
public PermissionStore Snapshot()
433459
{

Basis/Packages/com.basis.framework/BasisUI/Menus/Main Menu Providers/SettingsProvider.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ public override void RunAction()
5656
AddLazyTab(tabGroup, "Storage", () => SettingsProviderStorage.StorageTab(tabGroup));
5757
AddLazyTab(tabGroup, "Console", () => SettingsProviderConsoleTab.ConsoleTab(tabGroup));
5858
AddLazyTab(tabGroup, "Developer", () => DeveloperTab(tabGroup));
59+
AddLazyTab(tabGroup, "Permissions", () => SettingsProviderPermissionsTab.PermissionsTab(tabGroup));
5960
AddLazyTab(tabGroup, "Admin", () => SettingsProviderAdminTab.AdminTab(tabGroup));
6061

6162
panel.Descriptor.ForceRebuild();

0 commit comments

Comments
 (0)