Skip to content
38 changes: 29 additions & 9 deletions LabApi/Features/Permissions/PermissionsManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using LabApi.Features.Wrappers;
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;

namespace LabApi.Features.Permissions;
Expand Down Expand Up @@ -31,13 +32,7 @@ public static void RegisterProvider<T>()
return;
}

if (Activator.CreateInstance<T>() is not IPermissionsProvider provider)
{
Logger.Error($"{LoggerPrefix} Failed to create an instance of the permission provider of type {typeof(T).FullName}.");
return;
}

PermissionProviders.Add(typeof(T), provider);
PermissionProviders.Add(typeof(T), new T());
}

/// <summary>
Expand All @@ -62,13 +57,38 @@ public static void UnregisterProvider<T>()
/// <returns>The registered <see cref="IPermissionsProvider"/> of the given type <typeparamref name="T"/>; otherwise, null.</returns>
public static IPermissionsProvider? GetProvider<T>()
where T : IPermissionsProvider, new()
=> GetProvider(typeof(T));

/// <summary>
/// Retrieves the registered <see cref="IPermissionsProvider"/> of the given type <typeparamref name="T"/>.
/// </summary>
/// <typeparam name="T">The type of the permission provider to retrieve.</typeparam>
/// <returns>The registered <see cref="IPermissionsProvider"/> of the given type <typeparamref name="T"/>; otherwise, null.</returns>
public static bool TryGetProvider<T>([NotNullWhen(true)] out T? provider)
where T : class, IPermissionsProvider, new()
{
if (PermissionProviders.TryGetValue(typeof(T), out IPermissionsProvider provider))
provider = GetProvider(typeof(T)) as T;
return provider != null;
}

/// <summary>
/// Retrieves the registered <see cref="IPermissionsProvider"/> of the given type <paramref name="providerType"/>.
/// </summary>
/// <param name="providerType">The type of the permission provider to retrieve.</param>
/// <returns>The registered <see cref="IPermissionsProvider"/> of the given type <paramref name="providerType"/>; otherwise, null.</returns>
public static IPermissionsProvider? GetProvider(Type providerType)
{
if (providerType == null)
{
throw new ArgumentNullException(nameof(providerType));
}

if (PermissionProviders.TryGetValue(providerType, out IPermissionsProvider provider))
{
return provider;
}

Logger.Warn($"{LoggerPrefix} The permission provider of type {typeof(T).FullName} is not registered.");
Logger.Warn($"{LoggerPrefix} The permission provider of type {providerType.FullName} is not registered.");
return null;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using Serialization;
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;

Expand Down Expand Up @@ -110,9 +111,34 @@ public void RemovePermissions(Player player, params string[] permissions)
/// <inheritdoc />
void IPermissionsProvider.ReloadPermissions() => ReloadPermissions();

private PermissionGroup GetPlayerGroup(Player player) => _permissionsDictionary.GetValueOrDefault(player.PermissionsGroupName ?? "default") ?? PermissionGroup.Default;
/// <summary>
/// Gets the <see cref="PermissionGroup"/> the player is a part of.
/// </summary>
/// <param name="player">The player whose <see cref="PermissionGroup"/> to find.</param>
/// <returns>The <see cref="PermissionGroup"/> the player is a part of, otherwise <see cref="PermissionGroup.Default"/>.</returns>
public PermissionGroup GetPlayerGroup(Player player) => GetPermissionGroup(player.PermissionsGroupName ?? "default");

/// <summary>
/// Gets the <see cref="PermissionGroup"/> from a <see cref="UserGroup.Name"/>.
/// </summary>
/// <param name="groupName">A <see cref="UserGroup.Name"/> to find the <see cref="PermissionGroup"/> of.</param>
/// <returns>A <see cref="PermissionGroup"/> if one is defined, <see cref="PermissionGroup.Default"/> otherwise.</returns>
public PermissionGroup GetPermissionGroup(string groupName) => _permissionsDictionary.GetValueOrDefault(groupName) ?? PermissionGroup.Default;

/// <summary>
/// Tries to get the <see cref="PermissionGroup"/> from a <see cref="UserGroup.Name"/>.
/// </summary>
/// <param name="groupName">A <see cref="UserGroup.Name"/> to find the <see cref="PermissionGroup"/> of.</param>
/// <param name="permissionGroup">The found <see cref="PermissionGroup"/> when true, null otherwise.</param>
/// <returns>Whether a <see cref="PermissionGroup"/> with the registry name of <paramref name="groupName"/> was found.</returns>
public bool TryGetPermissionGroup(string groupName, [NotNullWhen(true)] out PermissionGroup? permissionGroup) => _permissionsDictionary.TryGetValue(groupName, out permissionGroup);

private string[] GetPermissions(PermissionGroup group)
/// <summary>
/// Gets the <see cref="string"/> array of permissions a <see cref="PermissionGroup"/> grants.
/// </summary>
/// <param name="group">The <see cref="PermissionGroup"/>, permissions of which will be returned.</param>
/// <returns>A <see cref="string"/> array of permissions this group grants.</returns>
public string[] GetPermissions(PermissionGroup group)
{
List<string> permissions = ListPool<string>.Shared.Rent();

Expand All @@ -132,6 +158,36 @@ private string[] GetPermissions(PermissionGroup group)
return [.. permissions];
}

/// <summary>
/// Adds a new permission group.
/// </summary>
/// <param name="groupName">A <see cref="UserGroup.Name"/> the <paramref name="group"/> is linked to.</param>
/// <param name="group">The group to add.</param>
/// <returns>Whether the group was successfully added. False if group with this name is already registered or if the <paramref name="group"/>'s <see cref="PermissionGroup.IsRuntime"/> is set to false.</returns>
public bool AddPermissionGroup(string groupName, PermissionGroup group)
=> group.IsRuntime && _permissionsDictionary.TryAdd(groupName, group);

/// <summary>
/// Removes a permission group if it exists.
/// </summary>
/// <param name="groupName">A <see cref="UserGroup.Name"/> which links to a <see cref="PermissionGroup"/> that is to be removed.</param>
/// <param name="group">The group which was removed or null.</param>
/// <returns>Whether the group was found and removed successfully. False if group with this name could not be found or if the <paramref name="group"/>'s <see cref="PermissionGroup.IsRuntime"/> is set to false.</returns>
public bool RemovePermissionGroup(string groupName, [NotNullWhen(true)] out PermissionGroup? group)
{
if (!_permissionsDictionary.TryGetValue(groupName, out group))
{
return false;
}

if (!group.IsRuntime)
{
return false;
}

return _permissionsDictionary.Remove(groupName, out group);
}

private bool HasPermission(PermissionGroup group, string permission)
{
if (group.IsRoot)
Expand Down Expand Up @@ -208,5 +264,5 @@ private void ReloadPermissions()
}
}

