Browse Source

Merge pull request #24775 from abpframework/Prevent-Privilege-Escalation

Prevent Privilege Escalation: Add Assignment Restrictions for Roles and Permissions
pull/24900/head
Engincan VESKE 7 months ago
committed by GitHub
parent
commit
3a477a8868
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 10
      framework/src/Volo.Abp.Security/Volo/Abp/Roles/AbpRoleConsts.cs
  2. 52
      modules/identity/src/Volo.Abp.Identity.Application/Volo/Abp/Identity/IdentityUserAppService.cs
  3. 16
      modules/identity/src/Volo.Abp.Identity.Blazor/Pages/Identity/UserManagement.razor
  4. 22
      modules/identity/src/Volo.Abp.Identity.Blazor/Pages/Identity/UserManagement.razor.cs
  5. 3
      modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityDataSeeder.cs
  6. 9
      modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Users/EditModal.cshtml
  7. 34
      modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Users/EditModal.cshtml.cs
  8. 39
      modules/identity/test/Volo.Abp.Identity.Application.Tests/Volo/Abp/Identity/FakeCurrentPrincipalAccessor.cs
  9. 136
      modules/identity/test/Volo.Abp.Identity.Application.Tests/Volo/Abp/Identity/IdentityUserAppService_Tests.cs
  10. 5
      modules/identity/test/Volo.Abp.Identity.TestBase/Volo/Abp/Identity/AbpIdentityTestDataBuilder.cs
  11. 8
      modules/identity/test/Volo.Abp.Identity.TestBase/Volo/Abp/Identity/IdentityRoleRepository_Tests.cs
  12. 1
      modules/identity/test/Volo.Abp.Identity.TestBase/Volo/Abp/Identity/IdentityTestData.cs
  13. 8
      modules/identity/test/Volo.Abp.Identity.TestBase/Volo/Abp/Identity/IdentityUserRepository_Tests.cs
  14. 2
      modules/permission-management/src/Volo.Abp.PermissionManagement.Application.Contracts/Volo/Abp/PermissionManagement/PermissionGrantInfoDto.cs
  15. 72
      modules/permission-management/src/Volo.Abp.PermissionManagement.Application/Volo/Abp/PermissionManagement/PermissionAppService.cs
  16. 45
      modules/permission-management/src/Volo.Abp.PermissionManagement.Blazor/Components/PermissionManagementModal.razor.cs
  17. 3
      modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/PermissionDataSeedContributor.cs
  18. 23
      modules/permission-management/src/Volo.Abp.PermissionManagement.Web/Pages/AbpPermissionManagement/PermissionManagementModal.cshtml.cs
  19. 14
      modules/permission-management/test/Volo.Abp.PermissionManagement.Application.Tests/Volo/Abp/PermissionManagement/AbpPermissionManagementApplicationTestBase.cs
  20. 53
      modules/permission-management/test/Volo.Abp.PermissionManagement.Application.Tests/Volo/Abp/PermissionManagement/FakePermissionChecker.cs
  21. 120
      modules/permission-management/test/Volo.Abp.PermissionManagement.Application.Tests/Volo/Abp/PermissionManagement/PermissionAppService_Tests.cs

10
framework/src/Volo.Abp.Security/Volo/Abp/Roles/AbpRoleConsts.cs

@ -0,0 +1,10 @@
namespace Volo.Abp.Roles;
public static class AbpRoleConsts
{
/// <summary>
/// The static name of the admin role.
/// Default value: "admin"
/// </summary>
public const string AdminRoleName = "admin";
}

52
modules/identity/src/Volo.Abp.Identity.Application/Volo/Abp/Identity/IdentityUserAppService.cs

@ -9,6 +9,8 @@ using Volo.Abp.Application.Dtos;
using Volo.Abp.Authorization.Permissions; using Volo.Abp.Authorization.Permissions;
using Volo.Abp.Data; using Volo.Abp.Data;
using Volo.Abp.ObjectExtending; using Volo.Abp.ObjectExtending;
using Volo.Abp.Roles;
using Volo.Abp.Users;
namespace Volo.Abp.Identity; namespace Volo.Abp.Identity;
@ -69,7 +71,18 @@ public class IdentityUserAppService : IdentityAppServiceBase, IIdentityUserAppSe
[Authorize(IdentityPermissions.Users.Default)] [Authorize(IdentityPermissions.Users.Default)]
public virtual async Task<ListResultDto<IdentityRoleDto>> GetAssignableRolesAsync() public virtual async Task<ListResultDto<IdentityRoleDto>> GetAssignableRolesAsync()
{ {
var list = (await RoleRepository.GetListAsync()).OrderBy(x => x.Name).ToList(); List<IdentityRole> list;
if (await HasAdminRoleAsync())
{
list = (await RoleRepository.GetListAsync()).OrderBy(x => x.Name).ToList();
}
else
{
var currentUserRoles = await UserManager.GetRolesAsync(await UserManager.GetByIdAsync(CurrentUser.GetId()));
list = (await RoleRepository.GetListAsync(currentUserRoles)).OrderBy(x => x.Name).ToList();
}
return new ListResultDto<IdentityRoleDto>(ObjectMapper.Map<List<IdentityRole>, List<IdentityRoleDto>>(list)); return new ListResultDto<IdentityRoleDto>(ObjectMapper.Map<List<IdentityRole>, List<IdentityRoleDto>>(list));
} }
@ -145,7 +158,9 @@ public class IdentityUserAppService : IdentityAppServiceBase, IIdentityUserAppSe
{ {
await IdentityOptions.SetAsync(); await IdentityOptions.SetAsync();
var user = await UserManager.GetByIdAsync(id); var user = await UserManager.GetByIdAsync(id);
(await UserManager.SetRolesAsync(user, input.RoleNames)).CheckErrors();
var effectiveRoles = await FilterRolesByCurrentUserAsync(user, input.RoleNames);
(await UserManager.SetRolesAsync(user, effectiveRoles)).CheckErrors();
await UserRepository.UpdateAsync(user); await UserRepository.UpdateAsync(user);
} }
@ -189,7 +204,38 @@ public class IdentityUserAppService : IdentityAppServiceBase, IIdentityUserAppSe
(await UserManager.UpdateAsync(user)).CheckErrors(); (await UserManager.UpdateAsync(user)).CheckErrors();
if (input.RoleNames != null && await PermissionChecker.IsGrantedAsync(IdentityPermissions.Users.ManageRoles)) if (input.RoleNames != null && await PermissionChecker.IsGrantedAsync(IdentityPermissions.Users.ManageRoles))
{ {
(await UserManager.SetRolesAsync(user, input.RoleNames)).CheckErrors(); var effectiveRoles = await FilterRolesByCurrentUserAsync(user, input.RoleNames);
(await UserManager.SetRolesAsync(user, effectiveRoles)).CheckErrors();
}
}
protected virtual async Task<string[]> FilterRolesByCurrentUserAsync(IdentityUser user, string[] inputRoleNames)
{
if (await HasAdminRoleAsync())
{
return (inputRoleNames ?? Array.Empty<string>())
.Distinct(StringComparer.OrdinalIgnoreCase)
.ToArray();
} }
var targetCurrentRoleSet = (await UserManager.GetRolesAsync(user)).ToHashSet(StringComparer.OrdinalIgnoreCase);
var operatorUser = await UserManager.GetByIdAsync(CurrentUser.GetId());
var operatorOwnRoleSet = (await UserManager.GetRolesAsync(operatorUser)).ToHashSet(StringComparer.OrdinalIgnoreCase);
var inputRoleNameSet = new HashSet<string>(inputRoleNames ?? Array.Empty<string>(), StringComparer.OrdinalIgnoreCase);
var keepUnmanageableRoles = targetCurrentRoleSet.Except(operatorOwnRoleSet, StringComparer.OrdinalIgnoreCase);
var desiredManageableRoles = inputRoleNameSet.Intersect(operatorOwnRoleSet, StringComparer.OrdinalIgnoreCase);
return keepUnmanageableRoles
.Concat(desiredManageableRoles)
.Distinct(StringComparer.OrdinalIgnoreCase)
.ToArray();
}
protected virtual Task<bool> HasAdminRoleAsync()
{
return Task.FromResult(CurrentUser.IsInRole(AbpRoleConsts.AdminRoleName));
} }
} }

