| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457 |
- using System.Linq;
- using System.Threading.Tasks;
- using Content.Server.Administration.Managers;
- using Content.Server.Database;
- using Content.Server.EUI;
- using Content.Shared.Administration;
- using Content.Shared.Eui;
- using Robust.Server.Player;
- using Robust.Shared.Network;
- using DbAdminRank = Content.Server.Database.AdminRank;
- using static Content.Shared.Administration.PermissionsEuiMsg;
- namespace Content.Server.Administration.UI
- {
- public sealed class PermissionsEui : BaseEui
- {
- [Dependency] private readonly IPlayerManager _playerManager = default!;
- [Dependency] private readonly IServerDbManager _db = default!;
- [Dependency] private readonly IAdminManager _adminManager = default!;
- [Dependency] private readonly ILogManager _logManager = default!;
- private readonly ISawmill _sawmill;
- private bool _isLoading;
- private readonly List<(Admin a, string? lastUserName)> _admins = new List<(Admin, string? lastUserName)>();
- private readonly List<DbAdminRank> _adminRanks = new();
- public PermissionsEui()
- {
- IoCManager.InjectDependencies(this);
- _sawmill = _logManager.GetSawmill("admin.perms");
- }
- public override void Opened()
- {
- base.Opened();
- StateDirty();
- LoadFromDb();
- _adminManager.OnPermsChanged += AdminManagerOnPermsChanged;
- }
- public override void Closed()
- {
- base.Closed();
- _adminManager.OnPermsChanged -= AdminManagerOnPermsChanged;
- }
- private void AdminManagerOnPermsChanged(AdminPermsChangedEventArgs obj)
- {
- // Close UI if user loses +PERMISSIONS.
- if (obj.Player == Player && !UserAdminFlagCheck(AdminFlags.Permissions))
- {
- Close();
- }
- }
- public override EuiStateBase GetNewState()
- {
- if (_isLoading)
- {
- return new PermissionsEuiState
- {
- IsLoading = true
- };
- }
- return new PermissionsEuiState
- {
- Admins = _admins.Select(p => new PermissionsEuiState.AdminData
- {
- PosFlags = AdminFlagsHelper.NamesToFlags(p.a.Flags.Where(f => !f.Negative).Select(f => f.Flag)),
- NegFlags = AdminFlagsHelper.NamesToFlags(p.a.Flags.Where(f => f.Negative).Select(f => f.Flag)),
- Title = p.a.Title,
- RankId = p.a.AdminRankId,
- UserId = new NetUserId(p.a.UserId),
- UserName = p.lastUserName,
- Suspended = p.a.Suspended,
- }).ToArray(),
- AdminRanks = _adminRanks.ToDictionary(a => a.Id, a => new PermissionsEuiState.AdminRankData
- {
- Flags = AdminFlagsHelper.NamesToFlags(a.Flags.Select(p => p.Flag)),
- Name = a.Name
- })
- };
- }
- public override async void HandleMessage(EuiMessageBase msg)
- {
- base.HandleMessage(msg);
- switch (msg)
- {
- case AddAdmin ca:
- {
- await HandleCreateAdmin(ca);
- break;
- }
- case UpdateAdmin ua:
- {
- await HandleUpdateAdmin(ua);
- break;
- }
- case RemoveAdmin ra:
- {
- await HandleRemoveAdmin(ra);
- break;
- }
- case AddAdminRank ar:
- {
- await HandleAddAdminRank(ar);
- break;
- }
- case UpdateAdminRank ur:
- {
- await HandleUpdateAdminRank(ur);
- break;
- }
- case RemoveAdminRank ra:
- {
- await HandleRemoveAdminRank(ra);
- break;
- }
- }
- if (!IsShutDown)
- {
- LoadFromDb();
- }
- }
- private async Task HandleRemoveAdminRank(RemoveAdminRank rr)
- {
- var rank = await _db.GetAdminRankAsync(rr.Id);
- if (rank == null)
- {
- return;
- }
- if (!CanTouchRank(rank))
- {
- _sawmill.Warning($"{Player} tried to remove higher-ranked admin rank {rank.Name}");
- return;
- }
- await _db.RemoveAdminRankAsync(rr.Id);
- _adminManager.ReloadAdminsWithRank(rr.Id);
- }
- private async Task HandleUpdateAdminRank(UpdateAdminRank ur)
- {
- var rank = await _db.GetAdminRankAsync(ur.Id);
- if (rank == null)
- {
- return;
- }
- if (!CanTouchRank(rank))
- {
- _sawmill.Warning($"{Player} tried to update higher-ranked admin rank {rank.Name}");
- return;
- }
- if (!UserAdminFlagCheck(ur.Flags))
- {
- _sawmill.Warning($"{Player} tried to give a rank permissions above their authorization.");
- return;
- }
- rank.Flags = GenRankFlagList(ur.Flags);
- rank.Name = ur.Name;
- await _db.UpdateAdminRankAsync(rank);
- var flagText = string.Join(' ', AdminFlagsHelper.FlagsToNames(ur.Flags).Select(f => $"+{f}"));
- _sawmill.Info($"{Player} updated admin rank {rank.Name}/{flagText}.");
- _adminManager.ReloadAdminsWithRank(ur.Id);
- }
- private async Task HandleAddAdminRank(AddAdminRank ar)
- {
- if (!UserAdminFlagCheck(ar.Flags))
- {
- _sawmill.Warning($"{Player} tried to give a rank permissions above their authorization.");
- return;
- }
- var rank = new DbAdminRank
- {
- Name = ar.Name,
- Flags = GenRankFlagList(ar.Flags)
- };
- await _db.AddAdminRankAsync(rank);
- var flagText = string.Join(' ', AdminFlagsHelper.FlagsToNames(ar.Flags).Select(f => $"+{f}"));
- _sawmill.Info($"{Player} added admin rank {rank.Name}/{flagText}.");
- }
- private async Task HandleRemoveAdmin(RemoveAdmin ra)
- {
- var admin = await _db.GetAdminDataForAsync(ra.UserId);
- if (admin == null)
- {
- // Doesn't exist.
- return;
- }
- if (!CanTouchAdmin(admin))
- {
- _sawmill.Warning($"{Player} tried to remove higher-ranked admin {ra.UserId.ToString()}");
- return;
- }
- await _db.RemoveAdminAsync(ra.UserId);
- var record = await _db.GetPlayerRecordByUserId(ra.UserId);
- _sawmill.Info($"{Player} removed admin {record?.LastSeenUserName ?? ra.UserId.ToString()}");
- if (_playerManager.TryGetSessionById(ra.UserId, out var player))
- {
- _adminManager.ReloadAdmin(player);
- }
- }
- private async Task HandleUpdateAdmin(UpdateAdmin ua)
- {
- if (!CheckCreatePerms(ua.PosFlags, ua.NegFlags))
- {
- return;
- }
- var admin = await _db.GetAdminDataForAsync(ua.UserId);
- if (admin == null)
- {
- // Was removed in the mean time I guess?
- return;
- }
- if (!CanTouchAdmin(admin))
- {
- _sawmill.Warning($"{Player} tried to modify higher-ranked admin {ua.UserId.ToString()}");
- return;
- }
- admin.Title = ua.Title;
- admin.AdminRankId = ua.RankId;
- admin.Flags = GenAdminFlagList(ua.PosFlags, ua.NegFlags);
- admin.Suspended = ua.Suspended;
- await _db.UpdateAdminAsync(admin);
- var playerRecord = await _db.GetPlayerRecordByUserId(ua.UserId);
- var (bad, rankName) = await FetchAndCheckRank(ua.RankId);
- if (bad)
- {
- return;
- }
- var name = playerRecord?.LastSeenUserName ?? ua.UserId.ToString();
- var title = ua.Title ?? "<no title>";
- var flags = AdminFlagsHelper.PosNegFlagsText(ua.PosFlags, ua.NegFlags);
- _sawmill.Info($"{Player} updated admin {name} to {title}/{rankName}/{flags}");
- if (_playerManager.TryGetSessionById(ua.UserId, out var player))
- {
- _adminManager.ReloadAdmin(player);
- }
- }
- private async Task HandleCreateAdmin(AddAdmin ca)
- {
- if (!CheckCreatePerms(ca.PosFlags, ca.NegFlags))
- {
- return;
- }
- string name;
- NetUserId userId;
- if (Guid.TryParse(ca.UserNameOrId, out var guid))
- {
- userId = new NetUserId(guid);
- var playerRecord = await _db.GetPlayerRecordByUserId(userId);
- if (playerRecord == null)
- {
- name = userId.ToString();
- }
- else
- {
- name = playerRecord.LastSeenUserName;
- }
- }
- else
- {
- // Username entered, resolve user ID from DB.
- var dbPlayer = await _db.GetPlayerRecordByUserName(ca.UserNameOrId);
- if (dbPlayer == null)
- {
- // username not in DB.
- // TODO: Notify user.
- _sawmill.Warning($"{Player} tried to add admin with unknown username {ca.UserNameOrId}.");
- return;
- }
- userId = dbPlayer.UserId;
- name = ca.UserNameOrId;
- }
- var existing = await _db.GetAdminDataForAsync(userId);
- if (existing != null)
- {
- // Already exists.
- return;
- }
- var (bad, rankName) = await FetchAndCheckRank(ca.RankId);
- if (bad)
- {
- return;
- }
- rankName ??= "<no rank>";
- var admin = new Admin
- {
- Flags = GenAdminFlagList(ca.PosFlags, ca.NegFlags),
- AdminRankId = ca.RankId,
- UserId = userId.UserId,
- Title = ca.Title,
- Suspended = ca.Suspended,
- };
- await _db.AddAdminAsync(admin);
- var title = ca.Title ?? "<no title>";
- var flags = AdminFlagsHelper.PosNegFlagsText(ca.PosFlags, ca.NegFlags);
- _sawmill.Info($"{Player} added admin {name} as {title}/{rankName}/{flags}");
- if (_playerManager.TryGetSessionById(userId, out var player))
- {
- _adminManager.ReloadAdmin(player);
- }
- }
- // ReSharper disable once ParameterOnlyUsedForPreconditionCheck.Local
- private bool CheckCreatePerms(AdminFlags posFlags, AdminFlags negFlags)
- {
- if ((posFlags & negFlags) != 0)
- {
- // Can't have overlapping pos and neg flags.
- // Just deny the entire message.
- return false;
- }
- if (!UserAdminFlagCheck(posFlags))
- {
- // Can't create an admin with higher perms than yourself, obviously.
- _sawmill.Warning($"{Player} tried to grant admin powers above their authorization.");
- return false;
- }
- return true;
- }
- private async Task<(bool bad, string?)> FetchAndCheckRank(int? rankId)
- {
- string? ret = null;
- if (rankId is { } r)
- {
- var rank = await _db.GetAdminRankAsync(r);
- if (rank == null)
- {
- // Tried to set to nonexistent rank.
- _sawmill.Warning($"{Player} tried to assign nonexistent admin rank.");
- return (true, null);
- }
- ret = rank.Name;
- var rankFlags = AdminFlagsHelper.NamesToFlags(rank.Flags.Select(p => p.Flag));
- if (!UserAdminFlagCheck(rankFlags))
- {
- // Can't assign a rank with flags you don't have yourself.
- _sawmill.Warning($"{Player} tried to assign admin rank above their authorization.");
- return (true, null);
- }
- }
- return (false, ret);
- }
- private async void LoadFromDb()
- {
- StateDirty();
- _isLoading = true;
- var (admins, ranks) = await _db.GetAllAdminAndRanksAsync();
- _admins.Clear();
- _admins.AddRange(admins);
- _adminRanks.Clear();
- _adminRanks.AddRange(ranks);
- _isLoading = false;
- StateDirty();
- }
- private static List<AdminFlag> GenAdminFlagList(AdminFlags posFlags, AdminFlags negFlags)
- {
- var posFlagList = AdminFlagsHelper.FlagsToNames(posFlags);
- var negFlagList = AdminFlagsHelper.FlagsToNames(negFlags);
- return posFlagList
- .Select(f => new AdminFlag {Negative = false, Flag = f})
- .Concat(negFlagList.Select(f => new AdminFlag {Negative = true, Flag = f}))
- .ToList();
- }
- private static List<AdminRankFlag> GenRankFlagList(AdminFlags flags)
- {
- return AdminFlagsHelper.FlagsToNames(flags).Select(f => new AdminRankFlag {Flag = f}).ToList();
- }
- private bool UserAdminFlagCheck(AdminFlags flags)
- {
- return _adminManager.HasAdminFlag(Player, flags);
- }
- private bool CanTouchAdmin(Admin admin)
- {
- var posFlags = AdminFlagsHelper.NamesToFlags(admin.Flags.Where(f => !f.Negative).Select(f => f.Flag));
- var rankFlags = AdminFlagsHelper.NamesToFlags(
- admin.AdminRank?.Flags.Select(f => f.Flag) ?? Array.Empty<string>());
- var totalFlags = posFlags | rankFlags;
- return UserAdminFlagCheck(totalFlags);
- }
- private bool CanTouchRank(DbAdminRank rank)
- {
- var rankFlags = AdminFlagsHelper.NamesToFlags(rank.Flags.Select(f => f.Flag));
- return UserAdminFlagCheck(rankFlags);
- }
- }
- }
|