Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions EXILED/Exiled.Permissions/Config.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,12 @@ public Config()
[Description("The permissions full path")]
public string FullPath { get; private set; }

/// <summary>
/// Gets a value indicating whether multi-group system should be used.
/// </summary>
[Description("Whether MultiGroup system should be used")]
public bool UseMultiGroup { get; private set; } = false;

/// <inheritdoc/>
public bool IsEnabled { get; set; } = true;

Expand Down
47 changes: 39 additions & 8 deletions EXILED/Exiled.Permissions/Extensions/Permissions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,14 @@ namespace Exiled.Permissions.Extensions
using System.Text;

using CommandSystem;

using Exiled.API.Extensions;
using Exiled.API.Features;
using Exiled.API.Features.Pools;
using Features;

using Features.MultipleGroups;
using Properties;
using Query;
using RemoteAdmin;

using YamlDotNet.Core;
using YamlDotNet.Serialization;
using YamlDotNet.Serialization.NamingConventions;
Expand Down Expand Up @@ -207,15 +205,48 @@ public static bool CheckPermission(this Player player, string permission)
Log.Debug($"UserID: {player.UserId} | PlayerId: {player.Id}");
Log.Debug($"Permission string: {permission}");

string plyGroupKey = player.Group is not null ? ServerStatic.PermissionsHandler.Groups.FirstOrDefault(g => g.Value.EqualsTo(player.Group)).Key : null;
Log.Debug($"GroupKey: {plyGroupKey ?? "(null)"}");
if (!MultiGroup.IsActive)
{
string plyGroupKey = player.Group is not null ? ServerStatic.PermissionsHandler.Groups.FirstOrDefault(g => g.Value.EqualsTo(player.Group)).Key : null;
Log.Debug($"GroupKey: {plyGroupKey ?? "(null)"}");

if (plyGroupKey is null || !Groups.TryGetValue(plyGroupKey, out Group group))
{
Log.Debug("The source group is null, the default group is used");
group = DefaultGroup;
}

return group.CheckPermission(permission);
}

if (!MultiGroup.Permissions.TryGetValue(player.UserId, out string[] data))
return false;

if (plyGroupKey is null || !Groups.TryGetValue(plyGroupKey, out Group group))
foreach (string group in data)
{
Log.Debug("The source group is null, the default group is used");
group = DefaultGroup;
if (!Groups.TryGetValue(group, out Group value))
{
Log.Error($"Invalid group name: {group}");
continue;
}

if (value.CheckPermission(permission))
return true;
}

return false;
}