16
modules/identity/src/Volo.Abp.Identity.Blazor/Pages/Identity/UserManagement.razor

@ -142,13 +142,13 @@
<TabPanel Name="Roles"> <TabPanel Name="Roles">
@if (NewUserRoles != null) @if (NewUserRoles != null)
{ {
@foreach (var role in NewUserRoles) @foreach (var role in NewUserRoles)
{ {
<Field> <Field>
<input type="hidden" @bind-value="@role.Name" /> <input type="hidden" @bind-value="@role.Name" />
<Check TValue="bool" @bind-Checked="@role.IsAssigned">@role.Name</Check> <Check TValue="bool" @bind-Checked="@role.IsAssigned" Disabled="@(!role.IsAssignable)">@role.Name</Check>
</Field> </Field>
} }
} }
</TabPanel> </TabPanel>
</Content> </Content>
@ -277,7 +277,7 @@
{ {
<Field> <Field>
<input type="hidden" @bind-value="@role.Name" /> <input type="hidden" @bind-value="@role.Name" />
<Check TValue="bool" @bind-Checked="@role.IsAssigned">@role.Name</Check> <Check TValue="bool" @bind-Checked="@role.IsAssigned" Disabled="@(!role.IsAssignable)">@role.Name</Check>
</Field> </Field>
} }
} }

22
modules/identity/src/Volo.Abp.Identity.Blazor/Pages/Identity/UserManagement.razor.cs

@ -103,7 +103,8 @@ public partial class UserManagement
NewUserRoles = Roles.Select(x => new AssignedRoleViewModel NewUserRoles = Roles.Select(x => new AssignedRoleViewModel
{ {
Name = x.Name, Name = x.Name,
IsAssigned = x.IsDefault IsAssigned = x.IsDefault,
IsAssignable = true
}).ToArray(); }).ToArray();
ChangePasswordTextRole(TextRole.Password); ChangePasswordTextRole(TextRole.Password);
@ -130,12 +131,23 @@ public partial class UserManagement
if (await PermissionChecker.IsGrantedAsync(IdentityPermissions.Users.ManageRoles)) if (await PermissionChecker.IsGrantedAsync(IdentityPermissions.Users.ManageRoles))
{ {
var userRoleIds = (await AppService.GetRolesAsync(entity.Id)).Items.Select(r => r.Id).ToList(); var assignableRoles = Roles ?? (await AppService.GetAssignableRolesAsync()).Items;
var currentRoles = (await AppService.GetRolesAsync(entity.Id)).Items;
EditUserRoles = Roles.Select(x => new AssignedRoleViewModel var combinedRoles = assignableRoles
.Concat(currentRoles)
.GroupBy(role => role.Id)
.Select(group => group.First())
.ToList();
var currentRoleIds = currentRoles.Select(r => r.Id).ToHashSet();
var assignableRoleIds = assignableRoles.Select(r => r.Id).ToHashSet();
EditUserRoles = combinedRoles.Select(x => new AssignedRoleViewModel
{ {
Name = x.Name, Name = x.Name,
IsAssigned = userRoleIds.Contains(x.Id) IsAssigned = currentRoleIds.Contains(x.Id),
IsAssignable = assignableRoleIds.Contains(x.Id)
}).ToArray(); }).ToArray();
ChangePasswordTextRole(TextRole.Password); ChangePasswordTextRole(TextRole.Password);
@ -262,4 +274,6 @@ public class AssignedRoleViewModel
public string Name { get; set; } public string Name { get; set; }
public bool IsAssigned { get; set; } public bool IsAssigned { get; set; }
public bool IsAssignable { get; set; }
} }

3
modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityDataSeeder.cs

@ -5,6 +5,7 @@ using Microsoft.Extensions.Options;
using Volo.Abp.DependencyInjection; using Volo.Abp.DependencyInjection;
using Volo.Abp.Guids; using Volo.Abp.Guids;
using Volo.Abp.MultiTenancy; using Volo.Abp.MultiTenancy;
using Volo.Abp.Roles;
using Volo.Abp.Uow; using Volo.Abp.Uow;
namespace Volo.Abp.Identity; namespace Volo.Abp.Identity;
@ -83,7 +84,7 @@ public class IdentityDataSeeder : ITransientDependency, IIdentityDataSeeder
result.CreatedAdminUser = true; result.CreatedAdminUser = true;
//"admin" role //"admin" role
const string adminRoleName = "admin"; const string adminRoleName = AbpRoleConsts.AdminRoleName;
var adminRole = var adminRole =
await RoleRepository.FindByNormalizedNameAsync(LookupNormalizer.NormalizeName(adminRoleName)); await RoleRepository.FindByNormalizedNameAsync(LookupNormalizer.NormalizeName(adminRoleName));
if (adminRole == null) if (adminRole == null)

9
modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Users/EditModal.cshtml