private void SavePermissions() => File.WriteAllText(_permissions.FullName, YamlParser.Serializer.Serialize(_permissionsDictionary));
private void SavePermissions() => File.WriteAllText(_permissions.FullName, YamlParser.Serializer.Serialize(_permissionsDictionary.Where(kvp => !kvp.Value.IsRuntime).ToDictionary(kvp => kvp.Key, kvp => kvp.Value)));
}
18 changes: 15 additions & 3 deletions LabApi/Features/Permissions/Providers/PermissionGroup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ public class PermissionGroup
/// <summary>
/// Gets the default permission group.
/// </summary>
public static PermissionGroup Default => new([], []);
public static PermissionGroup Default => new([], []) { IsRuntime = false };

/// <summary>
/// Generates the default permission groups based on the available groups in the RA settings.
Expand All @@ -32,8 +32,11 @@ public static Dictionary<string, PermissionGroup> DefaultPermissionGroups
/// <summary>
/// Constructor for deserialization.
/// </summary>
/// <remarks>
/// All objects created by this constructor will have their <see cref="IsRuntime"/> property set to false.
/// </remarks>
public PermissionGroup()
: this([], [])
: this([], [], false)
{
}

Expand All @@ -42,10 +45,12 @@ public PermissionGroup()
/// </summary>
/// <param name="inheritedGroups">Array of groups that should be inherited.</param>
/// <param name="permissions">Array of permissions this group should have.</param>
public PermissionGroup(string[] inheritedGroups, string[] permissions)
/// <param name="isRuntime">Bool indicating whether the <see cref="PermissionGroup"/> should skip being saved to the permission file config. See: <seealso cref="IsRuntime"/>.</param>
public PermissionGroup(string[] inheritedGroups, string[] permissions, bool isRuntime = true)
{
InheritedGroups = inheritedGroups;
Permissions = permissions;
IsRuntime = isRuntime;
}

/// <summary>
Expand All @@ -64,6 +69,13 @@ public PermissionGroup(string[] inheritedGroups, string[] permissions)
[YamlIgnore]
public bool IsRoot { get; set; } = false;

/// <summary>
/// A bool indicating whether the permission was created at runtime and should not be saved.
/// Will not be saved if set to true.
/// </summary>
[YamlIgnore]
public bool IsRuntime { get; internal set; }

/// <summary>
/// An internal dictionary that saves special permissions. (x.*).
/// </summary>
Expand Down