/// <summary>
/// Checks a group's permission.
/// </summary>
/// <param name="group">The group to be checked.</param>
/// <param name="permission">The permission to be checked.</param>
/// <returns><see langword="true"/> if the player's current or native group has permissions; otherwise, <see langword="false"/>.</returns>
public static bool CheckPermission(this Group group, string permission)
{
group ??= DefaultGroup;

if (group is null)
{
Log.Debug("There's no default group, returning false...");
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
// -----------------------------------------------------------------------
// <copyright file="BaseCheckPatch.cs" company="ExMod Team">
// Copyright (c) ExMod Team. All rights reserved.
// Licensed under the CC BY-SA 3.0 license.
// </copyright>
// -----------------------------------------------------------------------

namespace Exiled.Permissions.Features.MultipleGroups
{
using System.Collections.Generic;
using System.Reflection.Emit;

using CommandSystem;
using Exiled.API.Features;
using HarmonyLib;
using Query;
using RemoteAdmin;

/// <summary>
/// Patches base-game method to implement multiple groups check per user.
/// </summary>
[HarmonyPatch]
internal static class BaseCheckPatch
{
private static bool Check(ICommandSender sender, PlayerPermissions[] permissions)
{
if (sender is not CommandSender commandSender)
return false;

if (commandSender.FullPermissions || sender is ServerConsoleSender || commandSender == Server.Host.Sender)
{
return true;
}

if (sender is PlayerCommandSender || sender is QueryCommandSender)
{
if (!Player.TryGet(sender, out Player player))
return false;

if (!MultiGroup.Permissions.TryGetValue(player.UserId, out string[] data))
return false;

foreach (string groupName in data)
{
UserGroup group = ServerStatic.PermissionsHandler.GetGroup(groupName);
if (group == null)
{
Log.Error($"Invalid group name: {groupName}");
continue;
}

if (PermissionsHandler.IsPermitted(group.Permissions, permissions))
return true;
}
}

return false;
}

private static bool Check(ICommandSender sender, PlayerPermissions permissions) => Check(sender, new[] { permissions });

[HarmonyPatch(typeof(Misc), nameof(Misc.CheckPermission), typeof(ICommandSender), typeof(PlayerPermissions))]
private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
{
yield return new(OpCodes.Ldarg_0);
yield return new(OpCodes.Ldarg_1);
yield return new(OpCodes.Call, AccessTools.Method(typeof(BaseCheckPatch), nameof(Check), new[] { typeof(ICommandSender), typeof(PlayerPermissions) }));
yield return new(OpCodes.Ret);
}

[HarmonyTranspiler]
[HarmonyPatch(typeof(Misc), nameof(Misc.CheckPermission), typeof(ICommandSender), typeof(PlayerPermissions[]))]
private static IEnumerable<CodeInstruction> TranspilerMore(IEnumerable<CodeInstruction> instructions)
{
yield return new(OpCodes.Ldarg_0);
yield return new(OpCodes.Ldarg_1);
yield return new(OpCodes.Call, AccessTools.Method(typeof(BaseCheckPatch), nameof(Check), new[] { typeof(ICommandSender), typeof(PlayerPermissions[]) }));
yield return new(OpCodes.Ret);
}
}
}
105 changes: 105 additions & 0 deletions EXILED/Exiled.Permissions/Features/MultipleGroups/MultiGroup.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
// -----------------------------------------------------------------------
// <copyright file="MultiGroup.cs" company="ExMod Team">
// Copyright (c) ExMod Team. All rights reserved.
// Licensed under the CC BY-SA 3.0 license.
// </copyright>
// -----------------------------------------------------------------------

namespace Exiled.Permissions.Features.MultipleGroups
{
#pragma warning disable SA1401
using System;
using System.Collections.Generic;

using Exiled.API.Features;
using Exiled.API.Features.Pools;

using HarmonyLib;

/// <summary>
/// Main class for Multi-Group system.
/// </summary>
public static class MultiGroup
{
/// <summary>
/// A value indicating whether the system is active.
/// </summary>
internal static bool IsActive = false;

/// <summary>
/// Internal harmony instance.
/// </summary>
private static Harmony harmony;

/// <summary>
/// Gets a collection of all groups of all users.
/// </summary>
public static Dictionary<string, string[]> Permissions { get; private set; }

/// <summary>
/// Main method for initializing MultiGroup.
/// </summary>
/// <param name="force">Whether the load should be forced, ignoring config.</param>
public static void Init(bool force = false) // TODO: add list pooling
{
if (!Exiled.Permissions.Permissions.Instance.Config.UseMultiGroup && !force)
return;

List<string> groups = ServerStatic.PermissionsHandler._config.GetStringList("AdditionalGroups");
Dictionary<string, List<string>> users = new();

foreach (string entry in groups)
{
if (!entry.Contains(": "))
{
Log.Error($"Invalid entry at AdditionalGroups in config_remoteadmin.txt: {entry}");
continue;
}

int length = entry.IndexOf(": ", StringComparison.Ordinal);
string key = entry.Substring(0, length);
string value = entry.Substring(length + 2);

users.GetOrAdd(key, () => ListPool<string>.Pool.Get()).Add(value);
}

if (users.Count == 0)
return;

try
{
harmony = new("exiled.permissions-multigroups");
harmony.CreateClassProcessor(typeof(BaseCheckPatch)).Patch();
}
catch (HarmonyException exception)
{
Log.Error($"Patch error while patching for MultiGroups! {exception}");
return;
}

IsActive = true;
Permissions = new();

foreach (KeyValuePair<string, List<string>> kvp in users)
{
if (ServerStatic.PermissionsHandler.Members.TryGetValue(kvp.Key, out string group))
kvp.Value.Add(group);

Permissions[kvp.Key] = ListPool<string>.Pool.ToArrayReturn(kvp.Value);
}
}

/// <summary>
/// Disables module and frees all resources.
/// </summary>
public static void Disable()
{
if (!IsActive)
return;

IsActive = false;
harmony.UnpatchAll("exiled.permissions-multigroups");
Permissions.Clear();
}
}
}
5 changes: 5 additions & 0 deletions EXILED/Exiled.Permissions/Permissions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
namespace Exiled.Permissions
{
using Exiled.API.Features;
using Features.MultipleGroups;

using MEC;

Expand Down Expand Up @@ -36,12 +37,16 @@ public override void OnEnabled()
{
Extensions.Permissions.Create();
Extensions.Permissions.Reload();

MultiGroup.Init();
});
}

/// <inheritdoc/>
public override void OnDisabled()
{
MultiGroup.Disable();

base.OnDisabled();

Extensions.Permissions.Groups.Clear();
Expand Down
Loading