@ -83,7 +83,14 @@
@for (var i = 0; i < Model.Roles.Length; i++) @for (var i = 0; i < Model.Roles.Length; i++)
{ {
var role = Model.Roles[i]; var role = Model.Roles[i];
<abp-input abp-id-name="@Model.Roles[i].IsAssigned" asp-for="@role.IsAssigned" label="@role.Name" /> @if (role.IsAssignable)
{
<abp-input abp-id-name="@Model.Roles[i].IsAssigned" asp-for="@role.IsAssigned" label="@role.Name" />
}
else
{
<abp-input abp-id-name="@Model.Roles[i].IsAssigned" asp-for="@role.IsAssigned" label="@role.Name" disabled="true" readonly="true" />
}
<input abp-id-name="@Model.Roles[i].Name" asp-for="@role.Name" /> <input abp-id-name="@Model.Roles[i].Name" asp-for="@role.Name" />
} }
</div> </div>

34
modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Users/EditModal.cshtml.cs

@ -41,18 +41,32 @@ public class EditModalModel : IdentityPageModel
UserInfo = ObjectMapper.Map<IdentityUserDto, UserInfoViewModel>(user); UserInfo = ObjectMapper.Map<IdentityUserDto, UserInfoViewModel>(user);
if (await PermissionChecker.IsGrantedAsync(IdentityPermissions.Users.ManageRoles)) if (await PermissionChecker.IsGrantedAsync(IdentityPermissions.Users.ManageRoles))
{ {
Roles = ObjectMapper.Map<IReadOnlyList<IdentityRoleDto>, AssignedRoleViewModel[]>((await IdentityUserAppService.GetAssignableRolesAsync()).Items); var assignableRoles = (await IdentityUserAppService.GetAssignableRolesAsync()).Items;
} var currentRoles = (await IdentityUserAppService.GetRolesAsync(id)).Items;
IsEditCurrentUser = CurrentUser.Id == id;
// Combine assignable and current roles to show all roles user has
var userRoleIds = (await IdentityUserAppService.GetRolesAsync(UserInfo.Id)).Items.Select(r => r.Id).ToList(); var combinedRoles = assignableRoles
foreach (var role in Roles) .Concat(currentRoles)
{ .GroupBy(role => role.Id)
if (userRoleIds.Contains(role.Id)) .Select(group => group.First())
.ToList();
Roles = ObjectMapper.Map<IReadOnlyList<IdentityRoleDto>, AssignedRoleViewModel[]>(combinedRoles);
var currentRoleIds = currentRoles.Select(r => r.Id).ToHashSet();
var assignableRoleIds = assignableRoles.Select(r => r.Id).ToHashSet();
foreach (var role in Roles)
{ {
role.IsAssigned = true; role.IsAssigned = currentRoleIds.Contains(role.Id);
role.IsAssignable = assignableRoleIds.Contains(role.Id);
} }
} }
else
{
Roles = Array.Empty<AssignedRoleViewModel>();
}
IsEditCurrentUser = CurrentUser.Id == id;
Detail = ObjectMapper.Map<IdentityUserDto, DetailViewModel>(user); Detail = ObjectMapper.Map<IdentityUserDto, DetailViewModel>(user);
@ -129,6 +143,8 @@ public class EditModalModel : IdentityPageModel
public string Name { get; set; } public string Name { get; set; }
public bool IsAssigned { get; set; } public bool IsAssigned { get; set; }
public bool IsAssignable { get; set; }
} }
public class DetailViewModel public class DetailViewModel

39
modules/identity/test/Volo.Abp.Identity.Application.Tests/Volo/Abp/Identity/FakeCurrentPrincipalAccessor.cs

@ -0,0 +1,39 @@
using System;
using System.Collections.Generic;
using System.Security.Claims;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Security.Claims;
namespace Volo.Abp.Identity;
[Dependency(ReplaceServices = true)]
public class FakeCurrentPrincipalAccessor : ThreadCurrentPrincipalAccessor
{
private readonly IdentityTestData _testData;
private readonly Lazy<ClaimsPrincipal> _principal;
public FakeCurrentPrincipalAccessor(IdentityTestData testData)
{
_testData = testData;
_principal = new Lazy<ClaimsPrincipal>(() => new ClaimsPrincipal(
new ClaimsIdentity(
new List<Claim>
{
new Claim(AbpClaimTypes.UserId, _testData.UserAdminId.ToString()),
new Claim(AbpClaimTypes.UserName, "administrator"),
new Claim(AbpClaimTypes.Email, "administrator@abp.io")
}
)
));
}
protected override ClaimsPrincipal GetClaimsPrincipal()
{
return GetPrincipal();
}
private ClaimsPrincipal GetPrincipal()
{
return _principal.Value;
}
}

136
modules/identity/test/Volo.Abp.Identity.Application.Tests/Volo/Abp/Identity/IdentityUserAppService_Tests.cs

