Enhance role selection functionality in ListSelectableRolesQueryHandler and update frontend role display
- Updated ListSelectableRolesQueryHandler to include IIdentityService and ICurrentUser for user authorization and profile retrieval. - Added logic to filter out the current user's role and admin roles from the selectable roles list. - Enhanced CreateRoleRequestDialog to display role options with additional information, including max configs and IP limits, using localization support. - Updated i18n resources to include new role option formatting for both Russian and English.
This commit is contained in:
+13
-2
@@ -1,11 +1,15 @@
|
||||
using PnvPanel.Application.Auth;
|
||||
using PnvPanel.Application.Common.Interfaces;
|
||||
using PnvPanel.Application.Common.Messaging;
|
||||
using PnvPanel.Application.Common.Models;
|
||||
|
||||
namespace PnvPanel.Application.Support.ListSelectableRoles;
|
||||
|
||||
public sealed class ListSelectableRolesQueryHandler(IRoleService roleService)
|
||||
: IQueryHandler<ListSelectableRolesQuery, Result<IReadOnlyList<RoleDto>>>
|
||||
public sealed class ListSelectableRolesQueryHandler(
|
||||
IRoleService roleService,
|
||||
IIdentityService identityService,
|
||||
ICurrentUser currentUser
|
||||
) : IQueryHandler<ListSelectableRolesQuery, Result<IReadOnlyList<RoleDto>>>
|
||||
{
|
||||
// Совпадает со значением Infrastructure.Identity.RoleNames.Admin — см. пояснение в
|
||||
// CreateRoleRequestTicketCommandHandler (Application не может ссылаться на Infrastructure).
|
||||
@@ -16,9 +20,16 @@ public sealed class ListSelectableRolesQueryHandler(IRoleService roleService)
|
||||
CancellationToken cancellationToken
|
||||
)
|
||||
{
|
||||
if (currentUser.UserId is not { } userId)
|
||||
return Result.Failure<IReadOnlyList<RoleDto>>(AuthErrors.Unauthorized);
|
||||
|
||||
var profile = await identityService.GetProfileAsync(userId, cancellationToken);
|
||||
var roles = await roleService.ListRolesAsync(cancellationToken);
|
||||
|
||||
// Без admin (нельзя запросить) и без текущей роли пользователя (уже есть — нечего запрашивать).
|
||||
var selectable = roles
|
||||
.Where(r => !r.Name.Equals(AdminRoleName, StringComparison.OrdinalIgnoreCase))
|
||||
.Where(r => profile is null || r.Id != profile.RoleId)
|
||||
.ToList();
|
||||
|
||||
return Result.Success<IReadOnlyList<RoleDto>>(selectable);
|
||||
|
||||
+94
@@ -0,0 +1,94 @@
|
||||
using NSubstitute;
|
||||
using PnvPanel.Application.Common.Interfaces;
|
||||
using PnvPanel.Application.Support.ListSelectableRoles;
|
||||
using PnvPanel.Application.Tests.TestSupport;
|
||||
using Xunit;
|
||||
|
||||
namespace PnvPanel.Application.Tests.Support.ListSelectableRoles;
|
||||
|
||||
public class ListSelectableRolesQueryHandlerTests
|
||||
{
|
||||
private readonly IRoleService _roleService = Substitute.For<IRoleService>();
|
||||
private readonly IIdentityService _identityService = Substitute.For<IIdentityService>();
|
||||
|
||||
private static CurrentUserProfile Profile(Guid userId, Guid roleId, string role) =>
|
||||
new(
|
||||
userId,
|
||||
"user",
|
||||
roleId,
|
||||
role,
|
||||
IsActivated: true,
|
||||
IsBlocked: false,
|
||||
MaxConfigs: 3,
|
||||
MaxIpLimit: 1,
|
||||
SubscriptionToken: "token"
|
||||
);
|
||||
|
||||
[Fact]
|
||||
public async Task Handle_ExcludesAdminAndCurrentUserRole()
|
||||
{
|
||||
var userId = Guid.NewGuid();
|
||||
var currentRoleId = Guid.NewGuid();
|
||||
var extendedRoleId = Guid.NewGuid();
|
||||
var adminRoleId = Guid.NewGuid();
|
||||
|
||||
_roleService
|
||||
.ListRolesAsync(Arg.Any<CancellationToken>())
|
||||
.Returns(
|
||||
new List<RoleDto>
|
||||
{
|
||||
new(adminRoleId, "admin", -1, -1, IsSystem: true),
|
||||
new(currentRoleId, "user", 3, 1, IsSystem: true),
|
||||
new(extendedRoleId, "extended", 10, 5, IsSystem: false),
|
||||
}
|
||||
);
|
||||
_identityService
|
||||
.GetProfileAsync(userId, Arg.Any<CancellationToken>())
|
||||
.Returns(Profile(userId, currentRoleId, "user"));
|
||||
|
||||
var currentUser = FakeCurrentUser.Authenticated(userId, "alice");
|
||||
var handler = new ListSelectableRolesQueryHandler(
|
||||
_roleService,
|
||||
_identityService,
|
||||
currentUser
|
||||
);
|
||||
|
||||
var result = await handler.Handle(new ListSelectableRolesQuery(), CancellationToken.None);
|
||||
|
||||
Assert.True(result.IsSuccess);
|
||||
Assert.Equal(["extended"], result.Value.Select(r => r.Name));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Handle_WhenProfileMissing_StillExcludesAdmin()
|
||||
{
|
||||
var userId = Guid.NewGuid();
|
||||
var userRoleId = Guid.NewGuid();
|
||||
var adminRoleId = Guid.NewGuid();
|
||||
|
||||
_roleService
|
||||
.ListRolesAsync(Arg.Any<CancellationToken>())
|
||||
.Returns(
|
||||
new List<RoleDto>
|
||||
{
|
||||
new(adminRoleId, "admin", -1, -1, IsSystem: true),
|
||||
new(userRoleId, "user", 3, 1, IsSystem: true),
|
||||
}
|
||||
);
|
||||
_identityService
|
||||
.GetProfileAsync(userId, Arg.Any<CancellationToken>())
|
||||
.Returns((CurrentUserProfile?)null);
|
||||
|
||||
var currentUser = FakeCurrentUser.Authenticated(userId, "alice");
|
||||
var handler = new ListSelectableRolesQueryHandler(
|
||||
_roleService,
|
||||
_identityService,
|
||||
currentUser
|
||||
);
|
||||
|
||||
var result = await handler.Handle(new ListSelectableRolesQuery(), CancellationToken.None);
|
||||
|
||||
Assert.True(result.IsSuccess);
|
||||
Assert.Equal(["user"], result.Value.Select(r => r.Name));
|
||||
}
|
||||
}
|
||||
@@ -107,7 +107,11 @@ export function CreateRoleRequestDialog() {
|
||||
<SelectContent>
|
||||
{rolesQuery.data?.map((role) => (
|
||||
<SelectItem key={role.id} value={role.id}>
|
||||
{role.name}
|
||||
{t('support.roleOption', {
|
||||
name: role.name,
|
||||
configs: role.maxConfigs < 0 ? t('unlimited') : role.maxConfigs,
|
||||
ip: role.maxIpLimit < 0 ? t('unlimited') : role.maxIpLimit,
|
||||
})}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
|
||||
@@ -131,6 +131,7 @@ const resources = {
|
||||
existingRole: 'Существующая роль',
|
||||
newRole: 'Новая роль',
|
||||
selectRole: 'Выберите роль',
|
||||
roleOption: '{{name}} — конфигов: {{configs}}, IP: {{ip}}',
|
||||
newRoleName: 'Название роли',
|
||||
newRoleMaxConfigs: 'Количество конфигов (-1 — без лимита)',
|
||||
newRoleMaxIpLimit: 'Количество IP (-1 — без лимита)',
|
||||
@@ -548,6 +549,7 @@ const resources = {
|
||||
existingRole: 'Existing role',
|
||||
newRole: 'New role',
|
||||
selectRole: 'Select a role',
|
||||
roleOption: '{{name}} — configs: {{configs}}, IP: {{ip}}',
|
||||
newRoleName: 'Role name',
|
||||
newRoleMaxConfigs: 'Max configs (-1 = unlimited)',
|
||||
newRoleMaxIpLimit: 'Max IPs (-1 = unlimited)',
|
||||
|
||||
Reference in New Issue
Block a user