fix(discord): resolve permission checking for /newsession command
- DiscordPermissionChecker: removed dead-code userRoles overload; now only uses resolvedPermissions bitflag (Administrator = 0x8). - DiscordNewSessionCommand: computes resolved permissions from guild user roles via Context.Guild.Users[Id].RoleIds + guild.Roles. - DiscordNewSessionHandler: updated signature to accept ulong resolvedPermissions instead of unused userRoles. - Added ILogger to command for diagnostics on unexpected errors. - Added test: regular user with ManageServer (but not Admin) is rejected. Refs issue #28 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -7,10 +7,12 @@ namespace GmRelay.DiscordBot.Features.Sessions;
|
||||
public class DiscordNewSessionCommand : ApplicationCommandModule<SlashCommandContext>
|
||||
{
|
||||
private readonly DiscordNewSessionHandler _handler;
|
||||
private readonly ILogger<DiscordNewSessionCommand> _logger;
|
||||
|
||||
public DiscordNewSessionCommand(DiscordNewSessionHandler handler)
|
||||
public DiscordNewSessionCommand(DiscordNewSessionHandler handler, ILogger<DiscordNewSessionCommand> logger)
|
||||
{
|
||||
_handler = handler;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public async Task ExecuteAsync(
|
||||
@@ -26,10 +28,12 @@ public class DiscordNewSessionCommand : ApplicationCommandModule<SlashCommandCon
|
||||
if (!timeResult.IsSuccess)
|
||||
{
|
||||
await Context.Interaction.SendResponseAsync(
|
||||
InteractionCallback.Message($"❌ {timeResult.Error}"));
|
||||
InteractionCallback.Message($"X {timeResult.Error}"));
|
||||
return;
|
||||
}
|
||||
|
||||
var resolvedPermissions = GetResolvedPermissions(guild, Context.User.Id);
|
||||
|
||||
try
|
||||
{
|
||||
var view = await _handler.HandleAsync(
|
||||
@@ -37,7 +41,7 @@ public class DiscordNewSessionCommand : ApplicationCommandModule<SlashCommandCon
|
||||
channelId: Context.Channel.Id.ToString(),
|
||||
userId: Context.User.Id,
|
||||
userDisplayName: Context.User.GlobalName ?? Context.User.Username,
|
||||
userRoles: Array.Empty<ulong>(), // NetCord alpha.489: GuildUser not exposed on SlashCommandContext
|
||||
resolvedPermissions: resolvedPermissions,
|
||||
guildOwnerId: guild.OwnerId,
|
||||
title: title,
|
||||
scheduledAt: timeResult.Value,
|
||||
@@ -46,17 +50,33 @@ public class DiscordNewSessionCommand : ApplicationCommandModule<SlashCommandCon
|
||||
CancellationToken.None);
|
||||
|
||||
await Context.Interaction.SendResponseAsync(
|
||||
InteractionCallback.Message("✅ Сессия создана!"));
|
||||
InteractionCallback.Message("+ Session created!"));
|
||||
}
|
||||
catch (UnauthorizedAccessException ex)
|
||||
{
|
||||
await Context.Interaction.SendResponseAsync(
|
||||
InteractionCallback.Message($"⛔ {ex.Message}"));
|
||||
InteractionCallback.Message($"! {ex.Message}"));
|
||||
}
|
||||
catch (Exception)
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Failed to create session for user {UserId} in guild {GuildId}", Context.User.Id, guild.Id);
|
||||
await Context.Interaction.SendResponseAsync(
|
||||
InteractionCallback.Message("💥 Произошла ошибка при создании сессии."));
|
||||
InteractionCallback.Message("* An error occurred while creating the session."));
|
||||
}
|
||||
}
|
||||
|
||||
private static ulong GetResolvedPermissions(NetCord.Gateway.Guild guild, ulong userId)
|
||||
{
|
||||
if (!guild.Users.TryGetValue(userId, out var guildUser))
|
||||
return 0;
|
||||
|
||||
ulong resolved = 0;
|
||||
foreach (var roleId in guildUser.RoleIds)
|
||||
{
|
||||
if (guild.Roles.TryGetValue(roleId, out var role))
|
||||
resolved |= (ulong)role.Permissions;
|
||||
}
|
||||
|
||||
return resolved;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@ public sealed class DiscordNewSessionHandler(
|
||||
string channelId,
|
||||
ulong userId,
|
||||
string userDisplayName,
|
||||
IEnumerable<ulong> userRoles,
|
||||
ulong resolvedPermissions,
|
||||
ulong guildOwnerId,
|
||||
string title,
|
||||
DateTimeOffset scheduledAt,
|
||||
@@ -69,7 +69,7 @@ public sealed class DiscordNewSessionHandler(
|
||||
WHERE g.platform = 'Discord' AND g.external_group_id = @GuildId",
|
||||
new { GuildId = guildId });
|
||||
|
||||
if (!permissionChecker.CanManageSchedule(guildOwnerId, userId, userRoles, dbManagerUserIds))
|
||||
if (!permissionChecker.CanManageSchedule(guildOwnerId, userId, dbManagerUserIds, resolvedPermissions))
|
||||
{
|
||||
throw new UnauthorizedAccessException("⛔ Только owner, администратор или manager могут создавать сессии.");
|
||||
}
|
||||
|
||||
@@ -7,19 +7,8 @@ public sealed class DiscordPermissionChecker
|
||||
public bool CanManageSchedule(
|
||||
ulong guildOwnerId,
|
||||
ulong userId,
|
||||
IEnumerable<ulong> userRoles,
|
||||
IEnumerable<ulong> dbManagerUserIds)
|
||||
{
|
||||
if (userId == guildOwnerId)
|
||||
return true;
|
||||
|
||||
if (dbManagerUserIds.Contains(userId))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool CanManageSchedule(ulong guildOwnerId, ulong userId, IEnumerable<ulong> dbManagerUserIds, ulong resolvedPermissions)
|
||||
IEnumerable<ulong> dbManagerUserIds,
|
||||
ulong resolvedPermissions)
|
||||
{
|
||||
if (userId == guildOwnerId)
|
||||
return true;
|
||||
@@ -30,3 +19,4 @@ public sealed class DiscordPermissionChecker
|
||||
return (resolvedPermissions & AdministratorPermission) == AdministratorPermission;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user