@ -1,10 +1,13 @@
using System; using System;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks; using System.Threading.Tasks;
using Shouldly; using Shouldly;
using Volo.Abp.Authorization.Permissions; using Volo.Abp.Authorization.Permissions;
using Volo.Abp.Data; using Volo.Abp.Data;
using Volo.Abp.PermissionManagement; using Volo.Abp.PermissionManagement;
using Volo.Abp.PermissionManagement.Identity; using Volo.Abp.PermissionManagement.Identity;
using Volo.Abp.Security.Claims;
using Xunit; using Xunit;
namespace Volo.Abp.Identity; namespace Volo.Abp.Identity;
@ -16,6 +19,7 @@ public class IdentityUserAppService_Tests : AbpIdentityApplicationTestBase
private readonly IPermissionManager _permissionManager; private readonly IPermissionManager _permissionManager;
private readonly UserPermissionManagementProvider _userPermissionManagementProvider; private readonly UserPermissionManagementProvider _userPermissionManagementProvider;
private readonly IdentityTestData _testData; private readonly IdentityTestData _testData;
private readonly ICurrentPrincipalAccessor _currentPrincipalAccessor;
public IdentityUserAppService_Tests() public IdentityUserAppService_Tests()
{ {
@ -24,6 +28,7 @@ public class IdentityUserAppService_Tests : AbpIdentityApplicationTestBase
_permissionManager = GetRequiredService<IPermissionManager>(); _permissionManager = GetRequiredService<IPermissionManager>();
_userPermissionManagementProvider = GetRequiredService<UserPermissionManagementProvider>(); _userPermissionManagementProvider = GetRequiredService<UserPermissionManagementProvider>();
_testData = GetRequiredService<IdentityTestData>(); _testData = GetRequiredService<IdentityTestData>();
_currentPrincipalAccessor = GetRequiredService<ICurrentPrincipalAccessor>();
} }
[Fact] [Fact]
@ -239,6 +244,137 @@ public class IdentityUserAppService_Tests : AbpIdentityApplicationTestBase
roleNames.ShouldContain("manager"); roleNames.ShouldContain("manager");
} }
[Fact]
public async Task UpdateRolesAsync_Should_Not_Assign_Roles_Operator_Does_Not_Have()
{
// neo only has "supporter" role
using (_currentPrincipalAccessor.Change(new Claim(AbpClaimTypes.UserId, _testData.UserNeoId.ToString())))
{
// Try to assign "admin" and "supporter" to david (who has no roles)
await _userAppService.UpdateRolesAsync(
_testData.UserDavidId,
new IdentityUserUpdateRolesDto
{
RoleNames = new[] { "admin", "supporter" }
}
);
}
// Only "supporter" should be assigned (admin filtered out since neo doesn't have it)
var roleNames = await _userRepository.GetRoleNamesAsync(_testData.UserDavidId);
roleNames.ShouldContain("supporter");
roleNames.ShouldNotContain("admin");
}
[Fact]
public async Task UpdateRolesAsync_Should_Preserve_Unmanageable_Roles()
{
// john.nash has direct roles: moderator, supporter
// neo only has "supporter" role, so "moderator" is unmanageable for neo
using (_currentPrincipalAccessor.Change(new Claim(AbpClaimTypes.UserId, _testData.UserNeoId.ToString())))
{
await _userAppService.UpdateRolesAsync(
_testData.UserJohnId,
new IdentityUserUpdateRolesDto
{
RoleNames = new[] { "supporter" }
}
);
}
// "moderator" should be preserved (unmanageable), "supporter" kept (in input)
var roleNames = await _userRepository.GetRoleNamesAsync(_testData.UserJohnId);
roleNames.ShouldContain("moderator");
roleNames.ShouldContain("supporter");
}
[Fact]
public async Task UpdateRolesAsync_Should_Only_Remove_Manageable_Roles()
{
// john.nash has direct roles: moderator, supporter
// neo only has "supporter" role
using (_currentPrincipalAccessor.Change(new Claim(AbpClaimTypes.UserId, _testData.UserNeoId.ToString())))
{
// Input is empty - try to remove all roles
await _userAppService.UpdateRolesAsync(
_testData.UserJohnId,
new IdentityUserUpdateRolesDto
{
RoleNames = Array.Empty<string>()
}
);
}
// "moderator" should be preserved (neo can't manage it), "supporter" removed (neo has it and it's not in input)
var roleNames = await _userRepository.GetRoleNamesAsync(_testData.UserJohnId);
roleNames.ShouldContain("moderator");
roleNames.ShouldNotContain("supporter");
}
[Fact]
public async Task UpdateRolesAsync_Admin_Can_Assign_Any_Role()
{
// admin user can assign roles they do not have (e.g. "sale")
using (_currentPrincipalAccessor.Change(new[]
{
new Claim(AbpClaimTypes.UserId, _testData.UserAdminId.ToString()),
new Claim(AbpClaimTypes.Role, "admin")
}))
{
await _userAppService.UpdateRolesAsync(
_testData.UserDavidId,
new IdentityUserUpdateRolesDto
{
RoleNames = new[] { "sale" }
}
);
}
var roleNames = await _userRepository.GetRoleNamesAsync(_testData.UserDavidId);
roleNames.ShouldContain("sale");
}
[Fact]
public async Task UpdateRolesAsync_Self_Cannot_Add_New_Roles()
{
// neo only has "supporter", tries to add "admin" to self
using (_currentPrincipalAccessor.Change(new Claim(AbpClaimTypes.UserId, _testData.UserNeoId.ToString())))
{
await _userAppService.UpdateRolesAsync(
_testData.UserNeoId,
new IdentityUserUpdateRolesDto
{
RoleNames = new[] { "supporter", "admin" }
}
);
}
// "admin" should not be added (neo doesn't have it), "supporter" kept
var roleNames = await _userRepository.GetRoleNamesAsync(_testData.UserNeoId);
roleNames.ShouldContain("supporter");
roleNames.ShouldNotContain("admin");
}
[Fact]
public async Task UpdateRolesAsync_Self_Can_Remove_Own_Roles()
{
// admin user has: admin, moderator, supporter, manager
// Remove supporter and manager from self
await _userAppService.UpdateRolesAsync(
_testData.UserAdminId,
new IdentityUserUpdateRolesDto
{
RoleNames = new[] { "admin", "moderator" }
}
);
var roleNames = await _userRepository.GetRoleNamesAsync(_testData.UserAdminId);
roleNames.ShouldContain("admin");
roleNames.ShouldContain("moderator");
roleNames.ShouldNotContain("supporter");
roleNames.ShouldNotContain("manager");
}
private static string CreateRandomEmail() private static string CreateRandomEmail()
{ {
return Guid.NewGuid().ToString("N").Left(16) + "@abp.io"; return Guid.NewGuid().ToString("N").Left(16) + "@abp.io";

5
modules/identity/test/Volo.Abp.Identity.TestBase/Volo/Abp/Identity/AbpIdentityTestDataBuilder.cs

@ -124,8 +124,11 @@ public class AbpIdentityTestDataBuilder : ITransientDependency
private async Task AddUsers() private async Task AddUsers()
{ {
var adminUser = new IdentityUser(_guidGenerator.Create(), "administrator", "admin@abp.io"); var adminUser = new IdentityUser(_testData.UserAdminId, "administrator", "administrator@abp.io");
adminUser.AddRole(_adminRole.Id); adminUser.AddRole(_adminRole.Id);
adminUser.AddRole(_moderatorRole.Id);
adminUser.AddRole(_supporterRole.Id);
adminUser.AddRole(_managerRole.Id);
adminUser.AddClaim(_guidGenerator, new Claim("TestClaimType", "42")); adminUser.AddClaim(_guidGenerator, new Claim("TestClaimType", "42"));
await _userRepository.InsertAsync(adminUser); await _userRepository.InsertAsync(adminUser);

8
modules/identity/test/Volo.Abp.Identity.TestBase/Volo/Abp/Identity/IdentityRoleRepository_Tests.cs

@ -81,9 +81,9 @@ public abstract class IdentityRoleRepository_Tests<TStartupModule> : AbpIdentity
roles.Count.ShouldBe(5); roles.Count.ShouldBe(5);
roles.ShouldContain(r => r.Role.Name == "admin" && r.UserCount == 2); roles.ShouldContain(r => r.Role.Name == "admin" && r.UserCount == 2);
roles.ShouldContain(r => r.Role.Name == "moderator" && r.UserCount == 1); roles.ShouldContain(r => r.Role.Name == "moderator" && r.UserCount == 2);
roles.ShouldContain(r => r.Role.Name == "supporter" && r.UserCount == 2); roles.ShouldContain(r => r.Role.Name == "supporter" && r.UserCount == 3);
roles.ShouldContain(r => r.Role.Name == "manager" && r.UserCount == 1); roles.ShouldContain(r => r.Role.Name == "manager" && r.UserCount == 2);
using (var uow = UnitOfWorkManager.Begin()) using (var uow = UnitOfWorkManager.Begin())
@ -96,7 +96,7 @@ public abstract class IdentityRoleRepository_Tests<TStartupModule> : AbpIdentity
roles = await RoleRepository.GetListWithUserCountAsync(); roles = await RoleRepository.GetListWithUserCountAsync();
roles.Count.ShouldBe(5); roles.Count.ShouldBe(5);
roles.ShouldContain(r => r.Role.Name == "manager" && r.UserCount == 0); roles.ShouldContain(r => r.Role.Name == "manager" && r.UserCount == 1);
roles.ShouldContain(r => r.Role.Name == "sale" && r.UserCount == 0); roles.ShouldContain(r => r.Role.Name == "sale" && r.UserCount == 0);
} }
} }

1
modules/identity/test/Volo.Abp.Identity.TestBase/Volo/Abp/Identity/IdentityTestData.cs

@ -5,6 +5,7 @@ namespace Volo.Abp.Identity;
public class IdentityTestData : ISingletonDependency public class IdentityTestData : ISingletonDependency
{ {
public Guid UserAdminId { get; } = Guid.NewGuid();
public Guid RoleModeratorId { get; } = Guid.NewGuid(); public Guid RoleModeratorId { get; } = Guid.NewGuid();
public Guid RoleSupporterId { get; } = Guid.NewGuid(); public Guid RoleSupporterId { get; } = Guid.NewGuid();
public Guid RoleManagerId { get; } = Guid.NewGuid(); public Guid RoleManagerId { get; } = Guid.NewGuid();

8
modules/identity/test/Volo.Abp.Identity.TestBase/Volo/Abp/Identity/IdentityUserRepository_Tests.cs

@ -122,7 +122,8 @@ public abstract class IdentityUserRepository_Tests<TStartupModule> : AbpIdentity
public async Task GetListByNormalizedRoleNameAsync() public async Task GetListByNormalizedRoleNameAsync()
{ {
var users = await UserRepository.GetListByNormalizedRoleNameAsync(LookupNormalizer.NormalizeName("supporter")); var users = await UserRepository.GetListByNormalizedRoleNameAsync(LookupNormalizer.NormalizeName("supporter"));
users.Count.ShouldBe(2); users.Count.ShouldBe(3);
users.ShouldContain(u => u.UserName == "administrator");
users.ShouldContain(u => u.UserName == "john.nash"); users.ShouldContain(u => u.UserName == "john.nash");
users.ShouldContain(u => u.UserName == "neo"); users.ShouldContain(u => u.UserName == "neo");
} }
@ -130,14 +131,17 @@ public abstract class IdentityUserRepository_Tests<TStartupModule> : AbpIdentity
[Fact] [Fact]
public async Task GetUserIdListByRoleIdAsync() public async Task GetUserIdListByRoleIdAsync()
{ {
var admin = await UserRepository.FindByNormalizedUserNameAsync(LookupNormalizer.NormalizeName("administrator"));
var john = await UserRepository.FindByNormalizedUserNameAsync(LookupNormalizer.NormalizeName("john.nash")); var john = await UserRepository.FindByNormalizedUserNameAsync(LookupNormalizer.NormalizeName("john.nash"));
var neo = await UserRepository.FindByNormalizedUserNameAsync(LookupNormalizer.NormalizeName("neo")); var neo = await UserRepository.FindByNormalizedUserNameAsync(LookupNormalizer.NormalizeName("neo"));
admin.ShouldNotBeNull();
john.ShouldNotBeNull(); john.ShouldNotBeNull();
neo.ShouldNotBeNull(); neo.ShouldNotBeNull();
var roleId = (await RoleRepository.FindByNormalizedNameAsync(LookupNormalizer.NormalizeName("supporter"))).Id; var roleId = (await RoleRepository.FindByNormalizedNameAsync(LookupNormalizer.NormalizeName("supporter"))).Id;
var users = await UserRepository.GetUserIdListByRoleIdAsync(roleId); var users = await UserRepository.GetUserIdListByRoleIdAsync(roleId);
users.Count.ShouldBe(2); users.Count.ShouldBe(3);
users.ShouldContain(id => id == admin.Id);
users.ShouldContain(id => id == john.Id); users.ShouldContain(id => id == john.Id);
users.ShouldContain(id => id == neo.Id); users.ShouldContain(id => id == neo.Id);
} }

2
modules/permission-management/src/Volo.Abp.PermissionManagement.Application.Contracts/Volo/Abp/PermissionManagement/PermissionGrantInfoDto.cs

@ -15,4 +15,6 @@ public class PermissionGrantInfoDto
public List<string> AllowedProviders { get; set; } public List<string> AllowedProviders { get; set; }
public List<ProviderInfoDto> GrantedProviders { get; set; } public List<ProviderInfoDto> GrantedProviders { get; set; }
public bool IsEditable { get; set; }
} }

72
modules/permission-management/src/Volo.Abp.PermissionManagement.Application/Volo/Abp/PermissionManagement/PermissionAppService.cs

@ -10,6 +10,8 @@ using Volo.Abp.Localization;
using Volo.Abp.MultiTenancy; using Volo.Abp.MultiTenancy;
using Volo.Abp.SimpleStateChecking; using Volo.Abp.SimpleStateChecking;
using Volo.Abp.PermissionManagement.Localization; using Volo.Abp.PermissionManagement.Localization;
using Volo.Abp.Roles;
using Volo.Abp.Users;
namespace Volo.Abp.PermissionManagement; namespace Volo.Abp.PermissionManagement;
@ -128,9 +130,47 @@ public class PermissionAppService : ApplicationService, IPermissionAppService
} }
} }
// Filter permissions for the current user: only show permissions they have or that are already granted
await FilterOutputPermissionsByCurrentUserAsync(result);
return result; return result;
} }
protected virtual async Task FilterOutputPermissionsByCurrentUserAsync(GetPermissionListResultDto result)
{
if (await HasAdminRoleAsync())
{
return;
}
// Collect all permission names
var allPermissionNames = result.Groups
.SelectMany(g => g.Permissions)
.Select(p => p.Name)
.ToArray();
if (!allPermissionNames.Any())
{
return;
}
// Check which permissions current user has
var currentUserPermissions = await PermissionChecker.IsGrantedAsync(allPermissionNames);
var grantedPermissionNames = currentUserPermissions.Result
.Where(x => x.Value == PermissionGrantResult.Granted)
.Select(x => x.Key)
.ToHashSet();
// Mark editability: users can only edit permissions they currently have
foreach (var group in result.Groups)
{
foreach (var permission in group.Permissions)
{
permission.IsEditable = grantedPermissionNames.Contains(permission.Name);
}
}
}
protected virtual PermissionGrantInfoDto CreatePermissionGrantInfoDto(PermissionDefinition permission) protected virtual PermissionGrantInfoDto CreatePermissionGrantInfoDto(PermissionDefinition permission)
{ {
return new PermissionGrantInfoDto return new PermissionGrantInfoDto
@ -139,7 +179,8 @@ public class PermissionAppService : ApplicationService, IPermissionAppService
DisplayName = permission.DisplayName?.Localize(StringLocalizerFactory), DisplayName = permission.DisplayName?.Localize(StringLocalizerFactory),
ParentName = permission.Parent?.Name, ParentName = permission.Parent?.Name,
AllowedProviders = permission.Providers, AllowedProviders = permission.Providers,
GrantedProviders = new List<ProviderInfoDto>() GrantedProviders = new List<ProviderInfoDto>(),
IsEditable = true
}; };
} }
@ -162,6 +203,7 @@ public class PermissionAppService : ApplicationService, IPermissionAppService
public virtual async Task UpdateAsync(string providerName, string providerKey, UpdatePermissionsDto input) public virtual async Task UpdateAsync(string providerName, string providerKey, UpdatePermissionsDto input)
{ {
await CheckProviderPolicy(providerName); await CheckProviderPolicy(providerName);
await FilterInputPermissionsByCurrentUserAsync(input);
foreach (var permissionDto in input.Permissions) foreach (var permissionDto in input.Permissions)
{ {
@ -379,4 +421,32 @@ public class PermissionAppService : ApplicationService, IPermissionAppService
await AuthorizationService.CheckAsync(policyName); await AuthorizationService.CheckAsync(policyName);
} }
protected virtual async Task FilterInputPermissionsByCurrentUserAsync(UpdatePermissionsDto input)
{
if (await HasAdminRoleAsync())
{
return;
}
if (input.Permissions.IsNullOrEmpty())
{
input.Permissions = Array.Empty<UpdatePermissionDto>();
return;
}
var currentUserPermissions = await PermissionChecker.IsGrantedAsync(input.Permissions.Select(p => p.Name).ToArray());
var grantedPermissions = currentUserPermissions.Result
.Where(x => x.Value == PermissionGrantResult.Granted)
.Select(x => x.Key)
.ToHashSet();
// Filters the input DTO in-place to only include manageable permissions.
input.Permissions = input.Permissions.Where(x => grantedPermissions.Contains(x.Name)).ToArray();
}
protected virtual Task<bool> HasAdminRoleAsync()
{
return Task.FromResult(CurrentUser.IsInRole(AbpRoleConsts.AdminRoleName));
}
} }

45
modules/permission-management/src/Volo.Abp.PermissionManagement.Blazor/Components/PermissionManagementModal.razor.cs

@ -28,8 +28,6 @@ public partial class PermissionManagementModal
protected List<PermissionGroupDto> _allGroups; protected List<PermissionGroupDto> _allGroups;
protected List<PermissionGroupDto> _groups; protected List<PermissionGroupDto> _groups;
protected List<PermissionGrantInfoDto> _disabledPermissions = new List<PermissionGrantInfoDto>();
protected string _selectedTabName; protected string _selectedTabName;
protected bool _selectAllDisabled; protected bool _selectAllDisabled;
@ -102,19 +100,6 @@ public partial class PermissionManagementModal
{ {
_selectAllDisabled = _groups.All(IsPermissionGroupDisabled); _selectAllDisabled = _groups.All(IsPermissionGroupDisabled);
if (checkDisabledPermissions)
{
_disabledPermissions.Clear();
}
foreach (var permission in _groups.SelectMany(x => x.Permissions))
{
if (checkDisabledPermissions && permission.IsGranted && permission.GrantedProviders.All(x => x.ProviderName != _providerName))
{
_disabledPermissions.Add(permission);
}
}
foreach (var group in _groups) foreach (var group in _groups)
{ {
SetPermissionDepths(group.Permissions, null, 0); SetPermissionDepths(group.Permissions, null, 0);
@ -285,12 +270,28 @@ public partial class PermissionManagementModal
protected virtual bool IsDisabledPermission(PermissionGrantInfoDto permissionGrantInfo) protected virtual bool IsDisabledPermission(PermissionGrantInfoDto permissionGrantInfo)
{ {
return _disabledPermissions.Any(x => x == permissionGrantInfo); if (!permissionGrantInfo.IsEditable)
{
return true;
}
return permissionGrantInfo.IsGranted &&
permissionGrantInfo.GrantedProviders.All(p => p.ProviderName != _providerName);
} }
protected virtual string GetShownName(PermissionGrantInfoDto permissionGrantInfo) protected virtual string GetShownName(PermissionGrantInfoDto permissionGrantInfo)
{ {
if (!IsDisabledPermission(permissionGrantInfo)) if (permissionGrantInfo.GrantedProviders.All(p => p.ProviderName == _providerName))
{
return permissionGrantInfo.DisplayName;
}
var grantedByOtherProviders = permissionGrantInfo.GrantedProviders
.Where(p => p.ProviderName != _providerName)
.Select(p => p.ProviderName)
.ToList();
if (!grantedByOtherProviders.Any())
{ {
return permissionGrantInfo.DisplayName; return permissionGrantInfo.DisplayName;
} }
@ -298,10 +299,7 @@ public partial class PermissionManagementModal
return string.Format( return string.Format(
"{0} ({1})", "{0} ({1})",
permissionGrantInfo.DisplayName, permissionGrantInfo.DisplayName,
permissionGrantInfo.GrantedProviders grantedByOtherProviders.JoinAsString(", ")
.Where(p => p.ProviderName != _providerName)
.Select(p => p.ProviderName)
.JoinAsString(", ")
); );
} }
@ -313,10 +311,7 @@ public partial class PermissionManagementModal
protected virtual bool IsPermissionGroupDisabled(PermissionGroupDto group) protected virtual bool IsPermissionGroupDisabled(PermissionGroupDto group)
{ {
var permissions = group.Permissions; return group.Permissions.All(IsDisabledPermission);
var grantedProviders = permissions.SelectMany(x => x.GrantedProviders);
return permissions.All(x => x.IsGranted) && grantedProviders.Any(p => p.ProviderName != _providerName);
} }
protected virtual async Task ResetSearchTextAsync() protected virtual async Task ResetSearchTextAsync()

3
modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/PermissionDataSeedContributor.cs

@ -4,6 +4,7 @@ using Volo.Abp.Authorization.Permissions;
using Volo.Abp.Data; using Volo.Abp.Data;
using Volo.Abp.DependencyInjection; using Volo.Abp.DependencyInjection;
using Volo.Abp.MultiTenancy; using Volo.Abp.MultiTenancy;
using Volo.Abp.Roles;
namespace Volo.Abp.PermissionManagement; namespace Volo.Abp.PermissionManagement;
@ -34,7 +35,7 @@ public class PermissionDataSeedContributor : IDataSeedContributor, ITransientDep
await PermissionDataSeeder.SeedAsync( await PermissionDataSeeder.SeedAsync(
RolePermissionValueProvider.ProviderName, RolePermissionValueProvider.ProviderName,
"admin", AbpRoleConsts.AdminRoleName,
permissionNames, permissionNames,
context?.TenantId context?.TenantId
); );

23
modules/permission-management/src/Volo.Abp.PermissionManagement.Web/Pages/AbpPermissionManagement/PermissionManagementModal.cshtml.cs

@ -135,9 +135,7 @@ public class PermissionManagementModal : AbpPageModel
public bool IsDisabled(string currentProviderName) public bool IsDisabled(string currentProviderName)
{ {
var grantedProviders = Permissions.SelectMany(x => x.GrantedProviders); return Permissions.All(p => p.IsDisabled(currentProviderName));
return Permissions.All(x => x.IsGranted) && grantedProviders.All(p => p.ProviderName != currentProviderName);
} }
} }
@ -159,9 +157,11 @@ public class PermissionManagementModal : AbpPageModel
public List<ProviderInfoViewModel> GrantedProviders { get; set; } public List<ProviderInfoViewModel> GrantedProviders { get; set; }
public bool IsEditable { get; set; }
public bool IsDisabled(string currentProviderName) public bool IsDisabled(string currentProviderName)
{ {
return IsGranted && GrantedProviders.All(p => p.ProviderName != currentProviderName); return !IsEditable || (IsGranted && GrantedProviders.All(p => p.ProviderName != currentProviderName));
} }
public string GetShownName(string currentProviderName) public string GetShownName(string currentProviderName)
@ -171,13 +171,20 @@ public class PermissionManagementModal : AbpPageModel
return DisplayName; return DisplayName;
} }
var grantedByOtherProviders = GrantedProviders
.Where(p => p.ProviderName != currentProviderName)
.Select(p => p.ProviderName)
.ToList();
if (!grantedByOtherProviders.Any())
{
return DisplayName;
}
return string.Format( return string.Format(
"{0} ({1})", "{0} ({1})",
DisplayName, DisplayName,
GrantedProviders grantedByOtherProviders.JoinAsString(", ")
.Where(p => p.ProviderName != currentProviderName)
.Select(p => p.ProviderName)
.JoinAsString(", ")
); );
} }
} }

14
modules/permission-management/test/Volo.Abp.PermissionManagement.Application.Tests/Volo/Abp/PermissionManagement/AbpPermissionManagementApplicationTestBase.cs

@ -1,9 +1,7 @@
using System; using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using NSubstitute; using Microsoft.Extensions.DependencyInjection.Extensions;
using Volo.Abp.Users; using Volo.Abp.Authorization.Permissions;
namespace Volo.Abp.PermissionManagement; namespace Volo.Abp.PermissionManagement;
@ -17,10 +15,8 @@ public class AbpPermissionManagementApplicationTestBase : PermissionManagementTe
} }
protected override void AfterAddApplication(IServiceCollection services) protected override void AfterAddApplication(IServiceCollection services)
{ {
var currentUser = Substitute.For<ICurrentUser>(); var fakePermissionChecker = new FakePermissionChecker();
currentUser.Roles.Returns(new[] { "admin" }); services.AddSingleton(fakePermissionChecker);
currentUser.IsAuthenticated.Returns(true); services.Replace(ServiceDescriptor.Singleton<IPermissionChecker>(fakePermissionChecker));
services.AddSingleton(currentUser);
} }
} }

53
modules/permission-management/test/Volo.Abp.PermissionManagement.Application.Tests/Volo/Abp/PermissionManagement/FakePermissionChecker.cs

@ -0,0 +1,53 @@
using System.Collections.Generic;
using System.Security.Claims;
using System.Threading.Tasks;
using Volo.Abp.Authorization.Permissions;
namespace Volo.Abp.PermissionManagement;
public class FakePermissionChecker : IPermissionChecker
{
private HashSet<string>? _grantedPermissions;
public void GrantAllPermissions()
{
_grantedPermissions = null;
}
public void SetGrantedPermissions(params string[] permissions)
{
_grantedPermissions = new HashSet<string>(permissions);
}
private bool IsGranted(string name)
{
return _grantedPermissions == null || _grantedPermissions.Contains(name);
}
public Task<bool> IsGrantedAsync(string name)
{
return Task.FromResult(IsGranted(name));
}
public Task<bool> IsGrantedAsync(ClaimsPrincipal? claimsPrincipal, string name)
{
return Task.FromResult(IsGranted(name));
}
public Task<MultiplePermissionGrantResult> IsGrantedAsync(string[] names)
{
return IsGrantedAsync(null, names);
}
public Task<MultiplePermissionGrantResult> IsGrantedAsync(ClaimsPrincipal? claimsPrincipal, string[] names)
{
var result = new MultiplePermissionGrantResult();
foreach (var name in names)
{
result.Result[name] = IsGranted(name)
? PermissionGrantResult.Granted
: PermissionGrantResult.Undefined;
}
return Task.FromResult(result);
}
}

120
modules/permission-management/test/Volo.Abp.PermissionManagement.Application.Tests/Volo/Abp/PermissionManagement/PermissionAppService_Tests.cs

@ -16,12 +16,14 @@ public class PermissionAppService_Tests : AbpPermissionManagementApplicationTest
private readonly IPermissionAppService _permissionAppService; private readonly IPermissionAppService _permissionAppService;
private readonly IPermissionGrantRepository _permissionGrantRepository; private readonly IPermissionGrantRepository _permissionGrantRepository;
private readonly ICurrentPrincipalAccessor _currentPrincipalAccessor; private readonly ICurrentPrincipalAccessor _currentPrincipalAccessor;
private readonly FakePermissionChecker _fakePermissionChecker;
public PermissionAppService_Tests() public PermissionAppService_Tests()
{ {
_permissionAppService = GetRequiredService<IPermissionAppService>(); _permissionAppService = GetRequiredService<IPermissionAppService>();
_permissionGrantRepository = GetRequiredService<IPermissionGrantRepository>(); _permissionGrantRepository = GetRequiredService<IPermissionGrantRepository>();
_currentPrincipalAccessor = GetRequiredService<ICurrentPrincipalAccessor>(); _currentPrincipalAccessor = GetRequiredService<ICurrentPrincipalAccessor>();
_fakePermissionChecker = GetRequiredService<FakePermissionChecker>();
} }
[Fact] [Fact]
@ -135,4 +137,122 @@ public class PermissionAppService_Tests : AbpPermissionManagementApplicationTest
(await _permissionGrantRepository.FindAsync("MyPermission1", "Test", (await _permissionGrantRepository.FindAsync("MyPermission1", "Test",
"Test")).ShouldBeNull(); "Test")).ShouldBeNull();
} }
[Fact]
public async Task Get_Should_Mark_Permissions_As_Non_Editable_When_Current_User_Does_Not_Have_Them()
{
// Current user only has MyPermission1 and MyPermission2
_fakePermissionChecker.SetGrantedPermissions("MyPermission1", "MyPermission2");
var result = await _permissionAppService.GetAsync(
UserPermissionValueProvider.ProviderName,
PermissionTestDataBuilder.User1Id.ToString());
var testGroup = result.Groups.FirstOrDefault(g => g.Name == "TestGroup");
testGroup.ShouldNotBeNull();
// Permissions the current user has -> IsEditable = true
testGroup.Permissions.First(p => p.Name == "MyPermission1").IsEditable.ShouldBeTrue();
testGroup.Permissions.First(p => p.Name == "MyPermission2").IsEditable.ShouldBeTrue();
// Permissions the current user does NOT have -> IsEditable = false
testGroup.Permissions.First(p => p.Name == "MyPermission2.ChildPermission1").IsEditable.ShouldBeFalse();
testGroup.Permissions.First(p => p.Name == "MyPermission3").IsEditable.ShouldBeFalse();
testGroup.Permissions.First(p => p.Name == "MyPermission4").IsEditable.ShouldBeFalse();
testGroup.Permissions.First(p => p.Name == "MyPermission6").IsEditable.ShouldBeFalse();
testGroup.Permissions.First(p => p.Name == "MyPermission6.ChildPermission2").IsEditable.ShouldBeFalse();
}
[Fact]
public async Task Get_Should_Allow_Admin_To_Edit_All_Permissions()
{
// Current user does NOT have these permissions, but has admin role
_fakePermissionChecker.SetGrantedPermissions("MyPermission1");
using (_currentPrincipalAccessor.Change(new Claim(AbpClaimTypes.Role, "admin")))
{
var result = await _permissionAppService.GetAsync(
UserPermissionValueProvider.ProviderName,
PermissionTestDataBuilder.User1Id.ToString());
var testGroup = result.Groups.FirstOrDefault(g => g.Name == "TestGroup");
testGroup.ShouldNotBeNull();
testGroup.Permissions.First(p => p.Name == "MyPermission3").IsEditable.ShouldBeTrue();
testGroup.Permissions.First(p => p.Name == "MyPermission6").IsEditable.ShouldBeTrue();
}
}
[Fact]
public async Task Update_Should_Not_Grant_Permission_That_Current_User_Does_Not_Have()
{
// Current user only has MyPermission1, NOT MyPermission2
_fakePermissionChecker.SetGrantedPermissions("MyPermission1");
// Try to grant both MyPermission1 and MyPermission2
await _permissionAppService.UpdateAsync("Test", "Test", new UpdatePermissionsDto()
{
Permissions = new UpdatePermissionDto[]
{
new UpdatePermissionDto() { IsGranted = true, Name = "MyPermission1" },
new UpdatePermissionDto() { IsGranted = true, Name = "MyPermission2" }
}
});
// MyPermission1 should be granted (current user has it)
(await _permissionGrantRepository.FindAsync("MyPermission1", "Test", "Test")).ShouldNotBeNull();
// MyPermission2 should NOT be granted (current user doesn't have it, filtered out)
(await _permissionGrantRepository.FindAsync("MyPermission2", "Test", "Test")).ShouldBeNull();
}
[Fact]
public async Task Update_Should_Not_Revoke_Permission_That_Current_User_Does_Not_Have()
{
// First, grant both permissions
await _permissionGrantRepository.InsertAsync(
new PermissionGrant(Guid.NewGuid(), "MyPermission1", "Test", "Test"));
await _permissionGrantRepository.InsertAsync(
new PermissionGrant(Guid.NewGuid(), "MyPermission2", "Test", "Test"));
// Current user only has MyPermission1, NOT MyPermission2
_fakePermissionChecker.SetGrantedPermissions("MyPermission1");
// Try to revoke both
await _permissionAppService.UpdateAsync("Test", "Test", new UpdatePermissionsDto()
{
Permissions = new UpdatePermissionDto[]
{
new UpdatePermissionDto() { IsGranted = false, Name = "MyPermission1" },
new UpdatePermissionDto() { IsGranted = false, Name = "MyPermission2" }
}
});
// MyPermission1 should be revoked (current user has it)
(await _permissionGrantRepository.FindAsync("MyPermission1", "Test", "Test")).ShouldBeNull();
// MyPermission2 should still be granted (current user doesn't have it, revoke filtered out)
(await _permissionGrantRepository.FindAsync("MyPermission2", "Test", "Test")).ShouldNotBeNull();
}
[Fact]
public async Task Update_Should_Allow_Admin_To_Grant_Permissions_Without_Having_Them()
{
(await _permissionGrantRepository.FindAsync("MyPermission2", "Test", "Test")).ShouldBeNull();
_fakePermissionChecker.SetGrantedPermissions();
using (_currentPrincipalAccessor.Change(new Claim(AbpClaimTypes.Role, "admin")))
{
await _permissionAppService.UpdateAsync("Test", "Test", new UpdatePermissionsDto()
{
Permissions = new UpdatePermissionDto[]
{
new UpdatePermissionDto() { IsGranted = true, Name = "MyPermission2" }
}
});
}
(await _permissionGrantRepository.FindAsync("MyPermission2", "Test", "Test")).ShouldNotBeNull();
}
} }

Loading…
Cancel
Save